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"