2 * Drivers acquiring installation utility
3 * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
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
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.
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
22 #include "ui-gnome.h" /* self */
23 #include <glib/gmessages.h>
24 #include "moduriload.h"
26 #include <gtk/gtkmain.h>
27 #include <gtk/gtktreeviewcolumn.h>
28 #include <gtk/gtkbox.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>
38 #include <libgnomevfs/gnome-vfs-utils.h>
40 #include <captive/macros.h>
44 #define PROGRESS_UPDATE_USEC 100000
48 static GtkTreeStore *DriversTreeStore;
49 static GtkFrame *DriversFrame;
50 static GtkFrame *ProgressFrame;
51 static GtkEntry *ProgressEntry;
52 static GnomeDruid *Druid;
53 static GtkButton *DruidButtonSkip;
54 static GtkButton *DruidButtonOK;
55 static GtkTreeView *DriversTreeView;
56 static GnomeDruidPage *PageStart;
57 static GnomeDruidPage *ScanDiskPage;
58 static GnomeDruidPage *ScanPathPage;
59 static GnomeDruidPage *MicrosoftComPage;
60 static GnomeDruidPage *PageFinish;
61 static GtkEntry *ScanPathLocationComboEntry;
62 static GtkButton *MicrosoftComConfirmButton;
63 static GtkProgressBar *MicrosoftComProgress;
65 DRIVERS_TREE_STORE_COLUMN_TYPE,
66 DRIVERS_TREE_STORE_COLUMN_ID,
67 DRIVERS_TREE_STORE_COLUMN_NUM, /* total # */
69 #define DRIVERS_TREE_STORE_COLUMN_TYPE_LIST G_TYPE_STRING,G_TYPE_STRING
72 /* map: (gchar *)type-> (GtkTreeIter *) */
73 static GHashTable *DriversTreeStore_Iter_hash;
75 static void DriversTreeStore_Iter_hash_key_destroy_func(gchar *type)
77 g_return_if_fail(type!=NULL);
82 static void DriversTreeStore_Iter_hash_init(void)
84 if (DriversTreeStore_Iter_hash)
86 DriversTreeStore_Iter_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
87 (GDestroyNotify)DriversTreeStore_Iter_hash_key_destroy_func,
88 NULL); /* value_destroy_func */
91 static GtkTreeIter *DriversTreeStore_Iter_hash_get_iter(const gchar *type)
95 g_return_val_if_fail(type!=NULL,NULL);
97 DriversTreeStore_Iter_hash_init();
98 if (!(r=g_hash_table_lookup(DriversTreeStore_Iter_hash,type))) {
100 gtk_tree_store_append(DriversTreeStore,
103 g_hash_table_insert(DriversTreeStore_Iter_hash,g_strdup(type),r);
109 static gboolean some_modules_found=FALSE;
110 static gboolean in_progress=FALSE;
111 static GnomeDruidPage *page_active;
113 static void state_changed(void)
115 /* Not yet initialized? */
119 gtk_widget_set_sensitive(GTK_WIDGET(DruidButtonSkip),
120 (page_active!=PageStart && page_active!=PageFinish));
121 gtk_widget_set_sensitive(GTK_WIDGET(DruidButtonOK),some_modules_found);
124 gtk_widget_set_sensitive(Druid->next,FALSE);
125 gtk_widget_set_sensitive(GTK_WIDGET(DruidButtonOK),some_modules_found);
126 gtk_widget_set_sensitive(GTK_WIDGET(MicrosoftComConfirmButton),FALSE);
129 /* It is checked by GTK+ whether the text changed: */
130 gtk_entry_set_text(ProgressEntry,"");
131 gtk_widget_set_sensitive(Druid->next,
132 (page_active!=PageFinish && page_active!=MicrosoftComPage));
133 gtk_widget_set_sensitive(GTK_WIDGET(MicrosoftComConfirmButton),
134 (page_active==MicrosoftComPage));
135 gtk_progress_bar_set_fraction(MicrosoftComProgress,(gdouble)0);
136 gtk_progress_bar_set_text(MicrosoftComProgress,"");
140 static void ui_gnome_module_best_priority_notify(const gchar *module_type)
144 g_return_if_fail(module_type!=NULL);
146 iter=DriversTreeStore_Iter_hash_get_iter(module_type);
147 gtk_tree_store_set(DriversTreeStore,iter,
148 DRIVERS_TREE_STORE_COLUMN_TYPE,module_type,
149 DRIVERS_TREE_STORE_COLUMN_ID ,
151 || !strcmp(module_type,"ntoskrnl.exe")
152 || !strcmp(module_type,"ntfs.sys"))
153 ? _("NOT FOUND; essential module for NTFS disks access")
154 : _("not found; optional module")),
158 static void ui_gnome_module_available_notify(struct module_available *module_available)
161 static gboolean some_module_ntoskrnl_exe_found=FALSE;
162 static gboolean some_module_ntfs_sys_found=FALSE;
164 g_return_if_fail(module_available!=NULL);
165 g_return_if_fail(module_available->module!=NULL);
167 iter=DriversTreeStore_Iter_hash_get_iter(module_available->module->type);
168 gtk_tree_store_set(DriversTreeStore,iter,
169 DRIVERS_TREE_STORE_COLUMN_TYPE,module_available->module->type,
170 DRIVERS_TREE_STORE_COLUMN_ID ,module_available->module->id,
173 if (!strcmp(module_available->module->type,"ntoskrnl.exe"))
174 some_module_ntoskrnl_exe_found=TRUE;
175 if (!strcmp(module_available->module->type,"ntfs.sys"))
176 some_module_ntfs_sys_found=TRUE;
178 some_modules_found=some_module_ntoskrnl_exe_found && some_module_ntfs_sys_found;
182 static gboolean all_modules_found=FALSE;
184 static void ui_gnome_all_modules_found_notify(void)
186 all_modules_found=TRUE;
190 static gboolean aborted=FALSE;
191 static gboolean aborted_back=FALSE; /* 'Back' button was clicked. */
192 static struct timeval ProgressEntry_updated_timeval;
194 static void progress_start(void)
199 CAPTIVE_MEMZERO(&ProgressEntry_updated_timeval);
203 static void progress_end(void)
209 static gboolean ui_gnome_progress(GnomeVFSURI *uri)
211 /* 'uri' may be NULL */
214 static gchar *uri_text=NULL;
216 /* Store 'uri' on each call (not just if 'diff_timeval' permits)
217 * as we may get into long cabinet extraction phase with 'uri==NULL' calls
218 * where we want to display the currently processed 'uri'.
222 uri_text=gnome_vfs_uri_to_string(uri,GNOME_VFS_URI_HIDE_PASSWORD);
226 struct timeval now_timeval;
227 struct timeval diff_timeval;
229 gettimeofday( /* FIXME: errors ignored */
230 &now_timeval, /* tv */
232 timersub(&now_timeval,&ProgressEntry_updated_timeval,&diff_timeval);
233 if (!ProgressEntry_updated_timeval.tv_sec || diff_timeval.tv_sec>0 || diff_timeval.tv_usec>=PROGRESS_UPDATE_USEC) {
234 gtk_entry_set_text(ProgressEntry,
235 uri_text+(strncmp(uri_text,"file://",strlen("file://")) ? 0 : strlen("file://")));
236 ProgressEntry_updated_timeval=now_timeval;
240 while (gtk_events_pending())
241 gtk_main_iteration();
245 if (all_modules_found)
251 static void ui_gnome_progress_bar(gint done,gint length)
253 g_return_if_fail(done>=0);
254 g_return_if_fail(length>=0);
257 gtk_progress_bar_pulse(MicrosoftComProgress);
258 gtk_progress_bar_set_text(MicrosoftComProgress,"");
261 gchar *done_display,*length_display;
263 gtk_progress_bar_set_fraction(MicrosoftComProgress,((gdouble)done)/length);
264 done_display=gnome_vfs_format_file_size_for_display(done);
265 length_display=gnome_vfs_format_file_size_for_display(length);
266 gtk_progress_bar_set_text(MicrosoftComProgress,
267 captive_printf_alloca("%s / %s",done_display,length_display));
268 g_free(done_display);
269 g_free(length_display);
273 /* FIXME: Change it to "prepare" signal. */
274 void on_Page_map(GtkWidget *vbox_widget,GtkWidget *page_widget)
276 /* Handle non-object (NULL) signal with reversed parameters? */
277 if (GNOME_IS_DRUID_PAGE(vbox_widget) && page_widget==NULL) {
278 page_widget=vbox_widget;
281 g_return_if_fail(vbox_widget==NULL || GTK_IS_VBOX(vbox_widget));
282 g_return_if_fail(GNOME_IS_DRUID_PAGE(page_widget));
284 page_active=GNOME_DRUID_PAGE(page_widget);
285 if (page_active==PageFinish) {
286 gnome_druid_set_show_finish(Druid,FALSE); /* set it each time */
287 /**/ if (!some_modules_found)
288 gnome_druid_page_edge_set_text(GNOME_DRUID_PAGE_EDGE(PageFinish),_(
289 "We need at least some version of drivers essential for this project:"
290 " ntoskrnl.exe and ntfs.sys. Please click 'Back' button to obtain them"
291 " by several methods offered by this installer."));
295 text=final_text(all_modules_found);
296 gnome_druid_page_edge_set_text(GNOME_DRUID_PAGE_EDGE(PageFinish),text);
300 if (page_active==ScanPathPage)
301 gtk_widget_grab_focus(GTK_WIDGET(ScanPathLocationComboEntry));
307 /* FIXME: 'freeze' apparently does not help 'repositioning' of
308 * 'DriversTreeView' during first 'map' of each 'Page'.
310 gtk_widget_freeze_child_notify(vbox_widget);
312 gtk_widget_reparent(GTK_WIDGET(DriversFrame),vbox_widget);
313 gtk_widget_reparent(GTK_WIDGET(ProgressFrame),vbox_widget);
315 gtk_box_reorder_child(GTK_BOX(vbox_widget),GTK_WIDGET(DriversFrame),
318 gtk_box_set_child_packing(GTK_BOX(vbox_widget),GTK_WIDGET(DriversFrame),
323 gtk_box_set_child_packing(GTK_BOX(vbox_widget),GTK_WIDGET(ProgressFrame),
329 /* FIXME: Needed to fix (0,0)-position inside parent GdkWindow. */
330 gtk_widget_queue_resize(GTK_WIDGET(DriversTreeView));
331 gtk_widget_queue_resize(GTK_WIDGET(ProgressEntry));
333 gtk_widget_thaw_child_notify(vbox_widget);
336 gboolean on_Page_next(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer user_data /* unused */)
338 g_return_val_if_fail(GNOME_IS_DRUID_PAGE(gnomedruidpage),FALSE);
340 if (in_progress) /* bogus callback - we should be non-sensitive */
341 return TRUE; /* ignore button press */
343 /**/ if (page_active==PageStart) {
344 if (all_modules_found) {
345 gnome_druid_set_page(Druid,PageFinish);
346 return TRUE; /* ignore button press */
349 else if (page_active==ScanDiskPage) {
355 gnome_druid_set_page(Druid,PageStart);
356 return TRUE; /* ignore button press */
358 if (all_modules_found) {
359 gnome_druid_set_page(Druid,PageFinish);
360 return TRUE; /* ignore button press */
362 return FALSE; /* proceed to next page */
364 else if (page_active==ScanPathPage) {
365 const gchar *scan_path_uri_text=gtk_entry_get_text(ScanPathLocationComboEntry);
367 if (scan_path_uri_text && *scan_path_uri_text) {
368 GnomeVFSURI *scan_path_uri;
370 if ((scan_path_uri=gnome_vfs_uri_new(scan_path_uri_text))) {
372 mod_uri_load(scan_path_uri);
374 gnome_vfs_uri_unref(scan_path_uri);
376 gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanDiskPage));
377 return TRUE; /* ignore button press */
379 if (all_modules_found) {
380 gnome_druid_set_page(Druid,PageFinish);
381 return TRUE; /* ignore button press */
383 gtk_entry_set_text(ScanPathLocationComboEntry,"");
386 g_warning(_("Invalid URI: %s"),scan_path_uri_text);
387 gtk_widget_grab_focus(GTK_WIDGET(ScanPathLocationComboEntry));
388 return TRUE; /* ignore button press; we cleared the URI entry */
390 return FALSE; /* proceed to next page */
393 return FALSE; /* proceed to next page */
396 void on_MicrosoftComConfirmButton_clicked(GtkButton *button,gpointer user_data)
398 g_return_if_fail(GTK_IS_BUTTON(button));
400 if (in_progress) /* bogus callback */
402 if (page_active!=MicrosoftComPage) /* bogus callback */
409 gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanPathPage));
412 if (all_modules_found) {
413 gnome_druid_set_page(Druid,PageFinish);
417 gnome_druid_set_page(Druid,PageFinish);
420 void on_DruidButtonSkip_clicked(GtkButton *button,gpointer user_data /* unused */)
422 g_return_if_fail(GTK_IS_BUTTON(button));
429 if (all_modules_found)
430 gnome_druid_set_page(Druid,PageFinish);
431 else if (page_active==ScanDiskPage)
432 gnome_druid_set_page(Druid,ScanPathPage);
433 else if (page_active==ScanPathPage)
434 gnome_druid_set_page(Druid,MicrosoftComPage);
435 else if (page_active==MicrosoftComPage)
436 gnome_druid_set_page(Druid,PageFinish);
439 gboolean on_Page_back(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer user_data)
441 g_return_val_if_fail(GNOME_IS_DRUID_PAGE(gnomedruidpage),FALSE);
444 if (all_modules_found) {
445 gnome_druid_set_page(Druid,PageStart);
446 return TRUE; /* ignore button press */
448 return FALSE; /* proceed to previous page */
455 return TRUE; /* ignore button press now; we will respect 'aborted_back' */
458 void on_Druid_cancel(GnomeDruid *gnomedruid,gpointer user_data /* unused */)
460 g_return_if_fail(GNOME_IS_DRUID(gnomedruid));
462 /* gtk_main_quit() would not abort the current operation. */
466 static void on_DruidButtonOK_clicked_dialog_callback(gint reply,gint *replyp /* data */)
468 g_return_if_fail(reply>=0);
469 g_return_if_fail(replyp!=NULL);
474 void on_DruidButtonOK_clicked(GtkButton *button,gpointer user_data /* unused */)
479 g_return_if_fail(GTK_IS_BUTTON(button));
481 if (all_modules_found)
484 /* TODO: Avoid dialog if already on Finish page. */
486 dialog=gnome_app_ok_cancel_modal(App,_(
487 "Although essential modules (\"ntoskrnl.exe\" and \"ntfs.sys\") are available "
488 "you may still want to get their better version and/or more modules. "
490 (GnomeReplyCallback)on_DruidButtonOK_clicked_dialog_callback,
492 g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
494 /* 'dialog' gets destroyed automatically */
495 if (reply==0) /* 0 for 'OK', 1 for 'Cancel', left -1 for dialog close. */
499 static void button_stock_set_label(GtkWidget *widget,const gchar *label_text_new /* callback_data */)
501 g_return_if_fail(GTK_IS_WIDGET(widget));
502 g_return_if_fail(label_text_new!=NULL);
504 /**/ if (GTK_IS_CONTAINER(widget))
505 gtk_container_foreach(GTK_CONTAINER(widget),
506 (GtkCallback)button_stock_set_label, /* callback */
507 (/* de-conts */ gchar *)label_text_new); /* callback_data */
508 else if (GTK_IS_LABEL(widget))
509 gtk_label_set_text_with_mnemonic(GTK_LABEL(widget),label_text_new);
512 /* of "ui-gnome-interface.h": */
513 GtkWidget *create_App(void);
514 /* of "ui-gnome-support.h": */
515 GtkWidget *lookup_widget(GtkWidget *widget,const gchar *widget_name);
517 static void App_init(void)
519 GtkTreeViewColumn *column;
520 GtkCellRenderer *cell;
521 GtkBox *druid_button_box;
523 App=GNOME_APP(create_App());
525 DriversTreeView=GTK_TREE_VIEW(lookup_widget(GTK_WIDGET(App),"DriversTreeView"));
526 DriversFrame=GTK_FRAME(lookup_widget(GTK_WIDGET(App),"DriversFrame"));
527 ProgressFrame=GTK_FRAME(lookup_widget(GTK_WIDGET(App),"ProgressFrame"));
528 Druid=GNOME_DRUID(lookup_widget(GTK_WIDGET(App),"Druid"));
529 PageStart=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"PageStart"));
530 ScanDiskPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"ScanDiskPage"));
531 ScanPathPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"ScanPathPage"));
532 MicrosoftComPage=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"MicrosoftComPage"));
533 PageFinish=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"PageFinish"));
534 ScanPathLocationComboEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ScanPathLocationComboEntry"));
535 MicrosoftComConfirmButton=GTK_BUTTON(lookup_widget(GTK_WIDGET(App),"MicrosoftComConfirmButton"));
536 MicrosoftComProgress=GTK_PROGRESS_BAR(lookup_widget(GTK_WIDGET(App),"MicrosoftComProgress"));
537 ProgressEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ProgressEntry"));
539 druid_button_box=GTK_BOX(gtk_widget_get_parent(Druid->next));
541 DriversTreeStore=gtk_tree_store_new(DRIVERS_TREE_STORE_COLUMN_NUM,DRIVERS_TREE_STORE_COLUMN_TYPE_LIST);
542 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(DriversTreeStore),
543 DRIVERS_TREE_STORE_COLUMN_TYPE,GTK_SORT_ASCENDING);
544 gtk_tree_view_set_model(DriversTreeView,GTK_TREE_MODEL(DriversTreeStore));
546 column=gtk_tree_view_column_new();
547 cell=gtk_cell_renderer_text_new();
548 gtk_tree_view_column_pack_start(column,cell,
550 gtk_tree_view_column_set_attributes(column,cell,
551 "text",DRIVERS_TREE_STORE_COLUMN_TYPE,
553 gtk_tree_view_append_column(DriversTreeView,column);
555 column=gtk_tree_view_column_new();
556 cell=gtk_cell_renderer_text_new();
557 gtk_tree_view_column_pack_start(column,cell,
559 gtk_tree_view_column_set_attributes(column,cell,
560 "text",DRIVERS_TREE_STORE_COLUMN_ID,
562 gtk_tree_view_append_column(DriversTreeView,column);
564 /* gnome_druid_set_show_finish() just replaces Next<->Finish buttons displayed. */
565 gtk_widget_hide(GTK_WIDGET(Druid->finish));
567 DruidButtonSkip=GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_REDO));
568 gtk_container_foreach(GTK_CONTAINER(DruidButtonSkip),
569 (GtkCallback)button_stock_set_label, /* callback */
570 _("_Skip")); /* callback_data */
571 gtk_box_pack_end(druid_button_box,GTK_WIDGET(DruidButtonSkip),FALSE,TRUE,0);
572 gtk_widget_show(GTK_WIDGET(DruidButtonSkip));
573 g_signal_connect((gpointer)DruidButtonSkip,"clicked",G_CALLBACK(on_DruidButtonSkip_clicked),NULL);
575 DruidButtonOK=GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_OK));
576 gtk_box_pack_end(druid_button_box,GTK_WIDGET(DruidButtonOK),FALSE,TRUE,0);
577 gtk_widget_show(GTK_WIDGET(DruidButtonOK));
578 g_signal_connect((gpointer)DruidButtonOK,"clicked",G_CALLBACK(on_DruidButtonOK_clicked),NULL);
583 static void ui_gnome_g_log_handler(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
587 /* Ignore arrors by cabextract during its abortion. */
588 if (in_progress && aborted)
591 /**/ if (log_level & G_LOG_LEVEL_ERROR)
592 dialog=gnome_app_error(App,message);
593 else if (log_level & (G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING))
594 dialog=gnome_app_warning(App,message);
596 dialog=gnome_app_message(App,message);
598 gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
599 g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
601 /* 'dialog' gets destroyed automatically */
604 static void ui_gnome_interactive(void)
606 gtk_widget_show_all(GTK_WIDGET(App));
611 gboolean ui_gnome_init(void)
613 acquire_module_available_notify=ui_gnome_module_available_notify;
614 acquire_module_all_modules_found_notify=ui_gnome_all_modules_found_notify;
615 ui_progress=ui_gnome_progress;
616 ui_progress_bar=ui_gnome_progress_bar;
617 ui_interactive=ui_gnome_interactive;
618 captivemodid_module_best_priority_notify=ui_gnome_module_best_priority_notify;
620 /* Graphic widgets will all be hidden yet. */
622 /* ui_gnome_g_log_handler() needs 'App'. */
624 G_LOG_DOMAIN, /* log_domain; "Captive" */
625 G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL, /* log_levels */
626 ui_gnome_g_log_handler, /* log_func */
627 NULL); /* user_data */