Update version number
[gcalctool.git] / src / math-converter.c
blob2f543fe20a0248495dcf67dec439c67adc8446c9
1 /*
2 * Copyright (C) 2008-2011 Robert Ancell
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free Software
6 * Foundation, either version 2 of the License, or (at your option) any later
7 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
8 * license.
9 */
11 #include <glib/gi18n.h>
13 #include "math-converter.h"
14 #include "unit-manager.h"
15 #include "currency-manager.h"
17 enum {
18 CHANGED,
19 LAST_SIGNAL
21 static guint signals[LAST_SIGNAL] = { 0, };
23 struct MathConverterPrivate
25 MathEquation *equation;
27 gchar *category;
29 GtkWidget *from_combo;
30 GtkWidget *to_combo;
32 GtkWidget *result_label;
36 G_DEFINE_TYPE (MathConverter, math_converter, GTK_TYPE_HBOX);
38 static void display_changed_cb(MathEquation *equation, GParamSpec *spec, MathConverter *converter);
39 static void update_from_model(MathConverter *converter);
42 MathConverter *
43 math_converter_new(MathEquation *equation)
45 MathConverter *converter = g_object_new(math_converter_get_type(), NULL);
46 converter->priv->equation = g_object_ref(equation);
47 g_signal_connect(converter->priv->equation, "notify::display", G_CALLBACK(display_changed_cb), converter);
48 update_from_model(converter);
49 return converter;
53 static gboolean
54 convert_equation(MathConverter *converter, const MPNumber *x, MPNumber *z)
56 GtkTreeIter from_iter, to_iter;
57 UnitCategory *category = NULL;
58 Unit *source_unit = NULL, *target_unit = NULL;
59 gboolean result;
61 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &from_iter) ||
62 !gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->to_combo), &to_iter))
63 return FALSE;
65 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo)), &from_iter, 1, &category, 2, &source_unit, -1);
66 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->to_combo)), &to_iter, 2, &target_unit, -1);
68 result = unit_category_convert(category, x, source_unit, target_unit, z);
70 if (category)
71 g_object_unref(category);
72 if (source_unit)
73 g_object_unref(source_unit);
74 if (target_unit)
75 g_object_unref(target_unit);
77 return result;
81 static void
82 update_result_label(MathConverter *converter)
84 MPNumber x, z;
85 gboolean enabled;
87 if (!converter->priv->result_label)
88 return;
90 if (math_equation_get_number(converter->priv->equation, &x))
91 enabled = convert_equation(converter, &x, &z);
92 else
93 enabled = FALSE;
95 gtk_widget_set_sensitive(converter->priv->result_label, enabled);
96 if (enabled) {
97 gchar *source_text, *target_text, *label;
98 Unit *source_unit, *target_unit;
100 math_converter_get_conversion(converter, &source_unit, &target_unit);
102 source_text = unit_format(source_unit, &x);
103 target_text = unit_format(target_unit, &z);
104 label = g_strdup_printf("%s = %s", source_text, target_text);
105 gtk_label_set_text(GTK_LABEL(converter->priv->result_label), label);
107 g_free(source_text);
108 g_free(target_text);
109 g_free(label);
111 g_object_unref(source_unit);
112 g_object_unref(target_unit);
117 static void
118 display_changed_cb(MathEquation *equation, GParamSpec *spec, MathConverter *converter)
120 update_result_label(converter);
124 static void
125 update_from_model(MathConverter *converter)
127 GtkTreeStore *from_model;
129 from_model = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_OBJECT);
131 if (converter->priv->category == NULL) {
132 const GList *categories, *iter;
134 categories = unit_manager_get_categories(unit_manager_get_default());
135 for (iter = categories; iter; iter = iter->next) {
136 UnitCategory *category = iter->data;
137 GtkTreeIter parent;
138 const GList *unit_iter;
140 gtk_tree_store_append(from_model, &parent, NULL);
141 gtk_tree_store_set(from_model, &parent, 0, unit_category_get_display_name(category), 1, category, -1);
143 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
144 Unit *unit = unit_iter->data;
145 GtkTreeIter iter;
147 gtk_tree_store_append(from_model, &iter, &parent);
148 gtk_tree_store_set(from_model, &iter, 0, unit_get_display_name(unit), 1, category, 2, unit, -1);
152 else {
153 UnitCategory *category;
154 const GList *unit_iter;
156 category = unit_manager_get_category(unit_manager_get_default(), converter->priv->category);
157 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
158 Unit *unit = unit_iter->data;
159 GtkTreeIter iter;
161 gtk_tree_store_append(from_model, &iter, NULL);
162 gtk_tree_store_set(from_model, &iter, 0, unit_get_display_name(unit), 1, category, 2, unit, -1);
166 gtk_combo_box_set_model(GTK_COMBO_BOX(converter->priv->from_combo), GTK_TREE_MODEL(from_model));
170 void
171 math_converter_set_category(MathConverter *converter, const gchar *category)
173 g_return_if_fail (converter != NULL);
175 if (category == NULL && converter->priv->category == NULL)
176 return;
177 if (category != NULL && converter->priv->category != NULL && strcmp(category, converter->priv->category) == 0)
178 return;
180 g_free(converter->priv->category);
181 converter->priv->category = g_strdup(category);
183 update_from_model(converter);
187 const gchar *
188 math_converter_get_category(MathConverter *converter)
190 g_return_val_if_fail (converter != NULL, NULL);
191 return converter->priv->category;
195 static gboolean
196 iter_is_unit(GtkTreeModel *model, GtkTreeIter *iter, Unit *unit)
198 Unit *u;
200 gtk_tree_model_get(model, iter, 2, &u, -1);
202 if (!u)
203 return FALSE;
205 g_object_unref(u);
206 if (u == unit)
207 return TRUE;
209 return FALSE;
213 static gboolean
214 set_active_unit(GtkComboBox *combo, GtkTreeIter *iter, Unit *unit)
216 GtkTreeModel *model;
217 GtkTreeIter child_iter;
219 model = gtk_combo_box_get_model(combo);
221 if (iter && iter_is_unit(model, iter, unit)) {
222 gtk_combo_box_set_active_iter(combo, iter);
223 return TRUE;
226 if (!gtk_tree_model_iter_children(model, &child_iter, iter))
227 return FALSE;
229 do {
230 if (set_active_unit(combo, &child_iter, unit))
231 return TRUE;
232 } while (gtk_tree_model_iter_next(model, &child_iter));
234 return FALSE;
238 void
239 math_converter_set_conversion(MathConverter *converter, /*const gchar *category,*/ const gchar *unit_a, const gchar *unit_b)
241 Unit *ua;
242 Unit *ub;
244 g_return_if_fail (converter != NULL);
245 g_return_if_fail (unit_a != NULL);
246 g_return_if_fail (unit_b != NULL);
248 ua = unit_manager_get_unit_by_name(unit_manager_get_default(), unit_a);
249 ub = unit_manager_get_unit_by_name(unit_manager_get_default(), unit_b);
250 if (!ua || !ub)
252 GtkTreeModel *model;
253 GtkTreeIter iter;
255 /* Select the first unit */
256 model = gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo));
257 if (gtk_tree_model_get_iter_first(model, &iter)) {
258 GtkTreeIter child_iter;
259 while (gtk_tree_model_iter_children(model, &child_iter, &iter))
260 iter = child_iter;
261 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &iter);
263 return;
266 set_active_unit(GTK_COMBO_BOX(converter->priv->from_combo), NULL, ua);
267 set_active_unit(GTK_COMBO_BOX(converter->priv->to_combo), NULL, ub);
271 void
272 math_converter_get_conversion(MathConverter *converter, Unit **from_unit, Unit **to_unit)
274 GtkTreeIter from_iter, to_iter;
276 g_return_if_fail (converter != NULL);
277 g_return_if_fail (from_unit != NULL);
278 g_return_if_fail (to_unit != NULL);
280 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &from_iter);
281 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->to_combo), &to_iter);
283 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo)), &from_iter, 2, from_unit, -1);
284 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->to_combo)), &to_iter, 2, to_unit, -1);
288 static void
289 math_converter_class_init(MathConverterClass *klass)
291 g_type_class_add_private(klass, sizeof(MathConverterPrivate));
293 signals[CHANGED] =
294 g_signal_new("changed",
295 G_TYPE_FROM_CLASS (klass),
296 G_SIGNAL_RUN_LAST,
297 G_STRUCT_OFFSET (MathConverterClass, changed),
298 NULL, NULL,
299 g_cclosure_marshal_VOID__VOID,
300 G_TYPE_NONE, 0);
304 static void
305 from_combobox_changed_cb(GtkWidget *combo, MathConverter *converter)
307 GtkTreeModel *model;
308 GtkTreeIter iter;
309 UnitCategory *category;
310 Unit *unit;
311 const GList *unit_iter;
313 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
314 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter))
315 return;
316 gtk_tree_model_get(model, &iter, 1, &category, 2, &unit, -1);
318 /* Set the to combobox to be the list of units can be converted to */
319 model = GTK_TREE_MODEL(gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_OBJECT));
320 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
321 Unit *u = unit_iter->data;
322 if (u == unit)
323 continue;
324 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
325 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, unit_get_display_name(u), 1, category, 2, u, -1);
327 gtk_combo_box_set_model(GTK_COMBO_BOX(converter->priv->to_combo), model);
329 /* Select the first possible unit */
330 gtk_combo_box_set_active(GTK_COMBO_BOX(converter->priv->to_combo), 0);
332 g_object_unref(category);
333 g_object_unref(unit);
337 static void
338 to_combobox_changed_cb(GtkWidget *combo, MathConverter *converter)
340 /* Conversion must have changed */
341 update_result_label(converter);
343 g_signal_emit(converter, signals[CHANGED], 0);
347 static void
348 from_cell_data_func(GtkCellLayout *cell_layout,
349 GtkCellRenderer *cell,
350 GtkTreeModel *tree_model,
351 GtkTreeIter *iter,
352 gpointer data)
354 g_object_set(cell, "sensitive", !gtk_tree_model_iter_has_child(tree_model, iter), NULL);
358 static void
359 currency_updated_cb(CurrencyManager *manager, MathConverter *converter)
361 update_result_label(converter);
364 static void
365 swap_button_clicked_cb(GtkButton *button, MathConverter *converter)
367 Unit *from_unit, *to_unit;
368 MPNumber x, z;
370 if (math_equation_get_number(converter->priv->equation, &x) &&
371 convert_equation(converter, &x, &z))
372 math_equation_set_number(converter->priv->equation, &z);
374 math_converter_get_conversion(converter, &from_unit, &to_unit);
375 set_active_unit(GTK_COMBO_BOX(converter->priv->from_combo), NULL, to_unit);
376 set_active_unit(GTK_COMBO_BOX(converter->priv->to_combo), NULL, from_unit);
378 update_result_label(converter);
380 g_object_unref(from_unit);
381 g_object_unref(to_unit);
384 static void
385 math_converter_init(MathConverter *converter)
387 GtkWidget *hbox, *label, *swap_button;
388 GtkCellRenderer *renderer;
390 converter->priv = G_TYPE_INSTANCE_GET_PRIVATE(converter, math_converter_get_type(), MathConverterPrivate);
392 gtk_box_set_spacing(GTK_BOX(converter), 6);
394 hbox = gtk_hbox_new(FALSE, 0);
395 gtk_widget_show(hbox);
396 gtk_box_pack_start(GTK_BOX(converter), hbox, FALSE, TRUE, 0);
398 converter->priv->from_combo = gtk_combo_box_new ();
400 renderer = gtk_cell_renderer_text_new();
401 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter->priv->from_combo), renderer, TRUE);
402 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter->priv->from_combo), renderer, "text", 0);
403 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(converter->priv->from_combo),
404 renderer,
405 from_cell_data_func,
406 NULL, NULL);
407 g_signal_connect(converter->priv->from_combo, "changed", G_CALLBACK(from_combobox_changed_cb), converter);
408 gtk_widget_show(converter->priv->from_combo);
409 gtk_box_pack_start(GTK_BOX(hbox), converter->priv->from_combo, FALSE, TRUE, 0);
411 label = gtk_label_new(/* Label that is displayed between the two conversion combo boxes, e.g. "[degrees] in [radians]" */
412 _(" in "));
413 gtk_widget_show(label);
414 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 5);
416 converter->priv->to_combo = gtk_combo_box_new();
417 renderer = gtk_cell_renderer_text_new();
418 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter->priv->to_combo), renderer, TRUE);
419 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter->priv->to_combo), renderer, "text", 0);
420 g_signal_connect(converter->priv->to_combo, "changed", G_CALLBACK(to_combobox_changed_cb), converter);
421 gtk_widget_show(converter->priv->to_combo);
422 gtk_box_pack_start(GTK_BOX(hbox), converter->priv->to_combo, FALSE, TRUE, 0);
424 swap_button = gtk_button_new_with_label ("⇆");
425 gtk_widget_set_tooltip_text (swap_button,
426 /* Tooltip for swap conversion button */
427 _("Switch conversion units"));
428 gtk_button_set_relief (GTK_BUTTON (swap_button), GTK_RELIEF_NONE);
429 g_signal_connect (swap_button, "clicked", G_CALLBACK (swap_button_clicked_cb), converter);
430 gtk_widget_show(swap_button);
431 gtk_box_pack_start(GTK_BOX(hbox), swap_button, FALSE, TRUE, 0);
433 converter->priv->result_label = gtk_label_new("");
434 gtk_misc_set_alignment(GTK_MISC(converter->priv->result_label), 1.0, 0.5);
435 gtk_widget_set_sensitive(converter->priv->result_label, FALSE);
436 gtk_widget_show(converter->priv->result_label);
437 gtk_box_pack_start(GTK_BOX(converter), converter->priv->result_label, TRUE, TRUE, 0);
439 g_signal_connect(currency_manager_get_default(), "updated", G_CALLBACK(currency_updated_cb), converter);