123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- #include "TimerGraphWindow.h"
- #include "TimerTaskTree.h"
- #include "TimerGraph.h"
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <math.h>
- enum {
- DATA_COLUMN = 0,
- SEVEN_DAY_COLUMN,
- THIRTY_DAY_COLUMN,
- SIX_MONTH_COLUMN,
- YEAR_COLUMN,
- ALL_TIME_COLUMN,
- N_COLUMNS
- };
- typedef struct {
- gint64 max;
- gint64 min;
- gint64 avg;
- } TimerStatSet;
- struct _TimerGraphWindow {
- GtkApplicationWindow parent;
- GtkWidget *closeButton;
- GtkWidget *chartBox;
- GtkWidget *dayChart;
- GtkWidget *monthChart;
- GtkWidget *table;
- GtkTreeStore *tableStore;
- TimerDataPoint *dayData;
- gsize dayDataLen;
- TimerDataPoint *taskData;
- gsize taskDataLen;
- };
- G_DEFINE_TYPE(TimerGraphWindow, timer_graph_window, GTK_TYPE_DIALOG);
- static gboolean came_after_or_same_day(GDateTime *start, GDateTime *date) {
- int y1, m1, d1, y2, m2, d2;
- g_date_time_get_ymd(start, &y1, &m1, &d1);
- g_date_time_get_ymd(date, &y2, &m2, &d2);
- return (y1 < y2) || (y1 == y2 && m1 < m2) ||
- (y1 == y2 && m1 == m2 && d1 <= d2);
- }
- static gint day_offset(GDateTime *start, GDateTime *end) {
- gint64 su = g_date_time_to_unix(start);
- gint64 eu = g_date_time_to_unix(end);
- return round((float) (eu - su) / (60 * 60 * 24));
- }
- static TimerStatSet timer_graph_window_get_stats_for_days(TimerGraphWindow *self, gsize days) {
- GDateTime *ref = NULL;
- if (days != 0) {
- GDateTime *now = g_date_time_new_now_local();
- ref = g_date_time_add_days(now, -days);
- g_date_time_unref(now);
- } else {
- days = self->dayDataLen;
- }
- gint64 total = 0, max = 0, min = G_MAXINT64;
- gsize i;
- for (i = 0; i < self->dayDataLen; ++i) {
- if (ref == NULL || came_after_or_same_day(ref, self->dayData[i].date)) {
- total += self->dayData[i].lenght;
- if (self->dayData[i].lenght > max) {
- max = self->dayData[i].lenght;
- }
- if (self->dayData[i].lenght < min) {
- min = self->dayData[i].lenght;
- }
- }
- }
- if (ref != NULL) {
- g_date_time_unref(ref);
- }
- if (min == G_MAXINT64) {
- min = 0;
- }
- int avg = 0;
- if (days != 0) {
- avg = floor((float)total / (float)days);
- }
- return (TimerStatSet){max, min, avg};
- }
- static char *format_hours_and_min(gint64 time) {
- int hour = floor(time / 3600.0f);
- int min = floor(time / 60.0f) - (hour * 60);
- return g_strdup_printf("%02d:%02d", hour, min);
- }
- static void timer_graph_window_add_table_data(TimerGraphWindow *self) {
- TimerStatSet d7 = timer_graph_window_get_stats_for_days(self, 7);
- TimerStatSet d30 = timer_graph_window_get_stats_for_days(self, 30);
- TimerStatSet d183 = timer_graph_window_get_stats_for_days(self, 183);
- TimerStatSet d365 = timer_graph_window_get_stats_for_days(self, 365);
- TimerStatSet da = timer_graph_window_get_stats_for_days(self, 0);
- GtkTreeIter highIter;
- gtk_tree_store_append(self->tableStore, &highIter, NULL);
- char *d7ms = format_hours_and_min(d7.max);
- char *d30ms = format_hours_and_min(d30.max);
- char *d183ms = format_hours_and_min(d183.max);
- char *d365ms = format_hours_and_min(d365.max);
- char *dams = format_hours_and_min(da.max);
- gtk_tree_store_set(self->tableStore, &highIter, DATA_COLUMN, "High",
- SEVEN_DAY_COLUMN, d7ms, THIRTY_DAY_COLUMN, d30ms,
- SIX_MONTH_COLUMN, d183ms, YEAR_COLUMN, d365ms,
- ALL_TIME_COLUMN, dams, -1);
- g_free(d7ms);
- g_free(d30ms);
- g_free(d183ms);
- g_free(d365ms);
- g_free(dams);
- /* Dad requested I remove this
- GtkTreeIter lowIter;
- gtk_tree_store_append(self->tableStore, &lowIter, NULL);
- char *d7ls = format_hours_and_min(d7.min);
- char *d30ls = format_hours_and_min(d30.min);
- char *d183ls = format_hours_and_min(d183.min);
- char *d365ls = format_hours_and_min(d365.min);
- char *dals = format_hours_and_min(da.min);
- gtk_tree_store_set(self->tableStore, &lowIter, DATA_COLUMN, "Low",
- SEVEN_DAY_COLUMN, d7ls, THIRTY_DAY_COLUMN, d30ls,
- SIX_MONTH_COLUMN, d183ls, YEAR_COLUMN, d365ls,
- ALL_TIME_COLUMN, dals, -1);
- g_free(d7ls);
- g_free(d30ls);
- g_free(d183ls);
- g_free(d365ls);
- g_free(dals); */
- GtkTreeIter avgIter;
- gtk_tree_store_append(self->tableStore, &avgIter, NULL);
- char *d7as = format_hours_and_min(d7.avg);
- char *d30as = format_hours_and_min(d30.avg);
- char *d183as = format_hours_and_min(d183.avg);
- char *d365as = format_hours_and_min(d365.avg);
- char *daas = format_hours_and_min(da.avg);
- gtk_tree_store_set(self->tableStore, &avgIter, DATA_COLUMN, "Average",
- SEVEN_DAY_COLUMN, d7as, THIRTY_DAY_COLUMN, d30as,
- SIX_MONTH_COLUMN, d183as, YEAR_COLUMN, d365as,
- ALL_TIME_COLUMN, daas, -1);
- g_free(d7as);
- g_free(d30as);
- g_free(d183as);
- g_free(d365as);
- g_free(daas);
- }
- static TimerGraphPoint *timer_graph_window_get_day_graph_data(TimerGraphWindow *self) {
- TimerGraphPoint *pts = g_malloc_n(14, sizeof(TimerGraphPoint));
- gsize i;
- for (i = 0; i < 14; ++i) {
- pts[i].x = i + 1;
- pts[i].y = 0;
- }
- GDateTime *now = g_date_time_new_now_local();
- for (i = 0; i < self->dayDataLen; ++i) {
- int off = day_offset(self->dayData[i].date, now);
- if (off <= 14) {
- pts[off - 1].y += (int) (self->dayData[i].lenght / 60);
- }
- }
- g_date_time_unref(now);
- return pts;
- }
- static void timer_graph_window_make_day_graph(TimerGraphWindow *self) {
- TimerGraphPoint *pts = timer_graph_window_get_day_graph_data(self);
- self->dayChart = timer_graph_new(pts, 14, "Day", "Time (Total Minutes)", "Last 14 Days");
- gtk_widget_set_vexpand(self->dayChart, TRUE);
- gtk_widget_set_hexpand(self->dayChart, TRUE);
- g_free(pts);
- gtk_box_pack_start(GTK_BOX(self->chartBox), self->dayChart, TRUE, TRUE, 0);
- }
- static gboolean same_month(GDateTime *d1, GDateTime *d2) {
- int y1, m1, y2, m2;
- g_date_time_get_ymd(d1, &y1, &m1, NULL);
- g_date_time_get_ymd(d2, &y2, &m2, NULL);
- return y1 == y2 && m1 == m2;
- }
- static int get_days_for_month(int month, int year) {
- switch (month) {
- case 1:
- case 3:
- case 5:
- case 7:
- case 8:
- case 10:
- case 12:
- return 31;
- case 4:
- case 6:
- case 9:
- case 11:
- return 30;
- case 2:
- if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) {
- return 29;
- } else {
- return 28;
- }
- default:
- return 0;
- }
- }
- static TimerGraphPoint *timer_graph_window_get_month_graph_data(TimerGraphWindow *self) {
- TimerGraphPoint *pts = g_malloc_n(12, sizeof(TimerGraphPoint));
- GDateTime *now = g_date_time_new_now_local();
- GDateTime *ref = g_date_time_add_months(now, -1);
- g_date_time_unref(now);
- gsize i;
- for (i = 0; i < 12; ++i) {
- pts[i].x = i + 1;
- int total = 0;
- gsize j;
- for (j = 0; j < self->dayDataLen; ++j) {
- if (same_month(self->dayData[j].date, ref)) {
- total += (int) (self->dayData[j].lenght / 60);
- }
- }
- int y, m;
- g_date_time_get_ymd(ref, &y, &m, NULL);
- pts[i].y = total / get_days_for_month(m, y);
- GDateTime *new = g_date_time_add_months(ref, -1);
- g_date_time_unref(ref);
- ref = new;
- }
- g_date_time_unref(ref);
- return pts;
- }
- static void timer_graph_window_make_month_graph(TimerGraphWindow *self) {
- TimerGraphPoint *pts = timer_graph_window_get_month_graph_data(self);
- self->monthChart = timer_graph_new(pts, 12, "Month", "Time (Average Minutes)", "Last Year");
- gtk_widget_set_vexpand(self->monthChart, TRUE);
- gtk_widget_set_hexpand(self->monthChart, TRUE);
- g_free(pts);
- gtk_box_pack_start(GTK_BOX(self->chartBox), self->monthChart, TRUE, TRUE, 0);
- }
- TimerGraphWindow *timer_graph_window_new(TimerApplication *app,
- TimerDataPoint *dayData, gsize dayDataLen, TimerDataPoint *taskData, gsize taskDataLen) {
- TimerGraphWindow *w =
- g_object_new(TIMER_TYPE_GRAPH_WINDOW, "application", app, "title", "Statistics", NULL);
- w->dayData = dayData;
- w->taskData = taskData;
- w->dayDataLen = dayDataLen;
- w->taskDataLen = taskDataLen;
- timer_graph_window_add_table_data(w);
- timer_graph_window_make_day_graph(w);
- timer_graph_window_make_month_graph(w);
- GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(w));
- GdkDisplay *display = gdk_screen_get_display(screen);
- GdkMonitor *monitor = gdk_display_get_primary_monitor(display);
- if (monitor) {
- GdkRectangle monSize;
- gdk_monitor_get_geometry(monitor, &monSize);
- gtk_window_set_default_size(GTK_WINDOW(w), monSize.width * 0.75f, monSize.height * 0.75f);
- }
- gtk_widget_show_all(GTK_WIDGET(w));
- return w;
- }
- static void close_button_callback(GtkButton *btn, TimerGraphWindow *win) {
- gtk_window_close(GTK_WINDOW(win));
- }
- static void timer_graph_window_build_ui(TimerGraphWindow *self) {
- self->closeButton = gtk_dialog_add_button(GTK_DIALOG(self), "Close", 0);
- GtkWidget *topBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- self->table = gtk_tree_view_new();
- gtk_widget_set_halign(self->table, GTK_ALIGN_CENTER);
- gtk_box_pack_start(GTK_BOX(topBox), self->table, FALSE, FALSE, 0);
- self->chartBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
- gtk_container_add(GTK_CONTAINER(topBox), self->chartBox);
- gtk_container_add(
- GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(self))), topBox);
- }
- static void timer_graph_window_add_tree_columns(TimerGraphWindow *self) {
- gtk_tree_view_append_column(
- GTK_TREE_VIEW(self->table),
- gtk_tree_view_column_new_with_attributes(
- "", gtk_cell_renderer_text_new(), "text", DATA_COLUMN, NULL));
- gtk_tree_view_append_column(GTK_TREE_VIEW(self->table),
- gtk_tree_view_column_new_with_attributes(
- "Last 7 Days", gtk_cell_renderer_text_new(),
- "text", SEVEN_DAY_COLUMN, NULL));
- gtk_tree_view_append_column(GTK_TREE_VIEW(self->table),
- gtk_tree_view_column_new_with_attributes(
- "Last 30 Days",
- gtk_cell_renderer_text_new(), "text",
- THIRTY_DAY_COLUMN, NULL));
- gtk_tree_view_append_column(GTK_TREE_VIEW(self->table),
- gtk_tree_view_column_new_with_attributes(
- "Last 6 Months",
- gtk_cell_renderer_text_new(), "text",
- SIX_MONTH_COLUMN, NULL));
- gtk_tree_view_append_column(GTK_TREE_VIEW(self->table),
- gtk_tree_view_column_new_with_attributes(
- "Last Year", gtk_cell_renderer_text_new(),
- "text", YEAR_COLUMN, NULL));
- gtk_tree_view_append_column(GTK_TREE_VIEW(self->table),
- gtk_tree_view_column_new_with_attributes(
- "All Time", gtk_cell_renderer_text_new(),
- "text", ALL_TIME_COLUMN, NULL));
- }
- static void timer_graph_window_finalize(GObject *self) {
- timer_free_task_data(TIMER_GRAPH_WINDOW(self)->dayData, TIMER_GRAPH_WINDOW(self)->dayDataLen);
- timer_free_task_data(TIMER_GRAPH_WINDOW(self)->taskData, TIMER_GRAPH_WINDOW(self)->taskDataLen);
- G_OBJECT_CLASS(timer_graph_window_parent_class)->finalize(self);
- }
- static void timer_graph_window_class_init(TimerGraphWindowClass *class) {
- G_OBJECT_CLASS(class)->finalize = timer_graph_window_finalize;
- }
- static void timer_graph_window_init(TimerGraphWindow *self) {
- gtk_window_set_keep_above(GTK_WINDOW(self), TRUE);
- gtk_window_set_position(GTK_WINDOW(self), GTK_WIN_POS_MOUSE);
- timer_graph_window_build_ui(self);
- g_signal_connect(self->closeButton, "clicked",
- G_CALLBACK(close_button_callback), self);
- gtk_tree_selection_set_mode(
- gtk_tree_view_get_selection(GTK_TREE_VIEW(self->table)),
- GTK_SELECTION_NONE);
- self->tableStore = gtk_tree_store_new(
- N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING);
- gtk_tree_view_set_model(GTK_TREE_VIEW(self->table),
- GTK_TREE_MODEL(self->tableStore));
- timer_graph_window_add_tree_columns(self);
- }
|