diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch) | |
tree | 5ac38a06f3dde268dc7927dc155896926aaf7012 /kdeui/kedittoolbar.cpp | |
download | tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kdeui/kedittoolbar.cpp')
-rw-r--r-- | kdeui/kedittoolbar.cpp | 1455 |
1 files changed, 1455 insertions, 0 deletions
diff --git a/kdeui/kedittoolbar.cpp b/kdeui/kedittoolbar.cpp new file mode 100644 index 000000000..09fa3dee0 --- /dev/null +++ b/kdeui/kedittoolbar.cpp @@ -0,0 +1,1455 @@ +// -*- mode: c++; c-basic-offset: 2 -*- +/* This file is part of the KDE libraries + Copyright (C) 2000 Kurt Granroth <granroth@kde.org> + + 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 <qdom.h> +#include <qlayout.h> +#include <qdir.h> +#include <qfile.h> +#include <qheader.h> +#include <qcombobox.h> +#include <qdragobject.h> +#include <qtoolbutton.h> +#include <qlabel.h> +#include <qvaluelist.h> +#include <qapplication.h> +#include <qtextstream.h> + +#include <kaction.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include <kicontheme.h> +#include <kiconloader.h> +#include <kinstance.h> +#include <kmessagebox.h> +#include <kxmlguifactory.h> +#include <kseparator.h> +#include <kconfig.h> +#include <klistview.h> +#include <kdebug.h> +#include <kpushbutton.h> +#include <kprocio.h> + +static const char * const lineseparatorstring = I18N_NOOP("--- line separator ---"); +static const char * const separatorstring = I18N_NOOP("--- separator ---"); + +#define LINESEPARATORSTRING i18n(lineseparatorstring) +#define SEPARATORSTRING i18n(separatorstring) + +static void dump_xml(const QDomDocument& doc) +{ + QString str; + QTextStream ts(&str, IO_WriteOnly); + ts << doc; + kdDebug() << str << endl; +} + +typedef QValueList<QDomElement> ToolbarList; + +namespace +{ +class XmlData +{ +public: + enum XmlType { Shell = 0, Part, Local, Merged }; + XmlData() + { + m_isModified = false; + m_actionCollection = 0; + } + + QString m_xmlFile; + QDomDocument m_document; + XmlType m_type; + bool m_isModified; + KActionCollection* m_actionCollection; + + ToolbarList m_barList; +}; + +typedef QValueList<XmlData> XmlDataList; + +class ToolbarItem : public QListViewItem +{ +public: + ToolbarItem(KListView *parent, const QString& tag = QString::null, const QString& name = QString::null, const QString& statusText = QString::null) + : QListViewItem(parent), + m_tag(tag), + m_name(name), + m_statusText(statusText) + { + } + + ToolbarItem(KListView *parent, QListViewItem *item, const QString &tag = QString::null, const QString& name = QString::null, const QString& statusText = QString::null) + : QListViewItem(parent, item), + m_tag(tag), + m_name(name), + m_statusText(statusText) + { + } + + virtual QString key(int column, bool) const + { + QString s = text( column ); + if ( s == LINESEPARATORSTRING ) + return "0"; + if ( s == SEPARATORSTRING ) + return "1"; + return "2" + s; + } + + void setInternalTag(const QString &tag) { m_tag = tag; } + void setInternalName(const QString &name) { m_name = name; } + void setStatusText(const QString &text) { m_statusText = text; } + QString internalTag() const { return m_tag; } + QString internalName() const { return m_name; } + QString statusText() const { return m_statusText; } +private: + QString m_tag; + QString m_name; + QString m_statusText; +}; + +#define TOOLBARITEMMIMETYPE "data/x-kde.toolbar.item" +class ToolbarItemDrag : public QStoredDrag +{ +public: + ToolbarItemDrag(ToolbarItem *toolbarItem, + QWidget *dragSource = 0, const char *name = 0) + : QStoredDrag( TOOLBARITEMMIMETYPE, dragSource, name ) + { + if (toolbarItem) { + QByteArray data; + QDataStream out(data, IO_WriteOnly); + out << toolbarItem->internalTag(); + out << toolbarItem->internalName(); + out << toolbarItem->statusText(); + out << toolbarItem->text(1); // separators need this. + setEncodedData(data); + } + } + + static bool canDecode(QMimeSource* e) + { + return e->provides(TOOLBARITEMMIMETYPE); + } + + static bool decode( const QMimeSource* e, ToolbarItem& item ) + { + if (!e) + return false; + + QByteArray data = e->encodedData(TOOLBARITEMMIMETYPE); + if ( data.isEmpty() ) + return false; + + QString internalTag, internalName, statusText, text; + QDataStream in(data, IO_ReadOnly); + in >> internalTag; + in >> internalName; + in >> statusText; + in >> text; + + item.setInternalTag( internalTag ); + item.setInternalName( internalName ); + item.setStatusText( statusText ); + item.setText(1, text); + + return true; + } +}; + +class ToolbarListView : public KListView +{ +public: + ToolbarListView(QWidget *parent=0, const char *name=0) + : KListView(parent, name) + { + } +protected: + virtual QDragObject *dragObject() + { + ToolbarItem *item = dynamic_cast<ToolbarItem*>(selectedItem()); + if ( item ) { + ToolbarItemDrag *obj = new ToolbarItemDrag(item, + this, "ToolbarAction drag item"); + const QPixmap *pm = item->pixmap(0); + if( pm ) + obj->setPixmap( *pm ); + return obj; + } + return 0; + } + + virtual bool acceptDrag(QDropEvent *event) const + { + return ToolbarItemDrag::canDecode( event ); + } +}; +} // namespace + +class KEditToolbarWidgetPrivate +{ +public: + /** + * @param instance The instance. + * @param collection In a non-KParts application, this is the collection passed + * to the KEditToolbar constructor. + * In a KParts application we let create a KXMLGUIClient create a dummy one, + * but it probably isn't used. + */ + KEditToolbarWidgetPrivate(KInstance *instance, KActionCollection* collection) + : m_collection( collection ) + { + m_instance = instance; + m_isPart = false; + m_helpArea = 0L; + m_kdialogProcess = 0; + } + ~KEditToolbarWidgetPrivate() + { + } + + QString xmlFile(const QString& xml_file) + { + return xml_file.isNull() ? QString(m_instance->instanceName()) + "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); + //kdDebug() << "loadXMLFile xml_file=" << xml_file << endl; + + if ( !QDir::isRelativePath(xml_file) ) + raw_xml = KXMLGUIFactory::readConfigFile(xml_file); + else + raw_xml = KXMLGUIFactory::readConfigFile(xml_file, m_instance); + + return raw_xml; + } + + /** + * Return a list of toolbar elements given a toplevel element + */ + ToolbarList findToolbars(QDomNode n) + { + static const QString &tagToolbar = KGlobal::staticQString( "ToolBar" ); + static const QString &attrNoEdit = KGlobal::staticQString( "noEdit" ); + ToolbarList list; + + for( ; !n.isNull(); n = n.nextSibling() ) + { + QDomElement elem = n.toElement(); + if (elem.isNull()) + continue; + + if (elem.tagName() == tagToolbar && elem.attribute( attrNoEdit ) != "true" ) + list.append(elem); + + list += findToolbars(elem.firstChild()); + } + + return list; + } + + /** + * Return the name of a given toolbar + */ + QString toolbarName( const XmlData& xmlData, const QDomElement& it ) const + { + static const QString &tagText = KGlobal::staticQString( "text" ); + static const QString &tagText2 = KGlobal::staticQString( "Text" ); + static const QString &attrName = KGlobal::staticQString( "name" ); + + QString name; + QCString txt( it.namedItem( tagText ).toElement().text().utf8() ); + if ( txt.isEmpty() ) + txt = it.namedItem( tagText2 ).toElement().text().utf8(); + if ( txt.isEmpty() ) + name = it.attribute( attrName ); + else + name = i18n( txt ); + + // the name of the toolbar might depend on whether or not + // it is in kparts + if ( ( xmlData.m_type == XmlData::Shell ) || + ( xmlData.m_type == XmlData::Part ) ) + { + QString doc_name(xmlData.m_document.documentElement().attribute( attrName )); + name += " <" + doc_name + ">"; + } + return name; + } + /** + * Look for a given item in the current toolbar + */ + QDomElement findElementForToolbarItem( const ToolbarItem* item ) const + { + static const QString &attrName = KGlobal::staticQString( "name" ); + for(QDomNode n = m_currentToolbarElem.firstChild(); !n.isNull(); n = n.nextSibling()) + { + QDomElement elem = n.toElement(); + if ((elem.attribute(attrName) == item->internalName()) && + (elem.tagName() == item->internalTag())) + return elem; + } + return QDomElement(); + } + +#ifndef NDEBUG + void dump() + { + static const char* s_XmlTypeToString[] = { "Shell", "Part", "Local", "Merged" }; + XmlDataList::Iterator xit = m_xmlFiles.begin(); + for ( ; xit != m_xmlFiles.end(); ++xit ) + { + kdDebug(240) << "XmlData type " << s_XmlTypeToString[(*xit).m_type] << " xmlFile: " << (*xit).m_xmlFile << endl; + for( QValueList<QDomElement>::Iterator it = (*xit).m_barList.begin(); + it != (*xit).m_barList.end(); ++it ) { + kdDebug(240) << " Toolbar: " << toolbarName( *xit, *it ) << endl; + } + if ( (*xit).m_actionCollection ) + kdDebug(240) << " " << (*xit).m_actionCollection->count() << " actions in the collection." << endl; + else + kdDebug(240) << " no action collection." << endl; + } + } +#endif + + //QValueList<KAction*> m_actionList; + KActionCollection* m_collection; + KInstance *m_instance; + + XmlData* m_currentXmlData; + QDomElement m_currentToolbarElem; + + QString m_xmlFile; + QString m_globalFile; + QString m_rcFile; + QDomDocument m_localDoc; + bool m_isPart; + + ToolbarList m_barList; + + XmlDataList m_xmlFiles; + + QLabel *m_comboLabel; + KSeparator *m_comboSeparator; + QLabel * m_helpArea; + KPushButton* m_changeIcon; + KProcIO* m_kdialogProcess; + bool m_hasKDialog; +}; + +class KEditToolbarPrivate { +public: + bool m_accept; + + // Save parameters for recreating widget after resetting toolbar + bool m_global; + KActionCollection* m_collection; + QString m_file; + KXMLGUIFactory* m_factory; +}; + +const char *KEditToolbar::s_defaultToolbar = 0L; + +KEditToolbar::KEditToolbar(KActionCollection *collection, const QString& file, + bool global, QWidget* parent, const char* name) + : KDialogBase(Swallow, i18n("Configure Toolbars"), Default|Ok|Apply|Cancel, Ok, parent, name), + m_widget(new KEditToolbarWidget(QString::fromLatin1(s_defaultToolbar), collection, file, global, this)) +{ + init(); + d->m_global = global; + d->m_collection = collection; + d->m_file = file; +} + +KEditToolbar::KEditToolbar(const QString& defaultToolbar, KActionCollection *collection, + const QString& file, bool global, + QWidget* parent, const char* name) + : KDialogBase(Swallow, i18n("Configure Toolbars"), Default|Ok|Apply|Cancel, Ok, parent, name), + m_widget(new KEditToolbarWidget(defaultToolbar, collection, file, global, this)) +{ + init(); + d->m_global = global; + d->m_collection = collection; + d->m_file = file; +} + +KEditToolbar::KEditToolbar(KXMLGUIFactory* factory, QWidget* parent, const char* name) + : KDialogBase(Swallow, i18n("Configure Toolbars"), Default|Ok|Apply|Cancel, Ok, parent, name), + m_widget(new KEditToolbarWidget(QString::fromLatin1(s_defaultToolbar), factory, this)) +{ + init(); + d->m_factory = factory; +} + +KEditToolbar::KEditToolbar(const QString& defaultToolbar,KXMLGUIFactory* factory, + QWidget* parent, const char* name) + : KDialogBase(Swallow, i18n("Configure Toolbars"), Default|Ok|Apply|Cancel, Ok, parent, name), + m_widget(new KEditToolbarWidget(defaultToolbar, factory, this)) +{ + init(); + d->m_factory = factory; +} + +void KEditToolbar::init() +{ + d = new KEditToolbarPrivate(); + d->m_accept = false; + d->m_factory = 0; + + setMainWidget(m_widget); + + connect(m_widget, SIGNAL(enableOk(bool)), SLOT(acceptOK(bool))); + connect(m_widget, SIGNAL(enableOk(bool)), SLOT(enableButtonApply(bool))); + enableButtonApply(false); + + setMinimumSize(sizeHint()); + s_defaultToolbar = 0L; +} + +KEditToolbar::~KEditToolbar() +{ + delete d; +} + +void KEditToolbar::acceptOK(bool b) +{ + enableButtonOK(b); + d->m_accept = b; +} + +void KEditToolbar::slotDefault() +{ + if ( KMessageBox::warningContinueCancel(this, i18n("Do you really want to reset all toolbars of this application to their default? The changes will be applied immediately."), i18n("Reset Toolbars"),i18n("Reset"))!=KMessageBox::Continue ) + return; + + delete m_widget; + d->m_accept = false; + + if ( d->m_factory ) + { + const QString localPrefix = locateLocal("data", ""); + QPtrList<KXMLGUIClient> clients(d->m_factory->clients()); + QPtrListIterator<KXMLGUIClient> it( clients ); + + for( ; it.current(); ++it) + { + KXMLGUIClient *client = it.current(); + QString file = client->xmlFile(); + + if (file.isNull()) + continue; + + if (QDir::isRelativePath(file)) + { + const KInstance *instance = client->instance() ? client->instance() : KGlobal::instance(); + file = locateLocal("data", QString::fromLatin1( instance->instanceName() + '/' ) + file); + } + else + { + if (!file.startsWith(localPrefix)) + continue; + } + + if ( QFile::exists( file ) ) + if ( !QFile::remove( file ) ) + kdWarning() << "Could not delete " << file << endl; + } + + m_widget = new KEditToolbarWidget(QString::null, d->m_factory, this); + m_widget->rebuildKXMLGUIClients(); + } + else + { + int slash = d->m_file.findRev('/')+1; + if (slash) + d->m_file = d->m_file.mid(slash); + QString xml_file = locateLocal("data", QString::fromLatin1( KGlobal::instance()->instanceName() + '/' ) + d->m_file); + + if ( QFile::exists( xml_file ) ) + if ( !QFile::remove( xml_file ) ) + kdWarning() << "Could not delete " << xml_file << endl; + + m_widget = new KEditToolbarWidget(QString::null, d->m_collection, d->m_file, d->m_global, this); + } + + setMainWidget(m_widget); + m_widget->show(); + + connect(m_widget, SIGNAL(enableOk(bool)), SLOT(acceptOK(bool))); + connect(m_widget, SIGNAL(enableOk(bool)), SLOT(enableButtonApply(bool))); + + enableButtonApply(false); + emit newToolbarConfig(); +} + +void KEditToolbar::slotOk() +{ + if (!d->m_accept) { + reject(); + return; + } + + if (!m_widget->save()) + { + // some error box here is needed + } + else + { + emit newToolbarConfig(); + accept(); + } +} + +void KEditToolbar::slotApply() +{ + (void)m_widget->save(); + enableButtonApply(false); + emit newToolbarConfig(); +} + +void KEditToolbar::setDefaultToolbar(const char *toolbarName) +{ + s_defaultToolbar = toolbarName; +} + +KEditToolbarWidget::KEditToolbarWidget(KActionCollection *collection, + const QString& file, + bool global, QWidget *parent) + : QWidget(parent), + d(new KEditToolbarWidgetPrivate(instance(), collection)) +{ + initNonKPart(collection, file, global); + // now load in our toolbar combo box + loadToolbarCombo(); + adjustSize(); + setMinimumSize(sizeHint()); +} + +KEditToolbarWidget::KEditToolbarWidget(const QString& defaultToolbar, + KActionCollection *collection, + const QString& file, bool global, + QWidget *parent) + : QWidget(parent), + d(new KEditToolbarWidgetPrivate(instance(), collection)) +{ + initNonKPart(collection, file, global); + // now load in our toolbar combo box + loadToolbarCombo(defaultToolbar); + adjustSize(); + setMinimumSize(sizeHint()); +} + +KEditToolbarWidget::KEditToolbarWidget( KXMLGUIFactory* factory, + QWidget *parent) + : QWidget(parent), + d(new KEditToolbarWidgetPrivate(instance(), KXMLGUIClient::actionCollection() /*create new one*/)) +{ + initKPart(factory); + // now load in our toolbar combo box + loadToolbarCombo(); + adjustSize(); + setMinimumSize(sizeHint()); +} + +KEditToolbarWidget::KEditToolbarWidget( const QString& defaultToolbar, + KXMLGUIFactory* factory, + QWidget *parent) + : QWidget(parent), + d(new KEditToolbarWidgetPrivate(instance(), KXMLGUIClient::actionCollection() /*create new one*/)) +{ + initKPart(factory); + // now load in our toolbar combo box + loadToolbarCombo(defaultToolbar); + adjustSize(); + setMinimumSize(sizeHint()); +} + +KEditToolbarWidget::~KEditToolbarWidget() +{ + delete d; +} + +void KEditToolbarWidget::initNonKPart(KActionCollection *collection, + const QString& file, bool global) +{ + //d->m_actionList = collection->actions(); + + // handle the merging + if (global) + setXMLFile(locate("config", "ui/ui_standards.rc")); + QString localXML = d->loadXMLFile(file); + setXML(localXML, true); + + // reusable vars + QDomElement elem; + + // first, get all of the necessary info for our local xml + XmlData local; + local.m_xmlFile = d->xmlFile(file); + local.m_type = XmlData::Local; + local.m_document.setContent(localXML); + elem = local.m_document.documentElement().toElement(); + local.m_barList = d->findToolbars(elem); + local.m_actionCollection = collection; + d->m_xmlFiles.append(local); + + // then, the merged one (ui_standards + local xml) + XmlData merge; + merge.m_xmlFile = QString::null; + merge.m_type = XmlData::Merged; + merge.m_document = domDocument(); + elem = merge.m_document.documentElement().toElement(); + merge.m_barList = d->findToolbars(elem); + merge.m_actionCollection = collection; + d->m_xmlFiles.append(merge); + +#ifndef NDEBUG + //d->dump(); +#endif + + // okay, that done, we concern ourselves with the GUI aspects + setupLayout(); +} + +void KEditToolbarWidget::initKPart(KXMLGUIFactory* factory) +{ + // reusable vars + QDomElement elem; + + setFactory( factory ); + actionCollection()->setWidget( this ); + + // add all of the client data + QPtrList<KXMLGUIClient> clients(factory->clients()); + QPtrListIterator<KXMLGUIClient> it( clients ); + for( ; it.current(); ++it) + { + KXMLGUIClient *client = it.current(); + + if (client->xmlFile().isNull()) + continue; + + XmlData data; + data.m_xmlFile = client->localXMLFile(); + if ( it.atFirst() ) + data.m_type = XmlData::Shell; + else + data.m_type = XmlData::Part; + data.m_document.setContent( KXMLGUIFactory::readConfigFile( client->xmlFile(), client->instance() ) ); + elem = data.m_document.documentElement().toElement(); + data.m_barList = d->findToolbars(elem); + data.m_actionCollection = client->actionCollection(); + d->m_xmlFiles.append(data); + + //d->m_actionList += client->actionCollection()->actions(); + } + +#ifndef NDEBUG + //d->dump(); +#endif + + // okay, that done, we concern ourselves with the GUI aspects + setupLayout(); +} + +bool KEditToolbarWidget::save() +{ + //kdDebug(240) << "KEditToolbarWidget::save" << endl; + XmlDataList::Iterator it = d->m_xmlFiles.begin(); + for ( ; it != d->m_xmlFiles.end(); ++it) + { + // let's not save non-modified files + if ( !((*it).m_isModified) ) + continue; + + // let's also skip (non-existent) merged files + if ( (*it).m_type == XmlData::Merged ) + continue; + + dump_xml((*it).m_document); + + kdDebug(240) << "Saving " << (*it).m_xmlFile << endl; + // if we got this far, we might as well just save it + KXMLGUIFactory::saveConfigFile((*it).m_document, (*it).m_xmlFile); + } + + if ( !factory() ) + return true; + + rebuildKXMLGUIClients(); + + return true; +} + +void KEditToolbarWidget::rebuildKXMLGUIClients() +{ + if ( !factory() ) + return; + + QPtrList<KXMLGUIClient> clients(factory()->clients()); + //kdDebug(240) << "factory: " << clients.count() << " clients" << endl; + + // remove the elements starting from the last going to the first + KXMLGUIClient *client = clients.last(); + while ( client ) + { + //kdDebug(240) << "factory->removeClient " << client << endl; + factory()->removeClient( client ); + client = clients.prev(); + } + + KXMLGUIClient *firstClient = clients.first(); + + // now, rebuild the gui from the first to the last + //kdDebug(240) << "rebuilding the gui" << endl; + QPtrListIterator<KXMLGUIClient> cit( clients ); + for( ; cit.current(); ++cit) + { + KXMLGUIClient* client = cit.current(); + //kdDebug(240) << "updating client " << client << " " << client->instance()->instanceName() << " xmlFile=" << client->xmlFile() << endl; + 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->setXMLFile(locate("config", "ui/ui_standards.rc")); + + // and this forces it to use the *new* XML file + client->setXMLFile( file, client == firstClient /* merge if shell */ ); + } + } + + // 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. + cit.toFirst(); + for( ; cit.current(); ++cit) + factory()->addClient( cit.current() ); +} + +void KEditToolbarWidget::setupLayout() +{ + // the toolbar name combo + d->m_comboLabel = new QLabel(i18n("&Toolbar:"), this); + m_toolbarCombo = new QComboBox(this); + m_toolbarCombo->setEnabled(false); + d->m_comboLabel->setBuddy(m_toolbarCombo); + d->m_comboSeparator = new KSeparator(this); + connect(m_toolbarCombo, SIGNAL(activated(const QString&)), + this, SLOT(slotToolbarSelected(const QString&))); + +// QPushButton *new_toolbar = new QPushButton(i18n("&New"), this); +// new_toolbar->setPixmap(BarIcon("filenew", KIcon::SizeSmall)); +// new_toolbar->setEnabled(false); // disabled until implemented +// QPushButton *del_toolbar = new QPushButton(i18n("&Delete"), this); +// del_toolbar->setPixmap(BarIcon("editdelete", KIcon::SizeSmall)); +// del_toolbar->setEnabled(false); // disabled until implemented + + // our list of inactive actions + QLabel *inactive_label = new QLabel(i18n("A&vailable actions:"), this); + m_inactiveList = new ToolbarListView(this); + m_inactiveList->setDragEnabled(true); + m_inactiveList->setAcceptDrops(true); + m_inactiveList->setDropVisualizer(false); + m_inactiveList->setAllColumnsShowFocus(true); + m_inactiveList->setMinimumSize(180, 250); + m_inactiveList->header()->hide(); + m_inactiveList->addColumn(""); // icon + int column2 = m_inactiveList->addColumn(""); // text + m_inactiveList->setSorting( column2 ); + inactive_label->setBuddy(m_inactiveList); + connect(m_inactiveList, SIGNAL(selectionChanged(QListViewItem *)), + this, SLOT(slotInactiveSelected(QListViewItem *))); + connect(m_inactiveList, SIGNAL( doubleClicked( QListViewItem *, const QPoint &, int )), + this, SLOT(slotInsertButton())); + + // our list of active actions + QLabel *active_label = new QLabel(i18n("Curr&ent actions:"), this); + m_activeList = new ToolbarListView(this); + m_activeList->setDragEnabled(true); + m_activeList->setAcceptDrops(true); + m_activeList->setDropVisualizer(true); + m_activeList->setAllColumnsShowFocus(true); + m_activeList->setMinimumWidth(m_inactiveList->minimumWidth()); + m_activeList->header()->hide(); + m_activeList->addColumn(""); // icon + m_activeList->addColumn(""); // text + m_activeList->setSorting(-1); + active_label->setBuddy(m_activeList); + + connect(m_inactiveList, SIGNAL(dropped(KListView*,QDropEvent*,QListViewItem*)), + this, SLOT(slotDropped(KListView*,QDropEvent*,QListViewItem*))); + connect(m_activeList, SIGNAL(dropped(KListView*,QDropEvent*,QListViewItem*)), + this, SLOT(slotDropped(KListView*,QDropEvent*,QListViewItem*))); + connect(m_activeList, SIGNAL(selectionChanged(QListViewItem *)), + this, SLOT(slotActiveSelected(QListViewItem *))); + connect(m_activeList, SIGNAL( doubleClicked( QListViewItem *, const QPoint &, int )), + this, SLOT(slotRemoveButton())); + + // "change icon" button + d->m_changeIcon = new KPushButton( i18n( "Change &Icon..." ), this ); + QString kdialogExe = KStandardDirs::findExe(QString::fromLatin1("kdialog")); + d->m_hasKDialog = !kdialogExe.isEmpty(); + d->m_changeIcon->setEnabled( d->m_hasKDialog ); + + connect( d->m_changeIcon, SIGNAL( clicked() ), + this, SLOT( slotChangeIcon() ) ); + + // The buttons in the middle + QIconSet iconSet; + + m_upAction = new QToolButton(this); + iconSet = SmallIconSet( "up" ); + m_upAction->setIconSet( iconSet ); + m_upAction->setEnabled(false); + m_upAction->setAutoRepeat(true); + connect(m_upAction, SIGNAL(clicked()), SLOT(slotUpButton())); + + m_insertAction = new QToolButton(this); + iconSet = QApplication::reverseLayout() ? SmallIconSet( "back" ) : SmallIconSet( "forward" ); + m_insertAction->setIconSet( iconSet ); + m_insertAction->setEnabled(false); + connect(m_insertAction, SIGNAL(clicked()), SLOT(slotInsertButton())); + + m_removeAction = new QToolButton(this); + iconSet = QApplication::reverseLayout() ? SmallIconSet( "forward" ) : SmallIconSet( "back" ); + m_removeAction->setIconSet( iconSet ); + m_removeAction->setEnabled(false); + connect(m_removeAction, SIGNAL(clicked()), SLOT(slotRemoveButton())); + + m_downAction = new QToolButton(this); + iconSet = SmallIconSet( "down" ); + m_downAction->setIconSet( iconSet ); + m_downAction->setEnabled(false); + m_downAction->setAutoRepeat(true); + connect(m_downAction, SIGNAL(clicked()), SLOT(slotDownButton())); + + d->m_helpArea = new QLabel(this); + d->m_helpArea->setAlignment( Qt::WordBreak ); + + // now start with our layouts + QVBoxLayout *top_layout = new QVBoxLayout(this, 0, KDialog::spacingHint()); + + QVBoxLayout *name_layout = new QVBoxLayout(KDialog::spacingHint()); + QHBoxLayout *list_layout = new QHBoxLayout(KDialog::spacingHint()); + + QVBoxLayout *inactive_layout = new QVBoxLayout(KDialog::spacingHint()); + QVBoxLayout *active_layout = new QVBoxLayout(KDialog::spacingHint()); + QHBoxLayout *changeIcon_layout = new QHBoxLayout(KDialog::spacingHint()); + + QGridLayout *button_layout = new QGridLayout(5, 3, 0); + + name_layout->addWidget(d->m_comboLabel); + name_layout->addWidget(m_toolbarCombo); +// name_layout->addWidget(new_toolbar); +// name_layout->addWidget(del_toolbar); + + 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(m_inactiveList, 1); + + active_layout->addWidget(active_label); + active_layout->addWidget(m_activeList, 1); + active_layout->addLayout(changeIcon_layout); + + changeIcon_layout->addStretch( 1 ); + changeIcon_layout->addWidget( d->m_changeIcon ); + changeIcon_layout->addStretch( 1 ); + + list_layout->addLayout(inactive_layout); + list_layout->addLayout(button_layout); + list_layout->addLayout(active_layout); + + top_layout->addLayout(name_layout); + top_layout->addWidget(d->m_comboSeparator); + top_layout->addLayout(list_layout,10); + top_layout->addWidget(d->m_helpArea); + top_layout->addWidget(new KSeparator(this)); +} + +void KEditToolbarWidget::loadToolbarCombo(const QString& defaultToolbar) +{ + static const QString &attrName = KGlobal::staticQString( "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 + XmlDataList::Iterator xit = d->m_xmlFiles.begin(); + for ( ; xit != d->m_xmlFiles.end(); ++xit) + { + // skip the local one in favor of the merged + if ( (*xit).m_type == XmlData::Local ) + continue; + + // each xml file may have any number of toolbars + ToolbarList::Iterator it = (*xit).m_barList.begin(); + for ( ; it != (*xit).m_barList.end(); ++it) + { + QString name = d->toolbarName( *xit, *it ); + m_toolbarCombo->setEnabled( true ); + m_toolbarCombo->insertItem( name ); + if (defaultToolbarId == -1 && (name == defaultToolbar || defaultToolbar == (*it).attribute( attrName ))) + defaultToolbarId = count; + count++; + } + } + bool showCombo = (count > 1); + d->m_comboLabel->setShown(showCombo); + d->m_comboSeparator->setShown(showCombo); + m_toolbarCombo->setShown(showCombo); + if (defaultToolbarId == -1) + defaultToolbarId = 0; + // we want to the specified item selected and its actions loaded + m_toolbarCombo->setCurrentItem(defaultToolbarId); + slotToolbarSelected(m_toolbarCombo->currentText()); +} + +void KEditToolbarWidget::loadActionList(QDomElement& elem) +{ + static const QString &tagSeparator = KGlobal::staticQString( "Separator" ); + static const QString &tagMerge = KGlobal::staticQString( "Merge" ); + static const QString &tagActionList= KGlobal::staticQString( "ActionList" ); + static const QString &attrName = KGlobal::staticQString( "name" ); + static const QString &attrLineSeparator = KGlobal::staticQString( "lineSeparator" ); + + int sep_num = 0; + QString sep_name("separator_%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 = d->m_currentXmlData->m_actionCollection; + + // store the names of our active actions + QMap<QString, bool> active_list; + + // see if our current action is in this toolbar + KIconLoader *loader = KGlobal::instance()->iconLoader(); + QDomNode n = elem.lastChild(); + for( ; !n.isNull(); n = n.previousSibling() ) + { + 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::null); + bool isLineSep = ( it.attribute(attrLineSeparator, "true").lower() == QString::fromLatin1("true") ); + if(isLineSep) + act->setText(1, LINESEPARATORSTRING); + else + act->setText(1, SEPARATORSTRING); + 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(1, i18n("<Merge>")); + else + act->setText(1, i18n("<Merge %1>").arg(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 won't be able to re-add it.") ); + act->setText(1, i18n("ActionList: %1").arg(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... + for (unsigned int i = 0; i < actionCollection->count(); i++) + { + KAction *action = actionCollection->action( i ); + + // do we have a match? + if (it.attribute( attrName ) == action->name()) + { + // we have a match! + ToolbarItem *act = new ToolbarItem(m_activeList, it.tagName(), action->name(), action->toolTip()); + act->setText(1, action->plainText()); + if (action->hasIcon()) + if (!action->icon().isEmpty()) + act->setPixmap(0, loader->loadIcon(action->icon(), KIcon::Toolbar, 16, KIcon::DefaultState, 0, true) ); + else // Has iconset + act->setPixmap(0, action->iconSet(KIcon::Toolbar).pixmap()); + + active_list.insert(action->name(), true); + break; + } + } + } + + // go through the rest of the collection + for (int i = actionCollection->count() - 1; i > -1; --i) + { + KAction *action = actionCollection->action( i ); + + // skip our active ones + if (active_list.contains(action->name())) + continue; + + ToolbarItem *act = new ToolbarItem(m_inactiveList, tagActionList, action->name(), action->toolTip()); + act->setText(1, action->plainText()); + if (action->hasIcon()) + if (!action->icon().isEmpty()) + act->setPixmap(0, loader->loadIcon(action->icon(), KIcon::Toolbar, 16, KIcon::DefaultState, 0, true) ); + else // Has iconset + act->setPixmap(0, action->iconSet(KIcon::Toolbar).pixmap()); + } + + // finally, add default separators to the inactive list + ToolbarItem *act = new ToolbarItem(m_inactiveList, tagSeparator, sep_name.arg(sep_num++), QString::null); + act->setText(1, LINESEPARATORSTRING); + act = new ToolbarItem(m_inactiveList, tagSeparator, sep_name.arg(sep_num++), QString::null); + act->setText(1, SEPARATORSTRING); +} + +KActionCollection *KEditToolbarWidget::actionCollection() const +{ + return d->m_collection; +} + +void KEditToolbarWidget::slotToolbarSelected(const QString& _text) +{ + // iterate through everything + XmlDataList::Iterator xit = d->m_xmlFiles.begin(); + for ( ; xit != d->m_xmlFiles.end(); ++xit) + { + // each xml file may have any number of toolbars + ToolbarList::Iterator it = (*xit).m_barList.begin(); + for ( ; it != (*xit).m_barList.end(); ++it) + { + QString name = d->toolbarName( *xit, *it ); + // is this our toolbar? + if ( name == _text ) + { + // save our current settings + d->m_currentXmlData = & (*xit); + d->m_currentToolbarElem = (*it); + + // load in our values + loadActionList(d->m_currentToolbarElem); + + if ((*xit).m_type == XmlData::Part || (*xit).m_type == XmlData::Shell) + setDOMDocument( (*xit).m_document ); + return; + } + } + } +} + +void KEditToolbarWidget::slotInactiveSelected(QListViewItem *item) +{ + ToolbarItem* toolitem = static_cast<ToolbarItem *>(item); + if (item) + { + m_insertAction->setEnabled(true); + QString statusText = toolitem->statusText(); + d->m_helpArea->setText( statusText ); + } + else + { + m_insertAction->setEnabled(false); + d->m_helpArea->setText( QString::null ); + } +} + +void KEditToolbarWidget::slotActiveSelected(QListViewItem *item) +{ + ToolbarItem* toolitem = static_cast<ToolbarItem *>(item); + m_removeAction->setEnabled( item ); + + static const QString &tagAction = KGlobal::staticQString( "Action" ); + d->m_changeIcon->setEnabled( item && + d->m_hasKDialog && + toolitem->internalTag() == tagAction ); + + if (item) + { + if (item->itemAbove()) + m_upAction->setEnabled(true); + else + m_upAction->setEnabled(false); + + if (item->itemBelow()) + m_downAction->setEnabled(true); + else + m_downAction->setEnabled(false); + QString statusText = toolitem->statusText(); + d->m_helpArea->setText( statusText ); + } + else + { + m_upAction->setEnabled(false); + m_downAction->setEnabled(false); + d->m_helpArea->setText( QString::null ); + } +} + +void KEditToolbarWidget::slotDropped(KListView *list, QDropEvent *e, QListViewItem *after) +{ + ToolbarItem *item = new ToolbarItem(m_inactiveList); // needs parent, use inactiveList temporarily + if(!ToolbarItemDrag::decode(e, *item)) { + delete item; + return; + } + + if (list == m_activeList) { + if (e->source() == m_activeList) { + // has been dragged within the active list (moved). + moveActive(item, after); + } + else + 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; item = 0; // not neded anymore + + // we're modified, so let this change + emit enableOk(true); + + slotToolbarSelected( m_toolbarCombo->currentText() ); +} + +void KEditToolbarWidget::slotInsertButton() +{ + ToolbarItem *item = (ToolbarItem*)m_inactiveList->currentItem(); + insertActive(item, m_activeList->currentItem(), false); + + // we're modified, so let this change + emit enableOk(true); + + // TODO: #### this causes #97572. + // It would be better to just "delete item; loadActions( ... , ActiveListOnly );" or something. + slotToolbarSelected( m_toolbarCombo->currentText() ); +} + +void KEditToolbarWidget::slotRemoveButton() +{ + removeActive( dynamic_cast<ToolbarItem*>(m_activeList->currentItem()) ); + + // we're modified, so let this change + emit enableOk(true); + + slotToolbarSelected( m_toolbarCombo->currentText() ); +} + +void KEditToolbarWidget::insertActive(ToolbarItem *item, QListViewItem *before, bool prepend) +{ + if (!item) + return; + + static const QString &tagAction = KGlobal::staticQString( "Action" ); + static const QString &tagSeparator = KGlobal::staticQString( "Separator" ); + static const QString &attrName = KGlobal::staticQString( "name" ); + static const QString &attrLineSeparator = KGlobal::staticQString( "lineSeparator" ); + static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" ); + + QDomElement new_item; + // let's handle the separator specially + if (item->text(1) == LINESEPARATORSTRING) { + new_item = domDocument().createElement(tagSeparator); + } else if (item->text(1) == SEPARATORSTRING) { + new_item = domDocument().createElement(tagSeparator); + new_item.setAttribute(attrLineSeparator, "false"); + } else + new_item = domDocument().createElement(tagAction); + new_item.setAttribute(attrName, item->internalName()); + + 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 + ToolbarItem *act_item = (ToolbarItem*)before; + QDomElement elem = d->findElementForToolbarItem( act_item ); + Q_ASSERT( !elem.isNull() ); + d->m_currentToolbarElem.insertAfter(new_item, elem); + } + else + { + // simply put it at the beginning or the end of the list. + if (prepend) + d->m_currentToolbarElem.insertBefore(new_item, d->m_currentToolbarElem.firstChild()); + else + d->m_currentToolbarElem.appendChild(new_item); + } + + // and set this container as a noMerge + d->m_currentToolbarElem.setAttribute( attrNoMerge, "1"); + + // update the local doc + updateLocal(d->m_currentToolbarElem); +} + +void KEditToolbarWidget::removeActive(ToolbarItem *item) +{ + if (!item) + return; + + static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" ); + + // we're modified, so let this change + emit enableOk(true); + + // now iterate through to find the child to nuke + QDomElement elem = d->findElementForToolbarItem( item ); + if ( !elem.isNull() ) + { + // nuke myself! + d->m_currentToolbarElem.removeChild(elem); + + // and set this container as a noMerge + d->m_currentToolbarElem.setAttribute( attrNoMerge, "1"); + + // update the local doc + updateLocal(d->m_currentToolbarElem); + } +} + +void KEditToolbarWidget::slotUpButton() +{ + ToolbarItem *item = (ToolbarItem*)m_activeList->currentItem(); + + // make sure we're not the top item already + if (!item->itemAbove()) + return; + + // we're modified, so let this change + emit enableOk(true); + + moveActive( item, item->itemAbove()->itemAbove() ); + delete item; +} + +void KEditToolbarWidget::moveActive( ToolbarItem* item, QListViewItem* before ) +{ + QDomElement e = d->findElementForToolbarItem( item ); + + if ( e.isNull() ) + return; + + // cool, i found me. now clone myself + ToolbarItem *clone = new ToolbarItem(m_activeList, + before, + item->internalTag(), + item->internalName(), + item->statusText()); + + clone->setText(1, item->text(1)); + + // only set new pixmap if exists + if( item->pixmap(0) ) + clone->setPixmap(0, *item->pixmap(0)); + + // select my clone + m_activeList->setSelected(clone, true); + + // make clone visible + m_activeList->ensureItemVisible(clone); + + // and do the real move in the DOM + if ( !before ) + d->m_currentToolbarElem.insertBefore(e, d->m_currentToolbarElem.firstChild() ); + else + d->m_currentToolbarElem.insertAfter(e, d->findElementForToolbarItem( (ToolbarItem*)before )); + + // and set this container as a noMerge + static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" ); + d->m_currentToolbarElem.setAttribute( attrNoMerge, "1"); + + // update the local doc + updateLocal(d->m_currentToolbarElem); +} + +void KEditToolbarWidget::slotDownButton() +{ + ToolbarItem *item = (ToolbarItem*)m_activeList->currentItem(); + + // make sure we're not the bottom item already + if (!item->itemBelow()) + return; + + // we're modified, so let this change + emit enableOk(true); + + moveActive( item, item->itemBelow() ); + delete item; +} + +void KEditToolbarWidget::updateLocal(QDomElement& elem) +{ + static const QString &attrName = KGlobal::staticQString( "name" ); + + XmlDataList::Iterator xit = d->m_xmlFiles.begin(); + for ( ; xit != d->m_xmlFiles.end(); ++xit) + { + if ( (*xit).m_type == XmlData::Merged ) + continue; + + if ( (*xit).m_type == XmlData::Shell || + (*xit).m_type == XmlData::Part ) + { + if ( d->m_currentXmlData->m_xmlFile == (*xit).m_xmlFile ) + { + (*xit).m_isModified = true; + return; + } + + continue; + } + + (*xit).m_isModified = true; + + ToolbarList::Iterator it = (*xit).m_barList.begin(); + for ( ; it != (*xit).m_barList.end(); ++it) + { + QString name( (*it).attribute( attrName ) ); + QString tag( (*it).tagName() ); + if ( (tag != elem.tagName()) || (name != elem.attribute(attrName)) ) + continue; + + QDomElement toolbar = (*xit).m_document.documentElement().toElement(); + toolbar.replaceChild(elem, (*it)); + return; + } + + // just append it + QDomElement toolbar = (*xit).m_document.documentElement().toElement(); + toolbar.appendChild(elem); + } +} + +void KEditToolbarWidget::slotChangeIcon() +{ + // We can't use KIconChooser here, since it's in libkio + // ##### KDE4: reconsider this, e.g. move KEditToolbar to libkio + + //if the process is already running (e.g. when somebody clicked the change button twice (see #127149)) - do nothing... + //otherwise m_kdialogProcess will be overwritten and set to zero in slotProcessExited()...crash! + if ( d->m_kdialogProcess && d->m_kdialogProcess->isRunning() ) + return; + + d->m_kdialogProcess = new KProcIO; + QString kdialogExe = KStandardDirs::findExe(QString::fromLatin1("kdialog")); + (*d->m_kdialogProcess) << kdialogExe; + (*d->m_kdialogProcess) << "--embed"; + (*d->m_kdialogProcess) << QString::number( (ulong)topLevelWidget()->winId() ); + (*d->m_kdialogProcess) << "--geticon"; + (*d->m_kdialogProcess) << "Toolbar"; + (*d->m_kdialogProcess) << "Actions"; + if ( !d->m_kdialogProcess->start( KProcess::NotifyOnExit ) ) { + kdError(240) << "Can't run " << kdialogExe << endl; + delete d->m_kdialogProcess; + d->m_kdialogProcess = 0; + return; + } + + m_activeList->setEnabled( false ); // don't change the current item + m_toolbarCombo->setEnabled( false ); // don't change the current toolbar + + connect( d->m_kdialogProcess, SIGNAL( processExited( KProcess* ) ), + this, SLOT( slotProcessExited( KProcess* ) ) ); +} + +void KEditToolbarWidget::slotProcessExited( KProcess* ) +{ + m_activeList->setEnabled( true ); + m_toolbarCombo->setEnabled( true ); + + QString icon; + + if (!d->m_kdialogProcess) { + kdError(240) << "Something is wrong here! m_kdialogProcess is zero!" << endl; + return; + } + + if ( !d->m_kdialogProcess->normalExit() || + d->m_kdialogProcess->exitStatus() || + d->m_kdialogProcess->readln(icon, true) <= 0 ) { + delete d->m_kdialogProcess; + d->m_kdialogProcess = 0; + return; + } + + ToolbarItem *item = (ToolbarItem*)m_activeList->currentItem(); + if(item){ + item->setPixmap(0, BarIcon(icon, 16)); + + Q_ASSERT( d->m_currentXmlData->m_type != XmlData::Merged ); + + d->m_currentXmlData->m_isModified = true; + + // Get hold of ActionProperties tag + QDomElement elem = KXMLGUIFactory::actionPropertiesElement( d->m_currentXmlData->m_document ); + // 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( "icon", icon ); + + // we're modified, so let this change + emit enableOk(true); + } + + delete d->m_kdialogProcess; + d->m_kdialogProcess = 0; +} + +void KEditToolbar::virtual_hook( int id, void* data ) +{ KDialogBase::virtual_hook( id, data ); } + +void KEditToolbarWidget::virtual_hook( int id, void* data ) +{ KXMLGUIClient::virtual_hook( id, data ); } + +#include "kedittoolbar.moc" |