• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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 
00030 struct DocStruct
00031 {
00032     QString file;
00033     QString data;
00034 };
00035 
00036 static QList<QDomElement> extractToolBars(const QDomDocument& doc)
00037 {
00038     QList<QDomElement> toolbars;
00039     QDomElement parent = doc.documentElement();
00040     for (QDomElement e = parent.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
00041         if (e.tagName().compare(QLatin1String("ToolBar"), Qt::CaseInsensitive) == 0) {
00042             toolbars.append(e);
00043         }
00044     }
00045     return toolbars;
00046 }
00047 
00048 static void removeAllToolBars(QDomDocument& doc)
00049 {
00050     QDomElement parent = doc.documentElement();
00051     const QList<QDomElement> toolBars = extractToolBars(doc);
00052     foreach(const QDomElement& e, toolBars) {
00053         parent.removeChild(e);
00054     }
00055 }
00056 
00057 static void insertToolBars(QDomDocument& doc, const QList<QDomElement>& toolBars)
00058 {
00059     QDomElement parent = doc.documentElement();
00060     QDomElement menuBar = parent.namedItem("MenuBar").toElement();
00061     QDomElement insertAfter = menuBar;
00062     if (menuBar.isNull())
00063         insertAfter = parent.firstChildElement(); // if null, insertAfter will do an append
00064     foreach(const QDomElement& e, toolBars) {
00065         QDomNode result = parent.insertAfter(e, insertAfter);
00066         Q_ASSERT(!result.isNull());
00067     }
00068 }
00069 
00070 //
00071 
00072 typedef QMap<QString, QMap<QString, QString> > ActionPropertiesMap;
00073 
00074 static ActionPropertiesMap extractActionProperties(const QDomDocument &doc)
00075 {
00076   ActionPropertiesMap properties;
00077 
00078   QDomElement actionPropElement = doc.documentElement().namedItem( "ActionProperties" ).toElement();
00079 
00080   if ( actionPropElement.isNull() )
00081     return properties;
00082 
00083   QDomNode n = actionPropElement.firstChild();
00084   while(!n.isNull())
00085   {
00086     QDomElement e = n.toElement();
00087     n = n.nextSibling(); // Advance now so that we can safely delete e
00088     if ( e.isNull() )
00089       continue;
00090 
00091     if ( e.tagName().compare("action", Qt::CaseInsensitive) != 0 )
00092       continue;
00093 
00094     const QString actionName = e.attribute( "name" );
00095     if ( actionName.isEmpty() )
00096       continue;
00097 
00098     QMap<QString, QMap<QString, QString> >::Iterator propIt = properties.find( actionName );
00099     if ( propIt == properties.end() )
00100       propIt = properties.insert( actionName, QMap<QString, QString>() );
00101 
00102     const QDomNamedNodeMap attributes = e.attributes();
00103     const uint attributeslength = attributes.length();
00104 
00105     for ( uint i = 0; i < attributeslength; ++i )
00106     {
00107       const QDomAttr attr = attributes.item( i ).toAttr();
00108 
00109       if ( attr.isNull() )
00110         continue;
00111 
00112       const QString name = attr.name();
00113 
00114       if ( name == "name" || name.isEmpty() )
00115         continue;
00116 
00117       (*propIt)[ name ] = attr.value();
00118     }
00119 
00120   }
00121 
00122   return properties;
00123 }
00124 
00125 static void storeActionProperties( QDomDocument &doc,
00126                             const ActionPropertiesMap &properties )
00127 {
00128   QDomElement actionPropElement = doc.documentElement().namedItem( "ActionProperties" ).toElement();
00129 
00130   if ( actionPropElement.isNull() )
00131   {
00132     actionPropElement = doc.createElement( "ActionProperties" );
00133     doc.documentElement().appendChild( actionPropElement );
00134   }
00135 
00136   while ( !actionPropElement.firstChild().isNull() )
00137     actionPropElement.removeChild( actionPropElement.firstChild() );
00138 
00139   ActionPropertiesMap::ConstIterator it = properties.begin();
00140   const ActionPropertiesMap::ConstIterator end = properties.end();
00141   for (; it != end; ++it )
00142   {
00143     QDomElement action = doc.createElement( "Action" );
00144     action.setAttribute( "name", it.key() );
00145     actionPropElement.appendChild( action );
00146 
00147     const QMap<QString, QString> attributes = (*it);
00148     QMap<QString, QString>::ConstIterator attrIt = attributes.begin();
00149     const QMap<QString, QString>::ConstIterator attrEnd = attributes.end();
00150     for (; attrIt != attrEnd; ++attrIt )
00151       action.setAttribute( attrIt.key(), attrIt.value() );
00152   }
00153 }
00154 
00155 QString KXmlGuiVersionHandler::findVersionNumber( const QString &xml )
00156 {
00157     enum { ST_START, ST_AFTER_OPEN, ST_AFTER_GUI,
00158            ST_EXPECT_VERSION, ST_VERSION_NUM} state = ST_START;
00159     const int length = xml.length();
00160     for (int pos = 0; pos < length; pos++) {
00161         switch (state) {
00162         case ST_START:
00163             if (xml[pos] == '<')
00164                 state = ST_AFTER_OPEN;
00165             break;
00166         case ST_AFTER_OPEN:
00167         {
00168             //Jump to gui..
00169             const int guipos = xml.indexOf("gui", pos, Qt::CaseInsensitive);
00170             if (guipos == -1)
00171                 return QString(); //Reject
00172 
00173             pos = guipos + 2; //Position at i, so we're moved ahead to the next character by the ++;
00174             state = ST_AFTER_GUI;
00175             break;
00176         }
00177         case ST_AFTER_GUI:
00178             state = ST_EXPECT_VERSION;
00179             break;
00180         case ST_EXPECT_VERSION:
00181         {
00182             const int verpos = xml.indexOf("version", pos, Qt::CaseInsensitive);
00183             if (verpos == -1)
00184                 return QString(); //Reject
00185             pos = verpos + 7; // strlen("version") is 7
00186             while (xml.at(pos).isSpace())
00187                 ++pos;
00188             if (xml.at(pos++) != '=')
00189                 return QString(); //Reject
00190             while (xml.at(pos).isSpace())
00191                 ++pos;
00192 
00193             state = ST_VERSION_NUM;
00194             break;
00195         }
00196         case ST_VERSION_NUM:
00197         {
00198             int endpos;
00199             for (endpos = pos; endpos < length; endpos++) {
00200                 const ushort ch = xml[endpos].unicode();
00201                 if (ch >= '0' && ch <= '9')
00202                     continue; //Number..
00203                 if (ch == '"') //End of parameter
00204                     break;
00205                 else { //This shouldn't be here..
00206                     endpos = length;
00207                 }
00208             }
00209 
00210             if (endpos != pos && endpos < length ) {
00211                 const QString matchCandidate = xml.mid(pos, endpos - pos); //Don't include " ".
00212                 return matchCandidate;
00213             }
00214 
00215             state = ST_EXPECT_VERSION; //Try to match a well-formed version..
00216             break;
00217         } //case..
00218         } //switch
00219     } //for
00220 
00221     return QString();
00222 }
00223 
00224 
00225 KXmlGuiVersionHandler::KXmlGuiVersionHandler(const QStringList& files)
00226 {
00227     Q_ASSERT(!files.isEmpty());
00228 
00229     if (files.count() == 1) {
00230         // No need to parse version numbers if there's only one file anyway
00231         m_file = files.first();
00232         m_doc = KXMLGUIFactory::readConfigFile(m_file);
00233         return;
00234     }
00235 
00236 
00237     QList<DocStruct> allDocuments;
00238 
00239     foreach (const QString &file, files) {
00240         DocStruct d;
00241         d.file = file;
00242         d.data = KXMLGUIFactory::readConfigFile( file );
00243         allDocuments.append( d );
00244     }
00245 
00246     QList<DocStruct>::iterator best = allDocuments.end();
00247     uint bestVersion = 0;
00248 
00249     QList<DocStruct>::iterator docIt = allDocuments.begin();
00250     const QList<DocStruct>::iterator docEnd = allDocuments.end();
00251     for (; docIt != docEnd; ++docIt ) {
00252         const QString versionStr = findVersionNumber( (*docIt).data );
00253         if ( versionStr.isEmpty() ) {
00254             kDebug(260) << "found no version in" << (*docIt).file;
00255             continue;
00256         }
00257 
00258         bool ok = false;
00259         uint version = versionStr.toUInt( &ok );
00260         if ( !ok )
00261             continue;
00262         //kDebug(260) << "found version" << version << "for" << (*docIt).file;
00263 
00264         if ( version > bestVersion ) {
00265             best = docIt;
00266             //kDebug(260) << "best version is now " << version;
00267             bestVersion = version;
00268         }
00269     }
00270 
00271     if ( best != docEnd ) {
00272         if ( best != allDocuments.begin() ) {
00273             QList<DocStruct>::iterator local = allDocuments.begin();
00274 
00275             if ( (*local).file.startsWith(KGlobal::dirs()->localkdedir()) ) {
00276                 // load the local document and extract the action properties
00277                 QDomDocument document;
00278                 document.setContent( (*local).data );
00279 
00280                 const ActionPropertiesMap properties = extractActionProperties(document);
00281                 const QList<QDomElement> toolbars = extractToolBars(document);
00282 
00283                 // in case the document has a ActionProperties section
00284                 // we must not delete it but copy over the global doc
00285                 // to the local and insert the ActionProperties section
00286 
00287                 // TODO: kedittoolbar should mark toolbars as modified so that
00288                 // we don't keep old toolbars just because the user defined a shortcut
00289 
00290                 if ( !properties.isEmpty() || !toolbars.isEmpty() ) {
00291                     // now load the global one with the higher version number
00292                     // into memory
00293                     document.setContent( (*best).data );
00294                     // and store the properties in there
00295                     storeActionProperties( document, properties );
00296                     if (!toolbars.isEmpty()) {
00297                         // remove application toolbars
00298                         removeAllToolBars(document);
00299                         // add user toolbars
00300                         insertToolBars(document, toolbars);
00301                     }
00302 
00303                     (*local).data = document.toString();
00304                     // make sure we pick up the new local doc, when we return later
00305                     best = local;
00306 
00307                     // write out the new version of the local document
00308                     QFile f( (*local).file );
00309                     if ( f.open( QIODevice::WriteOnly ) )
00310                     {
00311                         const QByteArray utf8data = (*local).data.toUtf8();
00312                         f.write( utf8data.constData(), utf8data.length() );
00313                         f.close();
00314                     }
00315                 } else {
00316                     // Move away the outdated local file, to speed things up next time
00317                     const QString f = (*local).file;
00318                     const QString backup = f + QLatin1String( ".backup" );
00319                     QFile::rename( f, backup );
00320                 }
00321             }
00322         }
00323         m_doc = (*best).data;
00324         m_file = (*best).file;
00325     } else {
00326         //kDebug(260) << "returning first one...";
00327         m_doc = allDocuments.first().data;
00328         m_file = allDocuments.first().file;
00329     }
00330 }

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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