summaryrefslogtreecommitdiffstats
path: root/kdeui/kxmlguiclient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdeui/kxmlguiclient.cpp')
-rw-r--r--kdeui/kxmlguiclient.cpp951
1 files changed, 951 insertions, 0 deletions
diff --git a/kdeui/kxmlguiclient.cpp b/kdeui/kxmlguiclient.cpp
new file mode 100644
index 000000000..ef14c2f49
--- /dev/null
+++ b/kdeui/kxmlguiclient.cpp
@@ -0,0 +1,951 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
+ 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 "kxmlguiclient.h"
+#include "kxmlguifactory.h"
+#include "kxmlguibuilder.h"
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qdom.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <qguardedptr.h>
+
+#include <kinstance.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <kaction.h>
+#include <kapplication.h>
+
+#include <assert.h>
+
+class KXMLGUIClientPrivate
+{
+public:
+ KXMLGUIClientPrivate()
+ {
+ m_instance = KGlobal::instance();
+ m_parent = 0L;
+ m_builder = 0L;
+ m_actionCollection = 0;
+ }
+ ~KXMLGUIClientPrivate()
+ {
+ }
+
+ KInstance *m_instance;
+
+ QDomDocument m_doc;
+ KActionCollection *m_actionCollection;
+ QDomDocument m_buildDocument;
+ QGuardedPtr<KXMLGUIFactory> m_factory;
+ KXMLGUIClient *m_parent;
+ //QPtrList<KXMLGUIClient> m_supers;
+ QPtrList<KXMLGUIClient> m_children;
+ KXMLGUIBuilder *m_builder;
+ QString m_xmlFile;
+ QString m_localXMLFile;
+};
+
+KXMLGUIClient::KXMLGUIClient()
+{
+ d = new KXMLGUIClientPrivate;
+}
+
+KXMLGUIClient::KXMLGUIClient( KXMLGUIClient *parent )
+{
+ d = new KXMLGUIClientPrivate;
+ parent->insertChildClient( this );
+}
+
+KXMLGUIClient::~KXMLGUIClient()
+{
+ if ( d->m_parent )
+ d->m_parent->removeChildClient( this );
+
+ QPtrListIterator<KXMLGUIClient> it( d->m_children );
+ for ( ; it.current(); ++it ) {
+ assert( it.current()->d->m_parent == this );
+ it.current()->d->m_parent = 0;
+ }
+
+ delete d->m_actionCollection;
+ delete d;
+}
+
+KAction *KXMLGUIClient::action( const char *name ) const
+{
+ KAction* act = actionCollection()->action( name );
+ if ( !act ) {
+ QPtrListIterator<KXMLGUIClient> childIt( d->m_children );
+ for (; childIt.current(); ++childIt ) {
+ act = childIt.current()->actionCollection()->action( name );
+ if ( act )
+ break;
+ }
+ }
+ return act;
+}
+
+KActionCollection *KXMLGUIClient::actionCollection() const
+{
+ if ( !d->m_actionCollection )
+ {
+ d->m_actionCollection = new KActionCollection(
+ "KXMLGUIClient-KActionCollection", this );
+ }
+ return d->m_actionCollection;
+}
+
+KAction *KXMLGUIClient::action( const QDomElement &element ) const
+{
+ static const QString &attrName = KGlobal::staticQString( "name" );
+ return actionCollection()->action( element.attribute( attrName ).latin1() );
+}
+
+KInstance *KXMLGUIClient::instance() const
+{
+ return d->m_instance;
+}
+
+QDomDocument KXMLGUIClient::domDocument() const
+{
+ return d->m_doc;
+}
+
+QString KXMLGUIClient::xmlFile() const
+{
+ return d->m_xmlFile;
+}
+
+QString KXMLGUIClient::localXMLFile() const
+{
+ if ( !d->m_localXMLFile.isEmpty() )
+ return d->m_localXMLFile;
+
+ if ( !QDir::isRelativePath(d->m_xmlFile) )
+ return QString::null; // can't save anything here
+
+ return locateLocal( "data", QString::fromLatin1( instance()->instanceName() + '/' ) + d->m_xmlFile );
+}
+
+
+void KXMLGUIClient::reloadXML()
+{
+ QString file( xmlFile() );
+ if ( !file.isEmpty() )
+ setXMLFile( file );
+}
+
+void KXMLGUIClient::setInstance( KInstance *instance )
+{
+ d->m_instance = instance;
+ actionCollection()->setInstance( instance );
+ if ( d->m_builder )
+ d->m_builder->setBuilderClient( this );
+}
+
+void KXMLGUIClient::setXMLFile( const QString& _file, bool merge, bool setXMLDoc )
+{
+ // store our xml file name
+ if ( !_file.isNull() ) {
+ d->m_xmlFile = _file;
+ actionCollection()->setXMLFile( _file );
+ }
+
+ if ( !setXMLDoc )
+ return;
+
+ QString file = _file;
+ if ( QDir::isRelativePath(file) )
+ {
+ QString doc;
+
+ QString filter = QString::fromLatin1( instance()->instanceName() + '/' ) + _file;
+
+ QStringList allFiles = instance()->dirs()->findAllResources( "data", filter ) + instance()->dirs()->findAllResources( "data", _file );
+
+ file = findMostRecentXMLFile( allFiles, doc );
+
+ if ( file.isEmpty() )
+ {
+ // this might or might not be an error. for the time being,
+ // let's treat this as if it isn't a problem and the user just
+ // wants the global standards file
+
+ // however if a non-empty file gets passed and we can't find it we might
+ // inform the developer using some debug output
+ if ( !_file.isEmpty() )
+ kdWarning() << "KXMLGUIClient::setXMLFile: cannot find .rc file " << _file << endl;
+
+ setXML( QString::null, true );
+ return;
+ }
+ else if ( !doc.isEmpty() )
+ {
+ setXML( doc, merge );
+ return;
+ }
+ }
+
+ QString xml = KXMLGUIFactory::readConfigFile( file );
+ setXML( xml, merge );
+}
+
+void KXMLGUIClient::setLocalXMLFile( const QString &file )
+{
+ d->m_localXMLFile = file;
+}
+
+void KXMLGUIClient::setXML( const QString &document, bool merge )
+{
+ QDomDocument doc;
+ doc.setContent( document );
+ setDOMDocument( doc, merge );
+}
+
+void KXMLGUIClient::setDOMDocument( const QDomDocument &document, bool merge )
+{
+ if ( merge )
+ {
+ QDomElement base = d->m_doc.documentElement();
+
+ QDomElement e = document.documentElement();
+
+ // merge our original (global) xml with our new one
+ mergeXML(base, e, actionCollection());
+
+ // reassign our pointer as mergeXML might have done something
+ // strange to it
+ base = d->m_doc.documentElement();
+
+ // we want some sort of failsafe.. just in case
+ if ( base.isNull() )
+ d->m_doc = document;
+ }
+ else
+ {
+ d->m_doc = document;
+ }
+
+ setXMLGUIBuildDocument( QDomDocument() );
+}
+
+bool KXMLGUIClient::mergeXML( QDomElement &base, const QDomElement &additive, KActionCollection *actionCollection )
+{
+ static const QString &tagAction = KGlobal::staticQString( "Action" );
+ static const QString &tagMerge = KGlobal::staticQString( "Merge" );
+ static const QString &tagSeparator = KGlobal::staticQString( "Separator" );
+ static const QString &attrName = KGlobal::staticQString( "name" );
+ static const QString &attrAppend = KGlobal::staticQString( "append" );
+ static const QString &attrWeakSeparator = KGlobal::staticQString( "weakSeparator" );
+ static const QString &tagMergeLocal = KGlobal::staticQString( "MergeLocal" );
+ static const QString &tagText = KGlobal::staticQString( "text" );
+ static const QString &attrAlreadyVisited = KGlobal::staticQString( "alreadyVisited" );
+ static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" );
+ static const QString &attrOne = KGlobal::staticQString( "1" );
+
+ // there is a possibility that we don't want to merge in the
+ // additive.. rather, we might want to *replace* the base with the
+ // additive. this can be for any container.. either at a file wide
+ // level or a simple container level. we look for the 'noMerge'
+ // tag, in any event and just replace the old with the new
+ if ( additive.attribute(attrNoMerge) == attrOne ) // ### use toInt() instead? (Simon)
+ {
+ base.parentNode().replaceChild(additive, base);
+ return true;
+ }
+
+ QString tag;
+
+ // iterate over all elements in the container (of the global DOM tree)
+ QDomNode n = base.firstChild();
+ while ( !n.isNull() )
+ {
+ QDomElement e = n.toElement();
+ n = n.nextSibling(); // Advance now so that we can safely delete e
+ if (e.isNull())
+ continue;
+
+ tag = e.tagName();
+
+ // if there's an action tag in the global tree and the action is
+ // not implemented, then we remove the element
+ if ( tag == tagAction )
+ {
+ QCString name = e.attribute( attrName ).utf8(); // WABA
+ if ( !actionCollection->action( name ) ||
+ (kapp && !kapp->authorizeKAction(name)))
+ {
+ // remove this child as we aren't using it
+ base.removeChild( e );
+ continue;
+ }
+ }
+
+ // if there's a separator defined in the global tree, then add an
+ // attribute, specifying that this is a "weak" separator
+ else if ( tag == tagSeparator )
+ {
+ e.setAttribute( attrWeakSeparator, (uint)1 );
+
+ // okay, hack time. if the last item was a weak separator OR
+ // this is the first item in a container, then we nuke the
+ // current one
+ QDomElement prev = e.previousSibling().toElement();
+ if ( prev.isNull() ||
+ ( prev.tagName() == tagSeparator && !prev.attribute( attrWeakSeparator ).isNull() ) ||
+ ( prev.tagName() == tagText ) )
+ {
+ // the previous element was a weak separator or didn't exist
+ base.removeChild( e );
+ continue;
+ }
+ }
+
+ // the MergeLocal tag lets us specify where non-standard elements
+ // of the local tree shall be merged in. After inserting the
+ // elements we delete this element
+ else if ( tag == tagMergeLocal )
+ {
+ QDomNode it = additive.firstChild();
+ while ( !it.isNull() )
+ {
+ QDomElement newChild = it.toElement();
+ it = it.nextSibling();
+ if (newChild.isNull() )
+ continue;
+
+ if ( newChild.tagName() == tagText )
+ continue;
+
+ if ( newChild.attribute( attrAlreadyVisited ) == attrOne )
+ continue;
+
+ QString itAppend( newChild.attribute( attrAppend ) );
+ QString elemName( e.attribute( attrName ) );
+
+ if ( ( itAppend.isNull() && elemName.isEmpty() ) ||
+ ( itAppend == elemName ) )
+ {
+ // first, see if this new element matches a standard one in
+ // the global file. if it does, then we skip it as it will
+ // be merged in, later
+ QDomElement matchingElement = findMatchingElement( newChild, base );
+ if ( matchingElement.isNull() || newChild.tagName() == tagSeparator )
+ base.insertBefore( newChild, e );
+ }
+ }
+
+ base.removeChild( e );
+ continue;
+ }
+
+ // in this last case we check for a separator tag and, if not, we
+ // can be sure that its a container --> proceed with child nodes
+ // recursively and delete the just proceeded container item in
+ // case its empty (if the recursive call returns true)
+ else if ( tag != tagMerge )
+ {
+ // handle the text tag
+ if ( tag == tagText )
+ continue;
+
+ QDomElement matchingElement = findMatchingElement( e, additive );
+
+ if ( !matchingElement.isNull() )
+ {
+ matchingElement.setAttribute( attrAlreadyVisited, (uint)1 );
+
+ if ( mergeXML( e, matchingElement, actionCollection ) )
+ {
+ base.removeChild( e );
+ continue;
+ }
+
+ // Merge attributes
+ const QDomNamedNodeMap attribs = matchingElement.attributes();
+ const uint attribcount = attribs.count();
+
+ for(uint i = 0; i < attribcount; ++i)
+ {
+ const QDomNode node = attribs.item(i);
+ e.setAttribute(node.nodeName(), node.nodeValue());
+ }
+
+ continue;
+ }
+ else
+ {
+ // this is an important case here! We reach this point if the
+ // "local" tree does not contain a container definition for
+ // this container. However we have to call mergeXML recursively
+ // and make it check if there are actions implemented for this
+ // container. *If* none, then we can remove this container now
+ if ( mergeXML( e, QDomElement(), actionCollection ) )
+ base.removeChild( e );
+ continue;
+ }
+ }
+ }
+
+ //here we append all child elements which were not inserted
+ //previously via the LocalMerge tag
+ n = additive.firstChild();
+ while ( !n.isNull() )
+ {
+ QDomElement e = n.toElement();
+ n = n.nextSibling(); // Advance now so that we can safely delete e
+ if (e.isNull())
+ continue;
+
+ QDomElement matchingElement = findMatchingElement( e, base );
+
+ if ( matchingElement.isNull() )
+ {
+ base.appendChild( e );
+ }
+ }
+
+ // do one quick check to make sure that the last element was not
+ // a weak separator
+ QDomElement last = base.lastChild().toElement();
+ if ( (last.tagName() == tagSeparator) && (!last.attribute( attrWeakSeparator ).isNull()) )
+ {
+ base.removeChild( last );
+ }
+
+ // now we check if we are empty (in which case we return "true", to
+ // indicate the caller that it can delete "us" (the base element
+ // argument of "this" call)
+ bool deleteMe = true;
+
+ n = base.firstChild();
+ while ( !n.isNull() )
+ {
+ QDomElement e = n.toElement();
+ n = n.nextSibling(); // Advance now so that we can safely delete e
+ if (e.isNull())
+ continue;
+
+ tag = e.tagName();
+
+ if ( tag == tagAction )
+ {
+ // if base contains an implemented action, then we must not get
+ // deleted (note that the actionCollection contains both,
+ // "global" and "local" actions
+ if ( actionCollection->action( e.attribute( attrName ).utf8() ) )
+ {
+ deleteMe = false;
+ break;
+ }
+ }
+ else if ( tag == tagSeparator )
+ {
+ // if we have a separator which has *not* the weak attribute
+ // set, then it must be owned by the "local" tree in which case
+ // we must not get deleted either
+ QString weakAttr = e.attribute( attrWeakSeparator );
+ if ( weakAttr.isEmpty() || weakAttr.toInt() != 1 )
+ {
+ deleteMe = false;
+ break;
+ }
+ }
+
+ // in case of a merge tag we have unlimited lives, too ;-)
+ else if ( tag == tagMerge )
+ {
+// deleteMe = false;
+// break;
+ continue;
+ }
+
+ // a text tag is NOT enough to spare this container
+ else if ( tag == tagText )
+ {
+ continue;
+ }
+
+ // what's left are non-empty containers! *don't* delete us in this
+ // case (at this position we can be *sure* that the container is
+ // *not* empty, as the recursive call for it was in the first loop
+ // which deleted the element in case the call returned "true"
+ else
+ {
+ deleteMe = false;
+ break;
+ }
+ }
+
+ return deleteMe;
+}
+
+QDomElement KXMLGUIClient::findMatchingElement( const QDomElement &base, const QDomElement &additive )
+{
+ static const QString &tagAction = KGlobal::staticQString( "Action" );
+ static const QString &tagMergeLocal = KGlobal::staticQString( "MergeLocal" );
+ static const QString &attrName = KGlobal::staticQString( "name" );
+
+ QDomNode n = additive.firstChild();
+ while ( !n.isNull() )
+ {
+ QDomElement e = n.toElement();
+ n = n.nextSibling(); // Advance now so that we can safely delete e
+ if (e.isNull())
+ continue;
+
+ // skip all action and merge tags as we will never use them
+ if ( ( e.tagName() == tagAction ) || ( e.tagName() == tagMergeLocal ) )
+ {
+ continue;
+ }
+
+ // now see if our tags are equivalent
+ if ( ( e.tagName() == base.tagName() ) &&
+ ( e.attribute( attrName ) == base.attribute( attrName ) ) )
+ {
+ return e;
+ }
+ }
+
+ // nope, return a (now) null element
+ return QDomElement();
+}
+
+void KXMLGUIClient::conserveMemory()
+{
+ d->m_doc = QDomDocument();
+ d->m_buildDocument = QDomDocument();
+}
+
+void KXMLGUIClient::setXMLGUIBuildDocument( const QDomDocument &doc )
+{
+ d->m_buildDocument = doc;
+}
+
+QDomDocument KXMLGUIClient::xmlguiBuildDocument() const
+{
+ return d->m_buildDocument;
+}
+
+void KXMLGUIClient::setFactory( KXMLGUIFactory *factory )
+{
+ d->m_factory = factory;
+}
+
+KXMLGUIFactory *KXMLGUIClient::factory() const
+{
+ return d->m_factory;
+}
+
+KXMLGUIClient *KXMLGUIClient::parentClient() const
+{
+ return d->m_parent;
+}
+
+void KXMLGUIClient::insertChildClient( KXMLGUIClient *child )
+{
+ if ( child->d->m_parent )
+ child->d->m_parent->removeChildClient( child );
+ d->m_children.append( child );
+ child->d->m_parent = this;
+}
+
+void KXMLGUIClient::removeChildClient( KXMLGUIClient *child )
+{
+ assert( d->m_children.containsRef( child ) );
+ d->m_children.removeRef( child );
+ child->d->m_parent = 0;
+}
+
+/*bool KXMLGUIClient::addSuperClient( KXMLGUIClient *super )
+{
+ if ( d->m_supers.contains( super ) )
+ return false;
+ d->m_supers.append( super );
+ return true;
+}*/
+
+const QPtrList<KXMLGUIClient> *KXMLGUIClient::childClients()
+{
+ return &d->m_children;
+}
+
+void KXMLGUIClient::setClientBuilder( KXMLGUIBuilder *builder )
+{
+ d->m_builder = builder;
+ if ( builder )
+ builder->setBuilderInstance( instance() );
+}
+
+KXMLGUIBuilder *KXMLGUIClient::clientBuilder() const
+{
+ return d->m_builder;
+}
+
+void KXMLGUIClient::plugActionList( const QString &name, const QPtrList<KAction> &actionList )
+{
+ if ( !d->m_factory )
+ return;
+
+ d->m_factory->plugActionList( this, name, actionList );
+}
+
+void KXMLGUIClient::unplugActionList( const QString &name )
+{
+ if ( !d->m_factory )
+ return;
+
+ d->m_factory->unplugActionList( this, name );
+}
+
+QString KXMLGUIClient::findMostRecentXMLFile( const QStringList &files, QString &doc )
+{
+
+ QValueList<DocStruct> allDocuments;
+
+ QStringList::ConstIterator it = files.begin();
+ QStringList::ConstIterator end = files.end();
+ for (; it != end; ++it )
+ {
+ //kdDebug() << "KXMLGUIClient::findMostRecentXMLFile " << *it << endl;
+ QString data = KXMLGUIFactory::readConfigFile( *it );
+ DocStruct d;
+ d.file = *it;
+ d.data = data;
+ allDocuments.append( d );
+ }
+
+ QValueList<DocStruct>::Iterator best = allDocuments.end();
+ uint bestVersion = 0;
+
+ QValueList<DocStruct>::Iterator docIt = allDocuments.begin();
+ QValueList<DocStruct>::Iterator docEnd = allDocuments.end();
+ for (; docIt != docEnd; ++docIt )
+ {
+ QString versionStr = findVersionNumber( (*docIt).data );
+ if ( versionStr.isEmpty() )
+ continue;
+
+ bool ok = false;
+ uint version = versionStr.toUInt( &ok );
+ if ( !ok )
+ continue;
+ //kdDebug() << "FOUND VERSION " << version << endl;
+
+ if ( version > bestVersion )
+ {
+ best = docIt;
+ //kdDebug() << "best version is now " << version << endl;
+ bestVersion = version;
+ }
+ }
+
+ if ( best != docEnd )
+ {
+ if ( best != allDocuments.begin() )
+ {
+ QValueList<DocStruct>::Iterator local = allDocuments.begin();
+
+ // load the local document and extract the action properties
+ QDomDocument document;
+ document.setContent( (*local).data );
+
+ ActionPropertiesMap properties = extractActionProperties( document );
+
+ // in case the document has a ActionProperties section
+ // we must not delete it but copy over the global doc
+ // to the local and insert the ActionProperties section
+ if ( !properties.isEmpty() )
+ {
+ // now load the global one with the higher version number
+ // into memory
+ document.setContent( (*best).data );
+ // and store the properties in there
+ storeActionProperties( document, properties );
+
+ (*local).data = document.toString();
+ // make sure we pick up the new local doc, when we return later
+ best = local;
+
+ // write out the new version of the local document
+ QFile f( (*local).file );
+ if ( f.open( IO_WriteOnly ) )
+ {
+ QCString utf8data = (*local).data.utf8();
+ f.writeBlock( utf8data.data(), utf8data.length() );
+ f.close();
+ }
+ }
+ else
+ {
+ QString f = (*local).file;
+ QString backup = f + QString::fromLatin1( ".backup" );
+ QDir dir;
+ dir.rename( f, backup );
+ }
+ }
+ doc = (*best).data;
+ return (*best).file;
+ }
+ else if ( files.count() > 0 )
+ {
+ //kdDebug() << "returning first one..." << endl;
+ doc = (*allDocuments.begin()).data;
+ return (*allDocuments.begin()).file;
+ }
+
+ return QString::null;
+}
+
+
+
+QString KXMLGUIClient::findVersionNumber( const QString &xml )
+{
+ enum { ST_START, ST_AFTER_OPEN, ST_AFTER_GUI,
+ ST_EXPECT_VERSION, ST_VERSION_NUM} state = ST_START;
+ for (unsigned int pos = 0; pos < xml.length(); pos++)
+ {
+ switch (state)
+ {
+ case ST_START:
+ if (xml[pos] == '<')
+ state = ST_AFTER_OPEN;
+ break;
+ case ST_AFTER_OPEN:
+ {
+ //Jump to gui..
+ int guipos = xml.find("gui", pos, false /*case-insensitive*/);
+ if (guipos == -1)
+ return QString::null; //Reject
+
+ pos = guipos + 2; //Position at i, so we're moved ahead to the next character by the ++;
+ state = ST_AFTER_GUI;
+ break;
+ }
+ case ST_AFTER_GUI:
+ state = ST_EXPECT_VERSION;
+ break;
+ case ST_EXPECT_VERSION:
+ {
+ int verpos = xml.find("version=\"", pos, false /*case-insensitive*/);
+ if (verpos == -1)
+ return QString::null; //Reject
+
+ pos = verpos + 8; //v = 0, e = +1, r = +2, s = +3 , i = +4, o = +5, n = +6, = = +7, " = + 8
+ state = ST_VERSION_NUM;
+ break;
+ }
+ case ST_VERSION_NUM:
+ {
+ unsigned int endpos;
+ for (endpos = pos; endpos < xml.length(); endpos++)
+ {
+ if (xml[endpos].unicode() >= '0' && xml[endpos].unicode() <= '9')
+ continue; //Number..
+ if (xml[endpos].unicode() == '"') //End of parameter
+ break;
+ else //This shouldn't be here..
+ {
+ endpos = xml.length();
+ }
+ }
+
+ if (endpos != pos && endpos < xml.length() )
+ {
+ QString matchCandidate = xml.mid(pos, endpos - pos); //Don't include " ".
+ return matchCandidate;
+ }
+
+ state = ST_EXPECT_VERSION; //Try to match a well-formed version..
+ break;
+ } //case..
+ } //switch
+ } //for
+
+ return QString::null;
+}
+
+KXMLGUIClient::ActionPropertiesMap KXMLGUIClient::extractActionProperties( const QDomDocument &doc )
+{
+ ActionPropertiesMap properties;
+
+ QDomElement actionPropElement = doc.documentElement().namedItem( "ActionProperties" ).toElement();
+
+ if ( actionPropElement.isNull() )
+ return properties;
+
+ QDomNode n = actionPropElement.firstChild();
+ while(!n.isNull())
+ {
+ QDomElement e = n.toElement();
+ n = n.nextSibling(); // Advance now so that we can safely delete e
+ if ( e.isNull() )
+ continue;
+
+ if ( e.tagName().lower() != "action" )
+ continue;
+
+ QString actionName = e.attribute( "name" );
+
+ if ( actionName.isEmpty() )
+ continue;
+
+ QMap<QString, QMap<QString, QString> >::Iterator propIt = properties.find( actionName );
+ if ( propIt == properties.end() )
+ propIt = properties.insert( actionName, QMap<QString, QString>() );
+
+ const QDomNamedNodeMap attributes = e.attributes();
+ const uint attributeslength = attributes.length();
+
+ for ( uint i = 0; i < attributeslength; ++i )
+ {
+ const QDomAttr attr = attributes.item( i ).toAttr();
+
+ if ( attr.isNull() )
+ continue;
+
+ const QString name = attr.name();
+
+ if ( name == "name" || name.isEmpty() )
+ continue;
+
+ (*propIt)[ name ] = attr.value();
+ }
+
+ }
+
+ return properties;
+}
+
+void KXMLGUIClient::storeActionProperties( QDomDocument &doc, const ActionPropertiesMap &properties )
+{
+ QDomElement actionPropElement = doc.documentElement().namedItem( "ActionProperties" ).toElement();
+
+ if ( actionPropElement.isNull() )
+ {
+ actionPropElement = doc.createElement( "ActionProperties" );
+ doc.documentElement().appendChild( actionPropElement );
+ }
+
+ while ( !actionPropElement.firstChild().isNull() )
+ actionPropElement.removeChild( actionPropElement.firstChild() );
+
+ ActionPropertiesMap::ConstIterator it = properties.begin();
+ ActionPropertiesMap::ConstIterator end = properties.end();
+ for (; it != end; ++it )
+ {
+ QDomElement action = doc.createElement( "Action" );
+ action.setAttribute( "name", it.key() );
+ actionPropElement.appendChild( action );
+
+ QMap<QString, QString> attributes = (*it);
+ QMap<QString, QString>::ConstIterator attrIt = attributes.begin();
+ QMap<QString, QString>::ConstIterator attrEnd = attributes.end();
+ for (; attrIt != attrEnd; ++attrIt )
+ action.setAttribute( attrIt.key(), attrIt.data() );
+ }
+}
+
+void KXMLGUIClient::addStateActionEnabled(const QString& state,
+ const QString& action)
+{
+ StateChange stateChange = getActionsToChangeForState(state);
+
+ stateChange.actionsToEnable.append( action );
+ //kdDebug() << "KXMLGUIClient::addStateActionEnabled( " << state << ", " << action << ")" << endl;
+
+ m_actionsStateMap.replace( state, stateChange );
+}
+
+
+void KXMLGUIClient::addStateActionDisabled(const QString& state,
+ const QString& action)
+{
+ StateChange stateChange = getActionsToChangeForState(state);
+
+ stateChange.actionsToDisable.append( action );
+ //kdDebug() << "KXMLGUIClient::addStateActionDisabled( " << state << ", " << action << ")" << endl;
+
+ m_actionsStateMap.replace( state, stateChange );
+}
+
+
+KXMLGUIClient::StateChange KXMLGUIClient::getActionsToChangeForState(const QString& state)
+{
+ return m_actionsStateMap[state];
+}
+
+
+void KXMLGUIClient::stateChanged(const QString &newstate, KXMLGUIClient::ReverseStateChange reverse)
+{
+ StateChange stateChange = getActionsToChangeForState(newstate);
+
+ bool setTrue = (reverse == StateNoReverse);
+ bool setFalse = !setTrue;
+
+ // Enable actions which need to be enabled...
+ //
+ for ( QStringList::Iterator it = stateChange.actionsToEnable.begin();
+ it != stateChange.actionsToEnable.end(); ++it ) {
+
+ KAction *action = actionCollection()->action((*it).latin1());
+ if (action) action->setEnabled(setTrue);
+ }
+
+ // and disable actions which need to be disabled...
+ //
+ for ( QStringList::Iterator it = stateChange.actionsToDisable.begin();
+ it != stateChange.actionsToDisable.end(); ++it ) {
+
+ KAction *action = actionCollection()->action((*it).latin1());
+ if (action) action->setEnabled(setFalse);
+ }
+
+}
+
+void KXMLGUIClient::beginXMLPlug( QWidget *w )
+{
+ actionCollection()->beginXMLPlug( w );
+ QPtrListIterator<KXMLGUIClient> childIt( d->m_children );
+ for (; childIt.current(); ++childIt )
+ childIt.current()->actionCollection()->beginXMLPlug( w );
+}
+
+void KXMLGUIClient::endXMLPlug()
+{
+ actionCollection()->endXMLPlug();
+ QPtrListIterator<KXMLGUIClient> childIt( d->m_children );
+ for (; childIt.current(); ++childIt )
+ childIt.current()->actionCollection()->endXMLPlug();
+}
+
+void KXMLGUIClient::prepareXMLUnplug( QWidget * )
+{
+ actionCollection()->prepareXMLUnplug();
+ QPtrListIterator<KXMLGUIClient> childIt( d->m_children );
+ for (; childIt.current(); ++childIt )
+ childIt.current()->actionCollection()->prepareXMLUnplug();
+}
+
+void KXMLGUIClient::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }