File indexing completed on 2024-04-14 14:30:04

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000 Reginald Stadlbauer <reggie@kde.org>
0004     SPDX-FileCopyrightText: 1997, 1998 Stephan Kulow <coolo@kde.org>
0005     SPDX-FileCopyrightText: 1997, 1998 Mark Donohoe <donohoe@kde.org>
0006     SPDX-FileCopyrightText: 1997, 1998 Sven Radej <radej@kde.org>
0007     SPDX-FileCopyrightText: 1997, 1998 Matthias Ettrich <ettrich@kde.org>
0008     SPDX-FileCopyrightText: 1999 Chris Schlaeger <cs@kde.org>
0009     SPDX-FileCopyrightText: 1999 Kurt Granroth <granroth@kde.org>
0010     SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
0011 
0012     SPDX-License-Identifier: LGPL-2.0-only
0013 */
0014 
0015 #include "ktoolbar.h"
0016 
0017 #include <QAction>
0018 #include <QActionGroup>
0019 #include <QApplication>
0020 #include <QDomElement>
0021 #include <QDrag>
0022 #include <QFrame>
0023 #include <QLayout>
0024 #include <QMenu>
0025 #include <QMimeData>
0026 #include <QMouseEvent>
0027 #include <QPointer>
0028 #ifdef QT_DBUS_LIB
0029 #include <QDBusConnection>
0030 #include <QDBusMessage>
0031 #endif
0032 
0033 #include <KAuthorized>
0034 #include <KConfig>
0035 #include <KConfigGroup>
0036 #include <KIconTheme>
0037 #include <KLocalizedString>
0038 #include <KSharedConfig>
0039 #include <KStandardAction>
0040 #include <KToggleAction>
0041 
0042 #include "kactioncollection.h"
0043 #include "kedittoolbar.h"
0044 #include "kxmlguifactory.h"
0045 #include "kxmlguiwindow.h"
0046 
0047 #include "ktoolbarhelper_p.h"
0048 
0049 /*
0050  Toolbar settings (e.g. icon size or toolButtonStyle)
0051  =====================================================
0052 
0053  We have the following stack of settings (in order of priority) :
0054    - user-specified settings (loaded/saved in KConfig)
0055    - developer-specified settings in the XMLGUI file (if using xmlgui) (cannot change at runtime)
0056    - KDE-global default (user-configurable; can change at runtime)
0057  and when switching between kparts, they are saved as xml in memory,
0058  which, in the unlikely case of no-kmainwindow-autosaving, could be
0059  different from the user-specified settings saved in KConfig and would have
0060  priority over it.
0061 
0062  So, in summary, without XML:
0063    Global config / User settings (loaded/saved in kconfig)
0064  and with XML:
0065    Global config / App-XML attributes / User settings (loaded/saved in kconfig)
0066 
0067  And all those settings (except the KDE-global defaults) have to be stored in memory
0068  since we cannot retrieve them at random points in time, not knowing the xml document
0069  nor config file that holds these settings. Hence the iconSizeSettings and toolButtonStyleSettings arrays.
0070 
0071  For instance, if you change the KDE-global default, whether this makes a change
0072  on a given toolbar depends on whether there are settings at Level_AppXML or Level_UserSettings.
0073  Only if there are no settings at those levels, should the change of KDEDefault make a difference.
0074 */
0075 enum SettingLevel { Level_KDEDefault, Level_AppXML, Level_UserSettings, NSettingLevels };
0076 enum { Unset = -1 };
0077 
0078 class KToolBarPrivate
0079 {
0080 public:
0081     KToolBarPrivate(KToolBar *qq)
0082         : q(qq)
0083         , isMainToolBar(false)
0084 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0085         , enableContext(true)
0086 #endif
0087         , unlockedMovable(true)
0088         , contextOrient(nullptr)
0089         , contextMode(nullptr)
0090         , contextSize(nullptr)
0091         , contextButtonTitle(nullptr)
0092         , contextShowText(nullptr)
0093         , contextButtonAction(nullptr)
0094         , contextTop(nullptr)
0095         , contextLeft(nullptr)
0096         , contextRight(nullptr)
0097         , contextBottom(nullptr)
0098         , contextIcons(nullptr)
0099         , contextTextRight(nullptr)
0100         , contextText(nullptr)
0101         , contextTextUnder(nullptr)
0102         , contextLockAction(nullptr)
0103         , dropIndicatorAction(nullptr)
0104         , context(nullptr)
0105         , dragAction(nullptr)
0106     {
0107     }
0108 
0109     void slotAppearanceChanged();
0110     void slotContextAboutToShow();
0111     void slotContextAboutToHide();
0112     void slotContextLeft();
0113     void slotContextRight();
0114     void slotContextShowText();
0115     void slotContextTop();
0116     void slotContextBottom();
0117     void slotContextIcons();
0118     void slotContextText();
0119     void slotContextTextRight();
0120     void slotContextTextUnder();
0121     void slotContextIconSize(QAction *action);
0122     void slotLockToolBars(bool lock);
0123 
0124     void init(bool readConfig = true, bool isMainToolBar = false);
0125     QString getPositionAsString() const;
0126     QMenu *contextMenu(const QPoint &globalPos);
0127     void setLocked(bool locked);
0128     void adjustSeparatorVisibility();
0129     void loadKDESettings();
0130     void applyCurrentSettings();
0131 
0132     QAction *findAction(const QString &actionName, KXMLGUIClient **client = nullptr) const;
0133 
0134     static Qt::ToolButtonStyle toolButtonStyleFromString(const QString &style);
0135     static QString toolButtonStyleToString(Qt::ToolButtonStyle);
0136     static Qt::ToolBarArea positionFromString(const QString &position);
0137     static Qt::ToolButtonStyle toolButtonStyleSetting();
0138 
0139     KToolBar *const q;
0140     bool isMainToolBar : 1;
0141 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0142     bool enableContext : 1;
0143 #endif
0144     bool unlockedMovable : 1;
0145     static bool s_editable;
0146     static bool s_locked;
0147 
0148     QSet<KXMLGUIClient *> xmlguiClients;
0149 
0150     QMenu *contextOrient;
0151     QMenu *contextMode;
0152     QMenu *contextSize;
0153 
0154     QAction *contextButtonTitle;
0155     QAction *contextShowText;
0156     QAction *contextButtonAction;
0157     QAction *contextTop;
0158     QAction *contextLeft;
0159     QAction *contextRight;
0160     QAction *contextBottom;
0161     QAction *contextIcons;
0162     QAction *contextTextRight;
0163     QAction *contextText;
0164     QAction *contextTextUnder;
0165     KToggleAction *contextLockAction;
0166 
0167     struct ContextIconInfo {
0168         QAction *iconAction = nullptr;
0169         int iconSize = 0;
0170     };
0171 
0172     std::vector<ContextIconInfo> m_contextIconSizes;
0173 
0174     class IntSetting
0175     {
0176     public:
0177         IntSetting()
0178         {
0179             for (int &value : values) {
0180                 value = Unset;
0181             }
0182         }
0183         int currentValue() const
0184         {
0185             int val = Unset;
0186             for (int value : values) {
0187                 if (value != Unset) {
0188                     val = value;
0189                 }
0190             }
0191             return val;
0192         }
0193         // Default value as far as the user is concerned is kde-global + app-xml.
0194         // If currentValue()==defaultValue() then nothing to write into kconfig.
0195         int defaultValue() const
0196         {
0197             int val = Unset;
0198             for (int level = 0; level < Level_UserSettings; ++level) {
0199                 if (values[level] != Unset) {
0200                     val = values[level];
0201                 }
0202             }
0203             return val;
0204         }
0205         QString toString() const
0206         {
0207             QString str;
0208             for (int value : values) {
0209                 str += QString::number(value) + QLatin1Char(' ');
0210             }
0211             return str;
0212         }
0213         int &operator[](int index)
0214         {
0215             return values[index];
0216         }
0217 
0218     private:
0219         int values[NSettingLevels];
0220     };
0221     IntSetting iconSizeSettings;
0222     IntSetting toolButtonStyleSettings; // either Qt::ToolButtonStyle or -1, hence "int".
0223 
0224     QList<QAction *> actionsBeingDragged;
0225     QAction *dropIndicatorAction;
0226 
0227     QMenu *context;
0228     QAction *dragAction;
0229     QPoint dragStartPosition;
0230 };
0231 
0232 bool KToolBarPrivate::s_editable = false;
0233 bool KToolBarPrivate::s_locked = true;
0234 
0235 void KToolBarPrivate::init(bool readConfig, bool _isMainToolBar)
0236 {
0237     isMainToolBar = _isMainToolBar;
0238     loadKDESettings();
0239 
0240     // also read in our configurable settings (for non-xmlgui toolbars)
0241     if (readConfig) {
0242         KConfigGroup cg(KSharedConfig::openConfig(), QString());
0243         q->applySettings(cg);
0244     }
0245 
0246     if (q->mainWindow()) {
0247         // Get notified when settings change
0248         QObject::connect(q, &QToolBar::allowedAreasChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0249         QObject::connect(q, &QToolBar::iconSizeChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0250         QObject::connect(q, &QToolBar::toolButtonStyleChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0251         QObject::connect(q, &QToolBar::movableChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0252         QObject::connect(q, &QToolBar::orientationChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0253     }
0254 
0255     if (!KAuthorized::authorize(QStringLiteral("movable_toolbars"))) {
0256         q->setMovable(false);
0257     } else {
0258         q->setMovable(!KToolBar::toolBarsLocked());
0259     }
0260 
0261     q->toggleViewAction()->setEnabled(KAuthorized::authorizeAction(QStringLiteral("options_show_toolbar")));
0262 
0263     QObject::connect(q, &QToolBar::movableChanged, q, &KToolBar::slotMovableChanged);
0264 
0265     q->setAcceptDrops(true);
0266 
0267 #ifdef QT_DBUS_LIB
0268     QDBusConnection::sessionBus()
0269         .connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), q, SLOT(slotAppearanceChanged()));
0270 #endif
0271     QObject::connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, q, [this]() {
0272         slotAppearanceChanged();
0273     });
0274 }
0275 
0276 QString KToolBarPrivate::getPositionAsString() const
0277 {
0278     if (!q->mainWindow()) {
0279         return QStringLiteral("None");
0280     }
0281     // get all of the stuff to save
0282     switch (q->mainWindow()->toolBarArea(const_cast<KToolBar *>(q))) {
0283     case Qt::BottomToolBarArea:
0284         return QStringLiteral("Bottom");
0285     case Qt::LeftToolBarArea:
0286         return QStringLiteral("Left");
0287     case Qt::RightToolBarArea:
0288         return QStringLiteral("Right");
0289     case Qt::TopToolBarArea:
0290     default:
0291         return QStringLiteral("Top");
0292     }
0293 }
0294 
0295 QMenu *KToolBarPrivate::contextMenu(const QPoint &globalPos)
0296 {
0297     if (!context) {
0298         context = new QMenu(q);
0299         context->setIcon(QIcon::fromTheme(QStringLiteral("configure-toolbars")));
0300         context->setTitle(i18nc("@title:menu", "Toolbar Settings"));
0301 
0302         contextButtonTitle = context->addSection(i18nc("@title:menu", "Show Text"));
0303         contextShowText = context->addAction(QString(), q, [this]() {
0304             slotContextShowText();
0305         });
0306 
0307         context->addSection(i18nc("@title:menu", "Toolbar Settings"));
0308 
0309         contextOrient = new QMenu(i18nc("Toolbar orientation", "Orientation"), context);
0310 
0311         contextTop = contextOrient->addAction(i18nc("toolbar position string", "Top"), q, [this]() {
0312             slotContextTop();
0313         });
0314         contextTop->setChecked(true);
0315         contextLeft = contextOrient->addAction(i18nc("toolbar position string", "Left"), q, [this]() {
0316             slotContextLeft();
0317         });
0318         contextRight = contextOrient->addAction(i18nc("toolbar position string", "Right"), q, [this]() {
0319             slotContextRight();
0320         });
0321         contextBottom = contextOrient->addAction(i18nc("toolbar position string", "Bottom"), q, [this]() {
0322             slotContextBottom();
0323         });
0324 
0325         QActionGroup *positionGroup = new QActionGroup(contextOrient);
0326         const auto orientActions = contextOrient->actions();
0327         for (QAction *action : orientActions) {
0328             action->setActionGroup(positionGroup);
0329             action->setCheckable(true);
0330         }
0331 
0332         contextMode = new QMenu(i18n("Text Position"), context);
0333 
0334         contextIcons = contextMode->addAction(i18nc("@item:inmenu", "Icons Only"), q, [this]() {
0335             slotContextIcons();
0336         });
0337         contextText = contextMode->addAction(i18nc("@item:inmenu", "Text Only"), q, [this]() {
0338             slotContextText();
0339         });
0340         contextTextRight = contextMode->addAction(i18nc("@item:inmenu", "Text Alongside Icons"), q, [this]() {
0341             slotContextTextRight();
0342         });
0343         contextTextUnder = contextMode->addAction(i18nc("@item:inmenu", "Text Under Icons"), q, [this]() {
0344             slotContextTextUnder();
0345         });
0346 
0347         QActionGroup *textGroup = new QActionGroup(contextMode);
0348         const auto modeActions = contextMode->actions();
0349         for (QAction *action : modeActions) {
0350             action->setActionGroup(textGroup);
0351             action->setCheckable(true);
0352         }
0353 
0354         contextSize = new QMenu(i18n("Icon Size"), context);
0355 
0356         auto *act = contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"));
0357         q->connect(act, &QAction::triggered, q, [this, act]() {
0358             slotContextIconSize(act);
0359         });
0360         m_contextIconSizes.push_back({act, iconSizeSettings.defaultValue()});
0361 
0362         // Query the current theme for available sizes
0363         KIconTheme *theme = KIconLoader::global()->theme();
0364         QList<int> avSizes;
0365         if (theme) {
0366             avSizes = theme->querySizes(isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar);
0367         }
0368 
0369         std::sort(avSizes.begin(), avSizes.end());
0370 
0371         if (avSizes.count() < 10) {
0372             // Fixed or threshold type icons
0373             for (int it : std::as_const(avSizes)) {
0374                 QString text;
0375                 if (it < 19) {
0376                     text = i18n("Small (%1x%2)", it, it);
0377                 } else if (it < 25) {
0378                     text = i18n("Medium (%1x%2)", it, it);
0379                 } else if (it < 35) {
0380                     text = i18n("Large (%1x%2)", it, it);
0381                 } else {
0382                     text = i18n("Huge (%1x%2)", it, it);
0383                 }
0384 
0385                 auto *act = contextSize->addAction(text);
0386                 q->connect(act, &QAction::triggered, q, [this, act]() {
0387                     slotContextIconSize(act);
0388                 });
0389                 m_contextIconSizes.push_back({act, it});
0390             }
0391         } else {
0392             // Scalable icons.
0393             const int progression[] = {16, 22, 32, 48, 64, 96, 128, 192, 256};
0394 
0395             for (int p : progression) {
0396                 for (int it : std::as_const(avSizes)) {
0397                     if (it >= p) {
0398                         QString text;
0399                         if (it < 19) {
0400                             text = i18n("Small (%1x%2)", it, it);
0401                         } else if (it < 25) {
0402                             text = i18n("Medium (%1x%2)", it, it);
0403                         } else if (it < 35) {
0404                             text = i18n("Large (%1x%2)", it, it);
0405                         } else {
0406                             text = i18n("Huge (%1x%2)", it, it);
0407                         }
0408 
0409                         auto *act = contextSize->addAction(text);
0410                         q->connect(act, &QAction::triggered, q, [this, act]() {
0411                             slotContextIconSize(act);
0412                         });
0413                         m_contextIconSizes.push_back({act, it});
0414                         break;
0415                     }
0416                 }
0417             }
0418         }
0419 
0420         QActionGroup *sizeGroup = new QActionGroup(contextSize);
0421         const auto sizeActions = contextSize->actions();
0422         for (QAction *action : sizeActions) {
0423             action->setActionGroup(sizeGroup);
0424             action->setCheckable(true);
0425         }
0426 
0427         if (!q->toolBarsLocked() && !q->isMovable()) {
0428             unlockedMovable = false;
0429         }
0430 
0431         delete contextLockAction;
0432         contextLockAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("system-lock-screen")), i18n("Lock Toolbar Positions"), q);
0433         contextLockAction->setChecked(q->toolBarsLocked());
0434         QObject::connect(contextLockAction, &KToggleAction::toggled, q, [this](bool checked) {
0435             slotLockToolBars(checked);
0436         });
0437 
0438         // Now add the actions to the menu
0439         context->addMenu(contextMode);
0440         context->addMenu(contextSize);
0441         context->addMenu(contextOrient);
0442         context->addSeparator();
0443 
0444         QObject::connect(context, &QMenu::aboutToShow, q, [this]() {
0445             slotContextAboutToShow();
0446         });
0447     }
0448 
0449     contextButtonAction = q->actionAt(q->mapFromGlobal(globalPos));
0450     if (contextButtonAction) {
0451         contextShowText->setText(contextButtonAction->text());
0452         contextShowText->setIcon(contextButtonAction->icon());
0453         contextShowText->setCheckable(true);
0454     }
0455 
0456     contextOrient->menuAction()->setVisible(!q->toolBarsLocked());
0457     // Unplugging a submenu from abouttohide leads to the popupmenu floating around
0458     // So better simply call that code from after exec() returns (DF)
0459     // connect(context, SIGNAL(aboutToHide()), this, SLOT(slotContextAboutToHide()));
0460 
0461     // For tool buttons with delay popup or menu button popup (split button)
0462     // show the actions of that button for ease of access.
0463     // (no need to wait for the menu to open or aim at the arrow)
0464     if (auto *contextToolButton = qobject_cast<QToolButton *>(q->widgetForAction(contextButtonAction))) {
0465         if (contextToolButton->popupMode() == QToolButton::DelayedPopup || contextToolButton->popupMode() == QToolButton::MenuButtonPopup) {
0466             if (auto *actionMenu = contextButtonAction->menu()) {
0467                 // In case it is populated on demand
0468                 Q_EMIT actionMenu->aboutToShow();
0469 
0470                 auto *contextMenu = new QMenu(q);
0471                 contextMenu->setAttribute(Qt::WA_DeleteOnClose);
0472 
0473                 const auto actions = actionMenu->actions();
0474                 if (!actions.isEmpty()) {
0475                     for (QAction *action : actions) {
0476                         contextMenu->addAction(action);
0477                     }
0478 
0479                     // Now add the configure actions as submenu
0480                     contextMenu->addSeparator();
0481                     contextMenu->addMenu(context);
0482                     return contextMenu;
0483                 }
0484             }
0485         }
0486     }
0487 
0488     return context;
0489 }
0490 
0491 void KToolBarPrivate::setLocked(bool locked)
0492 {
0493     if (unlockedMovable) {
0494         q->setMovable(!locked);
0495     }
0496 }
0497 
0498 void KToolBarPrivate::adjustSeparatorVisibility()
0499 {
0500     bool visibleNonSeparator = false;
0501     int separatorToShow = -1;
0502 
0503     for (int index = 0; index < q->actions().count(); ++index) {
0504         QAction *action = q->actions().at(index);
0505         if (action->isSeparator()) {
0506             if (visibleNonSeparator) {
0507                 separatorToShow = index;
0508                 visibleNonSeparator = false;
0509             } else {
0510                 action->setVisible(false);
0511             }
0512         } else if (!visibleNonSeparator) {
0513             if (action->isVisible()) {
0514                 visibleNonSeparator = true;
0515                 if (separatorToShow != -1) {
0516                     q->actions().at(separatorToShow)->setVisible(true);
0517                     separatorToShow = -1;
0518                 }
0519             }
0520         }
0521     }
0522 
0523     if (separatorToShow != -1) {
0524         q->actions().at(separatorToShow)->setVisible(false);
0525     }
0526 }
0527 
0528 Qt::ToolButtonStyle KToolBarPrivate::toolButtonStyleFromString(const QString &_style)
0529 {
0530     QString style = _style.toLower();
0531     if (style == QLatin1String("textbesideicon") || style == QLatin1String("icontextright")) {
0532         return Qt::ToolButtonTextBesideIcon;
0533     } else if (style == QLatin1String("textundericon") || style == QLatin1String("icontextbottom")) {
0534         return Qt::ToolButtonTextUnderIcon;
0535     } else if (style == QLatin1String("textonly")) {
0536         return Qt::ToolButtonTextOnly;
0537     } else {
0538         return Qt::ToolButtonIconOnly;
0539     }
0540 }
0541 
0542 QString KToolBarPrivate::toolButtonStyleToString(Qt::ToolButtonStyle style)
0543 {
0544     switch (style) {
0545     case Qt::ToolButtonIconOnly:
0546     default:
0547         return QStringLiteral("IconOnly");
0548     case Qt::ToolButtonTextBesideIcon:
0549         return QStringLiteral("TextBesideIcon");
0550     case Qt::ToolButtonTextOnly:
0551         return QStringLiteral("TextOnly");
0552     case Qt::ToolButtonTextUnderIcon:
0553         return QStringLiteral("TextUnderIcon");
0554     }
0555 }
0556 
0557 Qt::ToolBarArea KToolBarPrivate::positionFromString(const QString &position)
0558 {
0559     Qt::ToolBarArea newposition = Qt::TopToolBarArea;
0560     if (position == QLatin1String("left")) {
0561         newposition = Qt::LeftToolBarArea;
0562     } else if (position == QLatin1String("bottom")) {
0563         newposition = Qt::BottomToolBarArea;
0564     } else if (position == QLatin1String("right")) {
0565         newposition = Qt::RightToolBarArea;
0566     } else if (position == QLatin1String("none")) {
0567         newposition = Qt::NoToolBarArea;
0568     }
0569     return newposition;
0570 }
0571 
0572 // Global setting was changed
0573 void KToolBarPrivate::slotAppearanceChanged()
0574 {
0575     loadKDESettings();
0576     applyCurrentSettings();
0577 }
0578 
0579 Qt::ToolButtonStyle KToolBarPrivate::toolButtonStyleSetting()
0580 {
0581     KConfigGroup group(KSharedConfig::openConfig(), "Toolbar style");
0582     const QString fallback = KToolBarPrivate::toolButtonStyleToString(Qt::ToolButtonTextBesideIcon);
0583     return KToolBarPrivate::toolButtonStyleFromString(group.readEntry("ToolButtonStyle", fallback));
0584 }
0585 
0586 void KToolBarPrivate::loadKDESettings()
0587 {
0588     iconSizeSettings[Level_KDEDefault] = q->iconSizeDefault();
0589 
0590     if (isMainToolBar) {
0591         toolButtonStyleSettings[Level_KDEDefault] = toolButtonStyleSetting();
0592     } else {
0593         const QString fallBack = toolButtonStyleToString(Qt::ToolButtonTextBesideIcon);
0594         /**
0595           TODO: if we get complaints about text beside icons on small screens,
0596                 try the following code out on such systems - aseigo.
0597         // if we are on a small screen with a non-landscape ratio, then
0598         // we revert to text under icons since width is probably not our
0599         // friend in such cases
0600         QDesktopWidget *desktop = QApplication::desktop();
0601         QRect screenGeom = desktop->screenGeometry(desktop->primaryScreen());
0602         qreal ratio = screenGeom.width() / qreal(screenGeom.height());
0603 
0604         if (screenGeom.width() < 1024 && ratio <= 1.4) {
0605             fallBack = "TextUnderIcon";
0606         }
0607         **/
0608 
0609         KConfigGroup group(KSharedConfig::openConfig(), "Toolbar style");
0610         const QString value = group.readEntry("ToolButtonStyleOtherToolbars", fallBack);
0611         toolButtonStyleSettings[Level_KDEDefault] = KToolBarPrivate::toolButtonStyleFromString(value);
0612     }
0613 }
0614 
0615 // Call this after changing something in d->iconSizeSettings or d->toolButtonStyleSettings
0616 void KToolBarPrivate::applyCurrentSettings()
0617 {
0618     // qCDebug(DEBUG_KXMLGUI) << q->objectName() << "iconSizeSettings:" << iconSizeSettings.toString() << "->" << iconSizeSettings.currentValue();
0619     const int currentIconSize = iconSizeSettings.currentValue();
0620     q->setIconSize(QSize(currentIconSize, currentIconSize));
0621     // qCDebug(DEBUG_KXMLGUI) << q->objectName() << "toolButtonStyleSettings:" << toolButtonStyleSettings.toString() << "->" <<
0622     // toolButtonStyleSettings.currentValue();
0623     q->setToolButtonStyle(static_cast<Qt::ToolButtonStyle>(toolButtonStyleSettings.currentValue()));
0624 
0625     // And remember to save the new look later
0626     KMainWindow *kmw = q->mainWindow();
0627     if (kmw) {
0628         kmw->setSettingsDirty();
0629     }
0630 }
0631 
0632 QAction *KToolBarPrivate::findAction(const QString &actionName, KXMLGUIClient **clientOut) const
0633 {
0634     for (KXMLGUIClient *client : xmlguiClients) {
0635         QAction *action = client->actionCollection()->action(actionName);
0636         if (action) {
0637             if (clientOut) {
0638                 *clientOut = client;
0639             }
0640             return action;
0641         }
0642     }
0643     return nullptr;
0644 }
0645 
0646 void KToolBarPrivate::slotContextAboutToShow()
0647 {
0648     /**
0649      * The idea here is to reuse the "static" part of the menu to save time.
0650      * But the "Toolbars" action is dynamic (can be a single action or a submenu)
0651      * and ToolBarHandler::setupActions() deletes it, so better not keep it around.
0652      * So we currently plug/unplug the last two actions of the menu.
0653      * Another way would be to keep around the actions and plug them all into a (new each time) popupmenu.
0654      */
0655 
0656     KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(q->mainWindow());
0657 
0658     // try to find "configure toolbars" action
0659     QAction *configureAction = nullptr;
0660     const char *actionName = KStandardAction::name(KStandardAction::ConfigureToolbars);
0661     configureAction = findAction(QLatin1String(actionName));
0662 
0663     if (!configureAction && kmw) {
0664         configureAction = kmw->actionCollection()->action(QLatin1String(actionName));
0665     }
0666 
0667     if (configureAction) {
0668         context->addAction(configureAction);
0669     }
0670 
0671     context->addAction(contextLockAction);
0672 
0673     if (kmw) {
0674         kmw->setupToolbarMenuActions();
0675         // Only allow hiding a toolbar if the action is also plugged somewhere else (e.g. menubar)
0676         QAction *tbAction = kmw->toolBarMenuAction();
0677         if (!q->toolBarsLocked() && tbAction && !tbAction->associatedWidgets().isEmpty()) {
0678             context->addAction(tbAction);
0679         }
0680     }
0681 
0682     KEditToolBar::setGlobalDefaultToolBar(q->QObject::objectName().toLatin1().constData());
0683 
0684     // Check the actions that should be checked
0685     switch (q->toolButtonStyle()) {
0686     case Qt::ToolButtonIconOnly:
0687     default:
0688         contextIcons->setChecked(true);
0689         break;
0690     case Qt::ToolButtonTextBesideIcon:
0691         contextTextRight->setChecked(true);
0692         break;
0693     case Qt::ToolButtonTextOnly:
0694         contextText->setChecked(true);
0695         break;
0696     case Qt::ToolButtonTextUnderIcon:
0697         contextTextUnder->setChecked(true);
0698         break;
0699     }
0700 
0701     auto it = std::find_if(m_contextIconSizes.cbegin(), m_contextIconSizes.cend(), [this](const ContextIconInfo &info) {
0702         return info.iconSize == q->iconSize().width();
0703     });
0704     if (it != m_contextIconSizes.cend()) {
0705         it->iconAction->setChecked(true);
0706     }
0707 
0708     switch (q->mainWindow()->toolBarArea(q)) {
0709     case Qt::BottomToolBarArea:
0710         contextBottom->setChecked(true);
0711         break;
0712     case Qt::LeftToolBarArea:
0713         contextLeft->setChecked(true);
0714         break;
0715     case Qt::RightToolBarArea:
0716         contextRight->setChecked(true);
0717         break;
0718     default:
0719     case Qt::TopToolBarArea:
0720         contextTop->setChecked(true);
0721         break;
0722     }
0723 
0724     const bool showButtonSettings = contextButtonAction //
0725         && !contextShowText->text().isEmpty() //
0726         && contextTextRight->isChecked();
0727     contextButtonTitle->setVisible(showButtonSettings);
0728     contextShowText->setVisible(showButtonSettings);
0729     if (showButtonSettings) {
0730         contextShowText->setChecked(contextButtonAction->priority() >= QAction::NormalPriority);
0731     }
0732 }
0733 
0734 void KToolBarPrivate::slotContextAboutToHide()
0735 {
0736     // We have to unplug whatever slotContextAboutToShow plugged into the menu.
0737     // Unplug the toolbar menu action
0738     KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(q->mainWindow());
0739     if (kmw && kmw->toolBarMenuAction()) {
0740         if (kmw->toolBarMenuAction()->associatedWidgets().count() > 1) {
0741             context->removeAction(kmw->toolBarMenuAction());
0742         }
0743     }
0744 
0745     // Unplug the configure toolbars action too, since it's afterwards anyway
0746     QAction *configureAction = nullptr;
0747     const char *actionName = KStandardAction::name(KStandardAction::ConfigureToolbars);
0748     configureAction = findAction(QLatin1String(actionName));
0749 
0750     if (!configureAction && kmw) {
0751         configureAction = kmw->actionCollection()->action(QLatin1String(actionName));
0752     }
0753 
0754     if (configureAction) {
0755         context->removeAction(configureAction);
0756     }
0757 
0758     context->removeAction(contextLockAction);
0759 }
0760 
0761 void KToolBarPrivate::slotContextLeft()
0762 {
0763     q->mainWindow()->addToolBar(Qt::LeftToolBarArea, q);
0764 }
0765 
0766 void KToolBarPrivate::slotContextRight()
0767 {
0768     q->mainWindow()->addToolBar(Qt::RightToolBarArea, q);
0769 }
0770 
0771 void KToolBarPrivate::slotContextShowText()
0772 {
0773     Q_ASSERT(contextButtonAction);
0774     const QAction::Priority priority = contextShowText->isChecked() ? QAction::NormalPriority : QAction::LowPriority;
0775     contextButtonAction->setPriority(priority);
0776 
0777     // Find to which xml file and componentData the action belongs to
0778     QString componentName;
0779     QString filename;
0780     KXMLGUIClient *client;
0781     if (findAction(contextButtonAction->objectName(), &client)) {
0782         componentName = client->componentName();
0783         filename = client->xmlFile();
0784     }
0785     if (filename.isEmpty()) {
0786         componentName = QCoreApplication::applicationName();
0787         filename = componentName + QLatin1String("ui.rc");
0788     }
0789 
0790     // Save the priority state of the action
0791     const QString configFile = KXMLGUIFactory::readConfigFile(filename, componentName);
0792 
0793     QDomDocument document;
0794     document.setContent(configFile);
0795     QDomElement elem = KXMLGUIFactory::actionPropertiesElement(document);
0796     QDomElement actionElem = KXMLGUIFactory::findActionByName(elem, contextButtonAction->objectName(), true);
0797     actionElem.setAttribute(QStringLiteral("priority"), priority);
0798     KXMLGUIFactory::saveConfigFile(document, filename, componentName);
0799 }
0800 
0801 void KToolBarPrivate::slotContextTop()
0802 {
0803     q->mainWindow()->addToolBar(Qt::TopToolBarArea, q);
0804 }
0805 
0806 void KToolBarPrivate::slotContextBottom()
0807 {
0808     q->mainWindow()->addToolBar(Qt::BottomToolBarArea, q);
0809 }
0810 
0811 void KToolBarPrivate::slotContextIcons()
0812 {
0813     q->setToolButtonStyle(Qt::ToolButtonIconOnly);
0814     toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
0815 }
0816 
0817 void KToolBarPrivate::slotContextText()
0818 {
0819     q->setToolButtonStyle(Qt::ToolButtonTextOnly);
0820     toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
0821 }
0822 
0823 void KToolBarPrivate::slotContextTextUnder()
0824 {
0825     q->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
0826     toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
0827 }
0828 
0829 void KToolBarPrivate::slotContextTextRight()
0830 {
0831     q->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0832     toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
0833 }
0834 
0835 void KToolBarPrivate::slotContextIconSize(QAction *action)
0836 {
0837     if (action) {
0838         auto it = std::find_if(m_contextIconSizes.cbegin(), m_contextIconSizes.cend(), [action](const ContextIconInfo &info) {
0839             return info.iconAction == action;
0840         });
0841         if (it != m_contextIconSizes.cend()) {
0842             q->setIconDimensions(it->iconSize);
0843         }
0844     }
0845 }
0846 
0847 void KToolBarPrivate::slotLockToolBars(bool lock)
0848 {
0849     q->setToolBarsLocked(lock);
0850 }
0851 
0852 KToolBar::KToolBar(QWidget *parent, bool isMainToolBar, bool readConfig)
0853     : QToolBar(parent)
0854     , d(new KToolBarPrivate(this))
0855 {
0856     d->init(readConfig, isMainToolBar);
0857 
0858     // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow
0859     if (QMainWindow *mw = qobject_cast<QMainWindow *>(parent)) {
0860         mw->addToolBar(this);
0861     }
0862 }
0863 
0864 KToolBar::KToolBar(const QString &objectName, QWidget *parent, bool readConfig)
0865     : QToolBar(parent)
0866     , d(new KToolBarPrivate(this))
0867 {
0868     setObjectName(objectName);
0869     // mainToolBar -> isMainToolBar = true  -> buttonStyle is configurable
0870     // others      -> isMainToolBar = false -> ### hardcoded default for buttonStyle !!! should be configurable? -> hidden key added
0871     d->init(readConfig, (objectName == QLatin1String("mainToolBar")));
0872 
0873     // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow
0874     if (QMainWindow *mw = qobject_cast<QMainWindow *>(parent)) {
0875         mw->addToolBar(this);
0876     }
0877 }
0878 
0879 KToolBar::KToolBar(const QString &objectName, QMainWindow *parent, Qt::ToolBarArea area, bool newLine, bool isMainToolBar, bool readConfig)
0880     : QToolBar(parent)
0881     , d(new KToolBarPrivate(this))
0882 {
0883     setObjectName(objectName);
0884     d->init(readConfig, isMainToolBar);
0885 
0886     if (newLine) {
0887         mainWindow()->addToolBarBreak(area);
0888     }
0889 
0890     mainWindow()->addToolBar(area, this);
0891 
0892     if (newLine) {
0893         mainWindow()->addToolBarBreak(area);
0894     }
0895 }
0896 
0897 KToolBar::~KToolBar()
0898 {
0899     delete d->contextLockAction;
0900 }
0901 
0902 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0903 void KToolBar::setContextMenuEnabled(bool enable)
0904 {
0905     d->enableContext = enable;
0906 }
0907 #endif
0908 
0909 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0910 bool KToolBar::contextMenuEnabled() const
0911 {
0912     return d->enableContext;
0913 }
0914 #endif
0915 
0916 void KToolBar::saveSettings(KConfigGroup &cg)
0917 {
0918     Q_ASSERT(!cg.name().isEmpty());
0919 
0920     const int currentIconSize = iconSize().width();
0921     // qCDebug(DEBUG_KXMLGUI) << objectName() << currentIconSize << d->iconSizeSettings.toString() << "defaultValue=" << d->iconSizeSettings.defaultValue();
0922     if (!cg.hasDefault("IconSize") && currentIconSize == d->iconSizeSettings.defaultValue()) {
0923         cg.revertToDefault("IconSize");
0924         d->iconSizeSettings[Level_UserSettings] = Unset;
0925     } else {
0926         cg.writeEntry("IconSize", currentIconSize);
0927         d->iconSizeSettings[Level_UserSettings] = currentIconSize;
0928     }
0929 
0930     const Qt::ToolButtonStyle currentToolButtonStyle = toolButtonStyle();
0931     if (!cg.hasDefault("ToolButtonStyle") && currentToolButtonStyle == d->toolButtonStyleSettings.defaultValue()) {
0932         cg.revertToDefault("ToolButtonStyle");
0933         d->toolButtonStyleSettings[Level_UserSettings] = Unset;
0934     } else {
0935         cg.writeEntry("ToolButtonStyle", d->toolButtonStyleToString(currentToolButtonStyle));
0936         d->toolButtonStyleSettings[Level_UserSettings] = currentToolButtonStyle;
0937     }
0938 }
0939 
0940 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0941 void KToolBar::setXMLGUIClient(KXMLGUIClient *client)
0942 {
0943     d->xmlguiClients.clear();
0944     d->xmlguiClients << client;
0945 }
0946 #endif
0947 
0948 void KToolBar::addXMLGUIClient(KXMLGUIClient *client)
0949 {
0950     d->xmlguiClients << client;
0951 }
0952 
0953 void KToolBar::removeXMLGUIClient(KXMLGUIClient *client)
0954 {
0955     d->xmlguiClients.remove(client);
0956 }
0957 
0958 void KToolBar::contextMenuEvent(QContextMenuEvent *event)
0959 {
0960 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0961     if (mainWindow() && d->enableContext) {
0962         QPointer<KToolBar> guard(this);
0963         const QPoint globalPos = event->globalPos();
0964         d->contextMenu(globalPos)->exec(globalPos);
0965 
0966         // "Configure Toolbars" recreates toolbars, so we might not exist anymore.
0967         if (guard) {
0968             d->slotContextAboutToHide();
0969         }
0970         return;
0971     }
0972 #endif
0973 
0974     QToolBar::contextMenuEvent(event);
0975 }
0976 
0977 void KToolBar::loadState(const QDomElement &element)
0978 {
0979     QMainWindow *mw = mainWindow();
0980     if (!mw) {
0981         return;
0982     }
0983 
0984     {
0985         const QString &i18nText = KToolbarHelper::i18nToolBarName(element);
0986         if (!i18nText.isEmpty()) {
0987             setWindowTitle(i18nText);
0988         }
0989     }
0990 
0991     /*
0992       This method is called in order to load toolbar settings from XML.
0993       However this can be used in two rather different cases:
0994       - for the initial loading of the app's XML. In that case the settings
0995       are only the defaults (Level_AppXML), the user's KConfig settings will override them
0996 
0997       - for later re-loading when switching between parts in KXMLGUIFactory.
0998       In that case the XML contains the final settings, not the defaults.
0999       We do need the defaults, and the toolbar might have been completely
1000       deleted and recreated meanwhile. So we store the app-default settings
1001       into the XML.
1002     */
1003     bool loadingAppDefaults = true;
1004     if (element.hasAttribute(QStringLiteral("tempXml"))) {
1005         // this isn't the first time, so the app-xml defaults have been saved into the (in-memory) XML
1006         loadingAppDefaults = false;
1007         const QString iconSizeDefault = element.attribute(QStringLiteral("iconSizeDefault"));
1008         if (!iconSizeDefault.isEmpty()) {
1009             d->iconSizeSettings[Level_AppXML] = iconSizeDefault.toInt();
1010         }
1011         const QString toolButtonStyleDefault = element.attribute(QStringLiteral("toolButtonStyleDefault"));
1012         if (!toolButtonStyleDefault.isEmpty()) {
1013             d->toolButtonStyleSettings[Level_AppXML] = d->toolButtonStyleFromString(toolButtonStyleDefault);
1014         }
1015     } else {
1016         // loading app defaults
1017         bool newLine = false;
1018         QString attrNewLine = element.attribute(QStringLiteral("newline")).toLower();
1019         if (!attrNewLine.isEmpty()) {
1020             newLine = (attrNewLine == QLatin1String("true"));
1021         }
1022         if (newLine && mw) {
1023             mw->insertToolBarBreak(this);
1024         }
1025     }
1026 
1027     int newIconSize = -1;
1028     if (element.hasAttribute(QStringLiteral("iconSize"))) {
1029         bool ok;
1030         newIconSize = element.attribute(QStringLiteral("iconSize")).trimmed().toInt(&ok);
1031         if (!ok) {
1032             newIconSize = -1;
1033         }
1034     }
1035     if (newIconSize != -1) {
1036         d->iconSizeSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = newIconSize;
1037     }
1038 
1039     const QString newToolButtonStyle = element.attribute(QStringLiteral("iconText"));
1040     if (!newToolButtonStyle.isEmpty()) {
1041         d->toolButtonStyleSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = d->toolButtonStyleFromString(newToolButtonStyle);
1042     }
1043 
1044     bool hidden = false;
1045     {
1046         QString attrHidden = element.attribute(QStringLiteral("hidden")).toLower();
1047         if (!attrHidden.isEmpty()) {
1048             hidden = (attrHidden == QLatin1String("true"));
1049         }
1050     }
1051 
1052     Qt::ToolBarArea pos = Qt::NoToolBarArea;
1053     {
1054         QString attrPosition = element.attribute(QStringLiteral("position")).toLower();
1055         if (!attrPosition.isEmpty()) {
1056             pos = KToolBarPrivate::positionFromString(attrPosition);
1057         }
1058     }
1059     if (pos != Qt::NoToolBarArea) {
1060         mw->addToolBar(pos, this);
1061     }
1062 
1063     setVisible(!hidden);
1064 
1065     d->applyCurrentSettings();
1066 }
1067 
1068 // Called when switching between xmlgui clients, in order to find any unsaved settings
1069 // again when switching back to the current xmlgui client.
1070 void KToolBar::saveState(QDomElement &current) const
1071 {
1072     Q_ASSERT(!current.isNull());
1073 
1074     current.setAttribute(QStringLiteral("tempXml"), QStringLiteral("true"));
1075 
1076     current.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1"));
1077     current.setAttribute(QStringLiteral("position"), d->getPositionAsString().toLower());
1078     current.setAttribute(QStringLiteral("hidden"), isHidden() ? QStringLiteral("true") : QStringLiteral("false"));
1079 
1080     const int currentIconSize = iconSize().width();
1081     if (currentIconSize == d->iconSizeSettings.defaultValue()) {
1082         current.removeAttribute(QStringLiteral("iconSize"));
1083     } else {
1084         current.setAttribute(QStringLiteral("iconSize"), iconSize().width());
1085     }
1086 
1087     if (toolButtonStyle() == d->toolButtonStyleSettings.defaultValue()) {
1088         current.removeAttribute(QStringLiteral("iconText"));
1089     } else {
1090         current.setAttribute(QStringLiteral("iconText"), d->toolButtonStyleToString(toolButtonStyle()));
1091     }
1092 
1093     // Note: if this method is used by more than KXMLGUIBuilder, e.g. to save XML settings to *disk*,
1094     // then the stuff below shouldn't always be done. This is not the case currently though.
1095     if (d->iconSizeSettings[Level_AppXML] != Unset) {
1096         current.setAttribute(QStringLiteral("iconSizeDefault"), d->iconSizeSettings[Level_AppXML]);
1097     }
1098     if (d->toolButtonStyleSettings[Level_AppXML] != Unset) {
1099         const Qt::ToolButtonStyle bs = static_cast<Qt::ToolButtonStyle>(d->toolButtonStyleSettings[Level_AppXML]);
1100         current.setAttribute(QStringLiteral("toolButtonStyleDefault"), d->toolButtonStyleToString(bs));
1101     }
1102 }
1103 
1104 // called by KMainWindow::applyMainWindowSettings to read from the user settings
1105 void KToolBar::applySettings(const KConfigGroup &cg)
1106 {
1107     Q_ASSERT(!cg.name().isEmpty());
1108 
1109     if (cg.hasKey("IconSize")) {
1110         d->iconSizeSettings[Level_UserSettings] = cg.readEntry("IconSize", 0);
1111     }
1112     if (cg.hasKey("ToolButtonStyle")) {
1113         d->toolButtonStyleSettings[Level_UserSettings] = d->toolButtonStyleFromString(cg.readEntry("ToolButtonStyle", QString()));
1114     }
1115 
1116     d->applyCurrentSettings();
1117 }
1118 
1119 KMainWindow *KToolBar::mainWindow() const
1120 {
1121     return qobject_cast<KMainWindow *>(const_cast<QObject *>(parent()));
1122 }
1123 
1124 void KToolBar::setIconDimensions(int size)
1125 {
1126     QToolBar::setIconSize(QSize(size, size));
1127     d->iconSizeSettings[Level_UserSettings] = size;
1128 }
1129 
1130 int KToolBar::iconSizeDefault() const
1131 {
1132     return KIconLoader::global()->currentSize(d->isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar);
1133 }
1134 
1135 void KToolBar::slotMovableChanged(bool movable)
1136 {
1137     if (movable && !KAuthorized::authorize(QStringLiteral("movable_toolbars"))) {
1138         setMovable(false);
1139     }
1140 }
1141 
1142 void KToolBar::dragEnterEvent(QDragEnterEvent *event)
1143 {
1144     if (toolBarsEditable() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction)
1145         && event->mimeData()->hasFormat(QStringLiteral("application/x-kde-action-list"))) {
1146         QByteArray data = event->mimeData()->data(QStringLiteral("application/x-kde-action-list"));
1147 
1148         QDataStream stream(data);
1149 
1150         QStringList actionNames;
1151 
1152         stream >> actionNames;
1153 
1154         const auto allCollections = KActionCollection::allCollections();
1155         for (const QString &actionName : std::as_const(actionNames)) {
1156             for (KActionCollection *ac : allCollections) {
1157                 QAction *newAction = ac->action(actionName);
1158                 if (newAction) {
1159                     d->actionsBeingDragged.append(newAction);
1160                     break;
1161                 }
1162             }
1163         }
1164 
1165         if (!d->actionsBeingDragged.isEmpty()) {
1166             QAction *overAction = actionAt(event->pos());
1167 
1168             QFrame *dropIndicatorWidget = new QFrame(this);
1169             dropIndicatorWidget->resize(8, height() - 4);
1170             dropIndicatorWidget->setFrameShape(QFrame::VLine);
1171             dropIndicatorWidget->setLineWidth(3);
1172 
1173             d->dropIndicatorAction = insertWidget(overAction, dropIndicatorWidget);
1174 
1175             insertAction(overAction, d->dropIndicatorAction);
1176 
1177             event->acceptProposedAction();
1178             return;
1179         }
1180     }
1181 
1182     QToolBar::dragEnterEvent(event);
1183 }
1184 
1185 void KToolBar::dragMoveEvent(QDragMoveEvent *event)
1186 {
1187     if (toolBarsEditable()) {
1188         Q_FOREVER {
1189             if (d->dropIndicatorAction) {
1190                 QAction *overAction = nullptr;
1191                 const auto actions = this->actions();
1192                 for (QAction *action : actions) {
1193                     // want to make it feel that half way across an action you're dropping on the other side of it
1194                     QWidget *widget = widgetForAction(action);
1195                     if (event->pos().x() < widget->pos().x() + (widget->width() / 2)) {
1196                         overAction = action;
1197                         break;
1198                     }
1199                 }
1200 
1201                 if (overAction != d->dropIndicatorAction) {
1202                     // Check to see if the indicator is already in the right spot
1203                     int dropIndicatorIndex = actions.indexOf(d->dropIndicatorAction);
1204                     if (dropIndicatorIndex + 1 < actions.count()) {
1205                         if (actions.at(dropIndicatorIndex + 1) == overAction) {
1206                             break;
1207                         }
1208                     } else if (!overAction) {
1209                         break;
1210                     }
1211 
1212                     insertAction(overAction, d->dropIndicatorAction);
1213                 }
1214 
1215                 event->accept();
1216                 return;
1217             }
1218             break;
1219         }
1220     }
1221 
1222     QToolBar::dragMoveEvent(event);
1223 }
1224 
1225 void KToolBar::dragLeaveEvent(QDragLeaveEvent *event)
1226 {
1227     // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely)
1228     delete d->dropIndicatorAction;
1229     d->dropIndicatorAction = nullptr;
1230     d->actionsBeingDragged.clear();
1231 
1232     if (toolBarsEditable()) {
1233         event->accept();
1234         return;
1235     }
1236 
1237     QToolBar::dragLeaveEvent(event);
1238 }
1239 
1240 void KToolBar::dropEvent(QDropEvent *event)
1241 {
1242     if (toolBarsEditable()) {
1243         for (QAction *action : std::as_const(d->actionsBeingDragged)) {
1244             if (actions().contains(action)) {
1245                 removeAction(action);
1246             }
1247             insertAction(d->dropIndicatorAction, action);
1248         }
1249     }
1250 
1251     // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely)
1252     delete d->dropIndicatorAction;
1253     d->dropIndicatorAction = nullptr;
1254     d->actionsBeingDragged.clear();
1255 
1256     if (toolBarsEditable()) {
1257         event->accept();
1258         return;
1259     }
1260 
1261     QToolBar::dropEvent(event);
1262 }
1263 
1264 void KToolBar::mousePressEvent(QMouseEvent *event)
1265 {
1266     if (toolBarsEditable() && event->button() == Qt::LeftButton) {
1267         if (QAction *action = actionAt(event->pos())) {
1268             d->dragAction = action;
1269             d->dragStartPosition = event->pos();
1270             event->accept();
1271             return;
1272         }
1273     }
1274 
1275     QToolBar::mousePressEvent(event);
1276 }
1277 
1278 void KToolBar::mouseMoveEvent(QMouseEvent *event)
1279 {
1280     if (!toolBarsEditable() || !d->dragAction) {
1281         QToolBar::mouseMoveEvent(event);
1282         return;
1283     }
1284 
1285     if ((event->pos() - d->dragStartPosition).manhattanLength() < QApplication::startDragDistance()) {
1286         event->accept();
1287         return;
1288     }
1289 
1290     QDrag *drag = new QDrag(this);
1291     QMimeData *mimeData = new QMimeData;
1292 
1293     QByteArray data;
1294     {
1295         QDataStream stream(&data, QIODevice::WriteOnly);
1296 
1297         QStringList actionNames;
1298         actionNames << d->dragAction->objectName();
1299 
1300         stream << actionNames;
1301     }
1302 
1303     mimeData->setData(QStringLiteral("application/x-kde-action-list"), data);
1304 
1305     drag->setMimeData(mimeData);
1306 
1307     Qt::DropAction dropAction = drag->exec(Qt::MoveAction);
1308 
1309     if (dropAction == Qt::MoveAction) {
1310         // Only remove from this toolbar if it was moved to another toolbar
1311         // Otherwise the receiver moves it.
1312         if (drag->target() != this) {
1313             removeAction(d->dragAction);
1314         }
1315     }
1316 
1317     d->dragAction = nullptr;
1318     event->accept();
1319 }
1320 
1321 void KToolBar::mouseReleaseEvent(QMouseEvent *event)
1322 {
1323     // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely)
1324     if (d->dragAction) {
1325         d->dragAction = nullptr;
1326         event->accept();
1327         return;
1328     }
1329 
1330     QToolBar::mouseReleaseEvent(event);
1331 }
1332 
1333 bool KToolBar::eventFilter(QObject *watched, QEvent *event)
1334 {
1335     // Generate context menu events for disabled buttons too...
1336     if (event->type() == QEvent::MouseButtonPress) {
1337         QMouseEvent *me = static_cast<QMouseEvent *>(event);
1338         if (me->buttons() & Qt::RightButton) {
1339             if (QWidget *ww = qobject_cast<QWidget *>(watched)) {
1340                 if (ww->parent() == this && !ww->isEnabled()) {
1341                     QCoreApplication::postEvent(this, new QContextMenuEvent(QContextMenuEvent::Mouse, me->pos(), me->globalPos()));
1342                 }
1343             }
1344         }
1345 
1346     } else if (event->type() == QEvent::ParentChange) {
1347         // Make sure we're not leaving stale event filters around,
1348         // when a child is reparented somewhere else
1349         if (QWidget *ww = qobject_cast<QWidget *>(watched)) {
1350             if (!this->isAncestorOf(ww)) {
1351                 // New parent is not a subwidget - remove event filter
1352                 ww->removeEventFilter(this);
1353                 const auto children = ww->findChildren<QWidget *>();
1354                 for (QWidget *child : children) {
1355                     child->removeEventFilter(this);
1356                 }
1357             }
1358         }
1359     }
1360 
1361     // Redirect mouse events to the toolbar when drag + drop editing is enabled
1362     if (toolBarsEditable()) {
1363         if (QWidget *ww = qobject_cast<QWidget *>(watched)) {
1364             switch (event->type()) {
1365             case QEvent::MouseButtonPress: {
1366                 QMouseEvent *me = static_cast<QMouseEvent *>(event);
1367                 QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers());
1368                 mousePressEvent(&newEvent);
1369                 return true;
1370             }
1371             case QEvent::MouseMove: {
1372                 QMouseEvent *me = static_cast<QMouseEvent *>(event);
1373                 QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers());
1374                 mouseMoveEvent(&newEvent);
1375                 return true;
1376             }
1377             case QEvent::MouseButtonRelease: {
1378                 QMouseEvent *me = static_cast<QMouseEvent *>(event);
1379                 QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers());
1380                 mouseReleaseEvent(&newEvent);
1381                 return true;
1382             }
1383             default:
1384                 break;
1385             }
1386         }
1387     }
1388 
1389     return QToolBar::eventFilter(watched, event);
1390 }
1391 
1392 void KToolBar::actionEvent(QActionEvent *event)
1393 {
1394     if (event->type() == QEvent::ActionRemoved) {
1395         QWidget *widget = widgetForAction(event->action());
1396         if (widget) {
1397             widget->removeEventFilter(this);
1398 
1399             const auto children = widget->findChildren<QWidget *>();
1400             for (QWidget *child : children) {
1401                 child->removeEventFilter(this);
1402             }
1403         }
1404     }
1405 
1406     QToolBar::actionEvent(event);
1407 
1408     if (event->type() == QEvent::ActionAdded) {
1409         QWidget *widget = widgetForAction(event->action());
1410         if (widget) {
1411             widget->installEventFilter(this);
1412 
1413             const auto children = widget->findChildren<QWidget *>();
1414             for (QWidget *child : children) {
1415                 child->installEventFilter(this);
1416             }
1417             // Center widgets that do not have any use for more space. See bug 165274
1418             if (!(widget->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag)
1419                 // ... but do not center when using text besides icon in vertical toolbar. See bug 243196
1420                 && !(orientation() == Qt::Vertical && toolButtonStyle() == Qt::ToolButtonTextBesideIcon)) {
1421                 const int index = layout()->indexOf(widget);
1422                 if (index != -1) {
1423                     layout()->itemAt(index)->setAlignment(Qt::AlignJustify);
1424                 }
1425             }
1426         }
1427     }
1428 
1429     d->adjustSeparatorVisibility();
1430 }
1431 
1432 bool KToolBar::toolBarsEditable()
1433 {
1434     return KToolBarPrivate::s_editable;
1435 }
1436 
1437 void KToolBar::setToolBarsEditable(bool editable)
1438 {
1439     if (KToolBarPrivate::s_editable != editable) {
1440         KToolBarPrivate::s_editable = editable;
1441     }
1442 }
1443 
1444 void KToolBar::setToolBarsLocked(bool locked)
1445 {
1446     if (KToolBarPrivate::s_locked != locked) {
1447         KToolBarPrivate::s_locked = locked;
1448 
1449         const auto windows = KMainWindow::memberList();
1450         for (KMainWindow *mw : windows) {
1451             const auto toolbars = mw->findChildren<KToolBar *>();
1452             for (KToolBar *toolbar : toolbars) {
1453                 toolbar->d->setLocked(locked);
1454             }
1455         }
1456     }
1457 }
1458 
1459 bool KToolBar::toolBarsLocked()
1460 {
1461     return KToolBarPrivate::s_locked;
1462 }
1463 
1464 void KToolBar::emitToolbarStyleChanged()
1465 {
1466 #ifdef QT_DBUS_LIB
1467     QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"));
1468     QDBusConnection::sessionBus().send(message);
1469 #endif
1470 }
1471 
1472 #include "moc_ktoolbar.cpp"