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