6 #include <gconf/gconf-client.h>
12 GtkWidget *main_window;
14 GtkWidget *error_info_bar;
15 GtkWidget *error_label;
17 struct DiveList dive_list;
20 struct units output_units;
22 #define GCONF_NAME(x) "/apps/subsurface/" #x
24 static int sortfn(const void *_a, const void *_b)
26 const struct dive *a = *(void **)_a;
27 const struct dive *b = *(void **)_b;
29 if (a->when < b->when)
31 if (a->when > b->when)
37 * This doesn't really report anything at all. We just sort the
38 * dives, the GUI does the reporting
40 void report_dives(void)
44 qsort(dive_table.dives, dive_table.nr, sizeof(struct dive *), sortfn);
46 for (i = 1; i < dive_table.nr; i++) {
47 struct dive **pp = &dive_table.dives[i-1];
48 struct dive *prev = pp[0];
49 struct dive *dive = pp[1];
52 if (prev->when + prev->duration.seconds < dive->when)
55 merged = try_to_merge(prev, dive);
63 memmove(pp+1, pp+2, sizeof(*pp)*(dive_table.nr - i));
65 /* Redo the new 'i'th dive */
70 static void parse_argument(const char *arg)
72 const char *p = arg+1;
80 fprintf(stderr, "Bad argument '%s'\n", arg);
86 static void on_destroy(GtkWidget* w, gpointer data)
91 static GtkWidget *dive_profile;
93 void update_dive(struct dive *new_dive)
95 static struct dive *buffered_dive;
96 struct dive *old_dive = buffered_dive;
99 flush_dive_info_changes(old_dive);
100 flush_dive_equipment_changes(old_dive);
101 flush_divelist(&dive_list, old_dive);
104 show_dive_info(new_dive);
105 show_dive_equipment(new_dive);
107 buffered_dive = new_dive;
110 void repaint_dive(void)
112 update_dive(current_dive);
113 gtk_widget_queue_draw(dive_profile);
116 static char *existing_filename;
118 static void on_info_bar_response(GtkWidget *widget, gint response,
121 if (response == GTK_RESPONSE_OK)
123 gtk_widget_destroy(widget);
124 error_info_bar = NULL;
128 void report_error(GError* error)
135 if (error_info_bar == NULL)
138 error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
141 g_signal_connect(error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
142 gtk_info_bar_set_message_type(GTK_INFO_BAR(error_info_bar),
145 error_label = gtk_label_new(error->message);
146 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(error_info_bar));
147 gtk_container_add(GTK_CONTAINER(container), error_label);
149 gtk_box_pack_start(GTK_BOX(main_vbox), error_info_bar, FALSE, FALSE, 0);
150 gtk_widget_show_all(main_vbox);
156 snprintf(buffer, sizeof(buffer), "Failed to open %i files.", error_count);
157 gtk_label_set(GTK_LABEL(error_label), buffer);
161 static void file_open(GtkWidget *w, gpointer data)
164 dialog = gtk_file_chooser_dialog_new("Open File",
165 GTK_WINDOW(main_window),
166 GTK_FILE_CHOOSER_ACTION_OPEN,
167 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
168 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
170 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
172 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
175 filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
177 GError *error = NULL;
178 while(filenames != NULL) {
179 filename = (char *)filenames->data;
180 parse_xml_file(filename, &error);
189 filenames = g_slist_next(filenames);
191 g_slist_free(filenames);
193 dive_list_update_dives(dive_list);
195 gtk_widget_destroy(dialog);
198 static void file_save(GtkWidget *w, gpointer data)
201 dialog = gtk_file_chooser_dialog_new("Save File",
202 GTK_WINDOW(main_window),
203 GTK_FILE_CHOOSER_ACTION_SAVE,
204 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
205 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
207 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
208 if (!existing_filename) {
209 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
211 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), existing_filename);
213 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
215 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
216 save_dives(filename);
219 gtk_widget_destroy(dialog);
222 static void quit(GtkWidget *w, gpointer data)
227 static void create_radio(GtkWidget *dialog, const char *name, ...)
230 GtkRadioButton *group = NULL;
231 GtkWidget *box, *label;
233 box = gtk_hbox_new(TRUE, 10);
234 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), box);
236 label = gtk_label_new(name);
237 gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
239 va_start(args, name);
246 name = va_arg(args, char *);
249 callback_fn = va_arg(args, void *);
250 enabled = va_arg(args, int);
252 button = gtk_radio_button_new_with_label_from_widget(group, name);
253 group = GTK_RADIO_BUTTON(button);
254 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
255 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), enabled);
256 g_signal_connect(button, "toggled", G_CALLBACK(callback_fn), NULL);
261 #define UNITCALLBACK(name, type, value) \
262 static void name(GtkWidget *w, gpointer data) \
264 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \
265 menu_units.type = value; \
268 static struct units menu_units;
270 UNITCALLBACK(set_meter, length, METERS)
271 UNITCALLBACK(set_feet, length, FEET)
272 UNITCALLBACK(set_bar, pressure, BAR)
273 UNITCALLBACK(set_psi, pressure, PSI)
274 UNITCALLBACK(set_liter, volume, LITER)
275 UNITCALLBACK(set_cuft, volume, CUFT)
276 UNITCALLBACK(set_celsius, temperature, CELSIUS)
277 UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT)
279 static void unit_dialog(GtkWidget *w, gpointer data)
284 menu_units = output_units;
286 dialog = gtk_dialog_new_with_buttons("Units",
287 GTK_WINDOW(main_window),
288 GTK_DIALOG_DESTROY_WITH_PARENT,
289 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
290 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
293 create_radio(dialog, "Depth:",
294 "Meter", set_meter, (output_units.length == METERS),
295 "Feet", set_feet, (output_units.length == FEET),
298 create_radio(dialog, "Pressure:",
299 "Bar", set_bar, (output_units.pressure == BAR),
300 "PSI", set_psi, (output_units.pressure == PSI),
303 create_radio(dialog, "Volume:",
304 "Liter", set_liter, (output_units.volume == LITER),
305 "CuFt", set_cuft, (output_units.volume == CUFT),
308 create_radio(dialog, "Temperature:",
309 "Celsius", set_celsius, (output_units.temperature == CELSIUS),
310 "Fahrenheit", set_fahrenheit, (output_units.temperature == FAHRENHEIT),
313 gtk_widget_show_all(dialog);
314 result = gtk_dialog_run(GTK_DIALOG(dialog));
315 if (result == GTK_RESPONSE_ACCEPT) {
316 /* Make sure to flush any modified old dive data with old units */
318 output_units = menu_units;
319 update_dive_list_units(&dive_list);
321 gconf_client_set_bool(gconf, GCONF_NAME(feet), output_units.length == FEET, NULL);
322 gconf_client_set_bool(gconf, GCONF_NAME(psi), output_units.pressure == PSI, NULL);
323 gconf_client_set_bool(gconf, GCONF_NAME(cuft), output_units.volume == CUFT, NULL);
324 gconf_client_set_bool(gconf, GCONF_NAME(fahrenheit), output_units.temperature == FAHRENHEIT, NULL);
326 gtk_widget_destroy(dialog);
329 static void renumber_dives(int nr)
333 for (i = 0; i < dive_table.nr; i++) {
334 struct dive *dive = dive_table.dives[i];
335 dive->number = nr + i;
339 static void renumber_dialog(GtkWidget *w, gpointer data)
342 GtkWidget *dialog, *frame, *button;
344 dialog = gtk_dialog_new_with_buttons("Renumber",
345 GTK_WINDOW(main_window),
346 GTK_DIALOG_DESTROY_WITH_PARENT,
347 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
348 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
351 frame = gtk_frame_new("New starting number");
352 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), frame);
354 button = gtk_spin_button_new_with_range(1, 50000, 1);
355 gtk_container_add(GTK_CONTAINER(frame), button);
357 gtk_widget_show_all(dialog);
358 result = gtk_dialog_run(GTK_DIALOG(dialog));
359 if (result == GTK_RESPONSE_ACCEPT) {
360 int nr = gtk_spin_button_get_value(GTK_SPIN_BUTTON(button));
364 gtk_widget_destroy(dialog);
367 static GtkActionEntry menu_items[] = {
368 { "FileMenuAction", GTK_STOCK_FILE, "Log", NULL, NULL, NULL},
369 { "OpenFile", GTK_STOCK_OPEN, NULL, "<control>O", NULL, G_CALLBACK(file_open) },
370 { "SaveFile", GTK_STOCK_SAVE, NULL, "<control>S", NULL, G_CALLBACK(file_save) },
371 { "Print", GTK_STOCK_PRINT, NULL, "<control>P", NULL, G_CALLBACK(do_print) },
372 { "Import", NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) },
373 { "Units", NULL, "Units", NULL, NULL, G_CALLBACK(unit_dialog) },
374 { "Renumber", NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) },
375 { "Quit", GTK_STOCK_QUIT, NULL, "<control>Q", NULL, G_CALLBACK(quit) },
377 static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
379 static const gchar* ui_string = " \
381 <menubar name=\"MainMenu\"> \
382 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
383 <menuitem name=\"Open\" action=\"OpenFile\" /> \
384 <menuitem name=\"Save\" action=\"SaveFile\" /> \
385 <menuitem name=\"Print\" action=\"Print\" /> \
386 <separator name=\"Separator1\"/> \
387 <menuitem name=\"Import\" action=\"Import\" /> \
388 <separator name=\"Separator2\"/> \
389 <menuitem name=\"Units\" action=\"Units\" /> \
390 <menuitem name=\"Renumber\" action=\"Renumber\" /> \
391 <separator name=\"Separator3\"/> \
392 <menuitem name=\"Quit\" action=\"Quit\" /> \
398 static GtkWidget *get_menubar_menu(GtkWidget *window)
400 GtkActionGroup *action_group = gtk_action_group_new("Menu");
401 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
403 GtkUIManager *ui_manager = gtk_ui_manager_new();
404 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
406 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
408 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
409 GtkWidget* menu = gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
414 static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data)
419 int main(int argc, char **argv)
426 GtkWidget *dive_info;
427 GtkWidget *equipment;
431 output_units = SI_units;
434 gtk_init(&argc, &argv);
437 gconf = gconf_client_get_default();
439 if (gconf_client_get_bool(gconf, GCONF_NAME(feet), NULL))
440 output_units.length = FEET;
441 if (gconf_client_get_bool(gconf, GCONF_NAME(psi), NULL))
442 output_units.pressure = PSI;
443 if (gconf_client_get_bool(gconf, GCONF_NAME(cuft), NULL))
444 output_units.volume = CUFT;
445 if (gconf_client_get_bool(gconf, GCONF_NAME(fahrenheit), NULL))
446 output_units.temperature = FAHRENHEIT;
448 error_info_bar = NULL;
449 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
450 g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL);
453 vbox = gtk_vbox_new(FALSE, 0);
454 gtk_container_add(GTK_CONTAINER(win), vbox);
457 menubar = get_menubar_menu(win);
458 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
460 /* HPane for left the dive list, and right the dive info */
461 paned = gtk_vpaned_new();
462 gtk_box_pack_end(GTK_BOX(vbox), paned, TRUE, TRUE, 0);
464 /* Create the actual divelist */
465 dive_list = dive_list_create();
466 gtk_paned_add1(GTK_PANED(paned), dive_list.container_widget);
468 /* VBox for dive info, and tabs */
469 info_box = gtk_vbox_new(FALSE, 6);
470 gtk_paned_add2(GTK_PANED(paned), info_box);
472 /* Notebook for dive info vs profile vs .. */
473 notebook = gtk_notebook_new();
474 g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL);
475 gtk_box_pack_start(GTK_BOX(info_box), notebook, TRUE, TRUE, 6);
477 /* Frame for dive profile */
478 dive_profile = dive_profile_widget();
479 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_profile, gtk_label_new("Dive Profile"));
481 /* Frame for extended dive info */
482 dive_info = extended_dive_info_widget();
483 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_info, gtk_label_new("Dive Notes"));
485 /* Frame for dive equipment */
486 equipment = equipment_widget();
487 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment"));
489 gtk_widget_set_app_paintable(win, TRUE);
490 gtk_widget_show_all(win);
492 for (i = 1; i < argc; i++) {
493 const char *a = argv[i];
499 GError *error = NULL;
500 parse_xml_file(a, &error);
511 dive_list_update_dives(dive_list);