File indexing completed on 2024-10-06 06:45:00
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 ¤t) 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"