diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22936a3..2426615 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,190 +1,190 @@ add_subdirectory(ksendbugmail) if (HAVE_ATTICA) set (XMLGUI_EXTRA_LIBS ${XMLGUI_EXTRA_LIBS} KF5::Attica) endif () if (HAVE_GLOBALACCEL) set (XMLGUI_EXTRA_LIBS ${XMLGUI_EXTRA_LIBS} KF5::GlobalAccel) endif () if(WIN32) set (XMLGUI_EXTRA_LIBS ${XMLGUI_EXTRA_LIBS} secur32) # GetUserNameEx() endif() configure_file(config-xmlgui.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-xmlgui.h ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/kaboutapplicationconfigattica_p.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kaboutapplicationconfigattica_p.h ) set(kxmlgui_SRCS kaboutapplicationdialog.cpp kaboutapplicationpersonlistdelegate_p.cpp kaboutapplicationpersonlistview_p.cpp kaboutapplicationpersonmodel_p.cpp kaboutkdedialog_p.cpp kactioncategory.cpp kactioncollection.cpp kactionconflictdetector.cpp kbugreport.cpp kedittoolbar.cpp kgesture.cpp kgesturemap.cpp khelpmenu.cpp kkeysequencewidget.cpp kmainwindow.cpp kmenumenuhandler_p.cpp kshortcuteditwidget.cpp kshortcutschemeseditor.cpp kshortcutschemeshelper.cpp kshortcutsdialog.cpp kshortcutseditor.cpp kshortcutseditordelegate.cpp kshortcutseditoritem.cpp kshortcutwidget.cpp kswitchlanguagedialog_p.cpp ktoggletoolbaraction.cpp ktoolbar.cpp ktoolbarhandler.cpp ktoolbarhelper.cpp kxmlguibuilder.cpp kxmlguiclient.cpp kxmlguifactory.cpp kxmlguifactory_p.cpp kxmlguiversionhandler.cpp kxmlguiwindow.cpp kundoactions.cpp kcheckaccelerators.cpp ) if (TARGET Qt5::DBus) list(APPEND kxmlgui_SRCS kmainwindowiface.cpp) endif() # add the resource file qt5_add_resources(kxmlgui_SRCS kxmlgui.qrc) ecm_qt_declare_logging_category(kxmlgui_SRCS HEADER debug.h IDENTIFIER DEBUG_KXMLGUI CATEGORY_NAME kf5.kxmlgui) set(kxmlgui_UI kshortcutsdialog.ui kshortcutwidget.ui ) ki18n_wrap_ui(kxmlgui_SRCS ${kxmlgui_UI} ) add_library(KF5XmlGui ${kxmlgui_SRCS}) ecm_generate_export_header(KF5XmlGui BASE_NAME KXmlGui GROUP_BASE_NAME KF VERSION ${KF5_VERSION} DEPRECATED_BASE_VERSION 0 - DEPRECATION_VERSIONS 4.1 5.0 + DEPRECATION_VERSIONS 4.0 4.1 5.0 EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT} ) add_library(KF5::XmlGui ALIAS KF5XmlGui) target_include_directories(KF5XmlGui INTERFACE "$") target_link_libraries(KF5XmlGui PUBLIC Qt5::Xml #To parse the configuration (QDomDocument etc) Qt5::Widgets #QWidget is used everywhere KF5::ConfigCore #Reading config for ToolbarIcons, Shortcut Schemes... KF5::ConfigWidgets #KStandardAction, KToggleAction... PRIVATE Qt5::Network #QNetworkAccessManager in kaboutapplicationpersonmodel_p Qt5::PrintSupport #QPrinter in kshortcutseditor Qt5::CorePrivate #QSystemLocale in initializeLanguages KF5::CoreAddons #KAboutData KF5::WidgetsAddons KF5::ItemViews #KWidgetItemDelegate in KAboutApplicationPersonListDelegate KF5::I18n #i18n and i18nc in many places KF5::IconThemes #KIconLoader and KIconThemes in KToolBar KF5::TextWidgets #KTextEdit in kbugreport KF5::WindowSystem #KKeyServer in kkeysequencewidget ${XMLGUI_EXTRA_LIBS} ) if (TARGET Qt5::DBus) target_link_libraries(KF5XmlGui PUBLIC Qt5::DBus) #QDBus connect to signal in KToolBar endif() set_target_properties(KF5XmlGui PROPERTIES VERSION ${KXMLGUI_VERSION_STRING} SOVERSION ${KXMLGUI_SOVERSION} EXPORT_NAME XmlGui ) ecm_generate_headers(KXmlGui_HEADERS HEADER_NAMES KAboutApplicationDialog KActionCategory KActionCollection KBugReport KEditToolBar KHelpMenu KKeySequenceWidget KMainWindow KShortcutsDialog KShortcutsEditor KShortcutWidget KToggleToolBarAction KToolBar KXMLGUIBuilder KXMLGUIClient KXMLGUIFactory KXmlGuiWindow KUndoActions REQUIRED_HEADERS KXmlGui_HEADERS ) install(TARGETS KF5XmlGui EXPORT KF5XmlGuiTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES kgesture_p.h kgesturemap_p.h ${CMAKE_CURRENT_BINARY_DIR}/kxmlgui_export.h ${KXmlGui_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KXmlGui COMPONENT Devel ) # install this file to be compatible, it is bundled in the resource file, too install( FILES ui_standards.rc DESTINATION ${KDE_INSTALL_CONFDIR}/ui ) if(BUILD_DESIGNERPLUGIN) add_subdirectory(designer) endif() if (BUILD_QCH) ecm_add_qch( KF5XmlGui_QCH NAME KXmlGui BASE_NAME KF5XmlGui VERSION ${KF5_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${KXmlGui_HEADERS} MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics" LINK_QCHS Qt5Xml_QCH Qt5DBus_QCH Qt5Widgets_QCH KF5Config_QCH KF5ConfigWidgets_QCH INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR} BLANK_MACROS KXMLGUI_EXPORT KXMLGUI_DEPRECATED_EXPORT KXMLGUI_DEPRECATED "KXMLGUI_DEPRECATED_VERSION(x, y, t)" TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KXmlGui LIB_NAME KF5XmlGui DEPS "dbus xml widgets KConfigCore KConfigWidgets" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KXmlGui) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/kedittoolbar.cpp b/src/kedittoolbar.cpp index 2d27dc3..41cb36d 100644 --- a/src/kedittoolbar.cpp +++ b/src/kedittoolbar.cpp @@ -1,1737 +1,1743 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Kurt Granroth Copyright (C) 2006 Hamish Rodda Copyright 2007 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kedittoolbar.h" #include "kedittoolbar_p.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kactioncollection.h" #include "kxmlguifactory.h" #include "ktoolbar.h" #include "ktoolbarhelper_p.h" #include "../kxmlgui_version.h" static const char separatorstring[] = I18N_NOOP("--- separator ---"); static const char spacerstring[] = I18N_NOOP("--- expanding spacer ---"); #define SEPARATORSTRING i18n(separatorstring) #define SPACERSTRING i18n(spacerstring) //static const char *const s_XmlTypeToString[] = { "Shell", "Part", "Local", "Merged" }; typedef QList ToolBarList; namespace KDEPrivate { /** * Return a list of toolbar elements given a toplevel element */ static ToolBarList findToolBars(const QDomElement &start) { ToolBarList list; for (QDomElement elem = start; !elem.isNull(); elem = elem.nextSiblingElement()) { if (elem.tagName() == QLatin1String("ToolBar")) { if (elem.attribute(QStringLiteral("noEdit")) != QLatin1String("true")) { list.append(elem); } } else { if (elem.tagName() != QLatin1String("MenuBar")) { // there are no toolbars inside the menubar :) list += findToolBars(elem.firstChildElement()); // recursive } } } return list; } class XmlData { public: enum XmlType { Shell = 0, Part, Local, Merged }; explicit XmlData(XmlType xmlType, const QString &xmlFile, KActionCollection *collection) : m_isModified(false), m_xmlFile(xmlFile), m_type(xmlType), m_actionCollection(collection) { } void dump() const { #if 0 qDebug(240) << "XmlData" << this << "type" << s_XmlTypeToString[m_type] << "xmlFile:" << m_xmlFile; foreach (const QDomElement &element, m_barList) { qDebug(240) << " ToolBar:" << toolBarText(element); } if (m_actionCollection) { qDebug(240) << " " << m_actionCollection->actions().count() << "actions in the collection."; } else { qDebug(240) << " no action collection."; } #endif } QString xmlFile() const { return m_xmlFile; } XmlType type() const { return m_type; } KActionCollection *actionCollection() const { return m_actionCollection; } void setDomDocument(const QDomDocument &domDoc) { m_document = domDoc.cloneNode().toDocument(); m_barList = findToolBars(m_document.documentElement()); } // Return reference, for e.g. actionPropertiesElement() to modify the document QDomDocument &domDocument() { return m_document; } const QDomDocument &domDocument() const { return m_document; } /** * Return the text (user-visible name) of a given toolbar */ QString toolBarText(const QDomElement &it) const; bool m_isModified; ToolBarList &barList() { return m_barList; } const ToolBarList &barList() const { return m_barList; } private: ToolBarList m_barList; QString m_xmlFile; QDomDocument m_document; XmlType m_type; KActionCollection *m_actionCollection; }; QString XmlData::toolBarText(const QDomElement &it) const { QString name = KToolbarHelper::i18nToolBarName(it); // the name of the toolbar might depend on whether or not // it is in kparts if ((m_type == XmlData::Shell) || (m_type == XmlData::Part)) { QString doc_name(m_document.documentElement().attribute(QStringLiteral("name"))); name += QLatin1String(" <") + doc_name + QLatin1Char('>'); } return name; } typedef QList XmlDataList; class ToolBarItem : public QListWidgetItem { public: ToolBarItem(QListWidget *parent, const QString &tag = QString(), const QString &name = QString(), const QString &statusText = QString()) : QListWidgetItem(parent), m_internalTag(tag), m_internalName(name), m_statusText(statusText), m_isSeparator(false), m_isSpacer(false), m_isTextAlongsideIconHidden(false) { // Drop between items, not onto items setFlags((flags() | Qt::ItemIsDragEnabled) & ~Qt::ItemIsDropEnabled); } void setInternalTag(const QString &tag) { m_internalTag = tag; } void setInternalName(const QString &name) { m_internalName = name; } void setStatusText(const QString &text) { m_statusText = text; } void setSeparator(bool sep) { m_isSeparator = sep; } void setSpacer(bool spacer) { m_isSpacer = spacer; } void setTextAlongsideIconHidden(bool hidden) { m_isTextAlongsideIconHidden = hidden; } QString internalTag() const { return m_internalTag; } QString internalName() const { return m_internalName; } QString statusText() const { return m_statusText; } bool isSeparator() const { return m_isSeparator; } bool isSpacer() const { return m_isSpacer; } bool isTextAlongsideIconHidden() const { return m_isTextAlongsideIconHidden; } int index() const { return listWidget()->row(const_cast(this)); } private: QString m_internalTag; QString m_internalName; QString m_statusText; bool m_isSeparator; bool m_isSpacer; bool m_isTextAlongsideIconHidden; }; static QDataStream &operator<< (QDataStream &s, const ToolBarItem &item) { s << item.internalTag(); s << item.internalName(); s << item.statusText(); s << item.isSeparator(); s << item.isSpacer(); s << item.isTextAlongsideIconHidden(); return s; } static QDataStream &operator>> (QDataStream &s, ToolBarItem &item) { QString internalTag; s >> internalTag; item.setInternalTag(internalTag); QString internalName; s >> internalName; item.setInternalName(internalName); QString statusText; s >> statusText; item.setStatusText(statusText); bool sep; s >> sep; item.setSeparator(sep); bool spacer; s >> spacer; item.setSpacer(spacer); bool hidden; s >> hidden; item.setTextAlongsideIconHidden(hidden); return s; } //// ToolBarListWidget::ToolBarListWidget(QWidget *parent) : QListWidget(parent), m_activeList(true) { setDragDropMode(QAbstractItemView::DragDrop); // no internal moves } QMimeData *ToolBarListWidget::mimeData(const QList items) const { if (items.isEmpty()) { return nullptr; } QMimeData *mimedata = new QMimeData(); QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); // we only support single selection ToolBarItem *item = static_cast(items.first()); stream << *item; } mimedata->setData(QStringLiteral("application/x-kde-action-list"), data); mimedata->setData(QStringLiteral("application/x-kde-source-treewidget"), m_activeList ? "active" : "inactive"); return mimedata; } bool ToolBarListWidget::dropMimeData(int index, const QMimeData *mimeData, Qt::DropAction action) { Q_UNUSED(action) const QByteArray data = mimeData->data(QStringLiteral("application/x-kde-action-list")); if (data.isEmpty()) { return false; } QDataStream stream(data); const bool sourceIsActiveList = mimeData->data(QStringLiteral("application/x-kde-source-treewidget")) == "active"; ToolBarItem *item = new ToolBarItem(this); // needs parent, use this temporarily stream >> *item; emit dropped(this, index, item, sourceIsActiveList); return true; } ToolBarItem *ToolBarListWidget::currentItem() const { return static_cast(QListWidget::currentItem()); } IconTextEditDialog::IconTextEditDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("Change Text")); setModal(true); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); QGridLayout *grid = new QGridLayout; grid->setContentsMargins(0, 0, 0, 0); m_lineEdit = new QLineEdit(this); m_lineEdit->setClearButtonEnabled(true); QLabel *label = new QLabel(i18n("Icon te&xt:"), this); label->setBuddy(m_lineEdit); grid->addWidget(label, 0, 0); grid->addWidget(m_lineEdit, 0, 1); m_cbHidden = new QCheckBox(i18n("&Hide text when toolbar shows text alongside icons"), this); grid->addWidget(m_cbHidden, 1, 1); layout->addLayout(grid); m_buttonBox = new QDialogButtonBox(this); m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); layout->addWidget(m_buttonBox); connect(m_lineEdit, &QLineEdit::textChanged, this, &IconTextEditDialog::slotTextChanged); m_lineEdit->setFocus(); setFixedHeight(sizeHint().height()); } void IconTextEditDialog::setIconText(const QString &text) { m_lineEdit->setText(text); } QString IconTextEditDialog::iconText() const { return m_lineEdit->text().trimmed(); } void IconTextEditDialog::setTextAlongsideIconHidden(bool hidden) { m_cbHidden->setChecked(hidden); } bool IconTextEditDialog::textAlongsideIconHidden() const { return m_cbHidden->isChecked(); } void IconTextEditDialog::slotTextChanged(const QString &text) { // Do not allow empty icon text m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.trimmed().isEmpty()); } class KEditToolBarWidgetPrivate { public: /** * * @param collection In the old-style constructor, this is the collection passed * to the KEditToolBar constructor. * In the xmlguifactory-based constructor, we let KXMLGUIClient create a dummy one, * but it probably isn't used. */ KEditToolBarWidgetPrivate(KEditToolBarWidget *widget, const QString &cName, KActionCollection *collection) : m_collection(collection), m_widget(widget), m_factory(nullptr), m_loadedOnce(false) { m_componentName = cName; m_isPart = false; m_helpArea = nullptr; // We want items with an icon to align with items without icon // So we use an empty QPixmap for that const int iconSize = widget->style()->pixelMetric(QStyle::PM_SmallIconSize); m_emptyIcon = QPixmap(iconSize, iconSize); m_emptyIcon.fill(Qt::transparent); } ~KEditToolBarWidgetPrivate() { } // private slots void slotToolBarSelected(int index); void slotInactiveSelectionChanged(); void slotActiveSelectionChanged(); void slotInsertButton(); void slotRemoveButton(); void slotUpButton(); void slotDownButton(); void selectActiveItem(const QString &); void slotChangeIcon(); void slotChangeIconText(); void slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList); void setupLayout(); void initOldStyle(const QString &file, bool global, const QString &defaultToolbar); void initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolbar); void loadToolBarCombo(const QString &defaultToolbar); void loadActions(const QDomElement &elem); QString xmlFile(const QString &xml_file) const { return xml_file.isEmpty() ? m_componentName + QLatin1String("ui.rc") : xml_file; } /** * Load in the specified XML file and dump the raw xml */ QString loadXMLFile(const QString &_xml_file) { QString raw_xml; QString xml_file = xmlFile(_xml_file); //qCDebug(DEBUG_KXMLGUI) << "loadXMLFile xml_file=" << xml_file; if (!QDir::isRelativePath(xml_file)) { raw_xml = KXMLGUIFactory::readConfigFile(xml_file); } else { raw_xml = KXMLGUIFactory::readConfigFile(xml_file, m_componentName); } return raw_xml; } /** * Look for a given item in the current toolbar */ QDomElement findElementForToolBarItem(const ToolBarItem *item) const { //qDebug(240) << "looking for name=" << item->internalName() << "and tag=" << item->internalTag(); for (QDomNode n = m_currentToolBarElem.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement elem = n.toElement(); if ((elem.attribute(QStringLiteral("name")) == item->internalName()) && (elem.tagName() == item->internalTag())) { return elem; } } //qDebug(240) << "no item found in the DOM with name=" << item->internalName() << "and tag=" << item->internalTag(); return QDomElement(); } void insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend = false); void removeActive(ToolBarItem *item); void moveActive(ToolBarItem *item, ToolBarItem *before); void updateLocal(QDomElement &elem); #ifndef NDEBUG void dump() const { for (const auto &xmlFile : m_xmlFiles) { xmlFile.dump(); } } #endif QComboBox *m_toolbarCombo; QToolButton *m_upAction; QToolButton *m_removeAction; QToolButton *m_insertAction; QToolButton *m_downAction; //QValueList m_actionList; KActionCollection *m_collection; KEditToolBarWidget * const m_widget; KXMLGUIFactory *m_factory; QString m_componentName; QPixmap m_emptyIcon; XmlData *m_currentXmlData; QDomElement m_currentToolBarElem; QString m_xmlFile; QString m_globalFile; QString m_rcFile; QDomDocument m_localDoc; ToolBarList m_barList; ToolBarListWidget *m_inactiveList; ToolBarListWidget *m_activeList; XmlDataList m_xmlFiles; QLabel *m_comboLabel; KSeparator *m_comboSeparator; QLabel *m_helpArea; QPushButton *m_changeIcon; QPushButton *m_changeIconText; bool m_isPart : 1; bool m_loadedOnce : 1; }; } using namespace KDEPrivate; class KEditToolBarPrivate { public: KEditToolBarPrivate(KEditToolBar *q): q(q), m_accept(false), m_global(false), m_collection(nullptr), m_factory(nullptr), m_widget(nullptr) {} void init(); void _k_slotButtonClicked(QAbstractButton *button); void _k_acceptOK(bool); void _k_enableApply(bool); void okClicked(); void applyClicked(); void defaultClicked(); KEditToolBar *q; bool m_accept; // Save parameters for recreating widget after resetting toolbar bool m_global; KActionCollection *m_collection; QString m_file; QString m_defaultToolBar; KXMLGUIFactory *m_factory; KEditToolBarWidget *m_widget; QVBoxLayout *m_layout; QDialogButtonBox *m_buttonBox; }; Q_GLOBAL_STATIC(QString, s_defaultToolBarName) KEditToolBar::KEditToolBar(KActionCollection *collection, QWidget *parent) : QDialog(parent), d(new KEditToolBarPrivate(this)) { d->m_widget = new KEditToolBarWidget(collection, this); d->init(); d->m_collection = collection; } KEditToolBar::KEditToolBar(KXMLGUIFactory *factory, QWidget *parent) : QDialog(parent), d(new KEditToolBarPrivate(this)) { d->m_widget = new KEditToolBarWidget(this); d->init(); d->m_factory = factory; } void KEditToolBarPrivate::init() { m_accept = false; m_factory = nullptr; q->setDefaultToolBar(QString()); q->setWindowTitle(i18n("Configure Toolbars")); q->setModal(false); m_layout = new QVBoxLayout; q->setLayout(m_layout); m_layout->addWidget(m_widget); m_buttonBox = new QDialogButtonBox(q); m_buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); q->connect(m_buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(_k_slotButtonClicked(QAbstractButton*))); QObject::connect(m_buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); m_layout->addWidget(m_buttonBox); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool))); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool))); _k_enableApply(false); q->setMinimumSize(q->sizeHint()); } void KEditToolBar::setResourceFile(const QString &file, bool global) { d->m_file = file; d->m_global = global; d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar); } KEditToolBar::~KEditToolBar() { delete d; s_defaultToolBarName()->clear(); } void KEditToolBar::setDefaultToolBar(const QString &toolBarName) { if (toolBarName.isEmpty()) { d->m_defaultToolBar = *s_defaultToolBarName(); } else { d->m_defaultToolBar = toolBarName; } } void KEditToolBarPrivate::_k_acceptOK(bool b) { m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(b); m_accept = b; } void KEditToolBarPrivate::_k_enableApply(bool b) { m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(b); } void KEditToolBarPrivate::defaultClicked() { 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) { return; } KEditToolBarWidget *oldWidget = m_widget; m_widget = nullptr; m_accept = false; if (m_factory) { const auto clients = m_factory->clients(); for (KXMLGUIClient *client : clients) { const QString file = client->localXMLFile(); if (file.isEmpty()) { continue; } //qDebug(240) << "Deleting local xml file" << file; // << "for client" << client << typeid(*client).name(); if (QFile::exists(file)) if (!QFile::remove(file)) { qCWarning(DEBUG_KXMLGUI) << "Could not delete" << file; } } // Reload the xml files in all clients, now that the local files are gone oldWidget->rebuildKXMLGUIClients(); m_widget = new KEditToolBarWidget(q); m_widget->load(m_factory, m_defaultToolBar); } else { int slash = m_file.lastIndexOf(QLatin1Char('/')) + 1; if (slash) { m_file.remove(0, slash); } const QString xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui5/") + QCoreApplication::instance()->applicationName() + QLatin1Char('/') + m_file; if (QFile::exists(xml_file)) if (!QFile::remove(xml_file)) { qCWarning(DEBUG_KXMLGUI) << "Could not delete " << xml_file; } m_widget = new KEditToolBarWidget(m_collection, q); q->setResourceFile(m_file, m_global); } // Copy the geometry to minimize UI flicker m_widget->setGeometry(oldWidget->geometry()); delete oldWidget; m_layout->insertWidget(0, m_widget); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool))); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool))); _k_enableApply(false); emit q->newToolBarConfig(); +#if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) emit q->newToolbarConfig(); // compat +#endif } void KEditToolBarPrivate::_k_slotButtonClicked(QAbstractButton *button) { QDialogButtonBox::StandardButton type = m_buttonBox->standardButton(button); switch (type) { case QDialogButtonBox::Ok: okClicked(); break; case QDialogButtonBox::Apply: applyClicked(); break; case QDialogButtonBox::RestoreDefaults: defaultClicked(); break; default: break; } } void KEditToolBarPrivate::okClicked() { if (!m_accept) { q->reject(); return; } // Do not rebuild GUI and emit the "newToolBarConfig" signal again here if the "Apply" // button was already pressed and no further changes were made. if (m_buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) { m_widget->save(); emit q->newToolBarConfig(); +#if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) emit q->newToolbarConfig(); // compat +#endif } q->accept(); } void KEditToolBarPrivate::applyClicked() { (void)m_widget->save(); _k_enableApply(false); emit q->newToolBarConfig(); +#if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) emit q->newToolbarConfig(); // compat +#endif } void KEditToolBar::setGlobalDefaultToolBar(const char *toolbarName) { *s_defaultToolBarName() = QString::fromLatin1(toolbarName); } KEditToolBarWidget::KEditToolBarWidget(KActionCollection *collection, QWidget *parent) : QWidget(parent), d(new KEditToolBarWidgetPrivate(this, componentName(), collection)) { d->setupLayout(); } KEditToolBarWidget::KEditToolBarWidget(QWidget *parent) : QWidget(parent), d(new KEditToolBarWidgetPrivate(this, componentName(), KXMLGUIClient::actionCollection() /*create new one*/)) { d->setupLayout(); } KEditToolBarWidget::~KEditToolBarWidget() { delete d; } void KEditToolBarWidget::load(const QString &file, bool global, const QString &defaultToolBar) { d->initOldStyle(file, global, defaultToolBar); } void KEditToolBarWidget::load(KXMLGUIFactory *factory, const QString &defaultToolBar) { d->initFromFactory(factory, defaultToolBar); } void KEditToolBarWidgetPrivate::initOldStyle(const QString &resourceFile, bool global, const QString &defaultToolBar) { //TODO: make sure we can call this multiple times? if (m_loadedOnce) { return; } m_loadedOnce = true; //d->m_actionList = collection->actions(); // handle the merging if (global) { m_widget->loadStandardsXmlFile(); // ui_standards.rc } const QString localXML = loadXMLFile(resourceFile); m_widget->setXML(localXML, global ? true /*merge*/ : false); // first, get all of the necessary info for our local xml XmlData local(XmlData::Local, xmlFile(resourceFile), m_collection); QDomDocument domDoc; domDoc.setContent(localXML); local.setDomDocument(domDoc); m_xmlFiles.append(local); // then, the merged one (ui_standards + local xml) XmlData merge(XmlData::Merged, QString(), m_collection); merge.setDomDocument(m_widget->domDocument()); m_xmlFiles.append(merge); #ifndef NDEBUG dump(); #endif // now load in our toolbar combo box loadToolBarCombo(defaultToolBar); m_widget->adjustSize(); m_widget->setMinimumSize(m_widget->sizeHint()); } void KEditToolBarWidgetPrivate::initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolBar) { //TODO: make sure we can call this multiple times? if (m_loadedOnce) { return; } m_loadedOnce = true; m_factory = factory; // add all of the client data bool first = true; const auto clients = factory->clients(); for (KXMLGUIClient *client : clients) { if (client->xmlFile().isEmpty()) { continue; } XmlData::XmlType type = XmlData::Part; if (first) { type = XmlData::Shell; first = false; Q_ASSERT(!client->localXMLFile().isEmpty()); // where would we save changes?? } XmlData data(type, client->localXMLFile(), client->actionCollection()); QDomDocument domDoc = client->domDocument(); data.setDomDocument(domDoc); m_xmlFiles.append(data); //d->m_actionList += client->actionCollection()->actions(); } #ifndef NDEBUG //d->dump(); #endif // now load in our toolbar combo box loadToolBarCombo(defaultToolBar); m_widget->adjustSize(); m_widget->setMinimumSize(m_widget->sizeHint()); m_widget->actionCollection()->addAssociatedWidget(m_widget); const auto widgetActions = m_widget->actionCollection()->actions(); for (QAction *action : widgetActions) { action->setShortcutContext(Qt::WidgetWithChildrenShortcut); } } void KEditToolBarWidget::save() { //qDebug(240) << "KEditToolBarWidget::save"; for (const auto &xmlFile : qAsConst(d->m_xmlFiles)) { // let's not save non-modified files if (!xmlFile.m_isModified) { continue; } // let's also skip (non-existent) merged files if (xmlFile.type() == XmlData::Merged) { continue; } // Add noMerge="1" to all the menus since we are saving the merged data QDomNodeList menuNodes = xmlFile.domDocument().elementsByTagName(QStringLiteral("Menu")); for (int i = 0; i < menuNodes.length(); ++i) { QDomNode menuNode = menuNodes.item(i); QDomElement menuElement = menuNode.toElement(); if (menuElement.isNull()) { continue; } menuElement.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); } //qCDebug(DEBUG_KXMLGUI) << (*it).domDocument().toString(); //qDebug(240) << "Saving " << (*it).xmlFile(); // if we got this far, we might as well just save it KXMLGUIFactory::saveConfigFile(xmlFile.domDocument(), xmlFile.xmlFile()); } if (!d->m_factory) { return; } rebuildKXMLGUIClients(); } void KEditToolBarWidget::rebuildKXMLGUIClients() { if (!d->m_factory) { return; } const QList clients = d->m_factory->clients(); //qDebug(240) << "factory: " << clients.count() << " clients"; // remove the elements starting from the last going to the first if (clients.isEmpty()) { return; } QListIterator clientIterator = clients; clientIterator.toBack(); while (clientIterator.hasPrevious()) { KXMLGUIClient *client = clientIterator.previous(); //qDebug(240) << "factory->removeClient " << client; d->m_factory->removeClient(client); } KXMLGUIClient *firstClient = clients.first(); // now, rebuild the gui from the first to the last //qDebug(240) << "rebuilding the gui"; for (KXMLGUIClient *client : clients) { //qDebug(240) << "updating client " << client << " " << client->componentName() << " xmlFile=" << client->xmlFile(); QString file(client->xmlFile()); // before setting ui_standards! if (!file.isEmpty()) { // passing an empty stream forces the clients to reread the XML client->setXMLGUIBuildDocument(QDomDocument()); // for the shell, merge in ui_standards.rc if (client == firstClient) { // same assumption as in the ctor: first==shell client->loadStandardsXmlFile(); } // and this forces it to use the *new* XML file client->setXMLFile(file, client == firstClient /* merge if shell */); // [we can't use reloadXML, it doesn't load ui_standards.rc] } } // Now we can add the clients to the factory // We don't do it in the loop above because adding a part automatically // adds its plugins, so we must make sure the plugins were updated first. for (KXMLGUIClient *client : clients) { d->m_factory->addClient(client); } } void KEditToolBarWidgetPrivate::setupLayout() { // the toolbar name combo m_comboLabel = new QLabel(i18n("&Toolbar:"), m_widget); m_toolbarCombo = new QComboBox(m_widget); m_comboLabel->setBuddy(m_toolbarCombo); m_comboSeparator = new KSeparator(m_widget); QObject::connect(m_toolbarCombo, SIGNAL(activated(int)), m_widget, SLOT(slotToolBarSelected(int))); // QPushButton *new_toolbar = new QPushButton(i18n("&New"), this); // new_toolbar->setPixmap(BarIcon("document-new", KIconLoader::SizeSmall)); // new_toolbar->setEnabled(false); // disabled until implemented // QPushButton *del_toolbar = new QPushButton(i18n("&Delete"), this); // del_toolbar->setPixmap(BarIcon("edit-delete", KIconLoader::SizeSmall)); // del_toolbar->setEnabled(false); // disabled until implemented // our list of inactive actions QLabel *inactive_label = new QLabel(i18n("A&vailable actions:"), m_widget); m_inactiveList = new ToolBarListWidget(m_widget); m_inactiveList->setDragEnabled(true); m_inactiveList->setActiveList(false); m_inactiveList->setMinimumSize(180, 250); m_inactiveList->setDropIndicatorShown(false); // #165663 inactive_label->setBuddy(m_inactiveList); QObject::connect(m_inactiveList, SIGNAL(itemSelectionChanged()), m_widget, SLOT(slotInactiveSelectionChanged())); QObject::connect(m_inactiveList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), m_widget, SLOT(slotInsertButton())); QObject::connect(m_inactiveList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)), m_widget, SLOT(slotDropped(ToolBarListWidget*,int,ToolBarItem*,bool))); KListWidgetSearchLine *inactiveListSearchLine = new KListWidgetSearchLine(m_widget, m_inactiveList); inactiveListSearchLine->setPlaceholderText(i18n("Filter")); // our list of active actions QLabel *active_label = new QLabel(i18n("Curr&ent actions:"), m_widget); m_activeList = new ToolBarListWidget(m_widget); m_activeList->setDragEnabled(true); m_activeList->setActiveList(true); // With Qt-4.1 only setting MiniumWidth results in a 0-width icon column ... m_activeList->setMinimumSize(m_inactiveList->minimumWidth(), 100); active_label->setBuddy(m_activeList); QObject::connect(m_activeList, SIGNAL(itemSelectionChanged()), m_widget, SLOT(slotActiveSelectionChanged())); QObject::connect(m_activeList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), m_widget, SLOT(slotRemoveButton())); QObject::connect(m_activeList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)), m_widget, SLOT(slotDropped(ToolBarListWidget*,int,ToolBarItem*,bool))); KListWidgetSearchLine *activeListSearchLine = new KListWidgetSearchLine(m_widget, m_activeList); activeListSearchLine->setPlaceholderText(i18n("Filter")); // "change icon" button m_changeIcon = new QPushButton(i18n("Change &Icon..."), m_widget); m_changeIcon->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-icons"))); m_changeIcon->setEnabled(m_activeList->currentItem()); QObject::connect(m_changeIcon, SIGNAL(clicked()), m_widget, SLOT(slotChangeIcon())); // "change icon text" button m_changeIconText = new QPushButton(i18n("Change Te&xt..."), m_widget); m_changeIconText->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); m_changeIconText->setEnabled(m_activeList->currentItem() != nullptr); QObject::connect(m_changeIconText, SIGNAL(clicked()), m_widget, SLOT(slotChangeIconText())); // The buttons in the middle m_upAction = new QToolButton(m_widget); m_upAction->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); m_upAction->setEnabled(false); m_upAction->setAutoRepeat(true); QObject::connect(m_upAction, SIGNAL(clicked()), m_widget, SLOT(slotUpButton())); m_insertAction = new QToolButton(m_widget); m_insertAction->setIcon(QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("go-previous") : QStringLiteral("go-next"))); m_insertAction->setEnabled(false); QObject::connect(m_insertAction, SIGNAL(clicked()), m_widget, SLOT(slotInsertButton())); m_removeAction = new QToolButton(m_widget); m_removeAction->setIcon(QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("go-next") : QStringLiteral("go-previous"))); m_removeAction->setEnabled(false); QObject::connect(m_removeAction, SIGNAL(clicked()), m_widget, SLOT(slotRemoveButton())); m_downAction = new QToolButton(m_widget); m_downAction->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); m_downAction->setEnabled(false); m_downAction->setAutoRepeat(true); QObject::connect(m_downAction, SIGNAL(clicked()), m_widget, SLOT(slotDownButton())); m_helpArea = new QLabel(m_widget); m_helpArea->setWordWrap(true); // now start with our layouts QVBoxLayout *top_layout = new QVBoxLayout(m_widget); top_layout->setContentsMargins(0, 0, 0, 0); QVBoxLayout *name_layout = new QVBoxLayout(); QHBoxLayout *list_layout = new QHBoxLayout(); QVBoxLayout *inactive_layout = new QVBoxLayout(); QVBoxLayout *active_layout = new QVBoxLayout(); QHBoxLayout *changeIcon_layout = new QHBoxLayout(); QGridLayout *button_layout = new QGridLayout(); name_layout->addWidget(m_comboLabel); name_layout->addWidget(m_toolbarCombo); // name_layout->addWidget(new_toolbar); // name_layout->addWidget(del_toolbar); button_layout->setSpacing(0); button_layout->setRowStretch(0, 10); button_layout->addWidget(m_upAction, 1, 1); button_layout->addWidget(m_removeAction, 2, 0); button_layout->addWidget(m_insertAction, 2, 2); button_layout->addWidget(m_downAction, 3, 1); button_layout->setRowStretch(4, 10); inactive_layout->addWidget(inactive_label); inactive_layout->addWidget(inactiveListSearchLine); inactive_layout->addWidget(m_inactiveList, 1); active_layout->addWidget(active_label); active_layout->addWidget(activeListSearchLine); active_layout->addWidget(m_activeList, 1); active_layout->addLayout(changeIcon_layout); changeIcon_layout->addWidget(m_changeIcon); changeIcon_layout->addStretch(1); changeIcon_layout->addWidget(m_changeIconText); list_layout->addLayout(inactive_layout); list_layout->addLayout(button_layout); list_layout->addLayout(active_layout); top_layout->addLayout(name_layout); top_layout->addWidget(m_comboSeparator); top_layout->addLayout(list_layout, 10); top_layout->addWidget(m_helpArea); top_layout->addWidget(new KSeparator(m_widget)); } void KEditToolBarWidgetPrivate::loadToolBarCombo(const QString &defaultToolBar) { const QLatin1String attrName("name"); // just in case, we clear our combo m_toolbarCombo->clear(); int defaultToolBarId = -1; int count = 0; // load in all of the toolbar names into this combo box for (const auto &xmlFile : qAsConst(m_xmlFiles)) { // skip the merged one in favor of the local one, // so that we can change icons // This also makes the app-defined named for "mainToolBar" appear rather than the ui_standards-defined name. if (xmlFile.type() == XmlData::Merged) { continue; } // each xml file may have any number of toolbars for (const auto &bar : qAsConst(xmlFile.barList())) { const QString text = xmlFile.toolBarText(bar); m_toolbarCombo->addItem(text); const QString name = bar.attribute(attrName); if (defaultToolBarId == -1 && name == defaultToolBar) { defaultToolBarId = count; } count++; } } const bool showCombo = (count > 1); m_comboLabel->setVisible(showCombo); m_comboSeparator->setVisible(showCombo); m_toolbarCombo->setVisible(showCombo); if (defaultToolBarId == -1) { defaultToolBarId = 0; } // we want to the specified item selected and its actions loaded m_toolbarCombo->setCurrentIndex(defaultToolBarId); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBarWidgetPrivate::loadActions(const QDomElement &elem) { const QLatin1String tagSeparator("Separator"); const QLatin1String tagSpacer("Spacer"); const QLatin1String tagMerge("Merge"); const QLatin1String tagActionList("ActionList"); const QLatin1String tagAction("Action"); const QLatin1String attrName("name"); int sep_num = 0; QString sep_name(QStringLiteral("separator_%1")); int spacer_num = 0; QString spacer_name(QStringLiteral("spacer_%1")); // clear our lists m_inactiveList->clear(); m_activeList->clear(); m_insertAction->setEnabled(false); m_removeAction->setEnabled(false); m_upAction->setEnabled(false); m_downAction->setEnabled(false); // We'll use this action collection KActionCollection *actionCollection = m_currentXmlData->actionCollection(); // store the names of our active actions QSet active_list; // Filtering message requested by translators (scripting). KLocalizedString nameFilter = ki18nc("@item:intable Action name in toolbar editor", "%1"); // see if our current action is in this toolbar QDomNode n = elem.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { QDomElement it = n.toElement(); if (it.isNull()) { continue; } if (it.tagName() == tagSeparator) { ToolBarItem *act = new ToolBarItem(m_activeList, tagSeparator, sep_name.arg(sep_num++), QString()); act->setSeparator(true); act->setText(SEPARATORSTRING); it.setAttribute(attrName, act->internalName()); continue; } if (it.tagName() == tagSpacer) { ToolBarItem *act = new ToolBarItem(m_activeList, tagSpacer, spacer_name.arg(spacer_num++), QString()); act->setSpacer(true); act->setText(SPACERSTRING); it.setAttribute(attrName, act->internalName()); continue; } if (it.tagName() == tagMerge) { // Merge can be named or not - use the name if there is one QString name = it.attribute(attrName); ToolBarItem *act = new ToolBarItem(m_activeList, tagMerge, name, i18n("This element will be replaced with all the elements of an embedded component.")); if (name.isEmpty()) { act->setText(i18n("")); } else { act->setText(i18n("", name)); } continue; } if (it.tagName() == tagActionList) { 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.")); act->setText(i18n("ActionList: %1", it.attribute(attrName))); continue; } // iterate through this client's actions // This used to iterate through _all_ actions, but we don't support // putting any action into any client... const auto actions = actionCollection->actions(); for (QAction *action : actions) { // do we have a match? if (it.attribute(attrName) == action->objectName()) { // we have a match! ToolBarItem *act = new ToolBarItem(m_activeList, it.tagName(), action->objectName(), action->toolTip()); act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->iconText())).toString()); act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon); act->setTextAlongsideIconHidden(action->priority() < QAction::NormalPriority); active_list.insert(action->objectName()); break; } } } // go through the rest of the collection const auto actions = actionCollection->actions(); for (QAction *action : actions) { // skip our active ones if (active_list.contains(action->objectName())) { continue; } ToolBarItem *act = new ToolBarItem(m_inactiveList, tagAction, action->objectName(), action->toolTip()); act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->text())).toString()); act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon); } m_inactiveList->sortItems(Qt::AscendingOrder); // finally, add default separators and spacers to the inactive list ToolBarItem *sep = new ToolBarItem(nullptr, tagSeparator, sep_name.arg(sep_num++), QString()); sep->setSeparator(true); sep->setText(SEPARATORSTRING); m_inactiveList->insertItem(0, sep); ToolBarItem *spacer = new ToolBarItem(nullptr, tagSpacer, spacer_name.arg(spacer_num++), QString()); spacer->setSpacer(true); spacer->setText(SPACERSTRING); m_inactiveList->insertItem(1, spacer); } KActionCollection *KEditToolBarWidget::actionCollection() const { return d->m_collection; } void KEditToolBarWidgetPrivate::slotToolBarSelected(int index) { // We need to find the XmlData and toolbar element for this index // To do that, we do the same iteration as the one which filled in the combobox. int toolbarNumber = 0; for (auto &xmlFile : m_xmlFiles) { // skip the merged one in favor of the local one, // so that we can change icons if (xmlFile.type() == XmlData::Merged) { continue; } // each xml file may have any number of toolbars for (const auto &bar : xmlFile.barList()) { // is this our toolbar? if (toolbarNumber == index) { // save our current settings m_currentXmlData = &xmlFile; m_currentToolBarElem = bar; //qCDebug(DEBUG_KXMLGUI) << "found toolbar" << m_currentXmlData->toolBarText(*it) << "m_currentXmlData set to"; m_currentXmlData->dump(); // If this is a Merged xmldata, clicking the "change icon" button would assert... Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); // load in our values loadActions(m_currentToolBarElem); if (xmlFile.type() == XmlData::Part || xmlFile.type() == XmlData::Shell) { m_widget->setDOMDocument(xmlFile.domDocument()); } return; } ++toolbarNumber; } } } void KEditToolBarWidgetPrivate::slotInactiveSelectionChanged() { if (!m_inactiveList->selectedItems().isEmpty()) { m_insertAction->setEnabled(true); QString statusText = static_cast(m_inactiveList->selectedItems().first())->statusText(); m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText)); } else { m_insertAction->setEnabled(false); m_helpArea->setText(QString()); } } void KEditToolBarWidgetPrivate::slotActiveSelectionChanged() { ToolBarItem *toolitem = nullptr; if (!m_activeList->selectedItems().isEmpty()) { toolitem = static_cast(m_activeList->selectedItems().first()); } m_removeAction->setEnabled(toolitem); m_changeIcon->setEnabled(toolitem && toolitem->internalTag() == QLatin1String("Action")); m_changeIconText->setEnabled(toolitem && toolitem->internalTag() == QLatin1String("Action")); if (toolitem) { m_upAction->setEnabled(toolitem->index() != 0); m_downAction->setEnabled(toolitem->index() != toolitem->listWidget()->count() - 1); QString statusText = toolitem->statusText(); m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText)); } else { m_upAction->setEnabled(false); m_downAction->setEnabled(false); m_helpArea->setText(QString()); } } void KEditToolBarWidgetPrivate::slotInsertButton() { QString internalName = static_cast(m_inactiveList->currentItem())->internalName(); insertActive(m_inactiveList->currentItem(), m_activeList->currentItem(), false); // we're modified, so let this change emit m_widget->enableOk(true); slotToolBarSelected(m_toolbarCombo->currentIndex()); selectActiveItem(internalName); } void KEditToolBarWidgetPrivate::selectActiveItem(const QString &internalName) { int activeItemCount = m_activeList->count(); for (int i = 0; i < activeItemCount; i++) { ToolBarItem *item = static_cast(m_activeList->item(i)); if (item->internalName() == internalName) { m_activeList->setCurrentItem(item); break; } } } void KEditToolBarWidgetPrivate::slotRemoveButton() { removeActive(m_activeList->currentItem()); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBarWidgetPrivate::insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend) { if (!item) { return; } QDomElement new_item; // let's handle the separator and spacer specially if (item->isSeparator()) { new_item = m_widget->domDocument().createElement(QStringLiteral("Separator")); } else if (item->isSpacer()) { new_item = m_widget->domDocument().createElement(QStringLiteral("Spacer")); } else { new_item = m_widget->domDocument().createElement(QStringLiteral("Action")); } new_item.setAttribute(QStringLiteral("name"), item->internalName()); Q_ASSERT(!m_currentToolBarElem.isNull()); if (before) { // we have the item in the active list which is before the new // item.. so let's try our best to add our new item right after it QDomElement elem = findElementForToolBarItem(before); Q_ASSERT(!elem.isNull()); m_currentToolBarElem.insertAfter(new_item, elem); } else { // simply put it at the beginning or the end of the list. if (prepend) { m_currentToolBarElem.insertBefore(new_item, m_currentToolBarElem.firstChild()); } else { m_currentToolBarElem.appendChild(new_item); } } // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); // update the local doc updateLocal(m_currentToolBarElem); } void KEditToolBarWidgetPrivate::removeActive(ToolBarItem *item) { if (!item) { return; } // we're modified, so let this change emit m_widget->enableOk(true); // now iterate through to find the child to nuke QDomElement elem = findElementForToolBarItem(item); if (!elem.isNull()) { // nuke myself! m_currentToolBarElem.removeChild(elem); // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); // update the local doc updateLocal(m_currentToolBarElem); } } void KEditToolBarWidgetPrivate::slotUpButton() { ToolBarItem *item = m_activeList->currentItem(); if (!item) { Q_ASSERT(false); return; } int row = item->listWidget()->row(item) - 1; // make sure we're not the top item already if (row < 0) { Q_ASSERT(false); return; } // we're modified, so let this change emit m_widget->enableOk(true); moveActive(item, static_cast(item->listWidget()->item(row - 1))); } void KEditToolBarWidgetPrivate::moveActive(ToolBarItem *item, ToolBarItem *before) { QDomElement e = findElementForToolBarItem(item); if (e.isNull()) { return; } // remove item m_activeList->takeItem(m_activeList->row(item)); // put it where it's supposed to go m_activeList->insertItem(m_activeList->row(before) + 1, item); // make it selected again m_activeList->setCurrentItem(item); // and do the real move in the DOM if (!before) { m_currentToolBarElem.insertBefore(e, m_currentToolBarElem.firstChild()); } else { m_currentToolBarElem.insertAfter(e, findElementForToolBarItem(before)); } // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); // update the local doc updateLocal(m_currentToolBarElem); } void KEditToolBarWidgetPrivate::slotDownButton() { ToolBarItem *item = m_activeList->currentItem(); if (!item) { Q_ASSERT(false); return; } // make sure we're not the bottom item already int newRow = item->listWidget()->row(item) + 1; if (newRow >= item->listWidget()->count()) { Q_ASSERT(false); return; } // we're modified, so let this change emit m_widget->enableOk(true); moveActive(item, static_cast(item->listWidget()->item(newRow))); } void KEditToolBarWidgetPrivate::updateLocal(QDomElement &elem) { for (auto &xmlFile : m_xmlFiles) { if (xmlFile.type() == XmlData::Merged) { continue; } if (xmlFile.type() == XmlData::Shell || xmlFile.type() == XmlData::Part) { if (m_currentXmlData->xmlFile() == xmlFile.xmlFile()) { xmlFile.m_isModified = true; return; } continue; } xmlFile.m_isModified = true; const QLatin1String attrName("name"); for (const auto &bar : qAsConst(xmlFile.barList())) { const QString name(bar.attribute(attrName)); const QString tag(bar.tagName()); if ((tag != elem.tagName()) || (name != elem.attribute(attrName))) { continue; } QDomElement toolbar = xmlFile.domDocument().documentElement().toElement(); toolbar.replaceChild(elem, bar); return; } // just append it QDomElement toolbar = xmlFile.domDocument().documentElement().toElement(); Q_ASSERT(!toolbar.isNull()); toolbar.appendChild(elem); } } void KEditToolBarWidgetPrivate::slotChangeIcon() { m_currentXmlData->dump(); Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); QString icon = KIconDialog::getIcon(KIconLoader::Toolbar, KIconLoader::Action, false, 0, false, // all defaults m_widget, i18n("Change Icon")); if (icon.isEmpty()) { return; } ToolBarItem *item = m_activeList->currentItem(); //qCDebug(DEBUG_KXMLGUI) << item; if (item) { item->setIcon(QIcon::fromTheme(icon)); m_currentXmlData->m_isModified = true; // Get hold of ActionProperties tag QDomElement elem = KXMLGUIFactory::actionPropertiesElement(m_currentXmlData->domDocument()); // Find or create an element for this action QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, item->internalName(), true /*create*/); Q_ASSERT(!act_elem.isNull()); act_elem.setAttribute(QStringLiteral("icon"), icon); // we're modified, so let this change emit m_widget->enableOk(true); } } void KEditToolBarWidgetPrivate::slotChangeIconText() { m_currentXmlData->dump(); ToolBarItem *item = m_activeList->currentItem(); if (item) { QString iconText = item->text(); bool hidden = item->isTextAlongsideIconHidden(); IconTextEditDialog dialog(m_widget); dialog.setIconText(iconText); dialog.setTextAlongsideIconHidden(hidden); bool ok = dialog.exec() == QDialog::Accepted; iconText = dialog.iconText(); hidden = dialog.textAlongsideIconHidden(); bool hiddenChanged = hidden != item->isTextAlongsideIconHidden(); bool iconTextChanged = iconText != item->text(); if (!ok || (!hiddenChanged && !iconTextChanged)) { return; } item->setText(iconText); item->setTextAlongsideIconHidden(hidden); Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); m_currentXmlData->m_isModified = true; // Get hold of ActionProperties tag QDomElement elem = KXMLGUIFactory::actionPropertiesElement(m_currentXmlData->domDocument()); // Find or create an element for this action QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, item->internalName(), true /*create*/); Q_ASSERT(!act_elem.isNull()); if (iconTextChanged) { act_elem.setAttribute(QStringLiteral("iconText"), iconText); } if (hiddenChanged) { act_elem.setAttribute(QStringLiteral("priority"), hidden ? QAction::LowPriority : QAction::NormalPriority); } // we're modified, so let this change emit m_widget->enableOk(true); } } void KEditToolBarWidgetPrivate::slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList) { //qCDebug(DEBUG_KXMLGUI) << "slotDropped list=" << (list==m_activeList?"activeList":"inactiveList") // << "index=" << index << "sourceIsActiveList=" << sourceIsActiveList; if (list == m_activeList) { ToolBarItem *after = index > 0 ? static_cast(list->item(index - 1)) : nullptr; //qCDebug(DEBUG_KXMLGUI) << "after" << after->text() << after->internalTag(); if (sourceIsActiveList) { // has been dragged within the active list (moved). moveActive(item, after); } else { // dragged from the inactive list to the active list insertActive(item, after, true); } } else if (list == m_inactiveList) { // has been dragged to the inactive list -> remove from the active list. removeActive(item); } delete item; // not needed anymore. must be deleted before slotToolBarSelected clears the lists // we're modified, so let this change emit m_widget->enableOk(true); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBar::showEvent(QShowEvent *event) { if (!event->spontaneous()) { // The dialog has been shown, enable toolbar editing if (d->m_factory) { // call the xmlgui-factory version d->m_widget->load(d->m_factory, d->m_defaultToolBar); } else { // call the action collection version d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar); } KToolBar::setToolBarsEditable(true); } QDialog::showEvent(event); } void KEditToolBar::hideEvent(QHideEvent *event) { // The dialog has been hidden, disable toolbar editing KToolBar::setToolBarsEditable(false); QDialog::hideEvent(event); } #include "moc_kedittoolbar.cpp" #include "moc_kedittoolbar_p.cpp" diff --git a/src/kedittoolbar.h b/src/kedittoolbar.h index ee39230..e3b44a0 100644 --- a/src/kedittoolbar.h +++ b/src/kedittoolbar.h @@ -1,175 +1,181 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Kurt Granroth Copyright (C) 2006 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KEDITTOOLBAR_H #define KEDITTOOLBAR_H #include #include class KActionCollection; class KEditToolBarPrivate; class KXMLGUIFactory; /** * @class KEditToolBar kedittoolbar.h KEditToolBar * * @short A dialog used to customize or configure toolbars. * * This dialog only works if your application uses the XML UI * framework for creating menus and toolbars. It depends on the XML * files to describe the toolbar layouts and it requires the actions * to determine which buttons are active. * * Typically you do not need to use it directly as KXmlGuiWindow::setupGUI * takes care of it. * * If you use KXMLGUIClient::plugActionList() you need to overload * KXmlGuiWindow::saveNewToolbarConfig() to plug actions again: * * \code * void MyClass::saveNewToolbarConfig() * { * KXmlGuiWindow::saveNewToolbarConfig(); * plugActionList( "list1", list1Actions ); * plugActionList( "list2", list2Actions ); * } * \endcode * * When created, KEditToolBar takes a KXMLGUIFactory object, and uses it to * find all of the action collections and XML files (there is one of each for the * mainwindow, but there could be more, when adding other XMLGUI clients like * KParts or plugins). The editor aims to be semi-intelligent about where it * assigns any modifications. In other words, it will not write out part specific * changes to your application's main XML file. * * KXmlGuiWindow and KParts::MainWindow take care of creating KEditToolBar correctly * and connecting to its newToolBarConfig slot, but if you really really want to do it * yourself, see the KXmlGuiWindow::configureToolbars() and KXmlGuiWindow::saveNewToolbarConfig() code. * * \image html kedittoolbar.png "KEditToolBar (example: usage in KWrite)" * * @author Kurt Granroth * @maintainer David Faure */ class KXMLGUI_EXPORT KEditToolBar : public QDialog { Q_OBJECT public: /** * Old constructor for apps that do not use components. * This constructor is somewhat deprecated, since it doesn't work * with any KXMLGuiClient being added to the mainwindow. * You really want to use the other constructor. * * You @em must pass along your collection of actions (some of which appear in your toolbars). * * @param collection The collection of actions to work on. * @param parent The parent of the dialog. */ explicit KEditToolBar(KActionCollection *collection, QWidget *parent = nullptr); /** * Main constructor. * * The main parameter, @p factory, is a pointer to the * XML GUI factory object for your application. It contains a list * of all of the GUI clients (along with the action collections and * xml files) and the toolbar editor uses that. * * Use this like so: * \code * KEditToolBar edit(factory()); * if (edit.exec()) * ... * \endcode * * @param factory Your application's factory object * @param parent The usual parent for the dialog. */ explicit KEditToolBar(KXMLGUIFactory *factory, QWidget *parent = nullptr); /// destructor ~KEditToolBar() override; /** * Sets the default toolbar that will be selected when the dialog is shown. * If not set, or QString() is passed in, the global default tool bar name * will be used. * @param toolBarName the name of the tool bar * @see setGlobalDefaultToolBar */ void setDefaultToolBar(const QString &toolBarName); /** * The name (absolute or relative) of your application's UI resource file * is assumed to be share/apps/appname/appnameui.rc though this can be * overridden by calling this method. * * The global parameter controls whether or not the * global resource file is used. If this is @c true, then you may * edit all of the actions in your toolbars -- global ones and * local one. If it is @c false, then you may edit only your * application's entries. The only time you should set this to * false is if your application does not use the global resource * file at all (very rare). * * @param file The application's local resource file. * @param global If @c true, then the global resource file will also * be parsed. */ void setResourceFile(const QString &file, bool global = true); /** * Sets the default toolbar which will be auto-selected for all * KEditToolBar instances. Can be overridden on a per-dialog basis * by calling setDefaultToolBar( const QString& ) on the dialog. * * @param toolBarName the name of the tool bar */ static void setGlobalDefaultToolBar(const char *toolBarName); // TODO should be const QString& Q_SIGNALS: /** * Signal emitted when 'apply' or 'ok' is clicked or toolbars were reset. * Connect to it, to plug action lists and to call applyMainWindowSettings * (see sample code in this class's documentation) */ void newToolBarConfig(); +#if KXMLGUI_ENABLE_DEPRECATED_SINCE(4, 0) + /** + * @deprecated Since 4.0, use newToolBarConfig() + */ + KXMLGUI_DEPRECATED_VERSION(4, 0, "Use KEditToolBar::newToolBarConfig()") QT_MOC_COMPAT void newToolbarConfig(); +#endif protected: void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; private: friend class KEditToolBarPrivate; KEditToolBarPrivate *const d; Q_PRIVATE_SLOT(d, void _k_slotButtonClicked(QAbstractButton *)) Q_PRIVATE_SLOT(d, void _k_acceptOK(bool)) Q_PRIVATE_SLOT(d, void _k_enableApply(bool)) Q_DISABLE_COPY(KEditToolBar) }; #endif // _KEDITTOOLBAR_H