292 lines
11 KiB
C
292 lines
11 KiB
C
|
#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);
|
||
|
}
|