Add status bar, progress bar, command line. master
authorRichard W.M. Jones <rjones@redhat.com>
Sun, 4 Sep 2011 13:21:53 +0000 (14:21 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Sun, 4 Sep 2011 13:21:53 +0000 (14:21 +0100)
.depend
Makefile.am
cmdline.ml [new file with mode: 0644]
cmdline.mli [new file with mode: 0644]
main.ml
source_tab.ml
source_tab.mli
window.ml
window.mli

diff --git a/.depend b/.depend
index 7cd1b72..93e710b 100644 (file)
--- a/.depend
+++ b/.depend
@@ -1,3 +1,6 @@
+cmdline.cmi:
+cmdline.cmo: utils.cmi config.cmi cmdline.cmi
+cmdline.cmx: utils.cmx config.cmx cmdline.cmi
 config.cmi:
 config.cmo: config.cmi
 config.cmx: config.cmi
@@ -7,8 +10,8 @@ destination_tab.cmx: destination_tab.cmi
 logvols_tab.cmi:
 logvols_tab.cmo: logvols_tab.cmi
 logvols_tab.cmx: logvols_tab.cmi
-main.cmo: window.cmi slave.cmi
-main.cmx: window.cmx slave.cmx
+main.cmo: window.cmi utils.cmi slave.cmi config.cmi cmdline.cmi
+main.cmx: window.cmx utils.cmx slave.cmx config.cmx cmdline.cmx
 partitions_tab.cmi:
 partitions_tab.cmo: partitions_tab.cmi
 partitions_tab.cmx: partitions_tab.cmi
@@ -18,12 +21,12 @@ slave.cmx: utils.cmx slave_types.cmx slave.cmi
 slave_types.cmi:
 slave_types.cmo: slave_types.cmi
 slave_types.cmx: slave_types.cmi
-source_tab.cmi:
-source_tab.cmo: utils.cmi slave_types.cmi slave.cmi source_tab.cmi
-source_tab.cmx: utils.cmx slave_types.cmx slave.cmx source_tab.cmi
+source_tab.cmi: cmdline.cmi
+source_tab.cmo: utils.cmi slave_types.cmi slave.cmi cmdline.cmi source_tab.cmi
+source_tab.cmx: utils.cmx slave_types.cmx slave.cmx cmdline.cmx source_tab.cmi
 utils.cmi:
 utils.cmo: config.cmi utils.cmi
 utils.cmx: config.cmx utils.cmi
-window.cmi:
-window.cmo: source_tab.cmi partitions_tab.cmi logvols_tab.cmi destination_tab.cmi window.cmi
-window.cmx: source_tab.cmx partitions_tab.cmx logvols_tab.cmx destination_tab.cmx window.cmi
+window.cmi: source_tab.cmi
+window.cmo: utils.cmi source_tab.cmi partitions_tab.cmi logvols_tab.cmi destination_tab.cmi window.cmi
+window.cmx: utils.cmx source_tab.cmx partitions_tab.cmx logvols_tab.cmx destination_tab.cmx window.cmi
index 8962540..5fe6bce 100644 (file)
@@ -29,6 +29,8 @@ CLEANFILES = *.cmi *.cmo *.cmx *.cmxa *.o virt-resize-ui *~
 
 # These are listed here in alphabetical order.
 SOURCES = \
+       cmdline.mli \
+       cmdline.ml \
        config.mli \
        destination_tab.mli \
        destination_tab.ml \
@@ -55,6 +57,7 @@ BUILT_SOURCES = \
 OBJECTS = \
        config.cmo \
        utils.cmo \
+       cmdline.cmo \
        slave_types.cmo \
        slave.cmo \
        source_tab.cmo \
diff --git a/cmdline.ml b/cmdline.ml
new file mode 100644 (file)
index 0000000..22e8d0b
--- /dev/null
@@ -0,0 +1,99 @@
+(* Virt resize UI.
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Utils
+
+type cli_request =
+  | Empty_window
+  | Open_guest of string
+  | Open_images of (string * string option) list
+
+let display_version () =
+  printf "%s %s\n" Config.package Config.version;
+  exit 0
+
+let format = ref None
+let images = ref []
+let guests = ref []
+
+let set_connect = function
+  | "" -> set_connect_uri None
+  | uri -> set_connect_uri (Some uri)
+let set_format = function
+  | "" -> format := None
+  | f -> format := Some f
+let add_image image =
+  images := (image, !format) :: !images
+let add_guest guest =
+  guests := guest :: !guests
+
+(* Parse command line arguments. *)
+let argspec = Arg.align [
+  "-a",        Arg.String add_image,      "image Open disk image";
+  "--add",     Arg.String add_image,      "image Open disk image";
+  "-c",        Arg.String set_connect,    "uri Connect to libvirt URI";
+  "--connect", Arg.String set_connect,    "uri Connect to libvirt URI";
+  "-d",        Arg.String add_guest,      "guest Open libvirt guest";
+  "--domain",  Arg.String add_guest,      "guest Open libvirt guest";
+  "--format",  Arg.String set_format,     "format Set format";
+  "-v",        Arg.Unit set_verbose_flag, " Enable debugging messages";
+  "--verbose", Arg.Unit set_verbose_flag, " Enable debugging messages";
+  "-V",        Arg.Unit display_version,  " Display version and exit";
+  "--version", Arg.Unit display_version,  " Display version and exit";
+  "-x",        Arg.Unit set_trace_flag,   " Enable tracing of libguestfs calls";
+]
+
+let prog = Filename.basename Sys.executable_name
+
+let anon_fun _ =
+  raise (Arg.Bad "unknown argument")
+
+let usage_msg =
+  sprintf "\
+%s: graphical guest filesystem browser
+
+Usage:
+  %s
+    Open the program with an empty window.
+
+  %s -a disk.img [-a disk.img [...]]
+    Start with a guest from a disk image file.
+
+  %s -d guest
+    Start with the named libvirt guest.
+
+Options:"
+    prog prog prog prog
+
+let command_line () =
+  Arg.parse argspec anon_fun usage_msg;
+
+  (* Verify number of -a and -d options given on the command line. *)
+  let images = List.rev !images in
+  let guests = List.rev !guests in
+
+  match images, guests with
+  | [], [] -> Empty_window
+  | _, [] -> Open_images images
+  | [], [guest] -> Open_guest guest
+  | [], _ ->
+      failwith "cannot use -d option more than once"
+  | _, _ ->
+      failwith "cannot mix -a and -d options"
diff --git a/cmdline.mli b/cmdline.mli
new file mode 100644 (file)
index 0000000..3ad75c2
--- /dev/null
@@ -0,0 +1,34 @@
+(* Virt resize UI.
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+(** Handle the command line arguments. *)
+
+type cli_request =
+  | Empty_window
+  | Open_guest of string
+  | Open_images of (string * string option) list
+      (** The initial action requested by the user on the command line.
+
+          [Empty_window] means nothing was requested on the command line.
+
+          [Open_guest] means to open a guest (-d option).
+
+          [Open_images] means to open a list of disk images (-a option). *)
+
+val command_line : unit -> cli_request
+  (** Read the command line and return {!cli_request}. *)
diff --git a/main.ml b/main.ml
index 775f006..1a3e8db 100644 (file)
--- a/main.ml
+++ b/main.ml
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *)
 
+open Utils
+
 let () =
-  ignore (new Window.window);
+  let cli_request = Cmdline.command_line () in
+
+  (* If we're in verbose mode, print some debug information which
+     could be useful in bug reports.  *)
+  if verbose () then (
+    debug "%s %s" Config.package Config.version;
+    debug "libguestfs %s" (libguestfs_version_string ());
+    debug "libvirt %s" (libvirt_version_string ());
+  );
+
+  (* Create the main window. *)
+  let w = new Window.window in
+
+  (* Wire up hooks that carry messages from the slave thread
+   * to the main thread.
+   *)
+(*
+  Slave.set_failure_hook w#failure;
+  Slave.set_busy_hook w#throbber_busy;
+  Slave.set_idle_hook w#throbber_idle;
+*)
+  Slave.set_status_hook w#set_statusbar;
+  Slave.set_progress_hook w#progress;
+
+  (* What did the user request on the command line? *)
+  w#source_tab#run_cli_request cli_request;
+
+  (* Run the main display thread.  When this returns, the application
+     has been closed so force the slave thread to exit.  *)
   GtkThread.main ();
   Slave.exit_thread ()
index f791e80..797ba4a 100644 (file)
@@ -68,36 +68,54 @@ object (self)
   method private opened inspection_data =
     (* We expect that there are some filesystems in the image,
        otherwise fail. *)
-    if inspection_data.insp_all_filesystems = [] then
-      inspection.inspection_label#set_text
-        "error: no filesystems were found in the selected disk image or guest"
+    let nr_filesystems = List.length inspection_data.insp_all_filesystems in
+    if nr_filesystems = 0 then
+      inspection.inspection_label#set_label
+        "<b>error: no filesystems were found in the selected disk image or guest</b>"
     else (
       (match inspection_data.insp_oses with
       | [] ->                           (* no OS, but there were filesystems *)
-        inspection.inspection_label#set_text
-          "warning: no operating systems were recognized in this disk image or guest"
-
-      | [ os ] ->
-        let label =
-          sprintf "%s %s %d.%d"
-            os.insp_type os.insp_distro
-            os.insp_major_version os.insp_minor_version in
-        let label =
-          if os.insp_product_name <> "" then
-            label ^ " (" ^ os.insp_product_name ^ ")"
+        let markup =
+          sprintf "%d filesystem(s) found\n<b>warning: no operating systems were recognized in this disk image or guest</b>"
+            nr_filesystems in
+        inspection.inspection_label#set_label markup
+
+      | os :: oses ->
+        let markup =
+          if os.insp_product_name = "" then
+            sprintf "%s %s %d.%d\n"
+              os.insp_type os.insp_distro
+              os.insp_major_version os.insp_minor_version
           else
-            label in
-        inspection.inspection_label#set_text label
+            sprintf "%s\n" (markup_escape os.insp_product_name) in
+        let markup =
+          markup ^ sprintf "with %d filesystem(s)\n" nr_filesystems in
 
-      | _ ->
-        inspection.inspection_label#set_text
-          "warning: resizing multi-boot virtual machines may not be successful"
+        let markup =
+          if oses <> [] then
+            markup ^ "<b>warning: resizing multi-boot virtual machines may not be successful</b>"
+          else markup in
+
+        inspection.inspection_label#set_label markup
       );
 
       (* Raise the ready signal. *)
       ready_signal#call ()
     )
 
+  method run_cli_request = function
+  | Cmdline.Empty_window -> ()
+  | Cmdline.Open_images images ->
+    let msg = Slave.Open_images (images, self#opened) in
+    Slave.send_message msg
+  | Cmdline.Open_guest guest ->
+(*
+      (* Open libvirt connection, and in the callback open the guest. *)
+      let uri = connect_uri () in
+      Slave.connect uri (self#when_connected_cli_request guest)
+*)
+    assert false (* XXX not implemented *)
+
   initializer
     (* Set the inputs to be sensitive according to the state of the
        top level radio buttons. *)
@@ -206,6 +224,7 @@ let tab () =
 
   let inspection =
     let label = GMisc.label ~packing:(tbl#attach ~top:7 ~left:0 ~right:2) () in
+    label#set_use_markup true;
     { inspection_label = label } in
 
   (* Return the object. *)
index 7da62ad..f492088 100644 (file)
@@ -39,6 +39,11 @@ object ('a)
       information requested, and the source disk image or guest has
       been opened successfully. *)
   method ready : callback:(unit -> unit) -> GtkSignal.id
+
+  (** This function performs the {!Cmdline.cli_request} operation.
+      The actual operation happens asynchronously after this function
+      has returned. *)
+  method run_cli_request : Cmdline.cli_request -> unit
 end
 
 val tab : unit -> tab
index b00c6b0..a42f1a1 100644 (file)
--- a/window.ml
+++ b/window.ml
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *)
 
+open Printf
+
+open Utils
+
 type file_menu = {
   quit_item : GMenu.menu_item;
 }
@@ -41,6 +45,34 @@ type buttons = {
 class window =
   let title = "Resize a virtual machine - virt-resize-ui" in
 object (self)
+  val mutable tabs = None
+  val mutable statusbar_context = None
+  val mutable progress_bar : GRange.progress_bar option = None
+
+  method source_tab = (Option.get tabs).source_tab
+
+  method set_statusbar msg =
+    Option.may (
+      fun c ->
+        c#pop ();
+        ignore (c#push msg)
+    ) statusbar_context
+
+  method progress (position, total) =
+    Option.may (
+      fun pb ->
+        if position = 0L && total = 1L then
+          pb#pulse ()
+        else (
+          let frac = Int64.to_float position /. Int64.to_float total in
+          if frac < 0. || frac > 1. then
+            eprintf "warning: progress bar out of range: %Ld / %Ld (%g)\n"
+              position total frac;
+          let frac = if frac < 0. then 0. else if frac > 1. then 1. else frac in
+          pb#set_fraction frac
+        )
+    ) progress_bar
+
   initializer
   (* Window. *)
   let window = GWindow.window ~width:700 ~height:700 ~title () in
@@ -63,8 +95,10 @@ object (self)
     let about = factory#add_item "About virt-resize-ui ..." in
     { about_item = about } in
 
+  ignore help_menu;
+
   (* Tabbed notebook for main part of the display. *)
-  let tabs =
+  tabs <- (
     let nb = GPack.notebook ~packing:(vbox#pack ~expand:true ~fill:true) () in
 
     let src = Source_tab.tab () in
@@ -85,8 +119,15 @@ object (self)
       (GMisc.label ~text:"Expand logical volumes" () :> GObj.widget) in
     ignore (nb#append_page ~tab_label (lvs :> GObj.widget));
 
-    { source_tab = src; destination_tab = dest;
-      partitions_tab = parts; logvols_tab = lvs } in
+    Some { source_tab = src; destination_tab = dest;
+           partitions_tab = parts; logvols_tab = lvs });
+
+  (* Status bar and progress bar. *)
+  let hbox = GPack.hbox ~spacing:4 ~packing:vbox#pack () in
+  progress_bar <- Some (GRange.progress_bar ~packing:hbox#pack ());
+  let statusbar = GMisc.statusbar ~packing:(hbox#pack ~expand:true) () in
+  statusbar_context <- Some (statusbar#new_context ~name:"Standard");
+  ignore ((Option.get statusbar_context)#push title);
 
   (* Buttons. *)
   let buttons =
@@ -104,9 +145,6 @@ object (self)
     { prev_button = prev; next_button = next; go_button = go;
       exit_button = ex } in
 
-  ignore help_menu;
-  ignore tabs;
-
   (* Quit button. *)
   let quit _ = GMain.quit (); false in
   ignore (window#connect#destroy ~callback:GMain.quit);
index f8eadac..0a42362 100644 (file)
 
 class window :
 object
-  (* empty, for now *)
+  (** Return the source tab. *)
+  method source_tab : Source_tab.tab
+
+  (** Set the statusbar text. *)
+  method set_statusbar : string -> unit
+
+  (** Move the progress bar. *)
+  method progress : (int64 * int64) -> unit
+
 end