5.2. doc_client.cc


5.2.1
#include "doc.hh"
#include <cstdio>
#include <popt.h>
#include <gtk/gtk.h>
using namespace libdoc;

5.2.2

A simple GUI client program which connects to Docd and sends it queries.

static bool decode_options (int argc, char **argv);
static void init_gui ();
static void handle_query_activate (GtkEditable *ed, gpointer dat);
static void handle_result_selected (GtkWidget *list, int row, int col,
                                    GdkEventButton *e, gpointer dat);
static gboolean handle_window_delete (GtkWidget *w, GdkEvent *e, gpointer dat);
void statusbar_setmsg (const string &msg);
void statusbar_clear ();

static const char *progname;
static int docd_socket;
static GtkWidget *results_list;
static vector <SearchResult> results_vec;

static GtkWidget *status_bar;
static int status_bar_cntxt;
static size_t statusbar_stack_sz = 0;

static const char *option_host = 0;
static int option_port = DEFAULT_PORT;

5.2.3

The table of command line options.

poptOption options_table[] =
{
   POPT_AUTOHELP
   { "port", 'p', POPT_ARG_INT, &option_port, 0,
      "Specify the number of the port to connect to", NULL },
   { "host", 'h', POPT_ARG_STRING, &option_host, 0,
      "Specify the IP address of the host to connect to", NULL },
   { NULL, 0, 0, NULL, 0, NULL, NULL }
};

5.2.4
int
main (int argc, char **argv)
{
   if (!decode_options (argc, argv))
      return 1;

   try
   {
      docd_socket = open_connection_to_docd (option_host, option_port);
   }
   catch (Exception &e)
   {
      fprintf (stderr, "%s: %s\n", progname, e.explain().c_str());
      return 9;
   }

   gtk_init (&argc, &argv);
   init_gui();

   gtk_main();

   return 0;
}

5.2.5
bool
decode_options (int argc, char **argv)
{
   progname = argv[0];
   poptContext context = poptGetContext ("doc_client", argc,
                                         (const char **) argv,
                                         options_table, 0);

   int rc = poptGetNextOpt (context);
   if (rc < -1)
   {
      fprintf (stderr, "%s: error in command line options.\n", progname);
      return false;
   }

5.2.6

If the host IP address wasn't specified, set a default.

   if (option_host == 0)
      option_host = x_strdup (inet_ntoa (find_hostip()));

   return true;
}

5.2.7
void
init_gui ()
{

5.2.8

The main window.

   GtkWidget *win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_window_set_title (GTK_WINDOW (win), "Documentation Search");
   gtk_widget_set_usize (win, 250, 300);
   gtk_signal_connect (GTK_OBJECT (win), "delete_event",
                       GTK_SIGNAL_FUNC (handle_window_delete), 0);

5.2.9

The vertical box inside the window.

   GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
   gtk_container_add (GTK_CONTAINER (win), vbox);
   gtk_widget_show (vbox);

5.2.10

The text entry box for entering queries.

   GtkWidget *query = gtk_entry_new();
   gtk_widget_show (query);
   gtk_box_pack_start (GTK_BOX (vbox), query, FALSE, TRUE, 0);
   gtk_signal_connect (GTK_OBJECT (query), "activate",
                       GTK_SIGNAL_FUNC (handle_query_activate), 0);

5.2.11

The list of search results, in a scrolled window.

   char *titles[] = { "Format", "Title" };
   results_list = gtk_clist_new_with_titles (2, titles);
   gtk_clist_set_selection_mode (GTK_CLIST (results_list),
                                 GTK_SELECTION_SINGLE);
   gtk_signal_connect (GTK_OBJECT (results_list), "select_row",
                       GTK_SIGNAL_FUNC (handle_result_selected), 0);
   GtkWidget *scrwin = gtk_scrolled_window_new (0, 0);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrwin),
                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
   gtk_container_add (GTK_CONTAINER (scrwin), results_list);
   gtk_box_pack_start (GTK_BOX (vbox), scrwin, TRUE, TRUE, 1);
   gtk_widget_show (results_list);
   gtk_widget_show (scrwin);

5.2.12

Put a status bar at the bottom of the window.

   status_bar = gtk_statusbar_new();
   gtk_box_pack_start (GTK_BOX (vbox), status_bar, FALSE, TRUE, 0);
   gtk_widget_show (status_bar);
   status_bar_cntxt = gtk_statusbar_get_context_id (GTK_STATUSBAR (status_bar),
                                                    "foo");

5.2.13
   gtk_widget_grab_focus (query);
   gtk_widget_show (win);
}

5.2.14
void
handle_query_activate (GtkEditable *ed, gpointer dat)
{
   if (docd_socket < 0)
      fputs ("Something has gone wrong.\n", stderr);

   statusbar_setmsg ("Searching...");
   const char *s = gtk_entry_get_text (GTK_ENTRY (ed));

   try
   {
      perform_search (docd_socket, s, results_vec);

      gtk_clist_freeze (GTK_CLIST (results_list));
      gtk_clist_clear (GTK_CLIST (results_list));

      for (size_t i = 0; i < results_vec.size(); ++i)
      {
         const char *labels[2];
         labels[0] = results_vec[i].format_name.c_str();
         if (results_vec[i].document_title.empty())
            labels[1] = results_vec[i].document_filename.c_str();
         else
            labels[1] = results_vec[i].document_title.c_str();
         gtk_clist_append (GTK_CLIST (results_list), (char **) labels);
      }

      gtk_clist_thaw (GTK_CLIST (results_list));

      char buf[32];
      sprintf (buf, "%u", (unsigned int) results_vec.size());
      statusbar_setmsg (string (buf) + " documents found.");
   }
   catch (Exception &e)
   {
      fprintf (stderr, "%s: %s\n", progname, e.explain().c_str());
      statusbar_setmsg ("Error.");
   }
}

5.2.15
void
handle_result_selected (GtkWidget *list, int row, int col, GdkEventButton *e,
                        gpointer dat)
{
   if (e->type == GDK_2BUTTON_PRESS)
   {
      string cmd = results_vec[row].format_viewers;
      cmd += ' ';
      cmd += results_vec[row].document_filename;
      cmd += " &";
      fprintf (stderr, "%s\n", cmd.c_str());
      system (cmd.c_str());
   }
}

5.2.16
gboolean
handle_window_delete (GtkWidget *w, GdkEvent *e, gpointer dat)
{
   close (docd_socket);
   docd_socket = -1;

   gtk_main_quit();

   return FALSE;
}

5.2.17
void
statusbar_setmsg (const string &msg)
{
   if (statusbar_stack_sz > 0)
      gtk_statusbar_pop (GTK_STATUSBAR (status_bar), status_bar_cntxt);
   else
      ++statusbar_stack_sz;

   gtk_statusbar_push (GTK_STATUSBAR (status_bar), status_bar_cntxt,
                       msg.c_str());
}

5.2.18
void
statusbar_clear ()
{
   while (statusbar_stack_sz > 0)
   {
      gtk_statusbar_pop (GTK_STATUSBAR (status_bar), status_bar_cntxt);
      --statusbar_stack_sz;
   }
}