1 /********************************************************************\
2  * basiccell.c -- base class for editable cell in a table           *
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  * basiccell.c
26  *
27  * FUNCTION:
28  * Implements the base class for the cell handler object.
29  * See the header file for additional documentation.
30  *
31  * HISTORY:
32  * Copyright (c) 1998 Linas Vepstas
33  * Copyright (c) 2000 Dave Peticolas <dave@krondo.com>
34  */
35 
36 #include <config.h>
37 
38 #include <stdlib.h>
39 #include <locale.h>
40 #include <string.h>
41 
42 #include "gnc-locale-utils.h"
43 
44 #include "basiccell.h"
45 #include "gnc-engine.h"
46 
47 /* Debugging module */
48 static QofLogModule log_module = GNC_MOD_REGISTER;
49 
50 gboolean
gnc_cell_name_equal(const char * cell_name_1,const char * cell_name_2)51 gnc_cell_name_equal (const char * cell_name_1,
52                      const char * cell_name_2)
53 {
54     return (g_strcmp0 (cell_name_1, cell_name_2) == 0);
55 }
56 
57 BasicCell *
gnc_basic_cell_new(void)58 gnc_basic_cell_new (void)
59 {
60     BasicCell * cell;
61 
62     cell = g_new0 (BasicCell, 1);
63 
64     gnc_basic_cell_init (cell);
65 
66     return cell;
67 }
68 
69 static void
gnc_basic_cell_clear(BasicCell * cell)70 gnc_basic_cell_clear (BasicCell *cell)
71 {
72     g_free (cell->cell_name);
73     cell->cell_name = NULL;
74     g_free (cell->cell_type_name);
75     cell->cell_type_name = NULL;
76     cell->changed = FALSE;
77     cell->conditionally_changed = FALSE;
78 
79     cell->value = NULL;
80     cell->value_chars = 0;
81 
82     cell->set_value = NULL;
83     cell->enter_cell = NULL;
84     cell->modify_verify = NULL;
85     cell->direct_update = NULL;
86     cell->leave_cell = NULL;
87     cell->gui_realize = NULL;
88     cell->gui_move = NULL;
89     cell->gui_destroy = NULL;
90 
91     cell->is_popup = FALSE;
92 
93     cell->gui_private = NULL;
94 
95     g_free (cell->sample_text);
96     cell->sample_text = NULL;
97 }
98 
99 void
gnc_basic_cell_init(BasicCell * cell)100 gnc_basic_cell_init (BasicCell *cell)
101 {
102     gnc_basic_cell_clear (cell);
103 
104     cell->value = g_strdup ("");
105 }
106 
107 void
gnc_basic_cell_destroy(BasicCell * cell)108 gnc_basic_cell_destroy (BasicCell *cell)
109 {
110     ENTER(" ");
111     if (cell->destroy)
112         cell->destroy (cell);
113 
114     /* give any gui elements a chance to clean up */
115     if (cell->gui_destroy)
116         (*(cell->gui_destroy)) (cell);
117 
118     /* free up data strings */
119     g_free (cell->value);
120     cell->value = NULL;
121 
122     /* help prevent access to freed memory */
123     gnc_basic_cell_clear (cell);
124 
125     /* free the object itself */
126     g_free (cell);
127     LEAVE(" ");
128 }
129 
130 void
gnc_basic_cell_set_name(BasicCell * cell,const char * name)131 gnc_basic_cell_set_name (BasicCell *cell, const char *name)
132 {
133     if (!cell) return;
134     if (cell->cell_name == name) return;
135 
136     g_free (cell->cell_name);
137     cell->cell_name = g_strdup (name);
138 }
139 
140 gboolean
gnc_basic_cell_has_name(BasicCell * cell,const char * name)141 gnc_basic_cell_has_name (BasicCell *cell, const char *name)
142 {
143     if (!cell) return FALSE;
144     if (!name) return FALSE;
145     if (!cell->cell_name) return FALSE;
146 
147     return (strcmp (name, cell->cell_name) == 0);
148 }
149 
150 
151 void
gnc_basic_cell_set_type_name(BasicCell * cell,const gchar * type_name)152 gnc_basic_cell_set_type_name (BasicCell *cell, const gchar *type_name)
153 {
154     if (!cell) return;
155     if (cell->cell_type_name == type_name) return;
156 
157     g_free (cell->cell_type_name);
158     cell->cell_type_name = g_strdup(type_name);
159 }
160 
161 gboolean
gnc_basic_cell_has_type_name(BasicCell * cell,const gchar * type_name)162 gnc_basic_cell_has_type_name (BasicCell *cell, const gchar *type_name)
163 {
164     if (!cell) return FALSE;
165     if (!type_name) return FALSE;
166     if (!cell->cell_type_name) return FALSE;
167 
168     return (g_strcmp0 (type_name, cell->cell_type_name));
169 }
170 
171 void
gnc_basic_cell_set_sample_text(BasicCell * cell,const char * sample_text)172 gnc_basic_cell_set_sample_text (BasicCell *cell,
173                                 const char *sample_text)
174 {
175     if (!cell) return;
176     if (cell->sample_text == sample_text) return;
177 
178     g_free (cell->sample_text);
179     cell->sample_text = g_strdup (sample_text);
180 }
181 
182 void
gnc_basic_cell_set_alignment(BasicCell * cell,CellAlignment alignment)183 gnc_basic_cell_set_alignment (BasicCell *cell,
184                               CellAlignment alignment)
185 {
186     if (!cell) return;
187     cell->alignment = alignment;
188 }
189 
190 void
gnc_basic_cell_set_expandable(BasicCell * cell,gboolean expandable)191 gnc_basic_cell_set_expandable (BasicCell *cell, gboolean expandable)
192 {
193     if (!cell) return;
194     cell->expandable = expandable;
195 }
196 
197 void
gnc_basic_cell_set_span(BasicCell * cell,gboolean span)198 gnc_basic_cell_set_span (BasicCell *cell, gboolean span)
199 {
200     if (!cell) return;
201     cell->span = span;
202 }
203 
204 const char *
gnc_basic_cell_get_value(BasicCell * cell)205 gnc_basic_cell_get_value (BasicCell *cell)
206 {
207     g_return_val_if_fail (cell != NULL, NULL);
208 
209     return cell->value;
210 }
211 
212 void
gnc_basic_cell_set_value(BasicCell * cell,const char * val)213 gnc_basic_cell_set_value (BasicCell *cell, const char *val)
214 {
215     CellSetValueFunc cb;
216 
217     cb = cell->set_value;
218     if (cb)
219     {
220         /* avoid recursion by disabling the
221          * callback while it's being called. */
222         cell->set_value = NULL;
223         cb (cell, val);
224         cell->set_value = cb;
225     }
226     else
227         gnc_basic_cell_set_value_internal (cell, val);
228 }
229 
230 gboolean
gnc_basic_cell_get_changed(BasicCell * cell)231 gnc_basic_cell_get_changed (BasicCell *cell)
232 {
233     if (!cell) return FALSE;
234 
235     return cell->changed;
236 }
237 
238 gboolean
gnc_basic_cell_get_conditionally_changed(BasicCell * cell)239 gnc_basic_cell_get_conditionally_changed (BasicCell *cell)
240 {
241     if (!cell) return FALSE;
242 
243     return cell->conditionally_changed;
244 }
245 
246 void
gnc_basic_cell_set_changed(BasicCell * cell,gboolean changed)247 gnc_basic_cell_set_changed (BasicCell *cell, gboolean changed)
248 {
249     if (!cell) return;
250 
251     cell->changed = changed;
252 }
253 
254 void
gnc_basic_cell_set_conditionally_changed(BasicCell * cell,gboolean changed)255 gnc_basic_cell_set_conditionally_changed (BasicCell *cell, gboolean changed)
256 {
257     if (!cell) return;
258 
259     cell->conditionally_changed = changed;
260 }
261 
262 void
gnc_basic_cell_set_value_internal(BasicCell * cell,const char * value)263 gnc_basic_cell_set_value_internal (BasicCell *cell, const char *value)
264 {
265     if (value == NULL)
266         value = "";
267 
268     /* If the caller tries to set the value with our own value then do
269      * nothing because we have no work to do (or, at least, all the work
270      * will result in the status-quo, so why do anything?)  See bug
271      * #103174 and the description in the changelog on 2003-09-04.
272      */
273     if (cell->value == value)
274         return;
275 
276     g_free (cell->value);
277     cell->value = g_strdup (value);
278     cell->value_chars = g_utf8_strlen(value, -1);
279 }
280 
281 char *
gnc_basic_cell_validate(BasicCell * cell,GNCPrintAmountInfo print_info,const char * change,const char * newval,const char * toks,gint * cursor_position)282 gnc_basic_cell_validate (BasicCell *cell, GNCPrintAmountInfo print_info,
283                          const char *change, const char *newval,
284                          const char *toks, gint *cursor_position)
285 {
286     struct lconv *lc = gnc_localeconv ();
287     gunichar decimal_point;
288     gunichar thousands_sep;
289     const char *symbol = NULL;
290     char *tokens;
291 
292     if (print_info.monetary)
293     {
294         const gnc_commodity *comm = print_info.commodity;
295 
296         decimal_point = g_utf8_get_char (lc->mon_decimal_point);
297         thousands_sep = g_utf8_get_char (lc->mon_thousands_sep);
298 
299         if (comm)
300             symbol = gnc_commodity_get_nice_symbol (comm);
301         else
302             symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
303 
304         tokens = g_strconcat (toks, symbol, NULL);
305     }
306     else
307     {
308         decimal_point = g_utf8_get_char (lc->decimal_point);
309         thousands_sep = g_utf8_get_char (lc->thousands_sep);
310 
311         tokens = g_strdup (toks);
312     }
313 
314     for (const char *c = change; c && *c; c = g_utf8_next_char (c))
315     {
316         gunichar uc = g_utf8_get_char (c);
317         if (!g_unichar_isdigit (uc) &&
318             !g_unichar_isspace (uc) &&
319             !g_unichar_isalpha (uc) &&
320             (decimal_point != uc) &&
321             (thousands_sep != uc) &&
322             (g_utf8_strchr (tokens, -1, uc) == NULL))
323         {
324             g_free (tokens);
325             return NULL;
326         }
327     }
328     g_free (tokens);
329 
330     gnc_filter_text_set_cursor_position (newval, symbol, cursor_position);
331 
332     return gnc_filter_text_for_currency_symbol (newval, symbol);
333 }
334