+mod_uri_load_base_reporting(): Complain on errors loading base URLs.
[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(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(module_available->module->type,"ntoskrnl.exe"))
188                 some_module_ntoskrnl_exe_found=TRUE;
189         if (!strcmp(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         /* 'uri' may be NULL */
254
255         gdk_threads_enter();
256
257         if (ProgressEntry) {
258 static gchar *uri_text=NULL;
259
260                 /* Store 'uri' on each call (not just if 'diff_timeval' permits)
261                  * as we may get into long cabinet extraction phase with 'uri==NULL' calls
262                  * where we want to display the currently processed 'uri'.
263                  */
264                 if (uri) {
265                         g_free(uri_text);
266                         uri_text=gnome_vfs_uri_to_string(uri,GNOME_VFS_URI_HIDE_PASSWORD);
267                         }
268
269                 if (uri_text) {
270                         if (want_progress_update(&ProgressEntry_updated_timeval))
271                                 gtk_entry_set_text(ProgressEntry,
272                                                 uri_text+(strncmp(uri_text,"file://",strlen("file://")) ? 0 : strlen("file://")));
273                         }
274                 }
275
276 #ifndef UI_GNOME_THREADS
277         while (g_main_context_pending(NULL))
278                 g_main_context_iteration(
279                                 NULL,   /* context */
280                                 FALSE); /* may_block */
281 #endif /* UI_GNOME_THREADS */
282
283         gdk_flush();
284         gdk_threads_leave();
285
286         /* Do not: g_thread_yield();
287          * as it is TOO much expensive and we are multithreaded anyway.
288          */
289
290         if (aborted)
291                 return TRUE;
292         if (all_modules_found)
293                 return TRUE;
294
295         return FALSE;
296 }
297
298 static void ui_gnome_progress_bar(gint done,gint length)
299 {
300         g_return_if_fail(done>=0);
301         g_return_if_fail(length>=0);
302
303         if (!want_progress_update(&ProgressBar_updated_timeval))
304                 return;
305
306         gdk_threads_enter();
307
308         if (!length) {
309                 gtk_progress_bar_pulse(MicrosoftComProgress);
310                 gtk_progress_bar_set_text(MicrosoftComProgress,"");
311                 }
312         else {
313 gchar *length_display;
314
315                 /* Do not format 'done' by gnome_vfs_format_file_size_for_display()
316                  * as the progress would not be visible for large 'done' sizes.
317                  */
318                 gtk_progress_bar_set_fraction(MicrosoftComProgress,((gdouble)done)/length);
319                 length_display=gnome_vfs_format_file_size_for_display(length);
320                 gtk_progress_bar_set_text(MicrosoftComProgress,
321                                 captive_printf_alloca("%d B / %s",(int)done,length_display));
322                 g_free(length_display);
323                 }
324
325         gdk_flush();
326         gdk_threads_leave();
327 }
328
329 /* FIXME: Change it to "prepare" signal. */
330 void on_Page_map(GtkWidget *vbox_widget,GtkWidget *page_widget)
331 {
332         /* Handle non-object (NULL) signal with reversed parameters? */
333         if (GNOME_IS_DRUID_PAGE(vbox_widget) && page_widget==NULL) {
334                 page_widget=vbox_widget;
335                 vbox_widget=NULL;
336                 }
337         g_return_if_fail(vbox_widget==NULL || GTK_IS_VBOX(vbox_widget));
338         g_return_if_fail(GNOME_IS_DRUID_PAGE(page_widget));
339
340         page_active=GNOME_DRUID_PAGE(page_widget);
341         if (page_active==PageFinish) {
342                 gnome_druid_set_show_finish(Druid,FALSE);       /* set it each time */
343                 /**/ if (!some_modules_found)
344                         gnome_druid_page_edge_set_text(GNOME_DRUID_PAGE_EDGE(PageFinish),_(
345                                         "We need at least some version of drivers essential for this project:"
346                                         " ntoskrnl.exe and ntfs.sys. Please click 'Back' button to obtain them"
347                                         " by several methods offered by this installer."));
348                 else {
349 gchar *text;
350
351                         text=final_text(all_modules_found);
352                         gnome_druid_page_edge_set_text(GNOME_DRUID_PAGE_EDGE(PageFinish),text);
353                         g_free(text);
354                         }
355                 }
356         if (page_active==ScanPathPage)
357                 gtk_widget_grab_focus(GTK_WIDGET(ScanPathLocationComboEntry));
358         state_changed();
359
360         if (!vbox_widget)
361                 return;
362
363         /* FIXME: 'freeze' apparently does not help 'repositioning' of
364          * 'DriversTreeView' during first 'map' of each 'Page'.
365          */
366         gtk_widget_freeze_child_notify(vbox_widget);
367
368         gtk_widget_reparent(GTK_WIDGET(DriversFrame),vbox_widget);
369         gtk_widget_reparent(GTK_WIDGET(ProgressFrame),vbox_widget);
370
371         gtk_box_reorder_child(GTK_BOX(vbox_widget),GTK_WIDGET(DriversFrame),
372                         0);     /* position */
373
374         gtk_box_set_child_packing(GTK_BOX(vbox_widget),GTK_WIDGET(DriversFrame),
375                         FALSE,  /* expand */
376                         TRUE,   /* fill */
377                         0,      /* padding */
378                         GTK_PACK_START);
379         gtk_box_set_child_packing(GTK_BOX(vbox_widget),GTK_WIDGET(ProgressFrame),
380                         FALSE,  /* expand */
381                         TRUE,   /* fill */
382                         0,      /* padding */
383                         GTK_PACK_START);
384
385         /* FIXME: Needed to fix (0,0)-position inside parent GdkWindow. */
386         gtk_widget_queue_resize(GTK_WIDGET(DriversTreeView));
387         gtk_widget_queue_resize(GTK_WIDGET(ProgressEntry));
388
389         gtk_widget_thaw_child_notify(vbox_widget);
390 }
391
392 typedef void (*process_t)(void);
393
394 #ifdef UI_GNOME_THREADS
395 /* 'GThreadFunc' type. */
396 gpointer execute_process_func(process_t process /* data */)
397 {
398         (*process)();
399
400         gdk_threads_enter();
401
402         gtk_main_quit();        /* Abort gtk_main() of execute_process(). */
403
404         gdk_flush();
405         gdk_threads_leave();
406
407         return NULL;
408 }
409 #endif /* UI_GNOME_THREADS */
410
411 /* We are called inside gdk_threads_enter(). */
412 static void execute_process(process_t process)
413 {
414 #ifdef UI_GNOME_THREADS
415 GThread *gthread;
416 #endif /* UI_GNOME_THREADS */
417
418         progress_start();
419 #ifdef UI_GNOME_THREADS
420         gthread=g_thread_create_full(
421                         (GThreadFunc)execute_process_func,      /* func */
422                         process,        /* data */
423                         0,      /* stack_size; 0 means the default size */
424                         TRUE,   /* joinable */
425                         TRUE,   /* bound; use system thread */
426                         G_THREAD_PRIORITY_LOW,  /* priority; G_THREAD_PRIORITY_LOW is the lowest one */
427                         NULL);  /* error */
428         gtk_main();     /* We are already called inside gdk_threads_enter(). */
429         /* I hope some other gtk_main_quit() did not occur as we would
430          * locked if the 'process' func did not finish yet.
431          */
432         g_thread_join(gthread);
433 #else /* UI_GNOME_THREADS */
434         (*process)();
435 #endif /* UI_GNOME_THREADS */
436         progress_end();
437 }
438
439 /* 'process_t' typed. */
440 static void process_scan_disk(void)
441 {
442         scan_disks_quick();
443         scan_disks();
444 }
445
446 static GnomeVFSURI *process_scan_path_scan_path_uri;
447
448 /* 'process_t' typed. */
449 static void process_scan_path(void)
450 {
451         mod_uri_load_base_reporting(process_scan_path_scan_path_uri);
452 }
453
454 /* 'process_t' typed. */
455 static void process_microsoft_com(void)
456 {
457         microsoft_com();
458 }
459
460 gboolean on_Page_next(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer user_data /* unused */)
461 {
462         g_return_val_if_fail(GNOME_IS_DRUID_PAGE(gnomedruidpage),FALSE);
463
464         if (in_progress)        /* bogus callback - we should be non-sensitive */
465                 return TRUE;    /* ignore button press */
466
467         /**/ if (page_active==PageStart) {
468                 if (all_modules_found) {
469                         gnome_druid_set_page(Druid,PageFinish);
470                         return TRUE;    /* ignore button press */
471                         }
472                 }
473         else if (page_active==ScanDiskPage) {
474                 execute_process(process_scan_disk);
475                 if (aborted_back) {
476                         gnome_druid_set_page(Druid,PageStart);
477                         return TRUE;    /* ignore button press */
478                         }
479                 if (all_modules_found) {
480                         gnome_druid_set_page(Druid,PageFinish);
481                         return TRUE;    /* ignore button press */
482                         }
483                 return FALSE;   /* proceed to next page */
484                 }
485         else if (page_active==ScanPathPage) {
486 const gchar *scan_path_uri_text=gtk_entry_get_text(ScanPathLocationComboEntry);
487
488                 if (scan_path_uri_text && *scan_path_uri_text) {
489 GnomeVFSURI *scan_path_uri;
490
491                         if ((scan_path_uri=gnome_vfs_uri_new(scan_path_uri_text))) {
492                                 process_scan_path_scan_path_uri=scan_path_uri;
493                                 execute_process(process_scan_path);
494                                 gnome_vfs_uri_unref(scan_path_uri);
495                                 if (aborted_back) {
496                                         gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanDiskPage));
497                                         return TRUE;    /* ignore button press */
498                                         }
499                                 if (all_modules_found) {
500                                         gnome_druid_set_page(Druid,PageFinish);
501                                         return TRUE;    /* ignore button press */
502                                         }
503                                 gtk_entry_set_text(ScanPathLocationComboEntry,"");
504                                 }
505                         else
506                                 g_warning(_("Invalid URI: %s"),scan_path_uri_text);
507                         gtk_widget_grab_focus(GTK_WIDGET(ScanPathLocationComboEntry));
508                         return TRUE;    /* ignore button press; we cleared the URI entry */
509                         }
510                 return FALSE;   /* proceed to next page */
511                 }
512
513         return FALSE;   /* proceed to next page */
514 }
515
516 void on_MicrosoftComConfirmButton_clicked(GtkButton *button,gpointer user_data)
517 {
518         g_return_if_fail(GTK_IS_BUTTON(button));
519
520         if (in_progress)        /* bogus callback */
521                 return;
522         if (page_active!=MicrosoftComPage)      /* bogus callback */
523                 return;
524
525         execute_process(process_microsoft_com);
526         if (aborted_back) {
527                 gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanPathPage));
528                 return;
529                 }
530         if (all_modules_found) {
531                 gnome_druid_set_page(Druid,PageFinish);
532                 return;
533                 }
534
535         gnome_druid_set_page(Druid,PageFinish);
536 }
537
538 void on_DruidButtonSkip_clicked(GtkButton *button,gpointer user_data /* unused */)
539 {
540         g_return_if_fail(GTK_IS_BUTTON(button));
541
542         if (in_progress) {
543                 aborted=TRUE;
544                 state_changed();
545                 return;
546                 }
547         if (all_modules_found)
548                 gnome_druid_set_page(Druid,PageFinish);
549         else if (page_active==ScanDiskPage)
550                 gnome_druid_set_page(Druid,ScanPathPage);
551         else if (page_active==ScanPathPage)
552                 gnome_druid_set_page(Druid,MicrosoftComPage);
553         else if (page_active==MicrosoftComPage)
554                 gnome_druid_set_page(Druid,PageFinish);
555 }
556
557 gboolean on_Page_back(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer user_data)
558 {
559         g_return_val_if_fail(GNOME_IS_DRUID_PAGE(gnomedruidpage),FALSE);
560
561         if (!in_progress) {
562                 if (all_modules_found) {
563                         gnome_druid_set_page(Druid,PageStart);
564                         return TRUE;    /* ignore button press */
565                         }
566                 return FALSE;   /* proceed to previous page */
567                 }
568
569         aborted=TRUE;
570         aborted_back=TRUE;
571         state_changed();
572
573         return TRUE;    /* ignore button press now; we will respect 'aborted_back' */
574 }
575
576 void on_Druid_cancel(GnomeDruid *gnomedruid,gpointer user_data /* unused */)
577 {
578         g_return_if_fail(GNOME_IS_DRUID(gnomedruid));
579
580         /* gtk_main_quit() would not abort the current operation. */
581         exit(EXIT_SUCCESS);
582 }
583
584 static void on_DruidButtonOK_clicked_dialog_callback(gint reply,gint *replyp /* data */)
585 {
586         g_return_if_fail(reply>=0);
587         g_return_if_fail(replyp!=NULL);
588
589         *replyp=reply;
590 }
591
592 void on_DruidButtonOK_clicked(GtkButton *button,gpointer user_data /* unused */)
593 {
594 GtkWidget *dialog;
595 gint reply;
596
597         g_return_if_fail(GTK_IS_BUTTON(button));
598
599         if (all_modules_found)
600                 exit(EXIT_SUCCESS);
601
602         /* TODO: Avoid dialog if already on Finish page. */
603         reply=-1;
604         dialog=gnome_app_ok_cancel_modal(App,_(
605                         "Although essential modules (\"ntoskrnl.exe\" and \"ntfs.sys\") are available "
606                         "you may still want to get their better version and/or more modules. "
607                         "Really quit?"),
608                         (GnomeReplyCallback)on_DruidButtonOK_clicked_dialog_callback,
609                         &reply);        /* data */
610         g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
611         /* Never call gtk_main() from other thread than the initial one.
612          * We would have to switch GTK+ context (g_main_context()?).
613          */
614         gtk_main();
615         /* 'dialog' gets destroyed automatically */
616         if (reply==0)   /* 0 for 'OK', 1 for 'Cancel', left -1 for dialog close. */
617                 exit(EXIT_SUCCESS);
618 }
619
620 static void button_stock_set_label(GtkWidget *widget,const gchar *label_text_new /* callback_data */)
621 {
622         g_return_if_fail(GTK_IS_WIDGET(widget));
623         g_return_if_fail(label_text_new!=NULL);
624
625         /**/ if (GTK_IS_CONTAINER(widget))
626                 gtk_container_foreach(GTK_CONTAINER(widget),
627                                 (GtkCallback)button_stock_set_label,    /* callback */
628                                 (/* de-conts */ gchar *)label_text_new);        /* callback_data */
629         else if (GTK_IS_LABEL(widget))
630                 gtk_label_set_text_with_mnemonic(GTK_LABEL(widget),label_text_new);
631 }
632
633 static void PageFinish_set_label_attr(GtkWidget *widget,gpointer callback_data /* unused */)
634 {
635         g_return_if_fail(GTK_IS_WIDGET(widget));
636
637         /**/ if (GTK_IS_CONTAINER(widget))
638                 gtk_container_foreach(GTK_CONTAINER(widget),
639                                 (GtkCallback)PageFinish_set_label_attr, /* callback */
640                                 callback_data); /* callback_data; unused */
641         else if (GTK_IS_LABEL(widget) && gtk_label_get_line_wrap(GTK_LABEL(widget)))
642                 gtk_label_set_selectable(GTK_LABEL(widget),TRUE);
643 }
644
645 /* of "ui-gnome-interface.h": */
646 GtkWidget *create_App(void);
647 /* of "ui-gnome-support.h": */
648 GtkWidget *lookup_widget(GtkWidget *widget,const gchar *widget_name);
649
650 static void App_init(void)
651 {
652 GtkTreeViewColumn *column;
653 GtkCellRenderer *cell;
654 GtkBox *druid_button_box;
655
656         gdk_threads_enter();
657
658         App=GNOME_APP(create_App());
659
660         DriversTreeView=GTK_TREE_VIEW(lookup_widget(GTK_WIDGET(App),"DriversTreeView"));
661         DriversFrame=GTK_FRAME(lookup_widget(GTK_WIDGET(App),"DriversFrame"));
662         ProgressFrame=GTK_FRAME(lookup_widget(GTK_WIDGET(App),"ProgressFrame"));
663         Druid=GNOME_DRUID(lookup_widget(GTK_WIDGET(App),"Druid"));
664         PageStart=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"PageStart"));
665         ScanDiskPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"ScanDiskPage"));
666         ScanPathPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"ScanPathPage"));
667         MicrosoftComPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"MicrosoftComPage"));
668         PageFinish=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"PageFinish"));
669         ScanPathLocationComboEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ScanPathLocationComboEntry"));
670         MicrosoftComConfirmButton=GTK_BUTTON(lookup_widget(GTK_WIDGET(App),"MicrosoftComConfirmButton"));
671         MicrosoftComProgress=GTK_PROGRESS_BAR(lookup_widget(GTK_WIDGET(App),"MicrosoftComProgress"));
672         ProgressEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ProgressEntry"));
673
674         druid_button_box=GTK_BOX(gtk_widget_get_parent(Druid->next));
675
676         DriversTreeStore=gtk_tree_store_new(DRIVERS_TREE_STORE_COLUMN_NUM,DRIVERS_TREE_STORE_COLUMN_TYPE_LIST);
677         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(DriversTreeStore),
678                         DRIVERS_TREE_STORE_COLUMN_TYPE,GTK_SORT_ASCENDING);
679         gtk_tree_view_set_model(DriversTreeView,GTK_TREE_MODEL(DriversTreeStore));
680
681         column=gtk_tree_view_column_new();
682         cell=gtk_cell_renderer_text_new();
683         gtk_tree_view_column_pack_start(column,cell,
684                         TRUE);  /* expand */
685         gtk_tree_view_column_set_attributes(column,cell,
686                         "text",DRIVERS_TREE_STORE_COLUMN_TYPE,
687                         NULL);
688         gtk_tree_view_append_column(DriversTreeView,column);
689
690         column=gtk_tree_view_column_new();
691         cell=gtk_cell_renderer_text_new();
692         gtk_tree_view_column_pack_start(column,cell,
693                         TRUE);  /* expand */
694         gtk_tree_view_column_set_attributes(column,cell,
695                         "text",DRIVERS_TREE_STORE_COLUMN_ID,
696                         NULL);
697         gtk_tree_view_append_column(DriversTreeView,column);
698
699         /* gnome_druid_set_show_finish() just replaces Next<->Finish buttons displayed. */
700         gtk_widget_hide(GTK_WIDGET(Druid->finish));
701
702         DruidButtonSkip=GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_REDO));
703         button_stock_set_label(
704                         GTK_WIDGET(DruidButtonSkip),    /* widget */
705                         _("_Skip"));    /* label_text_new */
706         gtk_box_pack_end(druid_button_box,GTK_WIDGET(DruidButtonSkip),FALSE,TRUE,0);
707         gtk_widget_show(GTK_WIDGET(DruidButtonSkip));
708         g_signal_connect((gpointer)DruidButtonSkip,"clicked",G_CALLBACK(on_DruidButtonSkip_clicked),NULL);
709
710         DruidButtonOK=GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_OK));
711         gtk_box_pack_end(druid_button_box,GTK_WIDGET(DruidButtonOK),FALSE,TRUE,0);
712         gtk_widget_show(GTK_WIDGET(DruidButtonOK));
713         g_signal_connect((gpointer)DruidButtonOK,"clicked",G_CALLBACK(on_DruidButtonOK_clicked),NULL);
714
715         PageFinish_set_label_attr(
716                         GTK_WIDGET(PageFinish), /* widget */
717                         NULL);  /* callback_data; unused */
718
719         state_changed();
720
721         gdk_threads_leave();
722 }
723
724 static void ui_gnome_g_log_handler(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
725 {
726 GtkWidget *dialog;
727
728         /* Ignore arrors by cabextract during its abortion. */
729         if (in_progress && aborted)
730                 return;
731
732         gdk_threads_enter();
733
734         /**/ if (log_level & G_LOG_LEVEL_ERROR)
735                 dialog=gnome_app_error(App,message);
736         else if (log_level & (G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING))
737                 dialog=gnome_app_warning(App,message);
738         else
739                 dialog=gnome_app_message(App,message);
740
741         gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
742         g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
743         gtk_main();
744         /* 'dialog' gets destroyed automatically */
745
746         gdk_flush();
747         gdk_threads_leave();
748 }
749
750 static void ui_gnome_interactive(void)
751 {
752         gdk_threads_enter();
753
754         /* Postpone gtk_widget_show_all() from App_init() here
755          * to have already passed all ui_gnome_module_available_notify().
756          */
757         gnome_druid_set_page(Druid,MicrosoftComPage);
758         gtk_widget_show_all(GTK_WIDGET(App));
759 #if 0
760         /* gnome_druid_set_page(Druid,PageStart); */
761         gnome_druid_set_page(Druid,ScanDiskPage);
762         gnome_druid_set_page(Druid,ScanPathPage);
763         /* gnome_druid_set_page(Druid,MicrosoftComPage); */
764         gnome_druid_set_page(Druid,PageFinish);
765 #endif
766         gnome_druid_set_page(Druid,PageStart);
767
768         gtk_main();
769
770         gdk_threads_leave();
771
772         exit(EXIT_SUCCESS);
773 }
774
775 gboolean ui_gnome_init(void)
776 {
777         acquire_module_available_notify=ui_gnome_module_available_notify;
778         acquire_module_all_modules_found_notify=ui_gnome_all_modules_found_notify;
779         ui_progress=ui_gnome_progress;
780         ui_progress_bar=ui_gnome_progress_bar;
781         ui_interactive=ui_gnome_interactive;
782         captivemodid_module_best_priority_notify=ui_gnome_module_best_priority_notify;
783
784 #ifdef UI_GNOME_THREADS
785         /* gdk_threads_init() must be called before gtk_init()!
786          * gtk_init() gets called by create_App() here.
787          */
788         if (!g_thread_supported())
789                 g_thread_init(NULL);
790         if (!gdk_threads_mutex)
791                 gdk_threads_init();
792 #endif /* UI_GNOME_THREADS */
793
794         /* Graphic widgets will all be hidden yet. */
795         App_init();
796         /* ui_gnome_g_log_handler() needs 'App'. */
797         g_log_set_handler(
798                         G_LOG_DOMAIN,   /* log_domain; "Captive" */
799                         G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL,    /* log_levels */
800                         ui_gnome_g_log_handler, /* log_func */
801                         NULL);  /* user_data */
802
803         return TRUE;
804 }