diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 5361d5fb1d..9a70915d57 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -554,8 +554,8 @@ taDefFlat: flatTextarea { font: inpDefFont; cursor: cursor(text); - phColor: #AAA; - phFocusColor: #CCC; + phColor: #999; + phFocusColor: #AAA; phAlign: align(topleft); phPos: point(2px, 0px); phShift: 50px; @@ -1595,7 +1595,7 @@ profileMinBtnPadding: 10px; membersPadding: margins(0px, 10px, 0px, 10px); forwardMargins: margins(30px, 10px, 30px, 10px); -forwardFont: font(16px); +forwardFont: font(16px); forwardBg: rgba(0, 0, 0, 76); btnProfileCancel: flatButton(btnDefFlat, btnDefBig) { color: #666d78; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 18ce12d35a..d8cf7f345b 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1443,6 +1443,18 @@ namespace App { return 0; } + void updateImage(ImagePtr &old, ImagePtr now) { + if (now->isNull()) return; + if (old->isNull()) { + old = now; + } else if (DelayedStorageImage *img = old->toDelayedStorageImage()) { + StorageImageLocation loc = now->location(); + if (!loc.isNull()) { + img->setStorageLocation(loc); + } + } + } + PhotoData *photo(const PhotoId &photo) { PhotosData::const_iterator i = ::photosData.constFind(photo); if (i == ::photosData.cend()) { @@ -1465,9 +1477,9 @@ namespace App { if (date) { convert->access = access; convert->date = date; - if (convert->thumb->isNull() && !thumb->isNull()) convert->thumb = thumb; - if (convert->medium->isNull() && !medium->isNull()) convert->medium = medium; - if (convert->full->isNull() && !full->isNull()) convert->full = full; + updateImage(convert->thumb, thumb); + updateImage(convert->medium, medium); + updateImage(convert->full, full); } } PhotosData::const_iterator i = ::photosData.constFind(photo); @@ -1485,9 +1497,9 @@ namespace App { if (result != convert && date) { result->access = access; result->date = date; - if (result->thumb->isNull() && !thumb->isNull()) result->thumb = thumb; - if (result->medium->isNull() && !medium->isNull()) result->medium = medium; - if (result->full->isNull() && !full->isNull()) result->full = full; + updateImage(result->thumb, thumb); + updateImage(result->medium, medium); + updateImage(result->full, full); } inLastIter = lastPhotosMap.find(result); } @@ -1526,9 +1538,7 @@ namespace App { if (date) { convert->access = access; convert->date = date; - if (convert->thumb->isNull() && !thumb->isNull()) { - convert->thumb = thumb; - } + updateImage(convert->thumb, thumb); convert->duration = duration; convert->w = w; convert->h = h; @@ -1553,9 +1563,7 @@ namespace App { result->duration = duration; result->w = w; result->h = h; - if (result->thumb->isNull() && !thumb->isNull()) { - result->thumb = thumb; - } + updateImage(result->thumb, thumb); result->dc = dc; result->size = size; } @@ -1632,9 +1640,6 @@ namespace App { Local::copyStickerImage(mediaKey(DocumentFileLocation, convert->dc, convert->id), mediaKey(DocumentFileLocation, dc, document)); convert->id = document; convert->status = FileReady; - if (cSavedGifs().indexOf(convert) >= 0) { // id changed - Local::writeSavedGifs(); - } sentSticker = !!convert->sticker(); } if (date) { @@ -1652,6 +1657,11 @@ namespace App { convert->sticker()->loc = thumbLocation; } } + + if (cSavedGifs().indexOf(convert) >= 0) { // id changed + Local::writeSavedGifs(); + } + const FileLocation &loc(convert->location(true)); if (!loc.isEmpty()) { Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), loc); diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index 7aa7e88d53..f2c24a9e38 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -330,6 +330,7 @@ void AutoDownloadBox::onSave() { for (DocumentsData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) { i.value()->automaticLoadSettingsChanged(); } + Notify::automaticLoadSettingsChangedGif(); } changed = true; } diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 82baaddb08..aa5a38aa36 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -1143,7 +1143,7 @@ void EmojiPanInner::updateSelected() { bool startanim = false; int oldSel = _selected, newSel = selIndex; - + if (newSel != oldSel) { if (oldSel >= 0) { _animations.remove(oldSel + 1); @@ -1229,7 +1229,7 @@ StickerPanInner::StickerPanInner() : TWidget() connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); - + _previewTimer.setSingleShot(true); connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); @@ -1252,7 +1252,7 @@ void StickerPanInner::setScrollTop(int top) { } int StickerPanInner::countHeight() { - int result = 0, minLastH = _maxHeight - st::rbEmoji.height - st::stickerPanPadding; + int result = 0, minLastH = _maxHeight - st::stickerPanPadding; if (_showingInlineItems) { result = st::emojiPanHeader; for (int i = 0, l = _inlineRows.count(); i < l; ++i) { @@ -1560,6 +1560,10 @@ void StickerPanInner::enterFromChildEvent(QEvent *e) { updateSelected(); } +bool StickerPanInner::showSectionIcons() const { + return !inlineResultsShown(); +} + void StickerPanInner::clearSelection(bool fast) { _lastMousePos = mapToGlobal(QPoint(-10, -10)); if (fast) { @@ -2347,6 +2351,7 @@ void StickerPanInner::showStickerSet(uint64 setId) { refreshSavedGifs(); emit scrollToY(0); emit scrollUpdated(); + _setGifCommand = App::insertBotCommand(qsl("@gif")); return; } @@ -2447,9 +2452,28 @@ EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button( updateText(); } -void EmojiSwitchButton::updateText() { - _text = lang(_toStickers ? (cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs) : lng_switch_emoji); +void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { + if (_toStickers) { + if (inlineBotUsername.isEmpty()) { + _text = lang(cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs); + } else { + _text = '@' + inlineBotUsername; + } + } else { + _text = lang(lng_switch_emoji); + } _textWidth = st::emojiPanHeaderFont->width(_text); + if (_toStickers && !inlineBotUsername.isEmpty()) { + int32 maxw = 0; + for (int c = 0; c < emojiTabCount; ++c) { + maxw = qMax(maxw, st::emojiPanHeaderFont->width(lang(LangKey(lng_emoji_category0 + c)))); + } + maxw += st::emojiPanHeaderLeft + st::emojiSwitchSkip + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); + if (_textWidth > st::emojiPanWidth - maxw) { + _text = st::emojiPanHeaderFont->elided(_text, st::emojiPanWidth - maxw); + _textWidth = st::emojiPanHeaderFont->width(_text); + } + } int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); resize(w, st::emojiPanHeader); @@ -2471,6 +2495,8 @@ void EmojiSwitchButton::paintEvent(QPaintEvent *e) { EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) , _maxHeight(st::emojiPanMaxHeight) +, _maxHeightEmoji(_maxHeight - st::rbEmoji.height) +, _maxHeightStickers(_maxHeight - st::rbEmoji.height) , _horizontal(false) , _noTabUpdate(false) , _hiding(false) @@ -2518,8 +2544,8 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) _height = st::dropdownDef.padding.top() + _maxHeight + st::dropdownDef.padding.bottom(); resize(_width, _height); - e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); - s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); + e_scroll.resize(st::emojiPanWidth, _maxHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _maxHeightStickers); e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); e_scroll.setWidget(&e_inner); @@ -2588,24 +2614,28 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) void EmojiPan::setMaxHeight(int32 h) { h = qMin(int(st::emojiPanMaxHeight), h); - if (h == _maxHeight) return; + int32 he = h - st::rbEmoji.height; + int32 hs = h - (s_inner.showSectionIcons() ? st::rbEmoji.height : 0); + if (h == _maxHeight && he == _maxHeightEmoji && hs == _maxHeightStickers) return; - int32 was = _maxHeight; + int32 was = _maxHeight, wase = _maxHeightEmoji, wass = _maxHeightStickers; _maxHeight = h; + _maxHeightEmoji = he; + _maxHeightStickers = hs; _height = st::dropdownDef.padding.top() + _maxHeight + st::dropdownDef.padding.bottom(); resize(_width, _height); - if (was > _maxHeight) { - e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); - s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); - s_inner.setMaxHeight(_maxHeight); - e_inner.setMaxHeight(_maxHeight); + if (was > _maxHeight || (was == _maxHeight && wass > _maxHeightStickers)) { + e_scroll.resize(st::emojiPanWidth, _maxHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _maxHeightStickers); + s_inner.setMaxHeight(_maxHeightStickers); + e_inner.setMaxHeight(_maxHeightEmoji); } else { - s_inner.setMaxHeight(_maxHeight); - e_inner.setMaxHeight(_maxHeight); - e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); - s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); + s_inner.setMaxHeight(_maxHeightStickers); + e_inner.setMaxHeight(_maxHeightEmoji); + e_scroll.resize(st::emojiPanWidth, _maxHeightEmoji); + s_scroll.resize(st::emojiPanWidth, _maxHeightStickers); } _iconsTop = st::dropdownDef.padding.top() + _maxHeight - st::rbEmoji.height; @@ -2657,7 +2687,7 @@ void EmojiPan::paintEvent(QPaintEvent *e) { if (_toCache.isNull()) { if (_cache.isNull()) { p.fillRect(myrtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height()), st::white->b); - if (_stickersShown) { + if (_stickersShown && s_inner.showSectionIcons()) { p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories->b); p.drawSpriteLeft(_iconsLeft + 7 * st::rbEmoji.width + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings); @@ -2748,6 +2778,18 @@ void EmojiPan::paintEvent(QPaintEvent *e) { } } +void EmojiPan::moveBottom(int32 bottom, bool force) { + if (isHidden() && !force) { + move(x(), bottom - height()); + return; + } + if (_stickersShown && s_inner.inlineResultsShown()) { + moveToLeft(0, bottom - height()); + } else { + moveToRight(0, bottom - height()); + } +} + void EmojiPan::enterEvent(QEvent *e) { _hideTimer.stop(); if (_hiding) showStart(); @@ -2919,7 +2961,10 @@ void EmojiPan::onRefreshIcons() { } updatePanelsPositions(s_panels, s_scroll.scrollTop()); updateSelected(); - if (_stickersShown) validateSelectedIcon(); + if (_stickersShown) { + validateSelectedIcon(); + setMaxHeight(_maxHeight); + } updateIcons(); } @@ -2995,6 +3040,8 @@ void EmojiPan::updateSelected() { } void EmojiPan::updateIcons() { + if (!_stickersShown || !s_inner.showSectionIcons()) return; + QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); } @@ -3108,6 +3155,7 @@ void EmojiPan::hideFinish() { _cache = _toCache = _fromCache = QPixmap(); _a_slide.stop(); _horizontal = false; + _hiding = false; e_scroll.scrollToY(0); if (!_recent.checked()) { @@ -3129,16 +3177,26 @@ void EmojiPan::hideFinish() { } void EmojiPan::showStart() { - if (!isHidden() && a_opacity.current() == 1) { + if (!isHidden() && !_hiding) { return; } if (isHidden()) { e_inner.refreshRecent(); - s_inner.refreshRecent(); + if (s_inner.inlineResultsShown() && refreshInlineRows()) { + _stickersShown = true; + } else { + s_inner.refreshRecent(); + _stickersShown = false; + } s_inner.preloadImages(); - _stickersShown = false; + setMaxHeight(_maxHeight); _fromCache = _toCache = QPixmap(); _a_slide.stop(); + moveBottom(y() + height(), true); + } else if (_hiding) { + if (s_inner.inlineResultsShown() && refreshInlineRows()) { + onSwitch(); + } } if (_cache.isNull()) { QPixmap from = _fromCache, to = _toCache; @@ -3170,9 +3228,10 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { //} } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) { if (isHidden() || _hiding) { - otherEnter(); + _hideTimer.stop(); + showStart(); } else { - otherLeave(); + hideAnimated(); } } return false; @@ -3181,6 +3240,7 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { void EmojiPan::stickersInstalled(uint64 setId) { _stickersShown = true; if (isHidden()) { + moveBottom(y() + height(), true); show(); a_opacity = anim::fvalue(0, 1); a_opacity.update(0, anim::linear); @@ -3188,6 +3248,7 @@ void EmojiPan::stickersInstalled(uint64 setId) { } showAll(); s_inner.showStickerSet(setId); + setMaxHeight(_maxHeight); showStart(); } @@ -3211,6 +3272,14 @@ bool EmojiPan::ui_isInlineItemBeingChosen() { return false; } +void EmojiPan::notify_automaticLoadSettingsChangedGif() { + for (InlineCache::const_iterator i = _inlineCache.cbegin(), ei = _inlineCache.cend(); i != ei; ++i) { + for (InlineResults::const_iterator j = i.value()->results.cbegin(), ej = i.value()->results.cend(); j != ej; ++j) { + (*j)->automaticLoadSettingsChangedGif(); + } + } +} + void EmojiPan::showAll() { if (_stickersShown) { s_scroll.show(); @@ -3344,20 +3413,20 @@ void EmojiPan::onSwitch() { _stickersShown = !_stickersShown; if (!_stickersShown) { Notify::clipStopperHidden(ClipStopperSavedGifsPanel); - } - - if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { - s_inner.showStickerSet(DefaultStickerSetId); - } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && cStickerSets().isEmpty()) { - s_inner.showStickerSet(NoneStickerSetId); + } else { + if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { + s_inner.showStickerSet(DefaultStickerSetId); + } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && cStickerSets().isEmpty()) { + s_inner.showStickerSet(NoneStickerSetId); + } + validateSelectedIcon(); + setMaxHeight(_maxHeight); } _iconOver = -1; _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); _iconAnimations.clear(); _a_icons.stop(); - validateSelectedIcon(); - _cache = QPixmap(); showAll(); _toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); @@ -3425,10 +3494,20 @@ void EmojiPan::onDelayedHide() { _removingSetId = 0; } +void EmojiPan::clearInlineBot() { + inlineBotChanged(); + e_switch.updateText(); + e_switch.moveToRight(0, 0, st::emojiPanWidth); +} + void EmojiPan::inlineBotChanged() { if (!_inlineBot) return; - if (!isHidden()) hideAnimated(); + if (!isHidden() && !_hiding) { + if (_stickersShown || !rect().contains(mapFromGlobal(QCursor::pos()))) { + hideAnimated(); + } + } if (_inlineRequestId) MTP::cancel(_inlineRequestId); _inlineRequestId = 0; @@ -3555,6 +3634,7 @@ void EmojiPan::queryInlineBot(UserData *bot, QString query) { _inlineRequestId = 0; } if (_inlineCache.contains(query)) { + _inlineRequestTimer.stop(); _inlineQuery = query; showInlineRows(true); } else { @@ -3565,7 +3645,7 @@ void EmojiPan::queryInlineBot(UserData *bot, QString query) { } void EmojiPan::onInlineRequest() { - if (_inlineRequestId) return; + if (_inlineRequestId || !_inlineBot) return; _inlineQuery = _inlineNextQuery; QString nextOffset; @@ -3577,7 +3657,7 @@ void EmojiPan::onInlineRequest() { _inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(_inlineBot->inputUser, MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail)); } -void EmojiPan::showInlineRows(bool newResults) { +bool EmojiPan::refreshInlineRows() { bool clear = true; InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery); if (i != _inlineCache.cend()) { @@ -3586,26 +3666,25 @@ void EmojiPan::showInlineRows(bool newResults) { } s_inner.refreshInlineRows(_inlineBot, clear ? InlineResults() : i.value()->results, false); + return !clear; +} + +void EmojiPan::showInlineRows(bool newResults) { + bool clear = !refreshInlineRows(); if (newResults) s_scroll.scrollToY(0); - if (clear && !isHidden() && _stickersShown && s_inner.inlineResultsShown()) { + + e_switch.updateText(clear ? QString() : _inlineBot->username); + e_switch.moveToRight(0, 0, st::emojiPanWidth); + + bool hidden = isHidden(); + if (clear && !hidden && _stickersShown && s_inner.inlineResultsShown()) { hideAnimated(); } else if (!clear) { _hideTimer.stop(); - if (!isHidden() || _hiding) { - if (!_stickersShown) { - onSwitch(); - } - } else { - _stickersShown = true; - if (isHidden()) { - show(); - a_opacity = anim::fvalue(0, 1); - a_opacity.update(0, anim::linear); - _cache = _fromCache = _toCache = QPixmap(); - } - } - if (isHidden() || _hiding) { + if (hidden || _hiding) { showStart(); + } else if (!_stickersShown) { + onSwitch(); } } } @@ -3662,7 +3741,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) { user->photo->load(); p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize)); user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); - + p.setFont(st::mentionFont->f); p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); @@ -4094,7 +4173,7 @@ void MentionsDropdown::setBoundings(QRect boundings) { void MentionsDropdown::recount(bool toDown) { int32 h = (_mrows.isEmpty() ? (_hrows.isEmpty() ? _brows.size() : _hrows.size()) : _mrows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst; - + if (_inner.height() != h) { // st += h - _inner.height(); _inner.resize(width(), h); diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index 2387cc094c..6c186b3b63 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -254,7 +254,7 @@ public: void fillPanels(QVector &panels); void refreshPanels(QVector &panels); - + public slots: void updateSelected(); @@ -336,6 +336,7 @@ public: void hideFinish(); void showStickerSet(uint64 setId); + bool showSectionIcons() const; void clearSelection(bool fast = false); void refreshStickers(); @@ -398,7 +399,7 @@ private: void paintInlineItems(Painter &p, const QRect &r); void paintStickers(Painter &p, const QRect &r); - + int32 _maxHeight; void appendSet(uint64 setId); @@ -514,7 +515,7 @@ public: EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji void paintEvent(QPaintEvent *e); - void updateText(); + void updateText(const QString &inlineBotUsername = QString()); protected: @@ -534,6 +535,8 @@ public: void setMaxHeight(int32 h); void paintEvent(QPaintEvent *e); + void moveBottom(int32 bottom, bool force = false); + void enterEvent(QEvent *e); void leaveEvent(QEvent *e); void otherEnter(); @@ -558,7 +561,7 @@ public: void stickersInstalled(uint64 setId); void queryInlineBot(UserData *bot, QString query); - void inlineBotChanged(); + void clearInlineBot(); bool overlaps(const QRect &globalRect) { if (isHidden() || !_cache.isNull()) return false; @@ -574,6 +577,8 @@ public: bool ui_isInlineItemVisible(const LayoutInlineItem *layout); bool ui_isInlineItemBeingChosen(); + void notify_automaticLoadSettingsChangedGif(); + public slots: void refreshStickers(); @@ -614,7 +619,7 @@ private: void validateSelectedIcon(bool animated = false); - int32 _maxHeight; + int32 _maxHeight, _maxHeightEmoji, _maxHeightStickers; bool _horizontal; void leaveToChildEvent(QEvent *e); @@ -693,7 +698,9 @@ private: InlineCache _inlineCache; QTimer _inlineRequestTimer; + void inlineBotChanged(); void showInlineRows(bool newResults); + bool refreshInlineRows(); UserData *_inlineBot; QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; mtpRequestId _inlineRequestId; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index cb9770ddaf..99bbba7cf6 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -164,4 +164,8 @@ namespace Notify { if (MainWidget *m = App::main()) m->notify_historyItemLayoutChanged(item); } + void automaticLoadSettingsChangedGif() { + if (MainWidget *m = App::main()) m->notify_automaticLoadSettingsChangedGif(); + } + } diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index c977520b36..d4a0890bfd 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -90,4 +90,6 @@ namespace Notify { } void historyItemLayoutChanged(const HistoryItem *item); + void automaticLoadSettingsChangedGif(); + }; diff --git a/Telegram/SourceFiles/gui/flattextarea.cpp b/Telegram/SourceFiles/gui/flattextarea.cpp index 755241e2b8..c204ed6843 100644 --- a/Telegram/SourceFiles/gui/flattextarea.cpp +++ b/Telegram/SourceFiles/gui/flattextarea.cpp @@ -30,6 +30,7 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const , _maxLength(-1) , _ctrlEnterSubmit(true) , _oldtext(v) +, _phAfter(0) , _phVisible(!v.length()) , a_phLeft(_phVisible ? 0 : st.phShift) , a_phAlpha(_phVisible ? 1 : 0) @@ -47,10 +48,10 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const , _correcting(false) { setAcceptRichText(false); resize(_st.width, _st.font->height); - + setFont(_st.font->f); setAlignment(_st.align); - + setPlaceholder(pholder); QPalette p(palette()); @@ -87,8 +88,21 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const } } -void FlatTextarea::setTextFast(const QString &text) { - setPlainText(text); +void FlatTextarea::setTextFast(const QString &text, bool withUndo) { + if (withUndo) { + QTextCursor c(document()->docHandle(), 0); + c.joinPreviousEditBlock(); + c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + c.insertText(text); + c.movePosition(QTextCursor::End); + c.endEditBlock(); + } else { + setPlainText(text); + } + finishPlaceholder(); +} + +void FlatTextarea::finishPlaceholder() { if (_a_appearance.animating()) { a_phLeft.finish(); a_phAlpha.finish(); @@ -206,10 +220,14 @@ void FlatTextarea::paintEvent(QPaintEvent *e) { if (phDraw) { p.save(); p.setClipRect(r); - QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom()); - p.setFont(_st.font->f); + p.setFont(_st.font); p.setPen(a_phColor.current()); - p.drawText(phRect, _ph, QTextOption(_st.phAlign)); + if (_st.phAlign == style::al_topleft && _phAfter > 0) { + p.drawText(_st.textMrg.left() - _fakeMargin + a_phLeft.current() + _st.font->width(getLastText().mid(0, _phAfter)), _st.textMrg.top() - _fakeMargin - st::lineWidth + _st.font->ascent, _ph); + } else { + QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom()); + p.drawText(phRect, _ph, QTextOption(_st.phAlign)); + } p.restore(); p.setOpacity(1); } @@ -241,7 +259,7 @@ EmojiPtr FlatTextarea::getSingleEmoji() const { QTextFragment fragment; getSingleEmojiFragment(text, fragment); - + if (!text.isEmpty()) { QTextCharFormat format = fragment.charFormat(); QString imageName = static_cast(&format)->name(); @@ -253,9 +271,6 @@ EmojiPtr FlatTextarea::getSingleEmoji() const { } void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&inlineBot, QString &inlineBotUsername) const { - int32 pos = textCursor().position(); - if (textCursor().anchor() != pos) return; - // check inline bot query const QString &text(getLastText()); int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size(); @@ -303,6 +318,9 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&i inlineBotUsername = QString(); } + int32 pos = textCursor().position(); + if (textCursor().anchor() != pos) return; + // check mention / hashtag / bot command QTextDocument *doc(document()); QTextBlock block = doc->findBlock(pos); @@ -764,7 +782,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) { emoji = 0; replacePosition = -1; - } else { + } else { break; } } @@ -888,14 +906,18 @@ void FlatTextarea::step_appearance(float64 ms, bool timer) { if (timer) update(); } -void FlatTextarea::setPlaceholder(const QString &ph) { +void FlatTextarea::setPlaceholder(const QString &ph, int32 afterSymbols) { _ph = ph; - _phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1); + if (_phAfter != afterSymbols) { + _phAfter = afterSymbols; + updatePlaceholder(); + } + _phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1 - (_phAfter ? _st.font->width(getLastText().mid(0, _phAfter)) : 0)); if (_phVisible) update(); } void FlatTextarea::updatePlaceholder() { - bool vis = getLastText().isEmpty(); + bool vis = (getLastText().size() <= _phAfter); if (vis == _phVisible) return; a_phLeft.start(vis ? 0 : _st.phShift); diff --git a/Telegram/SourceFiles/gui/flattextarea.h b/Telegram/SourceFiles/gui/flattextarea.h index f24bba43bb..8020a8d95d 100644 --- a/Telegram/SourceFiles/gui/flattextarea.h +++ b/Telegram/SourceFiles/gui/flattextarea.h @@ -51,8 +51,9 @@ public: const QString &getLastText() const { return _oldtext; } - void setPlaceholder(const QString &ph); + void setPlaceholder(const QString &ph, int32 afterSymbols = 0); void updatePlaceholder(); + void finishPlaceholder(); QRect getTextRect() const; int32 fakeMargin() const; @@ -78,7 +79,7 @@ public: QMimeData *createMimeDataFromSelection() const; void setCtrlEnterSubmit(bool ctrlEnterSubmit); - void setTextFast(const QString &text); + void setTextFast(const QString &text, bool withUndo = false); public slots: @@ -124,6 +125,7 @@ private: bool _ctrlEnterSubmit; QString _ph, _phelided, _oldtext; + int32 _phAfter; bool _phVisible; anim::ivalue a_phLeft; anim::fvalue a_phAlpha; diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index 450d37ba3a..7169462710 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -591,6 +591,10 @@ Image *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap return new Image(filecontent, format, pixmap); } +Image *getImage(int32 width, int32 height) { + return new DelayedStorageImage(width, height); +} + void clearStorageImages() { for (StorageImages::const_iterator i = storageImages.cbegin(), e = storageImages.cend(); i != e; ++i) { delete i.value(); @@ -644,6 +648,13 @@ void RemoteImage::doCheckload() const { _forgot = false; } +void RemoteImage::loadLocal() { + if (loaded() || amLoading()) return; + + _loader = createLoader(LoadFromLocalOnly, true); + if (_loader) _loader->start(); +} + void RemoteImage::setData(QByteArray &bytes, const QByteArray &bytesFormat) { QBuffer buffer(&bytes); @@ -781,6 +792,89 @@ FileLoader *StorageImage::createLoader(LoadFromCloudSetting fromCloud, bool auto return new mtpFileLoader(&_location, _size, fromCloud, autoLoading); } +DelayedStorageImage::DelayedStorageImage() : StorageImage(StorageImageLocation()) +, _loadRequested(false) +, _loadCancelled(false) +, _loadFromCloud(false) { +} + +DelayedStorageImage::DelayedStorageImage(int32 w, int32 h) : StorageImage(StorageImageLocation(w, h, 0, 0, 0, 0)) +, _loadRequested(false) +, _loadCancelled(false) +, _loadFromCloud(false) { +} + +DelayedStorageImage::DelayedStorageImage(QByteArray &bytes) : StorageImage(StorageImageLocation(), bytes) +, _loadRequested(false) +, _loadCancelled(false) +, _loadFromCloud(false) { +} + +void DelayedStorageImage::setStorageLocation(const StorageImageLocation location) { + _location = location; + if (_loadRequested) { + if (!_loadCancelled) { + if (_loadFromCloud) { + load(); + } else { + loadLocal(); + } + } + _loadRequested = false; + } +} + +void DelayedStorageImage::automaticLoad(const HistoryItem *item) { + if (_location.isNull()) { + if (!_loadCancelled && item) { + bool loadFromCloud = false; + if (item->history()->peer->isUser()) { + loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); + } else { + loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); + } + + if (_loadRequested) { + if (loadFromCloud) _loadFromCloud = loadFromCloud; + } else { + _loadFromCloud = loadFromCloud; + _loadRequested = true; + } + } + } else { + StorageImage::automaticLoad(item); + } +} + +void DelayedStorageImage::automaticLoadSettingsChanged() { + if (_loadCancelled) _loadCancelled = false; + StorageImage::automaticLoadSettingsChanged(); +} + +void DelayedStorageImage::load(bool loadFirst, bool prior) { + if (_location.isNull()) { + _loadRequested = _loadFromCloud = true; + } else { + StorageImage::load(loadFirst, prior); + } +} + +void DelayedStorageImage::loadEvenCancelled(bool loadFirst, bool prior) { + _loadCancelled = false; + StorageImage::loadEvenCancelled(loadFirst, prior); +} + +bool DelayedStorageImage::displayLoading() const { + return _location.isNull() ? true : StorageImage::displayLoading(); +} + +void DelayedStorageImage::cancel() { + if (_loadRequested) { + _loadRequested = false; + } + StorageImage::cancel(); +} + StorageImage *getImage(const StorageImageLocation &location, int32 size) { StorageKey key(storageKey(location)); StorageImages::const_iterator i = storageImages.constFind(key); diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 079bcefb0c..5ef9d93282 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -109,6 +109,8 @@ inline bool operator!=(const StorageImageLocation &a, const StorageImageLocation QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh); +class DelayedStorageImage; + class HistoryItem; class Image { public: @@ -173,7 +175,7 @@ public: } bool isNull() const; - + void forget() const; QByteArray savedFormat() const { @@ -183,6 +185,13 @@ public: return _saved; } + virtual DelayedStorageImage *toDelayedStorageImage() { + return 0; + } + virtual const DelayedStorageImage *toDelayedStorageImage() const { + return 0; + } + virtual ~Image(); protected: @@ -209,6 +218,7 @@ Image *getImage(const QString &file, QByteArray format); Image *getImage(const QByteArray &filecontent, QByteArray format); Image *getImage(const QPixmap &pixmap, QByteArray format); Image *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); +Image *getImage(int32 width, int32 height); typedef QPair StorageKey; inline uint64 storageMix32To64(int32 a, int32 b) { @@ -256,6 +266,7 @@ protected: void checkload() const { doCheckload(); } + void loadLocal(); private: mutable FileLoader *_loader; @@ -271,10 +282,10 @@ public: StorageImage(const StorageImageLocation &location, int32 size = 0); StorageImage(const StorageImageLocation &location, QByteArray &bytes); - + int32 width() const; int32 height() const; - + virtual void setInformation(int32 size, int32 width, int32 height); virtual FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading); @@ -282,12 +293,45 @@ public: return _location; } -private: +protected: StorageImageLocation _location; int32 _size; }; +class DelayedStorageImage : public StorageImage { +public: + + DelayedStorageImage(); + DelayedStorageImage(int32 w, int32 h); + DelayedStorageImage(QByteArray &bytes); + + void setStorageLocation(const StorageImageLocation location); + + virtual DelayedStorageImage *toDelayedStorageImage() { + return this; + } + virtual const DelayedStorageImage *toDelayedStorageImage() const { + return this; + } + + void automaticLoad(const HistoryItem *item); // auto load photo + void automaticLoadSettingsChanged(); + + bool loading() const { + return _location.isNull() ? _loadRequested : StorageImage::loading(); + } + bool displayLoading() const; + void cancel(); + + void load(bool loadFirst = false, bool prior = true); + void loadEvenCancelled(bool loadFirst = false, bool prior = true); + +private: + bool _loadRequested, _loadCancelled, _loadFromCloud; + +}; + StorageImage *getImage(const StorageImageLocation &location, int32 size = 0); StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes); Image *getImage(int32 width, int32 height, const MTPFileLocation &location); @@ -327,8 +371,22 @@ public: ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(getImage(location, bytes)) { } ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr()); + ImagePtr(int32 width, int32 height) : Parent(getImage(width, height)) { + } }; +inline QSize resizeKeepAspect(int32 width, int32 height, int32 towidth, int32 toheight) { + int32 w = qMax(width, 1), h = qMax(height, 1); + if (w * toheight > h * towidth) { + h = qRound(h * towidth / float64(w)); + w = towidth; + } else { + w = qRound(w * toheight / float64(h)); + h = toheight; + } + return QSize(qMax(w, 1), qMax(h, 1)); +} + void clearStorageImages(); void clearAllImages(); int64 imageCacheSize(); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 4dc01b085a..4546339f65 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -209,7 +209,7 @@ void FakeDialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBack QRect fullRect(0, 0, w, st::dlgHeight); p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b); if (onlyBackground) return; - + History *history = _item->history(); if (history->peer->migrateTo()) { p.drawPixmap(st::dlgPaddingHor, st::dlgPaddingVer, history->peer->migrateTo()->photo->pix(st::dlgPhotoSize)); @@ -2358,7 +2358,7 @@ void History::getNextShowFrom(HistoryBlock *block, int32 i) { void History::addUnreadBar() { if (unreadBar || !showFrom || showFrom->detached() || !unreadCount) return; - + int32 count = unreadCount; if (peer->migrateTo()) { if (History *h = App::historyLoaded(peer->migrateTo()->id)) { @@ -2842,7 +2842,7 @@ void HistoryBlock::removeItem(HistoryItem *item) { nextCollapse->destroy(); } } - + // fix date items HistoryItem *nextItem = (i < items.size() - 1) ? items[i + 1] : ((myIndex < history->blocks.size() - 1) ? history->blocks[myIndex + 1]->items[0] : 0); if (nextItem && nextItem == history->unreadBar) { // skip unread bar @@ -2991,7 +2991,7 @@ void HistoryItem::setId(MsgId newId) { void HistoryItem::clipCallback(ClipReaderNotification notification) { HistoryMedia *media = getMedia(); if (!media) return; - + ClipReader *reader = media ? media->getClipReader() : 0; if (!reader) return; @@ -4040,7 +4040,7 @@ HistoryDocument::HistoryDocument(DocumentData *document, const QString &caption, , _namew(st::semiboldFont->width(_name)) , _caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) { setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); - + setStatusSize(FileStatusSizeReady); if (!caption.isEmpty()) { @@ -4152,7 +4152,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r statustop = st::msgFileThumbStatusTop; linktop = st::msgFileThumbLinkTop; bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); - + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); if (_data->thumb->loaded()) { QPixmap thumb = loaded ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize); @@ -4432,6 +4432,7 @@ HistoryGif::HistoryGif(DocumentData *document, const QString &caption, const His } HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia() +, _parent(0) , _data(other._data) , _thumbw(other._thumbw) , _thumbh(other._thumbh) @@ -4442,6 +4443,7 @@ HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia() } void HistoryGif::initDimensions(const HistoryItem *parent) { + _parent = parent; if (_caption.hasSkipBlock()) { _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight()); } @@ -4556,7 +4558,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; _data->automaticLoad(parent); - bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); + bool loaded = _data->loaded(), displayLoading = (parent->id < 0) || _data->displayLoading(); if (loaded && !gif() && _gif != BadClipReader && cAutoPlayGif()) { const_cast(this)->playInline(const_cast(parent)); if (gif()) _gif->setAutoplay(); @@ -4570,11 +4572,11 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo bool animating = (gif() && _gif->started()); - if (!animating || _data->uploading()) { + if (!animating || parent->id < 0) { if (displayLoading) { ensureAnimation(parent); if (!_animation->radial.animating()) { - _animation->radial.start(_data->progress()); + _animation->radial.start(dataProgress()); } } updateStatusText(parent); @@ -4606,7 +4608,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo } if (radial || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif())) || (_gif == BadClipReader)) { - float64 radialOpacity = (radial && loaded && !_data->uploading()) ? _animation->radial.opacity() : 1; + float64 radialOpacity = (radial && loaded && parent->id > 0) ? _animation->radial.opacity() : 1; QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); if (selected) { @@ -4641,7 +4643,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg); } - if (!animating || _data->uploading()) { + if (!animating || parent->id < 0) { int32 statusX = skipx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = skipy + st::msgDateImgDelta + st::msgDateImgPadding.y(); int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); @@ -4784,6 +4786,18 @@ HistoryGif::~HistoryGif() { } } +float64 HistoryGif::dataProgress() const { + return (_data->uploading() || !_parent || _parent->id > 0) ? _data->progress() : 0; +} + +bool HistoryGif::dataFinished() const { + return (!_parent || _parent->id > 0) ? (!_data->loading() && !_data->uploading()) : false; +} + +bool HistoryGif::dataLoaded() const { + return (!_parent || _parent->id > 0) ? _data->loaded() : false; +} + HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia() , _pixw(1) , _pixh(1) @@ -5392,7 +5406,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + parent->skipBlockWidth() > width + bubble.left() + bubble.right())) { bshift += st::msgDateFont->height; } - + QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, _width)); p.fillRect(bar, barfg); @@ -5455,7 +5469,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, _attach->draw(p, parent, r.translated(-attachLeft, -attachTop), selected, ms); int32 pixwidth = _attach->currentWidth(), pixheight = _attach->height(); - + if (_data->type == WebPageVideo) { if (_data->siteName == qstr("YouTube")) { p.drawPixmap(QPoint((pixwidth - st::youtubeIcon.pxWidth()) / 2, (pixheight - st::youtubeIcon.pxHeight()) / 2), App::sprite(), st::youtubeIcon); @@ -5807,12 +5821,39 @@ int32 HistoryImageLink::fullHeight() const { return st::minPhotoSize; } +void ViaInlineBotLink::onClick(Qt::MouseButton button) const { + App::insertBotCommand('@' + _bot->username); +} + +HistoryMessageVia::HistoryMessageVia(int32 userId) + : bot(App::userLoaded(peerFromUser(userId))) + , width(0) + , fullWidth(st::msgServiceNameFont->width(qsl("via @") + bot->username)) + , lnk(new ViaInlineBotLink(bot)) { +} + +bool HistoryMessageVia::isNull() const { + return !bot || bot->username.isEmpty(); +} + +void HistoryMessageVia::resize(int32 availw) { + if (width < fullWidth && availw > width) { + if (availw < fullWidth) { + text = st::msgServiceNameFont->elided(qsl("via @") + bot->username, availw); + width = st::msgServiceNameFont->width(text); + } else { + text = qsl("via @") + bot->username; + width = fullWidth; + } + } +} + HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg) : HistoryItem(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) , _text(st::msgMinWidth) , _textWidth(0) , _textHeight(0) -, _viaBot(msg.has_via_bot_id() ? App::userLoaded(peerFromUser(msg.vvia_bot_id)) : 0) +, _via(msg.has_via_bot_id() ? new HistoryMessageVia(msg.vvia_bot_id.v) : 0) , _media(0) , _views(msg.has_views() ? msg.vviews.v : -1) { QString text(textClean(qs(msg.vmessage))); @@ -5826,7 +5867,7 @@ HistoryItem(history, block, msgId, flags, date, from) , _text(st::msgMinWidth) , _textWidth(0) , _textHeight(0) -, _viaBot(viaBotId ? App::userLoaded(peerFromUser(viaBotId)) : 0) +, _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0) , _media(0) , _views(fromChannel() ? 1 : -1) { initTime(); @@ -5842,7 +5883,7 @@ HistoryItem(history, block, msgId, flags, date, from) , _text(st::msgMinWidth) , _textWidth(0) , _textHeight(0) -, _viaBot(viaBotId ? App::userLoaded(peerFromUser(viaBotId)) : 0) +, _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0) , _media(0) , _views(fromChannel() ? 1 : -1) { initTime(); @@ -5855,7 +5896,7 @@ HistoryItem(history, block, msgId, flags, date, from) , _text(st::msgMinWidth) , _textWidth(0) , _textHeight(0) -, _viaBot(viaBotId ? App::userLoaded(peerFromUser(viaBotId)) : 0) +, _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0) , _media(0) , _views(fromChannel() ? 1 : -1) { initTime(); @@ -5994,6 +6035,11 @@ void HistoryMessage::initDimensions() { if (maxw > _maxw) _maxw = maxw; _minh += _media->minHeight(); } + if (via()) { + if (st::msgPadding.left() + via()->fullWidth + st::msgPadding.right() > _maxw) { + _maxw = st::msgPadding.left() + via()->fullWidth + st::msgPadding.right() > _maxw; + } + } } else { _media->initDimensions(this); _maxw = _media->maxWidth(); @@ -6251,7 +6297,7 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m App::roundRect(p, r, bg, cors, &sh); if (displayFromName()) { - p.setFont(st::msgNameFont->f); + p.setFont(st::msgNameFont); if (fromChannel()) { p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg); } else { @@ -6260,6 +6306,7 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m _from->nameText.drawElided(p, r.left() + st::msgPadding.left(), r.top() + st::msgPadding.top(), width - st::msgPadding.left() - st::msgPadding.right()); r.setTop(r.top() + st::msgNameFont->height); } + QRect trect(r.marginsAdded(-st::msgPadding)); drawMessageText(p, trect, selection); @@ -6286,7 +6333,15 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m textstyleRestore(); } -void HistoryMessage::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const { +void HistoryMessage::drawMessageText(Painter &p, QRect trect, uint32 selection) const { + bool outbg = out() && !fromChannel(), selected = (selection == FullSelection); + if (via()) { + p.setFont(st::msgServiceNameFont); + p.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); + p.drawTextLeft(trect.left(), trect.top(), _history->width, via()->text); + trect.setY(trect.y() + st::msgServiceNameFont->height); + } + p.setPen(st::msgColor->p); p.setFont(st::msgFont->f); uint16 selectedFrom = (selection == FullSelection) ? 0 : (selection >> 16) & 0xFFFF; @@ -6330,6 +6385,14 @@ int32 HistoryMessage::resize(int32 width) { _height += st::msgNameFont->height; } } + if (via()) { + via()->resize(width - st::msgPadding.left() - st::msgPadding.right()); + if (emptyText() && !displayFromName()) { + _height += st::msgPadding.top() + st::msgNameFont->height + st::mediaHeaderSkip; + } else { + _height += st::msgNameFont->height; + } + } } else { _height = _media->resize(width, this); } @@ -6390,7 +6453,6 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 } r.setTop(r.top() + st::msgNameFont->height); } - getStateFromMessageText(lnk, state, x, y, r); } else { _media->getState(lnk, state, x - left, y - st::msgMargin.top(), this); @@ -6401,6 +6463,15 @@ void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, HistoryCursorStat bool inDate = false; QRect trect(r.marginsAdded(-st::msgPadding)); + + if (via()) { + if (x >= trect.left() && y >= trect.top() && y < trect.top() + st::msgNameFont->height && x < trect.left() + via()->width) { + lnk = via()->lnk; + return; + } + trect.setTop(trect.top() + st::msgNameFont->height); + } + TextLinkPtr medialnk; if (_media && _media->isDisplayed()) { if (!_media->customInfoLayout()) { @@ -6443,6 +6514,9 @@ void HistoryMessage::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, if (displayFromName()) { // from user left name r.setTop(r.top() + st::msgNameFont->height); } + if (via()) { + r.setTop(r.top() + st::msgNameFont->height); + } QRect trect(r.marginsAdded(-st::msgPadding)); if (_media && _media->isDisplayed()) { trect.setBottom(trect.bottom() - _media->height()); @@ -6489,8 +6563,9 @@ QString HistoryMessage::notificationText() const { HistoryMessage::~HistoryMessage() { if (_media) { _media->unregItem(this); - delete _media; + deleteAndMark(_media); } + deleteAndMark(_via); if (_flags & MTPDmessage::flag_reply_markup) { App::clearReplyMarkup(channelId(), id); } @@ -6506,7 +6581,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const } HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) -: HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage::flag_media_unread : 0), msg->viaBot() ? peerToUser(msg->viaBot()->id) : 0, date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia()) +: HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage::flag_media_unread : 0), msg->via() ? peerToUser(msg->viaBot()->id) : 0, date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia()) , fwdDate(msg->dateForwarded()) , fwdFrom(msg->fromForwarded()) , fwdFromVersion(fwdFrom->nameVersion) @@ -6548,7 +6623,7 @@ void HistoryForwarded::drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, bool outbg = out() && !fromChannel(); p.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); - p.setFont(serviceFont->f); + p.setFont(serviceFont); if (w >= fromWidth) { p.drawText(x, y + serviceFont->ascent, lang(lng_forwarded_from)); @@ -6560,22 +6635,23 @@ void HistoryForwarded::drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, } } -void HistoryForwarded::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const { - QRect realtrect(trect); +void HistoryForwarded::drawMessageText(Painter &p, QRect trect, uint32 selection) const { if (displayForwardedFrom()) { - drawForwardedFrom(p, realtrect.x(), realtrect.y(), realtrect.width(), (selection == FullSelection)); - realtrect.setY(trect.y() + st::msgServiceNameFont->height); + drawForwardedFrom(p, trect.x(), trect.y(), trect.width(), (selection == FullSelection)); + trect.setY(trect.y() + st::msgServiceNameFont->height); } - HistoryMessage::drawMessageText(p, realtrect, selection); + HistoryMessage::drawMessageText(p, trect, selection); } int32 HistoryForwarded::resize(int32 width) { HistoryMessage::resize(width); - if (drawBubble() && displayForwardedFrom()) { - if (emptyText() && !displayFromName()) { - _height += st::msgPadding.top() + st::msgServiceNameFont->height + st::mediaHeaderSkip; - } else { - _height += st::msgServiceNameFont->height; + if (drawBubble()) { + if (displayForwardedFrom()) { + if (emptyText() && !displayFromName() && !via()) { + _height += st::msgPadding.top() + st::msgServiceNameFont->height + st::mediaHeaderSkip; + } else { + _height += st::msgServiceNameFont->height; + } } } return _height; @@ -6719,7 +6795,7 @@ bool HistoryReply::updateReplyTo(bool force) { replyToText.setText(st::msgFont, replyToMsg->inReplyText(), _textDlgOptions); replyToNameUpdated(); - + replyToLnk = TextLinkPtr(new MessageLink(replyToMsg->history()->peer->id, replyToMsg->id)); } else if (force) { replyToMsgId = 0; @@ -6836,21 +6912,20 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec } } -void HistoryReply::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const { +void HistoryReply::drawMessageText(Painter &p, QRect trect, uint32 selection) const { int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); drawReplyTo(p, trect.x(), trect.y(), trect.width(), (selection == FullSelection)); - QRect realtrect(trect); - realtrect.setY(trect.y() + h); - HistoryMessage::drawMessageText(p, realtrect, selection); + trect.setY(trect.y() + h); + HistoryMessage::drawMessageText(p, trect, selection); } int32 HistoryReply::resize(int32 width) { HistoryMessage::resize(width); if (drawBubble()) { - if (emptyText() && !displayFromName()) { + if (emptyText() && !displayFromName() && !via()) { _height += st::msgPadding.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom() + st::mediaHeaderSkip; } else { _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 6d26294ac4..7b5368702f 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -807,6 +807,10 @@ public: virtual int32 resize(int32 width) = 0; // return new height virtual void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const = 0; + virtual UserData *viaBot() const { + return 0; + } + History *history() const { return _history; } @@ -1066,7 +1070,7 @@ public: CommentsLink(HistoryItem *item) : _item(item) { } void onClick(Qt::MouseButton button) const; - + private: HistoryItem *_item; }; @@ -1243,7 +1247,7 @@ protected: } bool isThumbAnimation(uint64 ms) const { if (!_animation || !_animation->_a_thumbOver.animating()) return false; - + _animation->_a_thumbOver.step(ms); return _animation && _animation->_a_thumbOver.animating(); } @@ -1311,7 +1315,7 @@ public: return _caption.original(); } bool needsBubble(const HistoryItem *parent) const { - return !_caption.isEmpty() || parent->toHistoryReply(); + return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot(); } bool customInfoLayout() const { return _caption.isEmpty(); @@ -1380,7 +1384,7 @@ public: ImagePtr replyPreview(); bool needsBubble(const HistoryItem *parent) const { - return !_caption.isEmpty() || parent->toHistoryReply(); + return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot(); } bool customInfoLayout() const { return _caption.isEmpty(); @@ -1616,7 +1620,7 @@ public: return _caption.original(); } bool needsBubble(const HistoryItem *parent) const { - return !_caption.isEmpty() || parent->toHistoryReply(); + return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot(); } bool customInfoLayout() const { return _caption.isEmpty(); @@ -1632,18 +1636,13 @@ public: protected: - float64 dataProgress() const { - return _data->progress(); - } - bool dataFinished() const { - return !_data->loading() && !_data->uploading(); - } - bool dataLoaded() const { - return _data->loaded(); - } + float64 dataProgress() const; + bool dataFinished() const; + bool dataLoaded() const; private: + const HistoryItem *_parent; DocumentData *_data; int32 _thumbw, _thumbh; Text _caption; @@ -1840,7 +1839,7 @@ public: HistoryMedia *attach() const { return _attach; } - + ~HistoryWebPage(); private: @@ -1885,7 +1884,7 @@ public: } bool needsBubble(const HistoryItem *parent) const { - return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply(); + return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot(); } bool customInfoLayout() const { return true; @@ -1901,6 +1900,33 @@ private: }; +class ViaInlineBotLink : public ITextLink { + TEXT_LINK_CLASS(ViaInlineBotLink) + +public: + ViaInlineBotLink(UserData *bot) : _bot(bot) { + } + void onClick(Qt::MouseButton button) const; + +private: + UserData *_bot; + +}; + +class HistoryMessageVia { +public: + HistoryMessageVia(int32 userId); + + bool isNull() const; + void resize(int32 availw); + + UserData *bot; + QString text; + int32 width, fullWidth; + TextLinkPtr lnk; + +}; + class HistoryMessage : public HistoryItem { public: @@ -1915,8 +1941,11 @@ public: void initDimensions(); void fromNameUpdated() const; - UserData *viaBot() const { - return _viaBot; + virtual HistoryMessageVia *via() const { + return (_via && !_via->isNull()) ? _via : 0; + } + virtual UserData *viaBot() const { + return via() ? via()->bot : 0; } int32 plainMaxWidth() const; @@ -1932,7 +1961,7 @@ public: return drawBubble(); } bool displayFromName() const { - return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || !_media->hideFromName()); + return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || viaBot() || !_media->hideFromName()); } bool uploading() const { return _media && _media->uploading(); @@ -1943,7 +1972,7 @@ public: void setId(MsgId newId); void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; - virtual void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const; + virtual void drawMessageText(Painter &p, QRect trect, uint32 selection) const; int32 resize(int32 width); bool hasPoint(int32 x, int32 y) const; @@ -1966,7 +1995,7 @@ public: void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const; QString notificationHeader() const; QString notificationText() const; - + void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) { if (media && _media && _media->type() != MediaTypeWebPage) { _media->updateFrom(*media, this, allowEmitResize); @@ -2036,12 +2065,12 @@ protected: Text _text; int32 _textWidth, _textHeight; - UserData *_viaBot; + HistoryMessageVia *_via; HistoryMedia *_media; QString _timeText; int32 _timeWidth; - + QString _viewsText; int32 _views, _viewsWidth; @@ -2058,7 +2087,7 @@ public: void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; void drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, bool selected) const; - void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const; + void drawMessageText(Painter &p, QRect trect, uint32 selection) const; int32 resize(int32 width); bool hasPoint(int32 x, int32 y) const; void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; @@ -2115,7 +2144,7 @@ public: void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; void drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selected, bool likeService = false) const; - void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const; + void drawMessageText(Painter &p, QRect trect, uint32 selection) const; int32 resize(int32 width); bool hasPoint(int32 x, int32 y) const; void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index ec177cdd21..570db82872 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -357,7 +357,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) { _touchPrevPos = _touchPos; _touchPos = e->touchPoints().cbegin()->screenPos().toPoint(); } - + switch (e->type()) { case QEvent::TouchBegin: if (_menu) { @@ -682,7 +682,7 @@ void HistoryInner::itemRemoved(HistoryItem *item) { } if (_dragAction == NoDrag) return; - + if (_dragItem == item) { dragActionCancel(); } @@ -956,7 +956,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } QString contextMenuText = item->selectedText(FullSelection); - if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || _dragCursorState == HistoryInTextCursorState)) { + if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || (msg->getMedia()->type() != MediaTypeSticker && msg->getMedia()->type() != MediaTypeGif))) { _menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true); } } @@ -1044,7 +1044,7 @@ void HistoryInner::copyContextUrl() { void HistoryInner::saveContextImage() { PhotoLink *lnk = dynamic_cast(_contextMenuLnk.data()); if (!lnk) return; - + PhotoData *photo = lnk->photo(); if (!photo || !photo->date || !photo->loaded()) return; @@ -1059,7 +1059,7 @@ void HistoryInner::saveContextImage() { void HistoryInner::copyContextImage() { PhotoLink *lnk = dynamic_cast(_contextMenuLnk.data()); if (!lnk) return; - + PhotoData *photo = lnk->photo(); if (!photo || !photo->date || !photo->loaded()) return; @@ -1640,7 +1640,7 @@ void HistoryInner::onUpdateSelected() { } else if (_dragCursorState == HistoryInDateCursorState) { // cur = style::cur_cross; } - } else if (item) { + } else if (item) { if (item != _dragItem || (m - _dragStartPos).manhattanLength() >= QApplication::startDragDistance()) { if (_dragAction == PrepareDrag) { _dragAction = Dragging; @@ -1742,7 +1742,7 @@ void HistoryInner::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dr force = true; } if (!force) return; - + update(); } @@ -2568,7 +2568,7 @@ bool HistoryHider::offerPeer(PeerId peer) { if (toTextWidth > box.width() - st::boxPadding.left() - st::boxButtonPadding.right()) { toTextWidth = box.width() - st::boxPadding.left() - st::boxButtonPadding.right(); } - + resizeEvent(0); update(); setFocus(); @@ -3458,7 +3458,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re } App::main()->peerUpdated(_peer); - + if (_history->draftToId > 0 || !_history->draft.isEmpty()) { applyDraft(false); _replyToId = readyToForward() ? 0 : _history->draftToId; @@ -3823,11 +3823,10 @@ void HistoryWidget::updateControlsVisibility() { } if (hasBroadcastToggle()) { _broadcast.show(); - _field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); } else { _broadcast.hide(); - _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph)); } + updateFieldPlaceholder(); } if (_replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { if (_replyForwardPreviewCancel.isHidden()) { @@ -4187,7 +4186,7 @@ void HistoryWidget::loadMessagesDown() { if (_history->isEmpty() && _migrated && _migrated->isEmpty()) { return firstLoadMessages(); } - + bool loadMigrated = _migrated && !(_migrated->isEmpty() || _migrated->loadedAtBottom() || (!_history->isEmpty() && !_history->loadedAtTop())); History *from = loadMigrated ? _migrated : _history; if (from->loadedAtBottom()) { @@ -4277,7 +4276,7 @@ void HistoryWidget::onListScroll() { updateToEndVisibility(); updateCollapseCommentsVisibility(); - + int st = _scroll.scrollTop(), stm = _scroll.scrollTopMax(), sh = _scroll.height(); if (st + PreloadHeightsCount * sh > stm) { loadMessagesDown(); @@ -4442,7 +4441,7 @@ void HistoryWidget::onMuteUnmute() { } void HistoryWidget::onBroadcastChange() { - _field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); + updateFieldPlaceholder(); } void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) { @@ -4465,7 +4464,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const PeerData *p = App::peer(peer); int32 flags = newMessageFlags(p) | MTPDmessage::flag_media; // unread, out - + bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(peerToChannel(peer), replyTo)); int32 sendFlags = 0; @@ -4666,7 +4665,7 @@ void HistoryWidget::onPhotoSelect() { } QStringList photoExtensions(cPhotoExtensions()); - QStringList imgExtensions(cImgExtensions()); + QStringList imgExtensions(cImgExtensions()); QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(");;All files (*.*)")); QStringList files; @@ -4694,7 +4693,7 @@ void HistoryWidget::onDocumentSelect() { } QStringList photoExtensions(cPhotoExtensions()); - QStringList imgExtensions(cImgExtensions()); + QStringList imgExtensions(cImgExtensions()); QString filter(qsl("All files (*.*);;Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(")")); QStringList files; @@ -4838,23 +4837,27 @@ void HistoryWidget::insertBotCommand(const QString &cmd) { if (!bot->isUser() || !bot->asUser()->botInfo) bot = 0; QString username = bot ? bot->asUser()->username : QString(); int32 botStatus = _peer->isChat() ? _peer->asChat()->botStatus : (_peer->isMegagroup() ? _peer->asChannel()->mgInfo->botStatus : -1); - if (toInsert.indexOf('@') < 2 && !username.isEmpty() && (botStatus == 0 || botStatus == 2)) { + if (toInsert.indexOf('@') < 0 && !username.isEmpty() && (botStatus == 0 || botStatus == 2)) { toInsert += '@' + username; } toInsert += ' '; - QString text = _field.getLastText(); - QRegularExpressionMatch m = QRegularExpression(qsl("^/[A-Za-z_0-9]{0,64}(@[A-Za-z_0-9]{0,32})?(\\s|$)")).match(text); - if (m.hasMatch()) { - text = toInsert + text.mid(m.capturedLength()); - } else { - text = toInsert + text; - } - _field.setText(text); + if (toInsert.at(0) != '@') { + QString text = _field.getLastText(); + QRegularExpressionMatch m = QRegularExpression(qsl("^/[A-Za-z_0-9]{0,64}(@[A-Za-z_0-9]{0,32})?(\\s|$)")).match(text); + if (m.hasMatch()) { + text = toInsert + text.mid(m.capturedLength()); + } else { + text = toInsert + text; + } + _field.setTextFast(text); - QTextCursor cur(_field.textCursor()); - cur.movePosition(QTextCursor::End); - _field.setTextCursor(cur); + QTextCursor cur(_field.textCursor()); + cur.movePosition(QTextCursor::End); + _field.setTextCursor(cur); + } else { + _field.setTextFast(toInsert, true); + } } bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) { @@ -4951,23 +4954,21 @@ bool HistoryWidget::hasBroadcastToggle() const { } void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result) { - _inlineBot = 0; + _inlineBotUsername = QString(); if (result.type() == mtpc_contacts_resolvedPeer) { const MTPDcontacts_resolvedPeer &d(result.c_contacts_resolvedPeer()); App::feedUsers(d.vusers); App::feedChats(d.vchats); - PeerId peerId = peerFromMTP(d.vpeer); - if (peerId && peerIsUser(peerId)) { - _inlineBot = App::user(peerId); - } } onCheckMentionDropdown(); } -bool HistoryWidget::inlineBotResolveFail(const RPCError &error) { +bool HistoryWidget::inlineBotResolveFail(QString name, const RPCError &error) { if (mtpIsFlood(error)) return false; - _inlineBot = 0; - onCheckMentionDropdown(); + if (name == _inlineBotUsername) { + _inlineBot = 0; + onCheckMentionDropdown(); + } return true; } @@ -5299,7 +5300,7 @@ void HistoryWidget::onFieldResize() { _cmdStart.move(_attachEmoji.x() - _cmdStart.width(), height() - kbh - _cmdStart.height()); _attachType.move(0, _attachDocument.y() - _attachType.height()); - _emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height()); + _emojiPan.moveBottom(_attachEmoji.y()); updateListSize(); updateField(); @@ -5312,6 +5313,7 @@ void HistoryWidget::onFieldFocused() { void HistoryWidget::onCheckMentionDropdown() { if (!_history || _a_show.animating()) return; + UserData *bot = _inlineBot; QString start, inlineBotUsername(_inlineBotUsername); _field.getMentionHashtagBotCommandStart(start, _inlineBot, _inlineBotUsername); if (inlineBotUsername != _inlineBotUsername) { @@ -5320,8 +5322,7 @@ void HistoryWidget::onCheckMentionDropdown() { _inlineBotResolveRequestId = 0; } if (_inlineBot == InlineBotLookingUpData) { - LOG(("Inline bot unknown! resolving @%1").arg(_inlineBotUsername)); - _inlineBotResolveRequestId = MTP::send(MTPcontacts_ResolveUsername(MTP_string(_inlineBotUsername)), rpcDone(&HistoryWidget::inlineBotResolveDone), rpcFail(&HistoryWidget::inlineBotResolveFail)); + _inlineBotResolveRequestId = MTP::send(MTPcontacts_ResolveUsername(MTP_string(_inlineBotUsername)), rpcDone(&HistoryWidget::inlineBotResolveDone), rpcFail(&HistoryWidget::inlineBotResolveFail, _inlineBotUsername)); return; } } else if (_inlineBot == InlineBotLookingUpData) { @@ -5329,12 +5330,19 @@ void HistoryWidget::onCheckMentionDropdown() { } if (_inlineBot) { + if (_inlineBot != bot) { + updateFieldPlaceholder(); + } _emojiPan.queryInlineBot(_inlineBot, start); if (!_attachMention.isHidden()) { _attachMention.hideStart(); } } else { - _emojiPan.inlineBotChanged(); + if (_inlineBot != bot) { + updateFieldPlaceholder(); + _field.finishPlaceholder(); + } + _emojiPan.clearInlineBot(); if (!start.isEmpty()) { if (start.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtags(); if (start.at(0) == '@' && _peer->isUser()) return; @@ -5348,6 +5356,16 @@ void HistoryWidget::onCheckMentionDropdown() { } } +void HistoryWidget::updateFieldPlaceholder() { + if (_inlineBot && _inlineBot != InlineBotLookingUpData) { + _field.setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); + } else if (hasBroadcastToggle()) { + _field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); + } else { + _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph)); + } +} + void HistoryWidget::uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm, const QString &source, bool withText) { if (!_history) return; @@ -5790,6 +5808,10 @@ void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { } } +void HistoryWidget::notify_automaticLoadSettingsChangedGif() { + _emojiPan.notify_automaticLoadSettingsChangedGif(); +} + void HistoryWidget::resizeEvent(QResizeEvent *e) { _reportSpamPanel.resize(width(), _reportSpamPanel.height()); @@ -5828,7 +5850,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { _attachType.move(0, _attachDocument.y() - _attachType.height()); _emojiPan.setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height()); - _emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height()); + _emojiPan.moveBottom(_attachEmoji.y()); switch (_attachDrag) { case DragStateFiles: @@ -6341,18 +6363,21 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) { } else if (result->type == qstr("photo")) { QImage fileThumb(result->thumb->pix().toImage()); - PreparedPhotoThumbs photoThumbs; QVector photoSizes; QPixmap thumb = (fileThumb.width() > 100 || fileThumb.height() > 100) ? QPixmap::fromImage(fileThumb.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(fileThumb); - photoThumbs.insert('s', thumb); + ImagePtr thumbPtr = ImagePtr(thumb, "JPG"); photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); - QPixmap medium = (fileThumb.width() > 320 || fileThumb.height() > 320) ? QPixmap::fromImage(fileThumb.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(fileThumb); - photoThumbs.insert('m', medium); + QSize medium = resizeKeepAspect(result->width, result->height, 320, 320); photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); - MTPPhoto photo = MTP_photo(MTP_long(MTP::nonce()), MTP_long(0), MTP_int(unixtime()), MTP_vector(photoSizes)); + photoSizes.push_back(MTP_photoSize(MTP_string("x"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(result->width), MTP_int(result->height), MTP_int(0))); + + uint64 photoId = MTP::nonce(); + PhotoData *ph = App::photoSet(photoId, 0, 0, unixtime(), thumbPtr, ImagePtr(medium.width(), medium.height()), ImagePtr(result->width, result->height)); + MTPPhoto photo = MTP_photo(MTP_long(photoId), MTP_long(0), MTP_int(ph->date), MTP_vector(photoSizes)); + _history->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(_history->peer->id), MTPPeer(), MTPint(), MTP_int(bot ? peerToUser(bot->id) : 0), MTP_int(replyToId()), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(photo, MTP_string(result->caption)), MTPnullMarkup, MTPnullEntities, MTP_int(1)), NewMessageUnread); } } else { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 4b1619702c..bc3ece4216 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -98,7 +98,7 @@ public: void notifyMigrateUpdated(); ~HistoryInner(); - + public slots: void onUpdateSelected(); @@ -186,7 +186,7 @@ private: bool _touchScroll, _touchSelect, _touchInProgress; QPoint _touchStart, _touchPrevPos, _touchPos; QTimer _touchSelectTimer; - + TouchScrollState _touchScrollState; bool _touchPrevPosValid, _touchWaitingAcceleration; QPoint _touchSpeed; @@ -445,6 +445,8 @@ public: void destroyData(); + void updateFieldPlaceholder(); + void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false); void uploadFile(const QString &file, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, bool withText = false); // with confirmation void uploadFiles(const QStringList &files, PrepareMediaType type); @@ -484,7 +486,7 @@ public: void noSelectingScroll(); bool touchScroll(const QPoint &delta); - + uint64 animActiveTimeStart(const HistoryItem *msg) const; void stopAnimActive(); @@ -564,6 +566,7 @@ public: bool ui_isInlineItemBeingChosen(); void notify_historyItemLayoutChanged(const HistoryItem *item); + void notify_automaticLoadSettingsChangedGif(); void notify_botCommandsChanged(UserData *user); void notify_userIsBotChanged(UserData *user); void notify_migrateUpdated(PeerData *peer); @@ -769,7 +772,7 @@ private: QString _inlineBotUsername; mtpRequestId _inlineBotResolveRequestId; void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result); - bool inlineBotResolveFail(const RPCError &error); + bool inlineBotResolveFail(QString name, const RPCError &error); bool isBotStart() const; bool isBlocked() const; diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index 8b4dce32cc..a127836cab 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -504,7 +504,7 @@ void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, void LayoutOverviewVideo::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { bool loaded = _data->loaded(); - + if (hasPoint(x, y)) { link = loaded ? _openl : (_data->loading() ? _cancell : _savel); } @@ -1275,7 +1275,7 @@ void LayoutOverviewLink::getState(TextLinkPtr &link, HistoryCursorState &cursor, } } -LayoutOverviewLink::Link::Link(const QString &url, const QString &text) +LayoutOverviewLink::Link::Link(const QString &url, const QString &text) : text(text) , width(st::normalFont->width(text)) , lnk(linkFromUrl(url)) { @@ -1371,7 +1371,7 @@ void DeleteSavedGifLink::onClick(Qt::MouseButton button) const { } void LayoutInlineGif::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { -// content_automaticLoad(); + content_automaticLoad(); bool loaded = content_loaded(), loading = content_loading(), displayLoading = content_displayLoading(); if (loaded && !gif() && _gif != BadClipReader) { @@ -1832,23 +1832,20 @@ void LayoutInlineWebVideo::initDimensions() { } void LayoutInlineWebVideo::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { - int32 left = 0; - if (!_result->thumb->isNull()) { - left = st::inlineThumbSize + st::inlineThumbSkip; - prepareThumb(st::inlineThumbSize, st::inlineThumbSize); - if (_thumb.isNull()) { - p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), _result->thumb->isNull() ? st::black : st::overviewPhotoBg); - } else { - p.drawPixmapLeft(0, st::inlineRowMargin, _width, _thumb); - } + int32 left = st::inlineThumbSize + st::inlineThumbSkip; + prepareThumb(st::inlineThumbSize, st::inlineThumbSize); + if (_thumb.isNull()) { + p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), _result->thumb->isNull() ? st::black : st::overviewPhotoBg); + } else { + p.drawPixmapLeft(0, st::inlineRowMargin, _width, _thumb); + } - if (!_duration.isEmpty()) { - int32 durationTop = st::inlineRowMargin + st::inlineThumbSize - st::normalFont->height - st::inlineDurationMargin; - p.fillRect(rtlrect(0, durationTop - st::inlineDurationMargin, st::inlineThumbSize, st::normalFont->height + 2 * st::inlineDurationMargin, _width), st::msgDateImgBg); - p.setPen(st::white); - p.setFont(st::normalFont); - p.drawTextRight(_width - st::inlineThumbSize + st::inlineDurationMargin, durationTop, _width, _duration); - } + if (!_duration.isEmpty()) { + int32 durationTop = st::inlineRowMargin + st::inlineThumbSize - st::normalFont->height - st::inlineDurationMargin; + p.fillRect(rtlrect(0, durationTop - st::inlineDurationMargin, st::inlineThumbSize, st::normalFont->height + 2 * st::inlineDurationMargin, _width), st::msgDateImgBg); + p.setPen(st::white); + p.setFont(st::normalFont); + p.drawTextRight(_width - st::inlineThumbSize + st::inlineDurationMargin, durationTop, _width, _duration); } p.setPen(st::black); @@ -1951,7 +1948,7 @@ int32 LayoutInlineArticle::resizeGetHeight(int32 width) { } void LayoutInlineArticle::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { - int32 left = 0; + int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; if (_withThumb) { left = st::inlineThumbSize + st::inlineThumbSkip; prepareThumb(st::inlineThumbSize, st::inlineThumbSize); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index ff40b4441b..c5ea7381cd 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -367,7 +367,7 @@ namespace { bool fileExists(const FileKey &fkey, int options = UserPath | SafePath) { return fileExists(toFilePart(fkey), options); } - + bool readFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath) { if (options & UserPath) { if (!_userWorking()) return false; @@ -561,6 +561,8 @@ namespace { typedef QMap DraftsNotReadMap; DraftsNotReadMap _draftsNotReadMap; + typedef QPair FileDesc; // file, size + typedef QMultiMap FileLocations; FileLocations _fileLocations; typedef QPair FileLocationPair; @@ -568,10 +570,13 @@ namespace { FileLocationPairs _fileLocationPairs; typedef QMap FileLocationAliases; FileLocationAliases _fileLocationAliases; + typedef QMap WebFilesMap; + WebFilesMap _webFilesMap; + uint64 _storageWebFilesSize = 0; FileKey _locationsKey = 0, _reportSpamStatusesKey = 0; - + FileKey _recentStickersKeyOld = 0, _stickersKey = 0, _savedGifsKey = 0; - + FileKey _backgroundKey = 0; bool _backgroundWasRead = false; @@ -581,7 +586,6 @@ namespace { FileKey _savedPeersKey = 0; - typedef QPair FileDesc; // file, size typedef QMap StorageMap; StorageMap _imagesMap, _stickerImagesMap, _audiosMap; int32 _storageImagesSize = 0, _storageStickersSize = 0, _storageAudiosSize = 0; @@ -643,6 +647,12 @@ namespace { size += sizeof(quint64) * 2 + sizeof(quint64) * 2; } + size += sizeof(quint32); // web files count + for (WebFilesMap::const_iterator i = _webFilesMap.cbegin(), e = _webFilesMap.cend(); i != e; ++i) { + // url + filekey + size + size += _stringSize(i.key()) + sizeof(quint64) + sizeof(qint32); + } + EncryptedDescriptor data(size); for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) { data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(i.value().type) << i.value().name(); @@ -663,6 +673,11 @@ namespace { data.stream << quint64(i.key().first) << quint64(i.key().second) << quint64(i.value().first) << quint64(i.value().second); } + data.stream << quint32(_webFilesMap.size()); + for (WebFilesMap::const_iterator i = _webFilesMap.cbegin(), e = _webFilesMap.cend(); i != e; ++i) { + data.stream << i.key() << quint64(i.value().first) << qint32(i.value().second); + } + FileWriteDescriptor file(_locationsKey); file.writeEncrypted(data); } @@ -710,6 +725,20 @@ namespace { locations.stream >> kfirst >> ksecond >> vfirst >> vsecond; _fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond)); } + + _storageWebFilesSize = 0; + _webFilesMap.clear(); + + quint32 webLocationsCount; + locations.stream >> webLocationsCount; + for (quint32 i = 0; i < webLocationsCount; ++i) { + QString url; + quint64 key; + qint32 size; + locations.stream >> url >> key >> size; + _webFilesMap.insert(url, FileDesc(key, size)); + _storageWebFilesSize += size; + } } } @@ -2114,7 +2143,7 @@ namespace Local { data.stream << quint32(dbiDcOption) << quint32(i.key()); data.stream << quint32(i->flags) << QString::fromUtf8(i->ip.data(), i->ip.size()); data.stream << quint32(i->port); - } + } data.stream << quint32(dbiLangFile) << cLangFile(); data.stream << quint32(dbiConnectionType) << qint32(cConnectionType()); @@ -2154,6 +2183,8 @@ namespace Local { _stickerImagesMap.clear(); _audiosMap.clear(); _storageImagesSize = _storageStickersSize = _storageAudiosSize = 0; + _webFilesMap.clear(); + _storageWebFilesSize = 0; _locationsKey = _reportSpamStatusesKey = 0; _recentStickersKeyOld = _stickersKey = _savedGifsKey = 0; _backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0; @@ -2437,9 +2468,10 @@ namespace Local { quint32 imageType; readFromStream(image.stream, locFirst, locSecond, imageType, imageData); - if (locFirst != _location.first || locSecond != _location.second) { - return; - } + // we're saving files now before we have actual location + //if (locFirst != _location.first || locSecond != _location.second) { + // return; + //} _result = new Result(StorageFileType(imageType), imageData, _readImageFlag); } @@ -2652,6 +2684,110 @@ namespace Local { return _storageAudiosSize; } + qint32 _storageWebFileSize(const QString &url, qint32 rawlen) { + // fulllen + url + len + data + qint32 result = sizeof(uint32) + _stringSize(url) + sizeof(quint32) + rawlen; + if (result & 0x0F) result += 0x10 - (result & 0x0F); + result += tdfMagicLen + sizeof(qint32) + sizeof(quint32) + 0x10 + 0x10; // magic + version + len of encrypted + part of sha1 + md5 + return result; + } + + void writeWebFile(const QString &url, const QByteArray &content, bool overwrite) { + if (!_working()) return; + + qint32 size = _storageWebFileSize(url, content.size()); + WebFilesMap::const_iterator i = _webFilesMap.constFind(url); + if (i == _webFilesMap.cend()) { + i = _webFilesMap.insert(url, FileDesc(genKey(UserPath), size)); + _storageWebFilesSize += size; + _writeLocations(); + } else if (!overwrite) { + return; + } + EncryptedDescriptor data(_stringSize(url) + sizeof(quint32) + sizeof(quint32) + content.size()); + data.stream << url << content; + FileWriteDescriptor file(i.value().first, UserPath); + file.writeEncrypted(data); + if (i.value().second != size) { + _storageWebFilesSize += size; + _storageWebFilesSize -= i.value().second; + _webFilesMap[url].second = size; + } + } + + class WebFileLoadTask : public Task { + public: + WebFileLoadTask(const FileKey &key, const QString &url, webFileLoader *loader) + : _key(key) + , _url(url) + , _loader(loader) + , _result(0) { + } + void process() { + FileReadDescriptor image; + if (!readEncryptedFile(image, _key, UserPath)) { + return; + } + + QByteArray imageData; + QString url; + image.stream >> url >> imageData; + + _result = new Result(StorageFilePartial, imageData); + } + void finish() { + if (_result) { + _loader->localLoaded(_result->image, _result->format, _result->pixmap); + } else { + WebFilesMap::iterator j = _webFilesMap.find(_url); + if (j != _webFilesMap.cend() && j->first == _key) { + clearKey(j.value().first, UserPath); + _storageWebFilesSize -= j.value().second; + _webFilesMap.erase(j); + } + _loader->localLoaded(StorageImageSaved()); + } + } + virtual ~WebFileLoadTask() { + deleteAndMark(_result); + } + + protected: + FileKey _key; + QString _url; + struct Result { + Result(StorageFileType type, const QByteArray &data) : image(type, data) { + QByteArray guessFormat; + pixmap = QPixmap::fromImage(App::readImage(data, &guessFormat, false), Qt::ColorOnly); + if (!pixmap.isNull()) { + format = guessFormat; + } + } + StorageImageSaved image; + QByteArray format; + QPixmap pixmap; + }; + webFileLoader *_loader; + Result *_result; + + }; + + TaskId startWebFileLoad(const QString &url, webFileLoader *loader) { + WebFilesMap::const_iterator j = _webFilesMap.constFind(url); + if (j == _webFilesMap.cend() || !_localLoader) { + return 0; + } + return _localLoader->addTask(new WebFileLoadTask(j->first, url, loader)); + } + + int32 hasWebFiles() { + return _webFilesMap.size(); + } + + qint64 storageWebFilesSize() { + return _storageWebFilesSize; + } + void cancelTask(TaskId id) { if (_localLoader) { _localLoader->cancelTask(id); @@ -3003,7 +3139,7 @@ namespace Local { data.stream << quint32(saved.size()); for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { DocumentData *doc = *i; - + data.stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << qint32(doc->duration()); _writeStorageImageLocation(data.stream, doc->thumb->location()); } @@ -3056,7 +3192,7 @@ namespace Local { DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb); if (!doc->isAnimation()) continue; - + saved.push_back(doc); } } @@ -3185,7 +3321,7 @@ namespace Local { QString tag; quint16 count; - + RecentHashtagPack write, search; if (writeCount) { write.reserve(writeCount); @@ -3416,7 +3552,7 @@ namespace Local { QDateTime t; saved.stream >> t; - + cRefSavedPeers().insert(peer, t); cRefSavedPeersByTime().insert(t, peer); peers.push_back(peer); diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index 2497f95542..aa4f514326 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -65,7 +65,7 @@ namespace Local { bool checkPasscode(const QByteArray &passcode); void setPasscode(const QByteArray &passcode); - + enum ClearManagerTask { ClearManagerAll = 0xFFFF, ClearManagerDownloads = 0x01, @@ -139,6 +139,11 @@ namespace Local { int32 hasAudios(); qint64 storageAudiosSize(); + void writeWebFile(const QString &url, const QByteArray &data, bool overwrite = true); + TaskId startWebFileLoad(const QString &url, webFileLoader *loader); + int32 hasWebFiles(); + qint64 storageWebFilesSize(); + void cancelTask(TaskId id); void writeStickers(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index f51a5ebf41..d9fc79a60c 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -601,7 +601,7 @@ void MainWidget::cancelForwarding() { void MainWidget::finishForwarding(History *hist, bool broadcast) { if (!hist) return; - + bool fromChannelName = hist->peer->isChannel() && !hist->peer->isMegagroup() && hist->peer->asChannel()->canPublish() && (hist->peer->asChannel()->isBroadcast() || broadcast); if (!_toForward.isEmpty()) { bool genClientSideMessage = (_toForward.size() < 2); @@ -815,6 +815,10 @@ void MainWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { if (overview) overview->notify_historyItemLayoutChanged(item); } +void MainWidget::notify_automaticLoadSettingsChangedGif() { + history.notify_automaticLoadSettingsChangedGif(); +} + void MainWidget::notify_historyItemResized(const HistoryItem *item, bool scrollToIt) { if (!item || ((history.peer() == item->history()->peer || (history.peer() && history.peer() == item->history()->peer->migrateTo())) && !item->detached())) { history.notify_historyItemResized(item, scrollToIt); @@ -1355,7 +1359,7 @@ void MainWidget::saveRecentHashtags(const QString &text) { void MainWidget::readServerHistory(History *hist, bool force) { if (!hist || (!force && !hist->unreadCount)) return; - + MsgId upTo = hist->inboxRead(0); if (hist->isChannel() && !hist->peer->asChannel()->amIn()) { return; // no read request for channels that I didn't koin @@ -2988,7 +2992,7 @@ void MainWidget::updSetState(int32 pts, int32 date, int32 qts, int32 seq) { void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff) { _channelFailDifferenceTimeout.remove(channel); - + int32 timeout = 0; bool isFinal = true; switch (diff.type()) { @@ -3041,7 +3045,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha App::feedUsers(d.vusers); App::feedChats(d.vchats, false); - + _handlingChannelDifference = true; feedMessageIds(d.vother_updates); @@ -3194,7 +3198,7 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) { noUpdatesTimer.start(NoUpdatesTimeout); _ptsWaiter.setRequesting(false); - + App::emitPeerUpdated(); } break; case mtpc_updates_differenceSlice: { @@ -3995,7 +3999,7 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) { if (end <= from || !MTP::authedId()) return; App::wnd()->checkAutoLock(); - + if (mtpTypeId(*from) == mtpc_new_session_created) { MTPNewSession newSession(from, end); updSeq = 0; @@ -4135,7 +4139,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { } } } - + if (!ptsUpdated(d.vpts.v, d.vpts_count.v, updates)) { return; } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 78e92e1649..edff1b96de 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -94,7 +94,7 @@ private: bool _canDelete; QString _selStr; int32 _selStrLeft, _selStrWidth; - + bool _animating; FlatButton _clearSelection; @@ -317,10 +317,10 @@ public: DialogsIndexed &contactsList(); DialogsIndexed &dialogsList(); - + void sendMessage(History *hist, const QString &text, MsgId replyTo, bool broadcast, WebPageId webPageId = 0); void saveRecentHashtags(const QString &text); - + void readServerHistory(History *history, bool force = true); uint64 animActiveTimeStart(const HistoryItem *msg) const; @@ -364,7 +364,7 @@ public: void updateBotKeyboard(History *h); void pushReplyReturn(HistoryItem *item); - + bool hasForwardingItems(); void fillForwardingInfo(Text *&from, Text *&text, bool &serviceColor, ImagePtr &preview); void updateForwardingTexts(); @@ -422,6 +422,7 @@ public: void notify_clipStopperHidden(ClipStopperType type); void notify_historyItemResized(const HistoryItem *row, bool scrollToIt); void notify_historyItemLayoutChanged(const HistoryItem *item); + void notify_automaticLoadSettingsChangedGif(); ~MainWidget(); @@ -615,7 +616,7 @@ private: QSet updateNotifySettingPeers; SingleTimer updateNotifySettingTimer; - + typedef QMap > ReadRequests; ReadRequests _readRequests; typedef QMap ReadRequestsPending; diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp index 47d414a90b..163f4aa818 100644 --- a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp +++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp @@ -628,7 +628,7 @@ void webFileLoader::onFinished(const QByteArray &data) { emit App::wnd()->imageLoaded(); if (_localStatus == LocalNotFound || _localStatus == LocalFailed) { - //Local::writeWebFile(_url, StorageImageSaved(mtpToStorageType(_type), _data)); + Local::writeWebFile(_url, _data); } emit progress(this); loadNext(); @@ -646,7 +646,7 @@ bool webFileLoader::tryLoadLocal() { return true; } - _localTaskId = 0;// Local::startWebFileLoad(_url, this); + _localTaskId = Local::startWebFileLoad(_url, this); if (_localStatus != LocalNotTried) { return _complete; } else if (_localTaskId) { diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.h b/Telegram/SourceFiles/mtproto/mtpFileLoader.h index e181cacf3a..d2d55d2ab0 100644 --- a/Telegram/SourceFiles/mtproto/mtpFileLoader.h +++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.h @@ -279,7 +279,7 @@ protected: uint64 _id; // for other locations uint64 _access; - + }; class webFileLoaderPrivate; diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 68166bbaaf..efde423f99 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -283,7 +283,7 @@ void UserData::setBotInfo(const MTPBotInfo &info) { case mtpc_botInfo: { const MTPDbotInfo &d(info.c_botInfo()); if (peerFromUser(d.vuser_id.v) != id) return; - + if (botInfo) { botInfo->version = d.vversion.v; } else { @@ -296,7 +296,7 @@ void UserData::setBotInfo(const MTPBotInfo &info) { botInfo->text = Text(st::msgMinWidth); } botInfo->shareText = qs(d.vshare_text); - + const QVector &v(d.vcommands.c_vector().v); botInfo->commands.reserve(v.size()); bool changedCommands = false; @@ -681,7 +681,7 @@ void PhotoSaveLink::onClick(Qt::MouseButton button) const { void PhotoCancelLink::onClick(Qt::MouseButton button) const { if (button != Qt::LeftButton) return; - + PhotoData *data = photo(); if (!data->date) return; @@ -2050,6 +2050,11 @@ void InlineResult::automaticLoadGif() { } } +void InlineResult::automaticLoadSettingsChangedGif() { + if (loaded() || _loader != CancelledWebFileLoader) return; + _loader = 0; +} + void InlineResult::saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading) { if (loaded()) { return; diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index e190ce17f6..a62be4ea79 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1273,7 +1273,7 @@ inline WebPageType toWebPageType(const QString &type) { struct WebPageData { WebPageData(const WebPageId &id, WebPageType type = WebPageArticle, const QString &url = QString(), const QString &displayUrl = QString(), const QString &siteName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = 0, DocumentData *doc = 0, int32 duration = 0, const QString &author = QString(), int32 pendingTill = -1); - + void forget() { if (photo) photo->forget(); } @@ -1365,6 +1365,7 @@ public: ImagePtr thumb; void automaticLoadGif(); + void automaticLoadSettingsChangedGif(); void saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading); void cancelFile();