1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2018 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"
33 /* our global datas */
34 extern struct HomeBank
*GLOBALS
;
35 extern struct Preferences
*PREFS
;
38 gchar
*CYA_CAT_TYPE
[] = {
44 static void ui_cat_manage_populate_listview(struct ui_cat_manage_dialog_data
*data
);
46 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
49 * ui_cat_comboboxentry_get_name:
51 * get the name of the active category or -1
53 * Return value: a new allocated name tobe freed with g_free
57 ui_cat_comboboxentry_get_name(GtkComboBox
*entry_box
)
62 DB( g_print ("ui_cat_comboboxentry_get_name()\n") );
64 cbname
= (gchar
*)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box
))));
67 name
= g_strdup(cbname
);
76 * ui_cat_comboboxentry_get_key:
78 * get the key of the active category or -1
80 * Return value: the key or -1
84 ui_cat_comboboxentry_get_key_add_new(GtkComboBox
*entry_box
)
89 DB( g_print ("ui_cat_comboboxentry_get_key()\n") );
91 name
= (gchar
*)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box
))));
96 item
= da_cat_get_by_fullname(name
);
100 //todo: check prefs + ask the user here 1st time
101 item
= da_cat_append_ifnew_by_fullname(name
, FALSE
);
103 ui_cat_comboboxentry_add(entry_box
, item
);
111 * ui_cat_comboboxentry_get_key:
113 * get the key of the active category or -1
115 * Return value: the key or -1
119 ui_cat_comboboxentry_get_key(GtkComboBox
*entry_box
)
124 DB( g_print ("ui_cat_comboboxentry_get_key()\n") );
126 name
= (gchar
*)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box
))));
130 item
= da_cat_get_by_fullname(name
);
139 *ui_cat_comboboxentry_get(GtkComboBox
*entry_box
)
141 Category
*item
= NULL
;
144 DB( g_print ("ui_cat_comboboxentry_get_key()\n") );
146 name
= (gchar
*)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box
))));
150 item
= da_cat_get_by_fullname(name
);
157 ui_cat_comboboxentry_set_active(GtkComboBox
*entry_box
, guint32 key
)
162 DB( g_print ("ui_cat_comboboxentry_set_active()\n") );
167 item
= da_cat_get(key
);
170 if( item
->parent
== 0)
171 gtk_entry_set_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box
))), item
->name
);
174 fullname
= da_cat_get_fullname(item
);
175 gtk_entry_set_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box
))), fullname
);
181 gtk_entry_set_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box
))), "");
186 * ui_cat_comboboxentry_add:
188 * Add a single element (useful for dynamics add)
194 ui_cat_comboboxentry_add(GtkComboBox
*entry_box
, Category
*item
)
197 DB( g_print ("ui_cat_comboboxentry_add()\n") );
200 DB( g_print (" -> try to add: '%s'\n", item
->name
) );
202 if( item
->name
!= NULL
)
207 gchar
*fullname
, *name
;
209 fullname
= da_cat_get_fullname(item
);
210 model
= gtk_combo_box_get_model(GTK_COMBO_BOX(entry_box
));
212 if( item
->parent
== 0 )
213 name
= g_strdup(item
->name
);
215 name
= g_strdup_printf(" - %s", item
->name
);
217 gtk_list_store_append (GTK_LIST_STORE(model
), &iter
);
218 gtk_list_store_set (GTK_LIST_STORE(model
), &iter
,
219 LST_CMBCAT_DATAS
, item
,
220 LST_CMBCAT_FULLNAME
, fullname
,
221 LST_CMBCAT_SORTNAME
, NULL
,
222 LST_CMBCAT_NAME
, name
,
223 LST_CMBCAT_SUBCAT
, item
->parent
== 0 ? 1 : 0,
234 ui_cat_comboboxentry_populate_ghfunc(gpointer key
, gpointer value
, struct catPopContext
*ctx
)
237 Category
*item
= value
;
238 Category
*pitem
= NULL
;
239 gchar
*fullname
, *name
, *sortname
;
242 if( ( item
->key
!= ctx
->except_key
) )
244 pitem
= da_cat_get(item
->parent
);
246 type
= (item
->flags
& GF_INCOME
) ? '+' : '-';
247 fullname
= da_cat_get_fullname(item
);
251 //DB( g_print ("cat combo populate [%d:%d] %s\n", item->parent, item->key, fullname) );
255 name
= g_strdup(item
->name
);
256 sortname
= g_strdup(item
->name
);
260 if( item
->parent
== 0 )
262 name
= g_strdup_printf("%s [%c]", item
->name
, type
);
263 sortname
= g_strdup_printf("%s", item
->name
);
269 name
= g_strdup_printf(" %c %s", type
, item
->name
);
270 sortname
= g_strdup_printf("%s_%s", pitem
->name
, item
->name
);
275 hb_string_replace_char(' ', sortname
);
277 //gtk_list_store_append (GTK_LIST_STORE(ctx->model), &iter);
278 //gtk_list_store_set (GTK_LIST_STORE(ctx->model), &iter,
279 gtk_list_store_insert_with_values(GTK_LIST_STORE(ctx
->model
), &iter
, -1,
280 LST_CMBCAT_DATAS
, item
,
281 LST_CMBCAT_FULLNAME
, fullname
,
282 LST_CMBCAT_SORTNAME
, sortname
,
283 LST_CMBCAT_NAME
, name
,
284 LST_CMBCAT_SUBCAT
, item
->parent
== 0 ? 1 : 0,
287 DB( g_print(" - add [%2d:%2d] '%-12s' '%-12s' '%s' '%s' %d\n", item
->parent
, item
->key
, pitem
->name
, name
, fullname
, sortname
, item
->parent
== 0 ? 1 : 0) );
297 * ui_cat_comboboxentry_populate:
299 * Populate the list and completion
305 ui_cat_comboboxentry_populate(GtkComboBox
*entry_box
, GHashTable
*hash
)
307 ui_cat_comboboxentry_populate_except(entry_box
, hash
, -1);
311 ui_cat_comboboxentry_populate_except(GtkComboBox
*entry_box
, GHashTable
*hash
, guint except_key
)
314 //GtkEntryCompletion *completion;
315 struct catPopContext ctx
;
317 DB( g_print ("ui_cat_comboboxentry_populate()\n") );
319 model
= gtk_combo_box_get_model(GTK_COMBO_BOX(entry_box
));
320 //completion = gtk_entry_get_completion(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box))));
322 /* keep our model alive and detach from comboboxentry and completion */
323 //g_object_ref(model);
324 //gtk_combo_box_set_model(GTK_COMBO_BOX(entry_box), NULL);
325 //gtk_entry_completion_set_model (completion, NULL);
327 /* clear and populate */
330 ctx
.except_key
= except_key
;
331 gtk_list_store_clear (GTK_LIST_STORE(model
));
333 //gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GTK_LIST_STORE(model)), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
335 g_hash_table_foreach(hash
, (GHFunc
)ui_cat_comboboxentry_populate_ghfunc
, &ctx
);
337 /* reatach our model */
338 //gtk_combo_box_set_model(GTK_COMBO_BOX(entry_box), model);
339 //gtk_entry_completion_set_model (completion, model);
340 //g_object_unref(model);
342 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model
), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID
, GTK_SORT_ASCENDING
);
349 ui_cat_comboboxentry_compare_func (GtkTreeModel
*model
, GtkTreeIter
*a
, GtkTreeIter
*b
, gpointer userdata
)
352 gchar
*name1
, *name2
;
354 gtk_tree_model_get(model
, a
,
355 LST_CMBCAT_SORTNAME
, &name1
,
357 gtk_tree_model_get(model
, b
,
358 LST_CMBCAT_SORTNAME
, &name2
,
361 //DB( g_print(" compare '%s' '%s'\n", name1, name2) );
363 retval
= hb_string_utf8_compare(name1
, name2
);
373 ui_cat_comboboxentry_test (GtkCellLayout
*cell_layout
,
374 GtkCellRenderer
*cell
,
375 GtkTreeModel
*tree_model
,
385 gtk_tree_model_get(tree_model
, iter
,
386 LST_CMBCAT_NAME
, &name
,
387 LST_CMBCAT_SUBCAT
, &subcat
,
390 style
= subcat
== 0 ? PANGO_STYLE_ITALIC
: PANGO_STYLE_NORMAL
;
393 name
= _("(no category)"); //todo: not used
408 ui_cat_comboboxentry_completion_func (GtkEntryCompletion
*completion
,
414 gchar
*normalized_string
;
415 gchar
*case_normalized_string
;
417 gboolean ret
= FALSE
;
421 model
= gtk_entry_completion_get_model (completion
);
423 gtk_tree_model_get (model
, iter
,
424 LST_CMBCAT_FULLNAME
, &item
,
429 normalized_string
= g_utf8_normalize (item
, -1, G_NORMALIZE_ALL
);
431 if (normalized_string
!= NULL
)
433 case_normalized_string
= g_utf8_casefold (normalized_string
, -1);
435 //g_print("match '%s' for '%s' ?\n", key, case_normalized_string);
436 //if (!strncmp (key, case_normalized_string, strlen (key)))
437 if (g_strstr_len (case_normalized_string
, strlen (case_normalized_string
), key
))
440 // g_print(" ==> yes !\n");
444 g_free (case_normalized_string
);
446 g_free (normalized_string
);
455 * ui_cat_comboboxentry_new:
457 * Create a new category comboboxentry
459 * Return value: the new widget
463 ui_cat_comboboxentry_new(GtkWidget
*label
)
466 GtkWidget
*comboboxentry
;
467 GtkEntryCompletion
*completion
;
468 GtkCellRenderer
*renderer
;
470 DB( g_print ("ui_cat_comboboxentry_new()\n") );
472 store
= gtk_list_store_new (NUM_LST_CMBCAT
,
474 G_TYPE_STRING
, //fullname Car:Fuel
475 G_TYPE_STRING
, //parent name Car
476 G_TYPE_STRING
, //name Car or Fuel
477 G_TYPE_BOOLEAN
//subcat = 1
479 gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store
), ui_cat_comboboxentry_compare_func
, NULL
, NULL
);
481 completion
= gtk_entry_completion_new ();
482 gtk_entry_completion_set_model (completion
, GTK_TREE_MODEL(store
));
483 //g_object_set(completion, "text-column", LST_CMBCAT_FULLNAME, NULL);
484 gtk_entry_completion_set_match_func(completion
, ui_cat_comboboxentry_completion_func
, NULL
, NULL
);
485 //gtk_entry_completion_set_minimum_key_length(completion, 2);
487 gtk_entry_completion_set_text_column(completion
, 1);
489 /*renderer = gtk_cell_renderer_text_new ();
490 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), renderer, TRUE);
491 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), renderer, "text", LST_CMBCAT_FULLNAME, NULL);
493 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion),
495 ui_cat_comboboxentry_test,
499 // dothe same for combobox
501 comboboxentry
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(store
));
502 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(comboboxentry
), LST_CMBCAT_FULLNAME
);
504 gtk_cell_layout_clear(GTK_CELL_LAYOUT (comboboxentry
));
506 renderer
= gtk_cell_renderer_text_new ();
507 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (comboboxentry
), renderer
, TRUE
);
508 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (comboboxentry
), renderer
, "text", LST_CMBCAT_FULLNAME
, NULL
);
510 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (comboboxentry
),
512 ui_cat_comboboxentry_test
,
515 gtk_entry_set_completion (GTK_ENTRY (gtk_bin_get_child(GTK_BIN (comboboxentry
))), completion
);
517 g_object_unref(store
);
520 gtk_label_set_mnemonic_widget (GTK_LABEL(label
), comboboxentry
);
522 gtk_widget_set_size_request(comboboxentry
, HB_MINWIDTH_LIST
, -1);
524 return comboboxentry
;
527 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
530 ui_cat_listview_fixed_toggled (GtkCellRendererToggle
*cell
,
534 GtkTreeModel
*model
= (GtkTreeModel
*)data
;
535 GtkTreeIter iter
, child
;
536 GtkTreePath
*path
= gtk_tree_path_new_from_string (path_str
);
540 /* get toggled iter */
541 gtk_tree_model_get_iter (model
, &iter
, path
);
542 gtk_tree_model_get (model
, &iter
, LST_DEFCAT_TOGGLE
, &fixed
, -1);
544 /* do something with the value */
548 gtk_tree_store_set (GTK_TREE_STORE (model
), &iter
, LST_DEFCAT_TOGGLE
, fixed
, -1);
550 /* propagate to child */
551 n_child
= gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model
), &iter
);
552 gtk_tree_model_iter_children (GTK_TREE_MODEL(model
), &child
, &iter
);
555 gtk_tree_store_set (GTK_TREE_STORE (model
), &child
, LST_DEFCAT_TOGGLE
, fixed
, -1);
558 gtk_tree_model_iter_next(GTK_TREE_MODEL(model
), &child
);
562 gtk_tree_path_free (path
);
567 ui_cat_listview_compare_func (GtkTreeModel
*model
, GtkTreeIter
*a
, GtkTreeIter
*b
, gpointer userdata
)
569 gint sortcol
= GPOINTER_TO_INT(userdata
);
570 Category
*entry1
, *entry2
;
573 gtk_tree_model_get(model
, a
, LST_DEFCAT_DATAS
, &entry1
, -1);
574 gtk_tree_model_get(model
, b
, LST_DEFCAT_DATAS
, &entry2
, -1);
578 case LST_DEFCAT_SORT_NAME
:
579 retval
= (entry1
->flags
& GF_INCOME
) - (entry2
->flags
& GF_INCOME
);
582 retval
= hb_string_utf8_compare(entry1
->name
, entry2
->name
);
585 case LST_DEFCAT_SORT_USED
:
586 retval
= entry1
->usage_count
- entry2
->usage_count
;
589 g_return_val_if_reached(0);
596 ** draw some text from the stored data structure
599 ui_cat_listview_text_cell_data_function (GtkTreeViewColumn
*col
,
600 GtkCellRenderer
*renderer
,
609 gtk_tree_model_get(model
, iter
, LST_DEFCAT_DATAS
, &entry
, -1);
611 name
= _("(no category)");
615 gchar type
= (entry
->flags
& GF_INCOME
) ? '+' : '-';
618 string
= g_markup_printf_escaped ("%d > [%d] %s [%c] %d", entry
->key
, entry
->parent
, name
, type
, entry
->flags
);
621 string
= g_strdup(name
);
624 if( entry
->parent
== 0 )
625 string
= g_markup_printf_escaped("%s [%c]", name
, type
);
627 string
= g_markup_printf_escaped(" %c <i>%s</i>", type
, name
);
628 //string = g_strdup_printf(" - %s", name);
632 //g_object_set(renderer, "text", string, NULL);
633 g_object_set(renderer
, "markup", string
, NULL
);
641 ui_cat_listview_count_cell_data_function (GtkTreeViewColumn
*col
,
642 GtkCellRenderer
*renderer
,
650 gtk_tree_model_get(model
, iter
, LST_DEFCAT_DATAS
, &entry
, -1);
651 if(entry
->usage_count
> 0)
653 g_snprintf(buffer
, 256-1, "%d", entry
->usage_count
);
654 g_object_set(renderer
, "text", buffer
, NULL
);
657 g_object_set(renderer
, "text", "", NULL
);
661 /* = = = = = = = = = = = = = = = = */
665 ui_cat_listview_add(GtkTreeView
*treeview
, Category
*item
, GtkTreeIter
*parent
)
671 DB( g_print ("ui_cat_listview_add()\n") );
673 if( item
->name
!= NULL
)
675 model
= gtk_tree_view_get_model(treeview
);
677 gtk_tree_store_append (GTK_TREE_STORE(model
), &iter
, parent
);
678 gtk_tree_store_set (GTK_TREE_STORE(model
), &iter
,
679 LST_DEFCAT_TOGGLE
, FALSE
,
680 LST_DEFCAT_DATAS
, item
,
681 LST_DEFCAT_NAME
, item
->name
,
684 //select the added line
686 path
= gtk_tree_model_get_path(model
, &iter
);
687 gtk_tree_view_expand_to_path (treeview
, path
);
688 gtk_tree_path_free(path
);
689 gtk_tree_selection_select_iter (gtk_tree_view_get_selection(treeview
), &iter
);
695 ui_cat_listview_get_selected(GtkTreeView
*treeview
)
697 GtkTreeSelection
*selection
;
701 DB( g_print ("ui_cat_listview_get_selected()\n") );
703 selection
= gtk_tree_view_get_selection(treeview
);
704 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
708 gtk_tree_model_get(model
, &iter
, LST_DEFCAT_DATAS
, &item
, -1);
716 ui_cat_listview_get_selected_parent(GtkTreeView
*treeview
, GtkTreeIter
*return_iter
)
718 GtkTreeSelection
*selection
;
724 DB( g_print ("ui_cat_listview_get_selected_parent()\n") );
727 selection
= gtk_tree_view_get_selection(treeview
);
728 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
730 path
= gtk_tree_model_get_path(model
, &iter
);
732 DB( g_print ("path depth = %d\n", gtk_tree_path_get_depth(path
)) );
735 if(gtk_tree_path_get_depth(path
) > 1)
737 if( gtk_tree_path_up(path
) )
740 DB( g_print ("up ok\n") );
742 if(gtk_tree_model_get_iter(model
, &iter
, path
))
745 DB( g_print ("iter ok\n") );
748 gtk_tree_model_get(model
, &iter
, LST_DEFCAT_DATAS
, &item
, -1);
760 DB( g_print ("path <=1\n") );
762 gtk_tree_model_get(model
, &iter
, LST_DEFCAT_DATAS
, &item
, -1);
776 gboolean
ui_cat_listview_remove (GtkTreeModel
*model
, guint32 key
)
778 GtkTreeIter iter
, child
;
779 gboolean valid
, cvalid
;
782 DB( g_print("ui_cat_listview_remove() \n") );
784 valid
= gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model
), &iter
);
787 gtk_tree_model_get (model
, &iter
, LST_DEFCAT_DATAS
, &item
, -1);
789 DB( g_print(" + item %p, %s\n", item
, item
->name
) );
791 if(item
->key
== key
|| item
->parent
== key
)
793 DB( g_print(" + removing cat %s\n", item
->name
) );
794 gtk_tree_store_remove(GTK_TREE_STORE(model
), &iter
);
798 cvalid
= gtk_tree_model_iter_children (GTK_TREE_MODEL(model
), &child
, &iter
);
801 gtk_tree_model_get(GTK_TREE_MODEL(model
), &child
, LST_DEFCAT_DATAS
, &item
, -1);
802 if(item
->key
== key
|| item
->parent
== key
)
804 DB( g_print(" + removing subcat %s\n", item
->name
) );
805 gtk_tree_store_remove(GTK_TREE_STORE(model
), &child
);
808 cvalid
= gtk_tree_model_iter_next(GTK_TREE_MODEL(model
), &child
);
810 valid
= gtk_tree_model_iter_next(GTK_TREE_MODEL(model
), &iter
);
819 ui_cat_listview_remove_selected(GtkTreeView
*treeview
)
821 GtkTreeSelection
*selection
;
825 DB( g_print("ui_cat_listview_remove_selected() \n") );
827 selection
= gtk_tree_view_get_selection(treeview
);
828 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
830 gtk_tree_store_remove(GTK_TREE_STORE(model
), &iter
);
836 ui_cat_listview_get_top_level (GtkTreeModel
*liststore
, guint32 key
, GtkTreeIter
*return_iter
)
842 DB( g_print("ui_cat_listview_get_top_level() \n") );
844 if( liststore
!= NULL
)
846 valid
= gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore
), &iter
);
849 gtk_tree_model_get (liststore
, &iter
, LST_DEFCAT_DATAS
, &item
, -1);
857 valid
= gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore
), &iter
);
865 static void ui_cat_listview_populate_cat_ghfunc(gpointer key
, gpointer value
, struct catPopContext
*ctx
)
867 GtkTreeIter toplevel
;
868 Category
*item
= value
;
871 item_type
= (item
->flags
& GF_INCOME
) ? CAT_TYPE_INCOME
: CAT_TYPE_EXPENSE
;
873 //DB( g_print("cat listview populate: %d %s\n", (guint32 *)key, item->name) );
874 if( (ctx
->type
== CAT_TYPE_ALL
) || ctx
->type
== item_type
|| item
->key
== 0 )
876 if( item
->parent
== 0 )
878 gtk_tree_store_append (GTK_TREE_STORE(ctx
->model
), &toplevel
, NULL
);
880 gtk_tree_store_set (GTK_TREE_STORE(ctx
->model
), &toplevel
,
881 LST_DEFCAT_TOGGLE
, FALSE
,
882 LST_DEFCAT_DATAS
, item
,
883 LST_DEFCAT_NAME
, item
->name
,
891 static void ui_cat_listview_populate_subcat_ghfunc(gpointer key
, gpointer value
, struct catPopContext
*ctx
)
893 GtkTreeIter toplevel
, child
;
894 Category
*item
= value
;
898 item_type
= (item
->flags
& GF_INCOME
) ? CAT_TYPE_INCOME
: CAT_TYPE_EXPENSE
;
900 if( (ctx
->type
== CAT_TYPE_ALL
) || ctx
->type
== item_type
)
902 if( item
->parent
!= 0 )
904 ret
= ui_cat_listview_get_top_level(ctx
->model
, item
->parent
, &toplevel
);
907 gtk_tree_store_append (GTK_TREE_STORE(ctx
->model
), &child
, &toplevel
);
909 gtk_tree_store_set (GTK_TREE_STORE(ctx
->model
), &child
,
910 LST_DEFCAT_TOGGLE
, FALSE
,
911 LST_DEFCAT_DATAS
, item
,
912 LST_DEFCAT_NAME
, item
->name
,
921 static void ui_cat_listview_sort_force(GtkTreeSortable
*sortable
, gpointer user_data
)
926 DB( g_print("ui_cat_listview_sort_force()\n") );
928 gtk_tree_sortable_get_sort_column_id(sortable
, &sort_column_id
, &order
);
929 DB( g_print(" - id %d order %d\n", sort_column_id
, order
) );
931 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable
), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID
, order
);
932 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable
), sort_column_id
, order
);
936 void ui_cat_listview_populate(GtkWidget
*view
, gint type
)
939 struct catPopContext ctx
= { 0 };
941 DB( g_print("ui_cat_listview_populate() \n") );
944 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(view
));
946 gtk_tree_store_clear (GTK_TREE_STORE(model
));
948 g_object_ref(model
); /* Make sure the model stays with us after the tree view unrefs it */
949 gtk_tree_view_set_model(GTK_TREE_VIEW(view
), NULL
); /* Detach model from view */
951 /* clear and populate */
955 /* we have to do this in 2 times to ensure toplevel (cat) will be added before childs */
956 /* populate cat 1st */
957 g_hash_table_foreach(GLOBALS
->h_cat
, (GHFunc
)ui_cat_listview_populate_cat_ghfunc
, &ctx
);
958 g_hash_table_foreach(GLOBALS
->h_cat
, (GHFunc
)ui_cat_listview_populate_subcat_ghfunc
, &ctx
);
961 gtk_tree_view_set_model(GTK_TREE_VIEW(view
), model
); /* Re-attach model to view */
962 g_object_unref(model
);
964 gtk_tree_view_expand_all (GTK_TREE_VIEW(view
));
969 static gboolean
ui_cat_listview_search_equal_func (GtkTreeModel
*model
,
973 gpointer search_data
)
975 gboolean retval
= TRUE
;
976 gchar
*normalized_string
;
977 gchar
*normalized_key
;
978 gchar
*case_normalized_string
= NULL
;
979 gchar
*case_normalized_key
= NULL
;
982 //gtk_tree_model_get_value (model, iter, column, &value);
983 gtk_tree_model_get(model
, iter
, LST_DEFCAT_DATAS
, &item
, -1);
987 normalized_string
= g_utf8_normalize (item
->name
, -1, G_NORMALIZE_ALL
);
988 normalized_key
= g_utf8_normalize (key
, -1, G_NORMALIZE_ALL
);
990 if (normalized_string
&& normalized_key
)
992 case_normalized_string
= g_utf8_casefold (normalized_string
, -1);
993 case_normalized_key
= g_utf8_casefold (normalized_key
, -1);
995 if (strncmp (case_normalized_key
, case_normalized_string
, strlen (case_normalized_key
)) == 0)
999 g_free (normalized_key
);
1000 g_free (normalized_string
);
1001 g_free (case_normalized_key
);
1002 g_free (case_normalized_string
);
1009 ui_cat_listview_new(gboolean withtoggle
, gboolean withcount
)
1011 GtkTreeStore
*store
;
1012 GtkWidget
*treeview
;
1013 GtkCellRenderer
*renderer
;
1014 GtkTreeViewColumn
*column
;
1016 DB( g_print("ui_cat_listview_new() \n") );
1018 store
= gtk_tree_store_new(
1025 treeview
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
1026 g_object_unref(store
);
1028 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview
), PREFS
->grid_lines
);
1032 if( withtoggle
== TRUE
)
1034 renderer
= gtk_cell_renderer_toggle_new ();
1035 column
= gtk_tree_view_column_new_with_attributes (_("Visible"),
1036 renderer
, "active", LST_DEFCAT_TOGGLE
, NULL
);
1037 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview
), column
);
1039 g_signal_connect (G_OBJECT(renderer
), "toggled",
1040 G_CALLBACK (ui_cat_listview_fixed_toggled
), store
);
1045 renderer
= gtk_cell_renderer_text_new ();
1046 g_object_set(renderer
,
1047 "ellipsize", PANGO_ELLIPSIZE_END
,
1048 "ellipsize-set", TRUE
,
1051 column
= gtk_tree_view_column_new();
1052 gtk_tree_view_column_set_title(column
, _("Name"));
1053 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
1054 gtk_tree_view_column_set_cell_data_func(column
, renderer
, ui_cat_listview_text_cell_data_function
, GINT_TO_POINTER(LST_DEFCAT_NAME
), NULL
);
1055 gtk_tree_view_column_set_alignment (column
, 0.5);
1056 gtk_tree_view_column_set_min_width(column
, HB_MINWIDTH_LIST
*2);
1057 gtk_tree_view_column_set_sort_column_id (column
, LST_DEFCAT_SORT_NAME
);
1058 gtk_tree_view_column_set_resizable(column
, TRUE
);
1059 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview
), column
);
1061 if( withcount
== TRUE
)
1063 column
= gtk_tree_view_column_new();
1064 gtk_tree_view_column_set_title(column
, _("Usage"));
1065 renderer
= gtk_cell_renderer_text_new ();
1066 g_object_set(renderer
, "xalign", 0.5, NULL
);
1067 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
1068 gtk_tree_view_column_set_cell_data_func(column
, renderer
, ui_cat_listview_count_cell_data_function
, GINT_TO_POINTER(LST_DEFCAT_DATAS
), NULL
);
1069 gtk_tree_view_column_set_alignment (column
, 0.5);
1070 gtk_tree_view_column_set_sort_column_id (column
, LST_DEFCAT_SORT_USED
);
1071 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview
), column
);
1075 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview
), ui_cat_listview_search_equal_func
, NULL
, NULL
);
1077 // treeview attribute
1078 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview
), withcount
);
1080 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store
), LST_DEFCAT_SORT_NAME
, ui_cat_listview_compare_func
, GINT_TO_POINTER(LST_DEFCAT_SORT_NAME
), NULL
);
1081 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store
), LST_DEFCAT_SORT_USED
, ui_cat_listview_compare_func
, GINT_TO_POINTER(LST_DEFCAT_SORT_USED
), NULL
);
1083 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store
), LST_DEFCAT_SORT_NAME
, GTK_SORT_ASCENDING
);
1089 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1092 * ui_cat_manage_filter_text_handler
1094 * filter to entry to avoid seizure of ':' char
1097 static void ui_cat_manage_filter_text_handler (GtkEntry
*entry
,
1103 GtkEditable
*editable
= GTK_EDITABLE(entry
);
1105 gchar
*result
= g_new0 (gchar
, length
+1);
1107 for (i
=0; i
< length
; i
++)
1111 result
[count
++] = text
[i
];
1116 g_signal_handlers_block_by_func (G_OBJECT (editable
),
1117 G_CALLBACK (ui_cat_manage_filter_text_handler
),
1119 gtk_editable_insert_text (editable
, result
, count
, position
);
1120 g_signal_handlers_unblock_by_func (G_OBJECT (editable
),
1121 G_CALLBACK (ui_cat_manage_filter_text_handler
),
1124 g_signal_stop_emission_by_name (G_OBJECT (editable
), "insert_text");
1131 ui_cat_manage_dialog_delete_unused( GtkWidget
*widget
, gpointer user_data
)
1133 struct ui_cat_manage_dialog_data
*data
= user_data
;
1136 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
1138 DB( g_print("(ui_cat_manage_dialog) delete unused - data %p\n", data
) );
1140 result
= ui_dialog_msg_confirm_alert(
1141 GTK_WINDOW(data
->window
),
1142 _("Delete unused categories"),
1143 _("Are you sure you want to permanently\ndelete unused categories?"),
1147 if( result
== GTK_RESPONSE_OK
)
1149 GtkTreeModel
*model
;
1151 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(data
->LV_cat
));
1152 gtk_tree_store_clear (GTK_TREE_STORE(model
));
1154 category_delete_unused();
1156 ui_cat_manage_populate_listview (data
);
1163 * ui_cat_manage_dialog_load_csv:
1167 ui_cat_manage_dialog_load_csv( GtkWidget
*widget
, gpointer user_data
)
1169 struct ui_cat_manage_dialog_data
*data
= user_data
;
1170 gchar
*filename
= NULL
;
1173 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
1175 DB( g_print("(ui_cat_manage_dialog) load csv - data %p\n", data
) );
1177 if( ui_file_chooser_csv(GTK_WINDOW(data
->window
), GTK_FILE_CHOOSER_ACTION_OPEN
, &filename
, NULL
) == TRUE
)
1179 DB( g_print(" + filename is %s\n", filename
) );
1181 if(!category_load_csv(filename
, &error
))
1183 ui_dialog_msg_infoerror(GTK_WINDOW(data
->window
), GTK_MESSAGE_ERROR
,
1184 _("File format error"),
1185 _("The CSV file must contains the exact numbers of column,\nseparated by a semi-colon, please see the help for more details.")
1190 ui_cat_manage_populate_listview(data
);
1196 * ui_cat_manage_dialog_save_csv:
1200 ui_cat_manage_dialog_save_csv( GtkWidget
*widget
, gpointer user_data
)
1202 struct ui_cat_manage_dialog_data
*data
= user_data
;
1203 gchar
*filename
= NULL
;
1206 DB( g_print("(defcategory) save csv\n") );
1208 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1210 if( ui_file_chooser_csv(GTK_WINDOW(data
->window
), GTK_FILE_CHOOSER_ACTION_SAVE
, &filename
, NULL
) == TRUE
)
1212 DB( g_print(" + filename is %s\n", filename
) );
1214 category_save_csv(filename
, &error
);
1220 * ui_cat_manage_dialog_add:
1222 * add an empty new category/subcategory
1226 ui_cat_manage_dialog_add(GtkWidget
*widget
, gpointer user_data
)
1228 struct ui_cat_manage_dialog_data
*data
;
1229 gboolean subcat
= GPOINTER_TO_INT(user_data
);
1231 //GtkTreeModel *model;
1232 GtkTreeIter parent_iter
;
1233 GtkWidget
*tmpwidget
;
1234 Category
*item
, *paritem
;
1237 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget
, GTK_TYPE_WINDOW
)), "inst_data");
1238 DB( g_print("\n(defcategory) add (data=%p) is subcat=%d\n", data
, subcat
) );
1240 tmpwidget
= (subcat
== FALSE
? data
->ST_name1
: data
->ST_name2
);
1241 name
= gtk_entry_get_text(GTK_ENTRY(tmpwidget
));
1242 //model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
1244 /* ignore if item is empty */
1249 item
= da_cat_malloc();
1250 item
->name
= g_strdup(name
);
1252 g_strstrip(item
->name
);
1254 /* if cat use new id */
1257 type
= radio_get_active(GTK_CONTAINER(data
->RA_type
));
1259 item
->flags
|= GF_INCOME
;
1261 if( da_cat_append(item
) )
1263 DB( g_print(" => add cat: %p %d, %s type=%d\n", item
, subcat
, item
->name
, type
) );
1264 ui_cat_listview_add(GTK_TREE_VIEW(data
->LV_cat
), item
, NULL
);
1267 /* if subcat use parent id & gf_income */
1270 paritem
= ui_cat_listview_get_selected_parent(GTK_TREE_VIEW(data
->LV_cat
), &parent_iter
);
1273 DB( g_print(" => selitem parent: %d, %s\n", paritem
->key
, paritem
->name
) );
1275 item
->parent
= paritem
->key
;
1276 item
->flags
|= (paritem
->flags
& GF_INCOME
);
1277 item
->flags
|= GF_SUB
;
1279 if(da_cat_append(item
))
1281 DB( g_print(" => add subcat: %p %d, %s\n", item
, subcat
, item
->name
) );
1282 ui_cat_listview_add(GTK_TREE_VIEW(data
->LV_cat
), item
, &parent_iter
);
1287 gtk_entry_set_text(GTK_ENTRY(tmpwidget
),"");
1292 static void ui_cat_manage_dialog_edit_entry_cb(GtkEditable
*editable
, gpointer user_data
)
1294 GtkDialog
*window
= user_data
;
1295 const gchar
*buffer
;
1297 buffer
= gtk_entry_get_text(GTK_ENTRY(editable
));
1298 gtk_dialog_set_response_sensitive(GTK_DIALOG(window
), GTK_RESPONSE_ACCEPT
, strlen(buffer
) > 0 ? TRUE
: FALSE
);
1302 static void ui_cat_manage_dialog_edit(GtkWidget
*widget
, gpointer user_data
)
1304 struct ui_cat_manage_dialog_data
*data
;
1305 GtkWidget
*dialog
, *content
, *mainvbox
, *w_name
, *w_type
= NULL
;
1306 GtkTreeSelection
*selection
;
1307 GtkTreeModel
*model
;
1310 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget
, GTK_TYPE_WINDOW
)), "inst_data");
1311 DB( g_print("\n(defcategory) edit\n") );
1313 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(data
->LV_cat
));
1314 //if true there is a selected node
1315 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
1319 gtk_tree_model_get(model
, &iter
, LST_DEFCAT_DATAS
, &item
, -1);
1321 dialog
= gtk_dialog_new_with_buttons (_("Edit..."),
1322 GTK_WINDOW (data
->window
),
1325 GTK_RESPONSE_REJECT
,
1327 GTK_RESPONSE_ACCEPT
,
1330 content
= gtk_dialog_get_content_area(GTK_DIALOG (dialog
));
1331 mainvbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 0);
1332 gtk_box_pack_start (GTK_BOX (content
), mainvbox
, TRUE
, TRUE
, 0);
1333 gtk_container_set_border_width (GTK_CONTAINER (mainvbox
), SPACING_MEDIUM
);
1335 w_name
= gtk_entry_new();
1336 gtk_box_pack_start (GTK_BOX (mainvbox
), w_name
, TRUE
, TRUE
, 0);
1338 gtk_entry_set_text(GTK_ENTRY(w_name
), item
->name
);
1339 gtk_widget_grab_focus (w_name
);
1341 gtk_entry_set_activates_default (GTK_ENTRY(w_name
), TRUE
);
1343 if(!(item
->flags
& GF_SUB
))
1345 w_type
= gtk_check_button_new_with_mnemonic(_("_Income"));
1346 gtk_box_pack_start (GTK_BOX (mainvbox
), w_type
, TRUE
, TRUE
, 0);
1347 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_type
), item
->flags
& GF_INCOME
? TRUE
: FALSE
);
1350 g_signal_connect (G_OBJECT (w_name
), "changed", G_CALLBACK (ui_cat_manage_dialog_edit_entry_cb
), dialog
);
1353 gtk_widget_show_all(mainvbox
);
1355 gtk_dialog_set_default_response(GTK_DIALOG( dialog
), GTK_RESPONSE_ACCEPT
);
1358 gint result
= gtk_dialog_run (GTK_DIALOG (dialog
));
1360 if(result
== GTK_RESPONSE_ACCEPT
)
1364 // 1: manage renaming
1365 name
= gtk_entry_get_text(GTK_ENTRY(w_name
));
1366 // ignore if item is empty
1369 if( category_rename(item
, name
) )
1371 //to redraw the active entry
1372 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data
->LV_cat
));
1378 gchar
*fromname
, *toname
= NULL
;
1380 fromname
= da_cat_get_fullname(item
);
1382 if( item
->parent
== 0)
1383 toname
= g_strdup(name
);
1386 parent
= da_cat_get(item
->parent
);
1389 toname
= g_strdup_printf("%s:%s", parent
->name
, name
);
1394 ui_dialog_msg_infoerror(GTK_WINDOW(dialog
), GTK_MESSAGE_ERROR
,
1396 _("Cannot rename this Category,\n"
1397 "from '%s' to '%s',\n"
1398 "this name already exists."),
1409 // 2: manage flag change
1410 if(!(item
->flags
& GF_SUB
))
1414 isIncome
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w_type
));
1415 data
->change
+= category_change_type(item
, isIncome
);
1418 ui_cat_listview_sort_force(GTK_TREE_SORTABLE(model
), NULL
);
1421 // cleanup and destroy
1422 gtk_widget_destroy (dialog
);
1428 static void ui_cat_manage_dialog_merge_entry_cb(GtkComboBox
*widget
, gpointer user_data
)
1430 GtkDialog
*window
= user_data
;
1433 buffer
= (gchar
*)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (widget
))));
1434 gtk_dialog_set_response_sensitive(GTK_DIALOG(window
), GTK_RESPONSE_OK
, strlen(buffer
) > 0 ? TRUE
: FALSE
);
1438 static void ui_cat_manage_dialog_merge(GtkWidget
*widget
, gpointer user_data
)
1440 struct ui_cat_manage_dialog_data
*data
;
1441 GtkWidget
*dialog
, *content
, *mainvbox
;
1442 GtkWidget
*getwidget
, *togglebutton
;
1443 GtkTreeSelection
*selection
;
1444 GtkTreeModel
*model
;
1447 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget
, GTK_TYPE_WINDOW
)), "inst_data");
1448 DB( g_print("(defcategory) merge\n") );
1450 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(data
->LV_cat
));
1451 //if true there is a selected node
1452 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
1458 gtk_tree_model_get(model
, &iter
, LST_DEFCAT_DATAS
, &srccat
, -1);
1460 title
= g_strdup_printf (
1461 _("Merge category '%s'"), srccat
->name
);
1463 dialog
= gtk_message_dialog_new (GTK_WINDOW (data
->window
),
1464 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
1465 GTK_MESSAGE_WARNING
,
1471 gtk_dialog_add_buttons (GTK_DIALOG(dialog
),
1472 _("_Cancel"), GTK_RESPONSE_CANCEL
,
1473 _("Merge"), GTK_RESPONSE_OK
,
1476 gtk_dialog_set_default_response(GTK_DIALOG (dialog
), GTK_RESPONSE_CANCEL
);
1478 content
= gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG (dialog
));
1479 mainvbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, SPACING_SMALL
);
1480 gtk_box_pack_start (GTK_BOX (content
), mainvbox
, TRUE
, TRUE
, 0);
1482 secondtext
= _("Transactions assigned to this category,\n"
1483 "will be moved to the category selected below.");
1485 g_object_set(GTK_MESSAGE_DIALOG (dialog
), "secondary-text", secondtext
, NULL
);
1488 getwidget
= ui_cat_comboboxentry_new(NULL
);
1489 gtk_box_pack_start (GTK_BOX (mainvbox
), getwidget
, FALSE
, FALSE
, 0);
1491 secondtext
= g_strdup_printf (
1492 _("_Delete the category '%s'"), srccat
->name
);
1493 togglebutton
= gtk_check_button_new_with_mnemonic(secondtext
);
1494 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton
), TRUE
);
1496 gtk_box_pack_start (GTK_BOX (mainvbox
), togglebutton
, FALSE
, FALSE
, 0);
1499 //gtk_combo_box_set_active(GTK_COMBO_BOX(getwidget), oldpos);
1500 g_signal_connect (G_OBJECT (getwidget
), "changed", G_CALLBACK (ui_cat_manage_dialog_merge_entry_cb
), dialog
);
1501 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog
), GTK_RESPONSE_OK
, FALSE
);
1503 ui_cat_comboboxentry_populate_except(GTK_COMBO_BOX(getwidget
), GLOBALS
->h_cat
, srccat
->key
);
1504 gtk_widget_grab_focus (getwidget
);
1506 gtk_widget_show_all(mainvbox
);
1509 gint result
= gtk_dialog_run (GTK_DIALOG (dialog
));
1511 if(result
== GTK_RESPONSE_OK
)
1513 GtkTreeModel
*model
;
1514 Category
*newcat
, *parent
;
1518 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(data
->LV_cat
));
1519 gtk_tree_store_clear (GTK_TREE_STORE(model
));
1521 dstcatkey
= ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(getwidget
));
1523 DB( g_print(" -> move cat to %d\n", dstcatkey
) );
1525 category_move(srccat
->key
, dstcatkey
);
1527 newcat
= da_cat_get (dstcatkey
);
1529 //keep the income type with us
1530 parent
= da_cat_get(srccat
->parent
);
1531 if(parent
!= NULL
&& (parent
->flags
& GF_INCOME
))
1532 newcat
->flags
|= GF_INCOME
;
1534 //add the new category into listview
1536 ui_cat_listview_add(GTK_TREE_VIEW(data
->LV_cat
), newcat
, NULL
);
1538 // delete the old category
1539 if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton
)) )
1541 DB( g_print(" -> delete %d '%s'\n", srccat
->key
, srccat
->name
) );
1543 da_cat_remove(srccat
->key
);
1544 ui_cat_listview_remove_selected(GTK_TREE_VIEW(data
->LV_cat
));
1550 ui_cat_manage_populate_listview(data
);
1554 // cleanup and destroy
1555 gtk_widget_destroy (dialog
);
1562 ** delete the selected payee to our treeview and temp GList
1564 static void ui_cat_manage_dialog_delete(GtkWidget
*widget
, gpointer user_data
)
1566 struct ui_cat_manage_dialog_data
*data
;
1570 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget
, GTK_TYPE_WINDOW
)), "inst_data");
1571 DB( g_print("\n(defcategory) delete (data=%x)\n", (guint
)data
) );
1573 item
= ui_cat_listview_get_selected(GTK_TREE_VIEW(data
->LV_cat
));
1576 gchar
*title
= NULL
;
1577 gchar
*secondtext
= NULL
;
1579 title
= g_strdup_printf (
1580 _("Are you sure you want to permanently delete '%s'?"), item
->name
);
1582 if( item
->usage_count
> 0 )
1584 secondtext
= _("This category is used.\n"
1585 "Any transaction using that category will be set to (no category)");
1588 result
= ui_dialog_msg_confirm_alert(
1589 GTK_WINDOW(data
->window
),
1597 if( result
== GTK_RESPONSE_OK
)
1599 category_move(item
->key
, 0);
1600 ui_cat_listview_remove(gtk_tree_view_get_model(GTK_TREE_VIEW(data
->LV_cat
)), item
->key
);
1601 da_cat_remove(item
->key
);
1610 static void ui_cat_manage_dialog_expand_all(GtkWidget
*widget
, gpointer user_data
)
1612 struct ui_cat_manage_dialog_data
*data
;
1614 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget
, GTK_TYPE_WINDOW
)), "inst_data");
1615 DB( g_print("\n(defcategory) expand all (data=%x)\n", (guint
)data
) );
1617 gtk_tree_view_expand_all(GTK_TREE_VIEW(data
->LV_cat
));
1622 static void ui_cat_manage_dialog_collapse_all(GtkWidget
*widget
, gpointer user_data
)
1624 struct ui_cat_manage_dialog_data
*data
;
1626 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget
, GTK_TYPE_WINDOW
)), "inst_data");
1627 DB( g_print("\n(defcategory) collapse all (data=%x)\n", (guint
)data
) );
1629 gtk_tree_view_collapse_all(GTK_TREE_VIEW(data
->LV_cat
));
1634 static void ui_cat_manage_dialog_update(GtkWidget
*treeview
, gpointer user_data
)
1636 struct ui_cat_manage_dialog_data
*data
;
1638 gboolean selected
, sensitive
;
1639 GtkTreeSelection
*selection
;
1640 GtkTreeModel
*model
;
1644 gboolean haschild
= FALSE
;
1646 DB( g_print("ui_cat_manage_dialog_update()\n") );
1648 data
= g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview
), GTK_TYPE_WINDOW
)), "inst_data");
1649 //window = gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW);
1650 //DB( g_print("(defpayee) widget=%08lx, window=%08lx, inst_data=%08lx\n", treeview, window, data) );
1652 //if true there is a selected node
1653 selected
= gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data
->LV_cat
)), &model
, &iter
);
1655 DB( g_print(" selected = %d\n", selected
) );
1660 gtk_tree_model_get_iter_first(model
, &iter
);
1661 if(gtk_tree_selection_iter_is_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data
->LV_cat
)), &iter
))
1663 DB( g_print(" 0 active = %d\n", 1) );
1668 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
));
1670 //count = gtk_tree_selection_count_selected_rows(selection);
1671 //DB( g_print(" => select count=%d\n", count) );
1674 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
1676 gchar
*tree_path_str
;
1679 gtk_tree_model_get(GTK_TREE_MODEL(model
), &iter
,
1680 LST_DEFCAT_DATAS
, &item
,
1683 haschild
= gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model
), &iter
);
1684 DB( g_print(" => has child=%d\n", haschild
) );
1687 path
= gtk_tree_model_get_path(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview
)), &iter
);
1688 tree_path_str
= gtk_tree_path_to_string(path
);
1689 DB( g_print(" => select is=%s, depth=%d (id=%d, %s) flags=%d\n",
1691 gtk_tree_path_get_depth(path
),
1696 g_free(tree_path_str
);
1698 //get parent if subcategory selectd
1699 DB( g_print(" => get parent for title\n") );
1700 if(gtk_tree_path_get_depth(path
) != 1)
1701 gtk_tree_path_up(path
);
1703 if(gtk_tree_model_get_iter(model
, &iter
, path
))
1707 gtk_tree_model_get(GTK_TREE_MODEL(model
), &iter
,
1708 LST_DEFCAT_DATAS
, &tmpitem
,
1711 if(tmpitem
->key
> 0)
1712 category
= tmpitem
->name
;
1714 DB( g_print(" => parent is %s\n", category
) );
1718 gtk_tree_path_free(path
);
1722 gtk_label_set_text(GTK_LABEL(data
->LA_category
), category
);
1724 sensitive
= (selected
== TRUE
) ? TRUE
: FALSE
;
1725 gtk_widget_set_sensitive(data
->ST_name2
, sensitive
);
1726 gtk_widget_set_sensitive(data
->BT_edit
, sensitive
);
1727 gtk_widget_set_sensitive(data
->BT_merge
, sensitive
);
1729 //avoid removing top categories
1730 sensitive
= (haschild
== TRUE
) ? FALSE
: sensitive
;
1732 gtk_widget_set_sensitive(data
->BT_delete
, sensitive
);
1739 static void ui_cat_manage_dialog_selection(GtkTreeSelection
*treeselection
, gpointer user_data
)
1741 ui_cat_manage_dialog_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection
)), NULL
);
1744 static void ui_cat_manage_dialog_onRowActivated (GtkTreeView
*treeview
,
1746 GtkTreeViewColumn
*col
,
1749 GtkTreeModel
*model
;
1752 DB( g_print("ui_cat_manage_dialog_onRowActivated()\n") );
1755 model
= gtk_tree_view_get_model(treeview
);
1756 gtk_tree_model_get_iter_first(model
, &iter
);
1757 if(gtk_tree_selection_iter_is_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview
)), &iter
) == FALSE
)
1759 ui_cat_manage_dialog_edit(GTK_WIDGET(treeview
), NULL
);
1764 static gboolean
ui_cat_manage_dialog_cleanup(struct ui_cat_manage_dialog_data
*data
, gint result
)
1766 gboolean doupdate
= FALSE
;
1768 DB( g_print("(defcategory) cleanup\n") );
1770 if(result
== GTK_RESPONSE_ACCEPT
)
1773 //do_application_specific_something ();
1774 DB( g_print(" accept\n") );
1777 GLOBALS
->changes_count
+= data
->change
;
1780 DB( g_print(" free tmp_list\n") );
1782 //da_category_destroy(data->tmp_list);
1788 static void ui_cat_manage_populate_listview(struct ui_cat_manage_dialog_data
*data
)
1792 type
= radio_get_active(GTK_CONTAINER(data
->RA_type
)) == 1 ? CAT_TYPE_INCOME
: CAT_TYPE_EXPENSE
;
1793 ui_cat_listview_populate(data
->LV_cat
, type
);
1794 gtk_tree_view_expand_all (GTK_TREE_VIEW(data
->LV_cat
));
1801 static void ui_cat_manage_dialog_setup(struct ui_cat_manage_dialog_data
*data
)
1804 DB( g_print("(defcategory) setup\n") );
1807 data
->tmp_list
= NULL
; //data->tmp_list = hb-glist_clone_list(GLOBALS->cat_list, sizeof(struct _Group));
1811 //da_cat_debug_list();
1813 ui_cat_manage_populate_listview(data
);
1818 static void ui_cat_manage_type_changed_cb (GtkToggleButton
*button
, gpointer user_data
)
1820 ui_cat_manage_populate_listview(user_data
);
1821 //g_print(" toggle type=%d\n", gtk_toggle_button_get_active(button));
1825 GtkWidget
*ui_cat_manage_dialog (void)
1827 struct ui_cat_manage_dialog_data data
;
1828 GtkWidget
*window
, *content
, *mainvbox
, *bbox
, *table
, *hbox
, *vbox
, *label
, *scrollwin
, *treeview
;
1829 GtkWidget
*menu
, *menuitem
, *widget
, *image
, *tbar
;
1830 GtkToolItem
*toolitem
;
1833 window
= gtk_dialog_new_with_buttons (_("Manage Categories"),
1834 GTK_WINDOW(GLOBALS
->mainwindow
),
1837 GTK_RESPONSE_ACCEPT
,
1840 data
.window
= window
;
1843 gtk_window_set_icon_name(GTK_WINDOW (window
), ICONNAME_HB_CATEGORY
);
1845 //set a nice dialog size
1846 gtk_window_get_size(GTK_WINDOW(GLOBALS
->mainwindow
), &w
, &h
);
1847 gtk_window_set_default_size (GTK_WINDOW(window
), -1, h
/PHI
);
1850 //store our window private data
1851 g_object_set_data(G_OBJECT(window
), "inst_data", (gpointer
)&data
);
1852 DB( g_print("(defcategory) window=%x, inst_data=%x\n", (guint
)window
, (guint
)&data
) );
1854 g_signal_connect (window
, "destroy",
1855 G_CALLBACK (gtk_widget_destroyed
), &window
);
1858 content
= gtk_dialog_get_content_area(GTK_DIALOG (window
));
1859 mainvbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, SPACING_SMALL
);
1860 gtk_box_pack_start (GTK_BOX (content
), mainvbox
, TRUE
, TRUE
, 0);
1861 gtk_container_set_border_width (GTK_CONTAINER(mainvbox
), SPACING_MEDIUM
);
1864 table
= gtk_grid_new ();
1865 gtk_grid_set_row_spacing (GTK_GRID (table
), SPACING_SMALL
);
1866 gtk_grid_set_column_spacing (GTK_GRID (table
), SPACING_MEDIUM
);
1867 gtk_box_pack_start (GTK_BOX (mainvbox
), table
, TRUE
, TRUE
, 0);
1870 bbox
= make_radio(CYA_CAT_TYPE
, TRUE
, GTK_ORIENTATION_HORIZONTAL
);
1871 data
.RA_type
= bbox
;
1872 gtk_widget_set_halign (bbox
, GTK_ALIGN_CENTER
);
1873 gtk_grid_attach (GTK_GRID (table
), bbox
, 0, row
, 2, 1);
1875 widget
= radio_get_nth_widget(GTK_CONTAINER(bbox
), 1);
1877 g_signal_connect (widget
, "toggled", G_CALLBACK (ui_cat_manage_type_changed_cb
), &data
);
1879 menu
= gtk_menu_new ();
1880 gtk_widget_set_halign (menu
, GTK_ALIGN_END
);
1882 menuitem
= gtk_menu_item_new_with_mnemonic (_("_Import CSV"));
1883 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
1884 g_signal_connect (G_OBJECT (menuitem
), "activate", G_CALLBACK (ui_cat_manage_dialog_load_csv
), &data
);
1886 menuitem
= gtk_menu_item_new_with_mnemonic (_("E_xport CSV"));
1887 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
1888 g_signal_connect (G_OBJECT (menuitem
), "activate", G_CALLBACK (ui_cat_manage_dialog_save_csv
), &data
);
1890 menuitem
= gtk_separator_menu_item_new();
1891 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
1893 menuitem
= gtk_menu_item_new_with_mnemonic (_("_Delete unused"));
1894 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
1895 g_signal_connect (G_OBJECT (menuitem
), "activate", G_CALLBACK (ui_cat_manage_dialog_delete_unused
), &data
);
1897 gtk_widget_show_all (menu
);
1899 widget
= gtk_menu_button_new();
1900 image
= gtk_image_new_from_icon_name (ICONNAME_HB_BUTTON_MENU
, GTK_ICON_SIZE_MENU
);
1903 //gtk_image_get_icon_name(image, &thename, NULL);
1904 //g_print("the name is %s\n", thename);
1906 g_object_set (widget
, "image", image
, "popup", GTK_MENU(menu
), NULL
);
1907 gtk_widget_set_halign (widget
, GTK_ALIGN_END
);
1908 gtk_grid_attach (GTK_GRID (table
), widget
, 1, row
, 1, 1);
1911 widget
= gtk_entry_new ();
1912 data
.ST_name1
= widget
;
1913 gtk_entry_set_placeholder_text(GTK_ENTRY(data
.ST_name1
), _("new category") );
1914 gtk_widget_set_hexpand (widget
, TRUE
);
1915 gtk_grid_attach (GTK_GRID (table
), widget
, 0, row
, 2, 1);
1917 // subcategory + add button
1919 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, SPACING_SMALL
);
1920 gtk_grid_attach (GTK_GRID (table
), hbox
, 0, row
, 2, 1);
1921 data
.LA_category
= gtk_label_new(NULL
);
1922 gtk_box_pack_start (GTK_BOX (hbox
), data
.LA_category
, FALSE
, FALSE
, 0);
1923 label
= gtk_label_new(":");
1924 gtk_box_pack_start (GTK_BOX (hbox
), label
, FALSE
, FALSE
, 0);
1925 data
.ST_name2
= gtk_entry_new ();
1926 gtk_entry_set_placeholder_text(GTK_ENTRY(data
.ST_name2
), _("new subcategory") );
1927 gtk_box_pack_start (GTK_BOX (hbox
), data
.ST_name2
, TRUE
, TRUE
, 0);
1931 vbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 0);
1932 gtk_grid_attach (GTK_GRID (table
), vbox
, 0, row
, 2, 1);
1934 scrollwin
= gtk_scrolled_window_new(NULL
,NULL
);
1935 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin
), GTK_SHADOW_ETCHED_IN
);
1936 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
1937 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin
), HB_MINHEIGHT_LIST
);
1938 treeview
= ui_cat_listview_new(FALSE
, TRUE
);
1939 data
.LV_cat
= treeview
;
1940 gtk_container_add(GTK_CONTAINER(scrollwin
), treeview
);
1941 gtk_widget_set_hexpand (scrollwin
, TRUE
);
1942 gtk_widget_set_vexpand (scrollwin
, TRUE
);
1943 gtk_box_pack_start (GTK_BOX(vbox
), scrollwin
, TRUE
, TRUE
, 0);
1946 tbar
= gtk_toolbar_new();
1947 gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar
), GTK_ICON_SIZE_MENU
);
1948 gtk_toolbar_set_style(GTK_TOOLBAR(tbar
), GTK_TOOLBAR_ICONS
);
1949 gtk_style_context_add_class (gtk_widget_get_style_context (tbar
), GTK_STYLE_CLASS_INLINE_TOOLBAR
);
1950 gtk_box_pack_start (GTK_BOX (vbox
), tbar
, FALSE
, FALSE
, 0);
1952 /*widget = gtk_tool_item_new ();
1953 label = gtk_label_new("test");
1954 gtk_container_add(GTK_CONTAINER(widget), label);
1955 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(widget), -1);*/
1957 //hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
1959 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1960 toolitem = gtk_tool_item_new();
1961 gtk_container_add (GTK_CONTAINER(toolitem), hbox);
1962 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
1964 //widget = make_image_button("text-editor-symbolic", _("Edit"));
1965 widget = gtk_button_new_with_mnemonic(_("_Edit"));
1966 data.BT_edit = widget;
1967 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1969 //widget = make_image_button("merge-symbolic", _("Merge"));
1970 widget = gtk_button_new_with_mnemonic(_("_Merge"));
1971 data.BT_merge = widget;
1972 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1974 //widget = make_image_button(ICONNAME_SYM_EDIT_DELETE, _("Delete"));
1975 widget = gtk_button_new_with_mnemonic(_("_Delete"));
1976 data.BT_delete = widget;
1977 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1980 toolitem
= gtk_separator_tool_item_new ();
1981 gtk_tool_item_set_expand (toolitem
, TRUE
);
1982 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem
), FALSE
);
1983 gtk_toolbar_insert(GTK_TOOLBAR(tbar
), GTK_TOOL_ITEM(toolitem
), -1);
1985 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1986 toolitem
= gtk_tool_item_new();
1987 gtk_container_add (GTK_CONTAINER(toolitem
), hbox
);
1988 gtk_toolbar_insert(GTK_TOOLBAR(tbar
), GTK_TOOL_ITEM(toolitem
), -1);
1990 widget
= make_image_button(ICONNAME_HB_BUTTON_EXPAND
, _("Expand all"));
1991 data
.BT_expand
= widget
;
1992 gtk_box_pack_start (GTK_BOX (hbox
), widget
, FALSE
, FALSE
, 0);
1994 widget
= make_image_button(ICONNAME_HB_BUTTON_COLLAPSE
, _("Collapse all"));
1995 data
.BT_collapse
= widget
;
1996 gtk_box_pack_start (GTK_BOX (hbox
), widget
, FALSE
, FALSE
, 0);
2000 bbox
= gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL
);
2001 gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox
), GTK_BUTTONBOX_START
);
2002 gtk_box_set_spacing (GTK_BOX (bbox
), SPACING_SMALL
);
2003 gtk_grid_attach (GTK_GRID (table
), bbox
, 0, row
, 2, 1);
2005 data
.BT_edit
= gtk_button_new_with_mnemonic(_("_Edit"));
2006 gtk_container_add (GTK_CONTAINER (bbox
), data
.BT_edit
);
2008 data
.BT_merge
= gtk_button_new_with_mnemonic(_("_Merge"));
2009 gtk_container_add (GTK_CONTAINER (bbox
), data
.BT_merge
);
2011 data
.BT_delete
= gtk_button_new_with_mnemonic(_("_Delete"));
2012 gtk_container_add (GTK_CONTAINER (bbox
), data
.BT_delete
);
2016 widget = gtk_check_button_new_with_mnemonic(_("I_ncome"));
2017 data.CM_type = widget;
2018 gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);*/
2021 //connect all our signals
2022 g_signal_connect (G_OBJECT (data
.ST_name1
), "activate", G_CALLBACK (ui_cat_manage_dialog_add
), GINT_TO_POINTER(FALSE
));
2023 g_signal_connect (G_OBJECT (data
.ST_name2
), "activate", G_CALLBACK (ui_cat_manage_dialog_add
), GINT_TO_POINTER(TRUE
));
2025 g_signal_connect(G_OBJECT(data
.ST_name1
), "insert-text", G_CALLBACK(ui_cat_manage_filter_text_handler
), NULL
);
2026 g_signal_connect(G_OBJECT(data
.ST_name2
), "insert-text", G_CALLBACK(ui_cat_manage_filter_text_handler
), NULL
);
2029 g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data
.LV_cat
)), "changed", G_CALLBACK (ui_cat_manage_dialog_selection
), NULL
);
2030 g_signal_connect (GTK_TREE_VIEW(data
.LV_cat
), "row-activated", G_CALLBACK (ui_cat_manage_dialog_onRowActivated
), NULL
);
2032 g_signal_connect (G_OBJECT (data
.BT_edit
), "clicked", G_CALLBACK (ui_cat_manage_dialog_edit
), NULL
);
2033 g_signal_connect (G_OBJECT (data
.BT_merge
), "clicked", G_CALLBACK (ui_cat_manage_dialog_merge
), NULL
);
2034 g_signal_connect (G_OBJECT (data
.BT_delete
), "clicked", G_CALLBACK (ui_cat_manage_dialog_delete
), NULL
);
2036 g_signal_connect (G_OBJECT (data
.BT_expand
), "clicked", G_CALLBACK (ui_cat_manage_dialog_expand_all
), NULL
);
2037 g_signal_connect (G_OBJECT (data
.BT_collapse
), "clicked", G_CALLBACK (ui_cat_manage_dialog_collapse_all
), NULL
);
2039 //setup, init and show window
2040 category_fill_usage();
2041 ui_cat_manage_dialog_setup(&data
);
2042 ui_cat_manage_dialog_update(data
.LV_cat
, NULL
);
2044 gtk_widget_show_all (window
);
2047 gint result
= gtk_dialog_run (GTK_DIALOG (window
));
2051 case GTK_RESPONSE_ACCEPT
:
2052 //do_application_specific_something ();
2055 //do_nothing_since_dialog_was_cancelled ();
2059 // cleanup and destroy
2060 ui_cat_manage_dialog_cleanup(&data
, result
);
2061 gtk_widget_destroy (window
);
2063 GLOBALS
->changes_count
+= data
.change
;