]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrendereraccel.c
Updated Bulgarian translation by Alexander Shopov <ash@contact.bg>
[~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       gchar *text;
361
362       text = convert_keysym_state_to_string (accel, accel->accel_key, accel->accel_mods, accel->keycode);
363       g_object_set (accel, "text", text, NULL);
364       g_free (text);
365     }
366 }
367
368 static void
369 gtk_cell_renderer_accel_get_size (GtkCellRenderer *cell,
370                                   GtkWidget       *widget,
371                                   GdkRectangle    *cell_area,
372                                   gint            *x_offset,
373                                   gint            *y_offset,
374                                   gint            *width,
375                                   gint            *height)
376
377 {
378   GtkCellRendererAccel *accel = (GtkCellRendererAccel *) cell;
379   GtkRequisition requisition;
380
381   if (accel->sizing_label == NULL)
382     accel->sizing_label = gtk_label_new (_("New accelerator..."));
383
384   gtk_widget_size_request (accel->sizing_label, &requisition);
385   (* GTK_CELL_RENDERER_CLASS (gtk_cell_renderer_accel_parent_class)->get_size) (cell, widget, cell_area, 
386                                                                                 x_offset, y_offset, width, height);
387   /* FIXME: need to take the cell_area et al. into account */
388   if (width)
389     *width = MAX (*width, requisition.width);
390   if (height)
391     *height = MAX (*height, requisition.height);
392 }
393
394 static gboolean
395 grab_key_callback (GtkWidget    *widget,
396                    GdkEventKey  *event,
397                    void         *data)
398 {
399   GdkModifierType accel_mods = 0;
400   guint accel_key;
401   GtkCellRendererAccel *accel;
402   char *path;
403   gboolean edited;
404   gboolean cleared;
405   GdkModifierType consumed_modifiers;  
406   GdkDisplay *display;
407   
408   accel = GTK_CELL_RENDERER_ACCEL (data);
409
410   display = gtk_widget_get_display (widget);
411   
412   if (event->is_modifier)
413     return TRUE;
414
415   edited = FALSE;
416   cleared = FALSE;
417
418   gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (display),
419                                        event->hardware_keycode,
420                                        event->state,
421                                        event->group,
422                                        NULL, NULL, NULL, &consumed_modifiers);
423
424   accel_key = gdk_keyval_to_lower (event->keyval);
425   if (accel_key == GDK_ISO_Left_Tab) 
426     accel_key = GDK_Tab;
427
428   accel_mods = event->state & gtk_accelerator_get_default_mod_mask ();
429
430   /* Filter consumed modifiers 
431    */
432   if (accel->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
433     accel_mods &= ~consumed_modifiers;
434   
435   /* Put shift back if it changed the case of the key, not otherwise.
436    */
437   if (accel_key != event->keyval)
438     accel_mods |= GDK_SHIFT_MASK;
439     
440   if (accel_mods == 0)
441     {
442       switch (event->keyval)
443         {
444         case GDK_Escape:
445           goto out; /* cancel */
446         case GDK_BackSpace:
447           /* clear the accelerator on Backspace */
448           cleared = TRUE;
449           goto out;
450         default:
451           break;
452         }
453     }
454
455   if (accel->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
456     {
457       if (!gtk_accelerator_valid (accel_key, accel_mods))
458         {
459           gtk_widget_error_bell (widget);
460
461           return TRUE;
462         }
463     }
464
465   edited = TRUE;
466
467  out:
468   gdk_keyboard_ungrab (event->time);
469   gdk_pointer_ungrab (event->time);
470   
471   path = g_strdup (g_object_get_data (G_OBJECT (accel->edit_widget), "gtk-cell-renderer-text"));
472
473   gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (accel->edit_widget));
474   gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (accel->edit_widget));
475   accel->edit_widget = NULL;
476   accel->grab_widget = NULL;
477   
478   if (edited)
479     g_signal_emit (accel, signals[ACCEL_EDITED], 0, path, 
480                    accel_key, accel_mods, event->hardware_keycode);
481   else if (cleared)
482     g_signal_emit (accel, signals[ACCEL_CLEARED], 0, path);
483
484   g_free (path);
485
486   return TRUE;
487 }
488
489 static void
490 ungrab_stuff (GtkWidget *widget,
491               gpointer   data)
492 {
493   GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (data);
494
495   gdk_keyboard_ungrab (GDK_CURRENT_TIME);
496   gdk_pointer_ungrab (GDK_CURRENT_TIME);
497
498   g_signal_handlers_disconnect_by_func (G_OBJECT (accel->grab_widget),
499                                         G_CALLBACK (grab_key_callback), data);
500 }
501
502 static void
503 _gtk_cell_editable_event_box_start_editing (GtkCellEditable *cell_editable,
504                                             GdkEvent        *event)
505 {
506   /* do nothing, because we are pointless */
507 }
508
509 static void
510 _gtk_cell_editable_event_box_cell_editable_init (GtkCellEditableIface *iface)
511 {
512   iface->start_editing = _gtk_cell_editable_event_box_start_editing;
513 }
514
515 typedef GtkEventBox      GtkCellEditableEventBox;
516 typedef GtkEventBoxClass GtkCellEditableEventBoxClass;
517
518 G_DEFINE_TYPE_WITH_CODE (GtkCellEditableEventBox, _gtk_cell_editable_event_box, GTK_TYPE_EVENT_BOX, { \
519     G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, _gtk_cell_editable_event_box_cell_editable_init)   \
520       })
521
522
523 static void
524 _gtk_cell_editable_event_box_class_init (GtkCellEditableEventBoxClass *class)
525 {
526 }
527
528 static void
529 _gtk_cell_editable_event_box_init (GtkCellEditableEventBox *box)
530 {
531 }
532
533 static GtkCellEditable *
534 gtk_cell_renderer_accel_start_editing (GtkCellRenderer      *cell,
535                                        GdkEvent             *event,
536                                        GtkWidget            *widget,
537                                        const gchar          *path,
538                                        GdkRectangle         *background_area,
539                                        GdkRectangle         *cell_area,
540                                        GtkCellRendererState  flags)
541 {
542   GtkCellRendererText *celltext;
543   GtkCellRendererAccel *accel;
544   GtkWidget *label;
545   GtkWidget *eventbox;
546   
547   celltext = GTK_CELL_RENDERER_TEXT (cell);
548   accel = GTK_CELL_RENDERER_ACCEL (cell);
549
550   /* If the cell isn't editable we return NULL. */
551   if (celltext->editable == FALSE)
552     return NULL;
553
554   g_return_val_if_fail (widget->window != NULL, NULL);
555   
556   if (gdk_keyboard_grab (widget->window, FALSE,
557                          gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
558     return NULL;
559
560   if (gdk_pointer_grab (widget->window, FALSE,
561                         GDK_BUTTON_PRESS_MASK,
562                         NULL, NULL,
563                         gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
564     {
565       gdk_keyboard_ungrab (gdk_event_get_time (event));
566       return NULL;
567     }
568   
569   accel->grab_widget = widget;
570
571   g_signal_connect (G_OBJECT (widget), "key_press_event",
572                     G_CALLBACK (grab_key_callback),
573                     accel);
574
575   eventbox = g_object_new (_gtk_cell_editable_event_box_get_type (), NULL);
576   accel->edit_widget = eventbox;
577   g_object_add_weak_pointer (G_OBJECT (accel->edit_widget),
578                              (void**) &accel->edit_widget);
579   
580   label = gtk_label_new (NULL);
581   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
582   
583   gtk_widget_modify_bg (eventbox, GTK_STATE_NORMAL,
584                         &widget->style->bg[GTK_STATE_SELECTED]);
585
586   gtk_widget_modify_fg (label, GTK_STATE_NORMAL,
587                         &widget->style->fg[GTK_STATE_SELECTED]);
588   
589   /* This label is displayed in a treeview cell displaying
590    * an accelerator when the cell is clicked to change the 
591    * acelerator.
592    */
593   gtk_label_set_text (GTK_LABEL (label), _("New accelerator..."));
594
595   gtk_container_add (GTK_CONTAINER (eventbox), label);
596   
597   g_object_set_data_full (G_OBJECT (accel->edit_widget), "gtk-cell-renderer-text",
598                           g_strdup (path), g_free);
599   
600   gtk_widget_show_all (accel->edit_widget);
601
602   g_signal_connect (G_OBJECT (accel->edit_widget), "unrealize",
603                     G_CALLBACK (ungrab_stuff), accel);
604   
605   return GTK_CELL_EDITABLE (accel->edit_widget);
606 }
607
608
609 #define __GTK_CELL_RENDERER_ACCEL_C__
610 #include "gtkaliasdef.c"