1 /***************************************************************************
2 rkxmlguisyncer.cpp - description
3 -------------------
4 begin : Wed Aug 5 2009
5 copyright : (C) 2009 by Thomas Friedrichsmeier
6 email : thomas.friedrichsmeier@kdemail.net
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "rkxmlguisyncer.h"
19 #include "rkxmlguisyncer_p.h"
20
21 #include "../debug.h"
22
23 RKXMLGUISyncer* RKXMLGUISyncer::syncer = 0;
24
25 //static
self()26 RKXMLGUISyncer *RKXMLGUISyncer::self () {
27 RK_TRACE (MISC);
28
29 if (!syncer) syncer = new RKXMLGUISyncer ();
30 return syncer;
31 }
32
RKXMLGUISyncer()33 RKXMLGUISyncer::RKXMLGUISyncer () : d (new RKXMLGUISyncerPrivate) {
34 RK_TRACE (MISC);
35 }
36
~RKXMLGUISyncer()37 RKXMLGUISyncer::~RKXMLGUISyncer () {
38 RK_TRACE (MISC);
39 delete d;
40 }
41
watchXMLGUIClientUIrc(KXMLGUIClient * client,bool recursive)42 void RKXMLGUISyncer::watchXMLGUIClientUIrc (KXMLGUIClient *client, bool recursive) {
43 RK_TRACE (MISC);
44
45 KActionCollection *ac = client->actionCollection ();
46 QString local_xml_file = client->localXMLFile ();
47
48 // local_xml_file *can* be the name of a directory, if the client->xmlFile() is empty.
49 if (ac && (!local_xml_file.isEmpty()) && (!QDir (local_xml_file).exists ())) {
50 RK_ASSERT (ac->parentGUIClient () == client);
51
52 if (!d->client_map.contains (local_xml_file, ac)) {
53 if (!d->client_map.contains (local_xml_file)) {
54 d->file_watcher->addFile (local_xml_file);
55 }
56
57 d->client_map.insertMulti (local_xml_file, ac);
58 d->connect (ac, &KActionCollection::destroyed, d, &RKXMLGUISyncerPrivate::actionCollectionDestroyed);
59 } // we simply ignore attempts to watch the same client twice
60 }
61
62 if (recursive) {
63 foreach (KXMLGUIClient *child, client->childClients ()) {
64 watchXMLGUIClientUIrc (child, true);
65 }
66 }
67 }
68
registerChangeListener(KXMLGUIClient * watched_client,QObject * receiver,const char * method)69 void RKXMLGUISyncer::registerChangeListener (KXMLGUIClient *watched_client, QObject *receiver, const char *method) {
70 RK_TRACE (MISC);
71
72 KActionCollection *ac = watched_client->actionCollection ();
73
74 RKXMLGUISyncerNotifier *notifier = new RKXMLGUISyncerNotifier (0);
75 d->connect (notifier, SIGNAL (changed(KXMLGUIClient*)), receiver, method);
76
77 d->notifier_map.insertMulti (ac, notifier);
78 }
79
80
81
uiRcFileChanged(const QString & path)82 void RKXMLGUISyncerPrivate::uiRcFileChanged (const QString &path) {
83 RK_TRACE (MISC);
84
85 RK_ASSERT (client_map.contains (path));
86
87 // find affected clients and reload them
88 QMultiHash<QString, KActionCollection*>::const_iterator i = client_map.find(path);
89 while (i != client_map.constEnd() && i.key() == path) {
90 KXMLGUIClient *client = const_cast<KXMLGUIClient*> (i.value ()->parentGUIClient ());
91 if (!client) {
92 RK_ASSERT (false);
93 continue;
94 }
95 RK_ASSERT (client->localXMLFile () == path);
96 client->reloadXML ();
97 RK_DEBUG (MISC, DL_DEBUG, "reloaded client %p for file %s", client, qPrintable (path));
98 if (client->factory ()) {
99 affected_factories.insert (client->factory ());
100 connect (client->factory (), &KXMLGUIFactory::destroyed, this, &RKXMLGUISyncerPrivate::guiFactoryDestroyed);
101 }
102
103 // find notifiers listening for this client
104 QMultiHash<KActionCollection*, RKXMLGUISyncerNotifier*>::const_iterator n = notifier_map.find(i.value ());
105 while (n != notifier_map.constEnd() && n.key() == i.value ()) {
106 n.value ()->emitChangeSignal (client);
107 ++n;
108 }
109
110 ++i;
111 }
112
113 rebuild_guis_timer.start (0);
114 }
115
rebuildGUIs()116 void RKXMLGUISyncerPrivate::rebuildGUIs () {
117 RK_TRACE (MISC);
118
119 while (!affected_factories.isEmpty ()) {
120 KXMLGUIFactory *factory = *(affected_factories.begin ());
121 affected_factories.remove (factory);
122
123 RK_DEBUG (MISC, DL_DEBUG, "rebuilding factory %p", factory);
124 QList<KXMLGUIClient*> clients = factory->clients ();
125 for (int i = clients.size () - 1; i >= 0; --i) {
126 factory->removeClient (clients[i]);
127 }
128 for (int i = 0; i < clients.size (); ++i) {
129 factory->addClient (clients[i]);
130 if (clients[i]->xmlFile ().isEmpty ()) {
131 // somehow config-based settings get lost above, so re-read them, now.
132 clients[i]->actionCollection ()->readSettings();
133 }
134 }
135 RK_DEBUG (MISC, DL_DEBUG, "done rebuilding factory");
136 }
137 }
138
actionCollectionDestroyed(QObject * object)139 void RKXMLGUISyncerPrivate::actionCollectionDestroyed (QObject *object) {
140 RK_TRACE (MISC);
141
142 // warning: Do not call any methods on the ac. It is half-destroyed, already.
143 KActionCollection *ac = static_cast<KActionCollection*> (object);
144
145 // stop listening for the corresponding client
146 QString path_key = client_map.key (static_cast<KActionCollection*> (object));
147 client_map.remove (path_key, ac);
148
149 // if there are no further clients with this path, stop watching it.
150 if (!client_map.contains (path_key)) {
151 file_watcher->removeFile (path_key);
152 }
153
154 // remove any notifiers, too
155 RKXMLGUISyncerNotifier* notifier;
156 while ((notifier = notifier_map.take (ac))) {
157 notifier->deleteLater ();
158 }
159
160 RK_DEBUG (MISC, DL_DEBUG, "action collection destroyed. Still watch %d clients with %d notifiers", client_map.size (), notifier_map.size ());
161 }
162
guiFactoryDestroyed(QObject * object)163 void RKXMLGUISyncerPrivate::guiFactoryDestroyed (QObject *object) {
164 RK_TRACE (MISC);
165
166 affected_factories.remove (static_cast<KXMLGUIFactory*>(object));
167 }
168
169