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