Updated Spanish translation
[anjuta-git-plugin.git] / libegg / eggcellrendererkeys.c
blobab99c0fb62b31d67eecbd48d8f4f2ab39f25dd91
1 #include <config.h>
2 #include <libintl.h>
3 #include <gtk/gtk.h>
4 #include <gdk/gdkx.h>
5 #include <gdk/gdkkeysyms.h>
6 #include "eggcellrendererkeys.h"
7 #include "eggaccelerators.h"
9 #ifndef EGG_COMPILATION
10 #ifndef _
11 #define _(x) dgettext (GETTEXT_PACKAGE, x)
12 #define N_(x) x
13 #endif
14 #else
15 #define _(x) x
16 #define N_(x) x
17 #endif
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,
25 GdkEvent *event,
26 GtkWidget *widget,
27 const gchar *path,
28 GdkRectangle *background_area,
29 GdkRectangle *cell_area,
30 GtkCellRendererState flags);
33 static void egg_cell_renderer_keys_get_property (GObject *object,
34 guint param_id,
35 GValue *value,
36 GParamSpec *pspec);
37 static void egg_cell_renderer_keys_set_property (GObject *object,
38 guint param_id,
39 const GValue *value,
40 GParamSpec *pspec);
41 static void egg_cell_renderer_keys_get_size (GtkCellRenderer *cell,
42 GtkWidget *widget,
43 GdkRectangle *cell_area,
44 gint *x_offset,
45 gint *y_offset,
46 gint *width,
47 gint *height);
50 enum {
51 PROP_0,
53 PROP_ACCEL_KEY,
54 PROP_ACCEL_MASK,
55 PROP_ACCEL_MODE
58 static GtkCellRendererTextClass *parent_class = NULL;
60 GType
61 egg_cell_renderer_keys_get_type (void)
63 static GType cell_keys_type = 0;
65 if (!cell_keys_type)
67 static const GTypeInfo cell_keys_info =
69 sizeof (EggCellRendererKeysClass),
70 NULL, /* base_init */
71 NULL, /* base_finalize */
72 (GClassInitFunc)egg_cell_renderer_keys_class_init,
73 NULL, /* class_finalize */
74 NULL, /* class_data */
75 sizeof (EggCellRendererKeys),
76 0, /* n_preallocs */
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;
86 static void
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 */
94 static void
95 marshal_VOID__STRING_UINT_FLAGS_UINT (GClosure *closure,
96 GValue *return_value,
97 guint n_param_values,
98 const GValue *param_values,
99 gpointer invocation_hint,
100 gpointer marshal_data)
102 typedef void (*GMarshalFunc_VOID__STRING_UINT_FLAGS_UINT) (gpointer data1,
103 const char *arg_1,
104 guint arg_2,
105 int arg_3,
106 guint arg_4,
107 gpointer data2);
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);
119 else
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);
127 callback (data1,
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),
132 data2);
135 static void
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,
158 PROP_ACCEL_KEY,
159 g_param_spec_uint ("accel_key",
160 _("Accelerator key"),
161 _("Accelerator key"),
163 G_MAXINT,
165 G_PARAM_READABLE | G_PARAM_WRITABLE));
167 g_object_class_install_property (object_class,
168 PROP_ACCEL_MASK,
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,
178 PROP_ACCEL_MODE,
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,
189 G_SIGNAL_RUN_LAST,
190 G_STRUCT_OFFSET (EggCellRendererKeysClass, keys_edited),
191 NULL, NULL,
192 marshal_VOID__STRING_UINT_FLAGS_UINT,
193 G_TYPE_NONE, 4,
194 G_TYPE_STRING,
195 G_TYPE_UINT,
196 GDK_TYPE_MODIFIER_TYPE,
197 G_TYPE_UINT);
201 GtkCellRenderer *
202 egg_cell_renderer_keys_new (void)
204 return GTK_CELL_RENDERER (g_object_new (EGG_TYPE_CELL_RENDERER_KEYS, NULL));
207 static void
208 egg_cell_renderer_keys_finalize (GObject *object)
211 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
214 static gchar *
215 convert_keysym_state_to_string (guint keysym,
216 EggVirtualModifierType mask)
218 if (keysym == 0)
219 return g_strdup (_("Disabled"));
220 else
221 return egg_virtual_accelerator_name (keysym, mask);
224 static void
225 egg_cell_renderer_keys_get_property (GObject *object,
226 guint param_id,
227 GValue *value,
228 GParamSpec *pspec)
230 EggCellRendererKeys *keys;
232 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (object));
234 keys = EGG_CELL_RENDERER_KEYS (object);
236 switch (param_id)
238 case PROP_ACCEL_KEY:
239 g_value_set_uint (value, keys->accel_key);
240 break;
242 case PROP_ACCEL_MASK:
243 g_value_set_flags (value, keys->accel_mask);
244 break;
246 case PROP_ACCEL_MODE:
247 g_value_set_int (value, keys->accel_mode);
248 break;
250 default:
251 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
255 static void
256 egg_cell_renderer_keys_set_property (GObject *object,
257 guint param_id,
258 const GValue *value,
259 GParamSpec *pspec)
261 EggCellRendererKeys *keys;
263 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (object));
265 keys = EGG_CELL_RENDERER_KEYS (object);
267 switch (param_id)
269 case PROP_ACCEL_KEY:
270 egg_cell_renderer_keys_set_accelerator (keys,
271 g_value_get_uint (value),
272 keys->accel_mask);
273 break;
275 case PROP_ACCEL_MASK:
276 egg_cell_renderer_keys_set_accelerator (keys,
277 keys->accel_key,
278 g_value_get_flags (value));
279 break;
281 case PROP_ACCEL_MODE:
282 egg_cell_renderer_keys_set_accel_mode (keys, g_value_get_int (value));
283 break;
285 default:
286 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
290 static gboolean
291 is_modifier (guint keycode)
293 gint i;
294 gint map_size;
295 XModifierKeymap *mod_keymap;
296 gboolean retval = FALSE;
298 mod_keymap = XGetModifierMapping (gdk_display);
300 map_size = 8 * mod_keymap->max_keypermod;
301 i = 0;
302 while (i < map_size)
304 if (keycode == mod_keymap->modifiermap[i])
306 retval = TRUE;
307 break;
309 ++i;
312 XFreeModifiermap (mod_keymap);
314 return retval;
317 void
318 egg_cell_renderer_keys_get_size (GtkCellRenderer *cell,
319 GtkWidget *widget,
320 GdkRectangle *cell_area,
321 gint *x_offset,
322 gint *y_offset,
323 gint *width,
324 gint *height)
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 */
335 if (width)
336 *width = MAX (*width, requisition.width);
337 if (height)
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.
345 static gboolean
346 grab_key_callback (GtkWidget *widget,
347 GdkEventKey *event,
348 void *data)
350 GdkModifierType accel_mods;
351 guint accel_keyval;
352 EggCellRendererKeys *keys;
353 char *path;
354 gboolean edited;
355 GdkModifierType consumed_modifiers;
356 guint upper;
357 GdkModifierType ignored_modifiers;
359 keys = EGG_CELL_RENDERER_KEYS (data);
360 accel_mods = 0;
362 if (is_modifier (event->hardware_keycode))
363 return TRUE;
365 edited = FALSE;
367 consumed_modifiers = 0;
368 gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (),
369 event->hardware_keycode,
370 event->state,
371 event->group,
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,
392 &ignored_modifiers);
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);
400 else
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 &&
408 accel_mods == 0 &&
409 accel_keyval == GDK_BackSpace)
410 accel_keyval = 0;
412 if (keys->accel_mode == EGG_CELL_RENDERER_KEYS_MODE_GTK)
414 if (!gtk_accelerator_valid (accel_keyval, accel_mods))
416 accel_keyval = 0;
417 accel_mods = 0;
420 /* Remove modifiers like super and hyper, as GTK+ ignores them. */
421 accel_mods = accel_mods & GDK_MODIFIER_MASK;
424 edited = TRUE;
425 out:
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;
437 if (edited)
438 g_signal_emit_by_name (G_OBJECT (keys), "keys_edited", path,
439 accel_keyval, accel_mods, event->hardware_keycode);
441 g_free (path);
443 return TRUE;
446 static void
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);
458 static void
459 pointless_eventbox_start_editing (GtkCellEditable *cell_editable,
460 GdkEvent *event)
462 /* do nothing, because we are pointless */
465 static void
466 pointless_eventbox_cell_editable_init (GtkCellEditableIface *iface)
468 iface->start_editing = pointless_eventbox_start_editing;
471 static GType
472 pointless_eventbox_subclass_get_type (void)
474 static GType eventbox_type = 0;
476 if (!eventbox_type)
478 static const GTypeInfo eventbox_info =
480 sizeof (GtkEventBoxClass),
481 NULL, /* base_init */
482 NULL, /* base_finalize */
483 NULL,
484 NULL, /* class_finalize */
485 NULL, /* class_data */
486 sizeof (GtkEventBox),
487 0, /* n_preallocs */
488 (GInstanceInitFunc) NULL,
491 static const GInterfaceInfo cell_editable_info = {
492 (GInterfaceInitFunc) pointless_eventbox_cell_editable_init,
493 NULL, NULL };
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,
507 GdkEvent *event,
508 GtkWidget *widget,
509 const gchar *path,
510 GdkRectangle *background_area,
511 GdkRectangle *cell_area,
512 GtkCellRendererState flags)
514 GtkCellRendererText *celltext;
515 EggCellRendererKeys *keys;
516 GtkWidget *label;
517 GtkWidget *eventbox;
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)
524 return NULL;
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)
530 return NULL;
532 if (gdk_pointer_grab (widget->window, FALSE,
533 GDK_BUTTON_PRESS_MASK,
534 FALSE, NULL,
535 gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
537 gdk_keyboard_ungrab (gdk_event_get_time (event));
538 return NULL;
541 keys->grab_widget = widget;
543 g_signal_connect (G_OBJECT (widget), "key_press_event",
544 G_CALLBACK (grab_key_callback),
545 keys);
547 eventbox = g_object_new (pointless_eventbox_subclass_get_type (),
548 NULL);
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"));
565 else
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);
584 void
585 egg_cell_renderer_keys_set_accelerator (EggCellRendererKeys *keys,
586 guint keyval,
587 EggVirtualModifierType mask)
589 char *text;
590 gboolean changed;
591 GtkCellRendererText *celltext;
593 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (keys));
595 g_object_freeze_notify (G_OBJECT (keys));
597 changed = FALSE;
599 if (keyval != keys->accel_key)
601 keys->accel_key = keyval;
602 g_object_notify (G_OBJECT (keys), "accel_key");
603 changed = TRUE;
606 if (mask != keys->accel_mask)
608 keys->accel_mask = mask;
610 g_object_notify (G_OBJECT (keys), "accel_mask");
611 changed = TRUE;
613 g_object_thaw_notify (G_OBJECT (keys));
615 if (changed)
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);
621 g_free (text);
626 void
627 egg_cell_renderer_keys_get_accelerator (EggCellRendererKeys *keys,
628 guint *keyval,
629 EggVirtualModifierType *mask)
631 g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (keys));
633 if (keyval)
634 *keyval = keys->accel_key;
636 if (mask)
637 *mask = keys->accel_mask;
640 void
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");