GnuCash  5.6-150-g038405b370+
quickfillcell.c
1 /********************************************************************\
2  * quickfillcell.c -- autocompletion based on memorized history *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA gnu@gnu.org *
20  * *
21 \********************************************************************/
22 
23 /*
24  * FILE:
25  * quickfillcell.c
26  *
27  * FUNCTION:
28  * Implements a text cell with automatic typed-phrase
29  * completion.
30  *
31  * HISTORY:
32  * Copyright (c) 1998-2000 Linas Vepstas
33  * Copyright (c) 2000 Dave Peticolas
34  */
35 
36 #include <config.h>
37 
38 #include <glib.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include "basiccell.h"
44 #include "gnc-ui-util.h"
45 #include "quickfillcell.h"
46 
47 
48 static void gnc_quickfill_cell_set_original (QuickFillCell *cell,
49  const char *original);
50 
51 
52 static void
53 gnc_quickfill_cell_set_value_internal (BasicCell *_cell,
54  const char *val)
55 {
56  QuickFillCell *cell = (QuickFillCell *) _cell;
57  gnc_quickfill_cell_set_value (cell, val);
58 }
59 
60 /* when entering new cell, put cursor at end and select everything */
61 static gboolean
62 gnc_quickfill_cell_enter (BasicCell *_cell,
63  int *cursor_position,
64  int *start_selection,
65  int *end_selection)
66 {
67  QuickFillCell *cell = (QuickFillCell *) _cell;
68 
69  *cursor_position = -1;
70  *start_selection = 0;
71  *end_selection = -1;
72 
73  gnc_quickfill_cell_set_original (cell, NULL);
74 
75  return TRUE;
76 }
77 
78 static gboolean
79 utf8_caseequal (const char *s1, const char *s2)
80 {
81  char *s1new;
82  char *s2new;
83  gboolean equal = FALSE;
84 
85  if (s1 == s2)
86  return TRUE;
87 
88  if (!s1 || !s2)
89  return FALSE;
90 
91  s1new = g_utf8_casefold(s1, -1);
92  s2new = g_utf8_casefold(s2, -1);
93 
94  if (g_utf8_collate(s1new, s2new) == 0)
95  equal = TRUE;
96 
97  g_free (s1new);
98  g_free (s2new);
99 
100  return equal;
101 }
102 
103 static gboolean
104 utf8_caseequal_len (const char *s1, const char *s2, guint len)
105 {
106  gchar *s1new;
107  gchar *s2new;
108  const gchar *s1_offset;
109  const gchar *s2_offset;
110  glong s1chars;
111  glong s2chars;
112  glong s1_bytes_len;
113  glong s2_bytes_len;
114  gboolean equal = FALSE;
115 
116  if (len == 0)
117  return TRUE;
118 
119  if (s1 == s2)
120  return TRUE;
121 
122  if (!s1 || !s2)
123  return FALSE;
124 
125  /* Obtain the number of bytes for the given number of characters */
126  s1_offset = g_utf8_offset_to_pointer (s1, len);
127  s2_offset = g_utf8_offset_to_pointer (s2, len);
128  s1_bytes_len = s1_offset - s1;
129  s2_bytes_len = s2_offset - s2;
130 
131  /* Test whether the number of characters might be too small anyway
132  (don't need to examine more than bytes_len bytes to check that) */
133  s1chars = g_utf8_strlen (s1, s1_bytes_len);
134  s2chars = g_utf8_strlen (s2, s2_bytes_len);
135  if ( (s1chars < len) || (s2chars < len) )
136  return FALSE;
137 
138  /* Allocate new strings that are case-independent. */
139  s1new = g_utf8_casefold (s1, s1_bytes_len);
140  s2new = g_utf8_casefold (s2, s2_bytes_len);
141 
142  /* equal = utf8_caseequal (s1new, s2new); */
143  /* ^^ don't call this to save one string allocation; we used
144  g_utf8_casefold here already. */
145 
146  /* Now really compare the two strings */
147  if (g_utf8_collate(s1new, s2new) == 0)
148  equal = TRUE;
149 
150  g_free (s1new);
151  g_free (s2new);
152 
153  return equal;
154 }
155 
156 static void
157 gnc_quickfill_cell_modify_verify (BasicCell *_cell,
158  const char *change,
159  int change_len,
160  const char *newval,
161  int newval_len,
162  int *cursor_position,
163  int *start_selection,
164  int *end_selection)
165 {
166  QuickFillCell *cell = (QuickFillCell *) _cell;
167  const char *match_str;
168  QuickFill *match;
169 
170  glong newval_chars;
171  newval_chars = g_utf8_strlen(newval, newval_len);
172 
173  /* If deleting, just accept */
174  if (change == NULL)
175  {
176  /* if the new value is a prefix of the original modulo case,
177  * just truncate the end of the original. Otherwise, set it
178  * to NULL */
179  if ((*cursor_position >= newval_chars) &&
180  (cell->original != NULL) &&
181  (g_utf8_strlen (cell->original, -1) >= newval_chars) &&
182  utf8_caseequal_len (cell->original, newval, newval_chars))
183  {
184  gchar *temp = g_strndup (cell->original, newval_len);
185  gnc_quickfill_cell_set_original (cell, temp);
186  g_free (temp);
187  }
188  else
189  gnc_quickfill_cell_set_original (cell, NULL);
190 
191  gnc_basic_cell_set_value_internal (&cell->cell, newval);
192  // Remove any selection.
193  *end_selection = *start_selection = *cursor_position;
194  return;
195  }
196 
197  /* If we are inserting in the middle, just accept */
198  if (*cursor_position < newval_chars)
199  {
200  gnc_basic_cell_set_value_internal (&cell->cell, newval);
201  gnc_quickfill_cell_set_original (cell, NULL);
202  return;
203  }
204 
205  if (cell->original == NULL)
206  cell->original = g_strdup (newval);
207  else if (utf8_caseequal (cell->original, _cell->value))
208  {
209  GString *original;
210 
211  original = g_string_new (cell->original);
212  g_string_append (original, change);
213 
214  g_free (cell->original);
215  cell->original = g_strdup (original->str);
216  g_string_free (original, TRUE);
217  }
218  else
219  {
220  g_free (cell->original);
221  cell->original = NULL;
222  }
223 
224  match = gnc_quickfill_get_string_match (cell->qf, newval);
225 
226  match_str = gnc_quickfill_string (match);
227 
228  if (match_str == NULL)
229  {
230  if (cell->original != NULL)
231  newval = cell->original;
232 
233  gnc_basic_cell_set_value_internal (&cell->cell, newval);
234  return;
235  }
236 
237  *start_selection = newval_chars;
238  *end_selection = -1;
239 
240  gnc_basic_cell_set_value_internal (&cell->cell, match_str);
241 }
242 
243 /* when leaving cell, make sure that text was put into the qf */
244 
245 static void
246 gnc_quickfill_cell_leave (BasicCell * _cell)
247 {
248  QuickFillCell *cell = (QuickFillCell *) _cell;
249 
250  gnc_quickfill_insert (cell->qf, _cell->value, cell->sort);
251 }
252 
253 static void
254 gnc_quickfill_cell_destroy (BasicCell *bcell)
255 {
256  QuickFillCell *cell = (QuickFillCell *) bcell;
257 
258  if (!cell->use_quickfill_cache)
259  {
260  gnc_quickfill_destroy (cell->qf);
261  }
262  cell->qf = NULL;
263 
264  g_free (cell->original);
265  cell->original = NULL;
266 
267  cell->cell.enter_cell = NULL;
268  cell->cell.modify_verify = NULL;
269  cell->cell.leave_cell = NULL;
270  cell->cell.set_value = NULL;
271 }
272 
273 static void
274 gnc_quickfill_cell_init (QuickFillCell *cell)
275 {
276  gnc_basic_cell_init (&(cell->cell));
277 
278  cell->qf = gnc_quickfill_new ();
279  cell->use_quickfill_cache = FALSE;
280  cell->sort = QUICKFILL_LIFO;
281  cell->original = NULL;
282 
283  cell->cell.destroy = gnc_quickfill_cell_destroy;
284 
285  cell->cell.enter_cell = gnc_quickfill_cell_enter;
286  cell->cell.modify_verify = gnc_quickfill_cell_modify_verify;
287  cell->cell.leave_cell = gnc_quickfill_cell_leave;
288  cell->cell.set_value = gnc_quickfill_cell_set_value_internal;
289 }
290 
291 BasicCell *
292 gnc_quickfill_cell_new (void)
293 {
294  QuickFillCell *cell;
295 
296  cell = g_new0 (QuickFillCell, 1);
297 
298  gnc_quickfill_cell_init (cell);
299 
300  return &cell->cell;
301 }
302 
303 void
304 gnc_quickfill_cell_set_value (QuickFillCell *cell, const char * value)
305 {
306  if (cell == NULL)
307  return;
308 
309  gnc_basic_cell_set_value_internal (&cell->cell, value);
310  gnc_quickfill_insert (cell->qf, value, cell->sort);
311 }
312 
313 void
314 gnc_quickfill_cell_set_sort (QuickFillCell *cell, QuickFillSort sort)
315 {
316  if (cell == NULL)
317  return;
318 
319  cell->sort = sort;
320 }
321 
322 static void
323 gnc_quickfill_cell_set_original (QuickFillCell *cell, const char *original)
324 {
325  if (cell == NULL)
326  return;
327 
328  g_free (cell->original);
329 
330  if ((original != NULL) && (*original != 0))
331  cell->original = strdup (original);
332  else
333  cell->original = NULL;
334 }
335 
336 void
337 gnc_quickfill_cell_add_completion (QuickFillCell *cell, const char *completion)
338 {
339  if (cell == NULL)
340  return;
341 
342  gnc_quickfill_insert (cell->qf, completion, cell->sort);
343 }
344 
345 void
347 {
348  g_assert(cell);
349  g_assert(shared_qf);
350 
351  if (!cell->use_quickfill_cache)
352  {
353  cell->use_quickfill_cache = TRUE;
354  gnc_quickfill_destroy (cell->qf);
355  }
356  cell->qf = shared_qf;
357 }
void gnc_quickfill_insert(QuickFill *qf, const char *text, QuickFillSort sort)
Add the string "text" to the collection of searchable strings.
Definition: QuickFill.c:229
utility functions for the GnuCash UI
The QuickFillCell implements a text cell with quick-fill capabilities.
Definition: quickfillcell.h:46
gboolean use_quickfill_cache
original string entered in original case
Definition: quickfillcell.h:53
void gnc_quickfill_cell_set_value(QuickFillCell *cell, const char *value)
sets the current cell value to the indicated string, simultaneously adding the string to the quick-fi...
QuickFillSort sort
quickfill-tree handled by this cell
Definition: quickfillcell.h:50
void gnc_quickfill_cell_use_quickfill_cache(QuickFillCell *cell, QuickFill *shared_qf)
Lets the cell use the given shared quickfill object instead of the one it owns internally.
QuickFill * gnc_quickfill_get_string_match(QuickFill *qf, const char *str)
Return a subnode in the tree whose strings all match the string &#39;str&#39; as the next substring...
Definition: QuickFill.c:179
char * original
determines order of strings matched.
Definition: quickfillcell.h:52
const char * gnc_quickfill_string(QuickFill *qf)
For the given node &#39;qf&#39;, return the best-guess matching string.
Definition: QuickFill.c:123