5 #include <gdk/gdkkeysyms.h>
6 #include "eggcellrendererkeys.h"
7 #include "eggaccelerators.h"
9 #ifndef EGG_COMPILATION
11 #define _(x) dgettext (GETTEXT_PACKAGE, x)
19 #define EGG_CELL_RENDERER_TEXT_PATH "egg-cell-renderer-text"
21 static void egg_cell_renderer_keys_finalize (GObject
*object
);
22 static void egg_cell_renderer_keys_init (EggCellRendererKeys
*cell_keys
);
23 static void egg_cell_renderer_keys_class_init (EggCellRendererKeysClass
*cell_keys_class
);
24 static GtkCellEditable
*egg_cell_renderer_keys_start_editing (GtkCellRenderer
*cell
,
28 GdkRectangle
*background_area
,
29 GdkRectangle
*cell_area
,
30 GtkCellRendererState flags
);
33 static void egg_cell_renderer_keys_get_property (GObject
*object
,
37 static void egg_cell_renderer_keys_set_property (GObject
*object
,
41 static void egg_cell_renderer_keys_get_size (GtkCellRenderer
*cell
,
43 GdkRectangle
*cell_area
,
58 static GtkCellRendererTextClass
*parent_class
= NULL
;
61 egg_cell_renderer_keys_get_type (void)
63 static GType cell_keys_type
= 0;
67 static const GTypeInfo cell_keys_info
=
69 sizeof (EggCellRendererKeysClass
),
71 NULL
, /* base_finalize */
72 (GClassInitFunc
)egg_cell_renderer_keys_class_init
,
73 NULL
, /* class_finalize */
74 NULL
, /* class_data */
75 sizeof (EggCellRendererKeys
),
77 (GInstanceInitFunc
) egg_cell_renderer_keys_init
80 cell_keys_type
= g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT
, "EggCellRendererKeys", &cell_keys_info
, 0);
83 return cell_keys_type
;
87 egg_cell_renderer_keys_init (EggCellRendererKeys
*cell_keys
)
89 cell_keys
->accel_mode
= EGG_CELL_RENDERER_KEYS_MODE_GTK
;
92 /* FIXME setup stuff to generate this */
93 /* VOID:STRING,UINT,FLAGS,UINT */
95 marshal_VOID__STRING_UINT_FLAGS_UINT (GClosure
*closure
,
98 const GValue
*param_values
,
99 gpointer invocation_hint
,
100 gpointer marshal_data
)
102 typedef void (*GMarshalFunc_VOID__STRING_UINT_FLAGS_UINT
) (gpointer data1
,
108 register GMarshalFunc_VOID__STRING_UINT_FLAGS_UINT callback
;
109 register GCClosure
*cc
= (GCClosure
*) closure
;
110 register gpointer data1
, data2
;
112 g_return_if_fail (n_param_values
== 5);
114 if (G_CCLOSURE_SWAP_DATA (closure
))
116 data1
= closure
->data
;
117 data2
= g_value_peek_pointer (param_values
+ 0);
121 data1
= g_value_peek_pointer (param_values
+ 0);
122 data2
= closure
->data
;
125 callback
= (GMarshalFunc_VOID__STRING_UINT_FLAGS_UINT
) (marshal_data
? marshal_data
: cc
->callback
);
128 g_value_get_string (param_values
+ 1),
129 g_value_get_uint (param_values
+ 2),
130 g_value_get_flags (param_values
+ 3),
131 g_value_get_uint (param_values
+ 4),
136 egg_cell_renderer_keys_class_init (EggCellRendererKeysClass
*cell_keys_class
)
138 GObjectClass
*object_class
;
139 GtkCellRendererClass
*cell_renderer_class
;
141 object_class
= G_OBJECT_CLASS (cell_keys_class
);
142 cell_renderer_class
= GTK_CELL_RENDERER_CLASS (cell_keys_class
);
143 parent_class
= g_type_class_peek_parent (object_class
);
145 GTK_CELL_RENDERER_CLASS (cell_keys_class
)->start_editing
= egg_cell_renderer_keys_start_editing
;
147 object_class
->set_property
= egg_cell_renderer_keys_set_property
;
148 object_class
->get_property
= egg_cell_renderer_keys_get_property
;
149 cell_renderer_class
->get_size
= egg_cell_renderer_keys_get_size
;
151 object_class
->finalize
= egg_cell_renderer_keys_finalize
;
153 /* FIXME if this gets moved to a real library, rename the properties
154 * to match whatever the GTK convention is
157 g_object_class_install_property (object_class
,
159 g_param_spec_uint ("accel_key",
160 _("Accelerator key"),
161 _("Accelerator key"),
165 G_PARAM_READABLE
| G_PARAM_WRITABLE
));
167 g_object_class_install_property (object_class
,
169 g_param_spec_flags ("accel_mask",
170 _("Accelerator modifiers"),
171 _("Accelerator modifiers"),
172 GDK_TYPE_MODIFIER_TYPE
,
174 G_PARAM_READABLE
| G_PARAM_WRITABLE
));
176 /* FIXME: Register the enum when moving to GTK+ */
177 g_object_class_install_property (object_class
,
179 g_param_spec_int ("accel_mode",
180 _("Accelerator Mode"),
181 _("The type of accelerator."),
185 G_PARAM_READABLE
| G_PARAM_WRITABLE
));
187 g_signal_new ("keys_edited",
188 EGG_TYPE_CELL_RENDERER_KEYS
,
190 G_STRUCT_OFFSET (EggCellRendererKeysClass
, keys_edited
),
192 marshal_VOID__STRING_UINT_FLAGS_UINT
,
196 GDK_TYPE_MODIFIER_TYPE
,
202 egg_cell_renderer_keys_new (void)
204 return GTK_CELL_RENDERER (g_object_new (EGG_TYPE_CELL_RENDERER_KEYS
, NULL
));
208 egg_cell_renderer_keys_finalize (GObject
*object
)
211 (* G_OBJECT_CLASS (parent_class
)->finalize
) (object
);
215 convert_keysym_state_to_string (guint keysym
,
216 EggVirtualModifierType mask
)
219 return g_strdup (_("Disabled"));
221 return egg_virtual_accelerator_name (keysym
, mask
);
225 egg_cell_renderer_keys_get_property (GObject
*object
,
230 EggCellRendererKeys
*keys
;
232 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (object
));
234 keys
= EGG_CELL_RENDERER_KEYS (object
);
239 g_value_set_uint (value
, keys
->accel_key
);
242 case PROP_ACCEL_MASK
:
243 g_value_set_flags (value
, keys
->accel_mask
);
246 case PROP_ACCEL_MODE
:
247 g_value_set_int (value
, keys
->accel_mode
);
251 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
256 egg_cell_renderer_keys_set_property (GObject
*object
,
261 EggCellRendererKeys
*keys
;
263 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (object
));
265 keys
= EGG_CELL_RENDERER_KEYS (object
);
270 egg_cell_renderer_keys_set_accelerator (keys
,
271 g_value_get_uint (value
),
275 case PROP_ACCEL_MASK
:
276 egg_cell_renderer_keys_set_accelerator (keys
,
278 g_value_get_flags (value
));
281 case PROP_ACCEL_MODE
:
282 egg_cell_renderer_keys_set_accel_mode (keys
, g_value_get_int (value
));
286 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
291 is_modifier (guint keycode
)
295 XModifierKeymap
*mod_keymap
;
296 gboolean retval
= FALSE
;
298 mod_keymap
= XGetModifierMapping (gdk_display
);
300 map_size
= 8 * mod_keymap
->max_keypermod
;
304 if (keycode
== mod_keymap
->modifiermap
[i
])
312 XFreeModifiermap (mod_keymap
);
318 egg_cell_renderer_keys_get_size (GtkCellRenderer
*cell
,
320 GdkRectangle
*cell_area
,
326 EggCellRendererKeys
*keys
= (EggCellRendererKeys
*) cell
;
327 GtkRequisition requisition
;
329 if (keys
->sizing_label
== NULL
)
330 keys
->sizing_label
= gtk_label_new (_("Type a new accelerator, or press Backspace to clear"));
332 gtk_widget_size_request (keys
->sizing_label
, &requisition
);
333 (* GTK_CELL_RENDERER_CLASS (parent_class
)->get_size
) (cell
, widget
, cell_area
, x_offset
, y_offset
, width
, height
);
334 /* FIXME: need to take the cell_area et al. into account */
336 *width
= MAX (*width
, requisition
.width
);
338 *height
= MAX (*height
, requisition
.height
);
341 /* FIXME: Currently we don't differentiate between a 'bogus' key (like tab in
342 * GTK mode) and a removed key.
346 grab_key_callback (GtkWidget
*widget
,
350 GdkModifierType accel_mods
;
352 EggCellRendererKeys
*keys
;
355 GdkModifierType consumed_modifiers
;
357 GdkModifierType ignored_modifiers
;
359 keys
= EGG_CELL_RENDERER_KEYS (data
);
362 if (is_modifier (event
->hardware_keycode
))
367 consumed_modifiers
= 0;
368 gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (),
369 event
->hardware_keycode
,
372 NULL
, NULL
, NULL
, &consumed_modifiers
);
374 upper
= event
->keyval
;
375 accel_keyval
= gdk_keyval_to_lower (upper
);
376 if (accel_keyval
== GDK_ISO_Left_Tab
)
377 accel_keyval
= GDK_Tab
;
381 /* Put shift back if it changed the case of the key, not otherwise.
383 if (upper
!= accel_keyval
&&
384 (consumed_modifiers
& GDK_SHIFT_MASK
))
386 consumed_modifiers
&= ~(GDK_SHIFT_MASK
);
389 egg_keymap_resolve_virtual_modifiers (gdk_keymap_get_default (),
390 EGG_VIRTUAL_NUM_LOCK_MASK
|
391 EGG_VIRTUAL_SCROLL_LOCK_MASK
,
394 /* filter consumed/ignored modifiers */
396 if (keys
->accel_mode
== EGG_CELL_RENDERER_KEYS_MODE_GTK
)
397 accel_mods
= event
->state
& ~(consumed_modifiers
| ignored_modifiers
);
398 else if (keys
->accel_mode
== EGG_CELL_RENDERER_KEYS_MODE_X
)
399 accel_mods
= event
->state
& ~(ignored_modifiers
);
401 g_assert_not_reached ();
403 if (accel_mods
== 0 && accel_keyval
== GDK_Escape
)
404 goto out
; /* cancel */
406 /* clear the accelerator on Backspace */
407 if (keys
->edit_key
!= 0 &&
409 accel_keyval
== GDK_BackSpace
)
412 if (keys
->accel_mode
== EGG_CELL_RENDERER_KEYS_MODE_GTK
)
414 if (!gtk_accelerator_valid (accel_keyval
, accel_mods
))
420 /* Remove modifiers like super and hyper, as GTK+ ignores them. */
421 accel_mods
= accel_mods
& GDK_MODIFIER_MASK
;
426 path
= g_strdup (g_object_get_data (G_OBJECT (keys
->edit_widget
),
427 EGG_CELL_RENDERER_TEXT_PATH
));
429 gdk_keyboard_ungrab (event
->time
);
430 gdk_pointer_ungrab (event
->time
);
432 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (keys
->edit_widget
));
433 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (keys
->edit_widget
));
434 keys
->edit_widget
= NULL
;
435 keys
->grab_widget
= NULL
;
438 g_signal_emit_by_name (G_OBJECT (keys
), "keys_edited", path
,
439 accel_keyval
, accel_mods
, event
->hardware_keycode
);
447 ungrab_stuff (GtkWidget
*widget
, gpointer data
)
449 EggCellRendererKeys
*keys
= EGG_CELL_RENDERER_KEYS (data
);
451 gdk_keyboard_ungrab (GDK_CURRENT_TIME
);
452 gdk_pointer_ungrab (GDK_CURRENT_TIME
);
454 g_signal_handlers_disconnect_by_func (G_OBJECT (keys
->grab_widget
),
455 G_CALLBACK (grab_key_callback
), data
);
459 pointless_eventbox_start_editing (GtkCellEditable
*cell_editable
,
462 /* do nothing, because we are pointless */
466 pointless_eventbox_cell_editable_init (GtkCellEditableIface
*iface
)
468 iface
->start_editing
= pointless_eventbox_start_editing
;
472 pointless_eventbox_subclass_get_type (void)
474 static GType eventbox_type
= 0;
478 static const GTypeInfo eventbox_info
=
480 sizeof (GtkEventBoxClass
),
481 NULL
, /* base_init */
482 NULL
, /* base_finalize */
484 NULL
, /* class_finalize */
485 NULL
, /* class_data */
486 sizeof (GtkEventBox
),
488 (GInstanceInitFunc
) NULL
,
491 static const GInterfaceInfo cell_editable_info
= {
492 (GInterfaceInitFunc
) pointless_eventbox_cell_editable_init
,
495 eventbox_type
= g_type_register_static (GTK_TYPE_EVENT_BOX
, "EggCellEditableEventBox", &eventbox_info
, 0);
497 g_type_add_interface_static (eventbox_type
,
498 GTK_TYPE_CELL_EDITABLE
,
499 &cell_editable_info
);
502 return eventbox_type
;
505 static GtkCellEditable
*
506 egg_cell_renderer_keys_start_editing (GtkCellRenderer
*cell
,
510 GdkRectangle
*background_area
,
511 GdkRectangle
*cell_area
,
512 GtkCellRendererState flags
)
514 GtkCellRendererText
*celltext
;
515 EggCellRendererKeys
*keys
;
519 celltext
= GTK_CELL_RENDERER_TEXT (cell
);
520 keys
= EGG_CELL_RENDERER_KEYS (cell
);
522 /* If the cell isn't editable we return NULL. */
523 if (celltext
->editable
== FALSE
)
526 g_return_val_if_fail (widget
->window
!= NULL
, NULL
);
528 if (gdk_keyboard_grab (widget
->window
, FALSE
,
529 gdk_event_get_time (event
)) != GDK_GRAB_SUCCESS
)
532 if (gdk_pointer_grab (widget
->window
, FALSE
,
533 GDK_BUTTON_PRESS_MASK
,
535 gdk_event_get_time (event
)) != GDK_GRAB_SUCCESS
)
537 gdk_keyboard_ungrab (gdk_event_get_time (event
));
541 keys
->grab_widget
= widget
;
543 g_signal_connect (G_OBJECT (widget
), "key_press_event",
544 G_CALLBACK (grab_key_callback
),
547 eventbox
= g_object_new (pointless_eventbox_subclass_get_type (),
549 keys
->edit_widget
= eventbox
;
550 g_object_add_weak_pointer (G_OBJECT (keys
->edit_widget
),
551 (void**) &keys
->edit_widget
);
553 label
= gtk_label_new (NULL
);
554 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
556 gtk_widget_modify_bg (eventbox
, GTK_STATE_NORMAL
,
557 &widget
->style
->bg
[GTK_STATE_SELECTED
]);
559 gtk_widget_modify_fg (label
, GTK_STATE_NORMAL
,
560 &widget
->style
->fg
[GTK_STATE_SELECTED
]);
562 if (keys
->accel_key
!= 0)
563 gtk_label_set_text (GTK_LABEL (label
),
564 _("Type a new accelerator, or press Backspace to clear"));
566 gtk_label_set_text (GTK_LABEL (label
),
567 _("Type a new accelerator"));
569 gtk_container_add (GTK_CONTAINER (eventbox
), label
);
571 g_object_set_data_full (G_OBJECT (keys
->edit_widget
), EGG_CELL_RENDERER_TEXT_PATH
,
572 g_strdup (path
), g_free
);
574 gtk_widget_show_all (keys
->edit_widget
);
576 g_signal_connect (G_OBJECT (keys
->edit_widget
), "unrealize",
577 G_CALLBACK (ungrab_stuff
), keys
);
579 keys
->edit_key
= keys
->accel_key
;
581 return GTK_CELL_EDITABLE (keys
->edit_widget
);
585 egg_cell_renderer_keys_set_accelerator (EggCellRendererKeys
*keys
,
587 EggVirtualModifierType mask
)
591 GtkCellRendererText
*celltext
;
593 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (keys
));
595 g_object_freeze_notify (G_OBJECT (keys
));
599 if (keyval
!= keys
->accel_key
)
601 keys
->accel_key
= keyval
;
602 g_object_notify (G_OBJECT (keys
), "accel_key");
606 if (mask
!= keys
->accel_mask
)
608 keys
->accel_mask
= mask
;
610 g_object_notify (G_OBJECT (keys
), "accel_mask");
613 g_object_thaw_notify (G_OBJECT (keys
));
617 /* sync string to the key values */
618 celltext
= GTK_CELL_RENDERER_TEXT (keys
);
619 text
= convert_keysym_state_to_string (keys
->accel_key
, keys
->accel_mask
);
620 g_object_set (keys
, "text", text
, NULL
);
627 egg_cell_renderer_keys_get_accelerator (EggCellRendererKeys
*keys
,
629 EggVirtualModifierType
*mask
)
631 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (keys
));
634 *keyval
= keys
->accel_key
;
637 *mask
= keys
->accel_mask
;
641 egg_cell_renderer_keys_set_accel_mode (EggCellRendererKeys
*keys
,
642 EggCellRendererKeysMode accel_mode
)
644 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (keys
));
645 keys
->accel_mode
= accel_mode
;
646 g_object_notify (G_OBJECT (keys
), "accel_mode");