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  set_search_column(1);
104 
105  m_dispatcher.signal_connected().connect(sigc::mem_fun(*this, &PluginTreeView::on_connected));
106  m_dispatcher.signal_disconnected().connect(sigc::mem_fun(*this, &PluginTreeView::on_disconnected));
107  m_dispatcher.signal_message_received().connect(sigc::mem_fun(*this, &PluginTreeView::on_message_received));
108 }
109 
110 /** Destructor. */
112 {
113  if (m_dispatcher)
114  {
115  // unsubscribe
116  FawkesNetworkMessage* msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
118  m_dispatcher.get_client()->enqueue(msg);
119 
120  m_dispatcher.get_client()->deregister_handler(FAWKES_CID_PLUGINMANAGER);
121  }
122 
123 #ifdef HAVE_GCONFMM
124  if (__gconf) {
125  __gconf->remove_dir(__gconf_prefix);
126  }
127 #endif
128 }
129 
130 
131 /** Set the network client.
132  * @param client a Fawkes network client
133  */
134 void
136 {
137  m_dispatcher.set_client(client);
138 }
139 
140 
141 /** Set Gconf prefix.
142  * @param gconf_prefix the GConf prefix
143  */
144 void
145 PluginTreeView::set_gconf_prefix(Glib::ustring gconf_prefix)
146 {
147 #ifdef HAVE_GCONFMM
148  if (! __gconf) {
149  __gconf = Gnome::Conf::Client::get_default_client();
150  } else {
151  __gconf->remove_dir(__gconf_prefix);
152  }
153 
154  __gconf->add_dir(gconf_prefix);
155  __gconf_prefix = gconf_prefix;
156 
157  if (__gconf_connection) {
158  __gconf_connection.disconnect();
159  }
160  __gconf_connection = __gconf->signal_value_changed().connect(sigc::hide(sigc::hide(sigc::mem_fun(*this, &PluginTreeView::on_config_changed))));
161 
162  on_config_changed();
163 #endif
164 }
165 
166 void
167 PluginTreeView::on_connected()
168 {
169  try
170  {
171  FawkesNetworkClient *client = m_dispatcher.get_client();
172 
173  // subscribe for load-/unload messages
174  FawkesNetworkMessage* msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
176  client->enqueue(msg);
177 
178  // request list of available plugins
179  msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
181  client->enqueue(msg);
182 
183  // request list of loaded plugins
184  msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
186  client->enqueue(msg);
187  }
188  catch (Exception& e)
189  {
190  e.print_trace();
191  }
192 }
193 
194 /** Signal handler that is called whenever the connection is terminated. */
195 void
196 PluginTreeView::on_disconnected()
197 {
198  m_plugin_list->clear();
199 }
200 
201 
202 void
203 PluginTreeView::on_message_received(fawkes::FawkesNetworkMessage* msg)
204 {
205  if (msg->cid() != FAWKES_CID_PLUGINMANAGER) return;
206 
207  // loading
208  unsigned int msgid = msg->msgid();
209  if ( (msgid == MSG_PLUGIN_LOADED) ||
210  (msgid == MSG_PLUGIN_LOAD_FAILED) ||
211  (msgid == MSG_PLUGIN_UNLOADED) ||
212  (msgid == MSG_PLUGIN_UNLOAD_FAILED) )
213  {
214  Glib::ustring name = "";
215  bool loaded = false;
216 
217  if ( msgid == MSG_PLUGIN_LOADED)
218  {
219  if ( msg->payload_size() != sizeof(plugin_loaded_msg_t) )
220  {
221  printf("Invalid message size (load succeeded)\n");
222  }
223  else
224  {
226  name = m->name;
227  loaded = true;
228  }
229  }
230  else if ( msgid == MSG_PLUGIN_LOAD_FAILED )
231  {
232  if ( msg->payload_size() != sizeof(plugin_load_failed_msg_t) )
233  {
234  printf("Invalid message size (load failed)\n");
235  }
236  else
237  {
239  name = m->name;
240  loaded = false;
241  }
242  }
243  else if ( msg->msgid() == MSG_PLUGIN_UNLOADED )
244  {
245  if ( msg->payload_size() != sizeof(plugin_unloaded_msg_t) )
246  {
247  printf("Invalid message size (unload succeeded)\n");
248  }
249  else
250  {
252  name = m->name;
253  loaded = false;
254  }
255  }
256  else if ( msg->msgid() == MSG_PLUGIN_UNLOAD_FAILED)
257  {
258  if ( msg->payload_size() != sizeof(plugin_unload_failed_msg_t) )
259  {
260  printf("Invalid message size (unload failed)\n");
261  }
262  else
263  {
265  name = m->name;
266  loaded = true;
267  }
268  }
269 
270  Gtk::TreeIter iter;
271  for ( iter = m_plugin_list->children().begin();
272  iter != m_plugin_list->children().end();
273  ++iter )
274  {
275  Glib::ustring n = (*iter)[m_plugin_record.name];
276  if ( n == name )
277  {
278  (*iter)[m_plugin_record.loaded] = loaded;
279  break;
280  }
281  }
282  }
283  else if (msgid == MSG_PLUGIN_AVAIL_LIST)
284  {
285  m_plugin_list->clear();
287  while ( plm->has_next() )
288  {
289  char *plugin_name = plm->next();
290  char *plugin_desc = NULL;
291  if ( plm->has_next() ) {
292  plugin_desc = plm->next();
293  } else {
294  plugin_desc = strdup("Unknown, malformed plugin list message?");
295  }
296 
297  Gtk::TreeModel::Row row = *m_plugin_list->append();
298  unsigned int index = m_plugin_list->children().size();
299  row[m_plugin_record.index] = index;
300  row[m_plugin_record.name] = plugin_name;
301  row[m_plugin_record.description] = plugin_desc;
302  row[m_plugin_record.loaded] = false;
303 
304  free(plugin_name);
305  free(plugin_desc);
306  }
307  delete plm;
308  }
309  else if ( msg->msgid() == MSG_PLUGIN_AVAIL_LIST_FAILED)
310  {
311  printf("Obtaining list of available plugins failed\n");
312  }
313  else if (msg->msgid() == MSG_PLUGIN_LOADED_LIST )
314  {
316  while ( plm->has_next() )
317  {
318  char* name = plm->next();
319 
320  Gtk::TreeIter iter;
321  for ( iter = m_plugin_list->children().begin();
322  iter != m_plugin_list->children().end();
323  ++iter )
324  {
325  Glib::ustring n = (*iter)[m_plugin_record.name];
326  if ( n == name )
327  {
328  (*iter)[m_plugin_record.loaded] = true;
329  break;
330  }
331  }
332  free(name);
333  }
334  delete plm;
335  }
336  else if ( msg->msgid() == MSG_PLUGIN_LOADED_LIST_FAILED)
337  {
338  printf("Obtaining list of loaded plugins failed\n");
339  }
340 
341  // unknown message received
342  else
343  {
344  printf("received message with msg-id %d\n", msg->msgid());
345  }
346 }
347 
348 /** Signal handler that is called when the loaded checkbox is
349  * toggled.
350  * @param path the path of the selected row
351  */
352 void
353 PluginTreeView::on_status_toggled(const Glib::ustring& path)
354 {
355  if ( ! m_dispatcher.get_client()->connected() ) return;
356 
357  Gtk::TreeModel::Row row = *m_plugin_list->get_iter(path);
358  Glib::ustring plugin_name = row[m_plugin_record.name];
359  bool loaded = row[m_plugin_record.loaded];
360 
361  if (loaded)
362  {
363  plugin_load_msg_t* m = (plugin_load_msg_t*) calloc(1, sizeof(plugin_load_msg_t));
364  strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH);
365 
366  FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
368  m, sizeof(plugin_load_msg_t));
369  m_dispatcher.get_client()->enqueue(msg);
370  }
371  else
372  {
374  strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH);
375 
376  FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
378  m, sizeof(plugin_unload_msg_t));
379  m_dispatcher.get_client()->enqueue(msg);
380  }
381 }
382 
383 /**
384  * TreeView gets sorted by id
385  */
386 void
387 PluginTreeView::on_id_clicked()
388 {
389  m_plugin_list->set_sort_column(0, Gtk::SORT_ASCENDING);
390 }
391 
392 /**
393  * TreeView gets sorted by status (loaded/unloaded)
394  */
395 void
396 PluginTreeView::on_status_clicked()
397 {
398  m_plugin_list->set_sort_column(2, Gtk::SORT_DESCENDING);
399 }
400 
401 /**
402  * TreeView gets sorted by name
403  */
404 void
405 PluginTreeView::on_name_clicked()
406 {
407  m_plugin_list->set_sort_column(1, Gtk::SORT_ASCENDING);
408 }
409 
410 /**
411  * Configuration data has changed
412  */
413 void
414 PluginTreeView::on_config_changed()
415 {
416  Gtk::TreeViewColumn *plugin_col = get_column(2);
417  if (plugin_col) remove_column(*plugin_col);
418 
419  append_plugin_column();
420 }
421 
422 /**
423  * Append appropriate plugin column - depending on the GConf value
424  */
425 void
426 PluginTreeView::append_plugin_column()
427 {
428 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
429  bool description_as_tooltip = false;
430 # ifdef HAVE_GCONFMM
431  if ( __gconf )
432  {
433  description_as_tooltip = __gconf->get_bool(__gconf_prefix + "/description_as_tooltip");
434  }
435 # endif
436 #endif
437 
438 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
439  if (description_as_tooltip)
440  {
441 #endif
442  append_column("Plugin", m_plugin_record.name);
443 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
444  set_tooltip_column(2);
445  }
446  else
447  {
448  TwoLinesCellRenderer *twolines_renderer = new TwoLinesCellRenderer();
449  Gtk::TreeViewColumn *tlcol =
450  new Gtk::TreeViewColumn("Plugin", *Gtk::manage(twolines_renderer));
451  append_column(*Gtk::manage(tlcol));
452 
453  # ifdef GLIBMM_PROPERTIES_ENABLED
454  tlcol->add_attribute(twolines_renderer->property_line1(), m_plugin_record.name);
455  tlcol->add_attribute(twolines_renderer->property_line2(), m_plugin_record.description);
456  # else
457  tlcol->add_attribute(*twolines_renderer, "line1", m_plugin_record.name);
458  tlcol->add_attribute(*twolines_renderer, "line2", m_plugin_record.description);
459  # endif
460 
461  set_tooltip_column(-1);
462  }
463 #endif
464 
465  set_headers_clickable();
466  Gtk::TreeViewColumn *plugin_col = get_column(2);
467  if (plugin_col) plugin_col->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_name_clicked));
468 }
469 
470 } // end namespace fawkes
void * payload() const
Get payload buffer.
Definition: message.cpp:321
char * next()
Get next plugin from list.
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.
Plugin loaded message.
Definition: messages.h:70
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:52
Gtk cell renderer for two lines of text in a cell.
unsigned short int cid() const
Get component ID.
Definition: message.cpp:291
char name[PLUGIN_MSG_NAME_LENGTH]
name of the plugin that has been loaded
Definition: messages.h:71
Fawkes library namespace.
void enqueue(FawkesNetworkMessage *message)
Enqueue message to send.
Definition: client.cpp:587
Plugin unloaded message.
Definition: messages.h:87
STL namespace.
Representation of a message that is sent over the network.
Definition: message.h:75
PluginTreeView()
Constructor.
void set_client(FawkesNetworkClient *client)
Set Fawkes network client.
MT * msgc() const
Get correctly parsed output.
Definition: message.h:154
char name[PLUGIN_MSG_NAME_LENGTH]
name of the plugin to load.
Definition: messages.h:57
list of loaded plugins (plugin_list_msg_t)
Definition: messages.h:43
plugin unload failed (plugin_unload_failed_msg_t)
Definition: messages.h:38
char name[PLUGIN_MSG_NAME_LENGTH]
name of plugin that could not be unloaded
Definition: messages.h:76
Plugin unload failed.
Definition: messages.h:80
plugin loaded (plugin_loaded_msg_t)
Definition: messages.h:34
char name[PLUGIN_MSG_NAME_LENGTH]
name of plugin that could not be unloaded
Definition: messages.h:81
request plugin unload (plugin_unload_msg_t)
Definition: messages.h:36
list of available plugins (plugin_list_msg_t)
Definition: messages.h:40
Base class for exceptions in Fawkes.
Definition: exception.h:36
Plugin list message.
Definition: list_message.h:34
void set_gconf_prefix(Glib::ustring gconf_prefix)
Set Gconf prefix.
listing loaded plugins failed
Definition: messages.h:44
char name[PLUGIN_MSG_NAME_LENGTH]
name of the plugin that has been unloaded
Definition: messages.h:88
bool has_next()
Check if more list elements are available.
void print_trace()
Prints trace to stderr.
Definition: exception.cpp:619
Load plugin message.
Definition: messages.h:56
unsigned short int msgid() const
Get message type ID.
Definition: message.cpp:301
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:665
Plugin load failed.
Definition: messages.h:75
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
bool connected() const
Check if connection is alive.
Definition: client.cpp:823
char name[PLUGIN_MSG_NAME_LENGTH]
name of te plugin to unload.
Definition: messages.h:64
virtual ~PluginTreeView()
Destructor.
FawkesNetworkClient * get_client()
Get client.
size_t payload_size() const
Get payload size.
Definition: message.cpp:311
Unload plugin message.
Definition: messages.h:63