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 <netcomm/fawkes/client.h>
27 #include <plugin/net/messages.h>
28 #include <plugin/net/list_message.h>
29 #include <gui_utils/twolines_cellrenderer.h>
30 
31 #include <cstring>
32 #include <string>
33 
34 using namespace std;
35 
36 namespace fawkes {
37 #if 0 /* just to make Emacs auto-indent happy */
38 }
39 #endif
40 
41 /** @class PluginTreeView <gui_utils/plugin_tree_view.h>
42  * A TreeView class to list available plugins und trigger their
43  * loading/unloading.
44  *
45  * @author Daniel Beck
46  * @author Tim Niemueller
47  */
48 
49 /** @class PluginTreeView::PluginRecord <gui_utils/plugin_tree_view.h>
50  * Column record class for the plugin tree view.
51  *
52  * @author Daniel Beck
53  */
54 
55 /** @var PluginTreeView::m_plugin_list
56  * Storage object for the plugin data.
57  */
58 
59 /** @var PluginTreeView::m_plugin_record
60  * Column record object.
61  */
62 
63 /** Constructor. */
64 PluginTreeView::PluginTreeView()
65  : m_dispatcher(FAWKES_CID_PLUGINMANAGER)
66 {
67  ctor();
68 }
69 
70 /** Constructor.
71  * @param cobject pointer to base object type
72  * @param builder Gtk::Builder instance
73  */
74 PluginTreeView::PluginTreeView(BaseObjectType* cobject,
75  const Glib::RefPtr<Gtk::Builder> builder)
76  : Gtk::TreeView(cobject),
77  m_dispatcher(FAWKES_CID_PLUGINMANAGER)
78 {
79  ctor();
80 }
81 
82 
83 void
84 PluginTreeView::ctor()
85 {
86  m_plugin_list = Gtk::ListStore::create(m_plugin_record);
87  set_model(m_plugin_list);
88  set_rules_hint(true);
89  append_column("#", m_plugin_record.index);
90  append_column_editable("Status", m_plugin_record.loaded);
91  append_plugin_column();
92 
93  on_name_clicked();
94  Gtk::TreeViewColumn *column = get_column(0);
95  column->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_id_clicked));
96  column = get_column(1);
97  column->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_status_clicked));
98 
99  Gtk::CellRendererToggle* renderer;
100  renderer = dynamic_cast<Gtk::CellRendererToggle*>( get_column_cell_renderer(1) );
101  renderer->signal_toggled().connect( sigc::mem_fun(*this, &PluginTreeView::on_status_toggled));
102 
103  m_dispatcher.signal_connected().connect(sigc::mem_fun(*this, &PluginTreeView::on_connected));
104  m_dispatcher.signal_disconnected().connect(sigc::mem_fun(*this, &PluginTreeView::on_disconnected));
105  m_dispatcher.signal_message_received().connect(sigc::mem_fun(*this, &PluginTreeView::on_message_received));
106 
107 }
108 
109 /** Destructor. */
111 {
112  if (m_dispatcher)
113  {
114  // unsubscribe
115  FawkesNetworkMessage* msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
117  m_dispatcher.get_client()->enqueue(msg);
118 
119  m_dispatcher.get_client()->deregister_handler(FAWKES_CID_PLUGINMANAGER);
120  }
121 
122 #ifdef HAVE_GCONFMM
123  if (__gconf) {
124 # ifdef GLIBMM_EXCEPTIONS_ENABLED
125  __gconf->remove_dir(__gconf_prefix);
126 # else
127  std::auto_ptr<Glib::Error> error;
128  __gconf->remove_dir(__gconf_prefix, error);
129 # endif
130  }
131 #endif
132 }
133 
134 
135 /** Set the network client.
136  * @param client a Fawkes network client
137  */
138 void
140 {
141  m_dispatcher.set_client(client);
142 }
143 
144 
145 /** Set Gconf prefix.
146  * @param gconf_prefix the GConf prefix
147  */
148 void
149 PluginTreeView::set_gconf_prefix(Glib::ustring gconf_prefix)
150 {
151 #ifdef HAVE_GCONFMM
152  if (! __gconf) {
153  __gconf = Gnome::Conf::Client::get_default_client();
154  } else {
155 # ifdef GLIBMM_EXCEPTIONS_ENABLED
156  __gconf->remove_dir(__gconf_prefix);
157 # else
158  std::auto_ptr<Glib::Error> error;
159  __gconf->remove_dir(__gconf_prefix, error);
160 # endif
161  }
162 
163 #ifdef GLIBMM_EXCEPTIONS_ENABLED
164  __gconf->add_dir(gconf_prefix);
165 #else
166  std::auto_ptr<Glib::Error> error;
167  __gconf->add_dir(gconf_prefix, Gnome::Conf::CLIENT_PRELOAD_NONE, error);
168 #endif
169  __gconf_prefix = gconf_prefix;
170 
171  if (__gconf_connection) {
172  __gconf_connection.disconnect();
173  }
174  __gconf_connection = __gconf->signal_value_changed().connect(sigc::hide(sigc::hide(sigc::mem_fun(*this, &PluginTreeView::on_config_changed))));
175 
176  on_config_changed();
177 #endif
178 }
179 
180 void
181 PluginTreeView::on_connected()
182 {
183  try
184  {
185  FawkesNetworkClient *client = m_dispatcher.get_client();
186 
187  // subscribe for load-/unload messages
188  FawkesNetworkMessage* msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
190  client->enqueue(msg);
191 
192  // request list of available plugins
193  msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
195  client->enqueue(msg);
196 
197  // request list of loaded plugins
198  msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
200  client->enqueue(msg);
201  }
202  catch (Exception& e)
203  {
204  e.print_trace();
205  }
206 }
207 
208 /** Signal handler that is called whenever the connection is terminated. */
209 void
210 PluginTreeView::on_disconnected()
211 {
212  m_plugin_list->clear();
213 }
214 
215 
216 void
217 PluginTreeView::on_message_received(fawkes::FawkesNetworkMessage* msg)
218 {
219  if (msg->cid() != FAWKES_CID_PLUGINMANAGER) return;
220 
221  // loading
222  unsigned int msgid = msg->msgid();
223  if ( (msgid == MSG_PLUGIN_LOADED) ||
224  (msgid == MSG_PLUGIN_LOAD_FAILED) ||
225  (msgid == MSG_PLUGIN_UNLOADED) ||
226  (msgid == MSG_PLUGIN_UNLOAD_FAILED) )
227  {
228  Glib::ustring name = "";
229  bool loaded = false;
230 
231  if ( msgid == MSG_PLUGIN_LOADED)
232  {
233  if ( msg->payload_size() != sizeof(plugin_loaded_msg_t) )
234  {
235  printf("Invalid message size (load succeeded)\n");
236  }
237  else
238  {
239  plugin_loaded_msg_t* m = (plugin_loaded_msg_t*) msg->payload();
240  name = m->name;
241  loaded = true;
242  }
243  }
244  else if ( msgid == MSG_PLUGIN_LOAD_FAILED )
245  {
246  if ( msg->payload_size() != sizeof(plugin_load_failed_msg_t) )
247  {
248  printf("Invalid message size (load failed)\n");
249  }
250  else
251  {
252  plugin_load_failed_msg_t* m = (plugin_load_failed_msg_t*) msg->payload();
253  name = m->name;
254  loaded = false;
255  }
256  }
257  else if ( msg->msgid() == MSG_PLUGIN_UNLOADED )
258  {
259  if ( msg->payload_size() != sizeof(plugin_unloaded_msg_t) )
260  {
261  printf("Invalid message size (unload succeeded)\n");
262  }
263  else
264  {
265  plugin_unloaded_msg_t* m = (plugin_unloaded_msg_t*) msg->payload();
266  name = m->name;
267  loaded = false;
268  }
269  }
270  else if ( msg->msgid() == MSG_PLUGIN_UNLOAD_FAILED)
271  {
272  if ( msg->payload_size() != sizeof(plugin_unload_failed_msg_t) )
273  {
274  printf("Invalid message size (unload failed)\n");
275  }
276  else
277  {
278  plugin_unload_failed_msg_t* m = (plugin_unload_failed_msg_t*) msg->payload();
279  name = m->name;
280  loaded = true;
281  }
282  }
283 
284  Gtk::TreeIter iter;
285  for ( iter = m_plugin_list->children().begin();
286  iter != m_plugin_list->children().end();
287  ++iter )
288  {
289  Glib::ustring n = (*iter)[m_plugin_record.name];
290  if ( n == name )
291  {
292  (*iter)[m_plugin_record.loaded] = loaded;
293  break;
294  }
295  }
296  }
297  else if (msgid == MSG_PLUGIN_AVAIL_LIST)
298  {
299  m_plugin_list->clear();
300  PluginListMessage* plm = msg->msgc<PluginListMessage>();
301  while ( plm->has_next() )
302  {
303  char *plugin_name = plm->next();
304  char *plugin_desc = NULL;
305  if ( plm->has_next() ) {
306  plugin_desc = plm->next();
307  } else {
308  plugin_desc = strdup("Unknown, malformed plugin list message?");
309  }
310 
311  Gtk::TreeModel::Row row = *m_plugin_list->append();
312  unsigned int index = m_plugin_list->children().size();
313  row[m_plugin_record.index] = index;
314  row[m_plugin_record.name] = plugin_name;
315  row[m_plugin_record.description] = plugin_desc;
316  row[m_plugin_record.loaded] = false;
317 
318  free(plugin_name);
319  free(plugin_desc);
320  }
321  delete plm;
322  }
323  else if ( msg->msgid() == MSG_PLUGIN_AVAIL_LIST_FAILED)
324  {
325  printf("Obtaining list of available plugins failed\n");
326  }
327  else if (msg->msgid() == MSG_PLUGIN_LOADED_LIST )
328  {
329  PluginListMessage* plm = msg->msgc<PluginListMessage>();
330  while ( plm->has_next() )
331  {
332  char* name = plm->next();
333 
334  Gtk::TreeIter iter;
335  for ( iter = m_plugin_list->children().begin();
336  iter != m_plugin_list->children().end();
337  ++iter )
338  {
339  Glib::ustring n = (*iter)[m_plugin_record.name];
340  if ( n == name )
341  {
342  (*iter)[m_plugin_record.loaded] = true;
343  break;
344  }
345  }
346  free(name);
347  }
348  delete plm;
349  }
350  else if ( msg->msgid() == MSG_PLUGIN_LOADED_LIST_FAILED)
351  {
352  printf("Obtaining list of loaded plugins failed\n");
353  }
354 
355  // unknown message received
356  else
357  {
358  printf("received message with msg-id %d\n", msg->msgid());
359  }
360 }
361 
362 /** Signal handler that is called when the loaded checkbox is
363  * toggled.
364  * @param path the path of the selected row
365  */
366 void
367 PluginTreeView::on_status_toggled(const Glib::ustring& path)
368 {
369  if ( ! m_dispatcher.get_client()->connected() ) return;
370 
371  Gtk::TreeModel::Row row = *m_plugin_list->get_iter(path);
372  Glib::ustring plugin_name = row[m_plugin_record.name];
373  bool loaded = row[m_plugin_record.loaded];
374 
375  if (loaded)
376  {
377  plugin_load_msg_t* m = (plugin_load_msg_t*) calloc(1, sizeof(plugin_load_msg_t));
378  strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH);
379 
380  FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
382  m, sizeof(plugin_load_msg_t));
383  m_dispatcher.get_client()->enqueue(msg);
384  }
385  else
386  {
387  plugin_unload_msg_t* m = (plugin_unload_msg_t *)calloc(1, sizeof(plugin_unload_msg_t));
388  strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH);
389 
390  FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
392  m, sizeof(plugin_unload_msg_t));
393  m_dispatcher.get_client()->enqueue(msg);
394  }
395 }
396 
397 /**
398  * TreeView gets sorted by id
399  */
400 void
401 PluginTreeView::on_id_clicked()
402 {
403  m_plugin_list->set_sort_column(0, Gtk::SORT_ASCENDING);
404 }
405 
406 /**
407  * TreeView gets sorted by status (loaded/unloaded)
408  */
409 void
410 PluginTreeView::on_status_clicked()
411 {
412  m_plugin_list->set_sort_column(2, Gtk::SORT_DESCENDING);
413 }
414 
415 /**
416  * TreeView gets sorted by name
417  */
418 void
419 PluginTreeView::on_name_clicked()
420 {
421  m_plugin_list->set_sort_column(1, Gtk::SORT_ASCENDING);
422 }
423 
424 /**
425  * Configuration data has changed
426  */
427 void
428 PluginTreeView::on_config_changed()
429 {
430  Gtk::TreeViewColumn *plugin_col = get_column(2);
431  if (plugin_col) remove_column(*plugin_col);
432 
433  append_plugin_column();
434 }
435 
436 /**
437  * Append appropriate plugin column - depending on the GConf value
438  */
439 void
440 PluginTreeView::append_plugin_column()
441 {
442 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
443  bool description_as_tooltip = false;
444 # ifdef HAVE_GCONFMM
445  if ( __gconf )
446  {
447 # ifdef GLIBMM_EXCEPTIONS_ENABLED
448  description_as_tooltip = __gconf->get_bool(__gconf_prefix + "/description_as_tooltip");
449 # else
450  std::auto_ptr<Glib::Error> error;
451  description_as_tooltip = __gconf->get_bool(__gconf_prefix + "/description_as_tooltip", error);
452 # endif
453  }
454 # endif
455 #endif
456 
457 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
458  if (description_as_tooltip)
459  {
460 #endif
461  append_column("Plugin", m_plugin_record.name);
462 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
463  set_tooltip_column(2);
464  }
465  else
466  {
467  TwoLinesCellRenderer *twolines_renderer = new TwoLinesCellRenderer();
468  Gtk::TreeViewColumn *tlcol =
469  new Gtk::TreeViewColumn("Plugin", *Gtk::manage(twolines_renderer));
470  append_column(*Gtk::manage(tlcol));
471 
472  # ifdef GLIBMM_PROPERTIES_ENABLED
473  tlcol->add_attribute(twolines_renderer->property_line1(), m_plugin_record.name);
474  tlcol->add_attribute(twolines_renderer->property_line2(), m_plugin_record.description);
475  # else
476  tlcol->add_attribute(*twolines_renderer, "line1", m_plugin_record.name);
477  tlcol->add_attribute(*twolines_renderer, "line2", m_plugin_record.description);
478  # endif
479 
480  set_tooltip_column(-1);
481  }
482 #endif
483 
484  set_headers_clickable();
485  Gtk::TreeViewColumn *plugin_col = get_column(2);
486  if (plugin_col) plugin_col->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_name_clicked));
487 }
488 
489 } // end namespace fawkes
request list of available plugins
Definition: messages.h:39
plugin unloaded (plugin_unloaded_msg_t)
Definition: messages.h:37
sigc::signal< void > signal_disconnected()
Get "disconnected" signal.
listing available plugins failed
Definition: messages.h:41
sigc::signal< void > signal_connected()
Get "connected" signal.
Unsubscribe from watching load/unload events.
Definition: messages.h:46
Simple Fawkes network client.
Definition: client.h:51
Fawkes library namespace.
void * payload() const
Get payload buffer.
Definition: message.cpp:321
void enqueue(FawkesNetworkMessage *message)
Enqueue message to send.
Definition: client.cpp:561
STL namespace.
Representation of a message that is sent over the network.
Definition: message.h:75
unsigned short int msgid() const
Get message type ID.
Definition: message.cpp:301
PluginTreeView()
Constructor.
void set_client(FawkesNetworkClient *client)
Set Fawkes network client.
list of loaded plugins (plugin_list_msg_t)
Definition: messages.h:43
plugin unload failed (plugin_unload_failed_msg_t)
Definition: messages.h:38
size_t payload_size() const
Get payload size.
Definition: message.cpp:311
plugin loaded (plugin_loaded_msg_t)
Definition: messages.h:34
request plugin unload (plugin_unload_msg_t)
Definition: messages.h:36
list of available plugins (plugin_list_msg_t)
Definition: messages.h:40
void set_gconf_prefix(Glib::ustring gconf_prefix)
Set Gconf prefix.
listing loaded plugins failed
Definition: messages.h:44
bool connected() const
Check if connection is alive.
Definition: client.cpp:797
MT * msgc() const
Get correctly parsed output.
Definition: message.h:154
request lif of loaded plugins
Definition: messages.h:42
request plugin load (plugin_load_msg_t)
Definition: messages.h:33
void set_network_client(fawkes::FawkesNetworkClient *client)
Set the network client.
void deregister_handler(unsigned int component_id)
Deregister handler.
Definition: client.cpp:639
sigc::signal< void, FawkesNetworkMessage * > signal_message_received()
Get "message received" signal.
Subscribe for watching load/unload events.
Definition: messages.h:45
plugin load failed (plugin_load_failed_msg_t)
Definition: messages.h:35
unsigned short int cid() const
Get component ID.
Definition: message.cpp:291
virtual ~PluginTreeView()
Destructor.
FawkesNetworkClient * get_client()
Get client.