practice-timer/TimerEditWindow.c

461 lines
19 KiB
C
Raw Permalink Normal View History

2022-08-28 14:20:12 -07:00
#include "TimerEditWindow.h"
#include <math.h>
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);
}