TimerSettingsWindow.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #include "TimerSettingsWindow.h"
  2. #include "TimerGraphWindow.h"
  3. #include "TimerMainWindow.h"
  4. #define DEFAULT_DATA_FILE_NAME "practicetimer/data.json"
  5. struct _TimerSettingsWindow {
  6. GtkDialog parent;
  7. GtkWidget *cancelButton;
  8. GtkWidget *saveButton;
  9. GtkWidget *dataPathButton;
  10. GtkWidget *taskBox;
  11. GtkWidget *removeTaskButton;
  12. GtkWidget *taskNameEntry;
  13. GtkWidget *addTaskButton;
  14. GtkWidget *graphButton;
  15. GtkWidget *exportButton;
  16. GtkWidget *alwaysOnTopCheck;
  17. GtkWidget *versionLabel;
  18. GKeyFile *keyFile;
  19. TimerMainWindow *parentWindow;
  20. };
  21. G_DEFINE_TYPE(TimerSettingsWindow, timer_settings_window, GTK_TYPE_DIALOG)
  22. static void timer_settings_window_copy_key_file(TimerSettingsWindow *self,
  23. GKeyFile *keyFile) {
  24. self->keyFile = g_key_file_new();
  25. gsize len;
  26. char *data = g_key_file_to_data(keyFile, &len, NULL);
  27. g_key_file_load_from_data(self->keyFile, data, len, G_KEY_FILE_NONE, NULL);
  28. g_free(data);
  29. }
  30. static gboolean
  31. timer_settings_window_get_boolean_with_default(TimerSettingsWindow *self,
  32. const char *group,
  33. const char *key, gboolean def) {
  34. GError *err = NULL;
  35. gboolean b = g_key_file_get_boolean(self->keyFile, group, key, &err);
  36. if (err != NULL) {
  37. g_error_free(err);
  38. return def;
  39. }
  40. return b;
  41. }
  42. static char *timer_settings_window_get_string_with_default(
  43. TimerSettingsWindow *self, const char *group, const char *key,
  44. const char *def) {
  45. GError *err = NULL;
  46. char *s = g_key_file_get_string(self->keyFile, group, key, &err);
  47. if (err != NULL) {
  48. g_error_free(err);
  49. return g_strdup(def);
  50. }
  51. return s;
  52. }
  53. static void
  54. timer_settings_window_interpret_settings(TimerSettingsWindow *self) {
  55. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->alwaysOnTopCheck),
  56. timer_settings_window_get_boolean_with_default(
  57. self, "Settings", "Always on Top", FALSE));
  58. /* Data path stuff */
  59. char *defaultDataPath = g_strdup_printf("%s/%s", g_get_user_config_dir(),
  60. DEFAULT_DATA_FILE_NAME);
  61. char *dataPath = timer_settings_window_get_string_with_default(
  62. self, "Settings", "Data Path", defaultDataPath);
  63. gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(self->dataPathButton),
  64. dataPath);
  65. g_free(defaultDataPath);
  66. g_free(dataPath);
  67. /* Task list stuff */
  68. GError *err = NULL;
  69. gsize len;
  70. char **tasks = g_key_file_get_string_list(self->keyFile, "Settings",
  71. "Tasks", &len, &err);
  72. if (err == NULL) {
  73. gsize i;
  74. for (i = 0; i < len; ++i) {
  75. gtk_list_box_insert(GTK_LIST_BOX(self->taskBox),
  76. gtk_label_new(tasks[i]), -1);
  77. g_free(tasks[i]);
  78. }
  79. g_free(tasks);
  80. } else {
  81. g_error_free(err);
  82. }
  83. gtk_widget_show_all(self->taskBox);
  84. }
  85. TimerSettingsWindow *timer_settings_window_new(TimerApplication *app,
  86. GKeyFile *keyFile,
  87. GtkWindow *parentWindow) {
  88. TimerSettingsWindow *win = TIMER_SETTINGS_WINDOW(
  89. g_object_new(TIMER_TYPE_SETTINGS_WINDOW, "application", app, NULL));
  90. timer_settings_window_copy_key_file(win, keyFile);
  91. timer_settings_window_interpret_settings(win);
  92. if (TIMER_IS_MAIN_WINDOW(parentWindow)) {
  93. win->parentWindow = TIMER_MAIN_WINDOW(parentWindow);
  94. }
  95. return win;
  96. }
  97. GKeyFile *timer_settings_window_get_key_file(TimerSettingsWindow *self) {
  98. return self->keyFile;
  99. }
  100. static gboolean timer_settings_window_has_task(TimerSettingsWindow *self,
  101. const char *task) {
  102. GList *tasks = gtk_container_get_children(GTK_CONTAINER(self->taskBox));
  103. GList *i;
  104. for (i = tasks; i != NULL; i = i->next) {
  105. if (strcmp(task, gtk_label_get_text(GTK_LABEL(
  106. gtk_bin_get_child(GTK_BIN(i->data))))) == 0) {
  107. return TRUE;
  108. }
  109. }
  110. g_list_free(tasks);
  111. return FALSE;
  112. }
  113. static void add_button_callback(GtkButton *btn, TimerSettingsWindow *win) {
  114. const char *task = gtk_entry_get_text(GTK_ENTRY(win->taskNameEntry));
  115. if (strlen(task) != 0) {
  116. if (!timer_settings_window_has_task(win, task)) {
  117. gtk_list_box_insert(GTK_LIST_BOX(win->taskBox), gtk_label_new(task),
  118. -1);
  119. gtk_widget_show_all(win->taskBox);
  120. }
  121. gtk_entry_set_text(GTK_ENTRY(win->taskNameEntry), "");
  122. }
  123. }
  124. static void remove_button_callback(GtkButton *btn, TimerSettingsWindow *win) {
  125. GList *rows = gtk_list_box_get_selected_rows(GTK_LIST_BOX(win->taskBox));
  126. GList *i;
  127. for (i = rows; i != NULL; i = i->next) {
  128. gtk_container_remove(GTK_CONTAINER(win->taskBox), GTK_WIDGET(i->data));
  129. }
  130. g_list_free(rows);
  131. }
  132. static void cancel_button_callback(GtkButton *btn, TimerSettingsWindow *win) {
  133. gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_CANCEL);
  134. gtk_window_close(GTK_WINDOW(win));
  135. }
  136. static const char *const *
  137. timer_settings_window_get_task_list_string(TimerSettingsWindow *self,
  138. gsize *len) {
  139. GList *tasks = gtk_container_get_children(GTK_CONTAINER(self->taskBox));
  140. gsize taskCount = g_list_length(tasks);
  141. char **taskStrings = g_malloc(sizeof(char *) * taskCount);
  142. GList *it;
  143. int in;
  144. for (in = 0, it = tasks; it != NULL; ++in, it = it->next) {
  145. taskStrings[in] = (char *)gtk_label_get_text(
  146. GTK_LABEL(gtk_bin_get_child(GTK_BIN(it->data))));
  147. }
  148. g_list_free(tasks);
  149. if (len != NULL) {
  150. *len = taskCount;
  151. }
  152. return (const char *const *)taskStrings;
  153. }
  154. static void save_button_callback(GtkButton *btn, TimerSettingsWindow *win) {
  155. g_key_file_set_boolean(
  156. win->keyFile, "Settings", "Always on Top",
  157. gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->alwaysOnTopCheck)));
  158. g_key_file_set_string(
  159. win->keyFile, "Settings", "Data Path",
  160. gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(win->dataPathButton)));
  161. gsize taskCount;
  162. const char *const *taskList =
  163. timer_settings_window_get_task_list_string(win, &taskCount);
  164. g_key_file_set_string_list(win->keyFile, "Settings", "Tasks", taskList,
  165. taskCount);
  166. g_free((gpointer)taskList);
  167. gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_APPLY);
  168. gtk_window_close(GTK_WINDOW(win));
  169. }
  170. static void export_button_callback(GtkButton *btn, TimerSettingsWindow *win) {
  171. GtkWidget *diag = gtk_file_chooser_dialog_new(
  172. "Export Data", GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel",
  173. GTK_RESPONSE_CANCEL, "_Export", GTK_RESPONSE_ACCEPT, NULL);
  174. int resp = gtk_dialog_run(GTK_DIALOG(diag));
  175. if (resp == GTK_RESPONSE_ACCEPT) {
  176. char *path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(diag));
  177. char *csvData = timer_main_window_get_task_csv(win->parentWindow);
  178. GFile *file = g_file_new_for_path(path);
  179. GError *err = NULL;
  180. GFileOutputStream *out =
  181. g_file_create(file, G_FILE_CREATE_NONE, NULL, &err);
  182. if (err != NULL) {
  183. GtkWidget *msgDiag = gtk_message_dialog_new(
  184. GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
  185. GTK_BUTTONS_OK, "Could not export data: %s", err->message);
  186. gtk_window_set_position(GTK_WINDOW(msgDiag), GTK_WIN_POS_MOUSE);
  187. gtk_dialog_run(GTK_DIALOG(msgDiag));
  188. gtk_widget_destroy(msgDiag);
  189. g_clear_error(&err);
  190. }
  191. g_output_stream_write(G_OUTPUT_STREAM(out), csvData, strlen(csvData),
  192. NULL, &err);
  193. if (err != NULL) {
  194. GtkWidget *msgDiag = gtk_message_dialog_new(
  195. GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
  196. GTK_BUTTONS_OK, "Could not export data: %s", err->message);
  197. gtk_window_set_position(GTK_WINDOW(msgDiag), GTK_WIN_POS_MOUSE);
  198. gtk_dialog_run(GTK_DIALOG(msgDiag));
  199. gtk_widget_destroy(msgDiag);
  200. g_error_free(err);
  201. }
  202. g_object_unref(out);
  203. g_object_unref(file);
  204. g_free(csvData);
  205. g_free(path);
  206. }
  207. gtk_widget_destroy(diag);
  208. }
  209. static void graph_button_callback(GtkButton *btn, TimerSettingsWindow *win) {
  210. gsize taskLen;
  211. TimerDataPoint *taskData = timer_main_window_get_task_data(win->parentWindow, &taskLen);
  212. gsize dayLen;
  213. TimerDataPoint *dayData = timer_main_window_get_day_data(win->parentWindow, &dayLen);
  214. TimerGraphWindow *diag = timer_graph_window_new(TIMER_APPLICATION(gtk_window_get_application(GTK_WINDOW(win))), dayData, dayLen, taskData, taskLen);
  215. gtk_dialog_run(GTK_DIALOG(diag));
  216. gtk_widget_destroy(GTK_WIDGET(diag));
  217. }
  218. static void timer_settings_window_finalize(GObject *self) {
  219. g_key_file_free(TIMER_SETTINGS_WINDOW(self)->keyFile);
  220. G_OBJECT_CLASS(timer_settings_window_parent_class)->finalize(self);
  221. }
  222. static void timer_settings_window_class_init(TimerSettingsWindowClass *class) {
  223. G_OBJECT_CLASS(class)->finalize = timer_settings_window_finalize;
  224. gtk_widget_class_set_template_from_resource(
  225. GTK_WIDGET_CLASS(class),
  226. "/zander/practicetimer/ui/settings-window.glade");
  227. gtk_widget_class_bind_template_child_internal(
  228. GTK_WIDGET_CLASS(class), TimerSettingsWindow, cancelButton);
  229. gtk_widget_class_bind_template_child_internal(
  230. GTK_WIDGET_CLASS(class), TimerSettingsWindow, saveButton);
  231. gtk_widget_class_bind_template_child_internal(
  232. GTK_WIDGET_CLASS(class), TimerSettingsWindow, dataPathButton);
  233. gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class),
  234. TimerSettingsWindow, taskBox);
  235. gtk_widget_class_bind_template_child_internal(
  236. GTK_WIDGET_CLASS(class), TimerSettingsWindow, removeTaskButton);
  237. gtk_widget_class_bind_template_child_internal(
  238. GTK_WIDGET_CLASS(class), TimerSettingsWindow, taskNameEntry);
  239. gtk_widget_class_bind_template_child_internal(
  240. GTK_WIDGET_CLASS(class), TimerSettingsWindow, addTaskButton);
  241. gtk_widget_class_bind_template_child_internal(
  242. GTK_WIDGET_CLASS(class), TimerSettingsWindow, graphButton);
  243. gtk_widget_class_bind_template_child_internal(
  244. GTK_WIDGET_CLASS(class), TimerSettingsWindow, exportButton);
  245. gtk_widget_class_bind_template_child_internal(
  246. GTK_WIDGET_CLASS(class), TimerSettingsWindow, alwaysOnTopCheck);
  247. gtk_widget_class_bind_template_child_internal(
  248. GTK_WIDGET_CLASS(class), TimerSettingsWindow, versionLabel);
  249. }
  250. static void timer_settings_window_init(TimerSettingsWindow *self) {
  251. gtk_widget_init_template(GTK_WIDGET(self));
  252. gtk_window_set_keep_above(GTK_WINDOW(self), TRUE);
  253. g_signal_connect(self->cancelButton, "clicked",
  254. G_CALLBACK(cancel_button_callback), self);
  255. g_signal_connect(self->saveButton, "clicked",
  256. G_CALLBACK(save_button_callback), self);
  257. g_signal_connect(self->addTaskButton, "clicked",
  258. G_CALLBACK(add_button_callback), self);
  259. g_signal_connect(self->removeTaskButton, "clicked",
  260. G_CALLBACK(remove_button_callback), self);
  261. g_signal_connect(self->exportButton, "clicked",
  262. G_CALLBACK(export_button_callback), self);
  263. g_signal_connect(self->graphButton, "clicked",
  264. G_CALLBACK(graph_button_callback), self);
  265. gtk_label_set_text(GTK_LABEL(self->versionLabel), "Version " APPLICATION_VERSION);
  266. }