/* This file is part of the KDE libraries SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer SPDX-FileCopyrightText: 1998, 1999, 2000 KDE Team SPDX-FileCopyrightText: 2008 Nick Shaforostoff SPDX-License-Identifier: LGPL-2.0-or-later */ #include "kcheckaccelerators.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KCheckAcceleratorsInitializer : public QObject { Q_OBJECT public: explicit KCheckAcceleratorsInitializer(QObject *parent = nullptr) : QObject(parent) { } public Q_SLOTS: void initiateIfNeeded() { KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Development")); QString sKey = cg.readEntry("CheckAccelerators").trimmed(); int key = 0; if (!sKey.isEmpty()) { QList cuts = QKeySequence::listFromString(sKey); if (!cuts.isEmpty()) { key = cuts.first()[0].toCombined(); } } const bool autoCheck = cg.readEntry("AutoCheckAccelerators", true); if (key == 0 && !autoCheck) { deleteLater(); return; } new KCheckAccelerators(qApp, key, autoCheck); deleteLater(); } }; static void startupFunc() { // Static because in some cases this is called multiple times // but if an application had any of the bad cases we always want // to skip the check static bool doCheckAccelerators = true; if (!doCheckAccelerators) { return; } QCoreApplication *app = QCoreApplication::instance(); if (!app) { // We're being loaded by something that doesn't have a QCoreApplication // this would probably crash at some later point since we do use qApp-> // quite a lot, so skip the magic doCheckAccelerators = false; return; } if (!QCoreApplication::startingUp()) { // If the app has already started, this means we're not being run as part of // qt_call_pre_routines, which most probably means that we're being run as // part of KXmlGui being loaded as part of some plugin of the app, so don't // do any magic doCheckAccelerators = false; return; } if (!QCoreApplication::eventDispatcher()) { // We are called with event dispatcher being null when KXmlGui is being // loaded through plasma-integration instead of being linked to the app // (i.e. QtCreator vs Okular) For apps that don't link directly to KXmlGui // do not do the accelerator magic doCheckAccelerators = false; return; } KCheckAcceleratorsInitializer *initializer = new KCheckAcceleratorsInitializer(app); // Call initiateIfNeeded once we're in the event loop // This is to prevent using KSharedConfig before main() can set the app name QMetaObject::invokeMethod(initializer, "initiateIfNeeded", Qt::QueuedConnection); } Q_COREAPP_STARTUP_FUNCTION(startupFunc) KCheckAccelerators::KCheckAccelerators(QObject *parent, int key_, bool autoCheck_) : QObject(parent) , key(key_) , block(false) , autoCheck(autoCheck_) , drklash(nullptr) { setObjectName(QStringLiteral("kapp_accel_filter")); KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Development")); alwaysShow = cg.readEntry("AlwaysShowCheckAccelerators", false); parent->installEventFilter(this); connect(&autoCheckTimer, &QTimer::timeout, this, &KCheckAccelerators::autoCheckSlot); } bool KCheckAccelerators::eventFilter(QObject * /*obj*/, QEvent *e) { if (block) { return false; } switch (e->type()) { // just simplify debuggin case QEvent::ShortcutOverride: if (key && (static_cast(e)->key() == key)) { block = true; checkAccelerators(false); block = false; e->accept(); return true; } break; case QEvent::ChildAdded: case QEvent::ChildRemoved: // Only care about widgets; this also avoids starting the timer in other // threads if (!static_cast(e)->child()->isWidgetType()) { break; } Q_FALLTHROUGH(); // fall-through case QEvent::Resize: case QEvent::LayoutRequest: case QEvent::WindowActivate: case QEvent::WindowDeactivate: if (autoCheck) { autoCheckTimer.setSingleShot(true); autoCheckTimer.start(20); // 20 ms } return false; case QEvent::Timer: case QEvent::MouseMove: case QEvent::Paint: return false; default: // qCDebug(DEBUG_KXMLGUI) << "KCheckAccelerators::eventFilter " << e->type() // << " " << autoCheck; break; } return false; } void KCheckAccelerators::autoCheckSlot() { if (block) { autoCheckTimer.setSingleShot(true); autoCheckTimer.start(20); return; } block = true; checkAccelerators(!alwaysShow); block = false; } void KCheckAccelerators::createDialog(QWidget *actWin, bool automatic) { if (drklash) { return; } drklash = new QDialog(actWin); drklash->setAttribute(Qt::WA_DeleteOnClose); drklash->setObjectName(QStringLiteral("kapp_accel_check_dlg")); drklash->setWindowTitle(i18nc("@title:window", "Dr. Klash' Accelerator Diagnosis")); drklash->resize(500, 460); QVBoxLayout *layout = new QVBoxLayout(drklash); drklash_view = new QTextBrowser(drklash); layout->addWidget(drklash_view); QCheckBox *disableAutoCheck = nullptr; if (automatic) { disableAutoCheck = new QCheckBox(i18nc("@option:check", "Disable automatic checking"), drklash); connect(disableAutoCheck, &QCheckBox::toggled, this, &KCheckAccelerators::slotDisableCheck); layout->addWidget(disableAutoCheck); } QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, drklash); layout->addWidget(buttonBox); connect(buttonBox, &QDialogButtonBox::rejected, drklash, &QDialog::close); if (disableAutoCheck) { disableAutoCheck->setFocus(); } else { drklash_view->setFocus(); } } void KCheckAccelerators::slotDisableCheck(bool on) { autoCheck = !on; if (!on) { autoCheckSlot(); } } void KCheckAccelerators::checkAccelerators(bool automatic) { QWidget *actWin = qApp->activeWindow(); if (!actWin) { return; } KAcceleratorManager::manage(actWin); QString a; QString c; QString r; KAcceleratorManager::last_manage(a, c, r); if (automatic) { // for now we only show dialogs on F12 checks return; } if (c.isEmpty() && r.isEmpty() && (automatic || a.isEmpty())) { return; } QString s; if (!c.isEmpty()) { s += i18n("

Accelerators changed

") + QLatin1String( "%3
%1%2
") .arg(i18n("Old Text"), i18n("New Text"), c); } if (!r.isEmpty()) { s += i18n("

Accelerators removed

") + QLatin1String("%2
%1
").arg(i18n("Old Text"), r); } if (!a.isEmpty()) { s += i18n("

Accelerators added (just for your info)

") + QLatin1String("%2
%1
").arg(i18n("New Text"), a); } createDialog(actWin, automatic); drklash_view->setHtml(s); drklash->show(); drklash->raise(); // dlg will be destroyed before returning } #include "kcheckaccelerators.moc" #include "moc_kcheckaccelerators.cpp"