diff options
Diffstat (limited to 'src/math-equation.c')
-rw-r--r-- | src/math-equation.c | 1002 |
1 files changed, 627 insertions, 375 deletions
diff --git a/src/math-equation.c b/src/math-equation.c index ef86221..5bcae64 100644 --- a/src/math-equation.c +++ b/src/math-equation.c @@ -1,20 +1,12 @@ -/* Copyright (c) 1987-2008 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2008-2009 Robert Ancell +/* + * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (C) 2008-2011 Robert Ancell * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 2 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. */ #include <stdlib.h> @@ -24,14 +16,15 @@ #include <math.h> #include <errno.h> #include <glib.h> -#include <langinfo.h> -#include <locale.h> +#include <glib/gi18n.h> #include "math-equation.h" #include "mp.h" #include "mp-equation.h" -#include "currency.h" +#include "mp-serializer.h" +#include "mp-enums.h" +#include "unit-manager.h" enum { @@ -48,7 +41,10 @@ enum { PROP_WORD_SIZE, PROP_ANGLE_UNITS, PROP_SOURCE_CURRENCY, - PROP_TARGET_CURRENCY + PROP_TARGET_CURRENCY, + PROP_SOURCE_UNITS, + PROP_TARGET_UNITS, + PROP_SERIALIZER }; static GType number_mode_type, number_format_type, angle_unit_type; @@ -71,44 +67,49 @@ struct MathEquationPrivate { GtkTextTag *ans_tag; - gint show_tsep; /* Set if the thousands separator should be shown. */ - gint show_zeroes; /* Set if trailing zeroes should be shown. */ - DisplayFormat format; /* Number display mode. */ - gint accuracy; /* Number of digits to show */ gint word_size; /* Word size in bits */ MPAngleUnit angle_units; /* Units for trigonometric functions */ char *source_currency; char *target_currency; - gint base; /* Numeric base */ + char *source_units; + char *target_units; NumberMode number_mode; /* ??? */ gboolean can_super_minus; /* TRUE if entering minus can generate a superscript minus */ - const char *digits[16]; /* Localized digit values */ - const char *radix; /* Locale specific radix string. */ - const char *tsep; /* Locale specific thousands separator. */ - gint tsep_count; /* Number of digits between separator. */ + gunichar digits[16]; /* Localized digits */ GtkTextMark *ans_start, *ans_end; MathEquationState state; /* Equation state */ - GList *undo_stack; /* History of expression mode states */ + GList *undo_stack; /* History of expression mode states */ GList *redo_stack; gboolean in_undo_operation; - + gboolean in_reformat; gboolean in_delete; + gboolean in_solve; + MathVariables *variables; + MpSerializer *serializer; + + GAsyncQueue *queue; }; +typedef struct { + MPNumber *number_result; + gchar *text_result; + gchar *error; +} SolveData; + G_DEFINE_TYPE (MathEquation, math_equation, GTK_TYPE_TEXT_BUFFER); MathEquation * math_equation_new() { - return g_object_new (math_equation_get_type(), NULL); + return g_object_new(math_equation_get_type(), NULL); } @@ -123,7 +124,7 @@ static void get_ans_offsets(MathEquation *equation, gint *start, gint *end) { GtkTextIter iter; - + if (!equation->priv->ans_start) { *start = *end = -1; return; @@ -143,13 +144,13 @@ reformat_ans(MathEquation *equation) return; gchar *orig_ans_text; - gchar ans_text[MAX_DIGITS]; + gchar *ans_text; GtkTextIter ans_start, ans_end; gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_start, equation->priv->ans_start); gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_end, equation->priv->ans_end); orig_ans_text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation), &ans_start, &ans_end, FALSE); - display_make_number(equation, ans_text, MAX_DIGITS, &equation->priv->state.ans); + ans_text = mp_serializer_to_string(equation->priv->serializer, &equation->priv->state.ans); if (strcmp(orig_ans_text, ans_text) != 0) { gint start; @@ -171,166 +172,122 @@ reformat_ans(MathEquation *equation) gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_start, equation->priv->ans_start); gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_end, equation->priv->ans_end); g_free(orig_ans_text); + g_free(ans_text); +} + + +static gint +count_digits(MathEquation *equation, const gchar *text) +{ + const gchar *read_iter; + gint count = 0; + + read_iter = text; + while (*read_iter != '\0') { + if (!g_unichar_isdigit(g_utf8_get_char(read_iter))) + return count; + + read_iter = g_utf8_next_char(read_iter); + + /* Allow a thousands separator between digits follow a digit */ + if (g_utf8_get_char(read_iter) == mp_serializer_get_thousands_separator(equation->priv->serializer)) { + read_iter = g_utf8_next_char(read_iter); + if (!g_unichar_isdigit(g_utf8_get_char(read_iter))) + return count; + } + + count++; + } + + return count; } -/* NOTE: Not efficent but easy to write */ -// FIXME: This is just a lexer - use the same lexer as the solver static void -reformat_base(MathEquation *equation, gint old_base) +reformat_separators(MathEquation *equation) { - gunichar sub_zero, sub_nine; gchar *text, *read_iter; - gboolean in_number = FALSE, have_radix = FALSE; - gint offset = 0, offset_step = 0, max_digit = 0, base = -1, base_offset = 0; gint ans_start, ans_end; + gint offset, digit_offset = 0; + gboolean in_number = FALSE, in_radix = FALSE, last_is_tsep = FALSE; - if (equation->priv->base == old_base) - return; - - sub_zero = g_utf8_get_char("₀"); - sub_nine = g_utf8_get_char("₉"); + equation->priv->in_undo_operation = TRUE; + equation->priv->in_reformat = TRUE; - read_iter = text = math_equation_get_display(equation); + text = math_equation_get_display(equation); get_ans_offsets(equation, &ans_start, &ans_end); - while (TRUE) { + for (read_iter = text, offset = 0; *read_iter != '\0'; read_iter = g_utf8_next_char(read_iter), offset++) { gunichar c; - gint digit = -1, sub_digit = -1; + gboolean expect_tsep; /* See what digit this character is */ c = g_utf8_get_char(read_iter); - if (c >= sub_zero && c <= sub_nine) - sub_digit = c - sub_zero; - else if (c >= 'a' && c <= 'z') - digit = c - 'a'; - else if (c >= 'A' && c <= 'Z') - digit = c - 'A'; - else - digit = g_unichar_digit_value(c); + + expect_tsep = math_equation_get_base(equation) == 10 && + mp_serializer_get_show_thousands_separators(equation->priv->serializer) && + in_number && !in_radix && !last_is_tsep && + digit_offset > 0 && digit_offset % mp_serializer_get_thousands_separator_count(equation->priv->serializer) == 0; + last_is_tsep = FALSE; /* Don't mess with ans */ if (offset >= ans_start && offset <= ans_end) { - digit = -1; - sub_digit = -1; + in_number = in_radix = FALSE; + continue; } + if (g_unichar_isdigit(c)) { + if (!in_number) + digit_offset = count_digits(equation, read_iter); + in_number = TRUE; - if (in_number && digit >= 0) { - if (digit > max_digit) - max_digit = digit; - } - else if (in_number && sub_digit >= 0) { - if (base < 0) { - base_offset = offset; - base = 0; + /* Expected a thousands separator between these digits - insert it */ + if (expect_tsep) { + GtkTextIter iter; + gchar buffer[7]; + gint len; + + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &iter, offset); + len = g_unichar_to_utf8(mp_serializer_get_thousands_separator(equation->priv->serializer), buffer); + buffer[len] = '\0'; + gtk_text_buffer_insert(GTK_TEXT_BUFFER(equation), &iter, buffer, -1); + offset++; + last_is_tsep = TRUE; } - base = base * 10 + sub_digit; + digit_offset--; } - else if (in_number) { - /* Allow one radix inside a number */ - if (!have_radix && base < 0 && strncmp(read_iter, equation->priv->radix, strlen(equation->priv->radix)) == 0) { - have_radix = TRUE; - read_iter += strlen(equation->priv->radix); - offset += g_utf8_strlen(equation->priv->radix, -1); - continue; - } - - /* If had no base then insert it */ - if (base < 0) { - GtkTextIter iter; - gint multiplier = 1; - gint b = old_base; - const char *digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"}; - - equation->priv->in_undo_operation = TRUE; - equation->priv->in_reformat = TRUE; - - while (b / multiplier != 0) - multiplier *= 10; - while (multiplier != 1) { - int d; - multiplier /= 10; - d = b / multiplier; - gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &iter, offset + offset_step); - gtk_text_buffer_insert(GTK_TEXT_BUFFER(equation), &iter, digits[d], -1); - offset_step++; - b -= d * multiplier; - } - - equation->priv->in_reformat = FALSE; - equation->priv->in_undo_operation = FALSE; - } - /* Remove the base if the current value */ - else if (max_digit < base && base == equation->priv->base) { + else if (c == mp_serializer_get_radix(equation->priv->serializer)) { + in_number = in_radix = TRUE; + } + else if (c == mp_serializer_get_thousands_separator(equation->priv->serializer)) { + /* Didn't expect thousands separator - delete it */ + if (!expect_tsep && in_number) { GtkTextIter start, end; - - equation->priv->in_undo_operation = TRUE; - equation->priv->in_reformat = TRUE; - - gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &start, base_offset + offset_step); - gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &end, offset + offset_step); + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &start, offset); + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &end, offset + 1); gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation), &start, &end); - offset_step -= offset - base_offset; - - equation->priv->in_reformat = FALSE; - equation->priv->in_undo_operation = FALSE; + offset--; } - - in_number = FALSE; + else + last_is_tsep = TRUE; } - else if (digit >= 0) { - in_number = TRUE; - have_radix = FALSE; - base = -1; - max_digit = digit; + else { + in_number = in_radix = FALSE; } - - if (c == '\0') - break; - - read_iter = g_utf8_next_char(read_iter); - offset++; } g_free(text); -} - - -static void -reformat_separators(MathEquation *equation) -{ -#if 0 - gchar *text, *read_iter; - gboolean in_number = FALSE, in_fraction = FALSE; - - text = math_equation_get_display(equation); - - /* Find numbers in display, and modify if necessary */ - read_iter = text; - while(*read_iter != '\0') { - gunichar c; - c = g_utf8_get_char(read_iter); - - if (strncmp(read_iter, equation->priv->tsep, strlen(equation->priv->tsep)) == 0) - ; - read_iter = g_utf8_next_char(read_iter); - } - - g_free(text); -#endif + equation->priv->in_reformat = FALSE; + equation->priv->in_undo_operation = FALSE; } static void -reformat_display(MathEquation *equation, gint old_base) +reformat_display(MathEquation *equation) { /* Change ans */ reformat_ans(equation); - /* Add/remove base suffixes if have changed base */ - reformat_base(equation, old_base); - /* Add/remove thousands separators */ reformat_separators(equation); } @@ -362,7 +319,7 @@ get_current_state(MathEquation *equation) state->can_super_minus = equation->priv->can_super_minus; state->entered_multiply = equation->priv->state.entered_multiply; state->status = g_strdup(equation->priv->state.status); - + return state; } @@ -396,7 +353,7 @@ math_equation_push_undo_stack(MathEquation *equation) equation->priv->redo_stack = NULL; state = get_current_state(equation); - equation->priv->undo_stack = g_list_prepend(equation->priv->undo_stack, state); + equation->priv->undo_stack = g_list_prepend(equation->priv->undo_stack, state); } @@ -425,7 +382,7 @@ static void apply_state(MathEquation *equation, MathEquationState *state) { GtkTextIter cursor; - + /* Disable undo detection */ equation->priv->in_undo_operation = TRUE; @@ -459,13 +416,15 @@ math_equation_copy(MathEquation *equation) { GtkTextIter start, end; gchar *text; + + g_return_if_fail(equation != NULL); if (!gtk_text_buffer_get_selection_bounds(GTK_TEXT_BUFFER(equation), &start, &end)) gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation), &start, &end); text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation), &start, &end, FALSE); gtk_clipboard_set_text(gtk_clipboard_get(GDK_NONE), text, -1); - g_free (text); + g_free(text); } @@ -473,7 +432,6 @@ static void on_paste(GtkClipboard *clipboard, const gchar *text, gpointer data) { MathEquation *equation = data; - if (text != NULL) math_equation_insert(equation, text); } @@ -482,6 +440,7 @@ on_paste(GtkClipboard *clipboard, const gchar *text, gpointer data) void math_equation_paste(MathEquation *equation) { + g_return_if_fail(equation != NULL); gtk_clipboard_request_text(gtk_clipboard_get(GDK_NONE), on_paste, equation); } @@ -492,6 +451,8 @@ math_equation_undo(MathEquation *equation) GList *link; MathEquationState *state; + g_return_if_fail(equation != NULL); + if (!equation->priv->undo_stack) { math_equation_set_status(equation, /* Error shown when trying to undo with no undo history */ @@ -517,6 +478,8 @@ math_equation_redo(MathEquation *equation) GList *link; MathEquationState *state; + g_return_if_fail(equation != NULL); + if (!equation->priv->redo_stack) { math_equation_set_status(equation, /* Error shown when trying to redo with no redo history */ @@ -536,33 +499,25 @@ math_equation_redo(MathEquation *equation) } -const gchar * +gunichar math_equation_get_digit_text(MathEquation *equation, guint digit) { - return equation->priv->digits[digit]; -} - - -const gchar * -math_equation_get_numeric_point_text(MathEquation *equation) -{ - return equation->priv->radix; -} + g_return_val_if_fail(equation != NULL, '?'); + g_return_val_if_fail(digit < 16, '?'); - -const gchar *math_equation_get_thousands_separator_text(MathEquation *equation) -{ - return equation->priv->tsep; + return equation->priv->digits[digit]; } void math_equation_set_accuracy(MathEquation *equation, gint accuracy) { - if (equation->priv->accuracy == accuracy) + g_return_if_fail(equation != NULL); + + if (mp_serializer_get_trailing_digits(equation->priv->serializer) == accuracy) return; - equation->priv->accuracy = accuracy; - reformat_display(equation, equation->priv->base); + mp_serializer_set_trailing_digits(equation->priv->serializer, accuracy); + reformat_display(equation); g_object_notify(G_OBJECT(equation), "accuracy"); } @@ -570,17 +525,22 @@ math_equation_set_accuracy(MathEquation *equation, gint accuracy) gint math_equation_get_accuracy(MathEquation *equation) { - return equation->priv->accuracy; + g_return_val_if_fail(equation != NULL, 0); + + return mp_serializer_get_trailing_digits(equation->priv->serializer); } void math_equation_set_show_thousands_separators(MathEquation *equation, gboolean visible) { - if ((equation->priv->show_tsep && visible) || (!equation->priv->show_tsep && !visible)) + g_return_if_fail(equation != NULL); + + if (mp_serializer_get_show_thousands_separators(equation->priv->serializer) == visible) return; - equation->priv->show_tsep = visible; - reformat_display(equation, equation->priv->base); + + mp_serializer_set_show_thousands_separators(equation->priv->serializer, visible); + reformat_display(equation); g_object_notify(G_OBJECT(equation), "show-thousands-separators"); } @@ -588,17 +548,21 @@ math_equation_set_show_thousands_separators(MathEquation *equation, gboolean vis gboolean math_equation_get_show_thousands_separators(MathEquation *equation) { - return equation->priv->show_tsep; + g_return_val_if_fail(equation != NULL, FALSE); + return mp_serializer_get_show_thousands_separators(equation->priv->serializer); } void math_equation_set_show_trailing_zeroes(MathEquation *equation, gboolean visible) { - if ((equation->priv->show_zeroes && visible) || (!equation->priv->show_zeroes && !visible)) + g_return_if_fail(equation != NULL); + + if (mp_serializer_get_show_trailing_zeroes(equation->priv->serializer) == visible) return; - equation->priv->show_zeroes = visible; - reformat_display(equation, equation->priv->base); + + mp_serializer_set_show_trailing_zeroes(equation->priv->serializer, visible); + reformat_display(equation); g_object_notify(G_OBJECT(equation), "show-trailing-zeroes"); } @@ -606,40 +570,43 @@ math_equation_set_show_trailing_zeroes(MathEquation *equation, gboolean visible) gboolean math_equation_get_show_trailing_zeroes(MathEquation *equation) { - return equation->priv->show_zeroes; + g_return_val_if_fail(equation != NULL, FALSE); + return mp_serializer_get_show_trailing_zeroes(equation->priv->serializer); } void -math_equation_set_number_format(MathEquation *equation, DisplayFormat format) +math_equation_set_number_format(MathEquation *equation, MpDisplayFormat format) { - if (equation->priv->format == format) + g_return_if_fail(equation != NULL); + + if (mp_serializer_get_number_format(equation->priv->serializer) == format) return; - equation->priv->format = format; - reformat_display(equation, equation->priv->base); + mp_serializer_set_number_format(equation->priv->serializer, format); + reformat_display(equation); g_object_notify(G_OBJECT(equation), "number-format"); } -DisplayFormat +MpDisplayFormat math_equation_get_number_format(MathEquation *equation) { - return equation->priv->format; + g_return_val_if_fail(equation != NULL, MP_DISPLAY_FORMAT_AUTOMATIC); + return mp_serializer_get_number_format(equation->priv->serializer); } void math_equation_set_base(MathEquation *equation, gint base) { - gint old_base; + g_return_if_fail(equation != NULL); - if (equation->priv->base == base) + if (mp_serializer_get_base(equation->priv->serializer) == base) return; - old_base = equation->priv->base; - equation->priv->base = base; - reformat_display(equation, old_base); + mp_serializer_set_base(equation->priv->serializer, base); + reformat_display(equation); g_object_notify(G_OBJECT(equation), "base"); } @@ -647,15 +614,19 @@ math_equation_set_base(MathEquation *equation, gint base) gint math_equation_get_base(MathEquation *equation) { - return equation->priv->base; + g_return_val_if_fail(equation != NULL, 10); + return mp_serializer_get_base(equation->priv->serializer); } void math_equation_set_word_size(MathEquation *equation, gint word_size) { + g_return_if_fail(equation != NULL); + if (equation->priv->word_size == word_size) return; + equation->priv->word_size = word_size; g_object_notify(G_OBJECT(equation), "word-size"); } @@ -664,6 +635,7 @@ math_equation_set_word_size(MathEquation *equation, gint word_size) gint math_equation_get_word_size(MathEquation *equation) { + g_return_val_if_fail(equation != NULL, 64); return equation->priv->word_size; } @@ -671,8 +643,11 @@ math_equation_get_word_size(MathEquation *equation) void math_equation_set_angle_units(MathEquation *equation, MPAngleUnit angle_units) { + g_return_if_fail(equation != NULL); + if (equation->priv->angle_units == angle_units) return; + equation->priv->angle_units = angle_units; g_object_notify(G_OBJECT(equation), "angle-units"); } @@ -681,6 +656,7 @@ math_equation_set_angle_units(MathEquation *equation, MPAngleUnit angle_units) MPAngleUnit math_equation_get_angle_units(MathEquation *equation) { + g_return_val_if_fail(equation != NULL, MP_DEGREES); return equation->priv->angle_units; } @@ -688,9 +664,8 @@ math_equation_get_angle_units(MathEquation *equation) void math_equation_set_source_currency(MathEquation *equation, const gchar *currency) { - // FIXME: Pick based on locale - if (!currency || currency[0] == '\0') - currency = currency_names[0].short_name; + g_return_if_fail(equation != NULL); + g_return_if_fail(currency != NULL); if (strcmp(equation->priv->source_currency, currency) == 0) return; @@ -699,9 +674,11 @@ math_equation_set_source_currency(MathEquation *equation, const gchar *currency) g_object_notify(G_OBJECT(equation), "source-currency"); } + const gchar * math_equation_get_source_currency(MathEquation *equation) { + g_return_val_if_fail(equation != NULL, NULL); return equation->priv->source_currency; } @@ -709,9 +686,8 @@ math_equation_get_source_currency(MathEquation *equation) void math_equation_set_target_currency(MathEquation *equation, const gchar *currency) { - // FIXME: Pick based on locale - if (!currency || currency[0] == '\0') - currency = currency_names[0].short_name; + g_return_if_fail(equation != NULL); + g_return_if_fail(currency != NULL); if (strcmp(equation->priv->target_currency, currency) == 0) return; @@ -724,25 +700,75 @@ math_equation_set_target_currency(MathEquation *equation, const gchar *currency) const gchar * math_equation_get_target_currency(MathEquation *equation) { + g_return_val_if_fail(equation != NULL, NULL); return equation->priv->target_currency; } void +math_equation_set_source_units(MathEquation *equation, const gchar *units) +{ + g_return_if_fail(equation != NULL); + g_return_if_fail(units != NULL); + + if (strcmp(equation->priv->source_units, units) == 0) + return; + + g_free(equation->priv->source_units); + equation->priv->source_units = g_strdup(units); + g_object_notify(G_OBJECT(equation), "source-units"); +} + +const gchar * +math_equation_get_source_units(MathEquation *equation) +{ + g_return_val_if_fail(equation != NULL, NULL); + return equation->priv->source_units; +} + + +void +math_equation_set_target_units(MathEquation *equation, const gchar *units) +{ + g_return_if_fail(equation != NULL); + g_return_if_fail(units != NULL); + + if (strcmp(equation->priv->target_units, units) == 0) + return; + + g_free(equation->priv->target_units); + equation->priv->target_units = g_strdup(units); + g_object_notify(G_OBJECT(equation), "target-units"); +} + + +const gchar * +math_equation_get_target_units(MathEquation *equation) +{ + g_return_val_if_fail(equation != NULL, NULL); + return equation->priv->target_units; +} + + +void math_equation_set_status(MathEquation *equation, const gchar *status) { + g_return_if_fail(equation != NULL); + g_return_if_fail(status != NULL); + if (strcmp(equation->priv->state.status, status) == 0) return; g_free(equation->priv->state.status); equation->priv->state.status = g_strdup(status); - g_object_notify(G_OBJECT(equation), "status"); + g_object_notify(G_OBJECT(equation), "status"); } const gchar * math_equation_get_status(MathEquation *equation) { + g_return_val_if_fail(equation != NULL, NULL); return equation->priv->state.status; } @@ -750,6 +776,7 @@ math_equation_get_status(MathEquation *equation) gboolean math_equation_is_empty(MathEquation *equation) { + g_return_val_if_fail(equation != NULL, FALSE); return gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation)) == 0; } @@ -759,6 +786,8 @@ math_equation_is_result(MathEquation *equation) { char *text; gboolean result; + + g_return_val_if_fail(equation != NULL, FALSE); text = math_equation_get_equation(equation); result = strcmp(text, "ans") == 0; @@ -773,6 +802,8 @@ math_equation_get_display(MathEquation *equation) { GtkTextIter start, end; + g_return_val_if_fail(equation != NULL, NULL); + gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation), &start, &end); return gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation), &start, &end, FALSE); } @@ -781,19 +812,54 @@ math_equation_get_display(MathEquation *equation) gchar * math_equation_get_equation(MathEquation *equation) { - char *text, *t; - gint ans_start, ans_end; + gchar *text; + GString *eq_text; + gint ans_start = -1, ans_end = -1, offset; + const gchar *read_iter; + gboolean last_is_digit = FALSE; + + g_return_val_if_fail(equation != NULL, NULL); text = math_equation_get_display(equation); + eq_text = g_string_sized_new(strlen(text)); + + if (equation->priv->ans_start) + get_ans_offsets(equation, &ans_start, &ans_end); - /* No ans to substitute */ - if(!equation->priv->ans_start) - return text; + for (read_iter = text, offset = 0; *read_iter != '\0'; read_iter = g_utf8_next_char(read_iter), offset++) { + gunichar c; + gboolean is_digit, next_is_digit; - get_ans_offsets(equation, &ans_start, &ans_end); - t = g_strdup_printf("%.*sans%s", (int)(g_utf8_offset_to_pointer(text, ans_start) - text), text, g_utf8_offset_to_pointer(text, ans_end)); + c = g_utf8_get_char(read_iter); + is_digit = g_unichar_isdigit(c); + next_is_digit = g_unichar_isdigit(g_utf8_get_char(g_utf8_next_char(read_iter))); + + /* Replace ans text with variable */ + if (offset == ans_start) { + g_string_append(eq_text, "ans"); + read_iter = g_utf8_offset_to_pointer(read_iter, ans_end - ans_start - 1); + offset += ans_end - ans_start - 1; + is_digit = FALSE; + continue; + } + + /* Ignore thousands separators */ + if (c == mp_serializer_get_thousands_separator(equation->priv->serializer) && last_is_digit && next_is_digit) + ; + /* Substitute radix character */ + else if (c == mp_serializer_get_radix(equation->priv->serializer) && (last_is_digit || next_is_digit)) + g_string_append_unichar(eq_text, '.'); + else + g_string_append_unichar(eq_text, c); + + last_is_digit = is_digit; + } g_free(text); - return t; + + text = eq_text->str; + g_string_free(eq_text, FALSE); + + return text; } @@ -803,17 +869,35 @@ math_equation_get_number(MathEquation *equation, MPNumber *z) gchar *text; gboolean result; - text = math_equation_get_display(equation); - result = !mp_set_from_string(text, equation->priv->base, z); - g_free (text); + g_return_val_if_fail(equation != NULL, FALSE); + g_return_val_if_fail(z != NULL, FALSE); - return result; + if (math_equation_is_result(equation)) { + mp_set_from_mp(math_equation_get_answer(equation), z); + return TRUE; + } + else { + text = math_equation_get_equation(equation); + result = !mp_serializer_from_string(equation->priv->serializer, text, z); + g_free(text); + return result; + } +} + + +MpSerializer * +math_equation_get_serializer(MathEquation *equation) +{ + g_return_val_if_fail(equation != NULL, NULL); + return equation->priv->serializer; } void math_equation_set_number_mode(MathEquation *equation, NumberMode mode) { + g_return_if_fail(equation != NULL); + if (equation->priv->number_mode == mode) return; @@ -827,13 +911,23 @@ math_equation_set_number_mode(MathEquation *equation, NumberMode mode) NumberMode math_equation_get_number_mode(MathEquation *equation) { + g_return_val_if_fail(equation != NULL, NORMAL); return equation->priv->number_mode; } +gboolean +math_equation_in_solve(MathEquation *equation) +{ + g_return_val_if_fail(equation != NULL, FALSE); + return equation->priv->in_solve; +} + + const MPNumber * math_equation_get_answer(MathEquation *equation) { + g_return_val_if_fail(equation != NULL, FALSE); return &equation->priv->state.ans; } @@ -842,25 +936,31 @@ void math_equation_store(MathEquation *equation, const gchar *name) { MPNumber t; + + g_return_if_fail(equation != NULL); + g_return_if_fail(name != NULL); if (!math_equation_get_number(equation, &t)) math_equation_set_status(equation, _("No sane value to store")); else - math_variables_set_value(equation->priv->variables, name, &t); + math_variables_set(equation->priv->variables, name, &t); } void math_equation_recall(MathEquation *equation, const gchar *name) { + g_return_if_fail(equation != NULL); + g_return_if_fail(name != NULL); math_equation_insert(equation, name); } void math_equation_set(MathEquation *equation, const gchar *text) - { + g_return_if_fail(equation != NULL); + g_return_if_fail(text != NULL); gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), text, -1); clear_ans(equation, FALSE); } @@ -869,11 +969,14 @@ math_equation_set(MathEquation *equation, const gchar *text) void math_equation_set_number(MathEquation *equation, const MPNumber *x) { - char text[MAX_DIGITS]; + char *text; GtkTextIter start, end; + g_return_if_fail(equation != NULL); + g_return_if_fail(x != NULL); + /* Show the number in the user chosen format */ - display_make_number(equation, text, MAX_DIGITS, x); + text = mp_serializer_to_string(equation->priv->serializer, x); gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), text, -1); mp_set_from_mp(x, &equation->priv->state.ans); @@ -883,12 +986,16 @@ math_equation_set_number(MathEquation *equation, const MPNumber *x) equation->priv->ans_start = gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation), NULL, &start, FALSE); equation->priv->ans_end = gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation), NULL, &end, TRUE); gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(equation), equation->priv->ans_tag, &start, &end); + g_free(text); } void math_equation_insert(MathEquation *equation, const gchar *text) { + g_return_if_fail(equation != NULL); + g_return_if_fail(text != NULL); + /* Replace ** with ^ (not on all keyboards) */ if (!gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(equation)) && strcmp(text, "×") == 0 && equation->priv->state.entered_multiply) { @@ -900,10 +1007,6 @@ math_equation_insert(MathEquation *equation, const gchar *text) return; } - /* Start new equation when entering digits after existing result */ - if(math_equation_is_result(equation) && g_unichar_isdigit(g_utf8_get_char(text))) - gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), "", -1); - /* Can't enter superscript minus after entering digits */ if (strstr("⁰¹²³⁴⁵⁶⁷⁸⁹", text) != NULL || strcmp("⁻", text) == 0) equation->priv->can_super_minus = FALSE; @@ -912,8 +1015,6 @@ math_equation_insert(MathEquation *equation, const gchar *text) if (strstr("⁻⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉", text) == NULL) math_equation_set_number_mode(equation, NORMAL); - // FIXME: Add thousands separators - gtk_text_buffer_delete_selection(GTK_TEXT_BUFFER(equation), FALSE, FALSE); gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(equation), text, -1); } @@ -925,8 +1026,16 @@ math_equation_insert_digit(MathEquation *equation, guint digit) static const char *subscript_digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉", NULL}; static const char *superscript_digits[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹", NULL}; - if (equation->priv->number_mode == NORMAL || digit >= 10) - math_equation_insert(equation, math_equation_get_digit_text(equation, digit)); + g_return_if_fail(equation != NULL); + g_return_if_fail(digit < 16); + + if (equation->priv->number_mode == NORMAL || digit >= 10) { + gchar buffer[7]; + gint len; + len = g_unichar_to_utf8(math_equation_get_digit_text(equation, digit), buffer); + buffer[len] = '\0'; + math_equation_insert(equation, buffer); + } else if (equation->priv->number_mode == SUPERSCRIPT) math_equation_insert(equation, superscript_digits[digit]); else if (equation->priv->number_mode == SUBSCRIPT) @@ -937,22 +1046,35 @@ math_equation_insert_digit(MathEquation *equation, guint digit) void math_equation_insert_numeric_point(MathEquation *equation) { - math_equation_insert(equation, math_equation_get_numeric_point_text(equation)); + gchar buffer[7]; + gint len; + + g_return_if_fail(equation != NULL); + + len = g_unichar_to_utf8(mp_serializer_get_radix(equation->priv->serializer), buffer); + buffer[len] = '\0'; + math_equation_insert(equation, buffer); } void math_equation_insert_number(MathEquation *equation, const MPNumber *x) { - char text[MAX_DIGITS]; - display_make_number(equation, text, MAX_DIGITS, x); + char *text; + + g_return_if_fail(equation != NULL); + g_return_if_fail(x != NULL); + + text = mp_serializer_to_string(equation->priv->serializer, x); math_equation_insert(equation, text); + g_free(text); } void math_equation_insert_exponent(MathEquation *equation) { + g_return_if_fail(equation != NULL); math_equation_insert(equation, "×10"); math_equation_set_number_mode(equation, SUPERSCRIPT); } @@ -961,6 +1083,7 @@ math_equation_insert_exponent(MathEquation *equation) void math_equation_insert_subtract(MathEquation *equation) { + g_return_if_fail(equation != NULL); if (equation->priv->number_mode == SUPERSCRIPT && equation->priv->can_super_minus) { math_equation_insert(equation, "⁻"); equation->priv->can_super_minus = FALSE; @@ -982,14 +1105,14 @@ variable_is_defined(const char *name, void *data) for (c = lower_name; *c; c++) *c = tolower(*c); - if (strcmp(lower_name, "rand") == 0 || + if (strcmp(lower_name, "rand") == 0 || strcmp(lower_name, "ans") == 0) { - g_free (lower_name); + g_free(lower_name); return 1; } - g_free (lower_name); + g_free(lower_name); - return math_variables_get_value(equation->priv->variables, name) != NULL; + return math_variables_get(equation->priv->variables, name) != NULL; } @@ -1010,7 +1133,7 @@ get_variable(const char *name, MPNumber *z, void *data) else if (strcmp(lower_name, "ans") == 0) mp_set_from_mp(&equation->priv->state.ans, z); else { - t = math_variables_get_value(equation->priv->variables, name); + t = math_variables_get(equation->priv->variables, name); if (t) mp_set_from_mp(t, z); else @@ -1028,14 +1151,14 @@ set_variable(const char *name, const MPNumber *x, void *data) { MathEquation *equation = data; /* FIXME: Don't allow writing to built-in variables, e.g. ans, rand, sin, ... */ - math_variables_set_value(equation->priv->variables, name, x); + math_variables_set(equation->priv->variables, name, x); } static int convert(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data) { - return currency_convert(x, x_units, z_units, z); + return unit_manager_convert_by_symbol(unit_manager_get_default(), x, x_units, z_units, z); } @@ -1045,7 +1168,7 @@ parse(MathEquation *equation, const char *text, MPNumber *z, char **error_token) MPEquationOptions options; memset(&options, 0, sizeof(options)); - options.base = equation->priv->base; + options.base = mp_serializer_get_base(equation->priv->serializer); options.wordlen = equation->priv->word_size; options.angle_units = equation->priv->angle_units; options.variable_is_defined = variable_is_defined; @@ -1058,25 +1181,20 @@ parse(MathEquation *equation, const char *text, MPNumber *z, char **error_token) } -void -math_equation_solve(MathEquation *equation) +/* + * Executed in separate thread. It is thus not a good idea to write to anything + * in MathEquation but the async queue from here. + */ +static gpointer +math_equation_solve_real(gpointer data) { - MPNumber z; - gint result, n_brackets = 0; - gchar *c, *text, *error_token = NULL, *message = NULL; - GString *equation_text; - - if (math_equation_is_empty(equation)) - return; - - /* If showing a result return to the equation that caused it */ - // FIXME: Result may not be here due to solve (i.e. the user may have entered "ans") - if (math_equation_is_result(equation)) { - math_equation_undo(equation); - return; - } + MathEquation *equation = MATH_EQUATION(data); + SolveData *solvedata = g_slice_new0(SolveData); - math_equation_set_number_mode(equation, NORMAL); + gint n_brackets = 0, result; + gchar *c, *text, *error_token; + GString *equation_text; + MPNumber z; text = math_equation_get_equation(equation); equation_text = g_string_new(text); @@ -1099,81 +1217,183 @@ math_equation_solve(MathEquation *equation) switch (result) { case PARSER_ERR_NONE: - math_equation_set_number(equation, &z); + solvedata->number_result = g_slice_new(MPNumber); + mp_set_from_mp(&z, solvedata->number_result); break; case PARSER_ERR_OVERFLOW: - message = g_strdup(/* Error displayed to user when they perform a bitwise operation on numbers greater than the current word */ + solvedata->error = g_strdup(/* Error displayed to user when they perform a bitwise operation on numbers greater than the current word */ _("Overflow. Try a bigger word size")); break; case PARSER_ERR_UNKNOWN_VARIABLE: - message = g_strdup_printf(/* Error displayed to user when they an unknown variable is entered */ + solvedata->error = g_strdup_printf(/* Error displayed to user when they an unknown variable is entered */ _("Unknown variable '%s'"), error_token); break; case PARSER_ERR_UNKNOWN_FUNCTION: - message = g_strdup_printf(/* Error displayed to user when an unknown function is entered */ + solvedata->error = g_strdup_printf(/* Error displayed to user when an unknown function is entered */ _("Function '%s' is not defined"), error_token); break; case PARSER_ERR_UNKNOWN_CONVERSION: - message = g_strdup(/* Error displayed to user when an conversion with unknown units is attempted */ + solvedata->error = g_strdup(/* Error displayed to user when an conversion with unknown units is attempted */ _("Unknown conversion")); break; case PARSER_ERR_MP: - message = g_strdup(mp_get_error()); + if (mp_get_error()) + solvedata->error = g_strdup(mp_get_error()); + else if (error_token) + solvedata->error = g_strdup_printf(/* Uncategorized error. Show error token to user*/ + _("Malformed expression at token '%s'"), error_token); + else + solvedata->error = g_strdup (/* Unknown error. */ + _("Malformed expression")); break; default: - message = g_strdup(/* Error displayed to user when they enter an invalid calculation */ + solvedata->error = g_strdup(/* Error displayed to user when they enter an invalid calculation */ _("Malformed expression")); break; } + g_async_queue_push(equation->priv->queue, solvedata); + + return NULL; +} + - if (error_token) - free(error_token); +static gboolean +math_equation_show_in_progress(gpointer data) +{ + MathEquation *equation = MATH_EQUATION(data); + if (equation->priv->in_solve) + math_equation_set_status(equation, _("Calculating")); + return false; +} - if (message) { - math_equation_set_status(equation, message); - g_free(message); + +static gboolean +math_equation_look_for_answer(gpointer data) +{ + MathEquation *equation = MATH_EQUATION(data); + SolveData *result = g_async_queue_try_pop(equation->priv->queue); + + if (result == NULL) + return true; + + equation->priv->in_solve = false; + + if (!result->error) + math_equation_set_status(equation, ""); + + if (result->error != NULL) { + math_equation_set_status(equation, result->error); + g_free(result->error); + } + else if (result->number_result != NULL) { + math_equation_set_number(equation, result->number_result); + g_slice_free(MPNumber, result->number_result); } + else if (result->text_result != NULL) { + math_equation_set(equation, result->text_result); + g_free(result->text_result); + } + g_slice_free(SolveData, result); + + return false; } void -math_equation_factorize(MathEquation *equation) +math_equation_solve(MathEquation *equation) { - MPNumber x; - GList *factors, *factor; - GString *text; + g_return_if_fail(equation != NULL); - if (!math_equation_get_number(equation, &x) || !mp_is_integer(&x)) { - /* Error displayed when trying to factorize a non-integer value */ - math_equation_set_status(equation, _("Need an integer to factorize")); + // FIXME: should replace calculation or give error message + if (equation->priv->in_solve) + return; + + if (math_equation_is_empty(equation)) + return; + + /* If showing a result return to the equation that caused it */ + // FIXME: Result may not be here due to solve (i.e. the user may have entered "ans") + if (math_equation_is_result(equation)) { + math_equation_undo(equation); return; } + equation->priv->in_solve = true; + + math_equation_set_number_mode(equation, NORMAL); + + g_thread_new("", math_equation_solve_real, equation); + + g_timeout_add(50, math_equation_look_for_answer, equation); + g_timeout_add(100, math_equation_show_in_progress, equation); +} + + +static gpointer +math_equation_factorize_real(gpointer data) +{ + GString *text; + GList *factors, *factor; + MPNumber x; + MathEquation *equation = MATH_EQUATION(data); + SolveData *result = g_slice_new0(SolveData); + + math_equation_get_number(equation, &x); factors = mp_factorize(&x); text = g_string_new(""); for (factor = factors; factor; factor = factor->next) { - gchar temp[MAX_DIGITS]; + gchar *temp; MPNumber *n; n = factor->data; - display_make_number(equation, temp, MAX_DIGITS, n); + temp = mp_serializer_to_string(equation->priv->serializer, n); g_string_append(text, temp); if (factor->next) g_string_append(text, "×"); g_slice_free(MPNumber, n); + g_free(temp); } g_list_free(factors); - math_equation_set(equation, text->str); + result->text_result = g_strndup(text->str, text->len); + g_async_queue_push(equation->priv->queue, result); g_string_free(text, TRUE); + + return NULL; +} + + +void +math_equation_factorize(MathEquation *equation) +{ + MPNumber x; + + g_return_if_fail(equation != NULL); + + // FIXME: should replace calculation or give error message + if (equation->priv->in_solve) + return; + + if (!math_equation_get_number(equation, &x) || !mp_is_integer(&x)) { + /* Error displayed when trying to factorize a non-integer value */ + math_equation_set_status(equation, _("Need an integer to factorize")); + return; + } + + equation->priv->in_solve = true; + + g_thread_new("", math_equation_factorize_real, equation); + + g_timeout_add(50, math_equation_look_for_answer, equation); + g_timeout_add(100, math_equation_show_in_progress, equation); } @@ -1183,6 +1403,8 @@ math_equation_delete(MathEquation *equation) gint cursor; GtkTextIter start, end; + g_return_if_fail(equation != NULL); + g_object_get(G_OBJECT(equation), "cursor-position", &cursor, NULL); if (cursor >= gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation))) return; @@ -1196,6 +1418,8 @@ math_equation_delete(MathEquation *equation) void math_equation_backspace(MathEquation *equation) { + g_return_if_fail(equation != NULL); + /* Can't delete empty display */ if (math_equation_is_empty(equation)) return; @@ -1213,6 +1437,8 @@ math_equation_backspace(MathEquation *equation) void math_equation_clear(MathEquation *equation) { + g_return_if_fail(equation != NULL); + math_equation_set_number_mode(equation, NORMAL); gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), "", -1); clear_ans(equation, FALSE); @@ -1224,6 +1450,8 @@ math_equation_shift(MathEquation *equation, gint count) { MPNumber z; + g_return_if_fail(equation != NULL); + if (!math_equation_get_number(equation, &z)) { math_equation_set_status(equation, /* This message is displayed in the status bar when a bit @@ -1244,6 +1472,8 @@ math_equation_toggle_bit(MathEquation *equation, guint bit) guint64 bits; gboolean result; + g_return_if_fail(equation != NULL); + result = math_equation_get_number(equation, &x); if (result) { MPNumber max; @@ -1270,25 +1500,6 @@ math_equation_toggle_bit(MathEquation *equation, guint bit) } -/* Convert MP number to character string. */ -//FIXME: What to do with this? -void -display_make_number(MathEquation *equation, char *target, int target_len, const MPNumber *x) -{ - switch(equation->priv->format) { - case FIX: - mp_cast_to_string(x, equation->priv->base, equation->priv->base, equation->priv->accuracy, !equation->priv->show_zeroes, target, target_len); - break; - case SCI: - mp_cast_to_exponential_string(x, equation->priv->base, equation->priv->base, equation->priv->accuracy, !equation->priv->show_zeroes, false, target, target_len); - break; - case ENG: - mp_cast_to_exponential_string(x, equation->priv->base, equation->priv->base, equation->priv->accuracy, !equation->priv->show_zeroes, true, target, target_len); - break; - } -} - - static void math_equation_set_property(GObject *object, guint prop_id, @@ -1297,7 +1508,7 @@ math_equation_set_property(GObject *object, { MathEquation *self; - self = MATH_EQUATION (object); + self = MATH_EQUATION(object); switch (prop_id) { case PROP_STATUS: @@ -1336,8 +1547,14 @@ math_equation_set_property(GObject *object, case PROP_TARGET_CURRENCY: math_equation_set_target_currency(self, g_value_get_string(value)); break; + case PROP_SOURCE_UNITS: + math_equation_set_source_units(self, g_value_get_string(value)); + break; + case PROP_TARGET_UNITS: + math_equation_set_target_units(self, g_value_get_string(value)); + break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } @@ -1352,14 +1569,14 @@ math_equation_get_property(GObject *object, MathEquation *self; gchar *text; - self = MATH_EQUATION (object); + self = MATH_EQUATION(object); switch (prop_id) { case PROP_STATUS: g_value_set_string(value, self->priv->state.status); break; case PROP_DISPLAY: - text = math_equation_get_display(self); + text = math_equation_get_display(self); g_value_set_string(value, text); g_free(text); break; @@ -1372,16 +1589,16 @@ math_equation_get_property(GObject *object, g_value_set_enum(value, self->priv->number_mode); break; case PROP_ACCURACY: - g_value_set_int(value, self->priv->accuracy); + g_value_set_int(value, mp_serializer_get_trailing_digits(self->priv->serializer)); break; case PROP_SHOW_THOUSANDS_SEPARATORS: - g_value_set_boolean(value, self->priv->show_tsep); + g_value_set_boolean(value, mp_serializer_get_show_thousands_separators(self->priv->serializer)); break; case PROP_SHOW_TRAILING_ZEROES: - g_value_set_boolean(value, self->priv->show_zeroes); + g_value_set_boolean(value, mp_serializer_get_show_trailing_zeroes(self->priv->serializer)); break; case PROP_NUMBER_FORMAT: - g_value_set_enum(value, self->priv->format); + g_value_set_enum(value, mp_serializer_get_number_format(self->priv->serializer)); break; case PROP_BASE: g_value_set_int(value, math_equation_get_base(self)); @@ -1398,15 +1615,36 @@ math_equation_get_property(GObject *object, case PROP_TARGET_CURRENCY: g_value_set_string(value, self->priv->target_currency); break; + case PROP_SOURCE_UNITS: + g_value_set_string(value, self->priv->source_units); + break; + case PROP_TARGET_UNITS: + g_value_set_string(value, self->priv->target_units); + break; + case PROP_SERIALIZER: + g_value_set_object(value, self->priv->serializer); + break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void -math_equation_class_init (MathEquationClass *klass) +math_equation_constructed(GObject *object) +{ + GtkTextBuffer *parent_class; + parent_class = g_type_class_peek_parent(MATH_EQUATION_GET_CLASS(object)); + if (G_OBJECT_CLASS(parent_class)->constructed) + G_OBJECT_CLASS(parent_class)->constructed(object); + + MATH_EQUATION(object)->priv->ans_tag = gtk_text_buffer_create_tag(GTK_TEXT_BUFFER(object), NULL, "weight", PANGO_WEIGHT_BOLD, NULL); +} + + +static void +math_equation_class_init(MathEquationClass *klass) { static GEnumValue number_mode_values[] = { @@ -1415,13 +1653,6 @@ math_equation_class_init (MathEquationClass *klass) {SUBSCRIPT, "subscript", "subscript"}, {0, NULL, NULL} }; - static GEnumValue number_format_values[] = - { - {FIX, "fixed-point", "fixed-point"}, - {SCI, "scientific", "scientific"}, - {ENG, "engineering", "engineering"}, - {0, NULL, NULL} - }; static GEnumValue angle_unit_values[] = { {MP_RADIANS, "radians", "radians"}, @@ -1429,15 +1660,16 @@ math_equation_class_init (MathEquationClass *klass) {MP_GRADIANS, "gradians", "gradians"}, {0, NULL, NULL} }; - GObjectClass *object_class = G_OBJECT_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->get_property = math_equation_get_property; object_class->set_property = math_equation_set_property; + object_class->constructed = math_equation_constructed; - g_type_class_add_private (klass, sizeof (MathEquationPrivate)); - + g_type_class_add_private(klass, sizeof(MathEquationPrivate)); + number_mode_type = g_enum_register_static("NumberMode", number_mode_values); - number_format_type = g_enum_register_static("DisplayFormat", number_format_values); + number_format_type = math_mp_display_format_get_type(); angle_unit_type = g_enum_register_static("AngleUnit", angle_unit_values); g_object_class_install_property(object_class, @@ -1496,14 +1728,14 @@ math_equation_class_init (MathEquationClass *klass) "number-format", "Display format", number_format_type, - FIX, + MP_DISPLAY_FORMAT_FIXED, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_BASE, g_param_spec_int("base", "base", "Default number base (derived from number-format)", - 2, 16, 10, + 2, 16, 10, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_WORD_SIZE, @@ -1534,30 +1766,54 @@ math_equation_class_init (MathEquationClass *klass) "target Currency", "", G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_SOURCE_UNITS, + g_param_spec_string("source-units", + "source-units", + "Source Units", + "", + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_TARGET_UNITS, + g_param_spec_string("target-units", + "target-units", + "target Units", + "", + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_SERIALIZER, + g_param_spec_object("serializer", + "serializer", + "Serializer", + MP_TYPE_SERIALIZER, + G_PARAM_READABLE)); } static void -pre_insert_text_cb (MathEquation *equation, - GtkTextIter *location, - gchar *text, - gint len, - gpointer user_data) +pre_insert_text_cb(MathEquation *equation, + GtkTextIter *location, + gchar *text, + gint len, + gpointer user_data) { gunichar c; - + gint cursor; + if (equation->priv->in_reformat) return; - + /* If following a delete then have already pushed undo stack (GtkTextBuffer doesn't indicate replace operations so we have to infer them) */ if (!equation->priv->in_delete) math_equation_push_undo_stack(equation); /* Clear result on next digit entered if cursor at end of line */ - // FIXME Cursor c = g_utf8_get_char(text); - if (g_unichar_isdigit(c) && math_equation_is_result(equation)) { + g_object_get(G_OBJECT(equation), "cursor-position", &cursor, NULL); + if ((g_unichar_isdigit(c) || c == mp_serializer_get_radix(equation->priv->serializer)) && + math_equation_is_result(equation) && + cursor >= gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation))) { gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), "", -1); clear_ans(equation, FALSE); gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(equation), location); @@ -1569,7 +1825,7 @@ pre_insert_text_cb (MathEquation *equation, offset = gtk_text_iter_get_offset(location); get_ans_offsets(equation, &ans_start, &ans_end); - + /* Inserted inside ans */ if (offset > ans_start && offset < ans_end) clear_ans(equation, TRUE); @@ -1580,17 +1836,17 @@ pre_insert_text_cb (MathEquation *equation, static gboolean on_delete(MathEquation *equation) { - equation->priv->in_delete = FALSE; - return FALSE; + equation->priv->in_delete = FALSE; + return FALSE; } static void -pre_delete_range_cb (MathEquation *equation, - GtkTextIter *start, - GtkTextIter *end, - gpointer user_data) -{ +pre_delete_range_cb(MathEquation *equation, + GtkTextIter *start, + GtkTextIter *end, + gpointer user_data) +{ if (equation->priv->in_reformat) return; @@ -1606,7 +1862,7 @@ pre_delete_range_cb (MathEquation *equation, start_offset = gtk_text_iter_get_offset(start); end_offset = gtk_text_iter_get_offset(end); get_ans_offsets(equation, &ans_start, &ans_end); - + /* Deleted part of ans */ if (start_offset < ans_end && end_offset > ans_start) clear_ans(equation, TRUE); @@ -1615,29 +1871,38 @@ pre_delete_range_cb (MathEquation *equation, static void -insert_text_cb (MathEquation *equation, - GtkTextIter *location, - gchar *text, - gint len, - gpointer user_data) +insert_text_cb(MathEquation *equation, + GtkTextIter *location, + gchar *text, + gint len, + gpointer user_data) { if (equation->priv->in_reformat) return; equation->priv->state.entered_multiply = strcmp(text, "×") == 0; + + /* Update thousands separators */ + reformat_separators(equation); + g_object_notify(G_OBJECT(equation), "display"); } static void -delete_range_cb (MathEquation *equation, - GtkTextIter *start, - GtkTextIter *end, - gpointer user_data) +delete_range_cb(MathEquation *equation, + GtkTextIter *start, + GtkTextIter *end, + gpointer user_data) { if (equation->priv->in_reformat) return; + equation->priv->state.entered_multiply = FALSE; + + /* Update thousands separators */ + reformat_separators(equation); + // FIXME: A replace will emit this both for delete-range and insert-text, can it be avoided? g_object_notify(G_OBJECT(equation), "display"); } @@ -1649,19 +1914,16 @@ math_equation_init(MathEquation *equation) /* Digits localized for the given language */ const char *digit_values = _("0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F"); const char *default_digits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; - gchar *radix, *tsep; gchar **digits; - gboolean use_default_digits = FALSE; + /* Default to using untranslated digits, this is because it doesn't make sense in most languages and we need to make this optional. + * See https://bugzilla.gnome.org/show_bug.cgi?id=632661 */ + gboolean use_default_digits = TRUE; int i; - equation->priv = G_TYPE_INSTANCE_GET_PRIVATE (equation, math_equation_get_type(), MathEquationPrivate); - - // FIXME: Causes error - // (process:18573): Gtk-CRITICAL **: set_table: assertion buffer->tag_table == NULL' failed - equation->priv->ans_tag = gtk_text_buffer_create_tag(GTK_TEXT_BUFFER(equation), NULL, "weight", PANGO_WEIGHT_BOLD, NULL); + equation->priv = G_TYPE_INSTANCE_GET_PRIVATE(equation, math_equation_get_type(), MathEquationPrivate); g_signal_connect(equation, "insert-text", G_CALLBACK(pre_insert_text_cb), equation); - g_signal_connect(equation, "delete-range", G_CALLBACK(pre_delete_range_cb), equation); + g_signal_connect(equation, "delete-range", G_CALLBACK(pre_delete_range_cb), equation); g_signal_connect_after(equation, "insert-text", G_CALLBACK(insert_text_cb), equation); g_signal_connect_after(equation, "delete-range", G_CALLBACK(delete_range_cb), equation); @@ -1669,35 +1931,25 @@ math_equation_init(MathEquation *equation) for (i = 0; i < 16; i++) { if (use_default_digits || digits[i] == NULL) { use_default_digits = TRUE; - equation->priv->digits[i] = strdup(default_digits[i]); + equation->priv->digits[i] = g_utf8_get_char(default_digits[i]); } else - equation->priv->digits[i] = strdup(digits[i]); + equation->priv->digits[i] = g_utf8_get_char(digits[i]); } g_strfreev(digits); - setlocale(LC_NUMERIC, ""); - - radix = nl_langinfo(RADIXCHAR); - equation->priv->radix = radix ? g_locale_to_utf8(radix, -1, NULL, NULL, NULL) : g_strdup("."); - tsep = nl_langinfo(THOUSEP); - equation->priv->tsep = tsep ? g_locale_to_utf8(tsep, -1, NULL, NULL, NULL) : g_strdup(","); - - equation->priv->tsep_count = 3; - equation->priv->variables = math_variables_new(); equation->priv->state.status = g_strdup(""); - equation->priv->show_zeroes = FALSE; - equation->priv->show_tsep = FALSE; - equation->priv->format = FIX; - equation->priv->accuracy = 9; equation->priv->word_size = 32; equation->priv->angle_units = MP_DEGREES; // FIXME: Pick based on locale - equation->priv->source_currency = g_strdup(currency_names[0].short_name); - equation->priv->target_currency = g_strdup(currency_names[0].short_name); - equation->priv->base = 10; + equation->priv->source_currency = g_strdup(""); + equation->priv->target_currency = g_strdup(""); + equation->priv->source_units = g_strdup(""); + equation->priv->target_units = g_strdup(""); + equation->priv->serializer = mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC, 10, 9); + equation->priv->queue = g_async_queue_new(); mp_set_from_integer(0, &equation->priv->state.ans); } |