summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--display-gtk.h3
-rw-r--r--gtk-gui.c453
-rw-r--r--planner.c461
4 files changed, 465 insertions, 456 deletions
diff --git a/Makefile b/Makefile
index 0f6bee13b..900a2c89a 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/gtk-gui.c b/gtk-gui.c
index d1ddc97cf..dfd00bdb6 100644
--- a/gtk-gui.c
+++ b/gtk-gui.c
@@ -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},
diff --git a/planner.c b/planner.c
index e437cce4f..45da6f157 100644
--- a/planner.c
+++ b/planner.c
@@ -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);
+}