X-Git-Url: http://git.tdb.fi/?p=ext%2Fsubsurface.git;a=blobdiff_plain;f=gtk-gui.c;h=7db777ab92e4254fdb14644ad2ac2c9348387dc4;hp=47cab62fabc46f0d060d17e821434ce41d4bc211;hb=HEAD;hpb=8c0c6bad59b588de4fe1e8983d76ded16538fc44 diff --git a/gtk-gui.c b/gtk-gui.c index 47cab62..7db777a 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -7,8 +7,7 @@ #include #include #include - -#include +#include #include "dive.h" #include "divelist.h" @@ -21,20 +20,58 @@ GtkWidget *main_window; GtkWidget *main_vbox; GtkWidget *error_info_bar; GtkWidget *error_label; +GtkWidget *vpane, *hpane; +GtkWidget *notebook; + int error_count; -#define DIVELIST_DEFAULT_FONT "Sans 8" const char *divelist_font; -GConfClient *gconf; struct units output_units; -#define GCONF_NAME(x) "/apps/subsurface/" #x - static GtkWidget *dive_profile; visible_cols_t visible_cols = {TRUE, FALSE}; +static const char *default_dive_computer_vendor; +static const char *default_dive_computer_product; +static const char *default_dive_computer_device; + +static int is_default_dive_computer(const char *vendor, const char *product) +{ + return default_dive_computer_vendor && !strcmp(vendor, default_dive_computer_vendor) && + default_dive_computer_product && !strcmp(product, default_dive_computer_product); +} + +static int is_default_dive_computer_device(const char *name) +{ + return default_dive_computer_device && !strcmp(name, default_dive_computer_device); +} + +static void set_default_dive_computer(const char *vendor, const char *product) +{ + if (!vendor || !*vendor) + return; + if (!product || !*product) + return; + if (is_default_dive_computer(vendor, product)) + return; + default_dive_computer_vendor = vendor; + default_dive_computer_product = product; + subsurface_set_conf("dive_computer_vendor", PREF_STRING, vendor); + subsurface_set_conf("dive_computer_product", PREF_STRING, product); +} + +static void set_default_dive_computer_device(const char *name) +{ + if (!name || !*name) + return; + if (is_default_dive_computer_device(name)) + return; + default_dive_computer_device = name; + subsurface_set_conf("dive_computer_device", PREF_STRING, name); +} + void repaint_dive(void) { update_dive(current_dive); @@ -61,7 +98,7 @@ void report_error(GError* error) { return; } - + if (error_info_bar == NULL) { error_count = 1; @@ -71,11 +108,11 @@ void report_error(GError* error) g_signal_connect(error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL); gtk_info_bar_set_message_type(GTK_INFO_BAR(error_info_bar), GTK_MESSAGE_ERROR); - + error_label = gtk_label_new(error->message); GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(error_info_bar)); gtk_container_add(GTK_CONTAINER(container), error_label); - + gtk_box_pack_start(GTK_BOX(main_vbox), error_info_bar, FALSE, FALSE, 0); gtk_widget_show_all(main_vbox); } @@ -111,72 +148,138 @@ static void file_open(GtkWidget *w, gpointer data) gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - GSList *filenames; + GSList *filenames, *fn_glist; char *filename; - filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); - + filenames = fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + GError *error = NULL; while(filenames != NULL) { filename = filenames->data; - parse_xml_file(filename, &error); + parse_file(filename, &error); if (error != NULL) { report_error(error); g_error_free(error); error = NULL; } - + g_free(filename); filenames = g_slist_next(filenames); } - g_slist_free(filenames); + g_slist_free(fn_glist); report_dives(FALSE); } gtk_widget_destroy(dialog); } -static void file_save(GtkWidget *w, gpointer data) +/* return the path and the file component contained in the full path */ +static char *path_and_file(char *pathin, char **fileout) { + char *slash = pathin, *next; + char *result; + size_t len, n; + + if (! pathin) { + *fileout = strdup(""); + return strdup(""); + } + while ((next = strpbrk(slash + 1, "\\/"))) + slash = next; + if (pathin != slash) + slash++; + *fileout = strdup(slash); + + /* strndup(pathin, slash - pathin) */ + n = slash - pathin; + len = strlen(pathin); + if (n < len) + len = n; + + result = (char *)malloc(len + 1); + if (!result) + return 0; + + result[len] = '\0'; + return (char *)memcpy(result, pathin, len); +} + +static void file_save_as(GtkWidget *w, gpointer data) { GtkWidget *dialog; - dialog = gtk_file_chooser_dialog_new("Save File", + char *filename = NULL; + char *current_file; + char *current_dir; + + dialog = gtk_file_chooser_dialog_new("Save File As", GTK_WINDOW(main_window), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); - if (!existing_filename) { - gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document"); - } else - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), existing_filename); + + current_dir = path_and_file(existing_filename, ¤t_file); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), current_dir); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), current_file); + + free(current_dir); + free(current_file); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - char *filename; filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + } + gtk_widget_destroy(dialog); + + if (filename){ save_dives(filename); + set_filename(filename); g_free(filename); mark_divelist_changed(FALSE); } - gtk_widget_destroy(dialog); } -static void ask_save_changes() +static void file_save(GtkWidget *w, gpointer data) +{ + if (!existing_filename) + return file_save_as(w, data); + + save_dives(existing_filename); + mark_divelist_changed(FALSE); +} + +static gboolean ask_save_changes() { GtkWidget *dialog, *label, *content; + gboolean quit = TRUE; dialog = gtk_dialog_new_with_buttons("Save Changes?", GTK_WINDOW(main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + GTK_STOCK_NO, GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); - label = gtk_label_new ("You have unsaved changes\nWould you like to save those before exiting the program?"); + + if (!existing_filename){ + label = gtk_label_new ( + "You have unsaved changes\nWould you like to save those before exiting the program?"); + } else { + char *label_text = (char*) malloc(sizeof(char) * (93 + strlen(existing_filename))); + sprintf(label_text, + "You have unsaved changes to file: %s \nWould you like to save those before exiting the program?", + existing_filename); + label = gtk_label_new (label_text); + g_free(label_text); + } gtk_container_add (GTK_CONTAINER (content), label); gtk_widget_show_all (dialog); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + gint outcode = gtk_dialog_run(GTK_DIALOG(dialog)); + if (outcode == GTK_RESPONSE_ACCEPT) { file_save(NULL,NULL); + } else if (outcode == GTK_RESPONSE_CANCEL) { + quit = FALSE; } gtk_widget_destroy(dialog); + return quit; } static gboolean on_delete(GtkWidget* w, gpointer data) @@ -184,10 +287,15 @@ static gboolean on_delete(GtkWidget* w, gpointer data) /* Make sure to flush any modified dive data */ update_dive(NULL); + gboolean quit = TRUE; if (unsaved_changes()) - ask_save_changes(); + quit = ask_save_changes(); - return FALSE; /* go ahead, kill the program, we're good now */ + if (quit){ + return FALSE; /* go ahead, kill the program, we're good now */ + } else { + return TRUE; /* We are not leaving */ + } } static void on_destroy(GtkWidget* w, gpointer data) @@ -200,23 +308,35 @@ static void quit(GtkWidget *w, gpointer data) /* Make sure to flush any modified dive data */ update_dive(NULL); + gboolean quit = TRUE; if (unsaved_changes()) - ask_save_changes(); - gtk_main_quit(); + quit = ask_save_changes(); + + if (quit){ + gtk_main_quit(); + } } GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, - data_func_t data_func, PangoAlignment align, gboolean visible) + data_func_t data_func, unsigned int flags) { GtkCellRenderer *renderer; GtkTreeViewColumn *col; double xalign = 0.0; /* left as default */ + PangoAlignment align; + gboolean visible; + + align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT : + (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT : + PANGO_ALIGN_CENTER; + visible = !(flags & INVISIBLE); renderer = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, title); - gtk_tree_view_column_set_sort_column_id(col, index); + if (!(flags & UNSORTABLE)) + gtk_tree_view_column_set_sort_column_id(col, index); gtk_tree_view_column_set_resizable(col, TRUE); gtk_tree_view_column_pack_start(col, renderer, TRUE); if (data_func) @@ -241,7 +361,7 @@ GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char return col; } -static void create_radio(GtkWidget *vbox, const char *name, ...) +static void create_radio(GtkWidget *vbox, const char *w_name, ...) { va_list args; GtkRadioButton *group = NULL; @@ -250,10 +370,10 @@ static void create_radio(GtkWidget *vbox, const char *name, ...) box = gtk_hbox_new(TRUE, 10); gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 0); - label = gtk_label_new(name); + label = gtk_label_new(w_name); gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0); - va_start(args, name); + va_start(args, w_name); for (;;) { int enabled; const char *name; @@ -292,6 +412,8 @@ UNITCALLBACK(set_liter, volume, LITER) UNITCALLBACK(set_cuft, volume, CUFT) UNITCALLBACK(set_celsius, temperature, CELSIUS) UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT) +UNITCALLBACK(set_kg, weight, KG) +UNITCALLBACK(set_lbs, weight, LBS) #define OPTIONCALLBACK(name, option) \ static void name(GtkWidget *w, gpointer data) \ @@ -303,7 +425,17 @@ OPTIONCALLBACK(otu_toggle, visible_cols.otu) OPTIONCALLBACK(sac_toggle, visible_cols.sac) OPTIONCALLBACK(nitrox_toggle, visible_cols.nitrox) OPTIONCALLBACK(temperature_toggle, visible_cols.temperature) +OPTIONCALLBACK(totalweight_toggle, visible_cols.totalweight) +OPTIONCALLBACK(suit_toggle, visible_cols.suit) OPTIONCALLBACK(cylinder_toggle, visible_cols.cylinder) +OPTIONCALLBACK(autogroup_toggle, autogroup) + +static void event_toggle(GtkWidget *w, gpointer _data) +{ + gboolean *plot_ev = _data; + + *plot_ev = GTK_TOGGLE_BUTTON(w)->active; +} static void preferences_dialog(GtkWidget *w, gpointer data) { @@ -346,39 +478,68 @@ static void preferences_dialog(GtkWidget *w, gpointer data) "Fahrenheit", set_fahrenheit, (output_units.temperature == FAHRENHEIT), NULL); - frame = gtk_frame_new("Columns"); + create_radio(box, "Weight:", + "kg", set_kg, (output_units.weight == KG), + "lbs", set_lbs, (output_units.weight == LBS), + NULL); + + frame = gtk_frame_new("Show Columns"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); box = gtk_hbox_new(FALSE, 6); gtk_container_add(GTK_CONTAINER(frame), box); - button = gtk_check_button_new_with_label("Show Temp"); + button = gtk_check_button_new_with_label("Temp"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.temperature); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(temperature_toggle), NULL); - button = gtk_check_button_new_with_label("Show Cyl"); + button = gtk_check_button_new_with_label("Cyl"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.cylinder); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(cylinder_toggle), NULL); - button = gtk_check_button_new_with_label("Show O" UTF8_SUBSCRIPT_2 "%"); + button = gtk_check_button_new_with_label("O" UTF8_SUBSCRIPT_2 "%"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.nitrox); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(nitrox_toggle), NULL); - button = gtk_check_button_new_with_label("Show SAC"); + button = gtk_check_button_new_with_label("SAC"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.sac); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(sac_toggle), NULL); - button = gtk_check_button_new_with_label("Show OTU"); + button = gtk_check_button_new_with_label("OTU"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.otu); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL); + button = gtk_check_button_new_with_label("Weight"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.totalweight); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(totalweight_toggle), NULL); + + button = gtk_check_button_new_with_label("Suit"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.suit); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL); + + frame = gtk_frame_new("Divelist Font"); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); + font = gtk_font_button_new_with_font(divelist_font); - gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5); + gtk_container_add(GTK_CONTAINER(frame),font); + + frame = gtk_frame_new("Misc. Options"); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), box); + + button = gtk_check_button_new_with_label("Automatically group dives in trips"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autogroup); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(autogroup_toggle), NULL); gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); @@ -393,16 +554,82 @@ static void preferences_dialog(GtkWidget *w, gpointer data) update_dive_list_units(); repaint_dive(); update_dive_list_col_visibility(); - gconf_client_set_bool(gconf, GCONF_NAME(feet), output_units.length == FEET, NULL); - gconf_client_set_bool(gconf, GCONF_NAME(psi), output_units.pressure == PSI, NULL); - gconf_client_set_bool(gconf, GCONF_NAME(cuft), output_units.volume == CUFT, NULL); - gconf_client_set_bool(gconf, GCONF_NAME(fahrenheit), output_units.temperature == FAHRENHEIT, NULL); - gconf_client_set_bool(gconf, GCONF_NAME(TEMPERATURE), visible_cols.temperature, NULL); - gconf_client_set_bool(gconf, GCONF_NAME(CYLINDER), visible_cols.cylinder, NULL); - gconf_client_set_bool(gconf, GCONF_NAME(NITROX), visible_cols.nitrox, NULL); - gconf_client_set_bool(gconf, GCONF_NAME(SAC), visible_cols.sac, NULL); - gconf_client_set_bool(gconf, GCONF_NAME(OTU), visible_cols.otu, NULL); - gconf_client_set_string(gconf, GCONF_NAME(divelist_font), divelist_font, NULL); + + subsurface_set_conf("feet", PREF_BOOL, BOOL_TO_PTR(output_units.length == FEET)); + subsurface_set_conf("psi", PREF_BOOL, BOOL_TO_PTR(output_units.pressure == PSI)); + subsurface_set_conf("cuft", PREF_BOOL, BOOL_TO_PTR(output_units.volume == CUFT)); + subsurface_set_conf("fahrenheit", PREF_BOOL, BOOL_TO_PTR(output_units.temperature == FAHRENHEIT)); + subsurface_set_conf("lbs", PREF_BOOL, BOOL_TO_PTR(output_units.weight == LBS)); + subsurface_set_conf("TEMPERATURE", PREF_BOOL, BOOL_TO_PTR(visible_cols.temperature)); + subsurface_set_conf("TOTALWEIGHT", PREF_BOOL, BOOL_TO_PTR(visible_cols.totalweight)); + subsurface_set_conf("SUIT", PREF_BOOL, BOOL_TO_PTR(visible_cols.suit)); + subsurface_set_conf("CYLINDER", PREF_BOOL, BOOL_TO_PTR(visible_cols.cylinder)); + subsurface_set_conf("NITROX", PREF_BOOL, BOOL_TO_PTR(visible_cols.nitrox)); + subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(visible_cols.sac)); + subsurface_set_conf("OTU", PREF_BOOL, BOOL_TO_PTR(visible_cols.otu)); + subsurface_set_conf("divelist_font", PREF_STRING, divelist_font); + subsurface_set_conf("autogroup", PREF_BOOL, BOOL_TO_PTR(autogroup)); + + /* Flush the changes out to the system */ + subsurface_flush_conf(); + } + gtk_widget_destroy(dialog); +} + +static void create_toggle(const char* label, int *on, void *_data) +{ + GtkWidget *button, *table = _data; + int rows, cols, x, y; + static int count; + + if (table == NULL) { + /* magic way to reset the number of toggle buttons + * that we have already added - call this before you + * create the dialog */ + count = 0; + return; + } + g_object_get(G_OBJECT(table), "n-columns", &cols, "n-rows", &rows, NULL); + if (count > rows * cols) { + gtk_table_resize(GTK_TABLE(table),rows+1,cols); + rows++; + } + x = count % cols; + y = count / cols; + button = gtk_check_button_new_with_label(label); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *on); + gtk_table_attach_defaults(GTK_TABLE(table), button, x, x+1, y, y+1); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(event_toggle), on); + count++; +} + +static void selectevents_dialog(GtkWidget *w, gpointer data) +{ + int result; + GtkWidget *dialog, *frame, *vbox, *table; + + dialog = gtk_dialog_new_with_buttons("SelectEvents", + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + /* initialize the function that fills the table */ + create_toggle(NULL, NULL, NULL); + + frame = gtk_frame_new("Enable / Disable Events"); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + table = gtk_table_new(1, 4, TRUE); + gtk_container_add(GTK_CONTAINER(frame), table); + + evn_foreach(&create_toggle, table); + + gtk_widget_show_all(dialog); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + if (result == GTK_RESPONSE_ACCEPT) { + repaint_dive(); } gtk_widget_destroy(dialog); } @@ -452,7 +679,7 @@ static void about_dialog(GtkWidget *w, gpointer data) GdkPixbuf *logo = NULL; if (need_icon) { - GtkWidget *image = gtk_image_new_from_file("subsurface.svg"); + GtkWidget *image = gtk_image_new_from_file(subsurface_icon_name()); if (image) { logo = gtk_image_get_pixbuf(GTK_IMAGE(image)); @@ -472,18 +699,65 @@ static void about_dialog(GtkWidget *w, gpointer data) NULL); } +static void view_list(GtkWidget *w, gpointer data) +{ + gtk_paned_set_position(GTK_PANED(vpane), 0); +} + +static void view_profile(GtkWidget *w, gpointer data) +{ + gtk_paned_set_position(GTK_PANED(hpane), 0); + gtk_paned_set_position(GTK_PANED(vpane), 65535); +} + +static void view_info(GtkWidget *w, gpointer data) +{ + gtk_paned_set_position(GTK_PANED(vpane), 65535); + gtk_paned_set_position(GTK_PANED(hpane), 65535); +} + +static void view_three(GtkWidget *w, gpointer data) +{ + GtkAllocation alloc; + GtkRequisition requisition; + + gtk_widget_get_allocation(hpane, &alloc); + gtk_paned_set_position(GTK_PANED(hpane), alloc.width/2); + gtk_widget_get_allocation(vpane, &alloc); + gtk_widget_size_request(notebook, &requisition); + /* pick the requested size for the notebook plus 6 pixels for frame */ + gtk_paned_set_position(GTK_PANED(vpane), requisition.height + 6); +} + +static void toggle_zoom(GtkWidget *w, gpointer data) +{ + zoomed_plot = (zoomed_plot)?0 : 1; + /*Update dive*/ + repaint_dive(); +} + static GtkActionEntry menu_items[] = { - { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, - { "LogMenuAction", GTK_STOCK_FILE, "Log", NULL, NULL, NULL}, - { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL}, - { "OpenFile", GTK_STOCK_OPEN, NULL, "O", NULL, G_CALLBACK(file_open) }, - { "SaveFile", GTK_STOCK_SAVE, NULL, "S", NULL, G_CALLBACK(file_save) }, - { "Print", GTK_STOCK_PRINT, NULL, "P", NULL, G_CALLBACK(do_print) }, + { "FileMenuAction", NULL, "File", NULL, NULL, NULL}, + { "LogMenuAction", NULL, "Log", NULL, NULL, NULL}, + { "ViewMenuAction", NULL, "View", NULL, NULL, NULL}, + { "FilterMenuAction", NULL, "Filter", NULL, NULL, NULL}, + { "HelpMenuAction", NULL, "Help", NULL, NULL, NULL}, + { "OpenFile", GTK_STOCK_OPEN, NULL, CTRLCHAR "O", NULL, G_CALLBACK(file_open) }, + { "SaveFile", GTK_STOCK_SAVE, NULL, CTRLCHAR "S", NULL, G_CALLBACK(file_save) }, + { "SaveAsFile", GTK_STOCK_SAVE_AS, NULL, SHIFTCHAR CTRLCHAR "S", NULL, G_CALLBACK(file_save_as) }, + { "Print", GTK_STOCK_PRINT, NULL, CTRLCHAR "P", NULL, G_CALLBACK(do_print) }, { "Import", NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) }, - { "Preferences", NULL, "Preferences", NULL, NULL, G_CALLBACK(preferences_dialog) }, + { "AddDive", GTK_STOCK_ADD, "Add Dive", NULL, NULL, G_CALLBACK(add_dive_cb) }, + { "Preferences", GTK_STOCK_PREFERENCES, "Preferences", PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) }, { "Renumber", NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) }, - { "Quit", GTK_STOCK_QUIT, NULL, "Q", NULL, G_CALLBACK(quit) }, - { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, + { "SelectEvents", NULL, "SelectEvents", NULL, NULL, G_CALLBACK(selectevents_dialog) }, + { "Quit", GTK_STOCK_QUIT, NULL, CTRLCHAR "Q", NULL, G_CALLBACK(quit) }, + { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, + { "ViewList", NULL, "List", CTRLCHAR "1", NULL, G_CALLBACK(view_list) }, + { "ViewProfile", NULL, "Profile", CTRLCHAR "2", NULL, G_CALLBACK(view_profile) }, + { "ViewInfo", NULL, "Info", CTRLCHAR "3", NULL, G_CALLBACK(view_info) }, + { "ViewThree", NULL, "Three", CTRLCHAR "4", NULL, G_CALLBACK(view_three) }, + { "ToggleZoom", NULL, "Toggle Zoom", CTRLCHAR "0", NULL, G_CALLBACK(toggle_zoom) }, }; static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); @@ -493,16 +767,28 @@ static const gchar* ui_string = " \ \ \ \ + \ \ \ - \ - \ \ - \ + \ \ \ \ + \ + \ + \ \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ \ \ \ @@ -511,12 +797,11 @@ static const gchar* ui_string = " \ \ "; -static GtkWidget *get_menubar_menu(GtkWidget *window) +static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager) { GtkActionGroup *action_group = gtk_action_group_new("Menu"); gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0); - GtkUIManager *ui_manager = gtk_ui_manager_new(); gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); GError* error = 0; gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error); @@ -532,130 +817,52 @@ static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data) repaint_dive(); } -static const char notebook_group[] = "123"; -#define GRP_ID ((void *)notebook_group) -typedef struct { - char *name; - GtkWidget *widget; - GtkWidget *box; - gulong delete_handler; - gulong destroy_handler; -} notebook_data_t; - -static notebook_data_t nbd[2]; /* we rip at most two notebook pages off */ - -static GtkNotebook *create_new_notebook_window(GtkNotebook *source, - GtkWidget *page, - gint x, gint y, gpointer data) -{ - GtkWidget *win, *notebook, *vbox; - notebook_data_t *nbdp; - GtkAllocation allocation; - - /* pick the right notebook page data and return if both are detached */ - if (nbd[0].widget == NULL) - nbdp = nbd; - else if (nbd[1].widget == NULL) - nbdp = nbd + 1; - else - return NULL; - - nbdp->name = strdup(gtk_widget_get_name(page)); - nbdp->widget = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(win), nbdp->name); - gtk_window_move(GTK_WINDOW(win), x, y); - - /* Destroying the dive list will kill the application */ - nbdp->delete_handler = g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL); - nbdp->destroy_handler = g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL); - - nbdp->box = vbox = gtk_vbox_new(FALSE, 0); - gtk_container_add(GTK_CONTAINER(win), vbox); - - notebook = gtk_notebook_new(); - gtk_notebook_set_group(GTK_NOTEBOOK(notebook), GRP_ID); - gtk_widget_set_name(notebook, nbdp->name); - /* disallow drop events */ - gtk_drag_dest_set(notebook, 0, NULL, 0, 0); - gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 6); - gtk_widget_get_allocation(page, &allocation); - gtk_window_set_default_size(GTK_WINDOW(win), allocation.width, allocation.height); - - gtk_widget_show_all(win); - return GTK_NOTEBOOK(notebook); -} - -static void drag_cb(GtkWidget *widget, GdkDragContext *context, - gint x, gint y, guint time, - gpointer user_data) -{ - GtkWidget *source; - notebook_data_t *nbdp; - - source = gtk_drag_get_source_widget(context); - if (nbd[0].name && ! strcmp(nbd[0].name,gtk_widget_get_name(source))) - nbdp = nbd; - else if (nbd[1].name && ! strcmp(nbd[1].name,gtk_widget_get_name(source))) - nbdp = nbd + 1; - else - /* HU? */ - return; - - gtk_drag_finish(context, TRUE, TRUE, time); - - /* we no longer need the widget - but getting rid of this is hard; - * remove the signal handler, remove the notebook from the box - * then destroy the widget (and clear out our data structure) */ - g_signal_handler_disconnect(nbdp->widget,nbdp->delete_handler); - g_signal_handler_disconnect(nbdp->widget,nbdp->destroy_handler); - gtk_container_remove(GTK_CONTAINER(nbdp->box), source); - gtk_widget_destroy(nbdp->widget); - nbdp->widget = NULL; - free(nbdp->name); - nbdp->name = NULL; -} - -void init_ui(int argc, char **argv) +void init_ui(int *argcp, char ***argvp) { GtkWidget *win; - GtkWidget *notebook; - GtkWidget *dive_info; + GtkWidget *nb_page; GtkWidget *dive_list; - GtkWidget *equipment; GtkWidget *menubar; GtkWidget *vbox; + GtkWidget *scrolled; GdkScreen *screen; GtkIconTheme *icon_theme=NULL; GtkSettings *settings; - static const GtkTargetEntry notebook_target = { - "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 - }; + GtkUIManager *ui_manager; - gtk_init(&argc, &argv); + gtk_init(argcp, argvp); settings = gtk_settings_get_default(); gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "subsurface setting"); g_type_init(); - gconf = gconf_client_get_default(); - if (gconf_client_get_bool(gconf, GCONF_NAME(feet), NULL)) + subsurface_open_conf(); + if (subsurface_get_conf("feet", PREF_BOOL)) output_units.length = FEET; - if (gconf_client_get_bool(gconf, GCONF_NAME(psi), NULL)) + if (subsurface_get_conf("psi", PREF_BOOL)) output_units.pressure = PSI; - if (gconf_client_get_bool(gconf, GCONF_NAME(cuft), NULL)) + if (subsurface_get_conf("cuft", PREF_BOOL)) output_units.volume = CUFT; - if (gconf_client_get_bool(gconf, GCONF_NAME(fahrenheit), NULL)) + if (subsurface_get_conf("fahrenheit", PREF_BOOL)) output_units.temperature = FAHRENHEIT; + if (subsurface_get_conf("lbs", PREF_BOOL)) + output_units.weight = LBS; /* an unset key is FALSE - all these are hidden by default */ - visible_cols.cylinder = gconf_client_get_bool(gconf, GCONF_NAME(CYLINDER), NULL); - visible_cols.temperature = gconf_client_get_bool(gconf, GCONF_NAME(TEMPERATURE), NULL); - visible_cols.nitrox = gconf_client_get_bool(gconf, GCONF_NAME(NITROX), NULL); - visible_cols.otu = gconf_client_get_bool(gconf, GCONF_NAME(OTU), NULL); - visible_cols.sac = gconf_client_get_bool(gconf, GCONF_NAME(SAC), NULL); - - divelist_font = gconf_client_get_string(gconf, GCONF_NAME(divelist_font), NULL); - if (!divelist_font) - divelist_font = DIVELIST_DEFAULT_FONT; + visible_cols.cylinder = PTR_TO_BOOL(subsurface_get_conf("CYLINDER", PREF_BOOL)); + visible_cols.temperature = PTR_TO_BOOL(subsurface_get_conf("TEMPERATURE", PREF_BOOL)); + visible_cols.totalweight = PTR_TO_BOOL(subsurface_get_conf("TOTALWEIGHT", PREF_BOOL)); + visible_cols.suit = PTR_TO_BOOL(subsurface_get_conf("SUIT", PREF_BOOL)); + visible_cols.nitrox = PTR_TO_BOOL(subsurface_get_conf("NITROX", PREF_BOOL)); + visible_cols.otu = PTR_TO_BOOL(subsurface_get_conf("OTU", PREF_BOOL)); + visible_cols.sac = PTR_TO_BOOL(subsurface_get_conf("SAC", PREF_BOOL)); + + divelist_font = subsurface_get_conf("divelist_font", PREF_STRING); + + autogroup = PTR_TO_BOOL(subsurface_get_conf("autogroup", PREF_BOOL)); + + default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor", PREF_STRING); + default_dive_computer_product = subsurface_get_conf("dive_computer_product", PREF_STRING); + default_dive_computer_device = subsurface_get_conf("dive_computer_device", PREF_STRING); error_info_bar = NULL; win = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -671,8 +878,11 @@ void init_ui(int argc, char **argv) gtk_window_set_default_icon_name ("subsurface"); } } - if (need_icon) - gtk_window_set_icon_from_file(GTK_WINDOW(win), "subsurface.svg", NULL); + if (need_icon) { + const char *icon_name = subsurface_icon_name(); + if (!access(icon_name, R_OK)) + gtk_window_set_icon_from_file(GTK_WINDOW(win), icon_name, NULL); + } g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL); g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL); main_window = win; @@ -681,39 +891,50 @@ void init_ui(int argc, char **argv) gtk_container_add(GTK_CONTAINER(win), vbox); main_vbox = vbox; - menubar = get_menubar_menu(win); - gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); + ui_manager = gtk_ui_manager_new(); + menubar = get_menubar_menu(win, ui_manager); + + subsurface_ui_setup(settings, menubar, vbox, ui_manager); + + vpane = gtk_vpaned_new(); + gtk_box_pack_start(GTK_BOX(vbox), vpane, TRUE, TRUE, 3); + hpane = gtk_hpaned_new(); + gtk_paned_add1(GTK_PANED(vpane), hpane); + g_signal_connect_after(G_OBJECT(vbox), "realize", G_CALLBACK(view_three), NULL); /* Notebook for dive info vs profile vs .. */ notebook = gtk_notebook_new(); - gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 6); - gtk_notebook_set_group(GTK_NOTEBOOK(notebook), GRP_ID); - g_signal_connect(notebook, "create-window", G_CALLBACK(create_new_notebook_window), NULL); - gtk_drag_dest_set(notebook, GTK_DEST_DEFAULT_ALL, ¬ebook_target, 1, GDK_ACTION_MOVE); - g_signal_connect(notebook, "drag-drop", G_CALLBACK(drag_cb), notebook); + scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_paned_add1(GTK_PANED(hpane), scrolled); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), notebook); g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL); /* Create the actual divelist */ dive_list = dive_list_create(); gtk_widget_set_name(dive_list, "Dive List"); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_list, gtk_label_new("Dive List")); - gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(notebook), dive_list, 1); - gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(notebook), dive_list, 1); + gtk_paned_add2(GTK_PANED(vpane), dive_list); /* Frame for dive profile */ dive_profile = dive_profile_widget(); gtk_widget_set_name(dive_profile, "Dive Profile"); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_profile, gtk_label_new("Dive Profile")); - gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(notebook), dive_profile, 1); - gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(notebook), dive_profile, 1); + gtk_paned_add2(GTK_PANED(hpane), dive_profile); /* Frame for extended dive info */ - dive_info = extended_dive_info_widget(); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_info, gtk_label_new("Dive Notes")); + nb_page = extended_dive_info_widget(); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Notes")); /* Frame for dive equipment */ - equipment = equipment_widget(); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment")); + nb_page = equipment_widget(W_IDX_PRIMARY); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Equipment")); + + /* Frame for single dive statistics */ + nb_page = single_stats_widget(); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Info")); + + /* Frame for total dive statistics */ + nb_page = total_stats_widget(); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Stats")); gtk_widget_set_app_paintable(win, TRUE); gtk_widget_show_all(win); @@ -726,6 +947,11 @@ void run_ui(void) gtk_main(); } +void exit_ui(void) +{ + subsurface_close_conf(); +} + typedef struct { cairo_rectangle_int_t rect; const char *text; @@ -784,7 +1010,7 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer gc.cr = gdk_cairo_create(widget->window); g_object_set(widget, "has-tooltip", TRUE, NULL); g_signal_connect(widget, "query-tooltip", G_CALLBACK(profile_tooltip), &drawing_area); - set_source_rgb(&gc, 0, 0, 0); + init_profile_background(&gc); cairo_paint(gc.cr); if (dive) { @@ -821,35 +1047,64 @@ int process_ui_events(void) break; } } - return(ret); + return ret; } - -static void fill_computer_list(GtkListStore *store) +static int fill_computer_list(GtkListStore *store) { + int index = -1, i; GtkTreeIter iter; - struct device_list *list = device_list; + dc_iterator_t *iterator = NULL; + dc_descriptor_t *descriptor = NULL; + + i = 0; + dc_descriptor_iterator(&iterator); + while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) { + const char *vendor = dc_descriptor_get_vendor(descriptor); + const char *product = dc_descriptor_get_product(descriptor); - for (list = device_list ; list->name ; list++) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, - 0, list->name, - 1, list->type, + 0, descriptor, -1); + if (is_default_dive_computer(vendor, product)) + index = i; + i++; } + dc_iterator_free(iterator); + return index; } +void render_dive_computer(GtkCellLayout *cell, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + char buffer[40]; + dc_descriptor_t *descriptor = NULL; + const char *vendor, *product; + + gtk_tree_model_get(model, iter, 0, &descriptor, -1); + vendor = dc_descriptor_get_vendor(descriptor); + product = dc_descriptor_get_product(descriptor); + snprintf(buffer, sizeof(buffer), "%s %s", vendor, product); + g_object_set(renderer, "text", buffer, NULL); +} + + static GtkComboBox *dive_computer_selector(GtkWidget *vbox) { GtkWidget *hbox, *combo_box, *frame; GtkListStore *model; GtkCellRenderer *renderer; + int default_index; hbox = gtk_hbox_new(FALSE, 6); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); - model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); - fill_computer_list(model); + model = gtk_list_store_new(1, G_TYPE_POINTER); + default_index = fill_computer_list(model); frame = gtk_frame_new("Dive computer"); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 3); @@ -859,11 +1114,21 @@ static GtkComboBox *dive_computer_selector(GtkWidget *vbox) renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL); + gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo_box), renderer, render_dive_computer, NULL, NULL); + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), default_index); return GTK_COMBO_BOX(combo_box); } +const char *subsurface_device_name() +{ + if (!default_dive_computer_device || !*default_dive_computer_device) + return subsurface_USB_name(); + else + return default_dive_computer_device; +} + static GtkEntry *dive_computer_device(GtkWidget *vbox) { GtkWidget *hbox, *entry, *frame; @@ -876,7 +1141,7 @@ static GtkEntry *dive_computer_device(GtkWidget *vbox) entry = gtk_entry_new(); gtk_container_add(GTK_CONTAINER(frame), entry); - gtk_entry_set_text(GTK_ENTRY(entry), "/dev/ttyUSB0"); + gtk_entry_set_text(GTK_ENTRY(entry), subsurface_device_name()); return GTK_ENTRY(entry); } @@ -928,7 +1193,7 @@ static GtkWidget *xml_file_selector(GtkWidget *vbox, GtkWidget *main_dialog) static void do_import_file(gpointer data, gpointer user_data) { GError *error = NULL; - parse_xml_file(data, &error); + parse_file(data, &error); if (error != NULL) { @@ -938,10 +1203,33 @@ static void do_import_file(gpointer data, gpointer user_data) } } +static GtkWidget *import_dive_computer(device_data_t *data, GtkDialog *dialog) +{ + GError *error; + GtkWidget *vbox, *info, *container, *label, *button; + + error = do_import(data); + if (!error) + return NULL; + + button = gtk_dialog_get_widget_for_response(dialog, GTK_RESPONSE_ACCEPT); + gtk_button_set_use_stock(GTK_BUTTON(button), 0); + gtk_button_set_label(GTK_BUTTON(button), "Retry"); + + vbox = gtk_dialog_get_content_area(dialog); + + info = gtk_info_bar_new(); + container = gtk_info_bar_get_content_area(GTK_INFO_BAR(info)); + label = gtk_label_new(error->message); + gtk_container_add(GTK_CONTAINER(container), label); + gtk_box_pack_start(GTK_BOX(vbox), info, FALSE, FALSE, 0); + return info; +} + void import_dialog(GtkWidget *w, gpointer data) { int result; - GtkWidget *dialog, *hbox, *vbox, *label; + GtkWidget *dialog, *hbox, *vbox, *label, *info = NULL; GtkComboBox *computer; GtkEntry *device; GtkWidget *XMLchooser; @@ -967,30 +1255,42 @@ void import_dialog(GtkWidget *w, gpointer data) devicedata.progress.bar = gtk_progress_bar_new(); gtk_container_add(GTK_CONTAINER(hbox), devicedata.progress.bar); +repeat: gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { - int type; + dc_descriptor_t *descriptor; GtkTreeIter iter; GtkTreeModel *model; - const char *comp; GSList *list; case GTK_RESPONSE_ACCEPT: /* what happened - did the user pick a file? In that case * we ignore whether a dive computer model was picked */ + if (info) + gtk_widget_destroy(info); list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(XMLchooser)); if (g_slist_length(list) == 0) { + const char *vendor, *product; + if (!gtk_combo_box_get_active_iter(computer, &iter)) break; model = gtk_combo_box_get_model(computer); gtk_tree_model_get(model, &iter, - 0, &comp, - 1, &type, + 0, &descriptor, -1); - devicedata.type = type; - devicedata.name = comp; + + vendor = dc_descriptor_get_vendor(descriptor); + product = dc_descriptor_get_product(descriptor); + + devicedata.descriptor = descriptor; + devicedata.vendor = vendor; + devicedata.product = product; devicedata.devname = gtk_entry_get_text(device); - do_import(&devicedata); + set_default_dive_computer(vendor, product); + set_default_dive_computer_device(devicedata.devname); + info = import_dive_computer(&devicedata, GTK_DIALOG(dialog)); + if (info) + goto repeat; } else { g_slist_foreach(list,do_import_file,NULL); g_slist_free(list); @@ -1009,10 +1309,16 @@ void update_progressbar(progressbar_t *progress, double value) gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress->bar), value); } +void update_progressbar_text(progressbar_t *progress, const char *text) +{ + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress->bar), text); +} void set_filename(const char *filename) { - if (!existing_filename && filename) + if (existing_filename) + free(existing_filename); + existing_filename = NULL; + if (filename) existing_filename = strdup(filename); - return; }