GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-amount-edit.c
1 /*
2  * gnc-amount-edit.c -- Amount editor widget
3  *
4  * Copyright (C) 2000 Dave Peticolas <[email protected]>
5  * All rights reserved.
6  *
7  * Gnucash is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * Gnucash is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, contact:
19  *
20  * Free Software Foundation Voice: +1-617-542-5942
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
22  * Boston, MA 02110-1301, USA [email protected]
23  *
24  */
25 /*
26  @NOTATION@
27  */
28 
29 /*
30  * Amount editor widget
31  *
32  * Authors: Dave Peticolas <[email protected]>
33  */
34 
35 #include "config.h"
36 
37 #include <gtk/gtk.h>
38 #include <gdk/gdkkeysyms.h>
39 
40 #include "gnc-amount-edit.h"
41 #include "gnc-exp-parser.h"
42 #include "gnc-locale-utils.h"
43 #include "gnc-ui-util.h"
44 #include "qof.h"
45 
46 #ifdef G_OS_WIN32
47 # include <gdk/gdkwin32.h>
48 #endif
49 
50 /* Signal codes */
51 enum
52 {
53  AMOUNT_CHANGED,
54  LAST_SIGNAL
55 };
56 
57 
58 static guint amount_edit_signals [LAST_SIGNAL] = { 0 };
59 
60 
61 static void gnc_amount_edit_init (GNCAmountEdit *gae);
62 static void gnc_amount_edit_class_init (GNCAmountEditClass *klass);
63 static void gnc_amount_edit_changed (GtkEditable *gae, gpointer data);
64 static gint gnc_amount_edit_key_press (GtkWidget *widget,
65  GdkEventKey *event);
66 
67 
68 static GtkEntryClass *parent_class;
69 
75 GType
76 gnc_amount_edit_get_type (void)
77 {
78  static GType amount_edit_type = 0;
79 
80  if (amount_edit_type == 0)
81  {
82  GTypeInfo amount_edit_info =
83  {
84  sizeof (GNCAmountEditClass),
85  NULL,
86  NULL,
87  (GClassInitFunc) gnc_amount_edit_class_init,
88  NULL,
89  NULL,
90  sizeof (GNCAmountEdit),
91  0,
92  (GInstanceInitFunc) gnc_amount_edit_init
93  };
94 
95  amount_edit_type = g_type_register_static (GTK_TYPE_ENTRY,
96  "GNCAmountEdit",
97  &amount_edit_info,
98  0);
99  }
100 
101  return amount_edit_type;
102 }
103 
104 static void
105 gnc_amount_edit_class_init (GNCAmountEditClass *klass)
106 {
107  GObjectClass *object_class;
108  GtkWidgetClass *widget_class;
109  /* GtkEditableClass *editable_class; */
110 
111  object_class = G_OBJECT_CLASS (klass);
112  widget_class = GTK_WIDGET_CLASS (klass);
113  /* editable_class = GTK_EDITABLE_CLASS (g_type_interface_peek (klass, GTK_TYPE_EDITABLE)); */
114 
115  parent_class = g_type_class_peek_parent (klass);
116 
117  amount_edit_signals [AMOUNT_CHANGED] =
118  g_signal_new ("amount_changed",
119  G_OBJECT_CLASS_TYPE (object_class),
120  G_SIGNAL_RUN_FIRST,
121  G_STRUCT_OFFSET (GNCAmountEditClass, amount_changed),
122  NULL,
123  NULL,
124  g_cclosure_marshal_VOID__VOID,
125  G_TYPE_NONE,
126  0);
127 
128  widget_class->key_press_event = gnc_amount_edit_key_press;
129 
130  /* editable_class->changed = gnc_amount_edit_changed; */
131 }
132 
133 static void
134 gnc_amount_edit_init (GNCAmountEdit *gae)
135 {
136  gae->need_to_parse = FALSE;
137  gae->amount = gnc_numeric_zero ();
138  gae->print_info = gnc_default_print_info (FALSE);
139  gae->fraction = 0;
140  gae->evaluate_on_enter = FALSE;
141 
142  g_signal_connect (G_OBJECT (gae), "changed",
143  G_CALLBACK (gnc_amount_edit_changed), NULL);
144 }
145 
146 static void
147 gnc_amount_edit_changed (GtkEditable *editable, gpointer data)
148 {
149  /*GTK_EDITABLE_CLASS (parent_class)->changed(editable);*/
150 
151  GNC_AMOUNT_EDIT(editable)->need_to_parse = TRUE;
152 }
153 
154 static gint
155 gnc_amount_edit_key_press(GtkWidget *widget, GdkEventKey *event)
156 {
157  GNCAmountEdit *gae = GNC_AMOUNT_EDIT(widget);
158  gint result;
159 
160 #ifdef G_OS_WIN32
161  /* gdk never sends GDK_KP_Decimal on win32. See #486658 */
162  if (event->hardware_keycode == VK_DECIMAL)
163  event->keyval = GDK_KP_Decimal;
164 #endif
165  if (event->keyval == GDK_KP_Decimal)
166  {
167  if (gae->print_info.monetary)
168  {
169  struct lconv *lc = gnc_localeconv ();
170  event->keyval = lc->mon_decimal_point[0];
171  event->string[0] = lc->mon_decimal_point[0];
172  }
173  }
174 
175  result = (* GTK_WIDGET_CLASS (parent_class)->key_press_event)(widget, event);
176 
177  switch (event->keyval)
178  {
179  case GDK_Return:
180  if (gae->evaluate_on_enter)
181  break;
182  if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SHIFT_MASK))
183  break;
184  return result;
185  case GDK_KP_Enter:
186  break;
187  default:
188  return result;
189  }
190 
191  gnc_amount_edit_evaluate (gae);
192 
193  return TRUE;
194 }
195 
205 GtkWidget *
206 gnc_amount_edit_new (void)
207 {
208  GNCAmountEdit *gae;
209 
210  gae = g_object_new (GNC_TYPE_AMOUNT_EDIT, NULL);
211  gtk_widget_show (GTK_WIDGET(gae));
212 
213  return GTK_WIDGET (gae);
214 }
215 
232 gint
233 gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
234  gboolean empty_ok)
235 {
236  const char *string;
237  char *error_loc;
238  gboolean ok;
239 
240  g_return_val_if_fail(gae != NULL, -1);
241  g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), -1);
242 
243  string = gtk_entry_get_text(GTK_ENTRY(gae));
244  if (!string || *string == '\0')
245  {
246  *amount = gnc_numeric_zero();
247  if (empty_ok)
248  return -1; /* indicate an empty field */
249  else
250  return 0; /* indicate successfully parsed as 0 */
251  }
252 
253  error_loc = NULL;
254  ok = gnc_exp_parser_parse (string, amount, &error_loc);
255 
256  if (ok)
257  return 0;
258 
259  /* Not ok */
260  if (error_loc != NULL)
261  return error_loc - string;
262  else
263  return 1;
264 }
265 
266 
278 gboolean
279 gnc_amount_edit_evaluate (GNCAmountEdit *gae)
280 {
281  gint result;
282  gnc_numeric amount;
283 
284  g_return_val_if_fail(gae != NULL, FALSE);
285  g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), FALSE);
286 
287 
288  if (!gae->need_to_parse)
289  return TRUE;
290 
291  result = gnc_amount_edit_expr_is_valid (gae, &amount, FALSE);
292 
293  if (result == -1) /* field was empty and may remain so */
294  return TRUE;
295 
296  if (result == 0) /* parsing successful */
297  {
298  gnc_numeric old_amount = gae->amount;
299 
300  if (gae->fraction > 0)
301  amount = gnc_numeric_convert (amount, gae->fraction, GNC_HOW_RND_ROUND_HALF_UP);
302 
303  gnc_amount_edit_set_amount (gae, amount);
304 
305  if (!gnc_numeric_equal (amount, old_amount))
306  g_signal_emit (gae, amount_edit_signals [AMOUNT_CHANGED], 0);
307 
308  return TRUE;
309  }
310 
311  /* Parse error */
312  gtk_editable_set_position (GTK_EDITABLE(gae), result);
313  return FALSE;
314 }
315 
316 
326 gnc_amount_edit_get_amount (GNCAmountEdit *gae)
327 {
328  g_return_val_if_fail(gae != NULL, gnc_numeric_zero ());
329  g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), gnc_numeric_zero ());
330 
331  gnc_amount_edit_evaluate (gae);
332 
333  return gae->amount;
334 }
335 
336 
345 double
346 gnc_amount_edit_get_damount (GNCAmountEdit *gae)
347 {
348  g_return_val_if_fail(gae != NULL, 0.0);
349  g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), 0.0);
350 
351  gnc_amount_edit_evaluate (gae);
352 
353  return gnc_numeric_to_double (gae->amount);
354 }
355 
356 
364 void
365 gnc_amount_edit_set_amount (GNCAmountEdit *gae, gnc_numeric amount)
366 {
367  const char * amount_string;
368 
369  g_return_if_fail(gae != NULL);
370  g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
371  g_return_if_fail(!gnc_numeric_check (amount));
372 
373  /* Update the display. */
374  amount_string = xaccPrintAmount (amount, gae->print_info);
375  gtk_entry_set_text (GTK_ENTRY(gae), amount_string);
376 
377  gae->amount = amount;
378  gae->need_to_parse = FALSE;
379 }
380 
388 void
389 gnc_amount_edit_set_damount (GNCAmountEdit *gae, double damount)
390 {
391  gnc_numeric amount;
392  int fraction;
393 
394  g_return_if_fail(gae != NULL);
395  g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
396 
397  if (gae->fraction > 0)
398  fraction = gae->fraction;
399  else
400  fraction = 100000;
401 
402  amount = double_to_gnc_numeric (damount, fraction, GNC_HOW_RND_ROUND_HALF_UP);
403 
404  gnc_amount_edit_set_amount (gae, amount);
405 }
406 
414 void
415 gnc_amount_edit_set_print_info (GNCAmountEdit *gae,
416  GNCPrintAmountInfo print_info)
417 {
418  g_return_if_fail(gae != NULL);
419  g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
420 
421  gae->print_info = print_info;
422  gae->print_info.use_symbol = 0;
423 }
424 
425 
433 void
434 gnc_amount_edit_set_fraction (GNCAmountEdit *gae, int fraction)
435 {
436  g_return_if_fail(gae != NULL);
437  g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
438 
439  fraction = MAX (0, fraction);
440 
441  gae->fraction = fraction;
442 }
443 
444 
451 GtkWidget *
452 gnc_amount_edit_gtk_entry (GNCAmountEdit *gae)
453 {
454  g_return_val_if_fail(gae != NULL, NULL);
455  g_return_val_if_fail(GNC_IS_AMOUNT_EDIT(gae), NULL);
456 
457  return (GtkWidget *)gae;
458 }
459 
460 
468 void
469 gnc_amount_edit_set_evaluate_on_enter (GNCAmountEdit *gae,
470  gboolean evaluate_on_enter)
471 {
472  g_return_if_fail(gae != NULL);
473  g_return_if_fail(GNC_IS_AMOUNT_EDIT(gae));
474 
475  gae->evaluate_on_enter = evaluate_on_enter;
476 }
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
gnc_numeric double_to_gnc_numeric(double n, gint64 denom, gint how)
utility functions for the GnuCash UI
gdouble gnc_numeric_to_double(gnc_numeric n)
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)