KXmlGui

kxmlguiwindow.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2000 Reginald Stadlbauer <[email protected]>
4  SPDX-FileCopyrightText: 1997 Stephan Kulow <[email protected]>
5  SPDX-FileCopyrightText: 1997-2000 Sven Radej <[email protected]>
6  SPDX-FileCopyrightText: 1997-2000 Matthias Ettrich <[email protected]>
7  SPDX-FileCopyrightText: 1999 Chris Schlaeger <[email protected]>
8  SPDX-FileCopyrightText: 2002 Joseph Wenninger <[email protected]>
9  SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <[email protected]>
10 
11  SPDX-License-Identifier: LGPL-2.0-only
12 */
13 
14 #include "kxmlguiwindow.h"
15 #include "debug.h"
16 
17 #include "kactioncollection.h"
18 #include "kmainwindow_p.h"
19 #include "kmessagebox.h"
20 #ifdef QT_DBUS_LIB
21 #include "kmainwindowiface_p.h"
22 #endif
23 #include "kedittoolbar.h"
24 #include "khelpmenu.h"
25 #include "ktoolbar.h"
26 #include "ktoolbarhandler_p.h"
27 #include "kxmlguifactory.h"
28 
29 #ifdef QT_DBUS_LIB
30 #include <QDBusConnection>
31 #endif
32 #include <QDomDocument>
33 #include <QEvent>
34 #include <QList>
35 #include <QMenuBar>
36 #include <QStatusBar>
37 #include <QWidget>
38 
39 #include <KAboutData>
40 #include <KConfig>
41 #include <KConfigGroup>
42 #include <KLocalizedString>
43 #include <KSharedConfig>
44 #include <KStandardAction>
45 #include <KToggleAction>
46 
47 #include <cctype>
48 #include <cstdlib>
49 
50 class KXmlGuiWindowPrivate : public KMainWindowPrivate
51 {
52 public:
53  void slotFactoryMakingChanges(bool b)
54  {
55  // While the GUI factory is adding/removing clients,
56  // don't let KMainWindow think those are changes made by the user
57  // #105525
58  letDirtySettings = !b;
59  }
60 
61  bool showHelpMenu : 1;
63 
64  KDEPrivate::ToolBarHandler *toolBarHandler;
65  KToggleAction *showStatusBarAction;
66  QPointer<KEditToolBar> toolBarEditor;
67  KXMLGUIFactory *factory;
68 };
69 
71  : KMainWindow(*new KXmlGuiWindowPrivate, parent, f)
72  , KXMLGUIBuilder(this)
73 {
74  Q_D(KXmlGuiWindow);
75  d->showHelpMenu = true;
76  d->toolBarHandler = nullptr;
77  d->showStatusBarAction = nullptr;
78  d->factory = nullptr;
79 #ifdef QT_DBUS_LIB
80  new KMainWindowInterface(this);
81 #endif
82 }
83 
85 {
86  Q_D(KXmlGuiWindow);
87  if (!d->toolBarHandler) {
88  return nullptr;
89  }
90 
91  return d->toolBarHandler->toolBarMenuAction();
92 }
93 
95 {
96  Q_D(KXmlGuiWindow);
97  if (d->toolBarHandler) {
98  d->toolBarHandler->setupActions();
99  }
100 }
101 
103 {
104  Q_D(KXmlGuiWindow);
105  delete d->factory;
106 }
107 
109 {
110  bool ret = KMainWindow::event(ev);
111  if (ev->type() == QEvent::Polish) {
112 #ifdef QT_DBUS_LIB
113  /* clang-format off */
114  constexpr auto opts = QDBusConnection::ExportScriptableSlots
119  /* clang-format on */
121 #endif
122  }
123  return ret;
124 }
125 
126 void KXmlGuiWindow::setHelpMenuEnabled(bool showHelpMenu)
127 {
128  Q_D(KXmlGuiWindow);
129  d->showHelpMenu = showHelpMenu;
130 }
131 
133 {
134  Q_D(const KXmlGuiWindow);
135  return d->showHelpMenu;
136 }
137 
138 KXMLGUIFactory *KXmlGuiWindow::guiFactory()
139 {
140  Q_D(KXmlGuiWindow);
141  if (!d->factory) {
142  d->factory = new KXMLGUIFactory(this, this);
143  connect(d->factory, &KXMLGUIFactory::makingChanges, this, [d](bool state) {
144  d->slotFactoryMakingChanges(state);
145  });
146  }
147  return d->factory;
148 }
149 
151 {
152  Q_D(KXmlGuiWindow);
155  if (!d->toolBarEditor) {
156  d->toolBarEditor = new KEditToolBar(guiFactory(), this);
157  d->toolBarEditor->setAttribute(Qt::WA_DeleteOnClose);
159  }
160  d->toolBarEditor->show();
161 }
162 
164 {
165  // createGUI(xmlFile()); // this loses any plugged-in guiclients, so we use remove+add instead.
166 
167  guiFactory()->removeClient(this);
168  guiFactory()->addClient(this);
169 
172 }
173 
175 {
176  setupGUI(QSize(), options, xmlfile);
177 }
178 
179 void KXmlGuiWindow::setupGUI(const QSize &defaultSize, StandardWindowOptions options, const QString &xmlfile)
180 {
181  Q_D(KXmlGuiWindow);
182 
183  if (options & Keys) {
184  // KXMLGUIFactory::configureShortcuts(bool bAllowLetterShortcuts = true, bool bSaveSettings = true)
185  // can't be connected directly to the QAction::triggered(bool) signal
186  auto configureShortcutsSlot = [this]() {
187  guiFactory()->configureShortcuts();
188  };
189 
190  KStandardAction::keyBindings(guiFactory(), configureShortcutsSlot, actionCollection());
191  }
192 
193  if ((options & StatusBar) && statusBar()) {
195  }
196 
197  if (options & ToolBar) {
200  }
201 
202  d->defaultSize = defaultSize;
203 
204  if (options & Create) {
205  createGUI(xmlfile);
206  }
207 
208  if (d->defaultSize.isValid()) {
209  resize(d->defaultSize);
210  } else if (isHidden()) {
211  adjustSize();
212  }
213 
214  if (options & Save) {
215  const KConfigGroup cg(autoSaveConfigGroup());
216  if (cg.isValid()) {
218  } else {
220  }
221  }
222 }
223 void KXmlGuiWindow::createGUI(const QString &xmlfile)
224 {
225  Q_D(KXmlGuiWindow);
226  // disabling the updates prevents unnecessary redraws
227  // setUpdatesEnabled( false );
228 
229  // just in case we are rebuilding, let's remove our old client
230  guiFactory()->removeClient(this);
231 
232  // make sure to have an empty GUI
233  QMenuBar *mb = menuBar();
234  if (mb) {
235  mb->clear();
236  }
237 
238  qDeleteAll(toolBars()); // delete all toolbars
239 
240  // don't build a help menu unless the user ask for it
241  if (d->showHelpMenu) {
242  delete d->helpMenu;
243  // we always want a help menu
244  d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true);
245 
247  QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents);
248  QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis);
249  QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug);
250  QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage);
251  QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp);
252  QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE);
253  QAction *donateAction = d->helpMenu->action(KHelpMenu::menuDonate);
254 
255  if (helpContentsAction) {
256  actions->addAction(helpContentsAction->objectName(), helpContentsAction);
257  }
258  if (whatsThisAction) {
259  actions->addAction(whatsThisAction->objectName(), whatsThisAction);
260  }
261  if (reportBugAction) {
262  actions->addAction(reportBugAction->objectName(), reportBugAction);
263  }
264  if (switchLanguageAction) {
265  actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
266  }
267  if (aboutAppAction) {
268  actions->addAction(aboutAppAction->objectName(), aboutAppAction);
269  }
270  if (aboutKdeAction) {
271  actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
272  }
273  if (donateAction) {
274  actions->addAction(donateAction->objectName(), donateAction);
275  }
276  }
277 
278  const QString windowXmlFile = xmlfile.isNull() ? componentName() + QLatin1String("ui.rc") : xmlfile;
279 
280  // Help beginners who call setXMLFile and then setupGUI...
281  if (!xmlFile().isEmpty() && xmlFile() != windowXmlFile) {
282  qCWarning(DEBUG_KXMLGUI) << "You called setXMLFile(" << xmlFile() << ") and then createGUI or setupGUI,"
283  << "which also calls setXMLFile and will overwrite the file you have previously set.\n"
284  << "You should call createGUI(" << xmlFile() << ") or setupGUI(<options>," << xmlFile() << ") instead.";
285  }
286 
287  // we always want to load in our global standards file
289 
290  // now, merge in our local xml file.
291  setXMLFile(windowXmlFile, true);
292 
293  // make sure we don't have any state saved already
295 
296  // do the actual GUI building
297  guiFactory()->reset();
298  guiFactory()->addClient(this);
299 
301 
302  // setUpdatesEnabled( true );
303 }
304 
306 {
307  stateChanged(newstate, KXMLGUIClient::StateNoReverse);
308 }
309 
310 void KXmlGuiWindow::slotStateChanged(const QString &newstate, bool reverse)
311 {
312  stateChanged(newstate, reverse ? KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse);
313 }
314 
316 {
317  Q_D(KXmlGuiWindow);
318  if (enable) {
319  if (d->toolBarHandler) {
320  return;
321  }
322 
323  d->toolBarHandler = new KDEPrivate::ToolBarHandler(this);
324 
325  if (factory()) {
326  factory()->addClient(d->toolBarHandler);
327  }
328  } else {
329  if (!d->toolBarHandler) {
330  return;
331  }
332 
333  if (factory()) {
334  factory()->removeClient(d->toolBarHandler);
335  }
336 
337  delete d->toolBarHandler;
338  d->toolBarHandler = nullptr;
339  }
340 }
341 
342 bool KXmlGuiWindow::isStandardToolBarMenuEnabled() const
343 {
344  Q_D(const KXmlGuiWindow);
345  return (d->toolBarHandler);
346 }
347 
349 {
350  Q_D(KXmlGuiWindow);
351  if (!d->showStatusBarAction) {
353  QStatusBar *sb = statusBar(); // Creates statusbar if it doesn't exist already.
354  connect(d->showStatusBarAction, &QAction::toggled, sb, &QWidget::setVisible);
355  d->showStatusBarAction->setChecked(sb->isHidden());
356  } else {
357  // If the language has changed, we'll need to grab the new text and whatsThis
358  QAction *tmpStatusBar = KStandardAction::showStatusbar(nullptr, nullptr, nullptr);
359  d->showStatusBarAction->setText(tmpStatusBar->text());
360  d->showStatusBarAction->setWhatsThis(tmpStatusBar->whatsThis());
361  delete tmpStatusBar;
362  }
363 }
364 
365 void KXmlGuiWindow::finalizeGUI(bool /*force*/)
366 {
367  // FIXME: this really needs to be removed with a code more like the one we had on KDE3.
368  // what we need to do here is to position correctly toolbars so they don't overlap.
369  // Also, take in count plugins could provide their own toolbars and those also need to
370  // be restored.
371  if (autoSaveSettings() && autoSaveConfigGroup().isValid()) {
373  }
374 }
375 
377 {
378  Q_D(KXmlGuiWindow);
380  QStatusBar *sb = findChild<QStatusBar *>();
381  if (sb && d->showStatusBarAction) {
382  d->showStatusBarAction->setChecked(!sb->isHidden());
383  }
384 }
385 
386 // TODO KF6: change it to "using KXMLGUIBuilder::finalizeGUI;" in the header
387 // and remove the reimplementation
388 void KXmlGuiWindow::finalizeGUI(KXMLGUIClient *client)
389 {
390  KXMLGUIBuilder::finalizeGUI(client);
391 }
392 
394 {
395  QMap<QString, QAction *> shortcuts;
396  QAction *editCutAction = actionCollection()->action(QStringLiteral("edit_cut"));
397  QAction *deleteFileAction = actionCollection()->action(QStringLiteral("deletefile"));
398  const auto actions = actionCollection()->actions();
399  for (QAction *action : actions) {
400  if (action->isEnabled()) {
401  const auto actionShortcuts = action->shortcuts();
402  for (const QKeySequence &shortcut : actionShortcuts) {
403  if (shortcut.isEmpty()) {
404  continue;
405  }
406  const QString portableShortcutText = shortcut.toString();
407  const QAction *existingShortcutAction = shortcuts.value(portableShortcutText);
408  if (existingShortcutAction) {
409  // If the shortcut is already in use we give a warning, so that hopefully the developer will find it
410  // There is one exception, if the conflicting shortcut is a non primary shortcut of "edit_cut"
411  // and "deleteFileAction" is the other action since Shift+Delete is used for both in our default code
412  bool showWarning = true;
413  if ((action == editCutAction && existingShortcutAction == deleteFileAction)
414  || (action == deleteFileAction && existingShortcutAction == editCutAction)) {
415  QList<QKeySequence> editCutActionShortcuts = editCutAction->shortcuts();
416  if (editCutActionShortcuts.indexOf(shortcut) > 0) // alternate shortcut
417  {
418  editCutActionShortcuts.removeAll(shortcut);
419  editCutAction->setShortcuts(editCutActionShortcuts);
420 
421  showWarning = false;
422  }
423  }
424 
425  if (showWarning) {
427  const QString existingShortcutActionName = KLocalizedString::removeAcceleratorMarker(existingShortcutAction->text());
428  QString dontShowAgainString = existingShortcutActionName + actionName + shortcut.toString();
429  dontShowAgainString.remove(QLatin1Char('\\'));
431  i18n("There are two actions (%1, %2) that want to use the same shortcut (%3). This is most probably a bug. "
432  "Please report it in <a href='https://bugs.kde.org'>bugs.kde.org</a>",
433  existingShortcutActionName,
434  actionName,
435  shortcut.toString(QKeySequence::NativeText)),
436  i18n("Ambiguous Shortcuts"),
437  dontShowAgainString,
439  }
440  } else {
441  shortcuts.insert(portableShortcutText, action);
442  }
443  }
444  }
445  }
446 }
447 
448 #include "moc_kxmlguiwindow.cpp"
QAction * toolBarMenuAction()
Returns a pointer to the mainwindows action responsible for the toolbars menu.
bool event(QEvent *event) override
Reimplemented to catch QEvent::Polish in order to adjust the object name if needed, once all constructor code for the main window has run.
virtual void configureToolbars()
Show a standard configure toolbar dialog.
adds action to show/hide the statusbar if the statusbar exists.
void resize(int w, int h)
A container for a set of QAction objects.
QEvent::Type type() const const
void createGUI(const QString &xmlfile=QString())
Create a GUI given a local XML file.
bool isHidden() const const
void setSettingsDirty()
Tell the main window that it should save its settings when being closed.
virtual KActionCollection * actionCollection() const
Retrieves the entire action collection for the GUI client.
A KXMLGUIClient can be used with KXMLGUIFactory to create a GUI from actions and an XML document...
Definition: kxmlguiclient.h:39
bool event(QEvent *event) override
Reimplemented to catch QEvent::Polish in order to adjust the object name if needed, once all constructor code for the main window has run.
static KAboutData applicationData()
void setHelpMenuEnabled(bool showHelpMenu=true)
Enables the build of a standard help menu when calling createGUI() or setupGUI(). ...
QMenuBar * menuBar() const const
void removeClient(KXMLGUIClient *client)
Removes the GUI described by the client, by unplugging all provided actions and removing all owned co...
static QString removeAcceleratorMarker(const QString &label)
void clear()
KToggleAction * showStatusbar(const QObject *recvr, const char *slot, QObject *parent)
QStatusBar * statusBar() const const
void addClient(KXMLGUIClient *client)
Creates the GUI described by the QDomDocument of the client, using the client&#39;s actions, and merges it with the previously created GUI.
bool registerObject(const QString &path, QObject *object, QDBusConnection::RegisterOptions options)
QList< QKeySequence > shortcuts() const const
void loadStandardsXmlFile()
Load the ui_standards.rc file.
virtual void setVisible(bool visible)
Q_INVOKABLE QAction * addAction(const QString &name, QAction *action)
Add an action under the given name to the collection.
virtual void setXMLFile(const QString &file, bool merge=false, bool setXMLDoc=true)
Sets the name of the rc file containing the XML for the part.
QDBusConnection sessionBus()
KXMLGUIFactory * factory() const
Retrieves a pointer to the KXMLGUIFactory this client is associated with (will return nullptr if the ...
virtual QString componentName() const
virtual QString xmlFile() const
This will return the name of the XML file as set by setXMLFile().
bool isValid() const
virtual void slotStateChanged(const QString &newstate)
Apply a state change.
~KXmlGuiWindow() override
Destructor.
void reset()
Use this method to free all memory allocated by the KXMLGUIFactory.
void adjustSize()
void toggled(bool checked)
bool isNull() const const
int indexOf(const T &value, int from) const const
void checkAmbiguousShortcuts()
Checks if there are actions using the same shortcut.
void setShortcuts(const QList< QKeySequence > &shortcuts)
Standard KDE help menu with dialog boxes.
Definition: khelpmenu.h:108
Top level main window with predefined action layout.
Definition: kxmlguiwindow.h:46
virtual void stateChanged(const QString &newstate, ReverseStateChange reverse=StateNoReverse)
Actions can collectively be assigned a "State".
void newToolBarConfig()
Signal emitted when &#39;apply&#39; or &#39;ok&#39; is clicked or toolbars were reset.
KXmlGuiWindow(QWidget *parent=nullptr, Qt::WindowFlags f=Qt::WindowFlags())
Construct a main window.
adds action to show/hide the toolbar(s) and adds action to configure the toolbar(s).
Top level main window.
Definition: kmainwindow.h:92
Implements the creation of the GUI (menubar, menus and toolbars) as requested by the GUI factory...
void setupToolbarMenuActions()
auto-saves (and loads) the toolbar/menubar/statusbar settings and window size using the default name...
A dialog used to customize or configure toolbars.
Definition: kedittoolbar.h:62
bool isEmpty() const const
int removeAll(const T &value)
QList< QAction * > actions() const
Returns the list of QActions which belong to this action collection.
void createStandardStatusBarAction()
Sets whether KMainWindow should provide a menu that allows showing/hiding of the statusbar ( using KS...
bool autoSaveSettings() const
void applyMainWindowSettings(const KConfigGroup &config) override
Read settings for statusbar, menubar and toolbar from their respective groups in the config file and ...
void makingChanges(bool)
Emitted when the factory is currently making changes to the GUI, i.e.
adds action to show the key configure action.
void setupGUI(StandardWindowOptions options=Default, const QString &xmlfile=QString())
Configures the current windows and its actions in the typical KDE fashion.
KXMLGUIFactory, together with KXMLGUIClient objects, can be used to create a GUI of container widgets...
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
KREPORT_EXPORT QPageSize::PageSizeId defaultSize()
WA_DeleteOnClose
QString i18n(const char *text, const TYPE &arg...)
void setAutoSaveSettings(const QString &groupName=QStringLiteral("MainWindow"), bool saveWindowSize=true)
Call this to enable "auto-save" of toolbar/menubar/statusbar settings (and optionally window size)...
QAction * action(const char *name) const
Retrieves an action of the client by name.
QAction * keyBindings(const QObject *recvr, const char *slot, QObject *parent)
virtual void saveNewToolbarConfig()
Rebuilds the GUI after KEditToolBar changed the toolbar layout.
void setStandardToolBarMenuEnabled(bool enable)
Sets whether KMainWindow should provide a menu that allows showing/hiding the available toolbars ( us...
QString dbusName() const
Returns the path under which this window&#39;s D-Bus object is exported.
QAction * action(int index) const
Return the QAction* at position index in the action collection.
QMap::iterator insert(const Key &key, const T &value)
void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
int configureShortcuts(bool bAllowLetterShortcuts=true, bool bSaveSettings=true)
Show a standard configure shortcut for every action in this factory.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QList< QAction * > actions() const const
calls createGUI() once ToolBar, Keys and Statusbar have been taken care of.
bool isHelpMenuEnabled() const
Return true when the help menu is enabled.
typedef WindowFlags
void saveMainWindowSettings(KConfigGroup &config)
Save settings for statusbar, menubar and toolbar to their respective groups in the config group confi...
bool isEnabled() const const
KConfigGroup autoSaveConfigGroup() const
virtual void applyMainWindowSettings(const KConfigGroup &config)
Read settings for statusbar, menubar and toolbar from their respective groups in the config file and ...
QList< KToolBar * > toolBars() const
const T value(const Key &key, const T &defaultValue) const const
void setXMLGUIBuildDocument(const QDomDocument &doc)
QAction * configureToolbars(const QObject *recvr, const char *slot, QObject *parent)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu May 13 2021 22:50:35 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.