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