1 /********************************************************************\
2  * pricecell.c -- price input/display cell                          *
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  * pricecell.c
26  *
27  * FUNCTION:
28  * Implements the price cell
29  *
30  * HISTORY:
31  * Copyright (c) 1998, 1999, 2000 Linas Vepstas
32  * Copyright (c) 2000 Dave Peticolas
33  */
34 
35 #include <config.h>
36 
37 #include <glib.h>
38 #include <glib/gi18n.h>
39 #include <string.h>
40 
41 #include "gnc-exp-parser.h"
42 #include "gnc-engine.h"
43 #include "gnc-ui-util.h"
44 #include "gnc-ui.h"
45 
46 #include "basiccell.h"
47 #include "pricecell.h"
48 #include <qoflog.h>
49 
50 static const QofLogModule log_module = G_LOG_DOMAIN;
51 
52 static void gnc_price_cell_init (PriceCell *cell);
53 static void gnc_price_cell_set_value_internal (BasicCell *bcell,
54         const char *value);
55 static const char * gnc_price_cell_print_value (PriceCell *cell);
56 
57 
58 static gboolean
gnc_price_cell_enter(BasicCell * _cell,int * cursor_position,int * start_selection,int * end_selection)59 gnc_price_cell_enter (BasicCell *_cell,
60                       int *cursor_position,
61                       int *start_selection,
62                       int *end_selection)
63 {
64     *cursor_position = -1;
65     *start_selection = 0;
66     *end_selection   = -1;
67 
68     return TRUE;
69 }
70 
71 static void
gnc_price_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)72 gnc_price_cell_modify_verify (BasicCell *_cell,
73                               const char *change,
74                               int change_len,
75                               const char *newval,
76                               int newval_len,
77                               int *cursor_position,
78                               int *start_selection,
79                               int *end_selection)
80 {
81     PriceCell *cell = (PriceCell *) _cell;
82     const char *toks = "+-*/=()_";
83     char *validated_newval = NULL;
84 
85     DEBUG("%s, %d, %s, %d, %d, %d, %d",
86             change ? (gchar *)change : "(null)", change_len,
87             newval ? (gchar *)newval : "(null)", newval_len,
88             *cursor_position, *start_selection, *end_selection);
89 
90     validated_newval = gnc_basic_cell_validate (_cell, cell->print_info,
91                                                 change, newval, toks,
92                                                 cursor_position);
93 
94     if (!validated_newval)
95         return;
96 
97     gnc_basic_cell_set_value_internal (_cell, validated_newval);
98     g_free (validated_newval);
99 
100     *end_selection = *start_selection = *cursor_position;
101     cell->need_to_parse = TRUE;
102 }
103 
104 static gint
gnc_price_cell_parse(PriceCell * cell,gboolean update_value)105 gnc_price_cell_parse (PriceCell *cell, gboolean update_value)
106 {
107     const char *newval;
108     char *oldval;
109     gnc_numeric amount;
110 
111     if (!cell->need_to_parse)
112         return -1;
113 
114     oldval = cell->cell.value;
115     if (oldval == NULL)
116         oldval = "";
117 
118     {
119         char *err_location = NULL;
120         if (strlen(g_strstrip(cell->cell.value)) == 0)
121         {
122             cell->amount = gnc_numeric_zero ();
123         }
124         else if (gnc_exp_parser_parse (cell->cell.value, &amount, &err_location))
125         {
126             if (cell->fraction > 0)
127                 amount = gnc_numeric_convert (amount, cell->fraction, GNC_HOW_RND_ROUND_HALF_UP);
128 
129             cell->amount = amount;
130         }
131         else
132         {
133             return (err_location - cell->cell.value);
134         }
135     }
136 
137     if (!update_value)
138         return -1;
139 
140     newval = gnc_price_cell_print_value (cell);
141 
142     /* If they are identical do nothing */
143     if (strcmp(newval, oldval) == 0)
144         return -1;
145 
146     /* Otherwise, change it */
147     gnc_basic_cell_set_value_internal (&cell->cell, newval);
148     return -1;
149 }
150 
151 static void
gnc_price_cell_leave(BasicCell * _cell)152 gnc_price_cell_leave (BasicCell *_cell)
153 {
154     gint error_position = -1;
155     PriceCell *cell = (PriceCell *) _cell;
156 
157     error_position = gnc_price_cell_parse (cell, TRUE);
158     if (error_position != -1)
159     {
160         gnc_warning_dialog (gnc_ui_get_main_window (NULL),
161                             _("An error occurred while processing '%s' at position %d"),
162                             cell->cell.value, error_position);
163     }
164 
165 }
166 
167 BasicCell *
gnc_price_cell_new(void)168 gnc_price_cell_new (void)
169 {
170     PriceCell *cell;
171 
172     cell = g_new0 (PriceCell, 1);
173 
174     gnc_price_cell_init (cell);
175 
176     return &cell->cell;
177 }
178 
179 void
gnc_price_cell_init(PriceCell * cell)180 gnc_price_cell_init (PriceCell *cell)
181 {
182     gnc_basic_cell_init (&(cell->cell));
183 
184     cell->amount = gnc_numeric_zero ();
185     cell->fraction = 0;
186     cell->blank_zero = TRUE;
187 
188     cell->print_info = gnc_default_print_info (FALSE);
189 
190     cell->need_to_parse = FALSE;
191 
192     cell->cell.enter_cell = gnc_price_cell_enter;
193     cell->cell.modify_verify = gnc_price_cell_modify_verify;
194     cell->cell.leave_cell = gnc_price_cell_leave;
195     cell->cell.set_value = gnc_price_cell_set_value_internal;
196 }
197 
198 static const char *
gnc_price_cell_print_value(PriceCell * cell)199 gnc_price_cell_print_value (PriceCell *cell)
200 {
201     if (cell->blank_zero && gnc_numeric_zero_p (cell->amount))
202         return "";
203 
204     return xaccPrintAmount (cell->amount, cell->print_info);
205 }
206 
207 gnc_numeric
gnc_price_cell_get_value(PriceCell * cell)208 gnc_price_cell_get_value (PriceCell *cell)
209 {
210     if (cell == NULL)
211         return gnc_numeric_zero ();
212 
213     gnc_price_cell_parse (cell, FALSE);
214 
215     return cell->amount;
216 }
217 
218 gboolean
gnc_price_cell_set_value(PriceCell * cell,gnc_numeric amount)219 gnc_price_cell_set_value (PriceCell * cell, gnc_numeric amount)
220 {
221     const char *buff;
222 
223     if (cell == NULL)
224         return FALSE;
225 
226     if (cell->fraction > 0)
227         amount = gnc_numeric_convert (amount, cell->fraction, GNC_HOW_RND_ROUND_HALF_UP);
228 
229     cell->amount = amount;
230     buff = gnc_price_cell_print_value (cell);
231     cell->need_to_parse = FALSE;
232 
233     if (g_strcmp0 (buff, cell->cell.value) == 0)
234         return FALSE;
235 
236     gnc_basic_cell_set_value_internal (&cell->cell, buff);
237 
238     return TRUE;
239 }
240 
241 void
gnc_price_cell_set_fraction(PriceCell * cell,int fraction)242 gnc_price_cell_set_fraction (PriceCell *cell, int fraction)
243 {
244     if (cell == NULL)
245         return;
246 
247     cell->fraction = ABS (fraction);
248 }
249 
250 void
gnc_price_cell_blank(PriceCell * cell)251 gnc_price_cell_blank (PriceCell *cell)
252 {
253     if (cell == NULL)
254         return;
255 
256     cell->amount = gnc_numeric_zero ();
257     cell->need_to_parse = FALSE;
258 
259     gnc_basic_cell_set_value_internal (&cell->cell, "");
260 }
261 
262 void
gnc_price_cell_set_blank_zero(PriceCell * cell,gboolean blank_zero)263 gnc_price_cell_set_blank_zero (PriceCell *cell, gboolean blank_zero)
264 {
265     if (cell == NULL)
266         return;
267 
268     cell->blank_zero = blank_zero;
269 }
270 
271 void
gnc_price_cell_set_print_info(PriceCell * cell,GNCPrintAmountInfo print_info)272 gnc_price_cell_set_print_info (PriceCell *cell, GNCPrintAmountInfo print_info)
273 {
274     if (cell == NULL)
275         return;
276 
277     cell->print_info = print_info;
278 }
279 
280 void
gnc_price_cell_set_debt_credit_value(PriceCell * debit,PriceCell * credit,gnc_numeric amount)281 gnc_price_cell_set_debt_credit_value (PriceCell * debit,
282                                       PriceCell * credit,
283                                       gnc_numeric amount)
284 {
285     /* debits are positive, credits are negative */
286     if (gnc_numeric_positive_p (amount))
287     {
288         gnc_price_cell_set_value (debit, amount);
289         gnc_price_cell_set_value (credit, gnc_numeric_zero ());
290     }
291     else
292     {
293         gnc_price_cell_set_value (debit, gnc_numeric_zero ());
294         gnc_price_cell_set_value (credit, gnc_numeric_neg (amount));
295     }
296 }
297 
298 static void
gnc_price_cell_set_value_internal(BasicCell * _cell,const char * str)299 gnc_price_cell_set_value_internal (BasicCell *_cell, const char *str)
300 {
301     PriceCell *cell = (PriceCell *) _cell;
302     gnc_numeric amount;
303 
304     if (str == NULL)
305         str = "";
306 
307     if (*str == '\0')
308         gnc_price_cell_set_value (cell, gnc_numeric_zero ());
309     else if (gnc_exp_parser_parse (str, &amount, NULL))
310         gnc_price_cell_set_value (cell, amount);
311 }
312