Code

d140ae12cceea295a4d70d87d37cb0636b9b4782
[inkscape.git] / src / ege-adjustment-action.cpp
3 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
4  *
5  */
6 /* ***** BEGIN LICENSE BLOCK *****
7  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
8  *
9  * The contents of this file are subject to the Mozilla Public License Version
10  * 1.1 (the "License"); you may not use this file except in compliance with
11  * the License. You may obtain a copy of the License at
12  * http://www.mozilla.org/MPL/
13  *
14  * Software distributed under the License is distributed on an "AS IS" basis,
15  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16  * for the specific language governing rights and limitations under the
17  * License.
18  *
19  * The Original Code is EGE Adjustment Action.
20  *
21  * The Initial Developer of the Original Code is
22  * Jon A. Cruz.
23  * Portions created by the Initial Developer are Copyright (C) 2006
24  * the Initial Developer. All Rights Reserved.
25  *
26  * Contributor(s):
27  *
28  * Alternatively, the contents of this file may be used under the terms of
29  * either the GNU General Public License Version 2 or later (the "GPL"), or
30  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31  * in which case the provisions of the GPL or the LGPL are applicable instead
32  * of those above. If you wish to allow use of your version of this file only
33  * under the terms of either the GPL or the LGPL, and not to allow others to
34  * use your version of this file under the terms of the MPL, indicate your
35  * decision by deleting the provisions above and replace them with the notice
36  * and other provisions required by the GPL or the LGPL. If you do not delete
37  * the provisions above, a recipient may use your version of this file under
38  * the terms of any one of the MPL, the GPL or the LGPL.
39  *
40  * ***** END LICENSE BLOCK ***** */
42 /* Note: this file should be kept compliable as both .cpp and .c */
44 #include <string.h>
46 #include <gdk/gdkkeysyms.h>
47 #include <gtk/gtktoolitem.h>
48 #include <gtk/gtkspinbutton.h>
49 #include <gtk/gtkhbox.h>
50 #include <gtk/gtklabel.h>
51 #include <gtk/gtkmisc.h>
52 #include <gtk/gtktoolbar.h>
53 #include <gtk/gtkradiomenuitem.h>
55 #include "ege-adjustment-action.h"
58 static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass );
59 static void ege_adjustment_action_init( EgeAdjustmentAction* action );
60 static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec );
61 static void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec );
63 static GtkWidget* create_menu_item( GtkAction* action );
64 static GtkWidget* create_tool_item( GtkAction* action );
65 static void connect_proxy( GtkAction *action, GtkWidget *proxy );
66 static void disconnect_proxy( GtkAction *action, GtkWidget *proxy );
68 static gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
69 static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
70 static gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
72 static void ege_adjustment_action_defocus( EgeAdjustmentAction* action );
75 static GtkActionClass* gParentClass = 0;
76 static GQuark gDataName = 0;
78 typedef struct _EgeAdjustmentDescr EgeAdjustmentDescr;
80 struct _EgeAdjustmentDescr
81 {
82     gchar* descr;
83     gdouble value;
84 };
86 struct _EgeAdjustmentActionPrivate
87 {
88     GtkAdjustment* adj;
89     GtkWidget* focusWidget;
90     gdouble climbRate;
91     guint digits;
92     gchar* selfId;
93     EgeWidgetFixup toolPost;
94     gdouble lastVal;
95     gdouble step;
96     gdouble page;
97     gboolean transferFocus;
98     GList* descriptions;
99 };
101 #define EGE_ADJUSTMENT_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), EGE_ADJUSTMENT_ACTION_TYPE, EgeAdjustmentActionPrivate ) )
103 enum {
104     PROP_ADJUSTMENT = 1,
105     PROP_FOCUS_WIDGET,
106     PROP_CLIMB_RATE,
107     PROP_DIGITS,
108     PROP_SELFID,
109     PROP_TOOL_POST
110 };
112 enum {
113     BUMP_TOP = 0,
114     BUMP_PAGE_UP,
115     BUMP_UP,
116     BUMP_NONE,
117     BUMP_DOWN,
118     BUMP_PAGE_DOWN,
119     BUMP_BOTTOM
120 };
122 GType ege_adjustment_action_get_type( void )
124     static GType myType = 0;
125     if ( !myType ) {
126         static const GTypeInfo myInfo = {
127             sizeof( EgeAdjustmentActionClass ),
128             NULL, /* base_init */
129             NULL, /* base_finalize */
130             (GClassInitFunc)ege_adjustment_action_class_init,
131             NULL, /* class_finalize */
132             NULL, /* class_data */
133             sizeof( EgeAdjustmentAction ),
134             0, /* n_preallocs */
135             (GInstanceInitFunc)ege_adjustment_action_init,
136             NULL
137         };
139         myType = g_type_register_static( GTK_TYPE_ACTION, "EgeAdjustmentAction", &myInfo, (GTypeFlags)0 );
140     }
142     return myType;
146 static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass )
148     if ( klass ) {
149         gParentClass = GTK_ACTION_CLASS( g_type_class_peek_parent( klass ) );
150         GObjectClass * objClass = G_OBJECT_CLASS( klass );
152         gDataName = g_quark_from_string("ege-adj-action");
154         objClass->get_property = ege_adjustment_action_get_property;
155         objClass->set_property = ege_adjustment_action_set_property;
157         klass->parent_class.create_menu_item = create_menu_item;
158         klass->parent_class.create_tool_item = create_tool_item;
159         klass->parent_class.connect_proxy = connect_proxy;
160         klass->parent_class.disconnect_proxy = disconnect_proxy;
162         g_object_class_install_property( objClass,
163                                          PROP_ADJUSTMENT,
164                                          g_param_spec_object( "adjustment",
165                                                               "Adjustment",
166                                                               "The adjustment to change",
167                                                               GTK_TYPE_ADJUSTMENT,
168                                                               (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
170         g_object_class_install_property( objClass,
171                                          PROP_FOCUS_WIDGET,
172                                          g_param_spec_pointer( "focus-widget",
173                                                                "Focus Widget",
174                                                                "The widget to return focus to",
175                                                                (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
177         g_object_class_install_property( objClass,
178                                          PROP_CLIMB_RATE,
179                                          g_param_spec_double( "climb-rate",
180                                                               "Climb Rate",
181                                                               "The acelleraton rate",
182                                                               0.0, G_MAXDOUBLE, 0.0,
183                                                               (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
185         g_object_class_install_property( objClass,
186                                          PROP_DIGITS,
187                                          g_param_spec_uint( "digits",
188                                                             "Digits",
189                                                             "The number of digits to show",
190                                                             0, 20, 0,
191                                                             (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
193         g_object_class_install_property( objClass,
194                                          PROP_SELFID,
195                                          g_param_spec_string( "self-id",
196                                                               "Self ID",
197                                                               "Marker for self pointer",
198                                                               0,
199                                                               (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
201         g_object_class_install_property( objClass,
202                                          PROP_TOOL_POST,
203                                          g_param_spec_pointer( "tool-post",
204                                                                "Tool Widget post process",
205                                                                "Function for final adjustments",
206                                                                (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
208         g_type_class_add_private( klass, sizeof(EgeAdjustmentActionClass) );
209     }
212 static void ege_adjustment_action_init( EgeAdjustmentAction* action )
214     action->private_data = EGE_ADJUSTMENT_ACTION_GET_PRIVATE( action );
215     action->private_data->adj = 0;
216     action->private_data->focusWidget = 0;
217     action->private_data->climbRate = 0.0;
218     action->private_data->digits = 2;
219     action->private_data->selfId = 0;
220     action->private_data->toolPost = 0;
221     action->private_data->lastVal = 0.0;
222     action->private_data->step = 0.0;
223     action->private_data->page = 0.0;
224     action->private_data->transferFocus = FALSE;
225     action->private_data->descriptions = 0;
228 EgeAdjustmentAction* ege_adjustment_action_new( GtkAdjustment* adjustment,
229                                                 const gchar *name,
230                                                 const gchar *label,
231                                                 const gchar *tooltip,
232                                                 const gchar *stock_id,
233                                                 gdouble climb_rate,
234                                                 guint digits )
236     GObject* obj = (GObject*)g_object_new( EGE_ADJUSTMENT_ACTION_TYPE,
237                                            "name", name,
238                                            "label", label,
239                                            "tooltip", tooltip,
240                                            "stock_id", stock_id,
241                                            "adjustment", adjustment,
242                                            "climb-rate", climb_rate,
243                                            "digits", digits,
244                                            NULL );
246     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
248     return action;
251 static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec )
253     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
254     switch ( propId ) {
255         case PROP_ADJUSTMENT:
256             g_value_set_object( value, action->private_data->adj );
257             break;
259         case PROP_FOCUS_WIDGET:
260             g_value_set_pointer( value, action->private_data->focusWidget );
261             break;
263         case PROP_CLIMB_RATE:
264             g_value_set_double( value, action->private_data->climbRate );
265             break;
267         case PROP_DIGITS:
268             g_value_set_uint( value, action->private_data->digits );
269             break;
271         case PROP_SELFID:
272             g_value_set_string( value, action->private_data->selfId );
273             break;
275         case PROP_TOOL_POST:
276             g_value_set_pointer( value, (void*)action->private_data->toolPost );
277             break;
279         default:
280             G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
281     }
284 void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec )
286     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
287     switch ( propId ) {
288         case PROP_ADJUSTMENT:
289         {
290             action->private_data->adj = GTK_ADJUSTMENT( g_value_get_object( value ) );
291             g_object_get( G_OBJECT(action->private_data->adj),
292                           "step-increment", &action->private_data->step,
293                           "page-increment", &action->private_data->page,
294                           NULL );
295         }
296         break;
298         case PROP_FOCUS_WIDGET:
299         {
300             /* TODO unhook prior */
301             action->private_data->focusWidget = (GtkWidget*)g_value_get_pointer( value );
302         }
303         break;
305         case PROP_CLIMB_RATE:
306         {
307             /* TODO pass on */
308             action->private_data->climbRate = g_value_get_double( value );
309         }
310         break;
312         case PROP_DIGITS:
313         {
314             /* TODO pass on */
315             action->private_data->digits = g_value_get_uint( value );
316         }
317         break;
319         case PROP_SELFID:
320         {
321             /* TODO pass on */
322             gchar* prior = action->private_data->selfId;
323             action->private_data->selfId = g_value_dup_string( value );
324             g_free( prior );
325         }
326         break;
328         case PROP_TOOL_POST:
329         {
330             action->private_data->toolPost = (EgeWidgetFixup)g_value_get_pointer( value );
331         }
332         break;
334         default:
335             G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
336     }
339 GtkAdjustment* ege_adjustment_action_get_adjustment( EgeAdjustmentAction* action )
341     g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
343     return action->private_data->adj;
346 void ege_adjustment_action_set_focuswidget( EgeAdjustmentAction* action, GtkWidget* widget )
348     g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(action) );
350     /* TODO unhook prior */
352     action->private_data->focusWidget = widget;
355 GtkWidget* ege_adjustment_action_get_focuswidget( EgeAdjustmentAction* action )
357     g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
359     return action->private_data->focusWidget;
362 static void i_free_description( gpointer data, gpointer user_data ) {
363     (void)user_data;
364     if ( data ) {
365         EgeAdjustmentDescr* descr = (EgeAdjustmentDescr*)data;
366         if ( descr->descr ) {
367             g_free( descr->descr );
368             descr->descr = 0;
369         }
370         g_free( descr );
371     }
374 static gint i_compare_descriptions( gconstpointer a, gconstpointer b )
376     gint val = 0;
378     EgeAdjustmentDescr const * aa = (EgeAdjustmentDescr const *)a;
379     EgeAdjustmentDescr const * bb = (EgeAdjustmentDescr const *)b;
381     if ( aa && bb ) {
382         if ( aa->value < bb->value ) {
383             val = -1;
384         } else if ( aa->value > bb->value ) {
385             val = 1;
386         }
387     }
389     return val;
392 void ege_adjustment_action_set_descriptions( EgeAdjustmentAction* action, gchar const** descriptions, gdouble const* values, guint count )
394     g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(action) );
396     if ( action->private_data->descriptions ) {
397         g_list_foreach( action->private_data->descriptions, i_free_description, 0 );
398         g_list_free( action->private_data->descriptions );
399         action->private_data->descriptions = 0;
400     }
402     if ( count && descriptions && values ) {
403         guint i = 0;
404         for ( i = 0; i < count; i++ ) {
405             EgeAdjustmentDescr* descr = g_new0( EgeAdjustmentDescr, 1 );
406             if ( descriptions[i] ) {
407                 descr->descr = g_strdup( descriptions[i] );
408                 descr->value = values[i];
409             }
410             action->private_data->descriptions = g_list_insert_sorted( action->private_data->descriptions, (gpointer)descr, i_compare_descriptions );
411         }
412     }
415 static void process_menu_action( GtkWidget* obj, gpointer data )
417     GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(obj);
418     if ( item->active ) {
419         EgeAdjustmentAction* act = (EgeAdjustmentAction*)g_object_get_qdata( G_OBJECT(obj), gDataName );
420         gint what = GPOINTER_TO_INT(data);
423         gdouble base = gtk_adjustment_get_value( act->private_data->adj );
424         gdouble lower = 0.0;
425         gdouble upper = 0.0;
426         gdouble step = 0.0;
427         gdouble page = 0.0;
428         g_object_get( G_OBJECT(act->private_data->adj),
429                       "lower", &lower,
430                       "upper", &upper,
431                       "step-increment", &step,
432                       "page-increment", &page,
433                       NULL );
435         switch ( what ) {
436             case BUMP_TOP:
437                 gtk_adjustment_set_value( act->private_data->adj, upper );
438                 break;
440             case BUMP_PAGE_UP:
441                 gtk_adjustment_set_value( act->private_data->adj, base + page );
442                 break;
444             case BUMP_UP:
445                 gtk_adjustment_set_value( act->private_data->adj, base + step );
446                 break;
448             case BUMP_DOWN:
449                 gtk_adjustment_set_value( act->private_data->adj, base - step );
450                 break;
452             case BUMP_PAGE_DOWN:
453                 gtk_adjustment_set_value( act->private_data->adj, base - page );
454                 break;
456             case BUMP_BOTTOM:
457                 gtk_adjustment_set_value( act->private_data->adj, lower );
458                 break;
459         }
460     }
463 static void create_single_menu_item( GCallback toggleCb, int val, GtkWidget* menu, EgeAdjustmentAction* act, GtkWidget** dst, GSList** group, gdouble num, gboolean active )
465     gdouble epsilon = 0.1;
466     char* fmt = 0;
467     char* str = 0;
468     EgeAdjustmentDescr* marker = 0;
469     GList* cur = act->private_data->descriptions;
471     switch ( act->private_data->digits ) {
472         case 0: epsilon = 1.0; break;
473         case 1: epsilon = 0.1; break;
474         case 2: epsilon = 0.01; break;
475         case 3: epsilon = 0.001; break;
476         case 4: epsilon = 0.0001; break;
477     }
479     while ( cur ) {
480         EgeAdjustmentDescr* descr = (EgeAdjustmentDescr*)cur->data;
481         gdouble delta = num - descr->value;
482         if ( delta < 0.0 ) {
483             delta = -delta;
484         }
485         if ( delta < epsilon ) {
486             marker = descr;
487             break;
488         }
489         cur = g_list_next( cur );
490     }
492     fmt = g_strdup_printf("%%0.%df%%s%%s", act->private_data->digits);
493     str = g_strdup_printf ( fmt, num, (marker?" ":""), (marker?marker->descr:"") );
495     *dst = gtk_radio_menu_item_new_with_label( *group, str );
496     if ( !*group) {
497         *group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(*dst) );
498     }
499     if ( active ) {
500         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(*dst), TRUE );
501     }
502     gtk_menu_shell_append( GTK_MENU_SHELL(menu), *dst );
503     g_object_set_qdata( G_OBJECT(*dst), gDataName, act );
505     g_signal_connect( G_OBJECT(*dst), "toggled", toggleCb, GINT_TO_POINTER(val) );
507     g_free(str);
508     g_free(fmt);
511 static GtkWidget* create_popup_number_menu( EgeAdjustmentAction* act )
513     GtkWidget* menu = gtk_menu_new();
515     GSList* group = 0;
516     GtkWidget* single = 0;
518     gdouble base = gtk_adjustment_get_value( act->private_data->adj );
519     gdouble lower = 0.0;
520     gdouble upper = 0.0;
521     gdouble step = 0.0;
522     gdouble page = 0.0;
523     g_object_get( G_OBJECT(act->private_data->adj),
524                   "lower", &lower,
525                   "upper", &upper,
526                   "step-increment", &step,
527                   "page-increment", &page,
528                   NULL );
530     if ( base < upper ) {
531         create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_TOP, menu, act, &single, &group, upper, FALSE );
532         if ( (base + page) < upper ) {
533             create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_PAGE_UP, menu, act, &single, &group, base + page, FALSE );
534         }
535         if ( (base + step) < upper ) {
536             create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_UP, menu, act, &single, &group, base + step, FALSE );
537         }
538     }
540     create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_NONE, menu, act, &single, &group, base, TRUE );
542     if ( base > lower ) {
543         if ( (base - step) > lower ) {
544             create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_DOWN, menu, act, &single, &group, base - step, FALSE );
545         }
546         if ( (base - page) > lower ) {
547             create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_PAGE_DOWN, menu, act, &single, &group, base - page, FALSE );
548         }
549         create_single_menu_item( G_CALLBACK(process_menu_action), BUMP_BOTTOM, menu, act, &single, &group, lower, FALSE );
550     }
552     return menu;
555 static GtkWidget* create_menu_item( GtkAction* action )
557     GtkWidget* item = 0;
559     if ( IS_EGE_ADJUSTMENT_ACTION(action) ) {
560         EgeAdjustmentAction* act = EGE_ADJUSTMENT_ACTION( action );
561         GValue value;
562         const gchar*  sss = 0;
563         GtkWidget*  subby = 0;
565         memset( &value, 0, sizeof(value) );
566         g_value_init( &value, G_TYPE_STRING );
567         g_object_get_property( G_OBJECT(action), "label", &value );
569         sss = g_value_get_string( &value );
571         item = gtk_menu_item_new_with_label( sss );
573         subby = create_popup_number_menu( act );
574         gtk_menu_item_set_submenu( GTK_MENU_ITEM(item), subby );
575         gtk_widget_show_all( subby );
576     } else {
577         item = gParentClass->create_menu_item( action );
578     }
580     return item;
583 void value_changed_cb( GtkSpinButton* spin, EgeAdjustmentAction* act )
585     if ( GTK_WIDGET_HAS_FOCUS( GTK_WIDGET(spin) ) ) {
586         ege_adjustment_action_defocus( act );
587     }
590 static gboolean event_cb( EgeAdjustmentAction* act, GdkEvent* evt )
592     gboolean handled = FALSE;
593     if ( evt->type == GDK_BUTTON_PRESS ) {
594         if ( evt->button.button == 3 ) {
595             if ( IS_EGE_ADJUSTMENT_ACTION(act) ) {
596                 GdkEventButton* btnevt = (GdkEventButton*)evt;
597                 GtkWidget* menu = create_popup_number_menu(act);
598                 gtk_widget_show_all( menu );
599                 gtk_menu_popup( GTK_MENU(menu), NULL, NULL, NULL, NULL, btnevt->button, btnevt->time );
600             }
601             handled = TRUE;
602         }
603     }
605     return handled;
608 static GtkWidget* create_tool_item( GtkAction* action )
610     GtkWidget* item = 0;
612     if ( IS_EGE_ADJUSTMENT_ACTION(action) ) {
613         EgeAdjustmentAction* act = EGE_ADJUSTMENT_ACTION( action );
614         GtkWidget* spinbutton = gtk_spin_button_new( act->private_data->adj, act->private_data->climbRate, act->private_data->digits );
615         GtkWidget* hb = gtk_hbox_new( FALSE, 5 );
616         GValue value;
618         item = GTK_WIDGET( gtk_tool_item_new() );
620         memset( &value, 0, sizeof(value) );
621         g_value_init( &value, G_TYPE_STRING );
622         g_object_get_property( G_OBJECT(action), "label", &value );
623         const gchar* sss = g_value_get_string( &value );
624         GtkWidget* lbl = gtk_label_new( sss ? sss : "wwww" );
626         gtk_misc_set_alignment( GTK_MISC(lbl), 1.0, 0.5 );
628         gtk_box_pack_start( GTK_BOX(hb), lbl, FALSE, FALSE, 0 );
629         gtk_box_pack_end( GTK_BOX(hb), spinbutton, FALSE, FALSE, 0 );
630         gtk_container_add( GTK_CONTAINER(item), hb );
632         if ( act->private_data->selfId ) {
633             g_object_set_data( G_OBJECT(spinbutton), act->private_data->selfId, spinbutton );
634         }
636         g_signal_connect( G_OBJECT(spinbutton), "focus-in-event", G_CALLBACK(focus_in_cb), action );
637         g_signal_connect( G_OBJECT(spinbutton), "focus-out-event", G_CALLBACK(focus_out_cb), action );
638         g_signal_connect( G_OBJECT(spinbutton), "key-press-event", G_CALLBACK(keypress_cb), action );
640         g_signal_connect( G_OBJECT(spinbutton), "value-changed", G_CALLBACK(value_changed_cb), action );
642         g_signal_connect_swapped( G_OBJECT(spinbutton), "event", G_CALLBACK(event_cb), action );
644         gtk_widget_show_all( item );
646         /* Shrink or whatnot after shown */
647         if ( act->private_data->toolPost ) {
648             act->private_data->toolPost( item );
649         }
650     } else {
651         item = gParentClass->create_tool_item( action );
652     }
654     return item;
657 static void connect_proxy( GtkAction *action, GtkWidget *proxy )
659     gParentClass->connect_proxy( action, proxy );
662 static void disconnect_proxy( GtkAction *action, GtkWidget *proxy )
664     gParentClass->disconnect_proxy( action, proxy );
667 void ege_adjustment_action_defocus( EgeAdjustmentAction* action )
669     if ( action->private_data->transferFocus ) {
670         if ( action->private_data->focusWidget ) {
671             gtk_widget_grab_focus( action->private_data->focusWidget );
672         }
673     }
676 gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
678     (void)event;
679     if ( IS_EGE_ADJUSTMENT_ACTION(data) ) {
680         EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( data );
681         action->private_data->lastVal = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
682         action->private_data->transferFocus = TRUE;
683     }
685     return FALSE; /* report event not consumed */
688 static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
690     (void)widget;
691     (void)event;
692     if ( IS_EGE_ADJUSTMENT_ACTION(data) ) {
693         EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( data );
694         action->private_data->transferFocus = FALSE;
695     }
697     return FALSE; /* report event not consumed */
701 static gboolean process_tab( GtkWidget* widget, int direction )
703     gboolean handled = FALSE;
704     GtkWidget* parent = gtk_widget_get_parent(widget);
705     GtkWidget* gp = parent ? gtk_widget_get_parent(parent) : 0;
706     GtkWidget* ggp = gp ? gtk_widget_get_parent(gp) : 0;
708     if ( ggp && GTK_IS_TOOLBAR(ggp) ) {
709         GList* kids = gtk_container_get_children( GTK_CONTAINER(ggp) );
710         if ( kids ) {
711             GtkWidget* curr = widget;
712             while ( curr && (gtk_widget_get_parent(curr) != ggp) ) {
713                 curr = gtk_widget_get_parent( curr );
714             }
715             if ( curr ) {
716                 GList* mid = g_list_find( kids, curr );
717                 while ( mid ) {
718                     mid = ( direction < 0 ) ? g_list_previous(mid) : g_list_next(mid);
719                     if ( mid && GTK_IS_TOOL_ITEM(mid->data) ) {
720                         /* potential target */
721                         GtkWidget* child = gtk_bin_get_child( GTK_BIN(mid->data) );
722                         if ( child && GTK_IS_HBOX(child) ) { /* could be ours */
723                             GList* subChildren = gtk_container_get_children( GTK_CONTAINER(child) );
724                             if ( subChildren ) {
725                                 GList* last = g_list_last(subChildren);
726                                 if ( last && GTK_IS_SPIN_BUTTON(last->data) && GTK_WIDGET_IS_SENSITIVE( GTK_WIDGET(last->data) ) ) {
727                                     gtk_widget_grab_focus( GTK_WIDGET(last->data) );
728                                     handled = TRUE;
729                                     mid = 0; /* to stop loop */
730                                 }
732                                 g_list_free(subChildren);
733                             }
734                         }
735                     }
736                 }
737             }
738             g_list_free( kids );
739         }
740     }
742     return handled;
745 gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
747     gboolean wasConsumed = FALSE; /* default to report event not consumed */
748     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION(data);
749     guint key = 0;
750     gdk_keymap_translate_keyboard_state( gdk_keymap_get_for_display( gdk_display_get_default() ),
751                                          event->hardware_keycode, (GdkModifierType)event->state,
752                                          0, &key, 0, 0, 0 );
754     switch ( key ) {
755         case GDK_Escape:
756         {
757             action->private_data->transferFocus = TRUE;
758             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal );
759             ege_adjustment_action_defocus( action );
760             wasConsumed = TRUE;
761         }
762         break;
764         case GDK_Return:
765         case GDK_KP_Enter:
766         {
767             action->private_data->transferFocus = TRUE;
768             ege_adjustment_action_defocus( action );
769             wasConsumed = TRUE;
770         }
771         break;
773         case GDK_Tab:
774         {
775             action->private_data->transferFocus = FALSE;
776             wasConsumed = process_tab( widget, 1 );
777         }
778         break;
780         case GDK_ISO_Left_Tab:
781         {
782             action->private_data->transferFocus = FALSE;
783             wasConsumed = process_tab( widget, -1 );
784         }
785         break;
787         case GDK_Up:
788         case GDK_KP_Up:
789         {
790             action->private_data->transferFocus = FALSE;
791             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
792             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val + action->private_data->step );
793             wasConsumed = TRUE;
794         }
795         break;
797         case GDK_Down:
798         case GDK_KP_Down:
799         {
800             action->private_data->transferFocus = FALSE;
801             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
802             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val - action->private_data->step );
803             wasConsumed = TRUE;
804         }
805         break;
807         case GDK_Page_Up:
808         case GDK_KP_Page_Up:
809         {
810             action->private_data->transferFocus = FALSE;
811             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
812             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val + action->private_data->page );
813             wasConsumed = TRUE;
814         }
815         break;
817         case GDK_Page_Down:
818         case GDK_KP_Page_Down:
819         {
820             action->private_data->transferFocus = FALSE;
821             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
822             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val - action->private_data->page );
823             wasConsumed = TRUE;
824         }
825         break;
827         case GDK_z:
828         case GDK_Z:
829         {
830             action->private_data->transferFocus = FALSE;
831             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal );
832             wasConsumed = TRUE;
833         }
834         break;
836     }
838     return wasConsumed;