[PATCH] Added a dive add/edit/delete dialog

John Van Ostrand john at netdirect.ca
Fri Nov 11 05:06:53 PST 2011


Dives can now be manually entered, altered and deleted. A new edit menu
option exists that provides three options, Add, Edit and Delete dive.

Signed-off-by: John Van Ostrand <john at netdirect.ca>
---
 detail.c |  481 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 detail.h |    5 +
 2 files changed, 486 insertions(+), 0 deletions(-)
 create mode 100644 detail.c
 create mode 100644 detail.h

diff --git a/detail.c b/detail.c
new file mode 100644
index 0000000..934ca74
--- /dev/null
+++ b/detail.c
@@ -0,0 +1,481 @@
+/* detail.c */
+/* creates the UI for the dive detail dialog
+ * controlled through the following interfaces:
+ * 
+ * int add_dive_detail(GtkWidget *w, gpointer data)
+ * int edit_dive_detail_dialog(GtkWidget *w, gpointer data)
+ * int delete_dive_detail_dialog(GtkWidget *w, gpointer data)
+ *
+ * called from gtk-ui main_menu.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "dive.h"
+#include "display.h"
+#include "display-gtk.h"
+#include "divelist.h"
+
+
+/* Generic time widget, used by time_of_day_widget. */
+struct time_widget_t {
+	GtkWidget *frame;
+	GtkWidget *hbox;
+	GtkWidget *hour_widget, *min_widget, *sec_widget;
+	GtkWidget *hour_frame, *min_frame, *sec_frame;
+};
+
+
+/* Data we are manipulating in the detail widget. */
+struct dive_data_t {
+	struct tm when;
+	double maxdepth, meandepth, watertemp;
+	int duration, surfacetime, otu;
+};
+
+
+/* Detail widget components and data. */
+struct detail_widget_t {
+	GtkWidget *vbox;
+	GtkWidget *date_frame, *date_hbox, *date_widget;
+	struct time_widget_t time;
+	struct time_widget_t surfacetime;
+	struct time_widget_t duration;
+	GtkWidget *maxdepth_frame, *maxdepth_widget;
+	GtkWidget *meandepth_frame, *meandepth_widget;
+	GtkWidget *watertemp_frame, *watertemp_widget;
+	GtkWidget *otu_frame, *otu_widget;
+	struct dive_data_t data, orig;
+};
+
+
+/* Save last entered dive, next dive may have similar information. */
+static struct dive_data_t last_entered_dive;
+
+
+#define get_time_widget_duration(time) (gtk_spin_button_get_value(GTK_SPIN_BUTTON(time.hour_widget)) * 3600 + gtk_spin_button_get_value(GTK_SPIN_BUTTON(time.min_widget)) * 60 + gtk_spin_button_get_value(GTK_SPIN_BUTTON(time.sec_widget)))
+
+
+/* Keep track of original values, done prior to editing a dive. */
+static void remember_orig_values(struct detail_widget_t *detail, struct dive *dive)
+{
+	memcpy(&detail->orig.when, gmtime(&dive->when), sizeof(detail->orig.when));
+
+	/* zero out members we don't use so we can bulk compare later. */
+	detail->orig.when.tm_wday = 0;
+	detail->orig.when.tm_yday = 0;
+	detail->orig.when.tm_isdst = 0;
+#ifdef  __USE_BSD
+	detail->orig.when.tm_gmtoff = 0;
+	detail->orig.when.tm_zone = 0;
+#else
+	detail->orig.when.__tm_gmtoff = 0;
+	detail->orig.when.__tm_zone =0;
+#endif
+
+	detail->orig.surfacetime = dive->surfacetime.seconds;
+	detail->orig.duration = dive->duration.seconds;
+	switch (output_units.length) {
+	case METERS:
+		detail->orig.maxdepth = (double) dive->maxdepth.mm / 1000;
+		detail->orig.meandepth = (double) dive->meandepth.mm / 1000;
+		break;
+	case FEET:
+		detail->orig.maxdepth = to_feet(dive->maxdepth);
+		detail->orig.meandepth = to_feet(dive->meandepth);
+		break;
+	}
+	detail->orig.otu = dive->otu;
+	switch (output_units.temperature) {
+	case CELSIUS:
+		detail->orig.watertemp = to_C(dive->watertemp);
+		break;
+	case FAHRENHEIT:
+		detail->orig.watertemp = to_F(dive->watertemp);
+		break;
+	case KELVIN:
+		detail->orig.watertemp = (double) dive->watertemp.mkelvin / 1000;
+	}
+}
+
+
+/* Extract data entered in dialog. */
+static void pull_detail_data(struct detail_widget_t *detail)
+{
+	int year, month, day;
+
+	gtk_calendar_get_date(GTK_CALENDAR(detail->date_widget), &year, &month, &day);
+	detail->data.when.tm_year = year - 1900;
+	detail->data.when.tm_mon = month;
+	detail->data.when.tm_mday = day;
+	detail->data.when.tm_hour = gtk_spin_button_get_value(GTK_SPIN_BUTTON(detail->time.hour_widget));
+	detail->data.when.tm_min = gtk_spin_button_get_value(GTK_SPIN_BUTTON(detail->time.min_widget));
+	detail->data.when.tm_sec = gtk_spin_button_get_value(GTK_SPIN_BUTTON(detail->time.sec_widget));
+
+	/* Zero out unused tm members so comparison works. */
+	detail->data.when.tm_wday = 0;
+	detail->data.when.tm_yday = 0;
+	detail->data.when.tm_isdst = 0;
+#ifdef  __USE_BSD
+	detail->data.when.tm_gmtoff = 0;
+	detail->data.when.tm_zone = 0;
+#else
+	detail->data.when.__tm_gmtoff = 0;
+	detail->data.when.__tm_zone =0;
+#endif
+	detail->data.maxdepth = gtk_spin_button_get_value(GTK_SPIN_BUTTON(detail->maxdepth_widget));
+	detail->data.meandepth = gtk_spin_button_get_value(GTK_SPIN_BUTTON(detail->meandepth_widget));
+	detail->data.watertemp = gtk_spin_button_get_value(GTK_SPIN_BUTTON(detail->watertemp_widget));
+	detail->data.surfacetime = get_time_widget_duration(detail->surfacetime);
+	detail->data.duration = get_time_widget_duration(detail->duration);
+	detail->data.otu = gtk_spin_button_get_value(GTK_SPIN_BUTTON(detail->otu_widget));
+}
+
+/* Has the data been changed in the widget? */
+static int detail_changed(struct detail_widget_t *detail)
+{
+	return memcmp(&detail->data, &detail->orig, sizeof(detail->data));
+}
+
+
+/* Set dive data to the last entered dive, if none, then default to today. */
+static void set_default_dive_data(struct dive *dive)
+{
+	struct tm *today;
+	time_t now;
+
+	if (last_entered_dive.when.tm_year == 0 && last_entered_dive.when.tm_mon == 0 && last_entered_dive.when.tm_mday == 0)
+	{
+		/* Assume no previous entry */
+		now = time(NULL);
+		today = localtime(&now);
+		last_entered_dive.when.tm_year = today->tm_year;
+		last_entered_dive.when.tm_mon = today->tm_mon;
+		last_entered_dive.when.tm_mday = today->tm_mday;
+	}
+
+	/* Set dive data. */
+	dive->when = utc_mktime(&last_entered_dive.when);
+}
+
+
+/* Widget to ask for time of day, in 24 hour clock */
+static void time_of_day_widget(struct time_widget_t *time, uint hour, uint min, uint sec) 
+{
+	time->hbox = gtk_hbox_new(FALSE, 3);
+	time->hour_frame = gtk_frame_new("Hour");
+	time->min_frame = gtk_frame_new("Minute");
+	time->sec_frame = gtk_frame_new("Second");
+
+	time->hour_widget = gtk_spin_button_new_with_range(0, 23, 1);
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(time->hour_widget), hour);
+	gtk_container_add(GTK_CONTAINER(time->hour_frame), time->hour_widget);
+	gtk_box_pack_start(GTK_BOX(time->hbox), time->hour_frame, FALSE, FALSE, 0);
+
+	time->min_widget = gtk_spin_button_new_with_range(0, 59, 1);
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(time->min_widget), min);
+	gtk_container_add(GTK_CONTAINER(time->min_frame), time->min_widget);
+	gtk_box_pack_start(GTK_BOX(time->hbox), time->min_frame, FALSE, FALSE, 0);
+
+	time->sec_widget = gtk_spin_button_new_with_range(0, 59, 1);
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(time->sec_widget), sec);
+	gtk_container_add(GTK_CONTAINER(time->sec_frame), time->sec_widget);
+	gtk_box_pack_start(GTK_BOX(time->hbox), time->sec_frame, FALSE, FALSE, 0);
+
+	gtk_container_add(GTK_CONTAINER(time->frame), time->hbox);
+}
+
+
+/* widget to ask for interval: SIT or dive duration */
+static void duration_widget(struct time_widget_t *time, uint hour, uint min, uint sec)
+{
+
+	time->hbox = gtk_hbox_new(FALSE, 3);
+	time->hour_frame = gtk_frame_new("Hour");
+	time->min_frame = gtk_frame_new("Minute");
+	time->sec_frame = gtk_frame_new("Second");
+
+	time->hour_widget = gtk_spin_button_new_with_range(0, 99, 1);
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(time->hour_widget), hour);
+	gtk_container_add(GTK_CONTAINER(time->hour_frame), time->hour_widget);
+	gtk_box_pack_start(GTK_BOX(time->hbox), time->hour_frame, FALSE, FALSE, 0);
+
+	time->min_widget = gtk_spin_button_new_with_range(0, 59, 1);
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(time->min_widget), min);
+	gtk_container_add(GTK_CONTAINER(time->min_frame), time->min_widget);
+	gtk_box_pack_start(GTK_BOX(time->hbox), time->min_frame, FALSE, FALSE, 0);
+
+	time->sec_widget = gtk_spin_button_new_with_range(0, 59, 1);
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(time->sec_widget), sec);
+	gtk_container_add(GTK_CONTAINER(time->sec_frame), time->sec_widget);
+	gtk_box_pack_start(GTK_BOX(time->hbox), time->sec_frame, FALSE, FALSE, 0);
+
+	gtk_container_add(GTK_CONTAINER(time->frame), time->hbox);
+}
+
+
+/* Build the core of the dive detail dialog. */
+static void dive_detail_widget(GtkWidget *container, struct detail_widget_t *detail, struct dive *dive)
+{
+	struct tm *tm;
+	guint year, month, day, hour, min, sec;
+	uint dur_hour, dur_min, dur_sec;
+	uint sit_hour, sit_min, sit_sec;
+
+	detail->vbox = gtk_vbox_new(FALSE, 6);
+
+	tm = gmtime(&dive->when);
+	year = tm->tm_year + 1900;
+	month = tm->tm_mon;
+	day = tm->tm_mday;
+	hour = tm->tm_hour;
+	min = tm->tm_min;
+	sec = tm->tm_sec;
+
+	dur_hour = (uint) dive->duration.seconds / 3600;
+	dur_min = ((uint) dive->duration.seconds - dur_hour * 3600) / 60;
+	dur_sec = (uint) dive->duration.seconds - dur_hour * 3600 - dur_min * 60;
+
+	sit_hour = (uint) dive->surfacetime.seconds / 3600;
+	sit_min = ((uint) dive->surfacetime.seconds - sit_hour * 3600) / 60;
+	sit_sec = (uint) dive->surfacetime.seconds - sit_hour * 3600 - sit_min * 60;
+
+	/* Date */
+	detail->date_frame = gtk_frame_new("Date (mm/dd/yyy)");
+	detail->date_hbox = gtk_hbox_new(FALSE, 3);
+	gtk_container_add(GTK_CONTAINER(detail->date_frame), detail->date_hbox);
+	detail->date_widget = gtk_calendar_new();
+
+	gtk_calendar_select_month(GTK_CALENDAR(detail->date_widget), month, year);
+	gtk_calendar_select_day(GTK_CALENDAR(detail->date_widget), day);
+	gtk_calendar_mark_day(GTK_CALENDAR(detail->date_widget), day);
+
+	gtk_box_pack_start(GTK_BOX(detail->date_hbox), detail->date_widget, FALSE, FALSE, 0);
+	
+	gtk_box_pack_start(GTK_BOX(detail->vbox), detail->date_frame, FALSE, FALSE, 0);
+
+	/* Time */
+	detail->time.frame = gtk_frame_new("Time");
+	time_of_day_widget(&detail->time, hour, min, sec);
+	gtk_box_pack_start(GTK_BOX(detail->vbox), detail->time.frame, FALSE, FALSE, 0);
+
+	/* Surface Interval */
+	detail->surfacetime.frame = gtk_frame_new("Surface interval");
+	duration_widget(&detail->surfacetime, sit_hour, sit_min, sit_sec);
+	gtk_box_pack_start(GTK_BOX(detail->vbox), detail->surfacetime.frame, FALSE, FALSE, 0);
+
+	/* Duration */
+	detail->duration.frame = gtk_frame_new("Duration");
+	duration_widget(&detail->duration, dur_hour, dur_min, dur_sec);
+	gtk_box_pack_start(GTK_BOX(detail->vbox), detail->duration.frame, FALSE, FALSE, 0);
+
+	switch (output_units.length) {
+	case METERS:
+		/* Max depth */
+		detail->maxdepth_frame = gtk_frame_new("Max depth (m)");
+		detail->maxdepth_widget = gtk_spin_button_new_with_range(0, 999, 0.1);
+		gtk_spin_button_set_value(GTK_SPIN_BUTTON(detail->maxdepth_widget), (double) dive->maxdepth.mm / 1000);
+
+		/* Average Depth */
+		detail->meandepth_frame = gtk_frame_new("Avg depth (m)");
+		detail->meandepth_widget = gtk_spin_button_new_with_range(0, 999, 0.1);
+		gtk_spin_button_set_value(GTK_SPIN_BUTTON(detail->meandepth_widget), (double) dive->meandepth.mm / 1000);
+		break;
+	case FEET:
+		/* Max depth */
+		detail->maxdepth_frame = gtk_frame_new("Max depth (ft)");
+		detail->maxdepth_widget = gtk_spin_button_new_with_range(0, 999, 1);
+		gtk_spin_button_set_value(GTK_SPIN_BUTTON(detail->maxdepth_widget), to_feet(dive->maxdepth));
+	
+		/* Average Depth */
+		detail->meandepth_frame = gtk_frame_new("Avg depth (ft)");
+		detail->meandepth_widget = gtk_spin_button_new_with_range(0, 999, 1);
+		gtk_spin_button_set_value(GTK_SPIN_BUTTON(detail->meandepth_widget), to_feet(dive->meandepth));
+	}
+	gtk_container_add(GTK_CONTAINER(detail->maxdepth_frame), (GtkWidget *) detail->maxdepth_widget);
+	gtk_box_pack_start(GTK_BOX(detail->vbox), detail->maxdepth_frame, FALSE, FALSE, 0);
+
+	gtk_container_add(GTK_CONTAINER(detail->meandepth_frame), (GtkWidget *) detail->meandepth_widget);
+	gtk_box_pack_start(GTK_BOX(detail->vbox), detail->meandepth_frame, FALSE, FALSE, 0);
+
+	/* Water Temp */
+	detail->watertemp_frame = gtk_frame_new("Water Temp");
+	switch (output_units.temperature) {
+	case CELSIUS:
+		detail->watertemp_frame = gtk_frame_new("Water Temp (C)");
+		detail->watertemp_widget = gtk_spin_button_new_with_range(-2, 40, 1);
+		gtk_spin_button_set_value(GTK_SPIN_BUTTON(detail->watertemp_widget), detail->orig.watertemp);
+		break;
+	case FAHRENHEIT:
+		detail->watertemp_frame = gtk_frame_new("Water Temp (F)");
+		detail->watertemp_widget = gtk_spin_button_new_with_range(28, 104, 1);
+		gtk_spin_button_set_value(GTK_SPIN_BUTTON(detail->watertemp_widget), detail->orig.watertemp);
+		break;
+	case KELVIN:
+		detail->watertemp_frame = gtk_frame_new("Water Temp (K)");
+		detail->watertemp_widget = gtk_spin_button_new_with_range(271, 313, 1);
+		gtk_spin_button_set_value(GTK_SPIN_BUTTON(detail->watertemp_widget), detail->orig.watertemp);
+	}
+	gtk_container_add(GTK_CONTAINER(detail->watertemp_frame), (GtkWidget *) detail->watertemp_widget);
+
+	gtk_box_pack_start(GTK_BOX(detail->vbox), detail->watertemp_frame, FALSE, FALSE, 0);
+	
+	/* OTU */
+	detail->otu_frame = gtk_frame_new("OTU");
+	detail->otu_widget = gtk_spin_button_new_with_range(0, 100, 1);
+	gtk_spin_button_set_value(GTK_SPIN_BUTTON(detail->otu_widget), dive->otu);
+	gtk_container_add(GTK_CONTAINER(detail->otu_frame), (GtkWidget *) detail->otu_widget);
+
+	gtk_box_pack_start(GTK_BOX(detail->vbox), detail->otu_frame, FALSE, FALSE, 0);
+
+	gtk_container_add(GTK_CONTAINER(container), detail->vbox);
+}
+
+
+/* Load entered values back into dive structure. */
+static void save_dive_detail(struct dive *dive, struct detail_widget_t *detail)
+{
+	dive->when = utc_mktime(&detail->data.when);
+
+	switch (output_units.length) {
+	case METERS:
+		dive->maxdepth.mm = detail->data.maxdepth * 1000;
+		dive->meandepth.mm = detail->data.meandepth * 1000;
+		break;
+	case FEET:
+		dive->maxdepth.mm = feet_to_mm(detail->data.maxdepth);
+		dive->meandepth.mm = feet_to_mm(detail->data.meandepth);
+	}
+
+	switch (output_units.temperature) {
+	case CELSIUS:
+		dive->watertemp.mkelvin = C_to_mkelvin(detail->data.watertemp);
+		break;
+	case FAHRENHEIT:
+		dive->watertemp.mkelvin = F_to_mkelvin(detail->data.watertemp);
+		break;
+	case KELVIN:
+		dive->watertemp.mkelvin = detail->data.watertemp * 1000;
+	}
+
+	dive->surfacetime.seconds = detail->data.surfacetime;
+	dive->duration.seconds = detail->data.duration;
+	dive->otu = detail->data.otu;
+}
+
+
+/* Add a dive. */
+int add_dive_detail_dialog(GtkWidget *w, gpointer data) 
+{
+	GtkWidget *dialog, *hbox;
+	struct dive *dive;
+	struct detail_widget_t detail;
+
+	dive = alloc_dive();
+	set_default_dive_data(dive);
+
+	dialog = gtk_dialog_new_with_buttons("Add Dive",
+		GTK_WINDOW(main_window),
+		GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+		NULL);
+
+	hbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+	dive_detail_widget(hbox, &detail, dive);
+
+	gtk_widget_show_all(dialog);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+	{
+		pull_detail_data(&detail);
+		save_dive_detail(dive, &detail);
+		record_dive(dive);
+		mark_divelist_changed(TRUE);
+		/* Sort and redisplay */
+		report_dives(FALSE);
+	} else {
+		free(dive);
+	}
+
+	gtk_widget_destroy(dialog);
+	return 0;
+}
+
+
+/* Edit a dive. */
+int edit_dive_detail_dialog(GtkWidget *w, gpointer data)
+{
+
+	GtkWidget *dialog, *hbox;
+	struct dive *dive; 
+	struct detail_widget_t detail;
+	int old_selected_dive = selected_dive;
+
+	dive = current_dive;
+	remember_orig_values(&detail, dive);
+
+	dialog = gtk_dialog_new_with_buttons("Edit Dive",
+		GTK_WINDOW(main_window),
+		GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+		NULL);
+
+	hbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+	dive_detail_widget(hbox, &detail, dive);
+
+	gtk_widget_show_all(dialog);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+	{
+		pull_detail_data(&detail);
+		if (detail_changed(&detail))
+		{
+			save_dive_detail(dive, &detail);
+			mark_divelist_changed(TRUE);
+			/* Sort and redisplay */
+			report_dives(FALSE);
+
+			/* Set active row in divelist */
+			set_dive_list_cursor(old_selected_dive);
+		}
+	}
+
+	gtk_widget_destroy(dialog);
+	return 0;
+}
+
+
+/* Delete a dive */
+int delete_dive_detail_dialog(GtkWidget *w, gpointer data)
+{
+	GtkWidget *dialog;
+	int old_selected_dive = selected_dive;
+
+	dialog = gtk_dialog_new_with_buttons("Delete Dive?",
+		GTK_WINDOW(main_window),
+		GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_YES, GTK_RESPONSE_ACCEPT,
+		GTK_STOCK_NO, GTK_RESPONSE_REJECT,
+		NULL);
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+	{
+		delete_dive(selected_dive);
+		mark_divelist_changed(TRUE);
+		report_dives(FALSE);
+
+		/* Set active row in divelist */
+		set_dive_list_cursor(old_selected_dive);
+	}
+
+	gtk_widget_destroy(dialog);
+	return 0;
+}
+
diff --git a/detail.h b/detail.h
new file mode 100644
index 0000000..67f906d
--- /dev/null
+++ b/detail.h
@@ -0,0 +1,5 @@
+/* detail.h */
+
+extern int add_dive_detail_dialog(void);
+extern int edit_dive_detail_dialog(void);
+extern int delete_dive_detail_dialog(void);
-- 
1.7.3.4


More information about the subsurface mailing list