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