Initial original import from: fuse-2.4.2-2.fc4
[captive.git] / src / install / acquire / ui-gnome.c
index f5904df..eb52c09 100644 (file)
 #include <libgnomeui/gnome-app-util.h>
 #include <libgnomeui/gnome-druid-page-edge.h>
 #include "final.h"
+#include <libgnomevfs/gnome-vfs-utils.h>
 
 #include <captive/macros.h>
 
 
 /* Config: */
-#define PROGRESS_UPDATE_USEC 100000
+#define PROGRESS_UPDATE_USEC 200000
+/* Although proper GTK+ locking is provided below there are some
+ * bugs with compatibility of GTK+/Gnome-VFS/GConf.
+ * The main thread executes gtk_main()->g_main_loop_run()
+ * while the working thread initializes Gnome-VFS by GConf and
+ * executes also g_main_loop_run() while sharing some poll() fds.
+ */
+/* #define UI_GNOME_THREADS 1 */
 
 
 static GnomeApp *App;
@@ -59,6 +67,7 @@ static GnomeDruidPage *MicrosoftComPage;
 static GnomeDruidPage *PageFinish;
 static GtkEntry *ScanPathLocationComboEntry;
 static GtkButton *MicrosoftComConfirmButton;
+static GtkProgressBar *MicrosoftComProgress;
 enum {
                DRIVERS_TREE_STORE_COLUMN_TYPE,
                DRIVERS_TREE_STORE_COLUMN_ID,
@@ -130,6 +139,8 @@ static void state_changed(void)
                                (page_active!=PageFinish && page_active!=MicrosoftComPage));
                gtk_widget_set_sensitive(GTK_WIDGET(MicrosoftComConfirmButton),
                                (page_active==MicrosoftComPage));
+               gtk_progress_bar_set_fraction(MicrosoftComProgress,(gdouble)0);
+               gtk_progress_bar_set_text(MicrosoftComProgress,"");
                }
 }
 
@@ -139,6 +150,8 @@ GtkTreeIter *iter;
 
        g_return_if_fail(module_type!=NULL);
 
+       gdk_threads_enter();
+
        iter=DriversTreeStore_Iter_hash_get_iter(module_type);
        gtk_tree_store_set(DriversTreeStore,iter,
                        DRIVERS_TREE_STORE_COLUMN_TYPE,module_type,
@@ -149,6 +162,9 @@ GtkTreeIter *iter;
                                                        ? _("NOT FOUND; essential module for NTFS disks access")
                                                        : _("not found; optional module")),
                        -1);
+
+       gdk_flush();
+       gdk_threads_leave();
 }
 
 static void ui_gnome_module_available_notify(struct module_available *module_available)
@@ -160,32 +176,43 @@ static gboolean some_module_ntfs_sys_found=FALSE;
        g_return_if_fail(module_available!=NULL);
        g_return_if_fail(module_available->module!=NULL);
 
-       iter=DriversTreeStore_Iter_hash_get_iter(module_available->module->type);
+       gdk_threads_enter();
+
+       iter=DriversTreeStore_Iter_hash_get_iter((const gchar *)module_available->module->type);
        gtk_tree_store_set(DriversTreeStore,iter,
                        DRIVERS_TREE_STORE_COLUMN_TYPE,module_available->module->type,
                        DRIVERS_TREE_STORE_COLUMN_ID  ,module_available->module->id,
                        -1);
 
-       if (!strcmp(module_available->module->type,"ntoskrnl.exe"))
+       if (!strcmp((const char *)module_available->module->type,"ntoskrnl.exe"))
                some_module_ntoskrnl_exe_found=TRUE;
-       if (!strcmp(module_available->module->type,"ntfs.sys"))
+       if (!strcmp((const char *)module_available->module->type,"ntfs.sys"))
                some_module_ntfs_sys_found=TRUE;
 
        some_modules_found=some_module_ntoskrnl_exe_found && some_module_ntfs_sys_found;
        state_changed();
+
+       gdk_flush();
+       gdk_threads_leave();
 }
 
 static gboolean all_modules_found=FALSE;
 
 static void ui_gnome_all_modules_found_notify(void)
 {
+       gdk_threads_enter();
+
        all_modules_found=TRUE;
        state_changed();
+
+       gdk_flush();
+       gdk_threads_leave();
 }
 
 static gboolean aborted=FALSE;
 static gboolean aborted_back=FALSE;    /* 'Back' button was clicked. */
 static struct timeval ProgressEntry_updated_timeval;
+static struct timeval ProgressBar_updated_timeval;
 
 static void progress_start(void)
 {
@@ -193,6 +220,7 @@ static void progress_start(void)
        aborted=FALSE;
        aborted_back=FALSE;
        CAPTIVE_MEMZERO(&ProgressEntry_updated_timeval);
+       CAPTIVE_MEMZERO(&ProgressBar_updated_timeval);
        state_changed();
 }
 
@@ -202,10 +230,32 @@ static void progress_end(void)
        state_changed();
 }
 
+static gboolean want_progress_update(struct timeval *timeval)
+{
+struct timeval now_timeval;
+struct timeval diff_timeval;
+
+       g_return_val_if_fail(timeval!=NULL,FALSE);
+
+       gettimeofday(   /* FIXME: errors ignored */
+                       &now_timeval,   /* tv */
+                       NULL);  /* tz */
+       timersub(&now_timeval,timeval,&diff_timeval);
+       if (!timeval->tv_sec || diff_timeval.tv_sec>0 || diff_timeval.tv_usec>=PROGRESS_UPDATE_USEC) {
+               *timeval=now_timeval;
+               return TRUE;
+               }
+       return FALSE;
+}
+
 static gboolean ui_gnome_progress(GnomeVFSURI *uri)
 {
+gboolean want_gdk_flush=FALSE;
+
        /* 'uri' may be NULL */
 
+       gdk_threads_enter();
+
        if (ProgressEntry) {
 static gchar *uri_text=NULL;
 
@@ -219,22 +269,28 @@ static gchar *uri_text=NULL;
                        }
 
                if (uri_text) {
-struct timeval now_timeval;
-struct timeval diff_timeval;
-
-                       gettimeofday(   /* FIXME: errors ignored */
-                                       &now_timeval,   /* tv */
-                                       NULL);  /* tz */
-                       timersub(&now_timeval,&ProgressEntry_updated_timeval,&diff_timeval);
-                       if (!ProgressEntry_updated_timeval.tv_sec || diff_timeval.tv_sec>0 || diff_timeval.tv_usec>=PROGRESS_UPDATE_USEC) {
+                       if (want_progress_update(&ProgressEntry_updated_timeval)) {
                                gtk_entry_set_text(ProgressEntry,
                                                uri_text+(strncmp(uri_text,"file://",strlen("file://")) ? 0 : strlen("file://")));
-                               ProgressEntry_updated_timeval=now_timeval;
+                               want_gdk_flush=TRUE;
                                }
                        }
                }
-       while (gtk_events_pending())
-               gtk_main_iteration();
+
+#ifndef UI_GNOME_THREADS
+       while (g_main_context_pending(NULL))
+               g_main_context_iteration(
+                               NULL,   /* context */
+                               FALSE); /* may_block */
+#endif /* UI_GNOME_THREADS */
+
+       if (want_gdk_flush)
+               gdk_flush();
+       gdk_threads_leave();
+
+       /* Do not: g_thread_yield();
+        * as it is TOO much expensive and we are multithreaded anyway.
+        */
 
        if (aborted)
                return TRUE;
@@ -244,9 +300,45 @@ struct timeval diff_timeval;
        return FALSE;
 }
 
+static void ui_gnome_progress_bar(gint done,gint length)
+{
+       g_return_if_fail(done>=0);
+       g_return_if_fail(length>=0);
+
+       if (!want_progress_update(&ProgressBar_updated_timeval))
+               return;
+
+       gdk_threads_enter();
+
+       if (!length) {
+               gtk_progress_bar_pulse(MicrosoftComProgress);
+               gtk_progress_bar_set_text(MicrosoftComProgress,"");
+               }
+       else {
+gchar *length_display;
+
+               /* Do not format 'done' by gnome_vfs_format_file_size_for_display()
+                * as the progress would not be visible for large 'done' sizes.
+                */
+               gtk_progress_bar_set_fraction(MicrosoftComProgress,((gdouble)done)/length);
+               length_display=gnome_vfs_format_file_size_for_display(length);
+               gtk_progress_bar_set_text(MicrosoftComProgress,
+                               captive_printf_alloca("%d B / %s",(int)done,length_display));
+               g_free(length_display);
+               }
+
+       gdk_flush();
+       gdk_threads_leave();
+}
+
 /* FIXME: Change it to "prepare" signal. */
 void on_Page_map(GtkWidget *vbox_widget,GtkWidget *page_widget)
 {
+       /* Handle non-object (NULL) signal with reversed parameters? */
+       if (GNOME_IS_DRUID_PAGE(vbox_widget) && page_widget==NULL) {
+               page_widget=vbox_widget;
+               vbox_widget=NULL;
+               }
        g_return_if_fail(vbox_widget==NULL || GTK_IS_VBOX(vbox_widget));
        g_return_if_fail(GNOME_IS_DRUID_PAGE(page_widget));
 
@@ -302,6 +394,74 @@ gchar *text;
        gtk_widget_thaw_child_notify(vbox_widget);
 }
 
+typedef void (*process_t)(void);
+
+#ifdef UI_GNOME_THREADS
+/* 'GThreadFunc' type. */
+gpointer execute_process_func(process_t process /* data */)
+{
+       (*process)();
+
+       gdk_threads_enter();
+
+       gtk_main_quit();        /* Abort gtk_main() of execute_process(). */
+
+       gdk_flush();
+       gdk_threads_leave();
+
+       return NULL;
+}
+#endif /* UI_GNOME_THREADS */
+
+/* We are called inside gdk_threads_enter(). */
+static void execute_process(process_t process)
+{
+#ifdef UI_GNOME_THREADS
+GThread *gthread;
+#endif /* UI_GNOME_THREADS */
+
+       progress_start();
+#ifdef UI_GNOME_THREADS
+       gthread=g_thread_create_full(
+                       (GThreadFunc)execute_process_func,      /* func */
+                       process,        /* data */
+                       0,      /* stack_size; 0 means the default size */
+                       TRUE,   /* joinable */
+                       TRUE,   /* bound; use system thread */
+                       G_THREAD_PRIORITY_LOW,  /* priority; G_THREAD_PRIORITY_LOW is the lowest one */
+                       NULL);  /* error */
+       gtk_main();     /* We are already called inside gdk_threads_enter(). */
+       /* I hope some other gtk_main_quit() did not occur as we would
+        * locked if the 'process' func did not finish yet.
+        */
+       g_thread_join(gthread);
+#else /* UI_GNOME_THREADS */
+       (*process)();
+#endif /* UI_GNOME_THREADS */
+       progress_end();
+}
+
+/* 'process_t' typed. */
+static void process_scan_disk(void)
+{
+       scan_disks_quick();
+       scan_disks();
+}
+
+static GnomeVFSURI *process_scan_path_scan_path_uri;
+
+/* 'process_t' typed. */
+static void process_scan_path(void)
+{
+       mod_uri_load_base_reporting(process_scan_path_scan_path_uri);
+}
+
+/* 'process_t' typed. */
+static void process_microsoft_com(void)
+{
+       microsoft_com();
+}
+
 gboolean on_Page_next(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer user_data /* unused */)
 {
        g_return_val_if_fail(GNOME_IS_DRUID_PAGE(gnomedruidpage),FALSE);
@@ -316,10 +476,7 @@ gboolean on_Page_next(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer
                        }
                }
        else if (page_active==ScanDiskPage) {
-               progress_start();
-               scan_disks_quick();
-               scan_disks();
-               progress_end();
+               execute_process(process_scan_disk);
                if (aborted_back) {
                        gnome_druid_set_page(Druid,PageStart);
                        return TRUE;    /* ignore button press */
@@ -337,9 +494,8 @@ const gchar *scan_path_uri_text=gtk_entry_get_text(ScanPathLocationComboEntry);
 GnomeVFSURI *scan_path_uri;
 
                        if ((scan_path_uri=gnome_vfs_uri_new(scan_path_uri_text))) {
-                               progress_start();
-                               mod_uri_load(scan_path_uri);
-                               progress_end();
+                               process_scan_path_scan_path_uri=scan_path_uri;
+                               execute_process(process_scan_path);
                                gnome_vfs_uri_unref(scan_path_uri);
                                if (aborted_back) {
                                        gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanDiskPage));
@@ -371,9 +527,7 @@ void on_MicrosoftComConfirmButton_clicked(GtkButton *button,gpointer user_data)
        if (page_active!=MicrosoftComPage)      /* bogus callback */
                return;
 
-       progress_start();
-       microsoft_com();
-       progress_end();
+       execute_process(process_microsoft_com);
        if (aborted_back) {
                gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanPathPage));
                return;
@@ -453,12 +607,15 @@ gint reply;
        /* TODO: Avoid dialog if already on Finish page. */
        reply=-1;
        dialog=gnome_app_ok_cancel_modal(App,_(
-                       "Although essential modules (\"ntoskrnl.exe\" and \"ntfs.sys\") are available"
-                       "you may still want to get their better version and/or more modules."
+                       "Although essential modules (\"ntoskrnl.exe\" and \"ntfs.sys\") are available "
+                       "you may still want to get their better version and/or more modules. "
                        "Really quit?"),
                        (GnomeReplyCallback)on_DruidButtonOK_clicked_dialog_callback,
                        &reply);        /* data */
        g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
+       /* Never call gtk_main() from other thread than the initial one.
+        * We would have to switch GTK+ context (g_main_context()?).
+        */
        gtk_main();
        /* 'dialog' gets destroyed automatically */
        if (reply==0)   /* 0 for 'OK', 1 for 'Cancel', left -1 for dialog close. */
@@ -478,6 +635,18 @@ static void button_stock_set_label(GtkWidget *widget,const gchar *label_text_new
                gtk_label_set_text_with_mnemonic(GTK_LABEL(widget),label_text_new);
 }
 
+static void PageFinish_set_label_attr(GtkWidget *widget,gpointer callback_data /* unused */)
+{
+       g_return_if_fail(GTK_IS_WIDGET(widget));
+
+       /**/ if (GTK_IS_CONTAINER(widget))
+               gtk_container_foreach(GTK_CONTAINER(widget),
+                               (GtkCallback)PageFinish_set_label_attr, /* callback */
+                               callback_data); /* callback_data; unused */
+       else if (GTK_IS_LABEL(widget) && gtk_label_get_line_wrap(GTK_LABEL(widget)))
+               gtk_label_set_selectable(GTK_LABEL(widget),TRUE);
+}
+
 /* of "ui-gnome-interface.h": */
 GtkWidget *create_App(void);
 /* of "ui-gnome-support.h": */
@@ -489,6 +658,8 @@ GtkTreeViewColumn *column;
 GtkCellRenderer *cell;
 GtkBox *druid_button_box;
 
+       gdk_threads_enter();
+
        App=GNOME_APP(create_App());
 
        DriversTreeView=GTK_TREE_VIEW(lookup_widget(GTK_WIDGET(App),"DriversTreeView"));
@@ -502,6 +673,7 @@ GtkBox *druid_button_box;
        PageFinish=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"PageFinish"));
        ScanPathLocationComboEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ScanPathLocationComboEntry"));
        MicrosoftComConfirmButton=GTK_BUTTON(lookup_widget(GTK_WIDGET(App),"MicrosoftComConfirmButton"));
+       MicrosoftComProgress=GTK_PROGRESS_BAR(lookup_widget(GTK_WIDGET(App),"MicrosoftComProgress"));
        ProgressEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ProgressEntry"));
 
        druid_button_box=GTK_BOX(gtk_widget_get_parent(Druid->next));
@@ -533,9 +705,9 @@ GtkBox *druid_button_box;
        gtk_widget_hide(GTK_WIDGET(Druid->finish));
 
        DruidButtonSkip=GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_REDO));
-       gtk_container_foreach(GTK_CONTAINER(DruidButtonSkip),
-                       (GtkCallback)button_stock_set_label,    /* callback */
-                       _("_Skip"));    /* callback_data */
+       button_stock_set_label(
+                       GTK_WIDGET(DruidButtonSkip),    /* widget */
+                       _("_Skip"));    /* label_text_new */
        gtk_box_pack_end(druid_button_box,GTK_WIDGET(DruidButtonSkip),FALSE,TRUE,0);
        gtk_widget_show(GTK_WIDGET(DruidButtonSkip));
        g_signal_connect((gpointer)DruidButtonSkip,"clicked",G_CALLBACK(on_DruidButtonSkip_clicked),NULL);
@@ -545,7 +717,13 @@ GtkBox *druid_button_box;
        gtk_widget_show(GTK_WIDGET(DruidButtonOK));
        g_signal_connect((gpointer)DruidButtonOK,"clicked",G_CALLBACK(on_DruidButtonOK_clicked),NULL);
 
+       PageFinish_set_label_attr(
+                       GTK_WIDGET(PageFinish), /* widget */
+                       NULL);  /* callback_data; unused */
+
        state_changed();
+
+       gdk_threads_leave();
 }
 
 static void ui_gnome_g_log_handler(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
@@ -556,6 +734,8 @@ GtkWidget *dialog;
        if (in_progress && aborted)
                return;
 
+       gdk_threads_enter();
+
        /**/ if (log_level & G_LOG_LEVEL_ERROR)
                dialog=gnome_app_error(App,message);
        else if (log_level & (G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING))
@@ -567,12 +747,33 @@ GtkWidget *dialog;
        g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
        gtk_main();
        /* 'dialog' gets destroyed automatically */
+
+       gdk_flush();
+       gdk_threads_leave();
 }
 
 static void ui_gnome_interactive(void)
 {
+       gdk_threads_enter();
+
+       /* Postpone gtk_widget_show_all() from App_init() here
+        * to have already passed all ui_gnome_module_available_notify().
+        */
+       gnome_druid_set_page(Druid,MicrosoftComPage);
        gtk_widget_show_all(GTK_WIDGET(App));
+#if 0
+       /* gnome_druid_set_page(Druid,PageStart); */
+       gnome_druid_set_page(Druid,ScanDiskPage);
+       gnome_druid_set_page(Druid,ScanPathPage);
+       /* gnome_druid_set_page(Druid,MicrosoftComPage); */
+       gnome_druid_set_page(Druid,PageFinish);
+#endif
+       gnome_druid_set_page(Druid,PageStart);
+
        gtk_main();
+
+       gdk_threads_leave();
+
        exit(EXIT_SUCCESS);
 }
 
@@ -581,9 +782,20 @@ gboolean ui_gnome_init(void)
        acquire_module_available_notify=ui_gnome_module_available_notify;
        acquire_module_all_modules_found_notify=ui_gnome_all_modules_found_notify;
        ui_progress=ui_gnome_progress;
+       ui_progress_bar=ui_gnome_progress_bar;
        ui_interactive=ui_gnome_interactive;
        captivemodid_module_best_priority_notify=ui_gnome_module_best_priority_notify;
 
+#ifdef UI_GNOME_THREADS
+       /* gdk_threads_init() must be called before gtk_init()!
+        * gtk_init() gets called by create_App() here.
+        */
+       if (!g_thread_supported())
+               g_thread_init(NULL);
+       if (!gdk_threads_mutex)
+               gdk_threads_init();
+#endif /* UI_GNOME_THREADS */
+
        /* Graphic widgets will all be hidden yet. */
        App_init();
        /* ui_gnome_g_log_handler() needs 'App'. */