kdeui Library API Documentation

kxmlguifactory.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 1999,2000 Simon Hausmann <hausmann@kde.org>
00003    Copyright (C) 2000 Kurt Granroth <granroth@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include "kxmlguifactory.h"
00022 #include "kxmlguifactory_p.h"
00023 #include "kxmlguiclient.h"
00024 #include "kxmlguibuilder.h"
00025 
00026 #include <assert.h>
00027 
00028 #include <qfile.h>
00029 #include <qtextstream.h>
00030 #include <qwidget.h>
00031 #include <qdatetime.h>
00032 #include <qvariant.h>
00033 
00034 #include <kaction.h>
00035 #include <kdebug.h>
00036 #include <kinstance.h>
00037 #include <kglobal.h>
00038 #include <kshortcut.h>
00039 #include <kstandarddirs.h>
00040 
00041 using namespace KXMLGUI;
00042 
00043 /*
00044  * TODO:     - make more use of QValueList instead of QPtrList
00045  */
00046 
00047 class KXMLGUIFactoryPrivate : public BuildState
00048 {
00049 public:
00050     KXMLGUIFactoryPrivate()
00051     {
00052         static const QString &defaultMergingName = KGlobal::staticQString( "<default>" );
00053         static const QString &actionList = KGlobal::staticQString( "actionlist" );
00054         static const QString &name = KGlobal::staticQString( "name" );
00055 
00056         m_rootNode = new ContainerNode( 0L, QString::null, 0L );
00057         m_defaultMergingName = defaultMergingName;
00058         tagActionList = actionList;
00059         attrName = name;
00060     }
00061     ~KXMLGUIFactoryPrivate()
00062     {
00063         delete m_rootNode;
00064     }
00065 
00066     void pushState()
00067     {
00068         m_stateStack.push( *this );
00069     }
00070 
00071     void popState()
00072     {
00073         BuildState::operator=( m_stateStack.pop() );
00074     }
00075 
00076     ContainerNode *m_rootNode;
00077 
00078     QString m_defaultMergingName;
00079 
00080     /*
00081      * Contains the container which is searched for in ::container .
00082      */
00083     QString m_containerName;
00084 
00085     /*
00086      * List of all clients
00087      */
00088     QPtrList<KXMLGUIClient> m_clients;
00089 
00090     QString tagActionList;
00091 
00092     QString attrName;
00093 
00094     BuildStateStack m_stateStack;
00095 };
00096 
00097 QString KXMLGUIFactory::readConfigFile( const QString &filename, const KInstance *instance )
00098 {
00099     return readConfigFile( filename, false, instance );
00100 }
00101 
00102 QString KXMLGUIFactory::readConfigFile( const QString &filename, bool never_null, const KInstance *_instance )
00103 {
00104     const KInstance *instance = _instance ? _instance : KGlobal::instance();
00105     QString xml_file;
00106 
00107     if (filename[0] == '/')
00108         xml_file = filename;
00109     else
00110     {
00111         xml_file = locate("data", QString::fromLatin1(instance->instanceName() + '/' ) + filename);
00112         if ( !QFile::exists( xml_file ) )
00113           xml_file = locate( "data", filename );
00114     }
00115 
00116     QFile file( xml_file );
00117     if ( !file.open( IO_ReadOnly ) )
00118     {
00119         kdError(1000) << "No such XML file " << filename << endl;
00120         if ( never_null )
00121             return QString::fromLatin1( "<!DOCTYPE kpartgui>\n<kpartgui name=\"empty\">\n</kpartgui>" );
00122         else
00123             return QString::null;
00124     }
00125 
00126     QByteArray buffer(file.readAll());
00127     return QString::fromUtf8(buffer.data(), buffer.size());
00128 }
00129 
00130 bool KXMLGUIFactory::saveConfigFile( const QDomDocument& doc,
00131                                      const QString& filename, const KInstance *_instance )
00132 {
00133     const KInstance *instance = _instance ? _instance : KGlobal::instance();
00134     QString xml_file(filename);
00135 
00136     if (xml_file[0] != '/')
00137         xml_file = locateLocal("data", QString::fromLatin1( instance->instanceName() + '/' )
00138                                + filename);
00139 
00140     QFile file( xml_file );
00141     if ( !file.open( IO_WriteOnly ) )
00142     {
00143         kdError(1000) << "Could not write to " << filename << endl;
00144         return false;
00145     }
00146 
00147     // write out our document
00148     QTextStream ts(&file);
00149     ts.setEncoding( QTextStream::UnicodeUTF8 );
00150     ts << doc;
00151 
00152     file.close();
00153     return true;
00154 }
00155 
00156 QString KXMLGUIFactory::documentToXML( const QDomDocument& doc )
00157 {
00158     QString str;
00159     QTextStream ts(&str, IO_WriteOnly);
00160     ts.setEncoding( QTextStream::UnicodeUTF8 );
00161     ts << doc;
00162     return str;
00163 }
00164 
00165 QString KXMLGUIFactory::elementToXML( const QDomElement& elem )
00166 {
00167     QString str;
00168     QTextStream ts(&str, IO_WriteOnly);
00169     ts.setEncoding( QTextStream::UnicodeUTF8 );
00170     ts << elem;
00171     return str;
00172 }
00173 
00174 void KXMLGUIFactory::removeDOMComments( QDomNode &node )
00175 {
00176     QDomNode n = node.firstChild();
00177     while ( !n.isNull() )
00178     {
00179         if ( n.nodeType() == QDomNode::CommentNode )
00180         {
00181             QDomNode tmp = n;
00182             n = n.nextSibling();
00183             node.removeChild( tmp );
00184         }
00185         else
00186         {
00187             QDomNode tmp = n;
00188             n = n.nextSibling();
00189             removeDOMComments( tmp );
00190         }
00191     }
00192 }
00193 
00194 KXMLGUIFactory::KXMLGUIFactory( KXMLGUIBuilder *builder, QObject *parent, const char *name )
00195     : QObject( parent, name )
00196 {
00197     d = new KXMLGUIFactoryPrivate;
00198     d->builder = builder;
00199     d->guiClient = 0;
00200     if ( d->builder )
00201     {
00202         d->builderContainerTags = d->builder->containerTags();
00203         d->builderCustomTags = d->builder->customTags();
00204     }
00205 }
00206 
00207 KXMLGUIFactory::~KXMLGUIFactory()
00208 {
00209     delete d;
00210 }
00211 
00212 void KXMLGUIFactory::addClient( KXMLGUIClient *client )
00213 {
00214     kdDebug(129) << "KXMLGUIFactory::addClient( " << client << " )" << endl; // ellis
00215     static const QString &actionPropElementName = KGlobal::staticQString( "ActionProperties" );
00216 
00217     if ( client->factory() ) {
00218         if ( client->factory() == this )
00219             return;
00220         else
00221             client->factory()->removeClient( client ); //just in case someone does stupid things ;-)
00222     }
00223 
00224     d->pushState();
00225 
00226 //    QTime dt; dt.start();
00227 
00228     d->guiClient = client;
00229 
00230     // add this client to our client list
00231     if ( d->m_clients.containsRef( client ) == 0 )
00232         d->m_clients.append( client );
00233     else
00234         kdDebug(129) << "XMLGUI client already added " << client << endl;
00235 
00236     // Tell the client that plugging in is process and
00237     //  let it know what builder widget its mainwindow shortcuts
00238     //  should be attached to.
00239     client->beginXMLPlug( d->builder->widget() );
00240 
00241     // try to use the build document for building the client's GUI, as the build document
00242     // contains the correct container state information (like toolbar positions, sizes, etc.) .
00243     // if there is non available, then use the "real" document.
00244     QDomDocument doc = client->xmlguiBuildDocument();
00245     if ( doc.documentElement().isNull() )
00246         doc = client->domDocument();
00247 
00248     QDomElement docElement = doc.documentElement();
00249 
00250     d->m_rootNode->index = -1;
00251 
00252     // cache some variables
00253 
00254     d->clientName = docElement.attribute( d->attrName );
00255     d->clientBuilder = client->clientBuilder();
00256 
00257     if ( d->clientBuilder )
00258     {
00259         d->clientBuilderContainerTags = d->clientBuilder->containerTags();
00260         d->clientBuilderCustomTags = d->clientBuilder->customTags();
00261     }
00262     else
00263     {
00264         d->clientBuilderContainerTags.clear();
00265         d->clientBuilderCustomTags.clear();
00266     }
00267 
00268     // process a possibly existing actionproperties section
00269 
00270     QDomElement actionPropElement = docElement.namedItem( actionPropElementName ).toElement();
00271     if ( actionPropElement.isNull() )
00272         actionPropElement = docElement.namedItem( actionPropElementName.lower() ).toElement();
00273 
00274     if ( !actionPropElement.isNull() )
00275         applyActionProperties( actionPropElement );
00276 
00277     BuildHelper( *d, d->m_rootNode ).build( docElement );
00278 
00279     // let the client know that we built its GUI.
00280     client->setFactory( this );
00281 
00282     // call the finalizeGUI method, to fix up the positions of toolbars for example.
00283     // ### FIXME : obey client builder
00284     // --- Well, toolbars have a bool "positioned", so it doesn't really matter,
00285     // if we call positionYourself on all of them each time. (David)
00286     d->builder->finalizeGUI( d->guiClient );
00287 
00288     // reset some variables, for safety
00289     d->BuildState::reset();
00290 
00291     client->endXMLPlug();
00292 
00293     d->popState();
00294 
00295     emit clientAdded( client );
00296 
00297     // build child clients
00298     if ( client->childClients()->count() > 0 )
00299     {
00300         const QPtrList<KXMLGUIClient> *children = client->childClients();
00301         QPtrListIterator<KXMLGUIClient> childIt( *children );
00302         for (; childIt.current(); ++childIt )
00303             addClient( childIt.current() );
00304     }
00305 
00306 //    kdDebug() << "addClient took " << dt.elapsed() << endl;
00307 }
00308 
00309 void KXMLGUIFactory::removeClient( KXMLGUIClient *client )
00310 {
00311     kdDebug(129) << "KXMLGUIFactory::removeClient( " << client << " )" << endl; // ellis
00312 
00313     // don't try to remove the client's GUI if we didn't build it
00314     if ( !client || client->factory() != this )
00315         return;
00316 
00317     // remove this client from our client list
00318     d->m_clients.removeRef( client );
00319 
00320     // remove child clients first
00321     if ( client->childClients()->count() > 0 )
00322     {
00323         const QPtrList<KXMLGUIClient> *children = client->childClients();
00324         QPtrListIterator<KXMLGUIClient> childIt( *children );
00325         childIt.toLast();
00326         for (; childIt.current(); --childIt )
00327             removeClient( childIt.current() );
00328     }
00329 
00330     kdDebug(1002) << "KXMLGUIFactory::removeServant, calling removeRecursive" << endl;
00331 
00332     d->pushState();
00333 
00334     // cache some variables
00335 
00336     d->guiClient = client;
00337     d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00338     d->clientBuilder = client->clientBuilder();
00339 
00340     client->setFactory( 0L );
00341 
00342     // if we don't have a build document for that client, yet, then create one by
00343     // cloning the original document, so that saving container information in the
00344     // DOM tree does not touch the original document.
00345     QDomDocument doc = client->xmlguiBuildDocument();
00346     if ( doc.documentElement().isNull() )
00347     {
00348         doc = client->domDocument().cloneNode( true ).toDocument();
00349         client->setXMLGUIBuildDocument( doc );
00350     }
00351 
00352     d->m_rootNode->destruct( doc.documentElement(), *d );
00353 
00354     d->builder->finalizeGUI( d->guiClient ); //JoWenn
00355 
00356     // reset some variables
00357     d->BuildState::reset();
00358 
00359     // This will destruct the KAccel object built around the given widget.
00360     client->prepareXMLUnplug( d->builder->widget() );
00361 
00362     d->popState();
00363 
00364     emit clientRemoved( client );
00365 }
00366 
00367 QPtrList<KXMLGUIClient> KXMLGUIFactory::clients() const
00368 {
00369     return d->m_clients;
00370 }
00371 
00372 QWidget *KXMLGUIFactory::container( const QString &containerName, KXMLGUIClient *client,
00373                                     bool useTagName )
00374 {
00375     d->pushState();
00376     d->m_containerName = containerName;
00377     d->guiClient = client;
00378 
00379     QWidget *result = findRecursive( d->m_rootNode, useTagName );
00380 
00381     d->guiClient = 0L;
00382     d->m_containerName = QString::null;
00383 
00384     d->popState();
00385 
00386     return result;
00387 }
00388 
00389 QPtrList<QWidget> KXMLGUIFactory::containers( const QString &tagName )
00390 {
00391     return findRecursive( d->m_rootNode, tagName );
00392 }
00393 
00394 void KXMLGUIFactory::reset()
00395 {
00396     d->m_rootNode->reset();
00397 
00398     d->m_rootNode->clearChildren();
00399 }
00400 
00401 void KXMLGUIFactory::resetContainer( const QString &containerName, bool useTagName )
00402 {
00403     if ( containerName.isEmpty() )
00404         return;
00405 
00406     ContainerNode *container = d->m_rootNode->findContainer( containerName, useTagName );
00407 
00408     if ( !container )
00409         return;
00410 
00411     ContainerNode *parent = container->parent;
00412     if ( !parent )
00413         return;
00414 
00415     //  resetInternal( container );
00416 
00417     parent->removeChild( container );
00418 }
00419 
00420 QWidget *KXMLGUIFactory::findRecursive( KXMLGUI::ContainerNode *node, bool tag )
00421 {
00422     if ( ( ( !tag && node->name == d->m_containerName ) ||
00423            ( tag && node->tagName == d->m_containerName ) ) &&
00424          ( !d->guiClient || node->client == d->guiClient ) )
00425         return node->container;
00426 
00427     QPtrListIterator<ContainerNode> it( node->children );
00428     for (; it.current(); ++it )
00429     {
00430         QWidget *cont = findRecursive( it.current(), tag );
00431         if ( cont )
00432             return cont;
00433     }
00434 
00435     return 0L;
00436 }
00437 
00438 QPtrList<QWidget> KXMLGUIFactory::findRecursive( KXMLGUI::ContainerNode *node,
00439                                                  const QString &tagName )
00440 {
00441     QPtrList<QWidget> res;
00442 
00443     if ( node->tagName == tagName.lower() )
00444         res.append( node->container );
00445 
00446     QPtrListIterator<KXMLGUI::ContainerNode> it( node->children );
00447     for (; it.current(); ++it )
00448     {
00449         QPtrList<QWidget> lst = findRecursive( it.current(), tagName );
00450         QPtrListIterator<QWidget> wit( lst );
00451         for (; wit.current(); ++wit )
00452             res.append( wit.current() );
00453     }
00454 
00455     return res;
00456 }
00457 
00458 void KXMLGUIFactory::plugActionList( KXMLGUIClient *client, const QString &name,
00459                                      const QPtrList<KAction> &actionList )
00460 {
00461     d->pushState();
00462     d->guiClient = client;
00463     d->actionListName = name;
00464     d->actionList = actionList;
00465     d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00466 
00467     d->m_rootNode->plugActionList( *d );
00468 
00469     d->BuildState::reset();
00470     d->popState();
00471 }
00472 
00473 void KXMLGUIFactory::unplugActionList( KXMLGUIClient *client, const QString &name )
00474 {
00475     d->pushState();
00476     d->guiClient = client;
00477     d->actionListName = name;
00478     d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00479 
00480     d->m_rootNode->unplugActionList( *d );
00481 
00482     d->BuildState::reset();
00483     d->popState();
00484 }
00485 
00486 void KXMLGUIFactory::applyActionProperties( const QDomElement &actionPropElement )
00487 {
00488     static const QString &tagAction = KGlobal::staticQString( "action" );
00489 
00490     QDomElement e = actionPropElement.firstChild().toElement();
00491     for (; !e.isNull(); e = e.nextSibling().toElement() )
00492     {
00493         if ( e.tagName().lower() != tagAction )
00494             continue;
00495 
00496         KAction *action = d->guiClient->action( e );
00497         if ( !action )
00498             continue;
00499 
00500         configureAction( action, e.attributes() );
00501     }
00502 }
00503 
00504 void KXMLGUIFactory::configureAction( KAction *action, const QDomNamedNodeMap &attributes )
00505 {
00506     for ( uint i = 0; i < attributes.length(); i++ )
00507     {
00508         QDomAttr attr = attributes.item( i ).toAttr();
00509         if ( attr.isNull() )
00510             continue;
00511 
00512         configureAction( action, attr );
00513     }
00514 }
00515 
00516 void KXMLGUIFactory::configureAction( KAction *action, const QDomAttr &attribute )
00517 {
00518     static const QString &attrShortcut = KGlobal::staticQString( "shortcut" );
00519 
00520     QString attrName = attribute.name();
00521 
00522     QVariant propertyValue;
00523 
00524     QVariant::Type propertyType = action->property( attribute.name().latin1() ).type();
00525 
00526     // If the attribute is a deprecated "accel", change to "shortcut".
00527     if ( attrName.lower() == "accel" )
00528         attrName = attrShortcut;
00529 
00530     if ( propertyType == QVariant::Int )
00531         propertyValue = QVariant( attribute.value().toInt() );
00532     else if ( propertyType == QVariant::UInt )
00533         propertyValue = QVariant( attribute.value().toUInt() );
00534     else
00535         propertyValue = QVariant( attribute.value() );
00536 
00537     action->setProperty( attrName.latin1() /* ???????? */, propertyValue );
00538 }
00539 
00540 void KXMLGUIFactory::virtual_hook( int, void* )
00541 { /*BASE::virtual_hook( id, data );*/ }
00542 
00543 #include "kxmlguifactory.moc"
00544 
00545 /* vim: et sw=4
00546  */
KDE Logo
This file is part of the documentation for kdeui Library Version 3.2.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Mar 4 22:43:55 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003