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