GnuCash 2.4.99
account-quickfill.c
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 ================================== */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines