GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 [email protected] *
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  (dont 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  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  return;
196  }
197 
198  /* If we are inserting in the middle, just accept */
199  if (*cursor_position < _cell->value_chars)
200  {
201  gnc_basic_cell_set_value_internal (&cell->cell, newval);
202  gnc_quickfill_cell_set_original (cell, NULL);
203  return;
204  }
205 
206  if (cell->original == NULL)
207  cell->original = g_strdup (newval);
208  else if (utf8_caseequal (cell->original, _cell->value))
209  {
210  GString *original;
211 
212  original = g_string_new (cell->original);
213  g_string_append (original, change);
214 
215  g_free (cell->original);
216  cell->original = g_strdup (original->str);
217  g_string_free (original, TRUE);
218  }
219  else
220  {
221  g_free (cell->original);
222  cell->original = NULL;
223  }
224 
225  match = gnc_quickfill_get_string_match (cell->qf, newval);
226 
227  match_str = gnc_quickfill_string (match);
228 
229  if (match_str == NULL)
230  {
231  if (cell->original != NULL)
232  newval = cell->original;
233 
234  *cursor_position = -1;
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
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
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
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 *
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
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
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
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
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
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 }
void gnc_quickfill_insert(QuickFill *qf, const char *text, QuickFillSort sort)
Definition: QuickFill.c:229
utility functions for the GnuCash UI
QuickFill * gnc_quickfill_get_string_match(QuickFill *qf, const char *str)
Definition: QuickFill.c:179
const char * gnc_quickfill_string(QuickFill *qf)
Definition: QuickFill.c:123