KXmlGui

kcheckaccelerators.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
4 SPDX-FileCopyrightText: 1998, 1999, 2000 KDE Team
5 SPDX-FileCopyrightText: 2008 Nick Shaforostoff <shaforostoff@kde.ru>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10#include "kcheckaccelerators.h"
11
12#include <QAction>
13#include <QApplication>
14#include <QChar>
15#include <QCheckBox>
16#include <QClipboard>
17#include <QComboBox>
18#include <QDebug>
19#include <QDialog>
20#include <QDialogButtonBox>
21#include <QFile>
22#include <QGroupBox>
23#include <QLabel>
24#include <QMenu>
25#include <QMouseEvent>
26#include <QPushButton>
27#include <QShortcutEvent>
28#include <QTabBar>
29#include <QTextBrowser>
30#include <QVBoxLayout>
31
32#include <KAcceleratorManager>
33#include <KConfig>
34#include <KConfigGroup>
35#include <KLocalizedString>
36#include <KSharedConfig>
37
38class KCheckAcceleratorsInitializer : public QObject
39{
41public:
42 explicit KCheckAcceleratorsInitializer(QObject *parent = nullptr)
44 {
45 }
46
47public Q_SLOTS:
48 void initiateIfNeeded()
49 {
50 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Development"));
51 QString sKey = cg.readEntry("CheckAccelerators").trimmed();
52 int key = 0;
53 if (!sKey.isEmpty()) {
55 if (!cuts.isEmpty()) {
56 key = cuts.first()[0].toCombined();
57 }
58 }
59 const bool autoCheck = cg.readEntry("AutoCheckAccelerators", true);
60 if (key == 0 && !autoCheck) {
62 return;
63 }
64
65 new KCheckAccelerators(qApp, key, autoCheck);
67 }
68};
69
70static void startupFunc()
71{
72 // Static because in some cases this is called multiple times
73 // but if an application had any of the bad cases we always want
74 // to skip the check
75 static bool doCheckAccelerators = true;
76
77 if (!doCheckAccelerators) {
78 return;
79 }
80
82 if (!app) {
83 // We're being loaded by something that doesn't have a QCoreApplication
84 // this would probably crash at some later point since we do use qApp->
85 // quite a lot, so skip the magic
86 doCheckAccelerators = false;
87 return;
88 }
89
91 // If the app has already started, this means we're not being run as part of
92 // qt_call_pre_routines, which most probably means that we're being run as
93 // part of KXmlGui being loaded as part of some plugin of the app, so don't
94 // do any magic
95 doCheckAccelerators = false;
96 return;
97 }
98
100 // We are called with event dispatcher being null when KXmlGui is being
101 // loaded through plasma-integration instead of being linked to the app
102 // (i.e. QtCreator vs Okular) For apps that don't link directly to KXmlGui
103 // do not do the accelerator magic
104 doCheckAccelerators = false;
105 return;
106 }
107
108 KCheckAcceleratorsInitializer *initializer = new KCheckAcceleratorsInitializer(app);
109 // Call initiateIfNeeded once we're in the event loop
110 // This is to prevent using KSharedConfig before main() can set the app name
111 QMetaObject::invokeMethod(initializer, "initiateIfNeeded", Qt::QueuedConnection);
112}
113
114Q_COREAPP_STARTUP_FUNCTION(startupFunc)
115
116KCheckAccelerators::KCheckAccelerators(QObject *parent, int key_, bool autoCheck_)
117 : QObject(parent)
118 , key(key_)
119 , block(false)
120 , autoCheck(autoCheck_)
121 , drklash(nullptr)
122{
123 setObjectName(QStringLiteral("kapp_accel_filter"));
124
125 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Development"));
126 alwaysShow = cg.readEntry("AlwaysShowCheckAccelerators", false);
127
128 parent->installEventFilter(this);
129 connect(&autoCheckTimer, &QTimer::timeout, this, &KCheckAccelerators::autoCheckSlot);
130}
131
133{
134 if (block) {
135 return false;
136 }
137
138 switch (e->type()) { // just simplify debuggin
140 if (key && (static_cast<QKeyEvent *>(e)->key() == key)) {
141 block = true;
142 checkAccelerators(false);
143 block = false;
144 e->accept();
145 return true;
146 }
147 break;
150 // Only care about widgets; this also avoids starting the timer in other
151 // threads
152 if (!static_cast<QChildEvent *>(e)->child()->isWidgetType()) {
153 break;
154 }
155 Q_FALLTHROUGH();
156 // fall-through
157 case QEvent::Resize:
161 if (autoCheck) {
162 autoCheckTimer.setSingleShot(true);
163 autoCheckTimer.start(20); // 20 ms
164 }
165 return false;
166 case QEvent::Timer:
168 case QEvent::Paint:
169 return false;
170 default:
171 // qCDebug(DEBUG_KXMLGUI) << "KCheckAccelerators::eventFilter " << e->type()
172 // << " " << autoCheck;
173 break;
174 }
175 return false;
176}
177
178void KCheckAccelerators::autoCheckSlot()
179{
180 if (block) {
181 autoCheckTimer.setSingleShot(true);
182 autoCheckTimer.start(20);
183 return;
184 }
185 block = true;
186 checkAccelerators(!alwaysShow);
187 block = false;
188}
189
190void KCheckAccelerators::createDialog(QWidget *actWin, bool automatic)
191{
192 if (drklash) {
193 return;
194 }
195
196 drklash = new QDialog(actWin);
197 drklash->setAttribute(Qt::WA_DeleteOnClose);
198 drklash->setObjectName(QStringLiteral("kapp_accel_check_dlg"));
199 drklash->setWindowTitle(i18nc("@title:window", "Dr. Klash' Accelerator Diagnosis"));
200 drklash->resize(500, 460);
201 QVBoxLayout *layout = new QVBoxLayout(drklash);
202 drklash_view = new QTextBrowser(drklash);
203 layout->addWidget(drklash_view);
204 QCheckBox *disableAutoCheck = nullptr;
205 if (automatic) {
206 disableAutoCheck = new QCheckBox(i18nc("@option:check", "Disable automatic checking"), drklash);
207 connect(disableAutoCheck, &QCheckBox::toggled, this, &KCheckAccelerators::slotDisableCheck);
208 layout->addWidget(disableAutoCheck);
209 }
211 layout->addWidget(buttonBox);
212 connect(buttonBox, &QDialogButtonBox::rejected, drklash, &QDialog::close);
213 if (disableAutoCheck) {
214 disableAutoCheck->setFocus();
215 } else {
216 drklash_view->setFocus();
217 }
218}
219
220void KCheckAccelerators::slotDisableCheck(bool on)
221{
222 autoCheck = !on;
223 if (!on) {
224 autoCheckSlot();
225 }
226}
227
228void KCheckAccelerators::checkAccelerators(bool automatic)
229{
230 QWidget *actWin = qApp->activeWindow();
231 if (!actWin) {
232 return;
233 }
234
236 QString a;
237 QString c;
238 QString r;
240
241 if (automatic) { // for now we only show dialogs on F12 checks
242 return;
243 }
244
245 if (c.isEmpty() && r.isEmpty() && (automatic || a.isEmpty())) {
246 return;
247 }
248
249 QString s;
250
251 if (!c.isEmpty()) {
252 s += i18n("<h2>Accelerators changed</h2>")
254 "<table "
255 "border><tr><th><b>%1</b></th><th><b>%2</b></th></tr>%3</table>")
256 .arg(i18n("Old Text"), i18n("New Text"), c);
257 }
258
259 if (!r.isEmpty()) {
260 s += i18n("<h2>Accelerators removed</h2>") + QLatin1String("<table border><tr><th><b>%1</b></th></tr>%2</table>").arg(i18n("Old Text"), r);
261 }
262
263 if (!a.isEmpty()) {
264 s += i18n("<h2>Accelerators added (just for your info)</h2>")
265 + QLatin1String("<table border><tr><th><b>%1</b></th></tr>%2</table>").arg(i18n("New Text"), a);
266 }
267
268 createDialog(actWin, automatic);
269 drklash_view->setHtml(s);
270 drklash->show();
271 drklash->raise();
272
273 // dlg will be destroyed before returning
274}
275
276#include "kcheckaccelerators.moc"
277#include "moc_kcheckaccelerators.cpp"
static void last_manage(QString &added, QString &changed, QString &removed)
static void manage(QWidget *widget, bool programmers_mode=false)
bool eventFilter(QObject *, QEvent *e) override
Re-implemented to filter the parent's events.
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
void toggled(bool checked)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QAbstractEventDispatcher * eventDispatcher()
QCoreApplication * instance()
ShortcutOverride
void accept()
Type type() const const
QList< QKeySequence > listFromString(const QString &str, SequenceFormat format)
T & first()
bool isEmpty() const const
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
Q_OBJECTQ_OBJECT
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void installEventFilter(QObject *filterObj)
bool isWidgetType() const const
QObject * parent() const const
bool isEmpty() const const
QString trimmed() const const
QueuedConnection
WA_DeleteOnClose
void setHtml(const QString &text)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setSingleShot(bool singleShot)
void start()
void timeout()
bool close()
void setFocus()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:19:32 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.