File indexing completed on 2024-03-03 04:08:08

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 #include <KToolBarPopupAction>
0042 
0043 #include "kactioncollection.h"
0044 #include "kedittoolbar.h"
0045 #include "kxmlguifactory.h"
0046 #include "kxmlguiwindow.h"
0047 
0048 #include "ktoolbarhelper_p.h"
0049 
0050 #include <algorithm>
0051 
0052 /*
0053  Toolbar settings (e.g. icon size or toolButtonStyle)
0054  =====================================================
0055 
0056  We have the following stack of settings (in order of priority) :
0057    - user-specified settings (loaded/saved in KConfig)
0058    - developer-specified settings in the XMLGUI file (if using xmlgui) (cannot change at runtime)
0059    - KDE-global default (user-configurable; can change at runtime)
0060  and when switching between kparts, they are saved as xml in memory,
0061  which, in the unlikely case of no-kmainwindow-autosaving, could be
0062  different from the user-specified settings saved in KConfig and would have
0063  priority over it.
0064 
0065  So, in summary, without XML:
0066    Global config / User settings (loaded/saved in kconfig)
0067  and with XML:
0068    Global config / App-XML attributes / User settings (loaded/saved in kconfig)
0069 
0070  And all those settings (except the KDE-global defaults) have to be stored in memory
0071  since we cannot retrieve them at random points in time, not knowing the xml document
0072  nor config file that holds these settings. Hence the iconSizeSettings and toolButtonStyleSettings arrays.
0073 
0074  For instance, if you change the KDE-global default, whether this makes a change
0075  on a given toolbar depends on whether there are settings at Level_AppXML or Level_UserSettings.
0076  Only if there are no settings at those levels, should the change of KDEDefault make a difference.
0077 */
0078 enum SettingLevel { Level_KDEDefault, Level_AppXML, Level_UserSettings, NSettingLevels };
0079 enum { Unset = -1 };
0080 
0081 class KToolBarPrivate
0082 {
0083 public:
0084     KToolBarPrivate(KToolBar *qq)
0085         : q(qq)
0086         , isMainToolBar(false)
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     bool unlockedMovable : 1;
0142     static bool s_editable;
0143     static bool s_locked;
0144 
0145     QSet<KXMLGUIClient *> xmlguiClients;
0146 
0147     QMenu *contextOrient;
0148     QMenu *contextMode;
0149     QMenu *contextSize;
0150 
0151     QAction *contextButtonTitle;
0152     QAction *contextShowText;
0153     QAction *contextButtonAction;
0154     QAction *contextTop;
0155     QAction *contextLeft;
0156     QAction *contextRight;
0157     QAction *contextBottom;
0158     QAction *contextIcons;
0159     QAction *contextTextRight;
0160     QAction *contextText;
0161     QAction *contextTextUnder;
0162     KToggleAction *contextLockAction;
0163 
0164     struct ContextIconInfo {
0165         QAction *iconAction = nullptr;
0166         int iconSize = 0;
0167     };
0168 
0169     std::vector<ContextIconInfo> m_contextIconSizes;
0170 
0171     class IntSetting
0172     {
0173     public:
0174         IntSetting()
0175         {
0176             for (int &value : values) {
0177                 value = Unset;
0178             }
0179         }
0180         int currentValue() const
0181         {
0182             int val = Unset;
0183             for (int value : values) {
0184                 if (value != Unset) {
0185                     val = value;
0186                 }
0187             }
0188             return val;
0189         }
0190         // Default value as far as the user is concerned is kde-global + app-xml.
0191         // If currentValue()==defaultValue() then nothing to write into kconfig.
0192         int defaultValue() const
0193         {
0194             int val = Unset;
0195             for (int level = 0; level < Level_UserSettings; ++level) {
0196                 if (values[level] != Unset) {
0197                     val = values[level];
0198                 }
0199             }
0200             return val;
0201         }
0202         QString toString() const
0203         {
0204             QString str;
0205             for (int value : values) {
0206                 str += QString::number(value) + QLatin1Char(' ');
0207             }
0208             return str;
0209         }
0210         int &operator[](int index)
0211         {
0212             return values[index];
0213         }
0214 
0215     private:
0216         int values[NSettingLevels];
0217     };
0218     IntSetting iconSizeSettings;
0219     IntSetting toolButtonStyleSettings; // either Qt::ToolButtonStyle or -1, hence "int".
0220 
0221     QList<QAction *> actionsBeingDragged;
0222     QAction *dropIndicatorAction;
0223 
0224     QMenu *context;
0225     QAction *dragAction;
0226     QPoint dragStartPosition;
0227 };
0228 
0229 bool KToolBarPrivate::s_editable = false;
0230 bool KToolBarPrivate::s_locked = true;
0231 
0232 void KToolBarPrivate::init(bool readConfig, bool _isMainToolBar)
0233 {
0234     isMainToolBar = _isMainToolBar;
0235     loadKDESettings();
0236 
0237     // also read in our configurable settings (for non-xmlgui toolbars)
0238     if (readConfig) {
0239         KConfigGroup cg(KSharedConfig::openConfig(), QString());
0240         q->applySettings(cg);
0241     }
0242 
0243     if (q->mainWindow()) {
0244         // Get notified when settings change
0245         QObject::connect(q, &QToolBar::allowedAreasChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0246         QObject::connect(q, &QToolBar::iconSizeChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0247         QObject::connect(q, &QToolBar::toolButtonStyleChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0248         QObject::connect(q, &QToolBar::movableChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0249         QObject::connect(q, &QToolBar::orientationChanged, q->mainWindow(), &KMainWindow::setSettingsDirty);
0250     }
0251 
0252     if (!KAuthorized::authorize(QStringLiteral("movable_toolbars"))) {
0253         q->setMovable(false);
0254     } else {
0255         q->setMovable(!KToolBar::toolBarsLocked());
0256     }
0257 
0258     q->toggleViewAction()->setEnabled(KAuthorized::authorizeAction(QStringLiteral("options_show_toolbar")));
0259 
0260     QObject::connect(q, &QToolBar::movableChanged, q, &KToolBar::slotMovableChanged);
0261 
0262     q->setAcceptDrops(true);
0263 
0264 #ifdef QT_DBUS_LIB
0265     QDBusConnection::sessionBus()
0266         .connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), q, SLOT(slotAppearanceChanged()));
0267 #endif
0268     QObject::connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, q, [this]() {
0269         slotAppearanceChanged();
0270     });
0271 }
0272 
0273 QString KToolBarPrivate::getPositionAsString() const
0274 {
0275     if (!q->mainWindow()) {
0276         return QStringLiteral("None");
0277     }
0278     // get all of the stuff to save
0279     switch (q->mainWindow()->toolBarArea(const_cast<KToolBar *>(q))) {
0280     case Qt::BottomToolBarArea:
0281         return QStringLiteral("Bottom");
0282     case Qt::LeftToolBarArea:
0283         return QStringLiteral("Left");
0284     case Qt::RightToolBarArea:
0285         return QStringLiteral("Right");
0286     case Qt::TopToolBarArea:
0287     default:
0288         return QStringLiteral("Top");
0289     }
0290 }
0291 
0292 QMenu *KToolBarPrivate::contextMenu(const QPoint &globalPos)
0293 {
0294     if (!context) {
0295         context = new QMenu(q);
0296         context->setIcon(QIcon::fromTheme(QStringLiteral("configure-toolbars")));
0297         context->setTitle(i18nc("@title:menu", "Toolbar Settings"));
0298 
0299         contextButtonTitle = context->addSection(i18nc("@title:menu", "Show Text"));
0300         contextShowText = context->addAction(QString(), q, [this]() {
0301             slotContextShowText();
0302         });
0303 
0304         context->addSection(i18nc("@title:menu", "Toolbar Settings"));
0305 
0306         contextOrient = new QMenu(i18nc("Toolbar orientation", "Orientation"), context);
0307 
0308         contextTop = contextOrient->addAction(i18nc("toolbar position string", "Top"), q, [this]() {
0309             slotContextTop();
0310         });
0311         contextTop->setChecked(true);
0312         contextLeft = contextOrient->addAction(i18nc("toolbar position string", "Left"), q, [this]() {
0313             slotContextLeft();
0314         });
0315         contextRight = contextOrient->addAction(i18nc("toolbar position string", "Right"), q, [this]() {
0316             slotContextRight();
0317         });
0318         contextBottom = contextOrient->addAction(i18nc("toolbar position string", "Bottom"), q, [this]() {
0319             slotContextBottom();
0320         });
0321 
0322         QActionGroup *positionGroup = new QActionGroup(contextOrient);
0323         const auto orientActions = contextOrient->actions();
0324         for (QAction *action : orientActions) {
0325             action->setActionGroup(positionGroup);
0326             action->setCheckable(true);
0327         }
0328 
0329         contextMode = new QMenu(i18n("Text Position"), context);
0330 
0331         contextIcons = contextMode->addAction(i18nc("@item:inmenu", "Icons Only"), q, [this]() {
0332             slotContextIcons();
0333         });
0334         contextText = contextMode->addAction(i18nc("@item:inmenu", "Text Only"), q, [this]() {
0335             slotContextText();
0336         });
0337         contextTextRight = contextMode->addAction(i18nc("@item:inmenu", "Text Alongside Icons"), q, [this]() {
0338             slotContextTextRight();
0339         });
0340         contextTextUnder = contextMode->addAction(i18nc("@item:inmenu", "Text Under Icons"), q, [this]() {
0341             slotContextTextUnder();
0342         });
0343 
0344         QActionGroup *textGroup = new QActionGroup(contextMode);
0345         const auto modeActions = contextMode->actions();
0346         for (QAction *action : modeActions) {
0347             action->setActionGroup(textGroup);
0348             action->setCheckable(true);
0349         }
0350 
0351         contextSize = new QMenu(i18n("Icon Size"), context);
0352 
0353         auto *act = contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"));
0354         q->connect(act, &QAction::triggered, q, [this, act]() {
0355             slotContextIconSize(act);
0356         });
0357         m_contextIconSizes.push_back({act, iconSizeSettings.defaultValue()});
0358 
0359         // Query the current theme for available sizes
0360         KIconTheme *theme = KIconLoader::global()->theme();
0361         QList<int> avSizes;
0362         if (theme) {
0363             avSizes = theme->querySizes(isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar);
0364         }
0365 
0366         std::sort(avSizes.begin(), avSizes.end());
0367 
0368         if (avSizes.count() < 10) {
0369             // Fixed or threshold type icons
0370             for (int it : std::as_const(avSizes)) {
0371                 QString text;
0372                 if (it < 19) {
0373                     text = i18n("Small (%1x%2)", it, it);
0374                 } else if (it < 25) {
0375                     text = i18n("Medium (%1x%2)", it, it);
0376                 } else if (it < 35) {
0377                     text = i18n("Large (%1x%2)", it, it);
0378                 } else {
0379                     text = i18n("Huge (%1x%2)", it, it);
0380                 }
0381 
0382                 auto *act = contextSize->addAction(text);
0383                 q->connect(act, &QAction::triggered, q, [this, act]() {
0384                     slotContextIconSize(act);
0385                 });
0386                 m_contextIconSizes.push_back({act, it});
0387             }
0388         } else {
0389             // Scalable icons.
0390             const int progression[] = {16, 22, 32, 48, 64, 96, 128, 192, 256};
0391 
0392             for (int p : progression) {
0393                 for (int it : std::as_const(avSizes)) {
0394                     if (it >= p) {
0395                         QString text;
0396                         if (it < 19) {
0397                             text = i18n("Small (%1x%2)", it, it);
0398                         } else if (it < 25) {
0399                             text = i18n("Medium (%1x%2)", it, it);
0400                         } else if (it < 35) {
0401                             text = i18n("Large (%1x%2)", it, it);
0402                         } else {
0403                             text = i18n("Huge (%1x%2)", it, it);
0404                         }
0405 
0406                         auto *act = contextSize->addAction(text);
0407                         q->connect(act, &QAction::triggered, q, [this, act]() {
0408                             slotContextIconSize(act);
0409                         });
0410                         m_contextIconSizes.push_back({act, it});
0411                         break;
0412                     }
0413                 }
0414             }
0415         }
0416 
0417         QActionGroup *sizeGroup = new QActionGroup(contextSize);
0418         const auto sizeActions = contextSize->actions();
0419         for (QAction *action : sizeActions) {
0420             action->setActionGroup(sizeGroup);
0421             action->setCheckable(true);
0422         }
0423 
0424         if (!q->toolBarsLocked() && !q->isMovable()) {
0425             unlockedMovable = false;
0426         }
0427 
0428         delete contextLockAction;
0429         contextLockAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("system-lock-screen")), i18n("Lock Toolbar Positions"), q);
0430         contextLockAction->setChecked(q->toolBarsLocked());
0431         QObject::connect(contextLockAction, &KToggleAction::toggled, q, [this](bool checked) {
0432             slotLockToolBars(checked);
0433         });
0434 
0435         // Now add the actions to the menu
0436         context->addMenu(contextMode);
0437         context->addMenu(contextSize);
0438         context->addMenu(contextOrient);
0439         context->addSeparator();
0440 
0441         QObject::connect(context, &QMenu::aboutToShow, q, [this]() {
0442             slotContextAboutToShow();
0443         });
0444     }
0445 
0446     contextButtonAction = q->actionAt(q->mapFromGlobal(globalPos));
0447     if (contextButtonAction) {
0448         contextShowText->setText(contextButtonAction->text());
0449         contextShowText->setIcon(contextButtonAction->icon());
0450         contextShowText->setCheckable(true);
0451     }
0452 
0453     contextOrient->menuAction()->setVisible(!q->toolBarsLocked());
0454     // Unplugging a submenu from abouttohide leads to the popupmenu floating around
0455     // So better simply call that code from after exec() returns (DF)
0456     // connect(context, SIGNAL(aboutToHide()), this, SLOT(slotContextAboutToHide()));
0457 
0458     // For tool buttons with delay popup or menu button popup (split button)
0459     // show the actions of that button for ease of access.
0460     // (no need to wait for the menu to open or aim at the arrow)
0461     if (auto *contextToolButton = qobject_cast<QToolButton *>(q->widgetForAction(contextButtonAction))) {
0462         if (contextToolButton->popupMode() == QToolButton::DelayedPopup || contextToolButton->popupMode() == QToolButton::MenuButtonPopup) {
0463             auto *actionMenu = contextButtonAction->menu();
0464 
0465             if (!actionMenu) {
0466                 if (auto *toolBarPopupAction = qobject_cast<KToolBarPopupAction *>(contextButtonAction)) {
0467                     actionMenu = toolBarPopupAction->popupMenu();
0468                 }
0469             }
0470 
0471             if (actionMenu) {
0472                 // In case it is populated on demand
0473                 Q_EMIT actionMenu->aboutToShow();
0474 
0475                 auto *contextMenu = new QMenu(q);
0476                 contextMenu->setAttribute(Qt::WA_DeleteOnClose);
0477 
0478                 const auto actions = actionMenu->actions();
0479                 if (!actions.isEmpty()) {
0480                     for (QAction *action : actions) {
0481                         contextMenu->addAction(action);
0482                     }
0483 
0484                     // Now add the configure actions as submenu
0485                     contextMenu->addSeparator();
0486                     contextMenu->addMenu(context);
0487                     return contextMenu;
0488                 }
0489             }
0490         }
0491     }
0492 
0493     return context;
0494 }
0495 
0496 void KToolBarPrivate::setLocked(bool locked)
0497 {
0498     if (unlockedMovable) {
0499         q->setMovable(!locked);
0500     }
0501 }
0502 
0503 void KToolBarPrivate::adjustSeparatorVisibility()
0504 {
0505     bool visibleNonSeparator = false;
0506     int separatorToShow = -1;
0507 
0508     for (int index = 0; index < q->actions().count(); ++index) {
0509         QAction *action = q->actions().at(index);
0510         if (action->isSeparator()) {
0511             if (visibleNonSeparator) {
0512                 separatorToShow = index;
0513                 visibleNonSeparator = false;
0514             } else {
0515                 action->setVisible(false);
0516             }
0517         } else if (!visibleNonSeparator) {
0518             if (action->isVisible()) {
0519                 visibleNonSeparator = true;
0520                 if (separatorToShow != -1) {
0521                     q->actions().at(separatorToShow)->setVisible(true);
0522                     separatorToShow = -1;
0523                 }
0524             }
0525         }
0526     }
0527 
0528     if (separatorToShow != -1) {
0529         q->actions().at(separatorToShow)->setVisible(false);
0530     }
0531 }
0532 
0533 Qt::ToolButtonStyle KToolBarPrivate::toolButtonStyleFromString(const QString &_style)
0534 {
0535     QString style = _style.toLower();
0536     if (style == QLatin1String("textbesideicon") || style == QLatin1String("icontextright")) {
0537         return Qt::ToolButtonTextBesideIcon;
0538     } else if (style == QLatin1String("textundericon") || style == QLatin1String("icontextbottom")) {
0539         return Qt::ToolButtonTextUnderIcon;
0540     } else if (style == QLatin1String("textonly")) {
0541         return Qt::ToolButtonTextOnly;
0542     } else {
0543         return Qt::ToolButtonIconOnly;
0544     }
0545 }
0546 
0547 QString KToolBarPrivate::toolButtonStyleToString(Qt::ToolButtonStyle style)
0548 {
0549     switch (style) {
0550     case Qt::ToolButtonIconOnly:
0551     default:
0552         return QStringLiteral("IconOnly");
0553     case Qt::ToolButtonTextBesideIcon:
0554         return QStringLiteral("TextBesideIcon");
0555     case Qt::ToolButtonTextOnly:
0556         return QStringLiteral("TextOnly");
0557     case Qt::ToolButtonTextUnderIcon:
0558         return QStringLiteral("TextUnderIcon");
0559     }
0560 }
0561 
0562 Qt::ToolBarArea KToolBarPrivate::positionFromString(const QString &position)
0563 {
0564     Qt::ToolBarArea newposition = Qt::TopToolBarArea;
0565     if (position == QLatin1String("left")) {
0566         newposition = Qt::LeftToolBarArea;
0567     } else if (position == QLatin1String("bottom")) {
0568         newposition = Qt::BottomToolBarArea;
0569     } else if (position == QLatin1String("right")) {
0570         newposition = Qt::RightToolBarArea;
0571     } else if (position == QLatin1String("none")) {
0572         newposition = Qt::NoToolBarArea;
0573     }
0574     return newposition;
0575 }
0576 
0577 // Global setting was changed
0578 void KToolBarPrivate::slotAppearanceChanged()
0579 {
0580     loadKDESettings();
0581     applyCurrentSettings();
0582 }
0583 
0584 Qt::ToolButtonStyle KToolBarPrivate::toolButtonStyleSetting()
0585 {
0586     KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("Toolbar style"));
0587     const QString fallback = KToolBarPrivate::toolButtonStyleToString(Qt::ToolButtonTextBesideIcon);
0588     return KToolBarPrivate::toolButtonStyleFromString(group.readEntry("ToolButtonStyle", fallback));
0589 }
0590 
0591 void KToolBarPrivate::loadKDESettings()
0592 {
0593     iconSizeSettings[Level_KDEDefault] = q->iconSizeDefault();
0594 
0595     if (isMainToolBar) {
0596         toolButtonStyleSettings[Level_KDEDefault] = toolButtonStyleSetting();
0597     } else {
0598         const QString fallBack = toolButtonStyleToString(Qt::ToolButtonTextBesideIcon);
0599         /**
0600           TODO: if we get complaints about text beside icons on small screens,
0601                 try the following code out on such systems - aseigo.
0602         // if we are on a small screen with a non-landscape ratio, then
0603         // we revert to text under icons since width is probably not our
0604         // friend in such cases
0605         QDesktopWidget *desktop = QApplication::desktop();
0606         QRect screenGeom = desktop->screenGeometry(desktop->primaryScreen());
0607         qreal ratio = screenGeom.width() / qreal(screenGeom.height());
0608 
0609         if (screenGeom.width() < 1024 && ratio <= 1.4) {
0610             fallBack = "TextUnderIcon";
0611         }
0612         **/
0613 
0614         KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("Toolbar style"));
0615         const QString value = group.readEntry("ToolButtonStyleOtherToolbars", fallBack);
0616         toolButtonStyleSettings[Level_KDEDefault] = KToolBarPrivate::toolButtonStyleFromString(value);
0617     }
0618 }
0619 
0620 // Call this after changing something in d->iconSizeSettings or d->toolButtonStyleSettings
0621 void KToolBarPrivate::applyCurrentSettings()
0622 {
0623     // qCDebug(DEBUG_KXMLGUI) << q->objectName() << "iconSizeSettings:" << iconSizeSettings.toString() << "->" << iconSizeSettings.currentValue();
0624     const int currentIconSize = iconSizeSettings.currentValue();
0625     q->setIconSize(QSize(currentIconSize, currentIconSize));
0626     // qCDebug(DEBUG_KXMLGUI) << q->objectName() << "toolButtonStyleSettings:" << toolButtonStyleSettings.toString() << "->" <<
0627     // toolButtonStyleSettings.currentValue();
0628     q->setToolButtonStyle(static_cast<Qt::ToolButtonStyle>(toolButtonStyleSettings.currentValue()));
0629 
0630     // And remember to save the new look later
0631     KMainWindow *kmw = q->mainWindow();
0632     if (kmw) {
0633         kmw->setSettingsDirty();
0634     }
0635 }
0636 
0637 QAction *KToolBarPrivate::findAction(const QString &actionName, KXMLGUIClient **clientOut) const
0638 {
0639     for (KXMLGUIClient *client : xmlguiClients) {
0640         QAction *action = client->actionCollection()->action(actionName);
0641         if (action) {
0642             if (clientOut) {
0643                 *clientOut = client;
0644             }
0645             return action;
0646         }
0647     }
0648     return nullptr;
0649 }
0650 
0651 void KToolBarPrivate::slotContextAboutToShow()
0652 {
0653     /**
0654      * The idea here is to reuse the "static" part of the menu to save time.
0655      * But the "Toolbars" action is dynamic (can be a single action or a submenu)
0656      * and ToolBarHandler::setupActions() deletes it, so better not keep it around.
0657      * So we currently plug/unplug the last two actions of the menu.
0658      * Another way would be to keep around the actions and plug them all into a (new each time) popupmenu.
0659      */
0660 
0661     KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(q->mainWindow());
0662 
0663     // try to find "configure toolbars" action
0664     QAction *configureAction = nullptr;
0665     const QString actionName = KStandardAction::name(KStandardAction::ConfigureToolbars);
0666     configureAction = findAction(actionName);
0667 
0668     if (!configureAction && kmw) {
0669         configureAction = kmw->actionCollection()->action(actionName);
0670     }
0671 
0672     if (configureAction) {
0673         context->addAction(configureAction);
0674     }
0675 
0676     context->addAction(contextLockAction);
0677 
0678     if (kmw) {
0679         kmw->setupToolbarMenuActions();
0680         // Only allow hiding a toolbar if the action is also plugged somewhere else (e.g. menubar)
0681         QAction *tbAction = kmw->toolBarMenuAction();
0682         if (!q->toolBarsLocked() && tbAction) {
0683             const QList<QObject *> associatedObjects = tbAction->associatedObjects();
0684             const bool hasAssociatedWidgets = std::any_of(associatedObjects.cbegin(), associatedObjects.cend(), [](QObject *object) {
0685                 return (qobject_cast<QWidget *>(object) != nullptr);
0686             });
0687             if (hasAssociatedWidgets) {
0688                 context->addAction(tbAction);
0689             }
0690         }
0691     }
0692 
0693     KEditToolBar::setGlobalDefaultToolBar(q->QObject::objectName());
0694 
0695     // Check the actions that should be checked
0696     switch (q->toolButtonStyle()) {
0697     case Qt::ToolButtonIconOnly:
0698     default:
0699         contextIcons->setChecked(true);
0700         break;
0701     case Qt::ToolButtonTextBesideIcon:
0702         contextTextRight->setChecked(true);
0703         break;
0704     case Qt::ToolButtonTextOnly:
0705         contextText->setChecked(true);
0706         break;
0707     case Qt::ToolButtonTextUnderIcon:
0708         contextTextUnder->setChecked(true);
0709         break;
0710     }
0711 
0712     auto it = std::find_if(m_contextIconSizes.cbegin(), m_contextIconSizes.cend(), [this](const ContextIconInfo &info) {
0713         return info.iconSize == q->iconSize().width();
0714     });
0715     if (it != m_contextIconSizes.cend()) {
0716         it->iconAction->setChecked(true);
0717     }
0718 
0719     switch (q->mainWindow()->toolBarArea(q)) {
0720     case Qt::BottomToolBarArea:
0721         contextBottom->setChecked(true);
0722         break;
0723     case Qt::LeftToolBarArea:
0724         contextLeft->setChecked(true);
0725         break;
0726     case Qt::RightToolBarArea:
0727         contextRight->setChecked(true);
0728         break;
0729     default:
0730     case Qt::TopToolBarArea:
0731         contextTop->setChecked(true);
0732         break;
0733     }
0734 
0735     const bool showButtonSettings = contextButtonAction //
0736         && !contextShowText->text().isEmpty() //
0737         && contextTextRight->isChecked();
0738     contextButtonTitle->setVisible(showButtonSettings);
0739     contextShowText->setVisible(showButtonSettings);
0740     if (showButtonSettings) {
0741         contextShowText->setChecked(contextButtonAction->priority() >= QAction::NormalPriority);
0742     }
0743 }
0744 
0745 void KToolBarPrivate::slotContextAboutToHide()
0746 {
0747     // We have to unplug whatever slotContextAboutToShow plugged into the menu.
0748     // Unplug the toolbar menu action
0749     KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(q->mainWindow());
0750     if (kmw) {
0751         QAction *tbAction = kmw->toolBarMenuAction();
0752         const QList<QObject *> associatedObjects = tbAction->associatedObjects();
0753         const int associatedWidgetsCount = std::count_if(associatedObjects.cbegin(), associatedObjects.cend(), [](QObject *object) {
0754             return (qobject_cast<QWidget *>(object) != nullptr);
0755         });
0756         if (associatedWidgetsCount > 1) {
0757             context->removeAction(tbAction);
0758         }
0759     }
0760 
0761     // Unplug the configure toolbars action too, since it's afterwards anyway
0762     QAction *configureAction = nullptr;
0763     const QString actionName = KStandardAction::name(KStandardAction::ConfigureToolbars);
0764     configureAction = findAction(actionName);
0765 
0766     if (!configureAction && kmw) {
0767         configureAction = kmw->actionCollection()->action(actionName);
0768     }
0769 
0770     if (configureAction) {
0771         context->removeAction(configureAction);
0772     }
0773 
0774     context->removeAction(contextLockAction);
0775 }
0776 
0777 void KToolBarPrivate::slotContextLeft()
0778 {
0779     q->mainWindow()->addToolBar(Qt::LeftToolBarArea, q);
0780 }
0781 
0782 void KToolBarPrivate::slotContextRight()
0783 {
0784     q->mainWindow()->addToolBar(Qt::RightToolBarArea, q);
0785 }
0786 
0787 void KToolBarPrivate::slotContextShowText()
0788 {
0789     Q_ASSERT(contextButtonAction);
0790     const QAction::Priority priority = contextShowText->isChecked() ? QAction::NormalPriority : QAction::LowPriority;
0791     contextButtonAction->setPriority(priority);
0792 
0793     // Find to which xml file and componentData the action belongs to
0794     QString componentName;
0795     QString filename;
0796     KXMLGUIClient *client;
0797     if (findAction(contextButtonAction->objectName(), &client)) {
0798         componentName = client->componentName();
0799         filename = client->xmlFile();
0800     }
0801     if (filename.isEmpty()) {
0802         componentName = QCoreApplication::applicationName();
0803         filename = componentName + QLatin1String("ui.rc");
0804     }
0805 
0806     // Save the priority state of the action
0807     const QString configFile = KXMLGUIFactory::readConfigFile(filename, componentName);
0808 
0809     QDomDocument document;
0810     document.setContent(configFile);
0811     QDomElement elem = KXMLGUIFactory::actionPropertiesElement(document);
0812     QDomElement actionElem = KXMLGUIFactory::findActionByName(elem, contextButtonAction->objectName(), true);
0813     actionElem.setAttribute(QStringLiteral("priority"), priority);
0814     KXMLGUIFactory::saveConfigFile(document, filename, componentName);
0815 }
0816 
0817 void KToolBarPrivate::slotContextTop()
0818 {
0819     q->mainWindow()->addToolBar(Qt::TopToolBarArea, q);
0820 }
0821 
0822 void KToolBarPrivate::slotContextBottom()
0823 {
0824     q->mainWindow()->addToolBar(Qt::BottomToolBarArea, q);
0825 }
0826 
0827 void KToolBarPrivate::slotContextIcons()
0828 {
0829     q->setToolButtonStyle(Qt::ToolButtonIconOnly);
0830     toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
0831 }
0832 
0833 void KToolBarPrivate::slotContextText()
0834 {
0835     q->setToolButtonStyle(Qt::ToolButtonTextOnly);
0836     toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
0837 }
0838 
0839 void KToolBarPrivate::slotContextTextUnder()
0840 {
0841     q->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
0842     toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
0843 }
0844 
0845 void KToolBarPrivate::slotContextTextRight()
0846 {
0847     q->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0848     toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle();
0849 }
0850 
0851 void KToolBarPrivate::slotContextIconSize(QAction *action)
0852 {
0853     if (action) {
0854         auto it = std::find_if(m_contextIconSizes.cbegin(), m_contextIconSizes.cend(), [action](const ContextIconInfo &info) {
0855             return info.iconAction == action;
0856         });
0857         if (it != m_contextIconSizes.cend()) {
0858             q->setIconDimensions(it->iconSize);
0859         }
0860     }
0861 }
0862 
0863 void KToolBarPrivate::slotLockToolBars(bool lock)
0864 {
0865     q->setToolBarsLocked(lock);
0866 }
0867 
0868 KToolBar::KToolBar(QWidget *parent, bool isMainToolBar, bool readConfig)
0869     : QToolBar(parent)
0870     , d(new KToolBarPrivate(this))
0871 {
0872     d->init(readConfig, isMainToolBar);
0873 
0874     // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow
0875     if (QMainWindow *mw = qobject_cast<QMainWindow *>(parent)) {
0876         mw->addToolBar(this);
0877     }
0878 }
0879 
0880 KToolBar::KToolBar(const QString &objectName, QWidget *parent, bool readConfig)
0881     : QToolBar(parent)
0882     , d(new KToolBarPrivate(this))
0883 {
0884     setObjectName(objectName);
0885     // mainToolBar -> isMainToolBar = true  -> buttonStyle is configurable
0886     // others      -> isMainToolBar = false -> ### hardcoded default for buttonStyle !!! should be configurable? -> hidden key added
0887     d->init(readConfig, (objectName == QLatin1String("mainToolBar")));
0888 
0889     // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow
0890     if (QMainWindow *mw = qobject_cast<QMainWindow *>(parent)) {
0891         mw->addToolBar(this);
0892     }
0893 }
0894 
0895 KToolBar::KToolBar(const QString &objectName, QMainWindow *parent, Qt::ToolBarArea area, bool newLine, bool isMainToolBar, bool readConfig)
0896     : QToolBar(parent)
0897     , d(new KToolBarPrivate(this))
0898 {
0899     setObjectName(objectName);
0900     d->init(readConfig, isMainToolBar);
0901 
0902     if (newLine) {
0903         mainWindow()->addToolBarBreak(area);
0904     }
0905 
0906     mainWindow()->addToolBar(area, this);
0907 
0908     if (newLine) {
0909         mainWindow()->addToolBarBreak(area);
0910     }
0911 }
0912 
0913 KToolBar::~KToolBar()
0914 {
0915     delete d->contextLockAction;
0916 }
0917 
0918 void KToolBar::saveSettings(KConfigGroup &cg)
0919 {
0920     Q_ASSERT(!cg.name().isEmpty());
0921 
0922     const int currentIconSize = iconSize().width();
0923     // qCDebug(DEBUG_KXMLGUI) << objectName() << currentIconSize << d->iconSizeSettings.toString() << "defaultValue=" << d->iconSizeSettings.defaultValue();
0924     if (!cg.hasDefault("IconSize") && currentIconSize == d->iconSizeSettings.defaultValue()) {
0925         cg.revertToDefault("IconSize");
0926         d->iconSizeSettings[Level_UserSettings] = Unset;
0927     } else {
0928         cg.writeEntry("IconSize", currentIconSize);
0929         d->iconSizeSettings[Level_UserSettings] = currentIconSize;
0930     }
0931 
0932     const Qt::ToolButtonStyle currentToolButtonStyle = toolButtonStyle();
0933     if (!cg.hasDefault("ToolButtonStyle") && currentToolButtonStyle == d->toolButtonStyleSettings.defaultValue()) {
0934         cg.revertToDefault("ToolButtonStyle");
0935         d->toolButtonStyleSettings[Level_UserSettings] = Unset;
0936     } else {
0937         cg.writeEntry("ToolButtonStyle", d->toolButtonStyleToString(currentToolButtonStyle));
0938         d->toolButtonStyleSettings[Level_UserSettings] = currentToolButtonStyle;
0939     }
0940 }
0941 
0942 void KToolBar::addXMLGUIClient(KXMLGUIClient *client)
0943 {
0944     d->xmlguiClients << client;
0945 }
0946 
0947 void KToolBar::removeXMLGUIClient(KXMLGUIClient *client)
0948 {
0949     d->xmlguiClients.remove(client);
0950 }
0951 
0952 void KToolBar::contextMenuEvent(QContextMenuEvent *event)
0953 {
0954     if (mainWindow()) {
0955         QPointer<KToolBar> guard(this);
0956         const QPoint globalPos = event->globalPos();
0957         d->contextMenu(globalPos)->exec(globalPos);
0958 
0959         // "Configure Toolbars" recreates toolbars, so we might not exist anymore.
0960         if (guard) {
0961             d->slotContextAboutToHide();
0962         }
0963         return;
0964     }
0965 
0966     QToolBar::contextMenuEvent(event);
0967 }
0968 
0969 void KToolBar::loadState(const QDomElement &element)
0970 {
0971     QMainWindow *mw = mainWindow();
0972     if (!mw) {
0973         return;
0974     }
0975 
0976     {
0977         const QString &i18nText = KToolbarHelper::i18nToolBarName(element);
0978         if (!i18nText.isEmpty()) {
0979             setWindowTitle(i18nText);
0980         }
0981     }
0982 
0983     /*
0984       This method is called in order to load toolbar settings from XML.
0985       However this can be used in two rather different cases:
0986       - for the initial loading of the app's XML. In that case the settings
0987       are only the defaults (Level_AppXML), the user's KConfig settings will override them
0988 
0989       - for later re-loading when switching between parts in KXMLGUIFactory.
0990       In that case the XML contains the final settings, not the defaults.
0991       We do need the defaults, and the toolbar might have been completely
0992       deleted and recreated meanwhile. So we store the app-default settings
0993       into the XML.
0994     */
0995     bool loadingAppDefaults = true;
0996     if (element.hasAttribute(QStringLiteral("tempXml"))) {
0997         // this isn't the first time, so the app-xml defaults have been saved into the (in-memory) XML
0998         loadingAppDefaults = false;
0999         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(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(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);
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(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(attrPosition);
1049         }
1050     }
1051     if (pos != Qt::NoToolBarArea) {
1052         mw->addToolBar(pos, 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.
1062 void 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"), d->getPositionAsString().toLower());
1070     current.setAttribute(QStringLiteral("hidden"), 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"), iconSize().width());
1077     }
1078 
1079     if (toolButtonStyle() == d->toolButtonStyleSettings.defaultValue()) {
1080         current.removeAttribute(QStringLiteral("iconText"));
1081     } else {
1082         current.setAttribute(QStringLiteral("iconText"), d->toolButtonStyleToString(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"), 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"), d->toolButtonStyleToString(bs));
1093     }
1094 }
1095 
1096 // called by KMainWindow::applyMainWindowSettings to read from the user settings
1097 void KToolBar::applySettings(const KConfigGroup &cg)
1098 {
1099     Q_ASSERT(!cg.name().isEmpty());
1100 
1101     if (cg.hasKey("IconSize")) {
1102         d->iconSizeSettings[Level_UserSettings] = cg.readEntry("IconSize", 0);
1103     }
1104     if (cg.hasKey("ToolButtonStyle")) {
1105         d->toolButtonStyleSettings[Level_UserSettings] = d->toolButtonStyleFromString(cg.readEntry("ToolButtonStyle", QString()));
1106     }
1107 
1108     d->applyCurrentSettings();
1109 }
1110 
1111 KMainWindow *KToolBar::mainWindow() const
1112 {
1113     return qobject_cast<KMainWindow *>(const_cast<QObject *>(parent()));
1114 }
1115 
1116 void KToolBar::setIconDimensions(int size)
1117 {
1118     QToolBar::setIconSize(QSize(size, size));
1119     d->iconSizeSettings[Level_UserSettings] = size;
1120 }
1121 
1122 int KToolBar::iconSizeDefault() const
1123 {
1124     return KIconLoader::global()->currentSize(d->isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar);
1125 }
1126 
1127 void KToolBar::slotMovableChanged(bool movable)
1128 {
1129     if (movable && !KAuthorized::authorize(QStringLiteral("movable_toolbars"))) {
1130         setMovable(false);
1131     }
1132 }
1133 
1134 void 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(actionNames)) {
1148             for (KActionCollection *ac : allCollections) {
1149                 QAction *newAction = ac->action(actionName);
1150                 if (newAction) {
1151                     d->actionsBeingDragged.append(newAction);
1152                     break;
1153                 }
1154             }
1155         }
1156 
1157         if (!d->actionsBeingDragged.isEmpty()) {
1158             QAction *overAction = actionAt(event->position().toPoint());
1159 
1160             QFrame *dropIndicatorWidget = new QFrame(this);
1161             dropIndicatorWidget->resize(8, height() - 4);
1162             dropIndicatorWidget->setFrameShape(QFrame::VLine);
1163             dropIndicatorWidget->setLineWidth(3);
1164 
1165             d->dropIndicatorAction = insertWidget(overAction, dropIndicatorWidget);
1166 
1167             insertAction(overAction, d->dropIndicatorAction);
1168 
1169             event->acceptProposedAction();
1170             return;
1171         }
1172     }
1173 
1174     QToolBar::dragEnterEvent(event);
1175 }
1176 
1177 void 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(d->dropIndicatorAction);
1196                     if (dropIndicatorIndex + 1 < actions.count()) {
1197                         if (actions.at(dropIndicatorIndex + 1) == overAction) {
1198                             break;
1199                         }
1200                     } else if (!overAction) {
1201                         break;
1202                     }
1203 
1204                     insertAction(overAction, d->dropIndicatorAction);
1205                 }
1206 
1207                 event->accept();
1208                 return;
1209             }
1210             break;
1211         }
1212     }
1213 
1214     QToolBar::dragMoveEvent(event);
1215 }
1216 
1217 void 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 
1232 void KToolBar::dropEvent(QDropEvent *event)
1233 {
1234     if (toolBarsEditable()) {
1235         for (QAction *action : std::as_const(d->actionsBeingDragged)) {
1236             if (actions().contains(action)) {
1237                 removeAction(action);
1238             }
1239             insertAction(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 
1256 void KToolBar::mousePressEvent(QMouseEvent *event)
1257 {
1258     if (toolBarsEditable() && event->button() == Qt::LeftButton) {
1259         if (QAction *action = actionAt(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 
1270 void 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(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(d->dragAction);
1306         }
1307     }
1308 
1309     d->dragAction = nullptr;
1310     event->accept();
1311 }
1312 
1313 void 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 
1325 bool 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 *>(watched)) {
1332                 if (ww->parent() == this && !ww->isEnabled()) {
1333                     QCoreApplication::postEvent(this,
1334                                                 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 *>(watched)) {
1343             if (!this->isAncestorOf(ww)) {
1344                 // New parent is not a subwidget - remove event filter
1345                 ww->removeEventFilter(this);
1346                 const auto children = ww->findChildren<QWidget *>();
1347                 for (QWidget *child : children) {
1348                     child->removeEventFilter(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 *>(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(&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(&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(&newEvent);
1389                 return true;
1390             }
1391             default:
1392                 break;
1393             }
1394         }
1395     }
1396 
1397     return QToolBar::eventFilter(watched, event);
1398 }
1399 
1400 void KToolBar::actionEvent(QActionEvent *event)
1401 {
1402     if (event->type() == QEvent::ActionRemoved) {
1403         QWidget *widget = widgetForAction(event->action());
1404         if (widget) {
1405             widget->removeEventFilter(this);
1406 
1407             const auto children = widget->findChildren<QWidget *>();
1408             for (QWidget *child : children) {
1409                 child->removeEventFilter(this);
1410             }
1411         }
1412     }
1413 
1414     QToolBar::actionEvent(event);
1415 
1416     if (event->type() == QEvent::ActionAdded) {
1417         QWidget *widget = widgetForAction(event->action());
1418         if (widget) {
1419             widget->installEventFilter(this);
1420 
1421             const auto children = widget->findChildren<QWidget *>();
1422             for (QWidget *child : children) {
1423                 child->installEventFilter(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 
1440 bool KToolBar::toolBarsEditable()
1441 {
1442     return KToolBarPrivate::s_editable;
1443 }
1444 
1445 void KToolBar::setToolBarsEditable(bool editable)
1446 {
1447     if (KToolBarPrivate::s_editable != editable) {
1448         KToolBarPrivate::s_editable = editable;
1449     }
1450 }
1451 
1452 void 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 
1467 bool KToolBar::toolBarsLocked()
1468 {
1469     return KToolBarPrivate::s_locked;
1470 }
1471 
1472 void KToolBar::emitToolbarStyleChanged()
1473 {
1474 #ifdef QT_DBUS_LIB
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"