]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccellabel.c
Patch from Erwann Chenede, #73900 fixing a lot of warnings with Forte CC,
[~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 #include "gtkaccellabel.h"
30 #include "gtkaccelmap.h"
31 #include "gtkmain.h"
32 #include "gtksignal.h"
33 #include "gtkintl.h"
34
35 #include <string.h>
36
37 enum {
38   PROP_0,
39   PROP_ACCEL_CLOSURE,
40   PROP_ACCEL_WIDGET
41 };
42
43 static void     gtk_accel_label_class_init   (GtkAccelLabelClass *klass);
44 static void     gtk_accel_label_init         (GtkAccelLabel      *accel_label);
45 static void     gtk_accel_label_set_property (GObject            *object,
46                                               guint               prop_id,
47                                               const GValue       *value,
48                                               GParamSpec         *pspec);
49 static void     gtk_accel_label_get_property (GObject            *object,
50                                               guint               prop_id,
51                                               GValue             *value,
52                                               GParamSpec         *pspec);
53 static void     gtk_accel_label_destroy      (GtkObject          *object);
54 static void     gtk_accel_label_finalize     (GObject            *object);
55 static void     gtk_accel_label_size_request (GtkWidget          *widget,
56                                               GtkRequisition     *requisition);
57 static gboolean gtk_accel_label_expose_event (GtkWidget          *widget,
58                                               GdkEventExpose     *event);
59 static gboolean gtk_accel_label_refetch_idle (GtkAccelLabel      *accel_label);
60
61 static GtkAccelLabelClass *accel_label_class = NULL;
62 static GtkLabelClass *parent_class = NULL;
63
64
65 GtkType
66 gtk_accel_label_get_type (void)
67 {
68   static GtkType accel_label_type = 0;
69   
70   if (!accel_label_type)
71     {
72       static const GtkTypeInfo accel_label_info =
73       {
74         "GtkAccelLabel",
75         sizeof (GtkAccelLabel),
76         sizeof (GtkAccelLabelClass),
77         (GtkClassInitFunc) gtk_accel_label_class_init,
78         (GtkObjectInitFunc) gtk_accel_label_init,
79         /* reserved_1 */ NULL,
80         /* reserved_2 */ NULL,
81         (GtkClassInitFunc) NULL,
82       };
83       
84       accel_label_type = gtk_type_unique (GTK_TYPE_LABEL, &accel_label_info);
85     }
86   
87   return accel_label_type;
88 }
89
90 static void
91 gtk_accel_label_class_init (GtkAccelLabelClass *class)
92 {
93   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
94   GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
95   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
96   
97   accel_label_class = class;
98   parent_class = g_type_class_peek_parent (class);
99   
100   gobject_class->finalize = gtk_accel_label_finalize;
101   gobject_class->set_property = gtk_accel_label_set_property;
102   gobject_class->get_property = gtk_accel_label_get_property;
103   
104   object_class->destroy = gtk_accel_label_destroy;
105    
106   widget_class->size_request = gtk_accel_label_size_request;
107   widget_class->expose_event = gtk_accel_label_expose_event;
108
109   class->signal_quote1 = g_strdup ("<:");
110   class->signal_quote2 = g_strdup (":>");
111   /* This is the text that should appear next to menu accelerators
112    * that use the shift key. If the text on this key isn't typically
113    * translated on keyboards used for your language, don't translate
114    * this.
115    */
116   class->mod_name_shift = g_strdup (_("Shift"));
117   /* This is the text that should appear next to menu accelerators
118    * that use the control key. If the text on this key isn't typically
119    * translated on keyboards used for your language, don't translate
120    * this.
121    */
122   class->mod_name_control = g_strdup (_("Ctrl"));
123   /* This is the text that should appear next to menu accelerators
124    * that use the alt key. If the text on this key isn't typically
125    * translated on keyboards used for your language, don't translate
126    * this.
127    */
128   class->mod_name_alt = g_strdup (_("Alt"));
129   class->mod_separator = g_strdup ("+");
130   class->accel_seperator = g_strdup (" / ");
131   class->latin1_to_char = TRUE;
132   
133   g_object_class_install_property (G_OBJECT_CLASS (object_class),
134                                    PROP_ACCEL_CLOSURE,
135                                    g_param_spec_boxed ("accel_closure",
136                                                        _("Accelerator Closure"),
137                                                        _("The closure to be monitored for accelerator changes"),
138                                                        G_TYPE_CLOSURE,
139                                                        G_PARAM_READABLE | G_PARAM_WRITABLE));
140   g_object_class_install_property (G_OBJECT_CLASS (object_class),
141                                    PROP_ACCEL_WIDGET,
142                                    g_param_spec_object ("accel_widget",
143                                                         _("Accelerator Widget"),
144                                                         _("The widget to be monitored for accelerator changes"),
145                                                         GTK_TYPE_WIDGET,
146                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
147 }
148
149 static void
150 gtk_accel_label_set_property (GObject      *object,
151                               guint         prop_id,
152                               const GValue *value,
153                               GParamSpec   *pspec)
154 {
155   GtkAccelLabel  *accel_label;
156
157   accel_label = GTK_ACCEL_LABEL (object);
158
159   switch (prop_id)
160     {
161     case PROP_ACCEL_CLOSURE:
162       gtk_accel_label_set_accel_closure (accel_label, g_value_get_boxed (value));
163       break;
164     case PROP_ACCEL_WIDGET:
165       gtk_accel_label_set_accel_widget (accel_label, g_value_get_object (value));
166       break;
167     default:
168       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169       break;
170     }
171 }
172
173 static void
174 gtk_accel_label_get_property (GObject    *object,
175                               guint       prop_id,
176                               GValue     *value,
177                               GParamSpec *pspec)
178 {
179   GtkAccelLabel  *accel_label;
180
181   accel_label = GTK_ACCEL_LABEL (object);
182
183   switch (prop_id)
184     {
185     case PROP_ACCEL_CLOSURE:
186       g_value_set_boxed (value, accel_label->accel_closure);
187       break;
188     case PROP_ACCEL_WIDGET:
189       g_value_set_object (value, accel_label->accel_widget);
190       break;
191     default:
192       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
193       break;
194     }
195 }
196
197 static void
198 gtk_accel_label_init (GtkAccelLabel *accel_label)
199 {
200   accel_label->queue_id = 0;
201   accel_label->accel_padding = 3;
202   accel_label->accel_widget = NULL;
203   accel_label->accel_closure = NULL;
204   accel_label->accel_group = NULL;
205   accel_label->accel_string = NULL;
206   
207   gtk_accel_label_refetch (accel_label);
208 }
209
210 GtkWidget*
211 gtk_accel_label_new (const gchar *string)
212 {
213   GtkAccelLabel *accel_label;
214   
215   g_return_val_if_fail (string != NULL, NULL);
216   
217   accel_label = gtk_type_new (GTK_TYPE_ACCEL_LABEL);
218   
219   gtk_label_set_text (GTK_LABEL (accel_label), string);
220   
221   return GTK_WIDGET (accel_label);
222 }
223
224 static void
225 gtk_accel_label_destroy (GtkObject *object)
226 {
227   GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (object);
228
229   gtk_accel_label_set_accel_widget (accel_label, NULL);
230   gtk_accel_label_set_accel_closure (accel_label, NULL);
231   
232   GTK_OBJECT_CLASS (parent_class)->destroy (object);
233 }
234
235 static void
236 gtk_accel_label_finalize (GObject *object)
237 {
238   GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (object);
239
240   if (accel_label->queue_id)
241     {
242       gtk_idle_remove (accel_label->queue_id);
243       accel_label->queue_id = 0;
244     }
245   g_free (accel_label->accel_string);
246   
247   G_OBJECT_CLASS (parent_class)->finalize (object);
248 }
249
250 /**
251  * gtk_accel_label_get_accel_widget:
252  * @accel_label: a #GtkAccelLabel
253  *
254  * Fetches the widget monitored by this accelerator label. See
255  * gtk_accel_label_set_accel_widget().
256  *
257  * Return value: the object monitored by the accelerator label,
258  *               or %NULL.
259  **/
260 GtkWidget*
261 gtk_accel_label_get_accel_widget (GtkAccelLabel *accel_label)
262 {
263   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), NULL);
264
265   return accel_label->accel_widget;
266 }
267
268 guint
269 gtk_accel_label_get_accel_width (GtkAccelLabel *accel_label)
270 {
271   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), 0);
272   
273   return (accel_label->accel_string_width +
274           (accel_label->accel_string_width ? accel_label->accel_padding : 0));
275 }
276
277 static void
278 gtk_accel_label_size_request (GtkWidget      *widget,
279                               GtkRequisition *requisition)
280 {
281   GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (widget);
282   PangoLayout *layout;
283   gint width;
284   
285   if (GTK_WIDGET_CLASS (parent_class)->size_request)
286     GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
287
288   layout = gtk_widget_create_pango_layout (widget, accel_label->accel_string);
289   pango_layout_get_pixel_size (layout, &width, NULL);
290   accel_label->accel_string_width = width;
291   
292   g_object_unref (G_OBJECT (layout));
293 }
294
295 static gboolean 
296 gtk_accel_label_expose_event (GtkWidget      *widget,
297                               GdkEventExpose *event)
298 {
299   GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (widget);
300   GtkMisc *misc = GTK_MISC (accel_label);
301   PangoLayout *layout;
302           
303   if (GTK_WIDGET_DRAWABLE (accel_label))
304     {
305       guint ac_width;
306       
307       ac_width = gtk_accel_label_get_accel_width (accel_label);
308       
309       if (widget->allocation.width >= widget->requisition.width + ac_width)
310         {
311           guint x;
312           guint y;
313           
314           widget->allocation.width -= ac_width;
315           if (GTK_WIDGET_CLASS (parent_class)->expose_event)
316             GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
317           widget->allocation.width += ac_width;
318           
319           x = widget->allocation.x + widget->allocation.width - misc->xpad - ac_width;
320           
321           y = (widget->allocation.y * (1.0 - misc->yalign) +
322                (widget->allocation.y + widget->allocation.height -
323                 (widget->requisition.height - misc->ypad * 2)) *
324                misc->yalign) + 1.5;
325           
326           layout = gtk_widget_create_pango_layout (widget, accel_label->accel_string);
327
328           gtk_paint_layout (widget->style,
329                             widget->window,
330                             GTK_WIDGET_STATE (widget),
331                             FALSE,
332                             &event->area,
333                             widget,
334                             "accellabel",
335                             x, y,
336                             layout);                            
337
338           g_object_unref (G_OBJECT (layout));
339         }
340       else
341         {
342           if (GTK_WIDGET_CLASS (parent_class)->expose_event)
343             GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
344         }
345     }
346   
347   return FALSE;
348 }
349
350 static void
351 refetch_widget_accel_closure (GtkAccelLabel *accel_label)
352 {
353   GClosure *closure = NULL;
354   GList *clist, *list;
355   
356   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
357   g_return_if_fail (GTK_IS_WIDGET (accel_label->accel_widget));
358   
359   clist = gtk_widget_list_accel_closures (accel_label->accel_widget);
360   for (list = clist; list; list = list->next)
361     {
362       /* we just take the first closure used */
363       closure = list->data;
364       break;
365     }
366   g_list_free (clist);
367   gtk_accel_label_set_accel_closure (accel_label, closure);
368 }
369
370 /**
371  * gtk_accel_label_set_accel_widget:
372  * @accel_label: a #GtkAccelLabel
373  * @accel_widget: the widget to be monitored.
374  *
375  * Sets the widget to be monitored by this accelerator label. 
376  **/
377 void
378 gtk_accel_label_set_accel_widget (GtkAccelLabel *accel_label,
379                                   GtkWidget     *accel_widget)
380 {
381   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
382   if (accel_widget)
383     g_return_if_fail (GTK_IS_WIDGET (accel_widget));
384     
385   if (accel_widget != accel_label->accel_widget)
386     {
387       if (accel_label->accel_widget)
388         {
389           gtk_accel_label_set_accel_closure (accel_label, NULL);
390           g_signal_handlers_disconnect_by_func (accel_label->accel_widget,
391                                                 (gpointer) refetch_widget_accel_closure,
392                                                 accel_label);
393           g_object_unref (accel_label->accel_widget);
394         }
395       accel_label->accel_widget = accel_widget;
396       if (accel_label->accel_widget)
397         {
398           g_object_ref (accel_label->accel_widget);
399           g_signal_connect_object (accel_label->accel_widget, "accel_closures_changed",
400                                    G_CALLBACK (refetch_widget_accel_closure),
401                                    accel_label, G_CONNECT_SWAPPED);
402           refetch_widget_accel_closure (accel_label);
403         }
404       g_object_notify (G_OBJECT (accel_label), "accel_widget");
405     }
406 }
407
408 static void
409 gtk_accel_label_queue_refetch (GtkAccelLabel *accel_label)
410 {
411   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
412
413   if (accel_label->queue_id == 0)
414     accel_label->queue_id = gtk_idle_add_priority (G_PRIORITY_HIGH_IDLE,
415                                                    (GtkFunction) gtk_accel_label_refetch_idle,
416                                                    accel_label);
417 }
418
419 static void
420 check_accel_changed (GtkAccelGroup  *accel_group,
421                      guint           keyval,
422                      GdkModifierType modifier,
423                      GClosure       *accel_closure,
424                      GtkAccelLabel  *accel_label)
425 {
426   if (accel_closure == accel_label->accel_closure)
427     gtk_accel_label_queue_refetch (accel_label);
428 }
429
430 /**
431  * gtk_accel_label_set_accel_closure:
432  * @accel_label: a #GtkAccelLabel
433  * @accel_closure: the closure to monitor for accelerator changes.
434  *
435  * Sets the closure to be monitored by this accelerator label. The closure
436  * must be connected to an accelerator group; see gtk_accel_group_connect().
437  **/
438 void
439 gtk_accel_label_set_accel_closure (GtkAccelLabel *accel_label,
440                                    GClosure      *accel_closure)
441 {
442   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
443   if (accel_closure)
444     g_return_if_fail (gtk_accel_group_from_accel_closure (accel_closure) != NULL);
445
446   if (accel_closure != accel_label->accel_closure)
447     {
448       if (accel_label->accel_closure)
449         {
450           g_signal_handlers_disconnect_by_func (accel_label->accel_group,
451                                                 (gpointer) check_accel_changed,
452                                                 accel_label);
453           accel_label->accel_group = NULL;
454           g_closure_unref (accel_label->accel_closure);
455         }
456       accel_label->accel_closure = accel_closure;
457       if (accel_label->accel_closure)
458         {
459           g_closure_ref (accel_label->accel_closure);
460           accel_label->accel_group = gtk_accel_group_from_accel_closure (accel_closure);
461           g_signal_connect_object (accel_label->accel_group, "accel_changed",
462                                    G_CALLBACK (check_accel_changed),
463                                    accel_label, 0);
464         }
465       gtk_accel_label_queue_refetch (accel_label);
466       g_object_notify (G_OBJECT (accel_label), "accel_closure");
467     }
468 }
469
470 static gboolean
471 gtk_accel_label_refetch_idle (GtkAccelLabel *accel_label)
472 {
473   gboolean retval;
474
475   GDK_THREADS_ENTER ();
476   retval = gtk_accel_label_refetch (accel_label);
477   GDK_THREADS_LEAVE ();
478
479   return retval;
480 }
481
482 static gboolean
483 find_accel (GtkAccelKey *key,
484             GClosure    *closure,
485             gpointer     data)
486 {
487   return data == (gpointer) closure;
488 }
489
490 gboolean
491 gtk_accel_label_refetch (GtkAccelLabel *accel_label)
492 {
493   GtkAccelLabelClass *class;
494
495   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), FALSE);
496
497   class = GTK_ACCEL_LABEL_GET_CLASS (accel_label);
498
499   g_free (accel_label->accel_string);
500   accel_label->accel_string = NULL;
501
502   if (accel_label->accel_closure)
503     {
504       GtkAccelKey *key = gtk_accel_group_find (accel_label->accel_group, find_accel, accel_label->accel_closure);
505
506       if (key && key->accel_flags & GTK_ACCEL_VISIBLE)
507         {
508           GString *gstring;
509           gboolean seen_mod = FALSE;
510           gunichar ch;
511           
512           gstring = g_string_new (accel_label->accel_string);
513           g_string_append (gstring, gstring->len ? class->accel_seperator : "   ");
514           
515           if (key->accel_mods & GDK_SHIFT_MASK)
516             {
517               g_string_append (gstring, class->mod_name_shift);
518               seen_mod = TRUE;
519             }
520           if (key->accel_mods & GDK_CONTROL_MASK)
521             {
522               if (seen_mod)
523                 g_string_append (gstring, class->mod_separator);
524               g_string_append (gstring, class->mod_name_control);
525               seen_mod = TRUE;
526             }
527           if (key->accel_mods & GDK_MOD1_MASK)
528             {
529               if (seen_mod)
530                 g_string_append (gstring, class->mod_separator);
531               g_string_append (gstring, class->mod_name_alt);
532               seen_mod = TRUE;
533             }
534           if (seen_mod)
535             g_string_append (gstring, class->mod_separator);
536
537           ch = gdk_keyval_to_unicode (key->accel_key);
538           if (ch && (g_unichar_isgraph (ch) || ch == ' ') &&
539               (ch < 0x80 || class->latin1_to_char))
540             {
541               switch (ch)
542                 {
543                 case ' ':
544                   g_string_append (gstring, "Space");
545                   break;
546                 case '\\':
547                   g_string_append (gstring, "Backslash");
548                   break;
549                 default:
550                   g_string_append_unichar (gstring, g_unichar_toupper (ch));
551                   break;
552                 }
553             }
554           else
555             {
556               gchar *tmp;
557               
558               tmp = gtk_accelerator_name (key->accel_key, 0);
559               if (tmp[0] != 0 && tmp[1] == 0)
560                 tmp[0] = g_ascii_toupper (tmp[0]);
561               g_string_append (gstring, tmp);
562               g_free (tmp);
563             }
564           g_free (accel_label->accel_string);
565           accel_label->accel_string = gstring->str;
566           g_string_free (gstring, FALSE);
567         }
568       if (!accel_label->accel_string)
569         accel_label->accel_string = g_strdup ("-/-");
570     }
571   
572   if (!accel_label->accel_string)
573     accel_label->accel_string = g_strdup ("");
574
575   if (accel_label->queue_id)
576     {
577       gtk_idle_remove (accel_label->queue_id);
578       accel_label->queue_id = 0;
579     }
580
581   gtk_widget_queue_resize (GTK_WIDGET (accel_label));
582
583   return FALSE;
584 }