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