|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * account-quickfill.h -- Create an account-name quick-fill * 00003 * Copyright (C) 2004 Linas Vepstas <linas@linas.org> * 00004 * * 00005 * This program is free software; you can redistribute it and/or * 00006 * modify it under the terms of the GNU General Public License as * 00007 * published by the Free Software Foundation; either version 2 of * 00008 * the License, or (at your option) any later version. * 00009 * * 00010 * This program is distributed in the hope that it will be useful, * 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00013 * GNU General Public License for more details. * 00014 * * 00015 * You should have received a copy of the GNU General Public License* 00016 * along with this program; if not, contact: * 00017 * * 00018 * Free Software Foundation Voice: +1-617-542-5942 * 00019 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00020 * Boston, MA 02110-1301, USA gnu@gnu.org * 00021 * * 00022 \********************************************************************/ 00023 00024 #include "config.h" 00025 #include "account-quickfill.h" 00026 #include "gnc-gconf-utils.h" 00027 #include "gnc-engine.h" 00028 #include "gnc-ui-util.h" 00029 00030 /* This static indicates the debugging module that this .o belongs to. */ 00031 static QofLogModule log_module = GNC_MOD_REGISTER; 00032 00033 static void shared_quickfill_gconf_changed (GConfEntry *entry, gpointer qfb); 00034 static void listen_for_account_events (QofInstance *entity, QofEventId event_type, 00035 gpointer user_data, gpointer event_data); 00036 00037 /* Column indices for the list store */ 00038 #define ACCOUNT_NAME 0 00039 #define ACCOUNT_POINTER 1 00040 #define NUM_ACCOUNT_COLUMNS 2 00041 00042 /* ===================================================================== */ 00043 /* In order to speed up register starts for registers that have a huge 00044 * number of accounts in them (where 'huge' is >500) we build a quickfill 00045 * cache of account names. This cache is needed because some users on 00046 * some machines experience register open times in the tens of seconds 00047 * type timescales. Building the quickfill list accounts for almost 00048 * all of that cpu time (about 90% of the xfer_cell build time for 600 00049 * accounts). 00050 */ 00051 00052 typedef struct 00053 { 00054 QuickFill *qf; 00055 gboolean load_list_store; 00056 GtkListStore *list_store; 00057 QofBook *book; 00058 Account *root; 00059 gint listener; 00060 AccountBoolCB dont_add_cb; 00061 gpointer dont_add_data; 00062 } QFB; 00063 00064 static void 00065 shared_quickfill_destroy (QofBook *book, gpointer key, gpointer user_data) 00066 { 00067 QFB *qfb = user_data; 00068 gnc_gconf_general_remove_cb(KEY_ACCOUNT_SEPARATOR, 00069 shared_quickfill_gconf_changed, 00070 qfb); 00071 gnc_gconf_general_remove_cb(KEY_SHOW_LEAF_ACCOUNT_NAMES, 00072 shared_quickfill_gconf_changed, 00073 qfb); 00074 gnc_quickfill_destroy (qfb->qf); 00075 g_object_unref(qfb->list_store); 00076 qof_event_unregister_handler (qfb->listener); 00077 g_free (qfb); 00078 } 00079 00080 00081 typedef struct find_data 00082 { 00083 GList *accounts; 00084 GList *refs; 00085 } find_data; 00086 00087 static gboolean 00088 shared_quickfill_find_accounts (GtkTreeModel *model, 00089 GtkTreePath *path, 00090 GtkTreeIter *iter, 00091 gpointer user_data) 00092 { 00093 Account *account = NULL; 00094 find_data *data = user_data; 00095 GtkTreeRowReference* ref; 00096 GList *tmp; 00097 00098 gtk_tree_model_get(model, iter, ACCOUNT_POINTER, &account, -1); 00099 for (tmp = data->accounts; tmp; tmp = g_list_next(tmp)) 00100 { 00101 if (tmp->data == account) 00102 { 00103 ref = gtk_tree_row_reference_new(model, path); 00104 data->refs = g_list_append(data->refs, ref); 00105 data->accounts = g_list_remove_link(data->accounts, tmp); 00106 return (data->accounts == NULL); 00107 } 00108 } 00109 00110 return FALSE; 00111 } 00112 00113 00114 /* Splat the account name into the shared quickfill object */ 00115 static void 00116 load_shared_qf_cb (Account *account, gpointer data) 00117 { 00118 QFB *qfb = data; 00119 char *name; 00120 GtkTreeIter iter; 00121 00122 if (qfb->dont_add_cb) 00123 { 00124 gboolean skip = (qfb->dont_add_cb) (account, qfb->dont_add_data); 00125 if (skip) return; 00126 } 00127 00128 name = gnc_get_account_name_for_register (account); 00129 if (NULL == name) return; 00130 gnc_quickfill_insert (qfb->qf, name, QUICKFILL_ALPHA); 00131 if (qfb->load_list_store) 00132 { 00133 gtk_list_store_append (qfb->list_store, &iter); 00134 gtk_list_store_set (qfb->list_store, &iter, 00135 ACCOUNT_NAME, name, 00136 ACCOUNT_POINTER, account, 00137 -1); 00138 } 00139 g_free(name); 00140 } 00141 00142 static void 00143 shared_quickfill_gconf_changed (GConfEntry *entry, gpointer user_data) 00144 { 00145 QFB *qfb = user_data; 00146 00147 /* Reload the quickfill */ 00148 gnc_quickfill_purge(qfb->qf); 00149 gtk_list_store_clear(qfb->list_store); 00150 qfb->load_list_store = TRUE; 00151 gnc_account_foreach_descendant(qfb->root, load_shared_qf_cb, qfb); 00152 qfb->load_list_store = FALSE; 00153 } 00154 00155 00156 /* Build the quickfill list out of account names. 00157 * Essentially same loop as in gnc_load_xfer_cell() above. 00158 */ 00159 static QFB * 00160 build_shared_quickfill (QofBook *book, Account *root, const char * key, 00161 AccountBoolCB cb, gpointer data) 00162 { 00163 QFB *qfb; 00164 00165 qfb = g_new0(QFB, 1); 00166 qfb->qf = gnc_quickfill_new (); 00167 qfb->book = book; 00168 qfb->root = root; 00169 qfb->listener = 0; 00170 qfb->dont_add_cb = cb; 00171 qfb->dont_add_data = data; 00172 qfb->load_list_store = TRUE; 00173 qfb->list_store = 00174 gtk_list_store_new (NUM_ACCOUNT_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); 00175 00176 gnc_gconf_general_register_cb(KEY_ACCOUNT_SEPARATOR, 00177 shared_quickfill_gconf_changed, 00178 qfb); 00179 00180 gnc_gconf_general_register_cb(KEY_SHOW_LEAF_ACCOUNT_NAMES, 00181 shared_quickfill_gconf_changed, 00182 qfb); 00183 00184 gnc_account_foreach_descendant(root, load_shared_qf_cb, qfb); 00185 qfb->load_list_store = FALSE; 00186 00187 qfb->listener = 00188 qof_event_register_handler (listen_for_account_events, qfb); 00189 00190 qof_book_set_data_fin (book, key, qfb, shared_quickfill_destroy); 00191 00192 return qfb; 00193 } 00194 00195 QuickFill * 00196 gnc_get_shared_account_name_quickfill (Account *root, 00197 const char * key, 00198 AccountBoolCB cb, gpointer cb_data) 00199 { 00200 QFB *qfb; 00201 QofBook *book; 00202 00203 book = gnc_account_get_book (root); 00204 qfb = qof_book_get_data (book, key); 00205 00206 if (qfb) return qfb->qf; 00207 00208 qfb = build_shared_quickfill (book, root, key, cb, cb_data); 00209 return qfb->qf; 00210 } 00211 00212 GtkListStore * 00213 gnc_get_shared_account_name_list_store (Account *root, 00214 const char * key, 00215 AccountBoolCB cb, gpointer cb_data) 00216 { 00217 QFB *qfb; 00218 QofBook *book; 00219 00220 book = gnc_account_get_book (root); 00221 qfb = qof_book_get_data (book, key); 00222 00223 if (qfb) return qfb->list_store; 00224 00225 qfb = build_shared_quickfill (book, root, key, cb, cb_data); 00226 return qfb->list_store; 00227 } 00228 00229 /* Since we are maintaining a 'global' quickfill list, we need to 00230 * update it whenever the user creates a new account. So listen 00231 * for account modification events, and add new accounts. 00232 */ 00233 static void 00234 listen_for_account_events (QofInstance *entity, QofEventId event_type, 00235 gpointer user_data, gpointer event_data) 00236 { 00237 QFB *qfb = user_data; 00238 QuickFill *qf = qfb->qf; 00239 QuickFill *match; 00240 char * name; 00241 const char *match_str; 00242 Account *account; 00243 GtkTreeIter iter; 00244 find_data data = { 0 }; 00245 GtkTreePath *path; 00246 GList *tmp; 00247 00248 if (0 == (event_type & (QOF_EVENT_MODIFY | QOF_EVENT_ADD | QOF_EVENT_REMOVE))) 00249 return; 00250 00251 if (!GNC_IS_ACCOUNT (entity)) 00252 return; 00253 account = GNC_ACCOUNT (entity); 00254 00255 ENTER("entity %p, event type %x, user data %p, ecent data %p", 00256 entity, event_type, user_data, event_data); 00257 00258 if (gnc_account_get_root(account) != qfb->root) 00259 { 00260 LEAVE("root account mismatch"); 00261 return; 00262 } 00263 00264 name = gnc_get_account_name_for_register(account); 00265 if (NULL == name) 00266 { 00267 LEAVE("account has no name"); 00268 return; 00269 } 00270 00271 switch (event_type) 00272 { 00273 case QOF_EVENT_MODIFY: 00274 DEBUG("modify %s", name); 00275 00276 /* Find the account (and all its descendants) in the model. The 00277 * full name of all these accounts has changed. */ 00278 data.accounts = gnc_account_get_descendants(account); 00279 data.accounts = g_list_prepend(data.accounts, account); 00280 gtk_tree_model_foreach(GTK_TREE_MODEL(qfb->list_store), 00281 shared_quickfill_find_accounts, &data); 00282 00283 /* Update the existing items in the list store. Its possible 00284 * that the change has caused an existing item to now become 00285 * hidden, in which case it needs to be removed from the list 00286 * store. Otherwise its a simple update of the name string. */ 00287 for (tmp = data.refs; tmp; tmp = g_list_next(tmp)) 00288 { 00289 path = gtk_tree_row_reference_get_path(tmp->data); 00290 gtk_tree_row_reference_free(tmp->data); 00291 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(qfb->list_store), 00292 &iter, path)) 00293 { 00294 gtk_tree_path_free(path); 00295 continue; 00296 } 00297 gtk_tree_path_free(path); 00298 gtk_tree_model_get(GTK_TREE_MODEL(qfb->list_store), &iter, 00299 ACCOUNT_POINTER, &account, 00300 -1); 00301 if (qfb->dont_add_cb && 00302 qfb->dont_add_cb(account, qfb->dont_add_data)) 00303 { 00304 gtk_list_store_remove(qfb->list_store, &iter); 00305 } 00306 else 00307 { 00308 gchar *aname = gnc_get_account_name_for_register(account); 00309 gtk_list_store_set(qfb->list_store, &iter, 00310 ACCOUNT_NAME, aname, 00311 -1); 00312 g_free(aname); 00313 } 00314 } 00315 00316 /* Any accounts that weren't found in the tree are accounts that 00317 * were hidden but have now become visible. Add them to the list 00318 * store. */ 00319 for (tmp = data.accounts; tmp; tmp = g_list_next(tmp)) 00320 { 00321 account = tmp->data; 00322 if (qfb->dont_add_cb) 00323 { 00324 if (qfb->dont_add_cb(account, qfb->dont_add_data)) 00325 { 00326 continue; 00327 } 00328 } 00329 gtk_list_store_append (qfb->list_store, &iter); 00330 gtk_list_store_set (qfb->list_store, &iter, 00331 ACCOUNT_NAME, name, 00332 ACCOUNT_POINTER, account, 00333 -1); 00334 } 00335 break; 00336 00337 case QOF_EVENT_REMOVE: 00338 DEBUG("remove %s", name); 00339 00340 /* Remove from qf */ 00341 gnc_quickfill_remove(qfb->qf, name, QUICKFILL_ALPHA); 00342 00343 /* Does the account exist in the model? */ 00344 data.accounts = g_list_append(NULL, account); 00345 gtk_tree_model_foreach(GTK_TREE_MODEL(qfb->list_store), 00346 shared_quickfill_find_accounts, &data); 00347 00348 /* Remove from list store */ 00349 for (tmp = data.refs; tmp; tmp = g_list_next(tmp)) 00350 { 00351 path = gtk_tree_row_reference_get_path (tmp->data); 00352 gtk_tree_row_reference_free (tmp->data); 00353 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(qfb->list_store), 00354 &iter, path)) 00355 { 00356 gtk_list_store_remove(qfb->list_store, &iter); 00357 } 00358 gtk_tree_path_free(path); 00359 } 00360 break; 00361 00362 case QOF_EVENT_ADD: 00363 DEBUG("add %s", name); 00364 if (qfb->dont_add_cb && 00365 qfb->dont_add_cb(account, qfb->dont_add_data)) 00366 break; 00367 00368 match = gnc_quickfill_get_string_match (qf, name); 00369 if (match) 00370 { 00371 match_str = gnc_quickfill_string (match); 00372 if (match_str && (safe_strcmp(match_str, name) != 0)) 00373 { 00374 PINFO ("got match for %s", name); 00375 break; 00376 } 00377 } 00378 00379 PINFO ("insert new account %s into qf=%p", name, qf); 00380 gnc_quickfill_insert (qf, name, QUICKFILL_ALPHA); 00381 gtk_list_store_append (qfb->list_store, &iter); 00382 gtk_list_store_set (qfb->list_store, &iter, 00383 ACCOUNT_NAME, name, 00384 ACCOUNT_POINTER, account, 00385 -1); 00386 break; 00387 00388 default: 00389 DEBUG("other %s", name); 00390 break; 00391 } 00392 00393 if (data.accounts) 00394 g_list_free(data.accounts); 00395 if (data.refs) 00396 g_list_free(data.refs); 00397 g_free(name); 00398 LEAVE(" "); 00399 } 00400 00401 /* ====================== END OF FILE ================================== */
1.7.4