GnuCash 2.4.99
QuickFill.c
00001 /********************************************************************\
00002  * QuickFill.h -- the quickfill tree data structure                 *
00003  * Copyright (C) 1997 Robin D. Clark                                *
00004  * Copyright (C) 1998 Linas Vepstas                                 *
00005  * Copyright (C) 2000 Dave Peticolas                                *
00006  *                                                                  *
00007  * This program is free software; you can redistribute it and/or    *
00008  * modify it under the terms of the GNU General Public License as   *
00009  * published by the Free Software Foundation; either version 2 of   *
00010  * the License, or (at your option) any later version.              *
00011  *                                                                  *
00012  * This program is distributed in the hope that it will be useful,  *
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00015  * GNU General Public License for more details.                     *
00016  *                                                                  *
00017  * You should have received a copy of the GNU General Public License*
00018  * along with this program; if not, contact:                        *
00019  *                                                                  *
00020  * Free Software Foundation           Voice:  +1-617-542-5942       *
00021  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00022  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00023  *                                                                  *
00024 \********************************************************************/
00025 
00026 #include "config.h"
00027 
00028 #include <string.h>
00029 
00030 #include "QuickFill.h"
00031 #include "gnc-engine.h"
00032 #include "gnc-ui-util.h"
00033 
00034 
00035 struct _QuickFill
00036 {
00037     char *text;          /* the first matching text string     */
00038     int len;             /* number of chars in text string     */
00039     GHashTable *matches; /* array of children in the tree      */
00040 };
00041 
00042 
00044 static void quickfill_insert_recursive (QuickFill *qf, const char *text,
00045                                         int depth, QuickFillSort sort);
00046 
00047 static void gnc_quickfill_remove_recursive (QuickFill *qf, const gchar *text,
00048         gint depth, QuickFillSort sort);
00049 
00050 /* This static indicates the debugging module that this .o belongs to.  */
00051 static QofLogModule log_module = GNC_MOD_REGISTER;
00052 
00053 /********************************************************************\
00054 \********************************************************************/
00055 
00056 QuickFill *
00057 gnc_quickfill_new (void)
00058 {
00059     QuickFill *qf;
00060 
00061     if (sizeof (guint) < sizeof (gunichar))
00062     {
00063         PWARN ("Can't use quickfill");
00064         return NULL;
00065     }
00066 
00067     qf = g_new (QuickFill, 1);
00068 
00069     qf->text = NULL;
00070     qf->len = 0;
00071 
00072     qf->matches = g_hash_table_new (g_direct_hash, g_direct_equal);
00073 
00074     return qf;
00075 }
00076 
00077 /********************************************************************\
00078 \********************************************************************/
00079 
00080 static gboolean
00081 destroy_helper (gpointer key, gpointer value, gpointer data)
00082 {
00083     gnc_quickfill_destroy (value);
00084     return TRUE;
00085 }
00086 
00087 void
00088 gnc_quickfill_destroy (QuickFill *qf)
00089 {
00090     if (qf == NULL)
00091         return;
00092 
00093     g_hash_table_foreach (qf->matches, (GHFunc)destroy_helper, NULL);
00094     g_hash_table_destroy (qf->matches);
00095     qf->matches = NULL;
00096 
00097     if (qf->text)
00098         CACHE_REMOVE(qf->text);
00099     qf->text = NULL;
00100     qf->len = 0;
00101 
00102     g_free (qf);
00103 }
00104 
00105 void
00106 gnc_quickfill_purge (QuickFill *qf)
00107 {
00108     if (qf == NULL)
00109         return;
00110 
00111     g_hash_table_foreach_remove (qf->matches, destroy_helper, NULL);
00112 
00113     if (qf->text)
00114         CACHE_REMOVE (qf->text);
00115     qf->text = NULL;
00116     qf->len = 0;
00117 }
00118 
00119 /********************************************************************\
00120 \********************************************************************/
00121 
00122 const char *
00123 gnc_quickfill_string (QuickFill *qf)
00124 {
00125     if (qf == NULL)
00126         return NULL;
00127 
00128     return qf->text;
00129 }
00130 
00131 /********************************************************************\
00132 \********************************************************************/
00133 
00134 QuickFill *
00135 gnc_quickfill_get_char_match (QuickFill *qf, gunichar uc)
00136 {
00137     guint key = g_unichar_toupper (uc);
00138 
00139     if (NULL == qf) return NULL;
00140 
00141     DEBUG ("xaccGetQuickFill(): index = %u\n", key);
00142 
00143     return g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
00144 }
00145 
00146 /********************************************************************\
00147 \********************************************************************/
00148 
00149 QuickFill *
00150 gnc_quickfill_get_string_len_match (QuickFill *qf,
00151                                     const char *str, int len)
00152 {
00153     const char *c;
00154     gunichar uc;
00155 
00156     if (NULL == qf) return NULL;
00157     if (NULL == str) return NULL;
00158 
00159     c = str;
00160     while (*c && (len > 0))
00161     {
00162         if (qf == NULL)
00163             return NULL;
00164 
00165         uc = g_utf8_get_char (c);
00166         qf = gnc_quickfill_get_char_match (qf, uc);
00167 
00168         c = g_utf8_next_char (c);
00169         len--;
00170     }
00171 
00172     return qf;
00173 }
00174 
00175 /********************************************************************\
00176 \********************************************************************/
00177 
00178 QuickFill *
00179 gnc_quickfill_get_string_match (QuickFill *qf, const char *str)
00180 {
00181     if (NULL == qf) return NULL;
00182     if (NULL == str) return NULL;
00183 
00184     return gnc_quickfill_get_string_len_match (qf, str, g_utf8_strlen (str, -1));
00185 }
00186 
00187 /********************************************************************\
00188 \********************************************************************/
00189 
00190 static void
00191 unique_len_helper (gpointer key, gpointer value, gpointer data)
00192 {
00193     QuickFill **qf_p = data;
00194 
00195     *qf_p = value;
00196 }
00197 
00198 QuickFill *
00199 gnc_quickfill_get_unique_len_match (QuickFill *qf, int *length)
00200 {
00201     if (length != NULL)
00202         *length = 0;
00203 
00204     if (qf == NULL)
00205         return NULL;
00206 
00207     while (1)
00208     {
00209         guint count;
00210 
00211         count = g_hash_table_size (qf->matches);
00212 
00213         if (count != 1)
00214         {
00215             return qf;
00216         }
00217 
00218         g_hash_table_foreach (qf->matches, unique_len_helper, &qf);
00219 
00220         if (length != NULL)
00221             (*length)++;
00222     }
00223 }
00224 
00225 /********************************************************************\
00226 \********************************************************************/
00227 
00228 void
00229 gnc_quickfill_insert (QuickFill *qf, const char *text, QuickFillSort sort)
00230 {
00231     gchar *normalized_str;
00232 
00233     if (NULL == qf) return;
00234     if (NULL == text) return;
00235 
00236 
00237     normalized_str = g_utf8_normalize (text, -1, G_NORMALIZE_NFC);
00238     quickfill_insert_recursive (qf, normalized_str, 0, sort);
00239     g_free (normalized_str);
00240 }
00241 
00242 /********************************************************************\
00243 \********************************************************************/
00244 
00245 static void
00246 quickfill_insert_recursive (QuickFill *qf, const char *text, int depth,
00247                             QuickFillSort sort)
00248 {
00249     guint key;
00250     char *old_text;
00251     QuickFill *match_qf;
00252     int len;
00253     char *key_char;
00254     gunichar key_char_uc;
00255 
00256     if (qf == NULL)
00257         return;
00258 
00259     if ((text == NULL) || (g_utf8_strlen (text, -1) <= depth))
00260         return;
00261 
00262     key_char = g_utf8_offset_to_pointer (text, depth);
00263 
00264     key_char_uc = g_utf8_get_char (key_char);
00265     key = g_unichar_toupper (key_char_uc);
00266 
00267     match_qf = g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
00268     if (match_qf == NULL)
00269     {
00270         match_qf = gnc_quickfill_new ();
00271         g_hash_table_insert (qf->matches, GUINT_TO_POINTER (key), match_qf);
00272     }
00273 
00274     old_text = match_qf->text;
00275 
00276     switch (sort)
00277     {
00278     case QUICKFILL_ALPHA:
00279         if (old_text && (g_utf8_collate (text, old_text) >= 0))
00280             break;
00281 
00282     case QUICKFILL_LIFO:
00283     default:
00284         len = g_utf8_strlen (text, -1);
00285 
00286         /* If there's no string there already, just put the new one in. */
00287         if (old_text == NULL)
00288         {
00289             match_qf->text = CACHE_INSERT((gpointer) text);
00290             match_qf->len = len;
00291             break;
00292         }
00293 
00294         /* Leave prefixes in place */
00295         if ((len > match_qf->len) &&
00296                 (strncmp(text, old_text, strlen(old_text)) == 0))
00297             break;
00298 
00299         CACHE_REMOVE(old_text);
00300         match_qf->text = CACHE_INSERT((gpointer) text);
00301         match_qf->len = len;
00302         break;
00303     }
00304 
00305     quickfill_insert_recursive (match_qf, text, ++depth, sort);
00306 }
00307 
00308 /********************************************************************\
00309 \********************************************************************/
00310 
00311 void
00312 gnc_quickfill_remove (QuickFill *qf, const gchar *text, QuickFillSort sort)
00313 {
00314     gchar *normalized_str;
00315 
00316     if (qf == NULL) return;
00317     if (text == NULL) return;
00318 
00319     normalized_str = g_utf8_normalize (text, -1, G_NORMALIZE_NFC);
00320     gnc_quickfill_remove_recursive (qf, normalized_str, 0, sort);
00321     g_free (normalized_str);
00322 }
00323 
00324 /********************************************************************\
00325 \********************************************************************/
00326 
00327 struct _BestText
00328 {
00329     gchar *text;
00330     QuickFillSort sort;
00331 };
00332 
00333 static void
00334 best_text_helper (gpointer key, gpointer value, gpointer user_data)
00335 {
00336     QuickFill *qf = value;
00337     struct _BestText *best = user_data;
00338 
00339     if (best->text == NULL)
00340     {
00341         /* start with the first text */
00342         best->text = qf->text;
00343 
00344     }
00345     else if (best->text == QUICKFILL_LIFO)
00346     {
00347         /* we do not track history, so ignore it */
00348         return;
00349 
00350     }
00351     else if (g_utf8_collate (qf->text, best->text) < 0)
00352     {
00353         /* even better text */
00354         best->text = qf->text;
00355     }
00356 }
00357 
00358 
00359 
00360 static void
00361 gnc_quickfill_remove_recursive (QuickFill *qf, const gchar *text, gint depth,
00362                                 QuickFillSort sort)
00363 {
00364     QuickFill *match_qf;
00365     gchar *child_text;
00366     gint child_len;
00367 
00368     child_text = NULL;
00369     child_len = 0;
00370 
00371     if (depth < g_utf8_strlen (text, -1))
00372     {
00373         /* process next letter */
00374 
00375         gchar *key_char;
00376         gunichar key_char_uc;
00377         guint key;
00378 
00379         key_char = g_utf8_offset_to_pointer (text, depth);
00380         key_char_uc = g_utf8_get_char (key_char);
00381         key = g_unichar_toupper (key_char_uc);
00382 
00383         match_qf = g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
00384         if (match_qf)
00385         {
00386             /* remove text from child qf */
00387             gnc_quickfill_remove_recursive (match_qf, text, depth + 1, sort);
00388 
00389             if (match_qf->text == NULL)
00390             {
00391                 /* text was the only word with a prefix up to match_qf */
00392                 g_hash_table_remove (qf->matches, GUINT_TO_POINTER (key));
00393                 gnc_quickfill_destroy (match_qf);
00394 
00395             }
00396             else
00397             {
00398                 /* remember remaining best child string */
00399                 child_text = match_qf->text;
00400                 child_len = match_qf->len;
00401             }
00402         }
00403     }
00404 
00405     if (qf->text == NULL)
00406         return;
00407 
00408     if (strcmp (text, qf->text) == 0)
00409     {
00410         /* the currently best text is about to be removed */
00411 
00412         gchar *best_text = NULL;
00413         gint best_len = 0;
00414 
00415         if (child_text != NULL)
00416         {
00417             /* other children are pretty good as well */
00418             best_text = child_text;
00419             best_len = child_len;
00420 
00421         }
00422         else
00423         {
00424             if (g_hash_table_size (qf->matches) != 0)
00425             {
00426                 /* otherwise search for another good text */
00427                 struct _BestText bts;
00428                 bts.text = NULL;
00429                 bts.sort = sort;
00430 
00431                 g_hash_table_foreach (qf->matches, (GHFunc) best_text_helper, &bts);
00432                 best_text = bts.text;
00433                 best_len = (best_text == NULL) ? 0 : g_utf8_strlen (best_text, -1);
00434             }
00435         }
00436 
00437         /* now replace or clear text */
00438         CACHE_REMOVE(qf->text);
00439         if (best_text != NULL)
00440         {
00441             qf->text = CACHE_INSERT((gpointer) best_text);
00442             qf->len = best_len;
00443         }
00444         else
00445         {
00446             qf->text = NULL;
00447             qf->len = 0;
00448         }
00449     }
00450 }
00451 
00452 /********************** END OF FILE *********************************   \
00453 \********************************************************************/
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines