diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index deb8fd1384..3518eaddf8 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -527,7 +527,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_search_found_results" = "{count:No messages found|Found # message|Found # messages}"; "lng_search_global_results" = "Global search results"; -"lng_mediaview_save_as" = "Save as.."; +"lng_media_save_progress" = "{ready} of {total} {mb}"; +"lng_mediaview_save_as" = "Save As.."; "lng_mediaview_copy" = "Copy"; "lng_mediaview_forward" = "Forward"; "lng_mediaview_delete" = "Delete"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 31b97aa66b..9f34c5b78e 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1623,15 +1623,25 @@ mvFadeDuration: 150; mvDocPadding: 18px; mvDocSize: size(340px, 116px); mvDocBg: white; -mvDocNameTop: 5px; +mvDocNameTop: 4px; +mvDocNameFont: font(semibold 14px); mvDocNameColor: black; -mvDocSizeTop: 30px; +mvDocSizeTop: 29px; mvDocSizeColor: #808080; +mvDocExtTop: 35px; +mvDocExtFont: font(semibold 18px); +mvDocExtColor: white; +mvDocExtPadding: 10px; mvDocLinksTop: 57px; mvDocRed: sprite(0px, 400px, 80px, 80px); mvDocYellow: sprite(80px, 400px, 80px, 80px); mvDocGreen: sprite(160px, 400px, 80px, 80px); mvDocBlue: sprite(240px, 400px, 80px, 80px); +mvDocLink: linkButton(btnDefLink) { + color: #4595d3; + overColor: #4595d3; + downColor: #4595d3; +} mvDeltaFromLastAction: 5px; mvSwipeDistance: 80px; @@ -1694,9 +1704,13 @@ photoLoaderAlphaMin: 0.1; // not less than that radialSize: size(50px, 50px); radialLine: 2px; -radialDuration: 200; -radialPeriod: 2000; +radialDuration: 350; +radialPeriod: 3000; radialBgOpacity: 0.4; +radialDownload: sprite(346px, 0px, 50px, 50px); +radialDownloadOpacity: 0.8; +radialCancel: sprite(378px, 50px, 18px, 18px); +radialCancelOpacity: 0.7; overviewLoader: size(34px, 14px); overviewLoaderPoint: size(4px, 4px); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index c7719bd45a..d89191e50e 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -675,6 +675,8 @@ void Application::startApp() { DEBUG_LOG(("Application Info: starting app..")); + QMimeDatabase().mimeTypeForName(qsl("text/plain")); // create mime database + window->createWinId(); window->init(); diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index 7e3eeca29a..86fb78fda7 100644 Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index 820955566e..e2d45ef522 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index 656d9c0b1d..9300efb8ec 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -106,8 +106,10 @@ void AbstractBox::setMaxHeight(int32 maxHeight) { void AbstractBox::resizeMaxHeight(int32 newWidth, int32 maxHeight) { if (width() != newWidth || _maxHeight != maxHeight) { + QRect g(geometry()); _maxHeight = maxHeight; resize(newWidth, countHeight()); + if (parentWidget()) parentWidget()->update(geometry().united(g).marginsAdded(QMargins(st::boxShadow.pxWidth(), st::boxShadow.pxHeight(), st::boxShadow.pxWidth(), st::boxShadow.pxHeight()))); } } diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 027d4105c8..40bd55078c 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -18,7 +18,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "animation.h" -#include <QtCore/QTimer> + +#include "mainwidget.h" +#include "window.h" namespace { AnimationManager *manager = 0; @@ -94,3 +96,113 @@ namespace anim { } } + +bool AnimatedGif::animStep(float64 ms) { + int32 f = frame; + while (f < frames.size() && ms > delays[f]) { + ++f; + if (f == frames.size() && frames.size() < framesCount) { + if (reader->read(&img)) { + int64 d = reader->nextImageDelay(), delay = delays[f - 1]; + if (!d) d = 1; + delay += d; + frames.push_back(QPixmap::fromImage(img.size() == QSize(w, h) ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly)); + delays.push_back(delay); + for (int32 i = 0; i < frames.size(); ++i) { + if (!frames[i].isNull()) { + frames[i] = QPixmap(); + break; + } + } + } else { + framesCount = frames.size(); + } + } + if (f == frames.size()) { + if (!duration) { + duration = delays.isEmpty() ? 1 : delays.back(); + } + + f = 0; + for (int32 i = 0, s = delays.size() - 1; i <= s; ++i) { + delays[i] += duration; + } + if (frames[f].isNull()) { + QString fname = reader->fileName(); + delete reader; + reader = new QImageReader(fname); + } + } + if (frames[f].isNull() && reader->read(&img)) { + frames[f] = QPixmap::fromImage(img.size() == QSize(w, h) ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly); + } + } + if (frame != f) { + frame = f; + if (msg && App::main()) { + App::main()->msgUpdated(msg->history()->peer->id, msg); + } else { + emit updated(); + } + } + return true; +} + +void AnimatedGif::start(HistoryItem *row, const QString &file) { + stop(); + + reader = new QImageReader(file); + if (!reader->canRead() || !reader->supportsAnimation()) { + stop(); + return; + } + + QSize s = reader->size(); + w = s.width(); + h = s.height(); + framesCount = reader->imageCount(); + if (!w || !h || !framesCount) { + stop(); + return; + } + + frames.reserve(framesCount); + delays.reserve(framesCount); + + int32 sizeLeft = MediaViewImageSizeLimit, delay = 0; + for (bool read = reader->read(&img); read; read = reader->read(&img)) { + sizeLeft -= w * h * 4; + frames.push_back(QPixmap::fromImage(img.size() == s ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly)); + int32 d = reader->nextImageDelay(); + if (!d) d = 1; + delay += d; + delays.push_back(delay); + if (sizeLeft < 0) break; + } + + msg = row; + + anim::start(this); + if (msg) { + msg->initDimensions(); + if (App::main()) App::main()->itemResized(msg, true); + } +} + +void AnimatedGif::stop(bool onItemRemoved) { + if (isNull()) return; + + delete reader; + reader = 0; + HistoryItem *row = msg; + msg = 0; + frames.clear(); + delays.clear(); + w = h = frame = framesCount = duration = 0; + + anim::stop(this); + if (row && !onItemRemoved) { + row->initDimensions(); + if (App::main()) App::main()->itemResized(row, true); + } +} diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index 9d9556ba9b..e600f0d229 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -307,3 +307,39 @@ private: bool iterating; }; + +class HistoryItem; +class AnimatedGif : public QObject, public Animated { + Q_OBJECT + +public: + + AnimatedGif() : msg(0), reader(0), w(0), h(0), frame(0), framesCount(0), duration(0) { + } + + bool animStep(float64 ms); + + void start(HistoryItem *row, const QString &file); + void stop(bool onItemRemoved = false); + + bool isNull() const { + return !reader; + } + + ~AnimatedGif() { + stop(true); + } + +signals: + + void updated(); + +public: + + HistoryItem *msg; + QImage img; + QImageReader *reader; + QVector<QPixmap> frames; + QVector<int64> delays; + int32 w, h, frame, framesCount, duration; +}; diff --git a/Telegram/SourceFiles/gui/style_core.h b/Telegram/SourceFiles/gui/style_core.h index cfab2435e6..190b8b917e 100644 --- a/Telegram/SourceFiles/gui/style_core.h +++ b/Telegram/SourceFiles/gui/style_core.h @@ -26,6 +26,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include <QtGui/QCursor> #include <QtGui/QFont> +inline QRect rtlrect(int x, int y, int w, int h, int outerw) { + return rtl() ? QRect(outerw - x - w, y, w, h) : QRect(x, y, w, h); +} + namespace style { class FontData; diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index 9ac74f9cbf..8b26f7e5f1 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -887,7 +887,7 @@ public: _align = align; _parDirection = _t->_startDir; - if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = langDir(); + if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = cLangDir(); if ((*_t->_blocks.cbegin())->type() != TextBlockNewline) { initNextParagraph(_t->_blocks.cbegin()); } @@ -926,7 +926,7 @@ public: } _parDirection = static_cast<NewlineBlock*>(b)->nextDirection(); - if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = langDir(); + if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = cLangDir(); initNextParagraph(i + 1); longWordLine = true; @@ -2613,7 +2613,7 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, bool expandLinks) result += r; } else { QUrl u(url); - if (r.size() > 3 && _text.midRef(lnkFrom, r.size() - 3) == (u.isValid() ? u.toDisplayString() : url).midRef(0, r.size() - 3)) { // same link + if (r.size() <= 3 || _text.midRef(lnkFrom, r.size() - 3) == (u.isValid() ? u.toDisplayString() : url).midRef(0, r.size() - 3)) { // same link result += url; } else { result.append(r).append(qsl(" ( ")).append(url).append(qsl(" )")); @@ -4090,7 +4090,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some initLinkSets(); int32 len = text.size(), nextCmd = rich ? 0 : len; const QChar *start = text.unicode(), *end = start + text.size(); - for (int32 offset = 0, matchOffset = offset; offset < len;) { + for (int32 offset = 0, matchOffset = offset, mentionSkip = 0; offset < len;) { if (nextCmd <= offset) { for (nextCmd = offset; nextCmd < len; ++nextCmd) { if (*(start + nextCmd) == TextCommand) { @@ -4101,8 +4101,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some QRegularExpressionMatch mDomain = _reDomain.match(text, matchOffset); QRegularExpressionMatch mExplicitDomain = _reExplicitDomain.match(text, matchOffset); QRegularExpressionMatch mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch(); - QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, matchOffset) : QRegularExpressionMatch(); - if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch() && !mMention.hasMatch()) break; + QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch(); LinkRange link; int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX, @@ -4121,7 +4120,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some --hashtagEnd; } } - if (mMention.hasMatch()) { + while (mMention.hasMatch()) { if (!mMention.capturedRef(1).isEmpty()) { ++mentionOffset; } @@ -4129,10 +4128,21 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some --mentionEnd; } if (!(start + mentionOffset + 1)->isLetter() || !(start + mentionEnd - 1)->isLetterOrNumber()) { - mentionOffset = mentionEnd = INT_MAX; - if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break; + mentionSkip = mentionEnd; + mMention = _reMention.match(text, qMax(mentionSkip, matchOffset)); + if (mMention.hasMatch()) { + mentionOffset = mMention.capturedStart(); + mentionEnd = mMention.capturedEnd(); + } else { + mentionOffset = INT_MAX; + mentionEnd = INT_MAX; + } + } else { + break; } } + if (!mMention.hasMatch() && !mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break; + if (explicitDomainOffset < domainOffset) { domainOffset = explicitDomainOffset; domainEnd = explicitDomainEnd; diff --git a/Telegram/SourceFiles/gui/twidget.cpp b/Telegram/SourceFiles/gui/twidget.cpp index 7690b3c1eb..7cbcf90d17 100644 --- a/Telegram/SourceFiles/gui/twidget.cpp +++ b/Telegram/SourceFiles/gui/twidget.cpp @@ -20,9 +20,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "application.h" namespace { - Qt::LayoutDirection _dir = Qt::LeftToRight; - bool _rtl = false; - void _sendResizeEvents(QWidget *target) { QResizeEvent e(target->size(), QSize()); QApplication::sendEvent(target, &e); @@ -37,19 +34,6 @@ namespace { } } -void rtl(bool is) { - _rtl = is; - _dir = _rtl ? Qt::RightToLeft : Qt::LeftToRight; -} - -bool rtl() { - return _rtl; -} - -Qt::LayoutDirection langDir() { // current lang dependent - return _dir; -} - QPixmap myGrab(QWidget *target, const QRect &rect) { if (!cRetina()) return target->grab(rect); diff --git a/Telegram/SourceFiles/gui/twidget.h b/Telegram/SourceFiles/gui/twidget.h index 5e458f9737..83e15d8dd3 100644 --- a/Telegram/SourceFiles/gui/twidget.h +++ b/Telegram/SourceFiles/gui/twidget.h @@ -17,14 +17,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #pragma once -void rtl(bool is); -bool rtl(); -Qt::LayoutDirection langDir(); - -inline QRect rtlrect(int x, int y, int w, int h, int outerw) { - return rtl() ? QRect(outerw - x - w, y, w, h) : QRect(x, y, w, h); -} - class Widget : public QWidget { public: @@ -61,15 +53,36 @@ public: void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix, const QRect &from) { drawPixmap(QPoint(rtl() ? (outerw - x - (from.width() / pix.devicePixelRatio())) : x, y), pix, from); } + void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) { + return drawPixmapLeft(p.x(), p.y(), outerw, pix, from); + } void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix, const QRect &from) { drawPixmap(QPoint(rtl() ? x : (outerw - x - (from.width() / pix.devicePixelRatio())), y), pix, from); } - void drawSpriteLeft(int x, int y, int outerw, const QRect &sprite) { + void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) { + return drawPixmapRight(p.x(), p.y(), outerw, pix, from); + } + void drawSprite(int x, int y, const style::sprite &sprite) { + return drawPixmap(QPoint(x, y), App::sprite(), sprite); + } + void drawSprite(const QPoint &p, const style::sprite &sprite) { + return drawPixmap(p, App::sprite(), sprite); + } + void drawSpriteLeft(int x, int y, int outerw, const style::sprite &sprite) { return drawPixmapLeft(x, y, outerw, App::sprite(), sprite); } - void drawSpriteRight(int x, int y, int outerw, const QRect &sprite) { + void drawSpriteLeft(const QPoint &p, int outerw, const style::sprite &sprite) { + return drawPixmapLeft(p, outerw, App::sprite(), sprite); + } + void drawSpriteRight(int x, int y, int outerw, const style::sprite &sprite) { return drawPixmapRight(x, y, outerw, App::sprite(), sprite); } + void drawSpriteRight(const QPoint &p, int outerw, const style::sprite &sprite) { + return drawPixmapRight(p, outerw, App::sprite(), sprite); + } + void drawSpriteCenter(const QRect &in, const style::sprite &sprite) { + return drawPixmap(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), App::sprite(), sprite); + } }; class TWidget : public Widget { diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 246f03cda1..3c6d11b34d 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -81,7 +81,7 @@ namespace { }; inline void _initTextOptions() { - _historySrvOptions.dir = _textNameOptions.dir = _textDlgOptions.dir = langDir(); + _historySrvOptions.dir = _textNameOptions.dir = _textDlgOptions.dir = cLangDir(); _textDlgOptions.maxw = st::dlgMaxWidth * 2; _webpageTitleOptions.maxw = st::msgMaxWidth - st::msgPadding.left() - st::msgPadding.right() - st::webPageLeft; _webpageTitleOptions.maxh = st::webPageTitleFont->height * 2; @@ -89,127 +89,6 @@ namespace { _webpageDescriptionOptions.maxh = st::webPageDescriptionFont->height * 3; } - class AnimatedGif : public Animated { - public: - - AnimatedGif() : msg(0), reader(0), w(0), h(0), frame(0), framesCount(0), duration(0) { - } - - bool animStep(float64 ms) { - int32 f = frame; - while (f < frames.size() && ms > delays[f]) { - ++f; - if (f == frames.size() && frames.size() < framesCount) { - if (reader->read(&img)) { - int64 d = reader->nextImageDelay(), delay = delays[f - 1]; - if (!d) d = 1; - delay += d; - frames.push_back(QPixmap::fromImage(img.size() == QSize(w, h) ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly)); - delays.push_back(delay); - for (int32 i = 0; i < frames.size(); ++i) { - if (!frames[i].isNull()) { - frames[i] = QPixmap(); - break; - } - } - } else { - framesCount = frames.size(); - } - } - if (f == frames.size()) { - if (!duration) { - duration = delays.isEmpty() ? 1 : delays.back(); - } - - f = 0; - for (int32 i = 0, s = delays.size() - 1; i <= s; ++i) { - delays[i] += duration; - } - if (frames[f].isNull()) { - QString fname = reader->fileName(); - delete reader; - reader = new QImageReader(fname); - } - } - if (frames[f].isNull() && reader->read(&img)) { - frames[f] = QPixmap::fromImage(img.size() == QSize(w, h) ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly); - } - } - if (frame != f) { - frame = f; - if (App::main()) App::main()->msgUpdated(msg->history()->peer->id, msg); - } - return true; - } - - void start(HistoryItem *row, const QString &file) { - if (reader) { - stop(); - } - reader = new QImageReader(file); - if (!reader->canRead() || !reader->supportsAnimation()) { - stop(); - return; - } - - QSize s = reader->size(); - w = s.width(); - h = s.height(); - framesCount = reader->imageCount(); - if (!w || !h || !framesCount) { - stop(); - return; - } - - frames.reserve(framesCount); - delays.reserve(framesCount); - - int32 sizeLeft = MediaViewImageSizeLimit, delay = 0; - for (bool read = reader->read(&img); read; read = reader->read(&img)) { - sizeLeft -= w * h * 4; - frames.push_back(QPixmap::fromImage(img.size() == s ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly)); - int32 d = reader->nextImageDelay(); - if (!d) d = 1; - delay += d; - delays.push_back(delay); - if (sizeLeft < 0) break; - } - - msg = row; - - anim::start(this); - msg->initDimensions(); - App::main()->itemResized(msg, true); - } - - void stop(bool onItemRemoved = false) { - delete reader; - reader = 0; - HistoryItem *row = msg; - msg = 0; - frames.clear(); - delays.clear(); - w = h = frame = framesCount = duration = 0; - - anim::stop(this); - if (row && !onItemRemoved) { - row->initDimensions(); - if (App::main()) App::main()->itemResized(row, true); - } - } - - ~AnimatedGif() { - stop(true); - } - - HistoryItem *msg; - QImage img; - QImageReader *reader; - QVector<QPixmap> frames; - QVector<int64> delays; - int32 w, h, frame, framesCount, duration; - }; - AnimatedGif animated; inline HistoryReply *toHistoryReply(HistoryItem *item) { @@ -2508,6 +2387,7 @@ void HistoryDocument::initDimensions(const HistoryItem *parent) { } } } + _height = _minh; } void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const { @@ -3191,6 +3071,7 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) { if (data->pendingTill) { _maxw = st::webPageLeft + st::linkFont->m.width(lang((data->pendingTill < 0) ? lng_attach_failed : lng_profile_loading)); _minh = st::replyHeight; + _height = _minh; return; } if (!_openl && !data->url.isEmpty()) _openl = TextLinkPtr(new TextLink(data->url)); @@ -3280,6 +3161,7 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) { _duration = formatDurationText(data->duration); _durationWidth = st::msgDateFont->m.width(_duration); } + _height = _minh; } void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const { @@ -4054,6 +3936,7 @@ void HistoryImageLink::initDimensions(const HistoryItem *parent) { _minh += st::msgPadding.top() + st::msgNameFont->height; } } + _height = _minh; } void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index f01f53f7d9..6e57dce461 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -842,9 +842,9 @@ void HistoryList::saveContextFile() { VideoLink *lnkVideo = dynamic_cast<VideoLink*>(_contextMenuLnk.data()); AudioLink *lnkAudio = dynamic_cast<AudioLink*>(_contextMenuLnk.data()); DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data()); - if (lnkVideo) VideoSaveLink(lnkVideo->video()).doSave(true); - if (lnkAudio) AudioSaveLink(lnkAudio->audio()).doSave(true); - if (lnkDocument) DocumentSaveLink(lnkDocument->document()).doSave(true); + if (lnkVideo) VideoSaveLink::doSave(lnkVideo->video(), true); + if (lnkAudio) AudioSaveLink::doSave(lnkAudio->audio(), true); + if (lnkDocument) DocumentSaveLink::doSave(lnkDocument->document(), true); } void HistoryList::copyContextText() { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 37e3cd3e19..6cbd6eb395 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1431,6 +1431,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { msgUpdated(j.key()->history()->peer->id, j.key()); } } + App::wnd()->documentUpdated(document); } void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) { diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 6972183a7d..6995b47cd4 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -50,7 +50,10 @@ _width(0), _x(0), _y(0), _w(0), _h(0), _xStart(0), _yStart(0), _zoom(0), _zoomToScreen(0), _pressed(false), _dragging(0), _full(-1), _docNameWidth(0), _docSizeWidth(0), _docThumbx(0), _docThumby(0), _docThumbw(0), -_docRadialFirst(0), _docRadialStart(0), _docRadialLast(0), a_docRadialStart(0, 1), +_docRadialFirst(0), _docRadialStart(0), _docRadialLast(0), _docRadialOpacity(1), a_docRadialStart(0, 1), +_docDownload(this, lang(lng_media_download), st::mvDocLink), +_docSaveAs(this, lang(lng_mediaview_save_as), st::mvDocLink), +_docCancel(this, lang(lng_cancel), st::mvDocLink), _history(0), _peer(0), _user(0), _from(0), _index(-1), _msgid(0), _loadRequest(0), _over(OverNone), _down(OverNone), _lastAction(-st::mvDeltaFromLastAction, -st::mvDeltaFromLastAction), _ignoringDropdown(false), _controlsState(ControlsShown), _controlsAnimStarted(0), @@ -85,6 +88,10 @@ _saveMsgStarted(0), _saveMsgOpacity(0) _touchTimer.setSingleShot(true); connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); + connect(&_currentGif, SIGNAL(updated()), this, SLOT(onGifUpdated())); + + _btns.push_back(_btnSaveCancel = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_cancel)))); + connect(_btnSaveCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel())); _btns.push_back(_btnToMessage = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_context_to_msg)))); connect(_btnToMessage, SIGNAL(clicked()), this, SLOT(onToMessage())); _btns.push_back(_btnShowInFolder = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(cPlatform() == dbipMac ? lng_context_show_in_finder : lng_context_show_in_folder)))); @@ -96,7 +103,7 @@ _saveMsgStarted(0), _saveMsgOpacity(0) _btns.push_back(_btnDelete = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_delete)))); connect(_btnDelete, SIGNAL(clicked()), this, SLOT(onDelete())); _btns.push_back(_btnSaveAs = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_save_as)))); - connect(_btnSaveAs, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_btnSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs())); _btns.push_back(_btnViewAll = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_photos_all)))); connect(_btnViewAll, SIGNAL(clicked()), this, SLOT(onOverview())); @@ -105,6 +112,10 @@ _saveMsgStarted(0), _saveMsgOpacity(0) _controlsHideTimer.setSingleShot(true); connect(&_controlsHideTimer, SIGNAL(timeout()), this, SLOT(onHideControls())); + + connect(&_docDownload, SIGNAL(clicked()), this, SLOT(onDownload())); + connect(&_docSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs())); + connect(&_docCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel())); } void MediaView::moveToScreen() { @@ -149,6 +160,22 @@ void MediaView::mediaOverviewUpdated(PeerData *peer) { } } +void MediaView::documentUpdated(DocumentData *doc) { + if (_doc && _doc == doc && _current.isNull() && _currentGif.isNull()) { + if ((_doc->loader && _docCancel.isHidden()) || (!_doc->loader && !_docCancel.isHidden())) { + updateControls(); + } else if (_doc->loader) { + updateDocSize(); + update(_docRect); + } + } +} + +void MediaView::onGifUpdated() { + _currentGif.frames[_currentGif.frame].setDevicePixelRatio(cRetinaFactor()); + update(_x, _y, _w, _h); +} + void MediaView::changingMsgId(HistoryItem *row, MsgId newId) { if (row->id == _msgid) { _msgid = newId; @@ -156,10 +183,68 @@ void MediaView::changingMsgId(HistoryItem *row, MsgId newId) { mediaOverviewUpdated(row->history()->peer); } +void MediaView::updateDocSize() { + if (!_doc || !_current.isNull() || !_currentGif.isNull()) return; + + if (_doc->loader) { + quint64 ready = _doc->loader->currentOffset(), total = _doc->size; + QString readyStr, totalStr, mb; + if (total >= 1024 * 1024) { // more than 1 mb + qint64 readyTenthMb = (ready * 10 / (1024 * 1024)), totalTenthMb = (total * 10 / (1024 * 1024)); + readyStr = QString::number(readyTenthMb / 10) + '.' + QString::number(readyTenthMb % 10); + totalStr = QString::number(totalTenthMb / 10) + '.' + QString::number(totalTenthMb % 10); + mb = qsl("MB"); + } else { + qint64 readyKb = (ready / 1024), totalKb = (total / 1024); + readyStr = QString::number(readyKb); + totalStr = QString::number(totalKb); + mb = qsl("KB"); + } + _docSize = lng_media_save_progress(lt_ready, readyStr, lt_total, totalStr, lt_mb, mb); + } else { + _docSize = formatSizeText(_doc->size); + } + _docSizeWidth = st::mvFont->m.width(_docSize); + int32 maxw = st::mvDocSize.width() - st::mvDocBlue.pxWidth() - st::mvDocPadding * 3; + if (_docSizeWidth > maxw) { + _docSize = st::mvFont->m.elidedText(_docSize, Qt::ElideRight, maxw); + _docSizeWidth = st::mvFont->m.width(_docSize); + } +} + void MediaView::updateControls() { if (!_photo && !_doc) return; - _saveVisible = ((_photo && _photo->full->loaded()) || (_doc && !_doc->already(true).isEmpty())); + if (_doc && _current.isNull() && _currentGif.isNull()) { + if (_doc->loader) { + _docDownload.hide(); + _docSaveAs.hide(); + _docCancel.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocLinksTop, width()); + _docCancel.show(); + if (!_docRadialFirst) _docRadialFirst = _docRadialLast = _docRadialStart = getms(); + if (!animating()) anim::start(this); + } else { + if (_doc->already(true).isEmpty()) { + _docDownload.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocLinksTop, width()); + _docDownload.show(); + _docSaveAs.moveToLeft(_docRect.x() + 2.5 * st::mvDocPadding + st::mvDocBlue.pxWidth() + _docDownload.width(), _docRect.y() + st::mvDocPadding + st::mvDocLinksTop, width()); + _docSaveAs.show(); + _docCancel.hide(); + } else { + _docDownload.hide(); + _docSaveAs.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocLinksTop, width()); + _docSaveAs.show(); + _docCancel.hide(); + } + } + updateDocSize(); + } else { + _docDownload.hide(); + _docSaveAs.hide(); + _docCancel.hide(); + } + + _saveVisible = ((_photo && _photo->full->loaded()) || (_doc && (!_doc->already(true).isEmpty() || (_current.isNull() && _currentGif.isNull())))); _saveNav = rtlrect(width() - st::mvIconSize.width() * 2, height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height(), width()); _moreNav = rtlrect(width() - st::mvIconSize.width(), height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height(), width()); @@ -196,6 +281,7 @@ void MediaView::updateControls() { } void MediaView::updateDropdown() { + _btnSaveCancel->setVisible(_doc && _doc->loader); _btnToMessage->setVisible(_msgid > 0); _btnShowInFolder->setVisible(_doc && !_doc->already(true).isEmpty()); _btnSaveAs->setVisible(true); @@ -221,6 +307,7 @@ bool MediaView::animStep(float64 msp) { case OverHeader: update(_headerNav); break; case OverClose: update(_closeNav); break; case OverSave: update(_saveNav); break; + case OverIcon: update(_docIconRect); break; case OverMore: update(_moreNav); break; default: break; } @@ -246,28 +333,39 @@ bool MediaView::animStep(float64 msp) { if (dt < 1) result = true; } if (_doc && _docRadialStart > 0) { - float64 prg = _doc->loader ? _doc->loader->currentProgress() : (_doc->status == FileFailed ? 0 : 1); + float64 prg = _doc->loader ? qMax(_doc->loader->currentProgress(), 0.0001) : (_doc->status == FileFailed ? 0 : (_doc->already().isEmpty() ? 0 : 1)); if (prg != a_docRadial.to()) { a_docRadial.start(prg); _docRadialStart = _docRadialLast; } _docRadialLast = ms; - float64 dt = float64(ms - _docRadialStart); + float64 dt = float64(ms - _docRadialStart), fulldt = float64(ms - _docRadialFirst); + _docRadialOpacity = qMin(fulldt / st::radialDuration, 1.); if (_doc->loader) { a_docRadial.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear); result = true; } else if (dt >= st::radialDuration) { a_docRadial.update(1, anim::linear); result = true; -// _docRadialStart = 0; + _docRadialFirst = _docRadialLast = _docRadialStart = 0; + a_docRadial = anim::fvalue(0, 0); + if (!_doc->already().isEmpty() && _doc->size < MediaViewImageSizeLimit) { + QString fname(_doc->already(true)); + QImageReader reader(fname); + if (reader.canRead()) { + displayDocument(_doc, App::histItemById(_msgid)); + } + } } else { - a_docRadial.update(dt / st::radialDuration, anim::linear); + float64 r = dt / st::radialDuration; + a_docRadial.update(r, anim::linear); result = true; + _docRadialOpacity *= 1 - r; } - float64 fromstart = float64(ms - _docRadialFirst) / st::radialPeriod; + float64 fromstart = fulldt / st::radialPeriod; a_docRadialStart.update(fromstart - qFloor(fromstart), anim::linear); - update(_docIcon); + update(_docIconRect); } return result || !_animations.isEmpty(); } @@ -285,7 +383,6 @@ void MediaView::close() { } void MediaView::activateControls() { - LOG(("ACTIVATING CONTROLS!!")); _controlsHideTimer.start(int(st::mvWaitHide)); if (_controlsState == ControlsHiding || _controlsState == ControlsHidden) { _controlsState = ControlsShowing; @@ -306,13 +403,11 @@ void MediaView::onHideControls(bool force) { void MediaView::onDropdownHiding() { setFocus(); - LOG(("DROPDOWN HIDDEN")); _ignoringDropdown = true; _lastMouseMovePos = mapFromGlobal(QCursor::pos()); updateOver(_lastMouseMovePos); _ignoringDropdown = false; if (!_controlsHideTimer.isActive()) { - LOG((", STARTING CONTROLS HIDE")); onHideControls(true); } } @@ -327,23 +422,43 @@ void MediaView::onToMessage() { } } -void MediaView::onSave() { +void MediaView::onSaveAs() { QString file; if (_doc) { QString cur = _doc->already(true); if (cur.isEmpty()) { - _saveVisible = false; - update(_saveNav); + if (_current.isNull() && _currentGif.isNull()) { + DocumentSaveLink::doSave(_doc, true); + updateControls(); + } else { + _saveVisible = false; + update(_saveNav); + } + updateOver(_lastMouseMovePos); return; } + QFileInfo alreadyInfo(cur); + QDir alreadyDir(alreadyInfo.dir()); + QString name = alreadyInfo.fileName(), filter; + MimeType mimeType = mimeTypeForName(_doc->mime); + QStringList p = mimeType.globPatterns(); + QString pattern = p.isEmpty() ? QString() : p.front(); + if (name.isEmpty()) { + name = pattern.isEmpty() ? qsl(".unknown") : pattern.replace('*', QString()); + } + + if (pattern.isEmpty()) { + filter = qsl("All files (*.*)"); + } else { + filter = mimeType.filterString() + qsl(";;All files (*.*)"); + } + psBringToBack(this); - bool gotName = filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), cur); + file = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, true, alreadyDir); psShowOverAll(this); - if (gotName) { - if (!file.isEmpty() && file != cur) { - QFile(cur).copy(file); - } + if (!file.isEmpty() && file != cur) { + QFile(cur).copy(file); } } else { if (!_photo || !_photo->full->loaded()) return; @@ -359,9 +474,22 @@ void MediaView::onSave() { } } +void MediaView::onDocClick() { + QString fname = _doc->already(true); + if (fname.isEmpty()) { + if (_doc->loader) { + onSaveCancel(); + } else { + onDownload(); + } + } else { + psOpenFile(fname); + } +} + void MediaView::onDownload() { if (cAskDownloadPath()) { - return onSave(); + return onSaveAs(); } QString path; @@ -376,8 +504,14 @@ void MediaView::onDownload() { if (_doc) { QString cur = _doc->already(true); if (cur.isEmpty()) { - _saveVisible = false; - update(_saveNav); + if (_current.isNull() && _currentGif.isNull()) { + DocumentSaveLink::doSave(_doc); + updateControls(); + } else { + _saveVisible = false; + update(_saveNav); + } + updateOver(_lastMouseMovePos); } else { if (!QDir().exists(path)) QDir().mkpath(path); toName = filedialogNextFilename(_doc->name, cur, path); @@ -405,6 +539,12 @@ void MediaView::onDownload() { } } +void MediaView::onSaveCancel() { + if (_doc && _doc->loader) { + _doc->loader->cancel(); + } +} + void MediaView::onShowInFolder() { if (!_doc) return; QString already(_doc->already(true)); @@ -457,6 +597,8 @@ void MediaView::onCopy() { _dropdown.hideStart(); } if (_doc) { + if (_current.isNull()) return; + QApplication::clipboard()->setPixmap(_current); } else { if (!_photo || !_photo->full->loaded()) return; @@ -579,6 +721,7 @@ void MediaView::displayPhoto(PhotoData *photo) { MTP::clearLoaderPriorities(); _full = -1; _current = QPixmap(); + _currentGif.stop(); _down = OverNone; _w = convertScale(photo->full->width()); _h = convertScale(photo->full->height()); @@ -621,12 +764,31 @@ void MediaView::displayPhoto(PhotoData *photo) { void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { _doc = doc; - _docRadialFirst = _docRadialLast = _docRadialStart = _doc->loader ? getms() : 0; - QString already = _doc->already(true); - QPixmap pix = (_doc->sticker->isNull() || !_doc->sticker->loaded()) ? (already.isEmpty() ? QPixmap() : QPixmap::fromImage(App::readImage(already, 0, false), Qt::ColorOnly)) : _doc->sticker->pix(); - _current = pix; - if (_current.isNull()) { + if (!_doc->sticker->isNull() && _doc->sticker->loaded()) { + _currentGif.stop(); + _current = _doc->sticker->pix(); + } else if (!already.isEmpty()) { + QImageReader reader(already); + if (reader.canRead()) { + if (reader.supportsAnimation() && reader.imageCount() > 1) { + _currentGif.start(0, already); + _current = QPixmap(); + } else { + _currentGif.stop(); + QPixmap pix = QPixmap::fromImage(App::readImage(already, 0, false), Qt::ColorOnly); + _current = pix; + } + } else { + _currentGif.stop(); + _current = QPixmap(); + } + } else { + _currentGif.stop(); + _current = QPixmap(); + } + + if (_current.isNull() && _currentGif.isNull()) { if (_doc->thumb->isNull()) { style::sprite thumbs[] = { st::mvDocBlue, st::mvDocGreen, st::mvDocRed, st::mvDocYellow }; QString name = _doc->name.toLower(), mime = _doc->mime.toLower(); @@ -680,24 +842,38 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { int32 maxw = st::mvDocSize.width() - st::mvDocBlue.pxWidth() - st::mvDocPadding * 3; _docName = _doc->name.isEmpty() ? lang(_doc->type == StickerDocument ? lng_in_dlg_sticker : lng_mediaview_doc_image) : _doc->name; - _docNameWidth = st::mvThickFont->m.width(_docName); + int32 lastDot = _docName.lastIndexOf('.'); + _docExt = (lastDot < 0 || lastDot + 2 > _docName.size()) ? _docName : _docName.mid(lastDot + 1); + _docNameWidth = st::mvDocNameFont->m.width(_docName); if (_docNameWidth > maxw) { - _docName = st::mvThickFont->m.elidedText(_docName, Qt::ElideRight, maxw); - _docNameWidth = st::mvThickFont->m.width(_docName); + _docName = st::mvDocNameFont->m.elidedText(_docName, Qt::ElideMiddle, maxw); + _docNameWidth = st::mvDocNameFont->m.width(_docName); } - _docSize = formatSizeText(_doc->size); - _docSizeWidth = st::mvFont->m.width(_docSize); - if (_docSizeWidth > maxw) { - _docSize = st::mvFont->m.elidedText(_docSize, Qt::ElideRight, maxw); - _docSizeWidth = st::mvFont->m.width(_docSize); + int32 extmaxw = (st::mvDocBlue.pxWidth() - st::mvDocExtPadding * 2); + + _docExtWidth = st::mvDocExtFont->m.width(_docExt); + if (_docExtWidth > extmaxw) { + _docExt = st::mvDocNameFont->m.elidedText(_docExt, Qt::ElideMiddle, extmaxw); + _docExtWidth = st::mvDocNameFont->m.width(_docExt); } + _docRadialFirst = _docRadialLast = _docRadialStart = 0; + + float64 prg = _doc->loader ? _doc->loader->currentProgress() : 0; + a_docRadial = anim::fvalue(prg, qMax(prg, 0.0001)); + // _docSize is updated in updateControls() + _docRect = QRect((width() - st::mvDocSize.width()) / 2, (height() - st::mvDocSize.height()) / 2, st::mvDocSize.width(), st::mvDocSize.height()); - } else { + _docIconRect = rtlrect(_docRect.x() + st::mvDocPadding, _docRect.y() + st::mvDocPadding, st::mvDocBlue.pxWidth(), st::mvDocBlue.pxHeight(), width()); + } else if (!_current.isNull()) { _current.setDevicePixelRatio(cRetinaFactor()); _w = _current.width() / cIntRetinaFactor(); _h = _current.height() / cIntRetinaFactor(); + } else { + _currentGif.frames[_currentGif.frame].setDevicePixelRatio(cRetinaFactor()); + _w = _currentGif.frames[_currentGif.frame].width() / cIntRetinaFactor(); + _h = _currentGif.frames[_currentGif.frame].height() / cIntRetinaFactor(); } if (isHidden()) { moveToScreen(); @@ -776,21 +952,21 @@ void MediaView::paintEvent(QPaintEvent *e) { if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); } } - if (_photo || !_current.isNull()) { - p.setOpacity(1); - + p.setOpacity(1); + if (_photo || !_current.isNull() || !_currentGif.isNull()) { QRect imgRect(_x, _y, _w, _h); + const QPixmap *toDraw = _currentGif.isNull() ? &_current : &_currentGif.frames[_currentGif.frame]; if (imgRect.intersects(r)) { - if (_current.hasAlpha() && (!_doc || _doc->sticker->isNull())) { + if (toDraw->hasAlpha() && (!_doc || _doc->sticker->isNull())) { p.fillRect(imgRect, _transparentBrush); } if (_zoom) { bool was = (p.renderHints() & QPainter::SmoothPixmapTransform); if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform, true); - p.drawPixmap(QRect(_x, _y, _w, _h), _current); + p.drawPixmap(QRect(_x, _y, _w, _h), *toDraw); if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform, false); } else { - p.drawPixmap(_x, _y, _current); + p.drawPixmap(_x, _y, *toDraw); } uint64 ms = 0; @@ -849,43 +1025,65 @@ void MediaView::paintEvent(QPaintEvent *e) { } } } - } else { + } else if (_doc) { if (_docRect.intersects(r)) { p.fillRect(_docRect, st::mvDocBg->b); - QRect thumb = rtlrect(_docRect.x() + st::mvDocPadding, _docRect.y() + st::mvDocPadding, st::mvDocBlue.pxWidth(), st::mvDocBlue.pxHeight(), width()); - if (_doc->thumb->isNull()) { - p.drawPixmap(thumb.topLeft(), App::sprite(), _docIcon); - } else { - int32 rf(cIntRetinaFactor()); - p.drawPixmap(thumb.topLeft(), _doc->thumb->pix(_docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mvDocBlue.pxWidth() * rf, st::mvDocBlue.pxHeight() * rf)); + if (_docIconRect.intersects(r)) { + if (_doc->thumb->isNull()) { + p.drawPixmap(_docIconRect.topLeft(), App::sprite(), _docIcon); + if (!_doc->already().isEmpty() && (!_docRadialStart || _docRadialOpacity < 1)) { + p.setPen(st::mvDocExtColor->p); + p.setFont(st::mvDocExtFont->f); + p.drawText(_docIconRect.x() + (_docIconRect.width() - _docExtWidth) / 2, _docIconRect.y() + st::mvDocExtTop + st::mvDocExtFont->ascent, _docExt); + } + } else { + int32 rf(cIntRetinaFactor()); + p.drawPixmap(_docIconRect.topLeft(), _doc->thumb->pix(_docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mvDocBlue.pxWidth() * rf, st::mvDocBlue.pxHeight() * rf)); + } + + float64 o = overLevel(OverIcon); + if (_docRadialStart > 0) { + if (_doc->already().isEmpty() && _docRadialOpacity < 1) { + p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity) * (1 - _docRadialOpacity)); + p.drawSpriteCenter(_docIconRect, st::radialDownload); + } + + p.setRenderHint(QPainter::HighQualityAntialiasing); + + QRect inner(QPoint(_docIconRect.x() + ((_docIconRect.width() - st::radialSize.width()) / 2), _docIconRect.y() + ((_docIconRect.height() - st::radialSize.height()) / 2)), st::radialSize); + p.setPen(Qt::NoPen); + p.setBrush(st::black->b); + p.setOpacity(_docRadialOpacity * st::radialBgOpacity); + p.drawEllipse(inner); + + p.setOpacity((o * 1. + (1 - o) * st::radialCancelOpacity) * _docRadialOpacity); + p.drawSpriteCenter(_docIconRect, st::radialCancel); + + QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine))); + + p.setOpacity(_docRadialOpacity); + p.setPen(_docRadialPen); + + int len = 16 + a_docRadial.current() * 5744; + p.drawArc(arc, 1440 - a_docRadialStart.current() * 5760 - len, len); + + p.setOpacity(1); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + } else if (_doc->already().isEmpty()) { + p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity)); + p.drawSpriteCenter(_docIconRect, st::radialDownload); + } } - if (_doc && _docRadialStart > 0) { - p.setRenderHint(QPainter::HighQualityAntialiasing); + if (!_docIconRect.contains(r)) { + p.setPen(st::mvDocNameColor->p); + p.setFont(st::mvDocNameFont->f); + p.drawTextLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocNameTop, width(), _docName, _docNameWidth); - QRect inner(QPoint(thumb.x() + ((thumb.width() - st::radialSize.width()) / 2), thumb.y() + ((thumb.height() - st::radialSize.height()) / 2)), st::radialSize); - p.setPen(Qt::NoPen); - p.setBrush(st::black->b); - p.setOpacity(st::radialBgOpacity); - p.drawEllipse(inner); - - QRect arc(inner.marginsRemoved(QMargins(st::radialLine / 2, st::radialLine / 2, st::radialLine / 2, st::radialLine / 2))); - p.setOpacity(1); - - p.setPen(_docRadialPen); - - p.drawArc(arc, a_docRadialStart.current() * 5600, 10 + a_docRadial.current() * 5490); - - p.setRenderHint(QPainter::HighQualityAntialiasing, false); + p.setPen(st::mvDocSizeColor->p); + p.setFont(st::mvFont->f); + p.drawTextLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocSizeTop, width(), _docSize, _docSizeWidth); } - - p.setPen(st::mvDocNameColor->p); - p.setFont(st::mvThickFont->f); - p.drawTextLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocNameTop, width(), _docName, _docNameWidth); - - p.setPen(st::mvDocSizeColor->p); - p.setFont(st::mvFont->f); - p.drawTextLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocSizeTop, width(), _docSize, _docSizeWidth); } } @@ -1003,9 +1201,13 @@ void MediaView::keyPressEvent(QKeyEvent *e) { if (!_menu && e->key() == Qt::Key_Escape) { close(); } else if (e == QKeySequence::Save || e == QKeySequence::SaveAs) { - onSave(); + onSaveAs(); } else if (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier))) { onCopy(); + } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space) { + if (_doc && !_doc->loader && _current.isNull() && _currentGif.isNull()) { + onDocClick(); + } } else if (e->key() == Qt::Key_Left) { moveToNext(-1); } else if (e->key() == Qt::Key_Right) { @@ -1047,7 +1249,7 @@ void MediaView::keyPressEvent(QKeyEvent *e) { newZoom = 0; } _x = -_width / 2; - _y = -((_current.height() / cIntRetinaFactor()) / 2); + _y = -(((_currentGif.isNull() ? _current.height() : _currentGif.frames[_currentGif.frame].height()) / cIntRetinaFactor()) / 2); float64 z = (_zoom == ZoomToScreenLevel) ? _zoomToScreen : _zoom; if (z >= 0) { _x = qRound(_x * (z + 1)); @@ -1067,8 +1269,8 @@ void MediaView::keyPressEvent(QKeyEvent *e) { } if (_zoom != newZoom) { float64 nx, ny, z = (_zoom == ZoomToScreenLevel) ? _zoomToScreen : _zoom; - _w = _current.width() / cIntRetinaFactor(); - _h = _current.height() / cIntRetinaFactor(); + _w = (_currentGif.isNull() ? _current.width() : _currentGif.frames[_currentGif.frame].width()) / cIntRetinaFactor(); + _h = (_currentGif.isNull() ? _current.height() : _currentGif.frames[_currentGif.frame].height()) / cIntRetinaFactor(); if (z >= 0) { nx = (_x - width() / 2.) / (z + 1); ny = (_y - height() / 2.) / (z + 1); @@ -1195,6 +1397,8 @@ void MediaView::mousePressEvent(QMouseEvent *e) { _down = OverHeader; } else if (_over == OverSave) { _down = OverSave; + } else if (_over == OverIcon) { + _down = OverIcon; } else if (_over == OverMore) { _down = OverMore; } else if (_over == OverClose) { @@ -1227,7 +1431,6 @@ void MediaView::snapXY() { void MediaView::mouseMoveEvent(QMouseEvent *e) { bool moved = (e->pos() != _lastMouseMovePos); - LOG(("MOUSE MOVE: WAS %1 %2 NOW %3 %4 MOVED: %5").arg(_lastMouseMovePos.x()).arg(_lastMouseMovePos.y()).arg(e->pos().x()).arg(e->pos().y()).arg(logBool(moved))); _lastMouseMovePos = e->pos(); updateOver(e->pos()); @@ -1258,7 +1461,6 @@ void MediaView::mouseMoveEvent(QMouseEvent *e) { bool MediaView::updateOverState(OverState newState) { bool result = true; if (_over != newState) { - LOG(("UPDATING STATE TO %1, IGNORING: %2").arg(newState).arg(logBool(_ignoringDropdown))); if (newState == OverMore && !_ignoringDropdown) { QTimer::singleShot(0, this, SLOT(onDropdown())); } else if (newState == OverNone) { @@ -1272,6 +1474,8 @@ bool MediaView::updateOverState(OverState newState) { update(_dateNav); } else if (_over == OverSave) { update(_saveNav); + } else if (_over == OverIcon) { + update(_docIconRect); } else if (_over == OverHeader) { update(_headerNav); } else if (_over == OverClose) { @@ -1349,6 +1553,10 @@ void MediaView::updateOver(const QPoint &pos) { if (!updateOverState(OverSave)) { update(_saveNav); } + } else if (_doc && _current.isNull() && _currentGif.isNull() && _docIconRect.contains(pos)) { + if (!updateOverState(OverIcon)) { + update(_docIconRect); + } } else if (_moreNav.contains(pos)) { if (!updateOverState(OverMore)) { update(_moreNav); @@ -1379,6 +1587,8 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) { onOverview(); } else if (_over == OverSave && _down == OverSave) { onDownload(); + } else if (_over == OverIcon && _down == OverIcon) { + onDocClick(); } else if (_over == OverMore && _down == OverMore) { QTimer::singleShot(0, this, SLOT(onDropdown())); } else if (_over == OverClose && _down == OverClose) { @@ -1393,7 +1603,7 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) { } _dragging = 0; setCursor(style::cur_default); - } else if ((e->pos() - _lastAction).manhattanLength() >= st::mvDeltaFromLastAction) { + } else if ((e->pos() - _lastAction).manhattanLength() >= st::mvDeltaFromLastAction && (!_doc || !_current.isNull() || !_currentGif.isNull() || !_docRect.contains(e->pos()))) { close(); } _pressed = false; diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 79a02a72e5..103a490160 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -56,7 +56,9 @@ public: } void mediaOverviewUpdated(PeerData *peer); + void documentUpdated(DocumentData *doc); void changingMsgId(HistoryItem *row, MsgId newId); + void updateDocSize(); void updateControls(); void updateDropdown(); @@ -66,6 +68,7 @@ public: void close(); void activateControls(); + void onDocClick(); ~MediaView(); @@ -75,8 +78,9 @@ public slots: void onDropdownHiding(); void onToMessage(); - void onSave(); + void onSaveAs(); void onDownload(); + void onSaveCancel(); void onShowInFolder(); void onForward(); void onDelete(); @@ -91,6 +95,7 @@ public slots: void onTouchTimer(); void updateImage(); + void onGifUpdated(); private: @@ -125,16 +130,19 @@ private: bool _pressed; int32 _dragging; QPixmap _current; + AnimatedGif _currentGif; int32 _full; // -1 - thumb, 0 - medium, 1 - full style::sprite _docIcon; - QString _docName, _docSize; - int32 _docNameWidth, _docSizeWidth; - QRect _docRect; + QString _docName, _docSize, _docExt; + int32 _docNameWidth, _docSizeWidth, _docExtWidth; + QRect _docRect, _docIconRect; int32 _docThumbx, _docThumby, _docThumbw; uint64 _docRadialFirst, _docRadialStart, _docRadialLast; + float64 _docRadialOpacity; QPen _docRadialPen; anim::fvalue a_docRadial, a_docRadialStart; + LinkButton _docDownload, _docSaveAs, _docCancel; History *_history; // if conversation photos or files overview PeerData *_peer; @@ -158,6 +166,7 @@ private: OverDate, OverSave, OverMore, + OverIcon, }; OverState _over, _down; QPoint _lastAction, _lastMouseMovePos; @@ -176,7 +185,7 @@ private: ContextMenu *_menu; Dropdown _dropdown; - IconedButton *_btnToMessage, *_btnShowInFolder, *_btnSaveAs, *_btnCopy, *_btnForward, *_btnDelete, *_btnViewAll; + IconedButton *_btnSaveCancel, *_btnToMessage, *_btnShowInFolder, *_btnSaveAs, *_btnCopy, *_btnForward, *_btnDelete, *_btnViewAll; QList<IconedButton*> _btns; bool _receiveMouse; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 980fb4fb45..4d17ccba6d 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1225,9 +1225,9 @@ void OverviewInner::saveContextFile() { VideoLink *lnkVideo = dynamic_cast<VideoLink*>(_contextMenuLnk.data()); AudioLink *lnkAudio = dynamic_cast<AudioLink*>(_contextMenuLnk.data()); DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data()); - if (lnkVideo) VideoSaveLink(lnkVideo->video()).doSave(true); - if (lnkAudio) AudioSaveLink(lnkAudio->audio()).doSave(true); - if (lnkDocument) DocumentSaveLink(lnkDocument->document()).doSave(true); + if (lnkVideo) VideoSaveLink::doSave(lnkVideo->video(), true); + if (lnkAudio) AudioSaveLink::doSave(lnkAudio->audio(), true); + if (lnkDocument) DocumentSaveLink::doSave(lnkDocument->document(), true); } void OverviewInner::openContextFile() { @@ -1355,6 +1355,7 @@ void OverviewInner::mediaOverviewUpdated() { prevDate = date; } int32 w = _width - st::msgMargin.left() - st::msgMargin.right(); + media->initDimensions(item); y += media->countHeight(item, w) + st::msgMargin.top() + st::msgMargin.bottom(); // item height if (_items.size() > in) { _items[in].msgid = msgid; diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index a2821b219d..01b1b987d2 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -20,6 +20,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "settings.h" #include "lang.h" +bool gRtl = false; +Qt::LayoutDirection gLangDir = Qt::LeftToRight; + mtpDcOptions gDcOptions; bool gTestMode = false; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 0cf7eb7d05..5898e73605 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -41,6 +41,12 @@ inline void cSet##Name(const Type &Name) { \ g##Name = Name; \ } +DeclareSetting(bool, Rtl); +DeclareSetting(Qt::LayoutDirection, LangDir); +inline bool rtl() { + return cRtl(); +} + struct mtpDcOption { mtpDcOption(int _id, const string &_host, const string &_ip, int _port) : id(_id), host(_host), ip(_ip), port(_port) { } diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index ed30365cd5..bb7cc5f4ba 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -51,9 +51,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "mtproto/mtp.h" -#include "gui/twidget.h" - #include "gui/style_core.h" +#include "gui/twidget.h" #include "gui/animation.h" #include "gui/flatinput.h" #include "gui/flattextarea.h" diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index f797f9336a..cdcf7b5d46 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -230,7 +230,7 @@ void PhotoLink::onClick(Qt::MouseButton button) const { } } -QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir = QDir()) { +QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir) { #ifdef Q_OS_WIN name = name.replace(QRegularExpression(qsl("[\\\\\\/\\:\\*\\?\\\"\\<\\>\\|]")), qsl("_")); #elif defined Q_OS_MAC @@ -307,8 +307,7 @@ void VideoOpenLink::onClick(Qt::MouseButton button) const { } } -void VideoSaveLink::doSave(bool forceSavingAs) const { - VideoData *data = video(); +void VideoSaveLink::doSave(VideoData *data, bool forceSavingAs) { if (!data->user && !data->date) return; QString already = data->already(true); @@ -336,7 +335,7 @@ void VideoSaveLink::doSave(bool forceSavingAs) const { void VideoSaveLink::onClick(Qt::MouseButton button) const { if (button != Qt::LeftButton) return; - doSave(); + doSave(video()); } void VideoCancelLink::onClick(Qt::MouseButton button) const { @@ -399,8 +398,7 @@ void AudioOpenLink::onClick(Qt::MouseButton button) const { } } -void AudioSaveLink::doSave(bool forceSavingAs) const { - AudioData *data = audio(); +void AudioSaveLink::doSave(AudioData *data, bool forceSavingAs) { if (!data->user && !data->date) return; QString already = data->already(true); @@ -429,7 +427,7 @@ void AudioSaveLink::doSave(bool forceSavingAs) const { void AudioSaveLink::onClick(Qt::MouseButton button) const { if (button != Qt::LeftButton) return; - doSave(); + doSave(audio()); } void AudioCancelLink::onClick(Qt::MouseButton button) const { @@ -505,8 +503,7 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const { } } -void DocumentSaveLink::doSave(bool forceSavingAs) const { - DocumentData *data = document(); +void DocumentSaveLink::doSave(DocumentData *data, bool forceSavingAs) { if (!data->date) return; QString already = data->already(true); @@ -547,7 +544,7 @@ void DocumentSaveLink::doSave(bool forceSavingAs) const { void DocumentSaveLink::onClick(Qt::MouseButton button) const { if (button != Qt::LeftButton) return; - doSave(); + doSave(document()); } void DocumentCancelLink::onClick(Qt::MouseButton button) const { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 0b35db792f..8ba6657a94 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -291,7 +291,7 @@ class VideoSaveLink : public VideoLink { public: VideoSaveLink(VideoData *video) : VideoLink(video) { } - void doSave(bool forceSavingAs = false) const; + static void doSave(VideoData *video, bool forceSavingAs = false); void onClick(Qt::MouseButton button) const; }; @@ -378,7 +378,7 @@ class AudioSaveLink : public AudioLink { public: AudioSaveLink(AudioData *audio) : AudioLink(audio) { } - void doSave(bool forceSavingAs = false) const; + static void doSave(AudioData *audio, bool forceSavingAs = false); void onClick(Qt::MouseButton button) const; }; @@ -481,7 +481,7 @@ class DocumentSaveLink : public DocumentLink { public: DocumentSaveLink(DocumentData *document) : DocumentLink(document) { } - void doSave(bool forceSavingAs = false) const; + static void doSave(DocumentData *document, bool forceSavingAs = false); void onClick(Qt::MouseButton button) const; }; @@ -528,6 +528,7 @@ struct WebPageData { int32 pendingTill; }; +QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir = QDir()); MsgId clientMsgId(); struct MessageCursor { diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 8c07243726..fb19b8ee1f 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -1667,6 +1667,11 @@ void Window::mediaOverviewUpdated(PeerData *peer) { _mediaView->mediaOverviewUpdated(peer); } +void Window::documentUpdated(DocumentData *doc) { + if (!_mediaView || _mediaView->isHidden()) return; + _mediaView->documentUpdated(doc); +} + void Window::changingMsgId(HistoryItem *row, MsgId newId) { if (main) main->changingMsgId(row, newId); if (!_mediaView || _mediaView->isHidden()) return; diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index 595379c441..091a97af4f 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -227,6 +227,7 @@ public: void sendPaths(); void mediaOverviewUpdated(PeerData *peer); + void documentUpdated(DocumentData *doc); void changingMsgId(HistoryItem *row, MsgId newId); bool isActive(bool cached = true) const;