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