plugin_tree_view.cpp

00001
00002 /***************************************************************************
00003  *  plugin_tree_view.cpp - Displays a list of Fawkes plugins and allows to
00004  *                         start/stop them
00005  *
00006  *  Created: Fri Sep 26 21:13:48 2008
00007  *  Copyright  2008  Daniel Beck
00008  *             2008  Tim Niemueller [www.niemueller.de]
00009  *
00010  ****************************************************************************/
00011
00012 /*  This program is free software; you can redistribute it and/or modify
00013  *  it under the terms of the GNU General Public License as published by
00014  *  the Free Software Foundation; either version 2 of the License, or
00015  *  (at your option) any later version.
00016  *
00017  *  This program is distributed in the hope that it will be useful,
00018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  *  GNU Library General Public License for more details.
00021  *
00022  *  Read the full text in the LICENSE.GPL file in the doc directory.
00023  */
00024
00025 #include <gui_utils/plugin_tree_view.h>
00026 #include <netcomm/fawkes/client.h>
00027 #include <plugin/net/messages.h>
00028 #include <plugin/net/list_message.h>
00029 #include <gui_utils/twolines_cellrenderer.h>
00030
00031 #include <cstring>
00032 #include <string>
00033
00034 using namespace std;
00035
00036 namespace fawkes {
00037 #if 0 /* just to make Emacs auto-indent happy */
00038 }
00039 #endif
00040 
00041 /** @class PluginTreeView <gui_utils/plugin_tree_view.h>
00042  * A TreeView class to list available plugins und trigger their
00043  * loading/unloading.
00044  *
00045  * @author Daniel Beck
00046  * @author Tim Niemueller
00047  */
00048 
00049 /** @class PluginTreeView::PluginRecord <gui_utils/plugin_tree_view.h>
00050  * Column record class for the plugin tree view.
00051  *
00052  * @author Daniel Beck
00053  */
00054 
00055 /** @var PluginTreeView::m_plugin_list
00056  * Storage object for the plugin data.
00057  */
00058 
00059 /** @var PluginTreeView::m_plugin_record
00060  * Column record object.
00061  */
00062 
00063 /** Constructor.
00064  * @param cobject pointer to base object type
00065  * @param ref_xml Glade XML file
00066  */
00067 PluginTreeView::PluginTreeView(BaseObjectType* cobject,
00068                                const Glib::RefPtr<Gnome::Glade::Xml> ref_xml)
00069   : Gtk::TreeView(cobject),
00070     m_dispatcher(FAWKES_CID_PLUGINMANAGER)
00071 {
00072   m_plugin_list = Gtk::ListStore::create(m_plugin_record);
00073   set_model(m_plugin_list);
00074   set_rules_hint(true);
00075   append_column("#", m_plugin_record.index);
00076   append_column_editable("Status", m_plugin_record.loaded);
00077   append_plugin_column();
00078
00079   on_name_clicked();
00080   Gtk::TreeViewColumn *column = get_column(0);
00081   column->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_id_clicked));
00082   column = get_column(1);
00083   column->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_status_clicked));
00084
00085   Gtk::CellRendererToggle* renderer;
00086   renderer = dynamic_cast<Gtk::CellRendererToggle*>( get_column_cell_renderer(1) );
00087   renderer->signal_toggled().connect( sigc::mem_fun(*this, &PluginTreeView::on_status_toggled));
00088
00089   m_dispatcher.signal_connected().connect(sigc::mem_fun(*this, &PluginTreeView::on_connected));
00090   m_dispatcher.signal_disconnected().connect(sigc::mem_fun(*this, &PluginTreeView::on_disconnected));
00091   m_dispatcher.signal_message_received().connect(sigc::mem_fun(*this, &PluginTreeView::on_message_received));
00092 }
00093
00094 
00095 /** Destructor. */
00096 PluginTreeView::~PluginTreeView()
00097 {
00098   if (m_dispatcher)
00099   {
00100     // unsubscribe
00101     FawkesNetworkMessage* msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00102                                                          MSG_PLUGIN_UNSUBSCRIBE_WATCH);
00103     m_dispatcher.get_client()->enqueue(msg);
00104     msg->unref();
00105
00106     m_dispatcher.get_client()->deregister_handler(FAWKES_CID_PLUGINMANAGER);
00107   }
00108
00109 #ifdef HAVE_GCONFMM
00110   if (__gconf) {
00111     __gconf->remove_dir(__gconf_prefix);
00112   }
00113 #endif
00114 }
00115
00116 
00117 /** Set the network client.
00118  * @param client a Fawkes network client
00119  */
00120 void
00121 PluginTreeView::set_network_client(FawkesNetworkClient* client)
00122 {
00123   m_dispatcher.set_client(client);
00124 }
00125
00126 
00127 /** Set Gconf prefix.
00128  * @param gconf_prefix the GConf prefix
00129  */
00130 void
00131 PluginTreeView::set_gconf_prefix(Glib::ustring gconf_prefix)
00132 {
00133 #ifdef HAVE_GCONFMM
00134   if (! __gconf) {
00135     __gconf = Gnome::Conf::Client::get_default_client();
00136   } else {
00137     __gconf->remove_dir(__gconf_prefix);
00138   }
00139
00140   __gconf->add_dir(gconf_prefix);
00141   __gconf_prefix = gconf_prefix;
00142
00143   if (__gconf_connection) {
00144     __gconf_connection.disconnect();
00145   }
00146   __gconf_connection = __gconf->signal_value_changed().connect(sigc::hide(sigc::hide(sigc::mem_fun(*this, &PluginTreeView::on_config_changed))));
00147
00148   on_config_changed();
00149 #endif
00150 }
00151
00152 void
00153 PluginTreeView::on_connected()
00154 {
00155   try
00156   {
00157     FawkesNetworkClient *client = m_dispatcher.get_client();
00158
00159     // subscribe for load-/unload messages
00160     FawkesNetworkMessage* msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00161                                                          MSG_PLUGIN_SUBSCRIBE_WATCH);
00162     client->enqueue(msg);
00163     msg->unref();
00164
00165     // request list of available plugins
00166     msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00167                                    MSG_PLUGIN_LIST_AVAIL);
00168     client->enqueue(msg);
00169     msg->unref();
00170
00171     // request list of loaded plugins
00172     msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00173                                    MSG_PLUGIN_LIST_LOADED);
00174     client->enqueue(msg);
00175     msg->unref();
00176   }
00177   catch (Exception& e)
00178   {
00179     e.print_trace();
00180   }
00181 }
00182 
00183 /** Signal handler that is called whenever the connection is terminated. */
00184 void
00185 PluginTreeView::on_disconnected()
00186 {
00187   m_plugin_list->clear();
00188 }
00189
00190
00191 void
00192 PluginTreeView::on_message_received(fawkes::FawkesNetworkMessage* msg)
00193 {
00194   if (msg->cid() != FAWKES_CID_PLUGINMANAGER)  return;
00195
00196   // loading
00197   unsigned int msgid = msg->msgid();
00198   if ( (msgid == MSG_PLUGIN_LOADED) ||
00199        (msgid == MSG_PLUGIN_LOAD_FAILED) ||
00200        (msgid == MSG_PLUGIN_UNLOADED) ||
00201        (msgid == MSG_PLUGIN_UNLOAD_FAILED) )
00202   {
00203     Glib::ustring name = "";
00204     bool loaded = false;
00205
00206     if ( msgid == MSG_PLUGIN_LOADED)
00207     {
00208       if ( msg->payload_size() != sizeof(plugin_loaded_msg_t) )
00209       {
00210         printf("Invalid message size (load succeeded)\n");
00211       }
00212       else
00213       {
00214         plugin_loaded_msg_t* m = (plugin_loaded_msg_t*) msg->payload();
00215         name   = m->name;
00216         loaded = true;
00217       }
00218     }
00219     else if ( msgid == MSG_PLUGIN_LOAD_FAILED )
00220     {
00221       if ( msg->payload_size() != sizeof(plugin_load_failed_msg_t) )
00222       {
00223         printf("Invalid message size (load failed)\n");
00224       }
00225       else
00226       {
00227         plugin_load_failed_msg_t* m = (plugin_load_failed_msg_t*) msg->payload();
00228         name   = m->name;
00229         loaded = false;
00230       }
00231     }
00232     else if ( msg->msgid() == MSG_PLUGIN_UNLOADED )
00233     {
00234       if ( msg->payload_size() != sizeof(plugin_unloaded_msg_t) )
00235       {
00236         printf("Invalid message size (unload succeeded)\n");
00237       }
00238       else
00239       {
00240         plugin_unloaded_msg_t* m = (plugin_unloaded_msg_t*) msg->payload();
00241         name   = m->name;
00242         loaded = false;
00243       }
00244     }
00245     else if ( msg->msgid() == MSG_PLUGIN_UNLOAD_FAILED)
00246     {
00247       if ( msg->payload_size() != sizeof(plugin_unload_failed_msg_t) )
00248       {
00249         printf("Invalid message size (unload failed)\n");
00250       }
00251       else
00252       {
00253         plugin_unload_failed_msg_t* m = (plugin_unload_failed_msg_t*) msg->payload();
00254         name   = m->name;
00255         loaded = true;
00256       }
00257     }
00258
00259     Gtk::TreeIter iter;
00260     for ( iter  = m_plugin_list->children().begin();
00261           iter != m_plugin_list->children().end();
00262           ++iter )
00263     {
00264       Glib::ustring n = (*iter)[m_plugin_record.name];
00265       if ( n == name )
00266       {
00267         (*iter)[m_plugin_record.loaded] = loaded;
00268         break;
00269       }
00270     }
00271   }
00272   else if (msgid == MSG_PLUGIN_AVAIL_LIST)
00273   {
00274     m_plugin_list->clear();
00275     PluginListMessage* plm = msg->msgc<PluginListMessage>();
00276     while ( plm->has_next() )
00277     {
00278       char *plugin_name = plm->next();
00279       char *plugin_desc = NULL;
00280       if ( plm->has_next() ) {
00281         plugin_desc = plm->next();
00282       } else {
00283         plugin_desc = strdup("Unknown, malformed plugin list message?");
00284       }
00285
00286       Gtk::TreeModel::Row row = *m_plugin_list->append();
00287       unsigned int index = m_plugin_list->children().size();
00288       row[m_plugin_record.index]       = index;
00289       row[m_plugin_record.name]        = plugin_name;
00290       row[m_plugin_record.description] = plugin_desc;
00291       row[m_plugin_record.loaded]      = false;
00292
00293       free(plugin_name);
00294       free(plugin_desc);
00295     }
00296     delete plm;
00297   }
00298   else if ( msg->msgid() == MSG_PLUGIN_AVAIL_LIST_FAILED)
00299   {
00300     printf("Obtaining list of available plugins failed\n");
00301   }
00302   else if (msg->msgid() == MSG_PLUGIN_LOADED_LIST )
00303   {
00304     PluginListMessage* plm = msg->msgc<PluginListMessage>();
00305     while ( plm->has_next() )
00306     {
00307       char* name = plm->next();
00308
00309       Gtk::TreeIter iter;
00310       for ( iter  = m_plugin_list->children().begin();
00311             iter != m_plugin_list->children().end();
00312             ++iter )
00313       {
00314         Glib::ustring n = (*iter)[m_plugin_record.name];
00315         if ( n == name )
00316         {
00317           (*iter)[m_plugin_record.loaded] = true;
00318           break;
00319         }
00320       }
00321       free(name);
00322     }
00323     delete plm;
00324   }
00325   else if ( msg->msgid() == MSG_PLUGIN_LOADED_LIST_FAILED)
00326   {
00327     printf("Obtaining list of loaded plugins failed\n");
00328   }
00329
00330   // unknown message received
00331   else
00332   {
00333     printf("received message with msg-id %d\n", msg->msgid());
00334   }
00335 }
00336 
00337 /** Signal handler that is called when the loaded checkbox is
00338  * toggled.
00339  * @param path the path of the selected row
00340  */
00341 void
00342 PluginTreeView::on_status_toggled(const Glib::ustring& path)
00343 {
00344   if ( ! m_dispatcher.get_client()->connected() )  return;
00345
00346   Gtk::TreeModel::Row row = *m_plugin_list->get_iter(path);
00347   Glib::ustring plugin_name = row[m_plugin_record.name];
00348   bool loaded = row[m_plugin_record.loaded];
00349
00350   if (loaded)
00351   {
00352     plugin_load_msg_t* m = (plugin_load_msg_t*) calloc(1, sizeof(plugin_load_msg_t));
00353     strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH);
00354
00355     FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00356                                                          MSG_PLUGIN_LOAD,
00357                                                          m, sizeof(plugin_load_msg_t));
00358     m_dispatcher.get_client()->enqueue(msg);
00359     msg->unref();
00360   }
00361   else
00362   {
00363     plugin_unload_msg_t* m = (plugin_unload_msg_t *)calloc(1, sizeof(plugin_unload_msg_t));
00364     strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH);
00365
00366     FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00367                                                          MSG_PLUGIN_UNLOAD,
00368                                                          m, sizeof(plugin_unload_msg_t));
00369     m_dispatcher.get_client()->enqueue(msg);
00370     msg->unref();
00371   }
00372 }
00373 
00374 /**
00375  * TreeView gets sorted by id
00376  */
00377 void
00378 PluginTreeView::on_id_clicked()
00379 {
00380   m_plugin_list->set_sort_column(0, Gtk::SORT_ASCENDING);
00381 }
00382 
00383 /**
00384  * TreeView gets sorted by status (loaded/unloaded)
00385  */
00386 void
00387 PluginTreeView::on_status_clicked()
00388 {
00389   m_plugin_list->set_sort_column(2, Gtk::SORT_DESCENDING);
00390 }
00391 
00392 /**
00393  * TreeView gets sorted by name
00394  */
00395 void
00396 PluginTreeView::on_name_clicked()
00397 {
00398   m_plugin_list->set_sort_column(1, Gtk::SORT_ASCENDING);
00399 }
00400 
00401 /**
00402  * Configuration data has changed
00403  */
00404 void
00405 PluginTreeView::on_config_changed()
00406 {
00407   Gtk::TreeViewColumn *plugin_col = get_column(2);
00408   if (plugin_col) remove_column(*plugin_col);
00409
00410   append_plugin_column();
00411 }
00412 
00413 /**
00414  * Append appropriate plugin column - depending on the GConf value
00415  */
00416 void
00417 PluginTreeView::append_plugin_column()
00418 {
00419   bool description_as_tooltip = false;
00420 #ifdef HAVE_GCONFMM
00421   if ( __gconf )
00422   {
00423 #  ifdef GLIBMM_EXCEPTIONS_ENABLED
00424     description_as_tooltip = __gconf->get_bool(__gconf_prefix + "/description_as_tooltip");
00425 #  else
00426     std::auto_ptr<Glib::Error> error;
00427     description_as_tooltip = __gconf->get_bool(__gconf_prefix + "/description_as_tooltip", error);
00428 #  endif
00429   }
00430 #endif
00431 
00432 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
00433   if (description_as_tooltip)
00434   {
00435 #endif
00436     append_column("Plugin", m_plugin_record.name);
00437 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
00438     set_tooltip_column(2);
00439   }
00440   else
00441   {
00442     TwoLinesCellRenderer *twolines_renderer = new TwoLinesCellRenderer();
00443     Gtk::TreeViewColumn *tlcol = new Gtk::TreeViewColumn("Plugin", *Gtk::manage(twolines_renderer));
00444     append_column(*Gtk::manage(tlcol));
00445
00446  #  ifdef GLIBMM_PROPERTIES_ENABLED
00447     tlcol->add_attribute(twolines_renderer->property_line1(), m_plugin_record.name);
00448     tlcol->add_attribute(twolines_renderer->property_line2(), m_plugin_record.description);
00449  #  else
00450     tlcol->add_attribute(*twolines_renderer, "line1", m_plugin_record.line1);
00451     tlcol->add_attribute(*twolines_renderer, "line2", m_plugin_record.line2);
00452  #  endif
00453 
00454     set_tooltip_column(-1);
00455   }
00456 #endif
00457 
00458   set_headers_clickable();
00459   Gtk::TreeViewColumn *plugin_col = get_column(2);
00460   if (plugin_col) plugin_col->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_name_clicked));
00461 }
00462
00463 } // end namespace fawkes