]> Dogcows Code - chaz/homebank/blob - src/hb-category.c
Merge branch 'upstream'
[chaz/homebank] / src / hb-category.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 Maxime DOYEN
3 *
4 * This file is part of HomeBank.
5 *
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * HomeBank is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "homebank.h"
21 #include "hb-category.h"
22
23
24 /****************************************************************************/
25 /* Debug macros */
26 /****************************************************************************/
27 #define MYDEBUG 0
28
29 #if MYDEBUG
30 #define DB(x) (x);
31 #else
32 #define DB(x);
33 #endif
34
35 /* our global datas */
36 extern struct HomeBank *GLOBALS;
37
38 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
39
40 Category *
41 da_cat_clone(Category *src_item)
42 {
43 Category *new_item = g_memdup(src_item, sizeof(Category));
44
45 DB( g_print("da_cat_clone\n") );
46 if(new_item)
47 {
48 //duplicate the string
49 new_item->name = g_strdup(src_item->name);
50 new_item->fullname = g_strdup(src_item->fullname);
51 }
52 return new_item;
53 }
54
55
56 void
57 da_cat_free(Category *item)
58 {
59 DB( g_print("da_cat_free\n") );
60 if(item != NULL)
61 {
62 DB( g_print(" => %d, %s\n", item->key, item->name) );
63
64 g_free(item->name);
65 g_free(item->fullname);
66 g_free(item);
67 }
68 }
69
70
71 Category *
72 da_cat_malloc(void)
73 {
74 DB( g_print("da_cat_malloc\n") );
75 return g_malloc0(sizeof(Category));
76 }
77
78
79 void
80 da_cat_destroy(void)
81 {
82 DB( g_print("da_cat_destroy\n") );
83 g_hash_table_destroy(GLOBALS->h_cat);
84 }
85
86
87 void
88 da_cat_new(void)
89 {
90 Category *item;
91
92 DB( g_print("da_cat_new\n") );
93 GLOBALS->h_cat = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_cat_free);
94
95 // insert our 'no category'
96 item = da_cat_malloc();
97 item->key = 0;
98 item->name = g_strdup("");
99 item->fullname = g_strdup("");
100 da_cat_insert(item);
101 }
102
103
104 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
105
106 /**
107 * da_cat_length:
108 *
109 * Return value: the number of elements
110 */
111 guint
112 da_cat_length(void)
113 {
114 return g_hash_table_size(GLOBALS->h_cat);
115 }
116
117
118 static void
119 da_cat_max_key_ghfunc(gpointer key, Category *cat, guint32 *max_key)
120 {
121 *max_key = MAX(*max_key, cat->key);
122 }
123
124 /**
125 * da_cat_get_max_key:
126 *
127 * Get the biggest key from the GHashTable
128 *
129 * Return value: the biggest key value
130 *
131 */
132 guint32
133 da_cat_get_max_key(void)
134 {
135 guint32 max_key = 0;
136
137 g_hash_table_foreach(GLOBALS->h_cat, (GHFunc)da_cat_max_key_ghfunc, &max_key);
138 return max_key;
139 }
140
141
142 static gboolean
143 da_cat_remove_grfunc(gpointer key, Category *cat, guint32 *remkey)
144 {
145 if(cat->key == *remkey || cat->parent == *remkey)
146 return TRUE;
147
148 return FALSE;
149 }
150
151
152 /**
153 * da_cat_remove:
154 *
155 * delete a category from the GHashTable
156 *
157 * Return value: TRUE if the key was found and deleted
158 *
159 */
160 guint
161 da_cat_remove(guint32 key)
162 {
163 DB( g_print("\nda_cat_remove %d\n", key) );
164
165 return g_hash_table_foreach_remove(GLOBALS->h_cat, (GHRFunc)da_cat_remove_grfunc, &key);
166 }
167
168
169 static void
170 da_cat_build_fullname(Category *item)
171 {
172 Category *parent;
173
174 g_free(item->fullname);
175 if( item->parent == 0 )
176 item->fullname = g_strdup(item->name);
177 else
178 {
179 parent = da_cat_get(item->parent);
180 if( parent != NULL )
181 item->fullname = g_strconcat(parent->name, ":", item->name, NULL);
182 }
183
184 DB( g_print("- updated %d:'%s' fullname='%s'\n", item->key, item->name, item->fullname) );
185
186 }
187
188
189 static void
190 da_cat_rename(Category *item, gchar *newname)
191 {
192
193 DB( g_print("- renaming %s' => '%s'\n", item->name, newname) );
194
195 g_free(item->name);
196 item->name = g_strdup(newname);
197 da_cat_build_fullname(item);
198
199 if( item->parent == 0 )
200 {
201 GHashTableIter iter;
202 gpointer value;
203
204 DB( g_print("- updating subcat fullname\n") );
205
206 g_hash_table_iter_init (&iter, GLOBALS->h_cat);
207 while (g_hash_table_iter_next (&iter, NULL, &value))
208 {
209 Category *subcat = value;
210
211 if( subcat->parent == item->key )
212 da_cat_build_fullname(subcat);
213 }
214
215 }
216 }
217
218
219 /**
220 * da_cat_insert:
221 *
222 * insert a category into the GHashTable
223 *
224 * Return value: TRUE if inserted
225 *
226 */
227 gboolean
228 da_cat_insert(Category *item)
229 {
230 guint32 *new_key;
231
232 DB( g_print("\nda_cat_insert\n") );
233
234 DB( g_print("- '%s'\n", item->name) );
235
236 new_key = g_new0(guint32, 1);
237 *new_key = item->key;
238 g_hash_table_insert(GLOBALS->h_cat, new_key, item);
239
240 da_cat_build_fullname(item);
241
242 return TRUE;
243 }
244
245
246 /**
247 * da_cat_append:
248 *
249 * append a category into the GHashTable
250 *
251 * Return value: TRUE if inserted
252 *
253 */
254 // used only to add cat/subcat from ui_category with the 2 inputs
255 gboolean
256 da_cat_append(Category *cat)
257 {
258 Category *existitem;
259
260 DB( g_print("\nda_cat_append\n") );
261
262 if( !cat->fullname )
263 da_cat_build_fullname(cat);
264
265 existitem = da_cat_get_by_fullname( cat->fullname );
266 if( existitem == NULL )
267 {
268 cat->key = da_cat_get_max_key() + 1;
269 da_cat_insert(cat);
270 return TRUE;
271 }
272
273 DB( g_print(" -> %s already exist\n", cat->name) );
274 return FALSE;
275 }
276
277
278
279
280 /* fullname i.e. car:refuel */
281 struct fullcatcontext
282 {
283 guint32 parent;
284 gchar *name;
285 };
286
287
288 static gboolean
289 da_cat_fullname_grfunc(gpointer key, Category *item, struct fullcatcontext *ctx)
290 {
291
292 //DB( g_print("'%s' == '%s'\n", ctx->name, item->name) );
293 if( item->parent == ctx->parent )
294 {
295 if( ctx->name && item->name )
296 if(!strcasecmp(ctx->name, item->name))
297 return TRUE;
298 }
299 return FALSE;
300 }
301
302
303 static Category *da_cat_get_by_name_find_internal(guint32 parent, gchar *name)
304 {
305 struct fullcatcontext ctx;
306
307 ctx.parent = parent;
308 ctx.name = name;
309 DB( g_print("- searching %s %d '%s'\n", (parent == 0) ? "lv1cat" : "lv2cat", parent, name) );
310 return g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx);
311 }
312
313
314 static gchar **da_cat_get_by_fullname_split_clean(gchar *rawfullname, guint *outlen)
315 {
316 gchar **partstr = g_strsplit(rawfullname, ":", 2);
317 guint len = g_strv_length(partstr);
318 gboolean valid = TRUE;
319
320 DB( g_print("- spliclean '%s' - %d parts\n", rawfullname, g_strv_length(partstr)) );
321
322 if( outlen != NULL )
323 *outlen = len;
324
325 if(len >= 1)
326 {
327 g_strstrip(partstr[0]);
328 if( strlen(partstr[0]) == 0 )
329 valid = FALSE;
330
331 if(len == 2)
332 {
333 g_strstrip(partstr[1]);
334 if( strlen(partstr[1]) == 0 )
335 valid = FALSE;
336 }
337 }
338
339 if(valid == TRUE)
340 return partstr;
341
342 DB( g_print("- is invalid\n") );
343
344 g_strfreev(partstr);
345 return NULL;
346 }
347
348
349 Category *
350 da_cat_get_by_fullname(gchar *rawfullname)
351 {
352 gchar **partstr;
353 Category *parent = NULL;
354 Category *retval = NULL;
355 guint len;
356
357 DB( g_print("\nda_cat_get_by_fullname\n") );
358
359 if( rawfullname )
360 {
361 if( (partstr = da_cat_get_by_fullname_split_clean(rawfullname, &len)) != NULL )
362 {
363 if( len >= 1 )
364 {
365 parent = da_cat_get_by_name_find_internal(0, partstr[0]);
366 retval = parent;
367 }
368
369 if( len == 2 && parent != NULL )
370 {
371 retval = da_cat_get_by_name_find_internal(parent->key, partstr[1]);
372 }
373
374 g_strfreev(partstr);
375 }
376 }
377
378 return retval;
379 }
380
381
382 /**
383 * da_cat_append_ifnew_by_fullname:
384 *
385 * append a category if it is new by fullname
386 *
387 * Return value:
388 *
389 */
390 Category *
391 da_cat_append_ifnew_by_fullname(gchar *rawfullname)
392 {
393 gchar **partstr;
394 Category *parent = NULL;
395 Category *newcat = NULL;
396 Category *retval = NULL;
397 guint len;
398
399 DB( g_print("\nda_cat_append_ifnew_by_fullname\n") );
400
401 if( rawfullname )
402 {
403 if( (partstr = da_cat_get_by_fullname_split_clean(rawfullname, &len)) != NULL )
404 {
405 if( len >= 1 )
406 {
407 parent = da_cat_get_by_name_find_internal(0, partstr[0]);
408 if( parent == NULL )
409 {
410 parent = da_cat_malloc();
411 parent->key = da_cat_get_max_key() + 1;
412 parent->name = g_strdup(partstr[0]);
413 da_cat_insert(parent);
414 }
415 retval = parent;
416 }
417
418 /* if we have a subcategory - xxx:xxx */
419 if( len == 2 && parent != NULL )
420 {
421 newcat = da_cat_get_by_name_find_internal(parent->key, partstr[1]);
422 if( newcat == NULL )
423 {
424 newcat = da_cat_malloc();
425 newcat->key = da_cat_get_max_key() + 1;
426 newcat->parent = parent->key;
427 newcat->name = g_strdup(partstr[1]);
428 newcat->flags |= GF_SUB;
429 //#1713413 take parent type into account
430 if(parent->flags & GF_INCOME)
431 newcat->flags |= GF_INCOME;
432 da_cat_insert(newcat);
433 }
434 retval = newcat;
435 }
436
437 g_strfreev(partstr);
438 }
439 }
440
441 return retval;
442 }
443
444
445 /**
446 * da_cat_get:
447 *
448 * Get a category structure by key
449 *
450 * Return value: Category * or NULL if not found
451 *
452 */
453 Category *
454 da_cat_get(guint32 key)
455 {
456 //DB( g_print("da_cat_get\n") );
457
458 return g_hash_table_lookup(GLOBALS->h_cat, &key);
459 }
460
461
462 gchar *da_cat_get_name(Category *item)
463 {
464 gchar *name = NULL;
465
466 if(item != NULL)
467 {
468 name = item->key == 0 ? _("(no category)") : item->fullname;
469 }
470 return name;
471 }
472
473
474 void da_cat_consistency(Category *item)
475 {
476 gboolean isIncome;
477
478 if((item->flags & GF_SUB) && item->key > 0)
479 {
480 //check for existing parent
481 if( da_cat_get(item->parent) == NULL )
482 {
483 Category *parent = da_cat_append_ifnew_by_fullname ("orphaned");
484
485 item->parent = parent->key;
486 da_cat_build_fullname(item);
487 g_warning("category consistency: fixed missing parent %d", item->parent);
488 }
489 }
490
491 // ensure type equal for categories and its children
492 if(!(item->flags & GF_SUB) && item->key > 0)
493 {
494 isIncome = (item->flags & GF_INCOME) ? TRUE : FALSE;
495 if( category_change_type(item, isIncome) > 0 )
496 {
497 g_warning("category consistency: fixed type for child");
498 GLOBALS->changes_count++;
499 }
500 }
501
502 if( item->name != NULL )
503 g_strstrip(item->name);
504 else
505 {
506 item->name = g_strdup("void");
507 da_cat_build_fullname(item);
508 g_warning("category consistency: fixed null name");
509 GLOBALS->changes_count++;
510 }
511
512 }
513
514
515
516 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
517
518 #if MYDEBUG
519
520 static void
521 da_cat_debug_list_ghfunc(gpointer key, gpointer value, gpointer user_data)
522 {
523 guint32 *id = key;
524 Category *cat = value;
525
526 DB( g_print(" %d :: %s (parent=%d\n", *id, cat->name, cat->parent) );
527
528 }
529
530 static void
531 da_cat_debug_list(void)
532 {
533
534 DB( g_print("\n** debug **\n") );
535
536 g_hash_table_foreach(GLOBALS->h_cat, da_cat_debug_list_ghfunc, NULL);
537
538 DB( g_print("\n** end debug **\n") );
539
540 }
541
542 #endif
543
544
545
546 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
547
548
549 guint32 category_report_id(guint32 key, gboolean subcat)
550 {
551 guint32 retval = 0;
552
553 if(subcat == FALSE)
554 {
555 Category *catentry = da_cat_get(key);
556 if(catentry)
557 retval = (catentry->flags & GF_SUB) ? catentry->parent : catentry->key;
558 }
559 else
560 {
561 retval = key;
562 }
563 //DB( g_print("- cat '%s' reportid = %d\n", catentry->name, retval) );
564 return retval;
565 }
566
567
568 void
569 category_delete_unused(void)
570 {
571 GList *lcat, *list;
572
573 lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
574 while (list != NULL)
575 {
576 Category *entry = list->data;
577
578 if(entry->usage_count <= 0 && entry->key > 0)
579 da_cat_remove (entry->key);
580
581 list = g_list_next(list);
582 }
583 g_list_free(lcat);
584 }
585
586
587 static void
588 category_fill_usage_count(guint32 kcat)
589 {
590 Category *cat = da_cat_get (kcat);
591 Category *parent;
592
593 if(cat)
594 {
595 cat->usage_count++;
596 if( cat->parent > 0 )
597 {
598 parent = da_cat_get(cat->parent);
599 if( parent )
600 {
601 parent->usage_count++;
602 }
603 }
604 }
605 }
606
607
608 void
609 category_fill_usage(void)
610 {
611 GList *lcat;
612 GList *lst_acc, *lnk_acc;
613 GList *lnk_txn;
614 GList *lpay, *lrul, *list;
615 guint i, nbsplit;
616
617 lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
618 while (list != NULL)
619 {
620 Category *entry = list->data;
621 entry->usage_count = 0;
622 list = g_list_next(list);
623 }
624 g_list_free(lcat);
625
626
627 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
628 lnk_acc = g_list_first(lst_acc);
629 while (lnk_acc != NULL)
630 {
631 Account *acc = lnk_acc->data;
632
633 lnk_txn = g_queue_peek_head_link(acc->txn_queue);
634 while (lnk_txn != NULL)
635 {
636 Transaction *txn = lnk_txn->data;
637
638 //#1689308 count split as well
639 if( txn->flags & OF_SPLIT )
640 {
641 nbsplit = da_splits_length(txn->splits);
642 for(i=0;i<nbsplit;i++)
643 {
644 Split *split = da_splits_get(txn->splits, i);
645
646 category_fill_usage_count(split->kcat);
647 }
648 }
649 else
650 category_fill_usage_count(txn->kcat);
651
652 lnk_txn = g_list_next(lnk_txn);
653 }
654 lnk_acc = g_list_next(lnk_acc);
655 }
656 g_list_free(lst_acc);
657
658 lpay = list = g_hash_table_get_values(GLOBALS->h_pay);
659 while (list != NULL)
660 {
661 Payee *entry = list->data;
662
663 category_fill_usage_count(entry->kcat);
664 list = g_list_next(list);
665 }
666 g_list_free(lpay);
667
668
669 list = g_list_first(GLOBALS->arc_list);
670 while (list != NULL)
671 {
672 Archive *entry = list->data;
673
674 //#1689308 count split as well
675 if( entry->flags & OF_SPLIT )
676 {
677 nbsplit = da_splits_length(entry->splits);
678 for(i=0;i<nbsplit;i++)
679 {
680 Split *split = da_splits_get(entry->splits, i);
681
682 category_fill_usage_count(split->kcat);
683 }
684 }
685 else
686 category_fill_usage_count(entry->kcat);
687
688 list = g_list_next(list);
689 }
690
691
692 lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
693 while (list != NULL)
694 {
695 Assign *entry = list->data;
696
697 category_fill_usage_count(entry->kcat);
698 list = g_list_next(list);
699 }
700 g_list_free(lrul);
701
702 }
703
704
705 void
706 category_move(guint32 key1, guint32 key2)
707 {
708 GList *lst_acc, *lnk_acc;
709 GList *lnk_txn;
710 GList *lrul, *list;
711 guint i, nbsplit;
712
713 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
714 lnk_acc = g_list_first(lst_acc);
715 while (lnk_acc != NULL)
716 {
717 Account *acc = lnk_acc->data;
718
719 lnk_txn = g_queue_peek_head_link(acc->txn_queue);
720 while (lnk_txn != NULL)
721 {
722 Transaction *txn = lnk_txn->data;
723
724 if(txn->kcat == key1)
725 {
726 txn->kcat = key2;
727 txn->flags |= OF_CHANGED;
728 }
729
730 // move split category #1340142
731 nbsplit = da_splits_length(txn->splits);
732 for(i=0;i<nbsplit;i++)
733 {
734 Split *split = da_splits_get(txn->splits, i);
735
736 if( split->kcat == key1 )
737 {
738 split->kcat = key2;
739 txn->flags |= OF_CHANGED;
740 }
741 }
742
743 lnk_txn = g_list_next(lnk_txn);
744 }
745
746 lnk_acc = g_list_next(lnk_acc);
747 }
748 g_list_free(lst_acc);
749
750
751 list = g_list_first(GLOBALS->arc_list);
752 while (list != NULL)
753 {
754 Archive *entry = list->data;
755 if(entry->kcat == key1)
756 {
757 entry->kcat = key2;
758 }
759 list = g_list_next(list);
760 }
761
762 lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
763 while (list != NULL)
764 {
765 Assign *entry = list->data;
766
767 if(entry->kcat == key1)
768 {
769 entry->kcat = key2;
770 }
771 list = g_list_next(list);
772 }
773 g_list_free(lrul);
774
775 }
776
777
778 gboolean
779 category_rename(Category *item, const gchar *newname)
780 {
781 Category *parent, *existitem;
782 gchar *fullname = NULL;
783 gchar *stripname;
784 gboolean retval;
785
786 DB( g_print("\n(category) rename\n") );
787
788 stripname = g_strdup(newname);
789 g_strstrip(stripname);
790
791 if( item->parent == 0)
792 fullname = g_strdup(stripname);
793 else
794 {
795 parent = da_cat_get(item->parent);
796 if( parent )
797 {
798 fullname = g_strdup_printf("%s:%s", parent->name, stripname);
799 }
800 }
801
802 DB( g_print(" - search: %s\n", fullname) );
803
804 existitem = da_cat_get_by_fullname( fullname );
805
806 if( existitem != NULL && existitem->key != item->key)
807 {
808 DB( g_print("- error, same name already exist with other key %d <> %d\n",existitem->key, item->key) );
809 retval = FALSE;
810 }
811 else
812 {
813 DB( g_print("- renaming\n") );
814
815 da_cat_rename (item, stripname);
816 retval = TRUE;
817 }
818
819 g_free(fullname);
820 g_free(stripname);
821
822 return retval;
823 }
824
825
826 static gint
827 category_glist_name_compare_func(Category *c1, Category *c2)
828 {
829 gint retval = 0;
830
831 if( c1 != NULL && c2 != NULL )
832 {
833 retval = hb_string_utf8_compare(c1->fullname, c2->fullname);
834 }
835 return retval;
836 }
837
838
839 static gint
840 category_glist_key_compare_func(Category *a, Category *b)
841 {
842 gint ka, kb, retval = 0;
843
844 if(a->parent == 0 && b->parent == a->key)
845 retval = -1;
846 else
847 if(b->parent == 0 && a->parent == b->key)
848 retval = 1;
849 else
850 {
851 ka = a->parent != 0 ? a->parent : a->key;
852 kb = b->parent != 0 ? b->parent : b->key;
853 retval = ka - kb;
854 }
855
856
857 #if MYDEBUG == 1
858 gchar *str;
859
860 if(retval < 0)
861 str = "a < b";
862 else
863 if(retval ==0)
864 str = "a = b";
865 else
866 if(retval > 0)
867 str = "a > b";
868
869 DB( g_print("compare a=%2d:%2d to b=%2d:%2d :: %d [%s]\n", a->key, a->parent, b->key, b->parent, retval, str ) );
870 #endif
871
872 return retval;
873 }
874
875
876 GList *
877 category_glist_sorted(gint column)
878 {
879 GList *list = g_hash_table_get_values(GLOBALS->h_cat);
880
881 if(column == 0)
882 return g_list_sort(list, (GCompareFunc)category_glist_key_compare_func);
883 else
884 return g_list_sort(list, (GCompareFunc)category_glist_name_compare_func);
885 }
886
887
888 gboolean
889 category_load_csv(gchar *filename, gchar **error)
890 {
891 gboolean retval;
892 GIOChannel *io;
893 gchar *tmpstr;
894 gint io_stat;
895 gchar **str_array;
896 gchar *lastcatname = NULL;
897 gchar *fullcatname;
898 GError *err = NULL;
899 Category *item;
900 gint type = 0;
901 const gchar *encoding;
902
903 encoding = homebank_file_getencoding(filename);
904 DB( g_print(" -> encoding should be %s\n", encoding) );
905
906 retval = TRUE;
907 *error = NULL;
908 io = g_io_channel_new_file(filename, "r", NULL);
909 if(io != NULL)
910 {
911 if( encoding != NULL )
912 {
913 g_io_channel_set_encoding(io, encoding, NULL);
914 }
915
916 for(;;)
917 {
918 if( *error != NULL )
919 break;
920 io_stat = g_io_channel_read_line(io, &tmpstr, NULL, NULL, &err);
921
922 DB( g_print(" + iostat %d\n", io_stat) );
923
924 if( io_stat == G_IO_STATUS_ERROR )
925 {
926 DB (g_print(" + ERROR %s\n",err->message));
927 break;
928 }
929 if( io_stat == G_IO_STATUS_EOF)
930 break;
931 if( io_stat == G_IO_STATUS_NORMAL)
932 {
933 if( tmpstr != NULL )
934 {
935 DB( g_print(" + strip %s\n", tmpstr) );
936 hb_string_strip_crlf(tmpstr);
937
938 DB( g_print(" + split\n") );
939 str_array = g_strsplit (tmpstr, ";", 3);
940 // type; sign; name
941
942 if( g_strv_length (str_array) != 3 )
943 {
944 *error = _("invalid CSV format");
945 retval = FALSE;
946 DB( g_print(" + error %s\n", *error) );
947 }
948 else
949 {
950 DB( g_print(" + read %s : %s : %s\n", str_array[0], str_array[1], str_array[2]) );
951
952 fullcatname = NULL;
953 if( g_str_has_prefix(str_array[0], "1") )
954 {
955 fullcatname = g_strdup(str_array[2]);
956 g_free(lastcatname);
957 lastcatname = g_strdup(str_array[2]);
958
959 type = g_str_has_prefix(str_array[1], "+") ? GF_INCOME : 0;
960
961 DB( g_print(" + type = %d\n", type) );
962
963 }
964 else
965 if( g_str_has_prefix(str_array[0], "2") )
966 {
967 fullcatname = g_strdup_printf("%s:%s", lastcatname, str_array[2]);
968 }
969
970 item = da_cat_append_ifnew_by_fullname(fullcatname);
971 DB( g_print(" + item %p\n", item) );
972
973 if( item != NULL)
974 {
975 DB( g_print(" + assign flags: '%c'\n", type) );
976
977 item->flags |= type;
978
979 }
980
981 g_free(fullcatname);
982 g_strfreev (str_array);
983 }
984 }
985 }
986 g_free(tmpstr);
987 }
988 g_io_channel_unref (io);
989 }
990
991 g_free(lastcatname);
992
993 return retval;
994 }
995
996
997 gboolean
998 category_save_csv(gchar *filename, gchar **error)
999 {
1000 gboolean retval = FALSE;
1001 GIOChannel *io;
1002 gchar *outstr;
1003 GList *lcat, *list;
1004
1005 io = g_io_channel_new_file(filename, "w", NULL);
1006 if(io != NULL)
1007 {
1008 lcat = list = category_glist_sorted(1);
1009
1010 while (list != NULL)
1011 {
1012 Category *item = list->data;
1013
1014 if(item->key != 0)
1015 {
1016 gchar lvel, type;
1017
1018 if( item->parent == 0)
1019 {
1020 lvel = '1';
1021 type = (item->flags & GF_INCOME) ? '+' : '-';
1022 }
1023 else
1024 {
1025 lvel = '2';
1026 type = ' ';
1027 }
1028
1029 outstr = g_strdup_printf("%c;%c;%s\n", lvel, type, item->name);
1030
1031 DB( g_print(" + export %s\n", outstr) );
1032
1033 g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
1034
1035 g_free(outstr);
1036 }
1037 list = g_list_next(list);
1038 }
1039
1040 retval = TRUE;
1041
1042 g_list_free(lcat);
1043
1044 g_io_channel_unref (io);
1045 }
1046
1047 return retval;
1048 }
1049
1050
1051 gint
1052 category_type_get(Category *item)
1053 {
1054 if( (item->flags & (GF_INCOME)) )
1055 return 1;
1056 return -1;
1057 }
1058
1059
1060 gchar
1061 category_get_type_char(Category *item)
1062 {
1063 return (item->flags & GF_INCOME) ? '+' : '-';
1064 }
1065
1066
1067 static gint
1068 category_change_type_eval(Category *item, gboolean isIncome)
1069 {
1070 if( (item->flags & (GF_INCOME)) && !isIncome )
1071 return 1;
1072 return 0;
1073 }
1074
1075
1076 gint
1077 category_change_type(Category *item, gboolean isIncome)
1078 {
1079 gint changes = 0;
1080 GList *lcat, *list;
1081
1082 changes += category_change_type_eval(item, isIncome);
1083
1084 item->flags &= ~(GF_INCOME); //delete flag
1085 if(isIncome == TRUE)
1086 item->flags |= GF_INCOME;
1087
1088 // change also childs
1089 lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
1090 while (list != NULL)
1091 {
1092 Category *child = list->data;
1093
1094 if(child->parent == item->key)
1095 {
1096 changes += category_change_type_eval(child, isIncome);
1097 child->flags &= ~(GF_INCOME); //delete flag
1098 if(isIncome == TRUE)
1099 child->flags |= GF_INCOME;
1100 }
1101 list = g_list_next(list);
1102 }
1103
1104 g_list_free(lcat);
1105
1106 return changes;
1107 }
1108
1109
1110 /**
1111 * category_find_preset:
1112 *
1113 * find a user language compatible file for category preset
1114 *
1115 * Return value: a pathname to the file or NULL
1116 *
1117 */
1118 gchar *
1119 category_find_preset(gchar **lang)
1120 {
1121 gchar **langs;
1122 gchar *filename;
1123 gboolean exists;
1124 guint i;
1125
1126 DB( g_print("** category_find_preset **\n") );
1127
1128 langs = (gchar **)g_get_language_names ();
1129
1130 DB( g_print(" -> %d languages detected\n", g_strv_length(langs)) );
1131
1132 for(i=0;i<g_strv_length(langs);i++)
1133 {
1134 DB( g_print(" -> %d '%s'\n", i, langs[i]) );
1135 filename = g_strdup_printf("hb-categories-%s.csv", langs[i]);
1136 gchar *pathfilename = g_build_filename(homebank_app_get_datas_dir(), filename, NULL);
1137 exists = g_file_test(pathfilename, G_FILE_TEST_EXISTS);
1138 DB( g_print(" -> '%s' exists=%d\n", pathfilename, exists) );
1139 if(exists)
1140 {
1141 g_free(filename);
1142 *lang = langs[i];
1143 return pathfilename;
1144 }
1145 g_free(filename);
1146 g_free(pathfilename);
1147 }
1148
1149 DB( g_print("return NULL\n") );
1150
1151 *lang = NULL;
1152 return NULL;
1153 }
1154
This page took 0.088828 seconds and 5 git commands to generate.