1 /********************************************************************\
2  * numcell.c -- number handling cell incl. accelerator key support  *
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  * numcell.c
26  *
27  * FUNCTION:
28  * implements a gui-independent number handling cell.
29  *
30  * HISTORY:
31  * Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu>
32  */
33 
34 #include <config.h>
35 
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <string.h>
39 
40 #include "numcell.h"
41 #include "gnc-engine.h"
42 
43 
44 /* This static indicates the debugging module that this .o belongs to. */
45 /* static short module = MOD_REGISTER; */
46 
47 static void gnc_num_cell_init (NumCell *cell);
48 
49 
50 /* Parses the string value and returns true if it is a
51  * number. In that case, *num is set to the value parsed. */
52 static gboolean
gnc_parse_num(const char * string,long int * num)53 gnc_parse_num (const char *string, long int *num)
54 {
55     long int number;
56 
57     if (string == NULL)
58         return FALSE;
59 
60     if (!gnc_strisnum (string))
61         return FALSE;
62 
63     number = strtol (string, NULL, 10);
64 
65     if ((number == LONG_MIN) || (number == LONG_MAX))
66         return FALSE;
67 
68     if (num != NULL)
69         *num = number;
70 
71     return TRUE;
72 }
73 
74 static void
gnc_num_cell_modify_verify(BasicCell * _cell,const char * change,int change_len,const char * newval,int new_val_len,int * cursor_position,int * start_selection,int * end_selection)75 gnc_num_cell_modify_verify (BasicCell *_cell,
76                             const char *change,
77                             int change_len,
78                             const char *newval,
79                             int new_val_len,
80                             int *cursor_position,
81                             int *start_selection,
82                             int *end_selection)
83 {
84     NumCell *cell = (NumCell *) _cell;
85     gboolean accel = FALSE;
86     gboolean is_num;
87     long int number = 0;
88     gunichar uc;
89     glong change_chars;
90 
91     if (change == NULL) /* if we are deleting */
92         /* then just accept the proposed change */
93     {
94         gnc_basic_cell_set_value_internal (&cell->cell, newval);
95         // Remove any selection.
96         *end_selection = *start_selection = *cursor_position;
97         return;
98     }
99 
100     change_chars = g_utf8_strlen (change, -1);
101 
102     if ((change_chars == 0) || /* if we are deleting       */
103             (change_chars > 1))    /* or entering > 1 char     */
104         /* then just accept the proposed change */
105     {
106         gnc_basic_cell_set_value_internal (&cell->cell, newval);
107         return;
108     }
109 
110     /* otherwise, it may be an accelerator key. */
111 
112     is_num = gnc_parse_num (_cell->value, &number);
113 
114     if (is_num && (number < 0))
115         is_num = FALSE;
116 
117     uc = g_utf8_get_char (change);
118     switch (uc)
119     {
120     case '+':
121     case '=':
122         number++;
123         accel = TRUE;
124         break;
125 
126     case '_':
127     case '-':
128         number--;
129         accel = TRUE;
130         break;
131 
132     case '}':
133     case ']':
134         number += 10;
135         accel = TRUE;
136         break;
137 
138     case '{':
139     case '[':
140         number -= 10;
141         accel = TRUE;
142         break;
143     }
144 
145     if (number < 0)
146         number = 0;
147 
148     /* If there is already a non-number there, don't accelerate. */
149     if (accel && !is_num && (g_strcmp0(_cell->value, "") != 0))
150         accel = FALSE;
151 
152     if (accel)
153     {
154         char buff[128];
155 
156         if (!is_num)
157             number = cell->next_num;
158 
159         strcpy (buff, "");
160         snprintf (buff, sizeof(buff), "%ld", number);
161 
162         if (g_strcmp0 (buff, "") == 0)
163             return;
164 
165         gnc_basic_cell_set_value_internal (&cell->cell, buff);
166 
167         *cursor_position = -1;
168 
169         return;
170     }
171 
172     gnc_basic_cell_set_value_internal (&cell->cell, newval);
173 }
174 
175 BasicCell *
gnc_num_cell_new(void)176 gnc_num_cell_new (void)
177 {
178     NumCell *cell;
179 
180     cell = g_new0 (NumCell, 1);
181 
182     gnc_num_cell_init (cell);
183 
184     return &cell->cell;
185 }
186 
187 static void
gnc_num_cell_set_value_internal(BasicCell * _cell,const char * str)188 gnc_num_cell_set_value_internal (BasicCell *_cell, const char *str)
189 {
190     NumCell *cell = (NumCell *) _cell;
191 
192     if (!cell->next_num_set)
193     {
194         long int number;
195 
196         if (gnc_parse_num (str, &number))
197             cell->next_num = number + 1;
198     }
199 
200     gnc_basic_cell_set_value_internal (_cell, str);
201 }
202 
203 void
gnc_num_cell_set_value(NumCell * cell,const char * str)204 gnc_num_cell_set_value (NumCell *cell, const char *str)
205 {
206     if (!cell)
207         return;
208 
209     gnc_num_cell_set_value_internal (&cell->cell, str);
210 }
211 
212 gboolean
gnc_num_cell_set_last_num(NumCell * cell,const char * str)213 gnc_num_cell_set_last_num (NumCell *cell, const char *str)
214 {
215     long int number;
216 
217     if (!cell)
218         return FALSE;
219 
220     if (gnc_parse_num (str, &number))
221     {
222         cell->next_num = number + 1;
223         cell->next_num_set = TRUE;
224         return TRUE;
225     }
226 
227     return FALSE;
228 }
229 
230 static void
gnc_num_cell_init(NumCell * cell)231 gnc_num_cell_init (NumCell *cell)
232 {
233     gnc_basic_cell_init (&(cell->cell));
234 
235     cell->next_num = 0;
236     cell->next_num_set = FALSE;
237 
238     cell->cell.modify_verify = gnc_num_cell_modify_verify;
239     cell->cell.set_value = gnc_num_cell_set_value_internal;
240 }
241