File indexing completed on 2024-12-22 04:14:09

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