|
GnuCash 2.4.99
|
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 \********************************************************************/
1.7.4