KXmlGui

kxmlguiversionhandler.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2000 Simon Hausmann <[email protected]>
4  SPDX-FileCopyrightText: 2000 Kurt Granroth <[email protected]>
5  SPDX-FileCopyrightText: 2007 David Faure <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "kxmlguiversionhandler_p.h"
11 
12 #include "kxmlguiclient.h"
13 #include "kxmlguifactory.h"
14 
15 #include <QFile>
16 #include <QDomDocument>
17 #include <QDomElement>
18 #include <QStandardPaths>
19 #include <QMap>
20 
21 struct DocStruct {
22  QString file;
23  QString data;
24 };
25 
26 static QList<QDomElement> extractToolBars(const QDomDocument &doc)
27 {
28  QList<QDomElement> toolbars;
29  QDomElement parent = doc.documentElement();
30  for (QDomElement e = parent.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
31  if (e.tagName().compare(QStringLiteral("ToolBar"), Qt::CaseInsensitive) == 0) {
32  toolbars.append(e);
33  }
34  }
35  return toolbars;
36 }
37 
38 static void removeAllToolBars(QDomDocument &doc)
39 {
40  QDomElement parent = doc.documentElement();
41  const QList<QDomElement> toolBars = extractToolBars(doc);
42  for (const QDomElement &e : toolBars) {
43  parent.removeChild(e);
44  }
45 }
46 
47 static void insertToolBars(QDomDocument &doc, const QList<QDomElement> &toolBars)
48 {
49  QDomElement parent = doc.documentElement();
50  QDomElement menuBar = parent.namedItem(QStringLiteral("MenuBar")).toElement();
51  QDomElement insertAfter = menuBar;
52  if (menuBar.isNull()) {
53  insertAfter = parent.firstChildElement(); // if null, insertAfter will do an append
54  }
55  for (const QDomElement &e : toolBars) {
56  QDomNode result = parent.insertAfter(e, insertAfter);
57  Q_ASSERT(!result.isNull());
58  }
59 }
60 
61 //
62 
64 
65 static ActionPropertiesMap extractActionProperties(const QDomDocument &doc)
66 {
67  ActionPropertiesMap properties;
68 
69  QDomElement actionPropElement = doc.documentElement().namedItem(QStringLiteral("ActionProperties")).toElement();
70 
71  if (actionPropElement.isNull()) {
72  return properties;
73  }
74 
75  QDomNode n = actionPropElement.firstChild();
76  while (!n.isNull()) {
77  QDomElement e = n.toElement();
78  n = n.nextSibling(); // Advance now so that we can safely delete e
79  if (e.isNull()) {
80  continue;
81  }
82 
83  if (e.tagName().compare(QStringLiteral("action"), Qt::CaseInsensitive) != 0) {
84  continue;
85  }
86 
87  const QString actionName = e.attribute(QStringLiteral("name"));
88  if (actionName.isEmpty()) {
89  continue;
90  }
91 
92  QMap<QString, QMap<QString, QString> >::Iterator propIt = properties.find(actionName);
93  if (propIt == properties.end()) {
94  propIt = properties.insert(actionName, QMap<QString, QString>());
95  }
96 
97  const QDomNamedNodeMap attributes = e.attributes();
98  const int attributeslength = attributes.length();
99 
100  for (int i = 0; i < attributeslength; ++i) {
101  const QDomAttr attr = attributes.item(i).toAttr();
102 
103  if (attr.isNull()) {
104  continue;
105  }
106 
107  const QString name = attr.name();
108 
109  if (name == QLatin1String("name") || name.isEmpty()) {
110  continue;
111  }
112 
113  (*propIt)[ name ] = attr.value();
114  }
115 
116  }
117 
118  return properties;
119 }
120 
121 static void storeActionProperties(QDomDocument &doc,
122  const ActionPropertiesMap &properties)
123 {
124  QDomElement actionPropElement = doc.documentElement().namedItem(QStringLiteral("ActionProperties")).toElement();
125 
126  if (actionPropElement.isNull()) {
127  actionPropElement = doc.createElement(QStringLiteral("ActionProperties"));
128  doc.documentElement().appendChild(actionPropElement);
129  }
130 
131 //Remove only those ActionProperties entries from the document, that are present
132 //in the properties argument. In real life this means that local ActionProperties
133 //takes precedence over global ones, if they exists (think local override of shortcuts).
134  QDomNode actionNode = actionPropElement.firstChild();
135  while (!actionNode.isNull()) {
136  if (properties.contains(actionNode.toElement().attribute(QStringLiteral("name")))) {
137  QDomNode nextNode = actionNode.nextSibling();
138  actionPropElement.removeChild(actionNode);
139  actionNode = nextNode;
140  } else {
141  actionNode = actionNode.nextSibling();
142  }
143  }
144 
145  ActionPropertiesMap::ConstIterator it = properties.begin();
146  const ActionPropertiesMap::ConstIterator end = properties.end();
147  for (; it != end; ++it) {
148  QDomElement action = doc.createElement(QStringLiteral("Action"));
149  action.setAttribute(QStringLiteral("name"), it.key());
150  actionPropElement.appendChild(action);
151 
152  const QMap<QString, QString> attributes = (*it);
153  QMap<QString, QString>::ConstIterator attrIt = attributes.begin();
154  const QMap<QString, QString>::ConstIterator attrEnd = attributes.end();
155  for (; attrIt != attrEnd; ++attrIt) {
156  action.setAttribute(attrIt.key(), attrIt.value());
157  }
158  }
159 }
160 
161 KXmlGuiVersionHandler::KXmlGuiVersionHandler(const QStringList &files)
162 {
163  Q_ASSERT(!files.isEmpty());
164 
165  if (files.count() == 1) {
166  // No need to parse version numbers if there's only one file anyway
167  m_file = files.first();
168  m_doc = KXMLGUIFactory::readConfigFile(m_file);
169  return;
170  }
171 
172  QList<DocStruct> allDocuments;
173 
174  for (const QString &file : files) {
175  DocStruct d;
176  d.file = file;
177  d.data = KXMLGUIFactory::readConfigFile(file);
178  allDocuments.append(d);
179  }
180 
181  QList<DocStruct>::iterator best = allDocuments.end();
182  uint bestVersion = 0;
183 
184  QList<DocStruct>::iterator docIt = allDocuments.begin();
185  const QList<DocStruct>::iterator docEnd = allDocuments.end();
186  for (; docIt != docEnd; ++docIt) {
187  const QString versionStr = KXMLGUIClient::findVersionNumber((*docIt).data);
188  if (versionStr.isEmpty()) {
189  //qCDebug(DEBUG_KXMLGUI) << "found no version in" << (*docIt).file;
190  continue;
191  }
192 
193  bool ok = false;
194  uint version = versionStr.toUInt(&ok);
195  if (!ok) {
196  continue;
197  }
198  //qCDebug(DEBUG_KXMLGUI) << "found version" << version << "for" << (*docIt).file;
199 
200  if (version > bestVersion) {
201  best = docIt;
202  //qCDebug(DEBUG_KXMLGUI) << "best version is now " << version;
203  bestVersion = version;
204  }
205  }
206 
207  if (best != docEnd) {
208  if (best != allDocuments.begin()) {
209  QList<DocStruct>::iterator local = allDocuments.begin();
210 
212  // load the local document and extract the action properties
213  QDomDocument localDocument;
214  localDocument.setContent((*local).data);
215 
216  const ActionPropertiesMap properties = extractActionProperties(localDocument);
217  const QList<QDomElement> toolbars = extractToolBars(localDocument);
218 
219  // in case the document has a ActionProperties section
220  // we must not delete it but copy over the global doc
221  // to the local and insert the ActionProperties section
222 
223  // TODO: kedittoolbar should mark toolbars as modified so that
224  // we don't keep old toolbars just because the user defined a shortcut
225 
226  if (!properties.isEmpty() || !toolbars.isEmpty()) {
227  // now load the global one with the higher version number
228  // into memory
229  QDomDocument document;
230  document.setContent((*best).data);
231  // and store the properties in there
232  storeActionProperties(document, properties);
233  if (!toolbars.isEmpty()) {
234  // remove application toolbars
235  removeAllToolBars(document);
236  // add user toolbars
237  insertToolBars(document, toolbars);
238  }
239 
240  (*local).data = document.toString();
241  // make sure we pick up the new local doc, when we return later
242  best = local;
243 
244  // write out the new version of the local document
245  QFile f((*local).file);
246  if (f.open(QIODevice::WriteOnly)) {
247  const QByteArray utf8data = (*local).data.toUtf8();
248  f.write(utf8data.constData(), utf8data.length());
249  f.close();
250  }
251  } else {
252  // Move away the outdated local file, to speed things up next time
253  const QString f = (*local).file;
254  const QString backup = f + QLatin1String(".backup");
255  QFile::rename(f, backup);
256  }
257  }
258  }
259  m_doc = (*best).data;
260  m_file = (*best).file;
261  } else {
262  //qCDebug(DEBUG_KXMLGUI) << "returning first one...";
263  m_doc = allDocuments.first().data;
264  m_file = allDocuments.first().file;
265  }
266 }
static QString readConfigFile(const QString &filename, const QString &componentName=QString())
QString name(const QVariant &location)
QString writableLocation(QStandardPaths::StandardLocation type)
bool contains(const Key &key) const const
QString name() const const
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const const
QString toString(int indent) const const
QDomNode insertAfter(const QDomNode &newChild, const QDomNode &refChild)
bool rename(const QString &newName)
QDomElement documentElement() const const
int length() const const
QDomNode nextSibling() const const
QDomElement toElement() const const
int count(const T &value) const const
void append(const T &value)
KGuiItem properties()
void setAttribute(const QString &name, const QString &value)
CaseInsensitive
bool isEmpty() const const
bool isEmpty() const const
const char * constData() const const
T & first()
QMap::iterator end()
QMap::iterator begin()
QList::iterator end()
QDomNode removeChild(const QDomNode &oldChild)
QDomNode namedItem(const QString &name) const const
QString value() const const
bool isNull() const const
const Key key(const T &value, const Key &defaultKey) const const
const QList< QKeySequence > & end()
QDomNode firstChild() const const
int length() const const
QDomElement firstChildElement(const QString &tagName) const const
QDomAttr toAttr() const const
char * data()
QMap::iterator insert(const Key &key, const T &value)
bool isEmpty() const const
QString tagName() const const
QDomElement createElement(const QString &tagName)
typedef ConstIterator
QDomNode item(int index) const const
int compare(const QString &other, Qt::CaseSensitivity cs) const const
QMap::iterator find(const Key &key)
QList::iterator begin()
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
const T value(const Key &key, const T &defaultValue) const const
uint toUInt(bool *ok, int base) const const
QDomNamedNodeMap attributes() const const
KDB_EXPORT KDbVersionInfo version()
static QString findVersionNumber(const QString &xml)
Returns the version number of the given xml data (belonging to an xml rc file)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Aug 12 2020 22:50:46 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.