diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | dive.h | 2 | ||||
-rw-r--r-- | planner.c | 478 | ||||
-rw-r--r-- | planner.h | 21 |
4 files changed, 67 insertions, 437 deletions
@@ -162,7 +162,8 @@ LIBS = $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPU MSGLANGS=$(notdir $(wildcard po/*.po)) MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o planner.o \ +OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o divelist-gtk.o deco.o \ + planner.o planner-gtk.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ gtk-gui.o statistics.o file.o cochran.o device.o download-dialog.o prefs.o \ webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) @@ -685,9 +685,7 @@ struct diveplan { struct divedatapoint *dp; }; -void plan(struct diveplan *diveplan, char **cache_datap, struct dive **divep); void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2, int he, int po2); -void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel); void add_depth_to_nth_dp(struct diveplan *diveplan, int idx, int depth); void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he); void free_dps(struct divedatapoint *dp); @@ -10,7 +10,7 @@ #include <ctype.h> #include "dive.h" #include "divelist.h" -#include "display-gtk.h" +#include "planner.h" int decostoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000, 30000, 33000, 36000, 39000, 42000, 45000, 48000, 51000, 54000, 57000, @@ -21,7 +21,6 @@ int decostoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, }; double plangflow, plangfhigh; char *disclaimer; -GtkWidget *planner, *planner_error_bar, *error_label; #if DEBUG_PLAN void dump_plan(struct diveplan *diveplan) @@ -48,46 +47,6 @@ void dump_plan(struct diveplan *diveplan) } #endif -static void on_error_bar_response(GtkWidget *widget, gint response, gpointer data) -{ - if (response == GTK_RESPONSE_OK) - { - gtk_widget_destroy(widget); - planner_error_bar = NULL; - error_label = NULL; - } -} - -static void show_error(const char *fmt, ...) -{ - va_list args; - GError *error; - GtkWidget *box, *container; - gboolean bar_is_visible = TRUE; - - va_start(args, fmt); - error = g_error_new_valist(g_quark_from_string("subsurface"), DIVE_ERROR_PLAN, fmt, args); - va_end(args); - if (!planner_error_bar) { - planner_error_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); - g_signal_connect(planner_error_bar, "response", G_CALLBACK(on_error_bar_response), NULL); - gtk_info_bar_set_message_type(GTK_INFO_BAR(planner_error_bar), GTK_MESSAGE_ERROR); - bar_is_visible = FALSE; - } - container = gtk_info_bar_get_content_area(GTK_INFO_BAR(planner_error_bar)); - if (error_label) - gtk_container_remove(GTK_CONTAINER(container), error_label); - error_label = gtk_label_new(error->message); - gtk_container_add(GTK_CONTAINER(container), error_label); - box = gtk_dialog_get_content_area(GTK_DIALOG(planner)); - if (!bar_is_visible) - gtk_box_pack_start(GTK_BOX(box), planner_error_bar, FALSE, FALSE, 0); - gtk_widget_show_all(box); - /* make sure this actually gets shown BEFORE the calculations run */ - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); -} - void get_gas_from_events(struct divecomputer *dc, int time, int *o2, int *he) { struct event *event = dc->events; @@ -132,13 +91,14 @@ void get_gas_string(int o2, int he, char *text, int len) } /* returns the tissue tolerance at the end of this (partial) dive */ -double tissue_at_end(struct dive *dive, char **cached_datap) +double tissue_at_end(struct dive *dive, char **cached_datap, char **error_string_p) { struct divecomputer *dc; struct sample *sample, *psample; int i, j, t0, t1, gasidx, lastdepth; int o2, he; double tissue_tolerance; + static char buf[200]; if (!dive) return 0.0; @@ -160,7 +120,8 @@ double tissue_at_end(struct dive *dive, char **cached_datap) t1 = sample->time.seconds; get_gas_from_events(&dive->dc, t0, &o2, &he); if ((gasidx = get_gasidx(dive, o2, he)) == -1) { - show_error(_("Can't find gas %d/%d"), (o2 + 5) / 10, (he + 5) / 10); + snprintf(buf, sizeof(buf),_("Can't find gas %d/%d"), (o2 + 5) / 10, (he + 5) / 10); + *error_string_p = buf; gasidx = 0; } if (i > 0) @@ -177,7 +138,7 @@ double tissue_at_end(struct dive *dive, char **cached_datap) } /* how many seconds until we can ascend to the next stop? */ -int time_at_last_depth(struct dive *dive, int o2, int he, int next_stop, char **cached_data_p) +int time_at_last_depth(struct dive *dive, int o2, int he, int next_stop, char **cached_data_p, char **error_string_p) { int depth, gasidx; double surface_pressure, tissue_tolerance; @@ -187,7 +148,7 @@ int time_at_last_depth(struct dive *dive, int o2, int he, int next_stop, char ** if (!dive) return 0; surface_pressure = dive->dc.surface_pressure.mbar / 1000.0; - tissue_tolerance = tissue_at_end(dive, cached_data_p); + tissue_tolerance = tissue_at_end(dive, cached_data_p, error_string_p); sample = &dive->dc.sample[dive->dc.samples - 1]; depth = sample->depth.mm; gasidx = get_gasidx(dive, o2, he); @@ -214,7 +175,6 @@ int add_gas(struct dive *dive, int o2, int he) return i; } if (i == MAX_CYLINDERS) { - show_error(_("Too many gas mixes")); return -1; } mix->o2.permille = o2; @@ -225,7 +185,7 @@ int add_gas(struct dive *dive, int o2, int he) return i; } -struct dive *create_dive_from_plan(struct diveplan *diveplan) +struct dive *create_dive_from_plan(struct diveplan *diveplan, char **error_string) { struct dive *dive; struct divedatapoint *dp; @@ -235,6 +195,7 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) int oldpo2 = 0; int lasttime = 0; + *error_string = NULL; if (!diveplan || !diveplan->dp) return NULL; #if DEBUG_PLAN & 4 @@ -256,7 +217,8 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) sample = prepare_sample(dc); sample->po2 = dp->po2; finish_sample(dc); - add_gas(dive, oldo2, oldhe); + if (add_gas(dive, oldo2, oldhe) < 0) + goto gas_error_exit; while (dp) { int o2 = dp->o2, he = dp->he; int po2 = dp->po2; @@ -266,7 +228,8 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) if (time == 0) { /* special entries that just inform the algorithm about * additional gases that are available */ - add_gas(dive, o2, he); + if (add_gas(dive, o2, he) < 0) + goto gas_error_exit; dp = dp->next; continue; } @@ -289,7 +252,8 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) int plano2 = (o2 + 5) / 10 * 10; int planhe = (he + 5) / 10 * 10; int value; - add_gas(dive, plano2, planhe); + if (add_gas(dive, plano2, planhe) < 0) + goto gas_error_exit; value = (plano2 / 10) | ((planhe / 10) << 16); add_event(dc, lasttime, 25, 0, value, "gaschange"); // SAMPLE_EVENT_GASCHANGE2 oldo2 = o2; oldhe = he; @@ -316,6 +280,11 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan) save_dive(stdout, dive); #endif return dive; + +gas_error_exit: + free(dive); + *error_string = _("Too many gas mixes"); + return NULL; } void free_dps(struct divedatapoint *dp) @@ -359,7 +328,8 @@ struct divedatapoint *get_nth_dp(struct diveplan *diveplan, int idx) return dp; } -void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel) +/* return -1 to warn about potentially very long calculation */ +int add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel) { struct divedatapoint *pdp, *dp = get_nth_dp(diveplan, idx); if (idx > 0) { @@ -369,7 +339,8 @@ void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gb } dp->time = duration; if (duration > 180 * 60) - show_error(_("Warning - extremely long dives can cause long calculation time")); + return -1; + return 0; } /* this function is ONLY called from the dialog callback - so it @@ -612,7 +583,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive) dive->notes = strdup(buffer); } -void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) +void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, char **error_string_p) { struct dive *dive; struct sample *sample; @@ -629,7 +600,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) diveplan->surface_pressure = SURFACE_PRESSURE; if (*divep) delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan); + *divep = dive = create_dive_from_plan(diveplan, error_string_p); if (!dive) return; record_dive(dive); @@ -641,7 +612,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) get_gas_from_events(&dive->dc, sample->time.seconds, &o2, &he); po2 = dive->dc.sample[dive->dc.samples - 1].po2; depth = dive->dc.sample[dive->dc.samples - 1].depth.mm; - tissue_tolerance = tissue_at_end(dive, cached_datap); + tissue_tolerance = tissue_at_end(dive, cached_datap, error_string_p); ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); #if DEBUG_PLAN & 4 printf("gas %d/%d\n", o2, he); @@ -673,7 +644,9 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) plan_add_segment(diveplan, transitiontime, stoplevels[stopidx], o2, he, po2); /* re-create the dive */ delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan); + *divep = dive = create_dive_from_plan(diveplan, error_string_p); + if (!dive) + goto error_exit; record_dive(dive); } while (stopidx > 0) { /* this indicates that we aren't surfacing directly */ @@ -692,12 +665,12 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) #endif gi--; } - wait_time = time_at_last_depth(dive, o2, he, stoplevels[stopidx - 1], cached_datap); + wait_time = time_at_last_depth(dive, o2, he, stoplevels[stopidx - 1], cached_datap, error_string_p); /* typically deco plans are done in one minute increments; we may want to * make this configurable at some point */ wait_time = ((wait_time + 59) / 60) * 60; #if DEBUG_PLAN & 2 - tissue_tolerance = tissue_at_end(dive, cached_datap); + tissue_tolerance = tissue_at_end(dive, cached_datap, error_string_p); ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); printf("waittime %d:%02d at depth %5.2lfm; ceiling %5.2lfm\n", FRACTION(wait_time, 60), stoplevels[stopidx] / 1000.0, ceiling / 1000.0); @@ -711,7 +684,9 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) plan_add_segment(diveplan, transitiontime, stoplevels[stopidx - 1], o2, he, po2); /* re-create the dive */ delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan); + *divep = dive = create_dive_from_plan(diveplan, error_string_p); + if (!dive) + goto error_exit; record_dive(dive); stopidx--; } @@ -719,12 +694,11 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep) /* now make the dive visible in the dive list */ report_dives(FALSE, FALSE); show_and_select_dive(dive); +error_exit: free(stoplevels); free(gaschanges); } - -/* and now the UI for all this */ /* * Get a value in tenths (so "10.2" == 102, "9" = 90) * @@ -788,7 +762,7 @@ static int get_permille(const char *begin, const char **end) return value; } -static int validate_gas(const char *text, int *o2_p, int *he_p) +int validate_gas(const char *text, int *o2_p, int *he_p) { int o2, he; @@ -827,7 +801,7 @@ static int validate_gas(const char *text, int *o2_p, int *he_p) return 1; } -static int validate_time(const char *text, int *sec_p, int *rel_p) +int validate_time(const char *text, int *sec_p, int *rel_p) { int min, sec, rel; char *end; @@ -894,7 +868,7 @@ static int validate_time(const char *text, int *sec_p, int *rel_p) return 1; } -static int validate_depth(const char *text, int *mm_p) +int validate_depth(const char *text, int *mm_p) { int depth, imperial; @@ -933,7 +907,7 @@ static int validate_depth(const char *text, int *mm_p) return 1; } -static int validate_po2(const char *text, int *mbar_po2) +int validate_po2(const char *text, int *mbar_po2) { int po2; @@ -956,7 +930,7 @@ static int validate_po2(const char *text, int *mbar_po2) return 1; } -static int validate_volume(const char *text, int *sac) +int validate_volume(const char *text, int *sac) { int volume, imperial; @@ -992,32 +966,12 @@ static int validate_volume(const char *text, int *sac) return 1; } -static GtkWidget *add_entry_to_box(GtkWidget *box, const char *label) -{ - GtkWidget *entry, *frame; - - entry = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry), 16); - if (label) { - frame = gtk_frame_new(label); - gtk_container_add(GTK_CONTAINER(frame), entry); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); - } else { - gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 2); - } - return entry; -} - -#define MAX_WAYPOINTS 12 -GtkWidget *entry_depth[MAX_WAYPOINTS], *entry_duration[MAX_WAYPOINTS], *entry_gas[MAX_WAYPOINTS], *entry_po2[MAX_WAYPOINTS]; -int nr_waypoints = 0; -static GtkListStore *gas_model = NULL; struct diveplan diveplan = {}; char *cache_data = NULL; struct dive *planned_dive = NULL; /* make a copy of the diveplan so far and display the corresponding dive */ -void show_planned_dive(void) +void show_planned_dive(char **error_string_p) { struct diveplan tempplan; struct divedatapoint *dp, **dpp; @@ -1035,362 +989,18 @@ void show_planned_dive(void) printf("in show_planned_dive:\n"); dump_plan(&tempplan); #endif - plan(&tempplan, &cache_data, &planned_dive); + plan(&tempplan, &cache_data, &planned_dive, error_string_p); free_dps(tempplan.dp); } -static gboolean gas_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) -{ - const char *gastext; - int o2, he; - int idx = data - NULL; - - gastext = gtk_entry_get_text(GTK_ENTRY(entry)); - o2 = he = 0; - if (validate_gas(gastext, &o2, &he)) - add_string_list_entry(gastext, gas_model); - add_gas_to_nth_dp(&diveplan, idx, o2, he); - show_planned_dive(); - return FALSE; -} - -static void gas_changed_cb(GtkWidget *combo, gpointer data) -{ - const char *gastext; - int o2, he; - int idx = data - NULL; - - gastext = get_active_text(GTK_COMBO_BOX(combo)); - /* stupidly this gets called for two reasons: - * a) any keystroke into the entry field - * b) mouse selection of a dropdown - * we only care about b) (a) is handled much better with the focus-out event) - * so let's check that the text returned is actually in our model before going on - */ - if (match_list(gas_model, gastext) != MATCH_EXACT) - return; - o2 = he = 0; - if (!validate_gas(gastext, &o2, &he)) { - /* this should never happen as only validated texts should be - * in the dropdown */ - show_error(_("Invalid gas for row %d"),idx); - } - add_gas_to_nth_dp(&diveplan, idx, o2, he); - show_planned_dive(); -} - -static gboolean depth_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data) -{ - const char *depthtext; - int depth = -1; - int idx = data - NULL; - - depthtext = gtk_entry_get_text(GTK_ENTRY(entry)); - - if (validate_depth(depthtext, &depth)) { - if (depth > 150000) - show_error(_("Warning - planning very deep dives can take excessive amounts of time")); - add_depth_to_nth_dp(&diveplan, idx, depth); - show_planned_dive(); - } else { - /* it might be better to instead change the color of the input field or something */ - if (depth == -1) - show_error(_("Invalid depth - could not parse \"%s\""), depthtext); - else - show_error(_("Invalid depth - values deeper than 400m not supported")); - } - return FALSE; -} - -static gboolean duration_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *durationtext; - int duration, is_rel; - int idx = data - NULL; - - durationtext = gtk_entry_get_text(GTK_ENTRY(entry)); - if (validate_time(durationtext, &duration, &is_rel)) - add_duration_to_nth_dp(&diveplan, idx, duration, is_rel); - show_planned_dive(); - return FALSE; -} - -static gboolean po2_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *po2text; - int po2; - int idx = data - NULL; - - po2text = gtk_entry_get_text(GTK_ENTRY(entry)); - if (validate_po2(po2text, &po2)) - add_po2_to_nth_dp(&diveplan, idx, po2); - show_planned_dive(); - return FALSE; -} - /* Subsurface follows the lead of most divecomputers to use times * without timezone - so all times are implicitly assumed to be * local time of the dive location; so in order to give the current * time in that way we actually need to add the timezone offset */ -static timestamp_t current_time_notz(void) +timestamp_t current_time_notz(void) { time_t now = time(NULL); struct tm *local = localtime(&now); return utc_mktime(local); } -static gboolean starttime_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *starttimetext; - int starttime, is_rel; - - starttimetext = gtk_entry_get_text(GTK_ENTRY(entry)); - if (validate_time(starttimetext, &starttime, &is_rel)) { - /* we alway make this relative - either from the current time or from the - * end of the last dive, whichever is later */ - timestamp_t cur = current_time_notz(); - if (diveplan.lastdive_nr >= 0) { - struct dive *last_dive = get_dive(diveplan.lastdive_nr); - if (last_dive && last_dive->when + last_dive->dc.duration.seconds > cur) - cur = last_dive->when + last_dive->dc.duration.seconds; - } - diveplan.when = cur + starttime; - show_planned_dive(); - } else { - /* it might be better to instead change the color of the input field or something */ - show_error(_("Invalid starttime")); - } - return FALSE; -} - -static gboolean surfpres_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *surfprestext; - - surfprestext = gtk_entry_get_text(GTK_ENTRY(entry)); - diveplan.surface_pressure = atoi(surfprestext); - show_planned_dive(); - return FALSE; -} - -static gboolean sac_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *sactext; - - sactext = gtk_entry_get_text(GTK_ENTRY(entry)); - if (validate_volume(sactext, data)) - show_planned_dive(); - return FALSE; -} - -static gboolean gf_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - const char *gftext; - int gf; - double *gfp = data; - - gftext = gtk_entry_get_text(GTK_ENTRY(entry)); - if (sscanf(gftext, "%d", &gf) == 1) { - *gfp = gf / 100.0; - show_planned_dive(); - } - return FALSE; -} - -static gboolean last_stop_toggled_cb(GtkWidget *entry, GdkEvent * event, gpointer data) -{ - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(entry)) == TRUE) - decostoplevels[1] = 6000; - else - decostoplevels[1] = 3000; - show_planned_dive(); - return FALSE; -} - -static GtkWidget *add_gas_combobox_to_box(GtkWidget *box, const char *label, int idx) -{ - GtkWidget *frame, *combo; - - if (!gas_model) { - gas_model = gtk_list_store_new(1, G_TYPE_STRING); - add_string_list_entry(_("AIR"), gas_model); - add_string_list_entry(_("EAN32"), gas_model); - add_string_list_entry(_("EAN36"), gas_model); - } - combo = combo_box_with_model_and_entry(gas_model); - gtk_widget_add_events(combo, GDK_FOCUS_CHANGE_MASK); - g_signal_connect(gtk_bin_get_child(GTK_BIN(combo)), "focus-out-event", G_CALLBACK(gas_focus_out_cb), NULL + idx); - g_signal_connect(combo, "changed", G_CALLBACK(gas_changed_cb), NULL + idx); - if (label) { - frame = gtk_frame_new(label); - gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); - gtk_container_add(GTK_CONTAINER(frame), combo); - } else { - gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 2); - } - - return combo; -} - -static void add_waypoint_widgets(GtkWidget *box, int idx) -{ - GtkWidget *hbox; - - hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); - if (idx == 0) { - entry_depth[idx] = add_entry_to_box(hbox, _("Ending Depth")); - entry_duration[idx] = add_entry_to_box(hbox, _("Segment Time")); - entry_gas[idx] = add_gas_combobox_to_box(hbox, C_("Type of","Gas Used"), idx); - entry_po2[idx] = add_entry_to_box(hbox, _("CC SetPoint")); - } else { - entry_depth[idx] = add_entry_to_box(hbox, NULL); - entry_duration[idx] = add_entry_to_box(hbox, NULL); - entry_gas[idx] = add_gas_combobox_to_box(hbox, NULL, idx); - entry_po2[idx] = add_entry_to_box(hbox, NULL); - } - gtk_widget_add_events(entry_depth[idx], GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry_depth[idx], "focus-out-event", G_CALLBACK(depth_focus_out_cb), NULL + idx); - gtk_widget_add_events(entry_duration[idx], GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry_duration[idx], "focus-out-event", G_CALLBACK(duration_focus_out_cb), NULL + idx); - gtk_widget_add_events(entry_po2[idx], GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry_po2[idx], "focus-out-event", G_CALLBACK(po2_focus_out_cb), NULL + idx); -} - -static void add_waypoint_cb(GtkButton *button, gpointer _data) -{ - GtkWidget *vbox = _data; - if (nr_waypoints < MAX_WAYPOINTS) { - GtkWidget *ovbox, *dialog; - add_waypoint_widgets(vbox, nr_waypoints); - nr_waypoints++; - ovbox = gtk_widget_get_parent(GTK_WIDGET(button)); - dialog = gtk_widget_get_parent(ovbox); - gtk_widget_show_all(dialog); - } else { - show_error(_("Too many waypoints")); - } -} - -static void add_entry_with_callback(GtkWidget *box, int length, char *label, char *initialtext, - gboolean (*callback)(GtkWidget *, GdkEvent *, gpointer), gpointer data) -{ - GtkWidget *entry = add_entry_to_box(box, label); - gtk_entry_set_max_length(GTK_ENTRY(entry), length); - gtk_entry_set_text(GTK_ENTRY(entry), initialtext); - gtk_widget_add_events(entry, GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry, "focus-out-event", G_CALLBACK(callback), data); -} - -/* set up the dialog where the user can input their dive plan */ -void input_plan() -{ - GtkWidget *content, *vbox, *hbox, *outervbox, *add_row, *label; - char *bottom_sac, *deco_sac, gflowstring[4], gfhighstring[4]; - char *explanationtext = _("<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; - 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"); - - 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); - mark_divelist_changed(TRUE); - } else { - if (planned_dive) { - /* we have added a dive during the dynamic construction - * in the dialog; get rid of it */ - delete_single_dive(dive_table.nr - 1); - report_dives(FALSE, FALSE); - planned_dive = NULL; - } - } - gtk_widget_destroy(planner); - planner_error_bar = NULL; - error_label = NULL; - set_gf(prefs.gflow, prefs.gfhigh); -} diff --git a/planner.h b/planner.h new file mode 100644 index 000000000..099f640fc --- /dev/null +++ b/planner.h @@ -0,0 +1,21 @@ +#ifndef PLANNER_H +#define PLANNER_H + +extern void plan(struct diveplan *diveplan, char **cache_datap, struct dive **divep, char **error_string_p); +extern int validate_gas(const char *text, int *o2_p, int *he_p); +extern int validate_time(const char *text, int *sec_p, int *rel_p); +extern int validate_depth(const char *text, int *mm_p); +extern int validate_po2(const char *text, int *mbar_po2); +extern int validate_volume(const char *text, int *sac); +extern timestamp_t current_time_notz(void); +extern void show_planned_dive(char **error_string_p); +extern int add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel); +extern void add_po2_to_nth_dp(struct diveplan *diveplan, int idx, int po2); + +extern struct diveplan diveplan; +extern struct dive *planned_dive; +extern char *cache_data; +extern char *disclaimer; +extern double plangflow, plangfhigh; + +#endif /* PLANNER_H */ |