diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index fc539769f9..d0d76938ac 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -81,7 +81,8 @@ TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities) { return result; } -std::unique_ptr MimeDataFromTextWithEntities(const TextWithEntities &forClipboard) { +std::unique_ptr MimeDataFromTextWithEntities( + const TextWithEntities &forClipboard) { if (forClipboard.text.isEmpty()) { return nullptr; } @@ -93,11 +94,21 @@ std::unique_ptr MimeDataFromTextWithEntities(const TextWithEntities & for (auto &tag : tags) { tag.id = ConvertTagToMimeTag(tag.id); } - result->setData(Ui::FlatTextarea::tagsMimeType(), Ui::FlatTextarea::serializeTagsList(tags)); + result->setData( + Ui::FlatTextarea::tagsMimeType(), + Ui::FlatTextarea::serializeTagsList(tags)); } return result; } +void SetClipboardWithEntities( + const TextWithEntities &forClipboard, + QClipboard::Mode mode) { + if (auto data = MimeDataFromTextWithEntities(forClipboard)) { + QApplication::clipboard()->setMimeData(data.release(), mode); + } +} + MessageField::MessageField(QWidget *parent, not_null controller, const style::FlatTextarea &st, base::lambda placeholderFactory, const QString &val) : Ui::FlatTextarea(parent, st, std::move(placeholderFactory), val) , _controller(controller) { setMinHeight(st::historySendSize.height() - 2 * st::historySendPadding); diff --git a/Telegram/SourceFiles/chat_helpers/message_field.h b/Telegram/SourceFiles/chat_helpers/message_field.h index 65a3407362..9f10793971 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.h +++ b/Telegram/SourceFiles/chat_helpers/message_field.h @@ -17,8 +17,13 @@ class Controller; QString ConvertTagToMimeTag(const QString &tagId); EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags); -TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities); -std::unique_ptr MimeDataFromTextWithEntities(const TextWithEntities &forClipboard); +TextWithTags::Tags ConvertEntitiesToTextTags( + const EntitiesInText &entities); +std::unique_ptr MimeDataFromTextWithEntities( + const TextWithEntities &forClipboard); +void SetClipboardWithEntities( + const TextWithEntities &forClipboard, + QClipboard::Mode mode = QClipboard::Clipboard); class MessageField final : public Ui::FlatTextarea { Q_OBJECT diff --git a/Telegram/SourceFiles/core/click_handler.h b/Telegram/SourceFiles/core/click_handler.h index a1338a4af4..996429e4d7 100644 --- a/Telegram/SourceFiles/core/click_handler.h +++ b/Telegram/SourceFiles/core/click_handler.h @@ -48,7 +48,8 @@ public: } // Copy to clipboard support. - virtual void copyToClipboard() const { + virtual QString copyToClipboardText() const { + return QString(); } virtual QString copyToClipboardContextItemText() const { return QString(); diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h index 4bc7cf7735..04c8edb848 100644 --- a/Telegram/SourceFiles/core/click_handler_types.h +++ b/Telegram/SourceFiles/core/click_handler_types.h @@ -15,11 +15,8 @@ public: TextClickHandler(bool fullDisplayed = true) : _fullDisplayed(fullDisplayed) { } - void copyToClipboard() const override { - auto u = url(); - if (!u.isEmpty()) { - QApplication::clipboard()->setText(u); - } + QString copyToClipboardText() const override { + return url(); } QString tooltip() const override { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index da288e9f04..947c64821c 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -856,7 +856,7 @@ void InnerWidget::keyPressEvent(QKeyEvent *e) { copySelectedText(); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - setToClipboard(getSelectedText(), QClipboard::FindBuffer); + SetClipboardWithEntities(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else { e->ignore(); @@ -911,7 +911,8 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { HistoryStateRequest request; request.flags |= Text::StateRequest::Flag::LookupSymbol; auto dragState = App::mousedItem()->getState(mousePos, request); - if (dragState.cursor == HistoryInTextCursorState && dragState.symbol >= selFrom && dragState.symbol < selTo) { + if (dragState.cursor == HistoryInTextCursorState + && base::in_range(dragState.symbol, selFrom, selTo)) { isUponSelected = 1; } } @@ -922,13 +923,13 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu = base::make_unique_q(nullptr); - _contextMenuLink = ClickHandler::getActive(); + const auto link = ClickHandler::getActive(); auto view = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem(); - auto lnkPhoto = dynamic_cast(_contextMenuLink.get()); - auto lnkDocument = dynamic_cast(_contextMenuLink.get()); - auto lnkPeer = dynamic_cast(_contextMenuLink.get()); + auto lnkPhoto = dynamic_cast(link.get()); + auto lnkDocument = dynamic_cast(link.get()); + auto lnkPeer = dynamic_cast(link.get()); auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false; auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false; auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false; @@ -936,22 +937,22 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), [=] { copySelectedText(); - })->setEnabled(true); + }); } if (lnkPhoto) { const auto photo = lnkPhoto->photo(); _menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { savePhotoToFile(photo); - }))->setEnabled(true); + })); _menu->addAction(lang(lng_context_copy_image), [=] { copyContextImage(photo); - })->setEnabled(true); + }); } else { auto document = lnkDocument->document(); if (document->loading()) { _menu->addAction(lang(lng_context_cancel_download), [=] { cancelContextDownload(document); - })->setEnabled(true); + }); } else { if (document->loaded() && document->isGifv()) { if (!cAutoPlayGif()) { @@ -960,17 +961,17 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { : FullMsgId(); _menu->addAction(lang(lng_context_open_gif), [=] { openContextGif(itemId); - })->setEnabled(true); + }); } } if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) { _menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] { showContextInFolder(document); - })->setEnabled(true); + }); } _menu->addAction(lang(lnkIsVideo ? lng_context_save_video : (lnkIsVoice ? lng_context_save_audio : (lnkIsAudio ? lng_context_save_audio_file : lng_context_save_file))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] { saveDocumentToFile(document); - }))->setEnabled(true); + })); } } } else if (lnkPeer) { // suggest to block @@ -985,7 +986,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { auto msg = dynamic_cast(item); if (isUponSelected > 0) { - _menu->addAction(lang(lng_context_copy_selected), [this] { copySelectedText(); })->setEnabled(true); + _menu->addAction(lang(lng_context_copy_selected), [this] { copySelectedText(); }); } else { if (item && !isUponSelected) { auto mediaHasTextForCopy = false; @@ -1003,45 +1004,51 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } _menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] { saveDocumentToFile(document); - }))->setEnabled(true); + })); } - } else if (media->type() == MediaTypeGif && !_contextMenuLink) { + } else if (media->type() == MediaTypeGif && !link) { if (auto document = media->getDocument()) { if (document->loading()) { _menu->addAction(lang(lng_context_cancel_download), [=] { cancelContextDownload(document); - })->setEnabled(true); + }); } else { if (document->isGifv()) { if (!cAutoPlayGif()) { _menu->addAction(lang(lng_context_open_gif), [=] { openContextGif(itemId); - })->setEnabled(true); + }); } } if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) { _menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] { showContextInFolder(document); - })->setEnabled(true); + }); } _menu->addAction(lang(lng_context_save_file), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] { saveDocumentToFile(document); - }))->setEnabled(true); + })); } } } } - if (msg && !_contextMenuLink && (view->hasVisibleText() || mediaHasTextForCopy)) { + if (msg && !link && (view->hasVisibleText() || mediaHasTextForCopy)) { _menu->addAction(lang(lng_context_copy_text), [=] { copyContextText(itemId); - })->setEnabled(true); + }); } } } - auto linkCopyToClipboardText = _contextMenuLink ? _contextMenuLink->copyToClipboardContextItemText() : QString(); - if (!linkCopyToClipboardText.isEmpty()) { - _menu->addAction(linkCopyToClipboardText, [this] { copyContextUrl(); })->setEnabled(true); + const auto actionText = link + ? link->copyToClipboardContextItemText() + : QString(); + if (!actionText.isEmpty()) { + _menu->addAction( + actionText, + [text = link->copyToClipboardText()] { + QApplication::clipboard()->setText(text); + }); } } @@ -1079,13 +1086,7 @@ void InnerWidget::copyContextImage(PhotoData *photo) { } void InnerWidget::copySelectedText() { - setToClipboard(getSelectedText()); -} - -void InnerWidget::copyContextUrl() { - if (_contextMenuLink) { - _contextMenuLink->copyToClipboard(); - } + SetClipboardWithEntities(getSelectedText()); } void InnerWidget::showStickerPackInfo(not_null document) { @@ -1121,19 +1122,11 @@ void InnerWidget::openContextGif(FullMsgId itemId) { void InnerWidget::copyContextText(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { if (const auto view = viewForItem(item)) { - setToClipboard(view->selectedText(FullSelection)); + SetClipboardWithEntities(view->selectedText(FullSelection)); } } } -void InnerWidget::setToClipboard( - const TextWithEntities &forClipboard, - QClipboard::Mode mode) { - if (auto data = MimeDataFromTextWithEntities(forClipboard)) { - QApplication::clipboard()->setMimeData(data.release(), mode); - } -} - void InnerWidget::suggestRestrictUser(not_null user) { Expects(_menu != nullptr); @@ -1379,7 +1372,9 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 if (_selectedItem && _selectedText.from != _selectedText.to) { - setToClipboard(_selectedItem->selectedText(_selectedText), QClipboard::Selection); + SetClipboardWithEntities( + _selectedItem->selectedText(_selectedText), + QClipboard::Selection); } #endif // Q_OS_LINUX32 || Q_OS_LINUX64 } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index 43082b0601..e1b89a145d 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -131,14 +131,12 @@ private: void saveDocumentToFile(DocumentData *document); void copyContextImage(PhotoData *photo); void showStickerPackInfo(not_null document); - void copyContextUrl(); void cancelContextDownload(not_null document); void showContextInFolder(not_null document); void openContextGif(FullMsgId itemId); void copyContextText(FullMsgId itemId); void copySelectedText(); TextWithEntities getSelectedText() const; - void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard); void suggestRestrictUser(not_null user); void restrictUser(not_null user, const MTPChannelBannedRights &oldRights, const MTPChannelBannedRights &newRights); void restrictUserDone(not_null user, const MTPChannelBannedRights &rights); @@ -242,8 +240,6 @@ private: QPoint _trippleClickPoint; base::Timer _trippleClickTimer; - ClickHandlerPtr _contextMenuLink; - FilterValue _filter; QString _searchQuery; std::vector> _admins; diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.cpp b/Telegram/SourceFiles/history/feed/history_feed_section.cpp index 0e58a36cd6..9232b580db 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.cpp +++ b/Telegram/SourceFiles/history/feed/history_feed_section.cpp @@ -17,9 +17,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/widgets/scroll_area.h" +#include "ui/widgets/popup_menu.h" #include "boxes/confirm_box.h" #include "window/window_controller.h" #include "data/data_feed_messages.h" +#include "data/data_photo.h" +#include "data/data_document.h" #include "storage/storage_feed_messages.h" #include "styles/style_widgets.h" #include "styles/style_history.h" diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index c2dd0f48d0..ac16c901c3 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1258,7 +1258,7 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { const auto [item, selection] = *_selected.cbegin(); if (const auto view = item->mainView()) { - setToClipboard( + SetClipboardWithEntities( view->selectedText(selection), QClipboard::Selection); } @@ -1386,9 +1386,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } }; - _contextMenuLink = ClickHandler::getActive(); - auto lnkPhoto = dynamic_cast(_contextMenuLink.get()); - auto lnkDocument = dynamic_cast(_contextMenuLink.get()); + const auto link = ClickHandler::getActive(); + auto lnkPhoto = dynamic_cast(link.get()); + auto lnkDocument = dynamic_cast(link.get()); auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false; auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false; auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false; @@ -1396,42 +1396,42 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto item = _dragStateItem; const auto itemId = item ? item->fullId() : FullMsgId(); if (isUponSelected > 0) { - _menu->addAction(lang((isUponSelected > 1) ? lng_context_copy_selected_items : lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); + _menu->addAction(lang((isUponSelected > 1) ? lng_context_copy_selected_items : lng_context_copy_selected), this, SLOT(copySelectedText())); } addItemActions(item); if (lnkPhoto) { const auto photo = lnkPhoto->photo(); _menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { savePhotoToFile(photo); - }))->setEnabled(true); + })); _menu->addAction(lang(lng_context_copy_image), [=] { copyContextImage(photo); - })->setEnabled(true); + }); } else { auto document = lnkDocument->document(); if (document->loading()) { _menu->addAction(lang(lng_context_cancel_download), [=] { cancelContextDownload(document); - })->setEnabled(true); + }); } else { if (document->loaded() && document->isGifv()) { if (!cAutoPlayGif()) { _menu->addAction(lang(lng_context_open_gif), [=] { openContextGif(itemId); - })->setEnabled(true); + }); } _menu->addAction(lang(lng_context_save_gif), [=] { saveContextGif(itemId); - })->setEnabled(true); + }); } if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) { _menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] { showContextInFolder(document); - })->setEnabled(true); + }); } _menu->addAction(lang(lnkIsVideo ? lng_context_save_video : (lnkIsVoice ? lng_context_save_audio : (lnkIsAudio ? lng_context_save_audio_file : lng_context_save_file))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { saveDocumentToFile(document); - }))->setEnabled(true); + })); } } if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) { @@ -1455,7 +1455,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (item->allowsForward()) { _menu->addAction(lang(lng_context_forward_msg), [=] { forwardItem(itemId); - })->setEnabled(true); + }); } if (item->canDelete()) { _menu->addAction(lang(lng_context_delete_msg), [=] { @@ -1472,7 +1472,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _widget->updateTopBarSelection(); } } - })->setEnabled(true); + }); } } } else { // maybe cursor on some text history item? @@ -1498,7 +1498,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto msg = dynamic_cast(item); if (isUponSelected > 0) { - _menu->addAction(lang((isUponSelected > 1) ? lng_context_copy_selected_items : lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); + _menu->addAction(lang((isUponSelected > 1) ? lng_context_copy_selected_items : lng_context_copy_selected), this, SLOT(copySelectedText())); addItemActions(item); } else { addItemActions(item); @@ -1521,55 +1521,58 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } _menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] { saveDocumentToFile(document); - }))->setEnabled(true); + })); } - } else if (media->type() == MediaTypeGif && !_contextMenuLink) { + } else if (media->type() == MediaTypeGif && !link) { if (auto document = media->getDocument()) { if (document->loading()) { _menu->addAction(lang(lng_context_cancel_download), [=] { cancelContextDownload(document); - })->setEnabled(true); + }); } else { if (document->isGifv()) { if (!cAutoPlayGif()) { _menu->addAction(lang(lng_context_open_gif), [=] { openContextGif(itemId); - })->setEnabled(true); + }); } _menu->addAction(lang(lng_context_save_gif), [=] { saveContextGif(itemId); - })->setEnabled(true); + }); } if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) { _menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] { showContextInFolder(document); - })->setEnabled(true); + }); } _menu->addAction(lang(lng_context_save_file), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] { saveDocumentToFile(document); - }))->setEnabled(true); + })); } } } } - if (msg && view && !_contextMenuLink && (view->hasVisibleText() || mediaHasTextForCopy)) { + if (msg && view && !link && (view->hasVisibleText() || mediaHasTextForCopy)) { _menu->addAction(lang(lng_context_copy_text), [=] { copyContextText(itemId); - })->setEnabled(true); + }); } } } - auto linkCopyToClipboardText = _contextMenuLink ? _contextMenuLink->copyToClipboardContextItemText() : QString(); - if (!linkCopyToClipboardText.isEmpty()) { - _menu->addAction(linkCopyToClipboardText, this, SLOT(copyContextUrl()))->setEnabled(true); - } - if (linkCopyToClipboardText.isEmpty()) { - if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) { - _menu->addAction(lang(item->history()->peer->isMegagroup() ? lng_context_copy_link : lng_context_copy_post_link), [=] { - _widget->copyPostLink(itemId); + const auto actionText = link + ? link->copyToClipboardContextItemText() + : QString(); + if (!actionText.isEmpty()) { + _menu->addAction( + actionText, + [text = link->copyToClipboardText()] { + QApplication::clipboard()->setText(text); }); - } + } else if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) { + _menu->addAction(lang(item->history()->peer->isMegagroup() ? lng_context_copy_link : lng_context_copy_post_link), [=] { + _widget->copyPostLink(itemId); + }); } if (isUponSelected > 1) { if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) { @@ -1586,7 +1589,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (canForward) { _menu->addAction(lang(lng_context_forward_msg), [=] { forwardAsGroup(itemId); - })->setEnabled(true); + }); } if (canDelete) { @@ -1604,7 +1607,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _widget->updateTopBarSelection(); } } - })->setEnabled(true); + }); } } else { if (App::mousedItem() @@ -1619,7 +1622,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _widget->updateTopBarSelection(); } } - })->setEnabled(true); + }); } } } @@ -1633,13 +1636,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } void HistoryInner::copySelectedText() { - setToClipboard(getSelectedText()); -} - -void HistoryInner::copyContextUrl() { - if (_contextMenuLink) { - _contextMenuLink->copyToClipboard(); - } + SetClipboardWithEntities(getSelectedText()); } void HistoryInner::savePhotoToFile(not_null photo) { @@ -1714,17 +1711,11 @@ void HistoryInner::copyContextText(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { if (const auto view = item->mainView()) { // #TODO check for a group - setToClipboard(view->selectedText(FullSelection)); + SetClipboardWithEntities(view->selectedText(FullSelection)); } } } -void HistoryInner::setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode) { - if (auto data = MimeDataFromTextWithEntities(forClipboard)) { - QApplication::clipboard()->setMimeData(data.release(), mode); - } -} - void HistoryInner::resizeEvent(QResizeEvent *e) { onUpdateSelected(); } @@ -1821,12 +1812,14 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) { } else if (e == QKeySequence::Copy && !_selected.empty()) { copySelectedText(); #ifdef Q_OS_MAC - } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - setToClipboard(getSelectedText(), QClipboard::FindBuffer); + } else if (e->key() == Qt::Key_E + && e->modifiers().testFlag(Qt::ControlModifier)) { + SetClipboardWithEntities(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { auto selectedState = getSelectionState(); - if (selectedState.count > 0 && selectedState.canDeleteCount == selectedState.count) { + if (selectedState.count > 0 + && selectedState.canDeleteCount == selectedState.count) { _widget->confirmDeleteSelectedItems(); } } else { diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 455d33019c..6e542e326b 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -116,7 +116,6 @@ public slots: void onUpdateSelected(); void onParentGeometryChanged(); - void copyContextUrl(); void copySelectedText(); void onTouchSelect(); @@ -222,8 +221,6 @@ private: not_null selected, not_null view) const; - void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard); - void toggleScrollDateShown(); void repaintScrollDateCallback(); bool displayScrollDate() const; @@ -311,8 +308,6 @@ private: QPoint _trippleClickPoint; QTimer _trippleClickTimer; - ClickHandlerPtr _contextMenuLink; - Element *_dragSelFrom = nullptr; Element *_dragSelTo = nullptr; bool _dragSelecting = false; diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index ace2bf7940..addd5372d4 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -297,15 +297,13 @@ ReplyMarkupClickHandler::ReplyMarkupClickHandler( } // Copy to clipboard support. -void ReplyMarkupClickHandler::copyToClipboard() const { +QString ReplyMarkupClickHandler::copyToClipboardText() const { if (auto button = getButton()) { if (button->type == HistoryMessageMarkupButton::Type::Url) { - auto url = QString::fromUtf8(button->data); - if (!url.isEmpty()) { - QApplication::clipboard()->setText(url); - } + return QString::fromUtf8(button->data); } } + return QString(); } QString ReplyMarkupClickHandler::copyToClipboardContextItemText() const { diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 3bf464acb2..f781f90e35 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -184,7 +184,7 @@ public: } // Copy to clipboard support. - void copyToClipboard() const override; + QString copyToClipboardText() const override; QString copyToClipboardContextItemText() const override; // Finds the corresponding button in the items markup struct. diff --git a/Telegram/SourceFiles/history/history_location_manager.cpp b/Telegram/SourceFiles/history/history_location_manager.cpp index 0b633ffec7..608da3db98 100644 --- a/Telegram/SourceFiles/history/history_location_manager.cpp +++ b/Telegram/SourceFiles/history/history_location_manager.cpp @@ -18,6 +18,10 @@ constexpr auto kMaxHttpRedirects = 5; } // namespace +QString LocationClickHandler::copyToClipboardText() const { + return _text; +} + QString LocationClickHandler::copyToClipboardContextItemText() const { return lang(lng_context_copy_link); } diff --git a/Telegram/SourceFiles/history/history_location_manager.h b/Telegram/SourceFiles/history/history_location_manager.h index bbcad37c54..72f8a29702 100644 --- a/Telegram/SourceFiles/history/history_location_manager.h +++ b/Telegram/SourceFiles/history/history_location_manager.h @@ -85,11 +85,7 @@ public: return _text; } - void copyToClipboard() const override { - if (!_text.isEmpty()) { - QApplication::clipboard()->setText(_text); - } - } + QString copyToClipboardText() const override; QString copyToClipboardContextItemText() const override; private: diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp new file mode 100644 index 0000000000..b96b8c4e28 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -0,0 +1,241 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "history/view/history_view_context_menu.h" + +#include "history/view/history_view_list_widget.h" +#include "history/history_item.h" +#include "history/history_media_types.h" +#include "ui/widgets/popup_menu.h" +#include "chat_helpers/message_field.h" +#include "data/data_photo.h" +#include "data/data_document.h" +#include "data/data_media_types.h" +#include "core/file_utilities.h" +#include "window/window_peer_menu.h" +#include "lang/lang_keys.h" +#include "messenger.h" +#include "mainwidget.h" +#include "auth_session.h" + +namespace HistoryView { +namespace { + +void AddToggleGroupingAction( + not_null menu, + not_null peer) { + if (const auto channel = peer->asChannel()) { + const auto grouped = (channel->feed() != nullptr); + menu->addAction( + lang(grouped ? lng_feed_ungroup : lng_feed_group), + [=] { Window::ToggleChannelGrouping(channel, !grouped); }); + } +} + +void SavePhotoToFile(not_null photo) { + if (!photo->date || !photo->loaded()) { + return; + } + + FileDialog::GetWritePath( + lang(lng_save_photo), + qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(), + filedialogDefaultName(qsl("photo"), qsl(".jpg")), + base::lambda_guarded(&Auth(), [=](const QString &result) { + if (!result.isEmpty()) { + photo->full->pix().toImage().save(result, "JPG"); + } + })); +} + +void CopyImage(not_null photo) { + if (!photo->date || !photo->loaded()) { + return; + } + + QApplication::clipboard()->setPixmap(photo->full->pix()); +} + +void AddPhotoActions( + not_null menu, + not_null photo) { + menu->addAction( + lang(lng_context_save_image), + App::LambdaDelayed( + st::defaultDropdownMenu.menu.ripple.hideDuration, + &Auth(), + [=] { SavePhotoToFile(photo); })); + menu->addAction(lang(lng_context_copy_image), [=] { + CopyImage(photo); + }); +} + +void OpenGif(FullMsgId itemId) { + if (const auto item = App::histItemById(itemId)) { + if (const auto media = item->media()) { + if (const auto document = media->document()) { + Messenger::Instance().showDocument(document, item); + } + } + } +} + +void ShowInFolder(not_null document) { + const auto filepath = document->filepath( + DocumentData::FilePathResolveChecked); + if (!filepath.isEmpty()) { + File::ShowInFolder(filepath); + } +} + +void AddSaveDocumentAction( + not_null menu, + not_null document) { + menu->addAction( + lang(document->isVideoFile() + ? lng_context_save_video + : (document->isVoiceMessage() + ? lng_context_save_audio + : (document->isAudioFile() + ? lng_context_save_audio_file + : (document->sticker() + ? lng_context_save_image + : lng_context_save_file)))), + App::LambdaDelayed( + st::defaultDropdownMenu.menu.ripple.hideDuration, + &Auth(), + [=] { DocumentSaveClickHandler::doSave(document, true); })); +} + +void AddDocumentActions( + not_null menu, + not_null document, + FullMsgId contextId) { + if (document->loading()) { + menu->addAction(lang(lng_context_cancel_download), [=] { + document->cancel(); + }); + return; + } + if (document->loaded() && document->isGifv()) { + if (!cAutoPlayGif()) { + menu->addAction(lang(lng_context_open_gif), [=] { + OpenGif(contextId); + }); + } + } + if (!document->filepath( + DocumentData::FilePathResolveChecked).isEmpty()) { + menu->addAction( + lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) + ? lng_context_show_in_finder + : lng_context_show_in_folder), + [=] { ShowInFolder(document); }); + } + AddSaveDocumentAction(menu, document); +} + +void ShowStickerPackInfo(not_null document) { + if (const auto sticker = document->sticker()) { + if (sticker->set.type() != mtpc_inputStickerSetEmpty) { + App::main()->stickersBox(sticker->set); + } + } +} + +} // namespace + +base::unique_qptr FillContextMenu( + not_null list, + const ContextMenuRequest &request) { + auto result = base::make_unique_q(nullptr); + + const auto link = request.link; + const auto view = request.view; + const auto item = view ? view->data().get() : nullptr; + const auto itemId = item ? item->fullId() : FullMsgId(); + const auto rawLink = link.get(); + const auto linkPhoto = dynamic_cast(rawLink); + const auto linkDocument = dynamic_cast(rawLink); + const auto linkPeer = dynamic_cast(rawLink); + const auto photo = linkPhoto ? linkPhoto->photo().get() : nullptr; + const auto document = linkDocument + ? linkDocument->document().get() + : nullptr; + const auto isVideoLink = document ? document->isVideoFile() : false; + const auto isVoiceLink = document ? document->isVoiceMessage() : false; + const auto isAudioLink = document ? document->isAudioFile() : false; + const auto hasSelection = !request.selectedItems.empty() + || !request.selectedText.text.isEmpty(); + + if (request.overSelection) { + result->addAction(lang(lng_context_copy_selected), [=] { + SetClipboardWithEntities(list->getSelectedText()); + }); + } + + if (linkPhoto) { + AddPhotoActions(result, photo); + } else if (linkDocument) { + AddDocumentActions(result, document, itemId); + } else if (linkPeer) { + if (list->delegate()->listContext() == Context::Feed) { + AddToggleGroupingAction(result, linkPeer->peer()); + } + } else { // maybe cursor on some text history item? + bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg()); + bool canForward = item && item->allowsForward(); + + const auto msg = item->toHistoryMessage(); + if (!request.overSelection) { + if (item && !hasSelection) { + auto mediaHasTextForCopy = false; + if (auto media = view->media()) { + mediaHasTextForCopy = media->hasTextForCopy(); + if (media->type() == MediaTypeWebPage + && static_cast(media)->attach()) { + media = static_cast(media)->attach(); + } + if (media->type() == MediaTypeSticker) { + if (auto document = media->getDocument()) { + if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) { + result->addAction(lang(document->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] { + ShowStickerPackInfo(document); + }); + } + result->addAction( + lang(lng_context_save_image), + App::LambdaDelayed( + st::defaultDropdownMenu.menu.ripple.hideDuration, + list, + [=] { DocumentSaveClickHandler::doSave(document, true); })); + } + } + } + if (!link && (view->hasVisibleText() || mediaHasTextForCopy)) { + result->addAction(lang(lng_context_copy_text), [=] { + SetClipboardWithEntities(list->getItemText(itemId)); + }); + } + } + } + + const auto actionText = link + ? link->copyToClipboardContextItemText() + : QString(); + if (!actionText.isEmpty()) { + result->addAction( + actionText, + [text = link->copyToClipboardText()] { + QApplication::clipboard()->setText(text); + }); + } + } + return result; +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.h b/Telegram/SourceFiles/history/view/history_view_context_menu.h new file mode 100644 index 0000000000..cc919265be --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.h @@ -0,0 +1,34 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/unique_qptr.h" + +namespace Ui { +class PopupMenu; +} // namespace Ui + +namespace HistoryView { + +class ListWidget; +class Element; + +struct ContextMenuRequest { + ClickHandlerPtr link; + Element *view = nullptr; + MessageIdsList selectedItems; + TextWithEntities selectedText; + bool overView = false; + bool overSelection = false; +}; + +base::unique_qptr FillContextMenu( + not_null list, + const ContextMenuRequest &request); + +} // namespace diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 81e98d0106..23a49d20c2 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_media_types.h" #include "history/history_message.h" #include "history/history_item_components.h" +#include "history/view/history_view_context_menu.h" #include "history/view/history_view_element.h" #include "history/view/history_view_message.h" #include "history/view/history_view_service_message.h" @@ -23,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_peer_menu.h" #include "auth_session.h" #include "ui/widgets/popup_menu.h" -#include "core/file_utilities.h" #include "core/tl_help.h" #include "base/overload.h" #include "lang/lang_keys.h" @@ -273,6 +273,10 @@ ListWidget::ListWidget( }); } +not_null ListWidget::delegate() const { + return _delegate; +} + void ListWidget::refreshViewer() { _viewerLifetime.destroy(); _delegate->listSource( @@ -758,10 +762,11 @@ void ListWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) { _delegate->listCloseRequest(); } else if (e == QKeySequence::Copy && _selectedItem != nullptr) { - copySelectedText(); + SetClipboardWithEntities(getSelectedText()); #ifdef Q_OS_MAC - } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - setToClipboard(getSelectedText(), QClipboard::FindBuffer); + } else if (e->key() == Qt::Key_E + && e->modifiers().testFlag(Qt::ControlModifier)) { + SetClipboardWithEntities(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else { e->ignore(); @@ -800,247 +805,50 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { mouseActionUpdate(e->globalPos()); } - // -1 - has selection, but no over, 0 - no selection, 1 - over text - auto isUponSelected = 0; - auto hasSelected = 0; - if (_selectedItem) { - isUponSelected = -1; - - auto selFrom = _selectedText.from; - auto selTo = _selectedText.to; - hasSelected = (selTo > selFrom) ? 1 : 0; - if (App::mousedItem() && App::mousedItem() == App::hoveredItem()) { - auto mousePos = mapPointToItem( - mapFromGlobal(_mousePosition), - App::mousedItem()); - HistoryStateRequest request; - request.flags |= Text::StateRequest::Flag::LookupSymbol; - auto dragState = App::mousedItem()->getState(mousePos, request); - if (dragState.cursor == HistoryInTextCursorState && dragState.symbol >= selFrom && dragState.symbol < selTo) { - isUponSelected = 1; - } + ContextMenuRequest request; + request.link = ClickHandler::getActive(); + request.view = App::mousedItem(); + request.overView = (request.view == App::hoveredItem()); + request.selectedText = getSelectedText(); + if (_selectedItem + && _selectedItem == request.view + && request.overView) { + const auto mousePos = mapPointToItem( + mapFromGlobal(_mousePosition), + _selectedItem); + HistoryStateRequest stateRequest; + stateRequest.flags |= Text::StateRequest::Flag::LookupSymbol; + const auto dragState = _selectedItem->getState( + mousePos, + stateRequest); + if (dragState.cursor == HistoryInTextCursorState + && base::in_range( + dragState.symbol, + _selectedText.from, + _selectedText.to)) { + request.overSelection = true; } } - if (showFromTouch && hasSelected && isUponSelected < hasSelected) { - isUponSelected = hasSelected; + if (showFromTouch) { + request.overSelection = true; } - _menu = base::make_unique_q(nullptr); - - _contextMenuLink = ClickHandler::getActive(); - auto view = App::hoveredItem() - ? App::hoveredItem() - : App::hoveredLinkItem(); - auto lnkPhoto = dynamic_cast(_contextMenuLink.get()); - auto lnkDocument = dynamic_cast(_contextMenuLink.get()); - auto lnkPeer = dynamic_cast(_contextMenuLink.get()); - auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false; - auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false; - auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false; - if (lnkPhoto || lnkDocument) { - if (isUponSelected > 0) { - _menu->addAction(lang(lng_context_copy_selected), [=] { - copySelectedText(); - })->setEnabled(true); - } - if (lnkPhoto) { - const auto photo = lnkPhoto->photo(); - _menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { - savePhotoToFile(photo); - }))->setEnabled(true); - _menu->addAction(lang(lng_context_copy_image), [=] { - copyContextImage(photo); - })->setEnabled(true); - } else { - auto document = lnkDocument->document(); - if (document->loading()) { - _menu->addAction(lang(lng_context_cancel_download), [=] { - cancelContextDownload(document); - })->setEnabled(true); - } else { - if (document->loaded() && document->isGifv()) { - if (!cAutoPlayGif()) { - const auto itemId = view - ? view->data()->fullId() - : FullMsgId(); - _menu->addAction(lang(lng_context_open_gif), [=] { - openContextGif(itemId); - })->setEnabled(true); - } - } - if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) { - _menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] { - showContextInFolder(document); - })->setEnabled(true); - } - _menu->addAction(lang(lnkIsVideo ? lng_context_save_video : (lnkIsVoice ? lng_context_save_audio : (lnkIsAudio ? lng_context_save_audio_file : lng_context_save_file))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] { - saveDocumentToFile(document); - }))->setEnabled(true); - } - } - } else if (lnkPeer) { // suggest to block - // #TODO suggest restrict peer - if (const auto channel = lnkPeer->peer()->asChannel()) { - const auto grouped = (channel->feed() != nullptr); - _menu->addAction( - lang(grouped ? lng_feed_ungroup : lng_feed_group), - [=] { Window::ToggleChannelGrouping(channel, !grouped); }); - } - } else { // maybe cursor on some text history item? - const auto item = view ? view->data().get() : nullptr; - const auto itemId = item ? item->fullId() : FullMsgId(); - bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg()); - bool canForward = item && item->allowsForward(); - - auto msg = dynamic_cast(item); - if (isUponSelected > 0) { - _menu->addAction(lang(lng_context_copy_selected), [this] { copySelectedText(); })->setEnabled(true); - } else { - if (item && !isUponSelected) { - auto mediaHasTextForCopy = false; - if (auto media = view->media()) { - mediaHasTextForCopy = media->hasTextForCopy(); - if (media->type() == MediaTypeWebPage && static_cast(media)->attach()) { - media = static_cast(media)->attach(); - } - if (media->type() == MediaTypeSticker) { - if (auto document = media->getDocument()) { - if (document->sticker() && document->sticker()->set.type() != mtpc_inputStickerSetEmpty) { - _menu->addAction(lang(document->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), [=] { - showStickerPackInfo(document); - }); - } - _menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] { - saveDocumentToFile(document); - }))->setEnabled(true); - } - } else if (media->type() == MediaTypeGif && !_contextMenuLink) { - if (auto document = media->getDocument()) { - if (document->loading()) { - _menu->addAction(lang(lng_context_cancel_download), [=] { - cancelContextDownload(document); - })->setEnabled(true); - } else { - if (document->isGifv()) { - if (!cAutoPlayGif()) { - _menu->addAction(lang(lng_context_open_gif), [=] { - openContextGif(itemId); - })->setEnabled(true); - } - } - if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) { - _menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] { - showContextInFolder(document); - })->setEnabled(true); - } - _menu->addAction(lang(lng_context_save_file), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] { - saveDocumentToFile(document); - }))->setEnabled(true); - } - } - } - } - if (!_contextMenuLink && (view->hasVisibleText() || mediaHasTextForCopy)) { - _menu->addAction(lang(lng_context_copy_text), [=] { - copyContextText(itemId); - })->setEnabled(true); - } - } - } - - auto linkCopyToClipboardText = _contextMenuLink ? _contextMenuLink->copyToClipboardContextItemText() : QString(); - if (!linkCopyToClipboardText.isEmpty()) { - _menu->addAction(linkCopyToClipboardText, [this] { copyContextUrl(); })->setEnabled(true); - } - } - - if (_menu->actions().isEmpty()) { - _menu = nullptr; - } else { + _menu = FillContextMenu(this, request); + if (_menu && !_menu->actions().isEmpty()) { _menu->popup(e->globalPos()); e->accept(); + } else if (_menu) { + _menu = nullptr; } } -void ListWidget::savePhotoToFile(PhotoData *photo) { - if (!photo || !photo->date || !photo->loaded()) return; - - auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); - FileDialog::GetWritePath( - lang(lng_save_photo), - filter, - filedialogDefaultName(qsl("photo"), qsl(".jpg")), - base::lambda_guarded(this, [this, photo](const QString &result) { - if (!result.isEmpty()) { - photo->full->pix().toImage().save(result, "JPG"); - } - })); -} - -void ListWidget::saveDocumentToFile(DocumentData *document) { - DocumentSaveClickHandler::doSave(document, true); -} - -void ListWidget::copyContextImage(PhotoData *photo) { - if (!photo || !photo->date || !photo->loaded()) return; - - QApplication::clipboard()->setPixmap(photo->full->pix()); -} - -void ListWidget::copySelectedText() { - setToClipboard(getSelectedText()); -} - -void ListWidget::copyContextUrl() { - if (_contextMenuLink) { - _contextMenuLink->copyToClipboard(); - } -} - -void ListWidget::showStickerPackInfo(not_null document) { - if (auto sticker = document->sticker()) { - if (sticker->set.type() != mtpc_inputStickerSetEmpty) { - App::main()->stickersBox(sticker->set); - } - } -} - -void ListWidget::cancelContextDownload(not_null document) { - document->cancel(); -} - -void ListWidget::showContextInFolder(not_null document) { - const auto filepath = document->filepath( - DocumentData::FilePathResolveChecked); - if (!filepath.isEmpty()) { - File::ShowInFolder(filepath); - } -} - -void ListWidget::openContextGif(FullMsgId itemId) { - if (const auto item = App::histItemById(itemId)) { - if (auto media = item->media()) { - if (auto document = media->document()) { - Messenger::Instance().showDocument(document, item); - } - } - } -} - -void ListWidget::copyContextText(FullMsgId itemId) { +TextWithEntities ListWidget::getItemText(FullMsgId itemId) const { if (const auto item = App::histItemById(itemId)) { if (const auto view = viewForItem(item)) { - setToClipboard(view->selectedText(FullSelection)); + return view->selectedText(FullSelection); } } -} - -void ListWidget::setToClipboard( - const TextWithEntities &forClipboard, - QClipboard::Mode mode) { - if (auto data = MimeDataFromTextWithEntities(forClipboard)) { - QApplication::clipboard()->setMimeData(data.release(), mode); - } + return TextWithEntities(); } void ListWidget::mousePressEvent(QMouseEvent *e) { @@ -1207,7 +1015,9 @@ void ListWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton butt #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 if (_selectedItem && _selectedText.from != _selectedText.to) { - setToClipboard(_selectedItem->selectedText(_selectedText), QClipboard::Selection); + SetClipboardWithEntities( + _selectedItem->selectedText(_selectedText), + QClipboard::Selection); } #endif // Q_OS_LINUX32 || Q_OS_LINUX64 } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index dd22ab1a26..73885920ae 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -86,6 +86,8 @@ public: not_null controller, not_null delegate); + not_null delegate() const; + // Set the correct scroll position after being resized. void restoreScrollPosition(); @@ -97,6 +99,9 @@ public: void saveState(not_null memento); void restoreState(not_null memento); + TextWithEntities getSelectedText() const; + TextWithEntities getItemText(FullMsgId itemId) const; + // AbstractTooltipShower interface QString tooltipText() const override; QPoint tooltipPos() const override; @@ -171,20 +176,7 @@ private: QPoint mapPointToItem(QPoint point, const Element *view) const; void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false); - void savePhotoToFile(PhotoData *photo); - void saveDocumentToFile(DocumentData *document); - void copyContextImage(PhotoData *photo); void showStickerPackInfo(not_null document); - void copyContextUrl(); - void cancelContextDownload(not_null document); - void showContextInFolder(not_null document); - void openContextGif(FullMsgId itemId); - void copyContextText(FullMsgId itemId); - void copySelectedText(); - TextWithEntities getSelectedText() const; - void setToClipboard( - const TextWithEntities &forClipboard, - QClipboard::Mode mode = QClipboard::Clipboard); not_null findItemByY(int y) const; Element *strictFindItemByY(int y) const; @@ -277,8 +269,6 @@ private: QPoint _trippleClickPoint; base::Timer _trippleClickTimer; - ClickHandlerPtr _contextMenuLink; - rpl::lifetime _viewerLifetime; }; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index ea647b1a09..6d1cf88874 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -1283,13 +1283,12 @@ void ListWidget::showContextMenu( } } } else if (link) { - auto linkCopyToClipboardText - = link->copyToClipboardContextItemText(); - if (!linkCopyToClipboardText.isEmpty()) { + const auto actionText = link->copyToClipboardContextItemText(); + if (!actionText.isEmpty()) { _contextMenu->addAction( - linkCopyToClipboardText, - [link] { - link->copyToClipboard(); + actionText, + [text = link->copyToClipboardText()] { + QApplication::clipboard()->setText(text); }); } } @@ -2006,7 +2005,7 @@ void ListWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton butt #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 //if (hasSelectedText()) { // #TODO linux clipboard - // setToClipboard(_selected.cbegin()->first->selectedText(_selected.cbegin()->second), QClipboard::Selection); + // SetClipboardWithEntities(_selected.cbegin()->first->selectedText(_selected.cbegin()->second), QClipboard::Selection); //} #endif // Q_OS_LINUX32 || Q_OS_LINUX64 } diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index cf92c8b4de..62cf3944f1 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -153,14 +153,14 @@ void MainWindow::firstShow() { : lng_enable_notifications_from_tray); if (isLinux) { - trayIconMenu->addAction(lang(lng_open_from_tray), this, SLOT(showFromTray()))->setEnabled(true); - trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray()))->setEnabled(true); - trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()))->setEnabled(true); - trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()))->setEnabled(true); + trayIconMenu->addAction(lang(lng_open_from_tray), this, SLOT(showFromTray())); + trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray())); + trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray())); + trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray())); } else { - trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray()))->setEnabled(true); - trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()))->setEnabled(true); - trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()))->setEnabled(true); + trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray())); + trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray())); + trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray())); } Global::RefWorkMode().setForced(Global::WorkMode().value(), true); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 78b1a46972..9553c26c54 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -2836,7 +2836,7 @@ void MediaView::contextMenuEvent(QContextMenuEvent *e) { _menu = new Ui::PopupMenu(nullptr, st::mediaviewPopupMenu); updateActions(); for_const (auto &action, _actions) { - _menu->addAction(action.text, this, action.member)->setEnabled(true); + _menu->addAction(action.text, this, action.member); } connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); _menu->popup(e->globalPos()); diff --git a/Telegram/SourceFiles/ui/text/text.h b/Telegram/SourceFiles/ui/text/text.h index fee4ab42e1..8cc20e47d0 100644 --- a/Telegram/SourceFiles/ui/text/text.h +++ b/Telegram/SourceFiles/ui/text/text.h @@ -53,8 +53,8 @@ struct TextSelection { constexpr bool empty() const { return from == to; } - uint16 from : 16; - uint16 to : 16; + uint16 from; + uint16 to; }; inline bool operator==(TextSelection a, TextSelection b) { return a.from == b.from && a.to == b.to; diff --git a/Telegram/SourceFiles/ui/widgets/labels.cpp b/Telegram/SourceFiles/ui/widgets/labels.cpp index 500f419899..494971b0cc 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.cpp +++ b/Telegram/SourceFiles/ui/widgets/labels.cpp @@ -536,19 +536,23 @@ void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason) _contextMenu = new Ui::PopupMenu(nullptr); - _contextMenuClickHandler = ClickHandler::getActive(); - if (fullSelection && !_contextCopyText.isEmpty()) { - _contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText()))->setEnabled(true); + _contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText())); } else if (uponSelection && !fullSelection) { - _contextMenu->addAction(lang(lng_context_copy_selected), this, SLOT(onCopySelectedText()))->setEnabled(true); + _contextMenu->addAction(lang(lng_context_copy_selected), this, SLOT(onCopySelectedText())); } else if (!hasSelection && !_contextCopyText.isEmpty()) { - _contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText()))->setEnabled(true); + _contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText())); } - QString linkCopyToClipboardText = _contextMenuClickHandler ? _contextMenuClickHandler->copyToClipboardContextItemText() : QString(); - if (!linkCopyToClipboardText.isEmpty()) { - _contextMenu->addAction(linkCopyToClipboardText, this, SLOT(onCopyContextUrl()))->setEnabled(true); + if (const auto link = ClickHandler::getActive()) { + const auto actionText = link->copyToClipboardContextItemText(); + if (!actionText.isEmpty()) { + _contextMenu->addAction( + actionText, + [text = link->copyToClipboardText()] { + QApplication::clipboard()->setText(text); + }); + } } if (_contextMenu->actions().isEmpty()) { @@ -572,12 +576,6 @@ void FlatLabel::onCopyContextText() { QApplication::clipboard()->setText(_text.originalText({ 0, 0xFFFF }, _contextExpandLinksMode)); } -void FlatLabel::onCopyContextUrl() { - if (_contextMenuClickHandler) { - _contextMenuClickHandler->copyToClipboard(); - } -} - void FlatLabel::onTouchSelect() { _touchSelect = true; dragActionStart(_touchPos, Qt::LeftButton); diff --git a/Telegram/SourceFiles/ui/widgets/labels.h b/Telegram/SourceFiles/ui/widgets/labels.h index 66b157f22d..5408fba350 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.h +++ b/Telegram/SourceFiles/ui/widgets/labels.h @@ -142,7 +142,6 @@ protected: private slots: void onCopySelectedText(); void onCopyContextText(); - void onCopyContextUrl(); void onTouchSelect(); void onContextMenuDestroy(QObject *obj); @@ -201,7 +200,6 @@ private: QTimer _trippleClickTimer; Ui::PopupMenu *_contextMenu = nullptr; - ClickHandlerPtr _contextMenuClickHandler; QString _contextCopyText; ExpandLinksMode _contextExpandLinksMode = ExpandLinksAll; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 64ca0f2392..d8a15704b1 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -229,6 +229,8 @@ <(src_loc)/history/admin_log/history_admin_log_section.h <(src_loc)/history/feed/history_feed_section.cpp <(src_loc)/history/feed/history_feed_section.h +<(src_loc)/history/view/history_view_context_menu.cpp +<(src_loc)/history/view/history_view_context_menu.h <(src_loc)/history/view/history_view_cursor_state.cpp <(src_loc)/history/view/history_view_cursor_state.h <(src_loc)/history/view/history_view_element.cpp