2 * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
3 * Copyright (C) 2008-2011 Robert Ancell
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 2 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
19 #include <glib/gi18n.h>
21 #include "math-equation.h"
24 #include "mp-equation.h"
25 #include "mp-serializer.h"
27 #include "unit-manager.h"
37 PROP_SHOW_THOUSANDS_SEPARATORS
,
38 PROP_SHOW_TRAILING_ZEROES
,
50 static GType number_mode_type
, number_format_type
, angle_unit_type
;
52 #define MAX_DIGITS 512
54 /* Expression mode state */
56 MPNumber ans
; /* Previously calculated answer */
57 gchar
*expression
; /* Expression entered by user */
58 gint ans_start
, ans_end
; /* Start and end characters for ans variable in expression */
59 gint cursor
; /* ??? */
60 NumberMode number_mode
; /* ??? */
61 gboolean can_super_minus
; /* TRUE if entering minus can generate a superscript minus */
62 gboolean entered_multiply
; /* Last insert was a multiply character */
63 gchar
*status
; /* Equation status */
66 struct MathEquationPrivate
70 gint word_size
; /* Word size in bits */
71 MPAngleUnit angle_units
; /* Units for trigonometric functions */
72 char *source_currency
;
73 char *target_currency
;
76 NumberMode number_mode
; /* ??? */
77 gboolean can_super_minus
; /* TRUE if entering minus can generate a superscript minus */
79 gunichar digits
[16]; /* Localized digits */
81 GtkTextMark
*ans_start
, *ans_end
;
83 MathEquationState state
; /* Equation state */
84 GList
*undo_stack
; /* History of expression mode states */
86 gboolean in_undo_operation
;
94 MathVariables
*variables
;
95 MpSerializer
*serializer
;
101 MPNumber
*number_result
;
106 G_DEFINE_TYPE (MathEquation
, math_equation
, GTK_TYPE_TEXT_BUFFER
);
112 return g_object_new(math_equation_get_type(), NULL
);
117 math_equation_get_variables(MathEquation
*equation
)
119 return equation
->priv
->variables
;
124 get_ans_offsets(MathEquation
*equation
, gint
*start
, gint
*end
)
128 if (!equation
->priv
->ans_start
) {
133 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, equation
->priv
->ans_start
);
134 *start
= gtk_text_iter_get_offset(&iter
);
135 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, equation
->priv
->ans_end
);
136 *end
= gtk_text_iter_get_offset(&iter
);
141 reformat_ans(MathEquation
*equation
)
143 if (!equation
->priv
->ans_start
)
146 gchar
*orig_ans_text
;
148 GtkTextIter ans_start
, ans_end
;
150 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &ans_start
, equation
->priv
->ans_start
);
151 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &ans_end
, equation
->priv
->ans_end
);
152 orig_ans_text
= gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation
), &ans_start
, &ans_end
, FALSE
);
153 ans_text
= mp_serializer_to_string(equation
->priv
->serializer
, &equation
->priv
->state
.ans
);
154 if (strcmp(orig_ans_text
, ans_text
) != 0) {
157 equation
->priv
->in_undo_operation
= TRUE
;
158 equation
->priv
->in_reformat
= TRUE
;
160 start
= gtk_text_iter_get_offset(&ans_start
);
161 gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation
), &ans_start
, &ans_end
);
162 gtk_text_buffer_insert_with_tags(GTK_TEXT_BUFFER(equation
), &ans_end
, ans_text
, -1, equation
->priv
->ans_tag
, NULL
);
164 /* There seems to be a bug in the marks as they alternate being the correct and incorrect ways. Reset them */
165 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &ans_start
, start
);
166 gtk_text_buffer_move_mark(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_start
, &ans_start
);
167 gtk_text_buffer_move_mark(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_end
, &ans_end
);
169 equation
->priv
->in_reformat
= FALSE
;
170 equation
->priv
->in_undo_operation
= FALSE
;
172 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &ans_start
, equation
->priv
->ans_start
);
173 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &ans_end
, equation
->priv
->ans_end
);
174 g_free(orig_ans_text
);
180 count_digits(MathEquation
*equation
, const gchar
*text
)
182 const gchar
*read_iter
;
186 while (*read_iter
!= '\0') {
187 if (!g_unichar_isdigit(g_utf8_get_char(read_iter
)))
190 read_iter
= g_utf8_next_char(read_iter
);
192 /* Allow a thousands separator between digits follow a digit */
193 if (g_utf8_get_char(read_iter
) == mp_serializer_get_thousands_separator(equation
->priv
->serializer
)) {
194 read_iter
= g_utf8_next_char(read_iter
);
195 if (!g_unichar_isdigit(g_utf8_get_char(read_iter
)))
207 reformat_separators(MathEquation
*equation
)
209 gchar
*text
, *read_iter
;
210 gint ans_start
, ans_end
;
211 gint offset
, digit_offset
= 0;
212 gboolean in_number
= FALSE
, in_radix
= FALSE
, last_is_tsep
= FALSE
;
214 equation
->priv
->in_undo_operation
= TRUE
;
215 equation
->priv
->in_reformat
= TRUE
;
217 text
= math_equation_get_display(equation
);
218 get_ans_offsets(equation
, &ans_start
, &ans_end
);
219 for (read_iter
= text
, offset
= 0; *read_iter
!= '\0'; read_iter
= g_utf8_next_char(read_iter
), offset
++) {
221 gboolean expect_tsep
;
223 /* See what digit this character is */
224 c
= g_utf8_get_char(read_iter
);
226 expect_tsep
= mp_serializer_get_show_thousands_separators(equation
->priv
->serializer
) &&
227 in_number
&& !in_radix
&& !last_is_tsep
&&
228 digit_offset
> 0 && digit_offset
% mp_serializer_get_thousands_separator_count(equation
->priv
->serializer
) == 0;
229 last_is_tsep
= FALSE
;
231 /* Don't mess with ans */
232 if (offset
>= ans_start
&& offset
<= ans_end
) {
233 in_number
= in_radix
= FALSE
;
236 if (g_unichar_isdigit(c
)) {
238 digit_offset
= count_digits(equation
, read_iter
);
241 /* Expected a thousands separator between these digits - insert it */
247 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &iter
, offset
);
248 len
= g_unichar_to_utf8(mp_serializer_get_thousands_separator(equation
->priv
->serializer
), buffer
);
250 gtk_text_buffer_insert(GTK_TEXT_BUFFER(equation
), &iter
, buffer
, -1);
257 else if (c
== mp_serializer_get_radix(equation
->priv
->serializer
)) {
258 in_number
= in_radix
= TRUE
;
260 else if (c
== mp_serializer_get_thousands_separator(equation
->priv
->serializer
)) {
261 /* Didn't expect thousands separator - delete it */
262 if (!expect_tsep
&& in_number
) {
263 GtkTextIter start
, end
;
264 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &start
, offset
);
265 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &end
, offset
+ 1);
266 gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation
), &start
, &end
);
273 in_number
= in_radix
= FALSE
;
279 equation
->priv
->in_reformat
= FALSE
;
280 equation
->priv
->in_undo_operation
= FALSE
;
285 reformat_display(MathEquation
*equation
, gint old_base
)
288 reformat_ans(equation
);
290 /* Add/remove thousands separators */
291 reformat_separators(equation
);
295 static MathEquationState
*
296 get_current_state(MathEquation
*equation
)
298 MathEquationState
*state
;
299 gint ans_start
= -1, ans_end
= -1;
301 state
= g_malloc0(sizeof(MathEquationState
));
303 if (equation
->priv
->ans_start
)
306 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, equation
->priv
->ans_start
);
307 ans_start
= gtk_text_iter_get_offset(&iter
);
308 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, equation
->priv
->ans_end
);
309 ans_end
= gtk_text_iter_get_offset(&iter
);
312 mp_set_from_mp(&equation
->priv
->state
.ans
, &state
->ans
);
313 state
->expression
= math_equation_get_display(equation
);
314 state
->ans_start
= ans_start
;
315 state
->ans_end
= ans_end
;
316 g_object_get(G_OBJECT(equation
), "cursor-position", &state
->cursor
, NULL
);
317 state
->number_mode
= equation
->priv
->number_mode
;
318 state
->can_super_minus
= equation
->priv
->can_super_minus
;
319 state
->entered_multiply
= equation
->priv
->state
.entered_multiply
;
320 state
->status
= g_strdup(equation
->priv
->state
.status
);
327 free_state(MathEquationState
*state
)
329 g_free(state
->expression
);
330 g_free(state
->status
);
336 math_equation_push_undo_stack(MathEquation
*equation
)
339 MathEquationState
*state
;
341 if (equation
->priv
->in_undo_operation
)
344 math_equation_set_status(equation
, "");
346 /* Can't redo anymore */
347 for (link
= equation
->priv
->redo_stack
; link
; link
= link
->next
) {
351 g_list_free(equation
->priv
->redo_stack
);
352 equation
->priv
->redo_stack
= NULL
;
354 state
= get_current_state(equation
);
355 equation
->priv
->undo_stack
= g_list_prepend(equation
->priv
->undo_stack
, state
);
360 clear_ans(MathEquation
*equation
, gboolean remove_tag
)
362 if (!equation
->priv
->ans_start
)
366 GtkTextIter start
, end
;
368 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &start
, equation
->priv
->ans_start
);
369 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &end
, equation
->priv
->ans_end
);
370 gtk_text_buffer_remove_tag(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_tag
, &start
, &end
);
373 gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_start
);
374 gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_end
);
375 equation
->priv
->ans_start
= NULL
;
376 equation
->priv
->ans_end
= NULL
;
381 apply_state(MathEquation
*equation
, MathEquationState
*state
)
385 /* Disable undo detection */
386 equation
->priv
->in_undo_operation
= TRUE
;
388 mp_set_from_mp(&state
->ans
, &equation
->priv
->state
.ans
);
390 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), state
->expression
, -1);
391 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &cursor
, state
->cursor
);
392 gtk_text_buffer_place_cursor(GTK_TEXT_BUFFER(equation
), &cursor
);
393 clear_ans(equation
, FALSE
);
394 if (state
->ans_start
>= 0) {
395 GtkTextIter start
, end
;
397 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &start
, state
->ans_start
);
398 equation
->priv
->ans_start
= gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation
), NULL
, &start
, FALSE
);
399 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &end
, state
->ans_end
);
400 equation
->priv
->ans_end
= gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation
), NULL
, &end
, TRUE
);
401 gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_tag
, &start
, &end
);
404 math_equation_set_number_mode(equation
, state
->number_mode
);
405 equation
->priv
->can_super_minus
= state
->can_super_minus
;
406 equation
->priv
->state
.entered_multiply
= state
->entered_multiply
;
407 math_equation_set_status(equation
, state
->status
);
409 equation
->priv
->in_undo_operation
= FALSE
;
414 math_equation_copy(MathEquation
*equation
)
416 GtkTextIter start
, end
;
419 if (!gtk_text_buffer_get_selection_bounds(GTK_TEXT_BUFFER(equation
), &start
, &end
))
420 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation
), &start
, &end
);
422 text
= gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation
), &start
, &end
, FALSE
);
423 gtk_clipboard_set_text(gtk_clipboard_get(GDK_NONE
), text
, -1);
429 on_paste(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
431 MathEquation
*equation
= data
;
433 math_equation_insert(equation
, text
);
438 math_equation_paste(MathEquation
*equation
)
440 gtk_clipboard_request_text(gtk_clipboard_get(GDK_NONE
), on_paste
, equation
);
445 math_equation_undo(MathEquation
*equation
)
448 MathEquationState
*state
;
450 if (!equation
->priv
->undo_stack
) {
451 math_equation_set_status(equation
,
452 /* Error shown when trying to undo with no undo history */
453 _("No undo history"));
457 link
= equation
->priv
->undo_stack
;
458 equation
->priv
->undo_stack
= g_list_remove_link(equation
->priv
->undo_stack
, link
);
462 equation
->priv
->redo_stack
= g_list_prepend(equation
->priv
->redo_stack
, get_current_state(equation
));
464 apply_state(equation
, state
);
470 math_equation_redo(MathEquation
*equation
)
473 MathEquationState
*state
;
475 if (!equation
->priv
->redo_stack
) {
476 math_equation_set_status(equation
,
477 /* Error shown when trying to redo with no redo history */
478 _("No redo history"));
482 link
= equation
->priv
->redo_stack
;
483 equation
->priv
->redo_stack
= g_list_remove_link(equation
->priv
->redo_stack
, link
);
487 equation
->priv
->undo_stack
= g_list_prepend(equation
->priv
->undo_stack
, get_current_state(equation
));
489 apply_state(equation
, state
);
495 math_equation_get_digit_text(MathEquation
*equation
, guint digit
)
497 return equation
->priv
->digits
[digit
];
502 math_equation_set_accuracy(MathEquation
*equation
, gint accuracy
)
504 gint old_accuracy
= mp_serializer_get_accuracy(equation
->priv
->serializer
);
505 if (old_accuracy
== accuracy
)
507 mp_serializer_set_accuracy(equation
->priv
->serializer
, accuracy
);
508 reformat_display(equation
, mp_serializer_get_base(equation
->priv
->serializer
));
509 g_object_notify(G_OBJECT(equation
), "accuracy");
514 math_equation_get_accuracy(MathEquation
*equation
)
516 return mp_serializer_get_accuracy(equation
->priv
->serializer
);
521 math_equation_set_show_thousands_separators(MathEquation
*equation
, gboolean visible
)
523 gboolean old_visible
= mp_serializer_get_show_thousands_separators(equation
->priv
->serializer
);
524 if (old_visible
== visible
)
526 mp_serializer_set_show_thousands_separators(equation
->priv
->serializer
, visible
);
527 reformat_display(equation
, mp_serializer_get_base(equation
->priv
->serializer
));
528 g_object_notify(G_OBJECT(equation
), "show-thousands-separators");
533 math_equation_get_show_thousands_separators(MathEquation
*equation
)
535 return mp_serializer_get_show_thousands_separators(equation
->priv
->serializer
);
540 math_equation_set_show_trailing_zeroes(MathEquation
*equation
, gboolean visible
)
542 gboolean old_visible
= mp_serializer_get_show_trailing_zeroes(equation
->priv
->serializer
);
543 if (old_visible
== visible
)
545 mp_serializer_set_show_trailing_zeroes(equation
->priv
->serializer
, visible
);
546 reformat_display(equation
, mp_serializer_get_base(equation
->priv
->serializer
));
547 g_object_notify(G_OBJECT(equation
), "show-trailing-zeroes");
552 math_equation_get_show_trailing_zeroes(MathEquation
*equation
)
554 return mp_serializer_get_show_trailing_zeroes(equation
->priv
->serializer
);
559 math_equation_set_number_format(MathEquation
*equation
, MpDisplayFormat format
)
561 MpDisplayFormat old_format
= mp_serializer_get_number_format(equation
->priv
->serializer
);
562 if (old_format
== format
)
565 mp_serializer_set_number_format(equation
->priv
->serializer
, format
);
566 reformat_display(equation
, mp_serializer_get_base(equation
->priv
->serializer
));
567 g_object_notify(G_OBJECT(equation
), "number-format");
572 math_equation_get_number_format(MathEquation
*equation
)
574 return mp_serializer_get_number_format(equation
->priv
->serializer
);
579 math_equation_set_base(MathEquation
*equation
, gint base
)
581 gint old_base
= mp_serializer_get_base(equation
->priv
->serializer
);
583 if (old_base
== base
)
586 mp_serializer_set_base(equation
->priv
->serializer
, base
);
587 reformat_display(equation
, old_base
);
588 g_object_notify(G_OBJECT(equation
), "base");
593 math_equation_get_base(MathEquation
*equation
)
595 return mp_serializer_get_base(equation
->priv
->serializer
);
600 math_equation_set_word_size(MathEquation
*equation
, gint word_size
)
602 if (equation
->priv
->word_size
== word_size
)
604 equation
->priv
->word_size
= word_size
;
605 g_object_notify(G_OBJECT(equation
), "word-size");
610 math_equation_get_word_size(MathEquation
*equation
)
612 return equation
->priv
->word_size
;
617 math_equation_set_angle_units(MathEquation
*equation
, MPAngleUnit angle_units
)
619 if (equation
->priv
->angle_units
== angle_units
)
621 equation
->priv
->angle_units
= angle_units
;
622 g_object_notify(G_OBJECT(equation
), "angle-units");
627 math_equation_get_angle_units(MathEquation
*equation
)
629 return equation
->priv
->angle_units
;
634 math_equation_set_source_currency(MathEquation
*equation
, const gchar
*currency
)
636 if (strcmp(equation
->priv
->source_currency
, currency
) == 0)
638 g_free(equation
->priv
->source_currency
);
639 equation
->priv
->source_currency
= g_strdup(currency
);
640 g_object_notify(G_OBJECT(equation
), "source-currency");
645 math_equation_get_source_currency(MathEquation
*equation
)
647 return equation
->priv
->source_currency
;
652 math_equation_set_target_currency(MathEquation
*equation
, const gchar
*currency
)
654 if (strcmp(equation
->priv
->target_currency
, currency
) == 0)
656 g_free(equation
->priv
->target_currency
);
657 equation
->priv
->target_currency
= g_strdup(currency
);
658 g_object_notify(G_OBJECT(equation
), "target-currency");
663 math_equation_get_target_currency(MathEquation
*equation
)
665 return equation
->priv
->target_currency
;
670 math_equation_set_source_units(MathEquation
*equation
, const gchar
*units
)
672 if (strcmp(equation
->priv
->source_units
, units
) == 0)
674 g_free(equation
->priv
->source_units
);
675 equation
->priv
->source_units
= g_strdup(units
);
676 g_object_notify(G_OBJECT(equation
), "source-units");
680 math_equation_get_source_units(MathEquation
*equation
)
682 return equation
->priv
->source_units
;
687 math_equation_set_target_units(MathEquation
*equation
, const gchar
*units
)
689 if (strcmp(equation
->priv
->target_units
, units
) == 0)
691 g_free(equation
->priv
->target_units
);
692 equation
->priv
->target_units
= g_strdup(units
);
693 g_object_notify(G_OBJECT(equation
), "target-units");
698 math_equation_get_target_units(MathEquation
*equation
)
700 return equation
->priv
->target_units
;
705 math_equation_set_status(MathEquation
*equation
, const gchar
*status
)
707 if (strcmp(equation
->priv
->state
.status
, status
) == 0)
710 g_free(equation
->priv
->state
.status
);
711 equation
->priv
->state
.status
= g_strdup(status
);
712 g_object_notify(G_OBJECT(equation
), "status");
717 math_equation_get_status(MathEquation
*equation
)
719 return equation
->priv
->state
.status
;
724 math_equation_is_empty(MathEquation
*equation
)
726 return gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation
)) == 0;
731 math_equation_is_result(MathEquation
*equation
)
736 text
= math_equation_get_equation(equation
);
737 result
= strcmp(text
, "ans") == 0;
745 math_equation_get_display(MathEquation
*equation
)
747 GtkTextIter start
, end
;
749 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation
), &start
, &end
);
750 return gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation
), &start
, &end
, FALSE
);
755 math_equation_get_equation(MathEquation
*equation
)
759 gint ans_start
= -1, ans_end
= -1, offset
;
760 const gchar
*read_iter
;
761 gboolean last_is_digit
= FALSE
;
763 text
= math_equation_get_display(equation
);
764 eq_text
= g_string_sized_new(strlen(text
));
766 if (equation
->priv
->ans_start
)
767 get_ans_offsets(equation
, &ans_start
, &ans_end
);
769 for (read_iter
= text
, offset
= 0; *read_iter
!= '\0'; read_iter
= g_utf8_next_char(read_iter
), offset
++) {
771 gboolean is_digit
, next_is_digit
;
773 c
= g_utf8_get_char(read_iter
);
774 is_digit
= g_unichar_isdigit(c
);
775 next_is_digit
= g_unichar_isdigit(g_utf8_get_char(g_utf8_next_char(read_iter
)));
777 /* Replace ans text with variable */
778 if (offset
== ans_start
) {
779 g_string_append(eq_text
, "ans");
780 read_iter
= g_utf8_offset_to_pointer(read_iter
, ans_end
- ans_start
- 1);
781 offset
+= ans_end
- ans_start
- 1;
786 /* Ignore thousands separators */
787 if (c
== mp_serializer_get_thousands_separator(equation
->priv
->serializer
) && last_is_digit
&& next_is_digit
)
789 /* Substitute radix character */
790 else if (c
== mp_serializer_get_radix(equation
->priv
->serializer
) && (last_is_digit
|| next_is_digit
))
791 g_string_append_unichar(eq_text
, '.');
793 g_string_append_unichar(eq_text
, c
);
795 last_is_digit
= is_digit
;
800 g_string_free(eq_text
, FALSE
);
807 math_equation_get_number(MathEquation
*equation
, MPNumber
*z
)
812 text
= math_equation_get_equation(equation
);
813 result
= !mp_serializer_from_string(equation
->priv
->serializer
, text
, z
);
821 math_equation_get_serializer(MathEquation
*equation
)
823 return equation
->priv
->serializer
;
828 math_equation_set_number_mode(MathEquation
*equation
, NumberMode mode
)
830 if (equation
->priv
->number_mode
== mode
)
833 equation
->priv
->can_super_minus
= mode
== SUPERSCRIPT
;
835 equation
->priv
->number_mode
= mode
;
836 g_object_notify(G_OBJECT(equation
), "number-mode");
841 math_equation_get_number_mode(MathEquation
*equation
)
843 return equation
->priv
->number_mode
;
848 math_equation_in_solve(MathEquation
*equation
)
850 return equation
->priv
->in_solve
;
855 math_equation_get_answer(MathEquation
*equation
)
857 return &equation
->priv
->state
.ans
;
862 math_equation_store(MathEquation
*equation
, const gchar
*name
)
866 if (!math_equation_get_number(equation
, &t
))
867 math_equation_set_status(equation
, _("No sane value to store"));
869 math_variables_set(equation
->priv
->variables
, name
, &t
);
874 math_equation_recall(MathEquation
*equation
, const gchar
*name
)
876 math_equation_insert(equation
, name
);
881 math_equation_set(MathEquation
*equation
, const gchar
*text
)
884 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), text
, -1);
885 clear_ans(equation
, FALSE
);
890 math_equation_set_number(MathEquation
*equation
, const MPNumber
*x
)
893 GtkTextIter start
, end
;
895 /* Show the number in the user chosen format */
896 text
= mp_serializer_to_string(equation
->priv
->serializer
, x
);
897 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), text
, -1);
898 mp_set_from_mp(x
, &equation
->priv
->state
.ans
);
900 /* Mark this text as the answer variable */
901 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation
), &start
, &end
);
902 clear_ans(equation
, FALSE
);
903 equation
->priv
->ans_start
= gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation
), NULL
, &start
, FALSE
);
904 equation
->priv
->ans_end
= gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation
), NULL
, &end
, TRUE
);
905 gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_tag
, &start
, &end
);
911 math_equation_insert(MathEquation
*equation
, const gchar
*text
)
913 /* Replace ** with ^ (not on all keyboards) */
914 if (!gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(equation
)) &&
915 strcmp(text
, "×") == 0 && equation
->priv
->state
.entered_multiply
) {
918 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(equation
)));
919 gtk_text_buffer_backspace(GTK_TEXT_BUFFER(equation
), &iter
, TRUE
, TRUE
);
920 gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(equation
), "^", -1);
924 /* Start new equation when entering digits after existing result */
925 if(math_equation_is_result(equation
) && g_unichar_isdigit(g_utf8_get_char(text
)))
926 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), "", -1);
928 /* Can't enter superscript minus after entering digits */
929 if (strstr("⁰¹²³⁴⁵⁶⁷⁸⁹", text
) != NULL
|| strcmp("⁻", text
) == 0)
930 equation
->priv
->can_super_minus
= FALSE
;
932 /* Disable super/subscript mode when finished entering */
933 if (strstr("⁻⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉", text
) == NULL
)
934 math_equation_set_number_mode(equation
, NORMAL
);
936 gtk_text_buffer_delete_selection(GTK_TEXT_BUFFER(equation
), FALSE
, FALSE
);
937 gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(equation
), text
, -1);
942 math_equation_insert_digit(MathEquation
*equation
, guint digit
)
944 static const char *subscript_digits
[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉", NULL
};
945 static const char *superscript_digits
[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹", NULL
};
947 if (equation
->priv
->number_mode
== NORMAL
|| digit
>= 10) {
950 len
= g_unichar_to_utf8(math_equation_get_digit_text(equation
, digit
), buffer
);
952 math_equation_insert(equation
, buffer
);
954 else if (equation
->priv
->number_mode
== SUPERSCRIPT
)
955 math_equation_insert(equation
, superscript_digits
[digit
]);
956 else if (equation
->priv
->number_mode
== SUBSCRIPT
)
957 math_equation_insert(equation
, subscript_digits
[digit
]);
962 math_equation_insert_numeric_point(MathEquation
*equation
)
966 len
= g_unichar_to_utf8(mp_serializer_get_radix(equation
->priv
->serializer
), buffer
);
968 math_equation_insert(equation
, buffer
);
973 math_equation_insert_number(MathEquation
*equation
, const MPNumber
*x
)
976 text
= mp_serializer_to_string(equation
->priv
->serializer
, x
);
977 math_equation_insert(equation
, text
);
983 math_equation_insert_exponent(MathEquation
*equation
)
985 math_equation_insert(equation
, "×10");
986 math_equation_set_number_mode(equation
, SUPERSCRIPT
);
991 math_equation_insert_subtract(MathEquation
*equation
)
993 if (equation
->priv
->number_mode
== SUPERSCRIPT
&& equation
->priv
->can_super_minus
) {
994 math_equation_insert(equation
, "⁻");
995 equation
->priv
->can_super_minus
= FALSE
;
998 math_equation_insert(equation
, "−");
999 math_equation_set_number_mode(equation
, NORMAL
);
1005 variable_is_defined(const char *name
, void *data
)
1007 MathEquation
*equation
= data
;
1008 char *c
, *lower_name
;
1010 lower_name
= strdup(name
);
1011 for (c
= lower_name
; *c
; c
++)
1014 if (strcmp(lower_name
, "rand") == 0 ||
1015 strcmp(lower_name
, "ans") == 0) {
1021 return math_variables_get(equation
->priv
->variables
, name
) != NULL
;
1026 get_variable(const char *name
, MPNumber
*z
, void *data
)
1028 char *c
, *lower_name
;
1030 MathEquation
*equation
= data
;
1033 lower_name
= strdup(name
);
1034 for (c
= lower_name
; *c
; c
++)
1037 if (strcmp(lower_name
, "rand") == 0)
1038 mp_set_from_random(z
);
1039 else if (strcmp(lower_name
, "ans") == 0)
1040 mp_set_from_mp(&equation
->priv
->state
.ans
, z
);
1042 t
= math_variables_get(equation
->priv
->variables
, name
);
1044 mp_set_from_mp(t
, z
);
1056 set_variable(const char *name
, const MPNumber
*x
, void *data
)
1058 MathEquation
*equation
= data
;
1059 /* FIXME: Don't allow writing to built-in variables, e.g. ans, rand, sin, ... */
1060 math_variables_set(equation
->priv
->variables
, name
, x
);
1065 convert(const MPNumber
*x
, const char *x_units
, const char *z_units
, MPNumber
*z
, void *data
)
1067 return unit_manager_convert_by_symbol(unit_manager_get_default(), x
, x_units
, z_units
, z
);
1072 parse(MathEquation
*equation
, const char *text
, MPNumber
*z
, char **error_token
)
1074 MPEquationOptions options
;
1076 memset(&options
, 0, sizeof(options
));
1077 options
.base
= mp_serializer_get_base(equation
->priv
->serializer
);
1078 options
.wordlen
= equation
->priv
->word_size
;
1079 options
.angle_units
= equation
->priv
->angle_units
;
1080 options
.variable_is_defined
= variable_is_defined
;
1081 options
.get_variable
= get_variable
;
1082 options
.set_variable
= set_variable
;
1083 options
.convert
= convert
;
1084 options
.callback_data
= equation
;
1086 return mp_equation_parse(text
, &options
, z
, error_token
);
1091 * Executed in separate thread. It is thus not a good idea to write to anything
1092 * in MathEquation but the async queue from here.
1095 math_equation_solve_real(gpointer data
)
1097 MathEquation
*equation
= MATH_EQUATION(data
);
1098 SolveData
*solvedata
= g_slice_new0(SolveData
);
1100 gint n_brackets
= 0, result
;
1101 gchar
*c
, *text
, *error_token
;
1102 GString
*equation_text
;
1105 text
= math_equation_get_equation(equation
);
1106 equation_text
= g_string_new(text
);
1108 /* Count the number of brackets and automatically add missing closing brackets */
1109 for (c
= equation_text
->str
; *c
; c
++) {
1115 while (n_brackets
> 0) {
1116 g_string_append_c(equation_text
, ')');
1121 result
= parse(equation
, equation_text
->str
, &z
, &error_token
);
1122 g_string_free(equation_text
, TRUE
);
1125 case PARSER_ERR_NONE
:
1126 solvedata
->number_result
= g_slice_new(MPNumber
);
1127 mp_set_from_mp(&z
, solvedata
->number_result
);
1130 case PARSER_ERR_OVERFLOW
:
1131 solvedata
->error
= g_strdup(/* Error displayed to user when they perform a bitwise operation on numbers greater than the current word */
1132 _("Overflow. Try a bigger word size"));
1135 case PARSER_ERR_UNKNOWN_VARIABLE
:
1136 solvedata
->error
= g_strdup_printf(/* Error displayed to user when they an unknown variable is entered */
1137 _("Unknown variable '%s'"), error_token
);
1140 case PARSER_ERR_UNKNOWN_FUNCTION
:
1141 solvedata
->error
= g_strdup_printf(/* Error displayed to user when an unknown function is entered */
1142 _("Function '%s' is not defined"), error_token
);
1145 case PARSER_ERR_UNKNOWN_CONVERSION
:
1146 solvedata
->error
= g_strdup(/* Error displayed to user when an conversion with unknown units is attempted */
1147 _("Unknown conversion"));
1151 solvedata
->error
= g_strdup(mp_get_error());
1155 solvedata
->error
= g_strdup(/* Error displayed to user when they enter an invalid calculation */
1156 _("Malformed expression"));
1159 g_async_queue_push(equation
->priv
->queue
, solvedata
);
1166 math_equation_show_in_progress(gpointer data
)
1168 MathEquation
*equation
= MATH_EQUATION(data
);
1169 if (equation
->priv
->in_solve
)
1170 math_equation_set_status(equation
, "Calculating");
1176 math_equation_look_for_answer(gpointer data
)
1178 MathEquation
*equation
= MATH_EQUATION(data
);
1179 SolveData
*result
= g_async_queue_try_pop(equation
->priv
->queue
);
1184 equation
->priv
->in_solve
= false;
1187 math_equation_set_status(equation
, "");
1189 if (result
->error
!= NULL
) {
1190 math_equation_set_status(equation
, result
->error
);
1191 g_free(result
->error
);
1193 else if (result
->number_result
!= NULL
) {
1194 math_equation_set_number(equation
, result
->number_result
);
1195 g_slice_free(MPNumber
, result
->number_result
);
1197 else if (result
->text_result
!= NULL
) {
1198 math_equation_set(equation
, result
->text_result
);
1199 g_free(result
->text_result
);
1201 g_slice_free(SolveData
, result
);
1208 math_equation_solve(MathEquation
*equation
)
1210 GError
*error
= NULL
;
1212 // FIXME: should replace calculation or give error message
1213 if (equation
->priv
->in_solve
)
1216 if (math_equation_is_empty(equation
))
1219 /* If showing a result return to the equation that caused it */
1220 // FIXME: Result may not be here due to solve (i.e. the user may have entered "ans")
1221 if (math_equation_is_result(equation
)) {
1222 math_equation_undo(equation
);
1226 equation
->priv
->in_solve
= true;
1228 math_equation_set_number_mode(equation
, NORMAL
);
1230 g_thread_create(math_equation_solve_real
, equation
, false, &error
);
1233 g_warning("Error spawning thread for calculations: %s\n", error
->message
);
1235 g_timeout_add(50, math_equation_look_for_answer
, equation
);
1236 g_timeout_add(100, math_equation_show_in_progress
, equation
);
1241 math_equation_factorize_real(gpointer data
)
1244 GList
*factors
, *factor
;
1246 MathEquation
*equation
= MATH_EQUATION(data
);
1247 SolveData
*result
= g_slice_new0(SolveData
);
1249 math_equation_get_number(equation
, &x
);
1250 factors
= mp_factorize(&x
);
1252 text
= g_string_new("");
1254 for (factor
= factors
; factor
; factor
= factor
->next
) {
1259 temp
= mp_serializer_to_string(equation
->priv
->serializer
, n
);
1260 g_string_append(text
, temp
);
1262 g_string_append(text
, "×");
1263 g_slice_free(MPNumber
, n
);
1266 g_list_free(factors
);
1268 result
->text_result
= g_strndup(text
->str
, text
->len
);
1269 g_async_queue_push(equation
->priv
->queue
, result
);
1270 g_string_free(text
, TRUE
);
1277 math_equation_factorize(MathEquation
*equation
)
1280 GError
*error
= NULL
;
1282 // FIXME: should replace calculation or give error message
1283 if (equation
->priv
->in_solve
)
1286 if (!math_equation_get_number(equation
, &x
) || !mp_is_integer(&x
)) {
1287 /* Error displayed when trying to factorize a non-integer value */
1288 math_equation_set_status(equation
, _("Need an integer to factorize"));
1292 equation
->priv
->in_solve
= true;
1294 g_thread_create(math_equation_factorize_real
, equation
, false, &error
);
1297 g_warning("Error spawning thread for calculations: %s\n", error
->message
);
1299 g_timeout_add(50, math_equation_look_for_answer
, equation
);
1300 g_timeout_add(100, math_equation_show_in_progress
, equation
);
1305 math_equation_delete(MathEquation
*equation
)
1308 GtkTextIter start
, end
;
1310 g_object_get(G_OBJECT(equation
), "cursor-position", &cursor
, NULL
);
1311 if (cursor
>= gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation
)))
1314 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &start
, cursor
);
1315 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &end
, cursor
+1);
1316 gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation
), &start
, &end
);
1321 math_equation_backspace(MathEquation
*equation
)
1323 /* Can't delete empty display */
1324 if (math_equation_is_empty(equation
))
1327 if (gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(equation
)))
1328 gtk_text_buffer_delete_selection(GTK_TEXT_BUFFER(equation
), FALSE
, FALSE
);
1331 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(equation
)));
1332 gtk_text_buffer_backspace(GTK_TEXT_BUFFER(equation
), &iter
, TRUE
, TRUE
);
1338 math_equation_clear(MathEquation
*equation
)
1340 math_equation_set_number_mode(equation
, NORMAL
);
1341 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), "", -1);
1342 clear_ans(equation
, FALSE
);
1347 math_equation_shift(MathEquation
*equation
, gint count
)
1351 if (!math_equation_get_number(equation
, &z
)) {
1352 math_equation_set_status(equation
,
1353 /* This message is displayed in the status bar when a bit
1354 shift operation is performed and the display does not contain a number */
1355 _("No sane value to bitwise shift"));
1359 mp_shift(&z
, count
, &z
);
1360 math_equation_set_number(equation
, &z
);
1365 math_equation_toggle_bit(MathEquation
*equation
, guint bit
)
1371 result
= math_equation_get_number(equation
, &x
);
1374 mp_set_from_unsigned_integer(G_MAXUINT64
, &max
);
1375 if (mp_is_negative(&x
) || mp_is_greater_than(&x
, &max
))
1378 bits
= mp_cast_to_unsigned_int(&x
);
1382 math_equation_set_status(equation
,
1383 /* Message displayed when cannot toggle bit in display*/
1384 _("Displayed value not an integer"));
1388 bits
^= (1LL << (63 - bit
));
1390 mp_set_from_unsigned_integer(bits
, &x
);
1392 // FIXME: Only do this if in ans format, otherwise set text in same format as previous number
1393 math_equation_set_number(equation
, &x
);
1398 math_equation_set_property(GObject
*object
,
1400 const GValue
*value
,
1405 self
= MATH_EQUATION(object
);
1409 math_equation_set_status(self
, g_value_get_string(value
));
1412 math_equation_set(self
, g_value_get_string(value
));
1414 case PROP_NUMBER_MODE
:
1415 math_equation_set_number_mode(self
, g_value_get_int(value
));
1418 math_equation_set_accuracy(self
, g_value_get_int(value
));
1420 case PROP_SHOW_THOUSANDS_SEPARATORS
:
1421 math_equation_set_show_thousands_separators(self
, g_value_get_boolean(value
));
1423 case PROP_SHOW_TRAILING_ZEROES
:
1424 math_equation_set_show_trailing_zeroes(self
, g_value_get_boolean(value
));
1426 case PROP_NUMBER_FORMAT
:
1427 math_equation_set_number_format(self
, g_value_get_int(value
));
1430 math_equation_set_base(self
, g_value_get_int(value
));
1432 case PROP_WORD_SIZE
:
1433 math_equation_set_word_size(self
, g_value_get_int(value
));
1435 case PROP_ANGLE_UNITS
:
1436 math_equation_set_angle_units(self
, g_value_get_int(value
));
1438 case PROP_SOURCE_CURRENCY
:
1439 math_equation_set_source_currency(self
, g_value_get_string(value
));
1441 case PROP_TARGET_CURRENCY
:
1442 math_equation_set_target_currency(self
, g_value_get_string(value
));
1444 case PROP_SOURCE_UNITS
:
1445 math_equation_set_source_units(self
, g_value_get_string(value
));
1447 case PROP_TARGET_UNITS
:
1448 math_equation_set_target_units(self
, g_value_get_string(value
));
1451 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
1458 math_equation_get_property(GObject
*object
,
1466 self
= MATH_EQUATION(object
);
1470 g_value_set_string(value
, self
->priv
->state
.status
);
1473 text
= math_equation_get_display(self
);
1474 g_value_set_string(value
, text
);
1478 text
= math_equation_get_equation(self
);
1479 g_value_set_string(value
, text
);
1482 case PROP_NUMBER_MODE
:
1483 g_value_set_enum(value
, self
->priv
->number_mode
);
1486 g_value_set_int(value
, mp_serializer_get_accuracy(self
->priv
->serializer
));
1488 case PROP_SHOW_THOUSANDS_SEPARATORS
:
1489 g_value_set_boolean(value
, mp_serializer_get_show_thousands_separators(self
->priv
->serializer
));
1491 case PROP_SHOW_TRAILING_ZEROES
:
1492 g_value_set_boolean(value
, mp_serializer_get_show_trailing_zeroes(self
->priv
->serializer
));
1494 case PROP_NUMBER_FORMAT
:
1495 g_value_set_enum(value
, mp_serializer_get_number_format(self
->priv
->serializer
));
1498 g_value_set_int(value
, math_equation_get_base(self
));
1500 case PROP_WORD_SIZE
:
1501 g_value_set_int(value
, self
->priv
->word_size
);
1503 case PROP_ANGLE_UNITS
:
1504 g_value_set_enum(value
, self
->priv
->angle_units
);
1506 case PROP_SOURCE_CURRENCY
:
1507 g_value_set_string(value
, self
->priv
->source_currency
);
1509 case PROP_TARGET_CURRENCY
:
1510 g_value_set_string(value
, self
->priv
->target_currency
);
1512 case PROP_SOURCE_UNITS
:
1513 g_value_set_string(value
, self
->priv
->source_units
);
1515 case PROP_TARGET_UNITS
:
1516 g_value_set_string(value
, self
->priv
->target_units
);
1518 case PROP_SERIALIZER
:
1519 g_value_set_object(value
, self
->priv
->serializer
);
1522 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
1529 math_equation_constructed(GObject
*object
)
1531 GtkTextBuffer
*parent_class
;
1532 parent_class
= g_type_class_peek_parent(MATH_EQUATION_GET_CLASS(object
));
1533 if (G_OBJECT_CLASS(parent_class
)->constructed
)
1534 G_OBJECT_CLASS(parent_class
)->constructed(object
);
1536 MATH_EQUATION(object
)->priv
->ans_tag
= gtk_text_buffer_create_tag(GTK_TEXT_BUFFER(object
), NULL
, "weight", PANGO_WEIGHT_BOLD
, NULL
);
1541 math_equation_class_init(MathEquationClass
*klass
)
1543 static GEnumValue number_mode_values
[] =
1545 {NORMAL
, "normal", "normal"},
1546 {SUPERSCRIPT
, "superscript", "superscript"},
1547 {SUBSCRIPT
, "subscript", "subscript"},
1550 static GEnumValue angle_unit_values
[] =
1552 {MP_RADIANS
, "radians", "radians"},
1553 {MP_DEGREES
, "degrees", "degrees"},
1554 {MP_GRADIANS
, "gradians", "gradians"},
1557 GObjectClass
*object_class
= G_OBJECT_CLASS(klass
);
1559 object_class
->get_property
= math_equation_get_property
;
1560 object_class
->set_property
= math_equation_set_property
;
1561 object_class
->constructed
= math_equation_constructed
;
1563 g_type_class_add_private(klass
, sizeof(MathEquationPrivate
));
1565 number_mode_type
= g_enum_register_static("NumberMode", number_mode_values
);
1566 number_format_type
= math_mp_display_format_get_type();
1567 angle_unit_type
= g_enum_register_static("AngleUnit", angle_unit_values
);
1569 g_object_class_install_property(object_class
,
1571 g_param_spec_string("status",
1575 G_PARAM_READWRITE
));
1576 g_object_class_install_property(object_class
,
1578 g_param_spec_string("display",
1580 "Displayed equation text",
1582 G_PARAM_READWRITE
));
1583 g_object_class_install_property(object_class
,
1585 g_param_spec_string("equation",
1590 g_object_class_install_property(object_class
,
1592 g_param_spec_enum("number-mode",
1594 "Input number mode",
1597 G_PARAM_READWRITE
));
1598 g_object_class_install_property(object_class
,
1600 g_param_spec_int("accuracy",
1604 G_PARAM_READWRITE
));
1605 g_object_class_install_property(object_class
,
1606 PROP_SHOW_THOUSANDS_SEPARATORS
,
1607 g_param_spec_boolean("show-thousands-separators",
1608 "show-thousands-separators",
1609 "Show thousands separators",
1611 G_PARAM_READWRITE
));
1612 g_object_class_install_property(object_class
,
1613 PROP_SHOW_TRAILING_ZEROES
,
1614 g_param_spec_boolean("show-trailing-zeroes",
1615 "show-trailing-zeroes",
1616 "Show trailing zeroes",
1618 G_PARAM_READWRITE
));
1619 g_object_class_install_property(object_class
,
1621 g_param_spec_enum("number-format",
1625 MP_DISPLAY_FORMAT_FIXED
,
1626 G_PARAM_READWRITE
));
1627 g_object_class_install_property(object_class
,
1629 g_param_spec_int("base",
1631 "Default number base (derived from number-format)",
1633 G_PARAM_READWRITE
));
1634 g_object_class_install_property(object_class
,
1636 g_param_spec_int("word-size",
1638 "Word size in bits",
1640 G_PARAM_READWRITE
));
1641 g_object_class_install_property(object_class
,
1643 g_param_spec_enum("angle-units",
1648 G_PARAM_READWRITE
));
1649 g_object_class_install_property(object_class
,
1650 PROP_SOURCE_CURRENCY
,
1651 g_param_spec_string("source-currency",
1655 G_PARAM_READWRITE
));
1656 g_object_class_install_property(object_class
,
1657 PROP_TARGET_CURRENCY
,
1658 g_param_spec_string("target-currency",
1662 G_PARAM_READWRITE
));
1663 g_object_class_install_property(object_class
,
1665 g_param_spec_string("source-units",
1669 G_PARAM_READWRITE
));
1670 g_object_class_install_property(object_class
,
1672 g_param_spec_string("target-units",
1676 G_PARAM_READWRITE
));
1677 g_object_class_install_property(object_class
,
1679 g_param_spec_object("serializer",
1688 pre_insert_text_cb(MathEquation
*equation
,
1689 GtkTextIter
*location
,
1696 if (equation
->priv
->in_reformat
)
1699 /* If following a delete then have already pushed undo stack (GtkTextBuffer
1700 doesn't indicate replace operations so we have to infer them) */
1701 if (!equation
->priv
->in_delete
)
1702 math_equation_push_undo_stack(equation
);
1704 /* Clear result on next digit entered if cursor at end of line */
1706 c
= g_utf8_get_char(text
);
1707 if ((g_unichar_isdigit(c
) || c
== mp_serializer_get_radix(equation
->priv
->serializer
)) &&
1708 math_equation_is_result(equation
)) {
1709 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), "", -1);
1710 clear_ans(equation
, FALSE
);
1711 gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(equation
), location
);
1714 if (equation
->priv
->ans_start
) {
1715 gint ans_start
, ans_end
;
1718 offset
= gtk_text_iter_get_offset(location
);
1719 get_ans_offsets(equation
, &ans_start
, &ans_end
);
1721 /* Inserted inside ans */
1722 if (offset
> ans_start
&& offset
< ans_end
)
1723 clear_ans(equation
, TRUE
);
1729 on_delete(MathEquation
*equation
)
1731 equation
->priv
->in_delete
= FALSE
;
1737 pre_delete_range_cb(MathEquation
*equation
,
1742 if (equation
->priv
->in_reformat
)
1745 math_equation_push_undo_stack(equation
);
1747 equation
->priv
->in_delete
= TRUE
;
1748 g_idle_add((GSourceFunc
)on_delete
, equation
);
1750 if (equation
->priv
->ans_start
) {
1751 gint ans_start
, ans_end
;
1752 gint start_offset
, end_offset
;
1754 start_offset
= gtk_text_iter_get_offset(start
);
1755 end_offset
= gtk_text_iter_get_offset(end
);
1756 get_ans_offsets(equation
, &ans_start
, &ans_end
);
1758 /* Deleted part of ans */
1759 if (start_offset
< ans_end
&& end_offset
> ans_start
)
1760 clear_ans(equation
, TRUE
);
1766 insert_text_cb(MathEquation
*equation
,
1767 GtkTextIter
*location
,
1772 if (equation
->priv
->in_reformat
)
1775 equation
->priv
->state
.entered_multiply
= strcmp(text
, "×") == 0;
1777 /* Update thousands separators */
1778 reformat_separators(equation
);
1780 g_object_notify(G_OBJECT(equation
), "display");
1785 delete_range_cb(MathEquation
*equation
,
1790 if (equation
->priv
->in_reformat
)
1793 equation
->priv
->state
.entered_multiply
= FALSE
;
1795 /* Update thousands separators */
1796 reformat_separators(equation
);
1798 // FIXME: A replace will emit this both for delete-range and insert-text, can it be avoided?
1799 g_object_notify(G_OBJECT(equation
), "display");
1804 math_equation_init(MathEquation
*equation
)
1806 /* Digits localized for the given language */
1807 const char *digit_values
= _("0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F");
1808 const char *default_digits
[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
1810 /* Default to using untranslated digits, this is because it doesn't make sense in most languages and we need to make this optional.
1811 * See https://bugzilla.gnome.org/show_bug.cgi?id=632661 */
1812 gboolean use_default_digits
= TRUE
;
1815 equation
->priv
= G_TYPE_INSTANCE_GET_PRIVATE(equation
, math_equation_get_type(), MathEquationPrivate
);
1817 g_signal_connect(equation
, "insert-text", G_CALLBACK(pre_insert_text_cb
), equation
);
1818 g_signal_connect(equation
, "delete-range", G_CALLBACK(pre_delete_range_cb
), equation
);
1819 g_signal_connect_after(equation
, "insert-text", G_CALLBACK(insert_text_cb
), equation
);
1820 g_signal_connect_after(equation
, "delete-range", G_CALLBACK(delete_range_cb
), equation
);
1822 digits
= g_strsplit(digit_values
, ",", -1);
1823 for (i
= 0; i
< 16; i
++) {
1824 if (use_default_digits
|| digits
[i
] == NULL
) {
1825 use_default_digits
= TRUE
;
1826 equation
->priv
->digits
[i
] = g_utf8_get_char(default_digits
[i
]);
1829 equation
->priv
->digits
[i
] = g_utf8_get_char(digits
[i
]);
1833 equation
->priv
->variables
= math_variables_new();
1835 equation
->priv
->state
.status
= g_strdup("");
1836 equation
->priv
->word_size
= 32;
1837 equation
->priv
->angle_units
= MP_DEGREES
;
1838 // FIXME: Pick based on locale
1839 equation
->priv
->source_currency
= g_strdup("");
1840 equation
->priv
->target_currency
= g_strdup("");
1841 equation
->priv
->source_units
= g_strdup("");
1842 equation
->priv
->target_units
= g_strdup("");
1843 equation
->priv
->serializer
= mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC
, 10, 9);
1844 equation
->priv
->queue
= g_async_queue_new();
1846 mp_set_from_integer(0, &equation
->priv
->state
.ans
);