]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccellabel.c
accellabel: Remove gtk_widget_is_drawable() check from draw vfunc
[~andy/gtk] / gtk / gtkaccellabel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkAccelLabel: GtkLabel with accelerator monitoring facilities.
5  * Copyright (C) 1998 Tim Janik
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
25  * file for a list of people on the GTK+ Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
28  */
29
30 #include "config.h"
31 #include <string.h>
32
33 #include "gtkaccellabel.h"
34 #include "gtkaccelmap.h"
35 #include "gtkmain.h"
36 #include "gtksizerequest.h"
37 #include "gtkprivate.h"
38 #include "gtkintl.h"
39
40 #include <gdk/gdkkeysyms.h>
41
42 /**
43  * SECTION:gtkaccellabel
44  * @Short_description: A label which displays an accelerator key on the right of the text
45  * @Title: GtkAccelLabel
46  * @See_also: #GtkItemFactory, #GtkAccelGroup
47  *
48  * The #GtkAccelLabel widget is a subclass of #GtkLabel that also displays an
49  * accelerator key on the right of the label text, e.g. 'Ctl+S'.
50  * It is commonly used in menus to show the keyboard short-cuts for commands.
51  *
52  * The accelerator key to display is not set explicitly.
53  * Instead, the #GtkAccelLabel displays the accelerators which have been added to
54  * a particular widget. This widget is set by calling
55  * gtk_accel_label_set_accel_widget().
56  *
57  * For example, a #GtkMenuItem widget may have an accelerator added to emit the
58  * "activate" signal when the 'Ctl+S' key combination is pressed.
59  * A #GtkAccelLabel is created and added to the #GtkMenuItem, and
60  * gtk_accel_label_set_accel_widget() is called with the #GtkMenuItem as the
61  * second argument. The #GtkAccelLabel will now display 'Ctl+S' after its label.
62  *
63  * Note that creating a #GtkMenuItem with gtk_menu_item_new_with_label() (or
64  * one of the similar functions for #GtkCheckMenuItem and #GtkRadioMenuItem)
65  * automatically adds a #GtkAccelLabel to the #GtkMenuItem and calls
66  * gtk_accel_label_set_accel_widget() to set it up for you.
67  *
68  * A #GtkAccelLabel will only display accelerators which have %GTK_ACCEL_VISIBLE
69  * set (see #GtkAccelFlags).
70  * A #GtkAccelLabel can display multiple accelerators and even signal names,
71  * though it is almost always used to display just one accelerator key.
72  * <example>
73  * <title>Creating a simple menu item with an accelerator key.</title>
74  * <programlisting>
75  *   GtkWidget *save_item;
76  *   GtkAccelGroup *accel_group;
77  *
78  *   /<!---->* Create a GtkAccelGroup and add it to the window. *<!---->/
79  *   accel_group = gtk_accel_group_new (<!-- -->);
80  *   gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
81  *
82  *   /<!---->* Create the menu item using the convenience function. *<!---->/
83  *   save_item = gtk_menu_item_new_with_label ("Save");
84  *   gtk_widget_show (save_item);
85  *   gtk_container_add (GTK_CONTAINER (menu), save_item);
86  *
87  *   /<!---->* Now add the accelerator to the GtkMenuItem. Note that since we called
88  *      gtk_menu_item_new_with_label(<!-- -->) to create the GtkMenuItem the
89  *      GtkAccelLabel is automatically set up to display the GtkMenuItem
90  *      accelerators. We just need to make sure we use GTK_ACCEL_VISIBLE here. *<!---->/
91  *   gtk_widget_add_accelerator (save_item, "activate", accel_group,
92  *                               GDK_s, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
93  * </programlisting>
94  * </example>
95  */
96
97 enum {
98   PROP_0,
99   PROP_ACCEL_CLOSURE,
100   PROP_ACCEL_WIDGET
101 };
102
103 struct _GtkAccelLabelPrivate
104 {
105   guint          accel_padding;      /* should be style property? */
106   GtkWidget     *accel_widget;       /* done*/
107   GClosure      *accel_closure;      /* has set function */
108   GtkAccelGroup *accel_group;        /* set by set_accel_closure() */
109   gchar         *accel_string;       /* has set function */
110   guint16        accel_string_width; /* seems to be private */
111 };
112
113 static void         gtk_accel_label_set_property (GObject            *object,
114                                                   guint               prop_id,
115                                                   const GValue       *value,
116                                                   GParamSpec         *pspec);
117 static void         gtk_accel_label_get_property (GObject            *object,
118                                                   guint               prop_id,
119                                                   GValue             *value,
120                                                   GParamSpec         *pspec);
121 static void         gtk_accel_label_destroy      (GtkObject          *object);
122 static void         gtk_accel_label_finalize     (GObject            *object);
123 static gboolean     gtk_accel_label_draw         (GtkWidget          *widget,
124                                                   cairo_t            *cr);
125 static const gchar *gtk_accel_label_get_string   (GtkAccelLabel      *accel_label);
126
127
128 static void         gtk_accel_label_get_preferred_width (GtkWidget           *widget,
129                                                          gint                *min_width,
130                                                          gint                *nat_width);
131
132 #define GTK_ACCEL_LABEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_ACCEL_LABEL, GtkAccelLabelPrivate))
133
134
135 G_DEFINE_TYPE (GtkAccelLabel, gtk_accel_label, GTK_TYPE_LABEL)
136
137 static void
138 gtk_accel_label_class_init (GtkAccelLabelClass *class)
139 {
140   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
141   GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
142   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
143   
144   gobject_class->finalize = gtk_accel_label_finalize;
145   gobject_class->set_property = gtk_accel_label_set_property;
146   gobject_class->get_property = gtk_accel_label_get_property;
147   
148   object_class->destroy = gtk_accel_label_destroy;
149    
150   widget_class->draw = gtk_accel_label_draw;
151   widget_class->get_preferred_width = gtk_accel_label_get_preferred_width;
152
153   class->signal_quote1 = g_strdup ("<:");
154   class->signal_quote2 = g_strdup (":>");
155
156 #ifndef GDK_WINDOWING_QUARTZ
157   /* This is the text that should appear next to menu accelerators
158    * that use the shift key. If the text on this key isn't typically
159    * translated on keyboards used for your language, don't translate
160    * this.
161    */
162   class->mod_name_shift = g_strdup (C_("keyboard label", "Shift"));
163   /* This is the text that should appear next to menu accelerators
164    * that use the control key. If the text on this key isn't typically
165    * translated on keyboards used for your language, don't translate
166    * this.
167    */
168   class->mod_name_control = g_strdup (C_("keyboard label", "Ctrl"));
169   /* This is the text that should appear next to menu accelerators
170    * that use the alt key. If the text on this key isn't typically
171    * translated on keyboards used for your language, don't translate
172    * this.
173    */
174   class->mod_name_alt = g_strdup (C_("keyboard label", "Alt"));
175   class->mod_separator = g_strdup ("+");
176 #else /* GDK_WINDOWING_QUARTZ */
177
178   /* U+21E7 UPWARDS WHITE ARROW */
179   class->mod_name_shift = g_strdup ("\xe2\x87\xa7");
180   /* U+2303 UP ARROWHEAD */
181   class->mod_name_control = g_strdup ("\xe2\x8c\x83");
182   /* U+2325 OPTION KEY */
183   class->mod_name_alt = g_strdup ("\xe2\x8c\xa5");
184   class->mod_separator = g_strdup ("");
185
186 #endif /* GDK_WINDOWING_QUARTZ */
187
188   g_object_class_install_property (gobject_class,
189                                    PROP_ACCEL_CLOSURE,
190                                    g_param_spec_boxed ("accel-closure",
191                                                        P_("Accelerator Closure"),
192                                                        P_("The closure to be monitored for accelerator changes"),
193                                                        G_TYPE_CLOSURE,
194                                                        GTK_PARAM_READWRITE));
195   g_object_class_install_property (gobject_class,
196                                    PROP_ACCEL_WIDGET,
197                                    g_param_spec_object ("accel-widget",
198                                                         P_("Accelerator Widget"),
199                                                         P_("The widget to be monitored for accelerator changes"),
200                                                         GTK_TYPE_WIDGET,
201                                                         GTK_PARAM_READWRITE));
202
203   g_type_class_add_private (gobject_class, sizeof (GtkAccelLabelPrivate));
204 }
205
206 static void
207 gtk_accel_label_set_property (GObject      *object,
208                               guint         prop_id,
209                               const GValue *value,
210                               GParamSpec   *pspec)
211 {
212   GtkAccelLabel  *accel_label;
213
214   accel_label = GTK_ACCEL_LABEL (object);
215
216   switch (prop_id)
217     {
218     case PROP_ACCEL_CLOSURE:
219       gtk_accel_label_set_accel_closure (accel_label, g_value_get_boxed (value));
220       break;
221     case PROP_ACCEL_WIDGET:
222       gtk_accel_label_set_accel_widget (accel_label, g_value_get_object (value));
223       break;
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226       break;
227     }
228 }
229
230 static void
231 gtk_accel_label_get_property (GObject    *object,
232                               guint       prop_id,
233                               GValue     *value,
234                               GParamSpec *pspec)
235 {
236   GtkAccelLabel  *accel_label;
237
238   accel_label = GTK_ACCEL_LABEL (object);
239
240   switch (prop_id)
241     {
242     case PROP_ACCEL_CLOSURE:
243       g_value_set_boxed (value, accel_label->priv->accel_closure);
244       break;
245     case PROP_ACCEL_WIDGET:
246       g_value_set_object (value, accel_label->priv->accel_widget);
247       break;
248     default:
249       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
250       break;
251     }
252 }
253
254 static void
255 gtk_accel_label_init (GtkAccelLabel *accel_label)
256 {
257   GtkAccelLabelPrivate *priv = GTK_ACCEL_LABEL_GET_PRIVATE (accel_label);
258
259   priv->accel_padding = 3;
260   priv->accel_widget = NULL;
261   priv->accel_closure = NULL;
262   priv->accel_group = NULL;
263   priv->accel_string = NULL;
264
265   accel_label->priv = priv;
266 }
267
268 /**
269  * gtk_accel_label_new:
270  * @string: the label string. Must be non-%NULL.
271  *
272  * Creates a new #GtkAccelLabel.
273  *
274  * Returns: a new #GtkAccelLabel.
275  */
276 GtkWidget*
277 gtk_accel_label_new (const gchar *string)
278 {
279   GtkAccelLabel *accel_label;
280   
281   g_return_val_if_fail (string != NULL, NULL);
282   
283   accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
284   
285   gtk_label_set_text (GTK_LABEL (accel_label), string);
286   
287   return GTK_WIDGET (accel_label);
288 }
289
290 static void
291 gtk_accel_label_destroy (GtkObject *object)
292 {
293   GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (object);
294
295   gtk_accel_label_set_accel_widget (accel_label, NULL);
296   gtk_accel_label_set_accel_closure (accel_label, NULL);
297   
298   GTK_OBJECT_CLASS (gtk_accel_label_parent_class)->destroy (object);
299 }
300
301 static void
302 gtk_accel_label_finalize (GObject *object)
303 {
304   GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (object);
305
306   g_free (accel_label->priv->accel_string);
307
308   G_OBJECT_CLASS (gtk_accel_label_parent_class)->finalize (object);
309 }
310
311 /**
312  * gtk_accel_label_get_accel_widget:
313  * @accel_label: a #GtkAccelLabel
314  *
315  * Fetches the widget monitored by this accelerator label. See
316  * gtk_accel_label_set_accel_widget().
317  *
318  * Returns: (transfer none): the object monitored by the accelerator label, or %NULL.
319  **/
320 GtkWidget*
321 gtk_accel_label_get_accel_widget (GtkAccelLabel *accel_label)
322 {
323   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), NULL);
324
325   return accel_label->priv->accel_widget;
326 }
327
328 /**
329  * gtk_accel_label_get_accel_width:
330  * @accel_label: a #GtkAccelLabel.
331  *
332  * Returns the width needed to display the accelerator key(s).
333  * This is used by menus to align all of the #GtkMenuItem widgets, and shouldn't
334  * be needed by applications.
335  *
336  * Returns: the width needed to display the accelerator key(s).
337  */
338 guint
339 gtk_accel_label_get_accel_width (GtkAccelLabel *accel_label)
340 {
341   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), 0);
342
343   return (accel_label->priv->accel_string_width +
344           (accel_label->priv->accel_string_width ? accel_label->priv->accel_padding : 0));
345 }
346
347 static void
348 gtk_accel_label_get_preferred_width (GtkWidget       *widget,
349                                      gint            *min_width,
350                                      gint            *nat_width)
351 {
352   GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (widget);
353   PangoLayout   *layout;
354   gint           width;
355
356   GTK_WIDGET_CLASS (gtk_accel_label_parent_class)->get_preferred_width (widget, min_width, nat_width);
357
358   layout = gtk_widget_create_pango_layout (GTK_WIDGET (widget), 
359                                            gtk_accel_label_get_string (accel_label));
360   pango_layout_get_pixel_size (layout, &width, NULL);
361   accel_label->priv->accel_string_width = width;
362
363   g_object_unref (layout);
364 }
365
366 static gint
367 get_first_baseline (PangoLayout *layout)
368 {
369   PangoLayoutIter *iter;
370   gint result;
371
372   iter = pango_layout_get_iter (layout);
373   result = pango_layout_iter_get_baseline (iter);
374   pango_layout_iter_free (iter);
375
376   return PANGO_PIXELS (result);
377 }
378
379 static gboolean 
380 gtk_accel_label_draw (GtkWidget *widget,
381                       cairo_t   *cr)
382 {
383   GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (widget);
384   GtkMisc *misc = GTK_MISC (accel_label);
385   GtkTextDirection direction;
386   guint ac_width;
387   GtkAllocation allocation;
388   GtkRequisition requisition;
389
390   direction = gtk_widget_get_direction (widget);
391   ac_width = gtk_accel_label_get_accel_width (accel_label);
392   gtk_widget_get_allocation (widget, &allocation);
393   gtk_widget_get_preferred_size (widget, &requisition, NULL);
394
395   if (allocation.width >= requisition.width + ac_width)
396     {
397       PangoLayout *label_layout;
398       PangoLayout *accel_layout;
399       GtkLabel *label = GTK_LABEL (widget);
400
401       gint x;
402       gint y;
403       gint xpad;
404
405       label_layout = gtk_label_get_layout (GTK_LABEL (accel_label));
406
407       cairo_save (cr);
408
409       /* XXX: Mad hack: We modify the label's width so it renders
410        * properly in its draw function that we chain to. */
411       if (direction == GTK_TEXT_DIR_RTL)
412         cairo_translate (cr, ac_width, 0);
413       if (gtk_label_get_ellipsize (label))
414         pango_layout_set_width (label_layout,
415                                 pango_layout_get_width (label_layout) 
416                                 - ac_width * PANGO_SCALE);
417       
418       allocation.width -= ac_width;
419       gtk_widget_set_allocation (widget, &allocation);
420       if (GTK_WIDGET_CLASS (gtk_accel_label_parent_class)->draw)
421         GTK_WIDGET_CLASS (gtk_accel_label_parent_class)->draw (widget,
422                                                                cr);
423       allocation.width += ac_width;
424       gtk_widget_set_allocation (widget, &allocation);
425       if (gtk_label_get_ellipsize (label))
426         pango_layout_set_width (label_layout,
427                                 pango_layout_get_width (label_layout) 
428                                 + ac_width * PANGO_SCALE);
429
430       cairo_restore (cr);
431
432       gtk_misc_get_padding (misc, &xpad, NULL);
433
434       if (direction == GTK_TEXT_DIR_RTL)
435         x = xpad;
436       else
437         x = gtk_widget_get_allocated_width (widget) - xpad - ac_width;
438
439       gtk_label_get_layout_offsets (GTK_LABEL (accel_label), NULL, &y);
440
441       accel_layout = gtk_widget_create_pango_layout (widget, gtk_accel_label_get_string (accel_label));
442
443       y += get_first_baseline (label_layout) - get_first_baseline (accel_layout) - allocation.y;
444
445       gtk_paint_layout (gtk_widget_get_style (widget),
446                         cr,
447                         gtk_widget_get_state (widget),
448                         FALSE,
449                         widget,
450                         "accellabel",
451                         x, y,
452                         accel_layout);                            
453
454       g_object_unref (accel_layout);
455     }
456   else
457     {
458       if (GTK_WIDGET_CLASS (gtk_accel_label_parent_class)->draw)
459         GTK_WIDGET_CLASS (gtk_accel_label_parent_class)->draw (widget, cr);
460     }
461   
462   return FALSE;
463 }
464
465 static void
466 refetch_widget_accel_closure (GtkAccelLabel *accel_label)
467 {
468   GClosure *closure = NULL;
469   GList *clist, *list;
470   
471   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
472   g_return_if_fail (GTK_IS_WIDGET (accel_label->priv->accel_widget));
473   
474   clist = gtk_widget_list_accel_closures (accel_label->priv->accel_widget);
475   for (list = clist; list; list = list->next)
476     {
477       /* we just take the first closure used */
478       closure = list->data;
479       break;
480     }
481   g_list_free (clist);
482   gtk_accel_label_set_accel_closure (accel_label, closure);
483 }
484
485 /**
486  * gtk_accel_label_set_accel_widget:
487  * @accel_label: a #GtkAccelLabel
488  * @accel_widget: the widget to be monitored.
489  *
490  * Sets the widget to be monitored by this accelerator label. 
491  **/
492 void
493 gtk_accel_label_set_accel_widget (GtkAccelLabel *accel_label,
494                                   GtkWidget     *accel_widget)
495 {
496   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
497   if (accel_widget)
498     g_return_if_fail (GTK_IS_WIDGET (accel_widget));
499     
500   if (accel_widget != accel_label->priv->accel_widget)
501     {
502       if (accel_label->priv->accel_widget)
503         {
504           gtk_accel_label_set_accel_closure (accel_label, NULL);
505           g_signal_handlers_disconnect_by_func (accel_label->priv->accel_widget,
506                                                 refetch_widget_accel_closure,
507                                                 accel_label);
508           g_object_unref (accel_label->priv->accel_widget);
509         }
510       accel_label->priv->accel_widget = accel_widget;
511       if (accel_label->priv->accel_widget)
512         {
513           g_object_ref (accel_label->priv->accel_widget);
514           g_signal_connect_object (accel_label->priv->accel_widget, "accel-closures-changed",
515                                    G_CALLBACK (refetch_widget_accel_closure),
516                                    accel_label, G_CONNECT_SWAPPED);
517           refetch_widget_accel_closure (accel_label);
518         }
519       g_object_notify (G_OBJECT (accel_label), "accel-widget");
520     }
521 }
522
523 static void
524 gtk_accel_label_reset (GtkAccelLabel *accel_label)
525 {
526   if (accel_label->priv->accel_string)
527     {
528       g_free (accel_label->priv->accel_string);
529       accel_label->priv->accel_string = NULL;
530     }
531   
532   gtk_widget_queue_resize (GTK_WIDGET (accel_label));
533 }
534
535 static void
536 check_accel_changed (GtkAccelGroup  *accel_group,
537                      guint           keyval,
538                      GdkModifierType modifier,
539                      GClosure       *accel_closure,
540                      GtkAccelLabel  *accel_label)
541 {
542   if (accel_closure == accel_label->priv->accel_closure)
543     gtk_accel_label_reset (accel_label);
544 }
545
546 /**
547  * gtk_accel_label_set_accel_closure:
548  * @accel_label: a #GtkAccelLabel
549  * @accel_closure: the closure to monitor for accelerator changes.
550  *
551  * Sets the closure to be monitored by this accelerator label. The closure
552  * must be connected to an accelerator group; see gtk_accel_group_connect().
553  **/
554 void
555 gtk_accel_label_set_accel_closure (GtkAccelLabel *accel_label,
556                                    GClosure      *accel_closure)
557 {
558   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
559   if (accel_closure)
560     g_return_if_fail (gtk_accel_group_from_accel_closure (accel_closure) != NULL);
561
562   if (accel_closure != accel_label->priv->accel_closure)
563     {
564       if (accel_label->priv->accel_closure)
565         {
566           g_signal_handlers_disconnect_by_func (accel_label->priv->accel_group,
567                                                 check_accel_changed,
568                                                 accel_label);
569           accel_label->priv->accel_group = NULL;
570           g_closure_unref (accel_label->priv->accel_closure);
571         }
572       accel_label->priv->accel_closure = accel_closure;
573       if (accel_label->priv->accel_closure)
574         {
575           g_closure_ref (accel_label->priv->accel_closure);
576           accel_label->priv->accel_group = gtk_accel_group_from_accel_closure (accel_closure);
577           g_signal_connect_object (accel_label->priv->accel_group, "accel-changed",
578                                    G_CALLBACK (check_accel_changed),
579                                    accel_label, 0);
580         }
581       gtk_accel_label_reset (accel_label);
582       g_object_notify (G_OBJECT (accel_label), "accel-closure");
583     }
584 }
585
586 static gboolean
587 find_accel (GtkAccelKey *key,
588             GClosure    *closure,
589             gpointer     data)
590 {
591   return data == (gpointer) closure;
592 }
593
594 static const gchar *
595 gtk_accel_label_get_string (GtkAccelLabel *accel_label)
596 {
597   if (!accel_label->priv->accel_string)
598     gtk_accel_label_refetch (accel_label);
599   
600   return accel_label->priv->accel_string;
601 }
602
603 /* Underscores in key names are better displayed as spaces
604  * E.g., Page_Up should be "Page Up"
605  */
606 static void
607 substitute_underscores (char *str)
608 {
609   char *p;
610
611   for (p = str; *p; p++)
612     if (*p == '_')
613       *p = ' ';
614 }
615
616 /* On Mac, if the key has symbolic representation (e.g. arrow keys),
617  * append it to gstring and return TRUE; otherwise return FALSE.
618  * See http://docs.info.apple.com/article.html?path=Mac/10.5/en/cdb_symbs.html 
619  * for the list of special keys. */
620 static gboolean
621 append_keyval_symbol (guint    accelerator_key,
622                       GString *gstring)
623 {
624 #ifdef GDK_WINDOWING_QUARTZ
625   switch (accelerator_key)
626   {
627   case GDK_KEY_Return:
628     /* U+21A9 LEFTWARDS ARROW WITH HOOK */
629     g_string_append (gstring, "\xe2\x86\xa9");
630     return TRUE;
631
632   case GDK_KEY_ISO_Enter:
633     /* U+2324 UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS */
634     g_string_append (gstring, "\xe2\x8c\xa4");
635     return TRUE;
636
637   case GDK_KEY_Left:
638     /* U+2190 LEFTWARDS ARROW */
639     g_string_append (gstring, "\xe2\x86\x90");
640     return TRUE;
641
642   case GDK_KEY_Up:
643     /* U+2191 UPWARDS ARROW */
644     g_string_append (gstring, "\xe2\x86\x91");
645     return TRUE;
646
647   case GDK_KEY_Right:
648     /* U+2192 RIGHTWARDS ARROW */
649     g_string_append (gstring, "\xe2\x86\x92");
650     return TRUE;
651
652   case GDK_KEY_Down:
653     /* U+2193 DOWNWARDS ARROW */
654     g_string_append (gstring, "\xe2\x86\x93");
655     return TRUE;
656
657   case GDK_KEY_Page_Up:
658     /* U+21DE UPWARDS ARROW WITH DOUBLE STROKE */
659     g_string_append (gstring, "\xe2\x87\x9e");
660     return TRUE;
661
662   case GDK_KEY_Page_Down:
663     /* U+21DF DOWNWARDS ARROW WITH DOUBLE STROKE */
664     g_string_append (gstring, "\xe2\x87\x9f");
665     return TRUE;
666
667   case GDK_KEY_Home:
668     /* U+2196 NORTH WEST ARROW */
669     g_string_append (gstring, "\xe2\x86\x96");
670     return TRUE;
671
672   case GDK_KEY_End:
673     /* U+2198 SOUTH EAST ARROW */
674     g_string_append (gstring, "\xe2\x86\x98");
675     return TRUE;
676
677   case GDK_KEY_Escape:
678     /* U+238B BROKEN CIRCLE WITH NORTHWEST ARROW */
679     g_string_append (gstring, "\xe2\x8e\x8b");
680     return TRUE;
681
682   case GDK_KEY_BackSpace:
683     /* U+232B ERASE TO THE LEFT */
684     g_string_append (gstring, "\xe2\x8c\xab");
685     return TRUE;
686
687   case GDK_KEY_Delete:
688     /* U+2326 ERASE TO THE RIGHT */
689     g_string_append (gstring, "\xe2\x8c\xa6");
690     return TRUE;
691
692   default:
693     return FALSE;
694   }
695 #else /* !GDK_WINDOWING_QUARTZ */
696   return FALSE;
697 #endif
698 }
699
700 gchar *
701 _gtk_accel_label_class_get_accelerator_label (GtkAccelLabelClass *klass,
702                                               guint               accelerator_key,
703                                               GdkModifierType     accelerator_mods)
704 {
705   GString *gstring;
706   gboolean seen_mod = FALSE;
707   gunichar ch;
708   
709   gstring = g_string_new ("");
710   
711   if (accelerator_mods & GDK_SHIFT_MASK)
712     {
713       g_string_append (gstring, klass->mod_name_shift);
714       seen_mod = TRUE;
715     }
716   if (accelerator_mods & GDK_CONTROL_MASK)
717     {
718       if (seen_mod)
719         g_string_append (gstring, klass->mod_separator);
720       g_string_append (gstring, klass->mod_name_control);
721       seen_mod = TRUE;
722     }
723   if (accelerator_mods & GDK_MOD1_MASK)
724     {
725       if (seen_mod)
726         g_string_append (gstring, klass->mod_separator);
727       g_string_append (gstring, klass->mod_name_alt);
728       seen_mod = TRUE;
729     }
730   if (accelerator_mods & GDK_MOD2_MASK)
731     {
732       if (seen_mod)
733         g_string_append (gstring, klass->mod_separator);
734
735       g_string_append (gstring, "Mod2");
736       seen_mod = TRUE;
737     }
738   if (accelerator_mods & GDK_MOD3_MASK)
739     {
740       if (seen_mod)
741         g_string_append (gstring, klass->mod_separator);
742
743       g_string_append (gstring, "Mod3");
744       seen_mod = TRUE;
745     }
746   if (accelerator_mods & GDK_MOD4_MASK)
747     {
748       if (seen_mod)
749         g_string_append (gstring, klass->mod_separator);
750
751       g_string_append (gstring, "Mod4");
752       seen_mod = TRUE;
753     }
754   if (accelerator_mods & GDK_MOD5_MASK)
755     {
756       if (seen_mod)
757         g_string_append (gstring, klass->mod_separator);
758
759       g_string_append (gstring, "Mod5");
760       seen_mod = TRUE;
761     }
762   if (accelerator_mods & GDK_SUPER_MASK)
763     {
764       if (seen_mod)
765         g_string_append (gstring, klass->mod_separator);
766
767       /* This is the text that should appear next to menu accelerators
768        * that use the super key. If the text on this key isn't typically
769        * translated on keyboards used for your language, don't translate
770        * this.
771        */
772       g_string_append (gstring, C_("keyboard label", "Super"));
773       seen_mod = TRUE;
774     }
775   if (accelerator_mods & GDK_HYPER_MASK)
776     {
777       if (seen_mod)
778         g_string_append (gstring, klass->mod_separator);
779
780       /* This is the text that should appear next to menu accelerators
781        * that use the hyper key. If the text on this key isn't typically
782        * translated on keyboards used for your language, don't translate
783        * this.
784        */
785       g_string_append (gstring, C_("keyboard label", "Hyper"));
786       seen_mod = TRUE;
787     }
788   if (accelerator_mods & GDK_META_MASK)
789     {
790       if (seen_mod)
791         g_string_append (gstring, klass->mod_separator);
792
793 #ifndef GDK_WINDOWING_QUARTZ
794       /* This is the text that should appear next to menu accelerators
795        * that use the meta key. If the text on this key isn't typically
796        * translated on keyboards used for your language, don't translate
797        * this.
798        */
799       g_string_append (gstring, C_("keyboard label", "Meta"));
800 #else
801       /* Command key symbol U+2318 PLACE OF INTEREST SIGN */
802       g_string_append (gstring, "\xe2\x8c\x98");
803 #endif
804       seen_mod = TRUE;
805     }
806   if (seen_mod)
807     g_string_append (gstring, klass->mod_separator);
808   
809   ch = gdk_keyval_to_unicode (accelerator_key);
810   if (ch && ch < 0x80 && (g_unichar_isgraph (ch) || ch == ' '))
811     {
812       switch (ch)
813         {
814         case ' ':
815           g_string_append (gstring, C_("keyboard label", "Space"));
816           break;
817         case '\\':
818           g_string_append (gstring, C_("keyboard label", "Backslash"));
819           break;
820         default:
821           g_string_append_unichar (gstring, g_unichar_toupper (ch));
822           break;
823         }
824     }
825   else if (!append_keyval_symbol (accelerator_key, gstring))
826     {
827       gchar *tmp;
828
829       tmp = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
830       if (tmp != NULL)
831         {
832           if (tmp[0] != 0 && tmp[1] == 0)
833             g_string_append_c (gstring, g_ascii_toupper (tmp[0]));
834           else
835             {
836               const gchar *str;
837               str = g_dpgettext2 (GETTEXT_PACKAGE, "keyboard label", tmp);
838               if (str == tmp)
839                 {
840                   g_string_append (gstring, tmp);
841                   substitute_underscores (gstring->str);
842                 }
843               else
844                 g_string_append (gstring, str);
845             }
846         }
847     }
848
849   return g_string_free (gstring, FALSE);
850 }
851
852 /**
853  * gtk_accel_label_refetch:
854  * @accel_label: a #GtkAccelLabel.
855  *
856  * Recreates the string representing the accelerator keys.
857  * This should not be needed since the string is automatically updated whenever
858  * accelerators are added or removed from the associated widget.
859  *
860  * Returns: always returns %FALSE.
861  */
862 gboolean
863 gtk_accel_label_refetch (GtkAccelLabel *accel_label)
864 {
865   gboolean enable_accels;
866
867   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), FALSE);
868
869   if (accel_label->priv->accel_string)
870     {
871       g_free (accel_label->priv->accel_string);
872       accel_label->priv->accel_string = NULL;
873     }
874
875   g_object_get (gtk_widget_get_settings (GTK_WIDGET (accel_label)),
876                 "gtk-enable-accels", &enable_accels,
877                 NULL);
878
879   if (enable_accels && accel_label->priv->accel_closure)
880     {
881       GtkAccelKey *key = gtk_accel_group_find (accel_label->priv->accel_group, find_accel, accel_label->priv->accel_closure);
882
883       if (key && key->accel_flags & GTK_ACCEL_VISIBLE)
884         {
885           GtkAccelLabelClass *klass;
886           gchar *tmp;
887
888           klass = GTK_ACCEL_LABEL_GET_CLASS (accel_label);
889           tmp = _gtk_accel_label_class_get_accelerator_label (klass,
890                                                               key->accel_key,
891                                                               key->accel_mods);
892           accel_label->priv->accel_string = g_strconcat ("   ", tmp, NULL);
893           g_free (tmp);
894         }
895       if (!accel_label->priv->accel_string)
896         accel_label->priv->accel_string = g_strdup ("-/-");
897     }
898   
899   if (!accel_label->priv->accel_string)
900     accel_label->priv->accel_string = g_strdup ("");
901
902   gtk_widget_queue_resize (GTK_WIDGET (accel_label));
903
904   return FALSE;
905 }