]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrendereraccel.c
e6ff68b90c2b0d4e3c793a355165cbfcdcdc9e68
[~andy/gtk] / gtk / gtkcellrendereraccel.c
1 /* gtkcellrendereraccel.h
2  * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include "gtkintl.h"
22 #include "gtkaccelgroup.h"
23 #include "gtkmarshalers.h"
24 #include "gtkcellrendereraccel.h"
25 #include "gtklabel.h"
26 #include "gtkeventbox.h"
27 #include "gtkprivate.h"
28 #include "gdk/gdkkeysyms.h"
29 #include "gtkalias.h"
30
31
32 static void gtk_cell_renderer_accel_get_property (GObject         *object,
33                                                   guint            param_id,
34                                                   GValue          *value,
35                                                   GParamSpec      *pspec);
36 static void gtk_cell_renderer_accel_set_property (GObject         *object,
37                                                   guint            param_id,
38                                                   const GValue    *value,
39                                                   GParamSpec      *pspec);
40 static void gtk_cell_renderer_accel_get_size     (GtkCellRenderer *cell,
41                                                   GtkWidget       *widget,
42                                                   GdkRectangle    *cell_area,
43                                                   gint            *x_offset,
44                                                   gint            *y_offset,
45                                                   gint            *width,
46                                                   gint            *height);
47 static GtkCellEditable *
48            gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell,
49                                                   GdkEvent        *event,
50                                                   GtkWidget       *widget,
51                                                   const gchar     *path,
52                                                   GdkRectangle    *background_area,
53                                                   GdkRectangle    *cell_area,
54                                                   GtkCellRendererState flags);
55
56 enum {
57   ACCEL_EDITED,
58   ACCEL_CLEARED,
59   LAST_SIGNAL
60 };
61
62 enum {
63   PROP_0,
64   PROP_ACCEL_KEY,
65   PROP_ACCEL_MODS,
66   PROP_KEYCODE,
67   PROP_ACCEL_MODE
68 };
69
70 static guint signals[LAST_SIGNAL] = { 0 };
71
72 G_DEFINE_TYPE (GtkCellRendererAccel, gtk_cell_renderer_accel, GTK_TYPE_CELL_RENDERER_TEXT)
73
74 static void
75 gtk_cell_renderer_accel_init (GtkCellRendererAccel *cell_accel)
76 {
77 }
78
79 static void
80 gtk_cell_renderer_accel_class_init (GtkCellRendererAccelClass *cell_accel_class)
81 {
82   GObjectClass *object_class;
83   GtkCellRendererClass *cell_renderer_class;
84
85   object_class = G_OBJECT_CLASS (cell_accel_class);
86   cell_renderer_class = GTK_CELL_RENDERER_CLASS (cell_accel_class);
87
88   object_class->set_property = gtk_cell_renderer_accel_set_property;
89   object_class->get_property = gtk_cell_renderer_accel_get_property;
90
91   cell_renderer_class->get_size      = gtk_cell_renderer_accel_get_size;
92   cell_renderer_class->start_editing = gtk_cell_renderer_accel_start_editing;
93
94   /**
95    * GtkCellRendererAccel:accel-key:
96    *
97    * The keyval of the accelerator.
98    *
99    * Since: 2.10
100    */
101   g_object_class_install_property (object_class,
102                                    PROP_ACCEL_KEY,
103                                    g_param_spec_uint ("accel-key",
104                                                      P_("Accelerator key"),
105                                                      P_("The keyval of the accelerator"),
106                                                       0,
107                                                       G_MAXINT,
108                                                       0,
109                                                       GTK_PARAM_READWRITE));
110   
111   /**
112    * GtkCellRendererAccel:accel-mods:
113    *
114    * The modifier mask of the accelerator.
115    *
116    * Since: 2.10
117    */
118   g_object_class_install_property (object_class,
119                                    PROP_ACCEL_MODS,
120                                    g_param_spec_flags ("accel-mods",
121                                                        P_("Accelerator modifiers"),
122                                                        P_("The modifier mask of the accelerator"),
123                                                        GDK_TYPE_MODIFIER_TYPE,
124                                                        0,
125                                                        GTK_PARAM_READWRITE));
126
127   /**
128    * GtkCellRendererAccel:keycode:
129    *
130    * The hardware keycode of the accelerator. Note that the hardware keycode is
131    * only relevant if the key does not have a keyval. Normally, the keyboard
132    * configuration should assign keyvals to all keys.
133    *
134    * Since: 2.10
135    */ 
136   g_object_class_install_property (object_class,
137                                    PROP_KEYCODE,
138                                    g_param_spec_uint ("keycode",
139                                                       P_("Accelerator keycode"),
140                                                       P_("The hardware keycode of the accelerator"),
141                                                       0,
142                                                       G_MAXINT,
143                                                       0,
144                                                       GTK_PARAM_READWRITE));
145
146   /**
147    * GtkCellRendererAccel:accel-mode:
148    *
149    * Determines if the edited accelerators are GTK+ accelerators. If
150    * they are, consumed modifiers are suppressed, only accelerators
151    * accepted by GTK+ are allowed, and the accelerators are rendered
152    * in the same way as they are in menus.
153    *
154    * Since: 2.10
155    */
156   g_object_class_install_property (object_class,
157                                    PROP_ACCEL_MODE,
158                                    g_param_spec_enum ("accel-mode",
159                                                       P_("Accelerator Mode"),
160                                                       P_("The type of accelerators"),
161                                                       GTK_TYPE_CELL_RENDERER_ACCEL_MODE,
162                                                       GTK_CELL_RENDERER_ACCEL_MODE_GTK,
163                                                       GTK_PARAM_READWRITE));
164   
165   /**
166    * GtkCellRendererAccel::accel-edited:
167    * @accel: the object reveiving the signal
168    * @path_string: the path identifying the row of the edited cell
169    * @accel_key: the new accelerator keyval
170    * @accel_mods: the new acclerator modifier mask
171    * @hardware_keycode: the keycode of the new accelerator
172    *
173    * Gets emitted when the user has selected a new accelerator.
174    *
175    * Since: 2.10
176    */
177   signals[ACCEL_EDITED] = g_signal_new (I_("accel-edited"),
178                                         GTK_TYPE_CELL_RENDERER_ACCEL,
179                                         G_SIGNAL_RUN_LAST,
180                                         G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_edited),
181                                         NULL, NULL,
182                                         _gtk_marshal_VOID__STRING_UINT_FLAGS_UINT,
183                                         G_TYPE_NONE, 4,
184                                         G_TYPE_STRING,
185                                         G_TYPE_UINT,
186                                         GDK_TYPE_MODIFIER_TYPE,
187                                         G_TYPE_UINT);
188
189   /**
190    * GtkCellRendererAccel::accel-cleared:
191    * @accel: the object reveiving the signal
192    * @path_string: the path identifying the row of the edited cell
193    *
194    * Gets emitted when the user has removed the accelerator.
195    *
196    * Since: 2.10
197    */
198   signals[ACCEL_CLEARED] = g_signal_new (I_("accel-cleared"),
199                                          GTK_TYPE_CELL_RENDERER_ACCEL,
200                                          G_SIGNAL_RUN_LAST,
201                                          G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_cleared),
202                                          NULL, NULL,
203                                          g_cclosure_marshal_VOID__STRING,
204                                          G_TYPE_NONE, 1,
205                                          G_TYPE_STRING);
206 }
207
208
209 /**
210  * gtk_cell_renderer_accel_new:
211  *
212  * Creates a new #GtkCellRendererAccel.
213  * 
214  * Returns: the new cell renderer
215  *
216  * Since: 2.10
217  */
218 GtkCellRenderer *
219 gtk_cell_renderer_accel_new (void)
220 {
221   return g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL, NULL);
222 }
223
224 static gchar *
225 convert_keysym_state_to_string (GtkCellRendererAccel *accel,
226                                 guint                 keysym,
227                                 GdkModifierType       mask,
228                                 guint                 keycode)
229 {
230   if (keysym == 0 && keycode == 0)
231     /* This label is displayed in a treeview cell displaying
232      * a disabled accelerator key combination. Only include
233      * the text after the | in the translation.
234      */
235     return g_strdup (Q_("Accelerator|Disabled"));
236   else 
237     {
238       if (accel->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
239         return gtk_accelerator_get_label (keysym, mask);
240       else 
241         {
242           gchar *name;
243
244           name = gtk_accelerator_name (keysym, mask);
245           if (keysym == 0)
246             {
247               gchar *tmp;
248
249               tmp = name;
250               name = g_strdup_printf ("%s0x%02x", tmp, keycode);
251               g_free (tmp);
252             }
253
254           return name;
255         }
256     }
257 }
258
259 static void
260 gtk_cell_renderer_accel_get_property  (GObject    *object,
261                                        guint       param_id,
262                                        GValue     *value,
263                                        GParamSpec *pspec)
264 {
265   GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (object);
266
267   switch (param_id)
268     {
269     case PROP_ACCEL_KEY:
270       g_value_set_uint (value, accel->accel_key);
271       break;
272
273     case PROP_ACCEL_MODS:
274       g_value_set_flags (value, accel->accel_mods);
275       break;
276
277     case PROP_KEYCODE:
278       g_value_set_uint (value, accel->keycode);
279       break;
280
281     case PROP_ACCEL_MODE:
282       g_value_set_enum (value, accel->accel_mode);
283       break;
284
285     default:
286       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
287     }
288 }
289
290 static void
291 gtk_cell_renderer_accel_set_property  (GObject      *object,
292                                        guint         param_id,
293                                        const GValue *value,
294                                        GParamSpec   *pspec)
295 {
296   GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (object);
297   gboolean changed = FALSE;
298
299   switch (param_id)
300     {
301     case PROP_ACCEL_KEY:
302       {
303         guint accel_key = g_value_get_uint (value);
304
305         if (accel->accel_key != accel_key)
306           {
307             accel->accel_key = accel_key;
308             changed = TRUE;
309           }
310       }
311       break;
312
313     case PROP_ACCEL_MODS:
314       {
315         guint accel_mods = g_value_get_flags (value);
316
317         if (accel->accel_mods != accel_mods)
318           {
319             accel->accel_mods = accel_mods;
320             changed = TRUE;
321           }
322       }
323       break;
324     case PROP_KEYCODE:
325       {
326         guint keycode = g_value_get_uint (value);
327
328         if (accel->keycode != keycode)
329           {
330             accel->keycode = keycode;
331             changed = TRUE;
332           }
333       }
334       break;
335
336     case PROP_ACCEL_MODE:
337       accel->accel_mode = g_value_get_enum (value);
338       break;
339       
340     default:
341       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
342     }
343
344   if (changed)
345     {
346       gchar *text;
347
348       text = convert_keysym_state_to_string (accel, accel->accel_key, accel->accel_mods, accel->keycode);
349       g_object_set (accel, "text", text, NULL);
350       g_free (text);
351     }
352 }
353
354 static void
355 gtk_cell_renderer_accel_get_size (GtkCellRenderer *cell,
356                                   GtkWidget       *widget,
357                                   GdkRectangle    *cell_area,
358                                   gint            *x_offset,
359                                   gint            *y_offset,
360                                   gint            *width,
361                                   gint            *height)
362
363 {
364   GtkCellRendererAccel *accel = (GtkCellRendererAccel *) cell;
365   GtkRequisition requisition;
366
367   if (accel->sizing_label == NULL)
368     accel->sizing_label = gtk_label_new (_("New accelerator..."));
369
370   gtk_widget_size_request (accel->sizing_label, &requisition);
371
372   GTK_CELL_RENDERER_CLASS (gtk_cell_renderer_accel_parent_class)->get_size (cell, widget, cell_area,
373                                                                             x_offset, y_offset, width, height);
374
375   /* FIXME: need to take the cell_area et al. into account */
376   if (width)
377     *width = MAX (*width, requisition.width);
378   if (height)
379     *height = MAX (*height, requisition.height);
380 }
381
382 static gboolean
383 grab_key_callback (GtkWidget            *widget,
384                    GdkEventKey          *event,
385                    GtkCellRendererAccel *accel)
386 {
387   GdkModifierType accel_mods = 0;
388   guint accel_key;
389   gchar *path;
390   gboolean edited;
391   gboolean cleared;
392   GdkModifierType consumed_modifiers;
393   GdkDisplay *display;
394
395   display = gtk_widget_get_display (widget);
396
397   if (event->is_modifier)
398     return TRUE;
399
400   edited = FALSE;
401   cleared = FALSE;
402
403   gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (display),
404                                        event->hardware_keycode,
405                                        event->state,
406                                        event->group,
407                                        NULL, NULL, NULL, &consumed_modifiers);
408
409   accel_key = gdk_keyval_to_lower (event->keyval);
410   if (accel_key == GDK_ISO_Left_Tab) 
411     accel_key = GDK_Tab;
412
413   accel_mods = event->state & gtk_accelerator_get_default_mod_mask ();
414
415   /* Filter consumed modifiers 
416    */
417   if (accel->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
418     accel_mods &= ~consumed_modifiers;
419   
420   /* Put shift back if it changed the case of the key, not otherwise.
421    */
422   if (accel_key != event->keyval)
423     accel_mods |= GDK_SHIFT_MASK;
424     
425   if (accel_mods == 0)
426     {
427       switch (event->keyval)
428         {
429         case GDK_Escape:
430           goto out; /* cancel */
431         case GDK_BackSpace:
432           /* clear the accelerator on Backspace */
433           cleared = TRUE;
434           goto out;
435         default:
436           break;
437         }
438     }
439
440   if (accel->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
441     {
442       if (!gtk_accelerator_valid (accel_key, accel_mods))
443         {
444           gtk_widget_error_bell (widget);
445
446           return TRUE;
447         }
448     }
449
450   edited = TRUE;
451
452  out:
453   gdk_display_keyboard_ungrab (display, event->time);
454   gdk_display_pointer_ungrab (display, event->time);
455
456   path = g_strdup (g_object_get_data (G_OBJECT (accel->edit_widget), "gtk-cell-renderer-text"));
457
458   gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (accel->edit_widget));
459   gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (accel->edit_widget));
460   accel->edit_widget = NULL;
461   accel->grab_widget = NULL;
462   
463   if (edited)
464     g_signal_emit (accel, signals[ACCEL_EDITED], 0, path, 
465                    accel_key, accel_mods, event->hardware_keycode);
466   else if (cleared)
467     g_signal_emit (accel, signals[ACCEL_CLEARED], 0, path);
468
469   g_free (path);
470
471   return TRUE;
472 }
473
474 static void
475 ungrab_stuff (GtkWidget            *widget,
476               GtkCellRendererAccel *accel)
477 {
478   GdkDisplay *display = gtk_widget_get_display (widget);
479
480   gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
481   gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
482
483   g_signal_handlers_disconnect_by_func (G_OBJECT (accel->grab_widget),
484                                         G_CALLBACK (grab_key_callback),
485                                         accel);
486 }
487
488 static void
489 _gtk_cell_editable_event_box_start_editing (GtkCellEditable *cell_editable,
490                                             GdkEvent        *event)
491 {
492   /* do nothing, because we are pointless */
493 }
494
495 static void
496 _gtk_cell_editable_event_box_cell_editable_init (GtkCellEditableIface *iface)
497 {
498   iface->start_editing = _gtk_cell_editable_event_box_start_editing;
499 }
500
501 typedef GtkEventBox      GtkCellEditableEventBox;
502 typedef GtkEventBoxClass GtkCellEditableEventBoxClass;
503
504 G_DEFINE_TYPE_WITH_CODE (GtkCellEditableEventBox, _gtk_cell_editable_event_box, GTK_TYPE_EVENT_BOX, { \
505     G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, _gtk_cell_editable_event_box_cell_editable_init)   \
506       })
507
508
509 static void
510 _gtk_cell_editable_event_box_class_init (GtkCellEditableEventBoxClass *class)
511 {
512 }
513
514 static void
515 _gtk_cell_editable_event_box_init (GtkCellEditableEventBox *box)
516 {
517 }
518
519 static GtkCellEditable *
520 gtk_cell_renderer_accel_start_editing (GtkCellRenderer      *cell,
521                                        GdkEvent             *event,
522                                        GtkWidget            *widget,
523                                        const gchar          *path,
524                                        GdkRectangle         *background_area,
525                                        GdkRectangle         *cell_area,
526                                        GtkCellRendererState  flags)
527 {
528   GtkCellRendererText *celltext;
529   GtkCellRendererAccel *accel;
530   GtkWidget *label;
531   GtkWidget *eventbox;
532   
533   celltext = GTK_CELL_RENDERER_TEXT (cell);
534   accel = GTK_CELL_RENDERER_ACCEL (cell);
535
536   /* If the cell isn't editable we return NULL. */
537   if (celltext->editable == FALSE)
538     return NULL;
539
540   g_return_val_if_fail (widget->window != NULL, NULL);
541   
542   if (gdk_keyboard_grab (widget->window, FALSE,
543                          gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
544     return NULL;
545
546   if (gdk_pointer_grab (widget->window, FALSE,
547                         GDK_BUTTON_PRESS_MASK,
548                         NULL, NULL,
549                         gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
550     {
551       gdk_display_keyboard_ungrab (gtk_widget_get_display (widget),
552                                    gdk_event_get_time (event));
553       return NULL;
554     }
555   
556   accel->grab_widget = widget;
557
558   g_signal_connect (G_OBJECT (widget), "key_press_event",
559                     G_CALLBACK (grab_key_callback),
560                     accel);
561
562   eventbox = g_object_new (_gtk_cell_editable_event_box_get_type (), NULL);
563   accel->edit_widget = eventbox;
564   g_object_add_weak_pointer (G_OBJECT (accel->edit_widget),
565                              (gpointer) &accel->edit_widget);
566   
567   label = gtk_label_new (NULL);
568   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
569   
570   gtk_widget_modify_bg (eventbox, GTK_STATE_NORMAL,
571                         &widget->style->bg[GTK_STATE_SELECTED]);
572
573   gtk_widget_modify_fg (label, GTK_STATE_NORMAL,
574                         &widget->style->fg[GTK_STATE_SELECTED]);
575   
576   /* This label is displayed in a treeview cell displaying
577    * an accelerator when the cell is clicked to change the 
578    * acelerator.
579    */
580   gtk_label_set_text (GTK_LABEL (label), _("New accelerator..."));
581
582   gtk_container_add (GTK_CONTAINER (eventbox), label);
583   
584   g_object_set_data_full (G_OBJECT (accel->edit_widget), "gtk-cell-renderer-text",
585                           g_strdup (path), g_free);
586   
587   gtk_widget_show_all (accel->edit_widget);
588
589   g_signal_connect (G_OBJECT (accel->edit_widget), "unrealize",
590                     G_CALLBACK (ungrab_stuff), accel);
591   
592   return GTK_CELL_EDITABLE (accel->edit_widget);
593 }
594
595
596 #define __GTK_CELL_RENDERER_ACCEL_C__
597 #include "gtkaliasdef.c"