summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am147
-rw-r--r--src/currency-manager.c626
-rw-r--r--src/currency-manager.h47
-rw-r--r--src/currency.c300
-rw-r--r--src/currency.h103
-rw-r--r--src/financial.c26
-rw-r--r--src/financial.h46
-rw-r--r--src/lexer.c587
-rw-r--r--src/lexer.h40
-rw-r--r--src/mate-calc-cmd.c44
-rw-r--r--src/mate-calc.c433
-rw-r--r--src/math-buttons.c864
-rw-r--r--src/math-buttons.h52
-rw-r--r--src/math-converter.c440
-rw-r--r--src/math-converter.h53
-rw-r--r--src/math-display.c229
-rw-r--r--src/math-display.h42
-rw-r--r--src/math-equation.c1002
-rw-r--r--src/math-equation.h205
-rw-r--r--src/math-preferences.c74
-rw-r--r--src/math-preferences.h36
-rw-r--r--src/math-variable-popup.c308
-rw-r--r--src/math-variable-popup.h40
-rw-r--r--src/math-variables.c64
-rw-r--r--src/math-variables.h46
-rw-r--r--src/math-window.c502
-rw-r--r--src/math-window.h50
-rw-r--r--src/mp-binary.c70
-rw-r--r--src/mp-convert.c246
-rw-r--r--src/mp-enums.c.template36
-rw-r--r--src/mp-enums.h.template25
-rw-r--r--src/mp-equation.c273
-rw-r--r--src/mp-equation.h97
-rw-r--r--src/mp-private.h34
-rw-r--r--src/mp-serializer.c617
-rw-r--r--src/mp-serializer.h79
-rw-r--r--src/mp-trigonometric.c40
-rw-r--r--src/mp.c86
-rw-r--r--src/mp.h71
-rw-r--r--src/parser.c1228
-rw-r--r--src/parser.h79
-rw-r--r--src/parserfunc.c967
-rw-r--r--src/parserfunc.h80
-rw-r--r--src/prelexer.c214
-rw-r--r--src/prelexer.h93
-rw-r--r--src/test-mp-equation.c630
-rw-r--r--src/test-mp.c240
-rw-r--r--src/unit-category.c136
-rw-r--r--src/unit-category.h56
-rw-r--r--src/unit-manager.c268
-rw-r--r--src/unit-manager.h54
-rw-r--r--src/unit.c212
-rw-r--r--src/unit.h60
53 files changed, 9515 insertions, 2882 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index c01c037..03dbb78 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,4 +1,7 @@
bin_PROGRAMS = mate-calc mate-calc-cmd
+noinst_PROGRAMS = test-mp test-mp-equation
+
+TESTS = test-mp test-mp-equation
INCLUDES = \
-DUI_DIR=\""$(datadir)/mate-calc"\" \
@@ -9,11 +12,15 @@ INCLUDES = \
$(MATE_CALC_CFLAGS)
mate_calc_SOURCES = \
+ mate-calc.c \
currency.c \
currency.h \
- mate-calc.c \
+ currency-manager.c \
+ currency-manager.h \
math-buttons.c \
math-buttons.h \
+ math-converter.c \
+ math-converter.h \
math-display.c \
math-display.h \
math-equation.c \
@@ -22,61 +29,139 @@ mate_calc_SOURCES = \
math-preferences.h \
math-variables.c \
math-variables.h \
+ math-variable-popup.c \
+ math-variable-popup.h \
math-window.c \
math-window.h \
mp.c \
mp.h \
mp-binary.c \
mp-convert.c \
- mp-private.h \
- mp-trigonometric.c \
+ mp-enums.c \
+ mp-enums.h \
mp-equation.c \
mp-equation.h \
- mp-equation-private.h \
- mp-equation-lexer.c \
- mp-equation-lexer.h \
- mp-equation-parser.c \
- mp-equation-parser.h \
+ mp-private.h \
+ mp-serializer.c \
+ mp-serializer.h \
+ mp-trigonometric.c \
financial.c \
financial.h \
- unittest.c \
- unittest.h
+ unit.c \
+ unit.h \
+ unit-category.c \
+ unit-category.h \
+ unit-manager.c \
+ unit-manager.h \
+ prelexer.c \
+ prelexer.h \
+ lexer.c \
+ lexer.h \
+ parserfunc.c \
+ parserfunc.h \
+ parser.c \
+ parser.h
mate_calc_LDADD = \
- $(MATE_CALC_LIBS)
+ $(MATE_CALC_LIBS)
mate_calc_cmd_SOURCES = \
mate-calc-cmd.c \
+ currency.c \
+ currency.h \
+ currency-manager.c \
+ currency-manager.h \
mp.c \
- mp-convert.c \
mp-binary.c \
- mp-trigonometric.c \
+ mp-convert.c \
+ mp-enums.c \
+ mp-enums.h \
mp-equation.c \
- mp-equation-parser.c \
- mp-equation-lexer.c
+ mp-serializer.c \
+ mp-serializer.h\
+ mp-trigonometric.c \
+ unit.c \
+ unit.h \
+ unit-category.c \
+ unit-category.h \
+ unit-manager.c \
+ unit-manager.h \
+ prelexer.c \
+ prelexer.h \
+ lexer.c \
+ lexer.h \
+ parserfunc.c \
+ parserfunc.h \
+ parser.c \
+ parser.h
mate_calc_cmd_LDADD = \
$(MATE_CALC_CMD_LIBS) \
-lm
+test_mp_SOURCES = \
+ test-mp.c \
+ mp.c \
+ mp-binary.c \
+ mp-convert.c \
+ mp-enums.c \
+ mp-enums.h \
+ mp-serializer.c \
+ mp-serializer.h \
+ mp-trigonometric.c
+
+test_mp_LDADD = \
+ $(MATE_CALC_CMD_LIBS) \
+ -lm
+
+test_mp_equation_SOURCES = \
+ test-mp-equation.c \
+ currency.c \
+ currency.h \
+ currency-manager.c \
+ currency-manager.h \
+ mp.c \
+ mp-convert.c \
+ mp-binary.c \
+ mp-enums.c \
+ mp-enums.h \
+ mp-equation.c \
+ mp-serializer.c \
+ mp-serializer.h \
+ mp-trigonometric.c \
+ unit.c \
+ unit.h \
+ unit-category.c \
+ unit-category.h \
+ unit-manager.c \
+ unit-manager.h \
+ prelexer.c \
+ prelexer.h \
+ lexer.c \
+ lexer.h \
+ parserfunc.c \
+ parserfunc.h \
+ parser.c \
+ parser.h
+
+test_mp_equation_LDADD = \
+ $(MATE_CALC_CMD_LIBS) \
+ -lm
+
CLEANFILES = \
- mp-equation-parser.h \
- mp-equation-parser.c \
- mp-equation-lexer.c \
- mp-equation-lexer.h
+ mp-enums.c \
+ mp-enums.h
-# Generate parser files
-mp-equation-parser.c mp-equation-parser.h: mp-equation-parser.y mp-equation-lexer.h
- $(AM_V_GEN)$(YACC) -d -o mp-equation-parser.c $(srcdir)/mp-equation-parser.y
+# Generate enum types
+mp-enums.h: mp-enums.h.template mp-serializer.h
+ $(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/mp-enums.h.template $(srcdir)/mp-serializer.h > mp-enums.h
-# Generate lexer files
-mp-equation-lexer.c mp-equation-lexer.h: mp-equation-lexer.l
- $(AM_V_GEN)$(LEX) $(srcdir)/mp-equation-lexer.l
+mp-enums.c: mp-enums.c.template mp-enums.h mp-serializer.h
+ $(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/mp-enums.c.template $(srcdir)/mp-serializer.h > mp-enums.c
-# Rebuild parser when source files change
-mp-equation-parser.o: mp-equation-lexer.h
-mp-equation-lexer.o: mp-equation-parser.h
-mp-equation.c: mp-equation-lexer.h mp-equation-parser.h
+# Fix dependencies
+math-serializer.c: mp-enums.h
+math-equation.c: mp-enums.h
# Install a symlink between mate-calc and mate-calculator
install-exec-hook:
@@ -89,8 +174,8 @@ uninstall-local:
&& rm -f "$(DESTDIR)$(bindir)/mate-calculator"
EXTRA_DIST = \
- mp-equation-parser.y \
- mp-equation-lexer.l
+ mp-enums.c.template \
+ mp-enums.h.template
DISTCLEANFILES = \
Makefile.in
diff --git a/src/currency-manager.c b/src/currency-manager.c
new file mode 100644
index 0000000..cd57ab6
--- /dev/null
+++ b/src/currency-manager.c
@@ -0,0 +1,626 @@
+/*
+ * 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 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 <time.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <glib/gi18n.h>
+
+#include "currency-manager.h"
+#include "mp.h"
+
+typedef struct {
+ char *short_name;
+ char *symbol;
+ char *long_name;
+} CurrencyInfo;
+static const CurrencyInfo currency_info[] = {
+ {"AED", "إ.د", N_("UAE Dirham")},
+ {"AUD", "$", N_("Australian Dollar")},
+ {"BGN", "лв", N_("Bulgarian Lev")},
+ {"BHD", ".ب.د", N_("Bahraini Dinar")},
+ {"BND", "$", N_("Brunei Dollar")},
+ {"BRL", "R$", N_("Brazilian Real")},
+ {"BWP", "P", N_("Botswana Pula")},
+ {"CAD", "$", N_("Canadian Dollar")},
+ {"CFA", "Fr", N_("CFA Franc")},
+ {"CHF", "Fr", N_("Swiss Franc")},
+ {"CLP", "$", N_("Chilean Peso")},
+ {"CNY", "元", N_("Chinese Yuan")},
+ {"COP", "$", N_("Colombian Peso")},
+ {"CZK", "Kč", N_("Czech Koruna")},
+ {"DKK", "kr", N_("Danish Krone")},
+ {"DZD", "ج.د", N_("Algerian Dinar")},
+ {"EEK", "KR", N_("Estonian Kroon")},
+ {"EUR", "€", N_("Euro")},
+ {"GBP", "£", N_("Pound Sterling")},
+ {"HKD", "$", N_("Hong Kong Dollar")},
+ {"HRK", "kn", N_("Croatian Kuna")},
+ {"HUF", "Ft", N_("Hungarian Forint")},
+ {"IDR", "Rp", N_("Indonesian Rupiah")},
+ {"ILS", "₪", N_("Israeli New Shekel")},
+ {"INR", "₹", N_("Indian Rupee")},
+ {"IRR", "﷼", N_("Iranian Rial")},
+ {"ISK", "kr", N_("Icelandic Krona")},
+ {"JPY", "¥", N_("Japanese Yen")},
+ {"KRW", "₩", N_("South Korean Won")},
+ {"KWD", "ك.د", N_("Kuwaiti Dinar")},
+ {"KZT", "₸", N_("Kazakhstani Tenge")},
+ {"LKR", "Rs", N_("Sri Lankan Rupee")},
+ {"LTL", "Lt", N_("Lithuanian Litas")},
+ {"LVL", "Ls", N_("Latvian Lats")},
+ {"LYD", "د.ل", N_("Libyan Dinar")},
+ {"MUR", "Rs", N_("Mauritian Rupee")},
+ {"MXN", "$", N_("Mexican Peso")},
+ {"MYR", "RM", N_("Malaysian Ringgit")},
+ {"NOK", "kr", N_("Norwegian Krone")},
+ {"NPR", "Rs", N_("Nepalese Rupee")},
+ {"NZD", "$", N_("New Zealand Dollar")},
+ {"OMR", "ع.ر.", N_("Omani Rial")},
+ {"PEN", "S/.", N_("Peruvian Nuevo Sol")},
+ {"PHP", "₱", N_("Philippine Peso")},
+ {"PKR", "Rs", N_("Pakistani Rupee")},
+ {"PLN", "zł", N_("Polish Zloty")},
+ {"QAR", "ق.ر", N_("Qatari Riyal")},
+ {"RON", "L", N_("New Romanian Leu")},
+ {"RUB", "руб.", N_("Russian Rouble")},
+ {"SAR", "س.ر", N_("Saudi Riyal")},
+ {"SEK", "kr", N_("Swedish Krona")},
+ {"SGD", "$", N_("Singapore Dollar")},
+ {"THB", "฿", N_("Thai Baht")},
+ {"TND", "ت.د", N_("Tunisian Dinar")},
+ {"TRY", "TL", N_("New Turkish Lira")},
+ {"TTD", "$", N_("T&T Dollar (TTD)")},
+ {"USD", "$", N_("US Dollar")},
+ {"UYU", "$", N_("Uruguayan Peso")},
+ {"VEF", "Bs F", N_("Venezuelan Bolívar")},
+ {"ZAR", "R", N_("South African Rand")},
+ {NULL, NULL}
+};
+
+static gboolean downloading_imf_rates = FALSE, downloading_ecb_rates = FALSE;
+static gboolean loaded_rates = FALSE;
+static gboolean load_rates(CurrencyManager *manager);
+
+struct CurrencyManagerPrivate
+{
+ GList *currencies;
+};
+
+G_DEFINE_TYPE (CurrencyManager, currency_manager, G_TYPE_OBJECT);
+
+
+enum {
+ UPDATED,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static CurrencyManager *default_currency_manager = NULL;
+
+
+CurrencyManager *
+currency_manager_get_default(void)
+{
+ int i;
+
+ if (default_currency_manager)
+ return default_currency_manager;
+
+ default_currency_manager = g_object_new(currency_manager_get_type(), NULL);
+
+ for (i = 0; currency_info[i].short_name; i++) {
+ Currency *c = currency_new(currency_info[i].short_name,
+ _(currency_info[i].long_name),
+ currency_info[i].symbol);
+ default_currency_manager->priv->currencies = g_list_append(default_currency_manager->priv->currencies, c);
+ }
+
+ return default_currency_manager;
+}
+
+
+GList *
+currency_manager_get_currencies(CurrencyManager *manager)
+{
+ g_return_val_if_fail(manager != NULL, NULL);
+ return manager->priv->currencies;
+}
+
+
+Currency *
+currency_manager_get_currency(CurrencyManager *manager, const gchar *name)
+{
+ g_return_val_if_fail(manager != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ GList *link;
+ for (link = manager->priv->currencies; link; link = link->next) {
+ Currency *c = link->data;
+ const MPNumber *value;
+
+ value = currency_get_value(c);
+
+ if (!strcmp(name, currency_get_name(c))) {
+ if (mp_is_negative(value) ||
+ mp_is_zero(value)) {
+ return NULL;
+ }
+ else
+ return c;
+ }
+ }
+ return NULL;
+}
+
+
+static char *
+get_imf_rate_filepath()
+{
+ return g_build_filename(g_get_user_cache_dir (),
+ "mate-calc",
+ "rms_five.xls",
+ NULL);
+}
+
+
+static char *
+get_ecb_rate_filepath()
+{
+ return g_build_filename(g_get_user_cache_dir (),
+ "mate-calc",
+ "eurofxref-daily.xml",
+ NULL);
+}
+
+
+static Currency *
+add_currency(CurrencyManager *manager, const gchar *short_name)
+{
+ GList *iter;
+ Currency *c;
+
+ for (iter = manager->priv->currencies; iter; iter = iter->next) {
+ c = iter->data;
+ if (strcmp(short_name, currency_get_name(c)) == 0)
+ return c;
+ }
+
+ g_warning("Currency %s is not in the currency table", short_name);
+ c = currency_new(short_name, short_name, short_name);
+ manager->priv->currencies = g_list_append(manager->priv->currencies, c);
+
+ return c;
+}
+
+
+/* A file needs to be redownloaded if it doesn't exist, or is too old.
+ * When an error occur, it probably won't hurt to try to download again.
+ */
+static gboolean
+file_needs_update(gchar *filename, double max_age)
+{
+ struct stat buf;
+
+ if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR))
+ return TRUE;
+
+ if (g_stat(filename, &buf) == -1)
+ return TRUE;
+
+ if (difftime(time(NULL), buf.st_mtime) > max_age)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static void
+download_imf_cb(GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ CurrencyManager *manager = user_data;
+ GError *error = NULL;
+
+ if (g_file_copy_finish(G_FILE(object), result, &error))
+ g_debug("IMF rates updated");
+ else
+ g_warning("Couldn't download IMF currency rate file: %s", error->message);
+ g_clear_error(&error);
+ downloading_imf_rates = FALSE;
+ load_rates(manager);
+}
+
+
+static void
+download_ecb_cb(GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ CurrencyManager *manager = user_data;
+ GError *error = NULL;
+
+ if (g_file_copy_finish(G_FILE(object), result, &error))
+ g_debug("ECB rates updated");
+ else
+ g_warning("Couldn't download ECB currency rate file: %s", error->message);
+ g_clear_error(&error);
+ downloading_ecb_rates = FALSE;
+ load_rates(manager);
+}
+
+
+static void
+download_file(CurrencyManager *manager, gchar *uri, gchar *filename, GAsyncReadyCallback callback)
+{
+ gchar *directory;
+ GFile *source, *dest;
+
+ directory = g_path_get_dirname(filename);
+ g_mkdir_with_parents(directory, 0755);
+ g_free(directory);
+
+ source = g_file_new_for_uri(uri);
+ dest = g_file_new_for_path(filename);
+
+ g_file_copy_async(source, dest, G_FILE_COPY_OVERWRITE, G_PRIORITY_DEFAULT, NULL, NULL, NULL, callback, manager);
+ g_object_unref(source);
+ g_object_unref(dest);
+}
+
+
+static void
+load_imf_rates(CurrencyManager *manager)
+{
+ gchar *filename;
+ gchar *data, **lines;
+ gsize length;
+ GError *error = NULL;
+ int i;
+ gboolean result, in_data = FALSE;
+ struct
+ {
+ const gchar *name, *symbol;
+ } name_map[] =
+ {
+ {"Euro", "EUR"},
+ {"Japanese Yen", "JPY"},
+ {"U.K. Pound Sterling", "GBP"},
+ {"U.S. Dollar", "USD"},
+ {"Algerian Dinar", "DZD"},
+ {"Australian Dollar", "AUD"},
+ {"Bahrain Dinar", "BHD"},
+ {"Botswana Pula", "BWP"},
+ {"Brazilian Real", "BRL"},
+ {"Brunei Dollar", "BND"},
+ {"Canadian Dollar", "CAD"},
+ {"Chilean Peso", "CLP"},
+ {"Chinese Yuan", "CNY"},
+ {"Colombian Peso", "COP"},
+ {"Czech Koruna", "CZK"},
+ {"Danish Krone", "DKK"},
+ {"Hungarian Forint", "HUF"},
+ {"Icelandic Krona", "ISK"},
+ {"Indian Rupee", "INR"},
+ {"Indonesian Rupiah", "IDR"},
+ {"Iranian Rial", "IRR"},
+ {"Israeli New Sheqel", "ILS"},
+ {"Kazakhstani Tenge", "KZT"},
+ {"Korean Won", "KRW"},
+ {"Kuwaiti Dinar", "KWD"},
+ {"Libyan Dinar", "LYD"},
+ {"Malaysian Ringgit", "MYR"},
+ {"Mauritian Rupee", "MUR"},
+ {"Mexican Peso", "MXN"},
+ {"Nepalese Rupee", "NPR"},
+ {"New Zealand Dollar", "NZD"},
+ {"Norwegian Krone", "NOK"},
+ {"Rial Omani", "OMR"},
+ {"Pakistani Rupee", "PKR"},
+ {"Nuevo Sol", "PEN"},
+ {"Philippine Peso", "PHP"},
+ {"Polish Zloty", "PLN"},
+ {"Qatar Riyal", "QAR"},
+ {"Russian Ruble", "RUB"},
+ {"Saudi Arabian Riyal", "SAR"},
+ {"Singapore Dollar", "SGD"},
+ {"South African Rand", "ZAR"},
+ {"Sri Lanka Rupee", "LKR"},
+ {"Swedish Krona", "SEK"},
+ {"Swiss Franc", "CHF"},
+ {"Thai Baht", "THB"},
+ {"Trinidad And Tobago Dollar", "TTD"},
+ {"Tunisian Dinar", "TND"},
+ {"U.A.E. Dirham", "AED"},
+ {"Peso Uruguayo", "UYU"},
+ {"Bolivar Fuerte", "VEF"},
+ {NULL, NULL}
+ };
+
+ filename = get_imf_rate_filepath();
+ result = g_file_get_contents(filename, &data, &length, &error);
+ g_free(filename);
+ if (!result)
+ {
+ g_warning("Failed to read exchange rates: %s", error->message);
+ g_clear_error(&error);
+ return;
+ }
+
+ lines = g_strsplit(data, "\n", 0);
+ g_free(data);
+
+ for (i = 0; lines[i]; i++) {
+ gchar *line, **tokens;
+
+ line = g_strchug(lines[i]);
+
+ /* Start after first blank line, stop on next */
+ if (line[0] == '\0') {
+ if (!in_data) {
+ in_data = TRUE;
+ continue;
+ }
+ else
+ break;
+ }
+ if (!in_data)
+ continue;
+
+ tokens = g_strsplit(line, "\t", 0);
+ if (strcmp(tokens[0], "Currency") != 0) {
+ gint value_index, name_index;
+
+ for (value_index = 1; tokens[value_index]; value_index++) {
+ gchar *value = g_strchug (tokens[value_index]);
+ if (value[0] != '\0')
+ break;
+ }
+ if (tokens[value_index]) {
+ for (name_index = 0; name_map[name_index].name; name_index++) {
+ if (strcmp(name_map[name_index].name, tokens[0]) == 0)
+ break;
+ }
+ if (name_map[name_index].name) {
+ Currency *c = currency_manager_get_currency(manager, name_map[name_index].symbol);
+ MPNumber value;
+
+ if (!c) {
+ g_debug ("Using IMF rate of %s for %s", tokens[value_index], name_map[name_index].symbol);
+ c = add_currency(manager, name_map[name_index].symbol);
+ }
+ mp_set_from_string(tokens[value_index], 10, &value);
+ mp_reciprocal(&value, &value);
+ currency_set_value(c, &value);
+ }
+ else
+ g_warning("Unknown currency '%s'", tokens[0]);
+ }
+ }
+ g_strfreev(tokens);
+ }
+ g_strfreev(lines);
+}
+
+
+static void
+set_ecb_rate(CurrencyManager *manager, xmlNodePtr node, Currency *eur_rate)
+{
+ xmlAttrPtr attribute;
+ gchar *name = NULL, *value = NULL;
+
+ for (attribute = node->properties; attribute; attribute = attribute->next) {
+ if (strcmp((char *)attribute->name, "currency") == 0) {
+ if (name)
+ xmlFree(name);
+ name = (gchar *)xmlNodeGetContent((xmlNodePtr)attribute);
+ } else if (strcmp ((char *)attribute->name, "rate") == 0) {
+ if (value)
+ xmlFree(value);
+ value = (gchar *)xmlNodeGetContent((xmlNodePtr)attribute);
+ }
+ }
+
+ /* Use data if value and no rate currently defined */
+ if (name && value && !currency_manager_get_currency(manager, name)) {
+ Currency *c;
+ MPNumber r, v;
+
+ g_debug ("Using ECB rate of %s for %s", value, name);
+ c = add_currency(manager, name);
+ mp_set_from_string(value, 10, &r);
+ mp_set_from_mp(currency_get_value(eur_rate), &v);
+ mp_multiply(&v, &r, &v);
+ currency_set_value(c, &v);
+ }
+
+ if (name)
+ xmlFree(name);
+ if (value)
+ xmlFree(value);
+}
+
+
+static void
+set_ecb_fixed_rate(CurrencyManager *manager, const gchar *name, const gchar *value, Currency *eur_rate)
+{
+ Currency *c;
+ MPNumber r, v;
+
+ g_debug ("Using ECB fixed rate of %s for %s", value, name);
+ c = add_currency(manager, name);
+ mp_set_from_string(value, 10, &r);
+ mp_set_from_mp(currency_get_value(eur_rate), &v);
+ mp_divide(&v, &r, &v);
+ currency_set_value(c, &v);
+}
+
+
+static void
+load_ecb_rates(CurrencyManager *manager)
+{
+ Currency *eur_rate;
+ char *filename;
+ xmlDocPtr document;
+ xmlXPathContextPtr xpath_ctx;
+ xmlXPathObjectPtr xpath_obj;
+ int i, len;
+
+ /* Scale rates to the EUR value */
+ eur_rate = currency_manager_get_currency(manager, "EUR");
+ if (!eur_rate) {
+ g_warning("Cannot use ECB rates as don't have EUR rate");
+ return;
+ }
+
+ /* Set some fixed rates */
+ set_ecb_fixed_rate(manager, "EEK", "0.06391", eur_rate);
+ set_ecb_fixed_rate(manager, "CFA", "0.152449", eur_rate);
+
+ xmlInitParser();
+ filename = get_ecb_rate_filepath();
+ document = xmlReadFile(filename, NULL, 0);
+ if (!document)
+ g_warning("Couldn't parse ECB rate file %s", filename);
+ g_free (filename);
+ if (!document)
+ return;
+
+ xpath_ctx = xmlXPathNewContext(document);
+ if (xpath_ctx == NULL) {
+ xmlFreeDoc(document);
+ g_warning("Couldn't create XPath context");
+ return;
+ }
+
+ xmlXPathRegisterNs(xpath_ctx,
+ BAD_CAST("xref"),
+ BAD_CAST("http://www.ecb.int/vocabulary/2002-08-01/eurofxref"));
+ xpath_obj = xmlXPathEvalExpression(BAD_CAST("//xref:Cube[@currency][@rate]"),
+ xpath_ctx);
+ if (xpath_obj == NULL) {
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(document);
+ g_warning("Couldn't create XPath object");
+ return;
+ }
+ len = (xpath_obj->nodesetval) ? xpath_obj->nodesetval->nodeNr : 0;
+ for (i = 0; i < len; i++) {
+ if (xpath_obj->nodesetval->nodeTab[i]->type == XML_ELEMENT_NODE)
+ set_ecb_rate(manager, xpath_obj->nodesetval->nodeTab[i], eur_rate);
+
+ /* Avoid accessing removed elements */
+ if (xpath_obj->nodesetval->nodeTab[i]->type != XML_NAMESPACE_DECL)
+ xpath_obj->nodesetval->nodeTab[i] = NULL;
+ }
+
+ xmlXPathFreeObject(xpath_obj);
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(document);
+ xmlCleanupParser();
+}
+
+
+static gboolean
+load_rates(CurrencyManager *manager)
+{
+ int i;
+
+ /* Already loaded */
+ if (loaded_rates)
+ return TRUE;
+
+ /* In process */
+ if (downloading_imf_rates || downloading_ecb_rates)
+ return FALSE;
+
+ /* Use the IMF provided values and top up with currencies tracked by the ECB and not the IMF */
+ load_imf_rates(manager);
+ load_ecb_rates(manager);
+
+ for (i = 0; currency_info[i].short_name; i++) {
+ GList *link;
+ for (link = manager->priv->currencies; link; link = link->next) {
+ Currency *c = link->data;
+ if (strcmp(currency_get_name(c), currency_info[i].short_name) == 0)
+ break;
+ }
+ if (!link)
+ g_warning("Currency %s is not provided by IMF or ECB", currency_info[i].short_name);
+ }
+
+ g_debug("Rates loaded");
+ loaded_rates = TRUE;
+
+ g_signal_emit(manager, signals[UPDATED], 0);
+
+ return TRUE;
+}
+
+
+const MPNumber *
+currency_manager_get_value(CurrencyManager *manager, const gchar *currency)
+{
+ gchar *path;
+ Currency *c;
+
+ g_return_val_if_fail(manager != NULL, NULL);
+ g_return_val_if_fail(currency != NULL, NULL);
+
+ /* Update rates if necessary */
+ path = get_imf_rate_filepath();
+ if (!downloading_imf_rates && file_needs_update(path, 60 * 60 * 24 * 7)) {
+ downloading_imf_rates = TRUE;
+ g_debug("Downloading rates from the IMF...");
+ download_file(manager, "http://www.imf.org/external/np/fin/data/rms_five.aspx?tsvflag=Y", path, download_imf_cb);
+ }
+ g_free(path);
+ path = get_ecb_rate_filepath();
+ if (!downloading_ecb_rates && file_needs_update(path, 60 * 60 * 24 * 7)) {
+ downloading_ecb_rates = TRUE;
+ g_debug("Downloading rates from the ECB...");
+ download_file(manager, "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml", path, download_ecb_cb);
+ }
+ g_free(path);
+
+ if (!load_rates(manager))
+ return NULL;
+
+ c = currency_manager_get_currency(manager, currency);
+ if (c)
+ return currency_get_value(c);
+ else
+ return NULL;
+}
+
+
+static void
+currency_manager_class_init(CurrencyManagerClass *klass)
+{
+ g_type_class_add_private(klass, sizeof(CurrencyManagerPrivate));
+
+ signals[UPDATED] =
+ g_signal_new("updated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CurrencyManagerClass, updated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+
+static void
+currency_manager_init(CurrencyManager *manager)
+{
+ manager->priv = G_TYPE_INSTANCE_GET_PRIVATE(manager, currency_manager_get_type(), CurrencyManagerPrivate);
+}
diff --git a/src/currency-manager.h b/src/currency-manager.h
new file mode 100644
index 0000000..c5e8e91
--- /dev/null
+++ b/src/currency-manager.h
@@ -0,0 +1,47 @@
+/*
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef CURRENCY_MANAGER_H
+#define CURRENCY_MANAGER_H
+
+#include "currency.h"
+#include "mp.h"
+
+G_BEGIN_DECLS
+
+#define CURRENCY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), currency_manager_get_type(), CurrencyManager))
+
+typedef struct CurrencyManagerPrivate CurrencyManagerPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ CurrencyManagerPrivate *priv;
+} CurrencyManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ void (*updated)(CurrencyManager *manager);
+} CurrencyManagerClass;
+
+GType currency_manager_get_type(void);
+
+CurrencyManager *currency_manager_get_default(void);
+
+GList *currency_manager_get_currencies(CurrencyManager *manager);
+
+Currency *currency_manager_get_currency(CurrencyManager *manager, const gchar *name);
+
+const MPNumber *currency_manager_get_value(CurrencyManager *manager, const gchar *currency);
+
+G_END_DECLS
+
+#endif /* CURRENCY_MANAGER_H */
diff --git a/src/currency.c b/src/currency.c
index 2add81b..ea81761 100644
--- a/src/currency.c
+++ b/src/currency.c
@@ -1,276 +1,96 @@
-#include <time.h>
+/*
+ * 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 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 <glib.h>
-#include <glib/gstdio.h>
-#include <gio/gio.h>
-#include <libxml/tree.h>
-#include <libxml/xpath.h>
-#include <libxml/xpathInternals.h>
+#include <string.h>
+#include <stdarg.h>
#include "currency.h"
-#include "mp.h"
-
-typedef struct {
- char* short_name;
- MPNumber value;
-} currency;
-
-static currency* currencies = NULL;
-static int currency_count = 0;
-
-static gboolean downloading_rates = FALSE;
-static gboolean loaded_rates = FALSE;
-
-static char* get_rate_filepath()
-{
- return g_build_filename(g_get_user_cache_dir(), "mate-calc", "eurofxref-daily.xml", NULL);
-}
+#include "mp-serializer.h"
+#include "currency-manager.h" // FIXME: Move out of here
-static int currency_get_index(const char *short_name)
+struct CurrencyPrivate
{
- int i;
+ gchar *name;
+ gchar *display_name;
+ gchar *symbol;
+ MPNumber value;
+};
- for (i = 0; i < currency_count; i++)
- {
- if (!strcmp(short_name, currencies[i].short_name))
- {
- if (mp_is_negative(&currencies[i].value) || mp_is_zero(&currencies[i].value))
- {
- return -1;
- }
- else
- {
- return i;
- }
- }
- }
+G_DEFINE_TYPE (Currency, currency, G_TYPE_OBJECT);
- return -1;
-}
-/* A file needs to be redownloaded if it doesn't exist, or every 7 days.
- * When an error occur, it probably won't hurt to try to download again.
- */
-static int currency_rates_needs_update()
+Currency *
+currency_new(const gchar *name,
+ const gchar *display_name,
+ const gchar *symbol)
{
- gchar* filename = get_rate_filepath();
- struct stat buf;
-
- if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR))
- {
- g_free(filename);
- return 1;
- }
-
- if (g_stat(filename, &buf) == -1)
- {
- g_free(filename);
- return 1;
- }
-
- g_free(filename);
+ Currency *currency = g_object_new(currency_get_type(), NULL);
- if (difftime(time(NULL), buf.st_mtime) > (60 * 60 * 24 * 7))
- {
- return 1;
- }
+ currency->priv->name = g_strdup(name);
+ currency->priv->display_name = g_strdup(display_name);
+ currency->priv->symbol = g_strdup(symbol);
- return 0;
+ return currency;
}
-static void download_cb(GObject* object, GAsyncResult* result, gpointer user_data)
+const gchar *
+currency_get_name(Currency *currency)
{
- GError* error = NULL;
-
- if (g_file_copy_finish(G_FILE(object), result, &error))
- {
- g_debug("Rates updated");
- }
- else
- {
- g_warning("Couldn't download currency file: %s", error->message);
- }
-
- g_clear_error(&error);
- downloading_rates = FALSE;
+ g_return_val_if_fail (currency != NULL, NULL);
+ return currency->priv->name;
}
-static void currency_download_rates()
+const gchar *
+currency_get_display_name(Currency *currency)
{
- gchar* filename;
- gchar* directory;
- GFile* source;
- GFile* dest;
-
- downloading_rates = TRUE;
- g_debug("Downloading rates...");
-
- filename = get_rate_filepath();
- directory = g_path_get_dirname(filename);
- g_mkdir_with_parents(directory, 0755);
- g_free(directory);
-
- /* HACK: It is safe? */
- source = g_file_new_for_uri("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");
- dest = g_file_new_for_path(filename);
- g_free(filename);
-
- g_file_copy_async(source, dest, G_FILE_COPY_OVERWRITE, G_PRIORITY_DEFAULT, NULL, NULL, NULL, download_cb, NULL);
- g_object_unref(source);
- g_object_unref(dest);
+ g_return_val_if_fail (currency != NULL, NULL);
+ return currency->priv->display_name;
}
-static void set_rate(xmlNodePtr node, currency* cur)
+const gchar *
+currency_get_symbol(Currency *currency)
{
- xmlAttrPtr attribute;
-
- for (attribute = node->properties; attribute; attribute = attribute->next)
- {
- if (strcmp((char*) attribute->name, "currency") == 0)
- {
- cur->short_name = (char*) xmlNodeGetContent((xmlNodePtr) attribute);
- }
- else if (strcmp ((char*) attribute->name, "rate") == 0)
- {
- char* val = (char*) xmlNodeGetContent((xmlNodePtr) attribute);
- mp_set_from_string(val, 10, &(cur->value));
- xmlFree(val);
- }
- }
+ g_return_val_if_fail (currency != NULL, NULL);
+ return currency->priv->symbol;
}
-static void currency_load_rates()
-{
- char* filename = get_rate_filepath();
- xmlDocPtr document;
- xmlXPathContextPtr xpath_ctx;
- xmlXPathObjectPtr xpath_obj;
- int i;
- int len;
-
- g_return_if_fail(g_file_test(filename, G_FILE_TEST_IS_REGULAR));
-
- xmlInitParser();
- document = xmlReadFile(filename, NULL, 0);
- g_free (filename);
-
- if (document == NULL)
- {
- fprintf(stderr, "Couldn't parse data file\n");
- return;
- }
- xpath_ctx = xmlXPathNewContext(document);
-
- if (xpath_ctx == NULL)
- {
- xmlFreeDoc(document);
- fprintf(stderr, "Couldn't create XPath context\n");
- return;
- }
-
- xmlXPathRegisterNs(xpath_ctx, BAD_CAST("xref"), BAD_CAST("http://www.ecb.int/vocabulary/2002-08-01/eurofxref"));
- xpath_obj = xmlXPathEvalExpression(BAD_CAST("//xref:Cube[@currency][@rate]"), xpath_ctx);
-
- if (xpath_obj == NULL)
- {
- xmlXPathFreeContext(xpath_ctx);
- xmlFreeDoc(document);
- fprintf(stderr, "Couldn't create XPath object\n");
- return;
- }
-
- len = (xpath_obj->nodesetval) ? xpath_obj->nodesetval->nodeNr : 0;
- currency_count = len + 1;
- currencies = g_slice_alloc0(sizeof(currency) * currency_count);
-
- for (i = 0; i < len; i++)
- {
- if (xpath_obj->nodesetval->nodeTab[i]->type == XML_ELEMENT_NODE)
- {
- set_rate(xpath_obj->nodesetval->nodeTab[i], &currencies[i]);
- }
-
- // Avoid accessing removed elements
- if (xpath_obj->nodesetval->nodeTab[i]->type != XML_NAMESPACE_DECL)
- {
- xpath_obj->nodesetval->nodeTab[i] = NULL;
- }
- }
-
- currencies[len].short_name = g_strdup("EUR");
- MPNumber foo;
- mp_set_from_integer(1, &foo);
- currencies[len].value = foo;
-
- xmlXPathFreeObject(xpath_obj);
- xmlXPathFreeContext(xpath_ctx);
- xmlFreeDoc(document);
- xmlCleanupParser();
-
- g_debug("Rates loaded");
- loaded_rates = TRUE;
+void
+currency_set_value(Currency *currency, MPNumber *value)
+{
+ g_return_if_fail (currency != NULL);
+ g_return_if_fail (value != NULL);
+ mp_set_from_mp (value, &currency->priv->value);
}
-gboolean currency_convert(const MPNumber* from_amount, const char* source_currency, const char* target_currency, MPNumber* to_amount)
+const MPNumber *
+currency_get_value(Currency *currency)
{
- int from_index, to_index;
-
- if (downloading_rates)
- {
- return FALSE;
- }
-
- /* Update currency if necessary */
- if (currency_rates_needs_update())
- {
- currency_download_rates();
- return FALSE;
- }
-
- if (!loaded_rates)
- {
- currency_load_rates();
- }
-
- from_index = currency_get_index(source_currency);
- to_index = currency_get_index(target_currency);
-
- if (from_index < 0 || to_index < 0)
- {
- return FALSE;
- }
-
- if (mp_is_zero(&currencies[from_index].value) || mp_is_zero(&currencies[to_index].value))
- {
- mp_set_from_integer(0, to_amount);
- return FALSE;
- }
-
- mp_divide(from_amount, &currencies[from_index].value, to_amount);
- mp_multiply(to_amount, &currencies[to_index].value, to_amount);
-
- return TRUE;
+ g_return_val_if_fail (currency != NULL, NULL);
+ return &currency->priv->value;
}
-void currency_free_resources()
-{
- int i;
- for (i = 0; i < currency_count; i++)
- {
- if (currencies[i].short_name != NULL)
- {
- xmlFree(currencies[i].short_name);
- }
- }
+static void
+currency_class_init(CurrencyClass *klass)
+{
+ g_type_class_add_private(klass, sizeof(CurrencyPrivate));
+}
- g_slice_free1(currency_count * sizeof(currency), currencies);
- currencies = NULL;
- currency_count = 0;
+static void
+currency_init(Currency *currency)
+{
+ currency->priv = G_TYPE_INSTANCE_GET_PRIVATE(currency, currency_get_type(), CurrencyPrivate);
}
diff --git a/src/currency.h b/src/currency.h
index f29239e..663ece7 100644
--- a/src/currency.h
+++ b/src/currency.h
@@ -1,65 +1,54 @@
+/*
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
#ifndef CURRENCY_H
#define CURRENCY_H
-#include <glib/gi18n.h>
-
+#include <glib-object.h>
#include "mp.h"
-struct currency_name {
- char* short_name;
- char* symbol;
- char* long_name;
-};
+G_BEGIN_DECLS
-/*
- * List taken from http://www.ecb.int/press/pr/date/2008/html/pr081205.en.html
- * with euro added.
- */
-static const struct currency_name currency_names[] = {
- {"AUD", "$", N_("Australian dollar")},
- {"BGN", "лв", N_("Bulgarian lev")},
- {"BRL", "R$", N_("Brazilian real")},
- {"CAD", "$", N_("Canadian dollar")},
- {"CHF", "Fr", N_("Swiss franc")},
- {"CNY", "元", N_("Chinese yuan renminbi")},
- {"CZK", "Kč", N_("Czech koruna")},
- {"DKK", "kr", N_("Danish krone")},
- {"EEK", "KR", N_("Estonian kroon")},
- {"EUR", "€", N_("Euro")},
- {"GBP", "£", N_("Pound sterling")},
- {"HKD", "$", N_("Hong Kong dollar")},
- {"HRK", "kn", N_("Croatian kuna")},
- {"HUF", "Ft", N_("Hungarian forint")},
- {"IDR", "Rp", N_("Indonesian rupiah")},
- {"INR", "Rs", N_("Indian rupee")},
- {"ISK", "kr", N_("Icelandic krona")},
- {"JPY", "¥", N_("Japanese yen")},
- {"KRW", "₩", N_("South Korean won")},
- {"LTL", "Lt", N_("Lithuanian litas")},
- {"LVL", "Ls", N_("Latvian lats")},
- {"MXN", "$", N_("Mexican peso")},
- {"MYR", "RM", N_("Malaysian ringgit")},
- {"NOK", "kr", N_("Norwegian krone")},
- {"NZD", "$", N_("New Zealand dollar")},
- {"PHP", "₱", N_("Philippine peso")},
- {"PLN", "zł", N_("Polish zloty")},
- {"RON", "L", N_("New Romanian leu")},
- {"RUB", "руб.", N_("Russian rouble")},
- {"SEK", "kr", N_("Swedish krona")},
- {"SGD", "$", N_("Singapore dollar")},
- {"THB", "฿", N_("Thai baht")},
- {"TRY", "TL", N_("New Turkish lira")},
- {"USD", "$", N_("US dollar")},
- {"ZAR", "R", N_("South African rand")},
- {NULL, NULL}
-};
-
-// FIXME: Should indicate when rates are updated to UI
-
-/* Converts an amount of money from one currency to another */
-gboolean currency_convert(const MPNumber* from_amount, const char* source_currency, const char *target_currency, MPNumber* to_amount);
-
-/* Frees up all allocated resources */
-void currency_free_resources(void);
+#define CURRENCY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), currency_get_type(), Currency))
+
+typedef struct CurrencyPrivate CurrencyPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ CurrencyPrivate *priv;
+} Currency;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} CurrencyClass;
+
+GType currency_get_type(void);
+
+Currency *currency_new(const gchar *name,
+ const gchar *display_name,
+ const gchar *symbol);
+
+const gchar *currency_get_name(Currency *currency);
+
+const gchar *currency_get_short_display_name(Currency *currency);
+
+const gchar *currency_get_display_name(Currency *currency);
+
+const gchar *currency_get_symbol(Currency *currency);
+
+void currency_set_value(Currency *currency, MPNumber *value);
+
+const MPNumber *currency_get_value(Currency *currency);
+
+G_END_DECLS
#endif /* CURRENCY_H */
diff --git a/src/financial.c b/src/financial.c
index f388be3..fa0e33d 100644
--- a/src/financial.c
+++ b/src/financial.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 <glib/gi18n.h>
@@ -75,7 +67,7 @@ calc_ddb(MathEquation *equation, MPNumber *t, MPNumber *cost, MPNumber *life, MP
}
if (len >= 0) {
- math_equation_set_status (equation, ("Error: the number of periods must be positive"));
+ math_equation_set_status (equation, _("Error: the number of periods must be positive"));
mp_set_from_integer(0, t);
}
}
diff --git a/src/financial.h b/src/financial.h
index 53022d0..ec9f513 100644
--- a/src/financial.h
+++ b/src/financial.h
@@ -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.
*/
#ifndef FINANCIAL_H
@@ -23,19 +15,19 @@
#include "mp.h"
#include "math-equation.h"
-void do_finc_expression(MathEquation* equation, int function, MPNumber* arg1, MPNumber* arg2, MPNumber* arg3, MPNumber* arg4);
+void do_finc_expression(MathEquation *equation, int function, MPNumber *arg1, MPNumber *arg2, MPNumber *arg3, MPNumber *arg4);
enum finc_dialogs {
- FINC_CTRM_DIALOG,
- FINC_DDB_DIALOG,
- FINC_FV_DIALOG,
- FINC_GPM_DIALOG,
- FINC_PMT_DIALOG,
- FINC_PV_DIALOG,
- FINC_RATE_DIALOG,
- FINC_SLN_DIALOG,
- FINC_SYD_DIALOG,
- FINC_TERM_DIALOG
+ FINC_CTRM_DIALOG,
+ FINC_DDB_DIALOG,
+ FINC_FV_DIALOG,
+ FINC_GPM_DIALOG,
+ FINC_PMT_DIALOG,
+ FINC_PV_DIALOG,
+ FINC_RATE_DIALOG,
+ FINC_SLN_DIALOG,
+ FINC_SYD_DIALOG,
+ FINC_TERM_DIALOG
};
#endif /* FINANCIAL_H */
diff --git a/src/lexer.c b/src/lexer.c
new file mode 100644
index 0000000..176c773
--- /dev/null
+++ b/src/lexer.c
@@ -0,0 +1,587 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "lexer.h"
+#include "parserfunc.h"
+#include "mp-equation.h"
+
+static gboolean
+l_check_if_function(LexerState* state)
+{
+ gchar* name = pl_get_marked_substring(state->prelexer);
+ if(!state->parent->function_is_defined)
+ {
+ free(name);
+ return FALSE;
+ }
+ if ((*(state->parent->function_is_defined))(state->parent, name))
+ {
+ free(name);
+ return TRUE;
+ }
+ else
+ {
+ free(name);
+ return FALSE;
+ }
+}
+
+static gboolean
+l_check_if_number(LexerState* state)
+{
+ MPNumber tmp;
+ int count = 0;
+ gchar* text = pl_get_marked_substring(state->prelexer);
+ if(mp_set_from_string(text, state->parent->options->base, &tmp) == 0)
+ {
+ free(text);
+ return TRUE;
+ }
+ else
+ {
+ /* Try to rollback several characters to see, if that yeilds any number. */
+ while(strlen (text) > 0)
+ {
+ if(mp_set_from_string(text, state->parent->options->base, &tmp) == 0)
+ {
+ free(text);
+ return TRUE;
+ }
+ free(text);
+ count++;
+ pl_roll_back(state->prelexer);
+ text = pl_get_marked_substring(state->prelexer);
+ }
+ /* Undo all rollbacks. */
+ while(count--)
+ pl_get_next_token (state->prelexer);
+ free(text);
+ return FALSE;
+ }
+}
+
+/* Insert generated token to the LexerState structure. */
+static LexerToken*
+l_insert_token(LexerState* state, const LexerTokenType type)
+{
+ state->tokens = (LexerToken *) realloc(state->tokens, (state->token_count + 1) * sizeof(LexerToken));
+ assert(state->tokens != NULL);
+ state->tokens[state->token_count].string = pl_get_marked_substring(state->prelexer);
+ state->tokens[state->token_count].start_index = state->prelexer->mark_index;
+ state->tokens[state->token_count].end_index = state->prelexer->next_index;
+ state->tokens[state->token_count].token_type = type;
+ state->token_count++;
+ return &state->tokens[state->token_count - 1];
+}
+
+/* Generates next token from pre-lexer stream and call l_insert_token() to insert it at the end. */
+static LexerToken*
+l_insert_next_token(LexerState* lstate)
+{
+ PreLexerState* state = lstate->prelexer;
+ LexerTokenType type;
+ gchar* tmp;
+ pl_set_marker(state);
+ /* Ignore all blank spaces. :) */
+ while((type = pl_get_next_token(state)) == PL_SKIP)
+ /* Set marker. Beginning of new token. */
+ pl_set_marker(state);
+ if(type == T_AND
+ ||type == T_OR
+ ||type == T_XOR
+ ||type == T_NOT
+ ||type == T_ADD
+ ||type == T_SUBTRACT
+ ||type == T_MULTIPLY
+ ||type == T_DIV
+ ||type == T_L_FLOOR
+ ||type == T_R_FLOOR
+ ||type == T_L_CEILING
+ ||type == T_R_CEILING
+ ||type == T_ROOT
+ ||type == T_ROOT_3
+ ||type == T_ROOT_4
+ ||type == T_ASSIGN
+ ||type == T_L_R_BRACKET
+ ||type == T_R_R_BRACKET
+ ||type == T_L_S_BRACKET
+ ||type == T_R_S_BRACKET
+ ||type == T_L_C_BRACKET
+ ||type == T_R_C_BRACKET
+ ||type == T_ABS
+ ||type == T_POWER
+ ||type == T_FACTORIAL
+ ||type == T_PERCENTAGE)
+ {
+ return l_insert_token(lstate, type);
+ }
+ /* [PL_SUPER_MINUS][PL_SUPER_DIGIT]+ */
+ if(type == PL_SUPER_MINUS)
+ {
+ if((type = pl_get_next_token(state)) != PL_SUPER_DIGIT)
+ {
+ /* ERROR: expected PL_SUP_DIGIT */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring (state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ /* Get all PL_SUPER_DIGITs. */
+ while (pl_get_next_token(state) == PL_SUPER_DIGIT);
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NSUP_NUMBER);
+ }
+ /* [PL_SUPER_DIGIT]+ */
+ if(type == PL_SUPER_DIGIT)
+ {
+ while(pl_get_next_token(state) == PL_SUPER_DIGIT);
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_SUP_NUMBER);
+ }
+ /* [PL_SUB_DIGIT]+ */
+ if(type == PL_SUB_DIGIT)
+ {
+ while(pl_get_next_token(state) == PL_SUB_DIGIT);
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_SUB_NUMBER);
+ }
+ /* [PL_FRACTION] */
+ if(type == PL_FRACTION)
+ {
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ if(type == PL_DIGIT)
+ {
+ while((type = pl_get_next_token(state)) == PL_DIGIT);
+ if(type == PL_FRACTION)
+ {
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else if(type == PL_SUB_DIGIT)
+ {
+ while(pl_get_next_token(state) == PL_SUB_DIGIT);
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else if(type == PL_DEGREE)
+ {
+ type = pl_get_next_token(state);
+ if(type == PL_DIGIT)
+ {
+ while((type = pl_get_next_token(state)) == PL_DIGIT);
+ if(type == PL_DECIMAL)
+ {
+ goto ANGLE_NUM_DM_STATE;
+ }
+ else if(type == PL_MINUTE)
+ {
+ type = pl_get_next_token(state);
+ if(type == PL_DIGIT)
+ {
+ while((type = pl_get_next_token(state)) == PL_DIGIT);
+ if(type == PL_DECIMAL)
+ {
+ goto ANGLE_NUM_DMS_STATE;
+ }
+ else if(type == PL_SECOND)
+ {
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else
+ {
+ /* ERROR: expected PL_SECOND */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring (state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ }
+ else if(type == PL_DECIMAL)
+ {
+ANGLE_NUM_DMS_STATE:
+ if((type = pl_get_next_token (state)) != PL_DIGIT)
+ {
+ /* ERROR: expected PL_DIGIT */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ while((type = pl_get_next_token(state)) == PL_DIGIT);
+ if(type == PL_SECOND)
+ {
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else
+ {
+ /* ERROR: expected PL_SECOND */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ }
+ else
+ {
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ }
+ else
+ {
+ /* ERROR: expected PL_MINUTE | PL_DIGIT */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ }
+ else if(type == PL_DECIMAL)
+ {
+ANGLE_NUM_DM_STATE:
+ if((type = pl_get_next_token(state)) != PL_DIGIT)
+ {
+ /* ERROR: expected PL_DIGIT */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ while((type = pl_get_next_token(state)) == PL_DIGIT);
+ if(type == PL_MINUTE)
+ {
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else
+ {
+ /* ERROR: expected PL_MINUTE */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ }
+ else
+ {
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ }
+ else if(type == PL_DECIMAL)
+ {
+ goto DECIMAL_STATE;
+ }
+ else if(type == PL_HEX)
+ {
+ goto HEX_DEC_STATE;
+ }
+ else
+ {
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ }
+ if(type == PL_DECIMAL)
+ {
+DECIMAL_STATE:
+ type = pl_get_next_token(state);
+ if(type == PL_DIGIT)
+ {
+ while((type = pl_get_next_token(state)) == PL_DIGIT);
+ if(type == PL_DEGREE)
+ {
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else if(type == PL_HEX)
+ {
+ goto DECIMAL_HEX_STATE;
+ }
+ else if(type == PL_SUB_DIGIT)
+ {
+ while(pl_get_next_token(state) == PL_SUB_DIGIT);
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else
+ {
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ }
+ else if(type == PL_HEX)
+ {
+ goto DECIMAL_HEX_STATE;
+ }
+ else
+ {
+ /* ERROR: expected PL_DIGIT | PL_HEX */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ }
+ if(type == PL_HEX)
+ {
+ while((type = pl_get_next_token(state)) == PL_HEX);
+ if(type == PL_DIGIT)
+ {
+HEX_DEC_STATE:
+ while(1)
+ {
+ type = pl_get_next_token(state);
+ if(type == PL_DIGIT || type == PL_HEX)
+ {
+ continue;
+ }
+ else if(type == PL_DECIMAL)
+ {
+ goto DECIMAL_HEX_STATE;
+ }
+ else if(type == PL_SUB_DIGIT)
+ {
+ while(pl_get_next_token(state) == PL_SUB_DIGIT);
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else
+ {
+ if(l_check_if_number(lstate))
+ return l_insert_token(lstate, T_NUMBER);
+ /* ERROR: expected PL_DECIMAL | PL_DIGIT | PL_HEX */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ }
+ }
+ else if(type == PL_DECIMAL)
+ {
+DECIMAL_HEX_STATE:
+ type = pl_get_next_token(state);
+ if(!(type == PL_DIGIT || type == PL_HEX))
+ {
+ /* ERROR: expected PL_DIGIT | PL_HEX */
+ set_error(lstate->parent, PARSER_ERR_MP, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+ }
+ while(1)
+ {
+ type = pl_get_next_token(state);
+ if(type == PL_DIGIT || type == PL_HEX)
+ {
+ continue;
+ }
+ else if(type == PL_SUB_DIGIT)
+ {
+ while(pl_get_next_token(state) == PL_SUB_DIGIT);
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else
+ {
+ pl_roll_back(state);
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ }
+ }
+ else if(type == PL_SUB_DIGIT)
+ {
+ while(pl_get_next_token(state) == PL_SUB_DIGIT);
+ pl_roll_back(state);
+ if(l_check_if_number(lstate))
+ {
+ /* NUMBER */
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else
+ {
+ /* VARIABLE */
+ if(l_check_if_function(lstate))
+ {
+ return l_insert_token(lstate, T_FUNCTION);
+ }
+ else
+ {
+ return l_insert_token(lstate, T_VARIABLE);
+ }
+ }
+ }
+ else if(type == PL_LETTER)
+ {
+ goto LETTER_STATE;
+ }
+ else
+ {
+ pl_roll_back(state);
+ if(l_check_if_number(lstate))
+ {
+ /* NUMBER */
+ return l_insert_token(lstate, T_NUMBER);
+ }
+ else
+ {
+ /* VARIABLE */
+ if(l_check_if_function(lstate))
+ {
+ return l_insert_token(lstate, T_FUNCTION);
+ }
+ else
+ {
+ return l_insert_token(lstate, T_VARIABLE);
+ }
+ }
+ }
+ }
+ if(type == PL_LETTER)
+ {
+LETTER_STATE:
+ while(1)
+ {
+ type = pl_get_next_token(state);
+ if(type == PL_LETTER || type == PL_HEX)
+ {
+ continue;
+ }
+ else if(type == PL_SUB_DIGIT)
+ {
+ while(pl_get_next_token(state) == PL_SUB_DIGIT);
+ pl_roll_back(state);
+ tmp = g_ascii_strdown(pl_get_marked_substring(state), -1);
+ if(g_strcmp0(tmp, "mod") == 0)
+ {
+ return l_insert_token(lstate, T_MOD);
+ }
+ if(g_strcmp0(tmp, "and") == 0)
+ {
+ return l_insert_token(lstate, T_AND);
+ }
+ if(g_strcmp0(tmp, "or") == 0)
+ {
+ return l_insert_token(lstate, T_OR);
+ }
+ if(g_strcmp0(tmp, "xor") == 0)
+ {
+ return l_insert_token(lstate, T_XOR);
+ }
+ if(g_strcmp0(tmp, "not") == 0)
+ {
+ return l_insert_token(lstate, T_NOT);
+ }
+ if(g_strcmp0(tmp, "in") == 0)
+ {
+ return l_insert_token(lstate, T_IN);
+ }
+ if(l_check_if_function(lstate))
+ {
+ return l_insert_token(lstate, T_FUNCTION);
+ }
+ else
+ {
+ return l_insert_token(lstate, T_VARIABLE);
+ }
+ }
+ else
+ {
+ pl_roll_back(state);
+ tmp = g_ascii_strdown(pl_get_marked_substring(state), -1);
+ if(g_strcmp0(tmp, "mod") == 0)
+ {
+ return l_insert_token(lstate, T_MOD);
+ }
+ if(g_strcmp0(tmp, "and") == 0)
+ {
+ return l_insert_token(lstate, T_AND);
+ }
+ if(g_strcmp0(tmp, "or") == 0)
+ {
+ return l_insert_token(lstate, T_OR);
+ }
+ if(g_strcmp0(tmp, "xor") == 0)
+ {
+ return l_insert_token(lstate, T_XOR);
+ }
+ if(g_strcmp0(tmp, "not") == 0)
+ {
+ return l_insert_token(lstate, T_NOT);
+ }
+ if(g_strcmp0(tmp, "in") == 0)
+ {
+ return l_insert_token(lstate, T_IN);
+ }
+ if(l_check_if_function(lstate))
+ {
+ return l_insert_token(lstate, T_FUNCTION);
+ }
+ else
+ {
+ return l_insert_token(lstate, T_VARIABLE);
+ }
+ }
+ }
+ }
+ if(type == PL_EOS)
+ {
+ return l_insert_token(lstate, PL_EOS);
+ }
+ /* ERROR: Unexpected token.. X( */
+ set_error(lstate->parent, PARSER_ERR_INVALID, tmp = pl_get_marked_substring(state));
+ free(tmp);
+ return l_insert_token(lstate, T_UNKNOWN);
+}
+
+/* Call l_insert_next_token() as many times as needed to completely tokenize the string. */
+void
+l_insert_all_tokens(LexerState* state)
+{
+ LexerToken* token;
+ while(1)
+ {
+ token = l_insert_next_token(state);
+ assert(token != NULL);
+ if(token->token_type == PL_EOS)
+ {
+ break;
+ }
+ }
+}
+
+/* Create a lexer state from given input string. This will take care of pre-lexer state. */
+LexerState*
+l_create_lexer(const gchar* input, struct parser_state* parent)
+{
+ LexerState* ret;
+ ret = (LexerState *) malloc(sizeof(LexerState));
+ assert(ret != NULL);
+ ret->prelexer = pl_create_scanner(input);
+ ret->tokens = NULL;
+ ret->token_count = 0;
+ ret->next_token = 0;
+ ret->parent = parent;
+ return ret;
+}
+
+/* Destroy lexer state and free memory. */
+void
+l_destroy_lexer(LexerState* state)
+{
+ int l;
+ pl_destroy_scanner(state->prelexer);
+ for(l = 0; l < state->token_count; l++)
+ {
+ free(state->tokens[l].string);
+ }
+ free(state->tokens);
+ free(state);
+}
+
+/* Get next token interface. Will be called by parser to get pointer to next token in token stream. */
+LexerToken*
+l_get_next_token(LexerState* state)
+{
+ /* Return PL_EOS token after token stream reaches to its end. */
+ if(state->next_token >= state->token_count)
+ return &state->tokens[state->token_count - 1];
+ return &state->tokens[state->next_token++];
+}
+
+/* Roll back one lexer token. */
+void
+l_roll_back(LexerState* state)
+{
+ if(state->next_token > 0)
+ state->next_token--;
+}
diff --git a/src/lexer.h b/src/lexer.h
new file mode 100644
index 0000000..2fd7fa7
--- /dev/null
+++ b/src/lexer.h
@@ -0,0 +1,40 @@
+#ifndef LEXER_H
+#define LEXER_H
+
+#include "prelexer.h"
+
+/* Structure to hold single token. */
+typedef struct
+{
+ gchar* string; /* Poniter to local copy of token string. */
+ guint start_index; /* Start index in original stream. */
+ guint end_index; /* End index in original stream. */
+ LexerTokenType token_type; /* Type of token. */
+} LexerToken;
+
+/* Structure to hold lexer state and all the tokens. */
+typedef struct
+{
+ PreLexerState *prelexer; /* Pre-lexer state. Pre-lexer is part of lexer. */
+ LexerToken *tokens; /* Pointer to the dynamic array of LexerTokens. */
+ guint token_count; /* Count of tokens in array. */
+ guint next_token; /* Index of next, to be sent, token. */
+ struct parser_state *parent; /* Pointer to the parent parser. */
+} LexerState;
+
+/* Create a new LexerState object and fill the dynamic array with tokens. */
+LexerState* l_create_lexer(const gchar*, struct parser_state*);
+
+/* Destroy LexerState object and free up space. */
+void l_destroy_lexer(LexerState*);
+
+/* Tokanize complete string. */
+void l_insert_all_tokens(LexerState*);
+
+/* Return next, to be sent, token. */
+LexerToken* l_get_next_token(LexerState*);
+
+/* Roll back one token. */
+void l_roll_back(LexerState*);
+
+#endif /* LEXER_H */
diff --git a/src/mate-calc-cmd.c b/src/mate-calc-cmd.c
index 796941b..e65ac5a 100644
--- a/src/mate-calc-cmd.c
+++ b/src/mate-calc-cmd.c
@@ -1,21 +1,11 @@
-/* $Header$
+/*
+ * Copyright (C) 2009 Rich Burridge
*
- * Copyright (c) 2009 Rich Burridge
- *
- * 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 <stdio.h>
@@ -23,34 +13,39 @@
#include <string.h>
#include <sys/types.h>
#include <time.h>
+#include <locale.h>
#include "mp-equation.h"
+#include "mp-serializer.h"
#define MAXLINE 1024
+static MpSerializer *result_serializer;
+
static void
solve(const char *equation)
{
int ret;
MPEquationOptions options;
MPNumber z;
- char result_str[MAXLINE];
-
+ gchar *result_str = NULL;
+
memset(&options, 0, sizeof(options));
options.base = 10;
options.wordlen = 32;
options.angle_units = MP_DEGREES;
-
+
ret = mp_equation_parse(equation, &options, &z, NULL);
if (ret == PARSER_ERR_MP)
fprintf(stderr, "Error %s\n", mp_get_error());
- else if (ret)
+ else if (ret)
fprintf(stderr, "Error %d\n", ret);
else {
- mp_cast_to_string(&z, 10, 10, 9, 1, result_str, MAXLINE);
+ result_str = mp_serializer_to_string(result_serializer, &z);
printf("%s\n", result_str);
}
+ g_free(result_str);
}
@@ -79,6 +74,11 @@ main(int argc, char **argv)
/* Seed random number generator. */
srand48((long) time((time_t *) 0));
+ g_type_init ();
+ setlocale(LC_ALL, "");
+
+ result_serializer = mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC, 10, 9);
+
equation = (char *) malloc(MAXLINE * sizeof(char));
while (1) {
printf("> ");
diff --git a/src/mate-calc.c b/src/mate-calc.c
index c5439af..15efc97 100644
--- a/src/mate-calc.c
+++ b/src/mate-calc.c
@@ -1,34 +1,29 @@
-/* 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>
#include <stdio.h>
#include <string.h>
+#include <locale.h>
+#include <glib/gi18n.h>
-#include "currency.h"
-#include "unittest.h"
#include "math-window.h"
+#include "math-preferences.h"
#include "mp-equation.h"
+#include "unit-manager.h"
static GSettings *settings = NULL;
static MathWindow *window;
+static MathPreferencesDialog *preferences_dialog;
static void
version(const gchar *progname)
@@ -38,18 +33,26 @@ version(const gchar *progname)
}
+static int
+do_convert(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data)
+{
+ return unit_manager_convert_by_symbol(unit_manager_get_default(), x, x_units, z_units, z);
+}
+
+
static void
solve(const char *equation)
{
MPEquationOptions options;
MPErrorCode error;
MPNumber result;
- char result_str[1024];
+ char *result_str;
memset(&options, 0, sizeof(options));
options.base = 10;
options.wordlen = 32;
options.angle_units = MP_DEGREES;
+ options.convert = do_convert;
error = mp_equation_parse(equation, &options, &result, NULL);
if(error == PARSER_ERR_MP) {
@@ -61,7 +64,7 @@ solve(const char *equation)
exit(1);
}
else {
- mp_cast_to_string(&result, 10, 10, 9, 1, result_str, 1024);
+ result_str = mp_serializer_to_string(mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC, 10, 9), &result);
printf("%s\n", result_str);
exit(0);
}
@@ -107,7 +110,6 @@ usage(const gchar *progname, gboolean show_application, gboolean show_gtk)
fprintf(stderr,
/* Description on mate-calc application options displayed on command-line */
_("Application Options:\n"
- " -u, --unittest Perform unit tests\n"
" -s, --solve <equation> Solve the given equation"));
fprintf(stderr,
"\n\n");
@@ -158,10 +160,6 @@ get_options(int argc, char *argv[])
else
solve(argv[i]);
}
- else if (strcmp(arg, "-u") == 0 ||
- strcmp(arg, "--unittest") == 0) {
- unittest();
- }
else {
fprintf(stderr,
/* Error printed to stderr when user provides an unknown command-line argument */
@@ -175,52 +173,291 @@ get_options(int argc, char *argv[])
static void
-quit_cb(MathWindow *window)
+accuracy_cb(MathEquation *equation, GParamSpec *spec)
{
- MathEquation *equation;
- MathButtons *buttons;
+ g_settings_set_int(settings, "accuracy", math_equation_get_accuracy(equation));
+}
- equation = math_window_get_equation(window);
- buttons = math_window_get_buttons(window);
- g_settings_set_int(settings, "accuracy", math_equation_get_accuracy(equation));
+static void
+word_size_cb(MathEquation *equation, GParamSpec *spec)
+{
g_settings_set_int(settings, "word-size", math_equation_get_word_size(equation));
- g_settings_set_int(settings, "base", math_buttons_get_programming_base(buttons));
+}
+
+
+static void
+show_thousands_separators_cb(MathEquation *equation, GParamSpec *spec)
+{
g_settings_set_boolean(settings, "show-thousands", math_equation_get_show_thousands_separators(equation));
+}
+
+
+static void
+show_trailing_zeroes_cb(MathEquation *equation, GParamSpec *spec)
+{
g_settings_set_boolean(settings, "show-zeroes", math_equation_get_show_trailing_zeroes(equation));
+}
+
+
+static void
+number_format_cb(MathEquation *equation, GParamSpec *spec)
+{
g_settings_set_enum(settings, "number-format", math_equation_get_number_format(equation));
+}
+
+
+static void
+angle_unit_cb(MathEquation *equation, GParamSpec *spec)
+{
g_settings_set_enum(settings, "angle-units", math_equation_get_angle_units(equation));
- g_settings_set_enum(settings, "button-mode", math_buttons_get_mode(buttons));
+}
+
+
+static void
+source_currency_cb(MathEquation *equation, GParamSpec *spec)
+{
g_settings_set_string(settings, "source-currency", math_equation_get_source_currency(equation));
+}
+
+
+static void
+target_currency_cb(MathEquation *equation, GParamSpec *spec)
+{
g_settings_set_string(settings, "target-currency", math_equation_get_target_currency(equation));
- g_settings_sync();
+}
+
+
+static void
+source_units_cb(MathEquation *equation, GParamSpec *spec)
+{
+ g_settings_set_string(settings, "source-units", math_equation_get_source_units(equation));
+}
- currency_free_resources();
- gtk_main_quit();
+
+static void
+target_units_cb(MathEquation *equation, GParamSpec *spec)
+{
+ g_settings_set_string(settings, "target-units", math_equation_get_target_units(equation));
}
-int
-main(int argc, char **argv)
+static void
+programming_base_cb(MathButtons *buttons, GParamSpec *spec)
+{
+ g_settings_set_int(settings, "base", math_buttons_get_programming_base(buttons));
+}
+
+
+static void
+mode_cb(MathButtons *buttons, GParamSpec *spec, GApplication *app)
+{
+ const char *state;
+ GAction *action;
+
+ g_settings_set_enum(settings, "button-mode", math_buttons_get_mode(buttons));
+
+ switch(math_buttons_get_mode(buttons))
+ {
+ default:
+ case BASIC:
+ state = "basic";
+ //FIXME: Should it revert to decimal mode? math_equation_set_number_format(window->priv->equation, DEC);
+ break;
+
+ case ADVANCED:
+ state = "advanced";
+ break;
+
+ case FINANCIAL:
+ state = "financial";
+ break;
+
+ case PROGRAMMING:
+ state = "programming";
+ break;
+ }
+
+ action = g_action_map_lookup_action(G_ACTION_MAP(app), "mode");
+ g_simple_action_set_state(G_SIMPLE_ACTION(action),
+ g_variant_new_string(state));
+}
+
+
+static void
+copy_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ math_equation_copy(math_window_get_equation(window));
+}
+
+
+static void
+paste_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ math_equation_paste(math_window_get_equation(window));
+}
+
+
+static void
+undo_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ math_equation_undo(math_window_get_equation(window));
+}
+
+
+static void
+redo_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ math_equation_redo(math_window_get_equation(window));
+}
+
+
+static void
+mode_changed_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ const char *mode_str;
+ int mode = BASIC;
+
+ mode_str = g_variant_get_string(parameter, NULL);
+ if (strcmp(mode_str, "basic") == 0)
+ mode = BASIC;
+ else if (strcmp(mode_str, "advanced") == 0)
+ mode = ADVANCED;
+ else if (strcmp(mode_str, "financial") == 0)
+ mode = FINANCIAL;
+ else if (strcmp(mode_str, "programming") == 0)
+ mode = PROGRAMMING;
+ math_buttons_set_mode(math_window_get_buttons(window), mode);
+}
+
+
+static void
+show_preferences_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ if (!preferences_dialog) {
+ preferences_dialog = math_preferences_dialog_new(math_window_get_equation(window));
+ gtk_window_set_transient_for(GTK_WINDOW(preferences_dialog), GTK_WINDOW(window));
+ }
+ gtk_window_present(GTK_WINDOW(preferences_dialog));
+}
+
+
+static void
+help_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ GdkScreen *screen;
+ GError *error = NULL;
+
+ screen = gtk_widget_get_screen(GTK_WIDGET(window));
+ gtk_show_uri(screen, "help:mate-calc", gtk_get_current_event_time(), &error);
+
+ if (error != NULL)
+ {
+ GtkWidget *d;
+ /* Translators: Error message displayed when unable to launch help browser */
+ const char *message = _("Unable to open help file");
+
+ d = gtk_message_dialog_new(GTK_WINDOW (window),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
+ "%s", message);
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG (d),
+ "%s", error->message);
+ g_signal_connect(d, "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ gtk_window_present(GTK_WINDOW(d));
+
+ g_error_free(error);
+ }
+}
+
+
+static void
+about_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ const gchar *authors[] = {
+ "Rich Burridge <[email protected]>",
+ "Robert Ancell <[email protected]>",
+ "Klaus Niederkrüger <[email protected]>",
+ "Robin Sonefors <[email protected]>",
+ NULL
+ };
+ const gchar *documenters[] = {
+ "Sun Microsystems",
+ NULL
+ };
+
+ /* The translator credits. Please translate this with your name(s). */
+ const gchar *translator_credits = _("translator-credits");
+
+ /* The license this software is under (GPL2+) */
+ char *license = _("mate-calc is free software; you can redistribute it and/or modify\n"
+ "it under the terms of the GNU General Public License as published by\n"
+ "the Free Software Foundation; either version 2 of the License, or\n"
+ "(at your option) any later version.\n"
+ "\n"
+ "mate-calc is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "GNU General Public License for more details.\n"
+ "\n"
+ "You should have received a copy of the GNU General Public License\n"
+ "along with mate-calc; if not, write to the Free Software Foundation, Inc.,\n"
+ "151 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA");
+
+ gtk_show_about_dialog(GTK_WINDOW(window),
+ "name",
+ /* Program name in the about dialog */
+ _("mate-calc"),
+ "version", VERSION,
+ "copyright",
+ /* Copyright notice in the about dialog */
+ _("\xc2\xa9 1986–2010 The gcalctool authors\n 2011-2012 mate-calc authors"),
+ "license", license,
+ "comments",
+ /* Short description in the about dialog */
+ _("Calculator with financial and scientific modes."),
+ "authors", authors,
+ "documenters", documenters,
+ "translator_credits", translator_credits,
+ "logo-icon-name", "accessories-calculator",
+ "website", "http://mate-desktop.org",
+ NULL);
+}
+
+
+static void
+quit_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ gtk_widget_destroy(GTK_WIDGET(window));
+}
+
+
+static GActionEntry app_entries[] = {
+ { "copy", copy_cb, NULL, NULL, NULL },
+ { "paste", paste_cb, NULL, NULL, NULL },
+ { "undo", undo_cb, NULL, NULL, NULL },
+ { "redo", redo_cb, NULL, NULL, NULL },
+ { "mode", mode_changed_cb, "s", "\"basic\"", NULL },
+ { "preferences", show_preferences_cb, NULL, NULL, NULL },
+ { "help", help_cb, NULL, NULL, NULL },
+ { "about", about_cb, NULL, NULL, NULL },
+ { "quit", quit_cb, NULL, NULL, NULL },
+};
+
+
+static void
+startup_cb(GApplication *application)
{
MathEquation *equation;
+ MathButtons *buttons;
int accuracy = 9, word_size = 64, base = 10;
gboolean show_tsep = FALSE, show_zeroes = FALSE;
- DisplayFormat number_format;
+ MpDisplayFormat number_format;
MPAngleUnit angle_units;
ButtonMode button_mode;
gchar *source_currency, *target_currency;
-
- g_type_init();
-
- bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
- bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
- textdomain(GETTEXT_PACKAGE);
-
- /* Seed random number generator. */
- srand48((long) time((time_t *) 0));
-
- get_options(argc, argv);
+ gchar *source_units, *target_units;
+ GMenu *menu, *section;
settings = g_settings_new ("org.mate.calc");
accuracy = g_settings_get_int(settings, "accuracy");
@@ -233,6 +470,8 @@ main(int argc, char **argv)
button_mode = g_settings_get_enum(settings, "button-mode");
source_currency = g_settings_get_string(settings, "source-currency");
target_currency = g_settings_get_string(settings, "target-currency");
+ source_units = g_settings_get_string(settings, "source-units");
+ target_units = g_settings_get_string(settings, "target-units");
equation = math_equation_new();
math_equation_set_accuracy(equation, accuracy);
@@ -243,18 +482,98 @@ main(int argc, char **argv)
math_equation_set_angle_units(equation, angle_units);
math_equation_set_source_currency(equation, source_currency);
math_equation_set_target_currency(equation, target_currency);
+ math_equation_set_source_units(equation, source_units);
+ math_equation_set_target_units(equation, target_units);
g_free(source_currency);
g_free(target_currency);
+ g_free(source_units);
+ g_free(target_units);
+
+ g_signal_connect(equation, "notify::accuracy", G_CALLBACK(accuracy_cb), NULL);
+ g_signal_connect(equation, "notify::word-size", G_CALLBACK(word_size_cb), NULL);
+ g_signal_connect(equation, "notify::show-thousands-separators", G_CALLBACK(show_thousands_separators_cb), NULL);
+ g_signal_connect(equation, "notify::show-trailing-zeroes", G_CALLBACK(show_trailing_zeroes_cb), NULL);
+ g_signal_connect(equation, "notify::number-format", G_CALLBACK(number_format_cb), NULL);
+ g_signal_connect(equation, "notify::angle-units", G_CALLBACK(angle_unit_cb), NULL);
+ g_signal_connect(equation, "notify::source-currency", G_CALLBACK(source_currency_cb), NULL);
+ g_signal_connect(equation, "notify::target-currency", G_CALLBACK(target_currency_cb), NULL);
+ g_signal_connect(equation, "notify::source-units", G_CALLBACK(source_units_cb), NULL);
+ g_signal_connect(equation, "notify::target-units", G_CALLBACK(target_units_cb), NULL);
+
+ g_action_map_add_action_entries(G_ACTION_MAP(application), app_entries, G_N_ELEMENTS(app_entries), NULL);
+
+ window = math_window_new(GTK_APPLICATION(application), equation);
+ buttons = math_window_get_buttons(window);
+ math_buttons_set_programming_base(buttons, base);
+ math_buttons_set_mode(buttons, button_mode); // FIXME: We load the basic buttons even if we immediately switch to the next type
+ g_signal_connect(buttons, "notify::programming-base", G_CALLBACK(programming_base_cb), NULL);
+ g_signal_connect(buttons, "notify::mode", G_CALLBACK(mode_cb), application);
+ mode_cb (buttons, NULL, application);
+
+ menu = g_menu_new();
+
+ section = g_menu_new();
+ g_menu_append(section, _("Basic"), "app.mode::basic");
+ g_menu_append(section, _("Advanced"), "app.mode::advanced");
+ g_menu_append(section, _("Financial"), "app.mode::financial");
+ g_menu_append(section, _("Programming"), "app.mode::programming");
+ g_menu_append_section(menu, _("Mode"), G_MENU_MODEL(section));
+
+ section = g_menu_new();
+ g_menu_append(section, _("Preferences"), "app.preferences");
+ g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
+
+ section = g_menu_new();
+ g_menu_append(section, _("About Calculator"), "app.about");
+ g_menu_append(section, _("Help"), "app.help");
+ g_menu_append(section, _("Quit"), "app.quit");
+ g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
+
+ gtk_application_set_app_menu(GTK_APPLICATION(application), G_MENU_MODEL(menu));
+
+ gtk_application_add_accelerator(GTK_APPLICATION(application), "<control>Q", "app.quit", NULL);
+ gtk_application_add_accelerator(GTK_APPLICATION(application), "F1", "app.help", NULL);
+ gtk_application_add_accelerator(GTK_APPLICATION(application), "<control>C", "app.copy", NULL);
+ gtk_application_add_accelerator(GTK_APPLICATION(application), "<control>V", "app.paste", NULL);
+ gtk_application_add_accelerator(GTK_APPLICATION(application), "<control>Z", "app.undo", NULL);
+ gtk_application_add_accelerator(GTK_APPLICATION(application), "<control><shift>Z", "app.redo", NULL);
+}
+
+
+static void
+activate_cb(GApplication *application)
+{
+ gtk_window_present(GTK_WINDOW(window));
+}
+
+
+int
+main(int argc, char **argv)
+{
+ GtkApplication *app;
+ int status;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+ textdomain(GETTEXT_PACKAGE);
+
+ /* Seed random number generator. */
+ srand48((long) time((time_t *) 0));
+
+ g_type_init();
+
+ get_options(argc, argv);
gtk_init(&argc, &argv);
- window = math_window_new(equation);
- g_signal_connect(G_OBJECT(window), "quit", G_CALLBACK(quit_cb), NULL);
- math_buttons_set_programming_base(math_window_get_buttons(window), base);
- math_buttons_set_mode(math_window_get_buttons(window), button_mode); // FIXME: We load the basic buttons even if we immediately switch to the next type
+ gtk_window_set_default_icon_name("accessories-calculator");
+
+ app = gtk_application_new(NULL, G_APPLICATION_NON_UNIQUE);
+ g_signal_connect(app, "startup", G_CALLBACK(startup_cb), NULL);
+ g_signal_connect(app, "activate", G_CALLBACK(activate_cb), NULL);
- gtk_widget_show(GTK_WIDGET(window));
- gtk_main();
+ status = g_application_run(G_APPLICATION(app), argc, argv);
- return(0);
+ return status;
}
diff --git a/src/math-buttons.c b/src/math-buttons.c
index d33422b..cd28ee7 100644
--- a/src/math-buttons.c
+++ b/src/math-buttons.c
@@ -1,31 +1,26 @@
-/* Copyright (c) 2008-2009 Robert Ancell
+/*
+ * 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 <glib/gi18n.h>
#include "math-buttons.h"
+#include "math-converter.h"
+#include "math-variable-popup.h"
#include "financial.h"
-#include "currency.h"
+#include "mp-serializer.h"
enum {
PROP_0,
PROP_EQUATION,
- PROP_MODE
+ PROP_MODE,
+ PROP_PROGRAMMING_BASE
};
static GType button_mode_type;
@@ -39,9 +34,9 @@ struct MathButtonsPrivate
ButtonMode mode;
gint programming_base;
- GtkBuilder *basic_ui, *advanced_ui, *financial_ui, *programming_ui;
+ MathConverter *converter;
- GdkColor color_numbers, color_action, color_operator, color_function, color_memory, color_group;
+ GtkBuilder *basic_ui, *advanced_ui, *financial_ui, *programming_ui;
GtkWidget *bas_panel, *adv_panel, *fin_panel, *prog_panel;
GtkWidget *active_panel;
@@ -53,18 +48,11 @@ struct MathButtonsPrivate
GList *superscript_toggles;
GList *subscript_toggles;
- GtkWidget *angle_combo;
- GtkWidget *angle_label;
-
GtkWidget *base_combo;
GtkWidget *base_label;
GtkWidget *bit_panel;
GtkWidget *bit_labels[MAXBITS];
- GtkWidget *source_currency_combo;
- GtkWidget *target_currency_combo;
- GtkWidget *currency_label;
-
GtkWidget *character_code_dialog;
GtkWidget *character_code_entry;
};
@@ -178,15 +166,6 @@ static ButtonData button_data[] = {
{"tangent", "tan ", FUNCTION,
/* Tooltip for the tangent button */
N_("Tangent")},
- {"inverse_sine", "asin", FUNCTION,
- /* Tooltip for the inverse sine button */
- N_("Inverse Sine")},
- {"inverse_cosine", "acos", FUNCTION,
- /* Tooltip for the inverse cosine button */
- N_("Inverse Cosine")},
- {"inverse_tangent", "atan", FUNCTION,
- /* Tooltip for the inverse tangent button */
- N_("Inverse Tangent")},
{"hyperbolic_sine", "sinh ", FUNCTION,
/* Tooltip for the hyperbolic sine button */
N_("Hyperbolic Sine")},
@@ -224,11 +203,11 @@ static ButtonData button_data[] = {
/* Tooltip for the imaginary component button */
N_("Imaginary Component")},
{"ones_complement", "ones ", FUNCTION,
- /* Tooltip for the ones complement button */
- N_("Ones Complement")},
+ /* Tooltip for the ones' complement button */
+ N_("Ones' Complement")},
{"twos_complement", "twos ", FUNCTION,
- /* Tooltip for the twos complement button */
- N_("Twos Complement")},
+ /* Tooltip for the two's complement button */
+ N_("Two's Complement")},
{"trunc", "trunc ", FUNCTION,
/* Tooltip for the truncate button */
N_("Truncate")},
@@ -238,12 +217,9 @@ static ButtonData button_data[] = {
{"end_group", ")", GROUP,
/* Tooltip for the end group button */
N_("End Group [)]")},
- {"store", NULL, MEMORY,
- /* Tooltip for the assign variable button */
- N_("Assign Variable")},
- {"recall", NULL, MEMORY,
- /* Tooltip for the insert variable button */
- N_("Insert Variable")},
+ {"memory", NULL, MEMORY,
+ /* Tooltip for the memory button */
+ N_("Memory")},
{"character", NULL, MEMORY,
/* Tooltip for the insert character code button */
N_("Insert Character Code")},
@@ -261,10 +237,10 @@ static ButtonData button_data[] = {
N_("Undo [Ctrl+Z]")},
{"shift_left", NULL, ACTION,
/* Tooltip for the shift left button */
- N_("Shift Left [<<]")},
+ N_("Shift Left")},
{"shift_right", NULL, ACTION,
/* Tooltip for the shift right button */
- N_("Shift Right [>>]")},
+ N_("Shift Right")},
{"finc_compounding_term", NULL, FUNCTION,
/* Tooltip for the compounding term button */
N_("Compounding Term")},
@@ -298,11 +274,6 @@ static ButtonData button_data[] = {
{NULL, NULL, 0, NULL}
};
-typedef enum {
- CURRENCY_TARGET_UPPER,
- CURRENCY_TARGET_LOWER
-} CurrencyTargetRow;
-
/* The names of each field in the dialogs for the financial functions */
static char *finc_dialog_fields[][5] = {
{"ctrm_pint", "ctrm_fv", "ctrm_pv", NULL, NULL},
@@ -322,37 +293,9 @@ static char *finc_dialog_fields[][5] = {
MathButtons *
math_buttons_new(MathEquation *equation)
{
- return g_object_new (math_buttons_get_type(), "equation", equation, NULL);
-}
-
-
-static void
-set_tint (GtkWidget *widget, GdkColor *tint, gint alpha)
-{
- // hell no. It's a calculator, not a rainbow
- return;
-
- GtkStyle *style;
- int j;
-
- if (!widget)
- return;
-
- gtk_widget_ensure_style(widget);
- style = gtk_widget_get_style(widget);
-
- for (j = 0; j < 5; j++) {
- GdkColor color;
-
- color.red = (style->bg[j].red * (10 - alpha) + tint->red * alpha) / 10;
- color.green = (style->bg[j].green * (10 - alpha) + tint->green * alpha) / 10;
- color.blue = (style->bg[j].blue * (10 - alpha) + tint->blue * alpha) / 10;
- gdk_colormap_alloc_color(gdk_colormap_get_system(), &color, FALSE, TRUE);
- gtk_widget_modify_bg(widget, j, &color);
- }
+ return g_object_new(math_buttons_get_type(), "equation", equation, NULL);
}
-
static void
set_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, const char *value)
{
@@ -366,7 +309,7 @@ set_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, const char
static void
set_int_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, gint value)
{
- GObject *object;
+ GObject *object;
object = gtk_builder_get_object(ui, object_name);
if (object)
g_object_set_data(object, name, GINT_TO_POINTER(value));
@@ -392,9 +335,7 @@ load_finc_dialogs(MathButtons *buttons)
for (i = 0; finc_dialog_fields[i][0] != NULL; i++) {
for (j = 0; finc_dialog_fields[i][j]; j++) {
GObject *o;
- o = gtk_builder_get_object(buttons->priv->financial_ui, finc_dialog_fields[i][j]);
- if(!o)
- printf("missing '%s'\n", finc_dialog_fields[i][j]);
+ o = gtk_builder_get_object (buttons->priv->financial_ui, finc_dialog_fields[i][j]);
g_object_set_data(o, "finc_field", GINT_TO_POINTER(j));
g_object_set_data(o, "finc_dialog", GINT_TO_POINTER(i));
}
@@ -403,71 +344,6 @@ load_finc_dialogs(MathButtons *buttons)
static void
-update_angle_label (MathButtons *buttons)
-{
- MPNumber x;
- MPNumber pi, max_value, min_value, fraction, input, output;
- char *label, input_text[1024], output_text[1024];
-
- if (!buttons->priv->angle_label)
- return;
-
- if (!math_equation_get_number(buttons->priv->equation, &x))
- return;
-
- mp_get_pi(&pi);
- switch (math_equation_get_angle_units(buttons->priv->equation)) {
- default:
- case MP_DEGREES:
- label = g_strdup("");
- break;
- case MP_RADIANS:
- /* Clip to the range ±2π */
- mp_multiply_integer(&pi, 2, &max_value);
- mp_invert_sign(&max_value, &min_value);
- if (!mp_is_equal(&x, &max_value) && !mp_is_equal(&x, &min_value)) {
- mp_divide(&x, &max_value, &fraction);
- mp_fractional_component(&fraction, &fraction);
- mp_multiply(&fraction, &max_value, &input);
- }
- else {
- mp_set_from_mp(&x, &input);
- mp_set_from_integer(mp_is_negative(&input) ? -1 : 1, &fraction);
- }
- mp_cast_to_string(&input, 10, 10, 2, false, input_text, 1024);
-
- mp_multiply_integer(&fraction, 360, &output);
- mp_cast_to_string(&output, 10, 10, 2, false, output_text, 1024);
- label = g_strdup_printf(_("%s radians = %s degrees"), input_text, output_text);
- break;
- case MP_GRADIANS:
- /* Clip to the range ±400 */
- mp_set_from_integer(400, &max_value);
- mp_invert_sign(&max_value, &min_value);
- if (!mp_is_equal(&x, &max_value) && !mp_is_equal(&x, &min_value)) {
- mp_divide(&x, &max_value, &fraction);
- mp_fractional_component(&fraction, &fraction);
- mp_multiply(&fraction, &max_value, &input);
- }
- else {
- mp_set_from_mp(&x, &input);
- mp_set_from_integer(mp_is_negative(&input) ? -1 : 1, &fraction);
- }
-
- mp_cast_to_string(&input, 10, 10, 2, false, input_text, 1024);
-
- mp_multiply_integer(&fraction, 360, &output);
- mp_cast_to_string(&output, 10, 10, 2, false, output_text, 1024);
- label = g_strdup_printf(_("%s gradians = %s degrees"), input_text, output_text);
- break;
- }
-
- gtk_label_set_text(GTK_LABEL(buttons->priv->angle_label), label);
- g_free(label);
-}
-
-
-static void
update_bit_panel(MathButtons *buttons)
{
MPNumber x;
@@ -479,7 +355,7 @@ update_bit_panel(MathButtons *buttons)
if (!buttons->priv->bit_panel)
return;
-
+
enabled = math_equation_get_number(buttons->priv->equation, &x);
if (enabled) {
@@ -495,7 +371,7 @@ update_bit_panel(MathButtons *buttons)
gtk_widget_set_sensitive(buttons->priv->bit_panel, enabled);
gtk_widget_set_sensitive(buttons->priv->base_label, enabled);
-
+
if (!enabled)
return;
@@ -509,24 +385,24 @@ update_bit_panel(MathButtons *buttons)
gtk_label_set_text(GTK_LABEL(buttons->priv->bit_labels[i]), label);
}
- base = math_equation_get_base(buttons->priv->equation);
+ base = math_equation_get_base(buttons->priv->equation);
label = g_string_new("");
if (base != 8) {
if (label->len != 0)
g_string_append(label, " = ");
- g_string_append_printf(label, "%lo", bits);
+ g_string_append_printf(label, "%" G_GINT64_MODIFIER "o", bits);
g_string_append(label, "₈");
}
if (base != 10) {
if (label->len != 0)
g_string_append(label, " = ");
- g_string_append_printf(label, "%lu", bits);
+ g_string_append_printf(label, "%" G_GINT64_MODIFIER "u", bits);
g_string_append(label, "₁₀");
}
if (base != 16) {
if (label->len != 0)
g_string_append(label, " = ");
- g_string_append_printf(label, "%lX", bits);
+ g_string_append_printf(label, "%" G_GINT64_MODIFIER "X", bits);
g_string_append(label, "₁₆");
}
@@ -536,97 +412,13 @@ update_bit_panel(MathButtons *buttons)
static void
-update_currency_label(MathButtons *buttons)
-{
- MPNumber x, value;
- char *label;
-
- if (!buttons->priv->currency_label)
- return;
-
- if (!math_equation_get_number(buttons->priv->equation, &x))
- return;
-
- if (currency_convert(&x,
- math_equation_get_source_currency(buttons->priv->equation),
- math_equation_get_target_currency(buttons->priv->equation),
- &value)) {
- char input_text[1024], output_text[1024];
- const char *source_symbol, *target_symbol;
- int i;
-
- mp_cast_to_string(&x, 10, 10, 2, false, input_text, 1024);
- mp_cast_to_string(&value, 10, 10, 2, false, output_text, 1024);
-
- for (i = 0; strcmp(math_equation_get_source_currency(buttons->priv->equation), currency_names[i].short_name) != 0; i++);
- source_symbol = currency_names[i].symbol;
- for (i = 0; strcmp(math_equation_get_target_currency(buttons->priv->equation), currency_names[i].short_name) != 0; i++);
- target_symbol = currency_names[i].symbol;
-
- /* Translators: first and third %s are currency symbols, second
- * and fourth are amounts in these currencies, you may want to change
- * the order of these, example: $100 = €100 */
- label = g_strdup_printf(_("%s%s = %s%s"),
- source_symbol, input_text,
- target_symbol, output_text);
- }
- else
- label = g_strdup("");
-
- gtk_label_set_text(GTK_LABEL(buttons->priv->currency_label), label);
- g_free(label);
-}
-
-
-static void
display_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
{
- update_angle_label(buttons);
- update_currency_label(buttons);
update_bit_panel(buttons);
}
static void
-angle_unit_combobox_changed_cb(GtkWidget *combo, MathButtons *buttons)
-{
- MPAngleUnit value;
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
- gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
- gtk_tree_model_get(model, &iter, 1, &value, -1);
- math_equation_set_angle_units(buttons->priv->equation, value);
-}
-
-
-static void
-angle_unit_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
-{
- GtkTreeModel *model;
- GtkTreeIter iter;
- gboolean valid;
-
- model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->angle_combo));
- valid = gtk_tree_model_get_iter_first(model, &iter);
-
- while (valid) {
- gint v;
-
- gtk_tree_model_get(model, &iter, 1, &v, -1);
- if (v == math_equation_get_angle_units(buttons->priv->equation))
- break;
- valid = gtk_tree_model_iter_next(model, &iter);
- }
- if (!valid)
- valid = gtk_tree_model_get_iter_first(model, &iter);
-
- gtk_combo_box_set_active_iter(GTK_COMBO_BOX(buttons->priv->angle_combo), &iter);
-}
-
-
-static void
base_combobox_changed_cb(GtkWidget *combo, MathButtons *buttons)
{
gint value;
@@ -637,7 +429,7 @@ base_combobox_changed_cb(GtkWidget *combo, MathButtons *buttons)
gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
gtk_tree_model_get(model, &iter, 1, &value, -1);
- math_equation_set_base(buttons->priv->equation, value);
+ math_buttons_set_programming_base(buttons, value);
}
@@ -647,7 +439,7 @@ base_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
-
+
if (buttons->priv->mode != PROGRAMMING)
return;
@@ -670,102 +462,6 @@ base_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
}
-static void
-source_currency_combo_changed_cb(GtkWidget *combo, MathButtons *buttons)
-{
- gchar *value;
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
- gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
- gtk_tree_model_get(model, &iter, 0, &value, -1);
-
- math_equation_set_source_currency(buttons->priv->equation, value);
- g_free (value);
-}
-
-
-static void
-source_currency_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
-{
- GtkTreeModel *model;
- GtkTreeIter iter;
- gboolean valid;
-
- if (buttons->priv->mode != FINANCIAL)
- return;
-
- model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->source_currency_combo));
- valid = gtk_tree_model_get_iter_first(model, &iter);
-
- while (valid) {
- gchar *v;
- gboolean matched;
-
- gtk_tree_model_get(model, &iter, 0, &v, -1);
- matched = strcmp (math_equation_get_source_currency(buttons->priv->equation), v) == 0;
- g_free (v);
- if (matched)
- break;
- valid = gtk_tree_model_iter_next(model, &iter);
- }
- if (!valid)
- valid = gtk_tree_model_get_iter_first(model, &iter);
-
- gtk_combo_box_set_active_iter(GTK_COMBO_BOX(buttons->priv->source_currency_combo), &iter);
- update_currency_label(buttons);
-}
-
-
-static void
-target_currency_combo_changed_cb(GtkWidget *combo, MathButtons *buttons)
-{
- gchar *value;
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
- gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
- gtk_tree_model_get(model, &iter, 0, &value, -1);
-
- math_equation_set_target_currency(buttons->priv->equation, value);
- g_free (value);
-}
-
-
-static void
-target_currency_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
-{
- GtkTreeModel *model;
- GtkTreeIter iter;
- gboolean valid;
-
- if (buttons->priv->mode != FINANCIAL)
- return;
-
- model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->target_currency_combo));
- valid = gtk_tree_model_get_iter_first(model, &iter);
-
- while (valid) {
- gchar *v;
- gboolean matched;
-
- gtk_tree_model_get(model, &iter, 0, &v, -1);
- matched = strcmp (math_equation_get_target_currency(buttons->priv->equation), v) == 0;
- g_free (v);
- if (matched)
- break;
- valid = gtk_tree_model_iter_next(model, &iter);
- }
- if (!valid)
- valid = gtk_tree_model_get_iter_first(model, &iter);
-
- gtk_combo_box_set_active_iter(GTK_COMBO_BOX(buttons->priv->target_currency_combo), &iter);
- update_currency_label(buttons);
-}
-
-
static GtkWidget *
load_mode(MathButtons *buttons, ButtonMode mode)
{
@@ -781,6 +477,7 @@ load_mode(MathButtons *buttons, ButtonMode mode)
GError *error = NULL;
switch (mode) {
+ default:
case BASIC:
builder_ptr = &buttons->priv->basic_ui;
builder_file = UI_BASIC_FILE;
@@ -802,7 +499,7 @@ load_mode(MathButtons *buttons, ButtonMode mode)
panel = &buttons->priv->prog_panel;
break;
}
-
+
if (*panel)
return *panel;
@@ -814,7 +511,7 @@ load_mode(MathButtons *buttons, ButtonMode mode)
g_clear_error(&error);
}
*panel = GET_WIDGET(builder, "button_panel");
- gtk_box_pack_end(GTK_BOX(buttons), *panel, FALSE, TRUE, 0);
+ gtk_box_pack_end(GTK_BOX(buttons), *panel, TRUE, TRUE, 0);
/* Configure buttons */
for (i = 0; button_data[i].widget_name != NULL; i++) {
@@ -833,32 +530,8 @@ load_mode(MathButtons *buttons, ButtonMode mode)
if (button_data[i].tooltip)
gtk_widget_set_tooltip_text(button, _(button_data[i].tooltip));
-
- atk_object_set_name (gtk_widget_get_accessible (button), button_data[i].widget_name);
-
- switch (button_data[i].class) {
- case NUMBER:
- set_tint(button, &buttons->priv->color_numbers, 1);
- break;
- case NUMBER_BOLD:
- set_tint(button, &buttons->priv->color_numbers, 2);
- break;
- case OPERATOR:
- set_tint(button, &buttons->priv->color_operator, 1);
- break;
- case FUNCTION:
- set_tint(button, &buttons->priv->color_function, 1);
- break;
- case MEMORY:
- set_tint(button, &buttons->priv->color_memory, 1);
- break;
- case GROUP:
- set_tint(button, &buttons->priv->color_group, 1);
- break;
- case ACTION:
- set_tint(button, &buttons->priv->color_action, 2);
- break;
- }
+
+ atk_object_set_name(gtk_widget_get_accessible(button), button_data[i].widget_name);
}
/* Set special button data */
@@ -868,16 +541,26 @@ load_mode(MathButtons *buttons, ButtonMode mode)
name = g_strdup_printf("calc_%d_button", i);
button = GET_WIDGET(builder, name);
if (button) {
+ gchar buffer[7];
+ gint len;
+
g_object_set_data(G_OBJECT(button), "calc_digit", GINT_TO_POINTER(i));
- set_tint(button, &buttons->priv->color_numbers, 1);
- gtk_button_set_label(GTK_BUTTON(button), math_equation_get_digit_text(buttons->priv->equation, i));
+ len = g_unichar_to_utf8(math_equation_get_digit_text(buttons->priv->equation, i), buffer);
+ buffer[len] = '\0';
+ gtk_button_set_label(GTK_BUTTON(button), buffer);
}
g_free(name);
}
widget = GET_WIDGET(builder, "calc_numeric_point_button");
- if (widget)
- gtk_button_set_label(GTK_BUTTON(widget), math_equation_get_numeric_point_text(buttons->priv->equation));
-
+ if (widget) {
+ MpSerializer *serializer = math_equation_get_serializer(buttons->priv->equation);
+ gchar buffer[7];
+ gint len;
+ len = g_unichar_to_utf8(mp_serializer_get_radix(serializer), buffer);
+ buffer[len] = '\0';
+ gtk_button_set_label(GTK_BUTTON(widget), buffer);
+ }
+
widget = GET_WIDGET(builder, "calc_superscript_button");
if (widget) {
buttons->priv->superscript_toggles = g_list_append(buttons->priv->superscript_toggles, widget);
@@ -891,37 +574,6 @@ load_mode(MathButtons *buttons, ButtonMode mode)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
}
- if (mode == ADVANCED) {
- GtkListStore *model;
- GtkTreeIter iter;
- GtkCellRenderer *renderer;
-
- buttons->priv->angle_label = GET_WIDGET(builder, "angle_label");
-
- buttons->priv->angle_combo = GET_WIDGET(builder, "angle_units_combo");
- model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
- gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->angle_combo), GTK_TREE_MODEL(model));
- gtk_list_store_append(GTK_LIST_STORE(model), &iter);
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
- /* Advanced buttons: Angle unit combo box: Use degrees for trigonometric calculations */
- _("Degrees"), 1, MP_DEGREES, -1);
- gtk_list_store_append(GTK_LIST_STORE(model), &iter);
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
- /* Advanced buttons: Angle unit combo box: Use radians for trigonometric calculations */
- _("Radians"), 1, MP_RADIANS, -1);
- gtk_list_store_append(GTK_LIST_STORE(model), &iter);
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
- /* Advanced buttons: Angle unit combo box: Use gradians for trigonometric calculations */
- _("Gradians"), 1, MP_GRADIANS, -1);
- renderer = gtk_cell_renderer_text_new();
- gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->angle_combo), renderer, TRUE);
- gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->angle_combo), renderer, "text", 0);
-
- g_signal_connect(buttons->priv->angle_combo, "changed", G_CALLBACK(angle_unit_combobox_changed_cb), buttons);
- g_signal_connect(buttons->priv->equation, "notify::angle-units", G_CALLBACK(angle_unit_cb), buttons);
- angle_unit_cb(buttons->priv->equation, NULL, buttons);
- }
-
if (mode == PROGRAMMING) {
GtkListStore *model;
GtkTreeIter iter;
@@ -970,41 +622,8 @@ load_mode(MathButtons *buttons, ButtonMode mode)
/* Setup financial functions */
if (mode == FINANCIAL) {
- GtkListStore *model;
- GtkCellRenderer *renderer;
-
load_finc_dialogs(buttons);
- buttons->priv->source_currency_combo = GET_WIDGET(builder, "source_currency_combo");
- buttons->priv->target_currency_combo = GET_WIDGET(builder, "target_currency_combo");
- buttons->priv->currency_label = GET_WIDGET(builder, "currency_label");
-
- model = gtk_list_store_new(1, G_TYPE_STRING);
-
- for (i = 0; currency_names[i].short_name != NULL; i++) {
- GtkTreeIter iter;
-
- gtk_list_store_append(GTK_LIST_STORE(model), &iter);
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, currency_names[i].short_name, -1);
- }
-
- gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->source_currency_combo), GTK_TREE_MODEL(model));
- gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->target_currency_combo), GTK_TREE_MODEL(model));
-
- renderer = gtk_cell_renderer_text_new();
- gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->source_currency_combo), renderer, TRUE);
- gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->source_currency_combo), renderer, "text", 0);
- renderer = gtk_cell_renderer_text_new();
- gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->target_currency_combo), renderer, TRUE);
- gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->target_currency_combo), renderer, "text", 0);
-
- g_signal_connect(buttons->priv->source_currency_combo, "changed", G_CALLBACK(source_currency_combo_changed_cb), buttons);
- g_signal_connect(buttons->priv->target_currency_combo, "changed", G_CALLBACK(target_currency_combo_changed_cb), buttons);
- g_signal_connect(buttons->priv->equation, "notify::source-currency", G_CALLBACK(source_currency_changed_cb), buttons);
- g_signal_connect(buttons->priv->equation, "notify::target-currency", G_CALLBACK(target_currency_changed_cb), buttons);
- source_currency_changed_cb(buttons->priv->equation, NULL, buttons);
- target_currency_changed_cb(buttons->priv->equation, NULL, buttons);
-
set_data(builder, "calc_finc_compounding_term_button", "finc_dialog", "ctrm_dialog");
set_data(builder, "calc_finc_double_declining_depreciation_button", "finc_dialog", "ddb_dialog");
set_data(builder, "calc_finc_future_value_button", "finc_dialog", "fv_dialog");
@@ -1020,11 +639,30 @@ load_mode(MathButtons *buttons, ButtonMode mode)
gtk_builder_connect_signals(builder, buttons);
display_changed_cb(buttons->priv->equation, NULL, buttons);
-
+
return *panel;
}
+static void
+converter_changed_cb(MathConverter *converter, MathButtons *buttons)
+{
+ Unit *from_unit, *to_unit;
+
+ math_converter_get_conversion(converter, &from_unit, &to_unit);
+ if (buttons->priv->mode == FINANCIAL) {
+ math_equation_set_source_currency(buttons->priv->equation, unit_get_name(from_unit));
+ math_equation_set_target_currency(buttons->priv->equation, unit_get_name(to_unit));
+ }
+ else {
+ math_equation_set_source_units(buttons->priv->equation, unit_get_name(from_unit));
+ math_equation_set_target_units(buttons->priv->equation, unit_get_name(to_unit));
+ }
+
+ g_object_unref(from_unit);
+ g_object_unref(to_unit);
+}
+
static void
load_buttons(MathButtons *buttons)
@@ -1034,6 +672,12 @@ load_buttons(MathButtons *buttons)
if (!gtk_widget_get_visible(GTK_WIDGET(buttons)))
return;
+ if (!buttons->priv->converter) {
+ buttons->priv->converter = math_converter_new(buttons->priv->equation);
+ g_signal_connect(buttons->priv->converter, "changed", G_CALLBACK(converter_changed_cb), buttons);
+ gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(buttons->priv->converter), FALSE, TRUE, 0);
+ }
+
panel = load_mode(buttons, buttons->priv->mode);
if (buttons->priv->active_panel == panel)
return;
@@ -1052,14 +696,13 @@ load_buttons(MathButtons *buttons)
void
math_buttons_set_mode(MathButtons *buttons, ButtonMode mode)
{
- ButtonMode old_mode;
-
+ g_return_if_fail(buttons != NULL);
+
if (buttons->priv->mode == mode)
return;
- old_mode = buttons->priv->mode;
buttons->priv->mode = mode;
-
+
if (mode == PROGRAMMING)
math_equation_set_base(buttons->priv->equation, buttons->priv->programming_base);
else
@@ -1067,6 +710,20 @@ math_buttons_set_mode(MathButtons *buttons, ButtonMode mode)
load_buttons(buttons);
+ gtk_widget_set_visible(GTK_WIDGET(buttons->priv->converter), mode == ADVANCED || mode == FINANCIAL);
+ if (mode == ADVANCED) {
+ math_converter_set_category(buttons->priv->converter, NULL);
+ math_converter_set_conversion(buttons->priv->converter,
+ math_equation_get_source_units(buttons->priv->equation),
+ math_equation_get_target_units(buttons->priv->equation));
+ }
+ else if (mode == FINANCIAL) {
+ math_converter_set_category(buttons->priv->converter, "currency");
+ math_converter_set_conversion(buttons->priv->converter,
+ math_equation_get_source_currency(buttons->priv->equation),
+ math_equation_get_target_currency(buttons->priv->equation));
+ }
+
g_object_notify(G_OBJECT(buttons), "mode");
}
@@ -1081,13 +738,23 @@ math_buttons_get_mode(MathButtons *buttons)
void
math_buttons_set_programming_base(MathButtons *buttons, gint base)
{
+ g_return_if_fail(buttons != NULL);
+
+ if (base == buttons->priv->programming_base)
+ return;
+
buttons->priv->programming_base = base;
+ g_object_notify(G_OBJECT(buttons), "programming-base");
+
+ if (buttons->priv->mode == PROGRAMMING)
+ math_equation_set_base(buttons->priv->equation, base);
}
gint
math_buttons_get_programming_base(MathButtons *buttons)
{
+ g_return_val_if_fail(buttons != NULL, 10);
return buttons->priv->programming_base;
}
@@ -1106,7 +773,7 @@ G_MODULE_EXPORT
void
subtract_cb(GtkWidget *widget, MathButtons *buttons)
{
- math_equation_insert_subtract(buttons->priv->equation);
+ math_equation_insert_subtract(buttons->priv->equation);
}
@@ -1170,7 +837,7 @@ button_menu_position_func(GtkMenu *menu, gint *x, gint *y,
GtkAllocation allocation;
GdkPoint loc;
gint border;
-
+
gdk_window_get_origin(gtk_widget_get_window(button), &loc.x, &loc.y);
border = gtk_container_get_border_width(GTK_CONTAINER(button));
gtk_widget_get_allocation(button, &allocation);
@@ -1187,161 +854,22 @@ popup_button_menu(GtkWidget *widget, GtkMenu *menu)
}
-static void
-save_variable_cb(GtkWidget *widget, MathButtons *buttons)
-{
- printf("save\n");
-}
-
-
-static void
-delete_variable_cb(GtkWidget *widget, MathButtons *buttons)
-{
- printf("delete\n");
-}
-
-
-static GtkWidget *
-make_register_menu_item(MathButtons *buttons, const gchar *name, const MPNumber *value, gboolean can_modify, GCallback callback)
-{
- gchar text[1024] = "", *mstr;
- GtkWidget *item, *label;
-
- if (value) {
- display_make_number(buttons->priv->equation, text, 1024, value);
- mstr = g_strdup_printf("<span weight=\"bold\">%s</span> = %s", name, text);
- }
- else
- mstr = g_strdup_printf("<span weight=\"bold\">%s</span>", name);
- label = gtk_label_new(mstr);
- gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
- gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
- g_free(mstr);
-
- item = gtk_menu_item_new();
-
- // FIXME: Buttons don't work inside menus...
- if (0) {//can_modify) {
- GtkWidget *hbox, *button;
-
- hbox = gtk_hbox_new(FALSE, 6);
- gtk_container_add(GTK_CONTAINER(item), hbox);
-
- gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
-
- button = gtk_button_new();
- gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU));
- gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
- gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0);
- g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(delete_variable_cb), buttons);
-
- button = gtk_button_new();
- gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU));
- gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
- gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0);
- g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(save_variable_cb), buttons);
- }
- else
- gtk_container_add(GTK_CONTAINER(item), label);
-
- g_object_set_data(G_OBJECT(item), "register_id", g_strdup(name)); // FIXME: Memory leak
- g_signal_connect(item, "activate", callback, buttons);
-
- return item;
-}
-
-
-static void
-store_menu_cb(GtkMenuItem *menu, MathButtons *buttons)
-{
- math_equation_store(buttons->priv->equation, g_object_get_data(G_OBJECT(menu), "register_id"));
-}
-
-
-void store_cb(GtkWidget *widget, MathButtons *buttons);
+void memory_cb(GtkWidget *widget, MathButtons *buttons);
G_MODULE_EXPORT
void
-store_cb(GtkWidget *widget, MathButtons *buttons)
+memory_cb(GtkWidget *widget, MathButtons *buttons)
{
- int i;
- GtkWidget *menu;
- GtkWidget *item;
- gchar **names;
-
- menu = gtk_menu_new();
- gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
- set_tint(menu, &buttons->priv->color_memory, 1);
-
- names = math_variables_get_names(math_equation_get_variables(buttons->priv->equation));
- if (names[0] == NULL) {
- item = gtk_menu_item_new_with_label(/* Text shown in store menu when no variables defined */
- _("No variables defined"));
- gtk_widget_set_sensitive(item, FALSE);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- }
- for (i = 0; names[i]; i++) {
- MPNumber *value;
- value = math_variables_get_value(math_equation_get_variables(buttons->priv->equation), names[i]);
- item = make_register_menu_item(buttons, names[i], value, TRUE, G_CALLBACK(store_menu_cb));
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- }
-
- g_strfreev(names);
-
- // FIXME
- //item = gtk_menu_item_new_with_label(_("Add variable"));
- //gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-
- gtk_widget_show_all(menu);
- popup_button_menu(widget, GTK_MENU(menu));
-}
-
-
-static void
-recall_menu_cb(GtkMenuItem *menu, MathButtons *buttons)
-{
- math_equation_recall(buttons->priv->equation, g_object_get_data(G_OBJECT(menu), "register_id"));
-}
-
-
-void recall_cb(GtkWidget *widget, MathButtons *buttons);
-G_MODULE_EXPORT
-void
-recall_cb(GtkWidget *widget, MathButtons *buttons)
-{
- int i;
- GtkWidget *menu;
- GtkWidget *item;
- gchar **names;
-
- menu = gtk_menu_new();
- gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
- set_tint(menu, &buttons->priv->color_memory, 1);
-
- names = math_variables_get_names(math_equation_get_variables(buttons->priv->equation));
- if (names[0] == NULL) {
- item = gtk_menu_item_new_with_label(/* Text shown in recall menu when no variables defined */
- _("No variables defined"));
- gtk_widget_set_sensitive(item, FALSE);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- }
- for (i = 0; names[i]; i++) {
- MPNumber *value;
- value = math_variables_get_value(math_equation_get_variables(buttons->priv->equation), names[i]);
- item = make_register_menu_item(buttons, names[i], value, TRUE, G_CALLBACK(recall_menu_cb));
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- }
-
- g_strfreev(names);
+ MathVariablePopup *popup;
+ GtkAllocation allocation;
+ gint x, y;
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
- item = make_register_menu_item(buttons, "ans", math_equation_get_answer(buttons->priv->equation), FALSE, G_CALLBACK(recall_menu_cb));
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- item = make_register_menu_item(buttons, "rand", NULL, FALSE, G_CALLBACK(recall_menu_cb));
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ popup = math_variable_popup_new(buttons->priv->equation);
+ gtk_window_set_transient_for(GTK_WINDOW(popup), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
- gtk_widget_show_all(menu);
- popup_button_menu(widget, GTK_MENU(menu));
+ gtk_widget_get_allocation(widget, &allocation);
+ gdk_window_get_root_coords(gtk_widget_get_window(widget), allocation.x, allocation.y, &x, &y);
+ gtk_window_move(GTK_WINDOW(popup), x, y);
+ gtk_widget_show(GTK_WIDGET(popup));
}
@@ -1356,7 +884,6 @@ shift_left_cb(GtkWidget *widget, MathButtons *buttons)
menu = buttons->priv->shift_left_menu = gtk_menu_new();
gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
- set_tint(menu, &buttons->priv->color_action, 1);
for (i = 1; i < 16; i++) {
GtkWidget *item, *label;
@@ -1400,7 +927,6 @@ shift_right_cb(GtkWidget *widget, MathButtons *buttons)
menu = buttons->priv->shift_right_menu = gtk_menu_new();
gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
- set_tint(menu, &buttons->priv->color_action, 1);
for (i = 1; i < 16; i++) {
GtkWidget *item, *label;
@@ -1429,7 +955,7 @@ shift_right_cb(GtkWidget *widget, MathButtons *buttons)
}
}
- popup_button_menu(widget, GTK_MENU(buttons->priv->shift_right_menu));
+ popup_button_menu(widget, GTK_MENU(buttons->priv->shift_right_menu));
}
@@ -1448,10 +974,10 @@ function_cb(GtkWidget *widget, MathButtons *buttons)
if (!buttons->priv->function_menu) {
gint i;
GtkWidget *menu;
- struct
+ struct
{
gchar *name, *function;
- } functions[] =
+ } functions[] =
{
{ /* Tooltip for the integer component button */
N_("Integer Component"), "int " },
@@ -1470,20 +996,19 @@ function_cb(GtkWidget *widget, MathButtons *buttons)
menu = buttons->priv->function_menu = gtk_menu_new();
gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
- set_tint(menu, &buttons->priv->color_function, 1);
for (i = 0; functions[i].name != NULL; i++) {
GtkWidget *item;
-
+
item = gtk_menu_item_new_with_label(_(functions[i].name));
- g_object_set_data(G_OBJECT(item), "function", g_strdup (functions[i].function));
+ g_object_set_data(G_OBJECT(item), "function", g_strdup(functions[i].function));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
g_signal_connect(item, "activate", G_CALLBACK(insert_function_cb), buttons);
gtk_widget_show(item);
}
}
- popup_button_menu(widget, GTK_MENU(buttons->priv->function_menu));
+ popup_button_menu(widget, GTK_MENU(buttons->priv->function_menu));
}
@@ -1492,7 +1017,7 @@ G_MODULE_EXPORT
void
factorize_cb(GtkWidget *widget, MathButtons *buttons)
{
- math_equation_factorize (buttons->priv->equation);
+ math_equation_factorize(buttons->priv->equation);
}
@@ -1550,7 +1075,7 @@ finc_activate_cb(GtkWidget *widget, MathButtons *buttons)
if (finc_dialog_fields[dialog][field+1] == NULL) {
GtkWidget *dialog_widget;
dialog_widget = gtk_widget_get_toplevel(widget);
- if (gtk_widget_is_toplevel (dialog_widget)) {
+ if (gtk_widget_is_toplevel(dialog_widget)) {
gtk_dialog_response(GTK_DIALOG(dialog_widget),
GTK_RESPONSE_OK);
return;
@@ -1577,7 +1102,7 @@ finc_response_cb(GtkWidget *widget, gint response_id, MathButtons *buttons)
if (response_id != GTK_RESPONSE_OK)
return;
- dialog = GPOINTER_TO_INT (g_object_get_data(G_OBJECT(widget), "finc_dialog"));
+ dialog = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "finc_dialog"));
for (i = 0; i < 4; i++) {
if (finc_dialog_fields[dialog][i] == NULL) {
@@ -1602,7 +1127,7 @@ character_code_dialog_response_cb(GtkWidget *dialog, gint response_id, MathButto
text = gtk_entry_get_text(GTK_ENTRY(buttons->priv->character_code_entry));
- if (response_id == GTK_RESPONSE_OK) {
+ if (response_id == GTK_RESPONSE_OK) {
MPNumber x;
int i = 0;
@@ -1653,16 +1178,35 @@ bit_toggle_cb(GtkWidget *event_box, GdkEventButton *event, MathButtons *buttons)
}
+static void
+remove_trailing_spaces(MathButtons *buttons)
+{
+ GtkTextMark *insert_mark;
+ GtkTextIter start, end;
+ insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER(buttons->priv->equation));
+ gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(buttons->priv->equation), &end, insert_mark);
+ start = end;
+ while (gtk_text_iter_backward_char(&start)) {
+ if (!g_unichar_isspace(gtk_text_iter_get_char(&start)))
+ break;
+ gtk_text_buffer_delete(GTK_TEXT_BUFFER(buttons->priv->equation), &start, &end);
+ }
+}
+
void set_superscript_cb(GtkWidget *widget, MathButtons *buttons);
G_MODULE_EXPORT
void
set_superscript_cb(GtkWidget *widget, MathButtons *buttons)
{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
- math_equation_set_number_mode(buttons->priv->equation, SUPERSCRIPT);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+ math_equation_set_number_mode(buttons->priv->equation, SUPERSCRIPT);
+ if (!gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(buttons->priv->equation))) {
+ remove_trailing_spaces(buttons);
+ }
+ }
else if (math_equation_get_number_mode(buttons->priv->equation) == SUPERSCRIPT)
- math_equation_set_number_mode(buttons->priv->equation, NORMAL);
+ math_equation_set_number_mode(buttons->priv->equation, NORMAL);
}
@@ -1671,10 +1215,14 @@ G_MODULE_EXPORT
void
set_subscript_cb(GtkWidget *widget, MathButtons *buttons)
{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
- math_equation_set_number_mode(buttons->priv->equation, SUBSCRIPT);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+ math_equation_set_number_mode(buttons->priv->equation, SUBSCRIPT);
+ if (!gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(buttons->priv->equation))) {
+ remove_trailing_spaces(buttons);
+ }
+ }
else if (math_equation_get_number_mode(buttons->priv->equation) == SUBSCRIPT)
- math_equation_set_number_mode(buttons->priv->equation, NORMAL);
+ math_equation_set_number_mode(buttons->priv->equation, NORMAL);
}
@@ -1683,7 +1231,7 @@ number_mode_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *bu
{
GList *i;
NumberMode mode;
-
+
mode = math_equation_get_number_mode(equation);
for (i = buttons->priv->superscript_toggles; i; i = i->next) {
@@ -1698,18 +1246,18 @@ number_mode_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *bu
static void
-math_buttons_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+math_buttons_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
MathButtons *self;
- self = MATH_BUTTONS (object);
+ self = MATH_BUTTONS(object);
switch (prop_id) {
case PROP_EQUATION:
- self->priv->equation = g_value_get_object (value);
+ self->priv->equation = g_value_get_object(value);
math_buttons_set_mode(self, self->priv->mode);
g_signal_connect(self->priv->equation, "notify::display", G_CALLBACK(display_changed_cb), self);
g_signal_connect(self->priv->equation, "notify::number-mode", G_CALLBACK(number_mode_changed_cb), self);
@@ -1719,41 +1267,47 @@ math_buttons_set_property (GObject *object,
display_changed_cb(self->priv->equation, NULL, self);
break;
case PROP_MODE:
- math_buttons_set_mode(self, g_value_get_int (value));
+ math_buttons_set_mode(self, g_value_get_int(value));
+ break;
+ case PROP_PROGRAMMING_BASE:
+ math_buttons_set_programming_base(self, g_value_get_int(value));
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_buttons_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+math_buttons_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
MathButtons *self;
- self = MATH_BUTTONS (object);
+ self = MATH_BUTTONS(object);
switch (prop_id) {
case PROP_EQUATION:
- g_value_set_object (value, self->priv->equation);
+ g_value_set_object(value, self->priv->equation);
break;
case PROP_MODE:
- g_value_set_int (value, self->priv->mode);
+ g_value_set_int(value, self->priv->mode);
+ break;
+ case PROP_PROGRAMMING_BASE:
+ g_value_set_int(value, math_buttons_get_programming_base(self));
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_buttons_class_init (MathButtonsClass *klass)
+math_buttons_class_init(MathButtonsClass *klass)
{
static GEnumValue button_mode_values[] =
{
@@ -1763,43 +1317,45 @@ math_buttons_class_init (MathButtonsClass *klass)
{PROGRAMMING, "programming", "programming"},
{0, NULL, NULL}
};
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->get_property = math_buttons_get_property;
object_class->set_property = math_buttons_set_property;
- g_type_class_add_private (klass, sizeof (MathButtonsPrivate));
+ g_type_class_add_private(klass, sizeof(MathButtonsPrivate));
button_mode_type = g_enum_register_static("ButtonMode", button_mode_values);
- g_object_class_install_property (object_class,
- PROP_EQUATION,
- g_param_spec_object ("equation",
- "equation",
- "Equation being controlled",
- math_equation_get_type(),
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (object_class,
- PROP_MODE,
- g_param_spec_enum ("mode",
- "mode",
- "Button mode",
- button_mode_type,
- BASIC,
- G_PARAM_READWRITE));
+ g_object_class_install_property(object_class,
+ PROP_EQUATION,
+ g_param_spec_object("equation",
+ "equation",
+ "Equation being controlled",
+ math_equation_get_type(),
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property(object_class,
+ PROP_MODE,
+ g_param_spec_enum("mode",
+ "mode",
+ "Button mode",
+ button_mode_type,
+ BASIC,
+ G_PARAM_READWRITE));
+ g_object_class_install_property(object_class,
+ PROP_PROGRAMMING_BASE,
+ g_param_spec_int("programming-base",
+ "programming-base",
+ "Base to use in programming mode",
+ 2, 16, 10,
+ G_PARAM_READWRITE));
}
static void
-math_buttons_init (MathButtons *buttons)
+math_buttons_init(MathButtons *buttons)
{
- buttons->priv = G_TYPE_INSTANCE_GET_PRIVATE (buttons, math_buttons_get_type(), MathButtonsPrivate);
+ buttons->priv = G_TYPE_INSTANCE_GET_PRIVATE(buttons, math_buttons_get_type(), MathButtonsPrivate);
+ gtk_box_set_spacing(GTK_BOX(buttons), 6);
buttons->priv->programming_base = 10;
- gdk_color_parse("#0000FF", &buttons->priv->color_numbers);
- gdk_color_parse("#00FF00", &buttons->priv->color_action);
- gdk_color_parse("#FF0000", &buttons->priv->color_operator);
- gdk_color_parse("#00FFFF", &buttons->priv->color_function);
- gdk_color_parse("#FF00FF", &buttons->priv->color_memory);
- gdk_color_parse("#FFFFFF", &buttons->priv->color_group);
g_signal_connect(G_OBJECT(buttons), "show", G_CALLBACK(load_buttons), NULL);
}
diff --git a/src/math-buttons.h b/src/math-buttons.h
index e09c4c2..405a3de 100644
--- a/src/math-buttons.h
+++ b/src/math-buttons.h
@@ -1,19 +1,11 @@
-/* Copyright (c) 2008-2009 Robert Ancell
+/*
+ * 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.
*/
#ifndef MATH_BUTTONS_H
@@ -29,32 +21,36 @@ G_BEGIN_DECLS
typedef struct MathButtonsPrivate MathButtonsPrivate;
-typedef struct {
+typedef struct
+{
GtkVBox parent_instance;
- MathButtonsPrivate* priv;
+ MathButtonsPrivate *priv;
} MathButtons;
-typedef struct {
- GtkVBoxClass parent_class;
+typedef struct
+{
+ GtkVBoxClass parent_class;
} MathButtonsClass;
typedef enum {
- BASIC,
- ADVANCED,
- FINANCIAL,
- PROGRAMMING
+ BASIC,
+ ADVANCED,
+ FINANCIAL,
+ PROGRAMMING
} ButtonMode;
GType math_buttons_get_type(void);
-MathButtons* math_buttons_new(MathEquation* equation);
+MathButtons *math_buttons_new(MathEquation *equation);
+
+void math_buttons_set_mode(MathButtons *buttons, ButtonMode mode);
-void math_buttons_set_mode(MathButtons* buttons, ButtonMode mode);
+ButtonMode math_buttons_get_mode(MathButtons *buttons);
-ButtonMode math_buttons_get_mode(MathButtons* buttons);
+void math_buttons_set_programming_base(MathButtons *buttons, gint base);
-void math_buttons_set_programming_base(MathButtons* buttons, gint base);
+gint math_buttons_get_programming_base(MathButtons *buttons);
-gint math_buttons_get_programming_base(MathButtons* buttons);
+G_END_DECLS
#endif /* MATH_BUTTONS_H */
diff --git a/src/math-converter.c b/src/math-converter.c
new file mode 100644
index 0000000..ff28500
--- /dev/null
+++ b/src/math-converter.c
@@ -0,0 +1,440 @@
+/*
+ * 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 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 <glib/gi18n.h>
+
+#include "math-converter.h"
+#include "unit-manager.h"
+#include "currency-manager.h"
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0, };
+
+struct MathConverterPrivate
+{
+ MathEquation *equation;
+
+ gchar *category;
+
+ GtkWidget *from_combo;
+ GtkWidget *to_combo;
+
+ GtkWidget *result_label;
+};
+
+
+G_DEFINE_TYPE (MathConverter, math_converter, GTK_TYPE_HBOX);
+
+static void display_changed_cb(MathEquation *equation, GParamSpec *spec, MathConverter *converter);
+static void update_from_model(MathConverter *converter);
+
+
+MathConverter *
+math_converter_new(MathEquation *equation)
+{
+ MathConverter *converter = g_object_new(math_converter_get_type(), NULL);
+ converter->priv->equation = g_object_ref(equation);
+ g_signal_connect(converter->priv->equation, "notify::display", G_CALLBACK(display_changed_cb), converter);
+ update_from_model(converter);
+ return converter;
+}
+
+
+static gboolean
+convert_equation(MathConverter *converter, const MPNumber *x, MPNumber *z)
+{
+ GtkTreeIter from_iter, to_iter;
+ UnitCategory *category = NULL;
+ Unit *source_unit = NULL, *target_unit = NULL;
+ gboolean result;
+
+ if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &from_iter) ||
+ !gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->to_combo), &to_iter))
+ return FALSE;
+
+ gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo)), &from_iter, 1, &category, 2, &source_unit, -1);
+ gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->to_combo)), &to_iter, 2, &target_unit, -1);
+
+ result = unit_category_convert(category, x, source_unit, target_unit, z);
+
+ if (category)
+ g_object_unref(category);
+ if (source_unit)
+ g_object_unref(source_unit);
+ if (target_unit)
+ g_object_unref(target_unit);
+
+ return result;
+}
+
+
+static void
+update_result_label(MathConverter *converter)
+{
+ MPNumber x, z;
+ gboolean enabled;
+
+ if (!converter->priv->result_label)
+ return;
+
+ if (math_equation_get_number(converter->priv->equation, &x))
+ enabled = convert_equation(converter, &x, &z);
+ else
+ enabled = FALSE;
+
+ gtk_widget_set_sensitive(converter->priv->result_label, enabled);
+ if (enabled) {
+ gchar *source_text, *target_text, *label;
+ Unit *source_unit, *target_unit;
+
+ math_converter_get_conversion(converter, &source_unit, &target_unit);
+
+ source_text = unit_format(source_unit, &x);
+ target_text = unit_format(target_unit, &z);
+ label = g_strdup_printf("%s = %s", source_text, target_text);
+ gtk_label_set_text(GTK_LABEL(converter->priv->result_label), label);
+
+ g_free(source_text);
+ g_free(target_text);
+ g_free(label);
+
+ g_object_unref(source_unit);
+ g_object_unref(target_unit);
+ }
+}
+
+
+static void
+display_changed_cb(MathEquation *equation, GParamSpec *spec, MathConverter *converter)
+{
+ update_result_label(converter);
+}
+
+
+static void
+update_from_model(MathConverter *converter)
+{
+ GtkTreeStore *from_model;
+
+ from_model = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_OBJECT);
+
+ if (converter->priv->category == NULL) {
+ const GList *categories, *iter;
+
+ categories = unit_manager_get_categories(unit_manager_get_default());
+ for (iter = categories; iter; iter = iter->next) {
+ UnitCategory *category = iter->data;
+ GtkTreeIter parent;
+ const GList *unit_iter;
+
+ gtk_tree_store_append(from_model, &parent, NULL);
+ gtk_tree_store_set(from_model, &parent, 0, unit_category_get_display_name(category), 1, category, -1);
+
+ for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
+ Unit *unit = unit_iter->data;
+ GtkTreeIter iter;
+
+ gtk_tree_store_append(from_model, &iter, &parent);
+ gtk_tree_store_set(from_model, &iter, 0, unit_get_display_name(unit), 1, category, 2, unit, -1);
+ }
+ }
+ }
+ else {
+ UnitCategory *category;
+ const GList *unit_iter;
+
+ category = unit_manager_get_category(unit_manager_get_default(), converter->priv->category);
+ for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
+ Unit *unit = unit_iter->data;
+ GtkTreeIter iter;
+
+ gtk_tree_store_append(from_model, &iter, NULL);
+ gtk_tree_store_set(from_model, &iter, 0, unit_get_display_name(unit), 1, category, 2, unit, -1);
+ }
+ }
+
+ gtk_combo_box_set_model(GTK_COMBO_BOX(converter->priv->from_combo), GTK_TREE_MODEL(from_model));
+}
+
+
+void
+math_converter_set_category(MathConverter *converter, const gchar *category)
+{
+ g_return_if_fail (converter != NULL);
+
+ if (category == NULL && converter->priv->category == NULL)
+ return;
+ if (category != NULL && converter->priv->category != NULL && strcmp(category, converter->priv->category) == 0)
+ return;
+
+ g_free(converter->priv->category);
+ converter->priv->category = g_strdup(category);
+
+ update_from_model(converter);
+}
+
+
+const gchar *
+math_converter_get_category(MathConverter *converter)
+{
+ g_return_val_if_fail (converter != NULL, NULL);
+ return converter->priv->category;
+}
+
+
+static gboolean
+iter_is_unit(GtkTreeModel *model, GtkTreeIter *iter, Unit *unit)
+{
+ Unit *u;
+
+ gtk_tree_model_get(model, iter, 2, &u, -1);
+
+ if (!u)
+ return FALSE;
+
+ g_object_unref(u);
+ if (u == unit)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static gboolean
+set_active_unit(GtkComboBox *combo, GtkTreeIter *iter, Unit *unit)
+{
+ GtkTreeModel *model;
+ GtkTreeIter child_iter;
+
+ model = gtk_combo_box_get_model(combo);
+
+ if (iter && iter_is_unit(model, iter, unit)) {
+ gtk_combo_box_set_active_iter(combo, iter);
+ return TRUE;
+ }
+
+ if (!gtk_tree_model_iter_children(model, &child_iter, iter))
+ return FALSE;
+
+ do {
+ if (set_active_unit(combo, &child_iter, unit))
+ return TRUE;
+ } while (gtk_tree_model_iter_next(model, &child_iter));
+
+ return FALSE;
+}
+
+
+void
+math_converter_set_conversion(MathConverter *converter, /*const gchar *category,*/ const gchar *unit_a, const gchar *unit_b)
+{
+ Unit *ua;
+ Unit *ub;
+
+ g_return_if_fail (converter != NULL);
+ g_return_if_fail (unit_a != NULL);
+ g_return_if_fail (unit_b != NULL);
+
+ ua = unit_manager_get_unit_by_name(unit_manager_get_default(), unit_a);
+ ub = unit_manager_get_unit_by_name(unit_manager_get_default(), unit_b);
+ if (!ua || !ub)
+ {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* Select the first unit */
+ model = gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo));
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ GtkTreeIter child_iter;
+ while (gtk_tree_model_iter_children(model, &child_iter, &iter))
+ iter = child_iter;
+ gtk_combo_box_set_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &iter);
+ }
+ return;
+ }
+
+ set_active_unit(GTK_COMBO_BOX(converter->priv->from_combo), NULL, ua);
+ set_active_unit(GTK_COMBO_BOX(converter->priv->to_combo), NULL, ub);
+}
+
+
+void
+math_converter_get_conversion(MathConverter *converter, Unit **from_unit, Unit **to_unit)
+{
+ GtkTreeIter from_iter, to_iter;
+
+ g_return_if_fail (converter != NULL);
+ g_return_if_fail (from_unit != NULL);
+ g_return_if_fail (to_unit != NULL);
+
+ gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &from_iter);
+ gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->to_combo), &to_iter);
+
+ gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo)), &from_iter, 2, from_unit, -1);
+ gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->to_combo)), &to_iter, 2, to_unit, -1);
+}
+
+
+static void
+math_converter_class_init(MathConverterClass *klass)
+{
+ g_type_class_add_private(klass, sizeof(MathConverterPrivate));
+
+ signals[CHANGED] =
+ g_signal_new("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MathConverterClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+
+static void
+from_combobox_changed_cb(GtkWidget *combo, MathConverter *converter)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ UnitCategory *category;
+ Unit *unit;
+ const GList *unit_iter;
+
+ model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
+ if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter))
+ return;
+ gtk_tree_model_get(model, &iter, 1, &category, 2, &unit, -1);
+
+ /* Set the to combobox to be the list of units can be converted to */
+ model = GTK_TREE_MODEL(gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_OBJECT));
+ for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
+ Unit *u = unit_iter->data;
+ if (u == unit)
+ continue;
+ gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, unit_get_display_name(u), 1, category, 2, u, -1);
+ }
+ gtk_combo_box_set_model(GTK_COMBO_BOX(converter->priv->to_combo), model);
+
+ /* Select the first possible unit */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(converter->priv->to_combo), 0);
+
+ g_object_unref(category);
+ g_object_unref(unit);
+}
+
+
+static void
+to_combobox_changed_cb(GtkWidget *combo, MathConverter *converter)
+{
+ /* Conversion must have changed */
+ update_result_label(converter);
+
+ g_signal_emit(converter, signals[CHANGED], 0);
+}
+
+
+static void
+from_cell_data_func(GtkCellLayout *cell_layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ g_object_set(cell, "sensitive", !gtk_tree_model_iter_has_child(tree_model, iter), NULL);
+}
+
+
+static void
+currency_updated_cb(CurrencyManager *manager, MathConverter *converter)
+{
+ update_result_label(converter);
+}
+
+static void
+swap_button_clicked_cb(GtkButton *button, MathConverter *converter)
+{
+ Unit *from_unit, *to_unit;
+ MPNumber x, z;
+
+ if (math_equation_get_number(converter->priv->equation, &x) &&
+ convert_equation(converter, &x, &z))
+ math_equation_set_number(converter->priv->equation, &z);
+
+ math_converter_get_conversion(converter, &from_unit, &to_unit);
+ set_active_unit(GTK_COMBO_BOX(converter->priv->from_combo), NULL, to_unit);
+ set_active_unit(GTK_COMBO_BOX(converter->priv->to_combo), NULL, from_unit);
+
+ update_result_label(converter);
+
+ g_object_unref(from_unit);
+ g_object_unref(to_unit);
+}
+
+static void
+math_converter_init(MathConverter *converter)
+{
+ GtkWidget *hbox, *label, *swap_button;
+ GtkCellRenderer *renderer;
+
+ converter->priv = G_TYPE_INSTANCE_GET_PRIVATE(converter, math_converter_get_type(), MathConverterPrivate);
+
+ gtk_box_set_spacing(GTK_BOX(converter), 6);
+
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_widget_show(hbox);
+ gtk_box_pack_start(GTK_BOX(converter), hbox, FALSE, TRUE, 0);
+
+ converter->priv->from_combo = gtk_combo_box_new ();
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter->priv->from_combo), renderer, TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter->priv->from_combo), renderer, "text", 0);
+ gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(converter->priv->from_combo),
+ renderer,
+ from_cell_data_func,
+ NULL, NULL);
+ g_signal_connect(converter->priv->from_combo, "changed", G_CALLBACK(from_combobox_changed_cb), converter);
+ gtk_widget_show(converter->priv->from_combo);
+ gtk_box_pack_start(GTK_BOX(hbox), converter->priv->from_combo, FALSE, TRUE, 0);
+
+ label = gtk_label_new(/* Label that is displayed between the two conversion combo boxes, e.g. "[degrees] in [radians]" */
+ _(" in "));
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 5);
+
+ converter->priv->to_combo = gtk_combo_box_new();
+ renderer = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter->priv->to_combo), renderer, TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter->priv->to_combo), renderer, "text", 0);
+ g_signal_connect(converter->priv->to_combo, "changed", G_CALLBACK(to_combobox_changed_cb), converter);
+ gtk_widget_show(converter->priv->to_combo);
+ gtk_box_pack_start(GTK_BOX(hbox), converter->priv->to_combo, FALSE, TRUE, 0);
+
+ swap_button = gtk_button_new_with_label ("⇆");
+ gtk_widget_set_tooltip_text (swap_button,
+ /* Tooltip for swap conversion button */
+ _("Switch conversion units"));
+ gtk_button_set_relief (GTK_BUTTON (swap_button), GTK_RELIEF_NONE);
+ g_signal_connect (swap_button, "clicked", G_CALLBACK (swap_button_clicked_cb), converter);
+ gtk_widget_show(swap_button);
+ gtk_box_pack_start(GTK_BOX(hbox), swap_button, FALSE, TRUE, 0);
+
+ converter->priv->result_label = gtk_label_new("");
+ gtk_misc_set_alignment(GTK_MISC(converter->priv->result_label), 1.0, 0.5);
+ gtk_widget_set_sensitive(converter->priv->result_label, FALSE);
+ gtk_widget_show(converter->priv->result_label);
+ gtk_box_pack_start(GTK_BOX(converter), converter->priv->result_label, TRUE, TRUE, 0);
+
+ g_signal_connect(currency_manager_get_default(), "updated", G_CALLBACK(currency_updated_cb), converter);
+}
diff --git a/src/math-converter.h b/src/math-converter.h
new file mode 100644
index 0000000..f67b425
--- /dev/null
+++ b/src/math-converter.h
@@ -0,0 +1,53 @@
+/*
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef MATH_CONVERTER_H
+#define MATH_CONVERTER_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "math-equation.h"
+#include "unit.h"
+
+G_BEGIN_DECLS
+
+#define MATH_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), math_converter_get_type(), MathConverter))
+
+typedef struct MathConverterPrivate MathConverterPrivate;
+
+typedef struct
+{
+ GtkHBox parent_instance;
+ MathConverterPrivate *priv;
+} MathConverter;
+
+typedef struct
+{
+ GtkHBoxClass parent_class;
+
+ void (*changed)(MathConverter *converter);
+} MathConverterClass;
+
+GType math_converter_get_type(void);
+
+MathConverter *math_converter_new(MathEquation *equation);
+
+void math_converter_set_category(MathConverter *converter, const gchar *category);
+
+const gchar *math_converter_get_category(MathConverter *converter);
+
+void math_converter_set_conversion(MathConverter *converter, /*const gchar *category,*/ const gchar *unit_a, const gchar *unit_b);
+
+void math_converter_get_conversion(MathConverter *converter, Unit **from_unit, Unit **to_unit);
+
+G_END_DECLS
+
+#endif /* MATH_CONVERTER_H */
diff --git a/src/math-display.c b/src/math-display.c
index b809695..6718665 100644
--- a/src/math-display.c
+++ b/src/math-display.c
@@ -1,19 +1,11 @@
-/* Copyright (c) 2008-2009 Robert Ancell
+/*
+ * 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 <string.h>
@@ -37,23 +29,26 @@ struct MathDisplayPrivate
/* Buffer that shows errors etc */
GtkTextBuffer *info_buffer;
+
+ /* Spinner widget that shows if we're calculating a response */
+ GtkWidget *spinner;
};
-G_DEFINE_TYPE (MathDisplay, math_display, GTK_TYPE_VBOX);
+G_DEFINE_TYPE (MathDisplay, math_display, GTK_TYPE_VIEWPORT);
#define GET_WIDGET(ui, name) GTK_WIDGET(gtk_builder_get_object(ui, name))
MathDisplay *
math_display_new()
{
- return g_object_new (math_display_get_type(), "equation", math_equation_new(), NULL);
+ return g_object_new(math_display_get_type(), "equation", math_equation_new(), NULL);
}
MathDisplay *
math_display_new_with_equation(MathEquation *equation)
{
- return g_object_new (math_display_get_type(), "equation", equation, NULL);
+ return g_object_new(math_display_get_type(), "equation", equation, NULL);
}
@@ -69,24 +64,77 @@ display_key_press_cb(GtkWidget *widget, GdkEventKey *event, MathDisplay *display
{
int state;
guint32 c;
+ guint new_keyval = 0;
+
+ /* Treat keypad keys as numbers even when numlock is off */
+ switch(event->keyval)
+ {
+ case GDK_KEY_KP_Insert:
+ new_keyval = GDK_KEY_0;
+ break;
+ case GDK_KEY_KP_End:
+ new_keyval = GDK_KEY_1;
+ break;
+ case GDK_KEY_KP_Down:
+ new_keyval = GDK_KEY_2;
+ break;
+ case GDK_KEY_KP_Page_Down:
+ new_keyval = GDK_KEY_3;
+ break;
+ case GDK_KEY_KP_Left:
+ new_keyval = GDK_KEY_4;
+ break;
+ case GDK_KEY_KP_Begin: /* This is apparently what "5" does when numlock is off. */
+ new_keyval = GDK_KEY_5;
+ break;
+ case GDK_KEY_KP_Right:
+ new_keyval = GDK_KEY_6;
+ break;
+ case GDK_KEY_KP_Home:
+ new_keyval = GDK_KEY_7;
+ break;
+ case GDK_KEY_KP_Up:
+ new_keyval = GDK_KEY_8;
+ break;
+ case GDK_KEY_KP_Page_Up:
+ new_keyval = GDK_KEY_9;
+ break;
+ }
+
+ if (new_keyval) {
+ gboolean result;
+ GdkEvent *new_event;
+
+ new_event = gdk_event_copy((GdkEvent *)event);
+ ((GdkEventKey *)new_event)->keyval = new_keyval;
+ g_signal_emit_by_name(widget, "key-press-event", new_event, &result);
+ gdk_event_free(new_event);
+ return result;
+ }
state = event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK);
c = gdk_keyval_to_unicode(event->keyval);
/* Solve on enter */
- if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
+ if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
math_equation_solve(display->priv->equation);
return TRUE;
}
/* Clear on escape */
- if ((event->keyval == GDK_Escape && state == 0) ||
- (event->keyval == GDK_BackSpace && state == GDK_CONTROL_MASK) ||
- (event->keyval == GDK_Delete && state == GDK_SHIFT_MASK)) {
+ if ((event->keyval == GDK_KEY_Escape && state == 0) ||
+ (event->keyval == GDK_KEY_BackSpace && state == GDK_CONTROL_MASK) ||
+ (event->keyval == GDK_KEY_Delete && state == GDK_SHIFT_MASK)) {
math_equation_clear(display->priv->equation);
return TRUE;
}
+ /* Numeric keypad will often insert '.' regardless of locale */
+ if (event->keyval == GDK_KEY_KP_Decimal) {
+ math_equation_insert_numeric_point(display->priv->equation);
+ return TRUE;
+ }
+
/* Substitute */
if (state == 0) {
if (c == '*') {
@@ -107,34 +155,34 @@ display_key_press_cb(GtkWidget *widget, GdkEventKey *event, MathDisplay *display
if (state == GDK_CONTROL_MASK) {
switch(event->keyval)
{
- case GDK_bracketleft:
+ case GDK_KEY_bracketleft:
math_equation_insert(display->priv->equation, "⌈");
return TRUE;
- case GDK_bracketright:
+ case GDK_KEY_bracketright:
math_equation_insert(display->priv->equation, "⌉");
return TRUE;
- case GDK_e:
+ case GDK_KEY_e:
math_equation_insert_exponent(display->priv->equation);
return TRUE;
- case GDK_f:
+ case GDK_KEY_f:
math_equation_factorize(display->priv->equation);
return TRUE;
- case GDK_i:
+ case GDK_KEY_i:
math_equation_insert(display->priv->equation, "⁻¹");
return TRUE;
- case GDK_p:
+ case GDK_KEY_p:
math_equation_insert(display->priv->equation, "π");
return TRUE;
- case GDK_r:
+ case GDK_KEY_r:
math_equation_insert(display->priv->equation, "√");
return TRUE;
- case GDK_u:
+ case GDK_KEY_u:
math_equation_insert(display->priv->equation, "µ");
return TRUE;
- case GDK_minus:
+ case GDK_KEY_minus:
math_equation_insert(display->priv->equation, "⁻");
return TRUE;
- case GDK_apostrophe:
+ case GDK_KEY_apostrophe:
math_equation_insert(display->priv->equation, "°");
return TRUE;
}
@@ -142,10 +190,10 @@ display_key_press_cb(GtkWidget *widget, GdkEventKey *event, MathDisplay *display
if (state == GDK_MOD1_MASK) {
switch(event->keyval)
{
- case GDK_bracketleft:
+ case GDK_KEY_bracketleft:
math_equation_insert(display->priv->equation, "⌊");
return TRUE;
- case GDK_bracketright:
+ case GDK_KEY_bracketright:
math_equation_insert(display->priv->equation, "⌋");
return TRUE;
}
@@ -154,34 +202,44 @@ display_key_press_cb(GtkWidget *widget, GdkEventKey *event, MathDisplay *display
if (state == GDK_CONTROL_MASK || math_equation_get_number_mode(display->priv->equation) == SUPERSCRIPT) {
switch(event->keyval)
{
- case GDK_0:
+ case GDK_KEY_0:
+ case GDK_KEY_KP_0:
math_equation_insert(display->priv->equation, "⁰");
return TRUE;
- case GDK_1:
+ case GDK_KEY_1:
+ case GDK_KEY_KP_1:
math_equation_insert(display->priv->equation, "¹");
return TRUE;
- case GDK_2:
+ case GDK_KEY_2:
+ case GDK_KEY_KP_2:
math_equation_insert(display->priv->equation, "²");
return TRUE;
- case GDK_3:
+ case GDK_KEY_3:
+ case GDK_KEY_KP_3:
math_equation_insert(display->priv->equation, "³");
return TRUE;
- case GDK_4:
+ case GDK_KEY_4:
+ case GDK_KEY_KP_4:
math_equation_insert(display->priv->equation, "⁴");
return TRUE;
- case GDK_5:
+ case GDK_KEY_5:
+ case GDK_KEY_KP_5:
math_equation_insert(display->priv->equation, "⁵");
return TRUE;
- case GDK_6:
+ case GDK_KEY_6:
+ case GDK_KEY_KP_6:
math_equation_insert(display->priv->equation, "⁶");
return TRUE;
- case GDK_7:
+ case GDK_KEY_7:
+ case GDK_KEY_KP_7:
math_equation_insert(display->priv->equation, "⁷");
return TRUE;
- case GDK_8:
+ case GDK_KEY_8:
+ case GDK_KEY_KP_8:
math_equation_insert(display->priv->equation, "⁸");
return TRUE;
- case GDK_9:
+ case GDK_KEY_9:
+ case GDK_KEY_KP_9:
math_equation_insert(display->priv->equation, "⁹");
return TRUE;
}
@@ -189,34 +247,44 @@ display_key_press_cb(GtkWidget *widget, GdkEventKey *event, MathDisplay *display
else if (state == GDK_MOD1_MASK || math_equation_get_number_mode(display->priv->equation) == SUBSCRIPT) {
switch(event->keyval)
{
- case GDK_0:
+ case GDK_KEY_0:
+ case GDK_KEY_KP_0:
math_equation_insert(display->priv->equation, "₀");
return TRUE;
- case GDK_1:
+ case GDK_KEY_1:
+ case GDK_KEY_KP_1:
math_equation_insert(display->priv->equation, "₁");
return TRUE;
- case GDK_2:
+ case GDK_KEY_2:
+ case GDK_KEY_KP_2:
math_equation_insert(display->priv->equation, "₂");
return TRUE;
- case GDK_3:
+ case GDK_KEY_3:
+ case GDK_KEY_KP_3:
math_equation_insert(display->priv->equation, "₃");
return TRUE;
- case GDK_4:
+ case GDK_KEY_4:
+ case GDK_KEY_KP_4:
math_equation_insert(display->priv->equation, "₄");
return TRUE;
- case GDK_5:
+ case GDK_KEY_5:
+ case GDK_KEY_KP_5:
math_equation_insert(display->priv->equation, "₅");
return TRUE;
- case GDK_6:
+ case GDK_KEY_6:
+ case GDK_KEY_KP_6:
math_equation_insert(display->priv->equation, "₆");
return TRUE;
- case GDK_7:
+ case GDK_KEY_7:
+ case GDK_KEY_KP_7:
math_equation_insert(display->priv->equation, "₇");
return TRUE;
- case GDK_8:
+ case GDK_KEY_8:
+ case GDK_KEY_KP_8:
math_equation_insert(display->priv->equation, "₈");
return TRUE;
- case GDK_9:
+ case GDK_KEY_9:
+ case GDK_KEY_KP_9:
math_equation_insert(display->priv->equation, "₉");
return TRUE;
}
@@ -239,14 +307,27 @@ static void
status_changed_cb(MathEquation *equation, GParamSpec *spec, MathDisplay *display)
{
gtk_text_buffer_set_text(display->priv->info_buffer, math_equation_get_status(equation), -1);
+ if (math_equation_in_solve(equation) && !gtk_widget_get_visible(display->priv->spinner)) {
+ gtk_widget_show(display->priv->spinner);
+ gtk_spinner_start(GTK_SPINNER(display->priv->spinner));
+ }
+ else if (!math_equation_in_solve(equation) && gtk_widget_get_visible(display->priv->spinner)) {
+ gtk_widget_hide(display->priv->spinner);
+ gtk_spinner_stop(GTK_SPINNER(display->priv->spinner));
+ }
}
static void
create_gui(MathDisplay *display)
{
- GtkWidget *info_view;
+ GtkWidget *info_view, *info_box, *main_box;
PangoFontDescription *font_desc;
+ int i;
+ GtkStyle *style;
+
+ main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add(GTK_CONTAINER(display), main_box);
g_signal_connect(display, "key-press-event", G_CALLBACK(key_press_cb), display);
@@ -267,7 +348,10 @@ create_gui(MathDisplay *display)
atk_object_set_role(gtk_widget_get_accessible(display->priv->text_view), ATK_ROLE_EDITBAR);
//FIXME:<property name="AtkObject::accessible-description" translatable="yes" comments="Accessible description for the area in which results are displayed">Result Region</property>
g_signal_connect(display->priv->text_view, "key-press-event", G_CALLBACK(display_key_press_cb), display);
- gtk_box_pack_start(GTK_BOX(display), display->priv->text_view, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(main_box), display->priv->text_view, TRUE, TRUE, 0);
+
+ info_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start(GTK_BOX(main_box), info_box, FALSE, TRUE, 0);
info_view = gtk_text_view_new();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(info_view), GTK_WRAP_WORD);
@@ -277,11 +361,20 @@ create_gui(MathDisplay *display)
gtk_text_view_set_justification(GTK_TEXT_VIEW(info_view), GTK_JUSTIFY_RIGHT);
/* TEMP: Disabled for now as GTK+ doesn't properly render a right aligned right margin, see bug #482688 */
/*gtk_text_view_set_right_margin(GTK_TEXT_VIEW(info_view), 6);*/
- gtk_box_pack_start(GTK_BOX(display), info_view, FALSE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(info_box), info_view, TRUE, TRUE, 0);
display->priv->info_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(info_view));
+ display->priv->spinner = gtk_spinner_new();
+ gtk_box_pack_end(GTK_BOX(info_box), display->priv->spinner, FALSE, FALSE, 0);
+ style = gtk_widget_get_style(info_view);
+ for (i = 0; i < 5; i++) {
+ gtk_widget_modify_bg(GTK_WIDGET(display), i, &style->base[i]);
+ }
+
+ gtk_widget_show(info_box);
gtk_widget_show(info_view);
gtk_widget_show(display->priv->text_view);
+ gtk_widget_show(main_box);
g_signal_connect(display->priv->equation, "notify::status", G_CALLBACK(status_changed_cb), display);
status_changed_cb(display->priv->equation, NULL, display);
@@ -296,15 +389,15 @@ math_display_set_property(GObject *object,
{
MathDisplay *self;
- self = MATH_DISPLAY (object);
+ self = MATH_DISPLAY(object);
switch (prop_id) {
case PROP_EQUATION:
- self->priv->equation = g_value_get_object (value);
+ self->priv->equation = g_value_get_object(value);
create_gui(self);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
@@ -318,28 +411,28 @@ math_display_get_property(GObject *object,
{
MathDisplay *self;
- self = MATH_DISPLAY (object);
+ self = MATH_DISPLAY(object);
switch (prop_id) {
case PROP_EQUATION:
- g_value_set_object (value, self->priv->equation);
+ g_value_set_object(value, self->priv->equation);
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_display_class_init (MathDisplayClass *klass)
+math_display_class_init(MathDisplayClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->get_property = math_display_get_property;
object_class->set_property = math_display_set_property;
- g_type_class_add_private (klass, sizeof (MathDisplayPrivate));
+ g_type_class_add_private(klass, sizeof(MathDisplayPrivate));
g_object_class_install_property(object_class,
PROP_EQUATION,
@@ -351,8 +444,8 @@ math_display_class_init (MathDisplayClass *klass)
}
-static void
+static void
math_display_init(MathDisplay *display)
{
- display->priv = G_TYPE_INSTANCE_GET_PRIVATE (display, math_display_get_type(), MathDisplayPrivate);
+ display->priv = G_TYPE_INSTANCE_GET_PRIVATE(display, math_display_get_type(), MathDisplayPrivate);
}
diff --git a/src/math-display.h b/src/math-display.h
index 0179f17..1f37bd1 100644
--- a/src/math-display.h
+++ b/src/math-display.h
@@ -1,19 +1,11 @@
-/* Copyright (c) 2008-2009 Robert Ancell
+/*
+ * 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.
*/
#ifndef MATH_DISPLAY_H
@@ -30,21 +22,25 @@ G_BEGIN_DECLS
typedef struct MathDisplayPrivate MathDisplayPrivate;
-typedef struct {
- GtkVBox parent_instance;
- MathDisplayPrivate* priv;
+typedef struct
+{
+ GtkViewport parent_instance;
+ MathDisplayPrivate *priv;
} MathDisplay;
-typedef struct {
- GtkVBoxClass parent_class;
+typedef struct
+{
+ GtkViewportClass parent_class;
} MathDisplayClass;
GType math_display_get_type(void);
-MathDisplay* math_display_new(void);
+MathDisplay *math_display_new(void);
+
+MathDisplay *math_display_new_with_equation(MathEquation *equation);
-MathDisplay* math_display_new_with_equation(MathEquation* equation);
+MathEquation *math_display_get_equation(MathDisplay *display);
-MathEquation* math_display_get_equation(MathDisplay* display);
+G_END_DECLS
#endif /* MATH_DISPLAY_H */
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);
}
diff --git a/src/math-equation.h b/src/math-equation.h
index c9ebe0c..954334c 100644
--- a/src/math-equation.h
+++ b/src/math-equation.h
@@ -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.
*/
#ifndef MATH_EQUATION_H
@@ -22,110 +14,115 @@
#include <glib-object.h>
#include <gtk/gtk.h>
+
#include "mp.h"
#include "math-variables.h"
+#include "mp-serializer.h"
G_BEGIN_DECLS
+#define MATH_TYPE_EQUATION (math_equation_get_type ())
#define MATH_EQUATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), math_equation_get_type(), MathEquation))
+#define MATH_EQUATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MATH_TYPE_EQUATION, MathEquationClass))
typedef struct MathEquationPrivate MathEquationPrivate;
-typedef struct {
- GtkTextBuffer parent_instance;
- MathEquationPrivate* priv;
+typedef struct
+{
+ GtkTextBuffer parent_instance;
+ MathEquationPrivate *priv;
} MathEquation;
-typedef struct {
- GtkTextBufferClass parent_class;
+typedef struct
+{
+ GtkTextBufferClass parent_class;
} MathEquationClass;
-/* Number display mode. */
typedef enum {
- FIX,
- SCI,
- ENG
-} DisplayFormat;
-
-typedef enum {
- NORMAL,
- SUPERSCRIPT,
- SUBSCRIPT
+ NORMAL,
+ SUPERSCRIPT,
+ SUBSCRIPT
} NumberMode;
GType math_equation_get_type(void);
-MathEquation* math_equation_new(void);
-
-MathVariables* math_equation_get_variables(MathEquation* equation);
-
-const gchar* math_equation_get_digit_text(MathEquation* equation, guint digit);
-const gchar* math_equation_get_numeric_point_text(MathEquation* equation);
-const gchar* math_equation_get_thousands_separator_text(MathEquation* equation);
-
-void math_equation_set_status(MathEquation* equation, const gchar* status);
-const gchar* math_equation_get_status(MathEquation* equation);
-
-gboolean math_equation_is_empty(MathEquation* equation);
-gboolean math_equation_is_result(MathEquation* equation);
-gchar* math_equation_get_display(MathEquation* equation);
-gchar* math_equation_get_equation(MathEquation* equation);
-gboolean math_equation_get_number(MathEquation* equation, MPNumber* z);
-
-void math_equation_set_number_mode(MathEquation* equation, NumberMode mode);
-NumberMode math_equation_get_number_mode(MathEquation* equation);
-
-void math_equation_set_accuracy(MathEquation* equation, gint accuracy);
-gint math_equation_get_accuracy(MathEquation* equation);
-
-void math_equation_set_show_thousands_separators(MathEquation* equation, gboolean visible);
-gboolean math_equation_get_show_thousands_separators(MathEquation* equation);
-
-void math_equation_set_show_trailing_zeroes(MathEquation* equation, gboolean visible);
-gboolean math_equation_get_show_trailing_zeroes(MathEquation* equation);
-
-void math_equation_set_number_format(MathEquation* equation, DisplayFormat format);
-DisplayFormat math_equation_get_number_format(MathEquation* equation);
-
-void math_equation_set_base(MathEquation* equation, gint base);
-gint math_equation_get_base(MathEquation* equation);
-
-void math_equation_set_word_size(MathEquation* equation, gint word_size);
-gint math_equation_get_word_size(MathEquation* equation);
-
-void math_equation_set_angle_units(MathEquation* equation, MPAngleUnit angle_unit);
-MPAngleUnit math_equation_get_angle_units(MathEquation* equation);
-
-void math_equation_set_source_currency(MathEquation* equation, const gchar* currency);
-const gchar* math_equation_get_source_currency(MathEquation* equation);
-
-void math_equation_set_target_currency(MathEquation* equation, const gchar* currency);
-const gchar* math_equation_get_target_currency(MathEquation* equation);
-
-const MPNumber* math_equation_get_answer(MathEquation* equation);
-
-void math_equation_copy(MathEquation* equation);
-void math_equation_paste(MathEquation* equation);
-void math_equation_undo(MathEquation* equation);
-void math_equation_redo(MathEquation* equation);
-void math_equation_store(MathEquation* equation, const gchar* name);
-void math_equation_recall(MathEquation* equation, const gchar* name);
-void math_equation_set(MathEquation* equation, const gchar* text);
-void math_equation_set_number(MathEquation* equation, const MPNumber* x);
-void math_equation_insert(MathEquation* equation, const gchar* text);
-void math_equation_insert_digit(MathEquation* equation, guint digit);
-void math_equation_insert_numeric_point(MathEquation* equation);
-void math_equation_insert_number(MathEquation* equation, const MPNumber* x);
-void math_equation_insert_subtract(MathEquation* equation);
-void math_equation_insert_exponent(MathEquation* equation);
-void math_equation_solve(MathEquation* equation);
-void math_equation_factorize(MathEquation* equation);
-void math_equation_delete(MathEquation* equation);
-void math_equation_backspace(MathEquation* equation);
-void math_equation_clear(MathEquation* equation);
-void math_equation_shift(MathEquation* equation, gint count);
-void math_equation_toggle_bit(MathEquation* equation, guint bit);
-
-//FIXME: Obsolete
-void display_make_number(MathEquation* equation, char* target, int target_len, const MPNumber* x);
+MathEquation *math_equation_new(void);
+
+MathVariables *math_equation_get_variables(MathEquation *equation);
+
+gunichar math_equation_get_digit_text(MathEquation *equation, guint digit);
+
+void math_equation_set_status(MathEquation *equation, const gchar *status);
+const gchar *math_equation_get_status(MathEquation *equation);
+
+gboolean math_equation_is_empty(MathEquation *equation);
+gboolean math_equation_is_result(MathEquation *equation);
+gchar *math_equation_get_display(MathEquation *equation);
+gchar *math_equation_get_equation(MathEquation *equation);
+gboolean math_equation_get_number(MathEquation *equation, MPNumber *z);
+
+void math_equation_set_number_mode(MathEquation *equation, NumberMode mode);
+NumberMode math_equation_get_number_mode(MathEquation *equation);
+
+void math_equation_set_accuracy(MathEquation *equation, gint accuracy);
+gint math_equation_get_accuracy(MathEquation *equation);
+
+void math_equation_set_show_thousands_separators(MathEquation *equation, gboolean visible);
+gboolean math_equation_get_show_thousands_separators(MathEquation *equation);
+
+void math_equation_set_show_trailing_zeroes(MathEquation *equation, gboolean visible);
+gboolean math_equation_get_show_trailing_zeroes(MathEquation *equation);
+
+void math_equation_set_number_format(MathEquation *equation, MpDisplayFormat format);
+MpDisplayFormat math_equation_get_number_format(MathEquation *equation);
+
+void math_equation_set_base(MathEquation *equation, gint base);
+gint math_equation_get_base(MathEquation *equation);
+
+void math_equation_set_word_size(MathEquation *equation, gint word_size);
+gint math_equation_get_word_size(MathEquation *equation);
+
+void math_equation_set_angle_units(MathEquation *equation, MPAngleUnit angle_unit);
+MPAngleUnit math_equation_get_angle_units(MathEquation *equation);
+
+void math_equation_set_source_currency(MathEquation *equation, const gchar *currency);
+const gchar *math_equation_get_source_currency(MathEquation *equation);
+
+void math_equation_set_target_currency(MathEquation *equation, const gchar *currency);
+const gchar *math_equation_get_target_currency(MathEquation *equation);
+
+void math_equation_set_source_units(MathEquation *equation, const gchar *units);
+const gchar *math_equation_get_source_units(MathEquation *equation);
+
+void math_equation_set_target_units(MathEquation *equation, const gchar *units);
+const gchar *math_equation_get_target_units(MathEquation *equation);
+
+gboolean math_equation_in_solve(MathEquation *equation);
+
+const MPNumber *math_equation_get_answer(MathEquation *equation);
+MpSerializer *math_equation_get_serializer(MathEquation *equation);
+
+void math_equation_copy(MathEquation *equation);
+void math_equation_paste(MathEquation *equation);
+void math_equation_undo(MathEquation *equation);
+void math_equation_redo(MathEquation *equation);
+void math_equation_store(MathEquation *equation, const gchar *name);
+void math_equation_recall(MathEquation *equation, const gchar *name);
+void math_equation_set(MathEquation *equation, const gchar *text);
+void math_equation_set_number(MathEquation *equation, const MPNumber *x);
+void math_equation_insert(MathEquation *equation, const gchar *text);
+void math_equation_insert_digit(MathEquation *equation, guint digit);
+void math_equation_insert_numeric_point(MathEquation *equation);
+void math_equation_insert_number(MathEquation *equation, const MPNumber *x);
+void math_equation_insert_subtract(MathEquation *equation);
+void math_equation_insert_exponent(MathEquation *equation);
+void math_equation_solve(MathEquation *equation);
+void math_equation_factorize(MathEquation *equation);
+void math_equation_delete(MathEquation *equation);
+void math_equation_backspace(MathEquation *equation);
+void math_equation_clear(MathEquation *equation);
+void math_equation_shift(MathEquation *equation, gint count);
+void math_equation_toggle_bit(MathEquation *equation, guint bit);
+
+G_END_DECLS
#endif /* MATH_EQUATION_H */
diff --git a/src/math-preferences.c b/src/math-preferences.c
index ab651df..07190de 100644
--- a/src/math-preferences.c
+++ b/src/math-preferences.c
@@ -1,19 +1,11 @@
-/* Copyright (c) 2008-2009 Robert Ancell
+/*
+ * 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 <glib/gi18n.h>
@@ -41,8 +33,8 @@ struct MathPreferencesDialogPrivate
MathPreferencesDialog *
math_preferences_dialog_new(MathEquation *equation)
-{
- return g_object_new (math_preferences_get_type(), "equation", equation, NULL);
+{
+ return g_object_new(math_preferences_get_type(), "equation", equation, NULL);
}
@@ -66,7 +58,7 @@ G_MODULE_EXPORT
void
number_format_combobox_changed_cb(GtkWidget *combo, MathPreferencesDialog *dialog)
{
- DisplayFormat value;
+ MpDisplayFormat value;
GtkTreeModel *model;
GtkTreeIter iter;
@@ -211,7 +203,7 @@ word_size_cb(MathEquation *equation, GParamSpec *spec, MathPreferencesDialog *di
static void
angle_unit_cb(MathEquation *equation, GParamSpec *spec, MathPreferencesDialog *dialog)
{
- set_combo_box_from_int(GET_WIDGET(dialog->priv->ui, "angle_unit_combobox"), math_equation_get_angle_units(equation));
+ set_combo_box_from_int(GET_WIDGET(dialog->priv->ui, "angle_unit_combobox"), math_equation_get_angle_units(equation));
}
@@ -237,9 +229,7 @@ create_gui(MathPreferencesDialog *dialog)
gtk_window_set_title(GTK_WINDOW(dialog),
/* Title of preferences dialog */
_("Preferences"));
- gtk_window_set_icon_name(GTK_WINDOW(dialog), "accessories-calculator");
gtk_container_set_border_width(GTK_CONTAINER(dialog), 8);
- gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
gtk_dialog_add_button(GTK_DIALOG(dialog),
/* Label on close button in preferences dialog */
_("_Close"), 0);
@@ -269,16 +259,20 @@ create_gui(MathPreferencesDialog *dialog)
model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
+ /* Number display mode combo: Automatic, e.g. 1234 (or scientific for large number 1.234×10^99) */
+ _("Automatic"), 1, MP_DISPLAY_FORMAT_AUTOMATIC, -1);
+ gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
/* Number display mode combo: Fixed, e.g. 1234 */
- _("Fixed"), 1, FIX, -1);
+ _("Fixed"), 1, MP_DISPLAY_FORMAT_FIXED, -1);
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
/* Number display mode combo: Scientific, e.g. 1.234×10^3 */
- _("Scientific"), 1, SCI, -1);
+ _("Scientific"), 1, MP_DISPLAY_FORMAT_SCIENTIFIC, -1);
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
/* Number display mode combo: Engineering, e.g. 1.234k */
- _("Engineering"), 1, ENG, -1);
+ _("Engineering"), 1, MP_DISPLAY_FORMAT_ENGINEERING, -1);
renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 0);
@@ -333,21 +327,21 @@ create_gui(MathPreferencesDialog *dialog)
static void
math_preferences_set_property(GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
MathPreferencesDialog *self;
- self = MATH_PREFERENCES (object);
+ self = MATH_PREFERENCES(object);
switch (prop_id) {
case PROP_EQUATION:
- self->priv->equation = g_value_get_object (value);
+ self->priv->equation = g_value_get_object(value);
create_gui(self);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
@@ -355,34 +349,34 @@ math_preferences_set_property(GObject *object,
static void
math_preferences_get_property(GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
MathPreferencesDialog *self;
- self = MATH_PREFERENCES (object);
+ self = MATH_PREFERENCES(object);
switch (prop_id) {
case PROP_EQUATION:
- g_value_set_object (value, self->priv->equation);
+ g_value_set_object(value, self->priv->equation);
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_preferences_class_init (MathPreferencesDialogClass *klass)
+math_preferences_class_init(MathPreferencesDialogClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->get_property = math_preferences_get_property;
object_class->set_property = math_preferences_set_property;
- g_type_class_add_private (klass, sizeof (MathPreferencesDialogPrivate));
+ g_type_class_add_private(klass, sizeof(MathPreferencesDialogPrivate));
g_object_class_install_property(object_class,
PROP_EQUATION,
@@ -397,5 +391,5 @@ math_preferences_class_init (MathPreferencesDialogClass *klass)
static void
math_preferences_init(MathPreferencesDialog *dialog)
{
- dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog, math_preferences_get_type(), MathPreferencesDialogPrivate);
+ dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE(dialog, math_preferences_get_type(), MathPreferencesDialogPrivate);
}
diff --git a/src/math-preferences.h b/src/math-preferences.h
index ca7430e..0cfd6e1 100644
--- a/src/math-preferences.h
+++ b/src/math-preferences.h
@@ -1,19 +1,11 @@
-/* Copyright (c) 2008-2009 Robert Ancell
+/*
+ * 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.
*/
#ifndef MATH_PREFERENCES_H
@@ -29,18 +21,20 @@ G_BEGIN_DECLS
typedef struct MathPreferencesDialogPrivate MathPreferencesDialogPrivate;
-typedef struct {
- GtkDialog parent_instance;
- MathPreferencesDialogPrivate* priv;
+typedef struct
+{
+ GtkDialog parent_instance;
+ MathPreferencesDialogPrivate *priv;
} MathPreferencesDialog;
-typedef struct {
- GtkDialogClass parent_class;
+typedef struct
+{
+ GtkDialogClass parent_class;
} MathPreferencesDialogClass;
GType math_preferences_get_type(void);
-MathPreferencesDialog* math_preferences_dialog_new(MathEquation* equation);
+MathPreferencesDialog *math_preferences_dialog_new(MathEquation *equation);
G_END_DECLS
diff --git a/src/math-variable-popup.c b/src/math-variable-popup.c
new file mode 100644
index 0000000..d033571
--- /dev/null
+++ b/src/math-variable-popup.c
@@ -0,0 +1,308 @@
+/*
+ * 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 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 <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "math-variable-popup.h"
+
+enum {
+ PROP_0,
+ PROP_EQUATION
+};
+
+struct MathVariablePopupPrivate
+{
+ MathEquation *equation;
+
+ GtkWidget *vbox;
+ GtkWidget *variable_name_entry;
+ GtkWidget *add_variable_button;
+};
+
+G_DEFINE_TYPE (MathVariablePopup, math_variable_popup, GTK_TYPE_WINDOW);
+
+MathVariablePopup *
+math_variable_popup_new(MathEquation *equation)
+{
+ return g_object_new(math_variable_popup_get_type(), "equation", equation, NULL);
+}
+
+
+static void
+variable_focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event, MathVariablePopup *popup)
+{
+ gtk_widget_destroy(widget);
+}
+
+
+static void
+insert_variable_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *name;
+
+ name = g_object_get_data(G_OBJECT(widget), "variable_name");
+ math_equation_insert(popup->priv->equation, name);
+
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+
+static gboolean
+variable_name_key_press_cb(GtkWidget *widget, GdkEventKey *event, MathVariablePopup *popup)
+{
+ /* Can't have whitespace in names, so replace with underscores */
+ if (event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
+ event->keyval = GDK_KEY_underscore;
+
+ return FALSE;
+}
+
+
+static void
+variable_name_changed_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *text = gtk_entry_get_text(GTK_ENTRY(popup->priv->variable_name_entry));
+ gtk_widget_set_sensitive(popup->priv->add_variable_button, text[0] != '\0');
+}
+
+
+static void
+add_variable_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *name;
+ MPNumber z;
+
+ name = gtk_entry_get_text(GTK_ENTRY(popup->priv->variable_name_entry));
+ if (name[0] == '\0')
+ return;
+
+ if (math_equation_get_number(popup->priv->equation, &z))
+ math_variables_set(math_equation_get_variables(popup->priv->equation), name, &z);
+ else if (math_equation_is_result(popup->priv->equation))
+ math_variables_set(math_equation_get_variables(popup->priv->equation), name, math_equation_get_answer(popup->priv->equation));
+ else
+ g_warning("Can't add variable %s, the display is not a number", name);
+
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+
+static void
+save_variable_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *name;
+ MPNumber z;
+
+ name = g_object_get_data(G_OBJECT(widget), "variable_name");
+ if (math_equation_get_number(popup->priv->equation, &z))
+ math_variables_set(math_equation_get_variables(popup->priv->equation), name, &z);
+ else if (math_equation_is_result(popup->priv->equation))
+ math_variables_set(math_equation_get_variables(popup->priv->equation), name, math_equation_get_answer(popup->priv->equation));
+ else
+ g_warning("Can't save variable %s, the display is not a number", name);
+
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+
+static void
+delete_variable_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *name;
+
+ name = g_object_get_data(G_OBJECT(widget), "variable_name");
+ math_variables_delete(math_equation_get_variables(popup->priv->equation), name);
+
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+
+static GtkWidget *
+make_variable_entry(MathVariablePopup *popup, const gchar *name, const MPNumber *value, gboolean writable)
+{
+ GtkWidget *hbox, *button, *label;
+ gchar *text;
+
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+
+ if (value)
+ {
+ gchar *value_text = mp_serializer_to_string(math_equation_get_serializer(popup->priv->equation), value);
+ text = g_strdup_printf("<b>%s</b> = %s", name, value_text);
+ g_free (value_text);
+ }
+ else
+ text = g_strdup_printf("<b>%s</b>", name);
+
+ button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "variable_name", g_strdup(name)); // FIXME: These will all leak memory
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(insert_variable_cb), popup);
+ gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ label = gtk_label_new(text);
+ g_free(text);
+ gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ gtk_container_add(GTK_CONTAINER(button), label);
+ gtk_widget_show(label);
+
+ if (writable)
+ {
+ GtkWidget *image;
+
+ button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "variable_name", g_strdup(name));
+ image = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(button), image);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(save_variable_cb), popup);
+ gtk_widget_show(image);
+ gtk_widget_show(button);
+
+ button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "variable_name", g_strdup(name));
+ image = gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(button), image);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(delete_variable_cb), popup);
+ gtk_widget_show(image);
+ gtk_widget_show(button);
+ }
+
+ return hbox;
+}
+
+
+static void
+math_variable_popup_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MathVariablePopup *self;
+ gchar **names;
+ int i;
+ GtkWidget *entry, *image;
+
+ self = MATH_VARIABLE_POPUP(object);
+
+ switch (prop_id) {
+ case PROP_EQUATION:
+ self->priv->equation = g_value_get_object(value);
+
+ names = math_variables_get_names(math_equation_get_variables(self->priv->equation));
+ for (i = 0; names[i]; i++) {
+ MPNumber *value;
+
+ value = math_variables_get(math_equation_get_variables(self->priv->equation), names[i]);
+ entry = make_variable_entry(self, names[i], value, TRUE);
+ gtk_widget_show(entry);
+ gtk_box_pack_start(GTK_BOX(self->priv->vbox), entry, FALSE, TRUE, 0);
+ }
+ g_strfreev(names);
+
+ entry = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_widget_show(entry);
+
+ // TODO: Show greyed "variable name" text to give user a hint how to use
+ self->priv->variable_name_entry = gtk_entry_new();
+ g_signal_connect(G_OBJECT(self->priv->variable_name_entry), "key-press-event", G_CALLBACK(variable_name_key_press_cb), self);
+ g_signal_connect(G_OBJECT(self->priv->variable_name_entry), "changed", G_CALLBACK(variable_name_changed_cb), self);
+ g_signal_connect(G_OBJECT(self->priv->variable_name_entry), "activate", G_CALLBACK(add_variable_cb), self);
+ gtk_box_pack_start(GTK_BOX(entry), self->priv->variable_name_entry, TRUE, TRUE, 0);
+ gtk_widget_show(self->priv->variable_name_entry);
+
+ self->priv->add_variable_button = gtk_button_new();
+ gtk_widget_set_sensitive(self->priv->add_variable_button, FALSE);
+ g_signal_connect(G_OBJECT(self->priv->add_variable_button), "clicked", G_CALLBACK(add_variable_cb), self);
+ image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(self->priv->add_variable_button), image);
+ gtk_box_pack_start(GTK_BOX(entry), self->priv->add_variable_button, FALSE, TRUE, 0);
+ gtk_widget_show(image);
+ gtk_widget_show(self->priv->add_variable_button);
+ gtk_box_pack_end(GTK_BOX(self->priv->vbox), entry, FALSE, TRUE, 0);
+
+ entry = make_variable_entry(self, "rand", NULL, FALSE);
+ gtk_widget_show(entry);
+ gtk_box_pack_end(GTK_BOX(self->priv->vbox), entry, FALSE, TRUE, 0);
+
+ entry = make_variable_entry(self, "ans", math_equation_get_answer(self->priv->equation), FALSE);
+ gtk_widget_show(entry);
+ gtk_box_pack_end(GTK_BOX(self->priv->vbox), entry, FALSE, TRUE, 0);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+math_variable_popup_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MathVariablePopup *self;
+
+ self = MATH_VARIABLE_POPUP(object);
+
+ switch (prop_id) {
+ case PROP_EQUATION:
+ g_value_set_object(value, self->priv->equation);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+math_variable_popup_class_init(MathVariablePopupClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = math_variable_popup_get_property;
+ object_class->set_property = math_variable_popup_set_property;
+
+ g_type_class_add_private(klass, sizeof(MathVariablePopupPrivate));
+
+ g_object_class_install_property(object_class,
+ PROP_EQUATION,
+ g_param_spec_object("equation",
+ "equation",
+ "Equation being controlled",
+ math_equation_get_type(),
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+
+static void
+math_variable_popup_init(MathVariablePopup *popup)
+{
+ popup->priv = G_TYPE_INSTANCE_GET_PRIVATE(popup, math_variable_popup_get_type(), MathVariablePopupPrivate);
+
+ gtk_window_set_decorated(GTK_WINDOW(popup), FALSE);
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(popup), TRUE);
+
+ gtk_container_set_border_width(GTK_CONTAINER(popup), 6);
+
+ /* Destroy this window when it loses focus */
+ g_signal_connect(G_OBJECT(popup), "focus-out-event", G_CALLBACK(variable_focus_out_event_cb), popup);
+
+ popup->priv->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_set_homogeneous(GTK_BOX(popup->priv->vbox), TRUE);
+ gtk_container_add(GTK_CONTAINER(popup), popup->priv->vbox);
+ gtk_widget_show(popup->priv->vbox);
+}
diff --git a/src/math-variable-popup.h b/src/math-variable-popup.h
new file mode 100644
index 0000000..3c24b54
--- /dev/null
+++ b/src/math-variable-popup.h
@@ -0,0 +1,40 @@
+/*
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef MATH_VARIABLE_POPUP_H
+#define MATH_VARIABLE_POPUP_H
+
+#include <gtk/gtk.h>
+#include "math-equation.h"
+
+G_BEGIN_DECLS
+
+#define MATH_VARIABLE_POPUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), math_variable_popup_get_type(), MathVariablePopup))
+
+typedef struct MathVariablePopupPrivate MathVariablePopupPrivate;
+
+typedef struct
+{
+ GtkWindow parent_instance;
+ MathVariablePopupPrivate *priv;
+} MathVariablePopup;
+
+typedef struct
+{
+ GtkWindowClass parent_class;
+} MathVariablePopupClass;
+
+GType math_variable_popup_get_type(void);
+
+MathVariablePopup *math_variable_popup_new(MathEquation *equation);
+
+G_END_DECLS
+
+#endif /* MATH_VARIABLE_POPUP_H */
diff --git a/src/math-variables.c b/src/math-variables.c
index a76261c..7e20cdc 100644
--- a/src/math-variables.c
+++ b/src/math-variables.c
@@ -1,31 +1,25 @@
-/* Copyright (c) 2008-2009 Robert Ancell
+/*
+ * 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 <stdio.h>
#include <string.h>
#include "math-variables.h"
+#include "mp-serializer.h"
struct MathVariablesPrivate
{
gchar *file_name;
GHashTable *registers;
+ MpSerializer *serializer;
};
G_DEFINE_TYPE (MathVariables, math_variables, G_TYPE_OBJECT);
@@ -43,24 +37,24 @@ registers_load(MathVariables *variables)
{
FILE *f;
char line[1024];
-
+
f = fopen(variables->priv->file_name, "r");
if (!f)
return;
-
+
g_hash_table_remove_all(variables->priv->registers);
while (fgets(line, 1024, f) != NULL)
{
char *name, *value;
MPNumber *t;
-
+
value = strchr(line, '=');
if (!value)
continue;
*value = '\0';
value = value + 1;
-
+
name = g_strstrip(line);
value = g_strstrip(value);
@@ -89,16 +83,17 @@ registers_save(MathVariables *variables)
f = fopen(variables->priv->file_name, "w");
if (!f)
return;
-
+
g_hash_table_iter_init(&iter, variables->priv->registers);
while (g_hash_table_iter_next(&iter, &key, &val))
{
gchar *name = key;
MPNumber *value = val;
- char number[1024];
+ char *number;
- mp_cast_to_string(value, 10, 10, 50, TRUE, number, 1024);
+ number = mp_serializer_to_string(variables->priv->serializer, value);
fprintf(f, "%s=%s\n", name, number);
+ g_free(number);
}
fclose(f);
}
@@ -112,6 +107,8 @@ math_variables_get_names(MathVariables *variables)
gpointer key;
gint i = 0;
gchar **names;
+
+ g_return_val_if_fail(variables != NULL, NULL);
names = g_malloc0(sizeof(gchar *) * (g_hash_table_size(variables->priv->registers) + 1));
@@ -129,9 +126,14 @@ math_variables_get_names(MathVariables *variables)
void
-math_variables_set_value(MathVariables *variables, const char *name, const MPNumber *value)
+math_variables_set(MathVariables *variables, const char *name, const MPNumber *value)
{
MPNumber *t;
+
+ g_return_if_fail(variables != NULL);
+ g_return_if_fail(name != NULL);
+ g_return_if_fail(value != NULL);
+
t = g_malloc(sizeof(MPNumber));
mp_set_from_mp(value, t);
g_hash_table_insert(variables->priv->registers, g_strdup(name), t);
@@ -140,12 +142,24 @@ math_variables_set_value(MathVariables *variables, const char *name, const MPNum
MPNumber *
-math_variables_get_value(MathVariables *variables, const char *name)
+math_variables_get(MathVariables *variables, const char *name)
{
+ g_return_val_if_fail(variables != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
return g_hash_table_lookup(variables->priv->registers, name);
}
+void
+math_variables_delete(MathVariables *variables, const char *name)
+{
+ g_return_if_fail(variables != NULL);
+ g_return_if_fail(name != NULL);
+ g_hash_table_remove(variables->priv->registers, name);
+ registers_save(variables);
+}
+
+
static void
math_variables_class_init (MathVariablesClass *klass)
{
@@ -159,5 +173,7 @@ math_variables_init(MathVariables *variables)
variables->priv = G_TYPE_INSTANCE_GET_PRIVATE (variables, math_variables_get_type(), MathVariablesPrivate);
variables->priv->registers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
variables->priv->file_name = g_build_filename(g_get_user_data_dir(), "mate-calc", "registers", NULL);
+ variables->priv->serializer = mp_serializer_new(MP_DISPLAY_FORMAT_SCIENTIFIC, 10, 50);
+ mp_serializer_set_radix(variables->priv->serializer, '.');
registers_load(variables);
}
diff --git a/src/math-variables.h b/src/math-variables.h
index e846ace..ade4134 100644
--- a/src/math-variables.h
+++ b/src/math-variables.h
@@ -1,19 +1,11 @@
-/* Copyright (c) 2008-2009 Robert Ancell
+/*
+ * 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.
*/
#ifndef MATH_VARIABLES_H
@@ -28,23 +20,29 @@ G_BEGIN_DECLS
typedef struct MathVariablesPrivate MathVariablesPrivate;
-typedef struct {
- GObject parent_instance;
- MathVariablesPrivate* priv;
+typedef struct
+{
+ GObject parent_instance;
+ MathVariablesPrivate *priv;
} MathVariables;
-typedef struct {
- GObjectClass parent_class;
+typedef struct
+{
+ GObjectClass parent_class;
} MathVariablesClass;
GType math_variables_get_type(void);
-MathVariables* math_variables_new(void);
+MathVariables *math_variables_new(void);
+
+gchar **math_variables_get_names(MathVariables *variables);
+
+void math_variables_set(MathVariables *variables, const char *name, const MPNumber *value);
-gchar** math_variables_get_names(MathVariables* variables);
+MPNumber *math_variables_get(MathVariables *variables, const char *name);
-void math_variables_set_value(MathVariables* variables, const char* name, const MPNumber* value);
+void math_variables_delete(MathVariables *variables, const char *name);
-MPNumber* math_variables_get_value(MathVariables* variables, const char* name);
+G_END_DECLS
#endif /* MATH_VARIABLES_H */
diff --git a/src/math-window.c b/src/math-window.c
index 1caccab..4ecc5f7 100644
--- a/src/math-window.c
+++ b/src/math-window.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 <glib/gi18n.h>
@@ -22,472 +14,234 @@
#include <gdk/gdkkeysyms.h>
#include "math-window.h"
-#include "math-preferences.h"
enum {
PROP_0,
PROP_EQUATION
};
-struct MathWindowPrivate {
- GtkWidget* menu_bar;
- MathEquation* equation;
- MathDisplay* display;
- MathButtons* buttons;
- MathPreferencesDialog* preferences_dialog;
+struct MathWindowPrivate
+{
+ MathEquation *equation;
+ MathDisplay *display;
+ MathButtons *buttons;
gboolean right_aligned;
- GtkWidget* mode_basic_menu_item;
- GtkWidget* mode_advanced_menu_item;
- GtkWidget* mode_financial_menu_item;
- GtkWidget* mode_programming_menu_item;
-};
-
-G_DEFINE_TYPE(MathWindow, math_window, GTK_TYPE_WINDOW);
-
-enum {
- QUIT,
- LAST_SIGNAL
};
-static guint signals[LAST_SIGNAL] = {0, };
+G_DEFINE_TYPE (MathWindow, math_window, GTK_TYPE_APPLICATION_WINDOW);
-MathWindow* math_window_new(MathEquation* equation)
+MathWindow *
+math_window_new(GtkApplication *app, MathEquation *equation)
{
- return g_object_new(math_window_get_type(), "equation", equation, NULL);
+ return g_object_new(math_window_get_type(),
+ "application", app,
+ "equation", equation, NULL);
}
-GtkWidget* math_window_get_menu_bar(MathWindow* window)
-{
- return window->priv->menu_bar;
-}
-
-
-MathEquation* math_window_get_equation(MathWindow* window)
+MathEquation *
+math_window_get_equation(MathWindow *window)
{
+ g_return_val_if_fail(window != NULL, NULL);
return window->priv->equation;
}
-MathDisplay* math_window_get_display(MathWindow* window)
+MathDisplay *
+math_window_get_display(MathWindow *window)
{
+ g_return_val_if_fail(window != NULL, NULL);
return window->priv->display;
}
-MathButtons* math_window_get_buttons(MathWindow* window)
+MathButtons *
+math_window_get_buttons(MathWindow *window)
{
+ g_return_val_if_fail(window != NULL, NULL);
return window->priv->buttons;
}
-void math_window_critical_error(MathWindow* window, const gchar* title, const gchar* contents)
+void
+math_window_critical_error(MathWindow *window, const gchar *title, const gchar *contents)
{
- GtkWidget* dialog;
+ GtkWidget *dialog;
- dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, "%s", title);
- gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", contents);
+ g_return_if_fail(window != NULL);
+ g_return_if_fail(title != NULL);
+ g_return_if_fail(contents != NULL);
+
+ dialog = gtk_message_dialog_new(NULL, 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_NONE,
+ "%s", title);
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
+ "%s", contents);
gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_QUIT, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_run(GTK_DIALOG(dialog));
- g_signal_emit(window, signals[QUIT], 0);
-}
-
-
-static void copy_cb(GtkWidget* widget, MathWindow* window)
-{
- math_equation_copy(window->priv->equation);
-}
-
-
-static void paste_cb(GtkWidget* widget, MathWindow* window)
-{
- math_equation_paste(window->priv->equation);
-}
-
-
-static void undo_cb(GtkWidget* widget, MathWindow* window)
-{
- math_equation_undo(window->priv->equation);
-}
-
-
-static void redo_cb(GtkWidget* widget, MathWindow* window)
-{
- math_equation_redo(window->priv->equation);
-}
-
-
-static void mode_changed_cb(GtkWidget* menu, MathWindow* window)
-{
- int mode;
-
- if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)))
- {
- return;
- }
-
- mode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu), "calcmode"));
- math_buttons_set_mode(window->priv->buttons, mode);
-}
-
-
-static void show_preferences_cb(GtkMenuItem* menu, MathWindow* window)
-{
- if (!window->priv->preferences_dialog)
- {
- window->priv->preferences_dialog = math_preferences_dialog_new(window->priv->equation);
- gtk_window_set_transient_for(GTK_WINDOW(window->priv->preferences_dialog), GTK_WINDOW(window));
- }
-
- gtk_window_present(GTK_WINDOW(window->priv->preferences_dialog));
-}
-
-
-static void help_cb(GtkWidget* widget, MathWindow* window)
-{
- GdkScreen* screen;
- GError* error = NULL;
-
- screen = gtk_widget_get_screen(GTK_WIDGET(window));
- gtk_show_uri(screen, "ghelp:mate-calc", gtk_get_current_event_time(), &error);
-
- if (error != NULL)
- {
- GtkWidget* d;
- /* Translators: Error message displayed when unable to launch help browser */
- const char* message = _("Unable to open help file");
-
- d = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message);
- gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(d), "%s", error->message);
- g_signal_connect(d, "response", G_CALLBACK(gtk_widget_destroy), NULL);
- gtk_window_present(GTK_WINDOW (d));
-
- g_error_free(error);
- }
-}
-
-
-static void about_cb(GtkWidget* widget, MathWindow* window)
-{
- char* authors[] = {
- "Rich Burridge <[email protected]>",
- "Robert Ancell <[email protected]>",
- "Klaus Niederkrüger <[email protected]>",
- NULL
- };
-
- char* documenters[] = {
- "Sun Microsystems",
- NULL
- };
-
- /* The translator credits. Please translate this with your name(s). */
- char* translator_credits = _("translator-credits");
-
- char copyright[] = \
- "Copyright \xc2\xa9 1986–2010 The GCalctool authors\n"
- "Copyright \xc2\xa9 2011-2012 MATE developers";
-
- /* The license this software is under (GPL2+) */
- char* license = _("mate-calc is free software; you can redistribute it and/or modify\n"
- "it under the terms of the GNU General Public License as published by\n"
- "the Free Software Foundation; either version 2 of the License, or\n"
- "(at your option) any later version.\n"
- "\n"
- "mate-calc is distributed in the hope that it will be useful,\n"
- "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
- "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
- "GNU General Public License for more details.\n"
- "\n"
- "You should have received a copy of the GNU General Public License\n"
- "along with mate-calc; if not, write to the Free Software Foundation, Inc.,\n"
- "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA");
-
- gtk_show_about_dialog(GTK_WINDOW(window),
- "name", _("mate-calc"),
- "version", VERSION,
- "copyright", copyright,
- "license", license,
- "comments", _("Calculator with financial and scientific modes."),
- "authors", authors,
- "documenters", documenters,
- "translator_credits", translator_credits,
- "logo-icon-name", "accessories-calculator",
- "website", "http://mate-desktop.org",
- NULL);
+ gtk_widget_destroy(GTK_WIDGET(window));
}
-static void quit_cb(GtkWidget* widget, MathWindow* window)
-{
- g_signal_emit(window, signals[QUIT], 0);
-}
-
-
-static gboolean key_press_cb(MathWindow* window, GdkEventKey* event)
+static gboolean
+key_press_cb(MathWindow *window, GdkEventKey *event)
{
gboolean result;
-
g_signal_emit_by_name(window->priv->display, "key-press-event", event, &result);
- return result;
-}
-
+ if (math_buttons_get_mode (window->priv->buttons) == PROGRAMMING && (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) {
+ switch(event->keyval)
+ {
+ /* Binary */
+ case GDK_KEY_b:
+ math_equation_set_base (window->priv->equation, 2);
+ return TRUE;
+ /* Octal */
+ case GDK_KEY_o:
+ math_equation_set_base (window->priv->equation, 8);
+ return TRUE;
+ /* Decimal */
+ case GDK_KEY_d:
+ math_equation_set_base (window->priv->equation, 10);
+ return TRUE;
+ /* Hexdecimal */
+ case GDK_KEY_h:
+ math_equation_set_base (window->priv->equation, 16);
+ return TRUE;
+ }
+ }
-static void delete_cb(MathWindow* window, GdkEvent* event)
-{
- g_signal_emit(window, signals[QUIT], 0);
+ return result;
}
-static void scroll_changed_cb(GtkAdjustment* adjustment, MathWindow* window)
+static void
+scroll_changed_cb(GtkAdjustment *adjustment, MathWindow *window)
{
if (window->priv->right_aligned)
- {
gtk_adjustment_set_value(adjustment, gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
- }
}
-static void scroll_value_changed_cb(GtkAdjustment* adjustment, MathWindow* window)
+static void
+scroll_value_changed_cb(GtkAdjustment *adjustment, MathWindow *window)
{
if (gtk_adjustment_get_value(adjustment) == gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment))
- {
window->priv->right_aligned = TRUE;
- }
else
- {
window->priv->right_aligned = FALSE;
- }
-}
-
-
-static void button_mode_changed_cb(MathButtons* buttons, GParamSpec* spec, MathWindow* window)
-{
- GtkWidget* menu;
-
- switch(math_buttons_get_mode(buttons))
- {
- default:
- case BASIC:
- menu = window->priv->mode_basic_menu_item;
- //FIXME: Should it revert to decimal mode? math_equation_set_number_format(window->priv->equation, DEC);
- break;
-
- case ADVANCED:
- menu = window->priv->mode_advanced_menu_item;
- break;
-
- case FINANCIAL:
- menu = window->priv->mode_financial_menu_item;
- break;
-
- case PROGRAMMING:
- menu = window->priv->mode_programming_menu_item;
- break;
- }
-
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
-}
-
-
-static GtkWidget* add_menu(GtkWidget* menu_bar, const gchar* name)
-{
- GtkWidget* menu_item;
- GtkWidget* menu;
-
- menu_item = gtk_menu_item_new_with_mnemonic(name);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_item);
- gtk_widget_show(menu_item);
- menu = gtk_menu_new();
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
-
- return menu;
}
-static GtkWidget* add_menu_item(GtkWidget* menu, GtkWidget* menu_item, GCallback callback, gpointer callback_data)
+static void
+create_gui(MathWindow *window)
{
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
- gtk_widget_show(menu_item);
-
- if (callback)
- {
- g_signal_connect(G_OBJECT(menu_item), "activate", callback, callback_data);
- }
-
- return menu_item;
-}
-
-
-static GtkWidget* radio_menu_item_new(GSList** group, const gchar* name)
-{
- GtkWidget* menu_item = gtk_radio_menu_item_new_with_mnemonic(*group, name);
-
- *group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menu_item));
-
- return menu_item;
-}
-
-
-static void create_menu(MathWindow* window)
-{
- GtkAccelGroup* accel_group;
- GtkWidget* menu;
- GtkWidget* menu_item;
- GSList* group = NULL;
-
- accel_group = gtk_accel_group_new();
- gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
-
- /* Calculator menu */
- #define CALCULATOR_MENU_LABEL _("_Calculator")
- /* Mode menu */
- #define MODE_MENU_LABEL _("_Mode")
- /* Help menu label */
- #define HELP_MENU_LABEL _("_Help")
- /* Basic menu label */
- #define MODE_BASIC_LABEL _("_Basic")
- /* Advanced menu label */
- #define MODE_ADVANCED_LABEL _("_Advanced")
- /* Financial menu label */
- #define MODE_FINANCIAL_LABEL _("_Financial")
- /* Programming menu label */
- #define MODE_PROGRAMMING_LABEL _("_Programming")
- /* Help>Contents menu label */
- #define HELP_CONTENTS_LABEL _("_Contents")
-
- menu = add_menu(window->priv->menu_bar, CALCULATOR_MENU_LABEL);
- add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_COPY, accel_group), G_CALLBACK(copy_cb), window);
- add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_PASTE, accel_group), G_CALLBACK(paste_cb), window);
- menu_item = add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_UNDO, accel_group), G_CALLBACK(undo_cb), window);
- gtk_widget_add_accelerator(menu_item, "activate", accel_group, GDK_z, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
- menu_item = add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_REDO, accel_group), G_CALLBACK(redo_cb), window);
- gtk_widget_add_accelerator(menu_item, "activate", accel_group, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
- add_menu_item(menu, gtk_separator_menu_item_new(), NULL, NULL);
- add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, accel_group), G_CALLBACK(show_preferences_cb), window);
- add_menu_item(menu, gtk_separator_menu_item_new(), NULL, NULL);
- menu_item = add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group), G_CALLBACK(quit_cb), window);
- gtk_widget_add_accelerator(menu_item, "activate", accel_group, GDK_w, GDK_CONTROL_MASK, 0);
-
- menu = add_menu(window->priv->menu_bar, MODE_MENU_LABEL);
- window->priv->mode_basic_menu_item = add_menu_item(menu, radio_menu_item_new(&group, MODE_BASIC_LABEL), G_CALLBACK(mode_changed_cb), window);
- g_object_set_data(G_OBJECT(window->priv->mode_basic_menu_item), "calcmode", GINT_TO_POINTER(BASIC));
- window->priv->mode_advanced_menu_item = add_menu_item(menu, radio_menu_item_new(&group, MODE_ADVANCED_LABEL), G_CALLBACK(mode_changed_cb), window);
- g_object_set_data(G_OBJECT(window->priv->mode_advanced_menu_item), "calcmode", GINT_TO_POINTER(ADVANCED));
- window->priv->mode_financial_menu_item = add_menu_item(menu, radio_menu_item_new(&group, MODE_FINANCIAL_LABEL), G_CALLBACK(mode_changed_cb), window);
- g_object_set_data(G_OBJECT(window->priv->mode_financial_menu_item), "calcmode", GINT_TO_POINTER(FINANCIAL));
- window->priv->mode_programming_menu_item = add_menu_item(menu, radio_menu_item_new(&group, MODE_PROGRAMMING_LABEL), G_CALLBACK(mode_changed_cb), window);
- g_object_set_data(G_OBJECT(window->priv->mode_programming_menu_item), "calcmode", GINT_TO_POINTER(PROGRAMMING));
-
- menu = add_menu(window->priv->menu_bar, HELP_MENU_LABEL);
- menu_item = add_menu_item(menu, gtk_menu_item_new_with_mnemonic(HELP_CONTENTS_LABEL), G_CALLBACK(help_cb), window);
- gtk_widget_add_accelerator(menu_item, "activate", accel_group, GDK_F1, 0, GTK_ACCEL_VISIBLE);
- add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, accel_group), G_CALLBACK(about_cb), window);
-}
-
-
-static void create_gui(MathWindow* window)
-{
- GtkWidget* main_vbox;
- GtkWidget* vbox;
- GtkWidget* scrolled_window;
-
- main_vbox = gtk_vbox_new(FALSE, 0);
+ GtkWidget *main_vbox, *vbox;
+ GtkWidget *scrolled_window;
+
+ main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(window), main_vbox);
gtk_widget_show(main_vbox);
- window->priv->menu_bar = gtk_menu_bar_new();
- gtk_box_pack_start(GTK_BOX(main_vbox), window->priv->menu_bar, TRUE, TRUE, 0);
- gtk_widget_show(window->priv->menu_bar);
-
- create_menu(window);
-
- vbox = gtk_vbox_new(FALSE, 6);
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
- gtk_box_pack_start(GTK_BOX(main_vbox), vbox, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(main_vbox), vbox, TRUE, TRUE, 0);
gtk_widget_show(vbox);
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_IN);
- gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolled_window), TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolled_window), FALSE, FALSE, 0);
g_signal_connect(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scrolled_window)), "changed", G_CALLBACK(scroll_changed_cb), window);
g_signal_connect(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scrolled_window)), "value-changed", G_CALLBACK(scroll_value_changed_cb), window);
window->priv->right_aligned = TRUE;
gtk_widget_show(scrolled_window);
window->priv->display = math_display_new_with_equation(window->priv->equation);
- gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), GTK_WIDGET(window->priv->display));
+ gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(window->priv->display));
gtk_widget_show(GTK_WIDGET(window->priv->display));
window->priv->buttons = math_buttons_new(window->priv->equation);
- g_signal_connect(window->priv->buttons, "notify::mode", G_CALLBACK(button_mode_changed_cb), window);
- button_mode_changed_cb(window->priv->buttons, NULL, window);
gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(window->priv->buttons), TRUE, TRUE, 0);
gtk_widget_show(GTK_WIDGET(window->priv->buttons));
}
-static void math_window_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
+static void
+math_window_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- MathWindow* self = MATH_WINDOW(object);
-
- switch (prop_id)
- {
- case PROP_EQUATION:
- self->priv->equation = g_value_get_object(value);
- create_gui(self);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
- break;
+ MathWindow *self;
+
+ self = MATH_WINDOW(object);
+
+ switch (prop_id) {
+ case PROP_EQUATION:
+ self->priv->equation = g_value_get_object(value);
+ create_gui(self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
}
}
-static void math_window_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
+static void
+math_window_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- MathWindow* self = MATH_WINDOW(object);
-
- switch (prop_id)
- {
- case PROP_EQUATION:
- g_value_set_object (value, self->priv->equation);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
+ MathWindow *self;
+
+ self = MATH_WINDOW(object);
+
+ switch (prop_id) {
+ case PROP_EQUATION:
+ g_value_set_object(value, self->priv->equation);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
}
}
-static void math_window_class_init(MathWindowClass* klass)
+static void
+math_window_class_init(MathWindowClass *klass)
{
- GObjectClass* object_class = G_OBJECT_CLASS(klass);
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->get_property = math_window_get_property;
object_class->set_property = math_window_set_property;
g_type_class_add_private(klass, sizeof(MathWindowPrivate));
- g_object_class_install_property(object_class, PROP_EQUATION, g_param_spec_object("equation", "equation", "Equation being calculated", math_equation_get_type(), G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- signals[QUIT] = g_signal_new("quit", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(MathWindowClass, quit), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ g_object_class_install_property(object_class,
+ PROP_EQUATION,
+ g_param_spec_object("equation",
+ "equation",
+ "Equation being calculated",
+ math_equation_get_type(),
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
-static void math_window_init(MathWindow* window)
+static void
+math_window_init(MathWindow *window)
{
window->priv = G_TYPE_INSTANCE_GET_PRIVATE(window, math_window_get_type(), MathWindowPrivate);
-
- gtk_window_set_title(GTK_WINDOW(window), _("Calculator")); /* Title of main window */
- gtk_window_set_icon_name(GTK_WINDOW(window), "accessories-calculator");
+ gtk_window_set_title(GTK_WINDOW(window),
+ /* Title of main window */
+ _("Calculator"));
gtk_window_set_role(GTK_WINDOW(window), "mate-calc");
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
g_signal_connect_after(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press_cb), NULL);
- g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(delete_cb), NULL);
}
diff --git a/src/math-window.h b/src/math-window.h
index 463b078..22360fa 100644
--- a/src/math-window.h
+++ b/src/math-window.h
@@ -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.
*/
#ifndef MATH_WINDOW_H
@@ -31,29 +23,31 @@ G_BEGIN_DECLS
typedef struct MathWindowPrivate MathWindowPrivate;
-typedef struct {
- GtkWindow parent_instance;
- MathWindowPrivate* priv;
+typedef struct
+{
+ GtkApplicationWindow parent_instance;
+ MathWindowPrivate *priv;
} MathWindow;
-typedef struct {
- GtkWindowClass parent_class;
+typedef struct
+{
+ GtkApplicationWindowClass parent_class;
- void (*quit) (MathWindow* window);
+ void (*quit)(MathWindow *window);
} MathWindowClass;
GType math_window_get_type(void);
-MathWindow* math_window_new(MathEquation* equation);
+MathWindow *math_window_new(GtkApplication *app, MathEquation *equation);
-GtkWidget* math_window_get_menu_bar(MathWindow* window);
+MathEquation *math_window_get_equation(MathWindow *window);
-MathEquation* math_window_get_equation(MathWindow* window);
+MathDisplay *math_window_get_display(MathWindow *window);
-MathDisplay* math_window_get_display(MathWindow* window);
+MathButtons *math_window_get_buttons(MathWindow *window);
-MathButtons* math_window_get_buttons(MathWindow* window);
+void math_window_critical_error(MathWindow *window, const gchar *title, const gchar *contents);
-void math_window_critical_error(MathWindow* window, const gchar* title, const gchar* contents);
+G_END_DECLS
#endif /* MATH_WINDOW_H */
diff --git a/src/mp-binary.c b/src/mp-binary.c
index fd78c78..4c8eafb 100644
--- a/src/mp-binary.c
+++ b/src/mp-binary.c
@@ -1,26 +1,19 @@
-/* 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 <stdio.h>
#include "mp.h"
#include "mp-private.h"
+#include "mp-serializer.h"
// FIXME: Make dynamic
#define MAX_DIGITS 1000
@@ -39,14 +32,28 @@ static int hex_to_int(char digit)
}
+static gchar *
+to_hex_string(const MPNumber *x)
+{
+ MpSerializer *serializer;
+ gchar *result;
+
+ serializer = mp_serializer_new(MP_DISPLAY_FORMAT_FIXED, 16, 0);
+ result = mp_serializer_to_string(serializer, x);
+ g_object_unref(serializer);
+
+ return result;
+}
+
+
static void
mp_bitwise(const MPNumber *x, const MPNumber *y, int (*bitwise_operator)(int, int), MPNumber *z, int wordlen)
{
- char text1[MAX_DIGITS], text2[MAX_DIGITS], text_out[MAX_DIGITS], text_out2[MAX_DIGITS];
+ char *text1, *text2, text_out[MAX_DIGITS], text_out2[MAX_DIGITS];
int offset1, offset2, offset_out;
- mp_cast_to_string(x, 16, 16, 0, 0, text1, MAX_DIGITS);
- mp_cast_to_string(y, 16, 16, 0, 0, text2, MAX_DIGITS);
+ text1 = to_hex_string(x);
+ text2 = to_hex_string(y);
offset1 = strlen(text1) - 1;
offset2 = strlen(text2) - 1;
offset_out = wordlen / 4 - 1;
@@ -54,6 +61,9 @@ mp_bitwise(const MPNumber *x, const MPNumber *y, int (*bitwise_operator)(int, in
offset_out = offset1 > offset2 ? offset1 : offset2;
}
if (offset_out > 0 && (offset_out < offset1 || offset_out < offset2)) {
+ g_free(text1);
+ g_free(text2);
+ mp_set_from_integer(0, z);
mperr("Overflow. Try a bigger word size");
return;
}
@@ -75,6 +85,8 @@ mp_bitwise(const MPNumber *x, const MPNumber *y, int (*bitwise_operator)(int, in
snprintf(text_out2, MAX_DIGITS, "%s", text_out);
mp_set_from_string(text_out2, 16, z);
+ g_free(text1);
+ g_free(text2);
}
@@ -152,24 +164,23 @@ mp_not(const MPNumber *x, int wordlen, MPNumber *z)
void
mp_mask(const MPNumber *x, int wordlen, MPNumber *z)
{
- char text[MAX_DIGITS];
+ char *text;
size_t len, offset;
/* Convert to a hexadecimal string and use last characters */
- mp_cast_to_string(x, 16, 16, 0, 0, text, MAX_DIGITS);
+ text = to_hex_string(x);
len = strlen(text);
offset = wordlen / 4;
offset = len > offset ? len - offset: 0;
mp_set_from_string(text + offset, 16, z);
+ g_free(text);
}
void
mp_shift(const MPNumber *x, int count, MPNumber *z)
{
- int i;
- MPNumber multiplier;
- mp_set_from_integer(1, &multiplier);
+ int i, multiplier = 1;
if (!mp_is_integer(x)) {
/* Translators: Error displayed when bit shift attempted on non-integer values */
@@ -179,14 +190,15 @@ mp_shift(const MPNumber *x, int count, MPNumber *z)
if (count >= 0) {
for (i = 0; i < count; i++)
- mp_multiply_integer(&multiplier, 2, &multiplier);
- mp_multiply(x, &multiplier, z);
+ multiplier *= 2;
+ mp_multiply_integer(x, multiplier, z);
}
else {
+ MPNumber temp;
for (i = 0; i < -count; i++)
- mp_multiply_integer(&multiplier, 2, &multiplier);
- mp_divide(x, &multiplier, z);
- mp_floor(z, z);
+ multiplier *= 2;
+ mp_divide_integer(x, multiplier, &temp);
+ mp_floor(&temp, z);
}
}
diff --git a/src/mp-convert.c b/src/mp-convert.c
index 7ff2fca..1052351 100644
--- a/src/mp-convert.c
+++ b/src/mp-convert.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>
@@ -22,6 +14,7 @@
#include <string.h>
#include <ctype.h>
#include <math.h>
+#include <langinfo.h>
#include "mp.h"
#include "mp-private.h"
@@ -295,7 +288,7 @@ mp_cast_to_int(const MPNumber *x)
{
int i;
int64_t z = 0, v;
-
+
/* |x| <= 1 */
if (x->sign == 0 || x->exponent <= 0)
return 0;
@@ -306,7 +299,7 @@ mp_cast_to_int(const MPNumber *x)
t = z;
z = z * MP_BASE + x->fraction[i];
-
+
/* Check for overflow */
if (z <= t)
return 0;
@@ -336,7 +329,7 @@ mp_cast_to_unsigned_int(const MPNumber *x)
{
int i;
uint64_t z = 0, v;
-
+
/* x <= 1 */
if (x->sign <= 0 || x->exponent <= 0)
return 0;
@@ -347,7 +340,7 @@ mp_cast_to_unsigned_int(const MPNumber *x)
t = z;
z = z * MP_BASE + x->fraction[i];
-
+
/* Check for overflow */
if (z <= t)
return 0;
@@ -506,221 +499,12 @@ mp_cast_to_double(const MPNumber *x)
}
}
-
-static void
-mp_cast_to_string_real(const MPNumber *x, int default_base, int base, int accuracy, bool trim_zeroes, bool force_sign, GString *string)
-{
- static char digits[] = "0123456789ABCDEF";
- MPNumber number, integer_component, fractional_component, temp;
- int i, last_non_zero;
-
- if (mp_is_negative(x))
- mp_abs(x, &number);
- else
- mp_set_from_mp(x, &number);
-
- /* Add rounding factor */
- mp_set_from_integer(base, &temp);
- mp_xpowy_integer(&temp, -(accuracy+1), &temp);
- mp_multiply_integer(&temp, base, &temp);
- mp_divide_integer(&temp, 2, &temp);
- mp_add(&number, &temp, &number);
-
- /* Split into integer and fractional component */
- mp_floor(&number, &integer_component);
- mp_fractional_component(&number, &fractional_component);
-
- /* Write out the integer component least significant digit to most */
- mp_set_from_mp(&integer_component, &temp);
- do {
- MPNumber t, t2, t3;
- int64_t d;
-
- mp_divide_integer(&temp, base, &t);
- mp_floor(&t, &t);
- mp_multiply_integer(&t, base, &t2);
-
- mp_subtract(&temp, &t2, &t3);
-
- d = mp_cast_to_int(&t3);
- g_string_prepend_c(string, d < 16 ? digits[d] : '?');
-
- mp_set_from_mp(&t, &temp);
- } while (!mp_is_zero(&temp));
-
- last_non_zero = string->len;
- g_string_append_c(string, '.');
-
- /* Write out the fractional component */
- mp_set_from_mp(&fractional_component, &temp);
- for (i = accuracy; i > 0 && !mp_is_zero(&temp); i--) {
- int d;
- MPNumber digit;
-
- mp_multiply_integer(&temp, base, &temp);
- mp_floor(&temp, &digit);
- d = mp_cast_to_int(&digit);
-
- g_string_append_c(string, digits[d]);
-
- if(d != 0)
- last_non_zero = string->len;
- mp_subtract(&temp, &digit, &temp);
- }
-
- /* Strip trailing zeroes */
- if (trim_zeroes || accuracy == 0)
- g_string_truncate(string, last_non_zero);
-
- /* Add sign on non-zero values */
- if (strcmp(string->str, "0") != 0 || force_sign) {
- if (mp_is_negative(x))
- g_string_prepend(string, "−");
- else if (force_sign)
- g_string_prepend(string, "+");
- }
-
- /* Append base suffix if not in default base */
- if (base != default_base) {
- const char *digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"};
- int multiplier = 1;
- int b = base;
-
- while (base / multiplier != 0)
- multiplier *= 10;
- while (multiplier != 1) {
- int d;
- multiplier /= 10;
- d = b / multiplier;
- g_string_append(string, digits[d]);
- b -= d * multiplier;
- }
- }
-}
-
-
-void
-mp_cast_to_string(const MPNumber *x, int default_base, int base, int accuracy, bool trim_zeroes, char *buffer, int buffer_length)
-{
- GString *string;
- MPNumber x_real;
-
- string = g_string_sized_new(buffer_length);
-
- mp_real_component(x, &x_real);
- mp_cast_to_string_real(&x_real, default_base, base, accuracy, trim_zeroes, FALSE, string);
- if (mp_is_complex(x)) {
- GString *s;
- gboolean force_sign = TRUE;
- MPNumber x_im;
-
- mp_imaginary_component(x, &x_im);
-
- if (strcmp(string->str, "0") == 0) {
- g_string_assign(string, "");
- force_sign = false;
- }
-
- s = g_string_sized_new(buffer_length);
- mp_cast_to_string_real(&x_im, default_base, 10, accuracy, trim_zeroes, force_sign, s);
- if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) {
- /* Ignore */
- }
- else if (strcmp(s->str, "1") == 0) {
- g_string_append(string, "i");
- }
- else if (strcmp(s->str, "+1") == 0) {
- g_string_append(string, "+i");
- }
- else if (strcmp(s->str, "−1") == 0) {
- g_string_append(string, "−i");
- }
- else {
- if (strcmp(s->str, "+0") == 0)
- g_string_append(string, "+");
- else if (strcmp(s->str, "0") != 0)
- g_string_append(string, s->str);
-
- g_string_append(string, "i");
- }
- g_string_free(s, TRUE);
- }
-
- // FIXME: Check for truncation
- strncpy(buffer, string->str, buffer_length);
- g_string_free(string, TRUE);
-}
-
-
-void
-mp_cast_to_exponential_string(const MPNumber *x, int default_base, int base_, int max_digits, bool trim_zeroes, bool eng_format, char *buffer, int buffer_length)
-{
- char fixed[1024], *c;
- MPNumber t, z, base, base3, base10, base10inv, mantissa;
- int exponent = 0;
- GString *string;
- const char *super_digits[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"};
-
- string = g_string_sized_new(buffer_length);
-
- mp_abs(x, &z);
- if (mp_is_negative(x))
- g_string_append(string, "−");
- mp_set_from_mp(&z, &mantissa);
-
- mp_set_from_integer(base_, &base);
- mp_xpowy_integer(&base, 3, &base3);
- mp_xpowy_integer(&base, 10, &base10);
- mp_set_from_integer(1, &t);
- mp_divide(&t, &base10, &base10inv);
-
- if (!mp_is_zero(&mantissa)) {
- while (!eng_format && mp_is_greater_equal(&mantissa, &base10)) {
- exponent += 10;
- mp_multiply(&mantissa, &base10inv, &mantissa);
- }
-
- while ((!eng_format && mp_is_greater_equal(&mantissa, &base)) ||
- (eng_format && (mp_is_greater_equal(&mantissa, &base3) || exponent % 3 != 0))) {
- exponent += 1;
- mp_divide(&mantissa, &base, &mantissa);
- }
-
- while (!eng_format && mp_is_less_than(&mantissa, &base10inv)) {
- exponent -= 10;
- mp_multiply(&mantissa, &base10, &mantissa);
- }
-
- mp_set_from_integer(1, &t);
- while (mp_is_less_than(&mantissa, &t) || (eng_format && exponent % 3 != 0)) {
- exponent -= 1;
- mp_multiply(&mantissa, &base, &mantissa);
- }
- }
-
- mp_cast_to_string(&mantissa, default_base, base_, max_digits, trim_zeroes, fixed, 1024);
- g_string_append(string, fixed);
- if (exponent != 0) {
- g_string_append_printf(string, "×10"); // FIXME: Use the current base
- if (exponent < 0) {
- exponent = -exponent;
- g_string_append(string, "⁻");
- }
- snprintf(fixed, 1024, "%d", exponent);
- for (c = fixed; *c; c++)
- g_string_append(string, super_digits[*c - '0']);
- }
-
- strncpy(buffer, string->str, buffer_length);
- g_string_free(string, TRUE);
-}
-
-
static int
char_val(char **c, int base)
{
int i, j, value, offset;
const char *digits[][10] = {{"٠", "١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩"},
+ {"〇", "〡", "〢", "〣", "〤", "〥", "〦", "〧", "〨", "〩"},
{"۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"},
{"߀", "߁", "߂", "߃", "߄", "߅", "߆", "߇", "߈", "߉"},
{"०", "१", "२", "३", "४", "५", "६", "७", "८", "९"},
@@ -895,7 +679,7 @@ mp_set_from_string(const char *str, int default_base, MPNumber *z)
mp_add(z, &fraction, z);
}
- if (*c == '.' || *c == ',') {
+ if (*c == '.') {
has_fraction = TRUE;
c++;
}
diff --git a/src/mp-enums.c.template b/src/mp-enums.c.template
new file mode 100644
index 0000000..d10ea75
--- /dev/null
+++ b/src/mp-enums.c.template
@@ -0,0 +1,36 @@
+/*** BEGIN file-header ***/
+#include "mp-serializer.h"
+#include "mp-enums.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+math_@enum_name@_get_type (void)
+{
+ static GType etype = 0;
+ if (G_UNLIKELY(etype == 0)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ }
+ return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+
+/*** END file-tail ***/
diff --git a/src/mp-enums.h.template b/src/mp-enums.h.template
new file mode 100644
index 0000000..f192bf8
--- /dev/null
+++ b/src/mp-enums.h.template
@@ -0,0 +1,25 @@
+/*** BEGIN file-header ***/
+
+#ifndef __MATH_ENUMS_H__
+#define __MATH_ENUMS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType math_@enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (math_@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __MATH_ENUMS_H__ */
+/*** END file-tail ***/
diff --git a/src/mp-equation.c b/src/mp-equation.c
index cec5536..9b6a195 100644
--- a/src/mp-equation.c
+++ b/src/mp-equation.c
@@ -1,33 +1,21 @@
-/* Copyright (c) 2004-2008 Sami Pietila
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+/*
+ * Copyright (C) 2004-2008 Sami Pietila
+ * 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 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 <ctype.h>
-
-#include "mp-equation-private.h"
-#include "mp-equation-parser.h"
-#include "mp-equation-lexer.h"
-
-extern int _mp_equation_parse(yyscan_t yyscanner);
-
+#include <string.h>
+#include <stdlib.h>
+#include "parser.h"
static int
-variable_is_defined(MPEquationParserState *state, const char *name)
+variable_is_defined(ParserState *state, const char *name)
{
/* FIXME: Make more generic */
if (strcmp(name, "e") == 0 || strcmp(name, "i") == 0 || strcmp(name, "π") == 0)
@@ -39,7 +27,7 @@ variable_is_defined(MPEquationParserState *state, const char *name)
static int
-get_variable(MPEquationParserState *state, const char *name, MPNumber *z)
+get_variable(ParserState *state, const char *name, MPNumber *z)
{
int result = 1;
@@ -58,7 +46,7 @@ get_variable(MPEquationParserState *state, const char *name, MPNumber *z)
}
static void
-set_variable(MPEquationParserState *state, const char *name, const MPNumber *x)
+set_variable(ParserState *state, const char *name, const MPNumber *x)
{
// Reserved words, e, π, mod, and, or, xor, not, abs, log, ln, sqrt, int, frac, sin, cos, ...
if (strcmp(name, "e") == 0 || strcmp(name, "i") == 0 || strcmp(name, "π") == 0)
@@ -115,7 +103,7 @@ super_atoi(const char *data)
static int
-function_is_defined(MPEquationParserState *state, const char *name)
+function_is_defined(ParserState *state, const char *name)
{
char *c, *lower_name;
@@ -140,8 +128,8 @@ function_is_defined(MPEquationParserState *state, const char *name)
strcmp(lower_name, "re") == 0 ||
strcmp(lower_name, "im") == 0 ||
strcmp(lower_name, "sin") == 0 || strcmp(lower_name, "cos") == 0 || strcmp(lower_name, "tan") == 0 ||
- strcmp(lower_name, "sin⁻¹") == 0 || strcmp(lower_name, "cos⁻¹") == 0 || strcmp(lower_name, "tan⁻¹") == 0 ||
strcmp(lower_name, "asin") == 0 || strcmp(lower_name, "acos") == 0 || strcmp(lower_name, "atan") == 0 ||
+ strcmp(lower_name, "sin⁻¹") == 0 || strcmp(lower_name, "cos⁻¹") == 0 || strcmp(lower_name, "tan⁻¹") == 0 ||
strcmp(lower_name, "sinh") == 0 || strcmp(lower_name, "cosh") == 0 || strcmp(lower_name, "tanh") == 0 ||
strcmp(lower_name, "sinh⁻¹") == 0 || strcmp(lower_name, "cosh⁻¹") == 0 || strcmp(lower_name, "tanh⁻¹") == 0 ||
strcmp(lower_name, "asinh") == 0 || strcmp(lower_name, "acosh") == 0 || strcmp(lower_name, "atanh") == 0 ||
@@ -159,7 +147,7 @@ function_is_defined(MPEquationParserState *state, const char *name)
static int
-get_function(MPEquationParserState *state, const char *name, const MPNumber *x, MPNumber *z)
+get_function(ParserState *state, const char *name, const MPNumber *x, MPNumber *z)
{
char *c, *lower_name;
int result = 1;
@@ -247,168 +235,12 @@ get_function(MPEquationParserState *state, const char *name, const MPNumber *x,
static int
-do_convert(const char *units[][2], const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
-{
- int x_index, z_index;
- MPNumber x_factor, z_factor;
-
- for (x_index = 0; units[x_index][0] != NULL && strcmp(units[x_index][0], x_units) != 0; x_index++);
- if (units[x_index][0] == NULL)
- return 0;
- for (z_index = 0; units[z_index][0] != NULL && strcmp(units[z_index][0], z_units) != 0; z_index++);
- if (units[z_index][0] == NULL)
- return 0;
-
- mp_set_from_string(units[x_index][1], 10, &x_factor);
- mp_set_from_string(units[z_index][1], 10, &z_factor);
- mp_multiply(x, &x_factor, z);
- mp_divide(z, &z_factor, z);
-
- return 1;
-}
-
-
-static int
-convert(MPEquationParserState *state, const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
+convert(ParserState *state, const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
{
- const char *length_units[][2] = {
- {"parsec", "30857000000000000"},
- {"parsecs", "30857000000000000"},
- {"pc", "30857000000000000"},
- {"lightyear", "9460730472580800"},
- {"lightyears", "9460730472580800"},
- {"ly", "9460730472580800"},
- {"au", "149597870691"},
- {"nm", "1852000"},
- {"mile", "1609.344"},
- {"miles", "1609.344"},
- {"mi", "1609.344"},
- {"kilometer", "1000"},
- {"kilometers", "1000"},
- {"km", "1000"},
- {"kms", "1000"},
- {"cable", "219.456"},
- {"cables", "219.456"},
- {"cb", "219.456"},
- {"fathom", "1.8288"},
- {"fathoms", "1.8288"},
- {"ftm", "1.8288"},
- {"meter", "1"},
- {"meters", "1"},
- {"m", "1"},
- {"yard", "0.9144"},
- {"yd", "0.9144"},
- {"foot", "0.3048"},
- {"feet", "0.3048"},
- {"ft", "0.3048"},
- {"inch", "0.0254"},
- {"inches", "0.0254"},
- {"centimeter", "0.01"},
- {"centimeters", "0.01"},
- {"cm", "0.01"},
- {"cms", "0.01"},
- {"millimeter", "0.001"},
- {"millimeters", "0.001"},
- {"mm", "0.001"},
- {"micrometer", "0.000001"},
- {"micrometers", "0.000001"},
- {"um", "0.000001"},
- {"nanometer", "0.000000001"},
- {"nanometers", "0.000000001"},
- {NULL, NULL}
- };
-
- const char *area_units[][2] = {
- {"hectare", "10000"},
- {"hectares", "10000"},
- {"acre", "4046.8564224"},
- {"acres", "4046.8564224"},
- {"m²", "1"},
- {"cm²", "0.001"},
- {"mm²", "0.000001"},
- {NULL, NULL}
- };
-
- const char *volume_units[][2] = {
- {"m³", "1000"},
- {"gallon", "3.785412"},
- {"gallons", "3.785412"},
- {"gal", "3.785412"},
- {"litre", "1"},
- {"litres", "1"},
- {"liter", "1"},
- {"liters", "1"},
- {"L", "1"},
- {"quart", "0.9463529"},
- {"quarts", "0.9463529"},
- {"qt", "0.9463529"},
- {"pint", "0.4731765"},
- {"pints", "0.4731765"},
- {"pt", "0.4731765"},
- {"millilitre", "0.001"},
- {"millilitres", "0.001"},
- {"milliliter", "0.001"},
- {"milliliters", "0.001"},
- {"mL", "0.001"},
- {"cm³", "0.001"},
- {"mm³", "0.000001"},
- {NULL, NULL}
- };
-
- const char *weight_units[][2] = {
- {"tonne", "1000"},
- {"tonnes", "1000"},
- {"kilograms", "1"},
- {"kilogramme", "1"},
- {"kilogrammes", "1"},
- {"kg", "1"},
- {"kgs", "1"},
- {"pound", "0.45359237"},
- {"pounds", "0.45359237"},
- {"lb", "0.45359237"},
- {"ounce", "0.002834952"},
- {"ounces", "0.002834952"},
- {"oz", "0.002834952"},
- {"gram", "0.001"},
- {"grams", "0.001"},
- {"gramme", "0.001"},
- {"grammes", "0.001"},
- {"g", "0.001"},
- {NULL, NULL}
- };
-
- const char *time_units[][2] = {
- {"year", "31557600"},
- {"years", "31557600"},
- {"day", "86400"},
- {"days", "86400"},
- {"hour", "3600"},
- {"hours", "3600"},
- {"minute", "60"},
- {"minutes", "60"},
- {"second", "1"},
- {"seconds", "1"},
- {"s", "1"},
- {"millisecond", "0.001"},
- {"milliseconds", "0.001"},
- {"ms", "0.001"},
- {"microsecond", "0.000001"},
- {"microseconds", "0.000001"},
- {"us", "0.000001"},
- {NULL, NULL}
- };
-
- if (do_convert(length_units, x, x_units, z_units, z) ||
- do_convert(area_units, x, x_units, z_units, z) ||
- do_convert(volume_units, x, x_units, z_units, z) ||
- do_convert(weight_units, x, x_units, z_units, z) ||
- do_convert(time_units, x, x_units, z_units, z))
- return 1;
-
if (state->options->convert)
return state->options->convert(x, x_units, z_units, z, state->options->callback_data);
-
- return 0;
+ else
+ return 0;
}
@@ -416,49 +248,44 @@ MPErrorCode
mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token)
{
int ret;
- MPEquationParserState state;
- yyscan_t yyscanner;
- YY_BUFFER_STATE buffer;
+ int err;
+ ParserState* state;
+ state = p_create_parser (expression, options);
if (!(expression && result) || strlen(expression) == 0)
return PARSER_ERR_INVALID;
- memset(&state, 0, sizeof(MPEquationParserState));
- state.options = options;
- state.variable_is_defined = variable_is_defined;
- state.get_variable = get_variable;
- state.set_variable = set_variable;
- state.function_is_defined = function_is_defined;
- state.get_function = get_function;
- state.convert = convert;
- state.error = 0;
-
+ state->variable_is_defined = variable_is_defined;
+ state->get_variable = get_variable;
+ state->set_variable = set_variable;
+ state->function_is_defined = function_is_defined;
+ state->get_function = get_function;
+ state->convert = convert;
+ state->error = 0;
mp_clear_error();
-
- _mp_equation_lex_init_extra(&state, &yyscanner);
- buffer = _mp_equation__scan_string(expression, yyscanner);
-
- ret = _mp_equation_parse(yyscanner);
- if (state.error_token != NULL && error_token != NULL) {
- *error_token = state.error_token;
+ ret = p_parse (state);
+ if (state->error_token != NULL && error_token != NULL) {
+ *error_token = state->error_token;
}
-
- _mp_equation__delete_buffer(buffer, yyscanner);
- _mp_equation_lex_destroy(yyscanner);
-
/* Error during parsing */
- if (state.error)
- return state.error;
+ if (state->error) {
+ err = state->error;
+ p_destroy_parser (state);
+ return err;
+ }
- if (mp_get_error())
+ if (mp_get_error()) {
+ p_destroy_parser (state);
return PARSER_ERR_MP;
+ }
/* Failed to parse */
- if (ret)
+ if (ret) {
+ p_destroy_parser (state);
return PARSER_ERR_INVALID;
-
- mp_set_from_mp(&state.ret, result);
-
+ }
+ mp_set_from_mp(&state->ret, result);
+ p_destroy_parser (state);
return PARSER_ERR_NONE;
}
@@ -486,9 +313,3 @@ mp_error_code_to_string(MPErrorCode error_code)
return "Unknown parser error";
}
}
-
-
-int _mp_equation_error(void *yylloc, MPEquationParserState *state, char *text)
-{
- return 0;
-}
diff --git a/src/mp-equation.h b/src/mp-equation.h
index bb06de6..d6c0f24 100644
--- a/src/mp-equation.h
+++ b/src/mp-equation.h
@@ -1,20 +1,12 @@
-/* Copyright (c) 2004-2008 Sami Pietila
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+/*
+ * Copyright (C) 2004-2008 Sami Pietila
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
*/
#ifndef MP_EQUATION_H
@@ -22,55 +14,56 @@
#include "mp.h"
-typedef enum {
- PARSER_ERR_NONE = 0,
- PARSER_ERR_INVALID,
- PARSER_ERR_OVERFLOW,
- PARSER_ERR_UNKNOWN_VARIABLE,
- PARSER_ERR_UNKNOWN_FUNCTION,
- PARSER_ERR_UNKNOWN_CONVERSION,
- PARSER_ERR_MP
+typedef enum
+{
+ PARSER_ERR_NONE = 0,
+ PARSER_ERR_INVALID,
+ PARSER_ERR_OVERFLOW,
+ PARSER_ERR_UNKNOWN_VARIABLE,
+ PARSER_ERR_UNKNOWN_FUNCTION,
+ PARSER_ERR_UNKNOWN_CONVERSION,
+ PARSER_ERR_MP
} MPErrorCode;
/* Options for parser */
typedef struct {
- /* Default number base */
- int base;
+ /* Default number base */
+ int base;
- /* The wordlength for binary operations in bits (e.g. 8, 16, 32) */
- int wordlen;
+ /* The wordlength for binary operations in bits (e.g. 8, 16, 32) */
+ int wordlen;
- /* Units for angles (e.g. radians, degrees) */
- MPAngleUnit angle_units;
+ /* Units for angles (e.g. radians, degrees) */
+ MPAngleUnit angle_units;
- // FIXME:
- // int enable_builtins;
+ // FIXME:
+ // int enable_builtins;
- /* Data to pass to callbacks */
- void *callback_data;
+ /* Data to pass to callbacks */
+ void *callback_data;
+
+ /* Function to check if a variable is defined */
+ int (*variable_is_defined)(const char *name, void *data);
- /* Function to check if a variable is defined */
- int (*variable_is_defined)(const char* name, void* data);
+ /* Function to get variable values */
+ int (*get_variable)(const char *name, MPNumber *z, void *data);
- /* Function to get variable values */
- int (*get_variable)(const char* name, MPNumber* z, void* data);
+ /* Function to set variable values */
+ void (*set_variable)(const char *name, const MPNumber *x, void *data);
- /* Function to set variable values */
- void (*set_variable)(const char* name, const MPNumber *x, void* data);
+ /* Function to check if a function is defined */
+ int (*function_is_defined)(const char *name, void *data);
- /* Function to check if a function is defined */
- int (*function_is_defined)(const char* name, void* data);
+ /* Function to solve functions */
+ int (*get_function)(const char *name, const MPNumber *x, MPNumber *z, void *data);
- /* Function to solve functions */
- int (*get_function)(const char* name, const MPNumber* x, MPNumber* z, void* data);
-
- /* Function to convert units */
- int (*convert)(const MPNumber* x, const char* x_units, const char* z_units, MPNumber* z, void* data);
+ /* Function to convert units */
+ int (*convert)(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data);
} MPEquationOptions;
-MPErrorCode mp_equation_parse(const char* expression, MPEquationOptions* options, MPNumber* result, char** error_token);
-const char* mp_error_code_to_string(MPErrorCode error_code);
+MPErrorCode mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token);
+const char *mp_error_code_to_string(MPErrorCode error_code);
-int sub_atoi(const char* data);
-int super_atoi(const char* data);
+int sub_atoi(const char *data);
+int super_atoi(const char *data);
#endif
diff --git a/src/mp-private.h b/src/mp-private.h
index f715b34..3211ea8 100644
--- a/src/mp-private.h
+++ b/src/mp-private.h
@@ -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-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., 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.
*/
#ifndef MP_INTERNAL_H
@@ -38,10 +30,10 @@
//}
#define MP_T 100
-void mperr(const char* format, ...) __attribute__((format(printf, 1, 2)));
-void mp_gcd(int64_t*, int64_t*);
-void mp_normalize(MPNumber*);
-void convert_to_radians(const MPNumber* x, MPAngleUnit unit, MPNumber* z);
-void convert_from_radians(const MPNumber* x, MPAngleUnit unit, MPNumber* z);
+void mperr(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void mp_gcd(int64_t *, int64_t *);
+void mp_normalize(MPNumber *);
+void convert_to_radians(const MPNumber *x, MPAngleUnit unit, MPNumber *z);
+void convert_from_radians(const MPNumber *x, MPAngleUnit unit, MPNumber *z);
#endif /* MP_INTERNAL_H */
diff --git a/src/mp-serializer.c b/src/mp-serializer.c
new file mode 100644
index 0000000..356fc55
--- /dev/null
+++ b/src/mp-serializer.c
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2010 Robin Sonefors
+ * 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 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 <langinfo.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "mp-serializer.h"
+#include "mp-enums.h"
+
+enum {
+ PROP_0,
+ PROP_SHOW_THOUSANDS_SEPARATORS,
+ PROP_SHOW_TRAILING_ZEROES,
+ PROP_NUMBER_FORMAT,
+ PROP_BASE,
+};
+
+struct MpSerializerPrivate
+{
+ gint leading_digits; /* Number of digits to show before radix */
+ gint trailing_digits; /* Number of digits to show after radix */
+ MpDisplayFormat format; /* Number display mode. */
+ gint show_tsep; /* Set if the thousands separator should be shown. */
+ gint show_zeroes; /* Set if trailing zeroes should be shown. */
+
+ gint base; /* Numeric base */
+
+ gunichar tsep; /* Locale specific thousands separator. */
+ gunichar radix; /* Locale specific radix string. */
+ gint tsep_count; /* Number of digits between separator. */
+};
+
+
+G_DEFINE_TYPE(MpSerializer, mp_serializer, G_TYPE_OBJECT);
+
+MpSerializer *
+mp_serializer_new(MpDisplayFormat format, int base, int trailing_digits)
+{
+ MpSerializer *serializer = g_object_new(mp_serializer_get_type(), /*"number-format", format,*/ NULL);
+ mp_serializer_set_number_format(serializer, format);
+ mp_serializer_set_base(serializer, base);
+ mp_serializer_set_trailing_digits(serializer, trailing_digits);
+ return serializer;
+}
+
+
+static void
+mp_cast_to_string_real(MpSerializer *serializer, const MPNumber *x, int base, gboolean force_sign, int *n_digits, GString *string)
+{
+ static gchar digits[] = "0123456789ABCDEF";
+ MPNumber number, integer_component, fractional_component, temp;
+ int i, last_non_zero;
+
+ if (mp_is_negative(x))
+ mp_abs(x, &number);
+ else
+ mp_set_from_mp(x, &number);
+
+ /* Add rounding factor */
+ mp_set_from_integer(base, &temp);
+ mp_xpowy_integer(&temp, -(serializer->priv->trailing_digits+1), &temp);
+ mp_multiply_integer(&temp, base, &temp);
+ mp_divide_integer(&temp, 2, &temp);
+ mp_add(&number, &temp, &temp);
+
+ /* If trying to add rounding factor causes overflow, don't add it */
+ if (!mp_get_error())
+ mp_set_from_mp(&temp, &number);
+
+ /* Split into integer and fractional component */
+ mp_floor(&number, &integer_component);
+ mp_fractional_component(&number, &fractional_component);
+
+ /* Write out the integer component least significant digit to most */
+ mp_set_from_mp(&integer_component, &temp);
+ i = 0;
+ do {
+ MPNumber t, t2, t3;
+ int64_t d;
+
+ if (serializer->priv->base == 10 && serializer->priv->show_tsep && i == serializer->priv->tsep_count) {
+ g_string_prepend_unichar(string, serializer->priv->tsep);
+ i = 0;
+ }
+ i++;
+
+ mp_divide_integer(&temp, base, &t);
+ mp_floor(&t, &t);
+ mp_multiply_integer(&t, base, &t2);
+
+ mp_subtract(&temp, &t2, &t3);
+
+ d = mp_cast_to_int(&t3);
+ g_string_prepend_c(string, d < 16 ? digits[d] : '?');
+ (*n_digits)++;
+
+ mp_set_from_mp(&t, &temp);
+ } while (!mp_is_zero(&temp));
+
+ last_non_zero = string->len;
+
+ g_string_append_unichar(string, serializer->priv->radix);
+
+ /* Write out the fractional component */
+ mp_set_from_mp(&fractional_component, &temp);
+ for (i = serializer->priv->trailing_digits; i > 0 && !mp_is_zero(&temp); i--) {
+ int d;
+ MPNumber digit;
+
+ mp_multiply_integer(&temp, base, &temp);
+ mp_floor(&temp, &digit);
+ d = mp_cast_to_int(&digit);
+
+ g_string_append_c(string, digits[d]);
+
+ if(d != 0)
+ last_non_zero = string->len;
+ mp_subtract(&temp, &digit, &temp);
+ }
+
+ /* Strip trailing zeroes */
+ if (!serializer->priv->show_zeroes || serializer->priv->trailing_digits == 0)
+ g_string_truncate(string, last_non_zero);
+
+ /* Add sign on non-zero values */
+ if (strcmp(string->str, "0") != 0 || force_sign) {
+ if (mp_is_negative(x))
+ g_string_prepend(string, "−");
+ else if (force_sign)
+ g_string_prepend(string, "+");
+ }
+
+ /* Append base suffix if not in default base */
+ if (base != serializer->priv->base) {
+ const gchar *digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"};
+ int multiplier = 1;
+ int b = base;
+
+ while (base / multiplier != 0)
+ multiplier *= 10;
+ while (multiplier != 1) {
+ int d;
+ multiplier /= 10;
+ d = b / multiplier;
+ g_string_append(string, digits[d]);
+ b -= d * multiplier;
+ }
+ }
+}
+
+
+static gchar *
+mp_cast_to_string(MpSerializer *serializer, const MPNumber *x, int *n_digits)
+{
+ GString *string;
+ MPNumber x_real;
+ gchar *result;
+
+ string = g_string_sized_new(1024);
+
+ mp_real_component(x, &x_real);
+ mp_cast_to_string_real(serializer, &x_real, serializer->priv->base, FALSE, n_digits, string);
+ if (mp_is_complex(x)) {
+ GString *s;
+ gboolean force_sign = TRUE;
+ MPNumber x_im;
+ int n_complex_digits;
+
+ mp_imaginary_component(x, &x_im);
+
+ if (strcmp(string->str, "0") == 0) {
+ g_string_assign(string, "");
+ force_sign = FALSE;
+ }
+
+ s = g_string_sized_new(1024);
+ mp_cast_to_string_real(serializer, &x_im, 10, force_sign, &n_complex_digits, s);
+ if (n_complex_digits > *n_digits)
+ *n_digits = n_complex_digits;
+ if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) {
+ /* Ignore */
+ }
+ else if (strcmp(s->str, "1") == 0) {
+ g_string_append(string, "i");
+ }
+ else if (strcmp(s->str, "+1") == 0) {
+ g_string_append(string, "+i");
+ }
+ else if (strcmp(s->str, "−1") == 0) {
+ g_string_append(string, "−i");
+ }
+ else {
+ if (strcmp(s->str, "+0") == 0)
+ g_string_append(string, "+");
+ else if (strcmp(s->str, "0") != 0)
+ g_string_append(string, s->str);
+
+ g_string_append(string, "i");
+ }
+ g_string_free(s, TRUE);
+ }
+
+ result = g_strndup(string->str, string->len + 1);
+ g_string_free(string, TRUE);
+
+ return result;
+}
+
+
+static int
+mp_cast_to_exponential_string_real(MpSerializer *serializer, const MPNumber *x, GString *string, gboolean eng_format, int *n_digits)
+{
+ gchar *fixed;
+ MPNumber t, z, base, base3, base10, base10inv, mantissa;
+ int exponent = 0;
+
+ mp_abs(x, &z);
+ if (mp_is_negative(x))
+ g_string_append(string, "−");
+ mp_set_from_mp(&z, &mantissa);
+
+ mp_set_from_integer(serializer->priv->base, &base);
+ mp_xpowy_integer(&base, 3, &base3);
+ mp_xpowy_integer(&base, 10, &base10);
+ mp_set_from_integer(1, &t);
+ mp_divide(&t, &base10, &base10inv);
+
+ if (!mp_is_zero(&mantissa)) {
+ while (!eng_format && mp_is_greater_equal(&mantissa, &base10)) {
+ exponent += 10;
+ mp_multiply(&mantissa, &base10inv, &mantissa);
+ }
+
+ while ((!eng_format && mp_is_greater_equal(&mantissa, &base)) ||
+ (eng_format && (mp_is_greater_equal(&mantissa, &base3) || exponent % 3 != 0))) {
+ exponent += 1;
+ mp_divide(&mantissa, &base, &mantissa);
+ }
+
+ while (!eng_format && mp_is_less_than(&mantissa, &base10inv)) {
+ exponent -= 10;
+ mp_multiply(&mantissa, &base10, &mantissa);
+ }
+
+ mp_set_from_integer(1, &t);
+ while (mp_is_less_than(&mantissa, &t) || (eng_format && exponent % 3 != 0)) {
+ exponent -= 1;
+ mp_multiply(&mantissa, &base, &mantissa);
+ }
+ }
+
+ fixed = mp_cast_to_string(serializer, &mantissa, n_digits);
+ g_string_append(string, fixed);
+ g_free(fixed);
+
+ return exponent;
+}
+
+
+static void
+append_exponent(GString *string, int exponent)
+{
+ const gchar *super_digits[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"};
+ gchar *super_value, *c;
+
+ if (exponent == 0)
+ return;
+
+ g_string_append_printf(string, "×10"); // FIXME: Use the current base
+ if (exponent < 0) {
+ exponent = -exponent;
+ g_string_append(string, "⁻");
+ }
+
+ super_value = g_strdup_printf("%d", exponent);
+ for (c = super_value; *c; c++)
+ g_string_append(string, super_digits[*c - '0']);
+ g_free (super_value);
+}
+
+
+static gchar *
+mp_cast_to_exponential_string(MpSerializer *serializer, const MPNumber *x, gboolean eng_format, int *n_digits)
+{
+ GString *string;
+ MPNumber x_real;
+ gchar *result;
+ int exponent;
+
+ string = g_string_sized_new(1024);
+
+ mp_real_component(x, &x_real);
+ exponent = mp_cast_to_exponential_string_real(serializer, &x_real, string, eng_format, n_digits);
+ append_exponent(string, exponent);
+
+ if (mp_is_complex(x)) {
+ GString *s;
+ MPNumber x_im;
+ int n_complex_digits = 0;
+
+ mp_imaginary_component(x, &x_im);
+
+ if (strcmp(string->str, "0") == 0)
+ g_string_assign(string, "");
+
+ s = g_string_sized_new(1024);
+ exponent = mp_cast_to_exponential_string_real(serializer, &x_im, s, eng_format, &n_complex_digits);
+ if (n_complex_digits > *n_digits)
+ *n_digits = n_complex_digits;
+ if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) {
+ /* Ignore */
+ }
+ else if (strcmp(s->str, "1") == 0) {
+ g_string_append(string, "i");
+ }
+ else if (strcmp(s->str, "+1") == 0) {
+ g_string_append(string, "+i");
+ }
+ else if (strcmp(s->str, "−1") == 0) {
+ g_string_append(string, "−i");
+ }
+ else {
+ if (strcmp(s->str, "+0") == 0)
+ g_string_append(string, "+");
+ else if (strcmp(s->str, "0") != 0)
+ g_string_append(string, s->str);
+
+ g_string_append(string, "i");
+ }
+ g_string_free(s, TRUE);
+ append_exponent(string, exponent);
+ }
+
+ result = g_strndup(string->str, string->len + 1);
+ g_string_free(string, TRUE);
+
+ return result;
+}
+
+
+gchar *
+mp_serializer_to_string(MpSerializer *serializer, const MPNumber *x)
+{
+ gchar *s0;
+ int n_digits = 0;
+
+ switch(serializer->priv->format) {
+ default:
+ case MP_DISPLAY_FORMAT_AUTOMATIC:
+ s0 = mp_cast_to_string(serializer, x, &n_digits);
+ if (n_digits <= serializer->priv->leading_digits)
+ return s0;
+ else {
+ g_free (s0);
+ return mp_cast_to_exponential_string(serializer, x, FALSE, &n_digits);
+ }
+ break;
+ case MP_DISPLAY_FORMAT_FIXED:
+ return mp_cast_to_string(serializer, x, &n_digits);
+ case MP_DISPLAY_FORMAT_SCIENTIFIC:
+ return mp_cast_to_exponential_string(serializer, x, FALSE, &n_digits);
+ case MP_DISPLAY_FORMAT_ENGINEERING:
+ return mp_cast_to_exponential_string(serializer, x, TRUE, &n_digits);
+ }
+}
+
+
+gboolean
+mp_serializer_from_string(MpSerializer *serializer, const gchar *str, MPNumber *z)
+{
+ return mp_set_from_string(str, serializer->priv->base, z);
+}
+
+
+void
+mp_serializer_set_base(MpSerializer *serializer, gint base)
+{
+ serializer->priv->base = base;
+}
+
+
+int
+mp_serializer_get_base(MpSerializer *serializer)
+{
+ return serializer->priv->base;
+}
+
+
+void
+mp_serializer_set_radix(MpSerializer *serializer, gunichar radix)
+{
+ serializer->priv->radix = radix;
+}
+
+
+gunichar
+mp_serializer_get_radix(MpSerializer *serializer)
+{
+ return serializer->priv->radix;
+}
+
+
+void
+mp_serializer_set_thousands_separator(MpSerializer *serializer, gunichar separator)
+{
+ serializer->priv->tsep = separator;
+}
+
+
+gunichar
+mp_serializer_get_thousands_separator(MpSerializer *serializer)
+{
+ return serializer->priv->tsep;
+}
+
+
+gint
+mp_serializer_get_thousands_separator_count(MpSerializer *serializer)
+{
+ return serializer->priv->tsep_count;
+}
+
+
+void
+mp_serializer_set_show_thousands_separators(MpSerializer *serializer, gboolean visible)
+{
+ serializer->priv->show_tsep = visible;
+}
+
+
+gboolean
+mp_serializer_get_show_thousands_separators(MpSerializer *serializer)
+{
+ return serializer->priv->show_tsep;
+}
+
+
+void
+mp_serializer_set_show_trailing_zeroes(MpSerializer *serializer, gboolean visible)
+{
+ serializer->priv->show_zeroes = visible;
+}
+
+
+gboolean
+mp_serializer_get_show_trailing_zeroes(MpSerializer *serializer)
+{
+ return serializer->priv->show_zeroes;
+}
+
+
+int
+mp_serializer_get_leading_digits(MpSerializer *serializer)
+{
+ return serializer->priv->leading_digits;
+}
+
+
+void
+mp_serializer_set_leading_digits(MpSerializer *serializer, int leading_digits)
+{
+ serializer->priv->leading_digits = leading_digits;
+}
+
+
+int
+mp_serializer_get_trailing_digits(MpSerializer *serializer)
+{
+ return serializer->priv->trailing_digits;
+}
+
+
+void
+mp_serializer_set_trailing_digits(MpSerializer *serializer, int trailing_digits)
+{
+ serializer->priv->trailing_digits = trailing_digits;
+}
+
+
+MpDisplayFormat
+mp_serializer_get_number_format(MpSerializer *serializer)
+{
+ return serializer->priv->format;
+}
+
+
+void
+mp_serializer_set_number_format(MpSerializer *serializer, MpDisplayFormat format)
+{
+ serializer->priv->format = format;
+}
+
+
+static void
+mp_serializer_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MpSerializer *self = MP_SERIALIZER(object);
+
+ switch (prop_id) {
+ case PROP_SHOW_THOUSANDS_SEPARATORS:
+ mp_serializer_set_show_thousands_separators(self, g_value_get_boolean(value));
+ break;
+ case PROP_SHOW_TRAILING_ZEROES:
+ mp_serializer_set_show_trailing_zeroes(self, g_value_get_boolean(value));
+ break;
+ case PROP_BASE:
+ mp_serializer_set_base(self, g_value_get_int(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+mp_serializer_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MpSerializer *self = MP_SERIALIZER(object);
+
+ switch (prop_id) {
+ case PROP_SHOW_THOUSANDS_SEPARATORS:
+ g_value_set_boolean(value, mp_serializer_get_show_thousands_separators(self));
+ break;
+ case PROP_SHOW_TRAILING_ZEROES:
+ g_value_set_boolean(value, mp_serializer_get_show_trailing_zeroes(self));
+ break;
+ case PROP_BASE:
+ g_value_set_int(value, mp_serializer_get_base(self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+mp_serializer_class_init(MpSerializerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ object_class->get_property = mp_serializer_get_property;
+ object_class->set_property = mp_serializer_set_property;
+
+ g_type_class_add_private(klass, sizeof(MpSerializerPrivate));
+
+ 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",
+ math_mp_display_format_get_type(),
+ MP_DISPLAY_FORMAT_AUTOMATIC,
+ G_PARAM_READWRITE));
+ g_object_class_install_property(object_class,
+ PROP_BASE,
+ g_param_spec_int("base",
+ "base",
+ "Default number base",
+ 2, 16, 10,
+ G_PARAM_READWRITE));
+}
+
+
+static void
+mp_serializer_init(MpSerializer *serializer)
+{
+ gchar *radix, *tsep;
+ serializer->priv = G_TYPE_INSTANCE_GET_PRIVATE(serializer, mp_serializer_get_type(), MpSerializerPrivate);
+
+ radix = nl_langinfo(RADIXCHAR);
+ serializer->priv->radix = radix ? g_utf8_get_char(g_locale_to_utf8(radix, -1, NULL, NULL, NULL)) : '.';
+ tsep = nl_langinfo(THOUSEP);
+ if (tsep && tsep[0] != '\0')
+ serializer->priv->tsep = g_utf8_get_char(g_locale_to_utf8(tsep, -1, NULL, NULL, NULL));
+ else
+ serializer->priv->tsep = ' ';
+ serializer->priv->tsep_count = 3;
+
+ serializer->priv->base = 10;
+ serializer->priv->leading_digits = 12;
+ serializer->priv->trailing_digits = 9;
+ serializer->priv->show_zeroes = FALSE;
+ serializer->priv->show_tsep = FALSE;
+ serializer->priv->format = MP_DISPLAY_FORMAT_AUTOMATIC;
+}
diff --git a/src/mp-serializer.h b/src/mp-serializer.h
new file mode 100644
index 0000000..63e5937
--- /dev/null
+++ b/src/mp-serializer.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Robin Sonefors
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef MP_SERIALIZER_H
+#define MP_SERIALIZER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mp.h"
+
+G_BEGIN_DECLS
+
+#define MP_TYPE_SERIALIZER (mp_serializer_get_type())
+#define MP_SERIALIZER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), mp_serializer_get_type(), MpSerializer))
+
+typedef struct MpSerializerPrivate MpSerializerPrivate;
+
+typedef struct {
+ GObject parent;
+ MpSerializerPrivate *priv;
+} MpSerializer;
+
+typedef struct {
+ GObjectClass parent;
+} MpSerializerClass;
+
+/* Number display mode. */
+typedef enum {
+ MP_DISPLAY_FORMAT_AUTOMATIC,
+ MP_DISPLAY_FORMAT_FIXED,
+ MP_DISPLAY_FORMAT_SCIENTIFIC,
+ MP_DISPLAY_FORMAT_ENGINEERING
+} MpDisplayFormat;
+
+GType mp_serializer_get_type(void);
+
+MpSerializer *mp_serializer_new(MpDisplayFormat format, int base, int trailing_digits);
+
+gchar *mp_serializer_to_string(MpSerializer *serializer, const MPNumber *z);
+gboolean mp_serializer_from_string(MpSerializer *serializer, const gchar *str, MPNumber *z);
+
+void mp_serializer_set_number_format(MpSerializer *serializer, MpDisplayFormat format);
+MpDisplayFormat mp_serializer_get_number_format(MpSerializer *serializer);
+
+void mp_serializer_set_base(MpSerializer *serializer, int base);
+int mp_serializer_get_base(MpSerializer *serializer);
+
+void mp_serializer_set_leading_digits(MpSerializer *serializer, int leading_digits);
+int mp_serializer_get_leading_digits(MpSerializer *serializer);
+
+void mp_serializer_set_trailing_digits(MpSerializer *serializer, int trailing_digits);
+int mp_serializer_get_trailing_digits(MpSerializer *serializer);
+
+void mp_serializer_set_radix(MpSerializer *serializer, gunichar radix);
+gunichar mp_serializer_get_radix(MpSerializer *serializer);
+
+void mp_serializer_set_thousands_separator(MpSerializer *serializer, gunichar separator);
+gunichar mp_serializer_get_thousands_separator(MpSerializer *serializer);
+
+gint mp_serializer_get_thousands_separator_count(MpSerializer *serializer);
+
+void mp_serializer_set_show_trailing_zeroes(MpSerializer *serializer, gboolean visible);
+gboolean mp_serializer_get_show_trailing_zeroes(MpSerializer *serializer);
+
+void mp_serializer_set_show_thousands_separators(MpSerializer *serializer, gboolean visible);
+gboolean mp_serializer_get_show_thousands_separators(MpSerializer *serializer);
+
+G_END_DECLS
+
+#endif /* MP_SERIALIZER_H */
diff --git a/src/mp-trigonometric.c b/src/mp-trigonometric.c
index 4982041..3ad60c4 100644
--- a/src/mp-trigonometric.c
+++ b/src/mp-trigonometric.c
@@ -1,20 +1,12 @@
-/* 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+/*
+ * 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 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>
@@ -25,6 +17,9 @@
#include "mp.h"
#include "mp-private.h"
+static MPNumber pi;
+static gboolean have_pi = FALSE;
+
static int
mp_compare_mp_to_int(const MPNumber *x, int i)
{
@@ -64,7 +59,11 @@ convert_to_radians(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
void
mp_get_pi(MPNumber *z)
{
- mp_set_from_string("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 10, z);
+ if (mp_is_zero(&pi)) {
+ mp_set_from_string("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 10, &pi);
+ have_pi = TRUE;
+ }
+ mp_set_from_mp(&pi, z);
}
@@ -420,7 +419,7 @@ mp_atan(const MPNumber *x, MPAngleUnit unit, MPNumber *z)
break;
q *= 2;
-
+
/* t = t / (√(t² + 1) + 1) */
mp_multiply(&t2, &t2, z);
mp_add_integer(z, 1, z);
@@ -557,6 +556,7 @@ mp_tanh(const MPNumber *x, MPNumber *z)
} else {
mp_epowy(&t, &t);
mp_add_integer(&t, 1, z);
+ mp_add_integer(&t, -1, &t);
mp_divide(&t, z, z);
}
@@ -588,7 +588,7 @@ mp_acosh(const MPNumber *x, MPNumber *z)
mp_set_from_integer(1, &t);
if (mp_is_less_than(x, &t)) {
/* Translators: Error displayed when inverse hyperbolic cosine value is undefined */
- mperr(_("Inverse hyperbolic cosine is undefined for values less than or equal to one"));
+ mperr(_("Inverse hyperbolic cosine is undefined for values less than one"));
mp_set_from_integer(0, z);
return;
}
diff --git a/src/mp.c b/src/mp.c
index 0037135..3003202 100644
--- a/src/mp.c
+++ b/src/mp.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>
@@ -25,6 +17,9 @@
#include "mp.h"
#include "mp-private.h"
+static MPNumber eulers_number;
+static gboolean have_eulers_number = FALSE;
+
// FIXME: Re-add overflow and underflow detection
char *mp_error = NULL;
@@ -101,9 +96,13 @@ mp_ext(int i, int j, MPNumber *x)
void
mp_get_eulers(MPNumber *z)
{
- MPNumber t;
- mp_set_from_integer(1, &t);
- mp_epowy(&t, z);
+ if (!have_eulers_number) {
+ MPNumber t;
+ mp_set_from_integer(1, &t);
+ mp_epowy(&t, &eulers_number);
+ have_eulers_number = TRUE;
+ }
+ mp_set_from_mp(&eulers_number, z);
}
@@ -212,8 +211,8 @@ mp_imaginary_component(const MPNumber *x, MPNumber *z)
z->sign = x->im_sign;
z->exponent = x->im_exponent;
memcpy(z->fraction, x->im_fraction, sizeof(int) * MP_SIZE);
-
- /* Clear (old) imaginary component */
+
+ /* Clear (old) imaginary component */
z->im_sign = 0;
z->im_exponent = 0;
memset(z->im_fraction, 0, sizeof(int) * MP_SIZE);
@@ -235,7 +234,7 @@ mp_add_real(const MPNumber *x, int y_sign, const MPNumber *y, MPNumber *z)
z->sign = y_sign;
return;
}
- /* x + 0 = x */
+ /* x + 0 = x */
else if (mp_is_zero(y)) {
mp_set_from_mp(x, z);
return;
@@ -293,6 +292,15 @@ mp_add_real(const MPNumber *x, int y_sign, const MPNumber *y, MPNumber *z)
z->fraction[MP_T + i] = 0;
if (sign_prod >= 0) {
+ /* This is probably insufficient overflow detection, but it makes us
+ * not crash at least.
+ */
+ if (MP_T + 3 < med) {
+ mperr(_("Overflow: the result couldn't be calculated"));
+ mp_set_from_integer(0, z);
+ return;
+ }
+
/* HERE DO ADDITION, EXPONENT(Y) >= EXPONENT(X) */
for (i = MP_T + 3; i >= MP_T; i--)
z->fraction[i] = small_fraction[i - med];
@@ -450,7 +458,7 @@ mp_sgn(const MPNumber *x, MPNumber *z)
else if (mp_is_negative(x))
mp_set_from_integer(-1, z);
else
- mp_set_from_integer(1, z);
+ mp_set_from_integer(1, z);
}
void
@@ -829,7 +837,7 @@ bool
mp_is_integer(const MPNumber *x)
{
MPNumber t1, t2, t3;
-
+
if (mp_is_complex(x))
return false;
@@ -978,7 +986,7 @@ mp_epowy_real(const MPNumber *x, MPNumber *z)
{
float r__1;
int i, ix, xs, tss;
- float rx, rz, rlb;
+ float rx, rz;
MPNumber t1, t2;
/* e^0 = 1 */
@@ -993,11 +1001,6 @@ mp_epowy_real(const MPNumber *x, MPNumber *z)
return;
}
- /* SEE IF ABS(X) SO LARGE THAT EXP(X) WILL CERTAINLY OVERFLOW
- * OR UNDERFLOW. 1.01 IS TO ALLOW FOR ERRORS IN ALOG.
- */
- rlb = log((float)MP_BASE) * 1.01f;
-
/* NOW SAFE TO CONVERT X TO REAL */
rx = mp_cast_to_float(x);
@@ -1303,7 +1306,7 @@ void
mp_logarithm(int64_t n, const MPNumber *x, MPNumber *z)
{
MPNumber t1, t2;
-
+
/* log(0) undefined */
if (mp_is_zero(x)) {
/* Translators: Error displayed when attempting to take logarithm of zero */
@@ -1333,7 +1336,7 @@ mp_multiply_real(const MPNumber *x, const MPNumber *y, MPNumber *z)
int c, i, xi;
MPNumber r;
- /* x*0 = 0*y = 0 */
+ /* x*0 = 0*y = 0 */
if (x->sign == 0 || y->sign == 0) {
mp_set_from_integer(0, z);
return;
@@ -1418,7 +1421,7 @@ mp_multiply_real(const MPNumber *x, const MPNumber *y, MPNumber *z)
/* Clear complex part */
z->im_sign = 0;
z->im_exponent = 0;
- memset(z->im_fraction, 0, sizeof(int) * MP_SIZE);
+ memset(z->im_fraction, 0, sizeof(int) * MP_SIZE);
/* NORMALIZE AND ROUND RESULT */
// FIXME: Use stack variable because of mp_normalize brokeness
@@ -1445,11 +1448,11 @@ mp_multiply(const MPNumber *x, const MPNumber *y, MPNumber *z)
mp_imaginary_component(x, &im_x);
mp_real_component(y, &real_y);
mp_imaginary_component(y, &im_y);
-
+
mp_multiply_real(&real_x, &real_y, &t1);
mp_multiply_real(&im_x, &im_y, &t2);
mp_subtract(&t1, &t2, &real_z);
-
+
mp_multiply_real(&real_x, &im_y, &t1);
mp_multiply_real(&im_x, &real_y, &t2);
mp_add(&t1, &t2, &im_z);
@@ -1574,7 +1577,7 @@ mp_multiply_integer_real(const MPNumber *x, int64_t y, MPNumber *z)
z->im_sign = 0;
z->im_exponent = 0;
- memset(z->im_fraction, 0, sizeof(int) * MP_SIZE);
+ memset(z->im_fraction, 0, sizeof(int) * MP_SIZE);
mp_normalize(z);
}
@@ -1670,10 +1673,11 @@ mp_pwr(const MPNumber *x, const MPNumber *y, MPNumber *z)
return;
}*/
- /* 0^-y illegal */
- if (mp_is_zero(x) && y->sign < 0) {
- mperr(_("The power of zero is undefined for a negative exponent"));
+ /* 0^y = 0, 0^-y undefined */
+ if (mp_is_zero(x)) {
mp_set_from_integer(0, z);
+ if (y->sign < 0)
+ mperr(_("The power of zero is undefined for a negative exponent"));
return;
}
@@ -1746,7 +1750,7 @@ mp_reciprocal_real(const MPNumber *x, MPNumber *z)
void
mp_reciprocal(const MPNumber *x, MPNumber *z)
-{
+{
if (mp_is_complex(x)) {
MPNumber t1, t2;
MPNumber real_x, im_x;
@@ -2003,7 +2007,7 @@ mp_xpowy_integer(const MPNumber *x, int64_t n, MPNumber *z)
mp_set_from_integer(0, z);
return;
}
-
+
/* x^1 = x */
if (n == 1) {
mp_set_from_mp(x, z);
diff --git a/src/mp.h b/src/mp.h
index 3f99c60..634bb01 100644
--- a/src/mp.h
+++ b/src/mp.h
@@ -1,21 +1,12 @@
-
-/* 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+/*
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
*/
/* This maths library is based on the MP multi-precision floating-point
@@ -53,26 +44,28 @@
*
* x = sign * (MP_BASE^(exponent-1) + MP_BASE^(exponent-2) + ...)
*/
-typedef struct {
- /* Sign (+1, -1) or 0 for the value zero */
- int sign, im_sign;
+typedef struct
+{
+ /* Sign (+1, -1) or 0 for the value zero */
+ int sign, im_sign;
- /* Exponent (to base MP_BASE) */
- int exponent, im_exponent;
+ /* Exponent (to base MP_BASE) */
+ int exponent, im_exponent;
- /* Normalized fraction */
- int fraction[MP_SIZE], im_fraction[MP_SIZE];
+ /* Normalized fraction */
+ int fraction[MP_SIZE], im_fraction[MP_SIZE];
} MPNumber;
-typedef enum {
- MP_RADIANS,
- MP_DEGREES,
- MP_GRADIANS
+typedef enum
+{
+ MP_RADIANS,
+ MP_DEGREES,
+ MP_GRADIANS
} MPAngleUnit;
/* Returns error string or NULL if no error */
// FIXME: Global variable
-const char* mp_get_error(void);
+const char *mp_get_error(void);
/* Clear any current error */
void mp_clear_error(void);
@@ -268,24 +261,6 @@ int64_t mp_cast_to_int(const MPNumber *x);
/* Returns x as a native unsigned integer */
uint64_t mp_cast_to_unsigned_int(const MPNumber *x);
-/* Converts x to a string representation.
- * The string is written into 'buffer' which is guaranteed to be at least 'buffer_length' octets in size.
- * If not enough space is available the string is truncated.
- * The numbers are written in 'base' (e.g. 10).
- * If 'trim_zeroes' is non-zero then strip off trailing zeroes.
- * Fractional components are truncated at 'max_digits' digits.
- */
-void mp_cast_to_string(const MPNumber *x, int default_base, int base, int max_digits, bool trim_zeroes, char *buffer, int buffer_length);
-
-/* Converts x to a string representation in exponential form.
- * The string is written into 'buffer' which is guaranteed to be at least 'buffer_length' octets in size.
- * If not enough space is available the string is truncated.
- * The numbers are written in 'base' (e.g. 10).
- * If 'trim_zeroes' is non-zero then strip off trailing zeroes.
- * Fractional components are truncated at 'max_digits' digits.
- */
-void mp_cast_to_exponential_string(const MPNumber *x, int default_base, int base, int max_digits, bool trim_zeroes, bool eng_format, char *buffer, int buffer_length);
-
/* Sets z = sin x */
void mp_sin(const MPNumber *x, MPAngleUnit unit, MPNumber *z);
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..fb4fd12
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,1228 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "parser.h"
+#include "parserfunc.h"
+#include "mp-equation.h"
+
+/* Converts LexerTokenType to Precedence value. */
+static guint
+p_get_precedence(LexerTokenType type)
+{
+ /* WARNING: This function doesn't work for Unary Plus and Unary Minus. Use their precedence directly while inserting them in tree. */
+ if(type == T_ADD
+ ||type == T_SUBTRACT)
+ return P_AddSubtract;
+ if(type == T_MULTIPLY)
+ return P_Multiply;
+ if(type == T_MOD)
+ return P_Mod;
+ if(type == T_DIV)
+ return P_Divide;
+ if(type == T_NOT)
+ return P_Not;
+ if(type == T_ROOT
+ ||type == T_ROOT_3
+ ||type == T_ROOT_4)
+ return P_Root;
+ if(type == T_FUNCTION)
+ return P_Function;
+ if(type == T_AND
+ ||type == T_OR
+ ||type == T_XOR)
+ return P_Boolean;
+ if(type == T_PERCENTAGE)
+ return P_Percentage;
+ if(type == T_POWER)
+ return P_Power;
+ if(type == T_FACTORIAL)
+ return P_Factorial;
+ if(type == T_NUMBER
+ ||type == T_VARIABLE)
+ return P_NumberVariable;
+ return P_Unknown;
+}
+
+/* Return associativity of specific token type from precedence. */
+static Associativity
+p_get_associativity_p(Precedence type)
+{
+ if(type == P_Boolean
+ ||type == P_Divide
+ ||type == P_Mod
+ ||type == P_Multiply
+ ||type == P_AddSubtract)
+ return LEFT_ASSOCIATIVE;
+ if(type == P_Power)
+ return RIGHT_ASSOCIATIVE;
+ /* For all remaining / non-associative operators, return Left Associativity. */
+ return LEFT_ASSOCIATIVE;
+}
+
+/* Return associativity of specific token by converting it to precedence first. */
+static Associativity
+p_get_associativity(LexerToken* token)
+{
+ return p_get_associativity_p(p_get_precedence(token->token_type));
+}
+
+/* Generate precedence for a node from precedence value. Includes depth_level. */
+static guint
+p_make_precedence_p(ParserState* state, Precedence p)
+{
+ return (p + (state->depth_level * P_Depth));
+}
+
+/* Generate precedence for a node from lexer token type. Includes depth_level. */
+static guint
+p_make_precedence_t(ParserState* state, LexerTokenType type)
+{
+ return (p_get_precedence(type) + (state->depth_level * P_Depth));
+}
+
+/* Allocate and create a new node. */
+static ParseNode*
+p_create_node(ParserState* state, LexerToken* token, guint precedence, Associativity associativity, void* value, void* (*function)(ParseNode*))
+{
+ ParseNode* new;
+ new = (ParseNode*) malloc(sizeof(ParseNode));
+ assert(new != NULL);
+ new->parent = NULL;
+ new->left = NULL;
+ new->right = NULL;
+ new->token = token;
+ new->precedence = precedence;
+ new->associativity = associativity;
+ new->value = value;
+ new->state = state;
+ new->evaluate = function;
+ return new;
+}
+
+/* Compares two nodes to decide, which will be parent and which willbe child. */
+static gint
+p_cmp_nodes(ParseNode* left, ParseNode* right)
+{
+ /* Return values.
+ 1 = right goes up (near root) in parse tree.
+ 0 = left goes up (near root) in parse tree.
+ */
+ if(left == NULL)
+ return 0;
+ if(left->precedence > right->precedence)
+ {
+ return 1;
+ }
+ else if(left->precedence < right->precedence)
+ {
+ return 0;
+ }
+ else
+ {
+ if(right->associativity == RIGHT_ASSOCIATIVE)
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
+
+/* Unified interface (unary and binary nodes) to insert node into parse tree. */
+static void
+p_insert_into_tree_all(ParserState* state, ParseNode* node, guint unary_function)
+{
+ if(state->root == NULL)
+ {
+ state->root = node;
+ state->right_most = state->root;
+ return;
+ }
+ ParseNode* tmp = state->right_most;
+ while(p_cmp_nodes(tmp, node))
+ tmp = tmp->parent;
+ if(unary_function)
+ {
+ /* If tmp is null, that means, we have to insert new node at root. */
+ if(tmp == NULL)
+ {
+ node->right = state->root;
+ node->right->parent = node;
+
+ state->root = node;
+ }
+ else
+ {
+ node->right = tmp->right;
+ if(node->right)
+ node->right->parent = node;
+
+ tmp->right = node;
+ if(tmp->right)
+ tmp->right->parent = tmp;
+
+ }
+ state->right_most = node;
+ while(state->right_most->right != NULL)
+ state->right_most = state->right_most->right;
+ }
+ else
+ {
+ /* If tmp is null, that means, we have to insert new node at root. */
+ if(tmp == NULL)
+ {
+ node->left = state->root;
+ node->left->parent = node;
+
+ state->root = node;
+ }
+ else
+ {
+ node->left = tmp->right;
+ if(node->left)
+ node->left->parent = node;
+
+ tmp->right = node;
+ if(tmp->right)
+ tmp->right->parent = tmp;
+
+ }
+ state->right_most = node;
+ }
+}
+
+/* Insert binary node into the parse tree. */
+static void
+p_insert_into_tree(ParserState* state, ParseNode* node)
+{
+ p_insert_into_tree_all(state, node, 0);
+}
+
+/* Insert unary node into the parse tree. */
+static void
+p_insert_into_tree_unary(ParserState* state, ParseNode* node)
+{
+ p_insert_into_tree_all(state, node, 1);
+}
+
+/* Recursive call to free every node of parse-tree. */
+static void
+p_destroy_all_nodes(ParseNode* node)
+{
+ if(node == NULL)
+ return;
+ p_destroy_all_nodes(node->left);
+ p_destroy_all_nodes(node->right);
+ /* Don't call free for tokens, as they are allocated and freed in lexer. */
+ /* WARNING: If node->value is freed elsewhere, please assign it NULL before calling p_destroy_all_nodes(). */
+ if(node->value)
+ free(node->value);
+ free(node);
+}
+
+/* Create parser state. */
+ParserState*
+p_create_parser(const gchar* input, MPEquationOptions* options)
+{
+ ParserState* state;
+ state = (ParserState*) malloc(sizeof(ParserState));
+ assert(state != NULL);
+ state->lexer = l_create_lexer(input, state);
+ state->root = NULL;
+ state->depth_level = 0;
+ state->right_most = NULL;
+ state->options = options;
+ state->error = 0;
+ state->error_token = NULL;
+ return state;
+}
+
+static guint statement (ParserState*);
+/* Start parsing input string. And call evaluate on success. */
+guint
+p_parse(ParserState* state)
+{
+ guint ret;
+ LexerToken* token;
+ MPNumber* ans;
+ l_insert_all_tokens(state->lexer);
+ ret = statement(state);
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_ASSIGN)
+ {
+ token = l_get_next_token(state->lexer);
+ if(token->token_type != PL_EOS)
+ {
+ /* Full string is not parsed. */
+ if(!state->error)
+ set_error(state, PARSER_ERR_INVALID, token->string);
+ return PARSER_ERR_INVALID;
+ }
+ }
+ if(token->token_type != PL_EOS)
+ {
+ /* Full string is not parsed. */
+ if(!state->error)
+ set_error(state, PARSER_ERR_INVALID, token->string);
+ return PARSER_ERR_INVALID;
+ }
+ if(ret == 0)
+ /* Input can't be parsed with grammar. */
+ return PARSER_ERR_INVALID;
+ ans = (MPNumber *) (*(state->root->evaluate))(state->root);
+ if(ans)
+ {
+ mp_set_from_mp(ans, &state->ret);
+ free(ans);
+ return PARSER_ERR_NONE;
+ }
+ return PARSER_ERR_INVALID;
+}
+
+/* Destroy parser state. */
+void
+p_destroy_parser(ParserState* state)
+{
+ /* If state has a parse tree, destroy it first. */
+ if(state->root)
+ {
+ p_destroy_all_nodes(state->root);
+ }
+ l_destroy_lexer(state->lexer);
+ free(state);
+}
+
+/* LL (*) parser. Lookahead count depends on tokens. Handle with care. :P */
+
+static guint expression(ParserState* state);
+static guint expression_1(ParserState* state);
+static guint expression_2(ParserState* state);
+static guint unit(ParserState* state);
+static guint variable(ParserState* state);
+static guint term(ParserState* state);
+static guint term_2(ParserState* state);
+
+/* Helping function to p_check_variable. */
+static gchar*
+utf8_next_char(const gchar* c)
+{
+ c++;
+ while((*c & 0xC0) == 0x80)
+ c++;
+ return (gchar *)c;
+}
+
+/* Check if string "name" is a valid variable for given ParserState. It is the same code, used to get the value of variable in parserfunc.c. */
+static gboolean
+p_check_variable(ParserState* state, gchar* name)
+{
+ gint result = 0;
+
+ const gchar *c, *next;
+ gchar *buffer;
+ MPNumber temp;
+
+ if(!(state->get_variable))
+ {
+ return FALSE;
+ }
+
+ /* If defined, then get the variable */
+ if((*(state->get_variable))(state, name, &temp))
+ {
+ return TRUE;
+ }
+
+ /* If has more than one character then assume a multiplication of variables */
+ if(utf8_next_char(name)[0] != '\0')
+ {
+ result = 1;
+ buffer = (gchar*) malloc(sizeof(gchar) * strlen(name));
+ for(c = name; *c != '\0'; c = next)
+ {
+ next = utf8_next_char(c);
+ snprintf(buffer, next - c + 1, "%s", c);
+ if(!(*(state->get_variable))(state, buffer, &temp))
+ {
+ result = 0;
+ break;
+ }
+ }
+ free(buffer);
+ }
+ if(!result)
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static guint
+statement(ParserState* state)
+{
+ LexerToken* token;
+ LexerToken* token_old;
+ ParseNode* node;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_VARIABLE)
+ {
+ token_old = token;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_ASSIGN)
+ {
+ /* VARIABLE = expression. */
+
+ node = p_create_node(state, token_old, p_make_precedence_p(state, P_NumberVariable), p_get_associativity(token_old), NULL, pf_none);
+ p_insert_into_tree(state, node);
+
+ node = p_create_node(state, token, 0, p_get_associativity(token), NULL, pf_set_var);
+ p_insert_into_tree(state, node);
+
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_IN)
+ {
+ /* UNIT in UNIT. */
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ if(!unit(state))
+ return 0;
+ l_get_next_token(state->lexer);
+
+ node = p_create_node(state, token, 0, p_get_associativity(token), NULL, pf_convert_1);
+ p_insert_into_tree(state, node);
+
+ if(!unit(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_SUP_NUMBER)
+ {
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_IN)
+ {
+ /* UNIT in UNIT */
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ if(!unit(state))
+ return 0;
+ l_get_next_token(state->lexer);
+
+ node = p_create_node(state, token, 0, p_get_associativity(token), NULL, pf_convert_1);
+ p_insert_into_tree(state, node);
+
+ if(!unit(state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ }
+ else if(token->token_type == T_NUMBER)
+ {
+ token_old = token;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_VARIABLE)
+ {
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_IN)
+ {
+ /* NUMBER UNIT in UNIT */
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token), NULL, pf_constant);
+ p_insert_into_tree(state, node);
+
+ if(!unit(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+
+ node = p_create_node(state, token, 0, p_get_associativity(token), NULL, pf_convert_number);
+ p_insert_into_tree(state, node);
+
+ if(!unit(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_SUP_NUMBER)
+ {
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_IN)
+ {
+ /* NUMBER UNIT in UNIT */
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token), NULL, pf_constant);
+ p_insert_into_tree(state, node);
+
+ if(!unit(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+
+ node = p_create_node(state, token, 0, p_get_associativity(token), NULL, pf_convert_number);
+ p_insert_into_tree(state, node);
+
+ if(!unit(state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ l_roll_back(state->lexer);
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+}
+
+static guint
+unit(ParserState* state)
+{
+ LexerToken* token;
+ LexerToken* token_old;
+ ParseNode* node;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_VARIABLE)
+ {
+ token_old = token;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_SUP_NUMBER)
+ {
+ /* VARIABLE POWER */
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token_old), pf_make_unit(token_old->string, token->string), pf_none);
+ p_insert_into_tree(state, node);
+
+ return 1;
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ /* VARIABLE */
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token_old), NULL, pf_none);
+ p_insert_into_tree(state, node);
+
+ return 1;
+ }
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ return 0;
+ }
+}
+
+static guint
+expression(ParserState* state)
+{
+ if(!expression_1(state))
+ return 0;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+}
+
+static guint
+expression_1(ParserState* state)
+{
+ LexerToken* token;
+ ParseNode* node;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == PL_EOS
+ ||token->token_type == T_ASSIGN)
+ {
+ l_roll_back(state->lexer);
+ return 0;
+ }
+ if(token->token_type == T_L_R_BRACKET)
+ {
+ state->depth_level++;
+ if(!expression(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_R_R_BRACKET)
+ {
+ state->depth_level--;
+ return 1;
+ }
+ else
+ //Expected ")" here...
+ return 0;
+ }
+ else if(token->token_type == T_L_S_BRACKET)
+ {
+ state->depth_level++;
+
+ /* Give round, preference of P_Unknown aka 0, to keep it on the top of expression. */
+
+ node = p_create_node(state, token, p_make_precedence_p(state, P_Unknown), p_get_associativity(token), NULL, pf_do_round);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_R_S_BRACKET)
+ {
+ state->depth_level--;
+ return 1;
+ }
+ else
+ //Expected "]" here...
+ return 0;
+ }
+ else if(token->token_type == T_L_C_BRACKET)
+ {
+ state->depth_level++;
+
+ /* Give fraction, preference of P_Unknown aka 0, to keep it on the top of expression. */
+
+ node = p_create_node(state, token, p_make_precedence_p(state, P_Unknown), p_get_associativity(token), NULL, pf_do_fraction);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_R_C_BRACKET)
+ {
+ state->depth_level--;
+ return 1;
+ }
+ else
+ //Expected "}" here...
+ return 0;
+ }
+ else if(token->token_type == T_ABS)
+ {
+ state->depth_level++;
+
+ /* Give abs, preference of P_Unknown aka 0, to keep it on the top of expression. */
+
+ node = p_create_node(state, token, p_make_precedence_p(state, P_Unknown), p_get_associativity(token), NULL, pf_do_abs);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_ABS)
+ {
+ state->depth_level--;
+ return 1;
+ }
+ else
+ //Expected "|" here...
+ return 0;
+ }
+ else if(token->token_type == T_NOT)
+ {
+ /* NOT expression */
+
+ node = p_create_node(state, token, p_make_precedence_p(state, P_Not), p_get_associativity(token), NULL, pf_do_not);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_NUMBER)
+ {
+ /* NUMBER */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_constant);
+ p_insert_into_tree(state, node);
+
+ token = l_get_next_token(state->lexer);
+ l_roll_back(state->lexer);
+
+ if(token->token_type == T_FUNCTION
+ ||token->token_type == T_VARIABLE
+ ||token->token_type == T_SUB_NUMBER
+ ||token->token_type == T_ROOT
+ ||token->token_type == T_ROOT_3
+ ||token->token_type == T_ROOT_4)
+ {
+ /* NUMBER variable. */
+
+ node = p_create_node(state, NULL, p_make_precedence_p(state, P_Multiply), p_get_associativity_p(P_Multiply), NULL, pf_do_multiply);
+ p_insert_into_tree(state, node);
+
+ if(!variable(state))
+ return 0;
+ else
+ return 1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ else if(token->token_type == T_L_FLOOR)
+ {
+ state->depth_level++;
+ /* Give floor, preference of P_Unknown aka 0, to keep it on the top of expression. */
+
+ node = p_create_node(state, NULL, p_make_precedence_p(state, P_Unknown), p_get_associativity_p(P_Unknown), NULL, pf_do_floor);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_R_FLOOR)
+ {
+ state->depth_level--;
+ return 1;
+ }
+ else
+ //Expected ⌋ here...
+ return 0;
+ }
+ else if(token->token_type == T_L_CEILING)
+ {
+ state->depth_level++;
+ /* Give ceiling, preference of P_Unknown aka 0, to keep it on the top of expression. */
+
+ node = p_create_node(state, NULL, p_make_precedence_p(state, P_Unknown), p_get_associativity_p(P_Unknown), NULL, pf_do_ceiling);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_R_CEILING)
+ {
+ state->depth_level--;
+ return 1;
+ }
+ else
+ //Expected ⌉ here...
+ return 0;
+ }
+ else if(token->token_type == T_SUBTRACT)
+ {
+ /* UnaryMinus expression */
+
+ node = p_create_node(state, token, p_make_precedence_p(state, P_UnaryMinus), p_get_associativity_p(P_UnaryMinus), NULL, pf_unary_minus);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_ADD)
+ {
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_NUMBER)
+ {
+ /* UnaryPlus expression */
+ /* Ignore T_ADD. It is not required. */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_constant);
+ p_insert_into_tree(state, node);
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ if(!variable(state))
+ return 0;
+ else
+ return 1;
+ }
+}
+
+static guint
+expression_2(ParserState* state)
+{
+ LexerToken* token;
+ ParseNode* node;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_L_R_BRACKET)
+ {
+ /* expression "(" expression ")" */
+
+ node = p_create_node(state, NULL, p_make_precedence_p(state, P_Multiply), p_get_associativity_p(P_Multiply), NULL, pf_do_multiply);
+ p_insert_into_tree(state, node);
+
+ state->depth_level++;
+ if(!expression(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_R_R_BRACKET)
+ {
+ state->depth_level--;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else if(token->token_type == T_POWER)
+ {
+ /* expression "^" expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_x_pow_y);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_SUP_NUMBER)
+ {
+ /* expression T_SUP_NUMBER */
+
+ node = p_create_node(state, NULL, p_make_precedence_p(state, P_Power), p_get_associativity_p(P_Power), NULL, pf_do_x_pow_y_int);
+ p_insert_into_tree(state, node);
+
+ node = p_create_node(state, token, p_make_precedence_p(state, P_NumberVariable), p_get_associativity_p(P_NumberVariable), NULL, pf_none);
+ p_insert_into_tree(state, node);
+
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_NSUP_NUMBER)
+ {
+ /* expression T_NSUP_NUMBER */
+
+ node = p_create_node(state, NULL, p_make_precedence_p(state, P_Power), p_get_associativity_p(P_Power), NULL, pf_do_x_pow_y_int);
+ p_insert_into_tree(state, node);
+
+ node = p_create_node(state, token, p_make_precedence_p(state, P_NumberVariable), p_get_associativity_p(P_NumberVariable), NULL, pf_none);
+ p_insert_into_tree(state, node);
+
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_FACTORIAL)
+ {
+ /* expression T_FACTORIAL */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_factorial);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_MULTIPLY)
+ {
+ /* expression T_MULTIPLY expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_multiply);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_PERCENTAGE)
+ {
+ /* expression % */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_percent);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_AND)
+ {
+ /* expression T_AND expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_and);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_OR)
+ {
+ /* expression T_OR expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_or);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_XOR)
+ {
+ /* expression T_XOR expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_xor);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_DIV)
+ {
+ /* expression T_DIV expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_divide);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_MOD)
+ {
+ /* expression T_MOD expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_mod);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_ADD)
+ {
+ /* expression T_ADD expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_add);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_PERCENTAGE)
+ {
+ //FIXME: This condition needs to be verified for all cases.. :(
+ if(node->right->precedence > P_Percentage)
+ {
+ node->precedence = P_Percentage;
+ node->evaluate = pf_do_add_percent;
+ return 1;
+ }
+ else
+ {
+ /* Assume '%' to be part of 'expression T_PERCENTAGE' statement. */
+ l_roll_back(state->lexer);
+ if(!expression_2(state))
+ return 1;
+ }
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ }
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_SUBTRACT)
+ {
+ /* expression T_SUBTRACT expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_subtract);
+ p_insert_into_tree(state, node);
+
+ if(!expression_1(state))
+ return 0;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_PERCENTAGE)
+ {
+ //FIXME: This condition needs to be verified for all cases.. :(
+ if(node->right->precedence > P_Percentage)
+ {
+ node->precedence = P_Percentage;
+ node->evaluate = pf_do_subtract_percent;
+ return 1;
+ }
+ else
+ {
+ /* Assume '%' to be part of 'expression T_PERCENTAGE' statement. */
+ l_roll_back(state->lexer);
+ if(!expression_2 (state))
+ return 1;
+ }
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ }
+ if(!expression_2(state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ return 1;
+ }
+}
+
+static guint
+variable(ParserState* state)
+{
+ LexerToken* token;
+ LexerToken* token_old;
+ ParseNode* node;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_FUNCTION)
+ {
+ token_old = token;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_SUP_NUMBER)
+ {
+ /* FUNCTION SUP_NUMBER expression */
+ /* Pass power as void * value. That will be taken care in pf_apply_func_with_powre. */
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token_old), token, pf_apply_func_with_power);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_NSUP_NUMBER)
+ {
+ /* FUNCTION NSUP_NUMBER expression */
+ /* Pass power as void * value. That will be taken care in pf_apply_func_with_npowre. */
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token_old), token, pf_apply_func_with_npower);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ /* FUNCTION expression */
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token_old), NULL, pf_apply_func);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ }
+ else if(token->token_type == T_SUB_NUMBER)
+ {
+ token_old = token;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_ROOT)
+ {
+ /* SUB_NUM ROOT expression */
+ /* Pass SUB_NUM as void* value in node. pf_do_nth_root will take care of it. */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), token_old, pf_do_nth_root);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression (state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else if(token->token_type == T_ROOT)
+ {
+ /* ROOT expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_sqrt);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_ROOT_3)
+ {
+ /* ROOT_3 expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_root_3);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_ROOT_4)
+ {
+ /* ROOT_4 expression */
+
+ node = p_create_node(state, token, p_make_precedence_t(state, token->token_type), p_get_associativity(token), NULL, pf_do_root_4);
+ p_insert_into_tree_unary(state, node);
+
+ if(!expression(state))
+ return 0;
+ return 1;
+ }
+ else if(token->token_type == T_VARIABLE)
+ {
+ l_roll_back(state->lexer);
+ //TODO: unknown function ERROR for (T_VARIABLE T_SUP_NUMBER expression).
+ if(!term(state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static guint
+term(ParserState* state)
+{
+ LexerToken* token;
+ LexerToken* token_old;
+ ParseNode* node;
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_VARIABLE)
+ {
+ token_old = token;
+ /* Check if the token is a valid variable or not. */
+ if(!p_check_variable(state, token->string))
+ {
+ set_error(state, PARSER_ERR_UNKNOWN_VARIABLE, token->string);
+ return 0;
+ }
+ token = l_get_next_token(state->lexer);
+ if(token->token_type == T_SUP_NUMBER)
+ {
+ /* VARIABLE SUP_NUMBER */
+ /* Pass power as void* value. pf_get_variable_with_power will take care of it. */
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token_old), token, pf_get_variable_with_power);
+ p_insert_into_tree(state, node);
+
+ }
+ else
+ {
+ l_roll_back(state->lexer);
+ /* VARIABLE */
+
+ node = p_create_node(state, token_old, p_make_precedence_t(state, token_old->token_type), p_get_associativity(token_old), NULL, pf_get_variable);
+ p_insert_into_tree(state, node);
+
+ }
+ if(!term_2(state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static guint
+term_2(ParserState* state)
+{
+ LexerToken* token;
+ ParseNode* node;
+ token = l_get_next_token(state->lexer);
+ l_roll_back(state->lexer);
+ if(token->token_type == PL_EOS
+ ||token->token_type == T_ASSIGN)
+ {
+ return 1;
+ }
+ if(token->token_type == T_VARIABLE)
+ {
+ /* Insert multiply in between two distinct (variable). */
+
+ node = p_create_node(state, NULL, p_make_precedence_p(state, P_Multiply), p_get_associativity_p(P_Multiply), NULL, pf_do_multiply);
+ p_insert_into_tree(state, node);
+
+ if(!term(state))
+ return 0;
+ return 1;
+ }
+ else
+ {
+ return 1;
+ }
+}
diff --git a/src/parser.h b/src/parser.h
new file mode 100644
index 0000000..3efaa03
--- /dev/null
+++ b/src/parser.h
@@ -0,0 +1,79 @@
+#ifndef PARSER_H
+#define PARSER_H
+
+#include <lexer.h>
+
+#include "mp-equation.h"
+#include "mp.h"
+
+/* Operator Associativity. */
+typedef enum
+{
+ LEFT_ASSOCIATIVE,
+ RIGHT_ASSOCIATIVE
+} Associativity;
+
+/* Operator Precedence. */
+typedef enum
+{
+ P_Unknown = 0,
+ P_AddSubtract=1,
+ P_Multiply=2,
+ P_Mod=3,
+ P_Divide=4,
+ P_Not=5,
+ P_Root=6,
+ P_Function=7,
+ P_Boolean=8,
+ P_Percentage=9,
+ /* UnaryMinus and Power must have same precedence. */
+ P_UnaryMinus=10,
+ P_Power=10,
+ P_Factorial=11,
+ P_NumberVariable=12,
+ /* P_Depth should be always at the bottom. It stops node jumping off the current depth level. */
+ P_Depth
+} Precedence;
+
+/* ParseNode structure for parse tree. */
+typedef struct parse_node
+{
+ struct parse_node *parent;
+ struct parse_node *left, *right;
+ LexerToken *token;
+ guint precedence;
+ Associativity associativity;
+ void* value;
+ struct parser_state* state;
+ void* (*evaluate) (struct parse_node* self);
+} ParseNode;
+
+/* ParserState structure. Stores parser state. */
+typedef struct parser_state
+{
+ ParseNode *root;
+ ParseNode *right_most;
+ LexerState *lexer;
+ guint depth_level;
+ MPEquationOptions *options;
+ int error;
+ char *error_token;
+ MPNumber ret;
+ int (*variable_is_defined)(struct parser_state *state, const char *name);
+ int (*get_variable)(struct parser_state *state, const char *name, MPNumber *z);
+ void (*set_variable)(struct parser_state *state, const char *name, const MPNumber *x);
+ int (*function_is_defined)(struct parser_state *state, const char *name);
+ int (*get_function)(struct parser_state *state, const char *name, const MPNumber *x, MPNumber *z);
+ int (*convert)(struct parser_state *state, const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z);
+} ParserState;
+
+/* Create ParserState object. */
+ParserState* p_create_parser(const gchar*, MPEquationOptions*);
+
+/* Destroy ParserState object. */
+void p_destroy_parser(ParserState*);
+
+/* Parse string from ParserState. */
+guint p_parse(ParserState*);
+
+#endif /* PARSER_H */
diff --git a/src/parserfunc.c b/src/parserfunc.c
new file mode 100644
index 0000000..edd34f6
--- /dev/null
+++ b/src/parserfunc.c
@@ -0,0 +1,967 @@
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "parser.h"
+#include "parserfunc.h"
+
+/* Register error variables in ParserState structure. */
+void
+set_error(ParserState* state, gint errorno, const gchar *token)
+{
+ state->error = errorno;
+ if(token)
+ state->error_token = strdup(token);
+}
+
+/* Unused function pointer. This won't be called anytime. */
+void*
+pf_none(ParseNode* self)
+{
+ return NULL;
+}
+
+/* Set variable. */
+void*
+pf_set_var(ParseNode* self)
+{
+ MPNumber* val;
+ val = (MPNumber *) (*(self->right->evaluate))(self->right);
+ if(!val || !(self->state->set_variable))
+ {
+ if(val)
+ free(val);
+ return NULL;
+ }
+ (*(self->state->set_variable))(self->state, self->left->token->string, val);
+ return val;
+}
+
+/* Converts Number from one unit to other. */
+void*
+pf_convert_number(ParseNode* self)
+{
+ gchar* from;
+ gchar* to;
+ gint free_from = 0;
+ gint free_to = 0;
+ MPNumber tmp;
+ MPNumber* ans;
+ ans = (MPNumber *) malloc(sizeof(MPNumber));
+ if(self->left->value)
+ {
+ from = (gchar*) self->left->value;
+ free_from = 1;
+ }
+ else
+ from = self->left->token->string;
+ if(self->right->value)
+ {
+ to = (gchar*) self->right->value;
+ free_to = 1;
+ }
+ else
+ to = self->right->token->string;
+
+ if(mp_set_from_string(self->left->left->token->string, self->state->options->base, &tmp) != 0)
+ {
+ free(ans);
+ ans = NULL;
+ goto END_PF_CONVERT_NUMBER;
+ }
+ if(!(self->state->convert))
+ {
+ free(ans);
+ ans = NULL;
+ goto END_PF_CONVERT_NUMBER;
+ }
+ if(!(*(self->state->convert))(self->state, &tmp, from, to, ans))
+ {
+ set_error(self->state, PARSER_ERR_UNKNOWN_CONVERSION, NULL);
+ free(ans);
+ ans = NULL;
+ }
+END_PF_CONVERT_NUMBER:
+ if(free_from)
+ {
+ g_free(self->left->value);
+ self->left->value = NULL;
+ }
+ if(free_to)
+ {
+ g_free(self->right->value);
+ self->right->value = NULL;
+ }
+ return ans;
+}
+
+/* Conversion rate. */
+void*
+pf_convert_1(ParseNode* self )
+{
+ gchar* from;
+ gchar* to;
+ gint free_from = 0;
+ gint free_to = 0;
+ MPNumber tmp;
+ MPNumber* ans;
+ ans = (MPNumber *) malloc(sizeof(MPNumber));
+ if(self->left->value)
+ {
+ from = (gchar*) self->left->value;
+ free_from = 1;
+ }
+ else
+ from = self->left->token->string;
+ if(self->right->value)
+ {
+ to = (gchar*) self->right->value;
+ free_to = 1;
+ }
+ else
+ to = self->right->token->string;
+ mp_set_from_integer(1, &tmp);
+ if(!(self->state->convert))
+ {
+ free(ans);
+ return NULL;
+ }
+ if(!(*(self->state->convert))(self->state, &tmp, from, to, ans))
+ {
+ set_error(self->state, PARSER_ERR_UNKNOWN_CONVERSION, NULL);
+ free(ans);
+ ans = NULL;
+ }
+ if(free_from)
+ {
+ g_free(self->left->value);
+ self->left->value = NULL;
+ }
+ if(free_to)
+ {
+ g_free(self->right->value);
+ self->right->value = NULL;
+ }
+ return ans;
+}
+
+/* Join source unit and power. */
+gchar*
+pf_make_unit(gchar* source, gchar* power)
+{
+ return g_strjoin(NULL, source, power, NULL);
+}
+
+static gchar *
+utf8_next_char(const gchar *c)
+{
+ c++;
+ while((*c & 0xC0) == 0x80)
+ c++;
+ return(gchar *) c;
+}
+
+/* Get value of variable. */
+void*
+pf_get_variable(ParseNode* self)
+{
+ gint result = 0;
+
+ const gchar *c, *next;
+ gchar *buffer;
+ MPNumber value;
+
+ MPNumber t;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+
+ if(!(self->state->get_variable))
+ {
+ free(ans);
+ return NULL;
+ }
+
+ /* If defined, then get the variable */
+ if((*(self->state->get_variable))(self->state, self->token->string, ans))
+ {
+ return ans;
+ }
+
+ /* If has more than one character then assume a multiplication of variables */
+ if(utf8_next_char(self->token->string)[0] != '\0')
+ {
+ result = 1;
+ buffer = (gchar*) malloc(sizeof(gchar) * strlen(self->token->string));
+ mp_set_from_integer(1, &value);
+ for(c = self->token->string; *c != '\0'; c = next)
+ {
+ next = utf8_next_char(c);
+ snprintf(buffer, next - c + 1, "%s", c);
+ if(!(*(self->state->get_variable))(self->state, buffer, &t))
+ {
+ result = 0;
+ break;
+ }
+ mp_multiply(&value, &t, &value);
+ }
+ free(buffer);
+ if(result)
+ mp_set_from_mp(&value, ans);
+ }
+ if(!result)
+ {
+ free (ans);
+ ans = NULL;
+ set_error(self->state, PARSER_ERR_UNKNOWN_VARIABLE, self->token->string);
+ }
+ return ans;
+}
+
+/* Get value of variable with power. */
+void*
+pf_get_variable_with_power(ParseNode* self)
+{
+ gint result = 0;
+ gint pow;
+
+ const gchar *c, *next;
+ gchar *buffer;
+ MPNumber value;
+
+ MPNumber t;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ pow = super_atoi(((LexerToken*) self->value)->string);
+
+ /* No need to free the memory. It is allocated and freed somewhere else. */
+ self->value = NULL;
+
+ if(!(self->state->get_variable))
+ {
+ free(ans);
+ return NULL;
+ }
+
+ /* If defined, then get the variable */
+ if((*(self->state->get_variable))(self->state, self->token->string, ans))
+ {
+ mp_xpowy_integer(ans, pow, ans);
+ return ans;
+ }
+
+ /* If has more than one character then assume a multiplication of variables */
+ if(utf8_next_char(self->token->string)[0] != '\0')
+ {
+ result = 1;
+ buffer = (gchar*) malloc(sizeof(gchar) * strlen(self->token->string));
+ mp_set_from_integer(1, &value);
+ for(c = self->token->string; *c != '\0'; c = next)
+ {
+ next = utf8_next_char(c);
+ snprintf(buffer, next - c + 1, "%s", c);
+ if(!(*(self->state->get_variable))(self->state, buffer, &t))
+ {
+ result = 0;
+ break;
+ }
+
+ /* If last term do power */
+ if(*next == '\0')
+ mp_xpowy_integer(&t, pow, &t);
+ mp_multiply(&value, &t, &value);
+ }
+ free(buffer);
+ if(result)
+ mp_set_from_mp(&value, ans);
+ }
+ if(!result)
+ {
+ free(ans);
+ ans = NULL;
+ set_error(self->state, PARSER_ERR_UNKNOWN_VARIABLE, self->token->string);
+ }
+ return ans;
+}
+
+/* Apply function on child. */
+void*
+pf_apply_func(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!(self->state->get_function))
+ {
+ free(val);
+ free(ans);
+ return NULL;
+ }
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ if(!(*(self->state->get_function))(self->state, self->token->string, val, ans))
+ {
+ free(val);
+ free(ans);
+ set_error(self->state, PARSER_ERR_UNKNOWN_FUNCTION, self->token->string);
+ return NULL;
+ }
+ free(val);
+ return ans;
+}
+
+/* Apply function with +ve power. */
+void*
+pf_apply_func_with_power(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* tmp;
+ MPNumber* ans;
+ gint pow;
+ tmp = (MPNumber*) malloc(sizeof(MPNumber));
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!(self->state->get_function))
+ {
+ free(tmp);
+ free(ans);
+ free(val);
+ self->value = NULL;
+ return NULL;
+ }
+ if(!val)
+ {
+ free(tmp);
+ free(ans);
+ self->value = NULL;
+ return NULL;
+ }
+ if(!(*(self->state->get_function))(self->state, self->token->string, val, tmp))
+ {
+ free(tmp);
+ free(ans);
+ free(val);
+ self->value = NULL;
+ set_error(self->state, PARSER_ERR_UNKNOWN_FUNCTION, self->token->string);
+ return NULL;
+ }
+ pow = super_atoi(((LexerToken*) self->value)->string);
+ mp_xpowy_integer(tmp, pow, ans);
+ free(val);
+ free(tmp);
+ self->value = NULL;
+ return ans;
+}
+
+/* Apply function with -ve power. */
+void*
+pf_apply_func_with_npower(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* tmp;
+ MPNumber* ans;
+ gint pow;
+ gchar* inv_name;
+ inv_name = (gchar*) malloc(sizeof(gchar) * strlen(self->token->string) + strlen("⁻¹") + 1);
+ strcpy(inv_name, self->token->string);
+ strcat(inv_name, "⁻¹");
+ tmp = (MPNumber*) malloc(sizeof(MPNumber));
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(tmp);
+ free(inv_name);
+ free(ans);
+ self->value = NULL;
+ return NULL;
+ }
+ if(!(self->state->get_function))
+ {
+ free(tmp);
+ free(ans);
+ free(inv_name);
+ self->value = NULL;
+ return NULL;
+ }
+ if(!(*(self->state->get_function))(self->state, inv_name, val, tmp))
+ {
+ free(tmp);
+ free(ans);
+ free(val);
+ free(inv_name);
+ self->value = NULL;
+ set_error(self->state, PARSER_ERR_UNKNOWN_FUNCTION, self->token->string);
+ return NULL;
+ }
+ pow = super_atoi(((LexerToken*) self->value)->string);
+ mp_xpowy_integer(tmp, -pow, ans);
+ free(val);
+ free(tmp);
+ free(inv_name);
+ self->value = NULL;
+ return ans;
+}
+
+/* Find nth root of child. */
+void*
+pf_do_nth_root(ParseNode* self)
+{
+ MPNumber* val;
+ gint pow;
+ MPNumber* ans;
+ pow = sub_atoi(((LexerToken*) self->value)->string);
+ self->value = NULL;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_root(val, pow, ans);
+ free(val);
+ return ans;
+}
+
+/* Find sqrt of child. */
+void*
+pf_do_sqrt(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_sqrt(val, ans);
+ free(val);
+ return ans;
+}
+
+/* Find 3rd root of child. */
+void*
+pf_do_root_3(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_root(val, 3, ans);
+ free(val);
+ return ans;
+}
+
+/* Find 4th root of child. */
+void*
+pf_do_root_4(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_root(val, 4, ans);
+ free(val);
+ return ans;
+}
+
+/* Apply floor function. */
+void*
+pf_do_floor(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_floor(val, ans);
+ free(val);
+ return ans;
+}
+
+/* Apply ceiling function. */
+void* pf_do_ceiling (ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_ceiling(val, ans);
+ free(val);
+ return ans;
+}
+
+/* Round off. */
+void*
+pf_do_round(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_round(val, ans);
+ free(val);
+ return ans;
+}
+
+/* Fraction. */
+void*
+pf_do_fraction(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_fractional_part(val, ans);
+ free(val);
+ return ans;
+}
+
+/* Absolute value. */
+void*
+pf_do_abs(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_abs(val, ans);
+ free(val);
+ return ans;
+}
+
+/* Find x^y for x and y being MPNumber. */
+void*
+pf_do_x_pow_y(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* pow;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->left->evaluate))(self->left);
+ pow = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val || !pow)
+ {
+ if(val)
+ free(val);
+ if(pow)
+ free(pow);
+ free(ans);
+ return NULL;
+ }
+ mp_xpowy(val, pow, ans);
+ free(val);
+ free(pow);
+ return ans;
+}
+
+/* Find x^y for MPNumber x and integer y. */
+void*
+pf_do_x_pow_y_int(ParseNode* self)
+{
+ MPNumber* val;
+ gint pow;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->left->evaluate))(self->left);
+ pow = super_atoi(self->right->token->string);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_xpowy_integer(val, pow, ans);
+ free(val);
+ return ans;
+}
+
+/* Find factorial. */
+void*
+pf_do_factorial(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_factorial(val, ans);
+ free(val);
+ return ans;
+}
+
+/* Apply unary minus. */
+void*
+pf_unary_minus(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_invert_sign(val, ans);
+ free(val);
+ return ans;
+}
+
+/* Divide. */
+void*
+pf_do_divide(ParseNode* self)
+{
+ MPNumber* left;
+ MPNumber* right;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ left = (MPNumber*) (*(self->left->evaluate))(self->left);
+ right = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!left || !right)
+ {
+ if(left)
+ free(left);
+ if(right)
+ free(right);
+ free(ans);
+ return NULL;
+ }
+ mp_divide(left, right, ans);
+ free(left);
+ free(right);
+ return ans;
+}
+
+/* Modulus. */
+void*
+pf_do_mod(ParseNode* self)
+{
+ MPNumber* left;
+ MPNumber* right;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ left = (MPNumber*) (*(self->left->evaluate))(self->left);
+ right = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!left || !right)
+ {
+ if(left)
+ free(left);
+ if(right)
+ free(right);
+ free(ans);
+ return NULL;
+ }
+ mp_modulus_divide(left, right, ans);
+ free(left);
+ free(right);
+ return ans;
+}
+
+/* Multiply two numbers. */
+void*
+pf_do_multiply(ParseNode* self)
+{
+ MPNumber* left;
+ MPNumber* right;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ left = (MPNumber*) (*(self->left->evaluate))(self->left);
+ right = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!left || !right)
+ {
+ if(left)
+ free(left);
+ if(right)
+ free(right);
+ free(ans);
+ return NULL;
+ }
+ mp_multiply(left, right, ans);
+ free(left);
+ free(right);
+ return ans;
+}
+
+/* Subtract two numbers. */
+void*
+pf_do_subtract(ParseNode* self)
+{
+ MPNumber* left;
+ MPNumber* right;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ left = (MPNumber*) (*(self->left->evaluate))(self->left);
+ right = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!left || !right)
+ {
+ if(left)
+ free(left);
+ if(right)
+ free (right);
+ free(ans);
+ return NULL;
+ }
+ mp_subtract(left, right, ans);
+ free(left);
+ free(right);
+ return ans;
+}
+
+/* Add two numbers. */
+void*
+pf_do_add(ParseNode* self)
+{
+ MPNumber* left;
+ MPNumber* right;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ left = (MPNumber*) (*(self->left->evaluate))(self->left);
+ right = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!left || !right)
+ {
+ if(left)
+ free(left);
+ if(right)
+ free(right);
+ free(ans);
+ return NULL;
+ }
+ mp_add(left, right, ans);
+ free(left);
+ free(right);
+ return ans;
+}
+
+/*Add (x) Percentage to value. */
+void*
+pf_do_add_percent(ParseNode* self)
+{
+ MPNumber* ans;
+ MPNumber* val;
+ MPNumber* per;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->left->evaluate))(self->left);
+ per = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val || !per)
+ {
+ if(val)
+ free(val);
+ if(per)
+ free(per);
+ free(ans);
+ return NULL;
+ }
+ mp_add_integer(per, 100, per);
+ mp_divide_integer(per, 100, per);
+ mp_multiply(val, per, ans);
+ free(val);
+ free(per);
+ return ans;
+}
+
+/* Subtract (x) Percentage to value. */
+void*
+pf_do_subtract_percent(ParseNode* self)
+{
+ MPNumber* ans;
+ MPNumber* val;
+ MPNumber* per;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->left->evaluate))(self->left);
+ per = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val || !per)
+ {
+ if(val)
+ free(val);
+ if(per)
+ free(per);
+ free(ans);
+ return NULL;
+ }
+ mp_add_integer(per, -100, per);
+ mp_divide_integer(per, -100, per);
+ mp_multiply(val, per, ans);
+ free(val);
+ free(per);
+ return ans;
+}
+
+/* Converts a constant into percentage. */
+void*
+pf_do_percent(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ mp_divide_integer(val, 100, ans);
+ free(val);
+ return ans;
+}
+
+/* NOT. */
+void*
+pf_do_not(ParseNode* self)
+{
+ MPNumber* val;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ val = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!val)
+ {
+ free(ans);
+ return NULL;
+ }
+ if(!mp_is_overflow(val, self->state->options->wordlen))
+ {
+ set_error(self->state, PARSER_ERR_OVERFLOW, NULL);
+ free(ans);
+ ans = NULL;
+ }
+ mp_not(val, self->state->options->wordlen, ans);
+ free(val);
+ return ans;
+}
+
+/* AND. */
+void*
+pf_do_and(ParseNode* self)
+{
+ MPNumber* left;
+ MPNumber* right;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ left = (MPNumber*) (*(self->left->evaluate))(self->left);
+ right = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!left || !right)
+ {
+ if(left)
+ free(left);
+ if(right)
+ free(right);
+ free(ans);
+ return NULL;
+ }
+ mp_and(left, right, ans);
+ free(left);
+ free(right);
+ return ans;
+}
+
+/* OR. */
+void*
+pf_do_or(ParseNode* self)
+{
+ MPNumber* left;
+ MPNumber* right;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ left = (MPNumber*) (*(self->left->evaluate))(self->left);
+ right = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!left || !right)
+ {
+ if(left)
+ free(left);
+ if(right)
+ free(right);
+ free(ans);
+ return NULL;
+ }
+ mp_or(left, right, ans);
+ free(left);
+ free(right);
+ return ans;
+}
+
+/* XOR. */
+void*
+pf_do_xor(ParseNode* self)
+{
+ MPNumber* left;
+ MPNumber* right;
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ left = (MPNumber*) (*(self->left->evaluate))(self->left);
+ right = (MPNumber*) (*(self->right->evaluate))(self->right);
+ if(!left || !right)
+ {
+ if(left)
+ free(left);
+ if(right)
+ free(right);
+ free(ans);
+ return NULL;
+ }
+ mp_xor(left, right, ans);
+ free(left);
+ free(right);
+ return ans;
+}
+
+/* Constant value. Convert into MPNumber and return. */
+void*
+pf_constant(ParseNode* self)
+{
+ MPNumber* ans;
+ ans = (MPNumber*) malloc(sizeof(MPNumber));
+ if(mp_set_from_string(self->token->string, self->state->options->base, ans) != 0)
+ {
+ /* This should never happen, as l_check_if_number() has already passed the string once. */
+ /* If the code reaches this point, something is really wrong. X( */
+ free(ans);
+ set_error(self->state, PARSER_ERR_INVALID, self->token->string);
+ return NULL;
+ }
+ return ans;
+}
+
diff --git a/src/parserfunc.h b/src/parserfunc.h
new file mode 100644
index 0000000..cf049b0
--- /dev/null
+++ b/src/parserfunc.h
@@ -0,0 +1,80 @@
+#ifndef PAESERFUNC_H
+#define PARSERFUNC_H
+
+#include "parser.h"
+
+void set_error(ParserState*, gint, const gchar*);
+
+void* pf_none(ParseNode*);
+
+void* pf_set_var(ParseNode*);
+
+void* pf_convert_number(ParseNode*);
+
+void* pf_convert_1(ParseNode*);
+
+gchar* pf_make_unit(gchar* source, gchar* power);
+
+void* pf_get_variable(ParseNode*);
+
+void* pf_get_variable_with_power(ParseNode*);
+
+void* pf_apply_func(ParseNode*);
+
+void* pf_apply_func_with_power(ParseNode*);
+
+void* pf_apply_func_with_npower(ParseNode*);
+
+void* pf_do_nth_root(ParseNode*);
+
+void* pf_do_sqrt(ParseNode*);
+
+void* pf_do_root_3(ParseNode*);
+
+void* pf_do_root_4(ParseNode*);
+
+void* pf_do_floor(ParseNode*);
+
+void* pf_do_ceiling(ParseNode*);
+
+void* pf_do_round(ParseNode*);
+
+void* pf_do_fraction(ParseNode*);
+
+void* pf_do_abs(ParseNode*);
+
+void* pf_do_x_pow_y(ParseNode*);
+
+void* pf_do_x_pow_y_int(ParseNode*);
+
+void* pf_do_factorial(ParseNode*);
+
+void* pf_unary_minus(ParseNode*);
+
+void* pf_do_divide(ParseNode*);
+
+void* pf_do_mod(ParseNode*);
+
+void* pf_do_multiply(ParseNode*);
+
+void* pf_do_subtract(ParseNode*);
+
+void* pf_do_add(ParseNode*);
+
+void* pf_do_add_percent(ParseNode*);
+
+void* pf_do_subtract_percent(ParseNode*);
+
+void* pf_do_percent(ParseNode*);
+
+void* pf_do_not(ParseNode*);
+
+void* pf_do_and(ParseNode*);
+
+void* pf_do_or(ParseNode*);
+
+void* pf_do_xor(ParseNode*);
+
+void* pf_constant(ParseNode*);
+
+#endif /* PARSERFUNC_H */
diff --git a/src/prelexer.c b/src/prelexer.c
new file mode 100644
index 0000000..225d499
--- /dev/null
+++ b/src/prelexer.c
@@ -0,0 +1,214 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <assert.h>
+
+#include "prelexer.h"
+
+/* Creates a scanner state which will be useful for accessing the lexer later. */
+PreLexerState*
+pl_create_scanner(const gchar* input)
+{
+ PreLexerState* state;
+ assert(input != NULL);
+ assert(g_utf8_validate(input, -1, NULL));
+ state = (PreLexerState *) malloc(sizeof(PreLexerState));
+ assert(state != NULL);
+ state->stream = g_strdup(input);
+ state->length = strlen(state->stream); /* Can't find a GLib replacement of strlen. The mailing list discussion says, it is not implemented because strlen is perfectly capable. :) */
+ state->next_index = 0;
+ state->mark_index = 0;
+ return state;
+}
+
+/* Destroy and free memory used by LexerState object. */
+void
+pl_destroy_scanner(PreLexerState* state)
+{
+ free(state->stream);
+ free(state);
+}
+
+/* Roll back last scanned unichar. */
+void
+pl_roll_back(PreLexerState* state)
+{
+ gchar* tmp;
+ tmp = g_utf8_find_prev_char(state->stream, state->stream + state->next_index);
+ if(tmp == NULL)
+ /* Already at the beginning of the stram. Reset index. */
+ state->next_index = 0;
+ else
+ state->next_index = tmp - state->stream;
+}
+
+/* Get validated gunichar from input stream. */
+gunichar
+pl_get_next_gunichar(PreLexerState* state)
+{
+ gunichar ret;
+ if(state->next_index >= state->length)
+ {
+ /* To prevent scanning last letter multiple times, when a single unconditional rollback is used. */
+ if(state->next_index == state->length)
+ state->next_index++;
+ return 0;
+ }
+ ret = g_utf8_get_char_validated(state->stream + state->next_index, -1);
+ state->next_index = g_utf8_next_char(state->stream + state->next_index) - state->stream;
+ return ret;
+}
+
+/* Set marker index. To be used for highlighting and error reporting. */
+void
+pl_set_marker(PreLexerState* state)
+{
+ state->mark_index = state->next_index;
+}
+
+/* Get marked substring. To be used for error reporting. */
+gchar*
+pl_get_marked_substring(const PreLexerState* state)
+{
+ return g_strndup(state->stream + state->mark_index, state->next_index - state->mark_index);
+}
+
+/* Compares a list of strings with given unichar. To be used by pl_get_next_token() only. */
+static gboolean
+pl_compare_all(const gunichar ch, const gint count, gchar *arr[])
+{
+ gint l;
+ for(l = 0; l < count; l++)
+ {
+ if(ch == g_utf8_get_char_validated(arr[l], -1))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Pre-Lexer tokanizer. To be called only by Lexer. */
+LexerTokenType
+pl_get_next_token(PreLexerState* state)
+{
+ gunichar ch = pl_get_next_gunichar(state);
+ if(pl_compare_all(ch, 2, (gchar*[]){",","."}))
+ return PL_DECIMAL;
+
+ if(g_unichar_isdigit(ch) || pl_compare_all(ch, 10, (gchar*[]){"〇","〡","〢","〣","〤","〥","〦","〧","〨","〩"}))
+ return PL_DIGIT; /* 0-9 */
+
+ if(g_unichar_isxdigit(ch))
+ return PL_HEX; /* This is supposed to report just the A-F. */
+
+ if(pl_compare_all(ch, 10, (gchar*[]){"⁰","¹","²","³","⁴","⁵","⁶","⁷","⁸","⁹"}))
+ return PL_SUPER_DIGIT;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"⁻"}))
+ return PL_SUPER_MINUS;
+
+ if(pl_compare_all(ch, 10, (gchar*[]){"₀","₁","₂","₃","₄","₅","₆","₇","₈","₉"}))
+ return PL_SUB_DIGIT;
+
+ if(pl_compare_all(ch, 15, (gchar*[]){"½","⅓","⅔","¼","¾","⅕","⅖","⅗","⅘","⅙","⅚","⅛","⅜","⅝","⅞"}))
+ return PL_FRACTION;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"°"}))
+ return PL_DEGREE;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"'"}))
+ return PL_MINUTE;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"\""}))
+ return PL_SECOND;
+
+ if(g_unichar_isalpha(ch))
+ return PL_LETTER; /* All alphabets excluding A-F. [a-fA-F] are reported as PL_HEX. */
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"∧"}))
+ return T_AND;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"∨"}))
+ return T_OR;
+
+ if(pl_compare_all(ch, 2, (gchar*[]){"⊻","⊕"}))
+ return T_XOR;
+
+ if(pl_compare_all(ch, 2, (gchar*[]){"¬","~"}))
+ return T_NOT;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"+"}))
+ return T_ADD;
+
+ if(pl_compare_all(ch, 3, (gchar*[]){"-","−","–"}))
+ return T_SUBTRACT;
+
+ if(pl_compare_all(ch, 2, (gchar*[]){"*","×"}))
+ return T_MULTIPLY;
+
+ if(pl_compare_all(ch, 3, (gchar*[]){"/","∕","÷"}))
+ return T_DIV;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"⌊"}))
+ return T_L_FLOOR;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"⌋"}))
+ return T_R_FLOOR;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"⌈"}))
+ return T_L_CEILING;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"⌉"}))
+ return T_R_CEILING;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"√"}))
+ return T_ROOT;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"∛"}))
+ return T_ROOT_3;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"∜"}))
+ return T_ROOT_4;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"="}))
+ return T_ASSIGN;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"("}))
+ return T_L_R_BRACKET;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){")"}))
+ return T_R_R_BRACKET;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"["}))
+ return T_L_S_BRACKET;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"]"}))
+ return T_R_S_BRACKET;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"{"}))
+ return T_L_C_BRACKET;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"}"}))
+ return T_R_C_BRACKET;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"|"}))
+ return T_ABS;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"^"}))
+ return T_POWER;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"!"}))
+ return T_FACTORIAL;
+
+ if(pl_compare_all(ch, 1, (gchar*[]){"%"}))
+ return T_PERCENTAGE;
+
+ if(pl_compare_all(ch, 4, (gchar*[]){" ","\r","\t","\n"}))
+ /* Gotta ignore'Em all!!! ;) */
+ return PL_SKIP;
+
+ if(ch == 0)
+ return PL_EOS;
+
+ /* There is no spoon. */
+ return T_UNKNOWN;
+}
diff --git a/src/prelexer.h b/src/prelexer.h
new file mode 100644
index 0000000..c206656
--- /dev/null
+++ b/src/prelexer.h
@@ -0,0 +1,93 @@
+#ifndef PRE_LEXER_H
+#define PRE_LEXER_H
+
+#include <glib.h>
+
+/* Structure to store lexer state. */
+typedef struct
+{
+ gchar* stream; /* Pointer to the local copy of input string. */
+ guint length; /* Length of input string; stored to reduce calls to strlen(). */
+ guint next_index; /* Index of next (to be read) character from input. */
+ guint mark_index; /* Location, last marked. Useful for getting substrings as part of highlighting */
+} PreLexerState;
+
+/* Enum for tokens generated by pre-lexer and lexer. */
+typedef enum
+{
+ T_UNKNOWN=0, //Unknown
+
+ /* These are all Pre-Lexer tokens, returned by pre-lexer */
+ PL_DECIMAL, //Decimal saperator
+ PL_DIGIT, //Decimal digit
+ PL_HEX, //A-F of Hex digits
+ PL_SUPER_DIGIT, //Super digits
+ PL_SUPER_MINUS, //Super minus
+ PL_SUB_DIGIT, //Sub digits
+ PL_FRACTION, //Fractions
+ PL_DEGREE, //Degree
+ PL_MINUTE, //Minutes
+ PL_SECOND, //10 //Seconds
+ PL_LETTER, //Alphabets
+ PL_EOS, //End of stream. Yay!!
+ PL_SKIP, //Skip this symbol (whitespace or newline).
+
+ /* These are all tokens, returned by Lexer. */
+ T_ADD, //Plus
+ T_SUBTRACT, //Minus
+ T_MULTIPLY, //Multiply
+ T_DIV, //Divide (note can't use T_DIVIDE as it is defined in *BSD as an "integer divide fault" trap type value)
+ T_MOD, //Modulus
+ T_L_FLOOR, //Floor ( Left )
+ T_R_FLOOR, //20 //Floor ( Right )
+ T_L_CEILING, //Ceiling ( Left )
+ T_R_CEILING, //Ceiling ( Right )
+ T_ROOT, //Square root
+ T_ROOT_3, //Cube root
+ T_ROOT_4, //Fourth root
+ T_NOT, //Bitwise NOT
+ T_AND, //Bitwise AND
+ T_OR, //Bitwise OR
+ T_XOR, //Bitwise XOR
+ T_IN, //30 //IN ( for converter )
+ T_NUMBER, //Number
+ T_SUP_NUMBER, //Super Number
+ T_NSUP_NUMBER, //Negative Super Number
+ T_SUB_NUMBER, //Sub Number
+ T_FUNCTION, //Function
+ T_VARIABLE, //Variable name
+ T_ASSIGN, //=
+ T_L_R_BRACKET, //40 //(
+ T_R_R_BRACKET, //)
+ T_L_S_BRACKET, //[
+ T_R_S_BRACKET, //]
+ T_L_C_BRACKET, //{
+ T_R_C_BRACKET, //}
+ T_ABS, //|
+ T_POWER, //^
+ T_FACTORIAL, //!
+ T_PERCENTAGE //49 //%
+} LexerTokenType;
+
+/* Creates a scanner state. Useful when multiple scanners are in action. */
+PreLexerState* pl_create_scanner(const gchar*);
+
+/* Destroy and free memory used by LexerState object. */
+void pl_destroy_scanner(PreLexerState*);
+
+/* Roll back last scanned unichar. */
+void pl_roll_back(PreLexerState*);
+
+/* Get validated gunichar from input stream. */
+gunichar pl_get_next_gunichar(PreLexerState*);
+
+/* Set marker index. To be used for highlighting and error reporting. */
+void pl_set_marker(PreLexerState*);
+
+/* Get marked substring. To be used for error reporting. */
+gchar* pl_get_marked_substring(const PreLexerState*);
+
+/* Get next Pre-Lexer token from stream. */
+LexerTokenType pl_get_next_token(PreLexerState*);
+
+#endif /* PRE_LEXER_H */
diff --git a/src/test-mp-equation.c b/src/test-mp-equation.c
new file mode 100644
index 0000000..95bb4a8
--- /dev/null
+++ b/src/test-mp-equation.c
@@ -0,0 +1,630 @@
+/*
+ * 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 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>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <locale.h>
+
+#include "mp-equation.h"
+#include "mp-serializer.h"
+#include "unit-manager.h"
+
+static MPEquationOptions options;
+
+static int fails = 0;
+static int passes = 0;
+
+/* If we're not using GNU C, elide __attribute__ */
+#ifndef __GNUC__
+# define __attribute__(x) /*NOTHING*/
+#endif
+
+static void pass(const char *format, ...) __attribute__((format(printf, 1, 2)));
+static void fail(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+
+static void pass(const char *format, ...)
+{
+/* va_list args;
+
+ printf(" PASS: ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+*/
+ passes += 1;
+}
+
+static void fail(const char *format, ...)
+{
+ va_list args;
+
+ printf("*FAIL: ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+ fails++;
+}
+
+
+static const char *
+error_code_to_string(MPErrorCode error)
+{
+ static char error_string[1024];
+
+ if (error != PARSER_ERR_MP)
+ return mp_error_code_to_string(error);
+
+ snprintf(error_string, 1024, "PARSER_ERR_MP(\"%s\")", mp_get_error());
+ return error_string;
+}
+
+
+static void
+test(char *expression, char *expected, int expected_error)
+{
+ MPErrorCode error;
+ MPNumber result;
+
+ error = mp_equation_parse(expression, &options, &result, NULL);
+
+ if (error == 0) {
+ char *result_str;
+ MpSerializer *serializer;
+
+ serializer = mp_serializer_new(MP_DISPLAY_FORMAT_FIXED, options.base, 9);
+ result_str = mp_serializer_to_string(serializer, &result);
+ g_object_unref(serializer);
+
+ if(expected_error != PARSER_ERR_NONE)
+ fail("'%s' -> %s, expected error %s", expression, result_str, error_code_to_string(expected_error));
+ else if(strcmp(result_str, expected) != 0)
+ fail("'%s' -> '%s', expected '%s'", expression, result_str, expected);
+ else
+ pass("'%s' -> '%s'", expression, result_str);
+ g_free(result_str);
+ }
+ else {
+ if(error == expected_error)
+ pass("'%s' -> error %s", expression, error_code_to_string(error));
+ else if(expected_error == PARSER_ERR_NONE)
+ fail("'%s' -> error %s, expected result %s", expression,
+ error_code_to_string(error), expected);
+ else
+ fail("'%s' -> error %s, expected error %s", expression,
+ error_code_to_string(error), error_code_to_string(expected_error));
+ }
+}
+
+
+static int
+do_convert(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data)
+{
+ return unit_manager_convert_by_symbol(unit_manager_get_default(), x, x_units, z_units, z);
+}
+
+
+static void
+test_conversions()
+{
+ memset(&options, 0, sizeof(options));
+ options.base = 10;
+ options.wordlen = 32;
+ options.angle_units = MP_DEGREES;
+ options.convert = do_convert;
+
+ /* Angle units */
+ //test("π radians in degrees", "180", 0);
+ test("100 gradians in degrees", "90", 0);
+
+ /* Length */
+ test("1 meter in mm", "1000", 0);
+ test("1m in mm", "1000", 0);
+ test("1 inch in cm", "2.54", 0);
+
+ /* Area */
+ test("1m² in mm²", "1000000", 0);
+
+ /* Volume */
+ test("1m³ in mm³", "1000000000", 0);
+
+ /* Weight */
+ test("1 kg in pounds", "2.204622622", 0);
+
+ /* Duration */
+ test("1 minute in seconds", "60", 0);
+ test("1s in ms", "1000", 0);
+
+ /* Temperature */
+ //test("100˚C in ˚F", "", 0);
+ //test("0˚C in ˚F", "32", 0);
+ //test("0˚K in ˚C", "−273.15", 0);
+ test("100degC in degF", "212", 0);
+ test("0degC in degF", "32", 0);
+ test("0 K in degC", "−273.15", 0);
+}
+
+
+static int
+variable_is_defined(const char *name, void *data)
+{
+ return strcmp (name, "x") == 0 || strcmp (name, "y") == 0;
+}
+
+
+static int
+get_variable(const char *name, MPNumber *z, void *data)
+{
+ if (strcmp (name, "x") == 0) {
+ mp_set_from_integer (2, z);
+ return 1;
+ }
+ if (strcmp (name, "y") == 0) {
+ mp_set_from_integer (3, z);
+ return 1;
+ }
+ return 0;
+}
+
+
+static void
+set_variable(const char *name, const MPNumber *x, void *data)
+{
+}
+
+static void
+test_equations()
+{
+ memset(&options, 0, sizeof(options));
+ options.base = 10;
+ options.wordlen = 32;
+ options.angle_units = MP_DEGREES;
+ options.variable_is_defined = variable_is_defined;
+ options.get_variable = get_variable;
+ options.set_variable = set_variable;
+
+ options.base = 2;
+ test("2₁₀", "10", 0);
+
+ options.base = 8;
+ test("16434824₁₀", "76543210", 0);
+
+ options.base = 16;
+ test("FF", "FF", 0);
+ test("18364758544493064720₁₀", "FEDCBA9876543210", 0);
+
+ options.base = 10;
+ test("0₂", "0", 0); test("0₈", "0", 0); test("0", "0", 0); test("0₁₆", "0", 0);
+ test("1₂", "1", 0); test("1₈", "1", 0); test("1", "1", 0); test("1₁₆", "1", 0);
+ test("2₂", "", PARSER_ERR_INVALID); test("2₈", "2", 0); test("2", "2", 0); test("2₁₆", "2", 0);
+ test("3₂", "", PARSER_ERR_INVALID); test("3₈", "3", 0); test("3", "3", 0); test("3₁₆", "3", 0);
+ test("4₂", "", PARSER_ERR_INVALID); test("4₈", "4", 0); test("4", "4", 0); test("4₁₆", "4", 0);
+ test("5₂", "", PARSER_ERR_INVALID); test("5₈", "5", 0); test("5", "5", 0); test("5₁₆", "5", 0);
+ test("6₂", "", PARSER_ERR_INVALID); test("6₈", "6", 0); test("6", "6", 0); test("6₁₆", "6", 0);
+ test("7₂", "", PARSER_ERR_INVALID); test("7₈", "7", 0); test("7", "7", 0); test("7₁₆", "7", 0);
+ test("8₂", "", PARSER_ERR_INVALID); test("8₈", "", PARSER_ERR_INVALID); test("8", "8", 0); test("8₁₆", "8", 0);
+ test("9₂", "", PARSER_ERR_INVALID); test("9₈", "", PARSER_ERR_INVALID); test("9", "9", 0); test("9₁₆", "9", 0);
+ test("A₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("A₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("A", "", PARSER_ERR_UNKNOWN_VARIABLE); test("A₁₆", "10", 0);
+ test("B₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("B₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("B", "", PARSER_ERR_UNKNOWN_VARIABLE); test("B₁₆", "11", 0);
+ test("C₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("C₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("C", "", PARSER_ERR_UNKNOWN_VARIABLE); test("C₁₆", "12", 0);
+ test("D₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("D₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("D", "", PARSER_ERR_UNKNOWN_VARIABLE); test("D₁₆", "13", 0);
+ test("E₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("E₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("E", "", PARSER_ERR_UNKNOWN_VARIABLE); test("E₁₆", "14", 0);
+ test("F₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("F₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("F", "", PARSER_ERR_UNKNOWN_VARIABLE); test("F₁₆", "15", 0);
+ test("a₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("a₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("a", "", PARSER_ERR_UNKNOWN_VARIABLE); test("a₁₆", "10", 0);
+ test("b₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("b₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("b", "", PARSER_ERR_UNKNOWN_VARIABLE); test("b₁₆", "11", 0);
+ test("c₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("c₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("c", "", PARSER_ERR_UNKNOWN_VARIABLE); test("c₁₆", "12", 0);
+ test("d₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("d₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("d", "", PARSER_ERR_UNKNOWN_VARIABLE); test("d₁₆", "13", 0);
+ test("e₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("e₈", "", PARSER_ERR_UNKNOWN_VARIABLE); /* e is a built-in variable */ test("e₁₆", "14", 0);
+ test("f₂", "", PARSER_ERR_UNKNOWN_VARIABLE); test("f₈", "", PARSER_ERR_UNKNOWN_VARIABLE); test("f", "", PARSER_ERR_UNKNOWN_VARIABLE); test("f₁₆", "15", 0);
+
+ test("+1", "1", 0);
+ test("−1", "−1", 0);
+ test("+ 1", "1", 0); // FIXME: Should this be allowed?
+ test("− 1", "−1", 0); // FIXME: Should this be allowed?
+ test("++1", "1", PARSER_ERR_INVALID);
+ test("−−1", "1", 0);
+ test("255", "255", 0);
+ test("256", "256", 0);
+ test("½", "0.5", 0);
+ test("1½", "1.5", 0);
+ test("0°", "0", 0);
+ test("1°", "1", 0);
+ test("0°30'", "0.5", 0);
+ //test("0°0.1'", "1", 0); // FIXME: Not yet supported
+ test("0°0'1\"", "0.000277778", 0);
+ test("0°0'0.1\"", "0.000027778", 0);
+ test("1.00", "1", 0);
+ test("1.01", "1.01", 0);
+
+ test("١٢٣٤٥٦٧٨٩٠", "1234567890", 0);
+ test("۱۲۳۴۵۶۷۸۹۰", "1234567890", 0);
+
+/*
+ //test("2A", "2000000000000000", 0);
+ test("2T", "2000000000000", 0);
+ test("2G", "2000000000", 0);
+ test("2M", "2000000", 0);
+ test("2k", "2000", 0);
+ test("2c", "0.02", 0);
+ test("2d", "0.2", 0);
+ test("2c", "0.02", 0);
+ test("2m", "0.002", 0);
+ test("2u", "0.000002", 0);
+ test("2µ", "0.000002", 0);
+ test("2n", "0.000000002", 0);
+ //test("2p", "0.000000000002", 0); // FIXME: Need to print out significant figures, not decimal places
+ //test("2f", "0.000000000000002", 0); // FIXME: Need to print out significant figures, not decimal places
+ //test("2A3", "2300000000000000", 0);
+ test("2T3", "2300000000000", 0);
+ test("2G3", "2300000000", 0);
+ test("2M3", "2300000", 0);
+ test("2k3", "2300", 0);
+ test("2c3", "0.023", 0);
+ test("2d3", "0.23", 0);
+ test("2c3", "0.023", 0);
+ test("2m3", "0.0023", 0);
+ test("2u3", "0.0000023", 0);
+ test("2µ3", "0.0000023", 0);
+ //test("2n3", "0.0000000023", 0); // FIXME: Need to print out significant figures, not decimal places
+ //test("2p3", "0.0000000000023", 0); // FIXME: Need to print out significant figures, not decimal places
+ //test("2f3", "0.0000000000000023", 0); // FIXME: Need to print out significant figures, not decimal places
+*/
+
+ test("2×10^3", "2000", 0);
+ test("2×10^−3", "0.002", 0);
+
+ test("x", "2", 0);
+ test("y", "3", 0);
+ test("z", "", PARSER_ERR_UNKNOWN_VARIABLE);
+ test("2y", "6", 0);
+ test("y2", "", PARSER_ERR_INVALID);
+ test("y 2", "", PARSER_ERR_INVALID);
+ test("2z", "", PARSER_ERR_UNKNOWN_VARIABLE);
+ test("z2", "", PARSER_ERR_UNKNOWN_VARIABLE);
+ test("z 2", "", PARSER_ERR_UNKNOWN_VARIABLE);
+ test("z(2)", "", PARSER_ERR_UNKNOWN_VARIABLE);
+ test("y²", "9", 0);
+ test("2y²", "18", 0);
+ test("x×y", "6", 0);
+ test("xy", "6", 0);
+ test("yx", "6", 0);
+ test("2xy", "12", 0);
+ test("x²y", "12", 0);
+ test("xy²", "18", 0);
+ test("(xy)²", "36", 0);
+ test("2x²y", "24", 0);
+ test("2xy²", "36", 0);
+ test("2x²y²", "72", 0);
+ test("x²yx²y", "144", 0);
+ test("x³+2x²−5", "11", 0);
+ test("2(x+3y)", "22", 0);
+ test("x(x+3y)", "22", 0);
+ test("(x+3y)(2x-4y)", "−88", 0);
+ test("2x²+2xy−12y²", "−88", 0);
+
+ test("π", "3.141592654", 0);
+ test("e", "2.718281828", 0);
+
+ test("z=99", "99", 0);
+ test("longname=99", "99", 0);
+ //test("e=99", "", PARSER_ERR_BUILTIN_VARIABLE);
+
+ test("0+0", "0", 0);
+ test("1+1", "2", 0);
+ test("1+4", "5", 0);
+ test("4+1", "5", 0);
+ test("40000+0.001", "40000.001", 0);
+ test("0.001+40000", "40000.001", 0);
+ test("2-3", "−1", 0);
+ test("2−3", "−1", 0);
+ test("3−2", "1", 0);
+ test("40000−0.001", "39999.999", 0);
+ test("0.001−40000", "−39999.999", 0);
+ test("2*3", "6", 0);
+ test("2×3", "6", 0);
+ test("−2×3", "−6", 0);
+ test("2×−3", "−6", 0);
+ test("−2×−3", "6", 0);
+ test("6/3", "2", 0);
+ test("6÷3", "2", 0);
+ test("1÷2", "0.5", 0);
+ test("−6÷3", "−2", 0);
+ test("6÷−3", "−2", 0);
+ test("−6÷−3", "2", 0);
+ test("(−3)÷(−6)", "0.5", 0);
+ test("2÷2", "1", 0);
+ test("1203÷1", "1203", 0);
+ test("−0÷32352.689", "0", 0);
+ test("1÷4", "0.25", 0);
+ test("1÷3", "0.333333333", 0);
+ test("2÷3", "0.666666667", 0);
+ test("1÷0", "", PARSER_ERR_MP);
+ test("0÷0", "", PARSER_ERR_MP);
+
+ /* Precision */
+ test("1000000000000000−1000000000000000", "0", 0);
+ test("1000000000000000÷1000000000000000", "1", 0);
+ test("1000000000000000×0.000000000000001", "1", 0);
+
+ /* Order of operations */
+ test("1−0.9−0.1", "0", 0);
+ test("1+2×3", "7", 0);
+ test("1+(2×3)", "7", 0);
+ test("(1+2)×3", "9", 0);
+ test("(1+2×3)", "7", 0);
+ test("2(1+1)", "4", 0);
+ test("4÷2(1+1)", "4", 0);
+
+ /* Percentage */
+ test("100%", "1", 0);
+ test("1%", "0.01", 0);
+ test("100+1%", "101", 0);
+ test("100−1%", "99", 0);
+ test("100×1%", "1", 0);
+ test("100÷1%", "10000", 0);
+
+ /* Factorial */
+ test("0!", "1", 0);
+ test("1!", "1", 0);
+ test("5!", "120", 0);
+ test("69!", "171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000", 0);
+ test("0.1!", "", PARSER_ERR_MP);
+ test("−1!", "−1", 0);
+ test("(−1)!", "", PARSER_ERR_MP);
+ test("−(1!)", "−1", 0);
+
+ /* Powers */
+ test("2²", "4", 0);
+ test("2³", "8", 0);
+ test("2¹⁰", "1024", 0);
+ test("(1+2)²", "9", 0);
+ test("(x)²", "4", 0);
+ test("|1−3|²", "4", 0);
+ test("|x|²", "4", 0);
+ test("0^0", "1", 0);
+ test("0^0.5", "0", 0);
+ test("2^0", "1", 0);
+ test("2^1", "2", 0);
+ test("2^2", "4", 0);
+ test("2⁻¹", "0.5", 0);
+ test("2⁻", "", PARSER_ERR_MP);
+ test("2^−1", "0.5", 0);
+ test("2^(−1)", "0.5", 0);
+ test("x⁻¹", "0.5", 0);
+ test("−10^2", "−100", 0);
+ test("(−10)^2", "100", 0);
+ test("−(10^2)", "−100", 0);
+ test("2^100", "1267650600228229401496703205376", 0);
+ test("4^3^2", "262144", 0);
+ test("4^(3^2)", "262144", 0);
+ test("(4^3)^2", "4096", 0);
+ test("√4", "2", 0);
+ test("√4−2", "0", 0);
+ test("∛8", "2", 0);
+ test("∜16", "2", 0);
+ test("₃√8", "2", 0);
+ test("₁₀√1024", "2", 0);
+ test("√(2+2)", "2", 0);
+ test("2√4", "4", 0);
+ test("2×√4", "4", 0);
+ test("Sqrt(4)", "2", 0);
+ test("Sqrt(2)", "1.414213562", 0);
+ test("4^0.5", "2", 0);
+ test("2^0.5", "1.414213562", 0);
+ test("₃√−8", "−2", 0);
+ test("(−8)^(1÷3)", "−2", 0);
+
+ test("0 mod 7", "0", 0);
+ test("6 mod 7", "6", 0);
+ test("7 mod 7", "0", 0);
+ test("8 mod 7", "1", 0);
+ test("−1 mod 7", "6", 0);
+
+ test("sgn 0", "0", 0);
+ test("sgn 3", "1", 0);
+ test("sgn −3", "−1", 0);
+ test("⌊3⌋", "3", 0);
+ test("⌈3⌉", "3", 0);
+ test("[3]", "3", 0);
+ test("⌊−3⌋", "−3", 0);
+ test("⌈−3⌉", "−3", 0);
+ test("[−3]", "−3", 0);
+ test("⌊3.2⌋", "3", 0);
+ test("⌈3.2⌉", "4", 0);
+ test("[3.2]", "3", 0);
+ test("⌊−3.2⌋", "−4", 0);
+ test("⌈−3.2⌉", "−3", 0);
+ test("[−3.2]", "−3", 0);
+ test("⌊3.5⌋", "3", 0);
+ test("⌈3.5⌉", "4", 0);
+ test("[3.5]", "4", 0);
+ test("⌊−3.5⌋", "−4", 0);
+ test("⌈−3.5⌉", "−3", 0);
+ test("[−3.5]", "−4", 0);
+ test("⌊3.7⌋", "3", 0);
+ test("⌈3.7⌉", "4", 0);
+ test("[3.7]", "4", 0);
+ test("⌊−3.7⌋", "−4", 0);
+ test("⌈−3.7⌉", "−3", 0);
+ test("[−3.7]", "−4", 0);
+ test("{3.2}", "0.2", 0);
+ test("{−3.2}", "0.8", 0);
+
+ test("|1|", "1", 0);
+ test("|−1|", "1", 0);
+ test("|3−5|", "2", 0);
+ test("|x|", "2", 0);
+ test("abs 1", "1", 0);
+ test("abs (−1)", "1", 0);
+
+ test("log 0", "", PARSER_ERR_MP);
+ test("log 1", "0", 0);
+ test("log 2", "0.301029996", 0);
+ test("log 10", "1", 0);
+ test("log₁₀ 10", "1", 0);
+ test("log₂ 2", "1", 0);
+ test("2 log 2", "0.602059991", 0);
+
+ test("ln 0", "", PARSER_ERR_MP);
+ test("ln 1", "0", 0);
+ test("ln 2", "0.693147181", 0);
+ test("ln e", "1", 0);
+ test("2 ln 2", "1.386294361", 0);
+
+ options.angle_units = MP_DEGREES;
+ test("sin 0", "0", 0);
+ test("sin 45 − 1÷√2", "0", 0);
+ test("sin 20 + sin(−20)", "0", 0);
+ test("sin 90", "1", 0);
+ test("sin 180", "0", 0);
+ test("2 sin 90", "2", 0);
+ test("sin²45", "0.5", 0);
+
+ test("cos 0", "1", 0);
+ test("cos 45 − 1÷√2", "0", 0);
+ test("cos 20 − cos (−20)", "0", 0);
+ test("cos 90", "0", 0);
+ test("cos 180", "−1", 0);
+ test("2 cos 0", "2", 0);
+ test("cos²45", "0.5", 0);
+
+ test("tan 0", "0", 0);
+ test("tan 10 − sin 10÷cos 10", "0", 0);
+ test("tan 90", "", PARSER_ERR_MP);
+ test("tan 10", "0.176326981", 0);
+ test("tan²10", "0.031091204", 0);
+
+ test("cos⁻¹ 0", "90", 0);
+ test("cos⁻¹ 1", "0", 0);
+ test("cos⁻¹ (−1)", "180", 0);
+ test("cos⁻¹ (1÷√2)", "45", 0);
+ test("acos 0", "90", 0);
+ test("acos 1", "0", 0);
+
+ test("sin⁻¹ 0", "0", 0);
+ test("sin⁻¹ 1", "90", 0);
+ test("sin⁻¹ (−1)", "−90", 0);
+ test("sin⁻¹ (1÷√2)", "45", 0);
+ test("asin 0", "0", 0);
+ test("asin 1", "90", 0);
+
+ test("cosh 0", "1", 0);
+ test("cosh 10 − (e^10 + e^−10)÷2", "0", 0);
+
+ test("sinh 0", "0", 0);
+ test("sinh 10 − (e^10 − e^−10)÷2", "0", 0);
+ test("sinh (−10) + sinh 10", "0", 0);
+
+ test("cosh² (−5) − sinh² (−5)", "1", 0);
+ test("tanh 0", "0", 0);
+ test("tanh 10 − sinh 10 ÷ cosh 10", "0", 0);
+
+ test("atanh 0", "0", 0);
+ test("atanh (1÷10) − 0.5 ln(11÷9)", "0", 0);
+
+ options.angle_units = MP_DEGREES;
+ test("sin 90", "1", 0);
+
+ options.angle_units = MP_RADIANS;
+ test("sin (π÷2)", "1", 0); // FIXME: Shouldn't need brackets
+
+ options.angle_units = MP_GRADIANS;
+ test("sin 100", "1", 0);
+
+ /* Complex numbers */
+ options.angle_units = MP_DEGREES;
+ test("i", "i", 0);
+ test("−i", "−i", 0);
+ test("2i", "2i", 0);
+ test("1+i", "1+i", 0);
+ test("i+1", "1+i", 0);
+ test("1−i", "1−i", 0);
+ test("i−1", "−1+i", 0);
+ test("i×i", "−1", 0);
+ test("i÷i", "1", 0);
+ test("1÷i", "−i", 0);
+ test("|i|", "1", 0);
+ test("|3+4i|", "5", 0);
+ test("arg 0", "", PARSER_ERR_MP);
+ test("arg 1", "0", 0);
+ test("arg (1+i)", "45", 0);
+ test("arg i", "90", 0);
+ test("arg (−1+i)", "135", 0);
+ test("arg −1", "180", 0);
+ test("arg (1+−i)", "−45", 0);
+ test("arg −i", "−90", 0);
+ test("arg (−1−i)", "−135", 0);
+ test("i⁻¹", "−i", 0);
+ test("√−1", "i", 0);
+ test("(−1)^0.5", "i", 0);
+ test("√−4", "2i", 0);
+ test("e^iπ", "−1", 0);
+ test("log (−10) − (1 + πi÷ln(10))", "0", 0);
+ test("ln (−e) − (1 + πi)", "0", 0);
+ test("sin(iπ÷4) − i×sinh(π÷4)", "0", 0);
+ test("cos(iπ÷4) − cosh(π÷4)", "0", 0);
+
+ /* Boolean */
+ test("0 and 0", "0", 0);
+ test("1 and 0", "0", 0);
+ test("0 and 1", "0", 0);
+ test("1 and 1", "1", 0);
+ test("3 and 5", "1", 0);
+
+ test("0 or 0", "0", 0);
+ test("1 or 0", "1", 0);
+ test("0 or 1", "1", 0);
+ test("1 or 1", "1", 0);
+ test("3 or 5", "7", 0);
+
+ test("0 xor 0", "0", 0);
+ test("1 xor 0", "1", 0);
+ test("0 xor 1", "1", 0);
+ test("1 xor 1", "0", 0);
+ test("3 xor 5", "6", 0);
+
+ options.base = 16;
+ test("ones 1", "FFFFFFFE", 0);
+ test("ones 7FFFFFFF", "80000000", 0);
+ test("twos 1", "FFFFFFFF", 0);
+ test("twos 7FFFFFFF", "80000001", 0);
+ test("~7A₁₆", "FFFFFF85", 0);
+
+ options.base = 2;
+ options.wordlen = 4;
+ test("1100∧1010", "1000", 0);
+ test("1100∨1010", "1110", 0);
+ test("1100⊻1010", "110", 0);
+ test("1100⊕1010", "110", 0);
+ //test("1100⊼1010", "0111", 0);
+ //test("1100⊽1010", "0001", 0);
+ //options.wordlen = 2;
+ //test("¬01₂", "10₂", 0);
+ //test("¬¬10₂", "10₂", 0);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ setlocale(LC_ALL, "C");
+ g_type_init ();
+
+ test_conversions();
+ test_equations();
+ if (fails == 0)
+ printf("Passed all %i tests\n", passes);
+
+ return fails;
+}
diff --git a/src/test-mp.c b/src/test-mp.c
new file mode 100644
index 0000000..a750f2a
--- /dev/null
+++ b/src/test-mp.c
@@ -0,0 +1,240 @@
+/*
+ * 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 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 <glib-object.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <locale.h>
+
+#include "mp.h"
+#include "mp-private.h"
+
+static int fails = 0;
+static int passes = 0;
+
+/* If we're not using GNU C, elide __attribute__ */
+#ifndef __GNUC__
+# define __attribute__(x) /*NOTHING*/
+#endif
+
+static void pass(const char *format, ...) __attribute__((format(printf, 1, 2)));
+static void fail(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+
+static void pass(const char *format, ...)
+{
+/* va_list args;
+
+ printf(" PASS: ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+*/
+ passes += 1;
+}
+
+static void fail(const char *format, ...)
+{
+ va_list args;
+
+ printf("*FAIL: ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+ fails++;
+}
+
+
+static void
+print_number(MPNumber *x)
+{
+ int i, j;
+
+ printf("sign=%d exponent=%d fraction=%d", x->sign, x->exponent, x->fraction[0]);
+ for (i = 1; i < MP_SIZE; i++) {
+ for (j = i; j < MP_SIZE && x->fraction[j] == 0; j++);
+ if (j == MP_SIZE) {
+ printf(",...");
+ break;
+ }
+ printf(",%d", x->fraction[i]);
+ }
+}
+
+static void
+test_string(const char *number)
+{
+ MPNumber t;
+
+ mp_set_from_string(number, 10, &t);
+
+ printf("MPNumber(%s) -> {", number);
+ print_number(&t);
+ printf("}\n");
+}
+
+static void
+test_integer(int number)
+{
+ MPNumber t;
+
+ mp_set_from_integer(number, &t);
+
+ printf("MPNumber(%d) -> {", number);
+ print_number(&t);
+ printf("}\n");
+}
+
+
+static void
+test_numbers()
+{
+ printf("base=%d\n", MP_BASE);
+ test_integer(0);
+ test_integer(1);
+ test_integer(-1);
+ test_integer(2);
+ test_integer(9999);
+ test_integer(10000);
+ test_integer(10001);
+ test_integer(2147483647);
+
+ test_string("0");
+ test_string("1");
+ test_string("-1");
+ test_string("16383");
+ test_string("16384");
+ test_string("16385");
+ test_string("268435456");
+
+ test_string("0.1");
+ test_string("0.5");
+ test_string("0.25");
+ test_string("0.125");
+ test_string("0.0625");
+ test_string("0.00006103515625");
+ test_string("0.000030517578125");
+
+ test_string("1.00006103515625");
+ test_string("16384.00006103515625");
+}
+
+
+static void
+try(const char *string, bool result, bool expected)
+{
+ if ((result && !expected) || (!result && expected))
+ fail("%s -> %s, expected %s", string, expected ? "true" : "false", result ? "true" : "false");
+ else
+ pass("%s -> %s", string, result ? "true" : "false");
+}
+
+
+static void
+test_mp()
+{
+ MPNumber zero, one, minus_one;
+
+ mp_set_from_integer(0, &zero);
+ mp_set_from_integer(1, &one);
+ mp_set_from_integer(-1, &minus_one);
+
+ try("mp_is_zero(-1)", mp_is_zero(&minus_one), false);
+ try("mp_is_zero(0)", mp_is_zero(&zero), true);
+ try("mp_is_zero(1)", mp_is_zero(&one), false);
+
+ try("mp_is_negative(-1)", mp_is_negative(&minus_one), true);
+ try("mp_is_negative(0)", mp_is_negative(&zero), false);
+ try("mp_is_negative(1)", mp_is_negative(&one), false);
+
+ try("mp_is_integer(-1)", mp_is_integer(&minus_one), true);
+ try("mp_is_integer(0)", mp_is_integer(&zero), true);
+ try("mp_is_integer(1)", mp_is_integer(&one), true);
+
+ try("mp_is_positive_integer(-1)", mp_is_positive_integer(&minus_one), false);
+ try("mp_is_positive_integer(0)", mp_is_positive_integer(&zero), true);
+ try("mp_is_positive_integer(1)", mp_is_positive_integer(&one), true);
+
+ try("mp_is_natural(-1)", mp_is_natural(&minus_one), false);
+ try("mp_is_natural(0)", mp_is_natural(&zero), false);
+ try("mp_is_natural(1)", mp_is_natural(&one), true);
+
+ try("mp_is_complex(-1)", mp_is_complex(&minus_one), false);
+ try("mp_is_complex(0)", mp_is_complex(&zero), false);
+ try("mp_is_complex(1)", mp_is_complex(&one), false);
+
+ try("mp_is_equal(-1, -1)", mp_is_equal(&minus_one, &minus_one), true);
+ try("mp_is_equal(-1, 0)", mp_is_equal(&minus_one, &zero), false);
+ try("mp_is_equal(-1, 1)", mp_is_equal(&minus_one, &one), false);
+ try("mp_is_equal(0, -1)", mp_is_equal(&zero, &minus_one), false);
+ try("mp_is_equal(0, 0)", mp_is_equal(&zero, &zero), true);
+ try("mp_is_equal(0, 1)", mp_is_equal(&zero, &one), false);
+ try("mp_is_equal(1, -1)", mp_is_equal(&one, &minus_one), false);
+ try("mp_is_equal(1, 0)", mp_is_equal(&one, &zero), false);
+ try("mp_is_equal(1, 1)", mp_is_equal(&one, &one), true);
+
+ try("mp_is_greater_than(0, -1)", mp_is_greater_than (&zero, &minus_one), true);
+ try("mp_is_greater_than(0, 0)", mp_is_greater_than (&zero, &zero), false);
+ try("mp_is_greater_than(0, 1)", mp_is_greater_than (&zero, &one), false);
+ try("mp_is_greater_than(1, -1)", mp_is_greater_than (&one, &minus_one), true);
+ try("mp_is_greater_than(1, 0)", mp_is_greater_than (&one, &zero), true);
+ try("mp_is_greater_than(1, 1)", mp_is_greater_than (&one, &one), false);
+ try("mp_is_greater_than(-1, -1)", mp_is_greater_than (&minus_one, &minus_one), false);
+ try("mp_is_greater_than(-1, 0)", mp_is_greater_than (&minus_one, &zero), false);
+ try("mp_is_greater_than(-1, 1)", mp_is_greater_than (&minus_one, &one), false);
+
+ try("mp_is_greater_equal(0, -1)", mp_is_greater_equal (&zero, &minus_one), true);
+ try("mp_is_greater_equal(0, 0)", mp_is_greater_equal (&zero, &zero), true);
+ try("mp_is_greater_equal(0, 1)", mp_is_greater_equal (&zero, &one), false);
+ try("mp_is_greater_equal(1, -1)", mp_is_greater_equal (&one, &minus_one), true);
+ try("mp_is_greater_equal(1, 0)", mp_is_greater_equal (&one, &zero), true);
+ try("mp_is_greater_equal(1, 1)", mp_is_greater_equal (&one, &one), true);
+ try("mp_is_greater_equal(-1, -1)", mp_is_greater_equal (&minus_one, &minus_one), true);
+ try("mp_is_greater_equal(-1, 0)", mp_is_greater_equal (&minus_one, &zero), false);
+ try("mp_is_greater_equal(-1, 1)", mp_is_greater_equal (&minus_one, &one), false);
+
+ try("mp_is_less_than(0, -1)", mp_is_less_than (&zero, &minus_one), false);
+ try("mp_is_less_than(0, 0)", mp_is_less_than (&zero, &zero), false);
+ try("mp_is_less_than(0, 1)", mp_is_less_than (&zero, &one), true);
+ try("mp_is_less_than(1, -1)", mp_is_less_than (&one, &minus_one), false);
+ try("mp_is_less_than(1, 0)", mp_is_less_than (&one, &zero), false);
+ try("mp_is_less_than(1, 1)", mp_is_less_than (&one, &one), false);
+ try("mp_is_less_than(-1, -1)", mp_is_less_than (&minus_one, &minus_one), false);
+ try("mp_is_less_than(-1, 0)", mp_is_less_than (&minus_one, &zero), true);
+ try("mp_is_less_than(-1, 1)", mp_is_less_than (&minus_one, &one), true);
+
+ try("mp_is_less_equal(0, -1)", mp_is_less_equal (&zero, &minus_one), false);
+ try("mp_is_less_equal(0, 0)", mp_is_less_equal (&zero, &zero), true);
+ try("mp_is_less_equal(0, 1)", mp_is_less_equal (&zero, &one), true);
+ try("mp_is_less_equal(1, -1)", mp_is_less_equal (&one, &minus_one), false);
+ try("mp_is_less_equal(1, 0)", mp_is_less_equal (&one, &zero), false);
+ try("mp_is_less_equal(1, 1)", mp_is_less_equal (&one, &one), true);
+ try("mp_is_less_equal(-1, -1)", mp_is_less_equal (&minus_one, &minus_one), true);
+ try("mp_is_less_equal(-1, 0)", mp_is_less_equal (&minus_one, &zero), true);
+ try("mp_is_less_equal(-1, 1)", mp_is_less_equal (&minus_one, &one), true);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ setlocale(LC_ALL, "C");
+ g_type_init ();
+
+ test_mp();
+ test_numbers();
+ if (fails == 0)
+ printf("Passed all %i tests\n", passes);
+
+ return fails;
+}
diff --git a/src/unit-category.c b/src/unit-category.c
new file mode 100644
index 0000000..56a90e2
--- /dev/null
+++ b/src/unit-category.c
@@ -0,0 +1,136 @@
+/*
+ * 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 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 <string.h>
+
+#include "unit-category.h"
+
+struct UnitCategoryPrivate
+{
+ gchar *name;
+ gchar *display_name;
+ GList *units;
+};
+
+G_DEFINE_TYPE (UnitCategory, unit_category, G_TYPE_OBJECT);
+
+
+UnitCategory *
+unit_category_new(const gchar *name, const gchar *display_name)
+{
+ UnitCategory *category = g_object_new(unit_category_get_type(), NULL);
+ category->priv->name = g_strdup(name);
+ category->priv->display_name = g_strdup(display_name);
+ return category;
+}
+
+
+const gchar *
+unit_category_get_name(UnitCategory *category)
+{
+ g_return_val_if_fail (category != NULL, NULL);
+ return category->priv->name;
+}
+
+
+const gchar *
+unit_category_get_display_name(UnitCategory *category)
+{
+ g_return_val_if_fail (category != NULL, NULL);
+ return category->priv->display_name;
+}
+
+
+void
+unit_category_add_unit(UnitCategory *category, Unit *unit)
+{
+ g_return_if_fail (category != NULL);
+ g_return_if_fail (unit != NULL);
+ category->priv->units = g_list_append(category->priv->units, g_object_ref(unit));
+}
+
+
+Unit *
+unit_category_get_unit_by_name(UnitCategory *category, const gchar *name)
+{
+ GList *iter;
+
+ g_return_val_if_fail (category != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ for (iter = category->priv->units; iter; iter = iter->next)
+ {
+ Unit *unit = iter->data;
+ if (strcmp(unit_get_name(unit), name) == 0)
+ return unit;
+ }
+
+ return NULL;
+}
+
+
+Unit *
+unit_category_get_unit_by_symbol(UnitCategory *category, const gchar *symbol)
+{
+ GList *iter;
+
+ g_return_val_if_fail (category != NULL, NULL);
+ g_return_val_if_fail (symbol != NULL, NULL);
+
+ for (iter = category->priv->units; iter; iter = iter->next) {
+ Unit *unit = iter->data;
+ if (unit_matches_symbol(unit, symbol))
+ return unit;
+ }
+
+ return NULL;
+}
+
+
+const GList *
+unit_category_get_units(UnitCategory *category)
+{
+ g_return_val_if_fail (category != NULL, NULL);
+ return category->priv->units;
+}
+
+
+gboolean
+unit_category_convert(UnitCategory *category, const MPNumber *x, Unit *x_units, Unit *z_units, MPNumber *z)
+{
+ MPNumber t;
+
+ g_return_val_if_fail (category != NULL, FALSE);
+ g_return_val_if_fail (x_units != NULL, FALSE);
+ g_return_val_if_fail (z_units != NULL, FALSE);
+ g_return_val_if_fail (z != NULL, FALSE);
+
+ if (!unit_convert_from(x_units, x, &t))
+ return FALSE;
+ if (!unit_convert_to(z_units, &t, z))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+unit_category_class_init(UnitCategoryClass *klass)
+{
+ g_type_class_add_private(klass, sizeof(UnitCategoryPrivate));
+}
+
+
+static void
+unit_category_init(UnitCategory *category)
+{
+ category->priv = G_TYPE_INSTANCE_GET_PRIVATE(category, unit_category_get_type(), UnitCategoryPrivate);
+}
diff --git a/src/unit-category.h b/src/unit-category.h
new file mode 100644
index 0000000..fc2c739
--- /dev/null
+++ b/src/unit-category.h
@@ -0,0 +1,56 @@
+/*
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef UNIT_CATEGORY_H
+#define UNIT_CATEGORY_H
+
+#include <glib-object.h>
+#include "unit.h"
+#include "mp.h"
+
+G_BEGIN_DECLS
+
+#define UNIT_CATEGORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), unit_category_get_type(), UnitCategory))
+
+typedef struct UnitCategoryPrivate UnitCategoryPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ UnitCategoryPrivate *priv;
+} UnitCategory;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} UnitCategoryClass;
+
+GType unit_category_get_type(void);
+
+UnitCategory *unit_category_new(const gchar *name, const gchar *display_name);
+
+const gchar *unit_category_get_name(UnitCategory *category);
+
+const gchar *unit_category_get_display_name(UnitCategory *category);
+
+Unit *unit_category_get_unit_by_name(UnitCategory *category, const gchar *name);
+
+Unit *unit_category_get_unit_by_symbol(UnitCategory *category, const gchar *symbol);
+
+void unit_category_add_unit(UnitCategory *category, Unit *unit);
+
+const GList *unit_category_get_units(UnitCategory *category);
+
+gboolean unit_category_convert(UnitCategory *category, const MPNumber *x, Unit *x_units, Unit *z_units, MPNumber *z);
+
+G_END_DECLS
+
+#endif /* UNIT_CATEGORY_H */
diff --git a/src/unit-manager.c b/src/unit-manager.c
new file mode 100644
index 0000000..9943a31
--- /dev/null
+++ b/src/unit-manager.c
@@ -0,0 +1,268 @@
+/*
+ * 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 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 <string.h>
+#include <glib/gi18n.h> // FIXME: Move out of here
+
+#include "unit-manager.h"
+#include "currency-manager.h" // FIXME: Move out of here
+
+struct UnitManagerPrivate
+{
+ GList *categories;
+};
+
+G_DEFINE_TYPE (UnitManager, unit_manager, G_TYPE_OBJECT);
+
+
+static UnitManager *default_unit_manager = NULL;
+
+
+static gint
+compare_currencies(gconstpointer a, gconstpointer b)
+{
+ return strcmp(currency_get_display_name((Currency *)a), currency_get_display_name((Currency *)b));
+}
+
+
+UnitManager *
+unit_manager_get_default(void)
+{
+ UnitCategory *category = NULL;
+ GList *currencies, *iter;
+ int i;
+ const struct
+ {
+ gchar *category;
+ gchar *name;
+ gchar *display_name;
+ gchar *format;
+ gchar *from_function;
+ gchar *to_function;
+ gchar *symbols;
+ } units[] =
+ {
+ /* FIXME: Approximations of 1/(units in a circle), therefore, 360 deg != 400 grads */
+ {"angle", "degree", N_("Degrees"), NC_("unit-format", "%s degrees"), "π*x/180", "180x/π", NC_("unit-symbols", "degree,degrees,deg")},
+ {NULL, "radian", N_("Radians"), NC_("unit-format", "%s radians"), "x", "x", NC_("unit-symbols", "radian,radians,rad")},
+ {NULL, "gradian", N_("Gradians"), NC_("unit-format", "%s gradians"), "π*x/200", "200x/π", NC_("unit-symbols", "gradian,gradians,grad")},
+ {"length", "parsec", N_("Parsecs"), NC_("unit-format", "%s pc"), "30857000000000000x", "x/30857000000000000", NC_("unit-symbols", "parsec,parsecs,pc")},
+ {NULL, "lightyear", N_("Light Years"), NC_("unit-format", "%s ly"), "9460730472580800x", "x/9460730472580800", NC_("unit-symbols", "lightyear,lightyears,ly")},
+ {NULL, "astronomical-unit", N_("Astronomical Units"), NC_("unit-format", "%s au"), "149597870691x", "x/149597870691", NC_("unit-symbols", "au")},
+ {NULL, "nautical-mile", N_("Nautical Miles"), NC_("unit-format", "%s nmi"), "1852x", "x/1852", NC_("unit-symbols", "nmi")},
+ {NULL, "mile", N_("Miles"), NC_("unit-format", "%s mi"), "1609.344x", "x/1609.344", NC_("unit-symbols", "mile,miles,mi")},
+ {NULL, "kilometer", N_("Kilometers"), NC_("unit-format", "%s km"), "1000x", "x/1000", NC_("unit-symbols", "kilometer,kilometers,km,kms")},
+ {NULL, "cable", N_("Cables"), NC_("unit-format", "%s cb"), "219.456x", "x/219.456", NC_("unit-symbols", "cable,cables,cb")},
+ {NULL, "fathom", N_("Fathoms"), NC_("unit-format", "%s ftm"), "1.8288x", "x/1.8288", NC_("unit-symbols", "fathom,fathoms,ftm")},
+ {NULL, "meter", N_("Meters"), NC_("unit-format", "%s m"), "x", "x", NC_("unit-symbols", "meter,meters,m")},
+ {NULL, "yard", N_("Yards"), NC_("unit-format", "%s yd"), "0.9144x", "x/0.9144", NC_("unit-symbols", "yard,yards,yd")},
+ {NULL, "foot", N_("Feet"), NC_("unit-format", "%s ft"), "0.3048x", "x/0.3048", NC_("unit-symbols", "foot,feet,ft")},
+ {NULL, "inch", N_("Inches"), NC_("unit-format", "%s in"), "0.0254x", "x/0.0254", NC_("unit-symbols", "inch,inches,in")},
+ {NULL, "centimeter", N_("Centimeters"), NC_("unit-format", "%s cm"), "x/100", "100x", NC_("unit-symbols", "centimeter,centimeters,cm,cms")},
+ {NULL, "millimeter", N_("Millimeters"), NC_("unit-format", "%s mm"), "x/1000", "1000x", NC_("unit-symbols", "millimeter,millimeters,mm")},
+ {NULL, "micrometer", N_("Micrometers"), NC_("unit-format", "%s μm"), "x/1000000", "1000000x", NC_("unit-symbols", "micrometer,micrometers,um")},
+ {NULL, "nanometer", N_("Nanometers"), NC_("unit-format", "%s nm"), "x/1000000000", "1000000000x", NC_("unit-symbols", "nanometer,nanometers,nm")},
+ {"area", "hectare", N_("Hectares"), NC_("unit-format", "%s ha"), "10000x", "x/10000", NC_("unit-symbols", "hectare,hectares,ha")},
+ {NULL, "acre", N_("Acres"), NC_("unit-format", "%s acres"), "4046.8564224x", "x/4046.8564224", NC_("unit-symbols", "acre,acres")},
+ {NULL, "square-meter", N_("Square Meters"), NC_("unit-format", "%s m²"), "x", "x", NC_("unit-symbols", "m²")},
+ {NULL, "square-centimeter", N_("Square Centimeters"), NC_("unit-format", "%s cm²"), "0.0001x", "10000x", NC_("unit-symbols", "cm²")},
+ {NULL, "square-millimeter", N_("Square Millimeters"), NC_("unit-format", "%s mm²"), "0.000001x", "1000000x", NC_("unit-symbols", "mm²")},
+ {"volume", "cubic-meter", N_("Cubic Meters"), NC_("unit-format", "%s m³"), "1000x", "x/1000", NC_("unit-symbols", "m³")},
+ {NULL, "gallon", N_("Gallons"), NC_("unit-format", "%s gal"), "3.785412x", "x/3.785412", NC_("unit-symbols", "gallon,gallons,gal")},
+ {NULL, "litre", N_("Litres"), NC_("unit-format", "%s L"), "x", "x", NC_("unit-symbols", "litre,litres,liter,liters,L")},
+ {NULL, "quart", N_("Quarts"), NC_("unit-format", "%s qt"), "0.9463529x", "x/0.9463529", NC_("unit-symbols", "quart,quarts,qt")},
+ {NULL, "pint", N_("Pints"), NC_("unit-format", "%s pt"), "0.4731765x", "x/0.4731765", NC_("unit-symbols", "pint,pints,pt")},
+ {NULL, "millilitre", N_("Millilitres"), NC_("unit-format", "%s mL"), "0.001x", "1000x", NC_("unit-symbols", "millilitre,millilitres,milliliter,milliliters,mL,cm³")},
+ {NULL, "microlitre", N_("Microlitres"), NC_("unit-format", "%s μL"), "0.000001x", "1000000x", NC_("unit-symbols", "mm³,μL,uL")},
+ {"weight", "tonne", N_("Tonnes"), NC_("unit-format", "%s T"), "1000x", "x/1000", NC_("unit-symbols", "tonne,tonnes")},
+ {NULL, "kilograms", N_("Kilograms"), NC_("unit-format", "%s kg"), "x", "x", NC_("unit-symbols", "kilogram,kilograms,kilogramme,kilogrammes,kg,kgs")},
+ {NULL, "pound", N_("Pounds"), NC_("unit-format", "%s lb"), "0.45359237x", "x/0.45359237", NC_("unit-symbols", "pound,pounds,lb")},
+ {NULL, "ounce", N_("Ounces"), NC_("unit-format", "%s oz"), "0.02834952x", "x/0.02834952", NC_("unit-symbols", "ounce,ounces,oz")},
+ {NULL, "gram", N_("Grams"), NC_("unit-format", "%s g"), "0.001x", "1000x", NC_("unit-symbols", "gram,grams,gramme,grammes,g")},
+ {"duration", "year", N_("Years"), NC_("unit-format", "%s years"), "31557600x", "x/31557600", NC_("unit-symbols", "year,years")},
+ {NULL, "day", N_("Days"), NC_("unit-format", "%s days"), "86400x", "x/86400", NC_("unit-symbols", "day,days")},
+ {NULL, "hour", N_("Hours"), NC_("unit-format", "%s hours"), "3600x", "x/3600", NC_("unit-symbols", "hour,hours")},
+ {NULL, "minute", N_("Minutes"), NC_("unit-format", "%s minutes"), "60x", "x/60", NC_("unit-symbols", "minute,minutes")},
+ {NULL, "second", N_("Seconds"), NC_("unit-format", "%s s"), "x", "x", NC_("unit-symbols", "second,seconds,s")},
+ {NULL, "millisecond", N_("Milliseconds"), NC_("unit-format", "%s ms"), "0.001x", "1000x", NC_("unit-symbols", "millisecond,milliseconds,ms")},
+ {NULL, "microsecond", N_("Microseconds"), NC_("unit-format", "%s μs"), "0.000001x", "1000000x", NC_("unit-symbols", "microsecond,microseconds,us,μs")},
+ {"temperature", "degree-celcius", N_("Celsius"), NC_("unit-format", "%s ˚C"), "x+273.15", "x-273.15", NC_("unit-symbols", "degC,˚C")},
+ {NULL, "degree-farenheit", N_("Farenheit"), NC_("unit-format", "%s ˚F"), "(x+459.67)*5/9", "x*9/5-459.67", NC_("unit-symbols", "degF,˚F")},
+ {NULL, "degree-kelvin", N_("Kelvin"), NC_("unit-format", "%s K"), "x", "x", NC_("unit-symbols", "K")},
+ {NULL, "degree-rankine", N_("Rankine"), NC_("unit-format", "%s ˚R"), "x*5/9", "x*9/5", NC_("unit-symbols", "degR,˚R,˚Ra")},
+ { NULL, NULL, NULL, NULL, NULL, NULL }
+ };
+
+ if (default_unit_manager)
+ return default_unit_manager;
+
+ default_unit_manager = g_object_new(unit_manager_get_type(), NULL);
+
+ unit_manager_add_category(default_unit_manager, "angle", _("Angle"));
+ unit_manager_add_category(default_unit_manager, "length", _("Length"));
+ unit_manager_add_category(default_unit_manager, "area", _("Area"));
+ unit_manager_add_category(default_unit_manager, "volume", _("Volume"));
+ unit_manager_add_category(default_unit_manager, "weight", _("Weight"));
+ unit_manager_add_category(default_unit_manager, "duration", _("Duration"));
+ unit_manager_add_category(default_unit_manager, "temperature", _("Temperature"));
+
+ for (i = 0; units[i].name; i++) {
+ if (units[i].category)
+ category = unit_manager_get_category(default_unit_manager, units[i].category);
+ unit_category_add_unit(category, unit_new(units[i].name,
+ _(units[i].display_name),
+ g_dpgettext2(NULL, "unit-format", units[i].format),
+ units[i].from_function, units[i].to_function,
+ g_dpgettext2(NULL, "unit-symbols", units[i].symbols)));
+ }
+
+ category = unit_manager_add_category(default_unit_manager, "currency", _("Currency"));
+ currencies = g_list_copy(currency_manager_get_currencies(currency_manager_get_default()));
+ currencies = g_list_sort(currencies, compare_currencies);
+ for (iter = currencies; iter; iter = iter->next)
+ {
+ Currency *currency = iter->data;
+ gchar *format;
+ Unit *unit;
+
+ /* Translators: result of currency conversion, %s is the symbol, %%s is the placeholder for amount, i.e.: USD100 */
+ format = g_strdup_printf(_("%s%%s"), currency_get_symbol(currency));
+ unit = unit_new(currency_get_name(currency), currency_get_display_name(currency), format, NULL, NULL, currency_get_name(currency));
+ g_free(format);
+
+ unit_category_add_unit(category, unit);
+ }
+ g_list_free(currencies);
+
+ return default_unit_manager;
+}
+
+
+UnitCategory *
+unit_manager_add_category(UnitManager *manager, const gchar *name, const gchar *display_name)
+{
+ UnitCategory *category;
+
+ g_return_val_if_fail(manager != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+ g_return_val_if_fail(display_name != NULL, NULL);
+ g_return_val_if_fail(unit_manager_get_category(manager, name) == NULL, NULL);
+
+ category = unit_category_new(name, display_name);
+ manager->priv->categories = g_list_append(manager->priv->categories, category);
+
+ return category;
+}
+
+
+const GList *
+unit_manager_get_categories(UnitManager *manager)
+{
+ g_return_val_if_fail(manager != NULL, NULL);
+ return manager->priv->categories;
+}
+
+
+UnitCategory *
+unit_manager_get_category(UnitManager *manager, const gchar *category)
+{
+ GList *iter;
+
+ g_return_val_if_fail(manager != NULL, NULL);
+ g_return_val_if_fail(category != NULL, NULL);
+
+ for (iter = manager->priv->categories; iter; iter = iter->next) {
+ UnitCategory *c = iter->data;
+ if (strcmp(unit_category_get_name(c), category) == 0)
+ return c;
+ }
+
+ return NULL;
+}
+
+
+Unit *
+unit_manager_get_unit_by_name(UnitManager *manager, const gchar *name)
+{
+ GList *iter;
+ Unit *u;
+
+ g_return_val_if_fail(manager != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ for (iter = manager->priv->categories; iter; iter = iter->next) {
+ UnitCategory *c = iter->data;
+ u = unit_category_get_unit_by_name(c, name);
+ if (u)
+ return u;
+ }
+
+ return NULL;
+}
+
+
+Unit *
+unit_manager_get_unit_by_symbol(UnitManager *manager, const gchar *symbol)
+{
+ GList *iter;
+ Unit *u;
+
+ g_return_val_if_fail(manager != NULL, NULL);
+ g_return_val_if_fail(symbol != NULL, NULL);
+
+ for (iter = manager->priv->categories; iter; iter = iter->next) {
+ UnitCategory *c = iter->data;
+ u = unit_category_get_unit_by_symbol(c, symbol);
+ if (u)
+ return u;
+ }
+
+ return NULL;
+}
+
+
+gboolean
+unit_manager_convert_by_symbol(UnitManager *manager, const MPNumber *x, const char *x_symbol, const char *z_symbol, MPNumber *z)
+{
+ GList *iter;
+
+ g_return_val_if_fail(manager != NULL, FALSE);
+ g_return_val_if_fail(x != NULL, FALSE);
+ g_return_val_if_fail(x_symbol != NULL, FALSE);
+ g_return_val_if_fail(z_symbol != NULL, FALSE);
+ g_return_val_if_fail(z != NULL, FALSE);
+
+ for (iter = manager->priv->categories; iter; iter = iter->next) {
+ UnitCategory *c = iter->data;
+ Unit *x_units, *z_units;
+
+ x_units = unit_category_get_unit_by_symbol(c, x_symbol);
+ z_units = unit_category_get_unit_by_symbol(c, z_symbol);
+ if (x_units && z_units && unit_category_convert(c, x, x_units, z_units, z))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void
+unit_manager_class_init(UnitManagerClass *klass)
+{
+ g_type_class_add_private(klass, sizeof(UnitManagerPrivate));
+}
+
+
+static void
+unit_manager_init(UnitManager *manager)
+{
+ manager->priv = G_TYPE_INSTANCE_GET_PRIVATE(manager, unit_manager_get_type(), UnitManagerPrivate);
+}
diff --git a/src/unit-manager.h b/src/unit-manager.h
new file mode 100644
index 0000000..db1b4f2
--- /dev/null
+++ b/src/unit-manager.h
@@ -0,0 +1,54 @@
+/*
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef UNIT_MANAGER_H
+#define UNIT_MANAGER_H
+
+#include <glib-object.h>
+#include "unit-category.h"
+#include "mp.h"
+
+G_BEGIN_DECLS
+
+#define UNIT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), unit_manager_get_type(), UnitManager))
+
+typedef struct UnitManagerPrivate UnitManagerPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ UnitManagerPrivate *priv;
+} UnitManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} UnitManagerClass;
+
+GType unit_manager_get_type(void);
+
+UnitManager *unit_manager_get_default(void);
+
+UnitCategory *unit_manager_add_category(UnitManager *manager, const gchar *name, const gchar *display_name);
+
+const GList *unit_manager_get_categories(UnitManager *manager);
+
+UnitCategory *unit_manager_get_category(UnitManager *manager, const gchar *category);
+
+Unit *unit_manager_get_unit_by_name(UnitManager *manager, const gchar *name);
+
+Unit *unit_manager_get_unit_by_symbol(UnitManager *manager, const gchar *symbol);
+
+gboolean unit_manager_convert_by_symbol(UnitManager *manager, const MPNumber *x, const char *x_symbol, const char *z_symbol, MPNumber *z);
+
+G_END_DECLS
+
+#endif /* UNIT_MANAGER_H */
diff --git a/src/unit.c b/src/unit.c
new file mode 100644
index 0000000..f7c0277
--- /dev/null
+++ b/src/unit.c
@@ -0,0 +1,212 @@
+/*
+ * 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 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 <string.h>
+
+#include "unit.h"
+#include "mp-serializer.h"
+#include "mp-equation.h"
+#include "currency-manager.h" // FIXME: Move out of here
+
+struct UnitPrivate
+{
+ gchar *name;
+ gchar *display_name;
+ gchar *format;
+ GList *symbols;
+ gchar *from_function;
+ gchar *to_function;
+ MpSerializer *serializer;
+};
+
+G_DEFINE_TYPE (Unit, unit, G_TYPE_OBJECT);
+
+
+Unit *
+unit_new(const gchar *name,
+ const gchar *display_name,
+ const gchar *format,
+ const gchar *from_function,
+ const gchar *to_function,
+ const gchar *symbols)
+{
+ Unit *unit = g_object_new(unit_get_type(), NULL);
+ gchar **symbol_names;
+ int i;
+
+ unit->priv->name = g_strdup(name);
+ unit->priv->display_name = g_strdup(display_name);
+ unit->priv->format = g_strdup(format);
+ unit->priv->from_function = g_strdup(from_function);
+ unit->priv->to_function = g_strdup(to_function);
+ symbol_names = g_strsplit(symbols, ",", 0);
+ for (i = 0; symbol_names[i]; i++)
+ unit->priv->symbols = g_list_append(unit->priv->symbols, g_strdup(symbol_names[i]));
+ g_free(symbol_names);
+
+ return unit;
+}
+
+
+const gchar *
+unit_get_name(Unit *unit)
+{
+ g_return_val_if_fail (unit != NULL, NULL);
+ return unit->priv->name;
+}
+
+
+const gchar *
+unit_get_display_name(Unit *unit)
+{
+ g_return_val_if_fail (unit != NULL, NULL);
+ return unit->priv->display_name;
+}
+
+
+gboolean
+unit_matches_symbol(Unit *unit, const gchar *symbol)
+{
+ GList *iter;
+
+ g_return_val_if_fail (unit != NULL, FALSE);
+ g_return_val_if_fail (symbol != NULL, FALSE);
+
+ for (iter = unit->priv->symbols; iter; iter = iter->next) {
+ gchar *s = iter->data;
+ if (strcmp(s, symbol) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+const GList *
+unit_get_symbols(Unit *unit)
+{
+ g_return_val_if_fail (unit != NULL, NULL);
+ return unit->priv->symbols;
+}
+
+
+static int
+variable_is_defined(const char *name, void *data)
+{
+ return TRUE;
+}
+
+
+static int
+get_variable(const char *name, MPNumber *z, void *data)
+{
+ MPNumber *x = data;
+ mp_set_from_mp(x, z);
+ return TRUE;
+}
+
+
+static gboolean
+solve_function(const gchar *function, const MPNumber *x, MPNumber *z)
+{
+ MPEquationOptions options;
+ int ret;
+
+ memset(&options, 0, sizeof(options));
+ options.base = 10;
+ options.wordlen = 32;
+ options.variable_is_defined = variable_is_defined;
+ options.get_variable = get_variable;
+ options.callback_data = (void *)x;
+ ret = mp_equation_parse(function, &options, z, NULL);
+ if (ret) {
+ g_warning("Failed to convert value: %s", function);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+gboolean
+unit_convert_from(Unit *unit, const MPNumber *x, MPNumber *z)
+{
+ g_return_val_if_fail(unit != NULL, FALSE);
+ g_return_val_if_fail(x != NULL, FALSE);
+ g_return_val_if_fail(x != NULL, FALSE);
+
+ if (unit->priv->from_function)
+ return solve_function(unit->priv->from_function, x, z);
+ else {
+ // FIXME: Hack to make currency work
+ const MPNumber *r;
+ r = currency_manager_get_value(currency_manager_get_default(), unit->priv->name);
+ if (!r)
+ return FALSE;
+ mp_divide(x, r, z);
+
+ return TRUE;
+ }
+}
+
+
+gboolean
+unit_convert_to(Unit *unit, const MPNumber *x, MPNumber *z)
+{
+ g_return_val_if_fail(unit != NULL, FALSE);
+ g_return_val_if_fail(x != NULL, FALSE);
+ g_return_val_if_fail(x != NULL, FALSE);
+
+ if (unit->priv->from_function)
+ return solve_function(unit->priv->to_function, x, z);
+ else {
+ // FIXME: Hack to make currency work
+ const MPNumber *r;
+ r = currency_manager_get_value(currency_manager_get_default(), unit->priv->name);
+ if (!r)
+ return FALSE;
+ mp_multiply(x, r, z);
+
+ return TRUE;
+ }
+}
+
+
+gchar *
+unit_format(Unit *unit, MPNumber *x)
+{
+ gchar *number_text, *text;
+
+ g_return_val_if_fail(unit != NULL, FALSE);
+ g_return_val_if_fail(x != NULL, FALSE);
+
+ number_text = mp_serializer_to_string(unit->priv->serializer, x);
+ text = g_strdup_printf(unit->priv->format, number_text);
+ g_free(number_text);
+
+ return text;
+}
+
+
+static void
+unit_class_init(UnitClass *klass)
+{
+ g_type_class_add_private(klass, sizeof(UnitPrivate));
+}
+
+
+static void
+unit_init(Unit *unit)
+{
+ unit->priv = G_TYPE_INSTANCE_GET_PRIVATE(unit, unit_get_type(), UnitPrivate);
+ unit->priv->serializer = mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC, 10, 2);
+ mp_serializer_set_leading_digits(unit->priv->serializer, 6);
+}
diff --git a/src/unit.h b/src/unit.h
new file mode 100644
index 0000000..3c9087f
--- /dev/null
+++ b/src/unit.h
@@ -0,0 +1,60 @@
+/*
+ * 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef UNIT_H
+#define UNIT_H
+
+#include <glib-object.h>
+#include "mp.h"
+
+G_BEGIN_DECLS
+
+#define UNIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), unit_get_type(), Unit))
+
+typedef struct UnitPrivate UnitPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ UnitPrivate *priv;
+} Unit;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} UnitClass;
+
+GType unit_get_type(void);
+
+Unit *unit_new(const gchar *name,
+ const gchar *display_name,
+ const gchar *format,
+ const gchar *from_function,
+ const gchar *to_function,
+ const gchar *symbols);
+
+const gchar *unit_get_name(Unit *unit);
+
+const gchar *unit_get_display_name(Unit *unit);
+
+gboolean unit_matches_symbol(Unit *unit, const gchar *symbol);
+
+const GList *unit_get_symbols(Unit *unit);
+
+gboolean unit_convert_from(Unit *unit, const MPNumber *x, MPNumber *z);
+
+gboolean unit_convert_to(Unit *unit, const MPNumber *x, MPNumber *z);
+
+gchar *unit_format(Unit *unit, MPNumber *x);
+
+G_END_DECLS
+
+#endif /* UNIT_H */