mirror of https://github.com/procxx/kepka.git
Allow multiple items selection in HistoryView.
This commit is contained in:
parent
2aa477176c
commit
63c1212ef1
|
@ -574,20 +574,20 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_singleItem) {
|
if (_deleteConfirmedCallback) {
|
||||||
App::main()->clearSelectedItems();
|
_deleteConfirmedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<PeerData*, QVector<MTPint>> idsByPeer;
|
QMap<PeerData*, QVector<MTPint>> idsByPeer;
|
||||||
for_const (auto fullId, _ids) {
|
for (const auto itemId : _ids) {
|
||||||
if (auto item = App::histItemById(fullId)) {
|
if (auto item = App::histItemById(itemId)) {
|
||||||
auto history = item->history();
|
auto history = item->history();
|
||||||
auto wasOnServer = (item->id > 0);
|
auto wasOnServer = (item->id > 0);
|
||||||
auto wasLast = (history->lastMsg == item);
|
auto wasLast = (history->lastMsg == item);
|
||||||
item->destroy();
|
item->destroy();
|
||||||
|
|
||||||
if (wasOnServer) {
|
if (wasOnServer) {
|
||||||
idsByPeer[history->peer].push_back(MTP_int(fullId.msg));
|
idsByPeer[history->peer].push_back(MTP_int(itemId.msg));
|
||||||
} else if (wasLast) {
|
} else if (wasLast) {
|
||||||
App::main()->checkPeerHistory(history->peer);
|
App::main()->checkPeerHistory(history->peer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,10 @@ public:
|
||||||
bool suggestModerateActions);
|
bool suggestModerateActions);
|
||||||
DeleteMessagesBox(QWidget*, MessageIdsList &&selected);
|
DeleteMessagesBox(QWidget*, MessageIdsList &&selected);
|
||||||
|
|
||||||
|
void setDeleteConfirmedCallback(base::lambda<void()> callback) {
|
||||||
|
_deleteConfirmedCallback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
|
||||||
|
@ -188,6 +192,8 @@ private:
|
||||||
object_ptr<Ui::Checkbox> _reportSpam = { nullptr };
|
object_ptr<Ui::Checkbox> _reportSpam = { nullptr };
|
||||||
object_ptr<Ui::Checkbox> _deleteAll = { nullptr };
|
object_ptr<Ui::Checkbox> _deleteAll = { nullptr };
|
||||||
|
|
||||||
|
base::lambda<void()> _deleteConfirmedCallback;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfirmInviteBox : public BoxContent, public RPCSender {
|
class ConfirmInviteBox : public BoxContent, public RPCSender {
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_location_manager.h"
|
#include "history/history_location_manager.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "ui/text_options.h"
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
@ -129,6 +130,19 @@ QString WithCaptionNotificationText(
|
||||||
caption);
|
caption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities WithCaptionClipboardText(
|
||||||
|
const QString &attachType,
|
||||||
|
TextWithEntities &&caption) {
|
||||||
|
TextWithEntities result;
|
||||||
|
result.text.reserve(5 + attachType.size() + caption.text.size());
|
||||||
|
result.text.append(qstr("[ ")).append(attachType).append(qstr(" ]"));
|
||||||
|
if (!caption.text.isEmpty()) {
|
||||||
|
result.text.append(qstr("\n"));
|
||||||
|
TextUtilities::Append(result, std::move(caption));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Media::Media(not_null<HistoryItem*> parent) : _parent(parent) {
|
Media::Media(not_null<HistoryItem*> parent) : _parent(parent) {
|
||||||
|
@ -298,6 +312,12 @@ QString MediaPhoto::pinnedTextSubstring() const {
|
||||||
return lang(lng_action_pinned_media_photo);
|
return lang(lng_action_pinned_media_photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities MediaPhoto::clipboardText() const {
|
||||||
|
return WithCaptionClipboardText(
|
||||||
|
lang(lng_in_dlg_photo),
|
||||||
|
parent()->clipboardText());
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaPhoto::allowsEditCaption() const {
|
bool MediaPhoto::allowsEditCaption() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -536,6 +556,36 @@ QString MediaFile::pinnedTextSubstring() const {
|
||||||
return lang(lng_action_pinned_media_file);
|
return lang(lng_action_pinned_media_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities MediaFile::clipboardText() const {
|
||||||
|
const auto attachType = [&] {
|
||||||
|
const auto name = _document->composeNameString();
|
||||||
|
const auto addName = !name.isEmpty()
|
||||||
|
? qstr(" : ") + name
|
||||||
|
: QString();
|
||||||
|
if (const auto sticker = _document->sticker()) {
|
||||||
|
if (!_emoji.isEmpty()) {
|
||||||
|
return lng_in_dlg_sticker_emoji(lt_emoji, _emoji);
|
||||||
|
}
|
||||||
|
return lang(lng_in_dlg_sticker);
|
||||||
|
} else if (_document->isAnimation()) {
|
||||||
|
if (_document->isVideoMessage()) {
|
||||||
|
return lang(lng_in_dlg_video_message);
|
||||||
|
}
|
||||||
|
return qsl("GIF");
|
||||||
|
} else if (_document->isVideoFile()) {
|
||||||
|
return lang(lng_in_dlg_video);
|
||||||
|
} else if (_document->isVoiceMessage()) {
|
||||||
|
return lang(lng_in_dlg_audio) + addName;
|
||||||
|
} else if (_document->isSong()) {
|
||||||
|
return lang(lng_in_dlg_audio_file) + addName;
|
||||||
|
}
|
||||||
|
return lang(lng_in_dlg_file) + addName;
|
||||||
|
}();
|
||||||
|
return WithCaptionClipboardText(
|
||||||
|
attachType,
|
||||||
|
parent()->clipboardText());
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaFile::allowsEditCaption() const {
|
bool MediaFile::allowsEditCaption() const {
|
||||||
return !_document->isVideoMessage() && !_document->sticker();
|
return !_document->isVideoMessage() && !_document->sticker();
|
||||||
}
|
}
|
||||||
|
@ -666,6 +716,18 @@ QString MediaContact::pinnedTextSubstring() const {
|
||||||
return lang(lng_action_pinned_media_contact);
|
return lang(lng_action_pinned_media_contact);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities MediaContact::clipboardText() const {
|
||||||
|
const auto text = qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" ]\n")
|
||||||
|
+ lng_full_name(
|
||||||
|
lt_first_name,
|
||||||
|
_contact.firstName,
|
||||||
|
lt_last_name,
|
||||||
|
_contact.lastName).trimmed()
|
||||||
|
+ '\n'
|
||||||
|
+ _contact.phoneNumber;
|
||||||
|
return { text, EntitiesInText() };
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaContact::updateInlineResultMedia(const MTPMessageMedia &media) {
|
bool MediaContact::updateInlineResultMedia(const MTPMessageMedia &media) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -734,6 +796,29 @@ QString MediaLocation::pinnedTextSubstring() const {
|
||||||
return lang(lng_action_pinned_media_location);
|
return lang(lng_action_pinned_media_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities MediaLocation::clipboardText() const {
|
||||||
|
TextWithEntities result = {
|
||||||
|
qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"),
|
||||||
|
EntitiesInText()
|
||||||
|
};
|
||||||
|
auto titleResult = TextUtilities::ParseEntities(
|
||||||
|
TextUtilities::Clean(_title),
|
||||||
|
Ui::WebpageTextTitleOptions().flags);
|
||||||
|
auto descriptionResult = TextUtilities::ParseEntities(
|
||||||
|
TextUtilities::Clean(_description),
|
||||||
|
TextParseLinks | TextParseMultiline | TextParseRichText);
|
||||||
|
if (!titleResult.text.isEmpty()) {
|
||||||
|
TextUtilities::Append(result, std::move(titleResult));
|
||||||
|
result.text.append('\n');
|
||||||
|
}
|
||||||
|
if (!descriptionResult.text.isEmpty()) {
|
||||||
|
TextUtilities::Append(result, std::move(descriptionResult));
|
||||||
|
result.text.append('\n');
|
||||||
|
}
|
||||||
|
result.text += LocationClickHandler(_location->coords).dragText();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaLocation::updateInlineResultMedia(const MTPMessageMedia &media) {
|
bool MediaLocation::updateInlineResultMedia(const MTPMessageMedia &media) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -783,6 +868,10 @@ QString MediaCall::pinnedTextSubstring() const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities MediaCall::clipboardText() const {
|
||||||
|
return { qsl("[ ") + notificationText() + qsl(" ]"), EntitiesInText() };
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaCall::allowsForward() const {
|
bool MediaCall::allowsForward() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -852,6 +941,10 @@ QString MediaWebPage::pinnedTextSubstring() const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities MediaWebPage::clipboardText() const {
|
||||||
|
return TextWithEntities();
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaWebPage::allowsEdit() const {
|
bool MediaWebPage::allowsEdit() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -904,6 +997,10 @@ QString MediaGame::pinnedTextSubstring() const {
|
||||||
return lng_action_pinned_media_game(lt_game, title);
|
return lng_action_pinned_media_game(lt_game, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities MediaGame::clipboardText() const {
|
||||||
|
return TextWithEntities();
|
||||||
|
}
|
||||||
|
|
||||||
QString MediaGame::errorTextForForward(
|
QString MediaGame::errorTextForForward(
|
||||||
not_null<ChannelData*> channel) const {
|
not_null<ChannelData*> channel) const {
|
||||||
if (channel->restricted(ChannelRestriction::f_send_games)) {
|
if (channel->restricted(ChannelRestriction::f_send_games)) {
|
||||||
|
@ -965,6 +1062,10 @@ QString MediaInvoice::pinnedTextSubstring() const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities MediaInvoice::clipboardText() const {
|
||||||
|
return TextWithEntities();
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaInvoice::updateInlineResultMedia(const MTPMessageMedia &media) {
|
bool MediaInvoice::updateInlineResultMedia(const MTPMessageMedia &media) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ public:
|
||||||
virtual QString chatsListText() const;
|
virtual QString chatsListText() const;
|
||||||
virtual QString notificationText() const = 0;
|
virtual QString notificationText() const = 0;
|
||||||
virtual QString pinnedTextSubstring() const = 0;
|
virtual QString pinnedTextSubstring() const = 0;
|
||||||
|
virtual TextWithEntities clipboardText() const = 0;
|
||||||
virtual bool allowsForward() const;
|
virtual bool allowsForward() const;
|
||||||
virtual bool allowsEdit() const;
|
virtual bool allowsEdit() const;
|
||||||
virtual bool allowsEditCaption() const;
|
virtual bool allowsEditCaption() const;
|
||||||
|
@ -136,6 +137,7 @@ public:
|
||||||
QString chatsListText() const override;
|
QString chatsListText() const override;
|
||||||
QString notificationText() const override;
|
QString notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
bool allowsEditCaption() const override;
|
bool allowsEditCaption() const override;
|
||||||
QString errorTextForForward(
|
QString errorTextForForward(
|
||||||
not_null<ChannelData*> channel) const override;
|
not_null<ChannelData*> channel) const override;
|
||||||
|
@ -169,6 +171,7 @@ public:
|
||||||
QString chatsListText() const override;
|
QString chatsListText() const override;
|
||||||
QString notificationText() const override;
|
QString notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
bool allowsEditCaption() const override;
|
bool allowsEditCaption() const override;
|
||||||
bool forwardedBecomesUnread() const override;
|
bool forwardedBecomesUnread() const override;
|
||||||
QString errorTextForForward(
|
QString errorTextForForward(
|
||||||
|
@ -201,6 +204,7 @@ public:
|
||||||
const SharedContact *sharedContact() const override;
|
const SharedContact *sharedContact() const override;
|
||||||
QString notificationText() const override;
|
QString notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
|
|
||||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
@ -230,6 +234,7 @@ public:
|
||||||
QString chatsListText() const override;
|
QString chatsListText() const override;
|
||||||
QString notificationText() const override;
|
QString notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
|
|
||||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
@ -255,6 +260,7 @@ public:
|
||||||
const Call *call() const override;
|
const Call *call() const override;
|
||||||
QString notificationText() const override;
|
QString notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
bool allowsForward() const override;
|
bool allowsForward() const override;
|
||||||
bool allowsRevoke() const override;
|
bool allowsRevoke() const override;
|
||||||
|
|
||||||
|
@ -286,6 +292,7 @@ public:
|
||||||
QString chatsListText() const override;
|
QString chatsListText() const override;
|
||||||
QString notificationText() const override;
|
QString notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
bool allowsEdit() const override;
|
bool allowsEdit() const override;
|
||||||
|
|
||||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
|
@ -311,6 +318,7 @@ public:
|
||||||
|
|
||||||
QString notificationText() const override;
|
QString notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
QString errorTextForForward(
|
QString errorTextForForward(
|
||||||
not_null<ChannelData*> channel) const override;
|
not_null<ChannelData*> channel) const override;
|
||||||
|
|
||||||
|
@ -343,6 +351,7 @@ public:
|
||||||
|
|
||||||
QString notificationText() const override;
|
QString notificationText() const override;
|
||||||
QString pinnedTextSubstring() const override;
|
QString pinnedTextSubstring() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
|
|
||||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
|
|
@ -12,6 +12,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/history_item_text.h"
|
||||||
#include "history/admin_log/history_admin_log_section.h"
|
#include "history/admin_log/history_admin_log_section.h"
|
||||||
#include "history/admin_log/history_admin_log_filter.h"
|
#include "history/admin_log/history_admin_log_filter.h"
|
||||||
#include "history/view/history_view_message.h"
|
#include "history/view/history_view_message.h"
|
||||||
|
@ -486,6 +487,11 @@ std::unique_ptr<HistoryView::Element> InnerWidget::elementCreate(
|
||||||
return std::make_unique<HistoryView::Service>(this, message);
|
return std::make_unique<HistoryView::Service>(this, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InnerWidget::elementUnderCursor(
|
||||||
|
not_null<const HistoryView::Element*> view) {
|
||||||
|
return (App::hoveredItem() == view);
|
||||||
|
}
|
||||||
|
|
||||||
void InnerWidget::elementAnimationAutoplayAsync(
|
void InnerWidget::elementAnimationAutoplayAsync(
|
||||||
not_null<const HistoryView::Element*> view) {
|
not_null<const HistoryView::Element*> view) {
|
||||||
crl::on_main(this, [this, msgId = view->data()->fullId()] {
|
crl::on_main(this, [this, msgId = view->data()->fullId()] {
|
||||||
|
@ -1121,9 +1127,7 @@ 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)) {
|
SetClipboardWithEntities(HistoryItemText(item));
|
||||||
SetClipboardWithEntities(view->selectedText(FullSelection));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,8 +74,10 @@ public:
|
||||||
not_null<HistoryMessage*> message) override;
|
not_null<HistoryMessage*> message) override;
|
||||||
std::unique_ptr<HistoryView::Element> elementCreate(
|
std::unique_ptr<HistoryView::Element> elementCreate(
|
||||||
not_null<HistoryService*> message) override;
|
not_null<HistoryService*> message) override;
|
||||||
|
bool elementUnderCursor(
|
||||||
|
not_null<const HistoryView::Element*> view) override;
|
||||||
void elementAnimationAutoplayAsync(
|
void elementAnimationAutoplayAsync(
|
||||||
not_null<const HistoryView::Element*> element) override;
|
not_null<const HistoryView::Element*> view) override;
|
||||||
|
|
||||||
~InnerWidget();
|
~InnerWidget();
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#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"
|
||||||
|
#include "history/history_item.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
@ -20,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/popup_menu.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 "window/window_peer_menu.h"
|
||||||
#include "data/data_feed_messages.h"
|
#include "data/data_feed_messages.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
@ -69,6 +71,18 @@ Widget::Widget(
|
||||||
_topBar->move(0, 0);
|
_topBar->move(0, 0);
|
||||||
_topBar->resizeToWidth(width());
|
_topBar->resizeToWidth(width());
|
||||||
_topBar->show();
|
_topBar->show();
|
||||||
|
_topBar->forwardSelectionRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
forwardSelected();
|
||||||
|
}, _topBar->lifetime());
|
||||||
|
_topBar->deleteSelectionRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
confirmDeleteSelected();
|
||||||
|
}, _topBar->lifetime());
|
||||||
|
_topBar->clearSelectionRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
clearSelected();
|
||||||
|
}, _topBar->lifetime());
|
||||||
|
|
||||||
_topBarShadow->raise();
|
_topBarShadow->raise();
|
||||||
updateAdaptiveLayout();
|
updateAdaptiveLayout();
|
||||||
|
@ -175,6 +189,30 @@ rpl::producer<Data::MessagesSlice> Widget::listSource(
|
||||||
limitAfter);
|
limitAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Widget::listAllowsMultiSelect() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Widget::listIsLessInOrder(
|
||||||
|
not_null<HistoryItem*> first,
|
||||||
|
not_null<HistoryItem*> second) {
|
||||||
|
return first->position() < second->position();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::listSelectionChanged(HistoryView::SelectedItems &&items) {
|
||||||
|
HistoryView::TopBarWidget::SelectedState state;
|
||||||
|
state.count = items.size();
|
||||||
|
for (const auto item : items) {
|
||||||
|
if (item.canForward) {
|
||||||
|
++state.canForwardCount;
|
||||||
|
}
|
||||||
|
if (item.canDelete) {
|
||||||
|
++state.canDeleteCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_topBar->showSelected(state);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||||
auto result = std::make_unique<Memento>(_feed);
|
auto result = std::make_unique<Memento>(_feed);
|
||||||
saveState(result.get());
|
saveState(result.get());
|
||||||
|
@ -280,4 +318,35 @@ QRect Widget::rectForFloatPlayer() const {
|
||||||
return mapToGlobal(_scroll->geometry());
|
return mapToGlobal(_scroll->geometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::forwardSelected() {
|
||||||
|
auto items = _inner->getSelectedItems();
|
||||||
|
if (items.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto weak = make_weak(this);
|
||||||
|
Window::ShowForwardMessagesBox(std::move(items), [=] {
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->clearSelected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::confirmDeleteSelected() {
|
||||||
|
auto items = _inner->getSelectedItems();
|
||||||
|
if (items.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto weak = make_weak(this);
|
||||||
|
const auto box = Ui::show(Box<DeleteMessagesBox>(std::move(items)));
|
||||||
|
box->setDeleteConfirmedCallback([=] {
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->clearSelected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::clearSelected() {
|
||||||
|
_inner->cancelSelection();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryFeed
|
} // namespace HistoryFeed
|
||||||
|
|
|
@ -68,6 +68,12 @@ public:
|
||||||
Data::MessagePosition aroundId,
|
Data::MessagePosition aroundId,
|
||||||
int limitBefore,
|
int limitBefore,
|
||||||
int limitAfter) override;
|
int limitAfter) override;
|
||||||
|
bool listAllowsMultiSelect() override;
|
||||||
|
bool listIsLessInOrder(
|
||||||
|
not_null<HistoryItem*> first,
|
||||||
|
not_null<HistoryItem*> second) override;
|
||||||
|
void listSelectionChanged(
|
||||||
|
HistoryView::SelectedItems &&items) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
@ -86,6 +92,10 @@ private:
|
||||||
void saveState(not_null<Memento*> memento);
|
void saveState(not_null<Memento*> memento);
|
||||||
void restoreState(not_null<Memento*> memento);
|
void restoreState(not_null<Memento*> memento);
|
||||||
|
|
||||||
|
void forwardSelected();
|
||||||
|
void confirmDeleteSelected();
|
||||||
|
void clearSelected();
|
||||||
|
|
||||||
not_null<Data::Feed*> _feed;
|
not_null<Data::Feed*> _feed;
|
||||||
object_ptr<Ui::ScrollArea> _scroll;
|
object_ptr<Ui::ScrollArea> _scroll;
|
||||||
QPointer<HistoryView::ListWidget> _inner;
|
QPointer<HistoryView::ListWidget> _inner;
|
||||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/history_item_text.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"
|
||||||
#include "ui/text_options.h"
|
#include "ui/text_options.h"
|
||||||
|
@ -1326,7 +1327,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
|
|
||||||
// -2 - has full selected items, but not over, -1 - has selection, but no over, 0 - no selection, 1 - over text, 2 - over full selected items
|
// -2 - has full selected items, but not over, -1 - has selection, but no over, 0 - no selection, 1 - over text, 2 - over full selected items
|
||||||
auto isUponSelected = 0;
|
auto isUponSelected = 0;
|
||||||
auto hasSelected = 0;;
|
auto hasSelected = 0;
|
||||||
if (!_selected.empty()) {
|
if (!_selected.empty()) {
|
||||||
isUponSelected = -1;
|
isUponSelected = -1;
|
||||||
if (_selected.cbegin()->second == FullSelection) {
|
if (_selected.cbegin()->second == FullSelection) {
|
||||||
|
@ -1441,14 +1442,18 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
}
|
}
|
||||||
if (isUponSelected > 1) {
|
if (isUponSelected > 1) {
|
||||||
if (selectedState.count > 0 && selectedState.canForwardCount == selectedState.count) {
|
if (selectedState.count > 0 && selectedState.canForwardCount == selectedState.count) {
|
||||||
_menu->addAction(lang(lng_context_forward_selected), _widget, SLOT(onForwardSelected()));
|
_menu->addAction(lang(lng_context_forward_selected), [=] {
|
||||||
|
_widget->forwardSelected();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (selectedState.count > 0 && selectedState.canDeleteCount == selectedState.count) {
|
if (selectedState.count > 0 && selectedState.canDeleteCount == selectedState.count) {
|
||||||
_menu->addAction(lang(lng_context_delete_selected), [=] {
|
_menu->addAction(lang(lng_context_delete_selected), [=] {
|
||||||
_widget->confirmDeleteSelectedItems();
|
_widget->confirmDeleteSelected();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_menu->addAction(lang(lng_context_clear_selection), _widget, SLOT(onClearSelected()));
|
_menu->addAction(lang(lng_context_clear_selection), [=] {
|
||||||
|
_widget->clearSelected();
|
||||||
|
});
|
||||||
} else if (item) {
|
} else if (item) {
|
||||||
const auto itemId = item->fullId();
|
const auto itemId = item->fullId();
|
||||||
if (isUponSelected != -2) {
|
if (isUponSelected != -2) {
|
||||||
|
@ -1576,14 +1581,18 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
}
|
}
|
||||||
if (isUponSelected > 1) {
|
if (isUponSelected > 1) {
|
||||||
if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) {
|
if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) {
|
||||||
_menu->addAction(lang(lng_context_forward_selected), _widget, SLOT(onForwardSelected()));
|
_menu->addAction(lang(lng_context_forward_selected), [=] {
|
||||||
|
_widget->forwardSelected();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (selectedState.count > 0 && selectedState.count == selectedState.canDeleteCount) {
|
if (selectedState.count > 0 && selectedState.count == selectedState.canDeleteCount) {
|
||||||
_menu->addAction(lang(lng_context_delete_selected), [=] {
|
_menu->addAction(lang(lng_context_delete_selected), [=] {
|
||||||
_widget->confirmDeleteSelectedItems();
|
_widget->confirmDeleteSelected();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_menu->addAction(lang(lng_context_clear_selection), _widget, SLOT(onClearSelected()));
|
_menu->addAction(lang(lng_context_clear_selection), [=] {
|
||||||
|
_widget->clearSelected();
|
||||||
|
});
|
||||||
} else if (item && ((isUponSelected != -2 && (canForward || canDelete)) || item->id > 0)) {
|
} else if (item && ((isUponSelected != -2 && (canForward || canDelete)) || item->id > 0)) {
|
||||||
if (isUponSelected != -2) {
|
if (isUponSelected != -2) {
|
||||||
if (canForward) {
|
if (canForward) {
|
||||||
|
@ -1709,10 +1718,8 @@ void HistoryInner::saveContextGif(FullMsgId itemId) {
|
||||||
|
|
||||||
void HistoryInner::copyContextText(FullMsgId itemId) {
|
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()) {
|
// #TODO check for a group
|
||||||
// #TODO check for a group
|
SetClipboardWithEntities(HistoryItemText(item));
|
||||||
SetClipboardWithEntities(view->selectedText(FullSelection));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1743,13 +1750,11 @@ TextWithEntities HistoryInner::getSelectedText() const {
|
||||||
auto fullSize = 0;
|
auto fullSize = 0;
|
||||||
auto texts = base::flat_map<std::pair<int, MsgId>, TextWithEntities>();
|
auto texts = base::flat_map<std::pair<int, MsgId>, TextWithEntities>();
|
||||||
|
|
||||||
const auto addItem = [&](
|
const auto addItem = [&](not_null<HistoryView::Element*> view) {
|
||||||
not_null<HistoryView::Element*> view,
|
|
||||||
TextSelection selection) {
|
|
||||||
const auto item = view->data();
|
const auto item = view->data();
|
||||||
auto time = item->date.toString(timeFormat);
|
auto time = item->date.toString(timeFormat);
|
||||||
auto part = TextWithEntities();
|
auto part = TextWithEntities();
|
||||||
auto unwrapped = view->selectedText(selection);
|
auto unwrapped = HistoryItemText(item);
|
||||||
auto size = item->author()->name.size()
|
auto size = item->author()->name.size()
|
||||||
+ time.size()
|
+ time.size()
|
||||||
+ unwrapped.text.size();
|
+ unwrapped.text.size();
|
||||||
|
@ -1780,17 +1785,17 @@ TextWithEntities HistoryInner::getSelectedText() const {
|
||||||
// group->leader);
|
// group->leader);
|
||||||
//if (leaderSelection == FullSelection) {
|
//if (leaderSelection == FullSelection) {
|
||||||
// groupLeadersAdded.emplace(group->leader);
|
// groupLeadersAdded.emplace(group->leader);
|
||||||
// addItem(group->leader, FullSelection);
|
// addItem(group->leader);
|
||||||
//} else if (view == group->leader) {
|
//} else if (view == group->leader) {
|
||||||
// const auto leaderFullSelection = AddGroupItemSelection(
|
// const auto leaderFullSelection = AddGroupItemSelection(
|
||||||
// TextSelection(),
|
// TextSelection(),
|
||||||
// int(group->others.size()));
|
// int(group->others.size()));
|
||||||
// addItem(view, leaderFullSelection);
|
// addItem(view, leaderFullSelection);
|
||||||
//} else {
|
//} else {
|
||||||
// addItem(view, FullSelection);
|
// addItem(view);
|
||||||
//}
|
//}
|
||||||
} else {
|
} else {
|
||||||
addItem(view, FullSelection);
|
addItem(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1820,7 +1825,7 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) {
|
||||||
auto selectedState = getSelectionState();
|
auto selectedState = getSelectionState();
|
||||||
if (selectedState.count > 0
|
if (selectedState.count > 0
|
||||||
&& selectedState.canDeleteCount == selectedState.count) {
|
&& selectedState.canDeleteCount == selectedState.count) {
|
||||||
_widget->confirmDeleteSelectedItems();
|
_widget->confirmDeleteSelected();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
e->ignore();
|
e->ignore();
|
||||||
|
@ -2106,7 +2111,7 @@ bool HistoryInner::focusNextPrevChild(bool next) {
|
||||||
if (_selected.empty()) {
|
if (_selected.empty()) {
|
||||||
return TWidget::focusNextPrevChild(next);
|
return TWidget::focusNextPrevChild(next);
|
||||||
} else {
|
} else {
|
||||||
clearSelectedItems();
|
clearSelected();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2208,7 +2213,7 @@ auto HistoryInner::getSelectionState() const
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryInner::clearSelectedItems(bool onlyTextSelection) {
|
void HistoryInner::clearSelected(bool onlyTextSelection) {
|
||||||
if (!_selected.empty() && (!onlyTextSelection || _selected.cbegin()->second != FullSelection)) {
|
if (!_selected.empty() && (!onlyTextSelection || _selected.cbegin()->second != FullSelection)) {
|
||||||
_selected.clear();
|
_selected.clear();
|
||||||
_widget->updateTopBarSelection();
|
_widget->updateTopBarSelection();
|
||||||
|
@ -2927,6 +2932,10 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
|
||||||
not_null<HistoryService*> message) override {
|
not_null<HistoryService*> message) override {
|
||||||
return std::make_unique<HistoryView::Service>(this, message);
|
return std::make_unique<HistoryView::Service>(this, message);
|
||||||
}
|
}
|
||||||
|
bool elementUnderCursor(
|
||||||
|
not_null<const HistoryView::Element*> view) override {
|
||||||
|
return (App::hoveredItem() == view);
|
||||||
|
}
|
||||||
void elementAnimationAutoplayAsync(
|
void elementAnimationAutoplayAsync(
|
||||||
not_null<const HistoryView::Element*> view) override {
|
not_null<const HistoryView::Element*> view) override {
|
||||||
crl::on_main(&Auth(), [msgId = view->data()->fullId()] {
|
crl::on_main(&Auth(), [msgId = view->data()->fullId()] {
|
||||||
|
|
|
@ -58,7 +58,7 @@ public:
|
||||||
bool canDeleteSelected() const;
|
bool canDeleteSelected() const;
|
||||||
|
|
||||||
HistoryView::TopBarWidget::SelectedState getSelectionState() const;
|
HistoryView::TopBarWidget::SelectedState getSelectionState() const;
|
||||||
void clearSelectedItems(bool onlyTextSelection = false);
|
void clearSelected(bool onlyTextSelection = false);
|
||||||
MessageIdsList getSelectedItems() const;
|
MessageIdsList getSelectedItems() const;
|
||||||
void selectItem(not_null<HistoryItem*> item);
|
void selectItem(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
|
|
@ -679,14 +679,25 @@ HistoryItem::~HistoryItem() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClickHandlerPtr goToMessageClickHandler(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
FullMsgId returnToId) {
|
||||||
|
return goToMessageClickHandler(
|
||||||
|
item->history()->peer,
|
||||||
|
item->id,
|
||||||
|
returnToId);
|
||||||
|
}
|
||||||
|
|
||||||
ClickHandlerPtr goToMessageClickHandler(
|
ClickHandlerPtr goToMessageClickHandler(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId msgId) {
|
MsgId msgId,
|
||||||
|
FullMsgId returnToId) {
|
||||||
return std::make_shared<LambdaClickHandler>([=] {
|
return std::make_shared<LambdaClickHandler>([=] {
|
||||||
if (App::main()) {
|
if (const auto main = App::main()) {
|
||||||
auto view = App::mousedItem();
|
if (const auto returnTo = App::histItemById(returnToId)) {
|
||||||
if (view && view->data()->history()->peer == peer) {
|
if (returnTo->history()->peer == peer) {
|
||||||
App::main()->pushReplyReturn(view->data());
|
main->pushReplyReturn(returnTo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
App::wnd()->controller()->showPeerHistory(
|
App::wnd()->controller()->showPeerHistory(
|
||||||
peer,
|
peer,
|
||||||
|
@ -696,10 +707,6 @@ ClickHandlerPtr goToMessageClickHandler(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ClickHandlerPtr goToMessageClickHandler(not_null<HistoryItem*> item) {
|
|
||||||
return goToMessageClickHandler(item->history()->peer, item->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
not_null<HistoryItem*> HistoryItem::Create(
|
not_null<HistoryItem*> HistoryItem::Create(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
const MTPMessage &message) {
|
const MTPMessage &message) {
|
||||||
|
|
|
@ -180,6 +180,9 @@ public:
|
||||||
virtual TextWithEntities originalText() const {
|
virtual TextWithEntities originalText() const {
|
||||||
return { QString(), EntitiesInText() };
|
return { QString(), EntitiesInText() };
|
||||||
}
|
}
|
||||||
|
virtual TextWithEntities clipboardText() const {
|
||||||
|
return { QString(), EntitiesInText() };
|
||||||
|
}
|
||||||
|
|
||||||
virtual void setViewsCount(int32 count) {
|
virtual void setViewsCount(int32 count) {
|
||||||
}
|
}
|
||||||
|
@ -302,5 +305,8 @@ private:
|
||||||
|
|
||||||
ClickHandlerPtr goToMessageClickHandler(
|
ClickHandlerPtr goToMessageClickHandler(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
MsgId msgId);
|
MsgId msgId,
|
||||||
ClickHandlerPtr goToMessageClickHandler(not_null<HistoryItem*> item);
|
FullMsgId returnToId = FullMsgId());
|
||||||
|
ClickHandlerPtr goToMessageClickHandler(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
FullMsgId returnToId = FullMsgId());
|
||||||
|
|
|
@ -125,7 +125,9 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) {
|
bool HistoryMessageReply::updateData(
|
||||||
|
not_null<HistoryMessage*> holder,
|
||||||
|
bool force) {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
if (replyToMsg || !replyToMsgId) {
|
if (replyToMsg || !replyToMsgId) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -152,7 +154,7 @@ bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) {
|
||||||
|
|
||||||
updateName();
|
updateName();
|
||||||
|
|
||||||
replyToLnk = goToMessageClickHandler(replyToMsg);
|
replyToLnk = goToMessageClickHandler(replyToMsg, holder->fullId());
|
||||||
if (!replyToMsg->Has<HistoryMessageForwarded>()) {
|
if (!replyToMsg->Has<HistoryMessageForwarded>()) {
|
||||||
if (auto bot = replyToMsg->viaBot()) {
|
if (auto bot = replyToMsg->viaBot()) {
|
||||||
replyToVia = std::make_unique<HistoryMessageVia>();
|
replyToVia = std::make_unique<HistoryMessageVia>();
|
||||||
|
@ -168,7 +170,7 @@ bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) {
|
||||||
return (replyToMsg || !replyToMsgId);
|
return (replyToMsg || !replyToMsgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryMessageReply::clearData(HistoryMessage *holder) {
|
void HistoryMessageReply::clearData(not_null<HistoryMessage*> holder) {
|
||||||
replyToVia = nullptr;
|
replyToVia = nullptr;
|
||||||
if (replyToMsg) {
|
if (replyToMsg) {
|
||||||
App::historyUnregDependency(holder, replyToMsg);
|
App::historyUnregDependency(holder, replyToMsg);
|
||||||
|
|
|
@ -84,10 +84,10 @@ struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, Histor
|
||||||
Expects(replyToVia == nullptr);
|
Expects(replyToVia == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool updateData(HistoryMessage *holder, bool force = false);
|
bool updateData(not_null<HistoryMessage*> holder, bool force = false);
|
||||||
|
|
||||||
// Must be called before destructor.
|
// Must be called before destructor.
|
||||||
void clearData(HistoryMessage *holder);
|
void clearData(not_null<HistoryMessage*> holder);
|
||||||
|
|
||||||
bool isNameUpdated() const;
|
bool isNameUpdated() const;
|
||||||
void updateName() const;
|
void updateName() const;
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
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/history_item_text.h"
|
||||||
|
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "history/history_item_components.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
#include "data/data_web_page.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/text_options.h"
|
||||||
|
|
||||||
|
TextWithEntities WrapAsReply(
|
||||||
|
TextWithEntities &&text,
|
||||||
|
not_null<HistoryItem*> to) {
|
||||||
|
const auto name = to->author()->name;
|
||||||
|
auto result = TextWithEntities();
|
||||||
|
result.text.reserve(
|
||||||
|
lang(lng_in_reply_to).size()
|
||||||
|
+ name.size()
|
||||||
|
+ 4
|
||||||
|
+ text.text.size());
|
||||||
|
result.text.append('['
|
||||||
|
).append(lang(lng_in_reply_to)
|
||||||
|
).append(' '
|
||||||
|
).append(name
|
||||||
|
).append(qsl("]\n")
|
||||||
|
);
|
||||||
|
TextUtilities::Append(result, std::move(text));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextWithEntities WrapAsForwarded(
|
||||||
|
TextWithEntities &&text,
|
||||||
|
not_null<HistoryMessageForwarded*> forwarded) {
|
||||||
|
auto info = forwarded->text.originalTextWithEntities(
|
||||||
|
AllTextSelection,
|
||||||
|
ExpandLinksAll);
|
||||||
|
auto result = TextWithEntities();
|
||||||
|
result.text.reserve(
|
||||||
|
info.text.size()
|
||||||
|
+ 4
|
||||||
|
+ text.text.size());
|
||||||
|
result.entities.reserve(
|
||||||
|
info.entities.size()
|
||||||
|
+ text.entities.size());
|
||||||
|
result.text.append('[');
|
||||||
|
TextUtilities::Append(result, std::move(info));
|
||||||
|
result.text.append(qsl("]\n"));
|
||||||
|
TextUtilities::Append(result, std::move(text));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextWithEntities HistoryItemText(not_null<HistoryItem*> item) {
|
||||||
|
const auto media = item->media();
|
||||||
|
|
||||||
|
auto textResult = item->clipboardText();
|
||||||
|
auto mediaResult = media ? media->clipboardText() : TextWithEntities();
|
||||||
|
auto logEntryOriginalResult = [&] {
|
||||||
|
const auto entry = item->Get<HistoryMessageLogEntryOriginal>();
|
||||||
|
if (!entry) {
|
||||||
|
return TextWithEntities();
|
||||||
|
}
|
||||||
|
const auto title = TextUtilities::SingleLine(entry->page->title.isEmpty()
|
||||||
|
? entry->page->author
|
||||||
|
: entry->page->title);
|
||||||
|
auto titleResult = TextUtilities::ParseEntities(
|
||||||
|
title,
|
||||||
|
Ui::WebpageTextTitleOptions().flags);
|
||||||
|
auto descriptionResult = entry->page->description;
|
||||||
|
if (titleResult.text.isEmpty()) {
|
||||||
|
return descriptionResult;
|
||||||
|
} else if (descriptionResult.text.isEmpty()) {
|
||||||
|
return titleResult;
|
||||||
|
}
|
||||||
|
titleResult.text += '\n';
|
||||||
|
TextUtilities::Append(titleResult, std::move(descriptionResult));
|
||||||
|
return titleResult;
|
||||||
|
}();
|
||||||
|
auto result = textResult;
|
||||||
|
if (result.text.isEmpty()) {
|
||||||
|
result = std::move(mediaResult);
|
||||||
|
} else if (!mediaResult.text.isEmpty()) {
|
||||||
|
result.text += qstr("\n\n");
|
||||||
|
TextUtilities::Append(result, std::move(mediaResult));
|
||||||
|
}
|
||||||
|
if (result.text.isEmpty()) {
|
||||||
|
result = std::move(logEntryOriginalResult);
|
||||||
|
} else if (!logEntryOriginalResult.text.isEmpty()) {
|
||||||
|
result.text += qstr("\n\n");
|
||||||
|
TextUtilities::Append(result, std::move(logEntryOriginalResult));
|
||||||
|
}
|
||||||
|
if (const auto reply = item->Get<HistoryMessageReply>()) {
|
||||||
|
if (const auto message = reply->replyToMsg) {
|
||||||
|
result = WrapAsReply(std::move(result), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
|
||||||
|
result = WrapAsForwarded(std::move(result), forwarded);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
TextWithEntities HistoryItemText(not_null<HistoryItem*> item);
|
|
@ -59,7 +59,9 @@ public:
|
||||||
|
|
||||||
virtual HistoryMediaType type() const = 0;
|
virtual HistoryMediaType type() const = 0;
|
||||||
|
|
||||||
virtual TextWithEntities selectedText(TextSelection selection) const = 0;
|
virtual TextWithEntities selectedText(TextSelection selection) const {
|
||||||
|
return TextWithEntities();
|
||||||
|
}
|
||||||
|
|
||||||
bool hasPoint(QPoint point) const {
|
bool hasPoint(QPoint point) const {
|
||||||
return QRect(0, 0, width(), height()).contains(point);
|
return QRect(0, 0, width(), height()).contains(point);
|
||||||
|
|
|
@ -261,15 +261,17 @@ TextSelection HistoryGroupedMedia::adjustSelection(
|
||||||
|
|
||||||
TextWithEntities HistoryGroupedMedia::selectedText(
|
TextWithEntities HistoryGroupedMedia::selectedText(
|
||||||
TextSelection selection) const {
|
TextSelection selection) const {
|
||||||
if (!IsSubGroupSelection(selection)) {
|
return _caption.originalTextWithEntities(selection, ExpandLinksAll);
|
||||||
return WithCaptionSelectedText(
|
// #TODO group select
|
||||||
lang(lng_in_dlg_album),
|
//if (!IsSubGroupSelection(selection)) {
|
||||||
_caption,
|
// return WithCaptionSelectedText(
|
||||||
selection);
|
// lang(lng_in_dlg_album),
|
||||||
} else if (IsGroupItemSelection(selection, int(_parts.size()) - 1)) {
|
// _caption,
|
||||||
return main()->selectedText(FullSelection);
|
// selection);
|
||||||
}
|
//} else if (IsGroupItemSelection(selection, int(_parts.size()) - 1)) {
|
||||||
return TextWithEntities();
|
// return main()->selectedText(FullSelection);
|
||||||
|
//}
|
||||||
|
//return TextWithEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryGroupedMedia::clickHandlerActiveChanged(
|
void HistoryGroupedMedia::clickHandlerActiveChanged(
|
||||||
|
|
|
@ -94,27 +94,6 @@ std::unique_ptr<HistoryMedia> CreateAttach(
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TextWithEntities WithCaptionSelectedText(
|
|
||||||
const QString &attachType,
|
|
||||||
const Text &caption,
|
|
||||||
TextSelection selection) {
|
|
||||||
if (selection != FullSelection) {
|
|
||||||
return caption.originalTextWithEntities(selection, ExpandLinksAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextWithEntities result, original;
|
|
||||||
if (!caption.isEmpty()) {
|
|
||||||
original = caption.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
|
|
||||||
}
|
|
||||||
result.text.reserve(5 + attachType.size() + original.text.size());
|
|
||||||
result.text.append(qstr("[ ")).append(attachType).append(qstr(" ]"));
|
|
||||||
if (!caption.isEmpty()) {
|
|
||||||
result.text.append(qstr("\n"));
|
|
||||||
TextUtilities::Append(result, std::move(original));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||||
if (p == _savel || p == _cancell) {
|
if (p == _savel || p == _cancell) {
|
||||||
if (active && !dataLoaded()) {
|
if (active && !dataLoaded()) {
|
||||||
|
@ -682,10 +661,7 @@ void HistoryPhoto::validateGroupedCache(
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryPhoto::selectedText(TextSelection selection) const {
|
TextWithEntities HistoryPhoto::selectedText(TextSelection selection) const {
|
||||||
return WithCaptionSelectedText(
|
return _caption.originalTextWithEntities(selection, ExpandLinksAll);
|
||||||
lang(lng_in_dlg_photo),
|
|
||||||
_caption,
|
|
||||||
selection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryPhoto::needsBubble() const {
|
bool HistoryPhoto::needsBubble() const {
|
||||||
|
@ -1122,10 +1098,7 @@ void HistoryVideo::setStatusSize(int newSize) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryVideo::selectedText(TextSelection selection) const {
|
TextWithEntities HistoryVideo::selectedText(TextSelection selection) const {
|
||||||
return WithCaptionSelectedText(
|
return _caption.originalTextWithEntities(selection, ExpandLinksAll);
|
||||||
lang(lng_in_dlg_video),
|
|
||||||
_caption,
|
|
||||||
selection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryVideo::needsBubble() const {
|
bool HistoryVideo::needsBubble() const {
|
||||||
|
@ -1715,38 +1688,11 @@ bool HistoryDocument::hasTextForCopy() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryDocument::selectedText(TextSelection selection) const {
|
TextWithEntities HistoryDocument::selectedText(TextSelection selection) const {
|
||||||
TextWithEntities result;
|
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||||
buildStringRepresentation([&result, selection](const QString &type, const QString &fileName, const Text &caption) {
|
const auto &caption = captioned->_caption;
|
||||||
auto fullType = type;
|
return caption.originalTextWithEntities(selection, ExpandLinksAll);
|
||||||
if (!fileName.isEmpty()) {
|
|
||||||
fullType.append(qstr(" : ")).append(fileName);
|
|
||||||
}
|
|
||||||
result = WithCaptionSelectedText(fullType, caption, selection);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Callback>
|
|
||||||
void HistoryDocument::buildStringRepresentation(Callback callback) const {
|
|
||||||
const Text emptyCaption;
|
|
||||||
const Text *caption = &emptyCaption;
|
|
||||||
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
|
|
||||||
caption = &captioned->_caption;
|
|
||||||
}
|
}
|
||||||
QString attachType = lang(lng_in_dlg_file);
|
return TextWithEntities();
|
||||||
if (Has<HistoryDocumentVoice>()) {
|
|
||||||
attachType = lang(lng_in_dlg_audio);
|
|
||||||
} else if (_data->isAudioFile()) {
|
|
||||||
attachType = lang(lng_in_dlg_audio_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString attachFileName;
|
|
||||||
if (auto named = Get<HistoryDocumentNamed>()) {
|
|
||||||
if (!named->_name.isEmpty()) {
|
|
||||||
attachFileName = named->_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return callback(attachType, attachFileName, *caption);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryDocument::setStatusSize(int newSize, qint64 realDuration) const {
|
void HistoryDocument::setStatusSize(int newSize, qint64 realDuration) const {
|
||||||
|
@ -2521,7 +2467,7 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryGif::selectedText(TextSelection selection) const {
|
TextWithEntities HistoryGif::selectedText(TextSelection selection) const {
|
||||||
return WithCaptionSelectedText(mediaTypeString(), _caption, selection);
|
return _caption.originalTextWithEntities(selection, ExpandLinksAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGif::needsBubble() const {
|
bool HistoryGif::needsBubble() const {
|
||||||
|
@ -2979,17 +2925,6 @@ HistoryTextState HistorySticker::getState(QPoint point, HistoryStateRequest requ
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString HistorySticker::toString() const {
|
|
||||||
return _emoji.isEmpty() ? lang(lng_in_dlg_sticker) : lng_in_dlg_sticker_emoji(lt_emoji, _emoji);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextWithEntities HistorySticker::selectedText(TextSelection selection) const {
|
|
||||||
if (selection != FullSelection) {
|
|
||||||
return TextWithEntities();
|
|
||||||
}
|
|
||||||
return { qsl("[ ") + toString() + qsl(" ]"), EntitiesInText() };
|
|
||||||
}
|
|
||||||
|
|
||||||
ImagePtr HistorySticker::replyPreview() {
|
ImagePtr HistorySticker::replyPreview() {
|
||||||
return _data->makeReplyPreview();
|
return _data->makeReplyPreview();
|
||||||
}
|
}
|
||||||
|
@ -3197,13 +3132,6 @@ HistoryTextState HistoryContact::getState(QPoint point, HistoryStateRequest requ
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryContact::selectedText(TextSelection selection) const {
|
|
||||||
if (selection != FullSelection) {
|
|
||||||
return TextWithEntities();
|
|
||||||
}
|
|
||||||
return { qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" ]\n") + _name.originalText() + '\n' + _phone, EntitiesInText() };
|
|
||||||
}
|
|
||||||
|
|
||||||
HistoryCall::HistoryCall(
|
HistoryCall::HistoryCall(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<Data::Call*> call)
|
not_null<Data::Call*> call)
|
||||||
|
@ -3291,13 +3219,6 @@ HistoryTextState HistoryCall::getState(QPoint point, HistoryStateRequest request
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryCall::selectedText(TextSelection selection) const {
|
|
||||||
if (selection != FullSelection) {
|
|
||||||
return TextWithEntities();
|
|
||||||
}
|
|
||||||
return { qsl("[ ") + _text + qsl(" ]"), EntitiesInText() };
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
int articleThumbWidth(PhotoData *thumb, int height) {
|
int articleThumbWidth(PhotoData *thumb, int height) {
|
||||||
|
@ -3841,11 +3762,12 @@ bool HistoryWebPage::isDisplayed() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryWebPage::selectedText(TextSelection selection) const {
|
TextWithEntities HistoryWebPage::selectedText(TextSelection selection) const {
|
||||||
if (selection == FullSelection && !isLogEntryOriginal()) {
|
auto titleResult = _title.originalTextWithEntities(
|
||||||
return TextWithEntities();
|
selection,
|
||||||
}
|
ExpandLinksAll);
|
||||||
auto titleResult = _title.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection, ExpandLinksAll);
|
auto descriptionResult = _description.originalTextWithEntities(
|
||||||
auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection((selection == FullSelection) ? AllTextSelection : selection), ExpandLinksAll);
|
toDescriptionSelection(selection),
|
||||||
|
ExpandLinksAll);
|
||||||
if (titleResult.text.isEmpty()) {
|
if (titleResult.text.isEmpty()) {
|
||||||
return descriptionResult;
|
return descriptionResult;
|
||||||
} else if (descriptionResult.text.isEmpty()) {
|
} else if (descriptionResult.text.isEmpty()) {
|
||||||
|
@ -4237,11 +4159,12 @@ void HistoryGame::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pres
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryGame::selectedText(TextSelection selection) const {
|
TextWithEntities HistoryGame::selectedText(TextSelection selection) const {
|
||||||
if (selection == FullSelection) {
|
auto titleResult = _title.originalTextWithEntities(
|
||||||
return TextWithEntities();
|
selection,
|
||||||
}
|
ExpandLinksAll);
|
||||||
auto titleResult = _title.originalTextWithEntities(selection, ExpandLinksAll);
|
auto descriptionResult = _description.originalTextWithEntities(
|
||||||
auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection(selection), ExpandLinksAll);
|
toDescriptionSelection(selection),
|
||||||
|
ExpandLinksAll);
|
||||||
if (titleResult.text.isEmpty()) {
|
if (titleResult.text.isEmpty()) {
|
||||||
return descriptionResult;
|
return descriptionResult;
|
||||||
} else if (descriptionResult.text.isEmpty()) {
|
} else if (descriptionResult.text.isEmpty()) {
|
||||||
|
@ -4637,11 +4560,12 @@ void HistoryInvoice::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool p
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryInvoice::selectedText(TextSelection selection) const {
|
TextWithEntities HistoryInvoice::selectedText(TextSelection selection) const {
|
||||||
if (selection == FullSelection) {
|
auto titleResult = _title.originalTextWithEntities(
|
||||||
return TextWithEntities();
|
selection,
|
||||||
}
|
ExpandLinksAll);
|
||||||
auto titleResult = _title.originalTextWithEntities(selection, ExpandLinksAll);
|
auto descriptionResult = _description.originalTextWithEntities(
|
||||||
auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection(selection), ExpandLinksAll);
|
toDescriptionSelection(selection),
|
||||||
|
ExpandLinksAll);
|
||||||
if (titleResult.text.isEmpty()) {
|
if (titleResult.text.isEmpty()) {
|
||||||
return descriptionResult;
|
return descriptionResult;
|
||||||
} else if (descriptionResult.text.isEmpty()) {
|
} else if (descriptionResult.text.isEmpty()) {
|
||||||
|
@ -4685,12 +4609,11 @@ HistoryLocation::HistoryLocation(
|
||||||
Ui::WebpageTextTitleOptions());
|
Ui::WebpageTextTitleOptions());
|
||||||
}
|
}
|
||||||
if (!description.isEmpty()) {
|
if (!description.isEmpty()) {
|
||||||
auto marked = TextWithEntities { TextUtilities::Clean(description) };
|
|
||||||
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
|
|
||||||
TextUtilities::ParseEntities(marked, parseFlags);
|
|
||||||
_description.setMarkedText(
|
_description.setMarkedText(
|
||||||
st::webPageDescriptionStyle,
|
st::webPageDescriptionStyle,
|
||||||
marked,
|
TextUtilities::ParseEntities(
|
||||||
|
TextUtilities::Clean(description),
|
||||||
|
TextParseLinks | TextParseMultiline | TextParseRichText),
|
||||||
Ui::WebpageTextDescriptionOptions());
|
Ui::WebpageTextDescriptionOptions());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4924,17 +4847,6 @@ TextSelection HistoryLocation::adjustSelection(TextSelection selection, TextSele
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryLocation::selectedText(TextSelection selection) const {
|
TextWithEntities HistoryLocation::selectedText(TextSelection selection) const {
|
||||||
if (selection == FullSelection) {
|
|
||||||
TextWithEntities result = { qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"), EntitiesInText() };
|
|
||||||
auto info = selectedText(AllTextSelection);
|
|
||||||
if (!info.text.isEmpty()) {
|
|
||||||
TextUtilities::Append(result, std::move(info));
|
|
||||||
result.text.append('\n');
|
|
||||||
}
|
|
||||||
result.text += _link->dragText();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto titleResult = _title.originalTextWithEntities(selection);
|
auto titleResult = _title.originalTextWithEntities(selection);
|
||||||
auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection(selection));
|
auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection(selection));
|
||||||
if (titleResult.text.isEmpty()) {
|
if (titleResult.text.isEmpty()) {
|
||||||
|
|
|
@ -41,11 +41,6 @@ namespace Ui {
|
||||||
class EmptyUserpic;
|
class EmptyUserpic;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
TextWithEntities WithCaptionSelectedText(
|
|
||||||
const QString &attachType,
|
|
||||||
const Text &caption,
|
|
||||||
TextSelection selection);
|
|
||||||
|
|
||||||
class HistoryFileMedia : public HistoryMedia {
|
class HistoryFileMedia : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
using HistoryMedia::HistoryMedia;
|
using HistoryMedia::HistoryMedia;
|
||||||
|
@ -390,11 +385,6 @@ private:
|
||||||
void setStatusSize(int newSize, qint64 realDuration = 0) const;
|
void setStatusSize(int newSize, qint64 realDuration = 0) const;
|
||||||
bool updateStatusText() const; // returns showPause
|
bool updateStatusText() const; // returns showPause
|
||||||
|
|
||||||
// Callback is a void(const QString &, const QString &, const Text &) functor.
|
|
||||||
// It will be called as callback(attachType, attachFileName, attachCaption).
|
|
||||||
template <typename Callback>
|
|
||||||
void buildStringRepresentation(Callback callback) const;
|
|
||||||
|
|
||||||
not_null<DocumentData*> _data;
|
not_null<DocumentData*> _data;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -523,8 +513,6 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
|
||||||
|
|
||||||
DocumentData *getDocument() const override {
|
DocumentData *getDocument() const override {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
@ -550,7 +538,6 @@ private:
|
||||||
|
|
||||||
int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const;
|
int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const;
|
||||||
int additionalWidth() const;
|
int additionalWidth() const;
|
||||||
QString toString() const;
|
|
||||||
|
|
||||||
int _pixw = 1;
|
int _pixw = 1;
|
||||||
int _pixh = 1;
|
int _pixh = 1;
|
||||||
|
@ -584,8 +571,6 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
|
||||||
|
|
||||||
bool needsBubble() const override {
|
bool needsBubble() const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -643,8 +628,6 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
|
||||||
|
|
||||||
bool needsBubble() const override {
|
bool needsBubble() const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1017,6 +1017,13 @@ TextWithEntities HistoryMessage::originalText() const {
|
||||||
return _text.originalTextWithEntities();
|
return _text.originalTextWithEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities HistoryMessage::clipboardText() const {
|
||||||
|
if (emptyText()) {
|
||||||
|
return { QString(), EntitiesInText() };
|
||||||
|
}
|
||||||
|
return _text.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
|
||||||
|
}
|
||||||
|
|
||||||
bool HistoryMessage::textHasLinks() const {
|
bool HistoryMessage::textHasLinks() const {
|
||||||
return emptyText() ? false : _text.hasLinks();
|
return emptyText() ? false : _text.hasLinks();
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,6 +121,7 @@ public:
|
||||||
|
|
||||||
void setText(const TextWithEntities &textWithEntities) override;
|
void setText(const TextWithEntities &textWithEntities) override;
|
||||||
TextWithEntities originalText() const override;
|
TextWithEntities originalText() const override;
|
||||||
|
TextWithEntities clipboardText() const override;
|
||||||
bool textHasLinks() const override;
|
bool textHasLinks() const override;
|
||||||
|
|
||||||
int viewsCount() const override;
|
int viewsCount() const override;
|
||||||
|
|
|
@ -400,7 +400,10 @@ HistoryHider::~HistoryHider() {
|
||||||
parent()->noHider(this);
|
parent()->noHider(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> controller) : Window::AbstractSectionWidget(parent, controller)
|
HistoryWidget::HistoryWidget(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::Controller*> controller)
|
||||||
|
: Window::AbstractSectionWidget(parent, controller)
|
||||||
, _fieldBarCancel(this, st::historyReplyCancel)
|
, _fieldBarCancel(this, st::historyReplyCancel)
|
||||||
, _topBar(this, controller)
|
, _topBar(this, controller)
|
||||||
, _scroll(this, st::historyScroll, false)
|
, _scroll(this, st::historyScroll, false)
|
||||||
|
@ -672,9 +675,21 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> cont
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
_topBar->membersShowAreaActive(
|
_topBar->membersShowAreaActive(
|
||||||
) | rpl::start_with_next([this](bool active) {
|
) | rpl::start_with_next([=](bool active) {
|
||||||
setMembersShowAreaActive(active);
|
setMembersShowAreaActive(active);
|
||||||
}, _topBar->lifetime());
|
}, _topBar->lifetime());
|
||||||
|
_topBar->forwardSelectionRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
forwardSelected();
|
||||||
|
}, _topBar->lifetime());
|
||||||
|
_topBar->deleteSelectionRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
confirmDeleteSelected();
|
||||||
|
}, _topBar->lifetime());
|
||||||
|
_topBar->clearSelectionRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
clearSelected();
|
||||||
|
}, _topBar->lifetime());
|
||||||
|
|
||||||
Auth().api().sendActions(
|
Auth().api().sendActions(
|
||||||
) | rpl::start_with_next([this](const ApiWrap::SendOptions &options) {
|
) | rpl::start_with_next([this](const ApiWrap::SendOptions &options) {
|
||||||
|
@ -1675,11 +1690,16 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
|
||||||
Auth().data().stopAutoplayAnimations();
|
Auth().data().stopAutoplayAnimations();
|
||||||
}
|
}
|
||||||
clearReplyReturns();
|
clearReplyReturns();
|
||||||
|
|
||||||
clearAllLoadRequests();
|
clearAllLoadRequests();
|
||||||
|
|
||||||
if (_history) {
|
if (_history) {
|
||||||
if (App::main()) App::main()->saveDraftToCloud();
|
if (Ui::InFocusChain(_list)) {
|
||||||
|
// Removing focus from list clears selected and updates top bar.
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
if (App::main()) {
|
||||||
|
App::main()->saveDraftToCloud();
|
||||||
|
}
|
||||||
if (_migrated) {
|
if (_migrated) {
|
||||||
_migrated->clearLocalDraft(); // use migrated draft only once
|
_migrated->clearLocalDraft(); // use migrated draft only once
|
||||||
_migrated->clearEditDraft();
|
_migrated->clearEditDraft();
|
||||||
|
@ -1710,7 +1730,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
|
||||||
_fieldBarCancel->hide();
|
_fieldBarCancel->hide();
|
||||||
|
|
||||||
_membersDropdownShowTimer.stop();
|
_membersDropdownShowTimer.stop();
|
||||||
_scroll->takeWidget<HistoryInner>().destroyDelayed();
|
_scroll->takeWidget<HistoryInner>().destroy();
|
||||||
_list = nullptr;
|
_list = nullptr;
|
||||||
|
|
||||||
clearInlineBot();
|
clearInlineBot();
|
||||||
|
@ -3972,7 +3992,9 @@ void HistoryWidget::onFieldResize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onFieldFocused() {
|
void HistoryWidget::onFieldFocused() {
|
||||||
if (_list) _list->clearSelectedItems(true);
|
if (_list) {
|
||||||
|
_list->clearSelected(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onCheckFieldAutocomplete() {
|
void HistoryWidget::onCheckFieldAutocomplete() {
|
||||||
|
@ -6178,25 +6200,37 @@ void HistoryWidget::handlePeerUpdate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onForwardSelected() {
|
void HistoryWidget::forwardSelected() {
|
||||||
if (!_list) return;
|
if (!_list) {
|
||||||
auto weak = make_weak(this);
|
return;
|
||||||
|
}
|
||||||
|
const auto weak = make_weak(this);
|
||||||
Window::ShowForwardMessagesBox(getSelectedItems(), [=] {
|
Window::ShowForwardMessagesBox(getSelectedItems(), [=] {
|
||||||
if (weak) {
|
if (const auto strong = weak.data()) {
|
||||||
weak->onClearSelected();
|
strong->clearSelected();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::confirmDeleteSelectedItems() {
|
void HistoryWidget::confirmDeleteSelected() {
|
||||||
if (!_list) return;
|
if (!_list) return;
|
||||||
|
|
||||||
App::main()->deleteLayer(_list->getSelectedItems());
|
auto items = _list->getSelectedItems();
|
||||||
|
if (items.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto weak = make_weak(this);
|
||||||
|
const auto box = Ui::show(Box<DeleteMessagesBox>(std::move(items)));
|
||||||
|
box->setDeleteConfirmedCallback([=] {
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->clearSelected();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onListEscapePressed() {
|
void HistoryWidget::onListEscapePressed() {
|
||||||
if (_nonEmptySelection && _list) {
|
if (_nonEmptySelection && _list) {
|
||||||
onClearSelected();
|
clearSelected();
|
||||||
} else {
|
} else {
|
||||||
onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
|
@ -6208,8 +6242,10 @@ void HistoryWidget::onListEnterPressed() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onClearSelected() {
|
void HistoryWidget::clearSelected() {
|
||||||
if (_list) _list->clearSelectedItems();
|
if (_list) {
|
||||||
|
_list->clearSelected();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem *HistoryWidget::getItemFromHistoryOrMigrated(MsgId genericMsgId) const {
|
HistoryItem *HistoryWidget::getItemFromHistoryOrMigrated(MsgId genericMsgId) const {
|
||||||
|
|
|
@ -324,9 +324,9 @@ public:
|
||||||
void grapWithoutTopBarShadow();
|
void grapWithoutTopBarShadow();
|
||||||
void grabFinish() override;
|
void grabFinish() override;
|
||||||
|
|
||||||
bool isItemVisible(HistoryItem *item);
|
void forwardSelected();
|
||||||
|
void confirmDeleteSelected();
|
||||||
void confirmDeleteSelectedItems();
|
void clearSelected();
|
||||||
|
|
||||||
// Float player interface.
|
// Float player interface.
|
||||||
bool wheelEventFromFloatPlayer(QEvent *e) override;
|
bool wheelEventFromFloatPlayer(QEvent *e) override;
|
||||||
|
@ -415,9 +415,6 @@ public slots:
|
||||||
void onCheckFieldAutocomplete();
|
void onCheckFieldAutocomplete();
|
||||||
void onScrollTimer();
|
void onScrollTimer();
|
||||||
|
|
||||||
void onForwardSelected();
|
|
||||||
void onClearSelected();
|
|
||||||
|
|
||||||
void onDraftSaveDelayed();
|
void onDraftSaveDelayed();
|
||||||
void onDraftSave(bool delayed = false);
|
void onDraftSave(bool delayed = false);
|
||||||
void onCloudDraftSave();
|
void onCloudDraftSave();
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "history/view/history_view_list_widget.h"
|
#include "history/view/history_view_list_widget.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
#include "history/history_item_text.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "chat_helpers/message_field.h"
|
#include "chat_helpers/message_field.h"
|
||||||
|
@ -218,7 +219,9 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu(
|
||||||
}
|
}
|
||||||
if (!link && (view->hasVisibleText() || mediaHasTextForCopy)) {
|
if (!link && (view->hasVisibleText() || mediaHasTextForCopy)) {
|
||||||
result->addAction(lang(lng_context_copy_text), [=] {
|
result->addAction(lang(lng_context_copy_text), [=] {
|
||||||
SetClipboardWithEntities(list->getItemText(itemId));
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
|
SetClipboardWithEntities(HistoryItemText(item));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,7 +186,7 @@ int Element::marginBottom() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Element::isUnderCursor() const {
|
bool Element::isUnderCursor() const {
|
||||||
return (App::hoveredItem() == this);
|
return _delegate->elementUnderCursor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::setPendingResize() {
|
void Element::setPendingResize() {
|
||||||
|
|
|
@ -37,6 +37,7 @@ public:
|
||||||
not_null<HistoryMessage*> message) = 0;
|
not_null<HistoryMessage*> message) = 0;
|
||||||
virtual std::unique_ptr<Element> elementCreate(
|
virtual std::unique_ptr<Element> elementCreate(
|
||||||
not_null<HistoryService*> message) = 0;
|
not_null<HistoryService*> message) = 0;
|
||||||
|
virtual bool elementUnderCursor(not_null<const Element*> view) = 0;
|
||||||
virtual void elementAnimationAutoplayAsync(
|
virtual void elementAnimationAutoplayAsync(
|
||||||
not_null<const Element*> element) = 0;
|
not_null<const Element*> element) = 0;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,6 +27,18 @@ namespace HistoryView {
|
||||||
|
|
||||||
enum class Context : char;
|
enum class Context : char;
|
||||||
|
|
||||||
|
struct SelectedItem {
|
||||||
|
explicit SelectedItem(FullMsgId msgId) : msgId(msgId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FullMsgId msgId;
|
||||||
|
bool canDelete = false;
|
||||||
|
bool canForward = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using SelectedItems = std::vector<SelectedItem>;
|
||||||
|
|
||||||
class ListDelegate {
|
class ListDelegate {
|
||||||
public:
|
public:
|
||||||
virtual Context listContext() = 0;
|
virtual Context listContext() = 0;
|
||||||
|
@ -36,9 +48,25 @@ public:
|
||||||
Data::MessagePosition aroundId,
|
Data::MessagePosition aroundId,
|
||||||
int limitBefore,
|
int limitBefore,
|
||||||
int limitAfter) = 0;
|
int limitAfter) = 0;
|
||||||
|
virtual bool listAllowsMultiSelect() = 0;
|
||||||
|
virtual bool listIsLessInOrder(
|
||||||
|
not_null<HistoryItem*> first,
|
||||||
|
not_null<HistoryItem*> second) = 0;
|
||||||
|
virtual void listSelectionChanged(SelectedItems &&items) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SelectionData {
|
||||||
|
bool canDelete = false;
|
||||||
|
bool canForward = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using SelectedMap = base::flat_map<
|
||||||
|
FullMsgId,
|
||||||
|
SelectionData,
|
||||||
|
std::less<>>;
|
||||||
|
|
||||||
class ListMemento {
|
class ListMemento {
|
||||||
public:
|
public:
|
||||||
struct ScrollTopState {
|
struct ScrollTopState {
|
||||||
|
@ -100,7 +128,8 @@ public:
|
||||||
void restoreState(not_null<ListMemento*> memento);
|
void restoreState(not_null<ListMemento*> memento);
|
||||||
|
|
||||||
TextWithEntities getSelectedText() const;
|
TextWithEntities getSelectedText() const;
|
||||||
TextWithEntities getItemText(FullMsgId itemId) const;
|
MessageIdsList getSelectedItems() const;
|
||||||
|
void cancelSelection();
|
||||||
|
|
||||||
// AbstractTooltipShower interface
|
// AbstractTooltipShower interface
|
||||||
QString tooltipText() const override;
|
QString tooltipText() const override;
|
||||||
|
@ -112,6 +141,7 @@ public:
|
||||||
not_null<HistoryMessage*> message) override;
|
not_null<HistoryMessage*> message) override;
|
||||||
std::unique_ptr<Element> elementCreate(
|
std::unique_ptr<Element> elementCreate(
|
||||||
not_null<HistoryService*> message) override;
|
not_null<HistoryService*> message) override;
|
||||||
|
bool elementUnderCursor(not_null<const Element*> view) override;
|
||||||
void elementAnimationAutoplayAsync(
|
void elementAnimationAutoplayAsync(
|
||||||
not_null<const Element*> view) override;
|
not_null<const Element*> view) override;
|
||||||
|
|
||||||
|
@ -136,6 +166,21 @@ protected:
|
||||||
int resizeGetHeight(int newWidth) override;
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct CursorState {
|
||||||
|
FullMsgId itemId;
|
||||||
|
int height = 0;
|
||||||
|
QPoint cursor;
|
||||||
|
bool inside = false;
|
||||||
|
|
||||||
|
inline bool operator==(const CursorState &other) const {
|
||||||
|
return (itemId == other.itemId)
|
||||||
|
&& (cursor == other.cursor);
|
||||||
|
}
|
||||||
|
inline bool operator!=(const CursorState &other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
enum class Direction {
|
enum class Direction {
|
||||||
Up,
|
Up,
|
||||||
Down,
|
Down,
|
||||||
|
@ -144,12 +189,18 @@ private:
|
||||||
None,
|
None,
|
||||||
PrepareDrag,
|
PrepareDrag,
|
||||||
Dragging,
|
Dragging,
|
||||||
|
PrepareSelect,
|
||||||
Selecting,
|
Selecting,
|
||||||
};
|
};
|
||||||
enum class EnumItemsDirection {
|
enum class EnumItemsDirection {
|
||||||
TopToBottom,
|
TopToBottom,
|
||||||
BottomToTop,
|
BottomToTop,
|
||||||
};
|
};
|
||||||
|
enum class DragSelectAction {
|
||||||
|
None,
|
||||||
|
Selecting,
|
||||||
|
Deselecting,
|
||||||
|
};
|
||||||
using ScrollTopState = ListMemento::ScrollTopState;
|
using ScrollTopState = ListMemento::ScrollTopState;
|
||||||
|
|
||||||
void refreshViewer();
|
void refreshViewer();
|
||||||
|
@ -159,16 +210,23 @@ private:
|
||||||
void saveScrollState();
|
void saveScrollState();
|
||||||
void restoreScrollState();
|
void restoreScrollState();
|
||||||
|
|
||||||
|
Element *viewForItem(FullMsgId itemId) const;
|
||||||
Element *viewForItem(const HistoryItem *item) const;
|
Element *viewForItem(const HistoryItem *item) const;
|
||||||
not_null<Element*> enforceViewForItem(not_null<HistoryItem*> item);
|
not_null<Element*> enforceViewForItem(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button);
|
void mouseActionStart(
|
||||||
void mouseActionUpdate(const QPoint &screenPos);
|
const QPoint &globalPosition,
|
||||||
void mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button);
|
Qt::MouseButton button);
|
||||||
|
void mouseActionUpdate(const QPoint &globalPosition);
|
||||||
|
void mouseActionUpdate();
|
||||||
|
void mouseActionFinish(
|
||||||
|
const QPoint &globalPosition,
|
||||||
|
Qt::MouseButton button);
|
||||||
void mouseActionCancel();
|
void mouseActionCancel();
|
||||||
void updateSelected();
|
|
||||||
void performDrag();
|
void performDrag();
|
||||||
|
style::cursor computeMouseCursor() const;
|
||||||
int itemTop(not_null<const Element*> view) const;
|
int itemTop(not_null<const Element*> view) const;
|
||||||
|
void repaintItem(FullMsgId itemId);
|
||||||
void repaintItem(const Element *view);
|
void repaintItem(const Element *view);
|
||||||
void resizeItem(not_null<Element*> view);
|
void resizeItem(not_null<Element*> view);
|
||||||
void refreshItem(not_null<const Element*> view);
|
void refreshItem(not_null<const Element*> view);
|
||||||
|
@ -176,7 +234,6 @@ 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 showStickerPackInfo(not_null<DocumentData*> document);
|
|
||||||
|
|
||||||
not_null<Element*> findItemByY(int y) const;
|
not_null<Element*> findItemByY(int y) const;
|
||||||
Element *strictFindItemByY(int y) const;
|
Element *strictFindItemByY(int y) const;
|
||||||
|
@ -197,6 +254,38 @@ private:
|
||||||
void scrollDateCheck();
|
void scrollDateCheck();
|
||||||
void scrollDateHideByTimer();
|
void scrollDateHideByTimer();
|
||||||
|
|
||||||
|
void trySwitchToWordSelection();
|
||||||
|
void switchToWordSelection();
|
||||||
|
void validateTrippleClickStartTime();
|
||||||
|
SelectedItems collectSelectedItems() const;
|
||||||
|
MessageIdsList collectSelectedIds() const;
|
||||||
|
void pushSelectedItems();
|
||||||
|
void removeItemSelection(
|
||||||
|
const SelectedMap::const_iterator &i);
|
||||||
|
bool hasSelectedText() const;
|
||||||
|
bool hasSelectedItems() const;
|
||||||
|
void clearTextSelection();
|
||||||
|
void clearSelected();
|
||||||
|
void setTextSelection(
|
||||||
|
not_null<Element*> view,
|
||||||
|
TextSelection selection);
|
||||||
|
bool applyItemSelection(SelectedMap &applyTo, FullMsgId itemId) const;
|
||||||
|
void toggleItemSelection(FullMsgId itemId);
|
||||||
|
SelectedMap::iterator itemUnderPressSelection();
|
||||||
|
SelectedMap::const_iterator itemUnderPressSelection() const;
|
||||||
|
bool isItemUnderPressSelected() const;
|
||||||
|
bool requiredToStartDragging(not_null<Element*> view) const;
|
||||||
|
bool isPressInSelectedText(HistoryTextState state) const;
|
||||||
|
void updateDragSelection();
|
||||||
|
void clearDragSelection();
|
||||||
|
void applyDragSelection();
|
||||||
|
void applyDragSelection(SelectedMap &applyTo) const;
|
||||||
|
TextSelection itemRenderSelection(
|
||||||
|
not_null<const Element*> view) const;
|
||||||
|
TextSelection computeRenderSelection(
|
||||||
|
not_null<const SelectedMap*> selected,
|
||||||
|
not_null<const Element*> view) const;
|
||||||
|
|
||||||
// This function finds all history items that are displayed and calls template method
|
// This function finds all history items that are displayed and calls template method
|
||||||
// for each found message (in given direction) in the passed history with passed top offset.
|
// for each found message (in given direction) in the passed history with passed top offset.
|
||||||
//
|
//
|
||||||
|
@ -231,7 +320,10 @@ private:
|
||||||
int _idsLimit = kMinimalIdsLimit;
|
int _idsLimit = kMinimalIdsLimit;
|
||||||
Data::MessagesSlice _slice;
|
Data::MessagesSlice _slice;
|
||||||
std::vector<not_null<Element*>> _items;
|
std::vector<not_null<Element*>> _items;
|
||||||
std::map<not_null<HistoryItem*>, std::unique_ptr<Element>, std::less<>> _views;
|
std::map<
|
||||||
|
not_null<HistoryItem*>,
|
||||||
|
std::unique_ptr<Element>,
|
||||||
|
std::less<>> _views;
|
||||||
int _itemsTop = 0;
|
int _itemsTop = 0;
|
||||||
int _itemsWidth = 0;
|
int _itemsWidth = 0;
|
||||||
int _itemsHeight = 0;
|
int _itemsHeight = 0;
|
||||||
|
@ -252,22 +344,29 @@ private:
|
||||||
|
|
||||||
MouseAction _mouseAction = MouseAction::None;
|
MouseAction _mouseAction = MouseAction::None;
|
||||||
TextSelectType _mouseSelectType = TextSelectType::Letters;
|
TextSelectType _mouseSelectType = TextSelectType::Letters;
|
||||||
QPoint _dragStartPosition;
|
|
||||||
QPoint _mousePosition;
|
QPoint _mousePosition;
|
||||||
Element *_mouseActionItem = nullptr;
|
CursorState _overState;
|
||||||
|
CursorState _pressState;
|
||||||
|
Element *_overItem = nullptr;
|
||||||
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
|
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
|
||||||
uint16 _mouseTextSymbol = 0;
|
uint16 _mouseTextSymbol = 0;
|
||||||
bool _pressWasInactive = false;
|
bool _pressWasInactive = false;
|
||||||
|
|
||||||
Element *_selectedItem = nullptr;
|
bool _selectEnabled = false;
|
||||||
TextSelection _selectedText;
|
HistoryItem *_selectedTextItem = nullptr;
|
||||||
bool _wasSelectedText = false; // was some text selected in current drag action
|
TextSelection _selectedTextRange;
|
||||||
|
TextWithEntities _selectedText;
|
||||||
|
SelectedMap _selected;
|
||||||
|
base::flat_set<FullMsgId> _dragSelected;
|
||||||
|
DragSelectAction _dragSelectAction = DragSelectAction::None;
|
||||||
|
// Was some text selected in current drag action.
|
||||||
|
bool _wasSelectedText = false;
|
||||||
Qt::CursorShape _cursor = style::cur_default;
|
Qt::CursorShape _cursor = style::cur_default;
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
|
|
||||||
QPoint _trippleClickPoint;
|
QPoint _trippleClickPoint;
|
||||||
base::Timer _trippleClickTimer;
|
TimeMs _trippleClickStartTime = 0;
|
||||||
|
|
||||||
rpl::lifetime _viewerLifetime;
|
rpl::lifetime _viewerLifetime;
|
||||||
|
|
||||||
|
|
|
@ -970,13 +970,8 @@ TextWithEntities Message::selectedText(TextSelection selection) const {
|
||||||
const auto media = this->media();
|
const auto media = this->media();
|
||||||
|
|
||||||
TextWithEntities logEntryOriginalResult;
|
TextWithEntities logEntryOriginalResult;
|
||||||
const auto textSelection = (selection == FullSelection)
|
|
||||||
? AllTextSelection
|
|
||||||
: IsSubGroupSelection(selection)
|
|
||||||
? TextSelection(0, 0)
|
|
||||||
: selection;
|
|
||||||
auto textResult = item->_text.originalTextWithEntities(
|
auto textResult = item->_text.originalTextWithEntities(
|
||||||
textSelection,
|
selection,
|
||||||
ExpandLinksAll);
|
ExpandLinksAll);
|
||||||
auto skipped = skipTextSelection(selection);
|
auto skipped = skipTextSelection(selection);
|
||||||
auto mediaDisplayed = (media && media->isDisplayed());
|
auto mediaDisplayed = (media && media->isDisplayed());
|
||||||
|
@ -1002,28 +997,6 @@ TextWithEntities Message::selectedText(TextSelection selection) const {
|
||||||
result.text += qstr("\n\n");
|
result.text += qstr("\n\n");
|
||||||
TextUtilities::Append(result, std::move(logEntryOriginalResult));
|
TextUtilities::Append(result, std::move(logEntryOriginalResult));
|
||||||
}
|
}
|
||||||
if (auto reply = item->Get<HistoryMessageReply>()) {
|
|
||||||
if (selection == FullSelection && reply->replyToMsg) {
|
|
||||||
TextWithEntities wrapped;
|
|
||||||
wrapped.text.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.text.size());
|
|
||||||
wrapped.text.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n"));
|
|
||||||
TextUtilities::Append(wrapped, std::move(result));
|
|
||||||
result = wrapped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (auto forwarded = item->Get<HistoryMessageForwarded>()) {
|
|
||||||
if (selection == FullSelection) {
|
|
||||||
auto fwdinfo = forwarded->text.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
|
|
||||||
auto wrapped = TextWithEntities();
|
|
||||||
wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size());
|
|
||||||
wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size());
|
|
||||||
wrapped.text.append('[');
|
|
||||||
TextUtilities::Append(wrapped, std::move(fwdinfo));
|
|
||||||
wrapped.text.append(qsl("]\n"));
|
|
||||||
TextUtilities::Append(wrapped, std::move(result));
|
|
||||||
result = wrapped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -508,8 +508,7 @@ void Service::updatePressed(QPoint point) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities Service::selectedText(TextSelection selection) const {
|
TextWithEntities Service::selectedText(TextSelection selection) const {
|
||||||
return message()->_text.originalTextWithEntities(
|
return message()->_text.originalTextWithEntities(selection);
|
||||||
(selection == FullSelection) ? AllTextSelection : selection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextSelection Service::adjustSelection(
|
TextSelection Service::adjustSelection(
|
||||||
|
|
|
@ -43,7 +43,7 @@ TopBarWidget::TopBarWidget(
|
||||||
not_null<Window::Controller*> controller)
|
not_null<Window::Controller*> controller)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _controller(controller)
|
, _controller(controller)
|
||||||
, _clearSelection(this, langFactory(lng_selected_clear), st::topBarClearButton)
|
, _clear(this, langFactory(lng_selected_clear), st::topBarClearButton)
|
||||||
, _forward(this, langFactory(lng_selected_forward), st::defaultActiveButton)
|
, _forward(this, langFactory(lng_selected_forward), st::defaultActiveButton)
|
||||||
, _delete(this, langFactory(lng_selected_delete), st::defaultActiveButton)
|
, _delete(this, langFactory(lng_selected_delete), st::defaultActiveButton)
|
||||||
, _back(this, st::historyTopBarBack)
|
, _back(this, st::historyTopBarBack)
|
||||||
|
@ -55,11 +55,11 @@ TopBarWidget::TopBarWidget(
|
||||||
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
|
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
|
||||||
_forward->setClickedCallback([this] { onForwardSelection(); });
|
_forward->setClickedCallback([this] { _forwardSelection.fire({}); });
|
||||||
_forward->setWidthChangedCallback([this] { updateControlsGeometry(); });
|
_forward->setWidthChangedCallback([this] { updateControlsGeometry(); });
|
||||||
_delete->setClickedCallback([this] { onDeleteSelection(); });
|
_delete->setClickedCallback([this] { _deleteSelection.fire({}); });
|
||||||
_delete->setWidthChangedCallback([this] { updateControlsGeometry(); });
|
_delete->setWidthChangedCallback([this] { updateControlsGeometry(); });
|
||||||
_clearSelection->setClickedCallback([this] { onClearSelection(); });
|
_clear->setClickedCallback([this] { _clearSelection.fire({}); });
|
||||||
_call->setClickedCallback([this] { onCall(); });
|
_call->setClickedCallback([this] { onCall(); });
|
||||||
_search->setClickedCallback([this] { onSearch(); });
|
_search->setClickedCallback([this] { onSearch(); });
|
||||||
_menuToggle->setClickedCallback([this] { showMenu(); });
|
_menuToggle->setClickedCallback([this] { showMenu(); });
|
||||||
|
@ -132,18 +132,6 @@ void TopBarWidget::refreshLang() {
|
||||||
InvokeQueued(this, [this] { updateControlsGeometry(); });
|
InvokeQueued(this, [this] { updateControlsGeometry(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TopBarWidget::onForwardSelection() {
|
|
||||||
if (App::main()) App::main()->forwardSelectedItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TopBarWidget::onDeleteSelection() {
|
|
||||||
if (App::main()) App::main()->confirmDeleteSelectedItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TopBarWidget::onClearSelection() {
|
|
||||||
if (App::main()) App::main()->clearSelectedItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TopBarWidget::onSearch() {
|
void TopBarWidget::onSearch() {
|
||||||
if (_activeChat) {
|
if (_activeChat) {
|
||||||
App::main()->searchInChat(_activeChat);
|
App::main()->searchInChat(_activeChat);
|
||||||
|
@ -399,7 +387,7 @@ void TopBarWidget::updateControlsGeometry() {
|
||||||
auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.current(hasSelected ? 1. : 0.));
|
auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.current(hasSelected ? 1. : 0.));
|
||||||
auto otherButtonsTop = selectedButtonsTop + st::topBarHeight;
|
auto otherButtonsTop = selectedButtonsTop + st::topBarHeight;
|
||||||
auto buttonsLeft = st::topBarActionSkip + (Adaptive::OneColumn() ? 0 : st::lineWidth);
|
auto buttonsLeft = st::topBarActionSkip + (Adaptive::OneColumn() ? 0 : st::lineWidth);
|
||||||
auto buttonsWidth = _forward->contentWidth() + _delete->contentWidth() + _clearSelection->width();
|
auto buttonsWidth = _forward->contentWidth() + _delete->contentWidth() + _clear->width();
|
||||||
buttonsWidth += buttonsLeft + st::topBarActionSkip * 3;
|
buttonsWidth += buttonsLeft + st::topBarActionSkip * 3;
|
||||||
|
|
||||||
auto widthLeft = qMin(width() - buttonsWidth, -2 * st::defaultActiveButton.width);
|
auto widthLeft = qMin(width() - buttonsWidth, -2 * st::defaultActiveButton.width);
|
||||||
|
@ -414,7 +402,7 @@ void TopBarWidget::updateControlsGeometry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_delete->moveToLeft(buttonsLeft, selectedButtonsTop);
|
_delete->moveToLeft(buttonsLeft, selectedButtonsTop);
|
||||||
_clearSelection->moveToRight(st::topBarActionSkip, selectedButtonsTop);
|
_clear->moveToRight(st::topBarActionSkip, selectedButtonsTop);
|
||||||
|
|
||||||
if (_unreadBadge) {
|
if (_unreadBadge) {
|
||||||
_unreadBadge->setGeometryToLeft(
|
_unreadBadge->setGeometryToLeft(
|
||||||
|
@ -469,7 +457,7 @@ void TopBarWidget::updateControlsVisibility() {
|
||||||
hideChildren();
|
hideChildren();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_clearSelection->show();
|
_clear->show();
|
||||||
_delete->setVisible(_canDelete);
|
_delete->setVisible(_canDelete);
|
||||||
_forward->setVisible(_canForward);
|
_forward->setVisible(_canForward);
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,16 @@ public:
|
||||||
|
|
||||||
void setActiveChat(Dialogs::Key chat);
|
void setActiveChat(Dialogs::Key chat);
|
||||||
|
|
||||||
|
rpl::producer<> forwardSelectionRequest() const {
|
||||||
|
return _forwardSelection.events();
|
||||||
|
}
|
||||||
|
rpl::producer<> deleteSelectionRequest() const {
|
||||||
|
return _deleteSelection.events();
|
||||||
|
}
|
||||||
|
rpl::producer<> clearSelectionRequest() const {
|
||||||
|
return _clearSelection.events();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void mousePressEvent(QMouseEvent *e) override;
|
void mousePressEvent(QMouseEvent *e) override;
|
||||||
|
@ -62,9 +72,6 @@ private:
|
||||||
void selectedShowCallback();
|
void selectedShowCallback();
|
||||||
void updateInfoToggleActive();
|
void updateInfoToggleActive();
|
||||||
|
|
||||||
void onForwardSelection();
|
|
||||||
void onDeleteSelection();
|
|
||||||
void onClearSelection();
|
|
||||||
void onCall();
|
void onCall();
|
||||||
void onSearch();
|
void onSearch();
|
||||||
void showMenu();
|
void showMenu();
|
||||||
|
@ -95,7 +102,7 @@ private:
|
||||||
|
|
||||||
Animation _selectedShown;
|
Animation _selectedShown;
|
||||||
|
|
||||||
object_ptr<Ui::RoundButton> _clearSelection;
|
object_ptr<Ui::RoundButton> _clear;
|
||||||
object_ptr<Ui::RoundButton> _forward, _delete;
|
object_ptr<Ui::RoundButton> _forward, _delete;
|
||||||
|
|
||||||
object_ptr<Ui::IconButton> _back;
|
object_ptr<Ui::IconButton> _back;
|
||||||
|
@ -121,6 +128,10 @@ private:
|
||||||
int _unreadCounterSubscription = 0;
|
int _unreadCounterSubscription = 0;
|
||||||
base::Timer _onlineUpdater;
|
base::Timer _onlineUpdater;
|
||||||
|
|
||||||
|
rpl::event_stream<> _forwardSelection;
|
||||||
|
rpl::event_stream<> _deleteSelection;
|
||||||
|
rpl::event_stream<> _clearSelection;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -509,11 +509,13 @@ void TopBar::performForward() {
|
||||||
_cancelSelectionClicks.fire({});
|
_cancelSelectionClicks.fire({});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Window::ShowForwardMessagesBox(std::move(items), [weak = make_weak(this)]{
|
Window::ShowForwardMessagesBox(
|
||||||
if (weak) {
|
std::move(items),
|
||||||
weak->_cancelSelectionClicks.fire({});
|
[weak = make_weak(this)] {
|
||||||
}
|
if (weak) {
|
||||||
});
|
weak->_cancelSelectionClicks.fire({});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TopBar::performDelete() {
|
void TopBar::performDelete() {
|
||||||
|
@ -521,7 +523,12 @@ void TopBar::performDelete() {
|
||||||
if (items.empty()) {
|
if (items.empty()) {
|
||||||
_cancelSelectionClicks.fire({});
|
_cancelSelectionClicks.fire({});
|
||||||
} else {
|
} else {
|
||||||
Ui::show(Box<DeleteMessagesBox>(std::move(items)));
|
const auto box = Ui::show(Box<DeleteMessagesBox>(std::move(items)));
|
||||||
|
box->setDeleteConfirmedCallback([weak = make_weak(this)] {
|
||||||
|
if (weak) {
|
||||||
|
weak->_cancelSelectionClicks.fire({});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1387,7 +1387,14 @@ void ListWidget::forwardItems(MessageIdsList &&items) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::deleteSelected() {
|
void ListWidget::deleteSelected() {
|
||||||
deleteItems(collectSelectedIds());
|
if (const auto box = deleteItems(collectSelectedIds())) {
|
||||||
|
const auto weak = make_weak(this);
|
||||||
|
box->setDeleteConfirmedCallback([=]{
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->clearSelected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::deleteItem(UniversalMsgId universalId) {
|
void ListWidget::deleteItem(UniversalMsgId universalId) {
|
||||||
|
@ -1396,11 +1403,14 @@ void ListWidget::deleteItem(UniversalMsgId universalId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::deleteItems(MessageIdsList &&items) {
|
DeleteMessagesBox *ListWidget::deleteItems(MessageIdsList &&items) {
|
||||||
if (!items.empty()) {
|
if (!items.empty()) {
|
||||||
const auto box = Ui::show(Box<DeleteMessagesBox>(std::move(items)));
|
const auto box = Ui::show(
|
||||||
setActionBoxWeak(box.data());
|
Box<DeleteMessagesBox>(std::move(items))).data();
|
||||||
|
setActionBoxWeak(box);
|
||||||
|
return box;
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::setActionBoxWeak(QPointer<Ui::RpWidget> box) {
|
void ListWidget::setActionBoxWeak(QPointer<Ui::RpWidget> box) {
|
||||||
|
@ -1575,7 +1585,7 @@ void ListWidget::enterEventHook(QEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::leaveEventHook(QEvent *e) {
|
void ListWidget::leaveEventHook(QEvent *e) {
|
||||||
if (auto item = _overLayout) {
|
if (const auto item = _overLayout) {
|
||||||
if (_overState.inside) {
|
if (_overState.inside) {
|
||||||
repaintItem(item);
|
repaintItem(item);
|
||||||
_overState.inside = false;
|
_overState.inside = false;
|
||||||
|
@ -1596,12 +1606,12 @@ QPoint ListWidget::clampMousePosition(QPoint position) const {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::mouseActionUpdate(const QPoint &screenPos) {
|
void ListWidget::mouseActionUpdate(const QPoint &globalPosition) {
|
||||||
if (_sections.empty() || _visibleBottom <= _visibleTop) {
|
if (_sections.empty() || _visibleBottom <= _visibleTop) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_mousePosition = screenPos;
|
_mousePosition = globalPosition;
|
||||||
|
|
||||||
auto local = mapFromGlobal(_mousePosition);
|
auto local = mapFromGlobal(_mousePosition);
|
||||||
auto point = clampMousePosition(local);
|
auto point = clampMousePosition(local);
|
||||||
|
@ -1625,14 +1635,14 @@ void ListWidget::mouseActionUpdate(const QPoint &screenPos) {
|
||||||
auto inTextSelection = _overState.inside
|
auto inTextSelection = _overState.inside
|
||||||
&& (_overState.itemId == _pressState.itemId)
|
&& (_overState.itemId == _pressState.itemId)
|
||||||
&& hasSelectedText();
|
&& hasSelectedText();
|
||||||
auto cursorDeltaLength = [&] {
|
|
||||||
auto cursorDelta = (_overState.cursor - _pressState.cursor);
|
|
||||||
return cursorDelta.manhattanLength();
|
|
||||||
};
|
|
||||||
auto dragStartLength = [] {
|
|
||||||
return QApplication::startDragDistance();
|
|
||||||
};
|
|
||||||
if (_overLayout) {
|
if (_overLayout) {
|
||||||
|
auto cursorDeltaLength = [&] {
|
||||||
|
auto cursorDelta = (_overState.cursor - _pressState.cursor);
|
||||||
|
return cursorDelta.manhattanLength();
|
||||||
|
};
|
||||||
|
auto dragStartLength = [] {
|
||||||
|
return QApplication::startDragDistance();
|
||||||
|
};
|
||||||
if (_overState.itemId != _pressState.itemId
|
if (_overState.itemId != _pressState.itemId
|
||||||
|| cursorDeltaLength() >= dragStartLength()) {
|
|| cursorDeltaLength() >= dragStartLength()) {
|
||||||
if (_mouseAction == MouseAction::PrepareDrag) {
|
if (_mouseAction == MouseAction::PrepareDrag) {
|
||||||
|
@ -1770,9 +1780,13 @@ void ListWidget::clearDragSelection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton button) {
|
void ListWidget::mouseActionStart(
|
||||||
mouseActionUpdate(screenPos);
|
const QPoint &globalPosition,
|
||||||
if (button != Qt::LeftButton) return;
|
Qt::MouseButton button) {
|
||||||
|
mouseActionUpdate(globalPosition);
|
||||||
|
if (button != Qt::LeftButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ClickHandler::pressed();
|
ClickHandler::pressed();
|
||||||
if (_pressState != _overState) {
|
if (_pressState != _overState) {
|
||||||
|
@ -1799,9 +1813,9 @@ void ListWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_mouseAction == MouseAction::None && pressLayout) {
|
if (_mouseAction == MouseAction::None && pressLayout) {
|
||||||
HistoryTextState dragState;
|
|
||||||
validateTrippleClickStartTime();
|
validateTrippleClickStartTime();
|
||||||
auto startDistance = (screenPos - _trippleClickPoint).manhattanLength();
|
HistoryTextState dragState;
|
||||||
|
auto startDistance = (globalPosition - _trippleClickPoint).manhattanLength();
|
||||||
auto validStartPoint = startDistance < QApplication::startDragDistance();
|
auto validStartPoint = startDistance < QApplication::startDragDistance();
|
||||||
if (_trippleClickStartTime != 0 && validStartPoint) {
|
if (_trippleClickStartTime != 0 && validStartPoint) {
|
||||||
HistoryStateRequest request;
|
HistoryStateRequest request;
|
||||||
|
@ -1950,8 +1964,10 @@ void ListWidget::performDrag() {
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button) {
|
void ListWidget::mouseActionFinish(
|
||||||
mouseActionUpdate(screenPos);
|
const QPoint &globalPosition,
|
||||||
|
Qt::MouseButton button) {
|
||||||
|
mouseActionUpdate(globalPosition);
|
||||||
|
|
||||||
auto pressState = base::take(_pressState);
|
auto pressState = base::take(_pressState);
|
||||||
repaintItem(pressState.itemId);
|
repaintItem(pressState.itemId);
|
||||||
|
|
|
@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_shared_media.h"
|
#include "data/data_shared_media.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
|
|
||||||
|
class DeleteMessagesBox;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
@ -186,7 +188,7 @@ private:
|
||||||
void forwardItems(MessageIdsList &&items);
|
void forwardItems(MessageIdsList &&items);
|
||||||
void deleteSelected();
|
void deleteSelected();
|
||||||
void deleteItem(UniversalMsgId universalId);
|
void deleteItem(UniversalMsgId universalId);
|
||||||
void deleteItems(MessageIdsList &&items);
|
DeleteMessagesBox *deleteItems(MessageIdsList &&items);
|
||||||
void applyItemSelection(
|
void applyItemSelection(
|
||||||
UniversalMsgId universalId,
|
UniversalMsgId universalId,
|
||||||
TextSelection selection);
|
TextSelection selection);
|
||||||
|
@ -233,12 +235,12 @@ private:
|
||||||
|
|
||||||
QPoint clampMousePosition(QPoint position) const;
|
QPoint clampMousePosition(QPoint position) const;
|
||||||
void mouseActionStart(
|
void mouseActionStart(
|
||||||
const QPoint &screenPos,
|
const QPoint &globalPosition,
|
||||||
Qt::MouseButton button);
|
Qt::MouseButton button);
|
||||||
void mouseActionUpdate(const QPoint &screenPos);
|
void mouseActionUpdate(const QPoint &globalPosition);
|
||||||
void mouseActionUpdate();
|
void mouseActionUpdate();
|
||||||
void mouseActionFinish(
|
void mouseActionFinish(
|
||||||
const QPoint &screenPos,
|
const QPoint &globalPosition,
|
||||||
Qt::MouseButton button);
|
Qt::MouseButton button);
|
||||||
void mouseActionCancel();
|
void mouseActionCancel();
|
||||||
void performDrag();
|
void performDrag();
|
||||||
|
|
|
@ -877,12 +877,6 @@ void MainWidget::showSendPathsLayer() {
|
||||||
hiderLayer(object_ptr<HistoryHider>(this));
|
hiderLayer(object_ptr<HistoryHider>(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::deleteLayer(MessageIdsList &&items) {
|
|
||||||
if (!items.empty()) {
|
|
||||||
Ui::show(Box<DeleteMessagesBox>(std::move(items)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWidget::deleteLayer(FullMsgId itemId) {
|
void MainWidget::deleteLayer(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
const auto suggestModerateActions = true;
|
const auto suggestModerateActions = true;
|
||||||
|
@ -1349,18 +1343,6 @@ void MainWidget::onCacheBackground() {
|
||||||
_cachedFor = _willCacheFor;
|
_cachedFor = _willCacheFor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::forwardSelectedItems() {
|
|
||||||
_history->onForwardSelected();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWidget::confirmDeleteSelectedItems() {
|
|
||||||
_history->confirmDeleteSelectedItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWidget::clearSelectedItems() {
|
|
||||||
_history->onClearSelected();
|
|
||||||
}
|
|
||||||
|
|
||||||
Dialogs::IndexedList *MainWidget::contactsList() {
|
Dialogs::IndexedList *MainWidget::contactsList() {
|
||||||
return _dialogs->contactsList();
|
return _dialogs->contactsList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,6 @@ public:
|
||||||
|
|
||||||
void showForwardLayer(MessageIdsList &&items);
|
void showForwardLayer(MessageIdsList &&items);
|
||||||
void showSendPathsLayer();
|
void showSendPathsLayer();
|
||||||
void deleteLayer(MessageIdsList &&items);
|
|
||||||
void deleteLayer(FullMsgId itemId);
|
void deleteLayer(FullMsgId itemId);
|
||||||
void cancelUploadLayer(not_null<HistoryItem*> item);
|
void cancelUploadLayer(not_null<HistoryItem*> item);
|
||||||
void shareUrlLayer(const QString &url, const QString &text);
|
void shareUrlLayer(const QString &url, const QString &text);
|
||||||
|
@ -221,10 +220,6 @@ public:
|
||||||
|
|
||||||
bool sendMessageFail(const RPCError &error);
|
bool sendMessageFail(const RPCError &error);
|
||||||
|
|
||||||
void forwardSelectedItems();
|
|
||||||
void confirmDeleteSelectedItems();
|
|
||||||
void clearSelectedItems();
|
|
||||||
|
|
||||||
Dialogs::IndexedList *contactsList();
|
Dialogs::IndexedList *contactsList();
|
||||||
Dialogs::IndexedList *dialogsList();
|
Dialogs::IndexedList *dialogsList();
|
||||||
Dialogs::IndexedList *contactsNoDialogsList();
|
Dialogs::IndexedList *contactsNoDialogsList();
|
||||||
|
|
|
@ -1780,6 +1780,13 @@ void ParseMarkdown(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextWithEntities ParseEntities(const QString &text, int32 flags) {
|
||||||
|
const auto rich = ((flags & TextParseRichText) != 0);
|
||||||
|
auto result = TextWithEntities{ text, EntitiesInText() };
|
||||||
|
ParseEntities(result, flags, rich);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Some code is duplicated in flattextarea.cpp!
|
// Some code is duplicated in flattextarea.cpp!
|
||||||
void ParseEntities(TextWithEntities &result, int32 flags, bool rich) {
|
void ParseEntities(TextWithEntities &result, int32 flags, bool rich) {
|
||||||
if (flags & TextParseMarkdown) { // parse markdown entities (bold, italic, code and pre)
|
if (flags & TextParseMarkdown) { // parse markdown entities (bold, italic, code and pre)
|
||||||
|
|
|
@ -214,6 +214,7 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(const EntitiesInText &entities, Conver
|
||||||
|
|
||||||
// New entities are added to the ones that are already in result.
|
// New entities are added to the ones that are already in result.
|
||||||
// Changes text if (flags & TextParseMarkdown).
|
// Changes text if (flags & TextParseMarkdown).
|
||||||
|
TextWithEntities ParseEntities(const QString &text, int32 flags);
|
||||||
void ParseEntities(TextWithEntities &result, int32 flags, bool rich = false);
|
void ParseEntities(TextWithEntities &result, int32 flags, bool rich = false);
|
||||||
QString ApplyEntities(const TextWithEntities &text);
|
QString ApplyEntities(const TextWithEntities &text);
|
||||||
|
|
||||||
|
|
|
@ -349,7 +349,7 @@ void Filler::addChatActions(not_null<ChatData*> chat) {
|
||||||
|
|
||||||
void Filler::addChannelActions(not_null<ChannelData*> channel) {
|
void Filler::addChannelActions(not_null<ChannelData*> channel) {
|
||||||
auto isGroup = channel->isMegagroup();
|
auto isGroup = channel->isMegagroup();
|
||||||
if (false && !isGroup) {
|
if (!isGroup) {
|
||||||
const auto grouped = (channel->feed() != nullptr);
|
const auto grouped = (channel->feed() != nullptr);
|
||||||
_addAction(
|
_addAction(
|
||||||
lang(grouped ? lng_feed_ungroup : lng_feed_group),
|
lang(grouped ? lng_feed_ungroup : lng_feed_group),
|
||||||
|
|
|
@ -252,6 +252,8 @@
|
||||||
<(src_loc)/history/history_item.h
|
<(src_loc)/history/history_item.h
|
||||||
<(src_loc)/history/history_item_components.cpp
|
<(src_loc)/history/history_item_components.cpp
|
||||||
<(src_loc)/history/history_item_components.h
|
<(src_loc)/history/history_item_components.h
|
||||||
|
<(src_loc)/history/history_item_text.cpp
|
||||||
|
<(src_loc)/history/history_item_text.h
|
||||||
<(src_loc)/history/history_inner_widget.cpp
|
<(src_loc)/history/history_inner_widget.cpp
|
||||||
<(src_loc)/history/history_inner_widget.h
|
<(src_loc)/history/history_inner_widget.h
|
||||||
<(src_loc)/history/history_location_manager.cpp
|
<(src_loc)/history/history_location_manager.cpp
|
||||||
|
|
Loading…
Reference in New Issue