mirror of
https://github.com/subsurface/subsurface.git
synced 2025-01-19 14:25:27 +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
|
||||
$(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) \
|
||||
|
|
|
@ -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
|
||||
|
|
453
gtk-gui.c
453
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},
|
||||
|
|
461
planner.c
461
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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue