]> Dogcows Code - chaz/homebank/blob - src/ui-budget.c
56ddbd4b14adb0e85b5829dc1a5b625d9b73467c
[chaz/homebank] / src / ui-budget.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
21 #include "homebank.h"
22
23 #include "ui-category.h"
24 #include "ui-budget.h"
25
26 extern gchar *CYA_CAT_TYPE[];
27
28 /****************************************************************************/
29 /* Debug macros */
30 /****************************************************************************/
31 #define MYDEBUG 0
32
33 #if MYDEBUG
34 #define DB(x) (x);
35 #else
36 #define DB(x);
37 #endif
38
39 /* our global datas */
40 extern struct HomeBank *GLOBALS;
41 extern struct Preferences *PREFS;
42
43
44
45
46 gchar *months[] = {
47 "Jan",
48 "Feb",
49 "Mar",
50 "Apr",
51 "May",
52 "Jun",
53 "Jul",
54 "Aug",
55 "Sep",
56 "Oct",
57 "Nov",
58 "Dec"
59 };
60
61 static gchar *ui_bud_manage_getcsvbudgetstr(Category *item);
62 static void ui_bud_manage_update(GtkWidget *treeview, gpointer user_data);
63 static void ui_bud_manage_set(GtkWidget *widget, gpointer user_data);
64 static void ui_bud_manage_getlast(struct ui_bud_manage_data *data);
65 static void ui_bud_manage_selection_change(GtkWidget *treeview, gpointer user_data);
66 static void ui_bud_manage_toggle(GtkRadioButton *radiobutton, gpointer user_data);
67 static void ui_bud_manage_selection(GtkTreeSelection *treeselection, gpointer user_data);
68
69
70 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
71
72 /*
73 **
74 ** The function should return:
75 ** a negative integer if the first value comes before the second,
76 ** 0 if they are equal,
77 ** or a positive integer if the first value comes after the second.
78 */
79 static gint ui_bud_listview_compare_funct (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
80 {
81 gint retval = 0;
82 Category *entry1, *entry2;
83
84 gtk_tree_model_get(model, a, LST_DEFCAT_DATAS, &entry1, -1);
85 gtk_tree_model_get(model, b, LST_DEFCAT_DATAS, &entry2, -1);
86
87 retval = (entry1->flags & GF_INCOME) - (entry2->flags & GF_INCOME);
88 if(!retval)
89 {
90 retval = hb_string_utf8_compare(entry1->name, entry2->name);
91 }
92
93 return retval;
94 }
95
96
97 /*
98 **
99 */
100 static void ui_bud_listview_icon_cell_data_function (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
101 {
102 Category *item;
103 gchar *iconname = NULL;
104
105 // get the transaction
106 gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &item, -1);
107
108 iconname = ( item->flags & GF_BUDGET ) ? ICONNAME_HB_OPE_BUDGET : NULL;
109
110 g_object_set(renderer, "icon-name", iconname, NULL);
111 }
112
113
114 /*
115 ** draw some text from the stored data structure
116 */
117 static void ui_bud_listview_cell_data_function_text (GtkTreeViewColumn *col,
118 GtkCellRenderer *renderer,
119 GtkTreeModel *model,
120 GtkTreeIter *iter,
121 gpointer user_data)
122 {
123 Category *entry;
124 gchar *name;
125 gchar *string;
126 gchar type;
127
128 gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &entry, -1);
129
130 if(entry->key == 0)
131 name = g_strdup(_("(no category)"));
132 else
133 name = entry->name;
134
135 type = (entry->flags & GF_INCOME) ? '+' : '-';
136
137 #if MYDEBUG
138 string = g_markup_printf_escaped("%s ::(f=%d, %c)", name, entry->flags, type );
139 #else
140 if(entry->key == 0)
141 string = g_strdup(name);
142 else
143 {
144 if(entry->flags & GF_BUDGET)
145 {
146 if( entry->parent == 0 )
147 string = g_markup_printf_escaped("<b>%s</b> [%c]", name, type);
148 else
149 string = g_markup_printf_escaped(" %c <b><i>%s</i></b>", type, name);
150 }
151 else
152 {
153 if( entry->parent == 0 )
154 string = g_markup_printf_escaped("%s [%c]", name, type);
155 else
156 string = g_markup_printf_escaped(" %c <i>%s</i>", type, name);
157 }
158 }
159 #endif
160
161 g_object_set(renderer, "markup", string, NULL);
162
163 g_free(string);
164 }
165
166
167 static gboolean ui_bud_listview_search_equal_func (GtkTreeModel *model,
168 gint column,
169 const gchar *key,
170 GtkTreeIter *iter,
171 gpointer search_data)
172 {
173 gboolean retval = TRUE;
174 gchar *normalized_string;
175 gchar *normalized_key;
176 gchar *case_normalized_string = NULL;
177 gchar *case_normalized_key = NULL;
178 Category *item;
179
180 //gtk_tree_model_get_value (model, iter, column, &value);
181 gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &item, -1);
182
183 if(item != NULL)
184 {
185 normalized_string = g_utf8_normalize (item->name, -1, G_NORMALIZE_ALL);
186 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
187
188 if (normalized_string && normalized_key)
189 {
190 case_normalized_string = g_utf8_casefold (normalized_string, -1);
191 case_normalized_key = g_utf8_casefold (normalized_key, -1);
192
193 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
194 retval = FALSE;
195 }
196
197 g_free (normalized_key);
198 g_free (normalized_string);
199 g_free (case_normalized_key);
200 g_free (case_normalized_string);
201 }
202 return retval;
203 }
204
205
206
207 /*
208 **
209 */
210 static GtkWidget *ui_bud_listview_new(void)
211 {
212 GtkTreeStore *store;
213 GtkWidget *treeview;
214 GtkCellRenderer *renderer;
215 GtkTreeViewColumn *column;
216
217 //store
218 store = gtk_tree_store_new (
219 3,
220 //NUM_LST_DEFCAT,
221 G_TYPE_BOOLEAN,
222 G_TYPE_POINTER,
223 G_TYPE_UINT
224 );
225
226 //sortable
227 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFCAT_DATAS, ui_bud_listview_compare_funct, NULL, NULL);
228 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DEFCAT_DATAS, GTK_SORT_ASCENDING);
229
230
231 //treeview
232 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
233 g_object_unref(store);
234
235 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
236
237 /* column 1 */
238 renderer = gtk_cell_renderer_text_new ();
239 g_object_set(renderer,
240 "ellipsize", PANGO_ELLIPSIZE_END,
241 "ellipsize-set", TRUE,
242 NULL);
243
244 column = gtk_tree_view_column_new();
245 gtk_tree_view_column_set_title(column, _("Category"));
246 gtk_tree_view_column_pack_start(column, renderer, TRUE);
247 gtk_tree_view_column_set_cell_data_func(column, renderer, ui_bud_listview_cell_data_function_text, GINT_TO_POINTER(1), NULL);
248 gtk_tree_view_column_set_alignment (column, 0.5);
249 gtk_tree_view_column_set_min_width(column, HB_MINWIDTH_LIST);
250 //gtk_tree_view_column_set_sort_column_id (column, LST_DEFACC_NAME);
251 gtk_tree_view_column_set_resizable(column, TRUE);
252 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
253
254 /* icon column */
255 column = gtk_tree_view_column_new();
256 renderer = gtk_cell_renderer_pixbuf_new ();
257 //gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
258 gtk_tree_view_column_pack_start(column, renderer, TRUE);
259 gtk_tree_view_column_set_cell_data_func(column, renderer, ui_bud_listview_icon_cell_data_function, NULL, NULL);
260 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
261
262
263 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), ui_bud_listview_search_equal_func, NULL, NULL);
264
265
266 //gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), FALSE);
267 //gtk_tree_view_set_reorderable (GTK_TREE_VIEW(treeview), TRUE);
268
269 return(treeview);
270 }
271
272
273
274 /*
275 ** index 0 is all month, then 1 -> 12 are months
276 */
277 static gchar *ui_bud_manage_getcsvbudgetstr(Category *item)
278 {
279 gchar *retval = NULL;
280 char buf[G_ASCII_DTOSTR_BUF_SIZE];
281
282 //DB( g_print(" get budgetstr for '%s'\n", item->name) );
283
284 if( !(item->flags & GF_CUSTOM) )
285 {
286 if( item->budget[0] )
287 {
288 g_ascii_dtostr (buf, sizeof (buf), item->budget[0]);
289 retval = g_strdup(buf);
290
291 //DB( g_print(" => %d: %s\n", 0, retval) );
292 }
293 }
294 else
295 {
296 gint i;
297
298 for(i=1;i<=12;i++)
299 {
300 //if( item->budget[i] )
301 //{
302 gchar *tmp = retval;
303
304 g_ascii_dtostr (buf, sizeof (buf), item->budget[i]);
305
306 if(retval != NULL)
307 {
308 retval = g_strconcat(retval, ";", buf, NULL);
309 g_free(tmp);
310 }
311 else
312 retval = g_strdup(buf);
313
314 //DB( g_print(" => %d: %s\n", i, retval) );
315
316 //}
317 }
318 }
319
320 return retval;
321 }
322
323
324 static gint ui_bud_manage_category_exists (GtkTreeModel *model, gchar *level, gchar *type, gchar *name, GtkTreeIter *return_iter)
325 {
326 GtkTreeIter iter, child;
327 gboolean valid;
328 Category *entry;
329 gint pos = 0;
330
331 if(model == NULL)
332 return 0;
333
334 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
335
336 while (valid)
337 {
338 gtk_tree_model_get (model, &iter, LST_DEFCAT_DATAS, &entry, -1);
339
340 if(*level == '1')
341 {
342 if(entry->name && g_ascii_strcasecmp(name, entry->name) == 0)
343 {
344 *return_iter = iter;
345 return pos;
346 }
347 }
348 else
349 {
350 if(*level == '2')
351 {
352 gint n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
353 gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
354 while(n_child > 0)
355 {
356
357 gtk_tree_model_get(GTK_TREE_MODEL(model), &child,
358 LST_DEFCAT_DATAS, &entry,
359 -1);
360
361 if(entry->name && g_ascii_strcasecmp(name, entry->name) == 0)
362 {
363 *return_iter = child;
364 return pos;
365 }
366
367 n_child--;
368 gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
369 pos++;
370 }
371 }
372 }
373 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
374 pos++;
375 }
376
377 return 0;
378 }
379
380
381 static void ui_bud_manage_load_csv( GtkWidget *widget, gpointer user_data)
382 {
383 struct ui_bud_manage_data *data = user_data;
384 gchar *filename = NULL;
385 GIOChannel *io;
386 const gchar *encoding;
387
388
389 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
390
391
392 DB( g_print("(ui_bud_manage) load csv - data %p\n", data) );
393
394 if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_OPEN, &filename, NULL) == TRUE )
395 {
396 DB( g_print(" + filename is %s\n", filename) );
397
398 encoding = homebank_file_getencoding(filename);
399
400 io = g_io_channel_new_file(filename, "r", NULL);
401 if(io != NULL)
402 {
403 GtkTreeModel *model;
404 GtkTreeIter iter;
405 gboolean error = FALSE;
406 gchar *tmpstr;
407 gint io_stat;
408
409 DB( g_print(" -> encoding should be %s\n", encoding) );
410 if( encoding != NULL )
411 {
412 g_io_channel_set_encoding(io, encoding, NULL);
413 }
414
415 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
416
417
418 for(;;)
419 {
420 io_stat = g_io_channel_read_line(io, &tmpstr, NULL, NULL, NULL);
421 if( io_stat == G_IO_STATUS_EOF)
422 break;
423 if( io_stat == G_IO_STATUS_NORMAL)
424 {
425 if( tmpstr != NULL)
426 {
427 gchar **str_array;
428
429 hb_string_strip_crlf(tmpstr);
430
431 str_array = g_strsplit (tmpstr, ";", 15);
432 // type; sign; name
433
434 if( (g_strv_length (str_array) < 4 || *str_array[1] != '*') && (g_strv_length (str_array) < 15))
435 {
436 error = TRUE;
437 break;
438 }
439
440 DB( g_print(" csv read '%s : %s : %s ...'\n", str_array[0], str_array[1], str_array[2]) );
441
442 gint pos = ui_bud_manage_category_exists(model, str_array[0], str_array[1], str_array[2], &iter);
443
444 DB( g_print(" pos=%d\n", pos) );
445
446 if( pos != 0 )
447 {
448 gboolean budget;
449 Category *tmpitem;
450 gint i;
451
452 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
453 LST_DEFCAT_DATAS, &tmpitem,
454 -1);
455
456 DB( g_print(" found cat, updating '%s'\n", tmpitem->name) );
457
458 data->change++;
459
460 tmpitem->flags &= ~(GF_CUSTOM); //delete flag
461 if( *str_array[1] == '*' )
462 {
463 tmpitem->budget[0] = g_ascii_strtod(str_array[3], NULL);
464
465 DB( g_print(" monthly '%.2f'\n", tmpitem->budget[0]) );
466 }
467 else
468 {
469 tmpitem->flags |= (GF_CUSTOM);
470
471 for(i=1;i<=12;i++)
472 {
473 tmpitem->budget[i] = g_ascii_strtod(str_array[2+i], NULL);
474 DB( g_print(" month %d '%.2f'\n", i, tmpitem->budget[i]) );
475 }
476 }
477
478 // if any value,set the flag to visual indicator
479 budget = FALSE;
480 tmpitem->flags &= ~(GF_BUDGET); //delete flag
481 for(i=0;i<=12;i++)
482 {
483 if(tmpitem->budget[i])
484 {
485 budget = TRUE;
486 break;
487 }
488 }
489
490 if(budget == TRUE)
491 tmpitem->flags |= GF_BUDGET;
492 }
493
494 g_strfreev (str_array);
495 }
496 g_free(tmpstr);
497 }
498
499 }
500
501 //update the treeview
502 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat)));
503
504
505 g_io_channel_unref (io);
506
507 if( error == TRUE )
508 {
509 ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
510 _("File format error"),
511 _("The CSV file must contains the exact numbers of column,\nseparated by a semi-colon, please see the help for more details.")
512 );
513 }
514
515 }
516
517 g_free( filename );
518
519 }
520 }
521
522
523 static void ui_bud_manage_save_csv( GtkWidget *widget, gpointer user_data)
524 {
525 struct ui_bud_manage_data *data = user_data;
526 gchar *filename = NULL;
527 GtkTreeModel *model;
528 GtkTreeIter iter, child;
529 gboolean valid;
530 GIOChannel *io;
531
532 DB( g_print("(ui_bud_manage) save csv\n") );
533
534 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
535
536 if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, NULL) == TRUE )
537 {
538
539 DB( g_print(" + filename is %s\n", filename) );
540
541 io = g_io_channel_new_file(filename, "w", NULL);
542 if(io != NULL)
543 {
544
545 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
546
547 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
548
549 while (valid)
550 {
551 gchar *outstr, *outvalstr;
552 Category *category;
553 gchar type;
554
555 gtk_tree_model_get (GTK_TREE_MODEL(model), &iter, LST_DEFCAT_DATAS, &category, -1);
556
557 if( category->name != NULL )
558 {
559
560 //level 1: category
561 if( category->flags & GF_BUDGET )
562 {
563 type = (category->flags & GF_CUSTOM) ? ' ' : '*';
564
565 outvalstr = ui_bud_manage_getcsvbudgetstr(category);
566 outstr = g_strdup_printf("1;%c;%s;%s\n", type, category->name, outvalstr);
567 DB( g_print("%s", outstr) );
568 g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
569 g_free(outstr);
570 g_free(outvalstr);
571 }
572
573
574 //level 2: subcategory
575 gint n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
576 gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
577 while(n_child > 0)
578 {
579 gtk_tree_model_get(GTK_TREE_MODEL(model), &child, LST_DEFCAT_DATAS, &category, -1);
580
581 type = (category->flags & GF_CUSTOM) ? ' ' : '*';
582
583 outvalstr = ui_bud_manage_getcsvbudgetstr(category);
584 if( outvalstr )
585 {
586 outstr = g_strdup_printf("2;%c;%s;%s\n", type, category->name, outvalstr);
587 DB( g_print("%s", outstr) );
588 g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
589 g_free(outstr);
590 }
591 g_free(outvalstr);
592
593 n_child--;
594 gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
595 }
596 }
597
598 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
599 }
600
601 g_io_channel_unref (io);
602 }
603
604 g_free( filename );
605
606 }
607
608 }
609
610
611 static void ui_bud_manage_expand_all(GtkWidget *widget, gpointer user_data)
612 {
613 struct ui_bud_manage_data *data;
614
615 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
616 DB( g_print("\n(ui_bud_manage) expand all (data=%x)\n", (guint)data) );
617
618 gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_cat));
619
620 }
621
622
623 static void ui_bud_manage_collapse_all(GtkWidget *widget, gpointer user_data)
624 {
625 struct ui_bud_manage_data *data;
626
627 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
628 DB( g_print("\n(ui_bud_manage) collapse all (data=%x)\n", (guint)data) );
629
630 gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_cat));
631
632 }
633
634 static void ui_bud_manage_update(GtkWidget *treeview, gpointer user_data)
635 {
636 struct ui_bud_manage_data *data;
637 gboolean name, custom, sensitive;
638 gint i;
639
640 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
641
642 DB( g_print("\n(ui_bud_manage) update %x\n", (gint)data) );
643
644
645 name = FALSE;
646 if(data->cat != NULL)
647 {
648 name = data->cat->key == 0 ? FALSE : TRUE;
649 }
650
651 sensitive = name;
652
653 gtk_widget_set_sensitive(data->label_budget, sensitive);
654 gtk_widget_set_sensitive(data->CM_type[0], sensitive);
655 gtk_widget_set_sensitive(data->CM_type[1], sensitive);
656
657 gtk_widget_set_sensitive(data->label_options, sensitive);
658 gtk_widget_set_sensitive(data->CM_force, sensitive);
659
660 gtk_widget_set_sensitive(data->BT_clear, sensitive);
661
662 #if MYDEBUG == 1
663 gint toto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_type[0]));
664 DB( g_print(" monthly = %d\n", toto) );
665 #endif
666
667 custom = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_type[1]));
668 DB( g_print(" custom = %d\n\n", custom) );
669
670 sensitive = name == FALSE ? FALSE : custom == TRUE ? FALSE: TRUE;
671 gtk_widget_set_sensitive(data->spinner[0], sensitive);
672
673 sensitive = name == FALSE ? FALSE : custom;
674 for(i=0;i<12;i++)
675 {
676 gtk_widget_set_sensitive(data->label[i+1], sensitive);
677 gtk_widget_set_sensitive(data->spinner[i+1], sensitive);
678 }
679
680 }
681
682
683 static void ui_bud_manage_clear(GtkWidget *widget, gpointer user_data)
684 {
685 struct ui_bud_manage_data *data;
686 gchar *title;
687 gchar *secondtext;
688 gint result, i;
689
690 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
691
692 DB( g_print("(ui_bud_manage) clear\n") );
693
694
695 title = _("Are you sure you want to clear input?");
696
697 secondtext = _("If you proceed, every amount will be set to 0.");
698
699 result = ui_dialog_msg_confirm_alert(
700 GTK_WINDOW(data->window),
701 title,
702 secondtext,
703 _("_Clear")
704 );
705
706 if( result == GTK_RESPONSE_OK )
707 {
708 //g_signal_handler_block(data->CM_type[0], data->handler_id[HID_CUSTOM]);
709 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_type[0]), TRUE);
710 //g_signal_handler_unblock(data->CM_type[0], data->handler_id[HID_CUSTOM]);
711
712 for(i=0;i<=12;i++)
713 {
714 //g_signal_handler_block(data->spinner[i], data->spinner_hid[i]);
715
716 gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->spinner[i]), 0);
717 data->cat->budget[i] = 0;
718
719 //g_signal_handler_unblock(data->spinner[i], data->spinner_hid[i]);
720 }
721
722 data->cat->flags &= ~(GF_BUDGET); //delete flag
723
724 gtk_widget_queue_draw (data->LV_cat);
725 }
726
727 }
728
729
730 static void ui_bud_manage_set(GtkWidget *widget, gpointer user_data)
731 {
732 struct ui_bud_manage_data *data;
733 gint active;
734 gint i;
735
736 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
737
738 DB( g_print("(ui_bud_manage) set\n") );
739
740 active = data->cat->flags & GF_CUSTOM ? 1 : 0;
741 //data->custom = active;
742
743 //DB( g_print(" set custom to %d\n", data->custom) );
744
745 g_signal_handler_block(data->CM_type[0], data->handler_id[HID_CUSTOM]);
746 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_type[active]), TRUE);
747 g_signal_handler_unblock(data->CM_type[0], data->handler_id[HID_CUSTOM]);
748
749 for(i=0;i<=12;i++)
750 {
751 //g_signal_handler_block(data->spinner[i], data->spinner_hid[i]);
752
753 gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->spinner[i]), data->cat->budget[i]);
754 //DB( g_print(" %.2f\n", data->cat->budget[i]) );
755
756 //g_signal_handler_unblock(data->spinner[i], data->spinner_hid[i]);
757 }
758
759 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_force), (data->cat->flags & GF_FORCED) ? 1 : 0);
760
761 }
762
763 static gboolean ui_bud_manage_has_budget(GtkSpinButton *spinbutton, gpointer user_data)
764 {
765 struct ui_bud_manage_data *data;
766 gint i;
767 Category *item;
768 gboolean retval;
769
770 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(spinbutton), GTK_TYPE_WINDOW)), "inst_data");
771
772 DB( g_print("(ui_bud_manage) has budget\n") );
773
774 retval = FALSE;
775
776 item = data->cat;
777
778 if( item != NULL )
779 {
780 item->flags &= ~(GF_BUDGET); //delete flag
781 for(i=0;i<=12;i++)
782 {
783 gtk_spin_button_update(GTK_SPIN_BUTTON(data->spinner[i]));
784 if( gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->spinner[i])) )
785 {
786 retval = TRUE;
787 item->flags |= GF_BUDGET;
788 break;
789 }
790 }
791
792 }
793 return retval;
794 }
795
796
797 static void ui_bud_manage_getlast(struct ui_bud_manage_data *data)
798 {
799 gboolean budget, change;
800 gint i;
801 Category *item;
802 gdouble oldvalue;
803 gint active;
804
805 item = data->lastcatitem;
806
807 DB( g_print("****\n(ui_bud_manage) getlast for '%s'\n", item->name ) );
808
809 if( item != NULL )
810 {
811 gushort old_flags = item->flags;
812
813 item->flags &= ~(GF_CUSTOM);
814 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_type[0])) == FALSE)
815 item->flags |= GF_CUSTOM;
816
817 DB( g_print(" custom flag=%d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_type[1]))) );
818
819 // if any value,set the flag to visual indicator
820 budget = FALSE;
821 change = FALSE;
822 item->flags &= ~(GF_BUDGET); //delete flag
823 for(i=0;i<=12;i++)
824 {
825 gtk_spin_button_update(GTK_SPIN_BUTTON(data->spinner[i]));
826 oldvalue = item->budget[i];
827
828 item->budget[i] = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->spinner[i]));
829
830 if( oldvalue != item->budget[i])
831 change = TRUE;
832
833 DB( g_print(" set value %d to %.2f\n", i, item->budget[i]) );
834 if(item->budget[i])
835 {
836 budget = TRUE;
837 }
838 }
839
840 item->flags &= ~(GF_FORCED); //delete flag
841 active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_force));
842 if(active == 1)
843 item->flags |= GF_FORCED;
844
845 if(budget == TRUE || active == 1)
846 item->flags |= GF_BUDGET;
847
848 // compute chnages
849 if( (old_flags != item->flags) || change )
850 data->change++;
851
852 gtk_widget_queue_draw (data->LV_cat);
853
854 }
855
856 }
857
858
859 static void ui_bud_manage_selection_change(GtkWidget *treeview, gpointer user_data)
860 {
861 struct ui_bud_manage_data *data;
862 GtkTreeModel *model;
863 GtkTreeIter iter;
864
865 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
866
867 DB( g_print("(ui_bud_manage) changed\n") );
868
869 data->cat = NULL;
870
871 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat)), &model, &iter))
872 {
873 Category *item;
874
875 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
876 LST_DEFCAT_DATAS, &item,
877 -1);
878
879 DB( g_print(" selected %s\n", item->name) );
880
881 if(data->lastcatitem != NULL && item != data->lastcatitem)
882 {
883 DB( g_print(" -> should do a get for last selected (%s)\n", data->lastcatitem->name) );
884 ui_bud_manage_getlast(data);
885 }
886
887
888 data->cat = item;
889 data->lastcatitem = item;
890
891 ui_bud_manage_set(treeview, NULL);
892 }
893 else
894 {
895 data->lastcatitem = NULL;
896 }
897
898 ui_bud_manage_update(treeview, NULL);
899
900 }
901
902 static void ui_bud_manage_toggle(GtkRadioButton *radiobutton, gpointer user_data)
903 {
904 //struct ui_bud_manage_data *data;
905
906 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(radiobutton), GTK_TYPE_WINDOW)), "inst_data");
907
908 DB( g_print("(ui_bud_manage) toggle\n") );
909
910 //ui_bud_manage_get(GTK_WIDGET(radiobutton), GINT_TO_POINTER(FIELD_TYPE));
911
912 //data->custom ^= 1;
913 ui_bud_manage_update(GTK_WIDGET(radiobutton), NULL);
914 }
915
916
917 void ui_bud_manage_selection(GtkTreeSelection *treeselection, gpointer user_data)
918 {
919 ui_bud_manage_selection_change(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
920 }
921
922
923 static gboolean ui_bud_manage_cleanup(struct ui_bud_manage_data *data, gint result)
924 {
925 gboolean doupdate = FALSE;
926
927 DB( g_print("(ui_bud_manage) cleanup\n") );
928
929 if(data->lastcatitem != NULL)
930 {
931 DB( g_print(" -> should do a get for last selected (%s)\n", data->lastcatitem->name) );
932 ui_bud_manage_getlast(data);
933 }
934
935 //do_application_specific_something ();
936 DB( g_print(" accept\n") );
937
938 GLOBALS->changes_count += data->change;
939
940 DB( g_print(" free tmp_list\n") );
941
942 return doupdate;
943 }
944
945 static void ui_bud_manage_populate_listview(struct ui_bud_manage_data *data)
946 {
947 gint type;
948
949 type = radio_get_active(GTK_CONTAINER(data->RA_type)) == 1 ? CAT_TYPE_INCOME : CAT_TYPE_EXPENSE;
950
951 ui_cat_listview_populate(data->LV_cat, type);
952 gtk_tree_view_expand_all (GTK_TREE_VIEW(data->LV_cat));
953 }
954
955
956 static void ui_bud_manage_setup(struct ui_bud_manage_data *data)
957 {
958
959 DB( g_print("(ui_bud_manage) setup\n") );
960
961 data->tmp_list = NULL;
962 data->change = 0;
963 data->cat = NULL;
964 data->lastcatitem = NULL;
965
966 ui_bud_manage_populate_listview(data);
967
968 }
969
970
971 static void ui_bud_manage_type_changed_cb (GtkToggleButton *button, gpointer user_data)
972 {
973 ui_bud_manage_populate_listview(user_data);
974 //g_print(" toggle type=%d\n", gtk_toggle_button_get_active(button));
975 }
976
977
978
979
980
981 GtkWidget *ui_bud_manage_dialog (void)
982 {
983 struct ui_bud_manage_data data;
984 GtkWidget *dialog, *content_area;
985 GtkWidget *content_grid, *group_grid, *table, *scrollwin, *label;
986 GtkWidget *treeview, *hpaned, *bbox, *vbox, *hbox;
987 GtkWidget *menu, *menuitem, *widget, *image, *tbar;
988 GtkToolItem *toolitem;
989 GList *fchain;
990 guint i;
991 gint w, h;
992 gint crow, row;
993
994 memset(&data, 0, sizeof(struct ui_bud_manage_data));
995
996 dialog = gtk_dialog_new_with_buttons (_("Manage Budget"),
997 GTK_WINDOW(GLOBALS->mainwindow),
998 0,
999 _("_Close"),
1000 GTK_RESPONSE_ACCEPT,
1001 NULL);
1002
1003 data.window = dialog;
1004
1005 gtk_window_set_icon_name(GTK_WINDOW (dialog), ICONNAME_HB_BUDGET);
1006
1007 //set a nice dialog size
1008 gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
1009 gtk_window_set_default_size (GTK_WINDOW(dialog), -1, h/PHI);
1010
1011
1012 //store our window private data
1013 g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)&data);
1014 DB( g_print("(ui_bud_manage) window=%p, inst_data=%p\n", dialog, &data) );
1015
1016 //window contents
1017 content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
1018
1019 //our table
1020 table = gtk_grid_new ();
1021 gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_MEDIUM);
1022 gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
1023 g_object_set(table, "margin", SPACING_MEDIUM, NULL);
1024 gtk_box_pack_start (GTK_BOX (content_area), table, TRUE, TRUE, 0);
1025
1026 crow = 0;
1027 bbox = make_radio(CYA_CAT_TYPE, TRUE, GTK_ORIENTATION_HORIZONTAL);
1028 data.RA_type = bbox;
1029 gtk_widget_set_halign (bbox, GTK_ALIGN_CENTER);
1030 gtk_grid_attach (GTK_GRID (table), bbox, 0, crow, 1, 1);
1031
1032 widget = radio_get_nth_widget(GTK_CONTAINER(bbox), 1);
1033 if(widget)
1034 g_signal_connect (widget, "toggled", G_CALLBACK (ui_bud_manage_type_changed_cb), &data);
1035
1036 menu = gtk_menu_new ();
1037 gtk_widget_set_halign (menu, GTK_ALIGN_END);
1038
1039 menuitem = gtk_menu_item_new_with_mnemonic (_("_Import CSV"));
1040 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1041 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (ui_bud_manage_load_csv), &data);
1042
1043 menuitem = gtk_menu_item_new_with_mnemonic (_("E_xport CSV"));
1044 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1045 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (ui_bud_manage_save_csv), &data);
1046
1047 gtk_widget_show_all (menu);
1048
1049 widget = gtk_menu_button_new();
1050 image = gtk_image_new_from_icon_name (ICONNAME_HB_BUTTON_MENU, GTK_ICON_SIZE_MENU);
1051
1052 //gchar *thename;
1053 //gtk_image_get_icon_name(image, &thename, NULL);
1054 //g_print("the name is %s\n", thename);
1055
1056 g_object_set (widget, "image", image, "popup", GTK_MENU(menu), NULL);
1057
1058 gtk_widget_set_hexpand (widget, FALSE);
1059 gtk_widget_set_halign (widget, GTK_ALIGN_END);
1060 gtk_grid_attach (GTK_GRID (table), widget, 0, crow++, 1, 1);
1061
1062
1063
1064 crow++;
1065 hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
1066 //gtk_container_set_border_width (GTK_CONTAINER(hpaned), SPACING_MEDIUM);
1067 gtk_grid_attach (GTK_GRID (table), hpaned, 0, crow++, 1, 1);
1068
1069 /* left area */
1070 //list
1071 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1072 gtk_widget_set_margin_right(vbox, SPACING_SMALL);
1073 gtk_paned_pack1 (GTK_PANED(hpaned), vbox, FALSE, FALSE);
1074
1075 scrollwin = gtk_scrolled_window_new(NULL,NULL);
1076 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin), GTK_SHADOW_ETCHED_IN);
1077 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1078 treeview = (GtkWidget *)ui_bud_listview_new();
1079 data.LV_cat = treeview;
1080 gtk_widget_set_size_request(treeview, HB_MINWIDTH_LIST, -1);
1081 gtk_container_add(GTK_CONTAINER(scrollwin), treeview);
1082 gtk_widget_set_hexpand (scrollwin, TRUE);
1083 gtk_widget_set_vexpand (scrollwin, TRUE);
1084 gtk_box_pack_start (GTK_BOX(vbox), scrollwin, TRUE, TRUE, 0);
1085
1086 //list toolbar
1087 tbar = gtk_toolbar_new();
1088 gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
1089 gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
1090 gtk_box_pack_start (GTK_BOX (vbox), tbar, FALSE, FALSE, 0);
1091
1092 gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
1093
1094 toolitem = gtk_separator_tool_item_new ();
1095 gtk_tool_item_set_expand (toolitem, TRUE);
1096 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
1097 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
1098
1099 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1100 toolitem = gtk_tool_item_new();
1101 gtk_container_add (GTK_CONTAINER(toolitem), hbox);
1102 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
1103
1104 widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
1105 data.BT_expand = widget;
1106 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1107
1108 widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
1109 data.BT_collapse = widget;
1110 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1111
1112
1113 /* right area */
1114 content_grid = gtk_grid_new();
1115 gtk_grid_set_row_spacing (GTK_GRID (content_grid), SPACING_LARGE);
1116 gtk_orientable_set_orientation(GTK_ORIENTABLE(content_grid), GTK_ORIENTATION_VERTICAL);
1117 //gtk_container_set_border_width (GTK_CONTAINER(content_grid), SPACING_MEDIUM);
1118 gtk_widget_set_margin_left(content_grid, SPACING_SMALL);
1119 gtk_paned_pack2 (GTK_PANED(hpaned), content_grid, FALSE, FALSE);
1120
1121 crow = 0;
1122
1123
1124 // group :: General
1125 group_grid = gtk_grid_new ();
1126 gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
1127 gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
1128 gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
1129
1130 label = make_label_group(_("Budget for each month"));
1131 data.label_budget = label;
1132 gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
1133
1134 fchain = NULL;
1135
1136 row = 1;
1137 widget = gtk_radio_button_new_with_label (NULL, _("is the same"));
1138 data.CM_type[0] = widget;
1139 gtk_widget_set_hexpand (widget, TRUE);
1140 gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
1141 fchain = g_list_append(fchain, widget);
1142
1143 row++;
1144 widget = make_amount(label);
1145 data.spinner[0] = widget;
1146 gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
1147 fchain = g_list_append(fchain, widget);
1148
1149 g_signal_connect (G_OBJECT (data.spinner[0]), "value-changed", G_CALLBACK (ui_bud_manage_has_budget), NULL);
1150
1151 widget = gtk_button_new_with_mnemonic (_("_Clear input"));
1152 data.BT_clear = widget;
1153 gtk_widget_set_hexpand (widget, TRUE);
1154 gtk_widget_set_halign(widget, GTK_ALIGN_START);
1155 gtk_grid_attach (GTK_GRID (group_grid), widget, 4, row, 1, 1);
1156 fchain = g_list_append(fchain, widget);
1157
1158
1159 // propagate button
1160 /*row++;
1161 button = gtk_button_new_with_label(_("Propagate"));
1162 gtk_grid_attach (GTK_GRID (group_grid), button, 1, 2, row, row+1);
1163 */
1164
1165 row++;
1166 widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON (data.CM_type[0]), _("is different"));
1167 data.CM_type[1] = widget;
1168 gtk_widget_set_hexpand (widget, TRUE);
1169 gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
1170 fchain = g_list_append(fchain, widget);
1171
1172 row++;
1173 for(i=0;i<12;i++)
1174 {
1175 gint l, t;
1176
1177 l = ((i<6) ? 1 : 3);
1178 t = row + ((i<6) ? i : i-6);
1179
1180 label = make_label_widget(months[i]);
1181 data.label[i+1] = label;
1182 gtk_grid_attach (GTK_GRID (group_grid), label, l, t, 1, 1);
1183
1184 widget = make_amount(label);
1185 data.spinner[i+1] = widget;
1186 fchain = g_list_append(fchain, widget);
1187 gtk_widget_set_hexpand (widget, TRUE);
1188 gtk_grid_attach (GTK_GRID (group_grid), widget, l+1, t, 1, 1);
1189
1190 g_signal_connect (G_OBJECT (data.spinner[i+1]), "value-changed", G_CALLBACK (ui_bud_manage_has_budget), NULL);
1191
1192 //DB( g_print("(ui_bud_manage) %s, col=%d, row=%d", months[i], col, row) );
1193 }
1194
1195 gtk_container_set_focus_chain(GTK_CONTAINER(group_grid), fchain);
1196 g_list_free(fchain);
1197
1198 // group :: Options
1199 group_grid = gtk_grid_new ();
1200 gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
1201 gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
1202 gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
1203
1204 label = make_label_group(_("Options"));
1205 data.label_options = label;
1206 gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
1207
1208 row = 1;
1209 widget = gtk_check_button_new_with_mnemonic (_("_Force monitoring this category"));
1210 data.CM_force = widget;
1211 gtk_widget_set_hexpand (widget, TRUE);
1212 gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
1213
1214
1215 //connect all our signals
1216 g_signal_connect (dialog, "destroy",
1217 G_CALLBACK (gtk_widget_destroyed), &dialog);
1218
1219
1220 g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data.LV_cat)), "changed", G_CALLBACK (ui_bud_manage_selection), NULL);
1221 //g_signal_connect (GTK_TREE_VIEW(data.LV_cat), "row-activated", G_CALLBACK (ui_bud_manage_onRowActivated), NULL);
1222
1223 g_signal_connect (G_OBJECT (data.BT_expand), "clicked", G_CALLBACK (ui_bud_manage_expand_all), NULL);
1224 g_signal_connect (G_OBJECT (data.BT_collapse), "clicked", G_CALLBACK (ui_bud_manage_collapse_all), NULL);
1225
1226 data.handler_id[HID_CUSTOM] = g_signal_connect (data.CM_type[0], "toggled", G_CALLBACK (ui_bud_manage_toggle), NULL);
1227
1228 g_signal_connect (G_OBJECT (data.BT_clear), "clicked", G_CALLBACK (ui_bud_manage_clear), NULL);
1229
1230 //data.custom = FALSE;
1231 //gtk_widget_set_sensitive(data.table, FALSE);
1232
1233 //setup, init and show window
1234 ui_bud_manage_setup(&data);
1235 ui_bud_manage_update(dialog, NULL);
1236
1237
1238
1239 gtk_widget_show_all (dialog);
1240
1241 //result
1242 gint result = gtk_dialog_run (GTK_DIALOG (dialog));
1243 switch (result)
1244 {
1245 case GTK_RESPONSE_ACCEPT:
1246 //do_application_specific_something ();
1247 break;
1248 default:
1249 //do_nothing_since_dialog_was_cancelled ();
1250 break;
1251 }
1252
1253 // cleanup and destroy
1254 ui_bud_manage_cleanup(&data, result);
1255 gtk_widget_destroy (dialog);
1256
1257 return NULL;
1258 }
1259
This page took 0.089126 seconds and 4 git commands to generate.