Code

Adding climb and digits properties, key processing and focus handling
[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>
53 #include "ege-adjustment-action.h"
56 static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass );
57 static void ege_adjustment_action_init( EgeAdjustmentAction* action );
58 static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec );
59 static void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec );
61 static GtkWidget* create_menu_item( GtkAction* action );
62 static GtkWidget* create_tool_item( GtkAction* action );
63 static void connect_proxy( GtkAction *action, GtkWidget *proxy );
64 static void disconnect_proxy( GtkAction *action, GtkWidget *proxy );
66 static gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
67 static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
68 static gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data );
70 static void ege_adjustment_action_defocus( EgeAdjustmentAction* action );
73 static GtkActionClass* gParentClass = 0;
76 struct _EgeAdjustmentActionPrivate
77 {
78     GtkAdjustment* adj;
79     GtkWidget* focusWidget;
80     gdouble climbRate;
81     guint digits;
82     gdouble lastVal;
83     gdouble step;
84     gdouble page;
85     gboolean transferFocus;
86 };
88 #define EGE_ADJUSTMENT_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), EGE_ADJUSTMENT_ACTION_TYPE, EgeAdjustmentActionPrivate ) )
90 enum {
91     PROP_ADJUSTMENT = 1,
92     PROP_FOCUS_WIDGET,
93     PROP_CLIMB_RATE,
94     PROP_DIGITS
95 };
97 GType ege_adjustment_action_get_type( void )
98 {
99     static GType myType = 0;
100     if ( !myType ) {
101         static const GTypeInfo myInfo = {
102             sizeof( EgeAdjustmentActionClass ),
103             NULL, /* base_init */
104             NULL, /* base_finalize */
105             (GClassInitFunc)ege_adjustment_action_class_init,
106             NULL, /* class_finalize */
107             NULL, /* class_data */
108             sizeof( EgeAdjustmentAction ),
109             0, /* n_preallocs */
110             (GInstanceInitFunc)ege_adjustment_action_init,
111             NULL
112         };
114         myType = g_type_register_static( GTK_TYPE_ACTION, "EgeAdjustmentAction", &myInfo, (GTypeFlags)0 );
115     }
117     return myType;
121 static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass )
123     if ( klass ) {
124         gParentClass = GTK_ACTION_CLASS( g_type_class_peek_parent( klass ) );
125         GObjectClass * objClass = G_OBJECT_CLASS( klass );
127         objClass->get_property = ege_adjustment_action_get_property;
128         objClass->set_property = ege_adjustment_action_set_property;
130         klass->parent_class.create_menu_item = create_menu_item;
131         klass->parent_class.create_tool_item = create_tool_item;
132         klass->parent_class.connect_proxy = connect_proxy;
133         klass->parent_class.disconnect_proxy = disconnect_proxy;
135         g_object_class_install_property( objClass,
136                                          PROP_ADJUSTMENT,
137                                          g_param_spec_object( "adjustment",
138                                                               "Adjustment",
139                                                               "The adjustment to change",
140                                                               GTK_TYPE_ADJUSTMENT,
141                                                               (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
143         g_object_class_install_property( objClass,
144                                          PROP_FOCUS_WIDGET,
145                                          g_param_spec_pointer( "focus-widget",
146                                                                "Focus Widget",
147                                                                "The widget to return focus to",
148                                                                (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
150         g_object_class_install_property( objClass,
151                                          PROP_CLIMB_RATE,
152                                          g_param_spec_double( "climb-rate",
153                                                               "Climb Rate",
154                                                               "The acelleraton rate",
155                                                               0.0, G_MAXDOUBLE, 0.0,
156                                                               (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
158         g_object_class_install_property( objClass,
159                                          PROP_DIGITS,
160                                          g_param_spec_uint( "digits",
161                                                             "Digits",
162                                                             "The number of digits to show",
163                                                             0, 20, 0,
164                                                             (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) );
166         g_type_class_add_private( klass, sizeof(EgeAdjustmentActionClass) );
167     }
170 static void ege_adjustment_action_init( EgeAdjustmentAction* action )
172     action->private_data = EGE_ADJUSTMENT_ACTION_GET_PRIVATE( action );
173     action->private_data->adj = 0;
174     action->private_data->focusWidget = 0;
175     action->private_data->climbRate = 0.0;
176     action->private_data->digits = 2;
177     action->private_data->lastVal = 0.0;
178     action->private_data->step = 0.0;
179     action->private_data->page = 0.0;
180     action->private_data->transferFocus = FALSE;
183 EgeAdjustmentAction* ege_adjustment_action_new( GtkAdjustment* adjustment,
184                                                 const gchar *name,
185                                                 const gchar *label,
186                                                 const gchar *tooltip,
187                                                 const gchar *stock_id,
188                                                 gdouble climb_rate,
189                                                 guint digits )
191     GObject* obj = (GObject*)g_object_new( EGE_ADJUSTMENT_ACTION_TYPE,
192                                            "name", name,
193                                            "label", label,
194                                            "tooltip", tooltip,
195                                            "stock_id", stock_id,
196                                            "adjustment", adjustment,
197                                            "climb-rate", climb_rate,
198                                            "digits", digits,
199                                            NULL );
201     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
203     return action;
206 static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec )
208     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
209     switch ( propId ) {
210         case PROP_ADJUSTMENT:
211             g_value_set_object( value, action->private_data->adj );
212             break;
214         case PROP_FOCUS_WIDGET:
215             g_value_set_pointer( value, action->private_data->focusWidget );
216             break;
218         case PROP_CLIMB_RATE:
219             g_value_set_double( value, action->private_data->climbRate );
220             break;
222         case PROP_DIGITS:
223             g_value_set_uint( value, action->private_data->digits );
224             break;
226         default:
227             G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
228     }
231 void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec )
233     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( obj );
234     switch ( propId ) {
235         case PROP_ADJUSTMENT:
236         {
237             action->private_data->adj = GTK_ADJUSTMENT( g_value_get_object( value ) );
238             g_object_get( G_OBJECT(action->private_data->adj),
239                           "step-increment", &action->private_data->step,
240                           "page-increment", &action->private_data->page,
241                           NULL );
242         }
243         break;
245         case PROP_FOCUS_WIDGET:
246         {
247             /* TODO unhook prior */
248             action->private_data->focusWidget = (GtkWidget*)g_value_get_pointer( value );
249         }
250         break;
252         case PROP_CLIMB_RATE:
253         {
254             /* TODO pass on */
255             action->private_data->climbRate = g_value_get_double( value );
256         }
257         break;
259         case PROP_DIGITS:
260         {
261             /* TODO pass on */
262             action->private_data->digits = g_value_get_uint( value );
263         }
264         break;
266         default:
267             G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
268     }
271 GtkAdjustment* ege_adjustment_action_get_adjustment( EgeAdjustmentAction* action )
273     g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
275     return action->private_data->adj;
278 void ege_adjustment_action_set_focuswidget( EgeAdjustmentAction* action, GtkWidget* widget )
280     g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(action) );
282     /* TODO unhook prior */
284     action->private_data->focusWidget = widget;
287 GtkWidget* ege_adjustment_action_get_focuswidget( EgeAdjustmentAction* action )
289     g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
291     return action->private_data->focusWidget;
294 static GtkWidget* create_menu_item( GtkAction* action )
296     GtkWidget* item = 0;
298     item = gParentClass->create_menu_item( action );
300     return item;
303 void value_changed_cb( GtkSpinButton* spin, EgeAdjustmentAction* act )
305     if ( GTK_WIDGET_HAS_FOCUS( GTK_WIDGET(spin) ) ) {
306         ege_adjustment_action_defocus( act );
307     }
310 static GtkWidget* create_tool_item( GtkAction* action )
312     GtkWidget* item = 0;
314     if ( IS_EGE_ADJUSTMENT_ACTION(action) ) {
315         EgeAdjustmentAction* act = EGE_ADJUSTMENT_ACTION( action );
316         GtkWidget* spinbutton = gtk_spin_button_new( act->private_data->adj, act->private_data->climbRate, act->private_data->digits );
317         GtkWidget* hb = gtk_hbox_new( FALSE, 5 );
318         GValue value;
320         item = GTK_WIDGET( gtk_tool_item_new() );
322         memset( &value, 0, sizeof(value) );
323         g_value_init( &value, G_TYPE_STRING );
324         g_object_get_property( G_OBJECT(action), "label", &value );
325         const gchar* sss = g_value_get_string( &value );
326         GtkWidget* lbl = gtk_label_new( sss ? sss : "wwww" );
328         gtk_misc_set_alignment( GTK_MISC(lbl), 1.0, 0.5 );
330         gtk_box_pack_start( GTK_BOX(hb), lbl, FALSE, FALSE, 0 );
331         gtk_box_pack_end( GTK_BOX(hb), spinbutton, FALSE, FALSE, 0 );
332         gtk_container_add( GTK_CONTAINER(item), hb );
334         g_signal_connect( G_OBJECT(spinbutton), "focus-in-event", G_CALLBACK(focus_in_cb), action );
335         g_signal_connect( G_OBJECT(spinbutton), "focus-out-event", G_CALLBACK(focus_out_cb), action );
336         g_signal_connect( G_OBJECT(spinbutton), "key-press-event", G_CALLBACK(keypress_cb), action );
338         g_signal_connect( G_OBJECT(spinbutton), "value-changed", G_CALLBACK(value_changed_cb), action );
339 /*      g_signal_connect( G_OBJECT(EGE_ADJUSTMENT_ACTION(action)->private_data->adj), "value-changed", G_CALLBACK(flippy), action ); */
342         gtk_widget_show_all( item );
343     } else {
344         item = gParentClass->create_tool_item( action );
345     }
347     return item;
350 static void connect_proxy( GtkAction *action, GtkWidget *proxy )
352     gParentClass->connect_proxy( action, proxy );
355 static void disconnect_proxy( GtkAction *action, GtkWidget *proxy )
357     gParentClass->disconnect_proxy( action, proxy );
360 void ege_adjustment_action_defocus( EgeAdjustmentAction* action )
362     if ( action->private_data->transferFocus ) {
363         if ( action->private_data->focusWidget ) {
364             gtk_widget_grab_focus( action->private_data->focusWidget );
365         }
366     }
369 gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
371     (void)event;
372     if ( IS_EGE_ADJUSTMENT_ACTION(data) ) {
373         EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( data );
374         action->private_data->lastVal = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
375         action->private_data->transferFocus = TRUE;
376     }
378     return FALSE; /* report event not consumed */
381 static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
383     (void)widget;
384     (void)event;
385     if ( IS_EGE_ADJUSTMENT_ACTION(data) ) {
386         EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION( data );
387         action->private_data->transferFocus = FALSE;
388     }
390     return FALSE; /* report event not consumed */
394 gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
396     gboolean wasConsumed = FALSE; /* default to report event not consumed */
397     EgeAdjustmentAction* action = EGE_ADJUSTMENT_ACTION(data);
398     guint key = 0;
399     gdk_keymap_translate_keyboard_state( gdk_keymap_get_for_display( gdk_display_get_default() ),
400                                          event->hardware_keycode, (GdkModifierType)event->state,
401                                          0, &key, 0, 0, 0 );
403     switch ( key ) {
404         case GDK_Escape:
405         {
406             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal );
407             action->private_data->transferFocus = TRUE;
408             ege_adjustment_action_defocus( action );
409             wasConsumed = TRUE;
410         }
411         break;
413         case GDK_Return:
414         case GDK_KP_Enter:
415         {
416             action->private_data->transferFocus = TRUE;
417             ege_adjustment_action_defocus( action );
418             wasConsumed = TRUE;
419         }
420         break;
422         case GDK_Tab:
423         case GDK_ISO_Left_Tab:
424         {
425             action->private_data->transferFocus = FALSE;
426             wasConsumed = FALSE;
427         }
428         break;
430         case GDK_Up:
431         case GDK_KP_Up:
432         {
433             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
434             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val + action->private_data->step );
435             action->private_data->transferFocus = FALSE;
436             wasConsumed = TRUE;
437         }
438         break;
440         case GDK_Down:
441         case GDK_KP_Down:
442         {
443             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
444             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val - action->private_data->step );
445             action->private_data->transferFocus = FALSE;
446             wasConsumed = TRUE;
447         }
448         break;
450         case GDK_Page_Up:
451         case GDK_KP_Page_Up:
452         {
453             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
454             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val + action->private_data->page );
455             action->private_data->transferFocus = FALSE;
456             wasConsumed = TRUE;
457         }
458         break;
460         case GDK_Page_Down:
461         case GDK_KP_Page_Down:
462         {
463             gdouble val = gtk_spin_button_get_value( GTK_SPIN_BUTTON(widget) );
464             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), val - action->private_data->page );
465             action->private_data->transferFocus = FALSE;
466             wasConsumed = TRUE;
467         }
468         break;
470         case GDK_z:
471         case GDK_Z:
472         {
473             gtk_spin_button_set_value( GTK_SPIN_BUTTON(widget), action->private_data->lastVal );
474             action->private_data->transferFocus = FALSE;
475             wasConsumed = TRUE;
476         }
477         break;
479     }
481     return wasConsumed;