mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-08 02:56:17 +00:00
Move planner UI into planner.c
There should be NO other changes in this commit - just moving the code and adjusting the includes (and adding the entry point to display-gtk.h). Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
989cf37fcf
commit
d3570508b1
4 changed files with 465 additions and 456 deletions
4
Makefile
4
Makefile
|
@ -247,8 +247,8 @@ print.o: print.c dive.h display.h display-gtk.h
|
||||||
deco.o: deco.c dive.h
|
deco.o: deco.c dive.h
|
||||||
$(CC) $(CFLAGS) $(GLIB2CFLAGS) -c deco.c
|
$(CC) $(CFLAGS) $(GLIB2CFLAGS) -c deco.c
|
||||||
|
|
||||||
planner.o: planner.c dive.h
|
planner.o: planner.c dive.h divelist.h display-gtk.h
|
||||||
$(CC) $(CFLAGS) $(GLIB2CFLAGS) -c planner.c
|
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c planner.c
|
||||||
|
|
||||||
libdivecomputer.o: libdivecomputer.c dive.h display.h display-gtk.h libdivecomputer.h
|
libdivecomputer.o: libdivecomputer.c dive.h display.h display-gtk.h libdivecomputer.h
|
||||||
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \
|
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \
|
||||||
|
|
|
@ -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);
|
GError *uemis_download(const char *path, progressbar_t *progress, GtkDialog *dialog, gboolean force_download);
|
||||||
|
|
||||||
|
/* from planner.c */
|
||||||
|
extern void input_plan(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
453
gtk-gui.c
453
gtk-gui.c
|
@ -1125,459 +1125,6 @@ static void next_dc(GtkWidget *w, gpointer data)
|
||||||
repaint_dive();
|
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[] = {
|
static GtkActionEntry menu_items[] = {
|
||||||
{ "FileMenuAction", NULL, N_("File"), NULL, NULL, NULL},
|
{ "FileMenuAction", NULL, N_("File"), NULL, NULL, NULL},
|
||||||
{ "LogMenuAction", NULL, N_("Log"), NULL, NULL, NULL},
|
{ "LogMenuAction", NULL, N_("Log"), NULL, NULL, NULL},
|
||||||
|
|
461
planner.c
461
planner.c
|
@ -4,9 +4,13 @@
|
||||||
*
|
*
|
||||||
* (c) Dirk Hohndel 2013
|
* (c) Dirk Hohndel 2013
|
||||||
*/
|
*/
|
||||||
|
#include <libintl.h>
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "divelist.h"
|
#include "divelist.h"
|
||||||
|
#include "display-gtk.h"
|
||||||
|
|
||||||
int stoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 21000, 30000, 42000, 60000, 90000 };
|
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);
|
report_dives(FALSE, FALSE);
|
||||||
select_last_dive();
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue