GnuCash c935c2f+
Loading...
Searching...
No Matches
account-quickfill.c
1/********************************************************************\
2 * account-quickfill.h -- Create an account-name quick-fill *
3 * Copyright (C) 2004 Linas Vepstas <linas@linas.org> *
4 * *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License as *
7 * published by the Free Software Foundation; either version 2 of *
8 * the License, or (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License*
16 * along with this program; if not, contact: *
17 * *
18 * Free Software Foundation Voice: +1-617-542-5942 *
19 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 * Boston, MA 02110-1301, USA gnu@gnu.org *
21 * *
22\********************************************************************/
23
24#include <config.h>
25#include "account-quickfill.h"
26#include "gnc-engine.h"
27#include "gnc-prefs.h"
28#include "gnc-ui-util.h"
29
30/* This static indicates the debugging module that this .o belongs to. */
31static QofLogModule log_module = GNC_MOD_REGISTER;
32
33static void shared_quickfill_pref_changed (gpointer prefs, gchar* pref,
34 gpointer qfb);
35static void listen_for_account_events (QofInstance* entity,
36 QofEventId event_type,
37 gpointer user_data, gpointer event_data);
38
39/* Column indices for the list store */
40#define ACCOUNT_NAME 0
41#define ACCOUNT_POINTER 1
42#define NUM_ACCOUNT_COLUMNS 2
43
44/* ===================================================================== */
45/* In order to speed up register starts for registers that have a huge
46 * number of accounts in them (where 'huge' is >500) we build a quickfill
47 * cache of account names. This cache is needed because some users on
48 * some machines experience register open times in the tens of seconds
49 * type timescales. Building the quickfill list accounts for almost
50 * all of that cpu time (about 90% of the xfer_cell build time for 600
51 * accounts).
52 */
53
54typedef struct
55{
56 QuickFill* qf;
57 gboolean load_list_store;
58 GtkListStore* list_store;
59 QofBook* book;
60 Account* root;
61 gint listener;
62 AccountBoolCB dont_add_cb;
63 gpointer dont_add_data;
64} QFB;
65
66static void
67shared_quickfill_destroy (QofBook* book, gpointer key, gpointer user_data)
68{
69 QFB* qfb = user_data;
70 gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
71 GNC_PREF_ACCOUNT_SEPARATOR,
72 shared_quickfill_pref_changed,
73 qfb);
74 gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REGISTER,
75 GNC_PREF_SHOW_LEAF_ACCT_NAMES,
76 shared_quickfill_pref_changed,
77 qfb);
78 gnc_quickfill_destroy (qfb->qf);
79 g_object_unref (qfb->list_store);
80 qof_event_unregister_handler (qfb->listener);
81 g_free (qfb);
82}
83
84
85typedef struct find_data
86{
87 GList* accounts;
88 GList* refs;
89} find_data;
90
91static gboolean
92shared_quickfill_find_accounts (GtkTreeModel* model,
93 GtkTreePath* path,
94 GtkTreeIter* iter,
95 gpointer user_data)
96{
97 Account* account = NULL;
98 find_data* data = user_data;
99 GtkTreeRowReference* ref;
100 GList* tmp;
101
102 gtk_tree_model_get (model, iter, ACCOUNT_POINTER, &account, -1);
103 for (tmp = data->accounts; tmp; tmp = g_list_next (tmp))
104 {
105 if (tmp->data == account)
106 {
107 ref = gtk_tree_row_reference_new (model, path);
108 data->refs = g_list_append (data->refs, ref);
109 data->accounts = g_list_delete_link (data->accounts, tmp);
110 return (data->accounts == NULL);
111 }
112 }
113 return FALSE;
114}
115
116
117/* Splat the account name into the shared quickfill object */
118static void
119load_shared_qf_cb (Account* account, gpointer data)
120{
121 QFB* qfb = data;
122 char* name;
123 GtkTreeIter iter;
124
125 if (qfb->dont_add_cb)
126 {
127 gboolean skip = (qfb->dont_add_cb) (account, qfb->dont_add_data);
128 if (skip)
129 return;
130 }
131
132 name = gnc_get_account_name_for_register (account);
133 if (NULL == name)
134 return;
135 gnc_quickfill_insert (qfb->qf, name, QUICKFILL_ALPHA);
136 if (qfb->load_list_store)
137 {
138 gtk_list_store_append (qfb->list_store, &iter);
139 gtk_list_store_set (qfb->list_store, &iter,
140 ACCOUNT_NAME, name,
141 ACCOUNT_POINTER, account,
142 -1);
143 }
144 g_free (name);
145}
146
147
148static void
149shared_quickfill_pref_changed (gpointer prefs, gchar* pref, gpointer user_data)
150{
151 QFB* qfb = user_data;
152
153 /* Reload the quickfill */
154 gnc_quickfill_purge (qfb->qf);
155 gtk_list_store_clear (qfb->list_store);
156 qfb->load_list_store = TRUE;
157 gnc_account_foreach_descendant (qfb->root, load_shared_qf_cb, qfb);
158 qfb->load_list_store = FALSE;
159}
160
161
162/* Build the quickfill list out of account names.
163 * Essentially same loop as in gnc_load_xfer_cell() above.
164 */
165static QFB*
166build_shared_quickfill (QofBook* book, Account* root, const char* key,
167 AccountBoolCB cb, gpointer data)
168{
169 QFB* qfb;
170
171 qfb = g_new0 (QFB, 1);
172 qfb->qf = gnc_quickfill_new();
173 qfb->book = book;
174 qfb->root = root;
175 qfb->listener = 0;
176 qfb->dont_add_cb = cb;
177 qfb->dont_add_data = data;
178 qfb->load_list_store = TRUE;
179 qfb->list_store = gtk_list_store_new (NUM_ACCOUNT_COLUMNS,
180 G_TYPE_STRING, G_TYPE_POINTER);
181
182 gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
183 GNC_PREF_ACCOUNT_SEPARATOR,
184 shared_quickfill_pref_changed,
185 qfb);
186
187 gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL_REGISTER,
188 GNC_PREF_SHOW_LEAF_ACCT_NAMES,
189 shared_quickfill_pref_changed,
190 qfb);
191
192 gnc_account_foreach_descendant (root, load_shared_qf_cb, qfb);
193 qfb->load_list_store = FALSE;
194
195 qfb->listener = qof_event_register_handler (listen_for_account_events, qfb);
196
197 qof_book_set_data_fin (book, key, qfb, shared_quickfill_destroy);
198
199 return qfb;
200}
201
202QuickFill*
204 AccountBoolCB cb, gpointer cb_data)
205{
206 QFB* qfb;
207 QofBook* book;
208
209 book = gnc_account_get_book (root);
210 qfb = qof_book_get_data (book, key);
211
212 if (qfb)
213 return qfb->qf;
214
215 qfb = build_shared_quickfill (book, root, key, cb, cb_data);
216 return qfb->qf;
217}
218
219GtkListStore*
220gnc_get_shared_account_name_list_store (Account* root, const char* key,
221 AccountBoolCB cb, gpointer cb_data)
222{
223 QFB* qfb;
224 QofBook* book;
225
226 book = gnc_account_get_book (root);
227 qfb = qof_book_get_data (book, key);
228
229 if (qfb)
230 return qfb->list_store;
231
232 qfb = build_shared_quickfill (book, root, key, cb, cb_data);
233 return qfb->list_store;
234}
235
236/* Since we are maintaining a 'global' quickfill list, we need to
237 * update it whenever the user creates a new account. So listen
238 * for account modification events, and add new accounts.
239 */
240static void
241listen_for_account_events (QofInstance* entity, QofEventId event_type,
242 gpointer user_data, gpointer event_data)
243{
244 QFB* qfb = user_data;
245 QuickFill* qf = qfb->qf;
246 QuickFill* match;
247 char* name;
248 const char* match_str;
249 Account* account;
250 GtkTreeIter iter;
251 find_data data = { 0 };
252 GtkTreePath* path;
253 GList* tmp;
254
255 if (0 == (event_type & (QOF_EVENT_MODIFY | QOF_EVENT_ADD | QOF_EVENT_REMOVE)))
256 return;
257
258 if (!GNC_IS_ACCOUNT (entity))
259 return;
260 account = GNC_ACCOUNT (entity);
261
262 ENTER ("entity %p, event type %x, user data %p, ecent data %p",
263 entity, event_type, user_data, event_data);
264
265 if (gnc_account_get_root (account) != qfb->root)
266 {
267 LEAVE ("root account mismatch");
268 return;
269 }
270
271 name = gnc_get_account_name_for_register (account);
272 if (NULL == name)
273 {
274 LEAVE ("account has no name");
275 return;
276 }
277
278 switch (event_type)
279 {
280 case QOF_EVENT_MODIFY:
281 DEBUG ("modify %s", name);
282
283 /* Find the account (and all its descendants) in the model. The
284 * full name of all these accounts has changed. */
285 data.accounts = gnc_account_get_descendants (account);
286 data.accounts = g_list_prepend (data.accounts, account);
287 gtk_tree_model_foreach (GTK_TREE_MODEL (qfb->list_store),
288 shared_quickfill_find_accounts, &data);
289
290 /* Update the existing items in the list store. Its possible
291 * that the change has caused an existing item to now become
292 * hidden, in which case it needs to be removed from the list
293 * store. Otherwise its a simple update of the name string. */
294 for (tmp = data.refs; tmp; tmp = g_list_next (tmp))
295 {
296 gchar* old_name, *new_name;
297 path = gtk_tree_row_reference_get_path (tmp->data);
298 gtk_tree_row_reference_free (tmp->data);
299 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (qfb->list_store),
300 &iter, path))
301 {
302 gtk_tree_path_free (path);
303 continue;
304 }
305 gtk_tree_path_free (path);
306 gtk_tree_model_get (GTK_TREE_MODEL (qfb->list_store), &iter,
307 ACCOUNT_POINTER, &account,
308 ACCOUNT_NAME, &old_name,
309 -1);
310
311 new_name = gnc_get_account_name_for_register (account);
312
313 /* check if the name has changed */
314 match = gnc_quickfill_get_string_match (qf, old_name);
315 if (match && (g_strcmp0 (old_name, new_name) != 0))
316 gnc_quickfill_remove (qf, old_name, QUICKFILL_ALPHA);
317
318 if (qfb->dont_add_cb &&
319 qfb->dont_add_cb (account, qfb->dont_add_data))
320 {
321 gnc_quickfill_remove (qf, new_name, QUICKFILL_ALPHA);
322 gtk_list_store_remove (qfb->list_store, &iter);
323 }
324 else
325 {
326 gnc_quickfill_insert (qf, new_name, QUICKFILL_ALPHA);
327 gtk_list_store_set (qfb->list_store, &iter,
328 ACCOUNT_NAME, new_name,
329 -1);
330 }
331 g_free (old_name);
332 g_free (new_name);
333 }
334
335 /* Any accounts that weren't found in the tree are accounts that
336 * were hidden but have now become visible. Add them to the list
337 * store. */
338 for (tmp = data.accounts; tmp; tmp = g_list_next (tmp))
339 {
340 account = tmp->data;
341 if (qfb->dont_add_cb)
342 {
343 if (qfb->dont_add_cb (account, qfb->dont_add_data))
344 {
345 continue;
346 }
347 }
348 gnc_quickfill_insert (qf, name, QUICKFILL_ALPHA);
349 gtk_list_store_append (qfb->list_store, &iter);
350 gtk_list_store_set (qfb->list_store, &iter,
351 ACCOUNT_NAME, name,
352 ACCOUNT_POINTER, account,
353 -1);
354 }
355 break;
356
357 case QOF_EVENT_REMOVE:
358 DEBUG ("remove %s", name);
359
360 /* Remove from qf */
361 gnc_quickfill_remove (qfb->qf, name, QUICKFILL_ALPHA);
362
363 /* Does the account exist in the model? */
364 data.accounts = g_list_append (NULL, account);
365 gtk_tree_model_foreach (GTK_TREE_MODEL (qfb->list_store),
366 shared_quickfill_find_accounts, &data);
367
368 /* Remove from list store */
369 for (tmp = data.refs; tmp; tmp = g_list_next (tmp))
370 {
371 path = gtk_tree_row_reference_get_path (tmp->data);
372 gtk_tree_row_reference_free (tmp->data);
373 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (qfb->list_store),
374 &iter, path))
375 {
376 gtk_list_store_remove (qfb->list_store, &iter);
377 }
378 gtk_tree_path_free (path);
379 }
380 break;
381
382 case QOF_EVENT_ADD:
383 DEBUG ("add %s", name);
384
385 if (qfb->dont_add_cb &&
386 qfb->dont_add_cb (account, qfb->dont_add_data))
387 break;
388
389 match = gnc_quickfill_get_string_match (qf, name);
390 if (match)
391 {
392 match_str = gnc_quickfill_string (match);
393 if (match_str && (g_strcmp0 (match_str, name) != 0))
394 {
395 PINFO ("got match for %s", name);
396 break;
397 }
398 }
399
400 PINFO ("insert new account %s into qf=%p", name, qf);
401 gnc_quickfill_insert (qf, name, QUICKFILL_ALPHA);
402 gtk_list_store_append (qfb->list_store, &iter);
403 gtk_list_store_set (qfb->list_store, &iter,
404 ACCOUNT_NAME, name,
405 ACCOUNT_POINTER, account,
406 -1);
407 break;
408
409 default:
410 DEBUG ("other %s", name);
411 break;
412 }
413
414 if (data.accounts)
415 g_list_free (data.accounts);
416 if (data.refs)
417 g_list_free (data.refs);
418 g_free (name);
419 LEAVE (" ");
420}
421
422/* ====================== END OF FILE ================================== */
Create an account-name quick-fill.
All type declarations for the whole Gnucash engine.
Generic api to store and retrieve preferences.
utility functions for the GnuCash UI
QuickFill * gnc_get_shared_account_name_quickfill(Account *root, const char *key, AccountBoolCB cb, gpointer cb_data)
Create/fetch a quickfill of account names.
Account * gnc_account_get_root(Account *acc)
This routine returns the root account of the account tree that the specified account belongs to.
Definition Account.cpp:2942
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition Account.cpp:3044
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition qofevent.cpp:103
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition qofevent.cpp:73
gint QofEventId
Define the type of events allowed.
Definition qofevent.h:45
char * gnc_get_account_name_for_register(const Account *account)
Get either the full name of the account or the simple name, depending on the configuration parameter ...
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
QuickFill * gnc_quickfill_get_string_match(QuickFill *qf, const char *str)
Return a subnode in the tree whose strings all match the string 'str' as the next substring.
Definition QuickFill.c:179
const char * gnc_quickfill_string(QuickFill *qf)
For the given node 'qf', return the best-guess matching string.
Definition QuickFill.c:123
void gnc_quickfill_insert(QuickFill *qf, const char *text, QuickFillSort sort)
Add the string "text" to the collection of searchable strings.
Definition QuickFill.c:229
STRUCTS.
QofBook reference.
Definition qofbook-p.hpp:47