From 42365ede79ec34dba58447458bbc3f1c66cba7d1 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 1 Apr 2013 12:25:15 +0300 Subject: [PATCH 001/226] Setup Makefile for Qt/C++ builds Setup the build variables for building with QtWidgets, and add rules for processing the Q_OBJECT macros with moc and generate widget code from .ui files with uic. Signed-off-by: Alberto Mardegan --- .gitignore | 2 ++ Makefile | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 50d668f39..36c82543b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,9 @@ *.rej *.exe *.dmg +*.moc.cpp *.patch +*.ui.h *.xml version.h !dives/*.xml diff --git a/Makefile b/Makefile index b13caf7d6..3d3febd9a 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,15 @@ VERSION=3.0.2 CC=gcc CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE +CXX=g++ +CXXFLAGS=-Wall -g $(CLCFLAGS) INSTALL=install PKGCONFIG=pkg-config XML2CONFIG=xml2-config XSLCONFIG=xslt-config +QMAKE=qmake +MOC=moc +UIC=uic # these locations seem to work for SuSE and Fedora # prefix = $(HOME) @@ -96,6 +101,18 @@ endif # about it if it doesn't. LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null) +# Use qmake to find out which Qt version we are building for. +QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) +ifeq ($(QT_VERSION_MAJOR), 5) + QT_MODULES = Qt5Widgets + QT_CORE = Qt5Core +else + QT_MODULES = QtGui + QT_CORE = QtCore +endif +LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) +QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) + LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES) LIBDIVECOMPUTER = $(LIBDIVECOMPUTERARCHIVE) $(LIBUSB) @@ -130,6 +147,9 @@ ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0) OSSUPPORT = linux OSSUPPORT_CFLAGS = $(GTKCFLAGS) $(GCONF2CFLAGS) + ifneq ($(findstring reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))),) + CXXFLAGS += -fPIE + endif else ifeq ($(UNAME), darwin) OSSUPPORT = macos OSSUPPORT_CFLAGS = $(GTKCFLAGS) @@ -157,7 +177,7 @@ ifneq ($(strip $(LIBXSLT)),) XSLT=-DXSLT='"$(XSLTDIR)"' endif -LIBS = $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) +LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) MSGLANGS=$(notdir $(wildcard po/*.po)) MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) @@ -173,7 +193,7 @@ DEPS = $(wildcard .dep/*.dep) all: $(NAME) $(NAME): gen_version_file $(OBJS) $(MSGOBJS) $(INFOPLIST) - $(CC) $(LDFLAGS) -o $(NAME) $(OBJS) $(LIBS) + $(CXX) $(LDFLAGS) -o $(NAME) $(OBJS) $(LIBS) gen_version_file: ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING)) @@ -270,15 +290,37 @@ update-po-files: tx push -s tx pull -af -EXTRA_FLAGS = $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ +EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ $(XSLT) $(ZIP) $(SQLITE3) $(LIBDIVECOMPUTERCFLAGS) \ $(LIBSOUPCFLAGS) $(OSMGPSMAPFLAGS) $(GCONF2CFLAGS) +MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS)) + %.o: %.c @echo ' CC' $< @mkdir -p .dep @$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< +%.o: %.cpp + @echo ' CXX' $< + @mkdir -p .dep + @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< + +%.moc.cpp: %.h + @echo ' MOC' $< + @$(MOC) $(MOCFLAGS) $< -o $@ + +# This rule is for running the moc on QObject subclasses defined in the .cpp files; +# remember to #include ".moc.cpp" at the end of the .cpp file, or you'll +# get linker errors ("undefined vtable for...") +%.moc.cpp: %.cpp + @echo ' MOC' $< + @$(MOC) -i $(MOCFLAGS) $< -o $@ + +%.ui.h: ui/%.ui + @echo ' UIC' $< + @$(UIC) $< -o $@ + share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases mkdir -p $(dir $@) msgfmt -c -o $@ po/$*.po From 578d633d0148a13397f330aa91af1470843d73c1 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 1 Apr 2013 13:51:49 +0300 Subject: [PATCH 002/226] Have some C++ file in the project Rename gtk-gui.c to qt-gui.cpp, and make the necessary changes so that the project still builds. Signed-off-by: Alberto Mardegan --- Makefile | 2 +- callbacks-gtk.h | 2 +- device.h | 8 +++++ display-gtk.h | 8 +++++ display.h | 8 +++++ dive.h | 10 +++++- divelist.h | 9 ++++++ linux.c | 16 ++++----- macos.c | 14 ++++---- pref.h | 22 +++++++++---- gtk-gui.c => qt-gui.cpp | 72 +++++++++++++++++++++-------------------- webservice.h | 8 +++++ windows.c | 10 +++--- 13 files changed, 124 insertions(+), 65 deletions(-) rename gtk-gui.c => qt-gui.cpp (97%) diff --git a/Makefile b/Makefile index 3d3febd9a..a38586ff0 100644 --- a/Makefile +++ b/Makefile @@ -184,7 +184,7 @@ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.m OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o planner.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ - gtk-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ + qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) DEPS = $(wildcard .dep/*.dep) diff --git a/callbacks-gtk.h b/callbacks-gtk.h index 08c159b4d..568916f6c 100644 --- a/callbacks-gtk.h +++ b/callbacks-gtk.h @@ -9,7 +9,7 @@ static void name(GtkWidget *w, gpointer data) \ #define OPTIONCALLBACK(name, option) \ static void name(GtkWidget *w, gpointer data) \ { \ - GtkWidget **entry = data; \ + GtkWidget **entry = (GtkWidget**)data; \ option = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); \ update_screen(); \ if (entry) \ diff --git a/device.h b/device.h index 8a306ef78..636eb73c6 100644 --- a/device.h +++ b/device.h @@ -1,6 +1,10 @@ #ifndef DEVICE_INFO_H #define DEVICE_INFO_H +#ifdef __cplusplus +extern "C" { +#endif + struct device_info { const char *model; uint32_t deviceid; @@ -17,4 +21,8 @@ extern struct device_info *create_device_info(const char *model, uint32_t device extern struct device_info *remove_device_info(const char *model, uint32_t deviceid); extern struct device_info *head_of_device_info_list(void); +#ifdef __cplusplus +} +#endif + #endif diff --git a/display-gtk.h b/display-gtk.h index 7c1bcad4a..4cc86659f 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -8,6 +8,10 @@ #include #endif +#ifdef __cplusplus +extern "C" { +#endif + extern GtkWidget *main_window; /* we want a progress bar as part of the device_data_t - let's abstract this out */ @@ -117,4 +121,8 @@ GError *uemis_download(const char *path, progressbar_t *progress, GtkDialog *dia /* from planner.c */ extern void input_plan(void); +#ifdef __cplusplus +} +#endif + #endif diff --git a/display.h b/display.h index 8200770d7..d5c69e81b 100644 --- a/display.h +++ b/display.h @@ -3,6 +3,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + #define SCALE_SCREEN 1.0 #define SCALE_PRINT (1.0 / get_screen_dpi()) @@ -63,4 +67,8 @@ struct options { extern char zoomed_plot, dc_number; +#ifdef __cplusplus +} +#endif + #endif diff --git a/dive.h b/dive.h index 7d08828a8..4528ac1db 100644 --- a/dive.h +++ b/dive.h @@ -15,6 +15,10 @@ #include "sha1.h" +#ifdef __cplusplus +extern "C" { +#endif + #define O2_IN_AIR 209 // permille #define N2_IN_AIR 781 #define O2_DENSITY 1429 // mg/Liter @@ -523,7 +527,7 @@ static inline struct divecomputer *get_dive_dc(struct dive *dive, int nr) #define for_each_gps_location(_i,_x) \ for ((_i) = 0; ((_x) = get_gps_location(_i, &gps_location_table)) != NULL; (_i)++) -static inline struct dive *get_dive_by_diveid(int diveid, int deviceid) +static inline struct dive *get_dive_by_diveid(uint32_t diveid, uint32_t deviceid) { int i; struct dive *dive; @@ -700,6 +704,10 @@ extern char *debugfilename; extern FILE *debugfile; #endif +#ifdef __cplusplus +} +#endif + #include "pref.h" #endif /* DIVE_H */ diff --git a/divelist.h b/divelist.h index 856318e2d..c9ec973f7 100644 --- a/divelist.h +++ b/divelist.h @@ -1,6 +1,10 @@ #ifndef DIVELIST_H #define DIVELIST_H +#ifdef __cplusplus +extern "C" { +#endif + struct dive; extern void dive_list_update_dives(void); @@ -16,4 +20,9 @@ extern void select_prev_dive(void); extern void show_and_select_dive(struct dive *dive); extern double init_decompression(struct dive * dive); extern void export_all_dives_uddf_cb(); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/linux.c b/linux.c index 4add7bb01..bf7383822 100644 --- a/linux.c +++ b/linux.c @@ -9,7 +9,7 @@ const char system_divelist_default_font[] = "Sans 8"; GConfClient *gconf; -static char *gconf_name(char *name) +static char *gconf_name(const char *name) { static char buf[255] = "/apps/subsurface/"; @@ -23,32 +23,32 @@ void subsurface_open_conf(void) gconf = gconf_client_get_default(); } -void subsurface_unset_conf(char *name) +void subsurface_unset_conf(const char *name) { gconf_client_unset(gconf, gconf_name(name), NULL); } -void subsurface_set_conf(char *name, const char *value) +void subsurface_set_conf(const char *name, const char *value) { gconf_client_set_string(gconf, gconf_name(name), value, NULL); } -void subsurface_set_conf_bool(char *name, int value) +void subsurface_set_conf_bool(const char *name, int value) { gconf_client_set_bool(gconf, gconf_name(name), value > 0, NULL); } -void subsurface_set_conf_int(char *name, int value) +void subsurface_set_conf_int(const char *name, int value) { gconf_client_set_int(gconf, gconf_name(name), value , NULL); } -const void *subsurface_get_conf(char *name) +const char *subsurface_get_conf(const char *name) { return gconf_client_get_string(gconf, gconf_name(name), NULL); } -int subsurface_get_conf_bool(char *name) +int subsurface_get_conf_bool(const char *name) { GConfValue *val; gboolean ret; @@ -61,7 +61,7 @@ int subsurface_get_conf_bool(char *name) return ret; } -int subsurface_get_conf_int(char *name) +int subsurface_get_conf_int(const char *name) { int val = gconf_client_get_int(gconf, gconf_name(name), NULL); if(!val) diff --git a/macos.c b/macos.c index aee4c73ca..93bc00eea 100644 --- a/macos.c +++ b/macos.c @@ -30,29 +30,29 @@ void subsurface_open_conf(void) /* nothing at this time */ } -void subsurface_unset_conf(char *name) +void subsurface_unset_conf(const char *name) { CFPreferencesSetAppValue(CFSTR_VAR(name), NULL, SUBSURFACE_PREFERENCES); } -void subsurface_set_conf(char *name, const char *value) +void subsurface_set_conf(const char *name, const char *value) { CFPreferencesSetAppValue(CFSTR_VAR(name), CFSTR_VAR(value), SUBSURFACE_PREFERENCES); } -void subsurface_set_conf_bool(char *name, int value) +void subsurface_set_conf_bool(const char *name, int value) { CFPreferencesSetAppValue(CFSTR_VAR(name), value ? kCFBooleanTrue : kCFBooleanFalse, SUBSURFACE_PREFERENCES); } -void subsurface_set_conf_int(char *name, int value) +void subsurface_set_conf_int(const char *name, int value) { CFNumberRef numRef = CFNumberCreate(NULL, kCFNumberIntType, &value); CFPreferencesSetAppValue(CFSTR_VAR(name), numRef, SUBSURFACE_PREFERENCES); } -const void *subsurface_get_conf(char *name) +const char *subsurface_get_conf(char *name) { CFPropertyListRef strpref; @@ -62,7 +62,7 @@ const void *subsurface_get_conf(char *name) return strdup(CFStringGetCStringPtr(strpref, kCFStringEncodingMacRoman)); } -int subsurface_get_conf_bool(char *name) +int subsurface_get_conf_bool(const char *name) { Boolean boolpref, exists; @@ -72,7 +72,7 @@ int subsurface_get_conf_bool(char *name) return boolpref; } -int subsurface_get_conf_int(char *name) +int subsurface_get_conf_int(const char *name) { Boolean exists; CFIndex value; diff --git a/pref.h b/pref.h index 60fe1104c..d622ebf3a 100644 --- a/pref.h +++ b/pref.h @@ -1,6 +1,10 @@ #ifndef PREF_H #define PREF_H +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { gboolean cylinder; gboolean temperature; @@ -43,13 +47,13 @@ extern struct preferences prefs, default_prefs; #define PP_GRAPHS_ENABLED (prefs.pp_graphs.po2 || prefs.pp_graphs.pn2 || prefs.pp_graphs.phe) extern void subsurface_open_conf(void); -extern void subsurface_set_conf(char *name, const char *value); -extern void subsurface_set_conf_bool(char *name, gboolean value); -extern void subsurface_set_conf_int(char *name, int value); -extern void subsurface_unset_conf(char *name); -extern const void *subsurface_get_conf(char *name); -extern int subsurface_get_conf_bool(char *name); -extern int subsurface_get_conf_int(char *name); +extern void subsurface_set_conf(const char *name, const char *value); +extern void subsurface_set_conf_bool(const char *name, gboolean value); +extern void subsurface_set_conf_int(const char *name, int value); +extern void subsurface_unset_conf(const char *name); +extern const char *subsurface_get_conf(const char *name); +extern int subsurface_get_conf_bool(const char *name); +extern int subsurface_get_conf_int(const char *name); extern void subsurface_flush_conf(void); extern void subsurface_close_conf(void); @@ -59,4 +63,8 @@ extern const char *system_default_filename(); extern void load_preferences(void); extern void save_preferences(void); +#ifdef __cplusplus +} +#endif + #endif /* PREF_H */ diff --git a/gtk-gui.c b/qt-gui.cpp similarity index 97% rename from gtk-gui.c rename to qt-gui.cpp index 91a70acf0..54aa582b4 100644 --- a/gtk-gui.c +++ b/qt-gui.cpp @@ -27,12 +27,13 @@ #include #include -#include "subsurface-icon.h" #if HAVE_OSM_GPS_MAP #include #endif +static const GdkPixdata subsurface_icon_pixbuf = {}; + GtkWidget *main_window; GtkWidget *main_vbox; GtkWidget *error_info_bar; @@ -224,7 +225,7 @@ static gboolean ask_save_changes() GtkWidget *dialog, *label, *content; gboolean quit = TRUE; dialog = gtk_dialog_new_with_buttons(_("Save Changes?"), - GTK_WINDOW(main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_WINDOW(main_window), GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, GTK_STOCK_NO, GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, @@ -320,7 +321,7 @@ static void file_open(GtkWidget *w, gpointer data) fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); GError *error = NULL; - filename = fn_glist->data; + filename = (char *)fn_glist->data; parse_file(filename, &error, TRUE); if (error != NULL) { @@ -609,16 +610,16 @@ void update_screen() update_dive_list_col_visibility(); } -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) -UNITCALLBACK(set_kg, weight, KG) -UNITCALLBACK(set_lbs, weight, LBS) +UNITCALLBACK(set_meter, length, units::METERS) +UNITCALLBACK(set_feet, length, units::FEET) +UNITCALLBACK(set_bar, pressure, units::BAR) +UNITCALLBACK(set_psi, pressure, units::PSI) +UNITCALLBACK(set_liter, volume, units::LITER) +UNITCALLBACK(set_cuft, volume, units::CUFT) +UNITCALLBACK(set_celsius, temperature, units::CELSIUS) +UNITCALLBACK(set_fahrenheit, temperature, units::FAHRENHEIT) +UNITCALLBACK(set_kg, weight, units::KG) +UNITCALLBACK(set_lbs, weight, units::LBS) OPTIONCALLBACK(otu_toggle, prefs.visible_cols.otu) OPTIONCALLBACK(maxcns_toggle, prefs.visible_cols.maxcns) @@ -667,7 +668,7 @@ static gboolean gfhigh_edit(GtkWidget *w, GdkEvent *event, gpointer _data) static void event_toggle(GtkWidget *w, gpointer _data) { - gboolean *plot_ev = _data; + gboolean *plot_ev = (gboolean *)_data; *plot_ev = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); } @@ -710,7 +711,7 @@ static void pick_default_file(GtkWidget *w, GtkButton *button) list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(fs_dialog)); if (g_slist_length(list) == 1) - gtk_button_set_label(button, list->data); + gtk_button_set_label(button, (const gchar *)list->data); g_slist_free(list); } @@ -779,28 +780,28 @@ static void preferences_dialog(GtkWidget *w, gpointer data) gtk_container_add(GTK_CONTAINER(frame), box); create_radio(box, _("Depth:"), - _("Meter"), set_meter, (prefs.units.length == METERS), - _("Feet"), set_feet, (prefs.units.length == FEET), + _("Meter"), set_meter, (prefs.units.length == units::METERS), + _("Feet"), set_feet, (prefs.units.length == units::FEET), NULL); create_radio(box, _("Pressure:"), - _("Bar"), set_bar, (prefs.units.pressure == BAR), - _("PSI"), set_psi, (prefs.units.pressure == PSI), + _("Bar"), set_bar, (prefs.units.pressure == units::BAR), + _("PSI"), set_psi, (prefs.units.pressure == units::PSI), NULL); create_radio(box, _("Volume:"), - _("Liter"), set_liter, (prefs.units.volume == LITER), - _("CuFt"), set_cuft, (prefs.units.volume == CUFT), + _("Liter"), set_liter, (prefs.units.volume == units::LITER), + _("CuFt"), set_cuft, (prefs.units.volume == units::CUFT), NULL); create_radio(box, _("Temperature:"), - _("Celsius"), set_celsius, (prefs.units.temperature == CELSIUS), - _("Fahrenheit"), set_fahrenheit, (prefs.units.temperature == FAHRENHEIT), + _("Celsius"), set_celsius, (prefs.units.temperature == units::CELSIUS), + _("Fahrenheit"), set_fahrenheit, (prefs.units.temperature == units::FAHRENHEIT), NULL); create_radio(box, _("Weight:"), - _("kg"), set_kg, (prefs.units.weight == KG), - _("lbs"), set_lbs, (prefs.units.weight == LBS), + _("kg"), set_kg, (prefs.units.weight == units::KG), + _("lbs"), set_lbs, (prefs.units.weight == units::LBS), NULL); frame = gtk_frame_new(_("Show Columns")); @@ -1082,7 +1083,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) static void create_toggle(const char* label, int *on, void *_data) { - GtkWidget *button, *table = _data; + GtkWidget *button, *table = GTK_WIDGET(_data); int rows, cols, x, y; static int count; @@ -1345,7 +1346,7 @@ static void edit_dc_delete_rows(GtkTreeView *view) selected_rows = gtk_tree_selection_get_selected_rows(selection, &model); for (list = selected_rows; list; list = g_list_next(list)) { - path = list->data; + path = (GtkTreePath *)list->data; ref = gtk_tree_row_reference_new(model, path); row_references = g_list_append(row_references, ref); } @@ -1441,7 +1442,7 @@ static void edit_dc_nicknames(GtkWidget *w, gpointer data) dialog = gtk_dialog_new_with_buttons(_("Edit Dive Computer Nicknames"), GTK_WINDOW(main_window), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_STOCK_DELETE, SUB_RESPONSE_DELETE, GTK_STOCK_CANCEL, @@ -1509,7 +1510,7 @@ static void edit_dc_nicknames(GtkWidget *w, gpointer data) if (res == SUB_RESPONSE_DELETE) { confirm = gtk_dialog_new_with_buttons(_("Delete a dive computer information entry"), GTK_WINDOW(dialog), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_STOCK_YES, GTK_RESPONSE_YES, GTK_STOCK_NO, @@ -1855,7 +1856,8 @@ static int tooltips; void attach_tooltip(int x, int y, int w, int h, const char *text, struct event *event) { cairo_rectangle_t *rect; - tooltip_rects = realloc(tooltip_rects, (tooltips + 1) * sizeof(tooltip_record_t)); + tooltip_rects = (tooltip_record_t *) + realloc(tooltip_rects, (tooltips + 1) * sizeof(tooltip_record_t)); rect = &tooltip_rects[tooltips].rect; rect->x = x; rect->y = y; @@ -1975,7 +1977,7 @@ static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) { GtkAllocation allocation; - static struct graphics_context gc = { .printer = 0 }; + static struct graphics_context gc = { 0 }; /* the drawing area gives TOTAL width * height - x,y is used as the topx/topy offset * so effective drawing area is width-2x * height-2y */ @@ -2021,7 +2023,7 @@ static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer static void add_gas_change_cb(GtkWidget *menuitem, gpointer data) { - double *x = data; + double *x = (double *)data; int when = x_to_time(*x); int cylnr = select_cylinder(current_dive, when); if (cylnr >= 0) { @@ -2061,7 +2063,7 @@ int confirm_dialog(int when, char *action_text, char *event_text) static void add_bookmark_cb(GtkWidget *menuitem, gpointer data) { - double *x = data; + double *x = (double *)data; int when = x_to_time(*x); if (confirm_dialog(when, _("Add"), _("bookmark"))){ @@ -2089,7 +2091,7 @@ static struct event *event_at_x(double rel_x) static void remove_event_cb(GtkWidget *menuitem, gpointer data) { - struct event *event = data; + struct event *event = (struct event *)data; if (confirm_dialog(event->time.seconds, _("Remove"), _(event->name))){ struct event **ep = ¤t_dc->events; while (ep && *ep != event) @@ -2204,7 +2206,7 @@ static GtkWidget *dive_profile_widget(void) static void do_import_file(gpointer data, gpointer user_data) { GError *error = NULL; - parse_file(data, &error, FALSE); + parse_file((const char *)data, &error, FALSE); if (error != NULL) { diff --git a/webservice.h b/webservice.h index bb3a71f3b..ee07e9438 100644 --- a/webservice.h +++ b/webservice.h @@ -1,3 +1,11 @@ +#ifdef __cplusplus +extern "C" { +#endif + extern void webservice_download_dialog(void); extern gboolean webservice_request_user_xml(const gchar *, gchar **, guint *, guint *); extern int divelogde_upload(char *fn); + +#ifdef __cplusplus +} +#endif diff --git a/windows.c b/windows.c index f06ffc7a8..23b6d2f4c 100644 --- a/windows.c +++ b/windows.c @@ -20,12 +20,12 @@ void subsurface_open_conf(void) printf("CreateKey Software\\subsurface failed %ld\n", success); } -void subsurface_unset_conf(char *name) +void subsurface_unset_conf(const char *name) { RegDeleteValue(hkey, (LPCTSTR)name); } -void subsurface_set_conf(char *name, const char *value) +void subsurface_set_conf(const char *name, const char *value) { /* since we are using the pointer 'value' as both an actual * pointer to the string setting and as a way to pass the @@ -52,17 +52,17 @@ void subsurface_set_conf(char *name, const char *value) free(wname); } -void subsurface_set_conf_int(char *name, int value) +void subsurface_set_conf_int(const char *name, int value) { RegSetValueEx(hkey, (LPCTSTR)name, 0, REG_DWORD, (const BYTE *)&value, 4); } -void subsurface_set_conf_bool(char *name, int value) +void subsurface_set_conf_bool(const char *name, int value) { subsurface_set_conf_int(name, value); } -const void *subsurface_get_conf(char *name) +const char *subsurface_get_conf(const char *name) { const int csize = 64; int blen = 0; From 7ea22811802cda1df227e7fb4b76720fc15b2dcd Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 1 Apr 2013 13:57:51 +0300 Subject: [PATCH 003/226] Introduce QApplication Instantiate a QApplication and let Qt handle the event loop. Add a QTranslator subclass to translate the UI via gettext. Signed-off-by: Alberto Mardegan --- Makefile | 2 ++ qt-gui.cpp | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a38586ff0..a9da8dd1a 100644 --- a/Makefile +++ b/Makefile @@ -317,6 +317,8 @@ MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $ @echo ' MOC' $< @$(MOC) -i $(MOCFLAGS) $< -o $@ +qt-gui.o: qt-gui.moc.cpp + %.ui.h: ui/%.ui @echo ' UIC' $< @$(UIC) $< -o $@ diff --git a/qt-gui.cpp b/qt-gui.cpp index 54aa582b4..3285d8e27 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -27,11 +27,36 @@ #include #include +#include +#include #if HAVE_OSM_GPS_MAP #include #endif +class Translator: public QTranslator +{ + Q_OBJECT + +public: + Translator(QObject *parent = 0); + ~Translator() {} + + virtual QString translate(const char *context, const char *sourceText, + const char *disambiguation = NULL) const; +}; + +Translator::Translator(QObject *parent): + QTranslator(parent) +{ +} + +QString Translator::translate(const char *context, const char *sourceText, + const char *disambiguation) const +{ + return QString::fromUtf8(gettext(sourceText)); +} + static const GdkPixdata subsurface_icon_pixbuf = {}; GtkWidget *main_window; @@ -40,6 +65,7 @@ GtkWidget *error_info_bar; GtkWidget *error_label; GtkWidget *vpane, *hpane; GtkWidget *notebook; +static QApplication *application = NULL; int error_count; const char *existing_filename; @@ -1719,6 +1745,9 @@ void init_ui(int *argcp, char ***argvp) GtkSettings *settings; GtkUIManager *ui_manager; + application = new QApplication(*argcp, *argvp); + application->installTranslator(new Translator(application)); + gtk_init(argcp, argvp); settings = gtk_settings_get_default(); gtk_settings_set_long_property(settings, "gtk-tooltip-timeout", 10, "subsurface setting"); @@ -1832,11 +1861,12 @@ void init_ui(int *argcp, char ***argvp) void run_ui(void) { - gtk_main(); + application->exec(); } void exit_ui(void) { + delete application; subsurface_close_conf(); if (existing_filename) free((void *)existing_filename); @@ -2363,3 +2393,5 @@ gdouble get_screen_dpi(void) gdouble dpi_h = floor((h / h_mm) * mm_per_inch); return dpi_h; } + +#include "qt-gui.moc.cpp" From f2ab8f42ee5bbbe643b4810bf663d4f58835644b Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Mon, 1 Apr 2013 17:55:50 +0200 Subject: [PATCH 004/226] Match subsurface_get_conf* in windows/macos.c with linux.c The signatures for subsurface_get_conf* in windows.c and macos.c was slightly different from those in linux.c, which broke the build (at least on Mac). Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- macos.c | 2 +- windows.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/macos.c b/macos.c index 93bc00eea..38e58f54b 100644 --- a/macos.c +++ b/macos.c @@ -52,7 +52,7 @@ void subsurface_set_conf_int(const char *name, int value) CFPreferencesSetAppValue(CFSTR_VAR(name), numRef, SUBSURFACE_PREFERENCES); } -const char *subsurface_get_conf(char *name) +const char *subsurface_get_conf(const char *name) { CFPropertyListRef strpref; diff --git a/windows.c b/windows.c index 23b6d2f4c..94e3dc546 100644 --- a/windows.c +++ b/windows.c @@ -100,7 +100,7 @@ const char *subsurface_get_conf(const char *name) return utf8_string; } -int subsurface_get_conf_int(char *name) +int subsurface_get_conf_int(const char *name) { DWORD value = -1, len = 4; LONG ret = RegQueryValueEx(hkey, (LPCTSTR)TEXT(name), NULL, NULL, @@ -110,7 +110,7 @@ int subsurface_get_conf_int(char *name) return value; } -int subsurface_get_conf_bool(char *name) +int subsurface_get_conf_bool(const char *name) { int ret = subsurface_get_conf_int(name); if (ret == -1) From b5b14f1d95b0362a13fa3f11ac3f334012750099 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 1 Apr 2013 23:03:47 +0300 Subject: [PATCH 005/226] Define QT_NO_KEYWORDS This prevents Qt headers from defining macro symbols such as "emit" and "signals", therefore avoiding conflicts when struct members with the same names are defined in some C header. Signed-off-by: Alberto Mardegan --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a9da8dd1a..aeb3cca0f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ VERSION=3.0.2 CC=gcc CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE CXX=g++ -CXXFLAGS=-Wall -g $(CLCFLAGS) +CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS INSTALL=install PKGCONFIG=pkg-config XML2CONFIG=xml2-config From 40e3671bd2656be9657abe8c6d955cef612805c5 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 1 Apr 2013 23:08:13 +0300 Subject: [PATCH 006/226] Improve Makefile rules for running moc The previous rules were conflicting, and the naming of the moc-generated file to be included in .cpp files was deviating from what's most used in Qt: the usual way is to #include "file.moc" and not #include "file.moc.cpp" Signed-off-by: Alberto Mardegan --- Makefile | 15 ++++++++++----- qt-gui.cpp | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index aeb3cca0f..c0114386c 100644 --- a/Makefile +++ b/Makefile @@ -306,18 +306,23 @@ MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $ @mkdir -p .dep @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< +# This rule is for running the moc on QObject subclasses defined in the .h +# files. +# To activate this rule, add .moc.o to the OBJS variable. %.moc.cpp: %.h @echo ' MOC' $< @$(MOC) $(MOCFLAGS) $< -o $@ -# This rule is for running the moc on QObject subclasses defined in the .cpp files; -# remember to #include ".moc.cpp" at the end of the .cpp file, or you'll -# get linker errors ("undefined vtable for...") -%.moc.cpp: %.cpp +# This rule is for running the moc on QObject subclasses defined in the .cpp +# files; remember to #include ".moc" at the end of the .cpp file, or +# you'll get linker errors ("undefined vtable for...") +# To activate this rule, you need another rule on the .o file, like: +# file.o: file.moc +%.moc: %.cpp @echo ' MOC' $< @$(MOC) -i $(MOCFLAGS) $< -o $@ -qt-gui.o: qt-gui.moc.cpp +qt-gui.o: qt-gui.moc %.ui.h: ui/%.ui @echo ' UIC' $< diff --git a/qt-gui.cpp b/qt-gui.cpp index 3285d8e27..0fbcf2555 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -2394,4 +2394,4 @@ gdouble get_screen_dpi(void) return dpi_h; } -#include "qt-gui.moc.cpp" +#include "qt-gui.moc" From 2f759f52e12e2e065f6022797dc51106a7093899 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Tue, 2 Apr 2013 18:46:41 +0300 Subject: [PATCH 007/226] Move set_filename() calls outside of parse_file() Remove the boolean parameter from parse_file; the code is more readable by having an explicit call to set_filename() where necessary, rather than a boolean parameter. Signed-off-by: Alberto Mardegan --- dive.h | 2 +- file.c | 12 +----------- main.c | 10 +++++++--- qt-gui.cpp | 5 +++-- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/dive.h b/dive.h index 4528ac1db..5efde438d 100644 --- a/dive.h +++ b/dive.h @@ -555,7 +555,7 @@ extern void set_filename(const char *filename, gboolean force); extern int parse_dm4_buffer(const char *url, const char *buf, int size, struct dive_table *table, GError **error); -extern void parse_file(const char *filename, GError **error, gboolean possible_default_filename); +extern void parse_file(const char *filename, GError **error); extern void show_dive_info(struct dive *); diff --git a/file.c b/file.c index 8deb2fe97..401bd5c36 100644 --- a/file.c +++ b/file.c @@ -263,7 +263,7 @@ static void parse_file_buffer(const char *filename, struct memblock *mem, GError parse_xml_buffer(filename, mem->buffer, mem->size, &dive_table, error); } -void parse_file(const char *filename, GError **error, gboolean possible_default_filename) +void parse_file(const char *filename, GError **error) { struct memblock mem; #ifdef SQLITE3 @@ -283,19 +283,9 @@ void parse_file(const char *filename, GError **error, gboolean possible_default_ filename); } - /* - * We do *not* want to leave the old default_filename - * just because the open failed. - */ - if (possible_default_filename) - set_filename(filename, TRUE); - return; } - if (possible_default_filename) - set_filename(filename, TRUE); - #ifdef SQLITE3 fmt = strrchr(filename, '.'); if (fmt && (!strcasecmp(fmt + 1, "DB") || !strcasecmp(fmt + 1, "BAK"))) { diff --git a/main.c b/main.c index 3b6e00449..3c0ea7381 100644 --- a/main.c +++ b/main.c @@ -348,8 +348,12 @@ int main(int argc, char **argv) /* if we have exactly one filename, parse_file will set * that to be the default. Otherwise there will be no default filename */ set_filename(NULL, TRUE); - parse_file(a, &error, no_filenames); - no_filenames = FALSE; + parse_file(a, &error); + if (no_filenames) + { + set_filename(a, TRUE); + no_filenames = FALSE; + } if (error != NULL) { report_error(error); @@ -360,7 +364,7 @@ int main(int argc, char **argv) if (no_filenames) { GError *error = NULL; const char *filename = prefs.default_filename; - parse_file(filename, &error, TRUE); + parse_file(filename, &error); /* don't report errors - this file may not exist, but make sure we remember this as the filename in use */ set_filename(filename, FALSE); diff --git a/qt-gui.cpp b/qt-gui.cpp index 0fbcf2555..2e3f2c903 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -348,7 +348,8 @@ static void file_open(GtkWidget *w, gpointer data) GError *error = NULL; filename = (char *)fn_glist->data; - parse_file(filename, &error, TRUE); + parse_file(filename, &error); + set_filename(filename, TRUE); if (error != NULL) { report_error(error); @@ -2236,7 +2237,7 @@ static GtkWidget *dive_profile_widget(void) static void do_import_file(gpointer data, gpointer user_data) { GError *error = NULL; - parse_file((const char *)data, &error, FALSE); + parse_file((const char *)data, &error); if (error != NULL) { From a412753b0a2eb6323f350e98287b004f5b3b6c5c Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Tue, 2 Apr 2013 19:49:17 +0300 Subject: [PATCH 008/226] Add a Qt main window This is just an empty window with a File menu and a few items. It shows how to hook up functions to menu actions. Signed-off-by: Alberto Mardegan --- Makefile | 2 +- qt-gui.cpp | 120 ++++++++++++++++++++++++++++++++++++++++++++++ ui/main-window.ui | 101 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 ui/main-window.ui diff --git a/Makefile b/Makefile index c0114386c..59e2f9522 100644 --- a/Makefile +++ b/Makefile @@ -322,7 +322,7 @@ MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $ @echo ' MOC' $< @$(MOC) -i $(MOCFLAGS) $< -o $@ -qt-gui.o: qt-gui.moc +qt-gui.o: main-window.ui.h qt-gui.moc %.ui.h: ui/%.ui @echo ' UIC' $< diff --git a/qt-gui.cpp b/qt-gui.cpp index 2e3f2c903..285f2082d 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -24,10 +24,14 @@ #include "webservice.h" #include "version.h" #include "libdivecomputer.h" +#include "main-window.ui.h" #include #include #include +#include +#include +#include #include #if HAVE_OSM_GPS_MAP @@ -1733,6 +1737,120 @@ static gboolean notebook_tooltip (GtkWidget *widget, gint x, gint y, } } +class MainWindow: public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow() {} + + void setCurrentFileName(const QString &fileName); + +private Q_SLOTS: + void on_actionNew_triggered() { on_actionClose_triggered(); } + void on_actionOpen_triggered(); + void on_actionSave_triggered() { file_save(NULL, NULL); } + void on_actionSaveAs_triggered() { file_save_as(NULL, NULL); } + void on_actionClose_triggered(); + +private: + QStringList fileNameFilters() const; + +private: + QString m_currentFileName; +}; + +MainWindow::MainWindow(QWidget *parent): + QMainWindow(parent) +{ + setupUi(this); +} + +void MainWindow::setCurrentFileName(const QString &fileName) +{ + if (fileName == m_currentFileName) return; + m_currentFileName = fileName; + + QString title = tr("Subsurface"); + if (!m_currentFileName.isEmpty()) { + QFileInfo fileInfo(m_currentFileName); + title += " - " + fileInfo.fileName(); + } + setWindowTitle(title); +} + +void MainWindow::on_actionOpen_triggered() +{ + QString defaultFileName = QString::fromUtf8(prefs.default_filename); + QFileInfo fileInfo(defaultFileName); + + QFileDialog dialog(this, tr("Open File"), fileInfo.path()); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.selectFile(defaultFileName); + dialog.setNameFilters(fileNameFilters()); + if (dialog.exec()) { + /* first, close the existing file, if any */ + file_close(NULL, NULL); + + /* we know there is only one filename */ + QString fileName = dialog.selectedFiles().first(); + GError *error = NULL; + parse_file(fileName.toUtf8().constData(), &error); + if (error != NULL) { + report_error(error); + g_error_free(error); + error = NULL; + } else { + setCurrentFileName(fileName); + } + report_dives(FALSE, FALSE); + } +} + +void MainWindow::on_actionClose_triggered() +{ + if (unsaved_changes()) + if (ask_save_changes() == FALSE) + return; + + setCurrentFileName(QString()); + + /* free the dives and trips */ + while (dive_table.nr) + delete_single_dive(0); + mark_divelist_changed(FALSE); + + /* clear the selection and the statistics */ + selected_dive = 0; + process_selected_dives(); + clear_stats_widgets(); + clear_events(); + show_dive_stats(NULL); + + /* clear the equipment page */ + clear_equipment_widgets(); + + /* redraw the screen */ + dive_list_update_dives(); + show_dive_info(NULL); +} + +QStringList MainWindow::fileNameFilters() const +{ + QStringList filters; + + filters << "*.xml *.uddf *.udcf *.jlb" +#ifdef LIBZIP + " *.sde *.dld" +#endif +#ifdef SQLITE3 + " *.db" +#endif + ; + return filters; +} + void init_ui(int *argcp, char ***argvp) { GtkWidget *win; @@ -1748,6 +1866,8 @@ void init_ui(int *argcp, char ***argvp) application = new QApplication(*argcp, *argvp); application->installTranslator(new Translator(application)); + MainWindow *window = new MainWindow(); + window->show(); gtk_init(argcp, argvp); settings = gtk_settings_get_default(); diff --git a/ui/main-window.ui b/ui/main-window.ui new file mode 100644 index 000000000..58ed198fd --- /dev/null +++ b/ui/main-window.ui @@ -0,0 +1,101 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + Subsurface + + + + + + 0 + 0 + 800 + 23 + + + + + File + + + + + + + + + + + + + + + + + + New + + + Ctrl+N + + + + + + + + + + Open... + + + Ctrl+O + + + + + + + + Save... + + + Ctrl+S + + + + + + + + Save As... + + + Ctrl+Shift+S + + + + + + + + Close + + + Ctrl+W + + + + + + From 1d61955be973cfcc67f4e82a65487721a9ae84b8 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 6 Apr 2013 20:49:06 -0700 Subject: [PATCH 009/226] Separate Gtk related code from core logic: divelist This is simplistic & brute force: any function that touches Gtk related data structures is moved to divelist-gtk.c, everything else stays in divelist.c. Header files have been adjusted so that this still compiles and appears to work. More thought is needed to truly abstract this out, but this seems to be a good point to commit this change. Signed-off-by: Dirk Hohndel --- Makefile | 2 +- display-gtk.h | 2 +- dive.h | 2 +- divelist-gtk.c | 2325 +++++++++++++++++++++++++++++++++++++++++++ divelist.c | 2349 +------------------------------------------- divelist.h | 22 +- uemis-downloader.c | 4 +- 7 files changed, 2401 insertions(+), 2305 deletions(-) create mode 100644 divelist-gtk.c diff --git a/Makefile b/Makefile index 59e2f9522..aa42db2cf 100644 --- a/Makefile +++ b/Makefile @@ -182,7 +182,7 @@ LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIB MSGLANGS=$(notdir $(wildcard po/*.po)) MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o planner.o \ +OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o planner.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) diff --git a/display-gtk.h b/display-gtk.h index 4cc86659f..904d5efce 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -81,7 +81,7 @@ extern GtkWidget *create_label(const char *fmt, ...); extern gboolean icon_click_cb(GtkWidget *w, GdkEventButton *event, gpointer data); -unsigned int amount_selected; +extern unsigned int amount_selected; extern void process_selected_dives(void); diff --git a/dive.h b/dive.h index 5efde438d..aff856856 100644 --- a/dive.h +++ b/dive.h @@ -414,7 +414,7 @@ static inline int rel_mbar_to_depth(int mbar, struct dive *dive) * be able to edit a dive without unintended side effects */ extern struct dive edit_dive; -extern gboolean autogroup; +extern short autogroup; /* random threashold: three days without diving -> new trip * this works very well for people who usually dive as part of a trip and don't * regularly dive at a local facility; this is why trips are an optional feature */ diff --git a/divelist-gtk.c b/divelist-gtk.c new file mode 100644 index 000000000..8587e11c6 --- /dev/null +++ b/divelist-gtk.c @@ -0,0 +1,2325 @@ +/* divelist-gtk.c */ +/* this creates the UI for the dive list - + * controlled through the following interfaces: + * + * GdkPixbuf *get_gps_icon(void) + * void flush_divelist(struct dive *dive) + * void set_divelist_font(const char *font) + * void dive_list_update_dives(void) + * void update_dive_list_units(void) + * void update_dive_list_col_visibility(void) + * void dive_list_update_dives(void) + * void add_dive_cb(GtkWidget *menuitem, gpointer data) + * gboolean icon_click_cb(GtkWidget *w, GdkEventButton *event, gpointer data) + * void export_all_dives_uddf_cb() + * GtkWidget dive_list_create(void) + * void dive_list_destroy(void) + * void show_and_select_dive(struct dive *dive) + * void select_next_dive(void) + * void select_prev_dive(void) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef LIBZIP +#include +#endif +#ifdef XSLT +#include +#endif + +#include "dive.h" +#include "divelist.h" +#include "display.h" +#include "display-gtk.h" +#include "webservice.h" + +#include +#include "satellite.h" + +struct DiveList { + GtkWidget *tree_view; + GtkWidget *container_widget; + GtkTreeStore *model, *listmodel, *treemodel; + GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; + GtkTreeViewColumn *temperature, *cylinder, *totalweight, *suit, *nitrox, *sac, *otu, *maxcns; + int changed; +}; + +static struct DiveList dive_list; +#define MODEL(_dl) GTK_TREE_MODEL((_dl).model) +#define TREEMODEL(_dl) GTK_TREE_MODEL((_dl).treemodel) +#define LISTMODEL(_dl) GTK_TREE_MODEL((_dl).listmodel) +#define STORE(_dl) GTK_TREE_STORE((_dl).model) +#define TREESTORE(_dl) GTK_TREE_STORE((_dl).treemodel) +#define LISTSTORE(_dl) GTK_TREE_STORE((_dl).listmodel) + +dive_trip_t *dive_trip_list; +short autogroup = FALSE; +static gboolean in_set_cursor = FALSE; +static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data); + +/* + * The dive list has the dive data in both string format (for showing) + * and in "raw" format (for sorting purposes) + */ +enum { + DIVE_INDEX = 0, + DIVE_NR, /* int: dive->nr */ + DIVE_DATE, /* timestamp_t: dive->when */ + DIVE_RATING, /* int: 0-5 stars */ + DIVE_DEPTH, /* int: dive->maxdepth in mm */ + DIVE_DURATION, /* int: in seconds */ + DIVE_TEMPERATURE, /* int: in mkelvin */ + DIVE_TOTALWEIGHT, /* int: in grams */ + DIVE_SUIT, /* "wet, 3mm" */ + DIVE_CYLINDER, + DIVE_NITROX, /* int: dummy */ + DIVE_SAC, /* int: in ml/min */ + DIVE_OTU, /* int: in OTUs */ + DIVE_MAXCNS, /* int: in % */ + DIVE_LOCATION, /* "2nd Cathedral, Lanai" */ + DIVE_LOC_ICON, /* pixbuf for gps icon */ + DIVELIST_COLUMNS +}; + +static void merge_dive_into_trip_above_cb(GtkWidget *menuitem, GtkTreePath *path); + +#ifdef DEBUG_MODEL +static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + char *location; + int idx, nr, duration; + struct dive *dive; + timestamp_t when; + struct tm tm; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_DATE, &when, + DIVE_DURATION, &duration, DIVE_LOCATION, &location, -1); + utc_mkdate(when, &tm); + printf("iter %x:%x entry #%d : nr %d @ %04d-%02d-%02d %02d:%02d:%02d duration %d location %s ", + iter->stamp, iter->user_data, idx, nr, + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + duration, location); + dive = get_dive(idx); + if (dive) + printf("tripflag %d\n", dive->tripflag); + else + printf("without matching dive\n"); + + free(location); + + return FALSE; +} + +static void dump_model(GtkListStore *store) +{ + gtk_tree_model_foreach(GTK_TREE_MODEL(store), dump_model_entry, NULL); + printf("\n---\n\n"); +} +#endif + +/* when subsurface starts we want to have the last dive selected. So we simply + walk to the first leaf (and skip the summary entries - which have negative + DIVE_INDEX) */ +static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) +{ + GtkTreeIter parent; + GtkTreePath *tpath; + + while (*diveidx < 0) { + memcpy(&parent, iter, sizeof(parent)); + tpath = gtk_tree_model_get_path(model, &parent); + if (!gtk_tree_model_iter_children(model, iter, &parent)) { + /* we should never have a parent without child */ + gtk_tree_path_free(tpath); + return; + } + if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) + gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); + gtk_tree_path_free(tpath); + gtk_tree_model_get(model, iter, DIVE_INDEX, diveidx, -1); + } +} + +static struct dive *dive_from_path(GtkTreePath *path) +{ + GtkTreeIter iter; + int idx; + + if (gtk_tree_model_get_iter(MODEL(dive_list), &iter, path)) { + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + return get_dive(idx); + } else { + return NULL; + } + +} + +static int get_path_index(GtkTreePath *path) +{ + GtkTreeIter iter; + int idx; + + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + return idx; +} + +/* make sure that if we expand a summary row that is selected, the children show + up as selected, too */ +static void row_expanded_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) +{ + GtkTreeIter child; + GtkTreeModel *model = MODEL(dive_list); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + dive_trip_t *trip; + + trip = find_trip_by_idx(get_path_index(path)); + if (!trip) + return; + + trip->expanded = 1; + if (!gtk_tree_model_iter_children(model, &child, iter)) + return; + + do { + int idx; + struct dive *dive; + + gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + + if (dive->selected) + gtk_tree_selection_select_iter(selection, &child); + else + gtk_tree_selection_unselect_iter(selection, &child); + } while (gtk_tree_model_iter_next(model, &child)); +} + +/* Make sure that if we collapse a summary row with any selected children, the row + shows up as selected too */ +static void row_collapsed_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) +{ + dive_trip_t *trip; + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + + trip = find_trip_by_idx(get_path_index(path)); + if (!trip) + return; + + trip->expanded = 0; + if (trip_has_selected_dives(trip)) { + gtk_tree_selection_select_iter(selection, iter); + trip->selected = 1; + } +} + +const char *star_strings[] = { + ZERO_STARS, + ONE_STARS, + TWO_STARS, + THREE_STARS, + FOUR_STARS, + FIVE_STARS +}; + +static void star_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int nr_stars, idx; + char buffer[40]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_RATING, &nr_stars, -1); + if (idx < 0) { + *buffer = '\0'; + } else { + if (nr_stars < 0 || nr_stars > 5) + nr_stars = 0; + snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]); + } + g_object_set(renderer, "text", buffer, NULL); +} + +static void date_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int idx, nr; + struct tm tm; + timestamp_t when; + /* this should be enought for most languages. if not increase the value. */ + char buffer[256]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1); + nr = gtk_tree_model_iter_n_children(model, iter); + + utc_mkdate(when, &tm); + if (idx < 0) { + snprintf(buffer, sizeof(buffer), + /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, nr dives */ + ngettext("Trip %1$s, %2$s %3$d, %4$d (%5$d dive)", + "Trip %1$s, %2$s %3$d, %4$d (%5$d dives)", nr), + weekday(tm.tm_wday), + monthname(tm.tm_mon), + tm.tm_mday, tm.tm_year + 1900, + nr); + } else { + snprintf(buffer, sizeof(buffer), + /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, hour:min */ + _("%1$s, %2$s %3$d, %4$d %5$02d:%6$02d"), + weekday(tm.tm_wday), + monthname(tm.tm_mon), + tm.tm_mday, tm.tm_year + 1900, + tm.tm_hour, tm.tm_min); + } + g_object_set(renderer, "text", buffer, NULL); +} + +static void depth_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int depth, integer, frac, len, idx; + char buffer[40]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1); + + if (idx < 0) { + *buffer = '\0'; + } else { + switch (prefs.units.length) { + case METERS: + /* To tenths of meters */ + depth = (depth + 49) / 100; + integer = depth / 10; + frac = depth % 10; + if (integer < 20) + break; + if (frac >= 5) + integer++; + frac = -1; + break; + case FEET: + integer = mm_to_feet(depth) + 0.5; + frac = -1; + break; + default: + return; + } + len = snprintf(buffer, sizeof(buffer), "%d", integer); + if (frac >= 0) + len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac); + } + g_object_set(renderer, "text", buffer, NULL); +} + +static void duration_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + unsigned int sec; + int idx; + char buffer[40]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DURATION, &sec, -1); + if (idx < 0) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); + + g_object_set(renderer, "text", buffer, NULL); +} + +static void temperature_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int value, idx; + char buffer[80]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_TEMPERATURE, &value, -1); + + *buffer = 0; + if (idx >= 0 && value) { + double deg; + switch (prefs.units.temperature) { + case CELSIUS: + deg = mkelvin_to_C(value); + break; + case FAHRENHEIT: + deg = mkelvin_to_F(value); + break; + default: + return; + } + snprintf(buffer, sizeof(buffer), "%.1f", deg); + } + + g_object_set(renderer, "text", buffer, NULL); +} + +static void gpsicon_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int idx; + GdkPixbuf *icon; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_LOC_ICON, &icon, -1); + g_object_set(renderer, "pixbuf", icon, NULL); +} + +static void nr_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int idx, nr; + char buffer[40]; + struct dive *dive; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, -1); + if (idx < 0) { + *buffer = '\0'; + } else { + /* make dives that are not in trips stand out */ + dive = get_dive(idx); + if (!DIVE_IN_TRIP(dive)) + snprintf(buffer, sizeof(buffer), "%d", nr); + else + snprintf(buffer, sizeof(buffer), "%d", nr); + } + g_object_set(renderer, "markup", buffer, NULL); +} + +static void weight_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int indx, decimals; + double value; + char buffer[80]; + struct dive *dive; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &indx, -1); + dive = get_dive(indx); + value = get_weight_units(total_weight(dive), &decimals, NULL); + if (value == 0.0) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%.*f", decimals, value); + + g_object_set(renderer, "text", buffer, NULL); +} + +static gint nitrox_sort_func(GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data) +{ + int index_a, index_b; + struct dive *a, *b; + int a_o2, b_o2; + int a_he, b_he; + int a_o2low, b_o2low; + + gtk_tree_model_get(model, iter_a, DIVE_INDEX, &index_a, -1); + gtk_tree_model_get(model, iter_b, DIVE_INDEX, &index_b, -1); + a = get_dive(index_a); + b = get_dive(index_b); + get_dive_gas(a, &a_o2, &a_he, &a_o2low); + get_dive_gas(b, &b_o2, &b_he, &b_o2low); + + /* Sort by Helium first, O2 second */ + if (a_he == b_he) { + if (a_o2 == b_o2) + return a_o2low - b_o2low; + return a_o2 - b_o2; + } + return a_he - b_he; +} + +#define UTF8_ELLIPSIS "\xE2\x80\xA6" + +static void nitrox_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int idx, o2, he, o2low; + char buffer[80]; + struct dive *dive; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + *buffer = '\0'; + goto exit; + } + dive = get_dive(idx); + get_dive_gas(dive, &o2, &he, &o2low); + o2 = (o2 + 5) / 10; + he = (he + 5) / 10; + o2low = (o2low + 5) / 10; + + if (he) + snprintf(buffer, sizeof(buffer), "%d/%d", o2, he); + else if (o2) + if (o2 == o2low) + snprintf(buffer, sizeof(buffer), "%d", o2); + else + snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2); + else + strcpy(buffer, _("air")); +exit: + g_object_set(renderer, "text", buffer, NULL); +} + +/* Render the SAC data (integer value of "ml / min") */ +static void sac_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int value, idx; + const char *fmt; + char buffer[16]; + double sac; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_SAC, &value, -1); + + if (idx < 0 || !value) { + *buffer = '\0'; + goto exit; + } + + sac = value / 1000.0; + switch (prefs.units.volume) { + case LITER: + fmt = "%4.1f"; + break; + case CUFT: + fmt = "%4.2f"; + sac = ml_to_cuft(sac * 1000); + break; + } + snprintf(buffer, sizeof(buffer), fmt, sac); +exit: + g_object_set(renderer, "text", buffer, NULL); +} + +/* Render the OTU data (integer value of "OTU") */ +static void otu_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int value, idx; + char buffer[16]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_OTU, &value, -1); + + if (idx < 0 || !value) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d", value); + + g_object_set(renderer, "text", buffer, NULL); +} + +/* Render the CNS data (in full %) */ +static void cns_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int value, idx; + char buffer[16]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_MAXCNS, &value, -1); + + if (idx < 0 || !value) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d%%", value); + + g_object_set(renderer, "text", buffer, NULL); +} + +GdkPixbuf *get_gps_icon(void) +{ + return gdk_pixbuf_from_pixdata(&satellite_pixbuf, TRUE, NULL); +} + +static GdkPixbuf *get_gps_icon_for_dive(struct dive *dive) +{ + if (dive_has_gps_location(dive)) + return get_gps_icon(); + else + return NULL; +} + +/* + * Set up anything that could have changed due to editing + * of dive information; we need to do this for both models, + * so we simply call set_one_dive again with the non-current model + */ +/* forward declaration for recursion */ +static gboolean set_one_dive(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); + +static void fill_one_dive(struct dive *dive, + GtkTreeModel *model, + GtkTreeIter *iter) +{ + char *location, *cylinder, *suit; + GtkTreeModel *othermodel; + GdkPixbuf *icon; + + get_cylinder(dive, &cylinder); + get_location(dive, &location); + get_suit(dive, &suit); + icon = get_gps_icon_for_dive(dive); + gtk_tree_store_set(GTK_TREE_STORE(model), iter, + DIVE_NR, dive->number, + DIVE_LOCATION, location, + DIVE_LOC_ICON, icon, + DIVE_CYLINDER, cylinder, + DIVE_RATING, dive->rating, + DIVE_SAC, dive->sac, + DIVE_OTU, dive->otu, + DIVE_MAXCNS, dive->maxcns, + DIVE_TOTALWEIGHT, total_weight(dive), + DIVE_SUIT, suit, + -1); + + if (icon) + g_object_unref(icon); + free(location); + free(cylinder); + free(suit); + + if (model == TREEMODEL(dive_list)) + othermodel = LISTMODEL(dive_list); + else + othermodel = TREEMODEL(dive_list); + if (othermodel != MODEL(dive_list)) + /* recursive call */ + gtk_tree_model_foreach(othermodel, set_one_dive, dive); +} + +static gboolean set_one_dive(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + int idx; + struct dive *dive; + + /* Get the dive number */ + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) + return FALSE; + dive = get_dive(idx); + if (!dive) + return TRUE; + if (data && dive != data) + return FALSE; + + fill_one_dive(dive, model, iter); + return dive == data; +} + +void flush_divelist(struct dive *dive) +{ + GtkTreeModel *model = MODEL(dive_list); + + gtk_tree_model_foreach(model, set_one_dive, dive); +} + +void set_divelist_font(const char *font) +{ + PangoFontDescription *font_desc = pango_font_description_from_string(font); + gtk_widget_modify_font(dive_list.tree_view, font_desc); + pango_font_description_free(font_desc); +} + +void update_dive_list_units(void) +{ + const char *unit; + GtkTreeModel *model = MODEL(dive_list); + + (void) get_depth_units(0, NULL, &unit); + gtk_tree_view_column_set_title(dive_list.depth, unit); + + (void) get_temp_units(0, &unit); + gtk_tree_view_column_set_title(dive_list.temperature, unit); + + (void) get_weight_units(0, NULL, &unit); + gtk_tree_view_column_set_title(dive_list.totalweight, unit); + + gtk_tree_model_foreach(model, set_one_dive, NULL); +} + +void update_dive_list_col_visibility(void) +{ + gtk_tree_view_column_set_visible(dive_list.cylinder, prefs.visible_cols.cylinder); + gtk_tree_view_column_set_visible(dive_list.temperature, prefs.visible_cols.temperature); + gtk_tree_view_column_set_visible(dive_list.totalweight, prefs.visible_cols.totalweight); + gtk_tree_view_column_set_visible(dive_list.suit, prefs.visible_cols.suit); + gtk_tree_view_column_set_visible(dive_list.nitrox, prefs.visible_cols.nitrox); + gtk_tree_view_column_set_visible(dive_list.sac, prefs.visible_cols.sac); + gtk_tree_view_column_set_visible(dive_list.otu, prefs.visible_cols.otu); + gtk_tree_view_column_set_visible(dive_list.maxcns, prefs.visible_cols.maxcns); + return; +} + +static void clear_trip_indexes(void) +{ + dive_trip_t *trip; + + for (trip = dive_trip_list; trip != NULL; trip = trip->next) + trip->index = 0; +} + +/* Select the iter asked for, and set the keyboard focus on it */ +static void go_to_iter(GtkTreeSelection *selection, GtkTreeIter *iter); +static void fill_dive_list(void) +{ + int i, trip_index = 0; + GtkTreeIter iter, parent_iter, lookup, *parent_ptr = NULL; + GtkTreeStore *liststore, *treestore; + GdkPixbuf *icon; + + /* Do we need to create any dive groups automatically? */ + if (autogroup) + autogroup_dives(); + + treestore = TREESTORE(dive_list); + liststore = LISTSTORE(dive_list); + + clear_trip_indexes(); + + i = dive_table.nr; + while (--i >= 0) { + struct dive *dive = get_dive(i); + dive_trip_t *trip = dive->divetrip; + + if (!trip) { + parent_ptr = NULL; + } else if (!trip->index) { + trip->index = ++trip_index; + + /* Create new trip entry */ + gtk_tree_store_append(treestore, &parent_iter, NULL); + parent_ptr = &parent_iter; + + /* a duration of 0 (and negative index) identifies a group */ + gtk_tree_store_set(treestore, parent_ptr, + DIVE_INDEX, -trip_index, + DIVE_DATE, trip->when, + DIVE_LOCATION, trip->location, + DIVE_DURATION, 0, + -1); + } else { + int idx, ok; + GtkTreeModel *model = TREEMODEL(dive_list); + + parent_ptr = NULL; + ok = gtk_tree_model_get_iter_first(model, &lookup); + while (ok) { + gtk_tree_model_get(model, &lookup, DIVE_INDEX, &idx, -1); + if (idx == -trip->index) { + parent_ptr = &lookup; + break; + } + ok = gtk_tree_model_iter_next(model, &lookup); + } + } + + /* store dive */ + update_cylinder_related_info(dive); + gtk_tree_store_append(treestore, &iter, parent_ptr); + icon = get_gps_icon_for_dive(dive); + gtk_tree_store_set(treestore, &iter, + DIVE_INDEX, i, + DIVE_NR, dive->number, + DIVE_DATE, dive->when, + DIVE_DEPTH, dive->maxdepth, + DIVE_DURATION, dive->duration.seconds, + DIVE_LOCATION, dive->location, + DIVE_LOC_ICON, icon, + DIVE_RATING, dive->rating, + DIVE_TEMPERATURE, dive->watertemp.mkelvin, + DIVE_SAC, 0, + -1); + if (icon) + g_object_unref(icon); + gtk_tree_store_append(liststore, &iter, NULL); + gtk_tree_store_set(liststore, &iter, + DIVE_INDEX, i, + DIVE_NR, dive->number, + DIVE_DATE, dive->when, + DIVE_DEPTH, dive->maxdepth, + DIVE_DURATION, dive->duration.seconds, + DIVE_LOCATION, dive->location, + DIVE_LOC_ICON, icon, + DIVE_RATING, dive->rating, + DIVE_TEMPERATURE, dive->watertemp.mkelvin, + DIVE_TOTALWEIGHT, 0, + DIVE_SUIT, dive->suit, + DIVE_SAC, 0, + -1); + } + + update_dive_list_units(); + if (amount_selected == 0 && gtk_tree_model_get_iter_first(MODEL(dive_list), &iter)) { + GtkTreeSelection *selection; + + /* select the last dive (and make sure it's an actual dive that is selected) */ + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &selected_dive, -1); + first_leaf(MODEL(dive_list), &iter, &selected_dive); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + go_to_iter(selection, &iter); + } +} + +static void restore_tree_state(void); + +void dive_list_update_dives(void) +{ + dive_table.preexisting = dive_table.nr; + gtk_tree_store_clear(TREESTORE(dive_list)); + gtk_tree_store_clear(LISTSTORE(dive_list)); + fill_dive_list(); + restore_tree_state(); + repaint_dive(); +} + +static gint dive_nr_sort(GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data) +{ + int idx_a, idx_b; + timestamp_t when_a, when_b; + struct dive *a, *b; + dive_trip_t *tripa = NULL, *tripb = NULL; + + gtk_tree_model_get(model, iter_a, DIVE_INDEX, &idx_a, DIVE_DATE, &when_a, -1); + gtk_tree_model_get(model, iter_b, DIVE_INDEX, &idx_b, DIVE_DATE, &when_b, -1); + + if (idx_a < 0) { + a = NULL; + tripa = find_trip_by_idx(idx_a); + } else { + a = get_dive(idx_a); + if (a) + tripa = a->divetrip; + } + + if (idx_b < 0) { + b = NULL; + tripb = find_trip_by_idx(idx_b); + } else { + b = get_dive(idx_b); + if (b) + tripb = b->divetrip; + } + + /* + * Compare dive dates within the same trip (or when there + * are no trips involved at all). But if we have two + * different trips use the trip dates for comparison + */ + if (tripa != tripb) { + if (tripa) + when_a = tripa->when; + if (tripb) + when_b = tripb->when; + } + return when_a - when_b; +} + + +static struct divelist_column { + const char *header; + data_func_t data; + sort_func_t sort; + unsigned int flags; + int *visible; +} dl_column[] = { + [DIVE_NR] = { "#", nr_data_func, dive_nr_sort, ALIGN_RIGHT }, + [DIVE_DATE] = { N_("Date"), date_data_func, NULL, ALIGN_LEFT }, + [DIVE_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT }, + [DIVE_DEPTH] = { N_("ft"), depth_data_func, NULL, ALIGN_RIGHT }, + [DIVE_DURATION] = { N_("min"), duration_data_func, NULL, ALIGN_RIGHT }, + [DIVE_TEMPERATURE] = { UTF8_DEGREE "F", temperature_data_func, NULL, ALIGN_RIGHT, &prefs.visible_cols.temperature }, + [DIVE_TOTALWEIGHT] = { N_("lbs"), weight_data_func, NULL, ALIGN_RIGHT, &prefs.visible_cols.totalweight }, + [DIVE_SUIT] = { N_("Suit"), NULL, NULL, ALIGN_LEFT, &prefs.visible_cols.suit }, + [DIVE_CYLINDER] = { N_("Cyl"), NULL, NULL, 0, &prefs.visible_cols.cylinder }, + [DIVE_NITROX] = { "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, nitrox_sort_func, 0, &prefs.visible_cols.nitrox }, + [DIVE_SAC] = { N_("SAC"), sac_data_func, NULL, 0, &prefs.visible_cols.sac }, + [DIVE_OTU] = { N_("OTU"), otu_data_func, NULL, 0, &prefs.visible_cols.otu }, + [DIVE_MAXCNS] = { N_("maxCNS"), cns_data_func, NULL, 0, &prefs.visible_cols.maxcns }, + [DIVE_LOCATION] = { N_("Location"), NULL, NULL, ALIGN_LEFT }, +}; + + +static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_column *col) +{ + int index = col - &dl_column[0]; + const char *title = _(col->header); + data_func_t data_func = col->data; + sort_func_t sort_func = col->sort; + unsigned int flags = col->flags; + int *visible = col->visible; + GtkWidget *tree_view = dl->tree_view; + GtkTreeStore *treemodel = dl->treemodel; + GtkTreeStore *listmodel = dl->listmodel; + GtkTreeViewColumn *ret; + + if (visible && !*visible) + flags |= INVISIBLE; + ret = tree_view_column(tree_view, index, title, data_func, flags); + if (sort_func) { + /* the sort functions are needed in the corresponding models */ + if (index == DIVE_NR) + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(treemodel), index, sort_func, NULL, NULL); + else + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(listmodel), index, sort_func, NULL, NULL); + } + return ret; +} + +/* + * This is some crazy crap. The only way to get default focus seems + * to be to grab focus as the widget is being shown the first time. + */ +static void realize_cb(GtkWidget *tree_view, gpointer userdata) +{ + gtk_widget_grab_focus(tree_view); +} + +/* + * Double-clicking on a group entry will expand a collapsed group + * and vice versa. + */ +static void collapse_expand(GtkTreeView *tree_view, GtkTreePath *path) +{ + if (!gtk_tree_view_row_expanded(tree_view, path)) + gtk_tree_view_expand_row(tree_view, path, FALSE); + else + gtk_tree_view_collapse_row(tree_view, path); + +} + +/* Double-click on a dive list */ +static void row_activated_cb(GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer userdata) +{ + int index; + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter(MODEL(dive_list), &iter, path)) + return; + + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &index, -1); + /* a negative index is special for the "group by date" entries */ + if (index < 0) { + collapse_expand(tree_view, path); + return; + } + edit_dive_info(get_dive(index), FALSE); +} + +void add_dive_cb(GtkWidget *menuitem, gpointer data) +{ + struct dive *dive; + + dive = alloc_dive(); + if (add_new_dive(dive)) { + record_dive(dive); + report_dives(TRUE, FALSE); + return; + } + free(dive); +} + +static void edit_trip_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + int idx; + GtkTreeIter iter; + dive_trip_t *dive_trip; + + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + dive_trip = find_trip_by_idx(idx); + if (edit_trip(dive_trip)) + gtk_tree_store_set(STORE(dive_list), &iter, DIVE_LOCATION, dive_trip->location, -1); +} + +static void edit_selected_dives_cb(GtkWidget *menuitem, gpointer data) +{ + edit_multi_dive_info(NULL); +} + +static void edit_dive_from_path_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + struct dive *dive = dive_from_path(path); + + edit_multi_dive_info(dive); +} + +static void edit_dive_when_cb(GtkWidget *menuitem, struct dive *dive) +{ + GtkWidget *dialog, *cal, *h, *m, *timehbox; + timestamp_t when; + + guint yval, mval, dval; + int success; + struct tm tm; + + if (!dive) + return; + + when = dive->when; + utc_mkdate(when, &tm); + dialog = create_date_time_widget(&tm, &cal, &h, &m, &timehbox); + + gtk_widget_show_all(dialog); + success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + if (!success) { + gtk_widget_destroy(dialog); + return; + } + memset(&tm, 0, sizeof(tm)); + gtk_calendar_get_date(GTK_CALENDAR(cal), &yval, &mval, &dval); + tm.tm_year = yval; + tm.tm_mon = mval; + tm.tm_mday = dval; + tm.tm_hour = gtk_spin_button_get_value(GTK_SPIN_BUTTON(h)); + tm.tm_min = gtk_spin_button_get_value(GTK_SPIN_BUTTON(m)); + + gtk_widget_destroy(dialog); + when = utc_mktime(&tm); + if (dive->when != when) { + /* if this is the only dive in the trip, just change the trip time */ + if (dive->divetrip && dive->divetrip->nrdives == 1) + dive->divetrip->when = when; + /* if this is suddenly before the start of the trip, remove it from the trip */ + else if (dive->divetrip && dive->divetrip->when > when) + remove_dive_from_trip(dive); + else if (find_matching_trip(when) != dive->divetrip) + remove_dive_from_trip(dive); + dive->when = when; + mark_divelist_changed(TRUE); + report_dives(FALSE, FALSE); + dive_list_update_dives(); + } +} + +#if HAVE_OSM_GPS_MAP +static void show_gps_location_cb(GtkWidget *menuitem, struct dive *dive) +{ + show_gps_location(dive, NULL); +} +#endif + +gboolean icon_click_cb(GtkWidget *w, GdkEventButton *event, gpointer data) +{ +#if HAVE_OSM_GPS_MAP + GtkTreePath *path = NULL; + GtkTreeIter iter; + GtkTreeViewColumn *col; + int idx; + struct dive *dive; + + /* left click ? */ + if (event->button == 1 && + gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(dive_list.tree_view), event->x, event->y, &path, &col, NULL, NULL)) { + /* is it the icon column ? (we passed the correct column in when registering the callback) */ + if (col == data) { + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + if (dive && dive_has_gps_location(dive)) + show_gps_location(dive, NULL); + } + if (path) + gtk_tree_path_free(path); + } +#endif + /* keep processing the click */ + return FALSE; +} + +static void save_as_cb(GtkWidget *menuitem, struct dive *dive) +{ + GtkWidget *dialog; + char *filename = NULL; + + dialog = gtk_file_chooser_dialog_new(_("Save File As"), + GTK_WINDOW(main_window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + } + gtk_widget_destroy(dialog); + + if (filename){ + save_dives_logic(filename, TRUE); + g_free(filename); + } +} + +static void expand_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view) +{ + gtk_tree_view_expand_all(tree_view); +} + +static void collapse_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view) +{ + gtk_tree_view_collapse_all(tree_view); +} + +/* Move a top-level dive into the trip above it */ +static void merge_dive_into_trip_above_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + int idx; + struct dive *dive; + dive_trip_t *trip; + + idx = get_path_index(path); + dive = get_dive(idx); + + /* Needs to be a dive, and at the top level */ + if (!dive || dive->divetrip) + return; + + /* Find the "trip above". */ + for (;;) { + if (!gtk_tree_path_prev(path)) + return; + idx = get_path_index(path); + trip = find_trip_by_idx(idx); + if (trip) + break; + } + + add_dive_to_trip(dive, trip); + if (dive->selected) { + for_each_dive(idx, dive) { + if (!dive->selected) + continue; + add_dive_to_trip(dive, trip); + } + } + + trip->expanded = 1; + dive_list_update_dives(); + mark_divelist_changed(TRUE); +} + +static void insert_trip_before_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + int idx; + struct dive *dive; + dive_trip_t *trip; + + idx = get_path_index(path); + dive = get_dive(idx); + if (!dive) + return; + trip = create_and_hookup_trip_from_dive(dive); + if (dive->selected) { + for_each_dive(idx, dive) { + if (!dive->selected) + continue; + add_dive_to_trip(dive, trip); + } + } + trip->expanded = 1; + dive_list_update_dives(); + mark_divelist_changed(TRUE); +} + +static void remove_from_trip_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + struct dive *dive; + int idx; + + idx = get_path_index(path); + if (idx < 0) + return; + dive = get_dive(idx); + + if (dive->selected) { + /* remove all the selected dives */ + for_each_dive(idx, dive) { + if (!dive->selected) + continue; + remove_dive_from_trip(dive); + } + } else { + /* just remove the dive the mouse pointer is on */ + remove_dive_from_trip(dive); + } + dive_list_update_dives(); + mark_divelist_changed(TRUE); +} + +static void remove_trip(GtkTreePath *trippath) +{ + int idx, i; + dive_trip_t *trip; + struct dive *dive; + + idx = get_path_index(trippath); + trip = find_trip_by_idx(idx); + if (!trip) + return; + + for_each_dive(i, dive) { + if (dive->divetrip != trip) + continue; + remove_dive_from_trip(dive); + } + + dive_list_update_dives(); + +#ifdef DEBUG_TRIP + dump_trip_list(); +#endif +} + +static void remove_trip_cb(GtkWidget *menuitem, GtkTreePath *trippath) +{ + int success; + GtkWidget *dialog; + + dialog = gtk_dialog_new_with_buttons(_("Remove Trip"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + gtk_widget_show_all(dialog); + success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + gtk_widget_destroy(dialog); + if (!success) + return; + + remove_trip(trippath); + mark_divelist_changed(TRUE); +} + +static void merge_trips_cb(GtkWidget *menuitem, GtkTreePath *trippath) +{ + GtkTreePath *prevpath; + GtkTreeIter thistripiter, prevtripiter; + GtkTreeModel *tm = MODEL(dive_list); + dive_trip_t *thistrip, *prevtrip; + timestamp_t when; + + /* this only gets called when we are on a trip and there is another trip right before */ + prevpath = gtk_tree_path_copy(trippath); + gtk_tree_path_prev(prevpath); + gtk_tree_model_get_iter(tm, &thistripiter, trippath); + gtk_tree_model_get(tm, &thistripiter, DIVE_DATE, &when, -1); + thistrip = find_matching_trip(when); + gtk_tree_model_get_iter(tm, &prevtripiter, prevpath); + gtk_tree_model_get(tm, &prevtripiter, DIVE_DATE, &when, -1); + prevtrip = find_matching_trip(when); + /* move dives from trip */ + assert(thistrip != prevtrip); + while (thistrip->dives) + add_dive_to_trip(thistrip->dives, prevtrip); + dive_list_update_dives(); + mark_divelist_changed(TRUE); +} + +static gboolean restore_node_state(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + int idx; + struct dive *dive; + dive_trip_t *trip; + GtkTreeView *tree_view = GTK_TREE_VIEW(dive_list.tree_view); + GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view); + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + trip = find_trip_by_idx(idx); + if (trip && trip->expanded) + gtk_tree_view_expand_row(tree_view, path, FALSE); + if (trip && trip->selected) + gtk_tree_selection_select_iter(selection, iter); + } else { + dive = get_dive(idx); + if (dive && dive->selected) + gtk_tree_selection_select_iter(selection, iter); + } + /* continue foreach */ + return FALSE; +} + +/* restore expanded and selected state */ +static void restore_tree_state(void) +{ + gtk_tree_model_foreach(MODEL(dive_list), restore_node_state, NULL); +} + +/* called when multiple dives are selected and one of these is right-clicked for delete */ +static void delete_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + int i; + struct dive *dive; + int success; + GtkWidget *dialog; + char *dialog_title; + + if (!amount_selected) + return; + if (amount_selected == 1) + dialog_title = _("Delete dive"); + else + dialog_title = _("Delete dives"); + + dialog = gtk_dialog_new_with_buttons(dialog_title, + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + gtk_widget_show_all(dialog); + success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + gtk_widget_destroy(dialog); + if (!success) + return; + + /* walk the dive list in chronological order */ + for (i = 0; i < dive_table.nr; i++) { + dive = get_dive(i); + if (!dive) + continue; + if (!dive->selected) + continue; + /* now remove the dive from the table and free it. also move the iterator back, + * so that we don't skip a dive */ + delete_single_dive(i); + i--; + } + dive_list_update_dives(); + + /* if no dives are selected at this point clear the display widgets */ + if (!amount_selected) { + selected_dive = 0; + process_selected_dives(); + clear_stats_widgets(); + clear_equipment_widgets(); + show_dive_info(NULL); + } + mark_divelist_changed(TRUE); +} + +/* this gets called with path pointing to a dive, either in the top level + * or as part of a trip */ +static void delete_dive_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + int idx; + GtkTreeIter iter; + int success; + GtkWidget *dialog; + + dialog = gtk_dialog_new_with_buttons(_("Delete dive"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + gtk_widget_show_all(dialog); + success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + gtk_widget_destroy(dialog); + if (!success) + return; + + if (!gtk_tree_model_get_iter(MODEL(dive_list), &iter, path)) + return; + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + delete_single_dive(idx); + dive_list_update_dives(); + mark_divelist_changed(TRUE); +} + +#if defined(LIBZIP) && defined(XSLT) +static void export_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + int i; + struct dive *dive; + FILE *f; + char filename[PATH_MAX], *tempfile; + size_t streamsize; + char *membuf; + xmlDoc *doc; + xsltStylesheetPtr xslt = NULL; + xmlDoc *transformed; + struct zip_source *s[dive_table.nr]; + struct zip *zip; + const gchar *tmpdir = g_get_tmp_dir(); + + /* + * Creating a temporary .DLD file to be eventually uploaded to + * divelogs.de. I wonder if this could be done in-memory. + */ + tempfile = g_build_filename(tmpdir, "export.DLD-XXXXXX", NULL); + int fd = g_mkstemp(tempfile); + if (fd != -1) + close(fd); + zip = zip_open(tempfile, ZIP_CREATE, NULL); + + if (!zip) + return; + + if (!amount_selected) + return; + + /* walk the dive list in chronological order */ + for (i = 0; i < dive_table.nr; i++) { + + dive = get_dive(i); + if (!dive) + continue; + if (!dive->selected) + continue; + + f = tmpfile(); + if (!f) + return; + save_dive(f, dive); + fseek(f, 0, SEEK_END); + streamsize = ftell(f); + rewind(f); + membuf = malloc(streamsize + 1); + if (!membuf || !fread(membuf, streamsize, 1, f)) + return; + membuf[streamsize] = 0; + fclose(f); + + /* + * Parse the memory buffer into XML document and + * transform it to divelogs.de format, finally dumping + * the XML into a character buffer. + */ + doc = xmlReadMemory(membuf, strlen(membuf), "divelog", NULL, 0); + if (!doc) + continue; + + free((void *)membuf); + xslt = get_stylesheet("divelogs-export.xslt"); + if (!xslt) + return; + transformed = xsltApplyStylesheet(xslt, doc, NULL); + xsltFreeStylesheet(xslt); + xmlDocDumpMemory(transformed, (xmlChar **) &membuf, (int *)&streamsize); + xmlFreeDoc(doc); + xmlFreeDoc(transformed); + + /* + * Save the XML document into a zip file. + */ + snprintf(filename, PATH_MAX, "%d.xml", i + 1); + s[i] = zip_source_buffer(zip, membuf, streamsize, 1); + if (s[i]) { + int64_t ret = zip_add(zip, filename, s[i]); + if (ret == -1) + fprintf(stderr, "failed to include dive %d\n", i); + } + } + zip_close(zip); + if (divelogde_upload(tempfile)) + g_unlink(tempfile); + else + fprintf(stderr,"upload of %s failed\n", tempfile); + g_free(tempfile); +} +#endif + +#if defined(XSLT) +static void export_dives_uddf(const gboolean selected) +{ + FILE *f; + char *filename = NULL; + size_t streamsize; + char *membuf; + xmlDoc *doc; + xsltStylesheetPtr xslt = NULL; + xmlDoc *transformed; + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new(_("Export As UDDF 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 (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + } + gtk_widget_destroy(dialog); + + if (!filename) + return; + + /* Save XML to file and convert it into a memory buffer */ + save_dives_logic(filename, selected); + f = fopen(filename, "r"); + fseek(f, 0, SEEK_END); + streamsize = ftell(f); + rewind(f); + + membuf = malloc(streamsize + 1); + if (!membuf || !fread(membuf, streamsize, 1, f)) { + fprintf(stderr, "Failed to read memory buffer\n"); + return; + } + membuf[streamsize] = 0; + fclose(f); + g_unlink(filename); + + /* + * Parse the memory buffer into XML document and + * transform it to UDDF format, finally dumping + * the XML into a character buffer. + */ + doc = xmlReadMemory(membuf, strlen(membuf), "divelog", NULL, 0); + if (!doc) { + fprintf(stderr, "Failed to read XML memory\n"); + return; + } + free((void *)membuf); + + /* Convert to UDDF format */ + xslt = get_stylesheet("uddf-export.xslt"); + if (!xslt) { + fprintf(stderr, "Failed to open UDDF conversion stylesheet\n"); + return; + } + transformed = xsltApplyStylesheet(xslt, doc, NULL); + xsltFreeStylesheet(xslt); + xmlFreeDoc(doc); + + /* Write the transformed XML to file */ + f = g_fopen(filename, "w"); + xmlDocFormatDump(f, transformed, 1); + xmlFreeDoc(transformed); + + fclose(f); + g_free(filename); +} + +static void export_selected_dives_uddf_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + export_dives_uddf(TRUE); +} + +void export_all_dives_uddf_cb() +{ + export_dives_uddf(FALSE); +} +#endif + +static void merge_dives_cb(GtkWidget *menuitem, void *unused) +{ + int i; + struct dive *dive; + + for_each_dive(i, dive) { + if (dive->selected) { + merge_dive_index(i, dive); + return; + } + } +} + +/* Called if there are exactly two selected dives and the dive at idx is one of them */ +static void add_dive_merge_label(int idx, GtkMenuShell *menu) +{ + struct dive *a, *b; + GtkWidget *menuitem; + + /* The other selected dive must be next to it.. */ + a = get_dive(idx); + b = get_dive(idx+1); + if (!b || !b->selected) { + b = a; + a = get_dive(idx-1); + if (!a || !a->selected) + return; + } + + /* .. and they had better be in the same dive trip */ + if (a->divetrip != b->divetrip) + return; + + /* .. and if the surface interval is excessive, you must be kidding us */ + if (b->when > a->when + a->duration.seconds + 30*60) + return; + + /* If so, we can add a "merge dive" menu entry */ + menuitem = gtk_menu_item_new_with_label(_("Merge dives")); + g_signal_connect(menuitem, "activate", G_CALLBACK(merge_dives_cb), NULL); + gtk_menu_shell_append(menu, menuitem); +} + +static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button, GdkEventButton *event) +{ + GtkWidget *menu, *menuitem, *image; + char editplurallabel[] = N_("Edit dives"); + char editsinglelabel[] = N_("Edit dive"); + char *editlabel; + char deleteplurallabel[] = N_("Delete dives"); + char deletesinglelabel[] = N_("Delete dive"); + char *deletelabel; +#if defined(XSLT) + char exportuddflabel[] = N_("Export dive(s) to UDDF"); +#endif +#if defined(LIBZIP) && defined(XSLT) + char exportlabel[] = N_("Export dive(s)"); +#endif + GtkTreePath *path, *prevpath, *nextpath; + GtkTreeIter iter, previter, nextiter; + int idx, previdx, nextidx; + struct dive *dive; + + if (!event || !gtk_tree_view_get_path_at_pos(tree_view, event->x, event->y, &path, NULL, NULL, NULL)) + return; + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + + menu = gtk_menu_new(); + menuitem = gtk_image_menu_item_new_with_label(_("Add dive")); + image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); + g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + if (idx < 0) { + /* mouse pointer is on a trip summary entry */ + menuitem = gtk_menu_item_new_with_label(_("Edit Trip Summary")); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_trip_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + prevpath = gtk_tree_path_copy(path); + if (gtk_tree_path_prev(prevpath) && + gtk_tree_model_get_iter(MODEL(dive_list), &previter, prevpath)) { + gtk_tree_model_get(MODEL(dive_list), &previter, DIVE_INDEX, &previdx, -1); + if (previdx < 0) { + menuitem = gtk_menu_item_new_with_label(_("Merge trip with trip above")); + g_signal_connect(menuitem, "activate", G_CALLBACK(merge_trips_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + nextpath = gtk_tree_path_copy(path); + gtk_tree_path_next(nextpath); + if (gtk_tree_model_get_iter(MODEL(dive_list), &nextiter, nextpath)) { + gtk_tree_model_get(MODEL(dive_list), &nextiter, DIVE_INDEX, &nextidx, -1); + if (nextidx < 0) { + menuitem = gtk_menu_item_new_with_label(_("Merge trip with trip below")); + g_signal_connect(menuitem, "activate", G_CALLBACK(merge_trips_cb), nextpath); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + menuitem = gtk_menu_item_new_with_label(_("Remove Trip")); + g_signal_connect(menuitem, "activate", G_CALLBACK(remove_trip_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } else { + dive = get_dive(idx); + /* if we right click on selected dive(s), edit or delete those */ + if (dive->selected) { + if (amount_selected == 1) { + deletelabel = _(deletesinglelabel); + editlabel = _(editsinglelabel); + menuitem = gtk_menu_item_new_with_label(_("Edit dive date/time")); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_when_cb), dive); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } else { + deletelabel = _(deleteplurallabel); + editlabel = _(editplurallabel); + } + menuitem = gtk_menu_item_new_with_label(_("Save as")); + g_signal_connect(menuitem, "activate", G_CALLBACK(save_as_cb), dive); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + menuitem = gtk_menu_item_new_with_label(deletelabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(delete_selected_dives_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + +#if defined(LIBZIP) && defined(XSLT) + menuitem = gtk_menu_item_new_with_label(exportlabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(export_selected_dives_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); +#endif + +#if defined(XSLT) + menuitem = gtk_menu_item_new_with_label(exportuddflabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(export_selected_dives_uddf_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); +#endif + + menuitem = gtk_menu_item_new_with_label(editlabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_selected_dives_cb), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + /* Two contiguous selected dives? */ + if (amount_selected == 2) + add_dive_merge_label(idx, GTK_MENU_SHELL(menu)); + } else { + menuitem = gtk_menu_item_new_with_label(_("Edit dive date/time")); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_when_cb), dive); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + deletelabel = _(deletesinglelabel); + menuitem = gtk_menu_item_new_with_label(deletelabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(delete_dive_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + editlabel = _(editsinglelabel); + menuitem = gtk_menu_item_new_with_label(editlabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_from_path_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } +#if HAVE_OSM_GPS_MAP + /* Only offer to show on map if it has a location. */ + if (dive_has_gps_location(dive)) { + menuitem = gtk_menu_item_new_with_label(_("Show in map")); + g_signal_connect(menuitem, "activate", G_CALLBACK(show_gps_location_cb), dive); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } +#endif + /* only offer trip editing options when we are displaying the tree model */ + if (dive_list.model == dive_list.treemodel) { + int depth = gtk_tree_path_get_depth(path); + int *indices = gtk_tree_path_get_indices(path); + /* top level dive or child dive that is not the first child */ + if (depth == 1 || indices[1] > 0) { + menuitem = gtk_menu_item_new_with_label(_("Create new trip above")); + g_signal_connect(menuitem, "activate", G_CALLBACK(insert_trip_before_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + prevpath = gtk_tree_path_copy(path); + /* top level dive with a trip right before it */ + if (depth == 1 && + gtk_tree_path_prev(prevpath) && + gtk_tree_model_get_iter(MODEL(dive_list), &previter, prevpath) && + gtk_tree_model_iter_n_children(model, &previter)) { + menuitem = gtk_menu_item_new_with_label(_("Add to trip above")); + g_signal_connect(menuitem, "activate", G_CALLBACK(merge_dive_into_trip_above_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + if (DIVE_IN_TRIP(dive)) { + if (dive->selected && amount_selected > 1) + menuitem = gtk_menu_item_new_with_label(_("Remove selected dives from trip")); + else + menuitem = gtk_menu_item_new_with_label(_("Remove dive from trip")); + g_signal_connect(menuitem, "activate", G_CALLBACK(remove_from_trip_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + } + menuitem = gtk_menu_item_new_with_label(_("Expand all")); + g_signal_connect(menuitem, "activate", G_CALLBACK(expand_all_cb), tree_view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + menuitem = gtk_menu_item_new_with_label(_("Collapse all")); + g_signal_connect(menuitem, "activate", G_CALLBACK(collapse_all_cb), tree_view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + gtk_widget_show_all(menu); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + button, gtk_get_current_event_time()); +} + +static void popup_menu_cb(GtkTreeView *tree_view, gpointer userdata) +{ + popup_divelist_menu(tree_view, MODEL(dive_list), 0, NULL); +} + +static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpointer userdata) +{ + /* Right-click? Bring up the menu */ + if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + popup_divelist_menu(GTK_TREE_VIEW(treeview), MODEL(dive_list), 3, event); + return TRUE; + } + return FALSE; +} + +/* make sure 'path' is shown in the divelist widget; since set_cursor changes the + * selection to be only 'path' we need to let our selection handling callbacks know + * that we didn't really mean this */ +static void scroll_to_path(GtkTreePath *path) +{ + GtkTreeSelection *selection; + + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(dive_list.tree_view), path, NULL, FALSE, 0, 0); + in_set_cursor = TRUE; + gtk_tree_view_set_cursor(GTK_TREE_VIEW(dive_list.tree_view), path, NULL, FALSE); + in_set_cursor = FALSE; + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + gtk_tree_model_foreach(MODEL(dive_list), set_selected, selection); + +} + +/* we need to have a temporary copy of the selected dives while + switching model as the selection_cb function keeps getting called + when gtk_tree_selection_select_path is called. We also need to + keep copies of the sort order so we can restore that as well after + switching models. */ +static gboolean second_call = FALSE; +static GtkSortType sortorder[] = { [0 ... DIVELIST_COLUMNS - 1] = GTK_SORT_DESCENDING, }; +static int lastcol = DIVE_NR; + +/* Check if this dive was selected previously and select it again in the new model; + * This is used after we switch models to maintain consistent selections. + * We always return FALSE to iterate through all dives */ +static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + GtkTreeSelection *selection = GTK_TREE_SELECTION(data); + int idx, selected; + struct dive *dive; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + /* this is a trip - restore its state */ + dive_trip_t *trip = find_trip_by_idx(idx); + if (trip && trip->expanded) + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); + if (trip && trip->selected) + gtk_tree_selection_select_path(selection, path); + } else { + dive = get_dive(idx); + selected = dive && dive->selected; + if (selected) { + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); + gtk_tree_selection_select_path(selection, path); + } + } + return FALSE; +} + +static gboolean scroll_to_this(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + int idx; + struct dive *dive; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + if (dive == current_dive) { + scroll_to_path(path); + return TRUE; + } + return FALSE; +} + +static void scroll_to_current(GtkTreeModel *model) +{ + if (current_dive) + gtk_tree_model_foreach(model, scroll_to_this, current_dive); +} + +static void update_column_and_order(int colid) +{ + /* Careful: the index into treecolumns is off by one as we don't have a + tree_view column for DIVE_INDEX */ + GtkTreeViewColumn **treecolumns = &dive_list.nr; + + /* this will trigger a second call into sort_column_change_cb, + so make sure we don't start an infinite recursion... */ + second_call = TRUE; + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, sortorder[colid]); + gtk_tree_view_column_set_sort_order(treecolumns[colid - 1], sortorder[colid]); + second_call = FALSE; + scroll_to_current(GTK_TREE_MODEL(dive_list.model)); +} + +/* If the sort column is nr (default), show the tree model. + For every other sort column only show the list model. + If the model changed, inform the new model of the chosen sort column and make + sure the same dives are still selected. + + The challenge with this function is that once we change the model + we also need to change the sort column again (as it was changed in + the other model) and that causes this function to be called + recursively - so we need to catch that. +*/ +static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) +{ + int colid; + GtkSortType order; + GtkTreeStore *currentmodel = dive_list.model; + + gtk_widget_grab_focus(dive_list.tree_view); + if (second_call) + return; + + gtk_tree_sortable_get_sort_column_id(treeview, &colid, &order); + if (colid == lastcol) { + /* we just changed sort order */ + sortorder[colid] = order; + return; + } else { + lastcol = colid; + } + if (colid == DIVE_NR) + dive_list.model = dive_list.treemodel; + else + dive_list.model = dive_list.listmodel; + if (dive_list.model != currentmodel) { + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + + gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), MODEL(dive_list)); + update_column_and_order(colid); + gtk_tree_model_foreach(MODEL(dive_list), set_selected, selection); + } else { + if (order != sortorder[colid]) { + update_column_and_order(colid); + } + } +} + +static gboolean modify_selection_cb(GtkTreeSelection *selection, GtkTreeModel *model, + GtkTreePath *path, gboolean was_selected, gpointer userdata) +{ + int idx; + GtkTreeIter iter; + + if (!was_selected || in_set_cursor) + return TRUE; + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + int i; + struct dive *dive; + dive_trip_t *trip = find_trip_by_idx(idx); + if (!trip) + return TRUE; + + trip->selected = 0; + /* If this is expanded, let the gtk selection happen for each dive under it */ + if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), path)) + return TRUE; + /* Otherwise, consider each dive under it deselected */ + for_each_dive(i, dive) { + if (dive->divetrip == trip) + deselect_dive(i); + } + } else { + deselect_dive(idx); + } + return TRUE; +} + +/* This gets called for each selected entry after a selection has changed */ +static void entry_selected(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + int idx; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + int i; + struct dive *dive; + dive_trip_t *trip = find_trip_by_idx(idx); + + if (!trip) + return; + trip->selected = 1; + + /* If this is expanded, let the gtk selection happen for each dive under it */ + if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), path)) { + trip->fixup = 1; + return; + } + + /* Otherwise, consider each dive under it selected */ + for_each_dive(i, dive) { + if (dive->divetrip == trip) + select_dive(i); + } + trip->fixup = 0; + } else { + select_dive(idx); + } +} + +static void update_gtk_selection(GtkTreeSelection *selection, GtkTreeModel *model) +{ + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + do { + GtkTreeIter child; + + if (!gtk_tree_model_iter_children(model, &child, &iter)) + continue; + + do { + int idx; + struct dive *dive; + dive_trip_t *trip; + + gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + if (!dive || !dive->selected) + break; + trip = dive->divetrip; + if (!trip) + break; + gtk_tree_selection_select_iter(selection, &child); + } while (gtk_tree_model_iter_next(model, &child)); + } while (gtk_tree_model_iter_next(model, &iter)); +} + +/* this is called when gtk thinks that the selection has changed */ +static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) +{ + int i, fixup; + struct dive *dive; + + gtk_tree_selection_selected_foreach(selection, entry_selected, model); + + /* + * Go through all the dives, if there is a trip that is selected but no + * dives under it are selected, force-select all the dives + */ + + /* First, clear "fixup" for any trip that has selected dives */ + for_each_dive(i, dive) { + dive_trip_t *trip = dive->divetrip; + if (!trip || !trip->fixup) + continue; + if (dive->selected || !trip->selected) + trip->fixup = 0; + } + + /* + * Ok, not fixup is only set for trips that are selected + * but have no selected dives in them. Select all dives + * for such trips. + */ + fixup = 0; + for_each_dive(i, dive) { + dive_trip_t *trip = dive->divetrip; + if (!trip || !trip->fixup) + continue; + fixup = 1; + select_dive(i); + } + + /* + * Ok, we did a forced selection of dives, now we need to update the gtk + * view of what is selected too.. + */ + if (fixup) + update_gtk_selection(selection, model); + +#if DEBUG_SELECTION_TRACKING + dump_selection(); +#endif + + process_selected_dives(); + repaint_dive(); +} + +GtkWidget *dive_list_create(void) +{ + GtkTreeSelection *selection; + + dive_list.listmodel = gtk_tree_store_new(DIVELIST_COLUMNS, + G_TYPE_INT, /* index */ + G_TYPE_INT, /* nr */ + G_TYPE_INT64, /* Date */ + G_TYPE_INT, /* Star rating */ + G_TYPE_INT, /* Depth */ + G_TYPE_INT, /* Duration */ + G_TYPE_INT, /* Temperature */ + G_TYPE_INT, /* Total weight */ + G_TYPE_STRING, /* Suit */ + G_TYPE_STRING, /* Cylinder */ + G_TYPE_INT, /* Nitrox */ + G_TYPE_INT, /* SAC */ + G_TYPE_INT, /* OTU */ + G_TYPE_INT, /* MAXCNS */ + G_TYPE_STRING, /* Location */ + GDK_TYPE_PIXBUF /* GPS icon */ + ); + dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS, + G_TYPE_INT, /* index */ + G_TYPE_INT, /* nr */ + G_TYPE_INT64, /* Date */ + G_TYPE_INT, /* Star rating */ + G_TYPE_INT, /* Depth */ + G_TYPE_INT, /* Duration */ + G_TYPE_INT, /* Temperature */ + G_TYPE_INT, /* Total weight */ + G_TYPE_STRING, /* Suit */ + G_TYPE_STRING, /* Cylinder */ + G_TYPE_INT, /* Nitrox */ + G_TYPE_INT, /* SAC */ + G_TYPE_INT, /* OTU */ + G_TYPE_INT, /* MAXCNS */ + G_TYPE_STRING, /* Location */ + GDK_TYPE_PIXBUF /* GPS icon */ + ); + dive_list.model = dive_list.treemodel; + dive_list.tree_view = gtk_tree_view_new_with_model(TREEMODEL(dive_list)); + set_divelist_font(prefs.divelist_font); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + + gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE); + gtk_widget_set_size_request(dive_list.tree_view, 200, 200); + + /* check if utf8 stars are available as a default OS feature */ + if (!subsurface_os_feature_available(UTF8_FONT_WITH_STARS)) + dl_column[3].header = "*"; + + dive_list.nr = divelist_column(&dive_list, dl_column + DIVE_NR); + dive_list.date = divelist_column(&dive_list, dl_column + DIVE_DATE); + dive_list.stars = divelist_column(&dive_list, dl_column + DIVE_RATING); + dive_list.depth = divelist_column(&dive_list, dl_column + DIVE_DEPTH); + dive_list.duration = divelist_column(&dive_list, dl_column + DIVE_DURATION); + dive_list.temperature = divelist_column(&dive_list, dl_column + DIVE_TEMPERATURE); + dive_list.totalweight = divelist_column(&dive_list, dl_column + DIVE_TOTALWEIGHT); + dive_list.suit = divelist_column(&dive_list, dl_column + DIVE_SUIT); + dive_list.cylinder = divelist_column(&dive_list, dl_column + DIVE_CYLINDER); + dive_list.nitrox = divelist_column(&dive_list, dl_column + DIVE_NITROX); + dive_list.sac = divelist_column(&dive_list, dl_column + DIVE_SAC); + dive_list.otu = divelist_column(&dive_list, dl_column + DIVE_OTU); + dive_list.maxcns = divelist_column(&dive_list, dl_column + DIVE_MAXCNS); + dive_list.location = divelist_column(&dive_list, dl_column + DIVE_LOCATION); + gtk_tree_view_column_set_sort_indicator(dive_list.nr, TRUE); + gtk_tree_view_column_set_sort_order(dive_list.nr, GTK_SORT_DESCENDING); + /* now add the GPS icon to the location column */ + tree_view_column_add_pixbuf(dive_list.tree_view, gpsicon_data_func, dive_list.location); + + fill_dive_list(); + + g_object_set(G_OBJECT(dive_list.tree_view), "headers-visible", TRUE, + "search-column", DIVE_LOCATION, + "rules-hint", TRUE, + NULL); + + g_signal_connect_after(dive_list.tree_view, "realize", G_CALLBACK(realize_cb), NULL); + g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), NULL); + g_signal_connect(dive_list.tree_view, "row-expanded", G_CALLBACK(row_expanded_cb), NULL); + g_signal_connect(dive_list.tree_view, "row-collapsed", G_CALLBACK(row_collapsed_cb), NULL); + g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), NULL); + g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), NULL); + g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), dive_list.model); + g_signal_connect(dive_list.listmodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); + g_signal_connect(dive_list.treemodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); + + gtk_tree_selection_set_select_function(selection, modify_selection_cb, NULL, NULL); + + dive_list.container_widget = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dive_list.container_widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(dive_list.container_widget), dive_list.tree_view); + + dive_list.changed = 0; + + return dive_list.container_widget; +} + +void dive_list_destroy(void) +{ + gtk_widget_destroy(dive_list.tree_view); + g_object_unref(dive_list.treemodel); + g_object_unref(dive_list.listmodel); +} + +struct iteridx { + int idx; + GtkTreeIter *iter; +}; + +static gboolean iter_has_idx(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer _data) +{ + struct iteridx *iteridx = _data; + int idx; + /* Get the dive number */ + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx == iteridx->idx) { + iteridx->iter = gtk_tree_iter_copy(iter); + return TRUE; /* end foreach */ + } + return FALSE; +} + +static GtkTreeIter *get_iter_from_idx(int idx) +{ + struct iteridx iteridx = {idx, }; + gtk_tree_model_foreach(MODEL(dive_list), iter_has_idx, &iteridx); + return iteridx.iter; +} + +static void scroll_to_selected(GtkTreeIter *iter) +{ + GtkTreePath *treepath; + treepath = gtk_tree_model_get_path(MODEL(dive_list), iter); + scroll_to_path(treepath); + gtk_tree_path_free(treepath); +} + +static void go_to_iter(GtkTreeSelection *selection, GtkTreeIter *iter) +{ + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_iter(selection, iter); + scroll_to_selected(iter); +} + +void show_and_select_dive(struct dive *dive) +{ + GtkTreeSelection *selection; + GtkTreeIter *iter; + struct dive *odive; + int i, divenr; + + divenr = get_divenr(dive); + if (divenr < 0) + /* we failed to find the dive */ + return; + iter = get_iter_from_idx(divenr); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + for_each_dive(i, odive) + odive->selected = FALSE; + amount_selected = 1; + selected_dive = divenr; + dive->selected = TRUE; + go_to_iter(selection, iter); + gtk_tree_iter_free(iter); +} + +void select_next_dive(void) +{ + GtkTreeIter *nextiter, *parent = NULL; + GtkTreeIter *iter = get_iter_from_idx(selected_dive); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + int idx; + + if (!iter) + return; + nextiter = gtk_tree_iter_copy(iter); + if (!gtk_tree_model_iter_next(MODEL(dive_list), nextiter)) { + if (!gtk_tree_model_iter_parent(MODEL(dive_list), nextiter, iter)) { + /* we're at the last top level node */ + goto free_iter; + } + if (!gtk_tree_model_iter_next(MODEL(dive_list), nextiter)) { + /* last trip */ + goto free_iter; + } + } + gtk_tree_model_get(MODEL(dive_list), nextiter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + /* need the first child */ + parent = gtk_tree_iter_copy(nextiter); + if (! gtk_tree_model_iter_children(MODEL(dive_list), nextiter, parent)) + goto free_iter; + } + go_to_iter(selection, nextiter); +free_iter: + if (nextiter) + gtk_tree_iter_free(nextiter); + if (parent) + gtk_tree_iter_free(parent); + gtk_tree_iter_free(iter); +} + +void select_prev_dive(void) +{ + GtkTreeIter previter, *parent = NULL; + GtkTreeIter *iter = get_iter_from_idx(selected_dive); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + GtkTreePath *treepath; + int idx; + + if (!iter) + return; + treepath = gtk_tree_model_get_path(MODEL(dive_list), iter); + if (!gtk_tree_path_prev(treepath)) { + if (!gtk_tree_model_iter_parent(MODEL(dive_list), &previter, iter)) + /* we're at the last top level node */ + goto free_iter; + gtk_tree_path_free(treepath); + treepath = gtk_tree_model_get_path(MODEL(dive_list), &previter); + if (!gtk_tree_path_prev(treepath)) + /* first trip */ + goto free_iter; + if (!gtk_tree_model_get_iter(MODEL(dive_list), &previter, treepath)) + goto free_iter; + } + if (!gtk_tree_model_get_iter(MODEL(dive_list), &previter, treepath)) + goto free_iter; + gtk_tree_model_get(MODEL(dive_list), &previter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + /* need the last child */ + parent = gtk_tree_iter_copy(&previter); + if (! gtk_tree_model_iter_nth_child(MODEL(dive_list), &previter, parent, + gtk_tree_model_iter_n_children(MODEL(dive_list), parent) - 1)) + goto free_iter; + } + go_to_iter(selection, &previter); +free_iter: + gtk_tree_path_free(treepath); + if (parent) + gtk_tree_iter_free(parent); + gtk_tree_iter_free(iter); +} diff --git a/divelist.c b/divelist.c index 198e2d643..f574a31fd 100644 --- a/divelist.c +++ b/divelist.c @@ -1,14 +1,36 @@ /* divelist.c */ -/* this creates the UI for the dive list - - * controlled through the following interfaces: +/* core logic for the dive list - + * accessed 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) + * dive_trip_t *dive_trip_list; + * unsigned int amount_selected; + * void dump_selection(void) + * dive_trip_t *find_trip_by_idx(int idx) + * int trip_has_selected_dives(dive_trip_t *trip) + * void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p) + * int total_weight(struct dive *dive) + * int get_divenr(struct dive *dive) + * double init_decompression(struct dive *dive) + * void update_cylinder_related_info(struct dive *dive) + * void get_location(struct dive *dive, char **str) + * void get_cylinder(struct dive *dive, char **str) + * void get_suit(struct dive *dive, char **str) + * void dump_trip_list(void) + * dive_trip_t *find_matching_trip(timestamp_t when) + * void insert_trip(dive_trip_t **dive_trip_p) + * void remove_dive_from_trip(struct dive *dive) + * void add_dive_to_trip(struct dive *dive, dive_trip_t *trip) + * dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive) + * void autogroup_dives(void) + * void clear_trip_indexes(void) + * void delete_single_dive(int idx) + * void add_single_dive(int idx, struct dive *dive) + * void merge_dive_index(int i, struct dive *a) + * void select_dive(int idx) + * void deselect_dive(int idx) * void mark_divelist_changed(int changed) * int unsaved_changes() + * void remove_autogen_trips() */ #include #include @@ -25,99 +47,16 @@ #include #endif -#include "divelist.h" #include "dive.h" +#include "divelist.h" #include "display.h" -#include "display-gtk.h" #include "webservice.h" -#include -#include "satellite.h" - -struct DiveList { - GtkWidget *tree_view; - GtkWidget *container_widget; - GtkTreeStore *model, *listmodel, *treemodel; - GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; - GtkTreeViewColumn *temperature, *cylinder, *totalweight, *suit, *nitrox, *sac, *otu, *maxcns; - int changed; -}; - -static struct DiveList dive_list; -#define MODEL(_dl) GTK_TREE_MODEL((_dl).model) -#define TREEMODEL(_dl) GTK_TREE_MODEL((_dl).treemodel) -#define LISTMODEL(_dl) GTK_TREE_MODEL((_dl).listmodel) -#define STORE(_dl) GTK_TREE_STORE((_dl).model) -#define TREESTORE(_dl) GTK_TREE_STORE((_dl).treemodel) -#define LISTSTORE(_dl) GTK_TREE_STORE((_dl).listmodel) +static short dive_list_changed = FALSE; dive_trip_t *dive_trip_list; -gboolean autogroup = FALSE; -static gboolean in_set_cursor = FALSE; -static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data); -/* - * The dive list has the dive data in both string format (for showing) - * and in "raw" format (for sorting purposes) - */ -enum { - DIVE_INDEX = 0, - DIVE_NR, /* int: dive->nr */ - DIVE_DATE, /* timestamp_t: dive->when */ - DIVE_RATING, /* int: 0-5 stars */ - DIVE_DEPTH, /* int: dive->maxdepth in mm */ - DIVE_DURATION, /* int: in seconds */ - DIVE_TEMPERATURE, /* int: in mkelvin */ - DIVE_TOTALWEIGHT, /* int: in grams */ - DIVE_SUIT, /* "wet, 3mm" */ - DIVE_CYLINDER, - DIVE_NITROX, /* int: dummy */ - DIVE_SAC, /* int: in ml/min */ - DIVE_OTU, /* int: in OTUs */ - DIVE_MAXCNS, /* int: in % */ - DIVE_LOCATION, /* "2nd Cathedral, Lanai" */ - DIVE_LOC_ICON, /* pixbuf for gps icon */ - DIVELIST_COLUMNS -}; - -static void merge_dive_into_trip_above_cb(GtkWidget *menuitem, GtkTreePath *path); - -#ifdef DEBUG_MODEL -static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - char *location; - int idx, nr, duration; - struct dive *dive; - timestamp_t when; - struct tm tm; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_DATE, &when, - DIVE_DURATION, &duration, DIVE_LOCATION, &location, -1); - utc_mkdate(when, &tm); - printf("iter %x:%x entry #%d : nr %d @ %04d-%02d-%02d %02d:%02d:%02d duration %d location %s ", - iter->stamp, iter->user_data, idx, nr, - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, - duration, location); - dive = get_dive(idx); - if (dive) - printf("tripflag %d\n", dive->tripflag); - else - printf("without matching dive\n"); - - free(location); - - return FALSE; -} - -static void dump_model(GtkListStore *store) -{ - gtk_tree_model_foreach(GTK_TREE_MODEL(store), dump_model_entry, NULL); - printf("\n---\n\n"); -} -#endif +unsigned int amount_selected; #if DEBUG_SELECTION_TRACKING void dump_selection(void) @@ -134,44 +73,7 @@ void dump_selection(void) } #endif -/* when subsurface starts we want to have the last dive selected. So we simply - walk to the first leaf (and skip the summary entries - which have negative - DIVE_INDEX) */ -static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) -{ - GtkTreeIter parent; - GtkTreePath *tpath; - - while (*diveidx < 0) { - memcpy(&parent, iter, sizeof(parent)); - tpath = gtk_tree_model_get_path(model, &parent); - if (!gtk_tree_model_iter_children(model, iter, &parent)) { - /* we should never have a parent without child */ - gtk_tree_path_free(tpath); - return; - } - if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) - gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); - gtk_tree_path_free(tpath); - gtk_tree_model_get(model, iter, DIVE_INDEX, diveidx, -1); - } -} - -static struct dive *dive_from_path(GtkTreePath *path) -{ - GtkTreeIter iter; - int idx; - - if (gtk_tree_model_get_iter(MODEL(dive_list), &iter, path)) { - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); - return get_dive(idx); - } else { - return NULL; - } - -} - -static dive_trip_t *find_trip_by_idx(int idx) +dive_trip_t *find_trip_by_idx(int idx) { dive_trip_t *trip = dive_trip_list; @@ -186,48 +88,7 @@ static dive_trip_t *find_trip_by_idx(int idx) return NULL; } -static int get_path_index(GtkTreePath *path) -{ - GtkTreeIter iter; - int idx; - - gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); - return idx; -} - -/* make sure that if we expand a summary row that is selected, the children show - up as selected, too */ -static void row_expanded_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) -{ - GtkTreeIter child; - GtkTreeModel *model = MODEL(dive_list); - GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - dive_trip_t *trip; - - trip = find_trip_by_idx(get_path_index(path)); - if (!trip) - return; - - trip->expanded = 1; - if (!gtk_tree_model_iter_children(model, &child, iter)) - return; - - do { - int idx; - struct dive *dive; - - gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1); - dive = get_dive(idx); - - if (dive->selected) - gtk_tree_selection_select_iter(selection, &child); - else - gtk_tree_selection_unselect_iter(selection, &child); - } while (gtk_tree_model_iter_next(model, &child)); -} - -static int trip_has_selected_dives(dive_trip_t *trip) +int trip_has_selected_dives(dive_trip_t *trip) { struct dive *dive; for (dive = trip->dives; dive; dive = dive->next) { @@ -237,216 +98,6 @@ static int trip_has_selected_dives(dive_trip_t *trip) return 0; } -/* Make sure that if we collapse a summary row with any selected children, the row - shows up as selected too */ -static void row_collapsed_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) -{ - dive_trip_t *trip; - GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - - trip = find_trip_by_idx(get_path_index(path)); - if (!trip) - return; - - trip->expanded = 0; - if (trip_has_selected_dives(trip)) { - gtk_tree_selection_select_iter(selection, iter); - trip->selected = 1; - } -} - -const char *star_strings[] = { - ZERO_STARS, - ONE_STARS, - TWO_STARS, - THREE_STARS, - FOUR_STARS, - FIVE_STARS -}; - -static void star_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int nr_stars, idx; - char buffer[40]; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_RATING, &nr_stars, -1); - if (idx < 0) { - *buffer = '\0'; - } else { - if (nr_stars < 0 || nr_stars > 5) - nr_stars = 0; - snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]); - } - g_object_set(renderer, "text", buffer, NULL); -} - -static void date_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int idx, nr; - struct tm tm; - timestamp_t when; - /* this should be enought for most languages. if not increase the value. */ - char buffer[256]; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1); - nr = gtk_tree_model_iter_n_children(model, iter); - - utc_mkdate(when, &tm); - if (idx < 0) { - snprintf(buffer, sizeof(buffer), - /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, nr dives */ - ngettext("Trip %1$s, %2$s %3$d, %4$d (%5$d dive)", - "Trip %1$s, %2$s %3$d, %4$d (%5$d dives)", nr), - weekday(tm.tm_wday), - monthname(tm.tm_mon), - tm.tm_mday, tm.tm_year + 1900, - nr); - } else { - snprintf(buffer, sizeof(buffer), - /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, hour:min */ - _("%1$s, %2$s %3$d, %4$d %5$02d:%6$02d"), - weekday(tm.tm_wday), - monthname(tm.tm_mon), - tm.tm_mday, tm.tm_year + 1900, - tm.tm_hour, tm.tm_min); - } - g_object_set(renderer, "text", buffer, NULL); -} - -static void depth_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int depth, integer, frac, len, idx; - char buffer[40]; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1); - - if (idx < 0) { - *buffer = '\0'; - } else { - switch (prefs.units.length) { - case METERS: - /* To tenths of meters */ - depth = (depth + 49) / 100; - integer = depth / 10; - frac = depth % 10; - if (integer < 20) - break; - if (frac >= 5) - integer++; - frac = -1; - break; - case FEET: - integer = mm_to_feet(depth) + 0.5; - frac = -1; - break; - default: - return; - } - len = snprintf(buffer, sizeof(buffer), "%d", integer); - if (frac >= 0) - len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac); - } - g_object_set(renderer, "text", buffer, NULL); -} - -static void duration_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - unsigned int sec; - int idx; - char buffer[40]; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DURATION, &sec, -1); - if (idx < 0) - *buffer = '\0'; - else - snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); - - g_object_set(renderer, "text", buffer, NULL); -} - -static void temperature_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int value, idx; - char buffer[80]; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_TEMPERATURE, &value, -1); - - *buffer = 0; - if (idx >= 0 && value) { - double deg; - switch (prefs.units.temperature) { - case CELSIUS: - deg = mkelvin_to_C(value); - break; - case FAHRENHEIT: - deg = mkelvin_to_F(value); - break; - default: - return; - } - snprintf(buffer, sizeof(buffer), "%.1f", deg); - } - - g_object_set(renderer, "text", buffer, NULL); -} - -static void gpsicon_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int idx; - GdkPixbuf *icon; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_LOC_ICON, &icon, -1); - g_object_set(renderer, "pixbuf", icon, NULL); -} - -static void nr_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int idx, nr; - char buffer[40]; - struct dive *dive; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, -1); - if (idx < 0) { - *buffer = '\0'; - } else { - /* make dives that are not in trips stand out */ - dive = get_dive(idx); - if (!DIVE_IN_TRIP(dive)) - snprintf(buffer, sizeof(buffer), "%d", nr); - else - snprintf(buffer, sizeof(buffer), "%d", nr); - } - g_object_set(renderer, "markup", buffer, NULL); -} - /* * Get "maximal" dive gas for a dive. * Rules: @@ -454,7 +105,7 @@ static void nr_data_func(GtkTreeViewColumn *col, * - Nitrox trumps air (even if hypoxic) * These are the same rules as the inter-dive sorting rules. */ -static void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p) +void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p) { int i; int maxo2 = -1, maxhe = -1, mino2 = 1000; @@ -535,165 +186,6 @@ int total_weight(struct dive *dive) return total_grams; } -static void weight_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int indx, decimals; - double value; - char buffer[80]; - struct dive *dive; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &indx, -1); - dive = get_dive(indx); - value = get_weight_units(total_weight(dive), &decimals, NULL); - if (value == 0.0) - *buffer = '\0'; - else - snprintf(buffer, sizeof(buffer), "%.*f", decimals, value); - - g_object_set(renderer, "text", buffer, NULL); -} - -static gint nitrox_sort_func(GtkTreeModel *model, - GtkTreeIter *iter_a, - GtkTreeIter *iter_b, - gpointer user_data) -{ - int index_a, index_b; - struct dive *a, *b; - int a_o2, b_o2; - int a_he, b_he; - int a_o2low, b_o2low; - - gtk_tree_model_get(model, iter_a, DIVE_INDEX, &index_a, -1); - gtk_tree_model_get(model, iter_b, DIVE_INDEX, &index_b, -1); - a = get_dive(index_a); - b = get_dive(index_b); - get_dive_gas(a, &a_o2, &a_he, &a_o2low); - get_dive_gas(b, &b_o2, &b_he, &b_o2low); - - /* Sort by Helium first, O2 second */ - if (a_he == b_he) { - if (a_o2 == b_o2) - return a_o2low - b_o2low; - return a_o2 - b_o2; - } - return a_he - b_he; -} - -#define UTF8_ELLIPSIS "\xE2\x80\xA6" - -static void nitrox_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int idx, o2, he, o2low; - char buffer[80]; - struct dive *dive; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - if (idx < 0) { - *buffer = '\0'; - goto exit; - } - dive = get_dive(idx); - get_dive_gas(dive, &o2, &he, &o2low); - o2 = (o2 + 5) / 10; - he = (he + 5) / 10; - o2low = (o2low + 5) / 10; - - if (he) - snprintf(buffer, sizeof(buffer), "%d/%d", o2, he); - else if (o2) - if (o2 == o2low) - snprintf(buffer, sizeof(buffer), "%d", o2); - else - snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2); - else - strcpy(buffer, _("air")); -exit: - g_object_set(renderer, "text", buffer, NULL); -} - -/* Render the SAC data (integer value of "ml / min") */ -static void sac_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int value, idx; - const char *fmt; - char buffer[16]; - double sac; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_SAC, &value, -1); - - if (idx < 0 || !value) { - *buffer = '\0'; - goto exit; - } - - sac = value / 1000.0; - switch (prefs.units.volume) { - case LITER: - fmt = "%4.1f"; - break; - case CUFT: - fmt = "%4.2f"; - sac = ml_to_cuft(sac * 1000); - break; - } - snprintf(buffer, sizeof(buffer), fmt, sac); -exit: - g_object_set(renderer, "text", buffer, NULL); -} - -/* Render the OTU data (integer value of "OTU") */ -static void otu_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int value, idx; - char buffer[16]; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_OTU, &value, -1); - - if (idx < 0 || !value) - *buffer = '\0'; - else - snprintf(buffer, sizeof(buffer), "%d", value); - - g_object_set(renderer, "text", buffer, NULL); -} - -/* Render the CNS data (in full %) */ -static void cns_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int value, idx; - char buffer[16]; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_MAXCNS, &value, -1); - - if (idx < 0 || !value) - *buffer = '\0'; - else - snprintf(buffer, sizeof(buffer), "%d%%", value); - - g_object_set(renderer, "text", buffer, NULL); -} - static int active_o2(struct dive *dive, struct divecomputer *dc, duration_t time) { int o2permille = dive->cylinder[0].gasmix.o2.permille; @@ -806,7 +298,7 @@ static void add_dive_to_deco(struct dive *dive) } } -static int get_divenr(struct dive *dive) +int get_divenr(struct dive *dive) { int divenr = -1; while (++divenr < dive_table.nr && get_dive(divenr) != dive) @@ -913,157 +405,27 @@ static void get_string(char **str, const char *s) *str = n; } -static void get_location(struct dive *dive, char **str) +void get_location(struct dive *dive, char **str) { get_string(str, dive->location); } -static void get_cylinder(struct dive *dive, char **str) +void get_cylinder(struct dive *dive, char **str) { get_string(str, dive->cylinder[0].type.description); } -static void get_suit(struct dive *dive, char **str) +void get_suit(struct dive *dive, char **str) { get_string(str, dive->suit); } -GdkPixbuf *get_gps_icon(void) -{ - return gdk_pixbuf_from_pixdata(&satellite_pixbuf, TRUE, NULL); -} - -static GdkPixbuf *get_gps_icon_for_dive(struct dive *dive) -{ - if (dive_has_gps_location(dive)) - return get_gps_icon(); - else - return NULL; -} - -/* - * Set up anything that could have changed due to editing - * of dive information; we need to do this for both models, - * so we simply call set_one_dive again with the non-current model - */ -/* forward declaration for recursion */ -static gboolean set_one_dive(GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); - -static void fill_one_dive(struct dive *dive, - GtkTreeModel *model, - GtkTreeIter *iter) -{ - char *location, *cylinder, *suit; - GtkTreeModel *othermodel; - GdkPixbuf *icon; - - get_cylinder(dive, &cylinder); - get_location(dive, &location); - get_suit(dive, &suit); - icon = get_gps_icon_for_dive(dive); - gtk_tree_store_set(GTK_TREE_STORE(model), iter, - DIVE_NR, dive->number, - DIVE_LOCATION, location, - DIVE_LOC_ICON, icon, - DIVE_CYLINDER, cylinder, - DIVE_RATING, dive->rating, - DIVE_SAC, dive->sac, - DIVE_OTU, dive->otu, - DIVE_MAXCNS, dive->maxcns, - DIVE_TOTALWEIGHT, total_weight(dive), - DIVE_SUIT, suit, - -1); - - if (icon) - g_object_unref(icon); - free(location); - free(cylinder); - free(suit); - - if (model == TREEMODEL(dive_list)) - othermodel = LISTMODEL(dive_list); - else - othermodel = TREEMODEL(dive_list); - if (othermodel != MODEL(dive_list)) - /* recursive call */ - gtk_tree_model_foreach(othermodel, set_one_dive, dive); -} - -static gboolean set_one_dive(GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - int idx; - struct dive *dive; - - /* Get the dive number */ - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - if (idx < 0) - return FALSE; - dive = get_dive(idx); - if (!dive) - return TRUE; - if (data && dive != data) - return FALSE; - - fill_one_dive(dive, model, iter); - return dive == data; -} - -void flush_divelist(struct dive *dive) -{ - GtkTreeModel *model = MODEL(dive_list); - - gtk_tree_model_foreach(model, set_one_dive, dive); -} - -void set_divelist_font(const char *font) -{ - PangoFontDescription *font_desc = pango_font_description_from_string(font); - gtk_widget_modify_font(dive_list.tree_view, font_desc); - pango_font_description_free(font_desc); -} - -void update_dive_list_units(void) -{ - const char *unit; - GtkTreeModel *model = MODEL(dive_list); - - (void) get_depth_units(0, NULL, &unit); - gtk_tree_view_column_set_title(dive_list.depth, unit); - - (void) get_temp_units(0, &unit); - gtk_tree_view_column_set_title(dive_list.temperature, unit); - - (void) get_weight_units(0, NULL, &unit); - gtk_tree_view_column_set_title(dive_list.totalweight, unit); - - gtk_tree_model_foreach(model, set_one_dive, NULL); -} - -void update_dive_list_col_visibility(void) -{ - gtk_tree_view_column_set_visible(dive_list.cylinder, prefs.visible_cols.cylinder); - gtk_tree_view_column_set_visible(dive_list.temperature, prefs.visible_cols.temperature); - gtk_tree_view_column_set_visible(dive_list.totalweight, prefs.visible_cols.totalweight); - gtk_tree_view_column_set_visible(dive_list.suit, prefs.visible_cols.suit); - gtk_tree_view_column_set_visible(dive_list.nitrox, prefs.visible_cols.nitrox); - gtk_tree_view_column_set_visible(dive_list.sac, prefs.visible_cols.sac); - gtk_tree_view_column_set_visible(dive_list.otu, prefs.visible_cols.otu); - gtk_tree_view_column_set_visible(dive_list.maxcns, prefs.visible_cols.maxcns); - return; -} - /* * helper functions for dive_trip handling */ #ifdef DEBUG_TRIP -static void dump_trip_list(void) +void dump_trip_list(void) { dive_trip_t *trip; int i=0; @@ -1086,7 +448,7 @@ static void dump_trip_list(void) #endif /* this finds the last trip that at or before the time given */ -static dive_trip_t *find_matching_trip(timestamp_t when) +dive_trip_t *find_matching_trip(timestamp_t when) { dive_trip_t *trip = dive_trip_list; @@ -1181,7 +543,7 @@ static void find_new_trip_start_time(dive_trip_t *trip) trip->when = when; } -static void remove_dive_from_trip(struct dive *dive) +void remove_dive_from_trip(struct dive *dive) { struct dive *next, **pprev; dive_trip_t *trip = dive->divetrip; @@ -1226,7 +588,7 @@ void add_dive_to_trip(struct dive *dive, dive_trip_t *trip) trip->when = dive->when; } -static dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive) +dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive) { dive_trip_t *dive_trip = calloc(sizeof(dive_trip_t),1); dive_trip->when = dive->when; @@ -1242,7 +604,7 @@ static dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive) /* * Walk the dives from the oldest dive, and see if we can autogroup them */ -static void autogroup_dives(void) +void autogroup_dives(void) { int i; struct dive *dive, *lastdive = NULL; @@ -1280,7 +642,7 @@ static void autogroup_dives(void) #endif } -static void clear_trip_indexes(void) +void clear_trip_indexes(void) { dive_trip_t *trip; @@ -1288,571 +650,6 @@ static void clear_trip_indexes(void) trip->index = 0; } -/* Select the iter asked for, and set the keyboard focus on it */ -static void go_to_iter(GtkTreeSelection *selection, GtkTreeIter *iter); -static void fill_dive_list(void) -{ - int i, trip_index = 0; - GtkTreeIter iter, parent_iter, lookup, *parent_ptr = NULL; - GtkTreeStore *liststore, *treestore; - GdkPixbuf *icon; - - /* Do we need to create any dive groups automatically? */ - if (autogroup) - autogroup_dives(); - - treestore = TREESTORE(dive_list); - liststore = LISTSTORE(dive_list); - - clear_trip_indexes(); - - i = dive_table.nr; - while (--i >= 0) { - struct dive *dive = get_dive(i); - dive_trip_t *trip = dive->divetrip; - - if (!trip) { - parent_ptr = NULL; - } else if (!trip->index) { - trip->index = ++trip_index; - - /* Create new trip entry */ - gtk_tree_store_append(treestore, &parent_iter, NULL); - parent_ptr = &parent_iter; - - /* a duration of 0 (and negative index) identifies a group */ - gtk_tree_store_set(treestore, parent_ptr, - DIVE_INDEX, -trip_index, - DIVE_DATE, trip->when, - DIVE_LOCATION, trip->location, - DIVE_DURATION, 0, - -1); - } else { - int idx, ok; - GtkTreeModel *model = TREEMODEL(dive_list); - - parent_ptr = NULL; - ok = gtk_tree_model_get_iter_first(model, &lookup); - while (ok) { - gtk_tree_model_get(model, &lookup, DIVE_INDEX, &idx, -1); - if (idx == -trip->index) { - parent_ptr = &lookup; - break; - } - ok = gtk_tree_model_iter_next(model, &lookup); - } - } - - /* store dive */ - update_cylinder_related_info(dive); - gtk_tree_store_append(treestore, &iter, parent_ptr); - icon = get_gps_icon_for_dive(dive); - gtk_tree_store_set(treestore, &iter, - DIVE_INDEX, i, - DIVE_NR, dive->number, - DIVE_DATE, dive->when, - DIVE_DEPTH, dive->maxdepth, - DIVE_DURATION, dive->duration.seconds, - DIVE_LOCATION, dive->location, - DIVE_LOC_ICON, icon, - DIVE_RATING, dive->rating, - DIVE_TEMPERATURE, dive->watertemp.mkelvin, - DIVE_SAC, 0, - -1); - if (icon) - g_object_unref(icon); - gtk_tree_store_append(liststore, &iter, NULL); - gtk_tree_store_set(liststore, &iter, - DIVE_INDEX, i, - DIVE_NR, dive->number, - DIVE_DATE, dive->when, - DIVE_DEPTH, dive->maxdepth, - DIVE_DURATION, dive->duration.seconds, - DIVE_LOCATION, dive->location, - DIVE_LOC_ICON, icon, - DIVE_RATING, dive->rating, - DIVE_TEMPERATURE, dive->watertemp.mkelvin, - DIVE_TOTALWEIGHT, 0, - DIVE_SUIT, dive->suit, - DIVE_SAC, 0, - -1); - } - - update_dive_list_units(); - if (amount_selected == 0 && gtk_tree_model_get_iter_first(MODEL(dive_list), &iter)) { - GtkTreeSelection *selection; - - /* select the last dive (and make sure it's an actual dive that is selected) */ - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &selected_dive, -1); - first_leaf(MODEL(dive_list), &iter, &selected_dive); - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - go_to_iter(selection, &iter); - } -} - -static void restore_tree_state(void); - -void dive_list_update_dives(void) -{ - dive_table.preexisting = dive_table.nr; - gtk_tree_store_clear(TREESTORE(dive_list)); - gtk_tree_store_clear(LISTSTORE(dive_list)); - fill_dive_list(); - restore_tree_state(); - repaint_dive(); -} - -static gint dive_nr_sort(GtkTreeModel *model, - GtkTreeIter *iter_a, - GtkTreeIter *iter_b, - gpointer user_data) -{ - int idx_a, idx_b; - timestamp_t when_a, when_b; - struct dive *a, *b; - dive_trip_t *tripa = NULL, *tripb = NULL; - - gtk_tree_model_get(model, iter_a, DIVE_INDEX, &idx_a, DIVE_DATE, &when_a, -1); - gtk_tree_model_get(model, iter_b, DIVE_INDEX, &idx_b, DIVE_DATE, &when_b, -1); - - if (idx_a < 0) { - a = NULL; - tripa = find_trip_by_idx(idx_a); - } else { - a = get_dive(idx_a); - if (a) - tripa = a->divetrip; - } - - if (idx_b < 0) { - b = NULL; - tripb = find_trip_by_idx(idx_b); - } else { - b = get_dive(idx_b); - if (b) - tripb = b->divetrip; - } - - /* - * Compare dive dates within the same trip (or when there - * are no trips involved at all). But if we have two - * different trips use the trip dates for comparison - */ - if (tripa != tripb) { - if (tripa) - when_a = tripa->when; - if (tripb) - when_b = tripb->when; - } - return when_a - when_b; -} - - -static struct divelist_column { - const char *header; - data_func_t data; - sort_func_t sort; - unsigned int flags; - int *visible; -} dl_column[] = { - [DIVE_NR] = { "#", nr_data_func, dive_nr_sort, ALIGN_RIGHT }, - [DIVE_DATE] = { N_("Date"), date_data_func, NULL, ALIGN_LEFT }, - [DIVE_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT }, - [DIVE_DEPTH] = { N_("ft"), depth_data_func, NULL, ALIGN_RIGHT }, - [DIVE_DURATION] = { N_("min"), duration_data_func, NULL, ALIGN_RIGHT }, - [DIVE_TEMPERATURE] = { UTF8_DEGREE "F", temperature_data_func, NULL, ALIGN_RIGHT, &prefs.visible_cols.temperature }, - [DIVE_TOTALWEIGHT] = { N_("lbs"), weight_data_func, NULL, ALIGN_RIGHT, &prefs.visible_cols.totalweight }, - [DIVE_SUIT] = { N_("Suit"), NULL, NULL, ALIGN_LEFT, &prefs.visible_cols.suit }, - [DIVE_CYLINDER] = { N_("Cyl"), NULL, NULL, 0, &prefs.visible_cols.cylinder }, - [DIVE_NITROX] = { "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, nitrox_sort_func, 0, &prefs.visible_cols.nitrox }, - [DIVE_SAC] = { N_("SAC"), sac_data_func, NULL, 0, &prefs.visible_cols.sac }, - [DIVE_OTU] = { N_("OTU"), otu_data_func, NULL, 0, &prefs.visible_cols.otu }, - [DIVE_MAXCNS] = { N_("maxCNS"), cns_data_func, NULL, 0, &prefs.visible_cols.maxcns }, - [DIVE_LOCATION] = { N_("Location"), NULL, NULL, ALIGN_LEFT }, -}; - - -static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_column *col) -{ - int index = col - &dl_column[0]; - const char *title = _(col->header); - data_func_t data_func = col->data; - sort_func_t sort_func = col->sort; - unsigned int flags = col->flags; - int *visible = col->visible; - GtkWidget *tree_view = dl->tree_view; - GtkTreeStore *treemodel = dl->treemodel; - GtkTreeStore *listmodel = dl->listmodel; - GtkTreeViewColumn *ret; - - if (visible && !*visible) - flags |= INVISIBLE; - ret = tree_view_column(tree_view, index, title, data_func, flags); - if (sort_func) { - /* the sort functions are needed in the corresponding models */ - if (index == DIVE_NR) - gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(treemodel), index, sort_func, NULL, NULL); - else - gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(listmodel), index, sort_func, NULL, NULL); - } - return ret; -} - -/* - * This is some crazy crap. The only way to get default focus seems - * to be to grab focus as the widget is being shown the first time. - */ -static void realize_cb(GtkWidget *tree_view, gpointer userdata) -{ - gtk_widget_grab_focus(tree_view); -} - -/* - * Double-clicking on a group entry will expand a collapsed group - * and vice versa. - */ -static void collapse_expand(GtkTreeView *tree_view, GtkTreePath *path) -{ - if (!gtk_tree_view_row_expanded(tree_view, path)) - gtk_tree_view_expand_row(tree_view, path, FALSE); - else - gtk_tree_view_collapse_row(tree_view, path); - -} - -/* Double-click on a dive list */ -static void row_activated_cb(GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gpointer userdata) -{ - int index; - GtkTreeIter iter; - - if (!gtk_tree_model_get_iter(MODEL(dive_list), &iter, path)) - return; - - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &index, -1); - /* a negative index is special for the "group by date" entries */ - if (index < 0) { - collapse_expand(tree_view, path); - return; - } - edit_dive_info(get_dive(index), FALSE); -} - -void add_dive_cb(GtkWidget *menuitem, gpointer data) -{ - struct dive *dive; - - dive = alloc_dive(); - if (add_new_dive(dive)) { - record_dive(dive); - report_dives(TRUE, FALSE); - return; - } - free(dive); -} - -static void edit_trip_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - int idx; - GtkTreeIter iter; - dive_trip_t *dive_trip; - - gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); - dive_trip = find_trip_by_idx(idx); - if (edit_trip(dive_trip)) - gtk_tree_store_set(STORE(dive_list), &iter, DIVE_LOCATION, dive_trip->location, -1); -} - -static void edit_selected_dives_cb(GtkWidget *menuitem, gpointer data) -{ - edit_multi_dive_info(NULL); -} - -static void edit_dive_from_path_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - struct dive *dive = dive_from_path(path); - - edit_multi_dive_info(dive); -} - -static void edit_dive_when_cb(GtkWidget *menuitem, struct dive *dive) -{ - GtkWidget *dialog, *cal, *h, *m, *timehbox; - timestamp_t when; - - guint yval, mval, dval; - int success; - struct tm tm; - - if (!dive) - return; - - when = dive->when; - utc_mkdate(when, &tm); - dialog = create_date_time_widget(&tm, &cal, &h, &m, &timehbox); - - gtk_widget_show_all(dialog); - success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - if (!success) { - gtk_widget_destroy(dialog); - return; - } - memset(&tm, 0, sizeof(tm)); - gtk_calendar_get_date(GTK_CALENDAR(cal), &yval, &mval, &dval); - tm.tm_year = yval; - tm.tm_mon = mval; - tm.tm_mday = dval; - tm.tm_hour = gtk_spin_button_get_value(GTK_SPIN_BUTTON(h)); - tm.tm_min = gtk_spin_button_get_value(GTK_SPIN_BUTTON(m)); - - gtk_widget_destroy(dialog); - when = utc_mktime(&tm); - if (dive->when != when) { - /* if this is the only dive in the trip, just change the trip time */ - if (dive->divetrip && dive->divetrip->nrdives == 1) - dive->divetrip->when = when; - /* if this is suddenly before the start of the trip, remove it from the trip */ - else if (dive->divetrip && dive->divetrip->when > when) - remove_dive_from_trip(dive); - else if (find_matching_trip(when) != dive->divetrip) - remove_dive_from_trip(dive); - dive->when = when; - mark_divelist_changed(TRUE); - report_dives(FALSE, FALSE); - dive_list_update_dives(); - } -} - -#if HAVE_OSM_GPS_MAP -static void show_gps_location_cb(GtkWidget *menuitem, struct dive *dive) -{ - show_gps_location(dive, NULL); -} -#endif - -gboolean icon_click_cb(GtkWidget *w, GdkEventButton *event, gpointer data) -{ -#if HAVE_OSM_GPS_MAP - GtkTreePath *path = NULL; - GtkTreeIter iter; - GtkTreeViewColumn *col; - int idx; - struct dive *dive; - - /* left click ? */ - if (event->button == 1 && - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(dive_list.tree_view), event->x, event->y, &path, &col, NULL, NULL)) { - /* is it the icon column ? (we passed the correct column in when registering the callback) */ - if (col == data) { - gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); - dive = get_dive(idx); - if (dive && dive_has_gps_location(dive)) - show_gps_location(dive, NULL); - } - if (path) - gtk_tree_path_free(path); - } -#endif - /* keep processing the click */ - return FALSE; -} - -static void save_as_cb(GtkWidget *menuitem, struct dive *dive) -{ - GtkWidget *dialog; - char *filename = NULL; - - dialog = gtk_file_chooser_dialog_new(_("Save File As"), - GTK_WINDOW(main_window), - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - } - gtk_widget_destroy(dialog); - - if (filename){ - save_dives_logic(filename, TRUE); - g_free(filename); - } -} - -static void expand_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view) -{ - gtk_tree_view_expand_all(tree_view); -} - -static void collapse_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view) -{ - gtk_tree_view_collapse_all(tree_view); -} - -/* Move a top-level dive into the trip above it */ -static void merge_dive_into_trip_above_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - int idx; - struct dive *dive; - dive_trip_t *trip; - - idx = get_path_index(path); - dive = get_dive(idx); - - /* Needs to be a dive, and at the top level */ - if (!dive || dive->divetrip) - return; - - /* Find the "trip above". */ - for (;;) { - if (!gtk_tree_path_prev(path)) - return; - idx = get_path_index(path); - trip = find_trip_by_idx(idx); - if (trip) - break; - } - - add_dive_to_trip(dive, trip); - if (dive->selected) { - for_each_dive(idx, dive) { - if (!dive->selected) - continue; - add_dive_to_trip(dive, trip); - } - } - - trip->expanded = 1; - dive_list_update_dives(); - mark_divelist_changed(TRUE); -} - -static void insert_trip_before_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - int idx; - struct dive *dive; - dive_trip_t *trip; - - idx = get_path_index(path); - dive = get_dive(idx); - if (!dive) - return; - trip = create_and_hookup_trip_from_dive(dive); - if (dive->selected) { - for_each_dive(idx, dive) { - if (!dive->selected) - continue; - add_dive_to_trip(dive, trip); - } - } - trip->expanded = 1; - dive_list_update_dives(); - mark_divelist_changed(TRUE); -} - -static void remove_from_trip_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - struct dive *dive; - int idx; - - idx = get_path_index(path); - if (idx < 0) - return; - dive = get_dive(idx); - - if (dive->selected) { - /* remove all the selected dives */ - for_each_dive(idx, dive) { - if (!dive->selected) - continue; - remove_dive_from_trip(dive); - } - } else { - /* just remove the dive the mouse pointer is on */ - remove_dive_from_trip(dive); - } - dive_list_update_dives(); - mark_divelist_changed(TRUE); -} - -static void remove_trip(GtkTreePath *trippath) -{ - int idx, i; - dive_trip_t *trip; - struct dive *dive; - - idx = get_path_index(trippath); - trip = find_trip_by_idx(idx); - if (!trip) - return; - - for_each_dive(i, dive) { - if (dive->divetrip != trip) - continue; - remove_dive_from_trip(dive); - } - - dive_list_update_dives(); - -#ifdef DEBUG_TRIP - dump_trip_list(); -#endif -} - -static void remove_trip_cb(GtkWidget *menuitem, GtkTreePath *trippath) -{ - int success; - GtkWidget *dialog; - - dialog = gtk_dialog_new_with_buttons(_("Remove Trip"), - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - gtk_widget_show_all(dialog); - success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - gtk_widget_destroy(dialog); - if (!success) - return; - - remove_trip(trippath); - mark_divelist_changed(TRUE); -} - -static void merge_trips_cb(GtkWidget *menuitem, GtkTreePath *trippath) -{ - GtkTreePath *prevpath; - GtkTreeIter thistripiter, prevtripiter; - GtkTreeModel *tm = MODEL(dive_list); - dive_trip_t *thistrip, *prevtrip; - timestamp_t when; - - /* this only gets called when we are on a trip and there is another trip right before */ - prevpath = gtk_tree_path_copy(trippath); - gtk_tree_path_prev(prevpath); - gtk_tree_model_get_iter(tm, &thistripiter, trippath); - gtk_tree_model_get(tm, &thistripiter, DIVE_DATE, &when, -1); - thistrip = find_matching_trip(when); - gtk_tree_model_get_iter(tm, &prevtripiter, prevpath); - gtk_tree_model_get(tm, &prevtripiter, DIVE_DATE, &when, -1); - prevtrip = find_matching_trip(when); - /* move dives from trip */ - assert(thistrip != prevtrip); - while (thistrip->dives) - add_dive_to_trip(thistrip->dives, prevtrip); - dive_list_update_dives(); - mark_divelist_changed(TRUE); -} - /* this implements the mechanics of removing the dive from the table, * but doesn't deal with updating dive trips, etc */ void delete_single_dive(int idx) @@ -1895,300 +692,7 @@ void add_single_dive(int idx, struct dive *dive) } } -static gboolean restore_node_state(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) -{ - int idx; - struct dive *dive; - dive_trip_t *trip; - GtkTreeView *tree_view = GTK_TREE_VIEW(dive_list.tree_view); - GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view); - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - if (idx < 0) { - trip = find_trip_by_idx(idx); - if (trip && trip->expanded) - gtk_tree_view_expand_row(tree_view, path, FALSE); - if (trip && trip->selected) - gtk_tree_selection_select_iter(selection, iter); - } else { - dive = get_dive(idx); - if (dive && dive->selected) - gtk_tree_selection_select_iter(selection, iter); - } - /* continue foreach */ - return FALSE; -} - -/* restore expanded and selected state */ -static void restore_tree_state(void) -{ - gtk_tree_model_foreach(MODEL(dive_list), restore_node_state, NULL); -} - -/* called when multiple dives are selected and one of these is right-clicked for delete */ -static void delete_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - int i; - struct dive *dive; - int success; - GtkWidget *dialog; - char *dialog_title; - - if (!amount_selected) - return; - if (amount_selected == 1) - dialog_title = _("Delete dive"); - else - dialog_title = _("Delete dives"); - - dialog = gtk_dialog_new_with_buttons(dialog_title, - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - gtk_widget_show_all(dialog); - success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - gtk_widget_destroy(dialog); - if (!success) - return; - - /* walk the dive list in chronological order */ - for (i = 0; i < dive_table.nr; i++) { - dive = get_dive(i); - if (!dive) - continue; - if (!dive->selected) - continue; - /* now remove the dive from the table and free it. also move the iterator back, - * so that we don't skip a dive */ - delete_single_dive(i); - i--; - } - dive_list_update_dives(); - - /* if no dives are selected at this point clear the display widgets */ - if (!amount_selected) { - selected_dive = 0; - process_selected_dives(); - clear_stats_widgets(); - clear_equipment_widgets(); - show_dive_info(NULL); - } - mark_divelist_changed(TRUE); -} - -/* this gets called with path pointing to a dive, either in the top level - * or as part of a trip */ -static void delete_dive_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - int idx; - GtkTreeIter iter; - int success; - GtkWidget *dialog; - - dialog = gtk_dialog_new_with_buttons(_("Delete dive"), - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - gtk_widget_show_all(dialog); - success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - gtk_widget_destroy(dialog); - if (!success) - return; - - if (!gtk_tree_model_get_iter(MODEL(dive_list), &iter, path)) - return; - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); - delete_single_dive(idx); - dive_list_update_dives(); - mark_divelist_changed(TRUE); -} - -#if defined(LIBZIP) && defined(XSLT) -static void export_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - int i; - struct dive *dive; - FILE *f; - char filename[PATH_MAX], *tempfile; - size_t streamsize; - char *membuf; - xmlDoc *doc; - xsltStylesheetPtr xslt = NULL; - xmlDoc *transformed; - struct zip_source *s[dive_table.nr]; - struct zip *zip; - const gchar *tmpdir = g_get_tmp_dir(); - - /* - * Creating a temporary .DLD file to be eventually uploaded to - * divelogs.de. I wonder if this could be done in-memory. - */ - tempfile = g_build_filename(tmpdir, "export.DLD-XXXXXX", NULL); - int fd = g_mkstemp(tempfile); - if (fd != -1) - close(fd); - zip = zip_open(tempfile, ZIP_CREATE, NULL); - - if (!zip) - return; - - if (!amount_selected) - return; - - /* walk the dive list in chronological order */ - for (i = 0; i < dive_table.nr; i++) { - - dive = get_dive(i); - if (!dive) - continue; - if (!dive->selected) - continue; - - f = tmpfile(); - if (!f) - return; - save_dive(f, dive); - fseek(f, 0, SEEK_END); - streamsize = ftell(f); - rewind(f); - membuf = malloc(streamsize + 1); - if (!membuf || !fread(membuf, streamsize, 1, f)) - return; - membuf[streamsize] = 0; - fclose(f); - - /* - * Parse the memory buffer into XML document and - * transform it to divelogs.de format, finally dumping - * the XML into a character buffer. - */ - doc = xmlReadMemory(membuf, strlen(membuf), "divelog", NULL, 0); - if (!doc) - continue; - - free((void *)membuf); - xslt = get_stylesheet("divelogs-export.xslt"); - if (!xslt) - return; - transformed = xsltApplyStylesheet(xslt, doc, NULL); - xsltFreeStylesheet(xslt); - xmlDocDumpMemory(transformed, (xmlChar **) &membuf, (int *)&streamsize); - xmlFreeDoc(doc); - xmlFreeDoc(transformed); - - /* - * Save the XML document into a zip file. - */ - snprintf(filename, PATH_MAX, "%d.xml", i + 1); - s[i] = zip_source_buffer(zip, membuf, streamsize, 1); - if (s[i]) { - int64_t ret = zip_add(zip, filename, s[i]); - if (ret == -1) - fprintf(stderr, "failed to include dive %d\n", i); - } - } - zip_close(zip); - if (divelogde_upload(tempfile)) - g_unlink(tempfile); - else - fprintf(stderr,"upload of %s failed\n", tempfile); - g_free(tempfile); -} -#endif - -#if defined(XSLT) -static void export_dives_uddf(const gboolean selected) -{ - FILE *f; - char *filename = NULL; - size_t streamsize; - char *membuf; - xmlDoc *doc; - xsltStylesheetPtr xslt = NULL; - xmlDoc *transformed; - GtkWidget *dialog; - - dialog = gtk_file_chooser_dialog_new(_("Export As UDDF 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 (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - } - gtk_widget_destroy(dialog); - - if (!filename) - return; - - /* Save XML to file and convert it into a memory buffer */ - save_dives_logic(filename, selected); - f = fopen(filename, "r"); - fseek(f, 0, SEEK_END); - streamsize = ftell(f); - rewind(f); - - membuf = malloc(streamsize + 1); - if (!membuf || !fread(membuf, streamsize, 1, f)) { - fprintf(stderr, "Failed to read memory buffer\n"); - return; - } - membuf[streamsize] = 0; - fclose(f); - g_unlink(filename); - - /* - * Parse the memory buffer into XML document and - * transform it to UDDF format, finally dumping - * the XML into a character buffer. - */ - doc = xmlReadMemory(membuf, strlen(membuf), "divelog", NULL, 0); - if (!doc) { - fprintf(stderr, "Failed to read XML memory\n"); - return; - } - free((void *)membuf); - - /* Convert to UDDF format */ - xslt = get_stylesheet("uddf-export.xslt"); - if (!xslt) { - fprintf(stderr, "Failed to open UDDF conversion stylesheet\n"); - return; - } - transformed = xsltApplyStylesheet(xslt, doc, NULL); - xsltFreeStylesheet(xslt); - xmlFreeDoc(doc); - - /* Write the transformed XML to file */ - f = g_fopen(filename, "w"); - xmlDocFormatDump(f, transformed, 1); - xmlFreeDoc(transformed); - - fclose(f); - g_free(filename); -} - -static void export_selected_dives_uddf_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - export_dives_uddf(TRUE); -} - -void export_all_dives_uddf_cb() -{ - export_dives_uddf(FALSE); -} -#endif - -static void merge_dive_index(int i, struct dive *a) +void merge_dive_index(int i, struct dive *a) { struct dive *b = get_dive(i+1); struct dive *res; @@ -2205,369 +709,7 @@ static void merge_dive_index(int i, struct dive *a) mark_divelist_changed(TRUE); } -static void merge_dives_cb(GtkWidget *menuitem, void *unused) -{ - int i; - struct dive *dive; - - for_each_dive(i, dive) { - if (dive->selected) { - merge_dive_index(i, dive); - return; - } - } -} - -/* Called if there are exactly two selected dives and the dive at idx is one of them */ -static void add_dive_merge_label(int idx, GtkMenuShell *menu) -{ - struct dive *a, *b; - GtkWidget *menuitem; - - /* The other selected dive must be next to it.. */ - a = get_dive(idx); - b = get_dive(idx+1); - if (!b || !b->selected) { - b = a; - a = get_dive(idx-1); - if (!a || !a->selected) - return; - } - - /* .. and they had better be in the same dive trip */ - if (a->divetrip != b->divetrip) - return; - - /* .. and if the surface interval is excessive, you must be kidding us */ - if (b->when > a->when + a->duration.seconds + 30*60) - return; - - /* If so, we can add a "merge dive" menu entry */ - menuitem = gtk_menu_item_new_with_label(_("Merge dives")); - g_signal_connect(menuitem, "activate", G_CALLBACK(merge_dives_cb), NULL); - gtk_menu_shell_append(menu, menuitem); -} - -static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button, GdkEventButton *event) -{ - GtkWidget *menu, *menuitem, *image; - char editplurallabel[] = N_("Edit dives"); - char editsinglelabel[] = N_("Edit dive"); - char *editlabel; - char deleteplurallabel[] = N_("Delete dives"); - char deletesinglelabel[] = N_("Delete dive"); - char *deletelabel; -#if defined(XSLT) - char exportuddflabel[] = N_("Export dive(s) to UDDF"); -#endif -#if defined(LIBZIP) && defined(XSLT) - char exportlabel[] = N_("Export dive(s)"); -#endif - GtkTreePath *path, *prevpath, *nextpath; - GtkTreeIter iter, previter, nextiter; - int idx, previdx, nextidx; - struct dive *dive; - - if (!event || !gtk_tree_view_get_path_at_pos(tree_view, event->x, event->y, &path, NULL, NULL, NULL)) - return; - gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); - - menu = gtk_menu_new(); - menuitem = gtk_image_menu_item_new_with_label(_("Add dive")); - image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); - g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - if (idx < 0) { - /* mouse pointer is on a trip summary entry */ - menuitem = gtk_menu_item_new_with_label(_("Edit Trip Summary")); - g_signal_connect(menuitem, "activate", G_CALLBACK(edit_trip_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - prevpath = gtk_tree_path_copy(path); - if (gtk_tree_path_prev(prevpath) && - gtk_tree_model_get_iter(MODEL(dive_list), &previter, prevpath)) { - gtk_tree_model_get(MODEL(dive_list), &previter, DIVE_INDEX, &previdx, -1); - if (previdx < 0) { - menuitem = gtk_menu_item_new_with_label(_("Merge trip with trip above")); - g_signal_connect(menuitem, "activate", G_CALLBACK(merge_trips_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } - } - nextpath = gtk_tree_path_copy(path); - gtk_tree_path_next(nextpath); - if (gtk_tree_model_get_iter(MODEL(dive_list), &nextiter, nextpath)) { - gtk_tree_model_get(MODEL(dive_list), &nextiter, DIVE_INDEX, &nextidx, -1); - if (nextidx < 0) { - menuitem = gtk_menu_item_new_with_label(_("Merge trip with trip below")); - g_signal_connect(menuitem, "activate", G_CALLBACK(merge_trips_cb), nextpath); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } - } - menuitem = gtk_menu_item_new_with_label(_("Remove Trip")); - g_signal_connect(menuitem, "activate", G_CALLBACK(remove_trip_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } else { - dive = get_dive(idx); - /* if we right click on selected dive(s), edit or delete those */ - if (dive->selected) { - if (amount_selected == 1) { - deletelabel = _(deletesinglelabel); - editlabel = _(editsinglelabel); - menuitem = gtk_menu_item_new_with_label(_("Edit dive date/time")); - g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_when_cb), dive); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } else { - deletelabel = _(deleteplurallabel); - editlabel = _(editplurallabel); - } - menuitem = gtk_menu_item_new_with_label(_("Save as")); - g_signal_connect(menuitem, "activate", G_CALLBACK(save_as_cb), dive); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - menuitem = gtk_menu_item_new_with_label(deletelabel); - g_signal_connect(menuitem, "activate", G_CALLBACK(delete_selected_dives_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - -#if defined(LIBZIP) && defined(XSLT) - menuitem = gtk_menu_item_new_with_label(exportlabel); - g_signal_connect(menuitem, "activate", G_CALLBACK(export_selected_dives_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); -#endif - -#if defined(XSLT) - menuitem = gtk_menu_item_new_with_label(exportuddflabel); - g_signal_connect(menuitem, "activate", G_CALLBACK(export_selected_dives_uddf_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); -#endif - - menuitem = gtk_menu_item_new_with_label(editlabel); - g_signal_connect(menuitem, "activate", G_CALLBACK(edit_selected_dives_cb), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - /* Two contiguous selected dives? */ - if (amount_selected == 2) - add_dive_merge_label(idx, GTK_MENU_SHELL(menu)); - } else { - menuitem = gtk_menu_item_new_with_label(_("Edit dive date/time")); - g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_when_cb), dive); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - deletelabel = _(deletesinglelabel); - menuitem = gtk_menu_item_new_with_label(deletelabel); - g_signal_connect(menuitem, "activate", G_CALLBACK(delete_dive_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - editlabel = _(editsinglelabel); - menuitem = gtk_menu_item_new_with_label(editlabel); - g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_from_path_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } -#if HAVE_OSM_GPS_MAP - /* Only offer to show on map if it has a location. */ - if (dive_has_gps_location(dive)) { - menuitem = gtk_menu_item_new_with_label(_("Show in map")); - g_signal_connect(menuitem, "activate", G_CALLBACK(show_gps_location_cb), dive); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } -#endif - /* only offer trip editing options when we are displaying the tree model */ - if (dive_list.model == dive_list.treemodel) { - int depth = gtk_tree_path_get_depth(path); - int *indices = gtk_tree_path_get_indices(path); - /* top level dive or child dive that is not the first child */ - if (depth == 1 || indices[1] > 0) { - menuitem = gtk_menu_item_new_with_label(_("Create new trip above")); - g_signal_connect(menuitem, "activate", G_CALLBACK(insert_trip_before_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } - prevpath = gtk_tree_path_copy(path); - /* top level dive with a trip right before it */ - if (depth == 1 && - gtk_tree_path_prev(prevpath) && - gtk_tree_model_get_iter(MODEL(dive_list), &previter, prevpath) && - gtk_tree_model_iter_n_children(model, &previter)) { - menuitem = gtk_menu_item_new_with_label(_("Add to trip above")); - g_signal_connect(menuitem, "activate", G_CALLBACK(merge_dive_into_trip_above_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } - if (DIVE_IN_TRIP(dive)) { - if (dive->selected && amount_selected > 1) - menuitem = gtk_menu_item_new_with_label(_("Remove selected dives from trip")); - else - menuitem = gtk_menu_item_new_with_label(_("Remove dive from trip")); - g_signal_connect(menuitem, "activate", G_CALLBACK(remove_from_trip_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } - } - } - menuitem = gtk_menu_item_new_with_label(_("Expand all")); - g_signal_connect(menuitem, "activate", G_CALLBACK(expand_all_cb), tree_view); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - menuitem = gtk_menu_item_new_with_label(_("Collapse all")); - g_signal_connect(menuitem, "activate", G_CALLBACK(collapse_all_cb), tree_view); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - gtk_widget_show_all(menu); - - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, - button, gtk_get_current_event_time()); -} - -static void popup_menu_cb(GtkTreeView *tree_view, gpointer userdata) -{ - popup_divelist_menu(tree_view, MODEL(dive_list), 0, NULL); -} - -static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpointer userdata) -{ - /* Right-click? Bring up the menu */ - if (event->type == GDK_BUTTON_PRESS && event->button == 3) { - popup_divelist_menu(GTK_TREE_VIEW(treeview), MODEL(dive_list), 3, event); - return TRUE; - } - return FALSE; -} - -/* make sure 'path' is shown in the divelist widget; since set_cursor changes the - * selection to be only 'path' we need to let our selection handling callbacks know - * that we didn't really mean this */ -static void scroll_to_path(GtkTreePath *path) -{ - GtkTreeSelection *selection; - - gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); - gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(dive_list.tree_view), path, NULL, FALSE, 0, 0); - in_set_cursor = TRUE; - gtk_tree_view_set_cursor(GTK_TREE_VIEW(dive_list.tree_view), path, NULL, FALSE); - in_set_cursor = FALSE; - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - gtk_tree_model_foreach(MODEL(dive_list), set_selected, selection); - -} - -/* we need to have a temporary copy of the selected dives while - switching model as the selection_cb function keeps getting called - when gtk_tree_selection_select_path is called. We also need to - keep copies of the sort order so we can restore that as well after - switching models. */ -static gboolean second_call = FALSE; -static GtkSortType sortorder[] = { [0 ... DIVELIST_COLUMNS - 1] = GTK_SORT_DESCENDING, }; -static int lastcol = DIVE_NR; - -/* Check if this dive was selected previously and select it again in the new model; - * This is used after we switch models to maintain consistent selections. - * We always return FALSE to iterate through all dives */ -static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - GtkTreeSelection *selection = GTK_TREE_SELECTION(data); - int idx, selected; - struct dive *dive; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - if (idx < 0) { - /* this is a trip - restore its state */ - dive_trip_t *trip = find_trip_by_idx(idx); - if (trip && trip->expanded) - gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); - if (trip && trip->selected) - gtk_tree_selection_select_path(selection, path); - } else { - dive = get_dive(idx); - selected = dive && dive->selected; - if (selected) { - gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); - gtk_tree_selection_select_path(selection, path); - } - } - return FALSE; -} - -static gboolean scroll_to_this(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - int idx; - struct dive *dive; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - dive = get_dive(idx); - if (dive == current_dive) { - scroll_to_path(path); - return TRUE; - } - return FALSE; -} - -static void scroll_to_current(GtkTreeModel *model) -{ - if (current_dive) - gtk_tree_model_foreach(model, scroll_to_this, current_dive); -} - -static void update_column_and_order(int colid) -{ - /* Careful: the index into treecolumns is off by one as we don't have a - tree_view column for DIVE_INDEX */ - GtkTreeViewColumn **treecolumns = &dive_list.nr; - - /* this will trigger a second call into sort_column_change_cb, - so make sure we don't start an infinite recursion... */ - second_call = TRUE; - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, sortorder[colid]); - gtk_tree_view_column_set_sort_order(treecolumns[colid - 1], sortorder[colid]); - second_call = FALSE; - scroll_to_current(GTK_TREE_MODEL(dive_list.model)); -} - -/* If the sort column is nr (default), show the tree model. - For every other sort column only show the list model. - If the model changed, inform the new model of the chosen sort column and make - sure the same dives are still selected. - - The challenge with this function is that once we change the model - we also need to change the sort column again (as it was changed in - the other model) and that causes this function to be called - recursively - so we need to catch that. -*/ -static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) -{ - int colid; - GtkSortType order; - GtkTreeStore *currentmodel = dive_list.model; - - gtk_widget_grab_focus(dive_list.tree_view); - if (second_call) - return; - - gtk_tree_sortable_get_sort_column_id(treeview, &colid, &order); - if (colid == lastcol) { - /* we just changed sort order */ - sortorder[colid] = order; - return; - } else { - lastcol = colid; - } - if (colid == DIVE_NR) - dive_list.model = dive_list.treemodel; - else - dive_list.model = dive_list.listmodel; - if (dive_list.model != currentmodel) { - GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - - gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), MODEL(dive_list)); - update_column_and_order(colid); - gtk_tree_model_foreach(MODEL(dive_list), set_selected, selection); - } else { - if (order != sortorder[colid]) { - update_column_and_order(colid); - } - } -} - -static void select_dive(int idx) +void select_dive(int idx) { struct dive *dive = get_dive(idx); if (dive && !dive->selected) { @@ -2577,7 +719,7 @@ static void select_dive(int idx) } } -static void deselect_dive(int idx) +void deselect_dive(int idx) { struct dive *dive = get_dive(idx); if (dive && dive->selected) { @@ -2602,266 +744,14 @@ static void deselect_dive(int idx) } } -static gboolean modify_selection_cb(GtkTreeSelection *selection, GtkTreeModel *model, - GtkTreePath *path, gboolean was_selected, gpointer userdata) -{ - int idx; - GtkTreeIter iter; - - if (!was_selected || in_set_cursor) - return TRUE; - gtk_tree_model_get_iter(model, &iter, path); - gtk_tree_model_get(model, &iter, DIVE_INDEX, &idx, -1); - if (idx < 0) { - int i; - struct dive *dive; - dive_trip_t *trip = find_trip_by_idx(idx); - if (!trip) - return TRUE; - - trip->selected = 0; - /* If this is expanded, let the gtk selection happen for each dive under it */ - if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), path)) - return TRUE; - /* Otherwise, consider each dive under it deselected */ - for_each_dive(i, dive) { - if (dive->divetrip == trip) - deselect_dive(i); - } - } else { - deselect_dive(idx); - } - return TRUE; -} - -/* This gets called for each selected entry after a selection has changed */ -static void entry_selected(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) -{ - int idx; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - if (idx < 0) { - int i; - struct dive *dive; - dive_trip_t *trip = find_trip_by_idx(idx); - - if (!trip) - return; - trip->selected = 1; - - /* If this is expanded, let the gtk selection happen for each dive under it */ - if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), path)) { - trip->fixup = 1; - return; - } - - /* Otherwise, consider each dive under it selected */ - for_each_dive(i, dive) { - if (dive->divetrip == trip) - select_dive(i); - } - trip->fixup = 0; - } else { - select_dive(idx); - } -} - -static void update_gtk_selection(GtkTreeSelection *selection, GtkTreeModel *model) -{ - GtkTreeIter iter; - - if (!gtk_tree_model_get_iter_first(model, &iter)) - return; - do { - GtkTreeIter child; - - if (!gtk_tree_model_iter_children(model, &child, &iter)) - continue; - - do { - int idx; - struct dive *dive; - dive_trip_t *trip; - - gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1); - dive = get_dive(idx); - if (!dive || !dive->selected) - break; - trip = dive->divetrip; - if (!trip) - break; - gtk_tree_selection_select_iter(selection, &child); - } while (gtk_tree_model_iter_next(model, &child)); - } while (gtk_tree_model_iter_next(model, &iter)); -} - -/* this is called when gtk thinks that the selection has changed */ -static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) -{ - int i, fixup; - struct dive *dive; - - gtk_tree_selection_selected_foreach(selection, entry_selected, model); - - /* - * Go through all the dives, if there is a trip that is selected but no - * dives under it are selected, force-select all the dives - */ - - /* First, clear "fixup" for any trip that has selected dives */ - for_each_dive(i, dive) { - dive_trip_t *trip = dive->divetrip; - if (!trip || !trip->fixup) - continue; - if (dive->selected || !trip->selected) - trip->fixup = 0; - } - - /* - * Ok, not fixup is only set for trips that are selected - * but have no selected dives in them. Select all dives - * for such trips. - */ - fixup = 0; - for_each_dive(i, dive) { - dive_trip_t *trip = dive->divetrip; - if (!trip || !trip->fixup) - continue; - fixup = 1; - select_dive(i); - } - - /* - * Ok, we did a forced selection of dives, now we need to update the gtk - * view of what is selected too.. - */ - if (fixup) - update_gtk_selection(selection, model); - -#if DEBUG_SELECTION_TRACKING - dump_selection(); -#endif - - process_selected_dives(); - repaint_dive(); -} - -GtkWidget *dive_list_create(void) -{ - GtkTreeSelection *selection; - - dive_list.listmodel = gtk_tree_store_new(DIVELIST_COLUMNS, - G_TYPE_INT, /* index */ - G_TYPE_INT, /* nr */ - G_TYPE_INT64, /* Date */ - G_TYPE_INT, /* Star rating */ - G_TYPE_INT, /* Depth */ - G_TYPE_INT, /* Duration */ - G_TYPE_INT, /* Temperature */ - G_TYPE_INT, /* Total weight */ - G_TYPE_STRING, /* Suit */ - G_TYPE_STRING, /* Cylinder */ - G_TYPE_INT, /* Nitrox */ - G_TYPE_INT, /* SAC */ - G_TYPE_INT, /* OTU */ - G_TYPE_INT, /* MAXCNS */ - G_TYPE_STRING, /* Location */ - GDK_TYPE_PIXBUF /* GPS icon */ - ); - dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS, - G_TYPE_INT, /* index */ - G_TYPE_INT, /* nr */ - G_TYPE_INT64, /* Date */ - G_TYPE_INT, /* Star rating */ - G_TYPE_INT, /* Depth */ - G_TYPE_INT, /* Duration */ - G_TYPE_INT, /* Temperature */ - G_TYPE_INT, /* Total weight */ - G_TYPE_STRING, /* Suit */ - G_TYPE_STRING, /* Cylinder */ - G_TYPE_INT, /* Nitrox */ - G_TYPE_INT, /* SAC */ - G_TYPE_INT, /* OTU */ - G_TYPE_INT, /* MAXCNS */ - G_TYPE_STRING, /* Location */ - GDK_TYPE_PIXBUF /* GPS icon */ - ); - dive_list.model = dive_list.treemodel; - dive_list.tree_view = gtk_tree_view_new_with_model(TREEMODEL(dive_list)); - set_divelist_font(prefs.divelist_font); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - - gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE); - gtk_widget_set_size_request(dive_list.tree_view, 200, 200); - - /* check if utf8 stars are available as a default OS feature */ - if (!subsurface_os_feature_available(UTF8_FONT_WITH_STARS)) - dl_column[3].header = "*"; - - dive_list.nr = divelist_column(&dive_list, dl_column + DIVE_NR); - dive_list.date = divelist_column(&dive_list, dl_column + DIVE_DATE); - dive_list.stars = divelist_column(&dive_list, dl_column + DIVE_RATING); - dive_list.depth = divelist_column(&dive_list, dl_column + DIVE_DEPTH); - dive_list.duration = divelist_column(&dive_list, dl_column + DIVE_DURATION); - dive_list.temperature = divelist_column(&dive_list, dl_column + DIVE_TEMPERATURE); - dive_list.totalweight = divelist_column(&dive_list, dl_column + DIVE_TOTALWEIGHT); - dive_list.suit = divelist_column(&dive_list, dl_column + DIVE_SUIT); - dive_list.cylinder = divelist_column(&dive_list, dl_column + DIVE_CYLINDER); - dive_list.nitrox = divelist_column(&dive_list, dl_column + DIVE_NITROX); - dive_list.sac = divelist_column(&dive_list, dl_column + DIVE_SAC); - dive_list.otu = divelist_column(&dive_list, dl_column + DIVE_OTU); - dive_list.maxcns = divelist_column(&dive_list, dl_column + DIVE_MAXCNS); - dive_list.location = divelist_column(&dive_list, dl_column + DIVE_LOCATION); - gtk_tree_view_column_set_sort_indicator(dive_list.nr, TRUE); - gtk_tree_view_column_set_sort_order(dive_list.nr, GTK_SORT_DESCENDING); - /* now add the GPS icon to the location column */ - tree_view_column_add_pixbuf(dive_list.tree_view, gpsicon_data_func, dive_list.location); - - fill_dive_list(); - - g_object_set(G_OBJECT(dive_list.tree_view), "headers-visible", TRUE, - "search-column", DIVE_LOCATION, - "rules-hint", TRUE, - NULL); - - g_signal_connect_after(dive_list.tree_view, "realize", G_CALLBACK(realize_cb), NULL); - g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), NULL); - g_signal_connect(dive_list.tree_view, "row-expanded", G_CALLBACK(row_expanded_cb), NULL); - g_signal_connect(dive_list.tree_view, "row-collapsed", G_CALLBACK(row_collapsed_cb), NULL); - g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), NULL); - g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), NULL); - g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), dive_list.model); - g_signal_connect(dive_list.listmodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); - g_signal_connect(dive_list.treemodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); - - gtk_tree_selection_set_select_function(selection, modify_selection_cb, NULL, NULL); - - dive_list.container_widget = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dive_list.container_widget), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(dive_list.container_widget), dive_list.tree_view); - - dive_list.changed = 0; - - return dive_list.container_widget; -} - -void dive_list_destroy(void) -{ - gtk_widget_destroy(dive_list.tree_view); - g_object_unref(dive_list.treemodel); - g_object_unref(dive_list.listmodel); -} - void mark_divelist_changed(int changed) { - dive_list.changed = changed; + dive_list_changed = changed; } int unsaved_changes() { - return dive_list.changed; + return dive_list_changed; } void remove_autogen_trips() @@ -2877,142 +767,3 @@ void remove_autogen_trips() } } -struct iteridx { - int idx; - GtkTreeIter *iter; -}; - -static gboolean iter_has_idx(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer _data) -{ - struct iteridx *iteridx = _data; - int idx; - /* Get the dive number */ - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - if (idx == iteridx->idx) { - iteridx->iter = gtk_tree_iter_copy(iter); - return TRUE; /* end foreach */ - } - return FALSE; -} - -static GtkTreeIter *get_iter_from_idx(int idx) -{ - struct iteridx iteridx = {idx, }; - gtk_tree_model_foreach(MODEL(dive_list), iter_has_idx, &iteridx); - return iteridx.iter; -} - -static void scroll_to_selected(GtkTreeIter *iter) -{ - GtkTreePath *treepath; - treepath = gtk_tree_model_get_path(MODEL(dive_list), iter); - scroll_to_path(treepath); - gtk_tree_path_free(treepath); -} - -static void go_to_iter(GtkTreeSelection *selection, GtkTreeIter *iter) -{ - gtk_tree_selection_unselect_all(selection); - gtk_tree_selection_select_iter(selection, iter); - scroll_to_selected(iter); -} - -void show_and_select_dive(struct dive *dive) -{ - GtkTreeSelection *selection; - GtkTreeIter *iter; - struct dive *odive; - int i, divenr; - - divenr = get_divenr(dive); - if (divenr < 0) - /* we failed to find the dive */ - return; - iter = get_iter_from_idx(divenr); - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - for_each_dive(i, odive) - odive->selected = FALSE; - amount_selected = 1; - selected_dive = divenr; - dive->selected = TRUE; - go_to_iter(selection, iter); - gtk_tree_iter_free(iter); -} - -void select_next_dive(void) -{ - GtkTreeIter *nextiter, *parent = NULL; - GtkTreeIter *iter = get_iter_from_idx(selected_dive); - GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - int idx; - - if (!iter) - return; - nextiter = gtk_tree_iter_copy(iter); - if (!gtk_tree_model_iter_next(MODEL(dive_list), nextiter)) { - if (!gtk_tree_model_iter_parent(MODEL(dive_list), nextiter, iter)) { - /* we're at the last top level node */ - goto free_iter; - } - if (!gtk_tree_model_iter_next(MODEL(dive_list), nextiter)) { - /* last trip */ - goto free_iter; - } - } - gtk_tree_model_get(MODEL(dive_list), nextiter, DIVE_INDEX, &idx, -1); - if (idx < 0) { - /* need the first child */ - parent = gtk_tree_iter_copy(nextiter); - if (! gtk_tree_model_iter_children(MODEL(dive_list), nextiter, parent)) - goto free_iter; - } - go_to_iter(selection, nextiter); -free_iter: - if (nextiter) - gtk_tree_iter_free(nextiter); - if (parent) - gtk_tree_iter_free(parent); - gtk_tree_iter_free(iter); -} - -void select_prev_dive(void) -{ - GtkTreeIter previter, *parent = NULL; - GtkTreeIter *iter = get_iter_from_idx(selected_dive); - GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - GtkTreePath *treepath; - int idx; - - if (!iter) - return; - treepath = gtk_tree_model_get_path(MODEL(dive_list), iter); - if (!gtk_tree_path_prev(treepath)) { - if (!gtk_tree_model_iter_parent(MODEL(dive_list), &previter, iter)) - /* we're at the last top level node */ - goto free_iter; - gtk_tree_path_free(treepath); - treepath = gtk_tree_model_get_path(MODEL(dive_list), &previter); - if (!gtk_tree_path_prev(treepath)) - /* first trip */ - goto free_iter; - if (!gtk_tree_model_get_iter(MODEL(dive_list), &previter, treepath)) - goto free_iter; - } - if (!gtk_tree_model_get_iter(MODEL(dive_list), &previter, treepath)) - goto free_iter; - gtk_tree_model_get(MODEL(dive_list), &previter, DIVE_INDEX, &idx, -1); - if (idx < 0) { - /* need the last child */ - parent = gtk_tree_iter_copy(&previter); - if (! gtk_tree_model_iter_nth_child(MODEL(dive_list), &previter, parent, - gtk_tree_model_iter_n_children(MODEL(dive_list), parent) - 1)) - goto free_iter; - } - go_to_iter(selection, &previter); -free_iter: - gtk_tree_path_free(treepath); - if (parent) - gtk_tree_iter_free(parent); - gtk_tree_iter_free(iter); -} diff --git a/divelist.h b/divelist.h index c9ec973f7..6a0b98bc9 100644 --- a/divelist.h +++ b/divelist.h @@ -4,7 +4,6 @@ #ifdef __cplusplus extern "C" { #endif - struct dive; extern void dive_list_update_dives(void); @@ -21,6 +20,27 @@ extern void show_and_select_dive(struct dive *dive); extern double init_decompression(struct dive * dive); extern void export_all_dives_uddf_cb(); +/* divelist core logic functions */ +extern dive_trip_t *find_trip_by_idx(int idx); +extern int trip_has_selected_dives(dive_trip_t *trip); +extern void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p); +extern int get_divenr(struct dive *dive); +extern void get_location(struct dive *dive, char **str); +extern void get_cylinder(struct dive *dive, char **str); +extern void get_suit(struct dive *dive, char **str); +extern dive_trip_t *find_matching_trip(timestamp_t when); +extern void remove_dive_from_trip(struct dive *dive); +extern dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive); +extern void autogroup_dives(void); +extern void merge_dive_index(int i, struct dive *a); +extern void select_dive(int idx); +extern void deselect_dive(int idx); + +#ifdef DEBUG_TRIP +extern void dump_selection(void); +extern void dump_trip_list(void); +#endif + #ifdef __cplusplus } #endif diff --git a/uemis-downloader.c b/uemis-downloader.c index 9b54b0f3c..c5113d95a 100644 --- a/uemis-downloader.c +++ b/uemis-downloader.c @@ -731,7 +731,7 @@ static void process_raw_buffer(uint32_t deviceid, char *inbuf, char **max_divenr return; } -static char *get_divenr(char *deviceidstr) +static char *uemis_get_divenr(char *deviceidstr) { uint32_t deviceid, maxdiveid = 0; int i; @@ -789,7 +789,7 @@ static char *do_uemis_download(struct argument_block *args) /* if we have an empty divelist or force it, then we start downloading from the * first dive on the Uemis; otherwise check which was the last dive downloaded */ if (!args->force_download && dive_table.nr > 0) - newmax = get_divenr(deviceid); + newmax = uemis_get_divenr(deviceid); else newmax = strdup("0"); start = atoi(newmax); From 081000963a822c1a9814bf4eba3e9dd4485a2cf9 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 6 Apr 2013 21:28:03 -0700 Subject: [PATCH 010/226] Continue to separate Gtk related code from core logic: divelist Move some more logic out of the divelist-gtk.c file. Signed-off-by: Dirk Hohndel --- divelist-gtk.c | 61 +++++--------------------------------------- divelist.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ divelist.h | 2 ++ 3 files changed, 77 insertions(+), 55 deletions(-) diff --git a/divelist-gtk.c b/divelist-gtk.c index 8587e11c6..dc441881c 100644 --- a/divelist-gtk.c +++ b/divelist-gtk.c @@ -295,7 +295,7 @@ static void depth_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int depth, integer, frac, len, idx; + int depth, integer, frac, len, idx, show_decimal; char buffer[40]; gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1); @@ -303,27 +303,9 @@ static void depth_data_func(GtkTreeViewColumn *col, if (idx < 0) { *buffer = '\0'; } else { - switch (prefs.units.length) { - case METERS: - /* To tenths of meters */ - depth = (depth + 49) / 100; - integer = depth / 10; - frac = depth % 10; - if (integer < 20) - break; - if (frac >= 5) - integer++; - frac = -1; - break; - case FEET: - integer = mm_to_feet(depth) + 0.5; - frac = -1; - break; - default: - return; - } + get_depth_values(depth, &integer, &frac, &show_decimal); len = snprintf(buffer, sizeof(buffer), "%d", integer); - if (frac >= 0) + if (show_decimal) len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac); } g_object_set(renderer, "text", buffer, NULL); @@ -826,49 +808,18 @@ void dive_list_update_dives(void) repaint_dive(); } -static gint dive_nr_sort(GtkTreeModel *model, +static gint gtk_dive_nr_sort(GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer user_data) { int idx_a, idx_b; timestamp_t when_a, when_b; - struct dive *a, *b; - dive_trip_t *tripa = NULL, *tripb = NULL; gtk_tree_model_get(model, iter_a, DIVE_INDEX, &idx_a, DIVE_DATE, &when_a, -1); gtk_tree_model_get(model, iter_b, DIVE_INDEX, &idx_b, DIVE_DATE, &when_b, -1); - if (idx_a < 0) { - a = NULL; - tripa = find_trip_by_idx(idx_a); - } else { - a = get_dive(idx_a); - if (a) - tripa = a->divetrip; - } - - if (idx_b < 0) { - b = NULL; - tripb = find_trip_by_idx(idx_b); - } else { - b = get_dive(idx_b); - if (b) - tripb = b->divetrip; - } - - /* - * Compare dive dates within the same trip (or when there - * are no trips involved at all). But if we have two - * different trips use the trip dates for comparison - */ - if (tripa != tripb) { - if (tripa) - when_a = tripa->when; - if (tripb) - when_b = tripb->when; - } - return when_a - when_b; + return dive_nr_sort(idx_a, idx_b, when_a, when_b); } @@ -879,7 +830,7 @@ static struct divelist_column { unsigned int flags; int *visible; } dl_column[] = { - [DIVE_NR] = { "#", nr_data_func, dive_nr_sort, ALIGN_RIGHT }, + [DIVE_NR] = { "#", nr_data_func, gtk_dive_nr_sort, ALIGN_RIGHT }, [DIVE_DATE] = { N_("Date"), date_data_func, NULL, ALIGN_LEFT }, [DIVE_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT }, [DIVE_DEPTH] = { N_("ft"), depth_data_func, NULL, ALIGN_RIGHT }, diff --git a/divelist.c b/divelist.c index f574a31fd..61b9116b2 100644 --- a/divelist.c +++ b/divelist.c @@ -88,6 +88,43 @@ dive_trip_t *find_trip_by_idx(int idx) return NULL; } +int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b) +{ + struct dive *a, *b; + dive_trip_t *tripa = NULL, *tripb = NULL; + + if (idx_a < 0) { + a = NULL; + tripa = find_trip_by_idx(idx_a); + } else { + a = get_dive(idx_a); + if (a) + tripa = a->divetrip; + } + + if (idx_b < 0) { + b = NULL; + tripb = find_trip_by_idx(idx_b); + } else { + b = get_dive(idx_b); + if (b) + tripb = b->divetrip; + } + + /* + * Compare dive dates within the same trip (or when there + * are no trips involved at all). But if we have two + * different trips use the trip dates for comparison + */ + if (tripa != tripb) { + if (tripa) + when_a = tripa->when; + if (tripb) + when_b = tripb->when; + } + return when_a - when_b; +} + int trip_has_selected_dives(dive_trip_t *trip) { struct dive *dive; @@ -98,6 +135,38 @@ int trip_has_selected_dives(dive_trip_t *trip) return 0; } +/* Get the values as we want to show them. Whole feet. But meters with one decimal for + * values less than 20m, without decimals for larger values */ +void get_depth_values(int depth, int *depth_int, int *depth_decimal, int *show_decimal) +{ + int integer, frac; + + *show_decimal = 1; + switch (prefs.units.length) { + case METERS: + /* To tenths of meters */ + depth = (depth + 49) / 100; + integer = depth / 10; + frac = depth % 10; + if (integer < 20) + break; + if (frac >= 5) + integer++; + *show_decimal = 0; + break; + case FEET: + integer = mm_to_feet(depth) + 0.5; + *show_decimal = 0; + break; + default: + /* can't happen */ + return; + } + *depth_int = integer; + if (*show_decimal) + *depth_decimal = frac; +} + /* * Get "maximal" dive gas for a dive. * Rules: diff --git a/divelist.h b/divelist.h index 6a0b98bc9..4772f89d9 100644 --- a/divelist.h +++ b/divelist.h @@ -22,7 +22,9 @@ extern void export_all_dives_uddf_cb(); /* divelist core logic functions */ extern dive_trip_t *find_trip_by_idx(int idx); +extern int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b); extern int trip_has_selected_dives(dive_trip_t *trip); +extern void get_depth_values(int depth, int *depth_int, int *depth_decimal, int *show_decimal); extern void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p); extern int get_divenr(struct dive *dive); extern void get_location(struct dive *dive, char **str); From ba712c3b5420ed461b8b7a1fdd14849e622ae974 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sun, 7 Apr 2013 15:20:43 -0700 Subject: [PATCH 011/226] Start creating the Qt UI This is based on several commits from Tomaz - mingled together and mildly extended by Dirk (mostly Makefile hacking). All Qt UI related stuff should eventually move into the qt-ui directory. So the Makefile rules for moc and uic have been adjusted accordingly. The MainWindow class has been moved into its own file in qt-ui (but just with a placeholder, the existing class has simply been ifdef'ed out in qt-gui.cpp for the moment). We still have a couple of Qt things in qt-gui.cpp in the main directory... all this needs to move into the qt-ui directory and be built with separate .h files. Right now we have the one-off Makefile rule to create the qt-gui.moc file from the qt-gui.cpp file. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- Makefile | 40 +- qt-gui.cpp | 7 +- qt-ui/maintab.cpp | 10 + qt-ui/maintab.h | 20 + qt-ui/maintab.ui | 810 ++++++++++++++++++++++++++++++++++++++++ qt-ui/mainwindow.cpp | 11 + qt-ui/mainwindow.h | 24 ++ qt-ui/mainwindow.ui | 297 +++++++++++++++ qt-ui/plotareascene.cpp | 0 qt-ui/plotareascene.h | 0 10 files changed, 1204 insertions(+), 15 deletions(-) create mode 100644 qt-ui/maintab.cpp create mode 100644 qt-ui/maintab.h create mode 100644 qt-ui/maintab.ui create mode 100644 qt-ui/mainwindow.cpp create mode 100644 qt-ui/mainwindow.h create mode 100644 qt-ui/mainwindow.ui create mode 100644 qt-ui/plotareascene.cpp create mode 100644 qt-ui/plotareascene.h diff --git a/Makefile b/Makefile index aa42db2cf..c14d0b931 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ VERSION=3.0.2 CC=gcc CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE CXX=g++ -CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS +CXXFLAGS=-Wall -g $(CLCFLAGS) -fPIC -DQT_NO_KEYWORDS INSTALL=install PKGCONFIG=pkg-config XML2CONFIG=xml2-config @@ -182,10 +182,13 @@ LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIB MSGLANGS=$(notdir $(wildcard po/*.po)) MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) + +QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o + OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o planner.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ - webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) + webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) $(QTOBJS) DEPS = $(wildcard .dep/*.dep) @@ -298,36 +301,45 @@ MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $ %.o: %.c @echo ' CC' $< - @mkdir -p .dep + @mkdir -p .dep .dep/qt-ui @$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< %.o: %.cpp @echo ' CXX' $< - @mkdir -p .dep + @mkdir -p .dep .dep/qt-ui @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< -# This rule is for running the moc on QObject subclasses defined in the .h -# files. -# To activate this rule, add .moc.o to the OBJS variable. -%.moc.cpp: %.h - @echo ' MOC' $< - @$(MOC) $(MOCFLAGS) $< -o $@ - # This rule is for running the moc on QObject subclasses defined in the .cpp # files; remember to #include ".moc" at the end of the .cpp file, or # you'll get linker errors ("undefined vtable for...") # To activate this rule, you need another rule on the .o file, like: # file.o: file.moc + +qt-ui/%.moc: qt-ui/%.h + @echo ' MOC' $< + @$(MOC) -i $(MOCFLAGS) $< -o $@ + +# this is just here for qt-gui.cpp +# should be removed once all the Qt UI code has been moved into qt-ui + %.moc: %.cpp @echo ' MOC' $< @$(MOC) -i $(MOCFLAGS) $< -o $@ -qt-gui.o: main-window.ui.h qt-gui.moc +# This creates the ui headers. +# To activate this rule, you need to add the ui_*.h file to the .o file: +# file.o: ui_file.h -%.ui.h: ui/%.ui +qt-ui/ui_%.h: qt-ui/%.ui @echo ' UIC' $< @$(UIC) $< -o $@ +qt-gui.o: qt-gui.moc + +qt-ui/maintab.o: qt-ui/maintab.moc qt-ui/ui_maintab.h + +qt-ui/mainwindow.o: qt-ui/mainwindow.moc qt-ui/ui_mainwindow.h + share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases mkdir -p $(dir $@) msgfmt -c -o $@ po/$*.po @@ -355,7 +367,7 @@ doc: clean: rm -f $(OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ - $(VERSION_FILE) + $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h rm -rf share .dep -include $(DEPS) diff --git a/qt-gui.cpp b/qt-gui.cpp index 285f2082d..e46f4765f 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -24,10 +24,11 @@ #include "webservice.h" #include "version.h" #include "libdivecomputer.h" -#include "main-window.ui.h" +#include "qt-ui/mainwindow.h" #include #include + #include #include #include @@ -1737,6 +1738,9 @@ static gboolean notebook_tooltip (GtkWidget *widget, gint x, gint y, } } +#if NEEDS_TO_MOVE_TO_QT_UI +/* this appears to have moved - but it's very different in qt-ui */ + class MainWindow: public QMainWindow, private Ui::MainWindow { Q_OBJECT @@ -1850,6 +1854,7 @@ QStringList MainWindow::fileNameFilters() const ; return filters; } +#endif /* NEEDS_TO_MOVE_TO_QT_UI */ void init_ui(int *argcp, char ***argvp) { diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp new file mode 100644 index 000000000..4569958c8 --- /dev/null +++ b/qt-ui/maintab.cpp @@ -0,0 +1,10 @@ +#include "maintab.h" +#include "ui_maintab.h" + +MainTab::MainTab(QWidget *parent) : QTabWidget(parent), + ui(new Ui::MainTab()) +{ + ui->setupUi(this); +} + +#include "maintab.moc" diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h new file mode 100644 index 000000000..40904ab12 --- /dev/null +++ b/qt-ui/maintab.h @@ -0,0 +1,20 @@ +#ifndef MAINTAB_H +#define MAINTAB_H + +#include + +namespace Ui +{ + class MainTab; +} + +class MainTab : public QTabWidget +{ + Q_OBJECT +public: + MainTab(QWidget *parent); +private: + Ui::MainTab *ui; +}; + +#endif \ No newline at end of file diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui new file mode 100644 index 000000000..a0aec4358 --- /dev/null +++ b/qt-ui/maintab.ui @@ -0,0 +1,810 @@ + + + MainTab + + + + 0 + 0 + 400 + 320 + + + + TabWidget + + + 0 + + + + Dive Info + + + + + + + + + 75 + true + + + + SAC: + + + + + + + + 75 + true + + + + OTU: + + + + + + + + 75 + true + + + + 0²/He: + + + + + + + + 75 + true + + + + Gas Used: + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + + 10 + + + + + + 75 + true + + + + Dive Time: + + + + + + + + 75 + true + + + + Surf Interv: + + + + + + + + 75 + true + + + + Avg Depth: + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + 75 + true + + + + Air Press: + + + + + + + + 75 + true + + + + Max Depth: + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + 75 + true + + + + Air Temp: + + + + + + + TextLabel + + + + + + + + 75 + true + + + + Visibility: + + + + + + + + 75 + true + + + + Water Temp: + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + 75 + true + + + + Date: + + + + + + + + + Qt::Vertical + + + + 20 + 112 + + + + + + + + + Dive Notes + + + + + + Location + + + + + + + + + + Divemaster + + + + + + + Buddy + + + + + + + + + + + + + Rating + + + + + + + Suit + + + + + + + + + + + + + Notes + + + + + + + + + + + Equipment + + + + + + Qt::Vertical + + + + Cylinders + + + + + + + + + + + Edit + + + + + + + Add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete + + + + + + + + + + Weight + + + + + + + + + + + Edit + + + + + + + Add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete + + + + + + + + + + + + + + Stats + + + + + + 10 + + + + + + 75 + true + + + + Max Depth + + + + + + + + 75 + true + + + + Min Depth + + + + + + + + 75 + true + + + + Avg Depth + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + 75 + true + + + + Max SAC + + + + + + + + 75 + true + + + + Min SAC + + + + + + + + 75 + true + + + + Avg SAC + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + + 10 + + + 0 + + + + + + 75 + true + + + + Dives + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + + 75 + true + + + + Max Temp + + + + + + + + 75 + true + + + + Min Temp + + + + + + + + 75 + true + + + + Avg Temp + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + 75 + true + + + + Total Time + + + + + + + + 75 + true + + + + Avg Time + + + + + + + + 75 + true + + + + Longest Dive + + + + + + + + 75 + true + + + + Shortest Dive + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + + Qt::Vertical + + + + 20 + 85 + + + + + + + + + + diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp new file mode 100644 index 000000000..851d1aa1c --- /dev/null +++ b/qt-ui/mainwindow.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include + +MainWindow::MainWindow() : ui(new Ui::MainWindow()) +{ + ui->setupUi(this); +} + +#include "mainwindow.moc" diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h new file mode 100644 index 000000000..9e15e58b4 --- /dev/null +++ b/qt-ui/mainwindow.h @@ -0,0 +1,24 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace Ui +{ + class MainWindow; +} + +class DiveInfo; +class DiveNotes; +class Stats; +class Equipment; + +class MainWindow : public QMainWindow{ + Q_OBJECT +public: + MainWindow(); +private: + Ui::MainWindow *ui; +}; + +#endif \ No newline at end of file diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui new file mode 100644 index 000000000..b99d10222 --- /dev/null +++ b/qt-ui/mainwindow.ui @@ -0,0 +1,297 @@ + + + MainWindow + + + + 0 + 0 + 763 + 548 + + + + MainWindow + + + + + + + Qt::Vertical + + + + Qt::Horizontal + + + + + + + + + + + Qt::Horizontal + + + + + + + + + 0 + 0 + 763 + 19 + + + + + File + + + + + + + + + + + + + + + + + + + Log + + + + + + + + + + + + + + + View + + + + + + + + + + + Filter + + + + + + Planner + + + + + + Help + + + + + + + + + + + + + + New + + + Ctrl+N + + + + + Open + + + Ctrl+O + + + + + Save + + + Ctrl+S + + + + + Save as + + + Ctrl+Shift+S + + + + + Close + + + Ctrl+W + + + + + Import Files + + + Ctrl+I + + + + + Export UDDF + + + + + Print + + + Ctrl+P + + + + + Preferences + + + + + Quit + + + Ctrl+Q + + + + + Download from Dive computer + + + + + Download from Web Service + + + + + Edit Device Names + + + + + Add Dive + + + + + Renumber + + + + + Auto Group + + + + + Toggle Zoom + + + + + Yearly Statistics + + + + + List + + + + + Profile + + + + + Info + + + + + Tree + + + + + Prev DC + + + + + Next DC + + + + + Select Events + + + + + Input Plan + + + + + About Subsurface + + + + + User Manual + + + + + + MainTab + QWidget +
maintab.h
+ 1 +
+
+ + +
diff --git a/qt-ui/plotareascene.cpp b/qt-ui/plotareascene.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/qt-ui/plotareascene.h b/qt-ui/plotareascene.h new file mode 100644 index 000000000..e69de29bb From 1d02ba12a3342039c0156f48cb7a5103801aa368 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 7 Apr 2013 20:20:25 -0700 Subject: [PATCH 012/226] Separate Gtk related code from core logic: planner Relatively straight forward, just a handful of places where we call show_error() (a UI function) from the logic code. In the process I noticed a few places where error returns weren't dealt with correctly. Added a new planner.h files for the necessary declarations. This should make no difference to functionality. Signed-off-by: Dirk Hohndel --- Makefile | 3 +- dive.h | 2 - planner.c | 458 ++++++------------------------------------------------ planner.h | 21 +++ 4 files changed, 67 insertions(+), 417 deletions(-) create mode 100644 planner.h diff --git a/Makefile b/Makefile index c14d0b931..28b7e0365 100644 --- a/Makefile +++ b/Makefile @@ -185,7 +185,8 @@ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.m QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o planner.o \ +OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ + planner.o planner-gtk.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) $(QTOBJS) diff --git a/dive.h b/dive.h index aff856856..734aa2269 100644 --- a/dive.h +++ b/dive.h @@ -689,9 +689,7 @@ struct diveplan { struct divedatapoint *dp; }; -void plan(struct diveplan *diveplan, char **cache_datap, struct dive **divep); void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2, int he, int po2); -void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel); void add_depth_to_nth_dp(struct diveplan *diveplan, int idx, int depth); void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he); void free_dps(struct divedatapoint *dp); diff --git a/planner.c b/planner.c index 68f444882..0f739b1f0 100644 --- a/planner.c +++ b/planner.c @@ -10,7 +10,7 @@ #include #include "dive.h" #include "divelist.h" -#include "display-gtk.h" +#include "planner.h" int decostoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000, 30000, 33000, 36000, 39000, 42000, 45000, 48000, 51000, 54000, 57000, @@ -21,7 +21,6 @@ int decostoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, }; double plangflow, plangfhigh; char *disclaimer; -GtkWidget *planner, *planner_error_bar, *error_label; #if DEBUG_PLAN void dump_plan(struct diveplan *diveplan) @@ -48,46 +47,6 @@ void dump_plan(struct diveplan *diveplan) } #endif -static void on_error_bar_response(GtkWidget *widget, gint response, gpointer data) -{ - if (response == GTK_RESPONSE_OK) - { - gtk_widget_destroy(widget); - planner_error_bar = NULL; - error_label = NULL; - } -} - -static void show_error(const char *fmt, ...) -{ - va_list args; - GError *error; - GtkWidget *box, *container; - gboolean bar_is_visible = TRUE; - - va_start(args, fmt); - error = g_error_new_valist(g_quark_from_string("subsurface"), DIVE_ERROR_PLAN, fmt, args); - va_end(args); - if (!planner_error_bar) { - planner_error_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); - g_signal_connect(planner_error_bar, "response", G_CALLBACK(on_error_bar_response), NULL); - gtk_info_bar_set_message_type(GTK_INFO_BAR(planner_error_bar), GTK_MESSAGE_ERROR); - bar_is_visible = FALSE; - } - container = gtk_info_bar_get_content_area(GTK_INFO_BAR(planner_error_bar)); - if (error_label) - gtk_container_remove(GTK_CONTAINER(container), error_label); - error_label = gtk_label_new(error->message); - gtk_container_add(GTK_CONTAINER(container), error_label); - box = gtk_dialog_get_content_area(GTK_DIALOG(planner)); - if (!bar_is_visible) - gtk_box_pack_start(GTK_BOX(box), planner_error_bar, FALSE, FALSE, 0); - gtk_widget_show_all(box); - /* make sure this actually gets shown BEFORE the calculations run */ - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); -} - void get_gas_from_events(struct divecomputer *dc, int time, int *o2, int *he) { struct event *event = dc->events; @@ -132,13 +91,14 @@ void get_gas_string(int o2, int he, char *text, int len) } /* returns the tissue tolerance at the end of this (partial) dive */ -double tissue_at_end(struct dive *dive, char **cached_datap) +double tissue_at_end(struct dive *dive, char **cached_datap, char **error_string_p) { struct divecomputer *dc; struct sample *sample, *psample; int i, j, t0, t1, gasidx, lastdepth; int o2, he; double tissue_tolerance; + static char buf[200]; if (!dive) return 0.0; @@ -160,7 +120,8 @@ double tissue_at_end(struct dive *dive, char **cached_datap) t1 = sample->time.seconds; get_gas_from_events(&dive->dc, t0, &o2, &he); if ((gasidx = get_gasidx(dive, o2, he)) == -1) { - show_error(_("Can't find gas %d/%d"), (o2 + 5) / 10, (he + 5) / 10); + snprintf(buf, sizeof(buf),_("Can't find gas %d/%d"), (o2 + 5) / 10, (he + 5) / 10); + *error_string_p = buf; gasidx = 0; } if (i > 0) @@ -177,7 +138,7 @@ double tissue_at_end(struct dive *dive, char **cached_datap) } /* how many seconds until we can ascend to the next stop? */ -int time_at_last_depth(struct dive *dive, int o2, int he, int next_stop, char **cached_data_p) +int time_at_last_depth(struct dive *dive, int o2, int he, int next_stop, char **cached_data_p, char **error_string_p) { int depth, gasidx; double surface_pressure, tissue_tolerance; @@ -187,7 +148,7 @@ int time_at_last_depth(struct dive *dive, int o2, int he, int next_stop, char ** if (!dive) return 0; surface_pressure = dive->dc.surface_pressure.mbar / 1000.0; - tissue_tolerance = tissue_at_end(dive, cached_data_p); + tissue_tolerance = tissue_at_end(dive, cached_data_p, error_string_p); sample = &dive->dc.sample[dive->dc.samples - 1]; depth = sample->depth.mm; gasidx = get_gasidx(dive, o2, he); @@ -214,7 +175,6 @@ int add_gas(struct dive *dive, int o2, int he) return i; } if (i == MAX_CYLINDERS) { - show_error(_("Too many gas mixes")); return -1; } mix->o2.permille = o2; @@ -225,7 +185,7 @@ int add_gas(struct dive *dive, int o2, int he) return i; } -struct dive *create_dive_from_plan(struct diveplan *diveplan) +struct dive *create_dive_from_plan(struct diveplan *diveplan, char **error_string) { struct dive *dive; struct divedatapoint *dp; @@ -235,6 +195,7 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) int oldpo2 = 0; int lasttime = 0; + *error_string = NULL; if (!diveplan || !diveplan->dp) return NULL; #if DEBUG_PLAN & 4 @@ -256,7 +217,8 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) sample = prepare_sample(dc); sample->po2 = dp->po2; finish_sample(dc); - add_gas(dive, oldo2, oldhe); + if (add_gas(dive, oldo2, oldhe) < 0) + goto gas_error_exit; while (dp) { int o2 = dp->o2, he = dp->he; int po2 = dp->po2; @@ -266,7 +228,8 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) if (time == 0) { /* special entries that just inform the algorithm about * additional gases that are available */ - add_gas(dive, o2, he); + if (add_gas(dive, o2, he) < 0) + goto gas_error_exit; dp = dp->next; continue; } @@ -289,7 +252,8 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) int plano2 = (o2 + 5) / 10 * 10; int planhe = (he + 5) / 10 * 10; int value; - add_gas(dive, plano2, planhe); + if (add_gas(dive, plano2, planhe) < 0) + goto gas_error_exit; value = (plano2 / 10) | ((planhe / 10) << 16); add_event(dc, lasttime, 25, 0, value, "gaschange"); // SAMPLE_EVENT_GASCHANGE2 oldo2 = o2; oldhe = he; @@ -316,6 +280,11 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) save_dive(stdout, dive); #endif return dive; + +gas_error_exit: + free(dive); + *error_string = _("Too many gas mixes"); + return NULL; } void free_dps(struct divedatapoint *dp) @@ -359,7 +328,8 @@ struct divedatapoint *get_nth_dp(struct diveplan *diveplan, int idx) return dp; } -void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel) +/* return -1 to warn about potentially very long calculation */ +int add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel) { struct divedatapoint *pdp, *dp = get_nth_dp(diveplan, idx); if (idx > 0) { @@ -369,7 +339,8 @@ void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gb } dp->time = duration; if (duration > 180 * 60) - show_error(_("Warning - extremely long dives can cause long calculation time")); + return -1; + return 0; } /* this function is ONLY called from the dialog callback - so it @@ -612,7 +583,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive) dive->notes = strdup(buffer); } -void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) +void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, char **error_string_p) { struct dive *dive; struct sample *sample; @@ -629,7 +600,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) diveplan->surface_pressure = SURFACE_PRESSURE; if (*divep) delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan); + *divep = dive = create_dive_from_plan(diveplan, error_string_p); if (!dive) return; record_dive(dive); @@ -641,7 +612,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) get_gas_from_events(&dive->dc, sample->time.seconds, &o2, &he); po2 = dive->dc.sample[dive->dc.samples - 1].po2; depth = dive->dc.sample[dive->dc.samples - 1].depth.mm; - tissue_tolerance = tissue_at_end(dive, cached_datap); + tissue_tolerance = tissue_at_end(dive, cached_datap, error_string_p); ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); #if DEBUG_PLAN & 4 printf("gas %d/%d\n", o2, he); @@ -673,7 +644,9 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) plan_add_segment(diveplan, transitiontime, stoplevels[stopidx], o2, he, po2); /* re-create the dive */ delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan); + *divep = dive = create_dive_from_plan(diveplan, error_string_p); + if (!dive) + goto error_exit; record_dive(dive); } while (stopidx > 0) { /* this indicates that we aren't surfacing directly */ @@ -686,12 +659,12 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) #endif gi--; } - wait_time = time_at_last_depth(dive, o2, he, stoplevels[stopidx - 1], cached_datap); + wait_time = time_at_last_depth(dive, o2, he, stoplevels[stopidx - 1], cached_datap, error_string_p); /* typically deco plans are done in one minute increments; we may want to * make this configurable at some point */ wait_time = ((wait_time + 59) / 60) * 60; #if DEBUG_PLAN & 2 - tissue_tolerance = tissue_at_end(dive, cached_datap); + tissue_tolerance = tissue_at_end(dive, cached_datap, error_string_p); ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); printf("waittime %d:%02d at depth %5.2lfm; ceiling %5.2lfm\n", FRACTION(wait_time, 60), stoplevels[stopidx] / 1000.0, ceiling / 1000.0); @@ -705,7 +678,9 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) plan_add_segment(diveplan, transitiontime, stoplevels[stopidx - 1], o2, he, po2); /* re-create the dive */ delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan); + *divep = dive = create_dive_from_plan(diveplan, error_string_p); + if (!dive) + goto error_exit; record_dive(dive); stopidx--; } @@ -713,12 +688,11 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) /* now make the dive visible in the dive list */ report_dives(FALSE, FALSE); show_and_select_dive(dive); +error_exit: free(stoplevels); free(gaschanges); } - -/* and now the UI for all this */ /* * Get a value in tenths (so "10.2" == 102, "9" = 90) * @@ -782,7 +756,7 @@ static int get_permille(const char *begin, const char **end) return value; } -static int validate_gas(const char *text, int *o2_p, int *he_p) +int validate_gas(const char *text, int *o2_p, int *he_p) { int o2, he; @@ -821,7 +795,7 @@ static int validate_gas(const char *text, int *o2_p, int *he_p) return 1; } -static int validate_time(const char *text, int *sec_p, int *rel_p) +int validate_time(const char *text, int *sec_p, int *rel_p) { int min, sec, rel; char *end; @@ -888,7 +862,7 @@ static int validate_time(const char *text, int *sec_p, int *rel_p) return 1; } -static int validate_depth(const char *text, int *mm_p) +int validate_depth(const char *text, int *mm_p) { int depth, imperial; @@ -927,7 +901,7 @@ static int validate_depth(const char *text, int *mm_p) return 1; } -static int validate_po2(const char *text, int *mbar_po2) +int validate_po2(const char *text, int *mbar_po2) { int po2; @@ -950,7 +924,7 @@ static int validate_po2(const char *text, int *mbar_po2) return 1; } -static int validate_volume(const char *text, int *sac) +int validate_volume(const char *text, int *sac) { int volume, imperial; @@ -986,32 +960,12 @@ static int validate_volume(const char *text, int *sac) return 1; } -static GtkWidget *add_entry_to_box(GtkWidget *box, const char *label) -{ - GtkWidget *entry, *frame; - - entry = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry), 16); - if (label) { - frame = gtk_frame_new(label); - gtk_container_add(GTK_CONTAINER(frame), entry); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); - } else { - gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 2); - } - return entry; -} - -#define MAX_WAYPOINTS 12 -GtkWidget *entry_depth[MAX_WAYPOINTS], *entry_duration[MAX_WAYPOINTS], *entry_gas[MAX_WAYPOINTS], *entry_po2[MAX_WAYPOINTS]; -int nr_waypoints = 0; -static GtkListStore *gas_model = NULL; struct diveplan diveplan = {}; char *cache_data = NULL; struct dive *planned_dive = NULL; /* make a copy of the diveplan so far and display the corresponding dive */ -void show_planned_dive(void) +void show_planned_dive(char **error_string_p) { struct diveplan tempplan; struct divedatapoint *dp, **dpp; @@ -1029,342 +983,18 @@ void show_planned_dive(void) printf("in show_planned_dive:\n"); dump_plan(&tempplan); #endif - plan(&tempplan, &cache_data, &planned_dive); + plan(&tempplan, &cache_data, &planned_dive, error_string_p); free_dps(tempplan.dp); } -static gboolean gas_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) -{ - const char *gastext; - int o2, he; - int idx = data - NULL; - - gastext = gtk_entry_get_text(GTK_ENTRY(entry)); - o2 = he = 0; - if (validate_gas(gastext, &o2, &he)) - add_string_list_entry(gastext, gas_model); - add_gas_to_nth_dp(&diveplan, idx, o2, he); - show_planned_dive(); - return FALSE; -} - -static void gas_changed_cb(GtkWidget *combo, gpointer data) -{ - const char *gastext; - int o2, he; - int idx = data - NULL; - - gastext = get_active_text(GTK_COMBO_BOX(combo)); - /* stupidly this gets called for two reasons: - * a) any keystroke into the entry field - * b) mouse selection of a dropdown - * we only care about b) (a) is handled much better with the focus-out event) - * so let's check that the text returned is actually in our model before going on - */ - if (match_list(gas_model, gastext) != MATCH_EXACT) - return; - o2 = he = 0; - if (!validate_gas(gastext, &o2, &he)) { - /* this should never happen as only validated texts should be - * in the dropdown */ - show_error(_("Invalid gas for row %d"),idx); - } - add_gas_to_nth_dp(&diveplan, idx, o2, he); - show_planned_dive(); -} - -static gboolean depth_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) -{ - const char *depthtext; - int depth = -1; - int idx = data - NULL; - - depthtext = gtk_entry_get_text(GTK_ENTRY(entry)); - - if (validate_depth(depthtext, &depth)) { - if (depth > 150000) - show_error(_("Warning - planning very deep dives can take excessive amounts of time")); - add_depth_to_nth_dp(&diveplan, idx, depth); - show_planned_dive(); - } else { - /* it might be better to instead change the color of the input field or something */ - if (depth == -1) - show_error(_("Invalid depth - could not parse \"%s\""), depthtext); - else - show_error(_("Invalid depth - values deeper than 400m not supported")); - } - return FALSE; -} - -static gboolean duration_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *durationtext; - int duration, is_rel; - int idx = data - NULL; - - durationtext = gtk_entry_get_text(GTK_ENTRY(entry)); - if (validate_time(durationtext, &duration, &is_rel)) - add_duration_to_nth_dp(&diveplan, idx, duration, is_rel); - show_planned_dive(); - return FALSE; -} - -static gboolean po2_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *po2text; - int po2; - int idx = data - NULL; - - po2text = gtk_entry_get_text(GTK_ENTRY(entry)); - if (validate_po2(po2text, &po2)) - add_po2_to_nth_dp(&diveplan, idx, po2); - show_planned_dive(); - return FALSE; -} - /* Subsurface follows the lead of most divecomputers to use times * without timezone - so all times are implicitly assumed to be * local time of the dive location; so in order to give the current * time in that way we actually need to add the timezone offset */ -static timestamp_t current_time_notz(void) +timestamp_t current_time_notz(void) { time_t now = time(NULL); struct tm *local = localtime(&now); return utc_mktime(local); } -static gboolean starttime_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *starttimetext; - int starttime, is_rel; - - starttimetext = gtk_entry_get_text(GTK_ENTRY(entry)); - if (validate_time(starttimetext, &starttime, &is_rel)) { - /* we alway make this relative - either from the current time or from the - * end of the last dive, whichever is later */ - timestamp_t cur = current_time_notz(); - if (diveplan.lastdive_nr >= 0) { - struct dive *last_dive = get_dive(diveplan.lastdive_nr); - if (last_dive && last_dive->when + last_dive->dc.duration.seconds > cur) - cur = last_dive->when + last_dive->dc.duration.seconds; - } - diveplan.when = cur + starttime; - show_planned_dive(); - } else { - /* it might be better to instead change the color of the input field or something */ - show_error(_("Invalid starttime")); - } - return FALSE; -} - -static gboolean surfpres_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *surfprestext; - - surfprestext = gtk_entry_get_text(GTK_ENTRY(entry)); - diveplan.surface_pressure = atoi(surfprestext); - show_planned_dive(); - return FALSE; -} - -static gboolean sac_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *sactext; - - sactext = gtk_entry_get_text(GTK_ENTRY(entry)); - if (validate_volume(sactext, data)) - show_planned_dive(); - return FALSE; -} - -static gboolean gf_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *gftext; - int gf; - double *gfp = data; - - gftext = gtk_entry_get_text(GTK_ENTRY(entry)); - if (sscanf(gftext, "%d", &gf) == 1) { - *gfp = gf / 100.0; - show_planned_dive(); - } - return FALSE; -} - -static GtkWidget *add_gas_combobox_to_box(GtkWidget *box, const char *label, int idx) -{ - GtkWidget *frame, *combo; - - if (!gas_model) { - gas_model = gtk_list_store_new(1, G_TYPE_STRING); - add_string_list_entry(_("AIR"), gas_model); - add_string_list_entry(_("EAN32"), gas_model); - add_string_list_entry(_("EAN36"), gas_model); - } - combo = combo_box_with_model_and_entry(gas_model); - gtk_widget_add_events(combo, GDK_FOCUS_CHANGE_MASK); - g_signal_connect(gtk_bin_get_child(GTK_BIN(combo)), "focus-out-event", G_CALLBACK(gas_focus_out_cb), NULL + idx); - g_signal_connect(combo, "changed", G_CALLBACK(gas_changed_cb), NULL + idx); - if (label) { - frame = gtk_frame_new(label); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); - gtk_container_add(GTK_CONTAINER(frame), combo); - } else { - gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 2); - } - - return combo; -} - -static void add_waypoint_widgets(GtkWidget *box, int idx) -{ - GtkWidget *hbox; - - hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); - if (idx == 0) { - entry_depth[idx] = add_entry_to_box(hbox, _("Ending Depth")); - entry_duration[idx] = add_entry_to_box(hbox, _("Segment Time")); - entry_gas[idx] = add_gas_combobox_to_box(hbox, C_("Type of","Gas Used"), idx); - entry_po2[idx] = add_entry_to_box(hbox, _("CC SetPoint")); - } else { - entry_depth[idx] = add_entry_to_box(hbox, NULL); - entry_duration[idx] = add_entry_to_box(hbox, NULL); - entry_gas[idx] = add_gas_combobox_to_box(hbox, NULL, idx); - entry_po2[idx] = add_entry_to_box(hbox, NULL); - } - gtk_widget_add_events(entry_depth[idx], GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry_depth[idx], "focus-out-event", G_CALLBACK(depth_focus_out_cb), NULL + idx); - gtk_widget_add_events(entry_duration[idx], GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry_duration[idx], "focus-out-event", G_CALLBACK(duration_focus_out_cb), NULL + idx); - gtk_widget_add_events(entry_po2[idx], GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry_po2[idx], "focus-out-event", G_CALLBACK(po2_focus_out_cb), NULL + idx); -} - -static void add_waypoint_cb(GtkButton *button, gpointer _data) -{ - GtkWidget *vbox = _data; - if (nr_waypoints < MAX_WAYPOINTS) { - GtkWidget *ovbox, *dialog; - add_waypoint_widgets(vbox, nr_waypoints); - nr_waypoints++; - ovbox = gtk_widget_get_parent(GTK_WIDGET(button)); - dialog = gtk_widget_get_parent(ovbox); - gtk_widget_show_all(dialog); - } else { - show_error(_("Too many waypoints")); - } -} - -static void add_entry_with_callback(GtkWidget *box, int length, char *label, char *initialtext, - gboolean (*callback)(GtkWidget *, GdkEvent *, gpointer), gpointer data) -{ - GtkWidget *entry = add_entry_to_box(box, label); - gtk_entry_set_max_length(GTK_ENTRY(entry), length); - gtk_entry_set_text(GTK_ENTRY(entry), initialtext); - gtk_widget_add_events(entry, GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry, "focus-out-event", G_CALLBACK(callback), data); -} - -/* set up the dialog where the user can input their dive plan */ -void input_plan() -{ - GtkWidget *content, *vbox, *hbox, *outervbox, *add_row, *label; - char *bottom_sac, *deco_sac, gflowstring[4], gfhighstring[4]; - char *explanationtext = _("Add segments below.\nEach line describes part of the planned dive.\n" - "An entry with depth, time and gas describes a segment that ends " - "at the given depth, takes the given time (if relative, e.g. '+3:30') " - "or ends at the given time (if absolute e.g '@5:00', 'runtime'), and uses the given gas.\n" - "An empty gas means 'use previous gas' (or AIR if no gas was specified).\n" - "An entry that has a depth and a gas given but no time is special; it " - "informs the planner that the gas specified is available for the ascent " - "once the depth given has been reached.\n" - "CC SetPoint specifies CC (rebreather) dives, leave empty for OC.\n"); - char *labeltext; - int len; - - disclaimer = _("DISCLAIMER / WARNING: THIS IS A NEW IMPLEMENTATION OF THE BUHLMANN " - "ALGORITHM AND A DIVE PLANNER IMPLEMENTION BASED ON THAT WHICH HAS " - "RECEIVED ONLY A LIMITED AMOUNT OF TESTING. WE STRONGLY RECOMMEND NOT TO " - "PLAN DIVES SIMPLY BASED ON THE RESULTS GIVEN HERE."); - if (diveplan.dp) - free_dps(diveplan.dp); - memset(&diveplan, 0, sizeof(diveplan)); - diveplan.lastdive_nr = dive_table.nr - 1; - free(cache_data); - cache_data = NULL; - planned_dive = NULL; - planner = gtk_dialog_new_with_buttons(_("Dive Plan - THIS IS JUST A SIMULATION; DO NOT USE FOR DIVING"), - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - NULL); - content = gtk_dialog_get_content_area (GTK_DIALOG (planner)); - outervbox = gtk_vbox_new(FALSE, 2); - gtk_container_add (GTK_CONTAINER (content), outervbox); - - len = strlen(explanationtext) + strlen(disclaimer) + sizeof(""); - labeltext = malloc(len); - snprintf(labeltext, len, "%s%s", explanationtext, disclaimer); - label = gtk_label_new(labeltext); - gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); - gtk_label_set_use_markup(GTK_LABEL(label), TRUE); - gtk_label_set_width_chars(GTK_LABEL(label), 60); - gtk_box_pack_start(GTK_BOX(outervbox), label, TRUE, TRUE, 0); - vbox = gtk_vbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(outervbox), vbox, TRUE, TRUE, 0); - hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); - add_entry_with_callback(hbox, 12, _("Dive starts when?"), "+60:00", starttime_focus_out_cb, NULL); - add_entry_with_callback(hbox, 12, _("Surface Pressure (mbar)"), SURFACE_PRESSURE_STRING, surfpres_focus_out_cb, NULL); - - hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); - if (get_units()->volume == CUFT) { - bottom_sac = _("0.7 cuft/min"); - deco_sac = _("0.6 cuft/min"); - diveplan.bottomsac = 1000 * cuft_to_l(0.7); - diveplan.decosac = 1000 * cuft_to_l(0.6); - } else { - bottom_sac = _("20 l/min"); - deco_sac = _("17 l/min"); - diveplan.bottomsac = 20000; - diveplan.decosac = 17000; - } - add_entry_with_callback(hbox, 12, _("SAC during dive"), bottom_sac, sac_focus_out_cb, &diveplan.bottomsac); - add_entry_with_callback(hbox, 12, _("SAC during decostop"), deco_sac, sac_focus_out_cb, &diveplan.decosac); - plangflow = prefs.gflow; - plangfhigh = prefs.gfhigh; - snprintf(gflowstring, sizeof(gflowstring), "%3.0f", 100 * plangflow); - snprintf(gfhighstring, sizeof(gflowstring), "%3.0f", 100 * plangfhigh); - add_entry_with_callback(hbox, 5, _("GFlow for plan"), gflowstring, gf_focus_out_cb, &plangflow); - add_entry_with_callback(hbox, 5, _("GFhigh for plan"), gfhighstring, gf_focus_out_cb, &plangfhigh); - diveplan.when = current_time_notz() + 3600; - diveplan.surface_pressure = SURFACE_PRESSURE; - nr_waypoints = 4; - add_waypoint_widgets(vbox, 0); - add_waypoint_widgets(vbox, 1); - add_waypoint_widgets(vbox, 2); - add_waypoint_widgets(vbox, 3); - add_row = gtk_button_new_with_label(_("Add waypoint")); - g_signal_connect(G_OBJECT(add_row), "clicked", G_CALLBACK(add_waypoint_cb), vbox); - gtk_box_pack_start(GTK_BOX(outervbox), add_row, FALSE, FALSE, 0); - gtk_widget_show_all(planner); - if (gtk_dialog_run(GTK_DIALOG(planner)) == GTK_RESPONSE_ACCEPT) { - plan(&diveplan, &cache_data, &planned_dive); - mark_divelist_changed(TRUE); - } else { - if (planned_dive) { - /* we have added a dive during the dynamic construction - * in the dialog; get rid of it */ - delete_single_dive(dive_table.nr - 1); - report_dives(FALSE, FALSE); - planned_dive = NULL; - } - } - gtk_widget_destroy(planner); - planner_error_bar = NULL; - error_label = NULL; - set_gf(prefs.gflow, prefs.gfhigh); -} diff --git a/planner.h b/planner.h new file mode 100644 index 000000000..099f640fc --- /dev/null +++ b/planner.h @@ -0,0 +1,21 @@ +#ifndef PLANNER_H +#define PLANNER_H + +extern void plan(struct diveplan *diveplan, char **cache_datap, struct dive **divep, char **error_string_p); +extern int validate_gas(const char *text, int *o2_p, int *he_p); +extern int validate_time(const char *text, int *sec_p, int *rel_p); +extern int validate_depth(const char *text, int *mm_p); +extern int validate_po2(const char *text, int *mbar_po2); +extern int validate_volume(const char *text, int *sac); +extern timestamp_t current_time_notz(void); +extern void show_planned_dive(char **error_string_p); +extern int add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel); +extern void add_po2_to_nth_dp(struct diveplan *diveplan, int idx, int po2); + +extern struct diveplan diveplan; +extern struct dive *planned_dive; +extern char *cache_data; +extern char *disclaimer; +extern double plangflow, plangfhigh; + +#endif /* PLANNER_H */ From 4cf244e22877388adcabef133e66b2810c0ab664 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 7 Apr 2013 19:50:26 -0700 Subject: [PATCH 013/226] Fix some of the gcc-4.8 warnings Most of the warnings are IMHO false positives: e.g.: an enum variable is initialized in a switch statement that has a case for each possible enum value - yet gcc 4.8 warns that it could be used uninitialized; or: two variables are initialized together in the code - second one of them is previously initialized to -1 at declaration time, both are initialized in an if (second one == -1) clause - so they are guaranteed to both be initialized... I did not "fix" those as the code is actually correct. But there are three spots where it catches things that could indeed go wrong (with odd input data in one of them). This commit also adds a check to only call g_type_init() for older versions of glib as in newer ones it is deprecated. Signed-off-by: Dirk Hohndel --- qt-gui.cpp | 3 ++- statistics.c | 1 + uemis-downloader.c | 2 +- uemis.c | 5 +++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/qt-gui.cpp b/qt-gui.cpp index e46f4765f..e667e97bf 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -1889,8 +1889,9 @@ void init_ui(int *argcp, char ***argvp) star_strings[4] = "**** "; star_strings[5] = "*****"; } +#if !GLIB_CHECK_VERSION(2,3,6) g_type_init(); - +#endif subsurface_open_conf(); load_preferences(); diff --git a/statistics.c b/statistics.c index a39799a08..502c06cb4 100644 --- a/statistics.c +++ b/statistics.c @@ -668,6 +668,7 @@ static void get_ranges(char *buffer, int size) } } } + len = strlen(buffer); if (first != last) { if (first + 1 == last) snprintf(buffer + len, size - len, ", %d", last); diff --git a/uemis-downloader.c b/uemis-downloader.c index c5113d95a..d33f08b8c 100644 --- a/uemis-downloader.c +++ b/uemis-downloader.c @@ -546,7 +546,7 @@ static void parse_divespot(char *buf) char *tag, *type, *val; char locationstring[1024] = ""; int divespot, len; - double latitude, longitude; + double latitude = 0.0, longitude = 0.0; if (strcmp(tp, "divespot")) diff --git a/uemis.c b/uemis.c index aad76c10f..3f7c71b7f 100644 --- a/uemis.c +++ b/uemis.c @@ -288,7 +288,7 @@ void uemis_parse_divelog_binary(char *base64, void *datap) { int datalen; int i; uint8_t *data; - struct sample *sample; + struct sample *sample = NULL; uemis_sample_t *u_sample; struct dive *dive = datap; struct divecomputer *dc = &dive->dc; @@ -365,6 +365,7 @@ void uemis_parse_divelog_binary(char *base64, void *datap) { i += 0x25; u_sample++; } - dive->dc.duration.seconds = sample->time.seconds - 1; + if (sample) + dive->dc.duration.seconds = sample->time.seconds - 1; return; } From 459dc08c32abb19df77cab03093ab754878bfdcb Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 7 Apr 2013 20:33:48 -0700 Subject: [PATCH 014/226] Forgot to add planner-gtk.c Oops Signed-off-by: Dirk Hohndel --- planner-gtk.c | 435 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 planner-gtk.c diff --git a/planner-gtk.c b/planner-gtk.c new file mode 100644 index 000000000..1b99fb391 --- /dev/null +++ b/planner-gtk.c @@ -0,0 +1,435 @@ +/* planner.c + * + * code that allows us to plan future dives + * + * (c) Dirk Hohndel 2013 + */ +#include +#include +#include +#include +#include "dive.h" +#include "divelist.h" +#include "display-gtk.h" +#include "planner.h" + +GtkWidget *planner, *planner_error_bar, *error_label; + +static void on_error_bar_response(GtkWidget *widget, gint response, gpointer data) +{ + if (response == GTK_RESPONSE_OK) + { + gtk_widget_destroy(widget); + planner_error_bar = NULL; + error_label = NULL; + } +} + +static void show_error(const char *fmt, ...) +{ + va_list args; + GError *error; + GtkWidget *box, *container; + gboolean bar_is_visible = TRUE; + + va_start(args, fmt); + error = g_error_new_valist(g_quark_from_string("subsurface"), DIVE_ERROR_PLAN, fmt, args); + va_end(args); + if (!planner_error_bar) { + planner_error_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + g_signal_connect(planner_error_bar, "response", G_CALLBACK(on_error_bar_response), NULL); + gtk_info_bar_set_message_type(GTK_INFO_BAR(planner_error_bar), GTK_MESSAGE_ERROR); + bar_is_visible = FALSE; + } + container = gtk_info_bar_get_content_area(GTK_INFO_BAR(planner_error_bar)); + if (error_label) + gtk_container_remove(GTK_CONTAINER(container), error_label); + error_label = gtk_label_new(error->message); + gtk_container_add(GTK_CONTAINER(container), error_label); + box = gtk_dialog_get_content_area(GTK_DIALOG(planner)); + if (!bar_is_visible) + gtk_box_pack_start(GTK_BOX(box), planner_error_bar, FALSE, FALSE, 0); + gtk_widget_show_all(box); + /* make sure this actually gets shown BEFORE the calculations run */ + while (gtk_events_pending()) + gtk_main_iteration_do(FALSE); +} + + +static GtkWidget *add_entry_to_box(GtkWidget *box, const char *label) +{ + GtkWidget *entry, *frame; + + entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry), 16); + if (label) { + frame = gtk_frame_new(label); + gtk_container_add(GTK_CONTAINER(frame), entry); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); + } else { + gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 2); + } + return entry; +} + +#define MAX_WAYPOINTS 12 +GtkWidget *entry_depth[MAX_WAYPOINTS], *entry_duration[MAX_WAYPOINTS], *entry_gas[MAX_WAYPOINTS], *entry_po2[MAX_WAYPOINTS]; +int nr_waypoints = 0; +static GtkListStore *gas_model = NULL; + +static gboolean gas_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) +{ + const char *gastext; + int o2, he; + int idx = data - NULL; + char *error_string = NULL; + + gastext = gtk_entry_get_text(GTK_ENTRY(entry)); + o2 = he = 0; + if (validate_gas(gastext, &o2, &he)) + add_string_list_entry(gastext, gas_model); + add_gas_to_nth_dp(&diveplan, idx, o2, he); + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); + return FALSE; +} + +static void gas_changed_cb(GtkWidget *combo, gpointer data) +{ + const char *gastext; + int o2, he; + int idx = data - NULL; + char *error_string = NULL; + + gastext = get_active_text(GTK_COMBO_BOX(combo)); + /* stupidly this gets called for two reasons: + * a) any keystroke into the entry field + * b) mouse selection of a dropdown + * we only care about b) (a) is handled much better with the focus-out event) + * so let's check that the text returned is actually in our model before going on + */ + if (match_list(gas_model, gastext) != MATCH_EXACT) + return; + o2 = he = 0; + if (!validate_gas(gastext, &o2, &he)) { + /* this should never happen as only validated texts should be + * in the dropdown */ + show_error(_("Invalid gas for row %d"),idx); + } + add_gas_to_nth_dp(&diveplan, idx, o2, he); + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); +} + +static gboolean depth_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) +{ + const char *depthtext; + int depth = -1; + int idx = data - NULL; + char *error_string = NULL; + + depthtext = gtk_entry_get_text(GTK_ENTRY(entry)); + + if (validate_depth(depthtext, &depth)) { + if (depth > 150000) + show_error(_("Warning - planning very deep dives can take excessive amounts of time")); + add_depth_to_nth_dp(&diveplan, idx, depth); + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); + } else { + /* it might be better to instead change the color of the input field or something */ + if (depth == -1) + show_error(_("Invalid depth - could not parse \"%s\""), depthtext); + else + show_error(_("Invalid depth - values deeper than 400m not supported")); + } + return FALSE; +} + +static gboolean duration_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) +{ + const char *durationtext; + int duration, is_rel; + int idx = data - NULL; + char *error_string = NULL; + + durationtext = gtk_entry_get_text(GTK_ENTRY(entry)); + if (validate_time(durationtext, &duration, &is_rel)) + if (add_duration_to_nth_dp(&diveplan, idx, duration, is_rel) < 0) + show_error(_("Warning - extremely long dives can cause long calculation time")); + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); + return FALSE; +} + +static gboolean po2_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) +{ + const char *po2text; + int po2; + int idx = data - NULL; + char *error_string = NULL; + + po2text = gtk_entry_get_text(GTK_ENTRY(entry)); + if (validate_po2(po2text, &po2)) + add_po2_to_nth_dp(&diveplan, idx, po2); + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); + return FALSE; +} + +static gboolean starttime_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) +{ + const char *starttimetext; + int starttime, is_rel; + char *error_string = NULL; + + starttimetext = gtk_entry_get_text(GTK_ENTRY(entry)); + if (validate_time(starttimetext, &starttime, &is_rel)) { + /* we alway make this relative - either from the current time or from the + * end of the last dive, whichever is later */ + timestamp_t cur = current_time_notz(); + if (diveplan.lastdive_nr >= 0) { + struct dive *last_dive = get_dive(diveplan.lastdive_nr); + if (last_dive && last_dive->when + last_dive->dc.duration.seconds > cur) + cur = last_dive->when + last_dive->dc.duration.seconds; + } + diveplan.when = cur + starttime; + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); + } else { + /* it might be better to instead change the color of the input field or something */ + show_error(_("Invalid starttime")); + } + return FALSE; +} + +static gboolean surfpres_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) +{ + const char *surfprestext; + char *error_string = NULL; + + surfprestext = gtk_entry_get_text(GTK_ENTRY(entry)); + diveplan.surface_pressure = atoi(surfprestext); + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); + return FALSE; +} + +static gboolean sac_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) +{ + const char *sactext; + char *error_string = NULL; + + sactext = gtk_entry_get_text(GTK_ENTRY(entry)); + if (validate_volume(sactext, data)) { + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); + } + return FALSE; +} + +static gboolean gf_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) +{ + const char *gftext; + int gf; + double *gfp = data; + char *error_string = NULL; + + gftext = gtk_entry_get_text(GTK_ENTRY(entry)); + if (sscanf(gftext, "%d", &gf) == 1) { + *gfp = gf / 100.0; + show_planned_dive(&error_string); + if (error_string) + show_error(error_string); + } + return FALSE; +} + +static GtkWidget *add_gas_combobox_to_box(GtkWidget *box, const char *label, int idx) +{ + GtkWidget *frame, *combo; + + if (!gas_model) { + gas_model = gtk_list_store_new(1, G_TYPE_STRING); + add_string_list_entry(_("AIR"), gas_model); + add_string_list_entry(_("EAN32"), gas_model); + add_string_list_entry(_("EAN36"), gas_model); + } + combo = combo_box_with_model_and_entry(gas_model); + gtk_widget_add_events(combo, GDK_FOCUS_CHANGE_MASK); + g_signal_connect(gtk_bin_get_child(GTK_BIN(combo)), "focus-out-event", G_CALLBACK(gas_focus_out_cb), NULL + idx); + g_signal_connect(combo, "changed", G_CALLBACK(gas_changed_cb), NULL + idx); + if (label) { + frame = gtk_frame_new(label); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(frame), combo); + } else { + gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 2); + } + + return combo; +} + +static void add_waypoint_widgets(GtkWidget *box, int idx) +{ + GtkWidget *hbox; + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); + if (idx == 0) { + entry_depth[idx] = add_entry_to_box(hbox, _("Ending Depth")); + entry_duration[idx] = add_entry_to_box(hbox, _("Segment Time")); + entry_gas[idx] = add_gas_combobox_to_box(hbox, C_("Type of","Gas Used"), idx); + entry_po2[idx] = add_entry_to_box(hbox, _("CC SetPoint")); + } else { + entry_depth[idx] = add_entry_to_box(hbox, NULL); + entry_duration[idx] = add_entry_to_box(hbox, NULL); + entry_gas[idx] = add_gas_combobox_to_box(hbox, NULL, idx); + entry_po2[idx] = add_entry_to_box(hbox, NULL); + } + gtk_widget_add_events(entry_depth[idx], GDK_FOCUS_CHANGE_MASK); + g_signal_connect(entry_depth[idx], "focus-out-event", G_CALLBACK(depth_focus_out_cb), NULL + idx); + gtk_widget_add_events(entry_duration[idx], GDK_FOCUS_CHANGE_MASK); + g_signal_connect(entry_duration[idx], "focus-out-event", G_CALLBACK(duration_focus_out_cb), NULL + idx); + gtk_widget_add_events(entry_po2[idx], GDK_FOCUS_CHANGE_MASK); + g_signal_connect(entry_po2[idx], "focus-out-event", G_CALLBACK(po2_focus_out_cb), NULL + idx); +} + +static void add_waypoint_cb(GtkButton *button, gpointer _data) +{ + GtkWidget *vbox = _data; + if (nr_waypoints < MAX_WAYPOINTS) { + GtkWidget *ovbox, *dialog; + add_waypoint_widgets(vbox, nr_waypoints); + nr_waypoints++; + ovbox = gtk_widget_get_parent(GTK_WIDGET(button)); + dialog = gtk_widget_get_parent(ovbox); + gtk_widget_show_all(dialog); + } else { + show_error(_("Too many waypoints")); + } +} + +static void add_entry_with_callback(GtkWidget *box, int length, char *label, char *initialtext, + gboolean (*callback)(GtkWidget *, GdkEvent *, gpointer), gpointer data) +{ + GtkWidget *entry = add_entry_to_box(box, label); + gtk_entry_set_max_length(GTK_ENTRY(entry), length); + gtk_entry_set_text(GTK_ENTRY(entry), initialtext); + gtk_widget_add_events(entry, GDK_FOCUS_CHANGE_MASK); + g_signal_connect(entry, "focus-out-event", G_CALLBACK(callback), data); +} + +/* set up the dialog where the user can input their dive plan */ +void input_plan() +{ + GtkWidget *content, *vbox, *hbox, *outervbox, *add_row, *label; + char *bottom_sac, *deco_sac, gflowstring[4], gfhighstring[4]; + char *explanationtext = _("Add segments below.\nEach line describes part of the planned dive.\n" + "An entry with depth, time and gas describes a segment that ends " + "at the given depth, takes the given time (if relative, e.g. '+3:30') " + "or ends at the given time (if absolute e.g '@5:00', 'runtime'), and uses the given gas.\n" + "An empty gas means 'use previous gas' (or AIR if no gas was specified).\n" + "An entry that has a depth and a gas given but no time is special; it " + "informs the planner that the gas specified is available for the ascent " + "once the depth given has been reached.\n" + "CC SetPoint specifies CC (rebreather) dives, leave empty for OC.\n"); + char *labeltext; + char *error_string = NULL; + int len; + + disclaimer = _("DISCLAIMER / WARNING: THIS IS A NEW IMPLEMENTATION OF THE BUHLMANN " + "ALGORITHM AND A DIVE PLANNER IMPLEMENTION BASED ON THAT WHICH HAS " + "RECEIVED ONLY A LIMITED AMOUNT OF TESTING. WE STRONGLY RECOMMEND NOT TO " + "PLAN DIVES SIMPLY BASED ON THE RESULTS GIVEN HERE."); + if (diveplan.dp) + free_dps(diveplan.dp); + memset(&diveplan, 0, sizeof(diveplan)); + diveplan.lastdive_nr = dive_table.nr - 1; + free(cache_data); + cache_data = NULL; + planned_dive = NULL; + planner = gtk_dialog_new_with_buttons(_("Dive Plan - THIS IS JUST A SIMULATION; DO NOT USE FOR DIVING"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + content = gtk_dialog_get_content_area (GTK_DIALOG (planner)); + outervbox = gtk_vbox_new(FALSE, 2); + gtk_container_add (GTK_CONTAINER (content), outervbox); + + len = strlen(explanationtext) + strlen(disclaimer) + sizeof(""); + labeltext = malloc(len); + snprintf(labeltext, len, "%s%s", explanationtext, disclaimer); + label = gtk_label_new(labeltext); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_label_set_use_markup(GTK_LABEL(label), TRUE); + gtk_label_set_width_chars(GTK_LABEL(label), 60); + gtk_box_pack_start(GTK_BOX(outervbox), label, TRUE, TRUE, 0); + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(outervbox), vbox, TRUE, TRUE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); + add_entry_with_callback(hbox, 12, _("Dive starts when?"), "+60:00", starttime_focus_out_cb, NULL); + add_entry_with_callback(hbox, 12, _("Surface Pressure (mbar)"), SURFACE_PRESSURE_STRING, surfpres_focus_out_cb, NULL); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); + if (get_units()->volume == CUFT) { + bottom_sac = _("0.7 cuft/min"); + deco_sac = _("0.6 cuft/min"); + diveplan.bottomsac = 1000 * cuft_to_l(0.7); + diveplan.decosac = 1000 * cuft_to_l(0.6); + } else { + bottom_sac = _("20 l/min"); + deco_sac = _("17 l/min"); + diveplan.bottomsac = 20000; + diveplan.decosac = 17000; + } + add_entry_with_callback(hbox, 12, _("SAC during dive"), bottom_sac, sac_focus_out_cb, &diveplan.bottomsac); + add_entry_with_callback(hbox, 12, _("SAC during decostop"), deco_sac, sac_focus_out_cb, &diveplan.decosac); + plangflow = prefs.gflow; + plangfhigh = prefs.gfhigh; + snprintf(gflowstring, sizeof(gflowstring), "%3.0f", 100 * plangflow); + snprintf(gfhighstring, sizeof(gflowstring), "%3.0f", 100 * plangfhigh); + add_entry_with_callback(hbox, 5, _("GFlow for plan"), gflowstring, gf_focus_out_cb, &plangflow); + add_entry_with_callback(hbox, 5, _("GFhigh for plan"), gfhighstring, gf_focus_out_cb, &plangfhigh); + diveplan.when = current_time_notz() + 3600; + diveplan.surface_pressure = SURFACE_PRESSURE; + nr_waypoints = 4; + add_waypoint_widgets(vbox, 0); + add_waypoint_widgets(vbox, 1); + add_waypoint_widgets(vbox, 2); + add_waypoint_widgets(vbox, 3); + add_row = gtk_button_new_with_label(_("Add waypoint")); + g_signal_connect(G_OBJECT(add_row), "clicked", G_CALLBACK(add_waypoint_cb), vbox); + gtk_box_pack_start(GTK_BOX(outervbox), add_row, FALSE, FALSE, 0); + gtk_widget_show_all(planner); + if (gtk_dialog_run(GTK_DIALOG(planner)) == GTK_RESPONSE_ACCEPT) { + plan(&diveplan, &cache_data, &planned_dive, &error_string); + if (error_string) + show_error(error_string); + mark_divelist_changed(TRUE); + } else { + if (planned_dive) { + /* we have added a dive during the dynamic construction + * in the dialog; get rid of it */ + delete_single_dive(dive_table.nr - 1); + report_dives(FALSE, FALSE); + planned_dive = NULL; + } + } + gtk_widget_destroy(planner); + planner_error_bar = NULL; + error_label = NULL; + set_gf(prefs.gflow, prefs.gfhigh); +} From d3c0a90e347b8393a7517b08812c28f099bea65c Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 8 Apr 2013 09:36:38 -0700 Subject: [PATCH 015/226] Remove the toplevel ui directory This is fron Alberto's first stab at MainWindow, but Tomaz is doing all this work in the qt-ui directory now... Reported-by: Pierre-Yves Chibon Signed-off-by: Dirk Hohndel --- ui/main-window.ui | 101 ---------------------------------------------- 1 file changed, 101 deletions(-) delete mode 100644 ui/main-window.ui diff --git a/ui/main-window.ui b/ui/main-window.ui deleted file mode 100644 index 58ed198fd..000000000 --- a/ui/main-window.ui +++ /dev/null @@ -1,101 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 800 - 600 - - - - Subsurface - - - - - - 0 - 0 - 800 - 23 - - - - - File - - - - - - - - - - - - - - - - - - New - - - Ctrl+N - - - - - - - - - - Open... - - - Ctrl+O - - - - - - - - Save... - - - Ctrl+S - - - - - - - - Save As... - - - Ctrl+Shift+S - - - - - - - - Close - - - Ctrl+W - - - - - - From e865aed478b30a0f9a93b2f2c87d23cd7cbd72df Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Mon, 8 Apr 2013 19:54:56 +0100 Subject: [PATCH 016/226] Change tab widget names. Names for individual tab widgets were not specific and caused warnings from UIC. Rename the individual widgets to reflect purpose. [Dirk Hohndel: removed some of the hunks that appeared to be unintentional changes not mentioned in the commit log] Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/maintab.ui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index a0aec4358..7432dc466 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -16,7 +16,7 @@ 0 - + Dive Info @@ -307,7 +307,7 @@ - + Dive Notes @@ -374,7 +374,7 @@ - + Equipment @@ -484,7 +484,7 @@ - + Stats From a94e09ad5c212dd839b4bba166dba496b31212ce Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Tue, 9 Apr 2013 09:35:44 +0100 Subject: [PATCH 017/226] Add a slot to mainwindow but amend to use Q_SLOTS For some reason, 'private slots:' causes a build error but private Q_SLOTS: works. The error was that 'slots' did not name a type and it appeared to be insensitive to whether the Makefile rule for .moc was in its current place or preceeded the rule for .cpp. Add a slot using the connectByName idiom e.g. actionNew connects to slot on_actionNew_triggered(). Use qDebug to show this fires if the menu option is selected. Above to demonstrate how to begin to link menu to code paths. Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 6 ++++++ qt-ui/mainwindow.h | 13 ++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 851d1aa1c..d3e5603be 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -2,10 +2,16 @@ #include "ui_mainwindow.h" #include +#include MainWindow::MainWindow() : ui(new Ui::MainWindow()) { ui->setupUi(this); } +void MainWindow::on_actionNew_triggered() +{ + qDebug() << "actionNew"; +} + #include "mainwindow.moc" diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 9e15e58b4..6361870b2 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -3,6 +3,7 @@ #include + namespace Ui { class MainWindow; @@ -13,12 +14,18 @@ class DiveNotes; class Stats; class Equipment; -class MainWindow : public QMainWindow{ - Q_OBJECT +class MainWindow : public QMainWindow +{ +Q_OBJECT public: MainWindow(); + +private Q_SLOTS: + + void on_actionNew_triggered(); + private: Ui::MainWindow *ui; }; -#endif \ No newline at end of file +#endif From 80c8d08d8bf55b18a2da3ecda83fbe4573eee704 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 9 Apr 2013 07:01:20 -0700 Subject: [PATCH 018/226] Make Qt branch cross-build for Windows Signed-off-by: Dirk Hohndel --- packaging/windows/mingw-make.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/windows/mingw-make.sh b/packaging/windows/mingw-make.sh index 6201d41a4..53599ed20 100755 --- a/packaging/windows/mingw-make.sh +++ b/packaging/windows/mingw-make.sh @@ -11,7 +11,7 @@ rm packaging/windows/subsurface.nsi export PATH=/usr/i686-w64-mingw32/sys-root/mingw/bin:$PATH -make CC=i686-w64-mingw32-gcc \ +make CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ \ PKGCONFIG=i686-w64-mingw32-pkg-config \ PKG_CONFIG_PATH=/usr/i686-w64-mingw32/sys-root/mingw/lib/pkgconfig/ \ CROSS_PATH=/usr/i686-w64-mingw32/sys-root/mingw/ \ From 99153de715955a9a80097c9951eaf096efeb5752 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Tue, 9 Apr 2013 19:34:26 +0400 Subject: [PATCH 019/226] Makefile: detect which files need moc and uic Add some magic rules to detect which files need to be processed by the moc or uic tools, as well as a way to manually specify exceptions. Signed-off-by: Alberto Mardegan Signed-off-by: Dirk Hohndel --- Makefile | 54 +++++++++++++++++++++++++------------------- qt-ui/maintab.cpp | 2 -- qt-ui/mainwindow.cpp | 2 -- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Makefile b/Makefile index 28b7e0365..c89816de5 100644 --- a/Makefile +++ b/Makefile @@ -191,13 +191,24 @@ OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) $(QTOBJS) -DEPS = $(wildcard .dep/*.dep) +# Add files to the following variables if the auto-detection based on the +# filename fails +OBJS_NEEDING_MOC = +OBJS_NEEDING_UIC = +HEADERS_NEEDING_MOC = +# Add the objects for the header files which define QObject subclasses +HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(OBJS:.o=.h)) +MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) + +ALL_OBJS = $(OBJS) $(MOC_OBJS) + +DEPS = $(wildcard .dep/*.dep) all: $(NAME) -$(NAME): gen_version_file $(OBJS) $(MSGOBJS) $(INFOPLIST) - $(CXX) $(LDFLAGS) -o $(NAME) $(OBJS) $(LIBS) +$(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) + $(CXX) $(LDFLAGS) -o $(NAME) $(ALL_OBJS) $(LIBS) gen_version_file: ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING)) @@ -310,36 +321,33 @@ MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $ @mkdir -p .dep .dep/qt-ui @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< +# Detect which files require the moc or uic tools to be run +CPP_NEEDING_MOC = $(shell grep -l -s '^\#include \".*\.moc\"' $(OBJS:.o=.cpp)) +OBJS_NEEDING_MOC += $(CPP_NEEDING_MOC:.cpp=.o) + +CPP_NEEDING_UIC = $(shell grep -l -s '^\#include \"ui_.*\.h\"' $(OBJS:.o=.cpp)) +OBJS_NEEDING_UIC += $(CPP_NEEDING_UIC:.cpp=.o) + +# This rule is for running the moc on QObject subclasses defined in the .h +# files. +%.moc.cpp: %.h + @echo ' MOC' $< + @$(MOC) $(MOCFLAGS) $< -o $@ + # This rule is for running the moc on QObject subclasses defined in the .cpp # files; remember to #include ".moc" at the end of the .cpp file, or # you'll get linker errors ("undefined vtable for...") -# To activate this rule, you need another rule on the .o file, like: -# file.o: file.moc - -qt-ui/%.moc: qt-ui/%.h - @echo ' MOC' $< - @$(MOC) -i $(MOCFLAGS) $< -o $@ - -# this is just here for qt-gui.cpp -# should be removed once all the Qt UI code has been moved into qt-ui - %.moc: %.cpp @echo ' MOC' $< @$(MOC) -i $(MOCFLAGS) $< -o $@ # This creates the ui headers. -# To activate this rule, you need to add the ui_*.h file to the .o file: -# file.o: ui_file.h - -qt-ui/ui_%.h: qt-ui/%.ui +ui_%.h: %.ui @echo ' UIC' $< @$(UIC) $< -o $@ -qt-gui.o: qt-gui.moc - -qt-ui/maintab.o: qt-ui/maintab.moc qt-ui/ui_maintab.h - -qt-ui/mainwindow.o: qt-ui/mainwindow.moc qt-ui/ui_mainwindow.h +$(OBJS_NEEDING_MOC): %.o: %.moc +$(OBJS_NEEDING_UIC): qt-ui/%.o: qt-ui/ui_%.h share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases mkdir -p $(dir $@) @@ -367,7 +375,7 @@ doc: $(MAKE) -C Documentation doc clean: - rm -f $(OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ + rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h rm -rf share .dep diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 4569958c8..c630965d2 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -6,5 +6,3 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), { ui->setupUi(this); } - -#include "maintab.moc" diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index d3e5603be..03e14377f 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -13,5 +13,3 @@ void MainWindow::on_actionNew_triggered() { qDebug() << "actionNew"; } - -#include "mainwindow.moc" From 1182c989973a3006a482413ddd2d523be64e5443 Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Tue, 9 Apr 2013 17:26:23 +0100 Subject: [PATCH 020/226] Add stub slots for all menu items Naming of QActions was inconsistent wrt abbreviations - fixed. Add stub slots for each action relying on connect by name. Add qDebug() message to allow people to check that menu items fire slots; not really necessary but may provide some reassurance as we build familiarity with Qt. Some changes to display text for menu items (e.g. Tree becomes View All). Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 138 ++++++++++++++++++++++++++++++++++++++++++- qt-ui/mainwindow.h | 34 +++++++++++ qt-ui/mainwindow.ui | 110 +++++++++++++++++----------------- 3 files changed, 226 insertions(+), 56 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 03e14377f..9bcc03c75 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -11,5 +11,141 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()) void MainWindow::on_actionNew_triggered() { - qDebug() << "actionNew"; + qDebug() << "actionNew"; } + +void MainWindow::on_actionOpen_triggered() +{ + qDebug() << "actionOpen"; +} + +void MainWindow::on_actionSave_triggered() +{ + qDebug() << "actionSave"; +} + +void MainWindow::on_actionSaveAs_triggered() +{ + qDebug() << "actionSaveAs"; +} +void MainWindow::on_actionClose_triggered() +{ + qDebug() << "actionClose"; +} + +void MainWindow::on_actionImport_triggered() +{ + qDebug() << "actionImport"; +} + +void MainWindow::on_actionExportUDDF_triggered() +{ + qDebug() << "actionExportUDDF"; +} + +void MainWindow::on_actionPrint_triggered() +{ + qDebug() << "actionPrint"; +} + +void MainWindow::on_actionPreferences_triggered() +{ + qDebug() << "actionPreferences"; +} + +void MainWindow::on_actionQuit_triggered() +{ + qDebug() << "actionQuit"; +} + +void MainWindow::on_actionDownloadDC_triggered() +{ + qDebug() << "actionDownloadDC"; +} + +void MainWindow::on_actionDownloadWeb_triggered() +{ + qDebug() << "actionDownloadWeb";} + +void MainWindow::on_actionEditDeviceNames_triggered() +{ + qDebug() << "actionEditDeviceNames";} + +void MainWindow::on_actionAddDive_triggered() +{ + qDebug() << "actionAddDive"; +} + +void MainWindow::on_actionRenumber_triggered() +{ + qDebug() << "actionRenumber"; +} + +void MainWindow::on_actionAutoGroup_triggered() +{ + qDebug() << "actionAutoGroup"; +} + +void MainWindow::on_actionToggleZoom_triggered() +{ + qDebug() << "actionToggleZoom"; +} + +void MainWindow::on_actionYearlyStatistics_triggered() +{ + qDebug() << "actionYearlyStatistics"; +} + +void MainWindow::on_actionViewList_triggered() +{ + qDebug() << "actionViewList"; +} + +void MainWindow::on_actionViewProfile_triggered() +{ + qDebug() << "actionViewProfile"; +} + +void MainWindow::on_actionViewInfo_triggered() +{ + qDebug() << "actionViewInfo"; +} + +void MainWindow::on_actionViewAll_triggered() +{ + qDebug() << "actionViewAll"; +} + +void MainWindow::on_actionPreviousDC_triggered() +{ + qDebug() << "actionPreviousDC"; +} + +void MainWindow::on_actionNextDC_triggered() +{ + qDebug() << "actionNextDC"; +} + +void MainWindow::on_actionSelectEvents_triggered() +{ + qDebug() << "actionSelectEvents"; +} + +void MainWindow::on_actionInputPlan_triggered() +{ + qDebug() << "actionInputPlan"; +} + +void MainWindow::on_actionAboutSubsurface_triggered() +{ + qDebug() << "actionAboutSubsurface"; +} + +void MainWindow::on_actionUserManual_triggered() +{ + qDebug() << "actionUserManual"; +} + + + +#include "mainwindow.moc" diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 6361870b2..51b428c5f 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -22,7 +22,41 @@ public: private Q_SLOTS: + /* file menu action */ void on_actionNew_triggered(); + void on_actionOpen_triggered(); + void on_actionSave_triggered(); + void on_actionSaveAs_triggered(); + void on_actionClose_triggered(); + void on_actionImport_triggered(); + void on_actionExportUDDF_triggered(); + void on_actionPrint_triggered(); + void on_actionPreferences_triggered(); + void on_actionQuit_triggered(); + + /* log menu actions */ + void on_actionDownloadDC_triggered(); + void on_actionDownloadWeb_triggered(); + void on_actionEditDeviceNames_triggered(); + void on_actionAddDive_triggered(); + void on_actionRenumber_triggered(); + void on_actionAutoGroup_triggered(); + void on_actionToggleZoom_triggered(); + void on_actionYearlyStatistics_triggered(); + + /* view menu actions */ + void on_actionViewList_triggered(); + void on_actionViewProfile_triggered(); + void on_actionViewInfo_triggered(); + void on_actionViewAll_triggered(); + void on_actionPreviousDC_triggered(); + void on_actionNextDC_triggered(); + + /* other menu actions */ + void on_actionSelectEvents_triggered(); + void on_actionInputPlan_triggered(); + void on_actionAboutSubsurface_triggered(); + void on_actionUserManual_triggered(); private: Ui::MainWindow *ui; diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index b99d10222..96751dc0c 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -45,7 +45,7 @@ 0 0 763 - 19 + 20 @@ -54,63 +54,63 @@ - - - + + + - - + + - + - + - + Log - - - + + + - + - - - + + + View - - - - - - + + + + + + Filter - + Planner - + Help - - + + @@ -135,7 +135,7 @@ Ctrl+O - + Save @@ -143,7 +143,7 @@ Ctrl+S - + Save as @@ -151,7 +151,7 @@ Ctrl+Shift+S - + Close @@ -159,7 +159,7 @@ Ctrl+W - + Import Files @@ -167,12 +167,12 @@ Ctrl+I - + Export UDDF - + Print @@ -180,12 +180,12 @@ Ctrl+P - + Preferences - + Quit @@ -193,22 +193,22 @@ Ctrl+Q - + Download from Dive computer - + Download from Web Service - + Edit Device Names - + Add Dive @@ -218,67 +218,67 @@ Renumber - + Auto Group - + Toggle Zoom - + Yearly Statistics - + - List + View List - + - Profile + View Profile - + - Info + View Info - + - Tree + View All - + Prev DC - + Next DC - + Select Events - + Input Plan - + About Subsurface - + User Manual From 55106a75831af9260858896118d576c5156dc5d2 Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Wed, 10 Apr 2013 08:27:38 +0200 Subject: [PATCH 021/226] Avoid C++ style streams As Thiago wrote: qDebug("actionNew") also works. The current policy is to avoid C++ style source code as much as possible. Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 9bcc03c75..502bd9774 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -11,139 +11,139 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()) void MainWindow::on_actionNew_triggered() { - qDebug() << "actionNew"; + qDebug("actionNew"); } void MainWindow::on_actionOpen_triggered() { - qDebug() << "actionOpen"; + qDebug("actionOpen"); } void MainWindow::on_actionSave_triggered() { - qDebug() << "actionSave"; + qDebug("actionSave"); } void MainWindow::on_actionSaveAs_triggered() { - qDebug() << "actionSaveAs"; + qDebug("actionSaveAs"); } void MainWindow::on_actionClose_triggered() { - qDebug() << "actionClose"; + qDebug("actionClose"); } void MainWindow::on_actionImport_triggered() { - qDebug() << "actionImport"; + qDebug("actionImport"); } void MainWindow::on_actionExportUDDF_triggered() { - qDebug() << "actionExportUDDF"; + qDebug("actionExportUDDF"); } void MainWindow::on_actionPrint_triggered() { - qDebug() << "actionPrint"; + qDebug("actionPrint"); } void MainWindow::on_actionPreferences_triggered() { - qDebug() << "actionPreferences"; + qDebug("actionPreferences"); } void MainWindow::on_actionQuit_triggered() { - qDebug() << "actionQuit"; + qDebug("actionQuit"); } void MainWindow::on_actionDownloadDC_triggered() { - qDebug() << "actionDownloadDC"; + qDebug("actionDownloadDC"); } void MainWindow::on_actionDownloadWeb_triggered() { - qDebug() << "actionDownloadWeb";} + qDebug("actionDownloadWeb");} void MainWindow::on_actionEditDeviceNames_triggered() { - qDebug() << "actionEditDeviceNames";} + qDebug("actionEditDeviceNames");} void MainWindow::on_actionAddDive_triggered() { - qDebug() << "actionAddDive"; + qDebug("actionAddDive"); } void MainWindow::on_actionRenumber_triggered() { - qDebug() << "actionRenumber"; + qDebug("actionRenumber"); } void MainWindow::on_actionAutoGroup_triggered() { - qDebug() << "actionAutoGroup"; + qDebug("actionAutoGroup"); } void MainWindow::on_actionToggleZoom_triggered() { - qDebug() << "actionToggleZoom"; + qDebug("actionToggleZoom"); } void MainWindow::on_actionYearlyStatistics_triggered() { - qDebug() << "actionYearlyStatistics"; + qDebug("actionYearlyStatistics"); } void MainWindow::on_actionViewList_triggered() { - qDebug() << "actionViewList"; + qDebug("actionViewList"); } void MainWindow::on_actionViewProfile_triggered() { - qDebug() << "actionViewProfile"; + qDebug("actionViewProfile"); } void MainWindow::on_actionViewInfo_triggered() { - qDebug() << "actionViewInfo"; + qDebug("actionViewInfo"); } void MainWindow::on_actionViewAll_triggered() { - qDebug() << "actionViewAll"; + qDebug("actionViewAll"); } void MainWindow::on_actionPreviousDC_triggered() { - qDebug() << "actionPreviousDC"; + qDebug("actionPreviousDC"); } void MainWindow::on_actionNextDC_triggered() { - qDebug() << "actionNextDC"; + qDebug("actionNextDC"); } void MainWindow::on_actionSelectEvents_triggered() { - qDebug() << "actionSelectEvents"; + qDebug("actionSelectEvents"); } void MainWindow::on_actionInputPlan_triggered() { - qDebug() << "actionInputPlan"; + qDebug("actionInputPlan"); } void MainWindow::on_actionAboutSubsurface_triggered() { - qDebug() << "actionAboutSubsurface"; + qDebug("actionAboutSubsurface"); } void MainWindow::on_actionUserManual_triggered() { - qDebug() << "actionUserManual"; + qDebug("actionUserManual"); } From 6e02e9d5b7857d78a4f941b49bf884b1b96a58fe Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Wed, 10 Apr 2013 08:07:02 +0100 Subject: [PATCH 022/226] Add logic to switch views Rename the 3 main widgets in the Qt mainwindow. Wire view menu options to the setVisible methods of same. Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 16 ++++++++++++++++ qt-ui/mainwindow.ui | 6 +++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 502bd9774..36b9798ee 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -99,21 +99,37 @@ void MainWindow::on_actionYearlyStatistics_triggered() void MainWindow::on_actionViewList_triggered() { qDebug("actionViewList"); + + ui->InfoWidget->setVisible(false); + ui->ListWidget->setVisible(true); + ui->ProfileWidget->setVisible(false); } void MainWindow::on_actionViewProfile_triggered() { qDebug("actionViewProfile"); + + ui->InfoWidget->setVisible(false); + ui->ListWidget->setVisible(false); + ui->ProfileWidget->setVisible(true); } void MainWindow::on_actionViewInfo_triggered() { qDebug("actionViewInfo"); + + ui->InfoWidget->setVisible(true); + ui->ListWidget->setVisible(false); + ui->ProfileWidget->setVisible(false); } void MainWindow::on_actionViewAll_triggered() { qDebug("actionViewAll"); + + ui->InfoWidget->setVisible(true); + ui->ListWidget->setVisible(true); + ui->ProfileWidget->setVisible(true); } void MainWindow::on_actionPreviousDC_triggered() diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 96751dc0c..65b18c757 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -24,10 +24,10 @@ Qt::Horizontal - - + + - + From ec8c2b9368f89bd32808f8f7570394479c11e3d2 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Wed, 10 Apr 2013 19:00:20 +0400 Subject: [PATCH 023/226] MainWindow: remove useless include This was accidentally reintroduced by a previous commit, and was causing a moc warning. Signed-off-by: Alberto Mardegan --- qt-ui/mainwindow.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 36b9798ee..56c39015b 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -161,7 +161,3 @@ void MainWindow::on_actionUserManual_triggered() { qDebug("actionUserManual"); } - - - -#include "mainwindow.moc" From 9dbda9f62aca314aa7cb7ef588b1ce5ec43a01b9 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Wed, 10 Apr 2013 19:02:06 +0400 Subject: [PATCH 024/226] Update .gitignore Ignore some moc/uic-generated files. Signed-off-by: Alberto Mardegan --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 36c82543b..54650bcf2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.rej *.exe *.dmg +*.moc *.moc.cpp *.patch *.ui.h @@ -11,6 +12,7 @@ version.h !dives/*.xml *~ po/*.mo +qt-ui/ui_*.h /subsurface .dep/ share/ From 76f71b4ca07dbef1069aec3db1138c199927b1bb Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Fri, 12 Apr 2013 08:24:07 +0100 Subject: [PATCH 025/226] Add dive list view to main window Add files for dive list model/view implementation. Replace TableView with the custom list view. Amendments to makefile to match. Note: we don't yet handle trips and may want to add additional columns to describe the dive. A single, dummy dive is added to show how this works (get root; item is child of root). Purely to illustrate - needs proper integration etc. Amend member names for dive list view components Various naming changes to conform to coding style. Required changes to members (remove prefix) and methods (avoid clash with members). Clean up indentation (swap spaces for tabs). Code for model/view was written with a different editor which had different settings :-/ [Dirk Hohndel: minor whitespace cleanup] Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- Makefile | 2 +- qt-ui/divelistview.cpp | 5 ++ qt-ui/divelistview.h | 19 ++++++ qt-ui/divetripmodel.cpp | 136 ++++++++++++++++++++++++++++++++++++++++ qt-ui/divetripmodel.h | 80 +++++++++++++++++++++++ qt-ui/mainwindow.cpp | 29 +++++++++ qt-ui/mainwindow.h | 2 + qt-ui/mainwindow.ui | 7 ++- 8 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 qt-ui/divelistview.cpp create mode 100644 qt-ui/divelistview.h create mode 100644 qt-ui/divetripmodel.cpp create mode 100644 qt-ui/divetripmodel.h diff --git a/Makefile b/Makefile index c89816de5..9e8e69b2d 100644 --- a/Makefile +++ b/Makefile @@ -183,7 +183,7 @@ MSGLANGS=$(notdir $(wildcard po/*.po)) MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) -QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o +QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o qt-ui/divetripmodel.o OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ planner.o planner-gtk.o \ diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp new file mode 100644 index 000000000..eafbdd384 --- /dev/null +++ b/qt-ui/divelistview.cpp @@ -0,0 +1,5 @@ +#include "divelistview.h" + +DiveListView::DiveListView(QWidget *parent) : QTreeView(parent) +{ +} diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h new file mode 100644 index 000000000..3ac123a14 --- /dev/null +++ b/qt-ui/divelistview.h @@ -0,0 +1,19 @@ +#ifndef DIVELISTVIEW_H +#define DIVELISTVIEW_H + +/*! A view subclass for use with dives + + Note: calling this a list view might be misleading? + + +*/ + +#include + +class DiveListView : public QTreeView +{ +public: + DiveListView(QWidget *parent = 0); +}; + +#endif // DIVELISTVIEW_H diff --git a/qt-ui/divetripmodel.cpp b/qt-ui/divetripmodel.cpp new file mode 100644 index 000000000..a03603bcf --- /dev/null +++ b/qt-ui/divetripmodel.cpp @@ -0,0 +1,136 @@ +#include "divetripmodel.h" + + +DiveItem::DiveItem(int num, QString dt, float dur, float dep, QString loc, DiveItem *p): + number(num), dateTime(dt), duration(dur), depth(dep), location(loc), parentItem(p) +{ + if (parentItem) + parentItem->addChild(this); +} + + +DiveTripModel::DiveTripModel(const QString &filename, QObject *parent) : QAbstractItemModel(parent), filename(filename) +{ + rootItem = new DiveItem; +} + + +Qt::ItemFlags DiveTripModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags diveFlags = QAbstractItemModel::flags(index); + if (index.isValid()) { + diveFlags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled; + } + return diveFlags; +} + + +QVariant DiveTripModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role != Qt::DisplayRole) + return QVariant(); + + DiveItem *item = static_cast(index.internalPointer()); + + QVariant retVal; + switch (index.column()) { + case DIVE_NUMBER: + retVal = QVariant(item->diveNumber()); + break; + case DIVE_DATE_TIME: + retVal = QVariant(item->diveDateTime()); + break; + case DIVE_DURATION: + retVal = QVariant(item->diveDuration()); + break; + case DIVE_DEPTH: + retVal = QVariant(item->diveDepth()); + break; + case DIVE_LOCATION: + retVal = QVariant(item->diveLocation()); + break; + default: + return QVariant(); + }; + return retVal; +} + + +QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + if (section == DIVE_NUMBER) { + return tr("Dive number"); + } else if (section == DIVE_DATE_TIME) { + return tr("Date"); + } else if (section == DIVE_DURATION) { + return tr("Duration"); + } else if (section == DIVE_DEPTH) { + return tr("Depth"); + } else if (section == DIVE_LOCATION) { + return tr("Location"); + } + } + return QVariant(); +} + +int DiveTripModel::rowCount(const QModelIndex &parent) const +{ + /* only allow kids in column 0 */ + if (parent.isValid() && parent.column() > 0){ + return 0; + } + DiveItem *item = itemForIndex(parent); + return item ? item->childCount() : 0; +} + + + +int DiveTripModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() && parent.column() != 0 ? 0 : COLUMNS; +} + + +QModelIndex DiveTripModel::index(int row, int column, const QModelIndex &parent) const +{ + + if (!rootItem || row < 0 || column < 0 || column >= COLUMNS || + (parent.isValid() && parent.column() != 0)) + return QModelIndex(); + + DiveItem *parentItem = itemForIndex(parent); + Q_ASSERT(parentItem); + if (DiveItem *item = parentItem->childAt(row)){ + return createIndex(row, column, item); + } + return QModelIndex(); +} + + +QModelIndex DiveTripModel::parent(const QModelIndex &childIndex) const +{ + if (!childIndex.isValid()) + return QModelIndex(); + + DiveItem *child = static_cast(childIndex.internalPointer()); + DiveItem *parent = child->parent(); + + if (parent == rootItem) + return QModelIndex(); + + return createIndex(parent->rowOfChild(child), 0, parent); +} + + +DiveItem* DiveTripModel::itemForIndex(const QModelIndex &index) const +{ + if (index.isValid()) { + DiveItem *item = static_cast(index.internalPointer()); + return item; + } + return rootItem; +} diff --git a/qt-ui/divetripmodel.h b/qt-ui/divetripmodel.h new file mode 100644 index 000000000..8c8a829e2 --- /dev/null +++ b/qt-ui/divetripmodel.h @@ -0,0 +1,80 @@ +#ifndef DIVETRIPMODEL_H +#define DIVETRIPMODEL_H + +#include + +/*! A DiveItem for use with a DiveTripModel + * + * A simple class which wraps basic stats for a dive (e.g. duration, depth) and + * tidies up after it's children. This is done manually as we don't inherit from + * QObject. + * +*/ +class DiveItem +{ +public: + explicit DiveItem(): number(0), dateTime(QString()), duration(0.0), depth(0.0), location(QString()) {parentItem = 0;} + explicit DiveItem(int num, QString dt, float, float, QString loc, DiveItem *parent = 0); + ~DiveItem() { qDeleteAll(childlist); } + + int diveNumber() const { return number; } + QString diveDateTime() const { return dateTime; } + float diveDuration() const { return duration; } + float diveDepth() const { return depth; } + QString diveLocation() const { return location; } + + DiveItem *parent() const { return parentItem; } + DiveItem *childAt(int row) const { return childlist.value(row); } + int rowOfChild(DiveItem *child) const { return childlist.indexOf(child); } + int childCount() const { return childlist.count(); } + bool hasChildren() const { return !childlist.isEmpty(); } + QList children() const { return childlist; } + void addChild(DiveItem* item) { item->parentItem = this; childlist << item; } /* parent = self */ + + +private: + + int number; + QString dateTime; + float duration; + float depth; + QString location; + + DiveItem *parentItem; + QList childlist; + +}; + + +enum Column {DIVE_NUMBER, DIVE_DATE_TIME, DIVE_DURATION, DIVE_DEPTH, DIVE_LOCATION, COLUMNS}; + + +/*! An AbstractItemModel for recording dive trip information such as a list of dives. +* +*/ +class DiveTripModel : public QAbstractItemModel +{ +public: + + DiveTripModel(const QString &filename, QObject *parent = 0); + + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + int rowCount(const QModelIndex &parent) const; + + int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &child) const; + + DiveItem *itemForIndex(const QModelIndex &) const; + +private: + + DiveItem *rootItem; + QString filename; + +}; + +#endif // DIVETRIPMODEL_H diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 56c39015b..03f6b6ca6 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -4,9 +4,38 @@ #include #include +#include "divelistview.h" +#include "divetripmodel.h" + MainWindow::MainWindow() : ui(new Ui::MainWindow()) { ui->setupUi(this); + + /* may want to change ctor to avoid filename as 1st param. + * here we just use an empty string + */ + model = new DiveTripModel("",this); + if (model){ + ui->ListWidget->setModel(model); + } + /* add in dives here. + * we need to root to parent all top level dives + * trips need more work as it complicates parent/child stuff. + * + * We show how to obtain the root and add a demo dive + * + * Todo: work through integration with current list of dives/trips + * Todo: look at alignment/format of e.g. duration in view + */ + DiveItem *dive = 0; + DiveItem *root = model->itemForIndex(QModelIndex()); + if (root){ + dive = new DiveItem(1,QString("01/03/13"),14.2, 29.0,QString("Wraysbury"),root); + + Q_UNUSED(dive) + } + + } void MainWindow::on_actionNew_triggered() diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 51b428c5f..662d07cc5 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -3,6 +3,7 @@ #include +class DiveTripModel; namespace Ui { @@ -60,6 +61,7 @@ private Q_SLOTS: private: Ui::MainWindow *ui; + DiveTripModel *model; }; #endif diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 65b18c757..6ece13f57 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -27,7 +27,7 @@ - + @@ -291,6 +291,11 @@
maintab.h
1 + + DiveListView + QTreeView +
divelistview.h
+
From 92397a2bad3d1a2bc261dee67d230e3caa13b8d8 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 13 Apr 2013 10:17:59 -0300 Subject: [PATCH 026/226] Started the real code on the Qt Interface. 1 - Open File already open files, it tries to not break the Gtk version, but some methods on the GTK version still need to be called inside Qt because the code is too tight-coupled. 2 - Close file already close files, same comments for the open file dialog applies here. 3 - The code for adding new cylinders in the cylinder dialog is done, already works and it's integrated with the system. There's a need to implement the edit and delete now, but it will be easyer since I'm starting to not get lost on the code. 4 - Some functions that were used to convert unities have been moved to convert.h ( can be changed later, put there because it's easyer to find something that converts in a convert.h =p ) because they were static functions that operated in the GTK version but I need those functions in the Qt version too. [Dirk Hohndel: lots and lots of whitespace and coding style changes] Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- Makefile | 6 +- conversions.h | 16 ++ equipment.c | 6 +- qt-gui.cpp | 8 + qt-ui/addcylinderdialog.cpp | 48 ++++++ qt-ui/addcylinderdialog.h | 24 +++ qt-ui/addcylinderdialog.ui | 303 ++++++++++++++++++++++++++++++++++++ qt-ui/maintab.cpp | 68 +++++++- qt-ui/maintab.h | 17 +- qt-ui/maintab.ui | 12 +- qt-ui/mainwindow.cpp | 107 ++++++++++++- qt-ui/mainwindow.h | 3 + qt-ui/models.cpp | 175 +++++++++++++++++++++ qt-ui/models.h | 44 ++++++ 14 files changed, 823 insertions(+), 14 deletions(-) create mode 100644 conversions.h create mode 100644 qt-ui/addcylinderdialog.cpp create mode 100644 qt-ui/addcylinderdialog.h create mode 100644 qt-ui/addcylinderdialog.ui create mode 100644 qt-ui/models.cpp create mode 100644 qt-ui/models.h diff --git a/Makefile b/Makefile index 9e8e69b2d..81e3648c9 100644 --- a/Makefile +++ b/Makefile @@ -177,13 +177,15 @@ ifneq ($(strip $(LIBXSLT)),) XSLT=-DXSLT='"$(XSLTDIR)"' endif -LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) +LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ + $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) MSGLANGS=$(notdir $(wildcard po/*.po)) MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) -QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o qt-ui/divetripmodel.o +QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ + qt-ui/divetripmodel.o qt-ui/addcylinderdialog.o qt-ui/models.o OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ planner.o planner-gtk.o \ diff --git a/conversions.h b/conversions.h new file mode 100644 index 000000000..f59fcc96b --- /dev/null +++ b/conversions.h @@ -0,0 +1,16 @@ +/* + * conversions.h + * + * Helpers to convert between different units + * + */ +#ifdef __cplusplus +extern "C" { +#endif + +void convert_volume_pressure(int ml, int mbar, double *v, double *p); +int convert_pressure(int mbar, double *p); + +#ifdef __cplusplus +} +#endif diff --git a/equipment.c b/equipment.c index 2d90f7a52..a1d156f94 100644 --- a/equipment.c +++ b/equipment.c @@ -18,6 +18,7 @@ #include "display.h" #include "display-gtk.h" #include "divelist.h" +#include "conversions.h" static GtkListStore *cylinder_model, *weightsystem_model; @@ -70,7 +71,7 @@ struct ws_widget { }; /* we want bar - so let's not use our unit functions */ -static int convert_pressure(int mbar, double *p) +int convert_pressure(int mbar, double *p) { int decimals = 1; double pressure; @@ -86,7 +87,7 @@ static int convert_pressure(int mbar, double *p) return decimals; } -static void convert_volume_pressure(int ml, int mbar, double *v, double *p) +void convert_volume_pressure(int ml, int mbar, double *v, double *p) { double volume, pressure; @@ -724,6 +725,7 @@ static void fill_cylinder_info(struct cylinder_widget *cylinder, cylinder_t *cyl /* * Also, insert it into the model if it doesn't already exist */ + // WARNING: GTK-Specific Code. add_cylinder(cylinder, desc, ml, mbar); } diff --git a/qt-gui.cpp b/qt-gui.cpp index e667e97bf..745457763 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -126,6 +126,7 @@ static void on_info_bar_response(GtkWidget *widget, gint response, void report_error(GError* error) { + qDebug("Warning: Calling GTK-Specific Code."); if (error == NULL) { return; @@ -253,6 +254,8 @@ static void file_save(GtkWidget *w, gpointer data) static gboolean ask_save_changes() { + //WARNING: Porting to Qt + qDebug("This method is being ported to Qt, please, stop using it. "); GtkWidget *dialog, *label, *content; gboolean quit = TRUE; dialog = gtk_dialog_new_with_buttons(_("Save Changes?"), @@ -291,6 +294,7 @@ static gboolean ask_save_changes() static void file_close(GtkWidget *w, gpointer data) { + qDebug("Calling an already ported-to-qt Gtk method"); if (unsaved_changes()) if (ask_save_changes() == FALSE) return; @@ -319,8 +323,12 @@ static void file_close(GtkWidget *w, gpointer data) show_dive_info(NULL); } +//##################################################################### +//###### ALREAADY PORTED TO Qt. DELETE ME WHEN NOT MORE USERFUL. # +//##################################################################### static void file_open(GtkWidget *w, gpointer data) { + qDebug("Calling an already ported-to-qt Gtk method."); GtkWidget *dialog; GtkFileFilter *filter; const char *current_default; diff --git a/qt-ui/addcylinderdialog.cpp b/qt-ui/addcylinderdialog.cpp new file mode 100644 index 000000000..6f2294a25 --- /dev/null +++ b/qt-ui/addcylinderdialog.cpp @@ -0,0 +1,48 @@ +#include "addcylinderdialog.h" +#include "ui_addcylinderdialog.h" +#include +#include +#include "../conversions.h" + + +AddCylinderDialog::AddCylinderDialog(QWidget *parent) : ui(new Ui::AddCylinderDialog()) +{ + ui->setupUi(this); +} + +void AddCylinderDialog::setCylinder(cylinder_t *cylinder) +{ + double volume, pressure; + int index; + + currentCylinder = cylinder; + convert_volume_pressure(cylinder->type.size.mliter, cylinder->type.workingpressure.mbar, &volume, &pressure); + + index = ui->cylinderType->findText(QString(cylinder->type.description)); + ui->cylinderType->setCurrentIndex(index); + ui->size->setValue(volume); + ui->pressure->setValue(pressure); + + ui->o2percent->setValue(cylinder->gasmix.o2.permille / 10.0); + ui->hepercent->setValue(cylinder->gasmix.he.permille / 10.0); + + convert_pressure(cylinder->start.mbar, &pressure); + ui->start->setValue(pressure); + + convert_pressure(cylinder->end.mbar, &pressure); + ui->end->setValue(pressure); +} + +void AddCylinderDialog::updateCylinder() +{ + QByteArray description = ui->cylinderType->currentText().toLocal8Bit(); + + currentCylinder->type.description = description.data(); + currentCylinder->type.size.mliter = ui->size->value(); + currentCylinder->type.workingpressure.mbar = ui->pressure->value(); + currentCylinder->gasmix.o2.permille = ui->o2percent->value(); + currentCylinder->gasmix.he.permille = ui->hepercent->value(); + currentCylinder->start.mbar = ui->start->value(); + currentCylinder->end.mbar = ui->end->value(); +} + diff --git a/qt-ui/addcylinderdialog.h b/qt-ui/addcylinderdialog.h new file mode 100644 index 000000000..b32494c05 --- /dev/null +++ b/qt-ui/addcylinderdialog.h @@ -0,0 +1,24 @@ +#ifndef ADDCYLINDERDIALOG_H +#define ADDCYLINDERDIALOG_H + +#include +#include "../dive.h" + +namespace Ui{ + class AddCylinderDialog; +} + +class AddCylinderDialog : public QDialog{ + Q_OBJECT +public: + explicit AddCylinderDialog(QWidget* parent = 0); + void setCylinder(cylinder_t *cylinder); + void updateCylinder(); + +private: + Ui::AddCylinderDialog *ui; + cylinder_t *currentCylinder; +}; + + +#endif diff --git a/qt-ui/addcylinderdialog.ui b/qt-ui/addcylinderdialog.ui new file mode 100644 index 000000000..8bb0428b7 --- /dev/null +++ b/qt-ui/addcylinderdialog.ui @@ -0,0 +1,303 @@ + + + AddCylinderDialog + + + + 0 + 0 + 408 + 298 + + + + Dialog + + + + + + Cylinder + + + + QFormLayout::ExpandingFieldsGrow + + + + + Type + + + + + + + + 0 + 0 + + + + + + + + Size + + + + + + + + 0 + 0 + + + + + + + + Pressure + + + + + + + + 0 + 0 + + + + + + + + + + + Pressure + + + + + + Start + + + + + + + End + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + Gas Mix + + + + + + O2% + + + + + + + false + + + + 0 + 0 + + + + + + + + He% + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AddCylinderDialog + accept() + + + 248 + 269 + + + 157 + 260 + + + + + buttonBox + rejected() + AddCylinderDialog + reject() + + + 290 + 269 + + + 286 + 260 + + + + + checkBox + clicked(bool) + start + setEnabled(bool) + + + 216 + 46 + + + 280 + 66 + + + + + checkBox + clicked(bool) + end + setEnabled(bool) + + + 226 + 48 + + + 268 + 100 + + + + + checkBox_2 + clicked(bool) + o2percent + setEnabled(bool) + + + 214 + 165 + + + 260 + 190 + + + + + checkBox_2 + clicked(bool) + hepercent + setEnabled(bool) + + + 228 + 165 + + + 262 + 216 + + + + + diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index c630965d2..b957fd1c7 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -1,8 +1,74 @@ #include "maintab.h" #include "ui_maintab.h" +#include "addcylinderdialog.h" + +#include MainTab::MainTab(QWidget *parent) : QTabWidget(parent), - ui(new Ui::MainTab()) + ui(new Ui::MainTab()), + weightModel(new WeightModel()), + cylindersModel(new CylindersModel()) { ui->setupUi(this); + ui->cylinders->setModel(cylindersModel); + ui->weights->setModel(weightModel); +} + +void MainTab::clearEquipment() +{ +} + +void MainTab::clearInfo() +{ + QList labels; + labels << ui->sac << ui->otu << ui->oxygenhelium << ui->gasused + << ui->date << ui->divetime << ui->surfinterval + << ui->maxdepth << ui->avgdepth << ui->visibility + << ui->watertemperature << ui->airtemperature << ui->airpress; + + Q_FOREACH(QLabel *l, labels){ + l->setText(QString()); + } +} + +void MainTab::clearStats() +{ + QList labels; + labels << ui->maxdepth_2 << ui->mindepth << ui->avgdepth + << ui->maxsac << ui->minsac << ui->avgsac + << ui->dives << ui->maxtemp << ui->mintemp << ui->avgtemp + << ui->totaltime << ui->avgtime << ui->longestdive << ui->shortestdive; + + Q_FOREACH(QLabel *l, labels){ + l->setText(QString()); + } +} + +void MainTab::on_addCylinder_clicked() +{ + AddCylinderDialog dialog(this); + cylinder_t *newCylinder = (cylinder_t*) malloc(sizeof(cylinder_t)); + newCylinder->type.description = ""; + + dialog.setCylinder(newCylinder); + int result = dialog.exec(); + if (result == QDialog::Rejected){ + return; + } + + dialog.updateCylinder(); + cylindersModel->add(newCylinder); +} + +void MainTab::on_editCylinder_clicked() +{ +} + +void MainTab::on_delCylinder_clicked() +{ +} + +void MainTab::reload() +{ + cylindersModel->update(); } diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h index 40904ab12..0e9f285ac 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -2,6 +2,9 @@ #define MAINTAB_H #include +#include + +#include "models.h" namespace Ui { @@ -13,8 +16,20 @@ class MainTab : public QTabWidget Q_OBJECT public: MainTab(QWidget *parent); + void clearStats(); + void clearInfo(); + void clearEquipment(); + void reload(); + +public Q_SLOTS: + void on_addCylinder_clicked(); + void on_editCylinder_clicked(); + void on_delCylinder_clicked(); + private: Ui::MainTab *ui; + WeightModel *weightModel; + CylindersModel *cylindersModel; }; -#endif \ No newline at end of file +#endif diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index 7432dc466..0f46d91a5 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -14,7 +14,7 @@ TabWidget - 0 + 2 @@ -390,19 +390,19 @@ - + - + Edit - + Add @@ -422,7 +422,7 @@ - + Delete @@ -438,7 +438,7 @@ - + diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 03f6b6ca6..fdc823d9c 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -2,11 +2,19 @@ #include "ui_mainwindow.h" #include +#include +#include #include #include "divelistview.h" #include "divetripmodel.h" +#include "glib.h" +#include "../dive.h" +#include "../divelist.h" +#include "../pref.h" + + MainWindow::MainWindow() : ui(new Ui::MainWindow()) { ui->setupUi(this); @@ -45,7 +53,31 @@ void MainWindow::on_actionNew_triggered() void MainWindow::on_actionOpen_triggered() { - qDebug("actionOpen"); + QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::homePath(), filter()); + if (filename.isEmpty()){ + return; + } + + // Needed to convert to char* + QByteArray fileNamePtr = filename.toLocal8Bit(); + + on_actionClose_triggered(); + + GError *error = NULL; + parse_file(fileNamePtr.data(), &error); + set_filename(fileNamePtr.data(), TRUE); + + if (error != NULL) + { + QMessageBox::warning(this, "Error", error->message); + g_error_free(error); + error = NULL; + } + + //WARNING: Port This method to Qt + report_dives(FALSE, FALSE); + + ui->InfoWidget->reload(); } void MainWindow::on_actionSave_triggered() @@ -59,7 +91,37 @@ void MainWindow::on_actionSaveAs_triggered() } void MainWindow::on_actionClose_triggered() { - qDebug("actionClose"); + if (unsaved_changes() && (askSaveChanges() == FALSE)) + { + return; + } + + /* free the dives and trips */ + while (dive_table.nr) + { + delete_single_dive(0); + } + mark_divelist_changed(FALSE); + + /* clear the selection and the statistics */ + selected_dive = 0; + + //WARNING: Port this to Qt. + //process_selected_dives(); + + ui->InfoWidget->clearStats(); + ui->InfoWidget->clearInfo(); + ui->InfoWidget->clearEquipment(); + + clear_events(); + show_dive_stats(NULL); + + /* redraw the screen */ + //WARNING: Port this to Qt. + dive_list_update_dives(); + + // WARNING? Port this to Qt. + show_dive_info(NULL); } void MainWindow::on_actionImport_triggered() @@ -190,3 +252,44 @@ void MainWindow::on_actionUserManual_triggered() { qDebug("actionUserManual"); } + +QString MainWindow::filter() +{ + QString f; + f += "ALL ( *.xml *.XML *.uddf *.udcf *.UDFC *.jlb *.JLB "; +#ifdef LIBZIP + f += "*.sde *.SDE *.dld *.DLD "; +#endif +#ifdef SQLITE3 + f += "*.db"; +#endif + f += ");;"; + + f += "XML (*.xml *.XML);;"; + f += "UDDF (*.uddf);;"; + f += "UDCF (*.udcf *.UDCF);;"; + f += "JLB (*.jlb *.JLB);;"; + +#ifdef LIBZIP + f += "SDE (*.sde *.SDE);;"; + f += "DLD (*.dld *.DLD);;"; +#endif +#ifdef SQLITE3 + f += "DB (*.db)"; +#endif + + return f; +} + +bool MainWindow::askSaveChanges() +{ + QString message = ! existing_filename ? tr("You have unsaved changes\nWould you like to save those before closing the datafile?") + : tr("You have unsaved changes to file: %1 \nWould you like to save those before closing the datafile?").arg(existing_filename); + + if (QMessageBox::question(this, tr("Save Changes?"), message) == QMessageBox::Ok){ + // WARNING: Port. + // file_save(NULL,NULL); + return true; + } + return false; +} diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 662d07cc5..34acf2b67 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -62,6 +62,9 @@ private Q_SLOTS: private: Ui::MainWindow *ui; DiveTripModel *model; + QString filter(); + bool askSaveChanges(); + }; #endif diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp new file mode 100644 index 000000000..dac1c7215 --- /dev/null +++ b/qt-ui/models.cpp @@ -0,0 +1,175 @@ +#include "models.h" +#include "../dive.h" + +CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent) +{ +} + +QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant ret; + if (orientation == Qt::Vertical) { + return ret; + } + + if (role == Qt::DisplayRole) { + switch(section) { + case TYPE: + ret = tr("Type"); + break; + case SIZE: + ret = tr("Size"); + break; + case MAXPRESS: + ret = tr("MaxPress"); + break; + case START: + ret = tr("Start"); + break; + case END: + ret = tr("End"); + break; + case O2: + ret = tr("O2%"); + break; + case HE: + ret = tr("He%"); + break; + } + } + return ret; +} + +int CylindersModel::columnCount(const QModelIndex& parent) const +{ + return 7; +} + +QVariant CylindersModel::data(const QModelIndex& index, int role) const +{ + QVariant ret; + if (!index.isValid() || index.row() >= MAX_CYLINDERS) { + return ret; + } + + dive *d = get_dive(selected_dive); + cylinder_t& cyl = d->cylinder[index.row()]; + + if (role == Qt::DisplayRole) { + switch(index.column()) { + case TYPE: + ret = QString(cyl.type.description); + break; + case SIZE: + ret = cyl.type.size.mliter; + break; + case MAXPRESS: + ret = cyl.type.workingpressure.mbar; + break; + case START: + ret = cyl.start.mbar; + break; + case END: + ret = cyl.end.mbar; + break; + case O2: + ret = cyl.gasmix.o2.permille; + break; + case HE: + ret = cyl.gasmix.he.permille; + break; + } + } + return ret; +} + +int CylindersModel::rowCount(const QModelIndex& parent) const +{ + return usedRows[currentDive]; +} + +void CylindersModel::add(cylinder_t* cyl) +{ + if (usedRows[currentDive] >= MAX_CYLINDERS) { + free(cyl); + } + + int row = usedRows[currentDive]; + + cylinder_t& cylinder = currentDive->cylinder[row]; + + cylinder.end.mbar = cyl->end.mbar; + cylinder.start.mbar = cyl->start.mbar; + + beginInsertRows(QModelIndex(), row, row); + usedRows[currentDive]++; + endInsertRows(); +} + +void CylindersModel::update() +{ + if (usedRows[currentDive] > 0) { + beginRemoveRows(QModelIndex(), 0, usedRows[currentDive]-1); + endRemoveRows(); + } + + currentDive = get_dive(selected_dive); + if (usedRows[currentDive] > 0) { + beginInsertRows(QModelIndex(), 0, usedRows[currentDive]-1); + endInsertRows(); + } +} + +void CylindersModel::clear() +{ + if (usedRows[currentDive] > 0) { + beginRemoveRows(QModelIndex(), 0, usedRows[currentDive]-1); + usedRows[currentDive] = 0; + endRemoveRows(); + } +} + +void WeightModel::clear() +{ +} + +int WeightModel::columnCount(const QModelIndex& parent) const +{ + return 0; +} + +QVariant WeightModel::data(const QModelIndex& index, int role) const +{ + return QVariant(); +} + +int WeightModel::rowCount(const QModelIndex& parent) const +{ + return rows; +} + +QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant ret; + if (orientation == Qt::Vertical) { + return ret; + } + + switch(section){ + case TYPE: + ret = tr("Type"); + break; + case WEIGHT: + ret = tr("Weight"); + break; + } + return ret; +} + +void WeightModel::add(weight_t* weight) +{ +} + +void WeightModel::update() +{ +} diff --git a/qt-ui/models.h b/qt-ui/models.h new file mode 100644 index 000000000..0d0c7b41d --- /dev/null +++ b/qt-ui/models.h @@ -0,0 +1,44 @@ +#ifndef MODELS_H +#define MODELS_H + +#include +#include "../dive.h" + +class CylindersModel : public QAbstractTableModel { +Q_OBJECT +public: + enum {TYPE, SIZE, MAXPRESS, START, END, O2, HE}; + + explicit CylindersModel(QObject* parent = 0); + /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + /*reimp*/ int columnCount(const QModelIndex& parent = QModelIndex()) const; + /*reimp*/ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const; + + void add(cylinder_t *cyl); + void clear(); + void update(); +private: + dive *currentDive; + + /* Since the dive doesn`t stores the number of cylinders that + * it has ( max 8 ) and since I don`t want to make a + * model-for-each-dive, let`s hack this here instead. */ + QMap usedRows; +}; + +class WeightModel : public QAbstractTableModel { + enum{TYPE, WEIGHT}; + /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + /*reimp*/ int columnCount(const QModelIndex& parent = QModelIndex()) const; + /*reimp*/ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const; + + void add(weight_t *weight); + void clear(); + void update(); +private: + int rows; +}; + +#endif From 14e133321f4ac2cff9eb0b84ba9687e5d42e3e46 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 18:11:57 -0700 Subject: [PATCH 027/226] Remove the mandatory -fPIC from CXXFLAGS This isn't necessary. There's code below adding -fPIE when necessary only. Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 81e3648c9..8bbd26fb7 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ VERSION=3.0.2 CC=gcc CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE CXX=g++ -CXXFLAGS=-Wall -g $(CLCFLAGS) -fPIC -DQT_NO_KEYWORDS +CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS INSTALL=install PKGCONFIG=pkg-config XML2CONFIG=xml2-config From f5c958ad73db696e473aaa35e144f4c9d8ae24de Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 13 Apr 2013 20:44:02 -0700 Subject: [PATCH 028/226] Add Qtr_ macros that uses gettext in a tr() compatible manner This should wrap gettext nicely and replace the "_()" macros we use in C code. Also added comments to the top of all the new files. Suggested-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- Makefile | 6 ++++-- qt-gui.cpp | 5 +++-- qt-ui/addcylinderdialog.cpp | 6 ++++++ qt-ui/addcylinderdialog.h | 6 ++++++ qt-ui/divelistview.cpp | 6 ++++++ qt-ui/divelistview.h | 6 ++++++ qt-ui/divetripmodel.cpp | 17 ++++++++++++----- qt-ui/divetripmodel.h | 6 ++++++ qt-ui/maintab.cpp | 7 +++++++ qt-ui/maintab.h | 6 ++++++ qt-ui/mainwindow.cpp | 15 +++++++++++---- qt-ui/mainwindow.h | 6 ++++++ qt-ui/models.cpp | 25 ++++++++++++++++--------- qt-ui/models.h | 6 ++++++ qt-ui/plotareascene.cpp | 6 ++++++ qt-ui/plotareascene.h | 6 ++++++ 16 files changed, 113 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 8bbd26fb7..e33d92053 100644 --- a/Makefile +++ b/Makefile @@ -110,8 +110,10 @@ else QT_MODULES = QtGui QT_CORE = QtCore endif + +# we need GLIB2CFLAGS for gettext +QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) $(GLIB2CFLAGS) LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) -QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES) @@ -303,7 +305,7 @@ $(INFOPLIST): $(INFOPLISTINPUT) # Transifex merge the translations update-po-files: - xgettext -o po/subsurface-new.pot -s -k_ -kN_ --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c + xgettext -o po/subsurface-new.pot -s -k_ -kN_ -kQtr_ --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c qt-ui/*.cpp tx push -s tx pull -af diff --git a/qt-gui.cpp b/qt-gui.cpp index 745457763..1621317c2 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -25,6 +25,7 @@ #include "version.h" #include "libdivecomputer.h" #include "qt-ui/mainwindow.h" +#include "qt-ui/common.h" #include #include @@ -1784,7 +1785,7 @@ void MainWindow::setCurrentFileName(const QString &fileName) if (fileName == m_currentFileName) return; m_currentFileName = fileName; - QString title = tr("Subsurface"); + QString title = Qtr_("Subsurface"); if (!m_currentFileName.isEmpty()) { QFileInfo fileInfo(m_currentFileName); title += " - " + fileInfo.fileName(); @@ -1797,7 +1798,7 @@ void MainWindow::on_actionOpen_triggered() QString defaultFileName = QString::fromUtf8(prefs.default_filename); QFileInfo fileInfo(defaultFileName); - QFileDialog dialog(this, tr("Open File"), fileInfo.path()); + QFileDialog dialog(this, Qtr_("Open File"), fileInfo.path()); dialog.setFileMode(QFileDialog::ExistingFile); dialog.selectFile(defaultFileName); dialog.setNameFilters(fileNameFilters()); diff --git a/qt-ui/addcylinderdialog.cpp b/qt-ui/addcylinderdialog.cpp index 6f2294a25..043f29907 100644 --- a/qt-ui/addcylinderdialog.cpp +++ b/qt-ui/addcylinderdialog.cpp @@ -1,3 +1,9 @@ +/* + * addcylinderdialog.cpp + * + * classes for the add cylinder dialog of Subsurface + * + */ #include "addcylinderdialog.h" #include "ui_addcylinderdialog.h" #include diff --git a/qt-ui/addcylinderdialog.h b/qt-ui/addcylinderdialog.h index b32494c05..652f7b362 100644 --- a/qt-ui/addcylinderdialog.h +++ b/qt-ui/addcylinderdialog.h @@ -1,3 +1,9 @@ +/* + * addcylinderdialog.h + * + * header file for the add cylinder dialog of Subsurface + * + */ #ifndef ADDCYLINDERDIALOG_H #define ADDCYLINDERDIALOG_H diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index eafbdd384..a8b1eff05 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -1,3 +1,9 @@ +/* + * divelistview.cpp + * + * classes for the divelist of Subsurface + * + */ #include "divelistview.h" DiveListView::DiveListView(QWidget *parent) : QTreeView(parent) diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h index 3ac123a14..be9774c5c 100644 --- a/qt-ui/divelistview.h +++ b/qt-ui/divelistview.h @@ -1,3 +1,9 @@ +/* + * divelistview.h + * + * header file for the dive list of Subsurface + * + */ #ifndef DIVELISTVIEW_H #define DIVELISTVIEW_H diff --git a/qt-ui/divetripmodel.cpp b/qt-ui/divetripmodel.cpp index a03603bcf..0a2944d8b 100644 --- a/qt-ui/divetripmodel.cpp +++ b/qt-ui/divetripmodel.cpp @@ -1,3 +1,10 @@ +/* + * divetripmodel.cpp + * + * classes for the dive trip list in Subsurface + */ + +#include "common.h" #include "divetripmodel.h" @@ -63,15 +70,15 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == DIVE_NUMBER) { - return tr("Dive number"); + return Qtr_("Dive number"); } else if (section == DIVE_DATE_TIME) { - return tr("Date"); + return Qtr_("Date"); } else if (section == DIVE_DURATION) { - return tr("Duration"); + return Qtr_("Duration"); } else if (section == DIVE_DEPTH) { - return tr("Depth"); + return Qtr_("Depth"); } else if (section == DIVE_LOCATION) { - return tr("Location"); + return Qtr_("Location"); } } return QVariant(); diff --git a/qt-ui/divetripmodel.h b/qt-ui/divetripmodel.h index 8c8a829e2..ad1815798 100644 --- a/qt-ui/divetripmodel.h +++ b/qt-ui/divetripmodel.h @@ -1,3 +1,9 @@ +/* + * divetripmodel.h + * + * header file for the divetrip model of Subsurface + * + */ #ifndef DIVETRIPMODEL_H #define DIVETRIPMODEL_H diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index b957fd1c7..72d8dfebc 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -1,3 +1,10 @@ +/* + * maintab.cpp + * + * classes for the "notebook" area of the main window of Subsurface + * + */ +#include "common.h" #include "maintab.h" #include "ui_maintab.h" #include "addcylinderdialog.h" diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h index 0e9f285ac..44815fafc 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -1,3 +1,9 @@ +/* + * maintab.h + * + * header file for the main tab of Subsurface + * + */ #ifndef MAINTAB_H #define MAINTAB_H diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index fdc823d9c..d1cde044a 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -1,3 +1,10 @@ +/* + * mainwindow.cpp + * + * classes for the main UI window in Subsurface + */ + +#include "common.h" #include "mainwindow.h" #include "ui_mainwindow.h" @@ -53,7 +60,7 @@ void MainWindow::on_actionNew_triggered() void MainWindow::on_actionOpen_triggered() { - QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::homePath(), filter()); + QString filename = QFileDialog::getOpenFileName(this, Qtr_("Open File"), QDir::homePath(), filter()); if (filename.isEmpty()){ return; } @@ -283,10 +290,10 @@ QString MainWindow::filter() bool MainWindow::askSaveChanges() { - QString message = ! existing_filename ? tr("You have unsaved changes\nWould you like to save those before closing the datafile?") - : tr("You have unsaved changes to file: %1 \nWould you like to save those before closing the datafile?").arg(existing_filename); + QString message = ! existing_filename ? Qtr_("You have unsaved changes\nWould you like to save those before closing the datafile?") + : Qtr_("You have unsaved changes to file: %1 \nWould you like to save those before closing the datafile?").arg(existing_filename); - if (QMessageBox::question(this, tr("Save Changes?"), message) == QMessageBox::Ok){ + if (QMessageBox::question(this, Qtr_("Save Changes?"), message) == QMessageBox::Ok){ // WARNING: Port. // file_save(NULL,NULL); return true; diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 34acf2b67..43ebde7f5 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -1,3 +1,9 @@ +/* + * mainwindow.h + * + * header file for the main window of Subsurface + * + */ #ifndef MAINWINDOW_H #define MAINWINDOW_H diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index dac1c7215..a341c0c70 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -1,3 +1,10 @@ +/* + * models.cpp + * + * classes for the equipment models of Subsurface + * + */ +#include "common.h" #include "models.h" #include "../dive.h" @@ -15,25 +22,25 @@ QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, in if (role == Qt::DisplayRole) { switch(section) { case TYPE: - ret = tr("Type"); + ret = Qtr_("Type"); break; case SIZE: - ret = tr("Size"); + ret = Qtr_("Size"); break; case MAXPRESS: - ret = tr("MaxPress"); + ret = Qtr_("MaxPress"); break; case START: - ret = tr("Start"); + ret = Qtr_("Start"); break; case END: - ret = tr("End"); + ret = Qtr_("End"); break; case O2: - ret = tr("O2%"); + ret = Qtr_("O2%"); break; case HE: - ret = tr("He%"); + ret = Qtr_("He%"); break; } } @@ -157,10 +164,10 @@ QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int r switch(section){ case TYPE: - ret = tr("Type"); + ret = Qtr_("Type"); break; case WEIGHT: - ret = tr("Weight"); + ret = Qtr_("Weight"); break; } return ret; diff --git a/qt-ui/models.h b/qt-ui/models.h index 0d0c7b41d..697096f92 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -1,3 +1,9 @@ +/* + * models.h + * + * header file for the equipment models of Subsurface + * + */ #ifndef MODELS_H #define MODELS_H diff --git a/qt-ui/plotareascene.cpp b/qt-ui/plotareascene.cpp index e69de29bb..a728040f5 100644 --- a/qt-ui/plotareascene.cpp +++ b/qt-ui/plotareascene.cpp @@ -0,0 +1,6 @@ +/* + * plotareascene.cpp + * + * classes for profile plot area scene of Subsurface + * + */ diff --git a/qt-ui/plotareascene.h b/qt-ui/plotareascene.h index e69de29bb..a5b07d1be 100644 --- a/qt-ui/plotareascene.h +++ b/qt-ui/plotareascene.h @@ -0,0 +1,6 @@ +/* + * plotareascene.h + * + * header file for the profile plot area scene of Subsurface + * + */ From fc0e307f00ceb50771d1c7a1489c7b65721f4143 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 14 Apr 2013 06:21:32 -0700 Subject: [PATCH 029/226] Add common.h file Oops Signed-off-by: Dirk Hohndel --- qt-ui/common.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 qt-ui/common.h diff --git a/qt-ui/common.h b/qt-ui/common.h new file mode 100644 index 000000000..6e00c80ed --- /dev/null +++ b/qt-ui/common.h @@ -0,0 +1,20 @@ +/* + * common.h + * + * shared by all Qt/C++ code + * + */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include + +/* use this instead of tr() for translated string literals */ +inline QString Qtr_(const char *str) +{ + return QString::fromUtf8(gettext(str)); +} + +#endif From f913eacbd95ddd9ef52dcbd29995ac2b7fc17476 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sun, 14 Apr 2013 10:56:51 -0300 Subject: [PATCH 030/226] Fixed the C++ code Style to conform the C style that was previously agreed upon. Removed the use of operator<<() in a bunch of lines to do direct calls this way the code will not scare non-c++ hearted people. :) s Signed-off-by: Tomaz Canabrava --- qt-ui/maintab.cpp | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 72d8dfebc..dd5f7cfdd 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -27,28 +27,37 @@ void MainTab::clearEquipment() void MainTab::clearInfo() { - QList labels; - labels << ui->sac << ui->otu << ui->oxygenhelium << ui->gasused - << ui->date << ui->divetime << ui->surfinterval - << ui->maxdepth << ui->avgdepth << ui->visibility - << ui->watertemperature << ui->airtemperature << ui->airpress; - - Q_FOREACH(QLabel *l, labels){ - l->setText(QString()); - } + ui->sac->setText(QString()); + ui->otu->setText(QString()); + ui->oxygenhelium->setText(QString()); + ui->gasused->setText(QString()); + ui->date->setText(QString()); + ui->divetime->setText(QString()); + ui->surfinterval->setText(QString()); + ui->maxdepth->setText(QString()); + ui->avgdepth->setText(QString()); + ui->visibility->setText(QString()); + ui->watertemperature->setText(QString()); + ui->airtemperature->setText(QString()); + ui->airpress->setText(QString()); } void MainTab::clearStats() { - QList labels; - labels << ui->maxdepth_2 << ui->mindepth << ui->avgdepth - << ui->maxsac << ui->minsac << ui->avgsac - << ui->dives << ui->maxtemp << ui->mintemp << ui->avgtemp - << ui->totaltime << ui->avgtime << ui->longestdive << ui->shortestdive; - - Q_FOREACH(QLabel *l, labels){ - l->setText(QString()); - } + ui->maxdepth_2->setText(QString()); + ui->mindepth->setText(QString()); + ui->avgdepth->setText(QString()); + ui->maxsac->setText(QString()); + ui->minsac->setText(QString()); + ui->avgsac->setText(QString()); + ui->dives->setText(QString()); + ui->maxtemp->setText(QString()); + ui->mintemp->setText(QString()); + ui->avgtemp->setText(QString()); + ui->totaltime->setText(QString()); + ui->avgtime->setText(QString()); + ui->longestdive->setText(QString()); + ui->shortestdive->setText(QString()); } void MainTab::on_addCylinder_clicked() From d8e11439ad27063b0dad05b2f8f0f4cd7f3e7de1 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 14 Apr 2013 06:44:29 -0700 Subject: [PATCH 031/226] Undoing the last Qtr_ hack The Qtr_ hack isn't needed as in commit 720fc15b2dcd ("Introduce QApplication") had already made sure that we are using gettext. I didn't revert the two commits as I wanted to keep the added header comments and fix the tooling in the Makefile as well. Signed-off-by: Dirk Hohndel --- Makefile | 2 +- qt-gui.cpp | 5 ++--- qt-ui/common.h | 20 -------------------- qt-ui/divetripmodel.cpp | 12 +++++------- qt-ui/maintab.cpp | 1 - qt-ui/mainwindow.cpp | 10 ++++------ qt-ui/models.cpp | 19 +++++++++---------- 7 files changed, 21 insertions(+), 48 deletions(-) delete mode 100644 qt-ui/common.h diff --git a/Makefile b/Makefile index e33d92053..7778d2aa8 100644 --- a/Makefile +++ b/Makefile @@ -305,7 +305,7 @@ $(INFOPLIST): $(INFOPLISTINPUT) # Transifex merge the translations update-po-files: - xgettext -o po/subsurface-new.pot -s -k_ -kN_ -kQtr_ --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c qt-ui/*.cpp + xgettext -o po/subsurface-new.pot -s -k_ -kN_ -ktr --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c qt-ui/*.cpp tx push -s tx pull -af diff --git a/qt-gui.cpp b/qt-gui.cpp index 1621317c2..745457763 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -25,7 +25,6 @@ #include "version.h" #include "libdivecomputer.h" #include "qt-ui/mainwindow.h" -#include "qt-ui/common.h" #include #include @@ -1785,7 +1784,7 @@ void MainWindow::setCurrentFileName(const QString &fileName) if (fileName == m_currentFileName) return; m_currentFileName = fileName; - QString title = Qtr_("Subsurface"); + QString title = tr("Subsurface"); if (!m_currentFileName.isEmpty()) { QFileInfo fileInfo(m_currentFileName); title += " - " + fileInfo.fileName(); @@ -1798,7 +1797,7 @@ void MainWindow::on_actionOpen_triggered() QString defaultFileName = QString::fromUtf8(prefs.default_filename); QFileInfo fileInfo(defaultFileName); - QFileDialog dialog(this, Qtr_("Open File"), fileInfo.path()); + QFileDialog dialog(this, tr("Open File"), fileInfo.path()); dialog.setFileMode(QFileDialog::ExistingFile); dialog.selectFile(defaultFileName); dialog.setNameFilters(fileNameFilters()); diff --git a/qt-ui/common.h b/qt-ui/common.h deleted file mode 100644 index 6e00c80ed..000000000 --- a/qt-ui/common.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * common.h - * - * shared by all Qt/C++ code - * - */ - -#ifndef COMMON_H -#define COMMON_H - -#include -#include - -/* use this instead of tr() for translated string literals */ -inline QString Qtr_(const char *str) -{ - return QString::fromUtf8(gettext(str)); -} - -#endif diff --git a/qt-ui/divetripmodel.cpp b/qt-ui/divetripmodel.cpp index 0a2944d8b..5082494a0 100644 --- a/qt-ui/divetripmodel.cpp +++ b/qt-ui/divetripmodel.cpp @@ -3,8 +3,6 @@ * * classes for the dive trip list in Subsurface */ - -#include "common.h" #include "divetripmodel.h" @@ -70,15 +68,15 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == DIVE_NUMBER) { - return Qtr_("Dive number"); + return tr("Dive number"); } else if (section == DIVE_DATE_TIME) { - return Qtr_("Date"); + return tr("Date"); } else if (section == DIVE_DURATION) { - return Qtr_("Duration"); + return tr("Duration"); } else if (section == DIVE_DEPTH) { - return Qtr_("Depth"); + return tr("Depth"); } else if (section == DIVE_LOCATION) { - return Qtr_("Location"); + return tr("Location"); } } return QVariant(); diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 72d8dfebc..53926cb25 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -4,7 +4,6 @@ * classes for the "notebook" area of the main window of Subsurface * */ -#include "common.h" #include "maintab.h" #include "ui_maintab.h" #include "addcylinderdialog.h" diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index d1cde044a..577b7fb67 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -3,8 +3,6 @@ * * classes for the main UI window in Subsurface */ - -#include "common.h" #include "mainwindow.h" #include "ui_mainwindow.h" @@ -60,7 +58,7 @@ void MainWindow::on_actionNew_triggered() void MainWindow::on_actionOpen_triggered() { - QString filename = QFileDialog::getOpenFileName(this, Qtr_("Open File"), QDir::homePath(), filter()); + QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::homePath(), filter()); if (filename.isEmpty()){ return; } @@ -290,10 +288,10 @@ QString MainWindow::filter() bool MainWindow::askSaveChanges() { - QString message = ! existing_filename ? Qtr_("You have unsaved changes\nWould you like to save those before closing the datafile?") - : Qtr_("You have unsaved changes to file: %1 \nWould you like to save those before closing the datafile?").arg(existing_filename); + QString message = ! existing_filename ? tr("You have unsaved changes\nWould you like to save those before closing the datafile?") + : tr("You have unsaved changes to file: %1 \nWould you like to save those before closing the datafile?").arg(existing_filename); - if (QMessageBox::question(this, Qtr_("Save Changes?"), message) == QMessageBox::Ok){ + if (QMessageBox::question(this, tr("Save Changes?"), message) == QMessageBox::Ok){ // WARNING: Port. // file_save(NULL,NULL); return true; diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index a341c0c70..64fa6bac3 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -4,7 +4,6 @@ * classes for the equipment models of Subsurface * */ -#include "common.h" #include "models.h" #include "../dive.h" @@ -22,25 +21,25 @@ QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, in if (role == Qt::DisplayRole) { switch(section) { case TYPE: - ret = Qtr_("Type"); + ret = tr("Type"); break; case SIZE: - ret = Qtr_("Size"); + ret = tr("Size"); break; case MAXPRESS: - ret = Qtr_("MaxPress"); + ret = tr("MaxPress"); break; case START: - ret = Qtr_("Start"); + ret = tr("Start"); break; case END: - ret = Qtr_("End"); + ret = tr("End"); break; case O2: - ret = Qtr_("O2%"); + ret = tr("O2%"); break; case HE: - ret = Qtr_("He%"); + ret = tr("He%"); break; } } @@ -164,10 +163,10 @@ QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int r switch(section){ case TYPE: - ret = Qtr_("Type"); + ret = tr("Type"); break; case WEIGHT: - ret = Qtr_("Weight"); + ret = tr("Weight"); break; } return ret; From 4c73c70ecb30ab412248e0eff4285a6a59af30fa Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 14 Apr 2013 11:31:26 -0700 Subject: [PATCH 032/226] Separate Gtk related code from core logic: info Surprisingly straight forward, just a couple of places where we really mix significant logic with UI code (for example setting the window title). I had to move amount_selected from display-gtk.h to display.h - I guess the number of dives that are selected is UI independent. But I wonder if we still will track this as a global variable in a Qt UI (since the Gtk selection logic is the main reason this existed in the first place). Added a new info.h files for the necessary declarations. This should make no difference to functionality. Signed-off-by: Dirk Hohndel --- Makefile | 7 +- display-gtk.h | 2 - display.h | 2 + info-gtk.c | 965 ++++++++++++++++++++++++++++++++++++++++++++ info.c | 1068 ++++--------------------------------------------- info.h | 20 + 6 files changed, 1061 insertions(+), 1003 deletions(-) create mode 100644 info-gtk.c create mode 100644 info.h diff --git a/Makefile b/Makefile index 7778d2aa8..af33dde33 100644 --- a/Makefile +++ b/Makefile @@ -189,11 +189,12 @@ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.m QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ qt-ui/divetripmodel.o qt-ui/addcylinderdialog.o qt-ui/models.o -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ - planner.o planner-gtk.o \ +GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o + +OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o planner.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ - webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) $(QTOBJS) + webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) $(QTOBJS) $(GTKOBJS) # Add files to the following variables if the auto-detection based on the # filename fails diff --git a/display-gtk.h b/display-gtk.h index 904d5efce..ad286b0d3 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -81,8 +81,6 @@ extern GtkWidget *create_label(const char *fmt, ...); extern gboolean icon_click_cb(GtkWidget *w, GdkEventButton *event, gpointer data); -extern unsigned int amount_selected; - extern void process_selected_dives(void); typedef void (*data_func_t)(GtkTreeViewColumn *col, diff --git a/display.h b/display.h index d5c69e81b..9e01c73a9 100644 --- a/display.h +++ b/display.h @@ -67,6 +67,8 @@ struct options { extern char zoomed_plot, dc_number; +extern unsigned int amount_selected; + #ifdef __cplusplus } #endif diff --git a/info-gtk.c b/info-gtk.c new file mode 100644 index 000000000..ebba07b73 --- /dev/null +++ b/info-gtk.c @@ -0,0 +1,965 @@ +/* info-gtk.c + * creates the UI for the info frame - + * controlled through the following interfaces: + * + * void show_dive_info(struct dive *dive) + * + * called from gtk-ui: + * GtkWidget *extended_dive_info_widget(void) + */ +#include +#include +#include +#include +#include +#include +#include + +#include "dive.h" +#include "display.h" +#include "display-gtk.h" +#include "divelist.h" +#include "info.h" + +typedef enum { EDIT_NEW_DIVE, EDIT_ALL, EDIT_WHEN } edit_control_t; +static GtkEntry *location, *buddy, *divemaster, *rating, *suit; +static GtkTextView *notes; +static GtkListStore *location_list, *people_list, *star_list, *suit_list; + +static char *get_text(GtkTextView *view) +{ + GtkTextBuffer *buffer; + GtkTextIter start; + GtkTextIter end; + + buffer = gtk_text_view_get_buffer(view); + gtk_text_buffer_get_start_iter(buffer, &start); + gtk_text_buffer_get_end_iter(buffer, &end); + return gtk_text_buffer_get_text(buffer, &start, &end, FALSE); +} + +/* + * Get the string from a combo box. + * + * The "master" string is the string of the current dive - we only consider it + * changed if the old string is either empty, or matches that master string. + */ +static char *get_combo_box_entry_text(GtkComboBox *combo_box, char **textp, const char *master) +{ + const char *newstring = get_active_text(combo_box); + return evaluate_string_change(newstring, textp, master); +} + +#define SET_TEXT_VALUE(x) \ + gtk_entry_set_text(x, dive && dive->x ? dive->x : "") + +void show_dive_info(struct dive *dive) +{ + const char *title = get_window_title(dive); + gtk_window_set_title(GTK_WINDOW(main_window), title); + free((void *)title); + SET_TEXT_VALUE(divemaster); + SET_TEXT_VALUE(buddy); + SET_TEXT_VALUE(location); + SET_TEXT_VALUE(suit); + gtk_entry_set_text(rating, star_strings[dive ? dive->rating : 0]); + gtk_text_buffer_set_text(gtk_text_view_get_buffer(notes), + dive && dive->notes ? dive->notes : "", -1); + /* I'm not quite sure /why/ this is only called when we have no dive + * -- need to investigate */ + if (!dive) + show_dive_equipment(NULL, W_IDX_PRIMARY); +} + +static void info_menu_edit_cb(GtkMenuItem *menuitem, gpointer user_data) +{ + if (amount_selected) + edit_multi_dive_info(NULL); +} + +static void add_menu_item(GtkMenuShell *menu, const char *label, const char *icon, void (*cb)(GtkMenuItem *, gpointer)) +{ + GtkWidget *item; + if (icon) { + GtkWidget *image; + item = gtk_image_menu_item_new_with_label(label); + image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); + } else { + item = gtk_menu_item_new_with_label(label); + } + g_signal_connect(item, "activate", G_CALLBACK(cb), NULL); + gtk_widget_show(item); /* Yes, really */ + gtk_menu_shell_prepend(menu, item); +} + +static void populate_popup_cb(GtkTextView *entry, GtkMenuShell *menu, gpointer user_data) +{ + if (amount_selected) + add_menu_item(menu, _("Edit"), GTK_STOCK_EDIT, info_menu_edit_cb); +} + +static GtkEntry *text_value(GtkWidget *box, const char *label) +{ + GtkWidget *widget; + GtkWidget *frame = gtk_frame_new(label); + + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); + widget = gtk_entry_new(); + gtk_widget_set_can_focus(widget, FALSE); + gtk_editable_set_editable(GTK_EDITABLE(widget), FALSE); + gtk_container_add(GTK_CONTAINER(frame), widget); + g_signal_connect(widget, "populate-popup", G_CALLBACK(populate_popup_cb), NULL); + return GTK_ENTRY(widget); +} + +static GtkEntry *single_text_entry(GtkWidget *box, const char *label, const char *text) +{ + GtkEntry *entry; + GtkWidget *frame = gtk_frame_new(label); + + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); + entry = GTK_ENTRY(gtk_entry_new()); + gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(entry)); + if (text && *text) + gtk_entry_set_text(entry, text); + return entry; +} + +static GtkComboBox *text_entry(GtkWidget *box, const char *label, GtkListStore *completions, const char *text) +{ + GtkWidget *combo_box; + GtkWidget *frame = gtk_frame_new(label); + + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); + + combo_box = combo_box_with_model_and_entry(completions); + gtk_container_add(GTK_CONTAINER(frame), combo_box); + + if (text && *text) + set_active_text(GTK_COMBO_BOX(combo_box), text); + + return GTK_COMBO_BOX(combo_box); +} + +enum writable { + READ_ONLY, + READ_WRITE +}; + +static GtkTextView *text_view(GtkWidget *box, const char *label, enum writable writable) +{ + GtkWidget *view, *vbox; + GtkWidget *frame = gtk_frame_new(label); + + gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0); + box = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), box); + vbox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(box), vbox); + + GtkWidget* scrolled_window = gtk_scrolled_window_new(0, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_IN); + + view = gtk_text_view_new(); + if (writable == READ_ONLY) { + gtk_widget_set_can_focus(view, FALSE); + gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE); + g_signal_connect(view, "populate-popup", G_CALLBACK(populate_popup_cb), NULL); + } + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD); + gtk_container_add(GTK_CONTAINER(scrolled_window), view); + gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0); + return GTK_TEXT_VIEW(view); +} + +static GtkTreeIter string_entry_location; + +static gboolean match_string_entry(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + const char *string = data; + char *entry; + int cmp; + + gtk_tree_model_get(model, iter, 0, &entry, -1); + cmp = strcmp(entry, string); + if (entry) + free(entry); + + /* Stop. The entry is bigger than the new one */ + if (cmp > 0) + return TRUE; + + /* Exact match */ + if (!cmp) { + found_string_entry = MATCH_EXACT; + return TRUE; + } + + string_entry_location = *iter; + found_string_entry = MATCH_AFTER; + return FALSE; +} + +int match_list(GtkListStore *list, const char *string) +{ + found_string_entry = MATCH_PREPEND; + gtk_tree_model_foreach(GTK_TREE_MODEL(list), match_string_entry, (void *)string); + return found_string_entry; +} + +void add_string_list_entry(const char *string, GtkListStore *list) +{ + GtkTreeIter *iter, loc; + + if (!string || !*string) + return; + + switch (match_list(list, string)) { + case MATCH_EXACT: + return; + case MATCH_PREPEND: + iter = NULL; + break; + case MATCH_AFTER: + iter = &string_entry_location; + break; + } + gtk_list_store_insert_after(list, &loc, iter); + gtk_list_store_set(list, &loc, 0, string, -1); +} + +void add_people(const char *string) +{ + add_string_list_entry(string, people_list); +} + +void add_location(const char *string) +{ + add_string_list_entry(string, location_list); +} + +void add_suit(const char *string) +{ + add_string_list_entry(string, suit_list); +} + +/* this helper function is completely Gtk independent... but I really + * hope that a new UI would just have a decent star rating widget and + * we won't need this kludge... so I'll leave this in the -gtk file */ +static int get_rating(const char *string) +{ + int rating_val = 0; + int i; + + for (i = 0; i <= 5; i++) + if (!strcmp(star_strings[i],string)) + rating_val = i; + return rating_val; +} + +struct dive_info { + GtkComboBox *location, *divemaster, *buddy, *rating, *suit, *viz; + GtkEntry *airtemp, *gps; + GtkWidget *gps_icon; + GtkTextView *notes; +}; + +static void save_dive_info_changes(struct dive *dive, struct dive *master, struct dive_info *info) +{ + char *old_text, *new_text; + const char *gps_text; + char *rating_string; + double newtemp; + int changed = 0; + + new_text = get_combo_box_entry_text(info->location, &dive->location, master->location); + if (new_text) { + add_location(new_text); + changed = 1; + } + + gps_text = gtk_entry_get_text(info->gps); + if (gps_changed(dive, master, gps_text)) + changed = 1; + + new_text = get_combo_box_entry_text(info->divemaster, &dive->divemaster, master->divemaster); + if (new_text) { + add_people(new_text); + changed = 1; + } + + new_text = get_combo_box_entry_text(info->buddy, &dive->buddy, master->buddy); + if (new_text) { + add_people(new_text); + changed = 1; + } + + new_text = get_combo_box_entry_text(info->suit, &dive->suit, master->suit); + if (new_text) { + add_suit(new_text); + changed = 1; + } + + rating_string = strdup(star_strings[dive->rating]); + new_text = get_combo_box_entry_text(info->rating, &rating_string, star_strings[master->rating]); + if (new_text) { + dive->rating = get_rating(rating_string); + changed = 1; + } + free(rating_string); + + rating_string = strdup(star_strings[dive->visibility]); + new_text = get_combo_box_entry_text(info->viz, &rating_string, star_strings[master->visibility]); + if (new_text) { + dive->visibility = get_rating(rating_string); + changed = 1; + } + free(rating_string); + + new_text = (char *)gtk_entry_get_text(info->airtemp); + if (sscanf(new_text, "%lf", &newtemp) == 1) { + unsigned long mkelvin; + switch (prefs.units.temperature) { + case CELSIUS: + mkelvin = C_to_mkelvin(newtemp); + break; + case FAHRENHEIT: + mkelvin = F_to_mkelvin(newtemp); + break; + default: + mkelvin = 0; + } + if (mkelvin != dive->airtemp.mkelvin && dive->airtemp.mkelvin == master->airtemp.mkelvin) { + dive->airtemp.mkelvin = mkelvin; + changed = 1; + } + } + + if (info->notes) { + old_text = dive->notes; + dive->notes = get_text(info->notes); + if (text_changed(old_text,dive->notes)) + changed = 1; + if (old_text) + g_free(old_text); + } + if (changed) { + mark_divelist_changed(TRUE); + update_dive(dive); + } +} + +static void dive_trip_widget(GtkWidget *box, dive_trip_t *trip, struct dive_info *info) +{ + GtkWidget *hbox, *label; + char buffer[128] = N_("Edit trip summary"); + + label = gtk_label_new(_(buffer)); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0); + + info->location = text_entry(box, _("Location"), location_list, trip->location); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); + + info->notes = text_view(box, _("Notes"), READ_WRITE); + if (trip->notes && *trip->notes) + gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), trip->notes, -1); +} + +struct location_update { + char text[45]; + char set_by_hand; + GtkEntry *entry; + struct dive *dive; + void (*callback)(float, float); +} location_update; + +static void update_gps_entry(int lat, int lon) +{ + if (location_update.entry) { + print_gps_coordinates(location_update.text, 45, lat, lon); + gtk_entry_set_text(location_update.entry, location_update.text); + } +} + +#if HAVE_OSM_GPS_MAP +static void update_gps_entry_callback(float lat, float lon) +{ + update_gps_entry(lat * 1000000, lon * 1000000); + location_update.set_by_hand = 1; +} + +static gboolean gps_map_callback(GtkWidget *w, gpointer data) +{ + double latitude, longitude; + const char *gps_text = NULL; + struct dive fake_dive; + + memset(&fake_dive, 0, sizeof(fake_dive)); + if (location_update.entry) { + gps_text = gtk_entry_get_text(location_update.entry); + parse_gps_text(gps_text, &latitude, &longitude); + fake_dive.latitude.udeg = rint(latitude * 1000000); + fake_dive.longitude.udeg = rint(longitude * 1000000); + } + show_gps_location(&fake_dive, update_gps_entry_callback); + return TRUE; +} +#endif + +/* + * If somebody sets the string by editing the text entry, + * we consider a clear string an opportunity to set things + * automatically. + * + * A non-empty string, on the other hand, means that we + * should *not* touch it when we change the location field. + */ +static gboolean gps_entry_change_cb(GtkEntry *gps, GdkEvent *event, gpointer userdata) +{ + const char *string = gtk_entry_get_text(gps); + + /* A clear string is never considered to be "set" */ + if (!string) { + location_update.set_by_hand = 0; + return FALSE; + } + + /* + * If it wasn't set by hand, and it hasn't changed, + * it's still not set by hand + */ + if (!location_update.set_by_hand) { + if (!strcmp(location_update.text, string)) + return FALSE; + } + + /* Otherwise, check if it's all empty.. */ + while (g_ascii_isspace(*string)) + string++; + location_update.set_by_hand = !!*string; + + return FALSE; +} + +static void location_entry_change_cb(GtkComboBox *location, gpointer *userdata) +{ + int i; + struct dive *dive; + const char *name; + + /* + * Don't do any automatic gps changes of entries that have been + * explicitly set to some value! + */ + if (location_update.set_by_hand) + return; + + name = get_active_text(location); + for_each_dive(i, dive) { + if (!dive_has_gps_location(dive)) + continue; + if (!dive->location || strcasecmp(dive->location, name)) + continue; + update_gps_entry(dive->latitude.udeg, dive->longitude.udeg); + return; + } + update_gps_entry(0, 0); +} + +static void set_dive_button_label(GtkWidget *button, struct dive *dive) +{ + char buffer[256]; + + /* if there is only one dc and it has no samples we can edit the depth, too */ + if (dive->dc.next || dive->dc.samples) + divename(buffer, sizeof(buffer), dive, _("(click to edit date/time)")); + else + divename(buffer, sizeof(buffer), dive, _("(click to edit date/time/depth)")); + gtk_button_set_label(GTK_BUTTON(button), buffer); +} + +static int dive_time_widget(struct dive *dive, edit_control_t editing); + +static gboolean base_data_cb(GtkWidget *w, GdkEvent *event, gpointer _data) +{ + struct dive *dive = _data; + + /* if there are more than one divecomputers or if there are any sample + * then only the start time (well, date and time) can be changed, + * otherwise (this is most likely a dive that was added manually in Subsurface + * and we can edit duration, max and mean depth, too */ + if (dive->dc.next || dive->dc.samples) + dive_time_widget(dive, EDIT_WHEN); + else + dive_time_widget(dive, EDIT_ALL); + set_dive_button_label(w, dive); + return FALSE; +} + +static void dive_info_widget(GtkWidget *obox, struct dive *dive, struct dive_info *info, gboolean multi) +{ + GtkWidget *hbox, *frame, *equipment, *ibox, *box; +#if HAVE_OSM_GPS_MAP + GtkWidget *image; +#endif + char buffer[256]; + char airtemp[10]; + const char *unit; + double value; + + if (multi) { + GtkWidget *label; + snprintf(buffer, sizeof(buffer), "%s", _("Edit multiple dives")); + label = gtk_label_new(buffer); + gtk_box_pack_start(GTK_BOX(obox), label, FALSE, TRUE, 0); + } else { + GtkWidget *basedata = gtk_button_new_with_label(buffer); + set_dive_button_label(basedata, dive); + g_signal_connect(G_OBJECT(basedata), "button-press-event", G_CALLBACK(base_data_cb), dive); + gtk_box_pack_start(GTK_BOX(obox), basedata, FALSE, TRUE, 0); + } + /* two column layout (inner hbox ibox) within the outer vbox (obox) we are given */ + ibox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(obox), ibox, TRUE, TRUE, 0); + box = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(ibox), box, TRUE, TRUE, 0); + + info->location = text_entry(box, _("Location"), location_list, dive->location); + g_signal_connect(G_OBJECT(info->location), "changed", G_CALLBACK(location_entry_change_cb), NULL); + + hbox = gtk_hbox_new(FALSE, 2); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); + info->gps = single_text_entry(hbox, _("GPS (WGS84 or GPS format)"), NULL); + + location_update.entry = info->gps; + location_update.dive = dive; + update_gps_entry(dive->latitude.udeg, dive->longitude.udeg); + location_update.set_by_hand = !!location_update.text[0]; + + gtk_widget_add_events(GTK_WIDGET(info->gps), GDK_FOCUS_CHANGE_MASK); + g_signal_connect(G_OBJECT(info->gps), "focus-out-event", G_CALLBACK(gps_entry_change_cb), NULL); + gtk_entry_set_width_chars(info->gps, 30); +#if HAVE_OSM_GPS_MAP + info->gps_icon = gtk_button_new_with_label(_("Pick on map")); + gtk_box_pack_start(GTK_BOX(hbox), info->gps_icon, FALSE, FALSE, 6); + image = gtk_image_new_from_pixbuf(get_gps_icon()); + gtk_image_set_pixel_size(GTK_IMAGE(image), 128); + gtk_button_set_image(GTK_BUTTON(info->gps_icon), image); + + g_signal_connect(G_OBJECT(info->gps_icon), "clicked", G_CALLBACK(gps_map_callback), NULL); +#endif + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); + + info->divemaster = text_entry(hbox, _("Dive master"), people_list, dive->divemaster); + info->buddy = text_entry(hbox, _("Buddy"), people_list, dive->buddy); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); + + info->rating = text_entry(hbox, _("Rating"), star_list, star_strings[dive->rating]); + info->suit = text_entry(hbox, _("Suit"), suit_list, dive->suit); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); + + info->viz = text_entry(hbox, _("Visibility"), star_list, star_strings[dive->visibility]); + + value = get_temp_units(dive->airtemp.mkelvin, &unit); + snprintf(buffer, sizeof(buffer), _("Air Temp in %s"), unit); + if (dive->airtemp.mkelvin) + snprintf(airtemp, sizeof(airtemp), "%.1f", value); + else + airtemp[0] = '\0'; + info->airtemp = single_text_entry(hbox, buffer, airtemp); + + /* only show notes if editing a single dive */ + if (multi) { + info->notes = NULL; + } else { + info->notes = text_view(box, _("Notes"), READ_WRITE); + gtk_widget_set_size_request(GTK_WIDGET(info->notes), -1, 128); + if (dive->notes && *dive->notes) + gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1); + } + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(ibox), hbox, TRUE, TRUE, 0); + + /* create a secondary Equipment widget */ + frame = gtk_frame_new(_("Equipment")); + equipment = equipment_widget(W_IDX_SECONDARY); + gtk_container_add(GTK_CONTAINER(frame), equipment); + gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); +} + +gboolean edit_trip(dive_trip_t *trip) +{ + GtkWidget *dialog, *vbox; + int success; + gboolean changed = FALSE; + char *old_text, *new_text; + struct dive_info info; + + memset(&info, 0, sizeof(struct dive_info)); + dialog = gtk_dialog_new_with_buttons(_("Edit Trip Info"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 300); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + dive_trip_widget(vbox, trip, &info); + gtk_widget_show_all(dialog); + success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + if (success) { + /* we need to store the edited location and notes */ + new_text = get_combo_box_entry_text(info.location, &trip->location, trip->location); + if (new_text) { + add_location(new_text); + changed = TRUE; + } + if (info.notes) { + old_text = trip->notes; + trip->notes = get_text(info.notes); + if (text_changed(old_text, trip->notes)) + changed = TRUE; + if (old_text) + g_free(old_text); + } + if (changed) + mark_divelist_changed(TRUE); + } + gtk_widget_destroy(dialog); + return changed; +} + +int edit_multi_dive_info(struct dive *single_dive) +{ + int success; + GtkWidget *dialog, *vbox, *scrolled_window, *viewport; + GtkRequisition size; + struct dive_info info; + struct dive *master; + gboolean multi; + + scrolled_window = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + dialog = gtk_dialog_new_with_buttons(_("Dive Info"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0); + vbox = g_object_new(GTK_TYPE_VBOX, NULL); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), vbox); + master = single_dive; + if (!master) + master = current_dive; + if (!master) + return 0; + /* See if we should use multi dive mode */ + multi = FALSE; + if (!single_dive) { + int i; + struct dive *dive; + + for_each_dive(i, dive) { + if (dive != master && dive->selected) { + multi = TRUE; + break; + } + } + } + /* edit a temporary copy of the master dive; + * edit_dive is a global dive structure that is modified by the + * cylinder / weightsystem dialogs if we open W_IDX_SECONDARY + * edit widgets as we do here */ + memcpy(&edit_dive, master, sizeof(struct dive)); + + dive_info_widget(vbox, &edit_dive, &info, multi); + save_equipment_data(&edit_dive); + gtk_widget_show_all(dialog); + viewport = gtk_widget_get_ancestor(vbox, GTK_TYPE_VIEWPORT); +#if GTK_CHECK_VERSION(3,0,0) + gtk_widget_get_preferred_size(viewport, NULL, &size); +#else + gtk_widget_size_request(viewport, &size); +#endif + gtk_widget_set_size_request(scrolled_window, size.width, size.height); + /* add the equipment post the "blank" layout estimate */ + show_dive_equipment(&edit_dive, W_IDX_SECONDARY); + success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + if (success) { + mark_divelist_changed(TRUE); + /* Update the non-current selected dives first */ + if (!single_dive) { + int i; + struct dive *dive; + + for_each_dive(i, dive) { + if (dive == master || !dive->selected) + continue; + /* copy all "info" fields */ + save_dive_info_changes(dive, &edit_dive, &info); + /* copy the cylinders / weightsystems */ + update_equipment_data(dive, &edit_dive); + /* this is extremely inefficient... it loops through all + dives to find the right one - but we KNOW the index already */ + update_cylinder_related_info(dive); + flush_divelist(dive); + } + } + + /* Update the master dive last! */ + save_dive_info_changes(master, &edit_dive, &info); + update_equipment_data(master, &edit_dive); + update_cylinder_related_info(master); + /* if there was only one dive we might also have changed dive->when + * or even the duration and depth information (in a dive without samples) */ + if (! multi) + update_time_depth(master, &edit_dive); + dive_list_update_dives(); + } + gtk_widget_destroy(dialog); + location_update.entry = NULL; + + return success; +} + +static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...) +{ + va_list ap; + char buffer[128]; + GtkWidget *frame, *hbox; + + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + frame = gtk_frame_new(buffer); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0); + hbox = gtk_hbox_new(0, 3); + gtk_container_add(GTK_CONTAINER(frame), hbox); + return hbox; +} + +/* returns the dialog plus pointers to the calendar, hour and minute widget + * plus the hbox that holds the time entry (in case the caller wants to put + * a duration entry widget next to the time entry widget */ +GtkWidget *create_date_time_widget(struct tm *time, GtkWidget **cal, GtkWidget **h, + GtkWidget **m, GtkWidget **timehbox) +{ + GtkWidget *dialog; + GtkWidget *hbox, *vbox; + GtkWidget *label; + + dialog = gtk_dialog_new_with_buttons(_("Date and Time"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + + /* Calendar hbox */ + hbox = frame_box(vbox, _("Date:")); + *cal = gtk_calendar_new(); + gtk_box_pack_start(GTK_BOX(hbox), *cal, FALSE, TRUE, 0); + + /* Time hbox */ + *timehbox = gtk_hbox_new(TRUE, 3); + gtk_box_pack_start(GTK_BOX(vbox), *timehbox, FALSE, FALSE, 0); + hbox = frame_box(*timehbox, _("Time")); + + *h = gtk_spin_button_new_with_range (0.0, 23.0, 1.0); + *m = gtk_spin_button_new_with_range (0.0, 59.0, 1.0); + + gtk_calendar_select_month(GTK_CALENDAR(*cal), time->tm_mon, time->tm_year + 1900); + gtk_calendar_select_day(GTK_CALENDAR(*cal), time->tm_mday); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(*h), time->tm_hour); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(*m), time->tm_min); + + gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(*h), TRUE); + gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(*m), TRUE); + + gtk_box_pack_end(GTK_BOX(hbox), *m, FALSE, FALSE, 0); + label = gtk_label_new(":"); + gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(hbox), *h, FALSE, FALSE, 0); + + return dialog; +} + +static int mm_from_spinbutton(GtkWidget *depth) +{ + int result; + double val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(depth)); + if (prefs.units.length == FEET) { + result = feet_to_mm(val); + } else { + result = val * 1000 + 0.5; + } + return result; +} + +static int dive_time_widget(struct dive *dive, edit_control_t editing) +{ + GtkWidget *dialog; + GtkWidget *cal, *vbox, *hbox, *box; + GtkWidget *h, *m; + GtkWidget *duration, *depth, *avgdepth; + guint yval, mval, dval; + struct tm tm, *time; + int success; + double depthinterval; + + /* + * If we have a dive selected, 'add dive' will default + * to one hour after the end of that dive. Otherwise, + * we'll just take the current time. + */ + if (editing != EDIT_NEW_DIVE) { + utc_mkdate(dive->when, &tm); + time = &tm; + } else if (amount_selected == 1) { + timestamp_t when = current_dive->when; + when += current_dive->duration.seconds; + when += 60*60; + utc_mkdate(when, &tm); + time = &tm; + } else { + time_t now; + struct timeval tv; + gettimeofday(&tv, NULL); + now = tv.tv_sec; + time = localtime(&now); + } + dialog = create_date_time_widget(time, &cal, &h, &m, &hbox); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + + if (editing != EDIT_WHEN) { + /* Duration box */ + box = frame_box(hbox, _("Duration (min)")); + duration = gtk_spin_button_new_with_range (0.0, 1000.0, 1.0); + if (editing != EDIT_NEW_DIVE) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(duration), dive->dc.duration.seconds / 60.0); + gtk_box_pack_end(GTK_BOX(box), duration, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(TRUE, 3); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + /* Depth box */ + box = frame_box(hbox, _("Max Depth (%s):"), prefs.units.length == FEET ? _("ft") : _("m")); + if (prefs.units.length == FEET) { + depthinterval = 1.0; + } else { + depthinterval = 0.1; + } + depth = gtk_spin_button_new_with_range (0.0, 1000.0, depthinterval); + if (editing != EDIT_NEW_DIVE) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(depth), dive->dc.maxdepth.mm / 1000.0); + gtk_box_pack_end(GTK_BOX(box), depth, FALSE, FALSE, 0); + + box = frame_box(hbox, _("Avg Depth (%s):"), prefs.units.length == FEET ? _("ft") : _("m")); + if (prefs.units.length == FEET) { + depthinterval = 1.0; + } else { + depthinterval = 0.1; + } + avgdepth = gtk_spin_button_new_with_range (0.0, 1000.0, depthinterval); + if (editing != EDIT_NEW_DIVE) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(avgdepth), dive->dc.meandepth.mm / 1000.0); + gtk_box_pack_end(GTK_BOX(box), avgdepth, FALSE, FALSE, 0); + } + /* All done, show it and wait for editing */ + gtk_widget_show_all(dialog); + success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + if (!success) { + gtk_widget_destroy(dialog); + return 0; + } + + memset(&tm, 0, sizeof(tm)); + gtk_calendar_get_date(GTK_CALENDAR(cal), &yval, &mval, &dval); + tm.tm_year = yval; + tm.tm_mon = mval; + tm.tm_mday = dval; + + tm.tm_hour = gtk_spin_button_get_value(GTK_SPIN_BUTTON(h)); + tm.tm_min = gtk_spin_button_get_value(GTK_SPIN_BUTTON(m)); + + if (editing != EDIT_WHEN) { + dive->dc.maxdepth.mm = mm_from_spinbutton(depth); + dive->dc.meandepth.mm = mm_from_spinbutton(avgdepth); + dive->dc.duration.seconds = gtk_spin_button_get_value(GTK_SPIN_BUTTON(duration))*60; + } + gtk_widget_destroy(dialog); + dive->when = utc_mktime(&tm); + + return 1; +} + +int add_new_dive(struct dive *dive) +{ + if (!dive) + return 0; + + if (!dive_time_widget(dive, EDIT_NEW_DIVE)) + return 0; + + return edit_dive_info(dive, TRUE); +} + +GtkWidget *extended_dive_info_widget(void) +{ + GtkWidget *vbox, *hbox; + vbox = gtk_vbox_new(FALSE, 6); + + people_list = gtk_list_store_new(1, G_TYPE_STRING); + location_list = gtk_list_store_new(1, G_TYPE_STRING); + star_list = gtk_list_store_new(1, G_TYPE_STRING); + add_string_list_entry(star_strings[0], star_list); + add_string_list_entry(star_strings[1], star_list); + add_string_list_entry(star_strings[2], star_list); + add_string_list_entry(star_strings[3], star_list); + add_string_list_entry(star_strings[4], star_list); + add_string_list_entry(star_strings[5], star_list); + suit_list = gtk_list_store_new(1, G_TYPE_STRING); + + gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); + location = text_value(vbox, _("Location")); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); + + divemaster = text_value(hbox, _("Divemaster")); + buddy = text_value(hbox, _("Buddy")); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); + + rating = text_value(hbox, _("Rating")); + suit = text_value(hbox, _("Suit")); + + notes = text_view(vbox, _("Notes"), READ_ONLY); + return vbox; +} + +void info_widget_destroy(void) +{ + g_object_unref(people_list); + g_object_unref(location_list); + g_object_unref(star_list); + g_object_unref(suit_list); +} diff --git a/info.c b/info.c index 455fbea1a..0abee1c1b 100644 --- a/info.c +++ b/info.c @@ -1,11 +1,17 @@ -/* info.c */ -/* creates the UI for the info frame - - * controlled through the following interfaces: +/* info.c * - * void show_dive_info(struct dive *dive) + * UI toolkit independent logic used for the info frame * - * called from gtk-ui: - * GtkWidget *extended_dive_info_widget(void) + * gboolean gps_changed(struct dive *dive, struct dive *master, const char *gps_text); + * void print_gps_coordinates(char *buffer, int len, int lat, int lon); + * void save_equipment_data(struct dive *dive); + * void update_equipment_data(struct dive *dive, struct dive *master); + * void update_time_depth(struct dive *dive, struct dive *edited); + * const char *get_window_title(struct dive *dive); + * char *evaluate_string_change(const char *newstring, char **textp, const char *master); + * int text_changed(const char *old, const char *new); + * gboolean parse_gps_text(const char *gps_text, double *latitude, double *longitude); + * int divename(char *buf, size_t size, struct dive *dive, char *trailer); */ #include #include @@ -17,29 +23,11 @@ #include "dive.h" #include "display.h" -#include "display-gtk.h" #include "divelist.h" -typedef enum { EDIT_NEW_DIVE, EDIT_ALL, EDIT_WHEN } edit_control_t; -static GtkEntry *location, *buddy, *divemaster, *rating, *suit; -static GtkTextView *notes; -static GtkListStore *location_list, *people_list, *star_list, *suit_list; - -static char *get_text(GtkTextView *view) -{ - GtkTextBuffer *buffer; - GtkTextIter start; - GtkTextIter end; - - buffer = gtk_text_view_get_buffer(view); - gtk_text_buffer_get_start_iter(buffer, &start); - gtk_text_buffer_get_end_iter(buffer, &end); - return gtk_text_buffer_get_text(buffer, &start, &end, FALSE); -} - /* old is NULL or a valid string, new is a valid string - * NOTW: NULL and "" need to be treated as "unchanged" */ -static int text_changed(const char *old, const char *new) + * NOTE: NULL and "" need to be treated as "unchanged" */ +int text_changed(const char *old, const char *new) { return (old && strcmp(old,new)) || (!old && strcmp("",new)); @@ -57,16 +45,14 @@ static const char *skip_space(const char *str) } /* - * Get the string from a combo box. - * + * should this string be changed? * The "master" string is the string of the current dive - we only consider it * changed if the old string is either empty, or matches that master string. */ -static char *get_combo_box_entry_text(GtkComboBox *combo_box, char **textp, const char *master) +char *evaluate_string_change(const char *newstring, char **textp, const char *master) { char *old = *textp; const char *old_text; - const gchar *new; old_text = skip_space(old); master = skip_space(master); @@ -81,23 +67,19 @@ static char *get_combo_box_entry_text(GtkComboBox *combo_box, char **textp, cons if (strcmp(master, old_text)) return NULL; - new = get_active_text(combo_box); - while (g_ascii_isspace(*new)) - new++; + while (g_ascii_isspace(*newstring)) + newstring++; /* If the master string didn't change, don't change other dives either! */ - if (!text_changed(master,new)) + if (!text_changed(master, newstring)) return NULL; - if (!text_changed(old,new)) + if (!text_changed(old, newstring)) return NULL; free(old); - *textp = strdup(new); + *textp = strdup(newstring); return *textp; } -#define SET_TEXT_VALUE(x) \ - gtk_entry_set_text(x, dive && dive->x ? dive->x : "") - -static int divename(char *buf, size_t size, struct dive *dive, char *trailer) +int divename(char *buf, size_t size, struct dive *dive, char *trailer) { struct tm tm; @@ -112,9 +94,9 @@ static int divename(char *buf, size_t size, struct dive *dive, char *trailer) trailer); } -void show_dive_info(struct dive *dive) +/* caller should free the string returned after it is no longer needed */ +const char *get_window_title(struct dive *dive) { - const char *subs = "Subsurface: "; const char *text; const int maxlen = 128; char *basename; @@ -124,262 +106,56 @@ void show_dive_info(struct dive *dive) if (!dive) { if (existing_filename) { + char *basename; basename = g_path_get_basename(existing_filename); - len1 = strlen(subs); + len1 = sizeof("Subsurface: "); len2 = g_utf8_strlen(basename, -1); - sz = (len1 + len2 + 1) * sizeof(gunichar); + sz = (len1 + len2) * sizeof(gunichar); title = malloc(sz); - strncpy(title, subs, len1); + strncpy(title, "Subsurface: ", len1); g_utf8_strncpy(title + len1, basename, len2); - gtk_window_set_title(GTK_WINDOW(main_window), title); - free(basename); - free(title); } else { - gtk_window_set_title(GTK_WINDOW(main_window), "Subsurface"); + title = strdup("Subsurface"); } - SET_TEXT_VALUE(divemaster); - SET_TEXT_VALUE(buddy); - SET_TEXT_VALUE(location); - SET_TEXT_VALUE(suit); - gtk_entry_set_text(rating, star_strings[0]); - gtk_text_buffer_set_text(gtk_text_view_get_buffer(notes), "", -1); - show_dive_equipment(NULL, W_IDX_PRIMARY); - return; - } - - /* dive number and location (or lacking that, the date) go in the window title */ - text = dive->location; - if (!text) - text = ""; - if (*text) { - if (dive->number) { - len1 = g_utf8_strlen(text, -1); - sz = (len1 + 32) * sizeof(gunichar); + } else { + /* dive number and location (or lacking that, the date) go in the window title */ + text = dive->location; + if (!text) + text = ""; + if (*text) { + if (dive->number) { + len1 = g_utf8_strlen(text, -1); + sz = (len1 + 32) * sizeof(gunichar); + buffer = malloc(sz); + snprintf(buffer, sz, _("Dive #%d - "), dive->number); + g_utf8_strncpy(buffer + strlen(buffer), text, len1); + text = buffer; + } + } else { + sz = (maxlen + 32) * sizeof(gunichar); buffer = malloc(sz); - snprintf(buffer, sz, _("Dive #%d - "), dive->number); - g_utf8_strncpy(buffer + strlen(buffer), text, len1); + divename(buffer, sz, dive, ""); text = buffer; } - } else { - sz = (maxlen + 32) * sizeof(gunichar); - buffer = malloc(sz); - divename(buffer, sz, dive, ""); - text = buffer; + /* put it all together */ + if (existing_filename) { + basename = g_path_get_basename(existing_filename); + len1 = g_utf8_strlen(basename, -1); + len2 = g_utf8_strlen(text, -1); + if (len2 > maxlen) + len2 = maxlen; + sz = (len1 + len2 + 3) * sizeof(gunichar); /* reserver space for ": " */ + title = malloc(sz); + g_utf8_strncpy(title, basename, len1); + strncpy(title + strlen(basename), (const char *)": ", 2); + g_utf8_strncpy(title + strlen(basename) + 2, text, len2); + } else { + title = strdup(text); + } + if (buffer) + free(buffer); } - - /* put it all together */ - if (existing_filename) { - basename = g_path_get_basename(existing_filename); - len1 = g_utf8_strlen(basename, -1); - len2 = g_utf8_strlen(text, -1); - if (len2 > maxlen) - len2 = maxlen; - sz = (len1 + len2 + 3) * sizeof(gunichar); /* reserver space for ": " */ - title = malloc(sz); - g_utf8_strncpy(title, basename, len1); - strncpy(title + strlen(basename), (const char *)": ", 2); - g_utf8_strncpy(title + strlen(basename) + 2, text, len2); - gtk_window_set_title(GTK_WINDOW(main_window), title); - free(basename); - free(title); - } else { - gtk_window_set_title(GTK_WINDOW(main_window), text); - } - if (buffer) - free(buffer); - SET_TEXT_VALUE(divemaster); - SET_TEXT_VALUE(buddy); - SET_TEXT_VALUE(location); - SET_TEXT_VALUE(suit); - gtk_entry_set_text(rating, star_strings[dive->rating]); - gtk_text_buffer_set_text(gtk_text_view_get_buffer(notes), - dive && dive->notes ? dive->notes : "", -1); -} - -static void info_menu_edit_cb(GtkMenuItem *menuitem, gpointer user_data) -{ - if (amount_selected) - edit_multi_dive_info(NULL); -} - -static void add_menu_item(GtkMenuShell *menu, const char *label, const char *icon, void (*cb)(GtkMenuItem *, gpointer)) -{ - GtkWidget *item; - if (icon) { - GtkWidget *image; - item = gtk_image_menu_item_new_with_label(label); - image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); - } else { - item = gtk_menu_item_new_with_label(label); - } - g_signal_connect(item, "activate", G_CALLBACK(cb), NULL); - gtk_widget_show(item); /* Yes, really */ - gtk_menu_shell_prepend(menu, item); -} - -static void populate_popup_cb(GtkTextView *entry, GtkMenuShell *menu, gpointer user_data) -{ - if (amount_selected) - add_menu_item(menu, _("Edit"), GTK_STOCK_EDIT, info_menu_edit_cb); -} - -static GtkEntry *text_value(GtkWidget *box, const char *label) -{ - GtkWidget *widget; - GtkWidget *frame = gtk_frame_new(label); - - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); - widget = gtk_entry_new(); - gtk_widget_set_can_focus(widget, FALSE); - gtk_editable_set_editable(GTK_EDITABLE(widget), FALSE); - gtk_container_add(GTK_CONTAINER(frame), widget); - g_signal_connect(widget, "populate-popup", G_CALLBACK(populate_popup_cb), NULL); - return GTK_ENTRY(widget); -} - -static GtkEntry *single_text_entry(GtkWidget *box, const char *label, const char *text) -{ - GtkEntry *entry; - GtkWidget *frame = gtk_frame_new(label); - - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); - entry = GTK_ENTRY(gtk_entry_new()); - gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(entry)); - if (text && *text) - gtk_entry_set_text(entry, text); - return entry; -} - -static GtkComboBox *text_entry(GtkWidget *box, const char *label, GtkListStore *completions, const char *text) -{ - GtkWidget *combo_box; - GtkWidget *frame = gtk_frame_new(label); - - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0); - - combo_box = combo_box_with_model_and_entry(completions); - gtk_container_add(GTK_CONTAINER(frame), combo_box); - - if (text && *text) - set_active_text(GTK_COMBO_BOX(combo_box), text); - - return GTK_COMBO_BOX(combo_box); -} - -enum writable { - READ_ONLY, - READ_WRITE -}; - -static GtkTextView *text_view(GtkWidget *box, const char *label, enum writable writable) -{ - GtkWidget *view, *vbox; - GtkWidget *frame = gtk_frame_new(label); - - gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0); - box = gtk_hbox_new(FALSE, 3); - gtk_container_add(GTK_CONTAINER(frame), box); - vbox = gtk_vbox_new(FALSE, 3); - gtk_container_add(GTK_CONTAINER(box), vbox); - - GtkWidget* scrolled_window = gtk_scrolled_window_new(0, 0); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_IN); - - view = gtk_text_view_new(); - if (writable == READ_ONLY) { - gtk_widget_set_can_focus(view, FALSE); - gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE); - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE); - g_signal_connect(view, "populate-popup", G_CALLBACK(populate_popup_cb), NULL); - } - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD); - gtk_container_add(GTK_CONTAINER(scrolled_window), view); - gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0); - return GTK_TEXT_VIEW(view); -} - -static GtkTreeIter string_entry_location; - -static gboolean match_string_entry(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) -{ - const char *string = data; - char *entry; - int cmp; - - gtk_tree_model_get(model, iter, 0, &entry, -1); - cmp = strcmp(entry, string); - if (entry) - free(entry); - - /* Stop. The entry is bigger than the new one */ - if (cmp > 0) - return TRUE; - - /* Exact match */ - if (!cmp) { - found_string_entry = MATCH_EXACT; - return TRUE; - } - - string_entry_location = *iter; - found_string_entry = MATCH_AFTER; - return FALSE; -} - -int match_list(GtkListStore *list, const char *string) -{ - found_string_entry = MATCH_PREPEND; - gtk_tree_model_foreach(GTK_TREE_MODEL(list), match_string_entry, (void *)string); - return found_string_entry; -} - -void add_string_list_entry(const char *string, GtkListStore *list) -{ - GtkTreeIter *iter, loc; - - if (!string || !*string) - return; - - switch (match_list(list, string)) { - case MATCH_EXACT: - return; - case MATCH_PREPEND: - iter = NULL; - break; - case MATCH_AFTER: - iter = &string_entry_location; - break; - } - gtk_list_store_insert_after(list, &loc, iter); - gtk_list_store_set(list, &loc, 0, string, -1); -} - -void add_people(const char *string) -{ - add_string_list_entry(string, people_list); -} - -void add_location(const char *string) -{ - add_string_list_entry(string, location_list); -} - -void add_suit(const char *string) -{ - add_string_list_entry(string, suit_list); -} - -static int get_rating(const char *string) -{ - int rating_val = 0; - int i; - - for (i = 0; i <= 5; i++) - if (!strcmp(star_strings[i],string)) - rating_val = i; - return rating_val; + return title; } /* this is used to skip the cardinal directions (or check if they are @@ -401,7 +177,7 @@ static int string_advance_cardinal(const char *text, const char *look) } /* this has to be done with UTF8 as people might want to enter the degree symbol */ -static gboolean parse_gps_text(const char *gps_text, double *latitude, double *longitude) +gboolean parse_gps_text(const char *gps_text, double *latitude, double *longitude) { const char *text = gps_text; char *endptr; @@ -494,7 +270,7 @@ static gboolean parse_gps_text(const char *gps_text, double *latitude, double *l return TRUE; } -static gboolean gps_changed(struct dive *dive, struct dive *master, const char *gps_text) +gboolean gps_changed(struct dive *dive, struct dive *master, const char *gps_text) { double latitude, longitude; int latudeg, longudeg; @@ -520,127 +296,9 @@ static gboolean gps_changed(struct dive *dive, struct dive *master, const char * return TRUE; } -struct dive_info { - GtkComboBox *location, *divemaster, *buddy, *rating, *suit, *viz; - GtkEntry *airtemp, *gps; - GtkWidget *gps_icon; - GtkTextView *notes; -}; - -static void save_dive_info_changes(struct dive *dive, struct dive *master, struct dive_info *info) -{ - char *old_text, *new_text; - const char *gps_text; - char *rating_string; - double newtemp; - int changed = 0; - - new_text = get_combo_box_entry_text(info->location, &dive->location, master->location); - if (new_text) { - add_location(new_text); - changed = 1; - } - - gps_text = gtk_entry_get_text(info->gps); - if (gps_changed(dive, master, gps_text)) - changed = 1; - - new_text = get_combo_box_entry_text(info->divemaster, &dive->divemaster, master->divemaster); - if (new_text) { - add_people(new_text); - changed = 1; - } - - new_text = get_combo_box_entry_text(info->buddy, &dive->buddy, master->buddy); - if (new_text) { - add_people(new_text); - changed = 1; - } - - new_text = get_combo_box_entry_text(info->suit, &dive->suit, master->suit); - if (new_text) { - add_suit(new_text); - changed = 1; - } - - rating_string = strdup(star_strings[dive->rating]); - new_text = get_combo_box_entry_text(info->rating, &rating_string, star_strings[master->rating]); - if (new_text) { - dive->rating = get_rating(rating_string); - changed = 1; - } - free(rating_string); - - rating_string = strdup(star_strings[dive->visibility]); - new_text = get_combo_box_entry_text(info->viz, &rating_string, star_strings[master->visibility]); - if (new_text) { - dive->visibility = get_rating(rating_string); - changed = 1; - } - free(rating_string); - - new_text = (char *)gtk_entry_get_text(info->airtemp); - if (sscanf(new_text, "%lf", &newtemp) == 1) { - unsigned long mkelvin; - switch (prefs.units.temperature) { - case CELSIUS: - mkelvin = C_to_mkelvin(newtemp); - break; - case FAHRENHEIT: - mkelvin = F_to_mkelvin(newtemp); - break; - default: - mkelvin = 0; - } - if (mkelvin != dive->airtemp.mkelvin && dive->airtemp.mkelvin == master->airtemp.mkelvin) { - dive->airtemp.mkelvin = mkelvin; - changed = 1; - } - } - - if (info->notes) { - old_text = dive->notes; - dive->notes = get_text(info->notes); - if (text_changed(old_text,dive->notes)) - changed = 1; - if (old_text) - g_free(old_text); - } - if (changed) { - mark_divelist_changed(TRUE); - update_dive(dive); - } -} - -static void dive_trip_widget(GtkWidget *box, dive_trip_t *trip, struct dive_info *info) -{ - GtkWidget *hbox, *label; - char buffer[128] = N_("Edit trip summary"); - - label = gtk_label_new(_(buffer)); - gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0); - - info->location = text_entry(box, _("Location"), location_list, trip->location); - - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); - - info->notes = text_view(box, _("Notes"), READ_WRITE); - if (trip->notes && *trip->notes) - gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), trip->notes, -1); -} - -struct location_update { - char text[45]; - char set_by_hand; - GtkEntry *entry; - struct dive *dive; - void (*callback)(float, float); -} location_update; - /* take latitude and longitude in udeg and print them in a human readable * form, without losing precision */ -static void print_gps_coordinates(char *buffer, int len, int lat, int lon) +void print_gps_coordinates(char *buffer, int len, int lat, int lon) { unsigned int latdeg, londeg; double latmin, lonmin; @@ -670,232 +328,13 @@ static void print_gps_coordinates(char *buffer, int len, int lat, int lon) lonh, londeg, UTF8_DEGREE, dbuf_lon); } -static void update_gps_entry(int lat, int lon) -{ - if (location_update.entry) { - print_gps_coordinates(location_update.text, 45, lat, lon); - gtk_entry_set_text(location_update.entry, location_update.text); - } -} - -#if HAVE_OSM_GPS_MAP -static void update_gps_entry_callback(float lat, float lon) -{ - update_gps_entry(lat * 1000000, lon * 1000000); - location_update.set_by_hand = 1; -} - -static gboolean gps_map_callback(GtkWidget *w, gpointer data) -{ - double latitude, longitude; - const char *gps_text = NULL; - struct dive fake_dive; - - memset(&fake_dive, 0, sizeof(fake_dive)); - if (location_update.entry) { - gps_text = gtk_entry_get_text(location_update.entry); - parse_gps_text(gps_text, &latitude, &longitude); - fake_dive.latitude.udeg = rint(latitude * 1000000); - fake_dive.longitude.udeg = rint(longitude * 1000000); - } - show_gps_location(&fake_dive, update_gps_entry_callback); - return TRUE; -} -#endif - -/* - * If somebody sets the string by editing the text entry, - * we consider a clear string an opportunity to set things - * automatically. - * - * A non-empty string, on the other hand, means that we - * should *not* touch it when we change the location field. - */ -static gboolean gps_entry_change_cb(GtkEntry *gps, GdkEvent *event, gpointer userdata) -{ - const char *string = gtk_entry_get_text(gps); - - /* A clear string is never considered to be "set" */ - if (!string) { - location_update.set_by_hand = 0; - return FALSE; - } - - /* - * If it wasn't set by hand, and it hasn't changed, - * it's still not set by hand - */ - if (!location_update.set_by_hand) { - if (!strcmp(location_update.text, string)) - return FALSE; - } - - /* Otherwise, check if it's all empty.. */ - while (g_ascii_isspace(*string)) - string++; - location_update.set_by_hand = !!*string; - - return FALSE; -} - -static void location_entry_change_cb(GtkComboBox *location, gpointer *userdata) -{ - int i; - struct dive *dive; - const char *name; - - /* - * Don't do any automatic gps changes of entries that have been - * explicitly set to some value! - */ - if (location_update.set_by_hand) - return; - - name = get_active_text(location); - for_each_dive(i, dive) { - if (!dive_has_gps_location(dive)) - continue; - if (!dive->location || strcasecmp(dive->location, name)) - continue; - update_gps_entry(dive->latitude.udeg, dive->longitude.udeg); - return; - } - update_gps_entry(0, 0); -} - -static void set_dive_button_label(GtkWidget *button, struct dive *dive) -{ - char buffer[256]; - - /* if there is only one dc and it has no samples we can edit the depth, too */ - if (dive->dc.next || dive->dc.samples) - divename(buffer, sizeof(buffer), dive, _("(click to edit date/time)")); - else - divename(buffer, sizeof(buffer), dive, _("(click to edit date/time/depth)")); - gtk_button_set_label(GTK_BUTTON(button), buffer); -} - -static int dive_time_widget(struct dive *dive, edit_control_t editing); - -static gboolean base_data_cb(GtkWidget *w, GdkEvent *event, gpointer _data) -{ - struct dive *dive = _data; - - /* if there are more than one divecomputers or if there are any sample - * then only the start time (well, date and time) can be changed, - * otherwise (this is most likely a dive that was added manually in Subsurface - * and we can edit duration, max and mean depth, too */ - if (dive->dc.next || dive->dc.samples) - dive_time_widget(dive, EDIT_WHEN); - else - dive_time_widget(dive, EDIT_ALL); - set_dive_button_label(w, dive); - return FALSE; -} - -static void dive_info_widget(GtkWidget *obox, struct dive *dive, struct dive_info *info, gboolean multi) -{ - GtkWidget *hbox, *frame, *equipment, *ibox, *box; -#if HAVE_OSM_GPS_MAP - GtkWidget *image; -#endif - char buffer[256]; - char airtemp[10]; - const char *unit; - double value; - - if (multi) { - GtkWidget *label; - snprintf(buffer, sizeof(buffer), "%s", _("Edit multiple dives")); - label = gtk_label_new(buffer); - gtk_box_pack_start(GTK_BOX(obox), label, FALSE, TRUE, 0); - } else { - GtkWidget *basedata = gtk_button_new_with_label(buffer); - set_dive_button_label(basedata, dive); - g_signal_connect(G_OBJECT(basedata), "button-press-event", G_CALLBACK(base_data_cb), dive); - gtk_box_pack_start(GTK_BOX(obox), basedata, FALSE, TRUE, 0); - } - /* two column layout (inner hbox ibox) within the outer vbox (obox) we are given */ - ibox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(obox), ibox, TRUE, TRUE, 0); - box = gtk_vbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(ibox), box, TRUE, TRUE, 0); - - info->location = text_entry(box, _("Location"), location_list, dive->location); - g_signal_connect(G_OBJECT(info->location), "changed", G_CALLBACK(location_entry_change_cb), NULL); - - hbox = gtk_hbox_new(FALSE, 2); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); - info->gps = single_text_entry(hbox, _("GPS (WGS84 or GPS format)"), NULL); - - location_update.entry = info->gps; - location_update.dive = dive; - update_gps_entry(dive->latitude.udeg, dive->longitude.udeg); - location_update.set_by_hand = !!location_update.text[0]; - - gtk_widget_add_events(GTK_WIDGET(info->gps), GDK_FOCUS_CHANGE_MASK); - g_signal_connect(G_OBJECT(info->gps), "focus-out-event", G_CALLBACK(gps_entry_change_cb), NULL); - gtk_entry_set_width_chars(info->gps, 30); -#if HAVE_OSM_GPS_MAP - info->gps_icon = gtk_button_new_with_label(_("Pick on map")); - gtk_box_pack_start(GTK_BOX(hbox), info->gps_icon, FALSE, FALSE, 6); - image = gtk_image_new_from_pixbuf(get_gps_icon()); - gtk_image_set_pixel_size(GTK_IMAGE(image), 128); - gtk_button_set_image(GTK_BUTTON(info->gps_icon), image); - - g_signal_connect(G_OBJECT(info->gps_icon), "clicked", G_CALLBACK(gps_map_callback), NULL); -#endif - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); - - info->divemaster = text_entry(hbox, _("Dive master"), people_list, dive->divemaster); - info->buddy = text_entry(hbox, _("Buddy"), people_list, dive->buddy); - - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); - - info->rating = text_entry(hbox, _("Rating"), star_list, star_strings[dive->rating]); - info->suit = text_entry(hbox, _("Suit"), suit_list, dive->suit); - - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); - - info->viz = text_entry(hbox, _("Visibility"), star_list, star_strings[dive->visibility]); - - value = get_temp_units(dive->airtemp.mkelvin, &unit); - snprintf(buffer, sizeof(buffer), _("Air Temp in %s"), unit); - if (dive->airtemp.mkelvin) - snprintf(airtemp, sizeof(airtemp), "%.1f", value); - else - airtemp[0] = '\0'; - info->airtemp = single_text_entry(hbox, buffer, airtemp); - - /* only show notes if editing a single dive */ - if (multi) { - info->notes = NULL; - } else { - info->notes = text_view(box, _("Notes"), READ_WRITE); - gtk_widget_set_size_request(GTK_WIDGET(info->notes), -1, 128); - if (dive->notes && *dive->notes) - gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1); - } - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(ibox), hbox, TRUE, TRUE, 0); - - /* create a secondary Equipment widget */ - frame = gtk_frame_new(_("Equipment")); - equipment = equipment_widget(W_IDX_SECONDARY); - gtk_container_add(GTK_CONTAINER(frame), equipment); - gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); -} - /* we use these to find out if we edited the cylinder or weightsystem entries */ static cylinder_t remember_cyl[MAX_CYLINDERS]; static weightsystem_t remember_ws[MAX_WEIGHTSYSTEMS]; #define CYL_BYTES sizeof(cylinder_t) * MAX_CYLINDERS #define WS_BYTES sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS -static void save_equipment_data(struct dive *dive) +void save_equipment_data(struct dive *dive) { if (dive) { memcpy(remember_cyl, dive->cylinder, CYL_BYTES); @@ -984,7 +423,7 @@ static void update_cylinder(cylinder_t *dst, cylinder_t *src, cylinder_t *orig) data if it has changed in the master dive and the other dive either has no entries for the equipment or the same entries as the master dive had before it was edited */ -static void update_equipment_data(struct dive *dive, struct dive *master) +void update_equipment_data(struct dive *dive, struct dive *master) { int i; @@ -998,52 +437,10 @@ static void update_equipment_data(struct dive *dive, struct dive *master) memcpy(dive->weightsystem, master->weightsystem, WS_BYTES); } -gboolean edit_trip(dive_trip_t *trip) -{ - GtkWidget *dialog, *vbox; - int success; - gboolean changed = FALSE; - char *old_text, *new_text; - struct dive_info info; - - memset(&info, 0, sizeof(struct dive_info)); - dialog = gtk_dialog_new_with_buttons(_("Edit Trip Info"), - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 300); - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - dive_trip_widget(vbox, trip, &info); - gtk_widget_show_all(dialog); - success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - if (success) { - /* we need to store the edited location and notes */ - new_text = get_combo_box_entry_text(info.location, &trip->location, trip->location); - if (new_text) { - add_location(new_text); - changed = TRUE; - } - if (info.notes) { - old_text = trip->notes; - trip->notes = get_text(info.notes); - if (text_changed(old_text, trip->notes)) - changed = TRUE; - if (old_text) - g_free(old_text); - } - if (changed) - mark_divelist_changed(TRUE); - } - gtk_widget_destroy(dialog); - return changed; -} - /* we can simply overwrite these - this only gets called if we edited * a single dive and the dive was first copied into edited - so we can * just take those values */ -static void update_time_depth(struct dive *dive, struct dive *edited) +void update_time_depth(struct dive *dive, struct dive *edited) { dive->when = edited->when; dive->dc.duration.seconds = edited->dc.duration.seconds; @@ -1051,103 +448,6 @@ static void update_time_depth(struct dive *dive, struct dive *edited) dive->dc.meandepth.mm = edited->dc.meandepth.mm; } -int edit_multi_dive_info(struct dive *single_dive) -{ - int success; - GtkWidget *dialog, *vbox, *scrolled_window, *viewport; - GtkRequisition size; - struct dive_info info; - struct dive *master; - gboolean multi; - - scrolled_window = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - dialog = gtk_dialog_new_with_buttons(_("Dive Info"), - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0); - vbox = g_object_new(GTK_TYPE_VBOX, NULL); - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), vbox); - master = single_dive; - if (!master) - master = current_dive; - if (!master) - return 0; - /* See if we should use multi dive mode */ - multi = FALSE; - if (!single_dive) { - int i; - struct dive *dive; - - for_each_dive(i, dive) { - if (dive != master && dive->selected) { - multi = TRUE; - break; - } - } - } - /* edit a temporary copy of the master dive; - * edit_dive is a global dive structure that is modified by the - * cylinder / weightsystem dialogs if we open W_IDX_SECONDARY - * edit widgets as we do here */ - memcpy(&edit_dive, master, sizeof(struct dive)); - - dive_info_widget(vbox, &edit_dive, &info, multi); - save_equipment_data(&edit_dive); - gtk_widget_show_all(dialog); - viewport = gtk_widget_get_ancestor(vbox, GTK_TYPE_VIEWPORT); -#if GTK_CHECK_VERSION(3,0,0) - gtk_widget_get_preferred_size(viewport, NULL, &size); -#else - gtk_widget_size_request(viewport, &size); -#endif - gtk_widget_set_size_request(scrolled_window, size.width, size.height); - /* add the equipment post the "blank" layout estimate */ - show_dive_equipment(&edit_dive, W_IDX_SECONDARY); - success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - if (success) { - mark_divelist_changed(TRUE); - /* Update the non-current selected dives first */ - if (!single_dive) { - int i; - struct dive *dive; - - for_each_dive(i, dive) { - if (dive == master || !dive->selected) - continue; - /* copy all "info" fields */ - save_dive_info_changes(dive, &edit_dive, &info); - /* copy the cylinders / weightsystems */ - update_equipment_data(dive, &edit_dive); - /* this is extremely inefficient... it loops through all - dives to find the right one - but we KNOW the index already */ - update_cylinder_related_info(dive); - flush_divelist(dive); - } - } - - /* Update the master dive last! */ - save_dive_info_changes(master, &edit_dive, &info); - update_equipment_data(master, &edit_dive); - update_cylinder_related_info(master); - /* if there was only one dive we might also have changed dive->when - * or even the duration and depth information (in a dive without samples) */ - if (! multi) - update_time_depth(master, &edit_dive); - dive_list_update_dives(); - } - gtk_widget_destroy(dialog); - location_update.entry = NULL; - - return success; -} - int edit_dive_info(struct dive *dive, gboolean newdive) { if (!dive || (!newdive && !amount_selected)) @@ -1155,231 +455,3 @@ int edit_dive_info(struct dive *dive, gboolean newdive) return edit_multi_dive_info(dive); } - -static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...) -{ - va_list ap; - char buffer[128]; - GtkWidget *frame, *hbox; - - va_start(ap, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, ap); - va_end(ap); - - frame = gtk_frame_new(buffer); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0); - hbox = gtk_hbox_new(0, 3); - gtk_container_add(GTK_CONTAINER(frame), hbox); - return hbox; -} - -/* returns the dialog plus pointers to the calendar, hour and minute widget - * plus the hbox that holds the time entry (in case the caller wants to put - * a duration entry widget next to the time entry widget */ -GtkWidget *create_date_time_widget(struct tm *time, GtkWidget **cal, GtkWidget **h, - GtkWidget **m, GtkWidget **timehbox) -{ - GtkWidget *dialog; - GtkWidget *hbox, *vbox; - GtkWidget *label; - - dialog = gtk_dialog_new_with_buttons(_("Date and Time"), - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - - /* Calendar hbox */ - hbox = frame_box(vbox, _("Date:")); - *cal = gtk_calendar_new(); - gtk_box_pack_start(GTK_BOX(hbox), *cal, FALSE, TRUE, 0); - - /* Time hbox */ - *timehbox = gtk_hbox_new(TRUE, 3); - gtk_box_pack_start(GTK_BOX(vbox), *timehbox, FALSE, FALSE, 0); - hbox = frame_box(*timehbox, _("Time")); - - *h = gtk_spin_button_new_with_range (0.0, 23.0, 1.0); - *m = gtk_spin_button_new_with_range (0.0, 59.0, 1.0); - - gtk_calendar_select_month(GTK_CALENDAR(*cal), time->tm_mon, time->tm_year + 1900); - gtk_calendar_select_day(GTK_CALENDAR(*cal), time->tm_mday); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(*h), time->tm_hour); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(*m), time->tm_min); - - gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(*h), TRUE); - gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(*m), TRUE); - - gtk_box_pack_end(GTK_BOX(hbox), *m, FALSE, FALSE, 0); - label = gtk_label_new(":"); - gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_box_pack_end(GTK_BOX(hbox), *h, FALSE, FALSE, 0); - - return dialog; -} - -static int mm_from_spinbutton(GtkWidget *depth) -{ - int result; - double val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(depth)); - if (prefs.units.length == FEET) { - result = feet_to_mm(val); - } else { - result = val * 1000 + 0.5; - } - return result; -} - -static int dive_time_widget(struct dive *dive, edit_control_t editing) -{ - GtkWidget *dialog; - GtkWidget *cal, *vbox, *hbox, *box; - GtkWidget *h, *m; - GtkWidget *duration, *depth, *avgdepth; - guint yval, mval, dval; - struct tm tm, *time; - int success; - double depthinterval; - - /* - * If we have a dive selected, 'add dive' will default - * to one hour after the end of that dive. Otherwise, - * we'll just take the current time. - */ - if (editing != EDIT_NEW_DIVE) { - utc_mkdate(dive->when, &tm); - time = &tm; - } else if (amount_selected == 1) { - timestamp_t when = current_dive->when; - when += current_dive->duration.seconds; - when += 60*60; - utc_mkdate(when, &tm); - time = &tm; - } else { - time_t now; - struct timeval tv; - gettimeofday(&tv, NULL); - now = tv.tv_sec; - time = localtime(&now); - } - dialog = create_date_time_widget(time, &cal, &h, &m, &hbox); - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - - if (editing != EDIT_WHEN) { - /* Duration box */ - box = frame_box(hbox, _("Duration (min)")); - duration = gtk_spin_button_new_with_range (0.0, 1000.0, 1.0); - if (editing != EDIT_NEW_DIVE) - gtk_spin_button_set_value(GTK_SPIN_BUTTON(duration), dive->dc.duration.seconds / 60.0); - gtk_box_pack_end(GTK_BOX(box), duration, FALSE, FALSE, 0); - - hbox = gtk_hbox_new(TRUE, 3); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - - /* Depth box */ - box = frame_box(hbox, _("Max Depth (%s):"), prefs.units.length == FEET ? _("ft") : _("m")); - if (prefs.units.length == FEET) { - depthinterval = 1.0; - } else { - depthinterval = 0.1; - } - depth = gtk_spin_button_new_with_range (0.0, 1000.0, depthinterval); - if (editing != EDIT_NEW_DIVE) - gtk_spin_button_set_value(GTK_SPIN_BUTTON(depth), dive->dc.maxdepth.mm / 1000.0); - gtk_box_pack_end(GTK_BOX(box), depth, FALSE, FALSE, 0); - - box = frame_box(hbox, _("Avg Depth (%s):"), prefs.units.length == FEET ? _("ft") : _("m")); - if (prefs.units.length == FEET) { - depthinterval = 1.0; - } else { - depthinterval = 0.1; - } - avgdepth = gtk_spin_button_new_with_range (0.0, 1000.0, depthinterval); - if (editing != EDIT_NEW_DIVE) - gtk_spin_button_set_value(GTK_SPIN_BUTTON(avgdepth), dive->dc.meandepth.mm / 1000.0); - gtk_box_pack_end(GTK_BOX(box), avgdepth, FALSE, FALSE, 0); - } - /* All done, show it and wait for editing */ - gtk_widget_show_all(dialog); - success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - if (!success) { - gtk_widget_destroy(dialog); - return 0; - } - - memset(&tm, 0, sizeof(tm)); - gtk_calendar_get_date(GTK_CALENDAR(cal), &yval, &mval, &dval); - tm.tm_year = yval; - tm.tm_mon = mval; - tm.tm_mday = dval; - - tm.tm_hour = gtk_spin_button_get_value(GTK_SPIN_BUTTON(h)); - tm.tm_min = gtk_spin_button_get_value(GTK_SPIN_BUTTON(m)); - - if (editing != EDIT_WHEN) { - dive->dc.maxdepth.mm = mm_from_spinbutton(depth); - dive->dc.meandepth.mm = mm_from_spinbutton(avgdepth); - dive->dc.duration.seconds = gtk_spin_button_get_value(GTK_SPIN_BUTTON(duration))*60; - } - gtk_widget_destroy(dialog); - dive->when = utc_mktime(&tm); - - return 1; -} - -int add_new_dive(struct dive *dive) -{ - if (!dive) - return 0; - - if (!dive_time_widget(dive, EDIT_NEW_DIVE)) - return 0; - - return edit_dive_info(dive, TRUE); -} - -GtkWidget *extended_dive_info_widget(void) -{ - GtkWidget *vbox, *hbox; - vbox = gtk_vbox_new(FALSE, 6); - - people_list = gtk_list_store_new(1, G_TYPE_STRING); - location_list = gtk_list_store_new(1, G_TYPE_STRING); - star_list = gtk_list_store_new(1, G_TYPE_STRING); - add_string_list_entry(star_strings[0], star_list); - add_string_list_entry(star_strings[1], star_list); - add_string_list_entry(star_strings[2], star_list); - add_string_list_entry(star_strings[3], star_list); - add_string_list_entry(star_strings[4], star_list); - add_string_list_entry(star_strings[5], star_list); - suit_list = gtk_list_store_new(1, G_TYPE_STRING); - - gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); - location = text_value(vbox, _("Location")); - - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); - - divemaster = text_value(hbox, _("Divemaster")); - buddy = text_value(hbox, _("Buddy")); - - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); - - rating = text_value(hbox, _("Rating")); - suit = text_value(hbox, _("Suit")); - - notes = text_view(vbox, _("Notes"), READ_ONLY); - return vbox; -} - -void info_widget_destroy(void) -{ - g_object_unref(people_list); - g_object_unref(location_list); - g_object_unref(star_list); - g_object_unref(suit_list); -} diff --git a/info.h b/info.h new file mode 100644 index 000000000..a27373d89 --- /dev/null +++ b/info.h @@ -0,0 +1,20 @@ +/* + * info.h + * + * logic functions used from info-gtk.c + */ +#ifndef INFO_H +#define INFO_H + +extern gboolean gps_changed(struct dive *dive, struct dive *master, const char *gps_text); +extern void print_gps_coordinates(char *buffer, int len, int lat, int lon); +extern void save_equipment_data(struct dive *dive); +extern void update_equipment_data(struct dive *dive, struct dive *master); +extern void update_time_depth(struct dive *dive, struct dive *edited); +extern const char *get_window_title(struct dive *dive); +extern char *evaluate_string_change(const char *newstring, char **textp, const char *master); +extern int text_changed(const char *old, const char *new); +extern gboolean parse_gps_text(const char *gps_text, double *latitude, double *longitude); +extern int divename(char *buf, size_t size, struct dive *dive, char *trailer); + +#endif From 944d286132fe9529f7f6281c71b291af3ed937bc Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 14 Apr 2013 20:10:25 -0700 Subject: [PATCH 033/226] Separate Gtk related code from core logic: statistics Fairly straight forward, so far just one tiny bit of code restructuring, everything else separated cleanly. Added statistics-gtk.c and statistics.h This should make no difference to functionality. Signed-off-by: Dirk Hohndel --- Makefile | 2 +- statistics-gtk.c | 630 ++++++++++++++++++++++++++++++++++++++++++++ statistics.c | 660 +---------------------------------------------- statistics.h | 33 +++ 4 files changed, 678 insertions(+), 647 deletions(-) create mode 100644 statistics-gtk.c create mode 100644 statistics.h diff --git a/Makefile b/Makefile index af33dde33..d64be2edf 100644 --- a/Makefile +++ b/Makefile @@ -189,7 +189,7 @@ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.m QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ qt-ui/divetripmodel.o qt-ui/addcylinderdialog.o qt-ui/models.o -GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o +GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o planner.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ diff --git a/statistics-gtk.c b/statistics-gtk.c new file mode 100644 index 000000000..775a3bf3b --- /dev/null +++ b/statistics-gtk.c @@ -0,0 +1,630 @@ +/* statistics-gtk.c */ +/* creates the UI for the Info & Stats page - + * controlled through the following interfaces: + * + * void show_dive_stats(struct dive *dive) + * + * called from gtk-ui: + * GtkWidget *stats_widget(void) + */ +#include +#include + +#include "dive.h" +#include "display.h" +#include "display-gtk.h" +#include "divelist.h" +#include "statistics.h" + +typedef struct { + GtkWidget *date, + *dive_time, + *surf_intv, + *max_depth, + *avg_depth, + *viz, + *water_temp, + *air_temp, + *air_press, + *sac, + *otu, + *o2he, + *gas_used; +} single_stat_widget_t; + +static single_stat_widget_t single_w; + +typedef struct { + GtkWidget *total_time, + *avg_time, + *shortest_time, + *longest_time, + *max_overall_depth, + *min_overall_depth, + *avg_overall_depth, + *min_sac, + *avg_sac, + *max_sac, + *selection_size, + *max_temp, + *avg_temp, + *min_temp, + *framelabel; +} total_stats_widget_t; + +static total_stats_widget_t stats_w; + +GtkWidget *yearly_tree = NULL; + +enum { + YEAR, + DIVES, + TOTAL_TIME, + AVERAGE_TIME, + SHORTEST_TIME, + LONGEST_TIME, + AVG_DEPTH, + MIN_DEPTH, + MAX_DEPTH, + AVG_SAC, + MIN_SAC, + MAX_SAC, + AVG_TEMP, + MIN_TEMP, + MAX_TEMP, + N_COLUMNS +}; + +static void init_tree() +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeStore *store; + int i; + PangoFontDescription *font_desc = pango_font_description_from_string(prefs.divelist_font); + + gtk_widget_modify_font(yearly_tree, font_desc); + pango_font_description_free(font_desc); + + renderer = gtk_cell_renderer_text_new (); + /* don't use empty strings "" - they confuse gettext */ + char *columnstop[] = { N_("Year"), N_("#"), N_("Duration"), " ", " ", " ", N_("Depth"), " ", " ", N_("SAC"), " ", " ", N_("Temperature"), " ", " " }; + const char *columnsbot[15]; + columnsbot[0] = C_("Stats", " > Month"); + columnsbot[1] = " "; + columnsbot[2] = C_("Duration","Total"); + columnsbot[3] = C_("Duration","Average"); + columnsbot[4] = C_("Duration","Shortest"); + columnsbot[5] = C_("Duration","Longest"); + columnsbot[6] = C_("Depth", "Average"); + columnsbot[7] = C_("Depth","Minimum"); + columnsbot[8] = C_("Depth","Maximum"); + columnsbot[9] = C_("SAC","Average"); + columnsbot[10]= C_("SAC","Minimum"); + columnsbot[11]= C_("SAC","Maximum"); + columnsbot[12]= C_("Temp","Average"); + columnsbot[13]= C_("Temp","Minimum"); + columnsbot[14]= C_("Temp","Maximum"); + + /* Add all the columns to the tree view */ + for (i = 0; i < N_COLUMNS; ++i) { + char buf[256]; + column = gtk_tree_view_column_new(); + snprintf(buf, sizeof(buf), "%s\n%s", _(columnstop[i]), columnsbot[i]); + gtk_tree_view_column_set_title(column, buf); + gtk_tree_view_append_column(GTK_TREE_VIEW(yearly_tree), column); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "text", i); + gtk_tree_view_column_set_resizable(column, TRUE); + } + + /* Field types */ + store = gtk_tree_store_new ( + N_COLUMNS, // Columns in structure + G_TYPE_STRING, // Period (year or month) + G_TYPE_STRING, // Number of dives + G_TYPE_STRING, // Total duration + G_TYPE_STRING, // Average dive duation + G_TYPE_STRING, // Shortest dive + G_TYPE_STRING, // Longest dive + G_TYPE_STRING, // Average depth + G_TYPE_STRING, // Shallowest dive + G_TYPE_STRING, // Deepest dive + G_TYPE_STRING, // Average air consumption (SAC) + G_TYPE_STRING, // Minimum SAC + G_TYPE_STRING, // Maximum SAC + G_TYPE_STRING, // Average temperature + G_TYPE_STRING, // Minimum temperature + G_TYPE_STRING // Maximum temperature + ); + + gtk_tree_view_set_model (GTK_TREE_VIEW (yearly_tree), GTK_TREE_MODEL (store)); + g_object_unref (store); +} + +static void add_row_to_tree(GtkTreeStore *store, char *value, int index, GtkTreeIter *row_iter, GtkTreeIter *parent) +{ + gtk_tree_store_append(store, row_iter, parent); + gtk_tree_store_set(store, row_iter, index, value, -1); +} + +static void add_cell_to_tree(GtkTreeStore *store, char *value, int index, GtkTreeIter *parent) +{ + gtk_tree_store_set(store, parent, index, value, -1); +} + +static void add_cell(GtkTreeStore *store, GtkTreeIter *parent, unsigned int val, int cell, gboolean depth_not_volume) +{ + double value; + int decimals; + const char *unit; + char value_str[40]; + + if (depth_not_volume) { + value = get_depth_units(val, &decimals, &unit); + snprintf(value_str, sizeof(value_str), "%.*f %s", decimals, value, unit); + } else { + value = get_volume_units(val, &decimals, &unit); + snprintf(value_str, sizeof(value_str), _("%.*f %s/min"), decimals, value, unit); + } + add_cell_to_tree(store, value_str, cell, parent); +} + +static void process_interval_stats(stats_t stats_interval, GtkTreeIter *parent, GtkTreeIter *row) +{ + double value; + const char *unit; + char value_str[40]; + GtkTreeStore *store; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(yearly_tree))); + + /* Year or month */ + snprintf(value_str, sizeof(value_str), "%d", stats_interval.period); + add_row_to_tree(store, value_str, 0, row, parent); + /* Dives */ + snprintf(value_str, sizeof(value_str), "%d", stats_interval.selection_size); + add_cell_to_tree(store, value_str, 1, row); + /* Total duration */ + add_cell_to_tree(store, get_time_string(stats_interval.total_time.seconds, 0), 2, row); + /* Average dive duration */ + add_cell_to_tree(store, get_minutes(stats_interval.total_time.seconds / stats_interval.selection_size), 3, row); + /* Shortest duration */ + add_cell_to_tree(store, get_minutes(stats_interval.shortest_time.seconds), 4, row); + /* Longest duration */ + add_cell_to_tree(store, get_minutes(stats_interval.longest_time.seconds), 5, row); + /* Average depth */ + add_cell(store, row, stats_interval.avg_depth.mm, 6, TRUE); + /* Smallest maximum depth */ + add_cell(store, row, stats_interval.min_depth.mm, 7, TRUE); + /* Deepest maximum depth */ + add_cell(store, row, stats_interval.max_depth.mm, 8, TRUE); + /* Average air consumption */ + add_cell(store, row, stats_interval.avg_sac.mliter, 9, FALSE); + /* Smallest average air consumption */ + add_cell(store, row, stats_interval.min_sac.mliter, 10, FALSE); + /* Biggest air consumption */ + add_cell(store, row, stats_interval.max_sac.mliter, 11, FALSE); + /* Average water temperature */ + value = get_temp_units(stats_interval.min_temp, &unit); + if (stats_interval.combined_temp && stats_interval.combined_count) { + snprintf(value_str, sizeof(value_str), "%.1f %s", stats_interval.combined_temp / stats_interval.combined_count, unit); + add_cell_to_tree(store, value_str, 12, row); + } else { + add_cell_to_tree(store, "", 12, row); + } + /* Coldest water temperature */ + if (value > -100.0) { + snprintf(value_str, sizeof(value_str), "%.1f %s\t", value, unit); + add_cell_to_tree(store, value_str, 13, row); + } else { + add_cell_to_tree(store, "", 13, row); + } + /* Warmest water temperature */ + value = get_temp_units(stats_interval.max_temp, &unit); + if (value > -100.0) { + snprintf(value_str, sizeof(value_str), "%.1f %s", value, unit); + add_cell_to_tree(store, value_str, 14, row); + } else { + add_cell_to_tree(store, "", 14, row); + } +} + +static void clear_statistics() +{ + GtkTreeStore *store; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(yearly_tree))); + gtk_tree_store_clear(store); + yearly_tree = NULL; +} + +static gboolean stat_on_delete(GtkWidget *window, GdkEvent *event, gpointer data) +{ + clear_statistics(); + gtk_widget_destroy(window); + return TRUE; +} + +static void key_press_event(GtkWidget *window, GdkEventKey *event, gpointer data) +{ + if ((event->string != NULL && event->keyval == GDK_Escape) || + (event->string != NULL && event->keyval == GDK_w && event->state & GDK_CONTROL_MASK)) { + clear_statistics(); + gtk_widget_destroy(window); + } +} + +static void update_yearly_stats() +{ + int i, j, combined_months, month = 0; + GtkTreeIter year_iter, month_iter; + GtkTreeStore *store; + + store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(yearly_tree))); + gtk_tree_store_clear(store); + + for (i = 0; stats_yearly != NULL && stats_yearly[i].period; ++i) { + process_interval_stats(stats_yearly[i], NULL, &year_iter); + combined_months = 0; + + for (j = 0; combined_months < stats_yearly[i].selection_size; ++j) { + combined_months += stats_monthly[month].selection_size; + process_interval_stats(stats_monthly[month], &year_iter, &month_iter); + month++; + } + } +} + +void show_yearly_stats() +{ + GtkWidget *window; + GtkWidget *sw; + + if (yearly_tree) + return; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + sw = gtk_scrolled_window_new (NULL, NULL); + yearly_tree = gtk_tree_view_new (); + + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_default_size(GTK_WINDOW(window), 640, 480); + gtk_window_set_title(GTK_WINDOW(window), _("Yearly Statistics")); + gtk_container_set_border_width(GTK_CONTAINER(window), 5); + gtk_window_set_resizable(GTK_WINDOW(window), TRUE); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN); + + gtk_container_add (GTK_CONTAINER (sw), yearly_tree); + gtk_container_add (GTK_CONTAINER (window), sw); + + /* Display the yearly statistics on top level + * Monthly statistics are available by expanding a year */ + init_tree(); + update_yearly_stats(); + + g_signal_connect (G_OBJECT (window), "key_press_event", G_CALLBACK (key_press_event), NULL); + g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (stat_on_delete), NULL); + gtk_widget_show_all(window); +} + +static void set_label(GtkWidget *w, const char *fmt, ...) +{ + char buf[256]; + va_list args; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + gtk_label_set_text(GTK_LABEL(w), buf); +} + +/* we try to show the data from the currently selected divecomputer + * right now for some values (e.g., surface pressure) we could fall back + * to dive data, but for consistency we don't. */ +static void show_single_dive_stats(struct dive *dive) +{ + char buf[256]; + double value; + int decimals; + const char *unit; + int idx, offset, gas_used, mbar; + struct dive *prev_dive; + struct tm tm; + struct divecomputer *dc; + + process_all_dives(dive, &prev_dive); + if (yearly_tree) + update_yearly_stats(); + if (!dive) + return; + dc = select_dc(&dive->dc); + utc_mkdate(dive->when, &tm); + snprintf(buf, sizeof(buf), + /*++GETTEXT 80 chars: weekday, monthname, day, year, hour, min */ + _("%1$s, %2$s %3$d, %4$d %5$2d:%6$02d"), + weekday(tm.tm_wday), + monthname(tm.tm_mon), + tm.tm_mday, tm.tm_year + 1900, + tm.tm_hour, tm.tm_min); + + set_label(single_w.date, buf); + set_label(single_w.dive_time, _("%d min"), (dive->duration.seconds + 30) / 60); + if (prev_dive) + set_label(single_w.surf_intv, + get_time_string(dive->when - (prev_dive->when + prev_dive->duration.seconds), 4)); + else + set_label(single_w.surf_intv, _("unknown")); + value = get_depth_units(dc->maxdepth.mm, &decimals, &unit); + set_label(single_w.max_depth, "%.*f %s", decimals, value, unit); + value = get_depth_units(dc->meandepth.mm, &decimals, &unit); + set_label(single_w.avg_depth, "%.*f %s", decimals, value, unit); + set_label(single_w.viz, star_strings[dive->visibility]); + if (dc->watertemp.mkelvin) { + value = get_temp_units(dc->watertemp.mkelvin, &unit); + set_label(single_w.water_temp, "%.1f %s", value, unit); + } else { + set_label(single_w.water_temp, ""); + } + if (dc->airtemp.mkelvin) { + value = get_temp_units(dc->airtemp.mkelvin, &unit); + set_label(single_w.air_temp, "%.1f %s", value, unit); + } else { + if (dive->airtemp.mkelvin) { + value = get_temp_units(dive->airtemp.mkelvin, &unit); + set_label(single_w.air_temp, "%.1f %s", value, unit); + } else { + set_label(single_w.air_temp, ""); + } + } + mbar = dc->surface_pressure.mbar; + /* it would be easy to get dive data here: + * if (!mbar) + * mbar = get_surface_pressure_in_mbar(dive, FALSE); + */ + if (mbar) { + set_label(single_w.air_press, "%d mbar", mbar); + } else { + set_label(single_w.air_press, ""); + } + value = get_volume_units(dive->sac, &decimals, &unit); + if (value > 0) + set_label(single_w.sac, _("%.*f %s/min"), decimals, value, unit); + else + set_label(single_w.sac, ""); + set_label(single_w.otu, "%d", dive->otu); + offset = 0; + gas_used = 0; + buf[0] = '\0'; + /* for the O2/He readings just create a list of them */ + for (idx = 0; idx < MAX_CYLINDERS; idx++) { + cylinder_t *cyl = &dive->cylinder[idx]; + pressure_t start, end; + + start = cyl->start.mbar ? cyl->start : cyl->sample_start; + end = cyl->end.mbar ?cyl->sample_end : cyl->sample_end; + if (!cylinder_none(cyl)) { + /* 0% O2 strangely means air, so 21% - I don't like that at all */ + int o2 = get_o2(&cyl->gasmix); + int he = get_he(&cyl->gasmix); + if (offset > 0) { + snprintf(buf+offset, 80-offset, ", "); + offset += 2; + } + snprintf(buf+offset, 80-offset, "%d/%d", (o2 + 5) / 10, (he + 5) / 10); + offset = strlen(buf); + } + /* and if we have size, start and end pressure, we can + * calculate the total gas used */ + if (start.mbar && end.mbar) + gas_used += gas_volume(cyl, start) - gas_volume(cyl, end); + } + set_label(single_w.o2he, buf); + if (gas_used) { + value = get_volume_units(gas_used, &decimals, &unit); + set_label(single_w.gas_used, "%.*f %s", decimals, value, unit); + } else { + set_label(single_w.gas_used, ""); + } +} + +static void show_total_dive_stats(void) +{ + double value; + int decimals, seconds; + const char *unit; + char buffer[60]; + stats_t *stats_ptr; + + if (!stats_w.framelabel) + return; + stats_ptr = &stats_selection; + + get_selected_dives_text(buffer, sizeof(buffer)); + set_label(stats_w.framelabel, _("Statistics %s"), buffer); + set_label(stats_w.selection_size, "%d", stats_ptr->selection_size); + if (stats_ptr->selection_size == 0) { + clear_stats_widgets(); + return; + } + if (stats_ptr->min_temp) { + value = get_temp_units(stats_ptr->min_temp, &unit); + set_label(stats_w.min_temp, "%.1f %s", value, unit); + } + if (stats_ptr->combined_temp && stats_ptr->combined_count) + set_label(stats_w.avg_temp, "%.1f %s", stats_ptr->combined_temp / stats_ptr->combined_count, unit); + if (stats_ptr->max_temp) { + value = get_temp_units(stats_ptr->max_temp, &unit); + set_label(stats_w.max_temp, "%.1f %s", value, unit); + } + set_label(stats_w.total_time, get_time_string(stats_ptr->total_time.seconds, 0)); + seconds = stats_ptr->total_time.seconds; + if (stats_ptr->selection_size) + seconds /= stats_ptr->selection_size; + set_label(stats_w.avg_time, get_time_string(seconds, 0)); + set_label(stats_w.longest_time, get_time_string(stats_ptr->longest_time.seconds, 0)); + set_label(stats_w.shortest_time, get_time_string(stats_ptr->shortest_time.seconds, 0)); + value = get_depth_units(stats_ptr->max_depth.mm, &decimals, &unit); + set_label(stats_w.max_overall_depth, "%.*f %s", decimals, value, unit); + value = get_depth_units(stats_ptr->min_depth.mm, &decimals, &unit); + set_label(stats_w.min_overall_depth, "%.*f %s", decimals, value, unit); + value = get_depth_units(stats_ptr->avg_depth.mm, &decimals, &unit); + set_label(stats_w.avg_overall_depth, "%.*f %s", decimals, value, unit); + value = get_volume_units(stats_ptr->max_sac.mliter, &decimals, &unit); + set_label(stats_w.max_sac, _("%.*f %s/min"), decimals, value, unit); + value = get_volume_units(stats_ptr->min_sac.mliter, &decimals, &unit); + set_label(stats_w.min_sac, _("%.*f %s/min"), decimals, value, unit); + value = get_volume_units(stats_ptr->avg_sac.mliter, &decimals, &unit); + set_label(stats_w.avg_sac, _("%.*f %s/min"), decimals, value, unit); +} + +void show_dive_stats(struct dive *dive) +{ + /* they have to be called in this order, as 'total' depends on + * calculations done in 'single' */ + show_single_dive_stats(dive); + show_total_dive_stats(); +} + +static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label) +{ + GtkWidget *label_widget; + GtkWidget *frame; + + frame = gtk_frame_new(label); + label_widget = gtk_label_new(NULL); + gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); + gtk_container_add(GTK_CONTAINER(frame), label_widget); + + return label_widget; +} + +GtkWidget *total_stats_widget(void) +{ + GtkWidget *vbox, *hbox, *statsframe, *framebox; + + vbox = gtk_vbox_new(FALSE, 3); + + statsframe = gtk_frame_new(_("Statistics")); + stats_w.framelabel = gtk_frame_get_label_widget(GTK_FRAME(statsframe)); + gtk_label_set_max_width_chars(GTK_LABEL(stats_w.framelabel), 60); + gtk_box_pack_start(GTK_BOX(vbox), statsframe, FALSE, FALSE, 3); + framebox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(statsframe), framebox); + + /* first row */ + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); + stats_w.selection_size = new_info_label_in_frame(hbox, _("Dives")); + stats_w.max_temp = new_info_label_in_frame(hbox, _("Max Temp")); + stats_w.min_temp = new_info_label_in_frame(hbox, _("Min Temp")); + stats_w.avg_temp = new_info_label_in_frame(hbox, _("Avg Temp")); + + /* second row */ + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); + + stats_w.total_time = new_info_label_in_frame(hbox, _("Total Time")); + stats_w.avg_time = new_info_label_in_frame(hbox, _("Avg Time")); + stats_w.longest_time = new_info_label_in_frame(hbox, _("Longest Dive")); + stats_w.shortest_time = new_info_label_in_frame(hbox, _("Shortest Dive")); + + /* third row */ + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); + + stats_w.max_overall_depth = new_info_label_in_frame(hbox, _("Max Depth")); + stats_w.min_overall_depth = new_info_label_in_frame(hbox, _("Min Depth")); + stats_w.avg_overall_depth = new_info_label_in_frame(hbox, _("Avg Depth")); + + /* fourth row */ + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); + + stats_w.max_sac = new_info_label_in_frame(hbox, _("Max SAC")); + stats_w.min_sac = new_info_label_in_frame(hbox, _("Min SAC")); + stats_w.avg_sac = new_info_label_in_frame(hbox, _("Avg SAC")); + + return vbox; +} + +GtkWidget *single_stats_widget(void) +{ + GtkWidget *vbox, *hbox, *infoframe, *framebox; + + vbox = gtk_vbox_new(FALSE, 3); + + infoframe = gtk_frame_new(_("Dive Info")); + gtk_box_pack_start(GTK_BOX(vbox), infoframe, FALSE, FALSE, 3); + framebox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(infoframe), framebox); + + /* first row */ + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); + + single_w.date = new_info_label_in_frame(hbox, _("Date")); + single_w.dive_time = new_info_label_in_frame(hbox, _("Dive Time")); + single_w.surf_intv = new_info_label_in_frame(hbox, _("Surf Intv")); + + /* second row */ + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); + + single_w.max_depth = new_info_label_in_frame(hbox, _("Max Depth")); + single_w.avg_depth = new_info_label_in_frame(hbox, _("Avg Depth")); + single_w.viz = new_info_label_in_frame(hbox, _("Visibility")); + + /* third row */ + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); + + single_w.water_temp = new_info_label_in_frame(hbox, _("Water Temp")); + single_w.air_temp = new_info_label_in_frame(hbox, _("Air Temp")); + single_w.air_press = new_info_label_in_frame(hbox, _("Air Press")); + + /* fourth row */ + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); + + single_w.sac = new_info_label_in_frame(hbox, _("SAC")); + single_w.otu = new_info_label_in_frame(hbox, _("OTU")); + single_w.o2he = new_info_label_in_frame(hbox, "O" UTF8_SUBSCRIPT_2 " / He"); + single_w.gas_used = new_info_label_in_frame(hbox, C_("Amount","Gas Used")); + + return vbox; +} + +void clear_stats_widgets(void) +{ + set_label(single_w.date, ""); + set_label(single_w.dive_time, ""); + set_label(single_w.surf_intv, ""); + set_label(single_w.max_depth, ""); + set_label(single_w.avg_depth, ""); + set_label(single_w.viz, ""); + set_label(single_w.water_temp, ""); + set_label(single_w.air_temp, ""); + set_label(single_w.air_press, ""); + set_label(single_w.sac, ""); + set_label(single_w.sac, ""); + set_label(single_w.otu, ""); + set_label(single_w.o2he, ""); + set_label(single_w.gas_used, ""); + set_label(stats_w.total_time,""); + set_label(stats_w.avg_time,""); + set_label(stats_w.shortest_time,""); + set_label(stats_w.longest_time,""); + set_label(stats_w.max_overall_depth,""); + set_label(stats_w.min_overall_depth,""); + set_label(stats_w.avg_overall_depth,""); + set_label(stats_w.min_sac,""); + set_label(stats_w.avg_sac,""); + set_label(stats_w.max_sac,""); + set_label(stats_w.selection_size,""); + set_label(stats_w.max_temp,""); + set_label(stats_w.avg_temp,""); + set_label(stats_w.min_temp,""); +} diff --git a/statistics.c b/statistics.c index 502c06cb4..7532e346e 100644 --- a/statistics.c +++ b/statistics.c @@ -1,105 +1,25 @@ -/* statistics.c */ -/* creates the UI for the Info & Stats page - - * controlled through the following interfaces: +/* statistics.c * - * void show_dive_stats(struct dive *dive) - * - * called from gtk-ui: - * GtkWidget *stats_widget(void) + * core logic for the Info & Stats page - + * char *get_time_string(int seconds, int maxdays); + * char *get_minutes(int seconds); + * void process_all_dives(struct dive *dive, struct dive **prev_dive); + * void get_selected_dives_text(char *buffer, int size); */ #include #include #include "dive.h" #include "display.h" -#include "display-gtk.h" #include "divelist.h" - -typedef struct { - GtkWidget *date, - *dive_time, - *surf_intv, - *max_depth, - *avg_depth, - *viz, - *water_temp, - *air_temp, - *air_press, - *sac, - *otu, - *o2he, - *gas_used; -} single_stat_widget_t; - -static single_stat_widget_t single_w; - -typedef struct { - GtkWidget *total_time, - *avg_time, - *shortest_time, - *longest_time, - *max_overall_depth, - *min_overall_depth, - *avg_overall_depth, - *min_sac, - *avg_sac, - *max_sac, - *selection_size, - *max_temp, - *avg_temp, - *min_temp, - *framelabel; -} total_stats_widget_t; - -static total_stats_widget_t stats_w; - -typedef struct { - int period; - duration_t total_time; - /* avg_time is simply total_time / nr -- let's not keep this */ - duration_t shortest_time; - duration_t longest_time; - depth_t max_depth; - depth_t min_depth; - depth_t avg_depth; - volume_t max_sac; - volume_t min_sac; - volume_t avg_sac; - int max_temp; - int min_temp; - double combined_temp; - unsigned int combined_count; - unsigned int selection_size; - unsigned int total_sac_time; -} stats_t; +#include "statistics.h" static stats_t stats; -static stats_t stats_selection; -static stats_t *stats_monthly = NULL; -static stats_t *stats_yearly = NULL; +stats_t stats_selection; +stats_t *stats_monthly = NULL; +stats_t *stats_yearly = NULL; -GtkWidget *yearly_tree = NULL; -enum { - YEAR, - DIVES, - TOTAL_TIME, - AVERAGE_TIME, - SHORTEST_TIME, - LONGEST_TIME, - AVG_DEPTH, - MIN_DEPTH, - MAX_DEPTH, - AVG_SAC, - MIN_SAC, - MAX_SAC, - AVG_TEMP, - MIN_TEMP, - MAX_TEMP, - N_COLUMNS -}; - -static char *get_time_string(int seconds, int maxdays); static void process_temperatures(struct dive *dp, stats_t *stats) { @@ -159,248 +79,14 @@ static void process_dive(struct dive *dp, stats_t *stats) } } -static void init_tree() -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GtkTreeStore *store; - int i; - PangoFontDescription *font_desc = pango_font_description_from_string(prefs.divelist_font); - - gtk_widget_modify_font(yearly_tree, font_desc); - pango_font_description_free(font_desc); - - renderer = gtk_cell_renderer_text_new (); - /* don't use empty strings "" - they confuse gettext */ - char *columnstop[] = { N_("Year"), N_("#"), N_("Duration"), " ", " ", " ", N_("Depth"), " ", " ", N_("SAC"), " ", " ", N_("Temperature"), " ", " " }; - const char *columnsbot[15]; - columnsbot[0] = C_("Stats", " > Month"); - columnsbot[1] = " "; - columnsbot[2] = C_("Duration","Total"); - columnsbot[3] = C_("Duration","Average"); - columnsbot[4] = C_("Duration","Shortest"); - columnsbot[5] = C_("Duration","Longest"); - columnsbot[6] = C_("Depth", "Average"); - columnsbot[7] = C_("Depth","Minimum"); - columnsbot[8] = C_("Depth","Maximum"); - columnsbot[9] = C_("SAC","Average"); - columnsbot[10]= C_("SAC","Minimum"); - columnsbot[11]= C_("SAC","Maximum"); - columnsbot[12]= C_("Temp","Average"); - columnsbot[13]= C_("Temp","Minimum"); - columnsbot[14]= C_("Temp","Maximum"); - - /* Add all the columns to the tree view */ - for (i = 0; i < N_COLUMNS; ++i) { - char buf[256]; - column = gtk_tree_view_column_new(); - snprintf(buf, sizeof(buf), "%s\n%s", _(columnstop[i]), columnsbot[i]); - gtk_tree_view_column_set_title(column, buf); - gtk_tree_view_append_column(GTK_TREE_VIEW(yearly_tree), column); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_add_attribute(column, renderer, "text", i); - gtk_tree_view_column_set_resizable(column, TRUE); - } - - /* Field types */ - store = gtk_tree_store_new ( - N_COLUMNS, // Columns in structure - G_TYPE_STRING, // Period (year or month) - G_TYPE_STRING, // Number of dives - G_TYPE_STRING, // Total duration - G_TYPE_STRING, // Average dive duation - G_TYPE_STRING, // Shortest dive - G_TYPE_STRING, // Longest dive - G_TYPE_STRING, // Average depth - G_TYPE_STRING, // Shallowest dive - G_TYPE_STRING, // Deepest dive - G_TYPE_STRING, // Average air consumption (SAC) - G_TYPE_STRING, // Minimum SAC - G_TYPE_STRING, // Maximum SAC - G_TYPE_STRING, // Average temperature - G_TYPE_STRING, // Minimum temperature - G_TYPE_STRING // Maximum temperature - ); - - gtk_tree_view_set_model (GTK_TREE_VIEW (yearly_tree), GTK_TREE_MODEL (store)); - g_object_unref (store); -} - -static void add_row_to_tree(GtkTreeStore *store, char *value, int index, GtkTreeIter *row_iter, GtkTreeIter *parent) -{ - gtk_tree_store_append(store, row_iter, parent); - gtk_tree_store_set(store, row_iter, index, value, -1); -} - -static void add_cell_to_tree(GtkTreeStore *store, char *value, int index, GtkTreeIter *parent) -{ - gtk_tree_store_set(store, parent, index, value, -1); -} -static char *get_minutes(int seconds) +char *get_minutes(int seconds) { static char buf[80]; snprintf(buf, sizeof(buf), "%d:%.2d", FRACTION(seconds, 60)); return buf; } -static void add_cell(GtkTreeStore *store, GtkTreeIter *parent, unsigned int val, int cell, gboolean depth_not_volume) -{ - double value; - int decimals; - const char *unit; - char value_str[40]; - - if (depth_not_volume) { - value = get_depth_units(val, &decimals, &unit); - snprintf(value_str, sizeof(value_str), "%.*f %s", decimals, value, unit); - } else { - value = get_volume_units(val, &decimals, &unit); - snprintf(value_str, sizeof(value_str), _("%.*f %s/min"), decimals, value, unit); - } - add_cell_to_tree(store, value_str, cell, parent); -} - -static void process_interval_stats(stats_t stats_interval, GtkTreeIter *parent, GtkTreeIter *row) -{ - double value; - const char *unit; - char value_str[40]; - GtkTreeStore *store; - - store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(yearly_tree))); - - /* Year or month */ - snprintf(value_str, sizeof(value_str), "%d", stats_interval.period); - add_row_to_tree(store, value_str, 0, row, parent); - /* Dives */ - snprintf(value_str, sizeof(value_str), "%d", stats_interval.selection_size); - add_cell_to_tree(store, value_str, 1, row); - /* Total duration */ - add_cell_to_tree(store, get_time_string(stats_interval.total_time.seconds, 0), 2, row); - /* Average dive duration */ - add_cell_to_tree(store, get_minutes(stats_interval.total_time.seconds / stats_interval.selection_size), 3, row); - /* Shortest duration */ - add_cell_to_tree(store, get_minutes(stats_interval.shortest_time.seconds), 4, row); - /* Longest duration */ - add_cell_to_tree(store, get_minutes(stats_interval.longest_time.seconds), 5, row); - /* Average depth */ - add_cell(store, row, stats_interval.avg_depth.mm, 6, TRUE); - /* Smallest maximum depth */ - add_cell(store, row, stats_interval.min_depth.mm, 7, TRUE); - /* Deepest maximum depth */ - add_cell(store, row, stats_interval.max_depth.mm, 8, TRUE); - /* Average air consumption */ - add_cell(store, row, stats_interval.avg_sac.mliter, 9, FALSE); - /* Smallest average air consumption */ - add_cell(store, row, stats_interval.min_sac.mliter, 10, FALSE); - /* Biggest air consumption */ - add_cell(store, row, stats_interval.max_sac.mliter, 11, FALSE); - /* Average water temperature */ - value = get_temp_units(stats_interval.min_temp, &unit); - if (stats_interval.combined_temp && stats_interval.combined_count) { - snprintf(value_str, sizeof(value_str), "%.1f %s", stats_interval.combined_temp / stats_interval.combined_count, unit); - add_cell_to_tree(store, value_str, 12, row); - } else { - add_cell_to_tree(store, "", 12, row); - } - /* Coldest water temperature */ - if (value > -100.0) { - snprintf(value_str, sizeof(value_str), "%.1f %s\t", value, unit); - add_cell_to_tree(store, value_str, 13, row); - } else { - add_cell_to_tree(store, "", 13, row); - } - /* Warmest water temperature */ - value = get_temp_units(stats_interval.max_temp, &unit); - if (value > -100.0) { - snprintf(value_str, sizeof(value_str), "%.1f %s", value, unit); - add_cell_to_tree(store, value_str, 14, row); - } else { - add_cell_to_tree(store, "", 14, row); - } -} - -static void clear_statistics() -{ - GtkTreeStore *store; - - store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(yearly_tree))); - gtk_tree_store_clear(store); - yearly_tree = NULL; -} - -static gboolean stat_on_delete(GtkWidget *window, GdkEvent *event, gpointer data) -{ - clear_statistics(); - gtk_widget_destroy(window); - return TRUE; -} - -static void key_press_event(GtkWidget *window, GdkEventKey *event, gpointer data) -{ - if ((event->string != NULL && event->keyval == GDK_Escape) || - (event->string != NULL && event->keyval == GDK_w && event->state & GDK_CONTROL_MASK)) { - clear_statistics(); - gtk_widget_destroy(window); - } -} - -static void update_yearly_stats() -{ - int i, j, combined_months, month = 0; - GtkTreeIter year_iter, month_iter; - GtkTreeStore *store; - - store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(yearly_tree))); - gtk_tree_store_clear(store); - - for (i = 0; stats_yearly != NULL && stats_yearly[i].period; ++i) { - process_interval_stats(stats_yearly[i], NULL, &year_iter); - combined_months = 0; - - for (j = 0; combined_months < stats_yearly[i].selection_size; ++j) { - combined_months += stats_monthly[month].selection_size; - process_interval_stats(stats_monthly[month], &year_iter, &month_iter); - month++; - } - } -} - -void show_yearly_stats() -{ - GtkWidget *window; - GtkWidget *sw; - - if (yearly_tree) - return; - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - sw = gtk_scrolled_window_new (NULL, NULL); - yearly_tree = gtk_tree_view_new (); - - gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); - gtk_window_set_default_size(GTK_WINDOW(window), 640, 480); - gtk_window_set_title(GTK_WINDOW(window), _("Yearly Statistics")); - gtk_container_set_border_width(GTK_CONTAINER(window), 5); - gtk_window_set_resizable(GTK_WINDOW(window), TRUE); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN); - - gtk_container_add (GTK_CONTAINER (sw), yearly_tree); - gtk_container_add (GTK_CONTAINER (window), sw); - - /* Display the yearly statistics on top level - * Monthly statistics are available by expanding a year */ - init_tree(); - update_yearly_stats(); - - g_signal_connect (G_OBJECT (window), "key_press_event", G_CALLBACK (key_press_event), NULL); - g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (stat_on_delete), NULL); - gtk_widget_show_all(window); -} - -static void process_all_dives(struct dive *dive, struct dive **prev_dive) +void process_all_dives(struct dive *dive, struct dive **prev_dive) { int idx; struct dive *dp; @@ -476,8 +162,6 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive) prev_month = current_month; prev_year = current_year; } - if (yearly_tree) - update_yearly_stats(); } /* make sure we skip the selected summary entries */ @@ -498,18 +182,7 @@ void process_selected_dives(void) stats_selection.selection_size = nr; } -static void set_label(GtkWidget *w, const char *fmt, ...) -{ - char buf[256]; - va_list args; - - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - gtk_label_set_text(GTK_LABEL(w), buf); -} - -static char *get_time_string(int seconds, int maxdays) +char *get_time_string(int seconds, int maxdays) { static char buf[80]; if (maxdays && seconds > 3600 * 24 * maxdays) { @@ -526,113 +199,6 @@ static char *get_time_string(int seconds, int maxdays) return buf; } -/* we try to show the data from the currently selected divecomputer - * right now for some values (e.g., surface pressure) we could fall back - * to dive data, but for consistency we don't. */ -static void show_single_dive_stats(struct dive *dive) -{ - char buf[256]; - double value; - int decimals; - const char *unit; - int idx, offset, gas_used, mbar; - struct dive *prev_dive; - struct tm tm; - struct divecomputer *dc; - - process_all_dives(dive, &prev_dive); - if (!dive) - return; - dc = select_dc(&dive->dc); - utc_mkdate(dive->when, &tm); - snprintf(buf, sizeof(buf), - /*++GETTEXT 80 chars: weekday, monthname, day, year, hour, min */ - _("%1$s, %2$s %3$d, %4$d %5$2d:%6$02d"), - weekday(tm.tm_wday), - monthname(tm.tm_mon), - tm.tm_mday, tm.tm_year + 1900, - tm.tm_hour, tm.tm_min); - - set_label(single_w.date, buf); - set_label(single_w.dive_time, _("%d min"), (dive->duration.seconds + 30) / 60); - if (prev_dive) - set_label(single_w.surf_intv, - get_time_string(dive->when - (prev_dive->when + prev_dive->duration.seconds), 4)); - else - set_label(single_w.surf_intv, _("unknown")); - value = get_depth_units(dc->maxdepth.mm, &decimals, &unit); - set_label(single_w.max_depth, "%.*f %s", decimals, value, unit); - value = get_depth_units(dc->meandepth.mm, &decimals, &unit); - set_label(single_w.avg_depth, "%.*f %s", decimals, value, unit); - set_label(single_w.viz, star_strings[dive->visibility]); - if (dc->watertemp.mkelvin) { - value = get_temp_units(dc->watertemp.mkelvin, &unit); - set_label(single_w.water_temp, "%.1f %s", value, unit); - } else { - set_label(single_w.water_temp, ""); - } - if (dc->airtemp.mkelvin) { - value = get_temp_units(dc->airtemp.mkelvin, &unit); - set_label(single_w.air_temp, "%.1f %s", value, unit); - } else { - if (dive->airtemp.mkelvin) { - value = get_temp_units(dive->airtemp.mkelvin, &unit); - set_label(single_w.air_temp, "%.1f %s", value, unit); - } else { - set_label(single_w.air_temp, ""); - } - } - mbar = dc->surface_pressure.mbar; - /* it would be easy to get dive data here: - * if (!mbar) - * mbar = get_surface_pressure_in_mbar(dive, FALSE); - */ - if (mbar) { - set_label(single_w.air_press, "%d mbar", mbar); - } else { - set_label(single_w.air_press, ""); - } - value = get_volume_units(dive->sac, &decimals, &unit); - if (value > 0) - set_label(single_w.sac, _("%.*f %s/min"), decimals, value, unit); - else - set_label(single_w.sac, ""); - set_label(single_w.otu, "%d", dive->otu); - offset = 0; - gas_used = 0; - buf[0] = '\0'; - /* for the O2/He readings just create a list of them */ - for (idx = 0; idx < MAX_CYLINDERS; idx++) { - cylinder_t *cyl = &dive->cylinder[idx]; - pressure_t start, end; - - start = cyl->start.mbar ? cyl->start : cyl->sample_start; - end = cyl->end.mbar ?cyl->sample_end : cyl->sample_end; - if (!cylinder_none(cyl)) { - /* 0% O2 strangely means air, so 21% - I don't like that at all */ - int o2 = get_o2(&cyl->gasmix); - int he = get_he(&cyl->gasmix); - if (offset > 0) { - snprintf(buf+offset, 80-offset, ", "); - offset += 2; - } - snprintf(buf+offset, 80-offset, "%d/%d", (o2 + 5) / 10, (he + 5) / 10); - offset = strlen(buf); - } - /* and if we have size, start and end pressure, we can - * calculate the total gas used */ - if (start.mbar && end.mbar) - gas_used += gas_volume(cyl, start) - gas_volume(cyl, end); - } - set_label(single_w.o2he, buf); - if (gas_used) { - value = get_volume_units(gas_used, &decimals, &unit); - set_label(single_w.gas_used, "%.*f %s", decimals, value, unit); - } else { - set_label(single_w.gas_used, ""); - } -} - /* this gets called when at least two but not all dives are selected */ static void get_ranges(char *buffer, int size) { @@ -677,7 +243,7 @@ static void get_ranges(char *buffer, int size) } } -static void get_selected_dives_text(char *buffer, int size) +void get_selected_dives_text(char *buffer, int size) { if (amount_selected == 1) { if (current_dive) @@ -701,201 +267,3 @@ static void get_selected_dives_text(char *buffer, int size) } } -static void show_total_dive_stats(void) -{ - double value; - int decimals, seconds; - const char *unit; - char buffer[60]; - stats_t *stats_ptr; - - if (!stats_w.framelabel) - return; - stats_ptr = &stats_selection; - - get_selected_dives_text(buffer, sizeof(buffer)); - set_label(stats_w.framelabel, _("Statistics %s"), buffer); - set_label(stats_w.selection_size, "%d", stats_ptr->selection_size); - if (stats_ptr->selection_size == 0) { - clear_stats_widgets(); - return; - } - if (stats_ptr->min_temp) { - value = get_temp_units(stats_ptr->min_temp, &unit); - set_label(stats_w.min_temp, "%.1f %s", value, unit); - } - if (stats_ptr->combined_temp && stats_ptr->combined_count) - set_label(stats_w.avg_temp, "%.1f %s", stats_ptr->combined_temp / stats_ptr->combined_count, unit); - if (stats_ptr->max_temp) { - value = get_temp_units(stats_ptr->max_temp, &unit); - set_label(stats_w.max_temp, "%.1f %s", value, unit); - } - set_label(stats_w.total_time, get_time_string(stats_ptr->total_time.seconds, 0)); - seconds = stats_ptr->total_time.seconds; - if (stats_ptr->selection_size) - seconds /= stats_ptr->selection_size; - set_label(stats_w.avg_time, get_time_string(seconds, 0)); - set_label(stats_w.longest_time, get_time_string(stats_ptr->longest_time.seconds, 0)); - set_label(stats_w.shortest_time, get_time_string(stats_ptr->shortest_time.seconds, 0)); - value = get_depth_units(stats_ptr->max_depth.mm, &decimals, &unit); - set_label(stats_w.max_overall_depth, "%.*f %s", decimals, value, unit); - value = get_depth_units(stats_ptr->min_depth.mm, &decimals, &unit); - set_label(stats_w.min_overall_depth, "%.*f %s", decimals, value, unit); - value = get_depth_units(stats_ptr->avg_depth.mm, &decimals, &unit); - set_label(stats_w.avg_overall_depth, "%.*f %s", decimals, value, unit); - value = get_volume_units(stats_ptr->max_sac.mliter, &decimals, &unit); - set_label(stats_w.max_sac, _("%.*f %s/min"), decimals, value, unit); - value = get_volume_units(stats_ptr->min_sac.mliter, &decimals, &unit); - set_label(stats_w.min_sac, _("%.*f %s/min"), decimals, value, unit); - value = get_volume_units(stats_ptr->avg_sac.mliter, &decimals, &unit); - set_label(stats_w.avg_sac, _("%.*f %s/min"), decimals, value, unit); -} - -void show_dive_stats(struct dive *dive) -{ - /* they have to be called in this order, as 'total' depends on - * calculations done in 'single' */ - show_single_dive_stats(dive); - show_total_dive_stats(); -} - -static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label) -{ - GtkWidget *label_widget; - GtkWidget *frame; - - frame = gtk_frame_new(label); - label_widget = gtk_label_new(NULL); - gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3); - gtk_container_add(GTK_CONTAINER(frame), label_widget); - - return label_widget; -} - -GtkWidget *total_stats_widget(void) -{ - GtkWidget *vbox, *hbox, *statsframe, *framebox; - - vbox = gtk_vbox_new(FALSE, 3); - - statsframe = gtk_frame_new(_("Statistics")); - stats_w.framelabel = gtk_frame_get_label_widget(GTK_FRAME(statsframe)); - gtk_label_set_max_width_chars(GTK_LABEL(stats_w.framelabel), 60); - gtk_box_pack_start(GTK_BOX(vbox), statsframe, FALSE, FALSE, 3); - framebox = gtk_vbox_new(FALSE, 3); - gtk_container_add(GTK_CONTAINER(statsframe), framebox); - - /* first row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - stats_w.selection_size = new_info_label_in_frame(hbox, _("Dives")); - stats_w.max_temp = new_info_label_in_frame(hbox, _("Max Temp")); - stats_w.min_temp = new_info_label_in_frame(hbox, _("Min Temp")); - stats_w.avg_temp = new_info_label_in_frame(hbox, _("Avg Temp")); - - /* second row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - - stats_w.total_time = new_info_label_in_frame(hbox, _("Total Time")); - stats_w.avg_time = new_info_label_in_frame(hbox, _("Avg Time")); - stats_w.longest_time = new_info_label_in_frame(hbox, _("Longest Dive")); - stats_w.shortest_time = new_info_label_in_frame(hbox, _("Shortest Dive")); - - /* third row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - - stats_w.max_overall_depth = new_info_label_in_frame(hbox, _("Max Depth")); - stats_w.min_overall_depth = new_info_label_in_frame(hbox, _("Min Depth")); - stats_w.avg_overall_depth = new_info_label_in_frame(hbox, _("Avg Depth")); - - /* fourth row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - - stats_w.max_sac = new_info_label_in_frame(hbox, _("Max SAC")); - stats_w.min_sac = new_info_label_in_frame(hbox, _("Min SAC")); - stats_w.avg_sac = new_info_label_in_frame(hbox, _("Avg SAC")); - - return vbox; -} - -GtkWidget *single_stats_widget(void) -{ - GtkWidget *vbox, *hbox, *infoframe, *framebox; - - vbox = gtk_vbox_new(FALSE, 3); - - infoframe = gtk_frame_new(_("Dive Info")); - gtk_box_pack_start(GTK_BOX(vbox), infoframe, FALSE, FALSE, 3); - framebox = gtk_vbox_new(FALSE, 3); - gtk_container_add(GTK_CONTAINER(infoframe), framebox); - - /* first row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - - single_w.date = new_info_label_in_frame(hbox, _("Date")); - single_w.dive_time = new_info_label_in_frame(hbox, _("Dive Time")); - single_w.surf_intv = new_info_label_in_frame(hbox, _("Surf Intv")); - - /* second row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - - single_w.max_depth = new_info_label_in_frame(hbox, _("Max Depth")); - single_w.avg_depth = new_info_label_in_frame(hbox, _("Avg Depth")); - single_w.viz = new_info_label_in_frame(hbox, _("Visibility")); - - /* third row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - - single_w.water_temp = new_info_label_in_frame(hbox, _("Water Temp")); - single_w.air_temp = new_info_label_in_frame(hbox, _("Air Temp")); - single_w.air_press = new_info_label_in_frame(hbox, _("Air Press")); - - /* fourth row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - - single_w.sac = new_info_label_in_frame(hbox, _("SAC")); - single_w.otu = new_info_label_in_frame(hbox, _("OTU")); - single_w.o2he = new_info_label_in_frame(hbox, "O" UTF8_SUBSCRIPT_2 " / He"); - single_w.gas_used = new_info_label_in_frame(hbox, C_("Amount","Gas Used")); - - return vbox; -} - -void clear_stats_widgets(void) -{ - set_label(single_w.date, ""); - set_label(single_w.dive_time, ""); - set_label(single_w.surf_intv, ""); - set_label(single_w.max_depth, ""); - set_label(single_w.avg_depth, ""); - set_label(single_w.viz, ""); - set_label(single_w.water_temp, ""); - set_label(single_w.air_temp, ""); - set_label(single_w.air_press, ""); - set_label(single_w.sac, ""); - set_label(single_w.sac, ""); - set_label(single_w.otu, ""); - set_label(single_w.o2he, ""); - set_label(single_w.gas_used, ""); - set_label(stats_w.total_time,""); - set_label(stats_w.avg_time,""); - set_label(stats_w.shortest_time,""); - set_label(stats_w.longest_time,""); - set_label(stats_w.max_overall_depth,""); - set_label(stats_w.min_overall_depth,""); - set_label(stats_w.avg_overall_depth,""); - set_label(stats_w.min_sac,""); - set_label(stats_w.avg_sac,""); - set_label(stats_w.max_sac,""); - set_label(stats_w.selection_size,""); - set_label(stats_w.max_temp,""); - set_label(stats_w.avg_temp,""); - set_label(stats_w.min_temp,""); -} diff --git a/statistics.h b/statistics.h new file mode 100644 index 000000000..d2709ee93 --- /dev/null +++ b/statistics.h @@ -0,0 +1,33 @@ +/* + * statistics.h + * + * core logic functions called from statistics UI + * common types and variables + */ +typedef struct { + int period; + duration_t total_time; + /* avg_time is simply total_time / nr -- let's not keep this */ + duration_t shortest_time; + duration_t longest_time; + depth_t max_depth; + depth_t min_depth; + depth_t avg_depth; + volume_t max_sac; + volume_t min_sac; + volume_t avg_sac; + int max_temp; + int min_temp; + double combined_temp; + unsigned int combined_count; + unsigned int selection_size; + unsigned int total_sac_time; +} stats_t; +extern stats_t stats_selection; +extern stats_t *stats_yearly; +extern stats_t *stats_monthly; + +extern char *get_time_string(int seconds, int maxdays); +extern char *get_minutes(int seconds); +extern void process_all_dives(struct dive *dive, struct dive **prev_dive); +extern void get_selected_dives_text(char *buffer, int size); From db180bf46e28aea3cc7dad2a695b1e0fa1b20e1e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 15 Apr 2013 07:06:32 -0700 Subject: [PATCH 034/226] Remove second dive_trip_list definition and douplicate helper function dive_trip_list is simply a global variable, declared in dive.h. The clear_trip_indexes() helper is purely logic and was moved to divetrip.c - but then not deleted in divetrip-gtk.c Signed-off-by: Dirk Hohndel --- divelist-gtk.c | 9 --------- divelist.h | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/divelist-gtk.c b/divelist-gtk.c index dc441881c..5420f9e1a 100644 --- a/divelist-gtk.c +++ b/divelist-gtk.c @@ -59,7 +59,6 @@ static struct DiveList dive_list; #define TREESTORE(_dl) GTK_TREE_STORE((_dl).treemodel) #define LISTSTORE(_dl) GTK_TREE_STORE((_dl).listmodel) -dive_trip_t *dive_trip_list; short autogroup = FALSE; static gboolean in_set_cursor = FALSE; static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path, @@ -686,14 +685,6 @@ void update_dive_list_col_visibility(void) return; } -static void clear_trip_indexes(void) -{ - dive_trip_t *trip; - - for (trip = dive_trip_list; trip != NULL; trip = trip->next) - trip->index = 0; -} - /* Select the iter asked for, and set the keyboard focus on it */ static void go_to_iter(GtkTreeSelection *selection, GtkTreeIter *iter); static void fill_dive_list(void) diff --git a/divelist.h b/divelist.h index 4772f89d9..e7de9e631 100644 --- a/divelist.h +++ b/divelist.h @@ -21,6 +21,7 @@ extern double init_decompression(struct dive * dive); extern void export_all_dives_uddf_cb(); /* divelist core logic functions */ +extern void clear_trip_indexes(void); extern dive_trip_t *find_trip_by_idx(int idx); extern int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b); extern int trip_has_selected_dives(dive_trip_t *trip); From 115ee47bfc0aa8ca2b2bdaca047ccf595bbb7120 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 15 Apr 2013 15:04:35 -0300 Subject: [PATCH 035/226] Added the code that will load and populate the Tank Info Added the code that will load and populate the Tank Info ComboBox that`s used by the user to select the Cylinder description. Code curerntly implements more than the GTK version since the GTK version of it was a plain-list, this one is a table based model that can be used in ListViews ( like we use now in the ComboBox ) but also in TableViews ( if there`s a need in the future to see everything that`s catalogued in the Tank Info struct. ) Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- Makefile | 2 +- dive.h | 13 +++++ equipment.c | 5 +- qt-ui/addcylinderdialog.cpp | 4 +- qt-ui/addcylinderdialog.h | 3 ++ qt-ui/models.cpp | 103 +++++++++++++++++++++++++++++++++++- qt-ui/models.h | 20 +++++++ 7 files changed, 143 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index d64be2edf..5984d0123 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,7 @@ ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0) OSSUPPORT = linux OSSUPPORT_CFLAGS = $(GTKCFLAGS) $(GCONF2CFLAGS) - ifneq ($(findstring reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))),) + ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) CXXFLAGS += -fPIE endif else ifeq ($(UNAME), darwin) diff --git a/dive.h b/dive.h index 734aa2269..c276fe6de 100644 --- a/dive.h +++ b/dive.h @@ -697,6 +697,19 @@ void get_gas_string(int o2, int he, char *buf, int len); struct event *get_next_event(struct event *event, char *name); + +/* this struct holds the information that + * describes the cylinders of air. + * it is a global variable initialized in equipment.c + * used to fill the combobox in the add/edit cylinder + * dialog + */ + +struct tank_info { + const char *name; + int cuft, ml, psi, bar; +}; + #ifdef DEBUGFILE extern char *debugfilename; extern FILE *debugfile; diff --git a/equipment.c b/equipment.c index a1d156f94..d126b4327 100644 --- a/equipment.c +++ b/equipment.c @@ -790,10 +790,7 @@ static void record_weightsystem_changes(weightsystem_t *ws, struct ws_widget *we * we should pick up any other names from the dive * logs directly. */ -static struct tank_info { - const char *name; - int cuft, ml, psi, bar; -} tank_info[100] = { +struct tank_info tank_info[100] = { /* Need an empty entry for the no-cylinder case */ { "", }, diff --git a/qt-ui/addcylinderdialog.cpp b/qt-ui/addcylinderdialog.cpp index 043f29907..5b91617ed 100644 --- a/qt-ui/addcylinderdialog.cpp +++ b/qt-ui/addcylinderdialog.cpp @@ -9,11 +9,13 @@ #include #include #include "../conversions.h" - +#include "models.h" AddCylinderDialog::AddCylinderDialog(QWidget *parent) : ui(new Ui::AddCylinderDialog()) +, tankInfoModel(new TankInfoModel()) { ui->setupUi(this); + ui->cylinderType->setModel(tankInfoModel); } void AddCylinderDialog::setCylinder(cylinder_t *cylinder) diff --git a/qt-ui/addcylinderdialog.h b/qt-ui/addcylinderdialog.h index 652f7b362..fc68faa72 100644 --- a/qt-ui/addcylinderdialog.h +++ b/qt-ui/addcylinderdialog.h @@ -14,6 +14,8 @@ namespace Ui{ class AddCylinderDialog; } +class TankInfoModel; + class AddCylinderDialog : public QDialog{ Q_OBJECT public: @@ -24,6 +26,7 @@ public: private: Ui::AddCylinderDialog *ui; cylinder_t *currentCylinder; + TankInfoModel *tankInfoModel; }; diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 64fa6bac3..d1b8dc0a0 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -7,6 +7,8 @@ #include "models.h" #include "../dive.h" +extern struct tank_info tank_info[100]; + CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent) { } @@ -161,7 +163,7 @@ QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int r return ret; } - switch(section){ + switch(section) { case TYPE: ret = tr("Type"); break; @@ -179,3 +181,102 @@ void WeightModel::add(weight_t* weight) void WeightModel::update() { } + +void TankInfoModel::add(const QString& description) +{ + // When the user `creates` a new one on the combobox. + // for now, empty till dirk cleans the GTK code. +} + +void TankInfoModel::clear() +{ +} + +int TankInfoModel::columnCount(const QModelIndex& parent) const +{ + return 3; +} + +QVariant TankInfoModel::data(const QModelIndex& index, int role) const +{ + QVariant ret; + if (!index.isValid()) { + return ret; + } + struct tank_info *info = &tank_info[index.row()]; + + int ml = info->ml; + + int bar = ((info->psi) ? psi_to_bar(info->psi) : info->bar) * 1000 + 0.5; + + if (info->cuft) { + double airvolume = cuft_to_l(info->cuft) * 1000.0; + ml = airvolume / bar_to_atm(bar) + 0.5; + } + if (role == Qt::DisplayRole) { + switch(index.column()) { + case BAR: ret = bar; + break; + case ML: ret = ml; + break; + case DESCRIPTION: + ret = QString(info->name); + break; + } + } + return ret; +} + +QVariant TankInfoModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant ret; + + if (orientation != Qt::Horizontal) + return ret; + + if (role == Qt::DisplayRole) { + switch(section) { + case BAR: + ret = tr("Bar"); + break; + case ML: + ret = tr("Ml"); + break; + case DESCRIPTION: + ret = tr("Description"); + break; + } + } + return ret; +} + +int TankInfoModel::rowCount(const QModelIndex& parent) const +{ + return rows+1; +} + +TankInfoModel::TankInfoModel() : QAbstractTableModel(), rows(-1) +{ + struct tank_info *info = tank_info; + for (info = tank_info ; info->name; info++, rows++); + + if (rows > -1) { + beginInsertRows(QModelIndex(), 0, rows); + endInsertRows(); + } +} + +void TankInfoModel::update() +{ + if(rows > -1) { + beginRemoveRows(QModelIndex(), 0, rows); + endRemoveRows(); + } + struct tank_info *info = tank_info; + for (info = tank_info ; info->name; info++, rows++); + + if (rows > -1) { + beginInsertRows(QModelIndex(), 0, rows); + endInsertRows(); + } +} diff --git a/qt-ui/models.h b/qt-ui/models.h index 697096f92..8d86102cb 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -10,6 +10,26 @@ #include #include "../dive.h" +/* Encapsulates the tank_info global variable + * to show on Qt`s Model View System.*/ +class TankInfoModel : public QAbstractTableModel { +Q_OBJECT +public: + enum { DESCRIPTION, ML, BAR}; + TankInfoModel(); + + /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + /*reimp*/ int columnCount(const QModelIndex& parent = QModelIndex()) const; + /*reimp*/ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const; + + void add(const QString& description); + void clear(); + void update(); +private: + int rows; +}; + class CylindersModel : public QAbstractTableModel { Q_OBJECT public: From 073be111f48507f027256c6ecd6f11b19a2a18fc Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Thu, 18 Apr 2013 08:59:31 +0100 Subject: [PATCH 036/226] Delay Qt ui construction The Qt ui will need to read the dive_table to populate widgets with dives. Gtk functionality in init_ui is required to parse the dives. Split init_ui to allow parsing to proceed and complete before Qt ui mainwindow constructor is called. Play with qDebug()'s printf style (Thiago!) Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- dive.h | 1 + divelist.c | 1 - main.c | 7 ++++--- qt-gui.cpp | 13 ++++++++----- qt-ui/mainwindow.cpp | 4 ++++ 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dive.h b/dive.h index c276fe6de..0209197fb 100644 --- a/dive.h +++ b/dive.h @@ -602,6 +602,7 @@ extern void add_event(struct divecomputer *dc, int time, int type, int flags, in /* UI related protopypes */ extern void init_ui(int *argcp, char ***argvp); +extern void init_qt_ui(int *argcp, char ***argvp); extern void run_ui(void); extern void exit_ui(void); diff --git a/divelist.c b/divelist.c index 61b9116b2..088b0e626 100644 --- a/divelist.c +++ b/divelist.c @@ -492,7 +492,6 @@ void get_suit(struct dive *dive, char **str) /* * helper functions for dive_trip handling */ - #ifdef DEBUG_TRIP void dump_trip_list(void) { diff --git a/main.c b/main.c index 3c0ea7381..bf3bc0851 100644 --- a/main.c +++ b/main.c @@ -335,7 +335,7 @@ int main(int argc, char **argv) subsurface_command_line_init(&argc, &argv); parse_xml_init(); - init_ui(&argc, &argv); + init_ui(&argc, &argv); /* the gtk stuff is needed for parsing below */ for (i = 1; i < argc; i++) { const char *a = argv[i]; @@ -372,12 +372,13 @@ int main(int argc, char **argv) report_dives(imported, FALSE); if (dive_table.nr == 0) show_dive_info(NULL); - run_ui(); - exit_ui(); parse_xml_exit(); subsurface_command_line_exit(&argc, &argv); + init_qt_ui(&argc, &argv); /* qt bit delayed until dives are parsed */ + run_ui(); + exit_ui(); #ifdef DEBUGFILE if (debugfile) fclose(debugfile); diff --git a/qt-gui.cpp b/qt-gui.cpp index 745457763..86731bb36 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -1864,6 +1864,14 @@ QStringList MainWindow::fileNameFilters() const } #endif /* NEEDS_TO_MOVE_TO_QT_UI */ +void init_qt_ui(int *argcp, char ***argvp) +{ + application = new QApplication(*argcp, *argvp); + application->installTranslator(new Translator(application)); + MainWindow *window = new MainWindow(); + window->show(); +} + void init_ui(int *argcp, char ***argvp) { GtkWidget *win; @@ -1877,11 +1885,6 @@ void init_ui(int *argcp, char ***argvp) GtkSettings *settings; GtkUIManager *ui_manager; - application = new QApplication(*argcp, *argvp); - application->installTranslator(new Translator(application)); - MainWindow *window = new MainWindow(); - window->show(); - gtk_init(argcp, argvp); settings = gtk_settings_get_default(); gtk_settings_set_long_property(settings, "gtk-tooltip-timeout", 10, "subsurface setting"); diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 577b7fb67..e81d9deab 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -46,6 +46,10 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()) dive = new DiveItem(1,QString("01/03/13"),14.2, 29.0,QString("Wraysbury"),root); Q_UNUSED(dive) + + qDebug("dive_table checks - number of dives is %d", dive_table.nr); + qDebug("# allocated dives = %d, pre-existing = %d", + dive_table.allocated, dive_table.preexisting); } From 1a15462073d854674e4d745c6af0249a1d48452e Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Thu, 18 Apr 2013 08:59:32 +0100 Subject: [PATCH 037/226] Use get_dive to populate divelistview with dives Add dives from dive_table to the model/view using the functions provided to extract the dives. We do not yet handle trips. Formatting of date/time, depth and duration need attention. Relies on earlier patch to delay Qt ui construction. We should look at the signals we publish to link to other widgets etc.. [Dirk Hohndel: use for_each_dive -- clean up some white space] Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index e81d9deab..de7bb5460 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "divelistview.h" #include "divetripmodel.h" @@ -28,31 +29,32 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()) * here we just use an empty string */ model = new DiveTripModel("",this); - if (model){ + if (model) { ui->ListWidget->setModel(model); } - /* add in dives here. - * we need to root to parent all top level dives + /* we need root to parent all top level dives * trips need more work as it complicates parent/child stuff. * - * We show how to obtain the root and add a demo dive - * - * Todo: work through integration with current list of dives/trips * Todo: look at alignment/format of e.g. duration in view + * */ DiveItem *dive = 0; DiveItem *root = model->itemForIndex(QModelIndex()); - if (root){ - dive = new DiveItem(1,QString("01/03/13"),14.2, 29.0,QString("Wraysbury"),root); - + if (root) { + int i; Q_UNUSED(dive) - qDebug("dive_table checks - number of dives is %d", dive_table.nr); - qDebug("# allocated dives = %d, pre-existing = %d", - dive_table.allocated, dive_table.preexisting); + struct dive *d; + qDebug("address of dive_table %p", &dive_table); + for_each_dive(i, d) { + dive = new DiveItem(d->number, + QDateTime::fromTime_t(d->when).toString(), + (float)d->maxdepth.mm/1000 , + (float)d->duration.seconds/60, + d->location, + root); + } } - - } void MainWindow::on_actionNew_triggered() @@ -63,7 +65,7 @@ void MainWindow::on_actionNew_triggered() void MainWindow::on_actionOpen_triggered() { QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::homePath(), filter()); - if (filename.isEmpty()){ + if (filename.isEmpty()) { return; } @@ -295,7 +297,7 @@ bool MainWindow::askSaveChanges() QString message = ! existing_filename ? tr("You have unsaved changes\nWould you like to save those before closing the datafile?") : tr("You have unsaved changes to file: %1 \nWould you like to save those before closing the datafile?").arg(existing_filename); - if (QMessageBox::question(this, tr("Save Changes?"), message) == QMessageBox::Ok){ + if (QMessageBox::question(this, tr("Save Changes?"), message) == QMessageBox::Ok) { // WARNING: Port. // file_save(NULL,NULL); return true; From 75765be14cc90886d7f46ec02097b7efeb3d8234 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 18 Apr 2013 13:18:09 -0700 Subject: [PATCH 038/226] Move creation of dive and dive trip date string into helper functions This allows this code to easily be shared by Gtk and Qt UI. Signed-off-by: Dirk Hohndel --- divelist-gtk.c | 20 ++++---------------- divelist.c | 30 ++++++++++++++++++++++++++++++ divelist.h | 2 ++ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/divelist-gtk.c b/divelist-gtk.c index 5420f9e1a..189b75ba1 100644 --- a/divelist-gtk.c +++ b/divelist-gtk.c @@ -261,31 +261,19 @@ static void date_data_func(GtkTreeViewColumn *col, struct tm tm; timestamp_t when; /* this should be enought for most languages. if not increase the value. */ - char buffer[256]; + char *buffer; gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1); nr = gtk_tree_model_iter_n_children(model, iter); utc_mkdate(when, &tm); if (idx < 0) { - snprintf(buffer, sizeof(buffer), - /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, nr dives */ - ngettext("Trip %1$s, %2$s %3$d, %4$d (%5$d dive)", - "Trip %1$s, %2$s %3$d, %4$d (%5$d dives)", nr), - weekday(tm.tm_wday), - monthname(tm.tm_mon), - tm.tm_mday, tm.tm_year + 1900, - nr); + buffer = get_trip_date_string(&tm, nr); } else { - snprintf(buffer, sizeof(buffer), - /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, hour:min */ - _("%1$s, %2$s %3$d, %4$d %5$02d:%6$02d"), - weekday(tm.tm_wday), - monthname(tm.tm_mon), - tm.tm_mday, tm.tm_year + 1900, - tm.tm_hour, tm.tm_min); + buffer = get_dive_date_string(&tm); } g_object_set(renderer, "text", buffer, NULL); + free(buffer); } static void depth_data_func(GtkTreeViewColumn *col, diff --git a/divelist.c b/divelist.c index 088b0e626..42fb272b0 100644 --- a/divelist.c +++ b/divelist.c @@ -489,6 +489,36 @@ void get_suit(struct dive *dive, char **str) get_string(str, dive->suit); } +#define MAX_DATE_STRING 256 +/* caller needs to free the string */ +char *get_dive_date_string(struct tm *tm) { + char *buffer = malloc(MAX_DATE_STRING); + if (buffer) + snprintf(buffer, MAX_DATE_STRING, + /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, hour:min */ + _("%1$s, %2$s %3$d, %4$d %5$02d:%6$02d"), + weekday(tm->tm_wday), + monthname(tm->tm_mon), + tm->tm_mday, tm->tm_year + 1900, + tm->tm_hour, tm->tm_min); + return buffer; +} + +/* caller needs to free the string */ +char *get_trip_date_string(struct tm *tm, int nr) { + char *buffer = malloc(MAX_DATE_STRING); + if (buffer) + snprintf(buffer, MAX_DATE_STRING, + /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, nr dives */ + ngettext("Trip %1$s, %2$s %3$d, %4$d (%5$d dive)", + "Trip %1$s, %2$s %3$d, %4$d (%5$d dives)", nr), + weekday(tm->tm_wday), + monthname(tm->tm_mon), + tm->tm_mday, tm->tm_year + 1900, + nr); + return buffer; +} + /* * helper functions for dive_trip handling */ diff --git a/divelist.h b/divelist.h index e7de9e631..014c4e0ee 100644 --- a/divelist.h +++ b/divelist.h @@ -21,6 +21,8 @@ extern double init_decompression(struct dive * dive); extern void export_all_dives_uddf_cb(); /* divelist core logic functions */ +extern char *get_dive_date_string(struct tm *tm); +extern char *get_trip_date_string(struct tm *tm, int nr); extern void clear_trip_indexes(void); extern dive_trip_t *find_trip_by_idx(int idx); extern int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b); From 26076610e54957bedd9f04d68000c03009c20373 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 18 Apr 2013 13:23:46 -0700 Subject: [PATCH 039/226] Use helper function to get dive date string Correct order of arguments in the DiveItem constructor call. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index de7bb5460..551c28faa 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -47,12 +47,17 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()) struct dive *d; qDebug("address of dive_table %p", &dive_table); for_each_dive(i, d) { + struct tm tm; + char *buffer; + utc_mkdate(d->when, &tm); + buffer = get_dive_date_string(&tm); dive = new DiveItem(d->number, - QDateTime::fromTime_t(d->when).toString(), - (float)d->maxdepth.mm/1000 , - (float)d->duration.seconds/60, - d->location, - root); + buffer, + (float)d->duration.seconds/60, + (float)d->maxdepth.mm/1000 , + d->location, + root); + free(buffer); } } } From 5da11cbc6a1d3339cbb519cf74e4377b8cbaee5f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 22 Apr 2013 06:45:57 -0700 Subject: [PATCH 040/226] Fix crash with some setups Initialize Qt earlier, before Gtk gets its hands on argc / argv. Suggested-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt-gui.cpp b/qt-gui.cpp index 86731bb36..5da140765 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -1866,7 +1866,6 @@ QStringList MainWindow::fileNameFilters() const void init_qt_ui(int *argcp, char ***argvp) { - application = new QApplication(*argcp, *argvp); application->installTranslator(new Translator(application)); MainWindow *window = new MainWindow(); window->show(); @@ -1874,6 +1873,7 @@ void init_qt_ui(int *argcp, char ***argvp) void init_ui(int *argcp, char ***argvp) { + application = new QApplication(*argcp, *argvp); GtkWidget *win; GtkWidget *nb_page; GtkWidget *dive_list; From a0280ae7d2cdd483bc53c7bc91a8aa438f9234de Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sun, 21 Apr 2013 22:12:36 -0300 Subject: [PATCH 041/226] Move model related code from MainWindow and adjustments. Moves the DiveTrip model related code to models.h The DiveTripModel was implemented in three parts: DiveTripModel.h DiveTripModel.cpp MainWindow.cpp (the code to populate the model) This patch changes the DiveTripModel from it's original implementation to the file models.h, wich should store all models (Dirk requested the Qt developers to not create 2 files per class, but instead to use a file for functionality, and data-models are one functionality.) Besides that, this code cleans up a bit the style: removed operator<< for .push_back, const references where they apply, moved the internal DiveItem class to the .cpp since it should be visible only to the DiveTripModel class, and redesigned the current interface of the model to be identical of the GTK one (used the UTF8-star and 2 subscribed, for instance). Amit (the creator of the original code) should comment here if it's ok with my changes. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- Makefile | 2 +- qt-ui/divetripmodel.cpp | 141 ------------------------- qt-ui/divetripmodel.h | 86 --------------- qt-ui/mainwindow.cpp | 41 +------- qt-ui/models.cpp | 224 ++++++++++++++++++++++++++++++++++++++++ qt-ui/models.h | 34 +++++- 6 files changed, 259 insertions(+), 269 deletions(-) delete mode 100644 qt-ui/divetripmodel.cpp delete mode 100644 qt-ui/divetripmodel.h diff --git a/Makefile b/Makefile index 5984d0123..7b7ec0476 100644 --- a/Makefile +++ b/Makefile @@ -187,7 +187,7 @@ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.m QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ - qt-ui/divetripmodel.o qt-ui/addcylinderdialog.o qt-ui/models.o + qt-ui/addcylinderdialog.o qt-ui/models.o GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o diff --git a/qt-ui/divetripmodel.cpp b/qt-ui/divetripmodel.cpp deleted file mode 100644 index 5082494a0..000000000 --- a/qt-ui/divetripmodel.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * divetripmodel.cpp - * - * classes for the dive trip list in Subsurface - */ -#include "divetripmodel.h" - - -DiveItem::DiveItem(int num, QString dt, float dur, float dep, QString loc, DiveItem *p): - number(num), dateTime(dt), duration(dur), depth(dep), location(loc), parentItem(p) -{ - if (parentItem) - parentItem->addChild(this); -} - - -DiveTripModel::DiveTripModel(const QString &filename, QObject *parent) : QAbstractItemModel(parent), filename(filename) -{ - rootItem = new DiveItem; -} - - -Qt::ItemFlags DiveTripModel::flags(const QModelIndex &index) const -{ - Qt::ItemFlags diveFlags = QAbstractItemModel::flags(index); - if (index.isValid()) { - diveFlags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled; - } - return diveFlags; -} - - -QVariant DiveTripModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (role != Qt::DisplayRole) - return QVariant(); - - DiveItem *item = static_cast(index.internalPointer()); - - QVariant retVal; - switch (index.column()) { - case DIVE_NUMBER: - retVal = QVariant(item->diveNumber()); - break; - case DIVE_DATE_TIME: - retVal = QVariant(item->diveDateTime()); - break; - case DIVE_DURATION: - retVal = QVariant(item->diveDuration()); - break; - case DIVE_DEPTH: - retVal = QVariant(item->diveDepth()); - break; - case DIVE_LOCATION: - retVal = QVariant(item->diveLocation()); - break; - default: - return QVariant(); - }; - return retVal; -} - - -QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - if (section == DIVE_NUMBER) { - return tr("Dive number"); - } else if (section == DIVE_DATE_TIME) { - return tr("Date"); - } else if (section == DIVE_DURATION) { - return tr("Duration"); - } else if (section == DIVE_DEPTH) { - return tr("Depth"); - } else if (section == DIVE_LOCATION) { - return tr("Location"); - } - } - return QVariant(); -} - -int DiveTripModel::rowCount(const QModelIndex &parent) const -{ - /* only allow kids in column 0 */ - if (parent.isValid() && parent.column() > 0){ - return 0; - } - DiveItem *item = itemForIndex(parent); - return item ? item->childCount() : 0; -} - - - -int DiveTripModel::columnCount(const QModelIndex &parent) const -{ - return parent.isValid() && parent.column() != 0 ? 0 : COLUMNS; -} - - -QModelIndex DiveTripModel::index(int row, int column, const QModelIndex &parent) const -{ - - if (!rootItem || row < 0 || column < 0 || column >= COLUMNS || - (parent.isValid() && parent.column() != 0)) - return QModelIndex(); - - DiveItem *parentItem = itemForIndex(parent); - Q_ASSERT(parentItem); - if (DiveItem *item = parentItem->childAt(row)){ - return createIndex(row, column, item); - } - return QModelIndex(); -} - - -QModelIndex DiveTripModel::parent(const QModelIndex &childIndex) const -{ - if (!childIndex.isValid()) - return QModelIndex(); - - DiveItem *child = static_cast(childIndex.internalPointer()); - DiveItem *parent = child->parent(); - - if (parent == rootItem) - return QModelIndex(); - - return createIndex(parent->rowOfChild(child), 0, parent); -} - - -DiveItem* DiveTripModel::itemForIndex(const QModelIndex &index) const -{ - if (index.isValid()) { - DiveItem *item = static_cast(index.internalPointer()); - return item; - } - return rootItem; -} diff --git a/qt-ui/divetripmodel.h b/qt-ui/divetripmodel.h deleted file mode 100644 index ad1815798..000000000 --- a/qt-ui/divetripmodel.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * divetripmodel.h - * - * header file for the divetrip model of Subsurface - * - */ -#ifndef DIVETRIPMODEL_H -#define DIVETRIPMODEL_H - -#include - -/*! A DiveItem for use with a DiveTripModel - * - * A simple class which wraps basic stats for a dive (e.g. duration, depth) and - * tidies up after it's children. This is done manually as we don't inherit from - * QObject. - * -*/ -class DiveItem -{ -public: - explicit DiveItem(): number(0), dateTime(QString()), duration(0.0), depth(0.0), location(QString()) {parentItem = 0;} - explicit DiveItem(int num, QString dt, float, float, QString loc, DiveItem *parent = 0); - ~DiveItem() { qDeleteAll(childlist); } - - int diveNumber() const { return number; } - QString diveDateTime() const { return dateTime; } - float diveDuration() const { return duration; } - float diveDepth() const { return depth; } - QString diveLocation() const { return location; } - - DiveItem *parent() const { return parentItem; } - DiveItem *childAt(int row) const { return childlist.value(row); } - int rowOfChild(DiveItem *child) const { return childlist.indexOf(child); } - int childCount() const { return childlist.count(); } - bool hasChildren() const { return !childlist.isEmpty(); } - QList children() const { return childlist; } - void addChild(DiveItem* item) { item->parentItem = this; childlist << item; } /* parent = self */ - - -private: - - int number; - QString dateTime; - float duration; - float depth; - QString location; - - DiveItem *parentItem; - QList childlist; - -}; - - -enum Column {DIVE_NUMBER, DIVE_DATE_TIME, DIVE_DURATION, DIVE_DEPTH, DIVE_LOCATION, COLUMNS}; - - -/*! An AbstractItemModel for recording dive trip information such as a list of dives. -* -*/ -class DiveTripModel : public QAbstractItemModel -{ -public: - - DiveTripModel(const QString &filename, QObject *parent = 0); - - Qt::ItemFlags flags(const QModelIndex &index) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - int rowCount(const QModelIndex &parent) const; - - int columnCount(const QModelIndex &parent = QModelIndex()) const; - virtual QModelIndex index(int row, int column, - const QModelIndex &parent = QModelIndex()) const; - virtual QModelIndex parent(const QModelIndex &child) const; - - DiveItem *itemForIndex(const QModelIndex &) const; - -private: - - DiveItem *rootItem; - QString filename; - -}; - -#endif // DIVETRIPMODEL_H diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 551c28faa..46ce076d4 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -13,7 +13,6 @@ #include #include "divelistview.h" -#include "divetripmodel.h" #include "glib.h" #include "../dive.h" @@ -21,45 +20,11 @@ #include "../pref.h" -MainWindow::MainWindow() : ui(new Ui::MainWindow()) +MainWindow::MainWindow() : ui(new Ui::MainWindow()), + model(new DiveTripModel(this)) { ui->setupUi(this); - - /* may want to change ctor to avoid filename as 1st param. - * here we just use an empty string - */ - model = new DiveTripModel("",this); - if (model) { - ui->ListWidget->setModel(model); - } - /* we need root to parent all top level dives - * trips need more work as it complicates parent/child stuff. - * - * Todo: look at alignment/format of e.g. duration in view - * - */ - DiveItem *dive = 0; - DiveItem *root = model->itemForIndex(QModelIndex()); - if (root) { - int i; - Q_UNUSED(dive) - - struct dive *d; - qDebug("address of dive_table %p", &dive_table); - for_each_dive(i, d) { - struct tm tm; - char *buffer; - utc_mkdate(d->when, &tm); - buffer = get_dive_date_string(&tm); - dive = new DiveItem(d->number, - buffer, - (float)d->duration.seconds/60, - (float)d->maxdepth.mm/1000 , - d->location, - root); - free(buffer); - } - } + ui->ListWidget->setModel(model); } void MainWindow::on_actionNew_triggered() diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index d1b8dc0a0..40307d022 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -6,6 +6,7 @@ */ #include "models.h" #include "../dive.h" +#include "../divelist.h" extern struct tank_info tank_info[100]; @@ -280,3 +281,226 @@ void TankInfoModel::update() endInsertRows(); } } + +/*! A DiveItem for use with a DiveTripModel + * + * A simple class which wraps basic stats for a dive (e.g. duration, depth) and + * tidies up after it's children. This is done manually as we don't inherit from + * QObject. + * +*/ +class DiveItem +{ +public: + explicit DiveItem(): number(0), dateTime(QString()), duration(0.0), depth(0.0), location(QString()) {parentItem = 0;} + explicit DiveItem(int num, QString dt, float, float, QString loc, DiveItem *parent = 0); + ~DiveItem() { qDeleteAll(childlist); } + + int diveNumber() const { return number; } + const QString& diveDateTime() const { return dateTime; } + float diveDuration() const { return duration; } + float diveDepth() const { return depth; } + const QString& diveLocation() const { return location; } + DiveItem *parent() const { return parentItem; } + const QList& children() const { return childlist; } + + void addChild(DiveItem* item) { + item->parentItem = this; + childlist.push_back(item); + } /* parent = self */ + +private: + int number; + QString dateTime; + float duration; + float depth; + QString location; + + DiveItem *parentItem; + QList childlist; + +}; + +DiveItem::DiveItem(int num, QString dt, float dur, float dep, QString loc, DiveItem *p): + number(num), dateTime(dt), duration(dur), depth(dep), location(loc), parentItem(p) +{ + if (parentItem) + parentItem->addChild(this); +} + +DiveTripModel::DiveTripModel(QObject *parent) : QAbstractItemModel(parent) +{ + rootItem = new DiveItem; + int i; + struct dive *d; + + for_each_dive(i, d) { + struct tm tm; + char *buffer; + utc_mkdate(d->when, &tm); + buffer = get_dive_date_string(&tm); + new DiveItem(d->number, + buffer, + d->duration.seconds/60.0, + d->maxdepth.mm/1000.0 , + d->location, + rootItem); + free(buffer); + } +} + + +Qt::ItemFlags DiveTripModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags diveFlags = QAbstractItemModel::flags(index); + if (index.isValid()) { + diveFlags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled; + } + return diveFlags; +} + + +QVariant DiveTripModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + DiveItem *item = static_cast(index.internalPointer()); + + QVariant retVal; + if (role == Qt::DisplayRole){ + switch (index.column()) { + case NR: + retVal = item->diveNumber(); + break; + case DATE: + retVal = item->diveDateTime(); + break; + case DURATION: + retVal = item->diveDuration(); + break; + case DEPTH: + retVal = item->diveDepth(); + break; + case LOCATION: + retVal = item->diveLocation(); + break; + } + } + return retVal; +} + + +QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant ret; + if (orientation != Qt::Horizontal){ + return ret; + } + + if (role == Qt::DisplayRole) { + switch(section){ + case NR: + ret = tr("#"); + break; + case DATE: + ret = tr("Date"); + break; + case RATING: + ret = UTF8_BLACKSTAR; + break; + case DEPTH: + ret = tr("ft"); + break; + case DURATION: + ret = tr("min"); + break; + case TEMPERATURE: + ret = UTF8_DEGREE "F"; + break; + case TOTALWEIGHT: + ret = tr("lbs"); + break; + case SUIT: + ret = tr("Suit"); + break; + case CYLINDER: + ret = tr("Cyl"); + break; + case NITROX: + ret = "O" UTF8_SUBSCRIPT_2 "%"; + break; + case SAC: + ret = tr("SAC"); + break; + case OTU: + ret = tr("OTU"); + break; + case MAXCNS: + ret = tr("maxCNS"); + break; + case LOCATION: + ret = tr("Location"); + break; + } + } + return ret; +} + +int DiveTripModel::rowCount(const QModelIndex &parent) const +{ + /* only allow kids in column 0 */ + if (parent.isValid() && parent.column() > 0){ + return 0; + } + DiveItem *item = itemForIndex(parent); + return item ? item->children().count() : 0; +} + + + +int DiveTripModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() && parent.column() != 0 ? 0 : COLUMNS; +} + + +QModelIndex DiveTripModel::index(int row, int column, const QModelIndex &parent) const +{ + + if (!rootItem || row < 0 || column < 0 || column >= COLUMNS || + (parent.isValid() && parent.column() != 0)) + return QModelIndex(); + + DiveItem *parentItem = itemForIndex(parent); + Q_ASSERT(parentItem); + if (DiveItem *item = parentItem->children().at(row)){ + return createIndex(row, column, item); + } + return QModelIndex(); +} + + +QModelIndex DiveTripModel::parent(const QModelIndex &childIndex) const +{ + if (!childIndex.isValid()) + return QModelIndex(); + + DiveItem *child = static_cast(childIndex.internalPointer()); + DiveItem *parent = child->parent(); + + if (parent == rootItem) + return QModelIndex(); + + return createIndex(parent->children().indexOf(child), 0, parent); +} + + +DiveItem* DiveTripModel::itemForIndex(const QModelIndex &index) const +{ + if (index.isValid()) { + DiveItem *item = static_cast(index.internalPointer()); + return item; + } + return rootItem; +} diff --git a/qt-ui/models.h b/qt-ui/models.h index 8d86102cb..d64faf0bd 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -15,7 +15,7 @@ class TankInfoModel : public QAbstractTableModel { Q_OBJECT public: - enum { DESCRIPTION, ML, BAR}; + enum Column { DESCRIPTION, ML, BAR}; TankInfoModel(); /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; @@ -30,10 +30,12 @@ private: int rows; }; +/* Encapsulation of the Cylinder Model, that presents the + * Current cylinders that are used on a dive. */ class CylindersModel : public QAbstractTableModel { Q_OBJECT public: - enum {TYPE, SIZE, MAXPRESS, START, END, O2, HE}; + enum Column {TYPE, SIZE, MAXPRESS, START, END, O2, HE}; explicit CylindersModel(QObject* parent = 0); /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; @@ -53,8 +55,10 @@ private: QMap usedRows; }; +/* Encapsulation of the Weight Model, that represents + * the current weights on a dive. */ class WeightModel : public QAbstractTableModel { - enum{TYPE, WEIGHT}; + enum Column {TYPE, WEIGHT}; /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; /*reimp*/ int columnCount(const QModelIndex& parent = QModelIndex()) const; /*reimp*/ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; @@ -67,4 +71,28 @@ private: int rows; }; +/*! An AbstractItemModel for recording dive trip information such as a list of dives. +* +*/ +class DiveItem; // Represents a single item on the model, implemented in the .cpp since it's private for this class. +class DiveTripModel : public QAbstractItemModel +{ +public: + enum Column {NR, DATE, RATING, DEPTH, DURATION, TEMPERATURE, TOTALWEIGHT, SUIT, CYLINDER, NITROX, SAC, OTU, MAXCNS, LOCATION, COLUMNS }; + + DiveTripModel(QObject *parent = 0); + + /*reimp*/ Qt::ItemFlags flags(const QModelIndex &index) const; + /*reimp*/ QVariant data(const QModelIndex &index, int role) const; + /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role) const; + /*reimp*/ int rowCount(const QModelIndex &parent) const; + /*reimp*/ int columnCount(const QModelIndex &parent = QModelIndex()) const; + /*reimp*/ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + /*reimp*/ QModelIndex parent(const QModelIndex &child) const; + +private: + DiveItem *itemForIndex(const QModelIndex& index) const; + DiveItem *rootItem; +}; + #endif From 9ffb707d9d9d478b7538a8cd9ef9ef7c6edae25c Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 22 Apr 2013 16:00:27 -0700 Subject: [PATCH 042/226] Initial version of the Star Picker Widget. A very simple to use widget: StarWidget *stars = new StarWidget( windowParent); stars->setMaximumStars(10); stars->setCurrentStars( rand()%10); stars->show(); connect(stars, SIGNAL(valueChanged(int)), someObj, SLOT(starsChangedValue(int))); It currently uses a 'star.svg' file on the same folder as the binary. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- Makefile | 6 +-- qt-ui/mainwindow.cpp | 6 +++ qt-ui/starwidget.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++ qt-ui/starwidget.h | 42 ++++++++++++++++++ resources.qrc | 5 +++ star.svg | 74 +++++++++++++++++++++++++++++++ 6 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 qt-ui/starwidget.cpp create mode 100644 qt-ui/starwidget.h create mode 100644 resources.qrc create mode 100644 star.svg diff --git a/Makefile b/Makefile index 7b7ec0476..076b5f123 100644 --- a/Makefile +++ b/Makefile @@ -104,10 +104,10 @@ LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null) # Use qmake to find out which Qt version we are building for. QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) ifeq ($(QT_VERSION_MAJOR), 5) - QT_MODULES = Qt5Widgets + QT_MODULES = Qt5Widgets Qt5Svg QT_CORE = Qt5Core else - QT_MODULES = QtGui + QT_MODULES = QtGui QtSvg QT_CORE = QtCore endif @@ -187,7 +187,7 @@ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.m QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ - qt-ui/addcylinderdialog.o qt-ui/models.o + qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 46ce076d4..bb4bda1af 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -13,6 +13,7 @@ #include #include "divelistview.h" +#include "starwidget.h" #include "glib.h" #include "../dive.h" @@ -25,6 +26,11 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), { ui->setupUi(this); ui->ListWidget->setModel(model); + // Just to test the star widgets, can be safely removed. + StarWidget *star = new StarWidget(0); + star->setMaximumStars(10); + star->setCurrentStars(3); + star->show(); } void MainWindow::on_actionNew_triggered() diff --git a/qt-ui/starwidget.cpp b/qt-ui/starwidget.cpp new file mode 100644 index 000000000..457946feb --- /dev/null +++ b/qt-ui/starwidget.cpp @@ -0,0 +1,102 @@ +#include "starwidget.h" +#include +#include +#include +#include +#include + +int StarWidget::currentStars() const +{ + return current; +} + +void StarWidget::enableHalfStars(bool enabled) +{ + halfStar = enabled; + update(); +} + +bool StarWidget::halfStarsEnabled() const +{ + return halfStar; +} + +int StarWidget::maxStars() const +{ + return stars; +} + +void StarWidget::mouseReleaseEvent(QMouseEvent* event) +{ + int starClicked = event->pos().x() / IMG_SIZE + 1; + if (starClicked > stars) + starClicked = stars; + + if (current == starClicked) + current -= 1; + else + current = starClicked; + + update(); +} + +void StarWidget::paintEvent(QPaintEvent* event) +{ + QPainter p(this); + + for(int i = 0; i < current; i++) + p.drawPixmap(i * IMG_SIZE + SPACING, 0, activeStar); + + for(int i = current; i < stars; i++) + p.drawPixmap(i * IMG_SIZE + SPACING, 0, inactiveStar); +} + +void StarWidget::setCurrentStars(int value) +{ + current = value; + update(); + Q_EMIT valueChanged(current); +} + +void StarWidget::setMaximumStars(int maximum) +{ + stars = maximum; + update(); +} + +StarWidget::StarWidget(QWidget* parent, Qt::WindowFlags f): + QWidget(parent, f), + stars(5), + current(0), + halfStar(false) +{ + QSvgRenderer render(QString("star.svg")); + QPixmap renderedStar(IMG_SIZE, IMG_SIZE); + + renderedStar.fill(Qt::transparent); + QPainter painter(&renderedStar); + + render.render(&painter, QRectF(0, 0, IMG_SIZE, IMG_SIZE)); + activeStar = renderedStar; + inactiveStar = grayImage(&renderedStar); +} + +QPixmap StarWidget::grayImage(QPixmap* coloredImg) +{ + QImage img = coloredImg->toImage(); + for (int i = 0; i < img.width(); ++i) { + for (int j = 0; j < img.height(); ++j) { + QRgb col = img.pixel(i, j); + if (!col) + continue; + int gray = QColor(Qt::darkGray).rgb(); + img.setPixel(i, j, qRgb(gray, gray, gray)); + } + } + return QPixmap::fromImage(img); +} + +QSize StarWidget::sizeHint() const +{ + return QSize(IMG_SIZE * stars + SPACING * (stars-1), IMG_SIZE); +} diff --git a/qt-ui/starwidget.h b/qt-ui/starwidget.h new file mode 100644 index 000000000..cdbb3ab5c --- /dev/null +++ b/qt-ui/starwidget.h @@ -0,0 +1,42 @@ +#ifndef STARWIDGET_H +#define STARWIDGET_H + +#include + + +class StarWidget : public QWidget +{ + Q_OBJECT +public: + explicit StarWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); + + int maxStars() const; + int currentStars() const; + bool halfStarsEnabled() const; + + /*reimp*/ QSize sizeHint() const; + + enum {SPACING = 2, IMG_SIZE = 16}; + +Q_SIGNALS: + void valueChanged(int stars); + +public Q_SLOTS: + void setCurrentStars(int value); + void setMaximumStars(int maximum); + void enableHalfStars(bool enabled); + +protected: + /*reimp*/ void mouseReleaseEvent(QMouseEvent* ); + /*reimp*/ void paintEvent(QPaintEvent* ); + +private: + int stars; + int current; + bool halfStar; + QPixmap activeStar; + QPixmap inactiveStar; + QPixmap grayImage(QPixmap *coloredImg); +}; + +#endif // STARWIDGET_H diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 000000000..e257bf8f5 --- /dev/null +++ b/resources.qrc @@ -0,0 +1,5 @@ + + + star.svg + + diff --git a/star.svg b/star.svg new file mode 100644 index 000000000..e4345eb48 --- /dev/null +++ b/star.svg @@ -0,0 +1,74 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + From d326a91c865caff097af9b1736740291fb3e63ac Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 22 Apr 2013 22:46:38 -0700 Subject: [PATCH 043/226] Move some of the checks a little bit up Signed-off-by: Thiago Macieira --- Makefile | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 076b5f123..56f2c7941 100644 --- a/Makefile +++ b/Makefile @@ -114,8 +114,20 @@ endif # we need GLIB2CFLAGS for gettext QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) $(GLIB2CFLAGS) LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) +ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) + QTCXXFLAGS += -fPIE +endif LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) +ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) + LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0) + GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0) +else ifeq ($(UNAME), darwin) + LIBGTK += $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation -framework CoreServices + GTKCFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration) + GTK_MAC_BUNDLER = ~/.local/bin/gtk-mac-bundler +endif + LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES) LIBDIVECOMPUTER = $(LIBDIVECOMPUTERARCHIVE) $(LIBUSB) @@ -145,13 +157,8 @@ ifneq ($(strip $(LIBSQLITE3)),) endif ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) - LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0) - GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0) OSSUPPORT = linux OSSUPPORT_CFLAGS = $(GTKCFLAGS) $(GCONF2CFLAGS) - ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) - CXXFLAGS += -fPIE - endif else ifeq ($(UNAME), darwin) OSSUPPORT = macos OSSUPPORT_CFLAGS = $(GTKCFLAGS) @@ -160,10 +167,7 @@ else ifeq ($(UNAME), darwin) MACOSXSTAGING = $(MACOSXFILES)/Subsurface.app INFOPLIST = $(MACOSXFILES)/Info.plist INFOPLISTINPUT = $(INFOPLIST).in - EXTRALIBS = $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation -framework CoreServices - CFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration) LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) - GTK_MAC_BUNDLER = ~/.local/bin/gtk-mac-bundler else OSSUPPORT = windows OSSUPPORT_CFLAGS = $(GTKCFLAGS) From e9ec2dcc455450262b56a901e9d397988d384480 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 22 Apr 2013 23:09:23 -0700 Subject: [PATCH 044/226] Split the SQLite3, libzip, libxslt and osmgpsmap detection from use Signed-off-by: Thiago Macieira --- Makefile | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 56f2c7941..0eade3f36 100644 --- a/Makefile +++ b/Makefile @@ -136,25 +136,17 @@ LIBXSLT = $(shell $(XSLCONFIG) --libs) XML2CFLAGS = $(shell $(XML2CONFIG) --cflags) GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0) GTKCFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-2.0) -CFLAGS += $(shell $(XSLCONFIG) --cflags) +XSLCFLAGS = $(shell $(XSLCONFIG) --cflags) OSMGPSMAPFLAGS += $(shell $(PKGCONFIG) --cflags osmgpsmap 2> /dev/null) LIBOSMGPSMAP += $(shell $(PKGCONFIG) --libs osmgpsmap 2> /dev/null) -ifneq ($(strip $(LIBOSMGPSMAP)),) - GPSOBJ = gps.o - CFLAGS += -DHAVE_OSM_GPS_MAP -endif LIBSOUPCFLAGS = $(shell $(PKGCONFIG) --cflags libsoup-2.4) LIBSOUP = $(shell $(PKGCONFIG) --libs libsoup-2.4) LIBZIP = $(shell $(PKGCONFIG) --libs libzip 2> /dev/null) -ifneq ($(strip $(LIBZIP)),) - ZIP = -DLIBZIP $(shell $(PKGCONFIG) --cflags libzip) -endif +ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) -ifneq ($(strip $(LIBSQLITE3)),) - SQLITE3 = -DSQLITE3 $(shell $(PKGCONFIG) --cflags sqlite3) -endif +SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) OSSUPPORT = linux @@ -179,9 +171,6 @@ else XSLTDIR = .\\xslt endif -ifneq ($(strip $(LIBXSLT)),) - XSLT=-DXSLT='"$(XSLTDIR)"' -endif LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) @@ -198,7 +187,7 @@ GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o planner.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ - webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) $(QTOBJS) $(GTKOBJS) + webservice.o sha1.o $(OSSUPPORT).o $(RESFILE) $(QTOBJS) $(GTKOBJS) # Add files to the following variables if the auto-detection based on the # filename fails @@ -315,8 +304,22 @@ update-po-files: tx pull -af EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ - $(XSLT) $(ZIP) $(SQLITE3) $(LIBDIVECOMPUTERCFLAGS) \ - $(LIBSOUPCFLAGS) $(OSMGPSMAPFLAGS) $(GCONF2CFLAGS) + $(LIBDIVECOMPUTERCFLAGS) \ + $(LIBSOUPCFLAGS) $(GCONF2CFLAGS) + +ifneq ($(SQLITE3FLAGS),) + EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) +endif +ifneq ($(ZIPFLAGS),) + EXTRA_FLAGS += -DLIBZIP $(ZIPFLAGS) +endif +ifneq ($(strip $(LIBXSLT)),) + EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) +endif +ifneq ($(strip $(LIBOSMGPSMAP)),) + OBJS += gps.o + EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) +endif MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS)) From 83309d93692b7db7b6fce8c6c1f17eee228d626d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 22 Apr 2013 23:13:10 -0700 Subject: [PATCH 045/226] Move the list of objects a little further up This also implies we don't need the OSSUPPORT variable anymore. We simply add to OBJS. Signed-off-by: Thiago Macieira --- Makefile | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 0eade3f36..b699b56f2 100644 --- a/Makefile +++ b/Makefile @@ -148,11 +148,22 @@ ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) +QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ + qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o + +GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o + +OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ + planner.o planner-gtk.o \ + parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ + qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ + webservice.o sha1.o $(RESFILE) $(QTOBJS) $(GTKOBJS) + ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) - OSSUPPORT = linux + OBJS += linux.o OSSUPPORT_CFLAGS = $(GTKCFLAGS) $(GCONF2CFLAGS) else ifeq ($(UNAME), darwin) - OSSUPPORT = macos + OBJS += macos.o OSSUPPORT_CFLAGS = $(GTKCFLAGS) MACOSXINSTALL = /Applications/Subsurface.app MACOSXFILES = packaging/macosx @@ -161,7 +172,7 @@ else ifeq ($(UNAME), darwin) INFOPLISTINPUT = $(INFOPLIST).in LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) else - OSSUPPORT = windows + OBSJ += windows.o OSSUPPORT_CFLAGS = $(GTKCFLAGS) WINDOWSSTAGING = ./packaging/windows WINMSGDIRS=$(addprefix share/locale/,$(shell ls po/*.po | sed -e 's/po\/\(..\)_.*/\1\/LC_MESSAGES/')) @@ -179,16 +190,6 @@ MSGLANGS=$(notdir $(wildcard po/*.po)) MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) -QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ - qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o - -GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o - -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o planner.o \ - parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ - qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ - webservice.o sha1.o $(OSSUPPORT).o $(RESFILE) $(QTOBJS) $(GTKOBJS) - # Add files to the following variables if the auto-detection based on the # filename fails OBJS_NEEDING_MOC = From 50420d48d93a79866d34b0462625531257cb6151 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 22 Apr 2013 23:15:35 -0700 Subject: [PATCH 046/226] Remove the unused OSSUPPORT_CFLAGS variable Signed-off-by: Thiago Macieira --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index b699b56f2..37dc3437d 100644 --- a/Makefile +++ b/Makefile @@ -161,10 +161,8 @@ OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) OBJS += linux.o - OSSUPPORT_CFLAGS = $(GTKCFLAGS) $(GCONF2CFLAGS) else ifeq ($(UNAME), darwin) OBJS += macos.o - OSSUPPORT_CFLAGS = $(GTKCFLAGS) MACOSXINSTALL = /Applications/Subsurface.app MACOSXFILES = packaging/macosx MACOSXSTAGING = $(MACOSXFILES)/Subsurface.app @@ -173,7 +171,6 @@ else ifeq ($(UNAME), darwin) LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) else OBSJ += windows.o - OSSUPPORT_CFLAGS = $(GTKCFLAGS) WINDOWSSTAGING = ./packaging/windows WINMSGDIRS=$(addprefix share/locale/,$(shell ls po/*.po | sed -e 's/po\/\(..\)_.*/\1\/LC_MESSAGES/')) NSIINPUTFILE = $(WINDOWSSTAGING)/subsurface.nsi.in From 77ebc2f4c854748edaff6bc518e105b56f656a47 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 22 Apr 2013 23:18:36 -0700 Subject: [PATCH 047/226] Move the MSGOBJS variable definition a little further down Move it closer to the actual rules. This is not a variable that we'll usually modify. Signed-off-by: Thiago Macieira --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 37dc3437d..c5f72ccf8 100644 --- a/Makefile +++ b/Makefile @@ -184,8 +184,6 @@ LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIB $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) MSGLANGS=$(notdir $(wildcard po/*.po)) -MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) - # Add files to the following variables if the auto-detection based on the # filename fails @@ -193,6 +191,8 @@ OBJS_NEEDING_MOC = OBJS_NEEDING_UIC = HEADERS_NEEDING_MOC = +MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) + # Add the objects for the header files which define QObject subclasses HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(OBJS:.o=.h)) MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) From ddbb942d0fce58f5ef7335f23da703ddb721bcf9 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 22 Apr 2013 23:26:33 -0700 Subject: [PATCH 048/226] Reorder the Makefile Create three sections: 1) the detection rules 2) the main rules, what we usually edit 3) the build rules, which we usually don't change Signed-off-by: Thiago Macieira --- Makefile | 112 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index c5f72ccf8..e882a353d 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,8 @@ CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE CXX=g++ CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS INSTALL=install + +# This are the detection rules PKGCONFIG=pkg-config XML2CONFIG=xml2-config XSLCONFIG=xslt-config @@ -12,40 +14,7 @@ QMAKE=qmake MOC=moc UIC=uic -# these locations seem to work for SuSE and Fedora -# prefix = $(HOME) -prefix = $(DESTDIR)/usr -BINDIR = $(prefix)/bin -DATADIR = $(prefix)/share -DESKTOPDIR = $(DATADIR)/applications -ICONPATH = $(DATADIR)/icons/hicolor -ICONDIR = $(ICONPATH)/scalable/apps -MANDIR = $(DATADIR)/man/man1 -XSLTDIR = $(DATADIR)/subsurface/xslt -gtk_update_icon_cache = gtk-update-icon-cache -f -t $(ICONPATH) - -NAME = subsurface -ICONFILE = $(NAME)-icon.svg -DESKTOPFILE = $(NAME).desktop -MANFILES = $(NAME).1 -XSLTFILES = xslt/*.xslt - -VERSION_FILE = version.h -# There's only one line in $(VERSION_FILE); use the shell builtin `read' -STORED_VERSION_STRING = \ - $(subst ",,$(shell [ ! -r $(VERSION_FILE) ] || \ - read ignore ignore v <$(VERSION_FILE) && echo $$v)) -#" workaround editor syntax highlighting quirk - UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win|gnu|kfreebsd") -GET_VERSION = ./scripts/get-version -VERSION_STRING := $(shell $(GET_VERSION) linux || echo "v$(VERSION)") -# Mac Info.plist style with three numbers 1.2.3 -CFBUNDLEVERSION_STRING := $(shell $(GET_VERSION) darwin $(VERSION_STRING) || \ - echo "$(VERSION).0") -# Windows .nsi style with four numbers 1.2.3.4 -PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ - echo "$(VERSION).0.0") # find libdivecomputer # First deal with the cross compile environment and with Mac. @@ -148,6 +117,30 @@ ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) +# These are the main rules + +# these locations seem to work for SuSE and Fedora +# prefix = $(HOME) +prefix = $(DESTDIR)/usr +BINDIR = $(prefix)/bin +DATADIR = $(prefix)/share +DESKTOPDIR = $(DATADIR)/applications +ICONPATH = $(DATADIR)/icons/hicolor +ICONDIR = $(ICONPATH)/scalable/apps +MANDIR = $(DATADIR)/man/man1 +XSLTDIR = $(DATADIR)/subsurface/xslt +gtk_update_icon_cache = gtk-update-icon-cache -f -t $(ICONPATH) + +NAME = subsurface +ICONFILE = $(NAME)-icon.svg +DESKTOPFILE = $(NAME).desktop +MANFILES = $(NAME).1 +XSLTFILES = xslt/*.xslt + +EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ + $(LIBDIVECOMPUTERCFLAGS) \ + $(LIBSOUPCFLAGS) $(GCONF2CFLAGS) + QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o @@ -159,6 +152,20 @@ OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ webservice.o sha1.o $(RESFILE) $(QTOBJS) $(GTKOBJS) +ifneq ($(SQLITE3FLAGS),) + EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) +endif +ifneq ($(ZIPFLAGS),) + EXTRA_FLAGS += -DLIBZIP $(ZIPFLAGS) +endif +ifneq ($(strip $(LIBXSLT)),) + EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) +endif +ifneq ($(strip $(LIBOSMGPSMAP)),) + OBJS += gps.o + EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) +endif + ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) OBJS += linux.o else ifeq ($(UNAME), darwin) @@ -179,7 +186,6 @@ else XSLTDIR = .\\xslt endif - LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) @@ -191,6 +197,26 @@ OBJS_NEEDING_MOC = OBJS_NEEDING_UIC = HEADERS_NEEDING_MOC = +# These are the generic rules for building + +# Rules for building and creating the version file + +VERSION_FILE = version.h +# There's only one line in $(VERSION_FILE); use the shell builtin `read' +STORED_VERSION_STRING = \ + $(subst ",,$(shell [ ! -r $(VERSION_FILE) ] || \ + read ignore ignore v <$(VERSION_FILE) && echo $$v)) +#" workaround editor syntax highlighting quirk + +GET_VERSION = ./scripts/get-version +VERSION_STRING := $(shell $(GET_VERSION) linux || echo "v$(VERSION)") +# Mac Info.plist style with three numbers 1.2.3 +CFBUNDLEVERSION_STRING := $(shell $(GET_VERSION) darwin $(VERSION_STRING) || \ + echo "$(VERSION).0") +# Windows .nsi style with four numbers 1.2.3.4 +PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ + echo "$(VERSION).0.0") + MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) # Add the objects for the header files which define QObject subclasses @@ -301,24 +327,6 @@ update-po-files: tx push -s tx pull -af -EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ - $(LIBDIVECOMPUTERCFLAGS) \ - $(LIBSOUPCFLAGS) $(GCONF2CFLAGS) - -ifneq ($(SQLITE3FLAGS),) - EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) -endif -ifneq ($(ZIPFLAGS),) - EXTRA_FLAGS += -DLIBZIP $(ZIPFLAGS) -endif -ifneq ($(strip $(LIBXSLT)),) - EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) -endif -ifneq ($(strip $(LIBOSMGPSMAP)),) - OBJS += gps.o - EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) -endif - MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS)) %.o: %.c From f6d133f3872d700a7c99be515bc1369f265f550a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 22 Apr 2013 23:28:29 -0700 Subject: [PATCH 049/226] Create Rules.mk and Configure.mk by copying the Makefile This is to help Git know that the two files are the same content as the Makefile. The next commit will trim the files to what they need to be. --- Configure.mk | 400 +++++++++++++++++++++++++++++++++++++++++++++++++++ Rules.mk | 400 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 800 insertions(+) create mode 100644 Configure.mk create mode 100644 Rules.mk diff --git a/Configure.mk b/Configure.mk new file mode 100644 index 000000000..e882a353d --- /dev/null +++ b/Configure.mk @@ -0,0 +1,400 @@ +VERSION=3.0.2 + +CC=gcc +CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE +CXX=g++ +CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS +INSTALL=install + +# This are the detection rules +PKGCONFIG=pkg-config +XML2CONFIG=xml2-config +XSLCONFIG=xslt-config +QMAKE=qmake +MOC=moc +UIC=uic + +UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win|gnu|kfreebsd") + +# find libdivecomputer +# First deal with the cross compile environment and with Mac. +# For the native case, Linus doesn't want to trust pkg-config given +# how young libdivecomputer still is - so we check the typical +# subdirectories of /usr/local and /usr and then we give up. You can +# override by simply setting it here +# +ifeq ($(CC), i686-w64-mingw32-gcc) +# ok, we are cross building for Windows + LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer) + LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer) + RESFILE = packaging/windows/subsurface.res + LDFLAGS += -Wl,-subsystem,windows + LIBWINSOCK = -lwsock32 +else ifeq ($(UNAME), darwin) + LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer) + LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer) +else +libdc-local := $(wildcard /usr/local/lib/libdivecomputer.a) +libdc-local64 := $(wildcard /usr/local/lib64/libdivecomputer.a) +libdc-usr := $(wildcard /usr/lib/libdivecomputer.a) +libdc-usr64 := $(wildcard /usr/lib64/libdivecomputer.a) + +ifneq ($(LIBDCDEVEL),) + LIBDIVECOMPUTERDIR = ../libdivecomputer + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/src/.libs/libdivecomputer.a +else ifneq ($(strip $(libdc-local)),) + LIBDIVECOMPUTERDIR = /usr/local + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a +else ifneq ($(strip $(libdc-local64)),) + LIBDIVECOMPUTERDIR = /usr/local + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a +else ifneq ($(strip $(libdc-usr)),) + LIBDIVECOMPUTERDIR = /usr + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a +else ifneq ($(strip $(libdc-usr64)),) + LIBDIVECOMPUTERDIR = /usr + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a +else +$(error Cannot find libdivecomputer - please edit Makefile) +endif +endif + +# Libusb-1.0 is only required if libdivecomputer was built with it. +# And libdivecomputer is only built with it if libusb-1.0 is +# installed. So get libusb if it exists, but don't complain +# about it if it doesn't. +LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null) + +# Use qmake to find out which Qt version we are building for. +QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) +ifeq ($(QT_VERSION_MAJOR), 5) + QT_MODULES = Qt5Widgets Qt5Svg + QT_CORE = Qt5Core +else + QT_MODULES = QtGui QtSvg + QT_CORE = QtCore +endif + +# we need GLIB2CFLAGS for gettext +QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) $(GLIB2CFLAGS) +LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) +ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) + QTCXXFLAGS += -fPIE +endif + +LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) +ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) + LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0) + GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0) +else ifeq ($(UNAME), darwin) + LIBGTK += $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation -framework CoreServices + GTKCFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration) + GTK_MAC_BUNDLER = ~/.local/bin/gtk-mac-bundler +endif + +LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES) +LIBDIVECOMPUTER = $(LIBDIVECOMPUTERARCHIVE) $(LIBUSB) + +LIBXML2 = $(shell $(XML2CONFIG) --libs) +LIBXSLT = $(shell $(XSLCONFIG) --libs) +XML2CFLAGS = $(shell $(XML2CONFIG) --cflags) +GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0) +GTKCFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-2.0) +XSLCFLAGS = $(shell $(XSLCONFIG) --cflags) +OSMGPSMAPFLAGS += $(shell $(PKGCONFIG) --cflags osmgpsmap 2> /dev/null) +LIBOSMGPSMAP += $(shell $(PKGCONFIG) --libs osmgpsmap 2> /dev/null) +LIBSOUPCFLAGS = $(shell $(PKGCONFIG) --cflags libsoup-2.4) +LIBSOUP = $(shell $(PKGCONFIG) --libs libsoup-2.4) + +LIBZIP = $(shell $(PKGCONFIG) --libs libzip 2> /dev/null) +ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) + +LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) +SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) + +# These are the main rules + +# these locations seem to work for SuSE and Fedora +# prefix = $(HOME) +prefix = $(DESTDIR)/usr +BINDIR = $(prefix)/bin +DATADIR = $(prefix)/share +DESKTOPDIR = $(DATADIR)/applications +ICONPATH = $(DATADIR)/icons/hicolor +ICONDIR = $(ICONPATH)/scalable/apps +MANDIR = $(DATADIR)/man/man1 +XSLTDIR = $(DATADIR)/subsurface/xslt +gtk_update_icon_cache = gtk-update-icon-cache -f -t $(ICONPATH) + +NAME = subsurface +ICONFILE = $(NAME)-icon.svg +DESKTOPFILE = $(NAME).desktop +MANFILES = $(NAME).1 +XSLTFILES = xslt/*.xslt + +EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ + $(LIBDIVECOMPUTERCFLAGS) \ + $(LIBSOUPCFLAGS) $(GCONF2CFLAGS) + +QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ + qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o + +GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o + +OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ + planner.o planner-gtk.o \ + parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ + qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ + webservice.o sha1.o $(RESFILE) $(QTOBJS) $(GTKOBJS) + +ifneq ($(SQLITE3FLAGS),) + EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) +endif +ifneq ($(ZIPFLAGS),) + EXTRA_FLAGS += -DLIBZIP $(ZIPFLAGS) +endif +ifneq ($(strip $(LIBXSLT)),) + EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) +endif +ifneq ($(strip $(LIBOSMGPSMAP)),) + OBJS += gps.o + EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) +endif + +ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) + OBJS += linux.o +else ifeq ($(UNAME), darwin) + OBJS += macos.o + MACOSXINSTALL = /Applications/Subsurface.app + MACOSXFILES = packaging/macosx + MACOSXSTAGING = $(MACOSXFILES)/Subsurface.app + INFOPLIST = $(MACOSXFILES)/Info.plist + INFOPLISTINPUT = $(INFOPLIST).in + LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) +else + OBSJ += windows.o + WINDOWSSTAGING = ./packaging/windows + WINMSGDIRS=$(addprefix share/locale/,$(shell ls po/*.po | sed -e 's/po\/\(..\)_.*/\1\/LC_MESSAGES/')) + NSIINPUTFILE = $(WINDOWSSTAGING)/subsurface.nsi.in + NSIFILE = $(WINDOWSSTAGING)/subsurface.nsi + MAKENSIS = makensis + XSLTDIR = .\\xslt +endif + +LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ + $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) + +MSGLANGS=$(notdir $(wildcard po/*.po)) + +# Add files to the following variables if the auto-detection based on the +# filename fails +OBJS_NEEDING_MOC = +OBJS_NEEDING_UIC = +HEADERS_NEEDING_MOC = + +# These are the generic rules for building + +# Rules for building and creating the version file + +VERSION_FILE = version.h +# There's only one line in $(VERSION_FILE); use the shell builtin `read' +STORED_VERSION_STRING = \ + $(subst ",,$(shell [ ! -r $(VERSION_FILE) ] || \ + read ignore ignore v <$(VERSION_FILE) && echo $$v)) +#" workaround editor syntax highlighting quirk + +GET_VERSION = ./scripts/get-version +VERSION_STRING := $(shell $(GET_VERSION) linux || echo "v$(VERSION)") +# Mac Info.plist style with three numbers 1.2.3 +CFBUNDLEVERSION_STRING := $(shell $(GET_VERSION) darwin $(VERSION_STRING) || \ + echo "$(VERSION).0") +# Windows .nsi style with four numbers 1.2.3.4 +PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ + echo "$(VERSION).0.0") + +MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) + +# Add the objects for the header files which define QObject subclasses +HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(OBJS:.o=.h)) +MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) + +ALL_OBJS = $(OBJS) $(MOC_OBJS) + +DEPS = $(wildcard .dep/*.dep) + +all: $(NAME) + +$(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) + $(CXX) $(LDFLAGS) -o $(NAME) $(ALL_OBJS) $(LIBS) + +gen_version_file: +ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING)) + $(info updating $(VERSION_FILE) to $(VERSION_STRING)) + @echo \#define VERSION_STRING \"$(VERSION_STRING)\" >$(VERSION_FILE) +endif + +install: all + $(INSTALL) -d -m 755 $(BINDIR) + $(INSTALL) $(NAME) $(BINDIR) + $(INSTALL) -d -m 755 $(DESKTOPDIR) + $(INSTALL) $(DESKTOPFILE) $(DESKTOPDIR) + $(INSTALL) -d -m 755 $(ICONDIR) + $(INSTALL) -m 644 $(ICONFILE) $(ICONDIR) + @-if test -z "$(DESTDIR)"; then \ + $(gtk_update_icon_cache); \ + fi + $(INSTALL) -d -m 755 $(MANDIR) + $(INSTALL) -m 644 $(MANFILES) $(MANDIR) + @-if test ! -z "$(XSLT)"; then \ + $(INSTALL) -d -m 755 $(DATADIR)/subsurface; \ + $(INSTALL) -d -m 755 $(XSLTDIR); \ + $(INSTALL) -m 644 $(XSLTFILES) $(XSLTDIR); \ + fi + for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ + $(INSTALL) -d $(prefix)/$$LOC; \ + $(INSTALL) -m 644 $$LOC/subsurface.mo $(prefix)/$$LOC/subsurface.mo; \ + done + + +install-macosx: all + $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources + $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/MacOS + $(INSTALL) $(NAME) $(MACOSXINSTALL)/Contents/MacOS/$(NAME)-bin + $(INSTALL) $(MACOSXFILES)/$(NAME).sh $(MACOSXINSTALL)/Contents/MacOS/$(NAME) + $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXINSTALL)/Contents/ + $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXINSTALL)/Contents/ + $(INSTALL) $(ICONFILE) $(MACOSXINSTALL)/Contents/Resources/ + $(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXINSTALL)/Contents/Resources/ + for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ + $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/$$LOC; \ + $(INSTALL) $$LOC/subsurface.mo $(MACOSXINSTALL)/Contents/Resources/$$LOC/subsurface.mo; \ + done + @-if test ! -z "$(XSLT)"; then \ + $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/xslt; \ + $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXINSTALL)/Contents/Resources/xslt/; \ + fi + + +create-macosx-bundle: all + $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources + $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/MacOS + $(INSTALL) $(NAME) $(MACOSXSTAGING)/Contents/MacOS/ + $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXSTAGING)/Contents/ + $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXSTAGING)/Contents/ + $(INSTALL) $(ICONFILE) $(MACOSXSTAGING)/Contents/Resources/ + $(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXSTAGING)/Contents/Resources/ + for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ + $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/$$LOC; \ + $(INSTALL) $$LOC/subsurface.mo $(MACOSXSTAGING)/Contents/Resources/$$LOC/subsurface.mo; \ + done + @-if test ! -z "$(XSLT)"; then \ + $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/xslt; \ + $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXSTAGING)/Contents/Resources/xslt/; \ + fi + $(GTK_MAC_BUNDLER) packaging/macosx/subsurface.bundle + +sign-macosx-bundle: all + codesign -s "3A8CE62A483083EDEA5581A61E770EC1FA8BECE8" /Applications/Subsurface.app/Contents/MacOS/subsurface-bin + +install-cross-windows: all + $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/share/locale + for MSG in $(WINMSGDIRS); do\ + $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$MSG;\ + $(INSTALL) $(CROSS_PATH)/$$MSG/* $(WINDOWSSTAGING)/$$MSG;\ + done + for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ + $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$LOC; \ + $(INSTALL) $$LOC/subsurface.mo $(WINDOWSSTAGING)/$$LOC/subsurface.mo; \ + done + +create-windows-installer: all $(NSIFILE) install-cross-windows + $(MAKENSIS) $(NSIFILE) + +$(NSIFILE): $(NSIINPUTFILE) + $(shell cat $(NSIINPUTFILE) | sed -e 's/VERSIONTOKEN/$(VERSION_STRING)/;s/PRODVTOKEN/$(PRODVERSION_STRING)/' > $(NSIFILE)) + +$(INFOPLIST): $(INFOPLISTINPUT) + $(shell cat $(INFOPLISTINPUT) | sed -e 's/CFBUNDLEVERSION_TOKEN/$(CFBUNDLEVERSION_STRING)/' > $(INFOPLIST)) + +# Transifex merge the translations +update-po-files: + xgettext -o po/subsurface-new.pot -s -k_ -kN_ -ktr --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c qt-ui/*.cpp + tx push -s + tx pull -af + +MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS)) + +%.o: %.c + @echo ' CC' $< + @mkdir -p .dep .dep/qt-ui + @$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< + +%.o: %.cpp + @echo ' CXX' $< + @mkdir -p .dep .dep/qt-ui + @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< + +# Detect which files require the moc or uic tools to be run +CPP_NEEDING_MOC = $(shell grep -l -s '^\#include \".*\.moc\"' $(OBJS:.o=.cpp)) +OBJS_NEEDING_MOC += $(CPP_NEEDING_MOC:.cpp=.o) + +CPP_NEEDING_UIC = $(shell grep -l -s '^\#include \"ui_.*\.h\"' $(OBJS:.o=.cpp)) +OBJS_NEEDING_UIC += $(CPP_NEEDING_UIC:.cpp=.o) + +# This rule is for running the moc on QObject subclasses defined in the .h +# files. +%.moc.cpp: %.h + @echo ' MOC' $< + @$(MOC) $(MOCFLAGS) $< -o $@ + +# This rule is for running the moc on QObject subclasses defined in the .cpp +# files; remember to #include ".moc" at the end of the .cpp file, or +# you'll get linker errors ("undefined vtable for...") +%.moc: %.cpp + @echo ' MOC' $< + @$(MOC) -i $(MOCFLAGS) $< -o $@ + +# This creates the ui headers. +ui_%.h: %.ui + @echo ' UIC' $< + @$(UIC) $< -o $@ + +$(OBJS_NEEDING_MOC): %.o: %.moc +$(OBJS_NEEDING_UIC): qt-ui/%.o: qt-ui/ui_%.h + +share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases + mkdir -p $(dir $@) + msgfmt -c -o $@ po/$*.po + @-if test -s po/$*.aliases; then \ + for ALIAS in `cat po/$*.aliases`; do \ + mkdir -p share/locale/$$ALIAS/LC_MESSAGES; \ + cp $@ share/locale/$$ALIAS/LC_MESSAGES; \ + done; \ + fi + +satellite.png: satellite.svg + convert -transparent white -resize 11x16 -depth 8 $< $@ + +# This should work, but it doesn't get the colors quite right - so I manually converted with Gimp +# convert -colorspace RGB -transparent white -resize 256x256 subsurface-icon.svg subsurface-icon.png +# +# The following creates the pixbuf data in .h files with the basename followed by '_pixmap' +# as name of the data structure +%.h: %.png + @echo ' gdk-pixbuf-csource' $< + @gdk-pixbuf-csource --struct --name `echo $* | sed 's/-/_/g'`_pixbuf $< > $@ + +doc: + $(MAKE) -C Documentation doc + +clean: + rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ + $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h + rm -rf share .dep + +-include $(DEPS) diff --git a/Rules.mk b/Rules.mk new file mode 100644 index 000000000..e882a353d --- /dev/null +++ b/Rules.mk @@ -0,0 +1,400 @@ +VERSION=3.0.2 + +CC=gcc +CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE +CXX=g++ +CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS +INSTALL=install + +# This are the detection rules +PKGCONFIG=pkg-config +XML2CONFIG=xml2-config +XSLCONFIG=xslt-config +QMAKE=qmake +MOC=moc +UIC=uic + +UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win|gnu|kfreebsd") + +# find libdivecomputer +# First deal with the cross compile environment and with Mac. +# For the native case, Linus doesn't want to trust pkg-config given +# how young libdivecomputer still is - so we check the typical +# subdirectories of /usr/local and /usr and then we give up. You can +# override by simply setting it here +# +ifeq ($(CC), i686-w64-mingw32-gcc) +# ok, we are cross building for Windows + LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer) + LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer) + RESFILE = packaging/windows/subsurface.res + LDFLAGS += -Wl,-subsystem,windows + LIBWINSOCK = -lwsock32 +else ifeq ($(UNAME), darwin) + LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer) + LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer) +else +libdc-local := $(wildcard /usr/local/lib/libdivecomputer.a) +libdc-local64 := $(wildcard /usr/local/lib64/libdivecomputer.a) +libdc-usr := $(wildcard /usr/lib/libdivecomputer.a) +libdc-usr64 := $(wildcard /usr/lib64/libdivecomputer.a) + +ifneq ($(LIBDCDEVEL),) + LIBDIVECOMPUTERDIR = ../libdivecomputer + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/src/.libs/libdivecomputer.a +else ifneq ($(strip $(libdc-local)),) + LIBDIVECOMPUTERDIR = /usr/local + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a +else ifneq ($(strip $(libdc-local64)),) + LIBDIVECOMPUTERDIR = /usr/local + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a +else ifneq ($(strip $(libdc-usr)),) + LIBDIVECOMPUTERDIR = /usr + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a +else ifneq ($(strip $(libdc-usr64)),) + LIBDIVECOMPUTERDIR = /usr + LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include + LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a +else +$(error Cannot find libdivecomputer - please edit Makefile) +endif +endif + +# Libusb-1.0 is only required if libdivecomputer was built with it. +# And libdivecomputer is only built with it if libusb-1.0 is +# installed. So get libusb if it exists, but don't complain +# about it if it doesn't. +LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null) + +# Use qmake to find out which Qt version we are building for. +QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) +ifeq ($(QT_VERSION_MAJOR), 5) + QT_MODULES = Qt5Widgets Qt5Svg + QT_CORE = Qt5Core +else + QT_MODULES = QtGui QtSvg + QT_CORE = QtCore +endif + +# we need GLIB2CFLAGS for gettext +QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) $(GLIB2CFLAGS) +LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) +ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) + QTCXXFLAGS += -fPIE +endif + +LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) +ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) + LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0) + GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0) +else ifeq ($(UNAME), darwin) + LIBGTK += $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation -framework CoreServices + GTKCFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration) + GTK_MAC_BUNDLER = ~/.local/bin/gtk-mac-bundler +endif + +LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES) +LIBDIVECOMPUTER = $(LIBDIVECOMPUTERARCHIVE) $(LIBUSB) + +LIBXML2 = $(shell $(XML2CONFIG) --libs) +LIBXSLT = $(shell $(XSLCONFIG) --libs) +XML2CFLAGS = $(shell $(XML2CONFIG) --cflags) +GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0) +GTKCFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-2.0) +XSLCFLAGS = $(shell $(XSLCONFIG) --cflags) +OSMGPSMAPFLAGS += $(shell $(PKGCONFIG) --cflags osmgpsmap 2> /dev/null) +LIBOSMGPSMAP += $(shell $(PKGCONFIG) --libs osmgpsmap 2> /dev/null) +LIBSOUPCFLAGS = $(shell $(PKGCONFIG) --cflags libsoup-2.4) +LIBSOUP = $(shell $(PKGCONFIG) --libs libsoup-2.4) + +LIBZIP = $(shell $(PKGCONFIG) --libs libzip 2> /dev/null) +ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) + +LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) +SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) + +# These are the main rules + +# these locations seem to work for SuSE and Fedora +# prefix = $(HOME) +prefix = $(DESTDIR)/usr +BINDIR = $(prefix)/bin +DATADIR = $(prefix)/share +DESKTOPDIR = $(DATADIR)/applications +ICONPATH = $(DATADIR)/icons/hicolor +ICONDIR = $(ICONPATH)/scalable/apps +MANDIR = $(DATADIR)/man/man1 +XSLTDIR = $(DATADIR)/subsurface/xslt +gtk_update_icon_cache = gtk-update-icon-cache -f -t $(ICONPATH) + +NAME = subsurface +ICONFILE = $(NAME)-icon.svg +DESKTOPFILE = $(NAME).desktop +MANFILES = $(NAME).1 +XSLTFILES = xslt/*.xslt + +EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ + $(LIBDIVECOMPUTERCFLAGS) \ + $(LIBSOUPCFLAGS) $(GCONF2CFLAGS) + +QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ + qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o + +GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o + +OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ + planner.o planner-gtk.o \ + parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ + qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ + webservice.o sha1.o $(RESFILE) $(QTOBJS) $(GTKOBJS) + +ifneq ($(SQLITE3FLAGS),) + EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) +endif +ifneq ($(ZIPFLAGS),) + EXTRA_FLAGS += -DLIBZIP $(ZIPFLAGS) +endif +ifneq ($(strip $(LIBXSLT)),) + EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) +endif +ifneq ($(strip $(LIBOSMGPSMAP)),) + OBJS += gps.o + EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) +endif + +ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) + OBJS += linux.o +else ifeq ($(UNAME), darwin) + OBJS += macos.o + MACOSXINSTALL = /Applications/Subsurface.app + MACOSXFILES = packaging/macosx + MACOSXSTAGING = $(MACOSXFILES)/Subsurface.app + INFOPLIST = $(MACOSXFILES)/Info.plist + INFOPLISTINPUT = $(INFOPLIST).in + LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) +else + OBSJ += windows.o + WINDOWSSTAGING = ./packaging/windows + WINMSGDIRS=$(addprefix share/locale/,$(shell ls po/*.po | sed -e 's/po\/\(..\)_.*/\1\/LC_MESSAGES/')) + NSIINPUTFILE = $(WINDOWSSTAGING)/subsurface.nsi.in + NSIFILE = $(WINDOWSSTAGING)/subsurface.nsi + MAKENSIS = makensis + XSLTDIR = .\\xslt +endif + +LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ + $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) + +MSGLANGS=$(notdir $(wildcard po/*.po)) + +# Add files to the following variables if the auto-detection based on the +# filename fails +OBJS_NEEDING_MOC = +OBJS_NEEDING_UIC = +HEADERS_NEEDING_MOC = + +# These are the generic rules for building + +# Rules for building and creating the version file + +VERSION_FILE = version.h +# There's only one line in $(VERSION_FILE); use the shell builtin `read' +STORED_VERSION_STRING = \ + $(subst ",,$(shell [ ! -r $(VERSION_FILE) ] || \ + read ignore ignore v <$(VERSION_FILE) && echo $$v)) +#" workaround editor syntax highlighting quirk + +GET_VERSION = ./scripts/get-version +VERSION_STRING := $(shell $(GET_VERSION) linux || echo "v$(VERSION)") +# Mac Info.plist style with three numbers 1.2.3 +CFBUNDLEVERSION_STRING := $(shell $(GET_VERSION) darwin $(VERSION_STRING) || \ + echo "$(VERSION).0") +# Windows .nsi style with four numbers 1.2.3.4 +PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ + echo "$(VERSION).0.0") + +MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) + +# Add the objects for the header files which define QObject subclasses +HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(OBJS:.o=.h)) +MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) + +ALL_OBJS = $(OBJS) $(MOC_OBJS) + +DEPS = $(wildcard .dep/*.dep) + +all: $(NAME) + +$(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) + $(CXX) $(LDFLAGS) -o $(NAME) $(ALL_OBJS) $(LIBS) + +gen_version_file: +ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING)) + $(info updating $(VERSION_FILE) to $(VERSION_STRING)) + @echo \#define VERSION_STRING \"$(VERSION_STRING)\" >$(VERSION_FILE) +endif + +install: all + $(INSTALL) -d -m 755 $(BINDIR) + $(INSTALL) $(NAME) $(BINDIR) + $(INSTALL) -d -m 755 $(DESKTOPDIR) + $(INSTALL) $(DESKTOPFILE) $(DESKTOPDIR) + $(INSTALL) -d -m 755 $(ICONDIR) + $(INSTALL) -m 644 $(ICONFILE) $(ICONDIR) + @-if test -z "$(DESTDIR)"; then \ + $(gtk_update_icon_cache); \ + fi + $(INSTALL) -d -m 755 $(MANDIR) + $(INSTALL) -m 644 $(MANFILES) $(MANDIR) + @-if test ! -z "$(XSLT)"; then \ + $(INSTALL) -d -m 755 $(DATADIR)/subsurface; \ + $(INSTALL) -d -m 755 $(XSLTDIR); \ + $(INSTALL) -m 644 $(XSLTFILES) $(XSLTDIR); \ + fi + for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ + $(INSTALL) -d $(prefix)/$$LOC; \ + $(INSTALL) -m 644 $$LOC/subsurface.mo $(prefix)/$$LOC/subsurface.mo; \ + done + + +install-macosx: all + $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources + $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/MacOS + $(INSTALL) $(NAME) $(MACOSXINSTALL)/Contents/MacOS/$(NAME)-bin + $(INSTALL) $(MACOSXFILES)/$(NAME).sh $(MACOSXINSTALL)/Contents/MacOS/$(NAME) + $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXINSTALL)/Contents/ + $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXINSTALL)/Contents/ + $(INSTALL) $(ICONFILE) $(MACOSXINSTALL)/Contents/Resources/ + $(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXINSTALL)/Contents/Resources/ + for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ + $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/$$LOC; \ + $(INSTALL) $$LOC/subsurface.mo $(MACOSXINSTALL)/Contents/Resources/$$LOC/subsurface.mo; \ + done + @-if test ! -z "$(XSLT)"; then \ + $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/xslt; \ + $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXINSTALL)/Contents/Resources/xslt/; \ + fi + + +create-macosx-bundle: all + $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources + $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/MacOS + $(INSTALL) $(NAME) $(MACOSXSTAGING)/Contents/MacOS/ + $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXSTAGING)/Contents/ + $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXSTAGING)/Contents/ + $(INSTALL) $(ICONFILE) $(MACOSXSTAGING)/Contents/Resources/ + $(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXSTAGING)/Contents/Resources/ + for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ + $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/$$LOC; \ + $(INSTALL) $$LOC/subsurface.mo $(MACOSXSTAGING)/Contents/Resources/$$LOC/subsurface.mo; \ + done + @-if test ! -z "$(XSLT)"; then \ + $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/xslt; \ + $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXSTAGING)/Contents/Resources/xslt/; \ + fi + $(GTK_MAC_BUNDLER) packaging/macosx/subsurface.bundle + +sign-macosx-bundle: all + codesign -s "3A8CE62A483083EDEA5581A61E770EC1FA8BECE8" /Applications/Subsurface.app/Contents/MacOS/subsurface-bin + +install-cross-windows: all + $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/share/locale + for MSG in $(WINMSGDIRS); do\ + $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$MSG;\ + $(INSTALL) $(CROSS_PATH)/$$MSG/* $(WINDOWSSTAGING)/$$MSG;\ + done + for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ + $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$LOC; \ + $(INSTALL) $$LOC/subsurface.mo $(WINDOWSSTAGING)/$$LOC/subsurface.mo; \ + done + +create-windows-installer: all $(NSIFILE) install-cross-windows + $(MAKENSIS) $(NSIFILE) + +$(NSIFILE): $(NSIINPUTFILE) + $(shell cat $(NSIINPUTFILE) | sed -e 's/VERSIONTOKEN/$(VERSION_STRING)/;s/PRODVTOKEN/$(PRODVERSION_STRING)/' > $(NSIFILE)) + +$(INFOPLIST): $(INFOPLISTINPUT) + $(shell cat $(INFOPLISTINPUT) | sed -e 's/CFBUNDLEVERSION_TOKEN/$(CFBUNDLEVERSION_STRING)/' > $(INFOPLIST)) + +# Transifex merge the translations +update-po-files: + xgettext -o po/subsurface-new.pot -s -k_ -kN_ -ktr --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c qt-ui/*.cpp + tx push -s + tx pull -af + +MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS)) + +%.o: %.c + @echo ' CC' $< + @mkdir -p .dep .dep/qt-ui + @$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< + +%.o: %.cpp + @echo ' CXX' $< + @mkdir -p .dep .dep/qt-ui + @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< + +# Detect which files require the moc or uic tools to be run +CPP_NEEDING_MOC = $(shell grep -l -s '^\#include \".*\.moc\"' $(OBJS:.o=.cpp)) +OBJS_NEEDING_MOC += $(CPP_NEEDING_MOC:.cpp=.o) + +CPP_NEEDING_UIC = $(shell grep -l -s '^\#include \"ui_.*\.h\"' $(OBJS:.o=.cpp)) +OBJS_NEEDING_UIC += $(CPP_NEEDING_UIC:.cpp=.o) + +# This rule is for running the moc on QObject subclasses defined in the .h +# files. +%.moc.cpp: %.h + @echo ' MOC' $< + @$(MOC) $(MOCFLAGS) $< -o $@ + +# This rule is for running the moc on QObject subclasses defined in the .cpp +# files; remember to #include ".moc" at the end of the .cpp file, or +# you'll get linker errors ("undefined vtable for...") +%.moc: %.cpp + @echo ' MOC' $< + @$(MOC) -i $(MOCFLAGS) $< -o $@ + +# This creates the ui headers. +ui_%.h: %.ui + @echo ' UIC' $< + @$(UIC) $< -o $@ + +$(OBJS_NEEDING_MOC): %.o: %.moc +$(OBJS_NEEDING_UIC): qt-ui/%.o: qt-ui/ui_%.h + +share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases + mkdir -p $(dir $@) + msgfmt -c -o $@ po/$*.po + @-if test -s po/$*.aliases; then \ + for ALIAS in `cat po/$*.aliases`; do \ + mkdir -p share/locale/$$ALIAS/LC_MESSAGES; \ + cp $@ share/locale/$$ALIAS/LC_MESSAGES; \ + done; \ + fi + +satellite.png: satellite.svg + convert -transparent white -resize 11x16 -depth 8 $< $@ + +# This should work, but it doesn't get the colors quite right - so I manually converted with Gimp +# convert -colorspace RGB -transparent white -resize 256x256 subsurface-icon.svg subsurface-icon.png +# +# The following creates the pixbuf data in .h files with the basename followed by '_pixmap' +# as name of the data structure +%.h: %.png + @echo ' gdk-pixbuf-csource' $< + @gdk-pixbuf-csource --struct --name `echo $* | sed 's/-/_/g'`_pixbuf $< > $@ + +doc: + $(MAKE) -C Documentation doc + +clean: + rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ + $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h + rm -rf share .dep + +-include $(DEPS) From d47b9045802f5f05dfafad982cdf4ab4b86a2c2c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 12 Apr 2013 23:42:14 -0700 Subject: [PATCH 050/226] Trim Makefile, Configure.mk and Rules.mk Configure.mk contains the detection rules, whereas Rules.mk contains the rules to actually build Subsurface. This simplifies Makefile greatly, which is the file that should be actually modified during regular updates to the codebase. Signed-off-by: Thiago Macieira --- Configure.mk | 292 +---------------------------------------------- Makefile | 316 +-------------------------------------------------- Rules.mk | 202 +------------------------------- 3 files changed, 5 insertions(+), 805 deletions(-) diff --git a/Configure.mk b/Configure.mk index e882a353d..f9b9e0674 100644 --- a/Configure.mk +++ b/Configure.mk @@ -1,12 +1,6 @@ -VERSION=3.0.2 +# -*- Makefile -*- +# This file contains the detection rules -CC=gcc -CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE -CXX=g++ -CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS -INSTALL=install - -# This are the detection rules PKGCONFIG=pkg-config XML2CONFIG=xml2-config XSLCONFIG=xslt-config @@ -116,285 +110,3 @@ ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) - -# These are the main rules - -# these locations seem to work for SuSE and Fedora -# prefix = $(HOME) -prefix = $(DESTDIR)/usr -BINDIR = $(prefix)/bin -DATADIR = $(prefix)/share -DESKTOPDIR = $(DATADIR)/applications -ICONPATH = $(DATADIR)/icons/hicolor -ICONDIR = $(ICONPATH)/scalable/apps -MANDIR = $(DATADIR)/man/man1 -XSLTDIR = $(DATADIR)/subsurface/xslt -gtk_update_icon_cache = gtk-update-icon-cache -f -t $(ICONPATH) - -NAME = subsurface -ICONFILE = $(NAME)-icon.svg -DESKTOPFILE = $(NAME).desktop -MANFILES = $(NAME).1 -XSLTFILES = xslt/*.xslt - -EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ - $(LIBDIVECOMPUTERCFLAGS) \ - $(LIBSOUPCFLAGS) $(GCONF2CFLAGS) - -QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ - qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o - -GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o - -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ - planner.o planner-gtk.o \ - parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ - qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ - webservice.o sha1.o $(RESFILE) $(QTOBJS) $(GTKOBJS) - -ifneq ($(SQLITE3FLAGS),) - EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) -endif -ifneq ($(ZIPFLAGS),) - EXTRA_FLAGS += -DLIBZIP $(ZIPFLAGS) -endif -ifneq ($(strip $(LIBXSLT)),) - EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) -endif -ifneq ($(strip $(LIBOSMGPSMAP)),) - OBJS += gps.o - EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) -endif - -ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) - OBJS += linux.o -else ifeq ($(UNAME), darwin) - OBJS += macos.o - MACOSXINSTALL = /Applications/Subsurface.app - MACOSXFILES = packaging/macosx - MACOSXSTAGING = $(MACOSXFILES)/Subsurface.app - INFOPLIST = $(MACOSXFILES)/Info.plist - INFOPLISTINPUT = $(INFOPLIST).in - LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) -else - OBSJ += windows.o - WINDOWSSTAGING = ./packaging/windows - WINMSGDIRS=$(addprefix share/locale/,$(shell ls po/*.po | sed -e 's/po\/\(..\)_.*/\1\/LC_MESSAGES/')) - NSIINPUTFILE = $(WINDOWSSTAGING)/subsurface.nsi.in - NSIFILE = $(WINDOWSSTAGING)/subsurface.nsi - MAKENSIS = makensis - XSLTDIR = .\\xslt -endif - -LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ - $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) - -MSGLANGS=$(notdir $(wildcard po/*.po)) - -# Add files to the following variables if the auto-detection based on the -# filename fails -OBJS_NEEDING_MOC = -OBJS_NEEDING_UIC = -HEADERS_NEEDING_MOC = - -# These are the generic rules for building - -# Rules for building and creating the version file - -VERSION_FILE = version.h -# There's only one line in $(VERSION_FILE); use the shell builtin `read' -STORED_VERSION_STRING = \ - $(subst ",,$(shell [ ! -r $(VERSION_FILE) ] || \ - read ignore ignore v <$(VERSION_FILE) && echo $$v)) -#" workaround editor syntax highlighting quirk - -GET_VERSION = ./scripts/get-version -VERSION_STRING := $(shell $(GET_VERSION) linux || echo "v$(VERSION)") -# Mac Info.plist style with three numbers 1.2.3 -CFBUNDLEVERSION_STRING := $(shell $(GET_VERSION) darwin $(VERSION_STRING) || \ - echo "$(VERSION).0") -# Windows .nsi style with four numbers 1.2.3.4 -PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ - echo "$(VERSION).0.0") - -MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) - -# Add the objects for the header files which define QObject subclasses -HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(OBJS:.o=.h)) -MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) - -ALL_OBJS = $(OBJS) $(MOC_OBJS) - -DEPS = $(wildcard .dep/*.dep) - -all: $(NAME) - -$(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) - $(CXX) $(LDFLAGS) -o $(NAME) $(ALL_OBJS) $(LIBS) - -gen_version_file: -ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING)) - $(info updating $(VERSION_FILE) to $(VERSION_STRING)) - @echo \#define VERSION_STRING \"$(VERSION_STRING)\" >$(VERSION_FILE) -endif - -install: all - $(INSTALL) -d -m 755 $(BINDIR) - $(INSTALL) $(NAME) $(BINDIR) - $(INSTALL) -d -m 755 $(DESKTOPDIR) - $(INSTALL) $(DESKTOPFILE) $(DESKTOPDIR) - $(INSTALL) -d -m 755 $(ICONDIR) - $(INSTALL) -m 644 $(ICONFILE) $(ICONDIR) - @-if test -z "$(DESTDIR)"; then \ - $(gtk_update_icon_cache); \ - fi - $(INSTALL) -d -m 755 $(MANDIR) - $(INSTALL) -m 644 $(MANFILES) $(MANDIR) - @-if test ! -z "$(XSLT)"; then \ - $(INSTALL) -d -m 755 $(DATADIR)/subsurface; \ - $(INSTALL) -d -m 755 $(XSLTDIR); \ - $(INSTALL) -m 644 $(XSLTFILES) $(XSLTDIR); \ - fi - for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ - $(INSTALL) -d $(prefix)/$$LOC; \ - $(INSTALL) -m 644 $$LOC/subsurface.mo $(prefix)/$$LOC/subsurface.mo; \ - done - - -install-macosx: all - $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources - $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/MacOS - $(INSTALL) $(NAME) $(MACOSXINSTALL)/Contents/MacOS/$(NAME)-bin - $(INSTALL) $(MACOSXFILES)/$(NAME).sh $(MACOSXINSTALL)/Contents/MacOS/$(NAME) - $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXINSTALL)/Contents/ - $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXINSTALL)/Contents/ - $(INSTALL) $(ICONFILE) $(MACOSXINSTALL)/Contents/Resources/ - $(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXINSTALL)/Contents/Resources/ - for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ - $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/$$LOC; \ - $(INSTALL) $$LOC/subsurface.mo $(MACOSXINSTALL)/Contents/Resources/$$LOC/subsurface.mo; \ - done - @-if test ! -z "$(XSLT)"; then \ - $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/xslt; \ - $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXINSTALL)/Contents/Resources/xslt/; \ - fi - - -create-macosx-bundle: all - $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources - $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/MacOS - $(INSTALL) $(NAME) $(MACOSXSTAGING)/Contents/MacOS/ - $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXSTAGING)/Contents/ - $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXSTAGING)/Contents/ - $(INSTALL) $(ICONFILE) $(MACOSXSTAGING)/Contents/Resources/ - $(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXSTAGING)/Contents/Resources/ - for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ - $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/$$LOC; \ - $(INSTALL) $$LOC/subsurface.mo $(MACOSXSTAGING)/Contents/Resources/$$LOC/subsurface.mo; \ - done - @-if test ! -z "$(XSLT)"; then \ - $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/xslt; \ - $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXSTAGING)/Contents/Resources/xslt/; \ - fi - $(GTK_MAC_BUNDLER) packaging/macosx/subsurface.bundle - -sign-macosx-bundle: all - codesign -s "3A8CE62A483083EDEA5581A61E770EC1FA8BECE8" /Applications/Subsurface.app/Contents/MacOS/subsurface-bin - -install-cross-windows: all - $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/share/locale - for MSG in $(WINMSGDIRS); do\ - $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$MSG;\ - $(INSTALL) $(CROSS_PATH)/$$MSG/* $(WINDOWSSTAGING)/$$MSG;\ - done - for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ - $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$LOC; \ - $(INSTALL) $$LOC/subsurface.mo $(WINDOWSSTAGING)/$$LOC/subsurface.mo; \ - done - -create-windows-installer: all $(NSIFILE) install-cross-windows - $(MAKENSIS) $(NSIFILE) - -$(NSIFILE): $(NSIINPUTFILE) - $(shell cat $(NSIINPUTFILE) | sed -e 's/VERSIONTOKEN/$(VERSION_STRING)/;s/PRODVTOKEN/$(PRODVERSION_STRING)/' > $(NSIFILE)) - -$(INFOPLIST): $(INFOPLISTINPUT) - $(shell cat $(INFOPLISTINPUT) | sed -e 's/CFBUNDLEVERSION_TOKEN/$(CFBUNDLEVERSION_STRING)/' > $(INFOPLIST)) - -# Transifex merge the translations -update-po-files: - xgettext -o po/subsurface-new.pot -s -k_ -kN_ -ktr --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c qt-ui/*.cpp - tx push -s - tx pull -af - -MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS)) - -%.o: %.c - @echo ' CC' $< - @mkdir -p .dep .dep/qt-ui - @$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< - -%.o: %.cpp - @echo ' CXX' $< - @mkdir -p .dep .dep/qt-ui - @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< - -# Detect which files require the moc or uic tools to be run -CPP_NEEDING_MOC = $(shell grep -l -s '^\#include \".*\.moc\"' $(OBJS:.o=.cpp)) -OBJS_NEEDING_MOC += $(CPP_NEEDING_MOC:.cpp=.o) - -CPP_NEEDING_UIC = $(shell grep -l -s '^\#include \"ui_.*\.h\"' $(OBJS:.o=.cpp)) -OBJS_NEEDING_UIC += $(CPP_NEEDING_UIC:.cpp=.o) - -# This rule is for running the moc on QObject subclasses defined in the .h -# files. -%.moc.cpp: %.h - @echo ' MOC' $< - @$(MOC) $(MOCFLAGS) $< -o $@ - -# This rule is for running the moc on QObject subclasses defined in the .cpp -# files; remember to #include ".moc" at the end of the .cpp file, or -# you'll get linker errors ("undefined vtable for...") -%.moc: %.cpp - @echo ' MOC' $< - @$(MOC) -i $(MOCFLAGS) $< -o $@ - -# This creates the ui headers. -ui_%.h: %.ui - @echo ' UIC' $< - @$(UIC) $< -o $@ - -$(OBJS_NEEDING_MOC): %.o: %.moc -$(OBJS_NEEDING_UIC): qt-ui/%.o: qt-ui/ui_%.h - -share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases - mkdir -p $(dir $@) - msgfmt -c -o $@ po/$*.po - @-if test -s po/$*.aliases; then \ - for ALIAS in `cat po/$*.aliases`; do \ - mkdir -p share/locale/$$ALIAS/LC_MESSAGES; \ - cp $@ share/locale/$$ALIAS/LC_MESSAGES; \ - done; \ - fi - -satellite.png: satellite.svg - convert -transparent white -resize 11x16 -depth 8 $< $@ - -# This should work, but it doesn't get the colors quite right - so I manually converted with Gimp -# convert -colorspace RGB -transparent white -resize 256x256 subsurface-icon.svg subsurface-icon.png -# -# The following creates the pixbuf data in .h files with the basename followed by '_pixmap' -# as name of the data structure -%.h: %.png - @echo ' gdk-pixbuf-csource' $< - @gdk-pixbuf-csource --struct --name `echo $* | sed 's/-/_/g'`_pixbuf $< > $@ - -doc: - $(MAKE) -C Documentation doc - -clean: - rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ - $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h - rm -rf share .dep - --include $(DEPS) diff --git a/Makefile b/Makefile index e882a353d..3308c9f41 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +include Configure.mk VERSION=3.0.2 CC=gcc @@ -6,119 +7,6 @@ CXX=g++ CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS INSTALL=install -# This are the detection rules -PKGCONFIG=pkg-config -XML2CONFIG=xml2-config -XSLCONFIG=xslt-config -QMAKE=qmake -MOC=moc -UIC=uic - -UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win|gnu|kfreebsd") - -# find libdivecomputer -# First deal with the cross compile environment and with Mac. -# For the native case, Linus doesn't want to trust pkg-config given -# how young libdivecomputer still is - so we check the typical -# subdirectories of /usr/local and /usr and then we give up. You can -# override by simply setting it here -# -ifeq ($(CC), i686-w64-mingw32-gcc) -# ok, we are cross building for Windows - LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer) - LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer) - RESFILE = packaging/windows/subsurface.res - LDFLAGS += -Wl,-subsystem,windows - LIBWINSOCK = -lwsock32 -else ifeq ($(UNAME), darwin) - LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer) - LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer) -else -libdc-local := $(wildcard /usr/local/lib/libdivecomputer.a) -libdc-local64 := $(wildcard /usr/local/lib64/libdivecomputer.a) -libdc-usr := $(wildcard /usr/lib/libdivecomputer.a) -libdc-usr64 := $(wildcard /usr/lib64/libdivecomputer.a) - -ifneq ($(LIBDCDEVEL),) - LIBDIVECOMPUTERDIR = ../libdivecomputer - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/src/.libs/libdivecomputer.a -else ifneq ($(strip $(libdc-local)),) - LIBDIVECOMPUTERDIR = /usr/local - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a -else ifneq ($(strip $(libdc-local64)),) - LIBDIVECOMPUTERDIR = /usr/local - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a -else ifneq ($(strip $(libdc-usr)),) - LIBDIVECOMPUTERDIR = /usr - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a -else ifneq ($(strip $(libdc-usr64)),) - LIBDIVECOMPUTERDIR = /usr - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a -else -$(error Cannot find libdivecomputer - please edit Makefile) -endif -endif - -# Libusb-1.0 is only required if libdivecomputer was built with it. -# And libdivecomputer is only built with it if libusb-1.0 is -# installed. So get libusb if it exists, but don't complain -# about it if it doesn't. -LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null) - -# Use qmake to find out which Qt version we are building for. -QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) -ifeq ($(QT_VERSION_MAJOR), 5) - QT_MODULES = Qt5Widgets Qt5Svg - QT_CORE = Qt5Core -else - QT_MODULES = QtGui QtSvg - QT_CORE = QtCore -endif - -# we need GLIB2CFLAGS for gettext -QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) $(GLIB2CFLAGS) -LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) -ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) - QTCXXFLAGS += -fPIE -endif - -LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) -ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) - LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0) - GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0) -else ifeq ($(UNAME), darwin) - LIBGTK += $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation -framework CoreServices - GTKCFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration) - GTK_MAC_BUNDLER = ~/.local/bin/gtk-mac-bundler -endif - -LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES) -LIBDIVECOMPUTER = $(LIBDIVECOMPUTERARCHIVE) $(LIBUSB) - -LIBXML2 = $(shell $(XML2CONFIG) --libs) -LIBXSLT = $(shell $(XSLCONFIG) --libs) -XML2CFLAGS = $(shell $(XML2CONFIG) --cflags) -GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0) -GTKCFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-2.0) -XSLCFLAGS = $(shell $(XSLCONFIG) --cflags) -OSMGPSMAPFLAGS += $(shell $(PKGCONFIG) --cflags osmgpsmap 2> /dev/null) -LIBOSMGPSMAP += $(shell $(PKGCONFIG) --libs osmgpsmap 2> /dev/null) -LIBSOUPCFLAGS = $(shell $(PKGCONFIG) --cflags libsoup-2.4) -LIBSOUP = $(shell $(PKGCONFIG) --libs libsoup-2.4) - -LIBZIP = $(shell $(PKGCONFIG) --libs libzip 2> /dev/null) -ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) - -LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) -SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) - -# These are the main rules - # these locations seem to work for SuSE and Fedora # prefix = $(HOME) prefix = $(DESTDIR)/usr @@ -197,204 +85,4 @@ OBJS_NEEDING_MOC = OBJS_NEEDING_UIC = HEADERS_NEEDING_MOC = -# These are the generic rules for building - -# Rules for building and creating the version file - -VERSION_FILE = version.h -# There's only one line in $(VERSION_FILE); use the shell builtin `read' -STORED_VERSION_STRING = \ - $(subst ",,$(shell [ ! -r $(VERSION_FILE) ] || \ - read ignore ignore v <$(VERSION_FILE) && echo $$v)) -#" workaround editor syntax highlighting quirk - -GET_VERSION = ./scripts/get-version -VERSION_STRING := $(shell $(GET_VERSION) linux || echo "v$(VERSION)") -# Mac Info.plist style with three numbers 1.2.3 -CFBUNDLEVERSION_STRING := $(shell $(GET_VERSION) darwin $(VERSION_STRING) || \ - echo "$(VERSION).0") -# Windows .nsi style with four numbers 1.2.3.4 -PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ - echo "$(VERSION).0.0") - -MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) - -# Add the objects for the header files which define QObject subclasses -HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(OBJS:.o=.h)) -MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) - -ALL_OBJS = $(OBJS) $(MOC_OBJS) - -DEPS = $(wildcard .dep/*.dep) - -all: $(NAME) - -$(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) - $(CXX) $(LDFLAGS) -o $(NAME) $(ALL_OBJS) $(LIBS) - -gen_version_file: -ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING)) - $(info updating $(VERSION_FILE) to $(VERSION_STRING)) - @echo \#define VERSION_STRING \"$(VERSION_STRING)\" >$(VERSION_FILE) -endif - -install: all - $(INSTALL) -d -m 755 $(BINDIR) - $(INSTALL) $(NAME) $(BINDIR) - $(INSTALL) -d -m 755 $(DESKTOPDIR) - $(INSTALL) $(DESKTOPFILE) $(DESKTOPDIR) - $(INSTALL) -d -m 755 $(ICONDIR) - $(INSTALL) -m 644 $(ICONFILE) $(ICONDIR) - @-if test -z "$(DESTDIR)"; then \ - $(gtk_update_icon_cache); \ - fi - $(INSTALL) -d -m 755 $(MANDIR) - $(INSTALL) -m 644 $(MANFILES) $(MANDIR) - @-if test ! -z "$(XSLT)"; then \ - $(INSTALL) -d -m 755 $(DATADIR)/subsurface; \ - $(INSTALL) -d -m 755 $(XSLTDIR); \ - $(INSTALL) -m 644 $(XSLTFILES) $(XSLTDIR); \ - fi - for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ - $(INSTALL) -d $(prefix)/$$LOC; \ - $(INSTALL) -m 644 $$LOC/subsurface.mo $(prefix)/$$LOC/subsurface.mo; \ - done - - -install-macosx: all - $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources - $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/MacOS - $(INSTALL) $(NAME) $(MACOSXINSTALL)/Contents/MacOS/$(NAME)-bin - $(INSTALL) $(MACOSXFILES)/$(NAME).sh $(MACOSXINSTALL)/Contents/MacOS/$(NAME) - $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXINSTALL)/Contents/ - $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXINSTALL)/Contents/ - $(INSTALL) $(ICONFILE) $(MACOSXINSTALL)/Contents/Resources/ - $(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXINSTALL)/Contents/Resources/ - for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ - $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/$$LOC; \ - $(INSTALL) $$LOC/subsurface.mo $(MACOSXINSTALL)/Contents/Resources/$$LOC/subsurface.mo; \ - done - @-if test ! -z "$(XSLT)"; then \ - $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/xslt; \ - $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXINSTALL)/Contents/Resources/xslt/; \ - fi - - -create-macosx-bundle: all - $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources - $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/MacOS - $(INSTALL) $(NAME) $(MACOSXSTAGING)/Contents/MacOS/ - $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXSTAGING)/Contents/ - $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXSTAGING)/Contents/ - $(INSTALL) $(ICONFILE) $(MACOSXSTAGING)/Contents/Resources/ - $(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXSTAGING)/Contents/Resources/ - for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ - $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/$$LOC; \ - $(INSTALL) $$LOC/subsurface.mo $(MACOSXSTAGING)/Contents/Resources/$$LOC/subsurface.mo; \ - done - @-if test ! -z "$(XSLT)"; then \ - $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/xslt; \ - $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXSTAGING)/Contents/Resources/xslt/; \ - fi - $(GTK_MAC_BUNDLER) packaging/macosx/subsurface.bundle - -sign-macosx-bundle: all - codesign -s "3A8CE62A483083EDEA5581A61E770EC1FA8BECE8" /Applications/Subsurface.app/Contents/MacOS/subsurface-bin - -install-cross-windows: all - $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/share/locale - for MSG in $(WINMSGDIRS); do\ - $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$MSG;\ - $(INSTALL) $(CROSS_PATH)/$$MSG/* $(WINDOWSSTAGING)/$$MSG;\ - done - for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \ - $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$LOC; \ - $(INSTALL) $$LOC/subsurface.mo $(WINDOWSSTAGING)/$$LOC/subsurface.mo; \ - done - -create-windows-installer: all $(NSIFILE) install-cross-windows - $(MAKENSIS) $(NSIFILE) - -$(NSIFILE): $(NSIINPUTFILE) - $(shell cat $(NSIINPUTFILE) | sed -e 's/VERSIONTOKEN/$(VERSION_STRING)/;s/PRODVTOKEN/$(PRODVERSION_STRING)/' > $(NSIFILE)) - -$(INFOPLIST): $(INFOPLISTINPUT) - $(shell cat $(INFOPLISTINPUT) | sed -e 's/CFBUNDLEVERSION_TOKEN/$(CFBUNDLEVERSION_STRING)/' > $(INFOPLIST)) - -# Transifex merge the translations -update-po-files: - xgettext -o po/subsurface-new.pot -s -k_ -kN_ -ktr --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c qt-ui/*.cpp - tx push -s - tx pull -af - -MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS)) - -%.o: %.c - @echo ' CC' $< - @mkdir -p .dep .dep/qt-ui - @$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< - -%.o: %.cpp - @echo ' CXX' $< - @mkdir -p .dep .dep/qt-ui - @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< - -# Detect which files require the moc or uic tools to be run -CPP_NEEDING_MOC = $(shell grep -l -s '^\#include \".*\.moc\"' $(OBJS:.o=.cpp)) -OBJS_NEEDING_MOC += $(CPP_NEEDING_MOC:.cpp=.o) - -CPP_NEEDING_UIC = $(shell grep -l -s '^\#include \"ui_.*\.h\"' $(OBJS:.o=.cpp)) -OBJS_NEEDING_UIC += $(CPP_NEEDING_UIC:.cpp=.o) - -# This rule is for running the moc on QObject subclasses defined in the .h -# files. -%.moc.cpp: %.h - @echo ' MOC' $< - @$(MOC) $(MOCFLAGS) $< -o $@ - -# This rule is for running the moc on QObject subclasses defined in the .cpp -# files; remember to #include ".moc" at the end of the .cpp file, or -# you'll get linker errors ("undefined vtable for...") -%.moc: %.cpp - @echo ' MOC' $< - @$(MOC) -i $(MOCFLAGS) $< -o $@ - -# This creates the ui headers. -ui_%.h: %.ui - @echo ' UIC' $< - @$(UIC) $< -o $@ - -$(OBJS_NEEDING_MOC): %.o: %.moc -$(OBJS_NEEDING_UIC): qt-ui/%.o: qt-ui/ui_%.h - -share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases - mkdir -p $(dir $@) - msgfmt -c -o $@ po/$*.po - @-if test -s po/$*.aliases; then \ - for ALIAS in `cat po/$*.aliases`; do \ - mkdir -p share/locale/$$ALIAS/LC_MESSAGES; \ - cp $@ share/locale/$$ALIAS/LC_MESSAGES; \ - done; \ - fi - -satellite.png: satellite.svg - convert -transparent white -resize 11x16 -depth 8 $< $@ - -# This should work, but it doesn't get the colors quite right - so I manually converted with Gimp -# convert -colorspace RGB -transparent white -resize 256x256 subsurface-icon.svg subsurface-icon.png -# -# The following creates the pixbuf data in .h files with the basename followed by '_pixmap' -# as name of the data structure -%.h: %.png - @echo ' gdk-pixbuf-csource' $< - @gdk-pixbuf-csource --struct --name `echo $* | sed 's/-/_/g'`_pixbuf $< > $@ - -doc: - $(MAKE) -C Documentation doc - -clean: - rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ - $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h - rm -rf share .dep - --include $(DEPS) +include Rules.mk diff --git a/Rules.mk b/Rules.mk index e882a353d..59e73e13d 100644 --- a/Rules.mk +++ b/Rules.mk @@ -1,204 +1,4 @@ -VERSION=3.0.2 - -CC=gcc -CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE -CXX=g++ -CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS -INSTALL=install - -# This are the detection rules -PKGCONFIG=pkg-config -XML2CONFIG=xml2-config -XSLCONFIG=xslt-config -QMAKE=qmake -MOC=moc -UIC=uic - -UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win|gnu|kfreebsd") - -# find libdivecomputer -# First deal with the cross compile environment and with Mac. -# For the native case, Linus doesn't want to trust pkg-config given -# how young libdivecomputer still is - so we check the typical -# subdirectories of /usr/local and /usr and then we give up. You can -# override by simply setting it here -# -ifeq ($(CC), i686-w64-mingw32-gcc) -# ok, we are cross building for Windows - LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer) - LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer) - RESFILE = packaging/windows/subsurface.res - LDFLAGS += -Wl,-subsystem,windows - LIBWINSOCK = -lwsock32 -else ifeq ($(UNAME), darwin) - LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer) - LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer) -else -libdc-local := $(wildcard /usr/local/lib/libdivecomputer.a) -libdc-local64 := $(wildcard /usr/local/lib64/libdivecomputer.a) -libdc-usr := $(wildcard /usr/lib/libdivecomputer.a) -libdc-usr64 := $(wildcard /usr/lib64/libdivecomputer.a) - -ifneq ($(LIBDCDEVEL),) - LIBDIVECOMPUTERDIR = ../libdivecomputer - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/src/.libs/libdivecomputer.a -else ifneq ($(strip $(libdc-local)),) - LIBDIVECOMPUTERDIR = /usr/local - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a -else ifneq ($(strip $(libdc-local64)),) - LIBDIVECOMPUTERDIR = /usr/local - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a -else ifneq ($(strip $(libdc-usr)),) - LIBDIVECOMPUTERDIR = /usr - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a -else ifneq ($(strip $(libdc-usr64)),) - LIBDIVECOMPUTERDIR = /usr - LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include - LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a -else -$(error Cannot find libdivecomputer - please edit Makefile) -endif -endif - -# Libusb-1.0 is only required if libdivecomputer was built with it. -# And libdivecomputer is only built with it if libusb-1.0 is -# installed. So get libusb if it exists, but don't complain -# about it if it doesn't. -LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null) - -# Use qmake to find out which Qt version we are building for. -QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) -ifeq ($(QT_VERSION_MAJOR), 5) - QT_MODULES = Qt5Widgets Qt5Svg - QT_CORE = Qt5Core -else - QT_MODULES = QtGui QtSvg - QT_CORE = QtCore -endif - -# we need GLIB2CFLAGS for gettext -QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) $(GLIB2CFLAGS) -LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) -ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) - QTCXXFLAGS += -fPIE -endif - -LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) -ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) - LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0) - GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0) -else ifeq ($(UNAME), darwin) - LIBGTK += $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation -framework CoreServices - GTKCFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration) - GTK_MAC_BUNDLER = ~/.local/bin/gtk-mac-bundler -endif - -LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES) -LIBDIVECOMPUTER = $(LIBDIVECOMPUTERARCHIVE) $(LIBUSB) - -LIBXML2 = $(shell $(XML2CONFIG) --libs) -LIBXSLT = $(shell $(XSLCONFIG) --libs) -XML2CFLAGS = $(shell $(XML2CONFIG) --cflags) -GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0) -GTKCFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-2.0) -XSLCFLAGS = $(shell $(XSLCONFIG) --cflags) -OSMGPSMAPFLAGS += $(shell $(PKGCONFIG) --cflags osmgpsmap 2> /dev/null) -LIBOSMGPSMAP += $(shell $(PKGCONFIG) --libs osmgpsmap 2> /dev/null) -LIBSOUPCFLAGS = $(shell $(PKGCONFIG) --cflags libsoup-2.4) -LIBSOUP = $(shell $(PKGCONFIG) --libs libsoup-2.4) - -LIBZIP = $(shell $(PKGCONFIG) --libs libzip 2> /dev/null) -ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) - -LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) -SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) - -# These are the main rules - -# these locations seem to work for SuSE and Fedora -# prefix = $(HOME) -prefix = $(DESTDIR)/usr -BINDIR = $(prefix)/bin -DATADIR = $(prefix)/share -DESKTOPDIR = $(DATADIR)/applications -ICONPATH = $(DATADIR)/icons/hicolor -ICONDIR = $(ICONPATH)/scalable/apps -MANDIR = $(DATADIR)/man/man1 -XSLTDIR = $(DATADIR)/subsurface/xslt -gtk_update_icon_cache = gtk-update-icon-cache -f -t $(ICONPATH) - -NAME = subsurface -ICONFILE = $(NAME)-icon.svg -DESKTOPFILE = $(NAME).desktop -MANFILES = $(NAME).1 -XSLTFILES = xslt/*.xslt - -EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ - $(LIBDIVECOMPUTERCFLAGS) \ - $(LIBSOUPCFLAGS) $(GCONF2CFLAGS) - -QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ - qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o - -GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o - -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ - planner.o planner-gtk.o \ - parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ - qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ - webservice.o sha1.o $(RESFILE) $(QTOBJS) $(GTKOBJS) - -ifneq ($(SQLITE3FLAGS),) - EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) -endif -ifneq ($(ZIPFLAGS),) - EXTRA_FLAGS += -DLIBZIP $(ZIPFLAGS) -endif -ifneq ($(strip $(LIBXSLT)),) - EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) -endif -ifneq ($(strip $(LIBOSMGPSMAP)),) - OBJS += gps.o - EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) -endif - -ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) - OBJS += linux.o -else ifeq ($(UNAME), darwin) - OBJS += macos.o - MACOSXINSTALL = /Applications/Subsurface.app - MACOSXFILES = packaging/macosx - MACOSXSTAGING = $(MACOSXFILES)/Subsurface.app - INFOPLIST = $(MACOSXFILES)/Info.plist - INFOPLISTINPUT = $(INFOPLIST).in - LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) -else - OBSJ += windows.o - WINDOWSSTAGING = ./packaging/windows - WINMSGDIRS=$(addprefix share/locale/,$(shell ls po/*.po | sed -e 's/po\/\(..\)_.*/\1\/LC_MESSAGES/')) - NSIINPUTFILE = $(WINDOWSSTAGING)/subsurface.nsi.in - NSIFILE = $(WINDOWSSTAGING)/subsurface.nsi - MAKENSIS = makensis - XSLTDIR = .\\xslt -endif - -LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ - $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) - -MSGLANGS=$(notdir $(wildcard po/*.po)) - -# Add files to the following variables if the auto-detection based on the -# filename fails -OBJS_NEEDING_MOC = -OBJS_NEEDING_UIC = -HEADERS_NEEDING_MOC = - -# These are the generic rules for building - +# -*- Makefile -*- # Rules for building and creating the version file VERSION_FILE = version.h From c11ce7e1574889734fa010ad3d8379aa41720fdc Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 09:00:15 -0700 Subject: [PATCH 051/226] Introduce a cache of the configuration You may have noticed that running make is a little slow. Every time that it is loaded, it will try to detect everything again. So, instead, save the output and reload it the next time. This is implemented by adding a rule that (re-)creates the config.cache file, which is included by make. If the file doesn't exist yet, make will first run the rule which creates it, then reload itself. You can also cause it to reconfigure by running "make configure". Signed-off-by: Thiago Macieira --- .gitignore | 1 + Configure.mk | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ Rules.mk | 3 +++ 3 files changed, 52 insertions(+) diff --git a/.gitignore b/.gitignore index 54650bcf2..b62d8982b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ Documentation/user-manual.pdf Documentation/user-manual.text packaging/windows/subsurface.nsi packaging/macos/Info.plist +config.cache diff --git a/Configure.mk b/Configure.mk index f9b9e0674..fa04a44ec 100644 --- a/Configure.mk +++ b/Configure.mk @@ -1,5 +1,6 @@ # -*- Makefile -*- # This file contains the detection rules +all: PKGCONFIG=pkg-config XML2CONFIG=xml2-config @@ -8,6 +9,11 @@ QMAKE=qmake MOC=moc UIC=uic +CONFIGFILE = config.cache +ifeq ($(CONFIGURING),1) + +# Detect the target system +# Ask the compiler what OS it's producing files for UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win|gnu|kfreebsd") # find libdivecomputer @@ -110,3 +116,45 @@ ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null)) LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null) SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) + +# Write the configure file +all: configure +configure $(CONFIGURE): Configure.mk + @echo "\ + CONFIGURED = 1 \\\ + UNAME = $(UNAME) \\\ + LIBDIVECOMPUTERDIR = $(LIBDIVECOMPUTERDIR) \\\ + LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERCFLAGS) \\\ + LIBDIVECOMPUTER = $(LIBDIVECOMPUTER) \\\ + LIBQT = $(LIBQT) \\\ + QTCXXFLAGS = $(QTCXXFLAGS) \\\ + LIBGTK = $(LIBGTK) \\\ + GTKCFLAGS = $(GTKCFLAGS) \\\ + LIBGCONF2 = $(LIBGCONF2) \\\ + GCONF2CFLAGS = $(GCONF2CFLAGS) \\\ + GTK_MAC_BUNDLER = $(GTK_MAC_BUNDLER) \\\ + LIBXML2 = $(LIBXML2) \\\ + LIBXSLT = $(LIBXSLT) \\\ + XML2CFLAGS = $(XML2CFLAGS) \\\ + GLIB2CFLAGS = $(GLIB2CFLAGS) \\\ + XSLCFLAGS = $(XSLCFLAGS) \\\ + OSMGPSMAPFLAGS = $(OSMGPSMAPFLAGS) \\\ + LIBOSMGPSMAP = $(LIBOSMGPSMAP) \\\ + LIBSOUPCFLAGS = $(LIBSOUPCFLAGS) \\\ + LIBSOUP = $(LIBSOUP) \\\ + LIBZIP = $(LIBZIP) \\\ + ZIPFLAGS = $(ZIPFLAGS) \\\ + LIBSQLITE3 = $(LIBSQLITE3) \\\ + SQLITE3FLAGS = $(SQLITE3FLAGS) \\\ + " | sed 's,\\,\n,g' > $(CONFIGFILE) + +else +configure $(CONFIGFILE): Configure.mk + @test -e $(CONFIGFILE) && echo Reconfiguring.. || echo Configuring... + @$(MAKE) CONFIGURING=1 configure + @echo Done + +-include $(CONFIGFILE) +endif + +.PHONY: configure all diff --git a/Rules.mk b/Rules.mk index 59e73e13d..5374e060b 100644 --- a/Rules.mk +++ b/Rules.mk @@ -197,4 +197,7 @@ clean: $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h rm -rf share .dep +confclean: clean + rm -f $(CONFIGFILE) + -include $(DEPS) From b1c9ed76dcae007e500da286dbfaa421d33b6d45 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 09:09:52 -0700 Subject: [PATCH 052/226] Cache the Qt tools detected (and detect RCC) Since we're caching the include flags and linker flags, we should also cache the exact helper binaries we're running. This avoids getting errors because the environment changed. Signed-off-by: Thiago Macieira --- Configure.mk | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Configure.mk b/Configure.mk index fa04a44ec..99292795a 100644 --- a/Configure.mk +++ b/Configure.mk @@ -75,9 +75,11 @@ QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) ifeq ($(QT_VERSION_MAJOR), 5) QT_MODULES = Qt5Widgets Qt5Svg QT_CORE = Qt5Core + QTBINDIR = $(shell $(QMAKE) -query QT_HOST_BINS) else QT_MODULES = QtGui QtSvg QT_CORE = QtCore + QTBINDIR = $(shell $(QMAKE) -query QT_INSTALL_BINS) endif # we need GLIB2CFLAGS for gettext @@ -86,6 +88,9 @@ LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) QTCXXFLAGS += -fPIE endif +MOC = $(QTBINDIR)/moc +UIC = $(QTBINDIR)/uic +RCC = $(QTBINDIR)/rcc LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) @@ -128,6 +133,9 @@ configure $(CONFIGURE): Configure.mk LIBDIVECOMPUTER = $(LIBDIVECOMPUTER) \\\ LIBQT = $(LIBQT) \\\ QTCXXFLAGS = $(QTCXXFLAGS) \\\ + MOC = $(MOC) \\\ + UIC = $(UIC) \\\ + RCC = $(RCC) \\\ LIBGTK = $(LIBGTK) \\\ GTKCFLAGS = $(GTKCFLAGS) \\\ LIBGCONF2 = $(LIBGCONF2) \\\ From c8b360c3b5fdeb9f878cb5db648a121a10e33772 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 09:19:03 -0700 Subject: [PATCH 053/226] Add a HEADERS variable to the Makefile Similar to the qmake variable of the same name, this lists (at least) the headers that may need moc to be run on. Adding more headers is not a problem. Signed-off-by: Thiago Macieira --- Makefile | 10 ++++++++++ Rules.mk | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3308c9f41..41e5f093b 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,16 @@ EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ $(LIBDIVECOMPUTERCFLAGS) \ $(LIBSOUPCFLAGS) $(GCONF2CFLAGS) +HEADERS = \ + qt-ui/addcylinderdialog.h \ + qt-ui/divelistview.h \ + qt-ui/maintab.h \ + qt-ui/mainwindow.h \ + qt-ui/models.h \ + qt-ui/plotareascene.h \ + qt-ui/starwidget.h \ + + QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o diff --git a/Rules.mk b/Rules.mk index 5374e060b..8ffb88b86 100644 --- a/Rules.mk +++ b/Rules.mk @@ -20,7 +20,7 @@ PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) # Add the objects for the header files which define QObject subclasses -HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(OBJS:.o=.h)) +HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(HEADERS)) MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) ALL_OBJS = $(OBJS) $(MOC_OBJS) From d773c02bf36f3b09584123bfd0e84a56304b33fb Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 09:27:55 -0700 Subject: [PATCH 054/226] Add a SOURCES variable to the Makefile, replacing OBJS Instead of listing objects, let's list sources. This matches also what qmake and most other buildsystems do. The notable exception is the kernel. The reason that listing the sources will be interesting is because I'm about to add rules to create the dependency files. Signed-off-by: Thiago Macieira --- Makefile | 55 +++++++++++++++++++++++++++++++++++++++++-------------- Rules.mk | 5 +++++ 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 41e5f093b..055d7b138 100644 --- a/Makefile +++ b/Makefile @@ -39,16 +39,43 @@ HEADERS = \ qt-ui/starwidget.h \ -QTOBJS = qt-ui/maintab.o qt-ui/mainwindow.o qt-ui/plotareascene.o qt-ui/divelistview.o \ - qt-ui/addcylinderdialog.o qt-ui/models.o qt-ui/starwidget.o - -GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o - -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ - planner.o planner-gtk.o \ - parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ - qt-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ - webservice.o sha1.o $(RESFILE) $(QTOBJS) $(GTKOBJS) +SOURCES = \ + cochran.c \ + deco.c \ + device.c \ + dive.c \ + divelist.c \ + divelist-gtk.c \ + download-dialog.c \ + equipment.c \ + file.c \ + info.c \ + info-gtk.c \ + libdivecomputer.c \ + main.c \ + parse-xml.c \ + planner.c \ + planner-gtk.c \ + prefs.c \ + print.c \ + profile.c \ + save-xml.c \ + sha1.c \ + statistics.c \ + statistics-gtk.c \ + time.c \ + uemis.c \ + uemis-downloader.c \ + webservice.c \ + qt-gui.cpp \ + qt-ui/addcylinderdialog.cpp \ + qt-ui/divelistview.cpp \ + qt-ui/maintab.cpp \ + qt-ui/mainwindow.cpp \ + qt-ui/models.cpp \ + qt-ui/plotareascene.cpp \ + qt-ui/starwidget.cpp \ + $(RESFILE) ifneq ($(SQLITE3FLAGS),) EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) @@ -60,14 +87,14 @@ ifneq ($(strip $(LIBXSLT)),) EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) endif ifneq ($(strip $(LIBOSMGPSMAP)),) - OBJS += gps.o + SOURCES += gps.c EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) endif ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) - OBJS += linux.o + SOURCES += linux.c else ifeq ($(UNAME), darwin) - OBJS += macos.o + SOURCES += macos.c MACOSXINSTALL = /Applications/Subsurface.app MACOSXFILES = packaging/macosx MACOSXSTAGING = $(MACOSXFILES)/Subsurface.app @@ -75,7 +102,7 @@ else ifeq ($(UNAME), darwin) INFOPLISTINPUT = $(INFOPLIST).in LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) else - OBSJ += windows.o + SOURCES += windows.c WINDOWSSTAGING = ./packaging/windows WINMSGDIRS=$(addprefix share/locale/,$(shell ls po/*.po | sed -e 's/po\/\(..\)_.*/\1\/LC_MESSAGES/')) NSIINPUTFILE = $(WINDOWSSTAGING)/subsurface.nsi.in diff --git a/Rules.mk b/Rules.mk index 8ffb88b86..e7108fd4b 100644 --- a/Rules.mk +++ b/Rules.mk @@ -19,6 +19,11 @@ PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) +C_SOURCES = $(filter %.c, $(SOURCES)) +CXX_SOURCES = $(filter %.cpp, $(SOURCES)) +OTHER_SOURCES = $(filter-out %.c %.cpp, $(SOURCES)) +OBJS = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(OTHER_SOURCES) + # Add the objects for the header files which define QObject subclasses HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(HEADERS)) MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) From a0b523a5afc91c3816f6c44e61f8a05d5fe5e80a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 09:49:48 -0700 Subject: [PATCH 055/226] Get the list of dependency includes from the SOURCES list Signed-off-by: Thiago Macieira --- Rules.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Rules.mk b/Rules.mk index e7108fd4b..bc135aab7 100644 --- a/Rules.mk +++ b/Rules.mk @@ -30,8 +30,6 @@ MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) ALL_OBJS = $(OBJS) $(MOC_OBJS) -DEPS = $(wildcard .dep/*.dep) - all: $(NAME) $(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) @@ -205,4 +203,5 @@ clean: confclean: clean rm -f $(CONFIGFILE) +DEPS = $(addprefix .dep/,$(C_SOURCES:.c=.o.dep) $(CXX_SOURCES:.cpp=.o.dep)) -include $(DEPS) From ce3f073d422d03d473ff18e6793a9228fb02fa12 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 11:30:05 -0700 Subject: [PATCH 056/226] Generate the C++ dependencies at configure-time We use the -MG preprocessor option to the compiler to ask it to check all #includes and tell us what's missing. Then our own rules will generate the moc and uic files that the .cpp #include. Unfortunately, our rules make uic generate output in qt-ui/ for qt-ui/*.ui, while the compiler generates rules for no directory. We need to fake it by forcing the generation. Signed-off-by: Thiago Macieira --- Rules.mk | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Rules.mk b/Rules.mk index bc135aab7..56ef8813a 100644 --- a/Rules.mk +++ b/Rules.mk @@ -35,7 +35,7 @@ all: $(NAME) $(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) $(CXX) $(LDFLAGS) -o $(NAME) $(ALL_OBJS) $(LIBS) -gen_version_file: +gen_version_file $(VERSION_FILE): ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING)) $(info updating $(VERSION_FILE) to $(VERSION_STRING)) @echo \#define VERSION_STRING \"$(VERSION_STRING)\" >$(VERSION_FILE) @@ -142,13 +142,6 @@ MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $ @mkdir -p .dep .dep/qt-ui @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< -# Detect which files require the moc or uic tools to be run -CPP_NEEDING_MOC = $(shell grep -l -s '^\#include \".*\.moc\"' $(OBJS:.o=.cpp)) -OBJS_NEEDING_MOC += $(CPP_NEEDING_MOC:.cpp=.o) - -CPP_NEEDING_UIC = $(shell grep -l -s '^\#include \"ui_.*\.h\"' $(OBJS:.o=.cpp)) -OBJS_NEEDING_UIC += $(CPP_NEEDING_UIC:.cpp=.o) - # This rule is for running the moc on QObject subclasses defined in the .h # files. %.moc.cpp: %.h @@ -167,8 +160,12 @@ ui_%.h: %.ui @echo ' UIC' $< @$(UIC) $< -o $@ -$(OBJS_NEEDING_MOC): %.o: %.moc -$(OBJS_NEEDING_UIC): qt-ui/%.o: qt-ui/ui_%.h +# This forces the creation of ui headers with the wrong path +# This is required because the -MG option to the compiler outputs +# unknown files with no path prefix +ui_%.h: qt-ui/%.ui + @echo ' UIC' $< + @$(UIC) $< -o qt-ui/$@ share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases mkdir -p $(dir $@) @@ -198,10 +195,17 @@ doc: clean: rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h - rm -rf share .dep + rm -rf share confclean: clean rm -f $(CONFIGFILE) + rm -rf .dep + +ifneq ($(CONFIGURED)$(CONFIGURING),) +.dep/%.o.dep: %.cpp + @mkdir -p .dep .dep/qt-ui + @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MM -MG -MF $@ -MT $(<:.cpp=.o) -c $< +endif DEPS = $(addprefix .dep/,$(C_SOURCES:.c=.o.dep) $(CXX_SOURCES:.cpp=.o.dep)) -include $(DEPS) From f94b6bbefd1cef992fbc71808603612a6ee2f793 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 22:20:05 -0700 Subject: [PATCH 057/226] Make the "silent mode" compilation be optional only. If you run make V=1 Then we'll output the full command-line. It's useful for debugging problems with the build. Signed-off-by: Thiago Macieira --- Rules.mk | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/Rules.mk b/Rules.mk index 56ef8813a..844e843ba 100644 --- a/Rules.mk +++ b/Rules.mk @@ -19,6 +19,14 @@ PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) +ifeq ($(V),1) + PRETTYECHO=true + COMPILE_PREFIX= +else + PRETTYECHO=echo + COMPILE_PREFIX=@ +endif + C_SOURCES = $(filter %.c, $(SOURCES)) CXX_SOURCES = $(filter %.cpp, $(SOURCES)) OTHER_SOURCES = $(filter-out %.c %.cpp, $(SOURCES)) @@ -133,39 +141,39 @@ update-po-files: MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS)) %.o: %.c - @echo ' CC' $< + @$(PRETTYECHO) ' CC' $< @mkdir -p .dep .dep/qt-ui - @$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< + $(COMPILE_PREFIX)$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< %.o: %.cpp - @echo ' CXX' $< + @$(PRETTYECHO) ' CXX' $< @mkdir -p .dep .dep/qt-ui - @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< + $(COMPILE_PREFIX)$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< # This rule is for running the moc on QObject subclasses defined in the .h # files. %.moc.cpp: %.h - @echo ' MOC' $< - @$(MOC) $(MOCFLAGS) $< -o $@ + @$(PRETTYECHO) ' MOC' $< + $(COMPILE_PREFIX)$(MOC) $(MOCFLAGS) $< -o $@ # This rule is for running the moc on QObject subclasses defined in the .cpp # files; remember to #include ".moc" at the end of the .cpp file, or # you'll get linker errors ("undefined vtable for...") %.moc: %.cpp - @echo ' MOC' $< - @$(MOC) -i $(MOCFLAGS) $< -o $@ + @$(PRETTYECHO) ' MOC' $< + $(COMPILE_PREFIX)$(MOC) -i $(MOCFLAGS) $< -o $@ # This creates the ui headers. ui_%.h: %.ui - @echo ' UIC' $< - @$(UIC) $< -o $@ + @$(PRETTYECHO) ' UIC' $< + $(COMPILE_PREFIX)$(UIC) $< -o $@ # This forces the creation of ui headers with the wrong path # This is required because the -MG option to the compiler outputs # unknown files with no path prefix ui_%.h: qt-ui/%.ui - @echo ' UIC' $< - @$(UIC) $< -o qt-ui/$@ + @$(PRETTYECHO) ' UIC' $< + $(COMPILE_PREFIX)$(UIC) $< -o qt-ui/$@ share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases mkdir -p $(dir $@) From 17ea074dc0dc438a918d4e4fa1e5197ecec8217b Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 22:23:45 -0700 Subject: [PATCH 058/226] Prettify the msgfmt and linking arguments Signed-off-by: Thiago Macieira --- Rules.mk | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Rules.mk b/Rules.mk index 844e843ba..fd26d74d0 100644 --- a/Rules.mk +++ b/Rules.mk @@ -41,7 +41,8 @@ ALL_OBJS = $(OBJS) $(MOC_OBJS) all: $(NAME) $(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) - $(CXX) $(LDFLAGS) -o $(NAME) $(ALL_OBJS) $(LIBS) + @$(PRETTYECHO) ' LINK' $(NAME) + $(COMPILE_PREFIX)$(CXX) $(LDFLAGS) -o $(NAME) $(ALL_OBJS) $(LIBS) gen_version_file $(VERSION_FILE): ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING)) @@ -176,8 +177,9 @@ ui_%.h: qt-ui/%.ui $(COMPILE_PREFIX)$(UIC) $< -o qt-ui/$@ share/locale/%.UTF-8/LC_MESSAGES/subsurface.mo: po/%.po po/%.aliases - mkdir -p $(dir $@) - msgfmt -c -o $@ po/$*.po + @$(PRETTYECHO) ' MSGFMT' $*.po + @mkdir -p $(dir $@) + $(COMPILE_PREFIX)msgfmt -c -o $@ po/$*.po @-if test -s po/$*.aliases; then \ for ALIAS in `cat po/$*.aliases`; do \ mkdir -p share/locale/$$ALIAS/LC_MESSAGES; \ From c5d244eeea0583eaef3a30e01e7efb2e8a060af8 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 23:24:47 -0700 Subject: [PATCH 059/226] Add support for Qt resources in Subsurface Signed-off-by: Thiago Macieira --- .gitignore | 1 + Makefile | 3 +++ Rules.mk | 9 ++++++++- subsurface.qrc | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 subsurface.qrc diff --git a/.gitignore b/.gitignore index b62d8982b..5a90c6d4e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ Documentation/user-manual.text packaging/windows/subsurface.nsi packaging/macos/Info.plist config.cache +*.qrc.cpp diff --git a/Makefile b/Makefile index 055d7b138..3e0af9993 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,9 @@ SOURCES = \ qt-ui/starwidget.cpp \ $(RESFILE) + +RESOURCES = subsurface.qrc + ifneq ($(SQLITE3FLAGS),) EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS) endif diff --git a/Rules.mk b/Rules.mk index fd26d74d0..ee99773ba 100644 --- a/Rules.mk +++ b/Rules.mk @@ -28,7 +28,7 @@ else endif C_SOURCES = $(filter %.c, $(SOURCES)) -CXX_SOURCES = $(filter %.cpp, $(SOURCES)) +CXX_SOURCES = $(filter %.cpp, $(SOURCES)) $(RESOURCES:.qrc=.qrc.cpp) OTHER_SOURCES = $(filter-out %.c %.cpp, $(SOURCES)) OBJS = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(OTHER_SOURCES) @@ -164,6 +164,12 @@ MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $ @$(PRETTYECHO) ' MOC' $< $(COMPILE_PREFIX)$(MOC) -i $(MOCFLAGS) $< -o $@ +# This creates the Qt resource sources. +%.qrc.cpp: %.qrc + @$(PRETTYECHO) ' RCC' $< + $(COMPILE_PREFIX)$(RCC) $< -o $@ +%.qrc: + # This creates the ui headers. ui_%.h: %.ui @$(PRETTYECHO) ' UIC' $< @@ -205,6 +211,7 @@ doc: clean: rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h + rm -f $(RESOURCES:.qrc=.qrc.cpp) rm -rf share confclean: clean diff --git a/subsurface.qrc b/subsurface.qrc new file mode 100644 index 000000000..2496c8778 --- /dev/null +++ b/subsurface.qrc @@ -0,0 +1 @@ + \ No newline at end of file From d312b7d6fd40c961c1922f670388cdd8a4b92e77 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 23:36:46 -0700 Subject: [PATCH 060/226] Don't hardcode the paths for mkdir: just get them from the target The $(@D) (equivalent to $(dir $@)) tells us what the directory the target is in. We could also have used the one for the source. They're equivalent there. Signed-off-by: Thiago Macieira --- Rules.mk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rules.mk b/Rules.mk index ee99773ba..d3c8c79a8 100644 --- a/Rules.mk +++ b/Rules.mk @@ -143,12 +143,12 @@ MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $ %.o: %.c @$(PRETTYECHO) ' CC' $< - @mkdir -p .dep .dep/qt-ui + @mkdir -p .dep/$(@D) $(COMPILE_PREFIX)$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< %.o: %.cpp @$(PRETTYECHO) ' CXX' $< - @mkdir -p .dep .dep/qt-ui + @mkdir -p .dep/$(@D) $(COMPILE_PREFIX)$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $< # This rule is for running the moc on QObject subclasses defined in the .h @@ -220,7 +220,7 @@ confclean: clean ifneq ($(CONFIGURED)$(CONFIGURING),) .dep/%.o.dep: %.cpp - @mkdir -p .dep .dep/qt-ui + @mkdir -p $(@D) @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MM -MG -MF $@ -MT $(<:.cpp=.o) -c $< endif From b073823e3acbeb4b15c95101e9708d60cc7b2703 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 14 Apr 2013 00:30:25 -0700 Subject: [PATCH 061/226] Clean up the moc intermediates too in make clean Signed-off-by: Thiago Macieira --- Makefile | 2 +- Rules.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3e0af9993..6ebba4dd5 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ VERSION=3.0.2 CC=gcc CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE CXX=g++ -CXXFLAGS=-Wall -g $(CLCFLAGS) -DQT_NO_KEYWORDS +CXXFLAGS=-Wall -g $(CLCXXFLAGS) -DQT_NO_KEYWORDS INSTALL=install # these locations seem to work for SuSE and Fedora diff --git a/Rules.mk b/Rules.mk index d3c8c79a8..d5bc31246 100644 --- a/Rules.mk +++ b/Rules.mk @@ -210,7 +210,7 @@ doc: clean: rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/subsurface-new.pot \ - $(VERSION_FILE) qt-ui/*.moc qt-ui/ui_*.h + $(VERSION_FILE) $(HEADERS_NEEDING_MOC:.h=.moc) *.moc qt-ui/*.moc qt-ui/ui_*.h rm -f $(RESOURCES:.qrc=.qrc.cpp) rm -rf share From 72ad76fc2613e4cb51f7677d760985915cd25cbf Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 13 Apr 2013 00:41:14 -0700 Subject: [PATCH 062/226] Fix compilation: in C++, enums don't have operator++ To use ++, we need to declare the variable as int. But then we need to cast to the enum type. This is using C-style casts because this is still C-like code. Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- qt-gui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qt-gui.cpp b/qt-gui.cpp index 5da140765..d68071d35 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -765,14 +765,14 @@ static void pick_default_file(GtkWidget *w, GtkButton *button) #if HAVE_OSM_GPS_MAP static GtkWidget * map_provider_widget() { - OsmGpsMapSource_t i; + int i; #if GTK_CHECK_VERSION(2,24,0) GtkWidget *combobox = gtk_combo_box_text_new(); /* several of the providers seem to be redundant or non-functional; * we may have to skip more than just the last three eventually */ for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i < OSM_GPS_MAP_SOURCE_LAST; i++) - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), osm_gps_map_source_get_friendly_name(i)); + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), osm_gps_map_source_get_friendly_name((OsmGpsMapSource_t)i)); #else GtkWidget *combobox = gtk_combo_box_new_text(); for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i < OSM_GPS_MAP_SOURCE_LAST; i++) @@ -1099,14 +1099,14 @@ static void preferences_dialog(GtkWidget *w, gpointer data) } #if HAVE_OSM_GPS_MAP /* get the map provider selected */ - OsmGpsMapSource_t i; + int i; #if GTK_CHECK_VERSION(2,24,0) char *provider = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(map_provider)); #else char *provider = gtk_combo_box_get_active_text(GTK_COMBO_BOX(map_provider)); #endif for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i <= OSM_GPS_MAP_SOURCE_YAHOO_STREET; i++) - if (!strcmp(provider,osm_gps_map_source_get_friendly_name(i))) { + if (!strcmp(provider,osm_gps_map_source_get_friendly_name((OsmGpsMapSource_t)i))) { prefs.map_provider = i; break; } From d72f4e470a42b50ad70a33fd4c389c1318b45bf1 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 23 Apr 2013 08:30:10 -0700 Subject: [PATCH 063/226] Consolidate the two .qrc files Signed-off-by: Dirk Hohndel --- resources.qrc | 5 ----- subsurface.qrc | 6 +++++- 2 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 resources.qrc diff --git a/resources.qrc b/resources.qrc deleted file mode 100644 index e257bf8f5..000000000 --- a/resources.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - star.svg - - diff --git a/subsurface.qrc b/subsurface.qrc index 2496c8778..e257bf8f5 100644 --- a/subsurface.qrc +++ b/subsurface.qrc @@ -1 +1,5 @@ - \ No newline at end of file + + + star.svg + + From 315c0b505c94e0a4cc14948ff47180576e4786ed Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 23 Apr 2013 09:53:51 -0700 Subject: [PATCH 064/226] Set the window icon in Qt And even use the resource file to allow me to use an alias for it in the actual code. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 1 + subsurface.qrc | 1 + 2 files changed, 2 insertions(+) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index bb4bda1af..adfb3c74a 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -26,6 +26,7 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), { ui->setupUi(this); ui->ListWidget->setModel(model); + this->setWindowIcon(QIcon(":subsurface-icon")); // Just to test the star widgets, can be safely removed. StarWidget *star = new StarWidget(0); star->setMaximumStars(10); diff --git a/subsurface.qrc b/subsurface.qrc index e257bf8f5..e1939f28e 100644 --- a/subsurface.qrc +++ b/subsurface.qrc @@ -1,5 +1,6 @@ star.svg + subsurface-icon.png From 6a3ccaea352abdbac464e1ceb73157f959cf3c36 Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Tue, 23 Apr 2013 21:24:01 +0200 Subject: [PATCH 065/226] Fix config.cache creation on a Mac Mac uses BSD sed by default, which doesn't support \n substition by default. Replacing sed with tr. Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- Configure.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configure.mk b/Configure.mk index 99292795a..66bb562e9 100644 --- a/Configure.mk +++ b/Configure.mk @@ -154,7 +154,7 @@ configure $(CONFIGURE): Configure.mk ZIPFLAGS = $(ZIPFLAGS) \\\ LIBSQLITE3 = $(LIBSQLITE3) \\\ SQLITE3FLAGS = $(SQLITE3FLAGS) \\\ - " | sed 's,\\,\n,g' > $(CONFIGFILE) + " | tr '\\' '\n' > $(CONFIGFILE) else configure $(CONFIGFILE): Configure.mk From 0ac67c42560c628a8a0fffe0fc4447a7ae12ef77 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Tue, 23 Apr 2013 17:32:30 -0300 Subject: [PATCH 066/226] Use the star from the resource file instead of hardcoding it's patch Signed-off-by: Tomaz Canabrava --- qt-ui/starwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt-ui/starwidget.cpp b/qt-ui/starwidget.cpp index 457946feb..866fb5834 100644 --- a/qt-ui/starwidget.cpp +++ b/qt-ui/starwidget.cpp @@ -70,7 +70,7 @@ StarWidget::StarWidget(QWidget* parent, Qt::WindowFlags f): current(0), halfStar(false) { - QSvgRenderer render(QString("star.svg")); + QSvgRenderer render(QString(":star")); QPixmap renderedStar(IMG_SIZE, IMG_SIZE); renderedStar.fill(Qt::transparent); From b495c298e31ea95bb1ec33c114f0345e45d66078 Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Tue, 23 Apr 2013 21:54:24 +0200 Subject: [PATCH 067/226] Remove trailing spaces from config.cache file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Things like «ifeq ($(UNAME), darwin)» would not trigger, since variables in the config.cache files had trailing spaces. Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- Configure.mk | 56 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Configure.mk b/Configure.mk index 66bb562e9..9c39aa4d2 100644 --- a/Configure.mk +++ b/Configure.mk @@ -126,34 +126,34 @@ SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3)) all: configure configure $(CONFIGURE): Configure.mk @echo "\ - CONFIGURED = 1 \\\ - UNAME = $(UNAME) \\\ - LIBDIVECOMPUTERDIR = $(LIBDIVECOMPUTERDIR) \\\ - LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERCFLAGS) \\\ - LIBDIVECOMPUTER = $(LIBDIVECOMPUTER) \\\ - LIBQT = $(LIBQT) \\\ - QTCXXFLAGS = $(QTCXXFLAGS) \\\ - MOC = $(MOC) \\\ - UIC = $(UIC) \\\ - RCC = $(RCC) \\\ - LIBGTK = $(LIBGTK) \\\ - GTKCFLAGS = $(GTKCFLAGS) \\\ - LIBGCONF2 = $(LIBGCONF2) \\\ - GCONF2CFLAGS = $(GCONF2CFLAGS) \\\ - GTK_MAC_BUNDLER = $(GTK_MAC_BUNDLER) \\\ - LIBXML2 = $(LIBXML2) \\\ - LIBXSLT = $(LIBXSLT) \\\ - XML2CFLAGS = $(XML2CFLAGS) \\\ - GLIB2CFLAGS = $(GLIB2CFLAGS) \\\ - XSLCFLAGS = $(XSLCFLAGS) \\\ - OSMGPSMAPFLAGS = $(OSMGPSMAPFLAGS) \\\ - LIBOSMGPSMAP = $(LIBOSMGPSMAP) \\\ - LIBSOUPCFLAGS = $(LIBSOUPCFLAGS) \\\ - LIBSOUP = $(LIBSOUP) \\\ - LIBZIP = $(LIBZIP) \\\ - ZIPFLAGS = $(ZIPFLAGS) \\\ - LIBSQLITE3 = $(LIBSQLITE3) \\\ - SQLITE3FLAGS = $(SQLITE3FLAGS) \\\ + CONFIGURED = 1\\\ + UNAME = $(UNAME)\\\ + LIBDIVECOMPUTERDIR = $(LIBDIVECOMPUTERDIR)\\\ + LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERCFLAGS)\\\ + LIBDIVECOMPUTER = $(LIBDIVECOMPUTER)\\\ + LIBQT = $(LIBQT)\\\ + QTCXXFLAGS = $(QTCXXFLAGS)\\\ + MOC = $(MOC)\\\ + UIC = $(UIC)\\\ + RCC = $(RCC)\\\ + LIBGTK = $(LIBGTK)\\\ + GTKCFLAGS = $(GTKCFLAGS)\\\ + LIBGCONF2 = $(LIBGCONF2)\\\ + GCONF2CFLAGS = $(GCONF2CFLAGS)\\\ + GTK_MAC_BUNDLER = $(GTK_MAC_BUNDLER)\\\ + LIBXML2 = $(LIBXML2)\\\ + LIBXSLT = $(LIBXSLT)\\\ + XML2CFLAGS = $(XML2CFLAGS)\\\ + GLIB2CFLAGS = $(GLIB2CFLAGS)\\\ + XSLCFLAGS = $(XSLCFLAGS)\\\ + OSMGPSMAPFLAGS = $(OSMGPSMAPFLAGS)\\\ + LIBOSMGPSMAP = $(LIBOSMGPSMAP)\\\ + LIBSOUPCFLAGS = $(LIBSOUPCFLAGS)\\\ + LIBSOUP = $(LIBSOUP)\\\ + LIBZIP = $(LIBZIP)\\\ + ZIPFLAGS = $(ZIPFLAGS)\\\ + LIBSQLITE3 = $(LIBSQLITE3)\\\ + SQLITE3FLAGS = $(SQLITE3FLAGS)\\\ " | tr '\\' '\n' > $(CONFIGFILE) else From e9625d70c130555f00a3ad3f866d9f37548e9f64 Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Tue, 23 Apr 2013 22:08:11 +0200 Subject: [PATCH 068/226] Fix missing gtk-mac-integration includes GTKCFLAGS were overwritten. Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- Configure.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configure.mk b/Configure.mk index 9c39aa4d2..de4c0b0d8 100644 --- a/Configure.mk +++ b/Configure.mk @@ -109,7 +109,7 @@ LIBXML2 = $(shell $(XML2CONFIG) --libs) LIBXSLT = $(shell $(XSLCONFIG) --libs) XML2CFLAGS = $(shell $(XML2CONFIG) --cflags) GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0) -GTKCFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-2.0) +GTKCFLAGS += $(shell $(PKGCONFIG) --cflags gtk+-2.0) XSLCFLAGS = $(shell $(XSLCONFIG) --cflags) OSMGPSMAPFLAGS += $(shell $(PKGCONFIG) --cflags osmgpsmap 2> /dev/null) LIBOSMGPSMAP += $(shell $(PKGCONFIG) --libs osmgpsmap 2> /dev/null) From 8cdb8364008487753d9e8f31f9165048f827f2d1 Mon Sep 17 00:00:00 2001 From: Miika Turkia Date: Tue, 9 Apr 2013 10:25:04 +0800 Subject: [PATCH 069/226] Switch to UTF-8 on .DLD export Let's use the shiny new UTF-8 encoding on divelogs.de export. Signed-off-by: Miika Turkia Signed-off-by: Dirk Hohndel --- xslt/divelogs-export.xslt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xslt/divelogs-export.xslt b/xslt/divelogs-export.xslt index d09f503ab..b3bed6c35 100644 --- a/xslt/divelogs-export.xslt +++ b/xslt/divelogs-export.xslt @@ -3,7 +3,7 @@ - From ee0025f69687af08347de738dcd2d2dc9ebc6365 Mon Sep 17 00:00:00 2001 From: Anton Lundin Date: Tue, 9 Apr 2013 19:25:21 +0200 Subject: [PATCH 070/226] Add a simple table-based cns calculations For dives where divecomputers haven't provided us with a cns, we calculate our cns accumulated during that dive based on a simple table. We also check if we did a dive in the prior 12 ours and grab the cns from it and calculate how much of that still affects us. [Dirk Hohndel: a couple of small changes: remove unnecessary check of cns values in the samples of the first dive computer, changed the way we determine the 'previous dive' and used the end time of that previous dive for the decay calculation] Signed-off-by: Anton Lundin Signed-off-by: Dirk Hohndel --- divelist.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/divelist.c b/divelist.c index 42fb272b0..577750399 100644 --- a/divelist.c +++ b/divelist.c @@ -273,7 +273,7 @@ static int active_o2(struct dive *dive, struct divecomputer *dc, duration_t time return o2permille; } -/* calculate OTU for a dive - this only takes the first diveomputer into account */ +/* calculate OTU for a dive - this only takes the first divecomputer into account */ static int calculate_otu(struct dive *dive) { int i; @@ -297,6 +297,74 @@ static int calculate_otu(struct dive *dive) } return otu + 0.5; } +/* calculate CNS for a dive - this only takes the first divecomputer into account */ +int const cns_table[][3] = { +/* po2, Maximum Single Exposure, Maximum 24 hour Exposure */ + {1600, 45 * 60, 150 * 60}, + {1500, 120 * 60, 180 * 60}, + {1400, 150 * 60, 180 * 60}, + {1300, 180 * 60, 210 * 60}, + {1200, 210 * 60, 240 * 60}, + {1100, 240 * 60, 270 * 60}, + {1000, 300 * 60, 300 * 60}, + { 900, 360 * 60, 360 * 60}, + { 800, 450 * 60, 450 * 60}, + { 700, 570 * 60, 570 * 60}, + { 600, 720 * 60, 720 * 60} +}; + +/* this only gets called if dive->maxcns == 0 which means we know that + * none of the divecomputers has tracked any CNS for us + * so we calculated it "by hand" */ +static int calculate_cns(struct dive *dive) +{ + int i, j, divenr; + double cns = 0.0; + struct divecomputer *dc = &dive->dc; + struct dive *prev_dive; + timestamp_t endtime; + + /* shortcut */ + if (dive->cns) + return dive->cns; + /* + * Do we start with a cns loading from a privious dive? + * Check if we did a dive 12 hours prior, and what cns we had from that. + * Then apply ha 90min halftime to see whats left. + */ + divenr = get_divenr(dive); + if (divenr) { + prev_dive = get_dive(divenr -1 ); + endtime = prev_dive->when + prev_dive->duration.seconds; + if (prev_dive && dive->when < (endtime + 3600 * 12)) { + cns = calculate_cns(prev_dive); + cns = cns * 1/pow(2, (dive->when - endtime) / (90.0 * 60.0)); + } + } + /* Caclulate the cns for each sample in this dive and sum them */ + for (i = 1; i < dc->samples; i++) { + int t; + int po2; + struct sample *sample = dc->sample + i; + struct sample *psample = sample - 1; + t = sample->time.seconds - psample->time.seconds; + if (sample->po2) { + po2 = sample->po2; + } else { + int o2 = active_o2(dive, dc, sample->time); + po2 = o2 / 1000.0 * depth_to_mbar(sample->depth.mm, dive); + } + /* Find what table-row we should calculate % for */ + for (j = 1; j < sizeof(cns_table)/(sizeof(int) * 3); j++) + if (po2 > cns_table[j][0]) + break; + j--; + cns += ((double)t)/((double)cns_table[j][1]) * 100; + } + /* save calculated cns in dive struct */ + dive->cns = cns; + return dive->cns; +} /* * Return air usage (in liters). */ @@ -456,6 +524,8 @@ void update_cylinder_related_info(struct dive *dive) if (dive != NULL) { dive->sac = calculate_sac(dive); dive->otu = calculate_otu(dive); + if (dive->maxcns == 0) + dive->maxcns = calculate_cns(dive); } } From 2d0473f6f534e69eaae784724bd7d4d347b15d0a Mon Sep 17 00:00:00 2001 From: Miika Turkia Date: Mon, 22 Apr 2013 07:00:40 +0300 Subject: [PATCH 071/226] Retain event sort order on restart The events that had same time stamp were reversed in order on every new load of the log file. This patch will keep the order static. (Changing order is annoying when using version control to store the logs.) Signed-off-by: Miika Turkia Signed-off-by: Dirk Hohndel --- dive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dive.c b/dive.c index 46121848c..34025d68c 100644 --- a/dive.c +++ b/dive.c @@ -25,7 +25,7 @@ void add_event(struct divecomputer *dc, int time, int type, int flags, int value p = &dc->events; /* insert in the sorted list of events */ - while (*p && (*p)->time.seconds < time) + while (*p && (*p)->time.seconds <= time) p = &(*p)->next; ev->next = *p; *p = ev; From 68cf4f742e42323108d89eb3daab90a41d48ae21 Mon Sep 17 00:00:00 2001 From: Miika Turkia Date: Mon, 22 Apr 2013 07:02:47 +0300 Subject: [PATCH 072/226] XSLT for importing individual DM4 dives This XSLT can be used when exporting single dives from DM4 and importing them to Subsurface. Signed-off-by: Miika Turkia Signed-off-by: Dirk Hohndel --- xslt/SuuntoDM4.xslt | 138 +++++++++++++++++++++++++++++++++++++++ xslt/commonTemplates.xsl | 6 ++ 2 files changed, 144 insertions(+) create mode 100644 xslt/SuuntoDM4.xslt diff --git a/xslt/SuuntoDM4.xslt b/xslt/SuuntoDM4.xslt new file mode 100644 index 000000000..5c61c0c52 --- /dev/null +++ b/xslt/SuuntoDM4.xslt @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xslt/commonTemplates.xsl b/xslt/commonTemplates.xsl index 87af4d686..955cf8250 100644 --- a/xslt/commonTemplates.xsl +++ b/xslt/commonTemplates.xsl @@ -104,6 +104,12 @@ + + + + + + From 17c4120143dc946133bf602efb1d26844b750aaf Mon Sep 17 00:00:00 2001 From: Miika Turkia Date: Mon, 22 Apr 2013 07:02:48 +0300 Subject: [PATCH 073/226] A hook for DM4 XML import This will take the DM4 XSLT into use. In the future it is quite likely that we will have to start using something more specific on top of the root element name to identify dive log formats. But as "Dive" is currently a unique root element name for us, this suffices for now. Signed-off-by: Miika Turkia Signed-off-by: Dirk Hohndel --- parse-xml.c | 1 + 1 file changed, 1 insertion(+) diff --git a/parse-xml.c b/parse-xml.c index 4701a173a..8cf2b0c2a 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -1891,6 +1891,7 @@ static struct xslt_files { const char *file; } xslt_files[] = { { "SUUNTO", "SuuntoSDM.xslt" }, + { "Dive", "SuuntoDM4.xslt" }, { "JDiveLog", "jdivelog2subsurface.xslt" }, { "dives", "MacDive.xslt" }, { "DIVELOGSDATA", "divelogs.xslt" }, From edab566105eba559c1a3976f18ca9be0a05db43b Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Wed, 24 Apr 2013 16:57:30 +0100 Subject: [PATCH 074/226] Amend divetrip model to use int units Amend the DiveItem class to avoid float in favour of int. Add getters which return display friendly QStrings reflecting user preferences for (e.g.) depth. Modify DiveTripModel to support controlled alignment by column; right align for depth and duration. Fix problems with utf8 encoding on rating stars, degree symbols and O2 subscript. Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/divelistview.cpp | 1 + qt-ui/models.cpp | 110 +++++++++++++++++++++++++++++++---------- 2 files changed, 85 insertions(+), 26 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index a8b1eff05..676d7c463 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -8,4 +8,5 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent) { + setUniformRowHeights(true); } diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 40307d022..64ba01349 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -8,6 +8,8 @@ #include "../dive.h" #include "../divelist.h" +#include + extern struct tank_info tank_info[100]; CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent) @@ -292,14 +294,16 @@ void TankInfoModel::update() class DiveItem { public: - explicit DiveItem(): number(0), dateTime(QString()), duration(0.0), depth(0.0), location(QString()) {parentItem = 0;} - explicit DiveItem(int num, QString dt, float, float, QString loc, DiveItem *parent = 0); + explicit DiveItem(): number(0), dateTime(QString()), seconds(0), mm(0), location(QString()) { parentItem = 0; } + explicit DiveItem(int num, QString dt, int, int, QString loc, DiveItem *parent = 0); ~DiveItem() { qDeleteAll(childlist); } int diveNumber() const { return number; } const QString& diveDateTime() const { return dateTime; } - float diveDuration() const { return duration; } - float diveDepth() const { return depth; } + int diveDuration() const { return seconds; } + int diveDepth() const { return mm; } + QString displayDuration() const; + QString displayDepth() const; const QString& diveLocation() const { return location; } DiveItem *parent() const { return parentItem; } const QList& children() const { return childlist; } @@ -312,8 +316,8 @@ public: private: int number; QString dateTime; - float duration; - float depth; + int seconds; + int mm; QString location; DiveItem *parentItem; @@ -321,13 +325,49 @@ private: }; -DiveItem::DiveItem(int num, QString dt, float dur, float dep, QString loc, DiveItem *p): - number(num), dateTime(dt), duration(dur), depth(dep), location(loc), parentItem(p) +DiveItem::DiveItem(int num, QString dt, int dur, int dep, QString loc, DiveItem *p): + number(num), dateTime(dt), seconds(dur), mm(dep), location(loc), parentItem(p) { if (parentItem) parentItem->addChild(this); } +QString DiveItem::displayDepth() const +{ + const int scale = 1000; + QString fract, str; + if (get_units()->length == units::METERS) { + fract = QString::number((unsigned)(mm % scale) / 10); + str = QString("%1.%2").arg((unsigned)(mm / scale)).arg(fract); + } + if (get_units()->length == units::FEET) { + str = QString::number(mm_to_feet(mm),'f',2); + } + return str; +} + +QString DiveItem::displayDuration() const +{ + int hrs, mins, secs, val; + const int minutes_hour = 60; + const int seconds_minute= 60; + + val = seconds; + secs = seconds % seconds_minute; + val /= seconds_minute; + mins = val % seconds_minute; + val /= minutes_hour; + hrs = val % minutes_hour; + + QString displayTime; + if (hrs > 0) + displayTime = QString("%1:%2:%3").arg(hrs).arg(mins).arg(secs); + else + displayTime = QString("%1:%2").arg(mins).arg(secs); + + return displayTime; +} + DiveTripModel::DiveTripModel(QObject *parent) : QAbstractItemModel(parent) { rootItem = new DiveItem; @@ -341,8 +381,8 @@ DiveTripModel::DiveTripModel(QObject *parent) : QAbstractItemModel(parent) buffer = get_dive_date_string(&tm); new DiveItem(d->number, buffer, - d->duration.seconds/60.0, - d->maxdepth.mm/1000.0 , + d->duration.seconds, + d->maxdepth.mm, d->location, rootItem); free(buffer); @@ -368,7 +408,17 @@ QVariant DiveTripModel::data(const QModelIndex &index, int role) const DiveItem *item = static_cast(index.internalPointer()); QVariant retVal; - if (role == Qt::DisplayRole){ + if (role == Qt::TextAlignmentRole) { + switch (index.column()) { + case DURATION: /* fall through */ + case DEPTH: + retVal = Qt::AlignRight; + break; + default: + retVal = Qt::AlignLeft; + } + } + if (role == Qt::DisplayRole) { switch (index.column()) { case NR: retVal = item->diveNumber(); @@ -377,10 +427,12 @@ QVariant DiveTripModel::data(const QModelIndex &index, int role) const retVal = item->diveDateTime(); break; case DURATION: - retVal = item->diveDuration(); + retVal = item->displayDuration(); + //retVal = item->diveDuration(); break; case DEPTH: - retVal = item->diveDepth(); + retVal = item->displayDepth(); + //retVal = item->diveDepth(); break; case LOCATION: retVal = item->diveLocation(); @@ -394,12 +446,11 @@ QVariant DiveTripModel::data(const QModelIndex &index, int role) const QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant ret; - if (orientation != Qt::Horizontal){ + if (orientation != Qt::Horizontal) return ret; - } if (role == Qt::DisplayRole) { - switch(section){ + switch(section) { case NR: ret = tr("#"); break; @@ -407,19 +458,28 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int ret = tr("Date"); break; case RATING: - ret = UTF8_BLACKSTAR; + ret = QString::fromUtf8(UTF8_BLACKSTAR); break; case DEPTH: - ret = tr("ft"); + if (get_units()->length == units::METERS) + ret = tr("Depth (m)"); + else + ret = tr("Depth (ft)"); break; case DURATION: - ret = tr("min"); + ret = tr("Duration (h:mm:ss)"); break; case TEMPERATURE: - ret = UTF8_DEGREE "F"; + if (get_units()->temperature == units::CELSIUS) + ret = QString("%1%2").arg(QString::fromUtf8(UTF8_DEGREE)).arg("C"); + else + ret = QString("%1%2").arg(QString::fromUtf8(UTF8_DEGREE)).arg("F"); break; case TOTALWEIGHT: - ret = tr("lbs"); + if (get_units()->weight == units::KG) + ret = tr("Weight (kg)"); + else + ret = tr("Weight (lbs)"); break; case SUIT: ret = tr("Suit"); @@ -428,7 +488,7 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int ret = tr("Cyl"); break; case NITROX: - ret = "O" UTF8_SUBSCRIPT_2 "%"; + ret = QString("O%1%").arg(QString::fromUtf8(UTF8_SUBSCRIPT_2)); break; case SAC: ret = tr("SAC"); @@ -450,9 +510,8 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int int DiveTripModel::rowCount(const QModelIndex &parent) const { /* only allow kids in column 0 */ - if (parent.isValid() && parent.column() > 0){ + if (parent.isValid() && parent.column() > 0) return 0; - } DiveItem *item = itemForIndex(parent); return item ? item->children().count() : 0; } @@ -474,9 +533,8 @@ QModelIndex DiveTripModel::index(int row, int column, const QModelIndex &parent) DiveItem *parentItem = itemForIndex(parent); Q_ASSERT(parentItem); - if (DiveItem *item = parentItem->children().at(row)){ + if (DiveItem *item = parentItem->children().at(row)) return createIndex(row, column, item); - } return QModelIndex(); } From 634abb8bb2dadbf3657629b8f26bc1e4cc0dd79f Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Wed, 24 Apr 2013 19:57:10 -0300 Subject: [PATCH 075/226] Use the star widget in it's right place Signed-off-by: Tomaz Canabrava --- qt-ui/maintab.ui | 16 ++++++++++++---- qt-ui/mainwindow.cpp | 5 +---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index 0f46d91a5..5122d681d 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -14,7 +14,7 @@ TabWidget - 2 + 1 @@ -356,9 +356,6 @@ - - - @@ -372,6 +369,9 @@ + + + @@ -805,6 +805,14 @@
+ + + StarWidget + QWidget +
starwidget.h
+ 1 +
+
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index adfb3c74a..e8fcd3200 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -28,10 +28,7 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), ui->ListWidget->setModel(model); this->setWindowIcon(QIcon(":subsurface-icon")); // Just to test the star widgets, can be safely removed. - StarWidget *star = new StarWidget(0); - star->setMaximumStars(10); - star->setCurrentStars(3); - star->show(); + } void MainWindow::on_actionNew_triggered() From 607c08bb25926d964f0b430440d6a1c6b6366c95 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Wed, 24 Apr 2013 19:57:50 -0300 Subject: [PATCH 076/226] Remove the use of this-> Signed-off-by: Tomaz Canabrava --- qt-ui/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index e8fcd3200..b0b56a4e7 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -26,7 +26,7 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), { ui->setupUi(this); ui->ListWidget->setModel(model); - this->setWindowIcon(QIcon(":subsurface-icon")); + setWindowIcon(QIcon(":subsurface-icon")); // Just to test the star widgets, can be safely removed. } From 639dbe95f85c72a97b00ac17e76d968c692086e3 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Wed, 24 Apr 2013 19:59:52 -0300 Subject: [PATCH 077/226] Fix the order of the DiveInformation tab bar. Signed-off-by: Tomaz Canabrava --- qt-ui/maintab.ui | 356 +++++++++++++++++++++++------------------------ 1 file changed, 178 insertions(+), 178 deletions(-) diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index 5122d681d..7edbf5837 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -14,8 +14,185 @@ TabWidget - 1 + 0 + + + Dive Notes + + + + + + Location + + + + + + + + + + Divemaster + + + + + + + Buddy + + + + + + + + + + + + + Rating + + + + + + + Suit + + + + + + + + + + Notes + + + + + + + + + + + + + + Equipment + + + + + + Qt::Vertical + + + + Cylinders + + + + + + + + + + + Edit + + + + + + + Add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete + + + + + + + + + + Weight + + + + + + + + + + + Edit + + + + + + + Add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete + + + + + + + + + + + Dive Info @@ -307,183 +484,6 @@ - - - Dive Notes - - - - - - Location - - - - - - - - - - Divemaster - - - - - - - Buddy - - - - - - - - - - - - - Rating - - - - - - - Suit - - - - - - - - - - Notes - - - - - - - - - - - - - - Equipment - - - - - - Qt::Vertical - - - - Cylinders - - - - - - - - - - - Edit - - - - - - - Add - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Delete - - - - - - - - - - Weight - - - - - - - - - - - Edit - - - - - - - Add - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Delete - - - - - - - - - - - Stats From e156b00f05c99b990a5e482c23e172e168d80b74 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 24 Apr 2013 16:02:41 -0700 Subject: [PATCH 078/226] Fix column headings for divelist This looks better and is consistent with the Gtk version. Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 64ba01349..31610facd 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -462,12 +462,12 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int break; case DEPTH: if (get_units()->length == units::METERS) - ret = tr("Depth (m)"); + ret = tr("m"); else - ret = tr("Depth (ft)"); + ret = tr("ft"); break; case DURATION: - ret = tr("Duration (h:mm:ss)"); + ret = tr("min"); break; case TEMPERATURE: if (get_units()->temperature == units::CELSIUS) @@ -477,9 +477,9 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int break; case TOTALWEIGHT: if (get_units()->weight == units::KG) - ret = tr("Weight (kg)"); + ret = tr("kg"); else - ret = tr("Weight (lbs)"); + ret = tr("lbs"); break; case SUIT: ret = tr("Suit"); From ce235bd58133fd0ebde4d7156dd5cb4c62abeb60 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 24 Apr 2013 16:03:14 -0700 Subject: [PATCH 079/226] Correctly format depth and time We really need those leading 0s. Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 31610facd..e7f4b1f8b 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -338,7 +338,7 @@ QString DiveItem::displayDepth() const QString fract, str; if (get_units()->length == units::METERS) { fract = QString::number((unsigned)(mm % scale) / 10); - str = QString("%1.%2").arg((unsigned)(mm / scale)).arg(fract); + str = QString("%1.%2").arg((unsigned)(mm / scale)).arg(fract, 2, QChar('0')); } if (get_units()->length == units::FEET) { str = QString::number(mm_to_feet(mm),'f',2); @@ -348,23 +348,19 @@ QString DiveItem::displayDepth() const QString DiveItem::displayDuration() const { - int hrs, mins, secs, val; - const int minutes_hour = 60; - const int seconds_minute= 60; + int hrs, mins, secs; - val = seconds; - secs = seconds % seconds_minute; - val /= seconds_minute; - mins = val % seconds_minute; - val /= minutes_hour; - hrs = val % minutes_hour; + secs = seconds % 60; + mins = seconds / 60; + hrs = mins / 60; + mins -= hrs * 60; QString displayTime; - if (hrs > 0) - displayTime = QString("%1:%2:%3").arg(hrs).arg(mins).arg(secs); + if (hrs) + displayTime = QString("%1:%2:").arg(hrs).arg(mins, 2, 10, QChar('0')); else - displayTime = QString("%1:%2").arg(mins).arg(secs); - + displayTime = QString("%1:").arg(mins); + displayTime += QString("%1").arg(secs, 2, 10, QChar('0')); return displayTime; } From 92a5a5c35b39543a2feaeea247eb7390cd3a9e94 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 24 Apr 2013 16:33:29 -0700 Subject: [PATCH 080/226] Split report_dives into UI and logic and move to divelist files Functionality is unchanged, except we now have a nice process_dives function that deals with all the logic and that gets called from report_dives from the Gtk code. Signed-off-by: Dirk Hohndel --- dive.h | 1 - divelist-gtk.c | 7 ++- divelist.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++ divelist.h | 5 ++ main.c | 127 ------------------------------------------------- 5 files changed, 132 insertions(+), 129 deletions(-) diff --git a/dive.h b/dive.h index 0209197fb..f2153587f 100644 --- a/dive.h +++ b/dive.h @@ -589,7 +589,6 @@ extern struct sample *prepare_sample(struct divecomputer *dc); extern void finish_sample(struct divecomputer *dc); extern void sort_table(struct dive_table *table); -extern void report_dives(gboolean imported, gboolean prefer_imported); extern struct dive *fixup_dive(struct dive *dive); extern unsigned int dc_airtemp(struct divecomputer *dc); extern struct dive *merge_dives(struct dive *a, struct dive *b, int offset, gboolean prefer_downloaded); diff --git a/divelist-gtk.c b/divelist-gtk.c index 189b75ba1..e1482ee06 100644 --- a/divelist-gtk.c +++ b/divelist-gtk.c @@ -801,7 +801,6 @@ static gint gtk_dive_nr_sort(GtkTreeModel *model, return dive_nr_sort(idx_a, idx_b, when_a, when_b); } - static struct divelist_column { const char *header; data_func_t data; @@ -895,6 +894,12 @@ static void row_activated_cb(GtkTreeView *tree_view, edit_dive_info(get_dive(index), FALSE); } +void report_dives(bool is_imported, bool prefer_imported) +{ + process_dives(is_imported, prefer_imported); + dive_list_update_dives(); +} + void add_dive_cb(GtkWidget *menuitem, gpointer data) { struct dive *dive; diff --git a/divelist.c b/divelist.c index 577750399..bd1e90224 100644 --- a/divelist.c +++ b/divelist.c @@ -935,3 +935,124 @@ void remove_autogen_trips() } } +/* + * When adding dives to the dive table, we try to renumber + * the new dives based on any old dives in the dive table. + * + * But we only do it if: + * + * - there are no dives in the dive table + * + * OR + * + * - the last dive in the old dive table was numbered + * + * - all the new dives are strictly at the end (so the + * "last dive" is at the same location in the dive table + * after re-sorting the dives. + * + * - none of the new dives have any numbers + * + * This catches the common case of importing new dives from + * a dive computer, and gives them proper numbers based on + * your old dive list. But it tries to be very conservative + * and not give numbers if there is *any* question about + * what the numbers should be - in which case you need to do + * a manual re-numbering. + */ +static void try_to_renumber(struct dive *last, int preexisting) +{ + int i, nr; + + /* + * If the new dives aren't all strictly at the end, + * we're going to expect the user to do a manual + * renumbering. + */ + if (preexisting && get_dive(preexisting-1) != last) + return; + + /* + * If any of the new dives already had a number, + * we'll have to do a manual renumbering. + */ + for (i = preexisting; i < dive_table.nr; i++) { + struct dive *dive = get_dive(i); + if (dive->number) + return; + } + + /* + * Ok, renumber.. + */ + if (last) + nr = last->number; + else + nr = 0; + for (i = preexisting; i < dive_table.nr; i++) { + struct dive *dive = get_dive(i); + dive->number = ++nr; + } +} + +void process_dives(bool is_imported, bool prefer_imported) +{ + int i; + int preexisting = dive_table.preexisting; + struct dive *last; + + /* check if we need a nickname for the divecomputer for newly downloaded dives; + * since we know they all came from the same divecomputer we just check for the + * first one */ + if (preexisting < dive_table.nr && dive_table.dives[preexisting]->downloaded) + set_dc_nickname(dive_table.dives[preexisting]); + else + /* they aren't downloaded, so record / check all new ones */ + for (i = preexisting; i < dive_table.nr; i++) + set_dc_nickname(dive_table.dives[i]); + + /* This does the right thing for -1: NULL */ + last = get_dive(preexisting-1); + + sort_table(&dive_table); + + for (i = 1; i < dive_table.nr; i++) { + struct dive **pp = &dive_table.dives[i-1]; + struct dive *prev = pp[0]; + struct dive *dive = pp[1]; + struct dive *merged; + + /* only try to merge overlapping dives - or if one of the dives has + * zero duration (that might be a gps marker from the webservice) */ + if (prev->duration.seconds && dive->duration.seconds && + prev->when + prev->duration.seconds < dive->when) + continue; + + merged = try_to_merge(prev, dive, prefer_imported); + if (!merged) + continue; + + /* careful - we might free the dive that last points to. Oops... */ + if (last == prev || last == dive) + last = merged; + + /* Redo the new 'i'th dive */ + i--; + add_single_dive(i, merged); + delete_single_dive(i+1); + delete_single_dive(i+1); + } + /* make sure no dives are still marked as downloaded */ + for (i = 1; i < dive_table.nr; i++) + dive_table.dives[i]->downloaded = FALSE; + + if (is_imported) { + /* If there are dives in the table, are they numbered */ + if (!last || last->number) + try_to_renumber(last, preexisting); + + /* did we add dives to the dive table? */ + if (preexisting != dive_table.nr) + mark_divelist_changed(TRUE); + } +} diff --git a/divelist.h b/divelist.h index 014c4e0ee..e3ff1be09 100644 --- a/divelist.h +++ b/divelist.h @@ -4,8 +4,12 @@ #ifdef __cplusplus extern "C" { #endif + +#include + struct dive; +extern void report_dives(bool imported, bool prefer_imported); extern void dive_list_update_dives(void); extern void update_dive_list_col_visibility(void); extern void update_dive_list_units(void); @@ -21,6 +25,7 @@ extern double init_decompression(struct dive * dive); extern void export_all_dives_uddf_cb(); /* divelist core logic functions */ +extern void process_dives(bool imported, bool prefer_imported); extern char *get_dive_date_string(struct tm *tm); extern char *get_trip_date_string(struct tm *tm, int nr); extern void clear_trip_indexes(void); diff --git a/main.c b/main.c index bf3bc0851..40492a7e1 100644 --- a/main.c +++ b/main.c @@ -80,138 +80,11 @@ const char *monthname(int mon) return _(month_array[mon]); } -/* - * When adding dives to the dive table, we try to renumber - * the new dives based on any old dives in the dive table. - * - * But we only do it if: - * - * - there are no dives in the dive table - * - * OR - * - * - the last dive in the old dive table was numbered - * - * - all the new dives are strictly at the end (so the - * "last dive" is at the same location in the dive table - * after re-sorting the dives. - * - * - none of the new dives have any numbers - * - * This catches the common case of importing new dives from - * a dive computer, and gives them proper numbers based on - * your old dive list. But it tries to be very conservative - * and not give numbers if there is *any* question about - * what the numbers should be - in which case you need to do - * a manual re-numbering. - */ -static void try_to_renumber(struct dive *last, int preexisting) -{ - int i, nr; - - /* - * If the new dives aren't all strictly at the end, - * we're going to expect the user to do a manual - * renumbering. - */ - if (preexisting && get_dive(preexisting-1) != last) - return; - - /* - * If any of the new dives already had a number, - * we'll have to do a manual renumbering. - */ - for (i = preexisting; i < dive_table.nr; i++) { - struct dive *dive = get_dive(i); - if (dive->number) - return; - } - - /* - * Ok, renumber.. - */ - if (last) - nr = last->number; - else - nr = 0; - for (i = preexisting; i < dive_table.nr; i++) { - struct dive *dive = get_dive(i); - dive->number = ++nr; - } -} - /* * track whether we switched to importing dives */ static gboolean imported = FALSE; -/* - * This doesn't really report anything at all. We just sort the - * dives, the GUI does the reporting - */ -void report_dives(gboolean is_imported, gboolean prefer_imported) -{ - int i; - int preexisting = dive_table.preexisting; - struct dive *last; - - /* check if we need a nickname for the divecomputer for newly downloaded dives; - * since we know they all came from the same divecomputer we just check for the - * first one */ - if (preexisting < dive_table.nr && dive_table.dives[preexisting]->downloaded) - set_dc_nickname(dive_table.dives[preexisting]); - else - /* they aren't downloaded, so record / check all new ones */ - for (i = preexisting; i < dive_table.nr; i++) - set_dc_nickname(dive_table.dives[i]); - - /* This does the right thing for -1: NULL */ - last = get_dive(preexisting-1); - - sort_table(&dive_table); - - for (i = 1; i < dive_table.nr; i++) { - struct dive **pp = &dive_table.dives[i-1]; - struct dive *prev = pp[0]; - struct dive *dive = pp[1]; - struct dive *merged; - - /* only try to merge overlapping dives - or if one of the dives has - * zero duration (that might be a gps marker from the webservice) */ - if (prev->duration.seconds && dive->duration.seconds && - prev->when + prev->duration.seconds < dive->when) - continue; - - merged = try_to_merge(prev, dive, prefer_imported); - if (!merged) - continue; - - /* careful - we might free the dive that last points to. Oops... */ - if (last == prev || last == dive) - last = merged; - - /* Redo the new 'i'th dive */ - i--; - add_single_dive(i, merged); - delete_single_dive(i+1); - delete_single_dive(i+1); - } - /* make sure no dives are still marked as downloaded */ - for (i = 1; i < dive_table.nr; i++) - dive_table.dives[i]->downloaded = FALSE; - - if (is_imported) { - /* If there are dives in the table, are they numbered */ - if (!last || last->number) - try_to_renumber(last, preexisting); - - /* did we add dives to the dive table? */ - if (preexisting != dive_table.nr) - mark_divelist_changed(TRUE); - } - dive_list_update_dives(); -} - static void parse_argument(const char *arg) { const char *p = arg+1; From 8efd63b233df833d191b35adcc5ce58598367dbc Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 24 Apr 2013 16:52:18 -0700 Subject: [PATCH 081/226] MSVC is c89 In case someone wants to compile this with an outdated C compiler... Signed-off-by: Dirk Hohndel --- divelist.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/divelist.h b/divelist.h index e3ff1be09..47680f9b2 100644 --- a/divelist.h +++ b/divelist.h @@ -3,9 +3,13 @@ #ifdef __cplusplus extern "C" { -#endif - +#else +#if __STDC_VERSION__ >= 199901L #include +#else +typedef int bool; +#endif +#endif struct dive; From 1e0bc8a1ab5d124445786266ad916744b1368986 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 24 Apr 2013 17:02:49 -0700 Subject: [PATCH 082/226] Fix building for Windows: cache the Windows-specific variables Otherwise, we won't link to winsock or set the subsystem correctly. Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- Configure.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Configure.mk b/Configure.mk index de4c0b0d8..96508c65c 100644 --- a/Configure.mk +++ b/Configure.mk @@ -131,6 +131,9 @@ configure $(CONFIGURE): Configure.mk LIBDIVECOMPUTERDIR = $(LIBDIVECOMPUTERDIR)\\\ LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERCFLAGS)\\\ LIBDIVECOMPUTER = $(LIBDIVECOMPUTER)\\\ + LIBWINSOCK = $(LIBWINSOCK)\\\ + LDFLAGS = $(LDFLAGS)\\\ + RESFILE = $(RESFILE)\\\ LIBQT = $(LIBQT)\\\ QTCXXFLAGS = $(QTCXXFLAGS)\\\ MOC = $(MOC)\\\ From 4587f8a2744ed8286aa8912de75516deaf217f4e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 24 Apr 2013 21:05:57 -0700 Subject: [PATCH 083/226] Call the get_date functions with timestamp_t instead of struct tm This is the much more natural way to use this function, now that I look at it... Signed-off-by: Dirk Hohndel --- divelist-gtk.c | 6 ++---- divelist.c | 28 +++++++++++++++++----------- divelist.h | 4 ++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/divelist-gtk.c b/divelist-gtk.c index e1482ee06..a5d70f348 100644 --- a/divelist-gtk.c +++ b/divelist-gtk.c @@ -258,7 +258,6 @@ static void date_data_func(GtkTreeViewColumn *col, gpointer data) { int idx, nr; - struct tm tm; timestamp_t when; /* this should be enought for most languages. if not increase the value. */ char *buffer; @@ -266,11 +265,10 @@ static void date_data_func(GtkTreeViewColumn *col, gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1); nr = gtk_tree_model_iter_n_children(model, iter); - utc_mkdate(when, &tm); if (idx < 0) { - buffer = get_trip_date_string(&tm, nr); + buffer = get_trip_date_string(when, nr); } else { - buffer = get_dive_date_string(&tm); + buffer = get_dive_date_string(when); } g_object_set(renderer, "text", buffer, NULL); free(buffer); diff --git a/divelist.c b/divelist.c index bd1e90224..e34f7afeb 100644 --- a/divelist.c +++ b/divelist.c @@ -561,31 +561,37 @@ void get_suit(struct dive *dive, char **str) #define MAX_DATE_STRING 256 /* caller needs to free the string */ -char *get_dive_date_string(struct tm *tm) { +char *get_dive_date_string(timestamp_t when) { char *buffer = malloc(MAX_DATE_STRING); - if (buffer) + if (buffer) { + struct tm tm; + utc_mkdate(when, &tm); snprintf(buffer, MAX_DATE_STRING, /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, hour:min */ _("%1$s, %2$s %3$d, %4$d %5$02d:%6$02d"), - weekday(tm->tm_wday), - monthname(tm->tm_mon), - tm->tm_mday, tm->tm_year + 1900, - tm->tm_hour, tm->tm_min); + weekday(tm.tm_wday), + monthname(tm.tm_mon), + tm.tm_mday, tm.tm_year + 1900, + tm.tm_hour, tm.tm_min); + } return buffer; } /* caller needs to free the string */ -char *get_trip_date_string(struct tm *tm, int nr) { +char *get_trip_date_string(timestamp_t when, int nr) { char *buffer = malloc(MAX_DATE_STRING); - if (buffer) + if (buffer) { + struct tm tm; + utc_mkdate(when, &tm); snprintf(buffer, MAX_DATE_STRING, /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, nr dives */ ngettext("Trip %1$s, %2$s %3$d, %4$d (%5$d dive)", "Trip %1$s, %2$s %3$d, %4$d (%5$d dives)", nr), - weekday(tm->tm_wday), - monthname(tm->tm_mon), - tm->tm_mday, tm->tm_year + 1900, + weekday(tm.tm_wday), + monthname(tm.tm_mon), + tm.tm_mday, tm.tm_year + 1900, nr); + } return buffer; } diff --git a/divelist.h b/divelist.h index 47680f9b2..f3054d213 100644 --- a/divelist.h +++ b/divelist.h @@ -30,8 +30,8 @@ extern void export_all_dives_uddf_cb(); /* divelist core logic functions */ extern void process_dives(bool imported, bool prefer_imported); -extern char *get_dive_date_string(struct tm *tm); -extern char *get_trip_date_string(struct tm *tm, int nr); +extern char *get_dive_date_string(timestamp_t when); +extern char *get_trip_date_string(timestamp_t when, int nr); extern void clear_trip_indexes(void); extern dive_trip_t *find_trip_by_idx(int idx); extern int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b); From fde0f49df899a3c591e8def6dda93a4d83bbc962 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 24 Apr 2013 23:21:57 -0700 Subject: [PATCH 084/226] Use Subsurface's data structures for DiveItem We have these data structures for a reason. They provide context about the units used and prevent mistakes. And of course they are used everywhere else so we should use them here, too. This also tries to display some more data and make things look a bit more like the Gtk version when it comes to alignment and formatting. My guess is this will make Qt developers' eyes bleed. My apologies. Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 153 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 117 insertions(+), 36 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index e7f4b1f8b..cf6490051 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -294,17 +294,28 @@ void TankInfoModel::update() class DiveItem { public: - explicit DiveItem(): number(0), dateTime(QString()), seconds(0), mm(0), location(QString()) { parentItem = 0; } - explicit DiveItem(int num, QString dt, int, int, QString loc, DiveItem *parent = 0); + explicit DiveItem(): number(0), when(), duration(), maxdepth(), rating(0), + temperature(), totalweight(), suit(QString()), sac(0), + otu(0), maxcns(0), location(QString()) { parentItem = 0; } + explicit DiveItem(int, timestamp_t, duration_t, depth_t, int, temperature_t, + weight_t, QString, int, int, int, QString, DiveItem *parent = 0); ~DiveItem() { qDeleteAll(childlist); } int diveNumber() const { return number; } - const QString& diveDateTime() const { return dateTime; } - int diveDuration() const { return seconds; } - int diveDepth() const { return mm; } + const QString diveDateTime() const { return QString::fromUtf8(get_dive_date_string(when)); } + int diveDuration() const { return duration.seconds; } + int diveDepth() const { return maxdepth.mm; } + int diveSac() const { return sac; } + int diveOtu() const { return otu; } + int diveMaxcns() const { return maxcns; } + int diveWeight() const { return totalweight.grams; } QString displayDuration() const; QString displayDepth() const; + QString displayTemperature() const; + QString displayWeight() const; + QString displaySac() const; const QString& diveLocation() const { return location; } + const QString& diveSuit() const { return suit; } DiveItem *parent() const { return parentItem; } const QList& children() const { return childlist; } @@ -315,19 +326,30 @@ public: private: int number; - QString dateTime; - int seconds; - int mm; + timestamp_t when; + duration_t duration; + depth_t maxdepth; + int rating; + temperature_t temperature; + weight_t totalweight; + QString suit; + int sac; + int otu; + int maxcns; QString location; - DiveItem *parentItem; QList childlist; - }; -DiveItem::DiveItem(int num, QString dt, int dur, int dep, QString loc, DiveItem *p): - number(num), dateTime(dt), seconds(dur), mm(dep), location(loc), parentItem(p) +DiveItem::DiveItem(int num, timestamp_t when, duration_t duration, depth_t maxdepth, int rating, temperature_t temp, + weight_t weight, QString su, int sac, int otu, int maxcns, QString loc, DiveItem *p): + number(num), rating(rating), suit(su), sac(sac), otu(otu), maxcns(maxcns), location(loc), parentItem(p) { + this->when = when; + this->duration = duration; + this->maxdepth = maxdepth; + this->temperature = temp; + this->totalweight = weight; if (parentItem) parentItem->addChild(this); } @@ -337,11 +359,11 @@ QString DiveItem::displayDepth() const const int scale = 1000; QString fract, str; if (get_units()->length == units::METERS) { - fract = QString::number((unsigned)(mm % scale) / 10); - str = QString("%1.%2").arg((unsigned)(mm / scale)).arg(fract, 2, QChar('0')); + fract = QString::number((unsigned)(maxdepth.mm % scale) / 10); + str = QString("%1.%2").arg((unsigned)(maxdepth.mm / scale)).arg(fract, 2, QChar('0')); } if (get_units()->length == units::FEET) { - str = QString::number(mm_to_feet(mm),'f',2); + str = QString::number(mm_to_feet(maxdepth.mm),'f',0); } return str; } @@ -350,8 +372,8 @@ QString DiveItem::displayDuration() const { int hrs, mins, secs; - secs = seconds % 60; - mins = seconds / 60; + secs = duration.seconds % 60; + mins = duration.seconds / 60; hrs = mins / 60; mins -= hrs * 60; @@ -364,6 +386,44 @@ QString DiveItem::displayDuration() const return displayTime; } +QString DiveItem::displayTemperature() const +{ + QString str; + + if (get_units()->temperature == units::CELSIUS) { + str = QString::number(mkelvin_to_C(temperature.mkelvin), 'f', 1); + } else { + str = QString::number(mkelvin_to_F(temperature.mkelvin), 'f', 1); + } + return str; +} + +QString DiveItem::displaySac() const +{ + QString str; + + if (get_units()->volume == units::LITER) { + str = QString::number(sac / 1000, 'f', 1); + } else { + str = QString::number(ml_to_cuft(sac), 'f', 2); + } + return str; +} + +QString DiveItem::displayWeight() const +{ + QString str; + + if (get_units()->weight == units::KG) { + int gr = totalweight.grams % 1000; + int kg = totalweight.grams / 1000; + str = QString("%1.%2").arg(kg).arg((unsigned)(gr + 500) / 100); + } else { + str = QString("%1").arg((unsigned)(grams_to_lbs(totalweight.grams) + 0.5)); + } + return str; +} + DiveTripModel::DiveTripModel(QObject *parent) : QAbstractItemModel(parent) { rootItem = new DiveItem; @@ -371,17 +431,20 @@ DiveTripModel::DiveTripModel(QObject *parent) : QAbstractItemModel(parent) struct dive *d; for_each_dive(i, d) { - struct tm tm; - char *buffer; - utc_mkdate(d->when, &tm); - buffer = get_dive_date_string(&tm); + weight_t tw = {.grams = total_weight(d)}; new DiveItem(d->number, - buffer, - d->duration.seconds, - d->maxdepth.mm, - d->location, - rootItem); - free(buffer); + d->when, + d->duration, + d->maxdepth, + d->rating, + d->watertemp, + tw, + d->suit, + d->sac, + d->otu, + d->maxcns, + d->location, + rootItem); } } @@ -406,12 +469,14 @@ QVariant DiveTripModel::data(const QModelIndex &index, int role) const QVariant retVal; if (role == Qt::TextAlignmentRole) { switch (index.column()) { - case DURATION: /* fall through */ - case DEPTH: - retVal = Qt::AlignRight; + case DATE: /* fall through */ + case SUIT: /* fall through */ + case LOCATION: + retVal = Qt::AlignLeft; break; default: - retVal = Qt::AlignLeft; + retVal = Qt::AlignRight; + break; } } if (role == Qt::DisplayRole) { @@ -422,13 +487,29 @@ QVariant DiveTripModel::data(const QModelIndex &index, int role) const case DATE: retVal = item->diveDateTime(); break; - case DURATION: - retVal = item->displayDuration(); - //retVal = item->diveDuration(); - break; case DEPTH: retVal = item->displayDepth(); - //retVal = item->diveDepth(); + break; + case DURATION: + retVal = item->displayDuration(); + break; + case TEMPERATURE: + retVal = item->displayTemperature(); + break; + case TOTALWEIGHT: + retVal = item->displayWeight(); + break; + case SUIT: + retVal = item->diveSuit(); + break; + case SAC: + retVal = item->displaySac(); + break; + case OTU: + retVal = item->diveOtu(); + break; + case MAXCNS: + retVal = item->diveMaxcns(); break; case LOCATION: retVal = item->diveLocation(); From 5d4d40df910b7be15c58f46768b8fe54b0b429f0 Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Thu, 25 Apr 2013 09:50:01 +0200 Subject: [PATCH 085/226] Simplify DiveItem The DiveItem constructor had 13 variables. By passing it the full dive we reduce that to 2. [Dirk Hohndel: changed to use "struct dive *" instead of just "dive *"] Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index cf6490051..a725d0d61 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -63,7 +63,7 @@ QVariant CylindersModel::data(const QModelIndex& index, int role) const return ret; } - dive *d = get_dive(selected_dive); + struct dive *d = get_dive(selected_dive); cylinder_t& cyl = d->cylinder[index.row()]; if (role == Qt::DisplayRole) { @@ -297,8 +297,9 @@ public: explicit DiveItem(): number(0), when(), duration(), maxdepth(), rating(0), temperature(), totalweight(), suit(QString()), sac(0), otu(0), maxcns(0), location(QString()) { parentItem = 0; } - explicit DiveItem(int, timestamp_t, duration_t, depth_t, int, temperature_t, - weight_t, QString, int, int, int, QString, DiveItem *parent = 0); + + explicit DiveItem(struct dive *d, DiveItem *parent = 0); + ~DiveItem() { qDeleteAll(childlist); } int diveNumber() const { return number; } @@ -341,15 +342,24 @@ private: QList childlist; }; -DiveItem::DiveItem(int num, timestamp_t when, duration_t duration, depth_t maxdepth, int rating, temperature_t temp, - weight_t weight, QString su, int sac, int otu, int maxcns, QString loc, DiveItem *p): - number(num), rating(rating), suit(su), sac(sac), otu(otu), maxcns(maxcns), location(loc), parentItem(p) +DiveItem::DiveItem(struct dive *d, DiveItem *p): + number(d->number), + rating(d->rating), + suit(d->suit), + sac(d->sac), + otu(d->otu), + maxcns(d->maxcns), + location(d->location), + parentItem(p) { - this->when = when; - this->duration = duration; - this->maxdepth = maxdepth; - this->temperature = temp; - this->totalweight = weight; + this->when = d->when; + this->duration = d->duration; + this->maxdepth = d->maxdepth; + this->temperature = d->watertemp; + + weight_t tw = { total_weight(d) }; + this->totalweight = tw; + if (parentItem) parentItem->addChild(this); } @@ -431,20 +441,7 @@ DiveTripModel::DiveTripModel(QObject *parent) : QAbstractItemModel(parent) struct dive *d; for_each_dive(i, d) { - weight_t tw = {.grams = total_weight(d)}; - new DiveItem(d->number, - d->when, - d->duration, - d->maxdepth, - d->rating, - d->watertemp, - tw, - d->suit, - d->sac, - d->otu, - d->maxcns, - d->location, - rootItem); + new DiveItem(d, rootItem); } } From 8a884d2cf7d7680bc2c94000954684e3b95d0c12 Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Thu, 25 Apr 2013 16:04:41 +0200 Subject: [PATCH 086/226] Remove useless members of DiveItem Just use the dive struct directly. Suggested-by: Dirk Hohndel Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 83 ++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index a725d0d61..7894aa279 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -294,29 +294,33 @@ void TankInfoModel::update() class DiveItem { public: - explicit DiveItem(): number(0), when(), duration(), maxdepth(), rating(0), - temperature(), totalweight(), suit(QString()), sac(0), - otu(0), maxcns(0), location(QString()) { parentItem = 0; } + explicit DiveItem(): dive() { parentItem = 0; } explicit DiveItem(struct dive *d, DiveItem *parent = 0); ~DiveItem() { qDeleteAll(childlist); } - int diveNumber() const { return number; } - const QString diveDateTime() const { return QString::fromUtf8(get_dive_date_string(when)); } - int diveDuration() const { return duration.seconds; } - int diveDepth() const { return maxdepth.mm; } - int diveSac() const { return sac; } - int diveOtu() const { return otu; } - int diveMaxcns() const { return maxcns; } - int diveWeight() const { return totalweight.grams; } + int diveNumber() const { return dive->number; } + const QString diveDateTime() const { return QString::fromUtf8(get_dive_date_string(dive->when)); } + int diveDuration() const { return dive->duration.seconds; } + int diveDepth() const { return dive->maxdepth.mm; } + int diveSac() const { return dive->sac; } + int diveOtu() const { return dive->otu; } + int diveMaxcns() const { return dive->maxcns; } + + int diveWeight() const + { + weight_t tw = { total_weight(dive) }; + return tw.grams; + } + QString displayDuration() const; QString displayDepth() const; QString displayTemperature() const; QString displayWeight() const; QString displaySac() const; - const QString& diveLocation() const { return location; } - const QString& diveSuit() const { return suit; } + const QString diveLocation() const { return dive->location; } + const QString diveSuit() const { return dive->suit; } DiveItem *parent() const { return parentItem; } const QList& children() const { return childlist; } @@ -326,40 +330,15 @@ public: } /* parent = self */ private: - int number; - timestamp_t when; - duration_t duration; - depth_t maxdepth; - int rating; - temperature_t temperature; - weight_t totalweight; - QString suit; - int sac; - int otu; - int maxcns; - QString location; + struct dive *dive; DiveItem *parentItem; QList childlist; }; DiveItem::DiveItem(struct dive *d, DiveItem *p): - number(d->number), - rating(d->rating), - suit(d->suit), - sac(d->sac), - otu(d->otu), - maxcns(d->maxcns), - location(d->location), + dive(d), parentItem(p) { - this->when = d->when; - this->duration = d->duration; - this->maxdepth = d->maxdepth; - this->temperature = d->watertemp; - - weight_t tw = { total_weight(d) }; - this->totalweight = tw; - if (parentItem) parentItem->addChild(this); } @@ -369,11 +348,11 @@ QString DiveItem::displayDepth() const const int scale = 1000; QString fract, str; if (get_units()->length == units::METERS) { - fract = QString::number((unsigned)(maxdepth.mm % scale) / 10); - str = QString("%1.%2").arg((unsigned)(maxdepth.mm / scale)).arg(fract, 2, QChar('0')); + fract = QString::number((unsigned)(dive->maxdepth.mm % scale) / 10); + str = QString("%1.%2").arg((unsigned)(dive->maxdepth.mm / scale)).arg(fract, 2, QChar('0')); } if (get_units()->length == units::FEET) { - str = QString::number(mm_to_feet(maxdepth.mm),'f',0); + str = QString::number(mm_to_feet(dive->maxdepth.mm),'f',0); } return str; } @@ -382,8 +361,8 @@ QString DiveItem::displayDuration() const { int hrs, mins, secs; - secs = duration.seconds % 60; - mins = duration.seconds / 60; + secs = dive->duration.seconds % 60; + mins = dive->duration.seconds / 60; hrs = mins / 60; mins -= hrs * 60; @@ -401,9 +380,9 @@ QString DiveItem::displayTemperature() const QString str; if (get_units()->temperature == units::CELSIUS) { - str = QString::number(mkelvin_to_C(temperature.mkelvin), 'f', 1); + str = QString::number(mkelvin_to_C(dive->watertemp.mkelvin), 'f', 1); } else { - str = QString::number(mkelvin_to_F(temperature.mkelvin), 'f', 1); + str = QString::number(mkelvin_to_F(dive->watertemp.mkelvin), 'f', 1); } return str; } @@ -413,9 +392,9 @@ QString DiveItem::displaySac() const QString str; if (get_units()->volume == units::LITER) { - str = QString::number(sac / 1000, 'f', 1); + str = QString::number(dive->sac / 1000, 'f', 1); } else { - str = QString::number(ml_to_cuft(sac), 'f', 2); + str = QString::number(ml_to_cuft(dive->sac), 'f', 2); } return str; } @@ -425,11 +404,11 @@ QString DiveItem::displayWeight() const QString str; if (get_units()->weight == units::KG) { - int gr = totalweight.grams % 1000; - int kg = totalweight.grams / 1000; + int gr = diveWeight() % 1000; + int kg = diveWeight() / 1000; str = QString("%1.%2").arg(kg).arg((unsigned)(gr + 500) / 100); } else { - str = QString("%1").arg((unsigned)(grams_to_lbs(totalweight.grams) + 0.5)); + str = QString("%1").arg((unsigned)(grams_to_lbs(diveWeight()) + 0.5)); } return str; } From 98027be1c1895013bfc3c4071c5d61d10fb70092 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 25 Apr 2013 08:38:58 -0700 Subject: [PATCH 087/226] Minor cleanup of constructors and one accessor for DiveItem Suggested-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 7894aa279..2fbfa9f88 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -294,9 +294,9 @@ void TankInfoModel::update() class DiveItem { public: - explicit DiveItem(): dive() { parentItem = 0; } + DiveItem(): dive(NULL), parentItem(NULL) {} - explicit DiveItem(struct dive *d, DiveItem *parent = 0); + DiveItem(struct dive *d, DiveItem *parent = NULL); ~DiveItem() { qDeleteAll(childlist); } @@ -319,7 +319,7 @@ public: QString displayTemperature() const; QString displayWeight() const; QString displaySac() const; - const QString diveLocation() const { return dive->location; } + const QString diveLocation() const { return QString::fromUtf8(dive->location); } const QString diveSuit() const { return dive->suit; } DiveItem *parent() const { return parentItem; } const QList& children() const { return childlist; } From b20b57b29fe2efcbafb064670a8ca53b0022eca0 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 25 Apr 2013 20:44:06 -0300 Subject: [PATCH 088/226] Make dirk happy by enabling sort in the model. Signed-off-by: Tomaz Canabrava --- qt-ui/mainwindow.cpp | 14 ++++++++++---- qt-ui/mainwindow.h | 2 ++ qt-ui/mainwindow.ui | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index b0b56a4e7..8cdc60193 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "divelistview.h" #include "starwidget.h" @@ -22,13 +23,14 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), - model(new DiveTripModel(this)) + model(new DiveTripModel(this)), + sortModel(new QSortFilterProxyModel()) { ui->setupUi(this); - ui->ListWidget->setModel(model); - setWindowIcon(QIcon(":subsurface-icon")); - // Just to test the star widgets, can be safely removed. + sortModel->setSourceModel(model); + ui->ListWidget->setModel(sortModel); + setWindowIcon(QIcon(":subsurface-icon")); } void MainWindow::on_actionNew_triggered() @@ -63,6 +65,10 @@ void MainWindow::on_actionOpen_triggered() report_dives(FALSE, FALSE); ui->InfoWidget->reload(); + + model->deleteLater(); + model = new DiveTripModel(this); + sortModel->setSourceModel(model); } void MainWindow::on_actionSave_triggered() diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 43ebde7f5..fdb100c7a 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -9,6 +9,7 @@ #include +class QSortFilterProxyModel; class DiveTripModel; namespace Ui @@ -68,6 +69,7 @@ private Q_SLOTS: private: Ui::MainWindow *ui; DiveTripModel *model; + QSortFilterProxyModel *sortModel; QString filter(); bool askSaveChanges(); diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 6ece13f57..3d3f0ec35 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -27,7 +27,11 @@ - + + + true + + @@ -45,7 +49,7 @@ 0 0 763 - 20 + 25 From 8da7a6985b057c5abfbd5ad1dbc7bdbfdcdfa0fb Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 25 Apr 2013 15:28:31 -0700 Subject: [PATCH 089/226] Ask Qt 4 to use the UTF-8 codec as the "codec for C strings" Qt 5 does this by default, so it's not necessary there (in fact, setCodecForCStrings was removed, so you catch any mistakes). Now all QString methods taking a const char* or QByteArray (constructor, append(), operator+=, operator<, etc.) will interpret that char array as UTF-8. Conversely, the QByteArray methods taking a QString will generate UTF-8 too. This includes the badly named QString::fromAscii() and QString::toAscii(). Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- qt-gui.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qt-gui.cpp b/qt-gui.cpp index d68071d35..d13c3c449 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #if HAVE_OSM_GPS_MAP @@ -1874,6 +1875,15 @@ void init_qt_ui(int *argcp, char ***argvp) void init_ui(int *argcp, char ***argvp) { application = new QApplication(*argcp, *argvp); + +#if QT_VERSION < 0x050000 + // ask QString in Qt 4 to interpret all char* as UTF-8, + // like Qt 5 does. + // 106 is "UTF-8", this is faster than lookup by name + // [http://www.iana.org/assignments/character-sets/character-sets.xml] + QTextCodec::setCodecForCStrings(QTextCodec::codecForMib(106)); +#endif + GtkWidget *win; GtkWidget *nb_page; GtkWidget *dive_list; From 1d0d42f861fc3a658eb22b99ba58616d716e095e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 25 Apr 2013 21:10:05 -0700 Subject: [PATCH 090/226] Remove the explicit UTF-8 conversions Thanks to commit bdbfdcdfa0fb ('Ask Qt 4 to use the UTF-8 codec as the "codec for C strings"') we no longer need the explicit UTF-8 conversion when creating QStrings from char *. Suggested-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- qt-gui.cpp | 4 ++-- qt-ui/models.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/qt-gui.cpp b/qt-gui.cpp index d13c3c449..7853cc4c0 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -60,7 +60,7 @@ Translator::Translator(QObject *parent): QString Translator::translate(const char *context, const char *sourceText, const char *disambiguation) const { - return QString::fromUtf8(gettext(sourceText)); + return gettext(sourceText); } static const GdkPixdata subsurface_icon_pixbuf = {}; @@ -1795,7 +1795,7 @@ void MainWindow::setCurrentFileName(const QString &fileName) void MainWindow::on_actionOpen_triggered() { - QString defaultFileName = QString::fromUtf8(prefs.default_filename); + QString defaultFileName = prefs.default_filename; QFileInfo fileInfo(defaultFileName); QFileDialog dialog(this, tr("Open File"), fileInfo.path()); diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 2fbfa9f88..5f803766f 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -301,7 +301,7 @@ public: ~DiveItem() { qDeleteAll(childlist); } int diveNumber() const { return dive->number; } - const QString diveDateTime() const { return QString::fromUtf8(get_dive_date_string(dive->when)); } + const QString diveDateTime() const { return get_dive_date_string(dive->when); } int diveDuration() const { return dive->duration.seconds; } int diveDepth() const { return dive->maxdepth.mm; } int diveSac() const { return dive->sac; } @@ -319,7 +319,7 @@ public: QString displayTemperature() const; QString displayWeight() const; QString displaySac() const; - const QString diveLocation() const { return QString::fromUtf8(dive->location); } + const QString diveLocation() const { return dive->location; } const QString diveSuit() const { return dive->suit; } DiveItem *parent() const { return parentItem; } const QList& children() const { return childlist; } @@ -511,7 +511,7 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int ret = tr("Date"); break; case RATING: - ret = QString::fromUtf8(UTF8_BLACKSTAR); + ret = UTF8_BLACKSTAR; break; case DEPTH: if (get_units()->length == units::METERS) @@ -524,9 +524,9 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int break; case TEMPERATURE: if (get_units()->temperature == units::CELSIUS) - ret = QString("%1%2").arg(QString::fromUtf8(UTF8_DEGREE)).arg("C"); + ret = QString("%1%2").arg(UTF8_DEGREE).arg("C"); else - ret = QString("%1%2").arg(QString::fromUtf8(UTF8_DEGREE)).arg("F"); + ret = QString("%1%2").arg(UTF8_DEGREE).arg("F"); break; case TOTALWEIGHT: if (get_units()->weight == units::KG) @@ -541,7 +541,7 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int ret = tr("Cyl"); break; case NITROX: - ret = QString("O%1%").arg(QString::fromUtf8(UTF8_SUBSCRIPT_2)); + ret = QString("O%1%").arg(UTF8_SUBSCRIPT_2); break; case SAC: ret = tr("SAC"); From 2f4d6bbe535a195046b4746fd3a771087ee4a6c4 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 27 Apr 2013 12:27:27 -0300 Subject: [PATCH 091/226] Added support for showing the Stars on the DiveTable For the stars on the dive table I had to rework a bit my StarRating widget, because it used a pixmap for each widget that were created. Not it uses only 2 pixmaps: the active and inactive ones. A new file was created named modeldelegates(h, cpp) that should hold all delegates of the models. For the GTK / C folks, a 'Delegate' ia s way to bypass the default behavior of the view that's displaying the data. I also added the code to display the stars if no delegate is set ( good for debugging. ) Signed-off-by: Tomaz Canabrava --- Makefile | 2 + qt-ui/divelistview.cpp | 2 + qt-ui/mainwindow.cpp | 3 +- qt-ui/modeldelegates.cpp | 36 ++++++++++++++++++ qt-ui/modeldelegates.h | 12 ++++++ qt-ui/models.cpp | 19 +++++++--- qt-ui/models.h | 5 ++- qt-ui/starwidget.cpp | 80 ++++++++++++++++++++-------------------- qt-ui/starwidget.h | 15 +++----- 9 files changed, 115 insertions(+), 59 deletions(-) create mode 100644 qt-ui/modeldelegates.cpp create mode 100644 qt-ui/modeldelegates.h diff --git a/Makefile b/Makefile index 6ebba4dd5..aac1759bc 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,7 @@ HEADERS = \ qt-ui/models.h \ qt-ui/plotareascene.h \ qt-ui/starwidget.h \ + qt-ui/modeldelegates.h \ SOURCES = \ @@ -75,6 +76,7 @@ SOURCES = \ qt-ui/models.cpp \ qt-ui/plotareascene.cpp \ qt-ui/starwidget.cpp \ + qt-ui/modeldelegates.cpp \ $(RESFILE) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index 676d7c463..e8a3d2311 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -5,6 +5,8 @@ * */ #include "divelistview.h" +#include "models.h" +#include "modeldelegates.h" DiveListView::DiveListView(QWidget *parent) : QTreeView(parent) { diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 8cdc60193..4c4143e98 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -20,7 +20,7 @@ #include "../dive.h" #include "../divelist.h" #include "../pref.h" - +#include "modeldelegates.h" MainWindow::MainWindow() : ui(new Ui::MainWindow()), model(new DiveTripModel(this)), @@ -69,6 +69,7 @@ void MainWindow::on_actionOpen_triggered() model->deleteLater(); model = new DiveTripModel(this); sortModel->setSourceModel(model); + ui->ListWidget->setItemDelegateForColumn(DiveTripModel::RATING, new StarWidgetsDelegate()); } void MainWindow::on_actionSave_triggered() diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp new file mode 100644 index 000000000..1bbf1061b --- /dev/null +++ b/qt-ui/modeldelegates.cpp @@ -0,0 +1,36 @@ +#include "modeldelegates.h" +#include "../dive.h" +#include "../divelist.h" +#include "starwidget.h" +#include "models.h" + +#include +#include + +void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + if (!index.isValid()){ + return; + } + + int rating = index.model()->data(index, DiveTripModel::DelegatesRole).toInt(); + + if (option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + + for(int i = 0; i < rating; i++) + painter->drawPixmap(option.rect.x() + i * IMG_SIZE + SPACING, option.rect.y(), StarWidget::starActive()); + + for(int i = rating; i < TOTALSTARS; i++) + painter->drawPixmap(option.rect.x() + i * IMG_SIZE + SPACING, option.rect.y(), StarWidget::starInactive()); + + painter->restore(); +} + +QSize StarWidgetsDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + return QSize(IMG_SIZE * TOTALSTARS + SPACING * (TOTALSTARS-1), IMG_SIZE); +} diff --git a/qt-ui/modeldelegates.h b/qt-ui/modeldelegates.h new file mode 100644 index 000000000..eacbb5a1e --- /dev/null +++ b/qt-ui/modeldelegates.h @@ -0,0 +1,12 @@ +#ifndef MODELDELEGATES_H +#define MODELDELEGATES_H + +#include + +class StarWidgetsDelegate : public QAbstractItemDelegate { + Q_OBJECT +public: + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; +}; +#endif diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 5f803766f..0944fe3e3 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -5,9 +5,6 @@ * */ #include "models.h" -#include "../dive.h" -#include "../divelist.h" - #include extern struct tank_info tank_info[100]; @@ -284,6 +281,7 @@ void TankInfoModel::update() } } + /*! A DiveItem for use with a DiveTripModel * * A simple class which wraps basic stats for a dive (e.g. duration, depth) and @@ -314,6 +312,8 @@ public: return tw.grams; } + int diveRating() const { return dive->rating; } + QString displayDuration() const; QString displayDepth() const; QString displayTemperature() const; @@ -335,6 +335,7 @@ private: QList childlist; }; + DiveItem::DiveItem(struct dive *d, DiveItem *p): dive(d), parentItem(p) @@ -490,6 +491,16 @@ QVariant DiveTripModel::data(const QModelIndex &index, int role) const case LOCATION: retVal = item->diveLocation(); break; + case RATING: + retVal = item->diveRating(); + break; + } + } + if (role == DelegatesRole){ + switch(index.column()){ + case RATING: + retVal = item->diveRating(); + break; } } return retVal; @@ -569,8 +580,6 @@ int DiveTripModel::rowCount(const QModelIndex &parent) const return item ? item->children().count() : 0; } - - int DiveTripModel::columnCount(const QModelIndex &parent) const { return parent.isValid() && parent.column() != 0 ? 0 : COLUMNS; diff --git a/qt-ui/models.h b/qt-ui/models.h index d64faf0bd..f4d9c8d3b 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -9,7 +9,7 @@ #include #include "../dive.h" - +#include "../divelist.h" /* Encapsulates the tank_info global variable * to show on Qt`s Model View System.*/ class TankInfoModel : public QAbstractTableModel { @@ -74,11 +74,12 @@ private: /*! An AbstractItemModel for recording dive trip information such as a list of dives. * */ -class DiveItem; // Represents a single item on the model, implemented in the .cpp since it's private for this class. +class DiveItem; class DiveTripModel : public QAbstractItemModel { public: enum Column {NR, DATE, RATING, DEPTH, DURATION, TEMPERATURE, TOTALWEIGHT, SUIT, CYLINDER, NITROX, SAC, OTU, MAXCNS, LOCATION, COLUMNS }; + enum { DelegatesRole = Qt::UserRole }; DiveTripModel(QObject *parent = 0); diff --git a/qt-ui/starwidget.cpp b/qt-ui/starwidget.cpp index 866fb5834..4d1fa066c 100644 --- a/qt-ui/starwidget.cpp +++ b/qt-ui/starwidget.cpp @@ -5,32 +5,29 @@ #include #include +QPixmap* StarWidget::activeStar = 0; +QPixmap* StarWidget::inactiveStar = 0; + +QPixmap StarWidget::starActive() +{ + return (*activeStar); +} + +QPixmap StarWidget::starInactive() +{ + return (*inactiveStar); +} + int StarWidget::currentStars() const { return current; } -void StarWidget::enableHalfStars(bool enabled) -{ - halfStar = enabled; - update(); -} - -bool StarWidget::halfStarsEnabled() const -{ - return halfStar; -} - -int StarWidget::maxStars() const -{ - return stars; -} - void StarWidget::mouseReleaseEvent(QMouseEvent* event) { int starClicked = event->pos().x() / IMG_SIZE + 1; - if (starClicked > stars) - starClicked = stars; + if (starClicked > TOTALSTARS) + starClicked = TOTALSTARS; if (current == starClicked) current -= 1; @@ -45,10 +42,10 @@ void StarWidget::paintEvent(QPaintEvent* event) QPainter p(this); for(int i = 0; i < current; i++) - p.drawPixmap(i * IMG_SIZE + SPACING, 0, activeStar); + p.drawPixmap(i * IMG_SIZE + SPACING, 0, starActive()); - for(int i = current; i < stars; i++) - p.drawPixmap(i * IMG_SIZE + SPACING, 0, inactiveStar); + for(int i = current; i < TOTALSTARS; i++) + p.drawPixmap(i * IMG_SIZE + SPACING, 0, starInactive()); } void StarWidget::setCurrentStars(int value) @@ -58,27 +55,25 @@ void StarWidget::setCurrentStars(int value) Q_EMIT valueChanged(current); } -void StarWidget::setMaximumStars(int maximum) -{ - stars = maximum; - update(); -} - StarWidget::StarWidget(QWidget* parent, Qt::WindowFlags f): QWidget(parent, f), - stars(5), - current(0), - halfStar(false) + current(0) { - QSvgRenderer render(QString(":star")); - QPixmap renderedStar(IMG_SIZE, IMG_SIZE); + if(!activeStar){ + activeStar = new QPixmap(); + QSvgRenderer render(QString(":star")); + QPixmap renderedStar(IMG_SIZE, IMG_SIZE); - renderedStar.fill(Qt::transparent); - QPainter painter(&renderedStar); + renderedStar.fill(Qt::transparent); + QPainter painter(&renderedStar); - render.render(&painter, QRectF(0, 0, IMG_SIZE, IMG_SIZE)); - activeStar = renderedStar; - inactiveStar = grayImage(&renderedStar); + render.render(&painter, QRectF(0, 0, IMG_SIZE, IMG_SIZE)); + (*activeStar) = renderedStar; + } + if(!inactiveStar){ + inactiveStar = new QPixmap(); + (*inactiveStar) = grayImage(activeStar); + } } QPixmap StarWidget::grayImage(QPixmap* coloredImg) @@ -86,17 +81,20 @@ QPixmap StarWidget::grayImage(QPixmap* coloredImg) QImage img = coloredImg->toImage(); for (int i = 0; i < img.width(); ++i) { for (int j = 0; j < img.height(); ++j) { - QRgb col = img.pixel(i, j); - if (!col) + QRgb rgb = img.pixel(i, j); + if (!rgb) continue; - int gray = QColor(Qt::darkGray).rgb(); + + QColor c(rgb); + int gray = (c.red() + c.green() + c.blue()) / 3; img.setPixel(i, j, qRgb(gray, gray, gray)); } } + return QPixmap::fromImage(img); } QSize StarWidget::sizeHint() const { - return QSize(IMG_SIZE * stars + SPACING * (stars-1), IMG_SIZE); + return QSize(IMG_SIZE * TOTALSTARS + SPACING * (TOTALSTARS-1), IMG_SIZE); } diff --git a/qt-ui/starwidget.h b/qt-ui/starwidget.h index cdbb3ab5c..d92be5a98 100644 --- a/qt-ui/starwidget.h +++ b/qt-ui/starwidget.h @@ -3,39 +3,34 @@ #include +enum StarConfig {SPACING = 2, IMG_SIZE = 16, TOTALSTARS = 5}; class StarWidget : public QWidget { Q_OBJECT public: explicit StarWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); - - int maxStars() const; int currentStars() const; - bool halfStarsEnabled() const; /*reimp*/ QSize sizeHint() const; - enum {SPACING = 2, IMG_SIZE = 16}; + static QPixmap starActive(); + static QPixmap starInactive(); Q_SIGNALS: void valueChanged(int stars); public Q_SLOTS: void setCurrentStars(int value); - void setMaximumStars(int maximum); - void enableHalfStars(bool enabled); protected: /*reimp*/ void mouseReleaseEvent(QMouseEvent* ); /*reimp*/ void paintEvent(QPaintEvent* ); private: - int stars; int current; - bool halfStar; - QPixmap activeStar; - QPixmap inactiveStar; + static QPixmap* activeStar; + static QPixmap* inactiveStar; QPixmap grayImage(QPixmap *coloredImg); }; From 89f02c42aa2fb49a811cb6e31dd40cd56dee0cf9 Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Sat, 27 Apr 2013 10:09:57 +0100 Subject: [PATCH 092/226] Add support for remembering settings Use QSettings to provide persistent storage of settings. For example, we store and restore the size of the MainWindow. We use the organisation name hohndel.org and keep subsurface as the application name. A section is specified for things to do with the MainWindow; other sections could be added e.g. for preferred units? Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 37 +++++++++++++++++++++++++++++++++++++ qt-ui/mainwindow.h | 5 +++++ 2 files changed, 42 insertions(+) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 8cdc60193..0c6691a06 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "divelistview.h" #include "starwidget.h" @@ -31,6 +33,7 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), ui->ListWidget->setModel(sortModel); setWindowIcon(QIcon(":subsurface-icon")); + readSettings(); } void MainWindow::on_actionNew_triggered() @@ -138,6 +141,11 @@ void MainWindow::on_actionPreferences_triggered() void MainWindow::on_actionQuit_triggered() { qDebug("actionQuit"); + if (unsaved_changes() && (askSaveChanges() == FALSE)) + { + return; + } + } void MainWindow::on_actionDownloadDC_triggered() @@ -284,3 +292,32 @@ bool MainWindow::askSaveChanges() } return false; } + +void MainWindow::readSettings() +{ + QSettings settings("hohndel.org","subsurface"); + + /* note: section/key i.e. forward slash to separate */ + QSize sz = settings.value("MainWindow/size").value(); + resize(sz); +} + +void MainWindow::writeSettings() +{ + QSettings settings("hohndel.org","subsurface"); + settings.beginGroup("MainWindow"); + settings.setValue("size",size()); + settings.endGroup(); + /* other groups here; avoid '/' and '\' in keys with setValue(...) please */ +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (unsaved_changes() && (askSaveChanges() == FALSE)) + { + event->ignore(); + return; + } + event->accept(); + writeSettings(); +} \ No newline at end of file diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index fdb100c7a..eece91ade 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -66,12 +66,17 @@ private Q_SLOTS: void on_actionAboutSubsurface_triggered(); void on_actionUserManual_triggered(); +protected: + void closeEvent(QCloseEvent *); + private: Ui::MainWindow *ui; DiveTripModel *model; QSortFilterProxyModel *sortModel; QString filter(); bool askSaveChanges(); + void readSettings(); + void writeSettings(); }; From 4bb0cd8cef2bbbbe3df6fc3daa9ccca95f065038 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 27 Apr 2013 20:47:47 -0700 Subject: [PATCH 093/226] Minor coding style cleanups - and use core logic function Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 0c6691a06..d20f8cfb6 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -44,9 +44,8 @@ void MainWindow::on_actionNew_triggered() void MainWindow::on_actionOpen_triggered() { QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::homePath(), filter()); - if (filename.isEmpty()) { + if (filename.isEmpty()) return; - } // Needed to convert to char* QByteArray fileNamePtr = filename.toLocal8Bit(); @@ -57,15 +56,13 @@ void MainWindow::on_actionOpen_triggered() parse_file(fileNamePtr.data(), &error); set_filename(fileNamePtr.data(), TRUE); - if (error != NULL) - { + if (error != NULL) { QMessageBox::warning(this, "Error", error->message); g_error_free(error); error = NULL; } - //WARNING: Port This method to Qt - report_dives(FALSE, FALSE); + process_dives(FALSE, FALSE); ui->InfoWidget->reload(); @@ -86,15 +83,12 @@ void MainWindow::on_actionSaveAs_triggered() void MainWindow::on_actionClose_triggered() { if (unsaved_changes() && (askSaveChanges() == FALSE)) - { return; - } /* free the dives and trips */ while (dive_table.nr) - { delete_single_dive(0); - } + mark_divelist_changed(FALSE); /* clear the selection and the statistics */ @@ -142,10 +136,7 @@ void MainWindow::on_actionQuit_triggered() { qDebug("actionQuit"); if (unsaved_changes() && (askSaveChanges() == FALSE)) - { return; - } - } void MainWindow::on_actionDownloadDC_triggered() @@ -313,11 +304,10 @@ void MainWindow::writeSettings() void MainWindow::closeEvent(QCloseEvent *event) { - if (unsaved_changes() && (askSaveChanges() == FALSE)) - { + if (unsaved_changes() && (askSaveChanges() == FALSE)) { event->ignore(); return; } event->accept(); writeSettings(); -} \ No newline at end of file +} From 0be521bb25cf6210ad47e42eb7a8eb7638c32442 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sun, 28 Apr 2013 08:45:22 -0300 Subject: [PATCH 094/226] Fixed loading the stars when opening with file as argv. minor cleanup Signed-off-by: Tomaz Canabrava --- qt-ui/divelistview.cpp | 1 + qt-ui/mainwindow.cpp | 2 +- qt-ui/modeldelegates.cpp | 3 ++- qt-ui/models.cpp | 7 ------- qt-ui/models.h | 1 - 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index e8a3d2311..fb19a7060 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -11,4 +11,5 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent) { setUniformRowHeights(true); + setItemDelegateForColumn(DiveTripModel::RATING, new StarWidgetsDelegate()); } diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 4c4143e98..f4538db22 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -69,7 +69,7 @@ void MainWindow::on_actionOpen_triggered() model->deleteLater(); model = new DiveTripModel(this); sortModel->setSourceModel(model); - ui->ListWidget->setItemDelegateForColumn(DiveTripModel::RATING, new StarWidgetsDelegate()); + ui->ListWidget->sortByColumn(0, Qt::DescendingOrder); } void MainWindow::on_actionSave_triggered() diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 1bbf1061b..1ac2f46c6 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -6,6 +6,7 @@ #include #include +#include void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { @@ -13,7 +14,7 @@ void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o return; } - int rating = index.model()->data(index, DiveTripModel::DelegatesRole).toInt(); + int rating = index.model()->data(index, Qt::DisplayRole).toInt(); if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 0944fe3e3..23eea3e48 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -496,13 +496,6 @@ QVariant DiveTripModel::data(const QModelIndex &index, int role) const break; } } - if (role == DelegatesRole){ - switch(index.column()){ - case RATING: - retVal = item->diveRating(); - break; - } - } return retVal; } diff --git a/qt-ui/models.h b/qt-ui/models.h index f4d9c8d3b..9e4666dc7 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -79,7 +79,6 @@ class DiveTripModel : public QAbstractItemModel { public: enum Column {NR, DATE, RATING, DEPTH, DURATION, TEMPERATURE, TOTALWEIGHT, SUIT, CYLINDER, NITROX, SAC, OTU, MAXCNS, LOCATION, COLUMNS }; - enum { DelegatesRole = Qt::UserRole }; DiveTripModel(QObject *parent = 0); From 4c665be05f7128a5a259fb3b71f3d65019639b1d Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Sun, 28 Apr 2013 10:05:37 +0100 Subject: [PATCH 095/226] Save and retore splitter dimensions. Rename splitters and remove seemingly redundant empty splitter. Use save/restoreState to save splitter sizes using QSettings. Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 9 +++++++-- qt-ui/mainwindow.ui | 13 +++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index c1e15e30c..71ba70525 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -289,9 +289,12 @@ void MainWindow::readSettings() { QSettings settings("hohndel.org","subsurface"); - /* note: section/key i.e. forward slash to separate */ - QSize sz = settings.value("MainWindow/size").value(); + settings.beginGroup("MainWindow"); + QSize sz = settings.value("size").value(); resize(sz); + ui->mainSplitter->restoreState(settings.value("mainSplitter").toByteArray()); + ui->infoProfileSplitter->restoreState(settings.value("infoProfileSplitter").toByteArray()); + settings.endGroup(); } void MainWindow::writeSettings() @@ -299,6 +302,8 @@ void MainWindow::writeSettings() QSettings settings("hohndel.org","subsurface"); settings.beginGroup("MainWindow"); settings.setValue("size",size()); + settings.setValue("mainSplitter", ui->mainSplitter->saveState()); + settings.setValue("infoProfileSplitter", ui->infoProfileSplitter->saveState()); settings.endGroup(); /* other groups here; avoid '/' and '\' in keys with setValue(...) please */ } diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 3d3f0ec35..073476918 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -16,11 +16,11 @@ - + Qt::Vertical - + Qt::Horizontal @@ -34,13 +34,6 @@ - - - - Qt::Horizontal - - - @@ -49,7 +42,7 @@ 0 0 763 - 25 + 20 From 730e055e5aacd2f917be9d86140773853670e850 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 1 May 2013 09:04:14 -0700 Subject: [PATCH 096/226] Use the existing current_dive macro in Qt code Replicating this with the currentDive member seems to make no sense. Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 39 ++++++++++++++++++++------------------- qt-ui/models.h | 7 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 23eea3e48..d26b52ec9 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -59,9 +59,7 @@ QVariant CylindersModel::data(const QModelIndex& index, int role) const if (!index.isValid() || index.row() >= MAX_CYLINDERS) { return ret; } - - struct dive *d = get_dive(selected_dive); - cylinder_t& cyl = d->cylinder[index.row()]; + cylinder_t& cyl = current_dive->cylinder[index.row()]; if (role == Qt::DisplayRole) { switch(index.column()) { @@ -93,57 +91,60 @@ QVariant CylindersModel::data(const QModelIndex& index, int role) const int CylindersModel::rowCount(const QModelIndex& parent) const { - return usedRows[currentDive]; + return usedRows[current_dive]; } void CylindersModel::add(cylinder_t* cyl) { - if (usedRows[currentDive] >= MAX_CYLINDERS) { + if (usedRows[current_dive] >= MAX_CYLINDERS) { free(cyl); } - int row = usedRows[currentDive]; + int row = usedRows[current_dive]; - cylinder_t& cylinder = currentDive->cylinder[row]; + cylinder_t& cylinder = current_dive->cylinder[row]; cylinder.end.mbar = cyl->end.mbar; cylinder.start.mbar = cyl->start.mbar; beginInsertRows(QModelIndex(), row, row); - usedRows[currentDive]++; + usedRows[current_dive]++; endInsertRows(); } void CylindersModel::update() { - if (usedRows[currentDive] > 0) { - beginRemoveRows(QModelIndex(), 0, usedRows[currentDive]-1); + if (usedRows[current_dive] > 0) { + beginRemoveRows(QModelIndex(), 0, usedRows[current_dive]-1); endRemoveRows(); } - - currentDive = get_dive(selected_dive); - if (usedRows[currentDive] > 0) { - beginInsertRows(QModelIndex(), 0, usedRows[currentDive]-1); + if (usedRows[current_dive] > 0) { + beginInsertRows(QModelIndex(), 0, usedRows[current_dive]-1); endInsertRows(); } } void CylindersModel::clear() { - if (usedRows[currentDive] > 0) { - beginRemoveRows(QModelIndex(), 0, usedRows[currentDive]-1); - usedRows[currentDive] = 0; + if (usedRows[current_dive] > 0) { + beginRemoveRows(QModelIndex(), 0, usedRows[current_dive]-1); + usedRows[current_dive] = 0; endRemoveRows(); } } void WeightModel::clear() { + if (usedRows[current_dive] > 0) { + beginRemoveRows(QModelIndex(), 0, usedRows[current_dive]-1); + usedRows[current_dive] = 0; + endRemoveRows(); + } } int WeightModel::columnCount(const QModelIndex& parent) const { - return 0; + return 2; } QVariant WeightModel::data(const QModelIndex& index, int role) const @@ -153,7 +154,7 @@ QVariant WeightModel::data(const QModelIndex& index, int role) const int WeightModel::rowCount(const QModelIndex& parent) const { - return rows; + return usedRows[current_dive]; } QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int role) const diff --git a/qt-ui/models.h b/qt-ui/models.h index 9e4666dc7..b6bcdec78 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -47,12 +47,10 @@ public: void clear(); void update(); private: - dive *currentDive; - /* Since the dive doesn`t stores the number of cylinders that * it has ( max 8 ) and since I don`t want to make a * model-for-each-dive, let`s hack this here instead. */ - QMap usedRows; + QMap usedRows; }; /* Encapsulation of the Weight Model, that represents @@ -68,7 +66,8 @@ class WeightModel : public QAbstractTableModel { void clear(); void update(); private: - int rows; + /* Remember the number of rows in a dive */ + QMap usedRows; }; /*! An AbstractItemModel for recording dive trip information such as a list of dives. From 5c2ce0ac200cbf4b37a9765148b78d3091b5cd9f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 1 May 2013 10:11:46 -0700 Subject: [PATCH 097/226] Add data and add functions for WeightModel Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- qt-ui/models.h | 2 +- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index d26b52ec9..4583c36cd 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -149,7 +149,29 @@ int WeightModel::columnCount(const QModelIndex& parent) const QVariant WeightModel::data(const QModelIndex& index, int role) const { - return QVariant(); + QVariant ret; + if (!index.isValid() || index.row() >= MAX_WEIGHTSYSTEMS) { + return ret; + } + weightsystem_t *ws = ¤t_dive->weightsystem[index.row()]; + + if (role == Qt::DisplayRole) { + switch(index.column()) { + case TYPE: + ret = QString(ws->description); + break; + case WEIGHT: + if (get_units()->weight == units::KG) { + int gr = ws->weight.grams % 1000; + int kg = ws->weight.grams / 1000; + ret = QString("%1.%2").arg(kg).arg((unsigned)(gr + 500) / 100); + } else { + ret = QString("%1").arg((unsigned)(grams_to_lbs(ws->weight.grams) + 0.5)); + } + break; + } + } + return ret; } int WeightModel::rowCount(const QModelIndex& parent) const @@ -175,8 +197,22 @@ QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int r return ret; } -void WeightModel::add(weight_t* weight) +void WeightModel::add(weightsystem_t* weight) { + if (usedRows[current_dive] >= MAX_WEIGHTSYSTEMS) { + free(weight); + } + + int row = usedRows[current_dive]; + + weightsystem_t *ws = ¤t_dive->weightsystem[row]; + + ws->description = weight->description; + ws->weight.grams = weight->weight.grams; + + beginInsertRows(QModelIndex(), row, row); + usedRows[current_dive]++; + endInsertRows(); } void WeightModel::update() diff --git a/qt-ui/models.h b/qt-ui/models.h index b6bcdec78..46da9e51b 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -62,7 +62,7 @@ class WeightModel : public QAbstractTableModel { /*reimp*/ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const; - void add(weight_t *weight); + void add(weightsystem_t *weight); void clear(); void update(); private: From 04e59a0e1cdb32c5091fc4bc0d692f00821ab849 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 1 May 2013 14:30:34 -0700 Subject: [PATCH 098/226] Hook up adding a weightsystem Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 17 +++++++++++++++++ qt-ui/maintab.h | 3 +++ qt-ui/maintab.ui | 6 +++--- qt-ui/models.cpp | 16 +++++++++------- qt-ui/models.h | 2 ++ 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 7d60db5c2..f8c69ed9c 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -83,6 +83,23 @@ void MainTab::on_delCylinder_clicked() { } +void MainTab::on_addWeight_clicked() +{ + /* this needs a dialog - right now we just fill in a dummy */ + weightsystem_t *newWeightsystem = (weightsystem_t *) malloc(sizeof(weightsystem_t)); + newWeightsystem->description = "Just testing"; + newWeightsystem->weight.grams = 15000; + weightModel->add(newWeightsystem); +} + +void MainTab::on_editWeight_clicked() +{ +} + +void MainTab::on_delWeight_clicked() +{ +} + void MainTab::reload() { cylindersModel->update(); diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h index 44815fafc..cf83e0dfe 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -31,6 +31,9 @@ public Q_SLOTS: void on_addCylinder_clicked(); void on_editCylinder_clicked(); void on_delCylinder_clicked(); + void on_addWeight_clicked(); + void on_editWeight_clicked(); + void on_delWeight_clicked(); private: Ui::MainTab *ui; diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index 7edbf5837..a99b0aed7 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -152,14 +152,14 @@ - + Edit - + Add @@ -179,7 +179,7 @@ - + Delete diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 4583c36cd..6756002e2 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -186,13 +186,15 @@ QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int r return ret; } - switch(section) { - case TYPE: - ret = tr("Type"); - break; - case WEIGHT: - ret = tr("Weight"); - break; + if (role == Qt::DisplayRole) { + switch(section) { + case TYPE: + ret = tr("Type"); + break; + case WEIGHT: + ret = tr("Weight"); + break; + } } return ret; } diff --git a/qt-ui/models.h b/qt-ui/models.h index 46da9e51b..9a4602f18 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -56,6 +56,8 @@ private: /* Encapsulation of the Weight Model, that represents * the current weights on a dive. */ class WeightModel : public QAbstractTableModel { +Q_OBJECT +public: enum Column {TYPE, WEIGHT}; /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; /*reimp*/ int columnCount(const QModelIndex& parent = QModelIndex()) const; From 482bea84c2f218f515b3b16556197379623a8028 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 1 May 2013 14:49:17 -0700 Subject: [PATCH 099/226] Don't add cylinders and weightsystems past the MAX We actually should disable the 'Add' button, I guess. Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 6 ++++++ qt-ui/models.cpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index f8c69ed9c..4174ce5dd 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -61,6 +61,9 @@ void MainTab::clearStats() void MainTab::on_addCylinder_clicked() { + if (cylindersModel->rowCount() >= MAX_CYLINDERS) + return; + AddCylinderDialog dialog(this); cylinder_t *newCylinder = (cylinder_t*) malloc(sizeof(cylinder_t)); newCylinder->type.description = ""; @@ -85,6 +88,9 @@ void MainTab::on_delCylinder_clicked() void MainTab::on_addWeight_clicked() { + if (weightModel->rowCount() >= MAX_WEIGHTSYSTEMS) + return; + /* this needs a dialog - right now we just fill in a dummy */ weightsystem_t *newWeightsystem = (weightsystem_t *) malloc(sizeof(weightsystem_t)); newWeightsystem->description = "Just testing"; diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 6756002e2..f1bb8d137 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -98,6 +98,7 @@ void CylindersModel::add(cylinder_t* cyl) { if (usedRows[current_dive] >= MAX_CYLINDERS) { free(cyl); + return; } int row = usedRows[current_dive]; @@ -203,6 +204,7 @@ void WeightModel::add(weightsystem_t* weight) { if (usedRows[current_dive] >= MAX_WEIGHTSYSTEMS) { free(weight); + return; } int row = usedRows[current_dive]; From f45618f0c7ce5af85e0f69aab49ef20a1663bca1 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 1 May 2013 15:37:41 -0700 Subject: [PATCH 100/226] Create Add Weightsystem dialog My first attempt to create a Qt dialog and to hook it up with the program. Unsurprisingly this doesn't quite work as expected (i.e., the values I enter aren't populated in the model), but it's a start... Signed-off-by: Dirk Hohndel --- Makefile | 2 + qt-ui/addweightsystemdialog.cpp | 38 +++++++++++ qt-ui/addweightsystemdialog.h | 30 +++++++++ qt-ui/addweightsystemdialog.ui | 109 ++++++++++++++++++++++++++++++++ qt-ui/maintab.cpp | 14 +++- 5 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 qt-ui/addweightsystemdialog.cpp create mode 100644 qt-ui/addweightsystemdialog.h create mode 100644 qt-ui/addweightsystemdialog.ui diff --git a/Makefile b/Makefile index aac1759bc..1bb080363 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ HEADERS = \ qt-ui/addcylinderdialog.h \ + qt-ui/addweightsystemdialog.h \ qt-ui/divelistview.h \ qt-ui/maintab.h \ qt-ui/mainwindow.h \ @@ -70,6 +71,7 @@ SOURCES = \ webservice.c \ qt-gui.cpp \ qt-ui/addcylinderdialog.cpp \ + qt-ui/addweightsystemdialog.cpp \ qt-ui/divelistview.cpp \ qt-ui/maintab.cpp \ qt-ui/mainwindow.cpp \ diff --git a/qt-ui/addweightsystemdialog.cpp b/qt-ui/addweightsystemdialog.cpp new file mode 100644 index 000000000..6ac1c02f9 --- /dev/null +++ b/qt-ui/addweightsystemdialog.cpp @@ -0,0 +1,38 @@ +/* + * addweightsystemdialog.cpp + * + * classes for the add weightsystem dialog of Subsurface + * + */ +#include "addweightsystemdialog.h" +#include "ui_addweightsystemdialog.h" +#include +#include +#include "../conversions.h" +#include "models.h" + +AddWeightsystemDialog::AddWeightsystemDialog(QWidget *parent) : ui(new Ui::AddWeightsystemDialog()) +{ + ui->setupUi(this); +} + +void AddWeightsystemDialog::setWeightsystem(weightsystem_t *ws) +{ + currentWeightsystem = ws; + + ui->description->insert(QString(ws->description)); + if (get_units()->weight == units::KG) + ui->weight->setValue(ws->weight.grams / 1000); + else + ui->weight->setValue(grams_to_lbs(ws->weight.grams)); +} + +void AddWeightsystemDialog::updateWeightsystem() +{ + currentWeightsystem->description = strdup(ui->description->text().toUtf8().data()); + if (get_units()->weight == units::KG) + currentWeightsystem->weight.grams = ui->weight->value() * 1000; + else + currentWeightsystem->weight.grams = lbs_to_grams(ui->weight->value()); +} + diff --git a/qt-ui/addweightsystemdialog.h b/qt-ui/addweightsystemdialog.h new file mode 100644 index 000000000..e99dc08d8 --- /dev/null +++ b/qt-ui/addweightsystemdialog.h @@ -0,0 +1,30 @@ +/* + * addweightsystemdialog.h + * + * header file for the add weightsystem dialog of Subsurface + * + */ +#ifndef ADDWEIGHTSYSTEMDIALOG_H +#define ADDWEIGHTSYSTEMDIALOG_H + +#include +#include "../dive.h" + +namespace Ui{ + class AddWeightsystemDialog; +} + +class AddWeightsystemDialog : public QDialog{ + Q_OBJECT +public: + explicit AddWeightsystemDialog(QWidget* parent = 0); + void setWeightsystem(weightsystem_t *ws); + void updateWeightsystem(); + +private: + Ui::AddWeightsystemDialog *ui; + weightsystem_t *currentWeightsystem; +}; + + +#endif diff --git a/qt-ui/addweightsystemdialog.ui b/qt-ui/addweightsystemdialog.ui new file mode 100644 index 000000000..11e60d562 --- /dev/null +++ b/qt-ui/addweightsystemdialog.ui @@ -0,0 +1,109 @@ + + + AddWeightsystemDialog + + + + 0 + 0 + 408 + 186 + + + + Dialog + + + + + + Weightsystem + + + + QFormLayout::ExpandingFieldsGrow + + + + + Description + + + + + + + Weight + + + + + + + + 0 + 0 + + + + Qt::ImhFormattedNumbersOnly + + + true + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AddWeightsystemDialog + accept() + + + 248 + 269 + + + 157 + 260 + + + + + buttonBox + rejected() + AddWeightsystemDialog + reject() + + + 290 + 269 + + + 286 + 260 + + + + + diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 4174ce5dd..f26a72a8a 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -7,6 +7,7 @@ #include "maintab.h" #include "ui_maintab.h" #include "addcylinderdialog.h" +#include "addweightsystemdialog.h" #include @@ -91,10 +92,17 @@ void MainTab::on_addWeight_clicked() if (weightModel->rowCount() >= MAX_WEIGHTSYSTEMS) return; - /* this needs a dialog - right now we just fill in a dummy */ + AddWeightsystemDialog dialog(this); weightsystem_t *newWeightsystem = (weightsystem_t *) malloc(sizeof(weightsystem_t)); - newWeightsystem->description = "Just testing"; - newWeightsystem->weight.grams = 15000; + newWeightsystem->description = ""; + newWeightsystem->weight.grams = 0; + + dialog.setWeightsystem(newWeightsystem); + int result = dialog.exec(); + if (result == QDialog::Rejected) + return; + + dialog.updateWeightsystem(); weightModel->add(newWeightsystem); } From 00d85313827af88ae5f35b2391ffa6964e81da49 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 1 May 2013 16:23:20 -0700 Subject: [PATCH 101/226] Fix a couple of small problems in add weightsystem The constructor letf the currentWeightsytem variable uninitialized. Instead of creating the memory leak by malloc-ing the newWeightsystem in the on_addWeight_clicked() function use a local variable instead and pass its address around. Signed-off-by: Dirk Hohndel --- qt-ui/addweightsystemdialog.cpp | 1 + qt-ui/maintab.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/qt-ui/addweightsystemdialog.cpp b/qt-ui/addweightsystemdialog.cpp index 6ac1c02f9..48a399de9 100644 --- a/qt-ui/addweightsystemdialog.cpp +++ b/qt-ui/addweightsystemdialog.cpp @@ -14,6 +14,7 @@ AddWeightsystemDialog::AddWeightsystemDialog(QWidget *parent) : ui(new Ui::AddWeightsystemDialog()) { ui->setupUi(this); + currentWeightsystem = NULL; } void AddWeightsystemDialog::setWeightsystem(weightsystem_t *ws) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index f26a72a8a..2467dd0d7 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -93,17 +93,17 @@ void MainTab::on_addWeight_clicked() return; AddWeightsystemDialog dialog(this); - weightsystem_t *newWeightsystem = (weightsystem_t *) malloc(sizeof(weightsystem_t)); - newWeightsystem->description = ""; - newWeightsystem->weight.grams = 0; + weightsystem_t newWeightsystem; + newWeightsystem.description = ""; + newWeightsystem.weight.grams = 0; - dialog.setWeightsystem(newWeightsystem); + dialog.setWeightsystem(&newWeightsystem); int result = dialog.exec(); if (result == QDialog::Rejected) return; dialog.updateWeightsystem(); - weightModel->add(newWeightsystem); + weightModel->add(&newWeightsystem); } void MainTab::on_editWeight_clicked() From 764a863082f9337156fc4bcf5c0ecc6ae3d149d6 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Wed, 1 May 2013 23:51:34 -0300 Subject: [PATCH 102/226] Added Support for the Trips and Dives on the DiveList model. Now the list and dives will work in the same way that the GTK version does. The code got changed heavly because the old one was just looking at the dives and didn't worked like a tree. small adaptations on the list view and model delegates because of the changes done on this model. Signed-off-by: Tomaz Canabrava --- qt-ui/divelistview.cpp | 2 +- qt-ui/modeldelegates.cpp | 12 +- qt-ui/models.cpp | 447 +++++++++++++++++++++------------------ qt-ui/models.h | 41 +++- 4 files changed, 285 insertions(+), 217 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index fb19a7060..45b6cf4d7 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -11,5 +11,5 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent) { setUniformRowHeights(true); - setItemDelegateForColumn(DiveTripModel::RATING, new StarWidgetsDelegate()); + setItemDelegateForColumn(TreeItemDT::RATING, new StarWidgetsDelegate()); } diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 1ac2f46c6..00f10092d 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -14,10 +14,16 @@ void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o return; } - int rating = index.model()->data(index, Qt::DisplayRole).toInt(); + QVariant value = index.model()->data(index, Qt::DisplayRole); - if (option.state & QStyle::State_Selected) - painter->fillRect(option.rect, option.palette.highlight()); + if (!value.isValid()){ + return; + } + + int rating = value.toInt(); + + if(option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 23eea3e48..7b9d8dc16 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -5,7 +5,8 @@ * */ #include "models.h" -#include +#include +#include extern struct tank_info tank_info[100]; @@ -289,59 +290,162 @@ void TankInfoModel::update() * QObject. * */ -class DiveItem + +TreeItemDT::~TreeItemDT() { -public: - DiveItem(): dive(NULL), parentItem(NULL) {} + qDeleteAll(childs); +} - DiveItem(struct dive *d, DiveItem *parent = NULL); +int TreeItemDT::row() const +{ + if (parent) + return parent->childs.indexOf(const_cast(this)); - ~DiveItem() { qDeleteAll(childlist); } + return 0; +} - int diveNumber() const { return dive->number; } - const QString diveDateTime() const { return get_dive_date_string(dive->when); } - int diveDuration() const { return dive->duration.seconds; } - int diveDepth() const { return dive->maxdepth.mm; } - int diveSac() const { return dive->sac; } - int diveOtu() const { return dive->otu; } - int diveMaxcns() const { return dive->maxcns; } +QVariant TreeItemDT::data(int column, int role) const +{ + QVariant ret; + switch (column) { + case NR: + ret = tr("#"); + break; + case DATE: + ret = tr("Date"); + break; + case RATING: + ret = UTF8_BLACKSTAR; + break; + case DEPTH: + ret = (get_units()->length == units::METERS) ? tr("m") : tr("ft"); + break; + case DURATION: + ret = tr("min"); + break; + case TEMPERATURE: + ret = QString("%1%2").arg(UTF8_DEGREE).arg( (get_units()->temperature == units::CELSIUS) ? "C" : "F"); + break; + case TOTALWEIGHT: + ret = (get_units()->weight == units::KG) ? tr("kg") : tr("lbs"); + break; + case SUIT: + ret = tr("Suit"); + break; + case CYLINDER: + ret = tr("Cyl"); + break; + case NITROX: + ret = QString("O%1%").arg(UTF8_SUBSCRIPT_2); + break; + case SAC: + ret = tr("SAC"); + break; + case OTU: + ret = tr("OTU"); + break; + case MAXCNS: + ret = tr("maxCNS"); + break; + case LOCATION: + ret = tr("Location"); + break; + } + return ret; +} - int diveWeight() const - { - weight_t tw = { total_weight(dive) }; - return tw.grams; +struct TripItem : public TreeItemDT { + virtual QVariant data(int column, int role) const; + dive_trip_t* trip; +}; + +QVariant TripItem::data(int column, int role) const +{ + QVariant ret; + + if (column != LOCATION) { + return ret; } - int diveRating() const { return dive->rating; } + switch (role) { + case Qt::DisplayRole: + ret = QString(trip->location); + } + + return ret; +} + +struct DiveItem : public TreeItemDT { + virtual QVariant data(int column, int role) const; + struct dive* dive; QString displayDuration() const; QString displayDepth() const; QString displayTemperature() const; QString displayWeight() const; QString displaySac() const; - const QString diveLocation() const { return dive->location; } - const QString diveSuit() const { return dive->suit; } - DiveItem *parent() const { return parentItem; } - const QList& children() const { return childlist; } + int weight() const; - void addChild(DiveItem* item) { - item->parentItem = this; - childlist.push_back(item); - } /* parent = self */ - -private: - struct dive *dive; - DiveItem *parentItem; - QList childlist; }; - -DiveItem::DiveItem(struct dive *d, DiveItem *p): - dive(d), - parentItem(p) +QVariant DiveItem::data(int column, int role) const { - if (parentItem) - parentItem->addChild(this); + QVariant retVal; + + if (role == Qt::TextAlignmentRole) { + switch (column) { + case DATE: /* fall through */ + case SUIT: /* fall through */ + case LOCATION: + retVal = Qt::AlignLeft; + break; + default: + retVal = Qt::AlignRight; + break; + } + } + + if (role == Qt::DisplayRole) { + switch (column) { + case NR: + retVal = dive->number; + break; + case DATE: + retVal = QString(get_dive_date_string(dive->when)); + break; + case DEPTH: + retVal = displayDepth(); + break; + case DURATION: + retVal = displayDuration(); + break; + case TEMPERATURE: + retVal = displayTemperature(); + break; + case TOTALWEIGHT: + retVal = displayWeight(); + break; + case SUIT: + retVal = QString(dive->suit); + break; + case SAC: + retVal = displaySac(); + break; + case OTU: + retVal = dive->otu; + break; + case MAXCNS: + retVal = dive->maxcns; + break; + case LOCATION: + retVal = QString(dive->location); + break; + case RATING: + retVal = dive->rating; + break; + } + } + return retVal; } QString DiveItem::displayDepth() const @@ -361,7 +465,6 @@ QString DiveItem::displayDepth() const QString DiveItem::displayDuration() const { int hrs, mins, secs; - secs = dive->duration.seconds % 60; mins = dive->duration.seconds / 60; hrs = mins / 60; @@ -405,215 +508,147 @@ QString DiveItem::displayWeight() const QString str; if (get_units()->weight == units::KG) { - int gr = diveWeight() % 1000; - int kg = diveWeight() / 1000; + int gr = weight() % 1000; + int kg = weight() / 1000; str = QString("%1.%2").arg(kg).arg((unsigned)(gr + 500) / 100); } else { - str = QString("%1").arg((unsigned)(grams_to_lbs(diveWeight()) + 0.5)); + str = QString("%1").arg((unsigned)(grams_to_lbs(weight()) + 0.5)); } + return str; } -DiveTripModel::DiveTripModel(QObject *parent) : QAbstractItemModel(parent) +int DiveItem::weight() const { - rootItem = new DiveItem; - int i; - struct dive *d; - - for_each_dive(i, d) { - new DiveItem(d, rootItem); - } + weight_t tw = { total_weight(dive) }; + return tw.grams; } -Qt::ItemFlags DiveTripModel::flags(const QModelIndex &index) const +DiveTripModel::DiveTripModel(QObject* parent) + : QAbstractItemModel(parent) { - Qt::ItemFlags diveFlags = QAbstractItemModel::flags(index); - if (index.isValid()) { - diveFlags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled; - } - return diveFlags; + rootItem = new TreeItemDT(); + setupModelData(); } +DiveTripModel::~DiveTripModel() +{ + delete rootItem; +} -QVariant DiveTripModel::data(const QModelIndex &index, int role) const +int DiveTripModel::columnCount(const QModelIndex& parent) const +{ + if (parent.isValid()) + return static_cast(parent.internalPointer())->columnCount(); + else + return rootItem->columnCount(); +} + +QVariant DiveTripModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); - DiveItem *item = static_cast(index.internalPointer()); + if (role != Qt::DisplayRole) + return QVariant(); - QVariant retVal; - if (role == Qt::TextAlignmentRole) { - switch (index.column()) { - case DATE: /* fall through */ - case SUIT: /* fall through */ - case LOCATION: - retVal = Qt::AlignLeft; - break; - default: - retVal = Qt::AlignRight; - break; - } - } - if (role == Qt::DisplayRole) { - switch (index.column()) { - case NR: - retVal = item->diveNumber(); - break; - case DATE: - retVal = item->diveDateTime(); - break; - case DEPTH: - retVal = item->displayDepth(); - break; - case DURATION: - retVal = item->displayDuration(); - break; - case TEMPERATURE: - retVal = item->displayTemperature(); - break; - case TOTALWEIGHT: - retVal = item->displayWeight(); - break; - case SUIT: - retVal = item->diveSuit(); - break; - case SAC: - retVal = item->displaySac(); - break; - case OTU: - retVal = item->diveOtu(); - break; - case MAXCNS: - retVal = item->diveMaxcns(); - break; - case LOCATION: - retVal = item->diveLocation(); - break; - case RATING: - retVal = item->diveRating(); - break; - } - } - return retVal; + TreeItemDT* item = static_cast(index.internalPointer()); + + return item->data(index.column(), role); } - -QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int role) const +Qt::ItemFlags DiveTripModel::flags(const QModelIndex& index) const { - QVariant ret; - if (orientation != Qt::Horizontal) - return ret; - - if (role == Qt::DisplayRole) { - switch(section) { - case NR: - ret = tr("#"); - break; - case DATE: - ret = tr("Date"); - break; - case RATING: - ret = UTF8_BLACKSTAR; - break; - case DEPTH: - if (get_units()->length == units::METERS) - ret = tr("m"); - else - ret = tr("ft"); - break; - case DURATION: - ret = tr("min"); - break; - case TEMPERATURE: - if (get_units()->temperature == units::CELSIUS) - ret = QString("%1%2").arg(UTF8_DEGREE).arg("C"); - else - ret = QString("%1%2").arg(UTF8_DEGREE).arg("F"); - break; - case TOTALWEIGHT: - if (get_units()->weight == units::KG) - ret = tr("kg"); - else - ret = tr("lbs"); - break; - case SUIT: - ret = tr("Suit"); - break; - case CYLINDER: - ret = tr("Cyl"); - break; - case NITROX: - ret = QString("O%1%").arg(UTF8_SUBSCRIPT_2); - break; - case SAC: - ret = tr("SAC"); - break; - case OTU: - ret = tr("OTU"); - break; - case MAXCNS: - ret = tr("maxCNS"); - break; - case LOCATION: - ret = tr("Location"); - break; - } - } - return ret; -} - -int DiveTripModel::rowCount(const QModelIndex &parent) const -{ - /* only allow kids in column 0 */ - if (parent.isValid() && parent.column() > 0) + if (!index.isValid()) return 0; - DiveItem *item = itemForIndex(parent); - return item ? item->children().count() : 0; + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } -int DiveTripModel::columnCount(const QModelIndex &parent) const +QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, + int role) const { - return parent.isValid() && parent.column() != 0 ? 0 : COLUMNS; + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + return rootItem->data(section, role); + + return QVariant(); } - -QModelIndex DiveTripModel::index(int row, int column, const QModelIndex &parent) const +QModelIndex DiveTripModel::index(int row, int column, const QModelIndex& parent) +const { - - if (!rootItem || row < 0 || column < 0 || column >= COLUMNS || - (parent.isValid() && parent.column() != 0)) + if (!hasIndex(row, column, parent)) return QModelIndex(); - DiveItem *parentItem = itemForIndex(parent); - Q_ASSERT(parentItem); - if (DiveItem *item = parentItem->children().at(row)) - return createIndex(row, column, item); - return QModelIndex(); + TreeItemDT* parentItem = (!parent.isValid()) ? rootItem + : static_cast(parent.internalPointer()); + + TreeItemDT* childItem = parentItem->childs[row]; + + return (childItem) ? createIndex(row, column, childItem) + : QModelIndex(); } - -QModelIndex DiveTripModel::parent(const QModelIndex &childIndex) const +QModelIndex DiveTripModel::parent(const QModelIndex& index) const { - if (!childIndex.isValid()) + if (!index.isValid()) return QModelIndex(); - DiveItem *child = static_cast(childIndex.internalPointer()); - DiveItem *parent = child->parent(); + TreeItemDT* childItem = static_cast(index.internalPointer()); + TreeItemDT* parentItem = childItem->parent; - if (parent == rootItem) + if (parentItem == rootItem) return QModelIndex(); - return createIndex(parent->children().indexOf(child), 0, parent); + return createIndex(parentItem->row(), 0, parentItem); } - -DiveItem* DiveTripModel::itemForIndex(const QModelIndex &index) const +int DiveTripModel::rowCount(const QModelIndex& parent) const { - if (index.isValid()) { - DiveItem *item = static_cast(index.internalPointer()); - return item; + TreeItemDT* parentItem; + + if (parent.column() > 0) { + return 0; + } + + if (!parent.isValid()) + parentItem = rootItem; + + else + parentItem = static_cast(parent.internalPointer()); + + return parentItem->childs.count(); +} + +void DiveTripModel::setupModelData() +{ + int i = dive_table.nr; + + while (--i >= 0) { + struct dive* dive = get_dive(i); + dive_trip_t* trip = dive->divetrip; + + DiveItem* diveItem = new DiveItem(); + diveItem->dive = dive; + + if (!trip) { + diveItem->parent = rootItem; + rootItem->childs.push_back(diveItem); + continue; + } + + if (!trips.keys().contains(trip)) { + TripItem* tripItem = new TripItem(); + tripItem->trip = trip; + tripItem->parent = rootItem; + tripItem->childs.push_back(diveItem); + trips[trip] = tripItem; + rootItem->childs.push_back(tripItem); + continue; + } + + TripItem* tripItem = trips[trip]; + tripItem->childs.push_back(diveItem); } - return rootItem; } diff --git a/qt-ui/models.h b/qt-ui/models.h index 9e4666dc7..7bc7578b8 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -8,8 +8,11 @@ #define MODELS_H #include +#include + #include "../dive.h" #include "../divelist.h" + /* Encapsulates the tank_info global variable * to show on Qt`s Model View System.*/ class TankInfoModel : public QAbstractTableModel { @@ -74,25 +77,49 @@ private: /*! An AbstractItemModel for recording dive trip information such as a list of dives. * */ -class DiveItem; -class DiveTripModel : public QAbstractItemModel -{ + + + +struct TreeItemDT { + Q_DECLARE_TR_FUNCTIONS ( TreeItemDT ); public: enum Column {NR, DATE, RATING, DEPTH, DURATION, TEMPERATURE, TOTALWEIGHT, SUIT, CYLINDER, NITROX, SAC, OTU, MAXCNS, LOCATION, COLUMNS }; + virtual ~TreeItemDT(); + int columnCount() const { + return COLUMNS; + }; + virtual QVariant data ( int column, int role ) const; + int row() const; + QList childs; + TreeItemDT *parent; +}; + + +struct TripItem; + +class DiveTripModel : public QAbstractItemModel +{ + Q_OBJECT + +public: DiveTripModel(QObject *parent = 0); + ~DiveTripModel(); /*reimp*/ Qt::ItemFlags flags(const QModelIndex &index) const; /*reimp*/ QVariant data(const QModelIndex &index, int role) const; - /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role) const; - /*reimp*/ int rowCount(const QModelIndex &parent) const; + /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + /*reimp*/ int rowCount(const QModelIndex &parent = QModelIndex()) const; /*reimp*/ int columnCount(const QModelIndex &parent = QModelIndex()) const; /*reimp*/ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; /*reimp*/ QModelIndex parent(const QModelIndex &child) const; + private: - DiveItem *itemForIndex(const QModelIndex& index) const; - DiveItem *rootItem; + void setupModelData(); + + TreeItemDT *rootItem; + QMap trips; }; #endif From 6b7797140bcf706b1a0a2eeb70438d8572372b9f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 1 May 2013 22:00:08 -0700 Subject: [PATCH 103/226] Minor style updates Signed-off-by: Dirk Hohndel --- qt-ui/modeldelegates.cpp | 6 ++--- qt-ui/models.cpp | 53 +++++++++++++++++----------------------- qt-ui/models.h | 4 --- 3 files changed, 25 insertions(+), 38 deletions(-) diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 00f10092d..87629bd1b 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -10,15 +10,13 @@ void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - if (!index.isValid()){ + if (!index.isValid()) return; - } QVariant value = index.model()->data(index, Qt::DisplayRole); - if (!value.isValid()){ + if (!value.isValid()) return; - } int rating = value.toInt(); diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 6d9bbad39..dd2cf2f84 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -17,9 +17,8 @@ CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent) QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant ret; - if (orientation == Qt::Vertical) { + if (orientation == Qt::Vertical) return ret; - } if (role == Qt::DisplayRole) { switch(section) { @@ -57,9 +56,9 @@ int CylindersModel::columnCount(const QModelIndex& parent) const QVariant CylindersModel::data(const QModelIndex& index, int role) const { QVariant ret; - if (!index.isValid() || index.row() >= MAX_CYLINDERS) { + if (!index.isValid() || index.row() >= MAX_CYLINDERS) return ret; - } + cylinder_t& cyl = current_dive->cylinder[index.row()]; if (role == Qt::DisplayRole) { @@ -152,9 +151,9 @@ int WeightModel::columnCount(const QModelIndex& parent) const QVariant WeightModel::data(const QModelIndex& index, int role) const { QVariant ret; - if (!index.isValid() || index.row() >= MAX_WEIGHTSYSTEMS) { + if (!index.isValid() || index.row() >= MAX_WEIGHTSYSTEMS) return ret; - } + weightsystem_t *ws = ¤t_dive->weightsystem[index.row()]; if (role == Qt::DisplayRole) { @@ -184,9 +183,8 @@ int WeightModel::rowCount(const QModelIndex& parent) const QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant ret; - if (orientation == Qt::Vertical) { + if (orientation == Qt::Vertical) return ret; - } if (role == Qt::DisplayRole) { switch(section) { @@ -257,9 +255,11 @@ QVariant TankInfoModel::data(const QModelIndex& index, int role) const } if (role == Qt::DisplayRole) { switch(index.column()) { - case BAR: ret = bar; + case BAR: + ret = bar; break; - case ML: ret = ml; + case ML: + ret = ml; break; case DESCRIPTION: ret = QString(info->name); @@ -300,7 +300,7 @@ int TankInfoModel::rowCount(const QModelIndex& parent) const TankInfoModel::TankInfoModel() : QAbstractTableModel(), rows(-1) { struct tank_info *info = tank_info; - for (info = tank_info ; info->name; info++, rows++); + for (info = tank_info; info->name; info++, rows++); if (rows > -1) { beginInsertRows(QModelIndex(), 0, rows); @@ -315,7 +315,7 @@ void TankInfoModel::update() endRemoveRows(); } struct tank_info *info = tank_info; - for (info = tank_info ; info->name; info++, rows++); + for (info = tank_info; info->name; info++, rows++); if (rows > -1) { beginInsertRows(QModelIndex(), 0, rows); @@ -426,7 +426,6 @@ struct DiveItem : public TreeItemDT { QString displayWeight() const; QString displaySac() const; int weight() const; - }; QVariant DiveItem::data(int column, int role) const @@ -524,11 +523,11 @@ QString DiveItem::displayTemperature() const { QString str; - if (get_units()->temperature == units::CELSIUS) { + if (get_units()->temperature == units::CELSIUS) str = QString::number(mkelvin_to_C(dive->watertemp.mkelvin), 'f', 1); - } else { + else str = QString::number(mkelvin_to_F(dive->watertemp.mkelvin), 'f', 1); - } + return str; } @@ -536,11 +535,11 @@ QString DiveItem::displaySac() const { QString str; - if (get_units()->volume == units::LITER) { + if (get_units()->volume == units::LITER) str = QString::number(dive->sac / 1000, 'f', 1); - } else { + else str = QString::number(ml_to_cuft(dive->sac), 'f', 2); - } + return str; } @@ -566,8 +565,8 @@ int DiveItem::weight() const } -DiveTripModel::DiveTripModel(QObject* parent) - : QAbstractItemModel(parent) +DiveTripModel::DiveTripModel(QObject* parent) : + QAbstractItemModel(parent) { rootItem = new TreeItemDT(); setupModelData(); @@ -622,13 +621,11 @@ const if (!hasIndex(row, column, parent)) return QModelIndex(); - TreeItemDT* parentItem = (!parent.isValid()) ? rootItem - : static_cast(parent.internalPointer()); + TreeItemDT* parentItem = (!parent.isValid()) ? rootItem : static_cast(parent.internalPointer()); TreeItemDT* childItem = parentItem->childs[row]; - return (childItem) ? createIndex(row, column, childItem) - : QModelIndex(); + return (childItem) ? createIndex(row, column, childItem) : QModelIndex(); } QModelIndex DiveTripModel::parent(const QModelIndex& index) const @@ -649,13 +646,11 @@ int DiveTripModel::rowCount(const QModelIndex& parent) const { TreeItemDT* parentItem; - if (parent.column() > 0) { + if (parent.column() > 0) return 0; - } if (!parent.isValid()) parentItem = rootItem; - else parentItem = static_cast(parent.internalPointer()); @@ -678,7 +673,6 @@ void DiveTripModel::setupModelData() rootItem->childs.push_back(diveItem); continue; } - if (!trips.keys().contains(trip)) { TripItem* tripItem = new TripItem(); tripItem->trip = trip; @@ -688,7 +682,6 @@ void DiveTripModel::setupModelData() rootItem->childs.push_back(tripItem); continue; } - TripItem* tripItem = trips[trip]; tripItem->childs.push_back(diveItem); } diff --git a/qt-ui/models.h b/qt-ui/models.h index 52a3498df..d063874d3 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -79,8 +79,6 @@ private: * */ - - struct TreeItemDT { Q_DECLARE_TR_FUNCTIONS ( TreeItemDT ); public: @@ -96,7 +94,6 @@ public: TreeItemDT *parent; }; - struct TripItem; class DiveTripModel : public QAbstractItemModel @@ -115,7 +112,6 @@ public: /*reimp*/ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; /*reimp*/ QModelIndex parent(const QModelIndex &child) const; - private: void setupModelData(); From e6ec626c9758d30c3f714ddbf6f79195e710e037 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 2 May 2013 09:33:51 -0700 Subject: [PATCH 104/226] Show trip date (with number of dives) in dive/trip list That was easy :-) Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index dd2cf2f84..12a0398a0 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -404,13 +404,15 @@ QVariant TripItem::data(int column, int role) const { QVariant ret; - if (column != LOCATION) { - return ret; - } - - switch (role) { - case Qt::DisplayRole: - ret = QString(trip->location); + if (role == Qt::DisplayRole) { + switch (column) { + case LOCATION: + ret = QString(trip->location); + break; + case DATE: + ret = QString(get_trip_date_string(trip->when, trip->nrdives)); + break; + } } return ret; From 63c50805610c8d0dd0ec85dc62b3007df4e5dc0b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 2 May 2013 10:38:34 -0700 Subject: [PATCH 105/226] Move gas string computation into core logic code This is now a helper called from the Gtk UI code (and will soon be used by the Qt UI code). Signed-off-by: Dirk Hohndel --- divelist-gtk.c | 32 ++++++++------------------------ divelist.c | 28 ++++++++++++++++++++++++++++ divelist.h | 1 + 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/divelist-gtk.c b/divelist-gtk.c index a5d70f348..18ef0d122 100644 --- a/divelist-gtk.c +++ b/divelist-gtk.c @@ -431,40 +431,24 @@ static gint nitrox_sort_func(GtkTreeModel *model, return a_he - b_he; } -#define UTF8_ELLIPSIS "\xE2\x80\xA6" - static void nitrox_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { - int idx, o2, he, o2low; - char buffer[80]; + int idx; + char *buffer; struct dive *dive; gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - if (idx < 0) { - *buffer = '\0'; - goto exit; + if (idx >= 0 && (dive = get_dive(idx))) { + buffer = get_nitrox_string(dive); + g_object_set(renderer, "text", buffer, NULL); + free(buffer); + } else { + g_object_set(renderer, "text", "", NULL); } - dive = get_dive(idx); - get_dive_gas(dive, &o2, &he, &o2low); - o2 = (o2 + 5) / 10; - he = (he + 5) / 10; - o2low = (o2low + 5) / 10; - - if (he) - snprintf(buffer, sizeof(buffer), "%d/%d", o2, he); - else if (o2) - if (o2 == o2low) - snprintf(buffer, sizeof(buffer), "%d", o2); - else - snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2); - else - strcpy(buffer, _("air")); -exit: - g_object_set(renderer, "text", buffer, NULL); } /* Render the SAC data (integer value of "ml / min") */ diff --git a/divelist.c b/divelist.c index e34f7afeb..58d89d5f6 100644 --- a/divelist.c +++ b/divelist.c @@ -595,6 +595,34 @@ char *get_trip_date_string(timestamp_t when, int nr) { return buffer; } +#define MAX_NITROX_STRING 80 +#define UTF8_ELLIPSIS "\xE2\x80\xA6" + +/* callers needs to free the string */ +char *get_nitrox_string(struct dive *dive) +{ + int o2, he, o2low; + char *buffer = malloc(MAX_NITROX_STRING); + + if (buffer) { + get_dive_gas(dive, &o2, &he, &o2low); + o2 = (o2 + 5) / 10; + he = (he + 5) / 10; + o2low = (o2low + 5) / 10; + + if (he) + snprintf(buffer, sizeof(buffer), "%d/%d", o2, he); + else if (o2) + if (o2 == o2low) + snprintf(buffer, sizeof(buffer), "%d", o2); + else + snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2); + else + strcpy(buffer, _("air")); + } + return buffer; +} + /* * helper functions for dive_trip handling */ diff --git a/divelist.h b/divelist.h index f3054d213..d17ea7c12 100644 --- a/divelist.h +++ b/divelist.h @@ -32,6 +32,7 @@ extern void export_all_dives_uddf_cb(); extern void process_dives(bool imported, bool prefer_imported); extern char *get_dive_date_string(timestamp_t when); extern char *get_trip_date_string(timestamp_t when, int nr); +extern char *get_nitrox_string(struct dive *dive); extern void clear_trip_indexes(void); extern dive_trip_t *find_trip_by_idx(int idx); extern int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b); From f9c97ff97d072d6a4cb934a50427dc69382281e0 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 2 May 2013 10:48:09 -0700 Subject: [PATCH 106/226] Show cylinder and gas used in the Qt dive list Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 12a0398a0..68f575ec8 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -470,6 +470,12 @@ QVariant DiveItem::data(int column, int role) const case SUIT: retVal = QString(dive->suit); break; + case CYLINDER: + retVal = QString(dive->cylinder[0].type.description); + break; + case NITROX: + retVal = QString(get_nitrox_string(dive)); + break; case SAC: retVal = displaySac(); break; From c4e2c322a3f6505880b9330e577129e0110ed2ba Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 2 May 2013 14:05:53 -0700 Subject: [PATCH 107/226] Set better column widths in the dive list This code seems rather crude to me. I'm sure this could be done better. This also makes the column alignment work again. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 34 ++++++++++++++++++++++++++++++++++ qt-ui/models.cpp | 11 +++++------ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 9901e4186..8bf03531f 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "divelistview.h" #include "starwidget.h" @@ -32,6 +34,38 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), sortModel->setSourceModel(model); ui->ListWidget->setModel(sortModel); + /* figure out appropriate widths for the columns. The strings chosen + * are somewhat random (but at least we're trying to allow them to be + * localized so they are somewhat universal) */ + QFontMetrics fm(QApplication::font()); + int pixelsWide = fm.width(tr("Trip Wed, Mar 29, 2000 (100 dives)")); + ui->ListWidget->setColumnWidth(TreeItemDT::DATE, pixelsWide); + + /* all the columns that have usually up to four numbers plus maybe + * a decimal separator */ + pixelsWide = fm.width("000.0"); + ui->ListWidget->setColumnWidth(TreeItemDT::DEPTH, pixelsWide); + ui->ListWidget->setColumnWidth(TreeItemDT::TEMPERATURE, pixelsWide); + ui->ListWidget->setColumnWidth(TreeItemDT::TOTALWEIGHT, pixelsWide); + ui->ListWidget->setColumnWidth(TreeItemDT::SAC, pixelsWide); + ui->ListWidget->setColumnWidth(TreeItemDT::OTU, pixelsWide); + + /* this one is likely dominated by the header (need extra pixels) */ + pixelsWide = fm.width(tr("maxCNS")) + 10; + ui->ListWidget->setColumnWidth(TreeItemDT::MAXCNS, pixelsWide); + + /* the rest we try to cover with reasonable sample text again */ + pixelsWide = fm.width(" 123456"); + ui->ListWidget->setColumnWidth(TreeItemDT::NR, pixelsWide); + pixelsWide = fm.width("00:00:00"); + ui->ListWidget->setColumnWidth(TreeItemDT::DURATION, pixelsWide); + pixelsWide = fm.width(tr("twin HP119")); + ui->ListWidget->setColumnWidth(TreeItemDT::CYLINDER, pixelsWide); + pixelsWide = fm.width("888888"); + ui->ListWidget->setColumnWidth(TreeItemDT::NITROX, pixelsWide); + pixelsWide = fm.width(tr("7mm wet, farmer johns and jacket")); + ui->ListWidget->setColumnWidth(TreeItemDT::SUIT, pixelsWide); + setWindowIcon(QIcon(":subsurface-icon")); readSettings(); } diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 68f575ec8..5c7fc7a8b 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -434,7 +434,8 @@ QVariant DiveItem::data(int column, int role) const { QVariant retVal; - if (role == Qt::TextAlignmentRole) { + switch (role) { + case Qt::TextAlignmentRole: switch (column) { case DATE: /* fall through */ case SUIT: /* fall through */ @@ -445,9 +446,8 @@ QVariant DiveItem::data(int column, int role) const retVal = Qt::AlignRight; break; } - } - - if (role == Qt::DisplayRole) { + break; + case Qt::DisplayRole: switch (column) { case NR: retVal = dive->number; @@ -492,6 +492,7 @@ QVariant DiveItem::data(int column, int role) const retVal = dive->rating; break; } + break; } return retVal; } @@ -598,8 +599,6 @@ QVariant DiveTripModel::data(const QModelIndex& index, int role) const if (!index.isValid()) return QVariant(); - if (role != Qt::DisplayRole) - return QVariant(); TreeItemDT* item = static_cast(index.internalPointer()); From 021ef8ad09295a0ad4b5a3450a651637eea8672d Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 2 May 2013 14:19:15 -0700 Subject: [PATCH 108/226] Alternate background colors for dive list This was written with massive hand-holding by Tomaz - actually, this was mostly proposed via IRC by Tomaz and then implemented by me... Right now because of the list-of-lists nature of the model we have the small issue that every trip starts with a dark background dive, even if the trip itself has a dark background. Signed-off-by: Dirk Hohndel --- qt-ui/modeldelegates.cpp | 8 +++++--- qt-ui/models.cpp | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 87629bd1b..2adaaebc7 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -15,14 +15,16 @@ void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o QVariant value = index.model()->data(index, Qt::DisplayRole); + if(option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + else + painter->fillRect(option.rect, index.model()->data(index, Qt::BackgroundRole).value()); + if (!value.isValid()) return; int rating = value.toInt(); - if(option.state & QStyle::State_Selected) - painter->fillRect(option.rect, option.palette.highlight()); - painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 5c7fc7a8b..373770404 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -7,6 +7,8 @@ #include "models.h" #include #include +#include +#include extern struct tank_info tank_info[100]; @@ -599,6 +601,8 @@ QVariant DiveTripModel::data(const QModelIndex& index, int role) const if (!index.isValid()) return QVariant(); + if (role == Qt::BackgroundRole) + return QBrush(QColor(index.row() % 2 ? Qt::white : QColor(Qt::lightGray).lighter(120))); TreeItemDT* item = static_cast(index.internalPointer()); From 82b1b04920fdd5882e0cbf28c9871c2ddd404af8 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 2 May 2013 19:27:36 -0300 Subject: [PATCH 109/226] Test the CSS for styling the TableView This is a test and I shouldn't be taken seriously. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/divelistview.cpp | 2 + qt-ui/mainwindow.ui | 79 +++++++++++++++++++++++++++++++++++++++- qt-ui/modeldelegates.cpp | 14 ++++++- qt-ui/modeldelegates.h | 7 +++- qt-ui/models.cpp | 5 +-- 5 files changed, 99 insertions(+), 8 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index 45b6cf4d7..0bf0b35ba 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -7,6 +7,8 @@ #include "divelistview.h" #include "models.h" #include "modeldelegates.h" +#include +#include DiveListView::DiveListView(QWidget *parent) : QTreeView(parent) { diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 073476918..1d31f1cd3 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -28,9 +28,86 @@ + + QTreeView { + show-decoration-selected: 1; + } + + QTreeView::item { + border: 1px solid #d9d9d9; + border-top-color: transparent; + border-bottom-color: transparent; + padding: 2px; + } + + QTreeView::item:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1); + border: 1px solid #bfcde4; + } + + QTreeView::item:selected { + border: 1px solid #567dbc; + } + + QTreeView::item:selected:active{ + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6ea1f1, stop: 1 #567dbc); + } + + QTreeView::item:selected:!active { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6b9be8, stop: 1 #577fbf); + } + + +/* + QTreeView::branch { + background: palette(base); + } + + QTreeView::branch:has-siblings:!adjoins-item { + background: cyan; + } + + QTreeView::branch:has-siblings:adjoins-item { + background: red; + } + + QTreeView::branch:!has-children:!has-siblings:adjoins-item { + background: blue; + } + + QTreeView::branch:closed:has-children:has-siblings { + background: pink; + } + + QTreeView::branch:has-children:!has-siblings:closed { + background: gray; + } + + QTreeView::branch:open:has-children:has-siblings { + background: magenta; + } + + QTreeView::branch:open:has-children:!has-siblings { + background: green; + } +*/ + + + + true + + + true + true + + true + + + true + @@ -42,7 +119,7 @@ 0 0 763 - 20 + 19 diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 87629bd1b..0b3231583 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -7,9 +7,21 @@ #include #include #include +#include +#include +#include +#include + +StarWidgetsDelegate::StarWidgetsDelegate(QWidget* parent): + QStyledItemDelegate(parent), + parentWidget(parent) +{ + +} void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { + QStyledItemDelegate::paint(painter, option, index); if (!index.isValid()) return; @@ -20,8 +32,6 @@ void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o int rating = value.toInt(); - if(option.state & QStyle::State_Selected) - painter->fillRect(option.rect, option.palette.highlight()); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); diff --git a/qt-ui/modeldelegates.h b/qt-ui/modeldelegates.h index eacbb5a1e..5f90a3061 100644 --- a/qt-ui/modeldelegates.h +++ b/qt-ui/modeldelegates.h @@ -1,12 +1,15 @@ #ifndef MODELDELEGATES_H #define MODELDELEGATES_H -#include +#include -class StarWidgetsDelegate : public QAbstractItemDelegate { +class StarWidgetsDelegate : public QStyledItemDelegate { Q_OBJECT public: + explicit StarWidgetsDelegate(QWidget* parent = 0); virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; +private: + QWidget *parentWidget; }; #endif diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 68f575ec8..061ffe418 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -7,6 +7,8 @@ #include "models.h" #include #include +#include +#include extern struct tank_info tank_info[100]; @@ -598,9 +600,6 @@ QVariant DiveTripModel::data(const QModelIndex& index, int role) const if (!index.isValid()) return QVariant(); - if (role != Qt::DisplayRole) - return QVariant(); - TreeItemDT* item = static_cast(index.internalPointer()); return item->data(index.column(), role); From 696c9ccacd24392ea63477c5ec8a25d6649aedf7 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 2 May 2013 20:32:57 -0300 Subject: [PATCH 110/226] Added code to Select a dive, fixed minor annoyances. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- dive.c | 9 +++++++++ dive.h | 2 ++ qt-ui/mainwindow.cpp | 12 ++++++++++++ qt-ui/mainwindow.h | 3 +++ qt-ui/mainwindow.ui | 37 +++---------------------------------- qt-ui/modeldelegates.cpp | 3 ++- qt-ui/models.cpp | 12 +++++++++--- qt-ui/models.h | 6 +++++- 8 files changed, 45 insertions(+), 39 deletions(-) diff --git a/dive.c b/dive.c index 34025d68c..2c2307e41 100644 --- a/dive.c +++ b/dive.c @@ -1810,6 +1810,15 @@ struct dive *merge_dives(struct dive *a, struct dive *b, int offset, gboolean pr return res; } +int get_index_for_dive(struct dive *dive) { + int i; + struct dive *d; + for_each_dive(i, d) + if (d == dive) + return i; + return -1; +} + struct dive *find_dive_including(timestamp_t when) { int i; diff --git a/dive.h b/dive.h index f2153587f..c13cac0aa 100644 --- a/dive.h +++ b/dive.h @@ -350,6 +350,8 @@ struct dive { struct divecomputer dc; }; +extern int get_index_for_dive(struct dive *dive); + static inline int dive_has_gps_location(struct dive *dive) { return dive->latitude.udeg || dive->longitude.udeg; diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 9901e4186..ae2794e3c 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -23,6 +23,7 @@ #include "../divelist.h" #include "../pref.h" #include "modeldelegates.h" +#include "models.h" MainWindow::MainWindow() : ui(new Ui::MainWindow()), model(new DiveTripModel(this)), @@ -31,11 +32,22 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), ui->setupUi(this); sortModel->setSourceModel(model); ui->ListWidget->setModel(sortModel); + connect(ui->ListWidget, SIGNAL(activated(QModelIndex)), this, SLOT(diveSelected(QModelIndex))); setWindowIcon(QIcon(":subsurface-icon")); readSettings(); } +void MainWindow::diveSelected(const QModelIndex& index) +{ + struct dive *dive = (struct dive*) index.model()->data(index, TreeItemDT::DIVE_ROLE).value(); + + if (dive) + selected_dive = get_index_for_dive(dive); + + // Here should be the code to update the other widgets. +} + void MainWindow::on_actionNew_triggered() { qDebug("actionNew"); diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index eece91ade..e94cb5b7c 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -8,6 +8,7 @@ #define MAINWINDOW_H #include +#include class QSortFilterProxyModel; class DiveTripModel; @@ -66,6 +67,8 @@ private Q_SLOTS: void on_actionAboutSubsurface_triggered(); void on_actionUserManual_triggered(); + void diveSelected(const QModelIndex& index); + protected: void closeEvent(QCloseEvent *); diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 1d31f1cd3..fe97d98b4 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -57,45 +57,14 @@ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6b9be8, stop: 1 #577fbf); } - -/* - QTreeView::branch { - background: palette(base); - } - - QTreeView::branch:has-siblings:!adjoins-item { - background: cyan; - } - - QTreeView::branch:has-siblings:adjoins-item { - background: red; - } - - QTreeView::branch:!has-children:!has-siblings:adjoins-item { - background: blue; - } - - QTreeView::branch:closed:has-children:has-siblings { - background: pink; - } - - QTreeView::branch:has-children:!has-siblings:closed { - background: gray; - } - - QTreeView::branch:open:has-children:has-siblings { - background: magenta; - } - - QTreeView::branch:open:has-children:!has-siblings { - background: green; - } -*/ true + + QAbstractItemView::ExtendedSelection + true diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 0b3231583..33c1717e6 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -22,10 +22,11 @@ StarWidgetsDelegate::StarWidgetsDelegate(QWidget* parent): void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyledItemDelegate::paint(painter, option, index); + if (!index.isValid()) return; - QVariant value = index.model()->data(index, Qt::DisplayRole); + QVariant value = index.model()->data(index, TreeItemDT::STAR_ROLE); if (!value.isValid()) return; diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 061ffe418..bdc3a7576 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -490,11 +490,17 @@ QVariant DiveItem::data(int column, int role) const case LOCATION: retVal = QString(dive->location); break; - case RATING: - retVal = dive->rating; - break; } } + + if(role == STAR_ROLE){ + retVal = dive->rating; + } + + if(role == DIVE_ROLE){ + retVal = QVariant::fromValue(dive); + } + return retVal; } diff --git a/qt-ui/models.h b/qt-ui/models.h index d063874d3..307cdf5c3 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -82,7 +82,11 @@ private: struct TreeItemDT { Q_DECLARE_TR_FUNCTIONS ( TreeItemDT ); public: - enum Column {NR, DATE, RATING, DEPTH, DURATION, TEMPERATURE, TOTALWEIGHT, SUIT, CYLINDER, NITROX, SAC, OTU, MAXCNS, LOCATION, COLUMNS }; + enum Column {NR, DATE, RATING, DEPTH, DURATION, TEMPERATURE, TOTALWEIGHT, + SUIT, CYLINDER, NITROX, SAC, OTU, MAXCNS, LOCATION, DIVE, COLUMNS }; + + enum ExtraRoles{STAR_ROLE = Qt::UserRole + 1, DIVE_ROLE}; + virtual ~TreeItemDT(); int columnCount() const { return COLUMNS; From a6b89b3254adf7c559fe6fb89367cc50c9f41900 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 2 May 2013 22:06:01 -0700 Subject: [PATCH 111/226] Don't hard-code column width. Auto-detect on first start and keep in settings afterwards. So if the user resizes them, Subsurface remembers the correct sizes. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 53 +++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index cdd33ff45..3193eb2a8 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -35,39 +35,6 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), sortModel->setSourceModel(model); ui->ListWidget->setModel(sortModel); connect(ui->ListWidget, SIGNAL(activated(QModelIndex)), this, SLOT(diveSelected(QModelIndex))); - - /* figure out appropriate widths for the columns. The strings chosen - * are somewhat random (but at least we're trying to allow them to be - * localized so they are somewhat universal) */ - QFontMetrics fm(QApplication::font()); - int pixelsWide = fm.width(tr("Trip Wed, Mar 29, 2000 (100 dives)")); - ui->ListWidget->setColumnWidth(TreeItemDT::DATE, pixelsWide); - - /* all the columns that have usually up to four numbers plus maybe - * a decimal separator */ - pixelsWide = fm.width("000.0"); - ui->ListWidget->setColumnWidth(TreeItemDT::DEPTH, pixelsWide); - ui->ListWidget->setColumnWidth(TreeItemDT::TEMPERATURE, pixelsWide); - ui->ListWidget->setColumnWidth(TreeItemDT::TOTALWEIGHT, pixelsWide); - ui->ListWidget->setColumnWidth(TreeItemDT::SAC, pixelsWide); - ui->ListWidget->setColumnWidth(TreeItemDT::OTU, pixelsWide); - - /* this one is likely dominated by the header (need extra pixels) */ - pixelsWide = fm.width(tr("maxCNS")) + 10; - ui->ListWidget->setColumnWidth(TreeItemDT::MAXCNS, pixelsWide); - - /* the rest we try to cover with reasonable sample text again */ - pixelsWide = fm.width(" 123456"); - ui->ListWidget->setColumnWidth(TreeItemDT::NR, pixelsWide); - pixelsWide = fm.width("00:00:00"); - ui->ListWidget->setColumnWidth(TreeItemDT::DURATION, pixelsWide); - pixelsWide = fm.width(tr("twin HP119")); - ui->ListWidget->setColumnWidth(TreeItemDT::CYLINDER, pixelsWide); - pixelsWide = fm.width("888888"); - ui->ListWidget->setColumnWidth(TreeItemDT::NITROX, pixelsWide); - pixelsWide = fm.width(tr("7mm wet, farmer johns and jacket")); - ui->ListWidget->setColumnWidth(TreeItemDT::SUIT, pixelsWide); - setWindowIcon(QIcon(":subsurface-icon")); readSettings(); } @@ -333,6 +300,8 @@ bool MainWindow::askSaveChanges() void MainWindow::readSettings() { + int i; + QSettings settings("hohndel.org","subsurface"); settings.beginGroup("MainWindow"); @@ -341,16 +310,34 @@ void MainWindow::readSettings() ui->mainSplitter->restoreState(settings.value("mainSplitter").toByteArray()); ui->infoProfileSplitter->restoreState(settings.value("infoProfileSplitter").toByteArray()); settings.endGroup(); + + settings.beginGroup("ListWidget"); + for (i = TreeItemDT::NR; i < TreeItemDT::COLUMNS; i++) { + QVariant width = settings.value(QString("colwidth%1").arg(i)); + if (width.isValid()) + ui->ListWidget->setColumnWidth(i, width.toInt()); + else + ui->ListWidget->resizeColumnToContents(i); + } + settings.endGroup(); } void MainWindow::writeSettings() { + int i; QSettings settings("hohndel.org","subsurface"); + settings.beginGroup("MainWindow"); settings.setValue("size",size()); settings.setValue("mainSplitter", ui->mainSplitter->saveState()); settings.setValue("infoProfileSplitter", ui->infoProfileSplitter->saveState()); settings.endGroup(); + + settings.beginGroup("ListWidget"); + for (i = TreeItemDT::NR; i < TreeItemDT::COLUMNS; i++) + settings.setValue(QString("colwidth%1").arg(i), ui->ListWidget->columnWidth(i)); + settings.endGroup(); + /* other groups here; avoid '/' and '\' in keys with setValue(...) please */ } From 8677721e85a344e29782cfc2ab838edb8da9215b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 11:04:51 -0700 Subject: [PATCH 112/226] Remove the majority of the Gtk related code - rip all Gtk code from qt-gui.cpp - don't compile Gtk specific files - don't link against Gtk libraries - don't compile modules we don't use at all (yet) - use #if USE_GTK_UI on the remaining files to disable Gtk related parts - disable the non-functional Cochran support while I'm at it Signed-off-by: Dirk Hohndel --- Makefile | 16 +- display-gtk.h | 6 +- display.h | 6 + divelist-gtk.c | 1 - divelist.c | 12 +- download-dialog.c | 11 +- equipment.c | 33 +- file.c | 2 + gtk-gui.c | 2550 ++++++++++++++++++++++++++++++++++++++++++ info-gtk.c | 8 + info.c | 15 +- libdivecomputer.c | 4 + libdivecomputer.h | 4 +- linux.c | 1 + main.c | 14 + planner.c | 2 + profile.c | 18 +- qt-gui.cpp | 2418 +-------------------------------------- qt-ui/mainwindow.cpp | 2 + uemis-downloader.c | 4 + webservice.c | 2 + 21 files changed, 2684 insertions(+), 2445 deletions(-) create mode 100644 gtk-gui.c diff --git a/Makefile b/Makefile index 1bb080363..4a13de5eb 100644 --- a/Makefile +++ b/Makefile @@ -42,33 +42,21 @@ HEADERS = \ SOURCES = \ - cochran.c \ deco.c \ device.c \ dive.c \ divelist.c \ - divelist-gtk.c \ download-dialog.c \ equipment.c \ file.c \ info.c \ - info-gtk.c \ - libdivecomputer.c \ main.c \ parse-xml.c \ - planner.c \ - planner-gtk.c \ prefs.c \ - print.c \ profile.c \ save-xml.c \ sha1.c \ - statistics.c \ - statistics-gtk.c \ time.c \ - uemis.c \ - uemis-downloader.c \ - webservice.c \ qt-gui.cpp \ qt-ui/addcylinderdialog.cpp \ qt-ui/addweightsystemdialog.cpp \ @@ -93,10 +81,12 @@ endif ifneq ($(strip $(LIBXSLT)),) EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS) endif +ifeq ($(USE_GTK_UI),1) ifneq ($(strip $(LIBOSMGPSMAP)),) SOURCES += gps.c EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS) endif +endif ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) SOURCES += linux.c @@ -118,7 +108,7 @@ else XSLTDIR = .\\xslt endif -LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ +LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) MSGLANGS=$(notdir $(wildcard po/*.po)) diff --git a/display-gtk.h b/display-gtk.h index ad286b0d3..736616b1a 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -39,15 +39,11 @@ extern void set_divelist_font(const char *); extern void update_screen(void); extern void download_dialog(GtkWidget *, gpointer); -extern int is_default_dive_computer_device(const char *); -extern int is_default_dive_computer(const char *, const char *); + extern void add_dive_cb(GtkWidget *, gpointer); extern void update_progressbar(progressbar_t *progress, double value); extern void update_progressbar_text(progressbar_t *progress, const char *text); -extern const char *default_dive_computer_vendor; -extern const char *default_dive_computer_product; -extern const char *default_dive_computer_device; // info.c enum { diff --git a/display.h b/display.h index 9e01c73a9..b1a034e88 100644 --- a/display.h +++ b/display.h @@ -69,6 +69,12 @@ extern char zoomed_plot, dc_number; extern unsigned int amount_selected; +extern int is_default_dive_computer_device(const char *); +extern int is_default_dive_computer(const char *, const char *); +extern const char *default_dive_computer_vendor; +extern const char *default_dive_computer_product; +extern const char *default_dive_computer_device; + #ifdef __cplusplus } #endif diff --git a/divelist-gtk.c b/divelist-gtk.c index 18ef0d122..0c26430c1 100644 --- a/divelist-gtk.c +++ b/divelist-gtk.c @@ -59,7 +59,6 @@ static struct DiveList dive_list; #define TREESTORE(_dl) GTK_TREE_STORE((_dl).treemodel) #define LISTSTORE(_dl) GTK_TREE_STORE((_dl).listmodel) -short autogroup = FALSE; static gboolean in_set_cursor = FALSE; static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); diff --git a/divelist.c b/divelist.c index 58d89d5f6..e22dd331a 100644 --- a/divelist.c +++ b/divelist.c @@ -54,6 +54,8 @@ static short dive_list_changed = FALSE; +short autogroup = FALSE; + dive_trip_t *dive_trip_list; unsigned int amount_selected; @@ -73,6 +75,13 @@ void dump_selection(void) } #endif +void set_autogroup(gboolean value) +{ + /* if we keep the UI paradigm, this needs to toggle + * the checkbox on the autogroup menu item */ + autogroup = value; +} + dive_trip_t *find_trip_by_idx(int idx) { dive_trip_t *trip = dive_trip_list; @@ -906,8 +915,9 @@ void merge_dive_index(int i, struct dive *a) add_single_dive(i, res); delete_single_dive(i+1); delete_single_dive(i+1); - +#if USE_GTK_UI dive_list_update_dives(); +#endif mark_divelist_changed(TRUE); } diff --git a/download-dialog.c b/download-dialog.c index 5d5c1de04..729f3a6b6 100644 --- a/download-dialog.c +++ b/download-dialog.c @@ -3,19 +3,23 @@ #include "dive.h" #include "divelist.h" #include "display.h" +#if USE_GTK_UI #include "display-gtk.h" #include "callbacks-gtk.h" +#endif #include "libdivecomputer.h" const char *default_dive_computer_vendor; const char *default_dive_computer_product; const char *default_dive_computer_device; +#if USE_GTK_UI static gboolean force_download; static gboolean prefer_downloaded; OPTIONCALLBACK(force_toggle, force_download) OPTIONCALLBACK(prefer_dl_toggle, prefer_downloaded) +#endif struct product { const char *product; @@ -38,6 +42,7 @@ struct mydescriptor { struct vendor *dc_list; +#if USE_GTK_UI static void render_dc_vendor(GtkCellLayout *cell, GtkCellRenderer *renderer, GtkTreeModel *model, @@ -63,6 +68,7 @@ static void render_dc_product(GtkCellLayout *cell, product = dc_descriptor_get_product(descriptor); g_object_set(renderer, "text", product, NULL); } +#endif int is_default_dive_computer(const char *vendor, const char *product) { @@ -105,6 +111,7 @@ static void set_default_dive_computer_device(const char *name) subsurface_set_conf("dive_computer_device", name); } +#if USE_GTK_UI static void dive_computer_selector_changed(GtkWidget *combo, gpointer data) { GtkWidget *import, *button; @@ -149,7 +156,7 @@ static GtkWidget *import_dive_computer(device_data_t *data, GtkDialog *dialog) gtk_box_pack_start(GTK_BOX(vbox), info, FALSE, FALSE, 0); return info; } - +#endif /* create a list of lists and keep the elements sorted */ static void add_dc(const char *vendor, const char *product, dc_descriptor_t *descriptor) @@ -195,6 +202,7 @@ static void add_dc(const char *vendor, const char *product, dc_descriptor_t *des pl->descriptor = descriptor; } +#if USE_GTK_UI /* fill the vendors and create and fill the respective product stores; return the longest product name * and also the indices of the default vendor / product */ static int fill_computer_list(GtkListStore *vendorstore, GtkListStore ***productstore, int *vendor_index, int *product_index) @@ -469,3 +477,4 @@ void update_progressbar_text(progressbar_t *progress, const char *text) { gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress->bar), text); } +#endif diff --git a/equipment.c b/equipment.c index d126b4327..93f26dab1 100644 --- a/equipment.c +++ b/equipment.c @@ -16,10 +16,14 @@ #include "dive.h" #include "display.h" +#if USE_GTK_UI #include "display-gtk.h" +#endif #include "divelist.h" #include "conversions.h" +#if USE_GTK_UI +#include "display-gtk.h" static GtkListStore *cylinder_model, *weightsystem_model; enum { @@ -69,6 +73,7 @@ struct ws_widget { GtkSpinButton *weight; int w_idx; }; +#endif /* USE_GTK_UI */ /* we want bar - so let's not use our unit functions */ int convert_pressure(int mbar, double *p) @@ -120,6 +125,7 @@ static int convert_weight(int grams, double *m) return decimals; } +#if USE_GTK_UI static void set_cylinder_description(struct cylinder_widget *cylinder, const char *desc) { set_active_text(cylinder->description, desc); @@ -451,6 +457,28 @@ static void show_weightsystem(weightsystem_t *ws, struct ws_widget *weightsystem set_weight_description(weightsystem_widget, desc); set_weight_weight_spinbutton(weightsystem_widget, ws->weight.grams); } +#else +/* placeholders for a few functions that we need to redesign for the Qt UI */ +void add_cylinder_description(cylinder_type_t *type) +{ + const char *desc; + + desc = type->description; + if (!desc) + return; + /* now do something with it... */ +} +void add_weightsystem_description(weightsystem_t *weightsystem) +{ + const char *desc; + + desc = weightsystem->description; + if (!desc) + return; + /* now do something with it... */ +} + +#endif /* USE_GTK_UI */ gboolean cylinder_nodata(cylinder_t *cyl) { @@ -515,6 +543,7 @@ gboolean weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2) return TRUE; } +#if USE_GTK_UI static void set_one_cylinder(void *_data, GtkListStore *model, GtkTreeIter *iter) { cylinder_t *cyl = _data; @@ -784,7 +813,7 @@ static void record_weightsystem_changes(weightsystem_t *ws, struct ws_widget *we ws->description = desc; add_weightsystem_type(desc, grams, &iter); } - +#endif /* USE_GTK_UI */ /* * We hardcode the most common standard cylinders, * we should pick up any other names from the dive @@ -833,6 +862,7 @@ struct tank_info tank_info[100] = { { NULL, } }; +#if USE_GTK_UI static void fill_tank_list(GtkListStore *store) { GtkTreeIter iter; @@ -1679,3 +1709,4 @@ void clear_equipment_widgets() gtk_list_store_clear(cylinder_list[W_IDX_PRIMARY].model); gtk_list_store_clear(weightsystem_list[W_IDX_PRIMARY].model); } +#endif /* USE_GTK_UI */ diff --git a/file.c b/file.c index 401bd5c36..350ad25e1 100644 --- a/file.c +++ b/file.c @@ -239,9 +239,11 @@ static int open_by_filename(const char *filename, const char *fmt, struct memblo if (!strcasecmp(fmt, "DLD")) return try_to_open_zip(filename, mem, error); +#if ONCE_COCHRAN_IS_SUPPORTED /* Truly nasty intentionally obfuscated Cochran Anal software */ if (!strcasecmp(fmt, "CAN")) return try_to_open_cochran(filename, mem, error); +#endif /* Cochran export comma-separated-value files */ if (!strcasecmp(fmt, "DPT")) diff --git a/gtk-gui.c b/gtk-gui.c new file mode 100644 index 000000000..389257817 --- /dev/null +++ b/gtk-gui.c @@ -0,0 +1,2550 @@ +/* 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 + */ +/* + * This is the former qt-gui.cpp - so it already contains some Qt related + * functions. It's renamed back to gtk-ui.c to keep the old Gtk code + * around for reference in case we still need it... all that has now been + * ripped out of qt-gui.cpp */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dive.h" +#include "divelist.h" +#include "display.h" +#include "display-gtk.h" +#include "callbacks-gtk.h" +#include "uemis.h" +#include "device.h" +#include "webservice.h" +#include "version.h" +#include "libdivecomputer.h" +#include "qt-ui/mainwindow.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if HAVE_OSM_GPS_MAP +#include +#endif + +class Translator: public QTranslator +{ + Q_OBJECT + +public: + Translator(QObject *parent = 0); + ~Translator() {} + + virtual QString translate(const char *context, const char *sourceText, + const char *disambiguation = NULL) const; +}; + +Translator::Translator(QObject *parent): + QTranslator(parent) +{ +} + +QString Translator::translate(const char *context, const char *sourceText, + const char *disambiguation) const +{ + return gettext(sourceText); +} + +static const GdkPixdata subsurface_icon_pixbuf = {}; + +GtkWidget *main_window; +GtkWidget *main_vbox; +GtkWidget *error_info_bar; +GtkWidget *error_label; +GtkWidget *vpane, *hpane; +GtkWidget *notebook; +static QApplication *application = NULL; + +int error_count; +const char *existing_filename; + +typedef enum { PANE_INFO, PANE_PROFILE, PANE_LIST, PANE_THREE } pane_conf_t; +static pane_conf_t pane_conf; + +static struct device_info *holdnicknames = NULL; +static GtkWidget *dive_profile_widget(void); +static void import_files(GtkWidget *, gpointer); + +static void remember_dc(const char *model, uint32_t deviceid, const char *nickname) +{ + struct device_info *nn_entry; + + nn_entry = create_device_info(model, deviceid); + if (!nn_entry) + return; + if (!nickname || !*nickname) { + nn_entry->nickname = NULL; + return; + } + nn_entry->nickname = strdup(nickname); +} + +static void remove_dc(const char *model, uint32_t deviceid) +{ + free(remove_device_info(model, deviceid)); +} + +static GtkWidget *dive_profile; + +GtkActionGroup *action_group; + +void repaint_dive(void) +{ + update_dive(current_dive); + if (dive_profile) + gtk_widget_queue_draw(dive_profile); +} + +static gboolean need_icon = TRUE; + +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) +{ + qDebug("Warning: Calling GTK-Specific Code."); + 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_text(GTK_LABEL(error_label), buffer); + } +} + +static GtkFileFilter *setup_filter(void) +{ + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.xml"); + gtk_file_filter_add_pattern(filter, "*.XML"); + gtk_file_filter_add_pattern(filter, "*.uddf"); + gtk_file_filter_add_pattern(filter, "*.UDDF"); + gtk_file_filter_add_pattern(filter, "*.udcf"); + gtk_file_filter_add_pattern(filter, "*.UDCF"); + gtk_file_filter_add_pattern(filter, "*.jlb"); + gtk_file_filter_add_pattern(filter, "*.JLB"); +#ifdef LIBZIP + gtk_file_filter_add_pattern(filter, "*.sde"); + gtk_file_filter_add_pattern(filter, "*.SDE"); + gtk_file_filter_add_pattern(filter, "*.dld"); + gtk_file_filter_add_pattern(filter, "*.DLD"); +#endif +#ifdef SQLITE3 + gtk_file_filter_add_pattern(filter, "*.DB"); + gtk_file_filter_add_pattern(filter, "*.db"); +#endif + + gtk_file_filter_add_mime_type(filter, "text/xml"); + gtk_file_filter_set_name(filter, _("XML file")); + + return filter; +} + +static void file_save_as(GtkWidget *w, gpointer data) +{ + GtkWidget *dialog; + char *filename = NULL; + char *current_file; + char *current_dir; + + dialog = gtk_file_chooser_dialog_new(_("Save File As"), + GTK_WINDOW(main_window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + + if (existing_filename) { + current_dir = g_path_get_dirname(existing_filename); + current_file = g_path_get_basename(existing_filename); + } else { + const char *current_default = prefs.default_filename; + current_dir = g_path_get_dirname(current_default); + current_file = g_path_get_basename(current_default); + } + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), current_dir); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), current_file); + + free(current_dir); + free(current_file); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + } + gtk_widget_destroy(dialog); + + if (filename){ + save_dives(filename); + set_filename(filename, TRUE); + g_free(filename); + mark_divelist_changed(FALSE); + } +} + +static void file_save(GtkWidget *w, gpointer data) +{ + const char *current_default; + + if (!existing_filename) + return file_save_as(w, data); + + current_default = prefs.default_filename; + if (strcmp(existing_filename, current_default) == 0) { + /* if we are using the default filename the directory + * that we are creating the file in may not exist */ + char *current_def_dir; + struct stat sb; + + current_def_dir = g_path_get_dirname(existing_filename); + if (stat(current_def_dir, &sb) != 0) { + g_mkdir(current_def_dir, S_IRWXU); + } + free(current_def_dir); + } + save_dives(existing_filename); + mark_divelist_changed(FALSE); +} + +static gboolean ask_save_changes() +{ + //WARNING: Porting to Qt + qDebug("This method is being ported to Qt, please, stop using it. "); + GtkWidget *dialog, *label, *content; + gboolean quit = TRUE; + dialog = gtk_dialog_new_with_buttons(_("Save Changes?"), + GTK_WINDOW(main_window), GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + GTK_STOCK_NO, GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + if (!existing_filename){ + label = gtk_label_new ( + _("You have unsaved changes\nWould you like to save those before closing the datafile?")); + } else { + char *label_text = (char*) malloc(sizeof(char) * + (strlen(_("You have unsaved changes to file: %s \nWould you like to save those before closing the datafile?")) + + strlen(existing_filename))); + sprintf(label_text, + _("You have unsaved changes to file: %s \nWould you like to save those before closing the datafile?"), + existing_filename); + label = gtk_label_new (label_text); + free(label_text); + } + gtk_container_add (GTK_CONTAINER (content), label); + gtk_widget_show_all (dialog); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); + gint outcode = gtk_dialog_run(GTK_DIALOG(dialog)); + if (outcode == GTK_RESPONSE_ACCEPT) { + file_save(NULL,NULL); + } else if (outcode == GTK_RESPONSE_CANCEL || outcode == GTK_RESPONSE_DELETE_EVENT) { + quit = FALSE; + } + gtk_widget_destroy(dialog); + return quit; +} + +static void file_close(GtkWidget *w, gpointer data) +{ + qDebug("Calling an already ported-to-qt Gtk method"); + if (unsaved_changes()) + if (ask_save_changes() == FALSE) + return; + + if (existing_filename) + free((void *)existing_filename); + existing_filename = NULL; + + /* free the dives and trips */ + while (dive_table.nr) + delete_single_dive(0); + mark_divelist_changed(FALSE); + + /* clear the selection and the statistics */ + selected_dive = 0; + process_selected_dives(); + clear_stats_widgets(); + clear_events(); + show_dive_stats(NULL); + + /* clear the equipment page */ + clear_equipment_widgets(); + + /* redraw the screen */ + dive_list_update_dives(); + show_dive_info(NULL); +} + +//##################################################################### +//###### ALREAADY PORTED TO Qt. DELETE ME WHEN NOT MORE USERFUL. # +//##################################################################### +static void file_open(GtkWidget *w, gpointer data) +{ + qDebug("Calling an already ported-to-qt Gtk method."); + GtkWidget *dialog; + GtkFileFilter *filter; + const char *current_default; + + 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); + current_default = prefs.default_filename; + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), current_default); + /* when opening the data file we should allow only one file to be chosen */ + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); + + filter = setup_filter(); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + GSList *fn_glist; + char *filename; + + /* first, close the existing file, if any, and forget its name */ + file_close(w, data); + free((void *)existing_filename); + existing_filename = NULL; + + /* we know there is only one filename */ + fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + + GError *error = NULL; + filename = (char *)fn_glist->data; + parse_file(filename, &error); + set_filename(filename, TRUE); + if (error != NULL) + { + report_error(error); + g_error_free(error); + error = NULL; + } + g_free(filename); + g_slist_free(fn_glist); + report_dives(FALSE, FALSE); + } + gtk_widget_destroy(dialog); +} + +void save_pane_position() +{ + gint vpane_position = gtk_paned_get_position(GTK_PANED(vpane)); + gint hpane_position = gtk_paned_get_position(GTK_PANED(hpane)); + gboolean is_maximized = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(main_window))) & + GDK_WINDOW_STATE_MAXIMIZED; + + if (pane_conf == PANE_THREE){ + if (is_maximized) { + subsurface_set_conf_int("vpane_position_maximized", vpane_position); + subsurface_set_conf_int("hpane_position_maximized", hpane_position); + } else { + subsurface_set_conf_int("vpane_position", vpane_position); + subsurface_set_conf_int("hpane_position", hpane_position); + } + } +} + +/* Since we want direct control of what set of parameters to retrive, this function + * has an input argument. */ +void restore_pane_position(gboolean maximized) +{ + if (maximized) { + gtk_paned_set_position(GTK_PANED(vpane), subsurface_get_conf_int("vpane_position_maximized")); + gtk_paned_set_position(GTK_PANED(hpane), subsurface_get_conf_int("hpane_position_maximized")); + } else { + gtk_paned_set_position(GTK_PANED(vpane), subsurface_get_conf_int("vpane_position")); + gtk_paned_set_position(GTK_PANED(hpane), subsurface_get_conf_int("hpane_position")); + } +} + +void save_window_geometry(void) +{ + /* GDK_GRAVITY_NORTH_WEST assumed ( it is the default ) */ + int window_width, window_height; + gboolean is_maximized; + + gtk_window_get_size(GTK_WINDOW(main_window), &window_width, &window_height); + is_maximized = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(main_window))) & + GDK_WINDOW_STATE_MAXIMIZED; + subsurface_set_conf_int("window_width", window_width); + subsurface_set_conf_int("window_height", window_height); + subsurface_set_conf_bool("window_maximized", is_maximized); + save_pane_position(); + subsurface_flush_conf(); +} + +void restore_window_geometry(void) +{ + int window_width, window_height; + gboolean is_maximized = subsurface_get_conf_bool("window_maximized") > 0; + + window_height = subsurface_get_conf_int("window_height"); + window_width = subsurface_get_conf_int("window_width"); + + window_height == -1 ? window_height = 300 : window_height; + window_width == -1 ? window_width = 700 : window_width; + + restore_pane_position(is_maximized); + /* don't resize the window if in maximized state */ + if (is_maximized) + gtk_window_maximize(GTK_WINDOW(main_window)); + else + gtk_window_resize(GTK_WINDOW(main_window), window_width, window_height); +} + +gboolean on_delete(GtkWidget* w, gpointer data) +{ + /* Make sure to flush any modified dive data */ + update_dive(NULL); + + gboolean quit = TRUE; + if (unsaved_changes()) + quit = ask_save_changes(); + + if (quit){ + save_window_geometry(); + return FALSE; /* go ahead, kill the program, we're good now */ + } else { + return TRUE; /* We are not leaving */ + } +} + +static void on_destroy(GtkWidget* w, gpointer data) +{ + dive_list_destroy(); + info_widget_destroy(); + gtk_main_quit(); +} + +/* This "window-state-event" callback will be called after the actual action, such + * as maximize or restore. This means that if you have methods here that check + * for the current window state, they will obtain the already updated state... */ +static gboolean on_state(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data) +{ + gint vpane_position, hpane_position; + if (event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN || + event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) + return TRUE; /* do nothing if the window is shown for the first time or minimized */ + if (pane_conf == PANE_THREE) { + hpane_position = gtk_paned_get_position(GTK_PANED(hpane)); + vpane_position = gtk_paned_get_position(GTK_PANED(vpane)); + if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { /* maximize */ + subsurface_set_conf_int("vpane_position", vpane_position); + subsurface_set_conf_int("hpane_position", hpane_position); + restore_pane_position(TRUE); + } else if (event->new_window_state == 0) { /* restore */ + subsurface_set_conf_int("vpane_position_maximized", vpane_position); + subsurface_set_conf_int("hpane_position_maximized", hpane_position); + restore_pane_position(FALSE); + } + } + return TRUE; +} + +static void quit(GtkWidget *w, gpointer data) +{ + /* Make sure to flush any modified dive data */ + update_dive(NULL); + + gboolean quit = TRUE; + if (unsaved_changes()) + quit = ask_save_changes(); + + if (quit){ + save_window_geometry(); + dive_list_destroy(); + gtk_main_quit(); + } +} + +GtkTreeViewColumn *tree_view_column_add_pixbuf(GtkWidget *tree_view, data_func_t data_func, GtkTreeViewColumn *col) +{ + GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(col, renderer, FALSE); + gtk_tree_view_column_set_cell_data_func(col, renderer, data_func, NULL, NULL); + g_signal_connect(tree_view, "button-press-event", G_CALLBACK(icon_click_cb), col); + return col; +} + +GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, + data_func_t data_func, unsigned int flags) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *col; + double xalign = 0.0; /* left as default */ + PangoAlignment align; + gboolean visible; + + align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT : + (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT : + PANGO_ALIGN_CENTER; + visible = !(flags & INVISIBLE); + + renderer = gtk_cell_renderer_text_new(); + col = gtk_tree_view_column_new(); + + if (flags & EDITABLE) { + g_object_set(renderer, "editable", TRUE, NULL); + g_signal_connect(renderer, "edited", (GCallback) data_func, tree_view); + data_func = NULL; + } + + gtk_tree_view_column_set_title(col, title); + if (!(flags & UNSORTABLE)) + gtk_tree_view_column_set_sort_column_id(col, index); + gtk_tree_view_column_set_resizable(col, TRUE); + /* all but one column have only one renderer - so packing from the end + * makes no difference; for the location column we want to be able to + * prepend the icon in front of the text - so this works perfectly */ + gtk_tree_view_column_pack_end(col, renderer, TRUE); + if (data_func) + gtk_tree_view_column_set_cell_data_func(col, renderer, data_func, (void *)(long)index, NULL); + else + gtk_tree_view_column_add_attribute(col, renderer, "text", index); + switch (align) { + case PANGO_ALIGN_LEFT: + xalign = 0.0; + break; + case PANGO_ALIGN_CENTER: + xalign = 0.5; + break; + case PANGO_ALIGN_RIGHT: + xalign = 1.0; + break; + } + gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5); + gtk_tree_view_column_set_visible(col, visible); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col); + return col; +} + +/* Helper functions for gtk combo boxes */ +GtkEntry *get_entry(GtkComboBox *combo_box) +{ + return GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_box))); +} + +const char *get_active_text(GtkComboBox *combo_box) +{ + return gtk_entry_get_text(get_entry(combo_box)); +} + +void set_active_text(GtkComboBox *combo_box, const char *text) +{ + gtk_entry_set_text(get_entry(combo_box), text); +} + +GtkWidget *combo_box_with_model_and_entry(GtkListStore *model) +{ + GtkWidget *widget; + GtkEntryCompletion *completion; + +#if GTK_CHECK_VERSION(2,24,0) + widget = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model)); + gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(widget), 0); +#else + widget = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0); + gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(widget), 0); +#endif + + completion = gtk_entry_completion_new(); + gtk_entry_completion_set_text_column(completion, 0); + gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(model)); + gtk_entry_completion_set_inline_completion(completion, TRUE); + gtk_entry_completion_set_inline_selection(completion, TRUE); + gtk_entry_completion_set_popup_single_match(completion, FALSE); + gtk_entry_set_completion(get_entry(GTK_COMBO_BOX(widget)), completion); + g_object_unref(completion); + + return widget; +} + +static void create_radio(GtkWidget *vbox, const char *w_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(w_name); + gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0); + + va_start(args, w_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); +} + +void update_screen() +{ + update_dive_list_units(); + repaint_dive(); + update_dive_list_col_visibility(); +} + +UNITCALLBACK(set_meter, length, units::METERS) +UNITCALLBACK(set_feet, length, units::FEET) +UNITCALLBACK(set_bar, pressure, units::BAR) +UNITCALLBACK(set_psi, pressure, units::PSI) +UNITCALLBACK(set_liter, volume, units::LITER) +UNITCALLBACK(set_cuft, volume, units::CUFT) +UNITCALLBACK(set_celsius, temperature, units::CELSIUS) +UNITCALLBACK(set_fahrenheit, temperature, units::FAHRENHEIT) +UNITCALLBACK(set_kg, weight, units::KG) +UNITCALLBACK(set_lbs, weight, units::LBS) + +OPTIONCALLBACK(otu_toggle, prefs.visible_cols.otu) +OPTIONCALLBACK(maxcns_toggle, prefs.visible_cols.maxcns) +OPTIONCALLBACK(sac_toggle, prefs.visible_cols.sac) +OPTIONCALLBACK(nitrox_toggle, prefs.visible_cols.nitrox) +OPTIONCALLBACK(temperature_toggle, prefs.visible_cols.temperature) +OPTIONCALLBACK(totalweight_toggle, prefs.visible_cols.totalweight) +OPTIONCALLBACK(suit_toggle, prefs.visible_cols.suit) +OPTIONCALLBACK(cylinder_toggle, prefs.visible_cols.cylinder) +OPTIONCALLBACK(po2_toggle, prefs.pp_graphs.po2) +OPTIONCALLBACK(pn2_toggle, prefs.pp_graphs.pn2) +OPTIONCALLBACK(phe_toggle, prefs.pp_graphs.phe) +OPTIONCALLBACK(mod_toggle, prefs.mod) +OPTIONCALLBACK(ead_toggle, prefs.ead) +OPTIONCALLBACK(red_ceiling_toggle, prefs.profile_red_ceiling) +OPTIONCALLBACK(calc_ceiling_toggle, prefs.profile_calc_ceiling) +OPTIONCALLBACK(calc_ceiling_3m_toggle, prefs.calc_ceiling_3m_incr) + +static gboolean gflow_edit(GtkWidget *w, GdkEvent *event, gpointer _data) +{ + double gflow; + const char *buf; + if (event->type == GDK_FOCUS_CHANGE) { + buf = gtk_entry_get_text(GTK_ENTRY(w)); + sscanf(buf, "%lf", &gflow); + prefs.gflow = gflow / 100.0; + set_gf(prefs.gflow, -1.0); + update_screen(); + } + return FALSE; +} + +static gboolean gfhigh_edit(GtkWidget *w, GdkEvent *event, gpointer _data) +{ + double gfhigh; + const char *buf; + if (event->type == GDK_FOCUS_CHANGE) { + buf = gtk_entry_get_text(GTK_ENTRY(w)); + sscanf(buf, "%lf", &gfhigh); + prefs.gfhigh = gfhigh / 100.0; + set_gf(-1.0, prefs.gfhigh); + update_screen(); + } + return FALSE; +} + +static void event_toggle(GtkWidget *w, gpointer _data) +{ + gboolean *plot_ev = (gboolean *)_data; + + *plot_ev = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); +} + +static void pick_default_file(GtkWidget *w, GtkButton *button) +{ + GtkWidget *fs_dialog, *parent; + const char *current_default; + char *current_def_file, *current_def_dir; + GtkFileFilter *filter; + struct stat sb; + + fs_dialog = gtk_file_chooser_dialog_new(_("Choose Default XML File"), + GTK_WINDOW(main_window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + NULL); + parent = gtk_widget_get_ancestor(w, GTK_TYPE_DIALOG); + gtk_widget_set_sensitive(parent, FALSE); + gtk_window_set_transient_for(GTK_WINDOW(fs_dialog), GTK_WINDOW(parent)); + + current_default = prefs.default_filename; + current_def_dir = g_path_get_dirname(current_default); + current_def_file = g_path_get_basename(current_default); + + /* it's possible that the directory doesn't exist (especially for the default) + * For gtk's file select box to make sense we create it */ + if (stat(current_def_dir, &sb) != 0) + g_mkdir(current_def_dir, S_IRWXU); + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs_dialog), current_def_dir); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fs_dialog), current_def_file); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(fs_dialog), FALSE); + filter = setup_filter(); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fs_dialog), filter); + gtk_widget_show_all(fs_dialog); + if (gtk_dialog_run(GTK_DIALOG(fs_dialog)) == GTK_RESPONSE_ACCEPT) { + GSList *list; + + list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(fs_dialog)); + if (g_slist_length(list) == 1) + gtk_button_set_label(button, (const gchar *)list->data); + g_slist_free(list); + } + + free(current_def_dir); + free(current_def_file); + gtk_widget_destroy(fs_dialog); + + gtk_widget_set_sensitive(parent, TRUE); +} + +#if HAVE_OSM_GPS_MAP +static GtkWidget * map_provider_widget() +{ + int i; +#if GTK_CHECK_VERSION(2,24,0) + GtkWidget *combobox = gtk_combo_box_text_new(); + + /* several of the providers seem to be redundant or non-functional; + * we may have to skip more than just the last three eventually */ + for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i < OSM_GPS_MAP_SOURCE_LAST; i++) + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), osm_gps_map_source_get_friendly_name((OsmGpsMapSource_t)i)); +#else + GtkWidget *combobox = gtk_combo_box_new_text(); + for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i < OSM_GPS_MAP_SOURCE_LAST; i++) + gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), osm_gps_map_source_get_friendly_name(i)); +#endif + /* we don't offer choice 0 (none), so the index here is off by one */ + gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), prefs.map_provider - 1); + return combobox; +} +#endif + +static void preferences_dialog(GtkWidget *w, gpointer data) +{ + int result; + GtkWidget *dialog, *notebook, *font, *frame, *box, *hbox, *vbox, *button; + GtkWidget *xmlfile_button; +#if HAVE_OSM_GPS_MAP + GtkWidget *map_provider; +#endif + GtkWidget *entry_po2, *entry_pn2, *entry_phe, *entry_mod, *entry_gflow, *entry_gfhigh; + const char *current_default, *new_default; + char threshold_text[10], mod_text[10], utf8_buf[128]; + struct preferences oldprefs = prefs; + + 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_CANCEL, + NULL); + + /* create the notebook for the preferences and attach it to dialog */ + notebook = gtk_notebook_new(); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_start(GTK_BOX(vbox), notebook, FALSE, FALSE, 5); + + /* vbox that holds the first notebook page */ + vbox = gtk_vbox_new(FALSE, 6); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, + gtk_label_new(_("General Settings"))); + frame = gtk_frame_new(_("Units")); + gtk_box_pack_start(GTK_BOX(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, (prefs.units.length == units::METERS), + _("Feet"), set_feet, (prefs.units.length == units::FEET), + NULL); + + create_radio(box, _("Pressure:"), + _("Bar"), set_bar, (prefs.units.pressure == units::BAR), + _("PSI"), set_psi, (prefs.units.pressure == units::PSI), + NULL); + + create_radio(box, _("Volume:"), + _("Liter"), set_liter, (prefs.units.volume == units::LITER), + _("CuFt"), set_cuft, (prefs.units.volume == units::CUFT), + NULL); + + create_radio(box, _("Temperature:"), + _("Celsius"), set_celsius, (prefs.units.temperature == units::CELSIUS), + _("Fahrenheit"), set_fahrenheit, (prefs.units.temperature == units::FAHRENHEIT), + NULL); + + create_radio(box, _("Weight:"), + _("kg"), set_kg, (prefs.units.weight == units::KG), + _("lbs"), set_lbs, (prefs.units.weight == units::LBS), + NULL); + + frame = gtk_frame_new(_("Show Columns")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), box); + + button = gtk_check_button_new_with_label(_("Temp")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.temperature); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(temperature_toggle), NULL); + + button = gtk_check_button_new_with_label(_("Cyl")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.cylinder); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(cylinder_toggle), NULL); + + button = gtk_check_button_new_with_label("O" UTF8_SUBSCRIPT_2 "%"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.nitrox); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(nitrox_toggle), NULL); + + button = gtk_check_button_new_with_label(_("SAC")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.sac); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(sac_toggle), NULL); + + button = gtk_check_button_new_with_label(_("Weight")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.totalweight); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(totalweight_toggle), NULL); + + button = gtk_check_button_new_with_label(_("Suit")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.suit); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL); + + frame = gtk_frame_new(_("Divelist Font")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + font = gtk_font_button_new_with_font(prefs.divelist_font); + gtk_container_add(GTK_CONTAINER(frame),font); + + frame = gtk_frame_new(_("Misc. Options")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), box); + + frame = gtk_frame_new(_("Default XML Data File")); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5); + hbox = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), hbox); + current_default = prefs.default_filename; + xmlfile_button = gtk_button_new_with_label(current_default); + g_signal_connect(G_OBJECT(xmlfile_button), "clicked", + G_CALLBACK(pick_default_file), xmlfile_button); + gtk_box_pack_start(GTK_BOX(hbox), xmlfile_button, FALSE, FALSE, 6); +#if HAVE_OSM_GPS_MAP + frame = gtk_frame_new(_("Map provider")); + map_provider = map_provider_widget(); + gtk_container_add(GTK_CONTAINER(frame), map_provider); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 3); +#endif + /* vbox that holds the second notebook page */ + vbox = gtk_vbox_new(FALSE, 6); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, + gtk_label_new(_("Tec Settings"))); + + frame = gtk_frame_new(_("Show Columns")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), box); + + button = gtk_check_button_new_with_label(_("OTU")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.otu); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL); + + button = gtk_check_button_new_with_label(_("maxCNS")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.maxcns); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(maxcns_toggle), NULL); + + frame = gtk_frame_new(_("Profile Settings")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), vbox); + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + sprintf(utf8_buf, _("Show pO%s graph"), UTF8_SUBSCRIPT_2); + button = gtk_check_button_new_with_label(utf8_buf); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.po2); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(po2_toggle), &entry_po2); + + sprintf(utf8_buf, _("pO%s threshold"), UTF8_SUBSCRIPT_2); + frame = gtk_frame_new(utf8_buf); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); + entry_po2 = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry_po2), 4); + snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.po2_threshold); + gtk_entry_set_text(GTK_ENTRY(entry_po2), threshold_text); + gtk_widget_set_sensitive(entry_po2, prefs.pp_graphs.po2); + gtk_container_add(GTK_CONTAINER(frame), entry_po2); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + sprintf(utf8_buf, _("Show pN%s graph"), UTF8_SUBSCRIPT_2); + button = gtk_check_button_new_with_label(utf8_buf); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.pn2); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(pn2_toggle), &entry_pn2); + + sprintf(utf8_buf, _("pN%s threshold"), UTF8_SUBSCRIPT_2); + frame = gtk_frame_new(utf8_buf); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); + entry_pn2 = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry_pn2), 4); + snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.pn2_threshold); + gtk_entry_set_text(GTK_ENTRY(entry_pn2), threshold_text); + gtk_widget_set_sensitive(entry_pn2, prefs.pp_graphs.pn2); + gtk_container_add(GTK_CONTAINER(frame), entry_pn2); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + button = gtk_check_button_new_with_label(_("Show pHe graph")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.phe); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(phe_toggle), &entry_phe); + + frame = gtk_frame_new(_("pHe threshold")); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); + entry_phe = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry_phe), 4); + snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.phe_threshold); + gtk_entry_set_text(GTK_ENTRY(entry_phe), threshold_text); + gtk_widget_set_sensitive(entry_phe, prefs.pp_graphs.phe); + gtk_container_add(GTK_CONTAINER(frame), entry_phe); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + button = gtk_check_button_new_with_label(_("Show MOD")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.mod); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(mod_toggle), &entry_mod); + + frame = gtk_frame_new(_("max ppO2")); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); + entry_mod = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry_mod), 4); + snprintf(mod_text, sizeof(mod_text), "%.1f", prefs.mod_ppO2); + gtk_entry_set_text(GTK_ENTRY(entry_mod), mod_text); + gtk_widget_set_sensitive(entry_mod, prefs.mod); + gtk_container_add(GTK_CONTAINER(frame), entry_mod); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + button = gtk_check_button_new_with_label(_("Show EAD, END, EADD")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.ead); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(ead_toggle), NULL); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + button = gtk_check_button_new_with_label(_("Show dc reported ceiling in red")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_red_ceiling); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(red_ceiling_toggle), NULL); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + button = gtk_check_button_new_with_label(_("Show calculated ceiling")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_calc_ceiling); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_toggle), NULL); + + button = gtk_check_button_new_with_label(_("3m increments for calculated ceiling")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.calc_ceiling_3m_incr); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_3m_toggle), NULL); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + frame = gtk_frame_new(_("GFlow")); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); + entry_gflow = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry_gflow), 4); + snprintf(threshold_text, sizeof(threshold_text), "%.0f", prefs.gflow * 100); + gtk_entry_set_text(GTK_ENTRY(entry_gflow), threshold_text); + gtk_container_add(GTK_CONTAINER(frame), entry_gflow); + gtk_widget_add_events(entry_gflow, GDK_FOCUS_CHANGE_MASK); + g_signal_connect(G_OBJECT(entry_gflow), "event", G_CALLBACK(gflow_edit), NULL); + + frame = gtk_frame_new(_("GFhigh")); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); + entry_gfhigh = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry_gfhigh), 4); + snprintf(threshold_text, sizeof(threshold_text), "%.0f", prefs.gfhigh * 100); + gtk_entry_set_text(GTK_ENTRY(entry_gfhigh), threshold_text); + gtk_container_add(GTK_CONTAINER(frame), entry_gfhigh); + gtk_widget_add_events(entry_gfhigh, GDK_FOCUS_CHANGE_MASK); + g_signal_connect(G_OBJECT(entry_gfhigh), "event", G_CALLBACK(gfhigh_edit), NULL); + + gtk_widget_show_all(dialog); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + if (result == GTK_RESPONSE_ACCEPT) { + const char *po2_threshold_text, *pn2_threshold_text, *phe_threshold_text, *mod_text, *gflow_text, *gfhigh_text; + /* Make sure to flush any modified old dive data with old units */ + update_dive(NULL); + + prefs.divelist_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font))); + set_divelist_font(prefs.divelist_font); + po2_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_po2)); + sscanf(po2_threshold_text, "%lf", &prefs.pp_graphs.po2_threshold); + pn2_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_pn2)); + sscanf(pn2_threshold_text, "%lf", &prefs.pp_graphs.pn2_threshold); + phe_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_phe)); + sscanf(phe_threshold_text, "%lf", &prefs.pp_graphs.phe_threshold); + mod_text = gtk_entry_get_text(GTK_ENTRY(entry_mod)); + sscanf(mod_text, "%lf", &prefs.mod_ppO2); + gflow_text = gtk_entry_get_text(GTK_ENTRY(entry_gflow)); + sscanf(gflow_text, "%lf", &prefs.gflow); + gfhigh_text = gtk_entry_get_text(GTK_ENTRY(entry_gfhigh)); + sscanf(gfhigh_text, "%lf", &prefs.gfhigh); + prefs.gflow /= 100.0; + prefs.gfhigh /= 100.0; + set_gf(prefs.gflow, prefs.gfhigh); + + update_screen(); + + new_default = strdup(gtk_button_get_label(GTK_BUTTON(xmlfile_button))); + + /* if we opened the default file and are changing its name, + * update existing_filename */ + if (existing_filename) { + if (strcmp(current_default, existing_filename) == 0) { + free((void *)existing_filename); + existing_filename = strdup(new_default); + } + } + if (strcmp(current_default, new_default)) { + prefs.default_filename = new_default; + } +#if HAVE_OSM_GPS_MAP + /* get the map provider selected */ + int i; +#if GTK_CHECK_VERSION(2,24,0) + char *provider = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(map_provider)); +#else + char *provider = gtk_combo_box_get_active_text(GTK_COMBO_BOX(map_provider)); +#endif + for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i <= OSM_GPS_MAP_SOURCE_YAHOO_STREET; i++) + if (!strcmp(provider,osm_gps_map_source_get_friendly_name((OsmGpsMapSource_t)i))) { + prefs.map_provider = i; + break; + } + free((void *)provider); +#endif + save_preferences(); + } else if (result == GTK_RESPONSE_CANCEL) { + prefs = oldprefs; + set_gf(prefs.gflow, prefs.gfhigh); + update_screen(); + } + gtk_widget_destroy(dialog); +} + +static void create_toggle(const char* label, int *on, void *_data) +{ + GtkWidget *button, *table = GTK_WIDGET(_data); + int rows, cols, x, y; + static int count; + + if (table == NULL) { + /* magic way to reset the number of toggle buttons + * that we have already added - call this before you + * create the dialog */ + count = 0; + return; + } + g_object_get(G_OBJECT(table), "n-columns", &cols, "n-rows", &rows, NULL); + if (count > rows * cols) { + gtk_table_resize(GTK_TABLE(table),rows+1,cols); + rows++; + } + x = count % cols; + y = count / cols; + button = gtk_check_button_new_with_label(label); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *on); + gtk_table_attach_defaults(GTK_TABLE(table), button, x, x+1, y, y+1); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(event_toggle), on); + count++; +} + +static void selectevents_dialog(GtkWidget *w, gpointer data) +{ + int result; + GtkWidget *dialog, *frame, *vbox, *table, *label; + + dialog = gtk_dialog_new_with_buttons(_("Select Events"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + /* initialize the function that fills the table */ + create_toggle(NULL, NULL, NULL); + + frame = gtk_frame_new(_("Enable / Disable Events")); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + table = gtk_table_new(1, 4, TRUE); + if (!evn_foreach(&create_toggle, table)) { + g_object_ref_sink(G_OBJECT(table)); + label = gtk_label_new(_("\nNo Events\n")); + gtk_container_add(GTK_CONTAINER(frame), label); + } else { + gtk_container_add(GTK_CONTAINER(frame), table); + } + + gtk_widget_show_all(dialog); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + if (result == GTK_RESPONSE_ACCEPT) { + repaint_dive(); + } + gtk_widget_destroy(dialog); +} + +static void autogroup_cb(GtkWidget *w, gpointer data) +{ + autogroup = !autogroup; + if (! autogroup) + remove_autogen_trips(); + dive_list_update_dives(); +} + +void set_autogroup(gboolean value) +{ + GtkAction *autogroup_action; + + if (value == autogroup) + return; + + autogroup_action = gtk_action_group_get_action(action_group, "Autogroup"); + gtk_action_activate(autogroup_action); +} + +static void renumber_dialog(GtkWidget *w, gpointer data) +{ + int result; + struct dive *dive; + GtkWidget *dialog, *frame, *button, *vbox; + + 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); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + + frame = gtk_frame_new(_("New starting number")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + button = gtk_spin_button_new_with_range(1, 50000, 1); + gtk_container_add(GTK_CONTAINER(frame), button); + + /* + * Do we have a number for the first dive already? Use that + * as the default. + */ + dive = get_dive(0); + if (dive && dive->number) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), dive->number); + + 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 void about_dialog_link_cb(GtkAboutDialog *dialog, const gchar *link, gpointer data) +{ + subsurface_launch_for_uri(link); +} + +static void about_dialog(GtkWidget *w, gpointer data) +{ + const char *logo_property = NULL; + GdkPixbuf *logo = NULL; + GtkWidget *dialog; + + if (need_icon) { + logo_property = "logo"; + logo = gdk_pixbuf_from_pixdata(&subsurface_icon_pixbuf, TRUE, NULL); + } + dialog = gtk_about_dialog_new(); +#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 24) + gtk_about_dialog_set_url_hook(about_dialog_link_cb, NULL, NULL); /* deprecated since GTK 2.24 */ +#else + g_signal_connect(GTK_ABOUT_DIALOG(dialog), "activate-link", G_CALLBACK(about_dialog_link_cb), NULL); +#endif + g_object_set(GTK_OBJECT(dialog), + "title", _("About Subsurface"), + "program-name", "Subsurface", + "comments", _("Multi-platform divelog software in C"), + "website", "http://subsurface.hohndel.org", + "license", "GNU General Public License, version 2\nhttp://www.gnu.org/licenses/old-licenses/gpl-2.0.html", + "version", VERSION_STRING, + "copyright", _("Linus Torvalds, Dirk Hohndel, and others, 2011, 2012, 2013"), + /*++GETTEXT the term translator-credits is magic - list the names of the tranlators here */ + "translator_credits", _("translator-credits"), + "logo-icon-name", "subsurface", + /* Must be last: */ + logo_property, logo, + NULL); + if (logo) + g_object_unref(logo); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + +static void show_user_manual(GtkWidget *w, gpointer data) +{ + subsurface_launch_for_uri("http://subsurface.hohndel.org/documentation/user-manual/"); +} + +static void view_list(GtkWidget *w, gpointer data) +{ + save_pane_position(); + gtk_paned_set_position(GTK_PANED(vpane), 0); + pane_conf = PANE_LIST; +} + +static void view_profile(GtkWidget *w, gpointer data) +{ + save_pane_position(); + gtk_paned_set_position(GTK_PANED(hpane), 0); + gtk_paned_set_position(GTK_PANED(vpane), 65535); + pane_conf = PANE_PROFILE; +} + +static void view_info(GtkWidget *w, gpointer data) +{ + + save_pane_position(); + gtk_paned_set_position(GTK_PANED(vpane), 65535); + gtk_paned_set_position(GTK_PANED(hpane), 65535); + pane_conf = PANE_INFO; +} + +static void view_three(GtkWidget *w, gpointer data) +{ + GtkAllocation alloc; + GtkRequisition requisition; + + int vpane_position = subsurface_get_conf_int("vpane_position"); + int hpane_position = subsurface_get_conf_int("hpane_position"); + + gtk_widget_get_allocation(hpane, &alloc); + + if (hpane_position) + gtk_paned_set_position(GTK_PANED(hpane), hpane_position); + else + gtk_paned_set_position(GTK_PANED(hpane), alloc.width/2); + + gtk_widget_get_allocation(vpane, &alloc); + gtk_widget_size_request(notebook, &requisition); + /* pick the requested size for the notebook plus 6 pixels for frame */ + if (vpane_position) + gtk_paned_set_position(GTK_PANED(vpane), vpane_position); + else + gtk_paned_set_position(GTK_PANED(vpane), requisition.height + 6); + + pane_conf = PANE_THREE; +} + +static void toggle_zoom(GtkWidget *w, gpointer data) +{ + zoomed_plot = (zoomed_plot)?0 : 1; + /*Update dive*/ + repaint_dive(); +} + +static void prev_dc(GtkWidget *w, gpointer data) +{ + dc_number--; + /* If the dc number underflows, we'll "wrap around" and use the last dc */ + repaint_dive(); +} + +static void next_dc(GtkWidget *w, gpointer data) +{ + dc_number++; + /* If the dc number overflows, we'll "wrap around" and zero it */ + repaint_dive(); +} + + +/* list columns for nickname edit treeview */ +enum { + NE_MODEL, + NE_ID_STR, + NE_NICKNAME, + NE_NCOL +}; + +/* delete a selection of nicknames */ +static void edit_dc_delete_rows(GtkTreeView *view) +{ + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeRowReference *ref; + GtkTreeSelection *selection; + GList *selected_rows, *list, *row_references = NULL; + guint len; + /* params for delete op */ + const char *model_str; + const char *deviceid_string; /* convert to deviceid */ + uint32_t deviceid; + + selection = gtk_tree_view_get_selection(view); + selected_rows = gtk_tree_selection_get_selected_rows(selection, &model); + + for (list = selected_rows; list; list = g_list_next(list)) { + path = (GtkTreePath *)list->data; + ref = gtk_tree_row_reference_new(model, path); + row_references = g_list_append(row_references, ref); + } + len = g_list_length(row_references); + if (len == 0) + /* Warn about empty selection? */ + return; + + for (list = row_references; list; list = g_list_next(list)) { + path = gtk_tree_row_reference_get_path((GtkTreeRowReference *)(list->data)); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, + NE_MODEL, &model_str, + NE_ID_STR, &deviceid_string, + -1); + if (sscanf(deviceid_string, "0x%x8", &deviceid) == 1) + remove_dc(model_str, deviceid); + + gtk_list_store_remove(GTK_LIST_STORE (model), &iter); + gtk_tree_path_free(path); + } + g_list_free(selected_rows); + g_list_free(row_references); + g_list_free(list); + + if (gtk_tree_model_get_iter_first(model, &iter)) + gtk_tree_selection_select_iter(selection, &iter); +} + +/* repopulate the edited nickname cell of a DC */ +static void cell_edited_cb(GtkCellRendererText *cell, gchar *path, + gchar *new_text, gpointer store) +{ + GtkTreeIter iter; + const char *model; + const char *deviceid_string; + uint32_t deviceid; + int matched; + + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path); + /* display new text */ + gtk_list_store_set(GTK_LIST_STORE(store), &iter, NE_NICKNAME, new_text, -1); + /* and new_text */ + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, + NE_MODEL, &model, + NE_ID_STR, &deviceid_string, + -1); + /* extract deviceid */ + matched = sscanf(deviceid_string, "0x%x8", &deviceid); + + /* remember pending commit + * Need to extend list rather than wipe and store only one result */ + if (matched == 1){ + if (holdnicknames == NULL){ + holdnicknames = (struct device_info *) malloc(sizeof(struct device_info)); + holdnicknames->model = strdup(model); + holdnicknames->deviceid = deviceid; + holdnicknames->serial_nr = NULL; + holdnicknames->firmware = NULL; + holdnicknames->nickname = strdup(new_text); + holdnicknames->next = NULL; + } else { + struct device_info * top; + struct device_info * last = holdnicknames; + top = (struct device_info *) malloc(sizeof(struct device_info)); + top->model = strdup(model); + top->deviceid = deviceid; + top->serial_nr = NULL; + top->firmware = NULL; + top->nickname = strdup(new_text); + top->next = last; + holdnicknames = top; + } + } +} + +#define SUB_RESPONSE_DELETE 1 /* no delete response in gtk+2 */ +#define SUB_DONE 2 /* enable escape when done */ + +/* show the dialog to edit dc nicknames */ +static void edit_dc_nicknames(GtkWidget *w, gpointer data) +{ + const gchar *C_INACTIVE = "#e8e8ee", *C_ACTIVE = "#ffffff"; /* cell colours */ + GtkWidget *dialog, *confirm, *view, *scroll, *vbox; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkListStore *store; + GtkTreeIter iter; + gint res = -1; + char id_string[11] = {0}; + struct device_info * nnl; + + dialog = gtk_dialog_new_with_buttons(_("Edit Dive Computer Nicknames"), + GTK_WINDOW(main_window), + GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_DELETE, + SUB_RESPONSE_DELETE, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_APPLY, + GTK_RESPONSE_APPLY, + NULL); + gtk_widget_set_size_request(dialog, 700, 400); + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + view = gtk_tree_view_new(); + store = gtk_list_store_new(NE_NCOL, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + model = GTK_TREE_MODEL(store); + + /* columns */ + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "background", C_INACTIVE, NULL); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, _("Model"), + renderer, "text", NE_MODEL, NULL); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "background", C_INACTIVE, NULL); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, _("Device Id"), + renderer, "text", NE_ID_STR, NULL); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "background", C_INACTIVE, NULL); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, _("Nickname"), + renderer, "text", NE_NICKNAME, NULL); + g_object_set(renderer, "editable", TRUE, NULL); + g_object_set(renderer, "background", C_ACTIVE, NULL); + g_signal_connect(renderer, "edited", G_CALLBACK(cell_edited_cb), store); + + gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); + g_object_unref(model); + + /* populate list store from device_info_list */ + nnl = head_of_device_info_list(); + while (nnl) { + sprintf(&id_string[0], "%#08x", nnl->deviceid); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + NE_MODEL, nnl->model, + NE_ID_STR, id_string, + NE_NICKNAME, nnl->nickname, + -1); + nnl = nnl->next; + } + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); + gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_add(GTK_CONTAINER(scroll), view); + gtk_container_add(GTK_CONTAINER(vbox), + gtk_label_new(_("Edit a dive computer nickname by double-clicking the in the relevant nickname field"))); + gtk_container_add(GTK_CONTAINER(vbox), scroll); + gtk_widget_set_size_request(scroll, 500, 300); + gtk_widget_show_all(dialog); + + do { + res = gtk_dialog_run(GTK_DIALOG(dialog)); + if (res == SUB_RESPONSE_DELETE) { + confirm = gtk_dialog_new_with_buttons(_("Delete a dive computer information entry"), + GTK_WINDOW(dialog), + GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_YES, + GTK_RESPONSE_YES, + GTK_STOCK_NO, + GTK_RESPONSE_NO, + NULL); + gtk_widget_set_size_request(confirm, 350, 90); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(confirm)); + gtk_container_add(GTK_CONTAINER(vbox), + gtk_label_new(_("Ok to delete the selected entry?"))); + gtk_widget_show_all(confirm); + if (gtk_dialog_run(GTK_DIALOG(confirm)) == GTK_RESPONSE_YES) { + edit_dc_delete_rows(GTK_TREE_VIEW(view)); + res = SUB_DONE; /* want to close ** both ** dialogs now */ + } + mark_divelist_changed(TRUE); + gtk_widget_destroy(confirm); + } + if (res == GTK_RESPONSE_APPLY && holdnicknames && holdnicknames->model != NULL) { + struct device_info * walk = holdnicknames; + struct device_info * release = holdnicknames; + struct device_info * track = holdnicknames->next; + while (walk) { + remember_dc(walk->model, walk->deviceid, walk->nickname); + walk = walk->next; + } + /* clear down list */ + while (release){ + free(release); + release = track; + if (track) + track = track->next; + } + holdnicknames = NULL; + mark_divelist_changed(TRUE); + } + } while (res != SUB_DONE && res != GTK_RESPONSE_CANCEL && res != GTK_RESPONSE_DELETE_EVENT && res != GTK_RESPONSE_APPLY); + gtk_widget_destroy(dialog); +} + +static GtkActionEntry menu_items[] = { + { "FileMenuAction", NULL, N_("File"), NULL, NULL, NULL}, + { "LogMenuAction", NULL, N_("Log"), NULL, NULL, NULL}, + { "ViewMenuAction", NULL, N_("View"), NULL, NULL, NULL}, + { "FilterMenuAction", NULL, N_("Filter"), NULL, NULL, NULL}, + { "PlannerMenuAction", NULL, N_("Planner"), NULL, NULL, NULL}, + { "HelpMenuAction", NULL, N_("Help"), NULL, NULL, NULL}, + { "NewFile", GTK_STOCK_NEW, N_("New"), CTRLCHAR "N", NULL, G_CALLBACK(file_close) }, + { "OpenFile", GTK_STOCK_OPEN, N_("Open..."), CTRLCHAR "O", NULL, G_CALLBACK(file_open) }, + { "SaveFile", GTK_STOCK_SAVE, N_("Save..."), CTRLCHAR "S", NULL, G_CALLBACK(file_save) }, + { "SaveAsFile", GTK_STOCK_SAVE_AS, N_("Save As..."), SHIFTCHAR CTRLCHAR "S", NULL, G_CALLBACK(file_save_as) }, + { "CloseFile", GTK_STOCK_CLOSE, N_("Close"), NULL, NULL, G_CALLBACK(file_close) }, + { "Print", GTK_STOCK_PRINT, N_("Print..."), CTRLCHAR "P", NULL, G_CALLBACK(do_print) }, + { "ImportFile", NULL, N_("Import File(s)..."), CTRLCHAR "I", NULL, G_CALLBACK(import_files) }, + { "ExportUDDF", NULL, N_("Export UDDF..."), NULL, NULL, G_CALLBACK(export_all_dives_uddf_cb) }, + { "DownloadLog", NULL, N_("Download From Dive Computer..."), CTRLCHAR "D", NULL, G_CALLBACK(download_dialog) }, + { "DownloadWeb", GTK_STOCK_CONNECT, N_("Download From Web Service..."), NULL, NULL, G_CALLBACK(webservice_download_dialog) }, + { "AddDive", GTK_STOCK_ADD, N_("Add Dive..."), NULL, NULL, G_CALLBACK(add_dive_cb) }, + { "Preferences", GTK_STOCK_PREFERENCES, N_("Preferences..."), PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) }, + { "Renumber", NULL, N_("Renumber..."), NULL, NULL, G_CALLBACK(renumber_dialog) }, + { "YearlyStats", NULL, N_("Yearly Statistics"), NULL, NULL, G_CALLBACK(show_yearly_stats) }, +#if HAVE_OSM_GPS_MAP + { "DivesLocations", NULL, N_("Dives Locations"), CTRLCHAR "M", NULL, G_CALLBACK(show_gps_locations) }, +#endif + { "SelectEvents", NULL, N_("Select Events..."), NULL, NULL, G_CALLBACK(selectevents_dialog) }, + { "Quit", GTK_STOCK_QUIT, N_("Quit"), CTRLCHAR "Q", NULL, G_CALLBACK(quit) }, + { "About", GTK_STOCK_ABOUT, N_("About Subsurface"), NULL, NULL, G_CALLBACK(about_dialog) }, + { "UserManual", GTK_STOCK_HELP, N_("User Manual"), NULL, NULL, G_CALLBACK(show_user_manual) }, + { "ViewList", NULL, N_("List"), CTRLCHAR "1", NULL, G_CALLBACK(view_list) }, + { "ViewProfile", NULL, N_("Profile"), CTRLCHAR "2", NULL, G_CALLBACK(view_profile) }, + { "ViewInfo", NULL, N_("Info"), CTRLCHAR "3", NULL, G_CALLBACK(view_info) }, + { "ViewThree", NULL, N_("Three"), CTRLCHAR "4", NULL, G_CALLBACK(view_three) }, + { "EditNames", NULL, N_("Edit Device Names"), CTRLCHAR "E", NULL, G_CALLBACK(edit_dc_nicknames) }, + { "PrevDC", NULL, N_("Prev DC"), NULL, NULL, G_CALLBACK(prev_dc) }, + { "NextDC", NULL, N_("Next DC"), NULL, NULL, G_CALLBACK(next_dc) }, + { "InputPlan", NULL, N_("Input Plan"), NULL, NULL, G_CALLBACK(input_plan) }, +}; +static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); + +static GtkToggleActionEntry toggle_items[] = { + { "Autogroup", NULL, N_("Autogroup"), NULL, NULL, G_CALLBACK(autogroup_cb), FALSE }, + { "ToggleZoom", NULL, N_("Toggle Zoom"), CTRLCHAR "0", NULL, G_CALLBACK(toggle_zoom), FALSE }, +}; +static gint ntoggle_items = sizeof (toggle_items) / sizeof (toggle_items[0]); + +static const gchar* ui_string = " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " +#if HAVE_OSM_GPS_MAP + " " +#endif + " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +"; + +static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager) +{ + action_group = gtk_action_group_new("Menu"); + gtk_action_group_set_translation_domain(action_group, "subsurface"); + gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0); + toggle_items[0].is_active = autogroup; + gtk_action_group_add_toggle_actions(action_group, toggle_items, ntoggle_items, 0); + + 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(); +} + +static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, GtkWidget *divelist) +{ + if (event->type != GDK_KEY_PRESS || event->state != 0) + return FALSE; + switch (event->keyval) { + case GDK_Up: + select_prev_dive(); + return TRUE; + case GDK_Down: + select_next_dive(); + return TRUE; + case GDK_Left: + prev_dc(NULL, NULL); + return TRUE; + case GDK_Right: + next_dc(NULL, NULL); + return TRUE; + } + return FALSE; +} + +static gboolean notebook_tooltip (GtkWidget *widget, gint x, gint y, + gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data) +{ + if (amount_selected > 0 && gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)) == 0) { + gtk_tooltip_set_text(tooltip, _("To edit dive information\ndouble click on it in the dive list")); + return TRUE; + } else { + return FALSE; + } +} + +#if NEEDS_TO_MOVE_TO_QT_UI +/* this appears to have moved - but it's very different in qt-ui */ + +class MainWindow: public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow() {} + + void setCurrentFileName(const QString &fileName); + +private Q_SLOTS: + void on_actionNew_triggered() { on_actionClose_triggered(); } + void on_actionOpen_triggered(); + void on_actionSave_triggered() { file_save(NULL, NULL); } + void on_actionSaveAs_triggered() { file_save_as(NULL, NULL); } + void on_actionClose_triggered(); + +private: + QStringList fileNameFilters() const; + +private: + QString m_currentFileName; +}; + +MainWindow::MainWindow(QWidget *parent): + QMainWindow(parent) +{ + setupUi(this); +} + +void MainWindow::setCurrentFileName(const QString &fileName) +{ + if (fileName == m_currentFileName) return; + m_currentFileName = fileName; + + QString title = tr("Subsurface"); + if (!m_currentFileName.isEmpty()) { + QFileInfo fileInfo(m_currentFileName); + title += " - " + fileInfo.fileName(); + } + setWindowTitle(title); +} + +void MainWindow::on_actionOpen_triggered() +{ + QString defaultFileName = prefs.default_filename; + QFileInfo fileInfo(defaultFileName); + + QFileDialog dialog(this, tr("Open File"), fileInfo.path()); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.selectFile(defaultFileName); + dialog.setNameFilters(fileNameFilters()); + if (dialog.exec()) { + /* first, close the existing file, if any */ + file_close(NULL, NULL); + + /* we know there is only one filename */ + QString fileName = dialog.selectedFiles().first(); + GError *error = NULL; + parse_file(fileName.toUtf8().constData(), &error); + if (error != NULL) { + report_error(error); + g_error_free(error); + error = NULL; + } else { + setCurrentFileName(fileName); + } + report_dives(FALSE, FALSE); + } +} + +void MainWindow::on_actionClose_triggered() +{ + if (unsaved_changes()) + if (ask_save_changes() == FALSE) + return; + + setCurrentFileName(QString()); + + /* free the dives and trips */ + while (dive_table.nr) + delete_single_dive(0); + mark_divelist_changed(FALSE); + + /* clear the selection and the statistics */ + selected_dive = 0; + process_selected_dives(); + clear_stats_widgets(); + clear_events(); + show_dive_stats(NULL); + + /* clear the equipment page */ + clear_equipment_widgets(); + + /* redraw the screen */ + dive_list_update_dives(); + show_dive_info(NULL); +} + +QStringList MainWindow::fileNameFilters() const +{ + QStringList filters; + + filters << "*.xml *.uddf *.udcf *.jlb" +#ifdef LIBZIP + " *.sde *.dld" +#endif +#ifdef SQLITE3 + " *.db" +#endif + ; + return filters; +} +#endif /* NEEDS_TO_MOVE_TO_QT_UI */ + +void init_qt_ui(int *argcp, char ***argvp) +{ + application->installTranslator(new Translator(application)); + MainWindow *window = new MainWindow(); + window->show(); +} + +void init_ui(int *argcp, char ***argvp) +{ + application = new QApplication(*argcp, *argvp); + +#if QT_VERSION < 0x050000 + // ask QString in Qt 4 to interpret all char* as UTF-8, + // like Qt 5 does. + // 106 is "UTF-8", this is faster than lookup by name + // [http://www.iana.org/assignments/character-sets/character-sets.xml] + QTextCodec::setCodecForCStrings(QTextCodec::codecForMib(106)); +#endif + + GtkWidget *win; + GtkWidget *nb_page; + GtkWidget *dive_list; + GtkWidget *menubar; + GtkWidget *vbox; + GtkWidget *scrolled; + GdkScreen *screen; + GtkIconTheme *icon_theme=NULL; + GtkSettings *settings; + GtkUIManager *ui_manager; + + gtk_init(argcp, argvp); + settings = gtk_settings_get_default(); + gtk_settings_set_long_property(settings, "gtk-tooltip-timeout", 10, "subsurface setting"); + gtk_settings_set_long_property(settings, "gtk-menu-images", 1, "subsurface setting"); + gtk_settings_set_long_property(settings, "gtk-button-images", 1, "subsurface setting"); + + /* check if utf8 stars are available as a default OS feature */ + if (!subsurface_os_feature_available(UTF8_FONT_WITH_STARS)) { + star_strings[0] = " "; + star_strings[1] = "* "; + star_strings[2] = "** "; + star_strings[3] = "*** "; + star_strings[4] = "**** "; + star_strings[5] = "*****"; + } +#if !GLIB_CHECK_VERSION(2,3,6) + g_type_init(); +#endif + subsurface_open_conf(); + + load_preferences(); + + default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor"); + default_dive_computer_product = subsurface_get_conf("dive_computer_product"); + default_dive_computer_device = subsurface_get_conf("dive_computer_device"); + error_info_bar = NULL; + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_set_application_name ("subsurface"); + /* Let's check if the subsurface icon has been installed or if + * we need to try to load it from the current directory */ + screen = gdk_screen_get_default(); + if (screen) + icon_theme = gtk_icon_theme_get_for_screen(screen); + if (icon_theme) { + if (gtk_icon_theme_has_icon(icon_theme, "subsurface")) { + need_icon = FALSE; + gtk_window_set_default_icon_name ("subsurface"); + } + } + if (need_icon) { + const char *icon_name = subsurface_icon_name(); + if (!access(icon_name, R_OK)) + gtk_window_set_icon_from_file(GTK_WINDOW(win), icon_name, NULL); + } + g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL); + g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL); + g_signal_connect(G_OBJECT(win), "window-state-event", G_CALLBACK(on_state), NULL); + main_window = win; + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(win), vbox); + main_vbox = vbox; + + ui_manager = gtk_ui_manager_new(); + menubar = get_menubar_menu(win, ui_manager); + + subsurface_ui_setup(settings, menubar, vbox, ui_manager); + + vpane = gtk_vpaned_new(); + gtk_box_pack_start(GTK_BOX(vbox), vpane, TRUE, TRUE, 3); + hpane = gtk_hpaned_new(); + gtk_paned_add1(GTK_PANED(vpane), hpane); + g_signal_connect_after(G_OBJECT(vbox), "realize", G_CALLBACK(view_three), NULL); + + /* Notebook for dive info vs profile vs .. */ + notebook = gtk_notebook_new(); + scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_paned_add1(GTK_PANED(hpane), scrolled); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), notebook); + g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL); + + /* Create the actual divelist */ + dive_list = dive_list_create(); + gtk_widget_set_name(dive_list, "Dive List"); + gtk_paned_add2(GTK_PANED(vpane), dive_list); + + /* Frame for dive profile */ + dive_profile = dive_profile_widget(); + gtk_widget_set_name(dive_profile, "Dive Profile"); + gtk_paned_add2(GTK_PANED(hpane), dive_profile); + + /* Frame for extended dive info */ + nb_page = extended_dive_info_widget(); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Dive Notes"))); + + /* Frame for dive equipment */ + nb_page = equipment_widget(W_IDX_PRIMARY); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Equipment"))); + + /* Frame for single dive statistics */ + nb_page = single_stats_widget(); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Dive Info"))); + + /* Frame for total dive statistics */ + nb_page = total_stats_widget(); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Stats"))); + + /* add tooltip that tells people how to edit things */ + g_object_set(notebook, "has-tooltip", TRUE, NULL); + g_signal_connect(notebook, "query-tooltip", G_CALLBACK(notebook_tooltip), NULL); + + /* handle some keys globally (to deal with gtk focus issues) */ + g_signal_connect (G_OBJECT (win), "key_press_event", G_CALLBACK (on_key_press), dive_list); + + gtk_widget_set_app_paintable(win, TRUE); + restore_window_geometry(); + gtk_widget_show_all(win); + + return; +} + +void run_ui(void) +{ + application->exec(); +} + +void exit_ui(void) +{ + delete application; + subsurface_close_conf(); + if (existing_filename) + free((void *)existing_filename); + if (default_dive_computer_device) + free((void *)default_dive_computer_device); +} + +typedef struct { + cairo_rectangle_t rect; + const char *text; + struct event *event; +} tooltip_record_t; + +static tooltip_record_t *tooltip_rects; +static int tooltips; + +void attach_tooltip(int x, int y, int w, int h, const char *text, struct event *event) +{ + cairo_rectangle_t *rect; + tooltip_rects = (tooltip_record_t *) + realloc(tooltip_rects, (tooltips + 1) * sizeof(tooltip_record_t)); + rect = &tooltip_rects[tooltips].rect; + rect->x = x; + rect->y = y; + rect->width = w; + rect->height = h; + tooltip_rects[tooltips].text = strdup(text); + tooltip_rects[tooltips].event = event; + tooltips++; +} + +#define INSIDE_RECT(_r,_x,_y) ((_r.x <= _x) && (_r.x + _r.width >= _x) && \ + (_r.y <= _y) && (_r.y + _r.height >= _y)) +#define INSIDE_RECT_X(_r, _x) ((_r.x <= _x) && (_r.x + _r.width >= _x)) + +static gboolean profile_tooltip (GtkWidget *widget, gint x, gint y, + gboolean keyboard_mode, GtkTooltip *tooltip, struct graphics_context *gc) +{ + int i; + cairo_rectangle_t *drawing_area = &gc->drawing_area; + gint tx = x - drawing_area->x; /* get transformed coordinates */ + gint ty = y - drawing_area->y; + gint width, height, time = -1; + char buffer[2048], plot[1024]; + const char *event = ""; + + if (tx < 0 || ty < 0) + return FALSE; + + /* don't draw a tooltip if nothing is there */ + if (amount_selected == 0 || gc->pi.nr == 0) + return FALSE; + + width = drawing_area->width - 2*drawing_area->x; + height = drawing_area->height - 2*drawing_area->y; + if (width <= 0 || height <= 0) + return FALSE; + + if (tx > width || ty > height) + return FALSE; + + time = (tx * gc->maxtime) / width; + + /* are we over an event marker ? */ + for (i = 0; i < tooltips; i++) { + if (INSIDE_RECT(tooltip_rects[i].rect, tx, ty)) { + event = tooltip_rects[i].text; + break; + } + } + get_plot_details(gc, time, plot, sizeof(plot)); + + snprintf(buffer, sizeof(buffer), "@ %d:%02d%c%s%c%s", time / 60, time % 60, + *plot ? '\n' : ' ', plot, + *event ? '\n' : ' ', event); + gtk_tooltip_set_text(tooltip, buffer); + return TRUE; + +} + +static double zoom_factor = 1.0; +static int zoom_x = -1, zoom_y = -1; + +static void common_drawing_function(GtkWidget *widget, struct graphics_context *gc) +{ + int i = 0; + struct dive *dive = current_dive; + + gc->drawing_area.x = MIN(50,gc->drawing_area.width / 20.0); + gc->drawing_area.y = MIN(50,gc->drawing_area.height / 20.0); + + g_object_set(widget, "has-tooltip", TRUE, NULL); + g_signal_connect(widget, "query-tooltip", G_CALLBACK(profile_tooltip), gc); + init_profile_background(gc); + cairo_paint(gc->cr); + + if (zoom_factor > 1.0) { + double n = -(zoom_factor-1); + cairo_translate(gc->cr, n*zoom_x, n*zoom_y); + cairo_scale(gc->cr, zoom_factor, zoom_factor); + } + + if (dive) { + if (tooltip_rects) { + while (i < tooltips) { + if (tooltip_rects[i].text) + free((void *)tooltip_rects[i].text); + i++; + } + free(tooltip_rects); + tooltip_rects = NULL; + } + tooltips = 0; + plot(gc, dive, SC_SCREEN); + } +} + +#if GTK_CHECK_VERSION(3,0,0) + +static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) +{ + guint width, height; + static struct graphics_context gc = { .printer = 0 }; + + width = gtk_widget_get_allocated_width(widget); + height = gtk_widget_get_allocated_height(widget); + + gc.drawing_area.width = width; + gc.drawing_area.height = height; + gc.cr = cr; + + common_drawing_function(widget, &gc); + return FALSE; +} + +#else /* gtk2 */ + +static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + GtkAllocation allocation; + static struct graphics_context gc = { 0 }; + + /* the drawing area gives TOTAL width * height - x,y is used as the topx/topy offset + * so effective drawing area is width-2x * height-2y */ + gtk_widget_get_allocation(widget, &allocation); + gc.drawing_area.width = allocation.width; + gc.drawing_area.height = allocation.height; + gc.cr = gdk_cairo_create(gtk_widget_get_window(widget)); + + common_drawing_function(widget, &gc); + cairo_destroy(gc.cr); + return FALSE; +} + +#endif + +static void zoom_event(int x, int y, double inc) +{ + zoom_x = x; + zoom_y = y; + inc += zoom_factor; + if (inc < 1.0) + inc = 1.0; + else if (inc > 10) + inc = 10; + zoom_factor = inc; +} + +static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer user_data) +{ + switch (event->direction) { + case GDK_SCROLL_UP: + zoom_event(event->x, event->y, 0.1); + break; + case GDK_SCROLL_DOWN: + zoom_event(event->x, event->y, -0.1); + break; + default: + return TRUE; + } + gtk_widget_queue_draw(widget); + return TRUE; +} + +static void add_gas_change_cb(GtkWidget *menuitem, gpointer data) +{ + double *x = (double *)data; + int when = x_to_time(*x); + int cylnr = select_cylinder(current_dive, when); + if (cylnr >= 0) { + cylinder_t *cyl = ¤t_dive->cylinder[cylnr]; + int value = cyl->gasmix.o2.permille / 10 | ((cyl->gasmix.he.permille / 10) << 16); + add_event(current_dc, when, 25, 0, value, "gaschange"); + mark_divelist_changed(TRUE); + report_dives(FALSE, FALSE); + dive_list_update_dives(); + } +} + +int confirm_dialog(int when, char *action_text, char *event_text) +{ + GtkWidget *dialog, *vbox, *label; + int confirmed; + char title[80]; + + snprintf(title, sizeof(title), "%s %s", action_text, event_text); + dialog = gtk_dialog_new_with_buttons(title, + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + label = create_label(_("%s event at %d:%02u"), title, FRACTION(when, 60)); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_widget_show_all(dialog); + confirmed = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + + gtk_widget_destroy(dialog); + + return confirmed; +} + +static void add_bookmark_cb(GtkWidget *menuitem, gpointer data) +{ + double *x = (double *)data; + int when = x_to_time(*x); + + if (confirm_dialog(when, _("Add"), _("bookmark"))){ + add_event(current_dc, when, 8, 0, 0, "bookmark"); + mark_divelist_changed(TRUE); + report_dives(FALSE, FALSE); + } +} + +static struct event *event_at_x(double rel_x) +{ + /* is there an event marker at this x coordinate */ + struct event *ret = NULL; + int i; + int x = x_abs(rel_x); + + for (i = 0; i < tooltips; i++) { + if (INSIDE_RECT_X(tooltip_rects[i].rect, x)) { + ret = tooltip_rects[i].event; + break; + } + } + return ret; +} + +static void remove_event_cb(GtkWidget *menuitem, gpointer data) +{ + struct event *event = (struct event *)data; + if (confirm_dialog(event->time.seconds, _("Remove"), _(event->name))){ + struct event **ep = ¤t_dc->events; + while (ep && *ep != event) + ep = &(*ep)->next; + if (ep) { + *ep = event->next; + free(event); + } + mark_divelist_changed(TRUE); + report_dives(FALSE, FALSE); + } +} + +static void popup_profile_menu(GtkWidget *widget, GdkEventButton *gtk_event) +{ + GtkWidget *menu, *menuitem, *image; + static double x; + struct event *event; + + if (!gtk_event || !current_dive) + return; + x = gtk_event->x; + menu = gtk_menu_new(); + menuitem = gtk_image_menu_item_new_with_label(_("Add gas change event here")); + image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); + g_signal_connect(menuitem, "activate", G_CALLBACK(add_gas_change_cb), &x); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + menuitem = gtk_image_menu_item_new_with_label(_("Add bookmark event here")); + image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); + g_signal_connect(menuitem, "activate", G_CALLBACK(add_bookmark_cb), &x); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + if ((event = event_at_x(x)) != NULL) { + menuitem = gtk_image_menu_item_new_with_label(_("Remove event here")); + image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); + g_signal_connect(menuitem, "activate", G_CALLBACK(remove_event_cb), event); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + + gtk_widget_show_all(menu); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + gtk_event->button, gtk_get_current_event_time()); + +} + +static gboolean clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + switch (event->button) { + case 1: + zoom_x = event->x; + zoom_y = event->y; + zoom_factor = 2.5; + break; + case 3: + popup_profile_menu(widget, event); + break; + default: + return TRUE; + } + gtk_widget_queue_draw(widget); + return TRUE; +} + +static gboolean released(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + switch (event->button) { + case 1: + zoom_x = zoom_y = -1; + zoom_factor = 1.0; + break; + default: + return TRUE; + } + gtk_widget_queue_draw(widget); + return TRUE; +} + +static gboolean motion(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + if (zoom_x < 0) + return TRUE; + + zoom_x = event->x; + zoom_y = event->y; + gtk_widget_queue_draw(widget); + return TRUE; +} + +static GtkWidget *dive_profile_widget(void) +{ + GtkWidget *da; + + da = gtk_drawing_area_new(); + gtk_widget_set_size_request(da, 350, 250); +#if GTK_CHECK_VERSION(3,0,0) + g_signal_connect(da, "draw", G_CALLBACK (draw_callback), NULL); +#else + g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL); +#endif + g_signal_connect(da, "button-press-event", G_CALLBACK(clicked), NULL); + g_signal_connect(da, "scroll-event", G_CALLBACK(scroll_event), NULL); + g_signal_connect(da, "button-release-event", G_CALLBACK(released), NULL); + g_signal_connect(da, "motion-notify-event", G_CALLBACK(motion), NULL); + gtk_widget_add_events(da, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK); + + return da; +} + +static void do_import_file(gpointer data, gpointer user_data) +{ + GError *error = NULL; + parse_file((const char *)data, &error); + + if (error != NULL) + { + report_error(error); + g_error_free(error); + error = NULL; + } +} + +static void import_files(GtkWidget *w, gpointer data) +{ + GtkWidget *fs_dialog; + const char *current_default; + char *current_def_dir; + GtkFileFilter *filter; + struct stat sb; + GSList *filenames = NULL; + + fs_dialog = gtk_file_chooser_dialog_new(_("Choose XML Files To Import Into Current Data File"), + GTK_WINDOW(main_window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + /* I'm not sure what the best default path should be... */ + if (existing_filename) { + current_def_dir = g_path_get_dirname(existing_filename); + } else { + current_default = prefs.default_filename; + current_def_dir = g_path_get_dirname(current_default); + } + + /* it's possible that the directory doesn't exist (especially for the default) + * For gtk's file select box to make sense we create it */ + if (stat(current_def_dir, &sb) != 0) + g_mkdir(current_def_dir, S_IRWXU); + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs_dialog), current_def_dir); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(fs_dialog), TRUE); + filter = setup_filter(); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fs_dialog), filter); + gtk_widget_show_all(fs_dialog); + if (gtk_dialog_run(GTK_DIALOG(fs_dialog)) == GTK_RESPONSE_ACCEPT) { + /* grab the selected file list, import each file and update the list */ + filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(fs_dialog)); + if (filenames) { + g_slist_foreach(filenames, do_import_file, NULL); + report_dives(TRUE, FALSE); + g_slist_free(filenames); + } + } + + free(current_def_dir); + gtk_widget_destroy(fs_dialog); +} + +void set_filename(const char *filename, gboolean force) +{ + if (!force && existing_filename) + return; + free((void *)existing_filename); + if (filename) + existing_filename = strdup(filename); + else + existing_filename = NULL; +} + +const char *get_dc_nickname(const char *model, uint32_t deviceid) +{ + struct device_info *known = get_device_info(model, deviceid); + if (known) { + if (known->nickname && *known->nickname) + return known->nickname; + else + return known->model; + } + return NULL; +} + +void set_dc_nickname(struct dive *dive) +{ + GtkWidget *dialog, *vbox, *entry, *frame, *label; + char nickname[160] = ""; + char dialogtext[2048]; + const char *name = nickname; + struct divecomputer *dc = &dive->dc; + + if (!dive) + return; + while (dc) { +#if NICKNAME_DEBUG & 16 + fprintf(debugfile, "set_dc_nickname for model %s deviceid %8x\n", dc->model ? : "", dc->deviceid); +#endif + if (get_dc_nickname(dc->model, dc->deviceid) == NULL) { + struct device_info *nn_entry = get_different_device_info(dc->model, dc->deviceid); + if (nn_entry) { + dialog = gtk_dialog_new_with_buttons( + _("Dive Computer Nickname"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + snprintf(dialogtext, sizeof(dialogtext), + _("You already have a dive computer of this model\n" + "named %s\n" + "Subsurface can maintain a nickname for this device to " + "distinguish it from the existing one. " + "The default is the model and device ID as shown below.\n" + "If you don't want to name this dive computer click " + "'Cancel' and Subsurface will simply display its model " + "as its name (which may mean that you cannot tell the two " + "dive computers apart in the logs)."), + nn_entry->nickname && *nn_entry->nickname ? nn_entry->nickname : + (nn_entry->model && *nn_entry->model ? nn_entry->model : _("(nothing)"))); + label = gtk_label_new(dialogtext); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3); + frame = gtk_frame_new(_("Nickname")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 3); + entry = gtk_entry_new(); + gtk_container_add(GTK_CONTAINER(frame), entry); + gtk_entry_set_max_length(GTK_ENTRY(entry), 68); + snprintf(nickname, sizeof(nickname), "%s (%08x)", dc->model, dc->deviceid); + gtk_entry_set_text(GTK_ENTRY(entry), nickname); + gtk_widget_show_all(dialog); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + if (strcmp(dc->model, gtk_entry_get_text(GTK_ENTRY(entry)))) { + name = gtk_entry_get_text(GTK_ENTRY(entry)); + remember_dc(dc->model, dc->deviceid, name); + mark_divelist_changed(TRUE); + } + } else { + /* Remember that we declined the nickname */ + remember_dc(dc->model, dc->deviceid, NULL); + } + gtk_widget_destroy(dialog); + } else { + remember_dc(dc->model, dc->deviceid, NULL); + } + } + dc = dc->next; + } +} + +gdouble get_screen_dpi(void) +{ + const gdouble mm_per_inch = 25.4; + GdkScreen *scr = gdk_screen_get_default(); + gdouble h_mm = gdk_screen_get_height_mm(scr); + gdouble h = gdk_screen_get_height(scr); + gdouble dpi_h = floor((h / h_mm) * mm_per_inch); + return dpi_h; +} + +#include "qt-gui.moc" diff --git a/info-gtk.c b/info-gtk.c index ebba07b73..b386b9d4d 100644 --- a/info-gtk.c +++ b/info-gtk.c @@ -736,6 +736,14 @@ int edit_multi_dive_info(struct dive *single_dive) return success; } +int edit_dive_info(struct dive *dive, gboolean newdive) +{ + if (!dive || (!newdive && !amount_selected)) + return 0; + + return edit_multi_dive_info(dive); +} + static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...) { va_list ap; diff --git a/info.c b/info.c index 0abee1c1b..4e0c3cadb 100644 --- a/info.c +++ b/info.c @@ -448,10 +448,15 @@ void update_time_depth(struct dive *dive, struct dive *edited) dive->dc.meandepth.mm = edited->dc.meandepth.mm; } -int edit_dive_info(struct dive *dive, gboolean newdive) +void add_people(const char *string) { - if (!dive || (!newdive && !amount_selected)) - return 0; - - return edit_multi_dive_info(dive); + /* add names to the completion list for people */ +} +void add_location(const char *string) +{ + /* add names to the completion list for locations */ +} +void add_suit(const char *string) +{ + /* add names to the completion list for suits */ } diff --git a/libdivecomputer.c b/libdivecomputer.c index 9ad5df594..cc1c0be28 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -26,6 +26,7 @@ static double progress_bar_fraction = 0.0; static int stoptime, stopdepth, ndl, po2, cns; static gboolean in_deco, first_temp_is_air; +#if USE_GTK_UI static GError *error(const char *fmt, ...) { va_list args; @@ -38,6 +39,7 @@ static GError *error(const char *fmt, ...) va_end(args); return error; } +#endif static dc_status_t create_parser(device_data_t *devdata, dc_parser_t **parser) { @@ -708,6 +710,7 @@ static const char *do_libdivecomputer_import(device_data_t *data) return err; } +#if USE_GTK_UI static void *pthread_wrapper(void *_data) { device_data_t *data = _data; @@ -772,3 +775,4 @@ GError *do_import(device_data_t *data) return error(retval, data->vendor, data->product, data->devname); return NULL; } +#endif diff --git a/libdivecomputer.h b/libdivecomputer.h index 0950d32ae..f5b65f70f 100644 --- a/libdivecomputer.h +++ b/libdivecomputer.h @@ -17,10 +17,12 @@ typedef struct device_data_t { unsigned int deviceid, diveid; dc_device_t *device; dc_context_t *context; - progressbar_t progress; int preexisting; gboolean force_download; +#if USE_GTK_UI + progressbar_t progress; GtkDialog *dialog; +#endif } device_data_t; extern GError *do_import(device_data_t *data); diff --git a/linux.c b/linux.c index bf7383822..0c31b6c43 100644 --- a/linux.c +++ b/linux.c @@ -1,6 +1,7 @@ /* linux.c */ /* implements Linux specific functions */ #include "dive.h" +#include "display.h" #include "display-gtk.h" #include #include diff --git a/main.c b/main.c index 40492a7e1..424750d28 100644 --- a/main.c +++ b/main.c @@ -99,7 +99,11 @@ static void parse_argument(const char *arg) if (strcmp(arg,"--import") == 0) { /* mark the dives so far as the base, * everything after is imported */ +#if USE_GTK_UI report_dives(FALSE, FALSE); +#else + process_dives(FALSE, FALSE); +#endif imported = TRUE; return; } @@ -119,6 +123,7 @@ static void parse_argument(const char *arg) void update_dive(struct dive *new_dive) { +#if USE_GTK_UI static struct dive *buffered_dive; struct dive *old_dive = buffered_dive; @@ -129,6 +134,7 @@ void update_dive(struct dive *new_dive) show_dive_equipment(new_dive, W_IDX_PRIMARY); show_dive_stats(new_dive); buffered_dive = new_dive; +#endif } void renumber_dives(int nr) @@ -138,7 +144,9 @@ void renumber_dives(int nr) for (i = 0; i < dive_table.nr; i++) { struct dive *dive = dive_table.dives[i]; dive->number = nr + i; +#if USE_GTK_UI flush_divelist(dive); +#endif } mark_divelist_changed(TRUE); } @@ -229,7 +237,9 @@ int main(int argc, char **argv) } if (error != NULL) { +#if USE_GTK_UI report_error(error); +#endif g_error_free(error); error = NULL; } @@ -242,9 +252,13 @@ int main(int argc, char **argv) sure we remember this as the filename in use */ set_filename(filename, FALSE); } +#if USE_GTK_UI report_dives(imported, FALSE); if (dive_table.nr == 0) show_dive_info(NULL); +#else + process_dives(imported, FALSE); +#endif parse_xml_exit(); subsurface_command_line_exit(&argc, &argv); diff --git a/planner.c b/planner.c index 0f739b1f0..822d2e789 100644 --- a/planner.c +++ b/planner.c @@ -685,9 +685,11 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, c stopidx--; } add_plan_to_notes(diveplan, dive); +#if USE_GTK_UI /* now make the dive visible in the dive list */ report_dives(FALSE, FALSE); show_and_select_dive(dive); +#endif error_exit: free(stoplevels); free(gaschanges); diff --git a/profile.c b/profile.c index 18960fcff..bd395f127 100644 --- a/profile.c +++ b/profile.c @@ -136,6 +136,8 @@ static const color_t profile_color[] = { }; +#if USE_GTK_UI + /* Scale to 0,0 -> maxx,maxy */ #define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx) #define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) @@ -189,8 +191,7 @@ static void pattern_add_color_stop_rgba(struct graphics_context *gc, cairo_patte struct rgba rgb = col->media[gc->printer]; cairo_pattern_add_color_stop_rgba(pat, o, rgb.r, rgb.g, rgb.b, rgb.a); } - -#define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y)) +#endif /* USE_GTK_UI */ /* debugging tool - not normally used */ static void dump_pi (struct plot_info *pi) @@ -215,6 +216,8 @@ static void dump_pi (struct plot_info *pi) printf(" }\n"); } +#define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y)) + /* * When showing dive profiles, we scale things to the * current dive. However, we don't scale past less than @@ -276,6 +279,7 @@ typedef struct { #define MIDDLE (0) #define BOTTOM (-1) +#if USE_GTK_UI static void plot_text(struct graphics_context *gc, const text_render_options_t *tro, double x, double y, const char *fmt, ...) { @@ -308,6 +312,7 @@ static void plot_text(struct graphics_context *gc, const text_render_options_t * set_source_rgba(gc, tro->color); cairo_show_text(cr, buffer); } +#endif /* USE_GTK_UI */ /* collect all event names and whether we display them */ struct ev_select { @@ -357,6 +362,7 @@ void remember_event(const char *eventname) evn_used++; } +#if USE_GTK_UI static void plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *event) { int i, depth = 0; @@ -1027,6 +1033,7 @@ static void set_sac_color(struct graphics_context *gc, int sac, int avg_sac) set_source_rgba(gc, SAC_DEFAULT); } } +#endif /* USE_GTK_UI */ /* Get local sac-rate (in ml/min) between entry1 and entry2 */ static int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive) @@ -1065,6 +1072,7 @@ static int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, str #define SAC_WINDOW 45 /* sliding window in seconds for current SAC calculation */ +#if USE_GTK_UI static void plot_cylinder_pressure(struct graphics_context *gc, struct plot_info *pi, struct dive *dive, struct divecomputer *dc) { @@ -1189,6 +1197,7 @@ static void plot_deco_text(struct graphics_context *gc, struct plot_info *pi) plot_text(gc, &tro, x, y, "GF %.0f/%.0f", prefs.gflow * 100, prefs.gfhigh * 100); } } +#endif /* USE_GTK_UI */ static void analyze_plot_info_minmax_minute(struct plot_data *entry, struct plot_data *first, struct plot_data *last, int index) { @@ -1259,6 +1268,7 @@ static velocity_t velocity(int speed) return v; } + static struct plot_info *analyze_plot_info(struct plot_info *pi) { int i; @@ -1974,6 +1984,7 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer return analyze_plot_info(pi); } +#if USE_GTK_UI static void plot_set_scale(scale_mode_t scale) { switch (scale) { @@ -1986,6 +1997,7 @@ static void plot_set_scale(scale_mode_t scale) break; } } +#endif /* make sure you pass this the FIRST dc - it just walks the list */ static int nr_dcs(struct divecomputer *main) @@ -2015,6 +2027,7 @@ struct divecomputer *select_dc(struct divecomputer *main) return main; } +#if USE_GTK_UI void plot(struct graphics_context *gc, struct dive *dive, scale_mode_t scale) { struct plot_info *pi; @@ -2132,6 +2145,7 @@ void plot(struct graphics_context *gc, struct dive *dive, scale_mode_t scale) pi->nr = 0; } } +#endif /* USE_GTK_UI */ static void plot_string(struct plot_data *entry, char *buf, size_t bufsize, int depth, int pressure, int temp, gboolean has_ndl) diff --git a/qt-gui.cpp b/qt-gui.cpp index 7853cc4c0..2155f0029 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -1,8 +1,5 @@ -/* 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 - */ +/* qt-gui.cpp */ +/* Qt UI implementation */ #include #include #include @@ -17,8 +14,6 @@ #include "dive.h" #include "divelist.h" #include "display.h" -#include "display-gtk.h" -#include "callbacks-gtk.h" #include "uemis.h" #include "device.h" #include "webservice.h" @@ -26,9 +21,6 @@ #include "libdivecomputer.h" #include "qt-ui/mainwindow.h" -#include -#include - #include #include #include @@ -36,10 +28,6 @@ #include #include -#if HAVE_OSM_GPS_MAP -#include -#endif - class Translator: public QTranslator { Q_OBJECT @@ -63,1808 +51,11 @@ QString Translator::translate(const char *context, const char *sourceText, return gettext(sourceText); } -static const GdkPixdata subsurface_icon_pixbuf = {}; - -GtkWidget *main_window; -GtkWidget *main_vbox; -GtkWidget *error_info_bar; -GtkWidget *error_label; -GtkWidget *vpane, *hpane; -GtkWidget *notebook; static QApplication *application = NULL; int error_count; const char *existing_filename; -typedef enum { PANE_INFO, PANE_PROFILE, PANE_LIST, PANE_THREE } pane_conf_t; -static pane_conf_t pane_conf; - -static struct device_info *holdnicknames = NULL; -static GtkWidget *dive_profile_widget(void); -static void import_files(GtkWidget *, gpointer); - -static void remember_dc(const char *model, uint32_t deviceid, const char *nickname) -{ - struct device_info *nn_entry; - - nn_entry = create_device_info(model, deviceid); - if (!nn_entry) - return; - if (!nickname || !*nickname) { - nn_entry->nickname = NULL; - return; - } - nn_entry->nickname = strdup(nickname); -} - -static void remove_dc(const char *model, uint32_t deviceid) -{ - free(remove_device_info(model, deviceid)); -} - -static GtkWidget *dive_profile; - -GtkActionGroup *action_group; - -void repaint_dive(void) -{ - update_dive(current_dive); - if (dive_profile) - gtk_widget_queue_draw(dive_profile); -} - -static gboolean need_icon = TRUE; - -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) -{ - qDebug("Warning: Calling GTK-Specific Code."); - 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_text(GTK_LABEL(error_label), buffer); - } -} - -static GtkFileFilter *setup_filter(void) -{ - GtkFileFilter *filter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(filter, "*.xml"); - gtk_file_filter_add_pattern(filter, "*.XML"); - gtk_file_filter_add_pattern(filter, "*.uddf"); - gtk_file_filter_add_pattern(filter, "*.UDDF"); - gtk_file_filter_add_pattern(filter, "*.udcf"); - gtk_file_filter_add_pattern(filter, "*.UDCF"); - gtk_file_filter_add_pattern(filter, "*.jlb"); - gtk_file_filter_add_pattern(filter, "*.JLB"); -#ifdef LIBZIP - gtk_file_filter_add_pattern(filter, "*.sde"); - gtk_file_filter_add_pattern(filter, "*.SDE"); - gtk_file_filter_add_pattern(filter, "*.dld"); - gtk_file_filter_add_pattern(filter, "*.DLD"); -#endif -#ifdef SQLITE3 - gtk_file_filter_add_pattern(filter, "*.DB"); - gtk_file_filter_add_pattern(filter, "*.db"); -#endif - - gtk_file_filter_add_mime_type(filter, "text/xml"); - gtk_file_filter_set_name(filter, _("XML file")); - - return filter; -} - -static void file_save_as(GtkWidget *w, gpointer data) -{ - GtkWidget *dialog; - char *filename = NULL; - char *current_file; - char *current_dir; - - dialog = gtk_file_chooser_dialog_new(_("Save File As"), - GTK_WINDOW(main_window), - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); - - if (existing_filename) { - current_dir = g_path_get_dirname(existing_filename); - current_file = g_path_get_basename(existing_filename); - } else { - const char *current_default = prefs.default_filename; - current_dir = g_path_get_dirname(current_default); - current_file = g_path_get_basename(current_default); - } - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), current_dir); - gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), current_file); - - free(current_dir); - free(current_file); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - } - gtk_widget_destroy(dialog); - - if (filename){ - save_dives(filename); - set_filename(filename, TRUE); - g_free(filename); - mark_divelist_changed(FALSE); - } -} - -static void file_save(GtkWidget *w, gpointer data) -{ - const char *current_default; - - if (!existing_filename) - return file_save_as(w, data); - - current_default = prefs.default_filename; - if (strcmp(existing_filename, current_default) == 0) { - /* if we are using the default filename the directory - * that we are creating the file in may not exist */ - char *current_def_dir; - struct stat sb; - - current_def_dir = g_path_get_dirname(existing_filename); - if (stat(current_def_dir, &sb) != 0) { - g_mkdir(current_def_dir, S_IRWXU); - } - free(current_def_dir); - } - save_dives(existing_filename); - mark_divelist_changed(FALSE); -} - -static gboolean ask_save_changes() -{ - //WARNING: Porting to Qt - qDebug("This method is being ported to Qt, please, stop using it. "); - GtkWidget *dialog, *label, *content; - gboolean quit = TRUE; - dialog = gtk_dialog_new_with_buttons(_("Save Changes?"), - GTK_WINDOW(main_window), GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - GTK_STOCK_NO, GTK_RESPONSE_NO, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - NULL); - content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); - - if (!existing_filename){ - label = gtk_label_new ( - _("You have unsaved changes\nWould you like to save those before closing the datafile?")); - } else { - char *label_text = (char*) malloc(sizeof(char) * - (strlen(_("You have unsaved changes to file: %s \nWould you like to save those before closing the datafile?")) + - strlen(existing_filename))); - sprintf(label_text, - _("You have unsaved changes to file: %s \nWould you like to save those before closing the datafile?"), - existing_filename); - label = gtk_label_new (label_text); - free(label_text); - } - gtk_container_add (GTK_CONTAINER (content), label); - gtk_widget_show_all (dialog); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); - gint outcode = gtk_dialog_run(GTK_DIALOG(dialog)); - if (outcode == GTK_RESPONSE_ACCEPT) { - file_save(NULL,NULL); - } else if (outcode == GTK_RESPONSE_CANCEL || outcode == GTK_RESPONSE_DELETE_EVENT) { - quit = FALSE; - } - gtk_widget_destroy(dialog); - return quit; -} - -static void file_close(GtkWidget *w, gpointer data) -{ - qDebug("Calling an already ported-to-qt Gtk method"); - if (unsaved_changes()) - if (ask_save_changes() == FALSE) - return; - - if (existing_filename) - free((void *)existing_filename); - existing_filename = NULL; - - /* free the dives and trips */ - while (dive_table.nr) - delete_single_dive(0); - mark_divelist_changed(FALSE); - - /* clear the selection and the statistics */ - selected_dive = 0; - process_selected_dives(); - clear_stats_widgets(); - clear_events(); - show_dive_stats(NULL); - - /* clear the equipment page */ - clear_equipment_widgets(); - - /* redraw the screen */ - dive_list_update_dives(); - show_dive_info(NULL); -} - -//##################################################################### -//###### ALREAADY PORTED TO Qt. DELETE ME WHEN NOT MORE USERFUL. # -//##################################################################### -static void file_open(GtkWidget *w, gpointer data) -{ - qDebug("Calling an already ported-to-qt Gtk method."); - GtkWidget *dialog; - GtkFileFilter *filter; - const char *current_default; - - 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); - current_default = prefs.default_filename; - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), current_default); - /* when opening the data file we should allow only one file to be chosen */ - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); - - filter = setup_filter(); - gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - GSList *fn_glist; - char *filename; - - /* first, close the existing file, if any, and forget its name */ - file_close(w, data); - free((void *)existing_filename); - existing_filename = NULL; - - /* we know there is only one filename */ - fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); - - GError *error = NULL; - filename = (char *)fn_glist->data; - parse_file(filename, &error); - set_filename(filename, TRUE); - if (error != NULL) - { - report_error(error); - g_error_free(error); - error = NULL; - } - g_free(filename); - g_slist_free(fn_glist); - report_dives(FALSE, FALSE); - } - gtk_widget_destroy(dialog); -} - -void save_pane_position() -{ - gint vpane_position = gtk_paned_get_position(GTK_PANED(vpane)); - gint hpane_position = gtk_paned_get_position(GTK_PANED(hpane)); - gboolean is_maximized = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(main_window))) & - GDK_WINDOW_STATE_MAXIMIZED; - - if (pane_conf == PANE_THREE){ - if (is_maximized) { - subsurface_set_conf_int("vpane_position_maximized", vpane_position); - subsurface_set_conf_int("hpane_position_maximized", hpane_position); - } else { - subsurface_set_conf_int("vpane_position", vpane_position); - subsurface_set_conf_int("hpane_position", hpane_position); - } - } -} - -/* Since we want direct control of what set of parameters to retrive, this function - * has an input argument. */ -void restore_pane_position(gboolean maximized) -{ - if (maximized) { - gtk_paned_set_position(GTK_PANED(vpane), subsurface_get_conf_int("vpane_position_maximized")); - gtk_paned_set_position(GTK_PANED(hpane), subsurface_get_conf_int("hpane_position_maximized")); - } else { - gtk_paned_set_position(GTK_PANED(vpane), subsurface_get_conf_int("vpane_position")); - gtk_paned_set_position(GTK_PANED(hpane), subsurface_get_conf_int("hpane_position")); - } -} - -void save_window_geometry(void) -{ - /* GDK_GRAVITY_NORTH_WEST assumed ( it is the default ) */ - int window_width, window_height; - gboolean is_maximized; - - gtk_window_get_size(GTK_WINDOW(main_window), &window_width, &window_height); - is_maximized = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(main_window))) & - GDK_WINDOW_STATE_MAXIMIZED; - subsurface_set_conf_int("window_width", window_width); - subsurface_set_conf_int("window_height", window_height); - subsurface_set_conf_bool("window_maximized", is_maximized); - save_pane_position(); - subsurface_flush_conf(); -} - -void restore_window_geometry(void) -{ - int window_width, window_height; - gboolean is_maximized = subsurface_get_conf_bool("window_maximized") > 0; - - window_height = subsurface_get_conf_int("window_height"); - window_width = subsurface_get_conf_int("window_width"); - - window_height == -1 ? window_height = 300 : window_height; - window_width == -1 ? window_width = 700 : window_width; - - restore_pane_position(is_maximized); - /* don't resize the window if in maximized state */ - if (is_maximized) - gtk_window_maximize(GTK_WINDOW(main_window)); - else - gtk_window_resize(GTK_WINDOW(main_window), window_width, window_height); -} - -gboolean on_delete(GtkWidget* w, gpointer data) -{ - /* Make sure to flush any modified dive data */ - update_dive(NULL); - - gboolean quit = TRUE; - if (unsaved_changes()) - quit = ask_save_changes(); - - if (quit){ - save_window_geometry(); - return FALSE; /* go ahead, kill the program, we're good now */ - } else { - return TRUE; /* We are not leaving */ - } -} - -static void on_destroy(GtkWidget* w, gpointer data) -{ - dive_list_destroy(); - info_widget_destroy(); - gtk_main_quit(); -} - -/* This "window-state-event" callback will be called after the actual action, such - * as maximize or restore. This means that if you have methods here that check - * for the current window state, they will obtain the already updated state... */ -static gboolean on_state(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data) -{ - gint vpane_position, hpane_position; - if (event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN || - event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) - return TRUE; /* do nothing if the window is shown for the first time or minimized */ - if (pane_conf == PANE_THREE) { - hpane_position = gtk_paned_get_position(GTK_PANED(hpane)); - vpane_position = gtk_paned_get_position(GTK_PANED(vpane)); - if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { /* maximize */ - subsurface_set_conf_int("vpane_position", vpane_position); - subsurface_set_conf_int("hpane_position", hpane_position); - restore_pane_position(TRUE); - } else if (event->new_window_state == 0) { /* restore */ - subsurface_set_conf_int("vpane_position_maximized", vpane_position); - subsurface_set_conf_int("hpane_position_maximized", hpane_position); - restore_pane_position(FALSE); - } - } - return TRUE; -} - -static void quit(GtkWidget *w, gpointer data) -{ - /* Make sure to flush any modified dive data */ - update_dive(NULL); - - gboolean quit = TRUE; - if (unsaved_changes()) - quit = ask_save_changes(); - - if (quit){ - save_window_geometry(); - dive_list_destroy(); - gtk_main_quit(); - } -} - -GtkTreeViewColumn *tree_view_column_add_pixbuf(GtkWidget *tree_view, data_func_t data_func, GtkTreeViewColumn *col) -{ - GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(col, renderer, FALSE); - gtk_tree_view_column_set_cell_data_func(col, renderer, data_func, NULL, NULL); - g_signal_connect(tree_view, "button-press-event", G_CALLBACK(icon_click_cb), col); - return col; -} - -GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, - data_func_t data_func, unsigned int flags) -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *col; - double xalign = 0.0; /* left as default */ - PangoAlignment align; - gboolean visible; - - align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT : - (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT : - PANGO_ALIGN_CENTER; - visible = !(flags & INVISIBLE); - - renderer = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new(); - - if (flags & EDITABLE) { - g_object_set(renderer, "editable", TRUE, NULL); - g_signal_connect(renderer, "edited", (GCallback) data_func, tree_view); - data_func = NULL; - } - - gtk_tree_view_column_set_title(col, title); - if (!(flags & UNSORTABLE)) - gtk_tree_view_column_set_sort_column_id(col, index); - gtk_tree_view_column_set_resizable(col, TRUE); - /* all but one column have only one renderer - so packing from the end - * makes no difference; for the location column we want to be able to - * prepend the icon in front of the text - so this works perfectly */ - gtk_tree_view_column_pack_end(col, renderer, TRUE); - if (data_func) - gtk_tree_view_column_set_cell_data_func(col, renderer, data_func, (void *)(long)index, NULL); - else - gtk_tree_view_column_add_attribute(col, renderer, "text", index); - switch (align) { - case PANGO_ALIGN_LEFT: - xalign = 0.0; - break; - case PANGO_ALIGN_CENTER: - xalign = 0.5; - break; - case PANGO_ALIGN_RIGHT: - xalign = 1.0; - break; - } - gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5); - gtk_tree_view_column_set_visible(col, visible); - gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col); - return col; -} - -/* Helper functions for gtk combo boxes */ -GtkEntry *get_entry(GtkComboBox *combo_box) -{ - return GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_box))); -} - -const char *get_active_text(GtkComboBox *combo_box) -{ - return gtk_entry_get_text(get_entry(combo_box)); -} - -void set_active_text(GtkComboBox *combo_box, const char *text) -{ - gtk_entry_set_text(get_entry(combo_box), text); -} - -GtkWidget *combo_box_with_model_and_entry(GtkListStore *model) -{ - GtkWidget *widget; - GtkEntryCompletion *completion; - -#if GTK_CHECK_VERSION(2,24,0) - widget = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model)); - gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(widget), 0); -#else - widget = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0); - gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(widget), 0); -#endif - - completion = gtk_entry_completion_new(); - gtk_entry_completion_set_text_column(completion, 0); - gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(model)); - gtk_entry_completion_set_inline_completion(completion, TRUE); - gtk_entry_completion_set_inline_selection(completion, TRUE); - gtk_entry_completion_set_popup_single_match(completion, FALSE); - gtk_entry_set_completion(get_entry(GTK_COMBO_BOX(widget)), completion); - g_object_unref(completion); - - return widget; -} - -static void create_radio(GtkWidget *vbox, const char *w_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(w_name); - gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0); - - va_start(args, w_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); -} - -void update_screen() -{ - update_dive_list_units(); - repaint_dive(); - update_dive_list_col_visibility(); -} - -UNITCALLBACK(set_meter, length, units::METERS) -UNITCALLBACK(set_feet, length, units::FEET) -UNITCALLBACK(set_bar, pressure, units::BAR) -UNITCALLBACK(set_psi, pressure, units::PSI) -UNITCALLBACK(set_liter, volume, units::LITER) -UNITCALLBACK(set_cuft, volume, units::CUFT) -UNITCALLBACK(set_celsius, temperature, units::CELSIUS) -UNITCALLBACK(set_fahrenheit, temperature, units::FAHRENHEIT) -UNITCALLBACK(set_kg, weight, units::KG) -UNITCALLBACK(set_lbs, weight, units::LBS) - -OPTIONCALLBACK(otu_toggle, prefs.visible_cols.otu) -OPTIONCALLBACK(maxcns_toggle, prefs.visible_cols.maxcns) -OPTIONCALLBACK(sac_toggle, prefs.visible_cols.sac) -OPTIONCALLBACK(nitrox_toggle, prefs.visible_cols.nitrox) -OPTIONCALLBACK(temperature_toggle, prefs.visible_cols.temperature) -OPTIONCALLBACK(totalweight_toggle, prefs.visible_cols.totalweight) -OPTIONCALLBACK(suit_toggle, prefs.visible_cols.suit) -OPTIONCALLBACK(cylinder_toggle, prefs.visible_cols.cylinder) -OPTIONCALLBACK(po2_toggle, prefs.pp_graphs.po2) -OPTIONCALLBACK(pn2_toggle, prefs.pp_graphs.pn2) -OPTIONCALLBACK(phe_toggle, prefs.pp_graphs.phe) -OPTIONCALLBACK(mod_toggle, prefs.mod) -OPTIONCALLBACK(ead_toggle, prefs.ead) -OPTIONCALLBACK(red_ceiling_toggle, prefs.profile_red_ceiling) -OPTIONCALLBACK(calc_ceiling_toggle, prefs.profile_calc_ceiling) -OPTIONCALLBACK(calc_ceiling_3m_toggle, prefs.calc_ceiling_3m_incr) - -static gboolean gflow_edit(GtkWidget *w, GdkEvent *event, gpointer _data) -{ - double gflow; - const char *buf; - if (event->type == GDK_FOCUS_CHANGE) { - buf = gtk_entry_get_text(GTK_ENTRY(w)); - sscanf(buf, "%lf", &gflow); - prefs.gflow = gflow / 100.0; - set_gf(prefs.gflow, -1.0); - update_screen(); - } - return FALSE; -} - -static gboolean gfhigh_edit(GtkWidget *w, GdkEvent *event, gpointer _data) -{ - double gfhigh; - const char *buf; - if (event->type == GDK_FOCUS_CHANGE) { - buf = gtk_entry_get_text(GTK_ENTRY(w)); - sscanf(buf, "%lf", &gfhigh); - prefs.gfhigh = gfhigh / 100.0; - set_gf(-1.0, prefs.gfhigh); - update_screen(); - } - return FALSE; -} - -static void event_toggle(GtkWidget *w, gpointer _data) -{ - gboolean *plot_ev = (gboolean *)_data; - - *plot_ev = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); -} - -static void pick_default_file(GtkWidget *w, GtkButton *button) -{ - GtkWidget *fs_dialog, *parent; - const char *current_default; - char *current_def_file, *current_def_dir; - GtkFileFilter *filter; - struct stat sb; - - fs_dialog = gtk_file_chooser_dialog_new(_("Choose Default XML File"), - GTK_WINDOW(main_window), - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - NULL); - parent = gtk_widget_get_ancestor(w, GTK_TYPE_DIALOG); - gtk_widget_set_sensitive(parent, FALSE); - gtk_window_set_transient_for(GTK_WINDOW(fs_dialog), GTK_WINDOW(parent)); - - current_default = prefs.default_filename; - current_def_dir = g_path_get_dirname(current_default); - current_def_file = g_path_get_basename(current_default); - - /* it's possible that the directory doesn't exist (especially for the default) - * For gtk's file select box to make sense we create it */ - if (stat(current_def_dir, &sb) != 0) - g_mkdir(current_def_dir, S_IRWXU); - - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs_dialog), current_def_dir); - gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fs_dialog), current_def_file); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(fs_dialog), FALSE); - filter = setup_filter(); - gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fs_dialog), filter); - gtk_widget_show_all(fs_dialog); - if (gtk_dialog_run(GTK_DIALOG(fs_dialog)) == GTK_RESPONSE_ACCEPT) { - GSList *list; - - list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(fs_dialog)); - if (g_slist_length(list) == 1) - gtk_button_set_label(button, (const gchar *)list->data); - g_slist_free(list); - } - - free(current_def_dir); - free(current_def_file); - gtk_widget_destroy(fs_dialog); - - gtk_widget_set_sensitive(parent, TRUE); -} - -#if HAVE_OSM_GPS_MAP -static GtkWidget * map_provider_widget() -{ - int i; -#if GTK_CHECK_VERSION(2,24,0) - GtkWidget *combobox = gtk_combo_box_text_new(); - - /* several of the providers seem to be redundant or non-functional; - * we may have to skip more than just the last three eventually */ - for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i < OSM_GPS_MAP_SOURCE_LAST; i++) - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), osm_gps_map_source_get_friendly_name((OsmGpsMapSource_t)i)); -#else - GtkWidget *combobox = gtk_combo_box_new_text(); - for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i < OSM_GPS_MAP_SOURCE_LAST; i++) - gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), osm_gps_map_source_get_friendly_name(i)); -#endif - /* we don't offer choice 0 (none), so the index here is off by one */ - gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), prefs.map_provider - 1); - return combobox; -} -#endif - -static void preferences_dialog(GtkWidget *w, gpointer data) -{ - int result; - GtkWidget *dialog, *notebook, *font, *frame, *box, *hbox, *vbox, *button; - GtkWidget *xmlfile_button; -#if HAVE_OSM_GPS_MAP - GtkWidget *map_provider; -#endif - GtkWidget *entry_po2, *entry_pn2, *entry_phe, *entry_mod, *entry_gflow, *entry_gfhigh; - const char *current_default, *new_default; - char threshold_text[10], mod_text[10], utf8_buf[128]; - struct preferences oldprefs = prefs; - - 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_CANCEL, - NULL); - - /* create the notebook for the preferences and attach it to dialog */ - notebook = gtk_notebook_new(); - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - gtk_box_pack_start(GTK_BOX(vbox), notebook, FALSE, FALSE, 5); - - /* vbox that holds the first notebook page */ - vbox = gtk_vbox_new(FALSE, 6); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, - gtk_label_new(_("General Settings"))); - frame = gtk_frame_new(_("Units")); - gtk_box_pack_start(GTK_BOX(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, (prefs.units.length == units::METERS), - _("Feet"), set_feet, (prefs.units.length == units::FEET), - NULL); - - create_radio(box, _("Pressure:"), - _("Bar"), set_bar, (prefs.units.pressure == units::BAR), - _("PSI"), set_psi, (prefs.units.pressure == units::PSI), - NULL); - - create_radio(box, _("Volume:"), - _("Liter"), set_liter, (prefs.units.volume == units::LITER), - _("CuFt"), set_cuft, (prefs.units.volume == units::CUFT), - NULL); - - create_radio(box, _("Temperature:"), - _("Celsius"), set_celsius, (prefs.units.temperature == units::CELSIUS), - _("Fahrenheit"), set_fahrenheit, (prefs.units.temperature == units::FAHRENHEIT), - NULL); - - create_radio(box, _("Weight:"), - _("kg"), set_kg, (prefs.units.weight == units::KG), - _("lbs"), set_lbs, (prefs.units.weight == units::LBS), - NULL); - - frame = gtk_frame_new(_("Show Columns")); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(frame), box); - - button = gtk_check_button_new_with_label(_("Temp")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.temperature); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(temperature_toggle), NULL); - - button = gtk_check_button_new_with_label(_("Cyl")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.cylinder); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(cylinder_toggle), NULL); - - button = gtk_check_button_new_with_label("O" UTF8_SUBSCRIPT_2 "%"); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.nitrox); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(nitrox_toggle), NULL); - - button = gtk_check_button_new_with_label(_("SAC")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.sac); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(sac_toggle), NULL); - - button = gtk_check_button_new_with_label(_("Weight")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.totalweight); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(totalweight_toggle), NULL); - - button = gtk_check_button_new_with_label(_("Suit")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.suit); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL); - - frame = gtk_frame_new(_("Divelist Font")); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); - - font = gtk_font_button_new_with_font(prefs.divelist_font); - gtk_container_add(GTK_CONTAINER(frame),font); - - frame = gtk_frame_new(_("Misc. Options")); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(frame), box); - - frame = gtk_frame_new(_("Default XML Data File")); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5); - hbox = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(frame), hbox); - current_default = prefs.default_filename; - xmlfile_button = gtk_button_new_with_label(current_default); - g_signal_connect(G_OBJECT(xmlfile_button), "clicked", - G_CALLBACK(pick_default_file), xmlfile_button); - gtk_box_pack_start(GTK_BOX(hbox), xmlfile_button, FALSE, FALSE, 6); -#if HAVE_OSM_GPS_MAP - frame = gtk_frame_new(_("Map provider")); - map_provider = map_provider_widget(); - gtk_container_add(GTK_CONTAINER(frame), map_provider); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 3); -#endif - /* vbox that holds the second notebook page */ - vbox = gtk_vbox_new(FALSE, 6); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, - gtk_label_new(_("Tec Settings"))); - - frame = gtk_frame_new(_("Show Columns")); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(frame), box); - - button = gtk_check_button_new_with_label(_("OTU")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.otu); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL); - - button = gtk_check_button_new_with_label(_("maxCNS")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.maxcns); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(maxcns_toggle), NULL); - - frame = gtk_frame_new(_("Profile Settings")); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); - - vbox = gtk_vbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(frame), vbox); - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(vbox), box); - - sprintf(utf8_buf, _("Show pO%s graph"), UTF8_SUBSCRIPT_2); - button = gtk_check_button_new_with_label(utf8_buf); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.po2); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(po2_toggle), &entry_po2); - - sprintf(utf8_buf, _("pO%s threshold"), UTF8_SUBSCRIPT_2); - frame = gtk_frame_new(utf8_buf); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); - entry_po2 = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry_po2), 4); - snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.po2_threshold); - gtk_entry_set_text(GTK_ENTRY(entry_po2), threshold_text); - gtk_widget_set_sensitive(entry_po2, prefs.pp_graphs.po2); - gtk_container_add(GTK_CONTAINER(frame), entry_po2); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(vbox), box); - - sprintf(utf8_buf, _("Show pN%s graph"), UTF8_SUBSCRIPT_2); - button = gtk_check_button_new_with_label(utf8_buf); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.pn2); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(pn2_toggle), &entry_pn2); - - sprintf(utf8_buf, _("pN%s threshold"), UTF8_SUBSCRIPT_2); - frame = gtk_frame_new(utf8_buf); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); - entry_pn2 = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry_pn2), 4); - snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.pn2_threshold); - gtk_entry_set_text(GTK_ENTRY(entry_pn2), threshold_text); - gtk_widget_set_sensitive(entry_pn2, prefs.pp_graphs.pn2); - gtk_container_add(GTK_CONTAINER(frame), entry_pn2); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(vbox), box); - - button = gtk_check_button_new_with_label(_("Show pHe graph")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.phe); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(phe_toggle), &entry_phe); - - frame = gtk_frame_new(_("pHe threshold")); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); - entry_phe = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry_phe), 4); - snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.phe_threshold); - gtk_entry_set_text(GTK_ENTRY(entry_phe), threshold_text); - gtk_widget_set_sensitive(entry_phe, prefs.pp_graphs.phe); - gtk_container_add(GTK_CONTAINER(frame), entry_phe); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(vbox), box); - - button = gtk_check_button_new_with_label(_("Show MOD")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.mod); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(mod_toggle), &entry_mod); - - frame = gtk_frame_new(_("max ppO2")); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); - entry_mod = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry_mod), 4); - snprintf(mod_text, sizeof(mod_text), "%.1f", prefs.mod_ppO2); - gtk_entry_set_text(GTK_ENTRY(entry_mod), mod_text); - gtk_widget_set_sensitive(entry_mod, prefs.mod); - gtk_container_add(GTK_CONTAINER(frame), entry_mod); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(vbox), box); - - button = gtk_check_button_new_with_label(_("Show EAD, END, EADD")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.ead); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(ead_toggle), NULL); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(vbox), box); - - button = gtk_check_button_new_with_label(_("Show dc reported ceiling in red")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_red_ceiling); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(red_ceiling_toggle), NULL); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(vbox), box); - - button = gtk_check_button_new_with_label(_("Show calculated ceiling")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_calc_ceiling); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_toggle), NULL); - - button = gtk_check_button_new_with_label(_("3m increments for calculated ceiling")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.calc_ceiling_3m_incr); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_3m_toggle), NULL); - - box = gtk_hbox_new(FALSE, 6); - gtk_container_add(GTK_CONTAINER(vbox), box); - - frame = gtk_frame_new(_("GFlow")); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); - entry_gflow = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry_gflow), 4); - snprintf(threshold_text, sizeof(threshold_text), "%.0f", prefs.gflow * 100); - gtk_entry_set_text(GTK_ENTRY(entry_gflow), threshold_text); - gtk_container_add(GTK_CONTAINER(frame), entry_gflow); - gtk_widget_add_events(entry_gflow, GDK_FOCUS_CHANGE_MASK); - g_signal_connect(G_OBJECT(entry_gflow), "event", G_CALLBACK(gflow_edit), NULL); - - frame = gtk_frame_new(_("GFhigh")); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); - entry_gfhigh = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry_gfhigh), 4); - snprintf(threshold_text, sizeof(threshold_text), "%.0f", prefs.gfhigh * 100); - gtk_entry_set_text(GTK_ENTRY(entry_gfhigh), threshold_text); - gtk_container_add(GTK_CONTAINER(frame), entry_gfhigh); - gtk_widget_add_events(entry_gfhigh, GDK_FOCUS_CHANGE_MASK); - g_signal_connect(G_OBJECT(entry_gfhigh), "event", G_CALLBACK(gfhigh_edit), NULL); - - gtk_widget_show_all(dialog); - result = gtk_dialog_run(GTK_DIALOG(dialog)); - if (result == GTK_RESPONSE_ACCEPT) { - const char *po2_threshold_text, *pn2_threshold_text, *phe_threshold_text, *mod_text, *gflow_text, *gfhigh_text; - /* Make sure to flush any modified old dive data with old units */ - update_dive(NULL); - - prefs.divelist_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font))); - set_divelist_font(prefs.divelist_font); - po2_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_po2)); - sscanf(po2_threshold_text, "%lf", &prefs.pp_graphs.po2_threshold); - pn2_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_pn2)); - sscanf(pn2_threshold_text, "%lf", &prefs.pp_graphs.pn2_threshold); - phe_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_phe)); - sscanf(phe_threshold_text, "%lf", &prefs.pp_graphs.phe_threshold); - mod_text = gtk_entry_get_text(GTK_ENTRY(entry_mod)); - sscanf(mod_text, "%lf", &prefs.mod_ppO2); - gflow_text = gtk_entry_get_text(GTK_ENTRY(entry_gflow)); - sscanf(gflow_text, "%lf", &prefs.gflow); - gfhigh_text = gtk_entry_get_text(GTK_ENTRY(entry_gfhigh)); - sscanf(gfhigh_text, "%lf", &prefs.gfhigh); - prefs.gflow /= 100.0; - prefs.gfhigh /= 100.0; - set_gf(prefs.gflow, prefs.gfhigh); - - update_screen(); - - new_default = strdup(gtk_button_get_label(GTK_BUTTON(xmlfile_button))); - - /* if we opened the default file and are changing its name, - * update existing_filename */ - if (existing_filename) { - if (strcmp(current_default, existing_filename) == 0) { - free((void *)existing_filename); - existing_filename = strdup(new_default); - } - } - if (strcmp(current_default, new_default)) { - prefs.default_filename = new_default; - } -#if HAVE_OSM_GPS_MAP - /* get the map provider selected */ - int i; -#if GTK_CHECK_VERSION(2,24,0) - char *provider = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(map_provider)); -#else - char *provider = gtk_combo_box_get_active_text(GTK_COMBO_BOX(map_provider)); -#endif - for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i <= OSM_GPS_MAP_SOURCE_YAHOO_STREET; i++) - if (!strcmp(provider,osm_gps_map_source_get_friendly_name((OsmGpsMapSource_t)i))) { - prefs.map_provider = i; - break; - } - free((void *)provider); -#endif - save_preferences(); - } else if (result == GTK_RESPONSE_CANCEL) { - prefs = oldprefs; - set_gf(prefs.gflow, prefs.gfhigh); - update_screen(); - } - gtk_widget_destroy(dialog); -} - -static void create_toggle(const char* label, int *on, void *_data) -{ - GtkWidget *button, *table = GTK_WIDGET(_data); - int rows, cols, x, y; - static int count; - - if (table == NULL) { - /* magic way to reset the number of toggle buttons - * that we have already added - call this before you - * create the dialog */ - count = 0; - return; - } - g_object_get(G_OBJECT(table), "n-columns", &cols, "n-rows", &rows, NULL); - if (count > rows * cols) { - gtk_table_resize(GTK_TABLE(table),rows+1,cols); - rows++; - } - x = count % cols; - y = count / cols; - button = gtk_check_button_new_with_label(label); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *on); - gtk_table_attach_defaults(GTK_TABLE(table), button, x, x+1, y, y+1); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(event_toggle), on); - count++; -} - -static void selectevents_dialog(GtkWidget *w, gpointer data) -{ - int result; - GtkWidget *dialog, *frame, *vbox, *table, *label; - - dialog = gtk_dialog_new_with_buttons(_("Select Events"), - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - /* initialize the function that fills the table */ - create_toggle(NULL, NULL, NULL); - - frame = gtk_frame_new(_("Enable / Disable Events")); - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); - - table = gtk_table_new(1, 4, TRUE); - if (!evn_foreach(&create_toggle, table)) { - g_object_ref_sink(G_OBJECT(table)); - label = gtk_label_new(_("\nNo Events\n")); - gtk_container_add(GTK_CONTAINER(frame), label); - } else { - gtk_container_add(GTK_CONTAINER(frame), table); - } - - gtk_widget_show_all(dialog); - result = gtk_dialog_run(GTK_DIALOG(dialog)); - if (result == GTK_RESPONSE_ACCEPT) { - repaint_dive(); - } - gtk_widget_destroy(dialog); -} - -static void autogroup_cb(GtkWidget *w, gpointer data) -{ - autogroup = !autogroup; - if (! autogroup) - remove_autogen_trips(); - dive_list_update_dives(); -} - -void set_autogroup(gboolean value) -{ - GtkAction *autogroup_action; - - if (value == autogroup) - return; - - autogroup_action = gtk_action_group_get_action(action_group, "Autogroup"); - gtk_action_activate(autogroup_action); -} - -static void renumber_dialog(GtkWidget *w, gpointer data) -{ - int result; - struct dive *dive; - GtkWidget *dialog, *frame, *button, *vbox; - - 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); - - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - - frame = gtk_frame_new(_("New starting number")); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); - - button = gtk_spin_button_new_with_range(1, 50000, 1); - gtk_container_add(GTK_CONTAINER(frame), button); - - /* - * Do we have a number for the first dive already? Use that - * as the default. - */ - dive = get_dive(0); - if (dive && dive->number) - gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), dive->number); - - 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 void about_dialog_link_cb(GtkAboutDialog *dialog, const gchar *link, gpointer data) -{ - subsurface_launch_for_uri(link); -} - -static void about_dialog(GtkWidget *w, gpointer data) -{ - const char *logo_property = NULL; - GdkPixbuf *logo = NULL; - GtkWidget *dialog; - - if (need_icon) { - logo_property = "logo"; - logo = gdk_pixbuf_from_pixdata(&subsurface_icon_pixbuf, TRUE, NULL); - } - dialog = gtk_about_dialog_new(); -#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 24) - gtk_about_dialog_set_url_hook(about_dialog_link_cb, NULL, NULL); /* deprecated since GTK 2.24 */ -#else - g_signal_connect(GTK_ABOUT_DIALOG(dialog), "activate-link", G_CALLBACK(about_dialog_link_cb), NULL); -#endif - g_object_set(GTK_OBJECT(dialog), - "title", _("About Subsurface"), - "program-name", "Subsurface", - "comments", _("Multi-platform divelog software in C"), - "website", "http://subsurface.hohndel.org", - "license", "GNU General Public License, version 2\nhttp://www.gnu.org/licenses/old-licenses/gpl-2.0.html", - "version", VERSION_STRING, - "copyright", _("Linus Torvalds, Dirk Hohndel, and others, 2011, 2012, 2013"), - /*++GETTEXT the term translator-credits is magic - list the names of the tranlators here */ - "translator_credits", _("translator-credits"), - "logo-icon-name", "subsurface", - /* Must be last: */ - logo_property, logo, - NULL); - if (logo) - g_object_unref(logo); - gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); -} - -static void show_user_manual(GtkWidget *w, gpointer data) -{ - subsurface_launch_for_uri("http://subsurface.hohndel.org/documentation/user-manual/"); -} - -static void view_list(GtkWidget *w, gpointer data) -{ - save_pane_position(); - gtk_paned_set_position(GTK_PANED(vpane), 0); - pane_conf = PANE_LIST; -} - -static void view_profile(GtkWidget *w, gpointer data) -{ - save_pane_position(); - gtk_paned_set_position(GTK_PANED(hpane), 0); - gtk_paned_set_position(GTK_PANED(vpane), 65535); - pane_conf = PANE_PROFILE; -} - -static void view_info(GtkWidget *w, gpointer data) -{ - - save_pane_position(); - gtk_paned_set_position(GTK_PANED(vpane), 65535); - gtk_paned_set_position(GTK_PANED(hpane), 65535); - pane_conf = PANE_INFO; -} - -static void view_three(GtkWidget *w, gpointer data) -{ - GtkAllocation alloc; - GtkRequisition requisition; - - int vpane_position = subsurface_get_conf_int("vpane_position"); - int hpane_position = subsurface_get_conf_int("hpane_position"); - - gtk_widget_get_allocation(hpane, &alloc); - - if (hpane_position) - gtk_paned_set_position(GTK_PANED(hpane), hpane_position); - else - gtk_paned_set_position(GTK_PANED(hpane), alloc.width/2); - - gtk_widget_get_allocation(vpane, &alloc); - gtk_widget_size_request(notebook, &requisition); - /* pick the requested size for the notebook plus 6 pixels for frame */ - if (vpane_position) - gtk_paned_set_position(GTK_PANED(vpane), vpane_position); - else - gtk_paned_set_position(GTK_PANED(vpane), requisition.height + 6); - - pane_conf = PANE_THREE; -} - -static void toggle_zoom(GtkWidget *w, gpointer data) -{ - zoomed_plot = (zoomed_plot)?0 : 1; - /*Update dive*/ - repaint_dive(); -} - -static void prev_dc(GtkWidget *w, gpointer data) -{ - dc_number--; - /* If the dc number underflows, we'll "wrap around" and use the last dc */ - repaint_dive(); -} - -static void next_dc(GtkWidget *w, gpointer data) -{ - dc_number++; - /* If the dc number overflows, we'll "wrap around" and zero it */ - repaint_dive(); -} - - -/* list columns for nickname edit treeview */ -enum { - NE_MODEL, - NE_ID_STR, - NE_NICKNAME, - NE_NCOL -}; - -/* delete a selection of nicknames */ -static void edit_dc_delete_rows(GtkTreeView *view) -{ - GtkTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeRowReference *ref; - GtkTreeSelection *selection; - GList *selected_rows, *list, *row_references = NULL; - guint len; - /* params for delete op */ - const char *model_str; - const char *deviceid_string; /* convert to deviceid */ - uint32_t deviceid; - - selection = gtk_tree_view_get_selection(view); - selected_rows = gtk_tree_selection_get_selected_rows(selection, &model); - - for (list = selected_rows; list; list = g_list_next(list)) { - path = (GtkTreePath *)list->data; - ref = gtk_tree_row_reference_new(model, path); - row_references = g_list_append(row_references, ref); - } - len = g_list_length(row_references); - if (len == 0) - /* Warn about empty selection? */ - return; - - for (list = row_references; list; list = g_list_next(list)) { - path = gtk_tree_row_reference_get_path((GtkTreeRowReference *)(list->data)); - gtk_tree_model_get_iter(model, &iter, path); - gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, - NE_MODEL, &model_str, - NE_ID_STR, &deviceid_string, - -1); - if (sscanf(deviceid_string, "0x%x8", &deviceid) == 1) - remove_dc(model_str, deviceid); - - gtk_list_store_remove(GTK_LIST_STORE (model), &iter); - gtk_tree_path_free(path); - } - g_list_free(selected_rows); - g_list_free(row_references); - g_list_free(list); - - if (gtk_tree_model_get_iter_first(model, &iter)) - gtk_tree_selection_select_iter(selection, &iter); -} - -/* repopulate the edited nickname cell of a DC */ -static void cell_edited_cb(GtkCellRendererText *cell, gchar *path, - gchar *new_text, gpointer store) -{ - GtkTreeIter iter; - const char *model; - const char *deviceid_string; - uint32_t deviceid; - int matched; - - gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path); - /* display new text */ - gtk_list_store_set(GTK_LIST_STORE(store), &iter, NE_NICKNAME, new_text, -1); - /* and new_text */ - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, - NE_MODEL, &model, - NE_ID_STR, &deviceid_string, - -1); - /* extract deviceid */ - matched = sscanf(deviceid_string, "0x%x8", &deviceid); - - /* remember pending commit - * Need to extend list rather than wipe and store only one result */ - if (matched == 1){ - if (holdnicknames == NULL){ - holdnicknames = (struct device_info *) malloc(sizeof(struct device_info)); - holdnicknames->model = strdup(model); - holdnicknames->deviceid = deviceid; - holdnicknames->serial_nr = NULL; - holdnicknames->firmware = NULL; - holdnicknames->nickname = strdup(new_text); - holdnicknames->next = NULL; - } else { - struct device_info * top; - struct device_info * last = holdnicknames; - top = (struct device_info *) malloc(sizeof(struct device_info)); - top->model = strdup(model); - top->deviceid = deviceid; - top->serial_nr = NULL; - top->firmware = NULL; - top->nickname = strdup(new_text); - top->next = last; - holdnicknames = top; - } - } -} - -#define SUB_RESPONSE_DELETE 1 /* no delete response in gtk+2 */ -#define SUB_DONE 2 /* enable escape when done */ - -/* show the dialog to edit dc nicknames */ -static void edit_dc_nicknames(GtkWidget *w, gpointer data) -{ - const gchar *C_INACTIVE = "#e8e8ee", *C_ACTIVE = "#ffffff"; /* cell colours */ - GtkWidget *dialog, *confirm, *view, *scroll, *vbox; - GtkCellRenderer *renderer; - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkListStore *store; - GtkTreeIter iter; - gint res = -1; - char id_string[11] = {0}; - struct device_info * nnl; - - dialog = gtk_dialog_new_with_buttons(_("Edit Dive Computer Nicknames"), - GTK_WINDOW(main_window), - GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), - GTK_STOCK_DELETE, - SUB_RESPONSE_DELETE, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - GTK_STOCK_APPLY, - GTK_RESPONSE_APPLY, - NULL); - gtk_widget_set_size_request(dialog, 700, 400); - - scroll = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - - view = gtk_tree_view_new(); - store = gtk_list_store_new(NE_NCOL, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); - model = GTK_TREE_MODEL(store); - - /* columns */ - renderer = gtk_cell_renderer_text_new(); - g_object_set(renderer, "background", C_INACTIVE, NULL); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, _("Model"), - renderer, "text", NE_MODEL, NULL); - - renderer = gtk_cell_renderer_text_new(); - g_object_set(renderer, "background", C_INACTIVE, NULL); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, _("Device Id"), - renderer, "text", NE_ID_STR, NULL); - - renderer = gtk_cell_renderer_text_new(); - g_object_set(renderer, "background", C_INACTIVE, NULL); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, _("Nickname"), - renderer, "text", NE_NICKNAME, NULL); - g_object_set(renderer, "editable", TRUE, NULL); - g_object_set(renderer, "background", C_ACTIVE, NULL); - g_signal_connect(renderer, "edited", G_CALLBACK(cell_edited_cb), store); - - gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); - g_object_unref(model); - - /* populate list store from device_info_list */ - nnl = head_of_device_info_list(); - while (nnl) { - sprintf(&id_string[0], "%#08x", nnl->deviceid); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - NE_MODEL, nnl->model, - NE_ID_STR, id_string, - NE_NICKNAME, nnl->nickname, - -1); - nnl = nnl->next; - } - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE); - - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - gtk_container_add(GTK_CONTAINER(scroll), view); - gtk_container_add(GTK_CONTAINER(vbox), - gtk_label_new(_("Edit a dive computer nickname by double-clicking the in the relevant nickname field"))); - gtk_container_add(GTK_CONTAINER(vbox), scroll); - gtk_widget_set_size_request(scroll, 500, 300); - gtk_widget_show_all(dialog); - - do { - res = gtk_dialog_run(GTK_DIALOG(dialog)); - if (res == SUB_RESPONSE_DELETE) { - confirm = gtk_dialog_new_with_buttons(_("Delete a dive computer information entry"), - GTK_WINDOW(dialog), - GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), - GTK_STOCK_YES, - GTK_RESPONSE_YES, - GTK_STOCK_NO, - GTK_RESPONSE_NO, - NULL); - gtk_widget_set_size_request(confirm, 350, 90); - vbox = gtk_dialog_get_content_area(GTK_DIALOG(confirm)); - gtk_container_add(GTK_CONTAINER(vbox), - gtk_label_new(_("Ok to delete the selected entry?"))); - gtk_widget_show_all(confirm); - if (gtk_dialog_run(GTK_DIALOG(confirm)) == GTK_RESPONSE_YES) { - edit_dc_delete_rows(GTK_TREE_VIEW(view)); - res = SUB_DONE; /* want to close ** both ** dialogs now */ - } - mark_divelist_changed(TRUE); - gtk_widget_destroy(confirm); - } - if (res == GTK_RESPONSE_APPLY && holdnicknames && holdnicknames->model != NULL) { - struct device_info * walk = holdnicknames; - struct device_info * release = holdnicknames; - struct device_info * track = holdnicknames->next; - while (walk) { - remember_dc(walk->model, walk->deviceid, walk->nickname); - walk = walk->next; - } - /* clear down list */ - while (release){ - free(release); - release = track; - if (track) - track = track->next; - } - holdnicknames = NULL; - mark_divelist_changed(TRUE); - } - } while (res != SUB_DONE && res != GTK_RESPONSE_CANCEL && res != GTK_RESPONSE_DELETE_EVENT && res != GTK_RESPONSE_APPLY); - gtk_widget_destroy(dialog); -} - -static GtkActionEntry menu_items[] = { - { "FileMenuAction", NULL, N_("File"), NULL, NULL, NULL}, - { "LogMenuAction", NULL, N_("Log"), NULL, NULL, NULL}, - { "ViewMenuAction", NULL, N_("View"), NULL, NULL, NULL}, - { "FilterMenuAction", NULL, N_("Filter"), NULL, NULL, NULL}, - { "PlannerMenuAction", NULL, N_("Planner"), NULL, NULL, NULL}, - { "HelpMenuAction", NULL, N_("Help"), NULL, NULL, NULL}, - { "NewFile", GTK_STOCK_NEW, N_("New"), CTRLCHAR "N", NULL, G_CALLBACK(file_close) }, - { "OpenFile", GTK_STOCK_OPEN, N_("Open..."), CTRLCHAR "O", NULL, G_CALLBACK(file_open) }, - { "SaveFile", GTK_STOCK_SAVE, N_("Save..."), CTRLCHAR "S", NULL, G_CALLBACK(file_save) }, - { "SaveAsFile", GTK_STOCK_SAVE_AS, N_("Save As..."), SHIFTCHAR CTRLCHAR "S", NULL, G_CALLBACK(file_save_as) }, - { "CloseFile", GTK_STOCK_CLOSE, N_("Close"), NULL, NULL, G_CALLBACK(file_close) }, - { "Print", GTK_STOCK_PRINT, N_("Print..."), CTRLCHAR "P", NULL, G_CALLBACK(do_print) }, - { "ImportFile", NULL, N_("Import File(s)..."), CTRLCHAR "I", NULL, G_CALLBACK(import_files) }, - { "ExportUDDF", NULL, N_("Export UDDF..."), NULL, NULL, G_CALLBACK(export_all_dives_uddf_cb) }, - { "DownloadLog", NULL, N_("Download From Dive Computer..."), CTRLCHAR "D", NULL, G_CALLBACK(download_dialog) }, - { "DownloadWeb", GTK_STOCK_CONNECT, N_("Download From Web Service..."), NULL, NULL, G_CALLBACK(webservice_download_dialog) }, - { "AddDive", GTK_STOCK_ADD, N_("Add Dive..."), NULL, NULL, G_CALLBACK(add_dive_cb) }, - { "Preferences", GTK_STOCK_PREFERENCES, N_("Preferences..."), PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) }, - { "Renumber", NULL, N_("Renumber..."), NULL, NULL, G_CALLBACK(renumber_dialog) }, - { "YearlyStats", NULL, N_("Yearly Statistics"), NULL, NULL, G_CALLBACK(show_yearly_stats) }, -#if HAVE_OSM_GPS_MAP - { "DivesLocations", NULL, N_("Dives Locations"), CTRLCHAR "M", NULL, G_CALLBACK(show_gps_locations) }, -#endif - { "SelectEvents", NULL, N_("Select Events..."), NULL, NULL, G_CALLBACK(selectevents_dialog) }, - { "Quit", GTK_STOCK_QUIT, N_("Quit"), CTRLCHAR "Q", NULL, G_CALLBACK(quit) }, - { "About", GTK_STOCK_ABOUT, N_("About Subsurface"), NULL, NULL, G_CALLBACK(about_dialog) }, - { "UserManual", GTK_STOCK_HELP, N_("User Manual"), NULL, NULL, G_CALLBACK(show_user_manual) }, - { "ViewList", NULL, N_("List"), CTRLCHAR "1", NULL, G_CALLBACK(view_list) }, - { "ViewProfile", NULL, N_("Profile"), CTRLCHAR "2", NULL, G_CALLBACK(view_profile) }, - { "ViewInfo", NULL, N_("Info"), CTRLCHAR "3", NULL, G_CALLBACK(view_info) }, - { "ViewThree", NULL, N_("Three"), CTRLCHAR "4", NULL, G_CALLBACK(view_three) }, - { "EditNames", NULL, N_("Edit Device Names"), CTRLCHAR "E", NULL, G_CALLBACK(edit_dc_nicknames) }, - { "PrevDC", NULL, N_("Prev DC"), NULL, NULL, G_CALLBACK(prev_dc) }, - { "NextDC", NULL, N_("Next DC"), NULL, NULL, G_CALLBACK(next_dc) }, - { "InputPlan", NULL, N_("Input Plan"), NULL, NULL, G_CALLBACK(input_plan) }, -}; -static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); - -static GtkToggleActionEntry toggle_items[] = { - { "Autogroup", NULL, N_("Autogroup"), NULL, NULL, G_CALLBACK(autogroup_cb), FALSE }, - { "ToggleZoom", NULL, N_("Toggle Zoom"), CTRLCHAR "0", NULL, G_CALLBACK(toggle_zoom), FALSE }, -}; -static gint ntoggle_items = sizeof (toggle_items) / sizeof (toggle_items[0]); - -static const gchar* ui_string = " \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - " -#if HAVE_OSM_GPS_MAP - " " -#endif - " \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -"; - -static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager) -{ - action_group = gtk_action_group_new("Menu"); - gtk_action_group_set_translation_domain(action_group, "subsurface"); - gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0); - toggle_items[0].is_active = autogroup; - gtk_action_group_add_toggle_actions(action_group, toggle_items, ntoggle_items, 0); - - 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(); -} - -static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, GtkWidget *divelist) -{ - if (event->type != GDK_KEY_PRESS || event->state != 0) - return FALSE; - switch (event->keyval) { - case GDK_Up: - select_prev_dive(); - return TRUE; - case GDK_Down: - select_next_dive(); - return TRUE; - case GDK_Left: - prev_dc(NULL, NULL); - return TRUE; - case GDK_Right: - next_dc(NULL, NULL); - return TRUE; - } - return FALSE; -} - -static gboolean notebook_tooltip (GtkWidget *widget, gint x, gint y, - gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data) -{ - if (amount_selected > 0 && gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)) == 0) { - gtk_tooltip_set_text(tooltip, _("To edit dive information\ndouble click on it in the dive list")); - return TRUE; - } else { - return FALSE; - } -} - -#if NEEDS_TO_MOVE_TO_QT_UI -/* this appears to have moved - but it's very different in qt-ui */ - -class MainWindow: public QMainWindow, private Ui::MainWindow -{ - Q_OBJECT - -public: - MainWindow(QWidget *parent = 0); - ~MainWindow() {} - - void setCurrentFileName(const QString &fileName); - -private Q_SLOTS: - void on_actionNew_triggered() { on_actionClose_triggered(); } - void on_actionOpen_triggered(); - void on_actionSave_triggered() { file_save(NULL, NULL); } - void on_actionSaveAs_triggered() { file_save_as(NULL, NULL); } - void on_actionClose_triggered(); - -private: - QStringList fileNameFilters() const; - -private: - QString m_currentFileName; -}; - -MainWindow::MainWindow(QWidget *parent): - QMainWindow(parent) -{ - setupUi(this); -} - -void MainWindow::setCurrentFileName(const QString &fileName) -{ - if (fileName == m_currentFileName) return; - m_currentFileName = fileName; - - QString title = tr("Subsurface"); - if (!m_currentFileName.isEmpty()) { - QFileInfo fileInfo(m_currentFileName); - title += " - " + fileInfo.fileName(); - } - setWindowTitle(title); -} - -void MainWindow::on_actionOpen_triggered() -{ - QString defaultFileName = prefs.default_filename; - QFileInfo fileInfo(defaultFileName); - - QFileDialog dialog(this, tr("Open File"), fileInfo.path()); - dialog.setFileMode(QFileDialog::ExistingFile); - dialog.selectFile(defaultFileName); - dialog.setNameFilters(fileNameFilters()); - if (dialog.exec()) { - /* first, close the existing file, if any */ - file_close(NULL, NULL); - - /* we know there is only one filename */ - QString fileName = dialog.selectedFiles().first(); - GError *error = NULL; - parse_file(fileName.toUtf8().constData(), &error); - if (error != NULL) { - report_error(error); - g_error_free(error); - error = NULL; - } else { - setCurrentFileName(fileName); - } - report_dives(FALSE, FALSE); - } -} - -void MainWindow::on_actionClose_triggered() -{ - if (unsaved_changes()) - if (ask_save_changes() == FALSE) - return; - - setCurrentFileName(QString()); - - /* free the dives and trips */ - while (dive_table.nr) - delete_single_dive(0); - mark_divelist_changed(FALSE); - - /* clear the selection and the statistics */ - selected_dive = 0; - process_selected_dives(); - clear_stats_widgets(); - clear_events(); - show_dive_stats(NULL); - - /* clear the equipment page */ - clear_equipment_widgets(); - - /* redraw the screen */ - dive_list_update_dives(); - show_dive_info(NULL); -} - -QStringList MainWindow::fileNameFilters() const -{ - QStringList filters; - - filters << "*.xml *.uddf *.udcf *.jlb" -#ifdef LIBZIP - " *.sde *.dld" -#endif -#ifdef SQLITE3 - " *.db" -#endif - ; - return filters; -} -#endif /* NEEDS_TO_MOVE_TO_QT_UI */ - void init_qt_ui(int *argcp, char ***argvp) { application->installTranslator(new Translator(application)); @@ -1884,35 +75,6 @@ void init_ui(int *argcp, char ***argvp) QTextCodec::setCodecForCStrings(QTextCodec::codecForMib(106)); #endif - GtkWidget *win; - GtkWidget *nb_page; - GtkWidget *dive_list; - GtkWidget *menubar; - GtkWidget *vbox; - GtkWidget *scrolled; - GdkScreen *screen; - GtkIconTheme *icon_theme=NULL; - GtkSettings *settings; - GtkUIManager *ui_manager; - - gtk_init(argcp, argvp); - settings = gtk_settings_get_default(); - gtk_settings_set_long_property(settings, "gtk-tooltip-timeout", 10, "subsurface setting"); - gtk_settings_set_long_property(settings, "gtk-menu-images", 1, "subsurface setting"); - gtk_settings_set_long_property(settings, "gtk-button-images", 1, "subsurface setting"); - - /* check if utf8 stars are available as a default OS feature */ - if (!subsurface_os_feature_available(UTF8_FONT_WITH_STARS)) { - star_strings[0] = " "; - star_strings[1] = "* "; - star_strings[2] = "** "; - star_strings[3] = "*** "; - star_strings[4] = "**** "; - star_strings[5] = "*****"; - } -#if !GLIB_CHECK_VERSION(2,3,6) - g_type_init(); -#endif subsurface_open_conf(); load_preferences(); @@ -1920,89 +82,6 @@ void init_ui(int *argcp, char ***argvp) default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor"); default_dive_computer_product = subsurface_get_conf("dive_computer_product"); default_dive_computer_device = subsurface_get_conf("dive_computer_device"); - error_info_bar = NULL; - win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - g_set_application_name ("subsurface"); - /* Let's check if the subsurface icon has been installed or if - * we need to try to load it from the current directory */ - screen = gdk_screen_get_default(); - if (screen) - icon_theme = gtk_icon_theme_get_for_screen(screen); - if (icon_theme) { - if (gtk_icon_theme_has_icon(icon_theme, "subsurface")) { - need_icon = FALSE; - gtk_window_set_default_icon_name ("subsurface"); - } - } - if (need_icon) { - const char *icon_name = subsurface_icon_name(); - if (!access(icon_name, R_OK)) - gtk_window_set_icon_from_file(GTK_WINDOW(win), icon_name, NULL); - } - g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL); - g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL); - g_signal_connect(G_OBJECT(win), "window-state-event", G_CALLBACK(on_state), NULL); - main_window = win; - - vbox = gtk_vbox_new(FALSE, 0); - gtk_container_add(GTK_CONTAINER(win), vbox); - main_vbox = vbox; - - ui_manager = gtk_ui_manager_new(); - menubar = get_menubar_menu(win, ui_manager); - - subsurface_ui_setup(settings, menubar, vbox, ui_manager); - - vpane = gtk_vpaned_new(); - gtk_box_pack_start(GTK_BOX(vbox), vpane, TRUE, TRUE, 3); - hpane = gtk_hpaned_new(); - gtk_paned_add1(GTK_PANED(vpane), hpane); - g_signal_connect_after(G_OBJECT(vbox), "realize", G_CALLBACK(view_three), NULL); - - /* Notebook for dive info vs profile vs .. */ - notebook = gtk_notebook_new(); - scrolled = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_paned_add1(GTK_PANED(hpane), scrolled); - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), notebook); - g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL); - - /* Create the actual divelist */ - dive_list = dive_list_create(); - gtk_widget_set_name(dive_list, "Dive List"); - gtk_paned_add2(GTK_PANED(vpane), dive_list); - - /* Frame for dive profile */ - dive_profile = dive_profile_widget(); - gtk_widget_set_name(dive_profile, "Dive Profile"); - gtk_paned_add2(GTK_PANED(hpane), dive_profile); - - /* Frame for extended dive info */ - nb_page = extended_dive_info_widget(); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Dive Notes"))); - - /* Frame for dive equipment */ - nb_page = equipment_widget(W_IDX_PRIMARY); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Equipment"))); - - /* Frame for single dive statistics */ - nb_page = single_stats_widget(); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Dive Info"))); - - /* Frame for total dive statistics */ - nb_page = total_stats_widget(); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Stats"))); - - /* add tooltip that tells people how to edit things */ - g_object_set(notebook, "has-tooltip", TRUE, NULL); - g_signal_connect(notebook, "query-tooltip", G_CALLBACK(notebook_tooltip), NULL); - - /* handle some keys globally (to deal with gtk focus issues) */ - g_signal_connect (G_OBJECT (win), "key_press_event", G_CALLBACK (on_key_press), dive_list); - - gtk_widget_set_app_paintable(win, TRUE); - restore_window_geometry(); - gtk_widget_show_all(win); return; } @@ -2022,426 +101,6 @@ void exit_ui(void) free((void *)default_dive_computer_device); } -typedef struct { - cairo_rectangle_t rect; - const char *text; - struct event *event; -} tooltip_record_t; - -static tooltip_record_t *tooltip_rects; -static int tooltips; - -void attach_tooltip(int x, int y, int w, int h, const char *text, struct event *event) -{ - cairo_rectangle_t *rect; - tooltip_rects = (tooltip_record_t *) - realloc(tooltip_rects, (tooltips + 1) * sizeof(tooltip_record_t)); - rect = &tooltip_rects[tooltips].rect; - rect->x = x; - rect->y = y; - rect->width = w; - rect->height = h; - tooltip_rects[tooltips].text = strdup(text); - tooltip_rects[tooltips].event = event; - tooltips++; -} - -#define INSIDE_RECT(_r,_x,_y) ((_r.x <= _x) && (_r.x + _r.width >= _x) && \ - (_r.y <= _y) && (_r.y + _r.height >= _y)) -#define INSIDE_RECT_X(_r, _x) ((_r.x <= _x) && (_r.x + _r.width >= _x)) - -static gboolean profile_tooltip (GtkWidget *widget, gint x, gint y, - gboolean keyboard_mode, GtkTooltip *tooltip, struct graphics_context *gc) -{ - int i; - cairo_rectangle_t *drawing_area = &gc->drawing_area; - gint tx = x - drawing_area->x; /* get transformed coordinates */ - gint ty = y - drawing_area->y; - gint width, height, time = -1; - char buffer[2048], plot[1024]; - const char *event = ""; - - if (tx < 0 || ty < 0) - return FALSE; - - /* don't draw a tooltip if nothing is there */ - if (amount_selected == 0 || gc->pi.nr == 0) - return FALSE; - - width = drawing_area->width - 2*drawing_area->x; - height = drawing_area->height - 2*drawing_area->y; - if (width <= 0 || height <= 0) - return FALSE; - - if (tx > width || ty > height) - return FALSE; - - time = (tx * gc->maxtime) / width; - - /* are we over an event marker ? */ - for (i = 0; i < tooltips; i++) { - if (INSIDE_RECT(tooltip_rects[i].rect, tx, ty)) { - event = tooltip_rects[i].text; - break; - } - } - get_plot_details(gc, time, plot, sizeof(plot)); - - snprintf(buffer, sizeof(buffer), "@ %d:%02d%c%s%c%s", time / 60, time % 60, - *plot ? '\n' : ' ', plot, - *event ? '\n' : ' ', event); - gtk_tooltip_set_text(tooltip, buffer); - return TRUE; - -} - -static double zoom_factor = 1.0; -static int zoom_x = -1, zoom_y = -1; - -static void common_drawing_function(GtkWidget *widget, struct graphics_context *gc) -{ - int i = 0; - struct dive *dive = current_dive; - - gc->drawing_area.x = MIN(50,gc->drawing_area.width / 20.0); - gc->drawing_area.y = MIN(50,gc->drawing_area.height / 20.0); - - g_object_set(widget, "has-tooltip", TRUE, NULL); - g_signal_connect(widget, "query-tooltip", G_CALLBACK(profile_tooltip), gc); - init_profile_background(gc); - cairo_paint(gc->cr); - - if (zoom_factor > 1.0) { - double n = -(zoom_factor-1); - cairo_translate(gc->cr, n*zoom_x, n*zoom_y); - cairo_scale(gc->cr, zoom_factor, zoom_factor); - } - - if (dive) { - if (tooltip_rects) { - while (i < tooltips) { - if (tooltip_rects[i].text) - free((void *)tooltip_rects[i].text); - i++; - } - free(tooltip_rects); - tooltip_rects = NULL; - } - tooltips = 0; - plot(gc, dive, SC_SCREEN); - } -} - -#if GTK_CHECK_VERSION(3,0,0) - -static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) -{ - guint width, height; - static struct graphics_context gc = { .printer = 0 }; - - width = gtk_widget_get_allocated_width(widget); - height = gtk_widget_get_allocated_height(widget); - - gc.drawing_area.width = width; - gc.drawing_area.height = height; - gc.cr = cr; - - common_drawing_function(widget, &gc); - return FALSE; -} - -#else /* gtk2 */ - -static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) -{ - GtkAllocation allocation; - static struct graphics_context gc = { 0 }; - - /* the drawing area gives TOTAL width * height - x,y is used as the topx/topy offset - * so effective drawing area is width-2x * height-2y */ - gtk_widget_get_allocation(widget, &allocation); - gc.drawing_area.width = allocation.width; - gc.drawing_area.height = allocation.height; - gc.cr = gdk_cairo_create(gtk_widget_get_window(widget)); - - common_drawing_function(widget, &gc); - cairo_destroy(gc.cr); - return FALSE; -} - -#endif - -static void zoom_event(int x, int y, double inc) -{ - zoom_x = x; - zoom_y = y; - inc += zoom_factor; - if (inc < 1.0) - inc = 1.0; - else if (inc > 10) - inc = 10; - zoom_factor = inc; -} - -static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer user_data) -{ - switch (event->direction) { - case GDK_SCROLL_UP: - zoom_event(event->x, event->y, 0.1); - break; - case GDK_SCROLL_DOWN: - zoom_event(event->x, event->y, -0.1); - break; - default: - return TRUE; - } - gtk_widget_queue_draw(widget); - return TRUE; -} - -static void add_gas_change_cb(GtkWidget *menuitem, gpointer data) -{ - double *x = (double *)data; - int when = x_to_time(*x); - int cylnr = select_cylinder(current_dive, when); - if (cylnr >= 0) { - cylinder_t *cyl = ¤t_dive->cylinder[cylnr]; - int value = cyl->gasmix.o2.permille / 10 | ((cyl->gasmix.he.permille / 10) << 16); - add_event(current_dc, when, 25, 0, value, "gaschange"); - mark_divelist_changed(TRUE); - report_dives(FALSE, FALSE); - dive_list_update_dives(); - } -} - -int confirm_dialog(int when, char *action_text, char *event_text) -{ - GtkWidget *dialog, *vbox, *label; - int confirmed; - char title[80]; - - snprintf(title, sizeof(title), "%s %s", action_text, event_text); - dialog = gtk_dialog_new_with_buttons(title, - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - label = create_label(_("%s event at %d:%02u"), title, FRACTION(when, 60)); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); - gtk_widget_show_all(dialog); - confirmed = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; - - gtk_widget_destroy(dialog); - - return confirmed; -} - -static void add_bookmark_cb(GtkWidget *menuitem, gpointer data) -{ - double *x = (double *)data; - int when = x_to_time(*x); - - if (confirm_dialog(when, _("Add"), _("bookmark"))){ - add_event(current_dc, when, 8, 0, 0, "bookmark"); - mark_divelist_changed(TRUE); - report_dives(FALSE, FALSE); - } -} - -static struct event *event_at_x(double rel_x) -{ - /* is there an event marker at this x coordinate */ - struct event *ret = NULL; - int i; - int x = x_abs(rel_x); - - for (i = 0; i < tooltips; i++) { - if (INSIDE_RECT_X(tooltip_rects[i].rect, x)) { - ret = tooltip_rects[i].event; - break; - } - } - return ret; -} - -static void remove_event_cb(GtkWidget *menuitem, gpointer data) -{ - struct event *event = (struct event *)data; - if (confirm_dialog(event->time.seconds, _("Remove"), _(event->name))){ - struct event **ep = ¤t_dc->events; - while (ep && *ep != event) - ep = &(*ep)->next; - if (ep) { - *ep = event->next; - free(event); - } - mark_divelist_changed(TRUE); - report_dives(FALSE, FALSE); - } -} - -static void popup_profile_menu(GtkWidget *widget, GdkEventButton *gtk_event) -{ - GtkWidget *menu, *menuitem, *image; - static double x; - struct event *event; - - if (!gtk_event || !current_dive) - return; - x = gtk_event->x; - menu = gtk_menu_new(); - menuitem = gtk_image_menu_item_new_with_label(_("Add gas change event here")); - image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); - g_signal_connect(menuitem, "activate", G_CALLBACK(add_gas_change_cb), &x); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - menuitem = gtk_image_menu_item_new_with_label(_("Add bookmark event here")); - image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); - g_signal_connect(menuitem, "activate", G_CALLBACK(add_bookmark_cb), &x); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - if ((event = event_at_x(x)) != NULL) { - menuitem = gtk_image_menu_item_new_with_label(_("Remove event here")); - image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); - g_signal_connect(menuitem, "activate", G_CALLBACK(remove_event_cb), event); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - } - - gtk_widget_show_all(menu); - - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, - gtk_event->button, gtk_get_current_event_time()); - -} - -static gboolean clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data) -{ - switch (event->button) { - case 1: - zoom_x = event->x; - zoom_y = event->y; - zoom_factor = 2.5; - break; - case 3: - popup_profile_menu(widget, event); - break; - default: - return TRUE; - } - gtk_widget_queue_draw(widget); - return TRUE; -} - -static gboolean released(GtkWidget *widget, GdkEventButton *event, gpointer user_data) -{ - switch (event->button) { - case 1: - zoom_x = zoom_y = -1; - zoom_factor = 1.0; - break; - default: - return TRUE; - } - gtk_widget_queue_draw(widget); - return TRUE; -} - -static gboolean motion(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) -{ - if (zoom_x < 0) - return TRUE; - - zoom_x = event->x; - zoom_y = event->y; - gtk_widget_queue_draw(widget); - return TRUE; -} - -static GtkWidget *dive_profile_widget(void) -{ - GtkWidget *da; - - da = gtk_drawing_area_new(); - gtk_widget_set_size_request(da, 350, 250); -#if GTK_CHECK_VERSION(3,0,0) - g_signal_connect(da, "draw", G_CALLBACK (draw_callback), NULL); -#else - g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL); -#endif - g_signal_connect(da, "button-press-event", G_CALLBACK(clicked), NULL); - g_signal_connect(da, "scroll-event", G_CALLBACK(scroll_event), NULL); - g_signal_connect(da, "button-release-event", G_CALLBACK(released), NULL); - g_signal_connect(da, "motion-notify-event", G_CALLBACK(motion), NULL); - gtk_widget_add_events(da, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK); - - return da; -} - -static void do_import_file(gpointer data, gpointer user_data) -{ - GError *error = NULL; - parse_file((const char *)data, &error); - - if (error != NULL) - { - report_error(error); - g_error_free(error); - error = NULL; - } -} - -static void import_files(GtkWidget *w, gpointer data) -{ - GtkWidget *fs_dialog; - const char *current_default; - char *current_def_dir; - GtkFileFilter *filter; - struct stat sb; - GSList *filenames = NULL; - - fs_dialog = gtk_file_chooser_dialog_new(_("Choose XML Files To Import Into Current Data File"), - GTK_WINDOW(main_window), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - - /* I'm not sure what the best default path should be... */ - if (existing_filename) { - current_def_dir = g_path_get_dirname(existing_filename); - } else { - current_default = prefs.default_filename; - current_def_dir = g_path_get_dirname(current_default); - } - - /* it's possible that the directory doesn't exist (especially for the default) - * For gtk's file select box to make sense we create it */ - if (stat(current_def_dir, &sb) != 0) - g_mkdir(current_def_dir, S_IRWXU); - - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs_dialog), current_def_dir); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(fs_dialog), TRUE); - filter = setup_filter(); - gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fs_dialog), filter); - gtk_widget_show_all(fs_dialog); - if (gtk_dialog_run(GTK_DIALOG(fs_dialog)) == GTK_RESPONSE_ACCEPT) { - /* grab the selected file list, import each file and update the list */ - filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(fs_dialog)); - if (filenames) { - g_slist_foreach(filenames, do_import_file, NULL); - report_dives(TRUE, FALSE); - g_slist_free(filenames); - } - } - - free(current_def_dir); - gtk_widget_destroy(fs_dialog); -} - void set_filename(const char *filename, gboolean force) { if (!force && existing_filename) @@ -2467,79 +126,8 @@ const char *get_dc_nickname(const char *model, uint32_t deviceid) void set_dc_nickname(struct dive *dive) { - GtkWidget *dialog, *vbox, *entry, *frame, *label; - char nickname[160] = ""; - char dialogtext[2048]; - const char *name = nickname; - struct divecomputer *dc = &dive->dc; - - if (!dive) - return; - while (dc) { -#if NICKNAME_DEBUG & 16 - fprintf(debugfile, "set_dc_nickname for model %s deviceid %8x\n", dc->model ? : "", dc->deviceid); -#endif - if (get_dc_nickname(dc->model, dc->deviceid) == NULL) { - struct device_info *nn_entry = get_different_device_info(dc->model, dc->deviceid); - if (nn_entry) { - dialog = gtk_dialog_new_with_buttons( - _("Dive Computer Nickname"), - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - NULL); - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - snprintf(dialogtext, sizeof(dialogtext), - _("You already have a dive computer of this model\n" - "named %s\n" - "Subsurface can maintain a nickname for this device to " - "distinguish it from the existing one. " - "The default is the model and device ID as shown below.\n" - "If you don't want to name this dive computer click " - "'Cancel' and Subsurface will simply display its model " - "as its name (which may mean that you cannot tell the two " - "dive computers apart in the logs)."), - nn_entry->nickname && *nn_entry->nickname ? nn_entry->nickname : - (nn_entry->model && *nn_entry->model ? nn_entry->model : _("(nothing)"))); - label = gtk_label_new(dialogtext); - gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3); - frame = gtk_frame_new(_("Nickname")); - gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 3); - entry = gtk_entry_new(); - gtk_container_add(GTK_CONTAINER(frame), entry); - gtk_entry_set_max_length(GTK_ENTRY(entry), 68); - snprintf(nickname, sizeof(nickname), "%s (%08x)", dc->model, dc->deviceid); - gtk_entry_set_text(GTK_ENTRY(entry), nickname); - gtk_widget_show_all(dialog); - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - if (strcmp(dc->model, gtk_entry_get_text(GTK_ENTRY(entry)))) { - name = gtk_entry_get_text(GTK_ENTRY(entry)); - remember_dc(dc->model, dc->deviceid, name); - mark_divelist_changed(TRUE); - } - } else { - /* Remember that we declined the nickname */ - remember_dc(dc->model, dc->deviceid, NULL); - } - gtk_widget_destroy(dialog); - } else { - remember_dc(dc->model, dc->deviceid, NULL); - } - } - dc = dc->next; - } + /* needs Qt implementation */ } -gdouble get_screen_dpi(void) -{ - const gdouble mm_per_inch = 25.4; - GdkScreen *scr = gdk_screen_get_default(); - gdouble h_mm = gdk_screen_get_height_mm(scr); - gdouble h = gdk_screen_get_height(scr); - gdouble dpi_h = floor((h / h_mm) * mm_per_inch); - return dpi_h; -} #include "qt-gui.moc" diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 3193eb2a8..bd202b24f 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -116,6 +116,7 @@ void MainWindow::on_actionClose_triggered() ui->InfoWidget->clearEquipment(); clear_events(); +#if USE_GTK_UI show_dive_stats(NULL); /* redraw the screen */ @@ -124,6 +125,7 @@ void MainWindow::on_actionClose_triggered() // WARNING? Port this to Qt. show_dive_info(NULL); +#endif /* USE_GTK_UI */ } void MainWindow::on_actionImport_triggered() diff --git a/uemis-downloader.c b/uemis-downloader.c index d33f08b8c..53d4f6876 100644 --- a/uemis-downloader.c +++ b/uemis-downloader.c @@ -922,14 +922,18 @@ GError *uemis_download(const char *mountpath, progressbar_t *progress, if (!import_thread_cancelled) { int result; g_timeout_add(100, timeout_func, dialog); +#if USE_GTK_UI update_progressbar(args.progress, progress_bar_fraction); update_progressbar_text(args.progress, progress_bar_text); +#endif result = gtk_dialog_run(dialog); if (result == GTK_RESPONSE_CANCEL) import_thread_cancelled = TRUE; } else { +#if USE_GTK_UI update_progressbar(args.progress, progress_bar_fraction); update_progressbar_text(args.progress, _("Cancelled, exiting cleanly...")); +#endif usleep(100000); } } diff --git a/webservice.c b/webservice.c index 8f00025d2..6dbe32a38 100644 --- a/webservice.c +++ b/webservice.c @@ -164,7 +164,9 @@ static void download_dialog_response_cb(GtkDialog *d, gint response, gpointer da /* now merge the data in the gps_location table into the dive_table */ if (merge_locations_into_dives()) { mark_divelist_changed(TRUE); +#if USE_GTK_UI dive_list_update_dives(); +#endif } /* store last entered uid in config */ subsurface_set_conf("webservice_uid", gtk_entry_get_text(GTK_ENTRY(state->uid))); From 8577410fcd94f3c3109b8b3cf4932a9e0d11fc80 Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Fri, 3 May 2013 08:19:00 +0100 Subject: [PATCH 113/226] Reformat dive info page in tab widget Clean up the formatting. Distinguish between headings and value labels. Tidy up text appearance (remove trailing ':') Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 26 ++-- qt-ui/maintab.ui | 304 ++++++++++++++++++++++++++-------------------- 2 files changed, 184 insertions(+), 146 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 2467dd0d7..d72df965c 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -27,19 +27,19 @@ void MainTab::clearEquipment() void MainTab::clearInfo() { - ui->sac->setText(QString()); - ui->otu->setText(QString()); - ui->oxygenhelium->setText(QString()); - ui->gasused->setText(QString()); - ui->date->setText(QString()); - ui->divetime->setText(QString()); - ui->surfinterval->setText(QString()); - ui->maxdepth->setText(QString()); - ui->avgdepth->setText(QString()); - ui->visibility->setText(QString()); - ui->watertemperature->setText(QString()); - ui->airtemperature->setText(QString()); - ui->airpress->setText(QString()); + ui->sacText->setText(QString()); + ui->otuText->setText(QString()); + ui->oxygenHeliumText->setText(QString()); + ui->gasUsedText->setText(QString()); + ui->dateText->setText(QString()); + ui->diveTimeText->setText(QString()); + ui->surfaceIntervalText->setText(QString()); + ui->maximumDepthText->setText(QString()); + ui->averageDepthText->setText(QString()); + ui->visibilityText->setText(QString()); + ui->waterTemperatureText->setText(QString()); + ui->airTemperatureText->setText(QString()); + ui->airPressureText->setText(QString()); } void MainTab::clearStats() diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index a99b0aed7..82019e6c3 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -7,14 +7,14 @@ 0 0 400 - 320 + 325 TabWidget - 0 + 2 @@ -199,9 +199,31 @@ - + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 10 + + + 15 + + + 10 + - + 75 @@ -209,12 +231,12 @@ - SAC: + SAC - + 75 @@ -222,12 +244,12 @@ - OTU: + OTU - + 75 @@ -235,12 +257,12 @@ - 0²/He: + 0²/He - + 75 @@ -248,33 +270,33 @@ - Gas Used: + Gas Used - + TextLabel - + TextLabel - + TextLabel - + TextLabel @@ -283,12 +305,28 @@ - - + + + Qt::Vertical + + + + 20 + 40 + + + + + + + 10 - - + + 15 + + + 75 @@ -296,112 +334,26 @@ - Dive Time: + Date - - - - - 75 - true - - - - Surf Interv: - - - - - - - - 75 - true - - - - Avg Depth: - - - - - + + TextLabel - - - TextLabel - - - - - - - - 75 - true - - - - Air Press: - - - - - - - - 75 - true - - - - Max Depth: - - - - - - - TextLabel - - - - - - - TextLabel - - - - - - - - 75 - true - - - - Air Temp: - - - - - + TextLabel - + 75 @@ -409,12 +361,12 @@ - Visibility: + Visibility - + 75 @@ -422,40 +374,113 @@ - Water Temp: - - - - - - - TextLabel - - - - - - - TextLabel + Water Temp. - + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + 75 + true + + + + Air Pressure + + + + + + + + 75 + true + + + + Ave. Depth + + + + + + + + 75 + true + + + + Max. Depth + + + + + + + + 75 + true + + + + Air Temp. + + + + + TextLabel - + TextLabel - - + + + + TextLabel + + + + + + + TextLabel + + + + + 75 @@ -463,7 +488,20 @@ - Date: + Interval + + + + + + + + 75 + true + + + + Dive Time @@ -477,7 +515,7 @@ 20 - 112 + 40 From 0a19a964e09e31016b39ee2d65f6220b01c49dcb Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 11:55:22 -0700 Subject: [PATCH 114/226] Temporarily expand all dives so the automatic width calculation works Suggested-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index bd202b24f..aac829ef1 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -314,6 +314,9 @@ void MainWindow::readSettings() settings.endGroup(); settings.beginGroup("ListWidget"); + /* if no width are set, use the calculated width for each column; + * for that to work we need to temporarily expand all rows */ + ui->ListWidget->expandAll(); for (i = TreeItemDT::NR; i < TreeItemDT::COLUMNS; i++) { QVariant width = settings.value(QString("colwidth%1").arg(i)); if (width.isValid()) @@ -321,6 +324,7 @@ void MainWindow::readSettings() else ui->ListWidget->resizeColumnToContents(i); } + ui->ListWidget->collapseAll(); settings.endGroup(); } From ce5fc7d0ec32f41d9cfe4d97cba6a5a0fe373f2f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 12:50:11 -0700 Subject: [PATCH 115/226] Select the first dive This works regardless whether the first dive is in a trip or not. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index aac829ef1..93ea3d615 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -325,6 +325,12 @@ void MainWindow::readSettings() ui->ListWidget->resizeColumnToContents(i); } ui->ListWidget->collapseAll(); + ui->ListWidget->expand(sortModel->index(0,0)); + QModelIndex firstDiveOrTrip = sortModel->index(0,0); + if (sortModel->index(0,0, firstDiveOrTrip).isValid()) + ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); + else + ui->ListWidget->setCurrentIndex(firstDiveOrTrip); settings.endGroup(); } From 1e43106a28900b4e7924ae5060fbe313bdf5c66a Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 12:53:45 -0700 Subject: [PATCH 116/226] Fix Mac build Signed-off-by: Dirk Hohndel --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4a13de5eb..9961f04c7 100644 --- a/Makefile +++ b/Makefile @@ -97,7 +97,7 @@ else ifeq ($(UNAME), darwin) MACOSXSTAGING = $(MACOSXFILES)/Subsurface.app INFOPLIST = $(MACOSXFILES)/Info.plist INFOPLISTINPUT = $(INFOPLIST).in - LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST) + LDFLAGS += -headerpad_max_install_names else SOURCES += windows.c WINDOWSSTAGING = ./packaging/windows From 1a1b392c37fe01a2eff746594ff2f5b4210e52c6 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 12:56:17 -0700 Subject: [PATCH 117/226] Fix Linux build with newer tool chain No idea why this builds fine on Fedora 17. Reported-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- linux.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linux.c b/linux.c index 0c31b6c43..51d1ce75d 100644 --- a/linux.c +++ b/linux.c @@ -81,6 +81,7 @@ void subsurface_close_conf(void) /* this is a no-op */ } +#if USE_GTK_UI int subsurface_fill_device_list(GtkListStore *store) { int i = 0; @@ -140,6 +141,7 @@ int subsurface_fill_device_list(GtkListStore *store) } return index; } +#endif /* USE_GTK_UI */ const char *subsurface_icon_name() { @@ -171,11 +173,13 @@ const char *subsurface_gettext_domainpath(char *argv0) } } +#if USE_GTK_UI void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar, GtkWidget *vbox, GtkUIManager *ui_manager) { gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); } +#endif /* USE_GTK_UI */ void subsurface_command_line_init(gint *argc, gchar ***argv) { @@ -192,6 +196,7 @@ gboolean subsurface_os_feature_available(os_feature_t f) return TRUE; } +#if USE_GTK_UI gboolean subsurface_launch_for_uri(const char* uri) { GError *err = NULL; @@ -203,3 +208,4 @@ gboolean subsurface_launch_for_uri(const char* uri) } return TRUE; } +#endif /* USE_GTK_UI */ From aa3838308b22dd3940e6b18cb23224356544da92 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 13:13:14 -0700 Subject: [PATCH 118/226] Similar build fix for Mac Apparently only older Linux toolchains didn't bother to throw up with the remainders of Gtk related code. Signed-off-by: Dirk Hohndel --- macos.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/macos.c b/macos.c index 38e58f54b..d28ea1dae 100644 --- a/macos.c +++ b/macos.c @@ -151,8 +151,11 @@ const char *subsurface_icon_name() { static char path[PATH_MAX]; +#if USE_GTK_UI snprintf(path, sizeof(path), "%s/%s", gtkosx_application_get_resource_path(), ICON_NAME); - +#else + /* need Qt path */ +#endif return path; } @@ -174,15 +177,18 @@ const char *subsurface_gettext_domainpath(char *argv0) { /* on a Mac we ignore the argv0 argument and instead use the resource_path * to figure out where to find the translation files */ +#if USE_GTK_UI static char buffer[PATH_MAX]; const char *resource_path = gtkosx_application_get_resource_path(); if (resource_path) { snprintf(buffer, sizeof(buffer), "%s/share/locale", resource_path); return buffer; } +#endif /* USE_GTK_UI */ return "./share/locale"; } +#if USE_GTK_UI static void show_main_window(GtkWidget *w, gpointer data) { gtk_widget_show(main_window); @@ -230,6 +236,7 @@ void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar, gtkosx_application_ready(osx_app); } +#endif /* UES_GTK_UI */ void subsurface_command_line_init(gint *argc, gchar ***argv) { From 060e5c764cc66e54f422928e3921fd08dff31f69 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 13:32:23 -0700 Subject: [PATCH 119/226] Matching build fixes for cross built Windows Signed-off-by: Dirk Hohndel --- linux.c | 2 ++ macos.c | 3 +++ packaging/windows/subsurface.res | Bin 18056 -> 0 bytes profile.c | 2 ++ windows.c | 7 +++++++ 5 files changed, 14 insertions(+) delete mode 100644 packaging/windows/subsurface.res diff --git a/linux.c b/linux.c index 51d1ce75d..33a846f9b 100644 --- a/linux.c +++ b/linux.c @@ -2,7 +2,9 @@ /* implements Linux specific functions */ #include "dive.h" #include "display.h" +#if USE_GTK_UI #include "display-gtk.h" +#endif #include #include diff --git a/macos.c b/macos.c index d28ea1dae..b43849f87 100644 --- a/macos.c +++ b/macos.c @@ -2,7 +2,10 @@ /* implements Mac OS X specific functions */ #include #include "dive.h" +#include "display.h" +#if USE_GTK_UI #include "display-gtk.h" +#endif /* USE_GTK_UI */ #include #include #include diff --git a/packaging/windows/subsurface.res b/packaging/windows/subsurface.res deleted file mode 100644 index 616cf5ea91f052151757396a3e7e37e11f47f931..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18056 zcmeI4c~lff8o-ho_4mg6KM0BCkUp!@pcY=GCyw?Yx;Q_&%KVjOE9Eu9LgNQ(LnpFeyeby zkYXv`UJwF)j^Q#4pTh7Uh8UQovt#+pu@e8}ctJ2wV%$W%WK#~N-9AYA8kTw>JyFm# ziG!qX#dA*x^M$z>{vE~sE&S=pej^@WI2Diko*0NgHsm%bwi71e-te~S`kHr( zD@t?qd8hBFPwT%?ozDFBaTay9{+rA*IejJ7<=XQNHB>dMZnd?wzbq;@R>|X!!ii5l zf{4|#Ab80XII!RuIPm_&M_LXpcoqT|+4su}5VUv-gnBqh61IFAB>KBUN^As_RFxMt zG&KCoyb$pCSGh&`x03_b!a?UppLewWAhx7~GRQA6beNZcp{%4fd7t+q?c>ohTV8kY z{bwLCB!JZessmLGmt5qrp)1*I6|WQ1W{*7k zPUIr$gPWl5&K)L2DZyNH^0LibPg3A+D6TBi>vFRG8L@H(+W8C@snW(t{9T(Q zR-sqJS06mf&BoHSP>H`JO$@_p~^K1h+dj9PExs+rK;-6`q2 zarH8sO*jC@y6Y7 zsWL#$vAuA7&FoR>7Fm^g?EiHNv=R9^Nu5y}1Ez}jNp|__CnlF|BwKK7Pv^CX3sWJ+ZWUj-?kN8zu zB?K>;#9g9Rz694B&NJI8@tI&Gd7MiLWY=|fbwIo~&cm457qWOVM6GxUlGeTg8UD_2 zUKa;fD)L!g9j(pK)6)%Y*jEH&I`iwS?GwLJtAHb@pS!sI9`eIvl5ftP3>{V;VeV7l z=Jh6a-Njrvge{po=yi1N*oRXf8p|Vo^~(^y#t{-cogl%>365f4^dKhI{%ysNWlDa! z@pY|K2FCP=2ila4{e@Vcb^ARZnEpYGL!DP>vH#O#z!CJ7EhNNkG89IAD)A6ku{?N) zye7ldno=o!^5(aPNc-?M6TeEG0U^k5D~Va<2n9!F(0Zjt;xe9!u$tdVzYnA|J-t0} zeECaO)A6!3L#x8}pAI35k>5r_ZS?r^7eW8|a*2(~A{65-^UU~Usg$O@r4eX6VVQ>K zG4U%@X%LEirL85-^HqthyXzKXv&>WZ=`txzb9E7fFR{0&T}r+Bu9EoC7q*sE*+AW< zejv_g_C0x?^ml_c^tssgT{@F~&v{$ehhtxC|7q+VL-o{_ayodM#72y#cDu3sB0L6S>9kY~*f; z!27zb?PC1s|8V5Dr5ts)2VxfaD7;o*dC#$Ut|mv$`b%5dBkDHspTy^MR3SLjRliDes3D?dPlR6?a+^03d!uT&BYNZ1g8%DyRX8oU> zTfQ{TZ>lQ1uMUW;gM~D25xrt6H#>$~2E?yX$bsgocuASr+}~ilmhruPeNbo6K+Kw% z-0T<*j~d%QZ+`uLuMzvd>sC9?Z)(7p8VspB7mZmNXmI>X{8VpwNrLwbHaBVM;BJ-ZIYYwaAh0WrR|P{qxjp}1)L zr&6V`xn-JP<|XAB;S!6;G?MUoT_x)edD%0V9u=|0q;@qU#&4hZr}#I zhju~Cs+YOiWQ|XyH}TW_F;7YIef7R^z;OIub2=Hc0V|;o*SIovE#hjEH9offhxI=i z*D82Q%tsEmE)hH8I$%5*1bLw!LtTLa%F@HR+GLGSou$32RwP3V^7EDiPe&*}HLwmy zV?gsh&RTtFDAq$KJ~PldpDw_IyG@pi+HC!Ij350)KI1J3xE@3GfS&!0DTko7@!|vP zM8i2fD0ewS9JYVn_E_rEo;LBvBD<|5Kf<3e(^`JkUp?XKrGfPgYjxm4)(MD3c3asa z>0RXj_-;7!e=P3XO5~g00PSb)khb|vNcNcl=e5z0AMOX)2Ym+ZBh-7{ZDxMY%N8f^bm*K8^A{rV|P#P!J&p3~rj*EC4-#pgw{d0$o^u#j@E&tn5fx_bS%^O!WWQ@+V?BGft6v49nfTY*W_H8sMjL|B;D&!yiCH z;~$+03W9v$g!gpj7xK*yDO4{{ZFvJ!vbmt$vjDOK7Q>mZR)ZmYBb3JOg3_2>P?_=< zsFLrOq#`MR#T%n{B8xX=xV!)&k6(U z!RGhJVejL(qN}|HZq%KFi|P<43|$Y}-Se@}nZel7w!BIF->S3p|88n-mhQowE%i{N z3WdUub&$E&6%^b51Zf**gB;&COTs!r;}&yC_MHi-cs>KUR6FK^9(|Vkr3cHeJmFK! zJCv2}jk*%(xOxe?I<8Ca_6?f_*GqY%rMa~mHASqhJDs zgIm`c;O5uW(B4o6ZS_VbHybNiTt`bSKC?ALcW3(##xBTX&LZ^WXmuugo{{WsXEo#-Zzf=YVrNvF@Y9*9cRYGff8+3Gb-Y1V^JGs@} z1(g@70pE87Lxr)XqOtMU=Ir7=0N7Psy5M0bGgi|7BGWa>|DM8Yh9l`Xj!F=tuGXI7BeChexi>@N882-n!^Sf44IKf~F#|4-|J25mJ{{Q!JDFM-MVrVcKKCaai zP7wWW`Ub_EITTK0Uzo7N??}wRRPMrJVKx4~Be>z-d|{#Bg2(gmcOLH3Rxti=-*b7!BKb(Wd_kxW;Foi$UxEWKfBRhrhX8PRmoF8f&(BmXH2u_&p72&6tj`+wH zKMXyQc))UNc9S7HFb6xq8LwH#t|0R5$V?Wm6V~H-0nbt??!WR6C77o-RL3kc@ v;g?|WEV0Uv!xOC-@XwP!xjEiIDsY=1h+k)!Ahfa=iZlPZj2*^~V;cVlkK~nA diff --git a/profile.c b/profile.c index bd395f127..f30ff1d06 100644 --- a/profile.c +++ b/profile.c @@ -6,7 +6,9 @@ #include "dive.h" #include "display.h" +#if USE_GTK_UI #include "display-gtk.h" +#endif #include "divelist.h" #include "color.h" #include "libdivecomputer/parser.h" diff --git a/windows.c b/windows.c index 94e3dc546..d6cb531ae 100644 --- a/windows.c +++ b/windows.c @@ -1,7 +1,10 @@ /* windows.c */ /* implements Windows specific functions */ #include "dive.h" +#include "display.h" +#if USE_GTK_UI #include "display-gtk.h" +#endif #include #include @@ -128,6 +131,7 @@ void subsurface_close_conf(void) RegCloseKey(hkey); } +#if USE_GTK_UI int subsurface_fill_device_list(GtkListStore *store) { const int bufdef = 512; @@ -194,6 +198,7 @@ int subsurface_fill_device_list(GtkListStore *store) } return index; } +#endif /* USE_GTK_UI */ const char *subsurface_icon_name() { @@ -231,11 +236,13 @@ const char *subsurface_gettext_domainpath(char *argv0) return "./share/locale"; } +#if USE_GTK_UI void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar, GtkWidget *vbox, GtkUIManager *ui_manager) { gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); } +#endif /* USE_GTK_UI */ /* barely documented API */ extern int __wgetmainargs(int *, wchar_t ***, wchar_t ***, int, int *); From 98414ac9a9ab927b475088a982ebaf8200c647c8 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 14:16:09 -0700 Subject: [PATCH 120/226] Fix compiler warnings Doing this on Arch Linux with gcc 4.8.0 helped find one real bug. The rest are simply changes to make static functions externally visible (as they are kept around to eventually become helpers used by Qt) which for now avoids the warnings. Signed-off-by: Dirk Hohndel --- divelist.c | 6 +++--- download-dialog.c | 6 +++--- equipment.c | 2 +- profile.c | 16 +++++++++------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/divelist.c b/divelist.c index e22dd331a..deab1b05c 100644 --- a/divelist.c +++ b/divelist.c @@ -620,12 +620,12 @@ char *get_nitrox_string(struct dive *dive) o2low = (o2low + 5) / 10; if (he) - snprintf(buffer, sizeof(buffer), "%d/%d", o2, he); + snprintf(buffer, MAX_NITROX_STRING, "%d/%d", o2, he); else if (o2) if (o2 == o2low) - snprintf(buffer, sizeof(buffer), "%d", o2); + snprintf(buffer, MAX_NITROX_STRING, "%d", o2); else - snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2); + snprintf(buffer, MAX_NITROX_STRING, "%d" UTF8_ELLIPSIS "%d", o2low, o2); else strcpy(buffer, _("air")); } diff --git a/download-dialog.c b/download-dialog.c index 729f3a6b6..e12b07abd 100644 --- a/download-dialog.c +++ b/download-dialog.c @@ -81,7 +81,7 @@ int is_default_dive_computer_device(const char *name) return default_dive_computer_device && !strcmp(name, default_dive_computer_device); } -static void set_default_dive_computer(const char *vendor, const char *product) +void set_default_dive_computer(const char *vendor, const char *product) { if (!vendor || !*vendor) return; @@ -99,7 +99,7 @@ static void set_default_dive_computer(const char *vendor, const char *product) subsurface_set_conf("dive_computer_product", product); } -static void set_default_dive_computer_device(const char *name) +void set_default_dive_computer_device(const char *name) { if (!name || !*name) return; @@ -159,7 +159,7 @@ static GtkWidget *import_dive_computer(device_data_t *data, GtkDialog *dialog) #endif /* create a list of lists and keep the elements sorted */ -static void add_dc(const char *vendor, const char *product, dc_descriptor_t *descriptor) +void add_dc(const char *vendor, const char *product, dc_descriptor_t *descriptor) { struct vendor *dcl = dc_list; struct vendor **dclp = &dc_list; diff --git a/equipment.c b/equipment.c index 93f26dab1..4c692f33b 100644 --- a/equipment.c +++ b/equipment.c @@ -112,7 +112,7 @@ void convert_volume_pressure(int ml, int mbar, double *v, double *p) *v = volume; } -static int convert_weight(int grams, double *m) +int convert_weight(int grams, double *m) { int decimals = 1; /* not sure - do people do less than whole lbs/kg ? */ double weight; diff --git a/profile.c b/profile.c index f30ff1d06..b9a633cf6 100644 --- a/profile.c +++ b/profile.c @@ -18,7 +18,10 @@ int selected_dive = 0; char zoomed_plot = 0; char dc_number = 0; +#if USE_GTK_UI static double plot_scale = SCALE_SCREEN; +#endif + static struct plot_data *last_pi_entry = NULL; #define cairo_set_line_width_scaled(cr, w) \ @@ -228,7 +231,7 @@ static void dump_pi (struct plot_info *pi) * We also need to add 180 seconds at the end so the min/max * plots correctly */ -static int get_maxtime(struct plot_info *pi) +int get_maxtime(struct plot_info *pi) { int seconds = pi->maxtime; if (zoomed_plot) { @@ -251,7 +254,7 @@ static int get_maxtime(struct plot_info *pi) /* get the maximum depth to which we want to plot * take into account the additional verical space needed to plot * partial pressure graphs */ -static int get_maxdepth(struct plot_info *pi) +int get_maxdepth(struct plot_info *pi) { unsigned mm = pi->maxdepth; int md; @@ -1018,8 +1021,7 @@ static int get_cylinder_pressure_range(struct graphics_context *gc, struct plot_ * as compared to avg_sac; the calculation simply maps the delta between * sac and avg_sac to indexes 0 .. (SAC_COLORS - 1) with everything * more than 6000 ml/min below avg_sac mapped to 0 */ - -static void set_sac_color(struct graphics_context *gc, int sac, int avg_sac) +void set_sac_color(struct graphics_context *gc, int sac, int avg_sac) { int sac_index = 0; int delta = sac - avg_sac + 7000; @@ -1038,7 +1040,7 @@ static void set_sac_color(struct graphics_context *gc, int sac, int avg_sac) #endif /* USE_GTK_UI */ /* Get local sac-rate (in ml/min) between entry1 and entry2 */ -static int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive) +int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive) { int index = entry1->cylinderindex; cylinder_t *cyl; @@ -1597,7 +1599,7 @@ static void check_gas_change_events(struct dive *dive, struct divecomputer *dc, set_cylinder_index(pi, i, cylinderindex, ~0u); } -static void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc) +void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc) { struct plot_info *pi; int maxdepth; @@ -1953,7 +1955,7 @@ static void calculate_deco_information(struct dive *dive, struct divecomputer *d * sides, so that you can do end-points without having to worry * about it. */ -static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc) +struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc) { struct plot_info *pi; From c385e7aae8c7ac0d381a94440e32ecb3b115e9fc Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 16:30:36 -0700 Subject: [PATCH 121/226] Make Ctrl-q work as Quit It's annoying to always have to close the window with the mouse. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 93ea3d615..d8e786b86 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -150,9 +150,10 @@ void MainWindow::on_actionPreferences_triggered() void MainWindow::on_actionQuit_triggered() { - qDebug("actionQuit"); if (unsaved_changes() && (askSaveChanges() == FALSE)) return; + writeSettings(); + QApplication::quit(); } void MainWindow::on_actionDownloadDC_triggered() From 7add8594a71db739d86966ea2bc1e15aa7a6674f Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 3 May 2013 21:49:40 -0300 Subject: [PATCH 122/226] Added code to select / desselect a range of items Signed-off-by: Tomaz Canabrava --- qt-ui/mainwindow.cpp | 39 ++++++++++++++++++++++++++++----------- qt-ui/mainwindow.h | 4 +++- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index d8e786b86..3b0981d21 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -34,21 +34,14 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), ui->setupUi(this); sortModel->setSourceModel(model); ui->ListWidget->setModel(sortModel); - connect(ui->ListWidget, SIGNAL(activated(QModelIndex)), this, SLOT(diveSelected(QModelIndex))); setWindowIcon(QIcon(":subsurface-icon")); + + connect(ui->ListWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(dive_selection_changed(QItemSelection,QItemSelection))); + readSettings(); } -void MainWindow::diveSelected(const QModelIndex& index) -{ - struct dive *dive = (struct dive*) index.model()->data(index, TreeItemDT::DIVE_ROLE).value(); - - if (dive) - selected_dive = get_index_for_dive(dive); - - // Here should be the code to update the other widgets. -} - void MainWindow::on_actionNew_triggered() { qDebug("actionNew"); @@ -85,6 +78,30 @@ void MainWindow::on_actionOpen_triggered() ui->ListWidget->sortByColumn(0, Qt::DescendingOrder); } +void MainWindow::dive_selection_changed(const QItemSelection& newSelection, const QItemSelection& oldSelection) +{ + // struct dive *dive = (struct dive*) index.model()->data(index, TreeItemDT::DIVE_ROLE).value(); + //if (dive) + // selected_dive = get_index_for_dive(dive); + Q_FOREACH(const QModelIndex& desselect, oldSelection.indexes()){ + struct dive *d = (struct dive*) desselect.data(TreeItemDT::DIVE_ROLE).value(); + if (!d) + continue; + d->selected = false; + } + + struct dive *lastSelected = 0; + Q_FOREACH(const QModelIndex& select, oldSelection.indexes()){ + struct dive *d = (struct dive*) select.data(TreeItemDT::DIVE_ROLE).value(); + if (!d) + continue; + d->selected = true; + lastSelected = d; + } + + select_dive( get_divenr(lastSelected) ); +} + void MainWindow::on_actionSave_triggered() { qDebug("actionSave"); diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index e94cb5b7c..3b35d44e5 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -22,6 +22,7 @@ class DiveInfo; class DiveNotes; class Stats; class Equipment; +class QItemSelection; class MainWindow : public QMainWindow { @@ -67,7 +68,8 @@ private Q_SLOTS: void on_actionAboutSubsurface_triggered(); void on_actionUserManual_triggered(); - void diveSelected(const QModelIndex& index); + void dive_selection_changed(const QItemSelection& newSelection, + const QItemSelection& oldSelection); protected: void closeEvent(QCloseEvent *); From fcd6903621da975a83bfb6c9a9769221c4203ad9 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 3 May 2013 20:58:44 -0700 Subject: [PATCH 123/226] Small changes to the selection logic Now it correctly uses the existing helper functions and keeps our idea of the selection consistent. There is a small behavioral change compared to the Gtk code. Range selections no longer have the last dive clicked on as selected_dive but instead the dive with the highest index that was selected. I don't think that is a major issue for anyone. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 3b0981d21..3f545171e 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -78,28 +78,22 @@ void MainWindow::on_actionOpen_triggered() ui->ListWidget->sortByColumn(0, Qt::DescendingOrder); } -void MainWindow::dive_selection_changed(const QItemSelection& newSelection, const QItemSelection& oldSelection) +void MainWindow::dive_selection_changed(const QItemSelection& newSelection, const QItemSelection& oldSelection) { - // struct dive *dive = (struct dive*) index.model()->data(index, TreeItemDT::DIVE_ROLE).value(); - //if (dive) - // selected_dive = get_index_for_dive(dive); - Q_FOREACH(const QModelIndex& desselect, oldSelection.indexes()){ + /* first deselect the dives that are no longer selected */ + Q_FOREACH(const QModelIndex& desselect, oldSelection.indexes()) { struct dive *d = (struct dive*) desselect.data(TreeItemDT::DIVE_ROLE).value(); - if (!d) + if (!d || !d->selected) continue; - d->selected = false; + deselect_dive(get_divenr(d)); } - - struct dive *lastSelected = 0; - Q_FOREACH(const QModelIndex& select, oldSelection.indexes()){ + /* then select the newly selected dives */ + Q_FOREACH(const QModelIndex& select, newSelection.indexes()) { struct dive *d = (struct dive*) select.data(TreeItemDT::DIVE_ROLE).value(); - if (!d) + if (!d || d->selected) continue; - d->selected = true; - lastSelected = d; + select_dive(get_divenr(d)); } - - select_dive( get_divenr(lastSelected) ); } void MainWindow::on_actionSave_triggered() From 243016633d31f8225fc4d1475463cd0477cf9505 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 16:12:43 -0300 Subject: [PATCH 124/226] Started the code for the Profile Plotting This small patch adds a new class - ProfileGraphicsView it's a QGraphicsView based class that will holds all graphics-items for the plotting. The setup is simple, just call ui->ListView->plot( dive ) ( that's already a ProfileGraphicsView and magic will happen. Since Im using a QGraphicsView , the size of the canvas doesn't matter and I'm fixing it at 0,0,100,100. when a resize is done, the resizeEvent will be called, fitting the scene's rectangle on the view. Signed-off-by: Tomaz Canabrava --- Makefile | 2 ++ qt-ui/mainwindow.cpp | 2 ++ qt-ui/mainwindow.ui | 9 +++++++-- qt-ui/profilegraphics.cpp | 26 ++++++++++++++++++++++++++ qt-ui/profilegraphics.h | 17 +++++++++++++++++ 5 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 qt-ui/profilegraphics.cpp create mode 100644 qt-ui/profilegraphics.h diff --git a/Makefile b/Makefile index 9961f04c7..338f81a7c 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ HEADERS = \ qt-ui/plotareascene.h \ qt-ui/starwidget.h \ qt-ui/modeldelegates.h \ + qt-ui/profilegraphics.h \ SOURCES = \ @@ -67,6 +68,7 @@ SOURCES = \ qt-ui/plotareascene.cpp \ qt-ui/starwidget.cpp \ qt-ui/modeldelegates.cpp \ + qt-ui/profilegraphics.cpp \ $(RESFILE) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 3f545171e..e8134a5f6 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -94,6 +94,8 @@ void MainWindow::dive_selection_changed(const QItemSelection& newSelection, cons continue; select_dive(get_divenr(d)); } + + ui->ProfileWidget->plot( get_dive(selected_dive) ); } void MainWindow::on_actionSave_triggered() diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index fe97d98b4..7dfbae746 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -25,7 +25,7 @@ Qt::Horizontal - + @@ -88,7 +88,7 @@ 0 0 763 - 19 + 25 @@ -339,6 +339,11 @@ QTreeView
divelistview.h
+ + ProfileGraphicsView + QGraphicsView +
profilegraphics.h
+
diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp new file mode 100644 index 000000000..59a47826f --- /dev/null +++ b/qt-ui/profilegraphics.cpp @@ -0,0 +1,26 @@ +#include "profilegraphics.h" + +#include +#include + +#include + +ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) +{ + setScene(new QGraphicsScene()); + scene()->setSceneRect(0,0,100,100); +} + +void ProfileGraphicsView::plot(struct dive *d) +{ + qDebug() << "Start the plotting of the dive here."; +} + +void ProfileGraphicsView::resizeEvent(QResizeEvent *event) +{ + // Fits the scene's rectangle on the view. + // I can pass some parameters to this - + // like Qt::IgnoreAspectRatio or Qt::KeepAspectRatio + QRectF r = scene()->sceneRect(); + fitInView ( r.x() - 2, r.y() -2, r.width() + 4, r.height() + 4); // do a little bit of spacing; +} diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h new file mode 100644 index 000000000..e3380abcd --- /dev/null +++ b/qt-ui/profilegraphics.h @@ -0,0 +1,17 @@ +#ifndef PROFILEGRAPHICS_H +#define PROFILEGRAPHICS_H + +#include + +class ProfileGraphicsView : public QGraphicsView { +Q_OBJECT +public: + ProfileGraphicsView(QWidget* parent = 0); + void plot(struct dive *d); + +protected: + void resizeEvent(QResizeEvent *event); + +}; + +#endif From a58412cb3114160c6dadb849582f2ea57ddb2125 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 16:41:49 -0300 Subject: [PATCH 125/226] Converted the Colors.h code to Qt The colors on colors.h were done to fill a special struct by Subsurface - I removed that structure and replaced the code that generated the map of colors to a QMap. I know that this changes are not very 'welcomed', but C++ has issues on creating & initializing complex static members, this was the best way that I could think of. Signed-off-by: Tomaz Canabrava --- color.h | 80 ++++++++++++++++++------------------ profile.c | 86 ++------------------------------------- qt-ui/profilegraphics.cpp | 81 ++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 122 deletions(-) diff --git a/color.h b/color.h index 5d88fb5f7..f2013bcc9 100644 --- a/color.h +++ b/color.h @@ -4,54 +4,56 @@ /* The colors are named by picking the closest match from http://chir.ag/projects/name-that-color */ +#include + // Greens -#define CAMARONE1 { 0.0, 0.4, 0.0, 1 } -#define FUNGREEN1 { 0.0, 0.4, 0.2, 1 } -#define FUNGREEN1_HIGH_TRANS { 0.0, 0.4, 0.2, 0.25 } -#define KILLARNEY1 { 0.2, 0.4, 0.2, 1 } -#define APPLE1 { 0.2, 0.6, 0.2, 1 } -#define APPLE1_MED_TRANS { 0.2, 0.6, 0.2, 0.5 } -#define APPLE1_HIGH_TRANS { 0.2, 0.6, 0.2, 0.25 } -#define LIMENADE1 { 0.4, 0.8, 0.0, 1 } -#define ATLANTIS1 { 0.4, 0.8, 0.2, 1 } -#define ATLANTIS2 { 0.6, 0.8, 0.2, 1 } -#define RIOGRANDE1 { 0.8, 0.8, 0.0, 1 } -#define EARLSGREEN1 { 0.8, 0.8, 0.2, 1 } -#define FORESTGREEN1 { 0.1, 0.5, 0.1, 1 } +#define CAMARONE1 QColor::fromRgbF( 0.0, 0.4, 0.0, 1 ) +#define FUNGREEN1 QColor::fromRgbF( 0.0, 0.4, 0.2, 1 ) +#define FUNGREEN1_HIGH_TRANS QColor::fromRgbF( 0.0, 0.4, 0.2, 0.25 ) +#define KILLARNEY1 QColor::fromRgbF( 0.2, 0.4, 0.2, 1 ) +#define APPLE1 QColor::fromRgbF( 0.2, 0.6, 0.2, 1 ) +#define APPLE1_MED_TRANS QColor::fromRgbF( 0.2, 0.6, 0.2, 0.5 ) +#define APPLE1_HIGH_TRANS QColor::fromRgbF( 0.2, 0.6, 0.2, 0.25 ) +#define LIMENADE1 QColor::fromRgbF( 0.4, 0.8, 0.0, 1 ) +#define ATLANTIS1 QColor::fromRgbF( 0.4, 0.8, 0.2, 1 ) +#define ATLANTIS2 QColor::fromRgbF( 0.6, 0.8, 0.2, 1 ) +#define RIOGRANDE1 QColor::fromRgbF( 0.8, 0.8, 0.0, 1 ) +#define EARLSGREEN1 QColor::fromRgbF( 0.8, 0.8, 0.2, 1 ) +#define FORESTGREEN1 QColor::fromRgbF( 0.1, 0.5, 0.1, 1 ) // Reds -#define PERSIANRED1 { 0.8, 0.2, 0.2, 1 } -#define TUSCANY1 { 0.8, 0.4, 0.2, 1 } -#define PIRATEGOLD1 { 0.8, 0.5, 0.0, 1 } -#define HOKEYPOKEY1 { 0.8, 0.6, 0.2, 1 } -#define CINNABAR1 { 0.9, 0.3, 0.2, 1 } -#define REDORANGE1 { 1.0, 0.2, 0.2, 1 } -#define REDORANGE1_HIGH_TRANS { 1.0, 0.2, 0.2, 0.25 } -#define REDORANGE1_MED_TRANS { 1.0, 0.2, 0.2, 0.5 } -#define RED1_MED_TRANS { 1.0, 0.0, 0.0, 0.5 } -#define RED1 { 1.0, 0.0, 0.0, 1 } +#define PERSIANRED1 QColor::fromRgbF( 0.8, 0.2, 0.2, 1 ) +#define TUSCANY1 QColor::fromRgbF( 0.8, 0.4, 0.2, 1 ) +#define PIRATEGOLD1 QColor::fromRgbF( 0.8, 0.5, 0.0, 1 ) +#define HOKEYPOKEY1 QColor::fromRgbF( 0.8, 0.6, 0.2, 1 ) +#define CINNABAR1 QColor::fromRgbF( 0.9, 0.3, 0.2, 1 ) +#define REDORANGE1 QColor::fromRgbF( 1.0, 0.2, 0.2, 1 ) +#define REDORANGE1_HIGH_TRANS QColor::fromRgbF( 1.0, 0.2, 0.2, 0.25 ) +#define REDORANGE1_MED_TRANS QColor::fromRgbF( 1.0, 0.2, 0.2, 0.5 ) +#define RED1_MED_TRANS QColor::fromRgbF( 1.0, 0.0, 0.0, 0.5 ) +#define RED1 QColor::fromRgbF( 1.0, 0.0, 0.0, 1 ) // Monochromes -#define BLACK1_LOW_TRANS { 0.0, 0.0, 0.0, 0.75 } -#define BLACK1_HIGH_TRANS { 0.0, 0.0, 0.0, 0.25 } -#define TUNDORA1_MED_TRANS { 0.3, 0.3, 0.3, 0.5 } -#define MERCURY1_MED_TRANS { 0.9, 0.9, 0.9, 0.5 } -#define CONCRETE1_LOWER_TRANS { 0.95, 0.95, 0.95, 0.9 } -#define WHITE1_MED_TRANS { 1.0, 1.0, 1.0, 0.5 } -#define WHITE1 { 1.0, 1.0, 1.0, 1 } +#define BLACK1_LOW_TRANS QColor::fromRgbF( 0.0, 0.0, 0.0, 0.75 ) +#define BLACK1_HIGH_TRANS QColor::fromRgbF( 0.0, 0.0, 0.0, 0.25 ) +#define TUNDORA1_MED_TRANS QColor::fromRgbF( 0.3, 0.3, 0.3, 0.5 ) +#define MERCURY1_MED_TRANS QColor::fromRgbF( 0.9, 0.9, 0.9, 0.5 ) +#define CONCRETE1_LOWER_TRANS QColor::fromRgbF( 0.95, 0.95, 0.95, 0.9 ) +#define WHITE1_MED_TRANS QColor::fromRgbF( 1.0, 1.0, 1.0, 0.5 ) +#define WHITE1 QColor::fromRgbF( 1.0, 1.0, 1.0, 1 ) // Blues -#define GOVERNORBAY2 { 0.2, 0.2, 0.7, 1 } -#define GOVERNORBAY1_MED_TRANS { 0.2, 0.2, 0.8, 0.5 } -#define ROYALBLUE2 { 0.2, 0.2, 0.9, 1 } -#define ROYALBLUE2_LOW_TRANS { 0.2, 0.2, 0.9, 0.75 } +#define GOVERNORBAY2 QColor::fromRgbF( 0.2, 0.2, 0.7, 1 ) +#define GOVERNORBAY1_MED_TRANS QColor::fromRgbF( 0.2, 0.2, 0.8, 0.5 ) +#define ROYALBLUE2 QColor::fromRgbF( 0.2, 0.2, 0.9, 1 ) +#define ROYALBLUE2_LOW_TRANS QColor::fromRgbF( 0.2, 0.2, 0.9, 0.75 ) // Yellows / BROWNS -#define SPRINGWOOD1 { 0.95, 0.95, 0.9, 1 } -#define BROOM1_LOWER_TRANS { 1.0, 1.0, 0.1, 0.9 } -#define PEANUT { 0.5, 0.2, 0.1, 1.0 } -#define PEANUT_MED_TRANS { 0.5, 0.2, 0.1, 0.5 } +#define SPRINGWOOD1 QColor::fromRgbF( 0.95, 0.95, 0.9, 1 ) +#define BROOM1_LOWER_TRANS QColor::fromRgbF( 1.0, 1.0, 0.1, 0.9 ) +#define PEANUT QColor::fromRgbF( 0.5, 0.2, 0.1, 1.0 ) +#define PEANUT_MED_TRANS QColor::fromRgbF( 0.5, 0.2, 0.1, 0.5 ) // Magentas -#define MEDIUMREDVIOLET1_HIGHER_TRANS { 0.7, 0.2, 0.7, 0.1 } +#define MEDIUMREDVIOLET1_HIGHER_TRANS QColor::fromRgbF( 0.7, 0.2, 0.7, 0.1 ) #endif diff --git a/profile.c b/profile.c index b9a633cf6..fa0a24a06 100644 --- a/profile.c +++ b/profile.c @@ -10,7 +10,7 @@ #include "display-gtk.h" #endif #include "divelist.h" -#include "color.h" + #include "libdivecomputer/parser.h" #include "libdivecomputer/version.h" @@ -59,88 +59,6 @@ struct plot_data { #define INTERPOLATED_PRESSURE(_entry) (_entry)->pressure[INTERPOLATED_PR] #define GET_PRESSURE(_entry) (SENSOR_PRESSURE(_entry) ? : INTERPOLATED_PRESSURE(_entry)) -#define SAC_COLORS_START_IDX SAC_1 -#define SAC_COLORS 9 -#define VELOCITY_COLORS_START_IDX VELO_STABLE -#define VELOCITY_COLORS 5 - -typedef enum { - /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */ - SAC_1, SAC_2, SAC_3, SAC_4, SAC_5, SAC_6, SAC_7, SAC_8, SAC_9, - - /* Velocity colors. Order is still important, ref VELOCITY_COLORS_START_IDX. */ - VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY, - - /* gas colors */ - PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, PP_LINES, - - /* Other colors */ - TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW, - SMOOTHED, MINUTE, TIME_GRID, TIME_TEXT, DEPTH_GRID, MEAN_DEPTH, DEPTH_TOP, - DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND, - CEILING_SHALLOW, CEILING_DEEP, CALC_CEILING_SHALLOW, CALC_CEILING_DEEP -} color_indice_t; - -typedef struct { - /* media[0] is screen, media[1] is b/w printer media[2] is color printer */ - struct rgba { - double r,g,b,a; - } media[3]; -} color_t; - -/* [color indice] = {{screen color, b/w printer color, color printer}} printer & screen colours could be different */ -static const color_t profile_color[] = { - [SAC_1] = {{FUNGREEN1, BLACK1_LOW_TRANS, FUNGREEN1}}, - [SAC_2] = {{APPLE1, BLACK1_LOW_TRANS, APPLE1}}, - [SAC_3] = {{ATLANTIS1, BLACK1_LOW_TRANS, ATLANTIS1}}, - [SAC_4] = {{ATLANTIS2, BLACK1_LOW_TRANS, ATLANTIS2}}, - [SAC_5] = {{EARLSGREEN1, BLACK1_LOW_TRANS, EARLSGREEN1}}, - [SAC_6] = {{HOKEYPOKEY1, BLACK1_LOW_TRANS, HOKEYPOKEY1}}, - [SAC_7] = {{TUSCANY1, BLACK1_LOW_TRANS, TUSCANY1}}, - [SAC_8] = {{CINNABAR1, BLACK1_LOW_TRANS, CINNABAR1}}, - [SAC_9] = {{REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1}}, - - [VELO_STABLE] = {{CAMARONE1, BLACK1_LOW_TRANS, CAMARONE1}}, - [VELO_SLOW] = {{LIMENADE1, BLACK1_LOW_TRANS, LIMENADE1}}, - [VELO_MODERATE] = {{RIOGRANDE1, BLACK1_LOW_TRANS, RIOGRANDE1}}, - [VELO_FAST] = {{PIRATEGOLD1, BLACK1_LOW_TRANS, PIRATEGOLD1}}, - [VELO_CRAZY] = {{RED1, BLACK1_LOW_TRANS, RED1}}, - - [PO2] = {{APPLE1, BLACK1_LOW_TRANS, APPLE1}}, - [PO2_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}}, - [PN2] = {{BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS}}, - [PN2_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}}, - [PHE] = {{PEANUT, BLACK1_LOW_TRANS, PEANUT}}, - [PHE_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}}, - [PP_LINES] = {{BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS}}, - - [TEXT_BACKGROUND] = {{CONCRETE1_LOWER_TRANS, WHITE1, CONCRETE1_LOWER_TRANS}}, - [ALERT_BG] = {{BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS, BROOM1_LOWER_TRANS}}, - [ALERT_FG] = {{BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS}}, - [EVENTS] = {{REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1}}, - [SAMPLE_DEEP] = {{PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1}}, - [SAMPLE_SHALLOW] = {{PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1}}, - [SMOOTHED] = {{REDORANGE1_HIGH_TRANS, BLACK1_LOW_TRANS, REDORANGE1_HIGH_TRANS}}, - [MINUTE] = {{MEDIUMREDVIOLET1_HIGHER_TRANS, BLACK1_LOW_TRANS, MEDIUMREDVIOLET1_HIGHER_TRANS}}, - [TIME_GRID] = {{WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS}}, - [TIME_TEXT] = {{FORESTGREEN1, BLACK1_LOW_TRANS, FORESTGREEN1}}, - [DEPTH_GRID] = {{WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS}}, - [MEAN_DEPTH] = {{REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS}}, - [DEPTH_BOTTOM] = {{GOVERNORBAY1_MED_TRANS, BLACK1_HIGH_TRANS, GOVERNORBAY1_MED_TRANS}}, - [DEPTH_TOP] = {{MERCURY1_MED_TRANS, WHITE1_MED_TRANS, MERCURY1_MED_TRANS}}, - [TEMP_TEXT] = {{GOVERNORBAY2, BLACK1_LOW_TRANS, GOVERNORBAY2}}, - [TEMP_PLOT] = {{ROYALBLUE2_LOW_TRANS, BLACK1_LOW_TRANS, ROYALBLUE2_LOW_TRANS}}, - [SAC_DEFAULT] = {{WHITE1, BLACK1_LOW_TRANS, FORESTGREEN1}}, - [BOUNDING_BOX] = {{WHITE1, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS}}, - [PRESSURE_TEXT] = {{KILLARNEY1, BLACK1_LOW_TRANS, KILLARNEY1}}, - [BACKGROUND] = {{SPRINGWOOD1, BLACK1_LOW_TRANS, SPRINGWOOD1}}, - [CEILING_SHALLOW] = {{REDORANGE1_HIGH_TRANS, BLACK1_HIGH_TRANS, REDORANGE1_HIGH_TRANS}}, - [CEILING_DEEP] = {{RED1_MED_TRANS, BLACK1_HIGH_TRANS, RED1_MED_TRANS}}, - [CALC_CEILING_SHALLOW] = {{FUNGREEN1_HIGH_TRANS, BLACK1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS}}, - [CALC_CEILING_DEEP] = {{APPLE1_HIGH_TRANS, BLACK1_HIGH_TRANS, APPLE1_HIGH_TRANS}}, - -}; - #if USE_GTK_UI /* Scale to 0,0 -> maxx,maxy */ @@ -270,11 +188,13 @@ int get_maxdepth(struct plot_info *pi) return md; } +#if 0 typedef struct { double size; color_indice_t color; double hpos, vpos; } text_render_options_t; +#endif #define RIGHT (-1.0) #define CENTER (-0.5) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 59a47826f..fab17beb7 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -5,10 +5,91 @@ #include +#include "../color.h" + +#define SAC_COLORS_START_IDX SAC_1 +#define SAC_COLORS 9 +#define VELOCITY_COLORS_START_IDX VELO_STABLE +#define VELOCITY_COLORS 5 + +typedef enum { + /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */ + SAC_1, SAC_2, SAC_3, SAC_4, SAC_5, SAC_6, SAC_7, SAC_8, SAC_9, + + /* Velocity colors. Order is still important, ref VELOCITY_COLORS_START_IDX. */ + VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY, + + /* gas colors */ + PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, PP_LINES, + + /* Other colors */ + TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW, + SMOOTHED, MINUTE, TIME_GRID, TIME_TEXT, DEPTH_GRID, MEAN_DEPTH, DEPTH_TOP, + DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND, + CEILING_SHALLOW, CEILING_DEEP, CALC_CEILING_SHALLOW, CALC_CEILING_DEEP +} color_indice_t; + +/* profile_color[color indice] = COLOR(screen color, b/w printer color, color printer}} printer & screen colours could be different */ +QMap > profile_color; +void fill_profile_color() +{ +#define COLOR(x, y, z) QVector() << x << y << z; + profile_color[SAC_1] = COLOR(FUNGREEN1, BLACK1_LOW_TRANS, FUNGREEN1); + profile_color[SAC_2] = COLOR(APPLE1, BLACK1_LOW_TRANS, APPLE1); + profile_color[SAC_3] = COLOR(ATLANTIS1, BLACK1_LOW_TRANS, ATLANTIS1); + profile_color[SAC_4] = COLOR(ATLANTIS2, BLACK1_LOW_TRANS, ATLANTIS2); + profile_color[SAC_5] = COLOR(EARLSGREEN1, BLACK1_LOW_TRANS, EARLSGREEN1); + profile_color[SAC_6] = COLOR(HOKEYPOKEY1, BLACK1_LOW_TRANS, HOKEYPOKEY1); + profile_color[SAC_7] = COLOR(TUSCANY1, BLACK1_LOW_TRANS, TUSCANY1); + profile_color[SAC_8] = COLOR(CINNABAR1, BLACK1_LOW_TRANS, CINNABAR1); + profile_color[SAC_9] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1); + + profile_color[VELO_STABLE] = COLOR(CAMARONE1, BLACK1_LOW_TRANS, CAMARONE1); + profile_color[VELO_SLOW] = COLOR(LIMENADE1, BLACK1_LOW_TRANS, LIMENADE1); + profile_color[VELO_MODERATE] = COLOR(RIOGRANDE1, BLACK1_LOW_TRANS, RIOGRANDE1); + profile_color[VELO_FAST] = COLOR(PIRATEGOLD1, BLACK1_LOW_TRANS, PIRATEGOLD1); + profile_color[VELO_CRAZY] = COLOR(RED1, BLACK1_LOW_TRANS, RED1); + + profile_color[PO2] = COLOR(APPLE1, BLACK1_LOW_TRANS, APPLE1); + profile_color[PO2_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1); + profile_color[PN2] = COLOR(BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS); + profile_color[PN2_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1); + profile_color[PHE] = COLOR(PEANUT, BLACK1_LOW_TRANS, PEANUT); + profile_color[PHE_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1); + profile_color[PP_LINES] = COLOR(BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS); + + profile_color[TEXT_BACKGROUND] = COLOR(CONCRETE1_LOWER_TRANS, WHITE1, CONCRETE1_LOWER_TRANS); + profile_color[ALERT_BG] = COLOR(BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS, BROOM1_LOWER_TRANS); + profile_color[ALERT_FG] = COLOR(BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS); + profile_color[EVENTS] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1); + profile_color[SAMPLE_DEEP] = COLOR(PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1); + profile_color[SAMPLE_SHALLOW] = COLOR(PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1); + profile_color[SMOOTHED] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_LOW_TRANS, REDORANGE1_HIGH_TRANS); + profile_color[MINUTE] = COLOR(MEDIUMREDVIOLET1_HIGHER_TRANS, BLACK1_LOW_TRANS, MEDIUMREDVIOLET1_HIGHER_TRANS); + profile_color[TIME_GRID] = COLOR(WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS); + profile_color[TIME_TEXT] = COLOR(FORESTGREEN1, BLACK1_LOW_TRANS, FORESTGREEN1); + profile_color[DEPTH_GRID] = COLOR(WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS); + profile_color[MEAN_DEPTH] = COLOR(REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS); + profile_color[DEPTH_BOTTOM] = COLOR(GOVERNORBAY1_MED_TRANS, BLACK1_HIGH_TRANS, GOVERNORBAY1_MED_TRANS); + profile_color[DEPTH_TOP] = COLOR(MERCURY1_MED_TRANS, WHITE1_MED_TRANS, MERCURY1_MED_TRANS); + profile_color[TEMP_TEXT] = COLOR(GOVERNORBAY2, BLACK1_LOW_TRANS, GOVERNORBAY2); + profile_color[TEMP_PLOT] = COLOR(ROYALBLUE2_LOW_TRANS, BLACK1_LOW_TRANS, ROYALBLUE2_LOW_TRANS); + profile_color[SAC_DEFAULT] = COLOR(WHITE1, BLACK1_LOW_TRANS, FORESTGREEN1); + profile_color[BOUNDING_BOX] = COLOR(WHITE1, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS); + profile_color[PRESSURE_TEXT] = COLOR(KILLARNEY1, BLACK1_LOW_TRANS, KILLARNEY1); + profile_color[BACKGROUND] = COLOR(SPRINGWOOD1, BLACK1_LOW_TRANS, SPRINGWOOD1); + profile_color[CEILING_SHALLOW] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_HIGH_TRANS, REDORANGE1_HIGH_TRANS); + profile_color[CEILING_DEEP] = COLOR(RED1_MED_TRANS, BLACK1_HIGH_TRANS, RED1_MED_TRANS); + profile_color[CALC_CEILING_SHALLOW] = COLOR(FUNGREEN1_HIGH_TRANS, BLACK1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS); + profile_color[CALC_CEILING_DEEP] = COLOR(APPLE1_HIGH_TRANS, BLACK1_HIGH_TRANS, APPLE1_HIGH_TRANS); +#undef COLOR +} + ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) { setScene(new QGraphicsScene()); scene()->setSceneRect(0,0,100,100); + fill_profile_color(); } void ProfileGraphicsView::plot(struct dive *d) From c74dc111675980ddd1a1b256b38a41304c792d89 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 17:24:23 -0300 Subject: [PATCH 126/226] Moved the plot from the cairo version to the Qt version Started working on the Qt version of the Plot, initially nothing is printed - but this is not a bad thing, the program doesn't explodes too. :) some work had to be done about the 'bool/gboolean' stuff so I removed all gbooleans in the code that I'v encountered. A new file was created ( profile.h ) so I could put the signatures of helper methods that cairo used to call. till now the code computes the max limits. Next patch the first drawing will be made. Signed-off-by: Tomaz Canabrava --- display.h | 27 +++++--- profile.c | 123 --------------------------------- profile.h | 25 +++++++ qt-ui/profilegraphics.cpp | 140 +++++++++++++++++++++++++++++++++++++- 4 files changed, 182 insertions(+), 133 deletions(-) create mode 100644 profile.h diff --git a/display.h b/display.h index b1a034e88..bc60c059f 100644 --- a/display.h +++ b/display.h @@ -1,18 +1,26 @@ #ifndef DISPLAY_H #define DISPLAY_H -#include - #ifdef __cplusplus extern "C" { +#else +#if __STDC_VERSION__ >= 199901L +#include +#else +typedef int bool; +#endif #endif #define SCALE_SCREEN 1.0 -#define SCALE_PRINT (1.0 / get_screen_dpi()) +#warning "PORT THE get_screen_dpi to Qt" +#define SCALE_PRINT 1.0 +//#define SCALE_PRINT (1.0 / get_screen_dpi()) extern void repaint_dive(void); extern void do_print(void); -extern gdouble get_screen_dpi(void); + +// Commented out because I don't know how to get the dpi on a paint device yet. +// extern gdouble get_screen_dpi(void); /* Plot info with smoothing, velocity indication * and one-, two- and three-minute minimums and maximums */ @@ -24,10 +32,15 @@ struct plot_info { int mintemp, maxtemp; double endtempcoord; double maxpp; - gboolean has_ndl; + bool has_ndl; struct plot_data *entry; }; +/* +// I'm not sure if this is needed anymore - but keeping this here +// so I wont break stuff trying to redo the well. +*/ + /* * Cairo scaling really is horribly horribly mis-designed. * @@ -38,8 +51,6 @@ struct plot_info { */ struct graphics_context { int printer; - cairo_t *cr; - cairo_rectangle_t drawing_area; double maxx, maxy; double leftx, rightx; double topy, bottomy; @@ -61,7 +72,7 @@ struct options { enum { PRETTY, TABLE, TWOPERPAGE } type; int print_selected; int color_selected; - gboolean notes_up; + bool notes_up; int profile_height, notes_height, tanks_height; }; diff --git a/profile.c b/profile.c index fa0a24a06..3f413f4ed 100644 --- a/profile.c +++ b/profile.c @@ -18,9 +18,6 @@ int selected_dive = 0; char zoomed_plot = 0; char dc_number = 0; -#if USE_GTK_UI -static double plot_scale = SCALE_SCREEN; -#endif static struct plot_data *last_pi_entry = NULL; @@ -1951,126 +1948,6 @@ struct divecomputer *select_dc(struct divecomputer *main) return main; } -#if USE_GTK_UI -void plot(struct graphics_context *gc, struct dive *dive, scale_mode_t scale) -{ - struct plot_info *pi; - struct divecomputer *dc = &dive->dc; - cairo_rectangle_t *drawing_area = &gc->drawing_area; - const char *nickname; - - plot_set_scale(scale); - - if (!dc->samples) { - static struct sample fake[4]; - static struct divecomputer fakedc; - fakedc = dive->dc; - fakedc.sample = fake; - fakedc.samples = 4; - - /* The dive has no samples, so create a few fake ones. This assumes an - ascent/descent rate of 9 m/min, which is just below the limit for FAST. */ - int duration = dive->dc.duration.seconds; - int maxdepth = dive->dc.maxdepth.mm; - int asc_desc_time = dive->dc.maxdepth.mm*60/9000; - if (asc_desc_time * 2 >= duration) - asc_desc_time = duration / 2; - fake[1].time.seconds = asc_desc_time; - fake[1].depth.mm = maxdepth; - fake[2].time.seconds = duration - asc_desc_time; - fake[2].depth.mm = maxdepth; - fake[3].time.seconds = duration * 1.00; - fakedc.events = dc->events; - dc = &fakedc; - } - - /* - * Set up limits that are independent of - * the dive computer - */ - calculate_max_limits(dive, dc, gc); - - /* shift the drawing area so we have a nice margin around it */ - cairo_translate(gc->cr, drawing_area->x, drawing_area->y); - cairo_set_line_width_scaled(gc->cr, 1); - cairo_set_line_cap(gc->cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(gc->cr, CAIRO_LINE_JOIN_ROUND); - - /* - * We don't use "cairo_translate()" because that doesn't - * scale line width etc. But the actual scaling we need - * do set up ourselves.. - * - * Snif. What a pity. - */ - gc->maxx = (drawing_area->width - 2*drawing_area->x); - gc->maxy = (drawing_area->height - 2*drawing_area->y); - - dc = select_dc(dc); - - /* This is per-dive-computer. Right now we just do the first one */ - pi = create_plot_info(dive, dc, gc); - - /* Depth profile */ - plot_depth_profile(gc, pi); - plot_events(gc, pi, dc); - - /* Temperature profile */ - plot_temperature_profile(gc, pi); - - /* Cylinder pressure plot */ - plot_cylinder_pressure(gc, pi, dive, dc); - - /* Text on top of all graphs.. */ - plot_temperature_text(gc, pi); - plot_depth_text(gc, pi); - plot_cylinder_pressure_text(gc, pi); - plot_deco_text(gc, pi); - - /* Bounding box last */ - gc->leftx = 0; gc->rightx = 1.0; - gc->topy = 0; gc->bottomy = 1.0; - - set_source_rgba(gc, BOUNDING_BOX); - cairo_set_line_width_scaled(gc->cr, 1); - move_to(gc, 0, 0); - line_to(gc, 0, 1); - line_to(gc, 1, 1); - line_to(gc, 1, 0); - cairo_close_path(gc->cr); - cairo_stroke(gc->cr); - - /* Put the dive computer name in the lower left corner */ - nickname = get_dc_nickname(dc->model, dc->deviceid); - if (!nickname || *nickname == '\0') - nickname = dc->model; - if (nickname) { - static const text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; - plot_text(gc, &computer, 0, 1, "%s", nickname); - } - - if (PP_GRAPHS_ENABLED) { - plot_pp_gas_profile(gc, pi); - plot_pp_text(gc, pi); - } - - /* now shift the translation back by half the margin; - * this way we can draw the vertical scales on both sides */ - cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); - gc->maxx += drawing_area->x; - gc->leftx = -(drawing_area->x / drawing_area->width) / 2.0; - gc->rightx = 1.0 - gc->leftx; - - plot_depth_scale(gc, pi); - - if (gc->printer) { - free(pi->entry); - last_pi_entry = pi->entry = NULL; - pi->nr = 0; - } -} -#endif /* USE_GTK_UI */ - static void plot_string(struct plot_data *entry, char *buf, size_t bufsize, int depth, int pressure, int temp, gboolean has_ndl) { diff --git a/profile.h b/profile.h new file mode 100644 index 000000000..66d1ee190 --- /dev/null +++ b/profile.h @@ -0,0 +1,25 @@ +#ifndef PROFILE_H +#define PROFILE_H + +#ifdef __cplusplus +extern "C" { +#else +#if __STDC_VERSION__ >= 199901L +#include +#else +typedef int bool; +#endif +#endif + +struct dive; +struct divecomputer; +struct graphics_context; +struct plot_info; + +void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); +struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index fab17beb7..004c78a47 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -6,12 +6,17 @@ #include #include "../color.h" +#include "../display.h" +#include "../dive.h" +#include "../profile.h" #define SAC_COLORS_START_IDX SAC_1 #define SAC_COLORS 9 #define VELOCITY_COLORS_START_IDX VELO_STABLE #define VELOCITY_COLORS 5 +static double plot_scale = SCALE_SCREEN; + typedef enum { /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */ SAC_1, SAC_2, SAC_3, SAC_4, SAC_5, SAC_6, SAC_7, SAC_8, SAC_9, @@ -92,9 +97,140 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent fill_profile_color(); } -void ProfileGraphicsView::plot(struct dive *d) +static void plot_set_scale(scale_mode_t scale) { - qDebug() << "Start the plotting of the dive here."; + switch (scale) { + default: + case SC_SCREEN: + plot_scale = SCALE_SCREEN; + break; + case SC_PRINT: + plot_scale = SCALE_PRINT; + break; + } +} + +void ProfileGraphicsView::plot(struct dive *dive) +{ + struct plot_info *pi; + struct divecomputer *dc = &dive->dc; + + // This was passed around in the Cairo version / needed? + graphics_context gc; + const char *nickname; + + // Fix this for printing / screen later. + // plot_set_scale( scale_mode_t); + + if (!dc->samples) { + static struct sample fake[4]; + static struct divecomputer fakedc; + fakedc = dive->dc; + fakedc.sample = fake; + fakedc.samples = 4; + + /* The dive has no samples, so create a few fake ones. This assumes an + ascent/descent rate of 9 m/min, which is just below the limit for FAST. */ + int duration = dive->dc.duration.seconds; + int maxdepth = dive->dc.maxdepth.mm; + int asc_desc_time = dive->dc.maxdepth.mm*60/9000; + if (asc_desc_time * 2 >= duration) + asc_desc_time = duration / 2; + fake[1].time.seconds = asc_desc_time; + fake[1].depth.mm = maxdepth; + fake[2].time.seconds = duration - asc_desc_time; + fake[2].depth.mm = maxdepth; + fake[3].time.seconds = duration * 1.00; + fakedc.events = dc->events; + dc = &fakedc; + } + + /* + * Set up limits that are independent of + * the dive computer + */ + calculate_max_limits(dive, dc, &gc); + +#if 0 + /* shift the drawing area so we have a nice margin around it */ + cairo_translate(gc->cr, drawing_area->x, drawing_area->y); + cairo_set_line_width_scaled(gc->cr, 1); + cairo_set_line_cap(gc->cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join(gc->cr, CAIRO_LINE_JOIN_ROUND); + + /* + * We don't use "cairo_translate()" because that doesn't + * scale line width etc. But the actual scaling we need + * do set up ourselves.. + * + * Snif. What a pity. + */ + gc->maxx = (drawing_area->width - 2*drawing_area->x); + gc->maxy = (drawing_area->height - 2*drawing_area->y); + + dc = select_dc(dc); + + /* This is per-dive-computer. Right now we just do the first one */ + pi = create_plot_info(dive, dc, gc); + + /* Depth profile */ + plot_depth_profile(gc, pi); + plot_events(gc, pi, dc); + + /* Temperature profile */ + plot_temperature_profile(gc, pi); + + /* Cylinder pressure plot */ + plot_cylinder_pressure(gc, pi, dive, dc); + + /* Text on top of all graphs.. */ + plot_temperature_text(gc, pi); + plot_depth_text(gc, pi); + plot_cylinder_pressure_text(gc, pi); + plot_deco_text(gc, pi); + + /* Bounding box last */ + gc->leftx = 0; gc->rightx = 1.0; + gc->topy = 0; gc->bottomy = 1.0; + + set_source_rgba(gc, BOUNDING_BOX); + cairo_set_line_width_scaled(gc->cr, 1); + move_to(gc, 0, 0); + line_to(gc, 0, 1); + line_to(gc, 1, 1); + line_to(gc, 1, 0); + cairo_close_path(gc->cr); + cairo_stroke(gc->cr); + + /* Put the dive computer name in the lower left corner */ + nickname = get_dc_nickname(dc->model, dc->deviceid); + if (!nickname || *nickname == '\0') + nickname = dc->model; + if (nickname) { + static const text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; + plot_text(gc, &computer, 0, 1, "%s", nickname); + } + + if (PP_GRAPHS_ENABLED) { + plot_pp_gas_profile(gc, pi); + plot_pp_text(gc, pi); + } + + /* now shift the translation back by half the margin; + * this way we can draw the vertical scales on both sides */ + cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); + gc->maxx += drawing_area->x; + gc->leftx = -(drawing_area->x / drawing_area->width) / 2.0; + gc->rightx = 1.0 - gc->leftx; + + plot_depth_scale(gc, pi); + + if (gc->printer) { + free(pi->entry); + last_pi_entry = pi->entry = NULL; + pi->nr = 0; + } +#endif } void ProfileGraphicsView::resizeEvent(QResizeEvent *event) From 1b1ea35fac63ef7a6909f28e92bc2b4c24c2b7f4 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 19:20:49 -0300 Subject: [PATCH 127/226] Start plotting something. The first plotting method was removed from profile.c to profilegraphics.cpp and some conversion ( almost 1 to 1 ) was made so that the code could work. Since the code is big - this commit has just a part of it working - it plots the grid. but already works for testing the resizing of the window and Zooming ( unimplemented ) Signed-off-by: Tomaz Canabrava --- profile.c | 204 -------------------------------- profile.h | 15 +++ qt-ui/profilegraphics.cpp | 242 ++++++++++++++++++++++++++++++++++++-- qt-ui/profilegraphics.h | 2 + 4 files changed, 247 insertions(+), 216 deletions(-) diff --git a/profile.c b/profile.c index 3f413f4ed..be6efa1e6 100644 --- a/profile.c +++ b/profile.c @@ -58,11 +58,6 @@ struct plot_data { #if USE_GTK_UI -/* Scale to 0,0 -> maxx,maxy */ -#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx) -#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) -#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y) - /* keep the last used gc around so we can invert the SCALEX calculation in * order to calculate a time value for an x coordinate */ static struct graphics_context last_gc; @@ -611,205 +606,6 @@ static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *p } } -static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) -{ - int i, incr; - cairo_t *cr = gc->cr; - int sec, depth; - struct plot_data *entry; - int maxtime, maxdepth, marker, maxline; - int increments[8] = { 10, 20, 30, 60, 5*60, 10*60, 15*60, 30*60 }; - - /* Get plot scaling limits */ - maxtime = get_maxtime(pi); - maxdepth = get_maxdepth(pi); - - gc->maxtime = maxtime; - - /* Time markers: at most every 10 seconds, but no more than 12 markers. - * We start out with 10 seconds and increment up to 30 minutes, - * depending on the dive time. - * This allows for 6h dives - enough (I hope) for even the craziest - * divers - but just in case, for those 8h depth-record-breaking dives, - * we double the interval if this still doesn't get us to 12 or fewer - * time markers */ - i = 0; - while (maxtime / increments[i] > 12 && i < 7) - i++; - incr = increments[i]; - while (maxtime / incr > 12) - incr *= 2; - - gc->leftx = 0; gc->rightx = maxtime; - gc->topy = 0; gc->bottomy = 1.0; - - last_gc = *gc; - - set_source_rgba(gc, TIME_GRID); - cairo_set_line_width_scaled(gc->cr, 2); - - for (i = incr; i < maxtime; i += incr) { - move_to(gc, i, 0); - line_to(gc, i, 1); - } - cairo_stroke(cr); - - /* now the text on the time markers */ - text_render_options_t tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP}; - if (maxtime < 600) { - /* Be a bit more verbose with shorter dives */ - for (i = incr; i < maxtime; i += incr) - plot_text(gc, &tro, i, 1, "%02d:%02d", i/60, i%60); - } else { - /* Only render the time on every second marker for normal dives */ - for (i = incr; i < maxtime; i += 2 * incr) - plot_text(gc, &tro, i, 1, "%d", i/60); - } - /* Depth markers: every 30 ft or 10 m*/ - gc->leftx = 0; gc->rightx = 1.0; - gc->topy = 0; gc->bottomy = maxdepth; - switch (prefs.units.length) { - case METERS: marker = 10000; break; - case FEET: marker = 9144; break; /* 30 ft */ - } - maxline = MAX(pi->maxdepth + marker, maxdepth * 2 / 3); - set_source_rgba(gc, DEPTH_GRID); - for (i = marker; i < maxline; i += marker) { - move_to(gc, 0, i); - line_to(gc, 1, i); - } - cairo_stroke(cr); - - gc->leftx = 0; gc->rightx = maxtime; - - /* Show mean depth */ - if (! gc->printer) { - set_source_rgba(gc, MEAN_DEPTH); - move_to(gc, 0, pi->meandepth); - line_to(gc, pi->entry[pi->nr - 1].sec, pi->meandepth); - cairo_stroke(cr); - } - - /* - * These are good for debugging text placement etc, - * but not for actual display.. - */ - if (0) { - plot_smoothed_profile(gc, pi); - plot_minmax_profile(gc, pi); - } - - /* Do the depth profile for the neat fill */ - gc->topy = 0; gc->bottomy = maxdepth; - - cairo_pattern_t *pat; - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 1, DEPTH_BOTTOM); - pattern_add_color_stop_rgba (gc, pat, 0, DEPTH_TOP); - - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - cairo_set_line_width_scaled(gc->cr, 2); - - entry = pi->entry; - move_to(gc, 0, 0); - for (i = 0; i < pi->nr; i++, entry++) - line_to(gc, entry->sec, entry->depth); - - /* Show any ceiling we may have encountered */ - for (i = pi->nr - 1; i >= 0; i--, entry--) { - if (entry->ndl) { - /* non-zero NDL implies this is a safety stop, no ceiling */ - line_to(gc, entry->sec, 0); - } else if (entry->stopdepth < entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); - } else { - line_to(gc, entry->sec, entry->depth); - } - } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - - /* if the user wants the deco ceiling more visible, do that here (this - * basically draws over the background that we had allowed to shine - * through so far) */ - if (prefs.profile_red_ceiling) { - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - entry = pi->entry; - move_to(gc, 0, 0); - for (i = 0; i < pi->nr; i++, entry++) { - if (entry->ndl == 0 && entry->stopdepth) { - if (entry->ndl == 0 && entry->stopdepth < entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); - } else { - line_to(gc, entry->sec, entry->depth); - } - } else { - line_to(gc, entry->sec, 0); - } - } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - } - /* finally, plot the calculated ceiling over all this */ - if (prefs.profile_calc_ceiling) { - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CALC_CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CALC_CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - entry = pi->entry; - move_to(gc, 0, 0); - for (i = 0; i < pi->nr; i++, entry++) { - if (entry->ceiling) - line_to(gc, entry->sec, entry->ceiling); - else - line_to(gc, entry->sec, 0); - } - line_to(gc, (entry-1)->sec, 0); /* make sure we end at 0 */ - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - } - /* next show where we have been bad and crossed the dc's ceiling */ - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - entry = pi->entry; - move_to(gc, 0, 0); - for (i = 0; i < pi->nr; i++, entry++) - line_to(gc, entry->sec, entry->depth); - - for (i = pi->nr - 1; i >= 0; i--, entry--) { - if (entry->ndl == 0 && entry->stopdepth > entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); - } else { - line_to(gc, entry->sec, entry->depth); - } - } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - - /* Now do it again for the velocity colors */ - entry = pi->entry; - for (i = 1; i < pi->nr; i++) { - entry++; - sec = entry->sec; - /* we want to draw the segments in different colors - * representing the vertical velocity, so we need to - * chop this into short segments */ - depth = entry->depth; - set_source_rgba(gc, VELOCITY_COLORS_START_IDX + entry->velocity); - move_to(gc, entry[-1].sec, entry[-1].depth); - line_to(gc, sec, depth); - cairo_stroke(cr); - } -} static int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi) { diff --git a/profile.h b/profile.h index 66d1ee190..1ad6625db 100644 --- a/profile.h +++ b/profile.h @@ -19,6 +19,21 @@ struct plot_info; void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); +/* + * When showing dive profiles, we scale things to the + * current dive. However, we don't scale past less than + * 30 minutes or 90 ft, just so that small dives show + * up as such unless zoom is enabled. + * We also need to add 180 seconds at the end so the min/max + * plots correctly + */ +int get_maxtime(struct plot_info *pi); + +/* get the maximum depth to which we want to plot + * take into account the additional verical space needed to plot + * partial pressure graphs */ +int get_maxdepth(struct plot_info *pi); + #ifdef __cplusplus } #endif diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 004c78a47..01ad5ab45 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -2,7 +2,9 @@ #include #include - +#include +#include +#include #include #include "../color.h" @@ -15,6 +17,12 @@ #define VELOCITY_COLORS_START_IDX VELO_STABLE #define VELOCITY_COLORS 5 +/* Scale to 0,0 -> maxx,maxy */ +#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx) +#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) +#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y) + +static struct graphics_context last_gc; static double plot_scale = SCALE_SCREEN; typedef enum { @@ -93,6 +101,7 @@ void fill_profile_color() ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) { setScene(new QGraphicsScene()); + setBackgroundBrush(QColor("#F3F3E6")); scene()->setSceneRect(0,0,100,100); fill_profile_color(); } @@ -112,6 +121,13 @@ static void plot_set_scale(scale_mode_t scale) void ProfileGraphicsView::plot(struct dive *dive) { + // Clear the items before drawing this dive. + qDeleteAll(scene()->items()); + scene()->clear(); + + if(!dive) + return; + struct plot_info *pi; struct divecomputer *dc = &dive->dc; @@ -151,13 +167,6 @@ void ProfileGraphicsView::plot(struct dive *dive) */ calculate_max_limits(dive, dc, &gc); -#if 0 - /* shift the drawing area so we have a nice margin around it */ - cairo_translate(gc->cr, drawing_area->x, drawing_area->y); - cairo_set_line_width_scaled(gc->cr, 1); - cairo_set_line_cap(gc->cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(gc->cr, CAIRO_LINE_JOIN_ROUND); - /* * We don't use "cairo_translate()" because that doesn't * scale line width etc. But the actual scaling we need @@ -165,16 +174,18 @@ void ProfileGraphicsView::plot(struct dive *dive) * * Snif. What a pity. */ - gc->maxx = (drawing_area->width - 2*drawing_area->x); - gc->maxy = (drawing_area->height - 2*drawing_area->y); + QRectF drawing_area = scene()->sceneRect(); + gc.maxx = (drawing_area.width() - 2*drawing_area.x()); + gc.maxy = (drawing_area.height() - 2*drawing_area.y()); dc = select_dc(dc); /* This is per-dive-computer. Right now we just do the first one */ - pi = create_plot_info(dive, dc, gc); + pi = create_plot_info(dive, dc, &gc); /* Depth profile */ - plot_depth_profile(gc, pi); + plot_depth_profile(&gc, pi); +#if 0 plot_events(gc, pi, dc); /* Temperature profile */ @@ -233,6 +244,213 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } + +void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) +{ + int i, incr; + int sec, depth; + struct plot_data *entry; + int maxtime, maxdepth, marker, maxline; + int increments[8] = { 10, 20, 30, 60, 5*60, 10*60, 15*60, 30*60 }; + + /* Get plot scaling limits */ + maxtime = get_maxtime(pi); + maxdepth = get_maxdepth(pi); + + gc->maxtime = maxtime; + + /* Time markers: at most every 10 seconds, but no more than 12 markers. + * We start out with 10 seconds and increment up to 30 minutes, + * depending on the dive time. + * This allows for 6h dives - enough (I hope) for even the craziest + * divers - but just in case, for those 8h depth-record-breaking dives, + * we double the interval if this still doesn't get us to 12 or fewer + * time markers */ + i = 0; + while (maxtime / increments[i] > 12 && i < 7) + i++; + incr = increments[i]; + while (maxtime / incr > 12) + incr *= 2; + + gc->leftx = 0; gc->rightx = maxtime; + gc->topy = 0; gc->bottomy = 1.0; + + last_gc = *gc; + + QColor color; + color = profile_color[TIME_GRID].at(0); + for (i = incr; i < maxtime; i += incr) { + QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, i, 0), SCALE(gc, i, 1)); + line->setPen(QPen(color)); + scene()->addItem(line); + } + +#if 0 + /* now the text on the time markers */ + text_render_options_t tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP}; + if (maxtime < 600) { + /* Be a bit more verbose with shorter dives */ + for (i = incr; i < maxtime; i += incr) + plot_text(gc, &tro, i, 1, "%02d:%02d", i/60, i%60); + } else { + /* Only render the time on every second marker for normal dives */ + for (i = incr; i < maxtime; i += 2 * incr) + plot_text(gc, &tro, i, 1, "%d", i/60); + } +#endif + + /* Depth markers: every 30 ft or 10 m*/ + gc->leftx = 0; gc->rightx = 1.0; + gc->topy = 0; gc->bottomy = maxdepth; + switch (prefs.units.length) { + case units::METERS: marker = 10000; break; + case units::FEET: marker = 9144; break; /* 30 ft */ + } + maxline = MAX(pi->maxdepth + marker, maxdepth * 2 / 3); + + color = profile_color[TIME_GRID].at(0); + + for (i = marker; i < maxline; i += marker) { + QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, 0, i), SCALE(gc, 1, i)); + line->setPen(QPen(color)); + scene()->addItem(line); + } + +#if 0 + gc->leftx = 0; gc->rightx = maxtime; + + /* Show mean depth */ + if (! gc->printer) { + set_source_rgba(gc, MEAN_DEPTH); + move_to(gc, 0, pi->meandepth); + line_to(gc, pi->entry[pi->nr - 1].sec, pi->meandepth); + cairo_stroke(cr); + } + + /* + * These are good for debugging text placement etc, + * but not for actual display.. + */ + if (0) { + plot_smoothed_profile(gc, pi); + plot_minmax_profile(gc, pi); + } + + /* Do the depth profile for the neat fill */ + gc->topy = 0; gc->bottomy = maxdepth; + + cairo_pattern_t *pat; + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 1, DEPTH_BOTTOM); + pattern_add_color_stop_rgba (gc, pat, 0, DEPTH_TOP); + + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + cairo_set_line_width_scaled(gc->cr, 2); + + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) + line_to(gc, entry->sec, entry->depth); + + /* Show any ceiling we may have encountered */ + for (i = pi->nr - 1; i >= 0; i--, entry--) { + if (entry->ndl) { + /* non-zero NDL implies this is a safety stop, no ceiling */ + line_to(gc, entry->sec, 0); + } else if (entry->stopdepth < entry->depth) { + line_to(gc, entry->sec, entry->stopdepth); + } else { + line_to(gc, entry->sec, entry->depth); + } + } + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + + /* if the user wants the deco ceiling more visible, do that here (this + * basically draws over the background that we had allowed to shine + * through so far) */ + if (prefs.profile_red_ceiling) { + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); + pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) { + if (entry->ndl == 0 && entry->stopdepth) { + if (entry->ndl == 0 && entry->stopdepth < entry->depth) { + line_to(gc, entry->sec, entry->stopdepth); + } else { + line_to(gc, entry->sec, entry->depth); + } + } else { + line_to(gc, entry->sec, 0); + } + } + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + } + /* finally, plot the calculated ceiling over all this */ + if (prefs.profile_calc_ceiling) { + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 0, CALC_CEILING_SHALLOW); + pattern_add_color_stop_rgba (gc, pat, 1, CALC_CEILING_DEEP); + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) { + if (entry->ceiling) + line_to(gc, entry->sec, entry->ceiling); + else + line_to(gc, entry->sec, 0); + } + line_to(gc, (entry-1)->sec, 0); /* make sure we end at 0 */ + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + } + /* next show where we have been bad and crossed the dc's ceiling */ + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); + pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) + line_to(gc, entry->sec, entry->depth); + + for (i = pi->nr - 1; i >= 0; i--, entry--) { + if (entry->ndl == 0 && entry->stopdepth > entry->depth) { + line_to(gc, entry->sec, entry->stopdepth); + } else { + line_to(gc, entry->sec, entry->depth); + } + } + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + + /* Now do it again for the velocity colors */ + entry = pi->entry; + for (i = 1; i < pi->nr; i++) { + entry++; + sec = entry->sec; + /* we want to draw the segments in different colors + * representing the vertical velocity, so we need to + * chop this into short segments */ + depth = entry->depth; + set_source_rgba(gc, VELOCITY_COLORS_START_IDX + entry->velocity); + move_to(gc, entry[-1].sec, entry[-1].depth); + line_to(gc, sec, depth); + cairo_stroke(cr); + } +#endif +} + + void ProfileGraphicsView::resizeEvent(QResizeEvent *event) { // Fits the scene's rectangle on the view. diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index e3380abcd..633409b23 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -12,6 +12,8 @@ public: protected: void resizeEvent(QResizeEvent *event); +private: + void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi); }; #endif From 09597cd2d8729ab368f2e423dbb1ef5235745f58 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 19:36:40 -0300 Subject: [PATCH 128/226] Plot of the Mean Deph The mean depth now is plotted correctly. I wanted to do more stuff on this commit, but since it required that a few things on profile.c got moved to profile.h, commited to not have a huge blob for review. Signed-off-by: Tomaz Canabrava --- profile.c | 25 +------------------------ profile.h | 28 +++++++++++++++++++++++++++- qt-ui/profilegraphics.cpp | 13 +++++++------ 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/profile.c b/profile.c index be6efa1e6..161415e88 100644 --- a/profile.c +++ b/profile.c @@ -11,6 +11,7 @@ #endif #include "divelist.h" +#include "profile.h" #include "libdivecomputer/parser.h" #include "libdivecomputer/version.h" @@ -24,31 +25,7 @@ static struct plot_data *last_pi_entry = NULL; #define cairo_set_line_width_scaled(cr, w) \ cairo_set_line_width((cr), (w) * plot_scale); -typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t; -struct plot_data { - unsigned int in_deco:1; - unsigned int cylinderindex; - int sec; - /* pressure[0] is sensor pressure - * pressure[1] is interpolated pressure */ - int pressure[2]; - int temperature; - /* Depth info */ - int depth; - int ceiling; - int ndl; - int stoptime; - int stopdepth; - int cns; - int smoothed; - double po2, pn2, phe; - double mod, ead, end, eadd; - velocity_t velocity; - struct plot_data *min[3]; - struct plot_data *max[3]; - int avg[3]; -}; #define SENSOR_PR 0 #define INTERPOLATED_PR 1 diff --git a/profile.h b/profile.h index 1ad6625db..2c293fa7d 100644 --- a/profile.h +++ b/profile.h @@ -11,10 +11,36 @@ typedef int bool; #endif #endif -struct dive; +#include "dive.h" + +typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t; + struct divecomputer; struct graphics_context; struct plot_info; +struct plot_data { + unsigned int in_deco:1; + unsigned int cylinderindex; + int sec; + /* pressure[0] is sensor pressure + * pressure[1] is interpolated pressure */ + int pressure[2]; + int temperature; + /* Depth info */ + int depth; + int ceiling; + int ndl; + int stoptime; + int stopdepth; + int cns; + int smoothed; + double po2, pn2, phe; + double mod, ead, end, eadd; + velocity_t velocity; + struct plot_data *min[3]; + struct plot_data *max[3]; + int avg[3]; +}; void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 01ad5ab45..891cd81bf 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -309,7 +309,7 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct } maxline = MAX(pi->maxdepth + marker, maxdepth * 2 / 3); - color = profile_color[TIME_GRID].at(0); + color = profile_color[DEPTH_GRID].at(0); for (i = marker; i < maxline; i += marker) { QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, 0, i), SCALE(gc, 1, i)); @@ -317,17 +317,18 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct scene()->addItem(line); } -#if 0 + gc->leftx = 0; gc->rightx = maxtime; + color = profile_color[MEAN_DEPTH].at(0); /* Show mean depth */ if (! gc->printer) { - set_source_rgba(gc, MEAN_DEPTH); - move_to(gc, 0, pi->meandepth); - line_to(gc, pi->entry[pi->nr - 1].sec, pi->meandepth); - cairo_stroke(cr); + QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, 0, pi->meandepth), SCALE(gc, pi->entry[pi->nr - 1].sec, pi->meandepth)); + line->setPen(QPen(color)); + scene()->addItem(line); } +#if 0 /* * These are good for debugging text placement etc, * but not for actual display.. From 926224122ebc4cf6a53e3a63eb455293bcfb455d Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 20:18:16 -0300 Subject: [PATCH 129/226] Added the code to plot the actual Graph. This version already plots the dive-graph, with the gradient and all that jazz. One thing that will be easily spotted is that the size of the line is very thick - easily changed, I'm just using the default. As soon as everything is plotted correctly I'll fix the lines. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 32 ++++++++++++++++---------------- qt-ui/profilegraphics.h | 3 +++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 891cd81bf..761ef262b 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -337,38 +337,38 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_smoothed_profile(gc, pi); plot_minmax_profile(gc, pi); } +#endif /* Do the depth profile for the neat fill */ gc->topy = 0; gc->bottomy = maxdepth; - cairo_pattern_t *pat; - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 1, DEPTH_BOTTOM); - pattern_add_color_stop_rgba (gc, pat, 0, DEPTH_TOP); - - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - cairo_set_line_width_scaled(gc->cr, 2); - entry = pi->entry; - move_to(gc, 0, 0); + + QGraphicsPolygonItem *neatFill = new QGraphicsPolygonItem(); + QPolygonF p; for (i = 0; i < pi->nr; i++, entry++) - line_to(gc, entry->sec, entry->depth); + p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); /* Show any ceiling we may have encountered */ for (i = pi->nr - 1; i >= 0; i--, entry--) { if (entry->ndl) { /* non-zero NDL implies this is a safety stop, no ceiling */ - line_to(gc, entry->sec, 0); + p.append( QPointF( SCALE(gc, entry->sec, 0) )); } else if (entry->stopdepth < entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); + p.append( QPointF( SCALE(gc, entry->sec, entry->stopdepth) )); } else { - line_to(gc, entry->sec, entry->depth); + p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); } } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); + neatFill->setPolygon(p); + QLinearGradient pat(0.0,0.0,0.0,p.boundingRect().height()); + pat.setColorAt(1, profile_color[DEPTH_BOTTOM].first()); + pat.setColorAt(0, profile_color[DEPTH_TOP].first()); + neatFill->setBrush(QBrush(pat)); + scene()->addItem(neatFill); + +#if 0 /* if the user wants the deco ceiling more visible, do that here (this * basically draws over the background that we had allowed to shine * through so far) */ diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 633409b23..a62e55c4d 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -14,6 +14,9 @@ protected: private: void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi); + + QPen defaultPen; + QBrush defaultBrush; }; #endif From 8353d571643c83514012a84cdc904353d2c4972e Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 16:12:43 -0300 Subject: [PATCH 130/226] Started the code for the Profile Plotting This small patch adds a new class - ProfileGraphicsView it's a QGraphicsView based class that will holds all graphics-items for the plotting. The setup is simple, just call ui->ListView->plot( dive ) ( that's already a ProfileGraphicsView and magic will happen. Since Im using a QGraphicsView , the size of the canvas doesn't matter and I'm fixing it at 0,0,100,100. when a resize is done, the resizeEvent will be called, fitting the scene's rectangle on the view. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- Makefile | 2 ++ qt-ui/mainwindow.cpp | 2 +- qt-ui/mainwindow.ui | 9 +++++++-- qt-ui/profilegraphics.cpp | 26 ++++++++++++++++++++++++++ qt-ui/profilegraphics.h | 17 +++++++++++++++++ 5 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 qt-ui/profilegraphics.cpp create mode 100644 qt-ui/profilegraphics.h diff --git a/Makefile b/Makefile index 9961f04c7..338f81a7c 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ HEADERS = \ qt-ui/plotareascene.h \ qt-ui/starwidget.h \ qt-ui/modeldelegates.h \ + qt-ui/profilegraphics.h \ SOURCES = \ @@ -67,6 +68,7 @@ SOURCES = \ qt-ui/plotareascene.cpp \ qt-ui/starwidget.cpp \ qt-ui/modeldelegates.cpp \ + qt-ui/profilegraphics.cpp \ $(RESFILE) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 3f545171e..8ac0ad871 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -67,7 +67,6 @@ void MainWindow::on_actionOpen_triggered() g_error_free(error); error = NULL; } - process_dives(FALSE, FALSE); ui->InfoWidget->reload(); @@ -94,6 +93,7 @@ void MainWindow::dive_selection_changed(const QItemSelection& newSelection, cons continue; select_dive(get_divenr(d)); } + ui->ProfileWidget->plot(get_dive(selected_dive)); } void MainWindow::on_actionSave_triggered() diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index fe97d98b4..7dfbae746 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -25,7 +25,7 @@ Qt::Horizontal - + @@ -88,7 +88,7 @@ 0 0 763 - 19 + 25 @@ -339,6 +339,11 @@ QTreeView
divelistview.h
+ + ProfileGraphicsView + QGraphicsView +
profilegraphics.h
+
diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp new file mode 100644 index 000000000..59a47826f --- /dev/null +++ b/qt-ui/profilegraphics.cpp @@ -0,0 +1,26 @@ +#include "profilegraphics.h" + +#include +#include + +#include + +ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) +{ + setScene(new QGraphicsScene()); + scene()->setSceneRect(0,0,100,100); +} + +void ProfileGraphicsView::plot(struct dive *d) +{ + qDebug() << "Start the plotting of the dive here."; +} + +void ProfileGraphicsView::resizeEvent(QResizeEvent *event) +{ + // Fits the scene's rectangle on the view. + // I can pass some parameters to this - + // like Qt::IgnoreAspectRatio or Qt::KeepAspectRatio + QRectF r = scene()->sceneRect(); + fitInView ( r.x() - 2, r.y() -2, r.width() + 4, r.height() + 4); // do a little bit of spacing; +} diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h new file mode 100644 index 000000000..e3380abcd --- /dev/null +++ b/qt-ui/profilegraphics.h @@ -0,0 +1,17 @@ +#ifndef PROFILEGRAPHICS_H +#define PROFILEGRAPHICS_H + +#include + +class ProfileGraphicsView : public QGraphicsView { +Q_OBJECT +public: + ProfileGraphicsView(QWidget* parent = 0); + void plot(struct dive *d); + +protected: + void resizeEvent(QResizeEvent *event); + +}; + +#endif From ec4d4566adc59d5fa2642c5f4ca12653b3f384a3 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 16:41:49 -0300 Subject: [PATCH 131/226] Converted the Colors.h code to Qt The colors on colors.h were done to fill a special struct by Subsurface - I removed that structure and replaced the code that generated the map of colors to a QMap. I know that this changes are not very 'welcomed', but C++ has issues on creating & initializing complex static members, this was the best way that I could think of. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- color.h | 80 ++++++++++++++++++------------------ profile.c | 86 ++------------------------------------- qt-ui/profilegraphics.cpp | 81 ++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 122 deletions(-) diff --git a/color.h b/color.h index 5d88fb5f7..f2013bcc9 100644 --- a/color.h +++ b/color.h @@ -4,54 +4,56 @@ /* The colors are named by picking the closest match from http://chir.ag/projects/name-that-color */ +#include + // Greens -#define CAMARONE1 { 0.0, 0.4, 0.0, 1 } -#define FUNGREEN1 { 0.0, 0.4, 0.2, 1 } -#define FUNGREEN1_HIGH_TRANS { 0.0, 0.4, 0.2, 0.25 } -#define KILLARNEY1 { 0.2, 0.4, 0.2, 1 } -#define APPLE1 { 0.2, 0.6, 0.2, 1 } -#define APPLE1_MED_TRANS { 0.2, 0.6, 0.2, 0.5 } -#define APPLE1_HIGH_TRANS { 0.2, 0.6, 0.2, 0.25 } -#define LIMENADE1 { 0.4, 0.8, 0.0, 1 } -#define ATLANTIS1 { 0.4, 0.8, 0.2, 1 } -#define ATLANTIS2 { 0.6, 0.8, 0.2, 1 } -#define RIOGRANDE1 { 0.8, 0.8, 0.0, 1 } -#define EARLSGREEN1 { 0.8, 0.8, 0.2, 1 } -#define FORESTGREEN1 { 0.1, 0.5, 0.1, 1 } +#define CAMARONE1 QColor::fromRgbF( 0.0, 0.4, 0.0, 1 ) +#define FUNGREEN1 QColor::fromRgbF( 0.0, 0.4, 0.2, 1 ) +#define FUNGREEN1_HIGH_TRANS QColor::fromRgbF( 0.0, 0.4, 0.2, 0.25 ) +#define KILLARNEY1 QColor::fromRgbF( 0.2, 0.4, 0.2, 1 ) +#define APPLE1 QColor::fromRgbF( 0.2, 0.6, 0.2, 1 ) +#define APPLE1_MED_TRANS QColor::fromRgbF( 0.2, 0.6, 0.2, 0.5 ) +#define APPLE1_HIGH_TRANS QColor::fromRgbF( 0.2, 0.6, 0.2, 0.25 ) +#define LIMENADE1 QColor::fromRgbF( 0.4, 0.8, 0.0, 1 ) +#define ATLANTIS1 QColor::fromRgbF( 0.4, 0.8, 0.2, 1 ) +#define ATLANTIS2 QColor::fromRgbF( 0.6, 0.8, 0.2, 1 ) +#define RIOGRANDE1 QColor::fromRgbF( 0.8, 0.8, 0.0, 1 ) +#define EARLSGREEN1 QColor::fromRgbF( 0.8, 0.8, 0.2, 1 ) +#define FORESTGREEN1 QColor::fromRgbF( 0.1, 0.5, 0.1, 1 ) // Reds -#define PERSIANRED1 { 0.8, 0.2, 0.2, 1 } -#define TUSCANY1 { 0.8, 0.4, 0.2, 1 } -#define PIRATEGOLD1 { 0.8, 0.5, 0.0, 1 } -#define HOKEYPOKEY1 { 0.8, 0.6, 0.2, 1 } -#define CINNABAR1 { 0.9, 0.3, 0.2, 1 } -#define REDORANGE1 { 1.0, 0.2, 0.2, 1 } -#define REDORANGE1_HIGH_TRANS { 1.0, 0.2, 0.2, 0.25 } -#define REDORANGE1_MED_TRANS { 1.0, 0.2, 0.2, 0.5 } -#define RED1_MED_TRANS { 1.0, 0.0, 0.0, 0.5 } -#define RED1 { 1.0, 0.0, 0.0, 1 } +#define PERSIANRED1 QColor::fromRgbF( 0.8, 0.2, 0.2, 1 ) +#define TUSCANY1 QColor::fromRgbF( 0.8, 0.4, 0.2, 1 ) +#define PIRATEGOLD1 QColor::fromRgbF( 0.8, 0.5, 0.0, 1 ) +#define HOKEYPOKEY1 QColor::fromRgbF( 0.8, 0.6, 0.2, 1 ) +#define CINNABAR1 QColor::fromRgbF( 0.9, 0.3, 0.2, 1 ) +#define REDORANGE1 QColor::fromRgbF( 1.0, 0.2, 0.2, 1 ) +#define REDORANGE1_HIGH_TRANS QColor::fromRgbF( 1.0, 0.2, 0.2, 0.25 ) +#define REDORANGE1_MED_TRANS QColor::fromRgbF( 1.0, 0.2, 0.2, 0.5 ) +#define RED1_MED_TRANS QColor::fromRgbF( 1.0, 0.0, 0.0, 0.5 ) +#define RED1 QColor::fromRgbF( 1.0, 0.0, 0.0, 1 ) // Monochromes -#define BLACK1_LOW_TRANS { 0.0, 0.0, 0.0, 0.75 } -#define BLACK1_HIGH_TRANS { 0.0, 0.0, 0.0, 0.25 } -#define TUNDORA1_MED_TRANS { 0.3, 0.3, 0.3, 0.5 } -#define MERCURY1_MED_TRANS { 0.9, 0.9, 0.9, 0.5 } -#define CONCRETE1_LOWER_TRANS { 0.95, 0.95, 0.95, 0.9 } -#define WHITE1_MED_TRANS { 1.0, 1.0, 1.0, 0.5 } -#define WHITE1 { 1.0, 1.0, 1.0, 1 } +#define BLACK1_LOW_TRANS QColor::fromRgbF( 0.0, 0.0, 0.0, 0.75 ) +#define BLACK1_HIGH_TRANS QColor::fromRgbF( 0.0, 0.0, 0.0, 0.25 ) +#define TUNDORA1_MED_TRANS QColor::fromRgbF( 0.3, 0.3, 0.3, 0.5 ) +#define MERCURY1_MED_TRANS QColor::fromRgbF( 0.9, 0.9, 0.9, 0.5 ) +#define CONCRETE1_LOWER_TRANS QColor::fromRgbF( 0.95, 0.95, 0.95, 0.9 ) +#define WHITE1_MED_TRANS QColor::fromRgbF( 1.0, 1.0, 1.0, 0.5 ) +#define WHITE1 QColor::fromRgbF( 1.0, 1.0, 1.0, 1 ) // Blues -#define GOVERNORBAY2 { 0.2, 0.2, 0.7, 1 } -#define GOVERNORBAY1_MED_TRANS { 0.2, 0.2, 0.8, 0.5 } -#define ROYALBLUE2 { 0.2, 0.2, 0.9, 1 } -#define ROYALBLUE2_LOW_TRANS { 0.2, 0.2, 0.9, 0.75 } +#define GOVERNORBAY2 QColor::fromRgbF( 0.2, 0.2, 0.7, 1 ) +#define GOVERNORBAY1_MED_TRANS QColor::fromRgbF( 0.2, 0.2, 0.8, 0.5 ) +#define ROYALBLUE2 QColor::fromRgbF( 0.2, 0.2, 0.9, 1 ) +#define ROYALBLUE2_LOW_TRANS QColor::fromRgbF( 0.2, 0.2, 0.9, 0.75 ) // Yellows / BROWNS -#define SPRINGWOOD1 { 0.95, 0.95, 0.9, 1 } -#define BROOM1_LOWER_TRANS { 1.0, 1.0, 0.1, 0.9 } -#define PEANUT { 0.5, 0.2, 0.1, 1.0 } -#define PEANUT_MED_TRANS { 0.5, 0.2, 0.1, 0.5 } +#define SPRINGWOOD1 QColor::fromRgbF( 0.95, 0.95, 0.9, 1 ) +#define BROOM1_LOWER_TRANS QColor::fromRgbF( 1.0, 1.0, 0.1, 0.9 ) +#define PEANUT QColor::fromRgbF( 0.5, 0.2, 0.1, 1.0 ) +#define PEANUT_MED_TRANS QColor::fromRgbF( 0.5, 0.2, 0.1, 0.5 ) // Magentas -#define MEDIUMREDVIOLET1_HIGHER_TRANS { 0.7, 0.2, 0.7, 0.1 } +#define MEDIUMREDVIOLET1_HIGHER_TRANS QColor::fromRgbF( 0.7, 0.2, 0.7, 0.1 ) #endif diff --git a/profile.c b/profile.c index b9a633cf6..fa0a24a06 100644 --- a/profile.c +++ b/profile.c @@ -10,7 +10,7 @@ #include "display-gtk.h" #endif #include "divelist.h" -#include "color.h" + #include "libdivecomputer/parser.h" #include "libdivecomputer/version.h" @@ -59,88 +59,6 @@ struct plot_data { #define INTERPOLATED_PRESSURE(_entry) (_entry)->pressure[INTERPOLATED_PR] #define GET_PRESSURE(_entry) (SENSOR_PRESSURE(_entry) ? : INTERPOLATED_PRESSURE(_entry)) -#define SAC_COLORS_START_IDX SAC_1 -#define SAC_COLORS 9 -#define VELOCITY_COLORS_START_IDX VELO_STABLE -#define VELOCITY_COLORS 5 - -typedef enum { - /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */ - SAC_1, SAC_2, SAC_3, SAC_4, SAC_5, SAC_6, SAC_7, SAC_8, SAC_9, - - /* Velocity colors. Order is still important, ref VELOCITY_COLORS_START_IDX. */ - VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY, - - /* gas colors */ - PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, PP_LINES, - - /* Other colors */ - TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW, - SMOOTHED, MINUTE, TIME_GRID, TIME_TEXT, DEPTH_GRID, MEAN_DEPTH, DEPTH_TOP, - DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND, - CEILING_SHALLOW, CEILING_DEEP, CALC_CEILING_SHALLOW, CALC_CEILING_DEEP -} color_indice_t; - -typedef struct { - /* media[0] is screen, media[1] is b/w printer media[2] is color printer */ - struct rgba { - double r,g,b,a; - } media[3]; -} color_t; - -/* [color indice] = {{screen color, b/w printer color, color printer}} printer & screen colours could be different */ -static const color_t profile_color[] = { - [SAC_1] = {{FUNGREEN1, BLACK1_LOW_TRANS, FUNGREEN1}}, - [SAC_2] = {{APPLE1, BLACK1_LOW_TRANS, APPLE1}}, - [SAC_3] = {{ATLANTIS1, BLACK1_LOW_TRANS, ATLANTIS1}}, - [SAC_4] = {{ATLANTIS2, BLACK1_LOW_TRANS, ATLANTIS2}}, - [SAC_5] = {{EARLSGREEN1, BLACK1_LOW_TRANS, EARLSGREEN1}}, - [SAC_6] = {{HOKEYPOKEY1, BLACK1_LOW_TRANS, HOKEYPOKEY1}}, - [SAC_7] = {{TUSCANY1, BLACK1_LOW_TRANS, TUSCANY1}}, - [SAC_8] = {{CINNABAR1, BLACK1_LOW_TRANS, CINNABAR1}}, - [SAC_9] = {{REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1}}, - - [VELO_STABLE] = {{CAMARONE1, BLACK1_LOW_TRANS, CAMARONE1}}, - [VELO_SLOW] = {{LIMENADE1, BLACK1_LOW_TRANS, LIMENADE1}}, - [VELO_MODERATE] = {{RIOGRANDE1, BLACK1_LOW_TRANS, RIOGRANDE1}}, - [VELO_FAST] = {{PIRATEGOLD1, BLACK1_LOW_TRANS, PIRATEGOLD1}}, - [VELO_CRAZY] = {{RED1, BLACK1_LOW_TRANS, RED1}}, - - [PO2] = {{APPLE1, BLACK1_LOW_TRANS, APPLE1}}, - [PO2_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}}, - [PN2] = {{BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS}}, - [PN2_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}}, - [PHE] = {{PEANUT, BLACK1_LOW_TRANS, PEANUT}}, - [PHE_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}}, - [PP_LINES] = {{BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS}}, - - [TEXT_BACKGROUND] = {{CONCRETE1_LOWER_TRANS, WHITE1, CONCRETE1_LOWER_TRANS}}, - [ALERT_BG] = {{BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS, BROOM1_LOWER_TRANS}}, - [ALERT_FG] = {{BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS}}, - [EVENTS] = {{REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1}}, - [SAMPLE_DEEP] = {{PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1}}, - [SAMPLE_SHALLOW] = {{PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1}}, - [SMOOTHED] = {{REDORANGE1_HIGH_TRANS, BLACK1_LOW_TRANS, REDORANGE1_HIGH_TRANS}}, - [MINUTE] = {{MEDIUMREDVIOLET1_HIGHER_TRANS, BLACK1_LOW_TRANS, MEDIUMREDVIOLET1_HIGHER_TRANS}}, - [TIME_GRID] = {{WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS}}, - [TIME_TEXT] = {{FORESTGREEN1, BLACK1_LOW_TRANS, FORESTGREEN1}}, - [DEPTH_GRID] = {{WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS}}, - [MEAN_DEPTH] = {{REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS}}, - [DEPTH_BOTTOM] = {{GOVERNORBAY1_MED_TRANS, BLACK1_HIGH_TRANS, GOVERNORBAY1_MED_TRANS}}, - [DEPTH_TOP] = {{MERCURY1_MED_TRANS, WHITE1_MED_TRANS, MERCURY1_MED_TRANS}}, - [TEMP_TEXT] = {{GOVERNORBAY2, BLACK1_LOW_TRANS, GOVERNORBAY2}}, - [TEMP_PLOT] = {{ROYALBLUE2_LOW_TRANS, BLACK1_LOW_TRANS, ROYALBLUE2_LOW_TRANS}}, - [SAC_DEFAULT] = {{WHITE1, BLACK1_LOW_TRANS, FORESTGREEN1}}, - [BOUNDING_BOX] = {{WHITE1, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS}}, - [PRESSURE_TEXT] = {{KILLARNEY1, BLACK1_LOW_TRANS, KILLARNEY1}}, - [BACKGROUND] = {{SPRINGWOOD1, BLACK1_LOW_TRANS, SPRINGWOOD1}}, - [CEILING_SHALLOW] = {{REDORANGE1_HIGH_TRANS, BLACK1_HIGH_TRANS, REDORANGE1_HIGH_TRANS}}, - [CEILING_DEEP] = {{RED1_MED_TRANS, BLACK1_HIGH_TRANS, RED1_MED_TRANS}}, - [CALC_CEILING_SHALLOW] = {{FUNGREEN1_HIGH_TRANS, BLACK1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS}}, - [CALC_CEILING_DEEP] = {{APPLE1_HIGH_TRANS, BLACK1_HIGH_TRANS, APPLE1_HIGH_TRANS}}, - -}; - #if USE_GTK_UI /* Scale to 0,0 -> maxx,maxy */ @@ -270,11 +188,13 @@ int get_maxdepth(struct plot_info *pi) return md; } +#if 0 typedef struct { double size; color_indice_t color; double hpos, vpos; } text_render_options_t; +#endif #define RIGHT (-1.0) #define CENTER (-0.5) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 59a47826f..a878c1363 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -5,10 +5,91 @@ #include +#include "../color.h" + +#define SAC_COLORS_START_IDX SAC_1 +#define SAC_COLORS 9 +#define VELOCITY_COLORS_START_IDX VELO_STABLE +#define VELOCITY_COLORS 5 + +typedef enum { + /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */ + SAC_1, SAC_2, SAC_3, SAC_4, SAC_5, SAC_6, SAC_7, SAC_8, SAC_9, + + /* Velocity colors. Order is still important, ref VELOCITY_COLORS_START_IDX. */ + VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY, + + /* gas colors */ + PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, PP_LINES, + + /* Other colors */ + TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW, + SMOOTHED, MINUTE, TIME_GRID, TIME_TEXT, DEPTH_GRID, MEAN_DEPTH, DEPTH_TOP, + DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND, + CEILING_SHALLOW, CEILING_DEEP, CALC_CEILING_SHALLOW, CALC_CEILING_DEEP +} color_indice_t; + +#define COLOR(x, y, z) QVector() << x << y << z; +/* profile_color[color indice] = COLOR(screen color, b/w printer color, color printer}} printer & screen colours could be different */ +QMap > profile_color; +void fill_profile_color() +{ + profile_color[SAC_1] = COLOR(FUNGREEN1, BLACK1_LOW_TRANS, FUNGREEN1); + profile_color[SAC_2] = COLOR(APPLE1, BLACK1_LOW_TRANS, APPLE1); + profile_color[SAC_3] = COLOR(ATLANTIS1, BLACK1_LOW_TRANS, ATLANTIS1); + profile_color[SAC_4] = COLOR(ATLANTIS2, BLACK1_LOW_TRANS, ATLANTIS2); + profile_color[SAC_5] = COLOR(EARLSGREEN1, BLACK1_LOW_TRANS, EARLSGREEN1); + profile_color[SAC_6] = COLOR(HOKEYPOKEY1, BLACK1_LOW_TRANS, HOKEYPOKEY1); + profile_color[SAC_7] = COLOR(TUSCANY1, BLACK1_LOW_TRANS, TUSCANY1); + profile_color[SAC_8] = COLOR(CINNABAR1, BLACK1_LOW_TRANS, CINNABAR1); + profile_color[SAC_9] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1); + + profile_color[VELO_STABLE] = COLOR(CAMARONE1, BLACK1_LOW_TRANS, CAMARONE1); + profile_color[VELO_SLOW] = COLOR(LIMENADE1, BLACK1_LOW_TRANS, LIMENADE1); + profile_color[VELO_MODERATE] = COLOR(RIOGRANDE1, BLACK1_LOW_TRANS, RIOGRANDE1); + profile_color[VELO_FAST] = COLOR(PIRATEGOLD1, BLACK1_LOW_TRANS, PIRATEGOLD1); + profile_color[VELO_CRAZY] = COLOR(RED1, BLACK1_LOW_TRANS, RED1); + + profile_color[PO2] = COLOR(APPLE1, BLACK1_LOW_TRANS, APPLE1); + profile_color[PO2_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1); + profile_color[PN2] = COLOR(BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS); + profile_color[PN2_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1); + profile_color[PHE] = COLOR(PEANUT, BLACK1_LOW_TRANS, PEANUT); + profile_color[PHE_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1); + profile_color[PP_LINES] = COLOR(BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS); + + profile_color[TEXT_BACKGROUND] = COLOR(CONCRETE1_LOWER_TRANS, WHITE1, CONCRETE1_LOWER_TRANS); + profile_color[ALERT_BG] = COLOR(BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS, BROOM1_LOWER_TRANS); + profile_color[ALERT_FG] = COLOR(BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS); + profile_color[EVENTS] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1); + profile_color[SAMPLE_DEEP] = COLOR(PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1); + profile_color[SAMPLE_SHALLOW] = COLOR(PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1); + profile_color[SMOOTHED] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_LOW_TRANS, REDORANGE1_HIGH_TRANS); + profile_color[MINUTE] = COLOR(MEDIUMREDVIOLET1_HIGHER_TRANS, BLACK1_LOW_TRANS, MEDIUMREDVIOLET1_HIGHER_TRANS); + profile_color[TIME_GRID] = COLOR(WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS); + profile_color[TIME_TEXT] = COLOR(FORESTGREEN1, BLACK1_LOW_TRANS, FORESTGREEN1); + profile_color[DEPTH_GRID] = COLOR(WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS); + profile_color[MEAN_DEPTH] = COLOR(REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS); + profile_color[DEPTH_BOTTOM] = COLOR(GOVERNORBAY1_MED_TRANS, BLACK1_HIGH_TRANS, GOVERNORBAY1_MED_TRANS); + profile_color[DEPTH_TOP] = COLOR(MERCURY1_MED_TRANS, WHITE1_MED_TRANS, MERCURY1_MED_TRANS); + profile_color[TEMP_TEXT] = COLOR(GOVERNORBAY2, BLACK1_LOW_TRANS, GOVERNORBAY2); + profile_color[TEMP_PLOT] = COLOR(ROYALBLUE2_LOW_TRANS, BLACK1_LOW_TRANS, ROYALBLUE2_LOW_TRANS); + profile_color[SAC_DEFAULT] = COLOR(WHITE1, BLACK1_LOW_TRANS, FORESTGREEN1); + profile_color[BOUNDING_BOX] = COLOR(WHITE1, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS); + profile_color[PRESSURE_TEXT] = COLOR(KILLARNEY1, BLACK1_LOW_TRANS, KILLARNEY1); + profile_color[BACKGROUND] = COLOR(SPRINGWOOD1, BLACK1_LOW_TRANS, SPRINGWOOD1); + profile_color[CEILING_SHALLOW] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_HIGH_TRANS, REDORANGE1_HIGH_TRANS); + profile_color[CEILING_DEEP] = COLOR(RED1_MED_TRANS, BLACK1_HIGH_TRANS, RED1_MED_TRANS); + profile_color[CALC_CEILING_SHALLOW] = COLOR(FUNGREEN1_HIGH_TRANS, BLACK1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS); + profile_color[CALC_CEILING_DEEP] = COLOR(APPLE1_HIGH_TRANS, BLACK1_HIGH_TRANS, APPLE1_HIGH_TRANS); +} +#undef COLOR + ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) { setScene(new QGraphicsScene()); scene()->setSceneRect(0,0,100,100); + fill_profile_color(); } void ProfileGraphicsView::plot(struct dive *d) From fa82ba60798be8b5e20277527053199916888b16 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 17:24:23 -0300 Subject: [PATCH 132/226] Moved the plot from the cairo version to the Qt version Started working on the Qt version of the Plot, initially nothing is printed - but this is not a bad thing, the program doesn't explodes too. :) some work had to be done about the 'bool/gboolean' stuff so I removed all gbooleans in the code that I'v encountered. A new file was created ( profile.h ) so I could put the signatures of helper methods that cairo used to call. till now the code computes the max limits. Next patch the first drawing will be made. Signed-off-by: Tomaz Canabrava --- display.h | 27 +++++--- profile.c | 123 --------------------------------- profile.h | 25 +++++++ qt-ui/profilegraphics.cpp | 140 +++++++++++++++++++++++++++++++++++++- 4 files changed, 182 insertions(+), 133 deletions(-) create mode 100644 profile.h diff --git a/display.h b/display.h index b1a034e88..bc60c059f 100644 --- a/display.h +++ b/display.h @@ -1,18 +1,26 @@ #ifndef DISPLAY_H #define DISPLAY_H -#include - #ifdef __cplusplus extern "C" { +#else +#if __STDC_VERSION__ >= 199901L +#include +#else +typedef int bool; +#endif #endif #define SCALE_SCREEN 1.0 -#define SCALE_PRINT (1.0 / get_screen_dpi()) +#warning "PORT THE get_screen_dpi to Qt" +#define SCALE_PRINT 1.0 +//#define SCALE_PRINT (1.0 / get_screen_dpi()) extern void repaint_dive(void); extern void do_print(void); -extern gdouble get_screen_dpi(void); + +// Commented out because I don't know how to get the dpi on a paint device yet. +// extern gdouble get_screen_dpi(void); /* Plot info with smoothing, velocity indication * and one-, two- and three-minute minimums and maximums */ @@ -24,10 +32,15 @@ struct plot_info { int mintemp, maxtemp; double endtempcoord; double maxpp; - gboolean has_ndl; + bool has_ndl; struct plot_data *entry; }; +/* +// I'm not sure if this is needed anymore - but keeping this here +// so I wont break stuff trying to redo the well. +*/ + /* * Cairo scaling really is horribly horribly mis-designed. * @@ -38,8 +51,6 @@ struct plot_info { */ struct graphics_context { int printer; - cairo_t *cr; - cairo_rectangle_t drawing_area; double maxx, maxy; double leftx, rightx; double topy, bottomy; @@ -61,7 +72,7 @@ struct options { enum { PRETTY, TABLE, TWOPERPAGE } type; int print_selected; int color_selected; - gboolean notes_up; + bool notes_up; int profile_height, notes_height, tanks_height; }; diff --git a/profile.c b/profile.c index fa0a24a06..3f413f4ed 100644 --- a/profile.c +++ b/profile.c @@ -18,9 +18,6 @@ int selected_dive = 0; char zoomed_plot = 0; char dc_number = 0; -#if USE_GTK_UI -static double plot_scale = SCALE_SCREEN; -#endif static struct plot_data *last_pi_entry = NULL; @@ -1951,126 +1948,6 @@ struct divecomputer *select_dc(struct divecomputer *main) return main; } -#if USE_GTK_UI -void plot(struct graphics_context *gc, struct dive *dive, scale_mode_t scale) -{ - struct plot_info *pi; - struct divecomputer *dc = &dive->dc; - cairo_rectangle_t *drawing_area = &gc->drawing_area; - const char *nickname; - - plot_set_scale(scale); - - if (!dc->samples) { - static struct sample fake[4]; - static struct divecomputer fakedc; - fakedc = dive->dc; - fakedc.sample = fake; - fakedc.samples = 4; - - /* The dive has no samples, so create a few fake ones. This assumes an - ascent/descent rate of 9 m/min, which is just below the limit for FAST. */ - int duration = dive->dc.duration.seconds; - int maxdepth = dive->dc.maxdepth.mm; - int asc_desc_time = dive->dc.maxdepth.mm*60/9000; - if (asc_desc_time * 2 >= duration) - asc_desc_time = duration / 2; - fake[1].time.seconds = asc_desc_time; - fake[1].depth.mm = maxdepth; - fake[2].time.seconds = duration - asc_desc_time; - fake[2].depth.mm = maxdepth; - fake[3].time.seconds = duration * 1.00; - fakedc.events = dc->events; - dc = &fakedc; - } - - /* - * Set up limits that are independent of - * the dive computer - */ - calculate_max_limits(dive, dc, gc); - - /* shift the drawing area so we have a nice margin around it */ - cairo_translate(gc->cr, drawing_area->x, drawing_area->y); - cairo_set_line_width_scaled(gc->cr, 1); - cairo_set_line_cap(gc->cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(gc->cr, CAIRO_LINE_JOIN_ROUND); - - /* - * We don't use "cairo_translate()" because that doesn't - * scale line width etc. But the actual scaling we need - * do set up ourselves.. - * - * Snif. What a pity. - */ - gc->maxx = (drawing_area->width - 2*drawing_area->x); - gc->maxy = (drawing_area->height - 2*drawing_area->y); - - dc = select_dc(dc); - - /* This is per-dive-computer. Right now we just do the first one */ - pi = create_plot_info(dive, dc, gc); - - /* Depth profile */ - plot_depth_profile(gc, pi); - plot_events(gc, pi, dc); - - /* Temperature profile */ - plot_temperature_profile(gc, pi); - - /* Cylinder pressure plot */ - plot_cylinder_pressure(gc, pi, dive, dc); - - /* Text on top of all graphs.. */ - plot_temperature_text(gc, pi); - plot_depth_text(gc, pi); - plot_cylinder_pressure_text(gc, pi); - plot_deco_text(gc, pi); - - /* Bounding box last */ - gc->leftx = 0; gc->rightx = 1.0; - gc->topy = 0; gc->bottomy = 1.0; - - set_source_rgba(gc, BOUNDING_BOX); - cairo_set_line_width_scaled(gc->cr, 1); - move_to(gc, 0, 0); - line_to(gc, 0, 1); - line_to(gc, 1, 1); - line_to(gc, 1, 0); - cairo_close_path(gc->cr); - cairo_stroke(gc->cr); - - /* Put the dive computer name in the lower left corner */ - nickname = get_dc_nickname(dc->model, dc->deviceid); - if (!nickname || *nickname == '\0') - nickname = dc->model; - if (nickname) { - static const text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; - plot_text(gc, &computer, 0, 1, "%s", nickname); - } - - if (PP_GRAPHS_ENABLED) { - plot_pp_gas_profile(gc, pi); - plot_pp_text(gc, pi); - } - - /* now shift the translation back by half the margin; - * this way we can draw the vertical scales on both sides */ - cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); - gc->maxx += drawing_area->x; - gc->leftx = -(drawing_area->x / drawing_area->width) / 2.0; - gc->rightx = 1.0 - gc->leftx; - - plot_depth_scale(gc, pi); - - if (gc->printer) { - free(pi->entry); - last_pi_entry = pi->entry = NULL; - pi->nr = 0; - } -} -#endif /* USE_GTK_UI */ - static void plot_string(struct plot_data *entry, char *buf, size_t bufsize, int depth, int pressure, int temp, gboolean has_ndl) { diff --git a/profile.h b/profile.h new file mode 100644 index 000000000..66d1ee190 --- /dev/null +++ b/profile.h @@ -0,0 +1,25 @@ +#ifndef PROFILE_H +#define PROFILE_H + +#ifdef __cplusplus +extern "C" { +#else +#if __STDC_VERSION__ >= 199901L +#include +#else +typedef int bool; +#endif +#endif + +struct dive; +struct divecomputer; +struct graphics_context; +struct plot_info; + +void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); +struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index a878c1363..e500432fc 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -6,12 +6,17 @@ #include #include "../color.h" +#include "../display.h" +#include "../dive.h" +#include "../profile.h" #define SAC_COLORS_START_IDX SAC_1 #define SAC_COLORS 9 #define VELOCITY_COLORS_START_IDX VELO_STABLE #define VELOCITY_COLORS 5 +static double plot_scale = SCALE_SCREEN; + typedef enum { /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */ SAC_1, SAC_2, SAC_3, SAC_4, SAC_5, SAC_6, SAC_7, SAC_8, SAC_9, @@ -92,9 +97,140 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent fill_profile_color(); } -void ProfileGraphicsView::plot(struct dive *d) +static void plot_set_scale(scale_mode_t scale) { - qDebug() << "Start the plotting of the dive here."; + switch (scale) { + default: + case SC_SCREEN: + plot_scale = SCALE_SCREEN; + break; + case SC_PRINT: + plot_scale = SCALE_PRINT; + break; + } +} + +void ProfileGraphicsView::plot(struct dive *dive) +{ + struct plot_info *pi; + struct divecomputer *dc = &dive->dc; + + // This was passed around in the Cairo version / needed? + graphics_context gc; + const char *nickname; + + // Fix this for printing / screen later. + // plot_set_scale( scale_mode_t); + + if (!dc->samples) { + static struct sample fake[4]; + static struct divecomputer fakedc; + fakedc = dive->dc; + fakedc.sample = fake; + fakedc.samples = 4; + + /* The dive has no samples, so create a few fake ones. This assumes an + ascent/descent rate of 9 m/min, which is just below the limit for FAST. */ + int duration = dive->dc.duration.seconds; + int maxdepth = dive->dc.maxdepth.mm; + int asc_desc_time = dive->dc.maxdepth.mm*60/9000; + if (asc_desc_time * 2 >= duration) + asc_desc_time = duration / 2; + fake[1].time.seconds = asc_desc_time; + fake[1].depth.mm = maxdepth; + fake[2].time.seconds = duration - asc_desc_time; + fake[2].depth.mm = maxdepth; + fake[3].time.seconds = duration * 1.00; + fakedc.events = dc->events; + dc = &fakedc; + } + + /* + * Set up limits that are independent of + * the dive computer + */ + calculate_max_limits(dive, dc, &gc); + +#if 0 + /* shift the drawing area so we have a nice margin around it */ + cairo_translate(gc->cr, drawing_area->x, drawing_area->y); + cairo_set_line_width_scaled(gc->cr, 1); + cairo_set_line_cap(gc->cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join(gc->cr, CAIRO_LINE_JOIN_ROUND); + + /* + * We don't use "cairo_translate()" because that doesn't + * scale line width etc. But the actual scaling we need + * do set up ourselves.. + * + * Snif. What a pity. + */ + gc->maxx = (drawing_area->width - 2*drawing_area->x); + gc->maxy = (drawing_area->height - 2*drawing_area->y); + + dc = select_dc(dc); + + /* This is per-dive-computer. Right now we just do the first one */ + pi = create_plot_info(dive, dc, gc); + + /* Depth profile */ + plot_depth_profile(gc, pi); + plot_events(gc, pi, dc); + + /* Temperature profile */ + plot_temperature_profile(gc, pi); + + /* Cylinder pressure plot */ + plot_cylinder_pressure(gc, pi, dive, dc); + + /* Text on top of all graphs.. */ + plot_temperature_text(gc, pi); + plot_depth_text(gc, pi); + plot_cylinder_pressure_text(gc, pi); + plot_deco_text(gc, pi); + + /* Bounding box last */ + gc->leftx = 0; gc->rightx = 1.0; + gc->topy = 0; gc->bottomy = 1.0; + + set_source_rgba(gc, BOUNDING_BOX); + cairo_set_line_width_scaled(gc->cr, 1); + move_to(gc, 0, 0); + line_to(gc, 0, 1); + line_to(gc, 1, 1); + line_to(gc, 1, 0); + cairo_close_path(gc->cr); + cairo_stroke(gc->cr); + + /* Put the dive computer name in the lower left corner */ + nickname = get_dc_nickname(dc->model, dc->deviceid); + if (!nickname || *nickname == '\0') + nickname = dc->model; + if (nickname) { + static const text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; + plot_text(gc, &computer, 0, 1, "%s", nickname); + } + + if (PP_GRAPHS_ENABLED) { + plot_pp_gas_profile(gc, pi); + plot_pp_text(gc, pi); + } + + /* now shift the translation back by half the margin; + * this way we can draw the vertical scales on both sides */ + cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); + gc->maxx += drawing_area->x; + gc->leftx = -(drawing_area->x / drawing_area->width) / 2.0; + gc->rightx = 1.0 - gc->leftx; + + plot_depth_scale(gc, pi); + + if (gc->printer) { + free(pi->entry); + last_pi_entry = pi->entry = NULL; + pi->nr = 0; + } +#endif } void ProfileGraphicsView::resizeEvent(QResizeEvent *event) From 19048b98e59aedb4d03490095c646d337d47e38b Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 19:20:49 -0300 Subject: [PATCH 133/226] Start plotting something. The first plotting method was removed from profile.c to profilegraphics.cpp and some conversion ( almost 1 to 1 ) was made so that the code could work. Since the code is big - this commit has just a part of it working - it plots the grid. but already works for testing the resizing of the window and Zooming ( unimplemented ) Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- profile.c | 204 ------------------------------- profile.h | 15 +++ qt-ui/profilegraphics.cpp | 246 ++++++++++++++++++++++++++++++++++++-- qt-ui/profilegraphics.h | 2 + 4 files changed, 251 insertions(+), 216 deletions(-) diff --git a/profile.c b/profile.c index 3f413f4ed..be6efa1e6 100644 --- a/profile.c +++ b/profile.c @@ -58,11 +58,6 @@ struct plot_data { #if USE_GTK_UI -/* Scale to 0,0 -> maxx,maxy */ -#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx) -#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) -#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y) - /* keep the last used gc around so we can invert the SCALEX calculation in * order to calculate a time value for an x coordinate */ static struct graphics_context last_gc; @@ -611,205 +606,6 @@ static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *p } } -static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) -{ - int i, incr; - cairo_t *cr = gc->cr; - int sec, depth; - struct plot_data *entry; - int maxtime, maxdepth, marker, maxline; - int increments[8] = { 10, 20, 30, 60, 5*60, 10*60, 15*60, 30*60 }; - - /* Get plot scaling limits */ - maxtime = get_maxtime(pi); - maxdepth = get_maxdepth(pi); - - gc->maxtime = maxtime; - - /* Time markers: at most every 10 seconds, but no more than 12 markers. - * We start out with 10 seconds and increment up to 30 minutes, - * depending on the dive time. - * This allows for 6h dives - enough (I hope) for even the craziest - * divers - but just in case, for those 8h depth-record-breaking dives, - * we double the interval if this still doesn't get us to 12 or fewer - * time markers */ - i = 0; - while (maxtime / increments[i] > 12 && i < 7) - i++; - incr = increments[i]; - while (maxtime / incr > 12) - incr *= 2; - - gc->leftx = 0; gc->rightx = maxtime; - gc->topy = 0; gc->bottomy = 1.0; - - last_gc = *gc; - - set_source_rgba(gc, TIME_GRID); - cairo_set_line_width_scaled(gc->cr, 2); - - for (i = incr; i < maxtime; i += incr) { - move_to(gc, i, 0); - line_to(gc, i, 1); - } - cairo_stroke(cr); - - /* now the text on the time markers */ - text_render_options_t tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP}; - if (maxtime < 600) { - /* Be a bit more verbose with shorter dives */ - for (i = incr; i < maxtime; i += incr) - plot_text(gc, &tro, i, 1, "%02d:%02d", i/60, i%60); - } else { - /* Only render the time on every second marker for normal dives */ - for (i = incr; i < maxtime; i += 2 * incr) - plot_text(gc, &tro, i, 1, "%d", i/60); - } - /* Depth markers: every 30 ft or 10 m*/ - gc->leftx = 0; gc->rightx = 1.0; - gc->topy = 0; gc->bottomy = maxdepth; - switch (prefs.units.length) { - case METERS: marker = 10000; break; - case FEET: marker = 9144; break; /* 30 ft */ - } - maxline = MAX(pi->maxdepth + marker, maxdepth * 2 / 3); - set_source_rgba(gc, DEPTH_GRID); - for (i = marker; i < maxline; i += marker) { - move_to(gc, 0, i); - line_to(gc, 1, i); - } - cairo_stroke(cr); - - gc->leftx = 0; gc->rightx = maxtime; - - /* Show mean depth */ - if (! gc->printer) { - set_source_rgba(gc, MEAN_DEPTH); - move_to(gc, 0, pi->meandepth); - line_to(gc, pi->entry[pi->nr - 1].sec, pi->meandepth); - cairo_stroke(cr); - } - - /* - * These are good for debugging text placement etc, - * but not for actual display.. - */ - if (0) { - plot_smoothed_profile(gc, pi); - plot_minmax_profile(gc, pi); - } - - /* Do the depth profile for the neat fill */ - gc->topy = 0; gc->bottomy = maxdepth; - - cairo_pattern_t *pat; - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 1, DEPTH_BOTTOM); - pattern_add_color_stop_rgba (gc, pat, 0, DEPTH_TOP); - - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - cairo_set_line_width_scaled(gc->cr, 2); - - entry = pi->entry; - move_to(gc, 0, 0); - for (i = 0; i < pi->nr; i++, entry++) - line_to(gc, entry->sec, entry->depth); - - /* Show any ceiling we may have encountered */ - for (i = pi->nr - 1; i >= 0; i--, entry--) { - if (entry->ndl) { - /* non-zero NDL implies this is a safety stop, no ceiling */ - line_to(gc, entry->sec, 0); - } else if (entry->stopdepth < entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); - } else { - line_to(gc, entry->sec, entry->depth); - } - } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - - /* if the user wants the deco ceiling more visible, do that here (this - * basically draws over the background that we had allowed to shine - * through so far) */ - if (prefs.profile_red_ceiling) { - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - entry = pi->entry; - move_to(gc, 0, 0); - for (i = 0; i < pi->nr; i++, entry++) { - if (entry->ndl == 0 && entry->stopdepth) { - if (entry->ndl == 0 && entry->stopdepth < entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); - } else { - line_to(gc, entry->sec, entry->depth); - } - } else { - line_to(gc, entry->sec, 0); - } - } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - } - /* finally, plot the calculated ceiling over all this */ - if (prefs.profile_calc_ceiling) { - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CALC_CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CALC_CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - entry = pi->entry; - move_to(gc, 0, 0); - for (i = 0; i < pi->nr; i++, entry++) { - if (entry->ceiling) - line_to(gc, entry->sec, entry->ceiling); - else - line_to(gc, entry->sec, 0); - } - line_to(gc, (entry-1)->sec, 0); /* make sure we end at 0 */ - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - } - /* next show where we have been bad and crossed the dc's ceiling */ - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - entry = pi->entry; - move_to(gc, 0, 0); - for (i = 0; i < pi->nr; i++, entry++) - line_to(gc, entry->sec, entry->depth); - - for (i = pi->nr - 1; i >= 0; i--, entry--) { - if (entry->ndl == 0 && entry->stopdepth > entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); - } else { - line_to(gc, entry->sec, entry->depth); - } - } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - - /* Now do it again for the velocity colors */ - entry = pi->entry; - for (i = 1; i < pi->nr; i++) { - entry++; - sec = entry->sec; - /* we want to draw the segments in different colors - * representing the vertical velocity, so we need to - * chop this into short segments */ - depth = entry->depth; - set_source_rgba(gc, VELOCITY_COLORS_START_IDX + entry->velocity); - move_to(gc, entry[-1].sec, entry[-1].depth); - line_to(gc, sec, depth); - cairo_stroke(cr); - } -} static int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi) { diff --git a/profile.h b/profile.h index 66d1ee190..1ad6625db 100644 --- a/profile.h +++ b/profile.h @@ -19,6 +19,21 @@ struct plot_info; void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); +/* + * When showing dive profiles, we scale things to the + * current dive. However, we don't scale past less than + * 30 minutes or 90 ft, just so that small dives show + * up as such unless zoom is enabled. + * We also need to add 180 seconds at the end so the min/max + * plots correctly + */ +int get_maxtime(struct plot_info *pi); + +/* get the maximum depth to which we want to plot + * take into account the additional verical space needed to plot + * partial pressure graphs */ +int get_maxdepth(struct plot_info *pi); + #ifdef __cplusplus } #endif diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index e500432fc..f9d528831 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -2,7 +2,9 @@ #include #include - +#include +#include +#include #include #include "../color.h" @@ -15,6 +17,12 @@ #define VELOCITY_COLORS_START_IDX VELO_STABLE #define VELOCITY_COLORS 5 +/* Scale to 0,0 -> maxx,maxy */ +#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx) +#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) +#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y) + +static struct graphics_context last_gc; static double plot_scale = SCALE_SCREEN; typedef enum { @@ -93,6 +101,7 @@ void fill_profile_color() ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) { setScene(new QGraphicsScene()); + setBackgroundBrush(QColor("#F3F3E6")); scene()->setSceneRect(0,0,100,100); fill_profile_color(); } @@ -112,6 +121,13 @@ static void plot_set_scale(scale_mode_t scale) void ProfileGraphicsView::plot(struct dive *dive) { + // Clear the items before drawing this dive. + qDeleteAll(scene()->items()); + scene()->clear(); + + if(!dive) + return; + struct plot_info *pi; struct divecomputer *dc = &dive->dc; @@ -151,13 +167,6 @@ void ProfileGraphicsView::plot(struct dive *dive) */ calculate_max_limits(dive, dc, &gc); -#if 0 - /* shift the drawing area so we have a nice margin around it */ - cairo_translate(gc->cr, drawing_area->x, drawing_area->y); - cairo_set_line_width_scaled(gc->cr, 1); - cairo_set_line_cap(gc->cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(gc->cr, CAIRO_LINE_JOIN_ROUND); - /* * We don't use "cairo_translate()" because that doesn't * scale line width etc. But the actual scaling we need @@ -165,16 +174,18 @@ void ProfileGraphicsView::plot(struct dive *dive) * * Snif. What a pity. */ - gc->maxx = (drawing_area->width - 2*drawing_area->x); - gc->maxy = (drawing_area->height - 2*drawing_area->y); + QRectF drawing_area = scene()->sceneRect(); + gc.maxx = (drawing_area.width() - 2 * drawing_area.x()); + gc.maxy = (drawing_area.height() - 2 * drawing_area.y()); dc = select_dc(dc); /* This is per-dive-computer. Right now we just do the first one */ - pi = create_plot_info(dive, dc, gc); + pi = create_plot_info(dive, dc, &gc); /* Depth profile */ - plot_depth_profile(gc, pi); + plot_depth_profile(&gc, pi); +#if 0 plot_events(gc, pi, dc); /* Temperature profile */ @@ -233,6 +244,217 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } + +void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) +{ + int i, incr; + int sec, depth; + struct plot_data *entry; + int maxtime, maxdepth, marker, maxline; + int increments[8] = { 10, 20, 30, 60, 5*60, 10*60, 15*60, 30*60 }; + + /* Get plot scaling limits */ + maxtime = get_maxtime(pi); + maxdepth = get_maxdepth(pi); + + gc->maxtime = maxtime; + + /* Time markers: at most every 10 seconds, but no more than 12 markers. + * We start out with 10 seconds and increment up to 30 minutes, + * depending on the dive time. + * This allows for 6h dives - enough (I hope) for even the craziest + * divers - but just in case, for those 8h depth-record-breaking dives, + * we double the interval if this still doesn't get us to 12 or fewer + * time markers */ + i = 0; + while (maxtime / increments[i] > 12 && i < 7) + i++; + incr = increments[i]; + while (maxtime / incr > 12) + incr *= 2; + + gc->leftx = 0; gc->rightx = maxtime; + gc->topy = 0; gc->bottomy = 1.0; + + last_gc = *gc; + + QColor color; + color = profile_color[TIME_GRID].at(0); + for (i = incr; i < maxtime; i += incr) { + QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, i, 0), SCALE(gc, i, 1)); + line->setPen(QPen(color)); + scene()->addItem(line); + } + +#if 0 + /* now the text on the time markers */ + text_render_options_t tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP}; + if (maxtime < 600) { + /* Be a bit more verbose with shorter dives */ + for (i = incr; i < maxtime; i += incr) + plot_text(gc, &tro, i, 1, "%02d:%02d", i/60, i%60); + } else { + /* Only render the time on every second marker for normal dives */ + for (i = incr; i < maxtime; i += 2 * incr) + plot_text(gc, &tro, i, 1, "%d", i/60); + } +#endif + + /* Depth markers: every 30 ft or 10 m*/ + gc->leftx = 0; gc->rightx = 1.0; + gc->topy = 0; gc->bottomy = maxdepth; + switch (prefs.units.length) { + case units::METERS: + marker = 10000; + break; + case units::FEET: + marker = 9144; + break; /* 30 ft */ + } + maxline = MAX(pi->maxdepth + marker, maxdepth * 2 / 3); + + color = profile_color[TIME_GRID].at(0); + + for (i = marker; i < maxline; i += marker) { + QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, 0, i), SCALE(gc, 1, i)); + line->setPen(QPen(color)); + scene()->addItem(line); + } + +#if 0 + gc->leftx = 0; gc->rightx = maxtime; + + /* Show mean depth */ + if (! gc->printer) { + set_source_rgba(gc, MEAN_DEPTH); + move_to(gc, 0, pi->meandepth); + line_to(gc, pi->entry[pi->nr - 1].sec, pi->meandepth); + cairo_stroke(cr); + } + + /* + * These are good for debugging text placement etc, + * but not for actual display.. + */ + if (0) { + plot_smoothed_profile(gc, pi); + plot_minmax_profile(gc, pi); + } + + /* Do the depth profile for the neat fill */ + gc->topy = 0; gc->bottomy = maxdepth; + + cairo_pattern_t *pat; + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 1, DEPTH_BOTTOM); + pattern_add_color_stop_rgba (gc, pat, 0, DEPTH_TOP); + + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + cairo_set_line_width_scaled(gc->cr, 2); + + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) + line_to(gc, entry->sec, entry->depth); + + /* Show any ceiling we may have encountered */ + for (i = pi->nr - 1; i >= 0; i--, entry--) { + if (entry->ndl) { + /* non-zero NDL implies this is a safety stop, no ceiling */ + line_to(gc, entry->sec, 0); + } else if (entry->stopdepth < entry->depth) { + line_to(gc, entry->sec, entry->stopdepth); + } else { + line_to(gc, entry->sec, entry->depth); + } + } + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + + /* if the user wants the deco ceiling more visible, do that here (this + * basically draws over the background that we had allowed to shine + * through so far) */ + if (prefs.profile_red_ceiling) { + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); + pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) { + if (entry->ndl == 0 && entry->stopdepth) { + if (entry->ndl == 0 && entry->stopdepth < entry->depth) { + line_to(gc, entry->sec, entry->stopdepth); + } else { + line_to(gc, entry->sec, entry->depth); + } + } else { + line_to(gc, entry->sec, 0); + } + } + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + } + /* finally, plot the calculated ceiling over all this */ + if (prefs.profile_calc_ceiling) { + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 0, CALC_CEILING_SHALLOW); + pattern_add_color_stop_rgba (gc, pat, 1, CALC_CEILING_DEEP); + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) { + if (entry->ceiling) + line_to(gc, entry->sec, entry->ceiling); + else + line_to(gc, entry->sec, 0); + } + line_to(gc, (entry-1)->sec, 0); /* make sure we end at 0 */ + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + } + /* next show where we have been bad and crossed the dc's ceiling */ + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); + pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) + line_to(gc, entry->sec, entry->depth); + + for (i = pi->nr - 1; i >= 0; i--, entry--) { + if (entry->ndl == 0 && entry->stopdepth > entry->depth) { + line_to(gc, entry->sec, entry->stopdepth); + } else { + line_to(gc, entry->sec, entry->depth); + } + } + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + + /* Now do it again for the velocity colors */ + entry = pi->entry; + for (i = 1; i < pi->nr; i++) { + entry++; + sec = entry->sec; + /* we want to draw the segments in different colors + * representing the vertical velocity, so we need to + * chop this into short segments */ + depth = entry->depth; + set_source_rgba(gc, VELOCITY_COLORS_START_IDX + entry->velocity); + move_to(gc, entry[-1].sec, entry[-1].depth); + line_to(gc, sec, depth); + cairo_stroke(cr); + } +#endif +} + + void ProfileGraphicsView::resizeEvent(QResizeEvent *event) { // Fits the scene's rectangle on the view. diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index e3380abcd..633409b23 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -12,6 +12,8 @@ public: protected: void resizeEvent(QResizeEvent *event); +private: + void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi); }; #endif From f269f8649644c5726fcd79ead443b0bb605a798d Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 19:36:40 -0300 Subject: [PATCH 134/226] Plot of the Mean Deph The mean depth now is plotted correctly. I wanted to do more stuff on this commit, but since it required that a few things on profile.c got moved to profile.h, commited to not have a huge blob for review. Signed-off-by: Tomaz Canabrava --- profile.c | 25 +------------------------ profile.h | 28 +++++++++++++++++++++++++++- qt-ui/profilegraphics.cpp | 13 +++++++------ 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/profile.c b/profile.c index be6efa1e6..161415e88 100644 --- a/profile.c +++ b/profile.c @@ -11,6 +11,7 @@ #endif #include "divelist.h" +#include "profile.h" #include "libdivecomputer/parser.h" #include "libdivecomputer/version.h" @@ -24,31 +25,7 @@ static struct plot_data *last_pi_entry = NULL; #define cairo_set_line_width_scaled(cr, w) \ cairo_set_line_width((cr), (w) * plot_scale); -typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t; -struct plot_data { - unsigned int in_deco:1; - unsigned int cylinderindex; - int sec; - /* pressure[0] is sensor pressure - * pressure[1] is interpolated pressure */ - int pressure[2]; - int temperature; - /* Depth info */ - int depth; - int ceiling; - int ndl; - int stoptime; - int stopdepth; - int cns; - int smoothed; - double po2, pn2, phe; - double mod, ead, end, eadd; - velocity_t velocity; - struct plot_data *min[3]; - struct plot_data *max[3]; - int avg[3]; -}; #define SENSOR_PR 0 #define INTERPOLATED_PR 1 diff --git a/profile.h b/profile.h index 1ad6625db..2c293fa7d 100644 --- a/profile.h +++ b/profile.h @@ -11,10 +11,36 @@ typedef int bool; #endif #endif -struct dive; +#include "dive.h" + +typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t; + struct divecomputer; struct graphics_context; struct plot_info; +struct plot_data { + unsigned int in_deco:1; + unsigned int cylinderindex; + int sec; + /* pressure[0] is sensor pressure + * pressure[1] is interpolated pressure */ + int pressure[2]; + int temperature; + /* Depth info */ + int depth; + int ceiling; + int ndl; + int stoptime; + int stopdepth; + int cns; + int smoothed; + double po2, pn2, phe; + double mod, ead, end, eadd; + velocity_t velocity; + struct plot_data *min[3]; + struct plot_data *max[3]; + int avg[3]; +}; void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index f9d528831..14ed21c9b 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -313,7 +313,7 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct } maxline = MAX(pi->maxdepth + marker, maxdepth * 2 / 3); - color = profile_color[TIME_GRID].at(0); + color = profile_color[DEPTH_GRID].at(0); for (i = marker; i < maxline; i += marker) { QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, 0, i), SCALE(gc, 1, i)); @@ -321,17 +321,18 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct scene()->addItem(line); } -#if 0 + gc->leftx = 0; gc->rightx = maxtime; + color = profile_color[MEAN_DEPTH].at(0); /* Show mean depth */ if (! gc->printer) { - set_source_rgba(gc, MEAN_DEPTH); - move_to(gc, 0, pi->meandepth); - line_to(gc, pi->entry[pi->nr - 1].sec, pi->meandepth); - cairo_stroke(cr); + QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, 0, pi->meandepth), SCALE(gc, pi->entry[pi->nr - 1].sec, pi->meandepth)); + line->setPen(QPen(color)); + scene()->addItem(line); } +#if 0 /* * These are good for debugging text placement etc, * but not for actual display.. From adcae4d913c9e151c17da6b0371b2117c7dabb93 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sat, 4 May 2013 20:18:16 -0300 Subject: [PATCH 135/226] Added the code to plot the actual Graph. This version already plots the dive-graph, with the gradient and all that jazz. One thing that will be easily spotted is that the size of the line is very thick - easily changed, I'm just using the default. As soon as everything is plotted correctly I'll fix the lines. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 32 ++++++++++++++++---------------- qt-ui/profilegraphics.h | 3 +++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 14ed21c9b..1f0b8efa5 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -341,38 +341,38 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_smoothed_profile(gc, pi); plot_minmax_profile(gc, pi); } +#endif /* Do the depth profile for the neat fill */ gc->topy = 0; gc->bottomy = maxdepth; - cairo_pattern_t *pat; - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 1, DEPTH_BOTTOM); - pattern_add_color_stop_rgba (gc, pat, 0, DEPTH_TOP); - - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); - cairo_set_line_width_scaled(gc->cr, 2); - entry = pi->entry; - move_to(gc, 0, 0); + + QGraphicsPolygonItem *neatFill = new QGraphicsPolygonItem(); + QPolygonF p; for (i = 0; i < pi->nr; i++, entry++) - line_to(gc, entry->sec, entry->depth); + p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); /* Show any ceiling we may have encountered */ for (i = pi->nr - 1; i >= 0; i--, entry--) { if (entry->ndl) { /* non-zero NDL implies this is a safety stop, no ceiling */ - line_to(gc, entry->sec, 0); + p.append( QPointF( SCALE(gc, entry->sec, 0) )); } else if (entry->stopdepth < entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); + p.append( QPointF( SCALE(gc, entry->sec, entry->stopdepth) )); } else { - line_to(gc, entry->sec, entry->depth); + p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); } } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); + neatFill->setPolygon(p); + QLinearGradient pat(0.0,0.0,0.0,p.boundingRect().height()); + pat.setColorAt(1, profile_color[DEPTH_BOTTOM].first()); + pat.setColorAt(0, profile_color[DEPTH_TOP].first()); + neatFill->setBrush(QBrush(pat)); + scene()->addItem(neatFill); + +#if 0 /* if the user wants the deco ceiling more visible, do that here (this * basically draws over the background that we had allowed to shine * through so far) */ diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 633409b23..a62e55c4d 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -14,6 +14,9 @@ protected: private: void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi); + + QPen defaultPen; + QBrush defaultBrush; }; #endif From c72609046adcaff2ca5af475113e583cbb943a04 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sun, 5 May 2013 13:42:33 -0300 Subject: [PATCH 136/226] Finished the port of the plot_profile code. There are a few regressions, mostly because the text is not being plotted yet and the background of some of the curves are not being applied. This is because QGrapdien is based on the coordinates of the items that I wanna paint, but I'v setted a QGradient that's global and doesn't take this into consideration. all curves are being plotted. in Small resolutions they plot bad. but it's just a matter of redrawing in the correct resolution. the Line widths are being hardcoded now, on the cairo version they weren't, this will need a bit of porting too. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 110 +++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 761ef262b..c5421659c 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -102,7 +102,7 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent { setScene(new QGraphicsScene()); setBackgroundBrush(QColor("#F3F3E6")); - scene()->setSceneRect(0,0,100,100); + scene()->setSceneRect(0,0,1000,1000); fill_profile_color(); } @@ -344,8 +344,11 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct entry = pi->entry; - QGraphicsPolygonItem *neatFill = new QGraphicsPolygonItem(); + QPolygonF p; + QLinearGradient pat(0.0,0.0,0.0,scene()->height()); + QGraphicsPolygonItem *neatFill = 0; + for (i = 0; i < pi->nr; i++, entry++) p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); @@ -353,86 +356,98 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct for (i = pi->nr - 1; i >= 0; i--, entry--) { if (entry->ndl) { /* non-zero NDL implies this is a safety stop, no ceiling */ - p.append( QPointF( SCALE(gc, entry->sec, 0) )); + p.append( QPointF( SCALE(gc, entry->sec, 0) )); } else if (entry->stopdepth < entry->depth) { p.append( QPointF( SCALE(gc, entry->sec, entry->stopdepth) )); } else { p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); } } - neatFill->setPolygon(p); - QLinearGradient pat(0.0,0.0,0.0,p.boundingRect().height()); + +; pat.setColorAt(1, profile_color[DEPTH_BOTTOM].first()); pat.setColorAt(0, profile_color[DEPTH_TOP].first()); + neatFill = new QGraphicsPolygonItem(); + neatFill->setPolygon(p); neatFill->setBrush(QBrush(pat)); + neatFill->setPen(QPen()); scene()->addItem(neatFill); -#if 0 /* if the user wants the deco ceiling more visible, do that here (this * basically draws over the background that we had allowed to shine * through so far) */ - if (prefs.profile_red_ceiling) { - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); + // TODO: port the prefs.profile_red_ceiling to QSettings + //if (prefs.profile_red_ceiling) { + p.clear(); + pat.setColorAt(0, profile_color[CEILING_SHALLOW].first()); + pat.setColorAt(1, profile_color[CEILING_DEEP].first()); + entry = pi->entry; - move_to(gc, 0, 0); for (i = 0; i < pi->nr; i++, entry++) { if (entry->ndl == 0 && entry->stopdepth) { if (entry->ndl == 0 && entry->stopdepth < entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); + p.append( QPointF( SCALE(gc, entry->sec, entry->stopdepth) )); } else { - line_to(gc, entry->sec, entry->depth); + p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); } } else { - line_to(gc, entry->sec, 0); + p.append( QPointF( SCALE(gc, entry->sec, 0) )); } } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - } + + neatFill = new QGraphicsPolygonItem(); + neatFill->setBrush(QBrush(pat)); + neatFill->setPolygon(p); + neatFill->setPen(QPen()); + scene()->addItem(neatFill); + //} + /* finally, plot the calculated ceiling over all this */ - if (prefs.profile_calc_ceiling) { - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CALC_CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CALC_CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); + // TODO: Port the profile_calc_ceiling to QSettings + // if (prefs.profile_calc_ceiling) { + pat.setColorAt(0, profile_color[CALC_CEILING_SHALLOW].first()); + pat.setColorAt(1, profile_color[CALC_CEILING_DEEP].first()); + entry = pi->entry; - move_to(gc, 0, 0); + p.clear(); + p.append( QPointF(0,0)); for (i = 0; i < pi->nr; i++, entry++) { if (entry->ceiling) - line_to(gc, entry->sec, entry->ceiling); + p.append( QPointF( SCALE(gc, entry->sec, entry->ceiling) )); else - line_to(gc, entry->sec, 0); + p.append( QPointF( SCALE(gc, entry->sec, 0) )); } - line_to(gc, (entry-1)->sec, 0); /* make sure we end at 0 */ - cairo_close_path(gc->cr); - cairo_fill(gc->cr); - } + p.append( QPointF( SCALE(gc, (entry-1)->sec, 0) )); + neatFill = new QGraphicsPolygonItem(); + neatFill->setPolygon(p); + neatFill->setPen(QPen()); + scene()->addItem(neatFill); + //} + /* next show where we have been bad and crossed the dc's ceiling */ - pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); - pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); - pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); - cairo_set_source(gc->cr, pat); - cairo_pattern_destroy(pat); + pat.setColorAt(0, profile_color[CEILING_SHALLOW].first()); + pat.setColorAt(1, profile_color[CEILING_DEEP].first()); + entry = pi->entry; - move_to(gc, 0, 0); + p.clear(); + p.append( QPointF(0,0)); for (i = 0; i < pi->nr; i++, entry++) - line_to(gc, entry->sec, entry->depth); + p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); for (i = pi->nr - 1; i >= 0; i--, entry--) { if (entry->ndl == 0 && entry->stopdepth > entry->depth) { - line_to(gc, entry->sec, entry->stopdepth); + p.append( QPointF( SCALE(gc, entry->sec, entry->stopdepth) )); } else { - line_to(gc, entry->sec, entry->depth); + p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); } } - cairo_close_path(gc->cr); - cairo_fill(gc->cr); + + neatFill = new QGraphicsPolygonItem(); + neatFill->setPolygon(p); + neatFill->setPen(QPen()); + neatFill->setBrush(QBrush(pat)); + scene()->addItem(neatFill); /* Now do it again for the velocity colors */ entry = pi->entry; @@ -442,13 +457,12 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct /* we want to draw the segments in different colors * representing the vertical velocity, so we need to * chop this into short segments */ + depth = entry->depth; - set_source_rgba(gc, VELOCITY_COLORS_START_IDX + entry->velocity); - move_to(gc, entry[-1].sec, entry[-1].depth); - line_to(gc, sec, depth); - cairo_stroke(cr); + QGraphicsLineItem *colorLine = new QGraphicsLineItem( SCALE(gc, entry[-1].sec, entry[-1].depth), SCALE(gc, sec, depth)); + colorLine->setPen(QPen(QBrush(profile_color[ (color_indice_t) (VELOCITY_COLORS_START_IDX + entry->velocity)].first()), 2 )); + scene()->addItem(colorLine); } -#endif } From be0f2e0e8de184380047f71f88cbdf0c23e94bd9 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sun, 5 May 2013 13:51:02 -0300 Subject: [PATCH 137/226] Enabled some Render Hints for Antialiasing Enabled some Render Hints for Antialiasing and smooth transformations. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 053e3db0f..d5bf177f0 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -105,6 +105,11 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent setScene(new QGraphicsScene()); setBackgroundBrush(QColor("#F3F3E6")); scene()->setSceneRect(0,0,1000,1000); + + setRenderHint(QPainter::Antialiasing); + setRenderHint(QPainter::HighQualityAntialiasing); + setRenderHint(QPainter::SmoothPixmapTransform); + fill_profile_color(); } From 8e35868b9b09f6417809678bd22cc315a696b706 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sun, 5 May 2013 19:30:54 -0300 Subject: [PATCH 138/226] Fixed some bad drawings Removed the lines of the graphs that doesn't supposed to have lines. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index d5bf177f0..89c3a74fc 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -328,7 +328,6 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct scene()->addItem(line); } - gc->leftx = 0; gc->rightx = maxtime; color = profile_color[MEAN_DEPTH].at(0); @@ -359,6 +358,7 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct QLinearGradient pat(0.0,0.0,0.0,scene()->height()); QGraphicsPolygonItem *neatFill = NULL; + p.append( QPointF(SCALE(gc, 0, 0) )); for (i = 0; i < pi->nr; i++, entry++) p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); @@ -373,26 +373,28 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); } } - pat.setColorAt(1, profile_color[DEPTH_BOTTOM].first()); pat.setColorAt(0, profile_color[DEPTH_TOP].first()); neatFill = new QGraphicsPolygonItem(); neatFill->setPolygon(p); neatFill->setBrush(QBrush(pat)); - neatFill->setPen(QPen()); + neatFill->setPen(QPen(QBrush(),0)); scene()->addItem(neatFill); + /* if the user wants the deco ceiling more visible, do that here (this * basically draws over the background that we had allowed to shine * through so far) */ // TODO: port the prefs.profile_red_ceiling to QSettings + //if (prefs.profile_red_ceiling) { p.clear(); pat.setColorAt(0, profile_color[CEILING_SHALLOW].first()); pat.setColorAt(1, profile_color[CEILING_DEEP].first()); entry = pi->entry; + p.append( QPointF(SCALE(gc, 0, 0) )); for (i = 0; i < pi->nr; i++, entry++) { if (entry->ndl == 0 && entry->stopdepth) { if (entry->ndl == 0 && entry->stopdepth < entry->depth) { @@ -408,19 +410,23 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct neatFill = new QGraphicsPolygonItem(); neatFill->setBrush(QBrush(pat)); neatFill->setPolygon(p); - neatFill->setPen(QPen()); + neatFill->setPen(QPen(QBrush(),0)); scene()->addItem(neatFill); //} /* finally, plot the calculated ceiling over all this */ // TODO: Port the profile_calc_ceiling to QSettings // if (prefs.profile_calc_ceiling) { + + qDebug() << "CALC_CEILING_SHALLOW" << profile_color[CALC_CEILING_SHALLOW].first(); + qDebug() << "CALC_CEILING_DEEP" << profile_color[CALC_CEILING_DEEP].first(); + pat.setColorAt(0, profile_color[CALC_CEILING_SHALLOW].first()); pat.setColorAt(1, profile_color[CALC_CEILING_DEEP].first()); entry = pi->entry; p.clear(); - p.append( QPointF(0,0)); + p.append( QPointF(SCALE(gc, 0, 0) )); for (i = 0; i < pi->nr; i++, entry++) { if (entry->ceiling) p.append( QPointF( SCALE(gc, entry->sec, entry->ceiling) )); @@ -430,17 +436,17 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct p.append( QPointF( SCALE(gc, (entry-1)->sec, 0) )); neatFill = new QGraphicsPolygonItem(); neatFill->setPolygon(p); - neatFill->setPen(QPen()); + neatFill->setPen(QPen(QBrush(),0)); + neatFill->setBrush(pat); scene()->addItem(neatFill); //} - /* next show where we have been bad and crossed the dc's ceiling */ pat.setColorAt(0, profile_color[CEILING_SHALLOW].first()); pat.setColorAt(1, profile_color[CEILING_DEEP].first()); entry = pi->entry; p.clear(); - p.append( QPointF(0,0)); + p.append( QPointF(SCALE(gc, 0, 0) )); for (i = 0; i < pi->nr; i++, entry++) p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); @@ -454,7 +460,7 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct neatFill = new QGraphicsPolygonItem(); neatFill->setPolygon(p); - neatFill->setPen(QPen()); + neatFill->setPen(QPen(QBrush(),0)); neatFill->setBrush(QBrush(pat)); scene()->addItem(neatFill); From e960a5b55803a576bf883dd4b15dc0fbca775a46 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 5 May 2013 21:06:45 -0700 Subject: [PATCH 139/226] Add most settings to the QSettings code This is missing the char * based settings (as I have no idea how to do those) plus the map provider. Everything else should work. Signed-off-by: Dirk Hohndel --- qt-gui.cpp | 6 ++- qt-ui/mainwindow.cpp | 119 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 121 insertions(+), 4 deletions(-) diff --git a/qt-gui.cpp b/qt-gui.cpp index 2155f0029..a4801f760 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -75,14 +75,16 @@ void init_ui(int *argcp, char ***argvp) QTextCodec::setCodecForCStrings(QTextCodec::codecForMib(106)); #endif +#if 0 subsurface_open_conf(); load_preferences(); + /* these still need to be handled in QSettings */ default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor"); default_dive_computer_product = subsurface_get_conf("dive_computer_product"); default_dive_computer_device = subsurface_get_conf("dive_computer_device"); - +#endif return; } @@ -94,7 +96,9 @@ void run_ui(void) void exit_ui(void) { delete application; +#if 0 subsurface_close_conf(); +#endif if (existing_filename) free((void *)existing_filename); if (default_dive_computer_device) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 8ac0ad871..53469dc86 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -312,10 +312,22 @@ bool MainWindow::askSaveChanges() return false; } +#define GET_UNIT(v, name, field, f, t) \ + v = settings.value(QString(name)); \ + if (v.isValid()) \ + prefs.units.field = (v.toInt() == (t)) ? (t) : (f) + +#define GET_BOOL(v, name, field) \ + v = settings.value(QString(name)); \ + if (v.isValid() && v.toInt()) \ + field = TRUE; \ + else \ + field = FALSE + void MainWindow::readSettings() { int i; - + QVariant v; QSettings settings("hohndel.org","subsurface"); settings.beginGroup("MainWindow"); @@ -344,8 +356,78 @@ void MainWindow::readSettings() else ui->ListWidget->setCurrentIndex(firstDiveOrTrip); settings.endGroup(); + settings.beginGroup("Units"); + GET_UNIT(v, "feet", length, units::METERS, units::FEET); + GET_UNIT(v, "psi", pressure, units::BAR, units::PSI); + GET_UNIT(v, "cuft", volume, units::LITER, units::CUFT); + GET_UNIT(v, "fahrenheit", temperature, units::CELSIUS, units::FAHRENHEIT); + GET_UNIT(v, "lbs", weight, units::KG, units::LBS); + settings.endGroup(); + settings.beginGroup("DisplayListColumns"); + GET_BOOL(v, "CYLINDER", prefs.visible_cols.cylinder); + GET_BOOL(v, "TEMPERATURE", prefs.visible_cols.temperature); + GET_BOOL(v, "TOTALWEIGHT", prefs.visible_cols.totalweight); + GET_BOOL(v, "SUIT", prefs.visible_cols.suit); + GET_BOOL(v, "NITROX", prefs.visible_cols.nitrox); + GET_BOOL(v, "OTU", prefs.visible_cols.otu); + GET_BOOL(v, "MAXCNS", prefs.visible_cols.maxcns); + GET_BOOL(v, "SAC", prefs.visible_cols.sac); + GET_BOOL(v, "po2graph", prefs.pp_graphs.po2); + GET_BOOL(v, "pn2graph", prefs.pp_graphs.pn2); + GET_BOOL(v, "phegraph", prefs.pp_graphs.phe); + settings.endGroup(); + settings.beginGroup("TecDetails"); + v = settings.value(QString("po2threshold")); + if (v.isValid()) + prefs.pp_graphs.po2_threshold = v.toDouble(); + v = settings.value(QString("pn2threshold")); + if (v.isValid()) + prefs.pp_graphs.pn2_threshold = v.toDouble(); + v = settings.value(QString("phethreshold")); + if (v.isValid()) + prefs.pp_graphs.phe_threshold = v.toDouble(); + GET_BOOL(v, "mod", prefs.mod); + v = settings.value(QString("modppO2")); + if (v.isValid()) + prefs.mod_ppO2 = v.toDouble(); + GET_BOOL(v, "ead", prefs.ead); + GET_BOOL(v, "redceiling", prefs.profile_red_ceiling); + GET_BOOL(v, "calcceiling", prefs.profile_calc_ceiling); + GET_BOOL(v, "calcceiling3m", prefs.calc_ceiling_3m_incr); + v = settings.value(QString("gflow")); + if (v.isValid()) + prefs.gflow = v.toInt() / 100.0; + v = settings.value(QString("gfhigh")); + if (v.isValid()) + prefs.gfhigh = v.toInt() / 100.0; + set_gf(prefs.gflow, prefs.gfhigh); + settings.endGroup(); + +#if ONCE_WE_CAN_SET_FONTS + settings.beginGroup("Display"); + v = settings.value(QString("divelist_font")); + if (v.isValid()) + /* I don't think this is right */ + prefs.divelist_font = strdup(v.toString); +#endif + +#if DONT_KNOW_HOW_TO_DO_THAT + v = settings.value(QString("default_filename")); + if (v.isValid()) + prefs.default_filename = strdup(v.toString); +#endif + +#if ONCE_WE_HAVE_MAPS + v = settings.value(QString_int("map_provider")); + if(v.isValid()) + prefs.map_provider = v.toInt(); +#endif } +#define SAVE_VALUE(name, field) \ + if (prefs.field != default_prefs.field) \ + settings.setValue(name, prefs.field) + void MainWindow::writeSettings() { int i; @@ -361,8 +443,39 @@ void MainWindow::writeSettings() for (i = TreeItemDT::NR; i < TreeItemDT::COLUMNS; i++) settings.setValue(QString("colwidth%1").arg(i), ui->ListWidget->columnWidth(i)); settings.endGroup(); - - /* other groups here; avoid '/' and '\' in keys with setValue(...) please */ + settings.beginGroup("Units"); + SAVE_VALUE("feet", units.length); + SAVE_VALUE("psi", units.pressure); + SAVE_VALUE("cuft", units.volume); + SAVE_VALUE("fahrenheit", units.temperature); + SAVE_VALUE("lbs", units.weight); + settings.endGroup(); + settings.beginGroup("DisplayListColumns"); + SAVE_VALUE("TEMPERATURE", visible_cols.temperature); + SAVE_VALUE("TOTALWEIGHT", visible_cols.totalweight); + SAVE_VALUE("SUIT", visible_cols.suit); + SAVE_VALUE("CYLINDER", visible_cols.cylinder); + SAVE_VALUE("NITROX", visible_cols.nitrox); + SAVE_VALUE("SAC", visible_cols.sac); + SAVE_VALUE("OTU", visible_cols.otu); + SAVE_VALUE("MAXCNS", visible_cols.maxcns); + settings.endGroup(); + settings.beginGroup("TecDetails"); + SAVE_VALUE("po2graph", pp_graphs.po2); + SAVE_VALUE("pn2graph", pp_graphs.pn2); + SAVE_VALUE("phegraph", pp_graphs.phe); + SAVE_VALUE("po2threshold", pp_graphs.po2_threshold); + SAVE_VALUE("pn2threshold", pp_graphs.pn2_threshold); + SAVE_VALUE("phethreshold", pp_graphs.phe_threshold); + SAVE_VALUE("mod", mod); + SAVE_VALUE("modppO2", mod_ppO2); + SAVE_VALUE("ead", ead); + SAVE_VALUE("redceiling", profile_red_ceiling); + SAVE_VALUE("calcceiling", profile_calc_ceiling); + SAVE_VALUE("calcceiling3m", calc_ceiling_3m_incr); + SAVE_VALUE("gflow", gflow); + SAVE_VALUE("gfhigh", gfhigh); + settings.endGroup(); } void MainWindow::closeEvent(QCloseEvent *event) From 484c10ba5a0de376928201fbac168ea3738407e0 Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Mon, 6 May 2013 08:07:03 +0200 Subject: [PATCH 140/226] =?UTF-8?q?Avoid=20redefinition=20of=20typedef=20?= =?UTF-8?q?=E2=80=98bool=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compiler on MacOSX wouldn't build Subsurface when bool was redefined. Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- display.h | 6 ------ dive.h | 7 ++++++- divelist.h | 6 ------ profile.h | 6 ------ 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/display.h b/display.h index bc60c059f..047d00589 100644 --- a/display.h +++ b/display.h @@ -3,12 +3,6 @@ #ifdef __cplusplus extern "C" { -#else -#if __STDC_VERSION__ >= 199901L -#include -#else -typedef int bool; -#endif #endif #define SCALE_SCREEN 1.0 diff --git a/dive.h b/dive.h index c13cac0aa..0c2e5b721 100644 --- a/dive.h +++ b/dive.h @@ -17,6 +17,12 @@ #ifdef __cplusplus extern "C" { +#else +#if __STDC_VERSION__ >= 199901L +#include +#else +typedef int bool; +#endif #endif #define O2_IN_AIR 209 // permille @@ -32,7 +38,6 @@ extern "C" { #define SEAWATER_SALINITY 10300 #define FRESHWATER_SALINITY 10000 - /* * Some silly typedefs to make our units very explicit. * diff --git a/divelist.h b/divelist.h index d17ea7c12..01ef613d9 100644 --- a/divelist.h +++ b/divelist.h @@ -3,12 +3,6 @@ #ifdef __cplusplus extern "C" { -#else -#if __STDC_VERSION__ >= 199901L -#include -#else -typedef int bool; -#endif #endif struct dive; diff --git a/profile.h b/profile.h index 2c293fa7d..b3cc48a68 100644 --- a/profile.h +++ b/profile.h @@ -3,12 +3,6 @@ #ifdef __cplusplus extern "C" { -#else -#if __STDC_VERSION__ >= 199901L -#include -#else -typedef int bool; -#endif #endif #include "dive.h" From f07614fb2f55644a6124b0e837d2776afadee113 Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Mon, 6 May 2013 08:30:40 +0100 Subject: [PATCH 141/226] Reformat statistics tab in MainTab tab widget Rename various labels and text into clear pairs and reflect changes into .cpp file. To avoid clashes with names on other tabs use '..All..' to emphasise that this page deals with an aggregate across the selected dives. Re-format the statistics tab. Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 28 +++++------ qt-ui/maintab.ui | 125 ++++++++++++++++++++++++++++------------------ 2 files changed, 91 insertions(+), 62 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index d72df965c..8d196a872 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -44,20 +44,20 @@ void MainTab::clearInfo() void MainTab::clearStats() { - ui->maxdepth_2->setText(QString()); - ui->mindepth->setText(QString()); - ui->avgdepth->setText(QString()); - ui->maxsac->setText(QString()); - ui->minsac->setText(QString()); - ui->avgsac->setText(QString()); - ui->dives->setText(QString()); - ui->maxtemp->setText(QString()); - ui->mintemp->setText(QString()); - ui->avgtemp->setText(QString()); - ui->totaltime->setText(QString()); - ui->avgtime->setText(QString()); - ui->longestdive->setText(QString()); - ui->shortestdive->setText(QString()); + ui->maximumDepthAllText->setText(QString()); + ui->minimumDepthAllText->setText(QString()); + ui->averageDepthAllText->setText(QString()); + ui->maximumSacAllText->setText(QString()); + ui->minimumSacAllText->setText(QString()); + ui->averageSacAllText->setText(QString()); + ui->divesAllText->setText(QString()); + ui->maximumTemperatureAllText->setText(QString()); + ui->minimumTemperatureAllText->setText(QString()); + ui->averageTemperatureAllText->setText(QString()); + ui->totalTimeAllText->setText(QString()); + ui->averageTimeAllText->setText(QString()); + ui->longestAllText->setText(QString()); + ui->shortestAllText->setText(QString()); } void MainTab::on_addCylinder_clicked() diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index 82019e6c3..5ec104638 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -14,7 +14,7 @@ TabWidget - 2 + 0 @@ -528,12 +528,28 @@ - - + + + Qt::Vertical + + + + 20 + 20 + + + + + + + 10 + + 15 + - + 75 @@ -541,12 +557,12 @@ - Max Depth + Max. Depth - + 75 @@ -554,12 +570,12 @@ - Min Depth + Min. Depth - + 75 @@ -567,33 +583,33 @@ - Avg Depth + Ave. Depth - + TextLabel - + TextLabel - + TextLabel - + 75 @@ -601,12 +617,12 @@ - Max SAC + Max. SAC - + 75 @@ -614,12 +630,12 @@ - Min SAC + Min. SAC - + 75 @@ -627,26 +643,26 @@ - Avg SAC + Ave. SAC - + TextLabel - + TextLabel - + TextLabel @@ -655,15 +671,28 @@ - - + + + Qt::Vertical + + + + 20 + 20 + + + + + + + 10 - - 0 + + 15 - + 75 @@ -679,7 +708,7 @@ - + 75 @@ -687,12 +716,12 @@ - Max Temp + Max. Temp. - + 75 @@ -700,12 +729,12 @@ - Min Temp + Min. Temp. - + 75 @@ -713,40 +742,40 @@ - Avg Temp + Ave. Temp. - + TextLabel - + TextLabel - + TextLabel - + TextLabel - + 75 @@ -759,7 +788,7 @@ - + 75 @@ -767,12 +796,12 @@ - Avg Time + Ave. Time - + 75 @@ -780,12 +809,12 @@ - Longest Dive + Longest - + 75 @@ -793,33 +822,33 @@ - Shortest Dive + Shortest - + TextLabel - + TextLabel - + TextLabel - + TextLabel @@ -835,7 +864,7 @@ 20 - 85 + 20 From baee8975747e8a4944b377b53a554560f292a881 Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Mon, 6 May 2013 09:12:53 +0100 Subject: [PATCH 142/226] Add various keyboard shortcuts. Add shortcuts to match GTK version for view menu items and the log menu so that e.g. Ctrl+1 selects the list view. Remove debug statements from the view functions. Leave in place for functions with no obvious actions yet coded. Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 8 -------- qt-ui/mainwindow.ui | 23 ++++++++++++++++++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 53469dc86..f29c82588 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -207,8 +207,6 @@ void MainWindow::on_actionYearlyStatistics_triggered() void MainWindow::on_actionViewList_triggered() { - qDebug("actionViewList"); - ui->InfoWidget->setVisible(false); ui->ListWidget->setVisible(true); ui->ProfileWidget->setVisible(false); @@ -216,8 +214,6 @@ void MainWindow::on_actionViewList_triggered() void MainWindow::on_actionViewProfile_triggered() { - qDebug("actionViewProfile"); - ui->InfoWidget->setVisible(false); ui->ListWidget->setVisible(false); ui->ProfileWidget->setVisible(true); @@ -225,8 +221,6 @@ void MainWindow::on_actionViewProfile_triggered() void MainWindow::on_actionViewInfo_triggered() { - qDebug("actionViewInfo"); - ui->InfoWidget->setVisible(true); ui->ListWidget->setVisible(false); ui->ProfileWidget->setVisible(false); @@ -234,8 +228,6 @@ void MainWindow::on_actionViewInfo_triggered() void MainWindow::on_actionViewAll_triggered() { - qDebug("actionViewAll"); - ui->InfoWidget->setVisible(true); ui->ListWidget->setVisible(true); ui->ProfileWidget->setVisible(true); diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 7dfbae746..476b124e8 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -88,7 +88,7 @@ 0 0 763 - 25 + 20 @@ -240,6 +240,9 @@ Download from Dive computer + + Ctrl+D +
@@ -280,31 +283,49 @@ View List + + Ctrl+1 + View Profile + + Ctrl+2 + View Info + + Ctrl+3 + View All + + Ctrl+4 + Prev DC + + Ctrl+Shift+C + Next DC + + Ctrl+C + From 06eab74a72c67f5b1dac3cd92bdaa91a15ae8eac Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 6 May 2013 13:23:14 -0300 Subject: [PATCH 143/226] Added the code to populate the tabs when a dive is selected. So, this is what happens now: Every tab should be populated from updateDiveInfo method, it will be called whenever a new dive is selected I'm already populating the 'notes' box to show how it can be done. If you are unsure what's the name of anything, open the file maintab.ui on the designer, click on the item and check its objectName, the access is ui->objectName from here on. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 16 ++++++++++++++++ qt-ui/maintab.h | 2 ++ qt-ui/mainwindow.cpp | 1 + 3 files changed, 19 insertions(+) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 8d196a872..84f29e6e8 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -60,6 +60,22 @@ void MainTab::clearStats() ui->shortestAllText->setText(QString()); } +void MainTab::updateDiveInfo(int dive) +{ + // So, this is what happens now: + // Every tab should be populated from this method, + // it will be called whenever a new dive is selected + // I'm already populating the 'notes' box + // to show how it can be done. + // If you are unsure what's the name of anything, + // open the file maintab.ui on the designer + // click on the item and check its objectName, + // the access is ui->objectName from here on. + + struct dive *d = get_dive(dive); + ui->notes->setText(d->notes); +} + void MainTab::on_addCylinder_clicked() { if (cylindersModel->rowCount() >= MAX_CYLINDERS) diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h index cf83e0dfe..e09781362 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -35,6 +35,8 @@ public Q_SLOTS: void on_editWeight_clicked(); void on_delWeight_clicked(); + void updateDiveInfo(int dive); + private: Ui::MainTab *ui; WeightModel *weightModel; diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index f29c82588..e8fe80460 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -94,6 +94,7 @@ void MainWindow::dive_selection_changed(const QItemSelection& newSelection, cons select_dive(get_divenr(d)); } ui->ProfileWidget->plot(get_dive(selected_dive)); + ui->InfoWidget->updateDiveInfo(selected_dive); } void MainWindow::on_actionSave_triggered() From b3fce3497cc6cd801afae01f713b56b1d8e883b8 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 6 May 2013 10:16:16 -0700 Subject: [PATCH 144/226] Fill Dive Notes widget Make sure we clear things out if no dive is selected. Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 16 +++++++++++++++- qt-ui/maintab.ui | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 84f29e6e8..df6ee69eb 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -60,6 +60,12 @@ void MainTab::clearStats() ui->shortestAllText->setText(QString()); } +#define UPDATE_TEXT(d, field) \ + if (!d || !d->field) \ + ui->field->setText(""); \ + else \ + ui->field->setText(d->field) + void MainTab::updateDiveInfo(int dive) { // So, this is what happens now: @@ -73,7 +79,15 @@ void MainTab::updateDiveInfo(int dive) // the access is ui->objectName from here on. struct dive *d = get_dive(dive); - ui->notes->setText(d->notes); + UPDATE_TEXT(d, notes); + UPDATE_TEXT(d, location); + UPDATE_TEXT(d, suit); + UPDATE_TEXT(d, divemaster); + UPDATE_TEXT(d, buddy); + if (d) + ui->rating->setCurrentStars(d->rating); + else + ui->rating->setCurrentStars(0); } void MainTab::on_addCylinder_clicked() diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index 5ec104638..70f88caec 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -79,7 +79,7 @@ - + From 1b392b35bca24ce25da9073dbe9a1bb0186c47af Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 6 May 2013 15:35:17 -0300 Subject: [PATCH 145/226] Port the plot text method to Qt, also test it by actually plotting something The plot_text function from the cairo-methods are now ported on the qt version. this patch moves around some code since quite defines are already used and I didn't want to reinvent the whell. Original code used varargs, but I prefered to change it , so now it receives just a reference to a QString object and the string must be constructed before sending, using the .arg methods. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- display-gtk.h | 16 ------------ profile.c | 51 --------------------------------------- profile.h | 24 ++++++++++++++++++ qt-ui/profilegraphics.cpp | 36 ++++++++++++++++++++------- qt-ui/profilegraphics.h | 6 +++++ 5 files changed, 57 insertions(+), 76 deletions(-) diff --git a/display-gtk.h b/display-gtk.h index 736616b1a..634f05cab 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -90,22 +90,6 @@ typedef gint (*sort_func_t)(GtkTreeModel *model, GtkTreeIter *b, gpointer user_data); -#define ALIGN_LEFT 1 -#define ALIGN_RIGHT 2 -#define INVISIBLE 4 -#define UNSORTABLE 8 -#define EDITABLE 16 - -#ifndef TEXT_SCALE -#define TEXT_SCALE 1.0 -#endif - -#define DEPTH_TEXT_SIZE (10 * TEXT_SCALE) -#define PRESSURE_TEXT_SIZE (10 * TEXT_SCALE) -#define DC_TEXT_SIZE (10.5 * TEXT_SCALE) -#define PP_TEXT_SIZE (11 * TEXT_SCALE) -#define TEMP_TEXT_SIZE (12 * TEXT_SCALE) - extern GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, data_func_t data_func, unsigned int flags); extern GtkTreeViewColumn *tree_view_column_add_pixbuf(GtkWidget *tree_view, data_func_t data_func, GtkTreeViewColumn *col); diff --git a/profile.c b/profile.c index 161415e88..15fb0ff42 100644 --- a/profile.c +++ b/profile.c @@ -157,57 +157,6 @@ int get_maxdepth(struct plot_info *pi) return md; } -#if 0 -typedef struct { - double size; - color_indice_t color; - double hpos, vpos; -} text_render_options_t; -#endif - -#define RIGHT (-1.0) -#define CENTER (-0.5) -#define LEFT (0.0) - -#define TOP (1) -#define MIDDLE (0) -#define BOTTOM (-1) - -#if USE_GTK_UI -static void plot_text(struct graphics_context *gc, const text_render_options_t *tro, - double x, double y, const char *fmt, ...) -{ - cairo_t *cr = gc->cr; - cairo_font_extents_t fe; - cairo_text_extents_t extents; - double dx, dy; - char buffer[256]; - va_list args; - - va_start(args, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, args); - va_end(args); - - cairo_set_font_size(cr, tro->size * plot_scale); - cairo_font_extents(cr, &fe); - cairo_text_extents(cr, buffer, &extents); - dx = tro->hpos * (extents.width + extents.x_bearing); - dy = tro->vpos * (extents.height + fe.descent); - move_to(gc, x, y); - cairo_rel_move_to(cr, dx, dy); - - cairo_text_path(cr, buffer); - set_source_rgba(gc, TEXT_BACKGROUND); - cairo_stroke(cr); - - move_to(gc, x, y); - cairo_rel_move_to(cr, dx, dy); - - set_source_rgba(gc, tro->color); - cairo_show_text(cr, buffer); -} -#endif /* USE_GTK_UI */ - /* collect all event names and whether we display them */ struct ev_select { char *ev_name; diff --git a/profile.h b/profile.h index b3cc48a68..8f58082d1 100644 --- a/profile.h +++ b/profile.h @@ -54,6 +54,30 @@ int get_maxtime(struct plot_info *pi); * partial pressure graphs */ int get_maxdepth(struct plot_info *pi); +#define ALIGN_LEFT 1 +#define ALIGN_RIGHT 2 +#define INVISIBLE 4 +#define UNSORTABLE 8 +#define EDITABLE 16 + +#ifndef TEXT_SCALE +#define TEXT_SCALE 1.0 +#endif + +#define DEPTH_TEXT_SIZE (10 * TEXT_SCALE) +#define PRESSURE_TEXT_SIZE (10 * TEXT_SCALE) +#define DC_TEXT_SIZE (10.5 * TEXT_SCALE) +#define PP_TEXT_SIZE (11 * TEXT_SCALE) +#define TEMP_TEXT_SIZE (12 * TEXT_SCALE) + +#define RIGHT (-1.0) +#define CENTER (-0.5) +#define LEFT (0.0) + +#define TOP (1) +#define MIDDLE (0) +#define BOTTOM (-1) + #ifdef __cplusplus } #endif diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 89c3a74fc..b52368b11 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -100,6 +100,12 @@ void fill_profile_color() } #undef COLOR +struct text_render_options{ + double size; + color_indice_t color; + double hpos, vpos; +}; + ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) { setScene(new QGraphicsScene()); @@ -293,19 +299,17 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct scene()->addItem(line); } -#if 0 /* now the text on the time markers */ - text_render_options_t tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP}; + struct text_render_options tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP}; if (maxtime < 600) { /* Be a bit more verbose with shorter dives */ for (i = incr; i < maxtime; i += incr) - plot_text(gc, &tro, i, 1, "%02d:%02d", i/60, i%60); + plot_text(gc, &tro, i, 1, QString("%1:%2").arg(i/60).arg(i%60)); } else { /* Only render the time on every second marker for normal dives */ for (i = incr; i < maxtime; i += 2 * incr) - plot_text(gc, &tro, i, 1, "%d", i/60); + plot_text(gc, &tro, i, 1, QString::number(i/60)); } -#endif /* Depth markers: every 30 ft or 10 m*/ gc->leftx = 0; gc->rightx = 1.0; @@ -418,9 +422,6 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct // TODO: Port the profile_calc_ceiling to QSettings // if (prefs.profile_calc_ceiling) { - qDebug() << "CALC_CEILING_SHALLOW" << profile_color[CALC_CEILING_SHALLOW].first(); - qDebug() << "CALC_CEILING_DEEP" << profile_color[CALC_CEILING_DEEP].first(); - pat.setColorAt(0, profile_color[CALC_CEILING_SHALLOW].first()); pat.setColorAt(1, profile_color[CALC_CEILING_DEEP].first()); @@ -479,6 +480,23 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct } } +void ProfileGraphicsView::plot_text(struct graphics_context *gc, text_render_options_t *tro, double x, double y, const QString& text) +{ + QFontMetrics fm(font()); + + double dx = tro->hpos * (fm.width(text)); + double dy = tro->vpos * (fm.height()); + + QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text); + QPointF point( SCALE(gc, x, y) ); // This is neded because of the SCALE macro. + + item->setPos(point.x() + dx, point.y() +dy ); + item->setBrush( QBrush(profile_color[tro->color].first())); + item->setPen( QPen(profile_color[BACKGROUND].first())); + scene()->addItem(item); + qDebug() << item->pos(); +} + void ProfileGraphicsView::resizeEvent(QResizeEvent *event) { @@ -486,5 +504,5 @@ void ProfileGraphicsView::resizeEvent(QResizeEvent *event) // I can pass some parameters to this - // like Qt::IgnoreAspectRatio or Qt::KeepAspectRatio QRectF r = scene()->sceneRect(); - fitInView ( r.x() - 2, r.y() -2, r.width() + 4, r.height() + 4); // do a little bit of spacing; + fitInView ( r.x() - 50, r.y() -50, r.width() + 100, r.height() + 100); // do a little bit of spacing; } diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index a62e55c4d..0b472f8b5 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -3,6 +3,11 @@ #include +struct text_render_options; +struct graphics_context; +struct plot_info; +typedef struct text_render_options text_render_options_t; + class ProfileGraphicsView : public QGraphicsView { Q_OBJECT public: @@ -14,6 +19,7 @@ protected: private: void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi); + void plot_text(struct graphics_context *gc, text_render_options_t *tro, double x, double y, const QString &text); QPen defaultPen; QBrush defaultBrush; From 55f31dc0114a2719c867a8c1210c704f657b646f Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 6 May 2013 16:29:26 -0300 Subject: [PATCH 146/226] Make the text ignores transformations on the scene Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index b52368b11..d4c918ac6 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -493,6 +493,7 @@ void ProfileGraphicsView::plot_text(struct graphics_context *gc, text_render_opt item->setPos(point.x() + dx, point.y() +dy ); item->setBrush( QBrush(profile_color[tro->color].first())); item->setPen( QPen(profile_color[BACKGROUND].first())); + item->setFlag(QGraphicsItem::ItemIgnoresTransformations); scene()->addItem(item); qDebug() << item->pos(); } From 867435442b8f6f4094d7075be2cbc697d1618c26 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 6 May 2013 18:58:18 -0300 Subject: [PATCH 147/226] Plotting the Events done There are subtle differences, the Cairo version looks prettier - but that's fixable. I did a small triangle and a exclamation mark on it. maybe a gradient would make a good difference there. this item has a ItemIgnoresTransformation tag, so scalling, rotating or zooming will not change it's size. The tooltips are not yet ported. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- profile.c | 100 ++---------------------------------- profile.h | 5 ++ qt-ui/profilegraphics.cpp | 104 +++++++++++++++++++++++++++++++++++++- qt-ui/profilegraphics.h | 2 + 4 files changed, 113 insertions(+), 98 deletions(-) diff --git a/profile.c b/profile.c index 15fb0ff42..2a8d5e4ed 100644 --- a/profile.c +++ b/profile.c @@ -158,13 +158,9 @@ int get_maxdepth(struct plot_info *pi) } /* collect all event names and whether we display them */ -struct ev_select { - char *ev_name; - gboolean plot_ev; -}; -static struct ev_select *ev_namelist; -static int evn_allocated; -static int evn_used; +struct ev_select *ev_namelist; +int evn_allocated; +int evn_used; int evn_foreach(void (*callback)(const char *, int *, void *), void *data) { @@ -205,95 +201,7 @@ void remember_event(const char *eventname) evn_used++; } -#if USE_GTK_UI -static void plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *event) -{ - int i, depth = 0; - int x,y; - char buffer[256]; - - /* is plotting this event disabled? */ - if (event->name) { - for (i = 0; i < evn_used; i++) { - if (! strcmp(event->name, ev_namelist[i].ev_name)) { - if (ev_namelist[i].plot_ev) - break; - else - return; - } - } - } - if (event->time.seconds < 30 && !strcmp(event->name, "gaschange")) - /* a gas change in the first 30 seconds is the way of some dive computers - * to tell us the gas that is used; let's not plot a marker for that */ - return; - - for (i = 0; i < pi->nr; i++) { - struct plot_data *data = pi->entry + i; - if (event->time.seconds < data->sec) - break; - depth = data->depth; - } - /* draw a little triangular marker and attach tooltip */ - x = SCALEX(gc, event->time.seconds); - y = SCALEY(gc, depth); - set_source_rgba(gc, ALERT_BG); - cairo_move_to(gc->cr, x-6, y+12); - cairo_line_to(gc->cr, x+6, y+12); - cairo_line_to(gc->cr, x , y); - cairo_line_to(gc->cr, x-6, y+12); - cairo_stroke_preserve(gc->cr); - cairo_fill(gc->cr); - set_source_rgba(gc, ALERT_FG); - cairo_move_to(gc->cr, x, y+3); - cairo_line_to(gc->cr, x, y+7); - cairo_move_to(gc->cr, x, y+10); - cairo_line_to(gc->cr, x, y+10); - cairo_stroke(gc->cr); - /* we display the event on screen - so translate */ - if (event->value) { - if (event->name && !strcmp(event->name, "gaschange")) { - unsigned int he = event->value >> 16; - unsigned int o2 = event->value & 0xffff; - if (he) { - snprintf(buffer, sizeof(buffer), "%s:%u/%u", - _(event->name), o2, he); - } else { - if (o2 == 21) - snprintf(buffer, sizeof(buffer), "%s:%s", - _(event->name), _("air")); - else - snprintf(buffer, sizeof(buffer), "%s:%u%% %s", - _(event->name), o2, "O" UTF8_SUBSCRIPT_2); - } - } else if (event->name && !strcmp(event->name, "SP change")) { - snprintf(buffer, sizeof(buffer), "%s:%0.1f", _(event->name), (double) event->value / 1000); - } else { - snprintf(buffer, sizeof(buffer), "%s:%d", _(event->name), event->value); - } - } else if (event->name && !strcmp(event->name, "SP change")) { - snprintf(buffer, sizeof(buffer), _("Bailing out to OC")); - } else { - snprintf(buffer, sizeof(buffer), "%s%s", _(event->name), - event->flags == SAMPLE_FLAGS_BEGIN ? C_("Starts with space!"," begin") : - event->flags == SAMPLE_FLAGS_END ? C_("Starts with space!", " end") : ""); - } - attach_tooltip(x-6, y, 12, 12, buffer, event); -} - -static void plot_events(struct graphics_context *gc, struct plot_info *pi, struct divecomputer *dc) -{ - struct event *event = dc->events; - - if (gc->printer) - return; - - while (event) { - plot_one_event(gc, pi, event); - event = event->next; - } -} - +#if 0 static void render_depth_sample(struct graphics_context *gc, struct plot_data *entry, const text_render_options_t *tro) { int sec = entry->sec, decimals; diff --git a/profile.h b/profile.h index 8f58082d1..d22589c68 100644 --- a/profile.h +++ b/profile.h @@ -39,6 +39,11 @@ struct plot_data { void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); +struct ev_select { + char *ev_name; + bool plot_ev; +}; + /* * When showing dive profiles, we scale things to the * current dive. However, we don't scale past less than diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index d4c918ac6..b3163add4 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../color.h" #include "../display.h" @@ -106,6 +107,10 @@ struct text_render_options{ double hpos, vpos; }; +extern struct ev_select *ev_namelist; +extern int evn_allocated; +extern int evn_used; + ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) { setScene(new QGraphicsScene()); @@ -198,9 +203,9 @@ void ProfileGraphicsView::plot(struct dive *dive) /* Depth profile */ plot_depth_profile(&gc, pi); -#if 0 - plot_events(gc, pi, dc); + plot_events(&gc, pi, dc); +#if 0 /* Temperature profile */ plot_temperature_profile(gc, pi); @@ -257,6 +262,101 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } +void ProfileGraphicsView::plot_events(struct graphics_context *gc, struct plot_info *pi, struct divecomputer *dc) +{ + struct event *event = dc->events; + +// if (gc->printer){ +// return; +// } + + while (event) { + plot_one_event(gc, pi, event); + event = event->next; + } +} + +void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *ev) +{ + int i, depth = 0; + + /* is plotting this event disabled? */ + if (ev->name) { + for (i = 0; i < evn_used; i++) { + if (! strcmp(ev->name, ev_namelist[i].ev_name)) { + if (ev_namelist[i].plot_ev) + break; + else + return; + } + } + } + + if (ev->time.seconds < 30 && !strcmp(ev->name, "gaschange")) + /* a gas change in the first 30 seconds is the way of some dive computers + * to tell us the gas that is used; let's not plot a marker for that */ + return; + + for (i = 0; i < pi->nr; i++) { + struct plot_data *data = pi->entry + i; + if (ev->time.seconds < data->sec) + break; + depth = data->depth; + } + + /* draw a little triangular marker and attach tooltip */ + QPolygonF poly; + poly.push_back(QPointF(-8, 16)); + poly.push_back(QPointF(8, 16)); + poly.push_back(QPointF(0, 0)); + poly.push_back(QPointF(-8, 16)); + + int x = SCALEX(gc, ev->time.seconds); + int y = SCALEY(gc, depth); + + QGraphicsPolygonItem *triangle = new QGraphicsPolygonItem(); + triangle->setPolygon(poly); + triangle->setBrush(QBrush(profile_color[ALERT_BG].first())); + triangle->setPen(QPen(QBrush(profile_color[ALERT_FG].first()), 1)); + triangle->setFlag(QGraphicsItem::ItemIgnoresTransformations); + triangle->setPos(x, y); + + QGraphicsLineItem *line = new QGraphicsLineItem(0,5,0,10, triangle); + line->setPen(QPen(QBrush(Qt::black), 2)); + + QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(-1, 12, 2,2, triangle); + ball->setBrush(QBrush(Qt::black)); + + scene()->addItem(triangle); + + /* we display the event on screen - so translate */ + QString name = tr(ev->name); + if (ev->value) { + if (ev->name && name == "gaschange") { + unsigned int he = ev->value >> 16; + unsigned int o2 = ev->value & 0xffff; + if (he) { + name += QString("%1/%2").arg(o2, he); + } else { + if (o2 == 21) + name += tr(":air"); + else + name += QString("%1 %% %2").arg(o2).arg("O" UTF8_SUBSCRIPT_2); + } + } else if (ev->name && !strcmp(ev->name, "SP change")) { + name += QString(":%1").arg( (double) ev->value / 1000 ); + } else { + name += QString(":%1").arg(ev->value); + } + } else if (ev->name && name == "SP change") { + name += tr("Bailing out to OC"); + } else { + //name += ev->flags == SAMPLE_FLAGS_BEGIN ? C_("Starts with space!"," begin") : + // ev->flags == SAMPLE_FLAGS_END ? C_("Starts with space!", " end") : ""; + } + + //attach_tooltip(x-6, y, 12, 12, buffer, ev); +} void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) { diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 0b472f8b5..928c8ea89 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -20,6 +20,8 @@ protected: private: void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi); void plot_text(struct graphics_context *gc, text_render_options_t *tro, double x, double y, const QString &text); + void plot_events(struct graphics_context *gc, struct plot_info *pi, struct divecomputer *dc); + void plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *event); QPen defaultPen; QBrush defaultBrush; From 1a8239a240fe14e983da6d9a6048e2fb154ee053 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 6 May 2013 19:42:39 -0300 Subject: [PATCH 148/226] Finish the plotting of the events Beautification of the triangles done, Tooltips are also displaying Some rework on the code - don't know if dirk will accept, I'v changed an if-else-if-else by a ternary operator, since it improves legibility a little bit. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index b3163add4..9f1f115cb 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -13,6 +13,9 @@ #include "../dive.h" #include "../profile.h" +#include +#include + #define SAC_COLORS_START_IDX SAC_1 #define SAC_COLORS 9 #define VELOCITY_COLORS_START_IDX VELO_STABLE @@ -121,6 +124,10 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent setRenderHint(QPainter::HighQualityAntialiasing); setRenderHint(QPainter::SmoothPixmapTransform); + defaultPen.setJoinStyle(Qt::RoundJoin); + defaultPen.setCapStyle(Qt::RoundCap); + defaultPen.setWidth(2); + fill_profile_color(); } @@ -314,15 +321,18 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo int x = SCALEX(gc, ev->time.seconds); int y = SCALEY(gc, depth); + QPen pen = defaultPen; + pen.setBrush(QBrush(profile_color[ALERT_BG].first())); + QGraphicsPolygonItem *triangle = new QGraphicsPolygonItem(); triangle->setPolygon(poly); triangle->setBrush(QBrush(profile_color[ALERT_BG].first())); - triangle->setPen(QPen(QBrush(profile_color[ALERT_FG].first()), 1)); + triangle->setPen(pen); triangle->setFlag(QGraphicsItem::ItemIgnoresTransformations); triangle->setPos(x, y); QGraphicsLineItem *line = new QGraphicsLineItem(0,5,0,10, triangle); - line->setPen(QPen(QBrush(Qt::black), 2)); + line->setPen(defaultPen); QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(-1, 12, 2,2, triangle); ball->setBrush(QBrush(Qt::black)); @@ -335,14 +345,11 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo if (ev->name && name == "gaschange") { unsigned int he = ev->value >> 16; unsigned int o2 = ev->value & 0xffff; - if (he) { - name += QString("%1/%2").arg(o2, he); - } else { - if (o2 == 21) - name += tr(":air"); - else - name += QString("%1 %% %2").arg(o2).arg("O" UTF8_SUBSCRIPT_2); - } + + name += (he) ? QString("%1/%2").arg(o2, he) + : (o2 == 21) ? name += tr(":air") + : QString("%1 %% %2").arg(o2).arg("O" UTF8_SUBSCRIPT_2); + } else if (ev->name && !strcmp(ev->name, "SP change")) { name += QString(":%1").arg( (double) ev->value / 1000 ); } else { @@ -351,11 +358,12 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo } else if (ev->name && name == "SP change") { name += tr("Bailing out to OC"); } else { - //name += ev->flags == SAMPLE_FLAGS_BEGIN ? C_("Starts with space!"," begin") : - // ev->flags == SAMPLE_FLAGS_END ? C_("Starts with space!", " end") : ""; + name += ev->flags == SAMPLE_FLAGS_BEGIN ? tr("Starts with space!"," begin") : + ev->flags == SAMPLE_FLAGS_END ? tr("Starts with space!", " end") : ""; } //attach_tooltip(x-6, y, 12, 12, buffer, ev); + triangle->setToolTip(name); } void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) @@ -595,7 +603,6 @@ void ProfileGraphicsView::plot_text(struct graphics_context *gc, text_render_opt item->setPen( QPen(profile_color[BACKGROUND].first())); item->setFlag(QGraphicsItem::ItemIgnoresTransformations); scene()->addItem(item); - qDebug() << item->pos(); } From b75a89aa868d39be29d2bb220a6e0c50d5a2b0ac Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 6 May 2013 20:36:37 -0700 Subject: [PATCH 149/226] Start populating the maintab Dive Info widget Establish some useful helpers and use them when updating the values. One of the helpers (from statistics.c) puzzlingly doesn't link - so that's ifdefed out. Also had to re-arrange the settings reading code (it came too late) and to extract the expanding code of the top dive from the settings reading code (as it had no business being there to begin with). Signed-off-by: Dirk Hohndel --- Makefile | 1 + qt-gui.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++ qt-ui/maintab.cpp | 23 ++++++++++++++++++- qt-ui/mainwindow.cpp | 14 +++++------- qt-ui/models.cpp | 1 + statistics.c | 15 ++++++++++++ statistics.h | 1 + 7 files changed, 100 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 338f81a7c..add90b308 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,7 @@ SOURCES = \ profile.c \ save-xml.c \ sha1.c \ + statistics.c \ time.c \ qt-gui.cpp \ qt-ui/addcylinderdialog.cpp \ diff --git a/qt-gui.cpp b/qt-gui.cpp index a4801f760..1e2c86e69 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -133,5 +133,59 @@ void set_dc_nickname(struct dive *dive) /* needs Qt implementation */ } +QString get_depth_string(depth_t depth, bool showunit) +{ + if (prefs.units.length == units::METERS) { + double meters = depth.mm / 1000.0; + return QString("%1%2").arg(meters, 0, 'f', meters >= 20.0 ? 0 : 1 ).arg(showunit ? _("m") : ""); + } else { + double feet = mm_to_feet(depth.mm); + return QString("%1%2").arg(feet, 0, 'f', 1). arg(showunit ? _("ft") : ""); + } +} + +QString get_weight_string(weight_t weight, bool showunit) +{ + if (prefs.units.weight == units::KG) { + double kg = weight.grams / 1000.0; + return QString("%1%2").arg(kg, 0, 'f', kg >= 20.0 ? 0 : 1 ).arg(showunit ? _("kg") : ""); + } else { + double lbs = grams_to_lbs(weight.grams); + return QString("%1%2").arg(lbs, 0, 'f', lbs >= 40.0 ? 0 : 1 ).arg(showunit ? _("lbs") : ""); + } +} + +QString get_temperature_string(temperature_t temp, bool showunit) +{ + if (prefs.units.temperature == units::CELSIUS) { + double celsius = mkelvin_to_C(temp.mkelvin); + return QString("%1%2").arg(celsius, 0, 'f', 1).arg(showunit ? _("C") : ""); + } else { + double fahrenheit = mkelvin_to_F(temp.mkelvin); + return QString("%1%2").arg(fahrenheit, 0, 'f', 1).arg(showunit ? _("F") : ""); + } +} + +QString get_volume_string(volume_t volume, bool showunit) +{ + if (prefs.units.volume == units::LITER) { + double liter = volume.mliter / 1000.0; + return QString("%1%2").arg(liter, 0, 'f', liter >= 40.0 ? 0 : 1 ).arg(showunit ? _("l") : ""); + } else { + double cuft = ml_to_cuft(volume.mliter); + return QString("%1%2").arg(cuft, 0, 'f', cuft >= 20.0 ? 0 : (cuft >= 2.0 ? 1 : 2)).arg(showunit ? _("cuft") : ""); + } +} + +QString get_pressure_string(pressure_t pressure, bool showunit) +{ + if (prefs.units.pressure == units::BAR) { + double bar = pressure.mbar / 1000.0; + return QString("%1%2").arg(bar, 0, 'f', 1).arg(showunit ? _("bar") : ""); + } else { + double psi = mbar_to_PSI(pressure.mbar); + return QString("%1%2").arg(psi, 0, 'f', 0).arg(showunit ? _("psi") : ""); + } +} #include "qt-gui.moc" diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index df6ee69eb..5f668b2be 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -8,6 +8,8 @@ #include "ui_maintab.h" #include "addcylinderdialog.h" #include "addweightsystemdialog.h" +#include "../helpers.h" +#include "../statistics.h" #include @@ -66,6 +68,7 @@ void MainTab::clearStats() else \ ui->field->setText(d->field) + void MainTab::updateDiveInfo(int dive) { // So, this is what happens now: @@ -77,7 +80,7 @@ void MainTab::updateDiveInfo(int dive) // open the file maintab.ui on the designer // click on the item and check its objectName, // the access is ui->objectName from here on. - + volume_t sacVal; struct dive *d = get_dive(dive); UPDATE_TEXT(d, notes); UPDATE_TEXT(d, location); @@ -88,6 +91,24 @@ void MainTab::updateDiveInfo(int dive) ui->rating->setCurrentStars(d->rating); else ui->rating->setCurrentStars(0); + ui->maximumDepthText->setText(get_depth_string(d->maxdepth, TRUE)); + ui->averageDepthText->setText(get_depth_string(d->meandepth, TRUE)); + sacVal.mliter = d ? d->sac : 0; + ui->sacText->setText(get_volume_string(sacVal, TRUE).append("/min")); + ui->otuText->setText(QString("%1").arg( d ? d->otu : 0)); + ui->waterTemperatureText->setText(d ? get_temperature_string(d->watertemp, TRUE) : ""); + ui->airTemperatureText->setText(d ? get_temperature_string(d->airtemp, TRUE) : ""); + if (d && d->surface_pressure.mbar) + /* this is ALWAYS displayed in mbar */ + ui->airPressureText->setText(QString("%1mbar").arg(d->surface_pressure.mbar)); + else + ui->airPressureText->setText(QString("")); +#if 0 /* this fails to link, even though the function is defined in statistics.c / statistics.h */ + if (d) + ui->gasUsedText->setText(get_volume_string(get_gas_used(d), TRUE)); + else +#endif + ui->gasUsedText->setText(""); } void MainTab::on_addCylinder_clicked() diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index e8fe80460..56ae64b0c 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -32,14 +32,17 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), sortModel(new QSortFilterProxyModel()) { ui->setupUi(this); + readSettings(); sortModel->setSourceModel(model); ui->ListWidget->setModel(sortModel); setWindowIcon(QIcon(":subsurface-icon")); - connect(ui->ListWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(dive_selection_changed(QItemSelection,QItemSelection))); - - readSettings(); + QModelIndex firstDiveOrTrip = sortModel->index(0,0); + if (sortModel->index(0,0, firstDiveOrTrip).isValid()) + ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); + else + ui->ListWidget->setCurrentIndex(firstDiveOrTrip); } void MainWindow::on_actionNew_triggered() @@ -343,11 +346,6 @@ void MainWindow::readSettings() } ui->ListWidget->collapseAll(); ui->ListWidget->expand(sortModel->index(0,0)); - QModelIndex firstDiveOrTrip = sortModel->index(0,0); - if (sortModel->index(0,0, firstDiveOrTrip).isValid()) - ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); - else - ui->ListWidget->setCurrentIndex(firstDiveOrTrip); settings.endGroup(); settings.beginGroup("Units"); GET_UNIT(v, "feet", length, units::METERS, units::FEET); diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index eb4d8974b..9a6edce98 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -677,6 +677,7 @@ void DiveTripModel::setupModelData() while (--i >= 0) { struct dive* dive = get_dive(i); + update_cylinder_related_info(dive); dive_trip_t* trip = dive->divetrip; DiveItem* diveItem = new DiveItem(); diff --git a/statistics.c b/statistics.c index 7532e346e..bee5837d6 100644 --- a/statistics.c +++ b/statistics.c @@ -267,3 +267,18 @@ void get_selected_dives_text(char *buffer, int size) } } +volume_t get_gas_used(struct dive *dive) +{ + int idx; + volume_t gas_used = { 0 }; + for (idx = 0; idx < MAX_CYLINDERS; idx++) { + cylinder_t *cyl = &dive->cylinder[idx]; + pressure_t start, end; + + start = cyl->start.mbar ? cyl->start : cyl->sample_start; + end = cyl->end.mbar ?cyl->sample_end : cyl->sample_end; + if (start.mbar && end.mbar) + gas_used.mliter += gas_volume(cyl, start) - gas_volume(cyl, end); + } + return gas_used; +} diff --git a/statistics.h b/statistics.h index d2709ee93..95f2957e8 100644 --- a/statistics.h +++ b/statistics.h @@ -31,3 +31,4 @@ extern char *get_time_string(int seconds, int maxdays); extern char *get_minutes(int seconds); extern void process_all_dives(struct dive *dive, struct dive **prev_dive); extern void get_selected_dives_text(char *buffer, int size); +extern volume_t get_gas_used(struct dive *dive); From 1240455a9a1f23284af2b8859b2ca2d1f363e0c6 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 6 May 2013 20:50:30 -0700 Subject: [PATCH 150/226] Missing helpers.h file Signed-off-by: Dirk Hohndel --- helpers.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 helpers.h diff --git a/helpers.h b/helpers.h new file mode 100644 index 000000000..8e72d6a20 --- /dev/null +++ b/helpers.h @@ -0,0 +1,19 @@ +/* + * helpers.h + * + * header file for random helper functions of Subsurface + * + */ +#ifndef HELPER_H +#define HELPER_H + +#include +#include "dive.h" + +QString get_depth_string(depth_t depth, bool showunit); +QString get_weight_string(weight_t weight, bool showunit); +QString get_temperature_string(temperature_t temp, bool showunit); +QString get_volume_string(volume_t volume, bool showunit); +QString get_pressure_string(pressure_t pressure, bool showunit); + +#endif /* HELPER_H */ From 265376db067156356d208e1d36b420199a162c54 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 6 May 2013 20:55:27 -0700 Subject: [PATCH 151/226] Add #include guards to statistics.h and the extern "C" for C++ If the extern "C" is missing, the C++ compiler will try to find a function by its mangled name. Since the function is in a .c file, there will be no mangled name. Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- statistics.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/statistics.h b/statistics.h index 95f2957e8..732a287e1 100644 --- a/statistics.h +++ b/statistics.h @@ -4,6 +4,14 @@ * core logic functions called from statistics UI * common types and variables */ + +#ifndef STATISTICS_H +#define STATISTICS_H + +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { int period; duration_t total_time; @@ -32,3 +40,9 @@ extern char *get_minutes(int seconds); extern void process_all_dives(struct dive *dive, struct dive **prev_dive); extern void get_selected_dives_text(char *buffer, int size); extern volume_t get_gas_used(struct dive *dive); + +#ifdef __cplusplus +} +#endif + +#endif From 5e4f06e6ad2da553ac7b47bf6837d8053b888f6f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 6 May 2013 20:55:28 -0700 Subject: [PATCH 152/226] Enable the code that was #if 0'ed out Linking error is fixed. Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 5f668b2be..89501fc07 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -103,11 +103,9 @@ void MainTab::updateDiveInfo(int dive) ui->airPressureText->setText(QString("%1mbar").arg(d->surface_pressure.mbar)); else ui->airPressureText->setText(QString("")); -#if 0 /* this fails to link, even though the function is defined in statistics.c / statistics.h */ if (d) ui->gasUsedText->setText(get_volume_string(get_gas_used(d), TRUE)); else -#endif ui->gasUsedText->setText(""); } From c928b9cb94b7e43983aaee46c82074466a6179a6 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Tue, 7 May 2013 15:44:54 -0300 Subject: [PATCH 153/226] Added the first overlay of the tooltips, with some test data. The tooltips now can: 1 - be moved around the canvas 2 - dynamically expand / retreat when a new tooltip is added. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 134 +++++++++++++++++++++++++++++++++++++- qt-ui/profilegraphics.h | 44 +++++++++++++ 2 files changed, 175 insertions(+), 3 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 9f1f115cb..eede292d7 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include "../color.h" #include "../display.h" @@ -146,13 +149,21 @@ static void plot_set_scale(scale_mode_t scale) void ProfileGraphicsView::plot(struct dive *dive) { - // Clear the items before drawing this dive. - qDeleteAll(scene()->items()); scene()->clear(); if(!dive) return; + QSettings s; + s.beginGroup("ProfileMap"); + QPointF toolTipPos = s.value("tooltip_position", QPointF(0,0)).toPointF(); + s.endGroup(); + + toolTip = new ToolTipItem(); + toolTip->setPos(toolTipPos); + + scene()->addItem(toolTip); + struct plot_info *pi; struct divecomputer *dc = &dive->dc; @@ -363,7 +374,8 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo } //attach_tooltip(x-6, y, 12, 12, buffer, ev); - triangle->setToolTip(name); + //triangle->setToolTip(name); + toolTip->addToolTip(name); } void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) @@ -605,6 +617,16 @@ void ProfileGraphicsView::plot_text(struct graphics_context *gc, text_render_opt scene()->addItem(item); } +void ProfileGraphicsView::addToolTip(const QString& text, const QIcon& icon) +{ + toolTip->addToolTip(text, icon); +} + +void ProfileGraphicsView::removeToolTip(const QString& text) +{ + toolTip->removeToolTip(text); +} + void ProfileGraphicsView::resizeEvent(QResizeEvent *event) { @@ -614,3 +636,109 @@ void ProfileGraphicsView::resizeEvent(QResizeEvent *event) QRectF r = scene()->sceneRect(); fitInView ( r.x() - 50, r.y() -50, r.width() + 100, r.height() + 100); // do a little bit of spacing; } + +void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) +{ + qDebug() << "Tooltip Adicionado" << toolTip; + + QGraphicsPixmapItem *iconItem = 0; + if (!icon.isNull()) { + iconItem = new QGraphicsPixmapItem(icon.pixmap(ICON_SMALL,ICON_SMALL), this); + iconItem->setPos( 4, toolTips.keys().size() * ICON_SMALL + 4); + } + + QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(toolTip, this); + textItem->setPos( 4 + ICON_SMALL + 4, toolTips.keys().size() * ICON_SMALL + 4); + textItem->setPen(QPen(Qt::white, 1)); + textItem->setBrush(QBrush(Qt::white)); + textItem->setFlag(ItemIgnoresTransformations); + + toolTips[toolTip] = qMakePair(iconItem, textItem); + expand(); +} + +void ToolTipItem::removeToolTip(const QString& toolTip) +{ + ToolTip toBeRemoved = toolTips[toolTip]; + delete toBeRemoved.first; + delete toBeRemoved.second; + expand(); +} + +void ToolTipItem::clear() +{ + Q_FOREACH(ToolTip t, toolTips) { + delete t.first; + delete t.second; + } + toolTips.clear(); + expand(); +} + +void ToolTipItem::setRect(const QRectF& r) +{ + + // qDeleteAll(childItems()); + if (background) { + childItems().removeAt(childItems().indexOf(background)); + delete background; + } + + rectangle = r; + setBrush(QBrush(Qt::white)); + setPen(QPen(Qt::black, 0.5)); + + QPainterPath border; + border.addRoundedRect(-2, -2, rectangle.width() + 4, rectangle.height()+ 4, 3, 3); + border.addRoundedRect( 0, 0, rectangle.width(), rectangle.height(), 3, 3); + setPath(border); + + QGraphicsRectItem *b = new QGraphicsRectItem(-1, -1, rectangle.width()+1, rectangle.height()+1, this); + b->setFlag(ItemStacksBehindParent); + QColor c = QColor(Qt::black); + c.setAlpha(155); + b->setBrush(c); + background = b; + +} + + +void ToolTipItem::collapse() +{ + QRectF newRect = childrenBoundingRect(); + QPropertyAnimation *animation = new QPropertyAnimation(this, "rect"); + animation->setDuration(100); + animation->setStartValue(boundingRect()); + animation->setEndValue(QRect(0, 0, ICON_SMALL, ICON_SMALL )); + animation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ToolTipItem::expand() +{ + QRectF newRect = childrenBoundingRect(); + newRect = QRect(0, 0, newRect.width() + 8, newRect.height() + 8); + if (newRect != boundingRect()) { + QRectF newRect = childrenBoundingRect(); + QPropertyAnimation *animation = new QPropertyAnimation(this, "rect"); + animation->setDuration(100); + animation->setStartValue(boundingRect()); + animation->setEndValue(newRect); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } +} + +ToolTipItem::ToolTipItem(QGraphicsItem* parent): QGraphicsPathItem(parent), background(0) +{ + setRect(QRectF(0,0,ICON_SMALL, ICON_SMALL)); + setFlag(QGraphicsItem::ItemIgnoresTransformations); + setFlag(QGraphicsItem::ItemIsMovable); +} + +void ToolTipStatusHandler::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + QGraphicsItem::mousePressEvent(event); +} + +ToolTipStatusHandler::ToolTipStatusHandler(QObject* parent): QObject(parent), QGraphicsEllipseItem() +{ +} diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 928c8ea89..9ad04674b 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -2,17 +2,59 @@ #define PROFILEGRAPHICS_H #include +#include +#include struct text_render_options; struct graphics_context; struct plot_info; typedef struct text_render_options text_render_options_t; + +class ToolTipItem; +class ToolTipStatusHandler; + +class ToolTipStatusHandler :public QObject, public QGraphicsEllipseItem { +public: + explicit ToolTipStatusHandler(QObject* parent = 0); +protected: + void mousePressEvent(QGraphicsSceneMouseEvent* event); +}; + +class ToolTipItem :public QObject, public QGraphicsPathItem { + Q_OBJECT + Q_PROPERTY(QRectF rect READ boundingRect WRITE setRect) + +public: + enum Status {COLLAPSED, EXPANDED}; + enum {ICON_SMALL = 16, ICON_MEDIUM = 24, ICON_BIG = 32}; + + explicit ToolTipItem(QGraphicsItem* parent = 0); + + void collapse(); + void expand(); + void clear(); + void addToolTip(const QString& toolTip, const QIcon& icon = QIcon()); + void removeToolTip(const QString& toolTip); + +public Q_SLOTS: + void setRect(const QRectF& rect); + +private: + typedef QPair ToolTip; + enum Status status; + QMap toolTips; + QGraphicsRectItem *background; + QRectF rectangle; +}; + class ProfileGraphicsView : public QGraphicsView { Q_OBJECT public: ProfileGraphicsView(QWidget* parent = 0); void plot(struct dive *d); + void addToolTip(const QString& text, const QIcon& icon = QIcon()); + void removeToolTip(const QString& text); protected: void resizeEvent(QResizeEvent *event); @@ -25,6 +67,8 @@ private: QPen defaultPen; QBrush defaultBrush; + ToolTipItem *toolTip; }; + #endif From d590cb951988976f21bacc39d5ea1581c65d3b20 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 7 May 2013 16:09:51 -0700 Subject: [PATCH 154/226] Adds real support for ToolTips. This patch changes the Event drawing so it can display tooltips. It is now the responsibility of the item to show / hide a tooltip. A bit of code-refactoring got on here too because I was using only QGraphicsItem calls and I wanted to use a hover in / hover out event to show / hide the tooltip. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 181 +++++++++++++++++++++++++++++--------- qt-ui/profilegraphics.h | 39 ++++---- 2 files changed, 160 insertions(+), 60 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index eede292d7..81d343976 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "../color.h" #include "../display.h" @@ -323,32 +324,13 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo } /* draw a little triangular marker and attach tooltip */ - QPolygonF poly; - poly.push_back(QPointF(-8, 16)); - poly.push_back(QPointF(8, 16)); - poly.push_back(QPointF(0, 0)); - poly.push_back(QPointF(-8, 16)); int x = SCALEX(gc, ev->time.seconds); int y = SCALEY(gc, depth); - QPen pen = defaultPen; - pen.setBrush(QBrush(profile_color[ALERT_BG].first())); - - QGraphicsPolygonItem *triangle = new QGraphicsPolygonItem(); - triangle->setPolygon(poly); - triangle->setBrush(QBrush(profile_color[ALERT_BG].first())); - triangle->setPen(pen); - triangle->setFlag(QGraphicsItem::ItemIgnoresTransformations); - triangle->setPos(x, y); - - QGraphicsLineItem *line = new QGraphicsLineItem(0,5,0,10, triangle); - line->setPen(defaultPen); - - QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(-1, 12, 2,2, triangle); - ball->setBrush(QBrush(Qt::black)); - - scene()->addItem(triangle); + EventItem *item = new EventItem(); + item->setPos(x, y); + scene()->addItem(item); /* we display the event on screen - so translate */ QString name = tr(ev->name); @@ -373,9 +355,8 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo ev->flags == SAMPLE_FLAGS_END ? tr("Starts with space!", " end") : ""; } - //attach_tooltip(x-6, y, 12, 12, buffer, ev); - //triangle->setToolTip(name); - toolTip->addToolTip(name); + item->setToolTipController(toolTip); + item->addToolTip(name); } void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) @@ -639,16 +620,16 @@ void ProfileGraphicsView::resizeEvent(QResizeEvent *event) void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) { - qDebug() << "Tooltip Adicionado" << toolTip; - QGraphicsPixmapItem *iconItem = 0; + double yValue = title->boundingRect().height() + SPACING + toolTips.keys().size() * ICON_SMALL + SPACING; + if (!icon.isNull()) { iconItem = new QGraphicsPixmapItem(icon.pixmap(ICON_SMALL,ICON_SMALL), this); - iconItem->setPos( 4, toolTips.keys().size() * ICON_SMALL + 4); + iconItem->setPos( SPACING, yValue); } QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(toolTip, this); - textItem->setPos( 4 + ICON_SMALL + 4, toolTips.keys().size() * ICON_SMALL + 4); + textItem->setPos( SPACING + ICON_SMALL + SPACING, yValue); textItem->setPen(QPen(Qt::white, 1)); textItem->setBrush(QBrush(Qt::white)); textItem->setFlag(ItemIgnoresTransformations); @@ -662,6 +643,22 @@ void ToolTipItem::removeToolTip(const QString& toolTip) ToolTip toBeRemoved = toolTips[toolTip]; delete toBeRemoved.first; delete toBeRemoved.second; + toolTips.remove(toolTip); + + int toolTipIndex = 0; + + // We removed a toolTip, let's move the others to the correct location + Q_FOREACH(ToolTip t, toolTips){ + double yValue = title->boundingRect().height() + SPACING + toolTipIndex * ICON_SMALL + SPACING; + + // Icons can be null. + if (t.first) + t.first->setPos(SPACING, yValue); + + t.second->setPos(SPACING + ICON_SMALL + SPACING, yValue); + toolTipIndex++; + } + expand(); } @@ -700,6 +697,7 @@ void ToolTipItem::setRect(const QRectF& r) b->setBrush(c); background = b; + updateTitlePosition(); } @@ -715,30 +713,125 @@ void ToolTipItem::collapse() void ToolTipItem::expand() { - QRectF newRect = childrenBoundingRect(); - newRect = QRect(0, 0, newRect.width() + 8, newRect.height() + 8); - if (newRect != boundingRect()) { - QRectF newRect = childrenBoundingRect(); - QPropertyAnimation *animation = new QPropertyAnimation(this, "rect"); - animation->setDuration(100); - animation->setStartValue(boundingRect()); - animation->setEndValue(newRect); - animation->start(QAbstractAnimation::DeleteWhenStopped); + QRectF currentRect = rectangle; + QRectF nextRectangle; + + double width = 0; + Q_FOREACH(ToolTip t, toolTips) { + if (t.second->boundingRect().width() > width) + width = t.second->boundingRect().width(); } + + double height = toolTips.count() * 18 + title->boundingRect().height() + SPACING; + /* Left padding, Icon Size, space, right padding */ + width += SPACING + ICON_SMALL + SPACING + SPACING; + + if (width < title->boundingRect().width() + SPACING*2) + width = title->boundingRect().width() + SPACING*2; + + if( height < ICON_SMALL) + height = ICON_SMALL; + + nextRectangle.setWidth(width); + nextRectangle.setHeight(height); + + QPropertyAnimation *animation = new QPropertyAnimation(this, "rect"); + animation->setDuration(100); + animation->setStartValue(rectangle); + animation->setEndValue(nextRectangle); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } ToolTipItem::ToolTipItem(QGraphicsItem* parent): QGraphicsPathItem(parent), background(0) { - setRect(QRectF(0,0,ICON_SMALL, ICON_SMALL)); - setFlag(QGraphicsItem::ItemIgnoresTransformations); - setFlag(QGraphicsItem::ItemIsMovable); + title = new QGraphicsSimpleTextItem(tr("Information"), this); + separator = new QGraphicsLineItem(this); + + setFlag(ItemIgnoresTransformations); + setFlag(ItemIsMovable); + + updateTitlePosition(); + setZValue(99); } -void ToolTipStatusHandler::mousePressEvent(QGraphicsSceneMouseEvent* event) +void ToolTipItem::updateTitlePosition() { - QGraphicsItem::mousePressEvent(event); + if (rectangle.width() < title->boundingRect().width() + SPACING*4 ){ + QRectF newRect = rectangle; + newRect.setWidth(title->boundingRect().width() + SPACING*4); + newRect.setHeight( newRect.height() ? newRect.height() : ICON_SMALL ); + setRect(newRect); + } + + title->setPos(boundingRect().width()/2 -title->boundingRect().width()/2, 0); + title->setFlag(ItemIgnoresTransformations); + title->setPen(QPen(Qt::white, 1)); + title->setBrush(Qt::white); + + if (toolTips.size() > 0){ + double x1 = 0; + double y1 = title->pos().y() + SPACING/2 + title->boundingRect().height(); + double x2 = boundingRect().width() - 4; + double y2 = y1; + + separator->setLine(x1, y1, x2, y2); + separator->setFlag(ItemIgnoresTransformations); + separator->setPen(QPen(Qt::white)); + }else{ + separator->setLine(QLineF()); + } } -ToolTipStatusHandler::ToolTipStatusHandler(QObject* parent): QObject(parent), QGraphicsEllipseItem() +EventItem::EventItem(QGraphicsItem* parent): QGraphicsPolygonItem(parent) { + setFlag(ItemIgnoresTransformations); + setFlag(ItemIsFocusable); + setAcceptHoverEvents(true); + + QPolygonF poly; + poly.push_back(QPointF(-8, 16)); + poly.push_back(QPointF(8, 16)); + poly.push_back(QPointF(0, 0)); + poly.push_back(QPointF(-8, 16)); + + QPen defaultPen ; + defaultPen.setJoinStyle(Qt::RoundJoin); + defaultPen.setCapStyle(Qt::RoundCap); + defaultPen.setWidth(2); + + QPen pen = defaultPen; + pen.setBrush(QBrush(profile_color[ALERT_BG].first())); + + setPolygon(poly); + setBrush(QBrush(profile_color[ALERT_BG].first())); + setPen(pen); + + QGraphicsLineItem *line = new QGraphicsLineItem(0,5,0,10, this); + line->setPen(QPen(Qt::black, 2)); + + QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(-1, 12, 2,2, this); + ball->setBrush(QBrush(Qt::black)); + +} + +void EventItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event) +{ + controller->addToolTip(text, icon); +} + +void EventItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + controller->removeToolTip(text); +} + +void EventItem::addToolTip(const QString& t, const QIcon& i) +{ + text = t; + icon = i; +} + +void EventItem::setToolTipController(ToolTipItem* c) +{ + controller = c; } diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 9ad04674b..776b3141e 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -10,26 +10,16 @@ struct graphics_context; struct plot_info; typedef struct text_render_options text_render_options_t; - -class ToolTipItem; -class ToolTipStatusHandler; - -class ToolTipStatusHandler :public QObject, public QGraphicsEllipseItem { -public: - explicit ToolTipStatusHandler(QObject* parent = 0); -protected: - void mousePressEvent(QGraphicsSceneMouseEvent* event); -}; - class ToolTipItem :public QObject, public QGraphicsPathItem { Q_OBJECT + void updateTitlePosition(); Q_PROPERTY(QRectF rect READ boundingRect WRITE setRect) public: - enum Status {COLLAPSED, EXPANDED}; - enum {ICON_SMALL = 16, ICON_MEDIUM = 24, ICON_BIG = 32}; + enum Status{COLLAPSED, EXPANDED}; + enum {ICON_SMALL = 16, ICON_MEDIUM = 24, ICON_BIG = 32, SPACING=4}; - explicit ToolTipItem(QGraphicsItem* parent = 0); + explicit ToolTipItem(QGraphicsItem* parent = 0); void collapse(); void expand(); @@ -42,12 +32,30 @@ public Q_SLOTS: private: typedef QPair ToolTip; - enum Status status; QMap toolTips; QGraphicsRectItem *background; + QGraphicsLineItem *separator; + QGraphicsSimpleTextItem *title; + QRectF rectangle; }; +class EventItem : public QGraphicsPolygonItem{ +public: + explicit EventItem(QGraphicsItem* parent = 0); + void addToolTip(const QString& text,const QIcon& icon = QIcon()); + void setToolTipController(ToolTipItem *controller); + +protected: + void hoverEnterEvent(QGraphicsSceneHoverEvent* event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); + +private: + ToolTipItem *controller; + QString text; + QIcon icon; +}; + class ProfileGraphicsView : public QGraphicsView { Q_OBJECT public: @@ -70,5 +78,4 @@ private: ToolTipItem *toolTip; }; - #endif From df01a5d35f620d2f33e4283863b729ed9554e23f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 7 May 2013 16:12:01 -0700 Subject: [PATCH 155/226] Added a bit of documentation on how to use the ToolTips & removed 2 bad-api methods Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 11 ----------- qt-ui/profilegraphics.h | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 81d343976..a9a7c93f8 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -598,17 +598,6 @@ void ProfileGraphicsView::plot_text(struct graphics_context *gc, text_render_opt scene()->addItem(item); } -void ProfileGraphicsView::addToolTip(const QString& text, const QIcon& icon) -{ - toolTip->addToolTip(text, icon); -} - -void ProfileGraphicsView::removeToolTip(const QString& text) -{ - toolTip->removeToolTip(text); -} - - void ProfileGraphicsView::resizeEvent(QResizeEvent *event) { // Fits the scene's rectangle on the view. diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 776b3141e..c1668692d 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -10,7 +10,33 @@ struct graphics_context; struct plot_info; typedef struct text_render_options text_render_options_t; -class ToolTipItem :public QObject, public QGraphicsPathItem { +/**! + * + * Hookay, so, if you wanna extend the ToolTips that are displayed + * in the Profile Graph, there's one 'toolTip' widget already on it, + * you can just pass it to your Reimplementation of QGraphiscItem + * and do the following: + * + * EventItem::setController(ToolTipItem *c) + * { + * controller = c; + * } + * + * void EventItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event) + * { + * controller->addToolTip(text, icon); + * } + * + * void EventItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) + * { + * controller->removeToolTip(text); + * } + * + * Remember to removeToolTip when you don't want it to be displayed. + * + **/ +class ToolTipItem :public QObject, public QGraphicsPathItem +{ Q_OBJECT void updateTitlePosition(); Q_PROPERTY(QRectF rect READ boundingRect WRITE setRect) @@ -40,7 +66,8 @@ private: QRectF rectangle; }; -class EventItem : public QGraphicsPolygonItem{ +class EventItem : public QGraphicsPolygonItem +{ public: explicit EventItem(QGraphicsItem* parent = 0); void addToolTip(const QString& text,const QIcon& icon = QIcon()); @@ -56,13 +83,12 @@ private: QIcon icon; }; -class ProfileGraphicsView : public QGraphicsView { +class ProfileGraphicsView : public QGraphicsView +{ Q_OBJECT public: ProfileGraphicsView(QWidget* parent = 0); void plot(struct dive *d); - void addToolTip(const QString& text, const QIcon& icon = QIcon()); - void removeToolTip(const QString& text); protected: void resizeEvent(QResizeEvent *event); From 661aa67e89ad7710f90482b074715c4d1448860a Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 8 May 2013 10:51:15 -0700 Subject: [PATCH 156/226] Avoid potential crash if no dive is selected I missed to spots where we would unconditionally dereference the dive pointer. Reported-by: Henrik Brautaset Aronsen Reported-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 89501fc07..078d59ca9 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -91,8 +91,8 @@ void MainTab::updateDiveInfo(int dive) ui->rating->setCurrentStars(d->rating); else ui->rating->setCurrentStars(0); - ui->maximumDepthText->setText(get_depth_string(d->maxdepth, TRUE)); - ui->averageDepthText->setText(get_depth_string(d->meandepth, TRUE)); + ui->maximumDepthText->setText(d ? get_depth_string(d->maxdepth, TRUE) : ""); + ui->averageDepthText->setText(d ? get_depth_string(d->meandepth, TRUE) : ""); sacVal.mliter = d ? d->sac : 0; ui->sacText->setText(get_volume_string(sacVal, TRUE).append("/min")); ui->otuText->setText(QString("%1").arg( d ? d->otu : 0)); From b5d5b05140eac7a8031cdb15e7c31f659a6f2da6 Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Wed, 8 May 2013 12:08:00 -0700 Subject: [PATCH 157/226] Fix crash on right click dive trip A null pointer dereference occured after right click on a dive trip because updateDiveInfo was called with dive == -1 causing get_dive(int) to return null. Wrap to avoid crash and clear dive info widget text labels. [Dirk Hohndel: this is different from the fix I had committed earlier; I decided to combine the ideas, clean this one up a bit more and this is the result] Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 49 ++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 078d59ca9..4e7f6b3a1 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -87,26 +87,39 @@ void MainTab::updateDiveInfo(int dive) UPDATE_TEXT(d, suit); UPDATE_TEXT(d, divemaster); UPDATE_TEXT(d, buddy); - if (d) + if (d) { ui->rating->setCurrentStars(d->rating); - else - ui->rating->setCurrentStars(0); - ui->maximumDepthText->setText(d ? get_depth_string(d->maxdepth, TRUE) : ""); - ui->averageDepthText->setText(d ? get_depth_string(d->meandepth, TRUE) : ""); - sacVal.mliter = d ? d->sac : 0; - ui->sacText->setText(get_volume_string(sacVal, TRUE).append("/min")); - ui->otuText->setText(QString("%1").arg( d ? d->otu : 0)); - ui->waterTemperatureText->setText(d ? get_temperature_string(d->watertemp, TRUE) : ""); - ui->airTemperatureText->setText(d ? get_temperature_string(d->airtemp, TRUE) : ""); - if (d && d->surface_pressure.mbar) - /* this is ALWAYS displayed in mbar */ - ui->airPressureText->setText(QString("%1mbar").arg(d->surface_pressure.mbar)); - else - ui->airPressureText->setText(QString("")); - if (d) + ui->maximumDepthText->setText(get_depth_string(d->maxdepth, TRUE)); + ui->averageDepthText->setText(get_depth_string(d->meandepth, TRUE)); + ui->otuText->setText(QString("%1").arg(d->otu)); + ui->waterTemperatureText->setText(get_temperature_string(d->watertemp, TRUE)); + ui->airTemperatureText->setText(get_temperature_string(d->airtemp, TRUE)); ui->gasUsedText->setText(get_volume_string(get_gas_used(d), TRUE)); - else - ui->gasUsedText->setText(""); + if ((sacVal.mliter = d->sac) > 0) + ui->sacText->setText(get_volume_string(sacVal, TRUE).append("/min")); + else + ui->sacText->setText(QString()); + if (d->surface_pressure.mbar) + /* this is ALWAYS displayed in mbar */ + ui->airPressureText->setText(QString("%1mbar").arg(d->surface_pressure.mbar)); + else + ui->airPressureText->setText(QString()); + } else { + ui->rating->setCurrentStars(0); + ui->sacText->setText(QString()); + ui->otuText->setText(QString()); + ui->oxygenHeliumText->setText(QString()); + ui->dateText->setText(QString()); + ui->diveTimeText->setText(QString()); + ui->surfaceIntervalText->setText(QString()); + ui->maximumDepthText->setText(QString()); + ui->averageDepthText->setText(QString()); + ui->visibilityText->setText(QString()); + ui->waterTemperatureText->setText(QString()); + ui->airTemperatureText->setText(QString()); + ui->gasUsedText->setText(QString()); + ui->airPressureText->setText(QString()); + } } void MainTab::on_addCylinder_clicked() From 76e1436f684117766c5132c4ab69bf7ae50cd2ed Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Tue, 7 May 2013 23:00:05 -0300 Subject: [PATCH 158/226] Better design for the ToolTip Handler. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 28 +++++++++++++++++----------- qt-ui/profilegraphics.h | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index a9a7c93f8..e2fed39c5 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -665,25 +665,30 @@ void ToolTipItem::setRect(const QRectF& r) { // qDeleteAll(childItems()); - if (background) { - childItems().removeAt(childItems().indexOf(background)); - delete background; - } + delete background; rectangle = r; setBrush(QBrush(Qt::white)); setPen(QPen(Qt::black, 0.5)); + // Creates a 2pixels border QPainterPath border; - border.addRoundedRect(-2, -2, rectangle.width() + 4, rectangle.height()+ 4, 3, 3); - border.addRoundedRect( 0, 0, rectangle.width(), rectangle.height(), 3, 3); + border.addRoundedRect(-4, -4, rectangle.width() + 8, rectangle.height() + 10, 3, 3); + border.addRoundedRect(-1, -1, rectangle.width() + 3, rectangle.height() + 4, 3, 3); setPath(border); - QGraphicsRectItem *b = new QGraphicsRectItem(-1, -1, rectangle.width()+1, rectangle.height()+1, this); - b->setFlag(ItemStacksBehindParent); + QPainterPath bg; + bg.addRoundedRect( -1, -1, rectangle.width() + 3, rectangle.height() + 4, 3, 3); + QColor c = QColor(Qt::black); c.setAlpha(155); + + QGraphicsPathItem *b = new QGraphicsPathItem(bg, this); + b->setFlag(ItemStacksBehindParent); + b->setFlags(ItemIgnoresTransformations); b->setBrush(c); + b->setPen(QPen(QBrush(Qt::transparent), 0)); + b->setZValue(-10); background = b; updateTitlePosition(); @@ -753,7 +758,7 @@ void ToolTipItem::updateTitlePosition() setRect(newRect); } - title->setPos(boundingRect().width()/2 -title->boundingRect().width()/2, 0); + title->setPos(boundingRect().width()/2 - title->boundingRect().width()/2 -1, 0); title->setFlag(ItemIgnoresTransformations); title->setPen(QPen(Qt::white, 1)); title->setBrush(Qt::white); @@ -761,14 +766,15 @@ void ToolTipItem::updateTitlePosition() if (toolTips.size() > 0){ double x1 = 0; double y1 = title->pos().y() + SPACING/2 + title->boundingRect().height(); - double x2 = boundingRect().width() - 4; + double x2 = boundingRect().width() - 10; double y2 = y1; separator->setLine(x1, y1, x2, y2); separator->setFlag(ItemIgnoresTransformations); separator->setPen(QPen(Qt::white)); + separator->show(); }else{ - separator->setLine(QLineF()); + separator->hide(); } } diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index c1668692d..1c9238bee 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -59,7 +59,7 @@ public Q_SLOTS: private: typedef QPair ToolTip; QMap toolTips; - QGraphicsRectItem *background; + QGraphicsPathItem *background; QGraphicsLineItem *separator; QGraphicsSimpleTextItem *title; From 2089c124a582ac95c4e9adb9362446572ff791f7 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Wed, 8 May 2013 07:22:23 -0300 Subject: [PATCH 159/226] Work on the tooltips - WIP. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 37 ++++++++++++++----------------------- qt-ui/profilegraphics.h | 7 +------ 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index e2fed39c5..550a5ec57 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -123,6 +123,7 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent setScene(new QGraphicsScene()); setBackgroundBrush(QColor("#F3F3E6")); scene()->setSceneRect(0,0,1000,1000); + scene()->installEventFilter(this); setRenderHint(QPainter::Antialiasing); setRenderHint(QPainter::HighQualityAntialiasing); @@ -135,6 +136,16 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent fill_profile_color(); } +bool ProfileGraphicsView::eventFilter(QObject* obj, QEvent* event) +{ + // This will "Eat" the default tooltip behavior. + if (event->type() == QEvent::GraphicsSceneHelp){ + event->ignore(); + return true; + } + return QGraphicsView::eventFilter(obj, event); +} + static void plot_set_scale(scale_mode_t scale) { switch (scale) { @@ -355,8 +366,9 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo ev->flags == SAMPLE_FLAGS_END ? tr("Starts with space!", " end") : ""; } - item->setToolTipController(toolTip); - item->addToolTip(name); + //item->setToolTipController(toolTip); + //item->addToolTip(name); + item->setToolTip(name); } void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) @@ -809,24 +821,3 @@ EventItem::EventItem(QGraphicsItem* parent): QGraphicsPolygonItem(parent) ball->setBrush(QBrush(Qt::black)); } - -void EventItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event) -{ - controller->addToolTip(text, icon); -} - -void EventItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) -{ - controller->removeToolTip(text); -} - -void EventItem::addToolTip(const QString& t, const QIcon& i) -{ - text = t; - icon = i; -} - -void EventItem::setToolTipController(ToolTipItem* c) -{ - controller = c; -} diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 1c9238bee..ca1eb8983 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -70,12 +70,6 @@ class EventItem : public QGraphicsPolygonItem { public: explicit EventItem(QGraphicsItem* parent = 0); - void addToolTip(const QString& text,const QIcon& icon = QIcon()); - void setToolTipController(ToolTipItem *controller); - -protected: - void hoverEnterEvent(QGraphicsSceneHoverEvent* event); - void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); private: ToolTipItem *controller; @@ -89,6 +83,7 @@ Q_OBJECT public: ProfileGraphicsView(QWidget* parent = 0); void plot(struct dive *d); + bool eventFilter(QObject* obj, QEvent* event); protected: void resizeEvent(QResizeEvent *event); From ce8d30b938b80c334eb0869650c3c463084ae018 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Wed, 8 May 2013 16:21:49 -0300 Subject: [PATCH 160/226] Make tooltips works Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 11 +++++++++++ qt-ui/profilegraphics.h | 1 + 2 files changed, 12 insertions(+) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 550a5ec57..508251232 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "../color.h" #include "../display.h" @@ -136,6 +137,16 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent fill_profile_color(); } +void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) +{ + toolTip->clear(); + QList items = scene()->items( mapToScene(event->pos() ), Qt::IntersectsItemShape, Qt::DescendingOrder, transform()); + Q_FOREACH(QGraphicsItem *item, items){ + if (!item->toolTip().isEmpty()) + toolTip->addToolTip(item->toolTip()); + } +} + bool ProfileGraphicsView::eventFilter(QObject* obj, QEvent* event) { // This will "Eat" the default tooltip behavior. diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index ca1eb8983..d607e2615 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -87,6 +87,7 @@ public: protected: void resizeEvent(QResizeEvent *event); + void mouseMoveEvent(QMouseEvent* event); private: void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi); From ef7ace9926276f401cceb45d546a7134eeea0f00 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Wed, 8 May 2013 17:46:28 -0300 Subject: [PATCH 161/226] Plot the temperature Graph Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- profile.c | 79 +++++++++++++-------------------------- profile.h | 5 +++ qt-ui/profilegraphics.cpp | 45 ++++++++++++++++++---- qt-ui/profilegraphics.h | 1 + 4 files changed, 70 insertions(+), 60 deletions(-) diff --git a/profile.c b/profile.c index 2a8d5e4ed..f68822009 100644 --- a/profile.c +++ b/profile.c @@ -201,6 +201,32 @@ void remember_event(const char *eventname) evn_used++; } +int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi) +{ + int maxtime, mintemp, maxtemp, delta; + + /* Get plot scaling limits */ + maxtime = get_maxtime(pi); + mintemp = pi->mintemp; + maxtemp = pi->maxtemp; + + gc->leftx = 0; gc->rightx = maxtime; + /* Show temperatures in roughly the lower third, but make sure the scale + is at least somewhat reasonable */ + delta = maxtemp - mintemp; + if (delta < 3000) /* less than 3K in fluctuation */ + delta = 3000; + gc->topy = maxtemp + delta*2; + + if (PP_GRAPHS_ENABLED) + gc->bottomy = mintemp - delta * 2; + else + gc->bottomy = mintemp - delta / 3; + + pi->endtempcoord = SCALEY(gc, pi->mintemp); + return maxtemp && maxtemp >= mintemp; +} + #if 0 static void render_depth_sample(struct graphics_context *gc, struct plot_data *entry, const text_render_options_t *tro) { @@ -441,31 +467,7 @@ static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *p } -static int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi) -{ - int maxtime, mintemp, maxtemp, delta; - /* Get plot scaling limits */ - maxtime = get_maxtime(pi); - mintemp = pi->mintemp; - maxtemp = pi->maxtemp; - - gc->leftx = 0; gc->rightx = maxtime; - /* Show temperatures in roughly the lower third, but make sure the scale - is at least somewhat reasonable */ - delta = maxtemp - mintemp; - if (delta < 3000) /* less than 3K in fluctuation */ - delta = 3000; - gc->topy = maxtemp + delta*2; - - if (PP_GRAPHS_ENABLED) - gc->bottomy = mintemp - delta * 2; - else - gc->bottomy = mintemp - delta / 3; - - pi->endtempcoord = SCALEY(gc, pi->mintemp); - return maxtemp && maxtemp >= mintemp; -} static void plot_single_temp_text(struct graphics_context *gc, int sec, int mkelvin) { @@ -515,35 +517,6 @@ static void plot_temperature_text(struct graphics_context *gc, struct plot_info plot_single_temp_text(gc, sec, last_temperature); } -static void plot_temperature_profile(struct graphics_context *gc, struct plot_info *pi) -{ - int i; - cairo_t *cr = gc->cr; - int last = 0; - - if (!setup_temperature_limits(gc, pi)) - return; - - cairo_set_line_width_scaled(gc->cr, 2); - set_source_rgba(gc, TEMP_PLOT); - for (i = 0; i < pi->nr; i++) { - struct plot_data *entry = pi->entry + i; - int mkelvin = entry->temperature; - int sec = entry->sec; - if (!mkelvin) { - if (!last) - continue; - mkelvin = last; - } - if (last) - line_to(gc, sec, mkelvin); - else - move_to(gc, sec, mkelvin); - last = mkelvin; - } - cairo_stroke(cr); -} - /* gets both the actual start and end pressure as well as the scaling factors */ static int get_cylinder_pressure_range(struct graphics_context *gc, struct plot_info *pi) { diff --git a/profile.h b/profile.h index d22589c68..62affe04e 100644 --- a/profile.h +++ b/profile.h @@ -38,6 +38,7 @@ struct plot_data { void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); +int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi); struct ev_select { char *ev_name; @@ -83,6 +84,10 @@ int get_maxdepth(struct plot_info *pi); #define MIDDLE (0) #define BOTTOM (-1) +#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx) +#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) +#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y) + #ifdef __cplusplus } #endif diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 508251232..1d1b5530c 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -26,10 +26,6 @@ #define VELOCITY_COLORS_START_IDX VELO_STABLE #define VELOCITY_COLORS 5 -/* Scale to 0,0 -> maxx,maxy */ -#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx) -#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) -#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y) static struct graphics_context last_gc; static double plot_scale = SCALE_SCREEN; @@ -246,10 +242,10 @@ void ProfileGraphicsView::plot(struct dive *dive) plot_depth_profile(&gc, pi); plot_events(&gc, pi, dc); -#if 0 - /* Temperature profile */ - plot_temperature_profile(gc, pi); + /* Temperature profile */ + plot_temperature_profile(&gc, pi); +#if 0 /* Cylinder pressure plot */ plot_cylinder_pressure(gc, pi, dive, dc); @@ -630,6 +626,41 @@ void ProfileGraphicsView::resizeEvent(QResizeEvent *event) fitInView ( r.x() - 50, r.y() -50, r.width() + 100, r.height() + 100); // do a little bit of spacing; } +void ProfileGraphicsView::plot_temperature_profile(struct graphics_context *gc, struct plot_info *pi) +{ + int last = 0; + + if (!setup_temperature_limits(gc, pi)) + return; + + QPointF from; + QPointF to; + QColor color = profile_color[TEMP_PLOT].first(); + + for (int i = 0; i < pi->nr; i++) { + struct plot_data *entry = pi->entry + i; + int mkelvin = entry->temperature; + int sec = entry->sec; + if (!mkelvin) { + if (!last) + continue; + mkelvin = last; + } + if (last){ + to = QPointF(SCALE(gc, sec, mkelvin)); + //qDebug() << from << to; + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + item->setPen(QPen(color, 2*plot_scale)); + scene()->addItem(item); + from = to; + } + else{ + from = QPointF(SCALE(gc, sec, mkelvin)); + } + last = mkelvin; + } +} + void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) { QGraphicsPixmapItem *iconItem = 0; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index d607e2615..becad197a 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -94,6 +94,7 @@ private: void plot_text(struct graphics_context *gc, text_render_options_t *tro, double x, double y, const QString &text); void plot_events(struct graphics_context *gc, struct plot_info *pi, struct divecomputer *dc); void plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *event); + void plot_temperature_profile(struct graphics_context *gc, struct plot_info *pi); QPen defaultPen; QBrush defaultBrush; From 688276b6f200c5d8b44ab5b6092265a93f7c0b41 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Wed, 8 May 2013 17:48:20 -0300 Subject: [PATCH 162/226] Fix moving the ToolTip box with the mouse. When I changed the way that the tooltip box behaved, I accidentaly 'ate' the mouseMoveEvent, it was being used only to show tooltips instead of everything that it should have. this simple patch fixes it. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 1d1b5530c..271bd43ab 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -141,6 +141,7 @@ void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) if (!item->toolTip().isEmpty()) toolTip->addToolTip(item->toolTip()); } + QGraphicsView::mouseMoveEvent(event); } bool ProfileGraphicsView::eventFilter(QObject* obj, QEvent* event) From 6f06c31d0b9db9ffae45d409557864469ab169b0 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 8 May 2013 14:56:06 -0700 Subject: [PATCH 163/226] Stop passing around gc and pi Make the graphics_context part of the ProfileGraphicsView and remember that the plot info is already a part of the graphics_context (we kept passing around both of them in the Gtk code... pointless but a leftover from before adding the pi to the gc...) Signed-off-by: Dirk Hohndel --- display.h | 2 +- profile.c | 4 +- profile.h | 4 + qt-ui/profilegraphics.cpp | 179 +++++++++++++++++++------------------- qt-ui/profilegraphics.h | 12 +-- 5 files changed, 103 insertions(+), 98 deletions(-) diff --git a/display.h b/display.h index 047d00589..8567bc955 100644 --- a/display.h +++ b/display.h @@ -58,7 +58,7 @@ extern void plot(struct graphics_context *gc, struct dive *dive, scale_mode_t sc extern struct divecomputer *select_dc(struct divecomputer *main); extern void init_profile_background(struct graphics_context *gc); extern void attach_tooltip(int x, int y, int w, int h, const char *text, struct event *event); -extern void get_plot_details(struct graphics_context *gc, int time, char *buf, size_t bufsize); +extern void get_plot_details(struct graphics_context *gc, int time, char *buf, int bufsize); extern int x_to_time(double x); extern int x_abs(double x); diff --git a/profile.c b/profile.c index f68822009..85fb9f3e2 100644 --- a/profile.c +++ b/profile.c @@ -1551,7 +1551,7 @@ struct divecomputer *select_dc(struct divecomputer *main) return main; } -static void plot_string(struct plot_data *entry, char *buf, size_t bufsize, +static void plot_string(struct plot_data *entry, char *buf, int bufsize, int depth, int pressure, int temp, gboolean has_ndl) { int pressurevalue, mod, ead, end, eadd; @@ -1635,7 +1635,7 @@ static void plot_string(struct plot_data *entry, char *buf, size_t bufsize, free(buf2); } -void get_plot_details(struct graphics_context *gc, int time, char *buf, size_t bufsize) +void get_plot_details(struct graphics_context *gc, int time, char *buf, int bufsize) { struct plot_info *pi = &gc->pi; int pressure = 0, temp = 0; diff --git a/profile.h b/profile.h index 62affe04e..13e417785 100644 --- a/profile.h +++ b/profile.h @@ -84,6 +84,10 @@ int get_maxdepth(struct plot_info *pi); #define MIDDLE (0) #define BOTTOM (-1) +#define SCALEXGC(x) (((x) - gc.leftx) / (gc.rightx - gc.leftx) * gc.maxx) +#define SCALEYGC(y) (((y) - gc.topy) / (gc.bottomy - gc.topy) * gc.maxy) +#define SCALEGC(x,y) SCALEXGC(x),SCALEYGC(y) + #define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx) #define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) #define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 271bd43ab..b3893ed90 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -26,7 +26,6 @@ #define VELOCITY_COLORS_START_IDX VELO_STABLE #define VELOCITY_COLORS 5 - static struct graphics_context last_gc; static double plot_scale = SCALE_SCREEN; @@ -136,8 +135,8 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) { toolTip->clear(); - QList items = scene()->items( mapToScene(event->pos() ), Qt::IntersectsItemShape, Qt::DescendingOrder, transform()); - Q_FOREACH(QGraphicsItem *item, items){ + QList items = scene()->items(mapToScene(event->pos()), Qt::IntersectsItemShape, Qt::DescendingOrder, transform()); + Q_FOREACH(QGraphicsItem *item, items) { if (!item->toolTip().isEmpty()) toolTip->addToolTip(item->toolTip()); } @@ -147,7 +146,7 @@ void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) bool ProfileGraphicsView::eventFilter(QObject* obj, QEvent* event) { // This will "Eat" the default tooltip behavior. - if (event->type() == QEvent::GraphicsSceneHelp){ + if (event->type() == QEvent::GraphicsSceneHelp) { event->ignore(); return true; } @@ -184,15 +183,13 @@ void ProfileGraphicsView::plot(struct dive *dive) scene()->addItem(toolTip); - struct plot_info *pi; struct divecomputer *dc = &dive->dc; // This was passed around in the Cairo version / needed? - graphics_context gc; - const char *nickname; + // const char *nickname; // Fix this for printing / screen later. - // plot_set_scale( scale_mode_t); + // plot_set_scale(scale_mode_t); if (!dc->samples) { static struct sample fake[4]; @@ -237,15 +234,15 @@ void ProfileGraphicsView::plot(struct dive *dive) dc = select_dc(dc); /* This is per-dive-computer. Right now we just do the first one */ - pi = create_plot_info(dive, dc, &gc); + gc.pi = *create_plot_info(dive, dc, &gc); /* Depth profile */ - plot_depth_profile(&gc, pi); + plot_depth_profile(); - plot_events(&gc, pi, dc); + plot_events(dc); /* Temperature profile */ - plot_temperature_profile(&gc, pi); + plot_temperature_profile(); #if 0 /* Cylinder pressure plot */ plot_cylinder_pressure(gc, pi, dive, dc); @@ -300,7 +297,7 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } -void ProfileGraphicsView::plot_events(struct graphics_context *gc, struct plot_info *pi, struct divecomputer *dc) +void ProfileGraphicsView::plot_events(struct divecomputer *dc) { struct event *event = dc->events; @@ -309,14 +306,15 @@ void ProfileGraphicsView::plot_events(struct graphics_context *gc, struct plot_i // } while (event) { - plot_one_event(gc, pi, event); + plot_one_event(event); event = event->next; } } -void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *ev) +void ProfileGraphicsView::plot_one_event(struct event *ev) { int i, depth = 0; + struct plot_info *pi = &gc.pi; /* is plotting this event disabled? */ if (ev->name) { @@ -344,8 +342,8 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo /* draw a little triangular marker and attach tooltip */ - int x = SCALEX(gc, ev->time.seconds); - int y = SCALEY(gc, depth); + int x = SCALEXGC(ev->time.seconds); + int y = SCALEYGC(depth); EventItem *item = new EventItem(); item->setPos(x, y); @@ -363,7 +361,7 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo : QString("%1 %% %2").arg(o2).arg("O" UTF8_SUBSCRIPT_2); } else if (ev->name && !strcmp(ev->name, "SP change")) { - name += QString(":%1").arg( (double) ev->value / 1000 ); + name += QString(":%1").arg((double) ev->value / 1000); } else { name += QString(":%1").arg(ev->value); } @@ -379,7 +377,7 @@ void ProfileGraphicsView::plot_one_event(struct graphics_context *gc, struct plo item->setToolTip(name); } -void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) +void ProfileGraphicsView::plot_depth_profile() { int i, incr; int sec, depth; @@ -388,10 +386,10 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct int increments[8] = { 10, 20, 30, 60, 5*60, 10*60, 15*60, 30*60 }; /* Get plot scaling limits */ - maxtime = get_maxtime(pi); - maxdepth = get_maxdepth(pi); + maxtime = get_maxtime(&gc.pi); + maxdepth = get_maxdepth(&gc.pi); - gc->maxtime = maxtime; + gc.maxtime = maxtime; /* Time markers: at most every 10 seconds, but no more than 12 markers. * We start out with 10 seconds and increment up to 30 minutes, @@ -407,15 +405,15 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct while (maxtime / incr > 12) incr *= 2; - gc->leftx = 0; gc->rightx = maxtime; - gc->topy = 0; gc->bottomy = 1.0; + gc.leftx = 0; gc.rightx = maxtime; + gc.topy = 0; gc.bottomy = 1.0; - last_gc = *gc; + last_gc = gc; QColor color; color = profile_color[TIME_GRID].at(0); for (i = incr; i < maxtime; i += incr) { - QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, i, 0), SCALE(gc, i, 1)); + QGraphicsLineItem *line = new QGraphicsLineItem(SCALEGC(i, 0), SCALEGC(i, 1)); line->setPen(QPen(color)); scene()->addItem(line); } @@ -425,16 +423,16 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct if (maxtime < 600) { /* Be a bit more verbose with shorter dives */ for (i = incr; i < maxtime; i += incr) - plot_text(gc, &tro, i, 1, QString("%1:%2").arg(i/60).arg(i%60)); + plot_text(&tro, i, 1, QString("%1:%2").arg(i/60).arg(i%60)); } else { /* Only render the time on every second marker for normal dives */ for (i = incr; i < maxtime; i += 2 * incr) - plot_text(gc, &tro, i, 1, QString::number(i/60)); + plot_text(&tro, i, 1, QString::number(i/60)); } /* Depth markers: every 30 ft or 10 m*/ - gc->leftx = 0; gc->rightx = 1.0; - gc->topy = 0; gc->bottomy = maxdepth; + gc.leftx = 0; gc.rightx = 1.0; + gc.topy = 0; gc.bottomy = maxdepth; switch (prefs.units.length) { case units::METERS: marker = 10000; @@ -443,22 +441,23 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct marker = 9144; break; /* 30 ft */ } - maxline = MAX(pi->maxdepth + marker, maxdepth * 2 / 3); + maxline = MAX(gc.pi.maxdepth + marker, maxdepth * 2 / 3); color = profile_color[DEPTH_GRID].at(0); for (i = marker; i < maxline; i += marker) { - QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, 0, i), SCALE(gc, 1, i)); + QGraphicsLineItem *line = new QGraphicsLineItem(SCALEGC(0, i), SCALEGC(1, i)); line->setPen(QPen(color)); scene()->addItem(line); } - gc->leftx = 0; gc->rightx = maxtime; + gc.leftx = 0; gc.rightx = maxtime; color = profile_color[MEAN_DEPTH].at(0); /* Show mean depth */ - if (! gc->printer) { - QGraphicsLineItem *line = new QGraphicsLineItem(SCALE(gc, 0, pi->meandepth), SCALE(gc, pi->entry[pi->nr - 1].sec, pi->meandepth)); + if (! gc.printer) { + QGraphicsLineItem *line = new QGraphicsLineItem(SCALEGC(0, gc.pi.meandepth), + SCALEGC(gc.pi.entry[gc.pi.nr - 1].sec, gc.pi.meandepth)); line->setPen(QPen(color)); scene()->addItem(line); } @@ -475,27 +474,27 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct #endif /* Do the depth profile for the neat fill */ - gc->topy = 0; gc->bottomy = maxdepth; + gc.topy = 0; gc.bottomy = maxdepth; - entry = pi->entry; + entry = gc.pi.entry; QPolygonF p; QLinearGradient pat(0.0,0.0,0.0,scene()->height()); QGraphicsPolygonItem *neatFill = NULL; - p.append( QPointF(SCALE(gc, 0, 0) )); - for (i = 0; i < pi->nr; i++, entry++) - p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); + p.append(QPointF(SCALEGC(0, 0))); + for (i = 0; i < gc.pi.nr; i++, entry++) + p.append(QPointF(SCALEGC(entry->sec, entry->depth))); /* Show any ceiling we may have encountered */ - for (i = pi->nr - 1; i >= 0; i--, entry--) { + for (i = gc.pi.nr - 1; i >= 0; i--, entry--) { if (entry->ndl) { /* non-zero NDL implies this is a safety stop, no ceiling */ - p.append( QPointF( SCALE(gc, entry->sec, 0) )); + p.append(QPointF(SCALEGC(entry->sec, 0))); } else if (entry->stopdepth < entry->depth) { - p.append( QPointF( SCALE(gc, entry->sec, entry->stopdepth) )); + p.append(QPointF(SCALEGC(entry->sec, entry->stopdepth))); } else { - p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); + p.append(QPointF(SCALEGC(entry->sec, entry->depth))); } } pat.setColorAt(1, profile_color[DEPTH_BOTTOM].first()); @@ -518,17 +517,17 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct pat.setColorAt(0, profile_color[CEILING_SHALLOW].first()); pat.setColorAt(1, profile_color[CEILING_DEEP].first()); - entry = pi->entry; - p.append( QPointF(SCALE(gc, 0, 0) )); - for (i = 0; i < pi->nr; i++, entry++) { + entry = gc.pi.entry; + p.append(QPointF(SCALEGC(0, 0))); + for (i = 0; i < gc.pi.nr; i++, entry++) { if (entry->ndl == 0 && entry->stopdepth) { if (entry->ndl == 0 && entry->stopdepth < entry->depth) { - p.append( QPointF( SCALE(gc, entry->sec, entry->stopdepth) )); + p.append(QPointF(SCALEGC(entry->sec, entry->stopdepth))); } else { - p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); + p.append(QPointF(SCALEGC(entry->sec, entry->depth))); } } else { - p.append( QPointF( SCALE(gc, entry->sec, 0) )); + p.append(QPointF(SCALEGC(entry->sec, 0))); } } @@ -546,16 +545,16 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct pat.setColorAt(0, profile_color[CALC_CEILING_SHALLOW].first()); pat.setColorAt(1, profile_color[CALC_CEILING_DEEP].first()); - entry = pi->entry; + entry = gc.pi.entry; p.clear(); - p.append( QPointF(SCALE(gc, 0, 0) )); - for (i = 0; i < pi->nr; i++, entry++) { + p.append(QPointF(SCALEGC(0, 0))); + for (i = 0; i < gc.pi.nr; i++, entry++) { if (entry->ceiling) - p.append( QPointF( SCALE(gc, entry->sec, entry->ceiling) )); + p.append(QPointF(SCALEGC(entry->sec, entry->ceiling))); else - p.append( QPointF( SCALE(gc, entry->sec, 0) )); + p.append(QPointF(SCALEGC(entry->sec, 0))); } - p.append( QPointF( SCALE(gc, (entry-1)->sec, 0) )); + p.append(QPointF(SCALEGC((entry-1)->sec, 0))); neatFill = new QGraphicsPolygonItem(); neatFill->setPolygon(p); neatFill->setPen(QPen(QBrush(),0)); @@ -566,17 +565,17 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct pat.setColorAt(0, profile_color[CEILING_SHALLOW].first()); pat.setColorAt(1, profile_color[CEILING_DEEP].first()); - entry = pi->entry; + entry = gc.pi.entry; p.clear(); - p.append( QPointF(SCALE(gc, 0, 0) )); - for (i = 0; i < pi->nr; i++, entry++) - p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); + p.append(QPointF(SCALEGC(0, 0))); + for (i = 0; i < gc.pi.nr; i++, entry++) + p.append(QPointF(SCALEGC(entry->sec, entry->depth))); - for (i = pi->nr - 1; i >= 0; i--, entry--) { + for (i = gc.pi.nr - 1; i >= 0; i--, entry--) { if (entry->ndl == 0 && entry->stopdepth > entry->depth) { - p.append( QPointF( SCALE(gc, entry->sec, entry->stopdepth) )); + p.append(QPointF(SCALEGC(entry->sec, entry->stopdepth))); } else { - p.append( QPointF( SCALE(gc, entry->sec, entry->depth) )); + p.append(QPointF(SCALEGC(entry->sec, entry->depth))); } } @@ -587,21 +586,21 @@ void ProfileGraphicsView::plot_depth_profile(struct graphics_context *gc, struct scene()->addItem(neatFill); /* Now do it again for the velocity colors */ - entry = pi->entry; - for (i = 1; i < pi->nr; i++) { + entry = gc.pi.entry; + for (i = 1; i < gc.pi.nr; i++) { entry++; sec = entry->sec; /* we want to draw the segments in different colors * representing the vertical velocity, so we need to * chop this into short segments */ depth = entry->depth; - QGraphicsLineItem *colorLine = new QGraphicsLineItem( SCALE(gc, entry[-1].sec, entry[-1].depth), SCALE(gc, sec, depth)); - colorLine->setPen(QPen(QBrush(profile_color[ (color_indice_t) (VELOCITY_COLORS_START_IDX + entry->velocity)].first()), 2 )); + QGraphicsLineItem *colorLine = new QGraphicsLineItem(SCALEGC(entry[-1].sec, entry[-1].depth), SCALEGC(sec, depth)); + colorLine->setPen(QPen(QBrush(profile_color[ (color_indice_t) (VELOCITY_COLORS_START_IDX + entry->velocity)].first()), 2)); scene()->addItem(colorLine); } } -void ProfileGraphicsView::plot_text(struct graphics_context *gc, text_render_options_t *tro, double x, double y, const QString& text) +void ProfileGraphicsView::plot_text(text_render_options_t *tro, double x, double y, const QString& text) { QFontMetrics fm(font()); @@ -609,11 +608,11 @@ void ProfileGraphicsView::plot_text(struct graphics_context *gc, text_render_opt double dy = tro->vpos * (fm.height()); QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text); - QPointF point( SCALE(gc, x, y) ); // This is neded because of the SCALE macro. + QPointF point(SCALEGC(x, y)); // This is neded because of the SCALE macro. - item->setPos(point.x() + dx, point.y() +dy ); - item->setBrush( QBrush(profile_color[tro->color].first())); - item->setPen( QPen(profile_color[BACKGROUND].first())); + item->setPos(point.x() + dx, point.y() +dy); + item->setBrush(QBrush(profile_color[tro->color].first())); + item->setPen(QPen(profile_color[BACKGROUND].first())); item->setFlag(QGraphicsItem::ItemIgnoresTransformations); scene()->addItem(item); } @@ -624,22 +623,22 @@ void ProfileGraphicsView::resizeEvent(QResizeEvent *event) // I can pass some parameters to this - // like Qt::IgnoreAspectRatio or Qt::KeepAspectRatio QRectF r = scene()->sceneRect(); - fitInView ( r.x() - 50, r.y() -50, r.width() + 100, r.height() + 100); // do a little bit of spacing; + fitInView (r.x() - 50, r.y() -50, r.width() + 100, r.height() + 100); // do a little bit of spacing; } -void ProfileGraphicsView::plot_temperature_profile(struct graphics_context *gc, struct plot_info *pi) +void ProfileGraphicsView::plot_temperature_profile() { int last = 0; - if (!setup_temperature_limits(gc, pi)) + if (!setup_temperature_limits(&gc, &gc.pi)) return; QPointF from; QPointF to; QColor color = profile_color[TEMP_PLOT].first(); - for (int i = 0; i < pi->nr; i++) { - struct plot_data *entry = pi->entry + i; + for (int i = 0; i < gc.pi.nr; i++) { + struct plot_data *entry = gc.pi.entry + i; int mkelvin = entry->temperature; int sec = entry->sec; if (!mkelvin) { @@ -647,8 +646,8 @@ void ProfileGraphicsView::plot_temperature_profile(struct graphics_context *gc, continue; mkelvin = last; } - if (last){ - to = QPointF(SCALE(gc, sec, mkelvin)); + if (last) { + to = QPointF(SCALEGC(sec, mkelvin)); //qDebug() << from << to; QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); item->setPen(QPen(color, 2*plot_scale)); @@ -656,7 +655,7 @@ void ProfileGraphicsView::plot_temperature_profile(struct graphics_context *gc, from = to; } else{ - from = QPointF(SCALE(gc, sec, mkelvin)); + from = QPointF(SCALEGC(sec, mkelvin)); } last = mkelvin; } @@ -669,11 +668,11 @@ void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) if (!icon.isNull()) { iconItem = new QGraphicsPixmapItem(icon.pixmap(ICON_SMALL,ICON_SMALL), this); - iconItem->setPos( SPACING, yValue); + iconItem->setPos(SPACING, yValue); } QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(toolTip, this); - textItem->setPos( SPACING + ICON_SMALL + SPACING, yValue); + textItem->setPos(SPACING + ICON_SMALL + SPACING, yValue); textItem->setPen(QPen(Qt::white, 1)); textItem->setBrush(QBrush(Qt::white)); textItem->setFlag(ItemIgnoresTransformations); @@ -692,7 +691,7 @@ void ToolTipItem::removeToolTip(const QString& toolTip) int toolTipIndex = 0; // We removed a toolTip, let's move the others to the correct location - Q_FOREACH(ToolTip t, toolTips){ + Q_FOREACH(ToolTip t, toolTips) { double yValue = title->boundingRect().height() + SPACING + toolTipIndex * ICON_SMALL + SPACING; // Icons can be null. @@ -733,7 +732,7 @@ void ToolTipItem::setRect(const QRectF& r) setPath(border); QPainterPath bg; - bg.addRoundedRect( -1, -1, rectangle.width() + 3, rectangle.height() + 4, 3, 3); + bg.addRoundedRect(-1, -1, rectangle.width() + 3, rectangle.height() + 4, 3, 3); QColor c = QColor(Qt::black); c.setAlpha(155); @@ -756,7 +755,7 @@ void ToolTipItem::collapse() QPropertyAnimation *animation = new QPropertyAnimation(this, "rect"); animation->setDuration(100); animation->setStartValue(boundingRect()); - animation->setEndValue(QRect(0, 0, ICON_SMALL, ICON_SMALL )); + animation->setEndValue(QRect(0, 0, ICON_SMALL, ICON_SMALL)); animation->start(QAbstractAnimation::DeleteWhenStopped); } @@ -778,7 +777,7 @@ void ToolTipItem::expand() if (width < title->boundingRect().width() + SPACING*2) width = title->boundingRect().width() + SPACING*2; - if( height < ICON_SMALL) + if(height < ICON_SMALL) height = ICON_SMALL; nextRectangle.setWidth(width); @@ -806,10 +805,10 @@ ToolTipItem::ToolTipItem(QGraphicsItem* parent): QGraphicsPathItem(parent), back void ToolTipItem::updateTitlePosition() { - if (rectangle.width() < title->boundingRect().width() + SPACING*4 ){ + if (rectangle.width() < title->boundingRect().width() + SPACING*4) { QRectF newRect = rectangle; newRect.setWidth(title->boundingRect().width() + SPACING*4); - newRect.setHeight( newRect.height() ? newRect.height() : ICON_SMALL ); + newRect.setHeight(newRect.height() ? newRect.height() : ICON_SMALL); setRect(newRect); } @@ -818,7 +817,7 @@ void ToolTipItem::updateTitlePosition() title->setPen(QPen(Qt::white, 1)); title->setBrush(Qt::white); - if (toolTips.size() > 0){ + if (toolTips.size() > 0) { double x1 = 0; double y1 = title->pos().y() + SPACING/2 + title->boundingRect().height(); double x2 = boundingRect().width() - 10; @@ -828,7 +827,7 @@ void ToolTipItem::updateTitlePosition() separator->setFlag(ItemIgnoresTransformations); separator->setPen(QPen(Qt::white)); separator->show(); - }else{ + } else { separator->hide(); } } diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index becad197a..e1cb417f9 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -1,6 +1,7 @@ #ifndef PROFILEGRAPHICS_H #define PROFILEGRAPHICS_H +#include "../display.h" #include #include #include @@ -90,15 +91,16 @@ protected: void mouseMoveEvent(QMouseEvent* event); private: - void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi); - void plot_text(struct graphics_context *gc, text_render_options_t *tro, double x, double y, const QString &text); - void plot_events(struct graphics_context *gc, struct plot_info *pi, struct divecomputer *dc); - void plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *event); - void plot_temperature_profile(struct graphics_context *gc, struct plot_info *pi); + void plot_depth_profile(); + void plot_text(text_render_options_t *tro, double x, double y, const QString &text); + void plot_events(struct divecomputer *dc); + void plot_one_event(struct event *event); + void plot_temperature_profile(); QPen defaultPen; QBrush defaultBrush; ToolTipItem *toolTip; + graphics_context gc; }; #endif From 656b58f20554f31bd25ec7a2c3b65a1b19046062 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 8 May 2013 15:46:16 -0700 Subject: [PATCH 164/226] Add tooltip data and cleanup size calculations Tomaz' code had a fixed height per tooltip item and some rather suspicious logic how to position them (and how to size the surrounding box), based on a fixed height in pixels per item - which of course fails if you use larger fonts or multi line items. This uses the bounding rects to correctly calculate the sizes and populates the tooltip with the other dive data that we already had in a helper function. This also fixes a small formatting issue for gas change events. Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index b3893ed90..93502f637 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -135,6 +135,10 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) { toolTip->clear(); + int time = (mapToScene(event->pos()).x() * gc.maxtime) / scene()->sceneRect().width(); + char buffer[500]; + get_plot_details(&gc, time, buffer, 500); + toolTip->addToolTip(QString(buffer)); QList items = scene()->items(mapToScene(event->pos()), Qt::IntersectsItemShape, Qt::DescendingOrder, transform()); Q_FOREACH(QGraphicsItem *item, items) { if (!item->toolTip().isEmpty()) @@ -356,9 +360,10 @@ void ProfileGraphicsView::plot_one_event(struct event *ev) unsigned int he = ev->value >> 16; unsigned int o2 = ev->value & 0xffff; + name += ": "; name += (he) ? QString("%1/%2").arg(o2, he) - : (o2 == 21) ? name += tr(":air") - : QString("%1 %% %2").arg(o2).arg("O" UTF8_SUBSCRIPT_2); + : (o2 == 21) ? name += tr("air") + : QString("%1% %2").arg(o2).arg("O" UTF8_SUBSCRIPT_2); } else if (ev->name && !strcmp(ev->name, "SP change")) { name += QString(":%1").arg((double) ev->value / 1000); @@ -664,8 +669,10 @@ void ProfileGraphicsView::plot_temperature_profile() void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) { QGraphicsPixmapItem *iconItem = 0; - double yValue = title->boundingRect().height() + SPACING + toolTips.keys().size() * ICON_SMALL + SPACING; - + double yValue = title->boundingRect().height() + SPACING; + Q_FOREACH(ToolTip t, toolTips) { + yValue += t.second->boundingRect().height(); + } if (!icon.isNull()) { iconItem = new QGraphicsPixmapItem(icon.pixmap(ICON_SMALL,ICON_SMALL), this); iconItem->setPos(SPACING, yValue); @@ -764,13 +771,12 @@ void ToolTipItem::expand() QRectF currentRect = rectangle; QRectF nextRectangle; - double width = 0; + double width = 0, height = title->boundingRect().height() + SPACING; Q_FOREACH(ToolTip t, toolTips) { if (t.second->boundingRect().width() > width) width = t.second->boundingRect().width(); + height += t.second->boundingRect().height(); } - - double height = toolTips.count() * 18 + title->boundingRect().height() + SPACING; /* Left padding, Icon Size, space, right padding */ width += SPACING + ICON_SMALL + SPACING + SPACING; From 880870b46cce3b964d1ef3e472ec3c94544b5c25 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 8 May 2013 16:30:02 -0700 Subject: [PATCH 165/226] Show the dive computer name in the profile window And clean up some obsolete Gtk related stuff that is no longer relevant for the Qt port. Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 93502f637..46e040132 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -189,8 +189,6 @@ void ProfileGraphicsView::plot(struct dive *dive) struct divecomputer *dc = &dive->dc; - // This was passed around in the Cairo version / needed? - // const char *nickname; // Fix this for printing / screen later. // plot_set_scale(scale_mode_t); @@ -224,13 +222,6 @@ void ProfileGraphicsView::plot(struct dive *dive) */ calculate_max_limits(dive, dc, &gc); - /* - * We don't use "cairo_translate()" because that doesn't - * scale line width etc. But the actual scaling we need - * do set up ourselves.. - * - * Snif. What a pity. - */ QRectF drawing_area = scene()->sceneRect(); gc.maxx = (drawing_area.width() - 2 * drawing_area.x()); gc.maxy = (drawing_area.height() - 2 * drawing_area.y()); @@ -256,29 +247,21 @@ void ProfileGraphicsView::plot(struct dive *dive) plot_depth_text(gc, pi); plot_cylinder_pressure_text(gc, pi); plot_deco_text(gc, pi); +#endif - /* Bounding box last */ - gc->leftx = 0; gc->rightx = 1.0; - gc->topy = 0; gc->bottomy = 1.0; - - set_source_rgba(gc, BOUNDING_BOX); - cairo_set_line_width_scaled(gc->cr, 1); - move_to(gc, 0, 0); - line_to(gc, 0, 1); - line_to(gc, 1, 1); - line_to(gc, 1, 0); - cairo_close_path(gc->cr); - cairo_stroke(gc->cr); + gc.leftx = 0; gc.rightx = 1.0; + gc.topy = 0; gc.bottomy = 1.0; /* Put the dive computer name in the lower left corner */ + const char *nickname; nickname = get_dc_nickname(dc->model, dc->deviceid); if (!nickname || *nickname == '\0') nickname = dc->model; if (nickname) { - static const text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; - plot_text(gc, &computer, 0, 1, "%s", nickname); + text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; + plot_text(&computer, 0, 1, nickname); } - +#if 0 if (PP_GRAPHS_ENABLED) { plot_pp_gas_profile(gc, pi); plot_pp_text(gc, pi); From d120fed21191f547db1b2bc3a04a9ce389faec0f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 8 May 2013 16:53:00 -0700 Subject: [PATCH 166/226] Add bounding box to profile Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 46e040132..50791e14c 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -248,9 +248,13 @@ void ProfileGraphicsView::plot(struct dive *dive) plot_cylinder_pressure_text(gc, pi); plot_deco_text(gc, pi); #endif - - gc.leftx = 0; gc.rightx = 1.0; - gc.topy = 0; gc.bottomy = 1.0; + /* Bounding box */ + QColor color = profile_color[TIME_GRID].at(0); + QPen pen = QPen(color); + pen.setWidth(1); + QGraphicsRectItem *rect = new QGraphicsRectItem(scene()->sceneRect()); + rect->setPen(pen); + scene()->addItem(rect); /* Put the dive computer name in the lower left corner */ const char *nickname; @@ -259,7 +263,7 @@ void ProfileGraphicsView::plot(struct dive *dive) nickname = dc->model; if (nickname) { text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; - plot_text(&computer, 0, 1, nickname); + plot_text(&computer, gc.leftx, gc.bottomy, nickname); } #if 0 if (PP_GRAPHS_ENABLED) { From 9554cb57673f3dc4818b9578ea607526c813242e Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 00:24:03 -0300 Subject: [PATCH 167/226] Plot of the Cylinder Pressure over time. a few code was moved around, a macro that contained the form of x ? : y; had to be rewritten to x ? x : y since c++ doesn't allow ternarys without the middle operator. The color-choosing for the Cylinder Pressure broke on the Qt port - but it's a small issue. I'm painting everyone as 'dark green' now, will fix that later. Signed-off-by: Tomaz Canabrava --- profile.c | 114 +++----------------------------------- profile.h | 12 ++++ qt-ui/profilegraphics.cpp | 100 ++++++++++++++++++++++++++++++++- qt-ui/profilegraphics.h | 3 + 4 files changed, 121 insertions(+), 108 deletions(-) diff --git a/profile.c b/profile.c index 85fb9f3e2..61bbd12ce 100644 --- a/profile.c +++ b/profile.c @@ -25,14 +25,6 @@ static struct plot_data *last_pi_entry = NULL; #define cairo_set_line_width_scaled(cr, w) \ cairo_set_line_width((cr), (w) * plot_scale); - - -#define SENSOR_PR 0 -#define INTERPOLATED_PR 1 -#define SENSOR_PRESSURE(_entry) (_entry)->pressure[SENSOR_PR] -#define INTERPOLATED_PRESSURE(_entry) (_entry)->pressure[INTERPOLATED_PR] -#define GET_PRESSURE(_entry) (SENSOR_PRESSURE(_entry) ? : INTERPOLATED_PRESSURE(_entry)) - #if USE_GTK_UI /* keep the last used gc around so we can invert the SCALEX calculation in @@ -518,46 +510,28 @@ static void plot_temperature_text(struct graphics_context *gc, struct plot_info } /* gets both the actual start and end pressure as well as the scaling factors */ -static int get_cylinder_pressure_range(struct graphics_context *gc, struct plot_info *pi) + +#endif /* USE_GTK_UI */ + +int get_cylinder_pressure_range(struct graphics_context *gc) { gc->leftx = 0; - gc->rightx = get_maxtime(pi); + gc->rightx = get_maxtime(&gc->pi); if (PP_GRAPHS_ENABLED) - gc->bottomy = -pi->maxpressure * 0.75; + gc->bottomy = -gc->pi.maxpressure * 0.75; else gc->bottomy = 0; - gc->topy = pi->maxpressure * 1.5; - if (!pi->maxpressure) + gc->topy = gc->pi.maxpressure * 1.5; + if (!gc->pi.maxpressure) return FALSE; - while (pi->endtempcoord <= SCALEY(gc, pi->minpressure - (gc->topy) * 0.1)) + while (gc->pi.endtempcoord <= SCALEY(gc, gc->pi.minpressure - (gc->topy) * 0.1)) gc->bottomy -= gc->topy * 0.1; return TRUE; } -/* set the color for the pressure plot according to temporary sac rate - * as compared to avg_sac; the calculation simply maps the delta between - * sac and avg_sac to indexes 0 .. (SAC_COLORS - 1) with everything - * more than 6000 ml/min below avg_sac mapped to 0 */ -void set_sac_color(struct graphics_context *gc, int sac, int avg_sac) -{ - int sac_index = 0; - int delta = sac - avg_sac + 7000; - - if (!gc->printer) { - sac_index = delta / 2000; - if (sac_index < 0) - sac_index = 0; - if (sac_index > SAC_COLORS - 1) - sac_index = SAC_COLORS - 1; - set_source_rgba(gc, SAC_COLORS_START_IDX + sac_index); - } else { - set_source_rgba(gc, SAC_DEFAULT); - } -} -#endif /* USE_GTK_UI */ /* Get local sac-rate (in ml/min) between entry1 and entry2 */ int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive) @@ -590,78 +564,8 @@ int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct div return airuse / atm * 60 / duration; } -/* calculate the current SAC in ml/min and convert to int */ -#define GET_LOCAL_SAC(_entry1, _entry2, _dive) \ - get_local_sac(_entry1, _entry2, _dive) - -#define SAC_WINDOW 45 /* sliding window in seconds for current SAC calculation */ - #if USE_GTK_UI -static void plot_cylinder_pressure(struct graphics_context *gc, struct plot_info *pi, - struct dive *dive, struct divecomputer *dc) -{ - int i; - int last = -1, last_index = -1; - int lift_pen = FALSE; - int first_plot = TRUE; - int sac = 0; - struct plot_data *last_entry = NULL; - if (!get_cylinder_pressure_range(gc, pi)) - return; - - cairo_set_line_width_scaled(gc->cr, 2); - - for (i = 0; i < pi->nr; i++) { - int mbar; - struct plot_data *entry = pi->entry + i; - - mbar = GET_PRESSURE(entry); - if (entry->cylinderindex != last_index) { - lift_pen = TRUE; - last_entry = NULL; - } - if (!mbar) { - lift_pen = TRUE; - continue; - } - if (!last_entry) { - last = i; - last_entry = entry; - sac = GET_LOCAL_SAC(entry, pi->entry + i + 1, dive); - } else { - int j; - sac = 0; - for (j = last; j < i; j++) - sac += GET_LOCAL_SAC(pi->entry + j, pi->entry + j + 1, dive); - sac /= (i - last); - if (entry->sec - last_entry->sec >= SAC_WINDOW) { - last++; - last_entry = pi->entry + last; - } - } - set_sac_color(gc, sac, dive->sac); - if (lift_pen) { - if (!first_plot && entry->cylinderindex == last_index) { - /* if we have a previous event from the same tank, - * draw at least a short line */ - int prev_pr; - prev_pr = GET_PRESSURE(entry - 1); - move_to(gc, (entry-1)->sec, prev_pr); - line_to(gc, entry->sec, mbar); - } else { - first_plot = FALSE; - move_to(gc, entry->sec, mbar); - } - lift_pen = FALSE; - } else { - line_to(gc, entry->sec, mbar); - } - cairo_stroke(gc->cr); - move_to(gc, entry->sec, mbar); - last_index = entry->cylinderindex; - } -} static void plot_pressure_value(struct graphics_context *gc, int mbar, int sec, int xalign, int yalign) diff --git a/profile.h b/profile.h index 13e417785..9389641d5 100644 --- a/profile.h +++ b/profile.h @@ -39,6 +39,7 @@ struct plot_data { void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi); +int get_cylinder_pressure_range(struct graphics_context *gc); struct ev_select { char *ev_name; @@ -60,6 +61,9 @@ int get_maxtime(struct plot_info *pi); * partial pressure graphs */ int get_maxdepth(struct plot_info *pi); +int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive); + + #define ALIGN_LEFT 1 #define ALIGN_RIGHT 2 #define INVISIBLE 4 @@ -92,6 +96,14 @@ int get_maxdepth(struct plot_info *pi); #define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy) #define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y) +#define SENSOR_PR 0 +#define INTERPOLATED_PR 1 +#define SENSOR_PRESSURE(_entry) (_entry)->pressure[SENSOR_PR] +#define INTERPOLATED_PRESSURE(_entry) (_entry)->pressure[INTERPOLATED_PR] +#define GET_PRESSURE(_entry) (SENSOR_PRESSURE(_entry) ? SENSOR_PRESSURE(_entry) : INTERPOLATED_PRESSURE(_entry)) + +#define SAC_WINDOW 45 /* sliding window in seconds for current SAC calculation */ + #ifdef __cplusplus } #endif diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 50791e14c..32bd87d14 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -238,10 +238,10 @@ void ProfileGraphicsView::plot(struct dive *dive) /* Temperature profile */ plot_temperature_profile(); -#if 0 - /* Cylinder pressure plot */ - plot_cylinder_pressure(gc, pi, dive, dc); + /* Cylinder pressure plot */ + plot_cylinder_pressure(dive, dc); +#if 0 /* Text on top of all graphs.. */ plot_temperature_text(gc, pi); plot_depth_text(gc, pi); @@ -288,6 +288,100 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } +void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc) +{ + int i; + int last = -1, last_index = -1; + int lift_pen = FALSE; + int first_plot = TRUE; + int sac = 0; + struct plot_data *last_entry = NULL; + + if (!get_cylinder_pressure_range(&gc)) + return; + + QPointF from, to; + for (i = 0; i < gc.pi.nr; i++) { + int mbar; + struct plot_data *entry = gc.pi.entry + i; + + mbar = GET_PRESSURE(entry); + if (entry->cylinderindex != last_index) { + lift_pen = TRUE; + last_entry = NULL; + } + if (!mbar) { + lift_pen = TRUE; + continue; + } + if (!last_entry) { + last = i; + last_entry = entry; + sac = get_local_sac(entry, gc.pi.entry + i + 1, dive); + } else { + int j; + sac = 0; + for (j = last; j < i; j++) + sac += get_local_sac(gc.pi.entry + j, gc.pi.entry + j + 1, dive); + sac /= (i - last); + if (entry->sec - last_entry->sec >= SAC_WINDOW) { + last++; + last_entry = gc.pi.entry + last; + } + } + + // QColor c = get_sac_color(sac, dive->sac); Buggy TODO: fix. + QColor c = QColor(Qt::darkGreen); + + if (lift_pen) { + if (!first_plot && entry->cylinderindex == last_index) { + /* if we have a previous event from the same tank, + * draw at least a short line */ + int prev_pr; + prev_pr = GET_PRESSURE(entry - 1); + + QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC((entry-1)->sec, prev_pr), SCALEGC(entry->sec, mbar)); + item->setPen(QPen(c, 2)); + scene()->addItem(item); + } else { + first_plot = FALSE; + from = QPointF(SCALEGC(entry->sec, mbar)); + } + lift_pen = FALSE; + } else { + to = QPointF(SCALEGC(entry->sec, mbar)); + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + item->setPen(QPen(c, 2)); + scene()->addItem(item); + } + + + from = QPointF(SCALEGC(entry->sec, mbar)); + last_index = entry->cylinderindex; + } +} + + +/* set the color for the pressure plot according to temporary sac rate + * as compared to avg_sac; the calculation simply maps the delta between + * sac and avg_sac to indexes 0 .. (SAC_COLORS - 1) with everything + * more than 6000 ml/min below avg_sac mapped to 0 */ +QColor ProfileGraphicsView::get_sac_color(int sac, int avg_sac) +{ + int sac_index = 0; + int delta = sac - avg_sac + 7000; + + if (!gc.printer) { + sac_index = delta / 2000; + if (sac_index < 0) + sac_index = 0; + if (sac_index > SAC_COLORS - 1) + sac_index = SAC_COLORS - 1; + return profile_color[ (color_indice_t) (SAC_COLORS_START_IDX + sac_index)].first(); + } + return profile_color[SAC_DEFAULT].first(); +} + void ProfileGraphicsView::plot_events(struct divecomputer *dc) { struct event *event = dc->events; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index e1cb417f9..7f166574d 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -96,6 +96,9 @@ private: void plot_events(struct divecomputer *dc); void plot_one_event(struct event *event); void plot_temperature_profile(); + void plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc); + + QColor get_sac_color(int sac, int avg_sac); QPen defaultPen; QBrush defaultBrush; From 4f4d40925b668f015fead231de9f16319083f78b Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Wed, 8 May 2013 07:31:02 +0100 Subject: [PATCH 168/226] Remove 2 keyboard short cuts. Ctrl-C & Shift-Ctrl-C were used for next & prev DC which was considered to be poor style. Disable for now. Maybe replace with something else? Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.ui | 6 ------ 1 file changed, 6 deletions(-) diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 476b124e8..377605326 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -315,17 +315,11 @@ Prev DC - - Ctrl+Shift+C - Next DC - - Ctrl+C - From 7f9fbd229568a85109860c18dd18e1e9928e351f Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Thu, 9 May 2013 08:32:27 +0100 Subject: [PATCH 169/226] Horizontally align labels on info widget page. Left aligning text values looked wrong. Use Qobject cast to filter labels from any other qobjects around and set alignment. Doing this via Qt Designer would be tedious. Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 4e7f6b3a1..e1ac7c275 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -21,6 +21,14 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), ui->setupUi(this); ui->cylinders->setModel(cylindersModel); ui->weights->setModel(weightModel); + + /* example of where code is more concise than Qt designer */ + QList infoTabWidgets = ui->infoTab->children(); + Q_FOREACH( QObject* obj, infoTabWidgets ){ + QLabel* label = qobject_cast(obj); + if (label) + label->setAlignment(Qt::AlignHCenter); + } } void MainTab::clearEquipment() From 17c6db6a5b05b3ac1f85d136e86a0072e15e46cd Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 15:04:10 -0300 Subject: [PATCH 170/226] Shows the correct color for the CylinderPressure Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 32bd87d14..3baf2b008 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -116,6 +116,7 @@ extern int evn_used; ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) { + gc.printer = false; setScene(new QGraphicsScene()); setBackgroundBrush(QColor("#F3F3E6")); scene()->setSceneRect(0,0,1000,1000); @@ -330,8 +331,7 @@ void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divec } } - // QColor c = get_sac_color(sac, dive->sac); Buggy TODO: fix. - QColor c = QColor(Qt::darkGreen); + QColor c = get_sac_color(sac, dive->sac); if (lift_pen) { if (!first_plot && entry->cylinderindex == last_index) { From c62e8e5baa647bfa2e4773bc611eaef7985a7bb7 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 15:28:50 -0300 Subject: [PATCH 171/226] Plotting temperature text. Signed-off-by: Tomaz Canabrava --- profile.c | 49 ++------------------------------- profile.h | 2 +- qt-ui/profilegraphics.cpp | 57 ++++++++++++++++++++++++++++++++++++--- qt-ui/profilegraphics.h | 3 ++- 4 files changed, 59 insertions(+), 52 deletions(-) diff --git a/profile.c b/profile.c index 61bbd12ce..3905a029c 100644 --- a/profile.c +++ b/profile.c @@ -193,10 +193,11 @@ void remember_event(const char *eventname) evn_used++; } -int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi) +int setup_temperature_limits(struct graphics_context *gc) { int maxtime, mintemp, maxtemp, delta; + struct plot_info *pi = &gc->pi; /* Get plot scaling limits */ maxtime = get_maxtime(pi); mintemp = pi->mintemp; @@ -461,53 +462,7 @@ static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *p -static void plot_single_temp_text(struct graphics_context *gc, int sec, int mkelvin) -{ - double deg; - const char *unit; - static const text_render_options_t tro = {TEMP_TEXT_SIZE, TEMP_TEXT, LEFT, TOP}; - deg = get_temp_units(mkelvin, &unit); - - plot_text(gc, &tro, sec, mkelvin, "%.2g%s", deg, unit); -} - -static void plot_temperature_text(struct graphics_context *gc, struct plot_info *pi) -{ - int i; - int last = -300, sec = 0; - int last_temperature = 0, last_printed_temp = 0; - - if (!setup_temperature_limits(gc, pi)) - return; - - for (i = 0; i < pi->nr; i++) { - struct plot_data *entry = pi->entry+i; - int mkelvin = entry->temperature; - sec = entry->sec; - - if (!mkelvin) - continue; - last_temperature = mkelvin; - /* don't print a temperature - * if it's been less than 5min and less than a 2K change OR - * if it's been less than 2min OR if the change from the - * last print is less than .4K (and therefore less than 1F */ - if (((sec < last + 300) && (abs(mkelvin - last_printed_temp) < 2000)) || - (sec < last + 120) || - (abs(mkelvin - last_printed_temp) < 400)) - continue; - last = sec; - plot_single_temp_text(gc,sec,mkelvin); - last_printed_temp = mkelvin; - } - /* it would be nice to print the end temperature, if it's - * different or if the last temperature print has been more - * than a quarter of the dive back */ - if ((abs(last_temperature - last_printed_temp) > 500) || - ((double)last / (double)sec < 0.75)) - plot_single_temp_text(gc, sec, last_temperature); -} /* gets both the actual start and end pressure as well as the scaling factors */ diff --git a/profile.h b/profile.h index 9389641d5..73c5152f9 100644 --- a/profile.h +++ b/profile.h @@ -38,7 +38,7 @@ struct plot_data { void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc); -int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi); +int setup_temperature_limits(struct graphics_context *gc); int get_cylinder_pressure_range(struct graphics_context *gc); struct ev_select { diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 3baf2b008..53d943e09 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -242,10 +242,13 @@ void ProfileGraphicsView::plot(struct dive *dive) /* Cylinder pressure plot */ plot_cylinder_pressure(dive, dc); -#if 0 + /* Text on top of all graphs.. */ - plot_temperature_text(gc, pi); + plot_temperature_text(); + plot_depth_text(gc, pi); + +#if 0 plot_cylinder_pressure_text(gc, pi); plot_deco_text(gc, pi); #endif @@ -289,6 +292,54 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } +void ProfileGraphicsView::plot_temperature_text() +{ + int i; + int last = -300, sec = 0; + int last_temperature = 0, last_printed_temp = 0; + plot_info *pi = &gc.pi; + + if (!setup_temperature_limits(&gc)) + return; + + for (i = 0; i < pi->nr; i++) { + struct plot_data *entry = pi->entry+i; + int mkelvin = entry->temperature; + sec = entry->sec; + + if (!mkelvin) + continue; + last_temperature = mkelvin; + /* don't print a temperature + * if it's been less than 5min and less than a 2K change OR + * if it's been less than 2min OR if the change from the + * last print is less than .4K (and therefore less than 1F */ + if (((sec < last + 300) && (abs(mkelvin - last_printed_temp) < 2000)) || + (sec < last + 120) || + (abs(mkelvin - last_printed_temp) < 400)) + continue; + last = sec; + + plot_single_temp_text(sec,mkelvin); + last_printed_temp = mkelvin; + } + /* it would be nice to print the end temperature, if it's + * different or if the last temperature print has been more + * than a quarter of the dive back */ + if ((abs(last_temperature - last_printed_temp) > 500) || + ((double)last / (double)sec < 0.75)) + plot_single_temp_text(sec, last_temperature); +} + +void ProfileGraphicsView::plot_single_temp_text(int sec, int mkelvin) +{ + double deg; + const char *unit; + static text_render_options_t tro = {TEMP_TEXT_SIZE, TEMP_TEXT, LEFT, TOP}; + deg = get_temp_units(mkelvin, &unit); + plot_text(&tro, sec, mkelvin, QString("%1%2").arg(deg).arg(unit)); //"%.2g%s" +} + void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc) { int i; @@ -716,7 +767,7 @@ void ProfileGraphicsView::plot_temperature_profile() { int last = 0; - if (!setup_temperature_limits(&gc, &gc.pi)) + if (!setup_temperature_limits(&gc)) return; QPointF from; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 7f166574d..d6c30dda2 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -97,7 +97,8 @@ private: void plot_one_event(struct event *event); void plot_temperature_profile(); void plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc); - + void plot_temperature_text(); + void plot_single_temp_text(int sec, int mkelvin); QColor get_sac_color(int sac, int avg_sac); QPen defaultPen; From d6d1a10195e1d14bafb5987d5f7b8e776a82b4be Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 15:37:43 -0300 Subject: [PATCH 172/226] Plotting depth text. Signed-off-by: Tomaz Canabrava --- profile.c | 51 ----------------------------------- qt-ui/profilegraphics.cpp | 57 ++++++++++++++++++++++++++++++++++++++- qt-ui/profilegraphics.h | 4 +++ 3 files changed, 60 insertions(+), 52 deletions(-) diff --git a/profile.c b/profile.c index 3905a029c..67f83f0a6 100644 --- a/profile.c +++ b/profile.c @@ -221,57 +221,6 @@ int setup_temperature_limits(struct graphics_context *gc) } #if 0 -static void render_depth_sample(struct graphics_context *gc, struct plot_data *entry, const text_render_options_t *tro) -{ - int sec = entry->sec, decimals; - double d; - - d = get_depth_units(entry->depth, &decimals, NULL); - - plot_text(gc, tro, sec, entry->depth, "%.*f", decimals, d); -} - -static void plot_text_samples(struct graphics_context *gc, struct plot_info *pi) -{ - static const text_render_options_t deep = {14, SAMPLE_DEEP, CENTER, TOP}; - static const text_render_options_t shallow = {14, SAMPLE_SHALLOW, CENTER, BOTTOM}; - int i; - int last = -1; - - for (i = 0; i < pi->nr; i++) { - struct plot_data *entry = pi->entry + i; - - if (entry->depth < 2000) - continue; - - if ((entry == entry->max[2]) && entry->depth != last) { - render_depth_sample(gc, entry, &deep); - last = entry->depth; - } - - if ((entry == entry->min[2]) && entry->depth != last) { - render_depth_sample(gc, entry, &shallow); - last = entry->depth; - } - - if (entry->depth != last) - last = -1; - } -} - -static void plot_depth_text(struct graphics_context *gc, struct plot_info *pi) -{ - int maxtime, maxdepth; - - /* Get plot scaling limits */ - maxtime = get_maxtime(pi); - maxdepth = get_maxdepth(pi); - - gc->leftx = 0; gc->rightx = maxtime; - gc->topy = 0; gc->bottomy = maxdepth; - - plot_text_samples(gc, pi); -} static void plot_smoothed_profile(struct graphics_context *gc, struct plot_info *pi) { diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 53d943e09..158cec286 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -246,7 +246,7 @@ void ProfileGraphicsView::plot(struct dive *dive) /* Text on top of all graphs.. */ plot_temperature_text(); - plot_depth_text(gc, pi); + plot_depth_text(); #if 0 plot_cylinder_pressure_text(gc, pi); @@ -292,6 +292,61 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } +void ProfileGraphicsView::plot_depth_text() +{ + int maxtime, maxdepth; + + /* Get plot scaling limits */ + maxtime = get_maxtime(&gc.pi); + maxdepth = get_maxdepth(&gc.pi); + + gc.leftx = 0; gc.rightx = maxtime; + gc.topy = 0; gc.bottomy = maxdepth; + + plot_text_samples(); +} + +void ProfileGraphicsView::plot_text_samples() +{ + static text_render_options_t deep = {14, SAMPLE_DEEP, CENTER, TOP}; + static text_render_options_t shallow = {14, SAMPLE_SHALLOW, CENTER, BOTTOM}; + int i; + int last = -1; + + struct plot_info* pi = &gc.pi; + + for (i = 0; i < pi->nr; i++) { + struct plot_data *entry = pi->entry + i; + + if (entry->depth < 2000) + continue; + + if ((entry == entry->max[2]) && entry->depth != last) { + plot_depth_sample(entry, &deep); + last = entry->depth; + } + + if ((entry == entry->min[2]) && entry->depth != last) { + plot_depth_sample(entry, &shallow); + last = entry->depth; + } + + if (entry->depth != last) + last = -1; + } +} + +void ProfileGraphicsView::plot_depth_sample(struct plot_data *entry,text_render_options_t *tro) +{ + int sec = entry->sec, decimals; + double d; + + d = get_depth_units(entry->depth, &decimals, NULL); + + plot_text(tro, sec, entry->depth, QString("%1").arg(d)); // , decimals, d); +} + + void ProfileGraphicsView::plot_temperature_text() { int i; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index d6c30dda2..c1c3c2837 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -99,6 +99,10 @@ private: void plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc); void plot_temperature_text(); void plot_single_temp_text(int sec, int mkelvin); + void plot_depth_text(); + void plot_text_samples(); + void plot_depth_sample(struct plot_data *entry, text_render_options_t *tro); + QColor get_sac_color(int sac, int avg_sac); QPen defaultPen; From e62eb58ab5004fdeaee56b986d7f9b4c3f23619a Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 15:42:38 -0300 Subject: [PATCH 173/226] Plotting cylinder pressure text. Signed-off-by: Tomaz Canabrava --- profile.c | 49 -------------------------------------- qt-ui/profilegraphics.cpp | 50 ++++++++++++++++++++++++++++++++++++++- qt-ui/profilegraphics.h | 2 ++ 3 files changed, 51 insertions(+), 50 deletions(-) diff --git a/profile.c b/profile.c index 67f83f0a6..15fc2a920 100644 --- a/profile.c +++ b/profile.c @@ -470,55 +470,6 @@ int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct div #if USE_GTK_UI - -static void plot_pressure_value(struct graphics_context *gc, int mbar, int sec, - int xalign, int yalign) -{ - int pressure; - const char *unit; - - pressure = get_pressure_units(mbar, &unit); - text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, xalign, yalign}; - plot_text(gc, &tro, sec, mbar, "%d %s", pressure, unit); -} - -static void plot_cylinder_pressure_text(struct graphics_context *gc, struct plot_info *pi) -{ - int i; - int mbar, cyl; - int seen_cyl[MAX_CYLINDERS] = { FALSE, }; - int last_pressure[MAX_CYLINDERS] = { 0, }; - int last_time[MAX_CYLINDERS] = { 0, }; - struct plot_data *entry; - - if (!get_cylinder_pressure_range(gc, pi)) - return; - - cyl = -1; - for (i = 0; i < pi->nr; i++) { - entry = pi->entry + i; - mbar = GET_PRESSURE(entry); - - if (!mbar) - continue; - if (cyl != entry->cylinderindex) { - cyl = entry->cylinderindex; - if (!seen_cyl[cyl]) { - plot_pressure_value(gc, mbar, entry->sec, LEFT, BOTTOM); - seen_cyl[cyl] = TRUE; - } - } - last_pressure[cyl] = mbar; - last_time[cyl] = entry->sec; - } - - for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { - if (last_time[cyl]) { - plot_pressure_value(gc, last_pressure[cyl], last_time[cyl], CENTER, TOP); - } - } -} - static void plot_deco_text(struct graphics_context *gc, struct plot_info *pi) { if (prefs.profile_calc_ceiling) { diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 158cec286..787182389 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -248,8 +248,8 @@ void ProfileGraphicsView::plot(struct dive *dive) plot_depth_text(); + plot_cylinder_pressure_text(); #if 0 - plot_cylinder_pressure_text(gc, pi); plot_deco_text(gc, pi); #endif /* Bounding box */ @@ -292,6 +292,54 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } +void ProfileGraphicsView::plot_cylinder_pressure_text() +{ + int i; + int mbar, cyl; + int seen_cyl[MAX_CYLINDERS] = { FALSE, }; + int last_pressure[MAX_CYLINDERS] = { 0, }; + int last_time[MAX_CYLINDERS] = { 0, }; + struct plot_data *entry; + struct plot_info *pi = &gc.pi; + + if (!get_cylinder_pressure_range(&gc)) + return; + + cyl = -1; + for (i = 0; i < pi->nr; i++) { + entry = pi->entry + i; + mbar = GET_PRESSURE(entry); + + if (!mbar) + continue; + if (cyl != entry->cylinderindex) { + cyl = entry->cylinderindex; + if (!seen_cyl[cyl]) { + plot_pressure_value(mbar, entry->sec, LEFT, BOTTOM); + seen_cyl[cyl] = TRUE; + } + } + last_pressure[cyl] = mbar; + last_time[cyl] = entry->sec; + } + + for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { + if (last_time[cyl]) { + plot_pressure_value(last_pressure[cyl], last_time[cyl], CENTER, TOP); + } + } +} + +void ProfileGraphicsView::plot_pressure_value(int mbar, int sec, int xalign, int yalign) +{ + int pressure; + const char *unit; + + pressure = get_pressure_units(mbar, &unit); + static text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, xalign, yalign}; + plot_text(&tro, sec, mbar, QString("%1 %2").arg(pressure).arg(unit)); +} + void ProfileGraphicsView::plot_depth_text() { int maxtime, maxdepth; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index c1c3c2837..39f98f65f 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -102,6 +102,8 @@ private: void plot_depth_text(); void plot_text_samples(); void plot_depth_sample(struct plot_data *entry, text_render_options_t *tro); + void plot_cylinder_pressure_text(); + void plot_pressure_value(int mbar, int sec, int xalign, int yalign); QColor get_sac_color(int sac, int avg_sac); From 25d65ab97d80b80465e346f749a71e1664244301 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 15:47:39 -0300 Subject: [PATCH 174/226] Plotting deco text. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- profile.c | 134 +++++----------------------------- profile.h | 2 + qt-ui/profilegraphics.cpp | 147 +++++++++++++++++++++++++++++++++++--- qt-ui/profilegraphics.h | 2 + 4 files changed, 159 insertions(+), 126 deletions(-) diff --git a/profile.c b/profile.c index 15fc2a920..2dc82e37b 100644 --- a/profile.c +++ b/profile.c @@ -220,6 +220,22 @@ int setup_temperature_limits(struct graphics_context *gc) return maxtemp && maxtemp >= mintemp; } +void setup_pp_limits(struct graphics_context *gc) +{ + int maxdepth; + + gc->leftx = 0; + gc->rightx = get_maxtime(&gc->pi); + + /* the maxdepth already includes extra vertical space - and if + * we use 1.5 times the corresponding pressure as maximum partial + * pressure the graph seems to look fine*/ + maxdepth = get_maxdepth(&gc->pi); + gc->topy = 1.5 * (maxdepth + 10000) / 10000.0 * SURFACE_PRESSURE / 1000; + gc->bottomy = -gc->topy / 20; +} + + #if 0 static void plot_smoothed_profile(struct graphics_context *gc, struct plot_info *pi) @@ -290,21 +306,6 @@ static void plot_depth_scale(struct graphics_context *gc, struct plot_info *pi) } } -static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi) -{ - int maxdepth; - - gc->leftx = 0; - gc->rightx = get_maxtime(pi); - - /* the maxdepth already includes extra vertical space - and if - * we use 1.5 times the corresponding pressure as maximum partial - * pressure the graph seems to look fine*/ - maxdepth = get_maxdepth(pi); - gc->topy = 1.5 * (maxdepth + 10000) / 10000.0 * SURFACE_PRESSURE / 1000; - gc->bottomy = -gc->topy / 20; -} - static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) { double pp, dpp, m; @@ -324,95 +325,6 @@ static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) } } -static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *pi) -{ - int i; - struct plot_data *entry; - - setup_pp_limits(gc, pi); - - if (prefs.pp_graphs.pn2) { - set_source_rgba(gc, PN2); - entry = pi->entry; - move_to(gc, entry->sec, entry->pn2); - for (i = 1; i < pi->nr; i++) { - entry++; - if (entry->pn2 < prefs.pp_graphs.pn2_threshold) - line_to(gc, entry->sec, entry->pn2); - else - move_to(gc, entry->sec, entry->pn2); - } - cairo_stroke(gc->cr); - - set_source_rgba(gc, PN2_ALERT); - entry = pi->entry; - move_to(gc, entry->sec, entry->pn2); - for (i = 1; i < pi->nr; i++) { - entry++; - if (entry->pn2 >= prefs.pp_graphs.pn2_threshold) - line_to(gc, entry->sec, entry->pn2); - else - move_to(gc, entry->sec, entry->pn2); - } - cairo_stroke(gc->cr); - } - if (prefs.pp_graphs.phe) { - set_source_rgba(gc, PHE); - entry = pi->entry; - move_to(gc, entry->sec, entry->phe); - for (i = 1; i < pi->nr; i++) { - entry++; - if (entry->phe < prefs.pp_graphs.phe_threshold) - line_to(gc, entry->sec, entry->phe); - else - move_to(gc, entry->sec, entry->phe); - } - cairo_stroke(gc->cr); - - set_source_rgba(gc, PHE_ALERT); - entry = pi->entry; - move_to(gc, entry->sec, entry->phe); - for (i = 1; i < pi->nr; i++) { - entry++; - if (entry->phe >= prefs.pp_graphs.phe_threshold) - line_to(gc, entry->sec, entry->phe); - else - move_to(gc, entry->sec, entry->phe); - } - cairo_stroke(gc->cr); - } - if (prefs.pp_graphs.po2) { - set_source_rgba(gc, PO2); - entry = pi->entry; - move_to(gc, entry->sec, entry->po2); - for (i = 1; i < pi->nr; i++) { - entry++; - if (entry->po2 < prefs.pp_graphs.po2_threshold) - line_to(gc, entry->sec, entry->po2); - else - move_to(gc, entry->sec, entry->po2); - } - cairo_stroke(gc->cr); - - set_source_rgba(gc, PO2_ALERT); - entry = pi->entry; - move_to(gc, entry->sec, entry->po2); - for (i = 1; i < pi->nr; i++) { - entry++; - if (entry->po2 >= prefs.pp_graphs.po2_threshold) - line_to(gc, entry->sec, entry->po2); - else - move_to(gc, entry->sec, entry->po2); - } - cairo_stroke(gc->cr); - } -} - - - - - - /* gets both the actual start and end pressure as well as the scaling factors */ #endif /* USE_GTK_UI */ @@ -468,20 +380,6 @@ int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct div return airuse / atm * 60 / duration; } -#if USE_GTK_UI - -static void plot_deco_text(struct graphics_context *gc, struct plot_info *pi) -{ - if (prefs.profile_calc_ceiling) { - float x = gc->leftx + (gc->rightx - gc->leftx) / 2; - float y = gc->topy = 1.0; - text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, CENTER, -0.2}; - gc->bottomy = 0.0; - plot_text(gc, &tro, x, y, "GF %.0f/%.0f", prefs.gflow * 100, prefs.gfhigh * 100); - } -} -#endif /* USE_GTK_UI */ - static void analyze_plot_info_minmax_minute(struct plot_data *entry, struct plot_data *first, struct plot_data *last, int index) { struct plot_data *p = entry; diff --git a/profile.h b/profile.h index 73c5152f9..89324530a 100644 --- a/profile.h +++ b/profile.h @@ -63,6 +63,8 @@ int get_maxdepth(struct plot_info *pi); int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive); +void setup_pp_limits(struct graphics_context *gc); + #define ALIGN_LEFT 1 #define ALIGN_RIGHT 2 diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 787182389..5425cae32 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -249,9 +249,9 @@ void ProfileGraphicsView::plot(struct dive *dive) plot_depth_text(); plot_cylinder_pressure_text(); -#if 0 - plot_deco_text(gc, pi); -#endif + + plot_deco_text(); + /* Bounding box */ QColor color = profile_color[TIME_GRID].at(0); QPen pen = QPen(color); @@ -269,12 +269,14 @@ void ProfileGraphicsView::plot(struct dive *dive) text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; plot_text(&computer, gc.leftx, gc.bottomy, nickname); } -#if 0 - if (PP_GRAPHS_ENABLED) { - plot_pp_gas_profile(gc, pi); - plot_pp_text(gc, pi); - } + + //if (PP_GRAPHS_ENABLED) { + plot_pp_gas_profile(); + // plot_pp_text(gc, pi); + //} + +#if 0 /* now shift the translation back by half the margin; * this way we can draw the vertical scales on both sides */ cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); @@ -292,6 +294,135 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } +void ProfileGraphicsView::plot_pp_gas_profile() +{ + int i; + struct plot_data *entry; + struct plot_info *pi = &gc.pi; + + setup_pp_limits(&gc); + QColor c; + QPointF from, to; + //if (prefs.pp_graphs.pn2) { + c = profile_color[PN2].first(); + entry = pi->entry; + from = QPointF(SCALEGC(entry->sec, entry->pn2)); + for (i = 1; i < pi->nr; i++) { + entry++; + if (entry->pn2 < prefs.pp_graphs.pn2_threshold){ + to = QPointF(SCALEGC(entry->sec, entry->pn2)); + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + item->setPen(QPen(c)); + scene()->addItem(item); + from = to; + } + else{ + from = QPointF(SCALEGC(entry->sec, entry->pn2)); + } + } + + c = profile_color[PN2_ALERT].first(); + entry = pi->entry; + from = QPointF(SCALEGC(entry->sec, entry->pn2)); + for (i = 1; i < pi->nr; i++) { + entry++; + if (entry->pn2 >= prefs.pp_graphs.pn2_threshold){ + to = QPointF(SCALEGC(entry->sec, entry->pn2)); + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + item->setPen(QPen(c)); + scene()->addItem(item); + from = to; + } + else{ + from = QPointF(SCALEGC(entry->sec, entry->pn2)); + } + } + //} + + //if (prefs.pp_graphs.phe) { + c = profile_color[PHE].first(); + entry = pi->entry; + + from = QPointF(SCALEGC(entry->sec, entry->phe)); + for (i = 1; i < pi->nr; i++) { + entry++; + if (entry->phe < prefs.pp_graphs.phe_threshold){ + to = QPointF(SCALEGC(entry->sec, entry->phe)); + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + item->setPen(QPen(c)); + scene()->addItem(item); + from = to; + } + else{ + from = QPointF(SCALEGC(entry->sec, entry->phe)); + } + } + + c = profile_color[PHE_ALERT].first(); + entry = pi->entry; + from = QPointF(SCALEGC(entry->sec, entry->phe)); + for (i = 1; i < pi->nr; i++) { + entry++; + if (entry->phe >= prefs.pp_graphs.phe_threshold){ + to = QPointF(SCALEGC(entry->sec, entry->phe)); + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + item->setPen(QPen(c)); + scene()->addItem(item); + from = to; + } + else{ + from = QPointF(SCALEGC(entry->sec, entry->phe)); + } + } + //} + //if (prefs.pp_graphs.po2) { + c = profile_color[PO2].first(); + entry = pi->entry; + from = QPointF(SCALEGC(entry->sec, entry->po2)); + for (i = 1; i < pi->nr; i++) { + entry++; + if (entry->po2 < prefs.pp_graphs.po2_threshold){ + to = QPointF(SCALEGC(entry->sec, entry->po2)); + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + item->setPen(QPen(c)); + scene()->addItem(item); + from = to; + } + else{ + from = QPointF(SCALEGC(entry->sec, entry->po2)); + } + } + + c = profile_color[PO2_ALERT].first(); + entry = pi->entry; + from = QPointF(SCALEGC(entry->sec, entry->po2)); + for (i = 1; i < pi->nr; i++) { + entry++; + if (entry->po2 >= prefs.pp_graphs.po2_threshold){ + to = QPointF(SCALEGC(entry->sec, entry->po2)); + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + item->setPen(QPen(c)); + scene()->addItem(item); + from = to; + } + else{ + from = QPointF(SCALEGC(entry->sec, entry->po2)); + } + } + //} +} + +void ProfileGraphicsView::plot_deco_text() +{ + if (prefs.profile_calc_ceiling) { + float x = gc.leftx + (gc.rightx - gc.leftx) / 2; + float y = gc.topy = 1.0; + static text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, CENTER, -0.2}; + gc.bottomy = 0.0; + plot_text(&tro, x, y, QString("GF %1/%2").arg(prefs.gflow * 100).arg(prefs.gfhigh * 100)); + } +} + void ProfileGraphicsView::plot_cylinder_pressure_text() { int i; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 39f98f65f..6ed41de1e 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -104,6 +104,8 @@ private: void plot_depth_sample(struct plot_data *entry, text_render_options_t *tro); void plot_cylinder_pressure_text(); void plot_pressure_value(int mbar, int sec, int xalign, int yalign); + void plot_deco_text(); + void plot_pp_gas_profile(); QColor get_sac_color(int sac, int avg_sac); From b794c23099f97cefd0a65b070fc9aa9dd5523e87 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 17:30:16 -0300 Subject: [PATCH 175/226] Plot pp Text This patch plots the PP text, but when I plotted I realized that the gc.pi.mapp is being calculated wrong, probably something went wrong on the calculations - it's comming zered always. So, only one line & text is plotted. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 23 ++++++++++++++++++++++- qt-ui/profilegraphics.h | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 5425cae32..91e230e91 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -273,7 +273,7 @@ void ProfileGraphicsView::plot(struct dive *dive) //if (PP_GRAPHS_ENABLED) { plot_pp_gas_profile(); - // plot_pp_text(gc, pi); + plot_pp_text(); //} #if 0 @@ -294,6 +294,27 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } +void ProfileGraphicsView::plot_pp_text() +{ + double pp, dpp, m; + int hpos; + static text_render_options_t tro = {PP_TEXT_SIZE, PP_LINES, LEFT, MIDDLE}; + + setup_pp_limits(&gc); + pp = floor(gc.pi.maxpp * 10.0) / 10.0 + 0.2; + dpp = pp > 4 ? 1.0 : 0.5; + hpos = gc.pi.entry[gc.pi.nr - 1].sec; + QColor c = profile_color[PP_LINES].first(); + + qDebug() << pp << dpp; + for (m = 0.0; m <= pp; m += dpp) { + QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, m), SCALEGC(hpos, m)); + item->setPen(QPen(c)); + scene()->addItem(item); + plot_text(&tro, hpos + 30, m, QString::number(m)); + } +} + void ProfileGraphicsView::plot_pp_gas_profile() { int i; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 6ed41de1e..ca0aaedf9 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -106,7 +106,7 @@ private: void plot_pressure_value(int mbar, int sec, int xalign, int yalign); void plot_deco_text(); void plot_pp_gas_profile(); - + void plot_pp_text(); QColor get_sac_color(int sac, int avg_sac); QPen defaultPen; From cb8198b5243df425b6baa4d2fa4b5edb619b2855 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 18:02:52 -0300 Subject: [PATCH 176/226] Plot the vertical ruler on the left of the profile. Plot the numbers on the left of the profile. It seems that everythign is being plotted - But I can see that there are coordinate-errors on the code. ( the GTK one plots some curves below of the dive, but the Qt one is overlapping - probably the way that I'm using the gc information) Need to investigate a bit. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- profile.c | 25 ------------------------ qt-ui/profilegraphics.cpp | 41 +++++++++++++++++++++++++++++++++------ qt-ui/profilegraphics.h | 2 ++ 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/profile.c b/profile.c index 2dc82e37b..980db4f54 100644 --- a/profile.c +++ b/profile.c @@ -281,31 +281,6 @@ static void plot_minmax_profile(struct graphics_context *gc, struct plot_info *p plot_minmax_profile_minute(gc, pi, 0); } -static void plot_depth_scale(struct graphics_context *gc, struct plot_info *pi) -{ - int i, maxdepth, marker; - static const text_render_options_t tro = {DEPTH_TEXT_SIZE, SAMPLE_DEEP, RIGHT, MIDDLE}; - - /* Depth markers: every 30 ft or 10 m*/ - maxdepth = get_maxdepth(pi); - gc->topy = 0; gc->bottomy = maxdepth; - - switch (prefs.units.length) { - case METERS: marker = 10000; break; - case FEET: marker = 9144; break; /* 30 ft */ - } - set_source_rgba(gc, DEPTH_GRID); - /* don't write depth labels all the way to the bottom as - * there may be other graphs below the depth plot (like - * partial pressure graphs) where this would look out - * of place - so we only make sure that we print the next - * marker below the actual maxdepth of the dive */ - for (i = marker; i <= pi->maxdepth + marker; i += marker) { - double d = get_depth_units(i, NULL, NULL); - plot_text(gc, &tro, -0.002, i, "%.0f", d); - } -} - static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) { double pp, dpp, m; diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 91e230e91..625dfdf10 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -276,16 +276,18 @@ void ProfileGraphicsView::plot(struct dive *dive) plot_pp_text(); //} -#if 0 + /* now shift the translation back by half the margin; * this way we can draw the vertical scales on both sides */ - cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); - gc->maxx += drawing_area->x; - gc->leftx = -(drawing_area->x / drawing_area->width) / 2.0; - gc->rightx = 1.0 - gc->leftx; + //cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); - plot_depth_scale(gc, pi); + //gc->maxx += drawing_area->x; + //gc->leftx = -(drawing_area->x / drawing_area->width) / 2.0; + //gc->rightx = 1.0 - gc->leftx; + plot_depth_scale(); + +#if 0 if (gc->printer) { free(pi->entry); last_pi_entry = pi->entry = NULL; @@ -294,6 +296,33 @@ void ProfileGraphicsView::plot(struct dive *dive) #endif } +void ProfileGraphicsView::plot_depth_scale() +{ + int i, maxdepth, marker; + static text_render_options_t tro = {DEPTH_TEXT_SIZE, SAMPLE_DEEP, RIGHT, MIDDLE}; + + /* Depth markers: every 30 ft or 10 m*/ + maxdepth = get_maxdepth(&gc.pi); + gc.topy = 0; gc.bottomy = maxdepth; + + switch (prefs.units.length) { + case units::METERS: marker = 10000; break; + case units::FEET: marker = 9144; break; /* 30 ft */ + } + + QColor c(profile_color[DEPTH_GRID].first()); + + /* don't write depth labels all the way to the bottom as + * there may be other graphs below the depth plot (like + * partial pressure graphs) where this would look out + * of place - so we only make sure that we print the next + * marker below the actual maxdepth of the dive */ + for (i = marker; i <= gc.pi.maxdepth + marker; i += marker) { + double d = get_depth_units(i, NULL, NULL); + plot_text(&tro, -0.002, i, QString::number(d)); + } +} + void ProfileGraphicsView::plot_pp_text() { double pp, dpp, m; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index ca0aaedf9..394a97a9f 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -107,6 +107,8 @@ private: void plot_deco_text(); void plot_pp_gas_profile(); void plot_pp_text(); + void plot_depth_scale(); + QColor get_sac_color(int sac, int avg_sac); QPen defaultPen; From 5978afbcd88965aae8d67b5d3f20bd9d18c0bfb5 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 18:29:18 -0300 Subject: [PATCH 177/226] Use a Default pen to make the configuration easier. Created a default pen that has 'cosmetic' enabled, A cosmetic line doesn't change it's width no matter what zoom level we apply. Also , changed everything that used a line to have that as default pen instead. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 93 ++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 625dfdf10..8fa8e94a6 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -129,6 +129,7 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent defaultPen.setJoinStyle(Qt::RoundJoin); defaultPen.setCapStyle(Qt::RoundCap); defaultPen.setWidth(2); + defaultPen.setCosmetic(true); fill_profile_color(); } @@ -253,9 +254,8 @@ void ProfileGraphicsView::plot(struct dive *dive) plot_deco_text(); /* Bounding box */ - QColor color = profile_color[TIME_GRID].at(0); - QPen pen = QPen(color); - pen.setWidth(1); + QPen pen = defaultPen; + pen.setColor(profile_color[TIME_GRID].at(0)); QGraphicsRectItem *rect = new QGraphicsRectItem(scene()->sceneRect()); rect->setPen(pen); scene()->addItem(rect); @@ -338,7 +338,9 @@ void ProfileGraphicsView::plot_pp_text() qDebug() << pp << dpp; for (m = 0.0; m <= pp; m += dpp) { QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, m), SCALEGC(hpos, m)); - item->setPen(QPen(c)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); scene()->addItem(item); plot_text(&tro, hpos + 30, m, QString::number(m)); } @@ -362,7 +364,9 @@ void ProfileGraphicsView::plot_pp_gas_profile() if (entry->pn2 < prefs.pp_graphs.pn2_threshold){ to = QPointF(SCALEGC(entry->sec, entry->pn2)); QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - item->setPen(QPen(c)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); scene()->addItem(item); from = to; } @@ -379,7 +383,9 @@ void ProfileGraphicsView::plot_pp_gas_profile() if (entry->pn2 >= prefs.pp_graphs.pn2_threshold){ to = QPointF(SCALEGC(entry->sec, entry->pn2)); QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - item->setPen(QPen(c)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); scene()->addItem(item); from = to; } @@ -399,7 +405,9 @@ void ProfileGraphicsView::plot_pp_gas_profile() if (entry->phe < prefs.pp_graphs.phe_threshold){ to = QPointF(SCALEGC(entry->sec, entry->phe)); QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - item->setPen(QPen(c)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); scene()->addItem(item); from = to; } @@ -416,7 +424,9 @@ void ProfileGraphicsView::plot_pp_gas_profile() if (entry->phe >= prefs.pp_graphs.phe_threshold){ to = QPointF(SCALEGC(entry->sec, entry->phe)); QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - item->setPen(QPen(c)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); scene()->addItem(item); from = to; } @@ -434,7 +444,9 @@ void ProfileGraphicsView::plot_pp_gas_profile() if (entry->po2 < prefs.pp_graphs.po2_threshold){ to = QPointF(SCALEGC(entry->sec, entry->po2)); QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - item->setPen(QPen(c)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); scene()->addItem(item); from = to; } @@ -676,7 +688,9 @@ void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divec prev_pr = GET_PRESSURE(entry - 1); QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC((entry-1)->sec, prev_pr), SCALEGC(entry->sec, mbar)); - item->setPen(QPen(c, 2)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); scene()->addItem(item); } else { first_plot = FALSE; @@ -686,7 +700,9 @@ void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divec } else { to = QPointF(SCALEGC(entry->sec, mbar)); QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - item->setPen(QPen(c, 2)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); scene()->addItem(item); } @@ -831,12 +847,13 @@ void ProfileGraphicsView::plot_depth_profile() last_gc = gc; - QColor color; - color = profile_color[TIME_GRID].at(0); + QColor c = profile_color[TIME_GRID].at(0); for (i = incr; i < maxtime; i += incr) { - QGraphicsLineItem *line = new QGraphicsLineItem(SCALEGC(i, 0), SCALEGC(i, 1)); - line->setPen(QPen(color)); - scene()->addItem(line); + QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(i, 0), SCALEGC(i, 1)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); + scene()->addItem(item); } /* now the text on the time markers */ @@ -864,23 +881,27 @@ void ProfileGraphicsView::plot_depth_profile() } maxline = MAX(gc.pi.maxdepth + marker, maxdepth * 2 / 3); - color = profile_color[DEPTH_GRID].at(0); + c = profile_color[DEPTH_GRID].at(0); for (i = marker; i < maxline; i += marker) { - QGraphicsLineItem *line = new QGraphicsLineItem(SCALEGC(0, i), SCALEGC(1, i)); - line->setPen(QPen(color)); - scene()->addItem(line); + QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, i), SCALEGC(1, i)); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); + scene()->addItem(item); } gc.leftx = 0; gc.rightx = maxtime; - color = profile_color[MEAN_DEPTH].at(0); + c = profile_color[MEAN_DEPTH].at(0); /* Show mean depth */ if (! gc.printer) { - QGraphicsLineItem *line = new QGraphicsLineItem(SCALEGC(0, gc.pi.meandepth), + QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, gc.pi.meandepth), SCALEGC(gc.pi.entry[gc.pi.nr - 1].sec, gc.pi.meandepth)); - line->setPen(QPen(color)); - scene()->addItem(line); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); + scene()->addItem(item); } #if 0 @@ -924,7 +945,7 @@ void ProfileGraphicsView::plot_depth_profile() neatFill = new QGraphicsPolygonItem(); neatFill->setPolygon(p); neatFill->setBrush(QBrush(pat)); - neatFill->setPen(QPen(QBrush(),0)); + neatFill->setPen(QPen(QBrush(Qt::transparent),0)); scene()->addItem(neatFill); @@ -955,7 +976,7 @@ void ProfileGraphicsView::plot_depth_profile() neatFill = new QGraphicsPolygonItem(); neatFill->setBrush(QBrush(pat)); neatFill->setPolygon(p); - neatFill->setPen(QPen(QBrush(),0)); + neatFill->setPen(QPen(QBrush(Qt::NoBrush),0)); scene()->addItem(neatFill); //} @@ -978,7 +999,7 @@ void ProfileGraphicsView::plot_depth_profile() p.append(QPointF(SCALEGC((entry-1)->sec, 0))); neatFill = new QGraphicsPolygonItem(); neatFill->setPolygon(p); - neatFill->setPen(QPen(QBrush(),0)); + neatFill->setPen(QPen(QBrush(Qt::NoBrush),0)); neatFill->setBrush(pat); scene()->addItem(neatFill); //} @@ -1002,7 +1023,7 @@ void ProfileGraphicsView::plot_depth_profile() neatFill = new QGraphicsPolygonItem(); neatFill->setPolygon(p); - neatFill->setPen(QPen(QBrush(),0)); + neatFill->setPen(QPen(QBrush(Qt::NoBrush),0)); neatFill->setBrush(QBrush(pat)); scene()->addItem(neatFill); @@ -1015,9 +1036,11 @@ void ProfileGraphicsView::plot_depth_profile() * representing the vertical velocity, so we need to * chop this into short segments */ depth = entry->depth; - QGraphicsLineItem *colorLine = new QGraphicsLineItem(SCALEGC(entry[-1].sec, entry[-1].depth), SCALEGC(sec, depth)); - colorLine->setPen(QPen(QBrush(profile_color[ (color_indice_t) (VELOCITY_COLORS_START_IDX + entry->velocity)].first()), 2)); - scene()->addItem(colorLine); + QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(entry[-1].sec, entry[-1].depth), SCALEGC(sec, depth)); + QPen pen(defaultPen); + pen.setColor(profile_color[ (color_indice_t) (VELOCITY_COLORS_START_IDX + entry->velocity)].first()); + item->setPen(pen); + scene()->addItem(item); } } @@ -1033,7 +1056,6 @@ void ProfileGraphicsView::plot_text(text_render_options_t *tro, double x, double item->setPos(point.x() + dx, point.y() +dy); item->setBrush(QBrush(profile_color[tro->color].first())); - item->setPen(QPen(profile_color[BACKGROUND].first())); item->setFlag(QGraphicsItem::ItemIgnoresTransformations); scene()->addItem(item); } @@ -1071,7 +1093,9 @@ void ProfileGraphicsView::plot_temperature_profile() to = QPointF(SCALEGC(sec, mkelvin)); //qDebug() << from << to; QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - item->setPen(QPen(color, 2*plot_scale)); + QPen pen(defaultPen); + pen.setColor(color); + item->setPen(pen); scene()->addItem(item); from = to; } @@ -1096,10 +1120,8 @@ void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(toolTip, this); textItem->setPos(SPACING + ICON_SMALL + SPACING, yValue); - textItem->setPen(QPen(Qt::white, 1)); textItem->setBrush(QBrush(Qt::white)); textItem->setFlag(ItemIgnoresTransformations); - toolTips[toolTip] = qMakePair(iconItem, textItem); expand(); } @@ -1270,6 +1292,7 @@ EventItem::EventItem(QGraphicsItem* parent): QGraphicsPolygonItem(parent) defaultPen.setJoinStyle(Qt::RoundJoin); defaultPen.setCapStyle(Qt::RoundCap); defaultPen.setWidth(2); + defaultPen.setCosmetic(true); QPen pen = defaultPen; pen.setBrush(QBrush(profile_color[ALERT_BG].first())); From 9cc089f9f61a876057799e540c56178d4449fdce Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 18:45:58 -0300 Subject: [PATCH 178/226] Removed unused code that I'm sure it's safe to delete. Signed-off-by: Tomaz Canabrava --- profile.c | 70 --------------------------------------- qt-ui/profilegraphics.cpp | 2 -- qt-ui/profilegraphics.h | 28 ++-------------- 3 files changed, 3 insertions(+), 97 deletions(-) diff --git a/profile.c b/profile.c index 980db4f54..ed5d01850 100644 --- a/profile.c +++ b/profile.c @@ -41,40 +41,6 @@ int x_abs(double x) { return x - last_gc.drawing_area.x; } - -static void move_to(struct graphics_context *gc, double x, double y) -{ - cairo_move_to(gc->cr, SCALE(gc, x, y)); -} - -static void line_to(struct graphics_context *gc, double x, double y) -{ - cairo_line_to(gc->cr, SCALE(gc, x, y)); -} - -static void set_source_rgba(struct graphics_context *gc, color_indice_t c) -{ - const color_t *col = &profile_color[c]; - struct rgba rgb = col->media[gc->printer]; - double r = rgb.r; - double g = rgb.g; - double b = rgb.b; - double a = rgb.a; - - cairo_set_source_rgba(gc->cr, r, g, b, a); -} - -void init_profile_background(struct graphics_context *gc) -{ - set_source_rgba(gc, BACKGROUND); -} - -static void pattern_add_color_stop_rgba(struct graphics_context *gc, cairo_pattern_t *pat, double o, color_indice_t c) -{ - const color_t *col = &profile_color[c]; - struct rgba rgb = col->media[gc->printer]; - cairo_pattern_add_color_stop_rgba(pat, o, rgb.r, rgb.g, rgb.b, rgb.a); -} #endif /* USE_GTK_UI */ /* debugging tool - not normally used */ @@ -281,27 +247,6 @@ static void plot_minmax_profile(struct graphics_context *gc, struct plot_info *p plot_minmax_profile_minute(gc, pi, 0); } -static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) -{ - double pp, dpp, m; - int hpos; - static const text_render_options_t tro = {PP_TEXT_SIZE, PP_LINES, LEFT, MIDDLE}; - - setup_pp_limits(gc, pi); - pp = floor(pi->maxpp * 10.0) / 10.0 + 0.2; - dpp = pp > 4 ? 1.0 : 0.5; - hpos = pi->entry[pi->nr - 1].sec; - set_source_rgba(gc, PP_LINES); - for (m = 0.0; m <= pp; m += dpp) { - move_to(gc, 0, m); - line_to(gc, hpos, m); - cairo_stroke(gc->cr); - plot_text(gc, &tro, hpos + 30, m, "%.1f", m); - } -} - -/* gets both the actual start and end pressure as well as the scaling factors */ - #endif /* USE_GTK_UI */ int get_cylinder_pressure_range(struct graphics_context *gc) @@ -1140,21 +1085,6 @@ struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, s return analyze_plot_info(pi); } -#if USE_GTK_UI -static void plot_set_scale(scale_mode_t scale) -{ - switch (scale) { - default: - case SC_SCREEN: - plot_scale = SCALE_SCREEN; - break; - case SC_PRINT: - plot_scale = SCALE_PRINT; - break; - } -} -#endif - /* make sure you pass this the FIRST dc - it just walks the list */ static int nr_dcs(struct divecomputer *main) { diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 8fa8e94a6..ad6cbb172 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -335,7 +335,6 @@ void ProfileGraphicsView::plot_pp_text() hpos = gc.pi.entry[gc.pi.nr - 1].sec; QColor c = profile_color[PP_LINES].first(); - qDebug() << pp << dpp; for (m = 0.0; m <= pp; m += dpp) { QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, m), SCALEGC(hpos, m)); QPen pen(defaultPen); @@ -1091,7 +1090,6 @@ void ProfileGraphicsView::plot_temperature_profile() } if (last) { to = QPointF(SCALEGC(sec, mkelvin)); - //qDebug() << from << to; QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); QPen pen(defaultPen); pen.setColor(color); diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 394a97a9f..268226843 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -11,31 +11,9 @@ struct graphics_context; struct plot_info; typedef struct text_render_options text_render_options_t; -/**! - * - * Hookay, so, if you wanna extend the ToolTips that are displayed - * in the Profile Graph, there's one 'toolTip' widget already on it, - * you can just pass it to your Reimplementation of QGraphiscItem - * and do the following: - * - * EventItem::setController(ToolTipItem *c) - * { - * controller = c; - * } - * - * void EventItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event) - * { - * controller->addToolTip(text, icon); - * } - * - * void EventItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) - * { - * controller->removeToolTip(text); - * } - * - * Remember to removeToolTip when you don't want it to be displayed. - * - **/ +/* To use a tooltip, simply ->setToolTip on the QGraphicsItem that you want + * or, if it's a "global" tooltip, set it on the mouseMoveEvent of the ProfileGraphicsView. + */ class ToolTipItem :public QObject, public QGraphicsPathItem { Q_OBJECT From acdb5d97ebe88a75933089a80b1ce844c4375851 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 9 May 2013 19:59:55 -0300 Subject: [PATCH 179/226] The Zoom is working just like the GTK Version. This code enables Zoom in / Out with the Wheel, and it also enables panning by moving the mouse around. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 38 +++++++++++++++++++++++++++++++------- qt-ui/profilegraphics.h | 2 ++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index ad6cbb172..44af49e7c 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -114,7 +114,7 @@ extern struct ev_select *ev_namelist; extern int evn_allocated; extern int evn_used; -ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) +ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) , dive(0) { gc.printer = false; setScene(new QGraphicsScene()); @@ -131,9 +131,27 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent defaultPen.setWidth(2); defaultPen.setCosmetic(true); + setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); + setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); + fill_profile_color(); } +void ProfileGraphicsView::wheelEvent(QWheelEvent* event) +{ + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + + // Scale the view / do the zoom + double scaleFactor = 1.15; + if(event->delta() > 0) { + // Zoom in + scale(scaleFactor, scaleFactor); + } else { + // Zooming out + scale(1.0 / scaleFactor, 1.0 / scaleFactor); + } +} + void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) { toolTip->clear(); @@ -146,6 +164,10 @@ void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) if (!item->toolTip().isEmpty()) toolTip->addToolTip(item->toolTip()); } + + // Pan on mouseMove code. + ensureVisible(event->pos().x(), event->pos().y(), 10, 10, 100, 100); + QGraphicsView::mouseMoveEvent(event); } @@ -172,13 +194,16 @@ static void plot_set_scale(scale_mode_t scale) } } -void ProfileGraphicsView::plot(struct dive *dive) +void ProfileGraphicsView::plot(struct dive *d) { scene()->clear(); + dive = d; if(!dive) return; + scene()->setSceneRect(0,0, viewport()->width()-50, viewport()->height()-50); + QSettings s; s.beginGroup("ProfileMap"); QPointF toolTipPos = s.value("tooltip_position", QPointF(0,0)).toPointF(); @@ -294,6 +319,9 @@ void ProfileGraphicsView::plot(struct dive *dive) pi->nr = 0; } #endif + + QRectF curerntRect = scene()->itemsBoundingRect(); + scene()->setSceneRect( -10, -10, curerntRect.width() + 10, curerntRect.height() +10 ); } void ProfileGraphicsView::plot_depth_scale() @@ -1061,11 +1089,7 @@ void ProfileGraphicsView::plot_text(text_render_options_t *tro, double x, double void ProfileGraphicsView::resizeEvent(QResizeEvent *event) { - // Fits the scene's rectangle on the view. - // I can pass some parameters to this - - // like Qt::IgnoreAspectRatio or Qt::KeepAspectRatio - QRectF r = scene()->sceneRect(); - fitInView (r.x() - 50, r.y() -50, r.width() + 100, r.height() + 100); // do a little bit of spacing; + plot(dive); } void ProfileGraphicsView::plot_temperature_profile() diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 268226843..76179b956 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -67,6 +67,7 @@ public: protected: void resizeEvent(QResizeEvent *event); void mouseMoveEvent(QMouseEvent* event); + void wheelEvent(QWheelEvent* event); private: void plot_depth_profile(); @@ -93,6 +94,7 @@ private: QBrush defaultBrush; ToolTipItem *toolTip; graphics_context gc; + struct dive *dive; }; #endif From 45afd712e81910cd1995f64ea605ef0b4a114ede Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 9 May 2013 12:18:01 -0700 Subject: [PATCH 180/226] Add a "distclean" target to make. Right now, it does the same as confclean Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- Rules.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Rules.mk b/Rules.mk index d5bc31246..8dc95c08d 100644 --- a/Rules.mk +++ b/Rules.mk @@ -218,6 +218,8 @@ confclean: clean rm -f $(CONFIGFILE) rm -rf .dep +distclean: confclean + ifneq ($(CONFIGURED)$(CONFIGURING),) .dep/%.o.dep: %.cpp @mkdir -p $(@D) From aea8493165a98d1653c7adf475ca2267d505bfe8 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 9 May 2013 12:18:02 -0700 Subject: [PATCH 181/226] Add a make rule (creator-files) that creates files for using Qt Creator Qt Creator cannot import Subsurface directly because our buildsystem is not any of the three that it understands (qmake, cmake, autotools). So, instead, we can create the files Creator uses for "Other Project" projects. The files are: - subsurface.config: the #defines from the command line (-D args) - subsurface.creator: an XDG Desktop-style file with Creator settings - subsurface.files: the list of source and header files - subsurface.includes: the include paths (-I args) They are also added to .gitignore, alongside the *.user file that Creator uses to store per-user settings (editor configuration). Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- .gitignore | 5 +++++ Rules.mk | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/.gitignore b/.gitignore index 5a90c6d4e..0d488491e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,8 @@ packaging/windows/subsurface.nsi packaging/macos/Info.plist config.cache *.qrc.cpp +/subsurface.config +/subsurface.creator +/subsurface.creator.user +/subsurface.files +/subsurface.includes diff --git a/Rules.mk b/Rules.mk index 8dc95c08d..c0ef1ee9e 100644 --- a/Rules.mk +++ b/Rules.mk @@ -38,6 +38,9 @@ MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o) ALL_OBJS = $(OBJS) $(MOC_OBJS) +# Files for using Qt Creator +CREATOR_FILES = subsurface.config subsurface.creator subsurface.files subsurface.includes + all: $(NAME) $(NAME): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST) @@ -219,6 +222,19 @@ confclean: clean rm -rf .dep distclean: confclean + rm -f $(CREATOR_FILES) + +.PHONY: creator-files +creator-files: $(CREATOR_FILES) +subsurface.files: Makefile $(CONFIGFILE) + echo $(wildcard *.h) $(HEADERS) $(SOURCES) | tr ' ' '\n' | sort | uniq > subsurface.files +subsurface.config: Makefile $(CONFIGFILE) + echo $(patsubst -D%,%,$(filter -D%, $(CXXFLAGS) $(CFLAGS) $(EXTRA_FLAGS))) | tr ' ' '\n' | sort | uniq > subsurface.config +subsurface.includes: Makefile $(CONFIGFILE) + echo $$PWD > subsurface.includes + echo $(patsubst -I%,%,$(filter -I%, $(CXXFLAGS) $(CFLAGS) $(EXTRA_FLAGS))) | tr ' ' '\n' | sort | uniq >> subsurface.includes +subsurface.creator: + echo '[General]' > subsurface.creator ifneq ($(CONFIGURED)$(CONFIGURING),) .dep/%.o.dep: %.cpp From ed6a54f5066f3220ce357f9cf7686fc4d72079dd Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 9 May 2013 21:37:21 -0700 Subject: [PATCH 182/226] Enable defautl_filename in settings We don't have a UI to set it, yet, so you have to manually set it in the config file, but once you do that it works... Signed-off-by: Dirk Hohndel --- qt-gui.cpp | 12 ++++++++++++ qt-ui/mainwindow.cpp | 9 +++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/qt-gui.cpp b/qt-gui.cpp index 1e2c86e69..5d858d733 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -27,6 +27,7 @@ #include #include #include +#include class Translator: public QTranslator { @@ -65,6 +66,7 @@ void init_qt_ui(int *argcp, char ***argvp) void init_ui(int *argcp, char ***argvp) { + QVariant v; application = new QApplication(*argcp, *argvp); #if QT_VERSION < 0x050000 @@ -75,6 +77,16 @@ void init_ui(int *argcp, char ***argvp) QTextCodec::setCodecForCStrings(QTextCodec::codecForMib(106)); #endif + QSettings settings("hohndel.org","subsurface"); + settings.beginGroup("GeneralSettings"); + v = settings.value(QString("default_filename")); + if (v.isValid()) { + QString name = v.toString(); + prefs.default_filename = strdup(name.toUtf8()); + qDebug("default filename %s", prefs.default_filename); + } + settings.endGroup(); + #if 0 subsurface_open_conf(); diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 56ae64b0c..160a657da 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -402,12 +402,6 @@ void MainWindow::readSettings() prefs.divelist_font = strdup(v.toString); #endif -#if DONT_KNOW_HOW_TO_DO_THAT - v = settings.value(QString("default_filename")); - if (v.isValid()) - prefs.default_filename = strdup(v.toString); -#endif - #if ONCE_WE_HAVE_MAPS v = settings.value(QString_int("map_provider")); if(v.isValid()) @@ -467,6 +461,9 @@ void MainWindow::writeSettings() SAVE_VALUE("gflow", gflow); SAVE_VALUE("gfhigh", gfhigh); settings.endGroup(); + settings.beginGroup("GeneralSettings"); + SAVE_VALUE("default_filename", default_filename); + settings.endGroup(); } void MainWindow::closeEvent(QCloseEvent *event) From 93d3af768b86602b3b13062705cb2d12db82b5a2 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 10 May 2013 10:30:24 -0300 Subject: [PATCH 183/226] Uses the correct rectangle to englobe the profile grid. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 44af49e7c..4d900aa73 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -249,9 +249,9 @@ void ProfileGraphicsView::plot(struct dive *d) */ calculate_max_limits(dive, dc, &gc); - QRectF drawing_area = scene()->sceneRect(); - gc.maxx = (drawing_area.width() - 2 * drawing_area.x()); - gc.maxy = (drawing_area.height() - 2 * drawing_area.y()); + QRectF profile_grid_area = scene()->sceneRect(); + gc.maxx = (profile_grid_area.width() - 2 * profile_grid_area.x()); + gc.maxy = (profile_grid_area.height() - 2 * profile_grid_area.y()); dc = select_dc(dc); @@ -281,7 +281,7 @@ void ProfileGraphicsView::plot(struct dive *d) /* Bounding box */ QPen pen = defaultPen; pen.setColor(profile_color[TIME_GRID].at(0)); - QGraphicsRectItem *rect = new QGraphicsRectItem(scene()->sceneRect()); + QGraphicsRectItem *rect = new QGraphicsRectItem(profile_grid_area); rect->setPen(pen); scene()->addItem(rect); @@ -296,10 +296,10 @@ void ProfileGraphicsView::plot(struct dive *d) } - //if (PP_GRAPHS_ENABLED) { + if (PP_GRAPHS_ENABLED) { plot_pp_gas_profile(); plot_pp_text(); - //} + } /* now shift the translation back by half the margin; From 2052e44ba19b4bc9689792bbff2170fc3426d42a Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 10 May 2013 11:07:44 -0300 Subject: [PATCH 184/226] Fix Tooltip Positions, code cleanup Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 32 +++++++++++++++++++++----------- qt-ui/profilegraphics.h | 1 + 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 4d900aa73..9441f3ae3 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -142,6 +142,7 @@ void ProfileGraphicsView::wheelEvent(QWheelEvent* event) setTransformationAnchor(QGraphicsView::AnchorUnderMouse); // Scale the view / do the zoom + QPoint toolTipPos = mapFromScene(toolTip->pos()); double scaleFactor = 1.15; if(event->delta() > 0) { // Zoom in @@ -150,23 +151,16 @@ void ProfileGraphicsView::wheelEvent(QWheelEvent* event) // Zooming out scale(1.0 / scaleFactor, 1.0 / scaleFactor); } + toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y()); } void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) { - toolTip->clear(); - int time = (mapToScene(event->pos()).x() * gc.maxtime) / scene()->sceneRect().width(); - char buffer[500]; - get_plot_details(&gc, time, buffer, 500); - toolTip->addToolTip(QString(buffer)); - QList items = scene()->items(mapToScene(event->pos()), Qt::IntersectsItemShape, Qt::DescendingOrder, transform()); - Q_FOREACH(QGraphicsItem *item, items) { - if (!item->toolTip().isEmpty()) - toolTip->addToolTip(item->toolTip()); - } + toolTip->refresh(&gc, mapToScene(event->pos())); - // Pan on mouseMove code. + QPoint toolTipPos = mapFromScene(toolTip->pos()); ensureVisible(event->pos().x(), event->pos().y(), 10, 10, 100, 100); + toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y()); QGraphicsView::mouseMoveEvent(event); } @@ -1172,6 +1166,22 @@ void ToolTipItem::removeToolTip(const QString& toolTip) expand(); } +void ToolTipItem::refresh(struct graphics_context *gc, QPointF pos) +{ + clear(); + int time = (pos.x() * gc->maxtime) / scene()->sceneRect().width(); + char buffer[500]; + get_plot_details(gc, time, buffer, 500); + addToolTip(QString(buffer)); + + QList items = scene()->items(pos, Qt::IntersectsItemShape, Qt::DescendingOrder, transform()); + Q_FOREACH(QGraphicsItem *item, items) { + if (!item->toolTip().isEmpty()) + addToolTip(item->toolTip()); + } + +} + void ToolTipItem::clear() { Q_FOREACH(ToolTip t, toolTips) { diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 76179b956..b9b57db08 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -31,6 +31,7 @@ public: void clear(); void addToolTip(const QString& toolTip, const QIcon& icon = QIcon()); void removeToolTip(const QString& toolTip); + void refresh(struct graphics_context* gc, QPointF pos); public Q_SLOTS: void setRect(const QRectF& rect); From ea5353025f58aaaf31d7cf9d1ca92c93d9357c16 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 10 May 2013 11:45:07 -0300 Subject: [PATCH 185/226] Only drag the tooltip panel when not zooming. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 18 +++++++++++++----- qt-ui/profilegraphics.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 9441f3ae3..aeeccc875 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -144,12 +144,13 @@ void ProfileGraphicsView::wheelEvent(QWheelEvent* event) // Scale the view / do the zoom QPoint toolTipPos = mapFromScene(toolTip->pos()); double scaleFactor = 1.15; - if(event->delta() > 0) { - // Zoom in + if(event->delta() > 0 && zoomLevel <= 10) { scale(scaleFactor, scaleFactor); - } else { + zoomLevel++; + } else if (zoomLevel >= 0) { // Zooming out scale(1.0 / scaleFactor, 1.0 / scaleFactor); + zoomLevel--; } toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y()); } @@ -162,7 +163,9 @@ void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) ensureVisible(event->pos().x(), event->pos().y(), 10, 10, 100, 100); toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y()); - QGraphicsView::mouseMoveEvent(event); + if (zoomLevel < 0){ + QGraphicsView::mouseMoveEvent(event); + } } bool ProfileGraphicsView::eventFilter(QObject* obj, QEvent* event) @@ -192,7 +195,12 @@ void ProfileGraphicsView::plot(struct dive *d) { scene()->clear(); - dive = d; + if (dive != d){ + resetTransform(); + zoomLevel = 0; + dive = d; + } + if(!dive) return; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index b9b57db08..288c9230a 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -96,6 +96,7 @@ private: ToolTipItem *toolTip; graphics_context gc; struct dive *dive; + int zoomLevel; }; #endif From 9a403330673ffe1812a46e2afe4f473ca39f672b Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 10 May 2013 12:00:56 -0300 Subject: [PATCH 186/226] Fix showing the stuff on the canvas in the right positions. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index aeeccc875..71aebd19f 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -163,9 +163,8 @@ void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) ensureVisible(event->pos().x(), event->pos().y(), 10, 10, 100, 100); toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y()); - if (zoomLevel < 0){ + if (zoomLevel < 0) QGraphicsView::mouseMoveEvent(event); - } } bool ProfileGraphicsView::eventFilter(QObject* obj, QEvent* event) @@ -322,8 +321,7 @@ void ProfileGraphicsView::plot(struct dive *d) } #endif - QRectF curerntRect = scene()->itemsBoundingRect(); - scene()->setSceneRect( -10, -10, curerntRect.width() + 10, curerntRect.height() +10 ); + scene()->setSceneRect(scene()->itemsBoundingRect()); } void ProfileGraphicsView::plot_depth_scale() From bfef1d021970c57ce007fb4c29d62b0a1f4e13a2 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 10 May 2013 08:56:56 -0700 Subject: [PATCH 187/226] Fix some compiler warnings Passing the alignment as int instead of float or double was actually a bug as CENTER is defined as (-0.5) ... Signed-off-by: Dirk Hohndel --- profile.h | 2 +- qt-ui/profilegraphics.cpp | 4 +--- qt-ui/profilegraphics.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/profile.h b/profile.h index 89324530a..2b2c7bff5 100644 --- a/profile.h +++ b/profile.h @@ -14,7 +14,7 @@ struct graphics_context; struct plot_info; struct plot_data { unsigned int in_deco:1; - unsigned int cylinderindex; + int cylinderindex; int sec; /* pressure[0] is sensor pressure * pressure[1] is interpolated pressure */ diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 71aebd19f..5607cb3b8 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -550,7 +550,7 @@ void ProfileGraphicsView::plot_cylinder_pressure_text() } } -void ProfileGraphicsView::plot_pressure_value(int mbar, int sec, int xalign, int yalign) +void ProfileGraphicsView::plot_pressure_value(int mbar, int sec, double xalign, double yalign) { int pressure; const char *unit; @@ -1234,7 +1234,6 @@ void ToolTipItem::setRect(const QRectF& r) void ToolTipItem::collapse() { - QRectF newRect = childrenBoundingRect(); QPropertyAnimation *animation = new QPropertyAnimation(this, "rect"); animation->setDuration(100); animation->setStartValue(boundingRect()); @@ -1244,7 +1243,6 @@ void ToolTipItem::collapse() void ToolTipItem::expand() { - QRectF currentRect = rectangle; QRectF nextRectangle; double width = 0, height = title->boundingRect().height() + SPACING; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 288c9230a..a2033ca75 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -83,7 +83,7 @@ private: void plot_text_samples(); void plot_depth_sample(struct plot_data *entry, text_render_options_t *tro); void plot_cylinder_pressure_text(); - void plot_pressure_value(int mbar, int sec, int xalign, int yalign); + void plot_pressure_value(int mbar, int sec, double xalign, double yalign); void plot_deco_text(); void plot_pp_gas_profile(); void plot_pp_text(); From b3b1b3f58f39d1b87028908691b46d89a12d2143 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 10 May 2013 13:03:16 -0300 Subject: [PATCH 188/226] Change the plot text so it receives a QPointF instead of x,y, and a Parent Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 24 ++++++++++++------------ qt-ui/profilegraphics.h | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 71aebd19f..01f627622 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -293,7 +293,7 @@ void ProfileGraphicsView::plot(struct dive *d) nickname = dc->model; if (nickname) { text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; - plot_text(&computer, gc.leftx, gc.bottomy, nickname); + plot_text(&computer, QPointF(gc.leftx, gc.bottomy), nickname); } @@ -347,7 +347,7 @@ void ProfileGraphicsView::plot_depth_scale() * marker below the actual maxdepth of the dive */ for (i = marker; i <= gc.pi.maxdepth + marker; i += marker) { double d = get_depth_units(i, NULL, NULL); - plot_text(&tro, -0.002, i, QString::number(d)); + plot_text(&tro, QPointF(-0.002, i), QString::number(d)); } } @@ -369,7 +369,7 @@ void ProfileGraphicsView::plot_pp_text() pen.setColor(c); item->setPen(pen); scene()->addItem(item); - plot_text(&tro, hpos + 30, m, QString::number(m)); + plot_text(&tro, QPointF(hpos + 30, m), QString::number(m)); } } @@ -508,7 +508,7 @@ void ProfileGraphicsView::plot_deco_text() float y = gc.topy = 1.0; static text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, CENTER, -0.2}; gc.bottomy = 0.0; - plot_text(&tro, x, y, QString("GF %1/%2").arg(prefs.gflow * 100).arg(prefs.gfhigh * 100)); + plot_text(&tro, QPointF(x, y), QString("GF %1/%2").arg(prefs.gflow * 100).arg(prefs.gfhigh * 100)); } } @@ -557,7 +557,7 @@ void ProfileGraphicsView::plot_pressure_value(int mbar, int sec, int xalign, int pressure = get_pressure_units(mbar, &unit); static text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, xalign, yalign}; - plot_text(&tro, sec, mbar, QString("%1 %2").arg(pressure).arg(unit)); + plot_text(&tro, QPointF(sec, mbar), QString("%1 %2").arg(pressure).arg(unit)); } void ProfileGraphicsView::plot_depth_text() @@ -611,7 +611,7 @@ void ProfileGraphicsView::plot_depth_sample(struct plot_data *entry,text_render_ d = get_depth_units(entry->depth, &decimals, NULL); - plot_text(tro, sec, entry->depth, QString("%1").arg(d)); // , decimals, d); + plot_text(tro, QPointF(sec, entry->depth), QString("%1").arg(d)); // , decimals, d); } @@ -660,7 +660,7 @@ void ProfileGraphicsView::plot_single_temp_text(int sec, int mkelvin) const char *unit; static text_render_options_t tro = {TEMP_TEXT_SIZE, TEMP_TEXT, LEFT, TOP}; deg = get_temp_units(mkelvin, &unit); - plot_text(&tro, sec, mkelvin, QString("%1%2").arg(deg).arg(unit)); //"%.2g%s" + plot_text(&tro, QPointF(sec, mkelvin), QString("%1%2").arg(deg).arg(unit)); //"%.2g%s" } void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc) @@ -888,11 +888,11 @@ void ProfileGraphicsView::plot_depth_profile() if (maxtime < 600) { /* Be a bit more verbose with shorter dives */ for (i = incr; i < maxtime; i += incr) - plot_text(&tro, i, 1, QString("%1:%2").arg(i/60).arg(i%60)); + plot_text(&tro, QPointF(i, 1), QString("%1:%2").arg(i/60).arg(i%60)); } else { /* Only render the time on every second marker for normal dives */ for (i = incr; i < maxtime; i += 2 * incr) - plot_text(&tro, i, 1, QString::number(i/60)); + plot_text(&tro, QPointF(i, 1), QString::number(i/60)); } /* Depth markers: every 30 ft or 10 m*/ @@ -1071,15 +1071,15 @@ void ProfileGraphicsView::plot_depth_profile() } } -void ProfileGraphicsView::plot_text(text_render_options_t *tro, double x, double y, const QString& text) +void ProfileGraphicsView::plot_text(text_render_options_t *tro,const QPointF& pos, const QString& text, QGraphicsItem *parent) { QFontMetrics fm(font()); double dx = tro->hpos * (fm.width(text)); double dy = tro->vpos * (fm.height()); - QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text); - QPointF point(SCALEGC(x, y)); // This is neded because of the SCALE macro. + QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text, parent); + QPointF point(SCALEGC(pos.x(), pos.y())); // This is neded because of the SCALE macro. item->setPos(point.x() + dx, point.y() +dy); item->setBrush(QBrush(profile_color[tro->color].first())); diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 288c9230a..996c5927a 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -72,7 +72,7 @@ protected: private: void plot_depth_profile(); - void plot_text(text_render_options_t *tro, double x, double y, const QString &text); + void plot_text(text_render_options_t *tro, const QPointF& pos, const QString &text, QGraphicsItem *parent = 0); void plot_events(struct divecomputer *dc); void plot_one_event(struct event *event); void plot_temperature_profile(); From 3e22e388e9c6b3fe2804acc414c96511b738ad57 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 10 May 2013 09:24:06 -0700 Subject: [PATCH 189/226] Implement the get_screen_dpi function This is sadly known to be incorrect on most systems. But it's as incorrect as it was on Gtk so should work equally well to get roughly the right sizes for printing - once we implement printing, that is :-) Also removed a qDebug that snuck in in commit 6fc4d72079dd ("Enable defautl_filename in settings"). Signed-off-by: Dirk Hohndel --- display.h | 6 ++---- qt-gui.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/display.h b/display.h index 8567bc955..b62ae3ba3 100644 --- a/display.h +++ b/display.h @@ -6,15 +6,13 @@ extern "C" { #endif #define SCALE_SCREEN 1.0 -#warning "PORT THE get_screen_dpi to Qt" -#define SCALE_PRINT 1.0 -//#define SCALE_PRINT (1.0 / get_screen_dpi()) +#define SCALE_PRINT (1.0 / get_screen_dpi()) extern void repaint_dive(void); extern void do_print(void); // Commented out because I don't know how to get the dpi on a paint device yet. -// extern gdouble get_screen_dpi(void); +extern double get_screen_dpi(void); /* Plot info with smoothing, velocity indication * and one-, two- and three-minute minimums and maximums */ diff --git a/qt-gui.cpp b/qt-gui.cpp index 5d858d733..d70e00379 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -28,6 +28,7 @@ #include #include #include +#include class Translator: public QTranslator { @@ -83,7 +84,6 @@ void init_ui(int *argcp, char ***argvp) if (v.isValid()) { QString name = v.toString(); prefs.default_filename = strdup(name.toUtf8()); - qDebug("default filename %s", prefs.default_filename); } settings.endGroup(); @@ -200,4 +200,9 @@ QString get_pressure_string(pressure_t pressure, bool showunit) } } +double get_screen_dpi() +{ + QDesktopWidget *mydesk = application->desktop(); + return mydesk->physicalDpiX(); +} #include "qt-gui.moc" From c129902793446cda9137f2b44c59d5840e4eeaec Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 10 May 2013 08:56:56 -0700 Subject: [PATCH 190/226] Fix some compiler warnings Passing the alignment as int instead of float or double was actually a bug as CENTER is defined as (-0.5) ... Signed-off-by: Dirk Hohndel --- profile.h | 2 +- qt-ui/profilegraphics.cpp | 4 +--- qt-ui/profilegraphics.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/profile.h b/profile.h index 89324530a..2b2c7bff5 100644 --- a/profile.h +++ b/profile.h @@ -14,7 +14,7 @@ struct graphics_context; struct plot_info; struct plot_data { unsigned int in_deco:1; - unsigned int cylinderindex; + int cylinderindex; int sec; /* pressure[0] is sensor pressure * pressure[1] is interpolated pressure */ diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 01f627622..9c21028d9 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -550,7 +550,7 @@ void ProfileGraphicsView::plot_cylinder_pressure_text() } } -void ProfileGraphicsView::plot_pressure_value(int mbar, int sec, int xalign, int yalign) +void ProfileGraphicsView::plot_pressure_value(int mbar, int sec, double xalign, double yalign) { int pressure; const char *unit; @@ -1234,7 +1234,6 @@ void ToolTipItem::setRect(const QRectF& r) void ToolTipItem::collapse() { - QRectF newRect = childrenBoundingRect(); QPropertyAnimation *animation = new QPropertyAnimation(this, "rect"); animation->setDuration(100); animation->setStartValue(boundingRect()); @@ -1244,7 +1243,6 @@ void ToolTipItem::collapse() void ToolTipItem::expand() { - QRectF currentRect = rectangle; QRectF nextRectangle; double width = 0, height = title->boundingRect().height() + SPACING; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 996c5927a..08dffd6a3 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -83,7 +83,7 @@ private: void plot_text_samples(); void plot_depth_sample(struct plot_data *entry, text_render_options_t *tro); void plot_cylinder_pressure_text(); - void plot_pressure_value(int mbar, int sec, int xalign, int yalign); + void plot_pressure_value(int mbar, int sec, double xalign, double yalign); void plot_deco_text(); void plot_pp_gas_profile(); void plot_pp_text(); From fe6237b2733d50b36f0c2d6de515b86891581dd4 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 10 May 2013 09:24:06 -0700 Subject: [PATCH 191/226] Implement the get_screen_dpi function This is sadly known to be incorrect on most systems. But it's as incorrect as it was on Gtk so should work equally well to get roughly the right sizes for printing - once we implement printing, that is :-) Also removed a qDebug that snuck in in commit 6fc4d72079dd ("Enable defautl_filename in settings"). Signed-off-by: Dirk Hohndel --- display.h | 6 ++---- qt-gui.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/display.h b/display.h index 8567bc955..b62ae3ba3 100644 --- a/display.h +++ b/display.h @@ -6,15 +6,13 @@ extern "C" { #endif #define SCALE_SCREEN 1.0 -#warning "PORT THE get_screen_dpi to Qt" -#define SCALE_PRINT 1.0 -//#define SCALE_PRINT (1.0 / get_screen_dpi()) +#define SCALE_PRINT (1.0 / get_screen_dpi()) extern void repaint_dive(void); extern void do_print(void); // Commented out because I don't know how to get the dpi on a paint device yet. -// extern gdouble get_screen_dpi(void); +extern double get_screen_dpi(void); /* Plot info with smoothing, velocity indication * and one-, two- and three-minute minimums and maximums */ diff --git a/qt-gui.cpp b/qt-gui.cpp index 5d858d733..d70e00379 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -28,6 +28,7 @@ #include #include #include +#include class Translator: public QTranslator { @@ -83,7 +84,6 @@ void init_ui(int *argcp, char ***argvp) if (v.isValid()) { QString name = v.toString(); prefs.default_filename = strdup(name.toUtf8()); - qDebug("default filename %s", prefs.default_filename); } settings.endGroup(); @@ -200,4 +200,9 @@ QString get_pressure_string(pressure_t pressure, bool showunit) } } +double get_screen_dpi() +{ + QDesktopWidget *mydesk = application->desktop(); + return mydesk->physicalDpiX(); +} #include "qt-gui.moc" From 1fec7d849c69e7e6cca71c268e9331a4a52ad6e8 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 10 May 2013 13:57:36 -0300 Subject: [PATCH 192/226] Fixed Zoom and Positioning of the Ruler Items The items are still being placed far from each other when zooming in - I need a bit of help with the math for that. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 50 ++++++++++++++++++++++++++------------- qt-ui/profilegraphics.h | 8 ++++++- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 9c21028d9..c01022799 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -119,7 +119,6 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent gc.printer = false; setScene(new QGraphicsScene()); setBackgroundBrush(QColor("#F3F3E6")); - scene()->setSceneRect(0,0,1000,1000); scene()->installEventFilter(this); setRenderHint(QPainter::Antialiasing); @@ -160,7 +159,12 @@ void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) toolTip->refresh(&gc, mapToScene(event->pos())); QPoint toolTipPos = mapFromScene(toolTip->pos()); - ensureVisible(event->pos().x(), event->pos().y(), 10, 10, 100, 100); + + double dx = sceneRect().x(); + double dy = sceneRect().y(); + + ensureVisible(event->pos().x() + dx, event->pos().y() + dy, 1, 1); + toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y()); if (zoomLevel < 0) @@ -287,15 +291,16 @@ void ProfileGraphicsView::plot(struct dive *d) scene()->addItem(rect); /* Put the dive computer name in the lower left corner */ - const char *nickname; - nickname = get_dc_nickname(dc->model, dc->deviceid); - if (!nickname || *nickname == '\0') - nickname = dc->model; - if (nickname) { - text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; - plot_text(&computer, QPointF(gc.leftx, gc.bottomy), nickname); - } + QString nick(get_dc_nickname(dc->model, dc->deviceid)); + if (nick.isEmpty()) + nick = QString(dc->model); + if (!nick.isEmpty()) { + text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; + diveComputer = plot_text(&computer, QPointF(gc.leftx, gc.bottomy), nick); + } + // The Time ruler should be right after the DiveComputer: + timeMarkers->setPos(0, diveComputer->y()); if (PP_GRAPHS_ENABLED) { plot_pp_gas_profile(); @@ -321,7 +326,11 @@ void ProfileGraphicsView::plot(struct dive *d) } #endif - scene()->setSceneRect(scene()->itemsBoundingRect()); + QRectF r = scene()->itemsBoundingRect(); + scene()->setSceneRect(r.x() - 15, r.y() -15, r.width() + 30, r.height() + 30); + if(zoomLevel == 0){ + fitInView(sceneRect()); + } } void ProfileGraphicsView::plot_depth_scale() @@ -345,10 +354,13 @@ void ProfileGraphicsView::plot_depth_scale() * partial pressure graphs) where this would look out * of place - so we only make sure that we print the next * marker below the actual maxdepth of the dive */ + depthMarkers = new QGraphicsRectItem(); for (i = marker; i <= gc.pi.maxdepth + marker; i += marker) { double d = get_depth_units(i, NULL, NULL); - plot_text(&tro, QPointF(-0.002, i), QString::number(d)); + plot_text(&tro, QPointF(-0.002, i), QString::number(d), depthMarkers); } + scene()->addItem(depthMarkers); + depthMarkers->setPos(depthMarkers->pos().x() - 10, 0); } void ProfileGraphicsView::plot_pp_text() @@ -883,17 +895,20 @@ void ProfileGraphicsView::plot_depth_profile() scene()->addItem(item); } + timeMarkers = new QGraphicsRectItem(); /* now the text on the time markers */ struct text_render_options tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP}; if (maxtime < 600) { /* Be a bit more verbose with shorter dives */ for (i = incr; i < maxtime; i += incr) - plot_text(&tro, QPointF(i, 1), QString("%1:%2").arg(i/60).arg(i%60)); + plot_text(&tro, QPointF(i, 0), QString("%1:%2").arg(i/60).arg(i%60), timeMarkers); } else { /* Only render the time on every second marker for normal dives */ for (i = incr; i < maxtime; i += 2 * incr) - plot_text(&tro, QPointF(i, 1), QString::number(i/60)); + plot_text(&tro, QPointF(i, 0), QString("%1").arg(QString::number(i/60)), timeMarkers); } + timeMarkers->setPos(0,0); + scene()->addItem(timeMarkers); /* Depth markers: every 30 ft or 10 m*/ gc.leftx = 0; gc.rightx = 1.0; @@ -1071,7 +1086,7 @@ void ProfileGraphicsView::plot_depth_profile() } } -void ProfileGraphicsView::plot_text(text_render_options_t *tro,const QPointF& pos, const QString& text, QGraphicsItem *parent) +QGraphicsSimpleTextItem *ProfileGraphicsView::plot_text(text_render_options_t *tro,const QPointF& pos, const QString& text, QGraphicsItem *parent) { QFontMetrics fm(font()); @@ -1084,7 +1099,10 @@ void ProfileGraphicsView::plot_text(text_render_options_t *tro,const QPointF& po item->setPos(point.x() + dx, point.y() +dy); item->setBrush(QBrush(profile_color[tro->color].first())); item->setFlag(QGraphicsItem::ItemIgnoresTransformations); - scene()->addItem(item); + + if(!parent) + scene()->addItem(item); + return item; } void ProfileGraphicsView::resizeEvent(QResizeEvent *event) diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 08dffd6a3..cd87276e3 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -72,7 +72,7 @@ protected: private: void plot_depth_profile(); - void plot_text(text_render_options_t *tro, const QPointF& pos, const QString &text, QGraphicsItem *parent = 0); + QGraphicsSimpleTextItem* plot_text(text_render_options_t *tro, const QPointF& pos, const QString &text, QGraphicsItem *parent = 0); void plot_events(struct divecomputer *dc); void plot_one_event(struct event *event); void plot_temperature_profile(); @@ -97,6 +97,12 @@ private: graphics_context gc; struct dive *dive; int zoomLevel; + + // Top Level Items. + QGraphicsItem* profileGrid; + QGraphicsItem* timeMarkers; + QGraphicsItem* depthMarkers; + QGraphicsItem* diveComputer; }; #endif From 56c4cced536b8b1bb7282f263deb24640632e884 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 10 May 2013 14:18:33 -0300 Subject: [PATCH 193/226] Fixed loading the first dive via command line on the profile. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 12 +++++++++++- qt-ui/profilegraphics.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index c01022799..c2f71be11 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -194,16 +194,26 @@ static void plot_set_scale(scale_mode_t scale) } } +void ProfileGraphicsView::showEvent(QShowEvent* event) +{ + if (dive) + plot(dive); +} + void ProfileGraphicsView::plot(struct dive *d) { - scene()->clear(); + scene()->clear(); if (dive != d){ resetTransform(); zoomLevel = 0; dive = d; } + if(!isVisible()){ + return; + } + if(!dive) return; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index cd87276e3..7cf88cbc3 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -69,6 +69,7 @@ protected: void resizeEvent(QResizeEvent *event); void mouseMoveEvent(QMouseEvent* event); void wheelEvent(QWheelEvent* event); + void showEvent(QShowEvent* event); private: void plot_depth_profile(); From e69dbebf21a8bf7c5d02a12d93069e99d363ea64 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 10 May 2013 10:45:13 -0700 Subject: [PATCH 194/226] Make nextDC and previousDC work and assign shortcuts Cursor right and left now work again to switch between divecomputers. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 13 ++++++++++--- qt-ui/mainwindow.h | 2 +- qt-ui/mainwindow.ui | 6 ++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 160a657da..863e35088 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -45,6 +45,11 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), ui->ListWidget->setCurrentIndex(firstDiveOrTrip); } +void MainWindow::redrawProfile() +{ + ui->ProfileWidget->plot(get_dive(selected_dive)); +} + void MainWindow::on_actionNew_triggered() { qDebug("actionNew"); @@ -96,7 +101,7 @@ void MainWindow::dive_selection_changed(const QItemSelection& newSelection, cons continue; select_dive(get_divenr(d)); } - ui->ProfileWidget->plot(get_dive(selected_dive)); + redrawProfile(); ui->InfoWidget->updateDiveInfo(selected_dive); } @@ -239,12 +244,14 @@ void MainWindow::on_actionViewAll_triggered() void MainWindow::on_actionPreviousDC_triggered() { - qDebug("actionPreviousDC"); + dc_number--; + redrawProfile(); } void MainWindow::on_actionNextDC_triggered() { - qDebug("actionNextDC"); + dc_number++; + redrawProfile(); } void MainWindow::on_actionSelectEvents_triggered() diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 3b35d44e5..b76199a8b 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -82,7 +82,7 @@ private: bool askSaveChanges(); void readSettings(); void writeSettings(); - + void redrawProfile(); }; #endif diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 377605326..b2969ac61 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -315,11 +315,17 @@ Prev DC + + Left + Next DC + + Right + From ad8f96cd6efdec59cbec7e10571de2e9c2496d9e Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 10 May 2013 15:52:06 -0300 Subject: [PATCH 195/226] Crash fixed on clicking on the canvas while no dive is loaded. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 5607cb3b8..c1e51ac25 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -114,7 +114,7 @@ extern struct ev_select *ev_namelist; extern int evn_allocated; extern int evn_used; -ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) , dive(0) +ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) , dive(0), toolTip(0) { gc.printer = false; setScene(new QGraphicsScene()); @@ -139,6 +139,9 @@ ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent void ProfileGraphicsView::wheelEvent(QWheelEvent* event) { + if (!toolTip) + return; + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); // Scale the view / do the zoom @@ -157,6 +160,9 @@ void ProfileGraphicsView::wheelEvent(QWheelEvent* event) void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) { + if (!toolTip) + return; + toolTip->refresh(&gc, mapToScene(event->pos())); QPoint toolTipPos = mapFromScene(toolTip->pos()); From 97a044d41f67757de1e5bd0694f6502af1be4f4c Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Fri, 10 May 2013 23:56:05 +0100 Subject: [PATCH 196/226] Tweaks to maintab Align statistics tab labels as per infotab. Amend helper function to show degree symbol for temp measurements. Change order of member initialisation list to match order of decl (ProfileGraphicsView::ProfileGraphicsView) Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- profile.c | 2 +- qt-gui.cpp | 6 ++++-- qt-ui/maintab.cpp | 14 ++++++++++++++ qt-ui/profilegraphics.cpp | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/profile.c b/profile.c index ed5d01850..276641bb2 100644 --- a/profile.c +++ b/profile.c @@ -263,7 +263,7 @@ int get_cylinder_pressure_range(struct graphics_context *gc) return FALSE; while (gc->pi.endtempcoord <= SCALEY(gc, gc->pi.minpressure - (gc->topy) * 0.1)) - gc->bottomy -= gc->topy * 0.1; + gc->bottomy -= gc->topy * 0.1 * gc->maxy/abs(gc->maxy); return TRUE; } diff --git a/qt-gui.cpp b/qt-gui.cpp index d70e00379..e3a8ad5aa 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -171,10 +171,12 @@ QString get_temperature_string(temperature_t temp, bool showunit) { if (prefs.units.temperature == units::CELSIUS) { double celsius = mkelvin_to_C(temp.mkelvin); - return QString("%1%2").arg(celsius, 0, 'f', 1).arg(showunit ? _("C") : ""); + return QString("%1%2%3").arg(celsius, 0, 'f', 1).arg(showunit ? (UTF8_DEGREE): "") + .arg(showunit ? _("C") : ""); } else { double fahrenheit = mkelvin_to_F(temp.mkelvin); - return QString("%1%2").arg(fahrenheit, 0, 'f', 1).arg(showunit ? _("F") : ""); + return QString("%1%2%3").arg(fahrenheit, 0, 'f', 1).arg(showunit ? (UTF8_DEGREE): "") + .arg(showunit ? _("F") : ""); } } diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index e1ac7c275..edacf45a4 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -29,6 +29,12 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), if (label) label->setAlignment(Qt::AlignHCenter); } + QList statisticsTabWidgets = ui->statisticsTab->children(); + Q_FOREACH( QObject* obj, statisticsTabWidgets ){ + QLabel* label = qobject_cast(obj); + if (label) + label->setAlignment(Qt::AlignHCenter); + } } void MainTab::clearEquipment() @@ -95,6 +101,7 @@ void MainTab::updateDiveInfo(int dive) UPDATE_TEXT(d, suit); UPDATE_TEXT(d, divemaster); UPDATE_TEXT(d, buddy); + /* infoTab */ if (d) { ui->rating->setCurrentStars(d->rating); ui->maximumDepthText->setText(get_depth_string(d->maxdepth, TRUE)); @@ -128,6 +135,13 @@ void MainTab::updateDiveInfo(int dive) ui->gasUsedText->setText(QString()); ui->airPressureText->setText(QString()); } + /* statisticsTab*/ + /* we can access the stats_selection struct but how to we ensure the relevant dives are selected + * if we don't use the gtk widget to drive this? + * Maybe call process_selected_dives? Or re-write to query our Qt list view. + */ + qDebug("max temp %u",stats_selection.max_temp); + qDebug("min temp %u",stats_selection.min_temp); } void MainTab::on_addCylinder_clicked() diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 6e699fe51..c6a2669ef 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -114,7 +114,7 @@ extern struct ev_select *ev_namelist; extern int evn_allocated; extern int evn_used; -ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent) , dive(0), toolTip(0) +ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent), toolTip(0) , dive(0) { gc.printer = false; setScene(new QGraphicsScene()); From 06d296a17da927131ef8de511d0f9bf56dab0535 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 10 May 2013 21:07:49 -0700 Subject: [PATCH 197/226] Improve the text labels in the profile Only 1 decimal, please. Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index c6a2669ef..d606d8260 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -617,14 +617,14 @@ void ProfileGraphicsView::plot_text_samples() if (entry->depth < 2000) continue; - if ((entry == entry->max[2]) && entry->depth != last) { + if ((entry == entry->max[2]) && entry->depth / 100 != last) { plot_depth_sample(entry, &deep); - last = entry->depth; + last = entry->depth / 100; } - if ((entry == entry->min[2]) && entry->depth != last) { + if ((entry == entry->min[2]) && entry->depth / 100 != last) { plot_depth_sample(entry, &shallow); - last = entry->depth; + last = entry->depth / 100; } if (entry->depth != last) @@ -639,7 +639,7 @@ void ProfileGraphicsView::plot_depth_sample(struct plot_data *entry,text_render_ d = get_depth_units(entry->depth, &decimals, NULL); - plot_text(tro, QPointF(sec, entry->depth), QString("%1").arg(d)); // , decimals, d); + plot_text(tro, QPointF(sec, entry->depth), QString("%1").arg(d, 0, 'f', 1)); } @@ -688,7 +688,7 @@ void ProfileGraphicsView::plot_single_temp_text(int sec, int mkelvin) const char *unit; static text_render_options_t tro = {TEMP_TEXT_SIZE, TEMP_TEXT, LEFT, TOP}; deg = get_temp_units(mkelvin, &unit); - plot_text(&tro, QPointF(sec, mkelvin), QString("%1%2").arg(deg).arg(unit)); //"%.2g%s" + plot_text(&tro, QPointF(sec, mkelvin), QString("%1%2").arg(deg, 0, 'f', 1).arg(unit)); //"%.2g%s" } void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc) @@ -1112,7 +1112,7 @@ QGraphicsSimpleTextItem *ProfileGraphicsView::plot_text(text_render_options_t *t QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text, parent); QPointF point(SCALEGC(pos.x(), pos.y())); // This is neded because of the SCALE macro. - item->setPos(point.x() + dx, point.y() +dy); + item->setPos(point.x() + dx, point.y() + dy); item->setBrush(QBrush(profile_color[tro->color].first())); item->setFlag(QGraphicsItem::ItemIgnoresTransformations); From 5105d6a33316e87fe59652c461df8ad7ca07fb0e Mon Sep 17 00:00:00 2001 From: Amit Chaudhuri Date: Sat, 11 May 2013 11:38:24 +0100 Subject: [PATCH 198/226] Fix crash in DiveTripModel. A left click in the treeview header leads to a call to createIndex which results in a null pointer dereference. Signed-off-by: Amit Chaudhuri Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 9a6edce98..90143b912 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -650,7 +650,7 @@ QModelIndex DiveTripModel::parent(const QModelIndex& index) const TreeItemDT* childItem = static_cast(index.internalPointer()); TreeItemDT* parentItem = childItem->parent; - if (parentItem == rootItem) + if (parentItem == rootItem || !parentItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); From d62d1124cfed290a8f9c0cc8d5c4a83de5308e84 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 11 May 2013 22:52:02 -0700 Subject: [PATCH 199/226] Fix crash if we have no divecomputer information The string we print is lame, but it keeps things consistent (and prevents us from dereferencing functions in uninitialized objects). Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index d606d8260..54b0df8b1 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -311,10 +311,11 @@ void ProfileGraphicsView::plot(struct dive *d) if (nick.isEmpty()) nick = QString(dc->model); - if (!nick.isEmpty()) { - text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; - diveComputer = plot_text(&computer, QPointF(gc.leftx, gc.bottomy), nick); - } + if (nick.isEmpty()) + nick = tr("unknown divecomputer"); + + text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; + diveComputer = plot_text(&computer, QPointF(gc.leftx, gc.bottomy), nick); // The Time ruler should be right after the DiveComputer: timeMarkers->setPos(0, diveComputer->y()); From 32941cb84f27ddc4397857afed84bb156f983e4e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 11 May 2013 22:56:53 -0700 Subject: [PATCH 200/226] Don't print temperatures that are below 200K That threshold is of course ridiculous and arbitrary - but it seems like a good assumption to make that anything below that is DEFINITELY not valid data. Because of the way the scene grows automatically in Qt, printing these texts would squish the profile into one thin line. Signed-off-by: Dirk Hohndel --- qt-ui/profilegraphics.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 54b0df8b1..751d685e5 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -671,15 +671,15 @@ void ProfileGraphicsView::plot_temperature_text() (abs(mkelvin - last_printed_temp) < 400)) continue; last = sec; - - plot_single_temp_text(sec,mkelvin); + if (mkelvin > 200000) + plot_single_temp_text(sec,mkelvin); last_printed_temp = mkelvin; } /* it would be nice to print the end temperature, if it's * different or if the last temperature print has been more * than a quarter of the dive back */ - if ((abs(last_temperature - last_printed_temp) > 500) || - ((double)last / (double)sec < 0.75)) + if (last_temperature > 200000 && + ((abs(last_temperature - last_printed_temp) > 500) || ((double)last / (double)sec < 0.75))) plot_single_temp_text(sec, last_temperature); } From a180af38797f23b975bc092cf75e9703c4c93921 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 12 May 2013 06:38:47 -0700 Subject: [PATCH 201/226] Modify selection changed slot to deal with trips correctly If a user clicks on a trip, all the dives in a trip should be selected. But if a user selects a range of dives that happens to have a trip header in it, then only the range of dives should be selected (the trip header is marked as 'selected' for visual consistency, even though not all dives in this trip are selected). This also changes the code to scrollTo the first selected dive instead of just expanding the parent. This seems to give us a more pleasant visual appearance (trying to keep the selected dive centered in the dive list) and as a side effect no longer hides the first dive trip at program start (before this change the first dive in the first trip would be the top entry in the dive list, with its trip just out of sight above). Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 863e35088..91de38a3f 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -87,19 +87,81 @@ void MainWindow::on_actionOpen_triggered() void MainWindow::dive_selection_changed(const QItemSelection& newSelection, const QItemSelection& oldSelection) { + int cnt, i; /* first deselect the dives that are no longer selected */ - Q_FOREACH(const QModelIndex& desselect, oldSelection.indexes()) { - struct dive *d = (struct dive*) desselect.data(TreeItemDT::DIVE_ROLE).value(); + Q_FOREACH(const QModelIndex& deselect, oldSelection.indexes()) { + struct dive *d = (struct dive*) deselect.data(TreeItemDT::DIVE_ROLE).value(); + if (!d) { + // this is a trip - if just the trip is deselected but not its children, + // then we manually need to deselect its children + const QAbstractItemModel *model = deselect.model(); + cnt = model->rowCount(deselect); + if (cnt == 0) + continue; + for (i = 0; i < cnt; i++) { + QModelIndex child = deselect.child(i,0); + if (oldSelection.contains(child)) + break; + } + // if none of the dives were in the deselect list (so the user only ctrl-clicked + // on the trip header) then manually deselect all the dives + if (i == model->rowCount(deselect)) { + QItemSelectionModel::SelectionFlags flags = (QItemSelectionModel::Deselect | + QItemSelectionModel::Rows); + QItemSelection removedDives = QItemSelection(); + removedDives.select(deselect.child(0,0), deselect.child(i - 1,0)); + ui->ListWidget->selectionModel()->select(removedDives,flags); + } + } if (!d || !d->selected) continue; deselect_dive(get_divenr(d)); } /* then select the newly selected dives */ + bool needToScroll = TRUE; Q_FOREACH(const QModelIndex& select, newSelection.indexes()) { struct dive *d = (struct dive*) select.data(TreeItemDT::DIVE_ROLE).value(); + if (!d) { + // this is a trip + const QAbstractItemModel *model = select.model(); + cnt = model->rowCount(select); + if (cnt == 0) + continue; + for (i = 0; i < cnt; i++) { + QModelIndex child = select.child(i,0); + if (newSelection.contains(child)) + break; + } + // if just the trip header was clicked and none of its children, + // select all of them + if (i == model->rowCount(select)) { + if (needToScroll) { + // make sure the trip header is visible + needToScroll = FALSE; + ui->ListWidget->scrollTo(select, QAbstractItemView::PositionAtCenter); + } + QItemSelectionModel::SelectionFlags flags = (QItemSelectionModel::Select | + QItemSelectionModel::Rows); + QItemSelection addedDives = QItemSelection(); + addedDives.select(select.child(0,0), select.child(i - 1,0)); + ui->ListWidget->selectionModel()->select(addedDives,flags); + } + } if (!d || d->selected) continue; select_dive(get_divenr(d)); + if (needToScroll) { + // make sure at least one of them is visible in the list + // and if this is the first dive of a trip, make the trip visible, too + needToScroll = FALSE; + if (select.row() == 0 && d->divetrip && select.parent().isValid()) + ui->ListWidget->scrollTo(select.parent(), QAbstractItemView::PositionAtCenter); + else + ui->ListWidget->scrollTo(select, QAbstractItemView::PositionAtCenter); + } else { + // but all selected dives should be in expanded trips + ui->ListWidget->expand(select.parent()); + } } redrawProfile(); ui->InfoWidget->updateDiveInfo(selected_dive); @@ -352,7 +414,7 @@ void MainWindow::readSettings() ui->ListWidget->resizeColumnToContents(i); } ui->ListWidget->collapseAll(); - ui->ListWidget->expand(sortModel->index(0,0)); + ui->ListWidget->scrollTo(sortModel->index(0,0), QAbstractItemView::PositionAtCenter); settings.endGroup(); settings.beginGroup("Units"); GET_UNIT(v, "feet", length, units::METERS, units::FEET); From c729ae92ed1ea444ed2c9a38178ebe4cb22e54c4 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 12 May 2013 06:54:34 -0700 Subject: [PATCH 202/226] Allow walking the dive list with the cursor keys Figure out what is our first selected element (in case we start out from a multiple selection) and then move to the next logical element. So the code traverses an expanded tree (from a trip 'down' to its first dive or 'up' to the last dive of the previous trip - and similar from a first dive in a trip 'up' to its trip and from a last dive in a trip 'down' to the next trip. This does not take 'shift-cursor-up/down' into account (i.e. manual selection extension). Instead with just cursor up and down a single dive (or single trip) is selected. My guess is that the code will make someone's eyes bleed. Be warned. Signed-off-by: Dirk Hohndel --- qt-ui/mainwindow.cpp | 98 ++++++++++++++++++++++++++++++++++++++++++++ qt-ui/mainwindow.h | 8 ++++ 2 files changed, 106 insertions(+) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 91de38a3f..3ba4e6881 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -43,6 +43,14 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); else ui->ListWidget->setCurrentIndex(firstDiveOrTrip); + QAction *actionNextDive = new QAction(this); + addAction(actionNextDive); + actionNextDive->setShortcut(Qt::Key_Down); + connect(actionNextDive, SIGNAL(triggered()), this, SLOT(nextDive_triggered())); + QAction *actionPreviousDive = new QAction(this); + addAction(actionPreviousDive); + actionPreviousDive->setShortcut(Qt::Key_Up); + connect(actionPreviousDive, SIGNAL(triggered()), this, SLOT(previousDive_triggered())); } void MainWindow::redrawProfile() @@ -50,6 +58,96 @@ void MainWindow::redrawProfile() ui->ProfileWidget->plot(get_dive(selected_dive)); } +void MainWindow::nextDive_triggered() +{ + // Get the current Selection: + QItemSelectionModel *m = ui->ListWidget->selectionModel(); + QModelIndexList selection = m->selectedRows(); + + if (!selection.size()) + return; + + // check if it's a dive or trip: + QModelIndex index = selection.first(); + struct dive *d = (struct dive*) index.data(TreeItemDT::DIVE_ROLE).value(); + const QAbstractItemModel *model = index.model(); + + QItemSelectionModel::SelectionFlags flags = (QItemSelectionModel::Select | QItemSelectionModel::Rows); + + if (d) { + // it's a dive. + QModelIndex trip = index.parent(); + + // checks if it's the last dive on a trip list: + if (index.row() == model->rowCount(trip) - 1) { + // selects a trip. + QModelIndex nexttrip = model->index(trip.row()+1, trip.column(), trip.parent()); + if (nexttrip.isValid()) { + m->clear(); + m->select(nexttrip, flags); + } + } else { + m->clear(); + m->select(model->index(index.row()+1, index.column(), index.parent()), flags); + } + } else { + // it's a trip (and we have no empty trips, so there is a first child) + QModelIndex child = index.child(0,0); + m->select(model->index(0, index.column(), index), flags); + } +} + +void MainWindow::previousDive_triggered() +{ + // Get the current Selection: + QItemSelectionModel *m = ui->ListWidget->selectionModel(); + QModelIndexList selection = m->selectedRows(); + + if (!selection.size()) + return; + + // check if it's a dive or trip: + QModelIndex index = selection.first(); + struct dive *d = (struct dive*) index.data(TreeItemDT::DIVE_ROLE).value(); + const QAbstractItemModel *model = index.model(); + + QItemSelectionModel::SelectionFlags flags = (QItemSelectionModel::Select | QItemSelectionModel::Rows); + + if (d) { + // it's a dive. + QModelIndex trip = index.parent(); + + // checks if it's the first dive on a trip list: + if (index.row() == 0) { + if (trip.isValid()) { + // select the trip this dive is in + m->clear(); + m->select(model->index(trip.row(), trip.column(), trip.parent()),flags); + } + } else { + // select the previous dive + m->clear(); + m->select(model->index(index.row() - 1, index.column(), index.parent()), flags); + } + } else { + // it's a trip. + if (index.row() != 0) { + QModelIndex prevtrip = index.sibling(index.row() - 1, 0); + if (!prevtrip.isValid()) + return; + int cnt = prevtrip.model()->rowCount(); + QModelIndex child = prevtrip.child(prevtrip.model()->rowCount() - 1, 0); + /* I don't understand why this gives me incorrect rowCount... */ + while(!child.isValid() && cnt > 0) + child = prevtrip.child(--cnt, 0); + if (!child.isValid()) + return; + m->clear(); + m->select(model->index(child.row(), index.column(), prevtrip), flags); + } + } +} + void MainWindow::on_actionNew_triggered() { qDebug("actionNew"); diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index b76199a8b..8c07d048c 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -9,6 +9,7 @@ #include #include +#include class QSortFilterProxyModel; class DiveTripModel; @@ -68,6 +69,10 @@ private Q_SLOTS: void on_actionAboutSubsurface_triggered(); void on_actionUserManual_triggered(); + /* keyboard actions */ + void nextDive_triggered(); + void previousDive_triggered(); + void dive_selection_changed(const QItemSelection& newSelection, const QItemSelection& oldSelection); @@ -78,6 +83,9 @@ private: Ui::MainWindow *ui; DiveTripModel *model; QSortFilterProxyModel *sortModel; + QAction *actionNextDive; + QAction *actionPreviousDive; + QString filter(); bool askSaveChanges(); void readSettings(); From 61373eaccf506d022e3c31715b31c2b9432b6f49 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 13 May 2013 15:28:17 -0300 Subject: [PATCH 203/226] Fix segfault on mouseOver at the Profile with an invalid dive selected ( trip ) Signed-off-by: Tomaz Canabrava --- qt-ui/maintab.cpp | 4 ++-- qt-ui/mainwindow.cpp | 5 +++++ qt-ui/mainwindow.h | 2 ++ qt-ui/profilegraphics.cpp | 12 +++++++----- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index edacf45a4..a43ae563d 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -140,8 +140,8 @@ void MainTab::updateDiveInfo(int dive) * if we don't use the gtk widget to drive this? * Maybe call process_selected_dives? Or re-write to query our Qt list view. */ - qDebug("max temp %u",stats_selection.max_temp); - qDebug("min temp %u",stats_selection.min_temp); +// qDebug("max temp %u",stats_selection.max_temp); +// qDebug("min temp %u",stats_selection.min_temp); } void MainTab::on_addCylinder_clicked() diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 3ba4e6881..3766e2187 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -43,6 +43,8 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); else ui->ListWidget->setCurrentIndex(firstDiveOrTrip); + +#if 0 QAction *actionNextDive = new QAction(this); addAction(actionNextDive); actionNextDive->setShortcut(Qt::Key_Down); @@ -51,6 +53,7 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), addAction(actionPreviousDive); actionPreviousDive->setShortcut(Qt::Key_Up); connect(actionPreviousDive, SIGNAL(triggered()), this, SLOT(previousDive_triggered())); +#endif } void MainWindow::redrawProfile() @@ -58,6 +61,7 @@ void MainWindow::redrawProfile() ui->ProfileWidget->plot(get_dive(selected_dive)); } +#if 0 void MainWindow::nextDive_triggered() { // Get the current Selection: @@ -147,6 +151,7 @@ void MainWindow::previousDive_triggered() } } } +#endif void MainWindow::on_actionNew_triggered() { diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 8c07d048c..124f70f33 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -69,9 +69,11 @@ private Q_SLOTS: void on_actionAboutSubsurface_triggered(); void on_actionUserManual_triggered(); +#if 0 /* keyboard actions */ void nextDive_triggered(); void previousDive_triggered(); +#endif void dive_selection_changed(const QItemSelection& newSelection, const QItemSelection& oldSelection); diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 751d685e5..571214f6f 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -214,15 +214,13 @@ void ProfileGraphicsView::plot(struct dive *d) resetTransform(); zoomLevel = 0; dive = d; + toolTip = 0; } - if(!isVisible()){ + if(!isVisible() || !dive){ return; } - if(!dive) - return; - scene()->setSceneRect(0,0, viewport()->width()-50, viewport()->height()-50); QSettings s; @@ -1278,8 +1276,12 @@ void ToolTipItem::collapse() void ToolTipItem::expand() { - QRectF nextRectangle; + if (!title){ + return; + } + + QRectF nextRectangle; double width = 0, height = title->boundingRect().height() + SPACING; Q_FOREACH(ToolTip t, toolTips) { if (t.second->boundingRect().width() > width) From a6d9f274454c09b39d79a6300de85b4c99722770 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 13 May 2013 15:28:55 -0300 Subject: [PATCH 204/226] Change setText(QString()) to clear() Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/maintab.cpp | 84 +++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index a43ae563d..d8a966ae1 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -43,37 +43,37 @@ void MainTab::clearEquipment() void MainTab::clearInfo() { - ui->sacText->setText(QString()); - ui->otuText->setText(QString()); - ui->oxygenHeliumText->setText(QString()); - ui->gasUsedText->setText(QString()); - ui->dateText->setText(QString()); - ui->diveTimeText->setText(QString()); - ui->surfaceIntervalText->setText(QString()); - ui->maximumDepthText->setText(QString()); - ui->averageDepthText->setText(QString()); - ui->visibilityText->setText(QString()); - ui->waterTemperatureText->setText(QString()); - ui->airTemperatureText->setText(QString()); - ui->airPressureText->setText(QString()); + ui->sacText->clear(); + ui->otuText->clear(); + ui->oxygenHeliumText->clear(); + ui->gasUsedText->clear(); + ui->dateText->clear(); + ui->diveTimeText->clear(); + ui->surfaceIntervalText->clear(); + ui->maximumDepthText->clear(); + ui->averageDepthText->clear(); + ui->visibilityText->clear(); + ui->waterTemperatureText->clear(); + ui->airTemperatureText->clear(); + ui->airPressureText->clear(); } void MainTab::clearStats() { - ui->maximumDepthAllText->setText(QString()); - ui->minimumDepthAllText->setText(QString()); - ui->averageDepthAllText->setText(QString()); - ui->maximumSacAllText->setText(QString()); - ui->minimumSacAllText->setText(QString()); - ui->averageSacAllText->setText(QString()); - ui->divesAllText->setText(QString()); - ui->maximumTemperatureAllText->setText(QString()); - ui->minimumTemperatureAllText->setText(QString()); - ui->averageTemperatureAllText->setText(QString()); - ui->totalTimeAllText->setText(QString()); - ui->averageTimeAllText->setText(QString()); - ui->longestAllText->setText(QString()); - ui->shortestAllText->setText(QString()); + ui->maximumDepthAllText->clear(); + ui->minimumDepthAllText->clear(); + ui->averageDepthAllText->clear(); + ui->maximumSacAllText->clear(); + ui->minimumSacAllText->clear(); + ui->averageSacAllText->clear(); + ui->divesAllText->clear(); + ui->maximumTemperatureAllText->clear(); + ui->minimumTemperatureAllText->clear(); + ui->averageTemperatureAllText->clear(); + ui->totalTimeAllText->clear(); + ui->averageTimeAllText->clear(); + ui->longestAllText->clear(); + ui->shortestAllText->clear(); } #define UPDATE_TEXT(d, field) \ @@ -113,27 +113,27 @@ void MainTab::updateDiveInfo(int dive) if ((sacVal.mliter = d->sac) > 0) ui->sacText->setText(get_volume_string(sacVal, TRUE).append("/min")); else - ui->sacText->setText(QString()); + ui->sacText->clear(); if (d->surface_pressure.mbar) /* this is ALWAYS displayed in mbar */ ui->airPressureText->setText(QString("%1mbar").arg(d->surface_pressure.mbar)); else - ui->airPressureText->setText(QString()); + ui->airPressureText->clear(); } else { ui->rating->setCurrentStars(0); - ui->sacText->setText(QString()); - ui->otuText->setText(QString()); - ui->oxygenHeliumText->setText(QString()); - ui->dateText->setText(QString()); - ui->diveTimeText->setText(QString()); - ui->surfaceIntervalText->setText(QString()); - ui->maximumDepthText->setText(QString()); - ui->averageDepthText->setText(QString()); - ui->visibilityText->setText(QString()); - ui->waterTemperatureText->setText(QString()); - ui->airTemperatureText->setText(QString()); - ui->gasUsedText->setText(QString()); - ui->airPressureText->setText(QString()); + ui->sacText->clear(); + ui->otuText->clear(); + ui->oxygenHeliumText->clear(); + ui->dateText->clear(); + ui->diveTimeText->clear(); + ui->surfaceIntervalText->clear(); + ui->maximumDepthText->clear(); + ui->averageDepthText->clear(); + ui->visibilityText->clear(); + ui->waterTemperatureText->clear(); + ui->airTemperatureText->clear(); + ui->gasUsedText->clear(); + ui->airPressureText->clear(); } /* statisticsTab*/ /* we can access the stats_selection struct but how to we ensure the relevant dives are selected From ee7f579242568d86e7ec744c17585066c30fa943 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 13 May 2013 22:14:59 -0300 Subject: [PATCH 205/226] Trying to make the DiveList selection behave correctly And rip out all the code that Dirk put there to do that. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/divelistview.cpp | 59 ++++++++++++- qt-ui/divelistview.h | 11 +++ qt-ui/mainwindow.cpp | 188 +---------------------------------------- qt-ui/mainwindow.h | 9 -- qt-ui/models.cpp | 7 +- 5 files changed, 73 insertions(+), 201 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index 0bf0b35ba..af0ef8b0c 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -9,9 +9,66 @@ #include "modeldelegates.h" #include #include +#include +#include -DiveListView::DiveListView(QWidget *parent) : QTreeView(parent) + +DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelection(false) { setUniformRowHeights(true); setItemDelegateForColumn(TreeItemDT::RATING, new StarWidgetsDelegate()); + +} + +void DiveListView::setModel(QAbstractItemModel* model) +{ + QTreeView::setModel(model); +} + +void DiveListView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) +{ + if (mouseClickSelection) + QTreeView::setSelection(rect, command); +} + +void DiveListView::mousePressEvent(QMouseEvent* event) +{ + mouseClickSelection = true; + QTreeView::mousePressEvent(event); +} + +void DiveListView::mouseReleaseEvent(QMouseEvent* event) +{ + mouseClickSelection = false; + QTreeView::mouseReleaseEvent(event); +} + +void DiveListView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) +{ + Q_FOREACH(const QModelIndex& index, deselected.indexes()) { + const QAbstractItemModel *model = index.model(); + struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value(); + if (!dive) { // is's a trip! + if (model->rowCount(index)) { + expand(index); // leave this - even if it looks like it shouldn't be here. looks like I'v found a Qt bug. + } + } + } + + Q_FOREACH(const QModelIndex& index, selected.indexes()) { + const QAbstractItemModel *model = index.model(); + struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value(); + if (!dive) { // is's a trip! + if (model->rowCount(index)) { + expand(index); + QItemSelection selection; + selection.select(index.child(0,0), index.child(model->rowCount(index) -1 , 0)); + selectionModel()->select(selection, QItemSelectionModel::Select | QItemSelectionModel::Rows); + selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::NoUpdate); + } + } + else { + expand(index.parent()); + } + } } diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h index be9774c5c..9cccd39ae 100644 --- a/qt-ui/divelistview.h +++ b/qt-ui/divelistview.h @@ -20,6 +20,17 @@ class DiveListView : public QTreeView { public: DiveListView(QWidget *parent = 0); + + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void setModel(QAbstractItemModel* model); + + void mousePressEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent* event); + + void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command); + +private: + bool mouseClickSelection; }; #endif // DIVELISTVIEW_H diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 3766e2187..58cb86286 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -36,24 +36,12 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), sortModel->setSourceModel(model); ui->ListWidget->setModel(sortModel); setWindowIcon(QIcon(":subsurface-icon")); - connect(ui->ListWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, SLOT(dive_selection_changed(QItemSelection,QItemSelection))); + QModelIndex firstDiveOrTrip = sortModel->index(0,0); if (sortModel->index(0,0, firstDiveOrTrip).isValid()) ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); else ui->ListWidget->setCurrentIndex(firstDiveOrTrip); - -#if 0 - QAction *actionNextDive = new QAction(this); - addAction(actionNextDive); - actionNextDive->setShortcut(Qt::Key_Down); - connect(actionNextDive, SIGNAL(triggered()), this, SLOT(nextDive_triggered())); - QAction *actionPreviousDive = new QAction(this); - addAction(actionPreviousDive); - actionPreviousDive->setShortcut(Qt::Key_Up); - connect(actionPreviousDive, SIGNAL(triggered()), this, SLOT(previousDive_triggered())); -#endif } void MainWindow::redrawProfile() @@ -61,98 +49,6 @@ void MainWindow::redrawProfile() ui->ProfileWidget->plot(get_dive(selected_dive)); } -#if 0 -void MainWindow::nextDive_triggered() -{ - // Get the current Selection: - QItemSelectionModel *m = ui->ListWidget->selectionModel(); - QModelIndexList selection = m->selectedRows(); - - if (!selection.size()) - return; - - // check if it's a dive or trip: - QModelIndex index = selection.first(); - struct dive *d = (struct dive*) index.data(TreeItemDT::DIVE_ROLE).value(); - const QAbstractItemModel *model = index.model(); - - QItemSelectionModel::SelectionFlags flags = (QItemSelectionModel::Select | QItemSelectionModel::Rows); - - if (d) { - // it's a dive. - QModelIndex trip = index.parent(); - - // checks if it's the last dive on a trip list: - if (index.row() == model->rowCount(trip) - 1) { - // selects a trip. - QModelIndex nexttrip = model->index(trip.row()+1, trip.column(), trip.parent()); - if (nexttrip.isValid()) { - m->clear(); - m->select(nexttrip, flags); - } - } else { - m->clear(); - m->select(model->index(index.row()+1, index.column(), index.parent()), flags); - } - } else { - // it's a trip (and we have no empty trips, so there is a first child) - QModelIndex child = index.child(0,0); - m->select(model->index(0, index.column(), index), flags); - } -} - -void MainWindow::previousDive_triggered() -{ - // Get the current Selection: - QItemSelectionModel *m = ui->ListWidget->selectionModel(); - QModelIndexList selection = m->selectedRows(); - - if (!selection.size()) - return; - - // check if it's a dive or trip: - QModelIndex index = selection.first(); - struct dive *d = (struct dive*) index.data(TreeItemDT::DIVE_ROLE).value(); - const QAbstractItemModel *model = index.model(); - - QItemSelectionModel::SelectionFlags flags = (QItemSelectionModel::Select | QItemSelectionModel::Rows); - - if (d) { - // it's a dive. - QModelIndex trip = index.parent(); - - // checks if it's the first dive on a trip list: - if (index.row() == 0) { - if (trip.isValid()) { - // select the trip this dive is in - m->clear(); - m->select(model->index(trip.row(), trip.column(), trip.parent()),flags); - } - } else { - // select the previous dive - m->clear(); - m->select(model->index(index.row() - 1, index.column(), index.parent()), flags); - } - } else { - // it's a trip. - if (index.row() != 0) { - QModelIndex prevtrip = index.sibling(index.row() - 1, 0); - if (!prevtrip.isValid()) - return; - int cnt = prevtrip.model()->rowCount(); - QModelIndex child = prevtrip.child(prevtrip.model()->rowCount() - 1, 0); - /* I don't understand why this gives me incorrect rowCount... */ - while(!child.isValid() && cnt > 0) - child = prevtrip.child(--cnt, 0); - if (!child.isValid()) - return; - m->clear(); - m->select(model->index(child.row(), index.column(), prevtrip), flags); - } - } -} -#endif - void MainWindow::on_actionNew_triggered() { qDebug("actionNew"); @@ -188,88 +84,6 @@ void MainWindow::on_actionOpen_triggered() ui->ListWidget->sortByColumn(0, Qt::DescendingOrder); } -void MainWindow::dive_selection_changed(const QItemSelection& newSelection, const QItemSelection& oldSelection) -{ - int cnt, i; - /* first deselect the dives that are no longer selected */ - Q_FOREACH(const QModelIndex& deselect, oldSelection.indexes()) { - struct dive *d = (struct dive*) deselect.data(TreeItemDT::DIVE_ROLE).value(); - if (!d) { - // this is a trip - if just the trip is deselected but not its children, - // then we manually need to deselect its children - const QAbstractItemModel *model = deselect.model(); - cnt = model->rowCount(deselect); - if (cnt == 0) - continue; - for (i = 0; i < cnt; i++) { - QModelIndex child = deselect.child(i,0); - if (oldSelection.contains(child)) - break; - } - // if none of the dives were in the deselect list (so the user only ctrl-clicked - // on the trip header) then manually deselect all the dives - if (i == model->rowCount(deselect)) { - QItemSelectionModel::SelectionFlags flags = (QItemSelectionModel::Deselect | - QItemSelectionModel::Rows); - QItemSelection removedDives = QItemSelection(); - removedDives.select(deselect.child(0,0), deselect.child(i - 1,0)); - ui->ListWidget->selectionModel()->select(removedDives,flags); - } - } - if (!d || !d->selected) - continue; - deselect_dive(get_divenr(d)); - } - /* then select the newly selected dives */ - bool needToScroll = TRUE; - Q_FOREACH(const QModelIndex& select, newSelection.indexes()) { - struct dive *d = (struct dive*) select.data(TreeItemDT::DIVE_ROLE).value(); - if (!d) { - // this is a trip - const QAbstractItemModel *model = select.model(); - cnt = model->rowCount(select); - if (cnt == 0) - continue; - for (i = 0; i < cnt; i++) { - QModelIndex child = select.child(i,0); - if (newSelection.contains(child)) - break; - } - // if just the trip header was clicked and none of its children, - // select all of them - if (i == model->rowCount(select)) { - if (needToScroll) { - // make sure the trip header is visible - needToScroll = FALSE; - ui->ListWidget->scrollTo(select, QAbstractItemView::PositionAtCenter); - } - QItemSelectionModel::SelectionFlags flags = (QItemSelectionModel::Select | - QItemSelectionModel::Rows); - QItemSelection addedDives = QItemSelection(); - addedDives.select(select.child(0,0), select.child(i - 1,0)); - ui->ListWidget->selectionModel()->select(addedDives,flags); - } - } - if (!d || d->selected) - continue; - select_dive(get_divenr(d)); - if (needToScroll) { - // make sure at least one of them is visible in the list - // and if this is the first dive of a trip, make the trip visible, too - needToScroll = FALSE; - if (select.row() == 0 && d->divetrip && select.parent().isValid()) - ui->ListWidget->scrollTo(select.parent(), QAbstractItemView::PositionAtCenter); - else - ui->ListWidget->scrollTo(select, QAbstractItemView::PositionAtCenter); - } else { - // but all selected dives should be in expanded trips - ui->ListWidget->expand(select.parent()); - } - } - redrawProfile(); - ui->InfoWidget->updateDiveInfo(selected_dive); -} - void MainWindow::on_actionSave_triggered() { qDebug("actionSave"); diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 124f70f33..084818319 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -69,15 +69,6 @@ private Q_SLOTS: void on_actionAboutSubsurface_triggered(); void on_actionUserManual_triggered(); -#if 0 - /* keyboard actions */ - void nextDive_triggered(); - void previousDive_triggered(); -#endif - - void dive_selection_changed(const QItemSelection& newSelection, - const QItemSelection& oldSelection); - protected: void closeEvent(QCloseEvent *); diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 90143b912..68ab402cd 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -660,15 +660,14 @@ int DiveTripModel::rowCount(const QModelIndex& parent) const { TreeItemDT* parentItem; - if (parent.column() > 0) - return 0; - if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast(parent.internalPointer()); - return parentItem->childs.count(); + int amount = parentItem->childs.count(); + + return amount; } void DiveTripModel::setupModelData() From 6f74618d9c3907765633f6b6e17eb2ba5cd8f931 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 13 May 2013 21:39:49 -0500 Subject: [PATCH 206/226] Speed fixes Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/divelistview.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index af0ef8b0c..2b579e1f0 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -50,25 +50,33 @@ void DiveListView::selectionChanged(const QItemSelection& selected, const QItemS struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value(); if (!dive) { // is's a trip! if (model->rowCount(index)) { - expand(index); // leave this - even if it looks like it shouldn't be here. looks like I'v found a Qt bug. + expand(index); // leave this - even if it looks like it shouldn't be here. looks like I'v found a Qt bug. + // the subselection is removed, but the painting is not. this cleans the area. } } } + QList parents; Q_FOREACH(const QModelIndex& index, selected.indexes()) { const QAbstractItemModel *model = index.model(); struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value(); if (!dive) { // is's a trip! if (model->rowCount(index)) { - expand(index); QItemSelection selection; selection.select(index.child(0,0), index.child(model->rowCount(index) -1 , 0)); selectionModel()->select(selection, QItemSelectionModel::Select | QItemSelectionModel::Rows); selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::NoUpdate); + if (!isExpanded(index)){ + expand(index); + } } } - else { - expand(index.parent()); + else if (!parents.contains(index.parent())){ + parents.push_back(index.parent()); } } + + Q_FOREACH(const QModelIndex& index, parents){ + expand(index); + } } From 009e6a6fa7055ed0ef14e4b4d9407999d86ef158 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 13 May 2013 21:42:31 -0500 Subject: [PATCH 207/226] shift-key + shift-click + key corner cases covered. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- Configure.mk | 2 +- qt-ui/divelistview.cpp | 26 ++++++++++++++++++++------ qt-ui/divelistview.h | 2 ++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Configure.mk b/Configure.mk index 96508c65c..f3281d317 100644 --- a/Configure.mk +++ b/Configure.mk @@ -5,7 +5,7 @@ all: PKGCONFIG=pkg-config XML2CONFIG=xml2-config XSLCONFIG=xslt-config -QMAKE=qmake +QMAKE=qmake-qt4 MOC=moc UIC=uic diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index 2b579e1f0..e5d4baddc 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -17,7 +17,6 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec { setUniformRowHeights(true); setItemDelegateForColumn(TreeItemDT::RATING, new StarWidgetsDelegate()); - } void DiveListView::setModel(QAbstractItemModel* model) @@ -43,8 +42,22 @@ void DiveListView::mouseReleaseEvent(QMouseEvent* event) QTreeView::mouseReleaseEvent(event); } +void DiveListView::keyPressEvent(QKeyEvent* event) +{ + if(event->modifiers()) + mouseClickSelection = true; + QTreeView::keyPressEvent(event); +} + +void DiveListView::keyReleaseEvent(QKeyEvent* event) +{ + mouseClickSelection = false; + QWidget::keyReleaseEvent(event); +} + void DiveListView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { + QList parents; Q_FOREACH(const QModelIndex& index, deselected.indexes()) { const QAbstractItemModel *model = index.model(); struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value(); @@ -53,10 +66,11 @@ void DiveListView::selectionChanged(const QItemSelection& selected, const QItemS expand(index); // leave this - even if it looks like it shouldn't be here. looks like I'v found a Qt bug. // the subselection is removed, but the painting is not. this cleans the area. } + } else if (!parents.contains(index.parent())) { + parents.push_back(index.parent()); } } - QList parents; Q_FOREACH(const QModelIndex& index, selected.indexes()) { const QAbstractItemModel *model = index.model(); struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value(); @@ -66,17 +80,17 @@ void DiveListView::selectionChanged(const QItemSelection& selected, const QItemS selection.select(index.child(0,0), index.child(model->rowCount(index) -1 , 0)); selectionModel()->select(selection, QItemSelectionModel::Select | QItemSelectionModel::Rows); selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::NoUpdate); - if (!isExpanded(index)){ + if (!isExpanded(index)) { expand(index); } } - } - else if (!parents.contains(index.parent())){ + } else if (!parents.contains(index.parent())) { parents.push_back(index.parent()); } } - Q_FOREACH(const QModelIndex& index, parents){ + Q_FOREACH(const QModelIndex& index, parents) { + qDebug() << "Expanding"; expand(index); } } diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h index 9cccd39ae..9df8b5fa1 100644 --- a/qt-ui/divelistview.h +++ b/qt-ui/divelistview.h @@ -26,6 +26,8 @@ public: void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); + void keyPressEvent(QKeyEvent* event); + void keyReleaseEvent(QKeyEvent*); void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command); From 5d75b4b41b4fe9113683f14134fb5e81e2f895f9 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Mon, 13 May 2013 21:44:13 -0500 Subject: [PATCH 208/226] removed a unused debug. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/divelistview.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index e5d4baddc..a209118e0 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -89,8 +89,6 @@ void DiveListView::selectionChanged(const QItemSelection& selected, const QItemS } } - Q_FOREACH(const QModelIndex& index, parents) { - qDebug() << "Expanding"; + Q_FOREACH(const QModelIndex& index, parents) expand(index); - } } From c07e183f1f313316a411cfa4aac09379092add7d Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Tue, 14 May 2013 09:05:38 +0200 Subject: [PATCH 209/226] Don't use "qmake-qt4" as qmake Tomaz' commit 009e6a6fa7055ed0ef14e4b4d9407999d86ef158 introduced qmake-qt4 instead of qmake. Oops. Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- Configure.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configure.mk b/Configure.mk index f3281d317..96508c65c 100644 --- a/Configure.mk +++ b/Configure.mk @@ -5,7 +5,7 @@ all: PKGCONFIG=pkg-config XML2CONFIG=xml2-config XSLCONFIG=xslt-config -QMAKE=qmake-qt4 +QMAKE=qmake MOC=moc UIC=uic From f9598f062cf8684dd92ac56530c3758e3b1a6d9d Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Tue, 14 May 2013 09:28:30 +0200 Subject: [PATCH 210/226] Clean up some typos Cosmetic commit to clean up some of the annoying typos in qt-ui Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- qt-ui/divelistview.cpp | 6 +++--- qt-ui/maintab.cpp | 4 ++-- qt-ui/models.cpp | 16 ++++++++-------- qt-ui/models.h | 10 +++++----- qt-ui/profilegraphics.cpp | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index a209118e0..d192d84aa 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -61,9 +61,9 @@ void DiveListView::selectionChanged(const QItemSelection& selected, const QItemS Q_FOREACH(const QModelIndex& index, deselected.indexes()) { const QAbstractItemModel *model = index.model(); struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value(); - if (!dive) { // is's a trip! + if (!dive) { // it's a trip! if (model->rowCount(index)) { - expand(index); // leave this - even if it looks like it shouldn't be here. looks like I'v found a Qt bug. + expand(index); // leave this - even if it looks like it shouldn't be here. looks like I've found a Qt bug. // the subselection is removed, but the painting is not. this cleans the area. } } else if (!parents.contains(index.parent())) { @@ -74,7 +74,7 @@ void DiveListView::selectionChanged(const QItemSelection& selected, const QItemS Q_FOREACH(const QModelIndex& index, selected.indexes()) { const QAbstractItemModel *model = index.model(); struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value(); - if (!dive) { // is's a trip! + if (!dive) { // it's a trip! if (model->rowCount(index)) { QItemSelection selection; selection.select(index.child(0,0), index.child(model->rowCount(index) -1 , 0)); diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index d8a966ae1..70cb3caea 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -90,7 +90,7 @@ void MainTab::updateDiveInfo(int dive) // it will be called whenever a new dive is selected // I'm already populating the 'notes' box // to show how it can be done. - // If you are unsure what's the name of anything, + // If you are unsure about the name of something, // open the file maintab.ui on the designer // click on the item and check its objectName, // the access is ui->objectName from here on. @@ -136,7 +136,7 @@ void MainTab::updateDiveInfo(int dive) ui->airPressureText->clear(); } /* statisticsTab*/ - /* we can access the stats_selection struct but how to we ensure the relevant dives are selected + /* we can access the stats_selection struct, but how do we ensure the relevant dives are selected * if we don't use the gtk widget to drive this? * Maybe call process_selected_dives? Or re-write to query our Qt list view. */ diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 68ab402cd..0b933aeb1 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -336,13 +336,13 @@ void TankInfoModel::update() TreeItemDT::~TreeItemDT() { - qDeleteAll(childs); + qDeleteAll(children); } int TreeItemDT::row() const { if (parent) - return parent->childs.indexOf(const_cast(this)); + return parent->children.indexOf(const_cast(this)); return 0; } @@ -637,7 +637,7 @@ const TreeItemDT* parentItem = (!parent.isValid()) ? rootItem : static_cast(parent.internalPointer()); - TreeItemDT* childItem = parentItem->childs[row]; + TreeItemDT* childItem = parentItem->children[row]; return (childItem) ? createIndex(row, column, childItem) : QModelIndex(); } @@ -665,7 +665,7 @@ int DiveTripModel::rowCount(const QModelIndex& parent) const else parentItem = static_cast(parent.internalPointer()); - int amount = parentItem->childs.count(); + int amount = parentItem->children.count(); return amount; } @@ -684,19 +684,19 @@ void DiveTripModel::setupModelData() if (!trip) { diveItem->parent = rootItem; - rootItem->childs.push_back(diveItem); + rootItem->children.push_back(diveItem); continue; } if (!trips.keys().contains(trip)) { TripItem* tripItem = new TripItem(); tripItem->trip = trip; tripItem->parent = rootItem; - tripItem->childs.push_back(diveItem); + tripItem->children.push_back(diveItem); trips[trip] = tripItem; - rootItem->childs.push_back(tripItem); + rootItem->children.push_back(tripItem); continue; } TripItem* tripItem = trips[trip]; - tripItem->childs.push_back(diveItem); + tripItem->children.push_back(diveItem); } } diff --git a/qt-ui/models.h b/qt-ui/models.h index 307cdf5c3..ac533fd71 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -14,7 +14,7 @@ #include "../divelist.h" /* Encapsulates the tank_info global variable - * to show on Qt`s Model View System.*/ + * to show on Qt's Model View System.*/ class TankInfoModel : public QAbstractTableModel { Q_OBJECT public: @@ -50,9 +50,9 @@ public: void clear(); void update(); private: - /* Since the dive doesn`t stores the number of cylinders that - * it has ( max 8 ) and since I don`t want to make a - * model-for-each-dive, let`s hack this here instead. */ + /* Since the dive doesn't stores the number of cylinders that + * it has (max 8) and since I don't want to make a + * model-for-each-dive, let's hack this here instead. */ QMap usedRows; }; @@ -94,7 +94,7 @@ public: virtual QVariant data ( int column, int role ) const; int row() const; - QList childs; + QList children; TreeItemDT *parent; }; diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 571214f6f..8f25f1932 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -663,7 +663,7 @@ void ProfileGraphicsView::plot_temperature_text() /* don't print a temperature * if it's been less than 5min and less than a 2K change OR * if it's been less than 2min OR if the change from the - * last print is less than .4K (and therefore less than 1F */ + * last print is less than .4K (and therefore less than 1F) */ if (((sec < last + 300) && (abs(mkelvin - last_printed_temp) < 2000)) || (sec < last + 120) || (abs(mkelvin - last_printed_temp) < 400)) @@ -806,7 +806,7 @@ void ProfileGraphicsView::plot_one_event(struct event *ev) int i, depth = 0; struct plot_info *pi = &gc.pi; - /* is plotting this event disabled? */ + /* is plotting of this event disabled? */ if (ev->name) { for (i = 0; i < evn_used; i++) { if (! strcmp(ev->name, ev_namelist[i].ev_name)) { From 5868b37e6bde1eaa6da09aac5e269557d94d7641 Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Tue, 14 May 2013 09:45:01 +0200 Subject: [PATCH 211/226] Fix inaccurate weight and temperature display in dive list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A nonexisting temperature (mkelvin==0) was displayed as -273°C. Weight was always displayed with an extra 500 grams/0.5 lbs. Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 0b933aeb1..4c45f1489 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -540,6 +540,9 @@ QString DiveItem::displayTemperature() const { QString str; + if (!dive->watertemp.mkelvin) + return str; + if (get_units()->temperature == units::CELSIUS) str = QString::number(mkelvin_to_C(dive->watertemp.mkelvin), 'f', 1); else @@ -567,9 +570,9 @@ QString DiveItem::displayWeight() const if (get_units()->weight == units::KG) { int gr = weight() % 1000; int kg = weight() / 1000; - str = QString("%1.%2").arg(kg).arg((unsigned)(gr + 500) / 100); + str = QString("%1.%2").arg(kg).arg((unsigned)(gr) / 100); } else { - str = QString("%1").arg((unsigned)(grams_to_lbs(weight()) + 0.5)); + str = QString("%1").arg((unsigned)(grams_to_lbs(weight()))); } return str; From b0374047dd6a31d8b16c6991ee0ffc51c198569e Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Tue, 14 May 2013 08:18:26 -0300 Subject: [PATCH 212/226] code to show profile again Signed-off-by: Tomaz Canabrava --- divelist.c | 10 ++++++---- qt-ui/divelistview.cpp | 18 ++++++++++++++++++ qt-ui/divelistview.h | 8 ++++---- qt-ui/mainwindow.cpp | 9 +++++++++ qt-ui/mainwindow.h | 2 ++ 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/divelist.c b/divelist.c index deab1b05c..1398c78e8 100644 --- a/divelist.c +++ b/divelist.c @@ -144,7 +144,7 @@ int trip_has_selected_dives(dive_trip_t *trip) return 0; } -/* Get the values as we want to show them. Whole feet. But meters with one decimal for +/* Get the values as we want to show them. Whole feet. But meters with one decimal for * values less than 20m, without decimals for larger values */ void get_depth_values(int depth, int *depth_int, int *depth_decimal, int *show_decimal) { @@ -924,9 +924,11 @@ void merge_dive_index(int i, struct dive *a) void select_dive(int idx) { struct dive *dive = get_dive(idx); - if (dive && !dive->selected) { - dive->selected = 1; - amount_selected++; + if (dive) { + if (!dive->selected){ + dive->selected = 1; + amount_selected++; + } selected_dive = idx; } } diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index d192d84aa..d2354d910 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -55,6 +55,24 @@ void DiveListView::keyReleaseEvent(QKeyEvent* event) QWidget::keyReleaseEvent(event); } +void DiveListView::currentChanged(const QModelIndex& current, const QModelIndex& previous) +{ + if (!current.isValid()) + return; + const QAbstractItemModel *model = current.model(); + int selectedDive = 0; + struct dive *dive = (struct dive*) model->data(current, TreeItemDT::DIVE_ROLE).value(); + if (!dive) { // it's a trip! select first child. + dive = (struct dive*) model->data(current.child(0,0), TreeItemDT::DIVE_ROLE).value(); + selectedDive = get_divenr(dive); + }else{ + selectedDive = get_divenr(dive); + } + if (selectedDive == selected_dive) + return; + Q_EMIT currentDiveChanged(selectedDive); +} + void DiveListView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { QList parents; diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h index 9df8b5fa1..ce6238d25 100644 --- a/qt-ui/divelistview.h +++ b/qt-ui/divelistview.h @@ -18,19 +18,19 @@ class DiveListView : public QTreeView { + Q_OBJECT public: DiveListView(QWidget *parent = 0); - void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void currentChanged(const QModelIndex& current, const QModelIndex& previous); void setModel(QAbstractItemModel* model); - void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent*); - void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command); - +Q_SIGNALS: + void currentDiveChanged(int divenr); private: bool mouseClickSelection; }; diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 58cb86286..60a4623c7 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -42,6 +42,15 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); else ui->ListWidget->setCurrentIndex(firstDiveOrTrip); + + connect(ui->ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int))); +} + +void MainWindow::current_dive_changed(int divenr) +{ + select_dive(divenr); + redrawProfile(); + ui->InfoWidget->updateDiveInfo(divenr); } void MainWindow::redrawProfile() diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 084818319..04392ada9 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -69,6 +69,8 @@ private Q_SLOTS: void on_actionAboutSubsurface_triggered(); void on_actionUserManual_triggered(); + void current_dive_changed(int divenr); + protected: void closeEvent(QCloseEvent *); From c5590f835aba2cd4d0b622b0c63ec4f5796d9548 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Tue, 14 May 2013 08:34:16 -0300 Subject: [PATCH 213/226] Fix the focus issues, load a profile when load a file. Signed-off-by: Tomaz Canabrava --- qt-ui/mainwindow.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 60a4623c7..0b7f9f25d 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -36,6 +36,8 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), sortModel->setSourceModel(model); ui->ListWidget->setModel(sortModel); setWindowIcon(QIcon(":subsurface-icon")); + connect(ui->ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int))); + ui->ProfileWidget->setFocusProxy(ui->ListWidget); QModelIndex firstDiveOrTrip = sortModel->index(0,0); if (sortModel->index(0,0, firstDiveOrTrip).isValid()) @@ -43,7 +45,7 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), else ui->ListWidget->setCurrentIndex(firstDiveOrTrip); - connect(ui->ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int))); + ui->ListWidget->setFocus(); } void MainWindow::current_dive_changed(int divenr) @@ -91,6 +93,13 @@ void MainWindow::on_actionOpen_triggered() model = new DiveTripModel(this); sortModel->setSourceModel(model); ui->ListWidget->sortByColumn(0, Qt::DescendingOrder); + + QModelIndex firstDiveOrTrip = sortModel->index(0,0); + if (sortModel->index(0,0, firstDiveOrTrip).isValid()) + ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); + else + ui->ListWidget->setCurrentIndex(firstDiveOrTrip); + ui->ListWidget->setFocus(); } void MainWindow::on_actionSave_triggered() From a55a2e5d88c2e822f64e36b96910632b37ba8d6d Mon Sep 17 00:00:00 2001 From: Henrik Brautaset Aronsen Date: Tue, 14 May 2013 13:53:07 +0200 Subject: [PATCH 214/226] Don't add half a kilo/pound when adding weights The weight management widget added 500 grams / 0.5 lbs when a new entry was added. Signed-off-by: Henrik Brautaset Aronsen Signed-off-by: Dirk Hohndel --- qt-ui/models.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 4c45f1489..9bc8db0bb 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -167,9 +167,9 @@ QVariant WeightModel::data(const QModelIndex& index, int role) const if (get_units()->weight == units::KG) { int gr = ws->weight.grams % 1000; int kg = ws->weight.grams / 1000; - ret = QString("%1.%2").arg(kg).arg((unsigned)(gr + 500) / 100); + ret = QString("%1.%2").arg(kg).arg((unsigned) gr / 100); } else { - ret = QString("%1").arg((unsigned)(grams_to_lbs(ws->weight.grams) + 0.5)); + ret = QString("%1").arg((unsigned)(grams_to_lbs(ws->weight.grams))); } break; } From 30297ebd4bbe9d3048e7c4401b3b4b22c24305e0 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 16 May 2013 15:42:20 -0300 Subject: [PATCH 215/226] Correctly set the unselected dive. The selected dive was being set to zero when the program started, but zero is actually the first dive. There were workarounds on the gtk code for that probably Signed-off-by: Tomaz Canabrava --- divelist-gtk.c | 2 +- gtk-gui.c | 4 ++-- profile.c | 2 +- qt-ui/mainwindow.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/divelist-gtk.c b/divelist-gtk.c index 0c26430c1..fc90cdf26 100644 --- a/divelist-gtk.c +++ b/divelist-gtk.c @@ -1268,7 +1268,7 @@ static void delete_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path) /* if no dives are selected at this point clear the display widgets */ if (!amount_selected) { - selected_dive = 0; + selected_dive = -1; process_selected_dives(); clear_stats_widgets(); clear_equipment_widgets(); diff --git a/gtk-gui.c b/gtk-gui.c index 389257817..d24456251 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -315,7 +315,7 @@ static void file_close(GtkWidget *w, gpointer data) mark_divelist_changed(FALSE); /* clear the selection and the statistics */ - selected_dive = 0; + selected_dive = -1; process_selected_dives(); clear_stats_widgets(); clear_events(); @@ -1840,7 +1840,7 @@ void MainWindow::on_actionClose_triggered() mark_divelist_changed(FALSE); /* clear the selection and the statistics */ - selected_dive = 0; + selected_dive = -1; process_selected_dives(); clear_stats_widgets(); clear_events(); diff --git a/profile.c b/profile.c index 276641bb2..41e6724bb 100644 --- a/profile.c +++ b/profile.c @@ -15,7 +15,7 @@ #include "libdivecomputer/parser.h" #include "libdivecomputer/version.h" -int selected_dive = 0; +int selected_dive = -1; char zoomed_plot = 0; char dc_number = 0; diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 0b7f9f25d..d676549d1 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -123,7 +123,7 @@ void MainWindow::on_actionClose_triggered() mark_divelist_changed(FALSE); /* clear the selection and the statistics */ - selected_dive = 0; + selected_dive = -1; //WARNING: Port this to Qt. //process_selected_dives(); From e3cb36498d97750a9a961be21f5adb9073fa6863 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 16 May 2013 15:43:38 -0300 Subject: [PATCH 216/226] Fixed the loading of some maps On some maps, the lack of setting up the dc before plotting the dive-computer nick caused a division by zero, breaking the correct visualization of the dive. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 8f25f1932..e5212be1e 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -312,6 +312,9 @@ void ProfileGraphicsView::plot(struct dive *d) if (nick.isEmpty()) nick = tr("unknown divecomputer"); + gc.leftx = 0; gc.rightx = 1.0; + gc.topy = 0; gc.bottomy = 1.0; + text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE}; diveComputer = plot_text(&computer, QPointF(gc.leftx, gc.bottomy), nick); // The Time ruler should be right after the DiveComputer: From d39b1aedcd9dedecfca54c91661a81406b80c6ec Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 16 May 2013 16:00:33 -0300 Subject: [PATCH 217/226] Fix loading a second dive, after the first file was loaded. This patch fixes loading a second dive-file after the first one had been loaded. it simply clears some information and makes sure that the current selected dive is invalid when the file closes. I also did a bit of code cleanup on this one to make things simpler in the future. Signed-off-by: Tomaz Canabrava Signed-off-by: Dirk Hohndel --- qt-ui/divelistview.cpp | 19 +++++++++++++++++++ qt-ui/divelistview.h | 2 ++ qt-ui/mainwindow.cpp | 33 +++++++-------------------------- qt-ui/mainwindow.h | 2 -- qt-ui/profilegraphics.cpp | 19 ++++++++++++------- qt-ui/profilegraphics.h | 1 + 6 files changed, 41 insertions(+), 35 deletions(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index d2354d910..bed0599b0 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -11,12 +11,31 @@ #include #include #include +#include DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelection(false) { setUniformRowHeights(true); setItemDelegateForColumn(TreeItemDT::RATING, new StarWidgetsDelegate()); + QSortFilterProxyModel *model = new QSortFilterProxyModel(this); + setModel(model); +} + +void DiveListView::reload() +{ + QSortFilterProxyModel *m = qobject_cast(model()); + QAbstractItemModel *oldModel = m->sourceModel(); + oldModel->deleteLater(); + m->setSourceModel(new DiveTripModel(this)); + sortByColumn(0, Qt::DescendingOrder); + QModelIndex firstDiveOrTrip = m->index(0,0); + if (firstDiveOrTrip.isValid()){ + if (m->index(0,0, firstDiveOrTrip).isValid()) + setCurrentIndex(m->index(0,0, firstDiveOrTrip)); + else + setCurrentIndex(firstDiveOrTrip); + } } void DiveListView::setModel(QAbstractItemModel* model) diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h index ce6238d25..09830b7b5 100644 --- a/qt-ui/divelistview.h +++ b/qt-ui/divelistview.h @@ -29,6 +29,8 @@ public: void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent*); void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command); + void reload(); + Q_SIGNALS: void currentDiveChanged(int divenr); private: diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index d676549d1..e555473ca 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -27,24 +26,14 @@ #include "modeldelegates.h" #include "models.h" -MainWindow::MainWindow() : ui(new Ui::MainWindow()), - model(new DiveTripModel(this)), - sortModel(new QSortFilterProxyModel()) +MainWindow::MainWindow() : ui(new Ui::MainWindow()) { ui->setupUi(this); readSettings(); - sortModel->setSourceModel(model); - ui->ListWidget->setModel(sortModel); setWindowIcon(QIcon(":subsurface-icon")); connect(ui->ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int))); ui->ProfileWidget->setFocusProxy(ui->ListWidget); - - QModelIndex firstDiveOrTrip = sortModel->index(0,0); - if (sortModel->index(0,0, firstDiveOrTrip).isValid()) - ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); - else - ui->ListWidget->setCurrentIndex(firstDiveOrTrip); - + ui->ListWidget->reload(); ui->ListWidget->setFocus(); } @@ -89,16 +78,7 @@ void MainWindow::on_actionOpen_triggered() ui->InfoWidget->reload(); - model->deleteLater(); - model = new DiveTripModel(this); - sortModel->setSourceModel(model); - ui->ListWidget->sortByColumn(0, Qt::DescendingOrder); - - QModelIndex firstDiveOrTrip = sortModel->index(0,0); - if (sortModel->index(0,0, firstDiveOrTrip).isValid()) - ui->ListWidget->setCurrentIndex(sortModel->index(0,0, firstDiveOrTrip)); - else - ui->ListWidget->setCurrentIndex(firstDiveOrTrip); + ui->ListWidget->reload(); ui->ListWidget->setFocus(); } @@ -120,8 +100,6 @@ void MainWindow::on_actionClose_triggered() while (dive_table.nr) delete_single_dive(0); - mark_divelist_changed(FALSE); - /* clear the selection and the statistics */ selected_dive = -1; @@ -131,6 +109,8 @@ void MainWindow::on_actionClose_triggered() ui->InfoWidget->clearStats(); ui->InfoWidget->clearInfo(); ui->InfoWidget->clearEquipment(); + ui->ProfileWidget->clear(); + ui->ListWidget->reload(); clear_events(); #if USE_GTK_UI @@ -349,7 +329,8 @@ void MainWindow::readSettings() ui->ListWidget->resizeColumnToContents(i); } ui->ListWidget->collapseAll(); - ui->ListWidget->scrollTo(sortModel->index(0,0), QAbstractItemView::PositionAtCenter); + ui->ListWidget->scrollTo(ui->ListWidget->model()->index(0,0), QAbstractItemView::PositionAtCenter); + settings.endGroup(); settings.beginGroup("Units"); GET_UNIT(v, "feet", length, units::METERS, units::FEET); diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 04392ada9..60f0d4609 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -76,8 +76,6 @@ protected: private: Ui::MainWindow *ui; - DiveTripModel *model; - QSortFilterProxyModel *sortModel; QAction *actionNextDive; QAction *actionPreviousDive; diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index e5212be1e..8ab4aa845 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -206,16 +206,21 @@ void ProfileGraphicsView::showEvent(QShowEvent* event) plot(dive); } +void ProfileGraphicsView::clear() +{ + scene()->clear(); + resetTransform(); + zoomLevel = 0; + toolTip = 0; +} + void ProfileGraphicsView::plot(struct dive *d) { + if (dive == d) + return; - scene()->clear(); - if (dive != d){ - resetTransform(); - zoomLevel = 0; - dive = d; - toolTip = 0; - } + clear(); + dive = d; if(!isVisible() || !dive){ return; diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index 7cf88cbc3..453b8cf03 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -64,6 +64,7 @@ public: ProfileGraphicsView(QWidget* parent = 0); void plot(struct dive *d); bool eventFilter(QObject* obj, QEvent* event); + void clear(); protected: void resizeEvent(QResizeEvent *event); From 2ba236e288ace9c2c925e0e1bc1a97f9e3c59b46 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 14 May 2013 15:23:39 -0600 Subject: [PATCH 218/226] Search both qmake and qmake-qt4 Some Linux distributions do not ship a "qmake" binary, despite recommendations from the Qt Project. We need to cope with that, so we search for qmake-qt4 if qmake fails. We use "qmake -query QT_VERSION" instead of qmake -v because that is known to produce an error for Qt 3's qmake. Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- Configure.mk | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Configure.mk b/Configure.mk index 96508c65c..f8bd33ed4 100644 --- a/Configure.mk +++ b/Configure.mk @@ -5,9 +5,6 @@ all: PKGCONFIG=pkg-config XML2CONFIG=xml2-config XSLCONFIG=xslt-config -QMAKE=qmake -MOC=moc -UIC=uic CONFIGFILE = config.cache ifeq ($(CONFIGURING),1) @@ -70,6 +67,19 @@ endif # about it if it doesn't. LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null) +# Find qmake. Rules are: +# - use qmake if it is in $PATH +# [qmake -query QT_VERSION will fail if it's Qt 3's qmake] +# - if that fails, try qmake-qt4 +# - if that fails, print an error +# We specifically do not search for qmake-qt5 since that is not supposed +# to exist. +QMAKE = $(shell { qmake -query QT_VERSION >/dev/null 2>&1 && echo qmake; } || \ + { qmake-qt4 -v >/dev/null 2>&1 && echo qmake-qt4; }) +ifeq ($(strip $(QMAKE)),) +$(error Could not find qmake or qmake-qt4 in $$PATH or they failed) +endif + # Use qmake to find out which Qt version we are building for. QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) ifeq ($(QT_VERSION_MAJOR), 5) From 18e5155ba59f6abf74ce1ea952299df9d0a41d84 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 14 May 2013 15:23:40 -0600 Subject: [PATCH 219/226] Get the name of moc,uic,rcc from the .pc files in Qt 4 Just in case some crazy distributions also rename the binaries. Renaming the binaries is not supported in Qt 5 and should never be done. Besides, the binary names are missing from Qt 5.0. Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- Configure.mk | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Configure.mk b/Configure.mk index f8bd33ed4..a08a5541f 100644 --- a/Configure.mk +++ b/Configure.mk @@ -86,10 +86,16 @@ ifeq ($(QT_VERSION_MAJOR), 5) QT_MODULES = Qt5Widgets Qt5Svg QT_CORE = Qt5Core QTBINDIR = $(shell $(QMAKE) -query QT_HOST_BINS) + # Tool paths are not stored in .pc files in Qt 5.0 + MOC = $(QTBINDIR)/moc + UIC = $(QTBINDIR)/uic + RCC = $(QTBINDIR)/rcc else QT_MODULES = QtGui QtSvg QT_CORE = QtCore - QTBINDIR = $(shell $(QMAKE) -query QT_INSTALL_BINS) + MOC = $(shell $(PKGCONFIG) --variable=moc_location QtCore) + UIC = $(shell $(PKGCONFIG) --variable=uic_location QtGui) + RCC = $(shell $(PKGCONFIG) --variable=rcc_location QtGui) endif # we need GLIB2CFLAGS for gettext @@ -98,9 +104,6 @@ LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES)) ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), ) QTCXXFLAGS += -fPIE endif -MOC = $(QTBINDIR)/moc -UIC = $(QTBINDIR)/uic -RCC = $(QTBINDIR)/rcc LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0) ifneq (,$(filter $(UNAME),linux kfreebsd gnu)) From 3024b2b83cb40bb694da08b7b51ec3112c130980 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 16 May 2013 15:09:45 -0600 Subject: [PATCH 220/226] Don't deleteLater a null pointer When the application launches, the oldModel is null. Signed-off-by: Thiago Macieira Signed-off-by: Dirk Hohndel --- qt-ui/divelistview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index bed0599b0..5c8a93ff0 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -26,7 +26,8 @@ void DiveListView::reload() { QSortFilterProxyModel *m = qobject_cast(model()); QAbstractItemModel *oldModel = m->sourceModel(); - oldModel->deleteLater(); + if (oldModel) + oldModel->deleteLater(); m->setSourceModel(new DiveTripModel(this)); sortByColumn(0, Qt::DescendingOrder); QModelIndex firstDiveOrTrip = m->index(0,0); From 2f35c940261fe0f5fdb2723072553c036492e608 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Thu, 16 May 2013 21:25:31 -0300 Subject: [PATCH 221/226] Fix loading a dive via command line. Signed-off-by: Tomaz Canabrava --- qt-ui/profilegraphics.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 8ab4aa845..637c7fe95 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -202,8 +202,14 @@ static void plot_set_scale(scale_mode_t scale) void ProfileGraphicsView::showEvent(QShowEvent* event) { - if (dive) - plot(dive); + // Program just opened, + // but the dive was not ploted. + // force a replot by modifying the dive + // hold by the view, and issuing a plot. + if (dive){ + dive = 0; + plot(get_dive(selected_dive)); + } } void ProfileGraphicsView::clear() From 4098922b553c8a412b457a3b6fabbbd936317a65 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 17 May 2013 08:14:10 -0300 Subject: [PATCH 222/226] Adds preliminary support for Marble Widget Adds preliminary support for marble widget, alongside with the dive list. my idea is to let the view stay there at the left of the dive list since we got a lot of unused space and a globe is something nice to have - so you can look around where did you dived, the dives near the one that's currectly selected, and so on. I'm not using OpenStreetMaps right now, but a good thing about marble is that it is skinnable - so for instance, a dive school could present a dive lesson using subsurface with a globe from the 1600, to make it feel like 'history'. This version will only compile to Qt4. Signed-off-by: Tomaz Canabrava --- Configure.mk | 26 ++++++++++++------- Makefile | 4 ++- qt-ui/globe.cpp | 25 ++++++++++++++++++ qt-ui/globe.h | 13 ++++++++++ qt-ui/mainwindow.ui | 62 +++++++++++++++++++++++++++------------------ 5 files changed, 95 insertions(+), 35 deletions(-) create mode 100644 qt-ui/globe.cpp create mode 100644 qt-ui/globe.h diff --git a/Configure.mk b/Configure.mk index a08a5541f..fd9467ef0 100644 --- a/Configure.mk +++ b/Configure.mk @@ -83,20 +83,28 @@ endif # Use qmake to find out which Qt version we are building for. QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1) ifeq ($(QT_VERSION_MAJOR), 5) - QT_MODULES = Qt5Widgets Qt5Svg - QT_CORE = Qt5Core - QTBINDIR = $(shell $(QMAKE) -query QT_HOST_BINS) - # Tool paths are not stored in .pc files in Qt 5.0 - MOC = $(QTBINDIR)/moc - UIC = $(QTBINDIR)/uic - RCC = $(QTBINDIR)/rcc -else +# QT_MODULES = Qt5Widgets Qt5Svg +# QT_CORE = Qt5Core +# QTBINDIR = $(shell $(QMAKE) -query QT_HOST_BINS) +# # Tool paths are not stored in .pc files in Qt 5.0 +# MOC = $(QTBINDIR)/moc +# UIC = $(QTBINDIR)/uic +# RCC = $(QTBINDIR)/rcc +# if qmake is qt5, try to get the qt4 one. + QMAKE = { qmake-qt4 -v >/dev/null 2>&1 && echo qmake-qt4; } +#else +endif + +ifeq ($(strip $(QMAKE)),) +$(error Could not find qmake or qmake-qt4 in $$PATH for the Qt4 version they failed) +endif + QT_MODULES = QtGui QtSvg QT_CORE = QtCore MOC = $(shell $(PKGCONFIG) --variable=moc_location QtCore) UIC = $(shell $(PKGCONFIG) --variable=uic_location QtGui) RCC = $(shell $(PKGCONFIG) --variable=rcc_location QtGui) -endif +#endif # we need GLIB2CFLAGS for gettext QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) $(GLIB2CFLAGS) diff --git a/Makefile b/Makefile index add90b308..0bd3e5c27 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,7 @@ HEADERS = \ qt-ui/starwidget.h \ qt-ui/modeldelegates.h \ qt-ui/profilegraphics.h \ + qt-ui/globe.h SOURCES = \ @@ -70,6 +71,7 @@ SOURCES = \ qt-ui/starwidget.cpp \ qt-ui/modeldelegates.cpp \ qt-ui/profilegraphics.cpp \ + qt-ui/globe.cpp \ $(RESFILE) @@ -112,7 +114,7 @@ else endif LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGCONF2) $(LIBDIVECOMPUTER) \ - $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) + $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) -lmarblewidget MSGLANGS=$(notdir $(wildcard po/*.po)) diff --git a/qt-ui/globe.cpp b/qt-ui/globe.cpp new file mode 100644 index 000000000..770e51b02 --- /dev/null +++ b/qt-ui/globe.cpp @@ -0,0 +1,25 @@ +#include "globe.h" +#include + +using namespace Marble; + +GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent) +{ + setMapThemeId("earth/bluemarble/bluemarble.dgml"); + setProjection( Marble::Spherical ); + + // Enable the cloud cover and enable the country borders + setShowClouds( true ); + setShowBorders( true ); + + // Hide the FloatItems: Compass and StatusBar + setShowOverviewMap(false); + setShowScaleBar(false); + + Q_FOREACH( AbstractFloatItem * floatItem, floatItems() ){ + if ( floatItem && floatItem->nameId() == "compass" ) { + floatItem->setPosition( QPoint( 10, 10 ) ); + floatItem->setContentSize( QSize( 50, 50 ) ); + } + } +} diff --git a/qt-ui/globe.h b/qt-ui/globe.h new file mode 100644 index 000000000..bdf40fb1a --- /dev/null +++ b/qt-ui/globe.h @@ -0,0 +1,13 @@ +#ifndef GLOBE_H +#define GLOBE_H + +#include + +class GlobeGPS : public Marble::MarbleWidget{ + Q_OBJECT +public: + GlobeGPS(QWidget *parent); + +}; + +#endif diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index b2969ac61..ab0cd5f49 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -14,8 +14,8 @@ MainWindow - - + + Qt::Vertical @@ -27,9 +27,13 @@ - - - QTreeView { + + + Qt::Horizontal + + + + QTreeView { show-decoration-selected: 1; } @@ -58,25 +62,27 @@ } - - - true - - - QAbstractItemView::ExtendedSelection - - - true - - - true - - - true - - - true - + + + true + + + QAbstractItemView::ExtendedSelection + + + true + + + true + + + true + + + true + + + @@ -88,7 +94,7 @@ 0 0 763 - 20 + 25 @@ -365,6 +371,12 @@ QGraphicsView
profilegraphics.h
+ + GlobeGPS + QWidget +
globe.h
+ 1 +
From 9038b3aa6e89a1015d8dd50d772c995b2997fd1a Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 17 May 2013 13:28:02 -0300 Subject: [PATCH 223/226] Added real support for the marble widget The marble widget now shows the dive locations and also will center on the dive that the user clicked in the dive list. Signed-off-by: Tomaz Canabrava --- qt-ui/globe.cpp | 45 +++++++++++++++++++++++++++++++++++++++++--- qt-ui/globe.h | 10 +++++++++- qt-ui/mainwindow.cpp | 4 +++- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/qt-ui/globe.cpp b/qt-ui/globe.cpp index 770e51b02..77fd8b3b6 100644 --- a/qt-ui/globe.cpp +++ b/qt-ui/globe.cpp @@ -1,16 +1,27 @@ #include "globe.h" +#include "../dive.h" + +#include + #include +#include +#include +#include +#include using namespace Marble; -GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent) +GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent), loadedDives(0) { setMapThemeId("earth/bluemarble/bluemarble.dgml"); setProjection( Marble::Spherical ); // Enable the cloud cover and enable the country borders - setShowClouds( true ); - setShowBorders( true ); + setShowClouds( false ); + setShowBorders( false ); + setShowPlaces( false ); + setShowCrosshairs( false ); + setShowGrid( false ); // Hide the FloatItems: Compass and StatusBar setShowOverviewMap(false); @@ -23,3 +34,31 @@ GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent) } } } + +void GlobeGPS::reload() +{ + if (loadedDives){ + model()->treeModel()->removeDocument(loadedDives); + delete loadedDives; + } + + + loadedDives = new GeoDataDocument; + + int idx = 0; + struct dive *dive; + for_each_dive(idx, dive) { + if (dive_has_gps_location(dive)) { + GeoDataPlacemark *place = new GeoDataPlacemark( dive->location ); + place->setDescription(dive->notes); + place->setCoordinate(dive->longitude.udeg / 1000000.0,dive->latitude.udeg / 1000000.0 , 0, GeoDataCoordinates::Degree ); + loadedDives->append( place ); + } + } + model()->treeModel()->addDocument( loadedDives ); +} + +void GlobeGPS::centerOn(dive* dive) +{ + centerOn(dive->longitude.udeg / 1000000.0,dive->latitude.udeg / 1000000.0); +} diff --git a/qt-ui/globe.h b/qt-ui/globe.h index bdf40fb1a..8e8b9bec1 100644 --- a/qt-ui/globe.h +++ b/qt-ui/globe.h @@ -3,11 +3,19 @@ #include +namespace Marble{ + class GeoDataDocument; +} class GlobeGPS : public Marble::MarbleWidget{ Q_OBJECT public: + using Marble::MarbleWidget::centerOn; GlobeGPS(QWidget *parent); - + void reload(); + void centerOn(struct dive* dive); + +private: + Marble::GeoDataDocument *loadedDives; }; #endif diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index e555473ca..c5a4e5ce3 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -35,11 +35,13 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()) ui->ProfileWidget->setFocusProxy(ui->ListWidget); ui->ListWidget->reload(); ui->ListWidget->setFocus(); + ui->widget->reload(); } void MainWindow::current_dive_changed(int divenr) { select_dive(divenr); + ui->widget->centerOn(get_dive(selected_dive)); redrawProfile(); ui->InfoWidget->updateDiveInfo(divenr); } @@ -77,7 +79,7 @@ void MainWindow::on_actionOpen_triggered() process_dives(FALSE, FALSE); ui->InfoWidget->reload(); - + ui->widget->reload(); ui->ListWidget->reload(); ui->ListWidget->setFocus(); } From 3a1a4c1874c36480cc46d5ab241fb99bd25e5358 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 17 May 2013 13:30:47 -0300 Subject: [PATCH 224/226] removed whitespace. Signed-off-by: Tomaz Canabrava --- qt-ui/globe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt-ui/globe.h b/qt-ui/globe.h index 8e8b9bec1..86dc3bec0 100644 --- a/qt-ui/globe.h +++ b/qt-ui/globe.h @@ -13,7 +13,7 @@ public: GlobeGPS(QWidget *parent); void reload(); void centerOn(struct dive* dive); - + private: Marble::GeoDataDocument *loadedDives; }; From b89265c7f0de93c663435541167518188bcd4b2d Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 17 May 2013 14:09:10 -0300 Subject: [PATCH 225/226] Adds rotation while selecting a dive. This adds rotation, a very, very shinny feature. Signed-off-by: Tomaz Canabrava --- qt-ui/globe.cpp | 12 +++++++----- qt-ui/globe.h | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/qt-ui/globe.cpp b/qt-ui/globe.cpp index 77fd8b3b6..a8fb886d8 100644 --- a/qt-ui/globe.cpp +++ b/qt-ui/globe.cpp @@ -16,14 +16,12 @@ GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent), loadedDives(0) setMapThemeId("earth/bluemarble/bluemarble.dgml"); setProjection( Marble::Spherical ); - // Enable the cloud cover and enable the country borders + setAnimationsEnabled(true); setShowClouds( false ); setShowBorders( false ); setShowPlaces( false ); setShowCrosshairs( false ); setShowGrid( false ); - - // Hide the FloatItems: Compass and StatusBar setShowOverviewMap(false); setShowScaleBar(false); @@ -33,6 +31,7 @@ GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent), loadedDives(0) floatItem->setContentSize( QSize( 50, 50 ) ); } } + } void GlobeGPS::reload() @@ -49,8 +48,11 @@ void GlobeGPS::reload() struct dive *dive; for_each_dive(idx, dive) { if (dive_has_gps_location(dive)) { + // don't add dive locations twice. + if( diveLocations.contains( QString(dive->location))) + continue; + GeoDataPlacemark *place = new GeoDataPlacemark( dive->location ); - place->setDescription(dive->notes); place->setCoordinate(dive->longitude.udeg / 1000000.0,dive->latitude.udeg / 1000000.0 , 0, GeoDataCoordinates::Degree ); loadedDives->append( place ); } @@ -60,5 +62,5 @@ void GlobeGPS::reload() void GlobeGPS::centerOn(dive* dive) { - centerOn(dive->longitude.udeg / 1000000.0,dive->latitude.udeg / 1000000.0); + centerOn(dive->longitude.udeg / 1000000.0,dive->latitude.udeg / 1000000.0, true); } diff --git a/qt-ui/globe.h b/qt-ui/globe.h index 86dc3bec0..e89c9b777 100644 --- a/qt-ui/globe.h +++ b/qt-ui/globe.h @@ -2,6 +2,7 @@ #define GLOBE_H #include +#include namespace Marble{ class GeoDataDocument; @@ -16,6 +17,8 @@ public: private: Marble::GeoDataDocument *loadedDives; + QStringList diveLocations; + }; #endif From 56dbb7c2ff697a393f5051e2b5363bd4c0f2bb6e Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Fri, 17 May 2013 16:12:55 -0300 Subject: [PATCH 226/226] Added the possibility to change the coordinates of a dive. Added the possibility to change the coordinates of a dive. it's too intrusive in the moment, but it was a proof of concept. so I'll commit as is and try to find a better way to warn the user what's going on in the future, using something less terrible than a popup exploding in his face. Signed-off-by: Tomaz Canabrava --- qt-ui/globe.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++---- qt-ui/globe.h | 24 ++++++++++++++------- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/qt-ui/globe.cpp b/qt-ui/globe.cpp index a8fb886d8..93d1ab7c4 100644 --- a/qt-ui/globe.cpp +++ b/qt-ui/globe.cpp @@ -9,7 +9,8 @@ #include #include -using namespace Marble; +#include +#include GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent), loadedDives(0) { @@ -31,7 +32,6 @@ GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent), loadedDives(0) floatItem->setContentSize( QSize( 50, 50 ) ); } } - } void GlobeGPS::reload() @@ -40,7 +40,9 @@ void GlobeGPS::reload() model()->treeModel()->removeDocument(loadedDives); delete loadedDives; } - + if (editingDiveCoords){ + editingDiveCoords = 0; + } loadedDives = new GeoDataDocument; @@ -62,5 +64,50 @@ void GlobeGPS::reload() void GlobeGPS::centerOn(dive* dive) { - centerOn(dive->longitude.udeg / 1000000.0,dive->latitude.udeg / 1000000.0, true); + qreal longitude = dive->longitude.udeg / 1000000.0; + qreal latitude = dive->latitude.udeg / 1000000.0; + + if (!longitude || !latitude){ + prepareForGetDiveCoordinates(dive); + return; + } + + centerOn(longitude,latitude, true); } + +void GlobeGPS::prepareForGetDiveCoordinates(dive* dive) +{ + QMessageBox::warning(parentWidget(), + tr("This dive has no location!"), + tr("Move the planet to the desired position, then \n double-click to set the new location of this dive.")); + + editingDiveCoords = dive; +} + +void GlobeGPS::changeDiveGeoPosition(qreal lon, qreal lat, GeoDataCoordinates::Unit unit) +{ + // convert to degrees if in radian. + if (unit == GeoDataCoordinates::Radian){ + lon = lon * 180 / M_PI; + lat = lat * 180 / M_PI; + } + + if (!editingDiveCoords){ + return; + } + + editingDiveCoords->latitude.udeg = (int) lat * 1000000.0; + editingDiveCoords->longitude.udeg = (int) lon * 1000000.0; + centerOn(lon, lat, true); + reload(); + editingDiveCoords = 0; +} + +void GlobeGPS::mousePressEvent(QMouseEvent* event) +{ + qreal lat, lon; + if (editingDiveCoords && geoCoordinates(event->pos().x(), event->pos().y(), lon,lat, GeoDataCoordinates::Radian)){ + changeDiveGeoPosition(lon, lat, GeoDataCoordinates::Radian); + } +} + diff --git a/qt-ui/globe.h b/qt-ui/globe.h index e89c9b777..5f207a502 100644 --- a/qt-ui/globe.h +++ b/qt-ui/globe.h @@ -2,23 +2,33 @@ #define GLOBE_H #include +#include + #include -namespace Marble{ - class GeoDataDocument; -} -class GlobeGPS : public Marble::MarbleWidget{ +using namespace Marble; +struct dive; + +class GlobeGPS : public MarbleWidget{ Q_OBJECT + void prepareForGetDiveCoordinates(struct dive* dive); public: - using Marble::MarbleWidget::centerOn; + using MarbleWidget::centerOn; GlobeGPS(QWidget *parent); void reload(); void centerOn(struct dive* dive); +protected: + virtual void mousePressEvent(QMouseEvent* event); + private: - Marble::GeoDataDocument *loadedDives; + GeoDataDocument *loadedDives; QStringList diveLocations; - + struct dive* editingDiveCoords; + +public Q_SLOTS: + void changeDiveGeoPosition(qreal lon,qreal lat,GeoDataCoordinates::Unit); + }; #endif