d140ae12cceea295a4d70d87d37cb0636b9b4782
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 )
123 {
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;
143 }
146 static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass )
147 {
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 }
210 }
212 static void ege_adjustment_action_init( EgeAdjustmentAction* action )
213 {
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;
226 }
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 )
235 {
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;
249 }
251 static void ege_adjustment_action_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec )
252 {
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 }
282 }
284 void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec )
285 {
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 }
337 }
339 GtkAdjustment* ege_adjustment_action_get_adjustment( EgeAdjustmentAction* action )
340 {
341 g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
343 return action->private_data->adj;
344 }
346 void ege_adjustment_action_set_focuswidget( EgeAdjustmentAction* action, GtkWidget* widget )
347 {
348 g_return_if_fail( IS_EGE_ADJUSTMENT_ACTION(action) );
350 /* TODO unhook prior */
352 action->private_data->focusWidget = widget;
353 }
355 GtkWidget* ege_adjustment_action_get_focuswidget( EgeAdjustmentAction* action )
356 {
357 g_return_val_if_fail( IS_EGE_ADJUSTMENT_ACTION(action), NULL );
359 return action->private_data->focusWidget;
360 }
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 }
372 }
374 static gint i_compare_descriptions( gconstpointer a, gconstpointer b )
375 {
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;
390 }
392 void ege_adjustment_action_set_descriptions( EgeAdjustmentAction* action, gchar const** descriptions, gdouble const* values, guint count )
393 {
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 }
413 }
415 static void process_menu_action( GtkWidget* obj, gpointer data )
416 {
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 }
461 }
463 static void create_single_menu_item( GCallback toggleCb, int val, GtkWidget* menu, EgeAdjustmentAction* act, GtkWidget** dst, GSList** group, gdouble num, gboolean active )
464 {
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);
509 }
511 static GtkWidget* create_popup_number_menu( EgeAdjustmentAction* act )
512 {
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;
553 }
555 static GtkWidget* create_menu_item( GtkAction* action )
556 {
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;
581 }
583 void value_changed_cb( GtkSpinButton* spin, EgeAdjustmentAction* act )
584 {
585 if ( GTK_WIDGET_HAS_FOCUS( GTK_WIDGET(spin) ) ) {
586 ege_adjustment_action_defocus( act );
587 }
588 }
590 static gboolean event_cb( EgeAdjustmentAction* act, GdkEvent* evt )
591 {
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;
606 }
608 static GtkWidget* create_tool_item( GtkAction* action )
609 {
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;
655 }
657 static void connect_proxy( GtkAction *action, GtkWidget *proxy )
658 {
659 gParentClass->connect_proxy( action, proxy );
660 }
662 static void disconnect_proxy( GtkAction *action, GtkWidget *proxy )
663 {
664 gParentClass->disconnect_proxy( action, proxy );
665 }
667 void ege_adjustment_action_defocus( EgeAdjustmentAction* action )
668 {
669 if ( action->private_data->transferFocus ) {
670 if ( action->private_data->focusWidget ) {
671 gtk_widget_grab_focus( action->private_data->focusWidget );
672 }
673 }
674 }
676 gboolean focus_in_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
677 {
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 */
686 }
688 static gboolean focus_out_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
689 {
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 */
698 }
701 static gboolean process_tab( GtkWidget* widget, int direction )
702 {
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;
743 }
745 gboolean keypress_cb( GtkWidget *widget, GdkEventKey *event, gpointer data )
746 {
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;
839 }