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

source code of kxmlgui/src/ktoolbar.cpp