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 | |
38 | KCheckAccelerators::KCheckAccelerators(QObject *parent, int key_, bool autoCheck_) |
39 | : QObject(parent) |
40 | , key(key_) |
41 | , block(false) |
42 | , autoCheck(autoCheck_) |
43 | , drklash(nullptr) |
44 | { |
45 | setObjectName(QStringLiteral("kapp_accel_filter" )); |
46 | |
47 | KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Development" )); |
48 | alwaysShow = cg.readEntry(key: "AlwaysShowCheckAccelerators" , defaultValue: false); |
49 | |
50 | parent->installEventFilter(filterObj: this); |
51 | connect(sender: &autoCheckTimer, signal: &QTimer::timeout, context: this, slot: &KCheckAccelerators::autoCheckSlot); |
52 | } |
53 | |
54 | bool KCheckAccelerators::eventFilter(QObject * /*obj*/, QEvent *e) |
55 | { |
56 | if (block) { |
57 | return false; |
58 | } |
59 | |
60 | switch (e->type()) { // just simplify debuggin |
61 | case QEvent::ShortcutOverride: |
62 | if (key && (static_cast<QKeyEvent *>(e)->key() == key)) { |
63 | block = true; |
64 | checkAccelerators(automatic: false); |
65 | block = false; |
66 | e->accept(); |
67 | return true; |
68 | } |
69 | break; |
70 | case QEvent::ChildAdded: |
71 | case QEvent::ChildRemoved: |
72 | // Only care about widgets; this also avoids starting the timer in other |
73 | // threads |
74 | if (!static_cast<QChildEvent *>(e)->child()->isWidgetType()) { |
75 | break; |
76 | } |
77 | Q_FALLTHROUGH(); |
78 | // fall-through |
79 | case QEvent::Resize: |
80 | case QEvent::LayoutRequest: |
81 | case QEvent::WindowActivate: |
82 | case QEvent::WindowDeactivate: |
83 | if (autoCheck) { |
84 | autoCheckTimer.setSingleShot(true); |
85 | autoCheckTimer.start(msec: 20); // 20 ms |
86 | } |
87 | return false; |
88 | case QEvent::Timer: |
89 | case QEvent::MouseMove: |
90 | case QEvent::Paint: |
91 | return false; |
92 | default: |
93 | // qCDebug(DEBUG_KXMLGUI) << "KCheckAccelerators::eventFilter " << e->type() |
94 | // << " " << autoCheck; |
95 | break; |
96 | } |
97 | return false; |
98 | } |
99 | |
100 | void KCheckAccelerators::autoCheckSlot() |
101 | { |
102 | if (block) { |
103 | autoCheckTimer.setSingleShot(true); |
104 | autoCheckTimer.start(msec: 20); |
105 | return; |
106 | } |
107 | block = true; |
108 | checkAccelerators(automatic: !alwaysShow); |
109 | block = false; |
110 | } |
111 | |
112 | void KCheckAccelerators::createDialog(QWidget *actWin, bool automatic) |
113 | { |
114 | if (drklash) { |
115 | return; |
116 | } |
117 | |
118 | drklash = new QDialog(actWin); |
119 | drklash->setAttribute(Qt::WA_DeleteOnClose); |
120 | drklash->setObjectName(QStringLiteral("kapp_accel_check_dlg" )); |
121 | drklash->setWindowTitle(i18nc("@title:window" , "Dr. Klash' Accelerator Diagnosis" )); |
122 | drklash->resize(w: 500, h: 460); |
123 | QVBoxLayout *layout = new QVBoxLayout(drklash); |
124 | drklash_view = new QTextBrowser(drklash); |
125 | layout->addWidget(drklash_view); |
126 | QCheckBox *disableAutoCheck = nullptr; |
127 | if (automatic) { |
128 | disableAutoCheck = new QCheckBox(i18nc("@option:check" , "Disable automatic checking" ), drklash); |
129 | connect(sender: disableAutoCheck, signal: &QCheckBox::toggled, context: this, slot: &KCheckAccelerators::slotDisableCheck); |
130 | layout->addWidget(disableAutoCheck); |
131 | } |
132 | QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, drklash); |
133 | layout->addWidget(buttonBox); |
134 | connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, context: drklash, slot: &QDialog::close); |
135 | if (disableAutoCheck) { |
136 | disableAutoCheck->setFocus(); |
137 | } else { |
138 | drklash_view->setFocus(); |
139 | } |
140 | } |
141 | |
142 | void KCheckAccelerators::slotDisableCheck(bool on) |
143 | { |
144 | autoCheck = !on; |
145 | if (!on) { |
146 | autoCheckSlot(); |
147 | } |
148 | } |
149 | |
150 | void KCheckAccelerators::checkAccelerators(bool automatic) |
151 | { |
152 | QWidget *actWin = qApp->activeWindow(); |
153 | if (!actWin) { |
154 | return; |
155 | } |
156 | |
157 | KAcceleratorManager::manage(widget: actWin); |
158 | QString a; |
159 | QString c; |
160 | QString r; |
161 | KAcceleratorManager::last_manage(added&: a, changed&: c, removed&: r); |
162 | |
163 | if (automatic) { // for now we only show dialogs on F12 checks |
164 | return; |
165 | } |
166 | |
167 | if (c.isEmpty() && r.isEmpty() && (automatic || a.isEmpty())) { |
168 | return; |
169 | } |
170 | |
171 | QString s; |
172 | |
173 | if (!c.isEmpty()) { |
174 | s += i18n("<h2>Accelerators changed</h2>" ) |
175 | + QLatin1String( |
176 | "<table " |
177 | "border><tr><th><b>%1</b></th><th><b>%2</b></th></tr>%3</table>" ) |
178 | .arg(i18n("Old Text" ), i18n("New Text" ), args&: c); |
179 | } |
180 | |
181 | if (!r.isEmpty()) { |
182 | s += i18n("<h2>Accelerators removed</h2>" ) + QLatin1String("<table border><tr><th><b>%1</b></th></tr>%2</table>" ).arg(i18n("Old Text" ), args&: r); |
183 | } |
184 | |
185 | if (!a.isEmpty()) { |
186 | s += i18n("<h2>Accelerators added (just for your info)</h2>" ) |
187 | + QLatin1String("<table border><tr><th><b>%1</b></th></tr>%2</table>" ).arg(i18n("New Text" ), args&: a); |
188 | } |
189 | |
190 | createDialog(actWin, automatic); |
191 | drklash_view->setHtml(s); |
192 | drklash->show(); |
193 | drklash->raise(); |
194 | |
195 | // dlg will be destroyed before returning |
196 | } |
197 | |
198 | void KCheckAccelerators::initiateIfNeeded() |
199 | { |
200 | static QPointer<KCheckAccelerators> checker; |
201 | if (checker) { |
202 | return; |
203 | } |
204 | |
205 | KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Development" )); |
206 | QString sKey = cg.readEntry(key: "CheckAccelerators" ).trimmed(); |
207 | int key = 0; |
208 | if (!sKey.isEmpty()) { |
209 | QList<QKeySequence> cuts = QKeySequence::listFromString(str: sKey); |
210 | if (!cuts.isEmpty()) { |
211 | key = cuts.first()[0].toCombined(); |
212 | } |
213 | } |
214 | const bool autoCheck = cg.readEntry(key: "AutoCheckAccelerators" , defaultValue: true); |
215 | if (key == 0 && !autoCheck) { |
216 | return; |
217 | } |
218 | |
219 | checker = new KCheckAccelerators(qApp, key, autoCheck); |
220 | } |
221 | |
222 | #include "moc_kcheckaccelerators.cpp" |
223 | |