2022-08-28 14:20:12 -07:00
# 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 ) ;
}
2022-11-08 14:53:51 -08:00
self - > fileWatcher = NULL ;
2022-08-28 14:20:12 -07:00
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 , & currentIter ) ) {
if ( gtk_tree_model_iter_parent ( GTK_TREE_MODEL ( tree - > store ) ,
& parentIter , & currentIter ) ) {
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 ) ;
}