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