1 /* Copyright (c) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
2 * Copyright (c) 2008-2009 Robert Ancell
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 #include <glib/gi18n.h>
29 #include "math-equation.h"
32 #include "mp-equation.h"
33 #include "mp-serializer.h"
34 #include "math-enums.h"
44 PROP_SHOW_THOUSANDS_SEPARATORS
,
45 PROP_SHOW_TRAILING_ZEROES
,
57 static GType number_mode_type
, number_format_type
, angle_unit_type
;
59 #define MAX_DIGITS 512
61 /* Expression mode state */
63 MPNumber ans
; /* Previously calculated answer */
64 gchar
*expression
; /* Expression entered by user */
65 gint ans_start
, ans_end
; /* Start and end characters for ans variable in expression */
66 gint cursor
; /* ??? */
67 NumberMode number_mode
; /* ??? */
68 gboolean can_super_minus
; /* TRUE if entering minus can generate a superscript minus */
69 gboolean entered_multiply
; /* Last insert was a multiply character */
70 gchar
*status
; /* Equation status */
73 struct MathEquationPrivate
77 gint word_size
; /* Word size in bits */
78 MPAngleUnit angle_units
; /* Units for trigonometric functions */
79 char *source_currency
;
80 char *target_currency
;
83 NumberMode number_mode
; /* ??? */
84 gboolean can_super_minus
; /* TRUE if entering minus can generate a superscript minus */
86 gunichar digits
[16]; /* Localized digits */
88 GtkTextMark
*ans_start
, *ans_end
;
90 MathEquationState state
; /* Equation state */
91 GList
*undo_stack
; /* History of expression mode states */
93 gboolean in_undo_operation
;
101 MathVariables
*variables
;
102 UnitManager
*unit_manager
;
103 MpSerializer
*serializer
;
109 MPNumber
*number_result
;
114 G_DEFINE_TYPE (MathEquation
, math_equation
, GTK_TYPE_TEXT_BUFFER
);
120 return g_object_new(math_equation_get_type(), NULL
);
125 math_equation_get_variables(MathEquation
*equation
)
127 return equation
->priv
->variables
;
132 math_equation_get_unit_manager(MathEquation
*equation
)
134 return equation
->priv
->unit_manager
;
139 get_ans_offsets(MathEquation
*equation
, gint
*start
, gint
*end
)
143 if (!equation
->priv
->ans_start
) {
148 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, equation
->priv
->ans_start
);
149 *start
= gtk_text_iter_get_offset(&iter
);
150 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, equation
->priv
->ans_end
);
151 *end
= gtk_text_iter_get_offset(&iter
);
156 reformat_ans(MathEquation
*equation
)
158 if (!equation
->priv
->ans_start
)
161 gchar
*orig_ans_text
;
163 GtkTextIter ans_start
, ans_end
;
165 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &ans_start
, equation
->priv
->ans_start
);
166 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &ans_end
, equation
->priv
->ans_end
);
167 orig_ans_text
= gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation
), &ans_start
, &ans_end
, FALSE
);
168 ans_text
= mp_serializer_to_string(equation
->priv
->serializer
, &equation
->priv
->state
.ans
);
169 if (strcmp(orig_ans_text
, ans_text
) != 0) {
172 equation
->priv
->in_undo_operation
= TRUE
;
173 equation
->priv
->in_reformat
= TRUE
;
175 start
= gtk_text_iter_get_offset(&ans_start
);
176 gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation
), &ans_start
, &ans_end
);
177 gtk_text_buffer_insert_with_tags(GTK_TEXT_BUFFER(equation
), &ans_end
, ans_text
, -1, equation
->priv
->ans_tag
, NULL
);
179 /* There seems to be a bug in the marks as they alternate being the correct and incorrect ways. Reset them */
180 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &ans_start
, start
);
181 gtk_text_buffer_move_mark(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_start
, &ans_start
);
182 gtk_text_buffer_move_mark(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_end
, &ans_end
);
184 equation
->priv
->in_reformat
= FALSE
;
185 equation
->priv
->in_undo_operation
= FALSE
;
187 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &ans_start
, equation
->priv
->ans_start
);
188 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &ans_end
, equation
->priv
->ans_end
);
189 g_free(orig_ans_text
);
195 count_digits(MathEquation
*equation
, const gchar
*text
)
197 const gchar
*read_iter
;
201 while (*read_iter
!= '\0') {
202 if (!g_unichar_isdigit(g_utf8_get_char(read_iter
)))
205 read_iter
= g_utf8_next_char(read_iter
);
207 /* Allow a thousands separator between digits follow a digit */
208 if (g_utf8_get_char(read_iter
) == mp_serializer_get_thousands_separator(equation
->priv
->serializer
)) {
209 read_iter
= g_utf8_next_char(read_iter
);
210 if (!g_unichar_isdigit(g_utf8_get_char(read_iter
)))
222 reformat_separators(MathEquation
*equation
)
224 gchar
*text
, *read_iter
;
225 gint ans_start
, ans_end
;
226 gint offset
, digit_offset
= 0;
227 gboolean in_number
= FALSE
, in_radix
= FALSE
, last_is_tsep
= FALSE
;
229 equation
->priv
->in_undo_operation
= TRUE
;
230 equation
->priv
->in_reformat
= TRUE
;
232 text
= math_equation_get_display(equation
);
233 get_ans_offsets(equation
, &ans_start
, &ans_end
);
234 for (read_iter
= text
, offset
= 0; *read_iter
!= '\0'; read_iter
= g_utf8_next_char(read_iter
), offset
++) {
236 gboolean expect_tsep
;
238 /* See what digit this character is */
239 c
= g_utf8_get_char(read_iter
);
241 expect_tsep
= mp_serializer_get_show_thousands_separators(equation
->priv
->serializer
) &&
242 in_number
&& !in_radix
&& !last_is_tsep
&&
243 digit_offset
> 0 && digit_offset
% mp_serializer_get_thousands_separator_count(equation
->priv
->serializer
) == 0;
244 last_is_tsep
= FALSE
;
246 /* Don't mess with ans */
247 if (offset
>= ans_start
&& offset
<= ans_end
) {
248 in_number
= in_radix
= FALSE
;
251 if (g_unichar_isdigit(c
)) {
253 digit_offset
= count_digits(equation
, read_iter
);
256 /* Expected a thousands separator between these digits - insert it */
262 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &iter
, offset
);
263 len
= g_unichar_to_utf8(mp_serializer_get_thousands_separator(equation
->priv
->serializer
), buffer
);
265 gtk_text_buffer_insert(GTK_TEXT_BUFFER(equation
), &iter
, buffer
, -1);
272 else if (c
== mp_serializer_get_radix(equation
->priv
->serializer
)) {
273 in_number
= in_radix
= TRUE
;
275 else if (c
== mp_serializer_get_thousands_separator(equation
->priv
->serializer
)) {
276 /* Didn't expect thousands separator - delete it */
277 if (!expect_tsep
&& in_number
) {
278 GtkTextIter start
, end
;
279 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &start
, offset
);
280 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &end
, offset
+ 1);
281 gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation
), &start
, &end
);
288 in_number
= in_radix
= FALSE
;
294 equation
->priv
->in_reformat
= FALSE
;
295 equation
->priv
->in_undo_operation
= FALSE
;
300 reformat_display(MathEquation
*equation
, gint old_base
)
303 reformat_ans(equation
);
305 /* Add/remove thousands separators */
306 reformat_separators(equation
);
310 static MathEquationState
*
311 get_current_state(MathEquation
*equation
)
313 MathEquationState
*state
;
314 gint ans_start
= -1, ans_end
= -1;
316 state
= g_malloc0(sizeof(MathEquationState
));
318 if (equation
->priv
->ans_start
)
321 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, equation
->priv
->ans_start
);
322 ans_start
= gtk_text_iter_get_offset(&iter
);
323 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, equation
->priv
->ans_end
);
324 ans_end
= gtk_text_iter_get_offset(&iter
);
327 mp_set_from_mp(&equation
->priv
->state
.ans
, &state
->ans
);
328 state
->expression
= math_equation_get_display(equation
);
329 state
->ans_start
= ans_start
;
330 state
->ans_end
= ans_end
;
331 g_object_get(G_OBJECT(equation
), "cursor-position", &state
->cursor
, NULL
);
332 state
->number_mode
= equation
->priv
->number_mode
;
333 state
->can_super_minus
= equation
->priv
->can_super_minus
;
334 state
->entered_multiply
= equation
->priv
->state
.entered_multiply
;
335 state
->status
= g_strdup(equation
->priv
->state
.status
);
342 free_state(MathEquationState
*state
)
344 g_free(state
->expression
);
345 g_free(state
->status
);
351 math_equation_push_undo_stack(MathEquation
*equation
)
354 MathEquationState
*state
;
356 if (equation
->priv
->in_undo_operation
)
359 math_equation_set_status(equation
, "");
361 /* Can't redo anymore */
362 for (link
= equation
->priv
->redo_stack
; link
; link
= link
->next
) {
366 g_list_free(equation
->priv
->redo_stack
);
367 equation
->priv
->redo_stack
= NULL
;
369 state
= get_current_state(equation
);
370 equation
->priv
->undo_stack
= g_list_prepend(equation
->priv
->undo_stack
, state
);
375 clear_ans(MathEquation
*equation
, gboolean remove_tag
)
377 if (!equation
->priv
->ans_start
)
381 GtkTextIter start
, end
;
383 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &start
, equation
->priv
->ans_start
);
384 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &end
, equation
->priv
->ans_end
);
385 gtk_text_buffer_remove_tag(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_tag
, &start
, &end
);
388 gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_start
);
389 gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_end
);
390 equation
->priv
->ans_start
= NULL
;
391 equation
->priv
->ans_end
= NULL
;
396 apply_state(MathEquation
*equation
, MathEquationState
*state
)
400 /* Disable undo detection */
401 equation
->priv
->in_undo_operation
= TRUE
;
403 mp_set_from_mp(&state
->ans
, &equation
->priv
->state
.ans
);
405 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), state
->expression
, -1);
406 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &cursor
, state
->cursor
);
407 gtk_text_buffer_place_cursor(GTK_TEXT_BUFFER(equation
), &cursor
);
408 clear_ans(equation
, FALSE
);
409 if (state
->ans_start
>= 0) {
410 GtkTextIter start
, end
;
412 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &start
, state
->ans_start
);
413 equation
->priv
->ans_start
= gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation
), NULL
, &start
, FALSE
);
414 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &end
, state
->ans_end
);
415 equation
->priv
->ans_end
= gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation
), NULL
, &end
, TRUE
);
416 gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_tag
, &start
, &end
);
419 math_equation_set_number_mode(equation
, state
->number_mode
);
420 equation
->priv
->can_super_minus
= state
->can_super_minus
;
421 equation
->priv
->state
.entered_multiply
= state
->entered_multiply
;
422 math_equation_set_status(equation
, state
->status
);
424 equation
->priv
->in_undo_operation
= FALSE
;
429 math_equation_copy(MathEquation
*equation
)
431 GtkTextIter start
, end
;
434 if (!gtk_text_buffer_get_selection_bounds(GTK_TEXT_BUFFER(equation
), &start
, &end
))
435 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation
), &start
, &end
);
437 text
= gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation
), &start
, &end
, FALSE
);
438 gtk_clipboard_set_text(gtk_clipboard_get(GDK_NONE
), text
, -1);
444 on_paste(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
446 MathEquation
*equation
= data
;
448 math_equation_insert(equation
, text
);
453 math_equation_paste(MathEquation
*equation
)
455 gtk_clipboard_request_text(gtk_clipboard_get(GDK_NONE
), on_paste
, equation
);
460 math_equation_undo(MathEquation
*equation
)
463 MathEquationState
*state
;
465 if (!equation
->priv
->undo_stack
) {
466 math_equation_set_status(equation
,
467 /* Error shown when trying to undo with no undo history */
468 _("No undo history"));
472 link
= equation
->priv
->undo_stack
;
473 equation
->priv
->undo_stack
= g_list_remove_link(equation
->priv
->undo_stack
, link
);
477 equation
->priv
->redo_stack
= g_list_prepend(equation
->priv
->redo_stack
, get_current_state(equation
));
479 apply_state(equation
, state
);
485 math_equation_redo(MathEquation
*equation
)
488 MathEquationState
*state
;
490 if (!equation
->priv
->redo_stack
) {
491 math_equation_set_status(equation
,
492 /* Error shown when trying to redo with no redo history */
493 _("No redo history"));
497 link
= equation
->priv
->redo_stack
;
498 equation
->priv
->redo_stack
= g_list_remove_link(equation
->priv
->redo_stack
, link
);
502 equation
->priv
->undo_stack
= g_list_prepend(equation
->priv
->undo_stack
, get_current_state(equation
));
504 apply_state(equation
, state
);
510 math_equation_get_digit_text(MathEquation
*equation
, guint digit
)
512 return equation
->priv
->digits
[digit
];
517 math_equation_set_accuracy(MathEquation
*equation
, gint accuracy
)
519 gint old_accuracy
= mp_serializer_get_accuracy(equation
->priv
->serializer
);
520 if (old_accuracy
== accuracy
)
522 mp_serializer_set_accuracy(equation
->priv
->serializer
, accuracy
);
523 reformat_display(equation
, mp_serializer_get_base(equation
->priv
->serializer
));
524 g_object_notify(G_OBJECT(equation
), "accuracy");
529 math_equation_get_accuracy(MathEquation
*equation
)
531 return mp_serializer_get_accuracy(equation
->priv
->serializer
);
536 math_equation_set_show_thousands_separators(MathEquation
*equation
, gboolean visible
)
538 gboolean old_visible
= mp_serializer_get_show_thousands_separators(equation
->priv
->serializer
);
539 if (old_visible
== visible
)
541 mp_serializer_set_show_thousands_separators(equation
->priv
->serializer
, visible
);
542 reformat_display(equation
, mp_serializer_get_base(equation
->priv
->serializer
));
543 g_object_notify(G_OBJECT(equation
), "show-thousands-separators");
548 math_equation_get_show_thousands_separators(MathEquation
*equation
)
550 return mp_serializer_get_show_thousands_separators(equation
->priv
->serializer
);
555 math_equation_set_show_trailing_zeroes(MathEquation
*equation
, gboolean visible
)
557 gboolean old_visible
= mp_serializer_get_show_trailing_zeroes(equation
->priv
->serializer
);
558 if (old_visible
== visible
)
560 mp_serializer_set_show_trailing_zeroes(equation
->priv
->serializer
, visible
);
561 reformat_display(equation
, mp_serializer_get_base(equation
->priv
->serializer
));
562 g_object_notify(G_OBJECT(equation
), "show-trailing-zeroes");
567 math_equation_get_show_trailing_zeroes(MathEquation
*equation
)
569 return mp_serializer_get_show_trailing_zeroes(equation
->priv
->serializer
);
574 math_equation_set_number_format(MathEquation
*equation
, MpDisplayFormat format
)
576 MpDisplayFormat old_format
= mp_serializer_get_number_format(equation
->priv
->serializer
);
577 if (old_format
== format
)
580 mp_serializer_set_number_format(equation
->priv
->serializer
, format
);
581 reformat_display(equation
, mp_serializer_get_base(equation
->priv
->serializer
));
582 g_object_notify(G_OBJECT(equation
), "number-format");
587 math_equation_get_number_format(MathEquation
*equation
)
589 return mp_serializer_get_number_format(equation
->priv
->serializer
);
594 math_equation_set_base(MathEquation
*equation
, gint base
)
596 gint old_base
= mp_serializer_get_base(equation
->priv
->serializer
);
598 if (old_base
== base
)
601 mp_serializer_set_base(equation
->priv
->serializer
, base
);
602 reformat_display(equation
, old_base
);
603 g_object_notify(G_OBJECT(equation
), "base");
608 math_equation_get_base(MathEquation
*equation
)
610 return mp_serializer_get_base(equation
->priv
->serializer
);
615 math_equation_set_word_size(MathEquation
*equation
, gint word_size
)
617 if (equation
->priv
->word_size
== word_size
)
619 equation
->priv
->word_size
= word_size
;
620 g_object_notify(G_OBJECT(equation
), "word-size");
625 math_equation_get_word_size(MathEquation
*equation
)
627 return equation
->priv
->word_size
;
632 math_equation_set_angle_units(MathEquation
*equation
, MPAngleUnit angle_units
)
634 if (equation
->priv
->angle_units
== angle_units
)
636 equation
->priv
->angle_units
= angle_units
;
637 g_object_notify(G_OBJECT(equation
), "angle-units");
642 math_equation_get_angle_units(MathEquation
*equation
)
644 return equation
->priv
->angle_units
;
649 math_equation_set_source_currency(MathEquation
*equation
, const gchar
*currency
)
651 if (strcmp(equation
->priv
->source_currency
, currency
) == 0)
653 g_free(equation
->priv
->source_currency
);
654 equation
->priv
->source_currency
= g_strdup(currency
);
655 g_object_notify(G_OBJECT(equation
), "source-currency");
660 math_equation_get_source_currency(MathEquation
*equation
)
662 return equation
->priv
->source_currency
;
667 math_equation_set_target_currency(MathEquation
*equation
, const gchar
*currency
)
669 if (strcmp(equation
->priv
->target_currency
, currency
) == 0)
671 g_free(equation
->priv
->target_currency
);
672 equation
->priv
->target_currency
= g_strdup(currency
);
673 g_object_notify(G_OBJECT(equation
), "target-currency");
678 math_equation_get_target_currency(MathEquation
*equation
)
680 return equation
->priv
->target_currency
;
685 math_equation_set_source_units(MathEquation
*equation
, const gchar
*units
)
687 if (strcmp(equation
->priv
->source_units
, units
) == 0)
689 g_free(equation
->priv
->source_units
);
690 equation
->priv
->source_units
= g_strdup(units
);
691 g_object_notify(G_OBJECT(equation
), "source-units");
695 math_equation_get_source_units(MathEquation
*equation
)
697 return equation
->priv
->source_units
;
702 math_equation_set_target_units(MathEquation
*equation
, const gchar
*units
)
704 if (strcmp(equation
->priv
->target_units
, units
) == 0)
706 g_free(equation
->priv
->target_units
);
707 equation
->priv
->target_units
= g_strdup(units
);
708 g_object_notify(G_OBJECT(equation
), "target-units");
713 math_equation_get_target_units(MathEquation
*equation
)
715 return equation
->priv
->target_units
;
720 math_equation_set_status(MathEquation
*equation
, const gchar
*status
)
722 if (strcmp(equation
->priv
->state
.status
, status
) == 0)
725 g_free(equation
->priv
->state
.status
);
726 equation
->priv
->state
.status
= g_strdup(status
);
727 g_object_notify(G_OBJECT(equation
), "status");
732 math_equation_get_status(MathEquation
*equation
)
734 return equation
->priv
->state
.status
;
739 math_equation_is_empty(MathEquation
*equation
)
741 return gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation
)) == 0;
746 math_equation_is_result(MathEquation
*equation
)
751 text
= math_equation_get_equation(equation
);
752 result
= strcmp(text
, "ans") == 0;
760 math_equation_get_display(MathEquation
*equation
)
762 GtkTextIter start
, end
;
764 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation
), &start
, &end
);
765 return gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation
), &start
, &end
, FALSE
);
770 math_equation_get_equation(MathEquation
*equation
)
774 gint ans_start
= -1, ans_end
= -1, offset
;
775 const gchar
*read_iter
;
776 gboolean last_is_digit
= FALSE
;
778 text
= math_equation_get_display(equation
);
779 eq_text
= g_string_sized_new(strlen(text
));
781 if (equation
->priv
->ans_start
)
782 get_ans_offsets(equation
, &ans_start
, &ans_end
);
784 for (read_iter
= text
, offset
= 0; *read_iter
!= '\0'; read_iter
= g_utf8_next_char(read_iter
), offset
++) {
786 gboolean is_digit
, next_is_digit
;
788 c
= g_utf8_get_char(read_iter
);
789 is_digit
= g_unichar_isdigit(c
);
790 next_is_digit
= g_unichar_isdigit(g_utf8_get_char(g_utf8_next_char(read_iter
)));
792 /* Replace ans text with variable */
793 if (offset
== ans_start
) {
794 g_string_append(eq_text
, "ans");
795 read_iter
= g_utf8_offset_to_pointer(read_iter
, ans_end
- ans_start
- 1);
796 offset
+= ans_end
- ans_start
- 1;
801 /* Ignore thousands separators */
802 if (c
== mp_serializer_get_thousands_separator(equation
->priv
->serializer
) && last_is_digit
&& next_is_digit
)
804 /* Substitute radix character */
805 else if (c
== mp_serializer_get_radix(equation
->priv
->serializer
) && (last_is_digit
|| next_is_digit
))
806 g_string_append_unichar(eq_text
, '.');
808 g_string_append_unichar(eq_text
, c
);
810 last_is_digit
= is_digit
;
815 g_string_free(eq_text
, FALSE
);
822 math_equation_get_number(MathEquation
*equation
, MPNumber
*z
)
827 text
= math_equation_get_equation(equation
);
828 result
= !mp_serializer_from_string(equation
->priv
->serializer
, text
, z
);
836 math_equation_get_serializer(MathEquation
*equation
)
838 return equation
->priv
->serializer
;
843 math_equation_set_number_mode(MathEquation
*equation
, NumberMode mode
)
845 if (equation
->priv
->number_mode
== mode
)
848 equation
->priv
->can_super_minus
= mode
== SUPERSCRIPT
;
850 equation
->priv
->number_mode
= mode
;
851 g_object_notify(G_OBJECT(equation
), "number-mode");
856 math_equation_get_number_mode(MathEquation
*equation
)
858 return equation
->priv
->number_mode
;
863 math_equation_in_solve(MathEquation
*equation
)
865 return equation
->priv
->in_solve
;
870 math_equation_get_answer(MathEquation
*equation
)
872 return &equation
->priv
->state
.ans
;
877 math_equation_store(MathEquation
*equation
, const gchar
*name
)
881 if (!math_equation_get_number(equation
, &t
))
882 math_equation_set_status(equation
, _("No sane value to store"));
884 math_variables_set(equation
->priv
->variables
, name
, &t
);
889 math_equation_recall(MathEquation
*equation
, const gchar
*name
)
891 math_equation_insert(equation
, name
);
896 math_equation_set(MathEquation
*equation
, const gchar
*text
)
899 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), text
, -1);
900 clear_ans(equation
, FALSE
);
905 math_equation_set_number(MathEquation
*equation
, const MPNumber
*x
)
908 GtkTextIter start
, end
;
910 /* Show the number in the user chosen format */
911 text
= mp_serializer_to_string(equation
->priv
->serializer
, x
);
912 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), text
, -1);
913 mp_set_from_mp(x
, &equation
->priv
->state
.ans
);
915 /* Mark this text as the answer variable */
916 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation
), &start
, &end
);
917 clear_ans(equation
, FALSE
);
918 equation
->priv
->ans_start
= gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation
), NULL
, &start
, FALSE
);
919 equation
->priv
->ans_end
= gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation
), NULL
, &end
, TRUE
);
920 gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(equation
), equation
->priv
->ans_tag
, &start
, &end
);
926 math_equation_insert(MathEquation
*equation
, const gchar
*text
)
928 /* Replace ** with ^ (not on all keyboards) */
929 if (!gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(equation
)) &&
930 strcmp(text
, "×") == 0 && equation
->priv
->state
.entered_multiply
) {
933 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(equation
)));
934 gtk_text_buffer_backspace(GTK_TEXT_BUFFER(equation
), &iter
, TRUE
, TRUE
);
935 gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(equation
), "^", -1);
939 /* Start new equation when entering digits after existing result */
940 if(math_equation_is_result(equation
) && g_unichar_isdigit(g_utf8_get_char(text
)))
941 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), "", -1);
943 /* Can't enter superscript minus after entering digits */
944 if (strstr("⁰¹²³⁴⁵⁶⁷⁸⁹", text
) != NULL
|| strcmp("⁻", text
) == 0)
945 equation
->priv
->can_super_minus
= FALSE
;
947 /* Disable super/subscript mode when finished entering */
948 if (strstr("⁻⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉", text
) == NULL
)
949 math_equation_set_number_mode(equation
, NORMAL
);
951 gtk_text_buffer_delete_selection(GTK_TEXT_BUFFER(equation
), FALSE
, FALSE
);
952 gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(equation
), text
, -1);
957 math_equation_insert_digit(MathEquation
*equation
, guint digit
)
959 static const char *subscript_digits
[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉", NULL
};
960 static const char *superscript_digits
[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹", NULL
};
962 if (equation
->priv
->number_mode
== NORMAL
|| digit
>= 10) {
965 len
= g_unichar_to_utf8(math_equation_get_digit_text(equation
, digit
), buffer
);
967 math_equation_insert(equation
, buffer
);
969 else if (equation
->priv
->number_mode
== SUPERSCRIPT
)
970 math_equation_insert(equation
, superscript_digits
[digit
]);
971 else if (equation
->priv
->number_mode
== SUBSCRIPT
)
972 math_equation_insert(equation
, subscript_digits
[digit
]);
977 math_equation_insert_numeric_point(MathEquation
*equation
)
981 len
= g_unichar_to_utf8(mp_serializer_get_radix(equation
->priv
->serializer
), buffer
);
983 math_equation_insert(equation
, buffer
);
988 math_equation_insert_number(MathEquation
*equation
, const MPNumber
*x
)
991 text
= mp_serializer_to_string(equation
->priv
->serializer
, x
);
992 math_equation_insert(equation
, text
);
998 math_equation_insert_exponent(MathEquation
*equation
)
1000 math_equation_insert(equation
, "×10");
1001 math_equation_set_number_mode(equation
, SUPERSCRIPT
);
1006 math_equation_insert_subtract(MathEquation
*equation
)
1008 if (equation
->priv
->number_mode
== SUPERSCRIPT
&& equation
->priv
->can_super_minus
) {
1009 math_equation_insert(equation
, "⁻");
1010 equation
->priv
->can_super_minus
= FALSE
;
1013 math_equation_insert(equation
, "−");
1014 math_equation_set_number_mode(equation
, NORMAL
);
1020 variable_is_defined(const char *name
, void *data
)
1022 MathEquation
*equation
= data
;
1023 char *c
, *lower_name
;
1025 lower_name
= strdup(name
);
1026 for (c
= lower_name
; *c
; c
++)
1029 if (strcmp(lower_name
, "rand") == 0 ||
1030 strcmp(lower_name
, "ans") == 0) {
1036 return math_variables_get(equation
->priv
->variables
, name
) != NULL
;
1041 get_variable(const char *name
, MPNumber
*z
, void *data
)
1043 char *c
, *lower_name
;
1045 MathEquation
*equation
= data
;
1048 lower_name
= strdup(name
);
1049 for (c
= lower_name
; *c
; c
++)
1052 if (strcmp(lower_name
, "rand") == 0)
1053 mp_set_from_random(z
);
1054 else if (strcmp(lower_name
, "ans") == 0)
1055 mp_set_from_mp(&equation
->priv
->state
.ans
, z
);
1057 t
= math_variables_get(equation
->priv
->variables
, name
);
1059 mp_set_from_mp(t
, z
);
1071 set_variable(const char *name
, const MPNumber
*x
, void *data
)
1073 MathEquation
*equation
= data
;
1074 /* FIXME: Don't allow writing to built-in variables, e.g. ans, rand, sin, ... */
1075 math_variables_set(equation
->priv
->variables
, name
, x
);
1080 convert(const MPNumber
*x
, const char *x_units
, const char *z_units
, MPNumber
*z
, void *data
)
1082 MathEquation
*equation
= data
;
1083 return unit_manager_convert(equation
->priv
->unit_manager
, x
, x_units
, z_units
, z
);
1088 parse(MathEquation
*equation
, const char *text
, MPNumber
*z
, char **error_token
)
1090 MPEquationOptions options
;
1092 memset(&options
, 0, sizeof(options
));
1093 options
.base
= mp_serializer_get_base(equation
->priv
->serializer
);
1094 options
.wordlen
= equation
->priv
->word_size
;
1095 options
.angle_units
= equation
->priv
->angle_units
;
1096 options
.variable_is_defined
= variable_is_defined
;
1097 options
.get_variable
= get_variable
;
1098 options
.set_variable
= set_variable
;
1099 options
.convert
= convert
;
1100 options
.callback_data
= equation
;
1102 return mp_equation_parse(text
, &options
, z
, error_token
);
1107 * Executed in separate thread. It is thus not a good idea to write to anything
1108 * in MathEquation but the async queue from here.
1111 math_equation_solve_real(gpointer data
)
1113 MathEquation
*equation
= MATH_EQUATION(data
);
1114 SolveData
*solvedata
= g_slice_new0(SolveData
);
1116 gint n_brackets
= 0, result
;
1117 gchar
*c
, *text
, *error_token
;
1118 GString
*equation_text
;
1121 text
= math_equation_get_equation(equation
);
1122 equation_text
= g_string_new(text
);
1124 /* Count the number of brackets and automatically add missing closing brackets */
1125 for (c
= equation_text
->str
; *c
; c
++) {
1131 while (n_brackets
> 0) {
1132 g_string_append_c(equation_text
, ')');
1137 result
= parse(equation
, equation_text
->str
, &z
, &error_token
);
1138 g_string_free(equation_text
, TRUE
);
1141 case PARSER_ERR_NONE
:
1142 solvedata
->number_result
= g_slice_new(MPNumber
);
1143 mp_set_from_mp(&z
, solvedata
->number_result
);
1146 case PARSER_ERR_OVERFLOW
:
1147 solvedata
->error
= g_strdup(/* Error displayed to user when they perform a bitwise operation on numbers greater than the current word */
1148 _("Overflow. Try a bigger word size"));
1151 case PARSER_ERR_UNKNOWN_VARIABLE
:
1152 solvedata
->error
= g_strdup_printf(/* Error displayed to user when they an unknown variable is entered */
1153 _("Unknown variable '%s'"), error_token
);
1156 case PARSER_ERR_UNKNOWN_FUNCTION
:
1157 solvedata
->error
= g_strdup_printf(/* Error displayed to user when an unknown function is entered */
1158 _("Function '%s' is not defined"), error_token
);
1161 case PARSER_ERR_UNKNOWN_CONVERSION
:
1162 solvedata
->error
= g_strdup(/* Error displayed to user when an conversion with unknown units is attempted */
1163 _("Unknown conversion"));
1167 solvedata
->error
= g_strdup(mp_get_error());
1171 solvedata
->error
= g_strdup(/* Error displayed to user when they enter an invalid calculation */
1172 _("Malformed expression"));
1175 g_async_queue_push(equation
->priv
->queue
, solvedata
);
1182 math_equation_show_in_progress(gpointer data
)
1184 MathEquation
*equation
= MATH_EQUATION(data
);
1185 if (equation
->priv
->in_solve
)
1186 math_equation_set_status(equation
, "Calculating");
1192 math_equation_look_for_answer(gpointer data
)
1194 MathEquation
*equation
= MATH_EQUATION(data
);
1195 SolveData
*result
= g_async_queue_try_pop(equation
->priv
->queue
);
1200 equation
->priv
->in_solve
= false;
1203 math_equation_set_status(equation
, "");
1205 if (result
->error
!= NULL
) {
1206 math_equation_set_status(equation
, result
->error
);
1207 g_free(result
->error
);
1209 else if (result
->number_result
!= NULL
) {
1210 math_equation_set_number(equation
, result
->number_result
);
1211 g_slice_free(MPNumber
, result
->number_result
);
1213 else if (result
->text_result
!= NULL
) {
1214 math_equation_set(equation
, result
->text_result
);
1215 g_free(result
->text_result
);
1217 g_slice_free(SolveData
, result
);
1224 math_equation_solve(MathEquation
*equation
)
1226 GError
*error
= NULL
;
1228 // FIXME: should replace calculation or give error message
1229 if (equation
->priv
->in_solve
)
1232 if (math_equation_is_empty(equation
))
1235 /* If showing a result return to the equation that caused it */
1236 // FIXME: Result may not be here due to solve (i.e. the user may have entered "ans")
1237 if (math_equation_is_result(equation
)) {
1238 math_equation_undo(equation
);
1242 equation
->priv
->in_solve
= true;
1244 math_equation_set_number_mode(equation
, NORMAL
);
1246 g_thread_create(math_equation_solve_real
, equation
, false, &error
);
1249 g_warning("Error spawning thread for calculations: %s\n", error
->message
);
1251 g_timeout_add(50, math_equation_look_for_answer
, equation
);
1252 g_timeout_add(100, math_equation_show_in_progress
, equation
);
1257 math_equation_factorize_real(gpointer data
)
1260 GList
*factors
, *factor
;
1262 MathEquation
*equation
= MATH_EQUATION(data
);
1263 SolveData
*result
= g_slice_new0(SolveData
);
1265 math_equation_get_number(equation
, &x
);
1266 factors
= mp_factorize(&x
);
1268 text
= g_string_new("");
1270 for (factor
= factors
; factor
; factor
= factor
->next
) {
1275 temp
= mp_serializer_to_string(equation
->priv
->serializer
, n
);
1276 g_string_append(text
, temp
);
1278 g_string_append(text
, "×");
1279 g_slice_free(MPNumber
, n
);
1282 g_list_free(factors
);
1284 result
->text_result
= g_strndup(text
->str
, text
->len
);
1285 g_async_queue_push(equation
->priv
->queue
, result
);
1286 g_string_free(text
, TRUE
);
1293 math_equation_factorize(MathEquation
*equation
)
1296 GError
*error
= NULL
;
1298 // FIXME: should replace calculation or give error message
1299 if (equation
->priv
->in_solve
)
1302 if (!math_equation_get_number(equation
, &x
) || !mp_is_integer(&x
)) {
1303 /* Error displayed when trying to factorize a non-integer value */
1304 math_equation_set_status(equation
, _("Need an integer to factorize"));
1308 equation
->priv
->in_solve
= true;
1310 g_thread_create(math_equation_factorize_real
, equation
, false, &error
);
1313 g_warning("Error spawning thread for calculations: %s\n", error
->message
);
1315 g_timeout_add(50, math_equation_look_for_answer
, equation
);
1316 g_timeout_add(100, math_equation_show_in_progress
, equation
);
1321 math_equation_delete(MathEquation
*equation
)
1324 GtkTextIter start
, end
;
1326 g_object_get(G_OBJECT(equation
), "cursor-position", &cursor
, NULL
);
1327 if (cursor
>= gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation
)))
1330 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &start
, cursor
);
1331 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation
), &end
, cursor
+1);
1332 gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation
), &start
, &end
);
1337 math_equation_backspace(MathEquation
*equation
)
1339 /* Can't delete empty display */
1340 if (math_equation_is_empty(equation
))
1343 if (gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(equation
)))
1344 gtk_text_buffer_delete_selection(GTK_TEXT_BUFFER(equation
), FALSE
, FALSE
);
1347 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation
), &iter
, gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(equation
)));
1348 gtk_text_buffer_backspace(GTK_TEXT_BUFFER(equation
), &iter
, TRUE
, TRUE
);
1354 math_equation_clear(MathEquation
*equation
)
1356 math_equation_set_number_mode(equation
, NORMAL
);
1357 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), "", -1);
1358 clear_ans(equation
, FALSE
);
1363 math_equation_shift(MathEquation
*equation
, gint count
)
1367 if (!math_equation_get_number(equation
, &z
)) {
1368 math_equation_set_status(equation
,
1369 /* This message is displayed in the status bar when a bit
1370 shift operation is performed and the display does not contain a number */
1371 _("No sane value to bitwise shift"));
1375 mp_shift(&z
, count
, &z
);
1376 math_equation_set_number(equation
, &z
);
1381 math_equation_toggle_bit(MathEquation
*equation
, guint bit
)
1387 result
= math_equation_get_number(equation
, &x
);
1390 mp_set_from_unsigned_integer(G_MAXUINT64
, &max
);
1391 if (mp_is_negative(&x
) || mp_is_greater_than(&x
, &max
))
1394 bits
= mp_cast_to_unsigned_int(&x
);
1398 math_equation_set_status(equation
,
1399 /* Message displayed when cannot toggle bit in display*/
1400 _("Displayed value not an integer"));
1404 bits
^= (1LL << (63 - bit
));
1406 mp_set_from_unsigned_integer(bits
, &x
);
1408 // FIXME: Only do this if in ans format, otherwise set text in same format as previous number
1409 math_equation_set_number(equation
, &x
);
1414 math_equation_set_property(GObject
*object
,
1416 const GValue
*value
,
1421 self
= MATH_EQUATION(object
);
1425 math_equation_set_status(self
, g_value_get_string(value
));
1428 math_equation_set(self
, g_value_get_string(value
));
1430 case PROP_NUMBER_MODE
:
1431 math_equation_set_number_mode(self
, g_value_get_int(value
));
1434 math_equation_set_accuracy(self
, g_value_get_int(value
));
1436 case PROP_SHOW_THOUSANDS_SEPARATORS
:
1437 math_equation_set_show_thousands_separators(self
, g_value_get_boolean(value
));
1439 case PROP_SHOW_TRAILING_ZEROES
:
1440 math_equation_set_show_trailing_zeroes(self
, g_value_get_boolean(value
));
1442 case PROP_NUMBER_FORMAT
:
1443 math_equation_set_number_format(self
, g_value_get_int(value
));
1446 math_equation_set_base(self
, g_value_get_int(value
));
1448 case PROP_WORD_SIZE
:
1449 math_equation_set_word_size(self
, g_value_get_int(value
));
1451 case PROP_ANGLE_UNITS
:
1452 math_equation_set_angle_units(self
, g_value_get_int(value
));
1454 case PROP_SOURCE_CURRENCY
:
1455 math_equation_set_source_currency(self
, g_value_get_string(value
));
1457 case PROP_TARGET_CURRENCY
:
1458 math_equation_set_target_currency(self
, g_value_get_string(value
));
1460 case PROP_SOURCE_UNITS
:
1461 math_equation_set_source_units(self
, g_value_get_string(value
));
1463 case PROP_TARGET_UNITS
:
1464 math_equation_set_target_units(self
, g_value_get_string(value
));
1467 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
1474 math_equation_get_property(GObject
*object
,
1482 self
= MATH_EQUATION(object
);
1486 g_value_set_string(value
, self
->priv
->state
.status
);
1489 text
= math_equation_get_display(self
);
1490 g_value_set_string(value
, text
);
1494 text
= math_equation_get_equation(self
);
1495 g_value_set_string(value
, text
);
1498 case PROP_NUMBER_MODE
:
1499 g_value_set_enum(value
, self
->priv
->number_mode
);
1502 g_value_set_int(value
, mp_serializer_get_accuracy(self
->priv
->serializer
));
1504 case PROP_SHOW_THOUSANDS_SEPARATORS
:
1505 g_value_set_boolean(value
, mp_serializer_get_show_thousands_separators(self
->priv
->serializer
));
1507 case PROP_SHOW_TRAILING_ZEROES
:
1508 g_value_set_boolean(value
, mp_serializer_get_show_trailing_zeroes(self
->priv
->serializer
));
1510 case PROP_NUMBER_FORMAT
:
1511 g_value_set_enum(value
, mp_serializer_get_number_format(self
->priv
->serializer
));
1514 g_value_set_int(value
, math_equation_get_base(self
));
1516 case PROP_WORD_SIZE
:
1517 g_value_set_int(value
, self
->priv
->word_size
);
1519 case PROP_ANGLE_UNITS
:
1520 g_value_set_enum(value
, self
->priv
->angle_units
);
1522 case PROP_SOURCE_CURRENCY
:
1523 g_value_set_string(value
, self
->priv
->source_currency
);
1525 case PROP_TARGET_CURRENCY
:
1526 g_value_set_string(value
, self
->priv
->target_currency
);
1528 case PROP_SOURCE_UNITS
:
1529 g_value_set_string(value
, self
->priv
->source_units
);
1531 case PROP_TARGET_UNITS
:
1532 g_value_set_string(value
, self
->priv
->target_units
);
1534 case PROP_SERIALIZER
:
1535 g_value_set_object(value
, self
->priv
->serializer
);
1538 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
1545 math_equation_constructed(GObject
*object
)
1547 GtkTextBuffer
*parent_class
;
1548 parent_class
= g_type_class_peek_parent(MATH_EQUATION_GET_CLASS(object
));
1549 if (G_OBJECT_CLASS(parent_class
)->constructed
)
1550 G_OBJECT_CLASS(parent_class
)->constructed(object
);
1552 MATH_EQUATION(object
)->priv
->ans_tag
= gtk_text_buffer_create_tag(GTK_TEXT_BUFFER(object
), NULL
, "weight", PANGO_WEIGHT_BOLD
, NULL
);
1557 math_equation_class_init(MathEquationClass
*klass
)
1559 static GEnumValue number_mode_values
[] =
1561 {NORMAL
, "normal", "normal"},
1562 {SUPERSCRIPT
, "superscript", "superscript"},
1563 {SUBSCRIPT
, "subscript", "subscript"},
1566 static GEnumValue angle_unit_values
[] =
1568 {MP_RADIANS
, "radians", "radians"},
1569 {MP_DEGREES
, "degrees", "degrees"},
1570 {MP_GRADIANS
, "gradians", "gradians"},
1573 GObjectClass
*object_class
= G_OBJECT_CLASS(klass
);
1575 object_class
->get_property
= math_equation_get_property
;
1576 object_class
->set_property
= math_equation_set_property
;
1577 object_class
->constructed
= math_equation_constructed
;
1579 g_type_class_add_private(klass
, sizeof(MathEquationPrivate
));
1581 number_mode_type
= g_enum_register_static("NumberMode", number_mode_values
);
1582 number_format_type
= math_mp_display_format_get_type();
1583 angle_unit_type
= g_enum_register_static("AngleUnit", angle_unit_values
);
1585 g_object_class_install_property(object_class
,
1587 g_param_spec_string("status",
1591 G_PARAM_READWRITE
));
1592 g_object_class_install_property(object_class
,
1594 g_param_spec_string("display",
1596 "Displayed equation text",
1598 G_PARAM_READWRITE
));
1599 g_object_class_install_property(object_class
,
1601 g_param_spec_string("equation",
1606 g_object_class_install_property(object_class
,
1608 g_param_spec_enum("number-mode",
1610 "Input number mode",
1613 G_PARAM_READWRITE
));
1614 g_object_class_install_property(object_class
,
1616 g_param_spec_int("accuracy",
1620 G_PARAM_READWRITE
));
1621 g_object_class_install_property(object_class
,
1622 PROP_SHOW_THOUSANDS_SEPARATORS
,
1623 g_param_spec_boolean("show-thousands-separators",
1624 "show-thousands-separators",
1625 "Show thousands separators",
1627 G_PARAM_READWRITE
));
1628 g_object_class_install_property(object_class
,
1629 PROP_SHOW_TRAILING_ZEROES
,
1630 g_param_spec_boolean("show-trailing-zeroes",
1631 "show-trailing-zeroes",
1632 "Show trailing zeroes",
1634 G_PARAM_READWRITE
));
1635 g_object_class_install_property(object_class
,
1637 g_param_spec_enum("number-format",
1641 MP_DISPLAY_FORMAT_FIXED
,
1642 G_PARAM_READWRITE
));
1643 g_object_class_install_property(object_class
,
1645 g_param_spec_int("base",
1647 "Default number base (derived from number-format)",
1649 G_PARAM_READWRITE
));
1650 g_object_class_install_property(object_class
,
1652 g_param_spec_int("word-size",
1654 "Word size in bits",
1656 G_PARAM_READWRITE
));
1657 g_object_class_install_property(object_class
,
1659 g_param_spec_enum("angle-units",
1664 G_PARAM_READWRITE
));
1665 g_object_class_install_property(object_class
,
1666 PROP_SOURCE_CURRENCY
,
1667 g_param_spec_string("source-currency",
1671 G_PARAM_READWRITE
));
1672 g_object_class_install_property(object_class
,
1673 PROP_TARGET_CURRENCY
,
1674 g_param_spec_string("target-currency",
1678 G_PARAM_READWRITE
));
1679 g_object_class_install_property(object_class
,
1681 g_param_spec_string("source-units",
1685 G_PARAM_READWRITE
));
1686 g_object_class_install_property(object_class
,
1688 g_param_spec_string("target-units",
1692 G_PARAM_READWRITE
));
1693 g_object_class_install_property(object_class
,
1695 g_param_spec_object("serializer",
1704 pre_insert_text_cb(MathEquation
*equation
,
1705 GtkTextIter
*location
,
1712 if (equation
->priv
->in_reformat
)
1715 /* If following a delete then have already pushed undo stack (GtkTextBuffer
1716 doesn't indicate replace operations so we have to infer them) */
1717 if (!equation
->priv
->in_delete
)
1718 math_equation_push_undo_stack(equation
);
1720 /* Clear result on next digit entered if cursor at end of line */
1722 c
= g_utf8_get_char(text
);
1723 if ((g_unichar_isdigit(c
) || c
== mp_serializer_get_radix(equation
->priv
->serializer
)) &&
1724 math_equation_is_result(equation
)) {
1725 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation
), "", -1);
1726 clear_ans(equation
, FALSE
);
1727 gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(equation
), location
);
1730 if (equation
->priv
->ans_start
) {
1731 gint ans_start
, ans_end
;
1734 offset
= gtk_text_iter_get_offset(location
);
1735 get_ans_offsets(equation
, &ans_start
, &ans_end
);
1737 /* Inserted inside ans */
1738 if (offset
> ans_start
&& offset
< ans_end
)
1739 clear_ans(equation
, TRUE
);
1745 on_delete(MathEquation
*equation
)
1747 equation
->priv
->in_delete
= FALSE
;
1753 pre_delete_range_cb(MathEquation
*equation
,
1758 if (equation
->priv
->in_reformat
)
1761 math_equation_push_undo_stack(equation
);
1763 equation
->priv
->in_delete
= TRUE
;
1764 g_idle_add((GSourceFunc
)on_delete
, equation
);
1766 if (equation
->priv
->ans_start
) {
1767 gint ans_start
, ans_end
;
1768 gint start_offset
, end_offset
;
1770 start_offset
= gtk_text_iter_get_offset(start
);
1771 end_offset
= gtk_text_iter_get_offset(end
);
1772 get_ans_offsets(equation
, &ans_start
, &ans_end
);
1774 /* Deleted part of ans */
1775 if (start_offset
< ans_end
&& end_offset
> ans_start
)
1776 clear_ans(equation
, TRUE
);
1782 insert_text_cb(MathEquation
*equation
,
1783 GtkTextIter
*location
,
1788 if (equation
->priv
->in_reformat
)
1791 equation
->priv
->state
.entered_multiply
= strcmp(text
, "×") == 0;
1793 /* Update thousands separators */
1794 reformat_separators(equation
);
1796 g_object_notify(G_OBJECT(equation
), "display");
1801 delete_range_cb(MathEquation
*equation
,
1806 if (equation
->priv
->in_reformat
)
1809 equation
->priv
->state
.entered_multiply
= FALSE
;
1811 /* Update thousands separators */
1812 reformat_separators(equation
);
1814 // FIXME: A replace will emit this both for delete-range and insert-text, can it be avoided?
1815 g_object_notify(G_OBJECT(equation
), "display");
1820 math_equation_init(MathEquation
*equation
)
1822 /* Digits localized for the given language */
1823 const char *digit_values
= _("0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F");
1824 const char *default_digits
[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
1826 gboolean use_default_digits
= FALSE
;
1829 equation
->priv
= G_TYPE_INSTANCE_GET_PRIVATE(equation
, math_equation_get_type(), MathEquationPrivate
);
1831 g_signal_connect(equation
, "insert-text", G_CALLBACK(pre_insert_text_cb
), equation
);
1832 g_signal_connect(equation
, "delete-range", G_CALLBACK(pre_delete_range_cb
), equation
);
1833 g_signal_connect_after(equation
, "insert-text", G_CALLBACK(insert_text_cb
), equation
);
1834 g_signal_connect_after(equation
, "delete-range", G_CALLBACK(delete_range_cb
), equation
);
1836 digits
= g_strsplit(digit_values
, ",", -1);
1837 for (i
= 0; i
< 16; i
++) {
1838 if (use_default_digits
|| digits
[i
] == NULL
) {
1839 use_default_digits
= TRUE
;
1840 equation
->priv
->digits
[i
] = g_utf8_get_char(default_digits
[i
]);
1843 equation
->priv
->digits
[i
] = g_utf8_get_char(digits
[i
]);
1847 equation
->priv
->variables
= math_variables_new();
1848 equation
->priv
->unit_manager
= unit_manager_get_default();
1850 equation
->priv
->state
.status
= g_strdup("");
1851 equation
->priv
->word_size
= 32;
1852 equation
->priv
->angle_units
= MP_DEGREES
;
1853 // FIXME: Pick based on locale
1854 equation
->priv
->source_currency
= g_strdup("");
1855 equation
->priv
->target_currency
= g_strdup("");
1856 equation
->priv
->source_units
= g_strdup("");
1857 equation
->priv
->target_units
= g_strdup("");
1858 equation
->priv
->serializer
= mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC
, 10, 9);
1859 equation
->priv
->queue
= g_async_queue_new();
1861 mp_set_from_integer(0, &equation
->priv
->state
.ans
);