diff options
Diffstat (limited to 'tdeui/kxmlguifactory.cpp')
-rw-r--r-- | tdeui/kxmlguifactory.cpp | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/tdeui/kxmlguifactory.cpp b/tdeui/kxmlguifactory.cpp new file mode 100644 index 000000000..2714b1207 --- /dev/null +++ b/tdeui/kxmlguifactory.cpp @@ -0,0 +1,614 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999,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 as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "kxmlguifactory.h" +#include "kxmlguifactory_p.h" +#include "kxmlguiclient.h" +#include "kxmlguibuilder.h" + +#include <assert.h> + +#include <tqdir.h> +#include <tqfile.h> +#include <tqtextstream.h> +#include <tqwidget.h> +#include <tqdatetime.h> +#include <tqvariant.h> + +#include <kaction.h> +#include <kdebug.h> +#include <kinstance.h> +#include <kglobal.h> +#include <kshortcut.h> +#include <kstandarddirs.h> +#include <kkeydialog.h> + +using namespace KXMLGUI; + +/* + * TODO: - make more use of TQValueList instead of QPtrList + */ + +class KXMLGUIFactoryPrivate : public BuildState +{ +public: + KXMLGUIFactoryPrivate() + { + static const TQString &defaultMergingName = KGlobal::staticQString( "<default>" ); + static const TQString &actionList = KGlobal::staticQString( "actionlist" ); + static const TQString &name = KGlobal::staticQString( "name" ); + + m_rootNode = new ContainerNode( 0L, TQString::null, 0L ); + m_defaultMergingName = defaultMergingName; + tagActionList = actionList; + attrName = name; + } + ~KXMLGUIFactoryPrivate() + { + delete m_rootNode; + } + + void pushState() + { + m_stateStack.push( *this ); + } + + void popState() + { + BuildState::operator=( m_stateStack.pop() ); + } + + ContainerNode *m_rootNode; + + TQString m_defaultMergingName; + + /* + * Contains the container which is searched for in ::container . + */ + TQString m_containerName; + + /* + * List of all clients + */ + TQPtrList<KXMLGUIClient> m_clients; + + TQString tagActionList; + + TQString attrName; + + BuildStateStack m_stateStack; +}; + +TQString KXMLGUIFactory::readConfigFile( const TQString &filename, const KInstance *instance ) +{ + return readConfigFile( filename, false, instance ); +} + +TQString KXMLGUIFactory::readConfigFile( const TQString &filename, bool never_null, const KInstance *_instance ) +{ + const KInstance *instance = _instance ? _instance : KGlobal::instance(); + TQString xml_file; + + if (!TQDir::isRelativePath(filename)) + xml_file = filename; + else + { + xml_file = locate("data", TQString::tqfromLatin1(instance->instanceName() + '/' ) + filename); + if ( !TQFile::exists( xml_file ) ) + xml_file = locate( "data", filename ); + } + + TQFile file( xml_file ); + if ( !file.open( IO_ReadOnly ) ) + { + kdError(240) << "No such XML file " << filename << endl; + if ( never_null ) + return TQString::tqfromLatin1( "<!DOCTYPE kpartgui>\n<kpartgui name=\"empty\">\n</kpartgui>" ); + else + return TQString::null; + } + +#if QT_VERSION <= 0x030302 + // Work around bug in TQString::fromUtf8 (which calls strlen). + TQByteArray buffer(file.size() + 1); + buffer = file.readAll(); + if(!buffer.isEmpty()) + buffer[ buffer.size() - 1 ] = '\0'; + else + return TQString::null; +#else + TQByteArray buffer(file.readAll()); +#endif + return TQString::fromUtf8(buffer.data(), buffer.size()); +} + +bool KXMLGUIFactory::saveConfigFile( const TQDomDocument& doc, + const TQString& filename, const KInstance *_instance ) +{ + const KInstance *instance = _instance ? _instance : KGlobal::instance(); + TQString xml_file(filename); + + if (TQDir::isRelativePath(xml_file)) + xml_file = locateLocal("data", TQString::tqfromLatin1( instance->instanceName() + '/' ) + + filename); + + TQFile file( xml_file ); + if ( !file.open( IO_WriteOnly ) ) + { + kdError(240) << "Could not write to " << filename << endl; + return false; + } + + // write out our document + TQTextStream ts(&file); + ts.setEncoding( TQTextStream::UnicodeUTF8 ); + ts << doc; + + file.close(); + return true; +} + +TQString KXMLGUIFactory::documentToXML( const TQDomDocument& doc ) +{ + TQString str; + TQTextStream ts(&str, IO_WriteOnly); + ts.setEncoding( TQTextStream::UnicodeUTF8 ); + ts << doc; + return str; +} + +TQString KXMLGUIFactory::elementToXML( const TQDomElement& elem ) +{ + TQString str; + TQTextStream ts(&str, IO_WriteOnly); + ts.setEncoding( TQTextStream::UnicodeUTF8 ); + ts << elem; + return str; +} + +void KXMLGUIFactory::removeDOMComments( TQDomNode &node ) +{ + TQDomNode n = node.firstChild(); + while ( !n.isNull() ) + { + if ( n.nodeType() == TQDomNode::CommentNode ) + { + TQDomNode tmp = n; + n = n.nextSibling(); + node.removeChild( tmp ); + } + else + { + TQDomNode tmp = n; + n = n.nextSibling(); + removeDOMComments( tmp ); + } + } +} + +KXMLGUIFactory::KXMLGUIFactory( KXMLGUIBuilder *builder, TQObject *parent, const char *name ) + : TQObject( parent, name ) +{ + d = new KXMLGUIFactoryPrivate; + d->builder = builder; + d->guiClient = 0; + if ( d->builder ) + { + d->builderContainerTags = d->builder->containerTags(); + d->builderCustomTags = d->builder->customTags(); + } +} + +KXMLGUIFactory::~KXMLGUIFactory() +{ + delete d; +} + +void KXMLGUIFactory::addClient( KXMLGUIClient *client ) +{ + kdDebug(1002) << "KXMLGUIFactory::addClient( " << client << " )" << endl; // ellis + static const TQString &actionPropElementName = KGlobal::staticQString( "ActionProperties" ); + + if ( client->factory() ) { + if ( client->factory() == this ) + return; + else + client->factory()->removeClient( client ); //just in case someone does stupid things ;-) + } + + d->pushState(); + +// TQTime dt; dt.start(); + + d->guiClient = client; + + // add this client to our client list + if ( !d->m_clients.containsRef( client ) ) + d->m_clients.append( client ); + else + kdDebug(1002) << "XMLGUI client already added " << client << endl; + + // Tell the client that plugging in is process and + // let it know what builder widget its mainwindow shortcuts + // should be attached to. + client->beginXMLPlug( d->builder->widget() ); + + // try to use the build document for building the client's GUI, as the build document + // contains the correct container state information (like toolbar positions, sizes, etc.) . + // if there is non available, then use the "real" document. + TQDomDocument doc = client->xmlguiBuildDocument(); + if ( doc.documentElement().isNull() ) + doc = client->domDocument(); + + TQDomElement docElement = doc.documentElement(); + + d->m_rootNode->index = -1; + + // cache some variables + + d->clientName = docElement.attribute( d->attrName ); + d->clientBuilder = client->clientBuilder(); + + if ( d->clientBuilder ) + { + d->clientBuilderContainerTags = d->clientBuilder->containerTags(); + d->clientBuilderCustomTags = d->clientBuilder->customTags(); + } + else + { + d->clientBuilderContainerTags.clear(); + d->clientBuilderCustomTags.clear(); + } + + // process a possibly existing actionproperties section + + TQDomElement actionPropElement = docElement.namedItem( actionPropElementName ).toElement(); + if ( actionPropElement.isNull() ) + actionPropElement = docElement.namedItem( actionPropElementName.lower() ).toElement(); + + if ( !actionPropElement.isNull() ) + applyActionProperties( actionPropElement ); + + BuildHelper( *d, d->m_rootNode ).build( docElement ); + + // let the client know that we built its GUI. + client->setFactory( this ); + + // call the finalizeGUI method, to fix up the positions of toolbars for example. + // ### FIXME : obey client builder + // --- Well, toolbars have a bool "positioned", so it doesn't really matter, + // if we call positionYourself on all of them each time. (David) + d->builder->finalizeGUI( d->guiClient ); + + // reset some variables, for safety + d->BuildState::reset(); + + client->endXMLPlug(); + + d->popState(); + + emit clientAdded( client ); + + // build child clients + if ( client->childClients()->count() > 0 ) + { + const TQPtrList<KXMLGUIClient> *children = client->childClients(); + TQPtrListIterator<KXMLGUIClient> childIt( *children ); + for (; childIt.current(); ++childIt ) + addClient( childIt.current() ); + } + +// kdDebug() << "addClient took " << dt.elapsed() << endl; +} + +void KXMLGUIFactory::removeClient( KXMLGUIClient *client ) +{ + kdDebug(1002) << "KXMLGUIFactory::removeClient( " << client << " )" << endl; // ellis + + // don't try to remove the client's GUI if we didn't build it + if ( !client || client->factory() != this ) + return; + + // remove this client from our client list + d->m_clients.removeRef( client ); + + // remove child clients first + if ( client->childClients()->count() > 0 ) + { + const TQPtrList<KXMLGUIClient> *children = client->childClients(); + TQPtrListIterator<KXMLGUIClient> childIt( *children ); + childIt.toLast(); + for (; childIt.current(); --childIt ) + removeClient( childIt.current() ); + } + + kdDebug(1002) << "KXMLGUIFactory::removeServant, calling removeRecursive" << endl; + + d->pushState(); + + // cache some variables + + d->guiClient = client; + d->clientName = client->domDocument().documentElement().attribute( d->attrName ); + d->clientBuilder = client->clientBuilder(); + + client->setFactory( 0L ); + + // if we don't have a build document for that client, yet, then create one by + // cloning the original document, so that saving container information in the + // DOM tree does not touch the original document. + TQDomDocument doc = client->xmlguiBuildDocument(); + if ( doc.documentElement().isNull() ) + { + doc = client->domDocument().cloneNode( true ).toDocument(); + client->setXMLGUIBuildDocument( doc ); + } + + d->m_rootNode->destruct( doc.documentElement(), *d ); + + d->builder->finalizeGUI( d->guiClient ); //JoWenn + + // reset some variables + d->BuildState::reset(); + + // This will destruct the KAccel object built around the given widget. + client->prepareXMLUnplug( d->builder->widget() ); + + d->popState(); + + emit clientRemoved( client ); +} + +TQPtrList<KXMLGUIClient> KXMLGUIFactory::clients() const +{ + return d->m_clients; +} + +TQWidget *KXMLGUIFactory::container( const TQString &containerName, KXMLGUIClient *client, + bool useTagName ) +{ + d->pushState(); + d->m_containerName = containerName; + d->guiClient = client; + + TQWidget *result = findRecursive( d->m_rootNode, useTagName ); + + d->guiClient = 0L; + d->m_containerName = TQString::null; + + d->popState(); + + return result; +} + +TQPtrList<TQWidget> KXMLGUIFactory::containers( const TQString &tagName ) +{ + return findRecursive( d->m_rootNode, tagName ); +} + +void KXMLGUIFactory::reset() +{ + d->m_rootNode->reset(); + + d->m_rootNode->clearChildren(); +} + +void KXMLGUIFactory::resetContainer( const TQString &containerName, bool useTagName ) +{ + if ( containerName.isEmpty() ) + return; + + ContainerNode *container = d->m_rootNode->findContainer( containerName, useTagName ); + + if ( !container ) + return; + + ContainerNode *parent = container->parent; + if ( !parent ) + return; + + // resetInternal( container ); + + parent->removeChild( container ); +} + +TQWidget *KXMLGUIFactory::findRecursive( KXMLGUI::ContainerNode *node, bool tag ) +{ + if ( ( ( !tag && node->name == d->m_containerName ) || + ( tag && node->tagName == d->m_containerName ) ) && + ( !d->guiClient || node->client == d->guiClient ) ) + return node->container; + + TQPtrListIterator<ContainerNode> it( node->children ); + for (; it.current(); ++it ) + { + TQWidget *cont = findRecursive( it.current(), tag ); + if ( cont ) + return cont; + } + + return 0L; +} + +TQPtrList<TQWidget> KXMLGUIFactory::findRecursive( KXMLGUI::ContainerNode *node, + const TQString &tagName ) +{ + TQPtrList<TQWidget> res; + + if ( node->tagName == tagName.lower() ) + res.append( node->container ); + + TQPtrListIterator<KXMLGUI::ContainerNode> it( node->children ); + for (; it.current(); ++it ) + { + TQPtrList<TQWidget> lst = findRecursive( it.current(), tagName ); + TQPtrListIterator<TQWidget> wit( lst ); + for (; wit.current(); ++wit ) + res.append( wit.current() ); + } + + return res; +} + +void KXMLGUIFactory::plugActionList( KXMLGUIClient *client, const TQString &name, + const TQPtrList<KAction> &actionList ) +{ + d->pushState(); + d->guiClient = client; + d->actionListName = name; + d->actionList = actionList; + d->clientName = client->domDocument().documentElement().attribute( d->attrName ); + + d->m_rootNode->plugActionList( *d ); + + d->BuildState::reset(); + d->popState(); +} + +void KXMLGUIFactory::unplugActionList( KXMLGUIClient *client, const TQString &name ) +{ + d->pushState(); + d->guiClient = client; + d->actionListName = name; + d->clientName = client->domDocument().documentElement().attribute( d->attrName ); + + d->m_rootNode->unplugActionList( *d ); + + d->BuildState::reset(); + d->popState(); +} + +void KXMLGUIFactory::applyActionProperties( const TQDomElement &actionPropElement ) +{ + static const TQString &tagAction = KGlobal::staticQString( "action" ); + + for (TQDomNode n = actionPropElement.firstChild(); + !n.isNull(); n = n.nextSibling() ) + { + TQDomElement e = n.toElement(); + if ( e.tagName().lower() != tagAction ) + continue; + + KAction *action = d->guiClient->action( e ); + if ( !action ) + continue; + + configureAction( action, e.attributes() ); + } +} + +void KXMLGUIFactory::configureAction( KAction *action, const TQDomNamedNodeMap &attributes ) +{ + for ( uint i = 0; i < attributes.length(); i++ ) + { + TQDomAttr attr = attributes.item( i ).toAttr(); + if ( attr.isNull() ) + continue; + + configureAction( action, attr ); + } +} + +void KXMLGUIFactory::configureAction( KAction *action, const TQDomAttr &attribute ) +{ + static const TQString &attrShortcut = KGlobal::staticQString( "shortcut" ); + + TQString attrName = attribute.name(); + // If the attribute is a deprecated "accel", change to "shortcut". + if ( attrName.lower() == "accel" ) + attrName = attrShortcut; + + TQVariant propertyValue; + + TQVariant::Type propertyType = action->property( attrName.latin1() ).type(); + + if ( propertyType == TQVariant::Int ) + propertyValue = TQVariant( attribute.value().toInt() ); + else if ( propertyType == TQVariant::UInt ) + propertyValue = TQVariant( attribute.value().toUInt() ); + else + propertyValue = TQVariant( attribute.value() ); + + action->setProperty( attrName.latin1(), propertyValue ); +} + + +int KXMLGUIFactory::configureShortcuts(bool bAllowLetterShortcuts , bool bSaveSettings ) +{ + KKeyDialog dlg( bAllowLetterShortcuts, tqt_dynamic_cast<TQWidget*>(parent()) ); + TQPtrListIterator<KXMLGUIClient> it( d->m_clients ); + KXMLGUIClient *client; + while( (client=it.current()) !=0 ) + { + ++it; + if(!client->xmlFile().isEmpty()) + dlg.insert( client->actionCollection() ); + } + return dlg.configure(bSaveSettings); +} + +TQDomElement KXMLGUIFactory::actionPropertiesElement( TQDomDocument& doc ) +{ + const TQString tagActionProp = TQString::tqfromLatin1("ActionProperties"); + // first, lets see if we have existing properties + TQDomElement elem; + TQDomNode it = doc.documentElement().firstChild(); + for( ; !it.isNull(); it = it.nextSibling() ) { + TQDomElement e = it.toElement(); + if( e.tagName() == tagActionProp ) { + elem = e; + break; + } + } + + // if there was none, create one + if( elem.isNull() ) { + elem = doc.createElement( tagActionProp ); + doc.documentElement().appendChild( elem ); + } + return elem; +} + +TQDomElement KXMLGUIFactory::findActionByName( TQDomElement& elem, const TQString& sName, bool create ) +{ + static const TQString& attrName = KGlobal::staticQString( "name" ); + static const TQString& tagAction = KGlobal::staticQString( "Action" ); + for( TQDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling() ) { + TQDomElement e = it.toElement(); + if( e.attribute( attrName ) == sName ) + return e; + } + + if( create ) { + TQDomElement act_elem = elem.ownerDocument().createElement( tagAction ); + act_elem.setAttribute( attrName, sName ); + elem.appendChild( act_elem ); + return act_elem; + } + return TQDomElement(); +} + +void KXMLGUIFactory::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +#include "kxmlguifactory.moc" + +/* vim: et sw=4 + */ |