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
gnc_quickfill_cell_set_value_internal(BasicCell * _cell,const char * val)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
gnc_quickfill_cell_enter(BasicCell * _cell,int * cursor_position,int * start_selection,int * end_selection)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
utf8_caseequal(const char * s1,const char * s2)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
utf8_caseequal_len(const char * s1,const char * s2,guint len)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
gnc_quickfill_cell_modify_verify(BasicCell * _cell,const char * change,int change_len,const char * newval,int newval_len,int * cursor_position,int * start_selection,int * end_selection)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 glong change_chars;
172
173 newval_chars = g_utf8_strlen(newval, newval_len);
174 change_chars = g_utf8_strlen(change, change_len);
175
176 /* If deleting, just accept */
177 if (change == NULL)
178 {
179 /* if the new value is a prefix of the original modulo case,
180 * just truncate the end of the original. Otherwise, set it
181 * to NULL */
182 if ((*cursor_position >= newval_chars) &&
183 (cell->original != NULL) &&
184 (g_utf8_strlen (cell->original, -1) >= newval_chars) &&
185 utf8_caseequal_len (cell->original, newval, newval_chars))
186 {
187 gchar *temp = g_strndup (cell->original, newval_len);
188 gnc_quickfill_cell_set_original (cell, temp);
189 g_free (temp);
190 }
191 else
192 gnc_quickfill_cell_set_original (cell, NULL);
193
194 gnc_basic_cell_set_value_internal (&cell->cell, newval);
195 // Remove any selection.
196 *end_selection = *start_selection = *cursor_position;
197 return;
198 }
199
200 /* If we are inserting in the middle, just accept */
201 if (*cursor_position < newval_chars)
202 {
203 gnc_basic_cell_set_value_internal (&cell->cell, newval);
204 gnc_quickfill_cell_set_original (cell, NULL);
205 return;
206 }
207
208 if (cell->original == NULL)
209 cell->original = g_strdup (newval);
210 else if (utf8_caseequal (cell->original, _cell->value))
211 {
212 GString *original;
213
214 original = g_string_new (cell->original);
215 g_string_append (original, change);
216
217 g_free (cell->original);
218 cell->original = g_strdup (original->str);
219 g_string_free (original, TRUE);
220 }
221 else
222 {
223 g_free (cell->original);
224 cell->original = NULL;
225 }
226
227 match = gnc_quickfill_get_string_match (cell->qf, newval);
228
229 match_str = gnc_quickfill_string (match);
230
231 if (match_str == NULL)
232 {
233 if (cell->original != NULL)
234 newval = cell->original;
235
236 gnc_basic_cell_set_value_internal (&cell->cell, newval);
237 return;
238 }
239
240 *start_selection = newval_chars;
241 *end_selection = -1;
242 *cursor_position += change_chars;
243
244 gnc_basic_cell_set_value_internal (&cell->cell, match_str);
245 }
246
247 /* when leaving cell, make sure that text was put into the qf */
248
249 static void
gnc_quickfill_cell_leave(BasicCell * _cell)250 gnc_quickfill_cell_leave (BasicCell * _cell)
251 {
252 QuickFillCell *cell = (QuickFillCell *) _cell;
253
254 gnc_quickfill_insert (cell->qf, _cell->value, cell->sort);
255 }
256
257 static void
gnc_quickfill_cell_destroy(BasicCell * bcell)258 gnc_quickfill_cell_destroy (BasicCell *bcell)
259 {
260 QuickFillCell *cell = (QuickFillCell *) bcell;
261
262 if (!cell->use_quickfill_cache)
263 {
264 gnc_quickfill_destroy (cell->qf);
265 }
266 cell->qf = NULL;
267
268 g_free (cell->original);
269 cell->original = NULL;
270
271 cell->cell.enter_cell = NULL;
272 cell->cell.modify_verify = NULL;
273 cell->cell.leave_cell = NULL;
274 cell->cell.set_value = NULL;
275 }
276
277 static void
gnc_quickfill_cell_init(QuickFillCell * cell)278 gnc_quickfill_cell_init (QuickFillCell *cell)
279 {
280 gnc_basic_cell_init (&(cell->cell));
281
282 cell->qf = gnc_quickfill_new ();
283 cell->use_quickfill_cache = FALSE;
284 cell->sort = QUICKFILL_LIFO;
285 cell->original = NULL;
286
287 cell->cell.destroy = gnc_quickfill_cell_destroy;
288
289 cell->cell.enter_cell = gnc_quickfill_cell_enter;
290 cell->cell.modify_verify = gnc_quickfill_cell_modify_verify;
291 cell->cell.leave_cell = gnc_quickfill_cell_leave;
292 cell->cell.set_value = gnc_quickfill_cell_set_value_internal;
293 }
294
295 BasicCell *
gnc_quickfill_cell_new(void)296 gnc_quickfill_cell_new (void)
297 {
298 QuickFillCell *cell;
299
300 cell = g_new0 (QuickFillCell, 1);
301
302 gnc_quickfill_cell_init (cell);
303
304 return &cell->cell;
305 }
306
307 void
gnc_quickfill_cell_set_value(QuickFillCell * cell,const char * value)308 gnc_quickfill_cell_set_value (QuickFillCell *cell, const char * value)
309 {
310 if (cell == NULL)
311 return;
312
313 gnc_basic_cell_set_value_internal (&cell->cell, value);
314 gnc_quickfill_insert (cell->qf, value, cell->sort);
315 }
316
317 void
gnc_quickfill_cell_set_sort(QuickFillCell * cell,QuickFillSort sort)318 gnc_quickfill_cell_set_sort (QuickFillCell *cell, QuickFillSort sort)
319 {
320 if (cell == NULL)
321 return;
322
323 cell->sort = sort;
324 }
325
326 static void
gnc_quickfill_cell_set_original(QuickFillCell * cell,const char * original)327 gnc_quickfill_cell_set_original (QuickFillCell *cell, const char *original)
328 {
329 if (cell == NULL)
330 return;
331
332 g_free (cell->original);
333
334 if ((original != NULL) && (*original != 0))
335 cell->original = strdup (original);
336 else
337 cell->original = NULL;
338 }
339
340 void
gnc_quickfill_cell_add_completion(QuickFillCell * cell,const char * completion)341 gnc_quickfill_cell_add_completion (QuickFillCell *cell, const char *completion)
342 {
343 if (cell == NULL)
344 return;
345
346 gnc_quickfill_insert (cell->qf, completion, cell->sort);
347 }
348
349 void
gnc_quickfill_cell_use_quickfill_cache(QuickFillCell * cell,QuickFill * shared_qf)350 gnc_quickfill_cell_use_quickfill_cache (QuickFillCell *cell, QuickFill *shared_qf)
351 {
352 g_assert(cell);
353 g_assert(shared_qf);
354
355 if (!cell->use_quickfill_cache)
356 {
357 cell->use_quickfill_cache = TRUE;
358 gnc_quickfill_destroy (cell->qf);
359 }
360 cell->qf = shared_qf;
361 }
362