GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
QuickFill.c
1 /********************************************************************\
2  * QuickFill.h -- the quickfill tree data structure *
3  * Copyright (C) 1997 Robin D. Clark *
4  * Copyright (C) 1998 Linas Vepstas *
5  * Copyright (C) 2000 Dave Peticolas *
6  * *
7  * This program is free software; you can redistribute it and/or *
8  * modify it under the terms of the GNU General Public License as *
9  * published by the Free Software Foundation; either version 2 of *
10  * the License, or (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License*
18  * along with this program; if not, contact: *
19  * *
20  * Free Software Foundation Voice: +1-617-542-5942 *
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22  * Boston, MA 02110-1301, USA [email protected] *
23  * *
24 \********************************************************************/
25 
26 #include "config.h"
27 
28 #include <string.h>
29 
30 #include "QuickFill.h"
31 #include "gnc-engine.h"
32 #include "gnc-ui-util.h"
33 
34 
35 struct _QuickFill
36 {
37  char *text; /* the first matching text string */
38  int len; /* number of chars in text string */
39  GHashTable *matches; /* array of children in the tree */
40 };
41 
42 
44 static void quickfill_insert_recursive (QuickFill *qf, const char *text,
45  int depth, QuickFillSort sort);
46 
47 static void gnc_quickfill_remove_recursive (QuickFill *qf, const gchar *text,
48  gint depth, QuickFillSort sort);
49 
50 /* This static indicates the debugging module that this .o belongs to. */
51 static QofLogModule log_module = GNC_MOD_REGISTER;
52 
53 /********************************************************************\
54 \********************************************************************/
55 
56 QuickFill *
57 gnc_quickfill_new (void)
58 {
59  QuickFill *qf;
60 
61  if (sizeof (guint) < sizeof (gunichar))
62  {
63  PWARN ("Can't use quickfill");
64  return NULL;
65  }
66 
67  qf = g_new (QuickFill, 1);
68 
69  qf->text = NULL;
70  qf->len = 0;
71 
72  qf->matches = g_hash_table_new (g_direct_hash, g_direct_equal);
73 
74  return qf;
75 }
76 
77 /********************************************************************\
78 \********************************************************************/
79 
80 static gboolean
81 destroy_helper (gpointer key, gpointer value, gpointer data)
82 {
83  gnc_quickfill_destroy (value);
84  return TRUE;
85 }
86 
87 void
88 gnc_quickfill_destroy (QuickFill *qf)
89 {
90  if (qf == NULL)
91  return;
92 
93  g_hash_table_foreach (qf->matches, (GHFunc)destroy_helper, NULL);
94  g_hash_table_destroy (qf->matches);
95  qf->matches = NULL;
96 
97  if (qf->text)
98  CACHE_REMOVE(qf->text);
99  qf->text = NULL;
100  qf->len = 0;
101 
102  g_free (qf);
103 }
104 
105 void
106 gnc_quickfill_purge (QuickFill *qf)
107 {
108  if (qf == NULL)
109  return;
110 
111  g_hash_table_foreach_remove (qf->matches, destroy_helper, NULL);
112 
113  if (qf->text)
114  CACHE_REMOVE (qf->text);
115  qf->text = NULL;
116  qf->len = 0;
117 }
118 
119 /********************************************************************\
120 \********************************************************************/
121 
122 const char *
124 {
125  if (qf == NULL)
126  return NULL;
127 
128  return qf->text;
129 }
130 
131 /********************************************************************\
132 \********************************************************************/
133 
134 QuickFill *
136 {
137  guint key = g_unichar_toupper (uc);
138 
139  if (NULL == qf) return NULL;
140 
141  DEBUG ("xaccGetQuickFill(): index = %u\n", key);
142 
143  return g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
144 }
145 
146 /********************************************************************\
147 \********************************************************************/
148 
149 QuickFill *
151  const char *str, int len)
152 {
153  const char *c;
154  gunichar uc;
155 
156  if (NULL == qf) return NULL;
157  if (NULL == str) return NULL;
158 
159  c = str;
160  while (*c && (len > 0))
161  {
162  if (qf == NULL)
163  return NULL;
164 
165  uc = g_utf8_get_char (c);
166  qf = gnc_quickfill_get_char_match (qf, uc);
167 
168  c = g_utf8_next_char (c);
169  len--;
170  }
171 
172  return qf;
173 }
174 
175 /********************************************************************\
176 \********************************************************************/
177 
178 QuickFill *
180 {
181  if (NULL == qf) return NULL;
182  if (NULL == str) return NULL;
183 
184  return gnc_quickfill_get_string_len_match (qf, str, g_utf8_strlen (str, -1));
185 }
186 
187 /********************************************************************\
188 \********************************************************************/
189 
190 static void
191 unique_len_helper (gpointer key, gpointer value, gpointer data)
192 {
193  QuickFill **qf_p = data;
194 
195  *qf_p = value;
196 }
197 
198 QuickFill *
200 {
201  if (length != NULL)
202  *length = 0;
203 
204  if (qf == NULL)
205  return NULL;
206 
207  while (1)
208  {
209  guint count;
210 
211  count = g_hash_table_size (qf->matches);
212 
213  if (count != 1)
214  break;
215 
216  g_hash_table_foreach (qf->matches, unique_len_helper, &qf);
217 
218  if (length != NULL)
219  (*length)++;
220  }
221 
222  return qf;
223 }
224 
225 /********************************************************************\
226 \********************************************************************/
227 
228 void
229 gnc_quickfill_insert (QuickFill *qf, const char *text, QuickFillSort sort)
230 {
231  gchar *normalized_str;
232 
233  if (NULL == qf) return;
234  if (NULL == text) return;
235 
236 
237  normalized_str = g_utf8_normalize (text, -1, G_NORMALIZE_NFC);
238  quickfill_insert_recursive (qf, normalized_str, 0, sort);
239  g_free (normalized_str);
240 }
241 
242 /********************************************************************\
243 \********************************************************************/
244 
245 static void
246 quickfill_insert_recursive (QuickFill *qf, const char *text, int depth,
247  QuickFillSort sort)
248 {
249  guint key;
250  char *old_text;
251  QuickFill *match_qf;
252  int len;
253  char *key_char;
254  gunichar key_char_uc;
255 
256  if (qf == NULL)
257  return;
258 
259  if ((text == NULL) || (g_utf8_strlen (text, -1) <= depth))
260  return;
261 
262  key_char = g_utf8_offset_to_pointer (text, depth);
263 
264  key_char_uc = g_utf8_get_char (key_char);
265  key = g_unichar_toupper (key_char_uc);
266 
267  match_qf = g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
268  if (match_qf == NULL)
269  {
270  match_qf = gnc_quickfill_new ();
271  g_hash_table_insert (qf->matches, GUINT_TO_POINTER (key), match_qf);
272  }
273 
274  old_text = match_qf->text;
275 
276  switch (sort)
277  {
278  case QUICKFILL_ALPHA:
279  if (old_text && (g_utf8_collate (text, old_text) >= 0))
280  break;
281  /* fall through */
282 
283  case QUICKFILL_LIFO:
284  default:
285  len = g_utf8_strlen (text, -1);
286 
287  /* If there's no string there already, just put the new one in. */
288  if (old_text == NULL)
289  {
290  match_qf->text = CACHE_INSERT((gpointer) text);
291  match_qf->len = len;
292  break;
293  }
294 
295  /* Leave prefixes in place */
296  if ((len > match_qf->len) &&
297  (strncmp(text, old_text, strlen(old_text)) == 0))
298  break;
299 
300  CACHE_REMOVE(old_text);
301  match_qf->text = CACHE_INSERT((gpointer) text);
302  match_qf->len = len;
303  break;
304  }
305 
306  quickfill_insert_recursive (match_qf, text, ++depth, sort);
307 }
308 
309 /********************************************************************\
310 \********************************************************************/
311 
312 void
313 gnc_quickfill_remove (QuickFill *qf, const gchar *text, QuickFillSort sort)
314 {
315  gchar *normalized_str;
316 
317  if (qf == NULL) return;
318  if (text == NULL) return;
319 
320  normalized_str = g_utf8_normalize (text, -1, G_NORMALIZE_NFC);
321  gnc_quickfill_remove_recursive (qf, normalized_str, 0, sort);
322  g_free (normalized_str);
323 }
324 
325 /********************************************************************\
326 \********************************************************************/
327 
328 struct _BestText
329 {
330  gchar *text;
331  QuickFillSort sort;
332 };
333 
334 static void
335 best_text_helper (gpointer key, gpointer value, gpointer user_data)
336 {
337  QuickFill *qf = value;
338  struct _BestText *best = user_data;
339 
340  if (best->text == NULL)
341  {
342  /* start with the first text */
343  best->text = qf->text;
344 
345  }
346  else if (best->text == QUICKFILL_LIFO)
347  {
348  /* we do not track history, so ignore it */
349  return;
350 
351  }
352  else if (g_utf8_collate (qf->text, best->text) < 0)
353  {
354  /* even better text */
355  best->text = qf->text;
356  }
357 }
358 
359 
360 
361 static void
362 gnc_quickfill_remove_recursive (QuickFill *qf, const gchar *text, gint depth,
363  QuickFillSort sort)
364 {
365  QuickFill *match_qf;
366  gchar *child_text;
367  gint child_len;
368 
369  child_text = NULL;
370  child_len = 0;
371 
372  if (depth < g_utf8_strlen (text, -1))
373  {
374  /* process next letter */
375 
376  gchar *key_char;
377  gunichar key_char_uc;
378  guint key;
379 
380  key_char = g_utf8_offset_to_pointer (text, depth);
381  key_char_uc = g_utf8_get_char (key_char);
382  key = g_unichar_toupper (key_char_uc);
383 
384  match_qf = g_hash_table_lookup (qf->matches, GUINT_TO_POINTER (key));
385  if (match_qf)
386  {
387  /* remove text from child qf */
388  gnc_quickfill_remove_recursive (match_qf, text, depth + 1, sort);
389 
390  if (match_qf->text == NULL)
391  {
392  /* text was the only word with a prefix up to match_qf */
393  g_hash_table_remove (qf->matches, GUINT_TO_POINTER (key));
394  gnc_quickfill_destroy (match_qf);
395 
396  }
397  else
398  {
399  /* remember remaining best child string */
400  child_text = match_qf->text;
401  child_len = match_qf->len;
402  }
403  }
404  }
405 
406  if (qf->text == NULL)
407  return;
408 
409  if (strcmp (text, qf->text) == 0)
410  {
411  /* the currently best text is about to be removed */
412 
413  gchar *best_text = NULL;
414  gint best_len = 0;
415 
416  if (child_text != NULL)
417  {
418  /* other children are pretty good as well */
419  best_text = child_text;
420  best_len = child_len;
421 
422  }
423  else
424  {
425  if (g_hash_table_size (qf->matches) != 0)
426  {
427  /* otherwise search for another good text */
428  struct _BestText bts;
429  bts.text = NULL;
430  bts.sort = sort;
431 
432  g_hash_table_foreach (qf->matches, (GHFunc) best_text_helper, &bts);
433  best_text = bts.text;
434  best_len = (best_text == NULL) ? 0 : g_utf8_strlen (best_text, -1);
435  }
436  }
437 
438  /* now replace or clear text */
439  CACHE_REMOVE(qf->text);
440  if (best_text != NULL)
441  {
442  qf->text = CACHE_INSERT((gpointer) best_text);
443  qf->len = best_len;
444  }
445  else
446  {
447  qf->text = NULL;
448  qf->len = 0;
449  }
450  }
451 }
452 
453 /********************** END OF FILE ********************************* \
454 \********************************************************************/
void gnc_quickfill_insert(QuickFill *qf, const char *text, QuickFillSort sort)
Definition: QuickFill.c:229
QuickFill * gnc_quickfill_get_char_match(QuickFill *qf, gunichar uc)
Definition: QuickFill.c:135
utility functions for the GnuCash UI
#define DEBUG(format, args...)
Definition: qoflog.h:255
#define PWARN(format, args...)
Definition: qoflog.h:243
QuickFill * gnc_quickfill_get_string_len_match(QuickFill *qf, const char *str, int len)
Definition: QuickFill.c:150
QuickFill * gnc_quickfill_get_string_match(QuickFill *qf, const char *str)
Definition: QuickFill.c:179
All type declarations for the whole Gnucash engine.
const char * gnc_quickfill_string(QuickFill *qf)
Definition: QuickFill.c:123
QuickFill is used to auto-complete typed user entries.
QuickFill * gnc_quickfill_get_unique_len_match(QuickFill *qf, int *length)
Definition: QuickFill.c:199
const gchar * QofLogModule
Definition: qofid.h:89