Support both μs and us for entering microseconds
[gcalctool.git] / src / math-converter.c
blob46e360174b9c727c53e42cb19e0ff1d0c6f80b89
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 void
54 update_result_label(MathConverter *converter)
56 GtkTreeIter from_iter, to_iter;
57 UnitCategory *category = NULL;
58 Unit *source_unit = NULL, *target_unit = NULL;
59 MPNumber x, z;
60 gboolean enabled;
62 if (!converter->priv->result_label)
63 return;
65 enabled = math_equation_get_number(converter->priv->equation, &x);
67 if (enabled &&
68 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &from_iter) &&
69 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->to_combo), &to_iter)) {
70 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo)), &from_iter, 1, &category, 2, &source_unit, -1);
71 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->to_combo)), &to_iter, 2, &target_unit, -1);
72 if (!unit_category_convert(category, &x, source_unit, target_unit, &z))
73 enabled = FALSE;
75 else
76 enabled = FALSE;
78 gtk_widget_set_sensitive(converter->priv->result_label, enabled);
79 if (enabled) {
80 gchar *source_text, *target_text, *label;
81 source_text = unit_format(source_unit, &x);
82 target_text = unit_format(target_unit, &z);
83 label = g_strdup_printf("%s = %s", source_text, target_text);
84 gtk_label_set_text(GTK_LABEL(converter->priv->result_label), label);
85 g_free(source_text);
86 g_free(target_text);
87 g_free(label);
90 if (category)
91 g_object_unref(category);
92 if (source_unit)
93 g_object_unref(source_unit);
94 if (target_unit)
95 g_object_unref(target_unit);
99 static void
100 display_changed_cb(MathEquation *equation, GParamSpec *spec, MathConverter *converter)
102 update_result_label(converter);
106 static void
107 update_from_model(MathConverter *converter)
109 GtkTreeStore *from_model;
111 from_model = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_OBJECT);
113 if (converter->priv->category == NULL) {
114 const GList *categories, *iter;
116 categories = unit_manager_get_categories(unit_manager_get_default());
117 for (iter = categories; iter; iter = iter->next) {
118 UnitCategory *category = iter->data;
119 GtkTreeIter parent;
120 const GList *unit_iter;
122 gtk_tree_store_append(from_model, &parent, NULL);
123 gtk_tree_store_set(from_model, &parent, 0, unit_category_get_display_name(category), 1, category, -1);
125 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
126 Unit *unit = unit_iter->data;
127 GtkTreeIter iter;
129 gtk_tree_store_append(from_model, &iter, &parent);
130 gtk_tree_store_set(from_model, &iter, 0, unit_get_display_name(unit), 1, category, 2, unit, -1);
134 else {
135 UnitCategory *category;
136 const GList *unit_iter;
138 category = unit_manager_get_category(unit_manager_get_default(), converter->priv->category);
139 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
140 Unit *unit = unit_iter->data;
141 GtkTreeIter iter;
143 gtk_tree_store_append(from_model, &iter, NULL);
144 gtk_tree_store_set(from_model, &iter, 0, unit_get_display_name(unit), 1, category, 2, unit, -1);
148 gtk_combo_box_set_model(GTK_COMBO_BOX(converter->priv->from_combo), GTK_TREE_MODEL(from_model));
152 void
153 math_converter_set_category(MathConverter *converter, const gchar *category)
155 g_return_if_fail (converter != NULL);
157 if (category == NULL && converter->priv->category == NULL)
158 return;
159 if (category != NULL && converter->priv->category != NULL && strcmp(category, converter->priv->category) == 0)
160 return;
162 g_free(converter->priv->category);
163 converter->priv->category = g_strdup(category);
165 update_from_model(converter);
169 const gchar *
170 math_converter_get_category(MathConverter *converter)
172 g_return_val_if_fail (converter != NULL, NULL);
173 return converter->priv->category;
177 static gboolean
178 iter_is_unit(GtkTreeModel *model, GtkTreeIter *iter, Unit *unit)
180 Unit *u;
182 gtk_tree_model_get(model, iter, 2, &u, -1);
184 if (!u)
185 return FALSE;
187 g_object_unref(u);
188 if (u == unit)
189 return TRUE;
191 return FALSE;
195 static gboolean
196 set_active_unit(GtkComboBox *combo, GtkTreeIter *iter, Unit *unit)
198 GtkTreeModel *model;
199 GtkTreeIter child_iter;
201 model = gtk_combo_box_get_model(combo);
203 if (iter && iter_is_unit(model, iter, unit)) {
204 gtk_combo_box_set_active_iter(combo, iter);
205 return TRUE;
208 if (!gtk_tree_model_iter_children(model, &child_iter, iter))
209 return FALSE;
211 do {
212 if (set_active_unit(combo, &child_iter, unit))
213 return TRUE;
214 } while (gtk_tree_model_iter_next(model, &child_iter));
216 return FALSE;
220 void
221 math_converter_set_conversion(MathConverter *converter, /*const gchar *category,*/ const gchar *unit_a, const gchar *unit_b)
223 Unit *ua;
224 Unit *ub;
226 g_return_if_fail (converter != NULL);
227 g_return_if_fail (unit_a != NULL);
228 g_return_if_fail (unit_b != NULL);
230 ua = unit_manager_get_unit_by_name(unit_manager_get_default(), unit_a);
231 ub = unit_manager_get_unit_by_name(unit_manager_get_default(), unit_b);
232 if (!ua || !ub)
234 GtkTreeModel *model;
235 GtkTreeIter iter;
237 /* Select the first unit */
238 model = gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo));
239 if (gtk_tree_model_get_iter_first(model, &iter)) {
240 GtkTreeIter child_iter;
241 while (gtk_tree_model_iter_children(model, &child_iter, &iter))
242 iter = child_iter;
243 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &iter);
245 return;
248 set_active_unit(GTK_COMBO_BOX(converter->priv->from_combo), NULL, ua);
249 set_active_unit(GTK_COMBO_BOX(converter->priv->to_combo), NULL, ub);
253 void
254 math_converter_get_conversion(MathConverter *converter, Unit **from_unit, Unit **to_unit)
256 GtkTreeIter from_iter, to_iter;
258 g_return_if_fail (converter != NULL);
259 g_return_if_fail (from_unit != NULL);
260 g_return_if_fail (to_unit != NULL);
262 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->from_combo), &from_iter);
263 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(converter->priv->to_combo), &to_iter);
265 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->from_combo)), &from_iter, 2, from_unit, -1);
266 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(converter->priv->to_combo)), &to_iter, 2, to_unit, -1);
270 static void
271 math_converter_class_init(MathConverterClass *klass)
273 g_type_class_add_private(klass, sizeof(MathConverterPrivate));
275 signals[CHANGED] =
276 g_signal_new("changed",
277 G_TYPE_FROM_CLASS (klass),
278 G_SIGNAL_RUN_LAST,
279 G_STRUCT_OFFSET (MathConverterClass, changed),
280 NULL, NULL,
281 g_cclosure_marshal_VOID__VOID,
282 G_TYPE_NONE, 0);
286 static void
287 from_combobox_changed_cb(GtkWidget *combo, MathConverter *converter)
289 GtkTreeModel *model;
290 GtkTreeIter iter;
291 UnitCategory *category;
292 Unit *unit;
293 const GList *unit_iter;
295 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
296 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter))
297 return;
298 gtk_tree_model_get(model, &iter, 1, &category, 2, &unit, -1);
300 /* Set the to combobox to be the list of units can be converted to */
301 model = GTK_TREE_MODEL(gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_OBJECT));
302 for (unit_iter = unit_category_get_units(category); unit_iter; unit_iter = unit_iter->next) {
303 Unit *u = unit_iter->data;
304 if (u == unit)
305 continue;
306 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
307 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, unit_get_display_name(u), 1, category, 2, u, -1);
309 gtk_combo_box_set_model(GTK_COMBO_BOX(converter->priv->to_combo), model);
311 /* Select the first possible unit */
312 gtk_combo_box_set_active(GTK_COMBO_BOX(converter->priv->to_combo), 0);
314 g_object_unref(category);
315 g_object_unref(unit);
319 static void
320 to_combobox_changed_cb(GtkWidget *combo, MathConverter *converter)
322 /* Conversion must have changed */
323 update_result_label(converter);
325 g_signal_emit(converter, signals[CHANGED], 0);
329 static void
330 from_cell_data_func(GtkCellLayout *cell_layout,
331 GtkCellRenderer *cell,
332 GtkTreeModel *tree_model,
333 GtkTreeIter *iter,
334 gpointer data)
336 g_object_set(cell, "sensitive", !gtk_tree_model_iter_has_child(tree_model, iter), NULL);
340 static void
341 currency_updated_cb(CurrencyManager *manager, MathConverter *converter)
343 update_result_label(converter);
347 static void
348 math_converter_init(MathConverter *converter)
350 GtkWidget *hbox, *label;
351 GtkCellRenderer *renderer;
353 converter->priv = G_TYPE_INSTANCE_GET_PRIVATE(converter, math_converter_get_type(), MathConverterPrivate);
355 gtk_box_set_spacing(GTK_BOX(converter), 6);
357 hbox = gtk_hbox_new(FALSE, 0);
358 gtk_widget_show(hbox);
359 gtk_box_pack_start(GTK_BOX(converter), hbox, FALSE, TRUE, 0);
361 converter->priv->from_combo = gtk_combo_box_new ();
363 renderer = gtk_cell_renderer_text_new();
364 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter->priv->from_combo), renderer, TRUE);
365 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter->priv->from_combo), renderer, "text", 0);
366 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(converter->priv->from_combo),
367 renderer,
368 from_cell_data_func,
369 NULL, NULL);
370 g_signal_connect(converter->priv->from_combo, "changed", G_CALLBACK(from_combobox_changed_cb), converter);
371 gtk_widget_show(converter->priv->from_combo);
372 gtk_box_pack_start(GTK_BOX(hbox), converter->priv->from_combo, FALSE, TRUE, 0);
374 label = gtk_label_new(/* Label that is displayed between the two conversion combo boxes, e.g. "[degrees] in [radians]" */
375 _(" in "));
376 gtk_widget_show(label);
377 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
379 converter->priv->to_combo = gtk_combo_box_new();
380 renderer = gtk_cell_renderer_text_new();
381 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter->priv->to_combo), renderer, TRUE);
382 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter->priv->to_combo), renderer, "text", 0);
383 g_signal_connect(converter->priv->to_combo, "changed", G_CALLBACK(to_combobox_changed_cb), converter);
384 gtk_widget_show(converter->priv->to_combo);
385 gtk_box_pack_start(GTK_BOX(hbox), converter->priv->to_combo, FALSE, TRUE, 0);
387 converter->priv->result_label = gtk_label_new("");
388 gtk_misc_set_alignment(GTK_MISC(converter->priv->result_label), 1.0, 0.5);
389 gtk_widget_set_sensitive(converter->priv->result_label, FALSE);
390 gtk_widget_show(converter->priv->result_label);
391 gtk_box_pack_start(GTK_BOX(converter), converter->priv->result_label, TRUE, TRUE, 0);
393 g_signal_connect(currency_manager_get_default(), "updated", G_CALLBACK(currency_updated_cb), converter);