From 0b9477affd8b7978768ee76bc5631adbec3a2dcf Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sun, 30 Nov 2008 13:01:16 +0000 Subject: [PATCH] Tabbed notebook functionality. --- internal.h | 9 ++--- main.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++---------- wui_thread.c | 13 ++++--- 3 files changed, 109 insertions(+), 27 deletions(-) diff --git a/internal.h b/internal.h index 8f5ec01..9c39bef 100644 --- a/internal.h +++ b/internal.h @@ -104,17 +104,18 @@ struct vm { int hostid; int id; int vnc_port; + char *uuid; /* Printable UUID. */ - /* Only description, hostid, id and vnc_port fields are required. - * The remainder may be NULL / -1 to indicate they were missing in - * the data we got back from the WUI. + /* Only the fields above this point are required. The remainder may + * be NULL / -1 to indicate they were missing in the data we got + * back from the WUI. */ + long mem_allocated; /* Kbytes */ long mem_used; /* Kbytes */ int vcpus_allocated; int vcpus_used; char *state; - char *uuid; /* Printable UUID. */ char *mac_addr; /* Printable MAC addr. */ }; diff --git a/main.c b/main.c index 954ac3e..cf37b31 100644 --- a/main.c +++ b/main.c @@ -76,6 +76,9 @@ static GtkWidget *login_area; static GtkWidget *la_username; static GtkWidget *la_password; static GtkWidget *la_button; +static GtkWidget *notebook; +static GtkWidget *statusbar; +static guint statusbar_ctx; static GdkCursor *busy_cursor; /* Menus. */ @@ -221,7 +224,6 @@ start_ui (void) GtkWidget *ca_hbox; GtkWidget *ca_label; GtkWidget *la_hbox; - GtkWidget *notebook; DEBUG ("creating viewer windows and menus"); @@ -336,10 +338,16 @@ start_ui (void) /* Tabbed notebook. */ notebook = gtk_notebook_new (); - gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT); + /*gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);*/ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), TRUE); gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE); + /* Status bar. */ + statusbar = gtk_statusbar_new (); + statusbar_ctx = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), + "context"); + gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, ""); + /* Packing. */ gtk_container_add (GTK_CONTAINER (window), vbox); gtk_container_add_with_properties (GTK_CONTAINER (vbox), menubar, @@ -350,6 +358,8 @@ start_ui (void) "expand", FALSE, "fill", TRUE, NULL); gtk_container_add_with_properties (GTK_CONTAINER (vbox), notebook, "expand", TRUE, NULL); + gtk_container_add_with_properties (GTK_CONTAINER (vbox), statusbar, + "expand", FALSE, NULL); /* Show widgets. */ gtk_widget_show_all (window); @@ -427,6 +437,57 @@ login_to_wui (GtkWidget *widget, gpointer data) wui_thread_send_login (username, password); } +/* Connect to a virtual machine. This callback is called from the + * connect menu. It searches the notebook of gtk-vnc widgets to see + * if we have already connected to this machine, and if not it + * makes a new connection. + */ +static void +connect_to_vm (GtkWidget *widget, gpointer _vm) +{ + struct vm *vm = (struct vm *) _vm; + int n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)); + int i, uuidlen, len; + GtkWidget *child; + const char *label; + char *label2; + + DEBUG ("searching tabs for uuid %s", vm->uuid); + + uuidlen = strlen (vm->uuid); + + /* Search the tabs for this UUID, and if found, switch to it and return. */ + for (i = 0; i < n; ++i) { + child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i); + label = gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (notebook), child); + len = strlen (label); + if (len >= uuidlen && + STREQ (label + len - uuidlen, vm->uuid)) { + DEBUG ("found on tab %d", i); + gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i); + return; + } + } + + DEBUG ("not found, creating new tab"); + + /* This VM isn't in the notebook already, so create a new console. */ + len = strlen (vm->description) + 1 + strlen (vm->uuid) + 1; + label2 = g_alloca (len); + snprintf (label2, len, "%s %s", vm->description, vm->uuid); + + child = gtk_label_new (label2); /* XXX */ + + /* NB. We have to do this before adding it to the notebook. */ + gtk_widget_show (child); + + i = gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child, NULL); + gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (notebook), child, label2); + gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i); + + DEBUG ("finished creating new tab"); +} + /* Remove all menu items from the Connect menu. */ static void remove_menu_item (GtkWidget *menu_item, gpointer data) @@ -579,37 +640,38 @@ main_status_error (gpointer _str) DEBUG ("status error: %s", str); ASSERT_IS_MAIN_THREAD (); - /* - gtk_label_set_text (GTK_LABEL (...), str); - */ + gtk_statusbar_pop (GTK_STATUSBAR (statusbar), statusbar_ctx); + gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, str); g_free (str); return FALSE; } -/* The WUI thread has updated the vm list. */ -static void -add_vm_to_connectmenu (gpointer _vm, gpointer data) -{ - struct vm *vm = (struct vm *) _vm; - GtkWidget *item; - - DEBUG ("adding %s to Connect menu", vm->description); +/* The WUI thread has updated the vm list. Here in the main thread + * we keep our own copy of the vmlist. + */ +static GSList *vmlist = NULL; - item = gtk_menu_item_new_with_label (vm->description); - gtk_menu_append (GTK_MENU (connectmenu), item); -} +static void add_vm_to_connectmenu (gpointer _vm, gpointer data); gboolean main_vmlist_updated (gpointer data) { - GSList *vmlist; + GSList *new_vmlist; DEBUG ("vmlist updated"); ASSERT_IS_MAIN_THREAD (); /* Get the new vmlist. */ - if (wui_thread_get_vmlist (&vmlist)) { + if (wui_thread_get_vmlist (&new_vmlist)) { + /* Free the previous vmlist. This invalidates all the vm pointers + * contained in the Connect menu callbacks, but we're going to + * delete those callbacks and create news ones in a moment anyway ... + */ + free_vmlist (vmlist); + + vmlist = new_vmlist; + clear_connectmenu (); gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist); @@ -618,7 +680,6 @@ main_vmlist_updated (gpointer data) gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist_separator); g_slist_foreach (vmlist, add_vm_to_connectmenu, NULL); } - free_vmlist (vmlist); /* Grrrr Gtk is stupid. */ gtk_widget_show_all (connectmenu); @@ -626,3 +687,18 @@ main_vmlist_updated (gpointer data) return FALSE; } + +static void +add_vm_to_connectmenu (gpointer _vm, gpointer data) +{ + struct vm *vm = (struct vm *) _vm; + GtkWidget *item; + + DEBUG ("adding %s to Connect menu", vm->description); + + item = gtk_menu_item_new_with_label (vm->description); + gtk_menu_append (GTK_MENU (connectmenu), item); + + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (connect_to_vm), vm); +} diff --git a/wui_thread.c b/wui_thread.c index 060c7a2..2500ccb 100644 --- a/wui_thread.c +++ b/wui_thread.c @@ -245,9 +245,9 @@ wui_thread (gpointer _queue) /* This checks wui_gthread global which is actually set in the * main thread. Of course, it might not be set if the WUI thread - * runs first. Hence we yield for the main thread to run. + * runs first. Hence we sleep for the main thread to run. (XXX) */ - g_thread_yield (); + g_usleep (100000); ASSERT_IS_WUI_THREAD (); g_async_queue_ref (queue); @@ -798,8 +798,8 @@ copy_vm (struct vm *vm) vm2 = g_memdup (vm, sizeof (*vm)); vm2->description = g_strdup (vm->description); + vm2->uuid = g_strdup (vm->uuid); vm2->state = vm->state ? g_strdup (vm->state) : NULL; - vm2->uuid = vm->uuid ? g_strdup (vm->uuid) : NULL; vm2->mac_addr = vm->mac_addr ? g_strdup (vm->mac_addr) : NULL; return vm2; } @@ -878,12 +878,15 @@ parse_vmlist_from_xml (const char *xml) char *error_str; GSList *new_vmlist = NULL; struct vm *vm; + int len; /*DEBUG ("XML =\n%s", xml);*/ ASSERT_IS_WUI_THREAD (); /* We don't really expect that we won't be able to parse the XML ... */ - doc = xmlParseDoc ((const xmlChar *) xml); + len = strlen (xml); + doc = xmlReadMemory (xml, len, NULL, NULL, 0); + if (!doc) { DEBUG ("error parsing XML document, xml =\n%s", xml); error_str = g_strdup ("error parsing XML document from remote server"); @@ -1062,6 +1065,8 @@ parse_vm_from_xml (xmlNodePtr node) DEBUG ("required field \"description\" missing from structure"); else if (vm.vnc_port == -1) DEBUG ("required field \"vnc-port\" missing from structure"); + else if (vm.uuid == NULL) + DEBUG ("required field \"uuid\" missing from structure"); else ret = g_memdup (&vm, sizeof vm); -- 1.8.3.1