1 /**********************************************************************\
2 * gnc-tree-view-account.c -- GtkTreeView implementation to display *
3 * accounts in a GtkTreeView. *
4 * Copyright (C) 2003,2005,2006 David Hampton <hampton@employees.org> *
5 * *
6 * This program is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU General Public License as *
8 * published by the Free Software Foundation; either version 2 of *
9 * the License, or (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, contact: *
18 * *
19 * Free Software Foundation Voice: +1-617-542-5942 *
20 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21 * Boston, MA 02110-1301, USA gnu@gnu.org *
22 * *
23 \**********************************************************************/
24
25 #include <config.h>
26
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 #include <string.h>
30
31 #include "gnc-tree-view.h"
32 #include "gnc-tree-model-account.h"
33 #include "gnc-tree-model-account-types.h"
34 #include "gnc-tree-view-account.h"
35
36 #include "Account.h"
37 #include "gnc-accounting-period.h"
38 #include "gnc-commodity.h"
39 #include "gnc-component-manager.h"
40 #include "gnc-engine.h"
41 #include "gnc-glib-utils.h"
42 #include "gnc-gobject-utils.h"
43 #include "gnc-prefs.h"
44 #include "gnc-hooks.h"
45 #include "gnc-session.h"
46 #include "gnc-icons.h"
47 #include "gnc-ui-balances.h"
48 #include "dialog-utils.h"
49 #include "window-main-summarybar.h"
50
51 #define SAMPLE_ACCOUNT_VALUE "$1,000,000.00"
52 #define GNC_PREF_ACCOUNT_COLOR "show-account-color"
53
54 /** Static Globals *******************************************************/
55
56 /* This static indicates the debugging module that this .o belongs to. */
57 static QofLogModule log_module = GNC_MOD_GUI;
58
59 /** Declarations *********************************************************/
60 static void gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass);
61 static void gnc_tree_view_account_init (GncTreeViewAccount *view);
62 static void gnc_tree_view_account_finalize (GObject *object);
63 static gboolean gnc_tree_view_search_compare (GtkTreeModel *model, gint column,
64 const gchar *key, GtkTreeIter *iter, gpointer search_data);
65
66 static void gtva_update_column_names (GncTreeView *view);
67 static void gtva_currency_changed_cb (void);
68
69 static gboolean gnc_tree_view_account_filter_helper (GtkTreeModel *model,
70 GtkTreeIter *iter,
71 gpointer data);
72
73 static void gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view,
74 GtkTreeViewColumn *column,
75 GtkCellRenderer *renderer,
76 GncTreeViewAccountColumnTextEdited col_edited_cb);
77
78 static void tax_info_data_func (GtkTreeViewColumn *col,
79 GtkCellRenderer *renderer,
80 GtkTreeModel *model,
81 GtkTreeIter *iter,
82 gpointer view);
83
84 static void acc_color_data_func (GtkTreeViewColumn *col,
85 GtkCellRenderer *renderer,
86 GtkTreeModel *model,
87 GtkTreeIter *iter,
88 gpointer view);
89
90 static void gnc_tree_view_account_color_update (gpointer gsettings,
91 gchar *key, gpointer user_data);
92
93
94 typedef struct GncTreeViewAccountPrivate
95 {
96 AccountViewInfo avi;
97
98 gnc_tree_view_account_filter_func filter_fn;
99 gpointer filter_data;
100 GSourceFunc filter_destroy;
101
102 GtkTreeViewColumn *name_column;
103 GtkTreeViewColumn *code_column;
104 GtkTreeViewColumn *desc_column;
105 GtkTreeViewColumn *present_report_column;
106 GtkTreeViewColumn *balance_report_column;
107 GtkTreeViewColumn *cleared_report_column;
108 GtkTreeViewColumn *reconciled_report_column;
109 GtkTreeViewColumn *future_min_report_column;
110 GtkTreeViewColumn *total_report_column;
111 GtkTreeViewColumn *notes_column;
112
113 gboolean show_account_color;
114
115 } GncTreeViewAccountPrivate;
116
117 #define GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(o) \
118 ((GncTreeViewAccountPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_TREE_VIEW_ACCOUNT))
119
120
121 /************************************************************/
122 /* g_object required functions */
123 /************************************************************/
124
125 static GObjectClass *parent_class = NULL;
126
G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewAccount,gnc_tree_view_account,GNC_TYPE_TREE_VIEW)127 G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewAccount, gnc_tree_view_account, GNC_TYPE_TREE_VIEW)
128
129 static void
130 gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass)
131 {
132 GObjectClass *o_class;
133
134 parent_class = g_type_class_peek_parent (klass);
135
136 /* GObject signals */
137 o_class = G_OBJECT_CLASS (klass);
138 o_class->finalize = gnc_tree_view_account_finalize;
139
140 gnc_hook_add_dangler(HOOK_CURRENCY_CHANGED,
141 (GFunc)gtva_currency_changed_cb, NULL, NULL);
142 }
143
144 /********************************************************************\
145 * gnc_init_account_view_info *
146 * initialize an account view info structure with default values *
147 * *
148 * Args: avi - structure to initialize *
149 * Returns: nothing *
150 \********************************************************************/
151 static void
gnc_init_account_view_info(AccountViewInfo * avi)152 gnc_init_account_view_info(AccountViewInfo *avi)
153 {
154 int i;
155
156 for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
157 avi->include_type[i] = TRUE;
158 avi->show_hidden = FALSE;
159 }
160
161 static void
gnc_tree_view_account_init(GncTreeViewAccount * view)162 gnc_tree_view_account_init (GncTreeViewAccount *view)
163 {
164 GncTreeViewAccountPrivate *priv;
165
166 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
167
168 gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
169 GNC_PREF_ACCOUNT_COLOR,
170 gnc_tree_view_account_color_update,
171 view);
172
173 gnc_init_account_view_info(&priv->avi);
174 }
175
176 static void
gnc_tree_view_account_finalize(GObject * object)177 gnc_tree_view_account_finalize (GObject *object)
178 {
179 GncTreeViewAccount *account_view;
180 GncTreeViewAccountPrivate *priv;
181
182 ENTER("view %p", object);
183 g_return_if_fail (object != NULL);
184 g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (object));
185
186 account_view = GNC_TREE_VIEW_ACCOUNT (object);
187
188 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
189
190 gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
191 GNC_PREF_ACCOUNT_COLOR,
192 gnc_tree_view_account_color_update,
193 account_view);
194 if (priv->filter_destroy)
195 {
196 priv->filter_destroy(priv->filter_data);
197 priv->filter_destroy = NULL;
198 }
199 priv->filter_fn = NULL;
200
201 if (G_OBJECT_CLASS (parent_class)->finalize)
202 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
203 LEAVE(" ");
204 }
205
206
207 /************************************************************
208 * Callbacks *
209 ************************************************************/
210 static void
gnc_tree_view_account_hidden_toggled(GtkCellRendererToggle * cell,const gchar * s_path_str,gpointer user_data)211 gnc_tree_view_account_hidden_toggled (GtkCellRendererToggle *cell,
212 const gchar *s_path_str,
213 gpointer user_data)
214 {
215 GncTreeViewAccount *tree_view;
216 GtkTreePath *s_path;
217 Account *account;
218 gboolean hidden;
219
220 /* Change the requested account */
221 tree_view = user_data;
222 s_path = gtk_tree_path_new_from_string (s_path_str);
223 account = gnc_tree_view_account_get_account_from_path (tree_view, s_path);
224 if (account)
225 {
226 hidden = !gtk_cell_renderer_toggle_get_active (cell); // hasn't changed yet.
227 xaccAccountSetHidden (account, hidden);
228 }
229
230 /* Clean up */
231 gtk_tree_path_free (s_path);
232 }
233
234
235 static void
gnc_tree_view_account_placeholder_toggled(GtkCellRendererToggle * cell,const gchar * s_path_str,gpointer user_data)236 gnc_tree_view_account_placeholder_toggled (GtkCellRendererToggle *cell,
237 const gchar *s_path_str,
238 gpointer user_data)
239 {
240 GncTreeViewAccount *tree_view;
241 GtkTreePath *s_path;
242 Account *account;
243 gboolean placeholder;
244
245 /* Change the requested account */
246 tree_view = user_data;
247 s_path = gtk_tree_path_new_from_string (s_path_str);
248 account = gnc_tree_view_account_get_account_from_path (tree_view, s_path);
249 if (account)
250 {
251 placeholder = !gtk_cell_renderer_toggle_get_active (cell); // hasn't changed yet.
252 xaccAccountSetPlaceholder (account, placeholder);
253 }
254
255 /* Clean up */
256 gtk_tree_path_free (s_path);
257 }
258
259
260 /************************************************************/
261 /* sort functions */
262 /************************************************************/
263
264 static GtkTreeModel *
sort_cb_setup_w_iters(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,GtkTreeIter * iter_a,GtkTreeIter * iter_b,const Account ** account_a,const Account ** account_b)265 sort_cb_setup_w_iters (GtkTreeModel *f_model,
266 GtkTreeIter *f_iter_a,
267 GtkTreeIter *f_iter_b,
268 GtkTreeIter *iter_a,
269 GtkTreeIter *iter_b,
270 const Account **account_a,
271 const Account **account_b)
272 {
273 GtkTreeModel *model;
274
275 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
276 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
277 iter_a,
278 f_iter_a);
279 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
280 iter_b,
281 f_iter_b);
282 *account_a = gnc_tree_model_account_get_account (GNC_TREE_MODEL_ACCOUNT(model), iter_a);
283 *account_b = gnc_tree_model_account_get_account (GNC_TREE_MODEL_ACCOUNT(model), iter_b);
284 return model;
285 }
286
287 static void
sort_cb_setup(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,const Account ** account_a,const Account ** account_b)288 sort_cb_setup (GtkTreeModel *f_model,
289 GtkTreeIter *f_iter_a,
290 GtkTreeIter *f_iter_b,
291 const Account **account_a,
292 const Account **account_b)
293 {
294 GtkTreeIter iter_a, iter_b;
295
296 sort_cb_setup_w_iters (f_model, f_iter_a, f_iter_b,
297 &iter_a, &iter_b, account_a, account_b);
298 }
299
300 static gint
sort_by_last_reconcile_date(GtkTreeModel * f_model,GtkTreeIter * f_iter1,GtkTreeIter * f_iter2,gpointer user_data)301 sort_by_last_reconcile_date (GtkTreeModel *f_model,
302 GtkTreeIter *f_iter1,
303 GtkTreeIter *f_iter2,
304 gpointer user_data)
305 {
306 const Account *account1, *account2;
307 time64 account1_date, account2_date;
308
309 sort_cb_setup (f_model, f_iter1, f_iter2, &account1, &account2);
310
311 if (!xaccAccountGetReconcileLastDate (account1, &account1_date))
312 account1_date = 0;
313
314 if (!xaccAccountGetReconcileLastDate (account2, &account2_date))
315 account2_date = 0;
316
317 if (account1_date < account2_date)
318 return -1;
319 else if (account1_date > account2_date)
320 return 1;
321 else
322 return xaccAccountOrder (account1, account2);
323 }
324
325 static gint
sort_by_string(GtkTreeModel * f_model,GtkTreeIter * f_iter1,GtkTreeIter * f_iter2,gpointer user_data)326 sort_by_string (GtkTreeModel *f_model,
327 GtkTreeIter *f_iter1,
328 GtkTreeIter *f_iter2,
329 gpointer user_data)
330 {
331 GtkTreeModel *model;
332 GtkTreeIter iter1, iter2;
333 const Account *account1, *account2;
334 gchar *str1, *str2;
335 gint column = GPOINTER_TO_INT(user_data);
336 gint result;
337
338 model = sort_cb_setup_w_iters(f_model, f_iter1, f_iter2, &iter1, &iter2, &account1, &account2);
339
340 /* Get the strings. */
341 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter1, column, &str1, -1);
342 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter2, column, &str2, -1);
343
344 result = safe_utf8_collate(str1, str2);
345 g_free(str1);
346 g_free(str2);
347 if (result != 0)
348 return result;
349 return xaccAccountOrder(account1, account2);
350 }
351
352 static gint
sort_by_code(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)353 sort_by_code (GtkTreeModel *f_model,
354 GtkTreeIter *f_iter_a,
355 GtkTreeIter *f_iter_b,
356 gpointer user_data)
357 {
358 const Account *account_a, *account_b;
359
360 sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
361
362 /* Default ordering uses this column first. */
363 return xaccAccountOrder(account_a, account_b);
364 }
365
366 static gint
sort_by_xxx_value(xaccGetBalanceInCurrencyFn fn,gboolean recurse,GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)367 sort_by_xxx_value (xaccGetBalanceInCurrencyFn fn,
368 gboolean recurse,
369 GtkTreeModel *f_model,
370 GtkTreeIter *f_iter_a,
371 GtkTreeIter *f_iter_b,
372 gpointer user_data)
373 {
374 const Account *account_a, *account_b;
375 const gnc_commodity *cur = gnc_default_currency();
376 gnc_numeric balance_a, balance_b;
377 gint result;
378
379 /* Find the accounts */
380 sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
381
382 /* Get balances */
383 balance_a = gnc_ui_account_get_balance_full(fn, account_a, recurse, NULL, cur);
384 balance_b = gnc_ui_account_get_balance_full(fn, account_b, recurse, NULL, cur);
385
386 result = gnc_numeric_compare(balance_a, balance_b);
387 if (result != 0)
388 return result;
389 return xaccAccountOrder(account_a, account_b);
390 }
391
392 static gint
sort_by_present_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)393 sort_by_present_value (GtkTreeModel *f_model,
394 GtkTreeIter *f_iter_a,
395 GtkTreeIter *f_iter_b,
396 gpointer user_data)
397 {
398 return sort_by_xxx_value (xaccAccountGetPresentBalanceInCurrency, TRUE,
399 f_model, f_iter_a, f_iter_b, user_data);
400 }
401
402 static gint
sort_by_balance_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)403 sort_by_balance_value (GtkTreeModel *f_model,
404 GtkTreeIter *f_iter_a,
405 GtkTreeIter *f_iter_b,
406 gpointer user_data)
407 {
408 return sort_by_xxx_value (xaccAccountGetBalanceInCurrency, TRUE,
409 f_model, f_iter_a, f_iter_b, user_data);
410 }
411
412 static gint
sort_by_cleared_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)413 sort_by_cleared_value (GtkTreeModel *f_model,
414 GtkTreeIter *f_iter_a,
415 GtkTreeIter *f_iter_b,
416 gpointer user_data)
417 {
418 return sort_by_xxx_value (xaccAccountGetClearedBalanceInCurrency, TRUE,
419 f_model, f_iter_a, f_iter_b, user_data);
420 }
421
422 static gint
sort_by_reconciled_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)423 sort_by_reconciled_value (GtkTreeModel *f_model,
424 GtkTreeIter *f_iter_a,
425 GtkTreeIter *f_iter_b,
426 gpointer user_data)
427 {
428 return sort_by_xxx_value (xaccAccountGetReconciledBalanceInCurrency, TRUE,
429 f_model, f_iter_a, f_iter_b, user_data);
430 }
431
432 static gint
sort_by_future_min_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)433 sort_by_future_min_value (GtkTreeModel *f_model,
434 GtkTreeIter *f_iter_a,
435 GtkTreeIter *f_iter_b,
436 gpointer user_data)
437 {
438 return sort_by_xxx_value (xaccAccountGetProjectedMinimumBalanceInCurrency, TRUE,
439 f_model, f_iter_a, f_iter_b, user_data);
440 }
441
442 static gint
sort_by_total_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)443 sort_by_total_value (GtkTreeModel *f_model,
444 GtkTreeIter *f_iter_a,
445 GtkTreeIter *f_iter_b,
446 gpointer user_data)
447 {
448 return sort_by_xxx_value (xaccAccountGetBalanceInCurrency, TRUE,
449 f_model, f_iter_a, f_iter_b, user_data);
450 }
451
452 static gint
sort_by_hidden(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)453 sort_by_hidden (GtkTreeModel *f_model,
454 GtkTreeIter *f_iter_a,
455 GtkTreeIter *f_iter_b,
456 gpointer user_data)
457 {
458 const Account *account_a, *account_b;
459 gboolean flag_a, flag_b;
460
461 /* Find the accounts */
462 sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
463
464 /* Get the placeholder flags. */
465 flag_a = xaccAccountGetHidden (account_a);
466 flag_b = xaccAccountGetHidden (account_b);
467
468 if (flag_a > flag_b)
469 return -1;
470 else if (flag_a < flag_b)
471 return 1;
472 return xaccAccountOrder (account_a, account_b);
473 }
474
475 static gint
sort_by_placeholder(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)476 sort_by_placeholder (GtkTreeModel *f_model,
477 GtkTreeIter *f_iter_a,
478 GtkTreeIter *f_iter_b,
479 gpointer user_data)
480 {
481 const Account *account_a, *account_b;
482 gboolean flag_a, flag_b;
483
484 /* Find the accounts */
485 sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
486
487 /* Get the placeholder flags. */
488 flag_a = xaccAccountGetPlaceholder(account_a);
489 flag_b = xaccAccountGetPlaceholder(account_b);
490
491 if (flag_a > flag_b)
492 return -1;
493 else if (flag_a < flag_b)
494 return 1;
495 return xaccAccountOrder(account_a, account_b);
496 }
497
498 static gint
sort_by_opening_balance(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)499 sort_by_opening_balance (GtkTreeModel *f_model,
500 GtkTreeIter *f_iter_a,
501 GtkTreeIter *f_iter_b,
502 gpointer user_data)
503 {
504 const Account *account_a, *account_b;
505 gboolean flag_a, flag_b;
506
507 /* Find the accounts */
508 sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
509
510 /* Get the opening balance flags. */
511 flag_a = xaccAccountGetIsOpeningBalance (account_a);
512 flag_b = xaccAccountGetIsOpeningBalance (account_b);
513
514 if (flag_a > flag_b)
515 return -1;
516 else if (flag_a < flag_b)
517 return 1;
518 return xaccAccountOrder(account_a, account_b);
519 }
520
521 static gint
sort_by_xxx_period_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gboolean recurse)522 sort_by_xxx_period_value (GtkTreeModel *f_model,
523 GtkTreeIter *f_iter_a,
524 GtkTreeIter *f_iter_b,
525 gboolean recurse)
526 {
527 Account *acct1, *acct2;
528 time64 t1, t2;
529 gnc_numeric b1, b2;
530 gint result;
531
532 sort_cb_setup (f_model, f_iter_a, f_iter_b,
533 (const Account **)&acct1, (const Account **)&acct2);
534
535 t1 = gnc_accounting_period_fiscal_start();
536 t2 = gnc_accounting_period_fiscal_end();
537
538 b1 = xaccAccountGetBalanceChangeForPeriod(acct1, t1, t2, recurse);
539 b2 = xaccAccountGetBalanceChangeForPeriod(acct2, t1, t2, recurse);
540
541 result = gnc_numeric_compare(b1, b2);
542 if (result != 0)
543 return result;
544 return xaccAccountOrder(acct1, acct2);
545 }
546
547 static gint
sort_by_balance_period_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)548 sort_by_balance_period_value (GtkTreeModel *f_model,
549 GtkTreeIter *f_iter_a,
550 GtkTreeIter *f_iter_b,
551 gpointer user_data)
552 {
553 return sort_by_xxx_period_value (f_model, f_iter_a, f_iter_b, FALSE);
554 }
555
556 static gint
sort_by_total_period_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)557 sort_by_total_period_value (GtkTreeModel *f_model,
558 GtkTreeIter *f_iter_a,
559 GtkTreeIter *f_iter_b,
560 gpointer user_data)
561 {
562 return sort_by_xxx_period_value (f_model, f_iter_a, f_iter_b, TRUE);
563 }
564
565 /************************************************************/
566 /* Tax_Info data function */
567 /************************************************************/
568
569 /*
570 * The tax-info column in the account tree view is based on the
571 * combination of two columns in the account tree model. The data
572 * function displays only the the data in the
573 * GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO model column if the row is
574 * expanded; otherwise it combines it with the data
575 * in the GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT model column.
576 */
577 static void
tax_info_data_func(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer view)578 tax_info_data_func (GtkTreeViewColumn *col,
579 GtkCellRenderer *renderer,
580 GtkTreeModel *model,
581 GtkTreeIter *iter,
582 gpointer view)
583 {
584 gchar *tax_info = NULL;
585 GtkTreePath *path;
586
587 gtk_tree_model_get(model,
588 iter,
589 GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO,
590 &tax_info,
591 -1);
592
593 path = gtk_tree_model_get_path(model, iter);
594 if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(view), path))
595 g_object_set(renderer, "text",
596 (tax_info == NULL ? "" : tax_info), NULL);
597 else
598 {
599 gchar *tax_info_sub_acct = NULL;
600
601 gtk_tree_model_get(model,
602 iter,
603 GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT,
604 &tax_info_sub_acct,
605 -1);
606 if ((g_strcmp0 (tax_info_sub_acct, "") == 0) ||
607 (tax_info_sub_acct == NULL))
608 g_object_set(renderer, "text",
609 (tax_info == NULL ? "" : tax_info), NULL);
610 else
611 {
612 if ((g_strcmp0 (tax_info, "") == 0) ||
613 (tax_info == NULL))
614 g_object_set(renderer, "text",
615 (tax_info_sub_acct == NULL ? "" : tax_info_sub_acct),
616 NULL);
617 else
618 {
619 gchar *combined_tax_info;
620 combined_tax_info = g_strdup_printf ("%s; %s",
621 (tax_info == NULL ? "" : tax_info),
622 (tax_info_sub_acct == NULL ? "" :
623 tax_info_sub_acct));
624 g_object_set(renderer, "text", combined_tax_info, NULL);
625 g_free(combined_tax_info);
626 }
627 }
628 g_free(tax_info_sub_acct);
629 }
630 g_free(tax_info);
631 gtk_tree_path_free(path);
632 }
633
634 /************************************************************/
635 /* acc_color data function */
636 /************************************************************/
637 /*
638 * The account-color column in the account tree view is obtained
639 * from the GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT which is
640 * checked for a valid color string to set the background color
641 * of the cell.
642 */
643 static void
update_cell_renderers(GList * renderers,gchar * account_color)644 update_cell_renderers (GList *renderers, gchar *account_color)
645 {
646 GtkCellRenderer *cell;
647 GList *node;
648
649 /* Update the cell background in the list of renderers */
650 for (node = renderers; node; node = node->next)
651 {
652 cell = node->data;
653 g_object_set (cell, "cell-background", account_color, NULL);
654 }
655 }
656
657 /* Colorizes a cell in the account tree view if
658 * - a color is assigned to the given account
659 * - the user enabled account colorization in the preferences
660 * Only the account color column is special: it will always
661 * be colored if a valid color was set, regardless of the
662 * preference setting.
663 */
664 static void
acc_color_data_func(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer view)665 acc_color_data_func (GtkTreeViewColumn *col,
666 GtkCellRenderer *renderer,
667 GtkTreeModel *model,
668 GtkTreeIter *iter,
669 gpointer view)
670 {
671 GncTreeViewAccountPrivate *priv;
672 gchar *acc_color = NULL, *acc_cond_color = NULL;
673 gchar *item;
674 GdkRGBA color;
675 gchar *column_name;
676 GList *renderers;
677
678 gtk_tree_model_get(model,
679 iter,
680 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT,
681 &item,
682 -1);
683
684 /* Check if color was set for the account */
685 if ((item) && (*item != '\0'))
686 acc_color = g_strstrip(g_strdup(item));
687 g_free (item);
688
689 /* Test if the color string represents a valid color */
690 if (acc_color && (!gdk_rgba_parse(&color, acc_color)))
691 {
692 g_free (acc_color);
693 acc_color = NULL;
694 }
695
696 /* Determine whether columns other than the
697 * Account Color column should be colored. */
698 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
699 if (priv->show_account_color)
700 acc_cond_color = acc_color;
701
702 column_name = g_object_get_data(G_OBJECT(col), PREF_NAME);
703 renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (col));
704
705 /* Account Color column is always colored, other columns only conditionally. */
706 if (g_strcmp0(column_name, "account-color") == 0)
707 update_cell_renderers (renderers, acc_color);
708 else
709 update_cell_renderers (renderers, acc_cond_color);
710
711 g_list_free (renderers);
712 g_free (acc_color);
713 }
714
715 /** Tell the GncTreeViewAccount code to show or not show the
716 * Account name column with background in the account color.
717 *
718 * @internal
719 */
720 static void
gnc_tree_view_account_color_update(gpointer gsettings,gchar * key,gpointer user_data)721 gnc_tree_view_account_color_update (gpointer gsettings, gchar *key, gpointer user_data)
722 {
723 GncTreeViewAccountPrivate *priv;
724 GncTreeViewAccount *view;
725
726 g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(user_data));
727 view = user_data;
728 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
729 if (g_strcmp0 (key, GNC_PREF_ACCOUNT_COLOR) == 0)
730 priv->show_account_color = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, key);
731
732 // do a refilter so the tree view background color gets updated
733 gnc_tree_view_account_refilter (view);
734 }
735
736 /** Add the account color background data function to the GncTreeViewAccount column to
737 * show or not the column background in the account color.
738 */
739 void
gnc_tree_view_account_column_add_color(GncTreeViewAccount * view,GtkTreeViewColumn * col)740 gnc_tree_view_account_column_add_color (GncTreeViewAccount *view, GtkTreeViewColumn *col)
741 {
742 GtkCellRenderer *renderer = gnc_tree_view_column_get_renderer(col);
743
744 gtk_tree_view_column_set_cell_data_func (col, renderer, acc_color_data_func,
745 GTK_TREE_VIEW(view), NULL);
746 }
747
748 /************************************************************/
749 /* New View Creation */
750 /************************************************************/
751
752 /*
753 * Create a new account tree view with (optional) top level root node.
754 * This view will be based on a model that is common to all view of
755 * the same set of books, but will have its own private filter on that
756 * model.
757 */
758 GtkTreeView *
gnc_tree_view_account_new_with_root(Account * root,gboolean show_root)759 gnc_tree_view_account_new_with_root (Account *root, gboolean show_root)
760 {
761 GncTreeView *view;
762 GtkTreeModel *model, *f_model, *s_model;
763 GtkTreePath *virtual_root_path = NULL;
764 const gchar *sample_type, *sample_commodity;
765 GncTreeViewAccountPrivate *priv;
766 GtkTreeViewColumn *tax_info_column, *acc_color_column;
767 GtkCellRenderer *renderer;
768 GList *col_list = NULL, *node = NULL;
769
770 ENTER(" ");
771 /* Create our view */
772 view = g_object_new (GNC_TYPE_TREE_VIEW_ACCOUNT,
773 "name", "gnc-id-account-tree", NULL);
774
775 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(GNC_TREE_VIEW_ACCOUNT (view));
776
777 /* Get the show_account_color value from gsettings */
778 priv->show_account_color = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_COLOR);
779
780 /* Create/get a pointer to the existing model for this set of books. */
781 model = gnc_tree_model_account_new (root);
782
783 /* Set up the view private filter layer on the common model. */
784 if (!show_root)
785 virtual_root_path = gtk_tree_path_new_first ();
786 f_model = gtk_tree_model_filter_new (model, virtual_root_path);
787 /* A GncTreeModelAccount is based on a GncTreeModel, which is a
788 * GObject that provides a GtkTreeModel interface. */
789 g_object_unref(G_OBJECT(model));
790 if (virtual_root_path)
791 gtk_tree_path_free(virtual_root_path);
792
793 /* Set up the view private sort layer on the common model. */
794 s_model = gtk_tree_model_sort_new_with_model(f_model);
795 g_object_unref(G_OBJECT(f_model));
796 gtk_tree_view_set_model (GTK_TREE_VIEW (view), s_model);
797 g_object_unref(G_OBJECT(s_model));
798
799 /* Set default visibilities */
800 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(view), FALSE);
801
802 sample_type = xaccAccountGetTypeStr(ACCT_TYPE_CREDIT);
803 sample_commodity = gnc_commodity_get_fullname(gnc_default_currency());
804
805 priv->name_column
806 = gnc_tree_view_add_text_column(view, _("Account Name"), "name",
807 GNC_ICON_ACCOUNT, "Expenses:Entertainment",
808 GNC_TREE_MODEL_ACCOUNT_COL_NAME,
809 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
810 sort_by_string);
811
812 gnc_tree_view_add_text_column(view, _("Type"), "type", NULL, sample_type,
813 GNC_TREE_MODEL_ACCOUNT_COL_TYPE,
814 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
815 sort_by_string);
816
817 gnc_tree_view_add_text_column(view, _("Commodity"), "commodity", NULL,
818 sample_commodity,
819 GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY,
820 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
821 sort_by_string);
822 priv->code_column
823 = gnc_tree_view_add_text_column(view, _("Account Code"), "account-code", NULL,
824 "1-123-1234",
825 GNC_TREE_MODEL_ACCOUNT_COL_CODE,
826 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
827 sort_by_code);
828 priv->desc_column
829 = gnc_tree_view_add_text_column(view, _("Description"), "description", NULL,
830 "Sample account description.",
831 GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION,
832 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
833 sort_by_string);
834
835 gnc_tree_view_add_numeric_column(view, _("Last Num"), "lastnum", "12345",
836 GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM,
837 GNC_TREE_VIEW_COLUMN_COLOR_NONE,
838 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
839 sort_by_string);
840
841 gnc_tree_view_add_numeric_column(view, _("Present"), "present",
842 SAMPLE_ACCOUNT_VALUE,
843 GNC_TREE_MODEL_ACCOUNT_COL_PRESENT,
844 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT,
845 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
846 sort_by_present_value);
847 priv->present_report_column
848 = gnc_tree_view_add_numeric_column(view, _("Present (Report)"), "present_report",
849 SAMPLE_ACCOUNT_VALUE,
850 GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT,
851 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT,
852 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
853 sort_by_present_value);
854
855 gnc_tree_view_add_numeric_column(view, _("Balance"), "balance",
856 SAMPLE_ACCOUNT_VALUE,
857 GNC_TREE_MODEL_ACCOUNT_COL_BALANCE,
858 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE,
859 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
860 sort_by_balance_value);
861 priv->balance_report_column
862 = gnc_tree_view_add_numeric_column(view, _("Balance (Report)"), "balance_report",
863 SAMPLE_ACCOUNT_VALUE,
864 GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT,
865 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE,
866 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
867 sort_by_balance_value);
868
869 gnc_tree_view_add_numeric_column(view, _("Balance (Period)"), "balance-period",
870 SAMPLE_ACCOUNT_VALUE,
871 GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD,
872 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD,
873 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
874 sort_by_balance_period_value);
875
876 gnc_tree_view_add_numeric_column(view, _("Cleared"), "cleared",
877 SAMPLE_ACCOUNT_VALUE,
878 GNC_TREE_MODEL_ACCOUNT_COL_CLEARED,
879 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED,
880 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
881 sort_by_cleared_value);
882 priv->cleared_report_column
883 = gnc_tree_view_add_numeric_column(view, _("Cleared (Report)"), "cleared_report",
884 SAMPLE_ACCOUNT_VALUE,
885 GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT,
886 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED,
887 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
888 sort_by_cleared_value);
889
890 gnc_tree_view_add_numeric_column(view, _("Reconciled"), "reconciled",
891 SAMPLE_ACCOUNT_VALUE,
892 GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED,
893 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED,
894 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
895 sort_by_reconciled_value);
896 priv->reconciled_report_column
897 = gnc_tree_view_add_numeric_column(view, _("Reconciled (Report)"), "reconciled_report",
898 SAMPLE_ACCOUNT_VALUE,
899 GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT,
900 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED,
901 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
902 sort_by_reconciled_value);
903
904 gnc_tree_view_add_text_column(view, _("Last Reconcile Date"), "last-recon-date", NULL,
905 "Last Reconcile Date",
906 GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE,
907 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
908 sort_by_last_reconcile_date);
909
910 gnc_tree_view_add_numeric_column(view, _("Future Minimum"), "future_min",
911 SAMPLE_ACCOUNT_VALUE,
912 GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN,
913 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN,
914 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
915 sort_by_future_min_value);
916 priv->future_min_report_column
917 = gnc_tree_view_add_numeric_column(view, _("Future Minimum (Report)"), "future_min_report",
918 SAMPLE_ACCOUNT_VALUE,
919 GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT,
920 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN,
921 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
922 sort_by_future_min_value);
923
924 gnc_tree_view_add_numeric_column(view, _("Total"), "total",
925 SAMPLE_ACCOUNT_VALUE,
926 GNC_TREE_MODEL_ACCOUNT_COL_TOTAL,
927 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL,
928 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
929 sort_by_total_value);
930 priv->total_report_column
931 = gnc_tree_view_add_numeric_column(view, _("Total (Report)"), "total_report",
932 SAMPLE_ACCOUNT_VALUE,
933 GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT,
934 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL,
935 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
936 sort_by_total_value);
937
938 gnc_tree_view_add_numeric_column(view, _("Total (Period)"), "total-period",
939 SAMPLE_ACCOUNT_VALUE,
940 GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD,
941 GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD,
942 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
943 sort_by_total_period_value);
944
945 /* Translators: The C is the column title and stands for Color, this should be one character */
946 acc_color_column
947 = gnc_tree_view_add_text_column(view, C_("Column header for 'Color'", "C"), "account-color", NULL,
948 "xx",
949 GNC_TREE_VIEW_COLUMN_DATA_NONE,
950 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
951 NULL);
952
953 /* Add the full title to the object for menu creation */
954 g_object_set_data_full(G_OBJECT(acc_color_column), REAL_TITLE,
955 g_strdup(_("Account Color")), g_free);
956
957 /* Also add the full title to the column header as a tooltip */
958 gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (acc_color_column), _("Account Color"));
959
960 priv->notes_column
961 = gnc_tree_view_add_text_view_column(view, _("Notes"), "notes", NULL,
962 "Sample account notes.",
963 GNC_TREE_MODEL_ACCOUNT_COL_NOTES,
964 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
965 sort_by_string);
966
967 tax_info_column
968 = gnc_tree_view_add_text_column(view, _("Tax Info"), "tax-info", NULL,
969 "Sample tax info.",
970 GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO,
971 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
972 sort_by_string);
973
974 renderer = gnc_tree_view_column_get_renderer(tax_info_column);
975 gtk_tree_view_column_set_cell_data_func(tax_info_column,
976 renderer,
977 tax_info_data_func,
978 GTK_TREE_VIEW(view),
979 NULL);
980
981 gnc_tree_view_add_toggle_column (view, _("Hidden"),
982 C_("Column header for 'Hidden'", "H"),
983 "hidden",
984 GNC_TREE_MODEL_ACCOUNT_COL_HIDDEN,
985 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
986 sort_by_hidden,
987 gnc_tree_view_account_hidden_toggled);
988
989 gnc_tree_view_add_toggle_column(view, _("Placeholder"),
990 C_("Column header for 'Placeholder'", "P"),
991 "placeholder",
992 GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER,
993 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
994 sort_by_placeholder,
995 gnc_tree_view_account_placeholder_toggled);
996
997 gnc_tree_view_add_toggle_column(view, _("Opening Balance"),
998 C_("Column header for 'Opening Balance'", "O"),
999 "opening-balance",
1000 GNC_TREE_MODEL_ACCOUNT_COL_OPENING_BALANCE,
1001 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
1002 sort_by_opening_balance,
1003 NULL);
1004
1005 /* Add function to each column that optionally sets a background color for accounts */
1006 col_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
1007 for (node = col_list; node; node = node->next)
1008 {
1009 renderer = gnc_tree_view_column_get_renderer(node->data);
1010 gtk_tree_view_column_set_cell_data_func(node->data,
1011 renderer,
1012 acc_color_data_func,
1013 GTK_TREE_VIEW(view),
1014 NULL);
1015 }
1016 g_list_free (col_list);
1017
1018 /* Update column titles to use the currency name. */
1019 gtva_update_column_names(view);
1020
1021 /* By default only the first column is visible. */
1022 gnc_tree_view_configure_columns(view);
1023 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (f_model),
1024 gnc_tree_view_account_filter_helper,
1025 view,
1026 NULL);
1027
1028 /* Default the sorting to account name */
1029 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
1030 GNC_TREE_MODEL_ACCOUNT_COL_NAME,
1031 GTK_SORT_ASCENDING);
1032
1033 /* Set account find-as-you-type search function */
1034 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW(view), gnc_tree_view_search_compare, NULL, NULL);
1035
1036 gtk_widget_show(GTK_WIDGET(view));
1037 LEAVE("%p", view);
1038 return GTK_TREE_VIEW(view);
1039 }
1040
1041 /*
1042 * Create a new account tree view with (optional) top level root node.
1043 * This view will be based on a model that is common to all view of
1044 * the same set of books, but will have its own private filter on that
1045 * model.
1046 */
1047 GtkTreeView *
gnc_tree_view_account_new(gboolean show_root)1048 gnc_tree_view_account_new (gboolean show_root)
1049 {
1050 Account *root;
1051
1052 root = gnc_book_get_root_account (gnc_get_current_book ());
1053 return gnc_tree_view_account_new_with_root (root, show_root);
1054 }
1055
1056 /************************************************************/
1057 /* Auxiliary Functions */
1058 /************************************************************/
1059
1060 #define debug_path(fn, path) { \
1061 gchar *path_string = gtk_tree_path_to_string(path); \
1062 fn("tree path %s", path_string); \
1063 g_free(path_string); \
1064 }
1065
1066 static GtkTreePath *
gnc_tree_view_account_get_path_from_account(GncTreeViewAccount * view,Account * account)1067 gnc_tree_view_account_get_path_from_account (GncTreeViewAccount *view,
1068 Account *account)
1069 {
1070 GtkTreeModel *model, *f_model, *s_model;
1071 GtkTreePath *path, *f_path, *s_path;
1072
1073 ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
1074
1075 if (account == NULL)
1076 {
1077 LEAVE("no account");
1078 return NULL;
1079 }
1080
1081 /* Reach down to the real model and get a path for this account */
1082 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1083 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1084 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1085 path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
1086 if (path == NULL)
1087 {
1088 LEAVE("no path");
1089 return NULL;
1090 }
1091
1092 /* convert back to a filtered path */
1093 f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model), path);
1094 gtk_tree_path_free(path);
1095 if (!f_path)
1096 {
1097 LEAVE("no filter path");
1098 return NULL;
1099 }
1100
1101 /* convert back to a sorted path */
1102 s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model), f_path);
1103 gtk_tree_path_free(f_path);
1104 debug_path(LEAVE, s_path);
1105 return s_path;
1106 }
1107
1108 static gboolean
gnc_tree_view_account_get_iter_from_account(GncTreeViewAccount * view,Account * account,GtkTreeIter * s_iter)1109 gnc_tree_view_account_get_iter_from_account (GncTreeViewAccount *view,
1110 Account *account,
1111 GtkTreeIter *s_iter)
1112 {
1113 GtkTreeModel *model, *f_model, *s_model;
1114 GtkTreeIter iter, f_iter;
1115
1116 g_return_val_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view), FALSE);
1117 g_return_val_if_fail(account != NULL, FALSE);
1118 g_return_val_if_fail(s_iter != NULL, FALSE);
1119
1120 ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
1121
1122 /* Reach down to the real model and get an iter for this account */
1123 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1124 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1125 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1126 if (!gnc_tree_model_account_get_iter_from_account (
1127 GNC_TREE_MODEL_ACCOUNT(model), account, &iter))
1128 {
1129 LEAVE("model_get_iter_from_account failed");
1130 return FALSE;
1131 }
1132
1133 /* convert back to a sort iter */
1134 gtk_tree_model_filter_convert_child_iter_to_iter (
1135 GTK_TREE_MODEL_FILTER(f_model), &f_iter, &iter);
1136 gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(s_model),
1137 s_iter, &f_iter);
1138 LEAVE(" ");
1139 return TRUE;
1140 }
1141
1142 gint
gnc_tree_view_account_count_children(GncTreeViewAccount * view,Account * account)1143 gnc_tree_view_account_count_children (GncTreeViewAccount *view,
1144 Account *account)
1145 {
1146 GtkTreeModel *s_model;
1147 GtkTreeIter s_iter;
1148 gint num_children;
1149
1150 ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
1151
1152 if (account == NULL)
1153 {
1154 LEAVE("no account");
1155 return 0;
1156 }
1157
1158 if (!gnc_tree_view_account_get_iter_from_account (view, account, &s_iter))
1159 {
1160 LEAVE("view_get_iter_from_account failed");
1161 return 0;
1162 }
1163
1164 /* Any children? */
1165 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1166 num_children = gtk_tree_model_iter_n_children(s_model, &s_iter);
1167 LEAVE("%d children", num_children);
1168 return num_children;
1169 }
1170
1171 void
gnc_tree_view_account_clear_model_cache(GncTreeViewAccount * view)1172 gnc_tree_view_account_clear_model_cache (GncTreeViewAccount *view)
1173 {
1174 GtkTreeModel *model, *f_model, *s_model;
1175
1176 s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
1177 f_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(s_model));
1178 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(f_model));
1179
1180 gnc_tree_model_account_clear_cache (GNC_TREE_MODEL_ACCOUNT(model));
1181 }
1182
1183 /************************************************************/
1184 /* Account Tree View Filter Functions */
1185 /************************************************************/
1186
1187 /*
1188 * Get a copy of the account view info structure in use by the
1189 * specified tree.
1190 */
1191 void
gnc_tree_view_account_get_view_info(GncTreeViewAccount * account_view,AccountViewInfo * avi)1192 gnc_tree_view_account_get_view_info (GncTreeViewAccount *account_view,
1193 AccountViewInfo *avi)
1194 {
1195 GncTreeViewAccountPrivate *priv;
1196
1197 g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
1198 g_return_if_fail(avi != NULL);
1199
1200 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
1201
1202 *avi = priv->avi;
1203 }
1204
1205 /*
1206 * Set the account view info data in use by the specified tree to
1207 * match the callers request.
1208 *
1209 * DRH - COMPATIBILITY WARNING
1210 *
1211 * This function does not do anything with the 'include_type' field.
1212 * Should there be a automatic filter for backward compatibility
1213 * that uses these flags, or should all uses of this be converted to
1214 * a GtkTreeModelFilter?
1215 *
1216 * CAS - For now, I'll try the automatic filter approach by making
1217 * this function use GtkTreeModelFilter.
1218 */
1219 void
gnc_tree_view_account_set_view_info(GncTreeViewAccount * account_view,AccountViewInfo * avi)1220 gnc_tree_view_account_set_view_info (GncTreeViewAccount *account_view,
1221 AccountViewInfo *avi)
1222 {
1223 GncTreeViewAccountPrivate *priv;
1224 gint i;
1225 guint sel_bits = 0;
1226
1227 ENTER("%p", account_view);
1228 g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
1229 g_return_if_fail(avi != NULL);
1230
1231 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
1232 priv->avi = *avi;
1233
1234 for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
1235 {
1236 sel_bits |= avi->include_type[i] ? (1 << i) : 0;
1237 }
1238
1239 gnc_tree_view_account_set_filter(
1240 account_view, gnc_tree_view_account_filter_by_view_info,
1241 &priv->avi, NULL);
1242
1243 LEAVE(" ");
1244 }
1245
1246 static gboolean
gnc_tree_view_account_filter_helper(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1247 gnc_tree_view_account_filter_helper (GtkTreeModel *model,
1248 GtkTreeIter *iter,
1249 gpointer data)
1250 {
1251 Account *account;
1252 GncTreeViewAccount *view = data;
1253 GncTreeViewAccountPrivate *priv;
1254
1255 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), FALSE);
1256 g_return_val_if_fail (iter != NULL, FALSE);
1257
1258 account = gnc_tree_model_account_get_account (
1259 GNC_TREE_MODEL_ACCOUNT(model), iter);
1260
1261 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
1262 if (priv->filter_fn)
1263 return priv->filter_fn(account, priv->filter_data);
1264 else return TRUE;
1265 }
1266
1267 /*
1268 * Set an GtkTreeModel visible filter on this account. This filter will be
1269 * called for each account that the tree is about to show, and the
1270 * account will be passed to the callback function.
1271 *
1272 * Use NULL as func to remove filter.
1273 */
1274 void
gnc_tree_view_account_set_filter(GncTreeViewAccount * view,gnc_tree_view_account_filter_func func,gpointer data,GSourceFunc destroy)1275 gnc_tree_view_account_set_filter (GncTreeViewAccount *view,
1276 gnc_tree_view_account_filter_func func,
1277 gpointer data,
1278 GSourceFunc destroy)
1279 {
1280 GncTreeViewAccountPrivate *priv;
1281
1282 ENTER("view %p, filter func %p, data %p, destroy %p",
1283 view, func, data, destroy);
1284
1285 g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
1286
1287 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
1288 if (priv->filter_destroy)
1289 {
1290 priv->filter_destroy(priv->filter_data);
1291 }
1292 priv->filter_destroy = destroy;
1293 priv->filter_data = data;
1294 priv->filter_fn = func;
1295
1296 gnc_tree_view_account_refilter(view);
1297 LEAVE(" ");
1298 }
1299
1300 /*
1301 * Forces the entire account tree to be re-evaluated for visibility.
1302 */
1303 void
gnc_tree_view_account_refilter(GncTreeViewAccount * view)1304 gnc_tree_view_account_refilter (GncTreeViewAccount *view)
1305 {
1306 GtkTreeModel *f_model, *s_model;
1307
1308 g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
1309
1310 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1311 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1312 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (f_model));
1313 }
1314
1315 gboolean
gnc_tree_view_account_filter_by_view_info(Account * acct,gpointer data)1316 gnc_tree_view_account_filter_by_view_info(Account* acct, gpointer data)
1317 {
1318 GNCAccountType acct_type;
1319 AccountViewInfo* avi = (AccountViewInfo*)data;
1320
1321 g_return_val_if_fail(GNC_IS_ACCOUNT(acct), FALSE);
1322 acct_type = xaccAccountGetType(acct);
1323
1324 if (!avi->include_type[acct_type]) return FALSE;
1325 if (!avi->show_hidden && xaccAccountIsHidden(acct)) return FALSE;
1326 return TRUE;
1327 }
1328
1329 /************************************************************/
1330 /* Account Tree View Get/Set Functions */
1331 /************************************************************/
1332
1333 /*
1334 * Retrieve the selected account from an account tree view. The
1335 * account tree must be in single selection mode.
1336 */
1337 Account *
gnc_tree_view_account_get_account_from_path(GncTreeViewAccount * view,GtkTreePath * s_path)1338 gnc_tree_view_account_get_account_from_path (GncTreeViewAccount *view,
1339 GtkTreePath *s_path)
1340 {
1341 GtkTreeModel *model, *f_model, *s_model;
1342 GtkTreePath *path, *f_path;
1343 GtkTreeIter iter;
1344 Account *account;
1345
1346 ENTER("view %p", view);
1347 g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1348 g_return_val_if_fail (s_path != NULL, NULL);
1349
1350 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1351 f_path = gtk_tree_model_sort_convert_path_to_child_path (
1352 GTK_TREE_MODEL_SORT (s_model), s_path);
1353 if (!f_path)
1354 {
1355 LEAVE("no filter path");
1356 return NULL;
1357 }
1358
1359 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1360 path = gtk_tree_model_filter_convert_path_to_child_path (
1361 GTK_TREE_MODEL_FILTER (f_model), f_path);
1362 gtk_tree_path_free(f_path);
1363 if (!path)
1364 {
1365 LEAVE("no path");
1366 return NULL;
1367 }
1368
1369 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1370 if (!gtk_tree_model_get_iter (model, &iter, path))
1371 {
1372 LEAVE("no iter");
1373 return NULL;
1374 }
1375
1376 account = iter.user_data;
1377 gtk_tree_path_free(path);
1378 LEAVE("account %p (%s)", account, xaccAccountGetName (account));
1379 return account;
1380 }
1381
1382
1383 Account *
gnc_tree_view_account_get_account_from_iter(GtkTreeModel * s_model,GtkTreeIter * s_iter)1384 gnc_tree_view_account_get_account_from_iter (GtkTreeModel *s_model,
1385 GtkTreeIter *s_iter)
1386 {
1387 GtkTreeModel *model, *f_model;
1388 GtkTreeIter iter, f_iter;
1389 Account *account;
1390
1391 g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT(s_model), NULL);
1392 g_return_val_if_fail (s_iter != NULL, NULL);
1393
1394 ENTER("model %p, iter %p", s_model, s_iter);
1395
1396 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(s_model),
1397 &f_iter,
1398 s_iter);
1399 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1400 gtk_tree_model_filter_convert_iter_to_child_iter (
1401 GTK_TREE_MODEL_FILTER(f_model), &iter, &f_iter);
1402 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1403 account = gnc_tree_model_account_get_account (
1404 GNC_TREE_MODEL_ACCOUNT(model), &iter);
1405 LEAVE("account %p (%s)", account, xaccAccountGetName (account));
1406 return account;
1407 }
1408
1409
1410 /*
1411 * Retrieve the selected account from an account tree view. The
1412 * account tree must be in single selection mode.
1413 */
1414 Account *
gnc_tree_view_account_get_selected_account(GncTreeViewAccount * view)1415 gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view)
1416 {
1417 GtkTreeSelection *selection;
1418 GtkTreeModel *f_model, *s_model;
1419 GtkTreeIter iter, f_iter, s_iter;
1420 Account *account;
1421 GtkSelectionMode mode;
1422
1423 ENTER("view %p", view);
1424 g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1425
1426 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
1427 mode = gtk_tree_selection_get_mode(selection);
1428 if ((mode != GTK_SELECTION_SINGLE) && (mode != GTK_SELECTION_BROWSE))
1429 {
1430 return NULL;
1431 }
1432 if (!gtk_tree_selection_get_selected (selection, &s_model, &s_iter))
1433 {
1434 LEAVE("no account, get_selected failed");
1435 return FALSE;
1436 }
1437
1438 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
1439 &f_iter, &s_iter);
1440
1441 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1442 gtk_tree_model_filter_convert_iter_to_child_iter (
1443 GTK_TREE_MODEL_FILTER (f_model), &iter, &f_iter);
1444
1445 account = iter.user_data;
1446 LEAVE("account %p (%s)", account, xaccAccountGetName (account));
1447 return account;
1448 }
1449
1450 /*
1451 * Selects a single account in the account tree view. The account
1452 * tree must be in single selection mode.
1453 */
1454 void
gnc_tree_view_account_set_selected_account(GncTreeViewAccount * view,Account * account)1455 gnc_tree_view_account_set_selected_account (GncTreeViewAccount *view,
1456 Account *account)
1457 {
1458 GtkTreeModel *model, *f_model, *s_model;
1459 GtkTreePath *path, *f_path, *s_path, *parent_path;
1460 GtkTreeSelection *selection;
1461
1462 ENTER("view %p, account %p (%s)", view,
1463 account, xaccAccountGetName (account));
1464 g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
1465
1466 /* Clear any existing selection. */
1467 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1468 gtk_tree_selection_unselect_all (selection);
1469
1470 if (account == NULL)
1471 return;
1472
1473 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1474 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1475 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1476
1477 path = gnc_tree_model_account_get_path_from_account (
1478 GNC_TREE_MODEL_ACCOUNT(model), account);
1479 if (path == NULL)
1480 {
1481 LEAVE("no path");
1482 return;
1483 }
1484 debug_path(DEBUG, path);
1485
1486 f_path = gtk_tree_model_filter_convert_child_path_to_path (
1487 GTK_TREE_MODEL_FILTER (f_model), path);
1488 gtk_tree_path_free(path);
1489 if (f_path == NULL)
1490 {
1491 LEAVE("no filter path");
1492 return;
1493 }
1494 debug_path(DEBUG, f_path);
1495
1496 s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
1497 f_path);
1498 gtk_tree_path_free(f_path);
1499 if (s_path == NULL)
1500 {
1501 LEAVE("no sort path");
1502 return;
1503 }
1504
1505 /* gtk_tree_view requires that a row be visible before it can be selected */
1506 parent_path = gtk_tree_path_copy (s_path);
1507 if (gtk_tree_path_up (parent_path))
1508 {
1509 /* This function is misnamed. It expands the actual item
1510 * specified, not the path to the item specified. I.E. It expands
1511 * one level too many, thus the get of the parent. */
1512 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
1513 }
1514 gtk_tree_path_free(parent_path);
1515
1516 gtk_tree_selection_select_path (selection, s_path);
1517
1518 /* give gtk+ a chance to resize the tree view first by handling pending
1519 * configure events */
1520 while (gtk_events_pending ())
1521 gtk_main_iteration ();
1522 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
1523 debug_path(LEAVE, s_path);
1524 gtk_tree_path_free(s_path);
1525 }
1526
1527 /* Information re selection process */
1528 typedef struct
1529 {
1530 GList* return_list;
1531 GncTreeViewAccountPrivate* priv;
1532 } GncTreeViewSelectionInfo;
1533
1534 /*
1535 * This helper function is called once for each row in the tree view
1536 * that is currently selected. Its task is to append the corresponding
1537 * account to the end of a glist.
1538 */
1539 static void
get_selected_accounts_helper(GtkTreeModel * s_model,GtkTreePath * s_path,GtkTreeIter * s_iter,gpointer data)1540 get_selected_accounts_helper (GtkTreeModel *s_model,
1541 GtkTreePath *s_path,
1542 GtkTreeIter *s_iter,
1543 gpointer data)
1544 {
1545 GncTreeViewSelectionInfo *gtvsi = data;
1546 GtkTreeModel *f_model;
1547 GtkTreeIter iter, f_iter;
1548 Account *account;
1549
1550 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
1551 &f_iter, s_iter);
1552
1553 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1554 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
1555 &iter, &f_iter);
1556 account = iter.user_data;
1557
1558 /* Only selected if it passes the filter */
1559 if (gtvsi->priv->filter_fn == NULL || gtvsi->priv->filter_fn(account, gtvsi->priv->filter_data))
1560 {
1561 gtvsi->return_list = g_list_prepend (gtvsi->return_list, account);
1562 }
1563 }
1564
1565 /*
1566 * Given an account tree view, return a list of the selected accounts. The
1567 * account tree must be in multiple selection mode.
1568 *
1569 * Note: It is the responsibility of the caller to free the returned
1570 * list.
1571 */
1572 GList *
gnc_tree_view_account_get_selected_accounts(GncTreeViewAccount * view)1573 gnc_tree_view_account_get_selected_accounts (GncTreeViewAccount *view)
1574 {
1575 GtkTreeSelection *selection;
1576 GncTreeViewSelectionInfo info;
1577
1578 g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1579
1580 info.return_list = NULL;
1581 info.priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
1582 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
1583 gtk_tree_selection_selected_foreach(selection, get_selected_accounts_helper, &info);
1584 info.return_list = g_list_reverse (info.return_list);
1585 return info.return_list;
1586 }
1587
1588 /*
1589 * Given an account tree view and a list of accounts, select those
1590 * accounts in the tree view.
1591 */
1592 void
gnc_tree_view_account_set_selected_accounts(GncTreeViewAccount * view,GList * account_list,gboolean show_last)1593 gnc_tree_view_account_set_selected_accounts (GncTreeViewAccount *view,
1594 GList *account_list,
1595 gboolean show_last)
1596 {
1597 GtkTreeModel *model, *f_model, *s_model;
1598 GtkTreePath *path, *f_path, *s_path, *parent_path;
1599 GtkTreeSelection *selection;
1600 GList *element;
1601 Account *account;
1602
1603 g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
1604
1605 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1606 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1607 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1608
1609 /* Clear any existing selection. */
1610 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1611 gtk_tree_selection_unselect_all (selection);
1612 gtk_tree_view_collapse_all (GTK_TREE_VIEW(view));
1613
1614 /* Now go select what the user requested. */
1615 for (element = account_list; element; )
1616 {
1617 account = element->data;
1618 element = g_list_next(element);
1619
1620 if (account == NULL)
1621 {
1622 /*
1623 * Oops. Someone must have deleted this account and not cleaned
1624 * up all references to it.
1625 */
1626 continue;
1627 }
1628
1629 path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
1630 if (path == NULL)
1631 {
1632 /*
1633 * Oops. Someone must have deleted this account and not cleaned
1634 * up all references to it.
1635 */
1636 continue;
1637 }
1638
1639 f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model),
1640 path);
1641 gtk_tree_path_free(path);
1642 if (f_path == NULL)
1643 continue;
1644
1645 s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
1646 f_path);
1647 gtk_tree_path_free(f_path);
1648 if (s_path == NULL)
1649 continue;
1650
1651 /* gtk_tree_view requires that a row be visible before it can be selected */
1652 parent_path = gtk_tree_path_copy (s_path);
1653 if (gtk_tree_path_up (parent_path))
1654 {
1655 /* This function is misnamed. It expands the actual item
1656 * specified, not the path to the item specified. I.E. It
1657 * expands one level too many, thus the get of the parent. */
1658 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
1659 }
1660 gtk_tree_path_free(parent_path);
1661
1662 gtk_tree_selection_select_path (selection, s_path);
1663 if (show_last && (element == NULL))
1664 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
1665 gtk_tree_path_free(s_path);
1666 }
1667 }
1668
1669 /*
1670 * Selects all sub-accounts of an account.
1671 */
1672 void
gnc_tree_view_account_select_subaccounts(GncTreeViewAccount * view,Account * account)1673 gnc_tree_view_account_select_subaccounts (GncTreeViewAccount *view,
1674 Account *account)
1675 {
1676 GtkTreeModel *s_model;
1677 GtkTreeSelection *selection;
1678 GtkTreePath *sp_account, *sp_start, *sp_end;
1679 GtkTreeIter si_account, si_start, si_end;
1680 gboolean have_start, have_end = FALSE;
1681 gint num_children;
1682
1683 ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
1684
1685 g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
1686
1687 if (account == NULL)
1688 {
1689 LEAVE("no account");
1690 return;
1691 }
1692
1693 if (!gnc_tree_view_account_get_iter_from_account (view, account, &si_account))
1694 {
1695 LEAVE("view_get_iter_from_account failed");
1696 return;
1697 }
1698
1699 /* Any children? */
1700 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1701 num_children = gtk_tree_model_iter_n_children(s_model, &si_account);
1702 if (num_children == 0)
1703 {
1704 LEAVE("no children");
1705 return;
1706 }
1707
1708 /* Expand the tree. Required for selection to work */
1709 sp_account = gtk_tree_model_get_path (s_model, &si_account);
1710 gtk_tree_view_expand_row (GTK_TREE_VIEW(view), sp_account, TRUE);
1711
1712 /* compute start/end paths */
1713 have_start = gtk_tree_model_iter_nth_child(s_model, &si_start, &si_account, 0);
1714 si_end = si_account;
1715 while (num_children)
1716 {
1717 GtkTreeIter tmp_iter = si_end;
1718 have_end = gtk_tree_model_iter_nth_child(s_model, &si_end, &tmp_iter,
1719 num_children - 1);
1720 if (have_end)
1721 num_children = gtk_tree_model_iter_n_children(s_model, &si_end);
1722 else
1723 num_children = 0;
1724 }
1725
1726 if (have_start && have_end)
1727 {
1728 sp_start = gtk_tree_model_get_path (s_model, &si_start);
1729 sp_end = gtk_tree_model_get_path (s_model, &si_end);
1730
1731 /* select everything between */
1732 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1733 gtk_tree_selection_select_range (selection, sp_start, sp_end);
1734
1735 /* clean up */
1736 gtk_tree_path_free(sp_start);
1737 gtk_tree_path_free(sp_end);
1738 }
1739 gtk_tree_path_free(sp_account);
1740 LEAVE(" ");
1741 return;
1742 }
1743
1744 void
gnc_tree_view_account_expand_to_account(GncTreeViewAccount * view,Account * account)1745 gnc_tree_view_account_expand_to_account (GncTreeViewAccount *view,
1746 Account *account)
1747 {
1748 GtkTreePath *path;
1749
1750 g_return_if_fail(view != NULL);
1751 g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
1752 ENTER("view %p, account %p", view, account);
1753
1754 path = gnc_tree_view_account_get_path_from_account(view, account);
1755 if (path)
1756 {
1757 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), path);
1758 gtk_tree_path_free(path);
1759 }
1760 LEAVE(" ");
1761 }
1762
1763
1764 /*
1765 * Retrieve the account currently under the cursor.
1766 */
1767 Account *
gnc_tree_view_account_get_cursor_account(GncTreeViewAccount * view)1768 gnc_tree_view_account_get_cursor_account (GncTreeViewAccount *view)
1769 {
1770 GtkTreePath *s_path;
1771 Account *account;
1772
1773 ENTER("view %p", view);
1774 g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1775
1776 gtk_tree_view_get_cursor (GTK_TREE_VIEW(view), &s_path, NULL);
1777 if (!s_path)
1778 {
1779 LEAVE("no account");
1780 return NULL;
1781 }
1782
1783 account = gnc_tree_view_account_get_account_from_path (view, s_path);
1784 gtk_tree_path_free(s_path);
1785 LEAVE("account %p (%s)", account, xaccAccountGetName (account));
1786 return account;
1787 }
1788
1789
1790 /************************************************************/
1791 /* Account Tree View Add Column Functions */
1792 /************************************************************/
1793
1794 static void
gtva_update_column_name(GtkTreeViewColumn * column,const gchar * fmt,const gchar * mnemonic)1795 gtva_update_column_name (GtkTreeViewColumn *column,
1796 const gchar *fmt,
1797 const gchar *mnemonic)
1798 {
1799 gchar *name;
1800
1801 g_return_if_fail(column);
1802
1803 name = g_strdup_printf(fmt, mnemonic);
1804 gtk_tree_view_column_set_title(column, name);
1805 g_free(name);
1806 }
1807
1808
1809 static void
gtva_update_column_names(GncTreeView * view)1810 gtva_update_column_names (GncTreeView *view)
1811 {
1812 GncTreeViewAccountPrivate *priv;
1813 const gchar *mnemonic;
1814
1815 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
1816 mnemonic = gnc_commodity_get_mnemonic(gnc_default_report_currency());
1817
1818 gtva_update_column_name(priv->present_report_column,
1819 /* Translators: %s is a currency mnemonic.*/
1820 _("Present (%s)"), mnemonic);
1821 gtva_update_column_name(priv->balance_report_column,
1822 /* Translators: %s is a currency mnemonic.*/
1823 _("Balance (%s)"), mnemonic);
1824 gtva_update_column_name(priv->cleared_report_column,
1825 /* Translators: %s is a currency mnemonic.*/
1826 _("Cleared (%s)"), mnemonic);
1827 gtva_update_column_name(priv->reconciled_report_column,
1828 /* Translators: %s is a currency mnemonic.*/
1829 _("Reconciled (%s)"), mnemonic);
1830 gtva_update_column_name(priv->future_min_report_column,
1831 /* Translators: %s is a currency mnemonic.*/
1832 _("Future Minimum (%s)"), mnemonic);
1833 gtva_update_column_name(priv->total_report_column,
1834 /* Translators: %s is a currency mnemonic.*/
1835 _("Total (%s)"), mnemonic);
1836 gnc_tree_view_set_show_column_menu(view, FALSE);
1837 gnc_tree_view_set_show_column_menu(view, TRUE);
1838 }
1839
1840
1841 static void
gtva_currency_changed_cb(void)1842 gtva_currency_changed_cb (void)
1843 {
1844 const GList *views, *ptr;
1845
1846 views = gnc_gobject_tracking_get_list (GNC_TREE_VIEW_ACCOUNT_NAME);
1847 for (ptr = views; ptr; ptr = g_list_next(ptr))
1848 {
1849 gtva_update_column_names (ptr->data);
1850 }
1851 }
1852 /* Retrieve a specified account string property and put the result
1853 * into the tree column's text property.
1854 */
1855 static void
account_cell_property_data_func(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * s_model,GtkTreeIter * s_iter,gpointer key)1856 account_cell_property_data_func (GtkTreeViewColumn *tree_column,
1857 GtkCellRenderer *cell,
1858 GtkTreeModel *s_model,
1859 GtkTreeIter *s_iter,
1860 gpointer key)
1861 {
1862 GncTreeViewAccount *view;
1863 Account *account;
1864 gchar *string = NULL;
1865
1866 g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model));
1867 account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter);
1868 qof_instance_get (QOF_INSTANCE (account), key, &string, NULL);
1869 if (string == NULL)
1870 string = g_strdup ("");
1871
1872 g_object_set (G_OBJECT (cell), "text", string, "xalign", 0.0, NULL);
1873 g_free (string);
1874
1875 view = g_object_get_data(G_OBJECT(tree_column), "tree-view");
1876
1877 if (GNC_IS_TREE_VIEW_ACCOUNT (view))
1878 acc_color_data_func (tree_column, cell, s_model, s_iter, view);
1879 }
1880
1881
1882 GtkTreeViewColumn *
gnc_tree_view_account_add_property_column(GncTreeViewAccount * view,const gchar * column_title,const gchar * propname)1883 gnc_tree_view_account_add_property_column (GncTreeViewAccount *view,
1884 const gchar *column_title,
1885 const gchar *propname)
1886 {
1887 GtkCellRenderer *renderer;
1888 GtkTreeViewColumn *column;
1889
1890 g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1891 g_return_val_if_fail (propname != NULL, NULL);
1892
1893 column = gnc_tree_view_add_text_column(GNC_TREE_VIEW(view), column_title,
1894 propname, NULL, "Sample text",
1895 -1, -1, NULL);
1896
1897 /* This new kvp column has only had one renderer added to it so
1898 * far. Find that renderer. */
1899 renderer = gnc_tree_view_column_get_renderer(column);
1900 g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
1901
1902 // add a pointer to the view to make it easier to access in data_func
1903 g_object_set_data(G_OBJECT(column), "tree-view", (gpointer)view);
1904
1905 gtk_tree_view_column_set_cell_data_func (column, renderer,
1906 account_cell_property_data_func,
1907 g_strdup(propname), g_free);
1908 return column;
1909 }
1910
col_edited_helper(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,gpointer _s_model)1911 static void col_edited_helper(GtkCellRendererText *cell, gchar *path_string,
1912 gchar *new_text, gpointer _s_model)
1913 {
1914 Account *account;
1915 GtkTreeModel *s_model;
1916 GtkTreeIter s_iter;
1917 GncTreeViewAccountColumnTextEdited col_edited_cb;
1918 GtkTreeViewColumn *col;
1919
1920 col_edited_cb = g_object_get_data(G_OBJECT(cell),
1921 "column_edited_callback");
1922 col = GTK_TREE_VIEW_COLUMN(g_object_get_data(G_OBJECT(cell),
1923 "column_view"));
1924 s_model = GTK_TREE_MODEL(_s_model);
1925
1926 if (!gtk_tree_model_get_iter_from_string(s_model, &s_iter, path_string))
1927 return;
1928
1929 account = gnc_tree_view_account_get_account_from_iter(s_model, &s_iter);
1930 col_edited_cb(account, col, new_text);
1931 }
1932
col_source_helper(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * s_model,GtkTreeIter * s_iter,gpointer _col_source_cb)1933 static void col_source_helper(GtkTreeViewColumn *col, GtkCellRenderer *cell,
1934 GtkTreeModel *s_model, GtkTreeIter *s_iter,
1935 gpointer _col_source_cb)
1936 {
1937 Account *account;
1938 gchar *text;
1939 GncTreeViewAccountColumnSource col_source_cb;
1940
1941 g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model));
1942 col_source_cb = (GncTreeViewAccountColumnSource) _col_source_cb;
1943 account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter);
1944 text = col_source_cb(account, col, cell);
1945 g_object_set (G_OBJECT (cell), "text", text, "xalign", 1.0, NULL);
1946 g_free(text);
1947 }
1948
1949 /**
1950 * If col_edited_cb is null, the editing callback (helper) will be
1951 * effectively disconnected.
1952 **/
1953 void
gtva_setup_column_renderer_edited_cb(GncTreeViewAccount * account_view,GtkTreeViewColumn * column,GtkCellRenderer * renderer,GncTreeViewAccountColumnTextEdited col_edited_cb)1954 gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view,
1955 GtkTreeViewColumn *column,
1956 GtkCellRenderer *renderer,
1957 GncTreeViewAccountColumnTextEdited col_edited_cb)
1958 {
1959 GtkTreeModel *s_model;
1960
1961 if (col_edited_cb == NULL)
1962 {
1963 g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL);
1964 g_object_set_data(G_OBJECT(renderer), "column_edited_callback", col_edited_cb);
1965 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view));
1966 g_signal_handlers_disconnect_by_func(G_OBJECT(renderer), col_edited_cb, s_model);
1967 g_object_set_data(G_OBJECT(renderer), "column_view", column);
1968 }
1969 else
1970 {
1971 g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
1972 g_object_set_data(G_OBJECT(renderer), "column_edited_callback",
1973 col_edited_cb);
1974 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view));
1975 g_signal_connect(G_OBJECT(renderer), "edited",
1976 (GCallback) col_edited_helper, s_model);
1977 g_object_set_data(G_OBJECT(renderer), "column_view", column);
1978 }
1979 }
1980
1981 GtkTreeViewColumn *
gnc_tree_view_account_add_custom_column(GncTreeViewAccount * account_view,const gchar * column_title,GncTreeViewAccountColumnSource col_source_cb,GncTreeViewAccountColumnTextEdited col_edited_cb)1982 gnc_tree_view_account_add_custom_column(GncTreeViewAccount *account_view,
1983 const gchar *column_title,
1984 GncTreeViewAccountColumnSource
1985 col_source_cb,
1986 GncTreeViewAccountColumnTextEdited
1987 col_edited_cb)
1988 {
1989 GtkCellRenderer *renderer;
1990
1991 g_return_val_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view), NULL);
1992
1993 renderer = gtk_cell_renderer_text_new();
1994
1995 return gnc_tree_view_account_add_custom_column_renderer(
1996 account_view, column_title, col_source_cb, col_edited_cb, renderer);
1997 }
1998
1999 GtkTreeViewColumn *
gnc_tree_view_account_add_custom_column_renderer(GncTreeViewAccount * account_view,const gchar * column_title,GncTreeViewAccountColumnSource col_source_cb,GncTreeViewAccountColumnTextEdited col_edited_cb,GtkCellRenderer * renderer)2000 gnc_tree_view_account_add_custom_column_renderer(GncTreeViewAccount *account_view,
2001 const gchar *column_title,
2002 GncTreeViewAccountColumnSource
2003 col_source_cb,
2004 GncTreeViewAccountColumnTextEdited
2005 col_edited_cb,
2006 GtkCellRenderer *renderer)
2007 {
2008 GtkTreeViewColumn *column;
2009
2010 g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (account_view), NULL);
2011
2012 g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
2013
2014 column = gtk_tree_view_column_new_with_attributes (column_title,
2015 renderer, NULL);
2016 if (col_edited_cb)
2017 {
2018 gtva_setup_column_renderer_edited_cb(account_view, column,
2019 renderer, col_edited_cb);
2020 }
2021 gtk_tree_view_column_set_cell_data_func (column, renderer,
2022 col_source_helper,
2023 col_source_cb, NULL);
2024 gnc_tree_view_append_column (GNC_TREE_VIEW(account_view), column);
2025 return column;
2026 }
2027
2028
2029 /* BEGIN FILTER FUNCTIONS */
2030 #define FILTER_TREE_VIEW "types_tree_view"
2031
2032 /** This function tells the account tree view whether or not to filter
2033 * out a particular account. Accounts may be filtered if the user
2034 * has decided not to display that particular account type, or if the
2035 * user has requested that accounts with a zero total not be shown.
2036 *
2037 * @param account The account that was toggled.
2038 *
2039 * @param user_data A pointer to the AccountFilterDialog struct.
2040 *
2041 * @return TRUE if the account should be visible. FALSE if the
2042 * account should be hidden. */
2043 gboolean
gnc_plugin_page_account_tree_filter_accounts(Account * account,gpointer user_data)2044 gnc_plugin_page_account_tree_filter_accounts (Account *account,
2045 gpointer user_data)
2046 {
2047 AccountFilterDialog *fd = user_data;
2048 GNCAccountType acct_type;
2049 gnc_numeric total;
2050 gboolean result;
2051
2052 ENTER("account %p:%s", account, xaccAccountGetName(account));
2053
2054 if (g_hash_table_size (fd->filter_override) > 0)
2055 {
2056 Account *test_acc = NULL;
2057 test_acc = g_hash_table_lookup (fd->filter_override, account);
2058 if (test_acc != NULL)
2059 {
2060 LEAVE(" filter: override");
2061 return TRUE;
2062 }
2063 }
2064
2065 if (!fd->show_hidden && xaccAccountIsHidden (account))
2066 {
2067 LEAVE(" hide: hidden");
2068 return FALSE;
2069 }
2070
2071 if (!fd->show_zero_total)
2072 {
2073 total = xaccAccountGetBalanceInCurrency (account, NULL, TRUE);
2074 if (gnc_numeric_zero_p(total))
2075 {
2076 LEAVE(" hide: zero balance");
2077 return FALSE;
2078 }
2079 }
2080
2081 if (!fd->show_unused)
2082 {
2083 if (gnc_account_and_descendants_empty(account))
2084 {
2085 LEAVE(" hide: unused");
2086 return FALSE;
2087 }
2088 }
2089
2090 acct_type = xaccAccountGetType(account);
2091 result = (fd->visible_types & (1 << acct_type)) ? TRUE : FALSE;
2092 LEAVE(" %s", result ? "show" : "hide");
2093 return result;
2094 }
2095
2096 /** The "show hidden" button in the Filter dialog changed state.
2097 * Update the page to reflect these changes.
2098 *
2099 * @param button The GtkCheckButton that was toggled.
2100 *
2101 * @param fd A pointer to the account filter dialog struct.
2102 */
2103 void
gppat_filter_show_hidden_toggled_cb(GtkToggleButton * button,AccountFilterDialog * fd)2104 gppat_filter_show_hidden_toggled_cb (GtkToggleButton *button,
2105 AccountFilterDialog *fd)
2106 {
2107 g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
2108
2109 ENTER("button %p", button);
2110 fd->show_hidden = gtk_toggle_button_get_active(button);
2111 gnc_tree_view_account_refilter(fd->tree_view);
2112 LEAVE("show_hidden %d", fd->show_hidden);
2113 }
2114
2115 /** The "show zero totals" button in the Filter dialog changed state.
2116 * Update the page to reflect these changes.
2117 *
2118 * @param button The GtkCheckButton that was toggled.
2119 *
2120 * @param fd A pointer to the account filter dialog struct.
2121 */
2122 void
gppat_filter_show_zero_toggled_cb(GtkToggleButton * button,AccountFilterDialog * fd)2123 gppat_filter_show_zero_toggled_cb (GtkToggleButton *button,
2124 AccountFilterDialog *fd)
2125 {
2126 g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
2127
2128 ENTER("button %p", button);
2129 fd->show_zero_total = gtk_toggle_button_get_active(button);
2130 gnc_tree_view_account_refilter(fd->tree_view);
2131 LEAVE("show_zero %d", fd->show_zero_total);
2132 }
2133
2134 /** The "show unused" button in the Filter dialog changed state.
2135 * Update the page to reflect these changes.
2136 *
2137 * @param button The GtkCheckButton that was toggled.
2138 *
2139 * @param fd A pointer to the account filter dialog struct.
2140 */
2141 void
gppat_filter_show_unused_toggled_cb(GtkToggleButton * button,AccountFilterDialog * fd)2142 gppat_filter_show_unused_toggled_cb (GtkToggleButton *button,
2143 AccountFilterDialog *fd)
2144 {
2145 g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
2146
2147 ENTER("button %p", button);
2148 fd->show_unused = gtk_toggle_button_get_active(button);
2149 gnc_tree_view_account_refilter(fd->tree_view);
2150 LEAVE("show_unused %d", fd->show_unused);
2151 }
2152
2153 /** The "clear all account types" button in the Filter dialog was
2154 * clicked. Clear all account types shown, and update the visible
2155 * page.
2156 *
2157 * @param button The button that was clicked.
2158 *
2159 * @param fd A pointer to the account filter dialog struct.
2160 */
2161 void
gppat_filter_clear_all_cb(GtkWidget * button,AccountFilterDialog * fd)2162 gppat_filter_clear_all_cb (GtkWidget *button,
2163 AccountFilterDialog *fd)
2164 {
2165 g_return_if_fail(GTK_IS_BUTTON(button));
2166
2167 ENTER("button %p", button);
2168 fd->visible_types = 0;
2169 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fd->model));
2170 gnc_tree_view_account_refilter(fd->tree_view);
2171 LEAVE("types 0x%x", fd->visible_types);
2172 }
2173
2174 /** The "select all account types" button in the Filter dialog was
2175 * clicked. Make all account types visible, and update the page.
2176 *
2177 * @param button The button that was clicked.
2178 *
2179 * @param fd A pointer to the account filter dialog struct. */
2180 void
gppat_filter_select_all_cb(GtkWidget * button,AccountFilterDialog * fd)2181 gppat_filter_select_all_cb (GtkWidget *button,
2182 AccountFilterDialog *fd)
2183 {
2184 g_return_if_fail(GTK_IS_BUTTON(button));
2185
2186 ENTER("button %p", button);
2187 fd->visible_types = -1;
2188 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fd->model));
2189 gnc_tree_view_account_refilter(fd->tree_view);
2190 LEAVE("types 0x%x", fd->visible_types);
2191 }
2192
2193 /** The "select default account types" button in the Filter dialog was
2194 * clicked. Set all account types to their default visibility (which
2195 * happens to be visible for all of them), and update the page.
2196 *
2197 * @param button The button that was clicked.
2198 *
2199 * @param fd A pointer to the account filter dialog struct.*/
2200 void
gppat_filter_select_default_cb(GtkWidget * button,AccountFilterDialog * fd)2201 gppat_filter_select_default_cb (GtkWidget *button,
2202 AccountFilterDialog *fd)
2203 {
2204 ENTER("button %p", button);
2205 gppat_filter_select_all_cb(button, fd);
2206 LEAVE(" ");
2207 }
2208
2209 /** Set the renderer's properties.
2210 *
2211 * @param column A GtkTreeColumn
2212 *
2213 * @param renderer The GtkCellRendererToggle being rendered by @column
2214 *
2215 * @param model The GtkTreeModel being rendered
2216 *
2217 * @param iter A GtkTreeIter of the current row rendered
2218 *
2219 * @param data A pointer to the account filter dialog struct. */
2220 static void
gppat_filter_visible_set_func(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)2221 gppat_filter_visible_set_func (GtkTreeViewColumn *column,
2222 GtkCellRenderer *renderer,
2223 GtkTreeModel *model,
2224 GtkTreeIter *iter,
2225 gpointer data)
2226 {
2227 AccountFilterDialog *fd = data;
2228 GNCAccountType type;
2229 gboolean active;
2230
2231 gtk_tree_model_get(model, iter, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_TYPE, &type, -1);
2232
2233 active = (fd->visible_types & (1 << type)) ? TRUE : FALSE;
2234 g_object_set (G_OBJECT (renderer), "active", active, NULL);
2235 }
2236
2237 /** A check box in the tree view was toggled.
2238 *
2239 * @param renderer The GtkCellRendererToggle being toggled.
2240 *
2241 * @param fd A pointer to the account filter dialog struct. */
2242 static void
gppat_filter_visible_toggled_cb(GtkCellRendererToggle * renderer,gchar * path_str,AccountFilterDialog * fd)2243 gppat_filter_visible_toggled_cb (GtkCellRendererToggle *renderer,
2244 gchar *path_str,
2245 AccountFilterDialog *fd)
2246 {
2247 GtkTreeModel *model = fd->model;
2248 GtkTreeIter iter;
2249 GtkTreePath *path;
2250 GNCAccountType type;
2251
2252 ENTER("toggled %p", path_str);
2253 path = gtk_tree_path_new_from_string(path_str);
2254
2255 if (gtk_tree_model_get_iter(model, &iter, path))
2256 {
2257 gtk_tree_model_get(model, &iter, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_TYPE, &type, -1);
2258 fd->visible_types ^= (1 << type);
2259 gnc_tree_view_account_refilter(fd->tree_view);
2260 }
2261 gtk_tree_path_free(path);
2262 LEAVE("types 0x%x", fd->visible_types);
2263 }
2264
2265 /** The Filter dialog was closed. Check to see if this was done via
2266 * the OK button. If so, make the changes permanent. If not, revert
2267 * any changes.
2268 *
2269 * @param dialog A pointer to the "Filter By" dialog.
2270 *
2271 * @param response The response code from closing the dialog.
2272 *
2273 * @param fd A pointer to the account filter dialog struct.
2274 */
2275 void
gppat_filter_response_cb(GtkWidget * dialog,gint response,AccountFilterDialog * fd)2276 gppat_filter_response_cb (GtkWidget *dialog,
2277 gint response,
2278 AccountFilterDialog *fd)
2279 {
2280 gpointer gptemp;
2281
2282 g_return_if_fail(GTK_IS_DIALOG(dialog));
2283
2284 ENTER("dialog %p, response %d", dialog, response);
2285
2286 if (response != GTK_RESPONSE_OK)
2287 {
2288 fd->visible_types = fd->original_visible_types;
2289 fd->show_hidden = fd->original_show_hidden;
2290 fd->show_zero_total = fd->original_show_zero_total;
2291 fd->show_unused = fd->original_show_unused;
2292 gnc_tree_view_account_refilter(fd->tree_view);
2293 }
2294
2295 /* Clean up and delete dialog */
2296 gptemp = (gpointer)fd->dialog;
2297 g_atomic_pointer_compare_and_exchange(&gptemp,
2298 (gpointer)dialog, NULL);
2299 fd->dialog = gptemp;
2300 gtk_widget_destroy(dialog);
2301 LEAVE("types 0x%x", fd->visible_types);
2302 }
2303
2304 void
account_filter_dialog_create(AccountFilterDialog * fd,GncPluginPage * page)2305 account_filter_dialog_create(AccountFilterDialog *fd, GncPluginPage *page)
2306 {
2307 GtkWidget *dialog, *button;
2308 GtkTreeView *view;
2309 GtkCellRenderer *renderer;
2310 GtkBuilder *builder;
2311 gchar *title;
2312
2313 ENTER("(fd %p, page %p)", fd, page);
2314
2315 if (fd->dialog)
2316 {
2317 gtk_window_present(GTK_WINDOW(fd->dialog));
2318 LEAVE("existing dialog");
2319 return;
2320 }
2321
2322 /* Create the dialog */
2323 builder = gtk_builder_new();
2324 gnc_builder_add_from_file (builder, "dialog-account.glade", "account_filter_by_dialog");
2325 dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_filter_by_dialog"));
2326 fd->dialog = dialog;
2327 gtk_window_set_transient_for(GTK_WINDOW(dialog),
2328 GTK_WINDOW(GNC_PLUGIN_PAGE(page)->window));
2329 /* Translators: The %s is the name of the plugin page */
2330 title = g_strdup_printf(_("Filter %s by..."),
2331 gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(page)));
2332 gtk_window_set_title(GTK_WINDOW(dialog), title);
2333 g_free(title);
2334
2335 /* Remember current state */
2336 fd->original_visible_types = fd->visible_types;
2337 fd->original_show_hidden = fd->show_hidden;
2338 fd->original_show_zero_total = fd->show_zero_total;
2339 fd->original_show_unused = fd->show_unused;
2340
2341 /* Update the dialog widgets for the current state */
2342 button = GTK_WIDGET(gtk_builder_get_object (builder, "show_hidden"));
2343 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
2344 fd->show_hidden);
2345 button = GTK_WIDGET(gtk_builder_get_object (builder, "show_zero"));
2346 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
2347 fd->show_zero_total);
2348 button = GTK_WIDGET(gtk_builder_get_object (builder, "show_unused"));
2349 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
2350 fd->show_unused);
2351
2352 /* Set up the tree view and model */
2353 view = GTK_TREE_VIEW(gtk_builder_get_object (builder, FILTER_TREE_VIEW));
2354
2355 fd->model = gnc_tree_model_account_types_filter_using_mask
2356 (~(1 << ACCT_TYPE_ROOT));
2357 gtk_tree_view_set_model(view, fd->model);
2358 g_object_unref (fd->model);
2359
2360 renderer = gtk_cell_renderer_toggle_new();
2361
2362 g_signal_connect(renderer, "toggled",
2363 G_CALLBACK(gppat_filter_visible_toggled_cb), fd);
2364
2365 gtk_tree_view_insert_column_with_data_func (view, -1, NULL, renderer,
2366 gppat_filter_visible_set_func, fd, NULL);
2367
2368 gtk_tree_view_insert_column_with_attributes (view,
2369 -1, _("Account Types"), gtk_cell_renderer_text_new(),
2370 "text", GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME, NULL);
2371
2372 /* Wire up the rest of the callbacks */
2373 gtk_builder_connect_signals(builder, fd);
2374 g_object_unref(G_OBJECT(builder));
2375
2376 /* Show it */
2377 gtk_widget_show_all(dialog);
2378 LEAVE(" ");
2379 }
2380
2381 // page state section
2382 #define ACCT_COUNT "NumberOfOpenAccounts"
2383 #define ACCT_OPEN "OpenAccount%d"
2384 #define ACCT_SELECTED "SelectedAccount"
2385 #define SHOW_HIDDEN "ShowHidden"
2386 #define SHOW_ZERO "ShowZeroTotal"
2387 #define SHOW_UNUSED "ShowUnused"
2388 #define ACCT_TYPES "AccountTypes"
2389
2390 // account/budget state section
2391 // versions less than 3.2 would crash if key did not have an "_"
2392 #define SHOW_HIDDEN_ACCOUNTS "Show_Hidden"
2393 #define SHOW_ZERO_TOTALS "Show_ZeroTotal"
2394 #define SHOW_UNUSED_ACCOUNTS "Show_Unused"
2395 #define ACCOUNT_TYPES "Account_Types"
2396
2397
2398 typedef struct foo
2399 {
2400 GKeyFile *key_file;
2401 const gchar *group_name;
2402 int count;
2403 } bar_t;
2404
2405 /** Save information about an expanded row. This function is called
2406 * via a gtk_tree_view_map_expanded_rows, which calls it once per
2407 * expanded row. Its job is to write the full account name of the
2408 * row out to the state file.
2409 *
2410 * @param view A pointer to the GncTreeViewAccount
2411 *
2412 * @param path A pointer to a particular entry in the tree.
2413 *
2414 * @param data A pointer to a data structure holding the information
2415 * related to the state file. */
2416 static void
tree_save_expanded_row(GncTreeViewAccount * view,GtkTreePath * path,gpointer user_data)2417 tree_save_expanded_row (GncTreeViewAccount *view,
2418 GtkTreePath *path,
2419 gpointer user_data)
2420 {
2421 Account *account;
2422 bar_t *bar = user_data;
2423 gchar *key;
2424 gchar *account_name;
2425
2426 account = gnc_tree_view_account_get_account_from_path (view, path);
2427 if (account == NULL)
2428 return;
2429
2430 account_name = gnc_account_get_full_name(account);
2431 if (account_name == NULL)
2432 return;
2433
2434 key = g_strdup_printf(ACCT_OPEN, ++bar->count);
2435 g_key_file_set_string(bar->key_file, bar->group_name, key, account_name);
2436 g_free(key);
2437 g_free(account_name);
2438 }
2439
2440
2441 /** Save information about the selected row. Its job is to write the
2442 * full account name of the row out to the state file.
2443 *
2444 * @param view A pointer to the GtkTreeView embedded in an
2445 * account tree page.
2446 *
2447 * @param path A pointer to a particular entry in the tree.
2448 *
2449 * @param data A pointer to a data structure holding the information
2450 * related to the state file. */
2451 static void
tree_save_selected_row(GncTreeViewAccount * view,gpointer user_data)2452 tree_save_selected_row (GncTreeViewAccount *view,
2453 gpointer user_data)
2454 {
2455 Account *account;
2456 bar_t *bar = user_data;
2457 gchar *account_name;
2458
2459 account = gnc_tree_view_account_get_selected_account(view);
2460 if (account == NULL)
2461 return;
2462
2463 account_name = gnc_account_get_full_name (account);
2464 if (account_name == NULL)
2465 return;
2466
2467 g_key_file_set_string(bar->key_file, bar->group_name, ACCT_SELECTED,
2468 account_name);
2469 g_free(account_name);
2470 }
2471
2472 void
gnc_tree_view_account_save(GncTreeViewAccount * view,AccountFilterDialog * fd,GKeyFile * key_file,const gchar * group_name)2473 gnc_tree_view_account_save(GncTreeViewAccount *view,
2474 AccountFilterDialog *fd,
2475 GKeyFile *key_file, const gchar *group_name)
2476 {
2477 bar_t bar;
2478
2479 g_return_if_fail (key_file != NULL);
2480 g_return_if_fail (group_name != NULL);
2481
2482 ENTER("view %p, key_file %p, group_name %s", view, key_file,
2483 group_name);
2484
2485 g_key_file_set_integer(key_file, group_name, ACCT_TYPES,
2486 fd->visible_types);
2487 g_key_file_set_boolean(key_file, group_name, SHOW_HIDDEN,
2488 fd->show_hidden);
2489 g_key_file_set_boolean(key_file, group_name, SHOW_ZERO,
2490 fd->show_zero_total);
2491 g_key_file_set_boolean(key_file, group_name, SHOW_UNUSED,
2492 fd->show_unused);
2493
2494 bar.key_file = key_file;
2495 bar.group_name = group_name;
2496 bar.count = 0;
2497 tree_save_selected_row(view, &bar);
2498 gtk_tree_view_map_expanded_rows(
2499 GTK_TREE_VIEW(view), (GtkTreeViewMappingFunc) tree_save_expanded_row,
2500 &bar);
2501 g_key_file_set_integer(key_file, group_name, ACCT_COUNT, bar.count);
2502 LEAVE(" ");
2503
2504 }
2505
2506 void
gnc_tree_view_account_save_filter(GncTreeViewAccount * view,AccountFilterDialog * fd,GKeyFile * key_file,const gchar * group_name)2507 gnc_tree_view_account_save_filter (GncTreeViewAccount *view,
2508 AccountFilterDialog *fd,
2509 GKeyFile *key_file,
2510 const gchar *group_name)
2511 {
2512 g_return_if_fail (key_file != NULL);
2513 g_return_if_fail (group_name != NULL);
2514
2515 ENTER("view %p, key_file %p, group_name %s", view, key_file,
2516 group_name);
2517
2518 g_key_file_set_integer (key_file, group_name, ACCOUNT_TYPES,
2519 fd->visible_types);
2520 g_key_file_set_boolean (key_file, group_name, SHOW_HIDDEN_ACCOUNTS,
2521 fd->show_hidden);
2522 g_key_file_set_boolean (key_file, group_name, SHOW_ZERO_TOTALS,
2523 fd->show_zero_total);
2524 g_key_file_set_boolean (key_file, group_name, SHOW_UNUSED_ACCOUNTS,
2525 fd->show_unused);
2526 LEAVE("");
2527 }
2528
2529 /** Expand a row in the tree that was expanded when the user last quit
2530 * gnucash. Its job is to map from account name to tree row and
2531 * expand the row.
2532 *
2533 * @param view A pointer to the GncTreeViewAccount.
2534 *
2535 * @param account_name A pointer to the full account name. */
2536 static void
tree_restore_expanded_row(GncTreeViewAccount * view,const gchar * account_name)2537 tree_restore_expanded_row (GncTreeViewAccount *view,
2538 const gchar *account_name)
2539 {
2540 Account *account;
2541 QofBook *book;
2542
2543 book = qof_session_get_book(gnc_get_current_session());
2544 g_return_if_fail(book);
2545 account = gnc_account_lookup_by_full_name(gnc_book_get_root_account(book),
2546 account_name);
2547 if (account)
2548 gnc_tree_view_account_expand_to_account(view, account);
2549 }
2550
2551
2552 /** Select the row in the tree that was selected when the user last
2553 * quit gnucash. Its job is to map from account name to tree row and
2554 * select the row.
2555 *
2556 * @param tree A pointer to the GncTreeViewAccount embedded.
2557 *
2558 * @param account_name A pointer to the full account name. */
2559 static void
tree_restore_selected_row(GncTreeViewAccount * view,const gchar * account_name)2560 tree_restore_selected_row (GncTreeViewAccount *view,
2561 const gchar *account_name)
2562 {
2563 Account *account;
2564 QofBook *book;
2565
2566 book = qof_session_get_book(gnc_get_current_session());
2567 g_return_if_fail(book);
2568 account = gnc_account_lookup_by_full_name(gnc_book_get_root_account(book),
2569 account_name);
2570 if (account)
2571 gnc_tree_view_account_set_selected_account(view, account);
2572 }
2573
2574 void
gnc_tree_view_account_restore(GncTreeViewAccount * view,AccountFilterDialog * fd,GKeyFile * key_file,const gchar * group_name)2575 gnc_tree_view_account_restore(GncTreeViewAccount *view,
2576 AccountFilterDialog *fd,
2577 GKeyFile *key_file, const gchar *group_name)
2578 {
2579 GError *error = NULL;
2580 gchar *key, *value;
2581 gint i, count;
2582 gboolean show;
2583
2584 /* Filter information. Ignore missing keys. */
2585 show = g_key_file_get_boolean(key_file, group_name, SHOW_HIDDEN, &error);
2586 if (error)
2587 {
2588 g_warning("error reading group %s key %s: %s",
2589 group_name, SHOW_HIDDEN, error->message);
2590 g_error_free(error);
2591 error = NULL;
2592 show = TRUE;
2593 }
2594 fd->show_hidden = show;
2595
2596 show = g_key_file_get_boolean(key_file, group_name, SHOW_ZERO, &error);
2597 if (error)
2598 {
2599 g_warning("error reading group %s key %s: %s",
2600 group_name, SHOW_ZERO, error->message);
2601 g_error_free(error);
2602 error = NULL;
2603 show = TRUE;
2604 }
2605 fd->show_zero_total = show;
2606
2607 show = g_key_file_get_boolean(key_file, group_name, SHOW_UNUSED, &error);
2608 if (error)
2609 {
2610 g_warning("error reading group %s key %s: %s",
2611 group_name, SHOW_UNUSED, error->message);
2612 g_error_free(error);
2613 error = NULL;
2614 show = TRUE;
2615 }
2616 fd->show_unused = show;
2617
2618 i = g_key_file_get_integer(key_file, group_name, ACCT_TYPES, &error);
2619 if (error)
2620 {
2621 g_warning("error reading group %s key %s: %s",
2622 group_name, ACCT_TYPES, error->message);
2623 g_error_free(error);
2624 error = NULL;
2625 i = -1;
2626 }
2627 fd->visible_types = i;
2628
2629 /* Expanded accounts. Skip if count key missing. */
2630 count = g_key_file_get_integer(key_file, group_name, ACCT_COUNT, &error);
2631 if (error == NULL)
2632 {
2633 for (i = 1; i <= count; i++)
2634 {
2635 key = g_strdup_printf(ACCT_OPEN, i);
2636 value = g_key_file_get_string(key_file, group_name, key, &error);
2637 if (error)
2638 {
2639 g_warning("error reading group %s key %s: %s",
2640 group_name, key, error->message);
2641 g_error_free(error);
2642 error = NULL;
2643 }
2644 else
2645 {
2646 tree_restore_expanded_row(view, value);
2647 g_free(value);
2648 }
2649 g_free(key);
2650 }
2651 }
2652 else
2653 {
2654 g_warning("error reading group %s key %s: %s",
2655 group_name, ACCT_COUNT, error->message);
2656 g_error_free(error);
2657 }
2658
2659 /* Selected account (if any) */
2660 value = g_key_file_get_string(key_file, group_name, ACCT_SELECTED, NULL);
2661 if (value)
2662 {
2663 tree_restore_selected_row(view, value);
2664 g_free(value);
2665 }
2666
2667 /* Update tree view for any changes */
2668 gnc_tree_view_account_refilter(view);
2669 }
2670
2671 void
gnc_tree_view_account_restore_filter(GncTreeViewAccount * view,AccountFilterDialog * fd,GKeyFile * key_file,const gchar * group_name)2672 gnc_tree_view_account_restore_filter (GncTreeViewAccount *view,
2673 AccountFilterDialog *fd,
2674 GKeyFile *key_file,
2675 const gchar *group_name)
2676 {
2677 GError *error = NULL;
2678 gint i;
2679 gboolean show;
2680
2681 g_return_if_fail (key_file != NULL);
2682 g_return_if_fail (group_name != NULL);
2683
2684 /* if entry not found, filter will use the default setting */
2685
2686 /* Filter information. Ignore missing keys. */
2687 show = g_key_file_get_boolean (key_file, group_name, SHOW_HIDDEN_ACCOUNTS, &error);
2688 if (error)
2689 {
2690 g_error_free (error);
2691 error = NULL;
2692 }
2693 else
2694 fd->show_hidden = show;
2695
2696 show = g_key_file_get_boolean(key_file, group_name, SHOW_ZERO_TOTALS, &error);
2697 if (error)
2698 {
2699 g_error_free (error);
2700 error = NULL;
2701 }
2702 else
2703 fd->show_zero_total = show;
2704
2705 show = g_key_file_get_boolean(key_file, group_name, SHOW_UNUSED_ACCOUNTS, &error);
2706 if (error)
2707 {
2708 g_error_free (error);
2709 error = NULL;
2710 }
2711 else
2712 fd->show_unused = show;
2713
2714 i = g_key_file_get_integer(key_file, group_name, ACCOUNT_TYPES, &error);
2715 if (error)
2716 {
2717 g_error_free (error);
2718 error = NULL;
2719 }
2720 else
2721 fd->visible_types = i;
2722 }
2723
2724 // @@fixme -- factor this app-not-gui-specific-logic out.
2725 void
gnc_tree_view_account_name_edited_cb(Account * account,GtkTreeViewColumn * col,const gchar * new_name)2726 gnc_tree_view_account_name_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_name)
2727 {
2728 // check for accounts with the same name among our parent's children.
2729 // should probably factor this consistency check out to the account
2730 // itself....
2731 {
2732 Account *parent = gnc_account_get_parent(account);
2733 Account *existing = gnc_account_lookup_by_name(parent, new_name);
2734 if (existing != NULL && existing != account)
2735 {
2736 PERR("account with the same name [%s] already exists.", new_name);
2737 return;
2738 }
2739 }
2740 xaccAccountSetName(account, new_name);
2741 }
2742
2743 void
gnc_tree_view_account_code_edited_cb(Account * account,GtkTreeViewColumn * col,const gchar * new_code)2744 gnc_tree_view_account_code_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_code)
2745 {
2746 if (g_strcmp0(xaccAccountGetCode(account), new_code) == 0)
2747 return;
2748 xaccAccountSetCode(account, new_code);
2749 }
2750
2751 void
gnc_tree_view_account_description_edited_cb(Account * account,GtkTreeViewColumn * col,const gchar * new_desc)2752 gnc_tree_view_account_description_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_desc)
2753 {
2754 if (g_strcmp0(xaccAccountGetDescription(account), new_desc) == 0)
2755 return;
2756 xaccAccountSetDescription(account, new_desc);
2757 }
2758
2759 void
gnc_tree_view_account_notes_edited_cb(Account * account,GtkTreeViewColumn * col,const gchar * new_notes)2760 gnc_tree_view_account_notes_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_notes)
2761 {
2762 if (g_strcmp0(xaccAccountGetNotes(account), new_notes) == 0)
2763 return;
2764 xaccAccountSetNotes(account, new_notes);
2765 }
2766
2767 static void
gtva_set_column_editor(GncTreeViewAccount * view,GtkTreeViewColumn * column,GncTreeViewAccountColumnTextEdited edited_cb)2768 gtva_set_column_editor(GncTreeViewAccount *view,
2769 GtkTreeViewColumn *column,
2770 GncTreeViewAccountColumnTextEdited edited_cb)
2771 {
2772 GList *renderers_orig, *renderers;
2773 GtkCellRenderer *renderer = NULL;
2774
2775 // look for the first text-renderer; on the 0th column of the account tree,
2776 // there are two renderers: pixbuf and text. So find the text one.
2777 for (renderers_orig = renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
2778 renderers && !GTK_IS_CELL_RENDERER_TEXT(renderers->data);
2779 renderers = renderers->next);
2780 if (renderers)
2781 renderer = GTK_CELL_RENDERER(renderers->data);
2782 g_list_free(renderers_orig);
2783 g_return_if_fail(renderer != NULL);
2784 gtva_setup_column_renderer_edited_cb(GNC_TREE_VIEW_ACCOUNT(view), column, renderer, edited_cb);
2785 }
2786
2787 void
gnc_tree_view_account_set_name_edited(GncTreeViewAccount * view,GncTreeViewAccountColumnTextEdited edited_cb)2788 gnc_tree_view_account_set_name_edited(GncTreeViewAccount *view,
2789 GncTreeViewAccountColumnTextEdited edited_cb)
2790 {
2791 GncTreeViewAccountPrivate *priv;
2792 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2793 gtva_set_column_editor(view, priv->name_column, edited_cb);
2794 }
2795
2796 void
gnc_tree_view_account_set_code_edited(GncTreeViewAccount * view,GncTreeViewAccountColumnTextEdited edited_cb)2797 gnc_tree_view_account_set_code_edited(GncTreeViewAccount *view,
2798 GncTreeViewAccountColumnTextEdited edited_cb)
2799 {
2800 GncTreeViewAccountPrivate *priv;
2801 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2802 gtva_set_column_editor(view, priv->code_column, edited_cb);
2803 }
2804
2805 void
gnc_tree_view_account_set_description_edited(GncTreeViewAccount * view,GncTreeViewAccountColumnTextEdited edited_cb)2806 gnc_tree_view_account_set_description_edited(GncTreeViewAccount *view,
2807 GncTreeViewAccountColumnTextEdited edited_cb)
2808 {
2809 GncTreeViewAccountPrivate *priv;
2810 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2811 gtva_set_column_editor(view, priv->desc_column, edited_cb);
2812 }
2813
2814 void
gnc_tree_view_account_set_notes_edited(GncTreeViewAccount * view,GncTreeViewAccountColumnTextEdited edited_cb)2815 gnc_tree_view_account_set_notes_edited(GncTreeViewAccount *view,
2816 GncTreeViewAccountColumnTextEdited edited_cb)
2817 {
2818 GncTreeViewAccountPrivate *priv;
2819 priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2820 gtva_set_column_editor(view, priv->notes_column, edited_cb);
2821 }
2822
2823 static
gnc_tree_view_search_compare(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer search_data)2824 gboolean gnc_tree_view_search_compare (GtkTreeModel *model, gint column,
2825 const gchar *key, GtkTreeIter *iter, gpointer search_data)
2826 {
2827 gchar *normalized_key;
2828 gchar *case_normalized_key = NULL;
2829 gboolean match = FALSE;
2830
2831 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
2832 if (normalized_key)
2833 case_normalized_key = g_utf8_casefold (normalized_key, -1);
2834 if (case_normalized_key)
2835 {
2836 int i;
2837
2838 for (i=0;i<3;i++)
2839 {
2840 gchar *normalized_string;
2841 gchar *case_normalized_string = NULL;
2842 gchar *str = NULL;
2843
2844 switch (i)
2845 {
2846 case 0:
2847 gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_NAME,&str,-1);
2848 break;
2849 case 1:
2850 gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_CODE,&str,-1);
2851 break;
2852 case 2:
2853 gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION,&str,-1);
2854 break;
2855 }
2856
2857 if (!str)
2858 continue;
2859
2860 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
2861 if (normalized_string)
2862 case_normalized_string = g_utf8_casefold (normalized_string, -1);
2863 if (case_normalized_string&&NULL!=strstr(case_normalized_string,case_normalized_key))
2864 match=TRUE;
2865
2866 g_free (str);
2867 g_free (normalized_string);
2868 g_free (case_normalized_string);
2869
2870 if (match)
2871 break;
2872 }
2873 }
2874
2875 g_free (normalized_key);
2876 g_free (case_normalized_key);
2877
2878 // inverted return (FALSE means a match)
2879 return !match;
2880 }
2881
gnc_tree_view_account_set_editing_started_cb(GncTreeViewAccount * view,GFunc editing_started_cb,gpointer editing_cb_data)2882 void gnc_tree_view_account_set_editing_started_cb(GncTreeViewAccount *view,
2883 GFunc editing_started_cb, gpointer editing_cb_data)
2884 {
2885 gnc_tree_view_set_editing_started_cb (GNC_TREE_VIEW(view),
2886 editing_started_cb, editing_cb_data);
2887 }
2888
gnc_tree_view_account_set_editing_finished_cb(GncTreeViewAccount * view,GFunc editing_finished_cb,gpointer editing_cb_data)2889 void gnc_tree_view_account_set_editing_finished_cb(GncTreeViewAccount *view,
2890 GFunc editing_finished_cb, gpointer editing_cb_data)
2891 {
2892 gnc_tree_view_set_editing_finished_cb (GNC_TREE_VIEW(view),
2893 editing_finished_cb, editing_cb_data);
2894 }
2895