• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kxmlguifactory.cpp

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

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal