[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