diff options
-rw-r--r-- | callbacks-gtk.h | 17 | ||||
-rw-r--r-- | display-gtk.h | 106 | ||||
-rw-r--r-- | divelist-gtk.c | 2343 | ||||
-rw-r--r-- | info-gtk.c | 1043 | ||||
-rw-r--r-- | planner-gtk.c | 455 | ||||
-rw-r--r-- | statistics-gtk.c | 659 |
6 files changed, 0 insertions, 4623 deletions
diff --git a/callbacks-gtk.h b/callbacks-gtk.h deleted file mode 100644 index 568916f6c..000000000 --- a/callbacks-gtk.h +++ /dev/null @@ -1,17 +0,0 @@ -#define UNITCALLBACK(name, type, value) \ -static void name(GtkWidget *w, gpointer data) \ -{ \ - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \ - prefs.units.type = value; \ - update_screen(); \ -} - -#define OPTIONCALLBACK(name, option) \ -static void name(GtkWidget *w, gpointer data) \ -{ \ - GtkWidget **entry = (GtkWidget**)data; \ - option = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); \ - update_screen(); \ - if (entry) \ - gtk_widget_set_sensitive(*entry, option);\ -} diff --git a/display-gtk.h b/display-gtk.h deleted file mode 100644 index 634f05cab..000000000 --- a/display-gtk.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef DISPLAY_GTK_H -#define DISPLAY_GTK_H - -#include <gtk/gtk.h> -#include <gdk/gdk.h> -#include <gdk/gdkkeysyms.h> -#if GTK_CHECK_VERSION(2,22,0) -#include <gdk/gdkkeysyms-compat.h> -#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 */ -typedef struct { - GtkWidget *bar; -} progressbar_t; - -#if defined __APPLE__ -#define CTRLCHAR "<Meta>" -#define SHIFTCHAR "<Shift>" -#define PREFERENCE_ACCEL "<Meta>comma" -#else -#define CTRLCHAR "<Control>" -#define SHIFTCHAR "<Shift>" -#define PREFERENCE_ACCEL NULL -#endif - -extern int subsurface_fill_device_list(GtkListStore *store); -extern const char *subsurface_icon_name(void); -extern void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar, - GtkWidget *vbox, GtkUIManager *ui_manager); -extern gboolean on_delete(GtkWidget* w, gpointer data); - -extern void set_divelist_font(const char *); - -extern void update_screen(void); -extern void download_dialog(GtkWidget *, gpointer); - -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); - - -// info.c -enum { - MATCH_EXACT, - MATCH_PREPEND, - MATCH_AFTER -} found_string_entry; - -extern GtkWidget *create_date_time_widget(struct tm *time, GtkWidget **cal, GtkWidget **h, GtkWidget **m, GtkWidget **timehbox); -extern void add_string_list_entry(const char *string, GtkListStore *list); -extern int match_list(GtkListStore *list, const char *string); - -extern GtkWidget *dive_info_frame(void); -extern GtkWidget *extended_dive_info_widget(void); -extern GtkWidget *equipment_widget(int w_idx); -extern GtkWidget *single_stats_widget(void); -extern GtkWidget *total_stats_widget(void); -extern int select_cylinder(struct dive *dive, int when); -extern GtkWidget *dive_list_create(void); -extern void dive_list_destroy(void); -extern void info_widget_destroy(void); -extern GdkPixbuf *get_gps_icon(void); - -/* Helper functions for gtk combo boxes */ -extern GtkEntry *get_entry(GtkComboBox *); -extern const char *get_active_text(GtkComboBox *); -extern void set_active_text(GtkComboBox *, const char *); -extern GtkWidget *combo_box_with_model_and_entry(GtkListStore *); - -extern GtkWidget *create_label(const char *fmt, ...); - -extern gboolean icon_click_cb(GtkWidget *w, GdkEventButton *event, gpointer data); - -extern void process_selected_dives(void); - -typedef void (*data_func_t)(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data); - -typedef gint (*sort_func_t)(GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data); - -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); - -GError *uemis_download(const char *path, progressbar_t *progress, GtkDialog *dialog, gboolean force_download); - -/* from planner.c */ -extern void input_plan(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/divelist-gtk.c b/divelist-gtk.c deleted file mode 100644 index 49f4b143c..000000000 --- a/divelist-gtk.c +++ /dev/null @@ -1,2343 +0,0 @@ -/* 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 <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <math.h> -#include <glib/gi18n.h> -#include <assert.h> -#include <zip.h> -#include <libxslt/transform.h> - -#include "dive.h" -#include "divelist.h" -#include "display.h" -#include "display-gtk.h" -#include "webservice.h" -#include "profile.h" - -#include <gdk-pixbuf/gdk-pixdata.h> -#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 gboolean ignore_selection_changes = 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; - timestamp_t when; - /* this should be enought for most languages. if not increase the value. */ - char *buffer; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1); - nr = gtk_tree_model_iter_n_children(model, iter); - - if (idx < 0) { - buffer = get_trip_date_string(when, nr); - } else { - buffer = get_dive_date_string(when); - } - g_object_set(renderer, "text", buffer, NULL); - free(buffer); -} - -static void depth_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int depth, integer, frac, len, idx, show_decimal; - char buffer[40]; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1); - - if (idx < 0) { - *buffer = '\0'; - } else { - get_depth_values(depth, &integer, &frac, &show_decimal); - len = snprintf(buffer, sizeof(buffer), "%d", integer); - if (show_decimal) - 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), "<b>%d</b>", 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; -} - -static void nitrox_data_func(GtkTreeViewColumn *col, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - int idx; - char *buffer; - struct dive *dive; - - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - 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); - } -} - -/* 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; -} - -/* 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; - if (((dive->dive_tags & DTAG_INVALID) && !prefs.display_invalid_dives) || - (dive->dive_tags & dive_mask) != dive_mask) - continue; - 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; - ignore_selection_changes = TRUE; - gtk_tree_store_clear(TREESTORE(dive_list)); - gtk_tree_store_clear(LISTSTORE(dive_list)); - ignore_selection_changes = FALSE; - fill_dive_list(); - restore_tree_state(); - repaint_dive(); -} - -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; - - 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); - - return dive_nr_sort(idx_a, idx_b, 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, 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 }, - [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 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; - - 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(); - } -} - -static void show_gps_location_cb(GtkWidget *menuitem, struct dive *dive) -{ - show_gps_location(dive, NULL); -} - -gboolean icon_click_cb(GtkWidget *w, GdkEventButton *event, gpointer data) -{ - 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); - } - /* 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 invalid_dives_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - int i; - int changed = 0; - struct dive *dive; - - if (!amount_selected) - return; - /* walk the dive list in chronological order */ - for_each_dive(i, dive) { - if (!dive->selected) - continue; - /* now swap the invalid tag if just 1 dive was selected - * otherwise set all to invalid */ - if(amount_selected == 1) { - if (dive->dive_tags & DTAG_INVALID) - dive->dive_tags &= ~DTAG_INVALID; - else - dive->dive_tags |= DTAG_INVALID; - changed = 1; - } else { - if (! dive->dive_tags & DTAG_INVALID) { - dive->dive_tags |= DTAG_INVALID; - changed = 1; - } - } - /* if invalid dives aren't shown they need to be - * de-selected here to avoid confusion */ - if (dive->selected && !prefs.display_invalid_dives) { - dive->selected = 0; - amount_selected--; - } - } - if (amount_selected == 0) - selected_dive = -1; - if (changed) { - dive_list_update_dives(); - mark_divelist_changed(TRUE); - } -} - -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 = -1; - 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); -} - -void divelogs_status_dialog(char *error, GtkMessageType type) -{ - GtkWidget *dialog, *vbox, *label; - - dialog = gtk_message_dialog_new( - GTK_WINDOW(main_window), - GTK_DIALOG_DESTROY_WITH_PARENT, - type, - GTK_BUTTONS_OK, - _("%s: Response from divelogs.de"), type == GTK_MESSAGE_INFO ? _("Info") : _("Error") - ); - - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - - label = create_label("%s", error); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); - - gtk_widget_show_all(dialog); - gtk_dialog_run(GTK_DIALOG(dialog)); - - gtk_widget_destroy(dialog); - -} - -static void upload_dives_divelogs(const gboolean selected) -{ - 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(); - GtkMessageType type; - char *error = NULL; - char *parsed = NULL, *endat = NULL; - - /* - * 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 (selected && !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, &error)) { - type = GTK_MESSAGE_ERROR; - } else { - /* The upload status XML message should be parsed - * properly and displayed in a sensible manner. But just - * displaying the information part of the raw message is - * better than nothing. - * And at least the dialog is customized to indicate - * error or success. - */ - if (error) { - parsed = strstr(error, "<Login>"); - endat = strstr(error, "</divelogsDataImport>"); - if (parsed && endat) - *endat = '\0'; - } - if (error && strstr(error, "failed")) - type = GTK_MESSAGE_ERROR; - else - type = GTK_MESSAGE_INFO; - } - if (parsed) - divelogs_status_dialog(parsed, type); - else if (error) - divelogs_status_dialog(error, type); - free(error); - - g_unlink(tempfile); - g_free(tempfile); -} - -void upload_selected_dives_divelogs_cb(GtkWidget *menuitem, GtkTreePath *path) -{ - upload_dives_divelogs(TRUE); -} - -void upload_all_dives_divelogs_cb() -{ - upload_dives_divelogs(FALSE); -} - -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); -} - -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; - char exportuddflabel[] = N_("Export dive(s) to UDDF"); - char uploaddivelogslabel[] = N_("Upload dive(s) to divelogs.de"); - 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, delete or tag them as invalid */ - 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); - - if (amount_selected == 1) { - if (dive->dive_tags & DTAG_INVALID) - menuitem = gtk_menu_item_new_with_label(_("Mark valid")); - else - menuitem = gtk_menu_item_new_with_label(_("Mark invalid")); - } else { - menuitem = gtk_menu_item_new_with_label(_("Mark invalid")); - } - g_signal_connect(menuitem, "activate", G_CALLBACK(invalid_dives_cb), path); - 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); - - menuitem = gtk_menu_item_new_with_label(_(uploaddivelogslabel)); - g_signal_connect(menuitem, "activate", G_CALLBACK(upload_selected_dives_divelogs_cb), path); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - 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); - - 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); - } - /* 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); - } - /* 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; - - if (ignore_selection_changes) - return; - - 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/info-gtk.c b/info-gtk.c deleted file mode 100644 index 4ba9058cf..000000000 --- a/info-gtk.c +++ /dev/null @@ -1,1043 +0,0 @@ -/* 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 <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <time.h> -#include <ctype.h> -#include <sys/time.h> -#include <glib/gi18n.h> - -#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, int tags_shown) -{ - 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 (tags_shown && dive->dive_tags != master->dive_tags) { - changed = 1; - dive->dive_tags = master->dive_tags; - /* if a dive is selected and we aren't showing invalid dives and it is - * now marked as invalid we need to deselect it to keep the selection - * state consistent */ - if (!prefs.display_invalid_dives && dive->selected && dive->dive_tags & DTAG_INVALID) { - dive->selected = 0; - amount_selected--; - } - } - 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); - } -} - -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; -} - -/* - * 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; -} - -void divetag_toggle_cb(GtkWidget *widget, gpointer data) -{ - int togglebit = GPOINTER_TO_INT (data); - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) - edit_dive.dive_tags |= togglebit; - else - edit_dive.dive_tags &= ~togglebit; -} - -static void dive_info_widget(GtkWidget *obox, struct dive *dive, struct dive_info *info, int multi, int *show_tags) -{ - GtkWidget *hbox, *frame, *equipment, *ibox, *box, *button, *sbox, *framebox; - GtkWidget *image; - char buffer[256]; - char airtemp[10]; - const char *unit; - double value; - int i, tags; - struct dive *otherdive; - - 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; - snprintf(buffer, sizeof(buffer), "%s", _("Edit dive")); - 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); - 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); - 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); - - frame = gtk_frame_new(_("Dive Tags")); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); - - framebox = gtk_vbox_new(FALSE, 3); - gtk_container_add(GTK_CONTAINER(frame), framebox); - - /* we only want to show the tags if we have a single dive or if all selected - * dives have the exact same set of tags (like none at all right after import) */ - i = 0; - *show_tags = 1; - tags = dive->dive_tags; - for_each_dive(i, otherdive) - if (otherdive && otherdive->selected && otherdive->dive_tags != tags) - *show_tags = 0; - if (*show_tags) { - /* check boxes for the (currently fixed) list of tags; - * let's do 5 per row */ - const int cols = 5; - int rows = DTAG_NR / cols + (DTAG_NR % cols) ? 1 : 0; - GtkWidget *table = gtk_table_new(rows, cols, TRUE); - for (i = 0; i < DTAG_NR; i++) { - int x = i % cols; - int y = i / cols; - button = gtk_check_button_new_with_label(_(dtag_names[i])); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), dive->dive_tags & (1 << i)); - gtk_table_attach_defaults(GTK_TABLE(table), button, x, x+1, y, y+1); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(divetag_toggle_cb), - GINT_TO_POINTER(1 << i)); - } - gtk_box_pack_start(GTK_BOX(framebox), table, TRUE, FALSE, 3); - } else { - sbox = gtk_label_new(_("Tags are only shown if they are identical for all edited dives")); - gtk_box_pack_start(GTK_BOX(framebox), sbox, TRUE, FALSE, 3); - } - /* 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; - int multi; - int tags_shown; - - 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, &tags_shown); - 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, tags_shown); - /* 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, tags_shown); - 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); - /* these values could have changed because of the edit - so let's - * recreate them */ - master->duration.seconds = 0; - master->maxdepth.mm = 0; - master->meandepth.mm = 0; - (void)fixup_dive(master); - } - 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)) - return 0; - - 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) { - if (prefs.units.length == FEET) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(depth), mm_to_feet(dive->dc.maxdepth.mm)); - } else { - 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) { - if (prefs.units.length == FEET) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(avgdepth), mm_to_feet(dive->dc.meandepth.mm)); - } else { - 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/planner-gtk.c b/planner-gtk.c deleted file mode 100644 index b6306bdee..000000000 --- a/planner-gtk.c +++ /dev/null @@ -1,455 +0,0 @@ -/* planner.c - * - * code that allows us to plan future dives - * - * (c) Dirk Hohndel 2013 - */ -#include <glib/gi18n.h> -#include <unistd.h> -#include <ctype.h> -#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 gboolean last_stop_toggled_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - char *error_string = NULL; - set_last_stop(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(entry))); - 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 = _("<small>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.</small>\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("<span foreground='red'></span>"); - labeltext = malloc(len); - snprintf(labeltext, len, "%s<span foreground='red'>%s</span>", 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); - - if (get_units()->length == METERS) - labeltext = _("Last stop at 6 Meters"); - else - labeltext = _("Last stop at 20 Feet"); - - set_last_stop(FALSE); - content = gtk_check_button_new_with_label(labeltext); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(content), 0); - gtk_box_pack_start(GTK_BOX(hbox), content, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(content), "toggled", G_CALLBACK(last_stop_toggled_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); -} diff --git a/statistics-gtk.c b/statistics-gtk.c deleted file mode 100644 index b35516d3d..000000000 --- a/statistics-gtk.c +++ /dev/null @@ -1,659 +0,0 @@ -/* 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 <glib/gi18n.h> -#include <ctype.h> - -#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, - *dive_type; -} 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; - int more = 0; - - 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, ""); - } - - /* Dive type */ - *buf = '\0'; - if (dive->dive_tags) { - int i; - - for (i = 0; i < DTAG_NR; i++) - if(dive->dive_tags & (1 << i)) { - if (more) - strcat(buf, ", "); - strcat(buf, _(dtag_names[i])); - more = 1; - } - } - if (!(dive->dive_tags & DTAG_FRESH) && dc->salinity == 10000) { - if (more) - strcat(buf, ", "); - strcat(buf, _(dtag_names[DTAG_FRESH_NR])); - } - set_label(single_w.dive_type, buf); -} - -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")); - - /* fifth row */ - hbox = gtk_hbox_new(FALSE, 3); - gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3); - - single_w.dive_type = new_info_label_in_frame(hbox, _("Dive Tags")); - - 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(single_w.dive_type, ""); - 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,""); -} |