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
594Q_GLOBAL_STATIC(QString, s_defaultToolBarName)
595
597 QWidget *parent)
598 : QDialog(parent),
599 d(new KisKEditToolBarPrivate(this))
600{
601 d->m_widget = new KisKEditToolBarWidget(this);
602 d->init();
603 d->m_factory = factory;
604}
605
607{
608 m_accept = false;
609 m_factory = 0;
610
611 q->setDefaultToolBar(QString());
612
613 q->setWindowTitle(i18n("Configure Toolbars"));
614 q->setModal(false);
615
616 m_layout = new QVBoxLayout(q);
617
618 m_layout->addWidget(m_widget);
619
620 m_buttonBox = new QDialogButtonBox(q);
621 m_buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults
622 | QDialogButtonBox::Ok
623 | QDialogButtonBox::Apply
624 | QDialogButtonBox::Cancel);
625 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
626 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply());
627 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
628 KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults());
629 q->connect(m_buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(_k_slotButtonClicked(QAbstractButton*)));
630 q->connect(m_buttonBox, SIGNAL(rejected()), SLOT(reject()));
631 m_layout->addWidget(m_buttonBox);
632
633 q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool)));
634 q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool)));
635 _k_enableApply(false);
636
637 q->setMinimumSize(q->sizeHint());
638}
639
640void KisKEditToolBar::setResourceFile(const QString &file, bool global)
641{
642 d->m_file = file;
643 d->m_global = global;
645}
646
648{
649 delete d;
650 s_defaultToolBarName()->clear();
651}
652
653void KisKEditToolBar::setDefaultToolBar(const QString &toolBarName)
654{
655 if (toolBarName.isEmpty()) {
656 d->m_defaultToolBar = *s_defaultToolBarName();
657 } else {
658 d->m_defaultToolBar = toolBarName;
659 }
660}
661
663{
664 m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(b);
665 m_accept = b;
666}
667
669{
670 m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(b);
671}
672
674{
675 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) {
676 return;
677 }
678
679 KisKEditToolBarWidget *oldWidget = m_widget;
680 m_widget = 0;
681 m_accept = false;
682
683 if (m_factory) {
684 foreach (KisKXMLGUIClient *client, m_factory->clients()) {
685 const QString file = client->localXMLFile();
686 if (file.isEmpty()) {
687 continue;
688 }
689 //qDebug(240) << "Deleting local xml file" << file;
690 // << "for client" << client << typeid(*client).name();
691 if (QFile::exists(file))
692 if (!QFile::remove(file)) {
693 qWarning() << "Could not delete" << file;
694 }
695 }
696
697 // Reload the xml files in all clients, now that the local files are gone
698 oldWidget->rebuildKisKXMLGUIClients();
699
702 } else {
703 int slash = m_file.lastIndexOf(QLatin1Char('/')) + 1;
704 if (slash) {
705 m_file = m_file.mid(slash);
706 }
707 const QString xml_file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
708 QStringLiteral("/kxmlgui5/") + QCoreApplication::instance()->applicationName() + QLatin1Char('/') + m_file;
709
710 if (QFile::exists(xml_file))
711 if (!QFile::remove(xml_file)) {
712 qWarning() << "Could not delete " << xml_file;
713 }
714
717 }
718
719 // Copy the geometry to minimize UI flicker
720 m_widget->setGeometry(oldWidget->geometry());
721 delete oldWidget;
722 m_layout->insertWidget(0, m_widget);
723
724 q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool)));
725 q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool)));
726
727 _k_enableApply(false);
728
729 Q_EMIT q->newToolBarConfig();
730 Q_EMIT q->newToolbarConfig(); // compat
731}
732
734{
735 QDialogButtonBox::StandardButton type = m_buttonBox->standardButton(button);
736
737 switch (type) {
738 case QDialogButtonBox::Ok:
739 okClicked();
740 break;
741 case QDialogButtonBox::Apply:
742 applyClicked();
743 break;
744 case QDialogButtonBox::RestoreDefaults:
746 break;
747 default:
748 break;
749 }
750}
751
753{
754 if (!m_accept) {
755 q->reject();
756 return;
757 }
758
759 // Do not rebuild GUI and Q_EMIT the "newToolBarConfig" signal again here if the "Apply"
760 // button was already pressed and no further changes were made.
761 if (m_buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) {
762 m_widget->save();
763 Q_EMIT q->newToolBarConfig();
764 Q_EMIT q->newToolbarConfig(); // compat
765 }
766 q->accept();
767}
768
770{
771 (void)m_widget->save();
772 _k_enableApply(false);
773 Q_EMIT q->newToolBarConfig();
774 Q_EMIT q->newToolbarConfig(); // compat
775}
776
777void KisKEditToolBar::setGlobalDefaultToolBar(const char *toolbarName)
778{
779 *s_defaultToolBarName() = QString::fromLatin1(toolbarName);
780}
781
783 QWidget *parent)
784 : QWidget(parent),
785 d(new KisKEditToolBarWidgetPrivate(this, componentName(), collection))
786{
787 d->setupLayout();
788}
789
791 : QWidget(parent),
792 d(new KisKEditToolBarWidgetPrivate(this, componentName(), KisKXMLGUIClient::actionCollection() /*create new one*/))
793{
794 d->setupLayout();
795}
796
801
802void KisKEditToolBarWidget::load(const QString &file, bool global, const QString &defaultToolBar)
803{
804 d->initOldStyle(file, global, defaultToolBar);
805}
806
807void KisKEditToolBarWidget::load(KisKXMLGUIFactory *factory, const QString &defaultToolBar)
808{
809 d->initFromFactory(factory, defaultToolBar);
810}
811
812void KisKEditToolBarWidgetPrivate::initOldStyle(const QString &resourceFile,
813 bool global,
814 const QString &defaultToolBar)
815{
816 qDebug() << "initOldStyle";
817 //TODO: make sure we can call this multiple times?
818 if (m_loadedOnce) {
819 return;
820 }
821
822 m_loadedOnce = true;
823 //d->m_actionList = collection->actions();
824
825 // handle the merging
826 if (global) {
827 m_widget->loadStandardsXmlFile(); // ui_standards.xmlgui
828 }
829 const QString localXML = loadXMLFile(resourceFile);
830 m_widget->setXML(localXML, global ? true /*merge*/ : false);
831
832 // first, get all of the necessary info for our local xml
833 XmlData local(XmlData::Local, xmlFile(resourceFile), m_collection);
834 QDomDocument domDoc;
835 domDoc.setContent(localXML);
836 local.setDomDocument(domDoc);
837 m_xmlFiles.append(local);
838
839 // then, the merged one (ui_standards + local xml)
840 XmlData merge(XmlData::Merged, QString(), m_collection);
842 m_xmlFiles.append(merge);
843
844#ifndef NDEBUG
845 dump();
846#endif
847
848 // now load in our toolbar combo box
849 loadToolBarCombo(defaultToolBar);
850 m_widget->adjustSize();
851 m_widget->setMinimumSize(m_widget->sizeHint());
852}
853
855 const QString &defaultToolBar)
856{
857 qDebug() << "initFromFactory";
858 //TODO: make sure we can call this multiple times?
859 if (m_loadedOnce) {
860 return;
861 }
862
863 m_loadedOnce = true;
864
865 m_factory = factory;
866
867 // add all of the client data
868 bool first = true;
869 foreach (KisKXMLGUIClient *client, factory->clients()) {
870 if (client->xmlFile().isEmpty()) {
871 continue;
872 }
873
875 if (first) {
876 type = XmlData::Shell;
877 first = false;
878 Q_ASSERT(!client->localXMLFile().isEmpty()); // where would we save changes??
879 }
880
881 XmlData data(type, client->localXMLFile(), client->actionCollection());
882 QDomDocument domDoc = client->domDocument();
883 data.setDomDocument(domDoc);
884 m_xmlFiles.append(data);
885
886 //d->m_actionList += client->actionCollection()->actions();
887 }
888
889#ifndef NDEBUG
890 //d->dump();
891#endif
892
893 // now load in our toolbar combo box
894 loadToolBarCombo(defaultToolBar);
895 m_widget->adjustSize();
896 m_widget->setMinimumSize(m_widget->sizeHint());
897
899 foreach (QAction *action, m_widget->actionCollection()->actions()) {
900 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
901 }
902}
903
905{
906 //qDebug(240) << "KisKEditToolBarWidget::save";
908 for (; it != d->m_xmlFiles.end(); ++it) {
909 // let's not save non-modified files
910 if (!((*it).m_isModified)) {
911 continue;
912 }
913
914 // let's also skip (nonexistent) merged files
915 if ((*it).type() == XmlData::Merged) {
916 continue;
917 }
918
919 // Add noMerge="1" to all the menus since we are saving the merged data
920 QDomNodeList menuNodes = (*it).domDocument().elementsByTagName(QStringLiteral("Menu"));
921 for (int i = 0; i < menuNodes.length(); ++i) {
922 QDomNode menuNode = menuNodes.item(i);
923 QDomElement menuElement = menuNode.toElement();
924 if (menuElement.isNull()) {
925 continue;
926 }
927 menuElement.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
928 }
929
930 //qDebug() << (*it).domDocument().toString();
931
932 //qDebug(240) << "Saving " << (*it).xmlFile();
933 // if we got this far, we might as well just save it
934 KisKXMLGUIFactory::saveConfigFile((*it).domDocument(), (*it).xmlFile());
935 }
936
937 if (!d->m_factory) {
938 return;
939 }
940
942}
943
945{
946 if (!d->m_factory) {
947 return;
948 }
949
950 const QList<KisKXMLGUIClient *> clients = d->m_factory->clients();
951 //qDebug(240) << "factory: " << clients.count() << " clients";
952
953 // remove the elements starting from the last going to the first
954 if (!clients.count()) {
955 return;
956 }
957
958 QListIterator<KisKXMLGUIClient *> clientIterator = clients;
959 clientIterator.toBack();
960 while (clientIterator.hasPrevious()) {
961 KisKXMLGUIClient *client = clientIterator.previous();
962 //qDebug(240) << "factory->removeClient " << client;
963 d->m_factory->removeClient(client);
964 }
965
966 KisKXMLGUIClient *firstClient = clients.first();
967
968 // now, rebuild the gui from the first to the last
969 //qDebug(240) << "rebuilding the gui";
970 foreach (KisKXMLGUIClient *client, clients) {
971 //qDebug(240) << "updating client " << client << " " << client->componentName() << " xmlFile=" << client->xmlFile();
972 QString file(client->xmlFile()); // before setting ui_standards!
973 if (!file.isEmpty()) {
974 // passing an empty stream forces the clients to reread the XML
975 client->setXMLGUIBuildDocument(QDomDocument());
976
977 // for the shell, merge in ui_standards.xmlgui
978 if (client == firstClient) { // same assumption as in the ctor: first==shell
979 client->loadStandardsXmlFile();
980 }
981
982 // and this forces it to use the *new* XML file
983 client->setXMLFile(file, client == firstClient /* merge if shell */);
984
985 // [we can't use reloadXML, it doesn't load ui_standards.xmlgui]
986 }
987 }
988
989 // Now we can add the clients to the factory
990 // We don't do it in the loop above because adding a part automatically
991 // adds its plugins, so we must make sure the plugins were updated first.
992 foreach (KisKXMLGUIClient *client, clients) {
993 d->m_factory->addClient(client);
994 }
995}
996
998{
999 // the toolbar name combo
1000 m_comboLabel = new QLabel(i18n("&Toolbar:"), m_widget);
1001 m_toolbarCombo = new QComboBox(m_widget);
1002 m_comboLabel->setBuddy(m_toolbarCombo);
1003 m_comboSeparator = new KSeparator(m_widget);
1004 QObject::connect(m_toolbarCombo, SIGNAL(activated(int)),
1005 m_widget, SLOT(slotToolBarSelected(int)));
1006
1007// QPushButton *new_toolbar = new QPushButton(i18n("&New"), this);
1008// new_toolbar->setPixmap(BarIcon("document-new", KisIconUtils::SizeSmall));
1009// new_toolbar->setEnabled(false); // disabled until implemented
1010// QPushButton *del_toolbar = new QPushButton(i18n("&Delete"), this);
1011// del_toolbar->setPixmap(BarIcon("edit-delete", KisIconUtils::SizeSmall));
1012// del_toolbar->setEnabled(false); // disabled until implemented
1013
1014 // our list of inactive actions
1015 QLabel *inactive_label = new QLabel(i18n("A&vailable actions:"), m_widget);
1017 m_inactiveList->setDragEnabled(true);
1019 m_inactiveList->setMinimumSize(180, 250);
1020 m_inactiveList->setDropIndicatorShown(false); // #165663
1021 inactive_label->setBuddy(m_inactiveList);
1022 QObject::connect(m_inactiveList, SIGNAL(itemSelectionChanged()),
1024 QObject::connect(m_inactiveList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
1025 m_widget, SLOT(slotInsertButton()));
1026 QObject::connect(m_inactiveList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)),
1028
1029 {
1031 if (scroller) {
1032 QObject::connect(scroller, &QScroller::stateChanged, m_widget, [&](QScroller::State state) {
1034 });
1035 }
1036 }
1037
1038 KListWidgetSearchLine *inactiveListSearchLine = new KListWidgetSearchLine(m_widget, m_inactiveList);
1039 inactiveListSearchLine->setPlaceholderText(i18nc("Filter as in showing only matching items", "Filter"));
1040
1041 // our list of active actions
1042 QLabel *active_label = new QLabel(i18n("Curr&ent actions:"), m_widget);
1044 m_activeList->setDragEnabled(true);
1046 // With Qt-4.1 only setting MiniumWidth results in a 0-width icon column ...
1047 m_activeList->setMinimumSize(m_inactiveList->minimumWidth(), 100);
1048 active_label->setBuddy(m_activeList);
1049
1050 QObject::connect(m_activeList, SIGNAL(itemSelectionChanged()),
1052 QObject::connect(m_activeList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
1053 m_widget, SLOT(slotRemoveButton()));
1054 QObject::connect(m_activeList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)),
1056 {
1058 if (scroller) {
1059 QObject::connect(scroller, &QScroller::stateChanged, m_widget, [&](QScroller::State state) {
1061 });
1062 }
1063 }
1064
1065 // Edit Icon Button under active actions
1066 m_changeIconAction = new QToolButton(m_widget);
1067 m_changeIconAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("preferences-desktop-icons")));
1068 m_changeIconAction->setText(i18n("Change Icon..."));
1069 m_changeIconAction->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
1070 m_changeIconAction->setEnabled(false);
1071
1072 QObject::connect(m_changeIconAction, SIGNAL(clicked()), m_widget, SLOT(slotChangeIconButton()));
1073
1074 KListWidgetSearchLine *activeListSearchLine = new KListWidgetSearchLine(m_widget, m_activeList);
1075 activeListSearchLine->setPlaceholderText(i18n("Filter"));
1076
1077 // The buttons in the middle
1078
1079 m_upAction = new QToolButton(m_widget);
1080 m_upAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("arrow-up")));
1081 m_upAction->setEnabled(false);
1082 m_upAction->setAutoRepeat(true);
1083 QObject::connect(m_upAction, SIGNAL(clicked()), m_widget, SLOT(slotUpButton()));
1084
1085 m_insertAction = new QToolButton(m_widget);
1086 m_insertAction->setIcon(KisIconUtils::loadIcon(QApplication::isRightToLeft() ? QStringLiteral("arrow-left") : QLatin1String("arrow-right")));
1087 m_insertAction->setEnabled(false);
1088 QObject::connect(m_insertAction, SIGNAL(clicked()), m_widget, SLOT(slotInsertButton()));
1089
1090 m_removeAction = new QToolButton(m_widget);
1091 m_removeAction->setIcon(KisIconUtils::loadIcon(QApplication::isRightToLeft() ? QStringLiteral("arrow-right") : QLatin1String("arrow-left")));
1092 m_removeAction->setEnabled(false);
1093 QObject::connect(m_removeAction, SIGNAL(clicked()), m_widget, SLOT(slotRemoveButton()));
1094
1095 m_downAction = new QToolButton(m_widget);
1096 m_downAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("arrow-down")));
1097 m_downAction->setEnabled(false);
1098 m_downAction->setAutoRepeat(true);
1099 QObject::connect(m_downAction, SIGNAL(clicked()), m_widget, SLOT(slotDownButton()));
1100
1101 m_helpArea = new QLabel(m_widget);
1102 m_helpArea->setWordWrap(true);
1103
1104 // now start with our layouts
1105 QVBoxLayout *top_layout = new QVBoxLayout(m_widget);
1106 top_layout->setContentsMargins(0, 0, 0, 0);
1107
1108 QVBoxLayout *name_layout = new QVBoxLayout();
1109 QHBoxLayout *list_layout = new QHBoxLayout();
1110
1111 QVBoxLayout *inactive_layout = new QVBoxLayout();
1112 QVBoxLayout *active_layout = new QVBoxLayout();
1113
1114 QGridLayout *button_layout = new QGridLayout();
1115
1116 name_layout->addWidget(m_comboLabel);
1117 name_layout->addWidget(m_toolbarCombo);
1118// name_layout->addWidget(new_toolbar);
1119// name_layout->addWidget(del_toolbar);
1120
1121 button_layout->setSpacing(0);
1122 button_layout->setRowStretch(0, 10);
1123 button_layout->addWidget(m_upAction, 1, 1);
1124 button_layout->addWidget(m_removeAction, 2, 0);
1125 button_layout->addWidget(m_insertAction, 2, 2);
1126 button_layout->addWidget(m_downAction, 3, 1);
1127 button_layout->setRowStretch(4, 10);
1128
1129 inactive_layout->addWidget(inactive_label);
1130 inactive_layout->addWidget(inactiveListSearchLine);
1131 inactive_layout->addWidget(m_inactiveList, 1);
1132
1133 active_layout->addWidget(active_label);
1134 active_layout->addWidget(activeListSearchLine);
1135 active_layout->addWidget(m_activeList, 1);
1136 active_layout->addWidget(m_changeIconAction);
1137
1138 list_layout->addLayout(inactive_layout);
1139 list_layout->addLayout(button_layout);
1140 list_layout->addLayout(active_layout);
1141
1142 top_layout->addLayout(name_layout);
1143 top_layout->addWidget(m_comboSeparator);
1144 top_layout->addLayout(list_layout, 10);
1145 top_layout->addWidget(m_helpArea);
1146 top_layout->addWidget(new KSeparator(m_widget));
1147}
1148
1149void KisKEditToolBarWidgetPrivate::loadToolBarCombo(const QString &defaultToolBar)
1150{
1151 const QLatin1String attrName("name");
1152 // just in case, we clear our combo
1153 m_toolbarCombo->clear();
1154
1155 int defaultToolBarId = -1;
1156 int count = 0;
1157 // load in all of the toolbar names into this combo box
1158 QList<XmlData>::const_iterator xit = m_xmlFiles.constBegin();
1159 for (; xit != m_xmlFiles.constEnd(); ++xit) {
1160 // skip the merged one in favor of the local one,
1161 // so that we can change icons
1162 // This also makes the app-defined named for "mainToolBar" appear rather than the ui_standards-defined name.
1163 if ((*xit).type() == XmlData::Merged) {
1164 continue;
1165 }
1166
1167 // each xml file may have any number of toolbars
1168 ToolBarList::const_iterator it = (*xit).barList().begin();
1169 for (; it != (*xit).barList().constEnd(); ++it) {
1170 const QString text = (*xit).toolBarText(*it);
1171 m_toolbarCombo->addItem(text);
1172 const QString name = (*it).attribute(attrName);
1173 if (defaultToolBarId == -1 && name == defaultToolBar) {
1174 defaultToolBarId = count;
1175 }
1176 count++;
1177 }
1178 }
1179 const bool showCombo = (count > 1);
1180 m_comboLabel->setVisible(showCombo);
1181 m_comboSeparator->setVisible(showCombo);
1182 m_toolbarCombo->setVisible(showCombo);
1183 if (defaultToolBarId == -1) {
1184 defaultToolBarId = 0;
1185 }
1186 // we want to the specified item selected and its actions loaded
1187 m_toolbarCombo->setCurrentIndex(defaultToolBarId);
1188 slotToolBarSelected(m_toolbarCombo->currentIndex());
1189}
1190
1192{
1193 const QLatin1String tagSeparator("Separator");
1194 const QLatin1String tagMerge("Merge");
1195 const QLatin1String tagActionList("ActionList");
1196 const QLatin1String tagAction("Action");
1197 const QLatin1String attrName("name");
1198
1199 int sep_num = 0;
1200 QString sep_name(QStringLiteral("separator_%1"));
1201
1202 // clear our lists
1203 m_inactiveList->clear();
1204 m_activeList->clear();
1205 m_insertAction->setEnabled(false);
1206 m_removeAction->setEnabled(false);
1207 m_upAction->setEnabled(false);
1208 m_downAction->setEnabled(false);
1209
1210 // We'll use this action collection
1212
1213 // store the names of our active actions
1214 QSet<QString> active_list;
1215
1216 // Filtering message requested by translators (scripting).
1217 KLocalizedString nameFilter = ki18nc("@item:intable Action name in toolbar editor", "%1");
1218
1219 // see if our current action is in this toolbar
1220 QDomNode n = elem.firstChild();
1221 for (; !n.isNull(); n = n.nextSibling()) {
1222 QDomElement it = n.toElement();
1223 if (it.isNull()) {
1224 continue;
1225 }
1226 if (it.tagName() == tagSeparator) {
1227 ToolBarItem *act = new ToolBarItem(m_activeList, tagSeparator, sep_name.arg(sep_num++), QString());
1228 act->setSeparator(true);
1229 act->setText(SEPARATORSTRING);
1230 it.setAttribute(attrName, act->internalName());
1231 continue;
1232 }
1233
1234 if (it.tagName() == tagMerge) {
1235 // Merge can be named or not - use the name if there is one
1236 QString name = it.attribute(attrName);
1237 ToolBarItem *act = new ToolBarItem(m_activeList, tagMerge, name, i18n("This element will be replaced with all the elements of an embedded component."));
1238 if (name.isEmpty()) {
1239 act->setText(i18n("<Merge>"));
1240 } else {
1241 act->setText(i18n("<Merge %1>", name));
1242 }
1243 continue;
1244 }
1245
1246 if (it.tagName() == tagActionList) {
1247 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."));
1248 act->setText(i18n("ActionList: %1", it.attribute(attrName)));
1249 continue;
1250 }
1251
1252 // iterate through this client's actions
1253 // This used to iterate through _all_ actions, but we don't support
1254 // putting any action into any client...
1255 foreach (QAction *action, actionCollection->actions()) {
1256 // do we have a match?
1257 if (it.attribute(attrName) == action->objectName()) {
1258 // we have a match!
1259 ToolBarItem *act = new ToolBarItem(m_activeList, it.tagName(), action->objectName(), action->toolTip());
1260 act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->iconText())).toString());
1261 act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon);
1262 act->setTextAlongsideIconHidden(action->priority() < QAction::NormalPriority);
1263
1264 active_list.insert(action->objectName());
1265 break;
1266 }
1267 }
1268 }
1269
1270 // go through the rest of the collection
1271 foreach (QAction *action, actionCollection->actions()) {
1272 // skip our active ones
1273 if (active_list.contains(action->objectName())) {
1274 continue;
1275 }
1276
1277 ToolBarItem *act = new ToolBarItem(m_inactiveList, tagAction, action->objectName(), action->toolTip());
1278 act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->text())).toString());
1279 act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon);
1280 }
1281
1282 m_inactiveList->sortItems(Qt::AscendingOrder);
1283
1284 // finally, add default separators to the inactive list
1285 ToolBarItem *act = new ToolBarItem(0L, tagSeparator, sep_name.arg(sep_num++), QString());
1286 act->setSeparator(true);
1287 act->setText(SEPARATORSTRING);
1288 m_inactiveList->insertItem(0, act);
1289}
1290
1295
1297{
1298 // We need to find the XmlData and toolbar element for this index
1299 // To do that, we do the same iteration as the one which filled in the combobox.
1300
1301 int toolbarNumber = 0;
1303 for (; xit != m_xmlFiles.end(); ++xit) {
1304
1305 // skip the merged one in favor of the local one,
1306 // so that we can change icons
1307 if ((*xit).type() == XmlData::Merged) {
1308 continue;
1309 }
1310
1311 // each xml file may have any number of toolbars
1312 ToolBarList::Iterator it = (*xit).barList().begin();
1313 for (; it != (*xit).barList().end(); ++it) {
1314
1315 // is this our toolbar?
1316 if (toolbarNumber == index) {
1317
1318 // save our current settings
1319 m_currentXmlData = & (*xit);
1321
1322 //qDebug() << "found toolbar" << m_currentXmlData->toolBarText(*it) << "m_currentXmlData set to";
1324
1325 // If this is a Merged xmldata, clicking the "change icon" button would assert...
1326 Q_ASSERT(m_currentXmlData->type() != XmlData::Merged);
1327
1328 // load in our values
1330
1331 if ((*xit).type() == XmlData::Part || (*xit).type() == XmlData::Shell) {
1332 m_widget->setDOMDocument((*xit).domDocument());
1333 }
1334 return;
1335 }
1336 ++toolbarNumber;
1337
1338 }
1339 }
1340}
1341
1343{
1344 if (m_inactiveList->selectedItems().count()) {
1345 m_insertAction->setEnabled(true);
1346 QString statusText = static_cast<ToolBarItem *>(m_inactiveList->selectedItems().first())->statusText();
1347 m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText));
1348 } else {
1349 m_insertAction->setEnabled(false);
1350 m_helpArea->setText(QString());
1351 }
1352}
1353
1355{
1356 ToolBarItem *toolitem = 0;
1357 if (!m_activeList->selectedItems().isEmpty()) {
1358 toolitem = static_cast<ToolBarItem *>(m_activeList->selectedItems().first());
1359 }
1360
1361 m_removeAction->setEnabled(toolitem);
1362 m_changeIconAction->setEnabled(toolitem && !toolitem->isSeparator());
1363
1364 if (toolitem) {
1365 m_upAction->setEnabled(toolitem->index() != 0);
1366 m_downAction->setEnabled(toolitem->index() != toolitem->listWidget()->count() - 1);
1367
1368 QString statusText = toolitem->statusText();
1369 m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText));
1370 } else {
1371 m_upAction->setEnabled(false);
1372 m_downAction->setEnabled(false);
1373 m_helpArea->setText(QString());
1374 }
1375}
1376
1378{
1379 QString internalName = static_cast<ToolBarItem *>(m_inactiveList->currentItem())->internalName();
1380
1382 // we're modified, so let this change
1383 Q_EMIT m_widget->enableOk(true);
1384
1385 slotToolBarSelected(m_toolbarCombo->currentIndex());
1386
1387 selectActiveItem(internalName);
1388}
1389
1390void KisKEditToolBarWidgetPrivate::selectActiveItem(const QString &internalName)
1391{
1392 int activeItemCount = m_activeList->count();
1393 for (int i = 0; i < activeItemCount; i++) {
1394 ToolBarItem *item = static_cast<ToolBarItem *>(m_activeList->item(i));
1395 if (item->internalName() == internalName) {
1396 m_activeList->setCurrentItem(item);
1397 break;
1398 }
1399 }
1400}
1401
1408
1410{
1411 if (!item) {
1412 return;
1413 }
1414
1415 QDomElement new_item;
1416 // let's handle the separator specially
1417 if (item->isSeparator()) {
1418 new_item = m_widget->domDocument().createElement(QStringLiteral("Separator"));
1419 } else {
1420 new_item = m_widget->domDocument().createElement(QStringLiteral("Action"));
1421 }
1422
1423 new_item.setAttribute(QStringLiteral("name"), item->internalName());
1424
1425 Q_ASSERT(!m_currentToolBarElem.isNull());
1426
1427 if (before) {
1428 // we have the item in the active list which is before the new
1429 // item.. so let's try our best to add our new item right after it
1430 QDomElement elem = findElementForToolBarItem(before);
1431 Q_ASSERT(!elem.isNull());
1432 m_currentToolBarElem.insertAfter(new_item, elem);
1433 } else {
1434 // simply put it at the beginning or the end of the list.
1435 if (prepend) {
1436 m_currentToolBarElem.insertBefore(new_item, m_currentToolBarElem.firstChild());
1437 } else {
1438 m_currentToolBarElem.appendChild(new_item);
1439 }
1440 }
1441
1442 // and set this container as a noMerge
1443 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
1444
1445 // update the local doc
1447}
1448
1450{
1451 if (!item) {
1452 return;
1453 }
1454
1455 // we're modified, so let this change
1456 Q_EMIT m_widget->enableOk(true);
1457
1458 // now iterate through to find the child to nuke
1459 QDomElement elem = findElementForToolBarItem(item);
1460 if (!elem.isNull()) {
1461 // nuke myself!
1462 m_currentToolBarElem.removeChild(elem);
1463
1464 // and set this container as a noMerge
1465 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
1466
1467 // update the local doc
1469 }
1470}
1471
1473{
1476
1477 int row = item->listWidget()->row(item) - 1;
1478 // make sure we're not the top item already
1480
1481 // we're modified, so let this change
1482 Q_EMIT m_widget->enableOk(true);
1483
1484 moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(row - 1)));
1485}
1486
1488{
1489 QDomElement e = findElementForToolBarItem(item);
1490
1491 if (e.isNull()) {
1492 return;
1493 }
1494
1495 // remove item
1496 m_activeList->takeItem(m_activeList->row(item));
1497
1498 // put it where it's supposed to go
1499 m_activeList->insertItem(m_activeList->row(before) + 1, item);
1500
1501 // make it selected again
1502 m_activeList->setCurrentItem(item);
1503
1504 // and do the real move in the DOM
1505 if (!before) {
1506 m_currentToolBarElem.insertBefore(e, m_currentToolBarElem.firstChild());
1507 } else {
1509 }
1510
1511 // and set this container as a noMerge
1512 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
1513
1514 // update the local doc
1516}
1517
1519{
1522
1523 // make sure we're not the bottom item already
1524 int newRow = item->listWidget()->row(item) + 1;
1525 KIS_SAFE_ASSERT_RECOVER_RETURN(newRow < item->listWidget()->count());
1526
1527 // we're modified, so let this change
1528 Q_EMIT m_widget->enableOk(true);
1529
1530 moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(newRow)));
1531}
1532
1534{
1535 ToolBarItem *toolitem = m_activeList->currentItem();
1536 KIS_SAFE_ASSERT_RECOVER_RETURN(toolitem && !toolitem->isSeparator());
1537
1538 QDomElement elem = findElementForToolBarItem(toolitem);
1539 KIS_SAFE_ASSERT_RECOVER_RETURN(!elem.isNull());
1540
1542
1543 // Dialog UI
1544 QDialog dialog(m_widget);
1545 dialog.setWindowTitle(i18n("Choose Icon"));
1546 dialog.resize(480, 500);
1547
1548 QVBoxLayout *layout = new QVBoxLayout(&dialog);
1549
1550 // Filter input
1551 QLineEdit *filterEdit = new QLineEdit(&dialog);
1552 filterEdit->setPlaceholderText(i18n("Filter icons..."));
1553 layout->addWidget(filterEdit);
1554
1555 // Icon list
1556 QListWidget *list = new QListWidget(&dialog);
1557 list->setViewMode(QListView::ListMode); // icon + text side by side
1558 list->setIconSize(QSize(22, 22));
1559 list->setResizeMode(QListWidget::Adjust);
1560 list->setSelectionMode(QAbstractItemView::SingleSelection);
1561 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(list);
1562 if (scroller) {
1563 QObject::connect(scroller, &QScroller::stateChanged, m_widget, [&](QScroller::State state) {
1565 });
1566 }
1567
1568 // Fill the list
1569 for (const QString &iconName : loadedIcons) {
1570 QListWidgetItem *item = new QListWidgetItem(
1571 KisIconUtils::loadIcon(iconName),
1572 iconName,
1573 list
1574 );
1575 item->setData(Qt::UserRole, iconName);
1576 }
1577 layout->addWidget(list);
1578
1579 // OK/Cancel buttons
1580 QDialogButtonBox *backButton = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog);
1581 layout->addWidget(backButton);
1582
1583 QObject::connect(backButton, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
1584 QObject::connect(backButton, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
1585
1586 // Filter logic
1587 QObject::connect(filterEdit, &QLineEdit::textChanged, &dialog, [&list](const QString &text) {
1588 for (int i = 0; i < list->count(); ++i) {
1589 QListWidgetItem *item = list->item(i);
1590 bool match = item->text().contains(text, Qt::CaseInsensitive);
1591 item->setHidden(!match);
1592 }
1593 });
1594
1595 // Optional: pre-select current icon
1596 QString currentIconName = elem.attribute(QStringLiteral("icon"));
1597 for (int i = 0; i < list->count(); ++i) {
1598 if (list->item(i)->data(Qt::UserRole).toString() == currentIconName) {
1599 list->setCurrentItem(list->item(i));
1600 break;
1601 }
1602 }
1603
1604 // Execute
1605 if (dialog.exec() == QDialog::Accepted && list->currentItem()) {
1606 QString chosenIconName = list->currentItem()->data(Qt::UserRole).toString();
1607
1608 toolitem->setIcon(KisIconUtils::loadIcon(chosenIconName));
1609
1610 // Update XML in the toolbar DOM
1611 // Ensure (ActionIconOverrides) exist
1612 QDomDocument doc = m_widget->domDocument();
1613 QDomElement root = doc.documentElement();
1614 QDomElement overridesElem = root.namedItem(QStringLiteral("ActionIconOverrides")).toElement();
1615 if (overridesElem.isNull()) {
1616 overridesElem = doc.createElement(QStringLiteral("ActionIconOverrides"));
1617 root.appendChild(overridesElem);
1618 }
1619
1620 // Find Action in overrides or create new
1621 QString actionName = toolitem->internalName();
1622 QDomElement overrideAction;
1623 for (QDomNode child = overridesElem.firstChild(); !child.isNull(); child = child.nextSibling()) {
1624 QDomElement childElement = child.toElement();
1625 if (childElement.attribute(QStringLiteral("name")) == actionName) {
1626 overrideAction = childElement;
1627 break;
1628 }
1629 }
1630 if (overrideAction.isNull()) {
1631 overrideAction = doc.createElement(QStringLiteral("Action"));
1632 overrideAction.setAttribute(QStringLiteral("name"), actionName);
1633 overridesElem.appendChild(overrideAction);
1634 }
1635
1636 // Set icon override
1637 overrideAction.setAttribute(QStringLiteral("icon"), chosenIconName);
1638
1639 // Mark document modified
1640 m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
1643 m_activeList->update();
1644
1645 Q_EMIT m_widget->enableOk(true);
1646 }
1647}
1648
1650{
1652 for (; xit != m_xmlFiles.end(); ++xit) {
1653 if ((*xit).type() == XmlData::Merged) {
1654 continue;
1655 }
1656
1657 if ((*xit).type() == XmlData::Shell ||
1658 (*xit).type() == XmlData::Part) {
1659 if (m_currentXmlData->xmlFile() == (*xit).xmlFile()) {
1660 (*xit).m_isModified = true;
1661 return;
1662 }
1663
1664 continue;
1665 }
1666
1667 (*xit).m_isModified = true;
1668 const QLatin1String attrName("name");
1669 ToolBarList::Iterator it = (*xit).barList().begin();
1670 for (; it != (*xit).barList().end(); ++it) {
1671 QString name((*it).attribute(attrName));
1672 QString tag((*it).tagName());
1673 if ((tag != elem.tagName()) || (name != elem.attribute(attrName))) {
1674 continue;
1675 }
1676
1677 QDomElement toolbar = (*xit).domDocument().documentElement().toElement();
1678 toolbar.replaceChild(elem, (*it));
1679 return;
1680 }
1681
1682 // just append it
1683 QDomElement toolbar = (*xit).domDocument().documentElement().toElement();
1684 Q_ASSERT(!toolbar.isNull());
1685 toolbar.appendChild(elem);
1686 }
1687}
1688
1689
1690void KisKEditToolBarWidgetPrivate::slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList)
1691{
1692 //qDebug() << "slotDropped list=" << (list==m_activeList?"activeList":"inactiveList")
1693 // << "index=" << index << "sourceIsActiveList=" << sourceIsActiveList;
1694 if (list == m_activeList) {
1695 ToolBarItem *after = index > 0 ? static_cast<ToolBarItem *>(list->item(index - 1)) : 0;
1696 //qDebug() << "after" << after->text() << after->internalTag();
1697 if (sourceIsActiveList) {
1698 // has been dragged within the active list (moved).
1699 moveActive(item, after);
1700 } else {
1701 // dragged from the inactive list to the active list
1702 insertActive(item, after, true);
1703 }
1704 } else if (list == m_inactiveList) {
1705 // has been dragged to the inactive list -> remove from the active list.
1706 removeActive(item);
1707 }
1708
1709 delete item; // not needed anymore. must be deleted before slotToolBarSelected clears the lists
1710
1711 // we're modified, so let this change
1712 Q_EMIT m_widget->enableOk(true);
1713
1714 slotToolBarSelected(m_toolbarCombo->currentIndex());
1715}
1716
1717void KisKEditToolBar::showEvent(QShowEvent *event)
1718{
1719 if (!event->spontaneous()) {
1720 // The dialog has been shown, enable toolbar editing
1721 if (d->m_factory) {
1722 // call the xmlgui-factory version
1723 d->m_widget->load(d->m_factory, d->m_defaultToolBar);
1724 } else {
1725 // call the action collection version
1726 d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar);
1727 }
1728
1730 }
1731 QDialog::showEvent(event);
1732}
1733
1734void KisKEditToolBar::hideEvent(QHideEvent *event)
1735{
1736 // The dialog has been hidden, disable toolbar editing
1738
1739 QDialog::hideEvent(event);
1740}
1741
1742#include "moc_kedittoolbar.cpp"
1743#include "moc_kedittoolbar_p.cpp"
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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)