1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Reginald Stadlbauer <reggie@kde.org>
4 SPDX-FileCopyrightText: 1997, 1998 Stephan Kulow <coolo@kde.org>
5 SPDX-FileCopyrightText: 1997, 1998 Mark Donohoe <donohoe@kde.org>
6 SPDX-FileCopyrightText: 1997, 1998 Sven Radej <radej@kde.org>
7 SPDX-FileCopyrightText: 1997, 1998 Matthias Ettrich <ettrich@kde.org>
8 SPDX-FileCopyrightText: 1999 Chris Schlaeger <cs@kde.org>
9 SPDX-FileCopyrightText: 1999 Kurt Granroth <granroth@kde.org>
10 SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
11
12 SPDX-License-Identifier: LGPL-2.0-only
13*/
14
15#include "ktoolbar.h"
16
17#include <QAction>
18#include <QActionGroup>
19#include <QApplication>
20#include <QDomElement>
21#include <QDrag>
22#include <QFrame>
23#include <QLayout>
24#include <QMenu>
25#include <QMimeData>
26#include <QMouseEvent>
27#include <QPointer>
28#ifdef WITH_QTDBUS
29#include <QDBusConnection>
30#include <QDBusMessage>
31#endif
32
33#include <KAuthorized>
34#include <KConfig>
35#include <KConfigGroup>
36#include <KIconTheme>
37#include <KLocalizedString>
38#include <KSharedConfig>
39#include <KStandardActions>
40#include <KToggleAction>
41#include <KToolBarPopupAction>
42
43#include "kactioncollection.h"
44#include "kedittoolbar.h"
45#include "kxmlguifactory.h"
46#include "kxmlguiwindow.h"
47
48#include "ktoolbarhelper_p.h"
49
50#include <algorithm>
51
52/*
53 Toolbar settings (e.g. icon size or toolButtonStyle)
54 =====================================================
55
56 We have the following stack of settings (in order of priority) :
57 - user-specified settings (loaded/saved in KConfig)
58 - developer-specified settings in the XMLGUI file (if using xmlgui) (cannot change at runtime)
59 - KDE-global default (user-configurable; can change at runtime)
60 and when switching between kparts, they are saved as xml in memory,
61 which, in the unlikely case of no-kmainwindow-autosaving, could be
62 different from the user-specified settings saved in KConfig and would have
63 priority over it.
64
65 So, in summary, without XML:
66 Global config / User settings (loaded/saved in kconfig)
67 and with XML:
68 Global config / App-XML attributes / User settings (loaded/saved in kconfig)
69
70 And all those settings (except the KDE-global defaults) have to be stored in memory
71 since we cannot retrieve them at random points in time, not knowing the xml document
72 nor config file that holds these settings. Hence the iconSizeSettings and toolButtonStyleSettings arrays.
73
74 For instance, if you change the KDE-global default, whether this makes a change
75 on a given toolbar depends on whether there are settings at Level_AppXML or Level_UserSettings.
76 Only if there are no settings at those levels, should the change of KDEDefault make a difference.
77*/
78enum SettingLevel {
79 Level_KDEDefault,
80 Level_AppXML,
81 Level_UserSettings,
82 NSettingLevels
83};
84enum {
85 Unset = -1
86};
87
88class KToolBarPrivate
89{
90public:
91 KToolBarPrivate(KToolBar *qq)
92 : q(qq)
93 , isMainToolBar(false)
94 , unlockedMovable(true)
95 , contextOrient(nullptr)
96 , contextMode(nullptr)
97 , contextSize(nullptr)
98 , contextButtonTitle(nullptr)
99 , contextShowText(nullptr)
100 , contextButtonAction(nullptr)
101 , contextTop(nullptr)
102 , contextLeft(nullptr)
103 , contextRight(nullptr)
104 , contextBottom(nullptr)
105 , contextIcons(nullptr)
106 , contextTextRight(nullptr)
107 , contextText(nullptr)
108 , contextTextUnder(nullptr)
109 , contextConfigureAction(nullptr)
110 , contextToolBarAction(nullptr)
111 , contextLockAction(nullptr)
112 , dropIndicatorAction(nullptr)
113 , context(nullptr)
114 , dragAction(nullptr)
115 {
116 }
117
118 void slotAppearanceChanged();
119 void slotContextAboutToShow();
120 void slotContextAboutToHide();
121 void slotContextLeft();
122 void slotContextRight();
123 void slotContextShowText();
124 void slotContextTop();
125 void slotContextBottom();
126 void slotContextIcons();
127 void slotContextText();
128 void slotContextTextRight();
129 void slotContextTextUnder();
130 void slotContextIconSize(QAction *action);
131 void slotLockToolBars(bool lock);
132
133 void init(bool readConfig = true, bool isMainToolBar = false);
134 QString getPositionAsString() const;
135 QMenu *contextMenu(const QPoint &globalPos);
136 void setLocked(bool locked);
137 void adjustSeparatorVisibility();
138 void loadKDESettings();
139 void applyCurrentSettings();
140
141 QAction *findAction(const QString &actionName, KXMLGUIClient **client = nullptr) const;
142
143 static Qt::ToolButtonStyle toolButtonStyleFromString(const QString &style);
144 static QString toolButtonStyleToString(Qt::ToolButtonStyle);
145 static Qt::ToolBarArea positionFromString(const QString &position);
146 static Qt::ToolButtonStyle toolButtonStyleSetting();
147
148 KToolBar *const q;
149 bool isMainToolBar : 1;
150 bool unlockedMovable : 1;
151 static bool s_editable;
152 static bool s_locked;
153
154 QSet<KXMLGUIClient *> xmlguiClients;
155
156 QMenu *contextOrient;
157 QMenu *contextMode;
158 QMenu *contextSize;
159
160 QAction *contextButtonTitle;
161 QAction *contextShowText;
162 QAction *contextButtonAction;
163 QAction *contextTop;
164 QAction *contextLeft;
165 QAction *contextRight;
166 QAction *contextBottom;
167 QAction *contextIcons;
168 QAction *contextTextRight;
169 QAction *contextText;
170 QAction *contextTextUnder;
171 QAction *contextConfigureAction; // nullptr if not in context
172 QAction *contextToolBarAction; // nullptr if not in context
173 KToggleAction *contextLockAction;
174
175 struct ContextIconInfo {
176 QAction *iconAction = nullptr;
177 int iconSize = 0;
178 };
179
180 std::vector<ContextIconInfo> m_contextIconSizes;
181
182 class IntSetting
183 {
184 public:
185 IntSetting()
186 {
187 for (int &value : values) {
188 value = Unset;
189 }
190 }
191 int currentValue() const
192 {
193 int val = Unset;
194 for (int value : values) {
195 if (value != Unset) {
196 val = value;
197 }
198 }
199 return val;
200 }
201 // Default value as far as the user is concerned is kde-global + app-xml.
202 // If currentValue()==defaultValue() then nothing to write into kconfig.
203 int defaultValue() const
204 {
205 int val = Unset;
206 for (int level = 0; level < Level_UserSettings; ++level) {
207 if (values[level] != Unset) {
208 val = values[level];
209 }
210 }
211 return val;
212 }
213 QString toString() const
214 {
215 QString str;
216 for (int value : values) {
217 str += QString::number(value) + QLatin1Char(' ');
218 }
219 return str;
220 }
221 int &operator[](int index)
222 {
223 return values[index];
224 }
225
226 private:
227 int values[NSettingLevels];
228 };
229 IntSetting iconSizeSettings;
230 IntSetting toolButtonStyleSettings; // either Qt::ToolButtonStyle or -1, hence "int".
231
232 QList<QAction *> actionsBeingDragged;
233 QAction *dropIndicatorAction;
234
235 QMenu *context;
236 QAction *dragAction;
237 QPoint dragStartPosition;
238};
239
240bool KToolBarPrivate::s_editable = false;
241bool KToolBarPrivate::s_locked = true;
242
243void KToolBarPrivate::init(bool readConfig, bool _isMainToolBar)
244{
245 isMainToolBar = _isMainToolBar;
246 loadKDESettings();
247
248 // also read in our configurable settings (for non-xmlgui toolbars)
249 if (readConfig) {
250 KConfigGroup cg(KSharedConfig::openConfig(), QString());
251 q->applySettings(cg);
252 }
253
254 if (q->mainWindow()) {
255 // Get notified when settings change
256 QObject::connect(sender: q, signal: &QToolBar::allowedAreasChanged, context: q->mainWindow(), slot: &KMainWindow::setSettingsDirty);
257 QObject::connect(sender: q, signal: &QToolBar::iconSizeChanged, context: q->mainWindow(), slot: &KMainWindow::setSettingsDirty);
258 QObject::connect(sender: q, signal: &QToolBar::toolButtonStyleChanged, context: q->mainWindow(), slot: &KMainWindow::setSettingsDirty);
259 QObject::connect(sender: q, signal: &QToolBar::movableChanged, context: q->mainWindow(), slot: &KMainWindow::setSettingsDirty);
260 QObject::connect(sender: q, signal: &QToolBar::orientationChanged, context: q->mainWindow(), slot: &KMainWindow::setSettingsDirty);
261 }
262
263 if (!KAuthorized::authorize(QStringLiteral("movable_toolbars"))) {
264 q->setMovable(false);
265 } else {
266 q->setMovable(!KToolBar::toolBarsLocked());
267 }
268
269 q->toggleViewAction()->setEnabled(KAuthorized::authorizeAction(QStringLiteral("options_show_toolbar")));
270
271 QObject::connect(sender: q, signal: &QToolBar::movableChanged, context: q, slot: &KToolBar::slotMovableChanged);
272
273 q->setAcceptDrops(true);
274
275#ifdef WITH_QTDBUS
276 QDBusConnection::sessionBus()
277 .connect(service: QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), receiver: q, SLOT(slotAppearanceChanged()));
278#endif
279 QObject::connect(sender: KIconLoader::global(), signal: &KIconLoader::iconLoaderSettingsChanged, context: q, slot: [this]() {
280 slotAppearanceChanged();
281 });
282}
283
284QString KToolBarPrivate::getPositionAsString() const
285{
286 if (!q->mainWindow()) {
287 return QStringLiteral("None");
288 }
289 // get all of the stuff to save
290 switch (q->mainWindow()->toolBarArea(toolbar: const_cast<KToolBar *>(q))) {
291 case Qt::BottomToolBarArea:
292 return QStringLiteral("Bottom");
293 case Qt::LeftToolBarArea:
294 return QStringLiteral("Left");
295 case Qt::RightToolBarArea:
296 return QStringLiteral("Right");
297 case Qt::TopToolBarArea:
298 default:
299 return QStringLiteral("Top");
300 }
301}
302
303QMenu *KToolBarPrivate::contextMenu(const QPoint &globalPos)
304{
305 if (!context) {
306 context = new QMenu(q);
307 context->setIcon(QIcon::fromTheme(QStringLiteral("configure-toolbars")));
308 context->setTitle(i18nc("@title:menu", "Toolbar Settings"));
309
310 contextButtonTitle = context->addSection(i18nc("@title:menu", "Show Text"));
311 contextShowText = context->addAction(text: QString(), args: q, args: [this]() {
312 slotContextShowText();
313 });
314
315 context->addSection(i18nc("@title:menu", "Toolbar Settings"));
316
317 contextOrient = new QMenu(i18nc("Toolbar orientation", "Orientation"), context);
318
319 contextTop = contextOrient->addAction(i18nc("toolbar position string", "Top"), args: q, args: [this]() {
320 slotContextTop();
321 });
322 contextTop->setChecked(true);
323 contextLeft = contextOrient->addAction(i18nc("toolbar position string", "Left"), args: q, args: [this]() {
324 slotContextLeft();
325 });
326 contextRight = contextOrient->addAction(i18nc("toolbar position string", "Right"), args: q, args: [this]() {
327 slotContextRight();
328 });
329 contextBottom = contextOrient->addAction(i18nc("toolbar position string", "Bottom"), args: q, args: [this]() {
330 slotContextBottom();
331 });
332
333 QActionGroup *positionGroup = new QActionGroup(contextOrient);
334 const auto orientActions = contextOrient->actions();
335 for (QAction *action : orientActions) {
336 action->setActionGroup(positionGroup);
337 action->setCheckable(true);
338 }
339
340 contextMode = new QMenu(i18n("Text Position"), context);
341
342 contextIcons = contextMode->addAction(i18nc("@item:inmenu", "Icons Only"), args: q, args: [this]() {
343 slotContextIcons();
344 });
345 contextText = contextMode->addAction(i18nc("@item:inmenu", "Text Only"), args: q, args: [this]() {
346 slotContextText();
347 });
348 contextTextRight = contextMode->addAction(i18nc("@item:inmenu", "Text Alongside Icons"), args: q, args: [this]() {
349 slotContextTextRight();
350 });
351 contextTextUnder = contextMode->addAction(i18nc("@item:inmenu", "Text Under Icons"), args: q, args: [this]() {
352 slotContextTextUnder();
353 });
354
355 QActionGroup *textGroup = new QActionGroup(contextMode);
356 const auto modeActions = contextMode->actions();
357 for (QAction *action : modeActions) {
358 action->setActionGroup(textGroup);
359 action->setCheckable(true);
360 }
361
362 contextSize = new QMenu(i18n("Icon Size"), context);
363
364 auto *act = contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"));
365 q->connect(sender: act, signal: &QAction::triggered, context: q, slot: [this, act]() {
366 slotContextIconSize(action: act);
367 });
368 m_contextIconSizes.push_back(x: {.iconAction: act, .iconSize: iconSizeSettings.defaultValue()});
369
370 // Query the current theme for available sizes
371 KIconTheme *theme = KIconLoader::global()->theme();
372 QList<int> avSizes;
373 if (theme) {
374 avSizes = theme->querySizes(group: isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar);
375 }
376
377 std::sort(first: avSizes.begin(), last: avSizes.end());
378
379 if (avSizes.count() < 10) {
380 // Fixed or threshold type icons
381 for (int it : std::as_const(t&: avSizes)) {
382 QString text;
383 if (it < 19) {
384 text = i18n("Small (%1x%2)", it, it);
385 } else if (it < 25) {
386 text = i18n("Medium (%1x%2)", it, it);
387 } else if (it < 35) {
388 text = i18n("Large (%1x%2)", it, it);
389 } else {
390 text = i18n("Huge (%1x%2)", it, it);
391 }
392
393 auto *act = contextSize->addAction(text);
394 q->connect(sender: act, signal: &QAction::triggered, context: q, slot: [this, act]() {
395 slotContextIconSize(action: act);
396 });
397 m_contextIconSizes.push_back(x: {.iconAction: act, .iconSize: it});
398 }
399 } else {
400 // Scalable icons.
401 const int progression[] = {16, 22, 32, 48, 64, 96, 128, 192, 256};
402
403 for (int p : progression) {
404 for (int it : std::as_const(t&: avSizes)) {
405 if (it >= p) {
406 QString text;
407 if (it < 19) {
408 text = i18n("Small (%1x%2)", it, it);
409 } else if (it < 25) {
410 text = i18n("Medium (%1x%2)", it, it);
411 } else if (it < 35) {
412 text = i18n("Large (%1x%2)", it, it);
413 } else {
414 text = i18n("Huge (%1x%2)", it, it);
415 }
416
417 auto *act = contextSize->addAction(text);
418 q->connect(sender: act, signal: &QAction::triggered, context: q, slot: [this, act]() {
419 slotContextIconSize(action: act);
420 });
421 m_contextIconSizes.push_back(x: {.iconAction: act, .iconSize: it});
422 break;
423 }
424 }
425 }
426 }
427
428 QActionGroup *sizeGroup = new QActionGroup(contextSize);
429 const auto sizeActions = contextSize->actions();
430 for (QAction *action : sizeActions) {
431 action->setActionGroup(sizeGroup);
432 action->setCheckable(true);
433 }
434
435 if (!q->toolBarsLocked() && !q->isMovable()) {
436 unlockedMovable = false;
437 }
438
439 delete contextLockAction;
440 contextLockAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("system-lock-screen")), i18n("Lock Toolbar Positions"), q);
441 contextLockAction->setChecked(q->toolBarsLocked());
442 QObject::connect(sender: contextLockAction, signal: &KToggleAction::toggled, context: q, slot: [this](bool checked) {
443 slotLockToolBars(lock: checked);
444 });
445
446 // Now add the actions to the menu
447 context->addMenu(menu: contextMode);
448 context->addMenu(menu: contextSize);
449 context->addMenu(menu: contextOrient);
450 context->addSeparator();
451
452 QObject::connect(sender: context, signal: &QMenu::aboutToShow, context: q, slot: [this]() {
453 slotContextAboutToShow();
454 });
455 }
456
457 contextButtonAction = q->actionAt(p: q->mapFromGlobal(globalPos));
458 if (contextButtonAction) {
459 contextShowText->setText(contextButtonAction->text());
460 contextShowText->setIcon(contextButtonAction->icon());
461 contextShowText->setCheckable(true);
462 }
463
464 contextOrient->menuAction()->setVisible(!q->toolBarsLocked());
465 // Unplugging a submenu from abouttohide leads to the popupmenu floating around
466 // So better simply call that code from after exec() returns (DF)
467 // connect(context, SIGNAL(aboutToHide()), this, SLOT(slotContextAboutToHide()));
468
469 // For tool buttons with delay popup or menu button popup (split button)
470 // show the actions of that button for ease of access.
471 // (no need to wait for the menu to open or aim at the arrow)
472 if (auto *contextToolButton = qobject_cast<QToolButton *>(object: q->widgetForAction(action: contextButtonAction))) {
473 if (contextToolButton->popupMode() == QToolButton::DelayedPopup || contextToolButton->popupMode() == QToolButton::MenuButtonPopup) {
474 auto *actionMenu = contextButtonAction->menu();
475
476 if (!actionMenu) {
477 if (auto *toolBarPopupAction = qobject_cast<KToolBarPopupAction *>(object: contextButtonAction)) {
478 actionMenu = toolBarPopupAction->popupMenu();
479 }
480 }
481
482 if (actionMenu) {
483 // In case it is populated on demand
484 Q_EMIT actionMenu->aboutToShow();
485
486 auto *contextMenu = new QMenu(q);
487 contextMenu->setAttribute(Qt::WA_DeleteOnClose);
488
489 const auto actions = actionMenu->actions();
490 if (!actions.isEmpty()) {
491 for (QAction *action : actions) {
492 contextMenu->addAction(action);
493 }
494
495 // Now add the configure actions as submenu
496 contextMenu->addSeparator();
497 contextMenu->addMenu(menu: context);
498 return contextMenu;
499 }
500 }
501 }
502 }
503
504 return context;
505}
506
507void KToolBarPrivate::setLocked(bool locked)
508{
509 if (unlockedMovable) {
510 q->setMovable(!locked);
511 }
512}
513
514void KToolBarPrivate::adjustSeparatorVisibility()
515{
516 bool visibleNonSeparator = false;
517 int separatorToShow = -1;
518
519 for (int index = 0; index < q->actions().count(); ++index) {
520 QAction *action = q->actions().at(i: index);
521 if (action->isSeparator()) {
522 if (visibleNonSeparator) {
523 separatorToShow = index;
524 visibleNonSeparator = false;
525 } else {
526 action->setVisible(false);
527 }
528 } else if (!visibleNonSeparator) {
529 if (action->isVisible()) {
530 visibleNonSeparator = true;
531 if (separatorToShow != -1) {
532 q->actions().at(i: separatorToShow)->setVisible(true);
533 separatorToShow = -1;
534 }
535 }
536 }
537 }
538
539 if (separatorToShow != -1) {
540 q->actions().at(i: separatorToShow)->setVisible(false);
541 }
542}
543
544Qt::ToolButtonStyle KToolBarPrivate::toolButtonStyleFromString(const QString &_style)
545{
546 QString style = _style.toLower();
547 if (style == QLatin1String("textbesideicon") || style == QLatin1String("icontextright")) {
548 return Qt::ToolButtonTextBesideIcon;
549 } else if (style == QLatin1String("textundericon") || style == QLatin1String("icontextbottom")) {
550 return Qt::ToolButtonTextUnderIcon;
551 } else if (style == QLatin1String("textonly")) {
552 return Qt::ToolButtonTextOnly;
553 } else {
554 return Qt::ToolButtonIconOnly;
555 }
556}
557
558QString KToolBarPrivate::toolButtonStyleToString(Qt::ToolButtonStyle style)
559{
560 switch (style) {
561 case Qt::ToolButtonIconOnly:
562 default:
563 return QStringLiteral("IconOnly");
564 case Qt::ToolButtonTextBesideIcon:
565 return QStringLiteral("TextBesideIcon");
566 case Qt::ToolButtonTextOnly:
567 return QStringLiteral("TextOnly");
568 case Qt::ToolButtonTextUnderIcon:
569 return QStringLiteral("TextUnderIcon");
570 }
571}
572
573Qt::ToolBarArea KToolBarPrivate::positionFromString(const QString &position)
574{
575 Qt::ToolBarArea newposition = Qt::TopToolBarArea;
576 if (position == QLatin1String("left")) {
577 newposition = Qt::LeftToolBarArea;
578 } else if (position == QLatin1String("bottom")) {
579 newposition = Qt::BottomToolBarArea;
580 } else if (position == QLatin1String("right")) {
581 newposition = Qt::RightToolBarArea;
582 } else if (position == QLatin1String("none")) {
583 newposition = Qt::NoToolBarArea;
584 }
585 return newposition;
586}
587
588// Global setting was changed
589void KToolBarPrivate::slotAppearanceChanged()
590{
591 loadKDESettings();
592 applyCurrentSettings();
593}
594
595Qt::ToolButtonStyle KToolBarPrivate::toolButtonStyleSetting()
596{
597 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("Toolbar style"));
598 const QString fallback = KToolBarPrivate::toolButtonStyleToString(style: Qt::ToolButtonTextBesideIcon);
599 return KToolBarPrivate::toolButtonStyleFromString(style: group.readEntry(key: "ToolButtonStyle", aDefault: fallback));
600}
601
602void KToolBarPrivate::loadKDESettings()
603{
604 iconSizeSettings[Level_KDEDefault] = q->iconSizeDefault();
605
606 if (isMainToolBar) {
607 toolButtonStyleSettings[Level_KDEDefault] = toolButtonStyleSetting();
608 } else {
609 const QString fallBack = toolButtonStyleToString(style: Qt::ToolButtonTextBesideIcon);
610 /*
611 TODO: if we get complaints about text beside icons on small screens,
612 try the following code out on such systems - aseigo.
613 // if we are on a small screen with a non-landscape ratio, then
614 // we revert to text under icons since width is probably not our
615 // friend in such cases
616 QDesktopWidget *desktop = QApplication::desktop();
617 QRect screenGeom = desktop->screenGeometry(desktop->primaryScreen());
618 qreal ratio = screenGeom.width() / qreal(screenGeom.height());
619
620 if (screenGeom.width() < 1024 && ratio <= 1.4) {
621 fallBack = "TextUnderIcon";
622 }
623 **/
624
625 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("Toolbar style"));
626 const QString value = group.readEntry(key: "ToolButtonStyleOtherToolbars", aDefault: fallBack);
627 toolButtonStyleSettings[Level_KDEDefault] = KToolBarPrivate::toolButtonStyleFromString(style: value);
628 }
629}
630
631// Call this after changing something in d->iconSizeSettings or d->toolButtonStyleSettings
632void KToolBarPrivate::applyCurrentSettings()
633{
634 // qCDebug(DEBUG_KXMLGUI) << q->objectName() << "iconSizeSettings:" << iconSizeSettings.toString() << "->" << iconSizeSettings.currentValue();
635 const int currentIconSize = iconSizeSettings.currentValue();
636 q->setIconSize(QSize(currentIconSize, currentIconSize));
637 // qCDebug(DEBUG_KXMLGUI) << q->objectName() << "toolButtonStyleSettings:" << toolButtonStyleSettings.toString() << "->" <<
638 // toolButtonStyleSettings.currentValue();
639 q->setToolButtonStyle(static_cast<Qt::ToolButtonStyle>(toolButtonStyleSettings.currentValue()));
640
641 // And remember to save the new look later
642 KMainWindow *kmw = q->mainWindow();
643 if (kmw) {
644 kmw->setSettingsDirty();
645 }
646}
647
648QAction *KToolBarPrivate::findAction(const QString &actionName, KXMLGUIClient **clientOut) const
649{
650 for (KXMLGUIClient *client : xmlguiClients) {
651 QAction *action = client->actionCollection()->action(name: actionName);
652 if (action) {
653 if (clientOut) {
654 *clientOut = client;
655 }
656 return action;
657 }
658 }
659 return nullptr;
660}
661
662void KToolBarPrivate::slotContextAboutToShow()
663{
664 /*
665 * The idea here is to reuse the "static" part of the menu to save time.
666 * But the "Toolbars" action is dynamic (can be a single action or a submenu)
667 * and ToolBarHandler::setupActions() deletes it, so better not keep it around.
668 * So we currently plug/unplug the last two actions of the menu.
669 * Another way would be to keep around the actions and plug them all into a (new each time) popupmenu.
670 */
671
672 KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(object: q->mainWindow());
673
674 // try to find "configure toolbars" action
675 const QString actionName = KStandardActions::name(id: KStandardActions::ConfigureToolbars);
676 QAction *configureAction = findAction(actionName);
677 if (!configureAction && kmw) {
678 configureAction = kmw->actionCollection()->action(name: actionName);
679 }
680 contextConfigureAction = configureAction;
681 if (configureAction) {
682 context->addAction(action: configureAction);
683 }
684
685 context->addAction(action: contextLockAction);
686
687 contextToolBarAction = nullptr;
688 if (kmw) {
689 kmw->setupToolbarMenuActions();
690 // Only allow hiding a toolbar if the action is also plugged somewhere else (e.g. menubar)
691 QAction *tbAction = kmw->toolBarMenuAction();
692 if (!q->toolBarsLocked() && tbAction) {
693 // this compiles to less code than `for (QObject *object : tbAction->associatedObjects()) {`
694 const QList<QObject *> associatedObjects = tbAction->associatedObjects();
695 for (QObject *object : associatedObjects) {
696 if (qobject_cast<QWidget *>(o: object)) {
697 contextToolBarAction = tbAction;
698 context->addAction(action: tbAction);
699 break;
700 }
701 }
702 }
703 }
704
705 KEditToolBar::setGlobalDefaultToolBar(q->QObject::objectName());
706
707 // Check the actions that should be checked
708 switch (q->toolButtonStyle()) {
709 case Qt::ToolButtonIconOnly:
710 default:
711 contextIcons->setChecked(true);
712 break;
713 case Qt::ToolButtonTextBesideIcon:
714 contextTextRight->setChecked(true);
715 break;
716 case Qt::ToolButtonTextOnly:
717 contextText->setChecked(true);
718 break;
719 case Qt::ToolButtonTextUnderIcon:
720 contextTextUnder->setChecked(true);
721 break;
722 }
723
724 auto it = std::find_if(first: m_contextIconSizes.cbegin(), last: m_contextIconSizes.cend(), pred: [this](const ContextIconInfo &info) {
725 return info.iconSize == q->iconSize().width();
726 });
727 if (it != m_contextIconSizes.cend()) {
728 it->iconAction->setChecked(true);
729 }
730
731 switch (q->mainWindow()->toolBarArea(toolbar: q)) {
732 case Qt::BottomToolBarArea:
733 contextBottom->setChecked(true);
734 break;
735 case Qt::LeftToolBarArea:
736 contextLeft->setChecked(true);
737 break;
738 case Qt::RightToolBarArea:
739 contextRight->setChecked(true);
740 break;
741 default:
742 case Qt::TopToolBarArea:
743 contextTop->setChecked(true);
744 break;
745 }
746
747 const bool showButtonSettings = contextButtonAction //
748 && !contextShowText->text().isEmpty() //
749 && contextTextRight->isChecked();
750 contextButtonTitle->setVisible(showButtonSettings);
751 contextShowText->setVisible(showButtonSettings);
752 if (showButtonSettings) {
753 contextShowText->setChecked(contextButtonAction->priority() >= QAction::NormalPriority);
754 }
755}
756
757void KToolBarPrivate::slotContextAboutToHide()
758{
759 // We have to unplug whatever slotContextAboutToShow plugged into the menu.
760 if (contextConfigureAction) {
761 context->removeAction(action: contextConfigureAction);
762 contextConfigureAction = nullptr;
763 }
764 if (contextToolBarAction) {
765 context->removeAction(action: contextToolBarAction);
766 contextToolBarAction = nullptr;
767 }
768 context->removeAction(action: contextLockAction);
769}
770
771void KToolBarPrivate::slotContextLeft()
772{
773 q->mainWindow()->addToolBar(area: Qt::LeftToolBarArea, toolbar: q);
774}
775
776void KToolBarPrivate::slotContextRight()
777{
778 q->mainWindow()->addToolBar(area: Qt::RightToolBarArea, toolbar: q);
779}
780
781void KToolBarPrivate::slotContextShowText()
782{
783 Q_ASSERT(contextButtonAction);
784 const QAction::Priority priority = contextShowText->isChecked() ? QAction::NormalPriority : QAction::LowPriority;
785 contextButtonAction->setPriority(priority);
786
787 // Find to which xml file and componentData the action belongs to
788 QString componentName;
789 QString filename;
790 KXMLGUIClient *client;
791 if (findAction(actionName: contextButtonAction->objectName(), clientOut: &client)) {
792 componentName = client->componentName();
793 filename = client->xmlFile();
794 }
795 if (filename.isEmpty()) {
796 componentName = QCoreApplication::applicationName();
797 filename = componentName + QLatin1String("ui.rc");
798 }
799
800 // Save the priority state of the action
801 const QString configFile = KXMLGUIFactory::readConfigFile(filename, componentName);
802
803 QDomDocument document;
804 document.setContent(data: configFile);
805 QDomElement elem = KXMLGUIFactory::actionPropertiesElement(doc&: document);
806 QDomElement actionElem = KXMLGUIFactory::findActionByName(elem, sName: contextButtonAction->objectName(), create: true);
807 actionElem.setAttribute(QStringLiteral("priority"), value: priority);
808 KXMLGUIFactory::saveConfigFile(doc: document, filename, componentName);
809}
810
811void KToolBarPrivate::slotContextTop()
812{
813 q->mainWindow()->addToolBar(area: Qt::TopToolBarArea, toolbar: q);
814}
815
816void KToolBarPrivate::slotContextBottom()
817{
818 q->mainWindow()->addToolBar(area: Qt::BottomToolBarArea, toolbar: q);
819}
820
821void KToolBarPrivate::slotContextIcons()
822{
823 q->setToolButtonStyle(Qt::ToolButtonIconOnly);
824 toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
825}
826
827void KToolBarPrivate::slotContextText()
828{
829 q->setToolButtonStyle(Qt::ToolButtonTextOnly);
830 toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
831}
832
833void KToolBarPrivate::slotContextTextUnder()
834{
835 q->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
836 toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
837}
838
839void KToolBarPrivate::slotContextTextRight()
840{
841 q->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
842 toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
843}
844
845void KToolBarPrivate::slotContextIconSize(QAction *action)
846{
847 if (action) {
848 auto it = std::find_if(first: m_contextIconSizes.cbegin(), last: m_contextIconSizes.cend(), pred: [action](const ContextIconInfo &info) {
849 return info.iconAction == action;
850 });
851 if (it != m_contextIconSizes.cend()) {
852 q->setIconDimensions(it->iconSize);
853 }
854 }
855}
856
857void KToolBarPrivate::slotLockToolBars(bool lock)
858{
859 q->setToolBarsLocked(lock);
860}
861
862KToolBar::KToolBar(QWidget *parent, bool isMainToolBar, bool readConfig)
863 : QToolBar(parent)
864 , d(new KToolBarPrivate(this))
865{
866 d->init(readConfig, isMainToolBar: isMainToolBar);
867
868 // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow
869 if (QMainWindow *mw = qobject_cast<QMainWindow *>(object: parent)) {
870 mw->addToolBar(toolbar: this);
871 }
872}
873
874KToolBar::KToolBar(const QString &objectName, QWidget *parent, bool readConfig)
875 : QToolBar(parent)
876 , d(new KToolBarPrivate(this))
877{
878 setObjectName(objectName);
879 // mainToolBar -> isMainToolBar = true -> buttonStyle is configurable
880 // others -> isMainToolBar = false -> ### hardcoded default for buttonStyle !!! should be configurable? -> hidden key added
881 d->init(readConfig, isMainToolBar: (objectName == QLatin1String("mainToolBar")));
882
883 // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow
884 if (QMainWindow *mw = qobject_cast<QMainWindow *>(object: parent)) {
885 mw->addToolBar(toolbar: this);
886 }
887}
888
889KToolBar::KToolBar(const QString &objectName, QMainWindow *parent, Qt::ToolBarArea area, bool newLine, bool isMainToolBar, bool readConfig)
890 : QToolBar(parent)
891 , d(new KToolBarPrivate(this))
892{
893 setObjectName(objectName);
894 d->init(readConfig, isMainToolBar: isMainToolBar);
895
896 if (newLine) {
897 mainWindow()->addToolBarBreak(area);
898 }
899
900 mainWindow()->addToolBar(area, toolbar: this);
901
902 if (newLine) {
903 mainWindow()->addToolBarBreak(area);
904 }
905}
906
907KToolBar::~KToolBar()
908{
909 delete d->contextLockAction;
910}
911
912void KToolBar::saveSettings(KConfigGroup &cg)
913{
914 Q_ASSERT(!cg.name().isEmpty());
915
916 const int currentIconSize = iconSize().width();
917 // qCDebug(DEBUG_KXMLGUI) << objectName() << currentIconSize << d->iconSizeSettings.toString() << "defaultValue=" << d->iconSizeSettings.defaultValue();
918 if (!cg.hasDefault(key: "IconSize") && currentIconSize == d->iconSizeSettings.defaultValue()) {
919 cg.revertToDefault(key: "IconSize");
920 d->iconSizeSettings[Level_UserSettings] = Unset;
921 } else {
922 cg.writeEntry(key: "IconSize", value: currentIconSize);
923 d->iconSizeSettings[Level_UserSettings] = currentIconSize;
924 }
925
926 const Qt::ToolButtonStyle currentToolButtonStyle = toolButtonStyle();
927 if (!cg.hasDefault(key: "ToolButtonStyle") && currentToolButtonStyle == d->toolButtonStyleSettings.defaultValue()) {
928 cg.revertToDefault(key: "ToolButtonStyle");
929 d->toolButtonStyleSettings[Level_UserSettings] = Unset;
930 } else {
931 cg.writeEntry(key: "ToolButtonStyle", value: d->toolButtonStyleToString(style: currentToolButtonStyle));
932 d->toolButtonStyleSettings[Level_UserSettings] = currentToolButtonStyle;
933 }
934}
935
936void KToolBar::addXMLGUIClient(KXMLGUIClient *client)
937{
938 d->xmlguiClients << client;
939}
940
941void KToolBar::removeXMLGUIClient(KXMLGUIClient *client)
942{
943 d->xmlguiClients.remove(value: client);
944}
945
946void KToolBar::contextMenuEvent(QContextMenuEvent *event)
947{
948 if (mainWindow()) {
949 QPointer<KToolBar> guard(this);
950 const QPoint globalPos = event->globalPos();
951 d->contextMenu(globalPos)->exec(pos: globalPos);
952
953 // "Configure Toolbars" recreates toolbars, so we might not exist anymore.
954 if (guard) {
955 d->slotContextAboutToHide();
956 }
957 return;
958 }
959
960 QToolBar::contextMenuEvent(event);
961}
962
963void KToolBar::loadState(const QDomElement &element)
964{
965 QMainWindow *mw = mainWindow();
966 if (!mw) {
967 return;
968 }
969
970 {
971 const QString &i18nText = KToolbarHelper::i18nToolBarName(element);
972 if (!i18nText.isEmpty()) {
973 setWindowTitle(i18nText);
974 }
975 }
976
977 /*
978 This method is called in order to load toolbar settings from XML.
979 However this can be used in two rather different cases:
980 - for the initial loading of the app's XML. In that case the settings
981 are only the defaults (Level_AppXML), the user's KConfig settings will override them
982
983 - for later re-loading when switching between parts in KXMLGUIFactory.
984 In that case the XML contains the final settings, not the defaults.
985 We do need the defaults, and the toolbar might have been completely
986 deleted and recreated meanwhile. So we store the app-default settings
987 into the XML.
988 */
989 bool loadingAppDefaults = true;
990 if (element.hasAttribute(QStringLiteral("tempXml"))) {
991 // this isn't the first time, so the app-xml defaults have been saved into the (in-memory) XML
992 loadingAppDefaults = false;
993 const QString iconSizeDefault = element.attribute(QStringLiteral("iconSizeDefault"));
994 if (!iconSizeDefault.isEmpty()) {
995 d->iconSizeSettings[Level_AppXML] = iconSizeDefault.toInt();
996 }
997 const QString toolButtonStyleDefault = element.attribute(QStringLiteral("toolButtonStyleDefault"));
998 if (!toolButtonStyleDefault.isEmpty()) {
999 d->toolButtonStyleSettings[Level_AppXML] = d->toolButtonStyleFromString(style: toolButtonStyleDefault);
1000 }
1001 } else {
1002 // loading app defaults
1003 bool newLine = false;
1004 QString attrNewLine = element.attribute(QStringLiteral("newline")).toLower();
1005 if (!attrNewLine.isEmpty()) {
1006 newLine = (attrNewLine == QLatin1String("true"));
1007 }
1008 if (newLine && mw) {
1009 mw->insertToolBarBreak(before: this);
1010 }
1011 }
1012
1013 int newIconSize = -1;
1014 if (element.hasAttribute(QStringLiteral("iconSize"))) {
1015 bool ok;
1016 newIconSize = element.attribute(QStringLiteral("iconSize")).trimmed().toInt(ok: &ok);
1017 if (!ok) {
1018 newIconSize = -1;
1019 }
1020 }
1021 if (newIconSize != -1) {
1022 d->iconSizeSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = newIconSize;
1023 }
1024
1025 const QString newToolButtonStyle = element.attribute(QStringLiteral("iconText"));
1026 if (!newToolButtonStyle.isEmpty()) {
1027 d->toolButtonStyleSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = d->toolButtonStyleFromString(style: newToolButtonStyle);
1028 }
1029
1030 bool hidden = false;
1031 {
1032 QString attrHidden = element.attribute(QStringLiteral("hidden")).toLower();
1033 if (!attrHidden.isEmpty()) {
1034 hidden = (attrHidden == QLatin1String("true"));
1035 }
1036 }
1037
1038 Qt::ToolBarArea pos = Qt::NoToolBarArea;
1039 {
1040 QString attrPosition = element.attribute(QStringLiteral("position")).toLower();
1041 if (!attrPosition.isEmpty()) {
1042 pos = KToolBarPrivate::positionFromString(position: attrPosition);
1043 }
1044 }
1045 if (pos != Qt::NoToolBarArea) {
1046 mw->addToolBar(area: pos, toolbar: this);
1047 }
1048
1049 setVisible(!hidden);
1050
1051 d->applyCurrentSettings();
1052}
1053
1054// Called when switching between xmlgui clients, in order to find any unsaved settings
1055// again when switching back to the current xmlgui client.
1056void KToolBar::saveState(QDomElement &current) const
1057{
1058 Q_ASSERT(!current.isNull());
1059
1060 current.setAttribute(QStringLiteral("tempXml"), QStringLiteral("true"));
1061
1062 current.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1"));
1063 current.setAttribute(QStringLiteral("position"), value: d->getPositionAsString().toLower());
1064 current.setAttribute(QStringLiteral("hidden"), value: isHidden() ? QStringLiteral("true") : QStringLiteral("false"));
1065
1066 const int currentIconSize = iconSize().width();
1067 if (currentIconSize == d->iconSizeSettings.defaultValue()) {
1068 current.removeAttribute(QStringLiteral("iconSize"));
1069 } else {
1070 current.setAttribute(QStringLiteral("iconSize"), value: iconSize().width());
1071 }
1072
1073 if (toolButtonStyle() == d->toolButtonStyleSettings.defaultValue()) {
1074 current.removeAttribute(QStringLiteral("iconText"));
1075 } else {
1076 current.setAttribute(QStringLiteral("iconText"), value: d->toolButtonStyleToString(style: toolButtonStyle()));
1077 }
1078
1079 // Note: if this method is used by more than KXMLGUIBuilder, e.g. to save XML settings to *disk*,
1080 // then the stuff below shouldn't always be done. This is not the case currently though.
1081 if (d->iconSizeSettings[Level_AppXML] != Unset) {
1082 current.setAttribute(QStringLiteral("iconSizeDefault"), value: d->iconSizeSettings[Level_AppXML]);
1083 }
1084 if (d->toolButtonStyleSettings[Level_AppXML] != Unset) {
1085 const Qt::ToolButtonStyle bs = static_cast<Qt::ToolButtonStyle>(d->toolButtonStyleSettings[Level_AppXML]);
1086 current.setAttribute(QStringLiteral("toolButtonStyleDefault"), value: d->toolButtonStyleToString(style: bs));
1087 }
1088}
1089
1090// called by KMainWindow::applyMainWindowSettings to read from the user settings
1091void KToolBar::applySettings(const KConfigGroup &cg)
1092{
1093 Q_ASSERT(!cg.name().isEmpty());
1094
1095 if (cg.hasKey(key: "IconSize")) {
1096 d->iconSizeSettings[Level_UserSettings] = cg.readEntry(key: "IconSize", defaultValue: 0);
1097 }
1098 if (cg.hasKey(key: "ToolButtonStyle")) {
1099 d->toolButtonStyleSettings[Level_UserSettings] = d->toolButtonStyleFromString(style: cg.readEntry(key: "ToolButtonStyle", aDefault: QString()));
1100 }
1101
1102 d->applyCurrentSettings();
1103}
1104
1105KMainWindow *KToolBar::mainWindow() const
1106{
1107 return qobject_cast<KMainWindow *>(object: const_cast<QObject *>(parent()));
1108}
1109
1110void KToolBar::setIconDimensions(int size)
1111{
1112 QToolBar::setIconSize(QSize(size, size));
1113 d->iconSizeSettings[Level_UserSettings] = size;
1114}
1115
1116int KToolBar::iconSizeDefault() const
1117{
1118 return KIconLoader::global()->currentSize(group: d->isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar);
1119}
1120
1121void KToolBar::slotMovableChanged(bool movable)
1122{
1123 if (movable && !KAuthorized::authorize(QStringLiteral("movable_toolbars"))) {
1124 setMovable(false);
1125 }
1126}
1127
1128void KToolBar::dragEnterEvent(QDragEnterEvent *event)
1129{
1130 if (toolBarsEditable() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction)
1131 && event->mimeData()->hasFormat(QStringLiteral("application/x-kde-action-list"))) {
1132 QByteArray data = event->mimeData()->data(QStringLiteral("application/x-kde-action-list"));
1133
1134 QDataStream stream(data);
1135
1136 QStringList actionNames;
1137
1138 stream >> actionNames;
1139
1140 const auto allCollections = KActionCollection::allCollections();
1141 for (const QString &actionName : std::as_const(t&: actionNames)) {
1142 for (KActionCollection *ac : allCollections) {
1143 QAction *newAction = ac->action(name: actionName);
1144 if (newAction) {
1145 d->actionsBeingDragged.append(t: newAction);
1146 break;
1147 }
1148 }
1149 }
1150
1151 if (!d->actionsBeingDragged.isEmpty()) {
1152 QAction *overAction = actionAt(p: event->position().toPoint());
1153
1154 QFrame *dropIndicatorWidget = new QFrame(this);
1155 dropIndicatorWidget->resize(w: 8, h: height() - 4);
1156 dropIndicatorWidget->setFrameShape(QFrame::VLine);
1157 dropIndicatorWidget->setLineWidth(3);
1158
1159 d->dropIndicatorAction = insertWidget(before: overAction, widget: dropIndicatorWidget);
1160
1161 insertAction(before: overAction, action: d->dropIndicatorAction);
1162
1163 event->acceptProposedAction();
1164 return;
1165 }
1166 }
1167
1168 QToolBar::dragEnterEvent(event);
1169}
1170
1171void KToolBar::dragMoveEvent(QDragMoveEvent *event)
1172{
1173 if (toolBarsEditable()) {
1174 Q_FOREVER {
1175 if (d->dropIndicatorAction) {
1176 QAction *overAction = nullptr;
1177 const auto actions = this->actions();
1178 for (QAction *action : actions) {
1179 // want to make it feel that half way across an action you're dropping on the other side of it
1180 QWidget *widget = widgetForAction(action);
1181 if (event->position().toPoint().x() < widget->pos().x() + (widget->width() / 2)) {
1182 overAction = action;
1183 break;
1184 }
1185 }
1186
1187 if (overAction != d->dropIndicatorAction) {
1188 // Check to see if the indicator is already in the right spot
1189 int dropIndicatorIndex = actions.indexOf(t: d->dropIndicatorAction);
1190 if (dropIndicatorIndex + 1 < actions.count()) {
1191 if (actions.at(i: dropIndicatorIndex + 1) == overAction) {
1192 break;
1193 }
1194 } else if (!overAction) {
1195 break;
1196 }
1197
1198 insertAction(before: overAction, action: d->dropIndicatorAction);
1199 }
1200
1201 event->accept();
1202 return;
1203 }
1204 break;
1205 }
1206 }
1207
1208 QToolBar::dragMoveEvent(event);
1209}
1210
1211void KToolBar::dragLeaveEvent(QDragLeaveEvent *event)
1212{
1213 // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely)
1214 delete d->dropIndicatorAction;
1215 d->dropIndicatorAction = nullptr;
1216 d->actionsBeingDragged.clear();
1217
1218 if (toolBarsEditable()) {
1219 event->accept();
1220 return;
1221 }
1222
1223 QToolBar::dragLeaveEvent(event);
1224}
1225
1226void KToolBar::dropEvent(QDropEvent *event)
1227{
1228 if (toolBarsEditable()) {
1229 for (QAction *action : std::as_const(t&: d->actionsBeingDragged)) {
1230 if (actions().contains(t: action)) {
1231 removeAction(action);
1232 }
1233 insertAction(before: d->dropIndicatorAction, action);
1234 }
1235 }
1236
1237 // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely)
1238 delete d->dropIndicatorAction;
1239 d->dropIndicatorAction = nullptr;
1240 d->actionsBeingDragged.clear();
1241
1242 if (toolBarsEditable()) {
1243 event->accept();
1244 return;
1245 }
1246
1247 QToolBar::dropEvent(event);
1248}
1249
1250void KToolBar::mousePressEvent(QMouseEvent *event)
1251{
1252 if (toolBarsEditable() && event->button() == Qt::LeftButton) {
1253 if (QAction *action = actionAt(p: event->position().toPoint())) {
1254 d->dragAction = action;
1255 d->dragStartPosition = event->position().toPoint();
1256 event->accept();
1257 return;
1258 }
1259 }
1260
1261 QToolBar::mousePressEvent(event);
1262}
1263
1264void KToolBar::mouseMoveEvent(QMouseEvent *event)
1265{
1266 if (!toolBarsEditable() || !d->dragAction) {
1267 QToolBar::mouseMoveEvent(event);
1268 return;
1269 }
1270
1271 if ((event->position().toPoint() - d->dragStartPosition).manhattanLength() < QApplication::startDragDistance()) {
1272 event->accept();
1273 return;
1274 }
1275
1276 QDrag *drag = new QDrag(this);
1277 QMimeData *mimeData = new QMimeData;
1278
1279 QByteArray data;
1280 {
1281 QDataStream stream(&data, QIODevice::WriteOnly);
1282
1283 QStringList actionNames;
1284 actionNames << d->dragAction->objectName();
1285
1286 stream << actionNames;
1287 }
1288
1289 mimeData->setData(QStringLiteral("application/x-kde-action-list"), data);
1290
1291 drag->setMimeData(mimeData);
1292
1293 Qt::DropAction dropAction = drag->exec(supportedActions: Qt::MoveAction);
1294
1295 if (dropAction == Qt::MoveAction) {
1296 // Only remove from this toolbar if it was moved to another toolbar
1297 // Otherwise the receiver moves it.
1298 if (drag->target() != this) {
1299 removeAction(action: d->dragAction);
1300 }
1301 }
1302
1303 d->dragAction = nullptr;
1304 event->accept();
1305}
1306
1307void KToolBar::mouseReleaseEvent(QMouseEvent *event)
1308{
1309 // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely)
1310 if (d->dragAction) {
1311 d->dragAction = nullptr;
1312 event->accept();
1313 return;
1314 }
1315
1316 QToolBar::mouseReleaseEvent(event);
1317}
1318
1319bool KToolBar::eventFilter(QObject *watched, QEvent *event)
1320{
1321 // Generate context menu events for disabled buttons too...
1322 if (event->type() == QEvent::MouseButtonPress) {
1323 QMouseEvent *me = static_cast<QMouseEvent *>(event);
1324 if (me->buttons() & Qt::RightButton) {
1325 if (QWidget *ww = qobject_cast<QWidget *>(o: watched)) {
1326 if (ww->parent() == this && !ww->isEnabled()) {
1327 QCoreApplication::postEvent(receiver: this,
1328 event: new QContextMenuEvent(QContextMenuEvent::Mouse, me->position().toPoint(), me->globalPosition().toPoint()));
1329 }
1330 }
1331 }
1332
1333 } else if (event->type() == QEvent::ParentChange) {
1334 // Make sure we're not leaving stale event filters around,
1335 // when a child is reparented somewhere else
1336 if (QWidget *ww = qobject_cast<QWidget *>(o: watched)) {
1337 if (!this->isAncestorOf(child: ww)) {
1338 // New parent is not a subwidget - remove event filter
1339 ww->removeEventFilter(obj: this);
1340 const auto children = ww->findChildren<QWidget *>();
1341 for (QWidget *child : children) {
1342 child->removeEventFilter(obj: this);
1343 }
1344 }
1345 }
1346 }
1347
1348 // Redirect mouse events to the toolbar when drag + drop editing is enabled
1349 if (toolBarsEditable()) {
1350 if (QWidget *ww = qobject_cast<QWidget *>(o: watched)) {
1351 switch (event->type()) {
1352 case QEvent::MouseButtonPress: {
1353 QMouseEvent *me = static_cast<QMouseEvent *>(event);
1354 QMouseEvent newEvent(me->type(),
1355 mapFromGlobal(ww->mapToGlobal(me->position().toPoint())),
1356 me->globalPosition().toPoint(),
1357 me->button(),
1358 me->buttons(),
1359 me->modifiers());
1360 mousePressEvent(event: &newEvent);
1361 return true;
1362 }
1363 case QEvent::MouseMove: {
1364 QMouseEvent *me = static_cast<QMouseEvent *>(event);
1365 QMouseEvent newEvent(me->type(),
1366 mapFromGlobal(ww->mapToGlobal(me->position().toPoint())),
1367 me->globalPosition().toPoint(),
1368 me->button(),
1369 me->buttons(),
1370 me->modifiers());
1371 mouseMoveEvent(event: &newEvent);
1372 return true;
1373 }
1374 case QEvent::MouseButtonRelease: {
1375 QMouseEvent *me = static_cast<QMouseEvent *>(event);
1376 QMouseEvent newEvent(me->type(),
1377 mapFromGlobal(ww->mapToGlobal(me->position().toPoint())),
1378 me->globalPosition().toPoint(),
1379 me->button(),
1380 me->buttons(),
1381 me->modifiers());
1382 mouseReleaseEvent(event: &newEvent);
1383 return true;
1384 }
1385 default:
1386 break;
1387 }
1388 }
1389 }
1390
1391 return QToolBar::eventFilter(watched, event);
1392}
1393
1394void KToolBar::actionEvent(QActionEvent *event)
1395{
1396 if (event->type() == QEvent::ActionRemoved) {
1397 QWidget *widget = widgetForAction(action: event->action());
1398 if (widget) {
1399 widget->removeEventFilter(obj: this);
1400
1401 const auto children = widget->findChildren<QWidget *>();
1402 for (QWidget *child : children) {
1403 child->removeEventFilter(obj: this);
1404 }
1405 }
1406 }
1407
1408 QToolBar::actionEvent(event);
1409
1410 if (event->type() == QEvent::ActionAdded) {
1411 QWidget *widget = widgetForAction(action: event->action());
1412 if (widget) {
1413 widget->installEventFilter(filterObj: this);
1414
1415 const auto children = widget->findChildren<QWidget *>();
1416 for (QWidget *child : children) {
1417 child->installEventFilter(filterObj: this);
1418 }
1419 // Center widgets that do not have any use for more space. See bug 165274
1420 if (!(widget->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag)
1421 // ... but do not center when using text besides icon in vertical toolbar. See bug 243196
1422 && !(orientation() == Qt::Vertical && toolButtonStyle() == Qt::ToolButtonTextBesideIcon)) {
1423 const int index = layout()->indexOf(widget);
1424 if (index != -1) {
1425 layout()->itemAt(index)->setAlignment(Qt::AlignJustify);
1426 }
1427 }
1428 }
1429 }
1430
1431 d->adjustSeparatorVisibility();
1432}
1433
1434bool KToolBar::toolBarsEditable()
1435{
1436 return KToolBarPrivate::s_editable;
1437}
1438
1439void KToolBar::setToolBarsEditable(bool editable)
1440{
1441 if (KToolBarPrivate::s_editable != editable) {
1442 KToolBarPrivate::s_editable = editable;
1443 }
1444}
1445
1446void KToolBar::setToolBarsLocked(bool locked)
1447{
1448 if (KToolBarPrivate::s_locked != locked) {
1449 KToolBarPrivate::s_locked = locked;
1450
1451 const auto windows = KMainWindow::memberList();
1452 for (KMainWindow *mw : windows) {
1453 const auto toolbars = mw->findChildren<KToolBar *>();
1454 for (KToolBar *toolbar : toolbars) {
1455 toolbar->d->setLocked(locked);
1456 }
1457 }
1458 }
1459}
1460
1461bool KToolBar::toolBarsLocked()
1462{
1463 return KToolBarPrivate::s_locked;
1464}
1465
1466void KToolBar::emitToolbarStyleChanged()
1467{
1468#ifdef WITH_QTDBUS
1469 QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"));
1470 QDBusConnection::sessionBus().send(message);
1471#endif
1472}
1473
1474#include "moc_ktoolbar.cpp"
1475

source code of kxmlgui/src/ktoolbar.cpp