mirror of https://github.com/procxx/kepka.git
Move message context menu to a separate module.
This commit is contained in:
parent
65df137610
commit
fe1a90bd39
|
@ -81,7 +81,8 @@ TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(const TextWithEntities &forClipboard) {
|
std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(
|
||||||
|
const TextWithEntities &forClipboard) {
|
||||||
if (forClipboard.text.isEmpty()) {
|
if (forClipboard.text.isEmpty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -93,11 +94,21 @@ std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(const TextWithEntities &
|
||||||
for (auto &tag : tags) {
|
for (auto &tag : tags) {
|
||||||
tag.id = ConvertTagToMimeTag(tag.id);
|
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;
|
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<Window::Controller*> controller, const style::FlatTextarea &st, base::lambda<QString()> placeholderFactory, const QString &val) : Ui::FlatTextarea(parent, st, std::move(placeholderFactory), val)
|
MessageField::MessageField(QWidget *parent, not_null<Window::Controller*> controller, const style::FlatTextarea &st, base::lambda<QString()> placeholderFactory, const QString &val) : Ui::FlatTextarea(parent, st, std::move(placeholderFactory), val)
|
||||||
, _controller(controller) {
|
, _controller(controller) {
|
||||||
setMinHeight(st::historySendSize.height() - 2 * st::historySendPadding);
|
setMinHeight(st::historySendSize.height() - 2 * st::historySendPadding);
|
||||||
|
|
|
@ -17,8 +17,13 @@ class Controller;
|
||||||
QString ConvertTagToMimeTag(const QString &tagId);
|
QString ConvertTagToMimeTag(const QString &tagId);
|
||||||
|
|
||||||
EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags);
|
EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags);
|
||||||
TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities);
|
TextWithTags::Tags ConvertEntitiesToTextTags(
|
||||||
std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(const TextWithEntities &forClipboard);
|
const EntitiesInText &entities);
|
||||||
|
std::unique_ptr<QMimeData> MimeDataFromTextWithEntities(
|
||||||
|
const TextWithEntities &forClipboard);
|
||||||
|
void SetClipboardWithEntities(
|
||||||
|
const TextWithEntities &forClipboard,
|
||||||
|
QClipboard::Mode mode = QClipboard::Clipboard);
|
||||||
|
|
||||||
class MessageField final : public Ui::FlatTextarea {
|
class MessageField final : public Ui::FlatTextarea {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -48,7 +48,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy to clipboard support.
|
// Copy to clipboard support.
|
||||||
virtual void copyToClipboard() const {
|
virtual QString copyToClipboardText() const {
|
||||||
|
return QString();
|
||||||
}
|
}
|
||||||
virtual QString copyToClipboardContextItemText() const {
|
virtual QString copyToClipboardContextItemText() const {
|
||||||
return QString();
|
return QString();
|
||||||
|
|
|
@ -15,11 +15,8 @@ public:
|
||||||
TextClickHandler(bool fullDisplayed = true) : _fullDisplayed(fullDisplayed) {
|
TextClickHandler(bool fullDisplayed = true) : _fullDisplayed(fullDisplayed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyToClipboard() const override {
|
QString copyToClipboardText() const override {
|
||||||
auto u = url();
|
return url();
|
||||||
if (!u.isEmpty()) {
|
|
||||||
QApplication::clipboard()->setText(u);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString tooltip() const override {
|
QString tooltip() const override {
|
||||||
|
|
|
@ -856,7 +856,7 @@ void InnerWidget::keyPressEvent(QKeyEvent *e) {
|
||||||
copySelectedText();
|
copySelectedText();
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
} else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) {
|
} else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||||
setToClipboard(getSelectedText(), QClipboard::FindBuffer);
|
SetClipboardWithEntities(getSelectedText(), QClipboard::FindBuffer);
|
||||||
#endif // Q_OS_MAC
|
#endif // Q_OS_MAC
|
||||||
} else {
|
} else {
|
||||||
e->ignore();
|
e->ignore();
|
||||||
|
@ -911,7 +911,8 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
HistoryStateRequest request;
|
HistoryStateRequest request;
|
||||||
request.flags |= Text::StateRequest::Flag::LookupSymbol;
|
request.flags |= Text::StateRequest::Flag::LookupSymbol;
|
||||||
auto dragState = App::mousedItem()->getState(mousePos, request);
|
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;
|
isUponSelected = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -922,13 +923,13 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
|
|
||||||
_menu = base::make_unique_q<Ui::PopupMenu>(nullptr);
|
_menu = base::make_unique_q<Ui::PopupMenu>(nullptr);
|
||||||
|
|
||||||
_contextMenuLink = ClickHandler::getActive();
|
const auto link = ClickHandler::getActive();
|
||||||
auto view = App::hoveredItem()
|
auto view = App::hoveredItem()
|
||||||
? App::hoveredItem()
|
? App::hoveredItem()
|
||||||
: App::hoveredLinkItem();
|
: App::hoveredLinkItem();
|
||||||
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLink.get());
|
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(link.get());
|
||||||
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLink.get());
|
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(link.get());
|
||||||
auto lnkPeer = dynamic_cast<PeerClickHandler*>(_contextMenuLink.get());
|
auto lnkPeer = dynamic_cast<PeerClickHandler*>(link.get());
|
||||||
auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false;
|
auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false;
|
||||||
auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false;
|
auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false;
|
||||||
auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false;
|
auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false;
|
||||||
|
@ -936,22 +937,22 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
if (isUponSelected > 0) {
|
if (isUponSelected > 0) {
|
||||||
_menu->addAction(lang(lng_context_copy_selected), [=] {
|
_menu->addAction(lang(lng_context_copy_selected), [=] {
|
||||||
copySelectedText();
|
copySelectedText();
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
if (lnkPhoto) {
|
if (lnkPhoto) {
|
||||||
const auto photo = lnkPhoto->photo();
|
const auto photo = lnkPhoto->photo();
|
||||||
_menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
_menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||||
savePhotoToFile(photo);
|
savePhotoToFile(photo);
|
||||||
}))->setEnabled(true);
|
}));
|
||||||
_menu->addAction(lang(lng_context_copy_image), [=] {
|
_menu->addAction(lang(lng_context_copy_image), [=] {
|
||||||
copyContextImage(photo);
|
copyContextImage(photo);
|
||||||
})->setEnabled(true);
|
});
|
||||||
} else {
|
} else {
|
||||||
auto document = lnkDocument->document();
|
auto document = lnkDocument->document();
|
||||||
if (document->loading()) {
|
if (document->loading()) {
|
||||||
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
||||||
cancelContextDownload(document);
|
cancelContextDownload(document);
|
||||||
})->setEnabled(true);
|
});
|
||||||
} else {
|
} else {
|
||||||
if (document->loaded() && document->isGifv()) {
|
if (document->loaded() && document->isGifv()) {
|
||||||
if (!cAutoPlayGif()) {
|
if (!cAutoPlayGif()) {
|
||||||
|
@ -960,17 +961,17 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
: FullMsgId();
|
: FullMsgId();
|
||||||
_menu->addAction(lang(lng_context_open_gif), [=] {
|
_menu->addAction(lang(lng_context_open_gif), [=] {
|
||||||
openContextGif(itemId);
|
openContextGif(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
||||||
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
||||||
showContextInFolder(document);
|
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] {
|
_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);
|
saveDocumentToFile(document);
|
||||||
}))->setEnabled(true);
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (lnkPeer) { // suggest to block
|
} else if (lnkPeer) { // suggest to block
|
||||||
|
@ -985,7 +986,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
|
|
||||||
auto msg = dynamic_cast<HistoryMessage*>(item);
|
auto msg = dynamic_cast<HistoryMessage*>(item);
|
||||||
if (isUponSelected > 0) {
|
if (isUponSelected > 0) {
|
||||||
_menu->addAction(lang(lng_context_copy_selected), [this] { copySelectedText(); })->setEnabled(true);
|
_menu->addAction(lang(lng_context_copy_selected), [this] { copySelectedText(); });
|
||||||
} else {
|
} else {
|
||||||
if (item && !isUponSelected) {
|
if (item && !isUponSelected) {
|
||||||
auto mediaHasTextForCopy = false;
|
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] {
|
_menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] {
|
||||||
saveDocumentToFile(document);
|
saveDocumentToFile(document);
|
||||||
}))->setEnabled(true);
|
}));
|
||||||
}
|
}
|
||||||
} else if (media->type() == MediaTypeGif && !_contextMenuLink) {
|
} else if (media->type() == MediaTypeGif && !link) {
|
||||||
if (auto document = media->getDocument()) {
|
if (auto document = media->getDocument()) {
|
||||||
if (document->loading()) {
|
if (document->loading()) {
|
||||||
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
||||||
cancelContextDownload(document);
|
cancelContextDownload(document);
|
||||||
})->setEnabled(true);
|
});
|
||||||
} else {
|
} else {
|
||||||
if (document->isGifv()) {
|
if (document->isGifv()) {
|
||||||
if (!cAutoPlayGif()) {
|
if (!cAutoPlayGif()) {
|
||||||
_menu->addAction(lang(lng_context_open_gif), [=] {
|
_menu->addAction(lang(lng_context_open_gif), [=] {
|
||||||
openContextGif(itemId);
|
openContextGif(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
||||||
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
||||||
showContextInFolder(document);
|
showContextInFolder(document);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
_menu->addAction(lang(lng_context_save_file), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] {
|
_menu->addAction(lang(lng_context_save_file), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] {
|
||||||
saveDocumentToFile(document);
|
saveDocumentToFile(document);
|
||||||
}))->setEnabled(true);
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (msg && !_contextMenuLink && (view->hasVisibleText() || mediaHasTextForCopy)) {
|
if (msg && !link && (view->hasVisibleText() || mediaHasTextForCopy)) {
|
||||||
_menu->addAction(lang(lng_context_copy_text), [=] {
|
_menu->addAction(lang(lng_context_copy_text), [=] {
|
||||||
copyContextText(itemId);
|
copyContextText(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto linkCopyToClipboardText = _contextMenuLink ? _contextMenuLink->copyToClipboardContextItemText() : QString();
|
const auto actionText = link
|
||||||
if (!linkCopyToClipboardText.isEmpty()) {
|
? link->copyToClipboardContextItemText()
|
||||||
_menu->addAction(linkCopyToClipboardText, [this] { copyContextUrl(); })->setEnabled(true);
|
: 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() {
|
void InnerWidget::copySelectedText() {
|
||||||
setToClipboard(getSelectedText());
|
SetClipboardWithEntities(getSelectedText());
|
||||||
}
|
|
||||||
|
|
||||||
void InnerWidget::copyContextUrl() {
|
|
||||||
if (_contextMenuLink) {
|
|
||||||
_contextMenuLink->copyToClipboard();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::showStickerPackInfo(not_null<DocumentData*> document) {
|
void InnerWidget::showStickerPackInfo(not_null<DocumentData*> document) {
|
||||||
|
@ -1121,19 +1122,11 @@ void InnerWidget::openContextGif(FullMsgId itemId) {
|
||||||
void InnerWidget::copyContextText(FullMsgId itemId) {
|
void InnerWidget::copyContextText(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto view = viewForItem(item)) {
|
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<UserData*> user) {
|
void InnerWidget::suggestRestrictUser(not_null<UserData*> user) {
|
||||||
Expects(_menu != nullptr);
|
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 defined Q_OS_LINUX32 || defined Q_OS_LINUX64
|
||||||
if (_selectedItem && _selectedText.from != _selectedText.to) {
|
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
|
#endif // Q_OS_LINUX32 || Q_OS_LINUX64
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,14 +131,12 @@ private:
|
||||||
void saveDocumentToFile(DocumentData *document);
|
void saveDocumentToFile(DocumentData *document);
|
||||||
void copyContextImage(PhotoData *photo);
|
void copyContextImage(PhotoData *photo);
|
||||||
void showStickerPackInfo(not_null<DocumentData*> document);
|
void showStickerPackInfo(not_null<DocumentData*> document);
|
||||||
void copyContextUrl();
|
|
||||||
void cancelContextDownload(not_null<DocumentData*> document);
|
void cancelContextDownload(not_null<DocumentData*> document);
|
||||||
void showContextInFolder(not_null<DocumentData*> document);
|
void showContextInFolder(not_null<DocumentData*> document);
|
||||||
void openContextGif(FullMsgId itemId);
|
void openContextGif(FullMsgId itemId);
|
||||||
void copyContextText(FullMsgId itemId);
|
void copyContextText(FullMsgId itemId);
|
||||||
void copySelectedText();
|
void copySelectedText();
|
||||||
TextWithEntities getSelectedText() const;
|
TextWithEntities getSelectedText() const;
|
||||||
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
|
|
||||||
void suggestRestrictUser(not_null<UserData*> user);
|
void suggestRestrictUser(not_null<UserData*> user);
|
||||||
void restrictUser(not_null<UserData*> user, const MTPChannelBannedRights &oldRights, const MTPChannelBannedRights &newRights);
|
void restrictUser(not_null<UserData*> user, const MTPChannelBannedRights &oldRights, const MTPChannelBannedRights &newRights);
|
||||||
void restrictUserDone(not_null<UserData*> user, const MTPChannelBannedRights &rights);
|
void restrictUserDone(not_null<UserData*> user, const MTPChannelBannedRights &rights);
|
||||||
|
@ -242,8 +240,6 @@ private:
|
||||||
QPoint _trippleClickPoint;
|
QPoint _trippleClickPoint;
|
||||||
base::Timer _trippleClickTimer;
|
base::Timer _trippleClickTimer;
|
||||||
|
|
||||||
ClickHandlerPtr _contextMenuLink;
|
|
||||||
|
|
||||||
FilterValue _filter;
|
FilterValue _filter;
|
||||||
QString _searchQuery;
|
QString _searchQuery;
|
||||||
std::vector<not_null<UserData*>> _admins;
|
std::vector<not_null<UserData*>> _admins;
|
||||||
|
|
|
@ -17,9 +17,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "data/data_feed_messages.h"
|
#include "data/data_feed_messages.h"
|
||||||
|
#include "data/data_photo.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
#include "storage/storage_feed_messages.h"
|
#include "storage/storage_feed_messages.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
|
|
|
@ -1258,7 +1258,7 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
|
||||||
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
||||||
const auto [item, selection] = *_selected.cbegin();
|
const auto [item, selection] = *_selected.cbegin();
|
||||||
if (const auto view = item->mainView()) {
|
if (const auto view = item->mainView()) {
|
||||||
setToClipboard(
|
SetClipboardWithEntities(
|
||||||
view->selectedText(selection),
|
view->selectedText(selection),
|
||||||
QClipboard::Selection);
|
QClipboard::Selection);
|
||||||
}
|
}
|
||||||
|
@ -1386,9 +1386,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_contextMenuLink = ClickHandler::getActive();
|
const auto link = ClickHandler::getActive();
|
||||||
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLink.get());
|
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(link.get());
|
||||||
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLink.get());
|
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(link.get());
|
||||||
auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false;
|
auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false;
|
||||||
auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false;
|
auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false;
|
||||||
auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : 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 item = _dragStateItem;
|
||||||
const auto itemId = item ? item->fullId() : FullMsgId();
|
const auto itemId = item ? item->fullId() : FullMsgId();
|
||||||
if (isUponSelected > 0) {
|
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);
|
addItemActions(item);
|
||||||
if (lnkPhoto) {
|
if (lnkPhoto) {
|
||||||
const auto photo = lnkPhoto->photo();
|
const auto photo = lnkPhoto->photo();
|
||||||
_menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
_menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||||
savePhotoToFile(photo);
|
savePhotoToFile(photo);
|
||||||
}))->setEnabled(true);
|
}));
|
||||||
_menu->addAction(lang(lng_context_copy_image), [=] {
|
_menu->addAction(lang(lng_context_copy_image), [=] {
|
||||||
copyContextImage(photo);
|
copyContextImage(photo);
|
||||||
})->setEnabled(true);
|
});
|
||||||
} else {
|
} else {
|
||||||
auto document = lnkDocument->document();
|
auto document = lnkDocument->document();
|
||||||
if (document->loading()) {
|
if (document->loading()) {
|
||||||
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
||||||
cancelContextDownload(document);
|
cancelContextDownload(document);
|
||||||
})->setEnabled(true);
|
});
|
||||||
} else {
|
} else {
|
||||||
if (document->loaded() && document->isGifv()) {
|
if (document->loaded() && document->isGifv()) {
|
||||||
if (!cAutoPlayGif()) {
|
if (!cAutoPlayGif()) {
|
||||||
_menu->addAction(lang(lng_context_open_gif), [=] {
|
_menu->addAction(lang(lng_context_open_gif), [=] {
|
||||||
openContextGif(itemId);
|
openContextGif(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
_menu->addAction(lang(lng_context_save_gif), [=] {
|
_menu->addAction(lang(lng_context_save_gif), [=] {
|
||||||
saveContextGif(itemId);
|
saveContextGif(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
||||||
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
||||||
showContextInFolder(document);
|
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, [=] {
|
_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);
|
saveDocumentToFile(document);
|
||||||
}))->setEnabled(true);
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) {
|
if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) {
|
||||||
|
@ -1455,7 +1455,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
if (item->allowsForward()) {
|
if (item->allowsForward()) {
|
||||||
_menu->addAction(lang(lng_context_forward_msg), [=] {
|
_menu->addAction(lang(lng_context_forward_msg), [=] {
|
||||||
forwardItem(itemId);
|
forwardItem(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
if (item->canDelete()) {
|
if (item->canDelete()) {
|
||||||
_menu->addAction(lang(lng_context_delete_msg), [=] {
|
_menu->addAction(lang(lng_context_delete_msg), [=] {
|
||||||
|
@ -1472,7 +1472,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
_widget->updateTopBarSelection();
|
_widget->updateTopBarSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // maybe cursor on some text history item?
|
} else { // maybe cursor on some text history item?
|
||||||
|
@ -1498,7 +1498,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
|
|
||||||
const auto msg = dynamic_cast<HistoryMessage*>(item);
|
const auto msg = dynamic_cast<HistoryMessage*>(item);
|
||||||
if (isUponSelected > 0) {
|
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);
|
addItemActions(item);
|
||||||
} else {
|
} else {
|
||||||
addItemActions(item);
|
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] {
|
_menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] {
|
||||||
saveDocumentToFile(document);
|
saveDocumentToFile(document);
|
||||||
}))->setEnabled(true);
|
}));
|
||||||
}
|
}
|
||||||
} else if (media->type() == MediaTypeGif && !_contextMenuLink) {
|
} else if (media->type() == MediaTypeGif && !link) {
|
||||||
if (auto document = media->getDocument()) {
|
if (auto document = media->getDocument()) {
|
||||||
if (document->loading()) {
|
if (document->loading()) {
|
||||||
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
||||||
cancelContextDownload(document);
|
cancelContextDownload(document);
|
||||||
})->setEnabled(true);
|
});
|
||||||
} else {
|
} else {
|
||||||
if (document->isGifv()) {
|
if (document->isGifv()) {
|
||||||
if (!cAutoPlayGif()) {
|
if (!cAutoPlayGif()) {
|
||||||
_menu->addAction(lang(lng_context_open_gif), [=] {
|
_menu->addAction(lang(lng_context_open_gif), [=] {
|
||||||
openContextGif(itemId);
|
openContextGif(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
_menu->addAction(lang(lng_context_save_gif), [=] {
|
_menu->addAction(lang(lng_context_save_gif), [=] {
|
||||||
saveContextGif(itemId);
|
saveContextGif(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
||||||
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
||||||
showContextInFolder(document);
|
showContextInFolder(document);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
_menu->addAction(lang(lng_context_save_file), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] {
|
_menu->addAction(lang(lng_context_save_file), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [this, document] {
|
||||||
saveDocumentToFile(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), [=] {
|
_menu->addAction(lang(lng_context_copy_text), [=] {
|
||||||
copyContextText(itemId);
|
copyContextText(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto linkCopyToClipboardText = _contextMenuLink ? _contextMenuLink->copyToClipboardContextItemText() : QString();
|
const auto actionText = link
|
||||||
if (!linkCopyToClipboardText.isEmpty()) {
|
? link->copyToClipboardContextItemText()
|
||||||
_menu->addAction(linkCopyToClipboardText, this, SLOT(copyContextUrl()))->setEnabled(true);
|
: QString();
|
||||||
}
|
if (!actionText.isEmpty()) {
|
||||||
if (linkCopyToClipboardText.isEmpty()) {
|
_menu->addAction(
|
||||||
if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) {
|
actionText,
|
||||||
_menu->addAction(lang(item->history()->peer->isMegagroup() ? lng_context_copy_link : lng_context_copy_post_link), [=] {
|
[text = link->copyToClipboardText()] {
|
||||||
_widget->copyPostLink(itemId);
|
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 (isUponSelected > 1) {
|
||||||
if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) {
|
if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) {
|
||||||
|
@ -1586,7 +1589,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
if (canForward) {
|
if (canForward) {
|
||||||
_menu->addAction(lang(lng_context_forward_msg), [=] {
|
_menu->addAction(lang(lng_context_forward_msg), [=] {
|
||||||
forwardAsGroup(itemId);
|
forwardAsGroup(itemId);
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canDelete) {
|
if (canDelete) {
|
||||||
|
@ -1604,7 +1607,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
_widget->updateTopBarSelection();
|
_widget->updateTopBarSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (App::mousedItem()
|
if (App::mousedItem()
|
||||||
|
@ -1619,7 +1622,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
_widget->updateTopBarSelection();
|
_widget->updateTopBarSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})->setEnabled(true);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1633,13 +1636,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryInner::copySelectedText() {
|
void HistoryInner::copySelectedText() {
|
||||||
setToClipboard(getSelectedText());
|
SetClipboardWithEntities(getSelectedText());
|
||||||
}
|
|
||||||
|
|
||||||
void HistoryInner::copyContextUrl() {
|
|
||||||
if (_contextMenuLink) {
|
|
||||||
_contextMenuLink->copyToClipboard();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) {
|
void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) {
|
||||||
|
@ -1714,17 +1711,11 @@ void HistoryInner::copyContextText(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto view = item->mainView()) {
|
if (const auto view = item->mainView()) {
|
||||||
// #TODO check for a group
|
// #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) {
|
void HistoryInner::resizeEvent(QResizeEvent *e) {
|
||||||
onUpdateSelected();
|
onUpdateSelected();
|
||||||
}
|
}
|
||||||
|
@ -1821,12 +1812,14 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) {
|
||||||
} else if (e == QKeySequence::Copy && !_selected.empty()) {
|
} else if (e == QKeySequence::Copy && !_selected.empty()) {
|
||||||
copySelectedText();
|
copySelectedText();
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
} else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) {
|
} else if (e->key() == Qt::Key_E
|
||||||
setToClipboard(getSelectedText(), QClipboard::FindBuffer);
|
&& e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||||
|
SetClipboardWithEntities(getSelectedText(), QClipboard::FindBuffer);
|
||||||
#endif // Q_OS_MAC
|
#endif // Q_OS_MAC
|
||||||
} else if (e == QKeySequence::Delete) {
|
} else if (e == QKeySequence::Delete) {
|
||||||
auto selectedState = getSelectionState();
|
auto selectedState = getSelectionState();
|
||||||
if (selectedState.count > 0 && selectedState.canDeleteCount == selectedState.count) {
|
if (selectedState.count > 0
|
||||||
|
&& selectedState.canDeleteCount == selectedState.count) {
|
||||||
_widget->confirmDeleteSelectedItems();
|
_widget->confirmDeleteSelectedItems();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -116,7 +116,6 @@ public slots:
|
||||||
void onUpdateSelected();
|
void onUpdateSelected();
|
||||||
void onParentGeometryChanged();
|
void onParentGeometryChanged();
|
||||||
|
|
||||||
void copyContextUrl();
|
|
||||||
void copySelectedText();
|
void copySelectedText();
|
||||||
|
|
||||||
void onTouchSelect();
|
void onTouchSelect();
|
||||||
|
@ -222,8 +221,6 @@ private:
|
||||||
not_null<const SelectedItems*> selected,
|
not_null<const SelectedItems*> selected,
|
||||||
not_null<Element*> view) const;
|
not_null<Element*> view) const;
|
||||||
|
|
||||||
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
|
|
||||||
|
|
||||||
void toggleScrollDateShown();
|
void toggleScrollDateShown();
|
||||||
void repaintScrollDateCallback();
|
void repaintScrollDateCallback();
|
||||||
bool displayScrollDate() const;
|
bool displayScrollDate() const;
|
||||||
|
@ -311,8 +308,6 @@ private:
|
||||||
QPoint _trippleClickPoint;
|
QPoint _trippleClickPoint;
|
||||||
QTimer _trippleClickTimer;
|
QTimer _trippleClickTimer;
|
||||||
|
|
||||||
ClickHandlerPtr _contextMenuLink;
|
|
||||||
|
|
||||||
Element *_dragSelFrom = nullptr;
|
Element *_dragSelFrom = nullptr;
|
||||||
Element *_dragSelTo = nullptr;
|
Element *_dragSelTo = nullptr;
|
||||||
bool _dragSelecting = false;
|
bool _dragSelecting = false;
|
||||||
|
|
|
@ -297,15 +297,13 @@ ReplyMarkupClickHandler::ReplyMarkupClickHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy to clipboard support.
|
// Copy to clipboard support.
|
||||||
void ReplyMarkupClickHandler::copyToClipboard() const {
|
QString ReplyMarkupClickHandler::copyToClipboardText() const {
|
||||||
if (auto button = getButton()) {
|
if (auto button = getButton()) {
|
||||||
if (button->type == HistoryMessageMarkupButton::Type::Url) {
|
if (button->type == HistoryMessageMarkupButton::Type::Url) {
|
||||||
auto url = QString::fromUtf8(button->data);
|
return QString::fromUtf8(button->data);
|
||||||
if (!url.isEmpty()) {
|
|
||||||
QApplication::clipboard()->setText(url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ReplyMarkupClickHandler::copyToClipboardContextItemText() const {
|
QString ReplyMarkupClickHandler::copyToClipboardContextItemText() const {
|
||||||
|
|
|
@ -184,7 +184,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy to clipboard support.
|
// Copy to clipboard support.
|
||||||
void copyToClipboard() const override;
|
QString copyToClipboardText() const override;
|
||||||
QString copyToClipboardContextItemText() const override;
|
QString copyToClipboardContextItemText() const override;
|
||||||
|
|
||||||
// Finds the corresponding button in the items markup struct.
|
// Finds the corresponding button in the items markup struct.
|
||||||
|
|
|
@ -18,6 +18,10 @@ constexpr auto kMaxHttpRedirects = 5;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
QString LocationClickHandler::copyToClipboardText() const {
|
||||||
|
return _text;
|
||||||
|
}
|
||||||
|
|
||||||
QString LocationClickHandler::copyToClipboardContextItemText() const {
|
QString LocationClickHandler::copyToClipboardContextItemText() const {
|
||||||
return lang(lng_context_copy_link);
|
return lang(lng_context_copy_link);
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,11 +85,7 @@ public:
|
||||||
return _text;
|
return _text;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyToClipboard() const override {
|
QString copyToClipboardText() const override;
|
||||||
if (!_text.isEmpty()) {
|
|
||||||
QApplication::clipboard()->setText(_text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString copyToClipboardContextItemText() const override;
|
QString copyToClipboardContextItemText() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -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<Ui::PopupMenu*> menu,
|
||||||
|
not_null<PeerData*> 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<PhotoData*> 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<PhotoData*> photo) {
|
||||||
|
if (!photo->date || !photo->loaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QApplication::clipboard()->setPixmap(photo->full->pix());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddPhotoActions(
|
||||||
|
not_null<Ui::PopupMenu*> menu,
|
||||||
|
not_null<PhotoData*> 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<DocumentData*> document) {
|
||||||
|
const auto filepath = document->filepath(
|
||||||
|
DocumentData::FilePathResolveChecked);
|
||||||
|
if (!filepath.isEmpty()) {
|
||||||
|
File::ShowInFolder(filepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddSaveDocumentAction(
|
||||||
|
not_null<Ui::PopupMenu*> menu,
|
||||||
|
not_null<DocumentData*> 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<Ui::PopupMenu*> menu,
|
||||||
|
not_null<DocumentData*> 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<DocumentData*> document) {
|
||||||
|
if (const auto sticker = document->sticker()) {
|
||||||
|
if (sticker->set.type() != mtpc_inputStickerSetEmpty) {
|
||||||
|
App::main()->stickersBox(sticker->set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> FillContextMenu(
|
||||||
|
not_null<ListWidget*> list,
|
||||||
|
const ContextMenuRequest &request) {
|
||||||
|
auto result = base::make_unique_q<Ui::PopupMenu>(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<PhotoClickHandler*>(rawLink);
|
||||||
|
const auto linkDocument = dynamic_cast<DocumentClickHandler*>(rawLink);
|
||||||
|
const auto linkPeer = dynamic_cast<PeerClickHandler*>(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<HistoryWebPage*>(media)->attach()) {
|
||||||
|
media = static_cast<HistoryWebPage*>(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
|
|
@ -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<Ui::PopupMenu> FillContextMenu(
|
||||||
|
not_null<ListWidget*> list,
|
||||||
|
const ContextMenuRequest &request);
|
||||||
|
|
||||||
|
} // namespace
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_item_components.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_element.h"
|
||||||
#include "history/view/history_view_message.h"
|
#include "history/view/history_view_message.h"
|
||||||
#include "history/view/history_view_service_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 "window/window_peer_menu.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "core/file_utilities.h"
|
|
||||||
#include "core/tl_help.h"
|
#include "core/tl_help.h"
|
||||||
#include "base/overload.h"
|
#include "base/overload.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -273,6 +273,10 @@ ListWidget::ListWidget(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<ListDelegate*> ListWidget::delegate() const {
|
||||||
|
return _delegate;
|
||||||
|
}
|
||||||
|
|
||||||
void ListWidget::refreshViewer() {
|
void ListWidget::refreshViewer() {
|
||||||
_viewerLifetime.destroy();
|
_viewerLifetime.destroy();
|
||||||
_delegate->listSource(
|
_delegate->listSource(
|
||||||
|
@ -758,10 +762,11 @@ void ListWidget::keyPressEvent(QKeyEvent *e) {
|
||||||
if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
|
if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
|
||||||
_delegate->listCloseRequest();
|
_delegate->listCloseRequest();
|
||||||
} else if (e == QKeySequence::Copy && _selectedItem != nullptr) {
|
} else if (e == QKeySequence::Copy && _selectedItem != nullptr) {
|
||||||
copySelectedText();
|
SetClipboardWithEntities(getSelectedText());
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
} else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) {
|
} else if (e->key() == Qt::Key_E
|
||||||
setToClipboard(getSelectedText(), QClipboard::FindBuffer);
|
&& e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||||
|
SetClipboardWithEntities(getSelectedText(), QClipboard::FindBuffer);
|
||||||
#endif // Q_OS_MAC
|
#endif // Q_OS_MAC
|
||||||
} else {
|
} else {
|
||||||
e->ignore();
|
e->ignore();
|
||||||
|
@ -800,247 +805,50 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
mouseActionUpdate(e->globalPos());
|
mouseActionUpdate(e->globalPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
// -1 - has selection, but no over, 0 - no selection, 1 - over text
|
ContextMenuRequest request;
|
||||||
auto isUponSelected = 0;
|
request.link = ClickHandler::getActive();
|
||||||
auto hasSelected = 0;
|
request.view = App::mousedItem();
|
||||||
if (_selectedItem) {
|
request.overView = (request.view == App::hoveredItem());
|
||||||
isUponSelected = -1;
|
request.selectedText = getSelectedText();
|
||||||
|
if (_selectedItem
|
||||||
auto selFrom = _selectedText.from;
|
&& _selectedItem == request.view
|
||||||
auto selTo = _selectedText.to;
|
&& request.overView) {
|
||||||
hasSelected = (selTo > selFrom) ? 1 : 0;
|
const auto mousePos = mapPointToItem(
|
||||||
if (App::mousedItem() && App::mousedItem() == App::hoveredItem()) {
|
mapFromGlobal(_mousePosition),
|
||||||
auto mousePos = mapPointToItem(
|
_selectedItem);
|
||||||
mapFromGlobal(_mousePosition),
|
HistoryStateRequest stateRequest;
|
||||||
App::mousedItem());
|
stateRequest.flags |= Text::StateRequest::Flag::LookupSymbol;
|
||||||
HistoryStateRequest request;
|
const auto dragState = _selectedItem->getState(
|
||||||
request.flags |= Text::StateRequest::Flag::LookupSymbol;
|
mousePos,
|
||||||
auto dragState = App::mousedItem()->getState(mousePos, request);
|
stateRequest);
|
||||||
if (dragState.cursor == HistoryInTextCursorState && dragState.symbol >= selFrom && dragState.symbol < selTo) {
|
if (dragState.cursor == HistoryInTextCursorState
|
||||||
isUponSelected = 1;
|
&& base::in_range(
|
||||||
}
|
dragState.symbol,
|
||||||
|
_selectedText.from,
|
||||||
|
_selectedText.to)) {
|
||||||
|
request.overSelection = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (showFromTouch && hasSelected && isUponSelected < hasSelected) {
|
if (showFromTouch) {
|
||||||
isUponSelected = hasSelected;
|
request.overSelection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_menu = base::make_unique_q<Ui::PopupMenu>(nullptr);
|
_menu = FillContextMenu(this, request);
|
||||||
|
if (_menu && !_menu->actions().isEmpty()) {
|
||||||
_contextMenuLink = ClickHandler::getActive();
|
|
||||||
auto view = App::hoveredItem()
|
|
||||||
? App::hoveredItem()
|
|
||||||
: App::hoveredLinkItem();
|
|
||||||
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLink.get());
|
|
||||||
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLink.get());
|
|
||||||
auto lnkPeer = dynamic_cast<PeerClickHandler*>(_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<HistoryMessage*>(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<HistoryWebPage*>(media)->attach()) {
|
|
||||||
media = static_cast<HistoryWebPage*>(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->popup(e->globalPos());
|
_menu->popup(e->globalPos());
|
||||||
e->accept();
|
e->accept();
|
||||||
|
} else if (_menu) {
|
||||||
|
_menu = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::savePhotoToFile(PhotoData *photo) {
|
TextWithEntities ListWidget::getItemText(FullMsgId itemId) const {
|
||||||
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<DocumentData*> document) {
|
|
||||||
if (auto sticker = document->sticker()) {
|
|
||||||
if (sticker->set.type() != mtpc_inputStickerSetEmpty) {
|
|
||||||
App::main()->stickersBox(sticker->set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListWidget::cancelContextDownload(not_null<DocumentData*> document) {
|
|
||||||
document->cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListWidget::showContextInFolder(not_null<DocumentData*> 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) {
|
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto view = viewForItem(item)) {
|
if (const auto view = viewForItem(item)) {
|
||||||
setToClipboard(view->selectedText(FullSelection));
|
return view->selectedText(FullSelection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return TextWithEntities();
|
||||||
|
|
||||||
void ListWidget::setToClipboard(
|
|
||||||
const TextWithEntities &forClipboard,
|
|
||||||
QClipboard::Mode mode) {
|
|
||||||
if (auto data = MimeDataFromTextWithEntities(forClipboard)) {
|
|
||||||
QApplication::clipboard()->setMimeData(data.release(), mode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::mousePressEvent(QMouseEvent *e) {
|
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 defined Q_OS_LINUX32 || defined Q_OS_LINUX64
|
||||||
if (_selectedItem && _selectedText.from != _selectedText.to) {
|
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
|
#endif // Q_OS_LINUX32 || Q_OS_LINUX64
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@ public:
|
||||||
not_null<Window::Controller*> controller,
|
not_null<Window::Controller*> controller,
|
||||||
not_null<ListDelegate*> delegate);
|
not_null<ListDelegate*> delegate);
|
||||||
|
|
||||||
|
not_null<ListDelegate*> delegate() const;
|
||||||
|
|
||||||
// Set the correct scroll position after being resized.
|
// Set the correct scroll position after being resized.
|
||||||
void restoreScrollPosition();
|
void restoreScrollPosition();
|
||||||
|
|
||||||
|
@ -97,6 +99,9 @@ public:
|
||||||
void saveState(not_null<ListMemento*> memento);
|
void saveState(not_null<ListMemento*> memento);
|
||||||
void restoreState(not_null<ListMemento*> memento);
|
void restoreState(not_null<ListMemento*> memento);
|
||||||
|
|
||||||
|
TextWithEntities getSelectedText() const;
|
||||||
|
TextWithEntities getItemText(FullMsgId itemId) const;
|
||||||
|
|
||||||
// AbstractTooltipShower interface
|
// AbstractTooltipShower interface
|
||||||
QString tooltipText() const override;
|
QString tooltipText() const override;
|
||||||
QPoint tooltipPos() const override;
|
QPoint tooltipPos() const override;
|
||||||
|
@ -171,20 +176,7 @@ private:
|
||||||
QPoint mapPointToItem(QPoint point, const Element *view) const;
|
QPoint mapPointToItem(QPoint point, const Element *view) const;
|
||||||
|
|
||||||
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
|
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
|
||||||
void savePhotoToFile(PhotoData *photo);
|
|
||||||
void saveDocumentToFile(DocumentData *document);
|
|
||||||
void copyContextImage(PhotoData *photo);
|
|
||||||
void showStickerPackInfo(not_null<DocumentData*> document);
|
void showStickerPackInfo(not_null<DocumentData*> document);
|
||||||
void copyContextUrl();
|
|
||||||
void cancelContextDownload(not_null<DocumentData*> document);
|
|
||||||
void showContextInFolder(not_null<DocumentData*> 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<Element*> findItemByY(int y) const;
|
not_null<Element*> findItemByY(int y) const;
|
||||||
Element *strictFindItemByY(int y) const;
|
Element *strictFindItemByY(int y) const;
|
||||||
|
@ -277,8 +269,6 @@ private:
|
||||||
QPoint _trippleClickPoint;
|
QPoint _trippleClickPoint;
|
||||||
base::Timer _trippleClickTimer;
|
base::Timer _trippleClickTimer;
|
||||||
|
|
||||||
ClickHandlerPtr _contextMenuLink;
|
|
||||||
|
|
||||||
rpl::lifetime _viewerLifetime;
|
rpl::lifetime _viewerLifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1283,13 +1283,12 @@ void ListWidget::showContextMenu(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (link) {
|
} else if (link) {
|
||||||
auto linkCopyToClipboardText
|
const auto actionText = link->copyToClipboardContextItemText();
|
||||||
= link->copyToClipboardContextItemText();
|
if (!actionText.isEmpty()) {
|
||||||
if (!linkCopyToClipboardText.isEmpty()) {
|
|
||||||
_contextMenu->addAction(
|
_contextMenu->addAction(
|
||||||
linkCopyToClipboardText,
|
actionText,
|
||||||
[link] {
|
[text = link->copyToClipboardText()] {
|
||||||
link->copyToClipboard();
|
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 defined Q_OS_LINUX32 || defined Q_OS_LINUX64
|
||||||
//if (hasSelectedText()) { // #TODO linux clipboard
|
//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
|
#endif // Q_OS_LINUX32 || Q_OS_LINUX64
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,14 +153,14 @@ void MainWindow::firstShow() {
|
||||||
: lng_enable_notifications_from_tray);
|
: lng_enable_notifications_from_tray);
|
||||||
|
|
||||||
if (isLinux) {
|
if (isLinux) {
|
||||||
trayIconMenu->addAction(lang(lng_open_from_tray), this, SLOT(showFromTray()))->setEnabled(true);
|
trayIconMenu->addAction(lang(lng_open_from_tray), this, SLOT(showFromTray()));
|
||||||
trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray()))->setEnabled(true);
|
trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray()));
|
||||||
trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()))->setEnabled(true);
|
trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()));
|
||||||
trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()))->setEnabled(true);
|
trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()));
|
||||||
} else {
|
} else {
|
||||||
trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray()))->setEnabled(true);
|
trayIconMenu->addAction(lang(lng_minimize_to_tray), this, SLOT(minimizeToTray()));
|
||||||
trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()))->setEnabled(true);
|
trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()));
|
||||||
trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()))->setEnabled(true);
|
trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()));
|
||||||
}
|
}
|
||||||
Global::RefWorkMode().setForced(Global::WorkMode().value(), true);
|
Global::RefWorkMode().setForced(Global::WorkMode().value(), true);
|
||||||
|
|
||||||
|
|
|
@ -2836,7 +2836,7 @@ void MediaView::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
_menu = new Ui::PopupMenu(nullptr, st::mediaviewPopupMenu);
|
_menu = new Ui::PopupMenu(nullptr, st::mediaviewPopupMenu);
|
||||||
updateActions();
|
updateActions();
|
||||||
for_const (auto &action, _actions) {
|
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*)));
|
connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*)));
|
||||||
_menu->popup(e->globalPos());
|
_menu->popup(e->globalPos());
|
||||||
|
|
|
@ -53,8 +53,8 @@ struct TextSelection {
|
||||||
constexpr bool empty() const {
|
constexpr bool empty() const {
|
||||||
return from == to;
|
return from == to;
|
||||||
}
|
}
|
||||||
uint16 from : 16;
|
uint16 from;
|
||||||
uint16 to : 16;
|
uint16 to;
|
||||||
};
|
};
|
||||||
inline bool operator==(TextSelection a, TextSelection b) {
|
inline bool operator==(TextSelection a, TextSelection b) {
|
||||||
return a.from == b.from && a.to == b.to;
|
return a.from == b.from && a.to == b.to;
|
||||||
|
|
|
@ -536,19 +536,23 @@ void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason)
|
||||||
|
|
||||||
_contextMenu = new Ui::PopupMenu(nullptr);
|
_contextMenu = new Ui::PopupMenu(nullptr);
|
||||||
|
|
||||||
_contextMenuClickHandler = ClickHandler::getActive();
|
|
||||||
|
|
||||||
if (fullSelection && !_contextCopyText.isEmpty()) {
|
if (fullSelection && !_contextCopyText.isEmpty()) {
|
||||||
_contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText()))->setEnabled(true);
|
_contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText()));
|
||||||
} else if (uponSelection && !fullSelection) {
|
} 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()) {
|
} 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 (const auto link = ClickHandler::getActive()) {
|
||||||
if (!linkCopyToClipboardText.isEmpty()) {
|
const auto actionText = link->copyToClipboardContextItemText();
|
||||||
_contextMenu->addAction(linkCopyToClipboardText, this, SLOT(onCopyContextUrl()))->setEnabled(true);
|
if (!actionText.isEmpty()) {
|
||||||
|
_contextMenu->addAction(
|
||||||
|
actionText,
|
||||||
|
[text = link->copyToClipboardText()] {
|
||||||
|
QApplication::clipboard()->setText(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_contextMenu->actions().isEmpty()) {
|
if (_contextMenu->actions().isEmpty()) {
|
||||||
|
@ -572,12 +576,6 @@ void FlatLabel::onCopyContextText() {
|
||||||
QApplication::clipboard()->setText(_text.originalText({ 0, 0xFFFF }, _contextExpandLinksMode));
|
QApplication::clipboard()->setText(_text.originalText({ 0, 0xFFFF }, _contextExpandLinksMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlatLabel::onCopyContextUrl() {
|
|
||||||
if (_contextMenuClickHandler) {
|
|
||||||
_contextMenuClickHandler->copyToClipboard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlatLabel::onTouchSelect() {
|
void FlatLabel::onTouchSelect() {
|
||||||
_touchSelect = true;
|
_touchSelect = true;
|
||||||
dragActionStart(_touchPos, Qt::LeftButton);
|
dragActionStart(_touchPos, Qt::LeftButton);
|
||||||
|
|
|
@ -142,7 +142,6 @@ protected:
|
||||||
private slots:
|
private slots:
|
||||||
void onCopySelectedText();
|
void onCopySelectedText();
|
||||||
void onCopyContextText();
|
void onCopyContextText();
|
||||||
void onCopyContextUrl();
|
|
||||||
|
|
||||||
void onTouchSelect();
|
void onTouchSelect();
|
||||||
void onContextMenuDestroy(QObject *obj);
|
void onContextMenuDestroy(QObject *obj);
|
||||||
|
@ -201,7 +200,6 @@ private:
|
||||||
QTimer _trippleClickTimer;
|
QTimer _trippleClickTimer;
|
||||||
|
|
||||||
Ui::PopupMenu *_contextMenu = nullptr;
|
Ui::PopupMenu *_contextMenu = nullptr;
|
||||||
ClickHandlerPtr _contextMenuClickHandler;
|
|
||||||
QString _contextCopyText;
|
QString _contextCopyText;
|
||||||
ExpandLinksMode _contextExpandLinksMode = ExpandLinksAll;
|
ExpandLinksMode _contextExpandLinksMode = ExpandLinksAll;
|
||||||
|
|
||||||
|
|
|
@ -229,6 +229,8 @@
|
||||||
<(src_loc)/history/admin_log/history_admin_log_section.h
|
<(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.cpp
|
||||||
<(src_loc)/history/feed/history_feed_section.h
|
<(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.cpp
|
||||||
<(src_loc)/history/view/history_view_cursor_state.h
|
<(src_loc)/history/view/history_view_cursor_state.h
|
||||||
<(src_loc)/history/view/history_view_element.cpp
|
<(src_loc)/history/view/history_view_element.cpp
|
||||||
|
|
Loading…
Reference in New Issue