TimerEditWindow.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. #include "TimerEditWindow.h"
  2. #include <math.h>
  3. enum { TIMER_AM = 0, TIMER_PM = 1 };
  4. enum { TIMER_LAST_START, TIMER_LAST_END };
  5. struct _TimerEditWindow {
  6. GtkDialog parent;
  7. GtkWidget *deleteButton;
  8. GtkWidget *cancelButton;
  9. GtkWidget *saveButton;
  10. GtkWidget *nameBox;
  11. GtkWidget *startCalendar;
  12. GtkWidget *endCalendar;
  13. GtkWidget *endCalCheck;
  14. GtkWidget *startHour;
  15. GtkWidget *startMinute;
  16. GtkWidget *startSecond;
  17. GtkWidget *startLean;
  18. GtkWidget *endHour;
  19. GtkWidget *endMinute;
  20. GtkWidget *endSecond;
  21. GtkWidget *endLean;
  22. GtkWidget *lengthHour;
  23. GtkWidget *lengthMinute;
  24. GtkWidget *lengthSecond;
  25. GtkWidget *followBox;
  26. GtkWidget *followButton;
  27. GDateTime *lastTask;
  28. int lastEdit;
  29. gboolean invalidTime;
  30. gboolean enableAutoEdit;
  31. };
  32. G_DEFINE_TYPE(TimerEditWindow, timer_edit_window, GTK_TYPE_DIALOG);
  33. #define TIMER_MAX_LENGTH ((3600 * 99) + (60 * 59) + 59)
  34. static gboolean compare_dates(GDateTime *dt1, GDateTime *dt2) {
  35. int y1, m1, d1, y2, m2, d2;
  36. g_date_time_get_ymd(dt1, &y1, &m1, &d1);
  37. g_date_time_get_ymd(dt2, &y2, &m2, &d2);
  38. return y1 == y2 && m1 == m2 && d1 == d2;
  39. }
  40. static void to_leaned_time(/* 0-23 */ int t, /* 1-12*/ int *h, int *l) {
  41. if (t == 0) {
  42. *h = 12;
  43. *l = TIMER_AM;
  44. } else if (t == 12) {
  45. *h = 12;
  46. *l = TIMER_PM;
  47. } else if (t > 12) {
  48. *h = t - 12;
  49. *l = TIMER_PM;
  50. } else {
  51. *h = t;
  52. *l = TIMER_AM;
  53. }
  54. }
  55. static void to_total_time(/* 1-12 */ int h, int l, /* 0-23 */ int *t) {
  56. if (h == 12 && l == TIMER_AM) {
  57. *t = 0;
  58. } else if (h == 12 && l == TIMER_PM) {
  59. *t = 12;
  60. } else if (l == TIMER_PM) {
  61. *t = h + 12;
  62. } else {
  63. *t = h;
  64. }
  65. }
  66. static void timer_edit_window_calculate_start(TimerEditWindow *self) {
  67. GDateTime *end = timer_edit_window_get_end(self);
  68. gint64 length = timer_edit_window_get_length(self);
  69. GDateTime *start = g_date_time_add_seconds(end, -length);
  70. if (!compare_dates(start, end)) {
  71. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->endCalCheck),
  72. FALSE);
  73. }
  74. int y, month, d;
  75. g_date_time_get_ymd(start, &y, &month, &d);
  76. int s = g_date_time_get_second(start);
  77. int min = g_date_time_get_minute(start);
  78. int h, l;
  79. to_leaned_time(g_date_time_get_hour(start), &h, &l);
  80. gtk_calendar_select_day(GTK_CALENDAR(self->startCalendar), d);
  81. gtk_calendar_select_month(GTK_CALENDAR(self->startCalendar), month - 1, y);
  82. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->startSecond), s);
  83. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->startMinute), min);
  84. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->startHour), h);
  85. gtk_combo_box_set_active(GTK_COMBO_BOX(self->startLean), l);
  86. g_date_time_unref(end);
  87. g_date_time_unref(start);
  88. }
  89. static void timer_edit_window_calculate_end(TimerEditWindow *self) {
  90. GDateTime *start = timer_edit_window_get_start(self);
  91. gint64 length = timer_edit_window_get_length(self);
  92. GDateTime *end = g_date_time_add_seconds(start, length);
  93. if (!compare_dates(start, end)) {
  94. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->endCalCheck),
  95. FALSE);
  96. }
  97. int y, month, d;
  98. g_date_time_get_ymd(end, &y, &month, &d);
  99. int s = g_date_time_get_second(end);
  100. int min = g_date_time_get_minute(end);
  101. int h, l;
  102. to_leaned_time(g_date_time_get_hour(end), &h, &l);
  103. gtk_calendar_select_day(GTK_CALENDAR(self->endCalendar), d);
  104. gtk_calendar_select_month(GTK_CALENDAR(self->endCalendar), month - 1, y);
  105. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->endSecond), s);
  106. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->endMinute), min);
  107. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->endHour), h);
  108. gtk_combo_box_set_active(GTK_COMBO_BOX(self->endLean), l);
  109. g_date_time_unref(start);
  110. g_date_time_unref(end);
  111. }
  112. static void timer_edit_window_calculate_length(TimerEditWindow *self) {
  113. GDateTime *start = timer_edit_window_get_start(self);
  114. GDateTime *end = timer_edit_window_get_end(self);
  115. GTimeSpan length = g_date_time_difference(end, start) / 1000000;
  116. if (length < 0 || length > TIMER_MAX_LENGTH) {
  117. self->invalidTime = TRUE;
  118. } else {
  119. self->invalidTime = FALSE;
  120. int h = floor(length / 3600.0f);
  121. int m = floor(length / 60.0f) - (h * 60);
  122. int s = length - (m * 60) - (h * 3600);
  123. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->lengthSecond), s);
  124. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->lengthMinute), m);
  125. gtk_spin_button_set_value(GTK_SPIN_BUTTON(self->lengthHour), h);
  126. }
  127. g_date_time_unref(start);
  128. g_date_time_unref(end);
  129. }
  130. TimerEditWindow *timer_edit_window_new(const char *task, GDateTime *start,
  131. gint64 length, const char **taskOptions,
  132. gsize optionsLen, gboolean isNew, GDateTime *lastTask) {
  133. TimerEditWindow *win = g_object_new(TIMER_TYPE_EDIT_WINDOW, NULL);
  134. gtk_widget_set_visible(win->deleteButton, !isNew);
  135. gsize i;
  136. for (i = 0; i < optionsLen; ++i) {
  137. gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->nameBox),
  138. taskOptions[i]);
  139. }
  140. gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(win->nameBox))),
  141. task);
  142. int sy, smonth, sd;
  143. g_date_time_get_ymd(start, &sy, &smonth, &sd);
  144. int ss = g_date_time_get_second(start);
  145. int smin = g_date_time_get_minute(start);
  146. int sh, sl;
  147. to_leaned_time(g_date_time_get_hour(start), &sh, &sl);
  148. gtk_calendar_select_day(GTK_CALENDAR(win->startCalendar), sd);
  149. gtk_calendar_select_month(GTK_CALENDAR(win->startCalendar), smonth - 1, sy);
  150. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startSecond), ss);
  151. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startMinute), smin);
  152. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startHour), sh);
  153. gtk_combo_box_set_active(GTK_COMBO_BOX(win->startLean), sl);
  154. int lh = floor(length / 3600.0f);
  155. int lm = floor(length / 60.0f) - (lh * 60);
  156. int ls = length - (lm * 60) - (lh * 3600);
  157. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->lengthSecond), ls);
  158. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->lengthMinute), lm);
  159. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->lengthHour), lh);
  160. timer_edit_window_calculate_end(win);
  161. if (isNew && lastTask) {
  162. win->lastTask = lastTask;
  163. gtk_widget_show_all(win->followBox);
  164. }
  165. win->lastEdit = TIMER_LAST_END;
  166. win->enableAutoEdit = TRUE;
  167. return win;
  168. }
  169. GDateTime *timer_edit_window_get_start(TimerEditWindow *self) {
  170. unsigned int y, mn, d;
  171. gtk_calendar_get_date(GTK_CALENDAR(self->startCalendar), &y, &mn, &d);
  172. int s =
  173. gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->startSecond));
  174. int mi =
  175. gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->startMinute));
  176. int h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->startHour));
  177. int l = gtk_combo_box_get_active(GTK_COMBO_BOX(self->startLean));
  178. int t;
  179. to_total_time(h, l, &t);
  180. return g_date_time_new_local(y, mn + 1, d, t, mi, s);
  181. }
  182. GDateTime *timer_edit_window_get_end(TimerEditWindow *self) {
  183. unsigned int y, mn, d;
  184. if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->endCalCheck))) {
  185. gtk_calendar_get_date(GTK_CALENDAR(self->endCalendar), &y, &mn, &d);
  186. } else {
  187. gtk_calendar_get_date(GTK_CALENDAR(self->startCalendar), &y, &mn, &d);
  188. }
  189. int s = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->endSecond));
  190. int mi = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->endMinute));
  191. int h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->endHour));
  192. int l = gtk_combo_box_get_active(GTK_COMBO_BOX(self->endLean));
  193. int t;
  194. to_total_time(h, l, &t);
  195. return g_date_time_new_local(y, mn + 1, d, t, mi, s);
  196. }
  197. gint64 timer_edit_window_get_length(TimerEditWindow *self) {
  198. int h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->lengthHour));
  199. int m =
  200. gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->lengthMinute));
  201. int s =
  202. gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->lengthSecond));
  203. return (h * 3600) + (m * 60) + s;
  204. }
  205. char *timer_edit_window_get_name(TimerEditWindow *self) {
  206. char *name;
  207. if (gtk_entry_get_text_length(
  208. GTK_ENTRY(gtk_bin_get_child(GTK_BIN(self->nameBox)))) == 0) {
  209. name = g_strdup("Untitled");
  210. } else {
  211. name = gtk_combo_box_text_get_active_text(
  212. GTK_COMBO_BOX_TEXT(self->nameBox));
  213. }
  214. return name;
  215. }
  216. static void save_button_callback(GtkButton *btn, TimerEditWindow *win) {
  217. if (win->invalidTime) {
  218. GtkWidget *diag =
  219. gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL,
  220. GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
  221. "Start time must be before end time and "
  222. "length must be under 100 hours!");
  223. gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
  224. gtk_dialog_run(GTK_DIALOG(diag));
  225. gtk_widget_destroy(diag);
  226. } else {
  227. gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_APPLY);
  228. gtk_window_close(GTK_WINDOW(win));
  229. }
  230. }
  231. static void cancel_button_callback(GtkButton *btn, TimerEditWindow *win) {
  232. gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_CANCEL);
  233. gtk_window_close(GTK_WINDOW(win));
  234. }
  235. static void delete_button_callback(GtkButton *btn, TimerEditWindow *win) {
  236. GtkWidget *diag = gtk_message_dialog_new(
  237. GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
  238. GTK_BUTTONS_YES_NO, "Are you sure you would like to delete this task?");
  239. gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
  240. int resp = gtk_dialog_run(GTK_DIALOG(diag));
  241. gtk_widget_destroy(GTK_WIDGET(diag));
  242. if (resp == GTK_RESPONSE_YES) {
  243. gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_REJECT);
  244. gtk_window_close(GTK_WINDOW(win));
  245. }
  246. }
  247. static void start_edit_callback(GtkWidget *obj, TimerEditWindow *win) {
  248. if (win->enableAutoEdit) {
  249. win->enableAutoEdit = FALSE;
  250. timer_edit_window_calculate_length(win);
  251. win->lastEdit = TIMER_LAST_START;
  252. win->enableAutoEdit = TRUE;
  253. }
  254. }
  255. static void end_edit_callback(GtkWidget *btn, TimerEditWindow *win) {
  256. if (win->enableAutoEdit) {
  257. win->enableAutoEdit = FALSE;
  258. timer_edit_window_calculate_length(win);
  259. win->lastEdit = TIMER_LAST_END;
  260. win->enableAutoEdit = TRUE;
  261. }
  262. }
  263. static void length_edit_callback(GtkSpinButton *btn, TimerEditWindow *win) {
  264. if (win->enableAutoEdit) {
  265. win->enableAutoEdit = FALSE;
  266. if (win->lastEdit == TIMER_LAST_START) {
  267. timer_edit_window_calculate_end(win);
  268. } else {
  269. timer_edit_window_calculate_start(win);
  270. }
  271. win->enableAutoEdit = TRUE;
  272. }
  273. }
  274. static void end_calendar_check_callback(GtkToggleButton *tgb,
  275. TimerEditWindow *win) {
  276. gtk_widget_set_visible(win->endCalendar,
  277. !gtk_toggle_button_get_active(tgb));
  278. if (win->enableAutoEdit) {
  279. win->enableAutoEdit = FALSE;
  280. timer_edit_window_calculate_length(win);
  281. win->lastEdit = TIMER_LAST_END;
  282. win->enableAutoEdit = TRUE;
  283. }
  284. }
  285. static void follow_button_callback(GtkButton *btn, TimerEditWindow *win) {
  286. GDateTime *end = timer_edit_window_get_end(win);
  287. win->enableAutoEdit = FALSE;
  288. int sy, smonth, sd;
  289. g_date_time_get_ymd(win->lastTask, &sy, &smonth, &sd);
  290. int ss = g_date_time_get_second(win->lastTask);
  291. int smin = g_date_time_get_minute(win->lastTask);
  292. int sh, sl;
  293. to_leaned_time(g_date_time_get_hour(win->lastTask), &sh, &sl);
  294. gtk_calendar_select_day(GTK_CALENDAR(win->startCalendar), sd);
  295. gtk_calendar_select_month(GTK_CALENDAR(win->startCalendar), smonth - 1, sy);
  296. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startSecond), ss);
  297. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startMinute), smin);
  298. gtk_spin_button_set_value(GTK_SPIN_BUTTON(win->startHour), sh);
  299. gtk_combo_box_set_active(GTK_COMBO_BOX(win->startLean), sl);
  300. GDateTime *start = timer_edit_window_get_start(win);
  301. if (!compare_dates(start, end)) {
  302. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->endCalCheck), FALSE);
  303. }
  304. timer_edit_window_calculate_length(win);
  305. win->lastEdit = TIMER_LAST_START;
  306. win->enableAutoEdit = TRUE;
  307. }
  308. static void timer_edit_window_finalize(GObject *obj) {
  309. if (TIMER_EDIT_WINDOW(obj)->lastTask) {
  310. g_date_time_unref(TIMER_EDIT_WINDOW(obj)->lastTask);
  311. }
  312. G_OBJECT_CLASS(timer_edit_window_parent_class)->finalize(obj);
  313. }
  314. static void timer_edit_window_class_init(TimerEditWindowClass *class) {
  315. G_OBJECT_CLASS(class)->finalize = timer_edit_window_finalize;
  316. gtk_widget_class_set_template_from_resource(
  317. GTK_WIDGET_CLASS(class), "/zander/practicetimer/ui/edit-window.glade");
  318. gtk_widget_class_bind_template_child_internal(
  319. GTK_WIDGET_CLASS(class), TimerEditWindow, cancelButton);
  320. gtk_widget_class_bind_template_child_internal(
  321. GTK_WIDGET_CLASS(class), TimerEditWindow, deleteButton);
  322. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  323. TimerEditWindow, saveButton);
  324. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  325. TimerEditWindow, nameBox);
  326. gtk_widget_class_bind_template_child_internal(
  327. GTK_WIDGET_CLASS(class), TimerEditWindow, startCalendar);
  328. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  329. TimerEditWindow, endCalendar);
  330. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  331. TimerEditWindow, endCalCheck);
  332. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  333. TimerEditWindow, startHour);
  334. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  335. TimerEditWindow, startMinute);
  336. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  337. TimerEditWindow, startSecond);
  338. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  339. TimerEditWindow, startLean);
  340. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  341. TimerEditWindow, endHour);
  342. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  343. TimerEditWindow, endMinute);
  344. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  345. TimerEditWindow, endSecond);
  346. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  347. TimerEditWindow, endLean);
  348. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  349. TimerEditWindow, lengthHour);
  350. gtk_widget_class_bind_template_child_internal(
  351. GTK_WIDGET_CLASS(class), TimerEditWindow, lengthMinute);
  352. gtk_widget_class_bind_template_child_internal(
  353. GTK_WIDGET_CLASS(class), TimerEditWindow, lengthSecond);
  354. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, followButton);
  355. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerEditWindow, followBox);
  356. }
  357. static void timer_edit_window_init(TimerEditWindow *self) {
  358. self->enableAutoEdit = FALSE;
  359. gtk_widget_init_template(GTK_WIDGET(self));
  360. gtk_window_set_keep_above(GTK_WINDOW(self), TRUE);
  361. g_signal_connect(self->deleteButton, "clicked",
  362. G_CALLBACK(delete_button_callback), self);
  363. g_signal_connect(self->saveButton, "clicked",
  364. G_CALLBACK(save_button_callback), self);
  365. g_signal_connect(self->cancelButton, "clicked",
  366. G_CALLBACK(cancel_button_callback), self);
  367. g_signal_connect(self->startHour, "value-changed",
  368. G_CALLBACK(start_edit_callback), self);
  369. g_signal_connect(self->startMinute, "value-changed",
  370. G_CALLBACK(start_edit_callback), self);
  371. g_signal_connect(self->startSecond, "value-changed",
  372. G_CALLBACK(start_edit_callback), self);
  373. g_signal_connect(self->startLean, "changed",
  374. G_CALLBACK(start_edit_callback), self);
  375. g_signal_connect(self->startCalendar, "day-selected",
  376. G_CALLBACK(start_edit_callback), self);
  377. g_signal_connect(self->startCalendar, "month-changed",
  378. G_CALLBACK(start_edit_callback), self);
  379. g_signal_connect(self->startCalendar, "next-year",
  380. G_CALLBACK(start_edit_callback), self);
  381. g_signal_connect(self->startCalendar, "prev-year",
  382. G_CALLBACK(start_edit_callback), self);
  383. g_signal_connect(self->endHour, "value-changed",
  384. G_CALLBACK(end_edit_callback), self);
  385. g_signal_connect(self->endMinute, "value-changed",
  386. G_CALLBACK(end_edit_callback), self);
  387. g_signal_connect(self->endSecond, "value-changed",
  388. G_CALLBACK(end_edit_callback), self);
  389. g_signal_connect(self->endLean, "changed", G_CALLBACK(end_edit_callback),
  390. self);
  391. g_signal_connect(self->endCalendar, "day-selected",
  392. G_CALLBACK(end_edit_callback), self);
  393. g_signal_connect(self->endCalendar, "month-changed",
  394. G_CALLBACK(end_edit_callback), self);
  395. g_signal_connect(self->endCalendar, "next-year",
  396. G_CALLBACK(end_edit_callback), self);
  397. g_signal_connect(self->endCalendar, "prev-year",
  398. G_CALLBACK(end_edit_callback), self);
  399. g_signal_connect(self->lengthHour, "value-changed",
  400. G_CALLBACK(length_edit_callback), self);
  401. g_signal_connect(self->lengthMinute, "value-changed",
  402. G_CALLBACK(length_edit_callback), self);
  403. g_signal_connect(self->lengthSecond, "value-changed",
  404. G_CALLBACK(length_edit_callback), self);
  405. g_signal_connect(self->endCalCheck, "toggled",
  406. G_CALLBACK(end_calendar_check_callback), self);
  407. g_signal_connect(self->followButton, "clicked", G_CALLBACK(follow_button_callback), self);
  408. }