1 /* This file is part of the KDE libraries
2 Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
3 Copyright (C) 2000 Kurt Granroth <granroth@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18 */
19
20 #include "kxmlguiclient.h"
21
22 #include "kxmlguiversionhandler_p.h"
23 #include "kxmlguifactory.h"
24 #include "kxmlguibuilder.h"
25 #include "kactioncollection.h"
26
27 #include <QAction>
28 #include <QDir>
29 #include <QFile>
30 #include <QDomDocument>
31 #include <QTextStream>
32 #include <QRegExp>
33 #include <QPointer>
34 #include <QCoreApplication>
35 #include <QStandardPaths>
36 #include <QDebug>
37
38 #include <klocalizedstring.h>
39
40 #include <assert.h>
41
42 #if defined(KCONFIG_BEFORE_5_24)
43 # define authorizeAction authorizeKAction
44 #endif
45
46 class KXMLGUIClientPrivate
47 {
48 public:
KXMLGUIClientPrivate()49 KXMLGUIClientPrivate()
50 : m_componentName(QCoreApplication::applicationName()),
51 m_actionCollection(0),
52 m_parent(0L),
53 m_builder(0L)
54 {
55 m_textTagNames.append(QLatin1String("text"));
56 m_textTagNames.append(QLatin1String("Text"));
57 m_textTagNames.append(QLatin1String("title"));
58 }
~KXMLGUIClientPrivate()59 ~KXMLGUIClientPrivate()
60 {
61 }
62
63 bool mergeXML(QDomElement &base, QDomElement &additive,
64 KActionCollection *actionCollection);
65 bool isEmptyContainer(const QDomElement &base,
66 KActionCollection *actionCollection) const;
67
68 QDomElement findMatchingElement(const QDomElement &base,
69 const QDomElement &additive);
70
71 QString m_componentName;
72
73 QDomDocument m_doc;
74 KActionCollection *m_actionCollection;
75 QDomDocument m_buildDocument;
76 QPointer<KXMLGUIFactory> m_factory;
77 KXMLGUIClient *m_parent;
78 //QPtrList<KXMLGUIClient> m_supers;
79 QList<KXMLGUIClient *> m_children;
80 KXMLGUIBuilder *m_builder;
81 QString m_xmlFile;
82 QString m_localXMLFile;
83 QStringList m_textTagNames;
84
85 // Actions to enable/disable on a state change
86 QMap<QString, KXMLGUIClient::StateChange> m_actionsStateMap;
87 };
88
KXMLGUIClient()89 KXMLGUIClient::KXMLGUIClient()
90 : d(new KXMLGUIClientPrivate)
91 {
92 }
93
KXMLGUIClient(KXMLGUIClient * parent)94 KXMLGUIClient::KXMLGUIClient(KXMLGUIClient *parent)
95 : d(new KXMLGUIClientPrivate)
96 {
97 parent->insertChildClient(this);
98 }
99
~KXMLGUIClient()100 KXMLGUIClient::~KXMLGUIClient()
101 {
102 if (d->m_parent) {
103 d->m_parent->removeChildClient(this);
104 }
105
106 if (d->m_factory) {
107 qWarning() << this << "deleted without having been removed from the factory first. This will leak standalone popupmenus and could lead to crashes.";
108 d->m_factory->forgetClient(this);
109 }
110
111 Q_FOREACH (KXMLGUIClient *client, d->m_children) {
112 if (d->m_factory) {
113 d->m_factory->forgetClient(client);
114 }
115 assert(client->d->m_parent == this);
116 client->d->m_parent = 0;
117 }
118
119 delete d->m_actionCollection;
120 delete d;
121 }
122
action(const char * name) const123 QAction *KXMLGUIClient::action(const char *name) const
124 {
125 QAction *act = actionCollection()->action(QLatin1String(name));
126 if (!act) {
127 Q_FOREACH (KXMLGUIClient *client, d->m_children) {
128 act = client->actionCollection()->action(QLatin1String(name));
129 if (act) {
130 break;
131 }
132 }
133 }
134 return act;
135 }
136
actionCollection() const137 KActionCollection *KXMLGUIClient::actionCollection() const
138 {
139 if (!d->m_actionCollection) {
140 d->m_actionCollection = new KActionCollection(this);
141 d->m_actionCollection->setObjectName(QStringLiteral("KXMLGUIClient-KActionCollection"));
142 }
143 return d->m_actionCollection;
144 }
145
action(const QDomElement & element) const146 QAction *KXMLGUIClient::action(const QDomElement &element) const
147 {
148 return actionCollection()->action(element.attribute(QStringLiteral("name")));
149 }
150
componentName() const151 QString KXMLGUIClient::componentName() const
152 {
153 return d->m_componentName;
154 }
155
domDocument() const156 QDomDocument KXMLGUIClient::domDocument() const
157 {
158 return d->m_doc;
159 }
160
xmlFile() const161 QString KXMLGUIClient::xmlFile() const
162 {
163 return d->m_xmlFile;
164 }
165
localXMLFile() const166 QString KXMLGUIClient::localXMLFile() const
167 {
168 if (!d->m_localXMLFile.isEmpty()) {
169 return d->m_localXMLFile;
170 }
171
172 if (!QDir::isRelativePath(d->m_xmlFile)) {
173 return QString(); // can't save anything here
174 }
175
176 if (d->m_xmlFile.isEmpty()) { // setXMLFile not called at all, can't save. Use case: ToolBarHandler
177 return QString();
178 }
179
180 return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/kxmlgui5/") +
181 componentName() + QLatin1Char('/') + d->m_xmlFile;
182 }
183
reloadXML()184 void KXMLGUIClient::reloadXML()
185 {
186 // TODO: this method can't be used for the KXmlGuiWindow, since it doesn't merge in ui_standards.xmlgui!
187 // -> KDE5: load ui_standards_rc in setXMLFile using a flag, and remember that flag?
188 // and then KEditToolBar can use reloadXML.
189 QString file(xmlFile());
190 if (!file.isEmpty()) {
191 setXMLFile(file);
192 }
193 }
194
setComponentName(const QString & componentName,const QString & componentDisplayName)195 void KXMLGUIClient::setComponentName(const QString &componentName, const QString &componentDisplayName)
196 {
197 d->m_componentName = componentName;
198 actionCollection()->setComponentName(componentName);
199 actionCollection()->setComponentDisplayName(componentDisplayName);
200 if (d->m_builder) {
201 d->m_builder->setBuilderClient(this);
202 }
203 }
204
standardsXmlFileLocation()205 QString KXMLGUIClient::standardsXmlFileLocation()
206 {
207 QString file = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("ui/ui_standards.xmlgui"));
208 if (file.isEmpty()) {
209 // fallback to resource, to allow to use the rc file compiled into this framework, must exist!
210 file = QStringLiteral(":/kxmlgui5/ui_standards.xmlgui");
211 Q_ASSERT(QFile::exists(file));
212 }
213 return file;
214 }
215
loadStandardsXmlFile()216 void KXMLGUIClient::loadStandardsXmlFile()
217 {
218 setXML(KXMLGUIFactory::readConfigFile(standardsXmlFileLocation()));
219 }
220
setXMLFile(const QString & _file,bool merge,bool setXMLDoc)221 void KXMLGUIClient::setXMLFile(const QString &_file, bool merge, bool setXMLDoc)
222 {
223 // store our xml file name
224 if (!_file.isNull()) {
225 d->m_xmlFile = _file;
226 }
227
228 if (!setXMLDoc) {
229 return;
230 }
231
232 QString file = _file;
233 QStringList allFiles;
234 if (!QDir::isRelativePath(file)) {
235 allFiles.append(file);
236 } else {
237 const QString filter = componentName() + QLatin1Char('/') + _file;
238
239 // files on filesystem
240 allFiles << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("kxmlgui5/") + filter); // KF >= 5.1
241
242 // KF >= 5.4 (resource file)
243 const QString qrcFile(QStringLiteral(":/kxmlgui5/") + filter);
244 if (QFile::exists(qrcFile)) {
245 allFiles << qrcFile;
246 }
247
248 // then compat locations
249 const QStringList compatFiles =
250 QStandardPaths::locateAll(QStandardPaths::AppDataLocation, filter) + // kdelibs4, KF 5.0
251 QStandardPaths::locateAll(QStandardPaths::AppDataLocation, _file); // kdelibs4, KF 5.0, caller passes component name
252
253 if (allFiles.isEmpty() && !compatFiles.isEmpty()) {
254 qWarning() << "kxmlguiclient: KXMLGUI file found at deprecated location" << compatFiles << "-- please use ${KXMLGUI_INSTALL_DIR} to install this file instead.";
255 }
256 allFiles += compatFiles;
257 }
258 if (allFiles.isEmpty() && !_file.isEmpty()) {
259 // if a non-empty file gets passed and we can't find it,
260 // inform the developer using some debug output
261 qWarning() << "cannot find .xmlgui file" << _file << "for component" << componentName();
262 }
263
264 // make sure to merge the settings from any file specified by setLocalXMLFile()
265 if (!d->m_localXMLFile.isEmpty() && !file.endsWith(QStringLiteral("ui_standards.xmlgui"))) {
266 const bool exists = QDir::isRelativePath(d->m_localXMLFile) || QFile::exists(d->m_localXMLFile);
267 if (exists && !allFiles.contains(d->m_localXMLFile)) {
268 allFiles.prepend(d->m_localXMLFile);
269 }
270 }
271
272 QString doc;
273 if (!allFiles.isEmpty()) {
274 file = findMostRecentXMLFile(allFiles, doc);
275 }
276
277 // Always call setXML, even on error, so that we don't keep all ui_standards.xmlgui menus.
278 setXML(doc, merge);
279 }
280
setLocalXMLFile(const QString & file)281 void KXMLGUIClient::setLocalXMLFile(const QString &file)
282 {
283 d->m_localXMLFile = file;
284 }
285
replaceXMLFile(const QString & xmlfile,const QString & localxmlfile,bool merge)286 void KXMLGUIClient::replaceXMLFile(const QString &xmlfile, const QString &localxmlfile, bool merge)
287 {
288 if (!QDir::isAbsolutePath(xmlfile)) {
289 qWarning() << "xml file" << xmlfile << "is not an absolute path";
290 }
291
292 setLocalXMLFile(localxmlfile);
293 setXMLFile(xmlfile, merge);
294 }
295
296 // The top document element may have translation domain attribute set,
297 // or the translation domain may be implicitly the application domain.
298 // This domain must be used to fetch translations for all text elements
299 // in the document that do not have their own domain attribute.
300 // In order to preserve this semantics through document mergings,
301 // the top or application domain must be propagated to all text elements
302 // lacking their own domain attribute.
propagateTranslationDomain(QDomDocument & doc,const QStringList tagNames)303 static void propagateTranslationDomain(QDomDocument &doc, const QStringList tagNames)
304 {
305 const QLatin1String attrDomain("translationDomain");
306 QDomElement base = doc.documentElement();
307 QString domain = base.attribute(attrDomain);
308 if (domain.isEmpty()) {
309 domain = QString::fromUtf8(KLocalizedString::applicationDomain());
310 if (domain.isEmpty()) {
311 return;
312 }
313 }
314 foreach (const QString &tagName, tagNames) {
315 QDomNodeList textNodes = base.elementsByTagName(tagName);
316 for (int i = 0; i < textNodes.length(); ++i) {
317 QDomElement e = textNodes.item(i).toElement();
318 QString localDomain = e.attribute(attrDomain);
319 if (localDomain.isEmpty()) {
320 e.setAttribute(attrDomain, domain);
321 }
322 }
323 }
324 }
325
setXML(const QString & document,bool merge)326 void KXMLGUIClient::setXML(const QString &document, bool merge)
327 {
328 QDomDocument doc;
329 QString errorMsg;
330 int errorLine, errorColumn;
331 // QDomDocument raises a parse error on empty document, but we accept no app-specific document,
332 // in which case you only get ui_standards.xmlgui layout.
333 bool result = document.isEmpty() || doc.setContent(document, &errorMsg, &errorLine, &errorColumn);
334 if (result) {
335 propagateTranslationDomain(doc, d->m_textTagNames);
336 setDOMDocument(doc, merge);
337 } else {
338 #ifdef NDEBUG
339 qCritical() << "Error parsing XML document:" << errorMsg << "at line" << errorLine << "column" << errorColumn;
340 setDOMDocument(QDomDocument(), merge); // otherwise empty menus from ui_standards.xmlgui stay around
341 #else
342 qCritical() << "Error parsing XML document:" << errorMsg << "at line" << errorLine << "column" << errorColumn;
343 abort();
344 #endif
345 }
346 }
347
setDOMDocument(const QDomDocument & document,bool merge)348 void KXMLGUIClient::setDOMDocument(const QDomDocument &document, bool merge)
349 {
350 if (merge && !d->m_doc.isNull()) {
351 QDomElement base = d->m_doc.documentElement();
352
353 QDomElement e = document.documentElement();
354
355 // merge our original (global) xml with our new one
356 d->mergeXML(base, e, actionCollection());
357
358 // reassign our pointer as mergeXML might have done something
359 // strange to it
360 base = d->m_doc.documentElement();
361
362 //qDebug(260) << "Result of xmlgui merging:" << d->m_doc.toString();
363
364 // we want some sort of failsafe.. just in case
365 if (base.isNull()) {
366 d->m_doc = document;
367 }
368 } else {
369 d->m_doc = document;
370 }
371
372 setXMLGUIBuildDocument(QDomDocument());
373 }
374
375 // if (equals(a,b)) is more readable than if (a.compare(b, Qt::CaseInsensitive)==0)
equalstr(const QString & a,const QString & b)376 static inline bool equalstr(const QString &a, const QString &b)
377 {
378 return a.compare(b, Qt::CaseInsensitive) == 0;
379 }
equalstr(const QString & a,const QLatin1String & b)380 static inline bool equalstr(const QString &a, const QLatin1String &b)
381 {
382 return a.compare(b, Qt::CaseInsensitive) == 0;
383 }
384
mergeXML(QDomElement & base,QDomElement & additive,KActionCollection * actionCollection)385 bool KXMLGUIClientPrivate::mergeXML(QDomElement &base, QDomElement &additive, KActionCollection *actionCollection)
386 {
387 const QLatin1String tagAction("Action");
388 const QLatin1String tagMerge("Merge");
389 const QLatin1String tagSeparator("Separator");
390 const QLatin1String tagMergeLocal("MergeLocal");
391 const QLatin1String tagText("text");
392 const QLatin1String attrAppend("append");
393 const QString attrName(QStringLiteral("name"));
394 const QString attrWeakSeparator(QStringLiteral("weakSeparator"));
395 const QString attrAlreadyVisited(QStringLiteral("alreadyVisited"));
396 const QString attrNoMerge(QStringLiteral("noMerge"));
397 const QLatin1String attrOne("1");
398
399 // there is a possibility that we don't want to merge in the
400 // additive.. rather, we might want to *replace* the base with the
401 // additive. this can be for any container.. either at a file wide
402 // level or a simple container level. we look for the 'noMerge'
403 // tag, in any event and just replace the old with the new
404 if (additive.attribute(attrNoMerge) == attrOne) { // ### use toInt() instead? (Simon)
405 base.parentNode().replaceChild(additive, base);
406 return true;
407 } else {
408 // Merge attributes
409 {
410 const QDomNamedNodeMap attribs = additive.attributes();
411 const uint attribcount = attribs.count();
412
413 for (uint i = 0; i < attribcount; ++i) {
414 const QDomNode node = attribs.item(i);
415 base.setAttribute(node.nodeName(), node.nodeValue());
416 }
417 }
418
419 // iterate over all elements in the container (of the global DOM tree)
420 QDomNode n = base.firstChild();
421 while (!n.isNull()) {
422 QDomElement e = n.toElement();
423 n = n.nextSibling(); // Advance now so that we can safely delete e
424 if (e.isNull()) {
425 continue;
426 }
427
428 const QString tag = e.tagName();
429
430 // if there's an action tag in the global tree and the action is
431 // not implemented, then we remove the element
432 if (equalstr(tag, tagAction)) {
433 const QString name = e.attribute(attrName);
434 if (!actionCollection->action(name)) {
435 // remove this child as we aren't using it
436 base.removeChild(e);
437 continue;
438 }
439 }
440
441 // if there's a separator defined in the global tree, then add an
442 // attribute, specifying that this is a "weak" separator
443 else if (equalstr(tag, tagSeparator)) {
444 e.setAttribute(attrWeakSeparator, (uint)1);
445
446 // okay, hack time. if the last item was a weak separator OR
447 // this is the first item in a container, then we nuke the
448 // current one
449 QDomElement prev = e.previousSibling().toElement();
450 if (prev.isNull() ||
451 (equalstr(prev.tagName(), tagSeparator) && !prev.attribute(attrWeakSeparator).isNull()) ||
452 (equalstr(prev.tagName(), tagText))) {
453 // the previous element was a weak separator or didn't exist
454 base.removeChild(e);
455 continue;
456 }
457 }
458
459 // the MergeLocal tag lets us specify where non-standard elements
460 // of the local tree shall be merged in. After inserting the
461 // elements we delete this element
462 else if (equalstr(tag, tagMergeLocal)) {
463 QDomNode it = additive.firstChild();
464 while (!it.isNull()) {
465 QDomElement newChild = it.toElement();
466 it = it.nextSibling();
467 if (newChild.isNull()) {
468 continue;
469 }
470
471 if (equalstr(newChild.tagName(), tagText)) {
472 continue;
473 }
474
475 if (newChild.attribute(attrAlreadyVisited) == attrOne) {
476 continue;
477 }
478
479 QString itAppend(newChild.attribute(attrAppend));
480 QString elemName(e.attribute(attrName));
481
482 if ((itAppend.isNull() && elemName.isEmpty()) ||
483 (itAppend == elemName)) {
484 // first, see if this new element matches a standard one in
485 // the global file. if it does, then we skip it as it will
486 // be merged in, later
487 QDomElement matchingElement = findMatchingElement(newChild, base);
488 if (matchingElement.isNull() || equalstr(newChild.tagName(), tagSeparator)) {
489 base.insertBefore(newChild, e);
490 }
491 }
492 }
493
494 base.removeChild(e);
495 continue;
496 }
497
498 else if (equalstr(tag, tagText)) {
499 continue;
500 } else if (equalstr(tag, tagMerge)) {
501 continue;
502 }
503
504 // in this last case we check for a separator tag and, if not, we
505 // can be sure that it is a container --> proceed with child nodes
506 // recursively and delete the just proceeded container item in
507 // case it is empty (if the recursive call returns true)
508 else {
509 QDomElement matchingElement = findMatchingElement(e, additive);
510 if (!matchingElement.isNull()) {
511 matchingElement.setAttribute(attrAlreadyVisited, (uint)1);
512
513 if (mergeXML(e, matchingElement, actionCollection)) {
514 base.removeChild(e);
515 additive.removeChild(matchingElement); // make sure we don't append it below
516 continue;
517 }
518
519 continue;
520 } else {
521 // this is an important case here! We reach this point if the
522 // "local" tree does not contain a container definition for
523 // this container. However we have to call mergeXML recursively
524 // and make it check if there are actions implemented for this
525 // container. *If* none, then we can remove this container now
526 QDomElement dummy;
527 if (mergeXML(e, dummy, actionCollection)) {
528 base.removeChild(e);
529 }
530 continue;
531 }
532 }
533 }
534
535 //here we append all child elements which were not inserted
536 //previously via the LocalMerge tag
537 n = additive.firstChild();
538 while (!n.isNull()) {
539 QDomElement e = n.toElement();
540 n = n.nextSibling(); // Advance now so that we can safely delete e
541 if (e.isNull()) {
542 continue;
543 }
544
545 QDomElement matchingElement = findMatchingElement(e, base);
546
547 if (matchingElement.isNull()) {
548 base.appendChild(e);
549 }
550 }
551
552 // do one quick check to make sure that the last element was not
553 // a weak separator
554 QDomElement last = base.lastChild().toElement();
555 if (equalstr(last.tagName(), tagSeparator) &&
556 (!last.attribute(attrWeakSeparator).isNull())) {
557 base.removeChild(last);
558 }
559 }
560
561 return isEmptyContainer(base, actionCollection);
562 }
563
isEmptyContainer(const QDomElement & base,KActionCollection * actionCollection) const564 bool KXMLGUIClientPrivate::isEmptyContainer(const QDomElement &base, KActionCollection *actionCollection) const
565 {
566 // now we check if we are empty (in which case we return "true", to
567 // indicate the caller that it can delete "us" (the base element
568 // argument of "this" call)
569 QDomNode n = base.firstChild();
570 while (!n.isNull()) {
571 const QDomElement e = n.toElement();
572 n = n.nextSibling(); // Advance now so that we can safely delete e
573 if (e.isNull()) {
574 continue;
575 }
576
577 const QString tag = e.tagName();
578
579 if (equalstr(tag, QLatin1String("Action"))) {
580 // if base contains an implemented action, then we must not get
581 // deleted (note that the actionCollection contains both,
582 // "global" and "local" actions)
583 if (actionCollection->action(e.attribute(QStringLiteral("name")))) {
584 return false;
585 }
586 } else if (equalstr(tag, QLatin1String("Separator"))) {
587 // if we have a separator which has *not* the weak attribute
588 // set, then it must be owned by the "local" tree in which case
589 // we must not get deleted either
590 const QString weakAttr = e.attribute(QStringLiteral("weakSeparator"));
591 if (weakAttr.isEmpty() || weakAttr.toInt() != 1) {
592 return false;
593 }
594 }
595
596 else if (equalstr(tag, QLatin1String("merge"))) {
597 continue;
598 }
599
600 // a text tag is NOT enough to spare this container
601 else if (equalstr(tag, QLatin1String("text"))) {
602 continue;
603 }
604
605 // what's left are non-empty containers! *don't* delete us in this
606 // case (at this position we can be *sure* that the container is
607 // *not* empty, as the recursive call for it was in the first loop
608 // which deleted the element in case the call returned "true"
609 else {
610 return false;
611 }
612 }
613
614 return true; // I'm empty, please delete me.
615 }
616
findMatchingElement(const QDomElement & base,const QDomElement & additive)617 QDomElement KXMLGUIClientPrivate::findMatchingElement(const QDomElement &base, const QDomElement &additive)
618 {
619 QDomNode n = additive.firstChild();
620 while (!n.isNull()) {
621 QDomElement e = n.toElement();
622 n = n.nextSibling(); // Advance now so that we can safely delete e -- TODO we don't, so simplify this
623 if (e.isNull()) {
624 continue;
625 }
626
627 const QString tag = e.tagName();
628 // skip all action and merge tags as we will never use them
629 if (equalstr(tag, QLatin1String("Action"))
630 || equalstr(tag, QLatin1String("MergeLocal"))) {
631 continue;
632 }
633
634 // now see if our tags are equivalent
635 if (equalstr(tag, base.tagName()) &&
636 e.attribute(QStringLiteral("name")) == base.attribute(QStringLiteral("name"))) {
637 return e;
638 }
639 }
640
641 // nope, return a (now) null element
642 return QDomElement();
643 }
644
setXMLGUIBuildDocument(const QDomDocument & doc)645 void KXMLGUIClient::setXMLGUIBuildDocument(const QDomDocument &doc)
646 {
647 d->m_buildDocument = doc;
648 }
649
xmlguiBuildDocument() const650 QDomDocument KXMLGUIClient::xmlguiBuildDocument() const
651 {
652 return d->m_buildDocument;
653 }
654
setFactory(KXMLGUIFactory * factory)655 void KXMLGUIClient::setFactory(KXMLGUIFactory *factory)
656 {
657 d->m_factory = factory;
658 }
659
factory() const660 KXMLGUIFactory *KXMLGUIClient::factory() const
661 {
662 return d->m_factory;
663 }
664
parentClient() const665 KXMLGUIClient *KXMLGUIClient::parentClient() const
666 {
667 return d->m_parent;
668 }
669
insertChildClient(KXMLGUIClient * child)670 void KXMLGUIClient::insertChildClient(KXMLGUIClient *child)
671 {
672 if (child->d->m_parent) {
673 child->d->m_parent->removeChildClient(child);
674 }
675 d->m_children.append(child);
676 child->d->m_parent = this;
677 }
678
removeChildClient(KXMLGUIClient * child)679 void KXMLGUIClient::removeChildClient(KXMLGUIClient *child)
680 {
681 assert(d->m_children.contains(child));
682 d->m_children.removeAll(child);
683 child->d->m_parent = 0;
684 }
685
686 /*bool KXMLGUIClient::addSuperClient( KXMLGUIClient *super )
687 {
688 if ( d->m_supers.contains( super ) )
689 return false;
690 d->m_supers.append( super );
691 return true;
692 }*/
693
childClients()694 QList<KXMLGUIClient *> KXMLGUIClient::childClients()
695 {
696 return d->m_children;
697 }
698
setClientBuilder(KXMLGUIBuilder * builder)699 void KXMLGUIClient::setClientBuilder(KXMLGUIBuilder *builder)
700 {
701 d->m_builder = builder;
702 }
703
clientBuilder() const704 KXMLGUIBuilder *KXMLGUIClient::clientBuilder() const
705 {
706 return d->m_builder;
707 }
708
plugActionList(const QString & name,const QList<QAction * > & actionList)709 void KXMLGUIClient::plugActionList(const QString &name, const QList<QAction *> &actionList)
710 {
711 if (!d->m_factory) {
712 return;
713 }
714
715 d->m_factory->plugActionList(this, name, actionList);
716 }
717
unplugActionList(const QString & name)718 void KXMLGUIClient::unplugActionList(const QString &name)
719 {
720 if (!d->m_factory) {
721 return;
722 }
723
724 d->m_factory->unplugActionList(this, name);
725 }
726
findMostRecentXMLFile(const QStringList & files,QString & doc)727 QString KXMLGUIClient::findMostRecentXMLFile(const QStringList &files, QString &doc)
728 {
729 KXmlGuiVersionHandler versionHandler(files);
730 doc = versionHandler.finalDocument();
731 return versionHandler.finalFile();
732 }
733
addStateActionEnabled(const QString & state,const QString & action)734 void KXMLGUIClient::addStateActionEnabled(const QString &state,
735 const QString &action)
736 {
737 StateChange stateChange = getActionsToChangeForState(state);
738
739 stateChange.actionsToEnable.append(action);
740 //qDebug(260) << "KXMLGUIClient::addStateActionEnabled( " << state << ", " << action << ")";
741
742 d->m_actionsStateMap.insert(state, stateChange);
743 }
744
addStateActionDisabled(const QString & state,const QString & action)745 void KXMLGUIClient::addStateActionDisabled(const QString &state,
746 const QString &action)
747 {
748 StateChange stateChange = getActionsToChangeForState(state);
749
750 stateChange.actionsToDisable.append(action);
751 //qDebug(260) << "KXMLGUIClient::addStateActionDisabled( " << state << ", " << action << ")";
752
753 d->m_actionsStateMap.insert(state, stateChange);
754 }
755
getActionsToChangeForState(const QString & state)756 KXMLGUIClient::StateChange KXMLGUIClient::getActionsToChangeForState(const QString &state)
757 {
758 return d->m_actionsStateMap[state];
759 }
760
stateChanged(const QString & newstate,KXMLGUIClient::ReverseStateChange reverse)761 void KXMLGUIClient::stateChanged(const QString &newstate, KXMLGUIClient::ReverseStateChange reverse)
762 {
763 StateChange stateChange = getActionsToChangeForState(newstate);
764
765 bool setTrue = (reverse == StateNoReverse);
766 bool setFalse = !setTrue;
767
768 // Enable actions which need to be enabled...
769 //
770 for (QStringList::const_iterator it = stateChange.actionsToEnable.constBegin();
771 it != stateChange.actionsToEnable.constEnd(); ++it) {
772
773 QAction *action = actionCollection()->action(*it);
774 if (action) {
775 action->setEnabled(setTrue);
776 }
777 }
778
779 // and disable actions which need to be disabled...
780 //
781 for (QStringList::const_iterator it = stateChange.actionsToDisable.constBegin();
782 it != stateChange.actionsToDisable.constEnd(); ++it) {
783
784 QAction *action = actionCollection()->action(*it);
785 if (action) {
786 action->setEnabled(setFalse);
787 }
788 }
789
790 }
791
beginXMLPlug(QWidget * w)792 void KXMLGUIClient::beginXMLPlug(QWidget *w)
793 {
794 actionCollection()->addAssociatedWidget(w);
795 foreach (KXMLGUIClient *client, d->m_children) {
796 client->beginXMLPlug(w);
797 }
798 }
799
endXMLPlug()800 void KXMLGUIClient::endXMLPlug()
801 {
802 }
803
prepareXMLUnplug(QWidget * w)804 void KXMLGUIClient::prepareXMLUnplug(QWidget *w)
805 {
806 actionCollection()->removeAssociatedWidget(w);
807 foreach (KXMLGUIClient *client, d->m_children) {
808 client->prepareXMLUnplug(w);
809 }
810 }
811
virtual_hook(int,void *)812 void KXMLGUIClient::virtual_hook(int, void *)
813 {
814 /*BASE::virtual_hook( id, data );*/
815 }
816