1 /*
2 * Inkscape::UnitTracker - Simple mediator to synchronize changes to a set
3 * of possible units
4 *
5 * Authors:
6 * Jon A. Cruz <jon@joncruz.org>
7 *
8 * Copyright (C) 2007 Jon A. Cruz
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include <gtk/gtkliststore.h>
15 #include "unit-tracker.h"
16 #include "ege-select-one-action.h"
18 namespace Inkscape {
20 enum {
21 COLUMN_STRING,
22 COLUMN_SPUNIT,
23 N_COLUMNS
24 };
26 UnitTracker::UnitTracker( guint bases ) :
27 _active(0),
28 _isUpdating(false),
29 _activeUnit(0),
30 _store(0),
31 _unitList(0),
32 _actionList(0),
33 _adjList(0),
34 _priorValues()
35 {
36 _store = gtk_list_store_new( N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER );
37 setBase( bases );
38 }
40 UnitTracker::~UnitTracker()
41 {
42 if ( _unitList ) {
43 sp_unit_free_list( _unitList );
44 }
46 // Unhook weak references to GtkActions
47 while ( _actionList ) {
48 g_signal_handlers_disconnect_by_func( G_OBJECT(_actionList->data), (gpointer)_unitChangedCB, this );
49 g_object_weak_unref( G_OBJECT(_actionList->data), _actionFinalizedCB, this );
50 _actionList = g_slist_delete_link( _actionList, _actionList );
51 }
53 // Unhook wek references to GtkAdjustments
54 while ( _adjList ) {
55 g_object_weak_unref( G_OBJECT(_adjList->data), _adjustmentFinalizedCB, this );
56 _adjList = g_slist_delete_link( _adjList, _adjList );
57 }
58 }
60 void UnitTracker::setBase( guint bases )
61 {
62 GtkTreeIter iter;
63 _unitList = sp_unit_get_list( bases );
64 for ( GSList* cur = _unitList; cur; cur = g_slist_next(cur) ) {
65 SPUnit* unit = static_cast<SPUnit*>(cur->data);
66 gtk_list_store_append( _store, &iter );
67 gtk_list_store_set( _store, &iter, COLUMN_STRING, unit->abbr, COLUMN_SPUNIT, unit, -1 );
68 }
69 gint count = gtk_tree_model_iter_n_children( GTK_TREE_MODEL(_store), 0 );
70 if ( (count > 0) && (_active > count) ) {
71 _setActive( count - 1 );
72 } else {
73 _setActive( _active );
74 }
75 }
77 void UnitTracker::addUnit( SPUnitId id, gint index )
78 {
79 GtkTreeIter iter;
80 const SPUnit* percentUnit = &sp_unit_get_by_id( id );
81 gtk_list_store_insert( _store, &iter, index );
82 gtk_list_store_set( _store, &iter, COLUMN_STRING, percentUnit->abbr, COLUMN_SPUNIT, percentUnit, -1 );
83 }
85 bool UnitTracker::isUpdating() const
86 {
87 return _isUpdating;
88 }
90 SPUnit const* UnitTracker::getActiveUnit() const
91 {
92 return _activeUnit;
93 }
95 void UnitTracker::setActiveUnit( SPUnit const *unit )
96 {
97 if ( unit ) {
98 GtkTreeIter iter;
99 int index = 0;
100 gboolean found = gtk_tree_model_get_iter_first( GTK_TREE_MODEL(_store), &iter );
101 while ( found ) {
102 SPUnit* storedUnit = 0;
103 gtk_tree_model_get( GTK_TREE_MODEL(_store), &iter, COLUMN_SPUNIT, &storedUnit, -1 );
104 if ( storedUnit && (storedUnit->unit_id == unit->unit_id) ) {
105 _setActive(index);
106 break;
107 }
109 found = gtk_tree_model_iter_next( GTK_TREE_MODEL(_store), &iter );
110 index++;
111 }
112 }
113 }
115 void UnitTracker::addAdjustment( GtkAdjustment* adj )
116 {
117 if ( !g_slist_find( _adjList, adj ) ) {
118 g_object_weak_ref( G_OBJECT(adj), _adjustmentFinalizedCB, this );
119 _adjList = g_slist_append( _adjList, adj );
120 }
121 }
123 void UnitTracker::setFullVal( GtkAdjustment* adj, gdouble val )
124 {
125 _priorValues[adj] = val;
126 }
128 GtkAction* UnitTracker::createAction( gchar const* name, gchar const* label, gchar const* tooltip )
129 {
130 EgeSelectOneAction* act1 = ege_select_one_action_new( name, label, tooltip, NULL, GTK_TREE_MODEL(_store) );
131 ege_select_one_action_set_label_column( act1, COLUMN_STRING );
132 if ( _active ) {
133 ege_select_one_action_set_active( act1, _active );
134 }
136 ege_select_one_action_set_appearance( act1, "minimal" );
137 g_object_weak_ref( G_OBJECT(act1), _actionFinalizedCB, this );
138 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK( _unitChangedCB ), this );
139 _actionList = g_slist_append( _actionList, act1 );
141 return GTK_ACTION(act1);
142 }
144 void UnitTracker::_unitChangedCB( GtkAction* action, gpointer data )
145 {
146 if ( action && data ) {
147 EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION(action);
148 gint active = ege_select_one_action_get_active( act );
149 UnitTracker* self = reinterpret_cast<UnitTracker*>(data);
150 self->_setActive(active);
151 }
152 }
154 void UnitTracker::_actionFinalizedCB( gpointer data, GObject *where_the_object_was )
155 {
156 if ( data && where_the_object_was ) {
157 UnitTracker* self = reinterpret_cast<UnitTracker*>(data);
158 self->_actionFinalized( where_the_object_was );
159 }
160 }
162 void UnitTracker::_adjustmentFinalizedCB( gpointer data, GObject *where_the_object_was )
163 {
164 if ( data && where_the_object_was ) {
165 UnitTracker* self = reinterpret_cast<UnitTracker*>(data);
166 self->_adjustmentFinalized( where_the_object_was );
167 }
168 }
170 void UnitTracker::_actionFinalized( GObject *where_the_object_was )
171 {
172 GSList* target = g_slist_find( _actionList, where_the_object_was );
173 if ( target ) {
174 _actionList = g_slist_remove( _actionList, where_the_object_was );
175 } else {
176 g_warning("Received a finalization callback for unknown object %p", where_the_object_was );
177 }
178 }
180 void UnitTracker::_adjustmentFinalized( GObject *where_the_object_was )
181 {
182 GSList* target = g_slist_find( _adjList, where_the_object_was );
183 if ( target ) {
184 _adjList = g_slist_remove( _adjList, where_the_object_was );
185 } else {
186 g_warning("Received a finalization callback for unknown object %p", where_the_object_was );
187 }
188 }
190 void UnitTracker::_setActive( gint active )
191 {
192 if ( active != _active || (_activeUnit == 0) ) {
193 gint oldActive = _active;
195 GtkTreeIter iter;
196 gboolean found = gtk_tree_model_iter_nth_child( GTK_TREE_MODEL(_store), &iter, NULL, oldActive );
197 if ( found ) {
198 SPUnit* unit = 0;
199 gtk_tree_model_get( GTK_TREE_MODEL(_store), &iter, COLUMN_SPUNIT, &unit, -1 );
201 found = gtk_tree_model_iter_nth_child( GTK_TREE_MODEL(_store), &iter, NULL, active );
202 if ( found ) {
203 SPUnit* newUnit = 0;
204 gtk_tree_model_get( GTK_TREE_MODEL(_store), &iter, COLUMN_SPUNIT, &newUnit, -1 );
205 _activeUnit = newUnit;
207 if ( _adjList ) {
208 _fixupAdjustments( unit, newUnit );
209 }
211 } else {
212 g_warning("Did not find new unit");
213 }
214 } else {
215 g_warning("Did not find old unit");
216 }
218 _active = active;
220 for ( GSList* cur = _actionList; cur; cur = g_slist_next(cur) ) {
221 if ( IS_EGE_SELECT_ONE_ACTION( cur->data ) ) {
222 EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION( cur->data );
223 ege_select_one_action_set_active( act, active );
224 }
225 }
226 }
227 }
229 void UnitTracker::_fixupAdjustments( SPUnit const* oldUnit, SPUnit const *newUnit )
230 {
231 _isUpdating = true;
232 for ( GSList* cur = _adjList; cur; cur = g_slist_next(cur) ) {
233 GtkAdjustment* adj = GTK_ADJUSTMENT(cur->data);
234 gdouble oldVal = gtk_adjustment_get_value(adj);
235 gdouble val = oldVal;
237 if ((oldUnit->base == SP_UNIT_ABSOLUTE || oldUnit->base == SP_UNIT_DEVICE)
238 && (newUnit->base == SP_UNIT_DIMENSIONLESS))
239 {
240 val = 1.0 / newUnit->unittobase;
241 _priorValues[adj] = sp_units_get_pixels( oldVal, *oldUnit );
242 } else if ((oldUnit->base == SP_UNIT_DIMENSIONLESS)
243 && (newUnit->base == SP_UNIT_ABSOLUTE || newUnit->base == SP_UNIT_DEVICE)) {
244 if ( _priorValues.find(adj) != _priorValues.end() ) {
245 val = sp_pixels_get_units( _priorValues[adj], *newUnit );
246 }
247 } else {
248 val = sp_convert_distance_full( oldVal, *oldUnit, *newUnit );
249 }
251 gtk_adjustment_set_value( adj, val );
252 }
253 _isUpdating = false;
254 }
256 }
258 /*
259 Local Variables:
260 mode:c++
261 c-file-style:"stroustrup"
262 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
263 indent-tabs-mode:nil
264 fill-column:99
265 End:
266 */
267 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :