1 /*
2  * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
3  * Copyright (C) 2008-2011 Robert Ancell
4  *
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
9  * license.
10  */
11 
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <math.h>
17 #include <errno.h>
18 #include <glib.h>
19 #include <glib/gi18n.h>
20 
21 #include "math-equation.h"
22 
23 #include "mp.h"
24 #include "mp-equation.h"
25 #include "mp-serializer.h"
26 #include "mp-enums.h"
27 #include "unit-manager.h"
28 #include "utility.h"
29 
30 enum {
31     PROP_0,
32     PROP_STATUS,
33     PROP_DISPLAY,
34     PROP_EQUATION,
35     PROP_NUMBER_MODE,
36     PROP_ACCURACY,
37     PROP_SHOW_THOUSANDS_SEPARATORS,
38     PROP_SHOW_TRAILING_ZEROES,
39     PROP_NUMBER_FORMAT,
40     PROP_BASE,
41     PROP_WORD_SIZE,
42     PROP_ANGLE_UNITS,
43     PROP_SOURCE_CURRENCY,
44     PROP_TARGET_CURRENCY,
45     PROP_SOURCE_UNITS,
46     PROP_TARGET_UNITS,
47     PROP_SERIALIZER
48 };
49 
50 static GType number_mode_type, number_format_type, angle_unit_type;
51 
52 /* Expression mode state */
53 typedef struct {
54     MPNumber ans;              /* Previously calculated answer */
55     gchar *expression;         /* Expression entered by user */
56     gint ans_start, ans_end;   /* Start and end characters for ans variable in expression */
57     gint cursor;               /* ??? */
58     NumberMode number_mode;    /* ??? */
59     gboolean can_super_minus;  /* TRUE if entering minus can generate a superscript minus */
60     gboolean entered_multiply; /* Last insert was a multiply character */
61     gchar *status;             /* Equation status */
62 } MathEquationState;
63 
64 struct MathEquationPrivate
65 {
66     GtkTextTag *ans_tag;
67 
68     gint word_size;           /* Word size in bits */
69     MPAngleUnit angle_units;  /* Units for trigonometric functions */
70     char *source_currency;
71     char *target_currency;
72     char *source_units;
73     char *target_units;
74     NumberMode number_mode;   /* ??? */
75     gboolean can_super_minus; /* TRUE if entering minus can generate a superscript minus */
76 
77     gunichar digits[16];      /* Localized digits */
78 
79     GtkTextMark *ans_start, *ans_end;
80 
81     MathEquationState state;  /* Equation state */
82     GList *undo_stack;        /* History of expression mode states */
83     GList *redo_stack;
84     gboolean in_undo_operation;
85 
86     gboolean in_reformat;
87 
88     gboolean in_delete;
89 
90     gboolean in_solve;
91 
92     MathVariables *variables;
93     MpSerializer *serializer;
94 
95     GAsyncQueue *queue;
96 };
97 
98 typedef struct {
99     MPNumber *number_result;
100     gchar *text_result;
101     gchar *error;
102 } SolveData;
103 
104 G_DEFINE_TYPE_WITH_PRIVATE (MathEquation, math_equation, GTK_TYPE_TEXT_BUFFER);
105 
106 
107 MathEquation *
math_equation_new()108 math_equation_new()
109 {
110     return g_object_new(math_equation_get_type(), NULL);
111 }
112 
113 
114 MathVariables *
math_equation_get_variables(MathEquation * equation)115 math_equation_get_variables(MathEquation *equation)
116 {
117     return equation->priv->variables;
118 }
119 
120 
121 static void
get_ans_offsets(MathEquation * equation,gint * start,gint * end)122 get_ans_offsets(MathEquation *equation, gint *start, gint *end)
123 {
124     GtkTextIter iter;
125 
126     if (!equation->priv->ans_start) {
127         *start = *end = -1;
128         return;
129     }
130 
131     gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, equation->priv->ans_start);
132     *start = gtk_text_iter_get_offset(&iter);
133     gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, equation->priv->ans_end);
134     *end = gtk_text_iter_get_offset(&iter);
135 }
136 
137 
138 static void
reformat_ans(MathEquation * equation)139 reformat_ans(MathEquation *equation)
140 {
141     if (!equation->priv->ans_start)
142         return;
143 
144     gchar *orig_ans_text;
145     gchar *ans_text;
146     GtkTextIter ans_start, ans_end;
147 
148     gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_start, equation->priv->ans_start);
149     gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_end, equation->priv->ans_end);
150     orig_ans_text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation), &ans_start, &ans_end, FALSE);
151     ans_text = mp_serializer_to_string(equation->priv->serializer, &equation->priv->state.ans);
152     if (strcmp(orig_ans_text, ans_text) != 0) {
153         gint start;
154 
155         equation->priv->in_undo_operation = TRUE;
156         equation->priv->in_reformat = TRUE;
157 
158         start = gtk_text_iter_get_offset(&ans_start);
159         gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation), &ans_start, &ans_end);
160         gtk_text_buffer_insert_with_tags(GTK_TEXT_BUFFER(equation), &ans_end, ans_text, -1, equation->priv->ans_tag, NULL);
161 
162         /* There seems to be a bug in the marks as they alternate being the correct and incorrect ways.  Reset them */
163         gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &ans_start, start);
164         gtk_text_buffer_move_mark(GTK_TEXT_BUFFER(equation), equation->priv->ans_start, &ans_start);
165         gtk_text_buffer_move_mark(GTK_TEXT_BUFFER(equation), equation->priv->ans_end, &ans_end);
166 
167         equation->priv->in_reformat = FALSE;
168         equation->priv->in_undo_operation = FALSE;
169     }
170     gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_start, equation->priv->ans_start);
171     gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_end, equation->priv->ans_end);
172     g_free(orig_ans_text);
173     g_free(ans_text);
174 }
175 
176 
177 static gint
count_digits(MathEquation * equation,const gchar * text)178 count_digits(MathEquation *equation, const gchar *text)
179 {
180     const gchar *read_iter;
181     gint count = 0;
182 
183     read_iter = text;
184     while (*read_iter != '\0') {
185         if (!g_unichar_isdigit(g_utf8_get_char(read_iter)))
186             return count;
187 
188         read_iter = g_utf8_next_char(read_iter);
189 
190         /* Allow a thousands separator between digits follow a digit */
191         if (g_utf8_get_char(read_iter) == mp_serializer_get_thousands_separator(equation->priv->serializer)) {
192             read_iter = g_utf8_next_char(read_iter);
193             if (!g_unichar_isdigit(g_utf8_get_char(read_iter)))
194                 return count;
195         }
196 
197         count++;
198     }
199 
200     return count;
201 }
202 
203 
204 static void
reformat_separators(MathEquation * equation)205 reformat_separators(MathEquation *equation)
206 {
207     gchar *text, *read_iter;
208     gint ans_start, ans_end;
209     gint offset, digit_offset = 0;
210     gboolean in_number = FALSE, in_radix = FALSE, last_is_tsep = FALSE;
211 
212     equation->priv->in_undo_operation = TRUE;
213     equation->priv->in_reformat = TRUE;
214 
215     text = math_equation_get_display(equation);
216     get_ans_offsets(equation, &ans_start, &ans_end);
217     for (read_iter = text, offset = 0; *read_iter != '\0'; read_iter = g_utf8_next_char(read_iter), offset++) {
218         gunichar c;
219         gboolean expect_tsep;
220 
221         /* See what digit this character is */
222         c = g_utf8_get_char(read_iter);
223 
224         expect_tsep = math_equation_get_base(equation) == 10 &&
225                       mp_serializer_get_show_thousands_separators(equation->priv->serializer) &&
226                       in_number && !in_radix && !last_is_tsep &&
227                       digit_offset > 0 && digit_offset % mp_serializer_get_thousands_separator_count(equation->priv->serializer) == 0;
228         last_is_tsep = FALSE;
229 
230         /* Don't mess with ans */
231         if (offset >= ans_start && offset <= ans_end) {
232             in_number = in_radix = FALSE;
233             continue;
234         }
235         if (g_unichar_isdigit(c)) {
236             if (!in_number)
237                 digit_offset = count_digits(equation, read_iter);
238             in_number = TRUE;
239 
240             /* Expected a thousands separator between these digits - insert it */
241             if (expect_tsep) {
242                 GtkTextIter iter;
243                 gchar buffer[7];
244                 gint len;
245 
246                 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &iter, offset);
247                 len = g_unichar_to_utf8(mp_serializer_get_thousands_separator(equation->priv->serializer), buffer);
248                 buffer[len] = '\0';
249                 gtk_text_buffer_insert(GTK_TEXT_BUFFER(equation), &iter, buffer, -1);
250                 offset++;
251                 last_is_tsep = TRUE;
252             }
253 
254             digit_offset--;
255         }
256         else if (c == mp_serializer_get_radix(equation->priv->serializer)) {
257             in_number = in_radix = TRUE;
258         }
259         else if (c == mp_serializer_get_thousands_separator(equation->priv->serializer)) {
260             /* Didn't expect thousands separator - delete it */
261             if (!expect_tsep && in_number) {
262                 GtkTextIter start, end;
263                 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &start, offset);
264                 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &end, offset + 1);
265                 gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation), &start, &end);
266                 offset--;
267             }
268             else
269                 last_is_tsep = TRUE;
270         }
271         else {
272             in_number = in_radix = FALSE;
273         }
274     }
275 
276     g_free(text);
277 
278     equation->priv->in_reformat = FALSE;
279     equation->priv->in_undo_operation = FALSE;
280 }
281 
282 
283 static void
reformat_display(MathEquation * equation)284 reformat_display(MathEquation *equation)
285 {
286     /* Change ans */
287     reformat_ans(equation);
288 
289     /* Add/remove thousands separators */
290     reformat_separators(equation);
291 
292     g_signal_emit_by_name(equation, "display-changed");
293 }
294 
295 
296 static MathEquationState *
get_current_state(MathEquation * equation)297 get_current_state(MathEquation *equation)
298 {
299     MathEquationState *state;
300     gint ans_start = -1, ans_end = -1;
301 
302     state = g_malloc0(sizeof(MathEquationState));
303     state->ans = mp_new();
304 
305     if (equation->priv->ans_start)
306     {
307         GtkTextIter iter;
308         gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, equation->priv->ans_start);
309         ans_start = gtk_text_iter_get_offset(&iter);
310         gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, equation->priv->ans_end);
311         ans_end = gtk_text_iter_get_offset(&iter);
312     }
313 
314     mp_set_from_mp(&equation->priv->state.ans, &state->ans);
315     state->expression = math_equation_get_display(equation);
316     state->ans_start = ans_start;
317     state->ans_end = ans_end;
318     g_object_get(G_OBJECT(equation), "cursor-position", &state->cursor, NULL);
319     state->number_mode = equation->priv->number_mode;
320     state->can_super_minus = equation->priv->can_super_minus;
321     state->entered_multiply = equation->priv->state.entered_multiply;
322     state->status = g_strdup(equation->priv->state.status);
323 
324     return state;
325 }
326 
327 
328 static void
free_state(MathEquationState * state)329 free_state(MathEquationState *state)
330 {
331     g_free(state->expression);
332     g_free(state->status);
333     g_free(state);
334 }
335 
336 
337 static void
math_equation_push_undo_stack(MathEquation * equation)338 math_equation_push_undo_stack(MathEquation *equation)
339 {
340     GList *link;
341     MathEquationState *state;
342 
343     if (equation->priv->in_undo_operation)
344         return;
345 
346     math_equation_set_status(equation, "");
347 
348     /* Can't redo anymore */
349     for (link = equation->priv->redo_stack; link; link = link->next) {
350         state = link->data;
351         free_state(state);
352     }
353     g_list_free(equation->priv->redo_stack);
354     equation->priv->redo_stack = NULL;
355 
356     state = get_current_state(equation);
357     equation->priv->undo_stack = g_list_prepend(equation->priv->undo_stack, state);
358 }
359 
360 
361 static void
clear_ans(MathEquation * equation,gboolean remove_tag)362 clear_ans(MathEquation *equation, gboolean remove_tag)
363 {
364     if (!equation->priv->ans_start)
365         return;
366 
367     if (remove_tag) {
368         GtkTextIter start, end;
369 
370         gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &start, equation->priv->ans_start);
371         gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &end, equation->priv->ans_end);
372         gtk_text_buffer_remove_tag(GTK_TEXT_BUFFER(equation), equation->priv->ans_tag, &start, &end);
373     }
374 
375     gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(equation), equation->priv->ans_start);
376     gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(equation), equation->priv->ans_end);
377     equation->priv->ans_start = NULL;
378     equation->priv->ans_end = NULL;
379 }
380 
381 
382 static void
apply_state(MathEquation * equation,MathEquationState * state)383 apply_state(MathEquation *equation, MathEquationState *state)
384 {
385     GtkTextIter cursor;
386 
387     /* Disable undo detection */
388     equation->priv->in_undo_operation = TRUE;
389 
390     mp_set_from_mp(&state->ans, &equation->priv->state.ans);
391 
392     gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), state->expression, -1);
393     gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &cursor, state->cursor);
394     gtk_text_buffer_place_cursor(GTK_TEXT_BUFFER(equation), &cursor);
395     clear_ans(equation, FALSE);
396     if (state->ans_start >= 0) {
397         GtkTextIter start, end;
398 
399         gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &start, state->ans_start);
400         equation->priv->ans_start = gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation), NULL, &start, FALSE);
401         gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &end, state->ans_end);
402         equation->priv->ans_end = gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation), NULL, &end, TRUE);
403         gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(equation), equation->priv->ans_tag, &start, &end);
404     }
405 
406     math_equation_set_number_mode(equation, state->number_mode);
407     equation->priv->can_super_minus = state->can_super_minus;
408     equation->priv->state.entered_multiply = state->entered_multiply;
409     math_equation_set_status(equation, state->status);
410 
411     equation->priv->in_undo_operation = FALSE;
412 }
413 
414 
415 void
math_equation_copy(MathEquation * equation)416 math_equation_copy(MathEquation *equation)
417 {
418     GtkTextIter start, end;
419     gchar *text;
420 
421     g_return_if_fail(equation != NULL);
422 
423     if (!gtk_text_buffer_get_selection_bounds(GTK_TEXT_BUFFER(equation), &start, &end))
424         gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation), &start, &end);
425 
426     text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation), &start, &end, FALSE);
427     gtk_clipboard_set_text(gtk_clipboard_get(GDK_NONE), g_str_to_ascii (text, "C"), -1);
428     g_free(text);
429 }
430 
431 
432 static void
on_paste(GtkClipboard * clipboard,const gchar * text,gpointer data)433 on_paste(GtkClipboard *clipboard, const gchar *text, gpointer data)
434 {
435     MathEquation *equation = data;
436     if (text != NULL)
437         math_equation_insert(equation, text);
438 }
439 
440 
441 void
math_equation_paste(MathEquation * equation)442 math_equation_paste(MathEquation *equation)
443 {
444     g_return_if_fail(equation != NULL);
445     gtk_clipboard_request_text(gtk_clipboard_get(GDK_NONE), on_paste, equation);
446 }
447 
448 
449 void
math_equation_undo(MathEquation * equation)450 math_equation_undo(MathEquation *equation)
451 {
452     GList *link;
453     MathEquationState *state;
454 
455     g_return_if_fail(equation != NULL);
456 
457     if (!equation->priv->undo_stack) {
458         math_equation_set_status(equation,
459                                  /* Error shown when trying to undo with no undo history */
460                                  _("No undo history"));
461         return;
462     }
463 
464     link = equation->priv->undo_stack;
465     equation->priv->undo_stack = g_list_remove_link(equation->priv->undo_stack, link);
466     state = link->data;
467     g_list_free(link);
468 
469     equation->priv->redo_stack = g_list_prepend(equation->priv->redo_stack, get_current_state(equation));
470 
471     apply_state(equation, state);
472     free_state(state);
473 }
474 
475 
476 void
math_equation_redo(MathEquation * equation)477 math_equation_redo(MathEquation *equation)
478 {
479     GList *link;
480     MathEquationState *state;
481 
482     g_return_if_fail(equation != NULL);
483 
484     if (!equation->priv->redo_stack) {
485         math_equation_set_status(equation,
486                                  /* Error shown when trying to redo with no redo history */
487                                  _("No redo history"));
488         return;
489     }
490 
491     link = equation->priv->redo_stack;
492     equation->priv->redo_stack = g_list_remove_link(equation->priv->redo_stack, link);
493     state = link->data;
494     g_list_free(link);
495 
496     equation->priv->undo_stack = g_list_prepend(equation->priv->undo_stack, get_current_state(equation));
497 
498     apply_state(equation, state);
499     free_state(state);
500 }
501 
502 
503 gunichar
math_equation_get_digit_text(MathEquation * equation,guint digit)504 math_equation_get_digit_text(MathEquation *equation, guint digit)
505 {
506     g_return_val_if_fail(equation != NULL, '?');
507     g_return_val_if_fail(digit < 16, '?');
508 
509     return equation->priv->digits[digit];
510 }
511 
512 
513 void
math_equation_set_accuracy(MathEquation * equation,gint accuracy)514 math_equation_set_accuracy(MathEquation *equation, gint accuracy)
515 {
516     g_return_if_fail(equation != NULL);
517 
518     if (mp_serializer_get_trailing_digits(equation->priv->serializer) == accuracy)
519         return;
520     mp_serializer_set_trailing_digits(equation->priv->serializer, accuracy);
521     reformat_display(equation);
522     g_object_notify(G_OBJECT(equation), "accuracy");
523 }
524 
525 
526 gint
math_equation_get_accuracy(MathEquation * equation)527 math_equation_get_accuracy(MathEquation *equation)
528 {
529     g_return_val_if_fail(equation != NULL, 0);
530 
531     return mp_serializer_get_trailing_digits(equation->priv->serializer);
532 }
533 
534 
535 void
math_equation_set_show_thousands_separators(MathEquation * equation,gboolean visible)536 math_equation_set_show_thousands_separators(MathEquation *equation, gboolean visible)
537 {
538     g_return_if_fail(equation != NULL);
539 
540     if (mp_serializer_get_show_thousands_separators(equation->priv->serializer) == visible)
541         return;
542 
543     mp_serializer_set_show_thousands_separators(equation->priv->serializer, visible);
544     reformat_display(equation);
545     g_object_notify(G_OBJECT(equation), "show-thousands-separators");
546 }
547 
548 
549 gboolean
math_equation_get_show_thousands_separators(MathEquation * equation)550 math_equation_get_show_thousands_separators(MathEquation *equation)
551 {
552     g_return_val_if_fail(equation != NULL, FALSE);
553     return mp_serializer_get_show_thousands_separators(equation->priv->serializer);
554 }
555 
556 
557 void
math_equation_set_show_trailing_zeroes(MathEquation * equation,gboolean visible)558 math_equation_set_show_trailing_zeroes(MathEquation *equation, gboolean visible)
559 {
560     g_return_if_fail(equation != NULL);
561 
562     if (mp_serializer_get_show_trailing_zeroes(equation->priv->serializer) == visible)
563         return;
564 
565     mp_serializer_set_show_trailing_zeroes(equation->priv->serializer, visible);
566     reformat_display(equation);
567     g_object_notify(G_OBJECT(equation), "show-trailing-zeroes");
568 }
569 
570 
571 gboolean
math_equation_get_show_trailing_zeroes(MathEquation * equation)572 math_equation_get_show_trailing_zeroes(MathEquation *equation)
573 {
574     g_return_val_if_fail(equation != NULL, FALSE);
575     return mp_serializer_get_show_trailing_zeroes(equation->priv->serializer);
576 }
577 
578 
579 void
math_equation_set_number_format(MathEquation * equation,MpDisplayFormat format)580 math_equation_set_number_format(MathEquation *equation, MpDisplayFormat format)
581 {
582     g_return_if_fail(equation != NULL);
583 
584     if (mp_serializer_get_number_format(equation->priv->serializer) == format)
585         return;
586 
587     mp_serializer_set_number_format(equation->priv->serializer, format);
588     reformat_display(equation);
589     g_object_notify(G_OBJECT(equation), "number-format");
590 }
591 
592 
593 MpDisplayFormat
math_equation_get_number_format(MathEquation * equation)594 math_equation_get_number_format(MathEquation *equation)
595 {
596     g_return_val_if_fail(equation != NULL, MP_DISPLAY_FORMAT_AUTOMATIC);
597     return mp_serializer_get_number_format(equation->priv->serializer);
598 }
599 
600 
601 void
math_equation_set_base(MathEquation * equation,gint base)602 math_equation_set_base(MathEquation *equation, gint base)
603 {
604     g_return_if_fail(equation != NULL);
605 
606     if (mp_serializer_get_base(equation->priv->serializer) == base)
607         return;
608 
609     mp_serializer_set_base(equation->priv->serializer, base);
610     reformat_display(equation);
611     g_object_notify(G_OBJECT(equation), "base");
612 }
613 
614 
615 gint
math_equation_get_base(MathEquation * equation)616 math_equation_get_base(MathEquation *equation)
617 {
618     g_return_val_if_fail(equation != NULL, 10);
619     return mp_serializer_get_base(equation->priv->serializer);
620 }
621 
622 
623 void
math_equation_set_word_size(MathEquation * equation,gint word_size)624 math_equation_set_word_size(MathEquation *equation, gint word_size)
625 {
626     g_return_if_fail(equation != NULL);
627 
628     if (equation->priv->word_size == word_size)
629         return;
630 
631     equation->priv->word_size = word_size;
632     g_object_notify(G_OBJECT(equation), "word-size");
633 }
634 
635 
636 gint
math_equation_get_word_size(MathEquation * equation)637 math_equation_get_word_size(MathEquation *equation)
638 {
639     g_return_val_if_fail(equation != NULL, 64);
640     return equation->priv->word_size;
641 }
642 
643 
644 void
math_equation_set_angle_units(MathEquation * equation,MPAngleUnit angle_units)645 math_equation_set_angle_units(MathEquation *equation, MPAngleUnit angle_units)
646 {
647     g_return_if_fail(equation != NULL);
648 
649     if (equation->priv->angle_units == angle_units)
650         return;
651 
652     equation->priv->angle_units = angle_units;
653     g_object_notify(G_OBJECT(equation), "angle-units");
654 }
655 
656 
657 MPAngleUnit
math_equation_get_angle_units(MathEquation * equation)658 math_equation_get_angle_units(MathEquation *equation)
659 {
660     g_return_val_if_fail(equation != NULL, MP_DEGREES);
661     return equation->priv->angle_units;
662 }
663 
664 
665 void
math_equation_set_source_currency(MathEquation * equation,const gchar * currency)666 math_equation_set_source_currency(MathEquation *equation, const gchar *currency)
667 {
668     g_return_if_fail(equation != NULL);
669     g_return_if_fail(currency != NULL);
670 
671     if (strcmp(equation->priv->source_currency, currency) == 0)
672         return;
673     g_free(equation->priv->source_currency);
674     equation->priv->source_currency = g_strdup(currency);
675     g_settings_set_string(g_settings_var, "source-currency",
676             math_equation_get_source_currency(equation));
677 }
678 
679 
680 const gchar *
math_equation_get_source_currency(MathEquation * equation)681 math_equation_get_source_currency(MathEquation *equation)
682 {
683     g_return_val_if_fail(equation != NULL, NULL);
684     return equation->priv->source_currency;
685 }
686 
687 
688 void
math_equation_set_target_currency(MathEquation * equation,const gchar * currency)689 math_equation_set_target_currency(MathEquation *equation, const gchar *currency)
690 {
691     g_return_if_fail(equation != NULL);
692     g_return_if_fail(currency != NULL);
693 
694     if (strcmp(equation->priv->target_currency, currency) == 0)
695         return;
696     g_free(equation->priv->target_currency);
697     equation->priv->target_currency = g_strdup(currency);
698     g_settings_set_string(g_settings_var, "target-currency",
699             math_equation_get_target_currency(equation));
700 }
701 
702 
703 const gchar *
math_equation_get_target_currency(MathEquation * equation)704 math_equation_get_target_currency(MathEquation *equation)
705 {
706     g_return_val_if_fail(equation != NULL, NULL);
707     return equation->priv->target_currency;
708 }
709 
710 
711 void
math_equation_set_source_units(MathEquation * equation,const gchar * units)712 math_equation_set_source_units(MathEquation *equation, const gchar *units)
713 {
714     g_return_if_fail(equation != NULL);
715     g_return_if_fail(units != NULL);
716 
717     if (strcmp(equation->priv->source_units, units) == 0)
718         return;
719 
720     g_free(equation->priv->source_units);
721     equation->priv->source_units = g_strdup(units);
722     g_settings_set_string(g_settings_var, "source-units",
723             math_equation_get_source_units(equation));
724 }
725 
726 const gchar *
math_equation_get_source_units(MathEquation * equation)727 math_equation_get_source_units(MathEquation *equation)
728 {
729     g_return_val_if_fail(equation != NULL, NULL);
730     return equation->priv->source_units;
731 }
732 
733 
734 void
math_equation_set_target_units(MathEquation * equation,const gchar * units)735 math_equation_set_target_units(MathEquation *equation, const gchar *units)
736 {
737     g_return_if_fail(equation != NULL);
738     g_return_if_fail(units != NULL);
739 
740     if (strcmp(equation->priv->target_units, units) == 0)
741         return;
742 
743     g_free(equation->priv->target_units);
744     equation->priv->target_units = g_strdup(units);
745     g_settings_set_string(g_settings_var, "target-units",
746             math_equation_get_target_units(equation));
747 }
748 
749 
750 const gchar *
math_equation_get_target_units(MathEquation * equation)751 math_equation_get_target_units(MathEquation *equation)
752 {
753     g_return_val_if_fail(equation != NULL, NULL);
754     return equation->priv->target_units;
755 }
756 
757 
758 void
math_equation_set_status(MathEquation * equation,const gchar * status)759 math_equation_set_status(MathEquation *equation, const gchar *status)
760 {
761     g_return_if_fail(equation != NULL);
762     g_return_if_fail(status != NULL);
763 
764     if (strcmp(equation->priv->state.status, status) == 0)
765         return;
766 
767     g_free(equation->priv->state.status);
768     equation->priv->state.status = g_strdup(status);
769     g_object_notify(G_OBJECT(equation), "status");
770 }
771 
772 
773 const gchar *
math_equation_get_status(MathEquation * equation)774 math_equation_get_status(MathEquation *equation)
775 {
776     g_return_val_if_fail(equation != NULL, NULL);
777     return equation->priv->state.status;
778 }
779 
780 
781 gboolean
math_equation_is_empty(MathEquation * equation)782 math_equation_is_empty(MathEquation *equation)
783 {
784     g_return_val_if_fail(equation != NULL, FALSE);
785     return gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation)) == 0;
786 }
787 
788 
789 gboolean
math_equation_is_result(MathEquation * equation)790 math_equation_is_result(MathEquation *equation)
791 {
792     char *text;
793     gboolean result;
794 
795     g_return_val_if_fail(equation != NULL, FALSE);
796 
797     text = math_equation_get_equation(equation);
798     result = strcmp(text, "ans") == 0;
799     g_free(text);
800 
801     return result;
802 }
803 
804 
805 gchar *
math_equation_get_display(MathEquation * equation)806 math_equation_get_display(MathEquation *equation)
807 {
808     GtkTextIter start, end;
809 
810     g_return_val_if_fail(equation != NULL, NULL);
811 
812     gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation), &start, &end);
813     return gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation), &start, &end, FALSE);
814 }
815 
816 
817 gchar *
math_equation_get_equation(MathEquation * equation)818 math_equation_get_equation(MathEquation *equation)
819 {
820     gchar *text;
821     GString *eq_text;
822     gint ans_start = -1, ans_end = -1, offset;
823     const gchar *read_iter;
824     gboolean last_is_digit = FALSE;
825 
826     g_return_val_if_fail(equation != NULL, NULL);
827 
828     text = math_equation_get_display(equation);
829     eq_text = g_string_sized_new(strlen(text));
830 
831     if (equation->priv->ans_start)
832         get_ans_offsets(equation, &ans_start, &ans_end);
833 
834     for (read_iter = text, offset = 0; *read_iter != '\0'; read_iter = g_utf8_next_char(read_iter), offset++) {
835         gunichar c;
836         gboolean is_digit, next_is_digit;
837 
838         c = g_utf8_get_char(read_iter);
839         is_digit = g_unichar_isdigit(c);
840         next_is_digit = g_unichar_isdigit(g_utf8_get_char(g_utf8_next_char(read_iter)));
841 
842         /* Replace ans text with variable */
843         if (offset == ans_start) {
844              g_string_append(eq_text, "ans");
845              read_iter = g_utf8_offset_to_pointer(read_iter, ans_end - ans_start - 1);
846              offset += ans_end - ans_start - 1;
847              is_digit = FALSE;
848              continue;
849         }
850 
851         /* Ignore thousands separators */
852         if (c == mp_serializer_get_thousands_separator(equation->priv->serializer) && last_is_digit && next_is_digit)
853             ;
854         /* Substitute radix character */
855         else if (c == mp_serializer_get_radix(equation->priv->serializer) && (last_is_digit || next_is_digit))
856             g_string_append_unichar(eq_text, '.');
857         else
858             g_string_append_unichar(eq_text, c);
859 
860         last_is_digit = is_digit;
861     }
862     g_free(text);
863 
864     text = eq_text->str;
865     g_string_free(eq_text, FALSE);
866 
867     return text;
868 }
869 
870 
871 gboolean
math_equation_get_number(MathEquation * equation,MPNumber * z)872 math_equation_get_number(MathEquation *equation, MPNumber *z)
873 {
874     gchar *text;
875     gboolean result;
876 
877     g_return_val_if_fail(equation != NULL, FALSE);
878     g_return_val_if_fail(z != NULL, FALSE);
879 
880     if (math_equation_is_result(equation)) {
881         mp_set_from_mp(math_equation_get_answer(equation), z);
882         return TRUE;
883     }
884     else {
885         text = math_equation_get_equation(equation);
886         result = !mp_serializer_from_string(equation->priv->serializer, text, z);
887         g_free(text);
888         return result;
889     }
890 }
891 
892 
893 MpSerializer *
math_equation_get_serializer(MathEquation * equation)894 math_equation_get_serializer(MathEquation *equation)
895 {
896     g_return_val_if_fail(equation != NULL, NULL);
897     return equation->priv->serializer;
898 }
899 
900 
901 void
math_equation_set_number_mode(MathEquation * equation,NumberMode mode)902 math_equation_set_number_mode(MathEquation *equation, NumberMode mode)
903 {
904     g_return_if_fail(equation != NULL);
905 
906     if (equation->priv->number_mode == mode)
907         return;
908 
909     equation->priv->can_super_minus = mode == SUPERSCRIPT;
910 
911     equation->priv->number_mode = mode;
912     g_object_notify(G_OBJECT(equation), "number-mode");
913 }
914 
915 
916 NumberMode
math_equation_get_number_mode(MathEquation * equation)917 math_equation_get_number_mode(MathEquation *equation)
918 {
919     g_return_val_if_fail(equation != NULL, NORMAL);
920     return equation->priv->number_mode;
921 }
922 
923 
924 gboolean
math_equation_in_solve(MathEquation * equation)925 math_equation_in_solve(MathEquation *equation)
926 {
927     g_return_val_if_fail(equation != NULL, FALSE);
928     return equation->priv->in_solve;
929 }
930 
931 
932 const MPNumber *
math_equation_get_answer(MathEquation * equation)933 math_equation_get_answer(MathEquation *equation)
934 {
935     g_return_val_if_fail(equation != NULL, FALSE);
936     return &equation->priv->state.ans;
937 }
938 
939 
940 void
math_equation_store(MathEquation * equation,const gchar * name)941 math_equation_store(MathEquation *equation, const gchar *name)
942 {
943     MPNumber t = mp_new();
944 
945     g_return_if_fail(equation != NULL);
946     g_return_if_fail(name != NULL);
947 
948     if (!math_equation_get_number(equation, &t))
949         math_equation_set_status(equation, _("No sane value to store"));
950     else
951         math_variables_set(equation->priv->variables, name, &t);
952     mp_clear(&t);
953 }
954 
955 
956 void
math_equation_recall(MathEquation * equation,const gchar * name)957 math_equation_recall(MathEquation *equation, const gchar *name)
958 {
959     g_return_if_fail(equation != NULL);
960     g_return_if_fail(name != NULL);
961     math_equation_insert(equation, name);
962 }
963 
964 
965 void
math_equation_set(MathEquation * equation,const gchar * text)966 math_equation_set(MathEquation *equation, const gchar *text)
967 {
968     g_return_if_fail(equation != NULL);
969     g_return_if_fail(text != NULL);
970     gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), text, -1);
971     clear_ans(equation, FALSE);
972 }
973 
974 static void
math_equation_set_with_history(MathEquation * equation,const gchar * text)975 math_equation_set_with_history(MathEquation *equation, const gchar *text)
976 {
977     MathEquationState *state;
978     MPNumber x = mp_new();
979 
980     math_equation_get_number(equation, &x);
981     math_equation_set(equation, text);
982 
983     /* Notify history */
984     state = get_current_state(equation);
985     g_signal_emit_by_name(equation, "history", state->expression, &x);
986 
987     free_state(state);
988     mp_clear(&x);
989 }
990 
991 
992 void
math_equation_set_number(MathEquation * equation,const MPNumber * x)993 math_equation_set_number(MathEquation *equation, const MPNumber *x)
994 {
995     char *text;
996     GtkTextIter start, end;
997     MathEquationState *state;
998 
999     g_return_if_fail(equation != NULL);
1000     g_return_if_fail(x != NULL);
1001 
1002     /* Notify history */
1003     state = get_current_state(equation);
1004     g_signal_emit_by_name(equation, "history", state->expression, x);
1005 
1006     /* Show the number in the user chosen format */
1007     text = mp_serializer_to_string(equation->priv->serializer, x);
1008     gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), text, -1);
1009     mp_set_from_mp(x, &equation->priv->state.ans);
1010 
1011     /* Mark this text as the answer variable */
1012     gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation), &start, &end);
1013     clear_ans(equation, FALSE);
1014     equation->priv->ans_start = gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation), NULL, &start, FALSE);
1015     equation->priv->ans_end = gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation), NULL, &end, TRUE);
1016     gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(equation), equation->priv->ans_tag, &start, &end);
1017 
1018     g_object_notify(G_OBJECT(equation), "display");
1019 
1020     g_free(text);
1021     free_state(state);
1022 }
1023 
1024 
1025 void
math_equation_insert(MathEquation * equation,const gchar * text)1026 math_equation_insert(MathEquation *equation, const gchar *text)
1027 {
1028     g_return_if_fail(equation != NULL);
1029     g_return_if_fail(text != NULL);
1030 
1031     /* Replace ** with ^ (not on all keyboards) */
1032     if (!gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(equation)) &&
1033         strcmp(text, "×") == 0 && equation->priv->state.entered_multiply) {
1034         GtkTextIter iter;
1035 
1036         gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(equation)));
1037         gtk_text_buffer_backspace(GTK_TEXT_BUFFER(equation), &iter, TRUE, TRUE);
1038         gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(equation), "^", -1);
1039         return;
1040     }
1041 
1042     /* Can't enter superscript minus after entering digits */
1043     if (strstr("⁰¹²³⁴⁵⁶⁷⁸⁹", text) != NULL || strcmp("⁻", text) == 0)
1044         equation->priv->can_super_minus = FALSE;
1045 
1046     /* Disable super/subscript mode when finished entering */
1047     if (strstr("⁻⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉", text) == NULL)
1048         math_equation_set_number_mode(equation, NORMAL);
1049 
1050     gtk_text_buffer_delete_selection(GTK_TEXT_BUFFER(equation), FALSE, FALSE);
1051     gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(equation), text, -1);
1052 }
1053 
1054 
1055 void
math_equation_insert_digit(MathEquation * equation,guint digit)1056 math_equation_insert_digit(MathEquation *equation, guint digit)
1057 {
1058     static const char *subscript_digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉", NULL};
1059     static const char *superscript_digits[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹", NULL};
1060 
1061     g_return_if_fail(equation != NULL);
1062     g_return_if_fail(digit < 16);
1063 
1064     if (equation->priv->number_mode == NORMAL || digit >= 10) {
1065         gchar buffer[7];
1066         gint len;
1067         len = g_unichar_to_utf8(math_equation_get_digit_text(equation, digit), buffer);
1068         buffer[len] = '\0';
1069         math_equation_insert(equation, buffer);
1070     }
1071     else if (equation->priv->number_mode == SUPERSCRIPT)
1072         math_equation_insert(equation, superscript_digits[digit]);
1073     else if (equation->priv->number_mode == SUBSCRIPT)
1074         math_equation_insert(equation, subscript_digits[digit]);
1075 }
1076 
1077 
1078 void
math_equation_insert_numeric_point(MathEquation * equation)1079 math_equation_insert_numeric_point(MathEquation *equation)
1080 {
1081     gchar buffer[7];
1082     gint len;
1083 
1084     g_return_if_fail(equation != NULL);
1085 
1086     len = g_unichar_to_utf8(mp_serializer_get_radix(equation->priv->serializer), buffer);
1087     buffer[len] = '\0';
1088     math_equation_insert(equation, buffer);
1089 }
1090 
1091 
1092 void
math_equation_insert_number(MathEquation * equation,const MPNumber * x)1093 math_equation_insert_number(MathEquation *equation, const MPNumber *x)
1094 {
1095     char *text;
1096 
1097     g_return_if_fail(equation != NULL);
1098     g_return_if_fail(x != NULL);
1099 
1100     text = mp_serializer_to_string(equation->priv->serializer, x);
1101     math_equation_insert(equation, text);
1102     g_free(text);
1103 }
1104 
1105 
1106 void
math_equation_insert_exponent(MathEquation * equation)1107 math_equation_insert_exponent(MathEquation *equation)
1108 {
1109     g_return_if_fail(equation != NULL);
1110     math_equation_insert(equation, "×10");
1111     math_equation_set_number_mode(equation, SUPERSCRIPT);
1112 }
1113 
1114 
1115 void
math_equation_insert_subtract(MathEquation * equation)1116 math_equation_insert_subtract(MathEquation *equation)
1117 {
1118     g_return_if_fail(equation != NULL);
1119     if (equation->priv->number_mode == SUPERSCRIPT && equation->priv->can_super_minus) {
1120         math_equation_insert(equation, "⁻");
1121         equation->priv->can_super_minus = FALSE;
1122     }
1123     else {
1124         math_equation_insert(equation, "−");
1125         math_equation_set_number_mode(equation, NORMAL);
1126     }
1127 }
1128 
1129 
1130 static int
variable_is_defined(const char * name,void * data)1131 variable_is_defined(const char *name, void *data)
1132 {
1133     MathEquation *equation = data;
1134     char *c, *lower_name;
1135 
1136     lower_name = strdup(name);
1137     for (c = lower_name; *c; c++)
1138         *c = tolower(*c);
1139 
1140     if (strcmp(lower_name, "rand") == 0 ||
1141         strcmp(lower_name, "ans") == 0) {
1142         g_free(lower_name);
1143         return 1;
1144     }
1145     g_free(lower_name);
1146 
1147     return math_variables_get(equation->priv->variables, name) != NULL;
1148 }
1149 
1150 
1151 static int
get_variable(const char * name,MPNumber * z,void * data)1152 get_variable(const char *name, MPNumber *z, void *data)
1153 {
1154     char *c, *lower_name;
1155     int result = 1;
1156     MathEquation *equation = data;
1157     MPNumber *t;
1158 
1159     lower_name = strdup(name);
1160     for (c = lower_name; *c; c++)
1161         *c = tolower(*c);
1162 
1163     if (strcmp(lower_name, "rand") == 0)
1164         mp_set_from_random(z);
1165     else if (strcmp(lower_name, "ans") == 0)
1166         mp_set_from_mp(&equation->priv->state.ans, z);
1167     else {
1168         t = math_variables_get(equation->priv->variables, name);
1169         if (t)
1170             mp_set_from_mp(t, z);
1171         else
1172             result = 0;
1173     }
1174     free(lower_name);
1175 
1176     return result;
1177 }
1178 
1179 
1180 static void
set_variable(const char * name,const MPNumber * x,void * data)1181 set_variable(const char *name, const MPNumber *x, void *data)
1182 {
1183     MathEquation *equation = data;
1184     /* FIXME: Don't allow writing to built-in variables, e.g. ans, rand, sin, ... */
1185     math_variables_set(equation->priv->variables, name, x);
1186 }
1187 
1188 
1189 static int
convert(const MPNumber * x,const char * x_units,const char * z_units,MPNumber * z,void * data)1190 convert(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data)
1191 {
1192     return unit_manager_convert_by_symbol(unit_manager_get_default(), x, x_units, z_units, z);
1193 }
1194 
1195 
1196 static int
parse(MathEquation * equation,const char * text,MPNumber * z,char ** error_token)1197 parse(MathEquation *equation, const char *text, MPNumber *z, char **error_token)
1198 {
1199     MPEquationOptions options;
1200 
1201     memset(&options, 0, sizeof(options));
1202     options.base = mp_serializer_get_base(equation->priv->serializer);
1203     options.wordlen = equation->priv->word_size;
1204     options.angle_units = equation->priv->angle_units;
1205     options.variable_is_defined = variable_is_defined;
1206     options.get_variable = get_variable;
1207     options.set_variable = set_variable;
1208     options.convert = convert;
1209     options.callback_data = equation;
1210 
1211     return mp_equation_parse(text, &options, z, error_token);
1212 }
1213 
1214 
1215 /*
1216  * Executed in separate thread. It is thus not a good idea to write to anything
1217  * in MathEquation but the async queue from here.
1218  */
1219 static gpointer
math_equation_solve_real(gpointer data)1220 math_equation_solve_real(gpointer data)
1221 {
1222     MathEquation *equation = MATH_EQUATION(data);
1223     SolveData *solvedata = g_slice_new0(SolveData);
1224 
1225     gint n_brackets = 0, result;
1226     gchar *c, *text, *error_token;
1227     GString *equation_text;
1228     MPNumber z = mp_new();
1229 
1230     text = math_equation_get_equation(equation);
1231     equation_text = g_string_new(text);
1232     g_free(text);
1233     /* Count the number of brackets and automatically add missing closing brackets */
1234     for (c = equation_text->str; *c; c++) {
1235         if (*c == '(')
1236             n_brackets++;
1237         else if (*c == ')')
1238             n_brackets--;
1239     }
1240     while (n_brackets > 0) {
1241         g_string_append_c(equation_text, ')');
1242         n_brackets--;
1243     }
1244 
1245 
1246     result = parse(equation, equation_text->str, &z, &error_token);
1247     g_string_free(equation_text, TRUE);
1248 
1249     switch (result) {
1250         case PARSER_ERR_NONE:
1251             solvedata->number_result = g_slice_new(MPNumber);
1252             *solvedata->number_result = mp_new();
1253             mp_set_from_mp(&z, solvedata->number_result);
1254             break;
1255 
1256         case PARSER_ERR_OVERFLOW:
1257             solvedata->error = g_strdup(/* Error displayed to user when they perform a bitwise operation on numbers greater than the current word */
1258                                _("Overflow. Try a bigger word size"));
1259             break;
1260 
1261         case PARSER_ERR_UNKNOWN_VARIABLE:
1262             solvedata->error = g_strdup_printf(/* Error displayed to user when they an unknown variable is entered */
1263                                       _("Unknown variable '%s'"), error_token);
1264             break;
1265 
1266         case PARSER_ERR_UNKNOWN_FUNCTION:
1267             solvedata->error = g_strdup_printf(/* Error displayed to user when an unknown function is entered */
1268                                       _("Function '%s' is not defined"), error_token);
1269             break;
1270 
1271         case PARSER_ERR_UNKNOWN_CONVERSION:
1272             solvedata->error = g_strdup(/* Error displayed to user when an conversion with unknown units is attempted */
1273                                _("Unknown conversion"));
1274             break;
1275 
1276         case PARSER_ERR_MP:
1277             if (mp_get_error())
1278                 solvedata->error = g_strdup(mp_get_error());
1279             else if (error_token)
1280                 solvedata->error = g_strdup_printf(/* Uncategorized error. Show error token to user*/
1281                                     _("Malformed expression at token '%s'"), error_token);
1282             else
1283                 solvedata->error = g_strdup (/* Unknown error. */
1284                                     _("Malformed expression"));
1285             break;
1286 
1287         default:
1288             solvedata->error = g_strdup(/* Error displayed to user when they enter an invalid calculation */
1289                                _("Malformed expression"));
1290             break;
1291     }
1292     g_async_queue_push(equation->priv->queue, solvedata);
1293     mp_clear(&z);
1294 
1295     return NULL;
1296 }
1297 
1298 
1299 static gboolean
math_equation_show_in_progress(gpointer data)1300 math_equation_show_in_progress(gpointer data)
1301 {
1302     MathEquation *equation = MATH_EQUATION(data);
1303     if (equation->priv->in_solve)
1304         math_equation_set_status(equation, _("Calculating"));
1305     return false;
1306 }
1307 
1308 
1309 static gboolean
math_equation_look_for_answer(gpointer data)1310 math_equation_look_for_answer(gpointer data)
1311 {
1312     MathEquation *equation = MATH_EQUATION(data);
1313     SolveData *result = g_async_queue_try_pop(equation->priv->queue);
1314 
1315     if (result == NULL)
1316         return true;
1317 
1318     equation->priv->in_solve = false;
1319 
1320     if (!result->error)
1321         math_equation_set_status(equation, "");
1322 
1323     if (result->error != NULL) {
1324         math_equation_set_status(equation, result->error);
1325         g_free(result->error);
1326     }
1327     else if (result->number_result != NULL) {
1328         math_equation_set_number(equation, result->number_result);
1329         mp_clear(result->number_result);
1330         g_slice_free(MPNumber, result->number_result);
1331     }
1332     else if (result->text_result != NULL) {
1333         math_equation_set_with_history(equation, result->text_result);
1334         g_free(result->text_result);
1335     }
1336     g_slice_free(SolveData, result);
1337 
1338     return false;
1339 }
1340 
1341 
1342 void
math_equation_solve(MathEquation * equation)1343 math_equation_solve(MathEquation *equation)
1344 {
1345     g_return_if_fail(equation != NULL);
1346 
1347     // FIXME: should replace calculation or give error message
1348     if (equation->priv->in_solve)
1349         return;
1350 
1351     if (math_equation_is_empty(equation))
1352         return;
1353 
1354     /* If showing a result return to the equation that caused it */
1355     // FIXME: Result may not be here due to solve (i.e. the user may have entered "ans")
1356     if (math_equation_is_result(equation)) {
1357         math_equation_undo(equation);
1358         return;
1359     }
1360 
1361     equation->priv->in_solve = true;
1362 
1363     math_equation_set_number_mode(equation, NORMAL);
1364     g_thread_new("", math_equation_solve_real, equation);
1365 
1366     g_timeout_add(50, math_equation_look_for_answer, equation);
1367     g_timeout_add(100, math_equation_show_in_progress, equation);
1368 }
1369 
1370 
1371 static gpointer
math_equation_factorize_real(gpointer data)1372 math_equation_factorize_real(gpointer data)
1373 {
1374     GString *text;
1375     GList *factors, *factor, *next_factor;
1376     MPNumber x = mp_new();
1377     MathEquation *equation = MATH_EQUATION(data);
1378     SolveData *result = g_slice_new0(SolveData);
1379     MpDisplayFormat format = mp_serializer_get_number_format(equation->priv->serializer);
1380 
1381     mp_serializer_set_number_format(equation->priv->serializer, MP_DISPLAY_FORMAT_FIXED);
1382     math_equation_get_number(equation, &x);
1383     factors = mp_factorize(&x);
1384 
1385     text = g_string_new("");
1386 
1387     int e = 1;
1388     for (factor = factors; factor != NULL; factor = factor->next)
1389     {
1390         gchar *temp;
1391         MPNumber *n;
1392 
1393         n = factor->data;
1394         next_factor = factor->next;
1395         if (next_factor != NULL && mp_compare(n, next_factor->data) == 0)
1396         {
1397             e++;
1398             continue;
1399         }
1400         temp = mp_serializer_to_string(equation->priv->serializer, n);
1401         g_string_append(text, temp);
1402 
1403         if (e > 1)
1404         {
1405             g_string_append(text, "^");
1406             g_string_append_printf(text, "%d", e);
1407         }
1408         if (factor->next)
1409             g_string_append(text, " × ");
1410 
1411         e = 1;
1412 
1413         mp_clear(n);
1414         g_slice_free(MPNumber, n);
1415         g_free(temp);
1416     }
1417     g_list_free(factors);
1418 
1419     result->text_result = g_strndup(text->str, text->len);
1420     g_async_queue_push(equation->priv->queue, result);
1421     g_string_free(text, TRUE);
1422     mp_clear(&x);
1423 
1424     mp_serializer_set_number_format(equation->priv->serializer, format);
1425 
1426     return NULL;
1427 }
1428 
1429 
1430 void
math_equation_factorize(MathEquation * equation)1431 math_equation_factorize(MathEquation *equation)
1432 {
1433     MPNumber x = mp_new();
1434 
1435     g_return_if_fail(equation != NULL);
1436 
1437     // FIXME: should replace calculation or give error message
1438     if (equation->priv->in_solve)
1439         return;
1440 
1441     if (!math_equation_get_number(equation, &x) || !mp_is_integer(&x)) {
1442         /* Error displayed when trying to factorize a non-integer value */
1443         math_equation_set_status(equation, _("Need an integer to factorize"));
1444         mp_clear(&x);
1445         return;
1446     }
1447     mp_clear(&x);
1448     equation->priv->in_solve = true;
1449 
1450     g_thread_new("", math_equation_factorize_real, equation);
1451 
1452     g_timeout_add(50, math_equation_look_for_answer, equation);
1453     g_timeout_add(100, math_equation_show_in_progress, equation);
1454 }
1455 
1456 
1457 void
math_equation_delete(MathEquation * equation)1458 math_equation_delete(MathEquation *equation)
1459 {
1460     gint cursor;
1461     GtkTextIter start, end;
1462 
1463     g_return_if_fail(equation != NULL);
1464 
1465     g_object_get(G_OBJECT(equation), "cursor-position", &cursor, NULL);
1466     if (cursor >= gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation)))
1467         return;
1468 
1469     gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &start, cursor);
1470     gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &end, cursor+1);
1471     gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation), &start, &end);
1472 }
1473 
1474 
1475 void
math_equation_backspace(MathEquation * equation)1476 math_equation_backspace(MathEquation *equation)
1477 {
1478     g_return_if_fail(equation != NULL);
1479 
1480     /* Can't delete empty display */
1481     if (math_equation_is_empty(equation))
1482         return;
1483 
1484     if (gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(equation)))
1485         gtk_text_buffer_delete_selection(GTK_TEXT_BUFFER(equation), FALSE, FALSE);
1486     else {
1487         GtkTextIter iter;
1488         gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(equation)));
1489         gtk_text_buffer_backspace(GTK_TEXT_BUFFER(equation), &iter, TRUE, TRUE);
1490     }
1491 }
1492 
1493 
1494 void
math_equation_clear(MathEquation * equation)1495 math_equation_clear(MathEquation *equation)
1496 {
1497     g_return_if_fail(equation != NULL);
1498 
1499     math_equation_set_number_mode(equation, NORMAL);
1500     gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), "", -1);
1501     clear_ans(equation, FALSE);
1502 }
1503 
1504 
1505 void
math_equation_shift(MathEquation * equation,gint count)1506 math_equation_shift(MathEquation *equation, gint count)
1507 {
1508     MPNumber z = mp_new();
1509 
1510     g_return_if_fail(equation != NULL);
1511 
1512     if (!math_equation_get_number(equation, &z)) {
1513         math_equation_set_status(equation,
1514                                  /* This message is displayed in the status bar when a bit
1515                                   shift operation is performed and the display does not contain a number */
1516                                  _("No sane value to bitwise shift"));
1517         return;
1518     }
1519 
1520     mp_shift(&z, count, &z);
1521     math_equation_set_number(equation, &z);
1522     mp_clear(&z);
1523 }
1524 
1525 
1526 void
math_equation_toggle_bit(MathEquation * equation,guint bit)1527 math_equation_toggle_bit(MathEquation *equation, guint bit)
1528 {
1529     MPNumber x = mp_new();
1530     guint64 bits;
1531     gboolean result;
1532 
1533     g_return_if_fail(equation != NULL);
1534 
1535     result = math_equation_get_number(equation, &x);
1536     if (result) {
1537         MPNumber max = mp_new();
1538         mp_set_from_unsigned_integer(G_MAXUINT64, &max);
1539         if (mp_is_negative(&x) || mp_is_greater_than(&x, &max))
1540             result = FALSE;
1541         else
1542             bits = mp_to_unsigned_integer(&x);
1543         mp_clear(&max);
1544     }
1545 
1546     if (!result) {
1547         math_equation_set_status(equation,
1548                                  /* Message displayed when cannot toggle bit in display*/
1549                                  _("Displayed value not an integer"));
1550         mp_clear(&x);
1551         return;
1552     }
1553 
1554     bits ^= (1LL << (63 - bit));
1555 
1556     mp_set_from_unsigned_integer(bits, &x);
1557 
1558     // FIXME: Only do this if in ans format, otherwise set text in same format as previous number
1559     math_equation_set_number(equation, &x);
1560     mp_clear(&x);
1561 }
1562 
1563 
1564 static void
math_equation_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1565 math_equation_set_property(GObject      *object,
1566                            guint         prop_id,
1567                            const GValue *value,
1568                            GParamSpec   *pspec)
1569 {
1570     MathEquation *self;
1571 
1572     self = MATH_EQUATION(object);
1573 
1574     switch (prop_id) {
1575     case PROP_STATUS:
1576         math_equation_set_status(self, g_value_get_string(value));
1577         break;
1578     case PROP_DISPLAY:
1579         math_equation_set(self, g_value_get_string(value));
1580         break;
1581     case PROP_NUMBER_MODE:
1582         math_equation_set_number_mode(self, g_value_get_int(value));
1583         break;
1584     case PROP_ACCURACY:
1585         math_equation_set_accuracy(self, g_value_get_int(value));
1586         break;
1587     case PROP_SHOW_THOUSANDS_SEPARATORS:
1588         math_equation_set_show_thousands_separators(self, g_value_get_boolean(value));
1589         break;
1590     case PROP_SHOW_TRAILING_ZEROES:
1591         math_equation_set_show_trailing_zeroes(self, g_value_get_boolean(value));
1592         break;
1593     case PROP_NUMBER_FORMAT:
1594         math_equation_set_number_format(self, g_value_get_int(value));
1595         break;
1596     case PROP_BASE:
1597         math_equation_set_base(self, g_value_get_int(value));
1598         break;
1599     case PROP_WORD_SIZE:
1600         math_equation_set_word_size(self, g_value_get_int(value));
1601         break;
1602     case PROP_ANGLE_UNITS:
1603         math_equation_set_angle_units(self, g_value_get_int(value));
1604         break;
1605     case PROP_SOURCE_CURRENCY:
1606         math_equation_set_source_currency(self, g_value_get_string(value));
1607         break;
1608     case PROP_TARGET_CURRENCY:
1609         math_equation_set_target_currency(self, g_value_get_string(value));
1610         break;
1611     case PROP_SOURCE_UNITS:
1612         math_equation_set_source_units(self, g_value_get_string(value));
1613         break;
1614     case PROP_TARGET_UNITS:
1615         math_equation_set_target_units(self, g_value_get_string(value));
1616         break;
1617     default:
1618         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1619         break;
1620     }
1621 }
1622 
1623 
1624 static void
math_equation_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1625 math_equation_get_property(GObject    *object,
1626                            guint       prop_id,
1627                            GValue     *value,
1628                            GParamSpec *pspec)
1629 {
1630     MathEquation *self;
1631     gchar *text;
1632 
1633     self = MATH_EQUATION(object);
1634 
1635     switch (prop_id) {
1636     case PROP_STATUS:
1637         g_value_set_string(value, self->priv->state.status);
1638         break;
1639     case PROP_DISPLAY:
1640         text = math_equation_get_display(self);
1641         g_value_set_string(value, text);
1642         g_free(text);
1643         break;
1644     case PROP_EQUATION:
1645         text = math_equation_get_equation(self);
1646         g_value_set_string(value, text);
1647         g_free(text);
1648         break;
1649     case PROP_NUMBER_MODE:
1650         g_value_set_enum(value, self->priv->number_mode);
1651         break;
1652     case PROP_ACCURACY:
1653         g_value_set_int(value, mp_serializer_get_trailing_digits(self->priv->serializer));
1654         break;
1655     case PROP_SHOW_THOUSANDS_SEPARATORS:
1656         g_value_set_boolean(value, mp_serializer_get_show_thousands_separators(self->priv->serializer));
1657         break;
1658     case PROP_SHOW_TRAILING_ZEROES:
1659         g_value_set_boolean(value, mp_serializer_get_show_trailing_zeroes(self->priv->serializer));
1660         break;
1661     case PROP_NUMBER_FORMAT:
1662         g_value_set_enum(value, mp_serializer_get_number_format(self->priv->serializer));
1663         break;
1664     case PROP_BASE:
1665         g_value_set_int(value, math_equation_get_base(self));
1666         break;
1667     case PROP_WORD_SIZE:
1668         g_value_set_int(value, self->priv->word_size);
1669         break;
1670     case PROP_ANGLE_UNITS:
1671         g_value_set_enum(value, self->priv->angle_units);
1672         break;
1673     case PROP_SOURCE_CURRENCY:
1674         g_value_set_string(value, self->priv->source_currency);
1675         break;
1676     case PROP_TARGET_CURRENCY:
1677         g_value_set_string(value, self->priv->target_currency);
1678         break;
1679     case PROP_SOURCE_UNITS:
1680         g_value_set_string(value, self->priv->source_units);
1681         break;
1682     case PROP_TARGET_UNITS:
1683         g_value_set_string(value, self->priv->target_units);
1684         break;
1685     case PROP_SERIALIZER:
1686         g_value_set_object(value, self->priv->serializer);
1687         break;
1688     default:
1689         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1690         break;
1691     }
1692 }
1693 
1694 
1695 static void
math_equation_constructed(GObject * object)1696 math_equation_constructed(GObject *object)
1697 {
1698     GtkTextBuffer *parent_class;
1699     parent_class = g_type_class_peek_parent(MATH_EQUATION_GET_CLASS(object));
1700     if (G_OBJECT_CLASS(parent_class)->constructed)
1701         G_OBJECT_CLASS(parent_class)->constructed(object);
1702 
1703     MATH_EQUATION(object)->priv->ans_tag = gtk_text_buffer_create_tag(GTK_TEXT_BUFFER(object), NULL, "weight", PANGO_WEIGHT_BOLD, NULL);
1704 }
1705 
1706 
1707 static void
math_equation_class_init(MathEquationClass * klass)1708 math_equation_class_init(MathEquationClass *klass)
1709 {
1710     static GEnumValue number_mode_values[] =
1711     {
1712       {NORMAL,      "normal",      "normal"},
1713       {SUPERSCRIPT, "superscript", "superscript"},
1714       {SUBSCRIPT,   "subscript",   "subscript"},
1715       {0, NULL, NULL}
1716     };
1717     static GEnumValue angle_unit_values[] =
1718     {
1719       {MP_RADIANS,  "radians",  "radians"},
1720       {MP_DEGREES,  "degrees",  "degrees"},
1721       {MP_GRADIANS, "gradians", "gradians"},
1722       {0, NULL, NULL}
1723     };
1724     GObjectClass *object_class = G_OBJECT_CLASS(klass);
1725 
1726     object_class->get_property = math_equation_get_property;
1727     object_class->set_property = math_equation_set_property;
1728     object_class->constructed = math_equation_constructed;
1729 
1730     number_mode_type = g_enum_register_static("NumberMode", number_mode_values);
1731     number_format_type = math_mp_display_format_get_type();
1732     angle_unit_type = g_enum_register_static("AngleUnit", angle_unit_values);
1733 
1734     g_object_class_install_property(object_class,
1735                                     PROP_STATUS,
1736                                     g_param_spec_string("status",
1737                                                         "status",
1738                                                         "Equation status",
1739                                                         "",
1740                                                         G_PARAM_READWRITE));
1741     g_object_class_install_property(object_class,
1742                                     PROP_DISPLAY,
1743                                     g_param_spec_string("display",
1744                                                         "display",
1745                                                         "Displayed equation text",
1746                                                         "",
1747                                                         G_PARAM_READWRITE));
1748     g_object_class_install_property(object_class,
1749                                     PROP_EQUATION,
1750                                     g_param_spec_string("equation",
1751                                                         "equation",
1752                                                         "Equation text",
1753                                                         "",
1754                                                         G_PARAM_READABLE));
1755     g_object_class_install_property(object_class,
1756                                     PROP_NUMBER_MODE,
1757                                     g_param_spec_enum("number-mode",
1758                                                       "number-mode",
1759                                                       "Input number mode",
1760                                                       number_mode_type,
1761                                                       NORMAL,
1762                                                       G_PARAM_READWRITE));
1763     g_object_class_install_property(object_class,
1764                                     PROP_ACCURACY,
1765                                     g_param_spec_int("accuracy",
1766                                                      "accuracy",
1767                                                      "Display accuracy",
1768                                                      0, 20, 9,
1769                                                      G_PARAM_READWRITE));
1770     g_object_class_install_property(object_class,
1771                                     PROP_SHOW_THOUSANDS_SEPARATORS,
1772                                     g_param_spec_boolean("show-thousands-separators",
1773                                                          "show-thousands-separators",
1774                                                          "Show thousands separators",
1775                                                          TRUE,
1776                                                          G_PARAM_READWRITE));
1777     g_object_class_install_property(object_class,
1778                                     PROP_SHOW_TRAILING_ZEROES,
1779                                     g_param_spec_boolean("show-trailing-zeroes",
1780                                                          "show-trailing-zeroes",
1781                                                          "Show trailing zeroes",
1782                                                          FALSE,
1783                                                          G_PARAM_READWRITE));
1784     g_object_class_install_property(object_class,
1785                                     PROP_NUMBER_FORMAT,
1786                                     g_param_spec_enum("number-format",
1787                                                       "number-format",
1788                                                       "Display format",
1789                                                       number_format_type,
1790                                                       MP_DISPLAY_FORMAT_FIXED,
1791                                                       G_PARAM_READWRITE));
1792     g_object_class_install_property(object_class,
1793                                     PROP_BASE,
1794                                     g_param_spec_int("base",
1795                                                      "base",
1796                                                      "Default number base (derived from number-format)",
1797                                                      2, 16, 10,
1798                                                      G_PARAM_READWRITE));
1799     g_object_class_install_property(object_class,
1800                                     PROP_WORD_SIZE,
1801                                     g_param_spec_int("word-size",
1802                                                      "word-size",
1803                                                      "Word size in bits",
1804                                                      8, 64, 64,
1805                                                      G_PARAM_READWRITE));
1806     g_object_class_install_property(object_class,
1807                                     PROP_ANGLE_UNITS,
1808                                     g_param_spec_enum("angle-units",
1809                                                       "angle-units",
1810                                                       "Angle units",
1811                                                       angle_unit_type,
1812                                                       MP_DEGREES,
1813                                                       G_PARAM_READWRITE));
1814     g_object_class_install_property(object_class,
1815                                     PROP_SOURCE_CURRENCY,
1816                                     g_param_spec_string("source-currency",
1817                                                         "source-currency",
1818                                                         "Source Currency",
1819                                                         "",
1820                                                         G_PARAM_READWRITE));
1821     g_object_class_install_property(object_class,
1822                                     PROP_TARGET_CURRENCY,
1823                                     g_param_spec_string("target-currency",
1824                                                         "target-currency",
1825                                                         "target Currency",
1826                                                         "",
1827                                                         G_PARAM_READWRITE));
1828     g_object_class_install_property(object_class,
1829                                     PROP_SOURCE_UNITS,
1830                                     g_param_spec_string("source-units",
1831                                                         "source-units",
1832                                                         "Source Units",
1833                                                         "",
1834                                                         G_PARAM_READWRITE));
1835     g_object_class_install_property(object_class,
1836                                     PROP_TARGET_UNITS,
1837                                     g_param_spec_string("target-units",
1838                                                         "target-units",
1839                                                         "target Units",
1840                                                         "",
1841                                                         G_PARAM_READWRITE));
1842     g_object_class_install_property(object_class,
1843                                     PROP_SERIALIZER,
1844                                     g_param_spec_object("serializer",
1845                                                         "serializer",
1846                                                         "Serializer",
1847                                                         MP_TYPE_SERIALIZER,
1848                                                         G_PARAM_READABLE));
1849 
1850     GType param_types[2] = {G_TYPE_STRING, G_TYPE_POINTER};
1851     g_signal_newv("history",
1852                   G_TYPE_FROM_CLASS(klass),
1853                   G_SIGNAL_RUN_LAST,
1854                   0,
1855                   NULL,
1856                   NULL,
1857                   NULL,
1858                   G_TYPE_NONE,
1859                   2,
1860                   param_types);
1861 
1862     g_signal_new ("display-changed",
1863                   G_TYPE_FROM_CLASS(klass),
1864                   G_SIGNAL_RUN_LAST,
1865                   0,
1866                   NULL,
1867                   NULL,
1868                   NULL,
1869                   G_TYPE_NONE,
1870                   0,
1871                   NULL);
1872 }
1873 
1874 
1875 static void
pre_insert_text_cb(MathEquation * equation,GtkTextIter * location,gchar * text,gint len,gpointer user_data)1876 pre_insert_text_cb(MathEquation  *equation,
1877                    GtkTextIter   *location,
1878                    gchar         *text,
1879                    gint           len,
1880                    gpointer       user_data)
1881 {
1882     gunichar c;
1883     gint cursor;
1884 
1885     if (equation->priv->in_reformat)
1886         return;
1887 
1888     /* If following a delete then have already pushed undo stack (GtkTextBuffer
1889        doesn't indicate replace operations so we have to infer them) */
1890     if (!equation->priv->in_delete)
1891         math_equation_push_undo_stack(equation);
1892 
1893     /* Clear result on next digit entered if cursor at end of line */
1894     c = g_utf8_get_char(text);
1895     g_object_get(G_OBJECT(equation), "cursor-position", &cursor, NULL);
1896     if ((g_unichar_isdigit(c) || c == mp_serializer_get_radix(equation->priv->serializer)) &&
1897          math_equation_is_result(equation) &&
1898          cursor >= gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation))) {
1899         gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), "", -1);
1900         clear_ans(equation, FALSE);
1901         gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(equation), location);
1902     }
1903 
1904     if (equation->priv->ans_start) {
1905         gint ans_start, ans_end;
1906         gint offset;
1907 
1908         offset = gtk_text_iter_get_offset(location);
1909         get_ans_offsets(equation, &ans_start, &ans_end);
1910 
1911         /* Inserted inside or right before ans */
1912         if (offset >= ans_start && offset < ans_end)
1913             clear_ans(equation, TRUE);
1914     }
1915 }
1916 
1917 
1918 static gboolean
on_delete(MathEquation * equation)1919 on_delete(MathEquation *equation)
1920 {
1921     equation->priv->in_delete = FALSE;
1922     return FALSE;
1923 }
1924 
1925 
1926 static void
pre_delete_range_cb(MathEquation * equation,GtkTextIter * start,GtkTextIter * end,gpointer user_data)1927 pre_delete_range_cb(MathEquation  *equation,
1928                     GtkTextIter   *start,
1929                     GtkTextIter   *end,
1930                     gpointer       user_data)
1931 {
1932     if (equation->priv->in_reformat)
1933         return;
1934 
1935     math_equation_push_undo_stack(equation);
1936 
1937     equation->priv->in_delete = TRUE;
1938     g_idle_add((GSourceFunc)on_delete, equation);
1939 
1940     if (equation->priv->ans_start) {
1941         gint ans_start, ans_end;
1942         gint start_offset, end_offset;
1943 
1944         start_offset = gtk_text_iter_get_offset(start);
1945         end_offset = gtk_text_iter_get_offset(end);
1946         get_ans_offsets(equation, &ans_start, &ans_end);
1947 
1948         /* Deleted part of ans */
1949         if (start_offset < ans_end && end_offset > ans_start)
1950             clear_ans(equation, TRUE);
1951     }
1952 }
1953 
1954 
1955 static void
insert_text_cb(MathEquation * equation,GtkTextIter * location,gchar * text,gint len,gpointer user_data)1956 insert_text_cb(MathEquation  *equation,
1957                GtkTextIter   *location,
1958                gchar         *text,
1959                gint           len,
1960                gpointer       user_data)
1961 {
1962     if (equation->priv->in_reformat)
1963         return;
1964 
1965     equation->priv->state.entered_multiply = strcmp(text, "×") == 0;
1966 
1967     /* Update thousands separators */
1968     reformat_separators(equation);
1969 
1970     g_object_notify(G_OBJECT(equation), "display");
1971 }
1972 
1973 
1974 static void
delete_range_cb(MathEquation * equation,GtkTextIter * start,GtkTextIter * end,gpointer user_data)1975 delete_range_cb(MathEquation  *equation,
1976                 GtkTextIter   *start,
1977                 GtkTextIter   *end,
1978                 gpointer       user_data)
1979 {
1980     if (equation->priv->in_reformat)
1981         return;
1982 
1983     equation->priv->state.entered_multiply = FALSE;
1984 
1985     /* Update thousands separators */
1986     reformat_separators(equation);
1987 
1988     // FIXME: A replace will emit this both for delete-range and insert-text, can it be avoided?
1989     g_object_notify(G_OBJECT(equation), "display");
1990 }
1991 
1992 
1993 static void
math_equation_init(MathEquation * equation)1994 math_equation_init(MathEquation *equation)
1995 {
1996     /* Digits localized for the given language */
1997     const char *digit_values = _("0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F");
1998     const char *default_digits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
1999     gchar **digits;
2000     /* Default to using untranslated digits, this is because it doesn't make sense in most languages and we need to make this optional.
2001      * See https://bugzilla.gnome.org/show_bug.cgi?id=632661 */
2002     gboolean use_default_digits = TRUE;
2003     int i;
2004 
2005     equation->priv = math_equation_get_instance_private (equation);
2006 
2007     g_signal_connect(equation, "insert-text", G_CALLBACK(pre_insert_text_cb), equation);
2008     g_signal_connect(equation, "delete-range", G_CALLBACK(pre_delete_range_cb), equation);
2009     g_signal_connect_after(equation, "insert-text", G_CALLBACK(insert_text_cb), equation);
2010     g_signal_connect_after(equation, "delete-range", G_CALLBACK(delete_range_cb), equation);
2011 
2012     digits = g_strsplit(digit_values, ",", -1);
2013     for (i = 0; i < 16; i++) {
2014         if (use_default_digits || digits[i] == NULL) {
2015             use_default_digits = TRUE;
2016             equation->priv->digits[i] = g_utf8_get_char(default_digits[i]);
2017         }
2018         else
2019             equation->priv->digits[i] = g_utf8_get_char(digits[i]);
2020     }
2021     g_strfreev(digits);
2022 
2023     equation->priv->variables = math_variables_new();
2024 
2025     equation->priv->state.ans = mp_new();
2026     equation->priv->state.status = g_strdup("");
2027     equation->priv->word_size = 32;
2028     equation->priv->angle_units = MP_DEGREES;
2029     // FIXME: Pick based on locale
2030     equation->priv->source_currency = g_strdup("");
2031     equation->priv->target_currency = g_strdup("");
2032     equation->priv->source_units = g_strdup("");
2033     equation->priv->target_units = g_strdup("");
2034     equation->priv->serializer = mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC, 10, 9);
2035     equation->priv->queue = g_async_queue_new();
2036 
2037     mp_set_from_integer(0, &equation->priv->state.ans);
2038 }
2039