KXmlGui

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

KDE's Doxygen guidelines are available online.