Initial original import from: fuse-2.4.2-2.fc4
[captive.git] / src / install / acquire / ui-gnome.c
1 /* $Id$
2  * Drivers acquiring installation utility
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "ui-gnome.h"   /* self */
23 #include <glib/gmessages.h>
24 #include "moduriload.h"
25 #include "main.h"
26 #include <gtk/gtkmain.h>
27 #include <gtk/gtktreeviewcolumn.h>
28 #include <gtk/gtkbox.h>
29 #include <sys/time.h>
30 #include <libgnomeui/gnome-app.h>
31 #include <gtk/gtktreestore.h>
32 #include <gtk/gtkframe.h>
33 #include <gtk/gtkentry.h>
34 #include <libgnomeui/gnome-druid.h>
35 #include <libgnomeui/gnome-app-util.h>
36 #include <libgnomeui/gnome-druid-page-edge.h>
37 #include "final.h"
38 #include <libgnomevfs/gnome-vfs-utils.h>
39
40 #include <captive/macros.h>
41
42
43 /* Config: */
44 #define PROGRESS_UPDATE_USEC 200000
45 /* Although proper GTK+ locking is provided below there are some
46  * bugs with compatibility of GTK+/Gnome-VFS/GConf.
47  * The main thread executes gtk_main()->g_main_loop_run()
48  * while the working thread initializes Gnome-VFS by GConf and
49  * executes also g_main_loop_run() while sharing some poll() fds.
50  */
51 /* #define UI_GNOME_THREADS 1 */
52
53
54 static GnomeApp *App;
55 static GtkTreeStore *DriversTreeStore;
56 static GtkFrame *DriversFrame;
57 static GtkFrame *ProgressFrame;
58 static GtkEntry *ProgressEntry;
59 static GnomeDruid *Druid;
60 static GtkButton *DruidButtonSkip;
61 static GtkButton *DruidButtonOK;
62 static GtkTreeView *DriversTreeView;
63 static GnomeDruidPage *PageStart;
64 static GnomeDruidPage *ScanDiskPage;
65 static GnomeDruidPage *ScanPathPage;
66 static GnomeDruidPage *MicrosoftComPage;
67 static GnomeDruidPage *PageFinish;
68 static GtkEntry *ScanPathLocationComboEntry;
69 static GtkButton *MicrosoftComConfirmButton;
70 static GtkProgressBar *MicrosoftComProgress;
71 enum {
72                 DRIVERS_TREE_STORE_COLUMN_TYPE,
73                 DRIVERS_TREE_STORE_COLUMN_ID,
74                 DRIVERS_TREE_STORE_COLUMN_NUM,  /* total # */
75                 };
76 #define DRIVERS_TREE_STORE_COLUMN_TYPE_LIST G_TYPE_STRING,G_TYPE_STRING
77
78
79 /* map: (gchar *)type-> (GtkTreeIter *) */
80 static GHashTable *DriversTreeStore_Iter_hash;
81
82 static void DriversTreeStore_Iter_hash_key_destroy_func(gchar *type)
83 {
84         g_return_if_fail(type!=NULL);
85
86         g_free(type);
87 }
88
89 static void DriversTreeStore_Iter_hash_init(void)
90 {
91         if (DriversTreeStore_Iter_hash)
92                 return;
93         DriversTreeStore_Iter_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
94                         (GDestroyNotify)DriversTreeStore_Iter_hash_key_destroy_func,
95                         NULL);  /* value_destroy_func */
96 }
97
98 static GtkTreeIter *DriversTreeStore_Iter_hash_get_iter(const gchar *type)
99 {
100 GtkTreeIter *r;
101
102         g_return_val_if_fail(type!=NULL,NULL);
103
104         DriversTreeStore_Iter_hash_init();
105         if (!(r=g_hash_table_lookup(DriversTreeStore_Iter_hash,type))) {
106                 captive_new(r);
107                 gtk_tree_store_append(DriversTreeStore,
108                                 r,      /* iter */
109                                 NULL);  /* parent */
110                 g_hash_table_insert(DriversTreeStore_Iter_hash,g_strdup(type),r);
111                 }
112
113         return r;
114 }
115
116 static gboolean some_modules_found=FALSE;
117 static gboolean in_progress=FALSE;
118 static GnomeDruidPage *page_active;
119
120 static void state_changed(void)
121 {
122         /* Not yet initialized? */
123         if (!App)
124                 return;
125
126         gtk_widget_set_sensitive(GTK_WIDGET(DruidButtonSkip),
127                         (page_active!=PageStart && page_active!=PageFinish));
128         gtk_widget_set_sensitive(GTK_WIDGET(DruidButtonOK),some_modules_found);
129
130         if (in_progress) {
131                 gtk_widget_set_sensitive(Druid->next,FALSE);
132                 gtk_widget_set_sensitive(GTK_WIDGET(DruidButtonOK),some_modules_found);
133                 gtk_widget_set_sensitive(GTK_WIDGET(MicrosoftComConfirmButton),FALSE);
134                 }
135         else {
136                 /* It is checked by GTK+ whether the text changed: */
137                 gtk_entry_set_text(ProgressEntry,"");
138                 gtk_widget_set_sensitive(Druid->next,
139                                 (page_active!=PageFinish && page_active!=MicrosoftComPage));
140                 gtk_widget_set_sensitive(GTK_WIDGET(MicrosoftComConfirmButton),
141                                 (page_active==MicrosoftComPage));
142                 gtk_progress_bar_set_fraction(MicrosoftComProgress,(gdouble)0);
143                 gtk_progress_bar_set_text(MicrosoftComProgress,"");
144                 }
145 }
146
147 static void ui_gnome_module_best_priority_notify(const gchar *module_type)
148 {
149 GtkTreeIter *iter;
150
151         g_return_if_fail(module_type!=NULL);
152
153         gdk_threads_enter();
154
155         iter=DriversTreeStore_Iter_hash_get_iter(module_type);
156         gtk_tree_store_set(DriversTreeStore,iter,
157                         DRIVERS_TREE_STORE_COLUMN_TYPE,module_type,
158                         DRIVERS_TREE_STORE_COLUMN_ID  ,
159                                         ((0
160                                                                         || !strcmp(module_type,"ntoskrnl.exe")
161                                                                         || !strcmp(module_type,"ntfs.sys"))
162                                                         ? _("NOT FOUND; essential module for NTFS disks access")
163                                                         : _("not found; optional module")),
164                         -1);
165
166         gdk_flush();
167         gdk_threads_leave();
168 }
169
170 static void ui_gnome_module_available_notify(struct module_available *module_available)
171 {
172 GtkTreeIter *iter;
173 static gboolean some_module_ntoskrnl_exe_found=FALSE;
174 static gboolean some_module_ntfs_sys_found=FALSE;
175
176         g_return_if_fail(module_available!=NULL);
177         g_return_if_fail(module_available->module!=NULL);
178
179         gdk_threads_enter();
180
181         iter=DriversTreeStore_Iter_hash_get_iter((const gchar *)module_available->module->type);
182         gtk_tree_store_set(DriversTreeStore,iter,
183                         DRIVERS_TREE_STORE_COLUMN_TYPE,module_available->module->type,
184                         DRIVERS_TREE_STORE_COLUMN_ID  ,module_available->module->id,
185                         -1);
186
187         if (!strcmp((const char *)module_available->module->type,"ntoskrnl.exe"))
188                 some_module_ntoskrnl_exe_found=TRUE;
189         if (!strcmp((const char *)module_available->module->type,"ntfs.sys"))
190                 some_module_ntfs_sys_found=TRUE;
191
192         some_modules_found=some_module_ntoskrnl_exe_found && some_module_ntfs_sys_found;
193         state_changed();
194
195         gdk_flush();
196         gdk_threads_leave();
197 }
198
199 static gboolean all_modules_found=FALSE;
200
201 static void ui_gnome_all_modules_found_notify(void)
202 {
203         gdk_threads_enter();
204
205         all_modules_found=TRUE;
206         state_changed();
207
208         gdk_flush();
209         gdk_threads_leave();
210 }
211
212 static gboolean aborted=FALSE;
213 static gboolean aborted_back=FALSE;     /* 'Back' button was clicked. */
214 static struct timeval ProgressEntry_updated_timeval;
215 static struct timeval ProgressBar_updated_timeval;
216
217 static void progress_start(void)
218 {
219         in_progress=TRUE;
220         aborted=FALSE;
221         aborted_back=FALSE;
222         CAPTIVE_MEMZERO(&ProgressEntry_updated_timeval);
223         CAPTIVE_MEMZERO(&ProgressBar_updated_timeval);
224         state_changed();
225 }
226
227 static void progress_end(void)
228 {
229         in_progress=FALSE;
230         state_changed();
231 }
232
233 static gboolean want_progress_update(struct timeval *timeval)
234 {
235 struct timeval now_timeval;
236 struct timeval diff_timeval;
237
238         g_return_val_if_fail(timeval!=NULL,FALSE);
239
240         gettimeofday(   /* FIXME: errors ignored */
241                         &now_timeval,   /* tv */
242                         NULL);  /* tz */
243         timersub(&now_timeval,timeval,&diff_timeval);
244         if (!timeval->tv_sec || diff_timeval.tv_sec>0 || diff_timeval.tv_usec>=PROGRESS_UPDATE_USEC) {
245                 *timeval=now_timeval;
246                 return TRUE;
247                 }
248         return FALSE;
249 }
250
251 static gboolean ui_gnome_progress(GnomeVFSURI *uri)
252 {
253 gboolean want_gdk_flush=FALSE;
254
255         /* 'uri' may be NULL */
256
257         gdk_threads_enter();
258
259         if (ProgressEntry) {
260 static gchar *uri_text=NULL;
261
262                 /* Store 'uri' on each call (not just if 'diff_timeval' permits)
263                  * as we may get into long cabinet extraction phase with 'uri==NULL' calls
264                  * where we want to display the currently processed 'uri'.
265                  */
266                 if (uri) {
267                         g_free(uri_text);
268                         uri_text=gnome_vfs_uri_to_string(uri,GNOME_VFS_URI_HIDE_PASSWORD);
269                         }
270
271                 if (uri_text) {
272                         if (want_progress_update(&ProgressEntry_updated_timeval)) {
273                                 gtk_entry_set_text(ProgressEntry,
274                                                 uri_text+(strncmp(uri_text,"file://",strlen("file://")) ? 0 : strlen("file://")));
275                                 want_gdk_flush=TRUE;
276                                 }
277                         }
278                 }
279
280 #ifndef UI_GNOME_THREADS
281         while (g_main_context_pending(NULL))
282                 g_main_context_iteration(
283                                 NULL,   /* context */
284                                 FALSE); /* may_block */
285 #endif /* UI_GNOME_THREADS */
286
287         if (want_gdk_flush)
288                 gdk_flush();
289         gdk_threads_leave();
290
291         /* Do not: g_thread_yield();
292          * as it is TOO much expensive and we are multithreaded anyway.
293          */
294
295         if (aborted)
296                 return TRUE;
297         if (all_modules_found)
298                 return TRUE;
299
300         return FALSE;
301 }
302
303 static void ui_gnome_progress_bar(gint done,gint length)
304 {
305         g_return_if_fail(done>=0);
306         g_return_if_fail(length>=0);
307
308         if (!want_progress_update(&ProgressBar_updated_timeval))
309                 return;
310
311         gdk_threads_enter();
312
313         if (!length) {
314                 gtk_progress_bar_pulse(MicrosoftComProgress);
315                 gtk_progress_bar_set_text(MicrosoftComProgress,"");
316                 }
317         else {
318 gchar *length_display;
319
320                 /* Do not format 'done' by gnome_vfs_format_file_size_for_display()
321                  * as the progress would not be visible for large 'done' sizes.
322                  */
323                 gtk_progress_bar_set_fraction(MicrosoftComProgress,((gdouble)done)/length);
324                 length_display=gnome_vfs_format_file_size_for_display(length);
325                 gtk_progress_bar_set_text(MicrosoftComProgress,
326                                 captive_printf_alloca("%d B / %s",(int)done,length_display));
327                 g_free(length_display);
328                 }
329
330         gdk_flush();
331         gdk_threads_leave();
332 }
333
334 /* FIXME: Change it to "prepare" signal. */
335 void on_Page_map(GtkWidget *vbox_widget,GtkWidget *page_widget)
336 {
337         /* Handle non-object (NULL) signal with reversed parameters? */
338         if (GNOME_IS_DRUID_PAGE(vbox_widget) && page_widget==NULL) {
339                 page_widget=vbox_widget;
340                 vbox_widget=NULL;
341                 }
342         g_return_if_fail(vbox_widget==NULL || GTK_IS_VBOX(vbox_widget));
343         g_return_if_fail(GNOME_IS_DRUID_PAGE(page_widget));
344
345         page_active=GNOME_DRUID_PAGE(page_widget);
346         if (page_active==PageFinish) {
347                 gnome_druid_set_show_finish(Druid,FALSE);       /* set it each time */
348                 /**/ if (!some_modules_found)
349                         gnome_druid_page_edge_set_text(GNOME_DRUID_PAGE_EDGE(PageFinish),_(
350                                         "We need at least some version of drivers essential for this project:"
351                                         " ntoskrnl.exe and ntfs.sys. Please click 'Back' button to obtain them"
352                                         " by several methods offered by this installer."));
353                 else {
354 gchar *text;
355
356                         text=final_text(all_modules_found);
357                         gnome_druid_page_edge_set_text(GNOME_DRUID_PAGE_EDGE(PageFinish),text);
358                         g_free(text);
359                         }
360                 }
361         if (page_active==ScanPathPage)
362                 gtk_widget_grab_focus(GTK_WIDGET(ScanPathLocationComboEntry));
363         state_changed();
364
365         if (!vbox_widget)
366                 return;
367
368         /* FIXME: 'freeze' apparently does not help 'repositioning' of
369          * 'DriversTreeView' during first 'map' of each 'Page'.
370          */
371         gtk_widget_freeze_child_notify(vbox_widget);
372
373         gtk_widget_reparent(GTK_WIDGET(DriversFrame),vbox_widget);
374         gtk_widget_reparent(GTK_WIDGET(ProgressFrame),vbox_widget);
375
376         gtk_box_reorder_child(GTK_BOX(vbox_widget),GTK_WIDGET(DriversFrame),
377                         0);     /* position */
378
379         gtk_box_set_child_packing(GTK_BOX(vbox_widget),GTK_WIDGET(DriversFrame),
380                         FALSE,  /* expand */
381                         TRUE,   /* fill */
382                         0,      /* padding */
383                         GTK_PACK_START);
384         gtk_box_set_child_packing(GTK_BOX(vbox_widget),GTK_WIDGET(ProgressFrame),
385                         FALSE,  /* expand */
386                         TRUE,   /* fill */
387                         0,      /* padding */
388                         GTK_PACK_START);
389
390         /* FIXME: Needed to fix (0,0)-position inside parent GdkWindow. */
391         gtk_widget_queue_resize(GTK_WIDGET(DriversTreeView));
392         gtk_widget_queue_resize(GTK_WIDGET(ProgressEntry));
393
394         gtk_widget_thaw_child_notify(vbox_widget);
395 }
396
397 typedef void (*process_t)(void);
398
399 #ifdef UI_GNOME_THREADS
400 /* 'GThreadFunc' type. */
401 gpointer execute_process_func(process_t process /* data */)
402 {
403         (*process)();
404
405         gdk_threads_enter();
406
407         gtk_main_quit();        /* Abort gtk_main() of execute_process(). */
408
409         gdk_flush();
410         gdk_threads_leave();
411
412         return NULL;
413 }
414 #endif /* UI_GNOME_THREADS */
415
416 /* We are called inside gdk_threads_enter(). */
417 static void execute_process(process_t process)
418 {
419 #ifdef UI_GNOME_THREADS
420 GThread *gthread;
421 #endif /* UI_GNOME_THREADS */
422
423         progress_start();
424 #ifdef UI_GNOME_THREADS
425         gthread=g_thread_create_full(
426                         (GThreadFunc)execute_process_func,      /* func */
427                         process,        /* data */
428                         0,      /* stack_size; 0 means the default size */
429                         TRUE,   /* joinable */
430                         TRUE,   /* bound; use system thread */
431                         G_THREAD_PRIORITY_LOW,  /* priority; G_THREAD_PRIORITY_LOW is the lowest one */
432                         NULL);  /* error */
433         gtk_main();     /* We are already called inside gdk_threads_enter(). */
434         /* I hope some other gtk_main_quit() did not occur as we would
435          * locked if the 'process' func did not finish yet.
436          */
437         g_thread_join(gthread);
438 #else /* UI_GNOME_THREADS */
439         (*process)();
440 #endif /* UI_GNOME_THREADS */
441         progress_end();
442 }
443
444 /* 'process_t' typed. */
445 static void process_scan_disk(void)
446 {
447         scan_disks_quick();
448         scan_disks();
449 }
450
451 static GnomeVFSURI *process_scan_path_scan_path_uri;
452
453 /* 'process_t' typed. */
454 static void process_scan_path(void)
455 {
456         mod_uri_load_base_reporting(process_scan_path_scan_path_uri);
457 }
458
459 /* 'process_t' typed. */
460 static void process_microsoft_com(void)
461 {
462         microsoft_com();
463 }
464
465 gboolean on_Page_next(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer user_data /* unused */)
466 {
467         g_return_val_if_fail(GNOME_IS_DRUID_PAGE(gnomedruidpage),FALSE);
468
469         if (in_progress)        /* bogus callback - we should be non-sensitive */
470                 return TRUE;    /* ignore button press */
471
472         /**/ if (page_active==PageStart) {
473                 if (all_modules_found) {
474                         gnome_druid_set_page(Druid,PageFinish);
475                         return TRUE;    /* ignore button press */
476                         }
477                 }
478         else if (page_active==ScanDiskPage) {
479                 execute_process(process_scan_disk);
480                 if (aborted_back) {
481                         gnome_druid_set_page(Druid,PageStart);
482                         return TRUE;    /* ignore button press */
483                         }
484                 if (all_modules_found) {
485                         gnome_druid_set_page(Druid,PageFinish);
486                         return TRUE;    /* ignore button press */
487                         }
488                 return FALSE;   /* proceed to next page */
489                 }
490         else if (page_active==ScanPathPage) {
491 const gchar *scan_path_uri_text=gtk_entry_get_text(ScanPathLocationComboEntry);
492
493                 if (scan_path_uri_text && *scan_path_uri_text) {
494 GnomeVFSURI *scan_path_uri;
495
496                         if ((scan_path_uri=gnome_vfs_uri_new(scan_path_uri_text))) {
497                                 process_scan_path_scan_path_uri=scan_path_uri;
498                                 execute_process(process_scan_path);
499                                 gnome_vfs_uri_unref(scan_path_uri);
500                                 if (aborted_back) {
501                                         gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanDiskPage));
502                                         return TRUE;    /* ignore button press */
503                                         }
504                                 if (all_modules_found) {
505                                         gnome_druid_set_page(Druid,PageFinish);
506                                         return TRUE;    /* ignore button press */
507                                         }
508                                 gtk_entry_set_text(ScanPathLocationComboEntry,"");
509                                 }
510                         else
511                                 g_warning(_("Invalid URI: %s"),scan_path_uri_text);
512                         gtk_widget_grab_focus(GTK_WIDGET(ScanPathLocationComboEntry));
513                         return TRUE;    /* ignore button press; we cleared the URI entry */
514                         }
515                 return FALSE;   /* proceed to next page */
516                 }
517
518         return FALSE;   /* proceed to next page */
519 }
520
521 void on_MicrosoftComConfirmButton_clicked(GtkButton *button,gpointer user_data)
522 {
523         g_return_if_fail(GTK_IS_BUTTON(button));
524
525         if (in_progress)        /* bogus callback */
526                 return;
527         if (page_active!=MicrosoftComPage)      /* bogus callback */
528                 return;
529
530         execute_process(process_microsoft_com);
531         if (aborted_back) {
532                 gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanPathPage));
533                 return;
534                 }
535         if (all_modules_found) {
536                 gnome_druid_set_page(Druid,PageFinish);
537                 return;
538                 }
539
540         gnome_druid_set_page(Druid,PageFinish);
541 }
542
543 void on_DruidButtonSkip_clicked(GtkButton *button,gpointer user_data /* unused */)
544 {
545         g_return_if_fail(GTK_IS_BUTTON(button));
546
547         if (in_progress) {
548                 aborted=TRUE;
549                 state_changed();
550                 return;
551                 }
552         if (all_modules_found)
553                 gnome_druid_set_page(Druid,PageFinish);
554         else if (page_active==ScanDiskPage)
555                 gnome_druid_set_page(Druid,ScanPathPage);
556         else if (page_active==ScanPathPage)
557                 gnome_druid_set_page(Druid,MicrosoftComPage);
558         else if (page_active==MicrosoftComPage)
559                 gnome_druid_set_page(Druid,PageFinish);
560 }
561
562 gboolean on_Page_back(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer user_data)
563 {
564         g_return_val_if_fail(GNOME_IS_DRUID_PAGE(gnomedruidpage),FALSE);
565
566         if (!in_progress) {
567                 if (all_modules_found) {
568                         gnome_druid_set_page(Druid,PageStart);
569                         return TRUE;    /* ignore button press */
570                         }
571                 return FALSE;   /* proceed to previous page */
572                 }
573
574         aborted=TRUE;
575         aborted_back=TRUE;
576         state_changed();
577
578         return TRUE;    /* ignore button press now; we will respect 'aborted_back' */
579 }
580
581 void on_Druid_cancel(GnomeDruid *gnomedruid,gpointer user_data /* unused */)
582 {
583         g_return_if_fail(GNOME_IS_DRUID(gnomedruid));
584
585         /* gtk_main_quit() would not abort the current operation. */
586         exit(EXIT_SUCCESS);
587 }
588
589 static void on_DruidButtonOK_clicked_dialog_callback(gint reply,gint *replyp /* data */)
590 {
591         g_return_if_fail(reply>=0);
592         g_return_if_fail(replyp!=NULL);
593
594         *replyp=reply;
595 }
596
597 void on_DruidButtonOK_clicked(GtkButton *button,gpointer user_data /* unused */)
598 {
599 GtkWidget *dialog;
600 gint reply;
601
602         g_return_if_fail(GTK_IS_BUTTON(button));
603
604         if (all_modules_found)
605                 exit(EXIT_SUCCESS);
606
607         /* TODO: Avoid dialog if already on Finish page. */
608         reply=-1;
609         dialog=gnome_app_ok_cancel_modal(App,_(
610                         "Although essential modules (\"ntoskrnl.exe\" and \"ntfs.sys\") are available "
611                         "you may still want to get their better version and/or more modules. "
612                         "Really quit?"),
613                         (GnomeReplyCallback)on_DruidButtonOK_clicked_dialog_callback,
614                         &reply);        /* data */
615         g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
616         /* Never call gtk_main() from other thread than the initial one.
617          * We would have to switch GTK+ context (g_main_context()?).
618          */
619         gtk_main();
620         /* 'dialog' gets destroyed automatically */
621         if (reply==0)   /* 0 for 'OK', 1 for 'Cancel', left -1 for dialog close. */
622                 exit(EXIT_SUCCESS);
623 }
624
625 static void button_stock_set_label(GtkWidget *widget,const gchar *label_text_new /* callback_data */)
626 {
627         g_return_if_fail(GTK_IS_WIDGET(widget));
628         g_return_if_fail(label_text_new!=NULL);
629
630         /**/ if (GTK_IS_CONTAINER(widget))
631                 gtk_container_foreach(GTK_CONTAINER(widget),
632                                 (GtkCallback)button_stock_set_label,    /* callback */
633                                 (/* de-conts */ gchar *)label_text_new);        /* callback_data */
634         else if (GTK_IS_LABEL(widget))
635                 gtk_label_set_text_with_mnemonic(GTK_LABEL(widget),label_text_new);
636 }
637
638 static void PageFinish_set_label_attr(GtkWidget *widget,gpointer callback_data /* unused */)
639 {
640         g_return_if_fail(GTK_IS_WIDGET(widget));
641
642         /**/ if (GTK_IS_CONTAINER(widget))
643                 gtk_container_foreach(GTK_CONTAINER(widget),
644                                 (GtkCallback)PageFinish_set_label_attr, /* callback */
645                                 callback_data); /* callback_data; unused */
646         else if (GTK_IS_LABEL(widget) && gtk_label_get_line_wrap(GTK_LABEL(widget)))
647                 gtk_label_set_selectable(GTK_LABEL(widget),TRUE);
648 }
649
650 /* of "ui-gnome-interface.h": */
651 GtkWidget *create_App(void);
652 /* of "ui-gnome-support.h": */
653 GtkWidget *lookup_widget(GtkWidget *widget,const gchar *widget_name);
654
655 static void App_init(void)
656 {
657 GtkTreeViewColumn *column;
658 GtkCellRenderer *cell;
659 GtkBox *druid_button_box;
660
661         gdk_threads_enter();
662
663         App=GNOME_APP(create_App());
664
665         DriversTreeView=GTK_TREE_VIEW(lookup_widget(GTK_WIDGET(App),"DriversTreeView"));
666         DriversFrame=GTK_FRAME(lookup_widget(GTK_WIDGET(App),"DriversFrame"));
667         ProgressFrame=GTK_FRAME(lookup_widget(GTK_WIDGET(App),"ProgressFrame"));
668         Druid=GNOME_DRUID(lookup_widget(GTK_WIDGET(App),"Druid"));
669         PageStart=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"PageStart"));
670         ScanDiskPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"ScanDiskPage"));
671         ScanPathPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"ScanPathPage"));
672         MicrosoftComPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"MicrosoftComPage"));
673         PageFinish=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"PageFinish"));
674         ScanPathLocationComboEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ScanPathLocationComboEntry"));
675         MicrosoftComConfirmButton=GTK_BUTTON(lookup_widget(GTK_WIDGET(App),"MicrosoftComConfirmButton"));
676         MicrosoftComProgress=GTK_PROGRESS_BAR(lookup_widget(GTK_WIDGET(App),"MicrosoftComProgress"));
677         ProgressEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ProgressEntry"));
678
679         druid_button_box=GTK_BOX(gtk_widget_get_parent(Druid->next));
680
681         DriversTreeStore=gtk_tree_store_new(DRIVERS_TREE_STORE_COLUMN_NUM,DRIVERS_TREE_STORE_COLUMN_TYPE_LIST);
682         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(DriversTreeStore),
683                         DRIVERS_TREE_STORE_COLUMN_TYPE,GTK_SORT_ASCENDING);
684         gtk_tree_view_set_model(DriversTreeView,GTK_TREE_MODEL(DriversTreeStore));
685
686         column=gtk_tree_view_column_new();
687         cell=gtk_cell_renderer_text_new();
688         gtk_tree_view_column_pack_start(column,cell,
689                         TRUE);  /* expand */
690         gtk_tree_view_column_set_attributes(column,cell,
691                         "text",DRIVERS_TREE_STORE_COLUMN_TYPE,
692                         NULL);
693         gtk_tree_view_append_column(DriversTreeView,column);
694
695         column=gtk_tree_view_column_new();
696         cell=gtk_cell_renderer_text_new();
697         gtk_tree_view_column_pack_start(column,cell,
698                         TRUE);  /* expand */
699         gtk_tree_view_column_set_attributes(column,cell,
700                         "text",DRIVERS_TREE_STORE_COLUMN_ID,
701                         NULL);
702         gtk_tree_view_append_column(DriversTreeView,column);
703
704         /* gnome_druid_set_show_finish() just replaces Next<->Finish buttons displayed. */
705         gtk_widget_hide(GTK_WIDGET(Druid->finish));
706
707         DruidButtonSkip=GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_REDO));
708         button_stock_set_label(
709                         GTK_WIDGET(DruidButtonSkip),    /* widget */
710                         _("_Skip"));    /* label_text_new */
711         gtk_box_pack_end(druid_button_box,GTK_WIDGET(DruidButtonSkip),FALSE,TRUE,0);
712         gtk_widget_show(GTK_WIDGET(DruidButtonSkip));
713         g_signal_connect((gpointer)DruidButtonSkip,"clicked",G_CALLBACK(on_DruidButtonSkip_clicked),NULL);
714
715         DruidButtonOK=GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_OK));
716         gtk_box_pack_end(druid_button_box,GTK_WIDGET(DruidButtonOK),FALSE,TRUE,0);
717         gtk_widget_show(GTK_WIDGET(DruidButtonOK));
718         g_signal_connect((gpointer)DruidButtonOK,"clicked",G_CALLBACK(on_DruidButtonOK_clicked),NULL);
719
720         PageFinish_set_label_attr(
721                         GTK_WIDGET(PageFinish), /* widget */
722                         NULL);  /* callback_data; unused */
723
724         state_changed();
725
726         gdk_threads_leave();
727 }
728
729 static void ui_gnome_g_log_handler(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
730 {
731 GtkWidget *dialog;
732
733         /* Ignore arrors by cabextract during its abortion. */
734         if (in_progress && aborted)
735                 return;
736
737         gdk_threads_enter();
738
739         /**/ if (log_level & G_LOG_LEVEL_ERROR)
740                 dialog=gnome_app_error(App,message);
741         else if (log_level & (G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING))
742                 dialog=gnome_app_warning(App,message);
743         else
744                 dialog=gnome_app_message(App,message);
745
746         gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
747         g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
748         gtk_main();
749         /* 'dialog' gets destroyed automatically */
750
751         gdk_flush();
752         gdk_threads_leave();
753 }
754
755 static void ui_gnome_interactive(void)
756 {
757         gdk_threads_enter();
758
759         /* Postpone gtk_widget_show_all() from App_init() here
760          * to have already passed all ui_gnome_module_available_notify().
761          */
762         gnome_druid_set_page(Druid,MicrosoftComPage);
763         gtk_widget_show_all(GTK_WIDGET(App));
764 #if 0
765         /* gnome_druid_set_page(Druid,PageStart); */
766         gnome_druid_set_page(Druid,ScanDiskPage);
767         gnome_druid_set_page(Druid,ScanPathPage);
768         /* gnome_druid_set_page(Druid,MicrosoftComPage); */
769         gnome_druid_set_page(Druid,PageFinish);
770 #endif
771         gnome_druid_set_page(Druid,PageStart);
772
773         gtk_main();
774
775         gdk_threads_leave();
776
777         exit(EXIT_SUCCESS);
778 }
779
780 gboolean ui_gnome_init(void)
781 {
782         acquire_module_available_notify=ui_gnome_module_available_notify;
783         acquire_module_all_modules_found_notify=ui_gnome_all_modules_found_notify;
784         ui_progress=ui_gnome_progress;
785         ui_progress_bar=ui_gnome_progress_bar;
786         ui_interactive=ui_gnome_interactive;
787         captivemodid_module_best_priority_notify=ui_gnome_module_best_priority_notify;
788
789 #ifdef UI_GNOME_THREADS
790         /* gdk_threads_init() must be called before gtk_init()!
791          * gtk_init() gets called by create_App() here.
792          */
793         if (!g_thread_supported())
794                 g_thread_init(NULL);
795         if (!gdk_threads_mutex)
796                 gdk_threads_init();
797 #endif /* UI_GNOME_THREADS */
798
799         /* Graphic widgets will all be hidden yet. */
800         App_init();
801         /* ui_gnome_g_log_handler() needs 'App'. */
802         g_log_set_handler(
803                         G_LOG_DOMAIN,   /* log_domain; "Captive" */
804                         G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL,    /* log_levels */
805                         ui_gnome_g_log_handler, /* log_func */
806                         NULL);  /* user_data */
807
808         return TRUE;
809 }