Merge currency and units together, drop old financial conversion bar and use generic...
[gcalctool.git] / src / math-converter.c
blobe2279a74dd8559711e61a3c31fa2e0d4fc07a9bd
1 /* Copyright (c) 2008-2009 Robert Ancell
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16 * 02111-1307, USA.
19 #include <glib/gi18n.h>
21 #include "math-converter.h"
22 #include "unit-manager.h"
23 #include "currency.h"
25 enum {
26 CHANGED,
27 LAST_SIGNAL
29 static guint signals[LAST_SIGNAL] = { 0, };
31 struct MathConverterPrivate
33 MathEquation *equation;
35 gchar *category;
37 GtkWidget *from_combo;
38 GtkWidget *to_combo;
40 GtkWidget *result_label;
44 G_DEFINE_TYPE (MathConverter, math_converter, GTK_TYPE_HBOX);
46 static void display_changed_cb(MathEquation *equation, GParamSpec *spec, MathConverter *converter);
47 static void update_from_model(MathConverter *converter);
50 MathConverter *
51 math_converter_new(MathEquation *equation)
53 MathConverter *converter = g_object_new(math_converter_get_type(), NULL);
54 converter->priv->equation = g_object_ref(equation);
55 g_signal_connect(converter->priv->equation, "notify::display", G_CALLBACK(display_changed_cb), converter);
56 update_from_model(converter);
57 return converter;
61 static void
62 update_result_label(MathConverter *converter)
64 GtkTreeIter from_iter, to_iter;
65 UnitCategory *category = NULL;
66 Unit *source_unit = NULL, *target_unit = NULL;
67 MPNumber x, z;
68 gboolean enabled;
70 if (!converter->priv->result_label)
71 return;
73 enabled = math_equation_get_number(converter->priv->equation, &x);
75 if (enabled &&
76 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &from_iter) &&
77 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->to_combo), &to_iter)) {
78 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo)), &from_iter, 1, &category, 2, &source_unit, -1);
79 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->to_combo)), &to_iter, 2, &target_unit, -1);
80 if (!unit_category_convert(category, &x, unit_get_name(source_unit), unit_get_name(target_unit), &z))
81 enabled = FALSE;
83 else
84 enabled = FALSE;
86 gtk_widget_set_sensitive(converter->priv->result_label, enabled);
87 if (enabled) {
88 gchar *source_text, *target_text, *label;
89 source_text = unit_format(source_unit, &x);
90 target_text = unit_format(target_unit, &z);
91 label = g_strdup_printf("%s = %s", source_text, target_text);
92 gtk_label_set_text(GTK_LABEL(converter->priv->result_label), label);
93 g_free(source_text);
94 g_free(target_text);
95 g_free(label);
98 if (category)
99 g_object_unref(category);
100 if (source_unit)
101 g_object_unref(source_unit);
102 if (target_unit)
103 g_object_unref(target_unit);
107 static void
108 display_changed_cb(MathEquation *equation, GParamSpec *spec, MathConverter *converter)
110 update_result_label(converter);
114 static void
115 update_from_model(MathConverter *converter)
117 GtkTreeStore *from_model;
119 from_model = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_OBJECT);
121 if (converter->priv->category == NULL) {
122 const GList *categories, *iter;
124 categories = unit_manager_get_categories(math_equation_get_unit_manager(converter->priv->equation));
125 for (iter = categories; iter; iter = iter->next) {
126 UnitCategory *category = iter->data;
127 GtkTreeIter parent;
128 const GList *unit_iter;
130 gtk_tree_store_append(from_model, &parent, NULL);
131 gtk_tree_store_set(from_model, &parent, 0, unit_category_get_display_name(category), 1, category, -1);
133 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
134 Unit *unit = unit_iter->data;
135 GtkTreeIter iter;
137 gtk_tree_store_append(from_model, &iter, &parent);
138 gtk_tree_store_set(from_model, &iter, 0, unit_get_display_name(unit), 1, category, 2, unit, -1);
142 else {
143 UnitCategory *category;
144 const GList *unit_iter;
146 category = unit_manager_get_category(math_equation_get_unit_manager(converter->priv->equation), converter->priv->category);
147 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
148 Unit *unit = unit_iter->data;
149 GtkTreeIter iter;
151 gtk_tree_store_append(from_model, &iter, NULL);
152 gtk_tree_store_set(from_model, &iter, 0, unit_get_display_name(unit), 1, category, 2, unit, -1);
156 gtk_combo_box_set_model(GTK_COMBO_BOX(converter->priv->from_combo), GTK_TREE_MODEL(from_model));
160 void
161 math_converter_set_category(MathConverter *converter, const gchar *category)
163 if (category == NULL && converter->priv->category == NULL)
164 return;
165 if (category != NULL && converter->priv->category != NULL && strcmp(category, converter->priv->category) == 0)
166 return;
168 g_free(converter->priv->category);
169 converter->priv->category = g_strdup(category);
171 update_from_model(converter);
175 const gchar *
176 math_converter_get_category(MathConverter *converter)
178 return converter->priv->category;
182 static gboolean
183 iter_is_unit(GtkTreeModel *model, GtkTreeIter *iter, Unit *unit)
185 Unit *u;
187 gtk_tree_model_get(model, iter, 2, &u, -1);
189 if (!u)
190 return FALSE;
192 g_object_unref(u);
193 if (u == unit)
194 return TRUE;
196 return FALSE;
200 static gboolean
201 set_active_unit(GtkComboBox *combo, GtkTreeIter *iter, Unit *unit)
203 GtkTreeModel *model;
204 GtkTreeIter child_iter;
206 model = gtk_combo_box_get_model(combo);
208 if (iter && iter_is_unit(model, iter, unit)) {
209 gtk_combo_box_set_active_iter(combo, iter);
210 return TRUE;
213 if (!gtk_tree_model_iter_children(model, &child_iter, iter))
214 return FALSE;
216 do {
217 if (set_active_unit(combo, &child_iter, unit))
218 return TRUE;
219 } while (gtk_tree_model_iter_next(model, &child_iter));
221 return FALSE;
225 void
226 math_converter_set_conversion(MathConverter *converter, /*const gchar *category,*/ const gchar *unit_a, const gchar *unit_b)
228 Unit *ua;
229 Unit *ub;
231 ua = unit_manager_get_unit(math_equation_get_unit_manager(converter->priv->equation), unit_a);
232 ub = unit_manager_get_unit(math_equation_get_unit_manager(converter->priv->equation), unit_b);
233 if (!ua || !ub)
234 return;
236 set_active_unit(GTK_COMBO_BOX(converter->priv->from_combo), NULL, ua);
237 set_active_unit(GTK_COMBO_BOX(converter->priv->to_combo), NULL, ub);
241 void
242 math_converter_get_conversion(MathConverter *converter, Unit **from_unit, Unit **to_unit)
244 GtkTreeIter from_iter, to_iter;
246 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &from_iter);
247 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->to_combo), &to_iter);
249 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo)), &from_iter, 2, from_unit, -1);
250 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->to_combo)), &to_iter, 2, to_unit, -1);
254 static void
255 math_converter_class_init(MathConverterClass *klass)
257 g_type_class_add_private(klass, sizeof(MathConverterPrivate));
259 signals[CHANGED] =
260 g_signal_new("changed",
261 G_TYPE_FROM_CLASS (klass),
262 G_SIGNAL_RUN_LAST,
263 G_STRUCT_OFFSET (MathConverterClass, changed),
264 NULL, NULL,
265 g_cclosure_marshal_VOID__VOID,
266 G_TYPE_NONE, 0);
270 static void
271 from_combobox_changed_cb(GtkWidget *combo, MathConverter *converter)
273 GtkTreeModel *model;
274 GtkTreeIter iter;
275 UnitCategory *category;
276 Unit *unit;
277 const GList *unit_iter;
279 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
280 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter))
281 return;
282 gtk_tree_model_get(model, &iter, 1, &category, 2, &unit, -1);
284 /* Set the to combobox to be the list of units can be converted to */
285 model = GTK_TREE_MODEL(gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_OBJECT));
286 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
287 Unit *u = unit_iter->data;
288 if (u == unit)
289 continue;
290 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
291 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, unit_get_display_name(u), 1, category, 2, u, -1);
293 gtk_combo_box_set_model(GTK_COMBO_BOX(converter->priv->to_combo), model);
295 /* Select the first possible unit */
296 gtk_combo_box_set_active(GTK_COMBO_BOX(converter->priv->to_combo), 0);
298 g_object_unref(category);
299 g_object_unref(unit);
303 static void
304 to_combobox_changed_cb(GtkWidget *combo, MathConverter *converter)
306 /* Conversion must have changed */
307 update_result_label(converter);
309 g_signal_emit(converter, signals[CHANGED], 0);
313 static void
314 from_cell_data_func(GtkCellLayout *cell_layout,
315 GtkCellRenderer *cell,
316 GtkTreeModel *tree_model,
317 GtkTreeIter *iter,
318 gpointer data)
320 g_object_set(cell, "sensitive", !gtk_tree_model_iter_has_child(tree_model, iter), NULL);
324 static void
325 math_converter_init(MathConverter *converter)
327 GtkWidget *hbox, *label;
328 GtkCellRenderer *renderer;
330 converter->priv = G_TYPE_INSTANCE_GET_PRIVATE(converter, math_converter_get_type(), MathConverterPrivate);
332 gtk_box_set_spacing(GTK_BOX(converter), 6);
334 hbox = gtk_hbox_new(FALSE, 0);
335 gtk_widget_show(hbox);
336 gtk_box_pack_start(GTK_BOX(converter), hbox, FALSE, TRUE, 0);
338 converter->priv->from_combo = gtk_combo_box_new ();
340 renderer = gtk_cell_renderer_text_new();
341 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter->priv->from_combo), renderer, TRUE);
342 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter->priv->from_combo), renderer, "text", 0);
343 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(converter->priv->from_combo),
344 renderer,
345 from_cell_data_func,
346 NULL, NULL);
347 g_signal_connect(converter->priv->from_combo, "changed", G_CALLBACK(from_combobox_changed_cb), converter);
348 gtk_widget_show(converter->priv->from_combo);
349 gtk_box_pack_start(GTK_BOX(hbox), converter->priv->from_combo, FALSE, TRUE, 0);
351 label = gtk_label_new(/* Label that is displayed between the two conversion combo boxes, e.g. "[degrees] in [radians]" */
352 _(" in "));
353 gtk_widget_show(label);
354 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
356 converter->priv->to_combo = gtk_combo_box_new();
357 renderer = gtk_cell_renderer_text_new();
358 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter->priv->to_combo), renderer, TRUE);
359 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter->priv->to_combo), renderer, "text", 0);
360 g_signal_connect(converter->priv->to_combo, "changed", G_CALLBACK(to_combobox_changed_cb), converter);
361 gtk_widget_show(converter->priv->to_combo);
362 gtk_box_pack_start(GTK_BOX(hbox), converter->priv->to_combo, FALSE, TRUE, 0);
364 converter->priv->result_label = gtk_label_new("");
365 gtk_misc_set_alignment(GTK_MISC(converter->priv->result_label), 1.0, 0.5);
366 gtk_widget_set_sensitive(converter->priv->result_label, FALSE);
367 gtk_widget_show(converter->priv->result_label);
368 gtk_box_pack_start(GTK_BOX(converter), converter->priv->result_label, TRUE, TRUE, 0);