diff --git a/src/kcheckaccelerators.cpp b/src/kcheckaccelerators.cpp index 13fc012..e08447d 100644 --- a/src/kcheckaccelerators.cpp +++ b/src/kcheckaccelerators.cpp @@ -1,319 +1,353 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) Copyright (C) 1998, 1999, 2000 KDE Team Copyright (C) 2008 Nick Shaforostoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #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 class KCheckAcceleratorsInitializer : public QObject { Q_OBJECT public: explicit KCheckAcceleratorsInitializer(QObject *parent = nullptr) : QObject(parent) { } public Q_SLOTS: void initiateIfNeeded() { KConfigGroup cg(KSharedConfig::openConfig(), "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]; } } const bool autoCheck = cg.readEntry("AutoCheckAccelerators", true); const bool copyWidgetText = cg.readEntry("CopyWidgetText", false); if (!copyWidgetText && key == 0 && !autoCheck) { deleteLater(); return; } new KCheckAccelerators(qApp, key, autoCheck, copyWidgetText); deleteLater(); } }; static void startupFunc() { - // Call initiateIfNeeded once we're in the event loop - // This is to prevent using KSharedConfig before main() can set the app name + // 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_, bool copyWidgetText_) : QObject(parent) , key(key_) , block(false) , autoCheck(autoCheck_) , copyWidgetText(copyWidgetText_) , drklash(nullptr) { setObjectName(QStringLiteral("kapp_accel_filter")); KConfigGroup cg(KSharedConfig::openConfig(), "Development"); alwaysShow = cg.readEntry("AlwaysShowCheckAccelerators", false); copyWidgetTextCommand = cg.readEntry("CopyWidgetTextCommand", QString()); 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 } break; //case QEvent::MouseButtonDblClick: case QEvent::MouseButtonPress: if (copyWidgetText && static_cast(e)->button() == Qt::MidButton) { //kWarning()<<"obj"<(obj)->childAt(static_cast(e)->pos()); if (!w) { w = static_cast(obj); } if (!w) { return false; } //kWarning()<<"MouseButtonDblClick"<(w)) { text = static_cast(w)->text(); } else if (qobject_cast(w)) { text = static_cast(w)->text(); } else if (qobject_cast(w)) { text = static_cast(w)->currentText(); } else if (qobject_cast(w)) { text = static_cast(w)->tabText(static_cast(w)->tabAt(static_cast(e)->pos())); } else if (qobject_cast(w)) { text = static_cast(w)->title(); } else if (qobject_cast(obj)) { QAction *a = static_cast(obj)->actionAt(static_cast(e)->pos()); if (!a) { return false; } text = a->text(); if (text.isEmpty()) { text = a->iconText(); } } if (text.isEmpty()) { return false; } if (static_cast(e)->modifiers() == Qt::ControlModifier) { text.remove(QChar::fromLatin1('&')); } //kWarning()<setText(text); } else { QProcess *script = new QProcess(this); script->start(copyWidgetTextCommand.arg(text, QFile::decodeName(KLocalizedString::applicationDomain()))); connect(script, QOverload::of(&QProcess::finished), script, &QObject::deleteLater); } e->accept(); return true; //kWarning()<<"MouseButtonDblClick"<(obj)->childAt(static_cast(e)->globalPos()); } 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, c, 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

"); s += QStringLiteral(""); s += c; s += QStringLiteral("
"); s += i18n("Old Text"); s += QStringLiteral(""); s += i18n("New Text"); s += QStringLiteral("
"); } if (! r.isEmpty()) { s += i18n("

Accelerators removed

"); s += QStringLiteral(""); s += r; s += QStringLiteral("
"); s += i18n("Old Text"); s += QStringLiteral("
"); } if (! a.isEmpty()) { s += i18n("

Accelerators added (just for your info)

"); s += QStringLiteral(""); s += a; s += QStringLiteral("
"); s += i18n("New Text"); s += QStringLiteral("
"); } createDialog(actWin, automatic); drklash_view->setHtml(s); drklash->show(); drklash->raise(); // dlg will be destroyed before returning } #include "kcheckaccelerators.moc"