X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fhomebank;a=blobdiff_plain;f=src%2Fhb-category.c;fp=src%2Fhb-category.c;h=3dc34e402c382dd06e66772dd6d3714399e628b6;hp=80f7e19b62e0540df4239a4046d909220b218b2e;hb=236cb5e47660876f46488ea8f76ecd5bebfa1fac;hpb=8892e90b335f94c296462a91534334b674226cd9 diff --git a/src/hb-category.c b/src/hb-category.c index 80f7e19..3dc34e4 100644 --- a/src/hb-category.c +++ b/src/hb-category.c @@ -1,5 +1,5 @@ /* HomeBank -- Free, easy, personal accounting for everyone. - * Copyright (C) 1995-2018 Maxime DOYEN + * Copyright (C) 1995-2019 Maxime DOYEN * * This file is part of HomeBank. * @@ -46,7 +46,8 @@ Category *new_item = g_memdup(src_item, sizeof(Category)); if(new_item) { //duplicate the string - new_item->name = g_strdup(src_item->name); + new_item->name = g_strdup(src_item->name); + new_item->fullname = g_strdup(src_item->fullname); } return new_item; } @@ -61,6 +62,7 @@ da_cat_free(Category *item) DB( g_print(" => %d, %s\n", item->key, item->name) ); g_free(item->name); + g_free(item->fullname); g_free(item); } } @@ -92,7 +94,9 @@ Category *item; // insert our 'no category' item = da_cat_malloc(); + item->key = 0; item->name = g_strdup(""); + item->fullname = g_strdup(""); da_cat_insert(item); } @@ -111,15 +115,30 @@ da_cat_length(void) } +static void +da_cat_max_key_ghfunc(gpointer key, Category *cat, guint32 *max_key) +{ + *max_key = MAX(*max_key, cat->key); +} /** - * da_cat_remove_grfunc: + * da_cat_get_max_key: * - * GRFunc to get the max id + * Get the biggest key from the GHashTable * - * Return value: TRUE if the key/value must be deleted + * Return value: the biggest key value * */ +guint32 +da_cat_get_max_key(void) +{ +guint32 max_key = 0; + + g_hash_table_foreach(GLOBALS->h_cat, (GHFunc)da_cat_max_key_ghfunc, &max_key); + return max_key; +} + + static gboolean da_cat_remove_grfunc(gpointer key, Category *cat, guint32 *remkey) { @@ -141,11 +160,62 @@ da_cat_remove_grfunc(gpointer key, Category *cat, guint32 *remkey) guint da_cat_remove(guint32 key) { - DB( g_print("da_cat_remove %d\n", key) ); + DB( g_print("\nda_cat_remove %d\n", key) ); return g_hash_table_foreach_remove(GLOBALS->h_cat, (GHRFunc)da_cat_remove_grfunc, &key); } + +static void +da_cat_build_fullname(Category *item) +{ +Category *parent; + + g_free(item->fullname); + if( item->parent == 0 ) + item->fullname = g_strdup(item->name); + else + { + parent = da_cat_get(item->parent); + if( parent != NULL ) + item->fullname = g_strconcat(parent->name, ":", item->name, NULL); + } + + DB( g_print("- updated %d:'%s' fullname='%s'\n", item->key, item->name, item->fullname) ); + +} + + +static void +da_cat_rename(Category *item, gchar *newname) +{ + + DB( g_print("- renaming %s' => '%s'\n", item->name, newname) ); + + g_free(item->name); + item->name = g_strdup(newname); + da_cat_build_fullname(item); + + if( item->parent == 0 ) + { + GHashTableIter iter; + gpointer value; + + DB( g_print("- updating subcat fullname\n") ); + + g_hash_table_iter_init (&iter, GLOBALS->h_cat); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + Category *subcat = value; + + if( subcat->parent == item->key ) + da_cat_build_fullname(subcat); + } + + } +} + + /** * da_cat_insert: * @@ -159,12 +229,16 @@ da_cat_insert(Category *item) { guint32 *new_key; - DB( g_print("da_cat_insert\n") ); + DB( g_print("\nda_cat_insert\n") ); + DB( g_print("- '%s'\n", item->name) ); + new_key = g_new0(guint32, 1); *new_key = item->key; g_hash_table_insert(GLOBALS->h_cat, new_key, item); + da_cat_build_fullname(item); + return TRUE; } @@ -177,164 +251,36 @@ guint32 *new_key; * Return value: TRUE if inserted * */ +// used only to add cat/subcat from ui_category with the 2 inputs gboolean da_cat_append(Category *cat) { Category *existitem; -guint32 *new_key; -gchar *fullname; - DB( g_print("da_cat_append\n") ); + DB( g_print("\nda_cat_append\n") ); - if( cat->name != NULL) + if( !cat->fullname ) + da_cat_build_fullname(cat); + + existitem = da_cat_get_by_fullname( cat->fullname ); + if( existitem == NULL ) { - - fullname = da_cat_get_fullname(cat); - existitem = da_cat_get_by_fullname( fullname ); - g_free(fullname); - - if( existitem == NULL ) - { - new_key = g_new0(guint32, 1); - *new_key = da_cat_get_max_key() + 1; - cat->key = *new_key; - - DB( g_print(" -> insert id: %d\n", *new_key) ); - - g_hash_table_insert(GLOBALS->h_cat, new_key, cat); - return TRUE; - } - + cat->key = da_cat_get_max_key() + 1; + da_cat_insert(cat); + return TRUE; } DB( g_print(" -> %s already exist\n", cat->name) ); - - return FALSE; -} - - -/** - * da_cat_max_key_ghfunc: - * - * GHFunc for biggest key - * - */ -static void -da_cat_max_key_ghfunc(gpointer key, Category *cat, guint32 *max_key) -{ - - *max_key = MAX(*max_key, cat->key); -} - -/** - * da_cat_get_max_key: - * - * Get the biggest key from the GHashTable - * - * Return value: the biggest key value - * - */ -guint32 -da_cat_get_max_key(void) -{ -guint32 max_key = 0; - - g_hash_table_foreach(GLOBALS->h_cat, (GHFunc)da_cat_max_key_ghfunc, &max_key); - return max_key; -} - -/** - * da_cat_get_fullname: - * - * Get category the fullname 'xxxx:yyyyy' - * - * Return value: the category fullname (free it with g_free) - * - */ -gchar * -da_cat_get_fullname(Category *cat) -{ -Category *parent; - - if( cat->parent == 0 ) - return g_strdup(cat->name); - else - { - parent = da_cat_get(cat->parent); - if( parent ) - { - return g_strdup_printf("%s:%s", parent->name, cat->name); - } - } - - return NULL; -} - - -/** - * da_cat_name_grfunc: - * - * GRFunc to get the max id - * - * Return value: TRUE if the key/value pair match our name - * - */ -static gboolean -da_cat_name_grfunc(gpointer key, Category *cat, gchar *name) -{ - -// DB( g_print("%s == %s\n", name, cat->name) ); - if( name && cat->name) - { - if(!strcasecmp(name, cat->name)) - return TRUE; - } return FALSE; } -/** - * da_cat_get_key_by_name: - * - * Get a category key by its name - * - * Return value: the category key or -1 if not found - * - */ -guint32 -da_cat_get_key_by_name(gchar *name) -{ -Category *cat; - - DB( g_print("da_cat_get_key_by_name\n") ); - - cat = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_name_grfunc, name); - if( cat == NULL) - return -1; - - return cat->key; -} - -/** - * da_cat_get_by_name: - * - * Get a category structure by its name - * - * Return value: Category * or NULL if not found - * - */ -Category * -da_cat_get_by_name(gchar *name) -{ - DB( g_print("da_cat_get_by_name\n") ); - return g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_name_grfunc, name); -} /* fullname i.e. car:refuel */ struct fullcatcontext { - guint parent; + guint32 parent; gchar *name; }; @@ -346,54 +292,90 @@ da_cat_fullname_grfunc(gpointer key, Category *item, struct fullcatcontext *ctx) //DB( g_print("'%s' == '%s'\n", ctx->name, item->name) ); if( item->parent == ctx->parent ) { - if(!strcasecmp(ctx->name, item->name)) - return TRUE; + if( ctx->name && item->name ) + if(!strcasecmp(ctx->name, item->name)) + return TRUE; } return FALSE; } -Category * -da_cat_get_by_fullname(gchar *fullname) + +static Category *da_cat_get_by_name_find_internal(guint32 parent, gchar *name) { struct fullcatcontext ctx; -gchar **typestr; -Category *item = NULL; - DB( g_print("da_cat_get_by_fullname\n") ); + ctx.parent = parent; + ctx.name = name; + DB( g_print("- searching %s %d '%s'\n", (parent == 0) ? "lv1cat" : "lv2cat", parent, name) ); + return g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx); +} + + +static gchar **da_cat_get_by_fullname_split_clean(gchar *rawfullname, guint *outlen) +{ +gchar **partstr = g_strsplit(rawfullname, ":", 2); +guint len = g_strv_length(partstr); +gboolean valid = TRUE; - typestr = g_strsplit(fullname, ":", 2); - if( g_strv_length(typestr) == 2 ) + DB( g_print("- spliclean '%s' - %d parts\n", rawfullname, g_strv_length(partstr)) ); + + if( outlen != NULL ) + *outlen = len; + + if(len >= 1) { - ctx.parent = 0; - ctx.name = typestr[0]; - DB( g_print(" [x:x] try to find the parent : '%s'\n", typestr[0]) ); + g_strstrip(partstr[0]); + if( strlen(partstr[0]) == 0 ) + valid = FALSE; - Category *parent = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx); - if( parent != NULL ) + if(len == 2) { - ctx.parent = parent->key; - ctx.name = typestr[1]; + g_strstrip(partstr[1]); + if( strlen(partstr[1]) == 0 ) + valid = FALSE; + } + } - DB( g_print(" [x:x] and searching sub %d '%s'\n", ctx.parent, ctx.name) ); + if(valid == TRUE) + return partstr; - item = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx); - } - } - else - { - ctx.parent = 0; - ctx.name = fullname; + DB( g_print("- is invalid\n") ); - DB( g_print(" [x] try to '%s'\n", fullname) ); + g_strfreev(partstr); + return NULL; +} - item = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx); - } - g_strfreev(typestr); +Category * +da_cat_get_by_fullname(gchar *rawfullname) +{ +gchar **partstr; +Category *parent = NULL; +Category *retval = NULL; +guint len; + + DB( g_print("\nda_cat_get_by_fullname\n") ); - DB( g_print(" return value %p\n", item) ); + if( rawfullname ) + { + if( (partstr = da_cat_get_by_fullname_split_clean(rawfullname, &len)) != NULL ) + { + if( len >= 1 ) + { + parent = da_cat_get_by_name_find_internal(0, partstr[0]); + retval = parent; + } + + if( len == 2 && parent != NULL ) + { + retval = da_cat_get_by_name_find_internal(parent->key, partstr[1]); + } - return item; + g_strfreev(partstr); + } + } + + return retval; } @@ -406,118 +388,60 @@ Category *item = NULL; * */ Category * -da_cat_append_ifnew_by_fullname(gchar *fullname, gboolean imported) +da_cat_append_ifnew_by_fullname(gchar *rawfullname) { -struct fullcatcontext ctx; -gchar **typestr; -Category *newcat, *item, *retval = NULL; -guint32 *new_key; - - DB( g_print("da_cat_append_ifnew_by_fullname\n") ); +gchar **partstr; +Category *parent = NULL; +Category *newcat = NULL; +Category *retval = NULL; +guint len; - DB( g_print(" -> fullname: '%s' %d\n", fullname, strlen(fullname)) ); + DB( g_print("\nda_cat_append_ifnew_by_fullname\n") ); - if( strlen(fullname) > 0 ) + if( rawfullname ) { - typestr = g_strsplit(fullname, ":", 2); - - /* if we have a subcategory : aaaa:bbb */ - if( g_strv_length(typestr) == 2 ) + if( (partstr = da_cat_get_by_fullname_split_clean(rawfullname, &len)) != NULL ) { - ctx.parent = 0; - ctx.name = typestr[0]; - DB( g_print(" try to find the parent:'%s'\n", typestr[0]) ); - - Category *parent = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx); - if( parent == NULL ) - { - DB( g_print(" -> not found\n") ); - - // append a new category - new_key = g_new0(guint32, 1); - *new_key = da_cat_get_max_key() + 1; - - newcat = da_cat_malloc(); - newcat->key = *new_key; - newcat->name = g_strdup(typestr[0]); - newcat->imported = imported; - - parent = newcat; - - DB( g_print(" -> insert cat '%s' id: %d\n", newcat->name, newcat->key) ); - - g_hash_table_insert(GLOBALS->h_cat, new_key, newcat); - } - - ctx.parent = parent->key; - ctx.name = typestr[1]; - DB( g_print(" searching %d '%s'\n", ctx.parent, ctx.name) ); - - item = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx); - if( item == NULL ) + if( len >= 1 ) { - // append a new subcategory - new_key = g_new0(guint32, 1); - *new_key = da_cat_get_max_key() + 1; - - newcat = da_cat_malloc(); - newcat->key = *new_key; - newcat->parent = parent->key; - newcat->name = g_strdup(typestr[1]); - newcat->imported = imported; - - newcat->flags |= GF_SUB; - //#1713413 take parent type into account - if(parent->flags & GF_INCOME) - newcat->flags |= GF_INCOME; - - DB( g_print(" -> insert subcat '%s' id: %d\n", newcat->name, newcat->key) ); - - g_hash_table_insert(GLOBALS->h_cat, new_key, newcat); - - retval = newcat; + parent = da_cat_get_by_name_find_internal(0, partstr[0]); + if( parent == NULL ) + { + parent = da_cat_malloc(); + parent->key = da_cat_get_max_key() + 1; + parent->name = g_strdup(partstr[0]); + da_cat_insert(parent); + } + retval = parent; } - else - retval = item; - } - /* this a single category : aaaa */ - else - { - ctx.parent = 0; - ctx.name = typestr[0]; - DB( g_print(" searching %d '%s'\n", ctx.parent, ctx.name) ); - - item = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx); - if( item == NULL ) + + /* if we have a subcategory - xxx:xxx */ + if( len == 2 && parent != NULL ) { - // append a new category - new_key = g_new0(guint32, 1); - *new_key = da_cat_get_max_key() + 1; - - newcat = da_cat_malloc(); - newcat->key = *new_key; - newcat->name = g_strdup(typestr[0]); - newcat->imported = imported; - - DB( g_print(" -> insert cat '%s' id: %d\n", newcat->name, newcat->key) ); - - g_hash_table_insert(GLOBALS->h_cat, new_key, newcat); - + newcat = da_cat_get_by_name_find_internal(parent->key, partstr[1]); + if( newcat == NULL ) + { + newcat = da_cat_malloc(); + newcat->key = da_cat_get_max_key() + 1; + newcat->parent = parent->key; + newcat->name = g_strdup(partstr[1]); + newcat->flags |= GF_SUB; + //#1713413 take parent type into account + if(parent->flags & GF_INCOME) + newcat->flags |= GF_INCOME; + da_cat_insert(newcat); + } retval = newcat; } - else - retval = item; - + + g_strfreev(partstr); } - - g_strfreev(typestr); } return retval; } - /** * da_cat_get: * @@ -535,6 +459,18 @@ da_cat_get(guint32 key) } +gchar *da_cat_get_name(Category *item) +{ +gchar *name = NULL; + + if(item != NULL) + { + name = item->key == 0 ? _("(no category)") : item->fullname; + } + return name; +} + + void da_cat_consistency(Category *item) { gboolean isIncome; @@ -544,10 +480,10 @@ gboolean isIncome; //check for existing parent if( da_cat_get(item->parent) == NULL ) { - Category *parent = da_cat_append_ifnew_by_fullname ("orphaned", FALSE); + Category *parent = da_cat_append_ifnew_by_fullname ("orphaned"); item->parent = parent->key; - + da_cat_build_fullname(item); g_warning("category consistency: fixed missing parent %d", item->parent); } } @@ -562,8 +498,17 @@ gboolean isIncome; GLOBALS->changes_count++; } } + + if( item->name != NULL ) + g_strstrip(item->name); + else + { + item->name = g_strdup("void"); + da_cat_build_fullname(item); + g_warning("category consistency: fixed null name"); + GLOBALS->changes_count++; + } - g_strstrip(item->name); } @@ -600,22 +545,22 @@ da_cat_debug_list(void) /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ + guint32 category_report_id(guint32 key, gboolean subcat) { -Category *catentry = da_cat_get(key); guint32 retval = 0; - if(catentry) + if(subcat == FALSE) { - if(subcat == FALSE) - { + Category *catentry = da_cat_get(key); + if(catentry) retval = (catentry->flags & GF_SUB) ? catentry->parent : catentry->key; - } - else - { - retval = catentry->key; - } } + else + { + retval = key; + } + //DB( g_print("- cat '%s' reportid = %d\n", catentry->name, retval) ); return retval; } @@ -693,10 +638,10 @@ guint i, nbsplit; //#1689308 count split as well if( txn->flags & OF_SPLIT ) { - nbsplit = da_splits_count(txn->splits); + nbsplit = da_splits_length(txn->splits); for(i=0;isplits[i]; + Split *split = da_splits_get(txn->splits, i); category_fill_usage_count(split->kcat); } @@ -729,10 +674,10 @@ guint i, nbsplit; //#1689308 count split as well if( entry->flags & OF_SPLIT ) { - nbsplit = da_splits_count(entry->splits); + nbsplit = da_splits_length(entry->splits); for(i=0;isplits[i]; + Split *split = da_splits_get(entry->splits, i); category_fill_usage_count(split->kcat); } @@ -783,10 +728,10 @@ guint i, nbsplit; } // move split category #1340142 - nbsplit = da_splits_count(txn->splits); + nbsplit = da_splits_length(txn->splits); for(i=0;isplits[i]; + Split *split = da_splits_get(txn->splits, i); if( split->kcat == key1 ) { @@ -838,7 +783,7 @@ gchar *fullname = NULL; gchar *stripname; gboolean retval; - DB( g_print("(category) rename\n") ); + DB( g_print("\n(category) rename\n") ); stripname = g_strdup(newname); g_strstrip(stripname); @@ -860,15 +805,14 @@ gboolean retval; if( existitem != NULL && existitem->key != item->key) { - DB( g_print("error, same name already exist with other key %d <> %d\n",existitem->key, item->key) ); + DB( g_print("- error, same name already exist with other key %d <> %d\n",existitem->key, item->key) ); retval = FALSE; } else { - DB( g_print(" -renaming\n") ); + DB( g_print("- renaming\n") ); - g_free(item->name); - item->name = g_strdup(stripname); + da_cat_rename (item, stripname); retval = TRUE; } @@ -879,26 +823,21 @@ gboolean retval; } -static gint category_glist_name_compare_func(Category *c1, Category *c2) +static gint +category_glist_name_compare_func(Category *c1, Category *c2) { -gchar *name1, *name2; gint retval = 0; if( c1 != NULL && c2 != NULL ) { - name1 = da_cat_get_fullname(c1); - name2 = da_cat_get_fullname(c2); - - retval = hb_string_utf8_compare(name1, name2); - - g_free(name2); - g_free(name1); + retval = hb_string_utf8_compare(c1->fullname, c2->fullname); } return retval; } -static gint category_glist_key_compare_func(Category *a, Category *b) +static gint +category_glist_key_compare_func(Category *a, Category *b) { gint ka, kb, retval = 0; @@ -934,7 +873,8 @@ gint ka, kb, retval = 0; } -GList *category_glist_sorted(gint column) +GList * +category_glist_sorted(gint column) { GList *list = g_hash_table_get_values(GLOBALS->h_cat); @@ -961,16 +901,13 @@ gint type = 0; const gchar *encoding; encoding = homebank_file_getencoding(filename); - - DB( g_print(" -> encoding should be %s\n", encoding) ); - + DB( g_print(" -> encoding should be %s\n", encoding) ); retval = TRUE; *error = NULL; io = g_io_channel_new_file(filename, "r", NULL); if(io != NULL) { - if( encoding != NULL ) { g_io_channel_set_encoding(io, encoding, NULL); @@ -1030,10 +967,7 @@ const gchar *encoding; fullcatname = g_strdup_printf("%s:%s", lastcatname, str_array[2]); } - DB( g_print(" + fullcatname %s\n", fullcatname) ); - - item = da_cat_append_ifnew_by_fullname(fullcatname, FALSE); - + item = da_cat_append_ifnew_by_fullname(fullcatname); DB( g_print(" + item %p\n", item) ); if( item != NULL) @@ -1048,14 +982,10 @@ const gchar *encoding; g_strfreev (str_array); } } - } g_free(tmpstr); - } g_io_channel_unref (io); - - } g_free(lastcatname); @@ -1064,7 +994,6 @@ const gchar *encoding; } - gboolean category_save_csv(gchar *filename, gchar **error) { @@ -1073,7 +1002,6 @@ GIOChannel *io; gchar *outstr; GList *lcat, *list; - io = g_io_channel_new_file(filename, "w", NULL); if(io != NULL) { @@ -1116,11 +1044,12 @@ GList *lcat, *list; g_io_channel_unref (io); } - return retval; } -gint category_type_get(Category *item) + +gint +category_type_get(Category *item) { if( (item->flags & (GF_INCOME)) ) return 1; @@ -1128,8 +1057,15 @@ gint category_type_get(Category *item) } +gchar +category_get_type_char(Category *item) +{ + return (item->flags & GF_INCOME) ? '+' : '-'; +} -static gint category_change_type_eval(Category *item, gboolean isIncome) + +static gint +category_change_type_eval(Category *item, gboolean isIncome) { if( (item->flags & (GF_INCOME)) && !isIncome ) return 1; @@ -1137,7 +1073,8 @@ static gint category_change_type_eval(Category *item, gboolean isIncome) } -gint category_change_type(Category *item, gboolean isIncome) +gint +category_change_type(Category *item, gboolean isIncome) { gint changes = 0; GList *lcat, *list; @@ -1170,9 +1107,6 @@ GList *lcat, *list; } - - - /** * category_find_preset: * @@ -1181,7 +1115,8 @@ GList *lcat, *list; * Return value: a pathname to the file or NULL * */ -gchar *category_find_preset(gchar **lang) +gchar * +category_find_preset(gchar **lang) { gchar **langs; gchar *filename; @@ -1217,4 +1152,3 @@ guint i; return NULL; } -