Code

Added callback function to allow shrinking of widgets
[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>
54 #include "ege-adjustment-action.h"
57 static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass );
58 static void ege_adjustment_action_init( EgeAdjustmentAction* action );
59 static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec );
60 static void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec );
62 static GtkWidget* create_menu_item( GtkAction* action );
63 static GtkWidget* create_tool_item( GtkAction* action );
64 static void connect_proxy( GtkAction *action, GtkWidget *proxy );
65 static void disconnect_proxy( GtkAction *action, GtkWidget *proxy );
67 static gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
68 static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
69 static gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
71 static void ege_adjustment_action_defocus( EgeAdjustmentAction* action );
74 static GtkActionClass* gParentClass = 0;
77 struct _EgeAdjustmentActionPrivate
78 {
79     GtkAdjustment* adj;
80     GtkWidget* focusWidget;
81     gdouble climbRate;
82     guint digits;
83     gchar* selfId;
84     EgeWidgetFixup toolPost;
85     gdouble lastVal;
86     gdouble step;
87     gdouble page;
88     gboolean transferFocus;
89 };
91 #define EGE_ADJUSTMENT_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), EGE_ADJUSTMENT_ACTION_TYPE, EgeAdjustmentActionPrivate ) )
93 enum {
94     PROP_ADJUSTMENT = 1,
95     PROP_FOCUS_WIDGET,
96     PROP_CLIMB_RATE,
97     PROP_DIGITS,
98     PROP_SELFID,
99     PROP_TOOL_POST
100 };
102 GType ege_adjustment_action_get_type( void )
104     static GType myType = 0;
105     if ( !myType ) {
106         static const GTypeInfo myInfo = {
107             sizeof( EgeAdjustmentActionClass ),
108             NULL, /* base_init */
109             NULL, /* base_finalize */
110             (GClassInitFunc)ege_adjustment_action_class_init,
111             NULL, /* class_finalize */
112             NULL, /* class_data */
113             sizeof( EgeAdjustmentAction ),
114             0, /* n_preallocs */
115             (GInstanceInitFunc)ege_adjustment_action_init,
116             NULL
117         };
119         myType = g_type_register_static( GTK_TYPE_ACTION, "EgeAdjustmentAction", &myInfo, (GTypeFlags)0 );
120     }
122     return myType;
126 static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass )
128     if ( klass ) {
129         gParentClass = GTK_ACTION_CLASS( g_type_class_peek_parent( klass ) );
130         GObjectClass * objClass = G_OBJECT_CLASS( klass );
132         objClass->get_property = ege_adjustment_action_get_property;
133         objClass->set_property = ege_adjustment_action_set_property;
135         klass->parent_class.create_menu_item = create_menu_item;
136         klass->parent_class.create_tool_item = create_tool_item;
137         klass->parent_class.connect_proxy = connect_proxy;
138         klass->parent_class.disconnect_proxy = disconnect_proxy;
140         g_object_class_install_property( objClass,
141                                          PROP_ADJUSTMENT,
142                                          g_param_spec_object( "adjustment",
143                                                               "Adjustment",
144                                                               "The adjustment to change",
145                                                               GTK_TYPE_ADJUSTMENT,
146                                                               (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
148         g_object_class_install_property( objClass,
149                                          PROP_FOCUS_WIDGET,
150                                          g_param_spec_pointer( "focus-widget",
151                                                                "Focus Widget",
152                                                                "The widget to return focus to",
153                                                                (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
155         g_object_class_install_property( objClass,
156                                          PROP_CLIMB_RATE,
157                                          g_param_spec_double( "climb-rate",
158                                                               "Climb Rate",
159                                                               "The acelleraton rate",
160                                                               0.0, G_MAXDOUBLE, 0.0,
161                                                               (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
163         g_object_class_install_property( objClass,
164                                          PROP_DIGITS,
165                                          g_param_spec_uint( "digits",
166                                                             "Digits",
167                                                             "The number of digits to show",
168                                                             0, 20, 0,
169                                                             (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
171         g_object_class_install_property( objClass,
172                                          PROP_SELFID,
173                                          g_param_spec_string( "self-id",
174                                                               "Self ID",
175                                                               "Marker for self pointer",
176                                                               0,
177                                                               (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
179         g_object_class_install_property( objClass,
180                                          PROP_TOOL_POST,
181                                          g_param_spec_pointer( "tool-post",
182                                                                "Tool Widget post process",
183                                                                "Function for final adjustments",
184                                                                (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
186         g_type_class_add_private( klass, sizeof(EgeAdjustmentActionClass) );
187     }
190 static void ege_adjustment_action_init( EgeAdjustmentAction* action )
192     action->private_data = EGE_ADJUSTMENT_ACTION_GET_PRIVATE( action );
193     action->private_data->adj = 0;
194     action->private_data->focusWidget = 0;
195     action->private_data->climbRate = 0.0;
196     action->private_data->digits = 2;
197     action->private_data->selfId = 0;
198     action->private_data->toolPost = 0;
199     action->private_data->lastVal = 0.0;
200     action->private_data->step = 0.0;
201     action->private_data->page = 0.0;
202     action->private_data->transferFocus = FALSE;
205 EgeAdjustmentAction* ege_adjustment_action_new( GtkAdjustment* adjustment,
206                                                 const gchar *name,
207                                                 const gchar *label,
208                                                 const gchar *tooltip,
209                                                 const gchar *stock_id,
210                                                 gdouble climb_rate,
211                                                 guint digits )
213     GObject* obj = (GObject*)g_object_new( EGE_ADJUSTMENT_ACTION_TYPE,
214                                            "name", name,
215                                            "label", label,
216                                            "tooltip", tooltip,
217                                            "stock_id", stock_id,
218                                            "adjustment", adjustment,
219                                            "climb-rate", climb_rate,
220                                            "digits", digits,
221                                            NULL );
223     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
225     return action;
228 static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec )
230     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
231     switch ( propId ) {
232         case PROP_ADJUSTMENT:
233             g_value_set_object( value, action->private_data->adj );
234             break;
236         case PROP_FOCUS_WIDGET:
237             g_value_set_pointer( value, action->private_data->focusWidget );
238             break;
240         case PROP_CLIMB_RATE:
241             g_value_set_double( value, action->private_data->climbRate );
242             break;
244         case PROP_DIGITS:
245             g_value_set_uint( value, action->private_data->digits );
246             break;
248         case PROP_SELFID:
249             g_value_set_string( value, action->private_data->selfId );
250             break;
252         case PROP_TOOL_POST:
253             g_value_set_pointer( value, (void*)action->private_data->toolPost );
254             break;
256         default:
257             G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
258     }
261 void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec )
263     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
264     switch ( propId ) {
265         case PROP_ADJUSTMENT:
266         {
267             action->private_data->adj = GTK_ADJUSTMENT( g_value_get_object( value ) );
268             g_object_get( G_OBJECT(action->private_data->adj),
269                           "step-increment", &action->private_data->step,
270                           "page-increment", &action->private_data->page,
271                           NULL );
272         }
273         break;
275         case PROP_FOCUS_WIDGET:
276         {
277             /* TODO unhook prior */
278             action->private_data->focusWidget = (GtkWidget*)g_value_get_pointer( value );
279         }
280         break;
282         case PROP_CLIMB_RATE:
283         {
284             /* TODO pass on */
285             action->private_data->climbRate = g_value_get_double( value );
286         }
287         break;
289         case PROP_DIGITS:
290         {
291             /* TODO pass on */
292             action->private_data->digits = g_value_get_uint( value );
293         }
294         break;
296         case PROP_SELFID:
297         {
298             /* TODO pass on */
299             gchar* prior = action->private_data->selfId;
300             action->private_data->selfId = g_value_dup_string( value );
301             g_free( prior );
302         }
303         break;
305         case PROP_TOOL_POST:
306         {
307             action->private_data->toolPost = (EgeWidgetFixup)g_value_get_pointer( value );
308         }
309         break;
311         default:
312             G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
313     }
316 GtkAdjustment* ege_adjustment_action_get_adjustment( EgeAdjustmentAction* action )
318     g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
320     return action->private_data->adj;
323 void ege_adjustment_action_set_focuswidget( EgeAdjustmentAction* action, GtkWidget* widget )
325     g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(action) );
327     /* TODO unhook prior */
329     action->private_data->focusWidget = widget;
332 GtkWidget* ege_adjustment_action_get_focuswidget( EgeAdjustmentAction* action )
334     g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
336     return action->private_data->focusWidget;
339 static GtkWidget* create_menu_item( GtkAction* action )
341     GtkWidget* item = 0;
343     item = gParentClass->create_menu_item( action );
345     return item;
348 void value_changed_cb( GtkSpinButton* spin, EgeAdjustmentAction* act )
350     if ( GTK_WIDGET_HAS_FOCUS( GTK_WIDGET(spin) ) ) {
351         ege_adjustment_action_defocus( act );
352     }
355 static GtkWidget* create_tool_item( GtkAction* action )
357     GtkWidget* item = 0;
359     if ( IS_EGE_ADJUSTMENT_ACTION(action) ) {
360         EgeAdjustmentAction* act = EGE_ADJUSTMENT_ACTION( action );
361         GtkWidget* spinbutton = gtk_spin_button_new( act->private_data->adj, act->private_data->climbRate, act->private_data->digits );
362         GtkWidget* hb = gtk_hbox_new( FALSE, 5 );
363         GValue value;
365         item = GTK_WIDGET( gtk_tool_item_new() );
367         memset( &value, 0, sizeof(value) );
368         g_value_init( &value, G_TYPE_STRING );
369         g_object_get_property( G_OBJECT(action), "label", &value );
370         const gchar* sss = g_value_get_string( &value );
371         GtkWidget* lbl = gtk_label_new( sss ? sss : "wwww" );
373         gtk_misc_set_alignment( GTK_MISC(lbl), 1.0, 0.5 );
375         gtk_box_pack_start( GTK_BOX(hb), lbl, FALSE, FALSE, 0 );
376         gtk_box_pack_end( GTK_BOX(hb), spinbutton, FALSE, FALSE, 0 );
377         gtk_container_add( GTK_CONTAINER(item), hb );
379         if ( act->private_data->selfId ) {
380             gtk_object_set_data( GTK_OBJECT(spinbutton), act->private_data->selfId, spinbutton );
381         }
383         g_signal_connect( G_OBJECT(spinbutton), "focus-in-event", G_CALLBACK(focus_in_cb), action );
384         g_signal_connect( G_OBJECT(spinbutton), "focus-out-event", G_CALLBACK(focus_out_cb), action );
385         g_signal_connect( G_OBJECT(spinbutton), "key-press-event", G_CALLBACK(keypress_cb), action );
387         g_signal_connect( G_OBJECT(spinbutton), "value-changed", G_CALLBACK(value_changed_cb), action );
388 /*      g_signal_connect( G_OBJECT(EGE_ADJUSTMENT_ACTION(action)->private_data->adj), "value-changed", G_CALLBACK(flippy), action ); */
391         gtk_widget_show_all( item );
393         /* Shrink or whatnot after shown */
394         if ( act->private_data->toolPost ) {
395             act->private_data->toolPost( item );
396         }
397     } else {
398         item = gParentClass->create_tool_item( action );
399     }
401     return item;
404 static void connect_proxy( GtkAction *action, GtkWidget *proxy )
406     gParentClass->connect_proxy( action, proxy );
409 static void disconnect_proxy( GtkAction *action, GtkWidget *proxy )
411     gParentClass->disconnect_proxy( action, proxy );
414 void ege_adjustment_action_defocus( EgeAdjustmentAction* action )
416     if ( action->private_data->transferFocus ) {
417         if ( action->private_data->focusWidget ) {
418             gtk_widget_grab_focus( action->private_data->focusWidget );
419         }
420     }
423 gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
425     (void)event;
426     if ( IS_EGE_ADJUSTMENT_ACTION(data) ) {
427         EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( data );
428         action->private_data->lastVal = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
429         action->private_data->transferFocus = TRUE;
430     }
432     return FALSE; /* report event not consumed */
435 static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
437     (void)widget;
438     (void)event;
439     if ( IS_EGE_ADJUSTMENT_ACTION(data) ) {
440         EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( data );
441         action->private_data->transferFocus = FALSE;
442     }
444     return FALSE; /* report event not consumed */
448 static gboolean process_tab( GtkWidget* widget, int direction )
450     gboolean handled = FALSE;
451     GtkWidget* parent = gtk_widget_get_parent(widget);
452     GtkWidget* gp = parent ? gtk_widget_get_parent(parent) : 0;
453     GtkWidget* ggp = gp ? gtk_widget_get_parent(gp) : 0;
455     if ( ggp && GTK_IS_TOOLBAR(ggp) ) {
456         GList* kids = gtk_container_get_children( GTK_CONTAINER(ggp) );
457         if ( kids ) {
458             GtkWidget* curr = widget;
459             while ( curr && (gtk_widget_get_parent(curr) != ggp) ) {
460                 curr = gtk_widget_get_parent( curr );
461             }
462             if ( curr ) {
463                 GList* mid = g_list_find( kids, curr );
464                 while ( mid ) {
465                     mid = ( direction < 0 ) ? g_list_previous(mid) : g_list_next(mid);
466                     if ( mid && GTK_IS_TOOL_ITEM(mid->data) ) {
467                         /* potential target */
468                         GtkWidget* child = gtk_bin_get_child( GTK_BIN(mid->data) );
469                         if ( child && GTK_IS_HBOX(child) ) { /* could be ours */
470                             GList* subChildren = gtk_container_get_children( GTK_CONTAINER(child) );
471                             if ( subChildren ) {
472                                 GList* last = g_list_last(subChildren);
473                                 if ( last && GTK_IS_SPIN_BUTTON(last->data) && GTK_WIDGET_IS_SENSITIVE( GTK_WIDGET(last->data) ) ) {
474                                     gtk_widget_grab_focus( GTK_WIDGET(last->data) );
475                                     handled = TRUE;
476                                     mid = 0; /* to stop loop */
477                                 }
479                                 g_list_free(subChildren);
480                             }
481                         }
482                     }
483                 }
484             }
485             g_list_free( kids );
486         }
487     }
489     return handled;
492 gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
494     gboolean wasConsumed = FALSE; /* default to report event not consumed */
495     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION(data);
496     guint key = 0;
497     gdk_keymap_translate_keyboard_state( gdk_keymap_get_for_display( gdk_display_get_default() ),
498                                          event->hardware_keycode, (GdkModifierType)event->state,
499                                          0, &key, 0, 0, 0 );
501     switch ( key ) {
502         case GDK_Escape:
503         {
504             action->private_data->transferFocus = TRUE;
505             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal );
506             ege_adjustment_action_defocus( action );
507             wasConsumed = TRUE;
508         }
509         break;
511         case GDK_Return:
512         case GDK_KP_Enter:
513         {
514             action->private_data->transferFocus = TRUE;
515             ege_adjustment_action_defocus( action );
516             wasConsumed = TRUE;
517         }
518         break;
520         case GDK_Tab:
521         {
522             action->private_data->transferFocus = FALSE;
523             wasConsumed = process_tab( widget, 1 );
524         }
525         break;
527         case GDK_ISO_Left_Tab:
528         {
529             action->private_data->transferFocus = FALSE;
530             wasConsumed = process_tab( widget, -1 );
531         }
532         break;
534         case GDK_Up:
535         case GDK_KP_Up:
536         {
537             action->private_data->transferFocus = FALSE;
538             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
539             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val + action->private_data->step );
540             wasConsumed = TRUE;
541         }
542         break;
544         case GDK_Down:
545         case GDK_KP_Down:
546         {
547             action->private_data->transferFocus = FALSE;
548             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
549             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val - action->private_data->step );
550             wasConsumed = TRUE;
551         }
552         break;
554         case GDK_Page_Up:
555         case GDK_KP_Page_Up:
556         {
557             action->private_data->transferFocus = FALSE;
558             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
559             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val + action->private_data->page );
560             wasConsumed = TRUE;
561         }
562         break;
564         case GDK_Page_Down:
565         case GDK_KP_Page_Down:
566         {
567             action->private_data->transferFocus = FALSE;
568             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
569             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val - action->private_data->page );
570             wasConsumed = TRUE;
571         }
572         break;
574         case GDK_z:
575         case GDK_Z:
576         {
577             action->private_data->transferFocus = FALSE;
578             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal );
579             wasConsumed = TRUE;
580         }
581         break;
583     }
585     return wasConsumed;