#include "TimerSettingsWindow.h" #include "TimerGraphWindow.h" #include "TimerMainWindow.h" #define DEFAULT_DATA_FILE_NAME "practicetimer/data.json" struct _TimerSettingsWindow { GtkDialog parent; GtkWidget *cancelButton; GtkWidget *saveButton; GtkWidget *dataPathButton; GtkWidget *taskBox; GtkWidget *removeTaskButton; GtkWidget *taskNameEntry; GtkWidget *addTaskButton; GtkWidget *graphButton; GtkWidget *exportButton; GtkWidget *alwaysOnTopCheck; GtkWidget *versionLabel; GKeyFile *keyFile; TimerMainWindow *parentWindow; }; G_DEFINE_TYPE(TimerSettingsWindow, timer_settings_window, GTK_TYPE_DIALOG) static void timer_settings_window_copy_key_file(TimerSettingsWindow *self, GKeyFile *keyFile) { self->keyFile = g_key_file_new(); gsize len; char *data = g_key_file_to_data(keyFile, &len, NULL); g_key_file_load_from_data(self->keyFile, data, len, G_KEY_FILE_NONE, NULL); g_free(data); } static gboolean timer_settings_window_get_boolean_with_default(TimerSettingsWindow *self, const char *group, const char *key, gboolean def) { GError *err = NULL; gboolean b = g_key_file_get_boolean(self->keyFile, group, key, &err); if (err != NULL) { g_error_free(err); return def; } return b; } static char *timer_settings_window_get_string_with_default( TimerSettingsWindow *self, const char *group, const char *key, const char *def) { GError *err = NULL; char *s = g_key_file_get_string(self->keyFile, group, key, &err); if (err != NULL) { g_error_free(err); return g_strdup(def); } return s; } static void timer_settings_window_interpret_settings(TimerSettingsWindow *self) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->alwaysOnTopCheck), timer_settings_window_get_boolean_with_default( self, "Settings", "Always on Top", FALSE)); /* Data path stuff */ char *defaultDataPath = g_strdup_printf("%s/%s", g_get_user_config_dir(), DEFAULT_DATA_FILE_NAME); char *dataPath = timer_settings_window_get_string_with_default( self, "Settings", "Data Path", defaultDataPath); gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(self->dataPathButton), dataPath); g_free(defaultDataPath); g_free(dataPath); /* Task list stuff */ GError *err = NULL; gsize len; char **tasks = g_key_file_get_string_list(self->keyFile, "Settings", "Tasks", &len, &err); if (err == NULL) { gsize i; for (i = 0; i < len; ++i) { gtk_list_box_insert(GTK_LIST_BOX(self->taskBox), gtk_label_new(tasks[i]), -1); g_free(tasks[i]); } g_free(tasks); } else { g_error_free(err); } gtk_widget_show_all(self->taskBox); } TimerSettingsWindow *timer_settings_window_new(TimerApplication *app, GKeyFile *keyFile, GtkWindow *parentWindow) { TimerSettingsWindow *win = TIMER_SETTINGS_WINDOW( g_object_new(TIMER_TYPE_SETTINGS_WINDOW, "application", app, NULL)); timer_settings_window_copy_key_file(win, keyFile); timer_settings_window_interpret_settings(win); if (TIMER_IS_MAIN_WINDOW(parentWindow)) { win->parentWindow = TIMER_MAIN_WINDOW(parentWindow); } return win; } GKeyFile *timer_settings_window_get_key_file(TimerSettingsWindow *self) { return self->keyFile; } static gboolean timer_settings_window_has_task(TimerSettingsWindow *self, const char *task) { GList *tasks = gtk_container_get_children(GTK_CONTAINER(self->taskBox)); GList *i; for (i = tasks; i != NULL; i = i->next) { if (strcmp(task, gtk_label_get_text(GTK_LABEL( gtk_bin_get_child(GTK_BIN(i->data))))) == 0) { return TRUE; } } g_list_free(tasks); return FALSE; } static void add_button_callback(GtkButton *btn, TimerSettingsWindow *win) { const char *task = gtk_entry_get_text(GTK_ENTRY(win->taskNameEntry)); if (strlen(task) != 0) { if (!timer_settings_window_has_task(win, task)) { gtk_list_box_insert(GTK_LIST_BOX(win->taskBox), gtk_label_new(task), -1); gtk_widget_show_all(win->taskBox); } gtk_entry_set_text(GTK_ENTRY(win->taskNameEntry), ""); } } static void remove_button_callback(GtkButton *btn, TimerSettingsWindow *win) { GList *rows = gtk_list_box_get_selected_rows(GTK_LIST_BOX(win->taskBox)); GList *i; for (i = rows; i != NULL; i = i->next) { gtk_container_remove(GTK_CONTAINER(win->taskBox), GTK_WIDGET(i->data)); } g_list_free(rows); } static void cancel_button_callback(GtkButton *btn, TimerSettingsWindow *win) { gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_CANCEL); gtk_window_close(GTK_WINDOW(win)); } static const char *const * timer_settings_window_get_task_list_string(TimerSettingsWindow *self, gsize *len) { GList *tasks = gtk_container_get_children(GTK_CONTAINER(self->taskBox)); gsize taskCount = g_list_length(tasks); char **taskStrings = g_malloc(sizeof(char *) * taskCount); GList *it; int in; for (in = 0, it = tasks; it != NULL; ++in, it = it->next) { taskStrings[in] = (char *)gtk_label_get_text( GTK_LABEL(gtk_bin_get_child(GTK_BIN(it->data)))); } g_list_free(tasks); if (len != NULL) { *len = taskCount; } return (const char *const *)taskStrings; } static void save_button_callback(GtkButton *btn, TimerSettingsWindow *win) { g_key_file_set_boolean( win->keyFile, "Settings", "Always on Top", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->alwaysOnTopCheck))); g_key_file_set_string( win->keyFile, "Settings", "Data Path", gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(win->dataPathButton))); gsize taskCount; const char *const *taskList = timer_settings_window_get_task_list_string(win, &taskCount); g_key_file_set_string_list(win->keyFile, "Settings", "Tasks", taskList, taskCount); g_free((gpointer)taskList); gtk_dialog_response(GTK_DIALOG(win), GTK_RESPONSE_APPLY); gtk_window_close(GTK_WINDOW(win)); } static void export_button_callback(GtkButton *btn, TimerSettingsWindow *win) { GtkWidget *diag = gtk_file_chooser_dialog_new( "Export Data", GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL, "_Export", GTK_RESPONSE_ACCEPT, NULL); int resp = gtk_dialog_run(GTK_DIALOG(diag)); if (resp == GTK_RESPONSE_ACCEPT) { char *path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(diag)); char *csvData = timer_main_window_get_task_csv(win->parentWindow); GFile *file = g_file_new_for_path(path); GError *err = NULL; GFileOutputStream *out = g_file_create(file, G_FILE_CREATE_NONE, NULL, &err); if (err != NULL) { GtkWidget *msgDiag = gtk_message_dialog_new( GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not export data: %s", err->message); gtk_window_set_position(GTK_WINDOW(msgDiag), GTK_WIN_POS_MOUSE); gtk_dialog_run(GTK_DIALOG(msgDiag)); gtk_widget_destroy(msgDiag); g_clear_error(&err); } g_output_stream_write(G_OUTPUT_STREAM(out), csvData, strlen(csvData), NULL, &err); if (err != NULL) { GtkWidget *msgDiag = gtk_message_dialog_new( GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not export data: %s", err->message); gtk_window_set_position(GTK_WINDOW(msgDiag), GTK_WIN_POS_MOUSE); gtk_dialog_run(GTK_DIALOG(msgDiag)); gtk_widget_destroy(msgDiag); g_error_free(err); } g_object_unref(out); g_object_unref(file); g_free(csvData); g_free(path); } gtk_widget_destroy(diag); } static void graph_button_callback(GtkButton *btn, TimerSettingsWindow *win) { gsize taskLen; TimerDataPoint *taskData = timer_main_window_get_task_data(win->parentWindow, &taskLen); gsize dayLen; TimerDataPoint *dayData = timer_main_window_get_day_data(win->parentWindow, &dayLen); TimerGraphWindow *diag = timer_graph_window_new(TIMER_APPLICATION(gtk_window_get_application(GTK_WINDOW(win))), dayData, dayLen, taskData, taskLen); gtk_dialog_run(GTK_DIALOG(diag)); gtk_widget_destroy(GTK_WIDGET(diag)); } static void timer_settings_window_finalize(GObject *self) { g_key_file_free(TIMER_SETTINGS_WINDOW(self)->keyFile); G_OBJECT_CLASS(timer_settings_window_parent_class)->finalize(self); } static void timer_settings_window_class_init(TimerSettingsWindowClass *class) { G_OBJECT_CLASS(class)->finalize = timer_settings_window_finalize; gtk_widget_class_set_template_from_resource( GTK_WIDGET_CLASS(class), "/zander/practicetimer/ui/settings-window.glade"); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, cancelButton); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, saveButton); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, dataPathButton); gtk_widget_class_bind_template_child_internal(GTK_WIDGET_CLASS(class), TimerSettingsWindow, taskBox); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, removeTaskButton); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, taskNameEntry); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, addTaskButton); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, graphButton); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, exportButton); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, alwaysOnTopCheck); gtk_widget_class_bind_template_child_internal( GTK_WIDGET_CLASS(class), TimerSettingsWindow, versionLabel); } static void timer_settings_window_init(TimerSettingsWindow *self) { gtk_widget_init_template(GTK_WIDGET(self)); gtk_window_set_keep_above(GTK_WINDOW(self), TRUE); g_signal_connect(self->cancelButton, "clicked", G_CALLBACK(cancel_button_callback), self); g_signal_connect(self->saveButton, "clicked", G_CALLBACK(save_button_callback), self); g_signal_connect(self->addTaskButton, "clicked", G_CALLBACK(add_button_callback), self); g_signal_connect(self->removeTaskButton, "clicked", G_CALLBACK(remove_button_callback), self); g_signal_connect(self->exportButton, "clicked", G_CALLBACK(export_button_callback), self); g_signal_connect(self->graphButton, "clicked", G_CALLBACK(graph_button_callback), self); gtk_label_set_text(GTK_LABEL(self->versionLabel), "Version " APPLICATION_VERSION); }