diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index d3d478f0b..bc0e86d6b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -100,7 +100,7 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window) App::wnd()->getTitle()->updateBackButton(); _topBar->hide(); - _player->hide(); + _player->hidePlayer(); orderWidgets(); @@ -1558,12 +1558,14 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { if (playing == songId) { _player->updateState(playing, playingState, playingPosition, playingDuration, playingFrequency); - if (!(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing && !_a_show.animating()) { - if (_player->isHidden()) { - _player->clearSelection(); - _player->show(); - _playerHeight = _contentScrollAddToY = _player->height(); - resizeEvent(0); + if (!(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { + if (!_player->isOpened()) { + _player->openPlayer(); + if (_player->isHidden() && !_a_show.animating()) { + _player->showPlayer(); + _playerHeight = _contentScrollAddToY = _player->height(); + resizeEvent(0); + } } } } @@ -1578,12 +1580,15 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { } } -void MainWidget::hidePlayer() { - if (!_player->isHidden()) { - _player->hide(); - _contentScrollAddToY = -_player->height(); - _playerHeight = 0; - resizeEvent(0); +void MainWidget::closePlayer() { + if (_player->isOpened()) { + _player->closePlayer(); + if (!_player->isHidden() && !_a_show.animating()) { + _player->hidePlayer(); + _contentScrollAddToY = -_player->height(); + _playerHeight = 0; + resizeEvent(0); + } } } @@ -2477,8 +2482,10 @@ void MainWidget::hideAll() { } _topBar->hide(); _mediaType->hide(); - _player->hide(); - _playerHeight = 0; + if (_player->isOpened() && !_player->isHidden()) { + _player->hidePlayer(); + _playerHeight = 0; + } } void MainWidget::showAll() { @@ -2538,20 +2545,9 @@ void MainWidget::showAll() { _topBar->show(); } } - if (audioPlayer()) { - SongMsgId playing; - AudioPlayerState playingState = AudioPlayerStopped; - int64 playingPosition = 0, playingDuration = 0; - int32 playingFrequency = 0; - audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency); - if (playing) { - _player->updateState(playing, playingState, playingPosition, playingDuration, playingFrequency); - if (!(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { - _player->clearSelection(); - _player->show(); - _playerHeight = _player->height(); - } - } + if (_player->isOpened() && _player->isHidden()) { + _player->showPlayer(); + _playerHeight = _player->height(); } resizeEvent(0); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index dafb651b8..d080355c0 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -372,6 +372,8 @@ public: bool isItemVisible(HistoryItem *item); + void closePlayer(); + void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col); void ui_repaintHistoryItem(const HistoryItem *item); @@ -422,7 +424,6 @@ public slots: void documentPlayProgress(const SongMsgId &songId); void inlineResultLoadProgress(FileLoader *loader); void inlineResultLoadFailed(FileLoader *loader, bool started); - void hidePlayer(); void dialogsCancelled(); diff --git a/Telegram/SourceFiles/playerwidget.cpp b/Telegram/SourceFiles/playerwidget.cpp index 454763a2e..876ecd4a6 100644 --- a/Telegram/SourceFiles/playerwidget.cpp +++ b/Telegram/SourceFiles/playerwidget.cpp @@ -19,41 +19,21 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +#include "playerwidget.h" + +#include "shortcuts.h" #include "ui/style.h" #include "lang.h" - #include "boxes/addcontactbox.h" #include "application.h" #include "mainwindow.h" #include "playerwidget.h" #include "mainwidget.h" - #include "localstorage.h" - #include "audio.h" PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent) -, _prevAvailable(false) -, _nextAvailable(false) -, _fullAvailable(false) -, _over(OverNone) -, _down(OverNone) -, _downCoord(0) -, _downFrequency(AudioVoiceMsgFrequency) -, _downProgress(0.) , _a_state(animation(this, &PlayerWidget::step_state)) -, _msgmigrated(false) -, _index(-1) -, _migrated(0) -, _history(0) -, _timeWidth(0) -, _repeat(false) -, _showPause(false) -, _position(0) -, _duration(0) -, _loaded(0) -, a_progress(0., 0.) -, a_loadProgress(0., 0.) , _a_progress(animation(this, &PlayerWidget::step_progress)) , _sideShadow(this, st::shadowColor) { resize(st::wndMinWidth, st::playerHeight); @@ -372,6 +352,29 @@ bool PlayerWidget::seekingSong(const SongMsgId &song) const { return (_down == OverPlayback) && (song == _song); } +void PlayerWidget::openPlayer() { + _playerOpened = true; + Shortcuts::enableMediaShortcuts(); +} + +bool PlayerWidget::isOpened() const { + return _playerOpened; +} + +void PlayerWidget::closePlayer() { + _playerOpened = false; + Shortcuts::disableMediaShortcuts(); +} + +void PlayerWidget::showPlayer() { + TWidget::show(); +} + +void PlayerWidget::hidePlayer() { + clearSelection(); + TWidget::hide(); +} + void PlayerWidget::step_state(uint64 ms, bool timer) { for (StateAnimations::iterator i = _stateAnimations.begin(); i != _stateAnimations.cend();) { int32 over = qAbs(i.key()); @@ -463,7 +466,7 @@ void PlayerWidget::mouseReleaseEvent(QMouseEvent *e) { } update(); } else if (_down == OverClose && _over == OverClose) { - stopPressed(); + closePressed(); } _down = OverNone; } @@ -545,7 +548,11 @@ void PlayerWidget::stopPressed() { if (!_song || isHidden()) return; audioPlayer()->stop(OverviewFiles); - if (App::main()) App::main()->hidePlayer(); +} + +void PlayerWidget::closePressed() { + stopPressed(); + if (App::main()) App::main()->closePlayer(); } void PlayerWidget::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/playerwidget.h b/Telegram/SourceFiles/playerwidget.h index 3c5cffbd3..c5286fca5 100644 --- a/Telegram/SourceFiles/playerwidget.h +++ b/Telegram/SourceFiles/playerwidget.h @@ -42,6 +42,7 @@ public: void prevPressed(); void nextPressed(); void stopPressed(); + void closePressed(); void step_progress(float64 ms, bool timer); void step_state(uint64 ms, bool timer); @@ -55,12 +56,23 @@ public: bool seekingSong(const SongMsgId &song) const; + void openPlayer(); + bool isOpened() const; + void closePlayer(); + + void showPlayer(); + void hidePlayer(); + signals: void playerSongChanged(const FullMsgId &msgId); private: + // Use startPlayer()/stopPlayer() or showPlayer()/hidePlayer() instead. + void show(); + void hide(); + enum OverState { OverNone = 0, OverPrev, @@ -87,12 +99,17 @@ private: QPoint _lastMousePos; void updateSelected(); - bool _prevAvailable, _nextAvailable, _fullAvailable; - OverState _over, _down; - int32 _downCoord; + bool _playerOpened = false; + + bool _prevAvailable = false; + bool _nextAvailable = false; + bool _fullAvailable = false; + OverState _over = OverNone; + OverState _down = OverNone; + int32 _downCoord = 0; int64 _downDuration; - int32 _downFrequency; - float64 _downProgress; + int32 _downFrequency = AudioVoiceMsgFrequency; + float64 _downProgress = 0.; float64 _stateHovers[OverStateCount]; typedef QMap StateAnimations; @@ -100,20 +117,23 @@ private: Animation _a_state; SongMsgId _song; - bool _msgmigrated; - int32 _index; - History *_migrated, *_history; + bool _msgmigrated = false; + int32 _index = -1; + History *_migrated = nullptr; + History *_history = nullptr; QRect _playRect, _prevRect, _nextRect, _playbackRect; QRect _closeRect, _volumeRect, _fullRect, _repeatRect, _infoRect; - int32 _timeWidth; - bool _repeat; + int32 _timeWidth = 0; + bool _repeat = false; QString _time; Text _name; - bool _showPause; - int64 _position, _duration; - int32 _loaded; + bool _showPause = false; + int64 _position = 0; + int64 _duration = 0; + int32 _loaded = 0; - anim::fvalue a_progress, a_loadProgress; + anim::fvalue a_progress = { 0., 0. }; + anim::fvalue a_loadProgress = { 0., 0. }; Animation _a_progress; PlainShadow _sideShadow; diff --git a/Telegram/SourceFiles/shortcuts.cpp b/Telegram/SourceFiles/shortcuts.cpp index 2e5599ac5..c87e79f86 100644 --- a/Telegram/SourceFiles/shortcuts.cpp +++ b/Telegram/SourceFiles/shortcuts.cpp @@ -27,413 +27,450 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "playerwidget.h" namespace ShortcutCommands { - typedef void(*Handler)(); - void lock_telegram() { - if (auto w = App::wnd()) { - if (App::passcoded()) { - w->passcodeWidget()->onSubmit(); - } else if (cHasPasscode()) { - w->setupPasscode(true); - } +typedef void(*Handler)(); + +void lock_telegram() { + if (auto w = App::wnd()) { + if (App::passcoded()) { + w->passcodeWidget()->onSubmit(); + } else if (cHasPasscode()) { + w->setupPasscode(true); } } - - void minimize_telegram() { - if (auto w = App::wnd()) { - if (cWorkMode() == dbiwmTrayOnly) { - w->minimizeToTray(); - } else { - w->setWindowState(Qt::WindowMinimized); - } - } - } - - void close_telegram() { - if (!Ui::hideWindowNoQuit()) { - if (auto w = App::wnd()) { - w->close(); - } - } - } - - void quit_telegram() { - App::quit(); - } - - //void start_stop_recording() { - - //} - - //void cancel_recording() { - - //} - - void media_play() { - if (MainWidget *m = App::main()) { - m->player()->playPressed(); - } - } - - void media_pause() { - if (MainWidget *m = App::main()) { - m->player()->pausePressed(); - } - } - - void media_playpause() { - if (MainWidget *m = App::main()) { - m->player()->playPausePressed(); - } - } - - void media_stop() { - if (MainWidget *m = App::main()) { - m->player()->stopPressed(); - } - } - - void media_previous() { - if (MainWidget *m = App::main()) { - m->player()->prevPressed(); - } - } - - void media_next() { - if (MainWidget *m = App::main()) { - m->player()->nextPressed(); - } - } - - void search() { - if (MainWidget *m = App::main()) { - m->cmd_search(); - } - } - - void previous_chat() { - if (MainWidget *m = App::main()) { - m->cmd_previous_chat(); - } - } - - void next_chat() { - if (MainWidget *m = App::main()) { - m->cmd_next_chat(); - } - } - - // other commands here - } +void minimize_telegram() { + if (auto w = App::wnd()) { + if (cWorkMode() == dbiwmTrayOnly) { + w->minimizeToTray(); + } else { + w->setWindowState(Qt::WindowMinimized); + } + } +} + +void close_telegram() { + if (!Ui::hideWindowNoQuit()) { + if (auto w = App::wnd()) { + w->close(); + } + } +} + +void quit_telegram() { + App::quit(); +} + +//void start_stop_recording() { + +//} + +//void cancel_recording() { + +//} + +void media_play() { + if (MainWidget *m = App::main()) { + m->player()->playPressed(); + } +} + +void media_pause() { + if (MainWidget *m = App::main()) { + m->player()->pausePressed(); + } +} + +void media_playpause() { + if (MainWidget *m = App::main()) { + m->player()->playPausePressed(); + } +} + +void media_stop() { + if (MainWidget *m = App::main()) { + m->player()->stopPressed(); + } +} + +void media_previous() { + if (MainWidget *m = App::main()) { + m->player()->prevPressed(); + } +} + +void media_next() { + if (MainWidget *m = App::main()) { + m->player()->nextPressed(); + } +} + +void search() { + if (MainWidget *m = App::main()) { + m->cmd_search(); + } +} + +void previous_chat() { + if (MainWidget *m = App::main()) { + m->cmd_previous_chat(); + } +} + +void next_chat() { + if (MainWidget *m = App::main()) { + m->cmd_next_chat(); + } +} + +// other commands here + +} // namespace ShortcutCommands + inline bool qMapLessThanKey(const ShortcutCommands::Handler &a, const ShortcutCommands::Handler &b) { return a < b; } namespace Shortcuts { - // inspired by https://github.com/sindresorhus/strip-json-comments - QByteArray _stripJsonComments(const QByteArray &json) { - enum InsideComment { - InsideCommentNone, - InsideCommentSingleLine, - InsideCommentMultiLine, - }; - InsideComment insideComment = InsideCommentNone; - bool insideString = false; +// inspired by https://github.com/sindresorhus/strip-json-comments +QByteArray _stripJsonComments(const QByteArray &json) { + enum InsideComment { + InsideCommentNone, + InsideCommentSingleLine, + InsideCommentMultiLine, + }; + InsideComment insideComment = InsideCommentNone; + bool insideString = false; - QByteArray result; + QByteArray result; - const char *b = json.cbegin(), *e = json.cend(), *offset = b; - for (const char *ch = offset; ch != e; ++ch) { - char currentChar = *ch; - char nextChar = (ch + 1 == e) ? 0 : *(ch + 1); + const char *b = json.cbegin(), *e = json.cend(), *offset = b; + for (const char *ch = offset; ch != e; ++ch) { + char currentChar = *ch; + char nextChar = (ch + 1 == e) ? 0 : *(ch + 1); - if (insideComment == InsideCommentNone && currentChar == '"') { - bool escaped = ((ch > b) && *(ch - 1) == '\\') && ((ch - 1 < b) || *(ch - 2) != '\\'); - if (!escaped) { - insideString = !insideString; - } - } - - if (insideString) { - continue; - } - - if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '/') { - if (ch > offset) { - if (result.isEmpty()) result.reserve(json.size() - 2); - result.append(offset, ch - offset); - offset = ch; - } - insideComment = InsideCommentSingleLine; - ++ch; - } else if (insideComment == InsideCommentSingleLine && currentChar == '\r' && nextChar == '\n') { - if (ch > offset) { - offset = ch; - } - ++ch; - insideComment = InsideCommentNone; - } else if (insideComment == InsideCommentSingleLine && currentChar == '\n') { - if (ch > offset) { - offset = ch; - } - insideComment = InsideCommentNone; - } else if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '*') { - if (ch > offset) { - if (result.isEmpty()) result.reserve(json.size() - 2); - result.append(offset, ch - offset); - offset = ch; - } - insideComment = InsideCommentMultiLine; - ++ch; - } else if (insideComment == InsideCommentMultiLine && currentChar == '*' && nextChar == '/') { - if (ch > offset) { - offset = ch; - } - ++ch; - insideComment = InsideCommentNone; + if (insideComment == InsideCommentNone && currentChar == '"') { + bool escaped = ((ch > b) && *(ch - 1) == '\\') && ((ch - 1 < b) || *(ch - 2) != '\\'); + if (!escaped) { + insideString = !insideString; } } - if (insideComment == InsideCommentNone && e > offset && !result.isEmpty()) { - result.append(offset, e - offset); + if (insideString) { + continue; + } + + if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '/') { + if (ch > offset) { + if (result.isEmpty()) result.reserve(json.size() - 2); + result.append(offset, ch - offset); + offset = ch; + } + insideComment = InsideCommentSingleLine; + ++ch; + } else if (insideComment == InsideCommentSingleLine && currentChar == '\r' && nextChar == '\n') { + if (ch > offset) { + offset = ch; + } + ++ch; + insideComment = InsideCommentNone; + } else if (insideComment == InsideCommentSingleLine && currentChar == '\n') { + if (ch > offset) { + offset = ch; + } + insideComment = InsideCommentNone; + } else if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '*') { + if (ch > offset) { + if (result.isEmpty()) result.reserve(json.size() - 2); + result.append(offset, ch - offset); + offset = ch; + } + insideComment = InsideCommentMultiLine; + ++ch; + } else if (insideComment == InsideCommentMultiLine && currentChar == '*' && nextChar == '/') { + if (ch > offset) { + offset = ch; + } + ++ch; + insideComment = InsideCommentNone; } - return result.isEmpty() ? json : result; } - struct DataStruct; - DataStruct *DataPtr = nullptr; + if (insideComment == InsideCommentNone && e > offset && !result.isEmpty()) { + result.append(offset, e - offset); + } + return result.isEmpty() ? json : result; +} - void _createCommand(const QString &command, ShortcutCommands::Handler handler); - QKeySequence _setShortcut(const QString &keys, const QString &command); - struct DataStruct { - DataStruct() { - t_assert(DataPtr == nullptr); - DataPtr = this; +struct DataStruct; +DataStruct *DataPtr = nullptr; -#define DeclareAlias(keys, command) _setShortcut(qsl(keys), qsl(#command)) -#define DeclareCommand(keys, command) _createCommand(qsl(#command), ShortcutCommands::command); DeclareAlias(keys, command) +namespace { - 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); +void createCommand(const QString &command, ShortcutCommands::Handler handler); +QKeySequence setShortcut(const QString &keys, const QString &command); +void destroyShortcut(QShortcut *shortcut); - //DeclareCommand("ctrl+r", start_stop_recording); - //DeclareCommand("ctrl+shift+r", cancel_recording); - //DeclareCommand("media record", start_stop_recording); +} // namespace - 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); +struct DataStruct { + DataStruct() { + t_assert(DataPtr == nullptr); + DataPtr = this; - DeclareCommand("ctrl+f", search); - DeclareAlias("search", search); - DeclareAlias("find", search); +#define DeclareAlias(keys, command) setShortcut(qsl(keys), qsl(#command)) +#define DeclareCommand(keys, command) createCommand(qsl(#command), ShortcutCommands::command); DeclareAlias(keys, command) - 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); - } + 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); - // other commands here + //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; + } + QStringList errors; - QMap commands; - QMap commandnames; + QMap commands; + QMap commandnames; - QMap sequences; - QMap handlers; + QMap sequences; + QMap handlers; - QSet autoRepeatCommands = { - qsl("media_previous"), - qsl("media_next"), - qsl("next_chat"), - qsl("previous_chat"), - }; + QSet mediaShortcuts; + + QSet autoRepeatCommands = { + qsl("media_previous"), + qsl("media_next"), + qsl("next_chat"), + qsl("previous_chat"), }; - void _createCommand(const QString &command, ShortcutCommands::Handler handler) { - t_assert(DataPtr != nullptr); - t_assert(!command.isEmpty()); + QSet mediaCommands = { + qsl("media_play"), + qsl("media_pause"), + qsl("media_playpause"), + qsl("media_stop"), + qsl("media_previous"), + qsl("media_next") + }; +}; - DataPtr->commands.insert(command, handler); - DataPtr->commandnames.insert(handler, command); - } +namespace { - QKeySequence _setShortcut(const QString &keys, const QString &command) { - t_assert(DataPtr != nullptr); - t_assert(!command.isEmpty()); - if (keys.isEmpty()) return QKeySequence(); +void createCommand(const QString &command, ShortcutCommands::Handler handler) { + t_assert(DataPtr != nullptr); + t_assert(!command.isEmpty()); - QKeySequence seq(keys, QKeySequence::PortableText); - if (seq.isEmpty()) { - DataPtr->errors.push_back(qsl("Could not derive key sequence '%1'!").arg(keys)); + DataPtr->commands.insert(command, handler); + DataPtr->commandnames.insert(handler, command); +} + +QKeySequence setShortcut(const QString &keys, const QString &command) { + t_assert(DataPtr != nullptr); + t_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 it = DataPtr->commands.constFind(command); - if (it == DataPtr->commands.cend()) { - LOG(("Warning: could not find shortcut command handler '%1'").arg(command)); + auto shortcut = std_::make_unique(seq, App::wnd(), 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 { - QShortcut *shortcut(new QShortcut(seq, App::wnd(), nullptr, nullptr, Qt::ApplicationShortcut)); - if (!DataPtr->autoRepeatCommands.contains(command)) { - shortcut->setAutoRepeat(false); - } - int shortcutId = shortcut->id(); - if (!shortcutId) { - DataPtr->errors.push_back(qsl("Could not create shortcut '%1'!").arg(keys)); + auto seqIt = DataPtr->sequences.find(seq); + if (seqIt == DataPtr->sequences.cend()) { + seqIt = DataPtr->sequences.insert(seq, shortcut.release()); } else { - QMap::iterator seqIt = DataPtr->sequences.find(seq); - if (seqIt == DataPtr->sequences.cend()) { - seqIt = DataPtr->sequences.insert(seq, shortcut); - } else { - DataPtr->handlers.remove(seqIt.value()->id()); - delete seqIt.value(); - seqIt.value() = shortcut; - } - DataPtr->handlers.insert(shortcutId, it.value()); + 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; } + return seq; +} - QKeySequence _removeShortcut(const QString &keys) { - t_assert(DataPtr != nullptr); - if (keys.isEmpty()) return QKeySequence(); +QKeySequence removeShortcut(const QString &keys) { + t_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 { - QMap::iterator seqIt = DataPtr->sequences.find(seq); - if (seqIt != DataPtr->sequences.cend()) { - DataPtr->handlers.remove(seqIt.value()->id()); - delete seqIt.value(); - DataPtr->sequences.erase(seqIt); - } + 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; } + return seq; +} - void start() { - t_assert(Global::started()); +void destroyShortcut(QShortcut *shortcut) { + t_assert(DataPtr != nullptr); - new DataStruct(); + DataPtr->handlers.remove(shortcut->id()); + DataPtr->mediaShortcuts.remove(shortcut); + delete shortcut; +} - // 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(_stripJsonComments(defaultFile.readAll()), &error); - defaultFile.close(); +} // namespace - 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; - } +void start() { + t_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(_stripJsonComments(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 = "\ + } + 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); + defaultFile.write(defaultHeader); - QJsonArray shortcuts; + QJsonArray shortcuts; - QJsonObject version; - version.insert(qsl("version"), QString::number(AppVersion)); - shortcuts.push_back(version); + QJsonObject version; + version.insert(qsl("version"), QString::number(AppVersion)); + shortcuts.push_back(version); - for (QMap::const_iterator i = DataPtr->sequences.cbegin(), e = DataPtr->sequences.cend(); i != e; ++i) { - QMap::const_iterator h = DataPtr->handlers.constFind(i.value()->id()); - if (h != DataPtr->handlers.cend()) { - QMap::const_iterator 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); - } + 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(_stripJsonComments(customFile.readAll()), &error); - customFile.close(); + QJsonDocument doc; + doc.setArray(shortcuts); + defaultFile.write(doc.toJson(QJsonDocument::Indented)); + defaultFile.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")); + // 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(_stripJsonComments(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 { - 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; - } + 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 = "\ + } 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\ @@ -447,41 +484,55 @@ namespace Shortcuts { // \"keys\": \"ctrl+q\"\n\ // }\n\ ]\n"; - customFile.write(customContent); - customFile.close(); - } + customFile.write(customContent); + customFile.close(); } - - const QStringList &errors() { - t_assert(DataPtr != nullptr); - return DataPtr->errors; - } - - bool launch(int shortcutId) { - t_assert(DataPtr != nullptr); - - QMap::const_iterator it = DataPtr->handlers.constFind(shortcutId); - if (it == DataPtr->handlers.cend()) { - return false; - } - (*it.value())(); - return true; - } - - bool launch(const QString &command) { - t_assert(DataPtr != nullptr); - - QMap::const_iterator it = DataPtr->commands.constFind(command); - if (it == DataPtr->commands.cend()) { - return false; - } - (*it.value())(); - return true; - } - - void finish() { - delete DataPtr; - DataPtr = nullptr; - } - } + +const QStringList &errors() { + t_assert(DataPtr != nullptr); + return DataPtr->errors; +} + +bool launch(int shortcutId) { + t_assert(DataPtr != nullptr); + + auto it = DataPtr->handlers.constFind(shortcutId); + if (it == DataPtr->handlers.cend()) { + return false; + } + (*it.value())(); + return true; +} + +bool launch(const QString &command) { + t_assert(DataPtr != nullptr); + + auto it = DataPtr->commands.constFind(command); + if (it == DataPtr->commands.cend()) { + return false; + } + (*it.value())(); + return true; +} + +void enableMediaShortcuts() { + if (!DataPtr) return; + for_const (auto shortcut, DataPtr->mediaShortcuts) { + shortcut->setEnabled(true); + } +} + +void disableMediaShortcuts() { + if (!DataPtr) return; + for_const (auto shortcut, DataPtr->mediaShortcuts) { + shortcut->setEnabled(false); + } +} + +void finish() { + delete DataPtr; + DataPtr = nullptr; +} + +} // namespace Shortcuts diff --git a/Telegram/SourceFiles/shortcuts.h b/Telegram/SourceFiles/shortcuts.h index 8d2483dd1..bfb665e90 100644 --- a/Telegram/SourceFiles/shortcuts.h +++ b/Telegram/SourceFiles/shortcuts.h @@ -28,6 +28,12 @@ namespace Shortcuts { 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(); }