]> git.tdb.fi Git - ext/subsurface.git/commitdiff
Separate out the UI from the program logic
authorDirk Hohndel <dirk@hohndel.org>
Tue, 20 Sep 2011 19:40:34 +0000 (12:40 -0700)
committerDirk Hohndel <dirk@hohndel.org>
Tue, 20 Sep 2011 19:48:56 +0000 (12:48 -0700)
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 <dirk@hohndel.org>
18 files changed:
Makefile
README
display-gtk.h [new file with mode: 0644]
display.h
dive.c
dive.h
divelist.c
divelist.h
equipment.c
gtk-gui.c [new file with mode: 0644]
info.c
libdivecomputer.c
libdivecomputer.h [new file with mode: 0644]
main.c
print.c
profile.c
uemis.c
uemis.h

index 8f12d0a0af4dfae20757ddebdd76032699c0099f..59363e2a166b155a5433c6a1b3c655205022c2d3 100644 (file)
--- 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 35c9c6913b5eda0422f0407f94f83b26ba3ecbaf..204b9a3bf8290a8e454f65a464e4e5df1d02fa60 100644 (file)
--- 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 (file)
index 0000000..dee6636
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef DISPLAY_GTK_H
+#define DISPLAY_GTK_H
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+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
index b5b0f919bd5e112ce09218f472efa43684638740..9cd401a893da34928212b797110a146fe46107a5 100644 (file)
--- a/display.h
+++ b/display.h
@@ -1,23 +1,8 @@
 #ifndef DISPLAY_H
 #define DISPLAY_H
 
-#include <gtk/gtk.h>
-#include <gdk/gdk.h>
 #include <cairo.h>
 
-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 b7f61293636bba44b0d0c49cf344615ba22ef03f..1b906ee43bd9a7e86db41d08cc5d3f288732a012 100644 (file)
--- a/dive.c
+++ b/dive.c
@@ -1,3 +1,5 @@
+/* dive.c */
+/* maintains the internal dive list structure */
 #include <string.h>
 #include <stdio.h>
 
diff --git a/dive.h b/dive.h
index 109d0c57da1a984e59aa03dd0792c3f6ac258276..1927da7215a68e69ece2ce062cb58e61b6914bd9 100644 (file)
--- 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);
index 01ab1b11ad2c6b126b0e49a74c025b4b3c9b96e6..e000924368de40763520d22e1ab371957e287ce4 100644 (file)
@@ -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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -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,
index 25de6837a6f9c38332d3a3659fffa0a9ee613a27..7684a72d3e8e8d337666971b4cbe676c3f74f62f 100644 (file)
@@ -1,11 +1,8 @@
 #ifndef DIVELIST_H
 #define DIVELIST_H
 
-#include <gtk/gtk.h>
-
 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 *);
index b9b9259b5a8cd0631816842e95d513e14d1066e3..1e0721b9abd195370b6daa8ecdb81f2938cf70d4 100644 (file)
@@ -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 <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -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 (file)
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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <gconf/gconf-client.h>
+
+#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,   "<control>O", NULL, G_CALLBACK(file_open) },
+       { "SaveFile",       GTK_STOCK_SAVE, NULL,   "<control>S", NULL, G_CALLBACK(file_save) },
+       { "Print",          GTK_STOCK_PRINT, NULL,  "<control>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,   "<control>Q", NULL, G_CALLBACK(quit) },
+};
+static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
+
+static const gchar* ui_string = " \
+       <ui> \
+               <menubar name=\"MainMenu\"> \
+                       <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
+                               <menuitem name=\"Open\" action=\"OpenFile\" /> \
+                               <menuitem name=\"Save\" action=\"SaveFile\" /> \
+                               <menuitem name=\"Print\" action=\"Print\" /> \
+                               <separator name=\"Separator1\"/> \
+                               <menuitem name=\"Import\" action=\"Import\" /> \
+                               <separator name=\"Separator2\"/> \
+                               <menuitem name=\"Preferences\" action=\"Preferences\" /> \
+                               <separator name=\"Separator3\"/> \
+                               <menuitem name=\"Quit\" action=\"Quit\" /> \
+                       </menu> \
+                       <menu name=\"LogMenu\" action=\"LogMenuAction\"> \
+                               <menuitem name=\"Renumber\" action=\"Renumber\" /> \
+                       </menu> \
+               </menubar> \
+       </ui> \
+";
+
+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 c1779cc452ac6a96fab304eb7adfbf5aeb5846ab..9e061970d808ba14bb18d9c259de638781b0c0af 100644 (file)
--- 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 <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -5,6 +15,7 @@
 
 #include "dive.h"
 #include "display.h"
+#include "display-gtk.h"
 #include "divelist.h"
 
 static GtkEntry *location, *buddy, *divemaster;
index 9e5bd907ec18eb9e98281296f3644603f7ca50a4..cf8b048c9e79e62ea63f30bc04a364ce3ce59855 100644 (file)
@@ -1,26 +1,12 @@
 #include <stdio.h>
-#include <gtk/gtk.h>
 #include <pthread.h>
 
 #include "dive.h"
 #include "divelist.h"
 #include "display.h"
+#include "display-gtk.h"
 
-/* libdivecomputer */
-#include <device.h>
-#include <suunto.h>
-#include <reefnet.h>
-#include <uwatec.h>
-#include <oceanic.h>
-#include <mares.h>
-#include <hw.h>
-#include <cressi.h>
-#include <zeagle.h>
-#include <atomics.h>
-#include <utils.h>
-
-/* 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 (file)
index 0000000..205f28e
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef LIBDIVECOMPUTER_H
+#define LIBDIVECOMPUTER_H
+
+/* libdivecomputer */
+#include <device.h>
+#include <suunto.h>
+#include <reefnet.h>
+#include <uwatec.h>
+#include <oceanic.h>
+#include <mares.h>
+#include <hw.h>
+#include <cressi.h>
+#include <zeagle.h>
+#include <atomics.h>
+#include <utils.h>
+
+/* 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 7d65c87e10669066bf53cd7b6793aff87d59e2bb..3e4145645ba9ed48ecdf647e865d414b291e224c 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,3 +1,4 @@
+/* main.c */
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -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,   "<control>O", NULL, G_CALLBACK(file_open) },
-       { "SaveFile",       GTK_STOCK_SAVE, NULL,   "<control>S", NULL, G_CALLBACK(file_save) },
-       { "Print",          GTK_STOCK_PRINT, NULL,  "<control>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,   "<control>Q", NULL, G_CALLBACK(quit) },
-};
-static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
-
-static const gchar* ui_string = " \
-       <ui> \
-               <menubar name=\"MainMenu\"> \
-                       <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
-                               <menuitem name=\"Open\" action=\"OpenFile\" /> \
-                               <menuitem name=\"Save\" action=\"SaveFile\" /> \
-                               <menuitem name=\"Print\" action=\"Print\" /> \
-                               <separator name=\"Separator1\"/> \
-                               <menuitem name=\"Import\" action=\"Import\" /> \
-                               <separator name=\"Separator2\"/> \
-                               <menuitem name=\"Preferences\" action=\"Preferences\" /> \
-                               <separator name=\"Separator3\"/> \
-                               <menuitem name=\"Quit\" action=\"Quit\" /> \
-                       </menu> \
-                       <menu name=\"LogMenu\" action=\"LogMenuAction\"> \
-                               <menuitem name=\"Renumber\" action=\"Renumber\" /> \
-                       </menu> \
-               </menubar> \
-       </ui> \
-";
-
-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 57cac80757f3533c2ce458c9249cd5196d54365f..af7fc4cd6c232368ebe6adcaa111a626b17f88cd 100644 (file)
--- 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,
index 9a9d98642934bc308e12b0f0a9be7b5cb6497d0e..20317b93717704e922000528cf1b5060df1d7bd1 100644 (file)
--- 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 <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -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 5cede0facce9b161bde15db1e1ab2b8903cff38e..c390aae3b74635fa67466db2094030d4a99ce8bd 100644 (file)
--- a/uemis.c
+++ b/uemis.c
 #include <time.h>
 #include <regex.h>
 
-#include <gtk/gtk.h>
-
 #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 1b6c026d1ca1d23fb02f14cbb783d8501f32c6fe..d694007d031e2eb919136c2ce46d8dc9e9165be1 100644 (file)
--- a/uemis.h
+++ b/uemis.h
@@ -10,6 +10,4 @@
 
 void uemis_import();
 
-extern GtkWidget *main_window;
-
 #endif /* DIVE_H */