fuse_image_list_widget.cpp

00001 
00002 /***************************************************************************
00003  *  fuse_image_list_widget.cpp - Fuse image list widget
00004  *
00005  *  Created: Mon Mar 24 21:12:56 2008
00006  *  Copyright  2008  Daniel Beck
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 #include "fuse_image_list_widget.h"
00024 
00025 #include <fvutils/net/fuse_message.h>
00026 #include <fvutils/net/fuse_imagelist_content.h>
00027 
00028 #include <netinet/in.h>
00029 #include <cstring>
00030 #include <sstream>
00031 
00032 using namespace fawkes;
00033 
00034 namespace firevision {
00035 #if 0 /* just to make Emacs auto-indent happy */
00036 }
00037 #endif
00038 
00039 /** @class FuseImageListWidget <fvwidgets/fuse_image_list_widget.h>
00040  * This widget displays all available Fuse images in a tree view. It also can check
00041  * the registered host for new images, regularly.
00042  * @author Daniel Beck
00043  */
00044 
00045 /** Constructor. */
00046 FuseImageListWidget::FuseImageListWidget()
00047 {
00048   m_chk_compression   = NULL;
00049   m_chk_auto_update   = NULL;
00050 
00051   m_cur_client.active = false;
00052 
00053   m_new_clients.clear();
00054   m_delete_clients.clear();
00055 
00056   m_image_list = Gtk::TreeStore::create(m_image_record);
00057 
00058   m_signal_get_image_list.connect( sigc::mem_fun( *this, &FuseImageListWidget::get_image_list) );
00059   m_signal_delete_clients.connect( sigc::mem_fun( *this, &FuseImageListWidget::delete_clients) );
00060   m_signal_update_image_l.connect( sigc::mem_fun( *this, &FuseImageListWidget::update_image_list) );
00061 
00062   m_popup_menu = Gtk::manage( new Gtk::Menu() );
00063   Gtk::Menu::MenuList& menulist = m_popup_menu->items();
00064   menulist.push_back( Gtk::Menu_Helpers::MenuElem("Update now", sigc::mem_fun( *this, &FuseImageListWidget::update_image_list) ) );
00065   menulist.push_back( Gtk::Menu_Helpers::SeparatorElem() );
00066   menulist.push_back( Gtk::Menu_Helpers::MenuElem("Add host manually", sigc::mem_fun( *this, &FuseImageListWidget::on_add_host_manually) ) );
00067 
00068   set_image_list_trv(this);
00069 }
00070 
00071 /** Destructor. */
00072 FuseImageListWidget::~FuseImageListWidget()
00073 {
00074   FuseClient* c;
00075   m_new_clients.lock();
00076   while (m_new_clients.size() != 0)
00077     {
00078       c = m_new_clients.front().client;
00079       m_new_clients.pop_front();
00080       c->disconnect();
00081       c->cancel();
00082       c->join();
00083       delete c;
00084     }
00085   m_new_clients.unlock();
00086 
00087   if (m_cur_client.active)
00088     {
00089       m_cur_client.active = false;
00090       m_delete_clients.push_locked(m_cur_client.client);
00091     }
00092   delete_clients();
00093 }
00094 
00095 /** Call this method when new Fountain services are discovered.
00096  * @param name the name of the service
00097  * @param host_name the host the service is running on
00098  * @param port the port the service is running on
00099  */
00100 void
00101 FuseImageListWidget::add_fountain_service( const char* name,
00102                                            const char* host_name,
00103                                            uint32_t port )
00104 {
00105   // check whether it's already in the tree
00106   m_img_list_mutex.lock();
00107   Gtk::TreeModel::Children children = m_image_list->children();
00108   for ( Gtk::TreeModel::Children::iterator iter = children.begin();
00109         iter != children.end(); ++iter )
00110     {
00111       Gtk::TreeModel::Row row = *iter;
00112       if ( row[m_image_record.service_name] == Glib::ustring(name) )
00113         { 
00114           m_img_list_mutex.unlock();
00115           return; 
00116         }
00117     }
00118   m_img_list_mutex.unlock();
00119 
00120   // check if there is already a waiting request for this service
00121   m_new_clients.lock();
00122   for ( LockList<ClientData>::iterator iter = m_new_clients.begin();
00123         iter != m_new_clients.end(); ++iter )
00124     {
00125       if (name == iter->service_name)
00126         { 
00127           m_new_clients.unlock();
00128           return;
00129         }
00130     }
00131   m_new_clients.unlock();
00132 
00133   ClientData data;
00134   data.client = 0;
00135   data.service_name = std::string(name);
00136   data.host_name = std::string(host_name);
00137   data.port = port;
00138   data.active = false;
00139   
00140   m_new_clients.push_back_locked(data);
00141   m_signal_get_image_list();
00142 }
00143 
00144 /** Call this method when a Fountain service vanishes.
00145  * @param name the name of the service
00146  */
00147 void
00148 FuseImageListWidget::remove_fountain_service(const char* name)
00149 {
00150   m_img_list_mutex.lock();
00151   Gtk::TreeModel::Children children = m_image_list->children();
00152   Gtk::TreeModel::Children::iterator iter = children.begin();
00153   while ( iter != children.end() )
00154     {
00155       Gtk::TreeModel::Row row = *iter;
00156       if ( row[m_image_record.service_name] == Glib::ustring(name) )
00157         {
00158           iter = m_image_list->erase(iter);
00159           m_image_list->row_deleted( m_image_list->get_path(iter) );
00160         }
00161       else
00162         {
00163           ++iter;
00164         }
00165     }
00166   m_img_list_mutex.unlock();
00167 }
00168 
00169 /** Assign the TreeView widget to hold the list of images.
00170  * @param trv a Gtk::TreeView
00171  */
00172 void
00173 FuseImageListWidget::set_image_list_trv(Gtk::TreeView* trv)
00174 {
00175   m_img_list_mutex.lock();
00176   m_trv_image_list = trv;
00177   m_trv_image_list->set_model(m_image_list);
00178   m_trv_image_list->append_column("asdf", m_image_record.display_text);
00179   m_trv_image_list->set_headers_visible(false);
00180   m_trv_image_list->signal_event().connect( sigc::mem_fun(*this, &FuseImageListWidget::on_image_event) );
00181   m_trv_image_list->signal_cursor_changed().connect( sigc::mem_fun(*this, &FuseImageListWidget::on_image_selected) );
00182   m_img_list_mutex.unlock();
00183 }
00184 
00185 /** Assign the CheckButton to toggle the compression.
00186  * @param chk a Gtk::CheckButton
00187  */
00188 void
00189 FuseImageListWidget::set_toggle_compression_chk(Gtk::CheckButton* chk)
00190 {
00191   m_chk_compression = chk;
00192   m_chk_compression->signal_toggled().connect( sigc::mem_fun(*this, &FuseImageListWidget::on_compression_toggled) );
00193 }
00194 
00195 /** Assign the CheckButton that enables/disables the auto update function.
00196  * @param chk a Gtk::CheckButton
00197  */
00198 void
00199 FuseImageListWidget::set_auto_update_chk(Gtk::CheckButton* chk)
00200 {
00201   m_chk_auto_update = chk;
00202   m_chk_auto_update->signal_toggled().connect( sigc::mem_fun(*this, &FuseImageListWidget::on_auto_update_toggled) );
00203 }
00204 
00205 /** Access the Dispatcher that is signalled when a new image is selected in the list of
00206  * images.
00207  * @return reference to the Dispatcher that is activated when an image is selected in the
00208  *         list of images
00209  */
00210 Glib::Dispatcher&
00211 FuseImageListWidget::image_selected()
00212 {
00213   return m_signal_image_selected;
00214 }
00215 
00216 /** Get auto-update status.
00217  * @return true if auto-update is activated
00218  */
00219 bool
00220 FuseImageListWidget::auto_update()
00221 {
00222   return m_auto_update;
00223 }
00224 
00225 /** Set the auto-update status.
00226  * @param active (de-)activate auto-update
00227  * @param interval_sec the update interval in seconds
00228  */
00229 void
00230 FuseImageListWidget::set_auto_update(bool active, unsigned int interval_sec)
00231 {
00232   m_auto_update  = active;
00233   m_interval_sec = interval_sec;
00234 
00235   if (m_auto_update)
00236     {
00237 #if GLIBMM_MAJOR_VERSION > 2 || ( GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION >= 14 )
00238       m_timeout_conn = Glib::signal_timeout().connect_seconds( sigc::mem_fun(*this, &FuseImageListWidget::on_update_timeout),
00239                                                                m_interval_sec);
00240 #else
00241       m_timeout_conn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &FuseImageListWidget::on_update_timeout), m_interval_sec);
00242 #endif
00243     }
00244   else m_timeout_conn.disconnect();
00245 }
00246 
00247 /** Get the host name, port, and image id of the selected image.
00248  * @param host_name the host name of the selected image
00249  * @param port the port of the selected image
00250  * @param image_id the id of the selected image
00251  * @param compression true if compression shall be switched on
00252  * @return true if references could be assigned
00253  */
00254 bool
00255 FuseImageListWidget::get_selected_image( std::string& host_name, unsigned short& port,
00256                                          std::string& image_id, bool& compression )
00257 {
00258   if ( !m_trv_image_list )
00259     { return false; }
00260 
00261   m_img_list_mutex.lock();
00262   Glib::RefPtr<Gtk::TreeSelection> selection = m_trv_image_list->get_selection();
00263 
00264   if ( selection->count_selected_rows() != 1 )
00265     {
00266       m_img_list_mutex.unlock();
00267       return false;
00268     }
00269 
00270   Gtk::TreeModel::iterator iter = selection->get_selected();
00271   host_name = iter->get_value(m_image_record.host_name);
00272   port      = iter->get_value(m_image_record.port);
00273   image_id  = iter->get_value(m_image_record.image_id);
00274   m_img_list_mutex.unlock();
00275 
00276   if (m_chk_compression)
00277     { compression = m_chk_compression->get_active(); }
00278   else
00279     { compression = false; }
00280   
00281   return true;
00282 }
00283 
00284 
00285 bool
00286 FuseImageListWidget::on_image_event(GdkEvent *event)
00287 {
00288   GdkEventButton btn = event->button;
00289   if (btn.type == GDK_BUTTON_PRESS && btn.button == 3) {
00290     m_popup_menu->popup(btn.button, btn.time);
00291     return true;
00292   }
00293   return false;
00294 }
00295 
00296 void
00297 FuseImageListWidget::on_image_selected()
00298 {
00299   m_img_list_mutex.lock();
00300   Glib::RefPtr<Gtk::TreeSelection> selection = m_trv_image_list->get_selection();
00301 
00302   Gtk::TreeModel::iterator iter = selection->get_selected();
00303   Glib::ustring image_id;
00304   image_id = (*iter)[m_image_record.image_id];
00305   m_img_list_mutex.unlock();
00306 
00307   if ((image_id != m_cur_image_id) && (image_id != "invalid"))
00308     {
00309       m_cur_image_id = image_id;
00310       m_signal_image_selected();
00311     }
00312 }
00313 
00314 void
00315 FuseImageListWidget::on_auto_update_toggled()
00316 {
00317   set_auto_update( m_chk_auto_update->get_active() );
00318 }
00319 
00320 void
00321 FuseImageListWidget::on_compression_toggled()
00322 {
00323   m_signal_image_selected();
00324 }
00325 
00326 void
00327 FuseImageListWidget::get_image_list()
00328 {
00329   if (m_cur_client.active)
00330     // communication in progress
00331     { return; }
00332 
00333   m_new_clients.lock();
00334   if (m_new_clients.size() == 0)
00335     {
00336       if (m_auto_update)
00337         {
00338 #if GLIBMM_MAJOR_VERSION > 2 || ( GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION >= 14 )
00339           m_timeout_conn = Glib::signal_timeout().connect_seconds( sigc::mem_fun(*this, &FuseImageListWidget::on_update_timeout),
00340                                                                    m_interval_sec);
00341 #else
00342           m_timeout_conn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &FuseImageListWidget::on_update_timeout), m_interval_sec);
00343 #endif
00344         }
00345       m_new_clients.unlock();
00346       return;
00347     }
00348 
00349   m_cur_client = m_new_clients.front();
00350   m_cur_client.active = true;
00351   m_new_clients.pop_front();
00352   m_new_clients.unlock();
00353 
00354   try
00355     {
00356       m_cur_client.client = new FuseClient( m_cur_client.host_name.c_str(), 
00357                                             m_cur_client.port, this );
00358       m_cur_client.client->connect();
00359       m_cur_client.client->start();
00360       m_cur_client.client->enqueue(FUSE_MT_GET_IMAGE_LIST);
00361     }
00362   catch (Exception& e)
00363     {
00364       e.print_trace();
00365       m_cur_client.client->cancel();
00366       m_cur_client.client->join();
00367       delete m_cur_client.client;
00368       m_cur_client.active = false;
00369     }
00370 }
00371 
00372 void
00373 FuseImageListWidget::delete_clients()
00374 {
00375   FuseClient* c = 0;
00376 
00377   m_delete_clients.lock();
00378   while (m_delete_clients.size() != 0)
00379     {
00380       c = m_delete_clients.front();
00381       m_delete_clients.pop();
00382 
00383       c->disconnect();
00384       c->cancel();
00385       c->join();
00386       delete c;
00387     }
00388   m_delete_clients.unlock();
00389 }
00390 
00391 bool
00392 FuseImageListWidget::on_update_timeout()
00393 {
00394   m_signal_update_image_l();
00395   return m_auto_update;
00396 }
00397 
00398 void
00399 FuseImageListWidget::update_image_list()
00400 {
00401   m_timeout_conn.disconnect();
00402   if (m_img_list_mutex.try_lock())
00403     {
00404       Gtk::TreeModel::Children children = m_image_list->children();
00405       for ( Gtk::TreeModel::Children::iterator iter = children.begin();
00406             iter != children.end(); ++iter )
00407         {
00408           if ( (*iter)[m_image_record.image_id] == "invalid" )
00409             {
00410               ClientData data;
00411               data.client = 0;
00412               Glib::ustring service_name = (*iter)[m_image_record.service_name];
00413               Glib::ustring host_name = (*iter)[m_image_record.host_name];
00414               data.service_name = std::string( service_name.c_str() );
00415               data.host_name = std::string( host_name.c_str() );
00416               data.port = (*iter)[m_image_record.port];
00417               data.active = false;
00418     
00419               m_new_clients.push_back_locked(data);
00420             }
00421         }
00422       m_img_list_mutex.unlock();
00423     }
00424 
00425   m_signal_get_image_list();
00426 }
00427 
00428 void
00429 FuseImageListWidget::fuse_invalid_server_version(uint32_t local_version,
00430                                                 uint32_t remote_version) throw()
00431 {
00432   printf("Invalid versions: local: %u   remote: %u\n", local_version, remote_version);
00433 }
00434 
00435 void
00436 FuseImageListWidget::fuse_connection_established () throw()
00437 {
00438 }
00439 
00440 void
00441 FuseImageListWidget::fuse_connection_died() throw()
00442 {
00443   if (m_cur_client.active)
00444     {
00445       m_delete_clients.push_locked(m_cur_client.client);
00446       m_cur_client.active = false;
00447     }
00448   
00449   m_signal_delete_clients();
00450 }
00451 
00452 void
00453 FuseImageListWidget::fuse_inbound_received (FuseNetworkMessage *m) throw()
00454 {
00455   switch ( m->type() )
00456     {
00457     case FUSE_MT_IMAGE_LIST:
00458       {
00459         // check whether it's already in the tree
00460         m_img_list_mutex.lock();
00461         Gtk::TreeModel::Children children = m_image_list->children();
00462         Gtk::TreeModel::Children::iterator iter = children.begin();
00463         while ( iter != children.end() )
00464           {
00465             Gtk::TreeModel::Row row = *iter;
00466             if ( row[m_image_record.service_name] == Glib::ustring(m_cur_client.service_name) )
00467               {
00468                 iter = m_image_list->erase(iter);
00469               }
00470             else
00471               {
00472                 ++iter;
00473               }
00474           }
00475 
00476         try
00477           {
00478             FuseImageListContent* content = m->msgc<FuseImageListContent>();
00479             if ( content->has_next() )
00480               {
00481                 Gtk::TreeModel::Row row = *m_image_list->append();
00482                 row[m_image_record.display_text] = Glib::ustring(m_cur_client.host_name);
00483                 row[m_image_record.service_name] = Glib::ustring(m_cur_client.service_name);
00484                 row[m_image_record.host_name]    = Glib::ustring(m_cur_client.host_name);
00485                 row[m_image_record.port]         = m_cur_client.port;
00486                 row[m_image_record.colorspace]   = 0;
00487                 row[m_image_record.image_id]     = "invalid";
00488                 row[m_image_record.width]        = 0;
00489                 row[m_image_record.height]       = 0;
00490                 row[m_image_record.buffer_size]  = 0;
00491                 
00492                 Gtk::TreeModel::Path path = m_image_list->get_path(row);
00493 
00494                 while ( content->has_next() )
00495                   {
00496                     FUSE_imageinfo_t* image_info = content->next();
00497                     char image_id[IMAGE_ID_MAX_LENGTH + 1];
00498                     image_id[IMAGE_ID_MAX_LENGTH] = '\0';
00499                     strncpy(image_id, image_info->image_id, IMAGE_ID_MAX_LENGTH);
00500 
00501                     Gtk::TreeModel::Row childrow = *m_image_list->append( row.children() );
00502                     childrow[m_image_record.display_text] = Glib::ustring(image_id);
00503                     childrow[m_image_record.service_name] = Glib::ustring(m_cur_client.service_name);
00504                     childrow[m_image_record.host_name]    = Glib::ustring(m_cur_client.host_name);
00505                     childrow[m_image_record.port]         = m_cur_client.port;
00506                     childrow[m_image_record.colorspace]   = ntohl(image_info->colorspace);
00507                     childrow[m_image_record.image_id]     = Glib::ustring(image_id);
00508                     childrow[m_image_record.width]        = ntohl(image_info->width);
00509                     childrow[m_image_record.height]       = ntohl(image_info->height);
00510                     childrow[m_image_record.buffer_size]  = ntohl(image_info->buffer_size);
00511                   }
00512 
00513                 m_trv_image_list->expand_row(path, false);
00514               }
00515 
00516             delete content;
00517           }
00518         catch (Exception& e)
00519           {
00520             e.print_trace();
00521           }
00522 
00523         m_img_list_mutex.unlock();
00524 
00525         m_delete_clients.push_locked(m_cur_client.client);
00526         m_cur_client.active = false;
00527 
00528         m_signal_get_image_list();
00529         m_signal_delete_clients();
00530         
00531         break;
00532       }
00533 
00534     default:
00535       printf("Unhandled message type\n");
00536     }
00537 }
00538 
00539 void
00540 FuseImageListWidget::on_add_host_manually()
00541 {
00542   Gtk::Dialog* add_host = new Gtk::Dialog("Add host manually", this->get_window(), true);
00543   add_host->add_button(Gtk::Stock::ADD, Gtk::RESPONSE_OK);
00544   add_host->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
00545 
00546   Gtk::Table* tab = Gtk::manage( new Gtk::Table(2, 2, false) );
00547   Gtk::Label* hlab = Gtk::manage( new Gtk::Label("Host:") );
00548   Gtk::Label* plab = Gtk::manage( new Gtk::Label("Port:") );
00549   Gtk::Entry* hent = Gtk::manage( new Gtk::Entry() );
00550   Gtk::HBox*  pbox = Gtk::manage( new Gtk::HBox() );
00551 
00552   Gtk::Adjustment prange(2208, 1, 65535);
00553   Gtk::SpinButton *pent = Gtk::manage( new Gtk::SpinButton(prange) );
00554 
00555   char * fawkes_ip = getenv("FAWKES_IP");
00556   if (fawkes_ip) hent->set_text(std::string(fawkes_ip).append(":2208"));
00557   else hent->set_text("localhost:2208");
00558 
00559   pbox->pack_start(*pent, false, false, 0);
00560   tab->attach(*hlab, 1, 2, 1, 2);
00561   tab->attach(*plab, 1, 2, 2, 3);
00562   tab->attach(*hent, 2, 3, 1, 2);
00563   tab->attach(*pbox, 2, 3, 2, 3);
00564 
00565   add_host->get_vbox()->pack_start(*tab, false, true, 0);
00566   add_host->get_vbox()->show_all_children(true);
00567 
00568   if (add_host->run() == Gtk::RESPONSE_OK) {
00569     std::string name = "fountain on ";
00570     std::string host = hent->get_text();
00571     unsigned short port = 2208;
00572 
00573     Glib::ustring::size_type pos;
00574     if ((pos = host.find(':')) != Glib::ustring::npos)
00575     {
00576       Glib::ustring tmp_host = "";
00577       unsigned int tmp_port = 1234567; //Greater than max port num (i.e. 65535)
00578       std::istringstream is(host.replace(pos, 1, " "));
00579       is >> tmp_host;
00580       is >> tmp_port;
00581 
00582       if (tmp_port != 1234567 && tmp_host.size())
00583       {
00584         host = tmp_host;
00585         port = tmp_port;
00586       }
00587     }
00588 
00589     name.append(host);
00590     add_fountain_service(name.c_str(), host.c_str(), port);
00591   }
00592 
00593   add_host->hide();
00594   delete add_host;
00595 }
00596 
00597 } // end namespace firevision

Generated on Tue Feb 22 13:31:17 2011 for Fawkes API by  doxygen 1.4.7