summaryrefslogtreecommitdiff
path: root/src/math-equation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/math-equation.c')
-rw-r--r--src/math-equation.c1002
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);
}