]> git.tdb.fi Git - ext/subsurface.git/blob - gtk-gui.c
f28dde4844d5de2ce134844ae2d701d9603a4cf0
[ext/subsurface.git] / gtk-gui.c
1 /* gtk-gui.c */
2 /* gtk UI implementation */
3 /* creates the window and overall layout
4  * divelist, dive info, equipment and printing are handled in their own source files
5  */
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <time.h>
10 #include <unistd.h>
11
12 #include "dive.h"
13 #include "divelist.h"
14 #include "display.h"
15 #include "display-gtk.h"
16
17 #include "libdivecomputer.h"
18
19 GtkWidget *main_window;
20 GtkWidget *main_vbox;
21 GtkWidget *error_info_bar;
22 GtkWidget *error_label;
23 GtkWidget *vpane, *hpane;
24 int        error_count;
25
26 const char *divelist_font;
27
28 struct units output_units;
29
30 static GtkWidget *dive_profile;
31
32 visible_cols_t visible_cols = {TRUE, FALSE};
33
34 static const char *default_dive_computer_vendor;
35 static const char *default_dive_computer_product;
36 static const char *default_dive_computer_device;
37
38 static int is_default_dive_computer(const char *vendor, const char *product)
39 {
40         return default_dive_computer_vendor && !strcmp(vendor, default_dive_computer_vendor) &&
41                 default_dive_computer_product && !strcmp(product, default_dive_computer_product);
42 }
43
44 static int is_default_dive_computer_device(const char *name)
45 {
46         return default_dive_computer_device && !strcmp(name, default_dive_computer_device);
47 }
48
49 static void set_default_dive_computer(const char *vendor, const char *product)
50 {
51         if (!vendor || !*vendor)
52                 return;
53         if (!product || !*product)
54                 return;
55         if (is_default_dive_computer(vendor, product))
56                 return;
57         default_dive_computer_vendor = vendor;
58         default_dive_computer_product = product;
59         subsurface_set_conf("dive_computer_vendor", PREF_STRING, vendor);
60         subsurface_set_conf("dive_computer_product", PREF_STRING, product);
61 }
62
63 static void set_default_dive_computer_device(const char *name)
64 {
65         if (!name || !*name)
66                 return;
67         if (is_default_dive_computer_device(name))
68                 return;
69         default_dive_computer_device = name;
70         subsurface_set_conf("dive_computer_device", PREF_STRING, name);
71 }
72
73 void repaint_dive(void)
74 {
75         update_dive(current_dive);
76         if (dive_profile)
77                 gtk_widget_queue_draw(dive_profile);
78 }
79
80 static char *existing_filename;
81 static gboolean need_icon = TRUE;
82
83 static void on_info_bar_response(GtkWidget *widget, gint response,
84                                  gpointer data)
85 {
86         if (response == GTK_RESPONSE_OK)
87         {
88                 gtk_widget_destroy(widget);
89                 error_info_bar = NULL;
90         }
91 }
92
93 void report_error(GError* error)
94 {
95         if (error == NULL)
96         {
97                 return;
98         }
99         
100         if (error_info_bar == NULL)
101         {
102                 error_count = 1;
103                 error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
104                                                                GTK_RESPONSE_OK,
105                                                                NULL);
106                 g_signal_connect(error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
107                 gtk_info_bar_set_message_type(GTK_INFO_BAR(error_info_bar),
108                                               GTK_MESSAGE_ERROR);
109                 
110                 error_label = gtk_label_new(error->message);
111                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(error_info_bar));
112                 gtk_container_add(GTK_CONTAINER(container), error_label);
113                 
114                 gtk_box_pack_start(GTK_BOX(main_vbox), error_info_bar, FALSE, FALSE, 0);
115                 gtk_widget_show_all(main_vbox);
116         }
117         else
118         {
119                 error_count++;
120                 char buffer[256];
121                 snprintf(buffer, sizeof(buffer), "Failed to open %i files.", error_count);
122                 gtk_label_set(GTK_LABEL(error_label), buffer);
123         }
124 }
125
126 static void file_open(GtkWidget *w, gpointer data)
127 {
128         GtkWidget *dialog;
129         GtkFileFilter *filter;
130
131         dialog = gtk_file_chooser_dialog_new("Open File",
132                 GTK_WINDOW(main_window),
133                 GTK_FILE_CHOOSER_ACTION_OPEN,
134                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
135                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
136                 NULL);
137         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
138
139         filter = gtk_file_filter_new();
140         gtk_file_filter_add_pattern(filter, "*.xml");
141         gtk_file_filter_add_pattern(filter, "*.XML");
142         gtk_file_filter_add_pattern(filter, "*.sda");
143         gtk_file_filter_add_pattern(filter, "*.SDA");
144         gtk_file_filter_add_mime_type(filter, "text/xml");
145         gtk_file_filter_set_name(filter, "XML file");
146         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
147
148         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
149                 GSList *filenames, *fn_glist;
150                 char *filename;
151                 filenames = fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
152                 
153                 GError *error = NULL;
154                 while(filenames != NULL) {
155                         filename = filenames->data;
156                         parse_file(filename, &error);
157                         if (error != NULL)
158                         {
159                                 report_error(error);
160                                 g_error_free(error);
161                                 error = NULL;
162                         }
163                         
164                         g_free(filename);
165                         filenames = g_slist_next(filenames);
166                 }
167                 g_slist_free(fn_glist);
168                 report_dives(FALSE);
169         }
170         gtk_widget_destroy(dialog);
171 }
172
173 static void file_save(GtkWidget *w, gpointer data)
174 {
175         GtkWidget *dialog;
176         char *filename;
177         if (!existing_filename) {
178                 dialog = gtk_file_chooser_dialog_new("Save File",
179                 GTK_WINDOW(main_window),
180                 GTK_FILE_CHOOSER_ACTION_SAVE,
181                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
182                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
183                 NULL);
184                 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
185
186                 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
187                 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
188                         filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
189                 }
190                 gtk_widget_destroy(dialog);
191         } else {
192                 filename = existing_filename;
193         }
194         if (filename){
195                 save_dives(filename);
196                 mark_divelist_changed(FALSE);
197         }
198 }
199
200 static void ask_save_changes()
201 {
202         GtkWidget *dialog, *label, *content;
203         dialog = gtk_dialog_new_with_buttons("Save Changes?",
204                 GTK_WINDOW(main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
205                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
206                 GTK_STOCK_NO, GTK_RESPONSE_NO,
207                 NULL);
208         content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
209
210         if (!existing_filename){
211                 label = gtk_label_new (
212                         "You have unsaved changes\nWould you like to save those before exiting the program?");
213         } else {
214                 char *label_text = (char*) malloc(sizeof(char) * (92 + strlen(existing_filename)));
215                 sprintf(label_text,
216                         "You have unsaved changes to file: %s \nWould you like to save those before exiting the program?",
217                         existing_filename);
218                 label = gtk_label_new (label_text);
219                 g_free(label_text);
220         }
221         gtk_container_add (GTK_CONTAINER (content), label);
222         gtk_widget_show_all (dialog);
223         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
224         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
225                 file_save(NULL,NULL);
226         }
227         gtk_widget_destroy(dialog);
228 }
229
230 static gboolean on_delete(GtkWidget* w, gpointer data)
231 {
232         /* Make sure to flush any modified dive data */
233         update_dive(NULL);
234
235         if (unsaved_changes())
236                 ask_save_changes();
237
238         return FALSE; /* go ahead, kill the program, we're good now */
239 }
240
241 static void on_destroy(GtkWidget* w, gpointer data)
242 {
243         gtk_main_quit();
244 }
245
246 static void quit(GtkWidget *w, gpointer data)
247 {
248         /* Make sure to flush any modified dive data */
249         update_dive(NULL);
250
251         if (unsaved_changes())
252                 ask_save_changes();
253         gtk_main_quit();
254 }
255
256 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title,
257                                 data_func_t data_func, unsigned int flags)
258 {
259         GtkCellRenderer *renderer;
260         GtkTreeViewColumn *col;
261         double xalign = 0.0; /* left as default */
262         PangoAlignment align;
263         gboolean visible;
264
265         align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
266                 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
267                 PANGO_ALIGN_CENTER;
268         visible = !(flags & INVISIBLE);
269
270         renderer = gtk_cell_renderer_text_new();
271         col = gtk_tree_view_column_new();
272
273         gtk_tree_view_column_set_title(col, title);
274         if (!(flags & UNSORTABLE))
275                 gtk_tree_view_column_set_sort_column_id(col, index);
276         gtk_tree_view_column_set_resizable(col, TRUE);
277         gtk_tree_view_column_pack_start(col, renderer, TRUE);
278         if (data_func)
279                 gtk_tree_view_column_set_cell_data_func(col, renderer, data_func, (void *)(long)index, NULL);
280         else
281                 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
282         gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
283         switch (align) {
284         case PANGO_ALIGN_LEFT:
285                 xalign = 0.0;
286                 break;
287         case PANGO_ALIGN_CENTER:
288                 xalign = 0.5;
289                 break;
290         case PANGO_ALIGN_RIGHT:
291                 xalign = 1.0;
292                 break;
293         }
294         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
295         gtk_tree_view_column_set_visible(col, visible);
296         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
297         return col;
298 }
299
300 static void create_radio(GtkWidget *vbox, const char *w_name, ...)
301 {
302         va_list args;
303         GtkRadioButton *group = NULL;
304         GtkWidget *box, *label;
305
306         box = gtk_hbox_new(TRUE, 10);
307         gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 0);
308
309         label = gtk_label_new(w_name);
310         gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
311
312         va_start(args, w_name);
313         for (;;) {
314                 int enabled;
315                 const char *name;
316                 GtkWidget *button;
317                 void *callback_fn;
318
319                 name = va_arg(args, char *);
320                 if (!name)
321                         break;
322                 callback_fn = va_arg(args, void *);
323                 enabled = va_arg(args, int);
324
325                 button = gtk_radio_button_new_with_label_from_widget(group, name);
326                 group = GTK_RADIO_BUTTON(button);
327                 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
328                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), enabled);
329                 g_signal_connect(button, "toggled", G_CALLBACK(callback_fn), NULL);
330         }
331         va_end(args);
332 }
333
334 #define UNITCALLBACK(name, type, value)                         \
335 static void name(GtkWidget *w, gpointer data)                   \
336 {                                                               \
337         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \
338                 menu_units.type = value;                        \
339 }
340
341 static struct units menu_units;
342
343 UNITCALLBACK(set_meter, length, METERS)
344 UNITCALLBACK(set_feet, length, FEET)
345 UNITCALLBACK(set_bar, pressure, BAR)
346 UNITCALLBACK(set_psi, pressure, PSI)
347 UNITCALLBACK(set_liter, volume, LITER)
348 UNITCALLBACK(set_cuft, volume, CUFT)
349 UNITCALLBACK(set_celsius, temperature, CELSIUS)
350 UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT)
351 UNITCALLBACK(set_kg, weight, KG)
352 UNITCALLBACK(set_lbs, weight, LBS)
353
354 #define OPTIONCALLBACK(name, option) \
355 static void name(GtkWidget *w, gpointer data) \
356 { \
357         option = GTK_TOGGLE_BUTTON(w)->active; \
358 }
359
360 OPTIONCALLBACK(otu_toggle, visible_cols.otu)
361 OPTIONCALLBACK(sac_toggle, visible_cols.sac)
362 OPTIONCALLBACK(nitrox_toggle, visible_cols.nitrox)
363 OPTIONCALLBACK(temperature_toggle, visible_cols.temperature)
364 OPTIONCALLBACK(totalweight_toggle, visible_cols.totalweight)
365 OPTIONCALLBACK(suit_toggle, visible_cols.suit)
366 OPTIONCALLBACK(cylinder_toggle, visible_cols.cylinder)
367
368 static void event_toggle(GtkWidget *w, gpointer _data)
369 {
370         gboolean *plot_ev = _data;
371
372         *plot_ev = GTK_TOGGLE_BUTTON(w)->active;
373 }
374
375 static void preferences_dialog(GtkWidget *w, gpointer data)
376 {
377         int result;
378         GtkWidget *dialog, *font, *frame, *box, *vbox, *button;
379
380         menu_units = output_units;
381
382         dialog = gtk_dialog_new_with_buttons("Preferences",
383                 GTK_WINDOW(main_window),
384                 GTK_DIALOG_DESTROY_WITH_PARENT,
385                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
386                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
387                 NULL);
388
389         frame = gtk_frame_new("Units");
390         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
391         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
392
393         box = gtk_vbox_new(FALSE, 6);
394         gtk_container_add(GTK_CONTAINER(frame), box);
395
396         create_radio(box, "Depth:",
397                 "Meter", set_meter, (output_units.length == METERS),
398                 "Feet",  set_feet, (output_units.length == FEET),
399                 NULL);
400
401         create_radio(box, "Pressure:",
402                 "Bar", set_bar, (output_units.pressure == BAR),
403                 "PSI",  set_psi, (output_units.pressure == PSI),
404                 NULL);
405
406         create_radio(box, "Volume:",
407                 "Liter",  set_liter, (output_units.volume == LITER),
408                 "CuFt", set_cuft, (output_units.volume == CUFT),
409                 NULL);
410
411         create_radio(box, "Temperature:",
412                 "Celsius", set_celsius, (output_units.temperature == CELSIUS),
413                 "Fahrenheit",  set_fahrenheit, (output_units.temperature == FAHRENHEIT),
414                 NULL);
415
416         create_radio(box, "Weight:",
417                 "kg", set_kg, (output_units.weight == KG),
418                 "lbs",  set_lbs, (output_units.weight == LBS),
419                 NULL);
420
421         frame = gtk_frame_new("Show Columns");
422         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
423
424         box = gtk_hbox_new(FALSE, 6);
425         gtk_container_add(GTK_CONTAINER(frame), box);
426
427         button = gtk_check_button_new_with_label("Temp");
428         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.temperature);
429         gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
430         g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(temperature_toggle), NULL);
431
432         button = gtk_check_button_new_with_label("Cyl");
433         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.cylinder);
434         gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
435         g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(cylinder_toggle), NULL);
436
437         button = gtk_check_button_new_with_label("O" UTF8_SUBSCRIPT_2 "%");
438         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.nitrox);
439         gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
440         g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(nitrox_toggle), NULL);
441
442         button = gtk_check_button_new_with_label("SAC");
443         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.sac);
444         gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
445         g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(sac_toggle), NULL);
446
447         button = gtk_check_button_new_with_label("OTU");
448         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.otu);
449         gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
450         g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL);
451
452         button = gtk_check_button_new_with_label("Weight");
453         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.totalweight);
454         gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
455         g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(totalweight_toggle), NULL);
456
457         button = gtk_check_button_new_with_label("Suit");
458         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.suit);
459         gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
460         g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL);
461
462         font = gtk_font_button_new_with_font(divelist_font);
463         gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
464
465         gtk_widget_show_all(dialog);
466         result = gtk_dialog_run(GTK_DIALOG(dialog));
467         if (result == GTK_RESPONSE_ACCEPT) {
468                 /* Make sure to flush any modified old dive data with old units */
469                 update_dive(NULL);
470
471                 divelist_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
472                 set_divelist_font(divelist_font);
473
474                 output_units = menu_units;
475                 update_dive_list_units();
476                 repaint_dive();
477                 update_dive_list_col_visibility();
478
479                 subsurface_set_conf("feet", PREF_BOOL, BOOL_TO_PTR(output_units.length == FEET));
480                 subsurface_set_conf("psi", PREF_BOOL, BOOL_TO_PTR(output_units.pressure == PSI));
481                 subsurface_set_conf("cuft", PREF_BOOL, BOOL_TO_PTR(output_units.volume == CUFT));
482                 subsurface_set_conf("fahrenheit", PREF_BOOL, BOOL_TO_PTR(output_units.temperature == FAHRENHEIT));
483                 subsurface_set_conf("lbs", PREF_BOOL, BOOL_TO_PTR(output_units.weight == LBS));
484                 subsurface_set_conf("TEMPERATURE", PREF_BOOL, BOOL_TO_PTR(visible_cols.temperature));
485                 subsurface_set_conf("TOTALWEIGHT", PREF_BOOL, BOOL_TO_PTR(visible_cols.totalweight));
486                 subsurface_set_conf("SUIT", PREF_BOOL, BOOL_TO_PTR(visible_cols.suit));
487                 subsurface_set_conf("CYLINDER", PREF_BOOL, BOOL_TO_PTR(visible_cols.cylinder));
488                 subsurface_set_conf("NITROX", PREF_BOOL, BOOL_TO_PTR(visible_cols.nitrox));
489                 subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(visible_cols.sac));
490                 subsurface_set_conf("OTU", PREF_BOOL, BOOL_TO_PTR(visible_cols.otu));
491                 subsurface_set_conf("divelist_font", PREF_STRING, divelist_font);
492
493                 /* Flush the changes out to the system */
494                 subsurface_flush_conf();
495         }
496         gtk_widget_destroy(dialog);
497 }
498
499 static void create_toggle(const char* label, int *on, void *_data)
500 {
501         GtkWidget *button, *table = _data;
502         int rows, cols, x, y;
503         static int count;
504
505         if (table == NULL) {
506                 /* magic way to reset the number of toggle buttons
507                  * that we have already added - call this before you
508                  * create the dialog */
509                 count = 0;
510                 return;
511         }
512         g_object_get(G_OBJECT(table), "n-columns", &cols, "n-rows", &rows, NULL);
513         if (count > rows * cols) {
514                 gtk_table_resize(GTK_TABLE(table),rows+1,cols);
515                 rows++;
516         }
517         x = count % cols;
518         y = count / cols;
519         button = gtk_check_button_new_with_label(label);
520         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *on);
521         gtk_table_attach_defaults(GTK_TABLE(table), button, x, x+1, y, y+1);
522         g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(event_toggle), on);
523         count++;
524 }
525
526 static void selectevents_dialog(GtkWidget *w, gpointer data)
527 {
528         int result;
529         GtkWidget *dialog, *frame, *vbox, *table;
530
531         dialog = gtk_dialog_new_with_buttons("SelectEvents",
532                 GTK_WINDOW(main_window),
533                 GTK_DIALOG_DESTROY_WITH_PARENT,
534                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
535                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
536                 NULL);
537         /* initialize the function that fills the table */
538         create_toggle(NULL, NULL, NULL);
539
540         frame = gtk_frame_new("Enable / Disable Events");
541         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
542         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
543
544         table = gtk_table_new(1, 4, TRUE);
545         gtk_container_add(GTK_CONTAINER(frame), table);
546
547         evn_foreach(&create_toggle, table);
548
549         gtk_widget_show_all(dialog);
550         result = gtk_dialog_run(GTK_DIALOG(dialog));
551         if (result == GTK_RESPONSE_ACCEPT) {
552                 repaint_dive();
553         }
554         gtk_widget_destroy(dialog);
555 }
556
557 static void renumber_dialog(GtkWidget *w, gpointer data)
558 {
559         int result;
560         struct dive *dive;
561         GtkWidget *dialog, *frame, *button, *vbox;
562
563         dialog = gtk_dialog_new_with_buttons("Renumber",
564                 GTK_WINDOW(main_window),
565                 GTK_DIALOG_DESTROY_WITH_PARENT,
566                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
567                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
568                 NULL);
569
570         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
571
572         frame = gtk_frame_new("New starting number");
573         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
574
575         button = gtk_spin_button_new_with_range(1, 50000, 1);
576         gtk_container_add(GTK_CONTAINER(frame), button);
577
578         /*
579          * Do we have a number for the first dive already? Use that
580          * as the default.
581          */
582         dive = get_dive(0);
583         if (dive && dive->number)
584                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), dive->number);
585
586         gtk_widget_show_all(dialog);
587         result = gtk_dialog_run(GTK_DIALOG(dialog));
588         if (result == GTK_RESPONSE_ACCEPT) {
589                 int nr = gtk_spin_button_get_value(GTK_SPIN_BUTTON(button));
590                 renumber_dives(nr);
591                 repaint_dive();
592         }
593         gtk_widget_destroy(dialog);
594 }
595
596 static void about_dialog(GtkWidget *w, gpointer data)
597 {
598         const char *logo_property = NULL;
599         GdkPixbuf *logo = NULL;
600
601         if (need_icon) {
602                 GtkWidget *image = gtk_image_new_from_file(subsurface_icon_name());
603
604                 if (image) {
605                         logo = gtk_image_get_pixbuf(GTK_IMAGE(image));
606                         logo_property = "logo";
607                 }
608         }
609
610         gtk_show_about_dialog(NULL,
611                 "program-name", "SubSurface",
612                 "comments", "Half-arsed divelog software in C",
613                 "license", "GPLv2",
614                 "version", VERSION_STRING,
615                 "copyright", "Linus Torvalds 2011",
616                 "logo-icon-name", "subsurface",
617                 /* Must be last: */
618                 logo_property, logo,
619                 NULL);
620 }
621
622 static void view_list(GtkWidget *w, gpointer data)
623 {
624         gtk_paned_set_position(GTK_PANED(vpane), 0);
625 }
626
627 static void view_profile(GtkWidget *w, gpointer data)
628 {
629         gtk_paned_set_position(GTK_PANED(hpane), 0);
630         gtk_paned_set_position(GTK_PANED(vpane), 65535);
631 }
632
633 static void view_info(GtkWidget *w, gpointer data)
634 {
635         gtk_paned_set_position(GTK_PANED(vpane), 65535);
636         gtk_paned_set_position(GTK_PANED(hpane), 65535);
637 }
638
639 /* Ooh. I don't know how to get the half-way size. So I'm just using random numbers */
640 static void view_three(GtkWidget *w, gpointer data)
641 {
642         gtk_paned_set_position(GTK_PANED(hpane), 400);
643         gtk_paned_set_position(GTK_PANED(vpane), 200);
644 }
645
646 static GtkActionEntry menu_items[] = {
647         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
648         { "LogMenuAction",  GTK_STOCK_FILE, "Log", NULL, NULL, NULL},
649         { "ViewMenuAction",  GTK_STOCK_FILE, "View", NULL, NULL, NULL},
650         { "FilterMenuAction",  GTK_STOCK_FILE, "Filter", NULL, NULL, NULL},
651         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
652         { "OpenFile",       GTK_STOCK_OPEN, NULL,   CTRLCHAR "O", NULL, G_CALLBACK(file_open) },
653         { "SaveFile",       GTK_STOCK_SAVE, NULL,   CTRLCHAR "S", NULL, G_CALLBACK(file_save) },
654         { "Print",          GTK_STOCK_PRINT, NULL,  CTRLCHAR "P", NULL, G_CALLBACK(do_print) },
655         { "Import",         NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) },
656         { "AddDive",        NULL, "Add Dive", NULL, NULL, G_CALLBACK(add_dive_cb) },
657         { "Preferences",    NULL, "Preferences", PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) },
658         { "Renumber",       NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) },
659         { "SelectEvents",   NULL, "SelectEvents", NULL, NULL, G_CALLBACK(selectevents_dialog) },
660         { "Quit",           GTK_STOCK_QUIT, NULL,   CTRLCHAR "Q", NULL, G_CALLBACK(quit) },
661         { "About",          GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
662         { "ViewList",       NULL, "List",  CTRLCHAR "1", NULL, G_CALLBACK(view_list) },
663         { "ViewProfile",    NULL, "Profile", CTRLCHAR "2", NULL, G_CALLBACK(view_profile) },
664         { "ViewInfo",       NULL, "Info", CTRLCHAR "3", NULL, G_CALLBACK(view_info) },
665         { "ViewThree",       NULL, "Three", CTRLCHAR "4", NULL, G_CALLBACK(view_three) },
666 };
667 static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
668
669 static const gchar* ui_string = " \
670         <ui> \
671                 <menubar name=\"MainMenu\"> \
672                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
673                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
674                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
675                                 <menuitem name=\"Print\" action=\"Print\" /> \
676                                 <separator name=\"Separator1\"/> \
677                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
678                                 <separator name=\"Separator2\"/> \
679                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
680                         </menu> \
681                         <menu name=\"LogMenu\" action=\"LogMenuAction\"> \
682                                 <menuitem name=\"Import\" action=\"Import\" /> \
683                                 <menuitem name=\"Add Dive\" action=\"AddDive\" /> \
684                                 <separator name=\"Separator\"/> \
685                                 <menuitem name=\"Renumber\" action=\"Renumber\" /> \
686                                 <menu name=\"View\" action=\"ViewMenuAction\"> \
687                                         <menuitem name=\"List\" action=\"ViewList\" /> \
688                                         <menuitem name=\"Profile\" action=\"ViewProfile\" /> \
689                                         <menuitem name=\"Info\" action=\"ViewInfo\" /> \
690                                         <menuitem name=\"Paned\" action=\"ViewThree\" /> \
691                                 </menu> \
692                         </menu> \
693                         <menu name=\"FilterMenu\" action=\"FilterMenuAction\"> \
694                                 <menuitem name=\"SelectEvents\" action=\"SelectEvents\" /> \
695                         </menu> \
696                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
697                                 <menuitem name=\"About\" action=\"About\" /> \
698                         </menu> \
699                 </menubar> \
700         </ui> \
701 ";
702
703 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
704 {
705         GtkActionGroup *action_group = gtk_action_group_new("Menu");
706         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
707
708         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
709         GError* error = 0;
710         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
711
712         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
713         GtkWidget* menu = gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
714
715         return menu;
716 }
717
718 static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data)
719 {
720         repaint_dive();
721 }
722
723 void init_ui(int *argcp, char ***argvp)
724 {
725         GtkWidget *win;
726         GtkWidget *notebook;
727         GtkWidget *nb_page;
728         GtkWidget *dive_list;
729         GtkWidget *menubar;
730         GtkWidget *vbox;
731         GdkScreen *screen;
732         GtkIconTheme *icon_theme=NULL;
733         GtkSettings *settings;
734         GtkUIManager *ui_manager;
735
736         gtk_init(argcp, argvp);
737         settings = gtk_settings_get_default();
738         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "subsurface setting");
739
740         g_type_init();
741
742         subsurface_open_conf();
743         if (subsurface_get_conf("feet", PREF_BOOL))
744                 output_units.length = FEET;
745         if (subsurface_get_conf("psi", PREF_BOOL))
746                 output_units.pressure = PSI;
747         if (subsurface_get_conf("cuft", PREF_BOOL))
748                 output_units.volume = CUFT;
749         if (subsurface_get_conf("fahrenheit", PREF_BOOL))
750                 output_units.temperature = FAHRENHEIT;
751         if (subsurface_get_conf("lbs", PREF_BOOL))
752                 output_units.weight = LBS;
753         /* an unset key is FALSE - all these are hidden by default */
754         visible_cols.cylinder = PTR_TO_BOOL(subsurface_get_conf("CYLINDER", PREF_BOOL));
755         visible_cols.temperature = PTR_TO_BOOL(subsurface_get_conf("TEMPERATURE", PREF_BOOL));
756         visible_cols.totalweight = PTR_TO_BOOL(subsurface_get_conf("TOTALWEIGHT", PREF_BOOL));
757         visible_cols.suit = PTR_TO_BOOL(subsurface_get_conf("SUIT", PREF_BOOL));
758         visible_cols.nitrox = PTR_TO_BOOL(subsurface_get_conf("NITROX", PREF_BOOL));
759         visible_cols.otu = PTR_TO_BOOL(subsurface_get_conf("OTU", PREF_BOOL));
760         visible_cols.sac = PTR_TO_BOOL(subsurface_get_conf("SAC", PREF_BOOL));
761
762         divelist_font = subsurface_get_conf("divelist_font", PREF_STRING);
763
764         default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor", PREF_STRING);
765         default_dive_computer_product = subsurface_get_conf("dive_computer_product", PREF_STRING);
766         default_dive_computer_device = subsurface_get_conf("dive_computer_device", PREF_STRING);
767
768         error_info_bar = NULL;
769         win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
770         g_set_application_name ("subsurface");
771         /* Let's check if the subsurface icon has been installed or if
772          * we need to try to load it from the current directory */
773         screen = gdk_screen_get_default();
774         if (screen)
775                 icon_theme = gtk_icon_theme_get_for_screen(screen);
776         if (icon_theme) {
777                 if (gtk_icon_theme_has_icon(icon_theme, "subsurface")) {
778                         need_icon = FALSE;
779                         gtk_window_set_default_icon_name ("subsurface");
780                 }
781         }
782         if (need_icon) {
783                 const char *icon_name = subsurface_icon_name();
784                 if (!access(icon_name, R_OK))
785                         gtk_window_set_icon_from_file(GTK_WINDOW(win), icon_name, NULL);
786         }
787         g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL);
788         g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL);
789         main_window = win;
790
791         vbox = gtk_vbox_new(FALSE, 0);
792         gtk_container_add(GTK_CONTAINER(win), vbox);
793         main_vbox = vbox;
794
795         ui_manager = gtk_ui_manager_new();
796         menubar = get_menubar_menu(win, ui_manager);
797
798         subsurface_ui_setup(settings, menubar, vbox, ui_manager);
799
800         vpane = gtk_vpaned_new();
801         gtk_box_pack_start(GTK_BOX(vbox), vpane, TRUE, TRUE, 3);
802
803         hpane = gtk_hpaned_new();
804         gtk_paned_add1(GTK_PANED(vpane), hpane);
805
806         /* Notebook for dive info vs profile vs .. */
807         notebook = gtk_notebook_new();
808         gtk_paned_add1(GTK_PANED(hpane), notebook);
809         g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL);
810
811         /* Create the actual divelist */
812         dive_list = dive_list_create();
813         gtk_widget_set_name(dive_list, "Dive List");
814         gtk_paned_add2(GTK_PANED(vpane), dive_list);
815
816         /* Frame for dive profile */
817         dive_profile = dive_profile_widget();
818         gtk_widget_set_name(dive_profile, "Dive Profile");
819         gtk_paned_add2(GTK_PANED(hpane), dive_profile);
820
821         /* Frame for extended dive info */
822         nb_page = extended_dive_info_widget();
823         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Notes"));
824
825         /* Frame for dive equipment */
826         nb_page = equipment_widget(W_IDX_PRIMARY);
827         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Equipment"));
828
829         /* Frame for single dive statistics */
830         nb_page = single_stats_widget();
831         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Info"));
832
833         /* Frame for total dive statistics */
834         nb_page = total_stats_widget();
835         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Stats"));
836
837         gtk_widget_set_app_paintable(win, TRUE);
838         gtk_widget_show_all(win);
839
840         return;
841 }
842
843 void run_ui(void)
844 {
845         gtk_main();
846 }
847
848 void exit_ui(void)
849 {
850         subsurface_close_conf();
851 }
852
853 typedef struct {
854         cairo_rectangle_int_t rect;
855         const char *text;
856 } tooltip_record_t;
857
858 static tooltip_record_t *tooltip_rects;
859 static int tooltips;
860
861 void attach_tooltip(int x, int y, int w, int h, const char *text)
862 {
863         cairo_rectangle_int_t *rect;
864         tooltip_rects = realloc(tooltip_rects, (tooltips + 1) * sizeof(tooltip_record_t));
865         rect = &tooltip_rects[tooltips].rect;
866         rect->x = x;
867         rect->y = y;
868         rect->width = w;
869         rect->height = h;
870         tooltip_rects[tooltips].text = text;
871         tooltips++;
872 }
873
874 #define INSIDE_RECT(_r,_x,_y)   ((_r.x <= _x) && (_r.x + _r.width >= _x) && \
875                                 (_r.y <= _y) && (_r.y + _r.height >= _y))
876
877 static gboolean profile_tooltip (GtkWidget *widget, gint x, gint y,
878                         gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data)
879 {
880         int i;
881         cairo_rectangle_int_t *drawing_area = user_data;
882         gint tx = x - drawing_area->x; /* get transformed coordinates */
883         gint ty = y - drawing_area->y;
884
885         /* are we over an event marker ? */
886         for (i = 0; i < tooltips; i++) {
887                 if (INSIDE_RECT(tooltip_rects[i].rect, tx, ty)) {
888                         gtk_tooltip_set_text(tooltip,tooltip_rects[i].text);
889                         return TRUE; /* show tooltip */
890                 }
891         }
892         return FALSE; /* don't show tooltip */
893 }
894
895 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
896 {
897         struct dive *dive = current_dive;
898         struct graphics_context gc = { .printer = 0 };
899         static cairo_rectangle_int_t drawing_area;
900
901         /* the drawing area gives TOTAL width * height - x,y is used as the topx/topy offset
902          * so effective drawing area is width-2x * height-2y */
903         drawing_area.width = widget->allocation.width;
904         drawing_area.height = widget->allocation.height;
905         drawing_area.x = drawing_area.width / 20.0;
906         drawing_area.y = drawing_area.height / 20.0;
907
908         gc.cr = gdk_cairo_create(widget->window);
909         g_object_set(widget, "has-tooltip", TRUE, NULL);
910         g_signal_connect(widget, "query-tooltip", G_CALLBACK(profile_tooltip), &drawing_area);
911         init_profile_background(&gc);
912         cairo_paint(gc.cr);
913
914         if (dive) {
915                 if (tooltip_rects) {
916                         free(tooltip_rects);
917                         tooltip_rects = NULL;
918                 }
919                 tooltips = 0;
920                 plot(&gc, &drawing_area, dive);
921         }
922         cairo_destroy(gc.cr);
923
924         return FALSE;
925 }
926
927 GtkWidget *dive_profile_widget(void)
928 {
929         GtkWidget *da;
930
931         da = gtk_drawing_area_new();
932         gtk_widget_set_size_request(da, 350, 250);
933         g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);
934
935         return da;
936 }
937
938 int process_ui_events(void)
939 {
940         int ret=0;
941
942         while (gtk_events_pending()) {
943                 if (gtk_main_iteration_do(0)) {
944                         ret = 1;
945                         break;
946                 }
947         }
948         return ret;
949 }
950
951 static int fill_computer_list(GtkListStore *store)
952 {
953         int index = -1, i;
954         GtkTreeIter iter;
955         dc_iterator_t *iterator = NULL;
956         dc_descriptor_t *descriptor = NULL;
957
958         i = 0;
959         dc_descriptor_iterator(&iterator);
960         while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) {
961                 const char *vendor = dc_descriptor_get_vendor(descriptor);
962                 const char *product = dc_descriptor_get_product(descriptor);
963
964                 gtk_list_store_append(store, &iter);
965                 gtk_list_store_set(store, &iter,
966                         0, descriptor,
967                         -1);
968                 if (is_default_dive_computer(vendor, product))
969                         index = i;
970                 i++;
971         }
972         dc_iterator_free(iterator);
973         return index;
974 }
975
976 void render_dive_computer(GtkCellLayout *cell,
977                 GtkCellRenderer *renderer,
978                 GtkTreeModel *model,
979                 GtkTreeIter *iter,
980                 gpointer data)
981 {
982         char buffer[40];
983         dc_descriptor_t *descriptor = NULL;
984         const char *vendor, *product;
985
986         gtk_tree_model_get(model, iter, 0, &descriptor, -1);
987         vendor = dc_descriptor_get_vendor(descriptor);
988         product = dc_descriptor_get_product(descriptor);
989         snprintf(buffer, sizeof(buffer), "%s %s", vendor, product);
990         g_object_set(renderer, "text", buffer, NULL);
991 }
992
993
994 static GtkComboBox *dive_computer_selector(GtkWidget *vbox)
995 {
996         GtkWidget *hbox, *combo_box, *frame;
997         GtkListStore *model;
998         GtkCellRenderer *renderer;
999         int default_index;
1000
1001         hbox = gtk_hbox_new(FALSE, 6);
1002         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
1003
1004         model = gtk_list_store_new(1, G_TYPE_POINTER);
1005         default_index = fill_computer_list(model);
1006
1007         frame = gtk_frame_new("Dive computer");
1008         gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 3);
1009
1010         combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
1011         gtk_container_add(GTK_CONTAINER(frame), combo_box);
1012
1013         renderer = gtk_cell_renderer_text_new();
1014         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
1015         gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo_box), renderer, render_dive_computer, NULL, NULL);
1016
1017         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), default_index);
1018
1019         return GTK_COMBO_BOX(combo_box);
1020 }
1021
1022 const char *subsurface_device_name()
1023 {
1024         if (!default_dive_computer_device || !*default_dive_computer_device)
1025                 return subsurface_USB_name();
1026         else
1027                 return default_dive_computer_device;
1028 }
1029
1030 static GtkEntry *dive_computer_device(GtkWidget *vbox)
1031 {
1032         GtkWidget *hbox, *entry, *frame;
1033
1034         hbox = gtk_hbox_new(FALSE, 6);
1035         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
1036
1037         frame = gtk_frame_new("Device name");
1038         gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 3);
1039
1040         entry = gtk_entry_new();
1041         gtk_container_add(GTK_CONTAINER(frame), entry);
1042         gtk_entry_set_text(GTK_ENTRY(entry), subsurface_device_name());
1043
1044         return GTK_ENTRY(entry);
1045 }
1046
1047 /* once a file is selected in the FileChooserButton we want to exit the import dialog */
1048 static void on_file_set(GtkFileChooserButton *widget, gpointer _data)
1049 {
1050         GtkDialog *main_dialog = _data;
1051
1052         gtk_dialog_response(main_dialog, GTK_RESPONSE_ACCEPT);
1053 }
1054
1055 static GtkWidget *xml_file_selector(GtkWidget *vbox, GtkWidget *main_dialog)
1056 {
1057         GtkWidget *hbox, *frame, *chooser, *dialog;
1058         GtkFileFilter *filter;
1059
1060         hbox = gtk_hbox_new(FALSE, 6);
1061         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
1062
1063         frame = gtk_frame_new("XML file name");
1064         gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 3);
1065         dialog = gtk_file_chooser_dialog_new("Open XML File",
1066                 GTK_WINDOW(main_window),
1067                 GTK_FILE_CHOOSER_ACTION_OPEN,
1068                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1069                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1070                 NULL);
1071         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
1072
1073         filter = gtk_file_filter_new();
1074         gtk_file_filter_add_pattern(filter, "*.xml");
1075         gtk_file_filter_add_pattern(filter, "*.XML");
1076         gtk_file_filter_add_pattern(filter, "*.sda");
1077         gtk_file_filter_add_pattern(filter, "*.SDA");
1078         gtk_file_filter_add_mime_type(filter, "text/xml");
1079         gtk_file_filter_set_name(filter, "XML file");
1080         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1081
1082         chooser = gtk_file_chooser_button_new_with_dialog(dialog);
1083         g_signal_connect(G_OBJECT(chooser), "file-set", G_CALLBACK(on_file_set), main_dialog);
1084
1085         gtk_file_chooser_button_set_width_chars(GTK_FILE_CHOOSER_BUTTON(chooser), 30);
1086         gtk_container_add(GTK_CONTAINER(frame), chooser);
1087
1088         return chooser;
1089 }
1090
1091 static void do_import_file(gpointer data, gpointer user_data)
1092 {
1093         GError *error = NULL;
1094         parse_file(data, &error);
1095
1096         if (error != NULL)
1097         {
1098                 report_error(error);
1099                 g_error_free(error);
1100                 error = NULL;
1101         }
1102 }
1103
1104 static GtkWidget *import_dive_computer(device_data_t *data, GtkDialog *dialog)
1105 {
1106         GError *error;
1107         GtkWidget *vbox, *info, *container, *label, *button;
1108
1109         error = do_import(data);
1110         if (!error)
1111                 return NULL;
1112
1113         button = gtk_dialog_get_widget_for_response(dialog, GTK_RESPONSE_ACCEPT);
1114         gtk_button_set_use_stock(GTK_BUTTON(button), 0);
1115         gtk_button_set_label(GTK_BUTTON(button), "Retry");
1116
1117         vbox = gtk_dialog_get_content_area(dialog);
1118
1119         info = gtk_info_bar_new();
1120         container = gtk_info_bar_get_content_area(GTK_INFO_BAR(info));
1121         label = gtk_label_new(error->message);
1122         gtk_container_add(GTK_CONTAINER(container), label);
1123         gtk_box_pack_start(GTK_BOX(vbox), info, FALSE, FALSE, 0);
1124         return info;
1125 }
1126
1127 void import_dialog(GtkWidget *w, gpointer data)
1128 {
1129         int result;
1130         GtkWidget *dialog, *hbox, *vbox, *label, *info = NULL;
1131         GtkComboBox *computer;
1132         GtkEntry *device;
1133         GtkWidget *XMLchooser;
1134         device_data_t devicedata = {
1135                 .devname = NULL,
1136         };
1137
1138         dialog = gtk_dialog_new_with_buttons("Import from dive computer",
1139                 GTK_WINDOW(main_window),
1140                 GTK_DIALOG_DESTROY_WITH_PARENT,
1141                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1142                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1143                 NULL);
1144
1145         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1146         label = gtk_label_new("Import: \nLoad XML file or import directly from dive computer");
1147         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3);
1148         XMLchooser = xml_file_selector(vbox, dialog);
1149         computer = dive_computer_selector(vbox);
1150         device = dive_computer_device(vbox);
1151         hbox = gtk_hbox_new(FALSE, 6);
1152         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 3);
1153         devicedata.progress.bar = gtk_progress_bar_new();
1154         gtk_container_add(GTK_CONTAINER(hbox), devicedata.progress.bar);
1155
1156 repeat:
1157         gtk_widget_show_all(dialog);
1158         result = gtk_dialog_run(GTK_DIALOG(dialog));
1159         switch (result) {
1160                 dc_descriptor_t *descriptor;
1161                 GtkTreeIter iter;
1162                 GtkTreeModel *model;
1163                 GSList *list;
1164         case GTK_RESPONSE_ACCEPT:
1165                 /* what happened - did the user pick a file? In that case
1166                  * we ignore whether a dive computer model was picked */
1167                 if (info)
1168                         gtk_widget_destroy(info);
1169                 list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(XMLchooser));
1170                 if (g_slist_length(list) == 0) {
1171                         const char *vendor, *product;
1172
1173                         if (!gtk_combo_box_get_active_iter(computer, &iter))
1174                                 break;
1175                         model = gtk_combo_box_get_model(computer);
1176                         gtk_tree_model_get(model, &iter,
1177                                         0, &descriptor,
1178                                         -1);
1179
1180                         vendor = dc_descriptor_get_vendor(descriptor);
1181                         product = dc_descriptor_get_product(descriptor);
1182
1183                         devicedata.descriptor = descriptor;
1184                         devicedata.vendor = vendor;
1185                         devicedata.product = product;
1186                         devicedata.devname = gtk_entry_get_text(device);
1187                         set_default_dive_computer(vendor, product);
1188                         set_default_dive_computer_device(devicedata.devname);
1189                         info = import_dive_computer(&devicedata, GTK_DIALOG(dialog));
1190                         if (info)
1191                                 goto repeat;
1192                 } else {
1193                         g_slist_foreach(list,do_import_file,NULL);
1194                         g_slist_free(list);
1195                 }
1196                 break;
1197         default:
1198                 break;
1199         }
1200         gtk_widget_destroy(dialog);
1201
1202         report_dives(TRUE);
1203 }
1204
1205 void update_progressbar(progressbar_t *progress, double value)
1206 {
1207         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress->bar), value);
1208 }
1209
1210 void update_progressbar_text(progressbar_t *progress, const char *text)
1211 {
1212         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress->bar), text);
1213 }
1214
1215 void set_filename(const char *filename)
1216 {
1217         if (!existing_filename && filename)
1218                 existing_filename = strdup(filename);
1219         return;
1220 }