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