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