30 #include <gdk/gdkkeysyms.h> 31 #include <glib/gi18n.h> 33 #include "gnc-amount-edit.h" 34 #include "gnc-exp-parser.h" 35 #include "gnc-locale-utils.h" 38 #include "dialog-utils.h" 42 # include <gdk/gdkwin32.h> 54 static guint amount_edit_signals [LAST_SIGNAL] = { 0 };
56 static void gnc_amount_edit_changed (GtkEditable *gae,
58 static void gnc_amount_edit_paste_clipboard (GtkEntry *entry,
60 static gint gnc_amount_edit_key_press (GtkWidget *widget,
64 #define GNC_AMOUNT_EDIT_PATH "gnc-amount-edit-path" 74 gboolean need_to_parse;
78 gboolean block_changed;
84 gboolean evaluate_on_enter;
85 gboolean validate_on_change;
87 gboolean show_warning_symbol;
91 G_DEFINE_TYPE (GNCAmountEdit, gnc_amount_edit, GTK_TYPE_BOX)
94 gnc_amount_edit_finalize (GObject *
object)
96 g_return_if_fail (
object != NULL);
97 g_return_if_fail (GNC_IS_AMOUNT_EDIT(
object));
99 G_OBJECT_CLASS (gnc_amount_edit_parent_class)->finalize (
object);
103 gnc_amount_edit_dispose (GObject *
object)
107 g_return_if_fail (
object != NULL);
108 g_return_if_fail (GNC_IS_AMOUNT_EDIT(
object));
110 gae = GNC_AMOUNT_EDIT(
object);
115 gae->disposed = TRUE;
117 gtk_widget_destroy (GTK_WIDGET(gae->entry));
120 gtk_widget_destroy (GTK_WIDGET(gae->image));
123 G_OBJECT_CLASS (gnc_amount_edit_parent_class)->dispose (
object);
127 gnc_amount_edit_class_init (GNCAmountEditClass *klass)
129 GObjectClass *object_class = G_OBJECT_CLASS(klass);
131 object_class->dispose = gnc_amount_edit_dispose;
132 object_class->finalize = gnc_amount_edit_finalize;
134 amount_edit_signals [ACTIVATE] =
135 g_signal_new (
"activate",
136 G_OBJECT_CLASS_TYPE(object_class),
141 g_cclosure_marshal_VOID__VOID,
145 amount_edit_signals [CHANGED] =
146 g_signal_new (
"changed",
147 G_OBJECT_CLASS_TYPE(object_class),
152 g_cclosure_marshal_VOID__VOID,
156 amount_edit_signals [AMOUNT_CHANGED] =
157 g_signal_new (
"amount_changed",
158 G_OBJECT_CLASS_TYPE(object_class),
163 g_cclosure_marshal_VOID__VOID,
169 gnc_amount_edit_init (GNCAmountEdit *gae)
171 gtk_orientable_set_orientation (GTK_ORIENTABLE(gae),
172 GTK_ORIENTATION_HORIZONTAL);
174 gae->entry = GTK_ENTRY(gtk_entry_new());
175 gae->need_to_parse = FALSE;
176 gae->amount = gnc_numeric_zero ();
177 gae->print_info = gnc_default_print_info (FALSE);
179 gae->evaluate_on_enter = FALSE;
180 gae->validate_on_change = FALSE;
181 gae->block_changed = FALSE;
182 gae->show_warning_symbol = TRUE;
183 gae->disposed = FALSE;
186 gtk_widget_set_name (GTK_WIDGET(gae),
"gnc-id-amount-edit");
188 g_signal_connect (G_OBJECT(gae->entry),
"key-press-event",
189 G_CALLBACK(gnc_amount_edit_key_press), gae);
191 g_signal_connect (G_OBJECT(gae->entry),
"changed",
192 G_CALLBACK(gnc_amount_edit_changed), gae);
194 g_signal_connect (G_OBJECT(gae->entry),
"paste-clipboard",
195 G_CALLBACK(gnc_amount_edit_paste_clipboard), gae);
199 gnc_amount_edit_changed (GtkEditable *editable, gpointer user_data)
201 GNCAmountEdit *gae = GNC_AMOUNT_EDIT(user_data);
203 gae->need_to_parse = TRUE;
205 if (gae->block_changed)
208 if (gae->validate_on_change)
211 gnc_amount_edit_expr_is_valid (gae, &amount, TRUE, NULL);
213 g_signal_emit (gae, amount_edit_signals [CHANGED], 0);
217 gnc_amount_edit_paste_clipboard (GtkEntry *entry, gpointer user_data)
219 GNCAmountEdit *gae = GNC_AMOUNT_EDIT(user_data);
220 GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(entry),
221 GDK_SELECTION_CLIPBOARD);
222 gchar *text = gtk_clipboard_wait_for_text (clipboard);
223 gchar *filtered_text;
224 gint start_pos, end_pos;
230 if (gtk_widget_get_visible (GTK_WIDGET(gae->image)))
232 gtk_widget_hide (GTK_WIDGET(gae->image));
233 gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), NULL);
244 position = gtk_editable_get_position (GTK_EDITABLE(entry));
246 if (gtk_editable_get_selection_bounds (GTK_EDITABLE(entry),
247 &start_pos, &end_pos))
249 position = start_pos;
251 gae->block_changed = TRUE;
252 gtk_editable_delete_selection (GTK_EDITABLE(entry));
253 gae->block_changed = FALSE;
254 gtk_editable_insert_text (GTK_EDITABLE(entry),
255 filtered_text, -1, &position);
258 gtk_editable_insert_text (GTK_EDITABLE(entry),
259 filtered_text, -1, &position);
261 gtk_editable_set_position (GTK_EDITABLE(entry), position);
263 g_signal_stop_emission_by_name (G_OBJECT(entry),
"paste-clipboard");
266 g_free (filtered_text);
270 gnc_amount_edit_key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
272 GNCAmountEdit *gae = GNC_AMOUNT_EDIT(user_data);
275 if (gtk_widget_get_visible (GTK_WIDGET(gae->image)))
277 gtk_widget_hide (GTK_WIDGET(gae->image));
278 gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), NULL);
281 if (event->keyval == GDK_KEY_KP_Decimal)
285 if (gae->print_info.monetary)
287 struct lconv *lc = gnc_localeconv ();
288 event->keyval = lc->mon_decimal_point[0];
289 decimal = g_strdup_printf (
"%c", lc->mon_decimal_point[0]);
292 decimal = g_strdup_printf (
"%c",
'.');
294 GtkEditable *editable = GTK_EDITABLE(widget);
295 gint start_pos, end_pos;
296 gint position = gtk_editable_get_position (editable);
298 if (gtk_editable_get_selection_bounds (editable,
299 &start_pos, &end_pos))
301 position = start_pos;
303 gtk_editable_delete_selection (editable);
304 gtk_editable_insert_text (editable,
305 decimal, -1, &position);
308 gtk_editable_insert_text (editable,
309 decimal, -1, &position);
311 gtk_editable_set_position (editable, position);
316 result = (* GTK_WIDGET_GET_CLASS(widget)->key_press_event)(widget, event);
318 switch (event->keyval)
321 if (event->state & (GDK_MODIFIER_INTENT_DEFAULT_MOD_MASK))
323 case GDK_KEY_KP_Enter:
324 if (gae->evaluate_on_enter)
327 g_signal_emit (gae, amount_edit_signals [ACTIVATE], 0);
333 gnc_amount_edit_evaluate (gae, NULL);
334 g_signal_emit (gae, amount_edit_signals [ACTIVATE], 0);
340 gnc_amount_edit_new (
void)
342 GNCAmountEdit *gae = g_object_new (GNC_TYPE_AMOUNT_EDIT, NULL);
344 gtk_box_pack_start (GTK_BOX(gae), GTK_WIDGET(gae->entry), TRUE, TRUE, 0);
345 gtk_entry_set_width_chars (GTK_ENTRY(gae->entry), 12);
346 gae->image = gtk_image_new_from_icon_name (
"dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
347 gtk_box_pack_start (GTK_BOX(gae), GTK_WIDGET(gae->image), FALSE, FALSE, 6);
348 gtk_widget_set_no_show_all (GTK_WIDGET(gae->image), TRUE);
349 gtk_widget_hide (GTK_WIDGET(gae->image));
350 gtk_widget_show_all (GTK_WIDGET(gae));
352 return GTK_WIDGET(gae);
356 get_original_error_position (
const gchar *
string,
const gchar *symbol,
359 gint original_error_pos = error_pos;
366 if (!
string || !symbol)
369 if (g_strrstr (
string, symbol) == NULL)
372 if (!g_utf8_validate (
string, -1, NULL))
375 text_len = g_utf8_strlen (
string, -1);
376 symbol_len = g_utf8_strlen (symbol, -1);
378 for (gint x = 0; x < text_len; x++)
380 gchar *temp = g_utf8_offset_to_pointer (
string, x);
382 if (g_str_has_prefix (temp, symbol))
383 original_error_pos = original_error_pos + symbol_len;
385 if (x >= original_error_pos)
388 if (g_strrstr (temp, symbol) == NULL)
391 return original_error_pos;
395 exp_validate_quark (
void)
397 return g_quark_from_static_string (
"exp_validate");
401 gnc_amount_edit_expr_is_valid (GNCAmountEdit *gae, gnc_numeric *amount,
402 gboolean empty_ok, GError **error)
407 gchar *err_msg = NULL;
409 const gnc_commodity *comm;
410 char *filtered_string;
411 const gchar *symbol = NULL;
413 g_return_val_if_fail (gae != NULL, -1);
414 g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), -1);
416 string = gtk_entry_get_text (GTK_ENTRY(gae->entry));
418 if (gtk_widget_get_visible (GTK_WIDGET(gae->image)))
420 gtk_widget_hide (GTK_WIDGET(gae->image));
421 gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), NULL);
424 comm = gae->print_info.commodity;
428 if (!filtered_string || *filtered_string ==
'\0')
430 *amount = gnc_numeric_zero ();
431 g_free (filtered_string);
439 ok = gnc_exp_parser_parse (filtered_string, amount, &error_loc);
443 g_free (filtered_string);
448 if (error_loc != NULL)
450 err_code = get_original_error_position (
string, symbol,
451 (error_loc - filtered_string));
453 err_msg = g_strdup_printf (_(
"An error occurred while processing '%s' at position %d"),
459 err_msg = g_strdup_printf (_(
"An error occurred while processing '%s'"),
464 g_set_error_literal (error, exp_validate_quark(), err_code, err_msg);
466 if (gae->show_warning_symbol)
468 gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), err_msg);
469 gtk_widget_show (GTK_WIDGET(gae->image));
470 gtk_widget_queue_resize (GTK_WIDGET(gae->entry));
473 g_free (filtered_string);
479 gnc_amount_edit_evaluate (GNCAmountEdit *gae, GError **error)
483 GError *tmp_error = NULL;
485 g_return_val_if_fail (gae != NULL, FALSE);
486 g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), FALSE);
488 if (!gae->need_to_parse)
491 result = gnc_amount_edit_expr_is_valid (gae, &amount, FALSE, &tmp_error);
498 gnc_numeric old_amount = gae->amount;
500 if (gae->fraction > 0)
503 gnc_amount_edit_set_amount (gae, amount);
506 g_signal_emit (gae, amount_edit_signals [AMOUNT_CHANGED], 0);
508 gtk_editable_set_position (GTK_EDITABLE(gae->entry), -1);
515 if (tmp_error->code < 1000)
516 gtk_editable_set_position (GTK_EDITABLE(gae->entry), tmp_error->code);
519 g_propagate_error (error, tmp_error);
521 g_error_free (tmp_error);
527 gnc_amount_edit_get_amount (GNCAmountEdit *gae)
529 g_return_val_if_fail (gae != NULL, gnc_numeric_zero ());
530 g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), gnc_numeric_zero ());
532 gnc_amount_edit_evaluate (gae, NULL);
538 gnc_amount_edit_get_damount (GNCAmountEdit *gae)
540 g_return_val_if_fail (gae != NULL, 0.0);
541 g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), 0.0);
543 gnc_amount_edit_evaluate (gae, NULL);
549 gnc_amount_edit_set_amount (GNCAmountEdit *gae, gnc_numeric amount)
551 const char * amount_string;
553 g_return_if_fail (gae != NULL);
554 g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
557 if (gtk_widget_get_visible (GTK_WIDGET(gae->image)))
559 gtk_widget_hide (GTK_WIDGET(gae->image));
560 gtk_widget_set_tooltip_text (GTK_WIDGET(gae->image), NULL);
565 gtk_entry_set_text (GTK_ENTRY(gae->entry), amount_string);
567 gae->amount = amount;
568 gae->need_to_parse = FALSE;
572 gnc_amount_edit_set_damount (GNCAmountEdit *gae,
double damount)
577 g_return_if_fail (gae != NULL);
578 g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
580 if (gae->fraction > 0)
581 fraction = gae->fraction;
587 gnc_amount_edit_set_amount (gae, amount);
591 gnc_amount_edit_set_print_info (GNCAmountEdit *gae,
594 g_return_if_fail (gae != NULL);
595 g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
597 gae->print_info = print_info;
598 gae->print_info.use_symbol = 0;
602 gnc_amount_edit_set_fraction (GNCAmountEdit *gae,
int fraction)
604 g_return_if_fail (gae != NULL);
605 g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
607 fraction = MAX (0, fraction);
609 gae->fraction = fraction;
613 gnc_amount_edit_gtk_entry (GNCAmountEdit *gae)
615 g_return_val_if_fail (gae != NULL, NULL);
616 g_return_val_if_fail (GNC_IS_AMOUNT_EDIT(gae), NULL);
618 return GTK_WIDGET(gae->entry);
622 gnc_amount_edit_set_evaluate_on_enter (GNCAmountEdit *gae,
623 gboolean evaluate_on_enter)
625 g_return_if_fail (gae != NULL);
626 g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
628 gae->evaluate_on_enter = evaluate_on_enter;
632 gnc_amount_edit_set_validate_on_change (GNCAmountEdit *gae,
633 gboolean validate_on_change)
635 g_return_if_fail (gae != NULL);
636 g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
638 gae->validate_on_change = validate_on_change;
642 gnc_amount_edit_select_region (GNCAmountEdit *gae,
646 g_return_if_fail (gae != NULL);
647 g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
649 gtk_editable_select_region (GTK_EDITABLE(gae->entry),
655 gnc_amount_edit_show_warning_symbol (GNCAmountEdit *gae, gboolean show)
657 g_return_if_fail (gae != NULL);
658 g_return_if_fail (GNC_IS_AMOUNT_EDIT(gae));
660 gae->show_warning_symbol = show;
664 gnc_amount_edit_make_mnemonic_target (GNCAmountEdit *gae, GtkWidget *label)
669 gtk_label_set_mnemonic_widget (GTK_LABEL(label), GTK_WIDGET(gae->entry));
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gnc_numeric double_to_gnc_numeric(double in, gint64 denom, gint how)
Convert a floating-point number to a gnc_numeric.
utility functions for the GnuCash UI
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
char * gnc_filter_text_for_currency_commodity(const gnc_commodity *comm, const char *incoming_text, const char **symbol)
Returns the incoming text removed of currency symbol.
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
Change the denominator of a gnc_numeric value to the specified denominator under standard arguments '...
char * gnc_filter_text_for_control_chars(const char *text)
Returns the incoming text removed of control characters.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.