summaryrefslogtreecommitdiff
path: root/src/math-converter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/math-converter.c')
-rw-r--r--src/math-converter.c440
1 files changed, 440 insertions, 0 deletions
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);
+}