diff options
| author | Liam P. White <inkscapebrony@gmail.com> | 2014-10-08 02:22:03 +0000 |
|---|---|---|
| committer | Liam P. White <inkscapebrony@gmail.com> | 2014-10-08 02:22:03 +0000 |
| commit | f3840fe9d0d423f6ddf5a68d776a903d57ffb7b5 (patch) | |
| tree | c8e28b27f5885928ccf866a69eb6cbc99fde599c /src/widgets | |
| parent | Update to experimental r13565 (diff) | |
| parent | Some template cleanup... (diff) | |
| download | inkscape-f3840fe9d0d423f6ddf5a68d776a903d57ffb7b5.tar.gz inkscape-f3840fe9d0d423f6ddf5a68d776a903d57ffb7b5.zip | |
Update to experimental r13598
(bzr r13341.5.17)
Diffstat (limited to 'src/widgets')
39 files changed, 5027 insertions, 80 deletions
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index fe4433153..072b905a2 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -24,6 +24,9 @@ set(widgets_SRC desktop-widget.cpp eek-preview.cpp ege-paint-def.cpp + ege-adjustment-action.cpp + ege-output-action.cpp + ege-select-one-action.cpp fill-style.cpp font-selector.cpp gradient-image.cpp @@ -31,6 +34,8 @@ set(widgets_SRC gradient-toolbar.cpp gradient-vector.cpp icon.cpp + ink-action.cpp + ink-comboboxentry-action.cpp paint-selector.cpp ruler.cpp select-toolbar.cpp @@ -80,6 +85,9 @@ set(widgets_SRC desktop-widget.h eek-preview.h ege-paint-def.h + ege-adjustment-action.h + ege-output-action.h + ege-select-one-action.h fill-n-stroke-factory.h fill-style.h font-selector.h @@ -88,6 +96,8 @@ set(widgets_SRC gradient-toolbar.h gradient-vector.h icon.h + ink-action.h + ink-comboboxentry-action.h paint-selector.h ruler.h select-toolbar.h diff --git a/src/widgets/Makefile_insert b/src/widgets/Makefile_insert index 97713cbee..dc4c12967 100644 --- a/src/widgets/Makefile_insert +++ b/src/widgets/Makefile_insert @@ -19,8 +19,14 @@ ink_common_sources += \ widgets/dropper-toolbar.h \ widgets/eek-preview.cpp \ widgets/eek-preview.h \ + widgets/ege-adjustment-action.cpp \ + widgets/ege-adjustment-action.h \ widgets/ege-paint-def.cpp \ widgets/ege-paint-def.h \ + widgets/ege-output-action.cpp \ + widgets/ege-output-action.h \ + widgets/ege-select-one-action.cpp \ + widgets/ege-select-one-action.h \ widgets/eraser-toolbar.cpp \ widgets/eraser-toolbar.h \ widgets/fill-style.cpp \ @@ -38,6 +44,10 @@ ink_common_sources += \ widgets/gradient-vector.h \ widgets/icon.cpp \ widgets/icon.h \ + widgets/ink-action.cpp \ + widgets/ink-action.h \ + widgets/ink-comboboxentry-action.cpp \ + widgets/ink-comboboxentry-action.h \ widgets/lpe-toolbar.cpp \ widgets/lpe-toolbar.h \ widgets/measure-toolbar.cpp \ diff --git a/src/widgets/arc-toolbar.cpp b/src/widgets/arc-toolbar.cpp index 1005de70d..ca582924b 100644 --- a/src/widgets/arc-toolbar.cpp +++ b/src/widgets/arc-toolbar.cpp @@ -34,10 +34,10 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-output-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-output-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" #include "mod360.h" #include "preferences.h" #include "selection.h" diff --git a/src/widgets/box3d-toolbar.cpp b/src/widgets/box3d-toolbar.cpp index 052eb230b..f6e303d58 100644 --- a/src/widgets/box3d-toolbar.cpp +++ b/src/widgets/box3d-toolbar.cpp @@ -36,8 +36,8 @@ #include "desktop.h" #include "document-undo.h" #include "document.h" -#include "ege-adjustment-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ink-action.h" #include "inkscape.h" #include "persp3d.h" #include "selection.h" diff --git a/src/widgets/button.cpp b/src/widgets/button.cpp index cda97654e..1776e28c4 100644 --- a/src/widgets/button.cpp +++ b/src/widgets/button.cpp @@ -16,7 +16,7 @@ #include "button.h" #include "helper/action-context.h" -#include "interface.h" +#include "ui/interface.h" #include "shortcuts.h" #include "helper/action.h" diff --git a/src/widgets/calligraphy-toolbar.cpp b/src/widgets/calligraphy-toolbar.cpp index 9c0393cd9..4ae6427ad 100644 --- a/src/widgets/calligraphy-toolbar.cpp +++ b/src/widgets/calligraphy-toolbar.cpp @@ -34,9 +34,9 @@ #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" #include "preferences.h" #include "toolbox.h" #include "ui/icon-names.h" diff --git a/src/widgets/connector-toolbar.cpp b/src/widgets/connector-toolbar.cpp index 9bbc1bbb4..6ce926dc1 100644 --- a/src/widgets/connector-toolbar.cpp +++ b/src/widgets/connector-toolbar.cpp @@ -35,10 +35,10 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" +#include "widgets/ege-adjustment-action.h" #include "enums.h" #include "graphlayout.h" -#include "ink-action.h" +#include "widgets/ink-action.h" #include "inkscape.h" #include "preferences.h" #include "selection.h" diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 9b7ac2f42..ef3b29478 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -38,7 +38,7 @@ #include "display/canvas-arena.h" #include "document.h" #include "ege-color-prof-tracker.h" -#include "ege-select-one-action.h" +#include "widgets/ege-select-one-action.h" #include <extension/db.h> #include "file.h" #include "helper/action.h" @@ -46,7 +46,7 @@ #include "util/units.h" #include "ui/widget/unit-tracker.h" #include "inkscape.h" -#include "interface.h" +#include "ui/interface.h" #include "macros.h" #include "preferences.h" #include "sp-image.h" diff --git a/src/widgets/dropper-toolbar.cpp b/src/widgets/dropper-toolbar.cpp index 478d0c1a4..45ed9ead4 100644 --- a/src/widgets/dropper-toolbar.cpp +++ b/src/widgets/dropper-toolbar.cpp @@ -32,8 +32,8 @@ #include "dropper-toolbar.h" #include "document-undo.h" -#include "ege-output-action.h" -#include "ink-action.h" +#include "widgets/ege-output-action.h" +#include "widgets/ink-action.h" #include "preferences.h" #include "widgets/spinbutton-events.h" diff --git a/src/widgets/ege-adjustment-action.cpp b/src/widgets/ege-adjustment-action.cpp new file mode 100644 index 000000000..d89a6e3f1 --- /dev/null +++ b/src/widgets/ege-adjustment-action.cpp @@ -0,0 +1,1113 @@ +/* -*- 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 Adjustment Action. + * + * The Initial Developer of the Original Code is + * Jon A. Cruz. + * Portions created by the Initial Developer are Copyright (C) 2006 + * 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 <cmath> +#include <string.h> + +#include "widgets/icon.h" +#include <gdk/gdkkeysyms.h> + +#include "icon-size.h" +#include "widgets/ege-adjustment-action.h" +#include "ui/widget/gimpspinscale.h" +#include "ui/icon-names.h" + + +static void ege_adjustment_action_finalize( GObject* object ); +static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ); +static void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ); + +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 gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data ); +static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data ); +static gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data ); + +static void ege_adjustment_action_defocus( EgeAdjustmentAction* action ); + +static void egeAct_free_description( gpointer data, gpointer user_data ); +static void egeAct_free_all_descriptions( EgeAdjustmentAction* action ); + +static EgeCreateAdjWidgetCB gFactoryCb = 0; +static GQuark gDataName = 0; + +enum { + APPEARANCE_UNKNOWN = -1, + APPEARANCE_NONE = 0, + APPEARANCE_FULL, /* label, then all choices represented by separate buttons */ + APPEARANCE_COMPACT, /* label, then choices in a drop-down menu */ + APPEARANCE_MINIMAL, /* no label, just choices in a drop-down menu */ +}; + +/* TODO need to have appropriate icons setup for these: */ +static const gchar *floogles[] = { + INKSCAPE_ICON("list-remove"), + INKSCAPE_ICON("list-add"), + INKSCAPE_ICON("go-down"), + INKSCAPE_ICON("help-about"), + INKSCAPE_ICON("go-up"), + 0}; + +typedef struct _EgeAdjustmentDescr EgeAdjustmentDescr; + +struct _EgeAdjustmentDescr +{ + gchar* descr; + gdouble value; +}; + +struct _EgeAdjustmentActionPrivate +{ + GtkAdjustment* adj; + GtkWidget* focusWidget; + gdouble climbRate; + guint digits; + gdouble epsilon; + gchar* format; + gchar* selfId; + EgeWidgetFixup toolPost; + gdouble lastVal; + gdouble step; + gdouble page; + gint appearanceMode; + gboolean transferFocus; + GList* descriptions; + gchar* appearance; + gchar* iconId; + Inkscape::IconSize iconSize; + Inkscape::UI::Widget::UnitTracker *unitTracker; +}; + +#define EGE_ADJUSTMENT_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), EGE_ADJUSTMENT_ACTION_TYPE, EgeAdjustmentActionPrivate ) ) + +enum { + PROP_ADJUSTMENT = 1, + PROP_FOCUS_WIDGET, + PROP_CLIMB_RATE, + PROP_DIGITS, + PROP_SELFID, + PROP_TOOL_POST, + PROP_APPEARANCE, + PROP_ICON_ID, + PROP_ICON_SIZE, + PROP_UNIT_TRACKER +}; + +enum { + BUMP_TOP = 0, + BUMP_PAGE_UP, + BUMP_UP, + BUMP_NONE, + BUMP_DOWN, + BUMP_PAGE_DOWN, + BUMP_BOTTOM, + BUMP_CUSTOM = 100 +}; + +G_DEFINE_TYPE(EgeAdjustmentAction, ege_adjustment_action, GTK_TYPE_ACTION); + +static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass ) +{ + if ( klass ) { + GObjectClass * objClass = G_OBJECT_CLASS( klass ); + + gDataName = g_quark_from_string("ege-adj-action"); + + + objClass->finalize = ege_adjustment_action_finalize; + + objClass->get_property = ege_adjustment_action_get_property; + objClass->set_property = ege_adjustment_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_ADJUSTMENT, + g_param_spec_object( "adjustment", + "Adjustment", + "The adjustment to change", + GTK_TYPE_ADJUSTMENT, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_FOCUS_WIDGET, + g_param_spec_pointer( "focus-widget", + "Focus Widget", + "The widget to return focus to", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_CLIMB_RATE, + g_param_spec_double( "climb-rate", + "Climb Rate", + "The acelleraton rate", + 0.0, G_MAXDOUBLE, 0.0, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_DIGITS, + g_param_spec_uint( "digits", + "Digits", + "The number of digits to show", + 0, 20, 0, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_SELFID, + g_param_spec_string( "self-id", + "Self ID", + "Marker for self pointer", + 0, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_TOOL_POST, + g_param_spec_pointer( "tool-post", + "Tool Widget post process", + "Function for final adjustments", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_APPEARANCE, + g_param_spec_string( "appearance", + "Appearance hint", + "A hint for how to display", + "", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_ICON_ID, + g_param_spec_string( "iconId", + "Icon ID", + "The id for the icon", + "", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_ICON_SIZE, + g_param_spec_int( "iconSize", + "Icon Size", + "The size the icon", + (int)Inkscape::ICON_SIZE_MENU, + (int)Inkscape::ICON_SIZE_DECORATION, + (int)Inkscape::ICON_SIZE_SMALL_TOOLBAR, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_UNIT_TRACKER, + g_param_spec_pointer( "unit_tracker", + "Unit Tracker", + "The widget that keeps track of the unit", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_type_class_add_private( klass, sizeof(EgeAdjustmentActionClass) ); + } +} + +void ege_adjustment_action_set_compact_tool_factory( EgeCreateAdjWidgetCB factoryCb ) +{ + gFactoryCb = factoryCb; +} + +static void ege_adjustment_action_init( EgeAdjustmentAction* action ) +{ + action->private_data = EGE_ADJUSTMENT_ACTION_GET_PRIVATE( action ); + action->private_data->adj = 0; + action->private_data->focusWidget = 0; + action->private_data->climbRate = 0.0; + action->private_data->digits = 2; + action->private_data->epsilon = 0.009; + action->private_data->format = g_strdup_printf("%%0.%df%%s%%s", action->private_data->digits); + action->private_data->selfId = 0; + action->private_data->toolPost = 0; + action->private_data->lastVal = 0.0; + action->private_data->step = 0.0; + action->private_data->page = 0.0; + action->private_data->appearanceMode = APPEARANCE_NONE; + action->private_data->transferFocus = FALSE; + action->private_data->descriptions = 0; + action->private_data->appearance = 0; + action->private_data->iconId = 0; + action->private_data->iconSize = Inkscape::ICON_SIZE_SMALL_TOOLBAR; + action->private_data->unitTracker = NULL; +} + +static void ege_adjustment_action_finalize( GObject* object ) +{ + EgeAdjustmentAction* action = 0; + g_return_if_fail( object != NULL ); + g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(object) ); + + action = EGE_ADJUSTMENT_ACTION( object ); + + // g_free(NULL) does nothing + g_free( action->private_data->format ); + g_free( action->private_data->selfId ); + g_free( action->private_data->appearance ); + g_free( action->private_data->iconId ); + + egeAct_free_all_descriptions( action ); + + if ( G_OBJECT_CLASS(ege_adjustment_action_parent_class)->finalize ) { + (*G_OBJECT_CLASS(ege_adjustment_action_parent_class)->finalize)(object); + } +} + +EgeAdjustmentAction* ege_adjustment_action_new( GtkAdjustment* adjustment, + const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id, + gdouble climb_rate, + guint digits, + Inkscape::UI::Widget::UnitTracker *unit_tracker ) +{ + GObject* obj = (GObject*)g_object_new( EGE_ADJUSTMENT_ACTION_TYPE, + "name", name, + "label", label, + "tooltip", tooltip, + "stock_id", stock_id, + "adjustment", adjustment, + "climb-rate", climb_rate, + "digits", digits, + "unit_tracker", unit_tracker, + NULL ); + + EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj ); + + return action; +} + +static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ) +{ + EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj ); + switch ( propId ) { + case PROP_ADJUSTMENT: + g_value_set_object( value, action->private_data->adj ); + break; + + case PROP_FOCUS_WIDGET: + g_value_set_pointer( value, action->private_data->focusWidget ); + break; + + case PROP_CLIMB_RATE: + g_value_set_double( value, action->private_data->climbRate ); + break; + + case PROP_DIGITS: + g_value_set_uint( value, action->private_data->digits ); + break; + + case PROP_SELFID: + g_value_set_string( value, action->private_data->selfId ); + break; + + case PROP_TOOL_POST: + g_value_set_pointer( value, (void*)action->private_data->toolPost ); + break; + + case PROP_APPEARANCE: + g_value_set_string( value, action->private_data->appearance ); + break; + + case PROP_ICON_ID: + g_value_set_string( value, action->private_data->iconId ); + break; + + case PROP_ICON_SIZE: + g_value_set_int( value, action->private_data->iconSize ); + break; + + case PROP_UNIT_TRACKER: + g_value_set_pointer( value, action->private_data->unitTracker ); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + +void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ) +{ + EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj ); + switch ( propId ) { + case PROP_ADJUSTMENT: + { + action->private_data->adj = GTK_ADJUSTMENT( g_value_get_object( value ) ); + g_object_get( G_OBJECT(action->private_data->adj), + "step-increment", &action->private_data->step, + "page-increment", &action->private_data->page, + NULL ); + } + break; + + case PROP_FOCUS_WIDGET: + { + /* TODO unhook prior */ + action->private_data->focusWidget = (GtkWidget*)g_value_get_pointer( value ); + } + break; + + case PROP_CLIMB_RATE: + { + /* TODO pass on */ + action->private_data->climbRate = g_value_get_double( value ); + } + break; + + case PROP_DIGITS: + { + /* TODO pass on */ + action->private_data->digits = g_value_get_uint( value ); + switch ( action->private_data->digits ) { + case 0: action->private_data->epsilon = 0.9; break; + case 1: action->private_data->epsilon = 0.09; break; + case 2: action->private_data->epsilon = 0.009; break; + case 3: action->private_data->epsilon = 0.0009; break; + case 4: action->private_data->epsilon = 0.00009; break; + } + if ( action->private_data->format ) { + g_free( action->private_data->format ); + } + action->private_data->format = g_strdup_printf("%%0.%df%%s%%s", action->private_data->digits); + } + break; + + case PROP_SELFID: + { + /* TODO pass on */ + gchar* prior = action->private_data->selfId; + action->private_data->selfId = g_value_dup_string( value ); + g_free( prior ); + } + break; + + case PROP_TOOL_POST: + { + action->private_data->toolPost = (EgeWidgetFixup)g_value_get_pointer( value ); + } + break; + + case PROP_APPEARANCE: + { + gchar* tmp = action->private_data->appearance; + gchar* newVal = g_value_dup_string( value ); + action->private_data->appearance = newVal; + g_free( tmp ); + + if ( !action->private_data->appearance || (strcmp("", newVal) == 0) ) { + action->private_data->appearanceMode = APPEARANCE_NONE; + } else if ( strcmp("full", newVal) == 0 ) { + action->private_data->appearanceMode = APPEARANCE_FULL; + } else if ( strcmp("compact", newVal) == 0 ) { + action->private_data->appearanceMode = APPEARANCE_COMPACT; + } else if ( strcmp("minimal", newVal) == 0 ) { + action->private_data->appearanceMode = APPEARANCE_MINIMAL; + } else { + action->private_data->appearanceMode = APPEARANCE_UNKNOWN; + } + } + break; + + case PROP_ICON_ID: + { + gchar* tmp = action->private_data->iconId; + action->private_data->iconId = g_value_dup_string( value ); + g_free( tmp ); + } + break; + + case PROP_ICON_SIZE: + { + action->private_data->iconSize = (Inkscape::IconSize)g_value_get_int( value ); + } + break; + + case PROP_UNIT_TRACKER: + { + action->private_data->unitTracker = (Inkscape::UI::Widget::UnitTracker*)g_value_get_pointer( value ); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + +GtkAdjustment* ege_adjustment_action_get_adjustment( EgeAdjustmentAction* action ) +{ + g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL ); + + return action->private_data->adj; +} + +void ege_adjustment_action_set_focuswidget( EgeAdjustmentAction* action, GtkWidget* widget ) +{ + g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(action) ); + + /* TODO unhook prior */ + + action->private_data->focusWidget = widget; +} + +GtkWidget* ege_adjustment_action_get_focuswidget( EgeAdjustmentAction* action ) +{ + g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL ); + + return action->private_data->focusWidget; +} + +static void egeAct_free_description( gpointer data, gpointer user_data ) { + (void)user_data; + if ( data ) { + EgeAdjustmentDescr* descr = (EgeAdjustmentDescr*)data; + if ( descr->descr ) { + g_free( descr->descr ); + descr->descr = 0; + } + g_free( descr ); + } +} + +static void egeAct_free_all_descriptions( EgeAdjustmentAction* action ) +{ + if ( action->private_data->descriptions ) { + g_list_foreach( action->private_data->descriptions, egeAct_free_description, 0 ); + g_list_free( action->private_data->descriptions ); + action->private_data->descriptions = 0; + } +} + +static gint egeAct_compare_descriptions( gconstpointer a, gconstpointer b ) +{ + gint val = 0; + + EgeAdjustmentDescr const * aa = (EgeAdjustmentDescr const *)a; + EgeAdjustmentDescr const * bb = (EgeAdjustmentDescr const *)b; + + if ( aa && bb ) { + if ( aa->value < bb->value ) { + val = -1; + } else if ( aa->value > bb->value ) { + val = 1; + } + } + + return val; +} + +void ege_adjustment_action_set_descriptions( EgeAdjustmentAction* action, gchar const** descriptions, gdouble const* values, guint count ) +{ + g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(action) ); + + egeAct_free_all_descriptions( action ); + + if ( count && descriptions && values ) { + guint i = 0; + for ( i = 0; i < count; i++ ) { + EgeAdjustmentDescr* descr = g_new0( EgeAdjustmentDescr, 1 ); + descr->descr = descriptions[i] ? g_strdup( descriptions[i] ) : 0; + descr->value = values[i]; + action->private_data->descriptions = g_list_insert_sorted( action->private_data->descriptions, (gpointer)descr, egeAct_compare_descriptions ); + } + } +} + +void ege_adjustment_action_set_appearance( EgeAdjustmentAction* action, gchar const* val ) +{ + g_object_set( G_OBJECT(action), "appearance", val, NULL ); +} + +static void process_menu_action( GtkWidget* obj, gpointer data ) +{ + GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(obj); + if ( gtk_check_menu_item_get_active (item)) { + EgeAdjustmentAction* act = (EgeAdjustmentAction*)g_object_get_qdata( G_OBJECT(obj), gDataName ); + gint what = GPOINTER_TO_INT(data); + + + gdouble base = gtk_adjustment_get_value( act->private_data->adj ); + gdouble lower = 0.0; + gdouble upper = 0.0; + gdouble step = 0.0; + gdouble page = 0.0; + g_object_get( G_OBJECT(act->private_data->adj), + "lower", &lower, + "upper", &upper, + "step-increment", &step, + "page-increment", &page, + NULL ); + + switch ( what ) { + case BUMP_TOP: + gtk_adjustment_set_value( act->private_data->adj, upper ); + break; + + case BUMP_PAGE_UP: + gtk_adjustment_set_value( act->private_data->adj, base + page ); + break; + + case BUMP_UP: + gtk_adjustment_set_value( act->private_data->adj, base + step ); + break; + + case BUMP_DOWN: + gtk_adjustment_set_value( act->private_data->adj, base - step ); + break; + + case BUMP_PAGE_DOWN: + gtk_adjustment_set_value( act->private_data->adj, base - page ); + break; + + case BUMP_BOTTOM: + gtk_adjustment_set_value( act->private_data->adj, lower ); + break; + + default: + if ( what >= BUMP_CUSTOM ) { + guint index = what - BUMP_CUSTOM; + if ( index < g_list_length( act->private_data->descriptions ) ) { + EgeAdjustmentDescr* descr = (EgeAdjustmentDescr*)g_list_nth_data( act->private_data->descriptions, index ); + if ( descr ) { + gtk_adjustment_set_value( act->private_data->adj, descr->value ); + } + } + } + } + } +} + +static void create_single_menu_item( GCallback toggleCb, int val, GtkWidget* menu, EgeAdjustmentAction* act, GtkWidget** dst, GSList** group, gdouble num, gboolean active ) +{ + char* str = 0; + EgeAdjustmentDescr* marker = 0; + GList* cur = act->private_data->descriptions; + + while ( cur ) { + EgeAdjustmentDescr* descr = (EgeAdjustmentDescr*)cur->data; + gdouble delta = num - descr->value; + if ( delta < 0.0 ) { + delta = -delta; + } + if ( delta < act->private_data->epsilon ) { + marker = descr; + break; + } + cur = g_list_next( cur ); + } + + str = g_strdup_printf( act->private_data->format, num, + ((marker && marker->descr) ? ": " : ""), + ((marker && marker->descr) ? marker->descr : "")); + + *dst = gtk_radio_menu_item_new_with_label( *group, str ); + if ( !*group) { + *group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(*dst) ); + } + if ( active ) { + gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(*dst), TRUE ); + } + gtk_menu_shell_append( GTK_MENU_SHELL(menu), *dst ); + g_object_set_qdata( G_OBJECT(*dst), gDataName, act ); + + g_signal_connect( G_OBJECT(*dst), "toggled", toggleCb, GINT_TO_POINTER(val) ); + + g_free(str); +} + +static GList* flush_explicit_items( GList* descriptions, + GCallback toggleCb, + int val, + GtkWidget* menu, + EgeAdjustmentAction* act, + GtkWidget** dst, + GSList** group, + gdouble num ) +{ + GList* cur = descriptions; + + if ( cur ) { + gdouble valUpper = num + act->private_data->epsilon; + gdouble valLower = num - act->private_data->epsilon; + + EgeAdjustmentDescr* descr = (EgeAdjustmentDescr*)cur->data; + + while ( cur && descr && (descr->value >= valLower) ) { + if ( descr->value > valUpper ) { + create_single_menu_item( toggleCb, val + g_list_position(act->private_data->descriptions, cur), menu, act, dst, group, descr->value, FALSE ); + } + + cur = g_list_previous( cur ); + descr = cur ? (EgeAdjustmentDescr*)cur->data : 0; + } + } + + return cur; +} + +static GtkWidget* create_popup_number_menu( EgeAdjustmentAction* act ) +{ + GtkWidget* menu = gtk_menu_new(); + + GSList* group = 0; + GtkWidget* single = 0; + + GList* addOns = g_list_last( act->private_data->descriptions ); + + gdouble base = gtk_adjustment_get_value( act->private_data->adj ); + gdouble lower = 0.0; + gdouble upper = 0.0; + gdouble step = 0.0; + gdouble page = 0.0; + g_object_get( G_OBJECT(act->private_data->adj), + "lower", &lower, + "upper", &upper, + "step-increment", &step, + "page-increment", &page, + NULL ); + + + if ( base < upper ) { + addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, upper ); + create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_TOP, menu, act, &single, &group, upper, FALSE ); + if ( (base + page) < upper ) { + addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, base + page ); + create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_PAGE_UP, menu, act, &single, &group, base + page, FALSE ); + } + if ( (base + step) < upper ) { + addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, base + step ); + create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_UP, menu, act, &single, &group, base + step, FALSE ); + } + } + + addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, base ); + create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_NONE, menu, act, &single, &group, base, TRUE ); + + if ( base > lower ) { + if ( (base - step) > lower ) { + addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, base - step ); + create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_DOWN, menu, act, &single, &group, base - step, FALSE ); + } + if ( (base - page) > lower ) { + addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, base - page ); + create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_PAGE_DOWN, menu, act, &single, &group, base - page, FALSE ); + } + addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, lower ); + create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_BOTTOM, menu, act, &single, &group, lower, FALSE ); + } + + if ( act->private_data->descriptions ) { + gdouble value = ((EgeAdjustmentDescr*)act->private_data->descriptions->data)->value; + addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, value ); + } + + return menu; +} + +static GtkWidget* create_menu_item( GtkAction* action ) +{ + GtkWidget* item = 0; + + if ( IS_EGE_ADJUSTMENT_ACTION(action) ) { + EgeAdjustmentAction* act = EGE_ADJUSTMENT_ACTION( action ); + GValue value; + GtkWidget* subby = 0; + + memset( &value, 0, sizeof(value) ); + g_value_init( &value, G_TYPE_STRING ); + g_object_get_property( G_OBJECT(action), "label", &value ); + + item = gtk_menu_item_new_with_label( g_value_get_string( &value ) ); + + subby = create_popup_number_menu( act ); + gtk_menu_item_set_submenu( GTK_MENU_ITEM(item), subby ); + gtk_widget_show_all( subby ); + g_value_unset( &value ); + } else { + item = GTK_ACTION_CLASS(ege_adjustment_action_parent_class)->create_menu_item( action ); + } + + return item; +} + +static void value_changed_cb( GtkSpinButton* spin, EgeAdjustmentAction* act ) +{ + if ( gtk_widget_has_focus( GTK_WIDGET(spin) ) ) { + gint start = 0, end = 0; + if (GTK_IS_EDITABLE(spin) && gtk_editable_get_selection_bounds (GTK_EDITABLE(spin), &start, &end) + && start != end) { + // #167846, #363000 If the spin button has a selection, its probably + // because we got here from a Tab key from another spin, if so dont defocus + return; + } + ege_adjustment_action_defocus( act ); + } +} + +static gboolean event_cb( EgeAdjustmentAction* act, GdkEvent* evt ) +{ + gboolean handled = FALSE; + if ( evt->type == GDK_BUTTON_PRESS ) { + if ( evt->button.button == 3 ) { + if ( IS_EGE_ADJUSTMENT_ACTION(act) ) { + GdkEventButton* btnevt = (GdkEventButton*)evt; + GtkWidget* menu = create_popup_number_menu(act); + gtk_widget_show_all( menu ); + gtk_menu_popup( GTK_MENU(menu), NULL, NULL, NULL, NULL, btnevt->button, btnevt->time ); + } + handled = TRUE; + } + } + + return handled; +} + +static GtkWidget* create_tool_item( GtkAction* action ) +{ + GtkWidget* item = 0; + + if ( IS_EGE_ADJUSTMENT_ACTION(action) ) { + EgeAdjustmentAction* act = EGE_ADJUSTMENT_ACTION( action ); + GtkWidget* spinbutton = 0; +#if GTK_CHECK_VERSION(3,0,0) + GtkWidget* hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); +#else + GtkWidget* hb = gtk_hbox_new( FALSE, 5 ); +#endif + GValue value; + memset( &value, 0, sizeof(value) ); + g_value_init( &value, G_TYPE_STRING ); + g_object_get_property( G_OBJECT(action), "short_label", &value ); + + if ( act->private_data->appearanceMode == APPEARANCE_FULL ) { + /* Slider */ + spinbutton = gimp_spin_scale_new (act->private_data->adj, g_value_get_string( &value ), 0); + gtk_widget_set_size_request(spinbutton, 100, -1); + + } else if ( act->private_data->appearanceMode == APPEARANCE_MINIMAL ) { + spinbutton = gtk_scale_button_new( GTK_ICON_SIZE_MENU, 0, 100, 2, 0 ); + gtk_scale_button_set_adjustment( GTK_SCALE_BUTTON(spinbutton), act->private_data->adj ); + gtk_scale_button_set_icons( GTK_SCALE_BUTTON(spinbutton), floogles ); + } else { + if ( gFactoryCb ) { + spinbutton = gFactoryCb( act->private_data->adj, act->private_data->climbRate, act->private_data->digits, act->private_data->unitTracker ); + } else { + spinbutton = gtk_spin_button_new( act->private_data->adj, act->private_data->climbRate, act->private_data->digits ); + } + } + + item = GTK_WIDGET( gtk_tool_item_new() ); + + { + GValue tooltip; + memset( &tooltip, 0, sizeof(tooltip) ); + g_value_init( &tooltip, G_TYPE_STRING ); + g_object_get_property( G_OBJECT(action), "tooltip", &tooltip ); + const gchar* tipstr = g_value_get_string( &tooltip ); + if ( tipstr && *tipstr ) { + gtk_widget_set_tooltip_text( spinbutton, tipstr ); + } + g_value_unset( &tooltip ); + } + + if ( act->private_data->appearanceMode != APPEARANCE_FULL ) { + GtkWidget* filler1 = gtk_label_new(" "); + gtk_box_pack_start( GTK_BOX(hb), filler1, FALSE, FALSE, 0 ); + + /* Use an icon if available or use short-label */ + if ( act->private_data->iconId && strcmp( act->private_data->iconId, "" ) != 0 ) { + GtkWidget* icon = sp_icon_new( act->private_data->iconSize, act->private_data->iconId ); + gtk_box_pack_start( GTK_BOX(hb), icon, FALSE, FALSE, 0 ); + } else { + GtkWidget* lbl = gtk_label_new( g_value_get_string( &value ) ? g_value_get_string( &value ) : "wwww" ); + gtk_misc_set_alignment( GTK_MISC(lbl), 1.0, 0.5 ); + gtk_box_pack_start( GTK_BOX(hb), lbl, FALSE, FALSE, 0 ); + } + } + + if ( act->private_data->appearanceMode == APPEARANCE_FULL ) { + gtk_box_pack_start( GTK_BOX(hb), spinbutton, TRUE, TRUE, 0 ); + } else { + gtk_box_pack_start( GTK_BOX(hb), spinbutton, FALSE, FALSE, 0 ); + } + + gtk_container_add( GTK_CONTAINER(item), hb ); + + if ( act->private_data->selfId ) { + g_object_set_data( G_OBJECT(spinbutton), act->private_data->selfId, spinbutton ); + } + + g_signal_connect( G_OBJECT(spinbutton), "focus-in-event", G_CALLBACK(focus_in_cb), action ); + g_signal_connect( G_OBJECT(spinbutton), "focus-out-event", G_CALLBACK(focus_out_cb), action ); + g_signal_connect( G_OBJECT(spinbutton), "key-press-event", G_CALLBACK(keypress_cb), action ); + + g_signal_connect( G_OBJECT(spinbutton), "value-changed", G_CALLBACK(value_changed_cb), action ); + + g_signal_connect_swapped( G_OBJECT(spinbutton), "event", G_CALLBACK(event_cb), action ); + if ( act->private_data->appearanceMode == APPEARANCE_FULL ) { + /* */ + } else if ( act->private_data->appearanceMode == APPEARANCE_MINIMAL ) { + /* */ + } else { + gtk_entry_set_width_chars( GTK_ENTRY(spinbutton), act->private_data->digits + 3 ); + } + + gtk_widget_show_all( item ); + + /* Shrink or whatnot after shown */ + if ( act->private_data->toolPost ) { + act->private_data->toolPost( item ); + } + + g_value_unset( &value ); + } else { + item = GTK_ACTION_CLASS(ege_adjustment_action_parent_class)->create_tool_item( action ); + } + + return item; +} + +static void connect_proxy( GtkAction *action, GtkWidget *proxy ) +{ + GTK_ACTION_CLASS(ege_adjustment_action_parent_class)->connect_proxy( action, proxy ); +} + +static void disconnect_proxy( GtkAction *action, GtkWidget *proxy ) +{ + GTK_ACTION_CLASS(ege_adjustment_action_parent_class)->disconnect_proxy( action, proxy ); +} + +void ege_adjustment_action_defocus( EgeAdjustmentAction* action ) +{ + if ( action->private_data->transferFocus ) { + if ( action->private_data->focusWidget ) { + gtk_widget_grab_focus( action->private_data->focusWidget ); + } + } +} + +gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data ) +{ + (void)event; + if ( IS_EGE_ADJUSTMENT_ACTION(data) ) { + EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( data ); + if ( GTK_IS_SPIN_BUTTON(widget) ) { + action->private_data->lastVal = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) ); + } else if ( GTK_IS_SCALE_BUTTON(widget) ) { + action->private_data->lastVal = gtk_scale_button_get_value( GTK_SCALE_BUTTON(widget) ); + } else if (GTK_IS_RANGE(widget) ) { + action->private_data->lastVal = gtk_range_get_value( GTK_RANGE(widget) ); + } + action->private_data->transferFocus = TRUE; + } + + return FALSE; /* report event not consumed */ +} + +static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data ) +{ + (void)widget; + (void)event; + if ( IS_EGE_ADJUSTMENT_ACTION(data) ) { + EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( data ); + action->private_data->transferFocus = FALSE; + } + + return FALSE; /* report event not consumed */ +} + + +static gboolean process_tab( GtkWidget* widget, int direction ) +{ + gboolean handled = FALSE; + GtkWidget* parent = gtk_widget_get_parent(widget); + GtkWidget* gp = parent ? gtk_widget_get_parent(parent) : 0; + GtkWidget* ggp = gp ? gtk_widget_get_parent(gp) : 0; + + if ( ggp && GTK_IS_TOOLBAR(ggp) ) { + GList* kids = gtk_container_get_children( GTK_CONTAINER(ggp) ); + if ( kids ) { + GtkWidget* curr = widget; + while ( curr && (gtk_widget_get_parent(curr) != ggp) ) { + curr = gtk_widget_get_parent( curr ); + } + if ( curr ) { + GList* mid = g_list_find( kids, curr ); + while ( mid ) { + mid = ( direction < 0 ) ? g_list_previous(mid) : g_list_next(mid); + if ( mid && GTK_IS_TOOL_ITEM(mid->data) ) { + /* potential target */ + GtkWidget* child = gtk_bin_get_child( GTK_BIN(mid->data) ); + if ( child && GTK_IS_BOX(child) ) { /* could be ours */ + GList* subChildren = gtk_container_get_children( GTK_CONTAINER(child) ); + if ( subChildren ) { + GList* last = g_list_last(subChildren); + if ( last && GTK_IS_SPIN_BUTTON(last->data) && gtk_widget_is_sensitive( GTK_WIDGET(last->data) ) ) { + gtk_widget_grab_focus( GTK_WIDGET(last->data) ); + handled = TRUE; + mid = 0; /* to stop loop */ + } + + g_list_free(subChildren); + } + } + } + } + } + g_list_free( kids ); + } + } + + return handled; +} + +gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data ) +{ + gboolean wasConsumed = FALSE; /* default to report event not consumed */ + EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION(data); + guint key = 0; + gdk_keymap_translate_keyboard_state( gdk_keymap_get_for_display( gdk_display_get_default() ), + event->hardware_keycode, (GdkModifierType)event->state, + 0, &key, 0, 0, 0 ); + + switch ( key ) { + case GDK_KEY_Escape: + { + action->private_data->transferFocus = TRUE; + gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal ); + ege_adjustment_action_defocus( action ); + wasConsumed = TRUE; + } + break; + + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + { + action->private_data->transferFocus = TRUE; + ege_adjustment_action_defocus( action ); + wasConsumed = TRUE; + } + break; + + case GDK_KEY_Tab: + { + action->private_data->transferFocus = FALSE; + wasConsumed = process_tab( widget, 1 ); + } + break; + + case GDK_KEY_ISO_Left_Tab: + { + action->private_data->transferFocus = FALSE; + wasConsumed = process_tab( widget, -1 ); + } + break; + + case GDK_KEY_Up: + case GDK_KEY_KP_Up: + { + action->private_data->transferFocus = FALSE; + gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) ); + gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val + action->private_data->step ); + wasConsumed = TRUE; + } + break; + + case GDK_KEY_Down: + case GDK_KEY_KP_Down: + { + action->private_data->transferFocus = FALSE; + gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) ); + gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val - action->private_data->step ); + wasConsumed = TRUE; + } + break; + + case GDK_KEY_Page_Up: + case GDK_KEY_KP_Page_Up: + { + action->private_data->transferFocus = FALSE; + gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) ); + gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val + action->private_data->page ); + wasConsumed = TRUE; + } + break; + + case GDK_KEY_Page_Down: + case GDK_KEY_KP_Page_Down: + { + action->private_data->transferFocus = FALSE; + gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) ); + gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val - action->private_data->page ); + wasConsumed = TRUE; + } + break; + + case GDK_KEY_z: + case GDK_KEY_Z: + { + action->private_data->transferFocus = FALSE; + gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal ); + wasConsumed = TRUE; + } + break; + + } + + return wasConsumed; +} +/* + 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 : diff --git a/src/widgets/ege-adjustment-action.h b/src/widgets/ege-adjustment-action.h new file mode 100644 index 000000000..8cfaa3e52 --- /dev/null +++ b/src/widgets/ege-adjustment-action.h @@ -0,0 +1,192 @@ +#ifndef SEEN_EGE_ADJUSTMENT_ACTION +#define SEEN_EGE_ADJUSTMENT_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 Adjustment Action. + * + * The Initial Developer of the Original Code is + * Jon A. Cruz. + * Portions created by the Initial Developer are Copyright (C) 2006 + * 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 ***** */ + +/** \file + * GtkAction subclass that represents a GtkAdjustment value. + */ + +/* Note: this file should be kept compilable as both .cpp and .c */ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + + +#define EGE_ADJUSTMENT_ACTION_TYPE ( ege_adjustment_action_get_type() ) +#define EGE_ADJUSTMENT_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_CAST( (obj), EGE_ADJUSTMENT_ACTION_TYPE, EgeAdjustmentAction) ) +#define EGE_ADJUSTMENT_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( (klass), EGE_ADJUSTMENT_ACTION_TYPE, EgeAdjustmentActionClass) ) +#define IS_EGE_ADJUSTMENT_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_TYPE( (obj), EGE_ADJUSTMENT_ACTION_TYPE) ) +#define IS_EGE_ADJUSTMENT_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE( (klass), EGE_ADJUSTMENT_ACTION_TYPE) ) +#define EGE_ADJUSTMENT_ACTION_GET_CLASS( obj ) ( G_TYPE_INSTANCE_GET_CLASS( (obj), EGE_ADJUSTMENT_ACTION_TYPE, EgeAdjustmentActionClass) ) + +typedef struct _EgeAdjustmentAction EgeAdjustmentAction; +typedef struct _EgeAdjustmentActionClass EgeAdjustmentActionClass; +typedef struct _EgeAdjustmentActionPrivate EgeAdjustmentActionPrivate; + +namespace Inkscape { + namespace UI { + namespace Widget { + class UnitTracker; + } + } +} + +/** + * Instance structure of EgeAdjustmentAction. + */ +struct _EgeAdjustmentAction +{ + /** Parent instance structure. */ + GtkAction action; + + /** Pointer to private instance data. */ + EgeAdjustmentActionPrivate *private_data; +}; + +/** + * Class structure of EgeAdjustmentAction. + */ +struct _EgeAdjustmentActionClass +{ + /** Parent class structure. */ + GtkActionClass parent_class; +}; + +/** Standard Gtk type function */ +GType ege_adjustment_action_get_type( void ); + + +/* + * Note: This normally could be implemented via a GType property for the class to construct, + * but gtkmm classes implemented in C++ only will often not funciton properly. + * + */ + +/** Callback type for widgets creation factory */ +typedef GtkWidget* (*EgeCreateAdjWidgetCB)( GtkAdjustment *adjustment, gdouble climb_rate, guint digits, Inkscape::UI::Widget::UnitTracker *unit_tracker ); + +/** + * Sets a factory callback to be used to create the specific widget. + * + * @param factoryCb the callback to use to create custom widgets, NULL to use the default. + */ +void ege_adjustment_action_set_compact_tool_factory( EgeCreateAdjWidgetCB factoryCb ); + + +/** + * Creates a new EgeAdjustmentAction instance. + * This is a GtkAction subclass that manages a value stored in a + * GtkAdjustment. + * + * @param adjustment The GtkAdjustment to manage. + * @param name Functional name for the action. + * @param label Display label for the action. + * @param tooltip Tooltip for the action. + * @param stock_id Icon id to use. + * @param climb_rate Used for created widgets. + * @param digits Used for created widgets. + * @param unit_tracker Used to store unit. + */ +EgeAdjustmentAction* ege_adjustment_action_new( GtkAdjustment* adjustment, + const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id, + gdouble climb_rate, + guint digits, + Inkscape::UI::Widget::UnitTracker *unit_tracker + ); +/** + * Returns a pointer to the GtkAdjustment represented by the given + * EgeAdjustmentAction. + * + * @param action The action to fetch the GtkAdjustment for. + */ +GtkAdjustment* ege_adjustment_action_get_adjustment( EgeAdjustmentAction* action ); + +/** + * Sets the GtkWidget to return focus to. + * This is used to be able to transfer focus back out of a toolbar. + * + * @param action The action to set the widget for. + * @param widget The widget to return focus to after editing. + * @see ege_adjustment_action_get_focuswidget + */ +void ege_adjustment_action_set_focuswidget( EgeAdjustmentAction* action, GtkWidget* widget ); + +/** + * Returns a pointer to the GtkWidget to return focus to after changing + * the value. + * + * @param action The action to fetch the focus widget for. + * @returns A pointer to the widget to return focus to, NULL if none set. + * @see ege_adjustment_action_set_focuswidget + */ +GtkWidget* ege_adjustment_action_get_focuswidget( EgeAdjustmentAction* action ); + +/** + * Set a list of values with labels to explicitly include in menus. + * + * @param action The action to set explicit entries for. + * @param descriptions Array of descriptions to include. + * Descriptions will be matched one-for-one with numbers in the 'values' array. + * @param values Array of values to include. + * Values will be matched one-for-one with numbers in the 'descriptions' array. + * @param count Number of items in the 'descriptions' and 'values' arrays. + */ +void ege_adjustment_action_set_descriptions( EgeAdjustmentAction* action, gchar const** descriptions, gdouble const* values, guint count ); + +/** + * Sets a hint to be used in determining the display form. + * This is the XForms style 'appearance' hint: "full", "compact", "minimal". + * + * @param action The action to set the tooltip column for. + * @param val The value of the appearance hint. + */ +void ege_adjustment_action_set_appearance( EgeAdjustmentAction* action, gchar const* val ); + +/** Callback type for post-creation 'fixup' pass on generated widgets */ +typedef void (*EgeWidgetFixup)(GtkWidget *widget); + + +G_END_DECLS + +#endif /* SEEN_EGE_ADJUSTMENT_ACTION */ diff --git a/src/widgets/ege-output-action.cpp b/src/widgets/ege-output-action.cpp new file mode 100644 index 000000000..5dece8e91 --- /dev/null +++ b/src/widgets/ege-output-action.cpp @@ -0,0 +1,245 @@ +/* -*- 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 Output 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 <string.h> + +#include <gtk/gtk.h> + +#include "widgets/ege-output-action.h" + + +static void ege_output_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ); +static void ege_output_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ); +static void fixup_labels( GObject *gobject, GParamSpec *arg1, gpointer user_data ); + +/* static GtkWidget* create_menu_item( GtkAction* action ); */ +static GtkWidget* create_tool_item( GtkAction* action ); + +struct _EgeOutputActionPrivate +{ + gboolean useMarkup; +}; + +#define EGE_OUTPUT_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), EGE_OUTPUT_ACTION_TYPE, EgeOutputActionPrivate ) ) + +enum { + PROP_USE_MARKUP = 1, +}; + +G_DEFINE_TYPE(EgeOutputAction, ege_output_action, GTK_TYPE_ACTION); + +void ege_output_action_class_init( EgeOutputActionClass* klass ) +{ + if ( klass ) { + GObjectClass* objClass = G_OBJECT_CLASS( klass ); + + objClass->get_property = ege_output_action_get_property; + objClass->set_property = ege_output_action_set_property; + +/* klass->parent_class.create_menu_item = create_menu_item; */ + klass->parent_class.create_tool_item = create_tool_item; + + g_object_class_install_property( objClass, + PROP_USE_MARKUP, + g_param_spec_boolean( "use-markup", + "UseMarkup", + "If markup should be used", + FALSE, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_type_class_add_private( klass, sizeof(EgeOutputActionClass) ); + } +} + + +void ege_output_action_init( EgeOutputAction* action ) +{ + action->private_data = EGE_OUTPUT_ACTION_GET_PRIVATE( action ); + action->private_data->useMarkup = FALSE; + + g_signal_connect( action, "notify", G_CALLBACK( fixup_labels ), NULL ); +} + +EgeOutputAction* ege_output_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id ) +{ + GObject* obj = (GObject*)g_object_new( EGE_OUTPUT_ACTION_TYPE, + "name", name, + "label", label, + "tooltip", tooltip, + "stock_id", stock_id, + "use-markup", FALSE, + NULL ); + + EgeOutputAction* action = EGE_OUTPUT_ACTION( obj ); + + return action; +} + +gboolean ege_output_action_get_use_markup( EgeOutputAction* action ) +{ + g_return_val_if_fail( IS_EGE_OUTPUT_ACTION(action), FALSE ); + + return action->private_data->useMarkup; +} + +void ege_output_action_set_use_markup( EgeOutputAction* action, gboolean setting ) +{ + g_object_set( G_OBJECT(action), "use-markup", setting, NULL ); +} + +void ege_output_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ) +{ + EgeOutputAction* action = EGE_OUTPUT_ACTION( obj ); + switch ( propId ) { + case PROP_USE_MARKUP: + g_value_set_boolean( value, action->private_data->useMarkup ); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + +void ege_output_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ) +{ + EgeOutputAction* action = EGE_OUTPUT_ACTION( obj ); + switch ( propId ) { + case PROP_USE_MARKUP: + { + action->private_data->useMarkup = g_value_get_boolean( value ); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + + +/* static GtkWidget* create_menu_item( GtkAction* action ) */ + +GtkWidget* create_tool_item( GtkAction* action ) +{ + GtkWidget* item = 0; + + if ( IS_EGE_OUTPUT_ACTION(action) ) + { + GValue value; +#if GTK_CHECK_VERSION(3,0,0) + GtkWidget* hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); +#else + GtkWidget* hb = gtk_hbox_new( FALSE, 5 ); +#endif + GtkWidget* lbl = 0; + memset( &value, 0, sizeof(value) ); + + g_value_init( &value, G_TYPE_STRING ); + g_object_get_property( G_OBJECT(action), "short_label", &value ); + const gchar* sss = g_value_get_string( &value ); + + item = GTK_WIDGET( gtk_tool_item_new() ); + + lbl = gtk_label_new( " " ); + gtk_container_add( GTK_CONTAINER(hb), lbl ); + + if ( EGE_OUTPUT_ACTION(action)->private_data->useMarkup ) { + lbl = gtk_label_new(NULL); + gtk_label_set_markup( GTK_LABEL(lbl), sss ? sss : " " ); + } else { + lbl = gtk_label_new( sss ? sss : " " ); + } + gtk_container_add( GTK_CONTAINER(hb), lbl ); + + lbl = gtk_label_new( " " ); + gtk_container_add( GTK_CONTAINER(hb), lbl ); + + gtk_container_add( GTK_CONTAINER(item), hb ); + + gtk_widget_show_all( item ); + + g_value_unset( &value ); + } else { + item = GTK_ACTION_CLASS(ege_output_action_parent_class)->create_tool_item( action ); + } + + return item; +} + +void fixup_labels( GObject *gobject, GParamSpec *arg1, gpointer user_data ) +{ + /* TODO: handle 'use-markup' getting changed also */ + + if ( arg1 && arg1->name && (strcmp("label", arg1->name) == 0) ) { + GSList* proxies = gtk_action_get_proxies( GTK_ACTION(gobject) ); + gchar* str = 0; + g_object_get( gobject, "label", &str, NULL ); + (void)user_data; + 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 ) { + if ( GTK_IS_BOX(children->data) ) { + children = gtk_container_get_children( GTK_CONTAINER(children->data) ); + if ( children && g_list_next(children) ) { + GtkWidget* child = GTK_WIDGET( g_list_next(children)->data ); + if ( GTK_IS_LABEL(child) ) { + GtkLabel* lbl = GTK_LABEL(child); + if ( EGE_OUTPUT_ACTION(gobject)->private_data->useMarkup ) { + gtk_label_set_markup( lbl, str ); + } else { + gtk_label_set_text( lbl, str ); + } + } + } + } + } + } + proxies = g_slist_next( proxies ); + } + g_free( str ); + } +} + diff --git a/src/widgets/ege-output-action.h b/src/widgets/ege-output-action.h new file mode 100644 index 000000000..0f4e21805 --- /dev/null +++ b/src/widgets/ege-output-action.h @@ -0,0 +1,120 @@ +#ifndef SEEN_EGE_OUTPUT_ACTION +#define SEEN_EGE_OUTPUT_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 Output 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 ***** */ + +/** \file + * GtkAction subclass that represents a string for output. + */ + +/* Note: this file should be kept compilable as both .cpp and .c */ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + + +#define EGE_OUTPUT_ACTION_TYPE ( ege_output_action_get_type() ) +#define EGE_OUTPUT_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_CAST( (obj), EGE_OUTPUT_ACTION_TYPE, EgeOutputAction) ) +#define EGE_OUTPUT_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( (klass), EGE_OUTPUT_ACTION_TYPE, EgeOutputActionClass) ) +#define IS_EGE_OUTPUT_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_TYPE( (obj), EGE_OUTPUT_ACTION_TYPE) ) +#define IS_EGE_OUTPUT_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE( (klass), EGE_OUTPUT_ACTION_TYPE) ) +#define EGE_OUTPUT_ACTION_GET_CLASS( obj ) ( G_TYPE_INSTANCE_GET_CLASS( (obj), EGE_OUTPUT_ACTION_TYPE, EgeOutputActionClass) ) + +typedef struct _EgeOutputAction EgeOutputAction; +typedef struct _EgeOutputActionClass EgeOutputActionClass; +typedef struct _EgeOutputActionPrivate EgeOutputActionPrivate; + +/** + * Instance structure of EgeOutputAction. + */ +struct _EgeOutputAction +{ + /** Parent instance structure. */ + GtkAction action; + + /** Pointer to private instance data. */ + EgeOutputActionPrivate *private_data; +}; + +/** + * Class structure of EgeOutputAction. + */ +struct _EgeOutputActionClass +{ + /** Parent class structure. */ + GtkActionClass parent_class; +}; + +/** Standard Gtk type function */ +GType ege_output_action_get_type( void ); + +/** + * Creates a new EgeOutputAction instance. + * This is a GtkAction subclass that displays a string. + * + * @param name Functional name for the action. + * @param label Display label for the action. + * @param tooltip Tooltip for the action. + * @param stock_id Icon id to use. + */ +EgeOutputAction* ege_output_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id ); + +/** + * Return whether or not the displayed text is interpreted as markup. + * + * @param action The action to fetch the markup state for. + * @return True if the text is to be interpreted as markup, false otherwise. + */ +gboolean ege_output_action_get_use_markup( EgeOutputAction* action ); + +/** + * Sets whether or not the displayed text is interpreted as markup. + * + * @param action The action to set the markup state for. + * @param setting True if the text is to be interpreted as markup, false otherwise. + */ +void ege_output_action_set_use_markup( EgeOutputAction* action, gboolean setting ); + +G_END_DECLS + +#endif /* SEEN_EGE_OUTPUT_ACTION */ diff --git a/src/widgets/ege-select-one-action.cpp b/src/widgets/ege-select-one-action.cpp new file mode 100644 index 000000000..ab86c49f8 --- /dev/null +++ b/src/widgets/ege-select-one-action.cpp @@ -0,0 +1,1153 @@ +/* -*- 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) 2010 + * 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 <string.h> + +#include <gtk/gtk.h> + +#include "widgets/ege-select-one-action.h" + +enum { + CHANGED = 0, + LAST_SIGNAL}; + +static void ege_select_one_action_finalize( GObject* 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 gint find_text_index(EgeSelectOneAction *act, gchar const* text); +static void commit_pending_change(EgeSelectOneAction *act); +static void resync_active( EgeSelectOneAction* act, gint active, gboolean override ); +static void resync_sensitive( EgeSelectOneAction* act ); +static void combo_entry_changed_cb( GtkEntry* widget, gpointer user_data ); +static gboolean combo_entry_focus_lost_cb( GtkWidget *widget, GdkEventFocus *event, gpointer data ); +static void combo_changed_cb( GtkComboBox* widget, gpointer user_data ); +static void menu_toggled_cb( GtkWidget* obj, gpointer data ); +static void proxy_action_chagned_cb( GtkRadioAction* action, GtkRadioAction* current, gpointer user_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 int scan_max_width( GtkTreeModel *model, gint labelColumn ); + +static guint signals[LAST_SIGNAL] = {0}; +static GQuark gDataName = 0; + + +enum { + APPEARANCE_UNKNOWN = -1, + APPEARANCE_NONE = 0, + APPEARANCE_FULL, /* label, then all choices represented by separate buttons */ + APPEARANCE_COMPACT, /* label, then choices in a drop-down menu */ + APPEARANCE_MINIMAL, /* no label, just choices in a drop-down menu */ +}; + +enum { + SELECTION_UNKNOWN = -1, + SELECTION_CLOSED = 0, + SELECTION_OPEN, +}; + +struct _EgeSelectOneActionPrivate +{ + gint active; + gint labelColumn; + gint iconColumn; + gint tooltipColumn; + gint sensitiveColumn; + gint appearanceMode; + gint selectionMode; + gint iconSize; + GType radioActionType; + GtkTreeModel* model; + gchar *iconProperty; + gchar *appearance; + gchar *selection; + gchar *activeText; + gchar *pendingText; +}; + +#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_LABEL_COLUMN, + PROP_ICON_COLUMN, + PROP_TOOLTIP_COLUMN, + PROP_SENSITIVE_COLUMN, + PROP_ICON_PROP, + PROP_ICON_SIZE, + PROP_APPEARANCE, + PROP_SELECTION +}; + +G_DEFINE_TYPE(EgeSelectOneAction, ege_select_one_action, GTK_TYPE_ACTION); + +GtkTreeModel *ege_select_one_action_get_model(EgeSelectOneAction* action ){ + return GTK_TREE_MODEL(action->private_data->model); +} +void ege_select_one_action_class_init( EgeSelectOneActionClass* klass ) +{ + if ( klass ) { + GObjectClass* objClass = G_OBJECT_CLASS( klass ); + + gDataName = g_quark_from_string("ege-select1-action"); + + objClass->finalize = ege_select_one_action_finalize; + 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", + -1, G_MAXINT, 0, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_LABEL_COLUMN, + g_param_spec_int( "label-column", + "Display Column", + "The column of the model that holds display strings", + 0, G_MAXINT, 0, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_ICON_COLUMN, + g_param_spec_int( "icon-column", + "Icon Column", + "The column of the model that holds display icon name", + -1, G_MAXINT, -1, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_TOOLTIP_COLUMN, + g_param_spec_int( "tooltip-column", + "Tooltip Column", + "The column of the model that holds tooltip strings", + -1, G_MAXINT, -1, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_SENSITIVE_COLUMN, + g_param_spec_int( "sensitive-column", + "Sensitive Column", + "The column of the model that holds sensitive state", + -1, G_MAXINT, -1, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_ICON_PROP, + g_param_spec_string( "icon-property", + "Icon Property", + "Target icon property", + "", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_ICON_SIZE, + g_param_spec_int( "icon-size", + "Icon Size", + "Target icon size", + -1, G_MAXINT, -1, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_APPEARANCE, + g_param_spec_string( "appearance", + "Appearance hint", + "A hint for how to display", + "", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_SELECTION, + g_param_spec_string( "selection", + "Selection set open or closed", + "'open' to allow edits/additions, 'closed' to disallow.", + "", + (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->active = 0; + action->private_data->labelColumn = 0; + action->private_data->iconColumn = -1; + action->private_data->tooltipColumn = -1; + action->private_data->sensitiveColumn = -1; + action->private_data->appearanceMode = APPEARANCE_NONE; + action->private_data->selectionMode = SELECTION_CLOSED; + action->private_data->radioActionType = 0; + action->private_data->model = 0; + action->private_data->iconProperty = g_strdup("stock-id"); + action->private_data->iconSize = -1; + action->private_data->appearance = 0; + action->private_data->selection = 0; + action->private_data->activeText = 0; + action->private_data->pendingText = 0; + +/* g_signal_connect( action, "notify", G_CALLBACK( fixup_labels ), NULL ); */ +} + +void ege_select_one_action_finalize( GObject* object ) +{ + EgeSelectOneAction *action = EGE_SELECT_ONE_ACTION( object ); + + g_free( action->private_data->iconProperty ); + g_free( action->private_data->appearance ); + g_free( action->private_data->selection ); + + if ( G_OBJECT_CLASS(ege_select_one_action_parent_class)->finalize ) { + (*G_OBJECT_CLASS(ege_select_one_action_parent_class)->finalize)(object); + } +} + +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, + "icon-property", "stock-id", + 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; +} + +gchar *ege_select_one_action_get_active_text( EgeSelectOneAction* action ) +{ + GtkTreeIter iter; + gchar *str = 0; + g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); + + if ( action->private_data->active >= 0) { + if ( gtk_tree_model_iter_nth_child( action->private_data->model, &iter, NULL, action->private_data->active ) ) { + gtk_tree_model_get( action->private_data->model, &iter, + action->private_data->labelColumn, &str, + -1 ); + } + } else if ( (action->private_data->active == -1) && action->private_data->activeText ) { + str = g_strdup(action->private_data->activeText); + } + + return str; +} + +void ege_select_one_action_set_active_text( EgeSelectOneAction* action, gchar const *text ) +{ + g_return_if_fail( IS_EGE_SELECT_ONE_ACTION(action) ); + + if (action->private_data->activeText) { + g_free( action->private_data->activeText ); + } + action->private_data->activeText = g_strdup(text); + + if (action->private_data->active != -1) { + g_object_set( G_OBJECT(action), "active", -1, NULL ); + } else { + resync_active( action, -1, TRUE ); + } +} + +void ege_select_one_action_set_active( EgeSelectOneAction* action, gint val ) +{ + g_object_set( G_OBJECT(action), "active", val, NULL ); +} + +void ege_select_one_action_update_sensitive( EgeSelectOneAction* action ) +{ + if( action->private_data->sensitiveColumn < 0 ) { + g_warning( "ege_select_one_action: Attempt to update sensitivity of item without sensitive column\n" ); + return; + } + resync_sensitive( action ); +} + +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->labelColumn; +} + +void ege_select_one_action_set_label_column( EgeSelectOneAction* action, gint col ) +{ + g_object_set( G_OBJECT(action), "label-column", col, NULL ); +} + +gint ege_select_one_action_get_icon_column( EgeSelectOneAction* action ) +{ + g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); + return action->private_data->iconColumn; +} + +void ege_select_one_action_set_icon_column( EgeSelectOneAction* action, gint col ) +{ + g_object_set( G_OBJECT(action), "icon-column", col, NULL ); +} + +gint ege_select_one_action_get_icon_size( EgeSelectOneAction* action ) +{ + g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); + return action->private_data->iconSize; +} + +void ege_select_one_action_set_icon_size( EgeSelectOneAction* action, gint size ) +{ + g_object_set( G_OBJECT(action), "icon-size", size, NULL ); +} + +gint ege_select_one_action_get_tooltip_column( EgeSelectOneAction* action ) +{ + g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); + return action->private_data->tooltipColumn; +} + +void ege_select_one_action_set_tooltip_column( EgeSelectOneAction* action, gint col ) +{ + g_object_set( G_OBJECT(action), "tooltip-column", col, NULL ); +} + +gint ege_select_one_action_get_sensitive_column( EgeSelectOneAction* action ) +{ + g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); + return action->private_data->sensitiveColumn; +} + +void ege_select_one_action_set_sensitive_column( EgeSelectOneAction* action, gint col ) +{ + g_object_set( G_OBJECT(action), "sensitive-column", col, NULL ); +} + +void ege_select_one_action_set_appearance( EgeSelectOneAction* action, gchar const* val ) +{ + g_object_set( G_OBJECT(action), "appearance", val, NULL ); +} + +void ege_select_one_action_set_selection( EgeSelectOneAction* action, gchar const* val ) +{ + g_object_set( G_OBJECT(action), "selection", val, 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_LABEL_COLUMN: + g_value_set_int( value, action->private_data->labelColumn ); + break; + + case PROP_ICON_COLUMN: + g_value_set_int( value, action->private_data->iconColumn ); + break; + + case PROP_TOOLTIP_COLUMN: + g_value_set_int( value, action->private_data->tooltipColumn ); + break; + + case PROP_SENSITIVE_COLUMN: + g_value_set_int( value, action->private_data->sensitiveColumn ); + break; + + case PROP_ICON_PROP: + g_value_set_string( value, action->private_data->iconProperty ); + break; + + case PROP_ICON_SIZE: + g_value_set_int( value, action->private_data->iconSize ); + break; + + case PROP_APPEARANCE: + g_value_set_string( value, action->private_data->appearance ); + break; + + case PROP_SELECTION: + g_value_set_string( value, action->private_data->selection ); + 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 ), FALSE ); + } + break; + + case PROP_LABEL_COLUMN: + { + action->private_data->labelColumn = g_value_get_int( value ); + } + break; + + case PROP_ICON_COLUMN: + { + action->private_data->iconColumn = g_value_get_int( value ); + } + break; + + case PROP_TOOLTIP_COLUMN: + { + action->private_data->tooltipColumn = g_value_get_int( value ); + } + break; + + case PROP_SENSITIVE_COLUMN: + { + action->private_data->sensitiveColumn = g_value_get_int( value ); + } + break; + + case PROP_ICON_PROP: + { + gchar* tmp = action->private_data->iconProperty; + gchar* newVal = g_value_dup_string( value ); + action->private_data->iconProperty = newVal; + g_free( tmp ); + } + break; + + case PROP_ICON_SIZE: + { + action->private_data->iconSize = g_value_get_int( value ); + } + break; + + case PROP_APPEARANCE: + { + gchar* tmp = action->private_data->appearance; + gchar* newVal = g_value_dup_string( value ); + action->private_data->appearance = newVal; + g_free( tmp ); + + if ( !action->private_data->appearance || (strcmp("", newVal) == 0) ) { + action->private_data->appearanceMode = APPEARANCE_NONE; + } else if ( strcmp("full", newVal) == 0 ) { + action->private_data->appearanceMode = APPEARANCE_FULL; + } else if ( strcmp("compact", newVal) == 0 ) { + action->private_data->appearanceMode = APPEARANCE_COMPACT; + } else if ( strcmp("minimal", newVal) == 0 ) { + action->private_data->appearanceMode = APPEARANCE_MINIMAL; + } else { + action->private_data->appearanceMode = APPEARANCE_UNKNOWN; + } + } + break; + + case PROP_SELECTION: + { + gchar* tmp = action->private_data->selection; + gchar* newVal = g_value_dup_string( value ); + action->private_data->selection = newVal; + g_free( tmp ); + + if ( !action->private_data->selection || (strcmp("closed", newVal) == 0) ) { + action->private_data->selectionMode = SELECTION_CLOSED; + } else if ( strcmp("open", newVal) == 0 ) { + action->private_data->selectionMode = SELECTION_OPEN; + } else { + action->private_data->selectionMode = SELECTION_UNKNOWN; + } + } + 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; + 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 ); + + gboolean 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->labelColumn, &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); + str = 0; + + 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 = GTK_ACTION_CLASS(ege_select_one_action_parent_class)->create_menu_item( action ); + } + + return item; +} + + +void ege_select_one_action_set_radio_action_type( EgeSelectOneAction* action, GType radioActionType ) +{ + (void)action; + + if ( g_type_is_a( radioActionType, GTK_TYPE_RADIO_ACTION ) ) { + action->private_data->radioActionType = radioActionType; + } else { + g_warning("Passed in type '%s' is not derived from '%s'", g_type_name(radioActionType), g_type_name(GTK_TYPE_RADIO_ACTION) ); + } +} + +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() ); + + if ( act->private_data->appearanceMode == APPEARANCE_FULL ) { +#if GTK_CHECK_VERSION(3,0,0) + GtkWidget* holder = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous(GTK_BOX(holder), FALSE); +#else + GtkWidget* holder = gtk_hbox_new( FALSE, 0 ); +#endif + + GtkRadioAction* ract = 0; + GSList* group = 0; + GtkTreeIter iter; + gboolean valid = FALSE; + gint index = 0; + + { + gchar* sss = 0; + g_object_get( G_OBJECT(action), "short_label", &sss, NULL ); + // If short_label not defined, g_object_get will return label. + // This hack allows a label to be used with a drop-down menu when + // no label is used with a set of icons that are self-explanatory. + if (sss && strcmp( sss, "NotUsed" ) != 0 ) { + GtkWidget* lbl = gtk_label_new(sss); + gtk_box_pack_start( GTK_BOX(holder), lbl, FALSE, FALSE, 4 ); + } + g_free( sss ); + sss = 0; + } + + valid = gtk_tree_model_get_iter_first( act->private_data->model, &iter ); + while ( valid ) { + gchar* str = 0; + gchar* tip = 0; + gchar* iconId = 0; + gboolean sens = true; + /* + gint size = 0; + */ + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->labelColumn, &str, + -1 ); + if ( act->private_data->iconColumn >= 0 ) { + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->iconColumn, &iconId, + -1 ); + } + if ( act->private_data->tooltipColumn >= 0 ) { + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->tooltipColumn, &tip, + -1 ); + } + if ( act->private_data->sensitiveColumn >= 0 ) { + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->sensitiveColumn, &sens, + -1 ); + } + + if ( act->private_data->radioActionType ) { + void* obj = g_object_new( act->private_data->radioActionType, + "name", "Name 1", + "label", str, + "tooltip", tip, + "value", index, + /* + "iconId", iconId, + "iconSize", size, + */ + NULL ); + if ( iconId ) { + g_object_set( G_OBJECT(obj), act->private_data->iconProperty, iconId, NULL ); + } + + if ( act->private_data->iconProperty ) { + /* TODO get this string to be set instead of hardcoded */ + if ( act->private_data->iconSize >= 0 ) { + g_object_set( G_OBJECT(obj), "iconSize", act->private_data->iconSize, NULL ); + } + } + + ract = GTK_RADIO_ACTION(obj); + } else { + ract = gtk_radio_action_new( "Name 1", str, tip, iconId, index ); + } + + if ( act->private_data->sensitiveColumn >= 0 ) { + gtk_action_set_sensitive( GTK_ACTION(ract), sens ); + } + + gtk_radio_action_set_group( ract, group ); + group = gtk_radio_action_get_group( ract ); + + if ( index == act->private_data->active ) { + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(ract), TRUE ); + } + g_signal_connect( G_OBJECT(ract), "changed", G_CALLBACK( proxy_action_chagned_cb ), act ); + + GtkWidget* sub = gtk_action_create_tool_item( GTK_ACTION(ract) ); + gtk_activatable_set_related_action( GTK_ACTIVATABLE (sub), GTK_ACTION(ract) ); + gtk_tool_item_set_tooltip_text( GTK_TOOL_ITEM(sub), tip ); + + gtk_box_pack_start( GTK_BOX(holder), sub, FALSE, FALSE, 0 ); + + g_free( str ); + g_free( tip ); + g_free( iconId ); + + index++; + valid = gtk_tree_model_iter_next( act->private_data->model, &iter ); + } + + g_object_set_data( G_OBJECT(holder), "ege-proxy_action-group", group ); + + gtk_container_add( GTK_CONTAINER(item), holder ); + } else { +#if GTK_CHECK_VERSION(3,0,0) + GtkWidget* holder = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); + gtk_box_set_homogeneous(GTK_BOX(holder), FALSE); +#else + GtkWidget *holder = gtk_hbox_new( FALSE, 4 ); +#endif + + GtkEntry *entry = 0; + GtkWidget *normal; + + if (act->private_data->selectionMode == SELECTION_OPEN) { + normal = gtk_combo_box_new_with_model_and_entry (act->private_data->model); + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (normal), act->private_data->labelColumn); + + GtkWidget *child = gtk_bin_get_child( GTK_BIN(normal) ); + if (GTK_IS_ENTRY(child)) { + int maxUsed = scan_max_width( act->private_data->model, act->private_data->labelColumn ); + GtkEntryCompletion *complete = 0; + entry = GTK_ENTRY(child); + gtk_entry_set_width_chars(entry, maxUsed); /* replace with property */ + + complete = gtk_entry_completion_new(); + gtk_entry_completion_set_model( complete, act->private_data->model ); + gtk_entry_completion_set_text_column( complete, act->private_data->labelColumn ); + gtk_entry_completion_set_inline_completion( complete, FALSE ); + gtk_entry_completion_set_inline_selection( complete, FALSE ); + gtk_entry_completion_set_popup_completion( complete, TRUE ); + gtk_entry_completion_set_popup_set_width( complete, FALSE ); + gtk_entry_set_completion( entry, complete ); + + g_signal_connect( G_OBJECT(child), "activate", G_CALLBACK(combo_entry_changed_cb), act ); + g_signal_connect( G_OBJECT(child), "focus-out-event", G_CALLBACK(combo_entry_focus_lost_cb), act ); + } + } + else { + GtkCellRenderer * renderer = NULL; + normal = gtk_combo_box_new_with_model( act->private_data->model ); + if ( act->private_data->iconColumn >= 0 ) { + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(normal), renderer, TRUE ); + + /* "icon-name" */ + gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(normal), renderer, "stock-id", act->private_data->iconColumn ); + } + + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(normal), renderer, TRUE ); + gtk_cell_layout_add_attribute( GTK_CELL_LAYOUT(normal), renderer, "text", act->private_data->labelColumn ); + } + + gtk_combo_box_set_active( GTK_COMBO_BOX(normal), act->private_data->active ); + if ( entry && (act->private_data->active == -1) ) { + gtk_entry_set_text( entry, act->private_data->activeText ); + } + + g_signal_connect( G_OBJECT(normal), "changed", G_CALLBACK(combo_changed_cb), action ); + + g_object_set_data( G_OBJECT(holder), "ege-combo-box", normal ); + g_object_set_data( G_OBJECT(act), "ege-combo-box", normal ); + + if (act->private_data->appearanceMode == APPEARANCE_COMPACT) { + gchar* sss = 0; + g_object_get( G_OBJECT(action), "short_label", &sss, NULL ); + if (sss) { + GtkWidget* lbl = gtk_label_new(sss); + gtk_box_pack_start( GTK_BOX(holder), lbl, FALSE, FALSE, 4 ); + g_free( sss ); + sss = 0; + } + } + + gtk_box_pack_start( GTK_BOX(holder), normal, FALSE, FALSE, 0 ); + + { + GtkWidget *align = gtk_alignment_new(0, 0.5, 0, 0); + gtk_container_add( GTK_CONTAINER(align), holder); + gtk_container_add( GTK_CONTAINER(item), align ); + } + } + + gtk_widget_show_all( item ); + } else { + item = GTK_ACTION_CLASS(ege_select_one_action_parent_class)->create_tool_item( action ); + } + + return item; +} + + +void connect_proxy( GtkAction *action, GtkWidget *proxy ) +{ + GTK_ACTION_CLASS(ege_select_one_action_parent_class)->connect_proxy( action, proxy ); +} + +void disconnect_proxy( GtkAction *action, GtkWidget *proxy ) +{ + GTK_ACTION_CLASS(ege_select_one_action_parent_class)->disconnect_proxy( action, proxy ); +} + + +void resync_active( EgeSelectOneAction* act, gint active, gboolean override ) +{ + if ( override || (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 ) { + gpointer combodata = g_object_get_data( G_OBJECT(children->data), "ege-combo-box" ); + if (!combodata && GTK_IS_ALIGNMENT(children->data)) { + GList *other = gtk_container_get_children( GTK_CONTAINER(children->data) ); + combodata = g_object_get_data( G_OBJECT(other->data), "ege-combo-box" ); + } + if ( GTK_IS_COMBO_BOX(combodata) ) { + GtkComboBox* combo = GTK_COMBO_BOX(combodata); + if ((active == -1) && (gtk_combo_box_get_has_entry(combo))) { + gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo))), act->private_data->activeText); + } else if ( gtk_combo_box_get_active(combo) != active ) { + gtk_combo_box_set_active( combo, active ); + } + } else if ( GTK_IS_BOX(children->data) ) { + gpointer data = g_object_get_data( G_OBJECT(children->data), "ege-proxy_action-group" ); + if ( data ) { + GSList* group = (GSList*)data; + GtkRadioAction* oneAction = GTK_RADIO_ACTION(group->data); + gint hot = gtk_radio_action_get_current_value( oneAction ); + if ( hot != active ) { + /*gtk_radio_action_set_current_value( oneAction, active );*/ + gint value = 0; + while ( group ) { + GtkRadioAction* possible = GTK_RADIO_ACTION(group->data); + g_object_get( G_OBJECT(possible), "value", &value, NULL ); + if ( value == active ) { + /* Found the group member to set active */ + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(possible), TRUE ); + break; + } + + group = g_slist_next(group); + } + } + } + } + } + } 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 resync_sensitive( EgeSelectOneAction* act ) +{ + 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 ) { + gpointer combodata = g_object_get_data( G_OBJECT(children->data), "ege-combo-box" ); + if (!combodata && GTK_IS_ALIGNMENT(children->data)) { + GList *other = gtk_container_get_children( GTK_CONTAINER(children->data) ); + combodata = g_object_get_data( G_OBJECT(other->data), "ege-combo-box" ); + } + if ( GTK_IS_COMBO_BOX(combodata) ) { + /* Not implemented */ + } else if ( GTK_IS_BOX(children->data) ) { + gpointer data = g_object_get_data( G_OBJECT(children->data), "ege-proxy_action-group" ); + if ( data ) { + GSList* group = (GSList*)data; + // List is backwards in group as compared to GtkTreeModel, we better do matching. + while ( group ) { + GtkRadioAction* ract = GTK_RADIO_ACTION(group->data); + const gchar* label = gtk_action_get_label( GTK_ACTION( ract ) ); + + // Search for matching GtkTreeModel entry + GtkTreeIter iter; + gboolean valid; + valid = gtk_tree_model_get_iter_first( act->private_data->model, &iter ); + gboolean sens = true; + + while( valid ) { + + gchar* str = 0; + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->labelColumn, &str, + -1 ); + + if( strcmp( label, str ) == 0 ) { + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->sensitiveColumn, &sens, + -1 ); + break; + } + g_free( str ); + + valid = gtk_tree_model_iter_next( act->private_data->model, &iter ); + } + + gtk_action_set_sensitive( GTK_ACTION(ract), sens ); + group = g_slist_next(group); + } + } + } + } + } else if ( GTK_IS_MENU_ITEM(proxies->data) ) { + /* Not implemented */ + } + + 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); + gchar *text = 0; + GtkComboBox *cb = GTK_COMBO_BOX (widget); + gint newActive = gtk_combo_box_get_active(widget); + + if (gtk_combo_box_get_has_entry (cb)) { + GtkBin *bin = GTK_BIN (cb); + GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (bin)); + + text = g_strdup (gtk_entry_get_text (entry)); + } + else { + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (cb, &iter)) { + GtkTreeModel *model = gtk_combo_box_get_model (cb); + + gtk_tree_model_get (model, &iter, 0, &text, -1); + } + } + + if (!text) { + /* User probably deleted the data in the model */ + return; + } + + if (newActive == -1) { + /* indicates the user is entering text for a custom aka "open" value */ + if (act->private_data->pendingText && text && (strcmp(act->private_data->pendingText, text) == 0) ) { + /* The currently entered data matches the last seen */ + } else { + if (act->private_data->pendingText) { + g_free(act->private_data->pendingText); + } + act->private_data->pendingText = text; + text = 0; + } + } else if (newActive != act->private_data->active) { + if (act->private_data->pendingText) { + g_free(act->private_data->pendingText); + act->private_data->pendingText = 0; + } + g_object_set( G_OBJECT(act), "active", newActive, NULL ); + } + + if (text) { + g_free(text); + text = 0; + } +} + +gboolean combo_entry_focus_lost_cb( GtkWidget *widget, GdkEventFocus *event, gpointer data ) +{ + EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION(data); + (void)widget; + (void)event; + + commit_pending_change(act); + + return FALSE; +} + +void combo_entry_changed_cb( GtkEntry* widget, gpointer user_data ) +{ + EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION(user_data); + (void)widget; + commit_pending_change(act); +} + +void commit_pending_change(EgeSelectOneAction *act) +{ + if (act->private_data->pendingText) { + if (act->private_data->activeText && (strcmp(act->private_data->pendingText, act->private_data->activeText) == 0)) { + /* Was the same value */ + g_free(act->private_data->pendingText); + act->private_data->pendingText = 0; + } else { + gint matching = find_text_index(act, act->private_data->pendingText); + + if (act->private_data->activeText) { + g_free(act->private_data->activeText); + } + act->private_data->activeText = act->private_data->pendingText; + act->private_data->pendingText = 0; + + if (matching >= 0) { + g_free(act->private_data->activeText); + act->private_data->activeText = 0; + g_object_set( G_OBJECT(act), "active", matching, NULL ); + } else if (act->private_data->active != -1) { + g_object_set( G_OBJECT(act), "active", -1, NULL ); + } else { + resync_active( act, -1, TRUE ); + } + } + } +} + +gint find_text_index(EgeSelectOneAction *act, gchar const* text) +{ + gint index = -1; + + if (text) { + GtkTreeIter iter; + gboolean valid = gtk_tree_model_get_iter_first( act->private_data->model, &iter ); + gint curr = 0; + while ( valid && (index < 0) ) { + gchar* str = 0; + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->labelColumn, &str, + -1 ); + + if (str && (strcmp(text, str) == 0)) { + index = curr; + } + + g_free(str); + str = 0; + + curr++; + valid = gtk_tree_model_iter_next( act->private_data->model, &iter ); + } + } + + return index; +} + +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 ); + } +} + +void proxy_action_chagned_cb( GtkRadioAction* action, GtkRadioAction* current, gpointer user_data ) +{ + (void)current; + if ( gtk_toggle_action_get_active( GTK_TOGGLE_ACTION(action) ) ) { + EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION(user_data); + gint newActive = gtk_radio_action_get_current_value( action ); + if ( newActive != act->private_data->active ) { + g_object_set( G_OBJECT(act), "active", newActive, NULL ); + } + } +} + +int scan_max_width( GtkTreeModel *model, gint labelColumn ) +{ + int maxUsed = 0; + GtkTreeIter iter; + gboolean valid = gtk_tree_model_get_iter_first( model, &iter ); + while ( valid ) { + gchar* str = NULL; + gtk_tree_model_get( model, &iter, labelColumn, &str, -1 ); + if (str != NULL){ + int count = strlen(str); + if (count > maxUsed) { + maxUsed = count; + } + g_free(str); + } + valid = gtk_tree_model_iter_next( model, &iter ); + } + return maxUsed; +} + +/* + 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 : diff --git a/src/widgets/ege-select-one-action.h b/src/widgets/ege-select-one-action.h new file mode 100644 index 000000000..0c5cecaa3 --- /dev/null +++ b/src/widgets/ege-select-one-action.h @@ -0,0 +1,245 @@ +#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) 2010 + * 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 ***** */ + +/** \file + * GtkAction subclass that represents a set of values the user may select + * one from at a given time. + * This can manifest as a popup menu, a ComboBox, a set of toggle buttons, + * etc. + */ + +/* Note: this file should be kept compilable as both .cpp and .c */ + +#include <gtk/gtk.h> + +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; + +/** + * Instance structure of EgeSelectOneAction. + */ +struct _EgeSelectOneAction +{ + /** Parent instance structure. */ + GtkAction action; + + /** Pointer to private instance data. */ + EgeSelectOneActionPrivate *private_data; +}; + +/** + * Class structure of EgeSelectOneAction. + */ +struct _EgeSelectOneActionClass +{ + /** Parent class structure. */ + GtkActionClass parent_class; + + void (*changed) (EgeSelectOneAction* action); +}; + +/** Standard Gtk type function */ +GType ege_select_one_action_get_type( void ); + +/** + * Creates a new EgeSelectOneAction instance. + * This is a GtkAction subclass that represents a set of values the user + * may select one from at a given time. + * This can manifest as a popup menu, a ComboBox, a set of toggle buttons, + * etc. + * + * @param name Functional name for the action. + * @param label Display label for the action. + * @param tooltip Tooltip for the action. + * @param stock_id Icon id to use. + * @param model the source of choices to present. + */ +EgeSelectOneAction* ege_select_one_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id, + GtkTreeModel* model ); + +GtkTreeModel *ege_select_one_action_get_model(EgeSelectOneAction* action ); + +/** + * Returns the index of the currently selected item. + * + * @param action The action to fetch the selected index for. + */ +gint ege_select_one_action_get_active( EgeSelectOneAction* action ); + +/** + * Returns the text of the currently selected item. + * + * @param action The action to fetch the text for. + * @return the selected text. The caller is responsible to call g_free() on it when done. + */ +gchar *ege_select_one_action_get_active_text( EgeSelectOneAction* action ); + +/** + * Sets the text of the currently selected item. + * + * @param action The action to fetch the text for. + * @param text the text to set. + */ +void ege_select_one_action_set_active_text( EgeSelectOneAction* action, gchar const *text ); + +/** + * Sets the currently selected item. + * + * @param action The action to fetch the selected index for. + * @param val index of the item to make selected. + */ +void ege_select_one_action_set_active( EgeSelectOneAction* action, gint val ); + +//void ege_select_one_action_set_sensitive( EgeSelectOneAction *action, gint val, gboolean sensitive ); + +/** + * Update sensitive parameters. + * @param action The action to update. + */ +void ege_select_one_action_update_sensitive( EgeSelectOneAction *action ); + +/** + * Returns the column used for the display label. + * + * @param action The action to fetch the label column for. + */ +gint ege_select_one_action_get_label_column( EgeSelectOneAction* action ); + +/** + * Sets the column used for the display label. + * + * @param action The action to set the label column for. + * @param col column to use. + */ +void ege_select_one_action_set_label_column( EgeSelectOneAction* action, gint col ); + + +/** + * Returns the column used for the display icon. + * + * @param action The action to fetch the icon column for. + */ +gint ege_select_one_action_get_icon_column( EgeSelectOneAction* action ); + +/** + * Sets the column used for the display icon. + * + * @param action The action to set the icon column for. + * @param col column to use. + */ +void ege_select_one_action_set_icon_column( EgeSelectOneAction* action, gint col ); + +gint ege_select_one_action_get_icon_size( EgeSelectOneAction* action ); + +void ege_select_one_action_set_icon_size( EgeSelectOneAction* action, gint size ); + + +/** + * Returns the column used for the tooltip. + * + * @param action The action to fetch the tooltip column for. + */ +gint ege_select_one_action_get_tooltip_column( EgeSelectOneAction* action ); + +/** + * Sets the column used for the tooltip. + * + * @param action The action to set the tooltip column for. + * @param col column to use. + */ +void ege_select_one_action_set_tooltip_column( EgeSelectOneAction* action, gint col ); + + +/** + * Returns the column used for tracking sensitivity. + * + * @param action The action to fetch the sensitive column for. + */ +gint ege_select_one_action_get_sensitive_column( EgeSelectOneAction* action ); + +/** + * Sets the column used for sensitivity (if any). + * + * @param action The action to set the sensitive column for. + * @param col column to use. + */ +void ege_select_one_action_set_sensitive_column( EgeSelectOneAction* action, gint col ); + + +/** + * Sets a hint to be used in determining the display form. + * This is the XForms style 'appearance' hint: "full", "compact", "minimal". + * + * @param action The action to set the tooltip column for. + * @param val The value of the appearance hint. + */ +void ege_select_one_action_set_appearance( EgeSelectOneAction* action, gchar const* val ); + +/** + * Sets to allow or disallow free entry additions to the list. + * The default is "closed" selections that do not allow additions/edits. + * This is the XForms functional 'selection' attribute: "open", "closed". + * + * @param action The action to set the tooltip column for. + * @param val The value of the selection attribute. + */ +void ege_select_one_action_set_selection( EgeSelectOneAction *action, gchar const* val ); + +/* bit of a work-around */ +void ege_select_one_action_set_radio_action_type( EgeSelectOneAction* action, GType radioActionType ); + +G_END_DECLS + +#endif /* SEEN_EGE_SELECT_ONE_ACTION */ diff --git a/src/widgets/eraser-toolbar.cpp b/src/widgets/eraser-toolbar.cpp index 14e7cbf4e..f547cbd8b 100644 --- a/src/widgets/eraser-toolbar.cpp +++ b/src/widgets/eraser-toolbar.cpp @@ -35,9 +35,9 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" #include "preferences.h" #include "toolbox.h" #include "ui/icon-names.h" diff --git a/src/widgets/gradient-toolbar.cpp b/src/widgets/gradient-toolbar.cpp index f5a99f3e7..b6378b251 100644 --- a/src/widgets/gradient-toolbar.cpp +++ b/src/widgets/gradient-toolbar.cpp @@ -22,12 +22,12 @@ #include "desktop.h" #include "document-undo.h" #include "document.h" -#include "ege-adjustment-action.h" -#include "ege-select-one-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-select-one-action.h" #include "gradient-chemistry.h" #include "gradient-drag.h" #include "gradient-toolbar.h" -#include "ink-action.h" +#include "widgets/ink-action.h" #include "macros.h" #include "preferences.h" #include "selection.h" @@ -1109,7 +1109,7 @@ void sp_gradient_toolbox_prep(SPDesktop * desktop, GtkActionGroup* mainActions, GtkTreeIter iter; gtk_list_store_append( model, &iter ); - gtk_list_store_set( model, &iter, 0, _("None"), 1, SP_GRADIENT_SPREAD_PAD, -1 ); + gtk_list_store_set( model, &iter, 0, C_("Gradient repeat type", "None"), 1, SP_GRADIENT_SPREAD_PAD, -1 ); gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, _("Reflected"), 1, SP_GRADIENT_SPREAD_REFLECT, -1 ); diff --git a/src/widgets/ink-action.cpp b/src/widgets/ink-action.cpp new file mode 100644 index 000000000..5941c31e4 --- /dev/null +++ b/src/widgets/ink-action.cpp @@ -0,0 +1,638 @@ +#include "widgets/icon.h" +#include "icon-size.h" +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "widgets/ink-action.h" + +#include "widgets/button.h" + +static void ink_action_finalize( GObject* obj ); +static void ink_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ); +static void ink_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ); + +static GtkWidget* ink_action_create_menu_item( GtkAction* action ); +static GtkWidget* ink_action_create_tool_item( GtkAction* action ); + +struct _InkActionPrivate +{ + gchar* iconId; + Inkscape::IconSize iconSize; +}; + +#define INK_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), INK_ACTION_TYPE, InkActionPrivate ) ) + +G_DEFINE_TYPE(InkAction, ink_action, GTK_TYPE_ACTION); + +enum { + PROP_INK_ID = 1, + PROP_INK_SIZE +}; + +static void ink_action_class_init( InkActionClass* klass ) +{ + if ( klass ) { + GObjectClass * objClass = G_OBJECT_CLASS( klass ); + + objClass->finalize = ink_action_finalize; + objClass->get_property = ink_action_get_property; + objClass->set_property = ink_action_set_property; + + klass->parent_class.create_menu_item = ink_action_create_menu_item; + klass->parent_class.create_tool_item = ink_action_create_tool_item; + /*klass->parent_class.connect_proxy = connect_proxy;*/ + /*klass->parent_class.disconnect_proxy = disconnect_proxy;*/ + + g_object_class_install_property( objClass, + PROP_INK_ID, + g_param_spec_string( "iconId", + "Icon ID", + "The id for the icon", + "", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_INK_SIZE, + g_param_spec_int( "iconSize", + "Icon Size", + "The size the icon", + (int)Inkscape::ICON_SIZE_MENU, + (int)Inkscape::ICON_SIZE_DECORATION, + (int)Inkscape::ICON_SIZE_SMALL_TOOLBAR, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_type_class_add_private( klass, sizeof(InkActionClass) ); + } +} + +static void ink_action_init( InkAction* action ) +{ + action->private_data = INK_ACTION_GET_PRIVATE( action ); + action->private_data->iconId = 0; + action->private_data->iconSize = Inkscape::ICON_SIZE_SMALL_TOOLBAR; +} + +static void ink_action_finalize( GObject* obj ) +{ + InkAction* action = INK_ACTION( obj ); + + g_free( action->private_data->iconId ); + g_free( action->private_data ); + +} + +//Any strings passed in should already be localised +InkAction* ink_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *inkId, + Inkscape::IconSize size ) +{ + GObject* obj = (GObject*)g_object_new( INK_ACTION_TYPE, + "name", name, + "label", label, + "tooltip", tooltip, + "iconId", inkId, + "iconSize", size, + NULL ); + + InkAction* action = INK_ACTION( obj ); + + return action; +} + +static void ink_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ) +{ + InkAction* action = INK_ACTION( obj ); + (void)action; + switch ( propId ) { + case PROP_INK_ID: + { + g_value_set_string( value, action->private_data->iconId ); + } + break; + + case PROP_INK_SIZE: + { + g_value_set_int( value, action->private_data->iconSize ); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + +void ink_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ) +{ + InkAction* action = INK_ACTION( obj ); + (void)action; + switch ( propId ) { + case PROP_INK_ID: + { + gchar* tmp = action->private_data->iconId; + action->private_data->iconId = g_value_dup_string( value ); + g_free( tmp ); + } + break; + + case PROP_INK_SIZE: + { + action->private_data->iconSize = (Inkscape::IconSize)g_value_get_int( value ); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } + } +} + +static GtkWidget* ink_action_create_menu_item( GtkAction* action ) +{ + InkAction* act = INK_ACTION( action ); + GtkWidget* item = 0; + + if ( act->private_data->iconId ) { + gchar* label = 0; + g_object_get( G_OBJECT(act), "label", &label, NULL ); + + item = gtk_image_menu_item_new_with_mnemonic( label ); + GtkWidget* child = sp_icon_new( Inkscape::ICON_SIZE_MENU, act->private_data->iconId ); + // TODO this work-around is until SPIcon will live properly inside of a popup menu + if ( SP_IS_ICON(child) ) { + SPIcon* icon = SP_ICON(child); + sp_icon_fetch_pixbuf( icon ); + GdkPixbuf* target = icon->pb; + if ( target ) { + child = gtk_image_new_from_pixbuf( target ); + gtk_widget_set_sensitive(child, gtk_action_is_sensitive(action)); + gtk_widget_destroy( GTK_WIDGET(icon) ); + } + } + gtk_widget_show_all( child ); + gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(item), child ); + + g_free( label ); + label = 0; + } else { + item = GTK_ACTION_CLASS(ink_action_parent_class)->create_menu_item( action ); + } + + return item; +} + +static GtkWidget* ink_action_create_tool_item( GtkAction* action ) +{ + InkAction* act = INK_ACTION( action ); + GtkWidget* item = GTK_ACTION_CLASS(ink_action_parent_class)->create_tool_item(action); + + if ( act->private_data->iconId ) { + if ( GTK_IS_TOOL_BUTTON(item) ) { + GtkToolButton* button = GTK_TOOL_BUTTON(item); + + GtkWidget* child = sp_icon_new( act->private_data->iconSize, act->private_data->iconId ); + gtk_tool_button_set_icon_widget( button, child ); + } else { + // For now trigger a warning but don't do anything else + GtkToolButton* button = GTK_TOOL_BUTTON(item); + (void)button; + } + } + + // TODO investigate if needed + gtk_widget_show_all( item ); + + return item; +} + + + +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ + + +static void ink_toggle_action_finalize( GObject* obj ); +static void ink_toggle_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ); +static void ink_toggle_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ); + +static GtkWidget* ink_toggle_action_create_menu_item( GtkAction* action ); +static GtkWidget* ink_toggle_action_create_tool_item( GtkAction* action ); + +static void ink_toggle_action_update_icon( InkToggleAction* action ); + +struct _InkToggleActionPrivate +{ + gchar* iconId; + Inkscape::IconSize iconSize; +}; + +#define INK_TOGGLE_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), INK_TOGGLE_ACTION_TYPE, InkToggleActionPrivate ) ) + +G_DEFINE_TYPE(InkToggleAction, ink_toggle_action, GTK_TYPE_TOGGLE_ACTION); + +static void ink_toggle_action_class_init( InkToggleActionClass* klass ) +{ + if ( klass ) { + GObjectClass * objClass = G_OBJECT_CLASS( klass ); + + objClass->finalize = ink_toggle_action_finalize; + objClass->get_property = ink_toggle_action_get_property; + objClass->set_property = ink_toggle_action_set_property; + + klass->parent_class.parent_class.create_menu_item = ink_toggle_action_create_menu_item; + klass->parent_class.parent_class.create_tool_item = ink_toggle_action_create_tool_item; + /*klass->parent_class.connect_proxy = connect_proxy;*/ + /*klass->parent_class.disconnect_proxy = disconnect_proxy;*/ + + g_object_class_install_property( objClass, + PROP_INK_ID, + g_param_spec_string( "iconId", + "Icon ID", + "The id for the icon", + "", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_INK_SIZE, + g_param_spec_int( "iconSize", + "Icon Size", + "The size the icon", + (int)Inkscape::ICON_SIZE_MENU, + (int)99, + (int)Inkscape::ICON_SIZE_SMALL_TOOLBAR, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_type_class_add_private( klass, sizeof(InkToggleActionClass) ); + } +} + +static void ink_toggle_action_init( InkToggleAction* action ) +{ + action->private_data = INK_TOGGLE_ACTION_GET_PRIVATE( action ); + action->private_data->iconId = 0; + action->private_data->iconSize = Inkscape::ICON_SIZE_SMALL_TOOLBAR; +} + +static void ink_toggle_action_finalize( GObject* obj ) +{ + InkToggleAction* action = INK_TOGGLE_ACTION( obj ); + + g_free( action->private_data->iconId ); + g_free( action->private_data ); + +} + +InkToggleAction* ink_toggle_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *inkId, + Inkscape::IconSize size, + SPAttributeEnum attr) +{ + GObject* obj = (GObject*)g_object_new( INK_TOGGLE_ACTION_TYPE, + "name", name, + "label", label, + "tooltip", tooltip, + "iconId", inkId, + "iconSize", Inkscape::getRegisteredIconSize(size), + //"SP_ATTR_INKSCAPE", attr, // Why doesn't this work and do I need to use g_object_set_data below? + NULL ); + + g_object_set_data(obj, "SP_ATTR_INKSCAPE", GINT_TO_POINTER(attr)); + InkToggleAction* action = INK_TOGGLE_ACTION( obj ); + + return action; +} + +static void ink_toggle_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ) +{ + InkToggleAction* action = INK_TOGGLE_ACTION( obj ); + (void)action; + switch ( propId ) { + case PROP_INK_ID: + { + g_value_set_string( value, action->private_data->iconId ); + } + break; + + case PROP_INK_SIZE: + { + g_value_set_int( value, action->private_data->iconSize ); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + +void ink_toggle_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ) +{ + InkToggleAction* action = INK_TOGGLE_ACTION( obj ); + (void)action; + switch ( propId ) { + case PROP_INK_ID: + { + gchar* tmp = action->private_data->iconId; + action->private_data->iconId = g_value_dup_string( value ); + g_free( tmp ); + + ink_toggle_action_update_icon( action ); + } + break; + + case PROP_INK_SIZE: + { + action->private_data->iconSize = (Inkscape::IconSize)g_value_get_int( value ); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } + } +} + +static GtkWidget* ink_toggle_action_create_menu_item( GtkAction* action ) +{ + GtkWidget* item = GTK_TOGGLE_ACTION_CLASS(ink_toggle_action_parent_class)->parent_class.create_menu_item(action); + + return item; +} + +static GtkWidget* ink_toggle_action_create_tool_item( GtkAction* action ) +{ + InkToggleAction* act = INK_TOGGLE_ACTION( action ); + + GtkWidget* item = GTK_TOGGLE_ACTION_CLASS(ink_toggle_action_parent_class)->parent_class.create_tool_item(action); + if ( GTK_IS_TOOL_BUTTON(item) ) { + GtkToolButton* button = GTK_TOOL_BUTTON(item); + if ( act->private_data->iconId ) { + GtkWidget* child = sp_icon_new( act->private_data->iconSize, act->private_data->iconId ); + GtkWidget* align = gtk_alignment_new( 0.5, 0.5, 0.0, 0.0 ); + gtk_container_add( GTK_CONTAINER(align), child ); + gtk_tool_button_set_icon_widget( button, align ); + } else { + gchar *label = 0; + g_object_get( G_OBJECT(action), "short_label", &label, NULL ); + gtk_tool_button_set_label( button, label ); + g_free( label ); + label = 0; + } + } else { + // For now trigger a warning but don't do anything else + GtkToolButton* button = GTK_TOOL_BUTTON(item); + (void)button; + } + gtk_widget_show_all( item ); + + return item; +} + + +static void ink_toggle_action_update_icon( InkToggleAction* action ) +{ + if ( action ) { + GSList* proxies = gtk_action_get_proxies( GTK_ACTION(action) ); + while ( proxies ) { + if ( GTK_IS_TOOL_ITEM(proxies->data) ) { + if ( GTK_IS_TOOL_BUTTON(proxies->data) ) { + GtkToolButton* button = GTK_TOOL_BUTTON(proxies->data); + + GtkWidget* child = sp_icon_new( action->private_data->iconSize, action->private_data->iconId ); + GtkWidget* align = gtk_alignment_new( 0.5, 0.5, 0.0, 0.0 ); + gtk_container_add( GTK_CONTAINER(align), child ); + gtk_widget_show_all( align ); + gtk_tool_button_set_icon_widget( button, align ); + } + } + + proxies = g_slist_next( proxies ); + } + } +} + + +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ + + +static void ink_radio_action_finalize( GObject* obj ); +static void ink_radio_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ); +static void ink_radio_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ); + +static GtkWidget* ink_radio_action_create_menu_item( GtkAction* action ); +static GtkWidget* ink_radio_action_create_tool_item( GtkAction* action ); + +struct _InkRadioActionPrivate +{ + gchar* iconId; + Inkscape::IconSize iconSize; +}; + +#define INK_RADIO_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), INK_RADIO_ACTION_TYPE, InkRadioActionPrivate ) ) + +G_DEFINE_TYPE(InkRadioAction, ink_radio_action, GTK_TYPE_RADIO_ACTION); + +static void ink_radio_action_class_init( InkRadioActionClass* klass ) +{ + if ( klass ) { + GObjectClass * objClass = G_OBJECT_CLASS( klass ); + + objClass->finalize = ink_radio_action_finalize; + objClass->get_property = ink_radio_action_get_property; + objClass->set_property = ink_radio_action_set_property; + + klass->parent_class.parent_class.parent_class.create_menu_item = ink_radio_action_create_menu_item; + klass->parent_class.parent_class.parent_class.create_tool_item = ink_radio_action_create_tool_item; + /*klass->parent_class.connect_proxy = connect_proxy;*/ + /*klass->parent_class.disconnect_proxy = disconnect_proxy;*/ + + g_object_class_install_property( objClass, + PROP_INK_ID, + g_param_spec_string( "iconId", + "Icon ID", + "The id for the icon", + "", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_INK_SIZE, + g_param_spec_int( "iconSize", + "Icon Size", + "The size the icon", + (int)Inkscape::ICON_SIZE_MENU, + (int)Inkscape::ICON_SIZE_DECORATION, + (int)Inkscape::ICON_SIZE_SMALL_TOOLBAR, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_type_class_add_private( klass, sizeof(InkRadioActionClass) ); + } +} + +static void ink_radio_action_init( InkRadioAction* action ) +{ + action->private_data = INK_RADIO_ACTION_GET_PRIVATE( action ); + action->private_data->iconId = 0; + action->private_data->iconSize = Inkscape::ICON_SIZE_SMALL_TOOLBAR; +} + +static void ink_radio_action_finalize( GObject* obj ) +{ + InkRadioAction* action = INK_RADIO_ACTION( obj ); + + g_free( action->private_data->iconId ); + g_free( action->private_data ); + +} + +InkRadioAction* ink_radio_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *inkId, + Inkscape::IconSize size ) +{ + GObject* obj = (GObject*)g_object_new( INK_RADIO_ACTION_TYPE, + "name", name, + "label", label, + "tooltip", tooltip, + "iconId", inkId, + "iconSize", Inkscape::getRegisteredIconSize(size), + NULL ); + + InkRadioAction* action = INK_RADIO_ACTION( obj ); + + return action; +} + +static void ink_radio_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ) +{ + InkRadioAction* action = INK_RADIO_ACTION( obj ); + (void)action; + switch ( propId ) { + case PROP_INK_ID: + { + g_value_set_string( value, action->private_data->iconId ); + } + break; + + case PROP_INK_SIZE: + { + g_value_set_int( value, action->private_data->iconSize ); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } +} + +void ink_radio_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec ) +{ + InkRadioAction* action = INK_RADIO_ACTION( obj ); + (void)action; + switch ( propId ) { + case PROP_INK_ID: + { + gchar* tmp = action->private_data->iconId; + action->private_data->iconId = g_value_dup_string( value ); + g_free( tmp ); + } + break; + + case PROP_INK_SIZE: + { + action->private_data->iconSize = (Inkscape::IconSize)g_value_get_int( value ); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); + } + } +} + +static GtkWidget* ink_radio_action_create_menu_item( GtkAction* action ) +{ + GtkWidget* item = GTK_RADIO_ACTION_CLASS(ink_radio_action_parent_class)->parent_class.parent_class.create_menu_item(action); + + return item; +} + +static GtkWidget* ink_radio_action_create_tool_item( GtkAction* action ) +{ + InkRadioAction* act = INK_RADIO_ACTION( action ); + GtkWidget* item = GTK_RADIO_ACTION_CLASS(ink_radio_action_parent_class)->parent_class.parent_class.create_tool_item(action); + + if ( act->private_data->iconId ) { + if ( GTK_IS_TOOL_BUTTON(item) ) { + GtkToolButton* button = GTK_TOOL_BUTTON(item); + + GtkWidget* child = sp_icon_new( act->private_data->iconSize, act->private_data->iconId ); + GtkWidget* align = gtk_alignment_new( 0.5, 0.5, 0.0, 0.0 ); + gtk_container_add( GTK_CONTAINER(align), child ); + gtk_tool_button_set_icon_widget( button, align ); + } else { + // For now trigger a warning but don't do anything else + GtkToolButton* button = GTK_TOOL_BUTTON(item); + (void)button; + } + } + + // TODO investigate if needed + gtk_widget_show_all( item ); + + return item; +} + + +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ + +// ToolMenu Action is happily derived from http://www.gtkforums.com/viewtopic.php?t=4215 + +G_DEFINE_TYPE(InkToolMenuAction, ink_tool_menu_action, INK_ACTION_TYPE); + +static void +ink_tool_menu_action_class_init (InkToolMenuActionClass *klass) +{ + GtkActionClass *action_class = GTK_ACTION_CLASS (klass); + action_class->toolbar_item_type = GTK_TYPE_MENU_TOOL_BUTTON; +} + +static void +ink_tool_menu_action_init (InkToolMenuAction* /*tma*/) +{ +} + +InkToolMenuAction * +ink_tool_menu_action_new (const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *inkId, + Inkscape::IconSize size ) +{ + g_return_val_if_fail (name != NULL, NULL); + + GObject* obj = (GObject*)g_object_new( INK_TOOL_MENU_ACTION_TYPE, + "name", name, + "label", label, + "tooltip", tooltip, + "iconId", inkId, + "iconSize", size, + NULL ); + + InkToolMenuAction* action = INK_TOOL_MENU_ACTION( obj ); + + return action; +} diff --git a/src/widgets/ink-action.h b/src/widgets/ink-action.h new file mode 100644 index 000000000..ac5cb9873 --- /dev/null +++ b/src/widgets/ink-action.h @@ -0,0 +1,159 @@ +#ifndef SEEN_INK_ACTION +#define SEEN_INK_ACTION + + +#include <gtk/gtk.h> +#include "icon-size.h" +#include "attributes.h" + +/* Equivalent to GTK Actions of the same type, but can support Inkscape SVG icons */ + +G_BEGIN_DECLS + +#define INK_ACTION_TYPE ( ink_action_get_type() ) +#define INK_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_CAST( (obj), INK_ACTION_TYPE, InkAction) ) +#define INK_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( (klass), INK_ACTION_TYPE, InkActionClass) ) +#define IS_INK_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_TYPE( (obj), INK_ACTION_TYPE) ) +#define IS_INK_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE( (klass), INK_ACTION_TYPE) ) +#define INK_ACTION_GET_CLASS( obj ) ( G_TYPE_INSTANCE_GET_CLASS( (obj), INK_ACTION_TYPE, InkActionClass) ) + +typedef struct _InkAction InkAction; +typedef struct _InkActionClass InkActionClass; +typedef struct _InkActionPrivate InkActionPrivate; + +struct _InkAction +{ + GtkAction action; + InkActionPrivate *private_data; +}; + +struct _InkActionClass +{ + GtkActionClass parent_class; +}; + +GType ink_action_get_type( void ); + +InkAction* ink_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *inkId, + Inkscape::IconSize size ); + + +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ + + +#define INK_TOGGLE_ACTION_TYPE ( ink_toggle_action_get_type() ) +#define INK_TOGGLE_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_CAST( (obj), INK_TOGGLE_ACTION_TYPE, InkToggleAction) ) +#define INK_TOGGLE_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( (klass), INK_TOGGLE_ACTION_TYPE, InkToggleActionClass) ) +#define IS_INK_TOGGLE_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_TYPE( (obj), INK_TOGGLE_ACTION_TYPE) ) +#define IS_INK_TOGGLE_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE( (klass), INK_TOGGLE_ACTION_TYPE) ) +#define INK_TOGGLE_ACTION_GET_CLASS( obj ) ( G_TYPE_INSTANCE_GET_CLASS( (obj), INK_TOGGLE_ACTION_TYPE, InkToggleActionClass) ) + +typedef struct _InkToggleAction InkToggleAction; +typedef struct _InkToggleActionClass InkToggleActionClass; +typedef struct _InkToggleActionPrivate InkToggleActionPrivate; + +struct _InkToggleAction +{ + GtkToggleAction action; + InkToggleActionPrivate *private_data; +}; + +struct _InkToggleActionClass +{ + GtkToggleActionClass parent_class; +}; + +GType ink_toggle_action_get_type( void ); + +InkToggleAction* ink_toggle_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *inkId, + Inkscape::IconSize size, + SPAttributeEnum attr = SP_ATTR_INVALID); + + +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ + + +#define INK_RADIO_ACTION_TYPE ( ink_radio_action_get_type() ) +#define INK_RADIO_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_CAST( (obj), INK_RADIO_ACTION_TYPE, InkRadioAction) ) +#define INK_RADIO_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( (klass), INK_RADIO_ACTION_TYPE, InkRadioActionClass) ) +#define IS_INK_RADIO_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_TYPE( (obj), INK_RADIO_ACTION_TYPE) ) +#define IS_INK_RADIO_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE( (klass), INK_RADIO_ACTION_TYPE) ) +#define INK_RADIO_ACTION_GET_CLASS( obj ) ( G_TYPE_INSTANCE_GET_CLASS( (obj), INK_RADIO_ACTION_TYPE, InkRadioActionClass) ) + +typedef struct _InkRadioAction InkRadioAction; +typedef struct _InkRadioActionClass InkRadioActionClass; +typedef struct _InkRadioActionPrivate InkRadioActionPrivate; + +struct _InkRadioAction +{ + GtkRadioAction action; + InkRadioActionPrivate *private_data; +}; + +struct _InkRadioActionClass +{ + GtkRadioActionClass parent_class; +}; + +GType ink_radio_action_get_type( void ); + +InkRadioAction* ink_radio_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *inkId, + Inkscape::IconSize size ); + + +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ +/* --------------------------------------------------------------- */ + +// ToolMenu Action is happily derived from http://www.gtkforums.com/viewtopic.php?t=4215 + +#define INK_TOOL_MENU_ACTION_TYPE ( ink_tool_menu_action_get_type() ) +#define INK_TOOL_MENU_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_CAST( (obj), INK_TOOL_MENU_ACTION_TYPE, InkToolMenuAction) ) +#define INK_TOOL_MENU_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( (klass), INK_TOOL_MENU_ACTION_TYPE, InkToolMenuActionClass) ) +#define IS_INK_TOOL_MENU_ACTION( obj ) ( G_TYPE_CHECK_INSTANCE_TYPE( (obj), INK_TOOL_MENU_ACTION_TYPE) ) +#define IS_INK_TOOL_MENU_ACTION_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE( (klass), INK_TOOL_MENU_ACTION_TYPE) ) +#define INK_TOOL_MENU_ACTION_GET_CLASS( obj ) ( G_TYPE_INSTANCE_GET_CLASS( (obj), INK_TOOL_MENU_ACTION_TYPE, InkToolMenuActionClass) ) + +typedef struct _InkToolMenuAction InkToolMenuAction; +typedef struct _InkToolMenuActionClass InkToolMenuActionClass; +typedef struct _InkToolMenuActionPrivate InkToolMenuActionPrivate; + +struct _InkToolMenuAction +{ + InkAction action; +}; + +struct _InkToolMenuActionClass +{ + InkActionClass parent_class; +}; + +GType ink_tool_menu_action_get_type( void ); + +InkToolMenuAction* ink_tool_menu_action_new( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *inkId, + Inkscape::IconSize size ); + + + +G_END_DECLS + +#endif /* SEEN_INK_ACTION */ diff --git a/src/widgets/ink-comboboxentry-action.cpp b/src/widgets/ink-comboboxentry-action.cpp new file mode 100644 index 000000000..5c59f6961 --- /dev/null +++ b/src/widgets/ink-comboboxentry-action.cpp @@ -0,0 +1,958 @@ +/* + * A subclass of GtkAction that wraps a GtkComboBoxEntry. + * Features: + * Setting GtkEntryBox width in characters. + * Passing a function for formatting cells. + * Displaying a warning if entry text isn't in list. + * Check comma separated values in text against list. (Useful for font-family fallbacks.) + * Setting names for GtkComboBoxEntry and GtkEntry (actionName_combobox, actionName_entry) + * to allow setting resources. + * + * Author(s): + * Tavmjong Bah + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2010 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +/* + * We must provide for both a toolbar item and a menu item. + * As we don't know which widgets are used (or even constructed), + * we must keep track of things like active entry ourselves. + */ + +#include <iostream> +#include <string.h> +#include <glibmm/ustring.h> + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include "widgets/ink-comboboxentry-action.h" +#include "ui/icon-names.h" + +// Must handle both tool and menu items! +static GtkWidget* create_tool_item( GtkAction* action ); +static GtkWidget* create_menu_item( GtkAction* action ); + +// Internal +static gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text, gboolean exclude = false, gboolean ignore_case = false ); +static Glib::ustring check_comma_separated_text( Ink_ComboBoxEntry_Action* action ); + +// Callbacks +static void combo_box_changed_cb( GtkComboBox* widget, gpointer data ); +static void entry_activate_cb( GtkEntry* widget, gpointer data ); +static gboolean match_selected_cb( GtkEntryCompletion* widget, GtkTreeModel* model, GtkTreeIter* iter, gpointer data ); +static gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data ); + +enum { + PROP_MODEL = 1, + PROP_COMBOBOX, + PROP_ENTRY, + PROP_ENTRY_WIDTH, + PROP_EXTRA_WIDTH, + PROP_CELL_DATA_FUNC, + PROP_SEPARATOR_FUNC, + PROP_POPUP, + PROP_FOCUS_WIDGET +}; + +enum { + CHANGED = 0, + ACTIVATED, + N_SIGNALS +}; +static guint signals[N_SIGNALS] = {0}; + +static GQuark gDataName = 0; + +static void ink_comboboxentry_action_init (Ink_ComboBoxEntry_Action *action); +static void ink_comboboxentry_action_class_init (Ink_ComboBoxEntry_ActionClass *klass); + +G_DEFINE_TYPE(Ink_ComboBoxEntry_Action, ink_comboboxentry_action, GTK_TYPE_ACTION); + +static void ink_comboboxentry_action_finalize (GObject *object) +{ + // Free any allocated resources. + + G_OBJECT_CLASS (ink_comboboxentry_action_parent_class)->finalize (object); +} + + +static void ink_comboboxentry_action_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION (object); + + switch(property_id) { + + case PROP_MODEL: + action->model = GTK_TREE_MODEL( g_value_get_object( value )); + break; + + case PROP_COMBOBOX: + action->combobox = GTK_COMBO_BOX (g_value_get_object (value)); + break; + + case PROP_ENTRY: + action->entry = GTK_ENTRY( g_value_get_object( value )); + break; + + case PROP_ENTRY_WIDTH: + action->entry_width = g_value_get_int( value ); + break; + + case PROP_EXTRA_WIDTH: + action->extra_width = g_value_get_int( value ); + break; + + case PROP_CELL_DATA_FUNC: + action->cell_data_func = g_value_get_pointer( value ); + break; + + case PROP_SEPARATOR_FUNC: + action->separator_func = g_value_get_pointer( value ); + break; + + case PROP_POPUP: + action->popup = g_value_get_boolean( value ); + break; + + case PROP_FOCUS_WIDGET: + action->focusWidget = (GtkWidget*)g_value_get_pointer( value ); + break; + + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + + +static void ink_comboboxentry_action_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION (object); + + switch(property_id) { + + case PROP_MODEL: + g_value_set_object (value, action->model); + break; + + case PROP_COMBOBOX: + g_value_set_object (value, action->combobox); + break; + + case PROP_ENTRY: + g_value_set_object (value, action->entry); + break; + + case PROP_ENTRY_WIDTH: + g_value_set_int (value, action->entry_width); + break; + + case PROP_EXTRA_WIDTH: + g_value_set_int (value, action->extra_width); + break; + + case PROP_CELL_DATA_FUNC: + g_value_set_pointer (value, action->cell_data_func); + break; + + case PROP_SEPARATOR_FUNC: + g_value_set_pointer (value, action->separator_func); + break; + + case PROP_POPUP: + g_value_set_boolean (value, action->popup); + break; + + case PROP_FOCUS_WIDGET: + g_value_set_pointer (value, action->focusWidget); + break; + + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ink_comboboxentry_action_connect_proxy (GtkAction *action, + GtkWidget *proxy) +{ + /* Override any proxy properties. */ + // if (GTK_IS_MENU_ITEM (proxy)) { + // } + + GTK_ACTION_CLASS (ink_comboboxentry_action_parent_class)->connect_proxy (action, proxy); +} + +static void +ink_comboboxentry_action_class_init (Ink_ComboBoxEntry_ActionClass *klass) +{ + + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkActionClass *gtkaction_class = GTK_ACTION_CLASS (klass); + + gtkaction_class->connect_proxy = ink_comboboxentry_action_connect_proxy; + + gobject_class->finalize = ink_comboboxentry_action_finalize; + gobject_class->set_property = ink_comboboxentry_action_set_property; + gobject_class->get_property = ink_comboboxentry_action_get_property; + + gDataName = g_quark_from_string("ink_comboboxentry-action"); + + klass->parent_class.create_tool_item = create_tool_item; + klass->parent_class.create_menu_item = create_menu_item; + + g_object_class_install_property ( + gobject_class, + PROP_MODEL, + g_param_spec_object ("model", + "Tree Model", + "Tree Model", + GTK_TYPE_TREE_MODEL, + (GParamFlags)G_PARAM_READWRITE)); + g_object_class_install_property ( + gobject_class, + PROP_COMBOBOX, + g_param_spec_object ("combobox", + "GtkComboBoxEntry", + "GtkComboBoxEntry", + GTK_TYPE_WIDGET, + (GParamFlags)G_PARAM_READABLE)); + g_object_class_install_property ( + gobject_class, + PROP_ENTRY, + g_param_spec_object ("entry", + "GtkEntry", + "GtkEntry", + GTK_TYPE_WIDGET, + (GParamFlags)G_PARAM_READABLE)); + g_object_class_install_property ( + gobject_class, + PROP_ENTRY_WIDTH, + g_param_spec_int ("entry_width", + "EntryBox width", + "EntryBox width (characters)", + -1.0, 100, -1.0, + (GParamFlags)G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_EXTRA_WIDTH, + g_param_spec_int ("extra_width", + "Extra width", + "Extra width (px)", + -1.0, 500, -1.0, + (GParamFlags)G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_CELL_DATA_FUNC, + g_param_spec_pointer ("cell_data_func", + "Cell Data Func", + "Cell Deta Function", + (GParamFlags)G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_SEPARATOR_FUNC, + g_param_spec_pointer ("separator_func", + "Separator Func", + "Separator Function", + (GParamFlags)G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_POPUP, + g_param_spec_boolean ("popup", + "Entry Popup", + "Entry Popup", + false, + (GParamFlags)G_PARAM_READWRITE)); + + g_object_class_install_property( gobject_class, + PROP_FOCUS_WIDGET, + g_param_spec_pointer( "focus-widget", + "Focus Widget", + "The widget to return focus to", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + // We need to know when GtkComboBoxEvent or Menu ready for reading + signals[CHANGED] = g_signal_new( "changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(Ink_ComboBoxEntry_ActionClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + // Probably not needed... originally to keep track of key-presses. + signals[ACTIVATED] = g_signal_new( "activated", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(Ink_ComboBoxEntry_ActionClass, activated), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + +static void ink_comboboxentry_action_init (Ink_ComboBoxEntry_Action *action) +{ + action->active = -1; + action->text = strdup(""); + action->entry_completion = NULL; + action->indicator = NULL; + action->popup = false; + action->info = NULL; + action->info_cb = NULL; + action->info_cb_id = 0; + action->info_cb_blocked = false; + action->warning = NULL; + action->warning_cb = NULL; + action->warning_cb_id = 0; + action->warning_cb_blocked = false; + action->altx_name = NULL; + action->focusWidget = NULL; +} + +Ink_ComboBoxEntry_Action *ink_comboboxentry_action_new (const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id, + GtkTreeModel *model, + gint entry_width, + gint extra_width, + void *cell_data_func, + void *separator_func, + GtkWidget *focusWidget) +{ + g_return_val_if_fail (name != NULL, NULL); + + return (Ink_ComboBoxEntry_Action*)g_object_new (INK_COMBOBOXENTRY_TYPE_ACTION, + "name", name, + "label", label, + "tooltip", tooltip, + "stock-id", stock_id, + "model", model, + "entry_width", entry_width, + "extra_width", extra_width, + "cell_data_func", cell_data_func, + "separator_func", separator_func, + "focus-widget", focusWidget, + NULL); +} + +// Create a widget for a toolbar. +GtkWidget* create_tool_item( GtkAction* action ) +{ + GtkWidget* item = 0; + + if ( INK_COMBOBOXENTRY_IS_ACTION( action ) && INK_COMBOBOXENTRY_ACTION(action)->model ) { + + Ink_ComboBoxEntry_Action* ink_comboboxentry_action = INK_COMBOBOXENTRY_ACTION( action ); + + gchar *action_name = g_strdup( gtk_action_get_name( action ) ); + gchar *combobox_name = g_strjoin( NULL, action_name, "_combobox", NULL ); + gchar *entry_name = g_strjoin( NULL, action_name, "_entry", NULL ); + g_free( action_name ); + + item = GTK_WIDGET( gtk_tool_item_new() ); + + GtkWidget* comboBoxEntry = gtk_combo_box_new_with_model_and_entry (ink_comboboxentry_action->model); + gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (comboBoxEntry), 0); + + // Name it so we can muck with it using an RC file + gtk_widget_set_name( comboBoxEntry, combobox_name ); + g_free( combobox_name ); + + { + GtkWidget *align = gtk_alignment_new(0, 0.5, 0, 0); + gtk_container_add( GTK_CONTAINER(align), comboBoxEntry ); + gtk_container_add( GTK_CONTAINER(item), align ); + } + + ink_comboboxentry_action->combobox = GTK_COMBO_BOX (comboBoxEntry); + + //gtk_combo_box_set_active( GTK_COMBO_BOX( comboBoxEntry ), ink_comboboxentry_action->active ); + gtk_combo_box_set_active( GTK_COMBO_BOX( comboBoxEntry ), 0 ); + + g_signal_connect( G_OBJECT(comboBoxEntry), "changed", G_CALLBACK(combo_box_changed_cb), action ); + + // Optionally add separator function... + if( ink_comboboxentry_action->separator_func != NULL ) { + gtk_combo_box_set_row_separator_func( ink_comboboxentry_action->combobox, + GtkTreeViewRowSeparatorFunc (ink_comboboxentry_action->separator_func), + NULL, NULL ); + } + + // FIXME: once gtk3 migration is done this can be removed + // https://bugzilla.gnome.org/show_bug.cgi?id=734915 + gtk_widget_show_all (comboBoxEntry); + + // Optionally add formatting... + if( ink_comboboxentry_action->cell_data_func != NULL ) { + GtkCellRenderer *cell = gtk_cell_renderer_text_new(); + gtk_cell_layout_clear( GTK_CELL_LAYOUT( comboBoxEntry ) ); + gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( comboBoxEntry ), cell, true ); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT( comboBoxEntry ), cell, + GtkCellLayoutDataFunc (ink_comboboxentry_action->cell_data_func), + NULL, NULL ); + } + + // Optionally widen the combobox width... which widens the drop-down list in list mode. + if( ink_comboboxentry_action->extra_width > 0 ) { + GtkRequisition req; +#if GTK_CHECK_VERSION(3,0,0) + gtk_widget_get_preferred_size(GTK_WIDGET(ink_comboboxentry_action->combobox), &req, NULL); +#else + gtk_widget_size_request( GTK_WIDGET( ink_comboboxentry_action->combobox ), &req ); +#endif + gtk_widget_set_size_request( GTK_WIDGET( ink_comboboxentry_action->combobox ), + req.width + ink_comboboxentry_action->extra_width, -1 ); + } + + // Get reference to GtkEntry and fiddle a bit with it. + GtkWidget *child = gtk_bin_get_child( GTK_BIN(comboBoxEntry) ); + + // Name it so we can muck with it using an RC file + gtk_widget_set_name( child, entry_name ); + g_free( entry_name ); + + if( child && GTK_IS_ENTRY( child ) ) { + + ink_comboboxentry_action->entry = GTK_ENTRY(child); + + // Change width + if( ink_comboboxentry_action->entry_width > 0 ) { + gtk_entry_set_width_chars (GTK_ENTRY (child), ink_comboboxentry_action->entry_width ); + } + + // Add pop-up entry completion if required + if( ink_comboboxentry_action->popup ) { + ink_comboboxentry_action_popup_enable( ink_comboboxentry_action ); + } + + // Add altx_name if required + if( ink_comboboxentry_action->altx_name ) { + g_object_set_data( G_OBJECT( child ), ink_comboboxentry_action->altx_name, ink_comboboxentry_action->entry ); + } + + // Add signal for GtkEntry to check if finished typing. + g_signal_connect( G_OBJECT(child), "activate", G_CALLBACK(entry_activate_cb), action ); + g_signal_connect( G_OBJECT(child), "key-press-event", G_CALLBACK(keypress_cb), action ); + } + + gtk_activatable_set_related_action( GTK_ACTIVATABLE (item), GTK_ACTION( action ) ); + gtk_widget_show_all( item ); + + } else { + + item = GTK_ACTION_CLASS(ink_comboboxentry_action_parent_class)->create_tool_item( action ); + + } + + return item; +} + +// Create a drop-down menu. +GtkWidget* create_menu_item( GtkAction* action ) +{ + GtkWidget* item = 0; + + item = GTK_ACTION_CLASS(ink_comboboxentry_action_parent_class)->create_menu_item( action ); + g_warning( "ink_comboboxentry_action: create_menu_item not implemented" ); + // One can easily modify ege-select-one-action routine to implement this. + return item; +} + +// Setters/Getters --------------------------------------------------- + +GtkTreeModel *ink_comboboxentry_action_get_model( Ink_ComboBoxEntry_Action* action ) { + + return action->model; +} + +GtkComboBox *ink_comboboxentry_action_get_comboboxentry( Ink_ComboBoxEntry_Action* action ) { + + return action->combobox; +} + +gchar* ink_comboboxentry_action_get_active_text( Ink_ComboBoxEntry_Action* action ) { + + gchar* text = g_strdup( action->text ); + return text; +} + +/* + * For the font-family list we need to handle two cases: + * Text is in list store: + * In this case we use row number as the font-family list can have duplicate + * entries, one in the document font part and one in the system font part. In + * order that scrolling through the list works properly we must distinguish + * between the two. + * Text is not in the list store (i.e. default font-family is not on system): + * In this case we have a row number of -1, and the text must be set by hand. + */ +gboolean ink_comboboxentry_action_set_active_text( Ink_ComboBoxEntry_Action* action, const gchar* text, int row ) { + + if( strcmp( action->text, text ) != 0 ) { + g_free( action->text ); + action->text = g_strdup( text ); + } + + // Get active row or -1 if none + if( row < 0 ) { + row = get_active_row_from_text( action, action->text ); + } + action->active = row; + + // Set active row, check that combobox has been created. + if( action->combobox ) { + gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox ), action->active ); + } + + // Fiddle with entry + if( action->entry ) { + + // Explicitly set text in GtkEntry box (won't be set if text not in list). + gtk_entry_set_text( action->entry, text ); + + // Show or hide warning -- this might be better moved to text-toolbox.cpp + if( action->info_cb_id != 0 && + !action->info_cb_blocked ) { + g_signal_handler_block (G_OBJECT(action->entry), + action->info_cb_id ); + action->info_cb_blocked = true; + } + if( action->warning_cb_id != 0 && + !action->warning_cb_blocked ) { + g_signal_handler_block (G_OBJECT(action->entry), + action->warning_cb_id ); + action->warning_cb_blocked = true; + } + + bool set = false; + if( action->warning != NULL ) { + Glib::ustring missing = check_comma_separated_text( action ); + if( !missing.empty() ) { + gtk_entry_set_icon_from_icon_name( action->entry, + GTK_ENTRY_ICON_SECONDARY, + INKSCAPE_ICON("dialog-warning") ); + // Can't add tooltip until icon set + Glib::ustring warning = action->warning; + warning += ": "; + warning += missing; + gtk_entry_set_icon_tooltip_text( action->entry, + GTK_ENTRY_ICON_SECONDARY, + warning.c_str() ); + + if( action->warning_cb ) { + + // Add callback if we haven't already + if( action->warning_cb_id == 0 ) { + action->warning_cb_id = + g_signal_connect( G_OBJECT(action->entry), + "icon-press", + G_CALLBACK(action->warning_cb), + action); + } + // Unblock signal + if( action->warning_cb_blocked ) { + g_signal_handler_unblock (G_OBJECT(action->entry), + action->warning_cb_id ); + action->warning_cb_blocked = false; + } + } + set = true; + } + } + + if( !set && action->info != NULL ) { + gtk_entry_set_icon_from_icon_name( GTK_ENTRY(action->entry), + GTK_ENTRY_ICON_SECONDARY, + INKSCAPE_ICON("edit-select-all") ); + gtk_entry_set_icon_tooltip_text( action->entry, + GTK_ENTRY_ICON_SECONDARY, + action->info ); + + if( action->info_cb ) { + // Add callback if we haven't already + if( action->info_cb_id == 0 ) { + action->info_cb_id = + g_signal_connect( G_OBJECT(action->entry), + "icon-press", + G_CALLBACK(action->info_cb), + action); + } + // Unblock signal + if( action->info_cb_blocked ) { + g_signal_handler_unblock (G_OBJECT(action->entry), + action->info_cb_id ); + action->info_cb_blocked = false; + } + } + set = true; + } + + if( !set ) { + gtk_entry_set_icon_from_icon_name( GTK_ENTRY(action->entry), + GTK_ENTRY_ICON_SECONDARY, + NULL ); + } + } + + // Return if active text in list + gboolean found = ( action->active != -1 ); + return found; +} + +void ink_comboboxentry_action_set_entry_width( Ink_ComboBoxEntry_Action* action, gint entry_width ) { + + action->entry_width = entry_width; + + // Widget may not have been created.... + if( action->entry ) { + gtk_entry_set_width_chars( GTK_ENTRY(action->entry), entry_width ); + } +} + +void ink_comboboxentry_action_set_extra_width( Ink_ComboBoxEntry_Action* action, gint extra_width ) { + + action->extra_width = extra_width; + + // Widget may not have been created.... + if( action->combobox ) { + GtkRequisition req; +#if GTK_CHECK_VERSION(3,0,0) + gtk_widget_get_preferred_size(GTK_WIDGET(action->combobox), &req, NULL); +#else + gtk_widget_size_request( GTK_WIDGET( action->combobox ), &req ); +#endif + gtk_widget_set_size_request( GTK_WIDGET( action->combobox ), req.width + action->extra_width, -1 ); + } +} + +void ink_comboboxentry_action_popup_enable( Ink_ComboBoxEntry_Action* action ) { + + action->popup = true; + + // Widget may not have been created.... + if( action->entry ) { + + // Check we don't already have a GtkEntryCompletion + if( action->entry_completion ) return; + + action->entry_completion = gtk_entry_completion_new(); + + gtk_entry_set_completion( action->entry, action->entry_completion ); + gtk_entry_completion_set_model( action->entry_completion, action->model ); + gtk_entry_completion_set_text_column( action->entry_completion, 0 ); + gtk_entry_completion_set_popup_completion( action->entry_completion, true ); + gtk_entry_completion_set_inline_completion( action->entry_completion, false ); + gtk_entry_completion_set_inline_selection( action->entry_completion, true ); + + g_signal_connect (G_OBJECT (action->entry_completion), "match-selected", G_CALLBACK (match_selected_cb), action ); + + + } +} + +void ink_comboboxentry_action_popup_disable( Ink_ComboBoxEntry_Action* action ) { + + action->popup = false; + + if( action->entry_completion ) { + gtk_widget_destroy(GTK_WIDGET(action->entry_completion)); + action->entry_completion = 0; + } +} +void ink_comboboxentry_action_set_tooltip( Ink_ComboBoxEntry_Action* action, const gchar* tooltip ) { + + // Widget may not have been created.... + if( action->entry ) { + gtk_widget_set_tooltip_text ( GTK_WIDGET(action->entry), tooltip); + } + if( action->combobox ) { + gtk_widget_set_tooltip_text ( GTK_WIDGET(action->combobox), tooltip); + } + +} + +void ink_comboboxentry_action_set_info( Ink_ComboBoxEntry_Action* action, const gchar* info ) { + + g_free( action->info ); + action->info = g_strdup( info ); + + // Widget may not have been created.... + if( action->entry ) { + gtk_entry_set_icon_tooltip_text( GTK_ENTRY(action->entry), + GTK_ENTRY_ICON_SECONDARY, + action->info ); + } +} + +void ink_comboboxentry_action_set_info_cb( Ink_ComboBoxEntry_Action* action, gpointer info_cb ) { + + action->info_cb = info_cb; +} + +void ink_comboboxentry_action_set_warning( Ink_ComboBoxEntry_Action* action, const gchar* warning ) { + + g_free( action->warning ); + action->warning = g_strdup( warning ); + + // Widget may not have been created.... + if( action->entry ) { + gtk_entry_set_icon_tooltip_text( GTK_ENTRY(action->entry), + GTK_ENTRY_ICON_SECONDARY, + action->warning ); + } +} + +void ink_comboboxentry_action_set_warning_cb( Ink_ComboBoxEntry_Action* action, gpointer warning_cb ) { + + action->warning_cb = warning_cb; +} + +void ink_comboboxentry_action_set_altx_name( Ink_ComboBoxEntry_Action* action, const gchar* altx_name ) { + + g_free( action->altx_name ); + action->altx_name = g_strdup( altx_name ); + + // Widget may not have been created.... + if( action->entry ) { + g_object_set_data( G_OBJECT(action->entry), action->altx_name, action->entry ); + } +} + +// Internal --------------------------------------------------- + +// Return row of active text or -1 if not found. If exclude is true, +// use 3d colunm if available to exclude row from checking (useful to +// skip rows added for font-families included in doc and not on +// system) +gint get_active_row_from_text( Ink_ComboBoxEntry_Action* action, const gchar* target_text, + gboolean exclude, gboolean ignore_case ) { + + // Check if text in list + gint row = 0; + gboolean found = false; + GtkTreeIter iter; + gboolean valid = gtk_tree_model_get_iter_first( action->model, &iter ); + while ( valid ) { + + // See if we should exclude a row + gboolean check = true; // If true, font-family is on system. + if( exclude && gtk_tree_model_get_n_columns( action->model ) > 2 ) { + gtk_tree_model_get( action->model, &iter, 2, &check, -1 ); + } + + if( check ) { + // Get text from list entry + gchar* text = 0; + gtk_tree_model_get( action->model, &iter, 0, &text, -1 ); // Column 0 + + if( !ignore_case ) { + // Case sensitive compare + if( strcmp( target_text, text ) == 0 ){ + found = true; + break; + } + } else { + // Case insensitive compare + gchar* target_text_casefolded = g_utf8_casefold( target_text, -1 ); + gchar* text_casefolded = g_utf8_casefold( text, -1 ); + gboolean equal = (strcmp( target_text_casefolded, text_casefolded ) == 0 ); + g_free( text_casefolded ); + g_free( target_text_casefolded ); + if( equal ) { + found = true; + break; + } + } + } + + ++row; + valid = gtk_tree_model_iter_next( action->model, &iter ); + } + + if( !found ) row = -1; + + return row; + +} + +// Checks if all comma separated text fragments are in the list and +// returns a ustring with a list of missing fragments. +// This is useful for checking if all fonts in a font-family fallback +// list are available on the system. +// +// This routine could also create a Pango Markup string to show which +// fragments are invalid in the entry box itself. See: +// http://developer.gnome.org/pango/stable/PangoMarkupFormat.html +// However... it appears that while one can retrieve the PangoLayout +// for a GtkEntry box, it is only a copy and changing it has no effect. +// PangoLayout * pl = gtk_entry_get_layout( entry ); +// pango_layout_set_markup( pl, "NEW STRING", -1 ); // DOESN'T WORK +static Glib::ustring check_comma_separated_text( Ink_ComboBoxEntry_Action* action ) { + + Glib::ustring missing; + + // Parse fallback_list using a comma as deliminator + gchar** tokens = g_strsplit( action->text, ",", 0 ); + + gint i = 0; + while( tokens[i] != NULL ) { + + // Remove any surrounding white space. + g_strstrip( tokens[i] ); + + if( get_active_row_from_text( action, tokens[i], true, true ) == -1 ) { + missing += tokens[i]; + missing += ", "; + } + ++i; + } + g_strfreev( tokens ); + + // Remove extra comma and space from end. + if( missing.size() >= 2 ) { + missing.resize( missing.size()-2 ); + } + return missing; +} + +// Callbacks --------------------------------------------------- + +static void combo_box_changed_cb( GtkComboBox* widget, gpointer data ) { + + // Two things can happen to get here: + // An item is selected in the drop-down menu. + // Text is typed. + // We only react here if an item is selected. + + // Get action + Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); + + // Check if item selected: + gint newActive = gtk_combo_box_get_active(widget); + if( newActive >= 0 && newActive != action->active ) { + + action->active = newActive; + + GtkTreeIter iter; + if( gtk_combo_box_get_active_iter( GTK_COMBO_BOX( action->combobox ), &iter ) ) { + + gchar* text = 0; + gtk_tree_model_get( action->model, &iter, 0, &text, -1 ); + gtk_entry_set_text( action->entry, text ); + + g_free( action->text ); + action->text = text; + } + + // Now let the world know + g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); + } +} + +static void entry_activate_cb( GtkEntry* widget, gpointer data ) { + + // Get text from entry box.. check if it matches a menu entry. + + // Get action + Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); + + // Get text + g_free( action->text ); + action->text = g_strdup( gtk_entry_get_text( widget ) ); + + // Get row + action->active = + get_active_row_from_text( action, action->text ); + + // Set active row + gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox), action->active ); + + // Now let the world know + g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); + +} + +static gboolean match_selected_cb( GtkEntryCompletion* /*widget*/, GtkTreeModel* model, GtkTreeIter* iter, gpointer data ) +{ + // Get action + Ink_ComboBoxEntry_Action *action = INK_COMBOBOXENTRY_ACTION( data ); + GtkEntry *entry = action->entry; + + if( entry) { + gchar *family = 0; + gtk_tree_model_get(model, iter, 0, &family, -1); + + // Set text in GtkEntry + gtk_entry_set_text (GTK_ENTRY (entry), family ); + + // Set text in GtkAction + g_free( action->text ); + action->text = family; + + // Get row + action->active = + get_active_row_from_text( action, action->text ); + + // Set active row + gtk_combo_box_set_active( GTK_COMBO_BOX( action->combobox), action->active ); + + // Now let the world know + g_signal_emit( G_OBJECT(action), signals[CHANGED], 0 ); + + return true; + } + return false; +} + +static void ink_comboboxentry_action_defocus( Ink_ComboBoxEntry_Action* action ) +{ + if ( action->focusWidget ) { + gtk_widget_grab_focus( action->focusWidget ); + } +} + +gboolean keypress_cb( GtkWidget * /*widget*/, GdkEventKey *event, gpointer data ) +{ + gboolean wasConsumed = FALSE; /* default to report event not consumed */ + guint key = 0; + Ink_ComboBoxEntry_Action* action = INK_COMBOBOXENTRY_ACTION( data ); + gdk_keymap_translate_keyboard_state( gdk_keymap_get_for_display( gdk_display_get_default() ), + event->hardware_keycode, (GdkModifierType)event->state, + 0, &key, 0, 0, 0 ); + + switch ( key ) { + + // TODO Add bindings for Tab/LeftTab + case GDK_KEY_Escape: + { + //gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal ); + ink_comboboxentry_action_defocus( action ); + wasConsumed = TRUE; + } + break; + + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + { + ink_comboboxentry_action_defocus( action ); + //wasConsumed = TRUE; + } + break; + + + } + + return wasConsumed; +} diff --git a/src/widgets/ink-comboboxentry-action.h b/src/widgets/ink-comboboxentry-action.h new file mode 100644 index 000000000..04b66e8fe --- /dev/null +++ b/src/widgets/ink-comboboxentry-action.h @@ -0,0 +1,106 @@ +/* + * A subclass of GtkAction that wraps a GtkComboBoxEntry. + * Features: + * Setting GtkEntryBox width in characters. + * Passing a function for formatting cells. + * Displaying a warning if text isn't in list. + * Setting names for GtkComboBoxEntry and GtkEntry (actionName_combobox, actionName_entry) + * to allow setting resources. + * + * Author(s): + * Tavmjong Bah + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2010 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INK_COMBOBOXENTRY_ACTION +#define SEEN_INK_COMBOBOXENTRY_ACTION + +#include <gtk/gtk.h> + +#define INK_COMBOBOXENTRY_TYPE_ACTION (ink_comboboxentry_action_get_type()) +#define INK_COMBOBOXENTRY_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INK_COMBOBOXENTRY_TYPE_ACTION, Ink_ComboBoxEntry_Action)) +#define INK_COMBOBOXENTRY_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INK_COMBOBOXENTRY_TYPE_ACTION, Ink_ComboBoxEntry_ActionClass)) +#define INK_COMBOBOXENTRY_IS_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INK_COMBOBOXENTRY_TYPE_ACTION)) +#define INK_COMBOBOXENTRY_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INK_COMBOBOXENTRY_TYPE_ACTION, Ink_ComboBoxEntry_ActionClass)) + +typedef struct _Ink_ComboBoxEntry_ActionClass Ink_ComboBoxEntry_ActionClass; +typedef struct _Ink_ComboBoxEntry_Action Ink_ComboBoxEntry_Action; + +struct _Ink_ComboBoxEntry_ActionClass { + GtkActionClass parent_class; + + void (*changed) (Ink_ComboBoxEntry_Action* action); + void (*activated) (Ink_ComboBoxEntry_Action* action); +}; + +struct _Ink_ComboBoxEntry_Action { + GtkAction parent_instance; + + GtkTreeModel *model; + GtkComboBox *combobox; + GtkEntry *entry; + GtkEntryCompletion *entry_completion; + GtkWidget *indicator; + + gpointer cell_data_func; // drop-down menu format + gpointer separator_func; + + gint active; // Index of active menu item (-1 if not in list). + gchar *text; // Text of active menu item or entry box. + gint entry_width;// Width of GtkEntry in characters. + gint extra_width;// Extra Width of GtkComboBox.. to widen drop-down list in list mode. + gboolean popup; // Do we pop-up an entry-completion dialog? + gchar *info; // Text for tooltip info about entry. + gpointer info_cb; // Callback for clicking info icon. + gint info_cb_id; + gboolean info_cb_blocked; + gchar *warning; // Text for tooltip warning that entry isn't in list. + gpointer warning_cb; // Callback for clicking warning icon. + gint warning_cb_id; + gboolean warning_cb_blocked; + gchar *altx_name; // Target for Alt-X keyboard shortcut. + GtkWidget *focusWidget; +}; + + +GType ink_comboboxentry_action_get_type (void); + +/** + * Creates a GtkAction subclass that wraps a GtkComboBoxEntry object. + */ +Ink_ComboBoxEntry_Action *ink_comboboxentry_action_new ( const gchar *name, + const gchar *label, + const gchar *tooltip, + const gchar *stock_id, + GtkTreeModel *model, + gint entry_width = -1, + gint extra_width = -1, + gpointer cell_data_func = NULL, + gpointer separator_func = NULL, + GtkWidget* focusWidget = NULL); + +GtkTreeModel *ink_comboboxentry_action_get_model( Ink_ComboBoxEntry_Action* action ); +GtkComboBox *ink_comboboxentry_action_get_comboboxentry( Ink_ComboBoxEntry_Action* action ); + +gchar* ink_comboboxentry_action_get_active_text( Ink_ComboBoxEntry_Action* action ); +gboolean ink_comboboxentry_action_set_active_text( Ink_ComboBoxEntry_Action* action, const gchar* text, int row=-1 ); + +void ink_comboboxentry_action_set_entry_width( Ink_ComboBoxEntry_Action* action, gint entry_width ); +void ink_comboboxentry_action_set_extra_width( Ink_ComboBoxEntry_Action* action, gint extra_width ); + +void ink_comboboxentry_action_popup_enable( Ink_ComboBoxEntry_Action* action ); +void ink_comboboxentry_action_popup_disable( Ink_ComboBoxEntry_Action* action ); + +void ink_comboboxentry_action_set_info( Ink_ComboBoxEntry_Action* action, const gchar* info ); +void ink_comboboxentry_action_set_info_cb( Ink_ComboBoxEntry_Action* action, gpointer info_cb ); +void ink_comboboxentry_action_set_warning( Ink_ComboBoxEntry_Action* action, const gchar* warning_cb ); +void ink_comboboxentry_action_set_warning_cb(Ink_ComboBoxEntry_Action* action, gpointer warning ); +void ink_comboboxentry_action_set_tooltip( Ink_ComboBoxEntry_Action* action, const gchar* tooltip ); + +void ink_comboboxentry_action_set_altx_name( Ink_ComboBoxEntry_Action* action, const gchar* altx_name ); + +#endif /* SEEN_INK_COMBOBOXENTRY_ACTION */ diff --git a/src/widgets/lpe-toolbar.cpp b/src/widgets/lpe-toolbar.cpp index 7ad88b856..a85f3ae78 100644 --- a/src/widgets/lpe-toolbar.cpp +++ b/src/widgets/lpe-toolbar.cpp @@ -33,15 +33,15 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-select-one-action.h" +#include "widgets/ege-select-one-action.h" #include "helper/action-context.h" #include "helper/action.h" -#include "ink-action.h" +#include "widgets/ink-action.h" #include "live_effects/effect.h" #include "preferences.h" #include "selection.h" #include "sp-namedview.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "ui/tools/lpe-tool.h" #include "ui/widget/unit-tracker.h" #include "util/units.h" diff --git a/src/widgets/measure-toolbar.cpp b/src/widgets/measure-toolbar.cpp index 46d3bd4e0..1a4678332 100644 --- a/src/widgets/measure-toolbar.cpp +++ b/src/widgets/measure-toolbar.cpp @@ -34,8 +34,8 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-output-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-output-action.h" #include "preferences.h" #include "toolbox.h" #include "ui/widget/unit-tracker.h" diff --git a/src/widgets/mesh-toolbar.cpp b/src/widgets/mesh-toolbar.cpp index 73e450926..897d84278 100644 --- a/src/widgets/mesh-toolbar.cpp +++ b/src/widgets/mesh-toolbar.cpp @@ -50,11 +50,11 @@ #include "selection.h" #include "ui/icon-names.h" -#include "ege-adjustment-action.h" -#include "ege-output-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" -#include "ink-comboboxentry-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-output-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" +#include "widgets/ink-comboboxentry-action.h" #include "sp-stop.h" #include "svg/css-ostringstream.h" diff --git a/src/widgets/node-toolbar.cpp b/src/widgets/node-toolbar.cpp index ace78f8f5..467325d08 100644 --- a/src/widgets/node-toolbar.cpp +++ b/src/widgets/node-toolbar.cpp @@ -34,8 +34,8 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ink-action.h" #include "inkscape.h" #include "preferences.h" #include "selection-chemistry.h" @@ -270,7 +270,7 @@ static void sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::D } // quit if run by the attr_changed listener - if (g_object_get_data( tbl, "freeze" )) { + if (g_object_get_data( tbl, "freeze" ) || tracker->isUpdating()) { return; } diff --git a/src/widgets/paintbucket-toolbar.cpp b/src/widgets/paintbucket-toolbar.cpp index 8ddaccf64..d8edeb9f6 100644 --- a/src/widgets/paintbucket-toolbar.cpp +++ b/src/widgets/paintbucket-toolbar.cpp @@ -33,8 +33,8 @@ #include "paintbucket-toolbar.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-select-one-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-select-one-action.h" #include "preferences.h" #include "toolbox.h" #include "ui/icon-names.h" diff --git a/src/widgets/pencil-toolbar.cpp b/src/widgets/pencil-toolbar.cpp index cf09a4d34..1214a378a 100644 --- a/src/widgets/pencil-toolbar.cpp +++ b/src/widgets/pencil-toolbar.cpp @@ -33,12 +33,12 @@ #include "pencil-toolbar.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" #include "preferences.h" #include "toolbox.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "ui/icon-names.h" #include "ui/tools/pen-tool.h" #include "ui/uxmanager.h" @@ -157,7 +157,7 @@ static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) static GList * freehand_shape_dropdown_items_list() { GList *glist = NULL; - glist = g_list_append (glist, _("None")); + glist = g_list_append (glist, const_cast<gchar *>(C_("Freehand shape", "None"))); glist = g_list_append (glist, _("Triangle in")); glist = g_list_append (glist, _("Triangle out")); glist = g_list_append (glist, _("Ellipse")); diff --git a/src/widgets/rect-toolbar.cpp b/src/widgets/rect-toolbar.cpp index 48808fe70..e1ce01eaf 100644 --- a/src/widgets/rect-toolbar.cpp +++ b/src/widgets/rect-toolbar.cpp @@ -34,9 +34,9 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-output-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-output-action.h" +#include "widgets/ink-action.h" #include "inkscape.h" #include "preferences.h" #include "selection.h" @@ -97,7 +97,7 @@ static void sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const * } // quit if run by the attr_changed listener - if (g_object_get_data( tbl, "freeze" )) { + if (g_object_get_data( tbl, "freeze" ) || tracker->isUpdating()) { return; } diff --git a/src/widgets/select-toolbar.cpp b/src/widgets/select-toolbar.cpp index 18976b329..5f107c5a8 100644 --- a/src/widgets/select-toolbar.cpp +++ b/src/widgets/select-toolbar.cpp @@ -26,10 +26,10 @@ #include "display/sp-canvas.h" #include "document-undo.h" #include "document.h" -#include "ege-adjustment-action.h" +#include "widgets/ege-adjustment-action.h" #include "helper/action-context.h" #include "helper/action.h" -#include "ink-action.h" +#include "widgets/ink-action.h" #include "inkscape.h" #include "message-stack.h" #include "preferences.h" diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 87cb7cae3..c7fa96efd 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -38,7 +38,7 @@ #include "../profile-manager.h" #include "color-profile.h" #include "cms-system.h" -#include "tools-switch.h" +#include "ui/tools-switch.h" #include "ui/tools/tool-base.h" using Inkscape::CMSSystem; diff --git a/src/widgets/sp-widget.h b/src/widgets/sp-widget.h index 9a1a93a45..a53062cf4 100644 --- a/src/widgets/sp-widget.h +++ b/src/widgets/sp-widget.h @@ -24,12 +24,10 @@ #define SP_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_WIDGET)) #define SP_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_WIDGET)) - namespace Inkscape { class Selection; class SPWidgetImpl; - } struct SPWidget { diff --git a/src/widgets/spiral-toolbar.cpp b/src/widgets/spiral-toolbar.cpp index 710be9440..e85b024ed 100644 --- a/src/widgets/spiral-toolbar.cpp +++ b/src/widgets/spiral-toolbar.cpp @@ -34,9 +34,9 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-output-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-output-action.h" +#include "widgets/ink-action.h" #include "preferences.h" #include "selection.h" #include "sp-spiral.h" diff --git a/src/widgets/spray-toolbar.cpp b/src/widgets/spray-toolbar.cpp index 788ce6475..183814b7e 100644 --- a/src/widgets/spray-toolbar.cpp +++ b/src/widgets/spray-toolbar.cpp @@ -33,9 +33,9 @@ #include "spray-toolbar.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" #include "preferences.h" #include "toolbox.h" #include "ui/icon-names.h" diff --git a/src/widgets/star-toolbar.cpp b/src/widgets/star-toolbar.cpp index 0f8c31be8..6213263fc 100644 --- a/src/widgets/star-toolbar.cpp +++ b/src/widgets/star-toolbar.cpp @@ -34,10 +34,10 @@ #include "desktop-handles.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-output-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-output-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" #include "selection.h" #include "sp-star.h" #include "toolbox.h" diff --git a/src/widgets/stroke-marker-selector.cpp b/src/widgets/stroke-marker-selector.cpp index 9572f6426..24288299f 100644 --- a/src/widgets/stroke-marker-selector.cpp +++ b/src/widgets/stroke-marker-selector.cpp @@ -31,7 +31,7 @@ #include "preferences.h" #include "path-prefix.h" #include "io/sys.h" -#include "marker.h" +#include "sp-marker.h" #include "sp-defs.h" #include "sp-root.h" #include "ui/cache/svg_preview_cache.h" @@ -385,7 +385,7 @@ void MarkerComboBox::add_markers (GSList *marker_list, SPDocument *source, gbool if (history) { // add "None" Gtk::TreeModel::Row row = *(marker_store->prepend()); - row[marker_columns.label] = _("None"); + row[marker_columns.label] = C_("Marker", "None"); row[marker_columns.stock] = false; row[marker_columns.marker] = g_strdup("None"); row[marker_columns.image] = NULL; diff --git a/src/widgets/stroke-style.h b/src/widgets/stroke-style.h index 464e7d2d1..15e394097 100644 --- a/src/widgets/stroke-style.h +++ b/src/widgets/stroke-style.h @@ -44,7 +44,7 @@ #include "helper/stock-items.h" #include "inkscape.h" #include "io/sys.h" -#include "marker.h" +#include "sp-marker.h" #include "preferences.h" #include "path-prefix.h" #include "selection.h" diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index 88f698bc4..d62bb8027 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -36,10 +36,10 @@ #include "desktop.h" #include "document-undo.h" #include "document.h" -#include "ege-adjustment-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" -#include "ink-comboboxentry-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" +#include "widgets/ink-comboboxentry-action.h" #include "inkscape.h" #include "preferences.h" #include "selection-chemistry.h" diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 939546f78..9e55d1cf6 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -43,20 +43,20 @@ #include "../desktop-handles.h" #include "../desktop-style.h" #include "document-undo.h" -#include "../ege-adjustment-action.h" -#include "../ege-output-action.h" -#include "../ege-select-one-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-output-action.h" +#include "widgets/ege-select-one-action.h" #include "../graphlayout.h" #include "../helper/action.h" #include "../helper/action-context.h" #include "icon.h" -#include "../ink-action.h" -#include "../ink-comboboxentry-action.h" +#include "ink-action.h" +#include "ink-comboboxentry-action.h" #include "../inkscape.h" -#include "../interface.h" +#include "ui/interface.h" #include "../shortcuts.h" #include "../sp-namedview.h" -#include "../tools-switch.h" +#include "ui/tools-switch.h" #include "../ui/icon-names.h" #include "../ui/widget/style-swatch.h" #include "../verbs.h" diff --git a/src/widgets/tweak-toolbar.cpp b/src/widgets/tweak-toolbar.cpp index 050d7fb5e..a5d90fc3d 100644 --- a/src/widgets/tweak-toolbar.cpp +++ b/src/widgets/tweak-toolbar.cpp @@ -33,10 +33,10 @@ #include "tweak-toolbar.h" #include "desktop.h" #include "document-undo.h" -#include "ege-adjustment-action.h" -#include "ege-output-action.h" -#include "ege-select-one-action.h" -#include "ink-action.h" +#include "widgets/ege-adjustment-action.h" +#include "widgets/ege-output-action.h" +#include "widgets/ege-select-one-action.h" +#include "widgets/ink-action.h" #include "preferences.h" #include "toolbox.h" #include "ui/icon-names.h" |
