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)
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
19 #include <glib/gi18n.h>
21 #include "math-converter.h"
22 #include "mp-serializer.h"
23 #include "unit-manager.h"
31 struct MathConverterPrivate
33 MathEquation
*equation
;
35 GtkWidget
*from_combo
;
38 GtkWidget
*result_label
;
39 MpSerializer
*serializer
;
50 struct Unit units
[MAX_UNITS
];
53 static struct UnitCategory categories
[] = {
56 {N_("Degrees"), "degrees"},
58 {N_("Radians"), "radians"},
60 {N_("Gradians"), "gradians"},
64 {N_("Parsecs"), "parsecs"},
66 {N_("Light Years"), "lightyears"},
68 {N_("Astronomical Units"), "au"},
70 {N_("Nautical Miles"), "nm"},
72 {N_("Miles"), "miles"},
74 {N_("Kilometers"), "kilometers"},
76 {N_("Cables"), "cables"},
78 {N_("Fathoms"), "fathoms"},
80 {N_("Meters"), "meters"},
82 {N_("Yards"), "yards"},
86 {N_("Inches"), "inches"},
88 {N_("Centimeters"), "centimeters"},
90 {N_("Millimeters"), "millimeters"},
92 {N_("Micrometers"), "micrometers"},
94 {N_("Nanometers"), "nanometers"},
98 {N_("Hectares"), "hectares"},
100 {N_("Acres"), "acres"},
112 {N_("Gallons"), "gallons"},
114 {N_("Liters"), "liters"},
116 {N_("Quarts"), "quarts"},
118 {N_("Pints"), "pints"},
120 {N_("Milliliters"), "milliliters"},
128 {N_("Tonnes"), "tonnes"},
130 {N_("Kilograms"), "kilograms"},
132 {N_("Pounds"), "pounds"},
134 {N_("Ounces"), "ounces"},
136 {N_("Grams"), "grams"},
140 {N_("Years"), "years"},
142 {N_("Days"), "days"},
144 {N_("Hours"), "hours"},
146 {N_("Minutes"), "minutes"},
148 {N_("Seconds"), "seconds"},
150 {N_("Milliseconds"), "milliseconds"},
152 {N_("Microseconds"), "microseconds"},
156 G_DEFINE_TYPE (MathConverter
, math_converter
, GTK_TYPE_HBOX
);
160 math_converter_new(MathEquation
*equation
)
162 return g_object_new(math_converter_get_type(), "equation", equation
, NULL
);
167 math_converter_set_category(MathEquation
*equation
, const gchar
*category
)
173 math_converter_get_category(MathEquation
*equation
)
180 update_result_label(MathConverter
*converter
)
185 const gchar
*source_units
, *target_units
;
186 char *source_value
, *target_value
;
188 if (!converter
->priv
->result_label
)
191 enabled
= math_equation_get_number(converter
->priv
->equation
, &x
);
193 source_units
= math_equation_get_source_units(converter
->priv
->equation
);
194 target_units
= math_equation_get_target_units(converter
->priv
->equation
);
195 if (!source_units
|| !target_units
)
197 else if (!unit_manager_convert(math_equation_get_unit_manager(converter
->priv
->equation
), &x
, source_units
, target_units
, &z
)) {
198 if (!currency_convert(&x
, source_units
, target_units
, &z
))
202 gtk_widget_set_sensitive(converter
->priv
->result_label
, enabled
);
206 source_value
= mp_serializer_to_string(converter
->priv
->serializer
, &x
);
207 target_value
= mp_serializer_to_string(converter
->priv
->serializer
, &z
);
209 // FIXME: Use currency symbols for currency
210 label
= g_strdup_printf("%s %s = %s %s", source_value
, source_units
, target_value
, target_units
);
211 gtk_label_set_text(GTK_LABEL(converter
->priv
->result_label
), label
);
212 g_free(source_value
);
213 g_free(target_value
);
219 source_units_changed_cb(MathEquation
*equation
, GParamSpec
*spec
, MathConverter
*converter
)
224 model
= gtk_combo_box_get_model(GTK_COMBO_BOX(converter
->priv
->from_combo
));
225 if (!gtk_tree_model_get_iter_first(model
, &iter
))
228 GtkTreeIter child_iter
;
230 if (gtk_tree_model_iter_children(model
, &child_iter
, &iter
)) {
234 gtk_tree_model_get(model
, &child_iter
, 1, &i
, 2, &j
, -1);
235 if ((i
== -1 && strcmp(currency_info
[j
].short_name
, math_equation_get_source_units(equation
)) == 0) ||
236 (i
>= 0 && strcmp(categories
[i
].units
[j
].internal_name
, math_equation_get_source_units(equation
)) == 0)) {
237 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(converter
->priv
->from_combo
), &child_iter
);
238 update_result_label(converter
);
241 } while (gtk_tree_model_iter_next(model
, &child_iter
));
243 } while (gtk_tree_model_iter_next(model
, &iter
));
248 target_units_changed_cb(MathEquation
*equation
, GParamSpec
*spec
, MathConverter
*converter
)
253 model
= gtk_combo_box_get_model(GTK_COMBO_BOX(converter
->priv
->to_combo
));
254 if (!gtk_tree_model_get_iter_first(model
, &iter
))
259 gtk_tree_model_get(model
, &iter
, 1, &i
, 2, &j
, -1);
260 if ((i
== -1 && strcmp(currency_info
[j
].short_name
, math_equation_get_source_units(equation
)) == 0) ||
261 (i
>= 0 && strcmp(categories
[i
].units
[j
].internal_name
, math_equation_get_source_units(equation
)) == 0)) {
262 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(converter
->priv
->to_combo
), &iter
);
263 update_result_label(converter
);
266 } while (gtk_tree_model_iter_next(model
, &iter
));
271 display_changed_cb(MathEquation
*equation
, GParamSpec
*spec
, MathConverter
*converter
)
273 update_result_label(converter
);
278 math_converter_set_property(GObject
*object
,
285 self
= MATH_CONVERTER(object
);
289 self
->priv
->equation
= g_value_get_object(value
);
290 g_signal_connect(self
->priv
->equation
, "notify::source-units", G_CALLBACK(source_units_changed_cb
), self
);
291 g_signal_connect(self
->priv
->equation
, "notify::target-units", G_CALLBACK(target_units_changed_cb
), self
);
292 g_signal_connect(self
->priv
->equation
, "notify::display", G_CALLBACK(display_changed_cb
), self
);
293 source_units_changed_cb(self
->priv
->equation
, NULL
, self
);
294 target_units_changed_cb(self
->priv
->equation
, NULL
, self
);
295 display_changed_cb(self
->priv
->equation
, NULL
, self
);
298 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
305 math_converter_get_property(GObject
*object
,
312 self
= MATH_CONVERTER(object
);
316 g_value_set_object(value
, self
->priv
->equation
);
319 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
326 math_converter_class_init(MathConverterClass
*klass
)
328 GObjectClass
*object_class
= G_OBJECT_CLASS(klass
);
330 object_class
->get_property
= math_converter_get_property
;
331 object_class
->set_property
= math_converter_set_property
;
333 g_type_class_add_private(klass
, sizeof(MathConverterPrivate
));
335 g_object_class_install_property(object_class
,
337 g_param_spec_object("equation",
339 "Equation being controlled",
340 math_equation_get_type(),
341 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
346 from_combobox_changed_cb(GtkWidget
*combo
, MathConverter
*converter
)
350 int category_index
, unit_index
;
351 const gchar
*unit_name
;
353 model
= gtk_combo_box_get_model(GTK_COMBO_BOX(combo
));
354 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo
), &iter
);
355 gtk_tree_model_get(model
, &iter
, 1, &category_index
, 2, &unit_index
, -1);
357 model
= GTK_TREE_MODEL(gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_INT
));
359 if (category_index
== -1) {
361 for (i
= 0; currency_info
[i
].short_name
!= NULL
; i
++) {
364 gtk_list_store_append(GTK_LIST_STORE(model
), &iter
);
365 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
, 0, currency_info
[i
].short_name
, 1, -1, 2, i
, -1);
367 unit_name
= currency_info
[unit_index
].short_name
;
371 for (i
= 0; categories
[category_index
].units
[i
].ui_name
!= NULL
; i
++) {
374 gtk_list_store_append(GTK_LIST_STORE(model
), &iter
);
375 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
, 0, _(categories
[category_index
].units
[i
].ui_name
), 1, category_index
, 2, i
, -1);
377 unit_name
= categories
[category_index
].units
[unit_index
].internal_name
;
380 gtk_combo_box_set_model(GTK_COMBO_BOX(converter
->priv
->to_combo
), model
);
382 math_equation_set_source_units(converter
->priv
->equation
, unit_name
);
384 gtk_combo_box_set_active(GTK_COMBO_BOX(converter
->priv
->to_combo
), 0);
389 to_combobox_changed_cb(GtkWidget
*combo
, MathConverter
*converter
)
393 int category_index
, unit_index
;
395 model
= gtk_combo_box_get_model(GTK_COMBO_BOX(combo
));
396 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo
), &iter
);
397 gtk_tree_model_get(model
, &iter
, 1, &category_index
, 2, &unit_index
, -1);
398 if (category_index
== -1)
399 math_equation_set_target_units(converter
->priv
->equation
, currency_info
[unit_index
].short_name
);
401 math_equation_set_target_units(converter
->priv
->equation
, categories
[category_index
].units
[unit_index
].internal_name
);
406 from_cell_data_func(GtkCellLayout
*cell_layout
,
407 GtkCellRenderer
*cell
,
408 GtkTreeModel
*tree_model
,
412 g_object_set(cell
, "sensitive", !gtk_tree_model_iter_has_child(tree_model
, iter
), NULL
);
417 math_converter_init(MathConverter
*converter
)
419 GtkWidget
*hbox
, *label
;
420 GtkTreeStore
*from_model
;
422 GtkCellRenderer
*renderer
;
425 converter
->priv
= G_TYPE_INSTANCE_GET_PRIVATE(converter
, math_converter_get_type(), MathConverterPrivate
);
427 gtk_box_set_spacing(GTK_BOX(converter
), 6);
429 hbox
= gtk_hbox_new(FALSE
, 0);
430 gtk_widget_show(hbox
);
431 gtk_box_pack_start(GTK_BOX(converter
), hbox
, FALSE
, TRUE
, 0);
433 converter
->priv
->from_combo
= gtk_combo_box_new ();
434 from_model
= gtk_tree_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_INT
);
435 gtk_combo_box_set_model(GTK_COMBO_BOX(converter
->priv
->from_combo
), GTK_TREE_MODEL(from_model
));
437 for (i
= 0; i
< sizeof(categories
) / sizeof(categories
[0]); i
++) {
438 gtk_tree_store_append(from_model
, &parent
, NULL
);
439 gtk_tree_store_set(from_model
, &parent
, 0, _(categories
[i
].name
), 1, i
, -1);
440 for (j
= 0; categories
[i
].units
[j
].ui_name
!= NULL
; j
++) {
443 gtk_tree_store_append(from_model
, &iter
, &parent
);
444 gtk_tree_store_set(from_model
, &iter
, 0, _(categories
[i
].units
[j
].ui_name
), 1, i
, 2, j
, -1);
448 gtk_tree_store_append(from_model
, &parent
, NULL
);
449 gtk_tree_store_set(from_model
, &parent
, 0, _("Currency"), 1, i
, -1);
450 for (i
= 0; currency_info
[i
].short_name
!= NULL
; i
++) {
453 gtk_tree_store_append(from_model
, &iter
, &parent
);
454 gtk_tree_store_set(from_model
, &iter
, 0, currency_info
[i
].short_name
, 1, -1, 2, i
, -1);
457 renderer
= gtk_cell_renderer_text_new();
458 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter
->priv
->from_combo
), renderer
, TRUE
);
459 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter
->priv
->from_combo
), renderer
, "text", 0);
460 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(converter
->priv
->from_combo
),
464 g_signal_connect(converter
->priv
->from_combo
, "changed", G_CALLBACK(from_combobox_changed_cb
), converter
);
465 gtk_widget_show(converter
->priv
->from_combo
);
466 gtk_box_pack_start(GTK_BOX(hbox
), converter
->priv
->from_combo
, FALSE
, TRUE
, 0);
468 label
= gtk_label_new(/* Label that is displayed between the two conversion combo boxes, e.g. "[degrees] in [radians]" */
470 gtk_widget_show(label
);
471 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 0);
473 converter
->priv
->to_combo
= gtk_combo_box_new();
474 renderer
= gtk_cell_renderer_text_new();
475 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(converter
->priv
->to_combo
), renderer
, TRUE
);
476 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(converter
->priv
->to_combo
), renderer
, "text", 0);
477 g_signal_connect(converter
->priv
->to_combo
, "changed", G_CALLBACK(to_combobox_changed_cb
), converter
);
478 gtk_widget_show(converter
->priv
->to_combo
);
479 gtk_box_pack_start(GTK_BOX(hbox
), converter
->priv
->to_combo
, FALSE
, TRUE
, 0);
481 converter
->priv
->result_label
= gtk_label_new("");
482 gtk_misc_set_alignment(GTK_MISC(converter
->priv
->result_label
), 1.0, 0.5);
483 gtk_widget_set_sensitive(converter
->priv
->result_label
, FALSE
);
484 gtk_widget_show(converter
->priv
->result_label
);
485 gtk_box_pack_start(GTK_BOX(converter
), converter
->priv
->result_label
, TRUE
, TRUE
, 0);
487 converter
->priv
->serializer
= mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC
, 10, 2);