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 <dirk@hohndel.org>
This commit is contained in:
Dirk Hohndel 2011-09-20 12:40:34 -07:00
parent 6ea5132463
commit 682135838f
18 changed files with 808 additions and 648 deletions

View file

@ -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

19
README
View file

@ -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.

29
display-gtk.h Normal file
View file

@ -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

View file

@ -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

2
dive.c
View file

@ -1,3 +1,5 @@
/* dive.c */
/* maintains the internal dive list structure */
#include <string.h>
#include <stdio.h>

15
dive.h
View file

@ -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);

View 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,

View 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 *);

View 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 {

616
gtk-gui.c Normal file
View file

@ -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);
}

11
info.c
View file

@ -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;

View 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();
}

38
libdivecomputer.h Normal file
View file

@ -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

428
main.c
View file

@ -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;
}

View file

@ -2,6 +2,7 @@
#include "dive.h"
#include "display.h"
#include "display-gtk.h"
static void draw_page(GtkPrintOperation *operation,
GtkPrintContext *context,

View file

@ -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;
}

47
uemis.c
View file

@ -16,10 +16,7 @@
#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);
}
}

View file

@ -10,6 +10,4 @@
void uemis_import();
extern GtkWidget *main_window;
#endif /* DIVE_H */