File indexing completed on 2024-09-08 12:23:21
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org> 0004 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org> 0005 SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only 0008 */ 0009 0010 #include "kedittoolbar.h" 0011 #include "debug.h" 0012 #include "kedittoolbar_p.h" 0013 0014 #include <QAction> 0015 #include <QApplication> 0016 #include <QCheckBox> 0017 #include <QComboBox> 0018 #include <QDialogButtonBox> 0019 #include <QDir> 0020 #include <QDomDocument> 0021 #include <QFile> 0022 #include <QGridLayout> 0023 #include <QLabel> 0024 #include <QLineEdit> 0025 #include <QMimeData> 0026 #include <QPushButton> 0027 #include <QShowEvent> 0028 #include <QStandardPaths> 0029 #include <QToolButton> 0030 0031 #include <KIconDialog> 0032 #include <KListWidgetSearchLine> 0033 #include <KLocalizedString> 0034 #include <KMessageBox> 0035 #include <KSeparator> 0036 0037 #include "kactioncollection.h" 0038 #include "ktoolbar.h" 0039 #include "kxmlguifactory.h" 0040 0041 #include "../kxmlgui_version.h" 0042 #include "ktoolbarhelper_p.h" 0043 0044 // static const char *const s_XmlTypeToString[] = { "Shell", "Part", "Local", "Merged" }; 0045 0046 typedef QList<QDomElement> ToolBarList; 0047 0048 namespace KDEPrivate 0049 { 0050 /** 0051 * Return a list of toolbar elements given a toplevel element 0052 */ 0053 static ToolBarList findToolBars(const QDomElement &start) 0054 { 0055 ToolBarList list; 0056 0057 for (QDomElement elem = start; !elem.isNull(); elem = elem.nextSiblingElement()) { 0058 if (elem.tagName() == QLatin1String("ToolBar")) { 0059 if (elem.attribute(QStringLiteral("noEdit")) != QLatin1String("true")) { 0060 list.append(elem); 0061 } 0062 } else { 0063 if (elem.tagName() != QLatin1String("MenuBar")) { // there are no toolbars inside the menubar :) 0064 list += findToolBars(elem.firstChildElement()); // recursive 0065 } 0066 } 0067 } 0068 0069 return list; 0070 } 0071 0072 class XmlData 0073 { 0074 public: 0075 enum XmlType { Shell = 0, Part, Local, Merged }; 0076 0077 explicit XmlData(XmlType xmlType, const QString &xmlFile, KActionCollection *collection) 0078 : m_isModified(false) 0079 , m_xmlFile(xmlFile) 0080 , m_type(xmlType) 0081 , m_actionCollection(collection) 0082 { 0083 } 0084 void dump() const 0085 { 0086 #if 0 0087 qDebug(240) << "XmlData" << this << "type" << s_XmlTypeToString[m_type] << "xmlFile:" << m_xmlFile; 0088 foreach (const QDomElement &element, m_barList) { 0089 qDebug(240) << " ToolBar:" << toolBarText(element); 0090 } 0091 if (m_actionCollection) { 0092 qDebug(240) << " " << m_actionCollection->actions().count() << "actions in the collection."; 0093 } else { 0094 qDebug(240) << " no action collection."; 0095 } 0096 #endif 0097 } 0098 QString xmlFile() const 0099 { 0100 return m_xmlFile; 0101 } 0102 XmlType type() const 0103 { 0104 return m_type; 0105 } 0106 KActionCollection *actionCollection() const 0107 { 0108 return m_actionCollection; 0109 } 0110 void setDomDocument(const QDomDocument &domDoc) 0111 { 0112 m_document = domDoc.cloneNode().toDocument(); 0113 m_barList = findToolBars(m_document.documentElement()); 0114 } 0115 // Return reference, for e.g. actionPropertiesElement() to modify the document 0116 QDomDocument &domDocument() 0117 { 0118 return m_document; 0119 } 0120 const QDomDocument &domDocument() const 0121 { 0122 return m_document; 0123 } 0124 0125 /** 0126 * Return the text (user-visible name) of a given toolbar 0127 */ 0128 QString toolBarText(const QDomElement &it) const; 0129 0130 bool m_isModified = false; 0131 ToolBarList &barList() 0132 { 0133 return m_barList; 0134 } 0135 const ToolBarList &barList() const 0136 { 0137 return m_barList; 0138 } 0139 0140 private: 0141 ToolBarList m_barList; 0142 QString m_xmlFile; 0143 QDomDocument m_document; 0144 XmlType m_type; 0145 KActionCollection *m_actionCollection; 0146 }; 0147 0148 QString XmlData::toolBarText(const QDomElement &it) const 0149 { 0150 QString name = KToolbarHelper::i18nToolBarName(it); 0151 0152 // the name of the toolbar might depend on whether or not 0153 // it is in kparts 0154 if ((m_type == XmlData::Shell) || (m_type == XmlData::Part)) { 0155 QString doc_name(m_document.documentElement().attribute(QStringLiteral("name"))); 0156 name += QLatin1String(" <") + doc_name + QLatin1Char('>'); 0157 } 0158 return name; 0159 } 0160 0161 typedef QList<XmlData> XmlDataList; 0162 0163 class ToolBarItem : public QListWidgetItem 0164 { 0165 public: 0166 ToolBarItem(QListWidget *parent, const QString &tag = QString(), const QString &name = QString(), const QString &statusText = QString()) 0167 : QListWidgetItem(parent) 0168 , m_internalTag(tag) 0169 , m_internalName(name) 0170 , m_statusText(statusText) 0171 , m_isSeparator(false) 0172 , m_isSpacer(false) 0173 , m_isTextAlongsideIconHidden(false) 0174 { 0175 // Drop between items, not onto items 0176 setFlags((flags() | Qt::ItemIsDragEnabled) & ~Qt::ItemIsDropEnabled); 0177 } 0178 0179 void setInternalTag(const QString &tag) 0180 { 0181 m_internalTag = tag; 0182 } 0183 void setInternalName(const QString &name) 0184 { 0185 m_internalName = name; 0186 } 0187 void setStatusText(const QString &text) 0188 { 0189 m_statusText = text; 0190 } 0191 void setSeparator(bool sep) 0192 { 0193 m_isSeparator = sep; 0194 } 0195 void setSpacer(bool spacer) 0196 { 0197 m_isSpacer = spacer; 0198 } 0199 void setTextAlongsideIconHidden(bool hidden) 0200 { 0201 m_isTextAlongsideIconHidden = hidden; 0202 } 0203 QString internalTag() const 0204 { 0205 return m_internalTag; 0206 } 0207 QString internalName() const 0208 { 0209 return m_internalName; 0210 } 0211 QString statusText() const 0212 { 0213 return m_statusText; 0214 } 0215 bool isSeparator() const 0216 { 0217 return m_isSeparator; 0218 } 0219 bool isSpacer() const 0220 { 0221 return m_isSpacer; 0222 } 0223 bool isTextAlongsideIconHidden() const 0224 { 0225 return m_isTextAlongsideIconHidden; 0226 } 0227 0228 int index() const 0229 { 0230 return listWidget()->row(const_cast<ToolBarItem *>(this)); 0231 } 0232 0233 private: 0234 QString m_internalTag; 0235 QString m_internalName; 0236 QString m_statusText; 0237 bool m_isSeparator; 0238 bool m_isSpacer; 0239 bool m_isTextAlongsideIconHidden; 0240 }; 0241 0242 static QDataStream &operator<<(QDataStream &s, const ToolBarItem &item) 0243 { 0244 s << item.internalTag(); 0245 s << item.internalName(); 0246 s << item.statusText(); 0247 s << item.isSeparator(); 0248 s << item.isSpacer(); 0249 s << item.isTextAlongsideIconHidden(); 0250 return s; 0251 } 0252 static QDataStream &operator>>(QDataStream &s, ToolBarItem &item) 0253 { 0254 QString internalTag; 0255 s >> internalTag; 0256 item.setInternalTag(internalTag); 0257 QString internalName; 0258 s >> internalName; 0259 item.setInternalName(internalName); 0260 QString statusText; 0261 s >> statusText; 0262 item.setStatusText(statusText); 0263 bool sep; 0264 s >> sep; 0265 item.setSeparator(sep); 0266 bool spacer; 0267 s >> spacer; 0268 item.setSpacer(spacer); 0269 bool hidden; 0270 s >> hidden; 0271 item.setTextAlongsideIconHidden(hidden); 0272 return s; 0273 } 0274 0275 //// 0276 0277 ToolBarListWidget::ToolBarListWidget(QWidget *parent) 0278 : QListWidget(parent) 0279 , m_activeList(true) 0280 { 0281 setDragDropMode(QAbstractItemView::DragDrop); // no internal moves 0282 } 0283 0284 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0285 QMimeData *ToolBarListWidget::mimeData(const QList<QListWidgetItem *> items) const 0286 #else 0287 QMimeData *ToolBarListWidget::mimeData(const QList<QListWidgetItem *> &items) const 0288 #endif 0289 { 0290 if (items.isEmpty()) { 0291 return nullptr; 0292 } 0293 QMimeData *mimedata = new QMimeData(); 0294 0295 QByteArray data; 0296 { 0297 QDataStream stream(&data, QIODevice::WriteOnly); 0298 // we only support single selection 0299 ToolBarItem *item = static_cast<ToolBarItem *>(items.first()); 0300 stream << *item; 0301 } 0302 0303 mimedata->setData(QStringLiteral("application/x-kde-action-list"), data); 0304 mimedata->setData(QStringLiteral("application/x-kde-source-treewidget"), m_activeList ? "active" : "inactive"); 0305 0306 return mimedata; 0307 } 0308 0309 bool ToolBarListWidget::dropMimeData(int index, const QMimeData *mimeData, Qt::DropAction action) 0310 { 0311 Q_UNUSED(action) 0312 const QByteArray data = mimeData->data(QStringLiteral("application/x-kde-action-list")); 0313 if (data.isEmpty()) { 0314 return false; 0315 } 0316 QDataStream stream(data); 0317 const bool sourceIsActiveList = mimeData->data(QStringLiteral("application/x-kde-source-treewidget")) == "active"; 0318 ToolBarItem *item = new ToolBarItem(this); // needs parent, use this temporarily 0319 stream >> *item; 0320 Q_EMIT dropped(this, index, item, sourceIsActiveList); 0321 return true; 0322 } 0323 0324 ToolBarItem *ToolBarListWidget::currentItem() const 0325 { 0326 return static_cast<ToolBarItem *>(QListWidget::currentItem()); 0327 } 0328 0329 IconTextEditDialog::IconTextEditDialog(QWidget *parent) 0330 : QDialog(parent) 0331 { 0332 setWindowTitle(i18nc("@title:window", "Change Text")); 0333 setModal(true); 0334 0335 QVBoxLayout *layout = new QVBoxLayout(this); 0336 0337 QGridLayout *grid = new QGridLayout; 0338 grid->setContentsMargins(0, 0, 0, 0); 0339 0340 m_lineEdit = new QLineEdit(this); 0341 m_lineEdit->setClearButtonEnabled(true); 0342 QLabel *label = new QLabel(i18n("Icon te&xt:"), this); 0343 label->setBuddy(m_lineEdit); 0344 grid->addWidget(label, 0, 0); 0345 grid->addWidget(m_lineEdit, 0, 1); 0346 0347 m_cbHidden = new QCheckBox(i18nc("@option:check", "&Hide text when toolbar shows text alongside icons"), this); 0348 grid->addWidget(m_cbHidden, 1, 1); 0349 0350 layout->addLayout(grid); 0351 0352 m_buttonBox = new QDialogButtonBox(this); 0353 m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0354 connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); 0355 connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0356 layout->addWidget(m_buttonBox); 0357 0358 connect(m_lineEdit, &QLineEdit::textChanged, this, &IconTextEditDialog::slotTextChanged); 0359 0360 m_lineEdit->setFocus(); 0361 setFixedHeight(sizeHint().height()); 0362 } 0363 0364 void IconTextEditDialog::setIconText(const QString &text) 0365 { 0366 m_lineEdit->setText(text); 0367 } 0368 0369 QString IconTextEditDialog::iconText() const 0370 { 0371 return m_lineEdit->text().trimmed(); 0372 } 0373 0374 void IconTextEditDialog::setTextAlongsideIconHidden(bool hidden) 0375 { 0376 m_cbHidden->setChecked(hidden); 0377 } 0378 0379 bool IconTextEditDialog::textAlongsideIconHidden() const 0380 { 0381 return m_cbHidden->isChecked(); 0382 } 0383 0384 void IconTextEditDialog::slotTextChanged(const QString &text) 0385 { 0386 // Do not allow empty icon text 0387 m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.trimmed().isEmpty()); 0388 } 0389 0390 class KEditToolBarWidgetPrivate 0391 { 0392 public: 0393 /** 0394 * 0395 * @param collection In the old-style constructor, this is the collection passed 0396 * to the KEditToolBar constructor. 0397 * In the xmlguifactory-based constructor, we let KXMLGUIClient create a dummy one, 0398 * but it probably isn't used. 0399 */ 0400 KEditToolBarWidgetPrivate(KEditToolBarWidget *widget, const QString &cName, KActionCollection *collection) 0401 : m_collection(collection) 0402 , m_widget(widget) 0403 , m_factory(nullptr) 0404 , m_componentName(cName) 0405 , m_helpArea(nullptr) 0406 , m_isPart(false) 0407 , m_loadedOnce(false) 0408 { 0409 // We want items with an icon to align with items without icon 0410 // So we use an empty QPixmap for that 0411 const int iconSize = widget->style()->pixelMetric(QStyle::PM_SmallIconSize); 0412 m_emptyIcon = QPixmap(iconSize, iconSize); 0413 m_emptyIcon.fill(Qt::transparent); 0414 } 0415 ~KEditToolBarWidgetPrivate() 0416 { 0417 } 0418 0419 // private slots 0420 void slotToolBarSelected(int index); 0421 0422 void slotInactiveSelectionChanged(); 0423 void slotActiveSelectionChanged(); 0424 0425 void slotInsertButton(); 0426 void slotRemoveButton(); 0427 void slotUpButton(); 0428 void slotDownButton(); 0429 0430 void selectActiveItem(const QString &); 0431 0432 void slotChangeIcon(); 0433 void slotChangeIconText(); 0434 0435 void slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList); 0436 0437 void setupLayout(); 0438 0439 void initOldStyle(const QString &file, bool global, const QString &defaultToolbar); 0440 void initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolbar); 0441 void loadToolBarCombo(const QString &defaultToolbar); 0442 void loadActions(const QDomElement &elem); 0443 0444 QString xmlFile(const QString &xml_file) const 0445 { 0446 return xml_file.isEmpty() ? m_componentName + QLatin1String("ui.rc") : xml_file; 0447 } 0448 0449 /** 0450 * Load in the specified XML file and dump the raw xml 0451 */ 0452 QString loadXMLFile(const QString &_xml_file) 0453 { 0454 QString raw_xml; 0455 QString xml_file = xmlFile(_xml_file); 0456 // qCDebug(DEBUG_KXMLGUI) << "loadXMLFile xml_file=" << xml_file; 0457 0458 if (!QDir::isRelativePath(xml_file)) { 0459 raw_xml = KXMLGUIFactory::readConfigFile(xml_file); 0460 } else { 0461 raw_xml = KXMLGUIFactory::readConfigFile(xml_file, m_componentName); 0462 } 0463 0464 return raw_xml; 0465 } 0466 0467 /** 0468 * Look for a given item in the current toolbar 0469 */ 0470 QDomElement findElementForToolBarItem(const ToolBarItem *item) const 0471 { 0472 // qDebug(240) << "looking for name=" << item->internalName() << "and tag=" << item->internalTag(); 0473 for (QDomNode n = m_currentToolBarElem.firstChild(); !n.isNull(); n = n.nextSibling()) { 0474 QDomElement elem = n.toElement(); 0475 if ((elem.attribute(QStringLiteral("name")) == item->internalName()) && (elem.tagName() == item->internalTag())) { 0476 return elem; 0477 } 0478 } 0479 // qDebug(240) << "no item found in the DOM with name=" << item->internalName() << "and tag=" << item->internalTag(); 0480 return QDomElement(); 0481 } 0482 0483 void insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend = false); 0484 void removeActive(ToolBarItem *item); 0485 void moveActive(ToolBarItem *item, ToolBarItem *before); 0486 void updateLocal(QDomElement &elem); 0487 0488 #ifndef NDEBUG 0489 void dump() const 0490 { 0491 for (const auto &xmlFile : m_xmlFiles) { 0492 xmlFile.dump(); 0493 } 0494 } 0495 #endif 0496 0497 QComboBox *m_toolbarCombo; 0498 0499 QToolButton *m_upAction; 0500 QToolButton *m_removeAction; 0501 QToolButton *m_insertAction; 0502 QToolButton *m_downAction; 0503 0504 // QValueList<QAction*> m_actionList; 0505 KActionCollection *m_collection; 0506 KEditToolBarWidget *const m_widget; 0507 KXMLGUIFactory *m_factory; 0508 QString m_componentName; 0509 0510 QPixmap m_emptyIcon; 0511 0512 XmlData *m_currentXmlData; 0513 QDomElement m_currentToolBarElem; 0514 0515 QString m_xmlFile; 0516 QString m_globalFile; 0517 QString m_rcFile; 0518 QDomDocument m_localDoc; 0519 0520 ToolBarList m_barList; 0521 ToolBarListWidget *m_inactiveList; 0522 ToolBarListWidget *m_activeList; 0523 0524 XmlDataList m_xmlFiles; 0525 0526 QLabel *m_comboLabel; 0527 KSeparator *m_comboSeparator; 0528 QLabel *m_helpArea; 0529 QPushButton *m_changeIcon; 0530 QPushButton *m_changeIconText; 0531 bool m_isPart : 1; 0532 bool m_loadedOnce : 1; 0533 }; 0534 0535 } 0536 0537 using namespace KDEPrivate; 0538 0539 class KEditToolBarPrivate 0540 { 0541 public: 0542 KEditToolBarPrivate(KEditToolBar *qq) 0543 : q(qq) 0544 { 0545 } 0546 0547 void init(); 0548 0549 void slotButtonClicked(QAbstractButton *button); 0550 void acceptOK(bool); 0551 void enableApply(bool); 0552 void okClicked(); 0553 void applyClicked(); 0554 void defaultClicked(); 0555 0556 KEditToolBar *const q; 0557 bool m_accept = false; 0558 // Save parameters for recreating widget after resetting toolbar 0559 bool m_global = false; 0560 KActionCollection *m_collection = nullptr; 0561 QString m_file; 0562 QString m_defaultToolBar; 0563 KXMLGUIFactory *m_factory = nullptr; 0564 KEditToolBarWidget *m_widget = nullptr; 0565 QVBoxLayout *m_layout = nullptr; 0566 QDialogButtonBox *m_buttonBox = nullptr; 0567 }; 0568 0569 Q_GLOBAL_STATIC(QString, s_defaultToolBarName) 0570 0571 KEditToolBar::KEditToolBar(KActionCollection *collection, QWidget *parent) 0572 : QDialog(parent) 0573 , d(new KEditToolBarPrivate(this)) 0574 { 0575 d->m_widget = new KEditToolBarWidget(collection, this); 0576 d->init(); 0577 d->m_collection = collection; 0578 } 0579 0580 KEditToolBar::KEditToolBar(KXMLGUIFactory *factory, QWidget *parent) 0581 : QDialog(parent) 0582 , d(new KEditToolBarPrivate(this)) 0583 { 0584 d->m_widget = new KEditToolBarWidget(this); 0585 d->init(); 0586 d->m_factory = factory; 0587 } 0588 0589 void KEditToolBarPrivate::init() 0590 { 0591 m_accept = false; 0592 m_factory = nullptr; 0593 0594 q->setDefaultToolBar(QString()); 0595 0596 q->setWindowTitle(i18nc("@title:window", "Configure Toolbars")); 0597 q->setModal(false); 0598 0599 m_layout = new QVBoxLayout(q); 0600 m_layout->addWidget(m_widget); 0601 0602 m_buttonBox = new QDialogButtonBox(q); 0603 m_buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); 0604 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); 0605 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply()); 0606 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); 0607 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); 0608 q->connect(m_buttonBox, &QDialogButtonBox::clicked, q, [this](QAbstractButton *button) { 0609 slotButtonClicked(button); 0610 }); 0611 QObject::connect(m_buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); 0612 m_layout->addWidget(m_buttonBox); 0613 0614 q->connect(m_widget, &KEditToolBarWidget::enableOk, q, [this](bool state) { 0615 acceptOK(state); 0616 enableApply(state); 0617 }); 0618 enableApply(false); 0619 0620 q->setMinimumSize(q->sizeHint()); 0621 } 0622 0623 void KEditToolBar::setResourceFile(const QString &file, bool global) 0624 { 0625 d->m_file = file; 0626 d->m_global = global; 0627 d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar); 0628 } 0629 0630 KEditToolBar::~KEditToolBar() 0631 { 0632 s_defaultToolBarName()->clear(); 0633 } 0634 0635 void KEditToolBar::setDefaultToolBar(const QString &toolBarName) 0636 { 0637 if (toolBarName.isEmpty()) { 0638 d->m_defaultToolBar = *s_defaultToolBarName(); 0639 } else { 0640 d->m_defaultToolBar = toolBarName; 0641 } 0642 } 0643 0644 void KEditToolBarPrivate::acceptOK(bool b) 0645 { 0646 m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(b); 0647 m_accept = b; 0648 } 0649 0650 void KEditToolBarPrivate::enableApply(bool b) 0651 { 0652 m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(b); 0653 } 0654 0655 void KEditToolBarPrivate::defaultClicked() 0656 { 0657 if (KMessageBox::warningContinueCancel( 0658 q, 0659 i18n("Do you really want to reset all toolbars of this application to their default? The changes will be applied immediately."), 0660 i18n("Reset Toolbars"), 0661 KGuiItem(i18n("Reset"))) 0662 != KMessageBox::Continue) { 0663 return; 0664 } 0665 0666 KEditToolBarWidget *oldWidget = m_widget; 0667 m_widget = nullptr; 0668 m_accept = false; 0669 0670 if (m_factory) { 0671 const auto clients = m_factory->clients(); 0672 for (KXMLGUIClient *client : clients) { 0673 const QString file = client->localXMLFile(); 0674 if (file.isEmpty()) { 0675 continue; 0676 } 0677 // qDebug(240) << "Deleting local xml file" << file; 0678 // << "for client" << client << typeid(*client).name(); 0679 if (QFile::exists(file)) { 0680 if (!QFile::remove(file)) { 0681 qCWarning(DEBUG_KXMLGUI) << "Could not delete" << file; 0682 } 0683 } 0684 } 0685 0686 // Reload the xml files in all clients, now that the local files are gone 0687 oldWidget->rebuildKXMLGUIClients(); 0688 0689 m_widget = new KEditToolBarWidget(q); 0690 m_widget->load(m_factory, m_defaultToolBar); 0691 } else { 0692 int slash = m_file.lastIndexOf(QLatin1Char('/')) + 1; 0693 if (slash) { 0694 m_file.remove(0, slash); 0695 } 0696 const QString xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui5/") 0697 + QCoreApplication::instance()->applicationName() + QLatin1Char('/') + m_file; 0698 0699 if (QFile::exists(xml_file)) { 0700 if (!QFile::remove(xml_file)) { 0701 qCWarning(DEBUG_KXMLGUI) << "Could not delete " << xml_file; 0702 } 0703 } 0704 0705 m_widget = new KEditToolBarWidget(m_collection, q); 0706 q->setResourceFile(m_file, m_global); 0707 } 0708 0709 // Copy the geometry to minimize UI flicker 0710 m_widget->setGeometry(oldWidget->geometry()); 0711 delete oldWidget; 0712 m_layout->insertWidget(0, m_widget); 0713 0714 q->connect(m_widget, &KEditToolBarWidget::enableOk, q, [this](bool state) { 0715 acceptOK(state); 0716 enableApply(state); 0717 }); 0718 enableApply(false); 0719 0720 Q_EMIT q->newToolBarConfig(); 0721 #if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) 0722 Q_EMIT q->newToolbarConfig(); // compat 0723 #endif 0724 } 0725 0726 void KEditToolBarPrivate::slotButtonClicked(QAbstractButton *button) 0727 { 0728 QDialogButtonBox::StandardButton type = m_buttonBox->standardButton(button); 0729 0730 switch (type) { 0731 case QDialogButtonBox::Ok: 0732 okClicked(); 0733 break; 0734 case QDialogButtonBox::Apply: 0735 applyClicked(); 0736 break; 0737 case QDialogButtonBox::RestoreDefaults: 0738 defaultClicked(); 0739 break; 0740 default: 0741 break; 0742 } 0743 } 0744 0745 void KEditToolBarPrivate::okClicked() 0746 { 0747 if (!m_accept) { 0748 q->reject(); 0749 return; 0750 } 0751 0752 // Do not rebuild GUI and emit the "newToolBarConfig" signal again here if the "Apply" 0753 // button was already pressed and no further changes were made. 0754 if (m_buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) { 0755 m_widget->save(); 0756 Q_EMIT q->newToolBarConfig(); 0757 #if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) 0758 Q_EMIT q->newToolbarConfig(); // compat 0759 #endif 0760 } 0761 q->accept(); 0762 } 0763 0764 void KEditToolBarPrivate::applyClicked() 0765 { 0766 (void)m_widget->save(); 0767 enableApply(false); 0768 Q_EMIT q->newToolBarConfig(); 0769 #if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) 0770 Q_EMIT q->newToolbarConfig(); // compat 0771 #endif 0772 } 0773 0774 void KEditToolBar::setGlobalDefaultToolBar(const char *toolbarName) 0775 { 0776 *s_defaultToolBarName() = QString::fromLatin1(toolbarName); 0777 } 0778 0779 KEditToolBarWidget::KEditToolBarWidget(KActionCollection *collection, QWidget *parent) 0780 : QWidget(parent) 0781 , d(new KEditToolBarWidgetPrivate(this, componentName(), collection)) 0782 { 0783 d->setupLayout(); 0784 } 0785 0786 KEditToolBarWidget::KEditToolBarWidget(QWidget *parent) 0787 : QWidget(parent) 0788 , d(new KEditToolBarWidgetPrivate(this, componentName(), KXMLGUIClient::actionCollection() /*create new one*/)) 0789 { 0790 d->setupLayout(); 0791 } 0792 0793 KEditToolBarWidget::~KEditToolBarWidget() 0794 { 0795 delete d; 0796 } 0797 0798 void KEditToolBarWidget::load(const QString &file, bool global, const QString &defaultToolBar) 0799 { 0800 d->initOldStyle(file, global, defaultToolBar); 0801 } 0802 0803 void KEditToolBarWidget::load(KXMLGUIFactory *factory, const QString &defaultToolBar) 0804 { 0805 d->initFromFactory(factory, defaultToolBar); 0806 } 0807 0808 void KEditToolBarWidgetPrivate::initOldStyle(const QString &resourceFile, bool global, const QString &defaultToolBar) 0809 { 0810 // TODO: make sure we can call this multiple times? 0811 if (m_loadedOnce) { 0812 return; 0813 } 0814 0815 m_loadedOnce = true; 0816 // d->m_actionList = collection->actions(); 0817 0818 // handle the merging 0819 if (global) { 0820 m_widget->loadStandardsXmlFile(); // ui_standards.rc 0821 } 0822 const QString localXML = loadXMLFile(resourceFile); 0823 m_widget->setXML(localXML, global ? true /*merge*/ : false); 0824 0825 // first, get all of the necessary info for our local xml 0826 XmlData local(XmlData::Local, xmlFile(resourceFile), m_collection); 0827 QDomDocument domDoc; 0828 domDoc.setContent(localXML); 0829 local.setDomDocument(domDoc); 0830 m_xmlFiles.append(local); 0831 0832 // then, the merged one (ui_standards + local xml) 0833 XmlData merge(XmlData::Merged, QString(), m_collection); 0834 merge.setDomDocument(m_widget->domDocument()); 0835 m_xmlFiles.append(merge); 0836 0837 #ifndef NDEBUG 0838 dump(); 0839 #endif 0840 0841 // now load in our toolbar combo box 0842 loadToolBarCombo(defaultToolBar); 0843 m_widget->adjustSize(); 0844 m_widget->setMinimumSize(m_widget->sizeHint()); 0845 } 0846 0847 void KEditToolBarWidgetPrivate::initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolBar) 0848 { 0849 // TODO: make sure we can call this multiple times? 0850 if (m_loadedOnce) { 0851 return; 0852 } 0853 0854 m_loadedOnce = true; 0855 0856 m_factory = factory; 0857 0858 // add all of the client data 0859 bool first = true; 0860 const auto clients = factory->clients(); 0861 for (KXMLGUIClient *client : clients) { 0862 if (client->xmlFile().isEmpty()) { 0863 continue; 0864 } 0865 0866 XmlData::XmlType type = XmlData::Part; 0867 if (first) { 0868 type = XmlData::Shell; 0869 first = false; 0870 Q_ASSERT(!client->localXMLFile().isEmpty()); // where would we save changes?? 0871 } 0872 0873 XmlData data(type, client->localXMLFile(), client->actionCollection()); 0874 QDomDocument domDoc = client->domDocument(); 0875 data.setDomDocument(domDoc); 0876 m_xmlFiles.append(data); 0877 0878 // d->m_actionList += client->actionCollection()->actions(); 0879 } 0880 0881 #ifndef NDEBUG 0882 // d->dump(); 0883 #endif 0884 0885 // now load in our toolbar combo box 0886 loadToolBarCombo(defaultToolBar); 0887 m_widget->adjustSize(); 0888 m_widget->setMinimumSize(m_widget->sizeHint()); 0889 0890 m_widget->actionCollection()->addAssociatedWidget(m_widget); 0891 const auto widgetActions = m_widget->actionCollection()->actions(); 0892 for (QAction *action : widgetActions) { 0893 action->setShortcutContext(Qt::WidgetWithChildrenShortcut); 0894 } 0895 } 0896 0897 void KEditToolBarWidget::save() 0898 { 0899 // qDebug(240) << "KEditToolBarWidget::save"; 0900 for (const auto &xmlFile : std::as_const(d->m_xmlFiles)) { 0901 // let's not save non-modified files 0902 if (!xmlFile.m_isModified) { 0903 continue; 0904 } 0905 0906 // let's also skip (non-existent) merged files 0907 if (xmlFile.type() == XmlData::Merged) { 0908 continue; 0909 } 0910 0911 // Add noMerge="1" to all the menus since we are saving the merged data 0912 QDomNodeList menuNodes = xmlFile.domDocument().elementsByTagName(QStringLiteral("Menu")); 0913 for (int i = 0; i < menuNodes.length(); ++i) { 0914 QDomNode menuNode = menuNodes.item(i); 0915 QDomElement menuElement = menuNode.toElement(); 0916 if (menuElement.isNull()) { 0917 continue; 0918 } 0919 menuElement.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); 0920 } 0921 0922 // qCDebug(DEBUG_KXMLGUI) << (*it).domDocument().toString(); 0923 0924 // qDebug(240) << "Saving " << (*it).xmlFile(); 0925 // if we got this far, we might as well just save it 0926 KXMLGUIFactory::saveConfigFile(xmlFile.domDocument(), xmlFile.xmlFile()); 0927 } 0928 0929 if (!d->m_factory) { 0930 return; 0931 } 0932 0933 rebuildKXMLGUIClients(); 0934 } 0935 0936 void KEditToolBarWidget::rebuildKXMLGUIClients() 0937 { 0938 if (!d->m_factory) { 0939 return; 0940 } 0941 0942 const QList<KXMLGUIClient *> clients = d->m_factory->clients(); 0943 // qDebug() << "factory: " << clients.count() << " clients"; 0944 0945 if (clients.isEmpty()) { 0946 return; 0947 } 0948 0949 // remove the elements starting from the last going to the first 0950 for (auto it = clients.crbegin(); it != clients.crend(); ++it) { 0951 // qDebug() << "factory->removeClient " << client; 0952 d->m_factory->removeClient(*it); 0953 } 0954 0955 KXMLGUIClient *firstClient = clients.first(); 0956 0957 // now, rebuild the gui from the first to the last 0958 // qDebug(240) << "rebuilding the gui"; 0959 for (KXMLGUIClient *client : clients) { 0960 // qDebug(240) << "updating client " << client << " " << client->componentName() << " xmlFile=" << client->xmlFile(); 0961 QString file(client->xmlFile()); // before setting ui_standards! 0962 if (!file.isEmpty()) { 0963 // passing an empty stream forces the clients to reread the XML 0964 client->setXMLGUIBuildDocument(QDomDocument()); 0965 0966 // for the shell, merge in ui_standards.rc 0967 if (client == firstClient) { // same assumption as in the ctor: first==shell 0968 client->loadStandardsXmlFile(); 0969 } 0970 0971 // and this forces it to use the *new* XML file 0972 client->setXMLFile(file, client == firstClient /* merge if shell */); 0973 0974 // [we can't use reloadXML, it doesn't load ui_standards.rc] 0975 } 0976 } 0977 0978 // Now we can add the clients to the factory 0979 // We don't do it in the loop above because adding a part automatically 0980 // adds its plugins, so we must make sure the plugins were updated first. 0981 for (KXMLGUIClient *client : clients) { 0982 d->m_factory->addClient(client); 0983 } 0984 } 0985 0986 void KEditToolBarWidgetPrivate::setupLayout() 0987 { 0988 // the toolbar name combo 0989 m_comboLabel = new QLabel(i18n("&Toolbar:"), m_widget); 0990 m_toolbarCombo = new QComboBox(m_widget); 0991 m_comboLabel->setBuddy(m_toolbarCombo); 0992 m_comboSeparator = new KSeparator(m_widget); 0993 QObject::connect(m_toolbarCombo, qOverload<int>(&QComboBox::activated), m_widget, [this](int index) { 0994 slotToolBarSelected(index); 0995 }); 0996 0997 // QPushButton *new_toolbar = new QPushButton(i18n("&New"), this); 0998 // new_toolbar->setPixmap(BarIcon("document-new", KIconLoader::SizeSmall)); 0999 // new_toolbar->setEnabled(false); // disabled until implemented 1000 // QPushButton *del_toolbar = new QPushButton(i18n("&Delete"), this); 1001 // del_toolbar->setPixmap(BarIcon("edit-delete", KIconLoader::SizeSmall)); 1002 // del_toolbar->setEnabled(false); // disabled until implemented 1003 1004 // our list of inactive actions 1005 QLabel *inactive_label = new QLabel(i18n("A&vailable actions:"), m_widget); 1006 m_inactiveList = new ToolBarListWidget(m_widget); 1007 m_inactiveList->setDragEnabled(true); 1008 m_inactiveList->setActiveList(false); 1009 m_inactiveList->setMinimumSize(180, 200); 1010 m_inactiveList->setDropIndicatorShown(false); // #165663 1011 inactive_label->setBuddy(m_inactiveList); 1012 QObject::connect(m_inactiveList, &QListWidget::itemSelectionChanged, m_widget, [this]() { 1013 slotInactiveSelectionChanged(); 1014 }); 1015 QObject::connect(m_inactiveList, &QListWidget::itemDoubleClicked, m_widget, [this]() { 1016 slotInsertButton(); 1017 }); 1018 QObject::connect(m_inactiveList, 1019 &ToolBarListWidget::dropped, 1020 m_widget, 1021 [this](ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList) { 1022 slotDropped(list, index, item, sourceIsActiveList); 1023 }); 1024 1025 KListWidgetSearchLine *inactiveListSearchLine = new KListWidgetSearchLine(m_widget, m_inactiveList); 1026 inactiveListSearchLine->setPlaceholderText(i18n("Filter")); 1027 1028 // our list of active actions 1029 QLabel *active_label = new QLabel(i18n("Curr&ent actions:"), m_widget); 1030 m_activeList = new ToolBarListWidget(m_widget); 1031 m_activeList->setDragEnabled(true); 1032 m_activeList->setActiveList(true); 1033 // With Qt-4.1 only setting MiniumWidth results in a 0-width icon column ... 1034 m_activeList->setMinimumSize(m_inactiveList->minimumWidth(), 100); 1035 active_label->setBuddy(m_activeList); 1036 1037 QObject::connect(m_activeList, &QListWidget::itemSelectionChanged, m_widget, [this]() { 1038 slotActiveSelectionChanged(); 1039 }); 1040 QObject::connect(m_activeList, &QListWidget::itemDoubleClicked, m_widget, [this]() { 1041 slotRemoveButton(); 1042 }); 1043 QObject::connect(m_activeList, 1044 &ToolBarListWidget::dropped, 1045 m_widget, 1046 [this](ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList) { 1047 slotDropped(list, index, item, sourceIsActiveList); 1048 }); 1049 1050 KListWidgetSearchLine *activeListSearchLine = new KListWidgetSearchLine(m_widget, m_activeList); 1051 activeListSearchLine->setPlaceholderText(i18n("Filter")); 1052 1053 // "change icon" button 1054 m_changeIcon = new QPushButton(i18nc("@action:button", "Change &Icon..."), m_widget); 1055 m_changeIcon->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-icons"))); 1056 m_changeIcon->setEnabled(m_activeList->currentItem()); 1057 1058 QObject::connect(m_changeIcon, &QPushButton::clicked, m_widget, [this]() { 1059 slotChangeIcon(); 1060 }); 1061 1062 // "change icon text" button 1063 m_changeIconText = new QPushButton(i18nc("@action:button", "Change Te&xt..."), m_widget); 1064 m_changeIconText->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); 1065 m_changeIconText->setEnabled(m_activeList->currentItem() != nullptr); 1066 1067 QObject::connect(m_changeIconText, &QPushButton::clicked, m_widget, [this]() { 1068 slotChangeIconText(); 1069 }); 1070 1071 // The buttons in the middle 1072 1073 m_upAction = new QToolButton(m_widget); 1074 m_upAction->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); 1075 m_upAction->setEnabled(false); 1076 m_upAction->setAutoRepeat(true); 1077 QObject::connect(m_upAction, &QToolButton::clicked, m_widget, [this]() { 1078 slotUpButton(); 1079 }); 1080 1081 m_insertAction = new QToolButton(m_widget); 1082 m_insertAction->setIcon(QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("go-previous") : QStringLiteral("go-next"))); 1083 m_insertAction->setEnabled(false); 1084 QObject::connect(m_insertAction, &QToolButton::clicked, m_widget, [this]() { 1085 slotInsertButton(); 1086 }); 1087 1088 m_removeAction = new QToolButton(m_widget); 1089 m_removeAction->setIcon(QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("go-next") : QStringLiteral("go-previous"))); 1090 m_removeAction->setEnabled(false); 1091 QObject::connect(m_removeAction, &QToolButton::clicked, m_widget, [this]() { 1092 slotRemoveButton(); 1093 }); 1094 1095 m_downAction = new QToolButton(m_widget); 1096 m_downAction->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); 1097 m_downAction->setEnabled(false); 1098 m_downAction->setAutoRepeat(true); 1099 QObject::connect(m_downAction, &QToolButton::clicked, m_widget, [this]() { 1100 slotDownButton(); 1101 }); 1102 1103 m_helpArea = new QLabel(m_widget); 1104 m_helpArea->setWordWrap(true); 1105 1106 // now start with our layouts 1107 QVBoxLayout *top_layout = new QVBoxLayout(m_widget); 1108 top_layout->setContentsMargins(0, 0, 0, 0); 1109 1110 QVBoxLayout *name_layout = new QVBoxLayout(); 1111 QHBoxLayout *list_layout = new QHBoxLayout(); 1112 1113 QVBoxLayout *inactive_layout = new QVBoxLayout(); 1114 QVBoxLayout *active_layout = new QVBoxLayout(); 1115 QHBoxLayout *changeIcon_layout = new QHBoxLayout(); 1116 1117 QGridLayout *button_layout = new QGridLayout(); 1118 1119 name_layout->addWidget(m_comboLabel); 1120 name_layout->addWidget(m_toolbarCombo); 1121 // name_layout->addWidget(new_toolbar); 1122 // name_layout->addWidget(del_toolbar); 1123 1124 button_layout->setSpacing(0); 1125 button_layout->setRowStretch(0, 10); 1126 button_layout->addWidget(m_upAction, 1, 1); 1127 button_layout->addWidget(m_removeAction, 2, 0); 1128 button_layout->addWidget(m_insertAction, 2, 2); 1129 button_layout->addWidget(m_downAction, 3, 1); 1130 button_layout->setRowStretch(4, 10); 1131 1132 inactive_layout->addWidget(inactive_label); 1133 inactive_layout->addWidget(inactiveListSearchLine); 1134 inactive_layout->addWidget(m_inactiveList, 1); 1135 1136 active_layout->addWidget(active_label); 1137 active_layout->addWidget(activeListSearchLine); 1138 active_layout->addWidget(m_activeList, 1); 1139 active_layout->addLayout(changeIcon_layout); 1140 1141 changeIcon_layout->addWidget(m_changeIcon); 1142 changeIcon_layout->addStretch(1); 1143 changeIcon_layout->addWidget(m_changeIconText); 1144 1145 list_layout->addLayout(inactive_layout); 1146 list_layout->addLayout(button_layout); 1147 list_layout->addLayout(active_layout); 1148 1149 top_layout->addLayout(name_layout); 1150 top_layout->addWidget(m_comboSeparator); 1151 top_layout->addLayout(list_layout, 10); 1152 top_layout->addWidget(m_helpArea); 1153 top_layout->addWidget(new KSeparator(m_widget)); 1154 } 1155 1156 void KEditToolBarWidgetPrivate::loadToolBarCombo(const QString &defaultToolBar) 1157 { 1158 const QLatin1String attrName("name"); 1159 // just in case, we clear our combo 1160 m_toolbarCombo->clear(); 1161 1162 int defaultToolBarId = -1; 1163 int count = 0; 1164 // load in all of the toolbar names into this combo box 1165 for (const auto &xmlFile : std::as_const(m_xmlFiles)) { 1166 // skip the merged one in favor of the local one, 1167 // so that we can change icons 1168 // This also makes the app-defined named for "mainToolBar" appear rather than the ui_standards-defined name. 1169 if (xmlFile.type() == XmlData::Merged) { 1170 continue; 1171 } 1172 1173 // each xml file may have any number of toolbars 1174 for (const auto &bar : std::as_const(xmlFile.barList())) { 1175 const QString text = xmlFile.toolBarText(bar); 1176 m_toolbarCombo->addItem(text); 1177 const QString name = bar.attribute(attrName); 1178 if (defaultToolBarId == -1 && name == defaultToolBar) { 1179 defaultToolBarId = count; 1180 } 1181 count++; 1182 } 1183 } 1184 const bool showCombo = (count > 1); 1185 m_comboLabel->setVisible(showCombo); 1186 m_comboSeparator->setVisible(showCombo); 1187 m_toolbarCombo->setVisible(showCombo); 1188 if (defaultToolBarId == -1) { 1189 defaultToolBarId = 0; 1190 } 1191 // we want to the specified item selected and its actions loaded 1192 m_toolbarCombo->setCurrentIndex(defaultToolBarId); 1193 slotToolBarSelected(m_toolbarCombo->currentIndex()); 1194 } 1195 1196 void KEditToolBarWidgetPrivate::loadActions(const QDomElement &elem) 1197 { 1198 const QLatin1String tagSeparator("Separator"); 1199 const QLatin1String tagSpacer("Spacer"); 1200 const QLatin1String tagMerge("Merge"); 1201 const QLatin1String tagActionList("ActionList"); 1202 const QLatin1String tagAction("Action"); 1203 const QLatin1String attrName("name"); 1204 1205 const QString separatorstring = i18n("--- separator ---"); 1206 const QString spacerstring = i18n("--- expanding spacer ---"); 1207 1208 int sep_num = 0; 1209 QString sep_name(QStringLiteral("separator_%1")); 1210 int spacer_num = 0; 1211 QString spacer_name(QStringLiteral("spacer_%1")); 1212 1213 // clear our lists 1214 m_inactiveList->clear(); 1215 m_activeList->clear(); 1216 m_insertAction->setEnabled(false); 1217 m_removeAction->setEnabled(false); 1218 m_upAction->setEnabled(false); 1219 m_downAction->setEnabled(false); 1220 1221 // We'll use this action collection 1222 KActionCollection *actionCollection = m_currentXmlData->actionCollection(); 1223 1224 // store the names of our active actions 1225 QSet<QString> active_list; 1226 1227 // Filtering message requested by translators (scripting). 1228 KLocalizedString nameFilter = ki18nc("@item:intable Action name in toolbar editor", "%1"); 1229 1230 // see if our current action is in this toolbar 1231 QDomNode n = elem.firstChild(); 1232 for (; !n.isNull(); n = n.nextSibling()) { 1233 QDomElement it = n.toElement(); 1234 if (it.isNull()) { 1235 continue; 1236 } 1237 if (it.tagName() == tagSeparator) { 1238 ToolBarItem *act = new ToolBarItem(m_activeList, tagSeparator, sep_name.arg(sep_num++), QString()); 1239 act->setSeparator(true); 1240 act->setText(separatorstring); 1241 it.setAttribute(attrName, act->internalName()); 1242 continue; 1243 } 1244 if (it.tagName() == tagSpacer) { 1245 ToolBarItem *act = new ToolBarItem(m_activeList, tagSpacer, spacer_name.arg(spacer_num++), QString()); 1246 act->setSpacer(true); 1247 act->setText(spacerstring); 1248 it.setAttribute(attrName, act->internalName()); 1249 continue; 1250 } 1251 1252 if (it.tagName() == tagMerge) { 1253 // Merge can be named or not - use the name if there is one 1254 QString name = it.attribute(attrName); 1255 ToolBarItem *act = 1256 new ToolBarItem(m_activeList, tagMerge, name, i18n("This element will be replaced with all the elements of an embedded component.")); 1257 if (name.isEmpty()) { 1258 act->setText(i18n("<Merge>")); 1259 } else { 1260 act->setText(i18n("<Merge %1>", name)); 1261 } 1262 continue; 1263 } 1264 1265 if (it.tagName() == tagActionList) { 1266 ToolBarItem *act = 1267 new ToolBarItem(m_activeList, 1268 tagActionList, 1269 it.attribute(attrName), 1270 i18n("This is a dynamic list of actions. You can move it, but if you remove it you will not be able to re-add it.")); 1271 act->setText(i18n("ActionList: %1", it.attribute(attrName))); 1272 continue; 1273 } 1274 1275 // iterate through this client's actions 1276 // This used to iterate through _all_ actions, but we don't support 1277 // putting any action into any client... 1278 const auto actions = actionCollection->actions(); 1279 for (QAction *action : actions) { 1280 // do we have a match? 1281 if (it.attribute(attrName) == action->objectName()) { 1282 // we have a match! 1283 ToolBarItem *act = new ToolBarItem(m_activeList, it.tagName(), action->objectName(), action->toolTip()); 1284 act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->iconText())).toString()); 1285 act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon); 1286 act->setTextAlongsideIconHidden(action->priority() < QAction::NormalPriority); 1287 1288 active_list.insert(action->objectName()); 1289 break; 1290 } 1291 } 1292 } 1293 1294 // go through the rest of the collection 1295 const auto actions = actionCollection->actions(); 1296 for (QAction *action : actions) { 1297 // skip our active ones 1298 if (active_list.contains(action->objectName())) { 1299 continue; 1300 } 1301 1302 ToolBarItem *act = new ToolBarItem(m_inactiveList, tagAction, action->objectName(), action->toolTip()); 1303 act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->text())).toString()); 1304 act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon); 1305 } 1306 1307 m_inactiveList->sortItems(Qt::AscendingOrder); 1308 1309 // finally, add default separators and spacers to the inactive list 1310 ToolBarItem *sep = new ToolBarItem(nullptr, tagSeparator, sep_name.arg(sep_num++), QString()); 1311 sep->setSeparator(true); 1312 sep->setText(separatorstring); 1313 m_inactiveList->insertItem(0, sep); 1314 1315 ToolBarItem *spacer = new ToolBarItem(nullptr, tagSpacer, spacer_name.arg(spacer_num++), QString()); 1316 spacer->setSpacer(true); 1317 spacer->setText(spacerstring); 1318 m_inactiveList->insertItem(1, spacer); 1319 } 1320 1321 KActionCollection *KEditToolBarWidget::actionCollection() const 1322 { 1323 return d->m_collection; 1324 } 1325 1326 void KEditToolBarWidgetPrivate::slotToolBarSelected(int index) 1327 { 1328 // We need to find the XmlData and toolbar element for this index 1329 // To do that, we do the same iteration as the one which filled in the combobox. 1330 1331 int toolbarNumber = 0; 1332 for (auto &xmlFile : m_xmlFiles) { 1333 // skip the merged one in favor of the local one, 1334 // so that we can change icons 1335 if (xmlFile.type() == XmlData::Merged) { 1336 continue; 1337 } 1338 1339 // each xml file may have any number of toolbars 1340 const auto &barList = xmlFile.barList(); 1341 for (const auto &bar : barList) { 1342 // is this our toolbar? 1343 if (toolbarNumber == index) { 1344 // save our current settings 1345 m_currentXmlData = &xmlFile; 1346 m_currentToolBarElem = bar; 1347 1348 // qCDebug(DEBUG_KXMLGUI) << "found toolbar" << m_currentXmlData->toolBarText(*it) << "m_currentXmlData set to"; 1349 m_currentXmlData->dump(); 1350 1351 // If this is a Merged xmldata, clicking the "change icon" button would assert... 1352 Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); 1353 1354 // load in our values 1355 loadActions(m_currentToolBarElem); 1356 1357 if (xmlFile.type() == XmlData::Part || xmlFile.type() == XmlData::Shell) { 1358 m_widget->setDOMDocument(xmlFile.domDocument()); 1359 } 1360 return; 1361 } 1362 ++toolbarNumber; 1363 } 1364 } 1365 } 1366 1367 void KEditToolBarWidgetPrivate::slotInactiveSelectionChanged() 1368 { 1369 if (!m_inactiveList->selectedItems().isEmpty()) { 1370 m_insertAction->setEnabled(true); 1371 QString statusText = static_cast<ToolBarItem *>(m_inactiveList->selectedItems().first())->statusText(); 1372 m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText)); 1373 } else { 1374 m_insertAction->setEnabled(false); 1375 m_helpArea->setText(QString()); 1376 } 1377 } 1378 1379 void KEditToolBarWidgetPrivate::slotActiveSelectionChanged() 1380 { 1381 ToolBarItem *toolitem = nullptr; 1382 if (!m_activeList->selectedItems().isEmpty()) { 1383 toolitem = static_cast<ToolBarItem *>(m_activeList->selectedItems().first()); 1384 } 1385 1386 m_removeAction->setEnabled(toolitem); 1387 1388 m_changeIcon->setEnabled(toolitem && toolitem->internalTag() == QLatin1String("Action")); 1389 1390 m_changeIconText->setEnabled(toolitem && toolitem->internalTag() == QLatin1String("Action")); 1391 1392 if (toolitem) { 1393 m_upAction->setEnabled(toolitem->index() != 0); 1394 m_downAction->setEnabled(toolitem->index() != toolitem->listWidget()->count() - 1); 1395 1396 QString statusText = toolitem->statusText(); 1397 m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText)); 1398 } else { 1399 m_upAction->setEnabled(false); 1400 m_downAction->setEnabled(false); 1401 m_helpArea->setText(QString()); 1402 } 1403 } 1404 1405 void KEditToolBarWidgetPrivate::slotInsertButton() 1406 { 1407 QString internalName = static_cast<ToolBarItem *>(m_inactiveList->currentItem())->internalName(); 1408 1409 insertActive(m_inactiveList->currentItem(), m_activeList->currentItem(), false); 1410 // we're modified, so let this change 1411 Q_EMIT m_widget->enableOk(true); 1412 1413 slotToolBarSelected(m_toolbarCombo->currentIndex()); 1414 1415 selectActiveItem(internalName); 1416 } 1417 1418 void KEditToolBarWidgetPrivate::selectActiveItem(const QString &internalName) 1419 { 1420 int activeItemCount = m_activeList->count(); 1421 for (int i = 0; i < activeItemCount; i++) { 1422 ToolBarItem *item = static_cast<ToolBarItem *>(m_activeList->item(i)); 1423 if (item->internalName() == internalName) { 1424 m_activeList->setCurrentItem(item); 1425 break; 1426 } 1427 } 1428 } 1429 1430 void KEditToolBarWidgetPrivate::slotRemoveButton() 1431 { 1432 removeActive(m_activeList->currentItem()); 1433 1434 slotToolBarSelected(m_toolbarCombo->currentIndex()); 1435 } 1436 1437 void KEditToolBarWidgetPrivate::insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend) 1438 { 1439 if (!item) { 1440 return; 1441 } 1442 1443 QDomElement new_item; 1444 // let's handle the separator and spacer specially 1445 if (item->isSeparator()) { 1446 new_item = m_widget->domDocument().createElement(QStringLiteral("Separator")); 1447 } else if (item->isSpacer()) { 1448 new_item = m_widget->domDocument().createElement(QStringLiteral("Spacer")); 1449 } else { 1450 new_item = m_widget->domDocument().createElement(QStringLiteral("Action")); 1451 } 1452 1453 new_item.setAttribute(QStringLiteral("name"), item->internalName()); 1454 1455 Q_ASSERT(!m_currentToolBarElem.isNull()); 1456 1457 if (before) { 1458 // we have the item in the active list which is before the new 1459 // item.. so let's try our best to add our new item right after it 1460 QDomElement elem = findElementForToolBarItem(before); 1461 Q_ASSERT(!elem.isNull()); 1462 m_currentToolBarElem.insertAfter(new_item, elem); 1463 } else { 1464 // simply put it at the beginning or the end of the list. 1465 if (prepend) { 1466 m_currentToolBarElem.insertBefore(new_item, m_currentToolBarElem.firstChild()); 1467 } else { 1468 m_currentToolBarElem.appendChild(new_item); 1469 } 1470 } 1471 1472 // and set this container as a noMerge 1473 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); 1474 1475 // update the local doc 1476 updateLocal(m_currentToolBarElem); 1477 } 1478 1479 void KEditToolBarWidgetPrivate::removeActive(ToolBarItem *item) 1480 { 1481 if (!item) { 1482 return; 1483 } 1484 1485 // we're modified, so let this change 1486 Q_EMIT m_widget->enableOk(true); 1487 1488 // now iterate through to find the child to nuke 1489 QDomElement elem = findElementForToolBarItem(item); 1490 if (!elem.isNull()) { 1491 // nuke myself! 1492 m_currentToolBarElem.removeChild(elem); 1493 1494 // and set this container as a noMerge 1495 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); 1496 1497 // update the local doc 1498 updateLocal(m_currentToolBarElem); 1499 } 1500 } 1501 1502 void KEditToolBarWidgetPrivate::slotUpButton() 1503 { 1504 ToolBarItem *item = m_activeList->currentItem(); 1505 1506 if (!item) { 1507 Q_ASSERT(false); 1508 return; 1509 } 1510 1511 int row = item->listWidget()->row(item) - 1; 1512 // make sure we're not the top item already 1513 if (row < 0) { 1514 Q_ASSERT(false); 1515 return; 1516 } 1517 1518 // we're modified, so let this change 1519 Q_EMIT m_widget->enableOk(true); 1520 1521 moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(row - 1))); 1522 } 1523 1524 void KEditToolBarWidgetPrivate::moveActive(ToolBarItem *item, ToolBarItem *before) 1525 { 1526 QDomElement e = findElementForToolBarItem(item); 1527 1528 if (e.isNull()) { 1529 return; 1530 } 1531 1532 // remove item 1533 m_activeList->takeItem(m_activeList->row(item)); 1534 1535 // put it where it's supposed to go 1536 m_activeList->insertItem(m_activeList->row(before) + 1, item); 1537 1538 // make it selected again 1539 m_activeList->setCurrentItem(item); 1540 1541 // and do the real move in the DOM 1542 if (!before) { 1543 m_currentToolBarElem.insertBefore(e, m_currentToolBarElem.firstChild()); 1544 } else { 1545 m_currentToolBarElem.insertAfter(e, findElementForToolBarItem(before)); 1546 } 1547 1548 // and set this container as a noMerge 1549 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); 1550 1551 // update the local doc 1552 updateLocal(m_currentToolBarElem); 1553 } 1554 1555 void KEditToolBarWidgetPrivate::slotDownButton() 1556 { 1557 ToolBarItem *item = m_activeList->currentItem(); 1558 1559 if (!item) { 1560 Q_ASSERT(false); 1561 return; 1562 } 1563 1564 // make sure we're not the bottom item already 1565 int newRow = item->listWidget()->row(item) + 1; 1566 if (newRow >= item->listWidget()->count()) { 1567 Q_ASSERT(false); 1568 return; 1569 } 1570 1571 // we're modified, so let this change 1572 Q_EMIT m_widget->enableOk(true); 1573 1574 moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(newRow))); 1575 } 1576 1577 void KEditToolBarWidgetPrivate::updateLocal(QDomElement &elem) 1578 { 1579 for (auto &xmlFile : m_xmlFiles) { 1580 if (xmlFile.type() == XmlData::Merged) { 1581 continue; 1582 } 1583 1584 if (xmlFile.type() == XmlData::Shell || xmlFile.type() == XmlData::Part) { 1585 if (m_currentXmlData->xmlFile() == xmlFile.xmlFile()) { 1586 xmlFile.m_isModified = true; 1587 return; 1588 } 1589 1590 continue; 1591 } 1592 1593 xmlFile.m_isModified = true; 1594 const QLatin1String attrName("name"); 1595 for (const auto &bar : std::as_const(xmlFile.barList())) { 1596 const QString name(bar.attribute(attrName)); 1597 const QString tag(bar.tagName()); 1598 if ((tag != elem.tagName()) || (name != elem.attribute(attrName))) { 1599 continue; 1600 } 1601 1602 QDomElement toolbar = xmlFile.domDocument().documentElement().toElement(); 1603 toolbar.replaceChild(elem, bar); 1604 return; 1605 } 1606 1607 // just append it 1608 QDomElement toolbar = xmlFile.domDocument().documentElement().toElement(); 1609 Q_ASSERT(!toolbar.isNull()); 1610 toolbar.appendChild(elem); 1611 } 1612 } 1613 1614 void KEditToolBarWidgetPrivate::slotChangeIcon() 1615 { 1616 m_currentXmlData->dump(); 1617 Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); 1618 1619 QString icon = KIconDialog::getIcon(KIconLoader::Toolbar, 1620 KIconLoader::Action, 1621 false, 1622 0, 1623 false, // all defaults 1624 m_widget, 1625 i18n("Change Icon")); 1626 1627 if (icon.isEmpty()) { 1628 return; 1629 } 1630 1631 ToolBarItem *item = m_activeList->currentItem(); 1632 // qCDebug(DEBUG_KXMLGUI) << item; 1633 if (item) { 1634 item->setIcon(QIcon::fromTheme(icon)); 1635 1636 m_currentXmlData->m_isModified = true; 1637 1638 // Get hold of ActionProperties tag 1639 QDomElement elem = KXMLGUIFactory::actionPropertiesElement(m_currentXmlData->domDocument()); 1640 // Find or create an element for this action 1641 QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, item->internalName(), true /*create*/); 1642 Q_ASSERT(!act_elem.isNull()); 1643 act_elem.setAttribute(QStringLiteral("icon"), icon); 1644 1645 // we're modified, so let this change 1646 Q_EMIT m_widget->enableOk(true); 1647 } 1648 } 1649 1650 void KEditToolBarWidgetPrivate::slotChangeIconText() 1651 { 1652 m_currentXmlData->dump(); 1653 ToolBarItem *item = m_activeList->currentItem(); 1654 1655 if (item) { 1656 QString iconText = item->text(); 1657 bool hidden = item->isTextAlongsideIconHidden(); 1658 1659 IconTextEditDialog dialog(m_widget); 1660 dialog.setIconText(iconText); 1661 dialog.setTextAlongsideIconHidden(hidden); 1662 1663 bool ok = dialog.exec() == QDialog::Accepted; 1664 iconText = dialog.iconText(); 1665 hidden = dialog.textAlongsideIconHidden(); 1666 1667 bool hiddenChanged = hidden != item->isTextAlongsideIconHidden(); 1668 bool iconTextChanged = iconText != item->text(); 1669 1670 if (!ok || (!hiddenChanged && !iconTextChanged)) { 1671 return; 1672 } 1673 1674 item->setText(iconText); 1675 item->setTextAlongsideIconHidden(hidden); 1676 1677 Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); 1678 1679 m_currentXmlData->m_isModified = true; 1680 1681 // Get hold of ActionProperties tag 1682 QDomElement elem = KXMLGUIFactory::actionPropertiesElement(m_currentXmlData->domDocument()); 1683 // Find or create an element for this action 1684 QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, item->internalName(), true /*create*/); 1685 Q_ASSERT(!act_elem.isNull()); 1686 if (iconTextChanged) { 1687 act_elem.setAttribute(QStringLiteral("iconText"), iconText); 1688 } 1689 if (hiddenChanged) { 1690 act_elem.setAttribute(QStringLiteral("priority"), hidden ? QAction::LowPriority : QAction::NormalPriority); 1691 } 1692 1693 // we're modified, so let this change 1694 Q_EMIT m_widget->enableOk(true); 1695 } 1696 } 1697 1698 void KEditToolBarWidgetPrivate::slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList) 1699 { 1700 // qCDebug(DEBUG_KXMLGUI) << "slotDropped list=" << (list==m_activeList?"activeList":"inactiveList") 1701 // << "index=" << index << "sourceIsActiveList=" << sourceIsActiveList; 1702 if (list == m_activeList) { 1703 ToolBarItem *after = index > 0 ? static_cast<ToolBarItem *>(list->item(index - 1)) : nullptr; 1704 // qCDebug(DEBUG_KXMLGUI) << "after" << after->text() << after->internalTag(); 1705 if (sourceIsActiveList) { 1706 // has been dragged within the active list (moved). 1707 moveActive(item, after); 1708 } else { 1709 // dragged from the inactive list to the active list 1710 insertActive(item, after, true); 1711 } 1712 } else if (list == m_inactiveList) { 1713 // has been dragged to the inactive list -> remove from the active list. 1714 removeActive(item); 1715 } 1716 1717 delete item; // not needed anymore. must be deleted before slotToolBarSelected clears the lists 1718 1719 // we're modified, so let this change 1720 Q_EMIT m_widget->enableOk(true); 1721 1722 slotToolBarSelected(m_toolbarCombo->currentIndex()); 1723 } 1724 1725 void KEditToolBar::showEvent(QShowEvent *event) 1726 { 1727 if (!event->spontaneous()) { 1728 // The dialog has been shown, enable toolbar editing 1729 if (d->m_factory) { 1730 // call the xmlgui-factory version 1731 d->m_widget->load(d->m_factory, d->m_defaultToolBar); 1732 } else { 1733 // call the action collection version 1734 d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar); 1735 } 1736 1737 KToolBar::setToolBarsEditable(true); 1738 } 1739 QDialog::showEvent(event); 1740 } 1741 1742 void KEditToolBar::hideEvent(QHideEvent *event) 1743 { 1744 // The dialog has been hidden, disable toolbar editing 1745 KToolBar::setToolBarsEditable(false); 1746 1747 QDialog::hideEvent(event); 1748 } 1749 1750 #include "moc_kedittoolbar.cpp" 1751 #include "moc_kedittoolbar_p.cpp"