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;
118 }
121 static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass )
122 {
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 }
168 }
170 static void ege_adjustment_action_init( EgeAdjustmentAction* action )
171 {
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;
181 }
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 )
190 {
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;
204 }
206 static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec )
207 {
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 }
229 }
231 void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec )
232 {
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 }
269 }
271 GtkAdjustment* ege_adjustment_action_get_adjustment( EgeAdjustmentAction* action )
272 {
273 g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
275 return action->private_data->adj;
276 }
278 void ege_adjustment_action_set_focuswidget( EgeAdjustmentAction* action, GtkWidget* widget )
279 {
280 g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(action) );
282 /* TODO unhook prior */
284 action->private_data->focusWidget = widget;
285 }
287 GtkWidget* ege_adjustment_action_get_focuswidget( EgeAdjustmentAction* action )
288 {
289 g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
291 return action->private_data->focusWidget;
292 }
294 static GtkWidget* create_menu_item( GtkAction* action )
295 {
296 GtkWidget* item = 0;
298 item = gParentClass->create_menu_item( action );
300 return item;
301 }
303 void value_changed_cb( GtkSpinButton* spin, EgeAdjustmentAction* act )
304 {
305 if ( GTK_WIDGET_HAS_FOCUS( GTK_WIDGET(spin) ) ) {
306 ege_adjustment_action_defocus( act );
307 }
308 }
310 static GtkWidget* create_tool_item( GtkAction* action )
311 {
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;
348 }
350 static void connect_proxy( GtkAction *action, GtkWidget *proxy )
351 {
352 gParentClass->connect_proxy( action, proxy );
353 }
355 static void disconnect_proxy( GtkAction *action, GtkWidget *proxy )
356 {
357 gParentClass->disconnect_proxy( action, proxy );
358 }
360 void ege_adjustment_action_defocus( EgeAdjustmentAction* action )
361 {
362 if ( action->private_data->transferFocus ) {
363 if ( action->private_data->focusWidget ) {
364 gtk_widget_grab_focus( action->private_data->focusWidget );
365 }
366 }
367 }
369 gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
370 {
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 */
379 }
381 static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
382 {
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 */
391 }
394 gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
395 {
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;
482 }