Fawkes API Fawkes Development Version
plugin_tree_view.cpp
1
2/***************************************************************************
3 * plugin_tree_view.cpp - Displays a list of Fawkes plugins and allows to
4 * start/stop them
5 *
6 * Created: Fri Sep 26 21:13:48 2008
7 * Copyright 2008 Daniel Beck
8 * 2008 Tim Niemueller [www.niemueller.de]
9 *
10 ****************************************************************************/
11
12/* This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * Read the full text in the LICENSE.GPL file in the doc directory.
23 */
24
25#include <gui_utils/plugin_tree_view.h>
26#include <gui_utils/twolines_cellrenderer.h>
27#include <netcomm/fawkes/client.h>
28#include <plugin/net/list_message.h>
29#include <plugin/net/messages.h>
30
31#include <cstring>
32#include <string>
33
34using namespace std;
35
36namespace fawkes {
37
38/** @class PluginTreeView <gui_utils/plugin_tree_view.h>
39 * A TreeView class to list available plugins und trigger their
40 * loading/unloading.
41 *
42 * @author Daniel Beck
43 * @author Tim Niemueller
44 */
45
46/** @class PluginTreeView::PluginRecord <gui_utils/plugin_tree_view.h>
47 * Column record class for the plugin tree view.
48 *
49 * @author Daniel Beck
50 */
51
52/** @var PluginTreeView::m_plugin_list
53 * Storage object for the plugin data.
54 */
55
56/** @var PluginTreeView::m_plugin_record
57 * Column record object.
58 */
59
60/** Constructor. */
61PluginTreeView::PluginTreeView() : m_dispatcher(FAWKES_CID_PLUGINMANAGER)
62{
63 ctor();
64}
65
66/** Constructor.
67 * @param cobject pointer to base object type
68 * @param builder Gtk::Builder instance
69 */
70PluginTreeView::PluginTreeView(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> builder)
71: Gtk::TreeView(cobject), m_dispatcher(FAWKES_CID_PLUGINMANAGER)
72{
73 ctor();
74}
75
76void
77PluginTreeView::ctor()
78{
79 m_plugin_list = Gtk::ListStore::create(m_plugin_record);
80 set_model(m_plugin_list);
81 set_rules_hint(true);
82 append_column("#", m_plugin_record.index);
83 append_column_editable("Status", m_plugin_record.loaded);
84 append_plugin_column();
85
86 on_name_clicked();
87 Gtk::TreeViewColumn *column = get_column(0);
88 column->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_id_clicked));
89 column = get_column(1);
90 column->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_status_clicked));
91
92 Gtk::CellRendererToggle *renderer;
93 renderer = dynamic_cast<Gtk::CellRendererToggle *>(get_column_cell_renderer(1));
94 renderer->signal_toggled().connect(sigc::mem_fun(*this, &PluginTreeView::on_status_toggled));
95
96 set_search_column(1);
97
98 m_dispatcher.signal_connected().connect(sigc::mem_fun(*this, &PluginTreeView::on_connected));
99 m_dispatcher.signal_disconnected().connect(
100 sigc::mem_fun(*this, &PluginTreeView::on_disconnected));
101 m_dispatcher.signal_message_received().connect(
102 sigc::mem_fun(*this, &PluginTreeView::on_message_received));
103}
104
105/** Destructor. */
107{
108 if (m_dispatcher) {
109 // unsubscribe
111 new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_UNSUBSCRIBE_WATCH);
112 m_dispatcher.get_client()->enqueue(msg);
113
114 m_dispatcher.get_client()->deregister_handler(FAWKES_CID_PLUGINMANAGER);
115 }
116
117#ifdef HAVE_GCONFMM
118 if (gconf_) {
119 gconf_->remove_dir(gconf_prefix_);
120 }
121#endif
122}
123
124/** Set the network client.
125 * @param client a Fawkes network client
126 */
127void
129{
130 m_dispatcher.set_client(client);
131}
132
133/** Set Gconf prefix.
134 * @param gconf_prefix the GConf prefix
135 */
136void
137PluginTreeView::set_gconf_prefix(Glib::ustring gconf_prefix)
138{
139#ifdef HAVE_GCONFMM
140 if (!gconf_) {
141 gconf_ = Gnome::Conf::Client::get_default_client();
142 } else {
143 gconf_->remove_dir(gconf_prefix_);
144 }
145
146 gconf_->add_dir(gconf_prefix);
147 gconf_prefix_ = gconf_prefix;
148
149 if (gconf_connection_) {
150 gconf_connection_.disconnect();
151 }
152 gconf_connection_ = gconf_->signal_value_changed().connect(
153 sigc::hide(sigc::hide(sigc::mem_fun(*this, &PluginTreeView::on_config_changed))));
154
155 on_config_changed();
156#endif
157}
158
159void
160PluginTreeView::on_connected()
161{
162 try {
163 FawkesNetworkClient *client = m_dispatcher.get_client();
164
165 // subscribe for load-/unload messages
167 new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_SUBSCRIBE_WATCH);
168 client->enqueue(msg);
169
170 // request list of available plugins
171 msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LIST_AVAIL);
172 client->enqueue(msg);
173
174 // request list of loaded plugins
175 msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LIST_LOADED);
176 client->enqueue(msg);
177 } catch (Exception &e) {
178 e.print_trace();
179 }
180}
181
182/** Signal handler that is called whenever the connection is terminated. */
183void
184PluginTreeView::on_disconnected()
185{
186 m_plugin_list->clear();
187}
188
189void
190PluginTreeView::on_message_received(fawkes::FawkesNetworkMessage *msg)
191{
192 if (msg->cid() != FAWKES_CID_PLUGINMANAGER)
193 return;
194
195 // loading
196 unsigned int msgid = msg->msgid();
197 if ((msgid == MSG_PLUGIN_LOADED) || (msgid == MSG_PLUGIN_LOAD_FAILED)
198 || (msgid == MSG_PLUGIN_UNLOADED) || (msgid == MSG_PLUGIN_UNLOAD_FAILED)) {
199 Glib::ustring name = "";
200 bool loaded = false;
201
202 if (msgid == MSG_PLUGIN_LOADED) {
203 if (msg->payload_size() != sizeof(plugin_loaded_msg_t)) {
204 printf("Invalid message size (load succeeded)\n");
205 } else {
206 plugin_loaded_msg_t *m = (plugin_loaded_msg_t *)msg->payload();
207 name = m->name;
208 loaded = true;
209 }
210 } else if (msgid == MSG_PLUGIN_LOAD_FAILED) {
211 if (msg->payload_size() != sizeof(plugin_load_failed_msg_t)) {
212 printf("Invalid message size (load failed)\n");
213 } else {
214 plugin_load_failed_msg_t *m = (plugin_load_failed_msg_t *)msg->payload();
215 name = m->name;
216 loaded = false;
217 }
218 } else if (msg->msgid() == MSG_PLUGIN_UNLOADED) {
219 if (msg->payload_size() != sizeof(plugin_unloaded_msg_t)) {
220 printf("Invalid message size (unload succeeded)\n");
221 } else {
222 plugin_unloaded_msg_t *m = (plugin_unloaded_msg_t *)msg->payload();
223 name = m->name;
224 loaded = false;
225 }
226 } else if (msg->msgid() == MSG_PLUGIN_UNLOAD_FAILED) {
227 if (msg->payload_size() != sizeof(plugin_unload_failed_msg_t)) {
228 printf("Invalid message size (unload failed)\n");
229 } else {
230 plugin_unload_failed_msg_t *m = (plugin_unload_failed_msg_t *)msg->payload();
231 name = m->name;
232 loaded = true;
233 }
234 }
235
236 Gtk::TreeIter iter;
237 for (iter = m_plugin_list->children().begin(); iter != m_plugin_list->children().end();
238 ++iter) {
239 Glib::ustring n = (*iter)[m_plugin_record.name];
240 if (n == name) {
241 (*iter)[m_plugin_record.loaded] = loaded;
242 break;
243 }
244 }
245 } else if (msgid == MSG_PLUGIN_AVAIL_LIST) {
246 m_plugin_list->clear();
247 PluginListMessage *plm = msg->msgc<PluginListMessage>();
248 while (plm->has_next()) {
249 char *plugin_name = plm->next();
250 char *plugin_desc = NULL;
251 if (plm->has_next()) {
252 plugin_desc = plm->next();
253 } else {
254 plugin_desc = strdup("Unknown, malformed plugin list message?");
255 }
256
257 Gtk::TreeModel::Row row = *m_plugin_list->append();
258 unsigned int index = m_plugin_list->children().size();
259 row[m_plugin_record.index] = index;
260 row[m_plugin_record.name] = plugin_name;
261 row[m_plugin_record.description] = plugin_desc;
262 row[m_plugin_record.loaded] = false;
263
264 free(plugin_name);
265 free(plugin_desc);
266 }
267 delete plm;
268 } else if (msg->msgid() == MSG_PLUGIN_AVAIL_LIST_FAILED) {
269 printf("Obtaining list of available plugins failed\n");
270 } else if (msg->msgid() == MSG_PLUGIN_LOADED_LIST) {
271 PluginListMessage *plm = msg->msgc<PluginListMessage>();
272 while (plm->has_next()) {
273 char *name = plm->next();
274
275 Gtk::TreeIter iter;
276 for (iter = m_plugin_list->children().begin(); iter != m_plugin_list->children().end();
277 ++iter) {
278 Glib::ustring n = (*iter)[m_plugin_record.name];
279 if (n == name) {
280 (*iter)[m_plugin_record.loaded] = true;
281 break;
282 }
283 }
284 free(name);
285 }
286 delete plm;
287 } else if (msg->msgid() == MSG_PLUGIN_LOADED_LIST_FAILED) {
288 printf("Obtaining list of loaded plugins failed\n");
289 }
290
291 // unknown message received
292 else {
293 printf("received message with msg-id %d\n", msg->msgid());
294 }
295}
296
297/** Signal handler that is called when the loaded checkbox is
298 * toggled.
299 * @param path the path of the selected row
300 */
301void
302PluginTreeView::on_status_toggled(const Glib::ustring &path)
303{
304 if (!m_dispatcher.get_client()->connected())
305 return;
306
307 Gtk::TreeModel::Row row = *m_plugin_list->get_iter(path);
308 Glib::ustring plugin_name = row[m_plugin_record.name];
309 bool loaded = row[m_plugin_record.loaded];
310
311 if (loaded) {
312 plugin_load_msg_t *m = (plugin_load_msg_t *)calloc(1, sizeof(plugin_load_msg_t));
313 strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH - 1);
314
315 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
317 m,
318 sizeof(plugin_load_msg_t));
319 m_dispatcher.get_client()->enqueue(msg);
320 } else {
321 plugin_unload_msg_t *m = (plugin_unload_msg_t *)calloc(1, sizeof(plugin_unload_msg_t));
322 strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH - 1);
323
324 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
326 m,
327 sizeof(plugin_unload_msg_t));
328 m_dispatcher.get_client()->enqueue(msg);
329 }
330}
331
332/**
333 * TreeView gets sorted by id
334 */
335void
336PluginTreeView::on_id_clicked()
337{
338 m_plugin_list->set_sort_column(0, Gtk::SORT_ASCENDING);
339}
340
341/**
342 * TreeView gets sorted by status (loaded/unloaded)
343 */
344void
345PluginTreeView::on_status_clicked()
346{
347 m_plugin_list->set_sort_column(2, Gtk::SORT_DESCENDING);
348}
349
350/**
351 * TreeView gets sorted by name
352 */
353void
354PluginTreeView::on_name_clicked()
355{
356 m_plugin_list->set_sort_column(1, Gtk::SORT_ASCENDING);
357}
358
359/**
360 * Configuration data has changed
361 */
362void
363PluginTreeView::on_config_changed()
364{
365 Gtk::TreeViewColumn *plugin_col = get_column(2);
366 if (plugin_col)
367 remove_column(*plugin_col);
368
369 append_plugin_column();
370}
371
372/**
373 * Append appropriate plugin column - depending on the GConf value
374 */
375void
376PluginTreeView::append_plugin_column()
377{
378#if GTKMM_MAJOR_VERSION > 2 || (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14)
379 bool description_as_tooltip = false;
380# ifdef HAVE_GCONFMM
381 if (gconf_) {
382 description_as_tooltip = gconf_->get_bool(gconf_prefix_ + "/description_as_tooltip");
383 }
384# endif
385#endif
386
387#if GTKMM_MAJOR_VERSION > 2 || (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14)
388 if (description_as_tooltip) {
389#endif
390 append_column("Plugin", m_plugin_record.name);
391#if GTKMM_MAJOR_VERSION > 2 || (GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14)
392 set_tooltip_column(2);
393 } else {
394 TwoLinesCellRenderer *twolines_renderer = new TwoLinesCellRenderer();
395 Gtk::TreeViewColumn *tlcol = new Gtk::TreeViewColumn("Plugin", *Gtk::manage(twolines_renderer));
396 append_column(*Gtk::manage(tlcol));
397
398# ifdef GLIBMM_PROPERTIES_ENABLED
399 tlcol->add_attribute(twolines_renderer->property_line1(), m_plugin_record.name);
400 tlcol->add_attribute(twolines_renderer->property_line2(), m_plugin_record.description);
401# else
402 tlcol->add_attribute(*twolines_renderer, "line1", m_plugin_record.name);
403 tlcol->add_attribute(*twolines_renderer, "line2", m_plugin_record.description);
404# endif
405
406 set_tooltip_column(-1);
407 }
408#endif
409
410 set_headers_clickable();
411 Gtk::TreeViewColumn *plugin_col = get_column(2);
412 if (plugin_col)
413 plugin_col->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_name_clicked));
414}
415
416} // end namespace fawkes
void set_client(FawkesNetworkClient *client)
Set Fawkes network client.
sigc::signal< void > signal_connected()
Get "connected" signal.
FawkesNetworkClient * get_client()
Get client.
sigc::signal< void, FawkesNetworkMessage * > signal_message_received()
Get "message received" signal.
sigc::signal< void > signal_disconnected()
Get "disconnected" signal.
Simple Fawkes network client.
Definition: client.h:52
void deregister_handler(unsigned int component_id)
Deregister handler.
Definition: client.cpp:676
bool connected() const noexcept
Check if connection is alive.
Definition: client.cpp:828
void enqueue(FawkesNetworkMessage *message)
Enqueue message to send.
Definition: client.cpp:596
Representation of a message that is sent over the network.
Definition: message.h:77
unsigned short int msgid() const
Get message type ID.
Definition: message.cpp:294
unsigned short int cid() const
Get component ID.
Definition: message.cpp:285
void * payload() const
Get payload buffer.
Definition: message.cpp:312
size_t payload_size() const
Get payload size.
Definition: message.cpp:303
MT * msgc() const
Get correctly parsed output.
Definition: message.h:159
virtual ~PluginTreeView()
Destructor.
void set_network_client(fawkes::FawkesNetworkClient *client)
Set the network client.
void set_gconf_prefix(Glib::ustring gconf_prefix)
Set Gconf prefix.
Fawkes library namespace.
@ MSG_PLUGIN_UNLOAD
request plugin unload (plugin_unload_msg_t)
Definition: messages.h:36
@ MSG_PLUGIN_UNLOAD_FAILED
plugin unload failed (plugin_unload_failed_msg_t)
Definition: messages.h:38
@ MSG_PLUGIN_LIST_LOADED
request lif of loaded plugins
Definition: messages.h:42
@ MSG_PLUGIN_AVAIL_LIST_FAILED
listing available plugins failed
Definition: messages.h:41
@ MSG_PLUGIN_LOADED_LIST_FAILED
listing loaded plugins failed
Definition: messages.h:44
@ MSG_PLUGIN_AVAIL_LIST
list of available plugins (plugin_list_msg_t)
Definition: messages.h:40
@ MSG_PLUGIN_LOAD
request plugin load (plugin_load_msg_t)
Definition: messages.h:33
@ MSG_PLUGIN_LOAD_FAILED
plugin load failed (plugin_load_failed_msg_t)
Definition: messages.h:35
@ MSG_PLUGIN_LOADED_LIST
list of loaded plugins (plugin_list_msg_t)
Definition: messages.h:43
@ MSG_PLUGIN_LIST_AVAIL
request list of available plugins
Definition: messages.h:39
@ MSG_PLUGIN_SUBSCRIBE_WATCH
Subscribe for watching load/unload events.
Definition: messages.h:45
@ MSG_PLUGIN_UNSUBSCRIBE_WATCH
Unsubscribe from watching load/unload events.
Definition: messages.h:46
@ MSG_PLUGIN_UNLOADED
plugin unloaded (plugin_unloaded_msg_t)
Definition: messages.h:37
@ MSG_PLUGIN_LOADED
plugin loaded (plugin_loaded_msg_t)
Definition: messages.h:34