1 /* ************************************************************************** */
2 /* */
3 /* Copyright (C) 2000-2008 Cédric Auger (cedric@grisbi.org) */
4 /* 2004-2008 Benjamin Drieu (bdrieu@april.org) */
5 /* 2009-2011 Pierre Biava (grisbi@pierre.biava.name) */
6 /* https://www.grisbi.org/ */
7 /* */
8 /* This program is free software; you can redistribute it and/or modify */
9 /* it under the terms of the GNU General Public License as published by */
10 /* the Free Software Foundation; either version 2 of the License, or */
11 /* (at your option) any later version. */
12 /* */
13 /* This program is distributed in the hope that it will be useful, */
14 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
15 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
16 /* GNU General Public License for more details. */
17 /* */
18 /* You should have received a copy of the GNU General Public License */
19 /* along with this program; if not, write to the Free Software */
20 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
21 /* */
22 /* ************************************************************************** */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "include.h"
29 #include <glib/gi18n.h>
30
31 /*START_INCLUDE*/
32 #include "categories_onglet.h"
33 #include "dialog.h"
34 #include "grisbi_app.h"
35 #include "gsb_autofunc.h"
36 #include "gsb_automem.h"
37 #include "gsb_data_category.h"
38 #include "gsb_data_transaction.h"
39 #include "gsb_file.h"
40 #include "gsb_file_others.h"
41 #include "gsb_transactions_list.h"
42 #include "meta_categories.h"
43 #include "metatree.h"
44 #include "mouse.h"
45 #include "structures.h"
46 #include "traitement_variables.h"
47 #include "transaction_list.h"
48 #include "utils.h"
49 #include "utils_buttons.h"
50 #include "utils_files.h"
51 #include "utils_str.h"
52 #include "erreur.h"
53 /*END_INCLUDE*/
54
55 /*START_STATIC*/
56 static void appui_sur_ajout_category ( GtkTreeModel *model,
57 GtkButton *button );
58 static gboolean categ_drag_data_get ( GtkTreeDragSource *drag_source,
59 GtkTreePath *path,
60 GtkSelectionData *selection_data );
61 static gboolean category_list_button_press ( GtkWidget *tree_view,
62 GdkEventButton *ev,
63 gpointer null );
64 static void category_list_popup_context_menu ( void );
65 static GtkWidget *creation_barre_outils_categ ( void );
66 static gboolean edit_category ( GtkTreeView *tree_view );
67 static gboolean popup_category_view_mode_menu ( GtkWidget *button );
68 static void selectionne_sub_category ( GtkTreeModel *model );
69 /*END_STATIC*/
70
71
72 /* Category toolbar, tree model & tree view */
73 static GtkWidget *category_toolbar = NULL;
74 static GtkTreeStore *categ_tree_model = NULL;
75 static GtkWidget *arbre_categ = NULL;
76
77 /* variable for the management of the cancelled edition */
78 static gboolean sortie_edit_category = FALSE;
79
80 /* structure pour la sauvegarde de la position */
81 struct MetatreeHoldPosition *category_hold_position;
82
83
84 /*START_EXTERN*/
85 /*END_EXTERN*/
86
87
88 /**
89 * réinitialisation des variables globales
90 *
91 * \param
92 *
93 * \return
94 * */
categories_init_variables_list(void)95 void categories_init_variables_list ( void )
96 {
97 GrisbiWinEtat *w_etat;
98
99 w_etat = (GrisbiWinEtat *) grisbi_win_get_w_etat ();
100 categ_tree_model = NULL;
101 category_toolbar = NULL;
102 arbre_categ = NULL;
103 w_etat->no_devise_totaux_categ = 1;
104 sortie_edit_category = FALSE;
105 }
106
107
108 /**
109 * Create and return contents of the "Category" notebook page.
110 *
111 * \return A newly allocated hbox.
112 */
categories_create_list(void)113 GtkWidget *categories_create_list ( void )
114 {
115 GtkWidget *window;
116 GtkWidget *scroll_window, *vbox;
117 GtkWidget *frame;
118 GtkTreeViewColumn *column;
119 GtkCellRenderer *cell;
120 GtkTreeDragDestIface * dst_iface;
121 GtkTreeDragSourceIface * src_iface;
122 static GtkTargetEntry row_targets[] = {{(gchar*)"GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }};
123 MetatreeInterface *category_interface;
124
125 window = GTK_WIDGET (grisbi_app_get_active_window (NULL));
126
127 /* We create the main vbox */
128 vbox = gtk_box_new ( GTK_ORIENTATION_VERTICAL, MARGIN_BOX );
129
130 /* frame pour la barre d'outils */
131 frame = gtk_frame_new ( NULL );
132 gtk_box_pack_start ( GTK_BOX ( vbox ), frame, FALSE, FALSE, 0 );
133
134 category_interface = category_get_metatree_interface ( );
135
136 /* We create the gtktreeview and model early so that they can be referenced. */
137 arbre_categ = gtk_tree_view_new();
138
139 /* set the color of selected row */
140 gtk_widget_set_name (arbre_categ, "colorized_tree_view");
141
142 /* Create model */
143 categ_tree_model = gtk_tree_store_new ( META_TREE_NUM_COLUMNS, META_TREE_COLUMN_TYPES );
144 gtk_tree_sortable_set_sort_column_id ( GTK_TREE_SORTABLE(categ_tree_model),
145 META_TREE_TEXT_COLUMN, GTK_SORT_ASCENDING );
146 gtk_tree_sortable_set_sort_func ( GTK_TREE_SORTABLE(categ_tree_model),
147 META_TREE_TEXT_COLUMN, metatree_sort_column,
148 NULL, NULL );
149 g_object_set_data ( G_OBJECT (categ_tree_model), "metatree-interface", category_interface );
150
151 /* on y ajoute la barre d'outils après la création du model */
152 category_toolbar = creation_barre_outils_categ ();
153 gtk_container_add ( GTK_CONTAINER ( frame ), category_toolbar );
154
155 /* création de l'arbre principal */
156 scroll_window = gtk_scrolled_window_new ( NULL, NULL );
157 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW ( scroll_window ),
158 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
159 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroll_window),
160 GTK_SHADOW_IN );
161 gtk_box_pack_start ( GTK_BOX ( vbox ), scroll_window, TRUE, TRUE, 0 );
162 gtk_widget_show ( scroll_window );
163
164 /* Create container + TreeView */
165 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(arbre_categ),
166 GDK_BUTTON1_MASK, row_targets, 1,
167 GDK_ACTION_MOVE | GDK_ACTION_COPY );
168 gtk_tree_view_enable_model_drag_dest ( GTK_TREE_VIEW(arbre_categ), row_targets,
169 1, GDK_ACTION_MOVE | GDK_ACTION_COPY );
170 gtk_tree_view_set_reorderable (GTK_TREE_VIEW(arbre_categ), TRUE);
171 gtk_tree_selection_set_mode ( gtk_tree_view_get_selection ( GTK_TREE_VIEW(arbre_categ)),
172 GTK_SELECTION_SINGLE );
173 gtk_tree_view_set_model (GTK_TREE_VIEW (arbre_categ),
174 GTK_TREE_MODEL (categ_tree_model));
175 g_object_unref (G_OBJECT(categ_tree_model));
176 g_object_set_data ( G_OBJECT(categ_tree_model), "tree-view", arbre_categ );
177
178 /* Make category column */
179 cell = gtk_cell_renderer_text_new ();
180 column = gtk_tree_view_column_new_with_attributes (_("Category"), cell,
181 "text", META_TREE_TEXT_COLUMN,
182 "weight", META_TREE_FONT_COLUMN,
183 "cell-background-rgba", META_TREE_BACKGROUND_COLOR,
184 NULL);
185 gtk_tree_view_append_column ( GTK_TREE_VIEW ( arbre_categ ),
186 GTK_TREE_VIEW_COLUMN ( column ) );
187
188 /* Make account column */
189 cell = gtk_cell_renderer_text_new ();
190 column = gtk_tree_view_column_new_with_attributes (_("Account"), cell,
191 "text", META_TREE_ACCOUNT_COLUMN,
192 "weight", META_TREE_FONT_COLUMN,
193 "cell-background-rgba", META_TREE_BACKGROUND_COLOR,
194 NULL);
195 gtk_tree_view_append_column ( GTK_TREE_VIEW ( arbre_categ ),
196 GTK_TREE_VIEW_COLUMN ( column ) );
197
198 /* Make amount column */
199 cell = gtk_cell_renderer_text_new ();
200 gtk_cell_renderer_set_padding (GTK_CELL_RENDERER (cell), MARGIN_BOX, 0);
201 column = gtk_tree_view_column_new_with_attributes (_("Amount"), cell,
202 "text", META_TREE_BALANCE_COLUMN,
203 "weight", META_TREE_FONT_COLUMN,
204 "xalign", META_TREE_XALIGN_COLUMN,
205 "cell-background-rgba", META_TREE_BACKGROUND_COLOR,
206 NULL);
207 gtk_tree_view_column_set_alignment ( column, 1.0 );
208 gtk_tree_view_append_column ( GTK_TREE_VIEW ( arbre_categ ),
209 GTK_TREE_VIEW_COLUMN ( column ) );
210
211 gtk_container_add ( GTK_CONTAINER ( scroll_window ), arbre_categ );
212 gtk_widget_show ( arbre_categ );
213
214 /* Connect to signals */
215 g_signal_connect ( G_OBJECT ( arbre_categ ),
216 "row-collapsed",
217 G_CALLBACK ( division_column_collapsed ),
218 NULL );
219
220 g_signal_connect ( G_OBJECT ( arbre_categ ),
221 "row-expanded",
222 G_CALLBACK ( division_column_expanded ),
223 NULL );
224
225 g_signal_connect( G_OBJECT ( arbre_categ ),
226 "row-activated",
227 G_CALLBACK ( division_activated ),
228 NULL);
229
230 g_signal_connect ( G_OBJECT ( arbre_categ ),
231 "button-press-event",
232 G_CALLBACK ( category_list_button_press ),
233 NULL );
234
235 dst_iface = GTK_TREE_DRAG_DEST_GET_IFACE (categ_tree_model);
236 if ( dst_iface )
237 {
238 dst_iface -> drag_data_received = &division_drag_data_received;
239 dst_iface -> row_drop_possible = &division_row_drop_possible;
240 }
241
242 src_iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (categ_tree_model);
243 if ( src_iface )
244 {
245 gtk_selection_add_target (window,
246 GDK_SELECTION_PRIMARY,
247 GDK_SELECTION_TYPE_ATOM,
248 1);
249 src_iface -> drag_data_get = &categ_drag_data_get;
250 }
251
252 g_signal_connect ( gtk_tree_view_get_selection ( GTK_TREE_VIEW ( arbre_categ ) ),
253 "changed",
254 G_CALLBACK ( metatree_selection_changed ),
255 categ_tree_model );
256
257 /* création de la structure de sauvegarde de la position */
258 category_hold_position = g_malloc0 ( sizeof ( struct MetatreeHoldPosition ) );
259
260 gtk_widget_show_all ( vbox );
261
262 return ( vbox );
263 }
264
265
266
267 /**
268 * Fill category tree with data.
269 */
categories_fill_list(void)270 void categories_fill_list ( void )
271 {
272 GSList *category_list;
273 GtkTreeIter iter_categ, iter_sous_categ;
274 MetatreeInterface *category_interface;
275
276 devel_debug (NULL);
277
278 /* Model might be empty because we are importing categories
279 * before file is opened. So don't do anything. */
280 if ( ! categ_tree_model || ! GTK_IS_TREE_STORE(categ_tree_model) )
281 return;
282
283 /** First, remove previous tree */
284 gtk_tree_store_clear ( GTK_TREE_STORE (categ_tree_model) );
285
286 /* Compute category balances. */
287 gsb_data_category_update_counters ();
288
289 /** Then, populate tree with categories. */
290 category_list = gsb_data_category_get_categories_list ();
291
292 /* add first the empty category */
293 category_list = g_slist_prepend ( category_list, gsb_data_category_get_empty_category ());
294
295 category_interface = category_get_metatree_interface ( );
296
297 while ( category_list )
298 {
299 gint category_number;
300
301 category_number = gsb_data_category_get_no_category (category_list -> data);
302
303 gtk_tree_store_append (GTK_TREE_STORE (categ_tree_model), &iter_categ, NULL);
304 fill_division_row ( GTK_TREE_MODEL(categ_tree_model), category_interface,
305 &iter_categ, category_number);
306
307 /** Each category has subcategories. */
308 if ( category_number )
309 {
310 GSList *sub_category_list;
311
312 sub_category_list = gsb_data_category_get_sub_category_list ( category_number );
313
314 while ( sub_category_list )
315 {
316 gint sub_category_number;
317
318 sub_category_number = gsb_data_category_get_no_sub_category (sub_category_list -> data);
319
320 gtk_tree_store_append (GTK_TREE_STORE (categ_tree_model),
321 &iter_sous_categ, &iter_categ);
322 fill_sub_division_row ( GTK_TREE_MODEL(categ_tree_model), category_interface,
323 &iter_sous_categ,
324 category_number,
325 sub_category_number);
326
327 sub_category_list = sub_category_list -> next;
328 }
329 }
330
331 /* add the no sub category only if category */
332 if (category_number)
333 {
334 gtk_tree_store_append (GTK_TREE_STORE (categ_tree_model),
335 &iter_sous_categ, &iter_categ);
336 fill_sub_division_row ( GTK_TREE_MODEL(categ_tree_model), category_interface,
337 &iter_sous_categ, category_number, 0 );
338 }
339
340 category_list = category_list -> next;
341 }
342
343 if ( category_hold_position -> path )
344 {
345 GtkTreeSelection *selection;
346
347 if ( category_hold_position -> expand )
348 {
349 GtkTreePath *ancestor;
350
351 ancestor = gtk_tree_path_copy ( category_hold_position -> path );
352 gtk_tree_path_up ( ancestor );
353 gtk_tree_view_expand_to_path ( GTK_TREE_VIEW ( arbre_categ ), ancestor );
354 gtk_tree_path_free (ancestor );
355 }
356 /* on colorise les lignes du tree_view */
357 utils_set_tree_store_background_color ( arbre_categ, META_TREE_BACKGROUND_COLOR );
358 selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW ( arbre_categ ) );
359 gtk_tree_selection_select_path ( selection, category_hold_position -> path );
360 gtk_tree_view_scroll_to_cell ( GTK_TREE_VIEW ( arbre_categ ),
361 category_hold_position -> path,
362 NULL, TRUE, 0.5, 0.5 );
363 }
364 else
365 {
366 gchar *title;
367
368 /* on colorise les lignes du tree_view */
369 utils_set_tree_store_background_color ( arbre_categ, META_TREE_BACKGROUND_COLOR );
370 title = g_strdup(_("Categories"));
371 grisbi_win_headings_update_title ( title );
372 g_free ( title );
373 grisbi_win_headings_update_suffix ( "" );
374 }
375 }
376
377
378 /**
379 * Fill the drag & drop structure with the path of selected column.
380 * This is an interface function called from GTK, much like a callback.
381 *
382 * \param drag_source Not used.
383 * \param path Original path for the gtk selection.
384 * \param selection_data A pointer to the drag & drop structure.
385 *
386 * \return FALSE, to allow future processing by the callback chain.
387 */
categ_drag_data_get(GtkTreeDragSource * drag_source,GtkTreePath * path,GtkSelectionData * selection_data)388 gboolean categ_drag_data_get ( GtkTreeDragSource *drag_source,
389 GtkTreePath *path,
390 GtkSelectionData *selection_data )
391 {
392 if ( path )
393 {
394 gtk_tree_set_row_drag_data (selection_data,
395 GTK_TREE_MODEL(categ_tree_model), path);
396 }
397
398 return FALSE;
399 }
400
401
402
403 /**
404 *
405 *
406 */
categories_exporter_list(void)407 void categories_exporter_list ( void )
408 {
409 GtkWidget *dialog;
410 gint resultat;
411 gchar *nom_categ;
412 gchar *tmp_last_directory;
413
414 dialog = gtk_file_chooser_dialog_new ( _("Export categories"),
415 GTK_WINDOW ( grisbi_app_get_active_window (NULL) ),
416 GTK_FILE_CHOOSER_ACTION_SAVE,
417 "gtk-cancel", GTK_RESPONSE_CANCEL,
418 "gtk-save", GTK_RESPONSE_OK,
419 NULL);
420
421 gtk_file_chooser_set_current_name ( GTK_FILE_CHOOSER ( dialog ), _("Categories.cgsb"));
422 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER ( dialog ), gsb_file_get_last_path () );
423 gtk_file_chooser_set_do_overwrite_confirmation ( GTK_FILE_CHOOSER ( dialog ), TRUE);
424 gtk_window_set_position ( GTK_WINDOW ( dialog ), GTK_WIN_POS_CENTER_ON_PARENT );
425
426 resultat = gtk_dialog_run ( GTK_DIALOG ( dialog ));
427
428 if ( resultat != GTK_RESPONSE_OK )
429 {
430 gtk_widget_destroy ( dialog );
431 return;
432 }
433
434 nom_categ = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER ( dialog ));
435 tmp_last_directory = utils_files_selection_get_last_directory ( GTK_FILE_CHOOSER ( dialog ), TRUE );
436 gsb_file_update_last_path ( tmp_last_directory );
437 g_free ( tmp_last_directory );
438 gtk_widget_destroy ( GTK_WIDGET ( dialog ));
439
440 gsb_file_others_save_category ( nom_categ );
441 }
442
443
444
445 /**
446 *
447 *
448 */
categories_importer_list(void)449 void categories_importer_list ( void )
450 {
451 GtkWidget *dialog;
452 gint resultat;
453 gchar *category_name;
454 gint last_transaction_number;
455 GtkFileFilter * filter;
456 gchar *tmp_last_directory;
457
458 dialog = gtk_file_chooser_dialog_new ( _("Import categories"),
459 GTK_WINDOW ( grisbi_app_get_active_window (NULL) ),
460 GTK_FILE_CHOOSER_ACTION_OPEN,
461 "gtk-cancel", GTK_RESPONSE_CANCEL,
462 "gtk-open", GTK_RESPONSE_OK,
463 NULL);
464
465 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER ( dialog ), gsb_file_get_last_path () );
466 gtk_window_set_position ( GTK_WINDOW ( dialog ), GTK_WIN_POS_CENTER_ON_PARENT );
467
468 filter = gtk_file_filter_new ();
469 gtk_file_filter_set_name ( filter, _("Grisbi category files (*.cgsb)") );
470 gtk_file_filter_add_pattern ( filter, "*.cgsb" );
471 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER ( dialog ), filter );
472 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER ( dialog ), filter );
473
474 filter = gtk_file_filter_new ();
475 gtk_file_filter_set_name ( filter, _("All files") );
476 gtk_file_filter_add_pattern ( filter, "*" );
477 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER ( dialog ), filter );
478
479 resultat = gtk_dialog_run ( GTK_DIALOG ( dialog ));
480
481 if ( resultat != GTK_RESPONSE_OK )
482 {
483 gtk_widget_destroy ( dialog );
484 return;
485 }
486
487 category_name = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER ( dialog ));
488 tmp_last_directory = utils_files_selection_get_last_directory ( GTK_FILE_CHOOSER ( dialog ), TRUE );
489 gsb_file_update_last_path ( tmp_last_directory );
490 g_free ( tmp_last_directory );
491 gtk_widget_destroy ( GTK_WIDGET ( dialog ));
492
493 last_transaction_number = gsb_data_transaction_get_last_number();
494
495 /* on permet de remplacer/fusionner la liste */
496
497 dialog = dialogue_special_no_run ( GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
498 ( last_transaction_number ?
499 _("File already contains categories. If you decide to continue, existing categories will be merged with imported ones.") :
500 _("File does not contain categories. "
501 "If you decide to continue, existing categories will be merged with imported ones. "
502 "Once performed, there is no undo for this.\n"
503 "You may also decide to replace existing categories with imported ones." ) ),
504 _("Merge imported categories with existing?") );
505
506 if ( !last_transaction_number)
507 gtk_dialog_add_buttons ( GTK_DIALOG(dialog),
508 _("Replace existing"), 2,
509 NULL );
510
511 gtk_dialog_add_buttons ( GTK_DIALOG(dialog),
512 "gtk-cancel", 0,
513 "gtk-ok", 1,
514 NULL );
515
516 resultat = gtk_dialog_run ( GTK_DIALOG ( dialog ));
517 gtk_widget_destroy ( GTK_WIDGET ( dialog ));
518
519 switch ( resultat )
520 {
521 case 1 :
522 gsb_file_others_load_category ( category_name );
523 return;
524
525 case 2 :
526 /* we want to replace the list */
527 if ( !last_transaction_number )
528 gsb_data_category_init_variables (TRUE);
529 gsb_file_others_load_category ( category_name );
530
531 default :
532 return;
533 }
534 }
535
536
537 /**
538 * Create a button bar allowing to act on the categories list. Some
539 * of these buttons are "linked" to the selection status of the
540 * category metatree. That is, if nothing is selected, they will
541 * become unsensitive.
542 *
543 * \return A newly-allocated widget.
544 */
creation_barre_outils_categ(void)545 GtkWidget *creation_barre_outils_categ ( void )
546 {
547 GtkWidget *toolbar;
548 GtkToolItem *item;
549
550 toolbar = gtk_toolbar_new ();
551
552 /* New category button */
553 item = utils_buttons_tool_button_new_from_image_label ("gsb-new-categ-24.png", _("New\ncategory"));
554 gtk_widget_set_tooltip_text ( GTK_WIDGET ( item ), _("Create a new category") );
555 g_object_set_data ( G_OBJECT ( item ), "type", GINT_TO_POINTER (1) );
556 g_signal_connect_swapped ( G_OBJECT ( item ),
557 "clicked",
558 G_CALLBACK ( appui_sur_ajout_category ),
559 categ_tree_model );
560 gtk_toolbar_insert ( GTK_TOOLBAR ( toolbar ), item, -1 );
561
562 /* New sub category button */
563 item = utils_buttons_tool_button_new_from_image_label ("gsb-new-sub-categ-24.png", _("New sub\ncategory"));
564 metatree_register_widget_as_linked ( GTK_TREE_MODEL ( categ_tree_model ),
565 GTK_WIDGET ( item ),
566 "selection" );
567 metatree_register_widget_as_linked ( GTK_TREE_MODEL ( categ_tree_model ),
568 GTK_WIDGET ( item ),
569 "sub-division" );
570 gtk_widget_set_tooltip_text ( GTK_WIDGET ( item ), _("Create a new sub-category") );
571 g_object_set_data ( G_OBJECT ( item ), "type", GINT_TO_POINTER (2) );
572 g_signal_connect_swapped ( G_OBJECT ( item ),
573 "clicked",
574 G_CALLBACK ( appui_sur_ajout_category ),
575 categ_tree_model );
576 gtk_toolbar_insert ( GTK_TOOLBAR ( toolbar ), item, -1 );
577
578 /* Import button */
579 item = utils_buttons_tool_button_new_from_image_label ("gsb-import-24.png", _("Import"));
580 gtk_widget_set_tooltip_text ( GTK_WIDGET ( item ), _("Import a Grisbi category file (.cgsb)") );
581 g_signal_connect ( G_OBJECT ( item ),
582 "clicked",
583 G_CALLBACK ( categories_importer_list ),
584 NULL );
585 gtk_toolbar_insert ( GTK_TOOLBAR ( toolbar ), item, -1 );
586
587 /* Export button */
588 item = utils_buttons_tool_button_new_from_image_label ("gsb-export-24.png", _("Export"));
589 gtk_widget_set_tooltip_text ( GTK_WIDGET ( item ), _("Export a Grisbi category file (.cgsb)") );
590 g_signal_connect ( G_OBJECT ( item ),
591 "clicked",
592 G_CALLBACK ( categories_exporter_list ),
593 NULL );
594 gtk_toolbar_insert ( GTK_TOOLBAR ( toolbar ), item, -1 );
595
596 /* Delete button */
597 item = utils_buttons_tool_button_new_from_image_label ("gtk-delete-24.png", _("Delete"));
598 metatree_register_widget_as_linked ( GTK_TREE_MODEL ( categ_tree_model ),
599 GTK_WIDGET ( item ),
600 "selection" );
601 gtk_widget_set_tooltip_text ( GTK_WIDGET ( item ), _("Delete selected category") );
602 g_signal_connect_swapped ( G_OBJECT ( item ),
603 "clicked",
604 G_CALLBACK ( supprimer_division ),
605 arbre_categ );
606 gtk_toolbar_insert ( GTK_TOOLBAR ( toolbar ), item, -1 );
607
608 /* Properties button */
609 item = utils_buttons_tool_button_new_from_image_label ("gtk-edit-24.png", _("Edit"));
610 metatree_register_widget_as_linked ( GTK_TREE_MODEL ( categ_tree_model ),
611 GTK_WIDGET ( item ),
612 "selection" );
613 gtk_widget_set_tooltip_text ( GTK_WIDGET ( item ), _("Edit selected category") );
614 g_signal_connect_swapped ( G_OBJECT ( item ),
615 "clicked",
616 G_CALLBACK ( edit_category ),
617 arbre_categ );
618 gtk_toolbar_insert ( GTK_TOOLBAR ( toolbar ), item, -1 );
619
620 /* View button */
621 item = utils_buttons_tool_button_new_from_image_label ("gtk-select-color-24.png", _("View"));
622 gtk_widget_set_tooltip_text ( GTK_WIDGET ( item ), _("Change view mode"));
623 g_signal_connect ( G_OBJECT ( item ),
624 "clicked",
625 G_CALLBACK ( popup_category_view_mode_menu ),
626 NULL );
627 gtk_toolbar_insert ( GTK_TOOLBAR ( toolbar ), item, -1 );
628
629 metatree_set_linked_widgets_sensitive ( GTK_TREE_MODEL ( categ_tree_model ), FALSE, "selection" );
630
631 return ( toolbar );
632 }
633
634
635 /**
636 *
637 *
638 *
639 */
gsb_gui_categories_toolbar_set_style(gint toolbar_style)640 void gsb_gui_categories_toolbar_set_style ( gint toolbar_style )
641 {
642 gtk_toolbar_set_style ( GTK_TOOLBAR ( category_toolbar ), toolbar_style );
643 }
644
645
646 /**
647 * Popup a menu that allow changing the view mode of the category
648 * metatree.
649 *
650 * \param button Button that triggered the signal.
651 *
652 * \return FALSE
653 */
popup_category_view_mode_menu(GtkWidget * button)654 gboolean popup_category_view_mode_menu ( GtkWidget *button )
655 {
656 GtkWidget *menu, *menu_item;
657
658 menu = gtk_menu_new ();
659
660 /* Edit transaction */
661 menu_item = gtk_menu_item_new_with_label ( _("Category view") );
662 g_signal_connect ( G_OBJECT(menu_item), "activate",
663 G_CALLBACK(expand_arbre_division), (gpointer) 0 );
664 g_object_set_data ( G_OBJECT(menu_item), "tree-view", arbre_categ );
665 gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), menu_item );
666
667 menu_item = gtk_menu_item_new_with_label ( _("Subcategory view") );
668 g_signal_connect ( G_OBJECT(menu_item), "activate",
669 G_CALLBACK(expand_arbre_division), (gpointer) 1 );
670 g_object_set_data ( G_OBJECT(menu_item), "tree-view", arbre_categ );
671 gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), menu_item );
672
673 menu_item = gtk_menu_item_new_with_label ( _("Complete view") );
674 g_signal_connect ( G_OBJECT(menu_item), "activate",
675 G_CALLBACK(expand_arbre_division), (gpointer) 2 );
676 g_object_set_data ( G_OBJECT(menu_item), "tree-view", arbre_categ );
677 gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), menu_item );
678
679 gtk_widget_show_all ( menu );
680
681 #if GTK_CHECK_VERSION (3,22,0)
682 gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
683 #else
684 gtk_menu_popup ( GTK_MENU(menu), NULL, button, set_popup_position, button, 1,
685 gtk_get_current_event_time());
686 #endif
687 return FALSE;
688 }
689
690
691
692 /**
693 *
694 *
695 */
edit_category(GtkTreeView * tree_view)696 gboolean edit_category ( GtkTreeView *tree_view )
697 {
698 GtkWidget * dialog, *paddingbox, *table, *label, *entry, *hbox, *radiogroup;
699 gint category_number = -1, sub_category_number = -1;
700 GtkTreeSelection * selection;
701 GtkTreeModel * model = NULL;
702 GtkTreeIter iter;
703 gchar * title;
704 GtkTreeIter *div_iter;
705 MetatreeInterface *category_interface;
706
707 /* fill category_number and sub_category_number */
708
709 selection = gtk_tree_view_get_selection ( tree_view );
710 if ( selection && gtk_tree_selection_get_selected ( selection, &model, &iter ) )
711 {
712 gtk_tree_model_get ( model, &iter,
713 META_TREE_NO_DIV_COLUMN, &category_number,
714 META_TREE_NO_SUB_DIV_COLUMN, &sub_category_number,
715 -1 );
716 }
717
718 if ( !selection || category_number <= 0 )
719 return FALSE;
720
721 if ( sub_category_number > 0 )
722 title = g_strdup_printf ( _("Properties for %s"), gsb_data_category_get_sub_category_name ( category_number,
723 sub_category_number,
724 _("No sub-category defined" )));
725 else
726 title = g_strdup_printf ( _("Properties for %s"), gsb_data_category_get_name ( category_number,
727 0,
728 _("No category defined") ));
729
730 dialog = gtk_dialog_new_with_buttons ( title,
731 GTK_WINDOW ( grisbi_app_get_active_window (NULL) ),
732 GTK_DIALOG_MODAL,
733 "gtk-cancel", GTK_RESPONSE_NO,
734 "gtk-apply", GTK_RESPONSE_OK,
735 NULL );
736
737 gtk_window_set_position ( GTK_WINDOW ( dialog ), GTK_WIN_POS_CENTER_ON_PARENT );
738 gtk_window_set_resizable ( GTK_WINDOW ( dialog ), FALSE );
739
740 /* Ugly dance to avoid side effects on dialog's vbox. */
741 hbox = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL, 0 );
742 gtk_box_pack_start ( GTK_BOX ( dialog_get_content_area ( dialog ) ), hbox, FALSE, FALSE, 0 );
743 paddingbox = new_paddingbox_with_title ( hbox, TRUE, title );
744 gtk_container_set_border_width ( GTK_CONTAINER(hbox), 6 );
745 gtk_container_set_border_width ( GTK_CONTAINER(paddingbox), 6 );
746 g_free ( title );
747
748 table = gtk_grid_new ();
749 gtk_box_pack_start ( GTK_BOX ( paddingbox ), table, FALSE, FALSE, 6 );
750 gtk_grid_set_column_spacing (GTK_GRID (table), 6);
751 gtk_grid_set_row_spacing (GTK_GRID (table), 6);
752
753 /* Name entry */
754 label = gtk_label_new ( _("Name"));
755 utils_labels_set_alignment ( GTK_LABEL ( label ), 0.0, 0.5 );
756 gtk_grid_attach (GTK_GRID (table), label, 0, 0, 1, 1);
757
758 entry = gtk_entry_new ( );
759 if ( sub_category_number > 0 )
760 {
761 gtk_entry_set_text ( GTK_ENTRY ( entry ),
762 gsb_data_category_get_sub_category_name ( category_number,
763 sub_category_number,
764 NULL ) );
765 }
766 else
767 {
768 gtk_entry_set_text ( GTK_ENTRY ( entry ),
769 gsb_data_category_get_name ( category_number, 0, NULL ) );
770 }
771
772 gtk_widget_set_size_request ( entry, 400, -1 );
773 gtk_grid_attach (GTK_GRID (table), entry, 1, 0, 1, 1);
774
775 if ( sub_category_number <= 0 )
776 {
777 /* Description entry */
778 label = gtk_label_new ( _("Type"));
779 utils_labels_set_alignment ( GTK_LABEL ( label ), 0.0, 0.5 );
780 gtk_grid_attach (GTK_GRID (table), label, 0, 1, 1, 1);
781
782 radiogroup = gsb_autofunc_radiobutton_new ( _("Credit"), _("Debit"),
783 gsb_data_category_get_type (category_number),
784 NULL, NULL,
785 G_CALLBACK (gsb_data_category_set_type), category_number );
786 gtk_grid_attach (GTK_GRID (table), radiogroup, 1, 1, 1, 1);
787 }
788
789 gtk_widget_show_all ( dialog );
790
791 while ( 1 )
792 {
793 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_OK )
794 {
795 sortie_edit_category = TRUE;
796 gtk_widget_destroy ( GTK_WIDGET ( dialog ) );
797 return FALSE;
798 }
799
800 if ( ( sub_category_number > 0 &&
801 gsb_data_category_get_sub_category_number_by_name ( category_number,
802 gtk_entry_get_text ( GTK_ENTRY ( entry ) ),
803 FALSE ) &&
804 gsb_data_category_get_sub_category_number_by_name ( category_number,
805 gtk_entry_get_text ( GTK_ENTRY ( entry ) ),
806 FALSE ) != sub_category_number ) ||
807 ( sub_category_number <= 0 &&
808 gsb_data_category_get_number_by_name ( gtk_entry_get_text ( GTK_ENTRY ( entry ) ),
809 FALSE, 0 ) &&
810 gsb_data_category_get_number_by_name ( gtk_entry_get_text ( GTK_ENTRY ( entry ) ),
811 FALSE, 0 ) != category_number ) )
812 {
813 gchar * message;
814 if ( sub_category_number > 0 )
815 {
816 message = g_strdup_printf ( _("You tried to rename current sub-category to '%s' "
817 "but this sub-category already exists. Please "
818 "choose another name."),
819 gtk_entry_get_text ( GTK_ENTRY ( entry ) ) );
820 }
821 else
822 {
823 message = g_strdup_printf ( _("You tried to rename current category to '%s' "
824 "but this category already exists. Please "
825 "choose another name."),
826 gtk_entry_get_text ( GTK_ENTRY ( entry ) ) );
827 }
828 dialogue_warning_hint ( message, _("Category already exists") );
829 g_free ( message );
830 }
831 else
832 {
833 if ( sub_category_number > 0 )
834 {
835 gsb_data_category_set_sub_category_name ( category_number,
836 sub_category_number,
837 gtk_entry_get_text ( GTK_ENTRY (entry)));
838 }
839 else
840 {
841 gsb_data_category_set_name ( category_number,
842 gtk_entry_get_text ( GTK_ENTRY (entry)));
843 }
844 break;
845 }
846 }
847
848 gtk_widget_destroy ( dialog );
849
850 category_interface = category_get_metatree_interface ( );
851
852 if ( sub_category_number > 0 )
853 {
854 div_iter = get_iter_from_div ( model, category_number, sub_category_number );
855 fill_sub_division_row ( model, category_interface,
856 div_iter,
857 category_number,
858 sub_category_number);
859 }
860 else
861 {
862 div_iter = get_iter_from_div ( model, category_number, 0 );
863 fill_division_row ( model, category_interface,
864 div_iter,
865 category_number );
866 }
867 gtk_tree_iter_free (div_iter);
868
869 /* update the transactions list */
870 transaction_list_update_element (ELEMENT_CATEGORY);
871
872 gsb_file_set_modified ( TRUE );
873
874 return TRUE;
875 }
876
877
878 /**
879 * function to create and editing a new category.
880 *
881 * \param the model for the division
882 */
appui_sur_ajout_category(GtkTreeModel * model,GtkButton * button)883 void appui_sur_ajout_category ( GtkTreeModel *model,
884 GtkButton *button )
885 {
886 gint type;
887
888 type = GPOINTER_TO_INT ( g_object_get_data ( G_OBJECT (button), "type" ) );
889
890 if ( type == 1 )
891 metatree_new_division ( model );
892 else
893 {
894 appui_sur_ajout_sub_division ( model );
895 selectionne_sub_category ( model );
896 }
897 sortie_edit_category = FALSE;
898 edit_category ( GTK_TREE_VIEW ( arbre_categ ) );
899 if ( sortie_edit_category )
900 supprimer_division ( GTK_TREE_VIEW ( arbre_categ ) );
901 sortie_edit_category = FALSE;
902 }
903
904
905 /**
906 * function to expand category and select new sub-category.
907 *
908 * \param the model for the division
909 */
selectionne_sub_category(GtkTreeModel * model)910 void selectionne_sub_category ( GtkTreeModel *model )
911 {
912 GtkTreeSelection * selection;
913 GtkTreeIter parent;
914 GtkTreeIter iter;
915 GtkTreePath * path;
916 gchar * name;
917 gint category_number = -1, sub_category_number = -1;
918 gint i = 0,j = 0;
919
920 selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (arbre_categ) );
921 if ( selection && gtk_tree_selection_get_selected (
922 selection, &model, &parent ) )
923 {
924 gtk_tree_model_get ( model, &parent,
925 META_TREE_NO_DIV_COLUMN, &category_number,
926 META_TREE_NO_SUB_DIV_COLUMN, &sub_category_number,
927 -1 );
928 }
929
930 if ( !selection || category_number <= 0 )
931 return;
932
933 if ( sub_category_number > 0 )
934 return;
935
936 name = my_strdup (_("New sub-category"));
937 sub_category_number = gsb_data_category_get_sub_category_number_by_name (
938 category_number, name, FALSE );
939 j = gtk_tree_model_iter_n_children ( model, &parent );
940 for (i = 0; i < j; i++ )
941 {
942 gint numero;
943
944 gtk_tree_model_iter_nth_child ( model, &iter, &parent, i );
945 gtk_tree_model_get ( model, &iter,
946 META_TREE_NO_SUB_DIV_COLUMN, &numero,
947 -1 );
948 if ( numero == sub_category_number )
949 break;
950 }
951 path = gtk_tree_model_get_path ( model, &iter );
952 gtk_tree_view_expand_to_path ( GTK_TREE_VIEW (arbre_categ), path );
953 selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (arbre_categ) );
954 gtk_tree_selection_select_path ( selection, path );
955 gtk_tree_view_scroll_to_cell ( GTK_TREE_VIEW (arbre_categ), path,
956 NULL, TRUE, 0.5, 0.5 );
957 gtk_tree_path_free ( path );
958 g_free ( name );
959 }
960
961
962 /**
963 * renvoie le chemin de la dernière categorie sélectionnée.
964 *
965 * \return une copie de category_hold_position -> path
966 */
categories_hold_position_get_path(void)967 GtkTreePath *categories_hold_position_get_path ( void )
968 {
969 return gtk_tree_path_copy ( category_hold_position -> path );
970 }
971
972 /**
973 * sauvegarde le chemin de la dernière categorie sélectionnée.
974 *
975 * \param path
976 */
categories_hold_position_set_path(GtkTreePath * path)977 gboolean categories_hold_position_set_path ( GtkTreePath *path )
978 {
979 category_hold_position -> path = gtk_tree_path_copy ( path );
980
981 return TRUE;
982 }
983
984
985 /**
986 * sauvegarde l'attribut expand.
987 *
988 * \param expand
989 */
categories_hold_position_set_expand(gboolean expand)990 gboolean categories_hold_position_set_expand ( gboolean expand )
991 {
992 category_hold_position -> expand = expand;
993
994 return TRUE;
995 }
996
997
998 /**
999 * called when we press a button on the list
1000 *
1001 * \param tree_view
1002 * \param ev
1003 *
1004 * \return FALSE
1005 * */
category_list_button_press(GtkWidget * tree_view,GdkEventButton * ev,gpointer null)1006 gboolean category_list_button_press ( GtkWidget *tree_view,
1007 GdkEventButton *ev,
1008 gpointer null )
1009 {
1010 if ( ev -> button == RIGHT_BUTTON )
1011 {
1012 category_list_popup_context_menu ( );
1013
1014 return TRUE;
1015 }
1016 else if ( ev -> type == GDK_2BUTTON_PRESS )
1017 {
1018 GtkTreeSelection *selection;
1019 GtkTreeModel *model = NULL;
1020 GtkTreeIter iter;
1021 GtkTreePath *path = NULL;
1022 enum MetaTreeRowType type_division;
1023 GrisbiAppConf *a_conf;
1024
1025 type_division = metatree_get_row_type_from_tree_view ( tree_view );
1026 if ( type_division == META_TREE_TRANSACTION )
1027 return FALSE;
1028
1029 selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW ( tree_view ) );
1030 if ( selection && gtk_tree_selection_get_selected (selection, &model, &iter ) )
1031 path = gtk_tree_model_get_path ( model, &iter);
1032 else
1033 return FALSE;
1034
1035 a_conf = (GrisbiAppConf *) grisbi_app_get_a_conf ();
1036 if ( a_conf->metatree_action_2button_press == 0 || type_division == META_TREE_DIV )
1037 {
1038 if ( gtk_tree_view_row_expanded ( GTK_TREE_VIEW ( tree_view ), path ) )
1039 gtk_tree_view_collapse_row ( GTK_TREE_VIEW ( tree_view ), path );
1040 else
1041 gtk_tree_view_expand_row ( GTK_TREE_VIEW ( tree_view ), path, FALSE );
1042
1043 gtk_tree_path_free ( path );
1044 return FALSE;
1045 }
1046 else if ( a_conf->metatree_action_2button_press == 1 )
1047 {
1048 edit_category ( GTK_TREE_VIEW ( tree_view ) );
1049
1050 gtk_tree_path_free ( path );
1051 return TRUE;
1052 }
1053 else
1054 {
1055 if ( type_division == META_TREE_SUB_DIV || type_division == META_TREE_TRANS_S_S_DIV )
1056 {
1057 gtk_tree_view_collapse_row ( GTK_TREE_VIEW ( tree_view ), path );
1058
1059 gtk_tree_path_free ( path );
1060
1061 metatree_manage_sub_divisions ( tree_view );
1062 return TRUE;
1063 }
1064 else
1065 return FALSE;
1066 }
1067 }
1068
1069 return FALSE;
1070 }
1071
1072
1073 /**
1074 * Pop up a menu with several actions to apply to current selection.
1075 *
1076 * \param
1077 *
1078 */
category_list_popup_context_menu(void)1079 void category_list_popup_context_menu ( void )
1080 {
1081 GtkWidget *menu;
1082 GtkWidget *menu_item;
1083 gchar *title;
1084 enum MetaTreeRowType type_division;
1085
1086 type_division = metatree_get_row_type_from_tree_view ( arbre_categ );
1087
1088 if ( type_division == META_TREE_INVALID )
1089 return;
1090
1091 menu = gtk_menu_new ();
1092
1093 if ( type_division == META_TREE_TRANSACTION )
1094 {
1095 title = g_strdup ( _("Transfers the identical transactions in another sub-category") );
1096
1097 menu_item = utils_menu_item_new_from_image_label ("gsb-convert-16.png", title);
1098 g_signal_connect_swapped ( G_OBJECT ( menu_item ),
1099 "activate",
1100 G_CALLBACK ( metatree_transfer_identical_transactions ),
1101 arbre_categ );
1102 gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), menu_item );
1103
1104 g_free ( title );
1105 }
1106
1107 if ( type_division == META_TREE_DIV || type_division == META_TREE_SUB_DIV )
1108 {
1109
1110 /* Edit transaction */
1111 if ( type_division == META_TREE_DIV )
1112 title = g_strdup ( _("Edit selected category") );
1113 else
1114 title = g_strdup ( _("Edit selected sub-category") );
1115
1116 menu_item = utils_menu_item_new_from_image_label ("gtk-edit-16.png", title);
1117 g_signal_connect_swapped ( G_OBJECT ( menu_item ),
1118 "activate",
1119 G_CALLBACK ( edit_category ),
1120 arbre_categ );
1121 gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), menu_item );
1122
1123 g_free ( title );
1124 }
1125
1126 if ( type_division == META_TREE_SUB_DIV || type_division == META_TREE_TRANS_S_S_DIV )
1127 {
1128 /* Manage sub_divisions */
1129 if ( type_division == META_TREE_SUB_DIV )
1130 {
1131 /* Separator */
1132 gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), gtk_separator_menu_item_new ( ) );
1133 title = g_strdup ( _("Manage sub-categories") );
1134 }
1135 else
1136 title = g_strdup ( _("Transfer all transactions in another sub-category") );
1137
1138 menu_item = utils_menu_item_new_from_image_label ("gsb-convert-16.png", title);
1139 g_signal_connect_swapped ( G_OBJECT ( menu_item ),
1140 "activate",
1141 G_CALLBACK ( metatree_manage_sub_divisions ),
1142 arbre_categ );
1143 gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), menu_item );
1144
1145 g_free ( title );
1146 }
1147
1148 /* Finish all. */
1149 gtk_widget_show_all ( menu );
1150
1151 #if GTK_CHECK_VERSION (3,22,0)
1152 gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
1153 #else
1154 gtk_menu_popup ( GTK_MENU ( menu ), NULL, NULL, NULL, NULL, 3, gtk_get_current_event_time ( ) );
1155 #endif
1156 }
1157
1158
1159 /**
1160 *
1161 *
1162 *
1163 */
categories_get_tree_store(void)1164 GtkTreeStore *categories_get_tree_store ( void )
1165 {
1166 return categ_tree_model;
1167 }
1168
1169
1170 /**
1171 *
1172 *
1173 *
1174 */
categories_get_tree_view(void)1175 GtkWidget *categories_get_tree_view ( void )
1176 {
1177 return arbre_categ;
1178 }
1179
1180
1181 /**
1182 *
1183 *
1184 *
1185 */
categories_new_category(void)1186 void categories_new_category ( void )
1187 {
1188 metatree_new_division ( GTK_TREE_MODEL ( categ_tree_model ) );
1189
1190 sortie_edit_category = FALSE;
1191 edit_category ( GTK_TREE_VIEW ( arbre_categ ) );
1192 if ( sortie_edit_category )
1193 {
1194 supprimer_division ( GTK_TREE_VIEW ( arbre_categ ) );
1195 sortie_edit_category = FALSE;
1196 }
1197 }
1198
1199
1200 /**
1201 *
1202 *
1203 *
1204 */
categories_delete_category(void)1205 void categories_delete_category ( void )
1206 {
1207 supprimer_division ( GTK_TREE_VIEW ( arbre_categ ) );
1208 }
1209
1210
1211 /**
1212 *
1213 *
1214 *
1215 */
categories_edit_category(void)1216 void categories_edit_category ( void )
1217 {
1218 edit_category ( GTK_TREE_VIEW ( arbre_categ ) );
1219 }
1220
1221
1222 /**
1223 *
1224 *
1225 * \param
1226 *
1227 * \return
1228 * */
1229 /* Local Variables: */
1230 /* c-basic-offset: 4 */
1231 /* End: */
1232