From 682135838ff313594c7f67fabd9be8f88a33883b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 20 Sep 2011 12:40:34 -0700 Subject: [PATCH] Separate out the UI from the program logic The following are UI toolkit specific: gtk-gui.c - overall layout, main window of the UI divelist.c - list of dives subsurface maintains equipment.c - equipment / tank information for each dive info.c - detailed dive info print.c - printing The rest is independent of the UI: main.c i - program frame dive.c i - creates and maintaines the internal dive list structure libdivecomputer.c uemis.c parse-xml.c save-xml.c - interface with dive computers and the XML files profile.c - creates the data for the profile and draws it using cairo This commit should contain NO functional changes, just moving code around and a couple of minor abstractions. Signed-off-by: Dirk Hohndel --- Makefile | 16 +- README | 19 ++ display-gtk.h | 29 +++ display.h | 16 +- dive.c | 2 + dive.h | 15 +- divelist.c | 30 +-- divelist.h | 3 - equipment.c | 11 + gtk-gui.c | 616 ++++++++++++++++++++++++++++++++++++++++++++++ info.c | 11 + libdivecomputer.c | 134 +--------- libdivecomputer.h | 38 +++ main.c | 428 ++------------------------------ print.c | 1 + profile.c | 38 +-- uemis.c | 47 +--- uemis.h | 2 - 18 files changed, 808 insertions(+), 648 deletions(-) create mode 100644 display-gtk.h create mode 100644 gtk-gui.c create mode 100644 libdivecomputer.h diff --git a/Makefile b/Makefile index 8f12d0a..59363e2 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,8 @@ LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a LIBS = `pkg-config --libs gtk+-2.0 glib-2.0 gconf-2.0` OBJS = main.o dive.o profile.o info.o equipment.o divelist.o \ - parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o + parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o \ + gtk-gui.o subsurface: $(OBJS) $(CC) $(LDFLAGS) -o subsurface $(OBJS) \ @@ -26,13 +27,13 @@ dive.o: dive.c dive.h $(CC) $(CFLAGS) `pkg-config --cflags glib-2.0` -c dive.c main.o: main.c dive.h display.h divelist.h - $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0 gconf-2.0` \ + $(CC) $(CFLAGS) `pkg-config --cflags glib-2.0 gconf-2.0` \ -c main.c profile.o: profile.c dive.h display.h divelist.h $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` -c profile.c -info.o: info.c dive.h display.h divelist.h +info.o: info.c dive.h display.h display-gtk.h divelist.h $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` -c info.c equipment.o: equipment.c dive.h display.h divelist.h @@ -41,13 +42,18 @@ equipment.o: equipment.c dive.h display.h divelist.h divelist.o: divelist.c dive.h display.h divelist.h $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` -c divelist.c -print.o: print.c dive.h display.h +print.o: print.c dive.h display.h display-gtk.h $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` -c print.c -libdivecomputer.o: libdivecomputer.c dive.h display.h +libdivecomputer.o: libdivecomputer.c dive.h display.h display-gtk.h libdivecomputer.h $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` \ -I$(LIBDIVECOMPUTERINCLUDES) \ -c libdivecomputer.c +gtk-gui.o: gtk-gui.c dive.h display.h divelist.h display-gtk.h libdivecomputer.h + $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0 gconf-2.0` \ + -I$(LIBDIVECOMPUTERINCLUDES) \ + -c gtk-gui.c + uemis.o: uemis.c uemis.h $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` -c uemis.c diff --git a/README b/README index 35c9c69..204b9a3 100644 --- a/README +++ b/README @@ -37,6 +37,25 @@ and subsurface will de-duplicate the ones that are exactly the same the dives have duplicates that were edited by Dirk in the Suunto Dive Manager, so they don't trigger the "exact duplicates" match. +Implementation details: + +main.c - program frame +dive.c - creates and maintaines the internal dive list structure +libdivecomputer.c +uemis.c +parse-xml.c +save-xml.c - interface with dive computers and the XML files +profile.c - creates the data for the profile and draws it using cairo + +A first UI has been implemented in gtk and an attempt has been made to +separate program logic from UI implementation. + +gtk-gui.c - overall layout, main window of the UI +divelist.c - list of dives subsurface maintains +equipment.c - equipment / tank information for each dive +info.c - detailed dive info +print.c - printing + WARNING! I wasn't kidding when I said that I've done this by reading gtk2 tutorials as I've gone along. If somebody is more comfortable with gtk, feel free to send me (signed-off) patches. diff --git a/display-gtk.h b/display-gtk.h new file mode 100644 index 0000000..dee6636 --- /dev/null +++ b/display-gtk.h @@ -0,0 +1,29 @@ +#ifndef DISPLAY_GTK_H +#define DISPLAY_GTK_H + +#include +#include + +extern GtkWidget *main_window; + +/* we want a progress bar as part of the device_data_t - let's abstract this out */ +typedef struct { + GtkWidget *bar; +} progressbar_t; + +extern const char *divelist_font; +extern void set_divelist_font(const char *); + +extern void import_dialog(GtkWidget *, gpointer); +extern void report_error(GError* error); +extern int process_ui_events(void); +extern void update_progressbar(progressbar_t *progress, double value); + +extern GtkWidget *dive_profile_widget(void); +extern GtkWidget *dive_info_frame(void); +extern GtkWidget *extended_dive_info_widget(void); +extern GtkWidget *equipment_widget(void); + +extern GtkWidget *dive_list_create(void); + +#endif diff --git a/display.h b/display.h index b5b0f91..9cd401a 100644 --- a/display.h +++ b/display.h @@ -1,23 +1,8 @@ #ifndef DISPLAY_H #define DISPLAY_H -#include -#include #include -extern GtkWidget *main_window; - -extern const char *divelist_font; -extern void set_divelist_font(const char *); - -extern void import_dialog(GtkWidget *, gpointer); -extern void report_error(GError* error); - -extern GtkWidget *dive_profile_widget(void); -extern GtkWidget *dive_info_frame(void); -extern GtkWidget *extended_dive_info_widget(void); -extern GtkWidget *equipment_widget(void); - extern void repaint_dive(void); extern void do_print(void); @@ -38,5 +23,6 @@ struct graphics_context { }; extern void plot(struct graphics_context *gc, int w, int h, struct dive *dive); +extern void set_source_rgb(struct graphics_context *gc, double r, double g, double b); #endif diff --git a/dive.c b/dive.c index b7f6129..1b906ee 100644 --- a/dive.c +++ b/dive.c @@ -1,3 +1,5 @@ +/* dive.c */ +/* maintains the internal dive list structure */ #include #include diff --git a/dive.h b/dive.h index 109d0c5..1927da7 100644 --- a/dive.h +++ b/dive.h @@ -222,8 +222,21 @@ extern void report_dives(void); extern struct dive *fixup_dive(struct dive *dive); extern struct dive *try_to_merge(struct dive *a, struct dive *b); -extern void update_air_info(char *buffer); +extern void renumber_dives(int nr); +/* UI related protopypes */ + +extern void init_ui(int argc, char **argv); + +extern void run_ui(void); + +extern void report_error(GError* error); + +extern void dive_list_update_dives(void); +extern void flush_divelist(struct dive *dive); + +extern int open_import_file_dialog(char *filterpattern, char *filtertext, + void(* parse_function)(char *)); #define DIVE_ERROR_PARSE 1 const char *weekday(int wday); diff --git a/divelist.c b/divelist.c index 01ab1b1..e000924 100644 --- a/divelist.c +++ b/divelist.c @@ -1,3 +1,13 @@ +/* divelist.c */ +/* this creates the UI for the dive list - + * controlled through the following interfaces: + * + * void flush_divelist(struct dive *dive) + * GtkWidget dive_list_create(void) + * void dive_list_update_dives(void) + * void update_dive_list_units(void) + * void set_divelist_font(const char *font) + */ #include #include #include @@ -6,6 +16,7 @@ #include "divelist.h" #include "dive.h" #include "display.h" +#include "display-gtk.h" struct DiveList { GtkWidget *tree_view; @@ -34,6 +45,8 @@ enum { DIVELIST_COLUMNS }; +/* the global dive list that we maintain */ +static struct DiveList dive_list; static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) { @@ -48,23 +61,6 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) repaint_dive(); } -const char *weekday(int wday) -{ - static const char wday_array[7][4] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - return wday_array[wday]; -} - -const char *monthname(int mon) -{ - static const char month_array[12][4] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Oct", "Sep", "Nov", "Dec", - }; - return month_array[mon]; -} - static void date_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, diff --git a/divelist.h b/divelist.h index 25de683..7684a72 100644 --- a/divelist.h +++ b/divelist.h @@ -1,11 +1,8 @@ #ifndef DIVELIST_H #define DIVELIST_H -#include - struct dive; -extern GtkWidget *dive_list_create(void); extern void dive_list_update_dives(void); extern void update_dive_list_units(void); extern void flush_divelist(struct dive *); diff --git a/equipment.c b/equipment.c index b9b9259..1e0721b 100644 --- a/equipment.c +++ b/equipment.c @@ -1,3 +1,13 @@ +/* equipment.c */ +/* creates the UI for the equipment page - + * controlled through the following interfaces: + * + * void show_dive_equipment(struct dive *dive) + * void flush_dive_equipment_changes(struct dive *dive) + * + * called from gtk-ui: + * GtkWidget *equipment_widget(void) + */ #include #include #include @@ -6,6 +16,7 @@ #include "dive.h" #include "display.h" +#include "display-gtk.h" #include "divelist.h" struct cylinder_widget { diff --git a/gtk-gui.c b/gtk-gui.c new file mode 100644 index 0000000..77cf4a9 --- /dev/null +++ b/gtk-gui.c @@ -0,0 +1,616 @@ +/* gtk-gui.c */ +/* gtk UI implementation */ +/* creates the window and overall layout + * divelist, dive info, equipment and printing are handled in their own source files + */ +#include +#include +#include +#include + +#include + +#include "dive.h" +#include "divelist.h" +#include "display.h" +#include "display-gtk.h" + +#include "libdivecomputer.h" + +GtkWidget *main_window; +GtkWidget *main_vbox; +GtkWidget *error_info_bar; +GtkWidget *error_label; +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 + +void on_destroy(GtkWidget* w, gpointer data) +{ + gtk_main_quit(); +} + +static GtkWidget *dive_profile; + +void repaint_dive(void) +{ + update_dive(current_dive); + gtk_widget_queue_draw(dive_profile); +} + +static char *existing_filename; + +static void on_info_bar_response(GtkWidget *widget, gint response, + gpointer data) +{ + if (response == GTK_RESPONSE_OK) + { + gtk_widget_destroy(widget); + error_info_bar = NULL; + } +} + +void report_error(GError* error) +{ + if (error == NULL) + { + return; + } + + if (error_info_bar == NULL) + { + error_count = 1; + error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, + GTK_RESPONSE_OK, + NULL); + 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); + } + else + { + error_count++; + char buffer[256]; + snprintf(buffer, sizeof(buffer), "Failed to open %i files.", error_count); + gtk_label_set(GTK_LABEL(error_label), buffer); + } +} + +static void file_open(GtkWidget *w, gpointer data) +{ + GtkWidget *dialog; + dialog = gtk_file_chooser_dialog_new("Open File", + GTK_WINDOW(main_window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + GSList *filenames; + char *filename; + filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + + GError *error = NULL; + while(filenames != NULL) { + filename = (char *)filenames->data; + parse_xml_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); + report_dives(); + dive_list_update_dives(); + } + gtk_widget_destroy(dialog); +} + +static void file_save(GtkWidget *w, gpointer data) +{ + GtkWidget *dialog; + dialog = gtk_file_chooser_dialog_new("Save File", + 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); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + save_dives(filename); + g_free(filename); + } + gtk_widget_destroy(dialog); +} + +static void quit(GtkWidget *w, gpointer data) +{ + gtk_main_quit(); +} + +static void create_radio(GtkWidget *dialog, const char *name, ...) +{ + va_list args; + GtkRadioButton *group = NULL; + GtkWidget *box, *label; + + box = gtk_hbox_new(TRUE, 10); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), box); + + label = gtk_label_new(name); + gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0); + + va_start(args, name); + for (;;) { + int enabled; + const char *name; + GtkWidget *button; + void *callback_fn; + + name = va_arg(args, char *); + if (!name) + break; + callback_fn = va_arg(args, void *); + enabled = va_arg(args, int); + + button = gtk_radio_button_new_with_label_from_widget(group, name); + group = GTK_RADIO_BUTTON(button); + gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), enabled); + g_signal_connect(button, "toggled", G_CALLBACK(callback_fn), NULL); + } + va_end(args); +} + +#define UNITCALLBACK(name, type, value) \ +static void name(GtkWidget *w, gpointer data) \ +{ \ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \ + menu_units.type = value; \ +} + +static struct units menu_units; + +UNITCALLBACK(set_meter, length, METERS) +UNITCALLBACK(set_feet, length, FEET) +UNITCALLBACK(set_bar, pressure, BAR) +UNITCALLBACK(set_psi, pressure, PSI) +UNITCALLBACK(set_liter, volume, LITER) +UNITCALLBACK(set_cuft, volume, CUFT) +UNITCALLBACK(set_celsius, temperature, CELSIUS) +UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT) + +static void preferences_dialog(GtkWidget *w, gpointer data) +{ + int result; + GtkWidget *dialog, *font, *frame, *box; + + menu_units = output_units; + + dialog = gtk_dialog_new_with_buttons("Preferences", + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + frame = gtk_frame_new("Units"); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); + + box = gtk_vbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), box); + + create_radio(box, "Depth:", + "Meter", set_meter, (output_units.length == METERS), + "Feet", set_feet, (output_units.length == FEET), + NULL); + + create_radio(box, "Pressure:", + "Bar", set_bar, (output_units.pressure == BAR), + "PSI", set_psi, (output_units.pressure == PSI), + NULL); + + create_radio(box, "Volume:", + "Liter", set_liter, (output_units.volume == LITER), + "CuFt", set_cuft, (output_units.volume == CUFT), + NULL); + + create_radio(box, "Temperature:", + "Celsius", set_celsius, (output_units.temperature == CELSIUS), + "Fahrenheit", set_fahrenheit, (output_units.temperature == FAHRENHEIT), + NULL); + + font = gtk_font_button_new_with_font(divelist_font); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), font, FALSE, FALSE, 5); + + gtk_widget_show_all(dialog); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + if (result == GTK_RESPONSE_ACCEPT) { + /* Make sure to flush any modified old dive data with old units */ + update_dive(NULL); + + divelist_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font))); + set_divelist_font(divelist_font); + + output_units = menu_units; + update_dive_list_units(); + repaint_dive(); + 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_string(gconf, GCONF_NAME(divelist_font), divelist_font, NULL); + } + gtk_widget_destroy(dialog); +} + +static void renumber_dialog(GtkWidget *w, gpointer data) +{ + int result; + GtkWidget *dialog, *frame, *button; + + dialog = gtk_dialog_new_with_buttons("Renumber", + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + frame = gtk_frame_new("New starting number"); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), frame); + + button = gtk_spin_button_new_with_range(1, 50000, 1); + gtk_container_add(GTK_CONTAINER(frame), button); + + gtk_widget_show_all(dialog); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + if (result == GTK_RESPONSE_ACCEPT) { + int nr = gtk_spin_button_get_value(GTK_SPIN_BUTTON(button)); + renumber_dives(nr); + repaint_dive(); + } + gtk_widget_destroy(dialog); +} + +static GtkActionEntry menu_items[] = { + { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, + { "LogMenuAction", GTK_STOCK_FILE, "Log", 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) }, + { "Import", NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) }, + { "Preferences", NULL, "Preferences", NULL, NULL, G_CALLBACK(preferences_dialog) }, + { "Renumber", NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) }, + { "Quit", GTK_STOCK_QUIT, NULL, "Q", NULL, G_CALLBACK(quit) }, +}; +static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); + +static const gchar* ui_string = " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +"; + +static GtkWidget *get_menubar_menu(GtkWidget *window) +{ + 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); + + gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager)); + GtkWidget* menu = gtk_ui_manager_get_widget(ui_manager, "/MainMenu"); + + return menu; +} + +static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data) +{ + repaint_dive(); +} + +void init_ui(int argc, char **argv) +{ + GtkWidget *win; + GtkWidget *paned; + GtkWidget *info_box; + GtkWidget *notebook; + GtkWidget *dive_info; + GtkWidget *dive_list; + GtkWidget *equipment; + GtkWidget *menubar; + GtkWidget *vbox; + + gtk_init(&argc, &argv); + + g_type_init(); + gconf = gconf_client_get_default(); + + if (gconf_client_get_bool(gconf, GCONF_NAME(feet), NULL)) + output_units.length = FEET; + if (gconf_client_get_bool(gconf, GCONF_NAME(psi), NULL)) + output_units.pressure = PSI; + if (gconf_client_get_bool(gconf, GCONF_NAME(cuft), NULL)) + output_units.volume = CUFT; + if (gconf_client_get_bool(gconf, GCONF_NAME(fahrenheit), NULL)) + output_units.temperature = FAHRENHEIT; + + divelist_font = gconf_client_get_string(gconf, GCONF_NAME(divelist_font), NULL); + if (!divelist_font) + divelist_font = DIVELIST_DEFAULT_FONT; + + error_info_bar = NULL; + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_icon_from_file(GTK_WINDOW(win), "icon.svg", NULL); + g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL); + main_window = win; + + vbox = gtk_vbox_new(FALSE, 0); + 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); + + /* HPane for left the dive list, and right the dive info */ + paned = gtk_vpaned_new(); + gtk_box_pack_end(GTK_BOX(vbox), paned, TRUE, TRUE, 0); + + /* Create the actual divelist */ + dive_list = dive_list_create(); + gtk_paned_add2(GTK_PANED(paned), dive_list); + + /* VBox for dive info, and tabs */ + info_box = gtk_vbox_new(FALSE, 6); + gtk_paned_add1(GTK_PANED(paned), info_box); + + /* Notebook for dive info vs profile vs .. */ + notebook = gtk_notebook_new(); + g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL); + gtk_box_pack_start(GTK_BOX(info_box), notebook, TRUE, TRUE, 6); + + /* Frame for dive profile */ + dive_profile = dive_profile_widget(); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_profile, gtk_label_new("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")); + + /* Frame for dive equipment */ + equipment = equipment_widget(); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment")); + + gtk_widget_set_app_paintable(win, TRUE); + gtk_widget_show_all(win); + + return; +} + +void run_ui(void) +{ + gtk_main(); +} + +/* get the filenames the user selects and call the parsing function + * on them + * return 0 if the user cancelled the dialog + */ +int open_import_file_dialog(char *filterpattern, char *filtertext, + void(* parse_function)(char *)) +{ + int ret=0; + + GtkWidget *dialog; + GtkFileFilter *filter = gtk_file_filter_new (); + gtk_file_filter_add_pattern (filter, filterpattern); + gtk_file_filter_set_name(filter, filtertext); + dialog = gtk_file_chooser_dialog_new("Open File", + GTK_WINDOW(main_window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),filter); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + GSList *filenames; + char *filename; + filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + while(filenames != NULL) { + filename = (char *)filenames->data; + parse_function(filename); + g_free(filename); + filenames = g_slist_next(filenames); + } + g_slist_free(filenames); + ret = 1; + } + gtk_widget_destroy(dialog); + + return ret; +} + +static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + struct dive *dive = current_dive; + struct graphics_context gc = { .printer = 0 }; + int w,h; + + w = widget->allocation.width; + h = widget->allocation.height; + + gc.cr = gdk_cairo_create(widget->window); + set_source_rgb(&gc, 0, 0, 0); + cairo_paint(gc.cr); + + if (dive) + plot(&gc, w, h, dive); + + cairo_destroy(gc.cr); + + return FALSE; +} + +GtkWidget *dive_profile_widget(void) +{ + GtkWidget *da; + + da = gtk_drawing_area_new(); + gtk_widget_set_size_request(da, 350, 250); + g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL); + + return da; +} + +int process_ui_events(void) +{ + int ret=0; + + while (gtk_events_pending()) { + if (gtk_main_iteration_do(0)) { + ret = 1; + break; + } + } + return(ret); +} + + +static void fill_computer_list(GtkListStore *store) +{ + GtkTreeIter iter; + struct device_list *list = device_list; + + 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, + -1); + } +} + +static GtkComboBox *dive_computer_selector(GtkWidget *dialog) +{ + GtkWidget *hbox, *combo_box; + GtkListStore *model; + GtkCellRenderer *renderer; + + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3); + + model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); + fill_computer_list(model); + + combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); + gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3); + + 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); + + return GTK_COMBO_BOX(combo_box); +} + +void import_dialog(GtkWidget *w, gpointer data) +{ + int result; + GtkWidget *dialog, *hbox; + GtkComboBox *computer; + device_data_t devicedata = { + .devname = "/dev/ttyUSB0", + }; + + dialog = gtk_dialog_new_with_buttons("Import from dive computer", + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + computer = dive_computer_selector(dialog); + + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3); + devicedata.progress->bar = gtk_progress_bar_new(); + gtk_container_add(GTK_CONTAINER(hbox), devicedata.progress->bar); + + gtk_widget_show_all(dialog); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + int type; + GtkTreeIter iter; + GtkTreeModel *model; + const char *comp; + case GTK_RESPONSE_ACCEPT: + 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, + -1); + devicedata.type = type; + devicedata.name = comp; + do_import(&devicedata); + break; + default: + break; + } + gtk_widget_destroy(dialog); + + report_dives(); + dive_list_update_dives(); +} + +void update_progressbar(progressbar_t *progress, double value) +{ + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress->bar), value); +} diff --git a/info.c b/info.c index c1779cc..9e06197 100644 --- a/info.c +++ b/info.c @@ -1,3 +1,13 @@ +/* info.c */ +/* creates the UI for the info frame - + * controlled through the following interfaces: + * + * void flush_dive_info_changes(struct dive *dive) + * void show_dive_info(struct dive *dive) + * + * called from gtk-ui: + * GtkWidget *extended_dive_info_widget(void) + */ #include #include #include @@ -5,6 +15,7 @@ #include "dive.h" #include "display.h" +#include "display-gtk.h" #include "divelist.h" static GtkEntry *location, *buddy, *divemaster; diff --git a/libdivecomputer.c b/libdivecomputer.c index 9e5bd90..cf8b048 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -1,26 +1,12 @@ #include -#include #include #include "dive.h" #include "divelist.h" #include "display.h" +#include "display-gtk.h" -/* libdivecomputer */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* handling uemis Zurich SDA files */ -#include "uemis.h" +#include "libdivecomputer.h" static void error(const char *fmt, ...) { @@ -36,14 +22,6 @@ static void error(const char *fmt, ...) g_error_free(error); } -typedef struct device_data_t { - device_type_t type; - const char *name, *devname; - GtkWidget *progressbar; - device_devinfo_t devinfo; - device_clock_t clock; -} device_data_t; - static parser_status_t create_parser(device_data_t *devdata, parser_t **parser) { switch (devdata->type) { @@ -379,8 +357,7 @@ static device_status_t device_open(const char *devname, } } -static void -event_cb(device_t *device, device_event_t event, const void *data, void *userdata) +static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata) { const device_progress_t *progress = (device_progress_t *) data; const device_devinfo_t *devinfo = (device_devinfo_t *) data; @@ -392,7 +369,7 @@ event_cb(device_t *device, device_event_t event, const void *data, void *userdat printf("Event: waiting for user action\n"); break; case DEVICE_EVENT_PROGRESS: - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(devdata->progressbar), + update_progressbar(devdata->progress, (double) progress->current / (double) progress->maximum); break; case DEVICE_EVENT_DEVINFO: @@ -462,7 +439,7 @@ static void *pthread_wrapper(void *_data) return (void *)err_string; } -static void do_import(device_data_t *data) +void do_import(device_data_t *data) { pthread_t pthread; void *retval; @@ -470,16 +447,11 @@ static void do_import(device_data_t *data) if (data->type == DEVICE_TYPE_UEMIS) return uemis_import(); - /* I'm sure there is some better interface for waiting on a thread in a gtk main loop */ + /* I'm sure there is some better interface for waiting on a thread in a UI main loop */ import_thread_done = 0; pthread_create(&pthread, NULL, pthread_wrapper, data); while (!import_thread_done) { - while (gtk_events_pending()) { - if (gtk_main_iteration_do(0)) { - import_thread_cancelled = 1; - break; - } - } + import_thread_cancelled = process_ui_events(); usleep(100000); } if (pthread_join(pthread, &retval) < 0) @@ -495,10 +467,7 @@ static void do_import(device_data_t *data) * libdivecomputer tell us what devices it supports, * rather than have the application have to know.. */ -struct device_list { - const char *name; - device_type_t type; -} device_list[] = { +struct device_list device_list[] = { { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION }, { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON }, { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER }, @@ -523,90 +492,3 @@ struct device_list { { "Uemis Zurich SDA", DEVICE_TYPE_UEMIS }, { NULL } }; - -static void fill_computer_list(GtkListStore *store) -{ - GtkTreeIter iter; - struct device_list *list = device_list; - - 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, - -1); - } -} - -static GtkComboBox *dive_computer_selector(GtkWidget *dialog) -{ - GtkWidget *hbox, *combo_box; - GtkListStore *model; - GtkCellRenderer *renderer; - - hbox = gtk_hbox_new(FALSE, 6); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3); - - model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); - fill_computer_list(model); - - combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); - gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3); - - 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); - - return GTK_COMBO_BOX(combo_box); -} - -void import_dialog(GtkWidget *w, gpointer data) -{ - int result; - GtkWidget *dialog, *hbox; - GtkComboBox *computer; - device_data_t devicedata = { - .devname = "/dev/ttyUSB0", - }; - - dialog = gtk_dialog_new_with_buttons("Import from dive computer", - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - computer = dive_computer_selector(dialog); - - hbox = gtk_hbox_new(FALSE, 6); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3); - devicedata.progressbar = gtk_progress_bar_new(); - gtk_container_add(GTK_CONTAINER(hbox), devicedata.progressbar); - - gtk_widget_show_all(dialog); - result = gtk_dialog_run(GTK_DIALOG(dialog)); - switch (result) { - int type; - GtkTreeIter iter; - GtkTreeModel *model; - const char *comp; - case GTK_RESPONSE_ACCEPT: - 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, - -1); - devicedata.type = type; - devicedata.name = comp; - do_import(&devicedata); - break; - default: - break; - } - gtk_widget_destroy(dialog); - - report_dives(); - dive_list_update_dives(); -} diff --git a/libdivecomputer.h b/libdivecomputer.h new file mode 100644 index 0000000..205f28e --- /dev/null +++ b/libdivecomputer.h @@ -0,0 +1,38 @@ +#ifndef LIBDIVECOMPUTER_H +#define LIBDIVECOMPUTER_H + +/* libdivecomputer */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* handling uemis Zurich SDA files */ +#include "uemis.h" + +/* don't forget to include the UI toolkit specific display-XXX.h first + to get the definition of progressbar_t */ +typedef struct device_data_t { + device_type_t type; + const char *name, *devname; + progressbar_t *progress; + device_devinfo_t devinfo; + device_clock_t clock; +} device_data_t; + +struct device_list { + const char *name; + device_type_t type; +}; + +extern struct device_list device_list[]; +extern void do_import(device_data_t *data); + +#endif diff --git a/main.c b/main.c index 7d65c87..3e41456 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,4 @@ +/* main.c */ #include #include #include @@ -7,22 +8,13 @@ #include "dive.h" #include "divelist.h" -#include "display.h" - -GtkWidget *main_window; -GtkWidget *main_vbox; -GtkWidget *error_info_bar; -GtkWidget *error_label; -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 +/* random helper functions, used here or elsewhere */ static int sortfn(const void *_a, const void *_b) { const struct dive *a = *(void **)_a; @@ -35,6 +27,23 @@ static int sortfn(const void *_a, const void *_b) return 0; } +const char *weekday(int wday) +{ + static const char wday_array[7][4] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + return wday_array[wday]; +} + +const char *monthname(int mon) +{ + static const char month_array[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Oct", "Sep", "Nov", "Dec", + }; + return month_array[mon]; +} + /* * This doesn't really report anything at all. We just sort the * dives, the GUI does the reporting @@ -85,13 +94,6 @@ static void parse_argument(const char *arg) } while (*++p); } -static void on_destroy(GtkWidget* w, gpointer data) -{ - gtk_main_quit(); -} - -static GtkWidget *dive_profile; - void update_dive(struct dive *new_dive) { static struct dive *buffered_dive; @@ -109,240 +111,7 @@ void update_dive(struct dive *new_dive) buffered_dive = new_dive; } -void repaint_dive(void) -{ - update_dive(current_dive); - gtk_widget_queue_draw(dive_profile); -} - -static char *existing_filename; - -static void on_info_bar_response(GtkWidget *widget, gint response, - gpointer data) -{ - if (response == GTK_RESPONSE_OK) - { - gtk_widget_destroy(widget); - error_info_bar = NULL; - } -} - -void report_error(GError* error) -{ - if (error == NULL) - { - return; - } - - if (error_info_bar == NULL) - { - error_count = 1; - error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, - GTK_RESPONSE_OK, - NULL); - 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); - } - else - { - error_count++; - char buffer[256]; - snprintf(buffer, sizeof(buffer), "Failed to open %i files.", error_count); - gtk_label_set(GTK_LABEL(error_label), buffer); - } -} - -static void file_open(GtkWidget *w, gpointer data) -{ - GtkWidget *dialog; - dialog = gtk_file_chooser_dialog_new("Open File", - GTK_WINDOW(main_window), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - GSList *filenames; - char *filename; - filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); - - GError *error = NULL; - while(filenames != NULL) { - filename = (char *)filenames->data; - parse_xml_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); - report_dives(); - dive_list_update_dives(); - } - gtk_widget_destroy(dialog); -} - -static void file_save(GtkWidget *w, gpointer data) -{ - GtkWidget *dialog; - dialog = gtk_file_chooser_dialog_new("Save File", - 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); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - char *filename; - filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - save_dives(filename); - g_free(filename); - } - gtk_widget_destroy(dialog); -} - -static void quit(GtkWidget *w, gpointer data) -{ - gtk_main_quit(); -} - -static void create_radio(GtkWidget *vbox, const char *name, ...) -{ - va_list args; - GtkRadioButton *group = NULL; - GtkWidget *box, *label; - - box = gtk_hbox_new(TRUE, 10); - gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 0); - - label = gtk_label_new(name); - gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0); - - va_start(args, name); - for (;;) { - int enabled; - const char *name; - GtkWidget *button; - void *callback_fn; - - name = va_arg(args, char *); - if (!name) - break; - callback_fn = va_arg(args, void *); - enabled = va_arg(args, int); - - button = gtk_radio_button_new_with_label_from_widget(group, name); - group = GTK_RADIO_BUTTON(button); - gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), enabled); - g_signal_connect(button, "toggled", G_CALLBACK(callback_fn), NULL); - } - va_end(args); -} - -#define UNITCALLBACK(name, type, value) \ -static void name(GtkWidget *w, gpointer data) \ -{ \ - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \ - menu_units.type = value; \ -} - -static struct units menu_units; - -UNITCALLBACK(set_meter, length, METERS) -UNITCALLBACK(set_feet, length, FEET) -UNITCALLBACK(set_bar, pressure, BAR) -UNITCALLBACK(set_psi, pressure, PSI) -UNITCALLBACK(set_liter, volume, LITER) -UNITCALLBACK(set_cuft, volume, CUFT) -UNITCALLBACK(set_celsius, temperature, CELSIUS) -UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT) - -static void preferences_dialog(GtkWidget *w, gpointer data) -{ - int result; - GtkWidget *dialog, *font, *frame, *box; - - menu_units = output_units; - - dialog = gtk_dialog_new_with_buttons("Preferences", - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - frame = gtk_frame_new("Units"); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); - - box = gtk_vbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(frame), box); - - create_radio(box, "Depth:", - "Meter", set_meter, (output_units.length == METERS), - "Feet", set_feet, (output_units.length == FEET), - NULL); - - create_radio(box, "Pressure:", - "Bar", set_bar, (output_units.pressure == BAR), - "PSI", set_psi, (output_units.pressure == PSI), - NULL); - - create_radio(box, "Volume:", - "Liter", set_liter, (output_units.volume == LITER), - "CuFt", set_cuft, (output_units.volume == CUFT), - NULL); - - create_radio(box, "Temperature:", - "Celsius", set_celsius, (output_units.temperature == CELSIUS), - "Fahrenheit", set_fahrenheit, (output_units.temperature == FAHRENHEIT), - NULL); - - font = gtk_font_button_new_with_font(divelist_font); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), font, FALSE, FALSE, 5); - - gtk_widget_show_all(dialog); - result = gtk_dialog_run(GTK_DIALOG(dialog)); - if (result == GTK_RESPONSE_ACCEPT) { - /* Make sure to flush any modified old dive data with old units */ - update_dive(NULL); - - divelist_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font))); - set_divelist_font(divelist_font); - - output_units = menu_units; - update_dive_list_units(); - repaint_dive(); - 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_string(gconf, GCONF_NAME(divelist_font), divelist_font, NULL); - } - gtk_widget_destroy(dialog); -} - -static void renumber_dives(int nr) +void renumber_dives(int nr) { int i; @@ -352,167 +121,14 @@ static void renumber_dives(int nr) } } -static void renumber_dialog(GtkWidget *w, gpointer data) -{ - int result; - GtkWidget *dialog, *frame, *button; - - dialog = gtk_dialog_new_with_buttons("Renumber", - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - frame = gtk_frame_new("New starting number"); - gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), frame); - - button = gtk_spin_button_new_with_range(1, 50000, 1); - gtk_container_add(GTK_CONTAINER(frame), button); - - gtk_widget_show_all(dialog); - result = gtk_dialog_run(GTK_DIALOG(dialog)); - if (result == GTK_RESPONSE_ACCEPT) { - int nr = gtk_spin_button_get_value(GTK_SPIN_BUTTON(button)); - renumber_dives(nr); - repaint_dive(); - } - gtk_widget_destroy(dialog); -} - -static GtkActionEntry menu_items[] = { - { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, - { "LogMenuAction", GTK_STOCK_FILE, "Log", 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) }, - { "Import", NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) }, - { "Preferences", NULL, "Preferences", NULL, NULL, G_CALLBACK(preferences_dialog) }, - { "Renumber", NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) }, - { "Quit", GTK_STOCK_QUIT, NULL, "Q", NULL, G_CALLBACK(quit) }, -}; -static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); - -static const gchar* ui_string = " \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -"; - -static GtkWidget *get_menubar_menu(GtkWidget *window) -{ - 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); - - gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager)); - GtkWidget* menu = gtk_ui_manager_get_widget(ui_manager, "/MainMenu"); - - return menu; -} - -static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data) -{ - repaint_dive(); -} - int main(int argc, char **argv) { int i; - GtkWidget *win; - GtkWidget *paned; - GtkWidget *info_box; - GtkWidget *notebook; - GtkWidget *dive_info; - GtkWidget *dive_list; - GtkWidget *equipment; - GtkWidget *menubar; - GtkWidget *vbox; output_units = SI_units; parse_xml_init(); - gtk_init(&argc, &argv); - - g_type_init(); - gconf = gconf_client_get_default(); - - if (gconf_client_get_bool(gconf, GCONF_NAME(feet), NULL)) - output_units.length = FEET; - if (gconf_client_get_bool(gconf, GCONF_NAME(psi), NULL)) - output_units.pressure = PSI; - if (gconf_client_get_bool(gconf, GCONF_NAME(cuft), NULL)) - output_units.volume = CUFT; - if (gconf_client_get_bool(gconf, GCONF_NAME(fahrenheit), NULL)) - output_units.temperature = FAHRENHEIT; - - divelist_font = gconf_client_get_string(gconf, GCONF_NAME(divelist_font), NULL); - if (!divelist_font) - divelist_font = DIVELIST_DEFAULT_FONT; - - error_info_bar = NULL; - win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_icon_from_file(GTK_WINDOW(win), "icon.svg", NULL); - g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL); - main_window = win; - - vbox = gtk_vbox_new(FALSE, 0); - 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); - - /* HPane for left the dive list, and right the dive info */ - paned = gtk_vpaned_new(); - gtk_box_pack_end(GTK_BOX(vbox), paned, TRUE, TRUE, 0); - - /* Create the actual divelist */ - dive_list = dive_list_create(); - gtk_paned_add2(GTK_PANED(paned), dive_list); - - /* VBox for dive info, and tabs */ - info_box = gtk_vbox_new(FALSE, 6); - gtk_paned_add1(GTK_PANED(paned), info_box); - - /* Notebook for dive info vs profile vs .. */ - notebook = gtk_notebook_new(); - g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL); - gtk_box_pack_start(GTK_BOX(info_box), notebook, TRUE, TRUE, 6); - - /* Frame for dive profile */ - dive_profile = dive_profile_widget(); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_profile, gtk_label_new("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")); - - /* Frame for dive equipment */ - equipment = equipment_widget(); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment")); - - gtk_widget_set_app_paintable(win, TRUE); - gtk_widget_show_all(win); + init_ui(argc, argv); for (i = 1; i < argc; i++) { const char *a = argv[i]; @@ -535,6 +151,6 @@ int main(int argc, char **argv) report_dives(); dive_list_update_dives(); - gtk_main(); + run_ui(); return 0; } diff --git a/print.c b/print.c index 57cac80..af7fc4c 100644 --- a/print.c +++ b/print.c @@ -2,6 +2,7 @@ #include "dive.h" #include "display.h" +#include "display-gtk.h" static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context, diff --git a/profile.c b/profile.c index 9a9d986..20317b9 100644 --- a/profile.c +++ b/profile.c @@ -1,3 +1,7 @@ +/* profile.c */ +/* creates all the necessary data for drawing the dive profile + * uses cairo to draw it + */ #include #include #include @@ -72,7 +76,7 @@ static void set_source_rgba(struct graphics_context *gc, double r, double g, dou cairo_set_source_rgba(gc->cr, r, g, b, a); } -static void set_source_rgb(struct graphics_context *gc, double r, double g, double b) +void set_source_rgb(struct graphics_context *gc, double r, double g, double b) { set_source_rgba(gc, r, g, b, 1); } @@ -741,35 +745,3 @@ void plot(struct graphics_context *gc, int w, int h, struct dive *dive) cairo_stroke(gc->cr); } - -static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) -{ - struct dive *dive = current_dive; - struct graphics_context gc = { .printer = 0 }; - int w,h; - - w = widget->allocation.width; - h = widget->allocation.height; - - gc.cr = gdk_cairo_create(widget->window); - set_source_rgb(&gc, 0, 0, 0); - cairo_paint(gc.cr); - - if (dive) - plot(&gc, w, h, dive); - - cairo_destroy(gc.cr); - - return FALSE; -} - -GtkWidget *dive_profile_widget(void) -{ - GtkWidget *da; - - da = gtk_drawing_area_new(); - gtk_widget_set_size_request(da, 350, 250); - g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL); - - return da; -} diff --git a/uemis.c b/uemis.c index 5cede0f..c390aae 100644 --- a/uemis.c +++ b/uemis.c @@ -16,10 +16,7 @@ #include #include -#include - #include "dive.h" -#include "display.h" #include "uemis.h" /* @@ -214,8 +211,11 @@ static void parse_divelog_binary(char *base64, struct dive **divep) { return; } +/* parse a single file + * TODO: we don't report any errors when the parse fails - we simply don't add them to the list + */ void -parse_uemis_file(char *divelogfilename,GError **error) { +parse_uemis_file(char *divelogfilename) { char *found=NULL; struct tm tm; struct dive *dive; @@ -261,40 +261,7 @@ bail: */ void uemis_import() { - GtkWidget *dialog; - GtkFileFilter *filter = gtk_file_filter_new (); - gtk_file_filter_add_pattern (filter, "*.SDA"); - gtk_file_filter_set_name(filter, "uemis Zurich SDA files"); - dialog = gtk_file_chooser_dialog_new("Open File", - GTK_WINDOW(main_window), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),filter); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - GSList *filenames; - char *filename; - filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); - - GError *error = NULL; - while(filenames != NULL) { - filename = (char *)filenames->data; - parse_uemis_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); + if (open_import_file_dialog("*.SDA","uemis Zurich SDA files", + &parse_uemis_file)) report_dives(); - } - gtk_widget_destroy(dialog); -} +} diff --git a/uemis.h b/uemis.h index 1b6c026..d694007 100644 --- a/uemis.h +++ b/uemis.h @@ -10,6 +10,4 @@ void uemis_import(); -extern GtkWidget *main_window; - #endif /* DIVE_H */ -- 2.43.0