practice-timer/TimerSettingsWindow.c

292 lines
11 KiB
C
Raw Permalink Normal View History

2022-08-28 14:20:12 -07:00
#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);
}