• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDEUI

kxmlguiversionhandler.cpp

Go to the documentation of this file.
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    Copyright     2007 David Faure <faure@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "kxmlguiversionhandler_p.h"
00023 #include <kdebug.h>
00024 #include <QFile>
00025 #include <QDomDocument>
00026 #include "kxmlguifactory.h"
00027 #include <kglobal.h>
00028 #include <kstandarddirs.h>
00029 #include <QtXml/QDomElement>
00030 
00031 struct DocStruct
00032 {
00033     QString file;
00034     QString data;
00035 };
00036 
00037 static QList<QDomElement> extractToolBars(const QDomDocument& doc)
00038 {
00039     QList<QDomElement> toolbars;
00040     QDomElement parent = doc.documentElement();
00041     for (QDomElement e = parent.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
00042         if (e.tagName().compare(QLatin1String("ToolBar"), Qt::CaseInsensitive) == 0) {
00043             toolbars.append(e);
00044         }
00045     }
00046     return toolbars;
00047 }
00048 
00049 static void removeAllToolBars(QDomDocument& doc)
00050 {
00051     QDomElement parent = doc.documentElement();
00052     const QList<QDomElement> toolBars = extractToolBars(doc);
00053     foreach(const QDomElement& e, toolBars) {
00054         parent.removeChild(e);
00055     }
00056 }
00057 
00058 static void insertToolBars(QDomDocument& doc, const QList<QDomElement>& toolBars)
00059 {
00060     QDomElement parent = doc.documentElement();
00061     QDomElement menuBar = parent.namedItem("MenuBar").toElement();
00062     QDomElement insertAfter = menuBar;
00063     if (menuBar.isNull())
00064         insertAfter = parent.firstChildElement(); // if null, insertAfter will do an append
00065     foreach(const QDomElement& e, toolBars) {
00066         QDomNode result = parent.insertAfter(e, insertAfter);
00067         Q_ASSERT(!result.isNull());
00068     }
00069 }
00070 
00071 //
00072 
00073 typedef QMap<QString, QMap<QString, QString> > ActionPropertiesMap;
00074 
00075 static ActionPropertiesMap extractActionProperties(const QDomDocument &doc)
00076 {
00077   ActionPropertiesMap properties;
00078 
00079   QDomElement actionPropElement = doc.documentElement().namedItem( "ActionProperties" ).toElement();
00080 
00081   if ( actionPropElement.isNull() )
00082     return properties;
00083 
00084   QDomNode n = actionPropElement.firstChild();
00085   while(!n.isNull())
00086   {
00087     QDomElement e = n.toElement();
00088     n = n.nextSibling(); // Advance now so that we can safely delete e
00089     if ( e.isNull() )
00090       continue;
00091 
00092     if ( e.tagName().compare("action", Qt::CaseInsensitive) != 0 )
00093       continue;
00094 
00095     const QString actionName = e.attribute( "name" );
00096     if ( actionName.isEmpty() )
00097       continue;
00098 
00099     QMap<QString, QMap<QString, QString> >::Iterator propIt = properties.find( actionName );
00100     if ( propIt == properties.end() )
00101       propIt = properties.insert( actionName, QMap<QString, QString>() );
00102 
00103     const QDomNamedNodeMap attributes = e.attributes();
00104     const uint attributeslength = attributes.length();
00105 
00106     for ( uint i = 0; i < attributeslength; ++i )
00107     {
00108       const QDomAttr attr = attributes.item( i ).toAttr();
00109 
00110       if ( attr.isNull() )
00111         continue;
00112 
00113       const QString name = attr.name();
00114 
00115       if ( name == "name" || name.isEmpty() )
00116         continue;
00117 
00118       (*propIt)[ name ] = attr.value();
00119     }
00120 
00121   }
00122 
00123   return properties;
00124 }
00125 
00126 static void storeActionProperties( QDomDocument &doc,
00127                             const ActionPropertiesMap &properties )
00128 {
00129   QDomElement actionPropElement = doc.documentElement().namedItem( "ActionProperties" ).toElement();
00130 
00131   if ( actionPropElement.isNull() )
00132   {
00133     actionPropElement = doc.createElement( "ActionProperties" );
00134     doc.documentElement().appendChild( actionPropElement );
00135   }
00136 
00137 //Remove only those ActionProperties entries from the document, that are present
00138 //in the properties argument. In real life this means that local ActionProperties
00139 //takes precedence over global ones, if they exists (think local override of shortcuts).
00140   QDomNode actionNode = actionPropElement.firstChild();
00141   while (!actionNode.isNull()) 
00142   {
00143     if (properties.contains(actionNode.toElement().attribute("name")))
00144     {
00145       QDomNode nextNode = actionNode.nextSibling();
00146       actionPropElement.removeChild( actionNode );
00147       actionNode = nextNode;
00148     } else
00149       actionNode = actionNode.nextSibling();
00150   }
00151   
00152   ActionPropertiesMap::ConstIterator it = properties.begin();
00153   const ActionPropertiesMap::ConstIterator end = properties.end();
00154   for (; it != end; ++it )
00155   {
00156     QDomElement action = doc.createElement( "Action" );
00157     action.setAttribute( "name", it.key() );
00158     actionPropElement.appendChild( action );
00159 
00160     const QMap<QString, QString> attributes = (*it);
00161     QMap<QString, QString>::ConstIterator attrIt = attributes.begin();
00162     const QMap<QString, QString>::ConstIterator attrEnd = attributes.end();
00163     for (; attrIt != attrEnd; ++attrIt )
00164       action.setAttribute( attrIt.key(), attrIt.value() );
00165   }
00166 }
00167 
00168 QString KXmlGuiVersionHandler::findVersionNumber( const QString &xml )
00169 {
00170     enum { ST_START, ST_AFTER_OPEN, ST_AFTER_GUI,
00171            ST_EXPECT_VERSION, ST_VERSION_NUM} state = ST_START;
00172     const int length = xml.length();
00173     for (int pos = 0; pos < length; pos++) {
00174         switch (state) {
00175         case ST_START:
00176             if (xml[pos] == '<')
00177                 state = ST_AFTER_OPEN;
00178             break;
00179         case ST_AFTER_OPEN:
00180         {
00181             //Jump to gui..
00182             const int guipos = xml.indexOf("gui", pos, Qt::CaseInsensitive);
00183             if (guipos == -1)
00184                 return QString(); //Reject
00185 
00186             pos = guipos + 2; //Position at i, so we're moved ahead to the next character by the ++;
00187             state = ST_AFTER_GUI;
00188             break;
00189         }
00190         case ST_AFTER_GUI:
00191             state = ST_EXPECT_VERSION;
00192             break;
00193         case ST_EXPECT_VERSION:
00194         {
00195             const int verpos = xml.indexOf("version", pos, Qt::CaseInsensitive);
00196             if (verpos == -1)
00197                 return QString(); //Reject
00198             pos = verpos + 7; // strlen("version") is 7
00199             while (xml.at(pos).isSpace())
00200                 ++pos;
00201             if (xml.at(pos++) != '=')
00202                 return QString(); //Reject
00203             while (xml.at(pos).isSpace())
00204                 ++pos;
00205 
00206             state = ST_VERSION_NUM;
00207             break;
00208         }
00209         case ST_VERSION_NUM:
00210         {
00211             int endpos;
00212             for (endpos = pos; endpos < length; endpos++) {
00213                 const ushort ch = xml[endpos].unicode();
00214                 if (ch >= '0' && ch <= '9')
00215                     continue; //Number..
00216                 if (ch == '"') //End of parameter
00217                     break;
00218                 else { //This shouldn't be here..
00219                     endpos = length;
00220                 }
00221             }
00222 
00223             if (endpos != pos && endpos < length ) {
00224                 const QString matchCandidate = xml.mid(pos, endpos - pos); //Don't include " ".
00225                 return matchCandidate;
00226             }
00227 
00228             state = ST_EXPECT_VERSION; //Try to match a well-formed version..
00229             break;
00230         } //case..
00231         } //switch
00232     } //for
00233 
00234     return QString();
00235 }
00236 
00237 
00238 KXmlGuiVersionHandler::KXmlGuiVersionHandler(const QStringList& files)
00239 {
00240     Q_ASSERT(!files.isEmpty());
00241 
00242     if (files.count() == 1) {
00243         // No need to parse version numbers if there's only one file anyway
00244         m_file = files.first();
00245         m_doc = KXMLGUIFactory::readConfigFile(m_file);
00246         return;
00247     }
00248 
00249 
00250     QList<DocStruct> allDocuments;
00251 
00252     foreach (const QString &file, files) {
00253         DocStruct d;
00254         d.file = file;
00255         d.data = KXMLGUIFactory::readConfigFile( file );
00256         allDocuments.append( d );
00257     }
00258 
00259     QList<DocStruct>::iterator best = allDocuments.end();
00260     uint bestVersion = 0;
00261 
00262     QList<DocStruct>::iterator docIt = allDocuments.begin();
00263     const QList<DocStruct>::iterator docEnd = allDocuments.end();
00264     for (; docIt != docEnd; ++docIt ) {
00265         const QString versionStr = findVersionNumber( (*docIt).data );
00266         if ( versionStr.isEmpty() ) {
00267             kDebug(260) << "found no version in" << (*docIt).file;
00268             continue;
00269         }
00270 
00271         bool ok = false;
00272         uint version = versionStr.toUInt( &ok );
00273         if ( !ok )
00274             continue;
00275         //kDebug(260) << "found version" << version << "for" << (*docIt).file;
00276 
00277         if ( version > bestVersion ) {
00278             best = docIt;
00279             //kDebug(260) << "best version is now " << version;
00280             bestVersion = version;
00281         }
00282     }
00283 
00284     if ( best != docEnd ) {
00285         if ( best != allDocuments.begin() ) {
00286             QList<DocStruct>::iterator local = allDocuments.begin();
00287 
00288             if ( (*local).file.startsWith(KGlobal::dirs()->localkdedir()) ||
00289                  (*local).file.startsWith(KGlobal::dirs()->saveLocation("appdata")) ) {
00290                 // load the local document and extract the action properties
00291                 QDomDocument localDocument;
00292                 localDocument.setContent( (*local).data );
00293 
00294                 const ActionPropertiesMap properties = extractActionProperties(localDocument);
00295                 const QList<QDomElement> toolbars = extractToolBars(localDocument);
00296 
00297                 // in case the document has a ActionProperties section
00298                 // we must not delete it but copy over the global doc
00299                 // to the local and insert the ActionProperties section
00300 
00301                 // TODO: kedittoolbar should mark toolbars as modified so that
00302                 // we don't keep old toolbars just because the user defined a shortcut
00303 
00304                 if ( !properties.isEmpty() || !toolbars.isEmpty() ) {
00305                     // now load the global one with the higher version number
00306                     // into memory
00307                     QDomDocument document;
00308                     document.setContent( (*best).data );
00309                     // and store the properties in there
00310                     storeActionProperties( document, properties );
00311                     if (!toolbars.isEmpty()) {
00312                         // remove application toolbars
00313                         removeAllToolBars(document);
00314                         // add user toolbars
00315                         insertToolBars(document, toolbars);
00316                     }
00317 
00318                     (*local).data = document.toString();
00319                     // make sure we pick up the new local doc, when we return later
00320                     best = local;
00321 
00322                     // write out the new version of the local document
00323                     QFile f( (*local).file );
00324                     if ( f.open( QIODevice::WriteOnly ) )
00325                     {
00326                         const QByteArray utf8data = (*local).data.toUtf8();
00327                         f.write( utf8data.constData(), utf8data.length() );
00328                         f.close();
00329                     }
00330                 } else {
00331                     // Move away the outdated local file, to speed things up next time
00332                     const QString f = (*local).file;
00333                     const QString backup = f + QLatin1String( ".backup" );
00334                     QFile::rename( f, backup );
00335                 }
00336             }
00337         }
00338         m_doc = (*best).data;
00339         m_file = (*best).file;
00340     } else {
00341         //kDebug(260) << "returning first one...";
00342         m_doc = allDocuments.first().data;
00343         m_file = allDocuments.first().file;
00344     }
00345 }

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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