1 #define __SP_UNIT_MENU_C__
3 /*
4 * Unit selector with autupdate capability
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 *
10 * Copyright (C) 2000-2002 Authors
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #define noUNIT_SELECTOR_VERBOSE
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
20 #include <gtk/gtksignal.h>
21 #include <gtk/gtkhbox.h>
22 #include <gtk/gtkmenu.h>
23 #include <gtk/gtkmenuitem.h>
24 #include "helper/sp-marshal.h"
25 #include "helper/units.h"
26 #include "helper/unit-menu.h"
27 #include "widgets/spw-utilities.h"
29 struct SPUnitSelector {
30 GtkHBox box;
32 GtkWidget *menu;
34 guint bases;
35 GSList *units;
36 SPUnit const *unit;
37 gdouble ctmscale;
38 guint plural : 1;
39 guint abbr : 1;
41 guint update : 1;
43 GSList *adjustments;
44 };
46 struct SPUnitSelectorClass {
47 GtkHBoxClass parent_class;
49 gboolean (* set_unit)(SPUnitSelector *us, SPUnit const *old, SPUnit const *new_unit);
50 };
52 enum {SET_UNIT, LAST_SIGNAL};
54 static void sp_unit_selector_class_init(SPUnitSelectorClass *klass);
55 static void sp_unit_selector_init(SPUnitSelector *selector);
56 static void sp_unit_selector_finalize(GObject *object);
58 static GtkHBoxClass *unit_selector_parent_class;
59 static guint signals[LAST_SIGNAL] = {0};
61 GType sp_unit_selector_get_type(void)
62 {
63 static GType type = 0;
64 if (!type) {
65 GTypeInfo info = {
66 sizeof(SPUnitSelectorClass),
67 0, // base_init
68 0, // base_finalize
69 (GClassInitFunc)sp_unit_selector_class_init,
70 0, // class_finalize
71 0, // class_data
72 sizeof(SPUnitSelector),
73 0, // n_preallocs
74 (GInstanceInitFunc)sp_unit_selector_init,
75 0 // value_table
76 };
77 type = g_type_register_static(GTK_TYPE_HBOX, "SPUnitSelector", &info, static_cast<GTypeFlags>(0));
78 }
79 return type;
80 }
82 static void
83 sp_unit_selector_class_init(SPUnitSelectorClass *klass)
84 {
85 GObjectClass *object_class;
86 GtkWidgetClass *widget_class;
88 object_class = G_OBJECT_CLASS(klass);
89 widget_class = GTK_WIDGET_CLASS(klass);
91 unit_selector_parent_class = (GtkHBoxClass*)gtk_type_class(GTK_TYPE_HBOX);
93 signals[SET_UNIT] = g_signal_new("set_unit",
94 G_TYPE_FROM_CLASS(klass),
95 G_SIGNAL_RUN_LAST,
96 G_STRUCT_OFFSET(SPUnitSelectorClass, set_unit),
97 NULL, NULL,
98 sp_marshal_BOOLEAN__POINTER_POINTER,
99 G_TYPE_BOOLEAN, 2,
100 G_TYPE_POINTER, G_TYPE_POINTER);
102 object_class->finalize = sp_unit_selector_finalize;
103 }
105 static void
106 sp_unit_selector_init(SPUnitSelector *us)
107 {
108 us->ctmscale = 1.0;
109 us->abbr = FALSE;
110 us->plural = TRUE;
112 us->menu = gtk_option_menu_new();
114 gtk_widget_show(us->menu);
115 gtk_box_pack_start(GTK_BOX(us), us->menu, TRUE, TRUE, 0);
116 }
118 static void
119 sp_unit_selector_finalize(GObject *object)
120 {
121 SPUnitSelector *selector = SP_UNIT_SELECTOR(object);
123 if (selector->menu) {
124 selector->menu = NULL;
125 }
127 while (selector->adjustments) {
128 gtk_object_unref(GTK_OBJECT(selector->adjustments->data));
129 selector->adjustments = g_slist_remove(selector->adjustments, selector->adjustments->data);
130 }
132 if (selector->units) {
133 sp_unit_free_list(selector->units);
134 }
136 selector->unit = NULL;
138 G_OBJECT_CLASS(unit_selector_parent_class)->finalize(object);
139 }
141 GtkWidget *
142 sp_unit_selector_new(guint bases)
143 {
144 SPUnitSelector *us = (SPUnitSelector*)gtk_type_new(SP_TYPE_UNIT_SELECTOR);
146 sp_unit_selector_set_bases(us, bases);
148 return (GtkWidget *) us;
149 }
151 void
152 sp_unit_selector_setsize(GtkWidget *us, guint w, guint h)
153 {
154 gtk_widget_set_size_request(((SPUnitSelector *) us)->menu, w, h);
155 }
157 SPUnit const *
158 sp_unit_selector_get_unit(SPUnitSelector const *us)
159 {
160 g_return_val_if_fail(us != NULL, NULL);
161 g_return_val_if_fail(SP_IS_UNIT_SELECTOR(us), NULL);
163 return us->unit;
164 }
166 static void
167 spus_unit_activate(GtkWidget *widget, SPUnitSelector *us)
168 {
169 SPUnit const *unit = (SPUnit const *) gtk_object_get_data(GTK_OBJECT(widget), "unit");
170 g_return_if_fail(unit != NULL);
172 #ifdef UNIT_SELECTOR_VERBOSE
173 g_print("Old unit %s new unit %s\n", us->unit->name, unit->name);
174 #endif
176 SPUnit const *old = us->unit;
177 us->unit = unit;
179 us->update = TRUE;
181 gboolean consumed = FALSE;
182 g_signal_emit(G_OBJECT(us), signals[SET_UNIT], 0, old, unit, &consumed);
184 if ( !consumed
185 && ( unit->base == old->base
186 || ( unit->base == SP_UNIT_ABSOLUTE && old->base == SP_UNIT_DEVICE )
187 || ( old->base == SP_UNIT_ABSOLUTE && unit->base == SP_UNIT_DEVICE ) ) ) {
188 // Either the same base, or absolute<->device:
189 /* Recalculate adjustments. */
190 for (GSList *l = us->adjustments; l != NULL; l = g_slist_next(l)) {
191 GtkAdjustment *adj = GTK_ADJUSTMENT(l->data);
192 gdouble val = adj->value;
193 #ifdef UNIT_SELECTOR_VERBOSE
194 g_print("Old val %g ... ", val);
195 #endif
196 val = sp_convert_distance_full(val, *old, *unit);
197 #ifdef UNIT_SELECTOR_VERBOSE
198 g_print("new val %g\n", val);
199 #endif
200 adj->value = val;
201 }
202 /* need to separate the value changing from the notification
203 * or else the unit changes can break the calculations */
204 for (GSList *l = us->adjustments; l != NULL; l = g_slist_next(l)) {
205 gtk_adjustment_value_changed(GTK_ADJUSTMENT(l->data));
206 }
207 } else if (!consumed && unit->base != old->base) {
208 /* when the base changes, signal all the adjustments to get them
209 * to recalculate */
210 for (GSList *l = us->adjustments; l != NULL; l = g_slist_next(l)) {
211 gtk_signal_emit_by_name(GTK_OBJECT(l->data), "value_changed");
212 }
213 }
215 us->update = FALSE;
216 }
218 static void
219 spus_rebuild_menu(SPUnitSelector *us)
220 {
221 if (GTK_OPTION_MENU(us->menu)->menu) {
222 gtk_option_menu_remove_menu(GTK_OPTION_MENU(us->menu));
223 }
225 GtkWidget *m = gtk_menu_new();
227 gtk_widget_show(m);
229 gint pos = 0;
230 gint p = 0;
231 for (GSList *l = us->units; l != NULL; l = l->next) {
232 SPUnit const *u = (SPUnit*)l->data;
234 // use only abbreviations in the menu
235 // i = gtk_menu_item_new_with_label((us->abbr) ? (us->plural) ? u->abbr_plural : u->abbr : (us->plural) ? u->plural : u->name);
236 GtkWidget *i = gtk_menu_item_new_with_label( u->abbr );
238 gtk_object_set_data(GTK_OBJECT(i), "unit", (gpointer) u);
239 gtk_signal_connect(GTK_OBJECT(i), "activate", GTK_SIGNAL_FUNC(spus_unit_activate), us);
241 sp_set_font_size_smaller (i);
243 gtk_widget_show(i);
245 gtk_menu_shell_append(GTK_MENU_SHELL(m), i);
246 if (u == us->unit) pos = p;
247 p += 1;
248 }
250 gtk_option_menu_set_menu(GTK_OPTION_MENU(us->menu), m);
252 gtk_option_menu_set_history(GTK_OPTION_MENU(us->menu), pos);
253 }
255 void
256 sp_unit_selector_set_bases(SPUnitSelector *us, guint bases)
257 {
258 g_return_if_fail(us != NULL);
259 g_return_if_fail(SP_IS_UNIT_SELECTOR(us));
261 if (bases == us->bases) return;
263 GSList *units = sp_unit_get_list(bases);
264 g_return_if_fail(units != NULL);
265 sp_unit_free_list(us->units);
266 us->units = units;
267 us->unit = (SPUnit*)units->data;
269 spus_rebuild_menu(us);
270 }
272 void
273 sp_unit_selector_add_unit(SPUnitSelector *us, SPUnit const *unit, int position)
274 {
275 if (!g_slist_find(us->units, (gpointer) unit)) {
276 us->units = g_slist_insert(us->units, (gpointer) unit, position);
278 spus_rebuild_menu(us);
279 }
280 }
282 void
283 sp_unit_selector_set_unit(SPUnitSelector *us, SPUnit const *unit)
284 {
285 g_return_if_fail(us != NULL);
286 g_return_if_fail(SP_IS_UNIT_SELECTOR(us));
288 if (unit == NULL) {
289 return; // silently return, by default a newly created selector uses pt
290 }
291 if (unit == us->unit) {
292 return;
293 }
295 gint const pos = g_slist_index(us->units, (gpointer) unit);
296 g_return_if_fail(pos >= 0);
298 gtk_option_menu_set_history(GTK_OPTION_MENU(us->menu), pos);
300 SPUnit const *old = us->unit;
301 us->unit = unit;
303 /* Recalculate adjustments */
304 for (GSList *l = us->adjustments; l != NULL; l = l->next) {
305 GtkAdjustment *adj = GTK_ADJUSTMENT(l->data);
306 gdouble const val = sp_convert_distance_full(adj->value, *old, *unit);
307 gtk_adjustment_set_value(adj, val);
308 }
309 }
311 void
312 sp_unit_selector_add_adjustment(SPUnitSelector *us, GtkAdjustment *adj)
313 {
314 g_return_if_fail(us != NULL);
315 g_return_if_fail(SP_IS_UNIT_SELECTOR(us));
316 g_return_if_fail(adj != NULL);
317 g_return_if_fail(GTK_IS_ADJUSTMENT(adj));
319 g_return_if_fail(!g_slist_find(us->adjustments, adj));
321 gtk_object_ref(GTK_OBJECT(adj));
322 us->adjustments = g_slist_prepend(us->adjustments, adj);
323 }
325 void
326 sp_unit_selector_remove_adjustment(SPUnitSelector *us, GtkAdjustment *adj)
327 {
328 g_return_if_fail(us != NULL);
329 g_return_if_fail(SP_IS_UNIT_SELECTOR(us));
330 g_return_if_fail(adj != NULL);
331 g_return_if_fail(GTK_IS_ADJUSTMENT(adj));
333 g_return_if_fail(g_slist_find(us->adjustments, adj));
335 us->adjustments = g_slist_remove(us->adjustments, adj);
336 gtk_object_unref(GTK_OBJECT(adj));
337 }
339 gboolean
340 sp_unit_selector_update_test(SPUnitSelector const *selector)
341 {
342 g_return_val_if_fail(selector != NULL, FALSE);
343 g_return_val_if_fail(SP_IS_UNIT_SELECTOR(selector), FALSE);
345 return selector->update;
346 }
348 double
349 sp_unit_selector_get_value_in_pixels(SPUnitSelector const *selector, GtkAdjustment *adj)
350 {
351 g_return_val_if_fail(selector != NULL, adj->value);
352 g_return_val_if_fail(SP_IS_UNIT_SELECTOR(selector), adj->value);
354 return sp_units_get_pixels(adj->value, *(selector->unit));
355 }
357 void
358 sp_unit_selector_set_value_in_pixels(SPUnitSelector *selector, GtkAdjustment *adj, double value)
359 {
360 g_return_if_fail(selector != NULL);
361 g_return_if_fail(SP_IS_UNIT_SELECTOR(selector));
363 gtk_adjustment_set_value(adj, sp_pixels_get_units(value, *(selector->unit)));
364 }
366 /*
367 Local Variables:
368 mode:c++
369 c-file-style:"stroustrup"
370 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
371 indent-tabs-mode:nil
372 fill-column:99
373 End:
374 */
375 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :