From: joncruz Date: Thu, 5 Apr 2007 00:35:22 +0000 (+0000) Subject: Replacing old multifunction widget with separate widget & model X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=09e25b703eec45330206583138907e878579c1d2;p=inkscape.git Replacing old multifunction widget with separate widget & model --- diff --git a/src/Makefile_insert b/src/Makefile_insert index edf63b7b8..77bd4e7e5 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -275,6 +275,8 @@ libinkpost_a_SOURCES = \ ege-adjustment-action.h \ ege-output-action.cpp \ ege-output-action.h \ + ege-select-one-action.cpp \ + ege-select-one-action.h \ fill-or-stroke.h \ filter-chemistry.cpp filter-chemistry.h \ fixes.cpp \ diff --git a/src/ege-select-one-action.cpp b/src/ege-select-one-action.cpp new file mode 100644 index 000000000..b059308c5 --- /dev/null +++ b/src/ege-select-one-action.cpp @@ -0,0 +1,407 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is EGE Select One Action. + * + * The Initial Developer of the Original Code is + * Jon A. Cruz. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* Note: this file should be kept compilable as both .cpp and .c */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ege-select-one-action.h" + +enum { + CHANGED = 0, + LAST_SIGNAL}; + + +static void ege_select_one_action_class_init( EgeSelectOneActionClass* klass ); +static void ege_select_one_action_init( EgeSelectOneAction* action ); +static void ege_select_one_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ); +static void ege_select_one_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ); + +static void resync_active( EgeSelectOneAction* act, gint active ); +static void combo_changed_cb( GtkComboBox* widget, gpointer user_data ); +static void menu_toggled_cb( GtkWidget* obj, gpointer data ); + +static GtkWidget* create_menu_item( GtkAction* action ); +static GtkWidget* create_tool_item( GtkAction* action ); +static void connect_proxy( GtkAction *action, GtkWidget *proxy ); +static void disconnect_proxy( GtkAction *action, GtkWidget *proxy ); + +static GtkActionClass* gParentClass = 0; +static guint signals[LAST_SIGNAL] = {0}; +static GQuark gDataName = 0; + + +struct _EgeSelectOneActionPrivate +{ + GtkTreeModel* model; + gint active; + gint column; +}; + +#define EGE_SELECT_ONE_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), EGE_SELECT_ONE_ACTION_TYPE, EgeSelectOneActionPrivate ) ) + +enum { + PROP_MODEL = 1, + PROP_ACTIVE, + PROP_COLUMN +}; + +GType ege_select_one_action_get_type( void ) +{ + static GType myType = 0; + if ( !myType ) { + static const GTypeInfo myInfo = { + sizeof( EgeSelectOneActionClass ), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)ege_select_one_action_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof( EgeSelectOneAction ), + 0, /* n_preallocs */ + (GInstanceInitFunc)ege_select_one_action_init, + NULL + }; + + myType = g_type_register_static( GTK_TYPE_ACTION, "EgeSelectOneAction", &myInfo, (GTypeFlags)0 ); + } + + return myType; +} + +void ege_select_one_action_class_init( EgeSelectOneActionClass* klass ) +{ + if ( klass ) { + gParentClass = GTK_ACTION_CLASS( g_type_class_peek_parent( klass ) ); + GObjectClass* objClass = G_OBJECT_CLASS( klass ); + + gDataName = g_quark_from_string("ege-select1-action"); + + objClass->get_property = ege_select_one_action_get_property; + objClass->set_property = ege_select_one_action_set_property; + + klass->parent_class.create_menu_item = create_menu_item; + klass->parent_class.create_tool_item = create_tool_item; + klass->parent_class.connect_proxy = connect_proxy; + klass->parent_class.disconnect_proxy = disconnect_proxy; + + g_object_class_install_property( objClass, + PROP_MODEL, + g_param_spec_object( "model", + "Tree Model", + "Tree model of possible items", + GTK_TYPE_TREE_MODEL, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_ACTIVE, + g_param_spec_int( "active", + "Active Selection", + "The index of the selected item", + 0, 20, 0, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_COLUMN, + g_param_spec_int( "column", + "Display Column", + "The column of the model that holds display strings", + 0, 20, 0, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + signals[CHANGED] = g_signal_new( "changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(EgeSelectOneActionClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private( klass, sizeof(EgeSelectOneActionClass) ); + } +} + + +void ege_select_one_action_init( EgeSelectOneAction* action ) +{ + action->private_data = EGE_SELECT_ONE_ACTION_GET_PRIVATE( action ); + action->private_data->model = 0; + +/* g_signal_connect( action, "notify", G_CALLBACK( fixup_labels ), NULL ); */ +} + +EgeSelectOneAction* ege_select_one_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id, + GtkTreeModel* model ) +{ + GObject* obj = (GObject*)g_object_new( EGE_SELECT_ONE_ACTION_TYPE, + "name", name, + "label", label, + "tooltip", tooltip, + "stock_id", stock_id, + "model", model, + "active", 0, + NULL ); + + EgeSelectOneAction* action = EGE_SELECT_ONE_ACTION( obj ); + + return action; +} + + +gint ege_select_one_action_get_active( EgeSelectOneAction* action ) +{ + g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); + return action->private_data->active; +} + +void ege_select_one_action_set_active( EgeSelectOneAction* action, gint val ) +{ + g_object_set( G_OBJECT(action), "active", val, NULL ); +} + +gint ege_select_one_action_get_label_column( EgeSelectOneAction* action ) +{ + g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); + return action->private_data->column; +} + +void ege_select_one_action_set_label_column( EgeSelectOneAction* action, gint col ) +{ + g_object_set( G_OBJECT(action), "column", col, NULL ); +} + + +void ege_select_one_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ) +{ + EgeSelectOneAction* action = EGE_SELECT_ONE_ACTION( obj ); + switch ( propId ) { + case PROP_MODEL: + g_value_set_object( value, action->private_data->model ); + break; + + case PROP_ACTIVE: + g_value_set_int( value, action->private_data->active ); + break; + + case PROP_COLUMN: + g_value_set_int( value, action->private_data->column ); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + +void ege_select_one_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ) +{ + EgeSelectOneAction* action = EGE_SELECT_ONE_ACTION( obj ); + switch ( propId ) { + case PROP_MODEL: + { + action->private_data->model = GTK_TREE_MODEL( g_value_get_object( value ) ); + } + break; + + case PROP_ACTIVE: + { + resync_active( action, g_value_get_int( value ) ); + } + break; + + case PROP_COLUMN: + { + action->private_data->column = g_value_get_int( value ); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + +GtkWidget* create_menu_item( GtkAction* action ) +{ + GtkWidget* item = 0; + + if ( IS_EGE_SELECT_ONE_ACTION(action) ) { + EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION( action ); + gchar* sss = 0; + gboolean valid = FALSE; + gint index = 0; + GtkTreeIter iter; + GSList* group = 0; + GtkWidget* subby = gtk_menu_new(); + + g_object_get( G_OBJECT(action), "label", &sss, NULL ); + + item = gtk_menu_item_new_with_label( sss ); + + valid = gtk_tree_model_get_iter_first( act->private_data->model, &iter ); + while ( valid ) { + gchar* str = 0; + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->column, &str, + -1 ); + + GtkWidget *item = gtk_radio_menu_item_new_with_label( group, str ); + group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) ); + gtk_menu_shell_append( GTK_MENU_SHELL(subby), item ); + g_object_set_qdata( G_OBJECT(item), gDataName, act ); + + gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), index == act->private_data->active ); + + g_free(str); + + g_signal_connect( G_OBJECT(item), "toggled", G_CALLBACK(menu_toggled_cb), GINT_TO_POINTER(index) ); + + index++; + valid = gtk_tree_model_iter_next( act->private_data->model, &iter ); + } + + gtk_menu_item_set_submenu( GTK_MENU_ITEM(item), subby ); + gtk_widget_show_all( subby ); + + g_free(sss); + } else { + item = gParentClass->create_menu_item( action ); + } + + return item; +} + +GtkWidget* create_tool_item( GtkAction* action ) +{ + GtkWidget* item = 0; + + if ( IS_EGE_SELECT_ONE_ACTION(action) && EGE_SELECT_ONE_ACTION(action)->private_data->model ) + { + EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION(action); + item = GTK_WIDGET( gtk_tool_item_new() ); + + GtkWidget* normal = gtk_combo_box_new_with_model( act->private_data->model ); + + GtkCellRenderer * renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(normal), renderer, TRUE ); + gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT(normal), renderer, "text", act->private_data->column, (gchar*)0); + + gtk_combo_box_set_active( GTK_COMBO_BOX(normal), act->private_data->active ); + + g_signal_connect( G_OBJECT(normal), "changed", G_CALLBACK(combo_changed_cb), action ); + + gtk_container_add( GTK_CONTAINER(item), normal ); + + gtk_widget_show_all( item ); + } else { + item = gParentClass->create_tool_item( action ); + } + + return item; +} + +void connect_proxy( GtkAction *action, GtkWidget *proxy ) +{ + gParentClass->connect_proxy( action, proxy ); +} + +void disconnect_proxy( GtkAction *action, GtkWidget *proxy ) +{ + gParentClass->disconnect_proxy( action, proxy ); +} + + +void resync_active( EgeSelectOneAction* act, gint active ) +{ + if ( act->private_data->active != active ) { + act->private_data->active = active; + GSList* proxies = gtk_action_get_proxies( GTK_ACTION(act) ); + while ( proxies ) { + if ( GTK_IS_TOOL_ITEM(proxies->data) ) { + /* Search for the things we built up in create_tool_item() */ + GList* children = gtk_container_get_children( GTK_CONTAINER(proxies->data) ); + if ( children && children->data ) { + GtkComboBox* combo = GTK_COMBO_BOX(children->data); + if ( gtk_combo_box_get_active(combo) != active ) { + gtk_combo_box_set_active( combo, active ); + } + } + } else if ( GTK_IS_MENU_ITEM(proxies->data) ) { + GtkWidget* subMenu = gtk_menu_item_get_submenu( GTK_MENU_ITEM(proxies->data) ); + GList* children = gtk_container_get_children( GTK_CONTAINER(subMenu) ); + if ( children && (g_list_length(children) > (guint)active) ) { + gpointer data = g_list_nth_data( children, active ); + gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(data), TRUE ); + } + } + + proxies = g_slist_next( proxies ); + } + + g_signal_emit( G_OBJECT(act), signals[CHANGED], 0); + } +} + +void combo_changed_cb( GtkComboBox* widget, gpointer user_data ) +{ + EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION(user_data); + gint newActive = gtk_combo_box_get_active(widget); + if (newActive != act->private_data->active) { + g_object_set( G_OBJECT(act), "active", newActive, NULL ); + } +} + +void menu_toggled_cb( GtkWidget* obj, gpointer data ) +{ + GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(obj); + EgeSelectOneAction* act = (EgeSelectOneAction*)g_object_get_qdata( G_OBJECT(obj), gDataName ); + gint newActive = GPOINTER_TO_INT(data); + if ( gtk_check_menu_item_get_active(item) && (newActive != act->private_data->active) ) { + g_object_set( G_OBJECT(act), "active", newActive, NULL ); + } +} diff --git a/src/ege-select-one-action.h b/src/ege-select-one-action.h new file mode 100644 index 000000000..f617cc55c --- /dev/null +++ b/src/ege-select-one-action.h @@ -0,0 +1,91 @@ +#ifndef SEEN_EGE_SELECT_ONE_ACTION +#define SEEN_EGE_SELECT_ONE_ACTION +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is EGE Select One Action. + * + * The Initial Developer of the Original Code is + * Jon A. Cruz. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* Note: this file should be kept compilable as both .cpp and .c */ + +#include +#include +#include +#include + +G_BEGIN_DECLS + + +#define EGE_SELECT_ONE_ACTION_TYPE ( ege_select_one_action_get_type() ) +#define EGE_SELECT_ONE_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_CAST( (obj), EGE_SELECT_ONE_ACTION_TYPE, EgeSelectOneAction) ) +#define EGE_SELECT_ONE_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( (klass), EGE_SELECT_ONE_ACTION_TYPE, EgeSelectOneActionClass) ) +#define IS_EGE_SELECT_ONE_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_TYPE( (obj), EGE_SELECT_ONE_ACTION_TYPE) ) +#define IS_EGE_SELECT_ONE_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE( (klass), EGE_SELECT_ONE_ACTION_TYPE) ) +#define EGE_SELECT_ONE_ACTION_GET_CLASS( obj ) ( G_TYPE_INSTANCE_GET_CLASS( (obj), EGE_SELECT_ONE_ACTION_TYPE, EgeSelectOneActionClass) ) + +typedef struct _EgeSelectOneAction EgeSelectOneAction; +typedef struct _EgeSelectOneActionClass EgeSelectOneActionClass; +typedef struct _EgeSelectOneActionPrivate EgeSelectOneActionPrivate; + +struct _EgeSelectOneAction +{ + GtkAction action; + EgeSelectOneActionPrivate *private_data; +}; + +struct _EgeSelectOneActionClass +{ + GtkActionClass parent_class; + void (*changed) (EgeSelectOneAction* action); +}; + +GType ege_select_one_action_get_type( void ); + +EgeSelectOneAction* ege_select_one_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id, + GtkTreeModel* model ); + +gint ege_select_one_action_get_active( EgeSelectOneAction* action ); +void ege_select_one_action_set_active( EgeSelectOneAction* action, gint val ); + +gint ege_select_one_action_get_label_column( EgeSelectOneAction* action ); +void ege_select_one_action_set_label_column( EgeSelectOneAction* action, gint col ); + +G_END_DECLS + +#endif /* SEEN_EGE_SELECT_ONE_ACTION */ diff --git a/src/helper/Makefile_insert b/src/helper/Makefile_insert index 9f061c090..f781332d0 100644 --- a/src/helper/Makefile_insert +++ b/src/helper/Makefile_insert @@ -26,6 +26,8 @@ helper_libspchelp_a_SOURCES = \ helper/stlport.h \ helper/unit-menu.cpp \ helper/unit-menu.h \ + helper/unit-tracker.cpp \ + helper/unit-tracker.h \ helper/units.cpp \ helper/units.h \ helper/window.cpp \ diff --git a/src/helper/unit-tracker.cpp b/src/helper/unit-tracker.cpp new file mode 100644 index 000000000..989be2400 --- /dev/null +++ b/src/helper/unit-tracker.cpp @@ -0,0 +1,264 @@ +/* + * 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 ); + } +} + +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 ); + } + + 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 ) { + 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:encoding=utf-8:textwidth=99 : diff --git a/src/helper/unit-tracker.h b/src/helper/unit-tracker.h new file mode 100644 index 000000000..1832430b3 --- /dev/null +++ b/src/helper/unit-tracker.h @@ -0,0 +1,75 @@ +/* + * 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 + */ + +#ifndef SEEN_INKSCAPE_UNIT_TRACKER_H +#define SEEN_INKSCAPE_UNIT_TRACKER_H + +#include + +#include + +#include "helper/units.h" + +namespace Inkscape { + +class UnitTracker +{ +public: + UnitTracker( guint bases = (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE) ); + ~UnitTracker(); + + void setBase( guint bases ); // SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE + void addUnit( SPUnitId id, gint index ); + + bool isUpdating() const; + + void setActiveUnit( SPUnit const *unit ); + SPUnit const* getActiveUnit() const; + + void addAdjustment( GtkAdjustment* adj ); + void setFullVal( GtkAdjustment* adj, gdouble val ); + + GtkAction* createAction( gchar const* name, gchar const* label, gchar const* tooltip ); + +private: + static void _unitChangedCB( GtkAction* action, gpointer data ); + static void _actionFinalizedCB( gpointer data, GObject *where_the_object_was ); + static void _adjustmentFinalizedCB( gpointer data, GObject *where_the_object_was ); + void _setActive( gint index ); + void _fixupAdjustments( SPUnit const* oldUnit, SPUnit const *newUnit ); + void _actionFinalized( GObject *where_the_object_was ); + void _adjustmentFinalized( GObject *where_the_object_was ); + + gint _active; + bool _isUpdating; + SPUnit* _activeUnit; + GtkListStore* _store; + GSList* _unitList; + GSList* _actionList; + GSList* _adjList; + std::map _priorValues; +}; + +} + +#endif // SEEN_INKSCAPE_UNIT_TRACKER_H + +/* + 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:encoding=utf-8:textwidth=99 : diff --git a/src/widgets/select-toolbar.cpp b/src/widgets/select-toolbar.cpp index 0d64409cc..30ce00ca2 100644 --- a/src/widgets/select-toolbar.cpp +++ b/src/widgets/select-toolbar.cpp @@ -4,6 +4,7 @@ * Authors: * Lauris Kaplinski * bulia byak + * Jon A. Cruz * * Copyright (C) 2003-2005 authors * @@ -15,6 +16,7 @@ #endif #include +#include #include "widgets/button.h" #include "widgets/spw-utilities.h" @@ -43,6 +45,10 @@ #include "sp-item-transform.h" #include "message-stack.h" #include "display/sp-canvas.h" +#include "ege-select-one-action.h" +#include "helper/unit-tracker.h" + +using Inkscape::UnitTracker; static void sp_selection_layout_widget_update(SPWidget *spw, Inkscape::Selection *sel) @@ -60,23 +66,24 @@ sp_selection_layout_widget_update(SPWidget *spw, Inkscape::Selection *sel) if ( sel && !sel->isEmpty() ) { NR::Maybe const bbox(sel->bounds()); if ( bbox && !bbox->isEmpty() ) { - GtkWidget *us = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(spw), "units"); - SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us)); + UnitTracker *tracker = reinterpret_cast(gtk_object_get_data(GTK_OBJECT(spw), "tracker")); + SPUnit const &unit = *tracker->getActiveUnit(); + + struct { char const *key; double val; } const keyval[] = { + { "X", bbox->min()[X] }, + { "Y", bbox->min()[Y] }, + { "width", bbox->extent(X) }, + { "height", bbox->extent(Y) } + }; if (unit.base == SP_UNIT_DIMENSIONLESS) { - char const * const keys[] = {"X", "Y", "width", "height"}; double const val = 1. / unit.unittobase; - for (unsigned i = 0; i < G_N_ELEMENTS(keys); ++i) { - GtkAdjustment *a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), keys[i]); + for (unsigned i = 0; i < G_N_ELEMENTS(keyval); ++i) { + GtkAdjustment *a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), keyval[i].key); gtk_adjustment_set_value(a, val); + tracker->setFullVal( a, keyval[i].val ); } } else { - struct { char const *key; double val; } const keyval[] = { - { "X", bbox->min()[X] }, - { "Y", bbox->min()[Y] }, - { "width", bbox->extent(X) }, - { "height", bbox->extent(Y) } - }; for (unsigned i = 0; i < G_N_ELEMENTS(keyval); ++i) { GtkAdjustment *a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), keyval[i].key); gtk_adjustment_set_value(a, sp_pixels_get_units(keyval[i].val, unit)); @@ -123,9 +130,8 @@ sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw) return; } - GtkWidget *us = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(spw), "units"); - SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us)); - if (sp_unit_selector_update_test(SP_UNIT_SELECTOR(us))) { + UnitTracker *tracker = reinterpret_cast(gtk_object_get_data(GTK_OBJECT(spw), "tracker")); + if ( !tracker || tracker->isUpdating() ) { /* * When only units are being changed, don't treat changes * to adjuster values as object changes. @@ -145,34 +151,33 @@ sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw) return; } - gdouble x0, y0, x1, y1, xrel, yrel; - GtkAdjustment *a_w; - GtkAdjustment *a_h; + gdouble x0 = 0; + gdouble y0 = 0; + gdouble x1 = 0; + gdouble y1 = 0; + gdouble xrel = 0; + gdouble yrel = 0; + SPUnit const &unit = *tracker->getActiveUnit(); + + GtkAdjustment* a_x = (GtkAdjustment *)gtk_object_get_data( GTK_OBJECT(spw), "X" ); + GtkAdjustment* a_y = (GtkAdjustment *)gtk_object_get_data( GTK_OBJECT(spw), "Y" ); + GtkAdjustment* a_w = (GtkAdjustment *)gtk_object_get_data( GTK_OBJECT(spw), "width" ); + GtkAdjustment* a_h = (GtkAdjustment *)gtk_object_get_data( GTK_OBJECT(spw), "height" ); if (unit.base == SP_UNIT_ABSOLUTE || unit.base == SP_UNIT_DEVICE) { - GtkAdjustment *a; - a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "X"); - x0 = sp_units_get_pixels (a->value, unit); - a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "Y"); - y0 = sp_units_get_pixels (a->value, unit); - a_w = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "width"); + x0 = sp_units_get_pixels (a_x->value, unit); + y0 = sp_units_get_pixels (a_y->value, unit); x1 = x0 + sp_units_get_pixels (a_w->value, unit); xrel = sp_units_get_pixels (a_w->value, unit) / bbox->extent(NR::X); - a_h = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "height"); y1 = y0 + sp_units_get_pixels (a_h->value, unit); yrel = sp_units_get_pixels (a_h->value, unit) / bbox->extent(NR::Y); } else { - GtkAdjustment *a; - a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "X"); - double const x0_propn = a->value * unit.unittobase; + double const x0_propn = a_x->value * unit.unittobase; x0 = bbox->min()[NR::X] * x0_propn; - a = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "Y"); - double const y0_propn = a->value * unit.unittobase; + double const y0_propn = a_y->value * unit.unittobase; y0 = y0_propn * bbox->min()[NR::Y]; - a_w = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "width"); xrel = a_w->value * unit.unittobase; x1 = x0 + xrel * bbox->extent(NR::X); - a_h = (GtkAdjustment *) gtk_object_get_data(GTK_OBJECT(spw), "height"); yrel = a_h->value * unit.unittobase; y1 = y0 + yrel * bbox->extent(NR::Y); } @@ -206,9 +211,9 @@ sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw) // the value was changed by the user, the difference will be at least that much; otherwise it's // just rounding difference between the spinbox value and actual value, so no action is // performed - char const * const actionkey = ( mh > 5e-4 ? "selector:toolbar:move:horizontal" : - sh > 5e-4 ? "selector:toolbar:scale:horizontal" : - mv > 5e-4 ? "selector:toolbar:move:vertical" : + char const * const actionkey = ( mh > 5e-4 ? "selector:toolbar:move:horizontal" : + sh > 5e-4 ? "selector:toolbar:scale:horizontal" : + mv > 5e-4 ? "selector:toolbar:move:vertical" : sv > 5e-4 ? "selector:toolbar:scale:vertical" : NULL ); if (actionkey != NULL) { @@ -222,7 +227,7 @@ sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw) NR::Matrix scaler = get_scale_transform_with_stroke (*bbox, strokewidth, transform_stroke, x0, y0, x1, y1); sp_selection_apply_affine(selection, scaler); - sp_document_maybe_done (document, actionkey, SP_VERB_CONTEXT_SELECT, + sp_document_maybe_done (document, actionkey, SP_VERB_CONTEXT_SELECT, _("Transform by toolbar")); // defocus spinbuttons by moving focus to the canvas, unless "stay" is on @@ -236,7 +241,7 @@ sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw) } GtkWidget * -sp_select_toolbox_spinbutton(gchar *label, gchar *data, float lower_limit, GtkWidget *us, GtkWidget *spw, gchar *tooltip, gboolean altx) +sp_select_toolbox_spinbutton(gchar *label, gchar *data, float lower_limit, UnitTracker* tracker, GtkWidget *spw, gchar *tooltip, gboolean altx) { GtkTooltips *tt = gtk_tooltips_new(); @@ -248,7 +253,9 @@ sp_select_toolbox_spinbutton(gchar *label, gchar *data, float lower_limit, GtkWi gtk_container_add(GTK_CONTAINER(hb), l); GtkObject *a = gtk_adjustment_new(0.0, lower_limit, 1e6, SPIN_STEP, SPIN_PAGE_STEP, SPIN_PAGE_STEP); - sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a)); + if ( tracker ) { + tracker->addAdjustment( GTK_ADJUSTMENT(a) ); + } gtk_object_set_data(GTK_OBJECT(spw), data, a); GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), SPIN_STEP, 3); @@ -268,79 +275,6 @@ sp_select_toolbox_spinbutton(gchar *label, gchar *data, float lower_limit, GtkWi return hb; } -static gboolean aux_set_unit(SPUnitSelector *, - SPUnit const *old, - SPUnit const *new_units, - GObject *dlg) -{ - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - - if (!desktop) { - return FALSE; - } - - Inkscape::Selection *selection = sp_desktop_selection(desktop); - - if (selection->isEmpty()) - return FALSE; - - if ((old->base == SP_UNIT_ABSOLUTE || old->base == SP_UNIT_DEVICE) - && (new_units->base == SP_UNIT_DIMENSIONLESS)) - { - - NR::Maybe bbox = selection->bounds(); - if (!bbox) { - return FALSE; - } - - /* Absolute to percentage */ - g_object_set_data(dlg, "update", GUINT_TO_POINTER(TRUE)); - - GtkAdjustment *ax = GTK_ADJUSTMENT(g_object_get_data(dlg, "X")); - GtkAdjustment *ay = GTK_ADJUSTMENT(g_object_get_data(dlg, "Y")); - GtkAdjustment *aw = GTK_ADJUSTMENT(g_object_get_data(dlg, "width")); - GtkAdjustment *ah = GTK_ADJUSTMENT(g_object_get_data(dlg, "height")); - - double const x = sp_units_get_pixels (ax->value, *old); - double const y = sp_units_get_pixels (ay->value, *old); - double const w = sp_units_get_pixels (aw->value, *old); - double const h = sp_units_get_pixels (ah->value, *old); - - gtk_adjustment_set_value(ax, fabs(bbox->min()[NR::X]) > 1e-6? 100.0 * x / bbox->min()[NR::X] : 100.0); - gtk_adjustment_set_value(ay, fabs(bbox->min()[NR::Y]) > 1e-6? 100.0 * y / bbox->min()[NR::Y] : 100.0); - gtk_adjustment_set_value(aw, fabs(bbox->extent(NR::X)) > 1e-6? 100.0 * w / bbox->extent(NR::X) : 100.0); - gtk_adjustment_set_value(ah, fabs(bbox->extent(NR::Y)) > 1e-6? 100.0 * h / bbox->extent(NR::Y) : 100.0); - - g_object_set_data(dlg, "update", GUINT_TO_POINTER(FALSE)); - return TRUE; - } else if ((old->base == SP_UNIT_DIMENSIONLESS) - && (new_units->base == SP_UNIT_ABSOLUTE || new_units->base == SP_UNIT_DEVICE)) { - - NR::Maybe bbox = selection->bounds(); - if (!bbox) { - return FALSE; - } - - /* Percentage to absolute */ - g_object_set_data(dlg, "update", GUINT_TO_POINTER(TRUE)); - - GtkAdjustment *ax = GTK_ADJUSTMENT(g_object_get_data(dlg, "X")); - GtkAdjustment *ay = GTK_ADJUSTMENT(g_object_get_data(dlg, "Y")); - GtkAdjustment *aw = GTK_ADJUSTMENT(g_object_get_data(dlg, "width")); - GtkAdjustment *ah = GTK_ADJUSTMENT(g_object_get_data(dlg, "height")); - - gtk_adjustment_set_value(ax, sp_pixels_get_units(0.01 * ax->value * bbox->min()[NR::X], *new_units)); - gtk_adjustment_set_value(ay, sp_pixels_get_units(0.01 * ay->value * bbox->min()[NR::Y], *new_units)); - gtk_adjustment_set_value(aw, sp_pixels_get_units(0.01 * aw->value * bbox->extent(NR::X), *new_units)); - gtk_adjustment_set_value(ah, sp_pixels_get_units(0.01 * ah->value * bbox->extent(NR::Y), *new_units)); - - g_object_set_data(dlg, "update", GUINT_TO_POINTER(FALSE)); - return TRUE; - } - - return FALSE; -} - // toggle button callbacks and updaters static void toggle_stroke (GtkWidget *button, gpointer data) { @@ -399,6 +333,15 @@ static void toggle_lock (GtkWidget *button, gpointer data) { } } +static void destroy_tracker( GtkObject* obj, gpointer /*user_data*/ ) +{ + UnitTracker *tracker = reinterpret_cast(gtk_object_get_data(obj, "tracker")); + if ( tracker ) { + delete tracker; + gtk_object_set_data( obj, "tracker", 0 ); + } +} + GtkWidget * sp_select_toolbox_new(SPDesktop *desktop) { @@ -432,29 +375,31 @@ sp_select_toolbox_new(SPDesktop *desktop) gtk_object_set_data(GTK_OBJECT(spw), "frame", vb); // Create the units menu. - GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE); - sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT); - sp_unit_selector_add_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT), 0); - sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), sp_desktop_namedview(desktop)->doc_units); - g_signal_connect(G_OBJECT(us), "set_unit", G_CALLBACK(aux_set_unit), spw); + UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE ); + tracker->addUnit( SP_UNIT_PERCENT, 0 ); + tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units ); + + gtk_object_set_data( GTK_OBJECT(spw), "tracker", tracker ); + g_signal_connect( G_OBJECT(spw), "destroy", G_CALLBACK(destroy_tracker), spw ); + // four spinbuttons gtk_container_add(GTK_CONTAINER(vb), - //TRANSLATORS: only translate "string" in "context|string". + //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS - sp_select_toolbox_spinbutton(_("select_toolbar|X"), "X", -1e6, us, spw, _("Horizontal coordinate of selection"), TRUE)); + sp_select_toolbox_spinbutton(_("select_toolbar|X"), "X", -1e6, tracker, spw, _("Horizontal coordinate of selection"), TRUE)); aux_toolbox_space(vb, AUX_BETWEEN_SPINBUTTONS); gtk_container_add(GTK_CONTAINER(vb), - //TRANSLATORS: only translate "string" in "context|string". + //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS - sp_select_toolbox_spinbutton(_("select_toolbar|Y"), "Y", -1e6, us, spw, _("Vertical coordinate of selection"), FALSE)); + sp_select_toolbox_spinbutton(_("select_toolbar|Y"), "Y", -1e6, tracker, spw, _("Vertical coordinate of selection"), FALSE)); aux_toolbox_space(vb, AUX_BETWEEN_BUTTON_GROUPS); gtk_container_add(GTK_CONTAINER(vb), - //TRANSLATORS: only translate "string" in "context|string". + //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS - sp_select_toolbox_spinbutton(_("select_toolbar|W"), "width", 1e-3, us, spw, _("Width of selection"), FALSE)); + sp_select_toolbox_spinbutton(_("select_toolbar|W"), "width", 1e-3, tracker, spw, _("Width of selection"), FALSE)); // lock toggle GtkWidget *lockbox = gtk_vbox_new(TRUE, 0); @@ -470,16 +415,20 @@ sp_select_toolbox_new(SPDesktop *desktop) g_signal_connect_after (G_OBJECT (lock), "clicked", G_CALLBACK (toggle_lock), desktop); gtk_container_add(GTK_CONTAINER(vb), - //TRANSLATORS: only translate "string" in "context|string". + //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS - sp_select_toolbox_spinbutton(_("select_toolbar|H"), "height", 1e-3, us, spw, _("Height of selection"), FALSE)); + sp_select_toolbox_spinbutton(_("select_toolbar|H"), "height", 1e-3, tracker, spw, _("Height of selection"), FALSE)); aux_toolbox_space(vb, 2); // Add the units menu. - gtk_widget_show(us); - gtk_container_add(GTK_CONTAINER(vb), us); - gtk_object_set_data(GTK_OBJECT(spw), "units", us); + { + GtkAction* act = tracker->createAction( "UnitAction", _("Units"), _("") ); + + GtkWidget* normal = gtk_action_create_tool_item( act ); + gtk_widget_show( normal ); + gtk_container_add( GTK_CONTAINER(vb), normal ); + } // Set font size. sp_set_font_size_smaller (vb);