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

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.6
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