service_chooser_dialog.cpp

00001 
00002 /***************************************************************************
00003  *  service_chooser_dialog.cpp - Dialog for choosing a network service
00004  *
00005  *  Created: Sun Oct 12 17:06:06 2008 (split from lasergui_hildon.cpp)
00006  *  Copyright  2008  Tim Niemueller [www.niemueller.de]
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. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <core/exception.h>
00025 #include <core/exceptions/software.h>
00026 #include <netcomm/fawkes/client.h>
00027 #include <netcomm/utils/resolver.h>
00028 #include <gui_utils/service_chooser_dialog.h>
00029 #include <gui_utils/service_model.h>
00030 
00031 #include <algorithm>
00032 #include <cstring>
00033 #include <sys/types.h>
00034 #include <sys/socket.h>
00035 #include <arpa/inet.h>
00036 #include <netinet/in.h>
00037 
00038 namespace fawkes {
00039 #if 0 /* just to make Emacs auto-indent happy */
00040 }
00041 #endif
00042 
00043 /** @class ServiceChooserDialog <gui_utils/service_chooser_dialog.h>
00044  * Service chooser dialog.
00045  * Allows to choose a service discovered via Avahi. Use the run routine,
00046  * it returns 1 if a service was selected or 0 if no service was found or
00047  * the selection was cancelled. The dialog is always modal.
00048  * @author Tim Niemueller
00049  */
00050 
00051 /** Constructor.
00052  * @param parent parent window
00053  * @param title title of the dialog
00054  * @param service service string
00055  */
00056 ServiceChooserDialog::ServiceChooserDialog(Gtk::Window &parent,
00057                                            Glib::ustring title,
00058                                            const char *service)
00059   : Gtk::Dialog(title, parent, /* modal */ true, /* separator */ true),
00060     __parent(parent), __expander("Manual entry")
00061 {
00062   __service_model = new ServiceModel(service);
00063   ctor();
00064   __client = NULL;
00065 }
00066 
00067 
00068 /** Constructor.
00069  * @param parent parent window
00070  * @param client Fawkes network client to connect on run()
00071  * @param title title of the dialog
00072  * @param service service string
00073  */
00074 ServiceChooserDialog::ServiceChooserDialog(Gtk::Window &parent,
00075                                            FawkesNetworkClient *client,
00076                                            Glib::ustring title,
00077                                            const char *service)
00078   : Gtk::Dialog(title, parent, /* modal */ true, /* separator */ true),
00079     __parent(parent), __expander("Manual entry")
00080 {
00081   __service_model = new ServiceModel(service);
00082   ctor();
00083   __client = client;
00084 }
00085 
00086 
00087 /** Destructor. */
00088 ServiceChooserDialog::~ServiceChooserDialog()
00089 {
00090   delete __service_model;
00091 }
00092 
00093 
00094 void
00095 ServiceChooserDialog::ctor()
00096 {
00097   set_default_size(360, 240);
00098 
00099   __treeview.set_model(__service_model->get_list_store());
00100   __treeview.append_column("Service", __service_model->get_column_record().name);
00101   __treeview.append_column("IP Address", __service_model->get_column_record().ipaddr);
00102   __scrollwin.add(__treeview);
00103   __scrollwin.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
00104   __treeview.show();
00105   __expander.add(__entry);
00106   __entry.show();
00107   __entry.set_activates_default(true);
00108 
00109   char * fawkes_ip = getenv("FAWKES_IP");
00110   if (fawkes_ip) __entry.set_text(fawkes_ip);
00111   else __entry.set_text("localhost");
00112 
00113   Gtk::VBox *vbox = get_vbox();
00114   vbox->pack_start(__scrollwin);
00115   vbox->pack_end(__expander, Gtk::PACK_SHRINK);
00116   __scrollwin.show();
00117   __expander.show();
00118 
00119   add_button(Gtk::Stock::CANCEL, 0);
00120   add_button(Gtk::Stock::OK, 1);
00121 
00122   set_default_response(1);
00123 
00124   __treeview.signal_row_activated().connect(sigc::bind(sigc::hide<0>(sigc::hide<0>(sigc::mem_fun(*this, &ServiceChooserDialog::response))), 1));
00125 
00126 #ifdef GLIBMM_PROPERTIES_ENABLED
00127   __expander.property_expanded().signal_changed().connect(sigc::mem_fun(*this, &ServiceChooserDialog::on_expander_changed));
00128 #endif
00129 }
00130 
00131 
00132 /** Get selected service.
00133  * If a service has been selected use this method to get the IP Address as
00134  * string of the host that has the service and the port.
00135  * @param name name of the service
00136  * @param hostname hostname of the host associated with the service
00137  * @param ipaddr upon successful return contains the IP address as string
00138  * @param port upon successful return contains the port
00139  * @exception Exception thrown if no service has been selected
00140  */
00141 void
00142 ServiceChooserDialog::get_selected_service(Glib::ustring &name,
00143                                            Glib::ustring &hostname,
00144                                            Glib::ustring &ipaddr,
00145                                            unsigned short int &port)
00146 {
00147   Glib::RefPtr<Gtk::TreeSelection> treesel = __treeview.get_selection();
00148   if (__expander.get_expanded() && !__treeview.has_focus()) {
00149     if ( __entry.get_text_length() > 0 ) {
00150       char *tmpvalue = strdup(__entry.get_text().c_str());
00151 
00152       if ( strchr(tmpvalue, ':') != NULL ) {
00153         char *save_ptr;
00154         hostname = strtok_r(tmpvalue, ":", &save_ptr);
00155         char *tmpport = strtok_r(NULL, "", &save_ptr);
00156 
00157         int port_num = atoi(tmpport);
00158         if ( (port_num < 0) || (port_num > 0xFFFF) ) {
00159           throw OutOfBoundsException("Invalid port", port_num, 0, 0xFFFF);
00160         }
00161         port = port_num;
00162       } else {
00163         hostname = tmpvalue;
00164         port = 0;
00165       }
00166 
00167       // evil, but Glib::Regex is only availabel in ver >= 2.14, n/a on maemo
00168       ipaddr = hostname;
00169 
00170       name = hostname;
00171 
00172       free(tmpvalue);
00173       return;
00174     }
00175   }
00176 
00177   Gtk::TreeModel::iterator iter = treesel->get_selected();
00178   if (iter) {
00179     Gtk::TreeModel::Row row = *iter;
00180     name     = row[__service_model->get_column_record().name];
00181     hostname = row[__service_model->get_column_record().hostname];
00182     ipaddr   = row[__service_model->get_column_record().ipaddr];
00183     port     = row[__service_model->get_column_record().port];
00184 
00185   } else {
00186     throw Exception("No host selected");
00187   }
00188 }
00189 
00190 
00191 /** Get raw address.
00192  * @param addr upon returns contains the raw representation of the IP address
00193  * @param addr_size size in bytes of addr, if addr_size is too small for an
00194  * AF_INET addr an exception is thrown.
00195  */
00196 void
00197 ServiceChooserDialog::get_raw_address(struct sockaddr *addr, socklen_t addr_size)
00198 {
00199   if ( addr_size < sizeof(struct sockaddr_in) ) {
00200     throw Exception("Size of addrlen too small, only %u bytes, but required %zu\n",
00201                     addr_size, sizeof(struct sockaddr_in));
00202   }
00203   Glib::ustring name, hostname, ipaddr;
00204   unsigned short int port;
00205   get_selected_service (name, hostname, ipaddr, port);
00206 
00207   if (inet_pton(AF_INET, ipaddr.c_str(), &(((struct sockaddr_in *)addr)->sin_addr)) <= 0) {
00208     NetworkNameResolver resolver;
00209     struct sockaddr_in *saddr;
00210     socklen_t saddr_len;
00211     if (resolver.resolve_name_blocking(ipaddr.c_str(), (struct sockaddr **)&saddr, &saddr_len)) {
00212       memcpy(addr, saddr, std::min(saddr_len, addr_size));
00213     } else {
00214       throw Exception("Could not lookup hostname '%s' and it is not a valid IP address",
00215                       ipaddr.c_str());
00216     }
00217   }
00218 
00219 }
00220 
00221 
00222 /** Signal handler for expander event.
00223  * Called when expander is (de-)expanded. Only works with Glibmm properties
00224  * enabled, i.e. not on Maemo.
00225  */
00226 void
00227 ServiceChooserDialog::on_expander_changed()
00228 {
00229   if (__expander.get_expanded()) {
00230     __entry.grab_focus();
00231   } else {
00232     __treeview.grab_focus();
00233   }
00234 }
00235 
00236 
00237 /** Run dialog and try to connect.
00238  * This runs the service chooser dialog and connects to the given service
00239  * with the attached FawkesNetworkClient. If the connection couldn't be established
00240  * an error dialog is shown. You should not rely on the connection to be
00241  * active after calling this method, rather you should use a ConnectionDispatcher
00242  * to get the "connected" signal.
00243  */
00244 void
00245 ServiceChooserDialog::run_and_connect()
00246 {
00247   if (! __client)  throw NullPointerException("FawkesNetworkClient not set");
00248   if (__client->connected()) throw Exception("Client is already connected");
00249 
00250   if ( run() ) {
00251     try {
00252       Glib::ustring name;
00253       Glib::ustring hostname;
00254       Glib::ustring ipaddr;
00255       unsigned short int port;
00256       get_selected_service(name, hostname, ipaddr, port);
00257       if ( port == 0 )  port = 1910;
00258 
00259       __client->connect(hostname.c_str(), ipaddr.c_str(), port);
00260     } catch (Exception &e) {
00261       Glib::ustring message = *(e.begin());
00262       Gtk::MessageDialog md(__parent, message, /* markup */ false,
00263                             Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK,
00264                             /* modal */ true);
00265       md.set_title("Connection failed");
00266       md.run();
00267     }
00268   }
00269 }
00270 
00271 } // end of namespace fawkes

Generated on 1 Mar 2011 for Fawkes API by  doxygen 1.6.1