1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "chat_helpers/send_context_menu.h"
9
10 #include "api/api_common.h"
11 #include "base/event_filter.h"
12 #include "boxes/abstract_box.h"
13 #include "core/shortcuts.h"
14 #include "history/view/history_view_schedule_box.h"
15 #include "lang/lang_keys.h"
16 #include "ui/widgets/popup_menu.h"
17 #include "data/data_peer.h"
18 #include "main/main_session.h"
19 #include "apiwrap.h"
20
21 #include <QtWidgets/QApplication>
22
23 namespace SendMenu {
24
DefaultSilentCallback(Fn<void (Api::SendOptions)> send)25 Fn<void()> DefaultSilentCallback(Fn<void(Api::SendOptions)> send) {
26 return [=] { send({ .silent = true }); };
27 }
28
DefaultScheduleCallback(not_null<Ui::RpWidget * > parent,Type type,Fn<void (Api::SendOptions)> send)29 Fn<void()> DefaultScheduleCallback(
30 not_null<Ui::RpWidget*> parent,
31 Type type,
32 Fn<void(Api::SendOptions)> send) {
33 const auto weak = Ui::MakeWeak(parent);
34 return [=] {
35 Ui::show(
36 HistoryView::PrepareScheduleBox(
37 weak,
38 type,
39 [=](Api::SendOptions options) { send(options); }),
40 Ui::LayerOption::KeepOther);
41 };
42 }
43
FillSendMenu(not_null<Ui::PopupMenu * > menu,Type type,Fn<void ()> silent,Fn<void ()> schedule)44 FillMenuResult FillSendMenu(
45 not_null<Ui::PopupMenu*> menu,
46 Type type,
47 Fn<void()> silent,
48 Fn<void()> schedule) {
49 if (!silent && !schedule) {
50 return FillMenuResult::None;
51 }
52 const auto now = type;
53 if (now == Type::Disabled
54 || (!silent && now == Type::SilentOnly)) {
55 return FillMenuResult::None;
56 }
57
58 if (silent && now != Type::Reminder) {
59 menu->addAction(tr::lng_send_silent_message(tr::now), silent);
60 }
61 if (schedule && now != Type::SilentOnly) {
62 menu->addAction(
63 (now == Type::Reminder
64 ? tr::lng_reminder_message(tr::now)
65 : tr::lng_schedule_message(tr::now)),
66 schedule);
67 }
68 return FillMenuResult::Success;
69 }
70
SetupMenuAndShortcuts(not_null<Ui::RpWidget * > button,Fn<Type ()> type,Fn<void ()> silent,Fn<void ()> schedule)71 void SetupMenuAndShortcuts(
72 not_null<Ui::RpWidget*> button,
73 Fn<Type()> type,
74 Fn<void()> silent,
75 Fn<void()> schedule) {
76 if (!silent && !schedule) {
77 return;
78 }
79 const auto menu = std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
80 const auto showMenu = [=] {
81 *menu = base::make_unique_q<Ui::PopupMenu>(button);
82 const auto result = FillSendMenu(*menu, type(), silent, schedule);
83 const auto success = (result == FillMenuResult::Success);
84 if (success) {
85 (*menu)->popup(QCursor::pos());
86 }
87 return success;
88 };
89 base::install_event_filter(button, [=](not_null<QEvent*> e) {
90 if (e->type() == QEvent::ContextMenu && showMenu()) {
91 return base::EventFilterResult::Cancel;
92 }
93 return base::EventFilterResult::Continue;
94 });
95
96 Shortcuts::Requests(
97 ) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
98 using Command = Shortcuts::Command;
99
100 const auto now = type();
101 if (now == Type::Disabled
102 || (!silent && now == Type::SilentOnly)) {
103 return;
104 }
105 (silent
106 && (now != Type::Reminder)
107 && request->check(Command::SendSilentMessage)
108 && request->handle([=] {
109 silent();
110 return true;
111 }))
112 ||
113 (schedule
114 && (now != Type::SilentOnly)
115 && request->check(Command::ScheduleMessage)
116 && request->handle([=] {
117 schedule();
118 return true;
119 }))
120 ||
121 (request->check(Command::JustSendMessage) && request->handle([=] {
122 const auto post = [&](QEvent::Type type) {
123 QApplication::postEvent(
124 button,
125 new QMouseEvent(
126 type,
127 QPointF(0, 0),
128 Qt::LeftButton,
129 Qt::LeftButton,
130 Qt::NoModifier));
131 };
132 post(QEvent::MouseButtonPress);
133 post(QEvent::MouseButtonRelease);
134 return true;
135 }));
136 }, button->lifetime());
137 }
138
SetupUnreadMentionsMenu(not_null<Ui::RpWidget * > button,Fn<PeerData * ()> currentPeer)139 void SetupUnreadMentionsMenu(
140 not_null<Ui::RpWidget*> button,
141 Fn<PeerData*()> currentPeer) {
142 struct State {
143 base::unique_qptr<Ui::PopupMenu> menu;
144 base::flat_set<not_null<PeerData*>> sentForPeers;
145 };
146 const auto state = std::make_shared<State>();
147 const auto showMenu = [=] {
148 const auto peer = currentPeer();
149 if (!peer) {
150 return;
151 }
152 state->menu = base::make_unique_q<Ui::PopupMenu>(button);
153 const auto text = tr::lng_context_mark_read_mentions_all(tr::now);
154 state->menu->addAction(text, [=] {
155 if (!state->sentForPeers.emplace(peer).second) {
156 return;
157 }
158 peer->session().api().request(MTPmessages_ReadMentions(
159 peer->input
160 )).done([=](const MTPmessages_AffectedHistory &result) {
161 state->sentForPeers.remove(peer);
162 peer->session().api().applyAffectedHistory(peer, result);
163 }).fail([=](const MTP::Error &error) {
164 state->sentForPeers.remove(peer);
165 }).send();
166 });
167 state->menu->popup(QCursor::pos());
168 };
169
170 base::install_event_filter(button, [=](not_null<QEvent*> e) {
171 if (e->type() == QEvent::ContextMenu) {
172 showMenu();
173 return base::EventFilterResult::Cancel;
174 }
175 return base::EventFilterResult::Continue;
176 });
177
178 }
179
180 } // namespace SendMenu
181