#include "TimerEditWindow.h" #include enum { TIMER_AM = 0, TIMER_PM = 1 }; enum { TIMER_LAST_START, TIMER_LAST_END }; struct _TimerEditWindow { GtkDialog parent; GtkWidget *deleteButton; GtkWidget *cancelButton; GtkWidget *saveButton; GtkWidget *nameBox; GtkWidget *startCalendar; GtkWidget *endCalendar; GtkWidget *endCalCheck; GtkWidget *startHour; GtkWidget *startMinute; GtkWidget *startSecond; GtkWidget *startLean; GtkWidget *endHour; GtkWidget *endMinute; GtkWidget *endSecond; GtkWidget *endLean; GtkWidget *lengthHour; GtkWidget *lengthMinute; GtkWidget *lengthSecond; GtkWidget *followBox; GtkWidget *followButton; GDateTime *lastTask; int lastEdit; gboolean invalidTime; gboolean enableAutoEdit; }; G_DEFINE_TYPE(TimerEditWindow, timer_edit_window, GTK_TYPE_DIALOG); #define TIMER_MAX_LENGTH ((3600 * 99) + (60 * 59) + 59) static gboolean compare_dates(GDateTime *dt1, GDateTime *dt2) { int y1, m1, d1, y2, m2, d2; g_date_time_get_ymd(dt1, &y1, &m1, &d1); g_date_time_get_ymd(dt2, &y2, &m2, &d2); return y1 == y2 && m1 == m2 && d1 == d2; } static void to_leaned_time(/* 0-23 */ int t, /* 1-12*/ int *h, int *l) { if (t == 0) { *h = 12; *l = TIMER_AM; } else if (t == 12) { *h = 12; *l = TIMER_PM; } else if (t > 12) { *h = t - 12; *l = TIMER_PM; } else { *h = t; *l = TIMER_AM; } } static void to_total_time(/* 1-12 */ int h, int l, /* 0-23 */ int *t) { if (h == 12 && l == TIMER_AM) { *t = 0; } else if (h == 12 && l == TIMER_PM) { *t = 12; } else if (l == TIMER_PM) { *t = h + 12; } else { *t = h; } } static void timer_edit_window_calculate_start(TimerEditWindow *self) { GDateTime *end = timer_edit_window_get_end(self); gint64 length = timer_edit_window_get_length(self); GDateTime *start = g_date_time_add_seconds(end, -length); if (!compare_dates(start, end)) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->endCalCheck), FALSE); } int y, month, d; g_date_time_get_ymd(start, &y, &month, &d); int s = g_date_time_get_second(start); int min = g_date_time_get_minute(start); int h, l; to_leaned_time(g_date_time_get_hour(start), &h, &l); gtk_calendar_select_day(GTK_CALENDAR(self->startCalendar), d); gtk_calendar_select_month(GTK_CALENDAR(self->startCalendar), month - 1, y); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->startSecond), s); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->startMinute), min); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->startHour), h); gtk_combo_box_set_active(GTK_COMBO_BOX(self->startLean), l); g_date_time_unref(end); g_date_time_unref(start); } static void timer_edit_window_calculate_end(TimerEditWindow *self) { GDateTime *start = timer_edit_window_get_start(self); gint64 length = timer_edit_window_get_length(self); GDateTime *end = g_date_time_add_seconds(start, length); if (!compare_dates(start, end)) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->endCalCheck), FALSE); } int y, month, d; g_date_time_get_ymd(end, &y, &month, &d); int s = g_date_time_get_second(end); int min = g_date_time_get_minute(end); int h, l; to_leaned_time(g_date_time_get_hour(end), &h, &l); gtk_calendar_select_day(GTK_CALENDAR(self->endCalendar), d); gtk_calendar_select_month(GTK_CALENDAR(self->endCalendar), month - 1, y); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->endSecond), s); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->endMinute), min); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->endHour), h); gtk_combo_box_set_active(GTK_COMBO_BOX(self->endLean), l); g_date_time_unref(start); g_date_time_unref(end); } static void timer_edit_window_calculate_length(TimerEditWindow *self) { GDateTime *start = timer_edit_window_get_start(self); GDateTime *end = timer_edit_window_get_end(self); GTimeSpan length = g_date_time_difference(end, start) / 1000000; if (length < 0 || length > TIMER_MAX_LENGTH) { self->invalidTime = TRUE; } else { self->invalidTime = FALSE; int h = floor(length / 3600.0f); int m = floor(length / 60.0f) - (h * 60); int s = length - (m * 60) - (h * 3600); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->lengthSecond), s); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->lengthMinute), m); gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->lengthHour), h); } g_date_time_unref(start); g_date_time_unref(end); } TimerEditWindow *timer_edit_window_new(const char *task, GDateTime *start, gint64 length, const char **taskOptions, gsize optionsLen, gboolean isNew, GDateTime *lastTask) { TimerEditWindow *win = g_object_new(TIMER_TYPE_EDIT_WINDOW, NULL); gtk_widget_set_visible(win->deleteButton, !isNew); gsize i; for (i = 0; i < optionsLen; ++i) { gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->nameBox), taskOptions[i]); } gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(win->nameBox))), task); int sy, smonth, sd; g_date_time_get_ymd(start, &sy, &smonth, &sd); int ss = g_date_time_get_second(start); int smin = g_date_time_get_minute(start); int sh, sl; to_leaned_time(g_date_time_get_hour(start), &sh, &sl); gtk_calendar_select_day(GTK_CALENDAR(win->startCalendar), sd); gtk_calendar_select_month(GTK_CALENDAR(win->startCalendar), smonth - 1, sy); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startSecond), ss); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startMinute), smin); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startHour), sh); gtk_combo_box_set_active(GTK_COMBO_BOX(win->startLean), sl); int lh = floor(length / 3600.0f); int lm = floor(length / 60.0f) - (lh * 60); int ls = length - (lm * 60) - (lh * 3600); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->lengthSecond), ls); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->lengthMinute), lm); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->lengthHour), lh); timer_edit_window_calculate_end(win); if (isNew && lastTask) { win->lastTask = lastTask; gtk_widget_show_all(win->followBox); } win->lastEdit = TIMER_LAST_END; win->enableAutoEdit = TRUE; return win; } GDateTime *timer_edit_window_get_start(TimerEditWindow *self) { unsigned int y, mn, d; gtk_calendar_get_date(GTK_CALENDAR(self->startCalendar), &y, &mn, &d); int s = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->startSecond)); int mi = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->startMinute)); int h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->startHour)); int l = gtk_combo_box_get_active(GTK_COMBO_BOX(self->startLean)); int t; to_total_time(h, l, &t); return g_date_time_new_local(y, mn + 1, d, t, mi, s); } GDateTime *timer_edit_window_get_end(TimerEditWindow *self) { unsigned int y, mn, d; if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->endCalCheck))) { gtk_calendar_get_date(GTK_CALENDAR(self->endCalendar), &y, &mn, &d); } else { gtk_calendar_get_date(GTK_CALENDAR(self->startCalendar), &y, &mn, &d); } int s = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->endSecond)); int mi = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->endMinute)); int h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->endHour)); int l = gtk_combo_box_get_active(GTK_COMBO_BOX(self->endLean)); int t; to_total_time(h, l, &t); return g_date_time_new_local(y, mn + 1, d, t, mi, s); } gint64 timer_edit_window_get_length(TimerEditWindow *self) { int h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->lengthHour)); int m = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->lengthMinute)); int s = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->lengthSecond)); return (h * 3600) + (m * 60) + s; } char *timer_edit_window_get_name(TimerEditWindow *self) { char *name; if (gtk_entry_get_text_length( GTK_ENTRY(gtk_bin_get_child(GTK_BIN(self->nameBox)))) == 0) { name = g_strdup("Untitled"); } else { name = gtk_combo_box_text_get_active_text( GTK_COMBO_BOX_TEXT(self->nameBox)); } return name; } static void save_button_callback(GtkButton *btn, TimerEditWindow *win) { if (win->invalidTime) { GtkWidget *diag = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Start time must be before end time and " "length must be under 100 hours!"); gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE); gtk_dialog_run(GTK_DIALOG(diag)); gtk_widget_destroy(diag); } else { gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_APPLY); gtk_window_close(GTK_WINDOW(win)); } } static void cancel_button_callback(GtkButton *btn, TimerEditWindow *win) { gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_CANCEL); gtk_window_close(GTK_WINDOW(win)); } static void delete_button_callback(GtkButton *btn, TimerEditWindow *win) { GtkWidget *diag = gtk_message_dialog_new( GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Are you sure you would like to delete this task?"); gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE); int resp = gtk_dialog_run(GTK_DIALOG(diag)); gtk_widget_destroy(GTK_WIDGET(diag)); if (resp == GTK_RESPONSE_YES) { gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_REJECT); gtk_window_close(GTK_WINDOW(win)); } } static void start_edit_callback(GtkWidget *obj, TimerEditWindow *win) { if (win->enableAutoEdit) { win->enableAutoEdit = FALSE; timer_edit_window_calculate_length(win); win->lastEdit = TIMER_LAST_START; win->enableAutoEdit = TRUE; } } static void end_edit_callback(GtkWidget *btn, TimerEditWindow *win) { if (win->enableAutoEdit) { win->enableAutoEdit = FALSE; timer_edit_window_calculate_length(win); win->lastEdit = TIMER_LAST_END; win->enableAutoEdit = TRUE; } } static void length_edit_callback(GtkSpinButton *btn, TimerEditWindow *win) { if (win->enableAutoEdit) { win->enableAutoEdit = FALSE; if (win->lastEdit == TIMER_LAST_START) { timer_edit_window_calculate_end(win); } else { timer_edit_window_calculate_start(win); } win->enableAutoEdit = TRUE; } } static void end_calendar_check_callback(GtkToggleButton *tgb, TimerEditWindow *win) { gtk_widget_set_visible(win->endCalendar, !gtk_toggle_button_get_active(tgb)); if (win->enableAutoEdit) { win->enableAutoEdit = FALSE; timer_edit_window_calculate_length(win); win->lastEdit = TIMER_LAST_END; win->enableAutoEdit = TRUE; } } static void follow_button_callback(GtkButton *btn, TimerEditWindow *win) { GDateTime *end = timer_edit_window_get_end(win); win->enableAutoEdit = FALSE; int sy, smonth, sd; g_date_time_get_ymd(win->lastTask, &sy, &smonth, &sd); int ss = g_date_time_get_second(win->lastTask); int smin = g_date_time_get_minute(win->lastTask); int sh, sl; to_leaned_time(g_date_time_get_hour(win->lastTask), &sh, &sl); gtk_calendar_select_day(GTK_CALENDAR(win->startCalendar), sd); gtk_calendar_select_month(GTK_CALENDAR(win->startCalendar), smonth - 1, sy); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startSecond), ss); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startMinute), smin); gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startHour), sh); gtk_combo_box_set_active(GTK_COMBO_BOX(win->startLean), sl); GDateTime *start = timer_edit_window_get_start(win); if (!compare_dates(start, end)) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->endCalCheck), FALSE); } timer_edit_window_calculate_length(win); win->lastEdit = TIMER_LAST_START; win->enableAutoEdit = TRUE; } static void timer_edit_window_finalize(GObject *obj) { if (TIMER_EDIT_WINDOW(obj)->lastTask) { g_date_time_unref(TIMER_EDIT_WINDOW(obj)->lastTask); } G_OBJECT_CLASS(timer_edit_window_parent_class)->finalize(obj); } static void timer_edit_window_class_init(TimerEditWindowClass *class) { G_OBJECT_CLASS(class)->finalize = timer_edit_window_finalize; gtk_widget_class_set_template_from_resource( GTK_WIDGET_CLASS(class), "/zander/practicetimer/ui/edit-window.glade"); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerEditWindow, cancelButton); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerEditWindow, deleteButton); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, saveButton); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, nameBox); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerEditWindow, startCalendar); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, endCalendar); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, endCalCheck); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, startHour); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, startMinute); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, startSecond); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, startLean); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, endHour); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, endMinute); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, endSecond); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, endLean); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, lengthHour); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerEditWindow, lengthMinute); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerEditWindow, lengthSecond); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, followButton); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, followBox); } static void timer_edit_window_init(TimerEditWindow *self) { self->enableAutoEdit = FALSE; gtk_widget_init_template(GTK_WIDGET(self)); gtk_window_set_keep_above(GTK_WINDOW(self), TRUE); g_signal_connect(self->deleteButton, "clicked", G_CALLBACK(delete_button_callback), self); g_signal_connect(self->saveButton, "clicked", G_CALLBACK(save_button_callback), self); g_signal_connect(self->cancelButton, "clicked", G_CALLBACK(cancel_button_callback), self); g_signal_connect(self->startHour, "value-changed", G_CALLBACK(start_edit_callback), self); g_signal_connect(self->startMinute, "value-changed", G_CALLBACK(start_edit_callback), self); g_signal_connect(self->startSecond, "value-changed", G_CALLBACK(start_edit_callback), self); g_signal_connect(self->startLean, "changed", G_CALLBACK(start_edit_callback), self); g_signal_connect(self->startCalendar, "day-selected", G_CALLBACK(start_edit_callback), self); g_signal_connect(self->startCalendar, "month-changed", G_CALLBACK(start_edit_callback), self); g_signal_connect(self->startCalendar, "next-year", G_CALLBACK(start_edit_callback), self); g_signal_connect(self->startCalendar, "prev-year", G_CALLBACK(start_edit_callback), self); g_signal_connect(self->endHour, "value-changed", G_CALLBACK(end_edit_callback), self); g_signal_connect(self->endMinute, "value-changed", G_CALLBACK(end_edit_callback), self); g_signal_connect(self->endSecond, "value-changed", G_CALLBACK(end_edit_callback), self); g_signal_connect(self->endLean, "changed", G_CALLBACK(end_edit_callback), self); g_signal_connect(self->endCalendar, "day-selected", G_CALLBACK(end_edit_callback), self); g_signal_connect(self->endCalendar, "month-changed", G_CALLBACK(end_edit_callback), self); g_signal_connect(self->endCalendar, "next-year", G_CALLBACK(end_edit_callback), self); g_signal_connect(self->endCalendar, "prev-year", G_CALLBACK(end_edit_callback), self); g_signal_connect(self->lengthHour, "value-changed", G_CALLBACK(length_edit_callback), self); g_signal_connect(self->lengthMinute, "value-changed", G_CALLBACK(length_edit_callback), self); g_signal_connect(self->lengthSecond, "value-changed", G_CALLBACK(length_edit_callback), self); g_signal_connect(self->endCalCheck, "toggled", G_CALLBACK(end_calendar_check_callback), self); g_signal_connect(self->followButton, "clicked", G_CALLBACK(follow_button_callback), self); }