Krita Source Code Documentation
Loading...
Searching...
No Matches
kedittoolbar.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
3 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
4 SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-only
7*/
8
9#include "kedittoolbar.h"
10#include "kedittoolbar_p.h"
11
12#include "config-xmlgui.h"
13
14#include <QShowEvent>
15#include <QAction>
16#include <QDialogButtonBox>
17#include <QDomDocument>
18#include <QLayout>
19#include <QDir>
20#include <QFile>
21#include <QHeaderView>
22#include <QToolButton>
23#include <QLabel>
24#include <QApplication>
25#include <QGridLayout>
26#include <QCheckBox>
27#include <QMimeData>
28#include <QPushButton>
29#include <QStandardPaths>
30#include <QComboBox>
31#include <QLineEdit>
32#include <QDebug>
33
34#include <klistwidgetsearchline.h>
35#include <klocalizedstring.h>
36#include <kmessagebox.h>
37#include <kseparator.h>
38#include <kconfig.h>
39
40#include "kactioncollection.h"
41#include "kxmlguifactory.h"
42#include "ktoolbar.h"
43
44#include <kis_icon_utils.h>
45#include "kis_action_registry.h"
46#include <KisKineticScroller.h>
47#include <kis_assert.h>
48
49static const char separatorstring[] = QT_TRANSLATE_NOOP("KEditToolBar", "--- separator ---");
50
51#define SEPARATORSTRING i18n(separatorstring)
52
53//static const char *const s_XmlTypeToString[] = { "Shell", "Part", "Local", "Merged" };
54
56
57namespace KDEPrivate
58{
59
63static ToolBarList findToolBars(const QDomElement &start)
64{
65 ToolBarList list;
66
67 for (QDomElement elem = start; !elem.isNull(); elem = elem.nextSiblingElement()) {
68 if (elem.tagName() == QStringLiteral("ToolBar")) {
69 if (elem.attribute(QStringLiteral("noEdit")) != QLatin1String("true")) {
70 list.append(elem);
71 }
72 } else {
73 if (elem.tagName() != QStringLiteral("MenuBar")) { // there are no toolbars inside the menubar :)
74 list += findToolBars(elem.firstChildElement()); // recursive
75 }
76 }
77 }
78
79 return list;
80}
81
83{
84public:
85 enum XmlType { Shell = 0, Part, Local, Merged };
86
87 explicit XmlData(XmlType xmlType, const QString &xmlFile, KisKActionCollection *collection)
88 : m_isModified(false)
90 , m_type(xmlType)
91 , m_actionCollection(collection)
92 {
93 }
94
96 {
97 }
98
99 void dump() const
100 {
101#if 0
102 qDebug() << "XmlData" << this << "xmlFile:" << m_xmlFile;
103 foreach (const QDomElement &element, m_barList) {
104 qDebug() << " ToolBar:" << toolBarText(element);
105 }
106 //KisActionRegistry::instance()->
107 if (m_actionCollection) {
108 qDebug() << " " << m_actionCollection->actions().count() << "actions in the collection.";
109 } else {
110 qDebug() << " no action collection.";
111 }
112#endif
113 }
114
115 QString xmlFile() const
116 {
117 return m_xmlFile;
118 }
119
120 XmlType type() const
121 {
122 return m_type;
123 }
124
129
130 void setDomDocument(const QDomDocument &domDoc)
131 {
132 m_document = domDoc.cloneNode().toDocument();
133 m_barList = findToolBars(m_document.documentElement());
134 }
135
136 // Return reference, for e.g. actionPropertiesElement() to modify the document
137 QDomDocument &domDocument()
138 {
139 return m_document;
140 }
141
142 const QDomDocument &domDocument() const
143 {
144 return m_document;
145 }
146
150 QString toolBarText(const QDomElement &it) const;
151
153
155 {
156 return m_barList;
157 }
158
159 const ToolBarList &barList() const
160 {
161 return m_barList;
162 }
163
164private:
166 QString m_xmlFile;
167 QDomDocument m_document;
170};
171
172QString XmlData::toolBarText(const QDomElement &it) const
173{
174 const QLatin1String attrName("name");
175
176 QString name;
177 QByteArray txt(it.namedItem(QStringLiteral("text")).toElement().text().toUtf8());
178 if (txt.isEmpty()) {
179 txt = it.namedItem(QStringLiteral("text")).toElement().text().toUtf8();
180 }
181 if (txt.isEmpty()) {
182 name = it.attribute(attrName);
183 } else {
184 QByteArray domain = it.namedItem(QStringLiteral("text")).toElement().attribute(QStringLiteral("translationDomain")).toUtf8();
185 if (domain.isEmpty()) {
186 domain = it.ownerDocument().documentElement().attribute(QStringLiteral("translationDomain")).toUtf8();
187 if (domain.isEmpty()) {
188 domain = KLocalizedString::applicationDomain();
189 }
190 }
191 name = i18nd(domain.constData(), txt.constData());
192 }
193
194 // the name of the toolbar might depend on whether or not
195 // it is in kparts
196 if ((m_type == XmlData::Shell) ||
197 (m_type == XmlData::Part)) {
198 QString doc_name(m_document.documentElement().attribute(attrName));
199 name += QStringLiteral(" <") + doc_name + QLatin1Char('>');
200 }
201 return name;
202}
203
204class ToolBarItem : public QListWidgetItem
205{
206public:
207 ToolBarItem(QListWidget *parent, const QString &tag = QString(), const QString &name = QString(), const QString &statusText = QString())
208 : QListWidgetItem(parent),
209 m_internalTag(tag),
210 m_internalName(name),
212 m_isSeparator(false),
214 {
215 // Drop between items, not onto items
216 setFlags((flags() | Qt::ItemIsDragEnabled) & ~Qt::ItemIsDropEnabled);
217 }
218
219 void setInternalTag(const QString &tag)
220 {
221 m_internalTag = tag;
222 }
223 void setInternalName(const QString &name)
224 {
225 m_internalName = name;
226 }
227 void setStatusText(const QString &text)
228 {
229 m_statusText = text;
230 }
231 void setSeparator(bool sep)
232 {
233 m_isSeparator = sep;
234 }
236 {
238 }
239 QString internalTag() const
240 {
241 return m_internalTag;
242 }
243 QString internalName() const
244 {
245 return m_internalName;
246 }
247 QString statusText() const
248 {
249 return m_statusText;
250 }
251 bool isSeparator() const
252 {
253 return m_isSeparator;
254 }
256 {
258 }
259
260 int index() const
261 {
262 return listWidget()->row(const_cast<ToolBarItem *>(this));
263 }
264
265private:
271};
272
273static QDataStream &operator<< (QDataStream &s, const ToolBarItem &item)
274{
275 s << item.internalTag();
276 s << item.internalName();
277 s << item.statusText();
278 s << item.isSeparator();
279 s << item.isTextAlongsideIconHidden();
280 return s;
281}
282static QDataStream &operator>> (QDataStream &s, ToolBarItem &item)
283{
284 QString internalTag;
285 s >> internalTag;
286 item.setInternalTag(internalTag);
287 QString internalName;
288 s >> internalName;
289 item.setInternalName(internalName);
290 QString statusText;
291 s >> statusText;
292 item.setStatusText(statusText);
293 bool sep;
294 s >> sep;
295 item.setSeparator(sep);
296 bool hidden;
297 s >> hidden;
298 item.setTextAlongsideIconHidden(hidden);
299 return s;
300}
301
303
305 : QListWidget(parent),
306 m_activeList(true)
307{
308 setDragDropMode(QAbstractItemView::DragDrop); // no internal moves
309}
310
311#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
312QMimeData *ToolBarListWidget::mimeData(const QList<QListWidgetItem *> items) const
313#else
315#endif
316{
317 if (items.isEmpty()) {
318 return 0;
319 }
320 QMimeData *mimedata = new QMimeData();
321
322 QByteArray data;
323 {
324 QDataStream stream(&data, QIODevice::WriteOnly);
325 // we only support single selection
326 ToolBarItem *item = static_cast<ToolBarItem *>(items.first());
327 stream << *item;
328 }
329
330 mimedata->setData(QStringLiteral("application/x-kde-action-list"), data);
331 mimedata->setData(QStringLiteral("application/x-kde-source-treewidget"), m_activeList ? "active" : "inactive");
332
333 return mimedata;
334}
335
336bool ToolBarListWidget::dropMimeData(int index, const QMimeData *mimeData, Qt::DropAction action)
337{
338 Q_UNUSED(action);
339 const QByteArray data = mimeData->data(QStringLiteral("application/x-kde-action-list"));
340 if (data.isEmpty()) {
341 return false;
342 }
343 QDataStream stream(data);
344 const bool sourceIsActiveList = mimeData->data(QStringLiteral("application/x-kde-source-treewidget")) == "active";
345 ToolBarItem *item = new ToolBarItem(this); // needs parent, use this temporarily
346 stream >> *item;
347 Q_EMIT dropped(this, index, item, sourceIsActiveList);
348 return true;
349}
350
352{
353 return static_cast<ToolBarItem *>(QListWidget::currentItem());
354}
355
357 : QDialog(parent)
358{
359 setWindowTitle(i18n("Change Text"));
360 setModal(true);
361
362 QVBoxLayout *layout = new QVBoxLayout(this);
363
364 QGridLayout *grid = new QGridLayout;
365 grid->setContentsMargins(0, 0, 0, 0);
366
367 m_lineEdit = new QLineEdit(this);
368 m_lineEdit->setClearButtonEnabled(true);
369 QLabel *label = new QLabel(i18n("Icon te&xt:"), this);
370 label->setBuddy(m_lineEdit);
371 grid->addWidget(label, 0, 0);
372 grid->addWidget(m_lineEdit, 0, 1);
373
374 m_cbHidden = new QCheckBox(i18n("&Hide text when toolbar shows text alongside icons"), this);
375 grid->addWidget(m_cbHidden, 1, 1);
376
377 layout->addLayout(grid);
378
379 m_buttonBox = new QDialogButtonBox(this);
380 m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
381 connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
382 connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
383 layout->addWidget(m_buttonBox);
384
385 connect(m_lineEdit, SIGNAL(textChanged(QString)), SLOT(slotTextChanged(QString)));
386
387 m_lineEdit->setFocus();
388 setFixedHeight(sizeHint().height());
389}
390
391void IconTextEditDialog::setIconText(const QString &text)
392{
393 m_lineEdit->setText(text);
394}
395
397{
398 return m_lineEdit->text().trimmed();
399}
400
402{
403 m_cbHidden->setChecked(hidden);
404}
405
407{
408 return m_cbHidden->isChecked();
409}
410
411void IconTextEditDialog::slotTextChanged(const QString &text)
412{
413 // Do not allow empty icon text
414 m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.trimmed().isEmpty());
415}
416
418{
419public:
428 const QString &cName, KisKActionCollection *collection)
429 : m_collection(collection),
430 m_widget(widget),
431 m_factory(0),
432 m_loadedOnce(false)
433 {
434 m_componentName = cName;
435 m_helpArea = 0L;
436 // We want items with an icon to align with items without icon
437 // So we use an empty QPixmap for that
438 const int iconSize = widget->style()->pixelMetric(QStyle::PM_SmallIconSize);
439 m_emptyIcon = QPixmap(iconSize, iconSize);
440 m_emptyIcon.fill(Qt::transparent);
441 }
445
446 // private slots
447 void slotToolBarSelected(int index);
448
451
452 void slotInsertButton();
453 void slotRemoveButton();
454 void slotUpButton();
455 void slotDownButton();
456
458
459 void selectActiveItem(const QString &);
460
461 void slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList);
462
463 void setupLayout();
464
465 void initOldStyle(const QString &file, bool global, const QString &defaultToolbar);
466 void initFromFactory(KisKXMLGUIFactory *factory, const QString &defaultToolbar);
467 void loadToolBarCombo(const QString &defaultToolbar);
468 void loadActions(const QDomElement &elem);
469
470 QString xmlFile(const QString &xml_file) const
471 {
472 return xml_file.isEmpty() ? m_componentName + QStringLiteral("ui.xmlgui") : xml_file;
473 }
474
478 QString loadXMLFile(const QString &_xml_file)
479 {
480 QString raw_xml;
481 QString xml_file = xmlFile(_xml_file);
482 //qDebug() << "loadXMLFile xml_file=" << xml_file;
483
484 if (!QDir::isRelativePath(xml_file)) {
485 raw_xml = KisKXMLGUIFactory::readConfigFile(xml_file);
486 } else {
488 }
489
490 return raw_xml;
491 }
492
496 QDomElement findElementForToolBarItem(const ToolBarItem *item) const
497 {
498 //qDebug(240) << "looking for name=" << item->internalName() << "and tag=" << item->internalTag();
499 for (QDomNode n = m_currentToolBarElem.firstChild(); !n.isNull(); n = n.nextSibling()) {
500 QDomElement elem = n.toElement();
501 if ((elem.attribute(QStringLiteral("name")) == item->internalName()) &&
502 (elem.tagName() == item->internalTag())) {
503 return elem;
504 }
505 }
506 //qDebug(240) << "no item found in the DOM with name=" << item->internalName() << "and tag=" << item->internalTag();
507 return QDomElement();
508 }
509
510 void insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend = false);
511 void removeActive(ToolBarItem *item);
512 void moveActive(ToolBarItem *item, ToolBarItem *before);
513 void updateLocal(QDomElement &elem);
514
515#ifndef NDEBUG
516 void dump() const
517 {
519 for (; xit != m_xmlFiles.end(); ++xit) {
520 (*xit).dump();
521 }
522 }
523#endif
524
525 QComboBox *m_toolbarCombo {nullptr};
526
527 QToolButton *m_upAction {nullptr};
528 QToolButton *m_removeAction {nullptr};
529 QToolButton *m_insertAction {nullptr};
530 QToolButton *m_downAction {nullptr};
531
532 QToolButton *m_changeIconAction {nullptr};
533
534 //QValueList<QAction*> m_actionList;
539
540 QPixmap m_emptyIcon;
541
544
545 QString m_xmlFile;
547 QString m_rcFile;
548 QDomDocument m_localDoc;
549
553
555
556 QLabel *m_comboLabel {nullptr};
557 KSeparator *m_comboSeparator {nullptr};
558 QLabel *m_helpArea {nullptr};
559 bool m_loadedOnce {false};
560};
561
562}
563
564using namespace KDEPrivate;
565
567{
568public:
571
572 void init();
573
574 void _k_slotButtonClicked(QAbstractButton *button);
575 void _k_acceptOK(bool);
576 void _k_enableApply(bool);
577 void okClicked();
578 void applyClicked();
579 void defaultClicked();
580
581 KisKEditToolBar *q {nullptr};
582 bool m_accept {false};
583 // Save parameters for recreating widget after resetting toolbar
584 bool m_global {false};
586 QString m_file;
590 QVBoxLayout *m_layout {nullptr};
591 QDialogButtonBox *m_buttonBox {nullptr};
592
593
594};
595
596Q_GLOBAL_STATIC(QString, s_defaultToolBarName)
597
599 QWidget *parent)
600 : QDialog(parent),
601 d(new KisKEditToolBarPrivate(this))
602{
603 d->m_widget = new KisKEditToolBarWidget(this);
604 d->init();
605 d->m_factory = factory;
606}
607
609{
610 m_accept = false;
611 m_factory = 0;
612
613 q->setDefaultToolBar(QString());
614
615 q->setWindowTitle(i18n("Configure Toolbars"));
616 q->setModal(false);
617
618 m_layout = new QVBoxLayout(q);
619
620 m_layout->addWidget(m_widget);
621
622 m_buttonBox = new QDialogButtonBox(q);
623 m_buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults
624 | QDialogButtonBox::Ok
625 | QDialogButtonBox::Apply
626 | QDialogButtonBox::Cancel);
627 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
628 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply());
629 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
630 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults());
631 q->connect(m_buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(_k_slotButtonClicked(QAbstractButton*)));
632 q->connect(m_buttonBox, SIGNAL(rejected()), SLOT(reject()));
633 m_layout->addWidget(m_buttonBox);
634
635 q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool)));
636 q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool)));
637 _k_enableApply(false);
638
639 q->setMinimumSize(q->sizeHint());
640}
641
642void KisKEditToolBar::setResourceFile(const QString &file, bool global)
643{
644 d->m_file = file;
645 d->m_global = global;
647}
648
650{
651 delete d;
652 s_defaultToolBarName()->clear();
653}
654
655void KisKEditToolBar::setDefaultToolBar(const QString &toolBarName)
656{
657 if (toolBarName.isEmpty()) {
658 d->m_defaultToolBar = *s_defaultToolBarName();
659 } else {
660 d->m_defaultToolBar = toolBarName;
661 }
662}
663
665{
666 m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(b);
667 m_accept = b;
668}
669
671{
672 m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(b);
673}
674
676{
677 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) {
678 return;
679 }
680
681 KisKEditToolBarWidget *oldWidget = m_widget;
682 m_widget = 0;
683 m_accept = false;
684
685 if (m_factory) {
686 foreach (KisKXMLGUIClient *client, m_factory->clients()) {
687 const QString file = client->localXMLFile();
688 if (file.isEmpty()) {
689 continue;
690 }
691 //qDebug(240) << "Deleting local xml file" << file;
692 // << "for client" << client << typeid(*client).name();
693 if (QFile::exists(file))
694 if (!QFile::remove(file)) {
695 qWarning() << "Could not delete" << file;
696 }
697 }
698
699 // Reload the xml files in all clients, now that the local files are gone
700 oldWidget->rebuildKisKXMLGUIClients();
701
704 } else {
705 int slash = m_file.lastIndexOf(QLatin1Char('/')) + 1;
706 if (slash) {
707 m_file = m_file.mid(slash);
708 }
709 const QString xml_file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
710 QStringLiteral("/kxmlgui5/") + QCoreApplication::instance()->applicationName() + QLatin1Char('/') + m_file;
711
712 if (QFile::exists(xml_file))
713 if (!QFile::remove(xml_file)) {
714 qWarning() << "Could not delete " << xml_file;
715 }
716
719 }
720
721 // Copy the geometry to minimize UI flicker
722 m_widget->setGeometry(oldWidget->geometry());
723 delete oldWidget;
724 m_layout->insertWidget(0, m_widget);
725
726 q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool)));
727 q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool)));
728
729 _k_enableApply(false);
730
731 Q_EMIT q->newToolBarConfig();
732 Q_EMIT q->newToolbarConfig(); // compat
733}
734
736{
737 QDialogButtonBox::StandardButton type = m_buttonBox->standardButton(button);
738
739 switch (type) {
740 case QDialogButtonBox::Ok:
741 okClicked();
742 break;
743 case QDialogButtonBox::Apply:
744 applyClicked();
745 break;
746 case QDialogButtonBox::RestoreDefaults:
748 break;
749 default:
750 break;
751 }
752}
753
755{
756 if (!m_accept) {
757 q->reject();
758 return;
759 }
760
761 // Do not rebuild GUI and Q_EMIT the "newToolBarConfig" signal again here if the "Apply"
762 // button was already pressed and no further changes were made.
763 if (m_buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) {
764 m_widget->save();
765 Q_EMIT q->newToolBarConfig();
766 Q_EMIT q->newToolbarConfig(); // compat
767 }
768 q->accept();
769}
770
772{
773 (void)m_widget->save();
774 _k_enableApply(false);
775 Q_EMIT q->newToolBarConfig();
776 Q_EMIT q->newToolbarConfig(); // compat
777}
778
779void KisKEditToolBar::setGlobalDefaultToolBar(const char *toolbarName)
780{
781 *s_defaultToolBarName() = QString::fromLatin1(toolbarName);
782}
783
785 QWidget *parent)
786 : QWidget(parent),
787 d(new KisKEditToolBarWidgetPrivate(this, componentName(), collection))
788{
789 d->setupLayout();
790}
791
793 : QWidget(parent),
794 d(new KisKEditToolBarWidgetPrivate(this, componentName(), KisKXMLGUIClient::actionCollection() /*create new one*/))
795{
796 d->setupLayout();
797}
798
803
804void KisKEditToolBarWidget::load(const QString &file, bool global, const QString &defaultToolBar)
805{
806 d->initOldStyle(file, global, defaultToolBar);
807}
808
809void KisKEditToolBarWidget::load(KisKXMLGUIFactory *factory, const QString &defaultToolBar)
810{
811 d->initFromFactory(factory, defaultToolBar);
812}
813
814void KisKEditToolBarWidgetPrivate::initOldStyle(const QString &resourceFile,
815 bool global,
816 const QString &defaultToolBar)
817{
818 qDebug() << "initOldStyle";
819 //TODO: make sure we can call this multiple times?
820 if (m_loadedOnce) {
821 return;
822 }
823
824 m_loadedOnce = true;
825 //d->m_actionList = collection->actions();
826
827 // handle the merging
828 if (global) {
829 m_widget->loadStandardsXmlFile(); // ui_standards.xmlgui
830 }
831 const QString localXML = loadXMLFile(resourceFile);
832 m_widget->setXML(localXML, global ? true /*merge*/ : false);
833
834 // first, get all of the necessary info for our local xml
835 XmlData local(XmlData::Local, xmlFile(resourceFile), m_collection);
836 QDomDocument domDoc;
837 domDoc.setContent(localXML);
838 local.setDomDocument(domDoc);
839 m_xmlFiles.append(local);
840
841 // then, the merged one (ui_standards + local xml)
842 XmlData merge(XmlData::Merged, QString(), m_collection);
844 m_xmlFiles.append(merge);
845
846#ifndef NDEBUG
847 dump();
848#endif
849
850 // now load in our toolbar combo box
851 loadToolBarCombo(defaultToolBar);
852 m_widget->adjustSize();
853 m_widget->setMinimumSize(m_widget->sizeHint());
854}
855
857 const QString &defaultToolBar)
858{
859 qDebug() << "initFromFactory";
860 //TODO: make sure we can call this multiple times?
861 if (m_loadedOnce) {
862 return;
863 }
864
865 m_loadedOnce = true;
866
867 m_factory = factory;
868
869 // add all of the client data
870 bool first = true;
871 foreach (KisKXMLGUIClient *client, factory->clients()) {
872 if (client->xmlFile().isEmpty()) {
873 continue;
874 }
875
877 if (first) {
878 type = XmlData::Shell;
879 first = false;
880 Q_ASSERT(!client->localXMLFile().isEmpty()); // where would we save changes??
881 }
882
883 XmlData data(type, client->localXMLFile(), client->actionCollection());
884 QDomDocument domDoc = client->domDocument();
885 data.setDomDocument(domDoc);
886 m_xmlFiles.append(data);
887
888 //d->m_actionList += client->actionCollection()->actions();
889 }
890
891#ifndef NDEBUG
892 //d->dump();
893#endif
894
895 // now load in our toolbar combo box
896 loadToolBarCombo(defaultToolBar);
897 m_widget->adjustSize();
898 m_widget->setMinimumSize(m_widget->sizeHint());
899
901 foreach (QAction *action, m_widget->actionCollection()->actions()) {
902 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
903 }
904}
905
907{
908 //qDebug(240) << "KisKEditToolBarWidget::save";
910 for (; it != d->m_xmlFiles.end(); ++it) {
911 // let's not save non-modified files
912 if (!((*it).m_isModified)) {
913 continue;
914 }
915
916 // let's also skip (nonexistent) merged files
917 if ((*it).type() == XmlData::Merged) {
918 continue;
919 }
920
921 // Add noMerge="1" to all the menus since we are saving the merged data
922 QDomNodeList menuNodes = (*it).domDocument().elementsByTagName(QStringLiteral("Menu"));
923 for (int i = 0; i < menuNodes.length(); ++i) {
924 QDomNode menuNode = menuNodes.item(i);
925 QDomElement menuElement = menuNode.toElement();
926 if (menuElement.isNull()) {
927 continue;
928 }
929 menuElement.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
930 }
931
932 //qDebug() << (*it).domDocument().toString();
933
934 //qDebug(240) << "Saving " << (*it).xmlFile();
935 // if we got this far, we might as well just save it
936 KisKXMLGUIFactory::saveConfigFile((*it).domDocument(), (*it).xmlFile());
937 }
938
939 if (!d->m_factory) {
940 return;
941 }
942
944}
945
947{
948 if (!d->m_factory) {
949 return;
950 }
951
952 const QList<KisKXMLGUIClient *> clients = d->m_factory->clients();
953 //qDebug(240) << "factory: " << clients.count() << " clients";
954
955 // remove the elements starting from the last going to the first
956 if (!clients.count()) {
957 return;
958 }
959
960 QListIterator<KisKXMLGUIClient *> clientIterator = clients;
961 clientIterator.toBack();
962 while (clientIterator.hasPrevious()) {
963 KisKXMLGUIClient *client = clientIterator.previous();
964 //qDebug(240) << "factory->removeClient " << client;
965 d->m_factory->removeClient(client);
966 }
967
968 KisKXMLGUIClient *firstClient = clients.first();
969
970 // now, rebuild the gui from the first to the last
971 //qDebug(240) << "rebuilding the gui";
972 foreach (KisKXMLGUIClient *client, clients) {
973 //qDebug(240) << "updating client " << client << " " << client->componentName() << " xmlFile=" << client->xmlFile();
974 QString file(client->xmlFile()); // before setting ui_standards!
975 if (!file.isEmpty()) {
976 // passing an empty stream forces the clients to reread the XML
977 client->setXMLGUIBuildDocument(QDomDocument());
978
979 // for the shell, merge in ui_standards.xmlgui
980 if (client == firstClient) { // same assumption as in the ctor: first==shell
981 client->loadStandardsXmlFile();
982 }
983
984 // and this forces it to use the *new* XML file
985 client->setXMLFile(file, client == firstClient /* merge if shell */);
986
987 // [we can't use reloadXML, it doesn't load ui_standards.xmlgui]
988 }
989 }
990
991 // Now we can add the clients to the factory
992 // We don't do it in the loop above because adding a part automatically
993 // adds its plugins, so we must make sure the plugins were updated first.
994 foreach (KisKXMLGUIClient *client, clients) {
995 d->m_factory->addClient(client);
996 }
997}
998
1000{
1001 // the toolbar name combo
1002 m_comboLabel = new QLabel(i18n("&Toolbar:"), m_widget);
1003 m_toolbarCombo = new QComboBox(m_widget);
1004 m_comboLabel->setBuddy(m_toolbarCombo);
1005 m_comboSeparator = new KSeparator(m_widget);
1006 QObject::connect(m_toolbarCombo, SIGNAL(activated(int)),
1007 m_widget, SLOT(slotToolBarSelected(int)));
1008
1009// QPushButton *new_toolbar = new QPushButton(i18n("&New"), this);
1010// new_toolbar->setPixmap(BarIcon("document-new", KisIconUtils::SizeSmall));
1011// new_toolbar->setEnabled(false); // disabled until implemented
1012// QPushButton *del_toolbar = new QPushButton(i18n("&Delete"), this);
1013// del_toolbar->setPixmap(BarIcon("edit-delete", KisIconUtils::SizeSmall));
1014// del_toolbar->setEnabled(false); // disabled until implemented
1015
1016 // our list of inactive actions
1017 QLabel *inactive_label = new QLabel(i18n("A&vailable actions:"), m_widget);
1019 m_inactiveList->setDragEnabled(true);
1021 m_inactiveList->setMinimumSize(180, 250);
1022 m_inactiveList->setDropIndicatorShown(false); // #165663
1023 inactive_label->setBuddy(m_inactiveList);
1024 QObject::connect(m_inactiveList, SIGNAL(itemSelectionChanged()),
1026 QObject::connect(m_inactiveList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
1027 m_widget, SLOT(slotInsertButton()));
1028 QObject::connect(m_inactiveList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)),
1030
1031 {
1033 if (scroller) {
1034 QObject::connect(scroller, &QScroller::stateChanged, m_widget, [&](QScroller::State state) {
1036 });
1037 }
1038 }
1039
1040 KListWidgetSearchLine *inactiveListSearchLine = new KListWidgetSearchLine(m_widget, m_inactiveList);
1041 inactiveListSearchLine->setPlaceholderText(i18nc("Filter as in showing only matching items", "Filter"));
1042
1043 // our list of active actions
1044 QLabel *active_label = new QLabel(i18n("Curr&ent actions:"), m_widget);
1046 m_activeList->setDragEnabled(true);
1048 // With Qt-4.1 only setting MiniumWidth results in a 0-width icon column ...
1049 m_activeList->setMinimumSize(m_inactiveList->minimumWidth(), 100);
1050 active_label->setBuddy(m_activeList);
1051
1052 QObject::connect(m_activeList, SIGNAL(itemSelectionChanged()),
1054 QObject::connect(m_activeList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
1055 m_widget, SLOT(slotRemoveButton()));
1056 QObject::connect(m_activeList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)),
1058 {
1060 if (scroller) {
1061 QObject::connect(scroller, &QScroller::stateChanged, m_widget, [&](QScroller::State state) {
1063 });
1064 }
1065 }
1066
1067 // Edit Icon Button under active actions
1068 m_changeIconAction = new QToolButton(m_widget);
1069 m_changeIconAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("preferences-desktop-icons")));
1070 m_changeIconAction->setText(i18n("Change Icon..."));
1071 m_changeIconAction->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
1072 m_changeIconAction->setEnabled(false);
1073
1074 QObject::connect(m_changeIconAction, SIGNAL(clicked()), m_widget, SLOT(slotChangeIconButton()));
1075
1076 KListWidgetSearchLine *activeListSearchLine = new KListWidgetSearchLine(m_widget, m_activeList);
1077 activeListSearchLine->setPlaceholderText(i18n("Filter"));
1078
1079 // The buttons in the middle
1080
1081 m_upAction = new QToolButton(m_widget);
1082 m_upAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("arrow-up")));
1083 m_upAction->setEnabled(false);
1084 m_upAction->setAutoRepeat(true);
1085 QObject::connect(m_upAction, SIGNAL(clicked()), m_widget, SLOT(slotUpButton()));
1086
1087 m_insertAction = new QToolButton(m_widget);
1088 m_insertAction->setIcon(KisIconUtils::loadIcon(QApplication::isRightToLeft() ? QStringLiteral("arrow-left") : QLatin1String("arrow-right")));
1089 m_insertAction->setEnabled(false);
1090 QObject::connect(m_insertAction, SIGNAL(clicked()), m_widget, SLOT(slotInsertButton()));
1091
1092 m_removeAction = new QToolButton(m_widget);
1093 m_removeAction->setIcon(KisIconUtils::loadIcon(QApplication::isRightToLeft() ? QStringLiteral("arrow-right") : QLatin1String("arrow-left")));
1094 m_removeAction->setEnabled(false);
1095 QObject::connect(m_removeAction, SIGNAL(clicked()), m_widget, SLOT(slotRemoveButton()));
1096
1097 m_downAction = new QToolButton(m_widget);
1098 m_downAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("arrow-down")));
1099 m_downAction->setEnabled(false);
1100 m_downAction->setAutoRepeat(true);
1101 QObject::connect(m_downAction, SIGNAL(clicked()), m_widget, SLOT(slotDownButton()));
1102
1103 m_helpArea = new QLabel(m_widget);
1104 m_helpArea->setWordWrap(true);
1105
1106 // now start with our layouts
1107 QVBoxLayout *top_layout = new QVBoxLayout(m_widget);
1108 top_layout->setContentsMargins(0, 0, 0, 0);
1109
1110 QVBoxLayout *name_layout = new QVBoxLayout();
1111 QHBoxLayout *list_layout = new QHBoxLayout();
1112
1113 QVBoxLayout *inactive_layout = new QVBoxLayout();
1114 QVBoxLayout *active_layout = new QVBoxLayout();
1115
1116 QGridLayout *button_layout = new QGridLayout();
1117
1118 name_layout->addWidget(m_comboLabel);
1119 name_layout->addWidget(m_toolbarCombo);
1120// name_layout->addWidget(new_toolbar);
1121// name_layout->addWidget(del_toolbar);
1122
1123 button_layout->setSpacing(0);
1124 button_layout->setRowStretch(0, 10);
1125 button_layout->addWidget(m_upAction, 1, 1);
1126 button_layout->addWidget(m_removeAction, 2, 0);
1127 button_layout->addWidget(m_insertAction, 2, 2);
1128 button_layout->addWidget(m_downAction, 3, 1);
1129 button_layout->setRowStretch(4, 10);
1130
1131 inactive_layout->addWidget(inactive_label);
1132 inactive_layout->addWidget(inactiveListSearchLine);
1133 inactive_layout->addWidget(m_inactiveList, 1);
1134
1135 active_layout->addWidget(active_label);
1136 active_layout->addWidget(activeListSearchLine);
1137 active_layout->addWidget(m_activeList, 1);
1138 active_layout->addWidget(m_changeIconAction);
1139
1140 list_layout->addLayout(inactive_layout);
1141 list_layout->addLayout(button_layout);
1142 list_layout->addLayout(active_layout);
1143
1144 top_layout->addLayout(name_layout);
1145 top_layout->addWidget(m_comboSeparator);
1146 top_layout->addLayout(list_layout, 10);
1147 top_layout->addWidget(m_helpArea);
1148 top_layout->addWidget(new KSeparator(m_widget));
1149}
1150
1151void KisKEditToolBarWidgetPrivate::loadToolBarCombo(const QString &defaultToolBar)
1152{
1153 const QLatin1String attrName("name");
1154 // just in case, we clear our combo
1155 m_toolbarCombo->clear();
1156
1157 int defaultToolBarId = -1;
1158 int count = 0;
1159 // load in all of the toolbar names into this combo box
1160 QList<XmlData>::const_iterator xit = m_xmlFiles.constBegin();
1161 for (; xit != m_xmlFiles.constEnd(); ++xit) {
1162 // skip the merged one in favor of the local one,
1163 // so that we can change icons
1164 // This also makes the app-defined named for "mainToolBar" appear rather than the ui_standards-defined name.
1165 if ((*xit).type() == XmlData::Merged) {
1166 continue;
1167 }
1168
1169 // each xml file may have any number of toolbars
1170 ToolBarList::const_iterator it = (*xit).barList().begin();
1171 for (; it != (*xit).barList().constEnd(); ++it) {
1172 const QString text = (*xit).toolBarText(*it);
1173 m_toolbarCombo->addItem(text);
1174 const QString name = (*it).attribute(attrName);
1175 if (defaultToolBarId == -1 && name == defaultToolBar) {
1176 defaultToolBarId = count;
1177 }
1178 count++;
1179 }
1180 }
1181 const bool showCombo = (count > 1);
1182 m_comboLabel->setVisible(showCombo);
1183 m_comboSeparator->setVisible(showCombo);
1184 m_toolbarCombo->setVisible(showCombo);
1185 if (defaultToolBarId == -1) {
1186 defaultToolBarId = 0;
1187 }
1188 // we want to the specified item selected and its actions loaded
1189 m_toolbarCombo->setCurrentIndex(defaultToolBarId);
1190 slotToolBarSelected(m_toolbarCombo->currentIndex());
1191}
1192
1194{
1195 const QLatin1String tagSeparator("Separator");
1196 const QLatin1String tagMerge("Merge");
1197 const QLatin1String tagActionList("ActionList");
1198 const QLatin1String tagAction("Action");
1199 const QLatin1String attrName("name");
1200
1201 int sep_num = 0;
1202 QString sep_name(QStringLiteral("separator_%1"));
1203
1204 // clear our lists
1205 m_inactiveList->clear();
1206 m_activeList->clear();
1207 m_insertAction->setEnabled(false);
1208 m_removeAction->setEnabled(false);
1209 m_upAction->setEnabled(false);
1210 m_downAction->setEnabled(false);
1211
1212 // We'll use this action collection
1214
1215 // store the names of our active actions
1216 QSet<QString> active_list;
1217
1218 // Filtering message requested by translators (scripting).
1219 KLocalizedString nameFilter = ki18nc("@item:intable Action name in toolbar editor", "%1");
1220
1221 // see if our current action is in this toolbar
1222 QDomNode n = elem.firstChild();
1223 for (; !n.isNull(); n = n.nextSibling()) {
1224 QDomElement it = n.toElement();
1225 if (it.isNull()) {
1226 continue;
1227 }
1228 if (it.tagName() == tagSeparator) {
1229 ToolBarItem *act = new ToolBarItem(m_activeList, tagSeparator, sep_name.arg(sep_num++), QString());
1230 act->setSeparator(true);
1231 act->setText(SEPARATORSTRING);
1232 it.setAttribute(attrName, act->internalName());
1233 continue;
1234 }
1235
1236 if (it.tagName() == tagMerge) {
1237 // Merge can be named or not - use the name if there is one
1238 QString name = it.attribute(attrName);
1239 ToolBarItem *act = new ToolBarItem(m_activeList, tagMerge, name, i18n("This element will be replaced with all the elements of an embedded component."));
1240 if (name.isEmpty()) {
1241 act->setText(i18n("<Merge>"));
1242 } else {
1243 act->setText(i18n("<Merge %1>", name));
1244 }
1245 continue;
1246 }
1247
1248 if (it.tagName() == tagActionList) {
1249 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."));
1250 act->setText(i18n("ActionList: %1", it.attribute(attrName)));
1251 continue;
1252 }
1253
1254 // iterate through this client's actions
1255 // This used to iterate through _all_ actions, but we don't support
1256 // putting any action into any client...
1257 foreach (QAction *action, actionCollection->actions()) {
1258 // do we have a match?
1259 if (it.attribute(attrName) == action->objectName()) {
1260 // we have a match!
1261 ToolBarItem *act = new ToolBarItem(m_activeList, it.tagName(), action->objectName(), action->toolTip());
1262 act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->iconText())).toString());
1263 act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon);
1264 act->setTextAlongsideIconHidden(action->priority() < QAction::NormalPriority);
1265
1266 active_list.insert(action->objectName());
1267 break;
1268 }
1269 }
1270 }
1271
1272 // go through the rest of the collection
1273 foreach (QAction *action, actionCollection->actions()) {
1274 // skip our active ones
1275 if (active_list.contains(action->objectName())) {
1276 continue;
1277 }
1278
1279 ToolBarItem *act = new ToolBarItem(m_inactiveList, tagAction, action->objectName(), action->toolTip());
1280 act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->text())).toString());
1281 act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon);
1282 }
1283
1284 m_inactiveList->sortItems(Qt::AscendingOrder);
1285
1286 // finally, add default separators to the inactive list
1287 ToolBarItem *act = new ToolBarItem(0L, tagSeparator, sep_name.arg(sep_num++), QString());
1288 act->setSeparator(true);
1289 act->setText(SEPARATORSTRING);
1290 m_inactiveList->insertItem(0, act);
1291}
1292
1297
1299{
1300 // We need to find the XmlData and toolbar element for this index
1301 // To do that, we do the same iteration as the one which filled in the combobox.
1302
1303 int toolbarNumber = 0;
1305 for (; xit != m_xmlFiles.end(); ++xit) {
1306
1307 // skip the merged one in favor of the local one,
1308 // so that we can change icons
1309 if ((*xit).type() == XmlData::Merged) {
1310 continue;
1311 }
1312
1313 // each xml file may have any number of toolbars
1314 ToolBarList::Iterator it = (*xit).barList().begin();
1315 for (; it != (*xit).barList().end(); ++it) {
1316
1317 // is this our toolbar?
1318 if (toolbarNumber == index) {
1319
1320 // save our current settings
1321 m_currentXmlData = & (*xit);
1323
1324 //qDebug() << "found toolbar" << m_currentXmlData->toolBarText(*it) << "m_currentXmlData set to";
1326
1327 // If this is a Merged xmldata, clicking the "change icon" button would assert...
1328 Q_ASSERT(m_currentXmlData->type() != XmlData::Merged);
1329
1330 // load in our values
1332
1333 if ((*xit).type() == XmlData::Part || (*xit).type() == XmlData::Shell) {
1334 m_widget->setDOMDocument((*xit).domDocument());
1335 }
1336 return;
1337 }
1338 ++toolbarNumber;
1339
1340 }
1341 }
1342}
1343
1345{
1346 if (m_inactiveList->selectedItems().count()) {
1347 m_insertAction->setEnabled(true);
1348 QString statusText = static_cast<ToolBarItem *>(m_inactiveList->selectedItems().first())->statusText();
1349 m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText));
1350 } else {
1351 m_insertAction->setEnabled(false);
1352 m_helpArea->setText(QString());
1353 }
1354}
1355
1357{
1358 ToolBarItem *toolitem = 0;
1359 if (!m_activeList->selectedItems().isEmpty()) {
1360 toolitem = static_cast<ToolBarItem *>(m_activeList->selectedItems().first());
1361 }
1362
1363 m_removeAction->setEnabled(toolitem);
1364 m_changeIconAction->setEnabled(toolitem && !toolitem->isSeparator());
1365
1366 if (toolitem) {
1367 m_upAction->setEnabled(toolitem->index() != 0);
1368 m_downAction->setEnabled(toolitem->index() != toolitem->listWidget()->count() - 1);
1369
1370 QString statusText = toolitem->statusText();
1371 m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText));
1372 } else {
1373 m_upAction->setEnabled(false);
1374 m_downAction->setEnabled(false);
1375 m_helpArea->setText(QString());
1376 }
1377}
1378
1380{
1381 QString internalName = static_cast<ToolBarItem *>(m_inactiveList->currentItem())->internalName();
1382
1384 // we're modified, so let this change
1385 Q_EMIT m_widget->enableOk(true);
1386
1387 slotToolBarSelected(m_toolbarCombo->currentIndex());
1388
1389 selectActiveItem(internalName);
1390}
1391
1392void KisKEditToolBarWidgetPrivate::selectActiveItem(const QString &internalName)
1393{
1394 int activeItemCount = m_activeList->count();
1395 for (int i = 0; i < activeItemCount; i++) {
1396 ToolBarItem *item = static_cast<ToolBarItem *>(m_activeList->item(i));
1397 if (item->internalName() == internalName) {
1398 m_activeList->setCurrentItem(item);
1399 break;
1400 }
1401 }
1402}
1403
1410
1412{
1413 if (!item) {
1414 return;
1415 }
1416
1417 QDomElement new_item;
1418 // let's handle the separator specially
1419 if (item->isSeparator()) {
1420 new_item = m_widget->domDocument().createElement(QStringLiteral("Separator"));
1421 } else {
1422 new_item = m_widget->domDocument().createElement(QStringLiteral("Action"));
1423 }
1424
1425 new_item.setAttribute(QStringLiteral("name"), item->internalName());
1426
1427 Q_ASSERT(!m_currentToolBarElem.isNull());
1428
1429 if (before) {
1430 // we have the item in the active list which is before the new
1431 // item.. so let's try our best to add our new item right after it
1432 QDomElement elem = findElementForToolBarItem(before);
1433 Q_ASSERT(!elem.isNull());
1434 m_currentToolBarElem.insertAfter(new_item, elem);
1435 } else {
1436 // simply put it at the beginning or the end of the list.
1437 if (prepend) {
1438 m_currentToolBarElem.insertBefore(new_item, m_currentToolBarElem.firstChild());
1439 } else {
1440 m_currentToolBarElem.appendChild(new_item);
1441 }
1442 }
1443
1444 // and set this container as a noMerge
1445 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
1446
1447 // update the local doc
1449}
1450
1452{
1453 if (!item) {
1454 return;
1455 }
1456
1457 // we're modified, so let this change
1458 Q_EMIT m_widget->enableOk(true);
1459
1460 // now iterate through to find the child to nuke
1461 QDomElement elem = findElementForToolBarItem(item);
1462 if (!elem.isNull()) {
1463 // nuke myself!
1464 m_currentToolBarElem.removeChild(elem);
1465
1466 // and set this container as a noMerge
1467 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
1468
1469 // update the local doc
1471 }
1472}
1473
1475{
1478
1479 int row = item->listWidget()->row(item) - 1;
1480 // make sure we're not the top item already
1482
1483 // we're modified, so let this change
1484 Q_EMIT m_widget->enableOk(true);
1485
1486 moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(row - 1)));
1487}
1488
1490{
1491 QDomElement e = findElementForToolBarItem(item);
1492
1493 if (e.isNull()) {
1494 return;
1495 }
1496
1497 // remove item
1498 m_activeList->takeItem(m_activeList->row(item));
1499
1500 // put it where it's supposed to go
1501 m_activeList->insertItem(m_activeList->row(before) + 1, item);
1502
1503 // make it selected again
1504 m_activeList->setCurrentItem(item);
1505
1506 // and do the real move in the DOM
1507 if (!before) {
1508 m_currentToolBarElem.insertBefore(e, m_currentToolBarElem.firstChild());
1509 } else {
1511 }
1512
1513 // and set this container as a noMerge
1514 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
1515
1516 // update the local doc
1518}
1519
1521{
1524
1525 // make sure we're not the bottom item already
1526 int newRow = item->listWidget()->row(item) + 1;
1527 KIS_SAFE_ASSERT_RECOVER_RETURN(newRow < item->listWidget()->count());
1528
1529 // we're modified, so let this change
1530 Q_EMIT m_widget->enableOk(true);
1531
1532 moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(newRow)));
1533}
1534
1536{
1537 ToolBarItem *toolitem = m_activeList->currentItem();
1538 KIS_SAFE_ASSERT_RECOVER_RETURN(toolitem && !toolitem->isSeparator());
1539
1540 QDomElement elem = findElementForToolBarItem(toolitem);
1541 KIS_SAFE_ASSERT_RECOVER_RETURN(!elem.isNull());
1542
1543 static bool iconsLoaded {false};
1544
1545 // Load all icons, also the icons that only get loaded when an image is created
1546 if (!iconsLoaded) {
1547
1548 QFileInfoList entryList = QDir(":/").entryInfoList(QDir::Files);
1549 entryList += QDir(":/pics/").entryInfoList(QDir::Files);
1550 const QStringList suffixes {"png", "svg", "svgz"};
1551
1552 foreach (const QFileInfo info, entryList) {
1553 if (suffixes.contains(info.suffix())) {
1554 QString basename = info.baseName();
1555 int i = basename.indexOf('_', 5);
1556 basename = basename.mid(i + 1);
1557 KisIconUtils::loadIcon(basename);
1558 }
1559 }
1560 iconsLoaded = true;
1561 }
1562
1564
1565 // Dialog UI
1566 QDialog dialog(m_widget);
1567 dialog.setWindowTitle(i18n("Choose Icon"));
1568 dialog.resize(480, 500);
1569
1570 QVBoxLayout *layout = new QVBoxLayout(&dialog);
1571
1572 // Filter input
1573 QLineEdit *filterEdit = new QLineEdit(&dialog);
1574 filterEdit->setPlaceholderText(i18n("Filter icons..."));
1575 layout->addWidget(filterEdit);
1576
1577 // Icon list
1578 QListWidget *list = new QListWidget(&dialog);
1579 list->setViewMode(QListView::ListMode); // icon + text side by side
1580 list->setIconSize(QSize(22, 22));
1581 list->setResizeMode(QListWidget::Adjust);
1582 list->setSelectionMode(QAbstractItemView::SingleSelection);
1583 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(list);
1584 if (scroller) {
1585 QObject::connect(scroller, &QScroller::stateChanged, m_widget, [&](QScroller::State state) {
1587 });
1588 }
1589
1590 // Fill the list
1591 for (const QString &iconName : loadedIcons) {
1592 QListWidgetItem *item = new QListWidgetItem(
1593 KisIconUtils::loadIcon(iconName),
1594 iconName,
1595 list
1596 );
1597 item->setData(Qt::UserRole, iconName);
1598 }
1599 layout->addWidget(list);
1600
1601 // OK/Cancel buttons
1602 QDialogButtonBox *backButton = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog);
1603 layout->addWidget(backButton);
1604
1605 QObject::connect(backButton, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
1606 QObject::connect(backButton, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
1607
1608 // Filter logic
1609 QObject::connect(filterEdit, &QLineEdit::textChanged, &dialog, [&list](const QString &text) {
1610 for (int i = 0; i < list->count(); ++i) {
1611 QListWidgetItem *item = list->item(i);
1612 bool match = item->text().contains(text, Qt::CaseInsensitive);
1613 item->setHidden(!match);
1614 }
1615 });
1616
1617 // Optional: preselect current icon
1618 QString currentIconName = elem.attribute(QStringLiteral("icon"));
1619 for (int i = 0; i < list->count(); ++i) {
1620 if (list->item(i)->data(Qt::UserRole).toString() == currentIconName) {
1621 list->setCurrentItem(list->item(i));
1622 break;
1623 }
1624 }
1625
1626 // Execute
1627 if (dialog.exec() == QDialog::Accepted && list->currentItem()) {
1628 QString chosenIconName = list->currentItem()->data(Qt::UserRole).toString();
1629
1630 toolitem->setIcon(KisIconUtils::loadIcon(chosenIconName));
1631
1632 // Update XML in the toolbar DOM
1633 // Ensure (ActionIconOverrides) exist
1634 QDomDocument doc = m_widget->domDocument();
1635 QDomElement root = doc.documentElement();
1636 QDomElement overridesElem = root.namedItem(QStringLiteral("ActionIconOverrides")).toElement();
1637 if (overridesElem.isNull()) {
1638 overridesElem = doc.createElement(QStringLiteral("ActionIconOverrides"));
1639 root.appendChild(overridesElem);
1640 }
1641
1642 // Find Action in overrides or create new
1643 QString actionName = toolitem->internalName();
1644 QDomElement overrideAction;
1645 for (QDomNode child = overridesElem.firstChild(); !child.isNull(); child = child.nextSibling()) {
1646 QDomElement childElement = child.toElement();
1647 if (childElement.attribute(QStringLiteral("name")) == actionName) {
1648 overrideAction = childElement;
1649 break;
1650 }
1651 }
1652 if (overrideAction.isNull()) {
1653 overrideAction = doc.createElement(QStringLiteral("Action"));
1654 overrideAction.setAttribute(QStringLiteral("name"), actionName);
1655 overridesElem.appendChild(overrideAction);
1656 }
1657
1658 // Set icon override
1659 overrideAction.setAttribute(QStringLiteral("icon"), chosenIconName);
1660
1661 // Mark document modified
1662 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
1665 m_activeList->update();
1666
1667 Q_EMIT m_widget->enableOk(true);
1668 }
1669}
1670
1672{
1674 for (; xit != m_xmlFiles.end(); ++xit) {
1675 if ((*xit).type() == XmlData::Merged) {
1676 continue;
1677 }
1678
1679 if ((*xit).type() == XmlData::Shell ||
1680 (*xit).type() == XmlData::Part) {
1681 if (m_currentXmlData->xmlFile() == (*xit).xmlFile()) {
1682 (*xit).m_isModified = true;
1683 return;
1684 }
1685
1686 continue;
1687 }
1688
1689 (*xit).m_isModified = true;
1690 const QLatin1String attrName("name");
1691 ToolBarList::Iterator it = (*xit).barList().begin();
1692 for (; it != (*xit).barList().end(); ++it) {
1693 QString name((*it).attribute(attrName));
1694 QString tag((*it).tagName());
1695 if ((tag != elem.tagName()) || (name != elem.attribute(attrName))) {
1696 continue;
1697 }
1698
1699 QDomElement toolbar = (*xit).domDocument().documentElement().toElement();
1700 toolbar.replaceChild(elem, (*it));
1701 return;
1702 }
1703
1704 // just append it
1705 QDomElement toolbar = (*xit).domDocument().documentElement().toElement();
1706 Q_ASSERT(!toolbar.isNull());
1707 toolbar.appendChild(elem);
1708 }
1709}
1710
1711
1712void KisKEditToolBarWidgetPrivate::slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList)
1713{
1714 //qDebug() << "slotDropped list=" << (list==m_activeList?"activeList":"inactiveList")
1715 // << "index=" << index << "sourceIsActiveList=" << sourceIsActiveList;
1716 if (list == m_activeList) {
1717 ToolBarItem *after = index > 0 ? static_cast<ToolBarItem *>(list->item(index - 1)) : 0;
1718 //qDebug() << "after" << after->text() << after->internalTag();
1719 if (sourceIsActiveList) {
1720 // has been dragged within the active list (moved).
1721 moveActive(item, after);
1722 } else {
1723 // dragged from the inactive list to the active list
1724 insertActive(item, after, true);
1725 }
1726 } else if (list == m_inactiveList) {
1727 // has been dragged to the inactive list -> remove from the active list.
1728 removeActive(item);
1729 }
1730
1731 delete item; // not needed anymore. must be deleted before slotToolBarSelected clears the lists
1732
1733 // we're modified, so let this change
1734 Q_EMIT m_widget->enableOk(true);
1735
1736 slotToolBarSelected(m_toolbarCombo->currentIndex());
1737}
1738
1739void KisKEditToolBar::showEvent(QShowEvent *event)
1740{
1741 if (!event->spontaneous()) {
1742 // The dialog has been shown, enable toolbar editing
1743 if (d->m_factory) {
1744 // call the xmlgui-factory version
1745 d->m_widget->load(d->m_factory, d->m_defaultToolBar);
1746 } else {
1747 // call the action collection version
1748 d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar);
1749 }
1750
1752 }
1753 QDialog::showEvent(event);
1754}
1755
1756void KisKEditToolBar::hideEvent(QHideEvent *event)
1757{
1758 // The dialog has been hidden, disable toolbar editing
1760
1761 QDialog::hideEvent(event);
1762}
1763
1764#include "moc_kedittoolbar.cpp"
1765#include "moc_kedittoolbar_p.cpp"
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
int iconSize(qreal width, qreal height)
IconTextEditDialog(QWidget *parent=0)
void setIconText(const QString &text)
void setTextAlongsideIconHidden(bool hidden)
void slotTextChanged(const QString &text)
QString xmlFile(const QString &xml_file) const
KisKEditToolBarWidgetPrivate(KisKEditToolBarWidget *widget, const QString &cName, KisKActionCollection *collection)
void slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList)
void insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend=false)
void initOldStyle(const QString &file, bool global, const QString &defaultToolbar)
void moveActive(ToolBarItem *item, ToolBarItem *before)
void loadToolBarCombo(const QString &defaultToolbar)
void initFromFactory(KisKXMLGUIFactory *factory, const QString &defaultToolbar)
QDomElement findElementForToolBarItem(const ToolBarItem *item) const
QString loadXMLFile(const QString &_xml_file)
void loadActions(const QDomElement &elem)
A widget used to customize or configure toolbars.
KisKEditToolBarWidget(KisKActionCollection *collection, QWidget *parent=0L)
KisKEditToolBarWidgetPrivate *const d
KisKActionCollection * actionCollection() const override
void load(const QString &resourceFile, bool global=true, const QString &defaultToolBar=QString())
QString internalName() const
void setTextAlongsideIconHidden(bool hidden)
ToolBarItem(QListWidget *parent, const QString &tag=QString(), const QString &name=QString(), const QString &statusText=QString())
QString internalTag() const
void setStatusText(const QString &text)
bool isTextAlongsideIconHidden() const
void setInternalName(const QString &name)
void setSeparator(bool sep)
void setInternalTag(const QString &tag)
QString statusText() const
bool dropMimeData(int index, const QMimeData *data, Qt::DropAction action) override
void dropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList)
ToolBarItem * currentItem() const
void setActiveList(bool isActiveList)
ToolBarListWidget(QWidget *parent=0)
QMimeData * mimeData(const QList< QListWidgetItem * > &items) const override
QString toolBarText(const QDomElement &it) const
KisKActionCollection * actionCollection() const
XmlData(XmlType xmlType, const QString &xmlFile, KisKActionCollection *collection)
void setDomDocument(const QDomDocument &domDoc)
XmlType type() const
const ToolBarList & barList() const
KisKActionCollection * m_actionCollection
QDomDocument m_document
ToolBarList & barList()
QDomDocument & domDocument()
QString xmlFile() const
const QDomDocument & domDocument() const
A container for a set of QAction objects.
QList< QAction * > actions() const
void addAssociatedWidget(QWidget *widget)
KisKActionCollection * m_collection
KisKEditToolBar * q
KisKXMLGUIFactory * m_factory
KisKEditToolBarPrivate(KisKEditToolBar *q)
void _k_slotButtonClicked(QAbstractButton *button)
QDialogButtonBox * m_buttonBox
KisKEditToolBarWidget * m_widget
A dialog used to customize or configure toolbars.
void newToolBarConfig()
void hideEvent(QHideEvent *event) override
static void setGlobalDefaultToolBar(const char *toolBarName)
void setResourceFile(const QString &file, bool global=true)
~KisKEditToolBar() override
destructor
QT_MOC_COMPAT void newToolbarConfig()
void showEvent(QShowEvent *event) override
KisKEditToolBarPrivate *const d
void setDefaultToolBar(const QString &toolBarName)
void setXMLGUIBuildDocument(const QDomDocument &doc)
void setDOMDocument(const QDomDocument &document, bool merge=false)
virtual QString xmlFile() const
virtual QDomDocument domDocument() const
virtual QString localXMLFile() const
KisKXMLGUIFactory * factory() const
void setXMLFile(const QString &file, bool merge=false, bool setXMLDoc=true)
friend class KDEPrivate::KisKEditToolBarWidget
void setXML(const QString &document, bool merge=false)
virtual KisKActionCollection * actionCollection() const
static bool saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &componentName=QString())
static QString readConfigFile(const QString &filename, const QString &componentName=QString())
void removeClient(KisKXMLGUIClient *client)
void addClient(KisKXMLGUIClient *client)
QList< KisKXMLGUIClient * > clients() const
static void setToolBarsEditable(bool editable)
static const char separatorstring[]
QList< QDomElement > ToolBarList
#define SEPARATORSTRING
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
QString button(const QWheelEvent &ev)
static ToolBarList findToolBars(const QDomElement &start)
static QDataStream & operator>>(QDataStream &s, ToolBarItem &item)
static QDataStream & operator<<(QDataStream &s, const ToolBarItem &item)
QIcon loadIcon(const QString &name)
QStringList allUniqueLoadedIconNames()
KRITAWIDGETUTILS_EXPORT void updateCursor(QWidget *source, QScroller::State state)
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)