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