Warning, file /plasma/latte-dock/app/shortcuts/globalshortcuts.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org> 0003 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "globalshortcuts.h" 0009 0010 // local 0011 #include "modifiertracker.h" 0012 #include "shortcutstracker.h" 0013 #include "../lattecorona.h" 0014 #include "../layout/centrallayout.h" 0015 #include "../layouts/manager.h" 0016 #include "../layouts/synchronizer.h" 0017 #include "../settings/settingsdialog/settingsdialog.h" 0018 #include "../settings/universalsettings.h" 0019 #include "../view/containmentinterface.h" 0020 #include "../view/view.h" 0021 0022 // C++ 0023 #include <array> 0024 0025 // Qt 0026 #include <QAction> 0027 #include <QDebug> 0028 #include <QQuickItem> 0029 #include <QMetaMethod> 0030 #include <QX11Info> 0031 0032 // KDE 0033 #include <KActionCollection> 0034 #include <KGlobalAccel> 0035 #include <KLocalizedString> 0036 #include <KPluginMetaData> 0037 0038 // Plasma 0039 #include <Plasma/Applet> 0040 #include <Plasma/Containment> 0041 0042 0043 namespace Latte { 0044 0045 GlobalShortcuts::GlobalShortcuts(QObject *parent) 0046 : QObject(parent) 0047 { 0048 m_corona = qobject_cast<Latte::Corona *>(parent); 0049 m_modifierTracker = new ShortcutsPart::ModifierTracker(this); 0050 m_shortcutsTracker = new ShortcutsPart::ShortcutsTracker(this); 0051 0052 if (m_corona) { 0053 init(); 0054 } 0055 0056 m_hideViewsTimer.setSingleShot(true); 0057 0058 if (QX11Info::isPlatformX11()) { 0059 //in X11 the timer is a poller that checks to see if the modifier keys 0060 //from user global shortcut have been released 0061 m_hideViewsTimer.setInterval(300); 0062 } else { 0063 //on wayland in acting just as simple timer that hides the view afterwards 0064 m_hideViewsTimer.setInterval(2500); 0065 } 0066 0067 connect(&m_hideViewsTimer, &QTimer::timeout, this, &GlobalShortcuts::hideViewsTimerSlot); 0068 } 0069 0070 GlobalShortcuts::~GlobalShortcuts() 0071 { 0072 if (m_modifierTracker) { 0073 m_modifierTracker->deleteLater(); 0074 } 0075 0076 if (m_shortcutsTracker) { 0077 m_shortcutsTracker->deleteLater(); 0078 } 0079 } 0080 0081 void GlobalShortcuts::init() 0082 { 0083 KActionCollection *generalActions = new KActionCollection(m_corona); 0084 0085 //show-hide the main view in the primary screen 0086 QAction *showAction = generalActions->addAction(QStringLiteral("show latte view")); 0087 showAction->setText(i18n("Show Latte Dock/Panel")); 0088 showAction->setShortcut(QKeySequence(Qt::META + '`')); 0089 KGlobalAccel::setGlobalShortcut(showAction, QKeySequence(Qt::META + '`')); 0090 connect(showAction, &QAction::triggered, this, [this]() { 0091 showViews(); 0092 }); 0093 0094 //show-cycle between Latte settings windows 0095 QAction *settingsAction = generalActions->addAction(QStringLiteral("show view settings")); 0096 settingsAction->setText(i18n("Cycle Through Dock/Panel Settings Windows")); 0097 KGlobalAccel::setGlobalShortcut(settingsAction, QKeySequence(Qt::META + Qt::Key_A)); 0098 connect(settingsAction, &QAction::triggered, this, [this] { 0099 m_modifierTracker->cancelMetaPressed(); 0100 showSettings(); 0101 }); 0102 0103 //show the layouts editor 0104 QAction *layoutsAction = generalActions->addAction(QStringLiteral("show latte global settings")); 0105 layoutsAction->setText(i18n("Show Latte Global Settings")); 0106 layoutsAction->setShortcut(QKeySequence()); 0107 KGlobalAccel::setGlobalShortcut(layoutsAction, QKeySequence()); 0108 connect(layoutsAction, &QAction::triggered, this, [this]() { 0109 m_modifierTracker->cancelMetaPressed(); 0110 m_corona->layoutsManager()->showLatteSettingsDialog(Settings::Dialog::PreferencesPage, true); 0111 }); 0112 0113 KActionCollection *taskbarActions = new KActionCollection(m_corona); 0114 0115 //activate actions [1-9] 0116 for (int i = 1; i < 10; ++i) { 0117 const int entryNumber = i; 0118 const Qt::Key key = static_cast<Qt::Key>(Qt::Key_0 + i); 0119 0120 QAction *action = taskbarActions->addAction(QStringLiteral("activate entry %1").arg(QString::number(entryNumber))); 0121 action->setText(i18n("Activate Entry %1", entryNumber)); 0122 action->setShortcut(QKeySequence(Qt::META + key)); 0123 KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + key)); 0124 connect(action, &QAction::triggered, this, [this, i] { 0125 // qDebug() << "meta action..."; 0126 m_modifierTracker->cancelMetaPressed(); 0127 activateEntry(i, static_cast<Qt::Key>(Qt::META)); 0128 }); 0129 } 0130 0131 //! Array that is used to register correctly actions for task index>=10 and <19 0132 std::array<Qt::Key, 10> keysAboveTen{ Qt::Key_0, Qt::Key_Z, Qt::Key_X, Qt::Key_C, Qt::Key_V, Qt::Key_B, Qt::Key_N, Qt::Key_M, Qt::Key_Comma, Qt::Key_Period }; 0133 0134 //activate actions [10-19] 0135 for (int i = 10; i < 20; ++i) { 0136 QAction *action = taskbarActions->addAction(QStringLiteral("activate entry %1").arg(QString::number(i))); 0137 action->setText(i18n("Activate Entry %1", i)); 0138 action->setShortcut(QKeySequence(Qt::META + keysAboveTen[i - 10])); 0139 KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + keysAboveTen[i - 10])); 0140 connect(action, &QAction::triggered, this, [this, i] { 0141 m_modifierTracker->cancelMetaPressed(); 0142 activateEntry(i, static_cast<Qt::Key>(Qt::META)); 0143 }); 0144 } 0145 0146 //new instance actions [1-9] 0147 for (int i = 1; i < 10; ++i) { 0148 const int entryNumber = i; 0149 const Qt::Key key = static_cast<Qt::Key>(Qt::Key_0 + i); 0150 0151 QAction *action = taskbarActions->addAction(QStringLiteral("new instance for entry %1").arg(QString::number(entryNumber))); 0152 action->setText(i18n("New Instance for Entry %1", entryNumber)); 0153 KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + Qt::CTRL + key)); 0154 connect(action, &QAction::triggered, this, [this, i] { 0155 // qDebug() << "meta + ctrl + action..."; 0156 m_modifierTracker->cancelMetaPressed(); 0157 activateEntry(i, static_cast<Qt::Key>(Qt::CTRL)); 0158 }); 0159 } 0160 0161 //new instance actions [10-19] 0162 for (int i = 10; i < 20; ++i) { 0163 QAction *action = taskbarActions->addAction(QStringLiteral("new instance for entry %1").arg(QString::number(i))); 0164 action->setText(i18n("New Instance for Entry %1", i)); 0165 KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + Qt::CTRL + keysAboveTen[i - 10])); 0166 connect(action, &QAction::triggered, this, [this, i] { 0167 m_modifierTracker->cancelMetaPressed(); 0168 activateEntry(i, static_cast<Qt::Key>(Qt::CTRL)); 0169 }); 0170 } 0171 0172 m_singleMetaAction = new QAction(this); 0173 m_singleMetaAction->setShortcut(QKeySequence(Qt::META)); 0174 0175 connect(m_corona->universalSettings(), &UniversalSettings::metaPressAndHoldEnabledChanged , this, [&]() { 0176 if (!m_corona->universalSettings()->metaPressAndHoldEnabled()) { 0177 m_modifierTracker->blockModifierTracking(Qt::Key_Super_L); 0178 m_modifierTracker->blockModifierTracking(Qt::Key_Super_R); 0179 } else { 0180 m_modifierTracker->unblockModifierTracking(Qt::Key_Super_L); 0181 m_modifierTracker->unblockModifierTracking(Qt::Key_Super_R); 0182 } 0183 }); 0184 0185 //display shortcut badges while holding Meta 0186 connect(m_modifierTracker, &ShortcutsPart::ModifierTracker::metaModifierPressed, this, [&]() { 0187 m_metaShowedViews = true; 0188 showViews(); 0189 }); 0190 } 0191 0192 ShortcutsPart::ShortcutsTracker *GlobalShortcuts::shortcutsTracker() const 0193 { 0194 return m_shortcutsTracker; 0195 } 0196 0197 Latte::View *GlobalShortcuts::highestApplicationLauncherView(const QList<Latte::View *> &views) const 0198 { 0199 if (views.isEmpty()) { 0200 return nullptr; 0201 } 0202 0203 Latte::View *highestPriorityView{nullptr}; 0204 0205 for (const auto view : views) { 0206 if (view->extendedInterface()->applicationLauncherHasGlobalShortcut()) { 0207 highestPriorityView = view; 0208 break; 0209 } 0210 } 0211 0212 if (!highestPriorityView) { 0213 for (const auto view : views) { 0214 if (view->extendedInterface()->containsApplicationLauncher()) { 0215 highestPriorityView = view; 0216 break; 0217 } 0218 } 0219 } 0220 0221 return highestPriorityView; 0222 } 0223 0224 //! Activate launcher menu through dbus interface 0225 void GlobalShortcuts::activateLauncherMenu() 0226 { 0227 if (m_metaShowedViews) { 0228 return; 0229 } 0230 0231 QList<Latte::View *> sortedViews = m_corona->layoutsManager()->synchronizer()->sortedCurrentViews(); 0232 0233 Latte::View *highestPriorityView = highestApplicationLauncherView(sortedViews); 0234 0235 if (highestPriorityView) { 0236 if (!highestPriorityView->visibility()->isShownFully() && highestPriorityView->extendedInterface()->applicationLauncherInPopup()) { 0237 m_lastInvokedAction = m_singleMetaAction; 0238 0239 //! wait for view to fully shown 0240 connect(highestPriorityView->visibility(), &Latte::ViewPart::VisibilityManager::isShownFullyChanged, this, [&, highestPriorityView](){ 0241 if (highestPriorityView->visibility()->isShownFully()) { 0242 highestPriorityView->extendedInterface()->toggleAppletExpanded(highestPriorityView->extendedInterface()->applicationLauncherId()); 0243 //!remove that signal tracking 0244 disconnect(highestPriorityView->visibility(), &Latte::ViewPart::VisibilityManager::isShownFullyChanged, this, nullptr); 0245 } 0246 }); 0247 0248 //! That signal is removed from Latte::View only when the popup is really shown! 0249 highestPriorityView->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); 0250 0251 } else { 0252 highestPriorityView->extendedInterface()->toggleAppletExpanded(highestPriorityView->extendedInterface()->applicationLauncherId()); 0253 } 0254 } 0255 } 0256 0257 bool GlobalShortcuts::activatePlasmaTaskManager(const Latte::View *view, int index, Qt::Key modifier, bool *delayedExecution) 0258 { 0259 bool activation{modifier == static_cast<Qt::Key>(Qt::META)}; 0260 bool newInstance{!activation}; 0261 0262 if (!view->visibility()->isShownFully()) { 0263 //! wait for view to fully shown 0264 connect(view->visibility(), &Latte::ViewPart::VisibilityManager::isShownFullyChanged, this, [&, view, index, activation](){ 0265 if (view->visibility()->isShownFully()) { 0266 if (activation) { 0267 view->extendedInterface()->activatePlasmaTask(index); 0268 } else { 0269 view->extendedInterface()->newInstanceForPlasmaTask(index); 0270 } 0271 //!remove that signal tracking 0272 disconnect(view->visibility(), &Latte::ViewPart::VisibilityManager::isShownFullyChanged, this, nullptr); 0273 } 0274 }); 0275 0276 *delayedExecution = true; 0277 0278 return true; 0279 } else { 0280 *delayedExecution = false; 0281 0282 return (activation ? view->extendedInterface()->activatePlasmaTask(index) : view->extendedInterface()->newInstanceForPlasmaTask(index)); 0283 } 0284 } 0285 0286 bool GlobalShortcuts::activateLatteEntry(Latte::View *view, int index, Qt::Key modifier, bool *delayedExecution) 0287 { 0288 bool activation{modifier == static_cast<Qt::Key>(Qt::META)}; 0289 bool newInstance{!activation}; 0290 0291 int appletId = view->extendedInterface()->appletIdForVisualIndex(index); 0292 bool hasPopUp {(appletId>-1 && view->extendedInterface()->appletIsExpandable(appletId))}; 0293 0294 if (!view->visibility()->isShownFully() && hasPopUp) { 0295 //! wait for view to fully shown 0296 connect(view->visibility(), &Latte::ViewPart::VisibilityManager::isShownFullyChanged, this, [&, view, index, activation](){ 0297 if (view->visibility()->isShownFully()) { 0298 if (activation) { 0299 view->extendedInterface()->activateEntry(index); 0300 } else { 0301 view->extendedInterface()->newInstanceForEntry(index); 0302 } 0303 //!remove that signal tracking 0304 disconnect(view->visibility(), &Latte::ViewPart::VisibilityManager::isShownFullyChanged, this, nullptr); 0305 } 0306 }); 0307 0308 *delayedExecution = true; 0309 0310 return true; 0311 } else { 0312 *delayedExecution = false; 0313 0314 return (activation ? view->extendedInterface()->activateEntry(index) : view->extendedInterface()->newInstanceForEntry(index)); 0315 } 0316 } 0317 0318 bool GlobalShortcuts::activateEntryForView(Latte::View *view, int index, Qt::Key modifier) 0319 { 0320 if (!view) { 0321 return false; 0322 } 0323 0324 bool delayed{false}; 0325 0326 bool executed = ((!view->extendedInterface()->hasLatteTasks() && view->extendedInterface()->hasPlasmaTasks() 0327 && activatePlasmaTaskManager(view, index, modifier, &delayed)) 0328 || activateLatteEntry(view, index, modifier, &delayed)); 0329 0330 if (executed) { 0331 if (!m_hideViews.contains(view)) { 0332 m_hideViews.append(view); 0333 } 0334 0335 if (delayed) { 0336 view->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); 0337 m_hideViewsTimer.start(); 0338 } 0339 0340 return true; 0341 } 0342 0343 return false; 0344 } 0345 0346 //! Activate task manager entry 0347 void GlobalShortcuts::activateEntry(int index, Qt::Key modifier) 0348 { 0349 m_lastInvokedAction = dynamic_cast<QAction *>(sender()); 0350 0351 QList<Latte::View *> sortedViews = m_corona->layoutsManager()->synchronizer()->sortedCurrentViews(); 0352 0353 Latte::View *highest{nullptr}; 0354 0355 for (const auto view : sortedViews) { 0356 if (view->isPreferredForShortcuts()) { 0357 highest = view; 0358 break; 0359 } 0360 } 0361 0362 if (highest) { 0363 activateEntryForView(highest, index, modifier); 0364 } else { 0365 for (const auto view : sortedViews) { 0366 if (activateEntryForView(view, index, modifier)) { 0367 return; 0368 } 0369 } 0370 } 0371 } 0372 0373 //! update badge for specific view item 0374 void GlobalShortcuts::updateViewItemBadge(QString identifier, QString value) 0375 { 0376 QList<Latte::View *> views = m_corona->layoutsManager()->synchronizer()->currentViews(); 0377 0378 // update badges in all Latte Tasks plasmoids 0379 for (const auto &view : views) { 0380 view->extendedInterface()->updateBadgeForLatteTask(identifier, value); 0381 } 0382 } 0383 0384 void GlobalShortcuts::showViews() 0385 { 0386 m_lastInvokedAction = dynamic_cast<QAction *>(sender()); 0387 if (!m_lastInvokedAction) { 0388 // when holding Meta 0389 m_lastInvokedAction = m_singleMetaAction; 0390 } 0391 0392 QList<Latte::View *> sortedViews = m_corona->layoutsManager()->synchronizer()->sortedCurrentViews(); 0393 0394 Latte::View *viewWithTasks{nullptr}; 0395 Latte::View *viewWithMeta{nullptr}; 0396 0397 bool hasPreferredForShortcutsView{false}; 0398 0399 for (const auto view : sortedViews) { 0400 if (view->isPreferredForShortcuts()) { 0401 hasPreferredForShortcutsView = true; 0402 break; 0403 } 0404 } 0405 0406 for(const auto view : sortedViews) { 0407 if (!viewWithTasks 0408 && (!hasPreferredForShortcutsView || view->isPreferredForShortcuts())) { 0409 viewWithTasks = view; 0410 break; 0411 } 0412 } 0413 0414 if (m_corona->universalSettings()->kwin_metaForwardedToLatte()) { 0415 viewWithMeta = highestApplicationLauncherView(sortedViews); 0416 } 0417 0418 bool viewFound{false}; 0419 0420 if (!m_hideViewsTimer.isActive()) { 0421 m_hideViews.clear(); 0422 } 0423 0424 //! show view that contains tasks plasmoid 0425 if (viewWithTasks) { 0426 viewFound = true; 0427 0428 bool showMeta = (viewWithMeta && (viewWithMeta == viewWithTasks)); 0429 0430 viewWithTasks->extendedInterface()->showShortcutBadges(true, showMeta); 0431 0432 if (!m_hideViewsTimer.isActive()) { 0433 m_hideViews.append(viewWithTasks); 0434 viewWithTasks->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); 0435 } 0436 } 0437 0438 //! show view that contains only meta 0439 if (viewWithMeta && viewWithMeta != viewWithTasks && viewWithMeta->extendedInterface()->showOnlyMeta()) { 0440 viewFound = true; 0441 0442 if (!m_hideViewsTimer.isActive()) { 0443 m_hideViews.append(viewWithMeta); 0444 viewWithMeta->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); 0445 } 0446 } 0447 0448 //! show all the rest views that contain plasma shortcuts 0449 QList<Latte::View *> viewsWithShortcuts = m_corona->layoutsManager()->synchronizer()->currentViewsWithPlasmaShortcuts(); 0450 0451 if (viewsWithShortcuts.count() > 0) { 0452 viewFound = true; 0453 0454 if (!m_hideViewsTimer.isActive()) { 0455 for(const auto view : viewsWithShortcuts) { 0456 if (view != viewWithTasks && view != viewWithMeta) { 0457 if (view->extendedInterface()->showShortcutBadges(false, false)) { 0458 m_hideViews.append(view); 0459 view->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); 0460 } 0461 } 0462 } 0463 } 0464 } 0465 0466 if (viewFound) { 0467 if (!m_hideViewsTimer.isActive()) { 0468 m_hideViewsTimer.start(); 0469 } else { 0470 m_hideViewsTimer.stop(); 0471 hideViewsTimerSlot(); 0472 } 0473 } 0474 } 0475 0476 bool GlobalShortcuts::viewsToHideAreValid() 0477 { 0478 for(const auto view : m_hideViews) { 0479 if (!m_corona->layoutsManager()->synchronizer()->latteViewExists(view)) { 0480 return false; 0481 } 0482 0483 } 0484 0485 return true; 0486 } 0487 0488 void GlobalShortcuts::showSettings() 0489 { 0490 QList<Latte::View *> sortedViews = m_corona->layoutsManager()->synchronizer()->sortedCurrentOriginalViews(); 0491 0492 //! find which is the next view to show its settings 0493 if (sortedViews.count() > 0) { 0494 int openSettings = -1; 0495 0496 //! find last view that showed its config view 0497 for (int i = 0; i < sortedViews.size(); ++i) { 0498 for (auto currentLayout : m_corona->layoutsManager()->currentLayouts()) { 0499 if (sortedViews[i] == currentLayout->lastConfigViewFor()) { 0500 openSettings = i; 0501 break; 0502 } 0503 } 0504 0505 if (openSettings >= 0) { 0506 break; 0507 } 0508 } 0509 0510 if (openSettings >= 0 && sortedViews.count() > 1) { 0511 //! check if there is a view with opened settings window 0512 openSettings = sortedViews[openSettings]->settingsWindowIsShown() ? openSettings + 1 : openSettings; 0513 0514 if (openSettings >= sortedViews.size()) { 0515 openSettings = 0; 0516 } 0517 0518 sortedViews[openSettings]->showSettingsWindow(); 0519 } else { 0520 sortedViews[0]->showSettingsWindow(); 0521 } 0522 } 0523 } 0524 0525 void GlobalShortcuts::hideViewsTimerSlot() 0526 { 0527 if (!m_lastInvokedAction || m_hideViews.count() == 0) { 0528 return; 0529 } 0530 0531 auto initParameters = [this]() { 0532 m_lastInvokedAction = Q_NULLPTR; 0533 0534 if (viewsToHideAreValid()) { 0535 for(const auto latteView : m_hideViews) { 0536 latteView->visibility()->removeBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); 0537 latteView->extendedInterface()->hideShortcutBadges(); 0538 0539 if (latteView->visibility()->isSidebar() && !latteView->visibility()->isHidden()) { 0540 latteView->visibility()->toggleHiddenState(); 0541 } 0542 } 0543 } 0544 0545 m_hideViews.clear(); 0546 m_metaShowedViews = false; 0547 }; 0548 0549 // qDebug() << "MEMORY ::: " << m_hideViews.count() << " _ " << m_viewItemsCalled.count() << " _ " << m_showShortcutBadgesMethods.count(); 0550 0551 if (QX11Info::isPlatformX11()) { 0552 if (!m_modifierTracker->sequenceModifierPressed(m_lastInvokedAction->shortcut())) { 0553 initParameters(); 0554 0555 return; 0556 } else { 0557 m_hideViewsTimer.start(); 0558 } 0559 } else { 0560 // TODO: This is needs to be fixed in wayland 0561 initParameters(); 0562 } 0563 } 0564 0565 } 0566 0567 #include "moc_globalshortcuts.cpp"