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