KXmlGui

kxmlguiwindow.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Reginald Stadlbauer <reggie@kde.org>
4 SPDX-FileCopyrightText: 1997 Stephan Kulow <coolo@kde.org>
5 SPDX-FileCopyrightText: 1997-2000 Sven Radej <radej@kde.org>
6 SPDX-FileCopyrightText: 1997-2000 Matthias Ettrich <ettrich@kde.org>
7 SPDX-FileCopyrightText: 1999 Chris Schlaeger <cs@kde.org>
8 SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
9 SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
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>
20#include <kcommandbar.h>
21#ifdef WITH_QTDBUS
22#include "kmainwindowiface_p.h"
23#endif
24#include "kedittoolbar.h"
25#include "khelpmenu.h"
26#include "ktoolbar.h"
27#include "ktoolbarhandler_p.h"
28#include "kxmlguifactory.h"
29
30#ifdef WITH_QTDBUS
31#include <QDBusConnection>
32#endif
33#include <QDomDocument>
34#include <QEvent>
35#include <QList>
36#include <QMenuBar>
37#include <QStatusBar>
38#include <QWidget>
39
40#include <KAboutData>
41#include <KCommandBar>
42#include <KConfig>
43#include <KConfigGroup>
44#include <KLocalizedString>
45#include <KSharedConfig>
46#include <KStandardActions>
47#include <KToggleAction>
48
49#include <cctype>
50#include <cstdlib>
51
52/**
53 * A helper function that takes a list of KActionCollection* and converts it
54 * to KCommandBar::ActionGroup
55 */
56static QList<KCommandBar::ActionGroup> actionCollectionToActionGroup(const std::vector<KActionCollection *> &actionCollections)
57{
58 using ActionGroup = KCommandBar::ActionGroup;
59
60 QList<ActionGroup> actionList;
61 actionList.reserve(actionCollections.size());
62
63 for (const auto collection : actionCollections) {
64 const QList<QAction *> collectionActions = collection->actions();
65 const QString componentName = collection->componentDisplayName();
66
67 ActionGroup ag;
68 ag.name = componentName;
69 ag.actions.reserve(collection->count());
70 for (const auto action : collectionActions) {
71 /**
72 * If this action is a menu, fetch all its child actions
73 * and skip the menu action itself
74 */
75 if (QMenu *menu = action->menu()) {
76 const QList<QAction *> menuActions = menu->actions();
77
78 ActionGroup menuActionGroup;
79 menuActionGroup.name = KLocalizedString::removeAcceleratorMarker(action->text());
80 menuActionGroup.actions.reserve(menuActions.size());
81 for (const auto mAct : menuActions) {
82 if (mAct) {
83 menuActionGroup.actions.append(mAct);
84 }
85 }
86
87 /**
88 * If there were no actions in the menu, we
89 * add the menu to the list instead because it could
90 * be that the actions are created on demand i.e., aboutToShow()
91 */
92 if (!menuActions.isEmpty()) {
93 actionList.append(menuActionGroup);
94 continue;
95 }
96 }
97
98 if (action && !action->text().isEmpty()) {
99 ag.actions.append(action);
100 }
101 }
102 actionList.append(ag);
103 }
104 return actionList;
105}
106
107static void getActionCollections(KXMLGUIClient *client, std::vector<KActionCollection *> &actionCollections)
108{
109 if (!client) {
110 return;
111 }
112
113 auto actionCollection = client->actionCollection();
114 if (actionCollection && !actionCollection->isEmpty()) {
115 actionCollections.push_back(client->actionCollection());
116 }
117
118 const QList<KXMLGUIClient *> childClients = client->childClients();
119 for (auto child : childClients) {
120 getActionCollections(child, actionCollections);
121 }
122}
123
124class KXmlGuiWindowPrivate : public KMainWindowPrivate
125{
126public:
127 void slotFactoryMakingChanges(bool b)
128 {
129 // While the GUI factory is adding/removing clients,
130 // don't let KMainWindow think those are changes made by the user
131 // #105525
132 letDirtySettings = !b;
133 }
134
135 bool commandBarEnabled = true;
136 // Last executed actions in command bar
137 QList<QString> lastExecutedActions;
138
139 bool showHelpMenu : 1;
140 QSize defaultSize;
141
142 KDEPrivate::ToolBarHandler *toolBarHandler;
143 KToggleAction *showStatusBarAction;
144 QPointer<KEditToolBar> toolBarEditor;
145 KXMLGUIFactory *factory;
146};
147
149 : KMainWindow(*new KXmlGuiWindowPrivate, parent, flags)
150 , KXMLGUIBuilder(this)
151{
153 d->showHelpMenu = true;
154 d->toolBarHandler = nullptr;
155 d->showStatusBarAction = nullptr;
156 d->factory = nullptr;
157#ifdef WITH_QTDBUS
158 new KMainWindowInterface(this);
159#endif
160
161 /*
162 * Set up KCommandBar launcher action
163 */
164 auto a = actionCollection()->addAction(QStringLiteral("open_kcommand_bar"), this, [this] {
165 /*
166 * Do nothing when command bar is disabled
167 */
168 if (!isCommandBarEnabled()) {
169 return;
170 }
171
172 auto ac = actionCollection();
173 if (!ac) {
174 return;
175 }
176
177 auto kc = new KCommandBar(this);
178 std::vector<KActionCollection *> actionCollections;
179 const auto clients = guiFactory()->clients();
180 actionCollections.reserve(clients.size());
181
182 // Grab action collections recursively
183 for (const auto &client : clients) {
184 getActionCollections(client, actionCollections);
185 }
186
187 kc->setActions(actionCollectionToActionGroup(actionCollections));
188 kc->show();
189 });
190 a->setIcon(QIcon::fromTheme(QStringLiteral("search")));
191 a->setText(i18n("Find Action…"));
193}
194
196{
198 if (!d->toolBarHandler) {
199 return nullptr;
200 }
201
202 return d->toolBarHandler->toolBarMenuAction();
203}
204
206{
208 if (d->toolBarHandler) {
209 d->toolBarHandler->setupActions();
210 }
211}
212
214{
216 delete d->factory;
217}
218
220{
221 bool ret = KMainWindow::event(ev);
222 if (ev->type() == QEvent::Polish) {
223#ifdef WITH_QTDBUS
224 /* clang-format off */
225 constexpr auto opts = QDBusConnection::ExportScriptableSlots
230 /* clang-format on */
232#endif
233 }
234 return ret;
235}
236
238{
240 d->showHelpMenu = showHelpMenu;
241}
242
244{
245 Q_D(const KXmlGuiWindow);
246 return d->showHelpMenu;
247}
248
249KXMLGUIFactory *KXmlGuiWindow::guiFactory()
250{
252 if (!d->factory) {
253 d->factory = new KXMLGUIFactory(this, this);
254 connect(d->factory, &KXMLGUIFactory::makingChanges, this, [d](bool state) {
255 d->slotFactoryMakingChanges(state);
256 });
257 }
258 return d->factory;
259}
260
262{
266 if (!d->toolBarEditor) {
267 d->toolBarEditor = new KEditToolBar(guiFactory(), this);
268 d->toolBarEditor->setAttribute(Qt::WA_DeleteOnClose);
270 }
271 d->toolBarEditor->show();
272}
273
275{
276 // createGUI(xmlFile()); // this loses any plugged-in guiclients, so we use remove+add instead.
277
278 guiFactory()->removeClient(this);
279 guiFactory()->addClient(this);
280
283}
284
286{
287 setupGUI(QSize(), options, xmlfile);
288}
289
290void KXmlGuiWindow::setupGUI(const QSize &defaultSize, StandardWindowOptions options, const QString &xmlfile)
291{
293
294 if (options & Keys) {
295 KStandardActions::keyBindings(guiFactory(), &KXMLGUIFactory::showConfigureShortcutsDialog, actionCollection());
296 }
297
298 if ((options & StatusBar) && statusBar()) {
300 }
301
302 if (options & ToolBar) {
304 KStandardActions::configureToolbars(this, &KXmlGuiWindow::configureToolbars, actionCollection());
305 }
306
307 d->defaultSize = defaultSize;
308
309 if (options & Create) {
310 createGUI(xmlfile);
311 }
312
313 if (d->defaultSize.isValid()) {
314 resize(d->defaultSize);
315 } else if (isHidden()) {
316 adjustSize();
317 }
318
319 if (options & Save) {
321 if (cg.isValid()) {
323 } else {
325 }
326 }
327}
329{
331 // disabling the updates prevents unnecessary redraws
332 // setUpdatesEnabled( false );
333
334 // just in case we are rebuilding, let's remove our old client
335 guiFactory()->removeClient(this);
336
337 // make sure to have an empty GUI
338 QMenuBar *mb = menuBar();
339 if (mb) {
340 mb->clear();
341 }
342
343 qDeleteAll(toolBars()); // delete all toolbars
344
345 // don't build a help menu unless the user ask for it
346 if (d->showHelpMenu) {
347 delete d->helpMenu;
348 // we always want a help menu
349 d->helpMenu = new KHelpMenu(this);
350
352 QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents);
353 QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis);
354 QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug);
355 QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage);
356 QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp);
357 QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE);
358 QAction *donateAction = d->helpMenu->action(KHelpMenu::menuDonate);
359
360 if (helpContentsAction) {
361 actions->addAction(helpContentsAction->objectName(), helpContentsAction);
362 }
363 if (whatsThisAction) {
364 actions->addAction(whatsThisAction->objectName(), whatsThisAction);
365 }
366 if (reportBugAction) {
367 actions->addAction(reportBugAction->objectName(), reportBugAction);
368 }
369 if (switchLanguageAction) {
370 actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
371 }
372 if (aboutAppAction) {
373 actions->addAction(aboutAppAction->objectName(), aboutAppAction);
374 }
375 if (aboutKdeAction) {
376 actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
377 }
378 if (donateAction) {
379 actions->addAction(donateAction->objectName(), donateAction);
380 }
381 }
382
383 const QString windowXmlFile = xmlfile.isNull() ? componentName() + QLatin1String("ui.rc") : xmlfile;
384
385 // Help beginners who call setXMLFile and then setupGUI...
386 if (!xmlFile().isEmpty() && xmlFile() != windowXmlFile) {
387 qCWarning(DEBUG_KXMLGUI) << "You called setXMLFile(" << xmlFile() << ") and then createGUI or setupGUI,"
388 << "which also calls setXMLFile and will overwrite the file you have previously set.\n"
389 << "You should call createGUI(" << xmlFile() << ") or setupGUI(<options>," << xmlFile() << ") instead.";
390 }
391
392 // we always want to load in our global standards file
394
395 // now, merge in our local xml file.
396 setXMLFile(windowXmlFile, true);
397
398 // make sure we don't have any state saved already
400
401 // do the actual GUI building
402 guiFactory()->reset();
403 guiFactory()->addClient(this);
404
406
407 // setUpdatesEnabled( true );
408}
409
411{
412 stateChanged(newstate, KXMLGUIClient::StateNoReverse);
413}
414
415void KXmlGuiWindow::slotStateChanged(const QString &newstate, bool reverse)
416{
417 stateChanged(newstate, reverse ? KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse);
418}
419
421{
423 if (showToolBarMenu) {
424 if (d->toolBarHandler) {
425 return;
426 }
427
428 d->toolBarHandler = new KDEPrivate::ToolBarHandler(this);
429
430 if (factory()) {
431 factory()->addClient(d->toolBarHandler);
432 }
433 } else {
434 if (!d->toolBarHandler) {
435 return;
436 }
437
438 if (factory()) {
439 factory()->removeClient(d->toolBarHandler);
440 }
441
442 delete d->toolBarHandler;
443 d->toolBarHandler = nullptr;
444 }
445}
446
448{
449 Q_D(const KXmlGuiWindow);
450 return (d->toolBarHandler);
451}
452
454{
456 if (!d->showStatusBarAction) {
458 QStatusBar *sb = statusBar(); // Creates statusbar if it doesn't exist already.
459 connect(d->showStatusBarAction, &QAction::toggled, sb, &QWidget::setVisible);
460 d->showStatusBarAction->setChecked(sb->isHidden());
461 } else {
462 // If the language has changed, we'll need to grab the new text and whatsThis
463 QAction *tmpStatusBar = KStandardAction::showStatusbar(nullptr, nullptr, nullptr);
464 d->showStatusBarAction->setText(tmpStatusBar->text());
465 d->showStatusBarAction->setWhatsThis(tmpStatusBar->whatsThis());
466 delete tmpStatusBar;
467 }
468}
469
470void KXmlGuiWindow::finalizeGUI(bool /*force*/)
471{
472 // FIXME: this really needs to be removed with a code more like the one we had on KDE3.
473 // what we need to do here is to position correctly toolbars so they don't overlap.
474 // Also, take in count plugins could provide their own toolbars and those also need to
475 // be restored.
476 if (autoSaveSettings() && autoSaveConfigGroup().isValid()) {
478 }
479}
480
482{
486 if (sb && d->showStatusBarAction) {
487 d->showStatusBarAction->setChecked(!sb->isHidden());
488 }
489}
490
492{
493 QMap<QString, QAction *> shortcuts;
494 QAction *editCutAction = actionCollection()->action(QStringLiteral("edit_cut"));
495 QAction *deleteFileAction = actionCollection()->action(QStringLiteral("deletefile"));
496 const auto actions = actionCollection()->actions();
497 for (QAction *action : actions) {
498 if (action->isEnabled()) {
499 const auto actionShortcuts = action->shortcuts();
500 for (const QKeySequence &shortcut : actionShortcuts) {
501 if (shortcut.isEmpty()) {
502 continue;
503 }
504 const QString portableShortcutText = shortcut.toString();
505 const QAction *existingShortcutAction = shortcuts.value(portableShortcutText);
506 if (existingShortcutAction) {
507 // If the shortcut is already in use we give a warning, so that hopefully the developer will find it
508 // There is one exception, if the conflicting shortcut is a non primary shortcut of "edit_cut"
509 // and "deleteFileAction" is the other action since Shift+Delete is used for both in our default code
510 bool showWarning = true;
511 if ((action == editCutAction && existingShortcutAction == deleteFileAction)
512 || (action == deleteFileAction && existingShortcutAction == editCutAction)) {
513 QList<QKeySequence> editCutActionShortcuts = editCutAction->shortcuts();
514 if (editCutActionShortcuts.indexOf(shortcut) > 0) // alternate shortcut
515 {
516 editCutActionShortcuts.removeAll(shortcut);
517 editCutAction->setShortcuts(editCutActionShortcuts);
518
519 showWarning = false;
520 }
521 }
522
523 if (showWarning) {
525 const QString existingShortcutActionName = KLocalizedString::removeAcceleratorMarker(existingShortcutAction->text());
526 QString dontShowAgainString = existingShortcutActionName + actionName + shortcut.toString();
527 dontShowAgainString.remove(QLatin1Char('\\'));
529 i18n("There are two actions (%1, %2) that want to use the same shortcut (%3). This is most probably a bug. "
530 "Please report it in <a href='https://bugs.kde.org'>bugs.kde.org</a>",
531 existingShortcutActionName,
532 actionName,
533 shortcut.toString(QKeySequence::NativeText)),
534 i18n("Ambiguous Shortcuts"),
535 dontShowAgainString,
537 }
538 } else {
539 shortcuts.insert(portableShortcutText, action);
540 }
541 }
542 }
543 }
544}
545
546void KXmlGuiWindow::setCommandBarEnabled(bool showCommandBar)
547{
548 /**
549 * Unset the shortcut
550 */
551 auto cmdBarAction = actionCollection()->action(QStringLiteral("open_kcommand_bar"));
552 if (showCommandBar) {
554 } else {
556 }
557
559 d->commandBarEnabled = showCommandBar;
560}
561
563{
564 Q_D(const KXmlGuiWindow);
565 return d->commandBarEnabled;
566}
567
568#include "moc_kxmlguiwindow.cpp"
A container for a set of QAction objects.
Q_INVOKABLE QAction * addAction(const QString &name, QAction *action)
Add an action under the given name to the collection.
static void setDefaultShortcut(QAction *action, const QKeySequence &shortcut)
Set the default shortcut for the given action.
QList< QAction * > actions() const
Returns the list of QActions which belong to this action collection.
QAction * action(int index) const
Return the QAction* at position index in the action collection.
bool isValid() const
A dialog used to customize or configure toolbars.
void newToolBarConfig()
Signal emitted when 'apply' or 'ok' is clicked or toolbars were reset.
Standard KDE help menu with dialog boxes.
Definition khelpmenu.h:109
static QString removeAcceleratorMarker(const QString &label)
KMainWindow represents a top-level main window.
Definition kmainwindow.h:60
virtual void applyMainWindowSettings(const KConfigGroup &config)
Read settings for statusbar, menubar and toolbar from their respective groups in the config file and ...
bool event(QEvent *event) override
Reimplemented to catch QEvent::Polish in order to adjust the object name if needed,...
void setAutoSaveSettings(const QString &groupName=QStringLiteral("MainWindow"), bool saveWindowSize=true)
This enables autosave of toolbar/menubar/statusbar settings (and optionally window size).
void setSettingsDirty()
Tell the main window that it should save its settings when being closed.
KConfigGroup autoSaveConfigGroup() const
QList< KToolBar * > toolBars() const
void saveMainWindowSettings(KConfigGroup &config)
Manually save the settings for statusbar, menubar and toolbar to their respective groups in the KConf...
QString dbusName() const
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
Implements the creation of the GUI (menubar, menus and toolbars) as requested by the GUI factory.
A KXMLGUIClient can be used with KXMLGUIFactory to create a GUI from actions and an XML document,...
virtual QString xmlFile() const
This will return the name of the XML file as set by setXMLFile().
virtual QString componentName() const
virtual void stateChanged(const QString &newstate, ReverseStateChange reverse=StateNoReverse)
Actions can collectively be assigned a "State".
virtual KActionCollection * actionCollection() const
Retrieves the entire action collection for the GUI client.
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.
QList< KXMLGUIClient * > childClients()
Retrieves a list of all child clients.
KXMLGUIFactory * factory() const
Retrieves a pointer to the KXMLGUIFactory this client is associated with (will return nullptr if the ...
void setXMLGUIBuildDocument(const QDomDocument &doc)
void loadStandardsXmlFile()
Load the ui_standards.rc file.
QAction * action(const QString &name) const
Retrieves an action of the client by name.
KXMLGUIFactory, together with KXMLGUIClient objects, can be used to create a GUI of container widgets...
QList< KXMLGUIClient * > clients() const
Returns a list of all clients currently added to this factory.
void removeClient(KXMLGUIClient *client)
Removes the GUI described by the client, by unplugging all provided actions and removing all owned co...
void addClient(KXMLGUIClient *client)
Creates the GUI described by the QDomDocument of the client, using the client's actions,...
void reset()
Use this method to free all memory allocated by the KXMLGUIFactory.
void showConfigureShortcutsDialog()
Shows a dialog (KShortcutsDialog) that lists every action in this factory, and which can be used to c...
void makingChanges(bool)
Emitted when the factory is currently making changes to the GUI, i.e.
KMainWindow with convenience functions and integration with XmlGui files.
void createGUI(const QString &xmlfile=QString())
Generates the interface based on a local XML file.
KXmlGuiWindow(QWidget *parent=nullptr, Qt::WindowFlags flags=Qt::WindowFlags())
Construct a main window.
QAction * toolBarMenuAction()
void createStandardStatusBarAction()
Creates a toggle under the 'Settings' menu to show/hide the statusbar.
void setStandardToolBarMenuEnabled(bool showToolBarMenu)
Creates a toggle under the 'Settings' menu to show/hide the available toolbars.
void applyMainWindowSettings(const KConfigGroup &config) override
Read settings for statusbar, menubar and toolbar from their respective groups in the config file and ...
bool isStandardToolBarMenuEnabled() const
Returns whether setStandardToolBarMenuEnabled() was set.
@ StatusBar
Adds an action to show/hide the statusbar in the 'Settings' menu.
@ Save
Autosaves (and loads) the toolbar/menubar/statusbar settings and window size using the default name.
@ Create
Calls createGUI() once ToolBar, Keys and Statusbar have been taken care of.
@ Keys
Adds an action in the 'Settings' menu to open the configure keyboard shortcuts dialog.
void setupToolbarMenuActions()
virtual void slotStateChanged(const QString &newstate)
Applies a state change.
virtual void configureToolbars()
Show a standard configure toolbar dialog.
bool isCommandBarEnabled() const
Returns whether a KCommandBar was set.
void setHelpMenuEnabled(bool showHelpMenu=true)
Creates a standard help menu when calling createGUI() or setupGUI().
bool event(QEvent *event) override
Reimplemented to catch QEvent::Polish in order to adjust the object name if needed,...
~KXmlGuiWindow() override
Destructor.
virtual void saveNewToolbarConfig()
Rebuilds the GUI after KEditToolBar changes the toolbar layout.
void setCommandBarEnabled(bool showCommandBar)
Enable a KCommandBar to list and quickly execute actions.
void finalizeGUI(bool force)
void checkAmbiguousShortcuts()
Checks if there are actions using the same shortcut.
bool isHelpMenuEnabled() const
void setupGUI(StandardWindowOptions options=Default, const QString &xmlfile=QString())
Configures the current window and its actions in the typical KDE fashion.
QString i18n(const char *text, const TYPE &arg...)
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
KToggleAction * showStatusbar(const QObject *recvr, const char *slot, QObject *parent)
bool isEnabled() const const
void setIcon(const QIcon &icon)
void setShortcuts(QKeySequence::StandardKey key)
QList< QKeySequence > shortcuts() const const
void toggled(bool checked)
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
Type type() const const
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
qsizetype indexOf(const AT &value, qsizetype from) const const
bool isEmpty() const const
qsizetype removeAll(const AT &t)
void reserve(qsizetype size)
qsizetype size() const const
QMenuBar * menuBar() const const
QStatusBar * statusBar() const const
iterator insert(const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) const const
void clear()
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T findChild(const QString &name, Qt::FindChildOptions options) const const
bool isNull() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
void reserve(qsizetype size)
WA_DeleteOnClose
typedef WindowFlags
QList< QAction * > actions() const const
void adjustSize()
bool isHidden() const const
void resize(const QSize &)
virtual void setVisible(bool visible)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 29 2024 11:48:05 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.