diff options
Diffstat (limited to 'src/math-equation.c')
-rw-r--r-- | src/math-equation.c | 1701 |
1 files changed, 1701 insertions, 0 deletions
diff --git a/src/math-equation.c b/src/math-equation.c new file mode 100644 index 0000000..af68cd1 --- /dev/null +++ b/src/math-equation.c @@ -0,0 +1,1701 @@ +/* Copyright (c) 1987-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2008-2009 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <errno.h> +#include <glib.h> +#include <langinfo.h> +#include <locale.h> + +#include "math-equation.h" + +#include "mp.h" +#include "mp-equation.h" +#include "currency.h" + + +enum { + PROP_0, + PROP_STATUS, + PROP_DISPLAY, + PROP_EQUATION, + PROP_NUMBER_MODE, + PROP_ACCURACY, + PROP_SHOW_THOUSANDS_SEPARATORS, + PROP_SHOW_TRAILING_ZEROES, + PROP_NUMBER_FORMAT, + PROP_BASE, + PROP_WORD_SIZE, + PROP_ANGLE_UNITS, + PROP_SOURCE_CURRENCY, + PROP_TARGET_CURRENCY +}; + +static GType number_mode_type, number_format_type, angle_unit_type; + +#define MAX_DIGITS 512 + +/* Expression mode state */ +typedef struct { + MPNumber ans; /* Previously calculated answer */ + gchar *expression; /* Expression entered by user */ + gint ans_start, ans_end; /* Start and end characters for ans variable in expression */ + gint cursor; /* ??? */ + NumberMode number_mode; /* ??? */ + gboolean can_super_minus; /* TRUE if entering minus can generate a superscript minus */ + gboolean entered_multiply; /* Last insert was a multiply character */ + gchar *status; /* Equation status */ +} MathEquationState; + +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 */ + 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. */ + + GtkTextMark *ans_start, *ans_end; + + MathEquationState state; /* Equation state */ + GList *undo_stack; /* History of expression mode states */ + GList *redo_stack; + gboolean in_undo_operation; + + gboolean in_reformat; + + gboolean in_delete; + + MathVariables *variables; +}; + +G_DEFINE_TYPE (MathEquation, math_equation, GTK_TYPE_TEXT_BUFFER); + + +MathEquation * +math_equation_new() +{ + return g_object_new (math_equation_get_type(), NULL); +} + + +MathVariables * +math_equation_get_variables(MathEquation *equation) +{ + return equation->priv->variables; +} + + +static void +get_ans_offsets(MathEquation *equation, gint *start, gint *end) +{ + GtkTextIter iter; + + if (!equation->priv->ans_start) { + *start = *end = -1; + return; + } + + gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, equation->priv->ans_start); + *start = gtk_text_iter_get_offset(&iter); + gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, equation->priv->ans_end); + *end = gtk_text_iter_get_offset(&iter); +} + + +static void +reformat_ans(MathEquation *equation) +{ + if (!equation->priv->ans_start) + return; + + gchar *orig_ans_text; + gchar ans_text[MAX_DIGITS]; + 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); + if (strcmp(orig_ans_text, ans_text) != 0) { + gint start; + + equation->priv->in_undo_operation = TRUE; + equation->priv->in_reformat = TRUE; + + start = gtk_text_iter_get_offset(&ans_start); + gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation), &ans_start, &ans_end); + gtk_text_buffer_insert_with_tags(GTK_TEXT_BUFFER(equation), &ans_end, ans_text, -1, equation->priv->ans_tag, NULL); + + /* There seems to be a bug in the marks as they alternate being the correct and incorrect ways. Reset them */ + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &ans_start, start); + gtk_text_buffer_move_mark(GTK_TEXT_BUFFER(equation), equation->priv->ans_start, &ans_start); + gtk_text_buffer_move_mark(GTK_TEXT_BUFFER(equation), equation->priv->ans_end, &ans_end); + + equation->priv->in_reformat = FALSE; + equation->priv->in_undo_operation = FALSE; + } + 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); +} + + +/* 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) +{ + 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; + + if (equation->priv->base == old_base) + return; + + sub_zero = g_utf8_get_char("₀"); + sub_nine = g_utf8_get_char("₉"); + + read_iter = text = math_equation_get_display(equation); + get_ans_offsets(equation, &ans_start, &ans_end); + while (TRUE) { + gunichar c; + gint digit = -1, sub_digit = -1; + + /* 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); + + /* Don't mess with ans */ + if (offset >= ans_start && offset <= ans_end) { + digit = -1; + sub_digit = -1; + } + + 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; + } + + base = base * 10 + sub_digit; + } + 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) { + 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_delete(GTK_TEXT_BUFFER(equation), &start, &end); + offset_step -= offset - base_offset; + + equation->priv->in_reformat = FALSE; + equation->priv->in_undo_operation = FALSE; + } + + in_number = FALSE; + } + else if (digit >= 0) { + in_number = TRUE; + have_radix = FALSE; + base = -1; + max_digit = digit; + } + + 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 +} + + +static void +reformat_display(MathEquation *equation, gint old_base) +{ + /* 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); +} + + +static MathEquationState * +get_current_state(MathEquation *equation) +{ + MathEquationState *state; + gint ans_start = -1, ans_end = -1; + + state = g_malloc0(sizeof(MathEquationState)); + + if (equation->priv->ans_start) + { + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, equation->priv->ans_start); + ans_start = gtk_text_iter_get_offset(&iter); + gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, equation->priv->ans_end); + ans_end = gtk_text_iter_get_offset(&iter); + } + + mp_set_from_mp(&equation->priv->state.ans, &state->ans); + state->expression = math_equation_get_display(equation); + state->ans_start = ans_start; + state->ans_end = ans_end; + g_object_get(G_OBJECT(equation), "cursor-position", &state->cursor, NULL); + state->number_mode = equation->priv->number_mode; + 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; +} + + +static void +free_state(MathEquationState *state) +{ + g_free(state->expression); + g_free(state->status); + g_free(state); +} + + +static void +math_equation_push_undo_stack(MathEquation *equation) +{ + GList *link; + MathEquationState *state; + + if (equation->priv->in_undo_operation) + return; + + math_equation_set_status(equation, ""); + + /* Can't redo anymore */ + for (link = equation->priv->redo_stack; link; link = link->next) { + state = link->data; + free_state(state); + } + g_list_free(equation->priv->redo_stack); + equation->priv->redo_stack = NULL; + + state = get_current_state(equation); + equation->priv->undo_stack = g_list_prepend(equation->priv->undo_stack, state); +} + + +static void +clear_ans(MathEquation *equation, gboolean remove_tag) +{ + if (!equation->priv->ans_start) + return; + + if (remove_tag) { + GtkTextIter start, end; + + gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &start, equation->priv->ans_start); + gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &end, equation->priv->ans_end); + gtk_text_buffer_remove_tag(GTK_TEXT_BUFFER(equation), equation->priv->ans_tag, &start, &end); + } + + gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(equation), equation->priv->ans_start); + gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(equation), equation->priv->ans_end); + equation->priv->ans_start = NULL; + equation->priv->ans_end = NULL; +} + + +static void +apply_state(MathEquation *equation, MathEquationState *state) +{ + GtkTextIter cursor; + + /* Disable undo detection */ + equation->priv->in_undo_operation = TRUE; + + mp_set_from_mp(&state->ans, &equation->priv->state.ans); + + gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), state->expression, -1); + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &cursor, state->cursor); + gtk_text_buffer_place_cursor(GTK_TEXT_BUFFER(equation), &cursor); + clear_ans(equation, FALSE); + if (state->ans_start >= 0) { + GtkTextIter start, end; + + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &start, state->ans_start); + equation->priv->ans_start = gtk_text_buffer_create_mark(GTK_TEXT_BUFFER(equation), NULL, &start, FALSE); + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &end, state->ans_end); + 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); + } + + math_equation_set_number_mode(equation, state->number_mode); + equation->priv->can_super_minus = state->can_super_minus; + equation->priv->state.entered_multiply = state->entered_multiply; + math_equation_set_status(equation, state->status); + + equation->priv->in_undo_operation = FALSE; +} + + +void +math_equation_copy(MathEquation *equation) +{ + GtkTextIter start, end; + gchar *text; + + 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); +} + + +static void +on_paste(GtkClipboard *clipboard, const gchar *text, gpointer data) +{ + MathEquation *equation = data; + math_equation_insert (equation, text); +} + + +void +math_equation_paste(MathEquation *equation) +{ + gtk_clipboard_request_text(gtk_clipboard_get(GDK_NONE), on_paste, equation); +} + + +void +math_equation_undo(MathEquation *equation) +{ + GList *link; + MathEquationState *state; + + if (!equation->priv->undo_stack) { + math_equation_set_status(equation, + /* Error shown when trying to undo with no undo history */ + _("No undo history")); + return; + } + + link = equation->priv->undo_stack; + equation->priv->undo_stack = g_list_remove_link(equation->priv->undo_stack, link); + state = link->data; + g_list_free(link); + + equation->priv->redo_stack = g_list_prepend(equation->priv->redo_stack, get_current_state(equation)); + + apply_state(equation, state); + free_state(state); +} + + +void +math_equation_redo(MathEquation *equation) +{ + GList *link; + MathEquationState *state; + + if (!equation->priv->redo_stack) { + math_equation_set_status(equation, + /* Error shown when trying to redo with no redo history */ + _("No redo history")); + return; + } + + link = equation->priv->redo_stack; + equation->priv->redo_stack = g_list_remove_link(equation->priv->redo_stack, link); + state = link->data; + g_list_free(link); + + equation->priv->undo_stack = g_list_prepend(equation->priv->undo_stack, get_current_state(equation)); + + apply_state(equation, state); + free_state(state); +} + + +const gchar * +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; +} + + +const gchar *math_equation_get_thousands_separator_text(MathEquation *equation) +{ + return equation->priv->tsep; +} + + +void +math_equation_set_accuracy(MathEquation *equation, gint accuracy) +{ + if (equation->priv->accuracy == accuracy) + return; + equation->priv->accuracy = accuracy; + reformat_display(equation, equation->priv->base); + g_object_notify(G_OBJECT(equation), "accuracy"); +} + + +gint +math_equation_get_accuracy(MathEquation *equation) +{ + return equation->priv->accuracy; +} + + +void +math_equation_set_show_thousands_separators(MathEquation *equation, gboolean visible) +{ + if ((equation->priv->show_tsep && visible) || (!equation->priv->show_tsep && !visible)) + return; + equation->priv->show_tsep = visible; + reformat_display(equation, equation->priv->base); + g_object_notify(G_OBJECT(equation), "show-thousands-separators"); +} + + +gboolean +math_equation_get_show_thousands_separators(MathEquation *equation) +{ + return equation->priv->show_tsep; +} + + +void +math_equation_set_show_trailing_zeroes(MathEquation *equation, gboolean visible) +{ + if ((equation->priv->show_zeroes && visible) || (!equation->priv->show_zeroes && !visible)) + return; + equation->priv->show_zeroes = visible; + reformat_display(equation, equation->priv->base); + g_object_notify(G_OBJECT(equation), "show-trailing-zeroes"); +} + + +gboolean +math_equation_get_show_trailing_zeroes(MathEquation *equation) +{ + return equation->priv->show_zeroes; +} + + +void +math_equation_set_number_format(MathEquation *equation, DisplayFormat format) +{ + if (equation->priv->format == format) + return; + + equation->priv->format = format; + reformat_display(equation, equation->priv->base); + g_object_notify(G_OBJECT(equation), "number-format"); +} + + +DisplayFormat +math_equation_get_number_format(MathEquation *equation) +{ + return equation->priv->format; +} + + +void +math_equation_set_base(MathEquation *equation, gint base) +{ + gint old_base; + + if (equation->priv->base == base) + return; + + old_base = equation->priv->base; + equation->priv->base = base; + reformat_display(equation, old_base); + g_object_notify(G_OBJECT(equation), "base"); +} + + +gint +math_equation_get_base(MathEquation *equation) +{ + return equation->priv->base; +} + + +void +math_equation_set_word_size(MathEquation *equation, gint word_size) +{ + if (equation->priv->word_size == word_size) + return; + equation->priv->word_size = word_size; + g_object_notify(G_OBJECT(equation), "word-size"); +} + + +gint +math_equation_get_word_size(MathEquation *equation) +{ + return equation->priv->word_size; +} + + +void +math_equation_set_angle_units(MathEquation *equation, MPAngleUnit angle_units) +{ + if (equation->priv->angle_units == angle_units) + return; + equation->priv->angle_units = angle_units; + g_object_notify(G_OBJECT(equation), "angle-units"); +} + + +MPAngleUnit +math_equation_get_angle_units(MathEquation *equation) +{ + return equation->priv->angle_units; +} + + +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; + + if (strcmp(equation->priv->source_currency, currency) == 0) + return; + g_free(equation->priv->source_currency); + equation->priv->source_currency = g_strdup(currency); + g_object_notify(G_OBJECT(equation), "source-currency"); +} + +const gchar * +math_equation_get_source_currency(MathEquation *equation) +{ + return equation->priv->source_currency; +} + + +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; + + if (strcmp(equation->priv->target_currency, currency) == 0) + return; + g_free(equation->priv->target_currency); + equation->priv->target_currency = g_strdup(currency); + g_object_notify(G_OBJECT(equation), "target-currency"); +} + + +const gchar * +math_equation_get_target_currency(MathEquation *equation) +{ + return equation->priv->target_currency; +} + + +void +math_equation_set_status(MathEquation *equation, const gchar *status) +{ + 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"); +} + + +const gchar * +math_equation_get_status(MathEquation *equation) +{ + return equation->priv->state.status; +} + + +gboolean +math_equation_is_empty(MathEquation *equation) +{ + return gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation)) == 0; +} + + +gboolean +math_equation_is_result(MathEquation *equation) +{ + char *text; + gboolean result; + + text = math_equation_get_equation(equation); + result = strcmp(text, "ans") == 0; + g_free(text); + + return result; +} + + +gchar * +math_equation_get_display(MathEquation *equation) +{ + GtkTextIter start, end; + + gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation), &start, &end); + return gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation), &start, &end, FALSE); +} + + +gchar * +math_equation_get_equation(MathEquation *equation) +{ + char *text, *t; + gint ans_start, ans_end; + + text = math_equation_get_display(equation); + + /* No ans to substitute */ + if(!equation->priv->ans_start) + return text; + + 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)); + g_free(text); + return t; +} + + +gboolean +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); + + return result; +} + + +void +math_equation_set_number_mode(MathEquation *equation, NumberMode mode) +{ + if (equation->priv->number_mode == mode) + return; + + equation->priv->can_super_minus = mode == SUPERSCRIPT; + + equation->priv->number_mode = mode; + g_object_notify(G_OBJECT(equation), "number-mode"); +} + + +NumberMode +math_equation_get_number_mode(MathEquation *equation) +{ + return equation->priv->number_mode; +} + + +const MPNumber * +math_equation_get_answer(MathEquation *equation) +{ + return &equation->priv->state.ans; +} + + +void +math_equation_store(MathEquation *equation, const gchar *name) +{ + MPNumber t; + + 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); +} + + +void +math_equation_recall(MathEquation *equation, const gchar *name) +{ + math_equation_insert(equation, name); +} + + +void +math_equation_set(MathEquation *equation, const gchar *text) + +{ + gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), text, -1); + clear_ans(equation, FALSE); +} + + +void +math_equation_set_number(MathEquation *equation, const MPNumber *x) +{ + char text[MAX_DIGITS]; + GtkTextIter start, end; + + /* Show the number in the user chosen format */ + display_make_number(equation, text, MAX_DIGITS, x); + gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), text, -1); + mp_set_from_mp(x, &equation->priv->state.ans); + + /* Mark this text as the answer variable */ + gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(equation), &start, &end); + clear_ans(equation, FALSE); + 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); +} + + +void +math_equation_insert(MathEquation *equation, const gchar *text) +{ + /* 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) { + GtkTextIter iter; + + gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(equation))); + gtk_text_buffer_backspace(GTK_TEXT_BUFFER(equation), &iter, TRUE, TRUE); + gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(equation), "^", -1); + 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; + + /* Disable super/subscript mode when finished entering */ + 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); +} + + +void +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)); + else if (equation->priv->number_mode == SUPERSCRIPT) + math_equation_insert(equation, superscript_digits[digit]); + else if (equation->priv->number_mode == SUBSCRIPT) + math_equation_insert(equation, subscript_digits[digit]); +} + + +void +math_equation_insert_numeric_point(MathEquation *equation) +{ + math_equation_insert(equation, math_equation_get_numeric_point_text(equation)); +} + + +void +math_equation_insert_number(MathEquation *equation, const MPNumber *x) +{ + char text[MAX_DIGITS]; + display_make_number(equation, text, MAX_DIGITS, x); + math_equation_insert(equation, text); +} + + +void +math_equation_insert_exponent(MathEquation *equation) +{ + math_equation_insert(equation, "×10"); + math_equation_set_number_mode(equation, SUPERSCRIPT); +} + + +void +math_equation_insert_subtract(MathEquation *equation) +{ + if (equation->priv->number_mode == SUPERSCRIPT && equation->priv->can_super_minus) { + math_equation_insert(equation, "⁻"); + equation->priv->can_super_minus = FALSE; + } + else { + math_equation_insert(equation, "−"); + math_equation_set_number_mode(equation, NORMAL); + } +} + + +static int +variable_is_defined(const char *name, void *data) +{ + MathEquation *equation = data; + char *c, *lower_name; + + lower_name = strdup(name); + for (c = lower_name; *c; c++) + *c = tolower(*c); + + if (strcmp(lower_name, "rand") == 0 || + strcmp(lower_name, "ans") == 0) { + g_free (lower_name); + return 1; + } + g_free (lower_name); + + return math_variables_get_value(equation->priv->variables, name) != NULL; +} + + +static int +get_variable(const char *name, MPNumber *z, void *data) +{ + char *c, *lower_name; + int result = 1; + MathEquation *equation = data; + MPNumber *t; + + lower_name = strdup(name); + for (c = lower_name; *c; c++) + *c = tolower(*c); + + if (strcmp(lower_name, "rand") == 0) + mp_set_from_random(z); + 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); + if (t) + mp_set_from_mp(t, z); + else + result = 0; + } + + free(lower_name); + + return result; +} + + +static void +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); +} + + +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); +} + + +static int +parse(MathEquation *equation, const char *text, MPNumber *z, char **error_token) +{ + MPEquationOptions options; + + memset(&options, 0, sizeof(options)); + options.base = equation->priv->base; + options.wordlen = equation->priv->word_size; + options.angle_units = equation->priv->angle_units; + options.variable_is_defined = variable_is_defined; + options.get_variable = get_variable; + options.set_variable = set_variable; + options.convert = convert; + options.callback_data = equation; + + return mp_equation_parse(text, &options, z, error_token); +} + + +void +math_equation_solve(MathEquation *equation) +{ + 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; + } + + math_equation_set_number_mode(equation, NORMAL); + + text = math_equation_get_equation(equation); + equation_text = g_string_new(text); + g_free(text); + /* Count the number of brackets and automatically add missing closing brackets */ + for (c = equation_text->str; *c; c++) { + if (*c == '(') + n_brackets++; + else if (*c == ')') + n_brackets--; + } + while (n_brackets > 0) { + g_string_append_c(equation_text, ')'); + n_brackets--; + } + + + result = parse(equation, equation_text->str, &z, &error_token); + g_string_free(equation_text, TRUE); + + switch (result) { + case PARSER_ERR_NONE: + math_equation_set_number(equation, &z); + 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 */ + _("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 */ + _("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 */ + _("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 */ + _("Unknown conversion")); + break; + + case PARSER_ERR_MP: + message = g_strdup(mp_get_error()); + break; + + default: + message = g_strdup(/* Error displayed to user when they enter an invalid calculation */ + _("Malformed expression")); + break; + } + + if (error_token) + free(error_token); + + if (message) { + math_equation_set_status(equation, message); + g_free(message); + } +} + + +void +math_equation_factorize(MathEquation *equation) +{ + MPNumber x; + GList *factors, *factor; + GString *text; + + 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; + } + + factors = mp_factorize(&x); + + text = g_string_new(""); + + for (factor = factors; factor; factor = factor->next) { + gchar temp[MAX_DIGITS]; + MPNumber *n; + + n = factor->data; + display_make_number(equation, temp, MAX_DIGITS, n); + g_string_append(text, temp); + if (factor->next) + g_string_append(text, "×"); + g_slice_free(MPNumber, n); + } + g_list_free(factors); + + math_equation_set(equation, text->str); + g_string_free(text, TRUE); +} + + +void +math_equation_delete(MathEquation *equation) +{ + gint cursor; + GtkTextIter start, end; + + g_object_get(G_OBJECT(equation), "cursor-position", &cursor, NULL); + if (cursor >= gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(equation))) + return; + + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &start, cursor); + gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(equation), &end, cursor+1); + gtk_text_buffer_delete(GTK_TEXT_BUFFER(equation), &start, &end); +} + + +void +math_equation_backspace(MathEquation *equation) +{ + /* Can't delete empty display */ + if (math_equation_is_empty(equation)) + return; + + if (gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(equation))) + gtk_text_buffer_delete_selection(GTK_TEXT_BUFFER(equation), FALSE, FALSE); + else { + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &iter, gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(equation))); + gtk_text_buffer_backspace(GTK_TEXT_BUFFER(equation), &iter, TRUE, TRUE); + } +} + + +void +math_equation_clear(MathEquation *equation) +{ + math_equation_set_number_mode(equation, NORMAL); + gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), "", -1); + clear_ans(equation, FALSE); +} + + +void +math_equation_shift(MathEquation *equation, gint count) +{ + MPNumber z; + + if (!math_equation_get_number(equation, &z)) { + math_equation_set_status(equation, + /* This message is displayed in the status bar when a bit + shift operation is performed and the display does not contain a number */ + _("No sane value to bitwise shift")); + return; + } + + mp_shift(&z, count, &z); + math_equation_set_number(equation, &z); +} + + +void +math_equation_toggle_bit(MathEquation *equation, guint bit) +{ + MPNumber x; + guint64 bits; + gboolean result; + + result = math_equation_get_number(equation, &x); + if (result) { + MPNumber max; + mp_set_from_unsigned_integer(G_MAXUINT64, &max); + if (mp_is_negative(&x) || mp_is_greater_than(&x, &max)) + result = FALSE; + else + bits = mp_cast_to_unsigned_int(&x); + } + + if (!result) { + math_equation_set_status(equation, + /* Message displayed when cannot toggle bit in display*/ + _("Displayed value not an integer")); + return; + } + + bits ^= (1LL << (63 - bit)); + + mp_set_from_unsigned_integer(bits, &x); + + // FIXME: Only do this if in ans format, otherwise set text in same format as previous number + math_equation_set_number(equation, &x); +} + + +/* 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, + const GValue *value, + GParamSpec *pspec) +{ + MathEquation *self; + + self = MATH_EQUATION (object); + + switch (prop_id) { + case PROP_STATUS: + math_equation_set_status(self, g_value_get_string(value)); + break; + case PROP_DISPLAY: + math_equation_set(self, g_value_get_string(value)); + break; + case PROP_NUMBER_MODE: + math_equation_set_number_mode(self, g_value_get_int(value)); + break; + case PROP_ACCURACY: + math_equation_set_accuracy(self, g_value_get_int(value)); + break; + case PROP_SHOW_THOUSANDS_SEPARATORS: + math_equation_set_show_thousands_separators(self, g_value_get_boolean(value)); + break; + case PROP_SHOW_TRAILING_ZEROES: + math_equation_set_show_trailing_zeroes(self, g_value_get_boolean(value)); + break; + case PROP_NUMBER_FORMAT: + math_equation_set_number_format(self, g_value_get_int(value)); + break; + case PROP_BASE: + math_equation_set_base(self, g_value_get_int(value)); + break; + case PROP_WORD_SIZE: + math_equation_set_word_size(self, g_value_get_int(value)); + break; + case PROP_ANGLE_UNITS: + math_equation_set_angle_units(self, g_value_get_int(value)); + break; + case PROP_SOURCE_CURRENCY: + math_equation_set_source_currency(self, g_value_get_string(value)); + break; + case PROP_TARGET_CURRENCY: + math_equation_set_target_currency(self, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +math_equation_get_property(GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MathEquation *self; + gchar *text; + + 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); + g_value_set_string(value, text); + g_free(text); + break; + case PROP_EQUATION: + text = math_equation_get_equation(self); + g_value_set_string(value, text); + g_free(text); + break; + case PROP_NUMBER_MODE: + g_value_set_enum(value, self->priv->number_mode); + break; + case PROP_ACCURACY: + g_value_set_int(value, self->priv->accuracy); + break; + case PROP_SHOW_THOUSANDS_SEPARATORS: + g_value_set_boolean(value, self->priv->show_tsep); + break; + case PROP_SHOW_TRAILING_ZEROES: + g_value_set_boolean(value, self->priv->show_zeroes); + break; + case PROP_NUMBER_FORMAT: + g_value_set_enum(value, self->priv->format); + break; + case PROP_BASE: + g_value_set_int(value, math_equation_get_base(self)); + break; + case PROP_WORD_SIZE: + g_value_set_int(value, self->priv->word_size); + break; + case PROP_ANGLE_UNITS: + g_value_set_enum(value, self->priv->angle_units); + break; + case PROP_SOURCE_CURRENCY: + g_value_set_string(value, self->priv->source_currency); + break; + case PROP_TARGET_CURRENCY: + g_value_set_string(value, self->priv->target_currency); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +math_equation_class_init (MathEquationClass *klass) +{ + static GEnumValue number_mode_values[] = + { + {NORMAL, "normal", "normal"}, + {SUPERSCRIPT, "superscript", "superscript"}, + {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"}, + {MP_DEGREES, "degrees", "degrees"}, + {MP_GRADIANS, "gradians", "gradians"}, + {0, NULL, NULL} + }; + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = math_equation_get_property; + object_class->set_property = math_equation_set_property; + + 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); + angle_unit_type = g_enum_register_static("AngleUnit", angle_unit_values); + + g_object_class_install_property(object_class, + PROP_STATUS, + g_param_spec_string("status", + "status", + "Equation status", + "", + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_DISPLAY, + g_param_spec_string("display", + "display", + "Displayed equation text", + "", + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_EQUATION, + g_param_spec_string("equation", + "equation", + "Equation text", + "", + G_PARAM_READABLE)); + g_object_class_install_property(object_class, + PROP_NUMBER_MODE, + g_param_spec_enum("number-mode", + "number-mode", + "Input number mode", + number_mode_type, + NORMAL, + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_ACCURACY, + g_param_spec_int("accuracy", + "accuracy", + "Display accuracy", + 0, 20, 9, + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_SHOW_THOUSANDS_SEPARATORS, + g_param_spec_boolean("show-thousands-separators", + "show-thousands-separators", + "Show thousands separators", + TRUE, + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_SHOW_TRAILING_ZEROES, + g_param_spec_boolean("show-trailing-zeroes", + "show-trailing-zeroes", + "Show trailing zeroes", + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_NUMBER_FORMAT, + g_param_spec_enum("number-format", + "number-format", + "Display format", + number_format_type, + FIX, + 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, + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_WORD_SIZE, + g_param_spec_int("word-size", + "word-size", + "Word size in bits", + 8, 64, 64, + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_ANGLE_UNITS, + g_param_spec_enum("angle-units", + "angle-units", + "Angle units", + angle_unit_type, + MP_DEGREES, + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_SOURCE_CURRENCY, + g_param_spec_string("source-currency", + "source-currency", + "Source Currency", + "", + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_TARGET_CURRENCY, + g_param_spec_string("target-currency", + "target-currency", + "target Currency", + "", + G_PARAM_READWRITE)); +} + + +static void +pre_insert_text_cb (MathEquation *equation, + GtkTextIter *location, + gchar *text, + gint len, + gpointer user_data) +{ + gunichar c; + + 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)) { + 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); + } + + if (equation->priv->ans_start) { + gint ans_start, ans_end; + gint offset; + + 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); + } +} + + +static gboolean +on_delete(MathEquation *equation) +{ + equation->priv->in_delete = FALSE; + return FALSE; +} + + +static void +pre_delete_range_cb (MathEquation *equation, + GtkTextIter *start, + GtkTextIter *end, + gpointer user_data) +{ + if (equation->priv->in_reformat) + return; + + math_equation_push_undo_stack(equation); + + equation->priv->in_delete = TRUE; + g_idle_add((GSourceFunc)on_delete, equation); + + if (equation->priv->ans_start) { + gint ans_start, ans_end; + gint start_offset, end_offset; + + 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); + } +} + + +static void +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; + g_object_notify(G_OBJECT(equation), "display"); +} + + +static void +delete_range_cb (MathEquation *equation, + GtkTextIter *start, + GtkTextIter *end, + gpointer user_data) +{ + if (equation->priv->in_reformat) + return; + + // FIXME: A replace will emit this both for delete-range and insert-text, can it be avoided? + g_object_notify(G_OBJECT(equation), "display"); +} + + +static void +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; + 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); + + 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_after(equation, "insert-text", G_CALLBACK(insert_text_cb), equation); + g_signal_connect_after(equation, "delete-range", G_CALLBACK(delete_range_cb), equation); + + digits = g_strsplit(digit_values, ",", -1); + 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]); + } + else + equation->priv->digits[i] = strdup(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; + + mp_set_from_integer(0, &equation->priv->state.ans); +} |