diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | display-gtk.h | 3 | ||||
-rw-r--r-- | gtk-gui.c | 453 | ||||
-rw-r--r-- | planner.c | 461 |
4 files changed, 465 insertions, 456 deletions
@@ -247,8 +247,8 @@ print.o: print.c dive.h display.h display-gtk.h deco.o: deco.c dive.h $(CC) $(CFLAGS) $(GLIB2CFLAGS) -c deco.c -planner.o: planner.c dive.h - $(CC) $(CFLAGS) $(GLIB2CFLAGS) -c planner.c +planner.o: planner.c dive.h divelist.h display-gtk.h + $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c planner.c libdivecomputer.o: libdivecomputer.c dive.h display.h display-gtk.h libdivecomputer.h $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ diff --git a/display-gtk.h b/display-gtk.h index 083724a61..2613a20b6 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -135,4 +135,7 @@ extern GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, cons GError *uemis_download(const char *path, progressbar_t *progress, GtkDialog *dialog, gboolean force_download); +/* from planner.c */ +extern void input_plan(void); + #endif @@ -1125,459 +1125,6 @@ static void next_dc(GtkWidget *w, gpointer data) repaint_dive(); } -/* - * Get a value in tenths (so "10.2" == 102, "9" = 90) - * - * Return negative for errors. - */ -static int get_tenths(char *begin, char **end) -{ - int value = strtol(begin, end, 10); - if (begin == *end) - return -1; - value *= 10; - - /* Fraction? We only look at the first digit */ - if (**end == '.') { - ++*end; - if (!isdigit(**end)) - return -1; - value += **end - '0'; - do { - ++*end; - } while (isdigit(**end)); - } - return value; -} - -static int get_permille(char *begin, char **end) -{ - int value = get_tenths(begin, end); - if (value >= 0) { - /* Allow a percentage sign */ - if (**end == '%') - ++*end; - } - return value; -} - -static int validate_gas(char *text, int *o2_p, int *he_p) -{ - int o2, he; - - if (!text) - return 0; - - while (isspace(*text)) - text++; - - if (!*text) { - o2 = AIR_PERMILLE; he = 0; - } else if (!strcasecmp(text, "air")) { - o2 = AIR_PERMILLE; he = 0; text += 3; - } else if (!strncasecmp(text, "ean", 3)) { - o2 = get_permille(text+3, &text); he = 0; - } else { - o2 = get_permille(text, &text); he = 0; - if (*text == '/') - he = get_permille(text+1, &text); - } - - /* We don't want any extra crud */ - while (isspace(*text)) - text++; - if (*text) - return 0; - - /* Validate the gas mix */ - if (*text || o2 < 1 || o2 > 1000 || he < 0 || o2+he > 1000) - return 0; - - /* Let it rip */ - *o2_p = o2; - *he_p = he; - return 1; -} - -static int validate_time(char *text, int *sec_p, int *rel_p) -{ - int min, sec, rel; - char *end; - - if (!text) - return 0; - - while (isspace(*text)) - text++; - - rel = 0; - if (*text == '+') { - rel = 1; - text++; - while (isspace(*text)) - text++; - } - - min = strtol(text, &end, 10); - if (text == end) - return 0; - - if (min < 0 || min > 1000) - return 0; - - /* Ok, minutes look ok */ - text = end; - sec = 0; - if (*text == ':') { - text++; - sec = strtol(text, &end, 10); - if (end != text+2) - return 0; - if (sec < 0) - return 0; - text = end; - if (*text == ':') { - if (sec >= 60) - return 0; - min = min*60 + sec; - text++; - sec = strtol(text, &end, 10); - if (end != text+2) - return 0; - if (sec < 0) - return 0; - text = end; - } - } - - /* Maybe we should accept 'min' at the end? */ - if (isspace(*text)) - text++; - if (*text) - return 0; - - *sec_p = min*60 + sec; - *rel_p = rel; - return 1; -} - -static int validate_depth(char *text, int *mm_p) -{ - int depth, imperial; - - if (!text) - return 0; - - depth = get_tenths(text, &text); - if (depth < 0) - return 0; - - while (isspace(*text)) - text++; - - imperial = get_output_units()->length == FEET; - if (*text == 'm') { - imperial = 0; - text++; - } else if (!strcasecmp(text, "ft")) { - imperial = 1; - text += 2; - } - while (isspace(*text)) - text++; - if (*text) - return 0; - - if (imperial) { - depth = feet_to_mm(depth / 10.0); - } else { - depth *= 100; - } - *mm_p = depth; - return 1; -} - -static GtkWidget *add_entry_to_box(GtkWidget *box, const char *label) -{ - GtkWidget *entry, *frame; - - entry = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry), 16); - if (label) { - frame = gtk_frame_new(label); - gtk_container_add(GTK_CONTAINER(frame), entry); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); - } else { - gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 2); - } - return entry; -} - -#define MAX_WAYPOINTS 8 -GtkWidget *entry_depth[MAX_WAYPOINTS], *entry_duration[MAX_WAYPOINTS], *entry_gas[MAX_WAYPOINTS]; -int nr_waypoints = 0; -static GtkListStore *gas_model = NULL; -struct diveplan diveplan = {}; -char *cache_data = NULL; -struct dive *planned_dive = NULL; - -/* make a copy of the diveplan so far and display the corresponding dive */ -void show_planned_dive(void) -{ - struct diveplan tempplan; - struct divedatapoint *dp, **dpp; - - memcpy(&tempplan, &diveplan, sizeof(struct diveplan)); - dpp = &tempplan.dp; - dp = diveplan.dp; - while(*dpp) { - *dpp = malloc(sizeof(struct divedatapoint)); - memcpy(*dpp, dp, sizeof(struct divedatapoint)); - dp = dp->next; - if (dp && !dp->time) { - /* we have an incomplete entry - stop before it */ - (*dpp)->next = NULL; - break; - } - dpp = &(*dpp)->next; - } - plan(&tempplan, &cache_data, &planned_dive); - free_dps(tempplan.dp); -} - -static gboolean gas_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) -{ - char *gastext; - int o2, he; - int idx = data - NULL; - - gastext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); - 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(); - } else { - /* we need to instead change the color of the input field or something */ - printf("invalid gas for row %d\n",idx); - } - free(gastext); - return FALSE; -} - -static gboolean depth_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) -{ - char *depthtext; - int depth; - int idx = data - NULL; - - depthtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); - if (validate_depth(depthtext, &depth)) { - add_depth_to_nth_dp(&diveplan, idx, depth); - show_planned_dive(); - } else { - /* we need to instead change the color of the input field or something */ - printf("invalid depth for row %d\n", idx); - } - free(depthtext); - return FALSE; -} - -static gboolean duration_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - char *durationtext; - int duration, is_rel; - int idx = data - NULL; - - durationtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); - if (validate_time(durationtext, &duration, &is_rel)) { - add_duration_to_nth_dp(&diveplan, idx, duration, is_rel); - show_planned_dive(); - } else { - /* we need to instead change the color of the input field or something */ - printf("invalid duration for row %d\n", idx); - } - free(durationtext); - return FALSE; -} - -static gboolean starttime_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - char *starttimetext; - int starttime, is_rel; - - starttimetext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); - if (validate_time(starttimetext, &starttime, &is_rel)) { - /* we alway make this relative for now */ - diveplan.when = time(NULL) + starttime; - } else { - /* we need to instead change the color of the input field or something */ - printf("invalid starttime\n"); - } - free(starttimetext); - return FALSE; -} - -static GtkWidget *add_gas_combobox_to_box(GtkWidget *box, const char *label) -{ - GtkWidget *frame, *combo; - GtkEntryCompletion *completion; - GtkEntry *entry; - - 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 @ 1.6", gas_model); - } - combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(gas_model), 0); - 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); - - 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); - } - entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo))); - completion = gtk_entry_completion_new(); - gtk_entry_completion_set_text_column(completion, 0); - gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(gas_model)); - gtk_entry_completion_set_inline_completion(completion, TRUE); - gtk_entry_completion_set_inline_selection(completion, TRUE); - gtk_entry_completion_set_popup_single_match(completion, FALSE); - gtk_entry_set_completion(entry, completion); - g_object_unref(completion); - - 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, _("Gas Used")); - } 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); - } - 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); -} - -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 { - // some error - } -} - -void input_plan() -{ - GtkWidget *planner, *content, *vbox, *outervbox, *add_row, *deltat; - int lasttime = 0; - char starttimebuf[64] = "+60:00"; - - if (diveplan.dp) - free_dps(diveplan.dp); - memset(&diveplan, 0, sizeof(diveplan)); - 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); - vbox = gtk_vbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(outervbox), vbox, TRUE, TRUE, 0); - deltat = add_entry_to_box(vbox, _("Dive starts in how many minutes?")); - gtk_entry_set_max_length(GTK_ENTRY(deltat), 12); - gtk_entry_set_text(GTK_ENTRY(deltat), starttimebuf); - gtk_widget_add_events(deltat, GDK_FOCUS_CHANGE_MASK); - g_signal_connect(deltat, "focus-out-event", G_CALLBACK(starttime_focus_out_cb), NULL); - diveplan.when = time(NULL) + 3600; - 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) { - int i; - const char *deltattext; - - deltattext = gtk_entry_get_text(GTK_ENTRY(deltat)); - diveplan.when = time(NULL) + 60 * atoi(deltattext); - free_dps(diveplan.dp); - diveplan.dp = 0; - for (i = 0; i < nr_waypoints; i++) { - char *depthtext, *durationtext, *gastext; - int depth, duration, o2, he, is_rel; - GtkWidget *entry; - - depthtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry_depth[i]))); - if (!validate_depth(depthtext, &depth)) { - // mark error and redo? - free(depthtext); - continue; - } - durationtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry_duration[i]))); - if (!validate_time(durationtext, &duration, &is_rel)) { - // mark error and redo? - free(durationtext); - continue; - } - if (!is_rel) - duration -= lasttime; - entry = gtk_bin_get_child(GTK_BIN(entry_gas[i])); - gastext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); - if (!validate_gas(gastext, &o2, &he)) { - // mark error and redo? - free(gastext); - continue; - } - /* just in case this didn't get added by the callback */ - add_string_list_entry(gastext, gas_model); - - // still need to handle desired pO2 and a setpoint (for CC) - - if (duration == 0) - break; - plan_add_segment(&diveplan, duration, depth, o2, he); - lasttime += duration; - free(depthtext); - free(durationtext); - free(gastext); - } - } - show_planned_dive(); - gtk_widget_destroy(planner); -} - static GtkActionEntry menu_items[] = { { "FileMenuAction", NULL, N_("File"), NULL, NULL, NULL}, { "LogMenuAction", NULL, N_("Log"), NULL, NULL, NULL}, @@ -4,9 +4,13 @@ * * (c) Dirk Hohndel 2013 */ - +#include <libintl.h> +#include <glib/gi18n.h> +#include <unistd.h> +#include <ctype.h> #include "dive.h" #include "divelist.h" +#include "display-gtk.h" int stoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 21000, 30000, 42000, 60000, 90000 }; @@ -285,3 +289,458 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) report_dives(FALSE, FALSE); select_last_dive(); } + + +/* and now the UI for all this */ +/* + * Get a value in tenths (so "10.2" == 102, "9" = 90) + * + * Return negative for errors. + */ +static int get_tenths(char *begin, char **end) +{ + int value = strtol(begin, end, 10); + if (begin == *end) + return -1; + value *= 10; + + /* Fraction? We only look at the first digit */ + if (**end == '.') { + ++*end; + if (!isdigit(**end)) + return -1; + value += **end - '0'; + do { + ++*end; + } while (isdigit(**end)); + } + return value; +} + +static int get_permille(char *begin, char **end) +{ + int value = get_tenths(begin, end); + if (value >= 0) { + /* Allow a percentage sign */ + if (**end == '%') + ++*end; + } + return value; +} + +static int validate_gas(char *text, int *o2_p, int *he_p) +{ + int o2, he; + + if (!text) + return 0; + + while (isspace(*text)) + text++; + + if (!*text) { + o2 = AIR_PERMILLE; he = 0; + } else if (!strcasecmp(text, "air")) { + o2 = AIR_PERMILLE; he = 0; text += 3; + } else if (!strncasecmp(text, "ean", 3)) { + o2 = get_permille(text+3, &text); he = 0; + } else { + o2 = get_permille(text, &text); he = 0; + if (*text == '/') + he = get_permille(text+1, &text); + } + + /* We don't want any extra crud */ + while (isspace(*text)) + text++; + if (*text) + return 0; + + /* Validate the gas mix */ + if (*text || o2 < 1 || o2 > 1000 || he < 0 || o2+he > 1000) + return 0; + + /* Let it rip */ + *o2_p = o2; + *he_p = he; + return 1; +} + +static int validate_time(char *text, int *sec_p, int *rel_p) +{ + int min, sec, rel; + char *end; + + if (!text) + return 0; + + while (isspace(*text)) + text++; + + rel = 0; + if (*text == '+') { + rel = 1; + text++; + while (isspace(*text)) + text++; + } + + min = strtol(text, &end, 10); + if (text == end) + return 0; + + if (min < 0 || min > 1000) + return 0; + + /* Ok, minutes look ok */ + text = end; + sec = 0; + if (*text == ':') { + text++; + sec = strtol(text, &end, 10); + if (end != text+2) + return 0; + if (sec < 0) + return 0; + text = end; + if (*text == ':') { + if (sec >= 60) + return 0; + min = min*60 + sec; + text++; + sec = strtol(text, &end, 10); + if (end != text+2) + return 0; + if (sec < 0) + return 0; + text = end; + } + } + + /* Maybe we should accept 'min' at the end? */ + if (isspace(*text)) + text++; + if (*text) + return 0; + + *sec_p = min*60 + sec; + *rel_p = rel; + return 1; +} + +static int validate_depth(char *text, int *mm_p) +{ + int depth, imperial; + + if (!text) + return 0; + + depth = get_tenths(text, &text); + if (depth < 0) + return 0; + + while (isspace(*text)) + text++; + + imperial = get_output_units()->length == FEET; + if (*text == 'm') { + imperial = 0; + text++; + } else if (!strcasecmp(text, "ft")) { + imperial = 1; + text += 2; + } + while (isspace(*text)) + text++; + if (*text) + return 0; + + if (imperial) { + depth = feet_to_mm(depth / 10.0); + } else { + depth *= 100; + } + *mm_p = depth; + return 1; +} + +static GtkWidget *add_entry_to_box(GtkWidget *box, const char *label) +{ + GtkWidget *entry, *frame; + + entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry), 16); + if (label) { + frame = gtk_frame_new(label); + gtk_container_add(GTK_CONTAINER(frame), entry); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); + } else { + gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 2); + } + return entry; +} + +#define MAX_WAYPOINTS 8 +GtkWidget *entry_depth[MAX_WAYPOINTS], *entry_duration[MAX_WAYPOINTS], *entry_gas[MAX_WAYPOINTS]; +int nr_waypoints = 0; +static GtkListStore *gas_model = NULL; +struct diveplan diveplan = {}; +char *cache_data = NULL; +struct dive *planned_dive = NULL; + +/* make a copy of the diveplan so far and display the corresponding dive */ +void show_planned_dive(void) +{ + struct diveplan tempplan; + struct divedatapoint *dp, **dpp; + + memcpy(&tempplan, &diveplan, sizeof(struct diveplan)); + dpp = &tempplan.dp; + dp = diveplan.dp; + while(*dpp) { + *dpp = malloc(sizeof(struct divedatapoint)); + memcpy(*dpp, dp, sizeof(struct divedatapoint)); + dp = dp->next; + if (dp && !dp->time) { + /* we have an incomplete entry - stop before it */ + (*dpp)->next = NULL; + break; + } + dpp = &(*dpp)->next; + } + plan(&tempplan, &cache_data, &planned_dive); + free_dps(tempplan.dp); +} + +static gboolean gas_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) +{ + char *gastext; + int o2, he; + int idx = data - NULL; + + gastext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + 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(); + } else { + /* we need to instead change the color of the input field or something */ + printf("invalid gas for row %d\n",idx); + } + free(gastext); + return FALSE; +} + +static gboolean depth_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) +{ + char *depthtext; + int depth; + int idx = data - NULL; + + depthtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + if (validate_depth(depthtext, &depth)) { + add_depth_to_nth_dp(&diveplan, idx, depth); + show_planned_dive(); + } else { + /* we need to instead change the color of the input field or something */ + printf("invalid depth for row %d\n", idx); + } + free(depthtext); + return FALSE; +} + +static gboolean duration_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) +{ + char *durationtext; + int duration, is_rel; + int idx = data - NULL; + + durationtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + if (validate_time(durationtext, &duration, &is_rel)) { + add_duration_to_nth_dp(&diveplan, idx, duration, is_rel); + show_planned_dive(); + } else { + /* we need to instead change the color of the input field or something */ + printf("invalid duration for row %d\n", idx); + } + free(durationtext); + return FALSE; +} + +static gboolean starttime_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) +{ + char *starttimetext; + int starttime, is_rel; + + starttimetext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + if (validate_time(starttimetext, &starttime, &is_rel)) { + /* we alway make this relative for now */ + diveplan.when = time(NULL) + starttime; + } else { + /* we need to instead change the color of the input field or something */ + printf("invalid starttime\n"); + } + free(starttimetext); + return FALSE; +} + +static GtkWidget *add_gas_combobox_to_box(GtkWidget *box, const char *label) +{ + GtkWidget *frame, *combo; + GtkEntryCompletion *completion; + GtkEntry *entry; + + 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 @ 1.6", gas_model); + } + combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(gas_model), 0); + 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); + + 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); + } + entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo))); + completion = gtk_entry_completion_new(); + gtk_entry_completion_set_text_column(completion, 0); + gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(gas_model)); + gtk_entry_completion_set_inline_completion(completion, TRUE); + gtk_entry_completion_set_inline_selection(completion, TRUE); + gtk_entry_completion_set_popup_single_match(completion, FALSE); + gtk_entry_set_completion(entry, completion); + g_object_unref(completion); + + 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, _("Gas Used")); + } 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); + } + 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); +} + +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 { + // some error + } +} + +void input_plan() +{ + GtkWidget *planner, *content, *vbox, *outervbox, *add_row, *deltat; + int lasttime = 0; + char starttimebuf[64] = "+60:00"; + + if (diveplan.dp) + free_dps(diveplan.dp); + memset(&diveplan, 0, sizeof(diveplan)); + 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); + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(outervbox), vbox, TRUE, TRUE, 0); + deltat = add_entry_to_box(vbox, _("Dive starts in how many minutes?")); + gtk_entry_set_max_length(GTK_ENTRY(deltat), 12); + gtk_entry_set_text(GTK_ENTRY(deltat), starttimebuf); + gtk_widget_add_events(deltat, GDK_FOCUS_CHANGE_MASK); + g_signal_connect(deltat, "focus-out-event", G_CALLBACK(starttime_focus_out_cb), NULL); + diveplan.when = time(NULL) + 3600; + 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) { + int i; + const char *deltattext; + + deltattext = gtk_entry_get_text(GTK_ENTRY(deltat)); + diveplan.when = time(NULL) + 60 * atoi(deltattext); + free_dps(diveplan.dp); + diveplan.dp = 0; + for (i = 0; i < nr_waypoints; i++) { + char *depthtext, *durationtext, *gastext; + int depth, duration, o2, he, is_rel; + GtkWidget *entry; + + depthtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry_depth[i]))); + if (!validate_depth(depthtext, &depth)) { + // mark error and redo? + free(depthtext); + continue; + } + durationtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry_duration[i]))); + if (!validate_time(durationtext, &duration, &is_rel)) { + // mark error and redo? + free(durationtext); + continue; + } + if (!is_rel) + duration -= lasttime; + entry = gtk_bin_get_child(GTK_BIN(entry_gas[i])); + gastext = strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + if (!validate_gas(gastext, &o2, &he)) { + // mark error and redo? + free(gastext); + continue; + } + /* just in case this didn't get added by the callback */ + add_string_list_entry(gastext, gas_model); + + // still need to handle desired pO2 and a setpoint (for CC) + + if (duration == 0) + break; + plan_add_segment(&diveplan, duration, depth, o2, he); + lasttime += duration; + free(depthtext); + free(durationtext); + free(gastext); + } + } + show_planned_dive(); + gtk_widget_destroy(planner); +} |