KDEUI
kxmlguiversionhandler.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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() == "ToolBar") {
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();
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();
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
00169 const int guipos = xml.indexOf("gui", pos, Qt::CaseInsensitive);
00170 if (guipos == -1)
00171 return QString();
00172
00173 pos = guipos + 2;
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();
00185 pos = verpos + 7;
00186 while (xml.at(pos).isSpace())
00187 ++pos;
00188 if (xml.at(pos++) != '=')
00189 return QString();
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;
00203 if (ch == '"')
00204 break;
00205 else {
00206 endpos = length;
00207 }
00208 }
00209
00210 if (endpos != pos && endpos < length ) {
00211 const QString matchCandidate = xml.mid(pos, endpos - pos);
00212 return matchCandidate;
00213 }
00214
00215 state = ST_EXPECT_VERSION;
00216 break;
00217 }
00218 }
00219 }
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
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>::const_iterator best = allDocuments.end();
00247 uint bestVersion = 0;
00248
00249 QList<DocStruct>::const_iterator docIt = allDocuments.begin();
00250 const QList<DocStruct>::const_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
00263
00264 if ( version > bestVersion ) {
00265 best = docIt;
00266
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
00277 QDomDocument document;
00278 document.setContent( (*local).data );
00279
00280 const ActionPropertiesMap properties = extractActionProperties(document);
00281 const QList<QDomElement> toolbars = extractToolBars(document);
00282
00283
00284
00285
00286
00287
00288
00289
00290 if ( !properties.isEmpty() || !toolbars.isEmpty() ) {
00291
00292
00293 document.setContent( (*best).data );
00294
00295 storeActionProperties( document, properties );
00296 if (!toolbars.isEmpty()) {
00297
00298 removeAllToolBars(document);
00299
00300 insertToolBars(document, toolbars);
00301 }
00302
00303 (*local).data = document.toString();
00304
00305 best = local;
00306
00307
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
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
00327 m_doc = allDocuments.first().data;
00328 m_file = allDocuments.first().file;
00329 }
00330 }