GnuCash c935c2f+
Loading...
Searching...
No Matches
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
48static void gnc_quickfill_cell_set_original (QuickFillCell *cell,
49 const char *original);
50
51
52static void
53gnc_quickfill_cell_set_value_internal (BasicCell *_cell,
54 const char *val)
55{
56 QuickFillCell *cell = (QuickFillCell *) _cell;
58}
59
60/* when entering new cell, put cursor at end and select everything */
61static gboolean
62gnc_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
78static gboolean
79utf8_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
103static gboolean
104utf8_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
156static void
157gnc_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
245static void
246gnc_quickfill_cell_leave (BasicCell * _cell)
247{
248 QuickFillCell *cell = (QuickFillCell *) _cell;
249
250 gnc_quickfill_insert (cell->qf, _cell->value, cell->sort);
251}
252
253static void
254gnc_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
273static void
274gnc_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
291BasicCell *
292gnc_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
303void
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
313void
314gnc_quickfill_cell_set_sort (QuickFillCell *cell, QuickFillSort sort)
315{
316 if (cell == NULL)
317 return;
318
319 cell->sort = sort;
320}
321
322static void
323gnc_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
336void
337gnc_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
345void
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}
utility functions for the GnuCash UI
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.
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...
QuickFill * gnc_quickfill_get_string_match(QuickFill *qf, const char *str)
Return a subnode in the tree whose strings all match the string 'str' as the next substring.
Definition QuickFill.c:179
const char * gnc_quickfill_string(QuickFill *qf)
For the given node 'qf', return the best-guess matching string.
Definition QuickFill.c:123
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
The QuickFillCell implements a text cell with quick-fill capabilities.
QuickFillSort sort
quickfill-tree handled by this cell
char * original
determines order of strings matched.
gboolean use_quickfill_cache
original string entered in original case