123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834 |
- #include "TimerTaskTree.h"
- #include "TimerEditWindow.h"
- #include "TimerFileWatcher.h"
- #include <json-glib/json-glib.h>
- #include <math.h>
- static gint hash_date(GDateTime *dt) {
- int y, m, d;
- g_date_time_get_ymd(dt, &y, &m, &d);
- GDate *da = g_date_new_dmy(d, m, y);
- return g_date_get_julian(da);
- }
- static gboolean compare_date(GDateTime *dt1, GDateTime *dt2) {
- int y1, m1, d1, y2, m2, d2;
- g_date_time_get_ymd(dt1, &y1, &m1, &d1);
- g_date_time_get_ymd(dt2, &y2, &m2, &d2);
- return y1 == y2 && m1 == m2 && d1 == d2;
- }
- static gint hash_time(GDateTime *t) {
- int h = g_date_time_get_hour(t);
- int m = g_date_time_get_minute(t);
- int s = g_date_time_get_second(t);
- return (h * 3600) + (m * 60) + s;
- }
- enum { DATE_COLUMN, TOTAL_TIME_COLUMN, SORT_COLUMN, DATA_COLUMN, N_COLUMNS };
- /* TimerHeader declarations */
- #define TIMER_TYPE_HEADER timer_header_get_type()
- G_DECLARE_FINAL_TYPE(TimerHeader, timer_header, TIMER, HEADER, GObject);
- static TimerHeader *timer_header_new(GDateTime *date, TimerTaskTree *tree);
- static void timer_header_add_task(TimerHeader *self, const char *name,
- GDateTime *start, gint64 length);
- /* TimerTask declarations */
- #define TIMER_TYPE_TASK timer_task_get_type()
- G_DECLARE_FINAL_TYPE(TimerTask, timer_task, TIMER, TASK, GObject);
- static TimerTask *timer_task_new(const char *name, GDateTime *start,
- gint64 length, TimerHeader *header);
- static gint64 timer_task_get_length(TimerTask *self);
- static JsonObject *timer_task_serialize(TimerTask *self);
- /* TimerTaskTree variables */
- struct _TimerTaskTree {
- GtkTreeView parent;
- GtkTreeStore *store;
- GHashTable *headers;
- GtkWidget *popup;
- GtkWidget *deleteButton;
- GtkWidget *editButton;
- char **taskNames;
- gsize taskNamesLen;
- TimerFileWatcher *fileWatcher;
- char *dataPath;
- };
- G_DEFINE_TYPE(TimerTaskTree, timer_task_tree, GTK_TYPE_TREE_VIEW);
- /* TimerTask variables */
- struct _TimerTask {
- GObject parent;
- TimerTaskTree *tree;
- GtkTreeStore *store;
- char *name;
- GDateTime *start;
- gint64 length;
- GtkTreeIter iter;
- };
- G_DEFINE_TYPE(TimerTask, timer_task, G_TYPE_OBJECT);
- /* TimerHeader class */
- struct _TimerHeader {
- GObject parent;
- TimerTaskTree *tree;
- GtkTreeStore *store;
- GDateTime *date;
- GtkTreeIter iter;
- };
- G_DEFINE_TYPE(TimerHeader, timer_header, G_TYPE_OBJECT);
- static char *timer_header_get_date_string(TimerHeader *self) {
- GDateTime *now = g_date_time_new_now_local();
- if (compare_date(self->date, now)) {
- g_date_time_unref(now);
- return g_strdup("Today");
- }
- GDateTime *yesterday = g_date_time_add_days(now, -1);
- g_date_time_unref(now);
- if (compare_date(self->date, yesterday)) {
- g_date_time_unref(yesterday);
- return g_strdup("Yesterday");
- }
- g_date_time_unref(yesterday);
- int y, m, d;
- g_date_time_get_ymd(self->date, &y, &m, &d);
- return g_strdup_printf("%02d/%02d/%04d", m, d, y);
- }
- static JsonObject *timer_header_serialize(TimerHeader *self) {
- JsonObject *root = json_object_new();
- json_object_set_int_member(root, "date", g_date_time_to_unix(self->date));
- JsonArray *tasks = json_array_new();
- GtkTreeIter child;
- if (gtk_tree_model_iter_children(GTK_TREE_MODEL(self->store), &child,
- &self->iter)) {
- TimerTask *t;
- do {
- gtk_tree_model_get(GTK_TREE_MODEL(self->store), &child, DATA_COLUMN,
- &t, -1);
- g_object_unref(t);
- json_array_add_object_element(tasks, timer_task_serialize(t));
- } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(self->store), &child));
- }
- json_object_set_array_member(root, "tasks", tasks);
- return root;
- }
- static TimerHeader *timer_header_new(GDateTime *date, TimerTaskTree *tree) {
- TimerHeader *h = g_object_new(TIMER_TYPE_HEADER, NULL);
- h->date = g_date_time_to_local(date);
- h->tree = tree;
- h->store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree)));
- gtk_tree_store_append(h->store, &h->iter, NULL);
- char *ds = timer_header_get_date_string(h);
- gtk_tree_store_set(h->store, &h->iter, DATE_COLUMN, ds, TOTAL_TIME_COLUMN,
- "00:00:00", SORT_COLUMN, hash_date(h->date), DATA_COLUMN,
- h, -1);
- g_object_unref(h);
- g_free(ds);
- gtk_widget_show_all(GTK_WIDGET(tree));
- return h;
- }
- static guint64 timer_header_get_total_time(TimerHeader *self) {
- guint64 time = 0;
- GtkTreeIter task;
- if (gtk_tree_model_iter_children(GTK_TREE_MODEL(self->store), &task,
- &self->iter)) {
- TimerTask *t;
- do {
- gtk_tree_model_get(GTK_TREE_MODEL(self->store), &task, DATA_COLUMN,
- &t, -1);
- g_object_unref(t);
- time += timer_task_get_length(t);
- } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(self->store), &task));
- }
- return time;
- }
- static void timer_header_set_time(TimerHeader *self, guint64 time) {
- int h = floor(time / 3600.0f);
- int m = floor(time / 60.0f) - (h * 60);
- int s = time - (m * 60) - (h * 3600);
- char *total = g_strdup_printf("%02d:%02d:%02d", h, m, s);
- gtk_tree_store_set(self->store, &self->iter, TOTAL_TIME_COLUMN, total, -1);
- g_free(total);
- }
- static void timer_header_update_time(TimerHeader *self) {
- timer_header_set_time(self, timer_header_get_total_time(self));
- }
- static void timer_header_add_task(TimerHeader *self, const char *name,
- GDateTime *start, gint64 length) {
- timer_task_new(name, start, length, self);
- timer_header_update_time(self);
- gtk_widget_show_all(GTK_WIDGET(self->tree));
- }
- static void timer_header_remove(TimerHeader *self) {
- gtk_tree_store_remove(self->store, &self->iter);
- }
- static void timer_header_remove_task(TimerHeader *self, TimerTask *task) {
- gtk_tree_store_remove(self->store, &task->iter);
- if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(self->store),
- &self->iter)) {
- timer_header_remove(self);
- }
- }
- static void timer_header_finalize(GObject *self) {
- g_hash_table_remove(TIMER_HEADER(self)->tree->headers,
- TIMER_HEADER(self)->date);
- g_date_time_unref(TIMER_HEADER(self)->date);
- G_OBJECT_CLASS(timer_header_parent_class)->finalize(self);
- }
- static void timer_header_class_init(TimerHeaderClass *class) {
- G_OBJECT_CLASS(class)->finalize = timer_header_finalize;
- }
- static void timer_header_init(TimerHeader *self) {
- }
- /* TimerTask class */
- static TimerHeader *timer_task_get_header(TimerTask *self) {
- TimerHeader *h;
- GtkTreeIter parent;
- gtk_tree_model_iter_parent(GTK_TREE_MODEL(self->store), &parent,
- &self->iter);
- gtk_tree_model_get(GTK_TREE_MODEL(self->store), &parent, DATA_COLUMN, &h,
- -1);
- g_object_unref(h);
- return h;
- }
- static void timer_task_update_time(TimerTask *self) {
- int h = floor(self->length / 3600.0f);
- int m = floor(self->length / 60.0f) - (h * 60);
- int s = self->length - (m * 60) - (h * 3600);
- char *total = g_strdup_printf("%02d:%02d:%02d", h, m, s);
- gtk_tree_store_set(self->store, &self->iter, TOTAL_TIME_COLUMN, total, -1);
- TimerHeader *th = timer_task_get_header(self);
- timer_header_update_time(th);
- g_free(total);
- }
- static JsonObject *timer_task_serialize(TimerTask *self) {
- JsonObject *root = json_object_new();
- json_object_set_string_member(root, "name", self->name);
- json_object_set_int_member(root, "length", self->length);
- json_object_set_int_member(root, "start", g_date_time_to_unix(self->start));
- return root;
- }
- static TimerTask *timer_task_new(const char *name, GDateTime *start,
- gint64 length, TimerHeader *header) {
- TimerTask *t = g_object_new(TIMER_TYPE_TASK, NULL);
- t->name = g_strdup(name);
- t->start = g_date_time_to_local(start);
- t->length = length;
- t->tree = header->tree;
- t->store = header->store;
- gtk_tree_store_append(t->store, &t->iter, &header->iter);
- gtk_tree_store_set(t->store, &t->iter, DATE_COLUMN, t->name, SORT_COLUMN,
- hash_time(t->start), DATA_COLUMN, t, -1);
- g_object_unref(t);
- timer_task_update_time(t);
- gtk_widget_show_all(GTK_WIDGET(t->tree));
- return t;
- }
- static void timer_task_edit(TimerTask *self) {
- TimerEditWindow *diag = timer_edit_window_new(
- self->name, self->start, self->length,
- (const char **)self->tree->taskNames, self->tree->taskNamesLen, FALSE, NULL);
- int resp = gtk_dialog_run(GTK_DIALOG(diag));
- if (resp == GTK_RESPONSE_APPLY) {
- GDateTime *newTime = timer_edit_window_get_start(diag);
- if (compare_date(self->start, newTime)) {
- g_free(self->name);
- self->name = timer_edit_window_get_name(diag);
- g_date_time_unref(self->start);
- self->start = newTime;
- gtk_tree_store_set(self->store, &self->iter, DATE_COLUMN,
- self->name, SORT_COLUMN, hash_time(newTime), -1);
- self->length = timer_edit_window_get_length(diag);
- timer_task_update_time(self);
- timer_task_tree_save(self->tree);
- } else {
- TimerHeader *h = timer_task_get_header(self);
- timer_header_remove_task(h, self);
- char *name = timer_edit_window_get_name(diag);
- timer_task_tree_add_task(h->tree, newTime, name,
- timer_edit_window_get_length(diag));
- g_free(name);
- g_date_time_unref(newTime);
- }
- } else if (resp == GTK_RESPONSE_REJECT) {
- TimerHeader *h = timer_task_get_header(self);
- timer_header_remove_task(h, self);
- }
- gtk_widget_destroy(GTK_WIDGET(diag));
- }
- static gint64 timer_task_get_length(TimerTask *self) {
- return self->length;
- }
- static void timer_task_finalize(GObject *self) {
- g_free(TIMER_TASK(self)->name);
- g_date_time_unref(TIMER_TASK(self)->start);
- G_OBJECT_CLASS(timer_task_parent_class)->finalize(self);
- }
- static void timer_task_class_init(TimerTaskClass *class) {
- G_OBJECT_CLASS(class)->finalize = timer_task_finalize;
- }
- static void timer_task_init(TimerTask *self) {
- }
- /* TimerTaskTree class */
- GtkWidget *timer_task_tree_new() {
- TimerTaskTree *t = g_object_new(TIMER_TYPE_TASK_TREE, NULL);
- t->dataPath = NULL;
- return GTK_WIDGET(t);
- }
- static void timer_task_tree_add_task_no_save(TimerTaskTree *self,
- GDateTime *date, const char *task,
- gint64 time) {
- TimerHeader *h;
- if (g_hash_table_contains(self->headers, date)) {
- h = g_hash_table_lookup(self->headers, date);
- } else {
- h = timer_header_new(date, self);
- g_hash_table_insert(self->headers, g_date_time_to_local(date), h);
- }
- timer_header_add_task(h, task, date, time);
- }
- void timer_task_tree_add_task(TimerTaskTree *self, GDateTime *date,
- const char *task, gint64 time) {
- timer_task_tree_add_task_no_save(self, date, task, time);
- timer_task_tree_save(self);
- }
- void timer_task_tree_set_task_names(TimerTaskTree *self, const char **names,
- gsize len) {
- gsize i;
- for (i = 0; i < self->taskNamesLen; ++i) {
- g_free(self->taskNames[i]);
- }
- g_free(self->taskNames);
- self->taskNames = g_malloc_n(len, sizeof(char *));
- for (i = 0; i < len; ++i) {
- self->taskNames[i] = g_strdup(names[i]);
- }
- self->taskNamesLen = len;
- }
- const char **timer_task_tree_get_task_names(TimerTaskTree *self, gsize *len) {
- *len = self->taskNamesLen;
- return (const char **)self->taskNames;
- }
- void timer_task_tree_update_header_dates(TimerTaskTree *self) {
- GHashTableIter iter;
- g_hash_table_iter_init(&iter, self->headers);
- TimerHeader *h = NULL;
- while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &h)) {
- char *ds = timer_header_get_date_string(h);
- gtk_tree_store_set(self->store, &h->iter, DATE_COLUMN, ds, -1);
- free(ds);
- }
- }
- static void timer_task_tree_update_data(TimerTaskTree *self) {
- GHashTableIter iter;
- g_hash_table_iter_init(&iter, self->headers);
- GList *headers = NULL;
- TimerHeader *h;
- while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&h)) {
- headers = g_list_append(headers, h);
- }
- GList *i;
- for (i = headers; i != NULL; i = i->next) {
- timer_header_remove(TIMER_HEADER(i->data));
- }
- g_list_free(headers);
- timer_task_tree_add_from_file(self, self->dataPath);
- }
- static gboolean do_async_update_data_file(TimerTaskTree *tree) {
- timer_task_tree_update_data(tree);
- timer_task_tree_expand_today(tree);
- return FALSE;
- }
- static void data_file_updated(TimerFileWatcher *fw, TimerTaskTree *tree) {
- g_main_context_invoke(NULL, G_SOURCE_FUNC(do_async_update_data_file), tree);
- }
- void timer_task_tree_set_data_path(TimerTaskTree *self, const char *path) {
- g_free(self->dataPath);
- self->dataPath = g_strdup(path);
- timer_task_tree_update_data(self);
- if (self->fileWatcher) {
- g_object_unref(self->fileWatcher);
- }
- self->fileWatcher = NULL;
- self->fileWatcher = timer_file_watcher_new(path);
- g_signal_connect(self->fileWatcher, "file-changed", G_CALLBACK(data_file_updated), self);
- }
- void timer_task_tree_expand_today(TimerTaskTree *self) {
- GDateTime *today = g_date_time_new_now_local();
- TimerHeader *h = g_hash_table_lookup(self->headers, today);
- g_date_time_unref(today);
- if (h != NULL) {
- GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(self->store), &h->iter);
- gtk_tree_view_expand_row(GTK_TREE_VIEW(self), path, TRUE);
- gtk_tree_path_free(path);
- }
- }
- void timer_task_tree_save(TimerTaskTree *self) {
- if (self->fileWatcher) {
- timer_file_watcher_pause(self->fileWatcher);
- }
- GHashTableIter iter;
- g_hash_table_iter_init(&iter, self->headers);
- TimerHeader *h;
- JsonArray *headers = json_array_new();
- while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&h)) {
- json_array_add_object_element(headers, timer_header_serialize(h));
- }
- JsonNode *root = json_node_new(JSON_NODE_ARRAY);
- json_node_set_array(root, headers);
- JsonGenerator *out = json_generator_new();
- json_generator_set_root(out, root);
- json_node_unref(root);
- if (self->dataPath != NULL) {
- GError *err = NULL;
- json_generator_to_file(out, self->dataPath, &err);
- if (err != NULL) {
- GtkWidget *diag = gtk_message_dialog_new(
- NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Could not save tasks: %s", err->message);
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- g_error_free(err);
- }
- }
- g_object_unref(out);
- if (self->fileWatcher) {
- timer_file_watcher_resume(self->fileWatcher, FALSE);
- }
- }
- static gboolean timer_task_tree_add_task_from_object(TimerTaskTree *self,
- JsonObject *obj,
- gboolean status) {
- JsonNode *nameNode = json_object_get_member(obj, "name");
- JsonNode *lengthNode = json_object_get_member(obj, "length");
- JsonNode *startNode = json_object_get_member(obj, "start");
- if (nameNode == NULL || lengthNode == NULL || startNode == NULL ||
- json_node_get_value_type(nameNode) != G_TYPE_STRING ||
- json_node_get_value_type(lengthNode) != G_TYPE_INT64 ||
- json_node_get_value_type(startNode) != G_TYPE_INT64) {
- if (status) {
- GtkWidget *diag = gtk_message_dialog_new(
- NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Corrupt task found! It and all future corrupt objects will be "
- "skipped. No furtur errors will be emmited for the remailder "
- "of this load.");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- }
- return FALSE;
- }
- GDateTime *date =
- g_date_time_new_from_unix_local(json_node_get_int(startNode));
- timer_task_tree_add_task_no_save(self, date, json_node_get_string(nameNode),
- json_node_get_int(lengthNode));
- g_date_time_unref(date);
- return TRUE;
- }
- static gboolean timer_task_tree_add_header_from_object(TimerTaskTree *self,
- JsonObject *obj,
- gboolean status) {
- JsonNode *tasksNode = json_object_get_member(obj, "tasks");
- if (tasksNode == NULL || JSON_NODE_TYPE(tasksNode) != JSON_NODE_ARRAY) {
- if (status) {
- GtkWidget *diag = gtk_message_dialog_new(
- NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Corrupt header found! It and all future corrupt objects will "
- "be skipped. No furtur errors will be emmited for the "
- "remailder of this load.");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- }
- return FALSE;
- }
- JsonArray *arr = json_node_get_array(tasksNode);
- GList *tasks = json_array_get_elements(arr);
- GList *i;
- for (i = tasks; i != NULL; i = i->next) {
- JsonNode *taskNode = (JsonNode *)i->data;
- if (JSON_NODE_TYPE(taskNode) != JSON_NODE_OBJECT) {
- if (status) {
- GtkWidget *diag = gtk_message_dialog_new(
- NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Corrupt header found! It and all future corrupt objects "
- "will be skipped. No furtur errors will be emmited for the "
- "remailder of this load.");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- }
- status = FALSE;
- } else {
- if (!timer_task_tree_add_task_from_object(
- self, json_node_get_object(taskNode), status)) {
- status = FALSE;
- }
- }
- }
- g_list_free(tasks);
- return status;
- }
- void timer_task_tree_add_from_file(TimerTaskTree *self, const char *path) {
- JsonParser *in = json_parser_new_immutable();
- GError *err = NULL;
- json_parser_load_from_file(in, path, &err);
- if (err != NULL) {
- GtkWidget *diag = gtk_message_dialog_new(
- NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Could not loads tasks: %s", err->message);
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- g_error_free(err);
- } else {
- JsonNode *root = json_parser_get_root(in);
- if (JSON_NODE_TYPE(root) != JSON_NODE_ARRAY) {
- GtkWidget *diag = gtk_message_dialog_new(
- NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Task file corrupt! Root was not an array!");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- } else {
- gboolean status = TRUE;
- GList *headers =
- json_array_get_elements(json_node_get_array((root)));
- GList *i;
- for (i = headers; i != NULL; i = i->next) {
- JsonNode *hNode = (JsonNode *)i->data;
- if (JSON_NODE_TYPE(hNode) != JSON_NODE_OBJECT) {
- if (status) {
- GtkWidget *diag = gtk_message_dialog_new(
- NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
- GTK_BUTTONS_OK,
- "Corrupt header found! It and all future corrupt "
- "objects will be skipped. No furtur errors will be "
- "emmited for the remailder of this load.");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(diag));
- gtk_widget_destroy(diag);
- }
- status = FALSE;
- } else {
- if (!timer_task_tree_add_header_from_object(
- self, json_node_get_object(hNode), status)) {
- status = FALSE;
- }
- }
- }
- g_list_free(headers);
- }
- }
- g_object_unref(in);
- }
- char *timer_task_tree_get_csv(TimerTaskTree *self) {
- GString *string = g_string_new("name,start,length\n");
- GHashTableIter hIter;
- g_hash_table_iter_init(&hIter, self->headers);
- TimerHeader *header;
- while (g_hash_table_iter_next(&hIter, NULL, (gpointer *)&header)) {
- GtkTreeIter child;
- if (gtk_tree_model_iter_children(GTK_TREE_MODEL(self->store), &child,
- &header->iter)) {
- TimerTask *task;
- do {
- gtk_tree_model_get(GTK_TREE_MODEL(self->store), &child,
- DATA_COLUMN, &task, -1);
- g_object_unref(task);
- g_string_append_printf(string, "%s,%ld,%ld\n", task->name,
- g_date_time_to_unix(task->start),
- task->length);
- } while (
- gtk_tree_model_iter_next(GTK_TREE_MODEL(self->store), &child));
- }
- }
- return g_string_free(string, FALSE);
- }
- TimerDataPoint *timer_task_tree_get_day_data(TimerTaskTree *self, gsize *length) {
- *length = 0;
- TimerDataPoint *arr = g_malloc(1);
- GHashTableIter hIter;
- g_hash_table_iter_init(&hIter, self->headers);
- TimerHeader *header;
- while (g_hash_table_iter_next(&hIter, NULL, (gpointer *)&header)) {
- arr = g_realloc(arr, sizeof(TimerDataPoint) * ++(*length));
- arr[*length - 1] = (TimerDataPoint){
- g_date_time_to_local(header->date), timer_header_get_total_time(header)};
- }
- return arr;
- }
- TimerDataPoint *timer_task_tree_get_task_data(TimerTaskTree *self, gsize *length) {
- *length = 0;
- TimerDataPoint *arr = g_malloc(1);
- GHashTableIter hIter;
- g_hash_table_iter_init(&hIter, self->headers);
- TimerHeader *header;
- while (g_hash_table_iter_next(&hIter, NULL, (gpointer *)&header)) {
- GtkTreeIter child;
- if (gtk_tree_model_iter_children(GTK_TREE_MODEL(self->store), &child,
- &header->iter)) {
- TimerTask *task;
- do {
- gtk_tree_model_get(GTK_TREE_MODEL(self->store), &child,
- DATA_COLUMN, &task, -1);
- g_object_unref(task);
- arr = g_realloc(arr, sizeof(TimerDataPoint) * ++(*length));
- arr[*length - 1] = (TimerDataPoint){
- g_date_time_to_local(task->start), task->length};
- } while (
- gtk_tree_model_iter_next(GTK_TREE_MODEL(self->store), &child));
- }
- }
- return arr;
- }
- void timer_free_task_data(TimerDataPoint *data, gsize length) {
- gsize i;
- for (i = 0; i < length; ++i) {
- g_date_time_unref(data[i].date);
- }
- g_free(data);
- }
- GDateTime *timer_task_tree_get_last_task_end(TimerTaskTree *self) {
- GtkTreeIter headerIter;
- if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(self->store), &headerIter)) {
- return NULL;
- }
- GtkTreeIter child;
- if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(self->store), &child, &headerIter, 0)) {
- return NULL;
- }
- TimerTask *task;
- gtk_tree_model_get(GTK_TREE_MODEL(self->store), &child, DATA_COLUMN, &task, -1);
- g_object_unref(task);
- GDateTime *dt = g_date_time_add_seconds(task->start, task->length);
- return dt;
- }
- static gint tree_sort_compare_func(GtkTreeModel *model, GtkTreeIter *i1,
- GtkTreeIter *i2) {
- GValue v1 = G_VALUE_INIT, v2 = G_VALUE_INIT;
- gtk_tree_model_get_value(model, i1, SORT_COLUMN, &v1);
- gtk_tree_model_get_value(model, i2, SORT_COLUMN, &v2);
- return g_value_get_uint64(&v1) - g_value_get_uint64(&v2);
- }
- static gboolean mouse_click_callback(TimerTaskTree *tree,
- GdkEventButton *event) {
- if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
- GtkTreeSelection *selection =
- gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
- GtkTreePath *path;
- if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tree), event->x,
- event->y, &path, NULL, NULL, NULL)) {
- gtk_tree_selection_unselect_all(selection);
- gtk_tree_selection_select_path(selection, path);
- gtk_tree_path_free(path);
- }
- GtkTreeIter currentIter, parentIter;
- if (gtk_tree_selection_get_selected(selection, NULL, ¤tIter)) {
- if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(tree->store),
- &parentIter, ¤tIter)) {
- gtk_widget_set_visible(tree->editButton, TRUE);
- } else {
- gtk_widget_set_visible(tree->editButton, FALSE);
- }
- gtk_menu_popup_at_pointer(GTK_MENU(tree->popup), (GdkEvent *)event);
- }
- return TRUE;
- } else if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
- GtkTreeSelection *selection =
- gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
- GtkTreePath *path;
- if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tree), event->x,
- event->y, &path, NULL, NULL, NULL)) {
- gtk_tree_selection_unselect_all(selection);
- gtk_tree_selection_select_path(selection, path);
- gtk_tree_path_free(path);
- }
- GtkTreeIter select;
- if (gtk_tree_selection_get_selected(selection, NULL, &select)) {
- GObject *obj;
- gtk_tree_model_get(GTK_TREE_MODEL(tree->store), &select,
- DATA_COLUMN, &obj, -1);
- g_object_unref(obj);
- if (TIMER_IS_TASK(obj)) {
- timer_task_edit(TIMER_TASK(obj));
- timer_task_tree_save(tree);
- } else if (TIMER_IS_HEADER(obj)) {
- GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree->store), &TIMER_HEADER(obj)->iter);
- if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree), path)) {
- gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
- } else {
- gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, TRUE);
- }
- gtk_tree_path_free(path);
- }
- }
- return TRUE;
- }
- return FALSE;
- }
- static void popup_edit_button_callback(GtkMenuItem *btn, TimerTaskTree *tree) {
- GtkTreeSelection *selection =
- gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
- GtkTreeIter select;
- if (gtk_tree_selection_get_selected(selection, NULL, &select)) {
- TimerTask *task;
- gtk_tree_model_get(GTK_TREE_MODEL(tree->store), &select, DATA_COLUMN,
- &task, -1);
- g_object_unref(task);
- timer_task_edit(task);
- timer_task_tree_save(tree);
- }
- }
- static void popup_delete_button_callback(GtkMenuItem *btn,
- TimerTaskTree *tree) {
- GtkTreeSelection *selection =
- gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
- GtkTreeIter select;
- if (gtk_tree_selection_get_selected(selection, NULL, &select)) {
- GObject *obj;
- gtk_tree_model_get(GTK_TREE_MODEL(tree->store), &select, DATA_COLUMN,
- &obj, -1);
- g_object_unref(obj);
- if (TIMER_IS_TASK(obj)) {
- timer_header_remove_task(timer_task_get_header(TIMER_TASK(obj)),
- TIMER_TASK(obj));
- } else {
- GtkWidget *diag = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Are you sure you would like to delete ALL tasks on this day?");
- gtk_window_set_position(GTK_WINDOW(diag), GTK_WIN_POS_MOUSE);
- int resp = gtk_dialog_run(GTK_DIALOG(diag));
- if (resp == GTK_RESPONSE_YES) {
- timer_header_remove(TIMER_HEADER(obj));
- }
- gtk_widget_destroy(diag);
- }
- timer_task_tree_save(tree);
- }
- }
- static void timer_task_tree_finalize(GObject *self) {
- gsize i;
- for (i = 0; i < TIMER_TASK_TREE(self)->taskNamesLen; ++i) {
- g_free(TIMER_TASK_TREE(self)->taskNames[i]);
- }
- g_free(TIMER_TASK_TREE(self)->dataPath);
- g_free(TIMER_TASK_TREE(self)->taskNames);
- gtk_widget_destroy(TIMER_TASK_TREE(self)->popup);
- g_object_unref(TIMER_TASK_TREE(self)->store);
- g_hash_table_destroy(TIMER_TASK_TREE(self)->headers);
- G_OBJECT_CLASS(timer_task_tree_parent_class)->finalize(self);
- }
- static void timer_task_tree_class_init(TimerTaskTreeClass *class) {
- G_OBJECT_CLASS(class)->finalize = timer_task_tree_finalize;
- }
- static void timer_task_tree_init(TimerTaskTree *self) {
- self->store = gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_UINT64, G_TYPE_OBJECT);
- gtk_tree_view_set_model(GTK_TREE_VIEW(self), GTK_TREE_MODEL(self->store));
- self->headers =
- g_hash_table_new_full((GHashFunc)hash_date, (GCompareFunc)compare_date,
- (GDestroyNotify)g_date_time_unref, NULL);
- GtkCellRenderer *render = gtk_cell_renderer_text_new();
- GValue font = G_VALUE_INIT;
- g_value_init(&font, G_TYPE_STRING);
- g_value_set_static_string(&font, "16");
- g_object_set_property(G_OBJECT(render), "font", &font);
- g_value_unset(&font);
- gtk_tree_view_append_column(
- GTK_TREE_VIEW(self),
- gtk_tree_view_column_new_with_attributes(
- "Date", render, "text", DATE_COLUMN, NULL));
- gtk_tree_view_append_column(GTK_TREE_VIEW(self),
- gtk_tree_view_column_new_with_attributes(
- "Time", render,
- "text", TOTAL_TIME_COLUMN, NULL));
- GtkTreeViewColumn *sort = gtk_tree_view_column_new_with_attributes(
- "Sort", render, NULL);
- gtk_tree_view_column_set_visible(sort, FALSE);
- gtk_tree_view_append_column(GTK_TREE_VIEW(self), sort);
- gtk_tree_sortable_set_default_sort_func(
- GTK_TREE_SORTABLE(self->store),
- (GtkTreeIterCompareFunc)tree_sort_compare_func, NULL, NULL);
- gtk_tree_sortable_set_sort_column_id(
- GTK_TREE_SORTABLE(self->store),
- GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_DESCENDING);
- gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self), FALSE);
- self->popup = gtk_menu_new();
- self->deleteButton = gtk_menu_item_new_with_label("Delete");
- self->editButton = gtk_menu_item_new_with_label("Edit");
- gtk_menu_shell_append(GTK_MENU_SHELL(self->popup), self->deleteButton);
- gtk_menu_shell_append(GTK_MENU_SHELL(self->popup), self->editButton);
- gtk_widget_show_all(self->popup);
- g_signal_connect(self, "button_press_event",
- G_CALLBACK(mouse_click_callback), NULL);
- g_signal_connect(self->deleteButton, "activate",
- G_CALLBACK(popup_delete_button_callback), self);
- g_signal_connect(self->editButton, "activate",
- G_CALLBACK(popup_edit_button_callback), self);
- }
|