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 
34 using namespace std;
35 
36 namespace 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. */
61 PluginTreeView::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  */
70 PluginTreeView::PluginTreeView(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> builder)
71 : Gtk::TreeView(cobject), m_dispatcher(FAWKES_CID_PLUGINMANAGER)
72 {
73  ctor();
74 }
75 
76 void
77 PluginTreeView::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
110  FawkesNetworkMessage *msg =
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  */
127 void
129 {
130  m_dispatcher.set_client(client);
131 }
132 
133 /** Set Gconf prefix.
134  * @param gconf_prefix the GConf prefix
135  */
136 void
137 PluginTreeView::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 
159 void
160 PluginTreeView::on_connected()
161 {
162  try {
163  FawkesNetworkClient *client = m_dispatcher.get_client();
164 
165  // subscribe for load-/unload messages
166  FawkesNetworkMessage *msg =
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. */
183 void
184 PluginTreeView::on_disconnected()
185 {
186  m_plugin_list->clear();
187 }
188 
189 void
190 PluginTreeView::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  */
301 void
302 PluginTreeView::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  */
335 void
336 PluginTreeView::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  */
344 void
345 PluginTreeView::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  */
353 void
354 PluginTreeView::on_name_clicked()
355 {
356  m_plugin_list->set_sort_column(1, Gtk::SORT_ASCENDING);
357 }
358 
359 /**
360  * Configuration data has changed
361  */
362 void
363 PluginTreeView::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  */
375 void
376 PluginTreeView::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 * payload() const
Get payload buffer.
Definition: message.cpp:312
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
unsigned short int cid() const
Get component ID.
Definition: message.cpp:285
Fawkes library namespace.
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:76
void set_client(FawkesNetworkClient *client)
Set Fawkes network client.
MT * msgc() const
Get correctly parsed output.
Definition: message.h:159
list of loaded plugins (plugin_list_msg_t)
Definition: messages.h:43
plugin unload failed (plugin_unload_failed_msg_t)
Definition: messages.h:38
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
unsigned short int msgid() const
Get message type ID.
Definition: message.cpp:294
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:676
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:828
virtual ~PluginTreeView()
Destructor.
FawkesNetworkClient * get_client()
Get client.
size_t payload_size() const
Get payload size.
Definition: message.cpp:303