]> matita.cs.unibo.it Git - helm.git/commitdiff
first client implementation as a standalone application
authorStefano Zacchiroli <zack@upsilon.cc>
Fri, 10 Jan 2003 09:06:22 +0000 (09:06 +0000)
committerStefano Zacchiroli <zack@upsilon.cc>
Fri, 10 Jan 2003 09:06:22 +0000 (09:06 +0000)
helm/hbugs/client/.cvsignore [new file with mode: 0644]
helm/hbugs/client/Makefile [new file with mode: 0644]
helm/hbugs/client/hbugs_client.ml [new file with mode: 0644]
helm/hbugs/client/hbugs_gui.glade [new file with mode: 0644]

diff --git a/helm/hbugs/client/.cvsignore b/helm/hbugs/client/.cvsignore
new file mode 100644 (file)
index 0000000..b2aeedb
--- /dev/null
@@ -0,0 +1,9 @@
+*.cmi
+*.cmo
+*.cma
+*.cmx
+*.o
+*.a
+gui.ml
+hbugs_client
+hbugs_client.opt
diff --git a/helm/hbugs/client/Makefile b/helm/hbugs/client/Makefile
new file mode 100644 (file)
index 0000000..e96695d
--- /dev/null
@@ -0,0 +1,26 @@
+NAME = hbugs_client
+METADIR = ../meta
+REQUIRES = lablgtk threads hbugs-common
+PREDICATES = glade init
+COMMONOPTS = -package "$(REQUIRES)" -predicates "$(PREDICATES)"
+OCAMLC = OCAMLPATH="$(METADIR)" ocamlfind ocamlc $(COMMONOPTS)
+OCAMLOPT = OCAMLPATH="$(METADIR)" ocamlfind ocamlopt $(COMMONOPTS)
+
+all: byte
+world: byte opt
+byte: $(NAME)
+opt: $(NAME).opt
+
+gui.ml: hbugs_gui.glade
+       lablgladecc $< > $@
+gui.cmo: gui.ml
+       $(OCAMLC) -c $<
+gui.cmx: gui.ml
+       $(OCAMLOPT) -c $<
+$(NAME): gui.cmo $(NAME).ml
+       $(OCAMLC) -thread -package threads -linkpkg -o $@ $^
+$(NAME).opt: gui.cmx $(NAME).ml
+       $(OCAMLOPT) -thread -package threads -linkpkg -o $@ $^
+clean:
+       rm -f *.cm[aixo] *.cmxa *.[oa] $(NAME){,.opt} gui.ml
+.PHONY: all world byte opt clean
diff --git a/helm/hbugs/client/hbugs_client.ml b/helm/hbugs/client/hbugs_client.ml
new file mode 100644 (file)
index 0000000..c7df9e1
--- /dev/null
@@ -0,0 +1,347 @@
+(*
+ * Copyright (C) 2003:
+ *    Stefano Zacchiroli <zack@cs.unibo.it>
+ *    for the HELM Team http://helm.cs.unibo.it/
+ *
+ *  This file is part of HELM, an Hypertextual, Electronic
+ *  Library of Mathematics, developed at the Computer Science
+ *  Department, University of Bologna, Italy.
+ *
+ *  HELM is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  HELM is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with HELM; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ *  MA  02111-1307, USA.
+ *
+ *  For details, see the HELM World-Wide-Web page,
+ *  http://helm.cs.unibo.it/
+ *)
+
+open Hbugs_types;;
+open Printf;;
+
+exception Invalid_URL of string;;
+
+let global_debug = true;;
+
+class hbugsClient =
+
+  let http_url_RE = Pcre.regexp "^(http://)?(.*):(\\d+)" in
+  let port_of_http_url url =
+    try
+      let subs = Pcre.extract ~rex:http_url_RE url in
+      int_of_string subs.(3)
+    with e -> raise (Invalid_URL url)
+  in
+
+  object (self)
+
+    inherit Gui.hbugsMainWindow ()
+
+    val subscribeWindow = new Gui.subscribeWindow ()
+    val messageDialog = new Gui.messageDialog ()
+    val myOwnId = Hbugs_id_generator.new_client_id ()
+    val mutable myOwnUrl = "localhost:49082"
+    val mutable brokerUrl = "localhost:49081"
+    val mutable brokerId: broker_id option = None
+    val mutable selectedTutors: tutor_id list = []
+    val mutable statusContext = None
+    val mutable subscribeWindowStatusContext = None
+    val mutable debug = false (* enable/disable debugging buttons *)
+
+    initializer
+(*       self#setDebug global_debug; *)
+      self#initGui;
+      self#startLocalHttpDaemon ();
+      self#testLocalHttpDaemon ();
+      self#testBroker ();
+      self#registerClient ();
+      self#reconfigDebuggingButtons
+
+    method debugButtons =
+      List.map
+        (fun (b: GButton.button) -> new GObj.misc_ops b#as_widget)
+        [ self#startLocalHttpDaemonButton; self#testLocalHttpDaemonButton;
+        self#testBrokerButton; self#registerClientButton;
+        self#unregisterClientButton ]
+
+    method private initGui =
+
+        (* GUI: main window *)
+      ignore (self#hbugsMainWindow#connect#destroy self#quit);
+
+        (* GUI main window's menu *)
+      self#toggleDebuggingMenuItem#set_active debug;
+      ignore (self#toggleDebuggingMenuItem#connect#toggled self#toggleDebug);
+
+        (* GUI: local HTTP daemon settings *)
+      ignore (self#clientUrlEntry#connect#changed
+        (fun _ -> myOwnUrl <- self#clientUrlEntry#text));
+      self#clientUrlEntry#set_text myOwnUrl;
+      ignore (self#startLocalHttpDaemonButton#connect#clicked
+        self#startLocalHttpDaemon);
+      ignore (self#testLocalHttpDaemonButton#connect#clicked
+        self#testLocalHttpDaemon);
+
+        (* GUI: broker choice *)
+      ignore (self#brokerUrlEntry#connect#changed
+        (fun _ -> brokerUrl <- self#brokerUrlEntry#text));
+      self#brokerUrlEntry#set_text brokerUrl;
+      ignore (self#testBrokerButton#connect#clicked self#testBroker);
+      self#clientIdLabel#set_text myOwnId;
+
+        (* GUI: client registration *)
+      ignore (self#registerClientButton#connect#clicked self#registerClient);
+      ignore (self#unregisterClientButton#connect#clicked
+        self#unregisterClient);
+
+        (* GUI: subscriptions *)
+      ignore (self#showSubscriptionWindowButton#connect#clicked
+        (fun () ->
+          self#listTutors ();
+          subscribeWindow#subscribeWindow#show ()));
+
+        (* GUI: DEBUG state change *)
+      ignore (self#stateChangeButton#connect#clicked self#stateChange);
+
+        (* GUI: hints list *)
+      ignore (self#useHintButton#connect#clicked self#useHint);
+
+        (* GUI: main status bar *)
+      let ctxt = self#mainWindowStatusBar#new_context "0" in
+      statusContext <- Some ctxt;
+      ignore (ctxt#push "Ready");
+
+        (* GUI: subscription window *)
+      ignore (subscribeWindow#subscribeWindow#event#connect#delete
+        (fun _ -> subscribeWindow#subscribeWindow#misc#hide (); true));
+      ignore (subscribeWindow#listTutorsButton#connect#clicked self#listTutors);
+      let tutor_id_of_row row = subscribeWindow#tutorsCList#cell_text row 0 in
+      ignore (subscribeWindow#tutorsCList#connect#select_row
+        (fun ~row ~column ~event ->
+          selectedTutors <- tutor_id_of_row row :: selectedTutors));
+      ignore (subscribeWindow#tutorsCList#connect#unselect_row
+        (fun ~row ~column ~event ->
+          selectedTutors <-
+            List.filter ((<>) (tutor_id_of_row row)) selectedTutors));
+      ignore (subscribeWindow#subscribeButton#connect#clicked self#subscribe);
+      let ctxt = subscribeWindow#subscribeWindowStatusBar#new_context "0" in
+      subscribeWindowStatusContext <- Some ctxt;
+      ignore (ctxt#push "Ready");
+
+        (* GUI: message dialog *)
+      ignore (messageDialog#messageDialog#event#connect#delete
+        (fun _ -> messageDialog#messageDialog#misc#hide (); true));
+      ignore (messageDialog#okDialogButton#connect#clicked
+        (fun _ -> messageDialog#messageDialog#misc#hide ()))
+
+    (* accessory methods *)
+
+      (** pop up a (modal) dialog window showing msg to the user *)
+    method private showDialog msg =
+      messageDialog#dialogLabel#set_text msg;
+      messageDialog#messageDialog#show ()
+      (** use showDialog to display an hbugs message to the user *)
+    method private showMsgInDialog msg =
+      self#showDialog (Hbugs_messages.string_of_msg msg)
+
+      (** create a new thread which sends msg to broker, wait for an answer and
+      invoke callback passing response message as argument *)
+    method private sendReq ~msg callback =
+      let thread () =
+        try
+          callback (Hbugs_messages.submit_req ~url:(brokerUrl ^ "/act") msg)
+        with 
+        | (Hbugs_messages.Parse_error (subj, reason)) as e ->
+            self#showDialog
+              (sprintf
+"Parse_error, unable to fullfill request. Details follow.
+Request: %s
+Error: %s"
+                (Hbugs_messages.string_of_msg msg) (Printexc.to_string e))
+        | (Unix.Unix_error _) as e ->
+            self#showDialog
+              (sprintf
+"Can't connect to HBugs Broker
+Url: %s
+Error: %s"
+                brokerUrl (Printexc.to_string e))
+      in
+      ignore (Thread.create thread ())
+
+      (** check if a broker is authenticated using its broker_id
+      [ Background: during client registration, client save broker_id of its
+      broker, further messages from broker are accepted only if they carry the
+      same broker id ] *)
+    method private isAuthenticated id =
+      match brokerId with
+      | None -> false
+      | Some broker_id -> (id = broker_id)
+
+    (* actions *)
+
+    method startLocalHttpDaemon () =
+      let callback req outchan =
+        try
+          (match Hbugs_messages.msg_of_string req#body with
+          | Help ->
+              Hbugs_messages.respond_msg
+                (Usage "Local Http Daemon up and running!") outchan
+          | Hint (broker_id, hint) ->
+              if self#isAuthenticated broker_id then
+                ignore (self#hintsCList#append [hint])
+              else
+                Hbugs_messages.respond_exc "forbidden" broker_id outchan
+          | msg ->
+              Hbugs_messages.respond_exc
+                "unexpected_msg" (Hbugs_messages.string_of_msg msg) outchan)
+        with (Hbugs_messages.Parse_error _) as e ->
+          Hbugs_messages.respond_exc
+            "parse_error" (Printexc.to_string e) outchan
+      in
+      let addr = "0.0.0.0" in (* TODO actually user specified "My URL" is used
+                              only as a value to be sent to broker, local HTTP
+                              daemon will listen on "0.0.0.0", port is parsed
+                              from My URL though *)
+      let thread () =
+        try
+          Http_daemon.start'
+            ~addr ~port:(port_of_http_url myOwnUrl) ~mode:`Single callback
+        with
+        | Invalid_URL url -> self#showDialog (sprintf "Invalid URL: \"%s\"" url)
+        | e ->
+            self#showDialog (sprintf "Can't start local HTTP daemon: %s"
+              (Printexc.to_string e))
+      in
+      ignore (Thread.create thread ())
+
+    method testLocalHttpDaemon () =
+      try
+        let msg =
+          Hbugs_misc.http_post ~body:(Hbugs_messages.string_of_msg Help)
+            myOwnUrl
+        in
+        ignore msg
+(*         self#showDialog msg *)
+      with
+      | Hbugs_misc.Malformed_URL url ->
+          self#showDialog
+            (sprintf
+              "Handshake with local HTTP daemon failed, Invalid URL: \"%s\""
+              url)
+      | Hbugs_misc.Malformed_HTTP_response res ->
+          self#showDialog
+            (sprintf
+    "Handshake with local HTTP daemon failed, can't parse HTTP response: \"%s\""
+              res)
+      | (Unix.Unix_error _) as e ->
+          self#showDialog
+            (sprintf
+              "Handshake with local HTTP daemon failed, can't connect: \"%s\""
+              (Printexc.to_string e))
+
+    method testBroker () =
+      self#sendReq ~msg:Help
+        (function
+          | Usage _ -> ()
+          | unexpected_msg ->
+              self#showDialog
+                (sprintf
+                  "Handshake with HBugs Broker failed, unexpected message:\n%s"
+                  (Hbugs_messages.string_of_msg unexpected_msg)))
+
+    method registerClient () =
+      self#sendReq ~msg:(Register_client (myOwnId, myOwnUrl))
+        (function
+          | Client_registered broker_id ->
+              brokerId <- Some broker_id;
+(*
+              self#showDialog
+                (sprintf "Client %s registered @ broker %s" myOwnId broker_id)
+*)
+          | unexpected_msg ->
+              self#showDialog
+                (sprintf "Client NOT registered, unexpected message:\n%s"
+                  (Hbugs_messages.string_of_msg unexpected_msg)))
+
+    method unregisterClient () =
+      self#sendReq ~msg:(Unregister_client myOwnId)
+        self#showMsgInDialog
+
+    method stateChange () =
+      let state = (* TODO fill with a real state representation! *)
+        self#stateText#get_chars 0 (self#stateText#length)
+      in
+      self#sendReq ~msg:(State_change (myOwnId, state))
+        self#showMsgInDialog
+
+    method listTutors () =
+      self#sendReq ~msg:(List_tutors myOwnId)
+        (function
+          | Tutor_list (_, descriptions) ->
+              selectedTutors <- [];
+              subscribeWindow#tutorsCList#clear ();
+              List.iter
+                (fun (id, dsc) ->
+                  ignore (subscribeWindow#tutorsCList#append [id; dsc]))
+                descriptions
+          | unexpected_msg ->
+              self#showDialog
+                (sprintf "Can't list tutors, unexpected message:\n%s"
+                  (Hbugs_messages.string_of_msg unexpected_msg)))
+
+    method subscribe () =
+      let selectedTutors = List.sort compare selectedTutors in
+      self#sendReq ~msg:(Subscribe (myOwnId, selectedTutors))
+        (function
+          | (Subscribed (_, tutors)) as msg ->
+              let msg_string = Hbugs_messages.string_of_msg msg in
+              let subscribedTutors = List.sort compare tutors in
+              let msg =
+                if subscribedTutors = selectedTutors then
+                  sprintf "Subscription OK\n: %s" msg_string
+                else
+                  sprintf "Subscription mismatch\n: %s" msg_string
+              in
+              self#showDialog msg;
+              subscribeWindow#subscribeWindow#misc#hide ()
+          | unexpected_msg ->
+              self#showDialog
+                (sprintf "Subscription FAILED, unexpected message:\n%s"
+                  (Hbugs_messages.string_of_msg unexpected_msg)))
+
+    method useHint () = failwith "useHint: TODO not implemented"  (* TODO *)
+
+    method private quit () =
+      self#unregisterClient ();
+      GMain.Main.quit ()
+
+      (** enable/disable debugging buttons *)
+    method setDebug ?(force = false) value =
+      if (debug <> value) || force then
+      debug <- value
+
+    method private reconfigDebuggingButtons =
+      List.iter (* debug value changed, reconfigure buttons *)
+        (fun (b: GObj.misc_ops) -> if debug then b#show () else b#hide ())
+        self#debugButtons;
+    
+    method toggleDebug () =
+      self#setDebug (not debug);
+      self#reconfigDebuggingButtons
+
+  end
+;;
+
+ignore (new hbugsClient);
+GtkThread.main ()
+
diff --git a/helm/hbugs/client/hbugs_gui.glade b/helm/hbugs/client/hbugs_gui.glade
new file mode 100644 (file)
index 0000000..ff2d99c
--- /dev/null
@@ -0,0 +1,704 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+  <name>hbugs_gui</name>
+  <program_name>hbugs_gui</program_name>
+  <directory></directory>
+  <source_directory>src</source_directory>
+  <pixmaps_directory>pixmaps</pixmaps_directory>
+  <language>C</language>
+  <gnome_support>False</gnome_support>
+  <gettext_support>False</gettext_support>
+</project>
+
+<widget>
+  <class>GtkWindow</class>
+  <name>hbugsMainWindow</name>
+  <title>Hbugs: your personal proof trainer!</title>
+  <type>GTK_WINDOW_TOPLEVEL</type>
+  <position>GTK_WIN_POS_NONE</position>
+  <modal>False</modal>
+  <allow_shrink>False</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>False</auto_shrink>
+
+  <widget>
+    <class>GtkVBox</class>
+    <name>vbox1</name>
+    <homogeneous>False</homogeneous>
+    <spacing>0</spacing>
+
+    <widget>
+      <class>GtkMenuBar</class>
+      <name>menubar</name>
+      <shadow_type>GTK_SHADOW_OUT</shadow_type>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>False</fill>
+      </child>
+
+      <widget>
+       <class>GtkMenuItem</class>
+       <name>toolsMenu</name>
+       <label>Tools</label>
+       <right_justify>False</right_justify>
+
+       <widget>
+         <class>GtkMenu</class>
+         <name>toolsMenu_menu</name>
+
+         <widget>
+           <class>GtkCheckMenuItem</class>
+           <name>toggleDebuggingMenuItem</name>
+           <label>Debugging</label>
+           <active>False</active>
+           <always_show_toggle>True</always_show_toggle>
+         </widget>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkHBox</class>
+      <name>hbox4</name>
+      <homogeneous>False</homogeneous>
+      <spacing>2</spacing>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>False</fill>
+      </child>
+
+      <widget>
+       <class>GtkLabel</class>
+       <name>label11</name>
+       <label>My URL:</label>
+       <justify>GTK_JUSTIFY_CENTER</justify>
+       <wrap>False</wrap>
+       <xalign>0.5</xalign>
+       <yalign>0.5</yalign>
+       <xpad>0</xpad>
+       <ypad>0</ypad>
+       <child>
+         <padding>0</padding>
+         <expand>False</expand>
+         <fill>False</fill>
+       </child>
+      </widget>
+
+      <widget>
+       <class>GtkEntry</class>
+       <name>clientUrlEntry</name>
+       <tooltip>Local HTTP daemon URL</tooltip>
+       <can_focus>True</can_focus>
+       <editable>False</editable>
+       <text_visible>True</text_visible>
+       <text_max_length>0</text_max_length>
+       <text></text>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+      </widget>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>startLocalHttpDaemonButton</name>
+       <tooltip>Start the local HTTP daemon listening on the specified URL</tooltip>
+       <can_focus>True</can_focus>
+       <label>Start!</label>
+       <relief>GTK_RELIEF_NORMAL</relief>
+       <child>
+         <padding>0</padding>
+         <expand>False</expand>
+         <fill>False</fill>
+       </child>
+      </widget>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>testLocalHttpDaemonButton</name>
+       <can_focus>True</can_focus>
+       <label>Test!</label>
+       <relief>GTK_RELIEF_NORMAL</relief>
+       <child>
+         <padding>0</padding>
+         <expand>False</expand>
+         <fill>False</fill>
+       </child>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkVBox</class>
+      <name>vbox4</name>
+      <homogeneous>False</homogeneous>
+      <spacing>0</spacing>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>True</fill>
+      </child>
+
+      <widget>
+       <class>GtkHBox</class>
+       <name>hbox1</name>
+       <homogeneous>False</homogeneous>
+       <spacing>2</spacing>
+       <child>
+         <padding>0</padding>
+         <expand>False</expand>
+         <fill>False</fill>
+       </child>
+
+       <widget>
+         <class>GtkLabel</class>
+         <name>label1</name>
+         <label>Broker:</label>
+         <justify>GTK_JUSTIFY_CENTER</justify>
+         <wrap>False</wrap>
+         <xalign>0.5</xalign>
+         <yalign>0.5</yalign>
+         <xpad>0</xpad>
+         <ypad>0</ypad>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+       </widget>
+
+       <widget>
+         <class>GtkEntry</class>
+         <name>brokerUrlEntry</name>
+         <tooltip>HBugs broker URL</tooltip>
+         <can_focus>True</can_focus>
+         <editable>False</editable>
+         <text_visible>True</text_visible>
+         <text_max_length>0</text_max_length>
+         <text></text>
+         <child>
+           <padding>0</padding>
+           <expand>True</expand>
+           <fill>True</fill>
+         </child>
+       </widget>
+
+       <widget>
+         <class>GtkButton</class>
+         <name>testBrokerButton</name>
+         <can_focus>True</can_focus>
+         <label>Test!</label>
+         <relief>GTK_RELIEF_NORMAL</relief>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+       </widget>
+      </widget>
+
+      <widget>
+       <class>GtkHBox</class>
+       <name>hbox2</name>
+       <homogeneous>False</homogeneous>
+       <spacing>2</spacing>
+       <child>
+         <padding>0</padding>
+         <expand>False</expand>
+         <fill>False</fill>
+       </child>
+
+       <widget>
+         <class>GtkLabel</class>
+         <name>label2</name>
+         <label>Client ID:</label>
+         <justify>GTK_JUSTIFY_CENTER</justify>
+         <wrap>False</wrap>
+         <xalign>0.5</xalign>
+         <yalign>0.5</yalign>
+         <xpad>0</xpad>
+         <ypad>0</ypad>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+       </widget>
+
+       <widget>
+         <class>GtkLabel</class>
+         <name>clientIdLabel</name>
+         <label></label>
+         <justify>GTK_JUSTIFY_LEFT</justify>
+         <wrap>False</wrap>
+         <xalign>0.5</xalign>
+         <yalign>0.5</yalign>
+         <xpad>0</xpad>
+         <ypad>0</ypad>
+         <child>
+           <padding>0</padding>
+           <expand>True</expand>
+           <fill>True</fill>
+         </child>
+       </widget>
+
+       <widget>
+         <class>GtkButton</class>
+         <name>registerClientButton</name>
+         <can_focus>True</can_focus>
+         <label>Register</label>
+         <relief>GTK_RELIEF_NORMAL</relief>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+       </widget>
+
+       <widget>
+         <class>GtkButton</class>
+         <name>unregisterClientButton</name>
+         <can_focus>True</can_focus>
+         <label>Unregister</label>
+         <relief>GTK_RELIEF_NORMAL</relief>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkVBox</class>
+      <name>vbox5</name>
+      <homogeneous>True</homogeneous>
+      <spacing>0</spacing>
+      <child>
+       <padding>0</padding>
+       <expand>True</expand>
+       <fill>True</fill>
+      </child>
+
+      <widget>
+       <class>GtkFrame</class>
+       <name>frame3</name>
+       <border_width>4</border_width>
+       <label>Subscriptions</label>
+       <label_xalign>0</label_xalign>
+       <shadow_type>GTK_SHADOW_ETCHED_IN</shadow_type>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+
+       <widget>
+         <class>GtkVBox</class>
+         <name>vbox7</name>
+         <homogeneous>False</homogeneous>
+         <spacing>0</spacing>
+
+         <widget>
+           <class>GtkScrolledWindow</class>
+           <name>scrolledwindow3</name>
+           <hscrollbar_policy>GTK_POLICY_ALWAYS</hscrollbar_policy>
+           <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
+           <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+           <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+           <child>
+             <padding>0</padding>
+             <expand>True</expand>
+             <fill>True</fill>
+           </child>
+
+           <widget>
+             <class>GtkCList</class>
+             <name>subscriptionCList</name>
+             <can_focus>True</can_focus>
+             <columns>1</columns>
+             <column_widths>80</column_widths>
+             <selection_mode>GTK_SELECTION_SINGLE</selection_mode>
+             <show_titles>False</show_titles>
+             <shadow_type>GTK_SHADOW_IN</shadow_type>
+
+             <widget>
+               <class>GtkLabel</class>
+               <child_name>CList:title</child_name>
+               <name>label7</name>
+               <label>label7</label>
+               <justify>GTK_JUSTIFY_CENTER</justify>
+               <wrap>False</wrap>
+               <xalign>0.5</xalign>
+               <yalign>0.5</yalign>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+             </widget>
+           </widget>
+         </widget>
+
+         <widget>
+           <class>GtkButton</class>
+           <name>showSubscriptionWindowButton</name>
+           <can_focus>True</can_focus>
+           <label>Subscribe ...</label>
+           <relief>GTK_RELIEF_NORMAL</relief>
+           <child>
+             <padding>0</padding>
+             <expand>False</expand>
+             <fill>False</fill>
+           </child>
+         </widget>
+       </widget>
+      </widget>
+
+      <widget>
+       <class>GtkFrame</class>
+       <name>frame4</name>
+       <border_width>8</border_width>
+       <label>DEBUG: state</label>
+       <label_xalign>0</label_xalign>
+       <shadow_type>GTK_SHADOW_ETCHED_IN</shadow_type>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+
+       <widget>
+         <class>GtkHBox</class>
+         <name>hbox3</name>
+         <homogeneous>False</homogeneous>
+         <spacing>0</spacing>
+
+         <widget>
+           <class>GtkScrolledWindow</class>
+           <name>scrolledwindow5</name>
+           <hscrollbar_policy>GTK_POLICY_NEVER</hscrollbar_policy>
+           <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
+           <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+           <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+           <child>
+             <padding>0</padding>
+             <expand>True</expand>
+             <fill>True</fill>
+           </child>
+
+           <widget>
+             <class>GtkText</class>
+             <name>stateText</name>
+             <can_focus>True</can_focus>
+             <editable>True</editable>
+             <text></text>
+           </widget>
+         </widget>
+
+         <widget>
+           <class>GtkButton</class>
+           <name>stateChangeButton</name>
+           <can_focus>True</can_focus>
+           <label>send</label>
+           <relief>GTK_RELIEF_NORMAL</relief>
+           <child>
+             <padding>0</padding>
+             <expand>False</expand>
+             <fill>False</fill>
+           </child>
+         </widget>
+       </widget>
+      </widget>
+
+      <widget>
+       <class>GtkFrame</class>
+       <name>frame2</name>
+       <border_width>4</border_width>
+       <label>Hints</label>
+       <label_xalign>0</label_xalign>
+       <shadow_type>GTK_SHADOW_ETCHED_IN</shadow_type>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+
+       <widget>
+         <class>GtkVBox</class>
+         <name>vbox6</name>
+         <homogeneous>False</homogeneous>
+         <spacing>0</spacing>
+
+         <widget>
+           <class>GtkScrolledWindow</class>
+           <name>scrolledwindow2</name>
+           <width>400</width>
+           <hscrollbar_policy>GTK_POLICY_ALWAYS</hscrollbar_policy>
+           <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
+           <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+           <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+           <child>
+             <padding>0</padding>
+             <expand>True</expand>
+             <fill>True</fill>
+           </child>
+
+           <widget>
+             <class>GtkCList</class>
+             <name>hintsCList</name>
+             <can_focus>True</can_focus>
+             <columns>1</columns>
+             <column_widths>80</column_widths>
+             <selection_mode>GTK_SELECTION_SINGLE</selection_mode>
+             <show_titles>False</show_titles>
+             <shadow_type>GTK_SHADOW_IN</shadow_type>
+
+             <widget>
+               <class>GtkLabel</class>
+               <child_name>CList:title</child_name>
+               <name>label6</name>
+               <label>label6</label>
+               <justify>GTK_JUSTIFY_CENTER</justify>
+               <wrap>False</wrap>
+               <xalign>0.5</xalign>
+               <yalign>0.5</yalign>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+             </widget>
+           </widget>
+         </widget>
+
+         <widget>
+           <class>GtkButton</class>
+           <name>useHintButton</name>
+           <can_focus>True</can_focus>
+           <label>Use hint!</label>
+           <relief>GTK_RELIEF_NORMAL</relief>
+           <child>
+             <padding>0</padding>
+             <expand>False</expand>
+             <fill>False</fill>
+           </child>
+         </widget>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkStatusbar</class>
+      <name>mainWindowStatusBar</name>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>False</fill>
+      </child>
+    </widget>
+  </widget>
+</widget>
+
+<widget>
+  <class>GtkWindow</class>
+  <name>subscribeWindow</name>
+  <visible>False</visible>
+  <title>Hbugs: subscribe ...</title>
+  <type>GTK_WINDOW_TOPLEVEL</type>
+  <position>GTK_WIN_POS_NONE</position>
+  <modal>False</modal>
+  <allow_shrink>False</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>False</auto_shrink>
+
+  <widget>
+    <class>GtkVBox</class>
+    <name>vbox8</name>
+    <homogeneous>False</homogeneous>
+    <spacing>0</spacing>
+
+    <widget>
+      <class>GtkButton</class>
+      <name>listTutorsButton</name>
+      <can_focus>True</can_focus>
+      <label>Refresh</label>
+      <relief>GTK_RELIEF_NORMAL</relief>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>False</fill>
+      </child>
+    </widget>
+
+    <widget>
+      <class>GtkScrolledWindow</class>
+      <name>scrolledwindow4</name>
+      <hscrollbar_policy>GTK_POLICY_ALWAYS</hscrollbar_policy>
+      <vscrollbar_policy>GTK_POLICY_ALWAYS</vscrollbar_policy>
+      <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+      <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+      <child>
+       <padding>0</padding>
+       <expand>True</expand>
+       <fill>True</fill>
+      </child>
+
+      <widget>
+       <class>GtkCList</class>
+       <name>tutorsCList</name>
+       <width>600</width>
+       <height>300</height>
+       <can_focus>True</can_focus>
+       <columns>2</columns>
+       <column_widths>205,80</column_widths>
+       <selection_mode>GTK_SELECTION_EXTENDED</selection_mode>
+       <show_titles>True</show_titles>
+       <shadow_type>GTK_SHADOW_IN</shadow_type>
+
+       <widget>
+         <class>GtkLabel</class>
+         <child_name>CList:title</child_name>
+         <name>label12</name>
+         <label>Id</label>
+         <justify>GTK_JUSTIFY_CENTER</justify>
+         <wrap>False</wrap>
+         <xalign>0.5</xalign>
+         <yalign>0.5</yalign>
+         <xpad>0</xpad>
+         <ypad>0</ypad>
+       </widget>
+
+       <widget>
+         <class>GtkLabel</class>
+         <child_name>CList:title</child_name>
+         <name>label13</name>
+         <label>Description</label>
+         <justify>GTK_JUSTIFY_CENTER</justify>
+         <wrap>False</wrap>
+         <xalign>0.5</xalign>
+         <yalign>0.5</yalign>
+         <xpad>0</xpad>
+         <ypad>0</ypad>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkButton</class>
+      <name>subscribeButton</name>
+      <can_focus>True</can_focus>
+      <label>Subscribe to selected</label>
+      <relief>GTK_RELIEF_NORMAL</relief>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>False</fill>
+      </child>
+    </widget>
+
+    <widget>
+      <class>GtkStatusbar</class>
+      <name>subscribeWindowStatusBar</name>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>False</fill>
+      </child>
+    </widget>
+  </widget>
+</widget>
+
+<widget>
+  <class>GtkDialog</class>
+  <name>messageDialog</name>
+  <visible>False</visible>
+  <title>Message</title>
+  <type>GTK_WINDOW_TOPLEVEL</type>
+  <position>GTK_WIN_POS_CENTER</position>
+  <modal>True</modal>
+  <default_width>220</default_width>
+  <default_height>150</default_height>
+  <allow_shrink>True</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>False</auto_shrink>
+
+  <widget>
+    <class>GtkVBox</class>
+    <child_name>Dialog:vbox</child_name>
+    <name>dialogVbox1</name>
+    <homogeneous>False</homogeneous>
+    <spacing>0</spacing>
+
+    <widget>
+      <class>GtkHBox</class>
+      <child_name>Dialog:action_area</child_name>
+      <name>dialogAction_area1</name>
+      <border_width>2</border_width>
+      <homogeneous>True</homogeneous>
+      <spacing>5</spacing>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>True</fill>
+       <pack>GTK_PACK_END</pack>
+      </child>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>okDialogButton</name>
+       <can_focus>True</can_focus>
+       <label>OK</label>
+       <relief>GTK_RELIEF_NORMAL</relief>
+       <child>
+         <padding>0</padding>
+         <expand>False</expand>
+         <fill>True</fill>
+       </child>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkTable</class>
+      <name>table1</name>
+      <border_width>5</border_width>
+      <rows>1</rows>
+      <columns>1</columns>
+      <homogeneous>False</homogeneous>
+      <row_spacing>0</row_spacing>
+      <column_spacing>0</column_spacing>
+      <child>
+       <padding>0</padding>
+       <expand>True</expand>
+       <fill>True</fill>
+      </child>
+
+      <widget>
+       <class>GtkLabel</class>
+       <name>dialogLabel</name>
+       <label></label>
+       <justify>GTK_JUSTIFY_CENTER</justify>
+       <wrap>True</wrap>
+       <xalign>0.5</xalign>
+       <yalign>0.5</yalign>
+       <xpad>0</xpad>
+       <ypad>0</ypad>
+       <child>
+         <left_attach>0</left_attach>
+         <right_attach>1</right_attach>
+         <top_attach>0</top_attach>
+         <bottom_attach>1</bottom_attach>
+         <xpad>0</xpad>
+         <ypad>0</ypad>
+         <xexpand>True</xexpand>
+         <yexpand>True</yexpand>
+         <xshrink>False</xshrink>
+         <yshrink>False</yshrink>
+         <xfill>True</xfill>
+         <yfill>True</yfill>
+       </child>
+      </widget>
+    </widget>
+  </widget>
+</widget>
+
+</GTK-Interface>