diff -urN config/configure.in config/configure.in
--- config/configure.in	2005-10-28 11:04:17.000000000 +0200
+++ config/configure.in	2005-10-31 13:47:17.000000000 +0100
@@ -811,7 +811,7 @@
         AC_MSG_CHECKING(for librsvg - version >= $REQUIRED_RSVG)
         if pkg-config --atleast-version $REQUIRED_RSVG $PKG_RSVG; then
           GTK_CONFIG="pkg-config $PKG_GTK $PKG_RSVG"
-          REQUIRED_LABLGTK=2.4.0
+          REQUIRED_LABLGTK=2.6.0
           LABLGTK_NAME=lablgtk2
           AC_MSG_RESULT(yes)
         else
diff -urN config/Makefile.in config/Makefile.in
--- config/Makefile.in	2005-10-27 23:28:10.000000000 +0200
+++ config/Makefile.in	2005-10-31 10:14:00.000000000 +0100
@@ -1141,10 +1141,10 @@
 ifeq ("$(GUI)", "newgui2")
   GUI_BASE_SRCS= \
     $(SRC_GUI)/guiUtf8.ml      $(SRC_GUI)/guiMessages.ml \
-    $(SRC_GUI)/guiColumns.ml   $(SRC_GUI)/guiOptions.ml \
-    $(SRC_GUI)/guiArt.ml       $(SRC_GUI)/guiTools.ml \
-    $(SRC_GUI)/guiTypes2.ml    $(SRC_GUI)/guiTemplates.ml \
-    $(SRC_GUI)/configWindow.ml
+    $(SRC_GUI)/guiColumns.ml   $(SRC_GUI)/graphTypes.ml \
+    $(SRC_GUI)/guiOptions.ml   $(SRC_GUI)/guiArt.ml \
+    $(SRC_GUI)/guiTools.ml     $(SRC_GUI)/guiTypes2.ml \
+    $(SRC_GUI)/guiTemplates.ml $(SRC_GUI)/configWindow.ml
 else
   GUI_BASE_SRCS= \
     $(SRC_GUI)/gui_messages.ml   $(SRC_GUI)/gui_global.ml \
@@ -1153,6 +1153,9 @@
 endif
 
 GTK2GUI_SRCS=  \
+  $(SRC_GUI)/guiNetHtmlScanner.mll \
+  $(SRC_GUI)/guiNetHtml.ml \
+  $(SRC_GUI)/guiHtml.ml \
   $(SRC_GUI)/guiGlobal.ml \
   $(SRC_GUI)/guiMisc.ml \
   $(SRC_GUI)/guiCom.ml \
@@ -1160,6 +1163,8 @@
   $(SRC_GUI)/guiUsers.ml \
   $(SRC_GUI)/guiResults.ml \
   $(SRC_GUI)/guiInfoWindow.ml \
+  $(SRC_GUI)/guiGraphBase.ml \
+  $(SRC_GUI)/guiGraph.ml \
   $(SRC_GUI)/guiDownloads.ml \
   $(SRC_GUI)/guiServers.ml \
   $(SRC_GUI)/guiQueries.ml \
diff -urN src/gtk2/gui/graphTypes.ml src/gtk2/gui/graphTypes.ml
--- src/gtk2/gui/graphTypes.ml	1970-01-01 01:00:00.000000000 +0100
+++ src/gtk2/gui/graphTypes.ml	2005-02-02 22:18:03.000000000 +0100
@@ -0,0 +1,45 @@
+(* Copyright 2004 b8_bavard, INRIA *)
+(*
+    This file is part of mldonkey.
+
+    mldonkey 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.
+
+    mldonkey 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 mldonkey; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*)
+
+open CommonTypes
+
+type graph =
+  {
+   mutable quarter : (float * float) list;
+   mutable hour    : (float * float) list;
+   mutable halfday : (float * float) list;
+   mutable day     : (float * float) list;
+   mutable week    : (float * float) list;
+   mutable month   : (float * float) list;
+   mutable year    : (float * float) list;
+  }
+
+type graph_record =
+  GraphDownloads
+| GraphUploads
+| GraphFile of (uid_type * graph_record)
+
+type graph_time =
+  GraphQuarter
+| GraphHour
+| GraphHalfDay
+| GraphDay
+| GraphWeek
+| GraphMonth
+| GraphYear
diff -urN src/gtk2/gui/guiArt.ml src/gtk2/gui/guiArt.ml
--- src/gtk2/gui/guiArt.ml	2005-10-10 18:22:25.000000000 +0200
+++ src/gtk2/gui/guiArt.ml	2005-10-23 14:25:44.000000000 +0200
@@ -556,117 +556,3 @@
              lprintf' "   -----------------------\n   avail_bars : %d bars\n"  (List.length l)
            end
        end
-
-
-
-(*
-doesn't work ! pixbuf are freed as soon as they are rendered !!!!
-
-open Gui_types
-
-let dummy_bar =
-  {
-   finder = ("", "", true);
-   pixbuf = GdkPixbuf.create ~width:1 ~height:1 ();
-  }
-
-module H = Weak.Make(struct
-  type t = avail_bar
-  let hash s = Hashtbl.hash s.finder
-            
-  let equal x y = x.finder = y.finder
-end)
-
-let avail_bars = H.create 1023
-
-let f = (fun _ ->
-  Printf.printf "Finalising availability bar\n";
-  flush stdout
-  )
-
-let get_availability_of availability chunks is_file =
-  let height = 16 in
-  let nchunks = String.length chunks in
-  let avail =
-    if is_file
-      then begin
-        let s = String.copy availability in
-        normalize_availability s;
-        s
-      end else availability
-  in
-  let key = (avail, chunks, is_file) in
-  try
-    Printf.printf "Trying to use an existing availability bar\n";
-    flush stdout;
-    let bar = {dummy_bar with finder = (avail, chunks, is_file)} in
-    let pixb = H.find avail_bars bar in
-    pixb
-  with _ ->
-    begin
-      Printf.printf "Creating new availability bar\n";
-      flush stdout;
-      let pixmap = GDraw.pixmap ~width:nchunks ~height
-        ~colormap:(Gdk.Color.get_system_colormap ()) ()
-      in
-      (try
-        for i = 0 to (nchunks - 1) do
-          if is_file
-            then if chunks.[i] >= '2'
-              then pixmap#put_pixmap
-                     ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                     color_green#pixmap
-              else begin
-                let h = int_of_char (avail.[i]) in
-                if h = 0
-                  then if chunks.[i] = '0'
-                    then pixmap#put_pixmap
-                           ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                           color_red#pixmap
-                    else pixmap#put_pixmap
-                           ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                           color_orange#pixmap
-                  else begin
-                    let color_blue = !color_blue_relative.(!!O.gtk_misc_availability_max - h) in
-                    pixmap#put_pixmap
-                      ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                      color_blue#pixmap
-                  end
-              end
-            else if int_of_char avail.[i] >= 49
-              then if chunks.[i] >= '2'
-                then pixmap#put_pixmap
-                       ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                       color_black#pixmap
-                else pixmap#put_pixmap
-                       ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                       color_green#pixmap
-              else if chunks.[i] > '2'
-                then pixmap#put_pixmap
-                       ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                       color_orange#pixmap
-                else pixmap#put_pixmap
-                       ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                       color_red#pixmap
-          done;
-
-      with _ ->
-          begin
-            for i = 0 to nchunks do
-              pixmap#put_pixmap
-                ~x:i ~y:0 ~xsrc:0 ~ysrc:0 ~width:1 ~height:height
-                color_grey#pixmap
-            done
-          end);
-    let pb = GdkPixbuf.create ~width:nchunks ~height ~has_alpha:false () in
-    GdkPixbuf.get_from_drawable 
-       ~dest:pb ~dest_x:0 ~dest_y:0 
-       ~width:nchunks ~height ~src_x:0 ~src_y:0
-       ~colormap:(Gdk.Color.get_system_colormap ()) 
-       pixmap#pixmap;
-    let bar = {dummy_bar with finder = key; pixbuf = pb} in
-    Gc.finalise f bar;
-    H.add avail_bars bar;
-    bar
-  end
-*)
diff -urN src/gtk2/gui/guiConfig.ml src/gtk2/gui/guiConfig.ml
--- src/gtk2/gui/guiConfig.ml	2005-05-13 23:15:47.000000000 +0200
+++ src/gtk2/gui/guiConfig.ml	2005-10-16 12:34:47.000000000 +0200
@@ -61,7 +61,8 @@
       | "Filename" -> BFilename
       | "Path" -> BPath
       | "Password" -> BPassword
-      | "Time" -> BTime
+      | "GraphTime" -> BCombo
+      | "Time" -> BInt
       | "Color" -> BColor
       | "Font" -> BFont
       | "Int" -> BInt
@@ -185,6 +186,7 @@
 let nap_server_reg = Str.regexp_case_fold ".*napigator.*\\|.*server.*"
 let slsk_client_reg = Str.regexp_case_fold ".*client_.*\\|.*login.*\\|.*password.*"
 let slsk_sources_reg = Str.regexp_case_fold ".*token.*\\|.*clients.*\\|.*cache_.*"
+let mlgui_connection_reg = Str.regexp_case_fold ".*gtk_connection.*"
 let mlgui_client_reg = Str.regexp_case_fold ".*gtk_client_.*"
 let mlgui_look_reg = Str.regexp_case_fold ".*gtk_look_.*"
 let mlgui_color_reg = Str.regexp_case_fold ".*gtk_color_.*"
@@ -207,9 +209,10 @@
         [(Some display_reg, Some Display_conf); (Some look_reg, Some Look);
          (None, Some General_)];
    (Some Interfaces, Some Mlgui),
-        [(Some mlgui_client_reg, Some Client); (Some mlgui_look_reg, Some Look);
-         (Some mlgui_color_reg, Some Colors); (Some mlgui_font_reg, Some Fonts);
-         (Some mlgui_graph_reg, Some Graph); (None, Some Others)];
+        [(Some mlgui_connection_reg, Some Proxy); (Some mlgui_client_reg, Some Client);
+         (Some mlgui_look_reg, Some Look); (Some mlgui_color_reg, Some Colors);
+         (Some mlgui_font_reg, Some Fonts);(Some mlgui_graph_reg, Some Graph);
+         (None, Some Others)];
    (Some Tools, Some Startup),
         [(Some gui_reg, Some Gui); (None, Some User)];
    (Some Tools, Some Mail),
@@ -321,6 +324,15 @@
           | "gtk_client_lang" ->
               (p.pref_option_list <- U.languages;
                BCombo)
+          | "gtk_graph_time_downloads" ->
+              (p.pref_option_list <- ["quarter"; "hour"; "half_day"; "day"; "week"; "month"; "year"];
+               p.pref_type)
+          | "gtk_graph_time_uploads" ->
+              (p.pref_option_list <- ["quarter"; "hour"; "half_day"; "day"; "week"; "month"; "year"];
+               p.pref_type)
+          | "gtk_graph_time_file" ->
+              (p.pref_option_list <- ["quarter"; "hour"; "half_day"; "day"; "week"; "month"; "year"];
+               p.pref_type)
           | _ -> p.pref_type
       end
   | _ -> p.pref_type
@@ -381,6 +393,9 @@
             let _ =
               match o.M.option_name with
                   "client_name" -> G.client_name := o.M.option_value
+                | "max_hard_download_rate" -> G.max_hard_download_rate := int_of_float (safe_int o.M.option_value)
+                | "max_hard_upload_rate" -> G.max_hard_upload_rate := int_of_float (safe_int o.M.option_value)
+                | "update_gui_delay" -> G.update_gui_delay := int_of_float (1000. *. safe_float o.M.option_value)
                 | _ -> ()
             in
             try
diff -urN src/gtk2/gui/guiConsole.ml src/gtk2/gui/guiConsole.ml
--- src/gtk2/gui/guiConsole.ml	2005-03-02 20:45:11.000000000 +0100
+++ src/gtk2/gui/guiConsole.ml	2005-10-23 14:26:38.000000000 +0200
@@ -92,8 +92,6 @@
   ignore (text_buffer#connect#changed ~callback:
     (fun _ ->
        let nlines = text_buffer#line_count in
-       (* Printf.printf "Buffer modified %d\n" nlines;
-       flush stdout; *)
        if nlines > 1000
          then begin
            let stop = text_buffer#get_iter `END in
diff -urN src/gtk2/gui/guiDownloads.ml src/gtk2/gui/guiDownloads.ml
--- src/gtk2/gui/guiDownloads.ml	2005-05-13 23:15:47.000000000 +0200
+++ src/gtk2/gui/guiDownloads.ml	2005-10-31 13:24:43.000000000 +0100
@@ -22,6 +22,7 @@
 open GuiTypes2
 open GuiTypes
 open CommonTypes
+open GraphTypes
 
 open GuiTools
 open GuiColumns
@@ -84,13 +85,139 @@
      source_upload_rate = 0.;
      source_download_rate = 0.;
      source_upload = None;
-     source_has_upload = false;
+     source_has_upload = source_only;
      source_availability = [];
      source_files_requested = [];
   }
 
 (*************************************************************************)
 (*                                                                       *)
+(*                         is_file                                       *)
+(*                                                                       *)
+(*************************************************************************)
+
+let is_file key = not (String.contains key ':')
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         is_source                                     *)
+(*                                                                       *)
+(*************************************************************************)
+
+let is_source key = (snd (String2.cut_at key ':') <> "")
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         file_num                                      *)
+(*                                                                       *)
+(*************************************************************************)
+
+let file_num key =
+  if is_file key || is_source key
+  then begin
+    let t = fst (String2.cut_at key ':') in
+    int_of_string t
+  end else raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         source_num                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let source_num key =
+  if is_source key
+  then begin
+    let t = snd (String2.cut_at key ':') in
+    int_of_string t
+  end else raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         file_of_key                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let file_of_key key =
+  try
+    let num = file_num key in
+    Hashtbl.find G.files num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         source_of_key                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let source_of_key key =
+  try
+    let num = source_num key in
+    Hashtbl.find G.sources num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_files                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_files keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      if (is_file k) then
+        let f = file_of_key k in
+        if not (List.memq f !l) then
+          l := f :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_sources                               *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_sources keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = source_of_key k in
+      if not (List.memq s !l)
+        then l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         file_key                                      *)
+(*                                                                       *)
+(*************************************************************************)
+
+let file_key file_num =
+  Printf.sprintf "%d" file_num
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         source_key                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let source_key file_num source_num =
+  Printf.sprintf "%d:%d" file_num source_num
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         dummy_source_key                              *)
+(*                                                                       *)
+(*************************************************************************)
+
+let dummy_source_key file_num =
+  source_key file_num dummy_source.source_num
+
+(*************************************************************************)
+(*                                                                       *)
 (*                         Templates                                     *)
 (*                                                                       *)
 (*************************************************************************)
@@ -98,8 +225,8 @@
 let get_key =
   (fun i ->
      match i with
-         File f -> File_num f.file_num
-       | Source (s, file_num) -> Source_num (s.source_num, file_num)
+         File f -> (Printf.sprintf "%d" f.g_file_num)
+       | Source (s, file_num) -> (Printf.sprintf "%d:%d" file_num s.source_num)
   )
 
 module Downloads = GuiTemplates.Gview(struct
@@ -107,7 +234,6 @@
   module Column = GuiColumns.File
 
   type item = item_info
-  type key  = item_index
 
   let columns = O.downloads_columns
   let get_key = get_key
@@ -151,32 +277,31 @@
       match i with
           File f ->
             begin
-              store#set ~row ~column:download_network (Mi.network_name f.file_network);
-              store#set ~row ~column:download_network_pixb (Mi.network_pixb f.file_network ~size:A.SMALL ());
-              store#set ~row ~column:download_name (U.utf8_of f.file_name);
-              store#set ~row ~column:download_name_pixb (Mi.file_type_of_name f.file_name ~size:A.SMALL);
-              store#set ~row ~column:download_uid (Mi.uid_list_to_string f.file_uids);
-              store#set ~row ~column:download_size (Mi.size_of_int64 f.file_size);
-              store#set ~row ~column:download_downloaded (Mi.size_of_int64 f.file_downloaded);
-              store#set ~row ~column:download_percent (Mi.get_percent_of f.file_downloaded f.file_size);
-              store#set ~row ~column:download_sources (Printf.sprintf "(%d / %d)" f.file_active_sources f.file_all_sources);
-              store#set ~row ~column:download_state (Mi.string_of_file_state f.file_state f.file_download_rate);
-              let availability = Mi.main_availability_of f.file_network f.file_availability in
-              store#set ~row ~column:download_availability (Mi.string_of_availability availability f.file_chunks);
-              store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability f.file_chunks true);
-              store#set ~row ~column:download_download_rate (Mi.rate_to_string f.file_download_rate);
-              store#set ~row ~column:download_format (Mi.format_to_string f.file_format);
-              store#set ~row ~column:download_age (Mi.time_to_string f.file_age);
-              store#set ~row ~column:download_last_seen (Mi.time_to_string f.file_last_seen);
-              store#set ~row ~column:download_eta_inst (Mi.calc_eta_inst f.file_size f.file_downloaded f.file_download_rate);
-              store#set ~row ~column:download_eta_average (Mi.calc_eta_average f.file_size f.file_downloaded f.file_age);
-              store#set ~row ~column:download_priority (Mi.priority_to_string f.file_priority);
-              store#set ~row ~column:download_comment (U.utf8_of f.file_comment)
+              store#set ~row ~column:download_network (Mi.network_name f.g_file_network);
+              store#set ~row ~column:download_network_pixb (Mi.network_pixb f.g_file_network ~size:A.SMALL ());
+              store#set ~row ~column:download_name (U.utf8_of f.g_file_name);
+              store#set ~row ~column:download_name_pixb (Mi.file_type_of_name f.g_file_name ~size:A.SMALL);
+              store#set ~row ~column:download_uid (Mi.uid_list_to_string f.g_file_uids);
+              store#set ~row ~column:download_size (Mi.size_of_int64 f.g_file_size);
+              store#set ~row ~column:download_downloaded (Mi.size_of_int64 f.g_file_downloaded);
+              store#set ~row ~column:download_percent (Mi.get_percent_of f.g_file_downloaded f.g_file_size);
+              store#set ~row ~column:download_sources (Printf.sprintf "(%d / %d)" f.g_file_active_sources f.g_file_all_sources);
+              store#set ~row ~column:download_state (Mi.string_of_file_state f.g_file_state f.g_file_download_rate);
+              let availability = Mi.main_availability_of f.g_file_network f.g_file_availability in
+              store#set ~row ~column:download_availability (Mi.string_of_availability availability f.g_file_chunks);
+              store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability f.g_file_chunks true);
+              store#set ~row ~column:download_download_rate (Mi.rate_to_string f.g_file_download_rate);
+              store#set ~row ~column:download_format (Mi.format_to_string f.g_file_format);
+              store#set ~row ~column:download_age (Mi.time_to_string f.g_file_age);
+              store#set ~row ~column:download_last_seen (Mi.time_to_string f.g_file_last_seen);
+              store#set ~row ~column:download_eta_inst (Mi.calc_eta_inst f.g_file_size f.g_file_downloaded f.g_file_download_rate);
+              store#set ~row ~column:download_eta_average (Mi.calc_eta_average f.g_file_size f.g_file_downloaded f.g_file_age);
+              store#set ~row ~column:download_priority (Mi.priority_to_string f.g_file_priority);
+              store#set ~row ~column:download_comment (U.utf8_of f.g_file_comment)
             end
         | Source (s, file_num) ->
             try
-              let (_, i) = self#find_item (File_num file_num) in
-              let f = match i with File f -> f | _ -> raise Exit in
+              let f = Hashtbl.find G.files file_num in
               if s.source_num = (-1)
                 then begin
                   store#set ~row ~column:download_name s.source_name;
@@ -194,8 +319,8 @@
                   store#set ~row ~column:download_downloaded (Mi.size_of_int64 s.source_downloaded);
                   store#set ~row ~column:download_percent s.source_software;
                   store#set ~row ~column:download_state (Mi.string_of_state s.source_state file_num);
-                  store#set ~row ~column:download_availability (Mi.string_of_availability availability f.file_chunks);
-                  store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability f.file_chunks false);
+                  store#set ~row ~column:download_availability (Mi.string_of_availability availability f.g_file_chunks);
+                  store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability f.g_file_chunks false);
                   store#set ~row ~column:download_download_rate (Mi.rate_to_string s.source_download_rate);
                 end
             with _ -> ()
@@ -210,68 +335,67 @@
       match (i, i_new) with
           (File f, File f_new) ->
             begin
-              if f.file_comment <> f_new.file_comment
+              if f.g_file_comment <> f_new.g_file_comment
                 then begin
-                  store#set ~row ~column:download_comment (U.utf8_of f_new.file_comment)
+                  store#set ~row ~column:download_comment (U.utf8_of f_new.g_file_comment)
                 end;
-              if f.file_name <> f_new.file_name
+              if f.g_file_name <> f_new.g_file_name
                 then begin
-                  store#set ~row ~column:download_name (U.utf8_of f_new.file_name);
-                  if (Mi.extension_of f.file_name) <> (Mi.extension_of f_new.file_name)
+                  store#set ~row ~column:download_name (U.utf8_of f_new.g_file_name);
+                  if (Mi.extension_of f.g_file_name) <> (Mi.extension_of f_new.g_file_name)
                     then begin
-                      store#set ~row ~column:download_name_pixb (Mi.file_type_of_name f_new.file_name ~size:A.SMALL);
+                      store#set ~row ~column:download_name_pixb (Mi.file_type_of_name f_new.g_file_name ~size:A.SMALL);
                     end
                 end;
-              if f.file_uids <> f_new.file_uids
+              if f.g_file_uids <> f_new.g_file_uids
                 then begin
-                  store#set ~row ~column:download_uid (Mi.uid_list_to_string f.file_uids);
+                  store#set ~row ~column:download_uid (Mi.uid_list_to_string f.g_file_uids);
                 end;
-              if (f.file_size, f.file_downloaded, f.file_download_rate, f.file_age) <>
-                 (f_new.file_size, f_new.file_downloaded, f_new.file_download_rate, f_new.file_age)
+              if (f.g_file_size, f.g_file_downloaded, f.g_file_download_rate, f.g_file_age) <>
+                 (f_new.g_file_size, f_new.g_file_downloaded, f_new.g_file_download_rate, f_new.g_file_age)
                 then begin
-                  store#set ~row ~column:download_downloaded (Mi.size_of_int64 f_new.file_downloaded);
-                  store#set ~row ~column:download_percent (Mi.get_percent_of f_new.file_downloaded f_new.file_size);
-                  store#set ~row ~column:download_size (Mi.size_of_int64 f_new.file_size);
-                  store#set ~row ~column:download_age (Mi.time_to_string f_new.file_age);
-                  store#set ~row ~column:download_download_rate (Mi.rate_to_string f_new.file_download_rate);
-                  store#set ~row ~column:download_eta_inst (Mi.calc_eta_inst f_new.file_size f_new.file_downloaded
-                                                                             f_new.file_download_rate);
-                  store#set ~row ~column:download_eta_average (Mi.calc_eta_average f_new.file_size
-                                                                                   f_new.file_downloaded f_new.file_age);
+                  store#set ~row ~column:download_downloaded (Mi.size_of_int64 f_new.g_file_downloaded);
+                  store#set ~row ~column:download_percent (Mi.get_percent_of f_new.g_file_downloaded f_new.g_file_size);
+                  store#set ~row ~column:download_size (Mi.size_of_int64 f_new.g_file_size);
+                  store#set ~row ~column:download_age (Mi.time_to_string f_new.g_file_age);
+                  store#set ~row ~column:download_download_rate (Mi.rate_to_string f_new.g_file_download_rate);
+                  store#set ~row ~column:download_eta_inst (Mi.calc_eta_inst f_new.g_file_size f_new.g_file_downloaded
+                                                                             f_new.g_file_download_rate);
+                  store#set ~row ~column:download_eta_average (Mi.calc_eta_average f_new.g_file_size
+                                                                                   f_new.g_file_downloaded f_new.g_file_age);
                 end;
-              if (f.file_state, f.file_download_rate) <> (f_new.file_state, f_new.file_download_rate)
+              if (f.g_file_state, f.g_file_download_rate) <> (f_new.g_file_state, f_new.g_file_download_rate)
                 then begin
-                  store#set ~row ~column:download_state (Mi.string_of_file_state f_new.file_state f_new.file_download_rate);
+                  store#set ~row ~column:download_state (Mi.string_of_file_state f_new.g_file_state f_new.g_file_download_rate);
                 end;
-              let availability = Mi.main_availability_of f.file_network f.file_availability in
-              let availability_new = Mi.main_availability_of f_new.file_network f_new.file_availability in
-              if (availability, f.file_chunks) <> (availability_new, f_new.file_chunks)
+              let availability = Mi.main_availability_of f.g_file_network f.g_file_availability in
+              let availability_new = Mi.main_availability_of f_new.g_file_network f_new.g_file_availability in
+              if (availability, f.g_file_chunks) <> (availability_new, f_new.g_file_chunks)
                 then begin
-                  store#set ~row ~column:download_availability (Mi.string_of_availability availability_new f_new.file_chunks);
-                  store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability_new f_new.file_chunks true);
+                  store#set ~row ~column:download_availability (Mi.string_of_availability availability_new f_new.g_file_chunks);
+                  store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability_new f_new.g_file_chunks true);
                 end;
-              if f.file_format <> f_new.file_format
+              if f.g_file_format <> f_new.g_file_format
                 then begin
-                  store#set ~row ~column:download_format (Mi.format_to_string f_new.file_format);
+                  store#set ~row ~column:download_format (Mi.format_to_string f_new.g_file_format);
                 end;
-              if f.file_last_seen <> f_new.file_last_seen
+              if f.g_file_last_seen <> f_new.g_file_last_seen
                 then begin
-                  store#set ~row ~column:download_last_seen (Mi.time_to_string f_new.file_last_seen);
+                  store#set ~row ~column:download_last_seen (Mi.time_to_string f_new.g_file_last_seen);
                 end;
-              if f.file_priority <> f_new.file_priority
+              if f.g_file_priority <> f_new.g_file_priority
                 then begin
-                  store#set ~row ~column:download_priority (Mi.priority_to_string f_new.file_priority);
+                  store#set ~row ~column:download_priority (Mi.priority_to_string f_new.g_file_priority);
                 end;
-              if (f.file_active_sources, f.file_all_sources) <> (f_new.file_active_sources, f_new.file_all_sources)
+              if (f.g_file_active_sources, f.g_file_all_sources) <> (f_new.g_file_active_sources, f_new.g_file_all_sources)
                 then begin
-                  store#set ~row ~column:download_sources (Printf.sprintf "(%d / %d)" f_new.file_active_sources f_new.file_all_sources);
+                  store#set ~row ~column:download_sources (Printf.sprintf "(%d / %d)" f_new.g_file_active_sources f_new.g_file_all_sources);
                 end
             end
 
         | (Source (s, file_num), Source (s_new, file_num_new)) when file_num = file_num_new ->
            (try
-              let (_, i) = self#find_item (File_num file_num) in
-              let f = match i with File f -> f | _ -> raise Exit in
+              let f = Hashtbl.find G.files file_num in
               if s.source_num <> (-1)
                 then begin
                   if s.source_name <> s_new.source_name
@@ -306,8 +430,8 @@
                   in
                   if availability <> new_availability
                     then begin
-                      store#set ~row ~column:download_availability (Mi.string_of_availability new_availability f.file_chunks);
-                      store#set ~row ~column:download_availability_pixb (Mi.availability_bar new_availability f.file_chunks false)
+                      store#set ~row ~column:download_availability (Mi.string_of_availability new_availability f.g_file_chunks);
+                      store#set ~row ~column:download_availability_pixb (Mi.availability_bar new_availability f.g_file_chunks false)
                     end;
                   if s_new.source_download_rate <> s.source_download_rate
                     then begin
@@ -337,48 +461,43 @@
               let renderer = GTree.cell_renderer_text [`XALIGN 0.] in
               col#pack ~expand:false renderer;
               col#set_cell_data_func renderer
-               (fun model row ->
-                  match !view_context with
-                      Some context when col#width > 0 ->
-                        begin
-                          let width =
-                            if !!O.gtk_look_use_icons
-                              then (col#width - 4 - !!O.gtk_look_lists_icon_size) - 4 * !G.char_width
-                              else col#width - 4 * !G.char_width
-                          in
-                          let name = model#get ~row ~column:download_name in
-                          let s = GuiTools.fit_string_to_pixels name ~context ~pixels:width in
-                          renderer#set_properties [ `TEXT s ];
-                          let item = self#get_item row in
-                          begin
-                            match item with
-                                File f ->
-                                  renderer#set_properties [ `EDITABLE true ];
-                                  ignore (renderer#connect#edited ~callback:
-                                    (fun path name ->
-                                       try
-                                         let iter = self#get_iter path in
-                                         let item = self#get_item iter in
-                                         match item with
-                                             File ff ->
-                                               let child_row = self#convert_iter_to_child_iter iter in
-                                               store#set ~row:child_row ~column:download_name ff.file_name;
-                                               (match ff.file_state with
-                                                   FileDownloaded  -> GuiCom.send (SaveFile (ff.file_num, name))
-                                                 | _  ->  GuiCom.send (RenameFile (ff.file_num, name)))
-                                           | _ -> ()
-
-                                       with _ -> ()
+                (fun model row ->
+                   match !view_context with
+                       Some context when col#width > 0 ->
+                         begin
+                           let width =
+                             if !!O.gtk_look_use_icons
+                               then (col#width - 4 - !!O.gtk_look_lists_icon_size) - 4 * !G.char_width
+                               else col#width - 4 * !G.char_width
+                           in
+                           let name = model#get ~row ~column:download_name in
+                           let s = GuiTools.fit_string_to_pixels name ~context ~pixels:width in
+                           renderer#set_properties [ `TEXT s ];
+                           let key = self#find_model_key row in
+                           if (is_file key) then begin
+                             renderer#set_properties [ `EDITABLE true ];
+                             ignore (renderer#connect#edited ~callback:
+                               (fun path name ->
+                                  try
+                                    let iter = self#get_iter path in
+                                    let k = self#find_model_key iter in
+                                    if (is_file k) then begin
+                                      let file = file_of_key k in
+                                      (match file.g_file_state with
+                                           FileDownloaded  -> GuiCom.send (SaveFile (file.g_file_num, name))
+                                         | _  ->  GuiCom.send (RenameFile (file.g_file_num, name)));
+                                      let store_iter = self#convert_iter_to_child_iter iter in
+                                      store#set ~row:store_iter ~column:download_name file.g_file_name
+                                    end
+                                  with _ -> ()
                                   ))
 
-                              | _ -> renderer#set_properties [ `EDITABLE false ]
-
-                          end
-                        end
+                             end else renderer#set_properties [ `EDITABLE false ]
+                         end
 
                       | _ -> renderer#set_properties [ `TEXT "" ]
-              )
-           end
+                )
+            end
 
         | Col_file_size ->
             begin
@@ -529,74 +648,76 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method sort_items c i1 i2 =
-      match (i1, i2) with
-          (File f1, File f2) ->
-             begin
+    method sort_items c k1 k2 =
+      match (is_file k1, is_file k2) with
+          (true, true) ->
+             begin try
+               let f1 = file_of_key k1 in
+               let f2 = file_of_key k2 in
                match c with
-                   Col_file_name -> compare (String.lowercase f1.file_name) (String.lowercase f2.file_name)
-                 | Col_file_size -> compare f1.file_size f2.file_size
-                 | Col_file_downloaded -> compare f1.file_downloaded f2.file_downloaded
-                 | Col_file_percent -> compare (float_of_string (Mi.get_percent_of f1.file_downloaded f1.file_size))
-                                               (float_of_string (Mi.get_percent_of f2.file_downloaded f2.file_size))
-                 | Col_file_rate -> compare f1.file_download_rate f2.file_download_rate
-                 | Col_file_state -> compare (Mi.string_of_file_state f1.file_state f1.file_download_rate)
-                                             (Mi.string_of_file_state f2.file_state f2.file_download_rate)
+                   Col_file_name -> compare (String.lowercase f1.g_file_name) (String.lowercase f2.g_file_name)
+                 | Col_file_size -> compare f1.g_file_size f2.g_file_size
+                 | Col_file_downloaded -> compare f1.g_file_downloaded f2.g_file_downloaded
+                 | Col_file_percent -> compare (float_of_string (Mi.get_percent_of f1.g_file_downloaded f1.g_file_size))
+                                               (float_of_string (Mi.get_percent_of f2.g_file_downloaded f2.g_file_size))
+                 | Col_file_rate -> compare f1.g_file_download_rate f2.g_file_download_rate
+                 | Col_file_state -> compare (Mi.string_of_file_state f1.g_file_state f1.g_file_download_rate)
+                                             (Mi.string_of_file_state f2.g_file_state f2.g_file_download_rate)
                  | Col_file_availability ->
                      begin
-                       let av1 = Mi.main_availability_of f1.file_network f1.file_availability in
-                       let av2 = Mi.main_availability_of f2.file_network f2.file_availability in
+                       let av1 = Mi.main_availability_of f1.g_file_network f1.g_file_availability in
+                       let av2 = Mi.main_availability_of f2.g_file_network f2.g_file_availability in
                        if !!O.gtk_look_graphical_availability
                          then begin
-                           Mi.sort_availability_bar (av1, f1.file_chunks) (av2, f2.file_chunks) true
+                           Mi.sort_availability_bar (av1, f1.g_file_chunks) (av2, f2.g_file_chunks) true
                          end else begin
-                           let s1 = Mi.string_of_availability av1 f1.file_chunks in
-                           let s2 = Mi.string_of_availability av2 f2.file_chunks in
+                           let s1 = Mi.string_of_availability av1 f1.g_file_chunks in
+                           let s2 = Mi.string_of_availability av2 f2.g_file_chunks in
                            compare (float_of_string s1) (float_of_string s2)
                          end
                      end
-                 | Col_file_uid -> compare (Mi.uid_list_to_string f1.file_uids) (Mi.uid_list_to_string f2.file_uids)
-                 | Col_file_format -> compare f1.file_format f2.file_format
-                 | Col_file_network -> compare f1.file_network f2.file_network
-                 | Col_file_age -> compare f1.file_age f2.file_age
-                 | Col_file_last_seen -> compare f1.file_last_seen f2.file_last_seen
-                 | Col_file_eta -> compare (Mi.calc_eta_inst f1.file_size f1.file_downloaded f1.file_download_rate)
-                                           (Mi.calc_eta_inst f2.file_size f2.file_downloaded f2.file_download_rate)
-                 | Col_file_priority -> compare f1.file_priority f2.file_priority
-                 | Col_file_comment -> compare f1.file_comment f2.file_comment
+                 | Col_file_uid -> compare (Mi.uid_list_to_string f1.g_file_uids) (Mi.uid_list_to_string f2.g_file_uids)
+                 | Col_file_format -> compare f1.g_file_format f2.g_file_format
+                 | Col_file_network -> compare f1.g_file_network f2.g_file_network
+                 | Col_file_age -> compare f1.g_file_age f2.g_file_age
+                 | Col_file_last_seen -> compare f1.g_file_last_seen f2.g_file_last_seen
+                 | Col_file_eta -> compare (Mi.calc_eta_inst f1.g_file_size f1.g_file_downloaded f1.g_file_download_rate)
+                                           (Mi.calc_eta_inst f2.g_file_size f2.g_file_downloaded f2.g_file_download_rate)
+                 | Col_file_priority -> compare f1.g_file_priority f2.g_file_priority
+                 | Col_file_comment -> compare f1.g_file_comment f2.g_file_comment
                  | Col_file_sources ->
                      begin
-                       let i = compare f1.file_active_sources f2.file_active_sources in
+                       let i = compare f1.g_file_active_sources f2.g_file_active_sources in
                        if i = 0
-                         then compare f1.file_all_sources f2.file_all_sources
+                         then compare f1.g_file_all_sources f2.g_file_all_sources
                          else i
                      end
-             end
+             with _ -> 0 end
 
-        | (Source (s1, file_num_a), Source (s2, file_num_b)) when file_num_a = file_num_b ->
-             begin
+        | (false, false) when (try file_num k1 = file_num k2 with _ -> false) ->
+             begin try
+               let s1 = source_of_key k1 in
+               let s2 = source_of_key k2 in
+               let file_num = file_num k1 in
                match c with
                    Col_file_name -> compare (String.lowercase s1.source_name)
                                             (String.lowercase s2.source_name)
                  | Col_file_downloaded -> compare s1.source_downloaded s2.source_downloaded
                  | Col_file_percent -> compare s1.source_software s2.source_software
-                 | Col_file_state -> compare (Mi.string_of_state s1.source_state file_num_a)
-                                             (Mi.string_of_state s2.source_state file_num_a)
+                 | Col_file_state -> compare (Mi.string_of_state s1.source_state file_num)
+                                             (Mi.string_of_state s2.source_state file_num)
                  | Col_file_availability ->
                      begin
                        try
-                         let (_, i1) = self#find_item (File_num file_num_a) in
-                         let (_ ,i2) = self#find_item (File_num file_num_b) in
-                         let f1 = match i1 with File f -> f | _ -> raise Exit in
-                         let f2 = match i2 with File f -> f | _ -> raise Exit in
-                         let av_s1 = try List.assoc file_num_a s1.source_availability with _ -> "" in
-                         let av_s2 = try List.assoc file_num_a s2.source_availability with _ -> "" in
+                         let f = Hashtbl.find G.files file_num in
+                         let av_s1 = try List.assoc file_num s1.source_availability with _ -> "" in
+                         let av_s2 = try List.assoc file_num s2.source_availability with _ -> "" in
                          if !!O.gtk_look_graphical_availability
                            then begin
-                             Mi.sort_availability_bar (av_s1, f1.file_chunks) (av_s2, f2.file_chunks) false
+                             Mi.sort_availability_bar (av_s1, f.g_file_chunks) (av_s2, f.g_file_chunks) false
                            end else begin
-                             let av1 = Mi.string_of_availability av_s1 f1.file_chunks in
-                             let av2 = Mi.string_of_availability av_s2 f2.file_chunks in
+                             let av1 = Mi.string_of_availability av_s1 f.g_file_chunks in
+                             let av2 = Mi.string_of_availability av_s2 f.g_file_chunks in
                              compare (float_of_string av1) (float_of_string av2)
                            end
                        with _ -> 0
@@ -604,7 +725,7 @@
                  | Col_file_network -> compare s1.source_network s2.source_network
                  | Col_file_rate -> compare s1.source_download_rate s2.source_download_rate
                  | _ -> 0
-             end
+             with _ -> 0 end
 
         | _ -> 0
 
@@ -615,20 +736,18 @@
 (*************************************************************************)
 
     method force_update_icons () =
-      List.iter (fun i ->
+      List.iter (fun k ->
         try
-          let (row, i_) = self#find_item (get_key i) in 
-          match i with
-              File f ->
-                 begin
-                   store#set ~row ~column:download_network_pixb (Mi.network_pixb f.file_network ~size:A.SMALL ());
-                   store#set ~row ~column:download_name_pixb (Mi.file_type_of_name f.file_name ~size:A.SMALL);
-                 end
-            | Source (s, file_num) ->
-                begin
-                  store#set ~row ~column:download_network_pixb (Mi.network_pixb s.source_network ~size:A.SMALL ());
-                  store#set ~row ~column:download_name_pixb (Mi.source_type_to_icon s.source_type ~size:A.SMALL);
-                end
+          let row = self#find_row k in
+          if (is_file k) then begin
+            let f = file_of_key k in
+            store#set ~row ~column:download_network_pixb (Mi.network_pixb f.g_file_network ~size:A.SMALL ());
+            store#set ~row ~column:download_name_pixb (Mi.file_type_of_name f.g_file_name ~size:A.SMALL);
+          end else begin
+            let s = source_of_key k in
+            store#set ~row ~column:download_network_pixb (Mi.network_pixb s.source_network ~size:A.SMALL ());
+            store#set ~row ~column:download_name_pixb (Mi.source_type_to_icon s.source_type ~size:A.SMALL);
+          end
         with _ -> ()
       ) (self#all_items ())
 
@@ -639,29 +758,24 @@
 (*************************************************************************)
 
     method force_update_avail_bars () =
-      List.iter (fun i ->
+      List.iter (fun k ->
         try
-          let (row, _) = self#find_item (get_key i) in
+          let row = self#find_row k in
           if !!O.gtk_look_graphical_availability
-            then match i with
-                     File f ->
-                       begin
-                         let availability = Mi.main_availability_of f.file_network f.file_availability in
-                         store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability f.file_chunks true);
-                       end
-                   | Source (s, file_num) ->
-                       begin
-                         let (_, i') = self#find_item (File_num file_num) in
-                         let f = match i' with File f -> f | _ -> raise Exit in
-                         let availability =
-                           try
-                             List.assoc file_num s.source_availability
-                           with _ -> ""
-                         in
-                         store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability f.file_chunks false);
-                       end
-
-            else store#set ~row ~column:download_availability_pixb None
+            then if (is_file k) then begin
+               let f = file_of_key k in
+               let availability = Mi.main_availability_of f.g_file_network f.g_file_availability in
+               store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability f.g_file_chunks true);
+            end else begin
+               let f = file_of_key k in
+               let s = source_of_key k in
+               let availability =
+                 try
+                   List.assoc (file_num k) s.source_availability
+                 with _ -> ""
+               in
+               store#set ~row ~column:download_availability_pixb (Mi.availability_bar availability f.g_file_chunks false);
+            end else store#set ~row ~column:download_availability_pixb None
         with _ -> ()
       ) (self#all_items ())
 
@@ -684,65 +798,73 @@
           entry#set_text ""
         end
 
-let preview f () = GuiCom.send (Preview f.file_num)
+let preview k () = try GuiCom.send (Preview (file_num k)) with _ -> ()
 
-let show_details item () = GuiInfoWindow.window item ()
+let show_details k () =
+  try
+    let item =
+      if is_file k
+      then File (file_of_key k)
+      else Source ((source_of_key k), file_num k)
+    in GuiInfoWindow.window item ()
+  with _ -> ()
 
 let pause_resume sel () =
-  List.iter (fun item ->
-    match item with
-        File f ->
-           GuiCom.send (SwitchDownload (f.file_num,
-              match f.file_state with
-                FilePaused | FileAborted _ -> true
-              | _ -> false
-           ))
-      | _ -> ()
+  List.iter (fun k ->
+    if is_file k
+    then try
+      let f = file_of_key k in
+      let state =
+        match f.g_file_state with
+            FilePaused | FileAborted _ -> true
+          | _ -> false
+      in
+      GuiCom.send (SwitchDownload (f.g_file_num, state))
+    with _ -> ()
   ) sel
 
 let retry_connect sel () =
-  List.iter (fun item ->
-    match item with
-        File f ->
-           GuiCom.send (ConnectAll f.file_num)
-      | _ -> ()
+  List.iter (fun k ->
+    if is_file k
+    then try
+      GuiCom.send (ConnectAll (file_num k))
+    with _ -> ()
   ) sel
 
 let cancel sel () =
   let warning_message = !M.dT_lb_ask_cancel_download_files in
   let l = ref [] in
-  List.iter (fun item ->
-    match item with
-        File f ->
-          l := f.file_name :: !l
-      | _ -> ()
+  List.iter (fun k ->
+    if is_file k
+    then try
+      let f = file_of_key k in
+      l := f.g_file_name :: !l
+    with _ -> ()
   ) sel;
   let on_ok () =
-    List.iter (fun item ->
-      match item with
-          File f ->
-            (* Printf.printf "ASK to Cancel file : %d\n" f.file_num;
-            flush stdout; *)
-            GuiCom.send (RemoveDownload_query f.file_num)
-        | _ -> ()
+    List.iter (fun k ->
+    if is_file k
+    then try
+      GuiCom.send (RemoveDownload_query (file_num k))
+    with _ -> ()
     ) sel
   in
   GuiTools.warning_box ~warning_message ~text:!l ~on_ok ()
 
 let verify_chunks sel () =
-  List.iter (fun item ->
-    match item with
-        File f ->
-           GuiCom.send (VerifyAllChunks f.file_num)
-      | _ -> ()
+  List.iter (fun k ->
+    if is_file k
+    then try
+      GuiCom.send (VerifyAllChunks (file_num k))
+    with _ -> ()
   ) sel
 
 let set_priority sel prio () =
-  List.iter (fun item ->
-    match item with
-        File f ->
-           GuiCom.send (SetFilePriority (f.file_num, prio))
-      | _ -> ()
+  List.iter (fun k ->
+    if is_file k
+    then try
+      GuiCom.send (SetFilePriority (file_num k, prio))
+    with _ -> ()
   ) sel
 
 let preference label v box_type opt_list =
@@ -766,7 +888,7 @@
   }
 
 let edit_mp3_tags f () =
-  match f.file_format with
+  match f.g_file_format with
       MP3 (tag,_) ->
         begin
           let module C = ConfigWindow in
@@ -787,7 +909,7 @@
             tag.P.comment <- comment.C.pref_new_value;
             tag.P.tracknum <- int_of_float (C.safe_int tracknum.C.pref_new_value);
             tag.P.genre <- Mp3_misc.genre_of_string genre.C.pref_new_value;
-            GuiCom.send (ModifyMp3Tags (f.file_num, tag))
+            GuiCom.send (ModifyMp3Tags (f.g_file_num, tag))
           in
           C.simple_panel
               ~prefs:[title;artist;album;year;comment;tracknum;genre]
@@ -795,42 +917,32 @@
               ~icon:(A.get_icon ~icon:M.icon_menu_search_mp3 ~size:A.SMALL ())
               ~on_ok ()
         end
-
     | _ -> ()
 
-let save_all () =
-  List.iter (fun i ->
-    match i with
-        File f ->
-          (match f.file_state with
-              FileDownloaded -> 
-                GuiCom.send (SaveFile (f.file_num, f.file_name))
-            | _ -> ())
 
-      | _ -> ()
+let get_format sel () =
+  List.iter (fun k ->
+    if is_file k then try
+      GuiCom.send (QueryFormat (file_num k))
+    with _ -> ()
+  ) sel
 
+let save_all () =
+  List.iter (fun k ->
+    if is_file k
+    then try
+      let f = file_of_key k in
+      match f.g_file_state with
+          FileDownloaded -> GuiCom.send (SaveFile (f.g_file_num, f.g_file_name))
+        | _ -> ()
+    with _ -> ()
   ) (downloadstore#all_items ())
 
-(*
-let save_as f () =
-  let file_opt = GToolbox.input_string ~title:(!M.dT_wt_save_as)
-    ~text:(U.utf8_of f.file_name)
-    (!M.dT_lb_save_as)
-  in
-  match file_opt with
-    None -> ()
-  | Some name -> 
-      match f.file_state with
-           FileDownloaded  -> GuiCom.send (SaveFile (f.file_num, name))
-        | _  ->  GuiCom.send (RenameFile (f.file_num, name))
-*)
-
 let add_to_friends sel () =
-  List.iter (fun item ->
-    match item with
-        Source (s, _) ->
-           GuiCom.send (AddClientFriend s.source_num)
-      | _ -> ()
+  List.iter (fun k ->
+    if is_source k then try
+      GuiCom.send (AddClientFriend (source_num k))
+    with _ -> ()
   ) sel
 
 let browse_files = add_to_friends
@@ -849,13 +961,12 @@
       GuiCom.send (InterestedInSources true)
     end
 
-let get_format sel () =
-  List.iter (fun item ->
-    match item with
-        File f ->
-           GuiCom.send (QueryFormat f.file_num)
-      | _ -> ()
-  ) sel
+let update_downloading_files () =
+  let keys = downloadstore#all_items () in
+  let files = keys_to_files keys in
+  List.iter (fun f ->
+    GuiCom.send (GetFile_info f.g_file_num)
+  ) files
 
 (*************************************************************************)
 (*                                                                       *)
@@ -866,12 +977,12 @@
 let download_menu sel =
     match sel with
         [] -> []
-      | (File f) :: tail ->
+      | k :: tail when (is_file k) ->
           (if tail = []
              then
                [
-                `I ((!M.dT_me_show_file_details), show_details (File f)) ;
-                `I ((!M.dT_me_preview), preview f) ;
+                `I ((!M.dT_me_show_file_details), show_details k) ;
+                `I ((!M.dT_me_preview), preview k) ;
                 `S ;
                ]
              else  [])
@@ -890,13 +1001,14 @@
                      ]) ::
                 `S ::
                 `I ((!M.dT_me_get_format), get_format sel) ::
-             (match (f.file_state, f.file_format) with
+             (try let f = file_of_key k in
+              match (f.g_file_state, f.g_file_format) with
                   (FileDownloaded, MP3 _) ->
                [
                 `I ((!M.dT_me_edit_mp3), edit_mp3_tags f)
                ]
                 | _ ->
-               [])
+               [] with _ -> [])
              @
                 `I ((!M.dT_me_save_all), save_all) :: [] (*
                 (if tail = [] then
@@ -905,14 +1017,14 @@
                ]
             else  []) *)
 
-      | (Source (s, file_num)) :: tail ->
-          if s.source_num = (-1)
+      | k :: tail when (is_source k) ->
+          if (source_num k) = (-1)
             then []
             else
               (if tail = []
                  then
                    [
-                    `I ((!M.dT_me_show_source_details), show_details (Source (s, file_num))) ;
+                    `I ((!M.dT_me_show_source_details), show_details k) ;
                    ]
                  else  [])
               @
@@ -920,6 +1032,7 @@
                     `I (!M.dT_me_browse_files, browse_files sel);
                     `I (!M.dT_me_add_to_friends, add_to_friends sel)
                    ]
+      | _ -> []
 
 (*************************************************************************)
 (*                                                                       *)
@@ -927,12 +1040,20 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let filter_download i = 
-  match i with
-      File f -> not (List.memq f.file_network !G.networks_filtered)
-    | Source (s, _) -> not (List.memq s.source_network !G.networks_filtered)(*  &&
-                       s.source_state <> NewHost &&
-                       (s.source_name <> "" && s.source_software <> "unk") *)
+let filter_download k = 
+  if is_file k
+  then begin
+    try
+      let f = file_of_key k in
+      not (List.memq f.g_file_network !G.networks_filtered)
+    with _ -> true
+  end else begin
+    try
+      let s = source_of_key k in
+      not (List.memq s.source_network !G.networks_filtered)
+(*    && s.source_state <> NewHost && (s.source_name <> "" && s.source_software <> "unk") *)
+    with _ -> true
+  end
 
 (*************************************************************************)
 (*                                                                       *)
@@ -953,29 +1074,28 @@
   if !!O.gtk_look_graphical_availability
     then begin
       let list = ref [] in
-      List.iter (fun i ->
-        match i with
-            File f ->
-              begin
-                let availability = Mi.main_availability_of f.file_network f.file_availability in
-                let key = (availability, f.file_chunks, true) in
-                if not (List.mem key !list)
-                  then begin list := key :: !list end;
-                match f.file_sources with
-                    None -> ()
-                  | Some sources ->
-                      List.iter (fun source_num ->
-                        let s = Hashtbl.find G.sources source_num in
-                        List.iter (fun (_, avail) ->
-                          let key = (avail, f.file_chunks, false) in
-                          if not (List.mem key !list)
-                            then list := key :: !list
-                        ) s.source_availability;
-                      ) sources
-              end
-
-          | _ -> ()
-
+      List.iter (fun k ->
+        if is_file k
+          then begin
+            try
+              let f = file_of_key k in
+              let availability = Mi.main_availability_of f.g_file_network f.g_file_availability in
+              let key = (availability, f.g_file_chunks, true) in
+              if not (List.mem key !list)
+                then begin list := key :: !list end;
+              match f.g_file_sources with
+                  None -> ()
+                | Some sources ->
+                    List.iter (fun source_num ->
+                      let s = Hashtbl.find G.sources source_num in
+                      List.iter (fun (_, avail) ->
+                        let key = (avail, f.g_file_chunks, false) in
+                        if not (List.mem key !list)
+                          then list := key :: !list
+                      ) s.source_availability;
+                    ) sources
+           with _ -> ()
+          end
       ) (downloadstore#all_items ());
       A.clean_avail_bars !list
     end
@@ -1007,51 +1127,88 @@
 (*                                                                       *)
 (*************************************************************************)
 
+let all_files_razorback2_stats () =
+  let l = List.filter (fun k -> is_file k) (downloadstore#all_items ()) in
+  List.iter (fun k ->
+    try
+      let file = file_of_key k in
+      GuiHtml.get_razorback2_stats file
+    with _ -> ()
+  ) l
+
+let hashtbl_update f f_new =
+  f.g_file_comment <- f_new.g_file_comment;
+  f.g_file_name <- f_new.g_file_name;
+  f.g_file_names <- f_new.g_file_names;
+  f.g_file_size <- f_new.g_file_size;
+  f.g_file_downloaded <- f_new.g_file_downloaded;
+  f.g_file_active_sources <- f_new.g_file_active_sources;
+  f.g_file_all_sources <- f_new.g_file_all_sources;
+  f.g_file_state <- f_new.g_file_state;
+  f.g_file_chunks <- f_new.g_file_chunks;
+  f.g_file_availability <- f_new.g_file_availability;
+  f.g_file_sources <- f_new.g_file_sources;
+  f.g_file_download_rate <- f_new.g_file_download_rate;
+  f.g_file_format <- f_new.g_file_format;
+  f.g_file_chunks_age <- f_new.g_file_chunks_age;
+  f.g_file_age <- f_new.g_file_age;
+  f.g_file_last_seen <- f_new.g_file_last_seen;
+  f.g_file_priority <- f_new.g_file_priority;
+  f.g_file_uids <- f_new.g_file_uids
+
 let remove_dummy_source file_num =
-  downloadstore#remove_item (Source (dummy_source, file_num))
+  downloadstore#remove_item (dummy_source_key file_num)
 
 let add_dummy_source parent file_num =
   try
-    let _ = downloadstore#find_item (Source_num ((-1), file_num)) in
+    let _ = downloadstore#find_row (dummy_source_key file_num) in
     ()
   with _ ->
     ignore (downloadstore#add_item (Source (dummy_source, file_num)) ~parent ())
 
 let h_cancelled file_num =
   try
-    let (_, i) = downloadstore#find_item (File_num file_num) in
-    match i with
-        File f ->
-          begin
-            downloadstore#remove_item (File f);
-            decr G.ndownloads;
-            (if f.file_state = FileDownloaded
-               then decr G.ndownloaded);
-            GuiStatusBar.update_downloadedfiles ()
-          end
-      | _ -> ()
+    let fi = Hashtbl.find G.files file_num in
+    downloadstore#remove_item (file_key file_num);
+    Hashtbl.remove G.files file_num;
+    decr G.ndownloads;
+    (if fi.g_file_state = FileDownloaded
+       then decr G.ndownloaded);
+    GuiStatusBar.update_downloadedfiles ();
+    let file_uid = Mi.to_uid_type fi.g_file_uids in
+    GuiGraphBase.cancel_file file_uid;
+    Hashtbl.remove G.file_by_uid file_uid;
   with _ -> ()
 
 let h_paused f =
+  let file_uid = Mi.to_uid_type f.g_file_uids in
   try
-    let (row, i) = downloadstore#find_item (File_num f.file_num) in
-    match i with
-        File fi ->
-          begin
-            (if f.file_state = FileDownloaded && f.file_state <> fi.file_state
-               then incr G.ndownloaded);
-            let f_new = {f with file_sources = fi.file_sources} in
-            downloadstore#update_item row (File fi) (File f_new)
-          end
-        | _ -> ()
+    let fi = Hashtbl.find G.files f.g_file_num in
+    let row = downloadstore#find_row (file_key f.g_file_num) in
+    (if f.g_file_state = FileDownloaded && f.g_file_state <> fi.g_file_state
+       then incr G.ndownloaded);
+    let f_new = {f with g_file_sources = fi.g_file_sources} in
+    downloadstore#update_item row (File fi) (File f_new);
+    hashtbl_update fi f_new;
+    let rate = int_of_float f.g_file_download_rate in
+    GuiGraphBase.save_record rate (GraphFile (file_uid, GraphDownloads));
+    if f.g_file_name <> fi.g_file_name
+      then Hashtbl.replace G.file_by_uid (file_uid) (U.utf8_of f.g_file_name);
   with _ ->
     begin
       let parent = downloadstore#add_item (File f) () in
       incr G.ndownloads;
-      (if f.file_state = FileDownloaded
+      (if f.g_file_state = FileDownloaded
          then incr G.ndownloaded);
       GuiStatusBar.update_downloadedfiles ();
-      add_dummy_source parent f.file_num
+      add_dummy_source parent f.g_file_num;
+      Hashtbl.add G.files f.g_file_num f;
+      GuiGraphBase.add_file file_uid;
+      Hashtbl.add G.file_by_uid file_uid (U.utf8_of f.g_file_name);
+      if !!O.gtk_misc_display_razorback_stats
+        then begin
+          try GuiHtml.get_razorback2_stats f with _ -> ()
+        end
     end
 
 let h_downloading = h_paused
@@ -1062,32 +1219,33 @@
 
 
 let file_info f =
-  match f.file_state with
+  match f.g_file_state with
       FileNew -> assert false
     | FileCancelled -> 
-        h_cancelled f.file_num
+        h_cancelled f.g_file_num
     | FileDownloaded ->
         h_downloaded f
     | FileShared ->
-        h_removed f.file_num
+        h_removed f.g_file_num
     | FilePaused | FileQueued | FileAborted _ -> 
         h_paused f
     | FileDownloading ->
         h_downloading f
 
-let file_downloaded (num, downloaded, rate, last_seen) =
+let file_downloaded (file_num, downloaded, rate, last_seen) =
   try
-    let (row, i) = downloadstore#find_item (File_num num) in
-    match i with
-        File f ->
-          begin
-            let f_new = {f with file_downloaded = downloaded;
-                                file_download_rate = rate;
-                                file_last_seen = last_seen}
-            in
-            downloadstore#update_item row (File f) (File f_new)
-          end
-      | _ -> ()
+    let f = Hashtbl.find G.files file_num in
+    let row = downloadstore#find_row (file_key file_num) in
+    let f_new = {f with g_file_downloaded = downloaded;
+                        g_file_download_rate = rate;
+                        g_file_last_seen = last_seen}
+    in
+    downloadstore#update_item row (File f) (File f_new);
+    f.g_file_downloaded <- f_new.g_file_downloaded;
+    f.g_file_download_rate <- f_new.g_file_download_rate;
+    f.g_file_last_seen <- f_new.g_file_last_seen;
+    let rate = int_of_float f.g_file_download_rate in
+    GuiGraphBase.save_record rate (GraphFile ((Mi.to_uid_type f.g_file_uids), GraphDownloads));
   with _ -> ()
 
 (* File_add_source and File_update_availability could be merged now that we use
@@ -1095,64 +1253,53 @@
  *)
 let h_add_source s file_num =
   try
-    let (row, i) = downloadstore#find_item (File_num file_num) in
-    match i with
-        File f ->
+    let f = Hashtbl.find G.files file_num in
+    let parent = downloadstore#find_row (file_key file_num) in
+    match f.g_file_sources with
+        None ->
           begin
-            match f.file_sources with
-                None ->
-                  begin
-                    f.file_sources <- Some [s.source_num];
-                    ignore (downloadstore#add_item (Source (s, file_num)) ~parent:row ());
-                    remove_dummy_source file_num
-                  end
-              | Some sources ->
-                  begin
-                    if List.mem s.source_num sources
-                      then raise Exit
-                      else begin
-                        f.file_sources <- Some (s.source_num :: sources);
-                        ignore (downloadstore#add_item (Source (s, file_num)) ~parent:row ());
-                      end
-                  end
+            f.g_file_sources <- Some [s.source_num];
+            ignore (downloadstore#add_item (Source (s, file_num)) ~parent ());
+            remove_dummy_source file_num
+          end
+      | Some sources ->
+          begin
+            if List.mem s.source_num sources
+              then raise Exit
+              else begin
+                f.g_file_sources <- Some (s.source_num :: sources);
+                ignore (downloadstore#add_item (Source (s, file_num)) ~parent ());
+              end
           end
-      | _ -> ()
   with _ -> ()
 
 let h_remove_source s file_num =
   try
-    let (row, i) = downloadstore#find_item (File_num file_num) in
-    match i with
-        File f ->
-          begin
-            let _ =
-              match f.file_sources with
-                  None -> add_dummy_source row file_num
-                | Some sources ->
-                    begin
-                      f.file_sources <- Some (List.filter (fun num -> num <> s.source_num) sources)
-                    end
-            in
-            (match f.file_sources with
-                Some [] ->
-                  begin
-                    f.file_sources <- None;
-                    add_dummy_source row file_num;
-                  end
-              | _ -> ());
-            downloadstore#remove_item (Source (s, file_num));
-          end
-      | _ -> ()
+    let f = Hashtbl.find G.files file_num in
+    let parent = downloadstore#find_row (file_key file_num) in
+    let _ =
+      match f.g_file_sources with
+          None -> add_dummy_source parent file_num
+        | Some sources ->
+            begin
+              f.g_file_sources <- Some (List.filter (fun num -> num <> s.source_num) sources)
+            end
+    in
+    (match f.g_file_sources with
+         Some [] ->
+           begin
+             f.g_file_sources <- None;
+             add_dummy_source parent file_num;
+           end
+       | _ -> ());
+    downloadstore#remove_item (source_key file_num s.source_num);
   with _ -> ()
 
-let h_update_source s_new =
+let h_update_source s s_new =
   List.iter (fun file_num ->
     try
-      let (row, i) = downloadstore#find_item (Source_num (s_new.source_num, file_num)) in
-      match i with
-          Source (s, num) ->
-            downloadstore#update_item row (Source (s, num)) (Source (s_new, num))
-        | _ -> ()
+      let row = downloadstore#find_row (source_key file_num s_new.source_num) in
+      downloadstore#update_item row (Source (s, file_num)) (Source (s_new, file_num))
     with _ -> ()
   ) s_new.source_files_requested;
   match s_new.source_state with
@@ -1161,13 +1308,10 @@
           then downloaders := s_new.source_num :: !downloaders
     | _ -> ()
 
-let h_update_source_availability s_new file_num =
+let h_update_source_availability s s_new file_num =
    try
-     let (row, i) = downloadstore#find_item (Source_num (s_new.source_num, file_num)) in
-     match i with
-         Source (s, num) ->
-           downloadstore#update_item row (Source (s, num)) (Source (s_new, num))
-       | _ -> ()
+     let row = downloadstore#find_row (source_key file_num s_new.source_num) in
+     downloadstore#update_item row (Source (s, file_num)) (Source (s_new, file_num))
    with _ -> ()
 
 let clean_sources_table s_old =
@@ -1184,6 +1328,8 @@
 open GMain
 
 let downloads_box gui =
+  update_downloaders ();
+  update_downloading_files ();
   let vbox = GPack.vbox ~homogeneous:false ~border_width:6 () in
   ignore (vbox#connect#destroy ~callback:
     (fun _ ->
@@ -1210,17 +1356,71 @@
     Downloads.treeview ~mode:`MULTIPLE
       ~packing:(vbox#pack ~expand:true ~fill:true) ()
   in
-  let on_expand_item path (i : GuiTypes2.item_info) =
+  if !!O.gtk_misc_display_razorback_stats
+    then begin
+      let hbox_razorback_stats =
+        GPack.hbox ~homogeneous:false ~border_width:6
+          ~spacing:12
+          ~packing:(vbox#pack ~expand:false ~fill:true) ()
+      in
+      let razorback_history =
+        GMisc.image ~packing:(hbox_razorback_stats#pack ~expand:false ~fill:true) ()
+      in
+      let vbox_razorback_stats =
+        GPack.vbox ~homogeneous:false ~border_width:6
+          ~spacing:12
+          ~packing:(hbox_razorback_stats#pack ~expand:false ~fill:true) ()
+      in
+      let label_rating =
+        GMisc.label ~xalign:0. ~yalign:0.
+        ~packing:(vbox_razorback_stats#pack ~expand:false ~fill:true) ()
+      in
+      let label_availability =
+        GMisc.label ~xalign:0. ~yalign:0.
+        ~packing:(vbox_razorback_stats#pack ~expand:false ~fill:true) ()
+      in
+      let label_completed =
+        GMisc.label ~xalign:0. ~yalign:0.
+        ~packing:(vbox_razorback_stats#pack ~expand:false ~fill:true) ()
+      in
+      let on_select_files keys =
+        match keys with
+            [] -> ()
+          | k :: _ when (is_file k) ->
+              begin
+                try
+                  let file = file_of_key k in
+                  match file.g_file_razorback_stats with
+                      None -> ()
+                    | Some stats ->
+                        begin
+                          try
+                            let filename = stats.razorback_file_history in
+                            Printf2.lprintf_nl2 "image: %s" filename;
+                            let pixb = GdkPixbuf.from_file filename in
+                            razorback_history#set_pixbuf pixb;
+                            label_rating#set_label (Printf.sprintf "Rating: %s" stats.razorback_file_rating);
+                            label_availability#set_label (Printf.sprintf "Available: %d" stats.razorback_file_avalaibility);
+                            label_completed#set_label (Printf.sprintf "Completed: %d" stats.razorback_file_completed);
+                          with _ -> GuiHtml.get_razorback2_stats file
+                        end
+                with _ -> ()
+              end
+          | _ -> ()
+      in
+      downloadview#set_on_select on_select_files
+    end;
+  let on_expand_item path k =
     ask_for_sources ();
     false
   in
-  let on_expanded_item path (i : GuiTypes2.item_info) =
+  let on_expanded_item path k =
     expanded_rows := List.length downloadview#expanded_paths;
-    ( if !!verbose then lprintf' "rows expanded %d\n" !expanded_rows)
+    (if !!verbose then lprintf' "rows expanded %d\n" !expanded_rows)
   in
-  let on_collapsed_item path (i : GuiTypes2.item_info) =
+  let on_collapsed_item path k =
     expanded_rows := List.length downloadview#expanded_paths;
-    ( if !!verbose then lprintf' "rows expanded %d\n" !expanded_rows)
+    (if !!verbose then lprintf' "rows expanded %d\n" !expanded_rows)
   in
   view_context := Some downloadview#view#misc#pango_context;
   downloadview#set_model downloadstore#gmodel;
@@ -1233,6 +1433,7 @@
     (fun _ ->
       ( if !!verbose then lprintf' "Update downloaders\n");
       update_downloaders ();
+      update_downloading_files ();
       true);
   ignore (entry#event#connect#key_press ~callback:
     (fun ev ->
@@ -1247,6 +1448,8 @@
   ignore (Timeout.add ~ms:1800000 ~callback:
     (fun _ ->
       clean_avail_bars ();
+      if !!O.gtk_misc_display_razorback_stats
+        then all_files_razorback2_stats ();
       true
   ));
   ignore (Timeout.add ~ms:30000 ~callback:
diff -urN src/gtk2/gui/guiFriends.ml src/gtk2/gui/guiFriends.ml
--- src/gtk2/gui/guiFriends.ml	2005-03-02 20:45:11.000000000 +0100
+++ src/gtk2/gui/guiFriends.ml	2005-10-23 14:54:59.000000000 +0200
@@ -53,6 +53,16 @@
 let (dialogs : (int * GuiTemplates.chat_buffer) list ref) = ref []
 let source_has_file = ref []
 
+module H = Weak.Make(struct
+      type t = g_file_tree
+
+      let hash ft = Hashtbl.hash ft.g_file_tree_num
+      let equal x y  = x.g_file_tree_num = y.g_file_tree_num
+
+    end)
+
+let ft_by_num = H.create 107
+
 (*************************************************************************)
 (*                                                                       *)
 (*                         Global variables                              *)
@@ -74,6 +84,104 @@
     then Some (A.get_icon ~icon:M.icon_stock_directory ~size:A.SMALL ())
     else None
 
+let dummy_ft =
+  {
+   g_file_tree_num  = 0;
+   g_file_tree_name = "";
+   g_file_tree_list = [];
+   g_file_tree_pixb = None;
+  }
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         friend_num                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let friend_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         friend_of_key                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let friend_of_key key =
+  try
+    let num = friend_num key in
+    Hashtbl.find G.sources num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_friends                               *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_friends keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = friend_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         friend_key                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let friend_key friend_num =
+  Printf.sprintf "%d" friend_num
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         folder_num                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let folder_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         folder_of_key                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let folder_of_key key =
+  try
+    let num = folder_num key in
+    H.find ft_by_num {dummy_ft with g_file_tree_num = num}
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_folders                               *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_folders keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = friend_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         folder_key                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let folder_key ft_num =
+  Printf.sprintf "%d" ft_num
+
 (*************************************************************************)
 (*                                                                       *)
 (*                         Templates                                     *)
@@ -85,10 +193,9 @@
   module Column = GuiColumns.Friend
 
   type item = source_info
-  type key = int
 
   let columns = O.friends_columns
-  let get_key = (fun s -> s.source_num)
+  let get_key = (fun s -> friend_key s.source_num)
   let module_name = "Friends"
 
 end)
@@ -206,10 +313,14 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method sort_items c s1 s2 =
-      match c with
+    method sort_items c k1 k2 =
+      try
+        let s1 = friend_of_key k1 in
+        let s2 = friend_of_key k2 in
+        match c with
           Col_friend_network -> compare s1.source_network s2.source_network
         | Col_friend_name -> compare s1.source_name s2.source_name
+      with _  -> 0
 
 (*************************************************************************)
 (*                                                                       *)
@@ -218,9 +329,10 @@
 (*************************************************************************)
 
     method force_update_icons () =
-      List.iter (fun s ->
+      List.iter (fun k ->
         try
-          let (row, _) = self#find_item s.source_num in 
+          let s = friend_of_key k in
+          let row = self#find_row k in
           store#set ~row ~column:friend_network_pixb (Mi.network_pixb s.source_network ~size:A.SMALL ());
           store#set ~row ~column:friend_type_pixb (Mi.source_type_to_icon s.source_type ~size:A.SMALL);
           store#set ~row ~column:friend_state_pixb (Mi.client_state_to_icon false ~size:A.SMALL);
@@ -264,10 +376,9 @@
   module Column = GuiColumns.Directory
 
   type item = g_file_tree
-  type key = int
 
   let columns = O.friends_dirs_columns
-  let get_key = (fun ft -> ft.g_file_tree_num)
+  let get_key = (fun ft -> folder_key ft.g_file_tree_num)
   let module_name = "FriendFolders"
 
 end)
@@ -326,8 +437,7 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method sort_items _ ft1 ft2 =
-      compare ft1.g_file_tree_name ft2.g_file_tree_name
+    method sort_items _ k1 k2 = 0
 
   end
 
@@ -412,18 +522,22 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let remove sel () = 
-  List.iter (fun s ->
-    GuiCom.send (RemoveFriend s.source_num);
-    (* there is a #bug here or is it volontary ? The core doesn't send back the updated client_type !!! *)
-    GuiCom.send (GuiProto.GetClient_info s.source_num)
+let remove sel () =
+  List.iter (fun k ->
+    try
+      GuiCom.send (RemoveFriend (friend_num k));
+      (* there is a #bug here or is it volontary ? The core doesn't send back the updated client_type !!! *)
+      GuiCom.send (GuiProto.GetClient_info (friend_num k))
+    with _ -> ()
   ) sel
 
 let remove_all_friends () = 
   GuiCom.send RemoveAllFriends;
   (* there is a #bug here or is it volontary ? The core doesn't send back the updated client_type !!! *)
-  List.iter (fun s ->
-    GuiCom.send (GuiProto.GetClient_info s.source_num)
+  List.iter (fun k ->
+    try
+      GuiCom.send (GuiProto.GetClient_info (friend_num k))
+    with _ -> ()
   ) (friendstore#all_items ())
 
 
@@ -435,29 +549,35 @@
 let on_entry_return num s =
   GuiCom.send (MessageToClient (num, s))
 
-let download_selected_dir (sel : g_file_tree list) () =
+let download_selected_dir sel () =
   match sel with
       [] -> ()
-    | ft :: tail ->
+    | k :: tail ->
         begin
-          let files = Mi.list_files ft in
-          let len = List.length files in
-          match (GToolbox.question_box
-              (!M.qT_wt_download_selected_dir)
-              [ !M.pW_lb_ok ; !M.pW_lb_cancel]
-              (U.utf8_of (Printf.sprintf !M.qT_lb_confirm_download_dir 
-                 len ft.g_file_tree_name))) 
-          with
-            1 ->
-              List.iter (fun r -> 
-                GuiCom.send (Download_query ([r.res_name], r.res_num, false))
-              ) files
-          | _ -> ()
+          try
+            let ft = folder_of_key k in
+            let files = Mi.list_files ft in
+            let len = List.length files in
+            match (GToolbox.question_box
+                    (!M.qT_wt_download_selected_dir)
+                    [ !M.pW_lb_ok ; !M.pW_lb_cancel]
+                    (U.utf8_of (Printf.sprintf !M.qT_lb_confirm_download_dir 
+                    len ft.g_file_tree_name))) 
+            with
+              1 ->
+                List.iter (fun r -> 
+                  GuiCom.send (Download_query ([r.res_name], r.res_num, false))
+                ) files
+            | _ -> ()
+          with _ -> ()
         end
 
-let show_details s () =
-  let item = Source (s, 0) in
-  GuiInfoWindow.window item ()
+let show_details k () =
+  try
+    let s = friend_of_key k in
+    let item = Source (s, 0) in
+    GuiInfoWindow.window item ()
+  with _ -> ()
 
 (*************************************************************************)
 (*                                                                       *)
@@ -465,17 +585,17 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let friend_menu (sel : source_info list) =
+let friend_menu sel =
   [
    `I (!M.fT_me_find_friend, find_friend) ;
    `I (!M.fT_me_remove_all_friends, remove_all_friends)
   ] @
   (match sel with
       [] -> []
-    | s :: tail ->
+    | k :: tail ->
                [
                 `S;
-                `I ((!M.dT_me_show_source_details), show_details s) ;
+                `I ((!M.dT_me_show_source_details), show_details k) ;
                 `I (!M.fT_me_remove, remove sel) ;
                ])
 
@@ -488,6 +608,7 @@
 let rec insert_dir ft id ?parent () =
   ft.g_file_tree_num <- !id;
   let parent = folderstore#add_item ft ?parent () in
+  H.add ft_by_num ft;
   incr id;
   match ft.g_file_tree_list with
       [] -> ()
@@ -498,20 +619,26 @@
             | GTreeDirectory d -> insert_dir d id ~parent ()
         ) ft.g_file_tree_list
 
-let on_select_friend (sel : source_info list) =
+let on_select_friend sel =
+  H.clear ft_by_num;
   resultstore#clear ();
   folderstore#clear ();
   update_results_label ();
   match sel with
       [] -> ()
-    | s :: tail ->
-        match s.source_files with
-            None -> (GuiCom.send (GetClient_files s.source_num))
-          | Some ft ->
-            begin
-              let id = ref 1 in
-              insert_dir ft id ()
-            end
+    | k :: tail ->
+        begin
+          try
+            let s = friend_of_key k in
+            match s.source_files with
+                None -> (GuiCom.send (GetClient_files s.source_num))
+              | Some ft ->
+                  begin
+                    let id = ref 1 in
+                    insert_dir ft id ()
+                  end
+          with _ -> ()
+        end
 
 (*************************************************************************)
 (*                                                                       *)
@@ -519,16 +646,19 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let on_double_click_friend (s : source_info) =
-  match !wnote_chat with
-      Some w when not (List.mem_assoc s.source_num !dialogs) ->
-        begin
-          let buffer = GuiTemplates.chat_buffer ~on_entry:(on_entry_return s.source_num) () in
-          let chat = GuiTemplates.chat_view ~buffer  ~my_name:!G.client_name () in
-          dialogs := (s.source_num, buffer) :: !dialogs;
-          add_chat w s chat
-        end
-    | _ -> ()
+let on_double_click_friend k =
+  try
+    let s = friend_of_key k in
+    match !wnote_chat with
+        Some w when not (List.mem_assoc s.source_num !dialogs) ->
+          begin
+            let buffer = GuiTemplates.chat_buffer ~on_entry:(on_entry_return s.source_num) () in
+            let chat = GuiTemplates.chat_view ~buffer  ~my_name:!G.client_name () in
+            dialogs := (s.source_num, buffer) :: !dialogs;
+            add_chat w s chat
+          end
+      | _ -> ()
+  with _ -> ()
 
 (*************************************************************************)
 (*                                                                       *)
@@ -536,9 +666,11 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let filter_friend s = not (
-  List.memq s.source_network !G.networks_filtered
-  )
+let filter_friend k =
+  try
+    let s = friend_of_key k in
+    not (List.memq s.source_network !G.networks_filtered)
+  with _ -> true
 
 (*************************************************************************)
 (*                                                                       *)
@@ -546,7 +678,7 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let folder_menu (sel : g_file_tree list) =
+let folder_menu sel =
   match sel with
       [] -> []
     | _ ->
@@ -560,19 +692,22 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let on_select_folder (sel : g_file_tree list) =
+let on_select_folder sel =
   resultstore#clear ();
   update_results_label ();
   match sel with
       [] -> ()
-    | ft :: tail ->
+    | k :: tail ->
         begin
-          List.iter (fun tree_item ->
-            match tree_item with
-                GTreeFile r -> ignore (resultstore#add_item r)
-              | _ -> ()
-          ) ft.g_file_tree_list;
-          update_results_label ()
+          try
+            let ft = folder_of_key k in
+            List.iter (fun tree_item ->
+              match tree_item with
+                  GTreeFile r -> ignore (resultstore#add_item r)
+                | _ -> ()
+            ) ft.g_file_tree_list;
+            update_results_label ()
+          with _ -> ()
         end
 
 (*************************************************************************)
@@ -581,10 +716,14 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let on_expanded_folder path (ft : g_file_tree) =
-  let (row, _) = folderstore#find_item ft.g_file_tree_num in
-  let ft_new = {ft with g_file_tree_pixb = folder_opened ()} in
-  folderstore#update_item row ft ft_new
+let on_expanded_folder path k =
+  try
+    let row = folderstore#find_row k in
+    let ft = folder_of_key k in
+    let ft_new = {ft with g_file_tree_pixb = folder_opened ()} in
+    folderstore#update_item row ft ft_new;
+    ft.g_file_tree_pixb <- ft_new.g_file_tree_pixb
+  with _ -> ()
 
 (*************************************************************************)
 (*                                                                       *)
@@ -592,10 +731,14 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let on_collapsed_folder path (ft : g_file_tree) =
-  let (row, _) = folderstore#find_item ft.g_file_tree_num in
-  let ft_new = {ft with g_file_tree_pixb = folder_closed ()} in
-  folderstore#update_item row ft ft_new
+let on_collapsed_folder path k =
+  try
+    let row = folderstore#find_row k in
+    let ft = folder_of_key k in
+    let ft_new = {ft with g_file_tree_pixb = folder_closed ()} in
+    folderstore#update_item row ft ft_new;
+    ft.g_file_tree_pixb <- ft_new.g_file_tree_pixb
+  with _ -> ()
 
 (*************************************************************************)
 (*                                                                       *)
@@ -629,6 +772,7 @@
   in
   dialogs := [];
   source_has_file := [];
+  H.clear ft_by_num;
   resultstore#clear ();
   folderstore#clear ();
   friendstore#clear ();
@@ -642,14 +786,14 @@
 (*************************************************************************)
 
 let remove_friend s =
-  friendstore#remove_item s;
+  friendstore#remove_item (friend_key s.source_num);
   update_friends_label ();
   if List.mem_assoc s.source_num !source_has_file
     then source_has_file := List.remove_assoc s.source_num !source_has_file
 
-let h_update_friend s_new =
+let h_update_friend s s_new =
   try
-    let (row, s) = friendstore#find_item s_new.source_num in
+    let row = friendstore#find_row (friend_key s_new.source_num) in
     if client_browsed_tag land s_new.source_type = 0
       then remove_friend s
       else friendstore#update_item row s s_new
@@ -664,18 +808,19 @@
   try
     let r = Hashtbl.find G.results result_num in
     try
-      let (_, s_new) = friendstore#find_item source_num in
+      let s = Hashtbl.find G.sources source_num  in
       let tree =
-        match s_new.source_files with
+        match s.source_files with
             None -> {g_file_tree_num = 0; g_file_tree_list = []; g_file_tree_name = ""; g_file_tree_pixb = folder_closed ()}
-          | Some tree -> { tree with g_file_tree_list = tree.g_file_tree_list }
+          | Some tree -> {tree with g_file_tree_list = tree.g_file_tree_list }
       in
       Mi.add_file tree dirname r;
-      s_new.source_files <- Some tree;
+      let s_new = {s with source_files = Some tree} in
       Hashtbl.remove G.results result_num;
       (if not (List.mem_assoc source_num !source_has_file)
          then source_has_file := (source_num, false) :: !source_has_file);
-      h_update_friend s_new;
+      h_update_friend s s_new;
+      s.source_files <- s_new.source_files;
       if not (List.assoc source_num !source_has_file)
         then begin
           source_has_file := List.remove_assoc source_num !source_has_file;
diff -urN src/gtk2/gui/guiGlobal.ml src/gtk2/gui/guiGlobal.ml
--- src/gtk2/gui/guiGlobal.ml	2005-03-02 20:45:11.000000000 +0100
+++ src/gtk2/gui/guiGlobal.ml	2005-10-31 12:19:59.000000000 +0100
@@ -32,6 +32,15 @@
 (* The name of the client given by MLnet. *)
 let client_name = ref ""
 
+(* The max download rate of the client given by MLnet in ko/s. *)
+let max_hard_download_rate = ref 64
+
+(* The max upload rate of the client given by MLnet in ko/s. *)
+let max_hard_upload_rate = ref 16
+
+(* The time between updates of the client given by MLnet in ms. *)
+let update_gui_delay = ref 1000
+
 (* Number of known servers. *)
 let nservers = ref 0
 
@@ -63,20 +72,26 @@
 (*                                                                       *)
 (*************************************************************************)
 
-(* Information on networks. *)
 let networks_filtered = ref ([] : int list)
 
 let (networks : (int, GuiTypes2.net_info) Hashtbl.t) = Hashtbl.create 13
 
-(* Information on clients. *)
-let (sources : (int, GuiTypes2.source_info) Hashtbl.t) = Hashtbl.create 1023
+let (files : (int, GuiTypes2.g_file_info) Hashtbl.t) = Hashtbl.create 27
 
-let (results : (int, GuiTypes2.res_info) Hashtbl.t) = Hashtbl.create 103
+let (servers : (int, GuiTypes.server_info) Hashtbl.t) = Hashtbl.create 207
+
+let (rooms : (int, GuiTypes.room_info) Hashtbl.t) = Hashtbl.create 207
+
+let (sources : (int, GuiTypes2.source_info) Hashtbl.t) = Hashtbl.create 1023
 
+let (results : (int, GuiTypes2.res_info) Hashtbl.t) = Hashtbl.create 1023
 
-(* Information on users. *)
 let (users : (int, GuiTypes.user_info) Hashtbl.t) = Hashtbl.create 1023
 
+let (shared_files : (int, GuiTypes2.shared_file) Hashtbl.t) = Hashtbl.create 103
+
+let (file_by_uid : (CommonTypes.uid_type, string) Hashtbl.t) = Hashtbl.create 103
+
 (*************************************************************************)
 (*                                                                       *)
 (*                         Global functions                              *)
@@ -92,12 +107,16 @@
   search_counter := 0;
   use_interested_in_sources := false;
 (* tables    *)
-
   Hashtbl.clear networks;
+  Hashtbl.clear files;
+  Hashtbl.clear servers;
+  Hashtbl.clear rooms;
   Hashtbl.clear sources;
   Hashtbl.clear results;
-  Hashtbl.clear users
-
+  Hashtbl.clear users;
+  Hashtbl.clear shared_files;
+  Hashtbl.clear file_by_uid;
+  Gc.compact ()
 
 let console_message = ref (fun s -> 
       lprintf "CONSOLE: %s" s;
@@ -110,7 +129,7 @@
   done
 
 external setncnumeric : unit -> unit = "ml_setlcnumeric" "noalloc"
-    
+
 let _ =
   ignore (GMain.Main.init ~setlocale: true ());
   setncnumeric ();
@@ -124,11 +143,17 @@
   label#misc#modify_font (Pango.Font.from_string !!O.gtk_font_list);
   let font_metrics = label#misc#pango_context#get_metrics () in
   char_width := GPango.to_pixels font_metrics#approx_char_width;
-  digit_width := GPango.to_pixels font_metrics#approx_digit_width;
-  Printf.printf "char_width : %d digit_width : %d\n" !char_width !digit_width;
-  flush stdout
+  digit_width := GPango.to_pixels font_metrics#approx_digit_width
 
-let get_files = ref (fun (l : int list) -> ([] : GuiTypes.file_info list))
+let get_files list =
+  let l = ref [] in
+  List.iter (fun file_num ->
+    try
+      let f = Hashtbl.find files file_num in
+      l := f :: !l
+    with _ -> ()
+  ) list;
+  !l
 
 let is_docked = ref false
 
@@ -141,3 +166,36 @@
   }
 
 let (set_systray_callback : ((GuiTypes2.button_types -> unit) -> unit) ref) = ref (fun _ -> ())
+
+open GuiTypes2
+open CommonTypes
+
+let check_usefull_source s =
+  let is_not_source = if s.source_files_requested = [] then true else false in
+  let is_not_friend = if client_browsed_tag land s.source_type = 0 then false else true in
+  let is_not_uploader = if source_only land s.source_has_upload = 1 then true else false in
+  if is_not_source && is_not_friend && is_not_uploader
+    then (Hashtbl.remove sources s.source_num; false)
+    else true
+
+
+let hashtbl_update_sources s s_new =
+  s.source_kind <- s_new.source_kind;
+  s.source_state <- s_new.source_state;
+  s.source_type <- s_new.source_type;
+  s.source_tags <- s_new.source_tags;
+  s.source_name <- s_new.source_name;
+  s.source_files <- s_new.source_files;
+  s.source_rating <- s_new.source_rating;
+  s.source_chat_port <- s_new.source_chat_port;
+  s.source_connect_time <- s_new.source_connect_time;
+  s.source_last_seen <- s_new.source_last_seen;
+  s.source_software <- s_new.source_software;
+  s.source_downloaded <- s_new.source_downloaded;
+  s.source_uploaded <- s_new.source_uploaded;
+  s.source_upload_rate <- s_new.source_upload_rate;
+  s.source_download_rate <- s_new.source_download_rate;
+  s.source_upload <- s_new.source_upload;
+  s.source_has_upload <- s_new.source_has_upload;
+  s.source_availability <- s_new.source_availability;
+  s.source_files_requested <- s_new.source_files_requested
diff -urN src/gtk2/gui/guiGraphBase.ml src/gtk2/gui/guiGraphBase.ml
--- src/gtk2/gui/guiGraphBase.ml	1970-01-01 01:00:00.000000000 +0100
+++ src/gtk2/gui/guiGraphBase.ml	2005-10-23 14:37:24.000000000 +0200
@@ -0,0 +1,555 @@
+(* Copyright 2005 b8_bavard, INRIA *)
+(*
+    This file is part of mldonkey.
+
+    mldonkey 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.
+
+    mldonkey 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 mldonkey; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*)
+
+(* the graph base of the GUI. *)
+
+open Options
+open CommonTypes
+open GraphTypes
+
+module O = GuiOptions
+
+let verbose = O.gtk_verbose_graphbase
+
+let lprintf' fmt =
+  Printf2.lprintf ("GuiGraphBase: " ^^ fmt)
+
+let last_time = BasicSocket.current_time ()
+
+let dummy_graph () =
+  {
+   quarter = [last_time, 0.];
+   hour    = [last_time, 0.];
+   halfday = [last_time, 0.];
+   day     = [last_time, 0.];
+   week    = [last_time, 0.];
+   month   = [last_time, 0.];
+   year    = [last_time, 0.];
+  }
+
+let quarter = 15. *. 60.
+let hour = 4. *. quarter
+let halfday = 12. *. hour
+let day = 2. *. halfday
+let week = 7. *. day
+let month = 4. *. week
+let year = 52. *. week
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         GraphOption                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+module GraphOption = struct
+
+    let value_to_floats =
+      value_to_tuple2 (fun (v1, v2) ->
+        let f1 = value_to_float v1 in
+        let f2 = value_to_float v2 in
+        (f1, f2)
+      )
+
+    let floats_to_value =
+      tuple2_to_value (fun (f1, f2) ->
+        let v1 = float_to_value f1 in
+        let v2 = float_to_value f2 in
+        (v1, v2)
+      )
+
+    let check_graph_time l time_ref =
+      let last_graph_time, _ = List.hd (List.rev l) in
+      if last_time -. last_graph_time > time_ref
+        then [last_time, 0.] else l
+
+    let value_to_graph v =
+      match v with
+        Options.Module assocs ->
+          let get_value name conv = conv (List.assoc name assocs) in
+          let graph_quarter =
+            try
+              let l = get_value "graph_quarter" (value_to_list value_to_floats) in
+              check_graph_time l quarter
+            with _ -> [last_time, 0.]
+          in
+          let graph_hour =
+            try
+              let l = get_value "graph_hour" (value_to_list value_to_floats) in
+              check_graph_time l hour
+            with _ -> [last_time, 0.]
+          in
+          let graph_halfday =
+            try
+              let l = get_value "graph_halfday" (value_to_list value_to_floats) in
+              check_graph_time l halfday
+            with _ -> [last_time, 0.]
+          in
+          let graph_day =
+            try
+              let l = get_value "graph_day" (value_to_list value_to_floats) in
+              check_graph_time l day
+            with _ -> [last_time, 0.]
+          in
+          let graph_week =
+            try
+              let l = get_value "graph_week" (value_to_list value_to_floats) in
+              check_graph_time l week
+            with _ -> [last_time, 0.]
+          in
+          let graph_month =
+            try
+              let l = get_value "graph_month" (value_to_list value_to_floats) in
+              check_graph_time l month
+            with _ -> [last_time, 0.]
+          in
+          let graph_year =
+            try
+              let l = get_value "graph_year" (value_to_list value_to_floats) in
+              check_graph_time l year
+            with _ -> [last_time, 0.]
+          in
+          {
+             quarter = graph_quarter;
+             hour    = graph_hour;
+             halfday = graph_halfday;
+             day     = graph_day;
+             week    = graph_week;
+             month   = graph_month;
+             year    = graph_year;
+          }
+
+      | _ -> assert false
+
+    let graph_to_value graph =
+      Options.Module (
+        ("graph_quarter", list_to_value floats_to_value graph.quarter) ::
+        ("graph_hour",    list_to_value floats_to_value graph.hour)    ::
+        ("graph_halfday", list_to_value floats_to_value graph.halfday) ::
+        ("graph_day",     list_to_value floats_to_value graph.day)     ::
+        ("graph_week",    list_to_value floats_to_value graph.week)    ::
+        ("graph_month",   list_to_value floats_to_value graph.month)   ::
+        ("graph_year",    list_to_value floats_to_value graph.year)    ::
+        []
+      )
+
+    let t = define_option_class "Graph" value_to_graph graph_to_value
+
+end
+
+
+let uid_option  = define_option_class "TypeUid"
+    (fun v -> uid_of_string (value_to_string v))
+    (fun uid -> string_to_value (string_of_uid uid))
+
+let graph_ini = create_options_file
+    (Filename.concat GuiMessages.gui_config_dir "mlgui_graph.ini")
+
+let graph_section = file_section graph_ini [] ""
+
+let global_graph =
+  define_option graph_section ["global_graph"] "gobal records"
+    (tuple2_option (GraphOption.t, GraphOption.t))
+    (dummy_graph (), dummy_graph ())
+
+let files_graph =
+  define_option graph_section ["files_graph"] "files records"
+    (list_option (tuple2_option (uid_option, tuple2_option (GraphOption.t, GraphOption.t))))
+    []
+
+let _ =
+  (try Options.load graph_ini with _ -> ());
+  (try Options.save graph_ini with _ -> ())
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         Global tables                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let (global_queue : (graph_record, (float * float) list ref) Hashtbl.t) = Hashtbl.create 63
+
+let _ =
+  Hashtbl.add  global_queue GraphDownloads (ref [List.hd (List.rev (fst !!global_graph).quarter)]);
+  Hashtbl.add  global_queue GraphUploads (ref [List.hd (List.rev (snd !!global_graph).quarter)]);
+  List.iter (fun (file_uid, (down, up)) ->
+    Hashtbl.add  global_queue (GraphFile (file_uid, GraphDownloads)) (ref [List.hd (List.rev down.quarter)]);
+    Hashtbl.add  global_queue (GraphFile (file_uid, GraphUploads)) (ref [List.hd (List.rev up.quarter)]);
+  ) !!files_graph
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         Global variables                              *)
+(*                                                                       *)
+(*************************************************************************)
+
+let max_points = 200.
+
+let quarter_step = quarter /. max_points
+let hour_step = hour /. max_points
+let halfday_step = halfday /. max_points
+let day_step = day /. max_points
+let week_step = week /. max_points
+let month_step = month /. max_points
+let year_step = year /. max_points
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         r_avg                                         *)
+(*                                                                       *)
+(*************************************************************************)
+
+let r_avg l0 l1 =
+  let rx = ref 0. in
+  let min_t = ref (fst (List.hd l0)) in
+  let max_t = ref (fst (List.hd l1)) in
+  List.iter2 (fun (t0, r0) (t1, r1) ->
+    rx := !rx +. (r0 +. r1) *. (t1 -. t0) /. 2.;
+    min_t := min !min_t t0;
+    max_t := max !max_t t1;
+  ) l0 l1;
+  if (!max_t -. !min_t) > 0.
+    then !rx /. (!max_t -. !min_t)
+    else 0.
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         vect_from                                     *)
+(*                                                                       *)
+(*************************************************************************)
+
+let vect_from previous current =
+  if !!verbose then
+    begin
+      lprintf' "In vect_from : current length %d\n" (List.length current);
+      List.iter (fun (a, b) ->
+        lprintf' "     * [%f, %f]\n" a b
+      ) current;
+      lprintf' "In vect_from : previous length %d\n" (List.length previous);
+      List.iter (fun (a, b) ->
+        lprintf' "     * [%f, %f]\n" a b
+      ) previous
+    end;
+  let (t_current, r_current) = List.nth current (List.length current - 1) in
+  (if !!verbose then lprintf' "In vect_from : (t_current, r_current) = (%f, %f)\n" t_current r_current);
+  let l = ref [] in
+  let store = ref true in
+  let n = ref (List.length previous - 1) in
+  let (t, _) = List.nth previous !n in
+  (if !!verbose then lprintf' "In vect_from : t = %f\n" t);
+  while !store && (!n >= 0) do
+    let (t_n, r_n) = List.nth previous !n in
+    if t_n > t_current
+      then begin
+        l := (t_n, r_n) :: !l;
+        decr n
+      end else store := false
+  done;
+  l := (t_current, r_current) :: !l;
+  let l2 = List.tl !l in
+  let l1 = List.rev (List.tl (List.rev !l)) in
+  if !!verbose
+    then begin
+      lprintf' "In vect_from : l1 length %d\n" (List.length l1);
+      List.iter (fun (a, b) ->
+        lprintf' "     * [%f, %f]\n" a b
+      ) l1;
+      lprintf' "In vect_from : l2 length %d\n" (List.length l2);
+      List.iter (fun (a, b) ->
+        lprintf' "     * [%f, %f]\n" a b
+      ) l2
+    end;
+  let r_avg = r_avg l1 l2 in
+  (if !!verbose then lprintf' "In vect_from : r_avg %f\n" r_avg);
+  (t, r_avg)
+
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         record_year                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let record_year graph =
+  (if !!verbose then lprintf' "calculating record_year\n");
+  let vect = vect_from graph.month graph.year in
+  (if List.length graph.year < int_of_float max_points
+     then graph.year <- graph.year @ [vect]
+     else graph.year <- (List.tl graph.year) @ [vect]);
+  if !!verbose then lprintf' "record_year calculated successfully\n"
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         record_month                                  *)
+(*                                                                       *)
+(*************************************************************************)
+
+let record_month graph =
+  (if !!verbose then lprintf' "calculating record_month\n");
+  let (t, r) = vect_from graph.week graph.month in
+  (if List.length graph.month < int_of_float max_points
+     then graph.month <- graph.month @ [t, r]
+     else graph.month <- (List.tl graph.month) @ [t, r]);
+  (if !!verbose then lprintf' "record_month calculated successfully\n");
+  let (t_h, _) = List.nth graph.year (List.length graph.year - 1) in
+  if (t -. t_h) > year_step
+    then record_year graph
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         record_week                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let record_week graph =
+  (if !!verbose then lprintf' "calculating record_week\n");
+  let (t, r) = vect_from graph.day graph.week in
+  (if List.length graph.week < int_of_float max_points
+     then graph.week <- graph.week @ [t, r]
+     else graph.week <- (List.tl graph.week) @ [t, r]);
+  (if !!verbose then lprintf' "record_week calculated successfully\n");
+  let (t_h, _) = List.nth graph.month (List.length graph.month - 1) in
+  if (t -. t_h) > month_step
+    then record_month graph
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         record_day                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let record_day graph =
+  (if !!verbose then lprintf' "calculating record_day\n");
+  let (t, r) = vect_from graph.halfday graph.day in
+  (if List.length graph.day < int_of_float max_points
+     then graph.day <- graph.day @ [t, r]
+     else graph.day <- (List.tl graph.day) @ [t, r]);
+  (if !!verbose then lprintf' "record_day calculated successfully\n");
+  let (t_h, _) = List.nth graph.week (List.length graph.week - 1) in
+  if (t -. t_h) > week_step
+    then record_week graph
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         record_halfday                                *)
+(*                                                                       *)
+(*************************************************************************)
+
+let record_halfday graph =
+  (if !!verbose then lprintf' "calculating record_halfday\n");
+  let (t, r) = vect_from graph.hour graph.halfday in
+  (if List.length graph.halfday < int_of_float max_points
+     then graph.halfday <- graph.halfday @ [t, r]
+     else graph.halfday <- (List.tl graph.halfday) @ [t, r]);
+  (if !!verbose then lprintf' "record_halfday calculated successfully\n");
+  let (t_h, _) = List.nth graph.day (List.length graph.day - 1) in
+  if (t -. t_h) > day_step
+    then record_day graph
+
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         record_hour                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let record_hour graph =
+  (if !!verbose then lprintf' "calculating record_hour\n");
+  let (t, r) = vect_from graph.quarter graph.hour in
+  (if List.length graph.hour < int_of_float max_points
+     then graph.hour <- graph.hour @ [t, r]
+     else graph.hour <- (List.tl graph.hour) @ [t, r]);
+  (if !!verbose then lprintf' "record_hour calculated successfully\n");
+  let (t_h, _) = List.nth graph.halfday (List.length graph.halfday - 1) in
+  if (t -. t_h) > halfday_step
+    then record_halfday graph
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         record_quarter                                *)
+(*                                                                       *)
+(*************************************************************************)
+
+let record_quarter queue graph =
+  (if !!verbose then lprintf' "calculating record_quarter\n");
+  let (t, _) = List.hd !queue in
+  let l1 = List.tl !queue in
+  let l2 = List.rev (List.tl (List.rev !queue)) in
+  let r_avg = r_avg l1 l2 in
+  let vect = (t, r_avg) in
+  queue := [vect];
+  (if List.length graph.quarter < int_of_float max_points
+     then graph.quarter <- graph.quarter @ [vect]
+     else graph.quarter <- (List.tl graph.quarter) @ [vect]);
+  (if !!verbose then lprintf' "record_quarter calculated successfully\n");
+  let (t_h, _) = List.nth graph.hour (List.length graph.hour - 1) in
+  if (t -. t_h) > hour_step
+    then record_hour graph
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         save_record                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let print_graph q t s =
+  let n = List.length !q in
+  let str =
+    match t with
+      GraphDownloads -> Printf.sprintf "Global Downloads %d %s" n s
+    | GraphUploads -> Printf.sprintf "Global Uploads %d %s" n s
+    | GraphFile (file_uid, GraphDownloads) -> Printf.sprintf "%s Downloads %d %s" (string_of_uid file_uid) n s
+    | GraphFile (file_uid, GraphUploads) ->  Printf.sprintf "%s Uploads %d %s" (string_of_uid file_uid) n s
+    | _ -> "Unknown"
+  in
+  str
+
+let save_record rate (graph_type : graph_record) =
+  let rate = float_of_int rate /. 1024. in
+  let t = BasicSocket.current_time () in
+  let last_vect = (t, rate) in
+  let queue =
+    try
+      let q = Hashtbl.find global_queue graph_type in
+      (if !!verbose then lprintf' "Record %s\n" (print_graph q graph_type "old"));
+      q
+    with _ ->
+      let q = ref [last_time, 0.] in
+      Hashtbl.add global_queue graph_type q;
+       (if !!verbose then lprintf' "Record %s\n" (print_graph q graph_type "new"));
+      q
+  in
+  queue := last_vect :: !queue;
+  let first_vect = List.hd (List.rev !queue) in
+  let t_i = fst first_vect in
+  if (t -. t_i) > quarter_step
+    then match graph_type with
+             GraphDownloads ->
+               begin
+                 let graph = fst (!!global_graph) in
+                 record_quarter queue graph
+               end
+
+           | GraphUploads ->
+               begin
+                 let graph = snd (!!global_graph) in
+                 record_quarter queue graph
+               end
+
+           | GraphFile (file_uid, GraphDownloads) ->
+               begin
+                 let records =
+                   try
+                     List.assoc file_uid !!files_graph
+                   with _ ->
+                     begin
+                       (if !!verbose then lprintf'
+                          "In save_record / Downloads : Adding file %s\n" (string_of_uid file_uid));
+                       files_graph =:= (file_uid, (dummy_graph (), dummy_graph ())) :: !!files_graph;
+                       snd (List.hd !!files_graph)
+                     end
+                 in
+                 let graph = fst records in
+                 (if !!verbose then lprintf'
+                    "Found fst of %s GraphDownloads in !!files_graph\n" (string_of_uid file_uid));
+                 record_quarter queue graph;
+                 (if !!verbose then lprintf'
+                    "Found record_quarter of %s GraphDownloads in !!files_graph\n" (string_of_uid file_uid));
+               end
+
+           | GraphFile (file_uid, GraphUploads) ->
+               begin
+                 let records =
+                   try
+                     List.assoc file_uid !!files_graph
+                   with _ ->
+                     begin
+                       (if !!verbose then lprintf'
+                          "In save_record / Downloads : Adding file %s\n" (string_of_uid file_uid));
+                       files_graph =:= (file_uid, (dummy_graph (), dummy_graph ())) :: !!files_graph;
+                       snd (List.hd !!files_graph)
+                     end
+                 in
+                 let graph = snd records in
+                 (if !!verbose then lprintf'
+                    "Found fst of %s GraphUploads in !!files_graph\n" (string_of_uid file_uid));
+                 record_quarter queue graph;
+                 (if !!verbose then lprintf'
+                    "Found record_quarter of %s GraphUploads in !!files_graph\n" (string_of_uid file_uid));
+               end
+
+           | _ -> ()
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         cancel_file                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let cancel_file file_uid =
+  if List.mem_assoc file_uid !!files_graph
+    then begin
+      (if !!verbose
+         then lprintf' "In cancel_file : Cancelling file %s\n" (string_of_uid file_uid));
+      files_graph =:= List.filter (fun (fi_uid, _) -> fi_uid <> file_uid) !!files_graph;
+      Hashtbl.remove global_queue (GraphFile (file_uid, GraphDownloads));
+      Hashtbl.remove global_queue (GraphFile (file_uid, GraphUploads))
+    end
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         add_file                                      *)
+(*                                                                       *)
+(*************************************************************************)
+
+let add_file file_uid =
+  if not (List.mem_assoc file_uid !!files_graph)
+    then begin
+      (if !!verbose then lprintf' "In add_file : Adding file %s\n" (string_of_uid file_uid));
+      files_graph =:= (file_uid, (dummy_graph (), dummy_graph ())) :: !!files_graph;
+      let q = ref [last_time, 0.] in
+      Hashtbl.add global_queue (GraphFile (file_uid, GraphDownloads)) q;
+      Hashtbl.add global_queue (GraphFile (file_uid, GraphUploads)) q
+    end
+
+(*
+let _ =
+  Random.init 3;
+  ignore (GMain.Timeout.add ~ms:1200 ~callback:
+    (fun _ -> 
+       let rate = Random.int 10000 in
+       Printf2.lprintf "random rate = %d\n" rate;
+       flush stdout;
+       save_record rate GraphDownloads;
+       save_record (rate / 4) GraphUploads;
+       save_record (rate / 2) (GraphFile ("AAAAAAAAAAAAAAAAAAA", GraphDownloads));
+       save_record (rate / 5) (GraphFile ("AAAAAAAAAAAAAAAAAAA", GraphUploads));
+       save_record (rate / 3) (GraphFile ("BBBBBBBBBBBBBBBBBBB", GraphDownloads));
+       save_record (rate / 7) (GraphFile ("BBBBBBBBBBBBBBBBBBB", GraphUploads));
+       true
+  ))
+*)
+
+let _ =
+  ignore (GMain.Timeout.add ~ms:60000 ~callback:
+    (fun _ ->
+       (try Options.save graph_ini with _ -> ());
+       (if !!verbose then lprintf' "Save graph_ini\n");
+       true
+  ))
+
diff -urN src/gtk2/gui/guiGraph.ml src/gtk2/gui/guiGraph.ml
--- src/gtk2/gui/guiGraph.ml	1970-01-01 01:00:00.000000000 +0100
+++ src/gtk2/gui/guiGraph.ml	2005-10-23 16:04:39.000000000 +0200
@@ -0,0 +1,661 @@
+(* Copyright 2005 b8_bavard, INRIA *)
+(*
+    This file is part of mldonkey.
+
+    mldonkey 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.
+
+    mldonkey 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 mldonkey; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*)
+
+(* the graph tab of the GUI. *)
+
+open Options
+open GraphTypes
+open CommonTypes
+
+module G  = GuiGlobal
+module Gb = GuiGraphBase
+module M  = GuiMessages
+module O  = GuiOptions
+module U  = GuiUtf8
+
+let verbose = O.gtk_verbose_graph
+
+let lprintf' fmt =
+  Printf2.lprintf ("GuiGraph: " ^^ fmt)
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         Global variables                              *)
+(*                                                                       *)
+(*************************************************************************)
+
+let g_width = ((Gdk.Screen.width ()) * 4 / 9)
+let g_height = ((Gdk.Screen.height ()) * 1 / 3)
+
+let last_file = ref NoUid
+
+let graphtime_cols = new GTree.column_list
+let graphtime_num = graphtime_cols#add Gobject.Data.int
+let graphtime_text = graphtime_cols#add Gobject.Data.string
+let graphtime_store = GTree.list_store graphtime_cols
+
+let file_cols = new GTree.column_list
+let file_id = file_cols#add Gobject.Data.caml
+let file_name = file_cols#add Gobject.Data.string
+let file_store = GTree.list_store file_cols
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         lighten                                       *)
+(*                                                                       *)
+(*************************************************************************)
+
+let lighten col =
+  let gdk_col = GDraw.color ~colormap:(Gdk.Color.get_system_colormap ()) col in
+  let max_c = 255 * 256 in
+  let col_step = 100 * 256 in
+  let r = min max_c (Gdk.Color.red   gdk_col + col_step) in
+  let g = min max_c (Gdk.Color.green gdk_col + col_step) in
+  let b = min max_c (Gdk.Color.blue  gdk_col + col_step) in
+  `RGB (r, g, b)
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         graphtime_to_label                            *)
+(*                                                                       *)
+(*************************************************************************)
+
+let graph_times =
+  [
+   GraphQuarter;
+   GraphHour;
+   GraphHalfDay;
+   GraphDay;
+   GraphWeek;
+   GraphMonth;
+   GraphYear;
+  ]
+
+let graphtime_to_label g =
+  match g with
+      GraphQuarter -> !M.gT_lb_quarter
+    | GraphHour -> !M.gT_lb_hour
+    | GraphHalfDay -> !M.gT_lb_halfday
+    | GraphDay -> !M.gT_lb_day
+    | GraphWeek -> !M.gT_lb_week
+    | GraphMonth -> !M.gT_lb_month
+    | GraphYear -> !M.gT_lb_year
+
+let _ =
+  let n = ref 0 in
+  List.iter (fun g ->
+    let row = graphtime_store#append () in
+    graphtime_store#set ~row ~column:graphtime_num !n;
+    graphtime_store#set ~row ~column:graphtime_text (U.utf8_of (graphtime_to_label g));
+    incr n
+  ) graph_times
+
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         c_values                                      *)
+(*                                                                       *)
+(*************************************************************************)
+
+let c_values nbpts =
+  let step = 1. /. nbpts in
+  let rec r_values t l =
+    let t2 = t *. t in 
+    let t3 = t2 *. t in
+    let ct = 1. -. t
+      and ct2 = 1. -. 2. *. t +. t2
+      and ct3 = 1. -. 3. *. t +. 3. *. t2 -. t3
+    in
+    if t > 1.
+      then l
+      else r_values (t +. step) ((t3, 3. *. t2 *. ct, 3. *. t *. ct2, ct3) :: l)
+  in
+  r_values 0. []
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         Module Curve                                  *)
+(*                                                                       *)
+(*************************************************************************)
+
+module Curve(C:
+
+(*************************************************************************)
+(*************************************************************************)
+(*************************************************************************)
+(*                                                                       *)
+(*                         FUNCTOR Argument                              *)
+(*                                                                       *)
+(*************************************************************************)
+(*************************************************************************)
+(*************************************************************************)
+
+    sig
+
+      val graph_time       : graph_time option_record
+      val graph_foreground : string option_record
+      val graph_size       : (int * int)
+
+      val vertical_flip    : bool
+
+      val module_name      : string
+
+    end) = 
+  (struct
+
+  open C
+
+
+let lprintf'' fmt =
+  Printf2.lprintf ("GuiGraph - %s " ^^ fmt) module_name
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         Global variables                              *)
+(*                                                                       *)
+(*************************************************************************)
+
+let graph_data = ref (Gb.dummy_graph ())
+
+let graph_timerID = ref (GMain.Timeout.add ~ms:(8 * !G.update_gui_delay) ~callback:(fun _ -> true))
+
+let (graph_width, graph_height) = graph_size
+
+let scale_x = ref 1.
+let scale_y = ref 1.
+
+let abs_step = 70
+let step = (max graph_width graph_height) / abs_step
+
+let p =
+  GDraw.pixmap ~width:graph_width ~height:graph_height ~mask:false
+    ~colormap:(Gdk.Color.get_system_colormap ()) ()
+
+let image = GMisc.image ()
+let context = image#misc#pango_context
+let layout = context#create_layout
+let font = Pango.Font.from_string !!O.gtk_graph_font
+
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         graph_data_to_list                            *)
+(*                                                                       *)
+(*************************************************************************)
+
+let graph_data_to_list g =
+  match !!graph_time with
+      GraphQuarter -> g.quarter
+    | GraphHour -> g.hour
+    | GraphHalfDay -> g.halfday
+    | GraphDay -> g.day
+    | GraphWeek -> g.week
+    | GraphMonth -> g.month
+    | GraphYear -> g.year
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         scalex                                        *)
+(*                                                                       *)
+(*************************************************************************)
+
+let scalex () =
+  let t =
+    match !!graph_time with
+        GraphQuarter -> Gb.quarter
+      | GraphHour -> Gb.hour
+      | GraphHalfDay -> Gb.halfday
+      | GraphDay -> Gb.day
+      | GraphWeek -> Gb.week
+      | GraphMonth -> Gb.month
+      | GraphYear -> Gb.year
+  in
+  float_of_int graph_width /. t
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         scaley                                        *)
+(*                                                                       *)
+(*************************************************************************)
+
+let scaley () =
+  let max_rate = ref 1. in
+  let l = graph_data_to_list !graph_data in
+  List.iter (fun (_, r) ->
+    max_rate := max !max_rate r
+  ) l;
+  float_of_int graph_height /. (!max_rate *. 1.2)
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         compute_scales                                *)
+(*                                                                       *)
+(*************************************************************************)
+
+let compute_scales () =
+  scale_x := scalex ();
+  scale_y := scaley ()
+
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         draw_bezier_curve                             *)
+(*                                                                       *)
+(*************************************************************************)
+
+let draw_bezier_curve (x1, y1) (x2, y2) (x3, y3) (x4, y4) p =
+  let fore_col = `NAME !!graph_foreground in
+  let light_fore_col = lighten fore_col in
+  let points = float_of_int g_width (* (x4 -. x1) *. 1.3 *. !scale_x *) in
+  let draw_point (a, b, c, d) =
+    p#set_foreground fore_col;
+    let x = int_of_float ((x1 *. a +. x2 *. b +. x3 *. c +. x4 *. d) *. !scale_x) in
+    let y =
+      if vertical_flip
+        then int_of_float ((y1 *. a +. y2 *. b +. y3 *. c +. y4 *. d) *. !scale_y)
+        else graph_height - int_of_float ((y1 *. a +. y2 *. b +. y3 *. c +. y4 *. d) *. !scale_y)
+    in
+    let y0 =
+      if vertical_flip
+      then 0
+      else graph_height
+    in
+    p#line ~x ~y ~x ~y:y0;
+    p#set_foreground light_fore_col;
+    p#point ~x ~y
+  in
+  List.iter draw_point (c_values points)
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         normalize_vector                              *)
+(*                                                                       *)
+(*************************************************************************)
+
+let normalize_vector v x0 =
+  let (x, y) = v in
+  (x -. x0, y)
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         draw_curve                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let draw_curve () =
+  Pango.Context.set_font_description context#as_context font;
+  compute_scales ();
+  let grid_col = `NAME !!O.gtk_graph_grid in
+  p#set_foreground (`NAME !!O.gtk_graph_background);
+  p#rectangle ~x:0 ~y:0 ~width:graph_width ~height:graph_height ~filled:true ();
+  let l = graph_data_to_list !graph_data in
+  let n = List.length l - 4 in
+  (if !!verbose then lprintf'' "In draw_curve: data length = %d\n" (n + 4));
+  p#set_line_attributes ~width:1 ~style:`SOLID ();
+  let x0 =
+    match l with
+        [] -> BasicSocket.current_time ()
+      | v :: _ -> fst v
+  in
+  let i = ref 0 in
+  (try
+     while !i <= n do
+       let v1 = normalize_vector (List.nth l !i) x0 in
+       let v2 = normalize_vector (List.nth l (!i + 1)) x0 in
+       let v3 = normalize_vector (List.nth l (!i + 2)) x0 in
+       let v4 = normalize_vector (List.nth l (!i + 3)) x0 in
+       draw_bezier_curve v1 v2 v3 v4 p;
+       i := !i + 3
+     done
+  with _ -> ());
+  p#set_foreground grid_col;
+  p#set_line_attributes ~width:1 ~style:`ON_OFF_DASH ();
+  for i = 0 to step do
+    let a = i * abs_step in
+    p#line ~x:a ~y:0 ~x:a ~y:graph_height;
+    let y = if vertical_flip then a else (graph_height - a - 1) in
+    p#line ~x:0 ~y ~x:graph_width ~y;
+    if i > 0
+      then begin
+        let t = Date.to_string (x0 +. (float_of_int a /. !scale_x)) in
+        (if !!verbose then lprintf'' "  * time : %s\n" t);
+        Pango.Layout.set_text layout t;
+        let (offset_x, offset_y) = Pango.Layout.get_pixel_size layout in
+        p#put_layout ~x:(a - offset_x / 2) ~y:(graph_height - offset_y * (2 - (i mod 2)))
+                     ~fore:grid_col
+                     layout;
+        let t = Printf.sprintf "%5.1f ko/s" (float_of_int a /. !scale_y) in
+        (if !!verbose then lprintf'' "  * rate : %s\n" t);
+        Pango.Layout.set_text layout t;
+        let (_, offset) = Pango.Layout.get_pixel_size layout in
+        let y = if vertical_flip then (a - offset ) else (graph_height - a - offset) in
+        p#put_layout ~x:0 ~y
+                     ~fore:grid_col
+                     layout
+      end
+  done;
+  image#set_pixmap p
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         reset_graph_timer                             *)
+(*                                                                       *)
+(*************************************************************************)
+
+let reset_graph_timer () =
+  GMain.Timeout.remove (!graph_timerID);
+  let s =
+    match !!graph_time  with
+        GraphQuarter -> Gb.quarter
+      | GraphHour -> Gb.hour
+      | GraphHalfDay -> Gb.halfday
+      | GraphDay -> Gb.day
+      | GraphWeek -> Gb.week
+      | GraphMonth -> Gb.month
+      | GraphYear -> Gb.year
+  in
+  let ms = int_of_float (1000. *. s /. Gb.max_points) in
+  graph_timerID := GMain.Timeout.add ~ms ~callback:
+    (fun _ ->
+      (if !!verbose then lprintf'' "Update graph every %d ms -- time displayed = %s\n" ms (graphtime_to_label !!graph_time));
+      draw_curve ();
+      true);
+  (if !!verbose then lprintf'' "Update graphs every %d ms -- time displayed = %s\n" ms (graphtime_to_label !!graph_time));
+  draw_curve ()
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         set_graph_data                                *)
+(*                                                                       *)
+(*************************************************************************)
+
+let set_graph_data g =
+  graph_data := g;
+  reset_graph_timer ()
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         initializer                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let _ =
+  GMain.Timeout.remove (!graph_timerID);
+  ignore (image#misc#connect#destroy ~callback:
+    (fun _ ->
+       GMain.Timeout.remove (!graph_timerID);
+  ))
+
+end)
+
+
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         Module DisplayCurve                           *)
+(*                                                                       *)
+(*************************************************************************)
+
+module DisplayCurve(D:
+
+(*************************************************************************)
+(*************************************************************************)
+(*************************************************************************)
+(*                                                                       *)
+(*                         FUNCTOR Argument                              *)
+(*                                                                       *)
+(*************************************************************************)
+(*************************************************************************)
+(*************************************************************************)
+
+    sig
+
+      val w                 : GObj.widget
+      val graph_time        : graph_time option_record
+      val reset_graph_timer : unit -> unit
+      val title             : string
+
+    end) = 
+  (struct
+
+  open D
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         Global variables                              *)
+(*                                                                       *)
+(*************************************************************************)
+
+let vbox = GPack.vbox ~homogeneous:false ~spacing:6 ()
+let hbox =
+  GPack.hbox ~homogeneous:false ~spacing:6
+    ~packing:(vbox#pack ~expand:true ~fill:true) ()
+
+
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         initializer                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let _ =
+  let label =
+    GMisc.label ~text:!M.gT_lb_time_range
+      ~packing:(hbox#pack ~expand:false ~fill:true) ()
+  in
+  let combo =
+    GEdit.combo_box ~model:graphtime_store
+      ~packing:(hbox#pack ~expand:false ~fill:true) ()
+  in
+  let str_renderer = GTree.cell_renderer_text [ `XPAD 6 ] in
+  combo#pack str_renderer;
+  combo#add_attribute str_renderer "text" graphtime_text;
+  vbox#add w;
+  let markup = GuiTools.create_bold_markup title in
+  ignore (GMisc.label ~markup ~packing:(vbox#pack ~expand:true ~fill:true) ());
+  let n = ref 0 in
+  (try
+     let gt = !!graph_time in
+     List.iter (fun g ->
+       if g = gt
+         then assert false
+         else incr n
+     ) graph_times;
+     combo#set_active 0
+  with _ -> combo#set_active !n);
+  ignore (combo#connect#changed
+    (fun _ ->
+       match combo#active_iter with
+         | None -> ()
+         | Some row ->
+             begin
+               let n = combo#model#get ~row ~column:graphtime_num in
+               graph_time =:= (try List.nth graph_times n with _ -> GraphQuarter);
+               reset_graph_timer ()
+             end
+  ))
+
+end)
+
+(*************************************************************************)
+(*                                                                       *)
+(*                        file_name_from_uid                             *)
+(*                                                                       *)
+(*************************************************************************)
+
+let file_name_from_uid file_uid =
+  try
+    Hashtbl.find G.file_by_uid file_uid
+  with _ ->
+    begin
+      GuiGraphBase.cancel_file file_uid;
+      raise Not_found
+    end
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         Graph window                                  *)
+(*                                                                       *)
+(*************************************************************************)
+
+let ni = ref 0
+
+let graph_box gui =
+  incr ni;
+  let scroll_table_box =
+    GBin.scrolled_window ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC
+      ~placement:`TOP_LEFT ()
+  in
+  let table =
+    GPack.table ~columns:2 ~rows:2 ~homogeneous:true
+      ~row_spacings:6 ~col_spacings:6 ~border_width:6
+      ~packing:scroll_table_box#add_with_viewport ()
+  in
+
+  let module MyCurveD = Curve (struct
+      let graph_time       = O.gtk_graph_time_downloads
+      let graph_foreground = O.gtk_graph_download
+      let graph_size       = (g_width, g_height)
+      let vertical_flip    = false
+      let module_name      = Printf.sprintf "global_downloads_%d" !ni
+    end)
+  in
+  let module MyCurveU = Curve (struct 
+      let graph_time       = O.gtk_graph_time_uploads
+      let graph_foreground = O.gtk_graph_upload
+      let graph_size       = (g_width, g_height)
+      let vertical_flip    = false
+      let module_name      = Printf.sprintf "global_uploads_%d" !ni
+    end)
+  in
+  let module MyCurveFD = Curve (struct
+      let graph_time       = O.gtk_graph_time_file
+      let graph_foreground = O.gtk_graph_download
+      let graph_size       = (g_width, g_height / 2)
+      let vertical_flip    = false
+      let module_name      = Printf.sprintf "file_downloads_%d" !ni
+    end)
+  in
+  let module MyCurveFU = Curve (struct 
+      let graph_time       = O.gtk_graph_time_file
+      let graph_foreground = O.gtk_graph_upload
+      let graph_size       = (g_width, g_height / 2)
+      let vertical_flip    = true
+      let module_name      = Printf.sprintf "file_uploads_%d" !ni
+    end)
+  in
+
+  let file_data =
+    try
+      List.assoc !last_file !!Gb.files_graph
+    with _ -> 
+      match !!Gb.files_graph with
+          [] -> (Gb.dummy_graph (), Gb.dummy_graph ())
+        | c :: _ -> snd c
+  in
+  MyCurveD.set_graph_data (fst !!Gb.global_graph);
+  MyCurveU.set_graph_data (snd !!Gb.global_graph);
+  MyCurveFD.set_graph_data (fst file_data);
+  MyCurveFU.set_graph_data (snd file_data);
+
+  let vbox = GPack.vbox ~homogeneous:false () in
+  vbox#add MyCurveFD.image#coerce;
+  vbox#add MyCurveFU.image#coerce;
+  let reset_graph_timer () =
+    MyCurveFD.reset_graph_timer ();
+    MyCurveFU.reset_graph_timer ()
+  in
+
+  let module DisplayMyCurveD = DisplayCurve (struct
+      let w                 = MyCurveD.image#coerce
+      let graph_time        = O.gtk_graph_time_downloads
+      let reset_graph_timer = MyCurveD.reset_graph_timer
+      let title             = !M.gT_lb_global_downloads
+    end)
+  in
+  let module DisplayMyCurveU = DisplayCurve (struct
+      let w                 = MyCurveU.image#coerce
+      let graph_time        = O.gtk_graph_time_uploads
+      let reset_graph_timer = MyCurveU.reset_graph_timer
+      let title             = !M.gT_lb_global_uploads
+    end)
+  in
+  let module DisplayMyCurveF = DisplayCurve (struct
+      let w                 = vbox#coerce
+      let graph_time        = O.gtk_graph_time_file
+      let reset_graph_timer = reset_graph_timer
+      let title             = !M.gT_lb_file_down_up
+    end)
+  in
+
+
+  List.iter (fun (left, top, box) ->
+    table#attach ~left ~top ~expand:`X ~fill:`X box
+  ) [
+     0, 0, DisplayMyCurveD.vbox#coerce;
+     0, 1, DisplayMyCurveU.vbox#coerce;
+     1, 0, DisplayMyCurveF.vbox#coerce;
+    ];
+
+  let hbox = DisplayMyCurveF.hbox in
+  file_store#clear ();
+  List.iter (fun (file_uid, _) ->
+    try
+      let name = file_name_from_uid file_uid in
+      let row = file_store#append () in
+      file_store#set ~row ~column:file_id file_uid;
+      file_store#set ~row ~column:file_name name;
+    with _ -> ()
+  ) !!Gb.files_graph;
+  let combo =
+    GEdit.combo_box ~model:file_store ~width:(g_width * 2 / 3)
+      ~packing:(hbox#pack ~expand:false ~fill:true) ()
+  in
+  let str_renderer = GTree.cell_renderer_text [ `XPAD 6 ] in
+  combo#pack str_renderer;
+  combo#add_attribute str_renderer "text" file_name;
+  let n = ref 0 in
+  (try
+     List.iter (fun (file_uid, _) ->
+       if file_uid = !last_file
+         then assert false
+         else incr n
+     ) !!Gb.files_graph;
+     combo#set_active 0
+   with _ -> combo#set_active !n);
+  ignore (combo#connect#changed
+    (fun _ ->
+       match combo#active_iter with
+           None -> ()
+         | Some row ->
+             begin
+               let file_uid = combo#model#get ~row ~column:file_id in
+               last_file := file_uid;
+               try
+                 let data = List.assoc file_uid !!Gb.files_graph in
+                 MyCurveFD.set_graph_data (fst data);
+                 MyCurveFU.set_graph_data (snd data);
+               with _ -> ()
+             end
+    ));
+
+  scroll_table_box#coerce
+
+
diff -urN src/gtk2/gui/guiHtml.ml src/gtk2/gui/guiHtml.ml
--- src/gtk2/gui/guiHtml.ml	1970-01-01 01:00:00.000000000 +0100
+++ src/gtk2/gui/guiHtml.ml	2005-10-31 14:03:12.000000000 +0100
@@ -0,0 +1,1034 @@
+(* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
+(*
+    This file is part of mldonkey.
+
+    mldonkey 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.
+
+    mldonkey 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 mldonkey; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*)
+
+open Printf2
+open Options
+open GuiNetHtml
+open CommonTypes
+open GuiTypes2
+
+open Md4
+
+module O = GuiOptions
+
+let cache_dir = Filename.concat GuiMessages.gui_config_dir "cache"
+
+let clean_cache_dir () =
+  let files = Sys.readdir cache_dir in
+  Array.iter (fun basename ->
+    let filename = Filename.concat cache_dir basename in
+    try Sys.remove filename with _ -> ()
+  ) files
+
+let _ =
+  Unix2.safe_mkdir cache_dir;
+  Unix2.can_write_to_directory cache_dir;
+  clean_cache_dir ();
+  ignore (GMain.Timeout.add ~ms:3600000 ~callback:
+    (fun _ ->
+      clean_cache_dir ();
+      true
+  ))
+
+let make_request url =
+  let module H = Http_client in
+  let proxy =
+    if !!O.gtk_connection_http_use_proxy
+      then Some (!!O.gtk_connection_http_proxy_server, !!O.gtk_connection_http_proxy_port)
+      else None
+  in
+  let r = {
+      H.basic_request with
+      H.req_url = Url.of_string url;
+      H.req_proxy = proxy;
+      H.req_user_agent = "Mozilla/5.0 (Linux 2.4.19-16mdk i686; U) Opera 6.11  [en]";
+      H.req_referer = None;
+      H.req_request = H.GET;
+    } in
+  r
+
+let file_from_url url =
+  try
+    if not (Sys.file_exists cache_dir) then Unix.mkdir cache_dir 0o755;
+    let file = String.copy url in
+    for i = 0 to String.length file - 1 do
+      match file.[i] with
+        '/'
+      | '?' | '*' | '&' | ':' -> file.[i] <- '_'
+      | _ -> ()
+    done;
+    Filename.concat cache_dir file
+  with _ -> ""
+
+let get url_name f =
+  let r = make_request url_name in
+  let module H = Http_client in
+  let filename = file_from_url (Url.to_string r.H.req_url) in
+  H.wget_string r (fun _s ->
+    begin
+      try
+        (try Sys.remove filename with _ -> lprintf_nl2 "cannot remove file %s" filename);
+        File.from_string filename _s;
+      with _ -> (lprintf_nl2 "failed in H.wget_string")
+    end;
+    let in_chan = open_in_bin filename in
+    let lexb = Lexing.from_channel in_chan in
+    let dl = GuiNetHtml.parse_document
+               ~return_declarations:true ~return_pis:true
+               ~return_comments:true lexb
+    in
+    f dl;
+    close_in in_chan) (fun n m -> lprintf_nl2 "progress: %d / %d" n m)
+
+
+let uids_to_md4 uids =
+  let s = ref "" in
+  begin
+    try
+      List.iter (fun uid ->
+        match (Uid.to_uid uid) with
+            Ed2k md4 -> (s := Md4.to_string md4; raise Exit)
+          | _ -> ()
+      ) uids
+    with _ -> ()
+  end;
+  if !s <> ""
+    then !s
+    else raise Not_found
+
+let parse_razorback_stats stats md4 referer dl =
+  let rec iter _dl =
+    List.iter (fun d ->
+      match d with
+          Element (s, sl, __dl) when s = "img" ->
+            begin
+              List.iter (fun (_s, __s) ->
+                if _s = "src" & String2.contains __s md4
+                  then begin
+                    let url_name = Filename.concat referer __s in
+                    let basename = Filename2.basename __s in
+                    let filename = Filename.concat cache_dir basename in
+                    let r = make_request url_name in
+                    let module H = Http_client in
+                    H.wget_string r (fun ___s ->
+                      try
+                        (try Sys.remove filename with _ -> lprintf_nl2 "cannot remove file %s" filename);
+                        File.from_string filename ___s;
+                        stats.razorback_file_history <- filename
+                      with _ -> (lprintf_nl2 "failed in H.wget_string")) (fun n m -> lprintf_nl2 "progress: %d / %d" n m)
+                  end
+              ) sl;
+              iter __dl
+            end
+
+        | Element (s, sl, __dl) when s = "tfoot" ->
+            begin
+              let n = ref 0 in
+              let rec _iter ___dl pos =
+                List.iter (fun _d ->
+                  match _d with
+                      Element (_s, _sl, ____dl) when _s = "img" ->
+                        begin
+                          List.iter (fun (__s, ___s) ->
+                            if __s = "title"
+                              then begin
+                                stats.razorback_file_rating <- (GuiUtf8.utf8_of ___s)
+                              end
+                          ) _sl;
+                          _iter ____dl pos
+                        end
+
+                    | Element (_s, _sl, ____dl) when _s = "td" ->
+                        begin
+                          incr pos;
+                          _iter ____dl pos
+                        end
+
+                    | Element (_s, _sl, ____dl) ->
+                          _iter ____dl pos
+
+                    | Data _s ->
+                        if !pos = 5
+                          then begin
+                            try
+                              stats.razorback_file_completed <- int_of_string _s
+                            with _ -> ()
+                          end else if !pos = 6
+                          then begin
+                            try
+                              stats.razorback_file_avalaibility <- int_of_string _s
+                            with _ -> ()
+                          end
+
+                ) ___dl
+              in
+              _iter __dl n;
+              iter __dl
+            end
+
+        | Element (s, sl, __dl) ->
+            iter __dl
+
+        | _ -> ()
+
+    ) _dl
+  in
+  iter dl
+
+let get_razorback2_stats file =
+  try
+    let md4 = uids_to_md4 file.g_file_uids in
+    let stats = {
+      razorback_file_history      = "";
+      razorback_file_rating       = "";
+      razorback_file_avalaibility = 0;
+      razorback_file_completed    = 0;
+    } in
+    file.g_file_razorback_stats <- Some stats;
+    let referer = "http://stats.razorback2.com/" in
+    let url = Printf.sprintf "%sed2khistory?ed2k=%s" referer md4 in
+    get url (parse_razorback_stats stats md4 referer)
+  with _ -> ()
+
+(*
+Element: !
+  contents DOCTYPE html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+Element: html
+    Data: 
+
+    Element: head
+        Data: 
+
+        Element: meta
+          http-equiv content-type
+          content text/html;charset=iso-8859-1
+        Data: 
+
+        Element: meta
+          name keywords
+          content edonkey emule ed2khistory razorback database fakes files uncomplete
+        Data: 
+
+        Element: meta
+          name description
+          content razorback history database of ed2k/emule files
+        Data: 
+
+        Element: title
+            Data: ed2kHistory of file ed2k::8B16AC1A76E4D1D750547931E8ADD3F7
+        Data: 
+
+        Element: script
+          type text/javascript
+          src /sorttable3.js
+            Data: 
+        Data: 
+
+    Data: 
+
+    Element: body
+        Data: 
+
+        Element: script
+          type text/javascript
+            Data: <!-- 
+google_ad_client = "pub-4061973295205334"; 
+google_ad_width = 800; 
+google_ad_height = 90; 
+google_ad_format = "728x90_as"; 
+google_ad_channel ="5618134257"; 
+google_color_border = "A8DDA0"; 
+google_color_bg = "EBFFED"; 
+google_color_link = "0000CC"; 
+google_color_url = "008000"; 
+google_color_text = "6F6F6F"; 
+//-->
+        Data:  
+
+        Element: center
+            Element: script
+              type text/javascript
+              src http://pagead2.googlesyndication.com/pagead/show_ads.js
+                Data:  
+
+        Data: 
+
+        Element: br
+        Data: 
+
+        Element: center
+            Element: img
+              src /images/8B/8B16AC1A76E4D1D750547931E8ADD3F7.png
+              width 800
+              height 288
+              alt history graph for file ed2k::8B16AC1A76E4D1D750547931E8ADD3F7
+        Data: 
+
+        Element: br
+        Element: form
+          action /ed2khistory
+          method get
+            Data: 
+File Hash : 
+            Element: input
+              name ed2k
+              type text
+              maxlength 200
+              size 43
+            Data: &nbsp;
+            Element: input
+              type submit
+              value Submit
+        Data: 
+16063744 files in ed2khistory database
+        Element: br
+        Data: 
+
+        Element: br
+        Element: table
+          border 1
+          width 100%
+          bgcolor #D8D8D8
+          cellspacing 0
+          id t1
+          class sortable
+            Data: 
+
+            Element: colgroup
+              align right
+                Data: 
+
+            Element: colgroup
+              align right
+                Data: 
+
+            Element: colgroup
+              align right
+                Data: 
+
+            Element: colgroup
+              align center
+                Data: 
+
+            Element: colgroup
+              align right
+                Data: 
+
+            Element: colgroup
+              align right
+                Data: 
+
+            Element: thead
+                Element: tr
+                  bgcolor #B0B0B0
+                    Data:  
+                    Element: th
+                        Data: Server
+                    Element: th
+                        Data: File Name
+                    Element: th
+                        Data: Size
+                    Element: th
+                        Data: Rating
+                    Element: th
+                        Data: Complete
+                    Element: th
+                        Data: Avail
+
+            Element: tfoot
+              align right
+                Element: tr
+                  bgcolor #B0B0B0
+                    Element: td
+                        Element: font
+                          size +2
+                            Data: Total 39/
+                            Element: a
+                              href http://www.gruk.org/list.php
+                                Data: 59
+                    Element: td
+                    Element: td
+                    Element: td
+                        Element: img
+                          src /gifs/FileRating5.gif
+                          alt Excellent
+                          title Excellent (global rating 5, given by 1 clients (0%))
+                    Element: td
+                        Element: font
+                          size +2
+                            Data: 9822
+                    Element: td
+                        Element: font
+                          size +2
+                            Data: 9864
+                        Data: 
+
+            Element: tbody
+              align right
+                Data: 
+
+                Element: tr
+                    Element: td
+                      title &lt;&lt;&lt; Saugcenter &gt;&gt;&gt;
+                        Data: 80.190.233.144:6565
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 653
+                    Element: td
+                        Data: 655
+
+                Element: tr
+                    Element: td
+                      title Breizh Punisher's
+                        Data: 213.251.133.129:6567
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 184
+                    Element: td
+                        Data: 186
+
+                Element: tr
+                    Element: td
+                      title !-= www.FreeSexBay.com =-!
+                        Data: 83.149.102.1:4242
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 119
+                    Element: td
+                        Data: 120
+
+                Element: tr
+                    Element: td
+                      title eD2k Infinity
+                        Data: 213.251.161.152:4661
+                    Element: td
+                        Data: emule0.46c-installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 37
+                    Element: td
+                        Data: 37
+
+                Element: tr
+                    Element: td
+                      title Breizh Digitalus
+                        Data: 213.186.47.84:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                      align center
+                        Element: img
+                          src /gifs/FileRating5.gif
+                          alt Excellent
+                          title Excellent for 0% of clients
+                    Element: td
+                        Data: 500
+                    Element: td
+                        Data: 504
+
+                Element: tr
+                    Element: td
+                      title Em Server No.1
+                        Data: 193.138.230.251:4242
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 144
+                    Element: td
+                        Data: 146
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 9
+                        Data: 80.239.200.108:3000
+                    Element: td
+                        Data: eMule0.46c_Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 400
+                    Element: td
+                        Data: 400
+
+                Element: tr
+                    Element: td
+                      title !-= www.FreeOsex.com =-!
+                        Data: 83.149.123.189:4321
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 100
+                    Element: td
+                        Data: 100
+
+                Element: tr
+                    Element: td
+                      title &lt;&lt;&lt; Trafficservice24.de  &gt;&gt;&gt;
+                        Data: 81.169.143.167:7777
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 93
+                    Element: td
+                        Data: 94
+
+                Element: tr
+                    Element: td
+                      title !!!****ifreeex.net &gt;&gt;Hardcore XXX Movies !!!***
+                        Data: 194.30.160.81:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 96
+                    Element: td
+                        Data: 96
+
+                Element: tr
+                    Element: td
+                      title Rackbox 2
+                        Data: 213.251.161.69:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 73
+                    Element: td
+                        Data: 73
+
+                Element: tr
+                    Element: td
+                      title &#65403;FPRO&#65387;
+                        Data: 213.251.161.103:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 153
+                    Element: td
+                        Data: 154
+
+                Element: tr
+                    Element: td
+                      title Asyrix 2.1
+                        Data: 80.190.251.50:4321
+                    Element: td
+                        Data: eMule0.46c-Installer.[content.emule-project.net].exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 536
+                    Element: td
+                        Data: 536
+
+                Element: tr
+                  bgcolor #D0B0B0
+                    Element: td
+                      title Razorback 2.1
+                        Data: 195.245.244.244:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 645
+                    Element: td
+                        Data: 646
+
+                Element: tr
+                    Element: td
+                      title ### DOINGDO ###
+                        Data: 212.112.238.82:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 155
+                    Element: td
+                        Data: 156
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 8
+                        Data: 80.239.200.107:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 159
+                    Element: td
+                        Data: 160
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 10
+                        Data: 80.239.200.109:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 123
+                    Element: td
+                        Data: 123
+
+                Element: tr
+                    Element: td
+                      title !-= www.FreeOsex.com =-!
+                        Data: 83.149.123.188:4321
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 85
+                    Element: td
+                        Data: 85
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 6
+                        Data: 80.239.200.105:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 30
+                    Element: td
+                        Data: 31
+
+                Element: tr
+                    Element: td
+                      title ChezToff 2 (Serveur Fr)
+                        Data: 213.251.136.83:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 92
+                    Element: td
+                        Data: 93
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 11
+                        Data: 80.239.200.110:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 197
+                    Element: td
+                        Data: 197
+
+                Element: tr
+                    Element: td
+                      title Asyrix 2.2
+                        Data: 217.20.127.165:4321
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 15
+                    Element: td
+                        Data: 16
+
+                Element: tr
+                    Element: td
+                      title Byte Devils
+                        Data: 194.213.0.20:3306
+                    Element: td
+                        Data: eMule0.46c [par Ratiatum.com].exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 63
+                    Element: td
+                        Data: 64
+
+                Element: tr
+                    Element: td
+                      title www.UseNeXT.to
+                        Data: 212.112.243.146:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 53
+                    Element: td
+                        Data: 53
+
+                Element: tr
+                    Element: td
+                      title ChezToff (Serveur Fr)
+                        Data: 213.186.60.106:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 1275
+                    Element: td
+                        Data: 1278
+
+                Element: tr
+                    Element: td
+                      title !!!****ifreeex.net &gt;&gt;Hardcore XXX Movies !!!***
+                        Data: 194.30.160.41:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 225
+                    Element: td
+                        Data: 228
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 4
+                        Data: 80.239.200.103:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 143
+                    Element: td
+                        Data: 146
+
+                Element: tr
+                    Element: td
+                      title Jibsworld (www.jd2k.com)
+                        Data: 213.186.45.91:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 51
+                    Element: td
+                        Data: 51
+
+                Element: tr
+                    Element: td
+                      title Jibsworld2 (www.jd2k.com)
+                        Data: 213.251.162.35:4661
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 86
+                    Element: td
+                        Data: 86
+
+                Element: tr
+                    Element: td
+                      title ChezToff 3 (Serveur Fr)
+                        Data: 213.251.134.191:4661
+                    Element: td
+                        Data: Emule0.46C-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 217
+                    Element: td
+                        Data: 217
+
+                Element: tr
+                    Element: td
+                      title Byte Devils
+                        Data: 194.213.0.10:3306
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 10
+                    Element: td
+                        Data: 10
+
+                Element: tr
+                    Element: td
+                      title DonkeyServer No1
+                        Data: 62.241.53.2:4242
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 2585
+                    Element: td
+                        Data: 2597
+
+                Element: tr
+                    Element: td
+                      title &lt;&lt;&lt; Saugcenter &gt;&gt;&gt;
+                        Data: 212.112.241.158:6565
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 101
+                    Element: td
+                        Data: 102
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 2
+                        Data: 80.239.200.101:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 132
+                    Element: td
+                        Data: 132
+
+                Element: tr
+                    Element: td
+                      title Byte Devils
+                        Data: 194.213.0.30:3306
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 66
+                    Element: td
+                        Data: 66
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 3
+                        Data: 80.239.200.102:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 106
+                    Element: td
+                        Data: 106
+
+                Element: tr
+                    Element: td
+                      title BiG BanG 5
+                        Data: 80.239.200.104:3000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 114
+                    Element: td
+                        Data: 114
+
+                Element: tr
+                    Element: td
+                      title maxx1462
+                        Data: 84.150.43.187:9955
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 1
+                    Element: td
+                        Data: 1
+
+                Element: tr
+                    Element: td
+                      title ADSL Group
+                        Data: 217.91.58.88:7000
+                    Element: td
+                        Data: eMule0.46c-Installer.exe
+                    Element: td
+                        Data: 4577316
+                    Element: td
+                        Data: &nbsp;
+                    Element: td
+                        Data: 5
+                    Element: td
+                        Data: 5
+
+        Data: 
+
+        Element: p
+            Data: Legend :
+        Element: ul
+        Element: p
+Element: li
+    Data: Avail : The number of clients connected to the server sharing this file
+    Element: p
+Element: li
+    Data: Complete : The number of clients connected to the server sharing this file and that have the complete file
+    Element: p
+Element: li
+    Data: asked/h : The number of requests per hour received by razorback asking for sources. That gives an indication on the number of clients that are downloading this file
+    Element: p
+Element: li
+    Data: File rating is a score given by a client to a file, between 1 (Very bad) to 5 (Excellent). Servers then give you the average of those scores.
+    Element: br
+    Element: ul
+        Element: li
+            Element: img
+              src /gifs/FileRating1.gif
+              alt Very bad
+              title Invalid / Corrupt / Fake
+            Data:  : Means majority of clients said the file was a fake, or corrupt, or invalid
+            Element: br
+        Element: li
+            Element: img
+              src /gifs/FileRating2.gif
+              alt Poor
+              title Poor
+            Data:  : Means majority of clients said the file was of poor quality
+            Element: br
+        Element: li
+            Element: img
+              src /gifs/FileRating3.gif
+              alt Fair
+              title Fair
+            Data:  : Fair quality
+            Element: br
+        Element: li
+            Element: img
+              src /gifs/FileRating4.gif
+              alt Good
+              title Good
+            Data:  : Good
+            Element: br
+        Element: li
+            Element: img
+              src /gifs/FileRating5.gif
+              alt Excellent
+              title Excellent
+            Data:  : Excellent
+            Element: br
+    Data: 
+
+    Data: 
+
+*)
diff -urN src/gtk2/gui/guiInfoWindow.ml src/gtk2/gui/guiInfoWindow.ml
--- src/gtk2/gui/guiInfoWindow.ml	2005-04-26 13:02:21.000000000 +0200
+++ src/gtk2/gui/guiInfoWindow.ml	2005-10-31 12:21:29.000000000 +0100
@@ -105,7 +105,7 @@
 
 let item_name_to_string item =
   match item with
-      File f -> f.file_name
+      File f -> f.g_file_name
     | Source (s, files) -> s.source_name
 
 (*************************************************************************)
@@ -116,7 +116,7 @@
 
 let pixbuf_of_item item =
   match item with
-      File f -> Mi.file_type_of_name f.file_name ~size:A.LARGE
+      File f -> Mi.file_type_of_name f.g_file_name ~size:A.LARGE
     | Source (s, files) -> Mi.source_type_to_icon s.source_type ~size:A.LARGE
 
 (*************************************************************************)
@@ -140,16 +140,16 @@
 let item_info_of_item (table : GPack.table) item =
   match item with
       File f ->
-        let avail = Mi.main_availability_of f.file_network f.file_availability in
+        let avail = Mi.main_availability_of f.g_file_network f.g_file_availability in
         [
-         [!M.fW_lb_file_format; format_to_string table f.file_format];
-         [!M.fW_lb_file_hash;   Mi.uid_list_to_string f.file_uids];
-         [!M.fW_lb_file_size;   Mi.size_of_int64 f.file_size;
-          !M.fW_lb_file_state;  Mi.string_of_file_state f.file_state f.file_download_rate];
-         [!M.fW_lb_file_chunks; Mi.chunks_to_string f.file_chunks;
-          !M.fW_lb_file_avail;  Mi.string_of_availability avail f.file_chunks];
-         [!M.fW_lb_file_age;    Mi.time_to_string f.file_age;
-          !M.fW_lb_file_prio;   Mi.priority_to_string f.file_priority]
+         [!M.fW_lb_file_format; format_to_string table f.g_file_format];
+         [!M.fW_lb_file_hash;   Mi.uid_list_to_string f.g_file_uids];
+         [!M.fW_lb_file_size;   Mi.size_of_int64 f.g_file_size;
+          !M.fW_lb_file_state;  Mi.string_of_file_state f.g_file_state f.g_file_download_rate];
+         [!M.fW_lb_file_chunks; Mi.chunks_to_string f.g_file_chunks;
+          !M.fW_lb_file_avail;  Mi.string_of_availability avail f.g_file_chunks];
+         [!M.fW_lb_file_age;    Mi.time_to_string f.g_file_age;
+          !M.fW_lb_file_prio;   Mi.priority_to_string f.g_file_priority]
         ]
 
     | Source (s, files) ->
@@ -170,13 +170,13 @@
   match item with
       File f ->
         [
-         [!M.fW_lb_file_sources;         Mi.sources_to_string f.file_sources;
-          !M.fW_lb_file_rate;            Mi.average_rate f.file_downloaded f.file_age];
-         [!M.fW_lb_file_dled;            Mi.size_of_int64 f.file_downloaded;
-          !M.fW_lb_file_complete_chunks; Mi.completed_chunks_to_string f.file_chunks];
-         [!M.fW_lb_last_seen;            Mi.time_to_string f.file_last_seen;
-          !M.fW_lb_file_eta;            (Mi.calc_eta_inst f.file_size f.file_downloaded f.file_download_rate) ^ " / " ^
-                                        (Mi.calc_eta_average f.file_size f.file_downloaded f.file_age)]
+         [!M.fW_lb_file_sources;         Mi.sources_to_string f.g_file_sources;
+          !M.fW_lb_file_rate;            Mi.average_rate f.g_file_downloaded f.g_file_age];
+         [!M.fW_lb_file_dled;            Mi.size_of_int64 f.g_file_downloaded;
+          !M.fW_lb_file_complete_chunks; Mi.completed_chunks_to_string f.g_file_chunks];
+         [!M.fW_lb_last_seen;            Mi.time_to_string f.g_file_last_seen;
+          !M.fW_lb_file_eta;            (Mi.calc_eta_inst f.g_file_size f.g_file_downloaded f.g_file_download_rate) ^ " / " ^
+                                        (Mi.calc_eta_average f.g_file_size f.g_file_downloaded f.g_file_age)]
         ]
 
     | Source (s, files) ->
@@ -278,7 +278,7 @@
   let height = 16 in
   List.iter (fun (net_num, avail) ->
     let net_pixb = Mi.network_pixb net_num ~size:A.SMALL () in
-    let avail_bar = Mi.availability_bar avail f.file_chunks true in
+    let avail_bar = Mi.availability_bar avail f.g_file_chunks true in
     let _ =
       match (net_pixb, avail_bar) with
           (Some pb, Some pixb) ->
@@ -306,7 +306,7 @@
       | _ -> ()
     in
     incr top
-  ) f.file_availability;
+  ) f.g_file_availability;
   (!top + 1)
 
 (*************************************************************************)
@@ -322,7 +322,7 @@
 
 let add_file_names (table : GPack.table) f top =
   let height = !wheight * 1 / 4 in
-  let file_name = U.utf8_of f.file_name in
+  let file_name = U.utf8_of f.g_file_name in
   let cols = new GTree.column_list in
   let file_names = cols#add Gobject.Data.string in
   let file_count = cols#add Gobject.Data.int in
@@ -338,7 +338,7 @@
     liststore#set ~row ~column:file_names_pixb pixb;
     liststore#set ~row ~column:file_names s;
     liststore#set ~row ~column:file_count ips.nips;
-  ) f.file_names;
+  ) f.g_file_names;
   let col_name =
     let col =  GTree.view_column ~title:!M.fW_lb_file_names_col () in
     col#set_resizable true;
@@ -389,9 +389,9 @@
   ignore (button#connect#clicked ~callback:
     (fun _ ->
       if entry#text <> "" then
-        match f.file_state with
- 	    FileDownloaded  -> GuiCom.send (GuiProto.SaveFile (f.file_num, entry#text))
-          | _  ->  GuiCom.send (GuiProto.RenameFile (f.file_num, entry#text))
+        match f.g_file_state with
+ 	    FileDownloaded  -> GuiCom.send (GuiProto.SaveFile (f.g_file_num, entry#text))
+          | _  ->  GuiCom.send (GuiProto.RenameFile (f.g_file_num, entry#text))
   ));
   ignore (view#selection#connect#after#changed ~callback:
     (fun _ ->
@@ -427,10 +427,10 @@
       [] -> insert_data table [] top
     | files ->
         begin
-          let file_list = !G.get_files files in
+          let file_list = G.get_files files in
           let l = ref [] in
           List.iter (fun f ->
-            l := [GuiTools.fit_string_to_pixels (U.utf8_of f.file_name) ~context ~pixels] :: !l
+            l := [GuiTools.fit_string_to_pixels (U.utf8_of f.g_file_name) ~context ~pixels] :: !l
           ) file_list;
           insert_data table !l top
         end
diff -urN src/gtk2/gui/guiMain.ml src/gtk2/gui/guiMain.ml
--- src/gtk2/gui/guiMain.ml	2005-08-05 02:56:13.000000000 +0200
+++ src/gtk2/gui/guiMain.ml	2005-10-31 12:45:25.000000000 +0100
@@ -20,6 +20,7 @@
 open Options
 open GuiTypes2
 open CommonTypes
+open GraphTypes
 
 open GMain
 open GtkBase
@@ -47,7 +48,7 @@
 
 let chmod_config () =
   let base_config = 
-    (Filename.concat CommonOptions.home_dir "mlgui.ini")
+    (Filename.concat M.gui_config_dir "mlgui.ini")
   in
   let save_config =
     base_config ^ ".old"
@@ -80,13 +81,12 @@
 
 let value_reader gui t =
   try
-    
+
      begin
         if !!verbose then lprintf' "MESSAGE RECEIVED: %s\n" 
           (string_of_to_gui t);
       end;
-    
-    
+
     match t with
 
       (* The first message received from the core *)
@@ -134,6 +134,8 @@
         begin
           GuiStatusBar.update_updown s.tcp_upload_rate s.tcp_download_rate;
           GuiStatusBar.update_sharedfiles s.nshared_files s.upload_counter;
+          GuiGraphBase.save_record s.tcp_download_rate GraphDownloads;
+          GuiGraphBase.save_record s.tcp_upload_rate GraphUploads;
           if !G.is_docked
             then begin
               let up = float_of_int s.tcp_upload_rate /. 1024. in
@@ -167,8 +169,8 @@
                 else s.source_files_requested
             in
             let s_new = {s with source_files_requested = files_requested} in
-            GuiDownloads.h_add_source s_new file_num;
-            Hashtbl.replace G.sources source_num s_new
+            GuiDownloads.h_add_source s_new file_num;(* seen *)
+            s.source_files_requested <- s_new.source_files_requested;(* seen *)
           with _ -> 
             GuiCom.send (GuiProto.GetClient_info source_num)
         end
@@ -179,8 +181,8 @@
             let s = Hashtbl.find G.sources source_num in
             let files_requested = List.filter (fun num -> num <> file_num) s.source_files_requested in
             let s_new = {s with source_files_requested = files_requested} in
-            GuiDownloads.h_remove_source s_new file_num;
-            Hashtbl.replace G.sources source_num s_new
+            GuiDownloads.h_remove_source s_new file_num;(* seen *)
+            s.source_files_requested <- s_new.source_files_requested;(* seen *)
           with _ -> 
             GuiCom.send (GuiProto.GetClient_info source_num)
         end
@@ -200,8 +202,8 @@
                 {s with source_availability = (file_num, avail) :: avails}
               with _ -> {s with source_availability = (file_num, avail) :: s.source_availability}
             in
-            GuiDownloads.h_update_source_availability s_new file_num;
-            Hashtbl.replace G.sources source_num s_new
+            GuiDownloads.h_update_source_availability s s_new file_num;(* seen *)
+            s.source_availability <- s_new.source_availability(* seen *)
           with _ -> 
             GuiCom.send (GuiProto.GetClient_info source_num)
         end
@@ -214,10 +216,8 @@
                gui.init.downloads <- false
              end
           );
-          GuiDownloads.file_info
-            {f with file_age = (BasicSocket.last_time () - f.file_age);
-                    file_last_seen = (BasicSocket.last_time () - f.file_last_seen);
-            }
+          let file = Mi.file_info_to_g_file_info f in
+          GuiDownloads.file_info file
         end
 
     | Server_info s ->
@@ -269,10 +269,10 @@
           GuiServers.h_server_update_users user.user_server user.user_num;
           GuiRooms.update_user_info user
         end
-    
+
     | Room_add_user (num, user_num) ->
         GuiRooms.add_room_user num user_num
-    
+
     | Room_remove_user (num, user_num) ->
         GuiRooms.remove_room_user num user_num
 
@@ -317,10 +317,10 @@
           try
             let s = Hashtbl.find G.sources source_num in
             let s_new = {s with source_state = state} in
-            GuiDownloads.h_update_source s_new ;
-            GuiUploads.h_update_uploader s_new;
-            GuiFriends.h_update_friend s_new;
-            Hashtbl.replace G.sources s.source_num s_new
+            GuiDownloads.h_update_source s s_new;(* seen *)
+            GuiUploads.h_update_uploader s s_new;(* seen *)
+            GuiFriends.h_update_friend s s_new;(* seen *)
+            s.source_state <- state(* seen *)
           with _ ->
             begin
               GuiCom.send (GuiProto.GetClient_info source_num)
@@ -332,10 +332,10 @@
           try
             let s = Hashtbl.find G.sources source_num in
             let s_new = {s with source_type = friend_kind} in
-            GuiDownloads.h_update_source s_new ;
-            GuiUploads.h_update_uploader s_new;
-            GuiFriends.h_update_friend s_new;
-            Hashtbl.replace G.sources s.source_num s_new
+            GuiDownloads.h_update_source s s_new;(* seen *)
+            GuiUploads.h_update_uploader s s_new;(* seen *)
+            GuiFriends.h_update_friend s s_new;(* seen *)
+            s.source_type <- friend_kind;(* seen *)
           with _ ->
             begin
               GuiCom.send (GuiProto.GetClient_info source_num)
@@ -346,7 +346,7 @@
         GuiResults.result_info r
 
     | Client_file (source_num , dirname, file_num) ->
-        GuiFriends.add_friend_files (source_num , dirname, file_num)
+        GuiFriends.add_friend_files (source_num , dirname, file_num)(* seen *)
 
     | Client_info client ->
         begin
@@ -357,9 +357,9 @@
              end
           );
           let source = Mi.client_to_source client in
-          let s_new =
-            try
-              let s = Hashtbl.find G.sources source.source_num in
+          try
+            let s = Hashtbl.find G.sources source.source_num in
+            let s_new =
               { source with
                 source_files = s.source_files;
                 source_has_upload = s.source_has_upload;
@@ -368,12 +368,16 @@
                 source_upload_rate = Mi.source_upload_rate source s;
                 source_download_rate = Mi.source_download_rate source s;
               }
-            with _ -> source
-          in
-          GuiDownloads.h_update_source s_new;
-          GuiUploads.h_update_uploader s_new;
-          GuiFriends.h_update_friend s_new;
-          Hashtbl.replace G.sources s_new.source_num s_new
+            in
+            GuiDownloads.h_update_source s s_new;(* seen *)
+            GuiUploads.h_update_uploader s s_new;(* seen *)
+            GuiFriends.h_update_friend s s_new;(* seen *)
+            G.hashtbl_update_sources s s_new;(* seen *)
+          with _ ->
+            begin
+              GuiFriends.h_update_friend source source;(* seen *)
+              Hashtbl.add G.sources source.source_num source(* seen *)
+            end
         end
 
     | Room_message (_, PrivateMessage(num, mes) )
@@ -398,17 +402,6 @@
 
     | (DownloadedFiles _|DownloadFiles _|ConnectedServers _) -> assert false
 
-    | Shared_file_info s ->
-        begin
-          (if gui.init.uploads
-             then begin
-               gui.set_splash_screen M.icon_menu_uploads (Mi.remove_ !M.mW_lb_uploads);
-               gui.init.uploads <- false
-             end
-          );
-          GuiUploads. h_shared_file_info s
-        end
-
     | CleanTables (clients, servers) ->
         begin
           let all_sources = Hashtbl2.to_list G.sources in
@@ -431,6 +424,17 @@
           GuiServers.clean_servers_table servers;
         end
 
+    | Shared_file_info s ->
+        begin
+          (if gui.init.uploads
+             then begin
+               gui.set_splash_screen M.icon_menu_uploads (Mi.remove_ !M.mW_lb_uploads);
+               gui.init.uploads <- false
+             end
+          );
+          GuiUploads.h_shared_file_info (Mi.shared_info_to_shared_file s)
+        end
+
     | Shared_file_upload (num, size, requests) ->
         GuiUploads.h_shared_file_upload num size requests
 
@@ -781,7 +785,7 @@
            true
       );
       GMain.main ();
-      
+
   );
   window#show ()
 
@@ -803,7 +807,7 @@
   G.get_metrics_from_gtk_font_list ();
 
   let quit () = 
-    chmod_config (); 
+    chmod_config ();
     (if !G.is_docked then G.tray.destroy_tray ());
     CommonGlobals.exit_properly 0
   in
@@ -813,38 +817,37 @@
 
   ignore (w#event#connect#delete ~callback:
     (fun _ ->
-       w#misc#hide ();
-       let icon = A.get_icon ~icon:M.icon_type_source_normal ~size:A.MEDIUM () in
-       G.tray.create_tray icon "MLDonkey";
-       true
+       if Autoconf.system = "windows"
+         then begin
+           w#misc#hide ();
+           let icon = A.get_icon ~icon:M.icon_type_source_normal ~size:A.MEDIUM () in
+           G.tray.create_tray icon "MLDonkey";
+         end else begin
+           quit ()
+         end;
+         true
   ));
 
   let main_menu = core_menu gui quit in
   GuiStatusBar.menuitem#set_submenu (main_menu);
 
   G.console_message := (fun s -> GuiConsole.insert s);
-  G.get_files := (fun list ->
-    let l = ref [] in
-    List.iter (fun file_num ->
-      try
-        let (_, i) = GuiDownloads.downloadstore#find_item (File_num file_num) in
-        match i with
-            File f -> l := f :: !l
-          | _ -> ()
-      with _ -> ()
-    ) list;
-    !l
-  );
-
+  if Autoconf.system <> "windows"
+    then begin
+      let icon = A.get_icon ~icon:M.icon_type_source_normal ~size:A.MEDIUM () in
+      G.tray.create_tray icon "MLDonkey";
+    end;
+  let win_hidden = ref false in
   !G.set_systray_callback (fun ev ->
     match ev with
         DOUBLE_CLICKED ->
-           begin
+          if Autoconf.system = "windows"
+            then begin
              (if !!verbose then lprintf' "tray double clicked\n");
              G.tray.destroy_tray ();
              w#misc#show ();
              w#maximize ();
-           end
+            end
       | RBUTTON_CLICKED ->
            begin
              (if !!verbose then lprintf' "tray right clicked\n");
@@ -858,7 +861,19 @@
                      win#show ()
                    end
            end
-      | _ -> ()
+      | LBUTTON_CLICKED ->
+          if Autoconf.system <> "windows"
+            then begin
+             (if !!verbose then lprintf' "tray left clicked\n");
+             if !win_hidden
+               then begin
+                 w#misc#show ();
+                 win_hidden := false
+               end else begin
+                 w#misc#hide ();
+                 win_hidden := true
+               end
+           end
   );
 
   CommonGlobals.do_at_exit (fun _ ->
diff -urN src/gtk2/gui/guiMessages.ml src/gtk2/gui/guiMessages.ml
--- src/gtk2/gui/guiMessages.ml	2005-08-05 02:56:13.000000000 +0200
+++ src/gtk2/gui/guiMessages.ml	2005-10-31 10:07:58.000000000 +0100
@@ -23,7 +23,79 @@
 open Options
 open Gettext
 
-let filename = Filename.concat CommonOptions.home_dir "mlnet_strings"
+let bin_dir = Filename.dirname Sys.argv.(0)
+
+let hidden_dir_prefix =
+  if Autoconf.system = "windows" then "" else "."
+
+let config_dir_basename = hidden_dir_prefix ^ "mldonkey"
+
+let home_dir =
+  match Autoconf.system with
+  | "windows" -> if (Filename.basename bin_dir) = "bin" then Filename.dirname bin_dir else "."
+  | _ -> Filename.concat (try Sys.getenv "HOME" with _ -> ".") config_dir_basename
+
+let file_basedir_pre =
+  try
+    let s = Sys.getenv "MLDONKEY_DIR" in
+    if s = "" then home_dir else Filename2.normalize s
+  with _ -> home_dir
+
+let file_basedir =
+(* Creating dirs does work differently on Windows than Unix.
+   Dirs like c:\b are split down by unix2.safe_mkdir to "c".
+   This function splits the directory name into the drive name
+   and chdir to it before creating directories.
+   Non-absolute paths in $MLDONKEY_DIR do not work as well *)
+  if Sys.file_exists (Filename.concat (Sys.getcwd ()) "mlgui.ini") then
+    "."
+  else
+    if Autoconf.system = "windows" && file_basedir_pre <> home_dir then
+      match String2.split file_basedir_pre ':' with
+      | drive :: directory :: _ ->
+          Unix.chdir (drive ^ ":\\");
+          directory
+      | _ -> lprintf "Please provide an absolute path in MLDONKEY_DIR like d:\\mldonkey, exiting...\n"; exit 2
+    else file_basedir_pre
+
+let _ =
+  if not !CommonGlobals.core_included
+    then begin
+      (try
+         Unix2.safe_mkdir file_basedir
+       with e ->
+         lprintf_nl "Exception (%s) trying to create dir %s"
+           (Printexc2.to_string e) file_basedir;
+         exit 2);
+      Unix2.can_write_to_directory file_basedir;
+      Unix.chdir file_basedir;
+      lprintf_nl "Starting MLGui %s ... " Autoconf.current_version;
+      lprintf_nl "MLGui is working in %s" (Sys.getcwd ())
+    end
+
+
+let dirname_concat dir =
+  match Autoconf.system with
+      "windows" -> Filename.concat file_basedir dir
+    | _ -> file_basedir
+
+let lang_dir = dirname_concat "lang"
+let gui_config_dir = dirname_concat "interface"
+let log_dir = dirname_concat "logs"
+
+let _ =
+  if not !CommonGlobals.core_included
+  then begin
+    let filename =
+      try
+        Sys.getenv "MLGUI_STRINGS"
+      with _ ->
+        Filename.concat lang_dir "mlgui_strings"
+    in
+    Unix2.safe_mkdir (Filename.dirname filename);
+    Unix2.can_write_to_directory (Filename.dirname filename);
+    set_strings_file filename
+  end
 
 let _s x = (* lprintf "searching string %s\n" x; *) GuiUtf8.simple_utf8_of (_s "GuiMessages" x)
 
@@ -270,6 +342,18 @@
 
 (* Graph *)
 
+let gT_lb_time_range = ref ""
+let gT_lb_quarter = ref ""
+let gT_lb_hour = ref ""
+let gT_lb_halfday = ref ""
+let gT_lb_day = ref ""
+let gT_lb_week = ref ""
+let gT_lb_month = ref ""
+let gT_lb_year = ref ""
+let gT_lb_global_downloads = ref ""
+let gT_lb_global_uploads = ref ""
+let gT_lb_file_down_up = ref ""
+
 (* file Window *)
 
 let fW_wt_show_file_details  = ref ""
@@ -715,6 +799,18 @@
 
 (* Graph *)
 
+  gT_lb_time_range := _s_ "Time range";
+  gT_lb_quarter := _s "Quarter";
+  gT_lb_hour := _s "Hour";
+  gT_lb_halfday := _s "Half day";
+  gT_lb_day := _s "Day";
+  gT_lb_week := _s "Week";
+  gT_lb_month := _s "Month";
+  gT_lb_year := _s "Year";
+  gT_lb_global_downloads := _s "Global Downloads";
+  gT_lb_global_uploads := _s "Global Uploads";
+  gT_lb_file_down_up := _s "Files Uploads and Downloads";
+
 (* file Window *)
 
   fW_wt_show_file_details  :=  _s "File details";
@@ -969,8 +1065,9 @@
 
 (* Messages and string constants. *)
 
-let chat_config_file = 
-  Filename.concat CommonOptions.home_dir "chat.ini"
+let _ = Unix2.safe_mkdir gui_config_dir
+
+let chat_config_file = Filename.concat gui_config_dir "chat.ini"
 
 (* {2 Command line messages} *)
 
diff -urN src/gtk2/gui/guiMisc.ml src/gtk2/gui/guiMisc.ml
--- src/gtk2/gui/guiMisc.ml	2005-10-16 22:42:53.000000000 +0200
+++ src/gtk2/gui/guiMisc.ml	2005-10-31 12:04:58.000000000 +0100
@@ -60,12 +60,11 @@
 (*                      To pretty-print a file size                               *)
 (*                                                                                *)
 (**********************************************************************************)
-let ko = 1024l
-let mo = Int32.mul ko ko
+let ko = Int32.of_int 1024
   
 let unit_of_string s =
   match String.lowercase s with
-    "mo" -> mo
+    "mo" -> Int32.mul ko ko
   | "ko" -> ko
   | _ -> Int32.one
 
@@ -503,7 +502,7 @@
 (*************************************************************************)
 
 let uploader_state_to_string state has_upload =
-  if has_upload
+  if has_upload = source_has_upload
     then begin
       match state with
           Connected_downloading _  -> !M.dT_tx_updown
@@ -1269,27 +1268,27 @@
 let client_to_source c =
   let s =
     {
-     source_num = c.client_num;
-     source_network = c.client_network;
+     source_num             = c.client_num;
+     source_network         = c.client_network;
 
-     source_kind = c.client_kind;
-     source_state = c.client_state;
-     source_type = c.client_type;
-     source_tags = c.client_tags;
-     source_name = U.utf8_of c.client_name;
-     source_files = None;
-     source_rating = c.client_rating;
-     source_chat_port = c.client_chat_port;
-     source_connect_time = BasicSocket.last_time () - c.client_connect_time;
-     source_last_seen = BasicSocket.current_time ();
-     source_software = concat_strings c.client_software (concat_strings c.client_emulemod c.client_release);
-     source_downloaded = c.client_downloaded;
-     source_uploaded = c.client_uploaded;
-     source_upload_rate = 0.;
-     source_download_rate = 0.;
-     source_upload = c.client_upload;
-     source_has_upload = false;
-     source_availability = [];
+     source_kind            = c.client_kind;
+     source_state           = c.client_state;
+     source_type            = c.client_type;
+     source_tags            = c.client_tags;
+     source_name            = U.utf8_of c.client_name;
+     source_files           = None;
+     source_rating          = c.client_rating;
+     source_chat_port       = c.client_chat_port;
+     source_connect_time    = BasicSocket.last_time () - c.client_connect_time;
+     source_last_seen       = BasicSocket.current_time ();
+     source_software        = concat_strings c.client_software (concat_strings c.client_emulemod c.client_release);
+     source_downloaded      = c.client_downloaded;
+     source_uploaded        = c.client_uploaded;
+     source_upload_rate     = 0.;
+     source_download_rate   = 0.;
+     source_upload          = c.client_upload;
+     source_has_upload      = source_only;
+     source_availability    = [];
      source_files_requested = [];
     }
   in
@@ -1305,3 +1304,65 @@
   match l with
       [] -> ""
     | uid :: _ -> U.simple_utf8_of (Uid.to_string uid)
+
+(*************************************************************************)
+(*                                                                       *)
+(*                        to_uid_type                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let to_uid_type l =
+  match l with
+      [] -> NoUid
+    | uid :: _ -> Uid.to_uid uid
+
+(*************************************************************************)
+(*                                                                       *)
+(*                        shared_info_to_shared_file                     *)
+(*                                                                       *)
+(*************************************************************************)
+
+let shared_info_to_shared_file si =
+  {
+    g_shared_num       = si.shared_num;
+    g_shared_network   = si.shared_network;
+    g_shared_filename  = si.shared_filename;
+    g_shared_size      = si.shared_size;
+    g_shared_uploaded  = si.shared_uploaded;
+    g_shared_requests  = si.shared_requests;
+    g_shared_uids      = si.shared_uids;
+    g_shared_last_seen = BasicSocket.current_time ();
+  }
+
+(*************************************************************************)
+(*                                                                       *)
+(*                        file_info_to_g_file_info                       *)
+(*                                                                       *)
+(*************************************************************************)
+
+let file_info_to_g_file_info f =
+  {
+    g_file_num             = f.file_num;
+    g_file_network         = f.file_network;
+
+    g_file_comment         = f.file_comment;
+    g_file_name            = f.file_name;
+    g_file_names           = f.file_names;
+    g_file_size            = f.file_size;
+    g_file_downloaded      = f.file_downloaded;
+    g_file_active_sources  = f.file_active_sources;
+    g_file_all_sources     = f.file_all_sources;
+    g_file_state           = f.file_state;
+    g_file_chunks          = f.file_chunks;
+    g_file_availability    = f.file_availability;
+    g_file_sources         = f.file_sources;
+    g_file_download_rate   = f.file_download_rate;
+    g_file_format          = f.file_format;
+    g_file_chunks_age      = f.file_chunks_age;
+    g_file_age             = (BasicSocket.last_time () - f.file_age);
+    g_file_last_seen       = (BasicSocket.last_time () - f.file_last_seen);
+    g_file_priority        = f.file_priority;
+    g_file_uids            = f.file_uids;
+
+    g_file_razorback_stats = None;
+  }
diff -urN src/gtk2/gui/guiNetHtml.ml src/gtk2/gui/guiNetHtml.ml
--- src/gtk2/gui/guiNetHtml.ml	1970-01-01 01:00:00.000000000 +0100
+++ src/gtk2/gui/guiNetHtml.ml	2005-10-14 15:18:04.000000000 +0200
@@ -0,0 +1,653 @@
+(* $Id: nethtml.ml,v 2.9 2005/01/22 10:58:26 stolpmann Exp $
+ * ----------------------------------------------------------------------
+ *
+ *)
+
+open GuiNetHtmlScanner
+open Printf2
+
+type document =
+    Element of (string * (string * string) list * document list)
+  | Data of string
+;;
+
+
+exception End_of_scan;;
+exception Found;;
+
+
+type element_class =         (* What is the class of an element? *)
+  [ `Inline
+  | `Block
+  | `Essential_block
+  | `None
+  | `Everywhere
+  ]
+;;
+
+type model_constraint =      (* The constraint the subelements must fulfill *)
+  [ `Inline
+  | `Block
+  | `Flow         (* = `Inline or `Block *)
+  | `Empty
+  | `Any
+  | `Special
+  | `Elements of string list  (* Enumeration of allowed elements *)
+  | `Or of (model_constraint * model_constraint)
+  | `Except of (model_constraint * model_constraint)
+  | `Sub_exclusions of (string list * model_constraint)
+  ]
+;;
+
+type simplified_dtd =
+    (string * (element_class * model_constraint)) list
+
+
+let ( |. ) a b = `Or(a,b);;
+let ( -. ) a b = `Except(a,b);;
+
+
+let block_elements =
+  (* Only used for exclusions *)
+  [ "p"; "dl"; "div"; "center"; "noscript"; "noframes"; "blockquote"; "form";
+    "isindex"; "hr"; "table"; "fieldset"; "address"; "h1"; "h2"; "h3"; "h4";
+    "h5"; "h6"; "pre"; "ul"; "ol"; "dir"; "menu" ];;
+
+let html40_dtd =
+  [ (* --------- INLINE ELEMENTS ------------ *)
+    (* %fontstyle; *)
+    "tt",                 (`Inline, `Inline);
+    "i",                  (`Inline, `Inline);
+    "b",                  (`Inline, `Inline);
+    "big",                (`Inline, `Inline);
+    "small",              (`Inline, `Inline);
+    (* transitional: *)
+    "u",                  (`Inline, `Inline);
+    "s",                  (`Inline, `Inline);
+    "strike",             (`Inline, `Inline);
+    (* %phrase; *)
+    "em",                 (`Inline, `Inline);
+    "strong",             (`Inline, `Inline);
+    "dfn",                (`Inline, `Inline);
+    "code",               (`Inline, `Inline);
+    "samp",               (`Inline, `Inline);
+    "kbd",                (`Inline, `Inline);
+    "var",                (`Inline, `Inline);
+    "cite",               (`Inline, `Inline);
+    "abbr",               (`Inline, `Inline);
+    "acronym",            (`Inline, `Inline);
+    (* %special; *)
+    "sup",                (`Inline, `Inline);
+    "sub",                (`Inline, `Inline);
+    "span",               (`Inline, `Inline);
+    "bdo",                (`Inline, `Inline);
+    "br",                 (`Inline, `Empty);
+    "a",                  (`Inline, `Sub_exclusions(["a"],`Inline));
+    "img",                (`Inline, `Empty);
+    "object",             (`Inline, (`Flow |. `Elements ["param"]));
+    "script",             (`Inline, `Special);
+    "map",                (`Inline, (`Flow |. `Elements ["area"]));
+    "q",                  (`Inline, `Inline);
+    (* transitional: *)
+    "applet",             (`Inline, (`Flow |. `Elements ["param"]));
+    "font",               (`Inline, `Inline);
+    "basefont",           (`Inline, `Empty);
+    "iframe",             (`Inline, `Flow);
+    (* %formctrl; *)
+    "input",              (`Inline, `Empty);
+    "select",             (`Inline, `Elements ["optgroup"; "option"]);
+    "textarea",           (`Inline, `Elements []);    (* #PCDATA *)
+    "label",              (`Inline, `Sub_exclusions( ["label"],
+						    `Inline));
+    "button",             (`Inline, `Sub_exclusions( ["a"; "input"; "select";
+						     "textarea"; "label";
+						     "button"; "form";
+						     "fieldset"; "isindex";
+						     "iframe"],
+						    `Flow));
+    (* ------------ BLOCK ELEMENTS ----------*)
+    "p",                  (`Block, `Inline);
+    (* %heading; *)
+    "h1",                 (`Block, `Inline); 
+    "h2",                 (`Block, `Inline);
+    "h3",                 (`Block, `Inline);
+    "h4",                 (`Block, `Inline);
+    "h5",                 (`Block, `Inline);
+    "h6",                 (`Block, `Inline);
+    (* %list; *)
+    "ul",                 (`Block, `Elements ["li"]);
+    "ol",                 (`Block, `Elements ["li"]);
+    (* transitional: *)
+    "dir",                (`Block, `Sub_exclusions( block_elements,
+						   `Elements ["li"]));
+    "menu",               (`Block, `Sub_exclusions( block_elements,
+						   `Elements ["li"]));
+    (* %preformatted; *)
+    "pre",                (`Block, `Sub_exclusions( [ "img"; "object"; "applet";
+						      "big"; "small"; "sub"; 
+						      "sup"; "font"; "basefont"],
+						    `Inline ));
+    (* other: *)
+    "dl",                 (`Block, `Elements ["dt"; "dd"]);
+    "div",                (`Block, `Flow);
+    "noscript",           (`Block, `Flow);
+    "blockquote",         (`Block, (`Flow |. `Elements ["script"]));
+                          (* strict DTD has `Block here *)
+    "form",               (`Block, `Sub_exclusions( ["form"],
+						    `Flow |. 
+						       `Elements ["script"]));
+                          (* strict DTD has `Block here *)
+    "hr",                 (`Block, `Empty);
+    "table",              (`Block, `Elements ["caption"; "col"; "colgroup";
+					      "thead"; "tfoot"; "tbody"; "tr"]);
+    "fieldset",           (`Block, (`Flow |. `Elements ["legend"]));
+    "address",            (`Block, `Inline);
+    (* transitional: *)
+    "center",             (`Block, `Flow);
+    "noframes",           (`Block, `Flow);
+    "isindex",            (`Block, `Empty);
+    (* ------------ OTHER ELEMENTS ----------*)
+    "body",               (`None, (`Flow |. `Elements ["script"]));
+                          (* strict DTD has `Block here *)
+    "area",               (`None, `Empty);
+    "link",               (`None, `Empty);
+    "param",              (`None, `Empty);
+    "ins",                (`Everywhere, `Flow);
+    "del",                (`Everywhere, `Flow);
+    "dt",                 (`None, `Inline);
+    "dd",                 (`None, `Flow);
+    "li",                 (`None, `Flow);
+    "optgroup",           (`None, `Elements ["option"]);
+    "option",             (`None, `Elements []);   (* #PCDATA *)
+    "legend",             (`None, `Inline);
+    "caption",            (`None, `Inline);
+    "thead",              (`None, `Elements ["tr"]);
+    "tbody",              (`None, `Elements ["tr"]);
+    "tfoot",              (`None, `Elements ["tr"]);
+    "colgroup",           (`None, `Elements ["col"]);
+    "col",                (`None, `Empty);
+    "tr",                 (`None, `Elements ["th"; "td"]);
+    "th",                 (`None, `Flow);
+    "td",                 (`None, `Flow);
+    "head",               (`None, `Elements ["title"; "base"; "script";
+					     "style"; "meta"; "link";
+					     "object"]);
+    "title",              (`None, `Elements []);   (* #PCDATA *)
+    "base",               (`None, `Empty);
+    "meta",               (`None, `Empty);
+    "style",              (`None, `Special);
+    "html",               (`None, (`Flow |. 
+				       `Elements ["head"; 
+						  "title"; "base"; "script";
+						  "style"; "meta"; "link";
+						  "object";
+						  "body"; "frameset"]));
+    (* transitional: *)
+    "frameset",           (`None, `Elements ["frameset"; "frame"; "noframes"]);
+    "frame",              (`None, `Empty);
+  ]
+;;
+
+
+let relax_dtd dtd =
+  (* Changes (`Inline, `Inline) constraints into (`Inline, `Flow). *)
+  let rec relax_model m =
+    match m with
+	`Inline -> `Flow
+      | `Sub_exclusions(l,m') -> `Sub_exclusions(l,relax_model m')
+      | other -> other
+  in
+  List.map
+    (fun (name, (elclass, elconstr)) ->
+       match elclass with
+	   `Inline ->
+	     (name, (elclass, relax_model elconstr))
+	 | other ->
+	     (name, (elclass, elconstr))
+    )
+    dtd
+;;
+
+
+let essential_blocks dtd elements =
+  (* Changes the passed block elements into essential block elements *)
+  List.map
+    (fun (name, (elclass, elconstr)) ->
+       match elclass with
+	   `Block when List.mem name elements ->
+	     (name, ( `Essential_block, elconstr))
+         | other ->
+	     (name, (elclass, elconstr))
+    )
+    dtd
+;;
+
+
+let relaxed_html40_dtd =
+  essential_blocks
+    (relax_dtd html40_dtd)
+    [ "table"; "ol"; "ul"; "dl" ]
+;;
+
+
+let rec parse_comment buf =
+  let t = scan_comment buf in
+  match t with
+      Mcomment ->
+	let s = Lexing.lexeme buf in
+	s ^ parse_comment buf
+    | Eof ->
+	raise End_of_scan
+    | _ ->
+	(* must be Rcomment *)
+	""
+;;
+
+
+let rec parse_doctype buf =
+  let t = scan_doctype buf in
+  match t with
+      Mdoctype ->
+	let s = Lexing.lexeme buf in
+	s ^ parse_doctype buf
+    | Eof ->
+	raise End_of_scan
+    | _ ->
+	(* must be Rdoctype *)
+	""
+;;
+
+
+let rec parse_pi buf =
+  let t = scan_pi buf in
+  match t with
+      Mpi ->
+	let s = Lexing.lexeme buf in
+	s ^ parse_pi buf
+    | Eof ->
+	raise End_of_scan
+    | _ ->
+	(* must be Rpi *)
+	""
+;;
+
+
+let hashtbl_from_alist l =
+  let ht = Hashtbl.create (List.length l) in
+  List.iter
+    (fun (k, v) ->
+       Hashtbl.add ht k v)
+    l;
+  ht
+;;
+
+
+module S = struct
+  type t = string
+  let compare = (Pervasives.compare : string -> string -> int)
+end
+
+module Strset = Set.Make(S);;
+
+
+let parse_document ?(dtd = html40_dtd) 
+                   ?(return_declarations = false) 
+                   ?(return_pis = false)
+                   ?(return_comments = false) buf =
+  let current_name = ref "" in
+  let current_atts = ref [] in
+  let current_subs = ref [] in
+  let current_excl = ref Strset.empty in      (* current exclusions *)
+  let stack = Stack.create() in
+  let dtd_hash = hashtbl_from_alist dtd in
+
+  let model_of element_name =
+    if element_name = "" then
+      (`Everywhere, `Any)
+    else
+      let extract =
+	function
+	    (eclass, `Sub_exclusions(_,m)) -> eclass, m
+	  | m -> m
+      in
+      try
+	extract(Hashtbl.find dtd_hash element_name)
+      with
+	  Not_found -> (`Everywhere, `Any)
+  in
+
+  let exclusions_of element_name =
+    if element_name = "" then
+      []
+    else
+      let extract =
+	function
+	    (eclass, `Sub_exclusions(l,_)) -> l
+	  | _ -> []
+      in
+      try
+	extract(Hashtbl.find dtd_hash element_name)
+      with
+	  Not_found -> []
+  in
+
+  let is_possible_subelement parent_element parent_exclusions sub_element =
+    let (sub_class, _) = model_of sub_element in
+    let rec eval m =
+      match m with
+	  `Inline     -> sub_class = `Inline
+	| `Block      -> sub_class = `Block  || sub_class = `Essential_block
+	| `Flow       -> sub_class = `Inline || sub_class = `Block ||
+		         sub_class = `Essential_block
+	| `Elements l -> List.mem sub_element l
+	| `Any        -> true
+	| `Or(m1,m2)  -> eval m1 || eval m2
+	| `Except(m1,m2) -> eval m1 && not (eval m2)
+	| `Empty      -> false
+	| `Special    -> false
+	| `Sub_exclusions(_,_) -> assert false
+    in
+    (sub_class = `Everywhere) || (
+	      (not (Strset.mem sub_element parent_exclusions)) &&
+	      let (_, parent_model) = model_of parent_element in
+	      eval parent_model
+	    )
+  in
+
+  let unwind_stack sub_name =
+    (* If the current element is not a possible parent element for sub_name,
+     * search the parent element in the stack.
+     * Either the new current element is the parent, or there was no
+     * possible parent. In the latter case, the current element is the
+     * same element as before.
+     *)
+    let backup = Stack.create() in
+    let backup_name = !current_name in
+    let backup_atts = !current_atts in
+    let backup_subs = !current_subs in
+    let backup_excl = !current_excl in
+    try
+      while not (is_possible_subelement !current_name !current_excl sub_name) do
+	(* Maybe we are not allowed to end the current element: *)
+	let (current_class, _) = model_of !current_name in
+	if current_class = `Essential_block then raise Stack.Empty;
+	(* End the current element and remove it from the stack: *)
+	let grant_parent = Stack.pop stack in
+	Stack.push grant_parent backup;        (* Save it; may we need it *)
+	let (gp_name, gp_atts, gp_subs, gp_excl) = grant_parent in
+	(* If gp_name is an essential element, we are not allowed to close
+	 * it implicitly, even if that violates the DTD.
+	 *)
+	let current = Element (!current_name, !current_atts, 
+			       List.rev !current_subs) in
+	current_name := gp_name;
+	current_atts := gp_atts;
+	current_excl := gp_excl;
+	current_subs := current :: gp_subs
+      done;
+    with
+	Stack.Empty ->
+	  (* It did not work! Push everything back to the stack, and
+	   * resume the old state.
+	   *)
+	  while Stack.length backup > 0 do
+	    Stack.push (Stack.pop backup) stack
+	  done;
+	  current_name := backup_name;
+	  current_atts := backup_atts;
+	  current_subs := backup_subs;
+	  current_excl := backup_excl
+  in
+
+  let parse_atts() =
+    let rec next_no_space() =
+      match scan_element buf with
+	  Space _ -> next_no_space()
+	| t -> t
+    in
+
+    let rec parse_atts_lookahead next =
+      match next with
+	  Relement -> []
+      	| Name n ->
+	    begin match next_no_space() with
+	      	Is ->
+		  begin match next_no_space() with
+		      Name v ->
+		      	(String.lowercase n, v) ::
+			parse_atts_lookahead (next_no_space())
+		    | Literal v ->
+		      	(String.lowercase n, v) ::
+			parse_atts_lookahead (next_no_space())
+		    | Eof ->
+		      	raise End_of_scan
+		    | Relement ->
+		      	(* Illegal *)
+		      	[]
+		    | _ ->
+		      	(* Illegal *)
+		      	parse_atts_lookahead (next_no_space())
+		  end
+	      | Eof ->
+		  raise End_of_scan
+	      | Relement ->
+		  (* <tag name> <==> <tag name="name"> *)
+		  [ String.lowercase n, String.lowercase n ]
+	      | next' ->
+		  (* assume <tag name ... > <==> <tag name="name" ...> *)
+		  ( String.lowercase n, String.lowercase n ) ::
+		  parse_atts_lookahead next'
+	    end
+      	| Eof ->
+	    raise End_of_scan
+      	| _ ->
+	    (* Illegal *)
+	    parse_atts_lookahead (next_no_space())
+    in
+    parse_atts_lookahead (next_no_space())
+  in
+
+  let rec parse_special name =
+    (* Parse until </name> *)
+    match scan_special buf with
+	Lelementend n ->
+	  if String.lowercase n = name then
+	    ""
+	  else
+	    "</" ^ n ^ parse_special name
+      | Eof ->
+	  raise End_of_scan
+      | Cdata s ->
+	  s ^ parse_special name
+      | _ ->
+	  (* Illegal *)
+	  parse_special name
+  in
+
+  let rec skip_element() =
+    (* Skip until ">" *)
+    match scan_element buf with
+	Relement ->
+	  ()
+      | Eof ->
+	  raise End_of_scan
+      | _ ->
+	  skip_element()
+  in
+
+  let rec parse_next() =
+    let t = scan_document buf in
+    match t with
+	Lcomment ->
+	  let comment = parse_comment buf in
+	  if return_comments then
+	    current_subs := (Element("--",["contents",comment],[])) :: !current_subs;
+	  parse_next()
+      | Ldoctype ->
+	  let decl = parse_doctype buf in
+	  if return_declarations then
+	    current_subs := (Element("!",["contents",decl],[])) :: !current_subs;
+	  parse_next()
+      | Lpi ->
+	  let pi = parse_pi buf in
+	  if return_pis then
+	    current_subs := (Element("?",["contents",pi],[])) :: !current_subs;
+	  parse_next()
+      | Lelement name ->
+	  let name = String.lowercase name in
+	  let (_, model) = model_of name in
+	  ( match model with
+		`Empty ->
+		  let atts = parse_atts() in
+		  unwind_stack name;
+		  current_subs := (Element(name, atts, [])) :: !current_subs;
+		  parse_next()
+	      | `Special ->
+		  let atts = parse_atts() in
+		  unwind_stack name;
+		  let data = parse_special name in
+		  (* Read until ">" *)
+		  skip_element();
+		  current_subs := (Element(name, atts, [Data data])) :: !current_subs;
+		  parse_next()
+	      | _ ->
+		  let atts = parse_atts() in
+		  (* Unwind the stack until we find an element which can be
+		   * the parent of the new element:
+		   *)
+		  unwind_stack name;
+		  (* Push the current element on the stack, and this element
+		   * becomes the new current element:
+		   *)
+		  let new_excl = exclusions_of name in
+		  Stack.push 
+		    (!current_name, !current_atts, !current_subs, !current_excl)
+		    stack;
+		  current_name := name;
+		  current_atts := atts;
+		  current_subs := [];
+		  List.iter
+		    (fun xel -> current_excl := Strset.add xel !current_excl)
+		    new_excl;
+		  parse_next()
+	  )
+      | Cdata data ->
+	  current_subs := (Data data) :: !current_subs;
+	  parse_next()
+      | Lelementend name ->
+	  let name = String.lowercase name in
+	  (* Read until ">" *)
+	  skip_element();
+	  (* Search the element to close on the stack: *)
+	  let found = 
+	    (name = !current_name) ||
+	    try
+	      Stack.iter
+		(fun (old_name, _, _, _) ->
+		   if name = old_name then raise Found;
+		   match model_of old_name with
+		       `Essential_block, _ -> raise Not_found;
+			 (* Don't close essential blocks implicitly *)
+		     | _ -> ())
+		stack;
+	      false
+	    with
+		Found -> true
+	      | Not_found -> false
+	  in
+	  (* If not found, the end tag is wrong. Simply ignore it. *)
+	  if not found then
+	    parse_next()
+	  else begin
+	    (* If found: Remove the elements from the stack, and append
+	     * them to the previous element as sub elements
+	     *)
+	    while !current_name <> name do
+	      let old_name, old_atts, old_subs, old_excl = Stack.pop stack in
+	      current_subs := (Element (!current_name, !current_atts,
+					List.rev !current_subs)) :: old_subs;
+	      current_name := old_name;
+	      current_atts := old_atts;
+	      current_excl := old_excl
+	    done;
+	    (* Remove one more element: the element containing the element
+	     * currently being closed.
+	     *)
+	    let old_name, old_atts, old_subs, old_excl = Stack.pop stack in
+	    current_subs := (Element (!current_name, !current_atts,
+				      List.rev !current_subs)) :: old_subs;
+	    current_name := old_name;
+	    current_atts := old_atts;
+	    current_excl := old_excl;
+	    (* Go on *)
+	    parse_next()
+	  end
+      | Eof ->
+	  raise End_of_scan
+      | _ ->
+	  parse_next()
+  in
+  try
+    parse_next();
+    List.rev !current_subs
+  with
+      End_of_scan ->
+	(* Close all remaining elements: *)
+	while Stack.length stack > 0 do
+	  let old_name, old_atts, old_subs, old_excl = Stack.pop stack in
+	  current_subs := Element (!current_name,
+				   !current_atts,
+				   List.rev !current_subs) :: old_subs;
+	  current_name := old_name;
+	  current_atts := old_atts;
+	  current_excl := old_excl
+	done;
+	List.rev !current_subs
+;;
+
+let rec map f doc =
+  match doc with
+      Element(name,atts,subdocs) ->
+	(match name with
+	     "!"
+	   | "?"
+	   | "--" ->
+	       Element(name,atts,map_list f subdocs)
+	   | _ ->
+	       let atts' =
+		 List.map
+		   (fun (aname,aval) ->
+		      aname, f aval
+		   )
+		   atts
+	       in
+	       let subdocs' =  map_list f subdocs in
+	       Element(name,atts',subdocs')
+	)
+    | Data s ->
+	Data(f s)
+and map_list f l = List.map (map f) l;;
+
+let print_document dl =
+  let buf = Buffer.create 1000 in
+  let rec iter _dl ident =
+    let s_ident = String.make ident ' ' in
+    List.iter (fun d ->
+    match d with
+        Element (s, sl, __dl) ->
+          begin
+            Buffer.add_string buf (Printf.sprintf "%sElement: %s\n" s_ident s);
+            List.iter (fun (_s, __s) ->
+              Buffer.add_string buf (Printf.sprintf "%s  %s %s\n" s_ident _s __s);
+            ) sl;
+            iter __dl (ident + 4)
+          end
+      | Data s ->
+          Buffer.add_string buf (Printf.sprintf "%sData: %s\n" s_ident s)
+    ) _dl
+  in
+  iter dl 0;
+  let s = Buffer.contents buf in
+  Buffer.clear buf;
+  Buffer.reset buf;
+  s
diff -urN src/gtk2/gui/guiNetHtml.mli src/gtk2/gui/guiNetHtml.mli
--- src/gtk2/gui/guiNetHtml.mli	1970-01-01 01:00:00.000000000 +0100
+++ src/gtk2/gui/guiNetHtml.mli	2005-10-14 15:15:00.000000000 +0200
@@ -0,0 +1,235 @@
+(* $Id: nethtml.mli,v 2.4 2004/07/08 23:04:25 stolpmann Exp $
+ * ----------------------------------------------------------------------
+ *
+ *)
+
+(** Parsing of HTML *)
+
+
+(** The type [document] represents parsed HTML documents:
+ *
+ * {ul
+ * {- [Element (name, args, subnodes)] is an element node for an element of
+ *   type [name] (i.e. written [<name ...>...</name>]) with arguments [args]
+ *   and subnodes [subnodes] (the material within the element). The arguments
+ *   are simply name/value pairs. Entity references (something like [&xy;])
+ *   occuring in the values are {b not} resolved.
+ *
+ *   Arguments without values (e.g. [<select name="x" multiple>]: here,
+ *   [multiple] is such an argument) are represented as [(name,name)], i.e. the
+ *   name is also returned as value.
+ *
+ *   As argument names are case-insensitive, the names are all lowercase.}
+ * {- [Data s] is a character data node. Again, entity references are contained
+ *   as such and not as what they mean.}
+ * }
+ *
+ * Character encodings: The parser is restricted to ASCII-compatible
+ * encodings (see the function {!Netconversion.is_ascii_compatible} for
+ * a definition). In order to read other encodings, the text must be
+ * first recoded to an ASCII-compatible encoding (example below).
+ * Names of elements and attributes must additionally be ASCII-only.
+ *)
+type document =
+    Element of (string  *  (string*string) list  *  document list)
+  | Data of string
+;;
+
+
+(** We also need a type that declares how to handle the various tags.
+ * This is called a "simplified DTD", as it is derived from SGML DTDs,
+ * but simplified to the extent used in the HTML definition.
+ *)
+
+(* Now follows the type definition of simplified DTDs. *)
+
+type element_class =         (* What is the class of an element? *)
+  [ `Inline
+  | `Block
+  | `Essential_block
+  | `None
+  | `Everywhere
+  ]
+;;
+(** Element classes are a property used in the HTML DTD. For our purposes,
+ * we define element classes simply as an enumeration:
+ * - [`Inline] is the class of inline HTML elements
+ * - [`Block] is the class of block HTML elements
+ * - [`Essential_block] is a sub-class of [`Block] with the additional
+ *   property that every start tag must be explicitly ended
+ * - [`None] means that the members of the class are neither block nor
+ *   inline elements, but have to be handled specially
+ * - [`Everywhere] means that the members of the class can occur everywhere, 
+ *   regardless of whether a constraint allows it or not.
+ *)
+
+
+type model_constraint =      (* The constraint the subelements must fulfill *)
+  [ `Inline
+  | `Block
+  | `Flow                                            (* = `Inline or `Block *)
+  | `Empty
+  | `Any
+  | `Special
+  | `Elements of string list             (* Enumeration of allowed elements *)
+  | `Or of (model_constraint * model_constraint)
+  | `Except of (model_constraint * model_constraint)
+  | `Sub_exclusions of (string list * model_constraint)
+  ]
+;;
+(** Model constraints define the possible sub elements of an element:
+ * - [`Inline]: The sub elements must belong to the class [`Inline]
+ * - [`Block]: The sub elements must be members of the classes [`Block] or 
+ *   [`Essential_block]
+ * - [`Flow]: The sub elements must belong to the classes [`Inline], [`Block],
+ *   or [`Essential_block]
+ * - [`Empty]: There are no sub elements
+ * - [`Any]: Any sub element is allowed
+ * - [`Special]: The element has special content (e.g. [<script>]).
+ *   Functionally equivalent to [`Empty]
+ * - [`Elements l]: Only these enumerated elements may occur as sub elements
+ * - [`Or(m1,m2)]: One of the constraints [m1] or [m2] must hold
+ * - [`Except(m1,m2)]: The constraint [m1] must hold, and [m2] must not hold
+ * - [`Sub_exclusions(l,m)]: The constraint [m] must hold; furthermore, 
+ *   the elements enumerated in list [l] are not allowed as direct or
+ *   indirect subelements, even if [m] or the model of a subelement would
+ *   allow them. The difference to [`Except(m, `Elements l)] is that the
+ *   exclusion is inherited to the subelements. The [`Sub_exclusions]
+ *   expression must be toplevel, i.e. it must not occur within an [`Or], 
+ *   [`Except], or another ['Sub_exclusions] expression.
+ *
+ * Note that the members of the class [`Everywhere] are allowed everywhere,
+ * regardless of whether the model constraint allows them or not.
+ *
+ * Note that certain aspects are not modeled:
+ * - [#PCDATA]: We do not specify where PCDATA is allowed and where not.
+ * - Order, Number: We do neither specify in which order the sub elements must
+ *   occur nor how often they can occur
+ * - Inclusions: DTDs may describe that an element extraordinarily
+ *   allows a list of elements in all sub elements. 
+ * - Optional tags: Whether start or end tags can be omitted (to some extent,
+ *   this can be expressed with [`Essential_block], however)
+ *)
+
+type simplified_dtd =
+    (string * (element_class * model_constraint)) list;;
+(** A [simplified_dtd] is an associative list of tuples
+ *  [(element_name, (element_class, constraint))]: For every [element_name]
+ *  it is declared that it is a member of [element_class], and that
+ *  the sub elements must satisfy [constraint].
+ *
+ *  It is not allowed to have several entries for the same element.
+ *)
+
+val html40_dtd : simplified_dtd
+  (** The (transitional) HTML 4.0 DTD, expressed as [simplified_dtd] *)
+
+val relaxed_html40_dtd : simplified_dtd
+  (** A relaxed version of the HTML 4.0 DTD that matches better common
+   * practice. In particular, this DTD additionally allows that inline
+   * elements may span blocks. For example, 
+   * {[ <B>text1 <P>text2 ]}
+   * is parsed as
+   * {[ <B>text1 <P>text2</P></B> ]}
+   * and not as
+   * {[ <B>text1 </B><P>text2</P> ]}
+   * \- the latter is more correct (and parsed by [html40_dtd]), but is not what
+   * users expect.
+   *
+   * Note that this is still not what many browsers implement. For example,
+   * Netscape treats most inline tags specially: [<B>] switches bold on,
+   * [</B>] switches bold off. For example,
+   * {[ <A href='a'>text1<B>text2<A href='b'>text3 ]}
+   * is parsed as
+   * {[ <A href='a'>text1<B>text2</B></A><B><A href='b'>text3</A></B> ]}
+   * \- there is an extra [B] element around the second anchor! (You can
+   * see what Netscape parses by loading a page into the "Composer".)
+   * IMHO it is questionable to consider inline tags as switches because
+   * this is totally outside of the HTML specification, and browsers may
+   * differ in that point.
+   *
+   * Furthermore, several elements are turned into essential blocks:
+   * [TABLE], [UL], [OL], and [DL]. David Fox reported a problem with structures
+   * like:
+   * {[ <TABLE><TR><TD><TABLE><TR><TD>x</TD></TD></TR></TABLE>y</TD></TR></TABLE> ]}
+   * i.e. the [TD] of the inner table has two end tags. Without additional
+   * help, the second [</TD>] would close the outer table cell. Because of
+   * this problem, tables are now essential meaning that it is not allowed
+   * to implicitly add a missing [</TABLE>]; every table element has to
+   * be explicitly ended. This rule seems to be what many browsers implement.
+   *)
+
+val parse_document : ?dtd:simplified_dtd ->            (* default: html40_dtd *)
+                     ?return_declarations:bool ->      (* default: false *)
+                     ?return_pis:bool ->               (* default: false *)
+                     ?return_comments:bool ->          (* default: false *)
+                     Lexing.lexbuf ->
+                       document list
+  (** Parses the HTML document from a [lexbuf] and returns it. 
+   * 
+   * @param dtd specifies the DTD to use. By default, [html40_dtd] is used which
+   *   bases on the transitional HTML 4.0 DTD
+   * @param return_declarations if set, the parser returns [<!...>] declarations
+   *   as [Element("!",["contents",c],[])] nodes, where [c] is the string inside
+   *   [<!] and [>]. - By default, declarations are skipped.
+   * @param return_pis if set, the parser returns [<?...>] (or [<?...?>]) processing
+   *   instructions as [Element("?",["contents",c],[])] nodes, where [c] is the
+   *   string inside [<?] and [>] (or [?>]). - By default, processing instructions
+   *   are skipped.
+   * @param return_comments if set, the parser returns [<!--] .... [-->] comments
+   *   as [Element("--",["contents",c],[])] nodes, where [c] is the string inside
+   *   [<!--] and [-->]. - By default, comments are skipped.
+   *)
+
+(** {b Note on XHTML}
+ *
+ * The parser can read XHTML, as long as the following XML features are not
+ * used:
+ * - Internal DTD subset, i.e. [<!DOCTYPE html ... [ ... ]>]
+ * - External entities
+ * - [<!\[CDATA\[]
+ * - [<!\[INCLUDE\[]
+ * - [<!\[IGNORE\[]
+ *
+ * The following XML features are ok:
+ * - Processing instructions
+ * - Empty elements (e.g. [<br/>]) as long as the element is declared as 
+ *   [`Empty].
+ *)
+
+(** {b Note on Character Encodings}
+ *
+ * The parser can only read character streams that are encoded in an ASCII-
+ * compatible way. For example, it is possible to read a UTF-8-encoded
+ * stream, but not a UTF-16-encoded stream. All bytes between 1 and 127
+ * are taken as ASCII, and other bytes are ignored (copied from input
+ * to output).
+ *
+ * Non-ASCII-compatible streams must be recoded first. For example, to
+ * read a UTF-16-encoded netchannel [ch], use:
+ *
+ * {[
+ * let p = 
+ *   new Netconversion.recoding_pipe ~in_enc:`Enc_utf16 ~out_enc:`Enc_utf8 () in
+ * let ch' =
+ *   new Netchannels.input_filter ch p in
+ * let doc =
+ *   Nethtml.parse ch' in
+ * ch' # close_in();
+ * ch # close_in();
+ * ]}
+ *)
+
+val map_list : (string -> string) -> document list -> document list
+  (** [map_list f doclst]:
+   * Applies [f] to all attribute values and data strings (except
+   * the attributes of "?", "!", or "--" nodes). 
+   *
+   * This can be used to change the text encoding of a parsed document:
+   * {[
+   * let doc' = map_list String.lowercase doc
+   * ]}
+   * converts all text data to lowercase characters. 
+   *)
+
+val print_document : document list -> string
diff -urN src/gtk2/gui/guiNetHtmlScanner.mll src/gtk2/gui/guiNetHtmlScanner.mll
--- src/gtk2/gui/guiNetHtmlScanner.mll	1970-01-01 01:00:00.000000000 +0100
+++ src/gtk2/gui/guiNetHtmlScanner.mll	2005-07-26 06:43:36.000000000 +0200
@@ -0,0 +1,164 @@
+(* $Id: nethtml_scanner.mll,v 2.2 2002/07/06 16:24:44 stolpmann Exp $
+ * ----------------------------------------------------------------------
+ *
+ *)
+
+{
+  type token =
+      Lcomment  (* <!-- *)
+    | Rcomment  (* --> *)
+    | Mcomment  (* within comment *)
+    | Ldoctype  (* <! *)
+    | Rdoctype  (* > *)
+    | Mdoctype  (* within declaration *)
+    | Lpi       (* <? *)
+    | Rpi       (* ?> or > *)
+    | Mpi       (* within processing instruction *)
+    | Lelement of string
+    | Lelementend of string
+    | Relement
+    | Cdata of string 
+    | Space of int
+    | Name of string
+    | Is
+    | Literal of string
+    | Other
+    | Eof
+}
+
+(* Simplified rules: Only ASCII is recognized as character set *)
+
+let letter = ['A'-'Z' 'a'-'z' ]
+let digit = ['0'-'9']
+let hexdigit = ['0'-'9' 'A'-'F' 'a'-'f']
+let namechar = letter | digit | '.' | ':' | '-' | '_'
+let name = ( letter | '_' | ':' ) namechar*
+let nmtoken = namechar+
+let ws = [ ' ' '\t' '\r' '\n' ]
+let string_literal1 = '"' [^ '"' ]* '"'
+let string_literal2 = "'" [^ '\'' ]* "'"
+let string_literal3 = [^ '"' '\'' '>' '=' ' ' '\t' '\n' '\r' ]+
+
+(* This following rules reflect HTML as it is used, not the SGML
+ * rules.
+ *)
+
+rule scan_document = parse
+  | "<!--"
+      { Lcomment }
+  | "<!"
+      { Ldoctype }
+  | "<?"
+      { Lpi }
+  | "<" name
+      { let s = Lexing.lexeme lexbuf in
+	Lelement (String.sub s 1 (String.length s - 1))
+      }
+  | "</" name
+      { let s = Lexing.lexeme lexbuf in
+	Lelementend (String.sub s 2 (String.length s - 2))
+      }
+  | "<"                (* misplaced "<" *)
+      { Cdata "<" }
+  | eof
+      { Eof }
+  | [^ '<' ]+
+      { Cdata (Lexing.lexeme lexbuf)}
+
+and scan_special = parse
+  | "</" name 
+      { let s = Lexing.lexeme lexbuf in
+	Lelementend (String.sub s 2 (String.length s - 2))
+      }
+  | "<"
+      { Cdata "<" }
+  | eof
+      { Eof }
+  | [^ '<' ]+
+      { Cdata (Lexing.lexeme lexbuf)}
+
+
+and scan_comment = parse
+  | "-->"
+      { Rcomment }  (* FIXME: There may be any number of ws between -- and > *)
+  | "-"
+      { Mcomment }
+  | eof
+      { Eof }
+  | [^ '-']+
+      { Mcomment }
+
+and scan_doctype = parse
+  | ">"                   (* Occurence in strings, and [ ] brackets ignored *)
+      { Rdoctype }
+  | eof
+      { Eof }
+  | [^ '>' ] +
+      { Mdoctype }
+
+and scan_pi = parse
+  | "?>"
+      { Rpi }
+  | ">"
+      { Rpi }
+  | eof
+      { Eof }
+  | '?' 
+      { Mpi }
+  | [^ '>' '?' ] +
+      { Mpi }
+
+and scan_element = parse
+  | ">"
+      { Relement }
+  | ws+
+      { Space (String.length (Lexing.lexeme lexbuf)) }
+  | name
+      { Name (Lexing.lexeme lexbuf) }
+  | "="
+      { Is }
+  | string_literal1
+      { let s = Lexing.lexeme lexbuf in
+	Literal (String.sub s 1 (String.length s - 2)) 
+      }
+  | string_literal2
+      { let s = Lexing.lexeme lexbuf in
+	Literal (String.sub s 1 (String.length s - 2)) 
+      }
+  | string_literal3
+      { Literal (Lexing.lexeme lexbuf) }
+  | eof
+      { Eof }
+  | _
+      { Other }
+
+(* ======================================================================
+ * History:
+ * 
+ * $Log: nethtml_scanner.mll,v $
+ * Revision 2.2  2002/07/06 16:24:44  stolpmann
+ * 	Change: Only ASCII letters are accepted in names.
+ *
+ * Revision 2.1  2001/09/14 14:22:34  stolpmann
+ * 	Initial revision (sourceforge)
+ *
+ *
+ * ======================================================================
+ * Revision 1.4  2001/07/15 14:18:34  gerd
+ * 	Attribute parsing is now less strict.
+ *
+ * Revision 1.3  2001/06/08 16:25:27  gerd
+ * 	Bugfix: </SCRIPT> is now recognized (thanks to David Fox)
+ * 	The parser may now return comments, declarations, and processing
+ * instructions if requested to do so
+ * 	The parser accepts xhtml to some extent
+ * 	Now exported: parse_document.
+ *
+ * Revision 1.2  2001/04/24 19:27:00  gerd
+ * 	Added a FIXME comment
+ *
+ * Revision 1.1  2000/03/03 01:07:25  gerd
+ * 	Initial revision.
+ *
+ * 
+ *)
diff -urN src/gtk2/gui/guiNetworks.ml src/gtk2/gui/guiNetworks.ml
--- src/gtk2/gui/guiNetworks.ml	2005-03-02 20:45:11.000000000 +0100
+++ src/gtk2/gui/guiNetworks.ml	2005-10-23 14:27:33.000000000 +0200
@@ -50,18 +50,6 @@
   (if !!verbose then lprintf' "Enable network %d %b\n" num b);
   GuiCom.send (EnableNetwork (num, b))
 
-let update_networks () = ()
-(*
-TODO :
-  the core should update the values ... but it doesn't
-    Hashtbl.iter (fun num n ->
-      (* Printf.printf "Update network %d %b\n" num n.net_enabled;
-      flush stdout; *)
-      if n.net_enabled
-        then GuiCom.send (GetNetworkInfo num)
-  ) G.networks
-*)
-
 (*************************************************************************)
 (*                                                                       *)
 (*                         Global tables                                 *)
@@ -334,12 +322,4 @@
          n := 0;
        end
   ) net_list;
-  (*
-  networks_timerID := Timeout.add ~ms:6000 ~callback:
-    (fun _ ->
-      (* Printf.printf "Update networks\n";
-      flush stdout; *)
-      update_networks ();
-      true);
-*)
   vbox#coerce
diff -urN src/gtk2/gui/guiOptions.ml src/gtk2/gui/guiOptions.ml
--- src/gtk2/gui/guiOptions.ml	2005-08-05 02:56:13.000000000 +0200
+++ src/gtk2/gui/guiOptions.ml	2005-10-23 14:35:49.000000000 +0200
@@ -22,6 +22,7 @@
 open Gettext
 open Options
 open CommonGlobals
+open GraphTypes
 
 module U = GuiUtf8
 
@@ -40,10 +41,10 @@
 module M = GuiMessages
 module C = GuiColumns
 
-let _ = Unix2.safe_mkdir CommonOptions.home_dir
-  
-let gui_ini = create_options_file 
-    (Filename.concat CommonOptions.home_dir "mlgui.ini")
+let _ = Unix2.safe_mkdir GuiMessages.gui_config_dir
+
+let gui_ini = create_options_file
+    (Filename.concat GuiMessages.gui_config_dir "mlgui.ini")
 
 
 
@@ -72,6 +73,38 @@
     define_option_class "Toolbar" 
     value_to_tbstyle tbstyle_to_value
 
+let graphtime_to_string gt =
+  match gt with
+      GraphHour -> "hour"
+    | GraphHalfDay -> "half_day"
+    | GraphDay -> "day"
+    | GraphWeek -> "week"
+    | GraphMonth -> "month"
+    | GraphYear -> "year"
+    | _ -> "quarter"
+
+let string_to_graphtime s =
+  match s with
+      "hour" -> GraphHour
+    | "half_day" -> GraphHalfDay
+    | "day" -> GraphDay
+    | "week" -> GraphWeek
+    | "month" -> GraphMonth
+    | "year" -> GraphYear
+    | _ -> GraphQuarter
+
+let value_to_graphtime v =
+  match v with
+      StringValue s -> string_to_graphtime s
+    | _ -> GraphQuarter
+
+let graphtime_to_value (gt : graph_time) =
+  StringValue (graphtime_to_string gt)
+
+let (graphtime_option : graph_time option_class) =
+    define_option_class "GraphTime" 
+    value_to_graphtime graphtime_to_value
+
 let time_option =
     define_option_class "Time"
     value_to_int int_to_value
@@ -113,6 +146,26 @@
 
 let current_section = mlgui_section
 
+(* {Connection} *)
+
+let gtk_connection_http_proxy_server = define_option current_section
+    ["gtk_connection_http_proxy_server"]
+    ~desc:"HTTP proxy server"
+    "Direct HTTP queries to HTTP proxy"
+    string_option ""
+
+let gtk_connection_http_proxy_port = define_option current_section
+    ["gtk_connection_http_proxy_port"]
+    ~desc:"HTTP proxy server port"
+    "Port of HTTP proxy"
+    int_option 8080
+
+let gtk_connection_http_use_proxy = define_option current_section
+    ["gtk_connection_http_use_proxy"]
+    ~desc:"Enable Proxy server"
+    "Direct TCP connections to HTTP proxy (the proxy should support CONNECT)"
+    bool_option false
+
 (* {Client} *)
 
 let gtk_client_login = define_option current_section
@@ -246,33 +299,43 @@
     font_option "sans 16"
 
 
-(* TODO : Graph options! *)
-
 (* {2 Graph Options} *)
-(*
-let gtk_graph_time_range =  define_option current_section 
-    ["gtk_graph_time_range"]
-    ~desc:"Time range"
-    "Set the time range for both the uploads and downloads graphs"
-    time_option 21600
+
+let gtk_graph_time_downloads =  define_option current_section
+    ["gtk_graph_time_downloads"]
+    ~desc:"Time range to view the global downloads"
+    "Set the time range for the downloads graph"
+    graphtime_option GraphQuarter
+
+let gtk_graph_time_uploads =  define_option current_section
+    ["gtk_graph_time_uploads"]
+    ~desc:"Time range to view the global uploads"
+    "Set the time range for the uploads graph"
+    graphtime_option GraphQuarter
+
+let gtk_graph_time_file =  define_option current_section
+    ["gtk_graph_time_file"]
+    ~desc:"Time range to view one-file downloads and uploads"
+    "Set the time range to display the uploads and downloads of one file in the graph tab"
+    graphtime_option GraphQuarter
 
 let gtk_graph_font = define_option current_section
     ["gtk_graph_font"]
     ~desc:"Font"
     "Set the font to display texts in both the uploads and downloads graphs" 
-    font_option "sans 8"
+    font_option "sans 7"
 
 let gtk_graph_background =  define_option current_section 
     ["gtk_graph_background"]
     ~desc:"Background color"
     "Set the background color for both the uploads and downloads graphs" 
-    color_option "#454b55"
+    color_option "#000000"
 
 let gtk_graph_grid =  define_option current_section 
     ["gtk_graph_grid"]
     ~desc:"Grid color"
     "Set the color of the grid for both the uploads and downloads graphs" 
-    color_option "#fffcc6"
+    color_option "#484848"
 
 let gtk_graph_download = define_option current_section 
     ["gtk_graph_download"]
@@ -286,22 +349,15 @@
     "Set the foreground color of the upload rate"
     color_option "#6eec8b"
 
-let gtk_graph_download_av = define_option current_section 
-    ["gtk_graph_download_av"]
-    ~desc:"Average downloads color"
-    "Set the foreground color of the average download rate" 
-    color_option "#2d62c1"
-
-let gtk_graph_upload_av = define_option current_section 
-    ["gtk_graph_upload_av"]
-    ~desc:"Average uploads color"
-    "Set the foreground color of the average upload rate"
-    color_option "#61b722"
-*)
-
 
 (* {Misc} *)
 
+let gtk_misc_display_razorback_stats = define_option current_section
+    ["gtk_misc_display_razorback_stats"]
+    ~desc:"Display razorback statistics"
+    "Check to display file information from http://stats.razorback2.com in the downloads page"
+    bool_option true
+
 let gtk_misc_relative_availability = define_option current_section 
     ["gtk_misc_relative_availability"]
     ~desc:"Use relative % availability"
@@ -311,7 +367,7 @@
 let gtk_misc_files_auto_expand_depth = define_option current_section 
     ["gtk_misc_files_auto_expand_depth"]
     ~desc:"Files auto-expand depth"
-    "The depth to which the directories of a friend are automatically expanded" 
+    "The depth to which the directories of a friend are automatically expanded"
     int_option 3
 
 let gtk_misc_use_availability_height = define_option current_section 
@@ -667,6 +723,12 @@
     "Debug module GuiConsole"
     bool_option false
 
+let gtk_verbose_graphbase = define_expert_option current_section
+    ["gtk_verbose_graphbase"]
+    ~desc:"verbose graphbase"
+    "Debug module GuiGraphBase"
+    bool_option false
+
 let gtk_verbose_graph = define_expert_option current_section
     ["gtk_verbose_graph"]
     ~desc:"verbose graph"
diff -urN src/gtk2/gui/guiQueries.ml src/gtk2/gui/guiQueries.ml
--- src/gtk2/gui/guiQueries.ml	2005-03-02 20:45:11.000000000 +0100
+++ src/gtk2/gui/guiQueries.ml	2005-10-12 00:08:49.000000000 +0200
@@ -369,10 +369,14 @@
   qr.g_query_label <- Some label;
   qr.g_query_waiting_label <- Some query_status;
   let on_closure () =
+    let results = GuiResults.keys_to_results (qr.g_query_result#all_items ()) in
+    List.iter (fun r ->
+      Hashtbl.remove G.results r.res_num;
+    ) results;
     close_query qr.g_query_num true;
     qr.g_query_result#clear ();
     main_evbox#destroy ();
-    Hashtbl.remove qresults qr.g_query_num
+    Hashtbl.remove qresults qr.g_query_num;
   in
   GuiTools.add_key ~key:GdkKeysyms._w ~target:main_evbox ~f:on_closure ~mods:[`CONTROL] ();
   ignore (evbox#event#connect#button_press ~callback:
@@ -919,7 +923,6 @@
     try
       let r = Hashtbl.find G.results result_num in
       ignore (qr.g_query_result#add_item r);
-      Hashtbl.remove G.results result_num; (* remove the result from G.users once it is stored *)
       let text = Printf.sprintf "%s (%d)" qr.g_query_desc qr.g_query_result#nitems in
       match qr.g_query_label with
           None -> ()
diff -urN src/gtk2/gui/guiResults.ml src/gtk2/gui/guiResults.ml
--- src/gtk2/gui/guiResults.ml	2005-04-04 21:36:46.000000000 +0200
+++ src/gtk2/gui/guiResults.ml	2005-09-17 14:15:59.000000000 +0200
@@ -42,19 +42,66 @@
 
 (*************************************************************************)
 (*                                                                       *)
+(*                         result_num                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let result_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         result_of_key                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let result_of_key key =
+  try
+    let num = result_num key in
+    Hashtbl.find G.results num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_results                               *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_results keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = result_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         result_key                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let result_key result_num =
+  Printf.sprintf "%d" result_num
+
+(*************************************************************************)
+(*                                                                       *)
 (*                         message to the core                           *)
 (*                                                                       *)
 (*************************************************************************)
 
 let download sel () =
+  let l = keys_to_results sel in
   List.iter (fun r -> 
     GuiCom.send (Download_query ([r.res_name], r.res_num, false))
-  ) sel
+  ) l
 
 let force_download sel () =
+  let l = keys_to_results sel in
   List.iter (fun r -> 
     GuiCom.send (Download_query ([r.res_name], r.res_num, true))
-  ) sel
+  ) l
 
 (*************************************************************************)
 (*                                                                       *)
@@ -62,7 +109,7 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let result_menu (sel : res_info list) =
+let result_menu sel =
   match sel with
       [] -> []
     | _ ->
@@ -77,9 +124,11 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let filter_result (r : res_info) =
-  not (List.memq r.res_network !G.networks_filtered)
-
+let filter_result k =
+  try
+    let r = result_of_key k in
+    not (List.memq r.res_network !G.networks_filtered)
+  with _ -> true
 
 
 module ResultList(Res:
@@ -114,10 +163,9 @@
     module Column = GuiColumns.Result
 
     type item = res_info
-    type key = int
 
     let columns = Res.columns
-    let get_key = (fun r -> r.res_num)
+    let get_key = (fun r -> result_key r.res_num)
     let module_name = Res.module_name
 
   end)
@@ -322,8 +370,11 @@
 (*                                                                       *)
 (*************************************************************************)
 
-      method sort_items c r1 r2 =
-        match c with
+      method sort_items c k1 k2 =
+        try
+          let r1 = result_of_key k1 in
+          let r2 = result_of_key k2 in
+          match c with
             Col_result_name -> compare (String.lowercase r1.res_name) (String.lowercase r2.res_name)
           | Col_result_uid -> compare r1.res_uid r2.res_uid
           | Col_result_size -> compare r1.res_size r2.res_size
@@ -337,7 +388,7 @@
           | Col_result_comment -> compare r1.res_comment r2.res_comment
           | Col_result_network -> compare r1.res_network r2.res_network
           | Col_result_completesources -> compare r1.res_completesources r2.res_completesources
-
+        with _ -> 0
 
 (*************************************************************************)
 (*                                                                       *)
@@ -346,9 +397,10 @@
 (*************************************************************************)
 
     method force_update_icons () =
-      List.iter (fun r ->
+      List.iter (fun k ->
         try
-          let (row, _) = self#find_item r.res_num in
+          let r = result_of_key k in
+          let row = self#find_row k in
           r.res_network_pixb <- Mi.network_pixb r.res_num ~size:A.SMALL ();
           r.res_name_pixb <- Mi.file_type_of_name r.res_name ~size:A.SMALL;
           store#set ~row ~column:result_network_pixb r.res_network_pixb;
diff -urN src/gtk2/gui/guiRooms.ml src/gtk2/gui/guiRooms.ml
--- src/gtk2/gui/guiRooms.ml	2005-03-02 20:45:11.000000000 +0100
+++ src/gtk2/gui/guiRooms.ml	2005-09-17 14:44:45.000000000 +0200
@@ -47,6 +47,51 @@
 
 (*************************************************************************)
 (*                                                                       *)
+(*                         room_num                                      *)
+(*                                                                       *)
+(*************************************************************************)
+
+let room_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         room_of_key                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let room_of_key key =
+  try
+    let num = room_num key in
+    Hashtbl.find G.rooms num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_rooms                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_rooms keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = room_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         room_key                                      *)
+(*                                                                       *)
+(*************************************************************************)
+
+let room_key room_num =
+  Printf.sprintf "%d" room_num
+
+(*************************************************************************)
+(*                                                                       *)
 (*                         Global tables                                 *)
 (*                                                                       *)
 (*************************************************************************)
@@ -89,10 +134,9 @@
   module Column = GuiColumns.Room
 
   type item = room_info
-  type key = int
 
   let columns = O.rooms_columns
-  let get_key = (fun r -> r.room_num)
+  let get_key = (fun r -> room_key r.room_num)
   let module_name = "Rooms"
 
 end)
@@ -209,12 +253,16 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method sort_items c ro1 ro2 =
-      match c with
+    method sort_items c k1 k2 =
+      try
+        let ro1 = room_of_key k1 in
+        let ro2 = room_of_key k2 in
+        match c with
           Col_room_name -> compare (String.lowercase ro1.room_name) (String.lowercase ro2.room_name)
         | Col_room_nusers -> compare ro1.room_nusers ro2.room_nusers
         | Col_room_state -> compare ro1.room_state ro2.room_state
         | Col_room_network -> compare ro1.room_network ro2.room_network
+      with _ -> 0
 
 (*************************************************************************)
 (*                                                                       *)
@@ -223,9 +271,10 @@
 (*************************************************************************)
 
     method force_update_icons () =
-      List.iter (fun ro ->
+      List.iter (fun k ->
         try
-          let (row, _) = self#find_item ro.room_num in 
+          let row = self#find_row k in
+          let ro = room_of_key k in
           store#set ~row ~column:room_network_pixb (Mi.network_pixb ro.room_network ~size:A.SMALL ());
           store#set ~row ~column:room_name_pixb (Mi.room_state_to_icon ro.room_state ~size:A.SMALL);
         with _ -> ()
@@ -279,11 +328,12 @@
 (*************************************************************************)
 
 let close_open_room sel () =
+  let l = keys_to_rooms sel in
   List.iter (fun ro ->
     match ro.room_state with
         RoomOpened -> GuiCom.send (SetRoomState (ro.room_num, RoomClosed))
       | _ -> GuiCom.send (SetRoomState (ro.room_num, RoomOpened))
-  ) sel
+  ) l
 
 let on_entry_return num s =
   GuiCom.send (SendMessage (num, PublicMessage (0, s)))
@@ -297,7 +347,7 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let room_menu (sel : room_info list) =
+let room_menu sel =
   match sel with
       [] -> []
     | _ ->
@@ -311,7 +361,7 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let on_select_room (sel : room_info list) =
+let on_select_room sel =
   try
     userstore#clear ();
     update_users_label ();
@@ -323,28 +373,29 @@
     in
     match sel with
         [] -> (if !!verbose then lprintf' "No room selected\n")
-      | ro :: tail ->
+      | k :: tail ->
           begin
-            current_room := Some ro.room_num;
-            match ro.room_state with
-                RoomOpened  ->
-                  begin
-                    List.iter (fun user_num ->
+            try
+              let ro = room_of_key k in
+              current_room := Some ro.room_num;
+              match ro.room_state with
+                  RoomOpened  ->
+                    begin
+                      List.iter (fun user_num ->
+                        try
+                          let u = Hashtbl.find G.users user_num in
+                          ignore (userstore#add_item u);
+                        with _ -> get_user_info user_num
+                      ) ro.room_users;
+                      update_users_label ();
                       try
-                        let u = Hashtbl.find G.users user_num in
-                        ignore (userstore#add_item u);
-                      with _ -> get_user_info user_num
-                    ) ro.room_users;
-                    update_users_label ();
-                    try
-                      let chat_buf = List.assoc ro.room_num !dialogs in
-                      box#set_buffer chat_buf
-                    with _ -> (if !!verbose then lprintf' "No chat dialog availabale\n")
-                  end
-              | _  -> (if !!verbose then lprintf' "room_users empty\n")
-
+                        let chat_buf = List.assoc ro.room_num !dialogs in
+                        box#set_buffer chat_buf
+                      with _ -> (if !!verbose then lprintf' "No chat dialog availabale\n")
+                    end
+                | _  -> (if !!verbose then lprintf' "room_users empty\n")
+            with _ -> ()
           end
-
   with _ -> (if !!verbose then lprintf' "No chat_box found\n")
 
 (*************************************************************************)
@@ -353,8 +404,8 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let on_double_click_room (ro : room_info) =
-  close_open_room [ro] ()
+let on_double_click_room k =
+  close_open_room [k] ()
 
 (*************************************************************************)
 (*                                                                       *)
@@ -362,8 +413,11 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let filter_room (ro : room_info) =
-  not (List.memq ro.room_network !G.networks_filtered)
+let filter_room k =
+  try
+    let ro = room_of_key k in
+    not (List.memq ro.room_network !G.networks_filtered)
+  with _ -> true
 
 (*************************************************************************)
 (*                                                                       *)
@@ -403,6 +457,12 @@
 (*                                                                       *)
 (*************************************************************************)
 
+let hashtbl_rooms_update ro ro_new =
+  ro.room_state <- ro_new.room_state;
+  ro.room_users <- ro_new.room_users;
+  ro.room_messages <- ro_new.room_messages;
+  ro.room_nusers <- ro_new.room_nusers
+
 let add_chat_to_room ro =
   match ro.room_state with
       RoomOpened ->
@@ -440,18 +500,21 @@
       (if !!verbose then lprintf' "Adding room %s num: %d\n" ro.room_name ro.room_num);
       add_chat_to_room ro;
       ignore (roomstore#add_item ro);
+      Hashtbl.add G.rooms ro.room_num ro;
       update_rooms_label ()
     end
 
 let room_info r =
   try
     (if !!verbose then lprintf' "Room_info of %s\n" r.room_name);
-    let (row, ro) = roomstore#find_item r.room_num in
+    let ro = Hashtbl.find G.rooms r.room_num in
+    let row = roomstore#find_row (room_key r.room_num) in
     (* no need to keep ro.room_messages, it is stored in dialogs *)
     let ro_new = {r with room_users = ro.room_users} in
     (if ro_new.room_state <> ro.room_state
       then add_chat_to_room ro_new);
-    roomstore#update_item row ro ro_new
+    roomstore#update_item row ro ro_new;
+    hashtbl_rooms_update ro ro_new
   with _ -> add_room r
 
 let find_user_name user_num =
@@ -495,7 +558,8 @@
 let remove_room_user room_num user_num =
   (if !!verbose then lprintf' "Removing user to room %d\n" room_num);
   try
-    let (row, ro) = roomstore#find_item room_num in
+    let ro = Hashtbl.find G.rooms room_num in
+    let row = roomstore#find_row (room_key room_num) in
     (if List.mem user_num ro.room_users
       then ro.room_users <- List.filter (fun n -> n <> user_num) ro.room_users);
     let _ =
@@ -504,7 +568,7 @@
              begin
                try
                  let u = Hashtbl.find G.users user_num in
-                 userstore#remove_item u;
+                 userstore#remove_item (GuiUsers.user_key user_num);
                  update_users_label ();
                with _ -> ()
              end
@@ -520,7 +584,8 @@
 let add_room_user room_num user_num =
   (if !!verbose then lprintf' "Adding user to room %d\n" room_num);
   try
-    let (row, ro) = roomstore#find_item room_num in
+    let ro = Hashtbl.find G.rooms room_num in
+    let row = roomstore#find_row (room_key room_num) in
     (if not (List.mem user_num ro.room_users)
       then ro.room_users <- user_num :: ro.room_users);
     match (!current_room, ro.room_state) with
@@ -545,12 +610,14 @@
       Some room_num ->
          begin
            try
-             let (_, ro) = roomstore#find_item room_num in
+             let ro = Hashtbl.find G.rooms room_num in
              if List.mem u_new.user_num ro.room_users
                then begin
                  try
-                   let (row, u) = userstore#find_item u_new.user_num in
-                   userstore#update_item row u u_new
+                   let u = Hashtbl.find G.users u_new.user_num in
+                   let row = userstore#find_row (GuiUsers.user_key u_new.user_num) in
+                   userstore#update_item row u u_new;
+                   GuiUsers.hashtbl_users_update u u_new
                  with _ -> ()
                end
            with _ -> ()
diff -urN src/gtk2/gui/guiServers.ml src/gtk2/gui/guiServers.ml
--- src/gtk2/gui/guiServers.ml	2005-04-26 13:02:21.000000000 +0200
+++ src/gtk2/gui/guiServers.ml	2005-10-12 16:26:25.000000000 +0200
@@ -75,6 +75,51 @@
 
 (*************************************************************************)
 (*                                                                       *)
+(*                         server_num                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let server_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         server_of_key                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let server_of_key key =
+  try
+    let num = server_num key in
+    Hashtbl.find G.servers num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_servers                               *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_servers keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = server_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         server_key                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let server_key server_num =
+  Printf.sprintf "%d" server_num
+
+(*************************************************************************)
+(*                                                                       *)
 (*                         Templates                                     *)
 (*                                                                       *)
 (*************************************************************************)
@@ -95,10 +140,9 @@
   module Column = GuiColumns.Server
 
   type item = server_info
-  type key = int
 
   let columns = O.servers_columns
-  let get_key = (fun s -> s.server_num)
+  let get_key = (fun s -> server_key s.server_num)
   let module_name = "Servers"
 
 end)
@@ -232,8 +276,9 @@
                           (fun path name ->
                              try
                                let iter = self#get_iter path in
-                               let s = self#get_item iter in
-                               GuiCom.send (ServerRename (s.server_num, name));
+                               let k = self#find_model_key iter in
+                               let s = server_of_key k in
+                               GuiCom.send (ServerRename (server_num k, name));
                                let row = self#convert_iter_to_child_iter iter in
                                store#set ~row ~column:server_name s.server_name
                              with _ -> ()
@@ -308,8 +353,9 @@
              (fun path ->
                 try
                   let iter = self#get_iter path in
-                  let s = self#get_item iter in
-                  GuiCom.send (ServerSetPreferred (s.server_num, not s.server_preferred));
+                  let k = self#find_key iter in
+                  let s = server_of_key k in
+                  GuiCom.send (ServerSetPreferred (server_num k, not s.server_preferred));
                   let row = self#convert_iter_to_child_iter iter in
                   store#set ~row ~column:server_preferred s.server_preferred
                 with _ -> ()
@@ -322,8 +368,11 @@
 (*                                                                       *)
 (*************************************************************************)
 
-  method sort_items c s1 s2 =
-    match c with
+  method sort_items c k1 k2 =
+    try
+      let s1 = server_of_key k1 in
+      let s2 = server_of_key k2 in
+      match c with
         Col_server_address ->
           begin
             let i = compare s1.server_addr s2.server_addr in
@@ -343,6 +392,7 @@
                                    (String.lowercase s2.server_name)
       | Col_server_tags -> compare s1.server_tags s2.server_tags
       | Col_server_preferred -> compare s1.server_preferred s2.server_preferred
+  with _ -> 0
 
 (*************************************************************************)
 (*                                                                       *)
@@ -351,9 +401,10 @@
 (*************************************************************************)
 
     method force_update_icons () =
-      List.iter (fun s ->
+      List.iter (fun k ->
         try
-          let (row, _) = self#find_item s.server_num in 
+          let row = self#find_row k in
+          let s = server_of_key k in
           store#set ~row ~column:server_network_pixb (Mi.network_pixb s.server_network ~size:A.SMALL ());
           store#set ~row ~column:server_state_pixb   (Mi.server_state_of_server s.server_network s.server_state ~size:A.SMALL)
         with _ -> ()
@@ -409,24 +460,28 @@
 (*************************************************************************)
 
 let remove sel () =
+  let l = keys_to_servers sel in
   List.iter (fun s ->
     GuiCom.send (RemoveServer_query s.server_num)
-  ) sel
+  ) l
 
 let connect_to sel () =
+  let l = keys_to_servers sel in
   List.iter (fun s ->
     GuiCom.send (ConnectServer s.server_num)
-  ) sel
+  ) l
 
 let disconnect sel () =
+  let l = keys_to_servers sel in
   List.iter (fun s ->
     GuiCom.send (DisconnectServer s.server_num)
-  ) sel
+  ) l
 
 let view_users sel () =
+  let l = keys_to_servers sel in
   List.iter (fun s ->
     GuiCom.send (ViewUsers s.server_num)
-  ) sel
+  ) l
 
 let add_new_server entry_addr entry_port () =
   try
@@ -443,6 +498,7 @@
   GuiCom.send CleanOldServers
 
 let clear_users sel () =
+  let l = keys_to_servers sel in
   List.iter (fun s ->
     match s.server_users with
         None -> ()
@@ -451,10 +507,9 @@
             List.iter (fun user_num ->
               Hashtbl.remove G.users user_num
             ) l;
-            let (row, s) = serverstore#find_item s.server_num in
-            serverstore#update_item row s {s with server_users = None}
+            s.server_users <- None
           end
-  ) sel;
+  ) l;
   userstore#clear (); (* if we call clear_users there is a good chance that some users are currently displaied *)
   update_users_label ()
 
@@ -462,9 +517,10 @@
   GuiCom.send (GetUser_info user_num)
 
 let server_set_preferred sel b () =
+  let l = keys_to_servers sel in
   List.iter (fun s ->
     GuiCom.send (ServerSetPreferred (s.server_num, b))
-  ) sel
+  ) l
 
 let server_rename s name () =
   GuiCom.send (ServerRename (s.server_num, name))
@@ -475,7 +531,7 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let server_menu (sel : server_info list) =
+let server_menu sel =
   let l =
     match sel with
         [] -> []
@@ -501,27 +557,29 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let on_select_server (sel : server_info list) =
+let on_select_server sel =
   userstore#clear ();
   update_users_label ();
   match sel with
       [] -> ()
-    | s :: tail ->
+    | k :: tail ->
         begin
-          match s.server_users with
-              None -> (if !!verbose then lprintf' "No user for server %s\n" s.server_name)
-
-            | Some l ->
-                begin
-                  List.iter (fun user_num ->
-                    try
-                      (if !!verbose then lprintf' "Add user %d to list of server %s\n" user_num s.server_name);
-                      let u = Hashtbl.find G.users user_num in
-                      ignore (userstore#add_item u)
-                    with _ -> get_user_info user_num
-                  ) l;
-                  update_users_label ()
-                end
+          try
+            let s = server_of_key k in
+            match s.server_users with
+                None -> (if !!verbose then lprintf' "No user for server %s\n" s.server_name)
+              | Some l ->
+                  begin
+                    List.iter (fun user_num ->
+                      try
+                        (if !!verbose then lprintf' "Add user %d to list of server %s\n" user_num s.server_name);
+                        let u = Hashtbl.find G.users user_num in
+                        ignore (userstore#add_item u)
+                      with _ -> get_user_info user_num
+                    ) l;
+                    update_users_label ()
+                  end
+          with _ -> ()
         end
 
 (*************************************************************************)
@@ -532,12 +590,15 @@
 
 let filter_disconnected_servers = ref false
 
-let filter_server s = not (
-  (!filter_disconnected_servers && (match s.server_state with
-        NotConnected _ 
-      | NewHost -> true | _ -> false)) ||
-  List.memq s.server_network !G.networks_filtered
-  )
+let filter_server k =
+  try
+    let s = server_of_key k in
+    not ((!filter_disconnected_servers &&
+           (match s.server_state with
+                NotConnected _ 
+              | NewHost -> true | _ -> false)) ||
+         List.memq s.server_network !G.networks_filtered)
+  with _ -> true
 
 (*************************************************************************)
 (*                                                                       *)
@@ -568,9 +629,24 @@
 (*                                                                       *)
 (*************************************************************************)
 
+let hashtbl_server_update s s_new =
+  s.server_addr <- s_new.server_addr;
+  s.server_port <- s_new.server_port;
+  s.server_realport <- s_new.server_realport;
+  s.server_score <- s_new.server_score;
+  s.server_tags <- s_new.server_tags;
+  s.server_nusers <- s_new.server_nusers;
+  s.server_nfiles <- s_new.server_nfiles;
+  s.server_state <- s_new.server_state;
+  s.server_name <- s_new.server_name;
+  s.server_description <- s_new.server_description;
+  s.server_users <- s_new.server_users;
+  s.server_banner <- s_new.server_banner;
+  s.server_preferred <- s_new.server_preferred
+
 let remove_server server_num =
   try
-    let (_, s) = serverstore#find_item server_num in
+    let s = Hashtbl.find G.servers server_num in
     let _ =
       match s.server_users with
           None -> ()
@@ -579,14 +655,16 @@
               Hashtbl.remove G.users user_num
             ) l
     in
-    serverstore#remove_item s;
+    Hashtbl.remove G.servers server_num;
+    serverstore#remove_item (server_key server_num);
     decr G.nservers;
     update_servers_labels ()
   with _ -> ()
 
 let update_server serv =
   try
-    let (row, s) = serverstore#find_item serv.server_num in
+    let s = Hashtbl.find G.servers serv.server_num in
+    let row = serverstore#find_row (server_key serv.server_num) in
     let s_new = {serv with server_users = s.server_users} in
     let _ =
       match Mi.is_connected s_new.server_state, Mi.is_connected s.server_state with
@@ -595,6 +673,7 @@
         | _ -> ()
     in
     serverstore#update_item row s s_new;
+    hashtbl_server_update s s_new;
     update_servers_labels ()
   with Not_found ->
     begin
@@ -604,6 +683,7 @@
           incr G.nconnected_servers
         end;
       incr G.nservers;
+      Hashtbl.add G.servers serv.server_num serv;
       update_servers_labels ()
     end
 
@@ -615,11 +695,12 @@
     | _  ->
         update_server s
 
-let h_server_update_state num state =
+let h_server_update_state server_num state =
   try
-    let (row, s) = serverstore#find_item num in
+    let s = Hashtbl.find G.servers server_num in
+    let row = serverstore#find_row (server_key server_num) in
     if state = RemovedHost
-      then remove_server s.server_num
+      then remove_server server_num
       else begin
         let s_new = {s with server_state = state} in
         let _ =
@@ -629,54 +710,59 @@
             | _ -> ()
         in
         serverstore#update_item row s s_new;
+        s.server_state <- s_new.server_state;
         update_servers_labels ()
       end
 
-  with Not_found -> GuiCom.send (GetServer_info num)
-    
-let h_server_busy num nusers nfiles =
+  with Not_found -> GuiCom.send (GetServer_info server_num)
+
+let h_server_busy server_num nusers nfiles =
   try
-    let (row, s) = serverstore#find_item num in
+    let s = Hashtbl.find G.servers server_num in
+    let row = serverstore#find_row (server_key server_num) in
     let s_new = {s with server_nusers = nusers;
                         server_nfiles = nfiles}
     in
-    serverstore#update_item row s s_new
-
-  with Not_found -> GuiCom.send (GetServer_info num)
+    serverstore#update_item row s s_new;
+    s.server_nusers <- s_new.server_nusers;
+    s.server_nfiles <- s_new.server_nfiles
+  with Not_found -> GuiCom.send (GetServer_info server_num)
 
-let h_server_update_users num user =
+let h_server_update_users server_num user =
   try
-    let (row, s) = serverstore#find_item num in
+    let s = Hashtbl.find G.servers server_num in
+    let row = serverstore#find_row (server_key server_num) in
     match s.server_users with
         None ->
           begin
-            let s_new = {s with server_users = Some [user]} in
-            serverstore#update_item row s s_new
+            s.server_users <- Some [user]
           end
       | Some list ->
           if not (List.mem user list)
             then begin
-              let s_new = {s with server_users = Some (user :: list)} in
-              serverstore#update_item row s s_new
+              s.server_users <- Some (user :: list)
             end
 
   with Not_found ->
-    if num <> 0
+    if server_num <> 0
       then begin
-        Hashtbl.remove G.users num;       (* Anyway remove the user. Will be sent back by the core *)
-        GuiCom.send (GetServer_info num);
-        GuiCom.send (GetServer_users num)
+        Hashtbl.remove G.users user;       (* Anyway remove the user. Will be sent back by the core *)
+        GuiCom.send (GetServer_info server_num);
+        GuiCom.send (GetServer_users server_num)
       end
 
 let clean_servers_table servers =
   let l = serverstore#all_items () in
   (if !!verbose then lprintf' "Cleaning servers\n   servers table : %d\n   new servers   : %d\n"
      (List.length l) (List.length servers));
-  List.iter (fun s ->                                              (* the core sends more servers than what the GUI displays.
+  List.iter (fun k ->                                              (* the core sends more servers than what the GUI displays.
                                                                     * better to do it this way.
                                                                     *)
-    if not (List.mem s.server_num servers)
-      then remove_server s.server_num
+    try
+      let s = server_of_key k in
+      if not (List.mem s.server_num servers)
+        then remove_server s.server_num
+    with _ -> ()
   ) l;
   if !!verbose
     then begin
@@ -690,13 +776,16 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let clean_servers num net_enabled =
+let clean_servers net_num net_enabled =
   if not net_enabled
     then begin
       let l = serverstore#all_items () in
-      List.iter (fun s ->
-        if s.server_network = num
-          then remove_server s.server_num
+      List.iter (fun k ->
+        try
+          let s = server_of_key k in
+          if s.server_network = net_num
+            then remove_server s.server_num
+        with _ -> ()
       ) l
     end
 
diff -urN src/gtk2/gui/guiStarter.ml src/gtk2/gui/guiStarter.ml
--- src/gtk2/gui/guiStarter.ml	2005-08-05 02:56:13.000000000 +0200
+++ src/gtk2/gui/guiStarter.ml	2005-10-23 15:06:43.000000000 +0200
@@ -6,14 +6,15 @@
 module M = GuiMessages
 module A = GuiArt
 
-let message_file_name = try
-    Sys.getenv "MLDONKEY_GUI_MESSAGES"
+let message_file_name =
+  try
+    Sys.getenv "MLGUI_STRINGS"
   with _ -> 
-      let filename = 
-        Filename.concat CommonOptions.home_dir "gtk2gui_messages.ini"
-      in
-      Unix2.safe_mkdir (Filename.dirname filename);
-      filename
+     let filename = 
+       Filename.concat M.lang_dir "mlgui_strings"
+     in
+     Unix2.safe_mkdir (Filename.dirname filename);
+     filename
 
 let _s x = GuiUtf8.simple_utf8_of (_s "GuiStarter" x)
 let _b x = _b "GuiStarter" x  
diff -urN src/gtk2/gui/guiStatusBar.ml src/gtk2/gui/guiStatusBar.ml
--- src/gtk2/gui/guiStatusBar.ml	2005-10-16 22:42:53.000000000 +0200
+++ src/gtk2/gui/guiStatusBar.ml	2005-10-31 10:09:35.000000000 +0100
@@ -161,7 +161,7 @@
 (*************************************************************************)
 
 let clear () =
-  update_sharedfiles 0 Int64.zero;
+  update_sharedfiles 0 (Int64.of_int 0);
   update_downloadedfiles ();
   update_servers ();
   update_updown 0 0
@@ -213,7 +213,7 @@
 
 
 let _ =
-  update_sharedfiles 0 Int64.zero;
+  update_sharedfiles 0 (Int64.of_int 0);
   update_downloadedfiles ();
   update_servers ();
   update_updown 0 0;
diff -urN src/gtk2/gui/guiSystray.ml src/gtk2/gui/guiSystray.ml
--- src/gtk2/gui/guiSystray.ml	2005-07-10 15:28:50.000000000 +0200
+++ src/gtk2/gui/guiSystray.ml	2005-10-23 14:28:15.000000000 +0200
@@ -1,4 +1,5 @@
 open GuiTypes2
+open Printf2
 
 type f = {mutable f : button_types -> unit}
 
@@ -22,12 +23,12 @@
              match ev with
                DOUBLE_CLICKED ->
                begin
-                   Printf.printf "tray double clicked\n";
+                   lprintf "tray double clicked\n";
                    flush stdout;
                  end
              | RBUTTON_CLICKED ->
                  begin
-                   Printf.printf "tray right clicked\n";
+                   lprintf "tray right clicked\n";
                    flush stdout;
                  end
              | _ -> ()
diff -urN src/gtk2/gui/guiTemplates.ml src/gtk2/gui/guiTemplates.ml
--- src/gtk2/gui/guiTemplates.ml	2005-05-02 14:57:59.000000000 +0200
+++ src/gtk2/gui/guiTemplates.ml	2005-10-05 08:11:19.000000000 +0200
@@ -116,6 +116,7 @@
  *)
 
 
+type key = string
 
 module Gview(V:
 
@@ -144,7 +145,7 @@
       open Column
       
       type item
-      type key
+      (* type key *)
 
       val columns              : (column * float) list Options.option_record
       val get_key              : item -> key
@@ -171,7 +172,7 @@
     inherit GTree.model
     method content : GTree.view_column -> column -> unit
     method expanded_paths : int array list
-    method get_item : Gtk.tree_iter -> item
+    method find_model_key : Gtk.tree_iter -> key
     method set_view : g_view -> unit
     method sort : column -> Gtk.Tags.sort_type option -> unit
     method unset_view : g_view -> unit
@@ -185,14 +186,12 @@
     method unset_model : unit -> unit
   end
 
-
 type item_tree =
   {
-    parent : int array;      (* for compare_array and partition in g_tree *)
-    level : int;             (* should speed up sort / avoid rebuilding an int array *)
-    iter : Gtk.tree_iter;    (* a pointer to a row *)
-    mutable tree_item : item;
-    mutable removed : bool;  (* to manage easily the stamp *)
+            parent  : int array;      (* for compare_array and partition in g_tree *)
+            level   : int;            (* should speed up sort / avoid rebuilding an int array *)
+            iter    : Gtk.tree_iter;  (* a pointer to a row *)
+    mutable removed : bool;           (* to manage easily the stamp *)
   }
 
 let parent_from_indices indices = Array.sub indices 0 (Array.length indices - 1)
@@ -209,8 +208,8 @@
       [] -> res
     | _ ->
         begin
-          let it_p = List.hd list in
-          let (l', l'') = List.partition (fun it -> it.parent = it_p.parent) list in
+          let it_p = snd (List.hd list) in
+          let (l', l'') = List.partition (fun (k, it) -> it.parent = it_p.parent) list in
           partition l'' (l' :: res)
         end
 
@@ -232,67 +231,10 @@
   in
   iter 0
 
-(*
-type item =
-  {
-    value : string;
-    level : int array;
-  };;
-
-let items = [
-  {value = "p1"; level = [|0|]};
-  {value = "p2"; level = [|2|]};
-  {value = "c21"; level = [|2;1|]};
-  {value = "p3"; level = [|1|]};
-  {value = "c22"; level = [|2;2|]};
-  {value = "c23"; level = [|2;0|]};
-  {value = "c11"; level = [|0;0|]};
-  {value = "c12"; level = [|0;3|]};
-  {value = "c13"; level = [|0;2|]};
-  {value = "c14"; level = [|0;1|]};
-  {value = "c31"; level = [|1;1|]};
-  {value = "c31"; level = [|1;0|]};
-  {value = "c111"; level = [|0;3;0|]};
-  {value = "c112"; level = [|0;2;0|]};
-];;
-
-let compare_array a1 a2 =
-  let len1 = Array.length a1 in
-  let len2 = Array.length a2 in
-  let len = min len1 len2 in
-  let rec iter n =
-    if n = len
-      (* a1 a2 have a common node in the tree!
-       * put the one with a lower height in the tree first.
-       *)
-      then compare len1 len2
-      else if a1.(n) > a2.(n)
-        then 1
-        else if a1.(n) < a2.(n)
-          then (-1)
-          else iter (n + 1)
-  in
-  iter 0;;
-
-let parent a = Array.sub a 0 (Array.length a - 1);;
-
-let l = List.sort (fun it1 it2 -> compare_array it1.level it2.level) items;;
-
-let rec partition list res =
-  let it_p = List.hd list in
-  let (l', l'') = List.partition (fun it -> parent it.level = parent it_p.level) list in
-  match l'' with
-      [] -> l' :: res
-    | _ -> partition l'' (l' :: res);;
-
-let pl = partition items [];;
-
-*)
-
 class virtual g_list (cols : GTree.column_list) =
 (* one uses a Hashtbl because, the GTree.model is very bad for random access. *)
   let (table : (key, item_tree) Hashtbl.t) = Hashtbl.create 217 in
-  let key_col = cols#add Gobject.Data.caml in
+  let key_col = cols#add Gobject.Data.string in
 (*
  * A bool GTree.column. If the value is true the row will be displayed.
  * As g_list inherit from GTree.model_filter, it is initialized once the object has
@@ -330,7 +272,7 @@
 (* for public use *)
     val store = store
 (* allows to change, whenever necessary, the filter function of our g_list *)
-    val mutable filter_func = (fun _ -> true)
+    val mutable filter_func = (fun (key : key)  -> true)
 (* the list of #g_view that currently use the g_list *)
     val mutable gviews = ([] : g_view list)
 (*
@@ -342,7 +284,7 @@
  * connection/disconnection scheme proceeds.
  *)
     val mutable stamp = ([] : item_tree list)
-    val mutable timerID = GMain.Timeout.add ~ms:6000 ~callback:(fun _ -> true)
+    val mutable timerID = GMain.Timeout.add ~ms:60000 ~callback:(fun _ -> true)
 (* the number of rows. Includes filtered rows. *)
     val mutable nitems = 0
 
@@ -359,27 +301,28 @@
  *)
     method virtual content : GTree.view_column -> column -> unit
 (* defines how the data will be sorted on the #g_view request. *)
-    method virtual sort_items : column -> item -> item -> int
+    method virtual sort_items : column -> key -> key -> int
 
 (* the public method to add an item *)
     method add_item i =
       let key = get_key i in
       let row = store#append () in
-      store#set ~row ~column:filter_col (filter_func i);
+      store#set ~row ~column:filter_col (filter_func key);
       store#set ~row ~column:key_col key;
       self#from_item row i;
       let indices = GTree.Path.get_indices (store#get_path row) in
       let level = level_from_indices indices in
 (*      (if !!verbose_view then lprintf_g_list "in add_item new path %s\n" (GTree.Path.to_string (store#get_path row))); *)
-      Hashtbl.add table key {tree_item = i; parent = [||]; level = level.(0); iter = row; removed = false};
+      Hashtbl.add table key {parent = [||]; level = level.(0); iter = row; removed = false};
       nitems <- nitems + 1;
       row
 
 (* the public method to retrieve all items stored in the g_list *)
     method all_items () =
       let l = ref [] in
-      Hashtbl.iter (fun _ it ->
-        l := it.tree_item :: !l
+      Hashtbl.iter (fun key it ->
+        if not it.removed
+          then l := key :: !l
       ) table;
       !l
 
@@ -391,11 +334,32 @@
 
     method expanded_paths = []
 
+    method private filter_removed key =
+      try
+        let it = Hashtbl.find table key in
+        it.removed
+      with _ -> false
+
+    method find_key row =
+      try
+        let key = store#get ~row ~column:key_col in
+        let it = Hashtbl.find table key in
+        if not it.removed
+          then key else raise Exit
+      with _ -> raise Not_found
+
+    method find_model_key row =
+      try
+        let store_row = self#convert_iter_to_child_iter row in
+        self#find_key store_row
+      with _ -> raise Not_found
+
 (* the public method to retrieve an item by its [key] *)
-    method find_item key =
+    method find_row key =
       try
         let it = Hashtbl.find table key in
-        (it.iter, it.tree_item)
+        if not it.removed
+          then it.iter else raise Exit
       with _ -> raise Not_found
 
 (* the connection/disconnection scheme between the #g_list and a #g_view *)
@@ -408,20 +372,15 @@
           List.iter (fun v -> v#unset_model ()) gl;
           (* proceed ... *)
           List.iter (fun it ->
-            Hashtbl.remove table (get_key it.tree_item);
-            if store#iter_is_valid it.iter then ignore (store#remove it.iter);
+            try
+              ignore (store#remove it.iter);
+            with _ -> ()
           ) stamp;
           stamp <- [];
           (* reconnect to all the #g_view *)
           List.iter (fun v -> v#set_model self#gmodel) gl
         end
 
-    method get_item row =
-      try
-        let key = self#get ~row ~column:key_col in
-        snd (self#find_item key)
-      with _ -> raise Not_found
-
 (* the g_model interface to connect to a #g_view *)
     method gmodel = (self :> g_model)
 
@@ -434,30 +393,31 @@
       (* disconnect from all the #g_view *)
       List.iter (fun v -> v#unset_model ()) gl;
       (* proceed ... *)
-      Hashtbl.iter (fun _ it ->
-        store#set ~row:it.iter ~column:filter_col (filter_func it.tree_item);
+      Hashtbl.iter (fun key it ->
+        store#set ~row:it.iter ~column:filter_col (filter_func key);
       ) table;
       (* reconnect to all the #g_view *)
       List.iter (fun v -> v#set_model self#gmodel) gl
 
 (* the public method to remove an item from the g_list *)
-    method remove_item i =
+    method remove_item key =
       try
-        let key = get_key i in
         let it = Hashtbl.find table key in
         if not it.removed
           then begin
             match gviews with
                 [] ->     (* no #g_view connected, nothing to be disconnected from *)
                   begin
-                    Hashtbl.remove table key;
                     ignore (store#remove it.iter);
+                    Hashtbl.remove table key;
                     nitems <- nitems - 1
                   end
               | _ ->
                   begin
                     it.removed <- true;
                     stamp <- it :: stamp;
+                    store#set ~row:it.iter ~column:filter_col (filter_func key);
+                    Hashtbl.remove table key;
                     nitems <- nitems - 1
                   end
           end
@@ -465,7 +425,7 @@
 
 (* the public method to set the filter function *)
     method set_filter f =
-      filter_func <- f;
+      filter_func <- (fun key -> not (self#filter_removed key) && f key);
       self#refresh_filter ()
 
 (* the public method to connect a #g_view. *)
@@ -473,7 +433,7 @@
       if not (List.memq view gviews)
         then begin
           (if gviews = []
-             then timerID <- GMain.Timeout.add ~ms:700 ~callback:
+             then timerID <- GMain.Timeout.add ~ms:60000 ~callback:
                                (fun _ ->
                                   self#flush_stamp ();
                                   true)
@@ -485,20 +445,21 @@
  * fast that even the filtered items are sorted :-)
  *)
     method private sort' c n =
-      let items = ref [] in
-      Hashtbl.iter (fun _ it ->
-        items := it :: !items;
+      let l = ref [] in
+      Hashtbl.iter (fun key it ->
+        if not it.removed
+          then l := (key, it) :: !l;
       ) table;
       let l' =
-        List.sort (fun it1 it2 ->
-          let comp' =  self#sort_items c it1.tree_item it2.tree_item in
+        List.sort (fun (key1, it1) (key2, it2) ->
+          let comp' =  self#sort_items c key1 key2 in
           if comp' = 0
             then compare it1.level it2.level
             else comp'
-        ) !items
+        ) !l
       in
-      let len = max 0 (List.length !items - 1) in
-      List.iter (fun it ->
+      let len = max 0 (List.length !l - 1) in
+      List.iter (fun (_, it) ->
         let pos = store#get_iter (path_from_indices [|len|]) in
         ignore (store#move_after ~iter:it.iter ~pos)
       ) (if n < 0 then l' else List.rev l')
@@ -513,16 +474,17 @@
         match order_opt with
           None ->
             begin
-              let items = ref [] in
+              let l = ref [] in
               Hashtbl.iter (fun _ it ->
-                items := it :: !items
+                if not it.removed
+                  then l := it :: !l;
               ) table;
-              let l = List.sort (fun it1 it2 -> (-1) * (compare it1.level it2.level)) !items in
-              let len = max 0 (List.length !items - 1) in
+              let l' = List.sort (fun it1 it2 -> (-1) * (compare it1.level it2.level)) !l in
+              let len = max 0 (List.length !l - 1) in
               List.iter (fun it ->
                 let pos = store#get_iter (path_from_indices [|len|]) in
                 ignore (store#move_after ~iter:it.iter ~pos)
-              ) l
+              ) l'
             end
         | Some `ASCENDING -> self#sort' c (-1)
         | Some `DESCENDING -> self#sort' c 1
@@ -534,10 +496,8 @@
 (* the public method to update an item in the g_list *)
     method update_item row i i_new =
       try
-        let it = Hashtbl.find table (get_key i) in
-        store#set ~row ~column:filter_col (filter_func i_new);
-        self#from_new_item row i i_new;
-        it.tree_item <- i_new
+        store#set ~row ~column:filter_col (filter_func (get_key i_new));
+        self#from_new_item row i i_new
       with _ -> ()
 
 
@@ -556,7 +516,7 @@
 class virtual g_tree (cols : GTree.column_list) =
 (* one uses a Hashtbl because, the GTree.model is very bad for random access. *)
   let (table : (key, item_tree) Hashtbl.t) = Hashtbl.create 217 in
-  let key_col = cols#add Gobject.Data.caml in
+  let key_col = cols#add Gobject.Data.string in
 (*
  * A bool GTree.column. If the value is true the row will be displayed.
  * As g_tree inherits from GTree.model_filter, it is initialized once the object has
@@ -595,7 +555,7 @@
     val store = store
 
 (* allows to change, whenever necessary, the filter function of the g_tree *)
-    val mutable filter_func = (fun _ -> true)
+    val mutable filter_func = (fun (key : key) -> true)
 
 (* the list of #g_view that currently use the g_tree *)
     val mutable gviews = ([] : g_view list)
@@ -628,27 +588,28 @@
  *)
     method virtual content : GTree.view_column -> column -> unit
 (* defines how the data will be sorted on the #g_view request. *)
-    method virtual sort_items : column -> item -> item -> int
+    method virtual sort_items : column -> key -> key -> int
 
 (* the public method to add an item *)
     method add_item i ?(parent : Gtk.tree_iter option) () =
       let key = get_key i in
       let row = store#append ?parent () in
-      store#set ~row ~column:filter_col (filter_func i);
+      store#set ~row ~column:filter_col (filter_func key);
       store#set ~row ~column:key_col key;
       self#from_item row i;
       let indices = GTree.Path.get_indices (store#get_path row) in
       let level = level_from_indices indices in
       let parent = parent_from_indices indices in
-      Hashtbl.add table key {tree_item = i; parent = parent; level = level.(0); iter = row; removed = false};
+      Hashtbl.add table key {parent = parent; level = level.(0); iter = row; removed = false};
       nitems <- nitems + 1;
       row
 
 (* the public method to retrieve all items stored in the g_tree *)
     method all_items () =
       let l = ref [] in
-      Hashtbl.iter (fun _ it ->
-        l := it.tree_item :: !l;
+      Hashtbl.iter (fun key it ->
+        if not it.removed
+          then l := key :: !l;
       )table;
       !l
 
@@ -674,11 +635,35 @@
       expanded_rows <- !l';
       !l
 
+    method private filter_removed key =
+      try
+        let it = Hashtbl.find table key in
+        it.removed
+      with _ -> false
+
+    method find_key row =
+      if store#iter_is_valid row
+        then begin
+          try
+            let key = store#get ~row ~column:key_col in
+            let it = Hashtbl.find table key in
+            if not it.removed
+              then key else raise Exit
+          with _ -> raise Not_found
+        end else raise Not_found
+
+    method find_model_key row =
+      try
+        let store_row = self#convert_iter_to_child_iter row in
+        self#find_key store_row
+      with _ -> raise Not_found
+
 (* the public method to retrieve an item by its [key] *)
-    method find_item key =
+    method find_row key =
       try
         let it = Hashtbl.find table key in
-        (it.iter, it.tree_item)
+        if not it.removed && store#iter_is_valid it.iter
+          then it.iter else raise Exit
       with _ -> raise Not_found
 
 (* the connection/disconnection scheme between the g_tree and a #g_view *)
@@ -692,23 +677,15 @@
           List.iter (fun v -> v#unset_model ()) gl;
           (* proceed ... *)
           List.iter (fun it ->
-            (if store#iter_is_valid it.iter then ignore (store#remove it.iter));
-            Hashtbl.remove table (get_key it.tree_item)
+            try
+              ignore (store#remove it.iter)
+            with _ -> ()
           ) stamp;
           stamp <- [];
           (* reconnect to all the #g_view *)
           List.iter (fun v -> v#set_model self#gmodel) gl
         end
 
-(* Becarefull !!! get_item uses Gtk.tree_iter related to the model_filter.
- * They are quite different from the store Gtk.tree_iter. Don't forget to convert them before...
- *)
-    method get_item row =
-      try
-        let key = self#get ~row ~column:key_col in
-        snd (self#find_item key)
-      with _ -> raise Not_found
-
 (* the g_model interface to connect to a #g_view *)
     method gmodel = (self :> g_model)
 
@@ -721,8 +698,8 @@
       (* disconnect from all the #g_view *)
       List.iter (fun v -> v#unset_model ()) gl;
       (* proceed ... *)
-      Hashtbl.iter (fun _ it ->
-        store#set ~row:it.iter ~column:filter_col (filter_func it.tree_item);
+      Hashtbl.iter (fun key it ->
+        store#set ~row:it.iter ~column:filter_col (filter_func key);
       ) table;
       (* reconnect to all the #g_view *)
       List.iter (fun v -> v#set_model self#gmodel) gl
@@ -730,9 +707,8 @@
 (* the public method to remove an item from the g_tree.
  * removes the children when a parent is removed.
  *)
-    method remove_item i =
+    method remove_item key =
       try
-        let key = get_key i in
         let it = Hashtbl.find table key in
         if not it.removed
           then begin
@@ -749,8 +725,7 @@
                             let child_path = GTree.Path.from_string (Printf.sprintf "%s:%d" path_str i) in
                             let child_row = store#get_iter child_path in
                             let key = store#get ~row:child_row ~column:key_col in
-                            let it_child = Hashtbl.find table key in
-                            self#remove_item it_child.tree_item;
+                            self#remove_item key;
                           with _ -> (if !!verbose_view then lprintf_g_tree "failed to find child\n");
                         done
                       end;
@@ -770,13 +745,15 @@
                             let child_path = GTree.Path.from_string (Printf.sprintf "%s:%d" path_str i) in
                             let child_row = store#get_iter child_path in
                             let key = store#get ~row:child_row ~column:key_col in
-                            let it_child = Hashtbl.find table key in
-                            self#remove_item it_child.tree_item;
+                            self#remove_item key;
                           with _ -> (if !!verbose_view then lprintf_g_tree "failed to find child\n");
                         done
                       end;
                     (* order is parent->children *)
                     stamp <- it :: stamp;
+                    if store#iter_is_valid it.iter then begin
+                      store#set ~row:it.iter ~column:filter_col (filter_func key) end;
+                    Hashtbl.remove table key;
                     nitems <- nitems - 1
                   end
           end
@@ -785,7 +762,7 @@
 
 (* the public method to set the filter function *)
     method set_filter f =
-      filter_func <- f;
+      filter_func <- (fun key -> not (self#filter_removed key) && f key);
       self#refresh_filter ()
 
 (* the public method to connect a #g_view. *)
@@ -793,7 +770,7 @@
       if not (List.memq view gviews)
         then begin
           (if gviews = []
-             then timerID <- GMain.Timeout.add ~ms:800 ~callback:
+             then timerID <- GMain.Timeout.add ~ms:60000 ~callback:
                                (fun _ ->
                                   self#flush_stamp ();
                                   true)
@@ -801,18 +778,18 @@
           gviews <- view :: gviews
 	end
 
-    method private sort' c n items =
-      let pl = partition items [] in
+    method private sort' c n (l : (key * item_tree) list)=
+      let pl = partition l [] in
       List.iter (fun l ->
         let l' =
-          List.sort (fun it1 it2 ->
-            let comp' =  self#sort_items c it1.tree_item it2.tree_item in
+          List.sort (fun (key1, it1) (key2, it2) ->
+            let comp' =  self#sort_items c key1 key2 in
             if comp' = 0
               then compare_array it1.parent it2.parent
               else comp'
           ) l
         in
-        List.iter (fun it ->
+        List.iter (fun (_, it) ->
           let current_path = store#get_path it.iter in
           let new_indices = GTree.Path.get_indices current_path in
           let index = Array.length new_indices - 1 in
@@ -834,14 +811,14 @@
         ) expanded_rows
       in
       let store_paths = [||] :: store_paths in
-      let items = ref [] in
-      Hashtbl.iter (fun _ it ->
+      let l = ref [] in
+      Hashtbl.iter (fun key it ->
         let current_path = store#get_path it.iter in
         let current_indices = GTree.Path.get_indices current_path in
         let current_parent = parent_from_indices current_indices in
-        if List.mem current_parent store_paths
+        if List.mem current_parent store_paths && not it.removed
           then begin
-            items := it :: !items;
+            l := (key, it) :: !l;
           end
       ) table;
       (* proceed ... *)
@@ -850,23 +827,23 @@
           None ->
             begin
               (* make sure !items are sorted parent(1)->children(1)->...->parent(n)->children(n) !!! *)
-              let l =
-                List.sort (fun it1 it2 ->
+              let l' =
+                List.sort (fun (_, it1) (_, it2) ->
                   (-1) * (compare_array (Array.append it1.parent [|it1.level|])
                                         (Array.append it2.parent [|it2.level|]))
-                ) !items
+                ) !l
               in
-              List.iter (fun it ->
+              List.iter (fun (_, it) ->
                 let current_path = store#get_path it.iter in
                 let new_indices = GTree.Path.get_indices current_path in
                 let index = Array.length new_indices - 1 in
                 new_indices.(index) <- 0;
                 let pos = store#get_iter (path_from_indices new_indices) in
                 ignore (store#move_before ~iter:it.iter ~pos)
-              ) l
+              ) l'
             end
-        | Some `ASCENDING -> self#sort' c (-1) !items
-        | Some `DESCENDING -> self#sort' c 1 !items
+        | Some `ASCENDING -> self#sort' c (-1) !l
+        | Some `DESCENDING -> self#sort' c 1 !l
             
       in
       (* reconnect all the #g_view *)
@@ -890,9 +867,8 @@
     method update_item row i i_new =
       try
         let it = Hashtbl.find table (get_key i) in
-        store#set ~row ~column:filter_col (filter_func i_new);
-        self#from_new_item row i i_new;
-        it.tree_item <- i_new
+        store#set ~row ~column:filter_col (filter_func (get_key i_new));
+        self#from_new_item row i i_new
       with _ -> ()
 
     initializer
@@ -1142,8 +1118,8 @@
                         * that shall be the GTree.model interface of the current
                         * #g_model connected to us.
                         *)
-                       let i = m#get_item row in
-                       list := i :: !list
+                       let key = m#find_model_key row in
+                       list := key :: !list
                      with _ -> (if !!verbose_view then lprintf_g_view "in connect_events selection_changed no item found\n")
                    ) path_selected;
                    on_select !list
@@ -1157,8 +1133,8 @@
              | Some m ->
                  try
                    let row = view#model#get_iter path in
-                   let i = m#get_item row in
-                   on_double_click i
+                   let key = m#find_model_key row in
+                   on_double_click key
                  with _ -> (if !!verbose_view then lprintf_g_view "in connect_events row_activated no item found\n")
       ));
       (* connect the test_expand_row signal *)
@@ -1168,8 +1144,8 @@
                None -> true
              | Some m ->
                  try
-                   let i = m#get_item row in
-                   on_expand path i
+                   let key = m#find_model_key row in
+                   on_expand path key
                  with _ ->
                    (if !!verbose_view then lprintf_g_view "in connect_events test_expand_row no item found\n");
                    true
@@ -1181,8 +1157,8 @@
                None -> true
              | Some m ->
                  try
-                   let i = m#get_item row in
-                   on_collapse path i
+                   let key = m#find_model_key row in
+                   on_collapse path key
                  with _ ->
                    (if !!verbose_view then lprintf_g_view "in connect_events test_collapse_row no item found\n");
                    true
@@ -1194,11 +1170,11 @@
                None -> ()
              | Some m ->
                  try
-                   let i = m#get_item row in
+                   let key = m#find_model_key row in
                    let indices = GTree.Path.get_indices path in
                    (if not (List.mem indices expanded_paths)
                       then expanded_paths <- indices :: expanded_paths);
-                   on_expanded path i
+                   on_expanded path key
                  with _ -> (if !!verbose_view then lprintf_g_view "in connect_events row_expanded no item found\n")
       ));
       (* connect the row_collapsed signal *)
@@ -1208,11 +1184,11 @@
                None -> ()
              | Some m ->
                  try
-                   let i = m#get_item row in
+                   let key = m#find_model_key row in
                    let indices = GTree.Path.get_indices path in
                    (if (List.mem indices expanded_paths)
                       then expanded_paths <- List.filter (fun a -> indices <> a) expanded_paths);
-                   on_collapsed path i
+                   on_collapsed path key
                  with _ -> (if !!verbose_view then lprintf_g_view "in connect_events row_collapsed no item found\n")
       ));
       (* save automatically the column widths on a size change *)
@@ -1316,8 +1292,8 @@
                    * that shall be the GTree.model interface of the current
                    * #g_model connected to us.
                    *)
-                  let i = m#get_item row in
-                  list := i :: !list
+                  let key = m#find_model_key row in
+                  list := key :: !list
                 with _ -> (if !!verbose_view then lprintf_g_view "in contextual_menu no item found\n")
               ) path_selected;
               menu !list
diff -urN src/gtk2/gui/guiTemplates.mli src/gtk2/gui/guiTemplates.mli
--- src/gtk2/gui/guiTemplates.mli	2005-03-02 20:45:12.000000000 +0100
+++ src/gtk2/gui/guiTemplates.mli	2005-09-25 10:41:40.000000000 +0200
@@ -28,6 +28,8 @@
 (*************************************************************************)
 (*************************************************************************)
 
+type key = string
+
 module Gview :
   functor
     (V : sig
@@ -44,7 +46,6 @@
              end
 
            type item
-           type key
 
            val columns     : (Column.column * float) list Options.option_record
            val get_key     : item -> key
@@ -58,7 +59,7 @@
           inherit GTree.model
           method content : GTree.view_column -> V.Column.column -> unit
           method expanded_paths : int array list
-          method get_item : Gtk.tree_iter -> V.item
+          method find_model_key : Gtk.tree_iter -> key
           method sort : V.Column.column -> Gtk.Tags.sort_type option -> unit
           method set_view : g_view -> unit
           method unset_view : g_view -> unit
@@ -79,18 +80,19 @@
           method virtual from_item : Gtk.tree_iter -> V.item -> unit
           method virtual from_new_item : Gtk.tree_iter -> V.item -> V.item -> unit
           method virtual content : GTree.view_column -> V.Column.column -> unit
-          method virtual sort_items : V.Column.column -> V.item -> V.item -> int
+          method virtual sort_items : V.Column.column -> key -> key -> int
           method add_item : V.item -> Gtk.tree_iter
-          method all_items : unit -> V.item list
+          method all_items : unit -> key list
           method clear : unit -> unit
           method expanded_paths : int array list
-          method find_item : V.key -> (Gtk.tree_iter * V.item)
-          method get_item : Gtk.tree_iter -> V.item
+          method find_key : Gtk.tree_iter -> key
+          method find_model_key : Gtk.tree_iter -> key
+          method find_row : key -> Gtk.tree_iter
           method gmodel : g_model
           method nitems : int
           method refresh_filter : unit -> unit
-          method remove_item : V.item -> unit
-          method set_filter : (V.item -> bool) -> unit
+          method remove_item : key -> unit
+          method set_filter : (key -> bool) -> unit
           method set_view : g_view -> unit
           method sort : V.Column.column -> Gtk.Tags.sort_type option -> unit
           method unset_view : g_view -> unit
@@ -104,18 +106,19 @@
           method virtual from_item : Gtk.tree_iter -> V.item -> unit
           method virtual from_new_item : Gtk.tree_iter -> V.item -> V.item -> unit
           method virtual content : GTree.view_column -> V.Column.column -> unit
-          method virtual sort_items : V.Column.column -> V.item -> V.item -> int
+          method virtual sort_items : V.Column.column -> key -> key -> int
           method add_item : V.item -> ?parent:Gtk.tree_iter -> unit -> Gtk.tree_iter
-          method all_items : unit -> V.item list
+          method all_items : unit -> key list
           method clear : unit -> unit
           method expanded_paths : int array list
-          method find_item : V.key -> (Gtk.tree_iter * V.item)
-          method get_item : Gtk.tree_iter -> V.item
+          method find_key : Gtk.tree_iter -> key
+          method find_model_key : Gtk.tree_iter -> key
+          method find_row : key -> Gtk.tree_iter
           method gmodel : g_model
           method nitems : int
           method refresh_filter : unit -> unit
-          method remove_item : V.item -> unit
-          method set_filter : (V.item -> bool) -> unit
+          method remove_item : key -> unit
+          method set_filter : (key -> bool) -> unit
           method set_view : g_view -> unit
           method sort : V.Column.column -> Gtk.Tags.sort_type option -> unit
           method unset_view : g_view -> unit
@@ -128,14 +131,14 @@
           method expanded_paths : int array list
           method gview : g_view
           method id : int
-          method set_menu : (V.item list -> GToolbox.menu_entry list) -> unit
+          method set_menu : (key list -> GToolbox.menu_entry list) -> unit
           method set_model : g_model -> unit
-          method set_on_collapse : (Gtk.tree_path -> V.item -> bool) -> unit
-          method set_on_collapsed : (Gtk.tree_path -> V.item -> unit) -> unit
-          method set_on_double_click : (V.item -> unit) -> unit
-          method set_on_expand : (Gtk.tree_path -> V.item -> bool) -> unit
-          method set_on_expanded : (Gtk.tree_path -> V.item -> unit) -> unit
-          method set_on_select : (V.item list -> unit) -> unit
+          method set_on_collapse : (Gtk.tree_path -> key -> bool) -> unit
+          method set_on_collapsed : (Gtk.tree_path -> key -> unit) -> unit
+          method set_on_double_click : (key -> unit) -> unit
+          method set_on_expand : (Gtk.tree_path -> key -> bool) -> unit
+          method set_on_expanded : (Gtk.tree_path -> key -> unit) -> unit
+          method set_on_select : (key list -> unit) -> unit
           method set_selection_mode : Gtk.Tags.selection_mode -> unit
           method unset_model : unit -> unit
           method view : GTree.view
diff -urN src/gtk2/gui/guiTools.ml src/gtk2/gui/guiTools.ml
--- src/gtk2/gui/guiTools.ml	2005-07-13 01:10:16.000000000 +0200
+++ src/gtk2/gui/guiTools.ml	2005-10-12 16:45:04.000000000 +0200
@@ -432,36 +432,37 @@
 *)
 
 let fit_string_to_pixels s ~context ~pixels =
-  let us = U.utf8_of s in
-  let pango_layout = Pango.Layout.create context#as_context in
-  Pango.Layout.set_text pango_layout us;
-  let us' =
-    if fst (Pango.Layout.get_pixel_size pango_layout) <= pixels
-    then us
+  if s = ""
+    then s
     else begin
-      let len = Glib.Utf8.length us in
-      let uca = Glib.Utf8.to_unistring us in
-      let uca_ext = Array.sub uca (len - 7) 7 in
-      let ext = "..." ^ (Glib.Utf8.from_unistring uca_ext) in
-      let rec adjust_string n len =
-        if n = len
-          then us
-          else begin
-            let ustr = (Glib.Utf8.from_unistring (Array.sub uca 0 n)) ^ ext in
-            Pango.Layout.set_text pango_layout ustr;
-            let ustr_pixels = fst (Pango.Layout.get_pixel_size pango_layout) in
-            (* Printf.printf "width %d - Pango.Layout.get_pixel_size %d Pango.Layout.get_text %s\n" 
-               pixels s_width (Pango.Layout.get_text pango_layout);
-            flush stdout; *)
-            if ustr_pixels < pixels
-              then adjust_string (n + 1) len
-              else ustr
-          end
-      in
-      adjust_string 0 len
+      let us = U.utf8_of s in
+      let pango_layout = Pango.Layout.create context#as_context in
+      Pango.Layout.set_text pango_layout us;
+      let us' =
+        if fst (Pango.Layout.get_pixel_size pango_layout) <= pixels
+        then us
+        else begin
+          let len = Glib.Utf8.length us in
+          let uca = Glib.Utf8.to_unistring us in
+          let ext_len = min 7 (len - 1) in
+          let uca_ext = Array.sub uca (len - ext_len) ext_len in
+          let ext = "..." ^ (Glib.Utf8.from_unistring uca_ext) in
+          let rec adjust_string n len =
+            if n = len
+              then us
+              else begin
+                let ustr = (Glib.Utf8.from_unistring (Array.sub uca 0 n)) ^ ext in
+                Pango.Layout.set_text pango_layout ustr;
+                let ustr_pixels = fst (Pango.Layout.get_pixel_size pango_layout) in
+                if ustr_pixels < pixels
+                  then adjust_string (n + 1) len
+                  else ustr
+              end
+          in
+          adjust_string 0 len
+        end
+      in us'
     end
-  in us'
-
 
 
 let warning_box
diff -urN src/gtk2/gui/guiTypes2.ml src/gtk2/gui/guiTypes2.ml
--- src/gtk2/gui/guiTypes2.ml	2005-04-04 21:36:46.000000000 +0200
+++ src/gtk2/gui/guiTypes2.ml	2005-10-31 12:01:46.000000000 +0100
@@ -180,6 +180,10 @@
   GTreeDirectory of g_file_tree
 | GTreeFile of  res_info
 
+type source_slot = int
+let source_only = 0
+let source_has_slot = 1
+let source_has_upload = 3
 
 type source_info =
    {
@@ -202,14 +206,48 @@
     mutable source_upload_rate : float;
     mutable source_download_rate : float;
     mutable source_upload : string option;
-    mutable source_has_upload : bool;
+    mutable source_has_upload : source_slot;
     mutable source_availability : (int * string) list;     (* file_num, availability *)
     mutable source_files_requested : int list;             (* file_num *)
   }
 
+type razorback_stats =
+  {
+   mutable razorback_file_history      : string;
+   mutable razorback_file_rating       : string;
+   mutable razorback_file_avalaibility : int;
+   mutable razorback_file_completed    : int;
+  }
+
+type g_file_info = {
+    g_file_num : int;
+    g_file_network : int;
+
+    mutable g_file_comment : string;
+    mutable g_file_name : string;
+    mutable g_file_names : (string * ips_list) list;
+    mutable g_file_size : int64;
+    mutable g_file_downloaded : int64;
+    mutable g_file_active_sources : int;
+    mutable g_file_all_sources : int;
+    mutable g_file_state : file_state;
+    mutable g_file_chunks : string;
+    mutable g_file_availability : (int * string) list;
+    mutable g_file_sources : int list option;
+    mutable g_file_download_rate : float;
+    mutable g_file_format : format;
+    mutable g_file_chunks_age : int array;
+    mutable g_file_age : int;
+    mutable g_file_last_seen : int;
+    mutable g_file_priority : int;
+    mutable g_file_uids : Uid.t list;
+
+    mutable g_file_razorback_stats : razorback_stats option;
+  }
+
 type item_info =
-  File of file_info
-| Source of (source_info * int)   (* source_info, file_num*)
+  File of g_file_info
+| Source of (source_info * int)   (* source_info, file_num *)
 
 type item_index =
   File_num of int                 (* file_num *)
@@ -237,3 +275,15 @@
 | QF_COMBO of string ref
   
 | QF_HIDDEN of query_form list
+
+type shared_file =
+  {
+    g_shared_num : int;
+    g_shared_network : int;
+    mutable g_shared_filename : string;
+    mutable g_shared_size : int64;
+    mutable g_shared_uploaded : int64;
+    mutable g_shared_requests : int;
+    mutable g_shared_uids : Uid.t list;
+    mutable g_shared_last_seen : float;
+  }
diff -urN src/gtk2/gui/guiUploads.ml src/gtk2/gui/guiUploads.ml
--- src/gtk2/gui/guiUploads.ml	2005-09-13 11:54:47.000000000 +0200
+++ src/gtk2/gui/guiUploads.ml	2005-10-09 12:33:01.000000000 +0200
@@ -22,6 +22,7 @@
 open GuiTypes2
 open GuiTypes
 open CommonTypes
+open GraphTypes
 
 open GuiTools
 open GuiGlobal
@@ -55,8 +56,98 @@
 let (uploader_label : GMisc.label option ref) = ref None
 let nuploading = ref 0
 let show_pending = ref false
-let uploaders_timerID = ref (GMain.Timeout.add ~ms:2000 ~callback:(fun _ -> true))
 let (view_context : GPango.context option ref) = ref None
+let uploaders_n_pendings = ref []
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         shared_file_num                               *)
+(*                                                                       *)
+(*************************************************************************)
+
+let shared_file_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         shared_file_of_key                            *)
+(*                                                                       *)
+(*************************************************************************)
+
+let shared_file_of_key key =
+  try
+    let num = shared_file_num key in
+    Hashtbl.find G.shared_files num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_shared_files                          *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_shared_files keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = shared_file_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         shared_file_key                               *)
+(*                                                                       *)
+(*************************************************************************)
+
+let shared_file_key shared_file_num =
+  Printf.sprintf "%d" shared_file_num
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         uploader_num                                  *)
+(*                                                                       *)
+(*************************************************************************)
+
+let uploader_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         uploader_of_key                               *)
+(*                                                                       *)
+(*************************************************************************)
+
+let uploader_of_key key =
+  try
+    let num = uploader_num key in
+    Hashtbl.find G.sources num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_uploaders                             *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_uploaders keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = uploader_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         uploader_key                                  *)
+(*                                                                       *)
+(*************************************************************************)
+
+let uploader_key uploader_num =
+  Printf.sprintf "%d" uploader_num
 
 (*************************************************************************)
 (*                                                                       *)
@@ -68,11 +159,10 @@
 
   module Column = GuiColumns.Shared_files_up
 
-  type item = shared_info
-  type key = int
+  type item = shared_file
 
   let columns = O.shared_files_up_columns
-  let get_key = (fun si -> si.shared_num)
+  let get_key = (fun si -> (shared_file_key si.g_shared_num))
   let module_name = "Uploads"
 
 end)
@@ -97,15 +187,15 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method from_item row (si : shared_info) =
-      store#set ~row ~column:shared_network_str (Mi.network_name si.shared_network);
-      store#set ~row ~column:shared_network_pixb (Mi.network_pixb si.shared_network ~size:A.SMALL ());
-      store#set ~row ~column:shared_name (U.utf8_of si.shared_filename);
-      store#set ~row ~column:shared_name_pixb (Mi.file_type_of_name si.shared_filename ~size:A.SMALL);
-      store#set ~row ~column:shared_size_str (Mi.size_of_int64 si.shared_size);
-      store#set ~row ~column:shared_uploaded_str (Mi.size_of_int64 si.shared_uploaded);
-      store#set ~row ~column:shared_requests si.shared_requests;
-      store#set ~row ~column:shared_uid (Mi.uid_list_to_string si.shared_uids)
+    method from_item row (si : shared_file) =
+      store#set ~row ~column:shared_network_str (Mi.network_name si.g_shared_network);
+      store#set ~row ~column:shared_network_pixb (Mi.network_pixb si.g_shared_network ~size:A.SMALL ());
+      store#set ~row ~column:shared_name (U.utf8_of si.g_shared_filename);
+      store#set ~row ~column:shared_name_pixb (Mi.file_type_of_name si.g_shared_filename ~size:A.SMALL);
+      store#set ~row ~column:shared_size_str (Mi.size_of_int64 si.g_shared_size);
+      store#set ~row ~column:shared_uploaded_str (Mi.size_of_int64 si.g_shared_uploaded);
+      store#set ~row ~column:shared_requests si.g_shared_requests;
+      store#set ~row ~column:shared_uid (Mi.uid_list_to_string si.g_shared_uids)
 
 (*************************************************************************)
 (*                                                                       *)
@@ -113,30 +203,30 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method from_new_item (row : Gtk.tree_iter) (si : shared_info) (si_new : shared_info) =
-      if si.shared_filename <> si_new.shared_filename
+    method from_new_item (row : Gtk.tree_iter) (si : shared_file) (si_new : shared_file) =
+      if si.g_shared_filename <> si_new.g_shared_filename
         then begin
-          store#set ~row ~column:shared_name (U.utf8_of si_new.shared_filename);
-          if (Mi.extension_of si.shared_filename) <> (Mi.extension_of si_new.shared_filename)
+          store#set ~row ~column:shared_name (U.utf8_of si_new.g_shared_filename);
+          if (Mi.extension_of si.g_shared_filename) <> (Mi.extension_of si_new.g_shared_filename)
             then begin
-              store#set ~row ~column:shared_name_pixb (Mi.file_type_of_name si_new.shared_filename ~size:A.SMALL);
+              store#set ~row ~column:shared_name_pixb (Mi.file_type_of_name si_new.g_shared_filename ~size:A.SMALL);
             end
         end;
-      if si.shared_size <> si_new.shared_size
+      if si.g_shared_size <> si_new.g_shared_size
         then begin
-          store#set ~row ~column:shared_size_str (Mi.size_of_int64 si_new.shared_size);
+          store#set ~row ~column:shared_size_str (Mi.size_of_int64 si_new.g_shared_size);
         end;
-      if si.shared_uploaded <> si_new.shared_uploaded
+      if si.g_shared_uploaded <> si_new.g_shared_uploaded
         then begin
-          store#set ~row ~column:shared_uploaded_str (Mi.size_of_int64 si_new.shared_uploaded);
+          store#set ~row ~column:shared_uploaded_str (Mi.size_of_int64 si_new.g_shared_uploaded);
         end;
-      if si.shared_requests <> si_new.shared_requests
+      if si.g_shared_requests <> si_new.g_shared_requests
         then begin
-          store#set ~row ~column:shared_requests si_new.shared_requests;
+          store#set ~row ~column:shared_requests si_new.g_shared_requests;
         end;
-      if si.shared_uids <> si_new.shared_uids
+      if si.g_shared_uids <> si_new.g_shared_uids
         then begin
-          store#set ~row ~column:shared_uid (Mi.uid_list_to_string si_new.shared_uids)
+          store#set ~row ~column:shared_uid (Mi.uid_list_to_string si_new.g_shared_uids)
         end
 
 (*************************************************************************)
@@ -223,14 +313,18 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method sort_items c si1 si2 =
-      match c with
-          Col_shared_file -> compare si1.shared_filename si2.shared_filename
-        | Col_shared_network -> compare si1.shared_network si2.shared_network
-        | Col_shared_upsize -> compare si1.shared_uploaded si2.shared_uploaded
-        | Col_shared_requests -> compare si1.shared_requests si2.shared_requests
-        | Col_shared_size -> compare si1.shared_size si2.shared_size
-        | Col_shared_uid -> compare (Mi.uid_list_to_string si1.shared_uids) (Mi.uid_list_to_string si2.shared_uids)
+    method sort_items c k1 k2 =
+      try
+        let si1 = shared_file_of_key k1 in
+        let si2 = shared_file_of_key k2 in
+        match c with
+          Col_shared_file -> compare si1.g_shared_filename si2.g_shared_filename
+        | Col_shared_network -> compare si1.g_shared_network si2.g_shared_network
+        | Col_shared_upsize -> compare si1.g_shared_uploaded si2.g_shared_uploaded
+        | Col_shared_requests -> compare si1.g_shared_requests si2.g_shared_requests
+        | Col_shared_size -> compare si1.g_shared_size si2.g_shared_size
+        | Col_shared_uid -> compare (Mi.uid_list_to_string si1.g_shared_uids) (Mi.uid_list_to_string si2.g_shared_uids)
+      with _ -> 0
 
 (*************************************************************************)
 (*                                                                       *)
@@ -239,11 +333,12 @@
 (*************************************************************************)
 
     method force_update_icons () =
-      List.iter (fun si ->
+      List.iter (fun k ->
         try
-          let (row, _) = self#find_item si.shared_num in 
-          store#set ~row ~column:shared_network_pixb (Mi.network_pixb si.shared_network ~size:A.SMALL ());
-          store#set ~row ~column:shared_name_pixb (Mi.file_type_of_name si.shared_filename ~size:A.SMALL);
+          let si = shared_file_of_key k in
+          let row = self#find_row k in
+          store#set ~row ~column:shared_network_pixb (Mi.network_pixb si.g_shared_network ~size:A.SMALL ());
+          store#set ~row ~column:shared_name_pixb (Mi.file_type_of_name si.g_shared_filename ~size:A.SMALL);
         with _ -> ()
       ) (self#all_items ())
 
@@ -254,10 +349,9 @@
   module Column = GuiColumns.Client
 
   type item = source_info
-  type key = int
 
   let columns = O.uploaders_columns
-  let get_key = (fun s -> s.source_num)
+  let get_key = (fun s -> (uploader_key s.source_num))
   let module_name = "Uploaders"
 
 end)
@@ -507,8 +601,11 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method sort_items c s1 s2 =
-      match c with
+    method sort_items c k1 k2 =
+      try
+        let s1 = uploader_of_key k1 in
+        let s2 = uploader_of_key k2 in
+        match c with
           Col_client_name -> compare (String.lowercase s1.source_name) (String.lowercase s2.source_name)
         | Col_client_state -> compare s1.source_state s2.source_state
         | Col_client_kind -> compare s1.source_kind s2.source_kind
@@ -522,6 +619,7 @@
         | Col_client_download_rate -> compare s1.source_download_rate s2.source_download_rate
         | Col_client_upload_rate -> compare s2.source_upload_rate s2.source_upload_rate
         | Col_client_upload -> compare s1.source_upload s2.source_upload
+      with _ -> 0
 
 (*************************************************************************)
 (*                                                                       *)
@@ -530,9 +628,10 @@
 (*************************************************************************)
 
     method force_update_icons () =
-      List.iter (fun s ->
+      List.iter (fun k ->
         try
-          let (row, _) = self#find_item s.source_num in 
+          let s = uploader_of_key k in
+          let row = self#find_row k in
           store#set ~row ~column:uploader_network_pixb (Mi.network_pixb s.source_network ~size:A.SMALL ());
           store#set ~row ~column:uploader_name_pixb (Mi.source_type_to_icon s.source_type ~size:A.SMALL);
         with _ -> ()
@@ -600,15 +699,16 @@
 
 let copy_ed2k_links sel () = 
   let buf = Buffer.create 100 in
-  List.iter (fun s ->
-    match s.shared_uids with
+  let l = keys_to_shared_files sel in
+  List.iter (fun si ->
+    match si.g_shared_uids with
         uid :: _ -> (
           match (Uid.to_uid uid) with
               Ed2k md4 ->
                 begin
                   let link = Printf.sprintf "ed2k://|file|%s|%Ld|%s|" 
-                             (Url.encode (Filename.basename s.shared_filename))
-                             s.shared_size
+                             (Url.encode (Filename.basename si.g_shared_filename))
+                             si.g_shared_size
                              (Md4.to_string md4)
                   in
                   Printf.bprintf buf "%s\n" link;
@@ -616,7 +716,7 @@
             | _ -> ())
 
       | _ -> ()
-  ) sel;
+  ) l;
   let link = Buffer.contents buf in
   GuiConsole.insert link;
   GMain.clipboard#clear ();
@@ -631,7 +731,7 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let upload_menu (sel : shared_info list) =
+let upload_menu sel =
   match sel with
       [] -> []
     | _ ->
@@ -644,8 +744,11 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let filter_upload (si : shared_info) =
-  not (List.memq si.shared_network !G.networks_filtered)
+let filter_upload k =
+  try
+    let si = shared_file_of_key k in
+    not (List.memq si.g_shared_network !G.networks_filtered)
+  with _ -> true
 
 (*************************************************************************)
 (*                                                                       *)
@@ -672,32 +775,69 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let add_upload s =
-  ignore (uploadstore#add_item s);
-  update_uploads_label ()
-
-let remove_upload s =
-  uploadstore#remove_item s;
-  update_uploads_label ()
+let hashtbl_update_shared_files si si_new =
+  si.g_shared_filename <- si_new.g_shared_filename;
+  si.g_shared_size <- si_new.g_shared_size;
+  si.g_shared_uploaded <- si_new.g_shared_uploaded;
+  si.g_shared_requests <- si_new.g_shared_requests;
+  si.g_shared_uids <- si_new.g_shared_uids;
+  si.g_shared_last_seen <- si_new.g_shared_last_seen
+
+let add_upload si =
+  ignore (uploadstore#add_item si);
+  Hashtbl.add G.shared_files si.g_shared_num si;
+  update_uploads_label ();
+  let si_uid = Mi.to_uid_type si.g_shared_uids in
+  GuiGraphBase.add_file si_uid;
+  Hashtbl.add G.file_by_uid si_uid (U.utf8_of si.g_shared_filename)
+
+let remove_upload si =
+  uploadstore#remove_item (shared_file_key si.g_shared_num);
+  Hashtbl.remove G.shared_files si.g_shared_num;
+  update_uploads_label ();
+  let si_uid = Mi.to_uid_type si.g_shared_uids in
+  GuiGraphBase.cancel_file si_uid;
+  Hashtbl.remove G.file_by_uid si_uid
 
-let h_shared_file_info s_new =
+let h_shared_file_info si_new =
   try
-    let (row ,s) = uploadstore#find_item s_new.shared_num in
-    uploadstore#update_item row s s_new
-  with Not_found -> add_upload s_new
+    let si = Hashtbl.find G.shared_files si_new.g_shared_num in
+    let rate = (Int64.to_float si_new.g_shared_uploaded -. Int64.to_float si.g_shared_uploaded) /.
+               (si_new.g_shared_last_seen -. si.g_shared_last_seen)
+    in
+    let row = uploadstore#find_row (shared_file_key si_new.g_shared_num) in
+    uploadstore#update_item row si si_new;
+    hashtbl_update_shared_files si si_new;
+    let rate = int_of_float rate in
+    GuiGraphBase.save_record rate (GraphFile ((Mi.to_uid_type si.g_shared_uids), GraphUploads));
+  with Not_found -> add_upload si_new
 
 let h_shared_file_upload shared_num upsize requests =
   try
-    let (row, s) = uploadstore#find_item shared_num in
-    let s_new = {s with shared_uploaded = upsize; shared_requests = requests} in
-    uploadstore#update_item row s s_new
+    let si = Hashtbl.find G.shared_files shared_num in
+    let row = uploadstore#find_row (shared_file_key shared_num) in
+    let si_new = {si with
+                  g_shared_uploaded = upsize;
+                  g_shared_requests = requests;
+                  g_shared_last_seen = BasicSocket.current_time ()
+    } in
+    let rate = (Int64.to_float si_new.g_shared_uploaded -. Int64.to_float si.g_shared_uploaded) /.
+               (si_new.g_shared_last_seen -. si.g_shared_last_seen)
+    in
+    let rate = int_of_float rate in
+    uploadstore#update_item row si si_new;
+    si.g_shared_size <- si_new.g_shared_size;
+    si.g_shared_uploaded <- si_new.g_shared_uploaded;
+    si.g_shared_requests <- si_new.g_shared_requests;
+    si.g_shared_last_seen <- si_new.g_shared_last_seen;
+    GuiGraphBase.save_record rate (GraphFile ((Mi.to_uid_type si.g_shared_uids), GraphUploads))
   with Not_found ->
     (if !!verbose then lprintf' "Shared file %d not found\n" shared_num)
 
 let h_shared_file_unshared shared_num =
   try
-    let (row, s) = uploadstore#find_item shared_num in
-    remove_upload s
+    let si = Hashtbl.find G.shared_files shared_num in
+    remove_upload si
   with Not_found ->
     (if !!verbose then lprintf' "Shared file %d not found" shared_num)
 
@@ -730,20 +870,25 @@
 (*************************************************************************)
 
 let add_to_friends sel () =
+  let l = keys_to_uploaders sel in
   List.iter (fun s ->
     GuiCom.send (AddClientFriend s.source_num)
-  ) sel
+  ) l
 
 let browse_files = add_to_friends
 
-let show_details s () =
-  let item = Source (s, 0) in
-  GuiInfoWindow.window item ()
+let show_details k () =
+  try
+    let s = uploader_of_key k in
+    let item = Source (s, 0) in
+    GuiInfoWindow.window item ()
+  with _ -> ()
 
-let update_all uploaders =
+let update_all keys =
+  let l = keys_to_uploaders keys in
   List.iter (fun s ->
     GuiCom.send (GetClient_info s.source_num)
-  ) uploaders;
+  ) l;
   GuiCom.send GetUploaders;
   GuiCom.send GetPending
 
@@ -753,14 +898,14 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let uploader_menu (sel : source_info list) =
+let uploader_menu sel =
   match sel with
       [] -> []
-    | s :: tail ->
+    | k :: tail ->
           (if tail = []
              then
                [
-                `I ((!M.dT_me_show_source_details), show_details s) ;
+                `I ((!M.dT_me_show_source_details), show_details k) ;
                ]
              else  [])
              @
@@ -775,9 +920,12 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let filter_uploader (s : source_info) =  not (
-  (not !show_pending && not s.source_has_upload) ||
-  List.memq s.source_network !G.networks_filtered)
+let filter_uploader k =
+  try
+    let s = uploader_of_key k in
+    not ((not !show_pending && (s.source_has_upload <> source_has_upload)) ||
+          List.memq s.source_network !G.networks_filtered)
+  with _ -> true
 
 (*************************************************************************)
 (*                                                                       *)
@@ -805,74 +953,64 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let remove_uploader uploader_num =
-  try
-    let (_, s) = uploaderstore#find_item uploader_num in
-    uploaderstore#remove_item s;
-    (if s.source_has_upload then decr nuploading);
-    s.source_has_upload <- false;
-    update_uploaders_label ()
-  with _ -> ()
-
-let update_uploader s_new =
-  try
-    let (row, s) = uploaderstore#find_item s_new.source_num in
-    if s_new.source_has_upload && not s.source_has_upload
-      then begin
-        incr nuploading
-      end else if (not s_new.source_has_upload) && s.source_has_upload
-        then begin 
-          decr nuploading
-        end;
-    uploaderstore#update_item row s s_new;
-    update_uploaders_label ()
-  with _ -> 
-    begin
-      ignore (uploaderstore#add_item s_new);
-      (if s_new.source_has_upload then incr nuploading);
-      update_uploaders_label ()
-    end
+let remove_uploader s =
+  uploaderstore#remove_item (uploader_key s.source_num);
+  (if s.source_has_upload = source_has_upload then decr nuploading);
+  s.source_has_upload <- source_only
+
+let update_uploaders () =
+  let keys = uploaderstore#all_items () in
+  let all_uploaders = keys_to_uploaders keys in
+  List.iter (fun s ->
+    if not (List.mem_assoc s.source_num !uploaders_n_pendings)
+      then remove_uploader s
+  ) all_uploaders;
+  List.iter (fun (num, has_upload) ->
+    try
+      let s = Hashtbl.find G.sources num in
+      let s_new = {s with source_has_upload = has_upload} in
+      try
+        let row = uploaderstore#find_row (uploader_key num) in
+        uploaderstore#update_item row s s_new;
+        begin
+          if s_new.source_has_upload = source_has_upload && s.source_has_upload <> source_has_upload
+            then incr nuploading
+            else if s_new.source_has_upload <> source_has_upload && s.source_has_upload = source_has_upload
+              then decr nuploading
+        end;
+        s.source_has_upload <- s_new.source_has_upload
+      with _ ->
+        begin
+          s.source_has_upload <- s_new.source_has_upload;
+          ignore (uploaderstore#add_item s);
+          (if s.source_has_upload = source_has_upload then incr nuploading)
+        end
+    with _ ->
+      begin
+        GuiCom.send (GetClient_info num)
+      end
+  ) !uploaders_n_pendings;
+  uploaders_n_pendings := [];
+  update_uploaders_label ()
 
-let h_update_uploader s_new =
+let h_update_uploader s s_new =
   try
-    let (row, s) = uploaderstore#find_item s_new.source_num in
+    let row = uploaderstore#find_row (uploader_key s_new.source_num) in
     uploaderstore#update_item row s s_new
   with _ -> ()
 
 let h_update_uploaders uploaders =
-  let all_uploaders = List.filter (fun s -> s.source_has_upload) (uploaderstore#all_items ()) in
-  List.iter (fun s ->
-    if not (List.mem s.source_num uploaders)
-      then remove_uploader s.source_num;
-  ) all_uploaders;
   List.iter (fun uploader_num ->
-    try
-      let s = Hashtbl.find G.sources uploader_num in
-      let s_new = {s with source_has_upload = true} in
-      update_uploader s_new;
-      s.source_has_upload <- true
-    with Not_found ->
-      GuiCom.send (GetClient_info uploader_num)
+    uploaders_n_pendings := (uploader_num, source_has_upload) :: !uploaders_n_pendings
   ) uploaders
 
 let h_update_pending_slots pending_slots =
-  let all_pendings = List.filter (fun s -> not s.source_has_upload) (uploaderstore#all_items ()) in
-  List.iter (fun s ->
-    if not (List.mem s.source_num pending_slots)
-      then remove_uploader s.source_num
-  ) all_pendings;
   List.iter (fun uploader_num ->
-    try
-      let s = Hashtbl.find G.sources uploader_num in
-      let s_new = {s with source_has_upload = false} in
-      update_uploader s_new;
-      s.source_has_upload <- false
-    with Not_found ->
-      GuiCom.send (GetClient_info uploader_num)
+    uploaders_n_pendings := (uploader_num, source_has_slot) :: !uploaders_n_pendings
   ) pending_slots
 
 let clean_uploaders_table uploader =
-  remove_uploader uploader.source_num
+  remove_uploader uploader
 
 (*************************************************************************)
 (*                                                                       *)
@@ -923,7 +1061,6 @@
       view_context := None;
       upload_label := None;
       uploader_label := None;
-      Timeout.remove (!uploaders_timerID);
   ));
   let vbox_uploads =
     GPack.vbox ~homogeneous:false
@@ -994,12 +1131,17 @@
   uploader_label := Some uploaders_label;
   update_uploads_label ();
   update_uploaders_label ();
-  
-  uploaders_timerID := (Timeout.add ~ms:6000 ~callback:
-    (fun _ -> 
+  vpaned_uploads#coerce
+
+
+
+let _ =
+  ignore (Timeout.add ~ms:6000 ~callback:
+    (fun _ ->
+       update_uploaders ();
        update_all (uploaderstore#all_items ());
        refresh_uploadstats ();
        true
-  ));
+  ))
+
 
-  vpaned_uploads#coerce
diff -urN src/gtk2/gui/guiUsers.ml src/gtk2/gui/guiUsers.ml
--- src/gtk2/gui/guiUsers.ml	2005-03-02 20:45:12.000000000 +0100
+++ src/gtk2/gui/guiUsers.ml	2005-09-17 14:44:32.000000000 +0200
@@ -42,19 +42,66 @@
 
 (*************************************************************************)
 (*                                                                       *)
+(*                         user_num                                      *)
+(*                                                                       *)
+(*************************************************************************)
+
+let user_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         user_of_key                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let user_of_key key =
+  try
+    let num = user_num key in
+    Hashtbl.find G.users num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_users                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_users keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = user_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         user_key                                      *)
+(*                                                                       *)
+(*************************************************************************)
+
+let user_key user_num =
+  Printf.sprintf "%d" user_num
+
+(*************************************************************************)
+(*                                                                       *)
 (*                         message to the core                           *)
 (*                                                                       *)
 (*************************************************************************)
-    
+
 let add_to_friends sel ()  =
+  let l = keys_to_users sel in
   List.iter (fun u ->
     GuiCom.send (AddUserFriend u.user_num)
-  ) sel
+  ) l
 
 let browse_files sel () =
+  let l = keys_to_users sel in
   List.iter (fun u ->
     GuiCom.send (BrowseUser u.user_num)
-  ) sel
+  ) l
 
 
 (*************************************************************************)
@@ -63,7 +110,7 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let user_menu (sel : user_info list) =
+let user_menu sel =
   match sel with
       [] -> []
     | _ ->
@@ -72,6 +119,16 @@
            `I (!M.rT_me_browse_files, browse_files sel) ;
           ]
 
+(*************************************************************************)
+(*                                                                       *)
+(*                         hashtbl_users_update                          *)
+(*                                                                       *)
+(*************************************************************************)
+
+let hashtbl_users_update u u_new =
+  u.user_tags <- u_new.user_tags
+
+
 module UserList(R:
 
 (*************************************************************************)
@@ -104,16 +161,14 @@
       module Column = GuiColumns.User
 
       type item = user_info
-      type key = int
 
       let columns = R.columns
-      let get_key = (fun u -> u.user_num)
+      let get_key = (fun u -> user_key u.user_num)
       let module_name = R.module_name
     end)
 
     class g_user () =
       let user_cols     = new GTree.column_list in
-      let user_num      = user_cols#add Gobject.Data.int in
       let user_name     = user_cols#add Gobject.Data.string in
       let user_md4      = user_cols#add Gobject.Data.string in
       let user_ip_port  = user_cols#add Gobject.Data.string in
@@ -129,7 +184,6 @@
 (*************************************************************************)
 
         method from_item row (u : user_info) =
-          store#set ~row ~column:user_num u.user_num;
           store#set ~row ~column:user_name (U.utf8_of u.user_name);
           store#set ~row ~column:user_ip_port (Mi.ip_to_string u.user_ip u.user_port);
           store#set ~row ~column:user_md4 (Md4.to_string u.user_md4);
@@ -200,12 +254,16 @@
 (*                                                                       *)
 (*************************************************************************)
 
-        method sort_items c u1 u2 =
-          match c with
+        method sort_items c k1 k2 =
+          try
+            let u1 = user_of_key k1 in
+            let u2 = user_of_key k2 in
+            match c with
               Col_user_name -> compare (String.lowercase u1.user_name) (String.lowercase u2.user_name)
             | Col_user_addr -> compare u1.user_ip u2.user_ip
             | Col_user_tags -> compare u1.user_tags u2.user_tags
             | Col_user_md4 -> compare u1.user_md4 u2.user_md4
+          with _ -> 0
 
       end
 
diff -urN src/gtk2/gui/guiUtf8.ml src/gtk2/gui/guiUtf8.ml
--- src/gtk2/gui/guiUtf8.ml	2005-03-02 20:45:12.000000000 +0100
+++ src/gtk2/gui/guiUtf8.ml	2005-10-23 14:28:52.000000000 +0200
@@ -724,7 +724,7 @@
   enc_list := List.filter (fun enc -> enc <> charset) !enc_list;
   enc_list := if (charset <> "UTF-8") then charset::!enc_list else !enc_list;
   List.iter (fun enc ->
-    Printf.printf "Use encoding %s\n" enc; 
+    Printf2.lprintf "Use encoding %s\n" enc; 
     flush stdout
   ) !enc_list;
   nenc := List.length !enc_list
diff -urN src/gtk2/gui/guiWindow.ml src/gtk2/gui/guiWindow.ml
--- src/gtk2/gui/guiWindow.ml	2005-04-04 21:36:46.000000000 +0200
+++ src/gtk2/gui/guiWindow.ml	2005-10-01 10:27:20.000000000 +0200
@@ -144,10 +144,10 @@
 (*                                                                       *)
 (*************************************************************************)
 
-(* TODO *)
-
 let display_graph gui () =
   clean gui.vbox;
+  let graph = GuiGraph.graph_box gui in
+  gui.vbox#add graph;
   gui.current_page <- 8
 
 (*************************************************************************)
@@ -217,6 +217,12 @@
       ~icon:(A.get_icon ~icon:M.icon_menu_console ~size:A.LARGE ())
       ~f:(display_console gui) ()
   in
+  let bGraph = gui.wtool#add_toggle_button
+      ~style:!!O.gtk_look_toolbars_style
+      ~markup:!M.mW_lb_graph
+      ~icon:(A.get_icon ~icon:M.icon_menu_graph ~size:A.LARGE ())
+      ~f:(display_graph gui) ()
+  in
 (*
  * TODO : make a Graph tab + some stats
  *
@@ -250,9 +256,7 @@
       | 5 -> bRooms#set_active true
       | 6 -> bUploads#set_active true
       | 7 -> bConsole#set_active true
-(*
       | 8 -> bGraph#set_active true
-*)
       | _ -> bDownloads#set_active true
   );
   gui.update_current_page <- (fun _ ->
@@ -264,9 +268,7 @@
       | 5 -> display_rooms gui ()
       | 6 -> display_uploads gui ()
       | 7 -> display_console gui ()
-(*
       | 8 -> display_graph gui ()
-*)
       | _ -> display_downloads gui ()
   )
 
@@ -342,4 +344,4 @@
   vbox#pack ~expand:false ~fill:true statusbar;
 
   gui
-      
+
diff -urN src/gtk2/im/guiImAccounts.ml src/gtk2/im/guiImAccounts.ml
--- src/gtk2/im/guiImAccounts.ml	2005-03-02 21:16:53.000000000 +0100
+++ src/gtk2/im/guiImAccounts.ml	2005-09-18 17:17:20.000000000 +0200
@@ -25,7 +25,7 @@
 open ImTypes
 open ImIdentity
 open ImChat
-open ImRoom  
+open ImRoom
 open GuiColumns
 
 module U = GuiUtf8
@@ -40,6 +40,14 @@
 
 (*************************************************************************)
 (*                                                                       *)
+(*                         Global tables                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let (act_by_num : (int, account) Hashtbl.t) = Hashtbl.create 13
+
+(*************************************************************************)
+(*                                                                       *)
 (*                         account_pixb                                  *)
 (*                                                                       *)
 (*************************************************************************)
@@ -71,6 +79,51 @@
 
 (*************************************************************************)
 (*                                                                       *)
+(*                         act_num                                       *)
+(*                                                                       *)
+(*************************************************************************)
+
+let act_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         act_of_key                                    *)
+(*                                                                       *)
+(*************************************************************************)
+
+let act_of_key key =
+  try
+    let num = act_num key in
+    Hashtbl.find act_by_num num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_acts                                  *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_acts keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = act_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         act_key                                       *)
+(*                                                                       *)
+(*************************************************************************)
+
+let act_key act_num =
+  Printf.sprintf "%d" act_num
+
+(*************************************************************************)
+(*                                                                       *)
 (*                         Templates                                     *)
 (*                                                                       *)
 (*************************************************************************)
@@ -80,10 +133,9 @@
   module Column = GuiColumns.IMAccount
 
   type item = account
-  type key = int
 
   let columns = O.account_columns
-  let get_key = (fun ac -> account_num ac)
+  let get_key = (fun ac -> act_key (account_num ac))
   let module_name = "IM Accounts"
 
 end)
@@ -167,11 +219,15 @@
 (*                                                                       *)
 (*************************************************************************)
 
-  method sort_items c ac1 ac2 =
-    match c with
+  method sort_items c k1 k2 =
+    try
+      let ac1 = act_of_key k1 in
+      let ac2 = act_of_key k2 in
+      match c with
         Col_account_name -> compare (account_name ac1) (account_name ac2)
       | Col_account_status -> compare (account_status ac1) (account_status ac2)
       | Col_account_protocol -> compare (account_protocol ac1) (account_protocol ac2)
+    with _ -> 0
 
   end
 
@@ -263,16 +319,18 @@
 (*************************************************************************)
 
 let settings sel () =
+  let l = keys_to_acts sel in
   List.iter (fun account ->
      input_account account
-  ) sel
+  ) l
 
 let connect sel () =
+  let l = keys_to_acts sel in
   List.iter (fun account ->
     match account_status account with
         Status_offline -> account_login account
       | _ -> account_logout account
-  ) sel
+  ) l
 
 let remove sel () = ()
 
@@ -292,10 +350,10 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let account_menu (sel : account list) =
+let account_menu sel =
   match sel with
       [] -> []
-    | account :: tail ->
+    | k :: tail ->
         begin
           let basic_menu = 
             [
@@ -304,20 +362,23 @@
               `I (!M.iM_me_remove, remove sel) ;
             ]
           in
-          if tail = [] && account_has_rooms account then
-            (`I (!M.iM_me_join_room, (fun _ -> ask_for_room account)))::
-            (let prefered_rooms = account_prefered_rooms account in
-             if prefered_rooms = []
-               then []
-               else
-                 [ `M (!M.iM_me_prefered_rooms,
-                      List.map (fun name ->
-                          `I ((U.utf8_of name), (fun _ -> 
-                                account_join_room account name))  
-                      ) prefered_rooms)]
-              ) @basic_menu
-            else basic_menu
-        end
+          try
+            let account = act_of_key k in
+            if tail = [] && account_has_rooms account then
+              (`I (!M.iM_me_join_room, (fun _ -> ask_for_room account)))::
+              (let prefered_rooms = account_prefered_rooms account in
+               if prefered_rooms = []
+                 then []
+                 else
+                   [ `M (!M.iM_me_prefered_rooms,
+                        List.map (fun name ->
+                            `I ((U.utf8_of name), (fun _ -> 
+                                  account_join_room account name))
+                        ) prefered_rooms)]
+                ) @basic_menu
+              else basic_menu
+            with _ -> basic_menu
+          end
 
 (*************************************************************************)
 (*                                                                       *)
@@ -328,7 +389,8 @@
 let h_update_account account =
   lprintf' "h_update_account %d\n" (account_num account);
   try
-    let (row, ac) = accountstore#find_item (account_num account) in
+    let ac = act_of_key (act_key (account_num account)) in
+    let row = accountstore#find_row (act_key (account_num account)) in
     lprintf' "Updating Account %d\n" (account_num account);
     accountstore#update_item row ac account;
     lprintf' "Updated Account %d\n" (account_num account)
diff -urN src/gtk2/im/guiImRooms.ml src/gtk2/im/guiImRooms.ml
--- src/gtk2/im/guiImRooms.ml	2005-03-02 21:16:53.000000000 +0100
+++ src/gtk2/im/guiImRooms.ml	2005-09-18 17:02:55.000000000 +0200
@@ -39,6 +39,59 @@
 
 (*************************************************************************)
 (*                                                                       *)
+(*                         Global tables                                 *)
+(*                                                                       *)
+(*************************************************************************)
+
+let (id_by_num : (int, identity) Hashtbl.t) = Hashtbl.create 13
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         id_num                                        *)
+(*                                                                       *)
+(*************************************************************************)
+
+let id_num key =
+  try int_of_string key with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         id_of_key                                     *)
+(*                                                                       *)
+(*************************************************************************)
+
+let id_of_key key =
+  try
+    let num = id_num key in
+    Hashtbl.find id_by_num num
+  with _ -> raise Not_found
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         keys_to_ids                                   *)
+(*                                                                       *)
+(*************************************************************************)
+
+let keys_to_ids keys =
+  let l = ref [] in
+  List.iter (fun k ->
+    try
+      let s = id_of_key k in
+      l := s :: !l
+    with _ -> ()) keys;
+  !l
+
+(*************************************************************************)
+(*                                                                       *)
+(*                         id_key                                        *)
+(*                                                                       *)
+(*************************************************************************)
+
+let id_key id_num =
+  Printf.sprintf "%d" id_num
+
+(*************************************************************************)
+(*                                                                       *)
 (*                         Templates                                     *)
 (*                                                                       *)
 (*************************************************************************)
@@ -48,10 +101,9 @@
   module Column = GuiColumns.IMIdentities
 
   type item = identity
-  type key = int
 
   let columns = O.identities_columns
-  let get_key = (fun c -> identity_num c)
+  let get_key = (fun c -> id_key (identity_num c))
   let module_name = "IM Identities"
 
 end)
@@ -98,8 +150,12 @@
 (*                                                                       *)
 (*************************************************************************)
 
-    method sort_items c id1 id2 =
-      compare (String.lowercase (identity_name id1)) (String.lowercase (identity_name id2))
+    method sort_items c k1 k2 =
+      try
+        let id1 = id_of_key k1 in
+        let id2 = id_of_key k2 in
+        compare (String.lowercase (identity_name id1)) (String.lowercase (identity_name id2))
+      with _ -> 0
 
   end
 
@@ -140,8 +196,11 @@
 (*                                                                       *)
 (*************************************************************************)
 
-let on_double_click_identity id =
-  identity_open_chat id
+let on_double_click_identity k =
+  try
+    let id = id_of_key k in
+    identity_open_chat id
+  with _ -> ()
 
 (*************************************************************************)
 (*                                                                       *)
@@ -192,16 +251,19 @@
 
 let update_identity id_new idstore =
   try
-    let (row, id) = idstore#find_item (identity_num id_new) in
-    idstore#update_item row id id_new
-  with _ -> 
-    ignore (idstore#add_item id_new)
+    let id = Hashtbl.find id_by_num (identity_num id_new) in
+    let row = idstore#find_row (id_key (identity_num id_new)) in
+    idstore#update_item row id id_new;
+    Hashtbl.replace id_by_num (identity_num id_new) id_new
+  with _ ->
+    begin
+      ignore (idstore#add_item id_new);
+      Hashtbl.add id_by_num (identity_num id_new) id_new
+    end
 
-let remove_identity id idstore = 
-  try
-    let (_, id) = idstore#find_item (identity_num id) in
-    idstore#remove_item id
-  with _ -> ()
+let remove_identity id idstore =
+  idstore#remove_item (id_key (identity_num id));
+  Hashtbl.remove id_by_num (identity_num id)
 
 let h_join_room room (note : GPack.notebook) =
   try
@@ -225,7 +287,8 @@
     ro.buffer#clear ();
     ro.store#clear ();
     ro.box#destroy ();
-    Hashtbl.remove rooms (room_num room)
+    Hashtbl.remove rooms (room_num room);
+    Hashtbl.clear id_by_num
   with _ -> ()
 
 let h_room_event room event =
