1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 Maxime DOYEN
4 * This file is part of HomeBank.
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.
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.
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/>.
23 #include "ui-category.h"
24 #include "ui-budget.h"
26 extern gchar
*CYA_CAT_TYPE
[];
28 /****************************************************************************/
30 /****************************************************************************/
39 /* our global datas */
40 extern struct HomeBank
*GLOBALS
;
41 extern struct Preferences
*PREFS
;
44 extern gchar
*CYA_ABMONTHS
[];
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
);
56 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
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.
65 static gint
ui_bud_listview_compare_funct (GtkTreeModel
*model
, GtkTreeIter
*a
, GtkTreeIter
*b
, gpointer userdata
)
68 Category
*entry1
, *entry2
;
70 gtk_tree_model_get(model
, a
, LST_DEFCAT_DATAS
, &entry1
, -1);
71 gtk_tree_model_get(model
, b
, LST_DEFCAT_DATAS
, &entry2
, -1);
73 retval
= (entry1
->flags
& GF_INCOME
) - (entry2
->flags
& GF_INCOME
);
76 retval
= hb_string_utf8_compare(entry1
->name
, entry2
->name
);
86 static void ui_bud_listview_icon_cell_data_function (GtkTreeViewColumn
*col
, GtkCellRenderer
*renderer
, GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer user_data
)
89 gchar
*iconname
= NULL
;
91 // get the transaction
92 gtk_tree_model_get(model
, iter
, LST_DEFCAT_DATAS
, &item
, -1);
94 iconname
= ( item
->flags
& GF_BUDGET
) ? ICONNAME_HB_OPE_BUDGET
: NULL
;
96 g_object_set(renderer
, "icon-name", iconname
, NULL
);
101 ** draw some text from the stored data structure
103 static void ui_bud_listview_cell_data_function_text (GtkTreeViewColumn
*col
,
104 GtkCellRenderer
*renderer
,
114 gtk_tree_model_get(model
, iter
, LST_DEFCAT_DATAS
, &entry
, -1);
117 name
= g_strdup(_("(no category)"));
121 type
= category_get_type_char(entry
);
124 string
= g_markup_printf_escaped("%s ::(f=%d, %c)", name
, entry
->flags
, type
);
127 string
= g_strdup(name
);
130 if(entry
->flags
& GF_BUDGET
)
132 if( entry
->parent
== 0 )
133 string
= g_markup_printf_escaped("<b>%s</b> [%c]", name
, type
);
135 string
= g_markup_printf_escaped(" %c <b><i>%s</i></b>", type
, name
);
139 if( entry
->parent
== 0 )
140 string
= g_markup_printf_escaped("%s [%c]", name
, type
);
142 string
= g_markup_printf_escaped(" %c <i>%s</i>", type
, name
);
147 g_object_set(renderer
, "markup", string
, NULL
);
153 static gboolean
ui_bud_listview_search_equal_func (GtkTreeModel
*model
,
157 gpointer search_data
)
159 gboolean retval
= TRUE
;
160 gchar
*normalized_string
;
161 gchar
*normalized_key
;
162 gchar
*case_normalized_string
= NULL
;
163 gchar
*case_normalized_key
= NULL
;
166 //gtk_tree_model_get_value (model, iter, column, &value);
167 gtk_tree_model_get(model
, iter
, LST_DEFCAT_DATAS
, &item
, -1);
171 normalized_string
= g_utf8_normalize (item
->name
, -1, G_NORMALIZE_ALL
);
172 normalized_key
= g_utf8_normalize (key
, -1, G_NORMALIZE_ALL
);
174 if (normalized_string
&& normalized_key
)
176 case_normalized_string
= g_utf8_casefold (normalized_string
, -1);
177 case_normalized_key
= g_utf8_casefold (normalized_key
, -1);
179 if (strncmp (case_normalized_key
, case_normalized_string
, strlen (case_normalized_key
)) == 0)
183 g_free (normalized_key
);
184 g_free (normalized_string
);
185 g_free (case_normalized_key
);
186 g_free (case_normalized_string
);
196 static GtkWidget
*ui_bud_listview_new(void)
200 GtkCellRenderer
*renderer
;
201 GtkTreeViewColumn
*column
;
204 store
= gtk_tree_store_new (
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
);
218 treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
219 g_object_unref(store
);
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
);
225 renderer
= gtk_cell_renderer_text_new ();
226 g_object_set(renderer
,
227 "ellipsize", PANGO_ELLIPSIZE_END
,
228 "ellipsize-set", TRUE
,
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
);
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
);
250 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview
), ui_bud_listview_search_equal_func
, NULL
, NULL
);
253 //gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), FALSE);
254 //gtk_tree_view_set_reorderable (GTK_TREE_VIEW(treeview), TRUE);
262 ** index 0 is all month, then 1 -> 12 are months
264 static gchar
*ui_bud_manage_getcsvbudgetstr(Category
*item
)
266 gchar
*retval
= NULL
;
267 char buf
[G_ASCII_DTOSTR_BUF_SIZE
];
269 //DB( g_print(" get budgetstr for '%s'\n", item->name) );
271 if( !(item
->flags
& GF_CUSTOM
) )
273 if( item
->budget
[0] )
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
);
280 //DB( g_print(" => %d: %s\n", 0, retval) );
289 //if( item->budget[i] )
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
]);
299 retval
= g_strconcat(retval
, ";", buf
, NULL
);
303 retval
= g_strdup(buf
);
305 //DB( g_print(" => %d: %s\n", i, retval) );
315 static gint
ui_bud_manage_category_exists (GtkTreeModel
*model
, gchar
*level
, gchar
*type
, gchar
*name
, GtkTreeIter
*return_iter
)
317 GtkTreeIter iter
, child
;
325 valid
= gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model
), &iter
);
329 gtk_tree_model_get (model
, &iter
, LST_DEFCAT_DATAS
, &entry
, -1);
333 if(entry
->name
&& g_ascii_strcasecmp(name
, entry
->name
) == 0)
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
);
348 gtk_tree_model_get(GTK_TREE_MODEL(model
), &child
,
349 LST_DEFCAT_DATAS
, &entry
,
352 if(entry
->name
&& g_ascii_strcasecmp(name
, entry
->name
) == 0)
354 *return_iter
= child
;
359 gtk_tree_model_iter_next(GTK_TREE_MODEL(model
), &child
);
364 valid
= gtk_tree_model_iter_next(GTK_TREE_MODEL(model
), &iter
);
372 static void ui_bud_manage_load_csv( GtkWidget
*widget
, gpointer user_data
)
374 struct ui_bud_manage_data
*data
= user_data
;
375 gchar
*filename
= NULL
;
377 const gchar
*encoding
;
380 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
383 DB( g_print("(ui_bud_manage) load csv - data %p\n", data
) );
385 if( ui_file_chooser_csv(GTK_WINDOW(data
->window
), GTK_FILE_CHOOSER_ACTION_OPEN
, &filename
, NULL
) == TRUE
)
387 DB( g_print(" + filename is %s\n", filename
) );
389 encoding
= homebank_file_getencoding(filename
);
391 io
= g_io_channel_new_file(filename
, "r", NULL
);
396 gboolean error
= FALSE
;
400 DB( g_print(" -> encoding should be %s\n", encoding
) );
401 if( encoding
!= NULL
)
403 g_io_channel_set_encoding(io
, encoding
, NULL
);
406 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(data
->LV_cat
));
411 io_stat
= g_io_channel_read_line(io
, &tmpstr
, NULL
, NULL
, NULL
);
412 if( io_stat
== G_IO_STATUS_EOF
)
414 if( io_stat
== G_IO_STATUS_NORMAL
)
420 hb_string_strip_crlf(tmpstr
);
422 str_array
= g_strsplit (tmpstr
, ";", 15);
425 if( (g_strv_length (str_array
) < 4 || *str_array
[1] != '*') && (g_strv_length (str_array
) < 15))
431 DB( g_print(" csv read '%s : %s : %s ...'\n", str_array
[0], str_array
[1], str_array
[2]) );
433 gint pos
= ui_bud_manage_category_exists(model
, str_array
[0], str_array
[1], str_array
[2], &iter
);
435 DB( g_print(" pos=%d\n", pos
) );
443 gtk_tree_model_get(GTK_TREE_MODEL(model
), &iter
,
444 LST_DEFCAT_DATAS
, &tmpitem
,
447 DB( g_print(" found cat, updating '%s'\n", tmpitem
->name
) );
451 tmpitem
->flags
&= ~(GF_CUSTOM
); //delete flag
452 if( *str_array
[1] == '*' )
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
);
458 DB( g_print(" monthly '%.2f'\n", tmpitem
->budget
[0]) );
462 tmpitem
->flags
|= (GF_CUSTOM
);
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
]) );
473 // if any value,set the flag to visual indicator
475 tmpitem
->flags
&= ~(GF_BUDGET
); //delete flag
478 if(tmpitem
->budget
[i
])
486 tmpitem
->flags
|= GF_BUDGET
;
489 g_strfreev (str_array
);
496 //update the treeview
497 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection(GTK_TREE_VIEW(data
->LV_cat
)));
500 g_io_channel_unref (io
);
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.")
518 static void ui_bud_manage_save_csv( GtkWidget
*widget
, gpointer user_data
)
520 struct ui_bud_manage_data
*data
= user_data
;
521 gchar
*filename
= NULL
;
523 GtkTreeIter iter
, child
;
527 DB( g_print("(ui_bud_manage) save csv\n") );
529 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
531 if( ui_file_chooser_csv(GTK_WINDOW(data
->window
), GTK_FILE_CHOOSER_ACTION_SAVE
, &filename
, NULL
) == TRUE
)
534 DB( g_print(" + filename is %s\n", filename
) );
536 io
= g_io_channel_new_file(filename
, "w", NULL
);
540 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(data
->LV_cat
));
542 valid
= gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model
), &iter
);
546 gchar
*outstr
, *outvalstr
;
550 gtk_tree_model_get (GTK_TREE_MODEL(model
), &iter
, LST_DEFCAT_DATAS
, &category
, -1);
552 if( category
->name
!= NULL
)
556 if( category
->flags
& GF_BUDGET
)
558 type
= (category
->flags
& GF_CUSTOM
) ? ' ' : '*';
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
);
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
);
574 gtk_tree_model_get(GTK_TREE_MODEL(model
), &child
, LST_DEFCAT_DATAS
, &category
, -1);
576 type
= (category
->flags
& GF_CUSTOM
) ? ' ' : '*';
578 outvalstr
= ui_bud_manage_getcsvbudgetstr(category
);
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
);
589 gtk_tree_model_iter_next(GTK_TREE_MODEL(model
), &child
);
593 valid
= gtk_tree_model_iter_next(GTK_TREE_MODEL(model
), &iter
);
596 g_io_channel_unref (io
);
606 static void ui_bud_manage_expand_all(GtkWidget
*widget
, gpointer user_data
)
608 struct ui_bud_manage_data
*data
;
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
) );
613 gtk_tree_view_expand_all(GTK_TREE_VIEW(data
->LV_cat
));
618 static void ui_bud_manage_collapse_all(GtkWidget
*widget
, gpointer user_data
)
620 struct ui_bud_manage_data
*data
;
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
) );
625 gtk_tree_view_collapse_all(GTK_TREE_VIEW(data
->LV_cat
));
629 static void ui_bud_manage_update(GtkWidget
*treeview
, gpointer user_data
)
631 struct ui_bud_manage_data
*data
;
632 gboolean name
, custom
, sensitive
;
635 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview
), GTK_TYPE_WINDOW
)), "inst_data");
637 DB( g_print("\n(ui_bud_manage) update %x\n", (gint
)data
) );
641 if(data
->cat
!= NULL
)
643 name
= data
->cat
->key
== 0 ? FALSE
: TRUE
;
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
);
652 gtk_widget_set_sensitive(data
->label_options
, sensitive
);
653 gtk_widget_set_sensitive(data
->CM_force
, sensitive
);
655 gtk_widget_set_sensitive(data
->BT_clear
, sensitive
);
658 gint toto
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data
->CM_type
[0]));
659 DB( g_print(" monthly = %d\n", toto
) );
662 custom
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data
->CM_type
[1]));
663 DB( g_print(" custom = %d\n\n", custom
) );
665 sensitive
= name
== FALSE
? FALSE
: custom
== TRUE
? FALSE
: TRUE
;
666 gtk_widget_set_sensitive(data
->spinner
[0], sensitive
);
668 sensitive
= name
== FALSE
? FALSE
: custom
;
671 gtk_widget_set_sensitive(data
->label
[i
+1], sensitive
);
672 gtk_widget_set_sensitive(data
->spinner
[i
+1], sensitive
);
678 static void ui_bud_manage_clear(GtkWidget
*widget
, gpointer user_data
)
680 struct ui_bud_manage_data
*data
;
685 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget
), GTK_TYPE_WINDOW
)), "inst_data");
687 DB( g_print("(ui_bud_manage) clear\n") );
690 title
= _("Are you sure you want to clear input?");
692 secondtext
= _("If you proceed, every amount will be set to 0.");
694 result
= ui_dialog_msg_confirm_alert(
695 GTK_WINDOW(data
->window
),
701 if( result
== GTK_RESPONSE_OK
)
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]);
709 //g_signal_handler_block(data->spinner[i], data->spinner_hid[i]);
711 gtk_spin_button_set_value(GTK_SPIN_BUTTON(data
->spinner
[i
]), 0);
712 data
->cat
->budget
[i
] = 0;
714 //g_signal_handler_unblock(data->spinner[i], data->spinner_hid[i]);
717 data
->cat
->flags
&= ~(GF_BUDGET
); //delete flag
720 gtk_widget_queue_draw (data
->LV_cat
);
726 static void ui_bud_manage_set(GtkWidget
*widget
, gpointer user_data
)
728 struct ui_bud_manage_data
*data
;
732 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget
), GTK_TYPE_WINDOW
)), "inst_data");
734 DB( g_print("(ui_bud_manage) set\n") );
736 active
= data
->cat
->flags
& GF_CUSTOM
? 1 : 0;
737 //data->custom = active;
739 //DB( g_print(" set custom to %d\n", data->custom) );
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
]);
747 //g_signal_handler_block(data->spinner[i], data->spinner_hid[i]);
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]) );
752 //g_signal_handler_unblock(data->spinner[i], data->spinner_hid[i]);
755 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data
->CM_force
), (data
->cat
->flags
& GF_FORCED
) ? 1 : 0);
759 static gboolean
ui_bud_manage_has_budget(GtkSpinButton
*spinbutton
, gpointer user_data
)
761 struct ui_bud_manage_data
*data
;
766 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(spinbutton
), GTK_TYPE_WINDOW
)), "inst_data");
768 DB( g_print("(ui_bud_manage) has budget\n") );
776 item
->flags
&= ~(GF_BUDGET
); //delete flag
779 gtk_spin_button_update(GTK_SPIN_BUTTON(data
->spinner
[i
]));
780 if( gtk_spin_button_get_value(GTK_SPIN_BUTTON(data
->spinner
[i
])) )
783 item
->flags
|= GF_BUDGET
;
793 static void ui_bud_manage_getlast(struct ui_bud_manage_data
*data
)
795 gboolean budget
, change
;
801 item
= data
->lastcatitem
;
803 DB( g_print("****\n(ui_bud_manage) getlast for '%s'\n", item
->name
) );
807 gushort old_flags
= item
->flags
;
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
;
813 DB( g_print(" custom flag=%d\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data
->CM_type
[1]))) );
815 // if any value,set the flag to visual indicator
818 item
->flags
&= ~(GF_BUDGET
); //delete flag
821 gtk_spin_button_update(GTK_SPIN_BUTTON(data
->spinner
[i
]));
822 oldvalue
= item
->budget
[i
];
824 item
->budget
[i
] = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data
->spinner
[i
]));
826 if( oldvalue
!= item
->budget
[i
])
829 DB( g_print(" set value %d to %.2f\n", i
, item
->budget
[i
]) );
836 item
->flags
&= ~(GF_FORCED
); //delete flag
837 active
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data
->CM_force
));
839 item
->flags
|= GF_FORCED
;
841 if(budget
== TRUE
|| active
== 1)
842 item
->flags
|= GF_BUDGET
;
845 if( (old_flags
!= item
->flags
) || change
)
848 gtk_widget_queue_draw (data
->LV_cat
);
855 static void ui_bud_manage_selection_change(GtkWidget
*treeview
, gpointer user_data
)
857 struct ui_bud_manage_data
*data
;
861 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview
), GTK_TYPE_WINDOW
)), "inst_data");
863 DB( g_print("(ui_bud_manage) changed\n") );
867 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data
->LV_cat
)), &model
, &iter
))
871 gtk_tree_model_get(GTK_TREE_MODEL(model
), &iter
,
872 LST_DEFCAT_DATAS
, &item
,
875 DB( g_print(" selected %s\n", item
->name
) );
877 if(data
->lastcatitem
!= NULL
&& item
!= data
->lastcatitem
)
879 DB( g_print(" -> should do a get for last selected (%s)\n", data
->lastcatitem
->name
) );
880 ui_bud_manage_getlast(data
);
885 data
->lastcatitem
= item
;
887 ui_bud_manage_set(treeview
, NULL
);
891 data
->lastcatitem
= NULL
;
894 ui_bud_manage_update(treeview
, NULL
);
898 static void ui_bud_manage_toggle(GtkRadioButton
*radiobutton
, gpointer user_data
)
900 //struct ui_bud_manage_data *data;
902 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(radiobutton), GTK_TYPE_WINDOW)), "inst_data");
904 DB( g_print("(ui_bud_manage) toggle\n") );
906 //ui_bud_manage_get(GTK_WIDGET(radiobutton), GINT_TO_POINTER(FIELD_TYPE));
909 ui_bud_manage_update(GTK_WIDGET(radiobutton
), NULL
);
913 void ui_bud_manage_selection(GtkTreeSelection
*treeselection
, gpointer user_data
)
915 ui_bud_manage_selection_change(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection
)), NULL
);
919 static gboolean
ui_bud_manage_cleanup(struct ui_bud_manage_data
*data
, gint result
)
921 gboolean doupdate
= FALSE
;
923 DB( g_print("(ui_bud_manage) cleanup\n") );
925 if(data
->lastcatitem
!= NULL
)
927 DB( g_print(" -> should do a get for last selected (%s)\n", data
->lastcatitem
->name
) );
928 ui_bud_manage_getlast(data
);
931 //do_application_specific_something ();
932 DB( g_print(" accept\n") );
934 GLOBALS
->changes_count
+= data
->change
;
936 DB( g_print(" free tmp_list\n") );
941 static void ui_bud_manage_populate_listview(struct ui_bud_manage_data
*data
)
945 type
= hbtk_radio_get_active(GTK_CONTAINER(data
->RA_type
)) == 1 ? CAT_TYPE_INCOME
: CAT_TYPE_EXPENSE
;
947 ui_cat_listview_populate(data
->LV_cat
, type
);
948 gtk_tree_view_expand_all (GTK_TREE_VIEW(data
->LV_cat
));
952 static void ui_bud_manage_setup(struct ui_bud_manage_data
*data
)
955 DB( g_print("(ui_bud_manage) setup\n") );
957 data
->tmp_list
= NULL
;
960 data
->lastcatitem
= NULL
;
962 ui_bud_manage_populate_listview(data
);
967 static void ui_bud_manage_type_changed_cb (GtkToggleButton
*button
, gpointer user_data
)
969 ui_bud_manage_populate_listview(user_data
);
970 //g_print(" toggle type=%d\n", gtk_toggle_button_get_active(button));
977 GtkWidget
*ui_bud_manage_dialog (void)
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
;
990 memset(&data
, 0, sizeof(struct ui_bud_manage_data
));
992 dialog
= gtk_dialog_new_with_buttons (_("Manage Budget"),
993 GTK_WINDOW(GLOBALS
->mainwindow
),
999 data
.window
= dialog
;
1001 gtk_window_set_icon_name(GTK_WINDOW (dialog
), ICONNAME_HB_BUDGET
);
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
);
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
) );
1013 content_area
= gtk_dialog_get_content_area(GTK_DIALOG (dialog
)); // return a vbox
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);
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);
1028 hbtk_radio_connect (GTK_CONTAINER(bbox
), "toggled", G_CALLBACK (ui_bud_manage_type_changed_cb
), &data
);
1030 menu
= gtk_menu_new ();
1031 gtk_widget_set_halign (menu
, GTK_ALIGN_END
);
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
);
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
);
1041 gtk_widget_show_all (menu
);
1043 widget
= gtk_menu_button_new();
1044 image
= gtk_image_new_from_icon_name (ICONNAME_HB_BUTTON_MENU
, GTK_ICON_SIZE_MENU
);
1047 //gtk_image_get_icon_name(image, &thename, NULL);
1048 //g_print("the name is %s\n", thename);
1050 g_object_set (widget
, "image", image
, "popup", GTK_MENU(menu
), NULL
);
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);
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);
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
);
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);
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);
1086 gtk_style_context_add_class (gtk_widget_get_style_context (tbar
), GTK_STYLE_CLASS_INLINE_TOOLBAR
);
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);
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);
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);
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);
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
);
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);
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);
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
);
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
);
1143 g_signal_connect (G_OBJECT (data
.spinner
[0]), "value-changed", G_CALLBACK (ui_bud_manage_has_budget
), NULL
);
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
);
1155 button = gtk_button_new_with_label(_("Propagate"));
1156 gtk_grid_attach (GTK_GRID (group_grid), button, 1, 2, row, row+1);
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
);
1171 l
= ((i
<6) ? 1 : 3);
1172 t
= row
+ ((i
<6) ? i
: i
-6);
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);
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);
1184 g_signal_connect (G_OBJECT (data
.spinner
[i
+1]), "value-changed", G_CALLBACK (ui_bud_manage_has_budget
), NULL
);
1186 //DB( g_print("(ui_bud_manage) %s, col=%d, row=%d", CYA_ABMONTHS[i], col, row) );
1189 gtk_container_set_focus_chain(GTK_CONTAINER(group_grid
), fchain
);
1190 g_list_free(fchain
);
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);
1198 label
= make_label_group(_("Options"));
1199 data
.label_options
= label
;
1200 gtk_grid_attach (GTK_GRID (group_grid
), label
, 0, 0, 3, 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);
1209 //connect all our signals
1210 g_signal_connect (dialog
, "destroy",
1211 G_CALLBACK (gtk_widget_destroyed
), &dialog
);
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);
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
);
1220 data
.handler_id
[HID_CUSTOM
] = g_signal_connect (data
.CM_type
[0], "toggled", G_CALLBACK (ui_bud_manage_toggle
), NULL
);
1222 g_signal_connect (G_OBJECT (data
.BT_clear
), "clicked", G_CALLBACK (ui_bud_manage_clear
), NULL
);
1224 //data.custom = FALSE;
1225 //gtk_widget_set_sensitive(data.table, FALSE);
1227 //setup, init and show window
1228 ui_bud_manage_setup(&data
);
1229 ui_bud_manage_update(dialog
, NULL
);
1233 gtk_widget_show_all (dialog
);
1236 gint result
= gtk_dialog_run (GTK_DIALOG (dialog
));
1239 case GTK_RESPONSE_ACCEPT
:
1240 //do_application_specific_something ();
1243 //do_nothing_since_dialog_was_cancelled ();
1247 // cleanup and destroy
1248 ui_bud_manage_cleanup(&data
, result
);
1249 gtk_widget_destroy (dialog
);