KXmlGui

kcheckaccelerators.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <[email protected]>
4  SPDX-FileCopyrightText: 1998, 1999, 2000 KDE Team
5  SPDX-FileCopyrightText: 2008 Nick Shaforostoff <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8  */
9 
10 #include "kcheckaccelerators.h"
11 
12 #include <QApplication>
13 #include <QCheckBox>
14 #include <QDialog>
15 #include <QShortcutEvent>
16 #include <QMouseEvent>
17 #include <QPushButton>
18 #include <QTabBar>
19 #include <QTextBrowser>
20 #include <QChar>
21 #include <QLabel>
22 #include <QComboBox>
23 #include <QGroupBox>
24 #include <QClipboard>
25 #include <QProcess>
26 #include <QDialogButtonBox>
27 #include <QAction>
28 #include <QMenu>
29 #include <QVBoxLayout>
30 
31 #include <kconfig.h>
32 #include <kconfiggroup.h>
33 #include <ksharedconfig.h>
34 #include <klocalizedstring.h>
35 #include <kacceleratormanager.h>
36 
37 class KCheckAcceleratorsInitializer : public QObject
38 {
39  Q_OBJECT
40 public:
41  explicit KCheckAcceleratorsInitializer(QObject *parent = nullptr)
42  : QObject(parent)
43  {
44  }
45 
46 public Q_SLOTS:
47  void initiateIfNeeded()
48  {
49  KConfigGroup cg(KSharedConfig::openConfig(), "Development");
50  QString sKey = cg.readEntry("CheckAccelerators").trimmed();
51  int key = 0;
52  if (!sKey.isEmpty()) {
54  if (!cuts.isEmpty()) {
55  key = cuts.first()[0];
56  }
57  }
58  const bool autoCheck = cg.readEntry("AutoCheckAccelerators", true);
59  const bool copyWidgetText = cg.readEntry("CopyWidgetText", false);
60  if (!copyWidgetText && key == 0 && !autoCheck) {
61  deleteLater();
62  return;
63  }
64 
65  new KCheckAccelerators(qApp, key, autoCheck, copyWidgetText);
66  deleteLater();
67  }
68 };
69 
70 static 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 part
93  // 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 loaded
101  // through plasma-integration instead of being linked to the app (i.e. QtCreator vs Okular)
102  // For apps that don't link directly to KXmlGui do not do the accelerator magic
103  doCheckAccelerators = false;
104  return;
105  }
106 
107  KCheckAcceleratorsInitializer *initializer = new KCheckAcceleratorsInitializer(app);
108  // Call initiateIfNeeded once we're in the event loop
109  // This is to prevent using KSharedConfig before main() can set the app name
110  QMetaObject::invokeMethod(initializer, "initiateIfNeeded", Qt::QueuedConnection);
111 }
112 
113 Q_COREAPP_STARTUP_FUNCTION(startupFunc)
114 
115 KCheckAccelerators::KCheckAccelerators(QObject *parent, int key_, bool autoCheck_, bool copyWidgetText_)
116  : QObject(parent)
117  , key(key_)
118  , block(false)
119  , autoCheck(autoCheck_)
120  , copyWidgetText(copyWidgetText_)
121  , drklash(nullptr)
122 {
123  setObjectName(QStringLiteral("kapp_accel_filter"));
124 
125  KConfigGroup cg(KSharedConfig::openConfig(), "Development");
126  alwaysShow = cg.readEntry("AlwaysShowCheckAccelerators", false);
127  copyWidgetTextCommand = cg.readEntry("CopyWidgetTextCommand", QString());
128 
129  parent->installEventFilter(this);
130  connect(&autoCheckTimer, &QTimer::timeout, this, &KCheckAccelerators::autoCheckSlot);
131 }
132 
134 {
135  if (block) {
136  return false;
137  }
138 
139  switch (e->type()) { // just simplify debuggin
141  if (key && (static_cast<QKeyEvent *>(e)->key() == key)) {
142  block = true;
143  checkAccelerators(false);
144  block = false;
145  e->accept();
146  return true;
147  }
148  break;
149  case QEvent::ChildAdded:
151  // Only care about widgets; this also avoids starting the timer in other 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  break;
166  //case QEvent::MouseButtonDblClick:
168  if (copyWidgetText && static_cast<QMouseEvent *>(e)->button() == Qt::MidButton) {
169  //kWarning()<<"obj"<<obj;
170  QWidget *w = static_cast<QWidget *>(obj)->childAt(static_cast<QMouseEvent *>(e)->pos());
171  if (!w) {
172  w = static_cast<QWidget *>(obj);
173  }
174  if (!w) {
175  return false;
176  }
177  //kWarning()<<"MouseButtonDblClick"<<w;
178  QString text;
179  if (qobject_cast<QLabel *>(w)) {
180  text = static_cast<QLabel *>(w)->text();
181  } else if (qobject_cast<QAbstractButton *>(w)) {
182  text = static_cast<QAbstractButton *>(w)->text();
183  } else if (qobject_cast<QComboBox *>(w)) {
184  text = static_cast<QComboBox *>(w)->currentText();
185  } else if (qobject_cast<QTabBar *>(w)) {
186  text = static_cast<QTabBar *>(w)->tabText(static_cast<QTabBar *>(w)->tabAt(static_cast<QMouseEvent *>(e)->pos()));
187  } else if (qobject_cast<QGroupBox *>(w)) {
188  text = static_cast<QGroupBox *>(w)->title();
189  } else if (qobject_cast<QMenu *>(obj)) {
190  QAction *a = static_cast<QMenu *>(obj)->actionAt(static_cast<QMouseEvent *>(e)->pos());
191  if (!a) {
192  return false;
193  }
194  text = a->text();
195  if (text.isEmpty()) {
196  text = a->iconText();
197  }
198  }
199  if (text.isEmpty()) {
200  return false;
201  }
202 
203  if (static_cast<QMouseEvent *>(e)->modifiers() == Qt::ControlModifier) {
204  text.remove(QChar::fromLatin1('&'));
205  }
206 
207  //kWarning()<<KGlobal::activeComponent().catalogName()<<text;
208  if (copyWidgetTextCommand.isEmpty()) {
209  QClipboard *clipboard = QApplication::clipboard();
210  clipboard->setText(text);
211  } else {
212  QProcess *script = new QProcess(this);
213  script->start(copyWidgetTextCommand.arg(text, QFile::decodeName(KLocalizedString::applicationDomain())), QStringList());
214  connect(script, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
215  script, &QObject::deleteLater);
216  }
217  e->accept();
218  return true;
219 
220  //kWarning()<<"MouseButtonDblClick"<<static_cast<QWidget*>(obj)->childAt(static_cast<QMouseEvent*>(e)->globalPos());
221  }
222  return false;
223  case QEvent::Timer:
224  case QEvent::MouseMove:
225  case QEvent::Paint:
226  return false;
227  default:
228  // qCDebug(DEBUG_KXMLGUI) << "KCheckAccelerators::eventFilter " << e->type() << " " << autoCheck;
229  break;
230  }
231  return false;
232 }
233 
234 void KCheckAccelerators::autoCheckSlot()
235 {
236  if (block) {
237  autoCheckTimer.setSingleShot(true);
238  autoCheckTimer.start(20);
239  return;
240  }
241  block = true;
242  checkAccelerators(!alwaysShow);
243  block = false;
244 }
245 
246 void KCheckAccelerators::createDialog(QWidget *actWin, bool automatic)
247 {
248  if (drklash) {
249  return;
250  }
251 
252  drklash = new QDialog(actWin);
253  drklash->setAttribute(Qt::WA_DeleteOnClose);
254  drklash->setObjectName(QStringLiteral("kapp_accel_check_dlg"));
255  drklash->setWindowTitle(i18nc("@title:window", "Dr. Klash' Accelerator Diagnosis"));
256  drklash->resize(500, 460);
257  QVBoxLayout *layout = new QVBoxLayout(drklash);
258  drklash_view = new QTextBrowser(drklash);
259  layout->addWidget(drklash_view);
260  QCheckBox *disableAutoCheck = nullptr;
261  if (automatic) {
262  disableAutoCheck = new QCheckBox(i18nc("@option:check", "Disable automatic checking"), drklash);
263  connect(disableAutoCheck, &QCheckBox::toggled, this, &KCheckAccelerators::slotDisableCheck);
264  layout->addWidget(disableAutoCheck);
265  }
266  QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, drklash);
267  layout->addWidget(buttonBox);
268  connect(buttonBox, &QDialogButtonBox::rejected, drklash, &QDialog::close);
269  if (disableAutoCheck) {
270  disableAutoCheck->setFocus();
271  } else {
272  drklash_view->setFocus();
273  }
274 }
275 
276 void KCheckAccelerators::slotDisableCheck(bool on)
277 {
278  autoCheck = !on;
279  if (!on) {
280  autoCheckSlot();
281  }
282 }
283 
284 void KCheckAccelerators::checkAccelerators(bool automatic)
285 {
286  QWidget *actWin = qApp->activeWindow();
287  if (!actWin) {
288  return;
289  }
290 
292  QString a, c, r;
294 
295  if (automatic) { // for now we only show dialogs on F12 checks
296  return;
297  }
298 
299  if (c.isEmpty() && r.isEmpty() && (automatic || a.isEmpty())) {
300  return;
301  }
302 
303  QString s;
304 
305  if (! c.isEmpty()) {
306  s += i18n("<h2>Accelerators changed</h2>") +
307  QLatin1String("<table border><tr><th><b>") +
308  i18n("Old Text") +
309  QLatin1String("</b></th><th><b>") +
310  i18n("New Text") +
311  QLatin1String("</b></th></tr>") +
312  c +
313  QLatin1String("</table>");
314  }
315 
316  if (! r.isEmpty()) {
317  s += i18n("<h2>Accelerators removed</h2>") +
318  QLatin1String("<table border><tr><th><b>") +
319  i18n("Old Text") +
320  QLatin1String("</b></th></tr>") +
321  r +
322  QLatin1String("</table>");
323  }
324 
325  if (! a.isEmpty()) {
326  s += i18n("<h2>Accelerators added (just for your info)</h2>") +
327  QLatin1String("<table border><tr><th><b>") +
328  i18n("New Text") +
329  QLatin1String("</b></th></tr>") +
330  a +
331  QLatin1String("</table>");
332  }
333 
334  createDialog(actWin, automatic);
335  drklash_view->setHtml(s);
336  drklash->show();
337  drklash->raise();
338 
339  // dlg will be destroyed before returning
340 }
341 
342 #include "kcheckaccelerators.moc"
bool close()
ControlModifier
ShortcutOverride
QEvent::Type type() const const
QList< QKeySequence > listFromString(const QString &str, QKeySequence::SequenceFormat format)
MidButton
QString & remove(int position, int n)
static void last_manage(QString &added, QString &changed, QString &removed)
void timeout()
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
Q_OBJECTQ_OBJECT
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setFocus()
QChar fromLatin1(char c)
bool isEmpty() const const
void setObjectName(const QString &name)
bool isEmpty() const const
QString trimmed() const const
static QByteArray applicationDomain()
void finished(int exitCode)
QCoreApplication * instance()
void deleteLater()
T & first()
void accept()
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
WA_DeleteOnClose
QString i18n(const char *text, const TYPE &arg...)
void toggled(bool checked)
QAbstractEventDispatcher * eventDispatcher()
void setText(const QString &text, QClipboard::Mode mode)
bool isWidgetType() const const
Q_SLOTSQ_SLOTS
QueuedConnection
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
QClipboard * clipboard()
static void manage(QWidget *widget, bool programmers_mode=false)
bool eventFilter(QObject *, QEvent *e) override
Re-implemented to filter the parent&#39;s events.
void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode)
QString decodeName(const QByteArray &localFileName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Aug 12 2020 22:50:46 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.