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 #include <kkeydialog.h>
00041 
00042 using namespace KXMLGUI;
00043 
00044 /*
00045  * TODO:     - make more use of QValueList instead of QPtrList
00046  */
00047 
00048 class KXMLGUIFactoryPrivate : public BuildState
00049 {
00050 public:
00051     KXMLGUIFactoryPrivate()
00052     {
00053         static const QString &defaultMergingName = KGlobal::staticQString( "<default>" );
00054         static const QString &actionList = KGlobal::staticQString( "actionlist" );
00055         static const QString &name = KGlobal::staticQString( "name" );
00056 
00057         m_rootNode = new ContainerNode( 0L, QString::null, 0L );
00058         m_defaultMergingName = defaultMergingName;
00059         tagActionList = actionList;
00060         attrName = name;
00061     }
00062     ~KXMLGUIFactoryPrivate()
00063     {
00064         delete m_rootNode;
00065     }
00066 
00067     void pushState()
00068     {
00069         m_stateStack.push( *this );
00070     }
00071 
00072     void popState()
00073     {
00074         BuildState::operator=( m_stateStack.pop() );
00075     }
00076 
00077     ContainerNode *m_rootNode;
00078 
00079     QString m_defaultMergingName;
00080 
00081     /*
00082      * Contains the container which is searched for in ::container .
00083      */
00084     QString m_containerName;
00085 
00086     /*
00087      * List of all clients
00088      */
00089     QPtrList<KXMLGUIClient> m_clients;
00090 
00091     QString tagActionList;
00092 
00093     QString attrName;
00094 
00095     BuildStateStack m_stateStack;
00096 };
00097 
00098 QString KXMLGUIFactory::readConfigFile( const QString &filename, const KInstance *instance )
00099 {
00100     return readConfigFile( filename, false, instance );
00101 }
00102 
00103 QString KXMLGUIFactory::readConfigFile( const QString &filename, bool never_null, const KInstance *_instance )
00104 {
00105     const KInstance *instance = _instance ? _instance : KGlobal::instance();
00106     QString xml_file;
00107 
00108     if (filename[0] == '/')
00109         xml_file = filename;
00110     else
00111     {
00112         xml_file = locate("data", QString::fromLatin1(instance->instanceName() + '/' ) + filename);
00113         if ( !QFile::exists( xml_file ) )
00114           xml_file = locate( "data", filename );
00115     }
00116 
00117     QFile file( xml_file );
00118     if ( !file.open( IO_ReadOnly ) )
00119     {
00120         kdError(240) << "No such XML file " << filename << endl;
00121         if ( never_null )
00122             return QString::fromLatin1( "<!DOCTYPE kpartgui>\n<kpartgui name=\"empty\">\n</kpartgui>" );
00123         else
00124             return QString::null;
00125     }
00126 
00127 #if QT_VERSION <= 0x030302
00128     // Work around bug in QString::fromUtf8 (which calls strlen).
00129     QByteArray buffer(file.size() + 1);
00130     buffer = file.readAll();
00131     if(!buffer.isEmpty())
00132         buffer[ buffer.size() - 1 ] = '\0';
00133     else
00134         return QString::null;
00135 #else
00136     QByteArray buffer(file.readAll());
00137 #endif
00138     return QString::fromUtf8(buffer.data(), buffer.size());
00139 }
00140 
00141 bool KXMLGUIFactory::saveConfigFile( const QDomDocument& doc,
00142                                      const QString& filename, const KInstance *_instance )
00143 {
00144     const KInstance *instance = _instance ? _instance : KGlobal::instance();
00145     QString xml_file(filename);
00146 
00147     if (xml_file[0] != '/')
00148         xml_file = locateLocal("data", QString::fromLatin1( instance->instanceName() + '/' )
00149                                + filename);
00150 
00151     QFile file( xml_file );
00152     if ( !file.open( IO_WriteOnly ) )
00153     {
00154         kdError(240) << "Could not write to " << filename << endl;
00155         return false;
00156     }
00157 
00158     // write out our document
00159     QTextStream ts(&file);
00160     ts.setEncoding( QTextStream::UnicodeUTF8 );
00161     ts << doc;
00162 
00163     file.close();
00164     return true;
00165 }
00166 
00167 QString KXMLGUIFactory::documentToXML( const QDomDocument& doc )
00168 {
00169     QString str;
00170     QTextStream ts(&str, IO_WriteOnly);
00171     ts.setEncoding( QTextStream::UnicodeUTF8 );
00172     ts << doc;
00173     return str;
00174 }
00175 
00176 QString KXMLGUIFactory::elementToXML( const QDomElement& elem )
00177 {
00178     QString str;
00179     QTextStream ts(&str, IO_WriteOnly);
00180     ts.setEncoding( QTextStream::UnicodeUTF8 );
00181     ts << elem;
00182     return str;
00183 }
00184 
00185 void KXMLGUIFactory::removeDOMComments( QDomNode &node )
00186 {
00187     QDomNode n = node.firstChild();
00188     while ( !n.isNull() )
00189     {
00190         if ( n.nodeType() == QDomNode::CommentNode )
00191         {
00192             QDomNode tmp = n;
00193             n = n.nextSibling();
00194             node.removeChild( tmp );
00195         }
00196         else
00197         {
00198             QDomNode tmp = n;
00199             n = n.nextSibling();
00200             removeDOMComments( tmp );
00201         }
00202     }
00203 }
00204 
00205 KXMLGUIFactory::KXMLGUIFactory( KXMLGUIBuilder *builder, QObject *parent, const char *name )
00206     : QObject( parent, name )
00207 {
00208     d = new KXMLGUIFactoryPrivate;
00209     d->builder = builder;
00210     d->guiClient = 0;
00211     if ( d->builder )
00212     {
00213         d->builderContainerTags = d->builder->containerTags();
00214         d->builderCustomTags = d->builder->customTags();
00215     }
00216 }
00217 
00218 KXMLGUIFactory::~KXMLGUIFactory()
00219 {
00220     delete d;
00221 }
00222 
00223 void KXMLGUIFactory::addClient( KXMLGUIClient *client )
00224 {
00225     kdDebug(129) << "KXMLGUIFactory::addClient( " << client << " )" << endl; // ellis
00226     static const QString &actionPropElementName = KGlobal::staticQString( "ActionProperties" );
00227 
00228     if ( client->factory() ) {
00229         if ( client->factory() == this )
00230             return;
00231         else
00232             client->factory()->removeClient( client ); //just in case someone does stupid things ;-)
00233     }
00234 
00235     d->pushState();
00236 
00237 //    QTime dt; dt.start();
00238 
00239     d->guiClient = client;
00240 
00241     // add this client to our client list
00242     if ( d->m_clients.containsRef( client ) == 0 )
00243         d->m_clients.append( client );
00244     else
00245         kdDebug(129) << "XMLGUI client already added " << client << endl;
00246 
00247     // Tell the client that plugging in is process and
00248     //  let it know what builder widget its mainwindow shortcuts
00249     //  should be attached to.
00250     client->beginXMLPlug( d->builder->widget() );
00251 
00252     // try to use the build document for building the client's GUI, as the build document
00253     // contains the correct container state information (like toolbar positions, sizes, etc.) .
00254     // if there is non available, then use the "real" document.
00255     QDomDocument doc = client->xmlguiBuildDocument();
00256     if ( doc.documentElement().isNull() )
00257         doc = client->domDocument();
00258 
00259     QDomElement docElement = doc.documentElement();
00260 
00261     d->m_rootNode->index = -1;
00262 
00263     // cache some variables
00264 
00265     d->clientName = docElement.attribute( d->attrName );
00266     d->clientBuilder = client->clientBuilder();
00267 
00268     if ( d->clientBuilder )
00269     {
00270         d->clientBuilderContainerTags = d->clientBuilder->containerTags();
00271         d->clientBuilderCustomTags = d->clientBuilder->customTags();
00272     }
00273     else
00274     {
00275         d->clientBuilderContainerTags.clear();
00276         d->clientBuilderCustomTags.clear();
00277     }
00278 
00279     // process a possibly existing actionproperties section
00280 
00281     QDomElement actionPropElement = docElement.namedItem( actionPropElementName ).toElement();
00282     if ( actionPropElement.isNull() )
00283         actionPropElement = docElement.namedItem( actionPropElementName.lower() ).toElement();
00284 
00285     if ( !actionPropElement.isNull() )
00286         applyActionProperties( actionPropElement );
00287 
00288     BuildHelper( *d, d->m_rootNode ).build( docElement );
00289 
00290     // let the client know that we built its GUI.
00291     client->setFactory( this );
00292 
00293     // call the finalizeGUI method, to fix up the positions of toolbars for example.
00294     // ### FIXME : obey client builder
00295     // --- Well, toolbars have a bool "positioned", so it doesn't really matter,
00296     // if we call positionYourself on all of them each time. (David)
00297     d->builder->finalizeGUI( d->guiClient );
00298 
00299     // reset some variables, for safety
00300     d->BuildState::reset();
00301 
00302     client->endXMLPlug();
00303 
00304     d->popState();
00305 
00306     emit clientAdded( client );
00307 
00308     // build child clients
00309     if ( client->childClients()->count() > 0 )
00310     {
00311         const QPtrList<KXMLGUIClient> *children = client->childClients();
00312         QPtrListIterator<KXMLGUIClient> childIt( *children );
00313         for (; childIt.current(); ++childIt )
00314             addClient( childIt.current() );
00315     }
00316 
00317 //    kdDebug() << "addClient took " << dt.elapsed() << endl;
00318 }
00319 
00320 void KXMLGUIFactory::removeClient( KXMLGUIClient *client )
00321 {
00322     kdDebug(129) << "KXMLGUIFactory::removeClient( " << client << " )" << endl; // ellis
00323 
00324     // don't try to remove the client's GUI if we didn't build it
00325     if ( !client || client->factory() != this )
00326         return;
00327 
00328     // remove this client from our client list
00329     d->m_clients.removeRef( client );
00330 
00331     // remove child clients first
00332     if ( client->childClients()->count() > 0 )
00333     {
00334         const QPtrList<KXMLGUIClient> *children = client->childClients();
00335         QPtrListIterator<KXMLGUIClient> childIt( *children );
00336         childIt.toLast();
00337         for (; childIt.current(); --childIt )
00338             removeClient( childIt.current() );
00339     }
00340 
00341     kdDebug(1002) << "KXMLGUIFactory::removeServant, calling removeRecursive" << endl;
00342 
00343     d->pushState();
00344 
00345     // cache some variables
00346 
00347     d->guiClient = client;
00348     d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00349     d->clientBuilder = client->clientBuilder();
00350 
00351     client->setFactory( 0L );
00352 
00353     // if we don't have a build document for that client, yet, then create one by
00354     // cloning the original document, so that saving container information in the
00355     // DOM tree does not touch the original document.
00356     QDomDocument doc = client->xmlguiBuildDocument();
00357     if ( doc.documentElement().isNull() )
00358     {
00359         doc = client->domDocument().cloneNode( true ).toDocument();
00360         client->setXMLGUIBuildDocument( doc );
00361     }
00362 
00363     d->m_rootNode->destruct( doc.documentElement(), *d );
00364 
00365     d->builder->finalizeGUI( d->guiClient ); //JoWenn
00366 
00367     // reset some variables
00368     d->BuildState::reset();
00369 
00370     // This will destruct the KAccel object built around the given widget.
00371     client->prepareXMLUnplug( d->builder->widget() );
00372 
00373     d->popState();
00374 
00375     emit clientRemoved( client );
00376 }
00377 
00378 QPtrList<KXMLGUIClient> KXMLGUIFactory::clients() const
00379 {
00380     return d->m_clients;
00381 }
00382 
00383 QWidget *KXMLGUIFactory::container( const QString &containerName, KXMLGUIClient *client,
00384                                     bool useTagName )
00385 {
00386     d->pushState();
00387     d->m_containerName = containerName;
00388     d->guiClient = client;
00389 
00390     QWidget *result = findRecursive( d->m_rootNode, useTagName );
00391 
00392     d->guiClient = 0L;
00393     d->m_containerName = QString::null;
00394 
00395     d->popState();
00396 
00397     return result;
00398 }
00399 
00400 QPtrList<QWidget> KXMLGUIFactory::containers( const QString &tagName )
00401 {
00402     return findRecursive( d->m_rootNode, tagName );
00403 }
00404 
00405 void KXMLGUIFactory::reset()
00406 {
00407     d->m_rootNode->reset();
00408 
00409     d->m_rootNode->clearChildren();
00410 }
00411 
00412 void KXMLGUIFactory::resetContainer( const QString &containerName, bool useTagName )
00413 {
00414     if ( containerName.isEmpty() )
00415         return;
00416 
00417     ContainerNode *container = d->m_rootNode->findContainer( containerName, useTagName );
00418 
00419     if ( !container )
00420         return;
00421 
00422     ContainerNode *parent = container->parent;
00423     if ( !parent )
00424         return;
00425 
00426     //  resetInternal( container );
00427 
00428     parent->removeChild( container );
00429 }
00430 
00431 QWidget *KXMLGUIFactory::findRecursive( KXMLGUI::ContainerNode *node, bool tag )
00432 {
00433     if ( ( ( !tag && node->name == d->m_containerName ) ||
00434            ( tag && node->tagName == d->m_containerName ) ) &&
00435          ( !d->guiClient || node->client == d->guiClient ) )
00436         return node->container;
00437 
00438     QPtrListIterator<ContainerNode> it( node->children );
00439     for (; it.current(); ++it )
00440     {
00441         QWidget *cont = findRecursive( it.current(), tag );
00442         if ( cont )
00443             return cont;
00444     }
00445 
00446     return 0L;
00447 }
00448 
00449 QPtrList<QWidget> KXMLGUIFactory::findRecursive( KXMLGUI::ContainerNode *node,
00450                                                  const QString &tagName )
00451 {
00452     QPtrList<QWidget> res;
00453 
00454     if ( node->tagName == tagName.lower() )
00455         res.append( node->container );
00456 
00457     QPtrListIterator<KXMLGUI::ContainerNode> it( node->children );
00458     for (; it.current(); ++it )
00459     {
00460         QPtrList<QWidget> lst = findRecursive( it.current(), tagName );
00461         QPtrListIterator<QWidget> wit( lst );
00462         for (; wit.current(); ++wit )
00463             res.append( wit.current() );
00464     }
00465 
00466     return res;
00467 }
00468 
00469 void KXMLGUIFactory::plugActionList( KXMLGUIClient *client, const QString &name,
00470                                      const QPtrList<KAction> &actionList )
00471 {
00472     d->pushState();
00473     d->guiClient = client;
00474     d->actionListName = name;
00475     d->actionList = actionList;
00476     d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00477 
00478     d->m_rootNode->plugActionList( *d );
00479 
00480     d->BuildState::reset();
00481     d->popState();
00482 }
00483 
00484 void KXMLGUIFactory::unplugActionList( KXMLGUIClient *client, const QString &name )
00485 {
00486     d->pushState();
00487     d->guiClient = client;
00488     d->actionListName = name;
00489     d->clientName = client->domDocument().documentElement().attribute( d->attrName );
00490 
00491     d->m_rootNode->unplugActionList( *d );
00492 
00493     d->BuildState::reset();
00494     d->popState();
00495 }
00496 
00497 void KXMLGUIFactory::applyActionProperties( const QDomElement &actionPropElement )
00498 {
00499     static const QString &tagAction = KGlobal::staticQString( "action" );
00500 
00501     for (QDomNode n = actionPropElement.firstChild();
00502          !n.isNull(); n = n.nextSibling() )
00503     {
00504         QDomElement e = n.toElement();
00505         if ( e.tagName().lower() != tagAction )
00506             continue;
00507 
00508         KAction *action = d->guiClient->action( e );
00509         if ( !action )
00510             continue;
00511 
00512         configureAction( action, e.attributes() );
00513     }
00514 }
00515 
00516 void KXMLGUIFactory::configureAction( KAction *action, const QDomNamedNodeMap &attributes )
00517 {
00518     for ( uint i = 0; i < attributes.length(); i++ )
00519     {
00520         QDomAttr attr = attributes.item( i ).toAttr();
00521         if ( attr.isNull() )
00522             continue;
00523 
00524         configureAction( action, attr );
00525     }
00526 }
00527 
00528 void KXMLGUIFactory::configureAction( KAction *action, const QDomAttr &attribute )
00529 {
00530     static const QString &attrShortcut = KGlobal::staticQString( "shortcut" );
00531 
00532     QString attrName = attribute.name();
00533     // If the attribute is a deprecated "accel", change to "shortcut".
00534     if ( attrName.lower() == "accel" )
00535         attrName = attrShortcut;
00536 
00537     QVariant propertyValue;
00538 
00539     QVariant::Type propertyType = action->property( attrName.latin1() ).type();
00540 
00541     if ( propertyType == QVariant::Int )
00542         propertyValue = QVariant( attribute.value().toInt() );
00543     else if ( propertyType == QVariant::UInt )
00544         propertyValue = QVariant( attribute.value().toUInt() );
00545     else
00546         propertyValue = QVariant( attribute.value() );
00547 
00548     action->setProperty( attrName.latin1(), propertyValue );
00549 }
00550 
00551 
00552 int KXMLGUIFactory::configureShortcuts(bool bAllowLetterShortcuts , bool bSaveSettings )
00553 {
00554     KKeyDialog dlg( bAllowLetterShortcuts, dynamic_cast<QWidget*>(parent()) );
00555     QPtrListIterator<KXMLGUIClient> it( d->m_clients );
00556     KXMLGUIClient *client;
00557     while( (client=it.current()) !=0 )
00558     {
00559         ++it;
00560         if(!client->xmlFile().isEmpty())
00561             dlg.insert( client->actionCollection() );
00562     }
00563     return dlg.configure(bSaveSettings);
00564 }
00565 
00566 QDomElement KXMLGUIFactory::actionPropertiesElement( QDomDocument& doc )
00567 {
00568     const QString tagActionProp = QString::fromLatin1("ActionProperties");
00569     // first, lets see if we have existing properties
00570     QDomElement elem;
00571     QDomNode it = doc.documentElement().firstChild();
00572     for( ; !it.isNull(); it = it.nextSibling() ) {
00573         QDomElement e = it.toElement();
00574         if( e.tagName() == tagActionProp ) {
00575             elem = e;
00576             break;
00577         }
00578     }
00579 
00580     // if there was none, create one
00581     if( elem.isNull() ) {
00582         elem = doc.createElement( tagActionProp );
00583         doc.documentElement().appendChild( elem );
00584     }
00585     return elem;
00586 }
00587 
00588 QDomElement KXMLGUIFactory::findActionByName( QDomElement& elem, const QString& sName, bool create )
00589 {
00590         static const QString& attrName = KGlobal::staticQString( "name" );
00591     static const QString& tagAction = KGlobal::staticQString( "Action" );
00592     for( QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling() ) {
00593         QDomElement e = it.toElement();
00594         if( e.attribute( attrName ) == sName )
00595             return e;
00596     }
00597 
00598     if( create ) {
00599         QDomElement act_elem = elem.ownerDocument().createElement( tagAction );
00600         act_elem.setAttribute( attrName, sName );
00601                 elem.appendChild( act_elem );
00602                 return act_elem;
00603     }
00604         return QDomElement();
00605 }
00606 
00607 void KXMLGUIFactory::virtual_hook( int, void* )
00608 { /*BASE::virtual_hook( id, data );*/ }
00609 
00610 #include "kxmlguifactory.moc"
00611 
00612 /* vim: et sw=4
00613  */
KDE Logo
This file is part of the documentation for kdeui Library Version 3.3.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 30 10:12:03 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003