/* * Inkscape::UnitTracker - Simple mediator to synchronize changes to a set * of possible units * * Authors: * Jon A. Cruz * * Copyright (C) 2007 Jon A. Cruz * * Released under GNU GPL, read the file 'COPYING' for more information */ #include #include "unit-tracker.h" #include "ege-select-one-action.h" namespace Inkscape { enum { COLUMN_STRING, COLUMN_SPUNIT, N_COLUMNS }; UnitTracker::UnitTracker( guint bases ) : _active(0), _isUpdating(false), _activeUnit(0), _store(0), _unitList(0), _actionList(0), _adjList(0), _priorValues() { _store = gtk_list_store_new( N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER ); setBase( bases ); } UnitTracker::~UnitTracker() { if ( _unitList ) { sp_unit_free_list( _unitList ); } // Unhook weak references to GtkActions while ( _actionList ) { g_signal_handlers_disconnect_by_func( G_OBJECT(_actionList->data), (gpointer)_unitChangedCB, this ); g_object_weak_unref( G_OBJECT(_actionList->data), _actionFinalizedCB, this ); _actionList = g_slist_delete_link( _actionList, _actionList ); } // Unhook wek references to GtkAdjustments while ( _adjList ) { g_object_weak_unref( G_OBJECT(_adjList->data), _adjustmentFinalizedCB, this ); _adjList = g_slist_delete_link( _adjList, _adjList ); } } void UnitTracker::setBase( guint bases ) { GtkTreeIter iter; _unitList = sp_unit_get_list( bases ); for ( GSList* cur = _unitList; cur; cur = g_slist_next(cur) ) { SPUnit* unit = static_cast(cur->data); gtk_list_store_append( _store, &iter ); gtk_list_store_set( _store, &iter, COLUMN_STRING, unit->abbr, COLUMN_SPUNIT, unit, -1 ); } gint count = gtk_tree_model_iter_n_children( GTK_TREE_MODEL(_store), 0 ); if ( (count > 0) && (_active > count) ) { _setActive( count - 1 ); } else { _setActive( _active ); } } void UnitTracker::addUnit( SPUnitId id, gint index ) { GtkTreeIter iter; const SPUnit* percentUnit = &sp_unit_get_by_id( id ); gtk_list_store_insert( _store, &iter, index ); gtk_list_store_set( _store, &iter, COLUMN_STRING, percentUnit->abbr, COLUMN_SPUNIT, percentUnit, -1 ); } bool UnitTracker::isUpdating() const { return _isUpdating; } SPUnit const* UnitTracker::getActiveUnit() const { return _activeUnit; } void UnitTracker::setActiveUnit( SPUnit const *unit ) { if ( unit ) { GtkTreeIter iter; int index = 0; gboolean found = gtk_tree_model_get_iter_first( GTK_TREE_MODEL(_store), &iter ); while ( found ) { SPUnit* storedUnit = 0; gtk_tree_model_get( GTK_TREE_MODEL(_store), &iter, COLUMN_SPUNIT, &storedUnit, -1 ); if ( storedUnit && (storedUnit->unit_id == unit->unit_id) ) { _setActive(index); break; } found = gtk_tree_model_iter_next( GTK_TREE_MODEL(_store), &iter ); index++; } } } void UnitTracker::addAdjustment( GtkAdjustment* adj ) { if ( !g_slist_find( _adjList, adj ) ) { g_object_weak_ref( G_OBJECT(adj), _adjustmentFinalizedCB, this ); _adjList = g_slist_append( _adjList, adj ); } } void UnitTracker::setFullVal( GtkAdjustment* adj, gdouble val ) { _priorValues[adj] = val; } GtkAction* UnitTracker::createAction( gchar const* name, gchar const* label, gchar const* tooltip ) { EgeSelectOneAction* act1 = ege_select_one_action_new( name, label, tooltip, NULL, GTK_TREE_MODEL(_store) ); ege_select_one_action_set_label_column( act1, COLUMN_STRING ); if ( _active ) { ege_select_one_action_set_active( act1, _active ); } ege_select_one_action_set_appearance( act1, "minimal" ); g_object_weak_ref( G_OBJECT(act1), _actionFinalizedCB, this ); g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK( _unitChangedCB ), this ); _actionList = g_slist_append( _actionList, act1 ); return GTK_ACTION(act1); } void UnitTracker::_unitChangedCB( GtkAction* action, gpointer data ) { if ( action && data ) { EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION(action); gint active = ege_select_one_action_get_active( act ); UnitTracker* self = reinterpret_cast(data); self->_setActive(active); } } void UnitTracker::_actionFinalizedCB( gpointer data, GObject *where_the_object_was ) { if ( data && where_the_object_was ) { UnitTracker* self = reinterpret_cast(data); self->_actionFinalized( where_the_object_was ); } } void UnitTracker::_adjustmentFinalizedCB( gpointer data, GObject *where_the_object_was ) { if ( data && where_the_object_was ) { UnitTracker* self = reinterpret_cast(data); self->_adjustmentFinalized( where_the_object_was ); } } void UnitTracker::_actionFinalized( GObject *where_the_object_was ) { GSList* target = g_slist_find( _actionList, where_the_object_was ); if ( target ) { _actionList = g_slist_remove( _actionList, where_the_object_was ); } else { g_warning("Received a finalization callback for unknown object %p", where_the_object_was ); } } void UnitTracker::_adjustmentFinalized( GObject *where_the_object_was ) { GSList* target = g_slist_find( _adjList, where_the_object_was ); if ( target ) { _adjList = g_slist_remove( _adjList, where_the_object_was ); } else { g_warning("Received a finalization callback for unknown object %p", where_the_object_was ); } } void UnitTracker::_setActive( gint active ) { if ( active != _active || (_activeUnit == 0) ) { gint oldActive = _active; GtkTreeIter iter; gboolean found = gtk_tree_model_iter_nth_child( GTK_TREE_MODEL(_store), &iter, NULL, oldActive ); if ( found ) { SPUnit* unit = 0; gtk_tree_model_get( GTK_TREE_MODEL(_store), &iter, COLUMN_SPUNIT, &unit, -1 ); found = gtk_tree_model_iter_nth_child( GTK_TREE_MODEL(_store), &iter, NULL, active ); if ( found ) { SPUnit* newUnit = 0; gtk_tree_model_get( GTK_TREE_MODEL(_store), &iter, COLUMN_SPUNIT, &newUnit, -1 ); _activeUnit = newUnit; if ( _adjList ) { _fixupAdjustments( unit, newUnit ); } } else { g_warning("Did not find new unit"); } } else { g_warning("Did not find old unit"); } _active = active; for ( GSList* cur = _actionList; cur; cur = g_slist_next(cur) ) { if ( IS_EGE_SELECT_ONE_ACTION( cur->data ) ) { EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION( cur->data ); ege_select_one_action_set_active( act, active ); } } } } void UnitTracker::_fixupAdjustments( SPUnit const* oldUnit, SPUnit const *newUnit ) { _isUpdating = true; for ( GSList* cur = _adjList; cur; cur = g_slist_next(cur) ) { GtkAdjustment* adj = GTK_ADJUSTMENT(cur->data); gdouble oldVal = gtk_adjustment_get_value(adj); gdouble val = oldVal; if ((oldUnit->base == SP_UNIT_ABSOLUTE || oldUnit->base == SP_UNIT_DEVICE) && (newUnit->base == SP_UNIT_DIMENSIONLESS)) { val = 1.0 / newUnit->unittobase; _priorValues[adj] = sp_units_get_pixels( oldVal, *oldUnit ); } else if ((oldUnit->base == SP_UNIT_DIMENSIONLESS) && (newUnit->base == SP_UNIT_ABSOLUTE || newUnit->base == SP_UNIT_DEVICE)) { if ( _priorValues.find(adj) != _priorValues.end() ) { val = sp_pixels_get_units( _priorValues[adj], *newUnit ); } } else { val = sp_convert_distance_full( oldVal, *oldUnit, *newUnit ); } gtk_adjustment_set_value( adj, val ); } _isUpdating = false; } } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :