diff --git a/Telegram/Resources/default_shortcuts-custom.json b/Telegram/Resources/default_shortcuts-custom.json
new file mode 100644
index 000000000..42d412b15
--- /dev/null
+++ b/Telegram/Resources/default_shortcuts-custom.json
@@ -0,0 +1,14 @@
+// This is a list of your own shortcuts for Telegram Desktop
+// You can see full list of commands in the 'shortcuts-default.json' file
+// Place a null value instead of a command string to switch the shortcut off
+
+[
+ // {
+ // "command": "close_telegram",
+ // "keys": "ctrl+f4"
+ // },
+ // {
+ // "command": "quit_telegram",
+ // "keys": "ctrl+q"
+ // }
+]
diff --git a/Telegram/Resources/qrc/telegram.qrc b/Telegram/Resources/qrc/telegram.qrc
index 5d4433627..99f66c5cb 100644
--- a/Telegram/Resources/qrc/telegram.qrc
+++ b/Telegram/Resources/qrc/telegram.qrc
@@ -65,6 +65,9 @@
qmime/freedesktop.org.xml
+
+ ../default_shortcuts-custom.json
+
../langs/lang_it.strings
../langs/lang_es.strings
diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h
index 2fa792747..e2a02f4be 100644
--- a/Telegram/SourceFiles/config.h
+++ b/Telegram/SourceFiles/config.h
@@ -68,8 +68,6 @@ enum {
MaxZoomLevel = 7, // x8
ZoomToScreenLevel = 1024, // just constant
- ShortcutsCountLimit = 256, // how many shortcuts can be in json file
-
PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
SearchPeopleLimit = 5,
diff --git a/Telegram/SourceFiles/core/shortcuts.cpp b/Telegram/SourceFiles/core/shortcuts.cpp
new file mode 100644
index 000000000..01bf152db
--- /dev/null
+++ b/Telegram/SourceFiles/core/shortcuts.cpp
@@ -0,0 +1,451 @@
+/*
+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 "core/shortcuts.h"
+
+#include "mainwindow.h"
+#include "mainwidget.h"
+#include "messenger.h"
+#include "media/player/media_player_instance.h"
+#include "platform/platform_specific.h"
+#include "base/parse_helper.h"
+
+namespace Shortcuts {
+namespace {
+
+constexpr auto kCountLimit = 256; // How many shortcuts can be in json file.
+
+rpl::event_stream> RequestsStream;
+
+const auto AutoRepeatCommands = base::flat_set{
+ Command::MediaPrevious,
+ Command::MediaNext,
+ Command::ChatPrevious,
+ Command::ChatNext,
+};
+
+const auto MediaCommands = base::flat_set{
+ Command::MediaPlay,
+ Command::MediaPause,
+ Command::MediaPlayPause,
+ Command::MediaStop,
+ Command::MediaPrevious,
+ Command::MediaNext,
+};
+
+const auto CommandByName = base::flat_map{
+ { qsl("close_telegram") , Command::Close },
+ { qsl("lock_telegram") , Command::Lock },
+ { qsl("minimize_telegram"), Command::Minimize },
+ { qsl("quit_telegram") , Command::Quit },
+
+ { qsl("media_play") , Command::MediaPlay },
+ { qsl("media_pause") , Command::MediaPause },
+ { qsl("media_playpause") , Command::MediaPlayPause },
+ { qsl("media_stop") , Command::MediaStop },
+ { qsl("media_previous") , Command::MediaPrevious },
+ { qsl("media_next") , Command::MediaNext },
+
+ { qsl("search") , Command::Search },
+
+ { qsl("previous_chat") , Command::ChatPrevious },
+ { qsl("next_chat") , Command::ChatNext },
+};
+
+const auto CommandNames = base::flat_map{
+ { Command::Close , qsl("close_telegram") },
+ { Command::Lock , qsl("lock_telegram") },
+ { Command::Minimize , qsl("minimize_telegram") },
+ { Command::Quit , qsl("quit_telegram") },
+
+ { Command::MediaPlay , qsl("media_play") },
+ { Command::MediaPause , qsl("media_pause") },
+ { Command::MediaPlayPause, qsl("media_playpause") },
+ { Command::MediaStop , qsl("media_stop") },
+ { Command::MediaPrevious , qsl("media_previous") },
+ { Command::MediaNext , qsl("media_next") },
+
+ { Command::Search , qsl("search") },
+
+ { Command::ChatPrevious , qsl("previous_chat") },
+ { Command::ChatNext , qsl("next_chat") },
+};
+
+class Manager {
+public:
+ void fill();
+ void clear();
+
+ std::optional lookup(int shortcutId) const;
+ void toggleMedia(bool toggled);
+
+ const QStringList &errors() const;
+
+private:
+ void fillDefaults();
+ void writeDefaultFile();
+ bool readCustomFile();
+
+ void set(const QString &keys, Command command);
+ void remove(const QString &keys);
+ void unregister(base::unique_qptr shortcut);
+
+ QStringList _errors;
+
+ base::flat_map> _shortcuts;
+ base::flat_map _commandByShortcutId;
+
+ base::flat_set _mediaShortcuts;
+
+};
+
+QString DefaultFilePath() {
+ return cWorkingDir() + qsl("tdata/shortcuts-default.json");
+}
+
+QString CustomFilePath() {
+ return cWorkingDir() + qsl("tdata/shortcuts-custom.json");
+}
+
+bool DefaultFileIsValid() {
+ QFile file(DefaultFilePath());
+ if (!file.open(QIODevice::ReadOnly)) {
+ return false;
+ }
+ auto error = QJsonParseError{ 0, QJsonParseError::NoError };
+ const auto document = QJsonDocument::fromJson(
+ base::parse::stripComments(file.readAll()),
+ &error);
+ file.close();
+
+ if (error.error != QJsonParseError::NoError || !document.isArray()) {
+ return false;
+ }
+ const auto shortcuts = document.array();
+ if (shortcuts.isEmpty() || !(*shortcuts.constBegin()).isObject()) {
+ return false;
+ }
+ const auto versionObject = (*shortcuts.constBegin()).toObject();
+ const auto version = versionObject.constFind(qsl("version"));
+ if (version == versionObject.constEnd()
+ || !(*version).isString()
+ || (*version).toString() != QString::number(AppVersion)) {
+ return false;
+ }
+ return true;
+}
+
+void WriteDefaultCustomFile() {
+ const auto path = CustomFilePath();
+ auto input = QFile(":/misc/default_shortcuts-custom.json");
+ auto output = QFile(path);
+ if (input.open(QIODevice::ReadOnly) && output.open(QIODevice::WriteOnly)) {
+ output.write(input.readAll());
+ }
+}
+
+void Manager::fill() {
+ fillDefaults();
+
+ if (!DefaultFileIsValid()) {
+ writeDefaultFile();
+ }
+ if (!readCustomFile()) {
+ WriteDefaultCustomFile();
+ }
+}
+
+void Manager::clear() {
+ _errors.clear();
+ _shortcuts.clear();
+ _commandByShortcutId.clear();
+ _mediaShortcuts.clear();
+}
+
+const QStringList &Manager::errors() const {
+ return _errors;
+}
+
+std::optional Manager::lookup(int shortcutId) const {
+ const auto i = _commandByShortcutId.find(shortcutId);
+ return (i != end(_commandByShortcutId))
+ ? base::make_optional(i->second)
+ : std::nullopt;
+}
+
+void Manager::toggleMedia(bool toggled) {
+ for (const auto shortcut : _mediaShortcuts) {
+ shortcut->setEnabled(toggled);
+ }
+}
+
+bool Manager::readCustomFile() {
+ // read custom shortcuts from file if it exists or write an empty custom shortcuts file
+ QFile file(CustomFilePath());
+ if (!file.exists()) {
+ return false;
+ }
+ const auto guard = gsl::finally([&] {
+ if (!_errors.isEmpty()) {
+ _errors.push_front(qsl("While reading file '%1'..."
+ ).arg(file.fileName()));
+ }
+ });
+ if (!file.open(QIODevice::ReadOnly)) {
+ _errors.push_back(qsl("Could not read the file!"));
+ return true;
+ }
+ auto error = QJsonParseError{ 0, QJsonParseError::NoError };
+ const auto document = QJsonDocument::fromJson(
+ base::parse::stripComments(file.readAll()),
+ &error);
+ file.close();
+
+ if (error.error != QJsonParseError::NoError) {
+ _errors.push_back(qsl("Failed to parse! Error: %2"
+ ).arg(error.errorString()));
+ return true;
+ } else if (!document.isArray()) {
+ _errors.push_back(qsl("Failed to parse! Error: array expected"));
+ return true;
+ }
+ const auto shortcuts = document.array();
+ auto limit = kCountLimit;
+ for (auto i = shortcuts.constBegin(), e = shortcuts.constEnd(); i != e; ++i) {
+ if (!(*i).isObject()) {
+ _errors.push_back(qsl("Bad entry! Error: object expected"));
+ continue;
+ }
+ const auto entry = (*i).toObject();
+ const auto keys = entry.constFind(qsl("keys"));
+ const auto command = entry.constFind(qsl("command"));
+ if (keys == entry.constEnd()
+ || command == entry.constEnd()
+ || !(*keys).isString()
+ || (!(*command).isString() && !(*command).isNull())) {
+ _errors.push_back(qsl("Bad entry! "
+ "{\"keys\": \"...\", \"command\": [ \"...\" | null ]} "
+ "expected."));
+ } else if ((*command).isNull()) {
+ remove((*keys).toString());
+ } else {
+ const auto name = (*command).toString();
+ const auto i = CommandByName.find(name);
+ if (i != end(CommandByName)) {
+ set((*keys).toString(), i->second);
+ } else {
+ LOG(("Shortcut Warning: "
+ "could not find shortcut command handler '%1'"
+ ).arg(name));
+ }
+ }
+ if (!--limit) {
+ _errors.push_back(qsl("Too many entries! Limit is %1"
+ ).arg(kCountLimit));
+ break;
+ }
+ }
+ return true;
+}
+
+void Manager::fillDefaults() {
+ set(qsl("ctrl+w"), Command::Close);
+ set(qsl("ctrl+f4"), Command::Close);
+ set(qsl("ctrl+l"), Command::Lock);
+ set(qsl("ctrl+m"), Command::Minimize);
+ set(qsl("ctrl+q"), Command::Quit);
+
+ set(qsl("media play"), Command::MediaPlay);
+ set(qsl("media pause"), Command::MediaPause);
+ set(qsl("toggle media play/pause"), Command::MediaPlayPause);
+ set(qsl("media stop"), Command::MediaStop);
+ set(qsl("media previous"), Command::MediaPrevious);
+ set(qsl("media next"), Command::MediaNext);
+
+ set(qsl("ctrl+f"), Command::Search);
+ set(qsl("search"), Command::Search);
+ set(qsl("find"), Command::Search);
+
+ set(qsl("ctrl+pgdown"), Command::ChatNext);
+ set(qsl("alt+down"), Command::ChatNext);
+ set(qsl("ctrl+pgup"), Command::ChatPrevious);
+ set(qsl("alt+up"), Command::ChatPrevious);
+ if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
+ set(qsl("meta+tab"), Command::ChatNext);
+ set(qsl("meta+shift+tab"), Command::ChatPrevious);
+ set(qsl("meta+backtab"), Command::ChatPrevious);
+ } else {
+ set(qsl("ctrl+tab"), Command::ChatNext);
+ set(qsl("ctrl+shift+tab"), Command::ChatPrevious);
+ set(qsl("ctrl+backtab"), Command::ChatPrevious);
+ }
+}
+
+void Manager::writeDefaultFile() {
+ auto file = QFile(DefaultFilePath());
+ if (!file.open(QIODevice::WriteOnly)) {
+ return;
+ }
+ const char *defaultHeader = R"HEADER(
+// This is a list of default shortcuts for Telegram Desktop
+// Please don't modify it, its content is not used in any way
+// You can place your own shortcuts in the 'shortcuts-custom.json' file
+
+)HEADER";
+ file.write(defaultHeader);
+
+ auto shortcuts = QJsonArray();
+ auto version = QJsonObject();
+ version.insert(qsl("version"), QString::number(AppVersion));
+ shortcuts.push_back(version);
+
+ for (const auto &[sequence, shortcut] : _shortcuts) {
+ const auto i = _commandByShortcutId.find(shortcut->id());
+ if (i != end(_commandByShortcutId)) {
+ const auto j = CommandNames.find(i->second);
+ if (j != end(CommandNames)) {
+ QJsonObject entry;
+ entry.insert(qsl("keys"), sequence.toString().toLower());
+ entry.insert(qsl("command"), j->second);
+ shortcuts.append(entry);
+ }
+ }
+ }
+
+ auto document = QJsonDocument();
+ document.setArray(shortcuts);
+ file.write(document.toJson(QJsonDocument::Indented));
+}
+
+void Manager::set(const QString &keys, Command command) {
+ if (keys.isEmpty()) {
+ return;
+ }
+
+ const auto result = QKeySequence(keys, QKeySequence::PortableText);
+ if (result.isEmpty()) {
+ _errors.push_back(qsl("Could not derive key sequence '%1'!"
+ ).arg(keys));
+ return;
+ }
+ auto shortcut = base::make_unique_q(
+ result,
+ Messenger::Instance().getActiveWindow(),
+ nullptr,
+ nullptr,
+ Qt::ApplicationShortcut);
+ if (AutoRepeatCommands.contains(command)) {
+ shortcut->setAutoRepeat(false);
+ }
+ const auto isMediaShortcut = MediaCommands.contains(command);
+ if (isMediaShortcut) {
+ shortcut->setEnabled(false);
+ }
+ const auto id = shortcut->id();
+ if (!id) {
+ _errors.push_back(qsl("Could not create shortcut '%1'!").arg(keys));
+ return;
+ }
+ auto i = _shortcuts.find(result);
+ if (i == end(_shortcuts)) {
+ i = _shortcuts.emplace(result, std::move(shortcut)).first;
+ } else {
+ unregister(std::exchange(i->second, std::move(shortcut)));
+ }
+ _commandByShortcutId.emplace(id, command);
+ if (isMediaShortcut) {
+ _mediaShortcuts.emplace(i->second.get());
+ }
+}
+
+void Manager::remove(const QString &keys) {
+ if (keys.isEmpty()) {
+ return;
+ }
+
+ const auto result = QKeySequence(keys, QKeySequence::PortableText);
+ if (result.isEmpty()) {
+ _errors.push_back(qsl("Could not derive key sequence '%1'!"
+ ).arg(keys));
+ return;
+ }
+ const auto i = _shortcuts.find(result);
+ if (i != end(_shortcuts)) {
+ unregister(std::move(i->second));
+ _shortcuts.erase(i);
+ }
+}
+
+void Manager::unregister(base::unique_qptr shortcut) {
+ if (shortcut) {
+ _commandByShortcutId.erase(shortcut->id());
+ _mediaShortcuts.erase(shortcut.get());
+ }
+}
+
+Manager Data;
+
+} // namespace
+
+Request::Request(Command command) : _command(command) {
+}
+
+bool Request::check(Command command, int priority) {
+ if (_command == command && priority > _handlerPriority) {
+ _handlerPriority = priority;
+ return true;
+ }
+ return false;
+}
+
+bool Request::handle(FnMut handler) {
+ _handler = std::move(handler);
+ return true;
+}
+
+bool Launch(Command command) {
+ auto request = Request(command);
+ RequestsStream.fire(&request);
+ return request._handler ? request._handler() : false;
+}
+
+rpl::producer> Requests() {
+ return RequestsStream.events();
+}
+
+void Start() {
+ Assert(Global::started());
+
+ Data.fill();
+}
+
+const QStringList &Errors() {
+ return Data.errors();
+}
+
+bool HandleEvent(not_null event) {
+ if (const auto command = Data.lookup(event->shortcutId())) {
+ return Launch(*command);
+ }
+ return false;
+}
+
+void EnableMediaShortcuts() {
+ Data.toggleMedia(true);
+ Platform::SetWatchingMediaKeys(true);
+}
+
+void DisableMediaShortcuts() {
+ Data.toggleMedia(false);
+ Platform::SetWatchingMediaKeys(false);
+}
+
+void Finish() {
+ Data.clear();
+}
+
+} // namespace Shortcuts
diff --git a/Telegram/SourceFiles/core/shortcuts.h b/Telegram/SourceFiles/core/shortcuts.h
new file mode 100644
index 000000000..8f59c2e85
--- /dev/null
+++ b/Telegram/SourceFiles/core/shortcuts.h
@@ -0,0 +1,64 @@
+/*
+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
+
+namespace Shortcuts {
+
+enum class Command {
+ Close,
+ Lock,
+ Minimize,
+ Quit,
+
+ MediaPlay,
+ MediaPause,
+ MediaPlayPause,
+ MediaStop,
+ MediaPrevious,
+ MediaNext,
+
+ Search,
+
+ ChatPrevious,
+ ChatNext,
+};
+
+bool Launch(Command command);
+
+class Request {
+public:
+ bool check(Command command, int priority = 0);
+ bool handle(FnMut handler);
+
+private:
+ explicit Request(Command command);
+
+ Command _command;
+ int _handlerPriority = -1;
+ FnMut _handler;
+
+ friend bool Launch(Command command);
+
+};
+
+rpl::producer> Requests();
+
+void Start();
+void Finish();
+
+bool HandleEvent(not_null event);
+
+const QStringList &Errors();
+
+// Media shortcuts are not enabled by default, because other
+// applications also use them. They are enabled only when
+// the in-app player is active and disabled back after.
+void EnableMediaShortcuts();
+void DisableMediaShortcuts();
+
+} // namespace Shortcuts
diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp
index 8f2bb86be..cc9aa20f5 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp
@@ -10,9 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/admin_log/history_admin_log_inner.h"
#include "history/admin_log/history_admin_log_filter.h"
#include "profile/profile_back_button.h"
-#include "styles/style_history.h"
-#include "styles/style_window.h"
-#include "styles/style_info.h"
+#include "core/shortcuts.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/buttons.h"
@@ -25,6 +23,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "base/timer.h"
#include "lang/lang_keys.h"
+#include "styles/style_history.h"
+#include "styles/style_window.h"
+#include "styles/style_info.h"
namespace AdminLog {
@@ -262,6 +263,8 @@ Widget::Widget(QWidget *parent, not_null controller, not_nu
connect(_scroll, &Ui::ScrollArea::scrolled, this, [this] { onScroll(); });
_whatIsThis->setClickedCallback([=] { Ui::show(Box(lang(lng_admin_log_about_text))); });
+
+ setupShortcuts();
}
void Widget::showFilter() {
@@ -317,12 +320,17 @@ void Widget::setInternalState(const QRect &geometry, not_null m
restoreState(memento);
}
-bool Widget::cmd_search() {
- if (!inFocusChain()) {
- return false;
- }
- _fixedBar->showSearch();
- return true;
+void Widget::setupShortcuts() {
+ Shortcuts::Requests(
+ ) | rpl::start_with_next([=](not_null request) {
+ using Command = Shortcuts::Command;
+ if (isActiveWindow() && !Ui::isLayerShown() && inFocusChain()) {
+ request->check(Command::Search, 1) && request->handle([=] {
+ _fixedBar->showSearch();
+ return true;
+ });
+ }
+ }, lifetime());
}
std::unique_ptr Widget::createMemento() {
diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h
index abdb6c240..e066a61af 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h
@@ -87,8 +87,6 @@ public:
void applyFilter(FilterValue &&value);
- bool cmd_search() override;
-
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
@@ -104,6 +102,7 @@ private:
void updateAdaptiveLayout();
void saveState(not_null memento);
void restoreState(not_null memento);
+ void setupShortcuts();
object_ptr _scroll;
QPointer _inner;
diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.cpp b/Telegram/SourceFiles/history/feed/history_feed_section.cpp
index ee39ec781..b15ba1b7c 100644
--- a/Telegram/SourceFiles/history/feed/history_feed_section.cpp
+++ b/Telegram/SourceFiles/history/feed/history_feed_section.cpp
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_service.h"
#include "history/history_inner_widget.h"
#include "core/event_filter.h"
+#include "core/shortcuts.h"
#include "lang/lang_keys.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
@@ -134,6 +135,7 @@ Widget::Widget(
}, lifetime());
setupScrollDownButton();
+ setupShortcuts();
}
void Widget::setupScrollDownButton() {
@@ -295,13 +297,17 @@ void Widget::setInternalState(
restoreState(memento);
}
-bool Widget::cmd_search() {
- if (!inFocusChain()) {
- return false;
- }
-
- App::main()->searchInChat(_feed);
- return true;
+void Widget::setupShortcuts() {
+ Shortcuts::Requests(
+ ) | rpl::start_with_next([=](not_null request) {
+ using Command = Shortcuts::Command;
+ if (isActiveWindow() && !Ui::isLayerShown() && inFocusChain()) {
+ request->check(Command::Search, 1) && request->handle([=] {
+ App::main()->searchInChat(_feed);
+ return true;
+ });
+ }
+ }, lifetime());
}
HistoryView::Context Widget::listContext() {
diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.h b/Telegram/SourceFiles/history/feed/history_feed_section.h
index 07077dd36..cb0be4845 100644
--- a/Telegram/SourceFiles/history/feed/history_feed_section.h
+++ b/Telegram/SourceFiles/history/feed/history_feed_section.h
@@ -68,8 +68,6 @@ public:
bool wheelEventFromFloatPlayer(QEvent *e) override;
QRect rectForFloatPlayer() const override;
- bool cmd_search() override;
-
// HistoryView::ListDelegate interface.
HistoryView::Context listContext() override;
void listScrollTo(int top) override;
@@ -122,6 +120,8 @@ private:
void confirmDeleteSelected();
void clearSelected();
+ void setupShortcuts();
+
not_null _feed;
object_ptr _scroll;
QPointer _inner;
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 4bc312c44..d300a9197 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -72,6 +72,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "inline_bots/inline_results_widget.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "core/crash_reports.h"
+#include "core/shortcuts.h"
#include "support/support_common.h"
#include "support/support_autocomplete.h"
#include "dialogs/dialogs_key.h"
@@ -778,6 +779,7 @@ HistoryWidget::HistoryWidget(
}, lifetime());
orderWidgets();
+ setupShortcuts();
}
void HistoryWidget::supportInitAutocomplete() {
@@ -1582,17 +1584,30 @@ void HistoryWidget::notify_migrateUpdated(PeerData *peer) {
}
}
-bool HistoryWidget::cmd_search() {
- if (!inFocusChain() || !_history) return false;
-
- App::main()->searchInChat(_history);
- return true;
+void HistoryWidget::setupShortcuts() {
+ Shortcuts::Requests(
+ ) | rpl::start_with_next([=](not_null request) {
+ using Command = Shortcuts::Command;
+ if (isActiveWindow() && !Ui::isLayerShown() && _history) {
+ if (inFocusChain()) {
+ request->check(Command::Search) && request->handle([=] {
+ App::main()->searchInChat(_history);
+ return true;
+ });
+ }
+ request->check(Command::ChatPrevious) && request->handle([=] {
+ return showPreviousChat();
+ });
+ request->check(Command::ChatNext) && request->handle([=] {
+ return showNextChat();
+ });
+ }
+ }, lifetime());
}
-bool HistoryWidget::cmd_next_chat() {
- if (!_history) {
- return false;
- }
+bool HistoryWidget::showNextChat() {
+ Expects(_history != nullptr);
+
const auto next = App::main()->chatListEntryAfter(
Dialogs::RowDescriptor(
_history,
@@ -1611,10 +1626,9 @@ bool HistoryWidget::cmd_next_chat() {
return jumpToDialogRow(to);
}
-bool HistoryWidget::cmd_previous_chat() {
- if (!_history) {
- return false;
- }
+bool HistoryWidget::showPreviousChat() {
+ Expects(_history != nullptr);
+
const auto previous = App::main()->chatListEntryBefore(
Dialogs::RowDescriptor(
_history,
diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h
index 417ec9dcc..358c5eef2 100644
--- a/Telegram/SourceFiles/history/history_widget.h
+++ b/Telegram/SourceFiles/history/history_widget.h
@@ -354,10 +354,6 @@ public:
void notify_userIsBotChanged(UserData *user);
void notify_migrateUpdated(PeerData *peer);
- bool cmd_search();
- bool cmd_next_chat();
- bool cmd_previous_chat();
-
~HistoryWidget();
protected:
@@ -570,6 +566,10 @@ private:
}
bool jumpToDialogRow(const Dialogs::RowDescriptor &to);
+ void setupShortcuts();
+ bool showNextChat();
+ bool showPreviousChat();
+
MsgId _replyToId = 0;
Text _replyToName;
int _replyToNameVersion = 0;
diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
index a68ea6a46..0aeb8d402 100644
--- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
@@ -17,9 +17,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_shared_media.h"
#include "mainwidget.h"
#include "mainwindow.h"
-#include "shortcuts.h"
#include "auth_session.h"
#include "lang/lang_keys.h"
+#include "core/shortcuts.h"
#include "ui/special_buttons.h"
#include "ui/unread_badge.h"
#include "ui/widgets/buttons.h"
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index ee9b36b6e..95c316778 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -61,7 +61,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/download_path_box.h"
#include "boxes/connection_box.h"
#include "storage/localstorage.h"
-#include "shortcuts.h"
#include "media/media_audio.h"
#include "media/player/media_player_panel.h"
#include "media/player/media_player_widget.h"
@@ -79,6 +78,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/dc_options.h"
#include "core/file_utilities.h"
#include "core/update_checker.h"
+#include "core/shortcuts.h"
#include "calls/calls_instance.h"
#include "calls/calls_top_bar.h"
#include "export/export_settings.h"
@@ -621,24 +621,6 @@ void MainWidget::notify_historyMuteUpdated(History *history) {
_dialogs->notify_historyMuteUpdated(history);
}
-bool MainWidget::cmd_search() {
- if (Ui::isLayerShown() || !isActiveWindow()) return false;
- if (_mainSection) {
- return _mainSection->cmd_search();
- }
- return _history->cmd_search();
-}
-
-bool MainWidget::cmd_next_chat() {
- if (Ui::isLayerShown() || !isActiveWindow()) return false;
- return _history->cmd_next_chat();
-}
-
-bool MainWidget::cmd_previous_chat() {
- if (Ui::isLayerShown() || !isActiveWindow()) return false;
- return _history->cmd_previous_chat();
-}
-
void MainWidget::noHider(HistoryHider *destroyed) {
if (_hider == destroyed) {
_hider = nullptr;
@@ -1208,7 +1190,7 @@ void MainWidget::closeBothPlayers() {
Media::Player::instance()->stop(AudioMsgId::Type::Voice);
Media::Player::instance()->stop(AudioMsgId::Type::Song);
- Shortcuts::disableMediaShortcuts();
+ Shortcuts::DisableMediaShortcuts();
}
void MainWidget::createPlayer() {
@@ -1230,7 +1212,7 @@ void MainWidget::createPlayer() {
if (_a_show.animating()) {
_player->show(anim::type::instant);
_player->setVisible(false);
- Shortcuts::enableMediaShortcuts();
+ Shortcuts::EnableMediaShortcuts();
} else {
_player->hide(anim::type::instant);
}
@@ -1240,7 +1222,7 @@ void MainWidget::createPlayer() {
_player->show(anim::type::normal);
_playerHeight = _contentScrollAddToY = _player->contentHeight();
updateControlsGeometry();
- Shortcuts::enableMediaShortcuts();
+ Shortcuts::EnableMediaShortcuts();
}
}
}
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index 0a8f90acd..1bfc00940 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -314,10 +314,6 @@ public:
void notify_migrateUpdated(PeerData *peer);
void notify_historyMuteUpdated(History *history);
- bool cmd_search();
- bool cmd_next_chat();
- bool cmd_previous_chat();
-
~MainWidget();
signals:
diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp
index b1efaba00..c2336a1f2 100644
--- a/Telegram/SourceFiles/mainwindow.cpp
+++ b/Telegram/SourceFiles/mainwindow.cpp
@@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_cloud_manager.h"
#include "lang/lang_instance.h"
#include "lang/lang_keys.h"
-#include "shortcuts.h"
+#include "core/shortcuts.h"
#include "messenger.h"
#include "auth_session.h"
#include "application.h"
diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp
index 9b87a148b..bd28ae83d 100644
--- a/Telegram/SourceFiles/media/player/media_player_instance.cpp
+++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "data/data_media_types.h"
#include "window/window_controller.h"
+#include "core/shortcuts.h"
#include "messenger.h"
#include "mainwindow.h"
#include "auth_session.h"
@@ -75,6 +76,8 @@ Instance::Instance()
Messenger::Instance().authSessionChanged(),
handleAuthSessionChange);
handleAuthSessionChange();
+
+ setupShortcuts();
}
AudioMsgId::Type Instance::getActiveType() const {
@@ -494,5 +497,36 @@ void Instance::handleLogout() {
_usePanelPlayer.notify(false, true);
}
+void Instance::setupShortcuts() {
+ Shortcuts::Requests(
+ ) | rpl::start_with_next([=](not_null request) {
+ using Command = Shortcuts::Command;
+ request->check(Command::MediaPlay) && request->handle([=] {
+ play();
+ return true;
+ });
+ request->check(Command::MediaPause) && request->handle([=] {
+ pause();
+ return true;
+ });
+ request->check(Command::MediaPlayPause) && request->handle([=] {
+ playPause();
+ return true;
+ });
+ request->check(Command::MediaStop) && request->handle([=] {
+ stop();
+ return true;
+ });
+ request->check(Command::MediaPrevious) && request->handle([=] {
+ previous();
+ return true;
+ });
+ request->check(Command::MediaNext) && request->handle([=] {
+ next();
+ return true;
+ });
+ }, _lifetime);
+}
+
} // namespace Player
} // namespace Media
diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h
index e1aed71cc..c9f7eeace 100644
--- a/Telegram/SourceFiles/media/player/media_player_instance.h
+++ b/Telegram/SourceFiles/media/player/media_player_instance.h
@@ -127,6 +127,8 @@ private:
Instance();
friend void start();
+ void setupShortcuts();
+
using SharedMediaType = Storage::SharedMediaType;
using SliceKey = SparseIdsMergedSlice::Key;
struct Data {
@@ -202,6 +204,8 @@ private:
base::Observable _trackChangedNotifier;
base::Observable _repeatChangedNotifier;
+ rpl::lifetime _lifetime;
+
};
} // namespace Clip
diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp
index ba4ceea4b..458f22f39 100644
--- a/Telegram/SourceFiles/messenger.cpp
+++ b/Telegram/SourceFiles/messenger.cpp
@@ -12,13 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "base/timer.h"
#include "core/update_checker.h"
+#include "core/shortcuts.h"
#include "storage/localstorage.h"
#include "platform/platform_specific.h"
#include "mainwindow.h"
#include "dialogs/dialogs_entry.h"
#include "history/history.h"
#include "application.h"
-#include "shortcuts.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "calls/calls_instance.h"
@@ -143,8 +143,7 @@ Messenger::Messenger(not_null launcher)
DEBUG_LOG(("Application Info: window created..."));
- Shortcuts::start();
-
+ startShortcuts();
App::initMedia();
Local::ReadMapState state = Local::readMap(QByteArray());
@@ -172,11 +171,8 @@ Messenger::Messenger(not_null launcher)
_window->updateIsActive(Global::OnlineFocusTimeout());
- if (!Shortcuts::errors().isEmpty()) {
- const QStringList &errors(Shortcuts::errors());
- for (QStringList::const_iterator i = errors.cbegin(), e = errors.cend(); i != e; ++i) {
- LOG(("Shortcuts Error: %1").arg(*i));
- }
+ for (const auto &error : Shortcuts::Errors()) {
+ LOG(("Shortcuts Error: %1").arg(error));
}
}
@@ -252,8 +248,10 @@ bool Messenger::eventFilter(QObject *object, QEvent *e) {
} break;
case QEvent::Shortcut: {
- DEBUG_LOG(("Shortcut event caught: %1").arg(static_cast(e)->key().toString()));
- if (Shortcuts::launch(static_cast(e)->shortcutId())) {
+ const auto event = static_cast(e);
+ DEBUG_LOG(("Shortcut event caught: %1"
+ ).arg(event->key().toString()));
+ if (Shortcuts::HandleEvent(event)) {
return true;
}
} break;
@@ -1039,7 +1037,7 @@ Messenger::~Messenger() {
_mtproto.reset();
_mtprotoForKeysDestroy.reset();
- Shortcuts::finish();
+ Shortcuts::Finish();
Ui::Emoji::Clear();
@@ -1217,3 +1215,29 @@ void Messenger::quitDelayed() {
_private->quitTimer.callOnce(kQuitPreventTimeoutMs);
}
}
+
+void Messenger::startShortcuts() {
+ Shortcuts::Start();
+
+ Shortcuts::Requests(
+ ) | rpl::start_with_next([=](not_null request) {
+ using Command = Shortcuts::Command;
+ request->check(Command::Quit) && request->handle([] {
+ App::quit();
+ return true;
+ });
+ request->check(Command::Lock) && request->handle([=] {
+ if (!passcodeLocked() && Global::LocalPasscode()) {
+ lockByPasscode();
+ return true;
+ }
+ return false;
+ });
+ request->check(Command::Minimize) && request->handle([=] {
+ return minimizeActiveWindow();
+ });
+ request->check(Command::Close) && request->handle([=] {
+ return closeActiveWindow();
+ });
+ }, _lifetime);
+}
diff --git a/Telegram/SourceFiles/messenger.h b/Telegram/SourceFiles/messenger.h
index 2f8ddadf0..2e56e5a1c 100644
--- a/Telegram/SourceFiles/messenger.h
+++ b/Telegram/SourceFiles/messenger.h
@@ -220,6 +220,7 @@ public slots:
private:
void destroyMtpKeys(MTP::AuthKeysList &&keys);
void startLocalStorage();
+ void startShortcuts();
friend void App::quit();
static void QuitAttempt();
@@ -277,4 +278,6 @@ private:
};
std::vector _leaveSubscriptions;
+ rpl::lifetime _lifetime;
+
};
diff --git a/Telegram/SourceFiles/shortcuts.cpp b/Telegram/SourceFiles/shortcuts.cpp
deleted file mode 100644
index 34b62ea5d..000000000
--- a/Telegram/SourceFiles/shortcuts.cpp
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
-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 "shortcuts.h"
-
-#include "mainwindow.h"
-#include "mainwidget.h"
-#include "messenger.h"
-#include "media/player/media_player_instance.h"
-#include "platform/platform_specific.h"
-#include "base/parse_helper.h"
-
-namespace ShortcutCommands {
-
-using Handler = bool(*)();
-
-bool lock_telegram() {
- if (!Messenger::Instance().passcodeLocked()
- && Global::LocalPasscode()) {
- Messenger::Instance().lockByPasscode();
- return true;
- }
- return false;
-}
-
-bool minimize_telegram() {
- return Messenger::Instance().minimizeActiveWindow();
-}
-
-bool close_telegram() {
- return Messenger::Instance().closeActiveWindow();
-}
-
-bool quit_telegram() {
- App::quit();
- return true;
-}
-
-//void start_stop_recording() {
-
-//}
-
-//void cancel_recording() {
-
-//}
-
-bool media_play() {
- Media::Player::instance()->play();
- return true;
-}
-
-bool media_pause() {
- Media::Player::instance()->pause(AudioMsgId::Type::Song);
- return true;
-}
-
-bool media_playpause() {
- Media::Player::instance()->playPause();
- return true;
-}
-
-bool media_stop() {
- Media::Player::instance()->stop();
- return true;
-}
-
-bool media_previous() {
- Media::Player::instance()->previous();
- return true;
-}
-
-bool media_next() {
- Media::Player::instance()->next();
- return true;
-}
-
-bool search() {
- if (auto m = App::main()) {
- return m->cmd_search();
- }
- return false;
-}
-
-bool previous_chat() {
- if (auto m = App::main()) {
- return m->cmd_previous_chat();
- }
- return false;
-}
-
-bool next_chat() {
- if (auto m = App::main()) {
- return m->cmd_next_chat();
- }
- return false;
-}
-
-// other commands here
-
-} // namespace ShortcutCommands
-
-inline bool qMapLessThanKey(const ShortcutCommands::Handler &a, const ShortcutCommands::Handler &b) {
- return a < b;
-}
-
-namespace Shortcuts {
-
-struct DataStruct;
-DataStruct *DataPtr = nullptr;
-
-namespace {
-
-void createCommand(const QString &command, ShortcutCommands::Handler handler);
-QKeySequence setShortcut(const QString &keys, const QString &command);
-void destroyShortcut(QShortcut *shortcut);
-
-} // namespace
-
-struct DataStruct {
- DataStruct() {
- Assert(DataPtr == nullptr);
- DataPtr = this;
-
- if (autoRepeatCommands.isEmpty()) {
- autoRepeatCommands.insert(qsl("media_previous"));
- autoRepeatCommands.insert(qsl("media_next"));
- autoRepeatCommands.insert(qsl("next_chat"));
- autoRepeatCommands.insert(qsl("previous_chat"));
- }
-
- if (mediaCommands.isEmpty()) {
- mediaCommands.insert(qsl("media_play"));
- mediaCommands.insert(qsl("media_playpause"));
- mediaCommands.insert(qsl("media_play"));
- mediaCommands.insert(qsl("media_stop"));
- mediaCommands.insert(qsl("media_previous"));
- mediaCommands.insert(qsl("media_next"));
- }
-
-#define DeclareAlias(keys, command) setShortcut(qsl(keys), qsl(#command))
-#define DeclareCommand(keys, command) createCommand(qsl(#command), ShortcutCommands::command); DeclareAlias(keys, command)
-
- DeclareCommand("ctrl+w", close_telegram);
- DeclareAlias("ctrl+f4", close_telegram);
- DeclareCommand("ctrl+l", lock_telegram);
- DeclareCommand("ctrl+m", minimize_telegram);
- DeclareCommand("ctrl+q", quit_telegram);
-
- //DeclareCommand("ctrl+r", start_stop_recording);
- //DeclareCommand("ctrl+shift+r", cancel_recording);
- //DeclareCommand("media record", start_stop_recording);
-
- DeclareCommand("media play", media_play);
- DeclareCommand("media pause", media_pause);
- DeclareCommand("toggle media play/pause", media_playpause);
- DeclareCommand("media stop", media_stop);
- DeclareCommand("media previous", media_previous);
- DeclareCommand("media next", media_next);
-
- DeclareCommand("ctrl+f", search);
- DeclareAlias("search", search);
- DeclareAlias("find", search);
-
- DeclareCommand("ctrl+pgdown", next_chat);
- DeclareAlias("alt+down", next_chat);
- DeclareCommand("ctrl+pgup", previous_chat);
- DeclareAlias("alt+up", previous_chat);
- if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
- DeclareAlias("meta+tab", next_chat);
- DeclareAlias("meta+shift+tab", previous_chat);
- DeclareAlias("meta+backtab", previous_chat);
- } else {
- DeclareAlias("ctrl+tab", next_chat);
- DeclareAlias("ctrl+shift+tab", previous_chat);
- DeclareAlias("ctrl+backtab", previous_chat);
- }
-
- // other commands here
-
-#undef DeclareCommand
-#undef DeclareAlias
- }
- QStringList errors;
-
- QMap commands;
- QMap commandnames;
-
- QMap sequences;
- QMap handlers;
-
- QSet mediaShortcuts;
- QSet autoRepeatCommands;
- QSet mediaCommands;
-
-};
-
-namespace {
-
-void createCommand(const QString &command, ShortcutCommands::Handler handler) {
- Assert(DataPtr != nullptr);
- Assert(!command.isEmpty());
-
- DataPtr->commands.insert(command, handler);
- DataPtr->commandnames.insert(handler, command);
-}
-
-QKeySequence setShortcut(const QString &keys, const QString &command) {
- Assert(DataPtr != nullptr);
- Assert(!command.isEmpty());
- if (keys.isEmpty()) return QKeySequence();
-
- QKeySequence seq(keys, QKeySequence::PortableText);
- if (seq.isEmpty()) {
- DataPtr->errors.push_back(qsl("Could not derive key sequence '%1'!").arg(keys));
- } else {
- auto it = DataPtr->commands.constFind(command);
- if (it == DataPtr->commands.cend()) {
- LOG(("Warning: could not find shortcut command handler '%1'").arg(command));
- } else {
- auto shortcut = std::make_unique(seq, Messenger::Instance().getActiveWindow(), nullptr, nullptr, Qt::ApplicationShortcut);
- if (!DataPtr->autoRepeatCommands.contains(command)) {
- shortcut->setAutoRepeat(false);
- }
- auto isMediaShortcut = DataPtr->mediaCommands.contains(command);
- if (isMediaShortcut) {
- shortcut->setEnabled(false);
- }
- int shortcutId = shortcut->id();
- if (!shortcutId) {
- DataPtr->errors.push_back(qsl("Could not create shortcut '%1'!").arg(keys));
- } else {
- auto seqIt = DataPtr->sequences.find(seq);
- if (seqIt == DataPtr->sequences.cend()) {
- seqIt = DataPtr->sequences.insert(seq, shortcut.release());
- } else {
- auto oldShortcut = seqIt.value();
- seqIt.value() = shortcut.release();
- destroyShortcut(oldShortcut);
- }
- DataPtr->handlers.insert(shortcutId, it.value());
- if (isMediaShortcut) {
- DataPtr->mediaShortcuts.insert(seqIt.value());
- }
- }
- }
- }
- return seq;
-}
-
-QKeySequence removeShortcut(const QString &keys) {
- Assert(DataPtr != nullptr);
- if (keys.isEmpty()) return QKeySequence();
-
- QKeySequence seq(keys, QKeySequence::PortableText);
- if (seq.isEmpty()) {
- DataPtr->errors.push_back(qsl("Could not derive key sequence '%1'!").arg(keys));
- } else {
- auto seqIt = DataPtr->sequences.find(seq);
- if (seqIt != DataPtr->sequences.cend()) {
- auto shortcut = seqIt.value();
- DataPtr->sequences.erase(seqIt);
- destroyShortcut(shortcut);
- }
- }
- return seq;
-}
-
-void destroyShortcut(QShortcut *shortcut) {
- Assert(DataPtr != nullptr);
-
- DataPtr->handlers.remove(shortcut->id());
- DataPtr->mediaShortcuts.remove(shortcut);
- delete shortcut;
-}
-
-} // namespace
-
-void start() {
- Assert(Global::started());
-
- new DataStruct();
-
- // write default shortcuts to a file if they are not there already
- bool defaultValid = false;
- QFile defaultFile(cWorkingDir() + qsl("tdata/shortcuts-default.json"));
- if (defaultFile.open(QIODevice::ReadOnly)) {
- QJsonParseError error = { 0, QJsonParseError::NoError };
- QJsonDocument doc = QJsonDocument::fromJson(base::parse::stripComments(defaultFile.readAll()), &error);
- defaultFile.close();
-
- if (error.error == QJsonParseError::NoError && doc.isArray()) {
- QJsonArray shortcuts(doc.array());
- if (!shortcuts.isEmpty() && (*shortcuts.constBegin()).isObject()) {
- QJsonObject versionObject((*shortcuts.constBegin()).toObject());
- QJsonObject::const_iterator version = versionObject.constFind(qsl("version"));
- if (version != versionObject.constEnd() && (*version).isString() && (*version).toString() == QString::number(AppVersion)) {
- defaultValid = true;
- }
- }
- }
- }
- if (!defaultValid && defaultFile.open(QIODevice::WriteOnly)) {
- const char *defaultHeader = "\
-// This is a list of default shortcuts for Telegram Desktop\n\
-// Please don't modify it, its content is not used in any way\n\
-// You can place your own shortcuts in the 'shortcuts-custom.json' file\n\n";
- defaultFile.write(defaultHeader);
-
- QJsonArray shortcuts;
-
- QJsonObject version;
- version.insert(qsl("version"), QString::number(AppVersion));
- shortcuts.push_back(version);
-
- for (auto i = DataPtr->sequences.cbegin(), e = DataPtr->sequences.cend(); i != e; ++i) {
- auto h = DataPtr->handlers.constFind(i.value()->id());
- if (h != DataPtr->handlers.cend()) {
- auto n = DataPtr->commandnames.constFind(h.value());
- if (n != DataPtr->commandnames.cend()) {
- QJsonObject entry;
- entry.insert(qsl("keys"), i.key().toString().toLower());
- entry.insert(qsl("command"), n.value());
- shortcuts.append(entry);
- }
- }
- }
-
- QJsonDocument doc;
- doc.setArray(shortcuts);
- defaultFile.write(doc.toJson(QJsonDocument::Indented));
- defaultFile.close();
- }
-
- // read custom shortcuts from file if it exists or write an empty custom shortcuts file
- QFile customFile(cWorkingDir() + qsl("tdata/shortcuts-custom.json"));
- if (customFile.exists()) {
- if (customFile.open(QIODevice::ReadOnly)) {
- QJsonParseError error = { 0, QJsonParseError::NoError };
- QJsonDocument doc = QJsonDocument::fromJson(base::parse::stripComments(customFile.readAll()), &error);
- customFile.close();
-
- if (error.error != QJsonParseError::NoError) {
- DataPtr->errors.push_back(qsl("Failed to parse! Error: %2").arg(error.errorString()));
- } else if (!doc.isArray()) {
- DataPtr->errors.push_back(qsl("Failed to parse! Error: array expected"));
- } else {
- QJsonArray shortcuts = doc.array();
- int limit = ShortcutsCountLimit;
- for (QJsonArray::const_iterator i = shortcuts.constBegin(), e = shortcuts.constEnd(); i != e; ++i) {
- if (!(*i).isObject()) {
- DataPtr->errors.push_back(qsl("Bad entry! Error: object expected"));
- } else {
- QKeySequence seq;
- QJsonObject entry((*i).toObject());
- QJsonObject::const_iterator keys = entry.constFind(qsl("keys")), command = entry.constFind(qsl("command"));
- if (keys == entry.constEnd() || command == entry.constEnd() || !(*keys).isString() || (!(*command).isString() && !(*command).isNull())) {
- DataPtr->errors.push_back(qsl("Bad entry! {\"keys\": \"...\", \"command\": [ \"...\" | null ]} expected"));
- } else if ((*command).isNull()) {
- seq = removeShortcut((*keys).toString());
- } else {
- seq = setShortcut((*keys).toString(), (*command).toString());
- }
- if (!--limit) {
- DataPtr->errors.push_back(qsl("Too many entries! Limit is %1").arg(ShortcutsCountLimit));
- break;
- }
- }
- }
- }
- } else {
- DataPtr->errors.push_back(qsl("Could not read the file!"));
- }
- if (!DataPtr->errors.isEmpty()) {
- DataPtr->errors.push_front(qsl("While reading file '%1'...").arg(customFile.fileName()));
- }
- } else if (customFile.open(QIODevice::WriteOnly)) {
- const char *customContent = "\
-// This is a list of your own shortcuts for Telegram Desktop\n\
-// You can see full list of commands in the 'shortcuts-default.json' file\n\
-// Place a null value instead of a command string to switch the shortcut off\n\n\
-[\n\
- // {\n\
- // \"command\": \"close_telegram\",\n\
- // \"keys\": \"ctrl+f4\"\n\
- // },\n\
- // {\n\
- // \"command\": \"quit_telegram\",\n\
- // \"keys\": \"ctrl+q\"\n\
- // }\n\
-]\n";
- customFile.write(customContent);
- customFile.close();
- }
-}
-
-const QStringList &errors() {
- Assert(DataPtr != nullptr);
- return DataPtr->errors;
-}
-
-bool launch(int shortcutId) {
- Assert(DataPtr != nullptr);
-
- auto it = DataPtr->handlers.constFind(shortcutId);
- if (it == DataPtr->handlers.cend()) {
- return false;
- }
- return (*it.value())();
-}
-
-bool launch(const QString &command) {
- Assert(DataPtr != nullptr);
-
- auto it = DataPtr->commands.constFind(command);
- if (it == DataPtr->commands.cend()) {
- return false;
- }
- return (*it.value())();
-}
-
-void enableMediaShortcuts() {
- if (!DataPtr) return;
- for_const (auto shortcut, DataPtr->mediaShortcuts) {
- shortcut->setEnabled(true);
- }
- Platform::SetWatchingMediaKeys(true);
-}
-
-void disableMediaShortcuts() {
- if (!DataPtr) return;
- for_const (auto shortcut, DataPtr->mediaShortcuts) {
- shortcut->setEnabled(false);
- }
- Platform::SetWatchingMediaKeys(false);
-}
-
-void finish() {
- delete DataPtr;
- DataPtr = nullptr;
-}
-
-} // namespace Shortcuts
diff --git a/Telegram/SourceFiles/shortcuts.h b/Telegram/SourceFiles/shortcuts.h
deleted file mode 100644
index f53e59356..000000000
--- a/Telegram/SourceFiles/shortcuts.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-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
-
-namespace Shortcuts {
-
-void start();
-const QStringList &errors();
-
-bool launch(int shortcutId);
-bool launch(const QString &command);
-
-// Media shortcuts are not enabled by default, because other
-// applications also use them. They are enabled only when
-// the in-app player is active and disabled back after.
-void enableMediaShortcuts();
-void disableMediaShortcuts();
-
-void finish();
-
-} // namespace Shortcuts
diff --git a/Telegram/SourceFiles/support/support_common.cpp b/Telegram/SourceFiles/support/support_common.cpp
index a137b0ce4..2eac396d8 100644
--- a/Telegram/SourceFiles/support/support_common.cpp
+++ b/Telegram/SourceFiles/support/support_common.cpp
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "support/support_common.h"
-#include "shortcuts.h"
+#include "core/shortcuts.h"
namespace Support {
@@ -36,10 +36,10 @@ Qt::KeyboardModifiers SkipSwitchModifiers() {
void PerformSwitch(SwitchSettings value) {
switch (value) {
case SwitchSettings::Next:
- Shortcuts::launch("next_chat");
+ Shortcuts::Launch(Shortcuts::Command::ChatNext);
break;
case SwitchSettings::Previous:
- Shortcuts::launch("previous_chat");
+ Shortcuts::Launch(Shortcuts::Command::ChatPrevious);
break;
default:
break;
diff --git a/Telegram/SourceFiles/window/section_widget.h b/Telegram/SourceFiles/window/section_widget.h
index 0c269c8ce..72382fc2f 100644
--- a/Telegram/SourceFiles/window/section_widget.h
+++ b/Telegram/SourceFiles/window/section_widget.h
@@ -122,11 +122,6 @@ public:
return nullptr;
}
- // Global shortcut handler. For now that ugly :(
- virtual bool cmd_search() {
- return false;
- }
-
static void PaintBackground(QWidget *widget, QPaintEvent *event);
protected:
diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt
index 253c827af..6b7bdd279 100644
--- a/Telegram/gyp/telegram_sources.txt
+++ b/Telegram/gyp/telegram_sources.txt
@@ -122,6 +122,8 @@
<(src_loc)/core/media_active_cache.h
<(src_loc)/core/mime_type.cpp
<(src_loc)/core/mime_type.h
+<(src_loc)/core/shortcuts.cpp
+<(src_loc)/core/shortcuts.h
<(src_loc)/core/single_timer.cpp
<(src_loc)/core/single_timer.h
<(src_loc)/core/tl_help.h
@@ -766,8 +768,6 @@
<(src_loc)/qt_static_plugins.cpp
<(src_loc)/settings.cpp
<(src_loc)/settings.h
-<(src_loc)/shortcuts.cpp
-<(src_loc)/shortcuts.h
<(emoji_suggestions_loc)/emoji_suggestions.cpp
<(emoji_suggestions_loc)/emoji_suggestions.h