Fawkes API  Fawkes Development Version
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 PluginTreeView::PluginTreeView()
00065   : m_dispatcher(FAWKES_CID_PLUGINMANAGER)
00066 {
00067   ctor();
00068 }
00069 
00070 /** Constructor.
00071  * @param cobject pointer to base object type
00072  * @param builder Gtk::Builder instance
00073  */
00074 PluginTreeView::PluginTreeView(BaseObjectType* cobject,
00075                                const Glib::RefPtr<Gtk::Builder> builder)
00076   : Gtk::TreeView(cobject),
00077     m_dispatcher(FAWKES_CID_PLUGINMANAGER)
00078 {
00079   ctor();
00080 }
00081 
00082 
00083 void
00084 PluginTreeView::ctor()
00085 {
00086   m_plugin_list = Gtk::ListStore::create(m_plugin_record);
00087   set_model(m_plugin_list);
00088   set_rules_hint(true);
00089   append_column("#", m_plugin_record.index);
00090   append_column_editable("Status", m_plugin_record.loaded);
00091   append_plugin_column();
00092 
00093   on_name_clicked();
00094   Gtk::TreeViewColumn *column = get_column(0);
00095   column->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_id_clicked));
00096   column = get_column(1);
00097   column->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_status_clicked));
00098 
00099   Gtk::CellRendererToggle* renderer;
00100   renderer = dynamic_cast<Gtk::CellRendererToggle*>( get_column_cell_renderer(1) );
00101   renderer->signal_toggled().connect( sigc::mem_fun(*this, &PluginTreeView::on_status_toggled));
00102 
00103   m_dispatcher.signal_connected().connect(sigc::mem_fun(*this, &PluginTreeView::on_connected));
00104   m_dispatcher.signal_disconnected().connect(sigc::mem_fun(*this, &PluginTreeView::on_disconnected));
00105   m_dispatcher.signal_message_received().connect(sigc::mem_fun(*this, &PluginTreeView::on_message_received));
00106 
00107 }
00108 
00109 /** Destructor. */
00110 PluginTreeView::~PluginTreeView()
00111 {
00112   if (m_dispatcher)
00113   {
00114     // unsubscribe
00115     FawkesNetworkMessage* msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00116                                                          MSG_PLUGIN_UNSUBSCRIBE_WATCH);
00117     m_dispatcher.get_client()->enqueue(msg);
00118 
00119     m_dispatcher.get_client()->deregister_handler(FAWKES_CID_PLUGINMANAGER);
00120   }
00121 
00122 #ifdef HAVE_GCONFMM
00123   if (__gconf) {
00124 #  ifdef GLIBMM_EXCEPTIONS_ENABLED
00125     __gconf->remove_dir(__gconf_prefix);
00126 #  else
00127     std::auto_ptr<Glib::Error> error;
00128     __gconf->remove_dir(__gconf_prefix, error);
00129 #  endif
00130   }
00131 #endif
00132 }
00133 
00134 
00135 /** Set the network client.
00136  * @param client a Fawkes network client
00137  */
00138 void
00139 PluginTreeView::set_network_client(FawkesNetworkClient* client)
00140 {
00141   m_dispatcher.set_client(client);
00142 }
00143 
00144 
00145 /** Set Gconf prefix.
00146  * @param gconf_prefix the GConf prefix
00147  */
00148 void
00149 PluginTreeView::set_gconf_prefix(Glib::ustring gconf_prefix)
00150 {
00151 #ifdef HAVE_GCONFMM
00152   if (! __gconf) {
00153     __gconf = Gnome::Conf::Client::get_default_client();
00154   } else {
00155 #  ifdef GLIBMM_EXCEPTIONS_ENABLED
00156     __gconf->remove_dir(__gconf_prefix);
00157 #  else
00158     std::auto_ptr<Glib::Error> error;
00159     __gconf->remove_dir(__gconf_prefix, error);
00160 #  endif
00161   }
00162 
00163 #ifdef GLIBMM_EXCEPTIONS_ENABLED
00164   __gconf->add_dir(gconf_prefix);
00165 #else
00166   std::auto_ptr<Glib::Error> error;
00167   __gconf->add_dir(gconf_prefix, Gnome::Conf::CLIENT_PRELOAD_NONE, error);
00168 #endif
00169   __gconf_prefix = gconf_prefix;
00170 
00171   if (__gconf_connection) {
00172     __gconf_connection.disconnect();
00173   }
00174   __gconf_connection = __gconf->signal_value_changed().connect(sigc::hide(sigc::hide(sigc::mem_fun(*this, &PluginTreeView::on_config_changed))));
00175 
00176   on_config_changed();
00177 #endif
00178 }
00179 
00180 void
00181 PluginTreeView::on_connected()
00182 {
00183   try
00184   {
00185     FawkesNetworkClient *client = m_dispatcher.get_client();
00186 
00187     // subscribe for load-/unload messages
00188     FawkesNetworkMessage* msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00189                                                          MSG_PLUGIN_SUBSCRIBE_WATCH);
00190     client->enqueue(msg);
00191 
00192     // request list of available plugins
00193     msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00194                                    MSG_PLUGIN_LIST_AVAIL);
00195     client->enqueue(msg);
00196 
00197     // request list of loaded plugins
00198     msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00199                                    MSG_PLUGIN_LIST_LOADED);
00200     client->enqueue(msg);
00201   }
00202   catch (Exception& e)
00203   {
00204     e.print_trace();
00205   }
00206 }
00207 
00208 /** Signal handler that is called whenever the connection is terminated. */
00209 void
00210 PluginTreeView::on_disconnected()
00211 {
00212   m_plugin_list->clear();
00213 }
00214 
00215 
00216 void
00217 PluginTreeView::on_message_received(fawkes::FawkesNetworkMessage* msg)
00218 {
00219   if (msg->cid() != FAWKES_CID_PLUGINMANAGER)  return;
00220 
00221   // loading
00222   unsigned int msgid = msg->msgid();
00223   if ( (msgid == MSG_PLUGIN_LOADED) ||
00224        (msgid == MSG_PLUGIN_LOAD_FAILED) ||
00225        (msgid == MSG_PLUGIN_UNLOADED) ||
00226        (msgid == MSG_PLUGIN_UNLOAD_FAILED) )
00227   {
00228     Glib::ustring name = "";
00229     bool loaded = false;
00230 
00231     if ( msgid == MSG_PLUGIN_LOADED)
00232     {
00233       if ( msg->payload_size() != sizeof(plugin_loaded_msg_t) )
00234       {
00235         printf("Invalid message size (load succeeded)\n");
00236       }
00237       else
00238       {
00239         plugin_loaded_msg_t* m = (plugin_loaded_msg_t*) msg->payload();
00240         name   = m->name;
00241         loaded = true;
00242       }
00243     }
00244     else if ( msgid == MSG_PLUGIN_LOAD_FAILED )
00245     {
00246       if ( msg->payload_size() != sizeof(plugin_load_failed_msg_t) )
00247       {
00248         printf("Invalid message size (load failed)\n");
00249       }
00250       else
00251       {
00252         plugin_load_failed_msg_t* m = (plugin_load_failed_msg_t*) msg->payload();
00253         name   = m->name;
00254         loaded = false;
00255       }
00256     }
00257     else if ( msg->msgid() == MSG_PLUGIN_UNLOADED )
00258     {
00259       if ( msg->payload_size() != sizeof(plugin_unloaded_msg_t) )
00260       {
00261         printf("Invalid message size (unload succeeded)\n");
00262       }
00263       else
00264       {
00265         plugin_unloaded_msg_t* m = (plugin_unloaded_msg_t*) msg->payload();
00266         name   = m->name;
00267         loaded = false;
00268       }
00269     }
00270     else if ( msg->msgid() == MSG_PLUGIN_UNLOAD_FAILED)
00271     {
00272       if ( msg->payload_size() != sizeof(plugin_unload_failed_msg_t) )
00273       {
00274         printf("Invalid message size (unload failed)\n");
00275       }
00276       else
00277       {
00278         plugin_unload_failed_msg_t* m = (plugin_unload_failed_msg_t*) msg->payload();
00279         name   = m->name;
00280         loaded = true;
00281       }
00282     }
00283 
00284     Gtk::TreeIter iter;
00285     for ( iter  = m_plugin_list->children().begin();
00286           iter != m_plugin_list->children().end();
00287           ++iter )
00288     {
00289       Glib::ustring n = (*iter)[m_plugin_record.name];
00290       if ( n == name )
00291       {
00292         (*iter)[m_plugin_record.loaded] = loaded;
00293         break;
00294       }
00295     }
00296   }
00297   else if (msgid == MSG_PLUGIN_AVAIL_LIST)
00298   {
00299     m_plugin_list->clear();
00300     PluginListMessage* plm = msg->msgc<PluginListMessage>();
00301     while ( plm->has_next() )
00302     {
00303       char *plugin_name = plm->next();
00304       char *plugin_desc = NULL;
00305       if ( plm->has_next() ) {
00306         plugin_desc = plm->next();
00307       } else {
00308         plugin_desc = strdup("Unknown, malformed plugin list message?");
00309       }
00310 
00311       Gtk::TreeModel::Row row = *m_plugin_list->append();
00312       unsigned int index = m_plugin_list->children().size();
00313       row[m_plugin_record.index]       = index;
00314       row[m_plugin_record.name]        = plugin_name;
00315       row[m_plugin_record.description] = plugin_desc;
00316       row[m_plugin_record.loaded]      = false;
00317 
00318       free(plugin_name);
00319       free(plugin_desc);
00320     }
00321     delete plm;
00322   }
00323   else if ( msg->msgid() == MSG_PLUGIN_AVAIL_LIST_FAILED)
00324   {
00325     printf("Obtaining list of available plugins failed\n");
00326   }
00327   else if (msg->msgid() == MSG_PLUGIN_LOADED_LIST )
00328   {
00329     PluginListMessage* plm = msg->msgc<PluginListMessage>();
00330     while ( plm->has_next() )
00331     {
00332       char* name = plm->next();
00333 
00334       Gtk::TreeIter iter;
00335       for ( iter  = m_plugin_list->children().begin();
00336             iter != m_plugin_list->children().end();
00337             ++iter )
00338       {
00339         Glib::ustring n = (*iter)[m_plugin_record.name];
00340         if ( n == name )
00341         {
00342           (*iter)[m_plugin_record.loaded] = true;
00343           break;
00344         }
00345       }
00346       free(name);
00347     }
00348     delete plm;
00349   }
00350   else if ( msg->msgid() == MSG_PLUGIN_LOADED_LIST_FAILED)
00351   {
00352     printf("Obtaining list of loaded plugins failed\n");
00353   }
00354 
00355   // unknown message received
00356   else
00357   {
00358     printf("received message with msg-id %d\n", msg->msgid());
00359   }
00360 }
00361 
00362 /** Signal handler that is called when the loaded checkbox is
00363  * toggled.
00364  * @param path the path of the selected row
00365  */
00366 void
00367 PluginTreeView::on_status_toggled(const Glib::ustring& path)
00368 {
00369   if ( ! m_dispatcher.get_client()->connected() )  return;
00370 
00371   Gtk::TreeModel::Row row = *m_plugin_list->get_iter(path);
00372   Glib::ustring plugin_name = row[m_plugin_record.name];
00373   bool loaded = row[m_plugin_record.loaded];
00374 
00375   if (loaded)
00376   {
00377     plugin_load_msg_t* m = (plugin_load_msg_t*) calloc(1, sizeof(plugin_load_msg_t));
00378     strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH);
00379 
00380     FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00381                                                          MSG_PLUGIN_LOAD,
00382                                                          m, sizeof(plugin_load_msg_t));
00383     m_dispatcher.get_client()->enqueue(msg);
00384   }
00385   else
00386   {
00387     plugin_unload_msg_t* m = (plugin_unload_msg_t *)calloc(1, sizeof(plugin_unload_msg_t));
00388     strncpy(m->name, plugin_name.c_str(), PLUGIN_MSG_NAME_LENGTH);
00389 
00390     FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_PLUGINMANAGER,
00391                                                          MSG_PLUGIN_UNLOAD,
00392                                                          m, sizeof(plugin_unload_msg_t));
00393     m_dispatcher.get_client()->enqueue(msg);
00394   }
00395 }
00396 
00397 /**
00398  * TreeView gets sorted by id
00399  */
00400 void
00401 PluginTreeView::on_id_clicked()
00402 {
00403   m_plugin_list->set_sort_column(0, Gtk::SORT_ASCENDING);
00404 }
00405 
00406 /**
00407  * TreeView gets sorted by status (loaded/unloaded)
00408  */
00409 void
00410 PluginTreeView::on_status_clicked()
00411 {
00412   m_plugin_list->set_sort_column(2, Gtk::SORT_DESCENDING);
00413 }
00414 
00415 /**
00416  * TreeView gets sorted by name
00417  */
00418 void
00419 PluginTreeView::on_name_clicked()
00420 {
00421   m_plugin_list->set_sort_column(1, Gtk::SORT_ASCENDING);
00422 }
00423 
00424 /**
00425  * Configuration data has changed
00426  */
00427 void
00428 PluginTreeView::on_config_changed()
00429 {
00430   Gtk::TreeViewColumn *plugin_col = get_column(2);
00431   if (plugin_col) remove_column(*plugin_col);
00432 
00433   append_plugin_column();
00434 }
00435 
00436 /**
00437  * Append appropriate plugin column - depending on the GConf value
00438  */
00439 void
00440 PluginTreeView::append_plugin_column()
00441 {
00442 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
00443   bool description_as_tooltip = false;
00444 #  ifdef HAVE_GCONFMM
00445   if ( __gconf )
00446   {
00447 #    ifdef GLIBMM_EXCEPTIONS_ENABLED
00448     description_as_tooltip = __gconf->get_bool(__gconf_prefix + "/description_as_tooltip");
00449 #    else
00450     std::auto_ptr<Glib::Error> error;
00451     description_as_tooltip = __gconf->get_bool(__gconf_prefix + "/description_as_tooltip", error);
00452 #    endif
00453   }
00454 #  endif
00455 #endif
00456 
00457 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
00458   if (description_as_tooltip)
00459   {
00460 #endif
00461     append_column("Plugin", m_plugin_record.name);
00462 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
00463     set_tooltip_column(2);
00464   }
00465   else
00466   {
00467     TwoLinesCellRenderer *twolines_renderer = new TwoLinesCellRenderer();
00468     Gtk::TreeViewColumn *tlcol =
00469       new Gtk::TreeViewColumn("Plugin", *Gtk::manage(twolines_renderer));
00470     append_column(*Gtk::manage(tlcol));
00471 
00472  #  ifdef GLIBMM_PROPERTIES_ENABLED
00473     tlcol->add_attribute(twolines_renderer->property_line1(), m_plugin_record.name);
00474     tlcol->add_attribute(twolines_renderer->property_line2(), m_plugin_record.description);
00475  #  else
00476     tlcol->add_attribute(*twolines_renderer, "line1", m_plugin_record.name);
00477     tlcol->add_attribute(*twolines_renderer, "line2", m_plugin_record.description);
00478  #  endif
00479 
00480     set_tooltip_column(-1);
00481   }
00482 #endif
00483 
00484   set_headers_clickable();
00485   Gtk::TreeViewColumn *plugin_col = get_column(2);
00486   if (plugin_col) plugin_col->signal_clicked().connect(sigc::mem_fun(*this, &PluginTreeView::on_name_clicked));
00487 }
00488 
00489 } // end namespace fawkes