kdeui Library API Documentation

kxmlguiclient.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 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 version 2 as published by the Free Software Foundation.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 #include "kxmlguiclient.h"
00021 #include "kxmlguifactory.h"
00022 #include "kxmlguibuilder.h"
00023 
00024 #include <qdir.h>
00025 #include <qfile.h>
00026 #include <qdom.h>
00027 #include <qtextstream.h>
00028 #include <qregexp.h>
00029 
00030 #include <kinstance.h>
00031 #include <kstandarddirs.h>
00032 #include <kdebug.h>
00033 #include <kaction.h>
00034 #include <kapplication.h>
00035 
00036 #include <assert.h>
00037 
00038 class KXMLGUIClientPrivate
00039 {
00040 public:
00041   KXMLGUIClientPrivate()
00042   {
00043     m_instance = KGlobal::instance();
00044     m_factory = 0L;
00045     m_parent = 0L;
00046     m_builder = 0L;
00047     m_actionCollection = 0;
00048   }
00049   ~KXMLGUIClientPrivate()
00050   {
00051   }
00052 
00053   KInstance *m_instance;
00054 
00055   QDomDocument m_doc;
00056   KActionCollection *m_actionCollection;
00057   QDomDocument m_buildDocument;
00058   KXMLGUIFactory *m_factory;
00059   KXMLGUIClient *m_parent;
00060   //QPtrList<KXMLGUIClient> m_supers;
00061   QPtrList<KXMLGUIClient> m_children;
00062   KXMLGUIBuilder *m_builder;
00063   QString m_xmlFile;
00064   QString m_localXMLFile;
00065 };
00066 
00067 KXMLGUIClient::KXMLGUIClient()
00068 {
00069   d = new KXMLGUIClientPrivate;
00070 }
00071 
00072 KXMLGUIClient::KXMLGUIClient( KXMLGUIClient *parent )
00073 {
00074   d = new KXMLGUIClientPrivate;
00075   parent->insertChildClient( this );
00076 }
00077 
00078 KXMLGUIClient::~KXMLGUIClient()
00079 {
00080   if ( d->m_parent )
00081     d->m_parent->removeChildClient( this );
00082 
00083   QPtrListIterator<KXMLGUIClient> it( d->m_children );
00084   for ( ; it.current(); ++it ) {
00085       assert( it.current()->d->m_parent == this );
00086       it.current()->d->m_parent = 0;
00087   }
00088 
00089   delete d->m_actionCollection;
00090   delete d;
00091 }
00092 
00093 KAction *KXMLGUIClient::action( const char *name ) const
00094 {
00095   KAction* act = actionCollection()->action( name );
00096   if ( !act ) {
00097     QPtrListIterator<KXMLGUIClient> childIt( d->m_children );
00098     for (; childIt.current(); ++childIt ) {
00099       act = childIt.current()->actionCollection()->action( name );
00100       if ( act )
00101         break;
00102     }
00103   }
00104   return act;
00105 }
00106 
00107 KActionCollection *KXMLGUIClient::actionCollection() const
00108 {
00109   if ( !d->m_actionCollection )
00110     d->m_actionCollection = new KActionCollection( 0, 0,
00111       "KXMLGUILClient-KActionCollection" );
00112   return d->m_actionCollection;
00113 }
00114 
00115 KAction *KXMLGUIClient::action( const QDomElement &element ) const
00116 {
00117   static const QString &attrName = KGlobal::staticQString( "name" );
00118   return actionCollection()->action( element.attribute( attrName ).latin1() );
00119 }
00120 
00121 KInstance *KXMLGUIClient::instance() const
00122 {
00123   return d->m_instance;
00124 }
00125 
00126 QDomDocument KXMLGUIClient::domDocument() const
00127 {
00128   return d->m_doc;
00129 }
00130 
00131 QString KXMLGUIClient::xmlFile() const
00132 {
00133   return d->m_xmlFile;
00134 }
00135 
00136 QString KXMLGUIClient::localXMLFile() const
00137 {
00138   if ( !d->m_localXMLFile.isEmpty() )
00139     return d->m_localXMLFile;
00140 
00141   if ( d->m_xmlFile[0] == '/' )
00142       return QString::null; // can't save anything here
00143 
00144   return locateLocal( "data", QString::fromLatin1( instance()->instanceName() + '/' ) + d->m_xmlFile );
00145 }
00146 
00147 
00148 void KXMLGUIClient::reloadXML()
00149 {
00150     QString file( xmlFile() );
00151     if ( !file.isEmpty() )
00152         setXMLFile( file );
00153 }
00154 
00155 void KXMLGUIClient::setInstance( KInstance *instance )
00156 {
00157   d->m_instance = instance;
00158   actionCollection()->setInstance( instance );
00159   if ( d->m_builder )
00160     d->m_builder->setBuilderClient( this );
00161 }
00162 
00163 void KXMLGUIClient::setXMLFile( const QString& _file, bool merge, bool setXMLDoc )
00164 {
00165   // store our xml file name
00166   if ( !_file.isNull() ) {
00167     d->m_xmlFile = _file;
00168     actionCollection()->setXMLFile( _file );
00169   }
00170 
00171   if ( !setXMLDoc )
00172     return;
00173 
00174   QString file = _file;
00175   if ( file[0] != '/' )
00176   {
00177     QString doc;
00178 
00179     QString filter = QString::fromLatin1( instance()->instanceName() + '/' ) + _file;
00180 
00181     QStringList allFiles = instance()->dirs()->findAllResources( "data", filter ) + instance()->dirs()->findAllResources( "data", _file );
00182 
00183     file = findMostRecentXMLFile( allFiles, doc );
00184 
00185     if ( file.isEmpty() )
00186     {
00187       // this might or might not be an error.  for the time being,
00188       // let's treat this as if it isn't a problem and the user just
00189       // wants the global standards file
00190       setXML( QString::null, true );
00191       return;
00192     }
00193     else if ( !doc.isEmpty() )
00194     {
00195       setXML( doc, merge );
00196       return;
00197     }
00198   }
00199 
00200   QString xml = KXMLGUIFactory::readConfigFile( file );
00201   setXML( xml, merge );
00202 }
00203 
00204 void KXMLGUIClient::setLocalXMLFile( const QString &file )
00205 {
00206     d->m_localXMLFile = file;
00207 }
00208 
00209 void KXMLGUIClient::setXML( const QString &document, bool merge )
00210 {
00211   QDomDocument doc;
00212   doc.setContent( document );
00213   setDOMDocument( doc, merge );
00214 }
00215 
00216 void KXMLGUIClient::setDOMDocument( const QDomDocument &document, bool merge )
00217 {
00218   if ( merge )
00219   {
00220     QDomElement base = d->m_doc.documentElement();
00221 
00222     QDomElement e = document.documentElement();
00223     KXMLGUIFactory::removeDOMComments( e );
00224 
00225     // merge our original (global) xml with our new one
00226     mergeXML(base, e, actionCollection());
00227 
00228     // reassign our pointer as mergeXML might have done something
00229     // strange to it
00230     base = d->m_doc.documentElement();
00231 
00232     // we want some sort of failsafe.. just in case
00233     if ( base.isNull() )
00234       d->m_doc = document;
00235   }
00236   else
00237   {
00238     d->m_doc = document;
00239     KXMLGUIFactory::removeDOMComments( d->m_doc );
00240   }
00241 
00242   setXMLGUIBuildDocument( QDomDocument() );
00243 }
00244 
00245 bool KXMLGUIClient::mergeXML( QDomElement &base, const QDomElement &additive, KActionCollection *actionCollection )
00246 {
00247   static const QString &tagAction = KGlobal::staticQString( "Action" );
00248   static const QString &tagMerge = KGlobal::staticQString( "Merge" );
00249   static const QString &tagSeparator = KGlobal::staticQString( "Separator" );
00250   static const QString &attrName = KGlobal::staticQString( "name" );
00251   static const QString &attrAppend = KGlobal::staticQString( "append" );
00252   static const QString &attrWeakSeparator = KGlobal::staticQString( "weakSeparator" );
00253   static const QString &tagMergeLocal = KGlobal::staticQString( "MergeLocal" );
00254   static const QString &tagText = KGlobal::staticQString( "text" );
00255   static const QString &attrAlreadyVisited = KGlobal::staticQString( "alreadyVisited" );
00256   static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" );
00257   static const QString &attrOne = KGlobal::staticQString( "1" );
00258 
00259   // there is a possibility that we don't want to merge in the
00260   // additive.. rather, we might want to *replace* the base with the
00261   // additive.  this can be for any container.. either at a file wide
00262   // level or a simple container level.  we look for the 'noMerge'
00263   // tag, in any event and just replace the old with the new
00264   if ( additive.attribute(attrNoMerge) == attrOne ) // ### use toInt() instead? (Simon)
00265   {
00266     base.parentNode().replaceChild(additive, base);
00267     return true;
00268   }
00269 
00270   QString tag;
00271 
00272   QDomElement e = base.firstChild().toElement();
00273   // iterate over all elements in the container (of the global DOM tree)
00274   while ( !e.isNull() )
00275   {
00276     tag = e.tagName();
00277 
00278     // if there's an action tag in the global tree and the action is
00279     // not implemented, then we remove the element
00280     if ( tag == tagAction )
00281     {
00282       QCString name =  e.attribute( attrName ).utf8(); // WABA
00283       if ( !actionCollection->action( name ) ||
00284            (kapp && !kapp->authorizeKAction(name)))
00285       {
00286         // remove this child as we aren't using it
00287         QDomElement oldChild = e;
00288         e = e.nextSibling().toElement();
00289         base.removeChild( oldChild );
00290         continue;
00291       }
00292     }
00293 
00294     // if there's a separator defined in the global tree, then add an
00295     // attribute, specifying that this is a "weak" separator
00296     else if ( tag == tagSeparator )
00297     {
00298       e.setAttribute( attrWeakSeparator, (uint)1 );
00299 
00300       // okay, hack time. if the last item was a weak separator OR
00301       // this is the first item in a container, then we nuke the
00302       // current one
00303       QDomElement prev = e.previousSibling().toElement();
00304       if ( prev.isNull() ||
00305      ( prev.tagName() == tagSeparator && !prev.attribute( attrWeakSeparator ).isNull() ) ||
00306      ( prev.tagName() == tagText ) )
00307       {
00308         // the previous element was a weak separator or didn't exist
00309         QDomElement oldChild = e;
00310         e = e.nextSibling().toElement();
00311         base.removeChild( oldChild );
00312         continue;
00313       }
00314     }
00315 
00316     // the MergeLocal tag lets us specify where non-standard elements
00317     // of the local tree shall be merged in.  After inserting the
00318     // elements we delete this element
00319     else if ( tag == tagMergeLocal )
00320     {
00321       QDomElement currElement = e;
00322 
00323       // switch our iterator "e" to the next sibling, so that we don't
00324       // process the local tree's inserted items!
00325       e = e.nextSibling().toElement();
00326 
00327       QDomElement it = additive.firstChild().toElement();
00328       while ( !it.isNull() )
00329       {
00330         QDomElement newChild = it;
00331 
00332         it = it.nextSibling().toElement();
00333 
00334         if ( newChild.tagName() == tagText )
00335           continue;
00336 
00337         if ( newChild.attribute( attrAlreadyVisited ) == attrOne )
00338           continue;
00339 
00340         QString itAppend( newChild.attribute( attrAppend ) );
00341         QString elemName( currElement.attribute( attrName ) );
00342 
00343         if ( ( itAppend.isNull() && elemName.isEmpty() ) ||
00344              ( itAppend == elemName ) )
00345         {
00346           // first, see if this new element matches a standard one in
00347           // the global file.  if it does, then we skip it as it will
00348           // be merged in, later
00349           QDomElement matchingElement = findMatchingElement( newChild, base );
00350           if ( matchingElement.isNull() || newChild.tagName() == tagSeparator )
00351             base.insertBefore( newChild, currElement );
00352         }
00353       }
00354 
00355       base.removeChild( currElement );
00356       continue;
00357     }
00358 
00359     // in this last case we check for a separator tag and, if not, we
00360     // can be sure that its a container --> proceed with child nodes
00361     // recursively and delete the just proceeded container item in
00362     // case its empty (if the recursive call returns true)
00363     else if ( tag != tagMerge )
00364     {
00365       // handle the text tag
00366       if ( tag == tagText )
00367       {
00368         e = e.nextSibling().toElement();
00369         continue;
00370       }
00371 
00372       QDomElement matchingElement = findMatchingElement( e, additive );
00373 
00374       QDomElement currElement = e;
00375       e = e.nextSibling().toElement();
00376 
00377       if ( !matchingElement.isNull() )
00378       {
00379         matchingElement.setAttribute( attrAlreadyVisited, (uint)1 );
00380 
00381         if ( mergeXML( currElement, matchingElement, actionCollection ) )
00382         {
00383           base.removeChild( currElement );
00384           continue;
00385         }
00386 
00387         // Merge attributes
00388         QDomNamedNodeMap attribs = matchingElement.attributes();
00389         for(uint i = 0; i < attribs.count(); i++)
00390         {
00391           QDomNode node = attribs.item(i);
00392           currElement.setAttribute(node.nodeName(), node.nodeValue());
00393         }
00394 
00395         continue;
00396       }
00397       else
00398       {
00399         // this is an important case here! We reach this point if the
00400         // "local" tree does not contain a container definition for
00401         // this container. However we have to call mergeXML recursively
00402         // and make it check if there are actions implemented for this
00403         // container. *If* none, then we can remove this container now
00404         if ( mergeXML( currElement, QDomElement(), actionCollection ) )
00405           base.removeChild( currElement );
00406         continue;
00407       }
00408     }
00409 
00410     //I think this can be removed ;-)
00411     e = e.nextSibling().toElement();
00412   }
00413 
00414   //here we append all child elements which were not inserted
00415   //previously via the LocalMerge tag
00416   e = additive.firstChild().toElement();
00417   while ( !e.isNull() )
00418   {
00419     QDomElement matchingElement = findMatchingElement( e, base );
00420 
00421     if ( matchingElement.isNull() )
00422     {
00423       QDomElement newChild = e;
00424       e = e.nextSibling().toElement();
00425       base.appendChild( newChild );
00426     }
00427     else
00428       e = e.nextSibling().toElement();
00429   }
00430 
00431   // do one quick check to make sure that the last element was not
00432   // a weak separator
00433   QDomElement last = base.lastChild().toElement();
00434   if ( (last.tagName() == tagSeparator) && (!last.attribute( attrWeakSeparator ).isNull()) )
00435   {
00436     base.removeChild( base.lastChild() );
00437   }
00438 
00439   // now we check if we are empty (in which case we return "true", to
00440   // indicate the caller that it can delete "us" (the base element
00441   // argument of "this" call)
00442   bool deleteMe = true;
00443   e = base.firstChild().toElement();
00444   for ( ; !e.isNull(); e = e.nextSibling().toElement() )
00445   {
00446     tag = e.tagName();
00447 
00448     if ( tag == tagAction )
00449     {
00450       // if base contains an implemented action, then we must not get
00451       // deleted (note that the actionCollection contains both,
00452       // "global" and "local" actions
00453       if ( actionCollection->action( e.attribute( attrName ).utf8() ) )
00454       {
00455         deleteMe = false;
00456         break;
00457       }
00458     }
00459     else if ( tag == tagSeparator )
00460     {
00461       // if we have a separator which has *not* the weak attribute
00462       // set, then it must be owned by the "local" tree in which case
00463       // we must not get deleted either
00464       QString weakAttr = e.attribute( attrWeakSeparator );
00465       if ( weakAttr.isEmpty() || weakAttr.toInt() != 1 )
00466       {
00467         deleteMe = false;
00468         break;
00469       }
00470     }
00471 
00472     // in case of a merge tag we have unlimited lives, too ;-)
00473     else if ( tag == tagMerge )
00474     {
00475 //      deleteMe = false;
00476 //      break;
00477         continue;
00478     }
00479 
00480     // a text tag is NOT enough to spare this container
00481     else if ( tag == tagText )
00482     {
00483       continue;
00484     }
00485 
00486     // what's left are non-empty containers! *don't* delete us in this
00487     // case (at this position we can be *sure* that the container is
00488     // *not* empty, as the recursive call for it was in the first loop
00489     // which deleted the element in case the call returned "true"
00490     else
00491     {
00492       deleteMe = false;
00493       break;
00494     }
00495   }
00496 
00497   return deleteMe;
00498 }
00499 
00500 QDomElement KXMLGUIClient::findMatchingElement( const QDomElement &base, const QDomElement &additive )
00501 {
00502   static const QString &tagAction = KGlobal::staticQString( "Action" );
00503   static const QString &tagMergeLocal = KGlobal::staticQString( "MergeLocal" );
00504   static const QString &attrName = KGlobal::staticQString( "name" );
00505 
00506   QDomElement e = additive.firstChild().toElement();
00507   for ( ; !e.isNull(); e = e.nextSibling().toElement() )
00508   {
00509     // skip all action and merge tags as we will never use them
00510     if ( ( e.tagName() == tagAction ) || ( e.tagName() == tagMergeLocal ) )
00511     {
00512       continue;
00513     }
00514 
00515     // now see if our tags are equivalent
00516     if ( ( e.tagName() == base.tagName() ) &&
00517          ( e.attribute( attrName ) == base.attribute( attrName ) ) )
00518     {
00519         return e;
00520     }
00521   }
00522 
00523   // nope, return a (now) null element
00524   return e;
00525 }
00526 
00527 void KXMLGUIClient::conserveMemory()
00528 {
00529   d->m_doc = QDomDocument();
00530   d->m_buildDocument = QDomDocument();
00531 }
00532 
00533 void KXMLGUIClient::setXMLGUIBuildDocument( const QDomDocument &doc )
00534 {
00535   d->m_buildDocument = doc;
00536 }
00537 
00538 QDomDocument KXMLGUIClient::xmlguiBuildDocument() const
00539 {
00540   return d->m_buildDocument;
00541 }
00542 
00543 void KXMLGUIClient::setFactory( KXMLGUIFactory *factory )
00544 {
00545   d->m_factory = factory;
00546 }
00547 
00548 KXMLGUIFactory *KXMLGUIClient::factory() const
00549 {
00550   return d->m_factory;
00551 }
00552 
00553 KXMLGUIClient *KXMLGUIClient::parentClient() const
00554 {
00555   return d->m_parent;
00556 }
00557 
00558 void KXMLGUIClient::insertChildClient( KXMLGUIClient *child )
00559 {
00560   if (  child->d->m_parent )
00561     child->d->m_parent->removeChildClient( child );
00562    d->m_children.append( child );
00563    child->d->m_parent = this;
00564 }
00565 
00566 void KXMLGUIClient::removeChildClient( KXMLGUIClient *child )
00567 {
00568   assert( d->m_children.containsRef( child ) );
00569   d->m_children.removeRef( child );
00570   child->d->m_parent = 0;
00571 }
00572 
00573 /*bool KXMLGUIClient::addSuperClient( KXMLGUIClient *super )
00574 {
00575   if ( d->m_supers.contains( super ) )
00576     return false;
00577   d->m_supers.append( super );
00578   return true;
00579 }*/
00580 
00581 const QPtrList<KXMLGUIClient> *KXMLGUIClient::childClients()
00582 {
00583   return &d->m_children;
00584 }
00585 
00586 void KXMLGUIClient::setClientBuilder( KXMLGUIBuilder *builder )
00587 {
00588   d->m_builder = builder;
00589   if ( builder )
00590     builder->setBuilderInstance( instance() );
00591 }
00592 
00593 KXMLGUIBuilder *KXMLGUIClient::clientBuilder() const
00594 {
00595   return d->m_builder;
00596 }
00597 
00598 void KXMLGUIClient::plugActionList( const QString &name, const QPtrList<KAction> &actionList )
00599 {
00600   if ( !d->m_factory )
00601     return;
00602 
00603   d->m_factory->plugActionList( this, name, actionList );
00604 }
00605 
00606 void KXMLGUIClient::unplugActionList( const QString &name )
00607 {
00608   if ( !d->m_factory )
00609     return;
00610 
00611   d->m_factory->unplugActionList( this, name );
00612 }
00613 
00614 QString KXMLGUIClient::findMostRecentXMLFile( const QStringList &files, QString &doc )
00615 {
00616 
00617   QValueList<DocStruct> allDocuments;
00618 
00619   QStringList::ConstIterator it = files.begin();
00620   QStringList::ConstIterator end = files.end();
00621   for (; it != end; ++it )
00622   {
00623     //kdDebug() << "KXMLGUIClient::findMostRecentXMLFile " << *it << endl;
00624     QString data = KXMLGUIFactory::readConfigFile( *it );
00625     DocStruct d;
00626     d.file = *it;
00627     d.data = data;
00628     allDocuments.append( d );
00629   }
00630 
00631   QValueList<DocStruct>::Iterator best = allDocuments.end();
00632   uint bestVersion = 0;
00633 
00634   QValueList<DocStruct>::Iterator docIt = allDocuments.begin();
00635   QValueList<DocStruct>::Iterator docEnd = allDocuments.end();
00636   for (; docIt != docEnd; ++docIt )
00637   {
00638     QString versionStr = findVersionNumber( (*docIt).data );
00639     if ( versionStr.isEmpty() )
00640       continue;
00641 
00642     bool ok = false;
00643     uint version = versionStr.toUInt( &ok );
00644     if ( !ok )
00645       continue;
00646     //kdDebug() << "FOUND VERSION " << version << endl;
00647 
00648     if ( version > bestVersion )
00649     {
00650       best = docIt;
00651       //kdDebug() << "best version is now " << version << endl;
00652       bestVersion = version;
00653     }
00654   }
00655 
00656   if ( best != docEnd )
00657   {
00658     if ( best != allDocuments.begin() )
00659     {
00660       QValueList<DocStruct>::Iterator local = allDocuments.begin();
00661 
00662       // load the local document and extract the action properties
00663       QDomDocument document;
00664       document.setContent( (*local).data );
00665 
00666       ActionPropertiesMap properties = extractActionProperties( document );
00667 
00668       // in case the document has a ActionProperties section
00669       // we must not delete it but copy over the global doc
00670       // to the local and insert the ActionProperties section
00671       if ( !properties.isEmpty() )
00672       {
00673           // now load the global one with the higher version number
00674           // into memory
00675           document.setContent( (*best).data );
00676           // and store the properties in there
00677           storeActionProperties( document, properties );
00678 
00679           (*local).data = document.toString();
00680           // make sure we pick up the new local doc, when we return later
00681           best = local;
00682 
00683           // write out the new version of the local document
00684           QFile f( (*local).file );
00685           if ( f.open( IO_WriteOnly ) )
00686           {
00687             QCString utf8data = (*local).data.utf8();
00688             f.writeBlock( utf8data.data(), utf8data.length() );
00689             f.close();
00690           }
00691       }
00692       else
00693       {
00694         QString f = (*local).file;
00695         QString backup = f + QString::fromLatin1( ".backup" );
00696         QDir dir;
00697         dir.rename( f, backup );
00698       }
00699     }
00700     doc = (*best).data;
00701     return (*best).file;
00702   }
00703   else if ( files.count() > 0 )
00704   {
00705     //kdDebug() << "returning first one..." << endl;
00706     doc = (*allDocuments.begin()).data;
00707     return (*allDocuments.begin()).file;
00708   }
00709 
00710   return QString::null;
00711 }
00712 
00713 
00714 
00715 QString KXMLGUIClient::findVersionNumber( const QString &xml )
00716 {
00717   enum { ST_START, ST_AFTER_OPEN, ST_AFTER_GUI,
00718                ST_EXPECT_VERSION, ST_VERSION_NUM} state = ST_START;
00719   for (unsigned int pos = 0; pos < xml.length(); pos++)
00720   {
00721     switch (state)
00722     {
00723       case ST_START:
00724         if (xml[pos] == '<')
00725           state = ST_AFTER_OPEN;
00726         break;
00727       case ST_AFTER_OPEN:
00728       {
00729         //Jump to gui..
00730         int guipos = xml.find("gui", pos, false /*case-insensitive*/);
00731         if (guipos == -1)
00732           return QString::null; //Reject
00733 
00734         pos = guipos + 2; //Position at i, so we're moved ahead to the next character by the ++;
00735         state = ST_AFTER_GUI;
00736         break;
00737       }       
00738       case ST_AFTER_GUI:
00739         state = ST_EXPECT_VERSION;
00740         break;
00741       case ST_EXPECT_VERSION:
00742       {
00743         int verpos =  xml.find("version=\"", pos, false /*case-insensitive*/);
00744         if (verpos == -1)
00745           return QString::null; //Reject
00746 
00747         pos = verpos +  8; //v = 0, e = +1, r = +2, s = +3 , i = +4, o = +5, n = +6, = = +7, " = + 8
00748         state = ST_VERSION_NUM;
00749         break;
00750       }
00751       case ST_VERSION_NUM:
00752       {
00753         unsigned int endpos;
00754         for (endpos = pos; endpos <  xml.length(); endpos++)
00755         {
00756           if (xml[endpos].unicode() >= '0' && xml[endpos].unicode() <= '9')
00757             continue; //Number..
00758           if (xml[endpos].unicode() == '"') //End of parameter
00759             break;
00760           else //This shouldn't be here..
00761           {
00762             endpos = xml.length();
00763           }                  
00764         }
00765 
00766         if (endpos != pos && endpos < xml.length() )
00767         {
00768           QString matchCandidate = xml.mid(pos, endpos - pos); //Don't include " ".
00769           return matchCandidate;
00770         }
00771 
00772         state = ST_EXPECT_VERSION; //Try to match a well-formed version..            
00773         break;
00774       } //case..
00775     } //switch
00776   } //for
00777  
00778   return QString::null;
00779 }
00780 
00781 KXMLGUIClient::ActionPropertiesMap KXMLGUIClient::extractActionProperties( const QDomDocument &doc )
00782 {
00783   ActionPropertiesMap properties;
00784 
00785   QDomElement actionPropElement = doc.documentElement().namedItem( "ActionProperties" ).toElement();
00786 
00787   if ( actionPropElement.isNull() )
00788     return properties;
00789 
00790   QDomNode n = actionPropElement.firstChild();
00791   for (; !n.isNull(); n = n.nextSibling() )
00792   {
00793     QDomElement e = n.toElement();
00794     if ( e.isNull() )
00795       continue;
00796 
00797     if ( e.tagName().lower() != "action" )
00798       continue;
00799 
00800     QString actionName = e.attribute( "name" );
00801 
00802     if ( actionName.isEmpty() )
00803       continue;
00804 
00805     QMap<QString, QMap<QString, QString> >::Iterator propIt = properties.find( actionName );
00806     if ( propIt == properties.end() )
00807       propIt = properties.insert( actionName, QMap<QString, QString>() );
00808 
00809     QDomNamedNodeMap attributes = e.attributes();
00810     for ( uint i = 0; i < attributes.length(); ++i )
00811     {
00812       QDomAttr attr = attributes.item( i ).toAttr();
00813 
00814       if ( attr.isNull() )
00815         continue;
00816 
00817       QString name = attr.name();
00818 
00819       if ( name == "name" || name.isEmpty() )
00820         continue;
00821 
00822       (*propIt)[ name ] = attr.value();
00823     }
00824 
00825   }
00826 
00827   return properties;
00828 }
00829 
00830 void KXMLGUIClient::storeActionProperties( QDomDocument &doc, const ActionPropertiesMap &properties )
00831 {
00832   QDomElement actionPropElement = doc.documentElement().namedItem( "ActionProperties" ).toElement();
00833 
00834   if ( actionPropElement.isNull() )
00835   {
00836     actionPropElement = doc.createElement( "ActionProperties" );
00837     doc.documentElement().appendChild( actionPropElement );
00838   }
00839 
00840   while ( !actionPropElement.firstChild().isNull() )
00841     actionPropElement.removeChild( actionPropElement.firstChild() );
00842 
00843   ActionPropertiesMap::ConstIterator it = properties.begin();
00844   ActionPropertiesMap::ConstIterator end = properties.end();
00845   for (; it != end; ++it )
00846   {
00847     QDomElement action = doc.createElement( "Action" );
00848     action.setAttribute( "name", it.key() );
00849     actionPropElement.appendChild( action );
00850 
00851     QMap<QString, QString> attributes = (*it);
00852     QMap<QString, QString>::ConstIterator attrIt = attributes.begin();
00853     QMap<QString, QString>::ConstIterator attrEnd = attributes.end();
00854     for (; attrIt != attrEnd; ++attrIt )
00855       action.setAttribute( attrIt.key(), attrIt.data() );
00856   }
00857 }
00858 
00859 void KXMLGUIClient::addStateActionEnabled(const QString& state,
00860                                           const QString& action)
00861 {
00862   StateChange stateChange = getActionsToChangeForState(state);
00863 
00864   stateChange.actionsToEnable.append( action );
00865 
00866   m_actionsStateMap.replace( state, stateChange );
00867 }
00868 
00869 
00870 void KXMLGUIClient::addStateActionDisabled(const QString& state,
00871                                            const QString& action)
00872 {
00873   StateChange stateChange = getActionsToChangeForState(state);
00874 
00875   stateChange.actionsToDisable.append( action );
00876 
00877   m_actionsStateMap.replace( state, stateChange );
00878 }
00879 
00880 
00881 KXMLGUIClient::StateChange KXMLGUIClient::getActionsToChangeForState(const QString& state)
00882 {
00883   return m_actionsStateMap[state];
00884 }
00885 
00886 
00887 void KXMLGUIClient::stateChanged(const QString &newstate, KXMLGUIClient::ReverseStateChange reverse)
00888 {
00889   StateChange stateChange = getActionsToChangeForState(newstate);
00890 
00891   bool setTrue = (reverse == StateNoReverse);
00892   bool setFalse = !setTrue;
00893 
00894   // Enable actions which need to be enabled...
00895   //
00896   for ( QStringList::Iterator it = stateChange.actionsToEnable.begin();
00897         it != stateChange.actionsToEnable.end(); ++it ) {
00898 
00899     KAction *action = actionCollection()->action((*it).latin1());
00900     if (action) action->setEnabled(setTrue);
00901   }
00902 
00903   // and disable actions which need to be disabled...
00904   //
00905   for ( QStringList::Iterator it = stateChange.actionsToDisable.begin();
00906         it != stateChange.actionsToDisable.end(); ++it ) {
00907 
00908     KAction *action = actionCollection()->action((*it).latin1());
00909     if (action) action->setEnabled(setFalse);
00910   }
00911 
00912 }
00913 
00914 void KXMLGUIClient::beginXMLPlug( QWidget *w )
00915 {
00916   actionCollection()->beginXMLPlug( w );
00917   QPtrListIterator<KXMLGUIClient> childIt( d->m_children );
00918   for (; childIt.current(); ++childIt )
00919     childIt.current()->actionCollection()->beginXMLPlug( w );
00920 }
00921 
00922 void KXMLGUIClient::endXMLPlug()
00923 {
00924   actionCollection()->endXMLPlug();
00925   QPtrListIterator<KXMLGUIClient> childIt( d->m_children );
00926   for (; childIt.current(); ++childIt )
00927     childIt.current()->actionCollection()->endXMLPlug();
00928 }
00929 
00930 void KXMLGUIClient::prepareXMLUnplug( QWidget * )
00931 {
00932   actionCollection()->prepareXMLUnplug();
00933   QPtrListIterator<KXMLGUIClient> childIt( d->m_children );
00934   for (; childIt.current(); ++childIt )
00935     childIt.current()->actionCollection()->prepareXMLUnplug();
00936 }
00937 
00938 void KXMLGUIClient::virtual_hook( int, void* )
00939 { /*BASE::virtual_hook( id, data );*/ }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:15:05 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001