]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccellabel.c
don't add extra width or height if the requested size is mandatory (i.e.
[~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 Library 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  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library 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-1999.  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 <string.h>
31 #include <ctype.h>
32 #include "gtkmain.h"
33 #include "gtksignal.h"
34 #include "gtkaccellabel.h"
35
36
37 enum {
38   ARG_0,
39   ARG_ACCEL_WIDGET
40 };
41
42 static void gtk_accel_label_class_init   (GtkAccelLabelClass  *klass);
43 static void gtk_accel_label_init         (GtkAccelLabel       *accel_label);
44 static void gtk_accel_label_set_arg      (GtkObject      *object,
45                                           GtkArg         *arg,
46                                           guint           arg_id);
47 static void gtk_accel_label_get_arg      (GtkObject      *object,
48                                           GtkArg         *arg,
49                                           guint           arg_id);
50 static void gtk_accel_label_destroy      (GtkObject      *object);
51 static void gtk_accel_label_finalize     (GtkObject      *object);
52 static void gtk_accel_label_size_request (GtkWidget      *widget,
53                                           GtkRequisition *requisition);
54 static gint gtk_accel_label_expose_event (GtkWidget      *widget,
55                                           GdkEventExpose *event);
56 static gboolean gtk_accel_label_refetch_idle (GtkAccelLabel *accel_label);
57
58 static GtkAccelLabelClass *accel_label_class = NULL;
59 static GtkLabelClass *parent_class = NULL;
60
61
62 GtkType
63 gtk_accel_label_get_type (void)
64 {
65   static GtkType accel_label_type = 0;
66   
67   if (!accel_label_type)
68     {
69       static const GtkTypeInfo accel_label_info =
70       {
71         "GtkAccelLabel",
72         sizeof (GtkAccelLabel),
73         sizeof (GtkAccelLabelClass),
74         (GtkClassInitFunc) gtk_accel_label_class_init,
75         (GtkObjectInitFunc) gtk_accel_label_init,
76         /* reserved_1 */ NULL,
77         /* reserved_2 */ NULL,
78         (GtkClassInitFunc) NULL,
79       };
80       
81       accel_label_type = gtk_type_unique (gtk_label_get_type (), &accel_label_info);
82     }
83   
84   return accel_label_type;
85 }
86
87 static void
88 gtk_accel_label_class_init (GtkAccelLabelClass *class)
89 {
90   GtkObjectClass *object_class;
91   GtkWidgetClass *widget_class;
92   GtkMiscClass *misc_class;
93   GtkLabelClass *label_class;
94   
95   accel_label_class = class;
96   object_class = (GtkObjectClass*) class;
97   widget_class = (GtkWidgetClass*) class;
98   misc_class = (GtkMiscClass*) class;
99   label_class = (GtkLabelClass*) class;
100   
101   parent_class = gtk_type_class (gtk_label_get_type ());
102   
103   gtk_object_add_arg_type ("GtkAccelLabel::accel_widget", GTK_TYPE_WIDGET, GTK_ARG_READWRITE, ARG_ACCEL_WIDGET);
104
105   object_class->set_arg = gtk_accel_label_set_arg;
106   object_class->get_arg = gtk_accel_label_get_arg;
107   object_class->destroy = gtk_accel_label_destroy;
108   object_class->finalize = gtk_accel_label_finalize;
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   class->mod_name_shift = g_strdup ("Shft");
116   class->mod_name_control = g_strdup ("Ctl");
117   class->mod_name_alt = g_strdup ("Alt");
118   class->mod_separator = g_strdup ("+");
119   class->accel_seperator = g_strdup (" / ");
120   class->latin1_to_char = TRUE;
121 }
122
123 static void
124 gtk_accel_label_set_arg (GtkObject      *object,
125                          GtkArg         *arg,
126                          guint           arg_id)
127 {
128   GtkAccelLabel  *accel_label;
129
130   accel_label = GTK_ACCEL_LABEL (object);
131
132   switch (arg_id)
133     {
134     case ARG_ACCEL_WIDGET:
135       gtk_accel_label_set_accel_widget (accel_label, (GtkWidget*) GTK_VALUE_OBJECT (*arg));
136       break;
137     default:
138       break;
139     }
140 }
141
142 static void
143 gtk_accel_label_get_arg (GtkObject      *object,
144                          GtkArg         *arg,
145                          guint           arg_id)
146 {
147   GtkAccelLabel  *accel_label;
148
149   accel_label = GTK_ACCEL_LABEL (object);
150
151   switch (arg_id)
152     {
153     case ARG_ACCEL_WIDGET:
154       GTK_VALUE_OBJECT (*arg) = (GtkObject*) accel_label->accel_widget;
155       break;
156     default:
157       arg->type = GTK_TYPE_INVALID;
158       break;
159     }
160 }
161
162 static void
163 gtk_accel_label_init (GtkAccelLabel *accel_label)
164 {
165   accel_label->queue_id = 0;
166   accel_label->accel_padding = 3;
167   accel_label->accel_widget = NULL;
168   accel_label->accel_string = NULL;
169   
170   gtk_accel_label_refetch (accel_label);
171 }
172
173 GtkWidget*
174 gtk_accel_label_new (const gchar *string)
175 {
176   GtkAccelLabel *accel_label;
177   
178   g_return_val_if_fail (string != NULL, NULL);
179   
180   accel_label = gtk_type_new (GTK_TYPE_ACCEL_LABEL);
181   
182   gtk_label_set_text (GTK_LABEL (accel_label), string);
183   
184   return GTK_WIDGET (accel_label);
185 }
186
187 static void
188 gtk_accel_label_destroy (GtkObject *object)
189 {
190   GtkAccelLabel *accel_label;
191   
192   g_return_if_fail (object != NULL);
193   g_return_if_fail (GTK_IS_ACCEL_LABEL (object));
194   
195   accel_label = GTK_ACCEL_LABEL (object);
196
197   gtk_accel_label_set_accel_widget (accel_label, NULL);
198   
199   GTK_OBJECT_CLASS (parent_class)->destroy (object);
200 }
201
202 static void
203 gtk_accel_label_finalize (GtkObject *object)
204 {
205   GtkAccelLabel *accel_label;
206   
207   g_return_if_fail (object != NULL);
208   g_return_if_fail (GTK_IS_ACCEL_LABEL (object));
209   
210   accel_label = GTK_ACCEL_LABEL (object);
211   
212   g_free (accel_label->accel_string);
213   
214   GTK_OBJECT_CLASS (parent_class)->finalize (object);
215 }
216
217 guint
218 gtk_accel_label_get_accel_width (GtkAccelLabel *accel_label)
219 {
220   g_return_val_if_fail (accel_label != NULL, 0);
221   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), 0);
222   
223   return (accel_label->accel_string_width +
224           (accel_label->accel_string_width ? accel_label->accel_padding : 0));
225 }
226
227 static void
228 gtk_accel_label_size_request (GtkWidget      *widget,
229                               GtkRequisition *requisition)
230 {
231   GtkAccelLabel *accel_label;
232   
233   g_return_if_fail (widget != NULL);
234   g_return_if_fail (GTK_IS_ACCEL_LABEL (widget));
235   g_return_if_fail (requisition != NULL);
236   
237   accel_label = GTK_ACCEL_LABEL (widget);
238   
239   if (GTK_WIDGET_CLASS (parent_class)->size_request)
240     GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
241   
242   accel_label->accel_string_width = gdk_string_width (GTK_WIDGET (accel_label)->style->font,
243                                                       accel_label->accel_string);
244 }
245
246 static gint
247 gtk_accel_label_expose_event (GtkWidget      *widget,
248                               GdkEventExpose *event)
249 {
250   GtkMisc *misc;
251   GtkAccelLabel *accel_label;
252   
253   g_return_val_if_fail (widget != NULL, FALSE);
254   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (widget), FALSE);
255   g_return_val_if_fail (event != NULL, FALSE);
256   
257   accel_label = GTK_ACCEL_LABEL (widget);
258   misc = GTK_MISC (accel_label);
259           
260   if (GTK_WIDGET_DRAWABLE (accel_label))
261     {
262       guint ac_width;
263       
264       ac_width = gtk_accel_label_get_accel_width (accel_label);
265       
266       if (widget->allocation.width >= widget->requisition.width + ac_width)
267         {
268           guint x;
269           guint y;
270           
271           widget->allocation.width -= ac_width;
272           if (GTK_WIDGET_CLASS (parent_class)->expose_event)
273             GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
274           widget->allocation.width += ac_width;
275           
276           x = widget->allocation.x + widget->allocation.width - misc->xpad - ac_width;
277           
278           y = (widget->allocation.y * (1.0 - misc->yalign) +
279                (widget->allocation.y + widget->allocation.height -
280                 (widget->requisition.height - misc->ypad * 2)) *
281                misc->yalign + widget->style->font->ascent) + 1.5;
282           
283           if (GTK_WIDGET_STATE (accel_label) == GTK_STATE_INSENSITIVE)
284             gdk_draw_string (widget->window,
285                              widget->style->font,
286                              widget->style->white_gc,
287                              x + 1,
288                              y + 1,
289                              accel_label->accel_string);
290           
291           gdk_draw_string (widget->window,
292                            widget->style->font,
293                            widget->style->fg_gc[GTK_WIDGET_STATE (accel_label)],
294                            x,
295                            y,
296                            accel_label->accel_string);
297         }
298       else
299         {
300           if (GTK_WIDGET_CLASS (parent_class)->expose_event)
301             GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
302         }
303     }
304   
305   return TRUE;
306 }
307
308 static void
309 gtk_accel_label_queue_refetch (GtkAccelLabel *accel_label)
310 {
311   g_return_if_fail (accel_label != NULL);
312   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
313
314   if (accel_label->queue_id == 0)
315     accel_label->queue_id = gtk_idle_add_priority (G_PRIORITY_HIGH_IDLE,
316                                                    (GtkFunction) gtk_accel_label_refetch_idle,
317                                                    accel_label);
318 }
319
320 void
321 gtk_accel_label_set_accel_widget (GtkAccelLabel *accel_label,
322                                   GtkWidget     *accel_widget)
323 {
324   g_return_if_fail (accel_label != NULL);
325   g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label));
326   if (accel_widget != NULL)
327     g_return_if_fail (GTK_IS_WIDGET (accel_widget));
328   
329   if (accel_widget != accel_label->accel_widget)
330     {
331       if (accel_label->accel_widget)
332         {
333           gtk_signal_disconnect_by_func (GTK_OBJECT (accel_label->accel_widget),
334                                          GTK_SIGNAL_FUNC (gtk_accel_label_queue_refetch),
335                                          accel_label);
336           gtk_widget_unref (accel_label->accel_widget);
337         }
338       if (accel_label->queue_id)
339         {
340           gtk_idle_remove (accel_label->queue_id);
341           accel_label->queue_id = 0;
342         }
343       accel_label->accel_widget = accel_widget;
344       if (accel_label->accel_widget)
345         {
346           gtk_widget_ref (accel_label->accel_widget);
347           gtk_signal_connect_object_after (GTK_OBJECT (accel_label->accel_widget),
348                                            "add-accelerator",
349                                            GTK_SIGNAL_FUNC (gtk_accel_label_queue_refetch),
350                                            GTK_OBJECT (accel_label));
351           gtk_signal_connect_object_after (GTK_OBJECT (accel_label->accel_widget),
352                                            "remove-accelerator",
353                                            GTK_SIGNAL_FUNC (gtk_accel_label_queue_refetch),
354                                            GTK_OBJECT (accel_label));
355         }
356     }
357 }
358
359 static gboolean
360 gtk_accel_label_refetch_idle (GtkAccelLabel *accel_label)
361 {
362   gboolean retval;
363
364   GDK_THREADS_ENTER ();
365   retval = gtk_accel_label_refetch (accel_label);
366   GDK_THREADS_LEAVE ();
367
368   return retval;
369 }
370
371 gboolean
372 gtk_accel_label_refetch (GtkAccelLabel *accel_label)
373 {
374   GtkAccelLabelClass *class;
375
376   g_return_val_if_fail (accel_label != NULL, FALSE);
377   g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), FALSE);
378
379   class = GTK_ACCEL_LABEL_CLASS (GTK_OBJECT (accel_label)->klass);
380   
381   g_free (accel_label->accel_string);
382   accel_label->accel_string = NULL;
383   
384   if (accel_label->accel_widget)
385     {
386       GtkAccelEntry *entry = NULL;
387       GSList *slist;
388       
389       slist = gtk_accel_group_entries_from_object (GTK_OBJECT (accel_label->accel_widget));
390       for (; slist; slist = slist->next)
391         {
392           entry = slist->data;
393           if (entry->accel_flags & GTK_ACCEL_VISIBLE)
394             {
395               GString *gstring;
396               gboolean had_mod;
397               
398               gstring = g_string_new (accel_label->accel_string);
399               if (gstring->len)
400                 g_string_append (gstring, class->accel_seperator);
401               else
402                 g_string_append (gstring, "   ");
403
404               if (entry->accel_flags & GTK_ACCEL_SIGNAL_VISIBLE)
405                 {
406                   g_string_append (gstring, class->signal_quote1);
407                   g_string_append (gstring, gtk_signal_name (entry->signal_id));
408                   g_string_append (gstring, class->signal_quote2);
409                 }
410               
411               had_mod = FALSE;
412               if (entry->accelerator_mods & GDK_SHIFT_MASK)
413                 {
414                   g_string_append (gstring, class->mod_name_shift);
415                   had_mod = TRUE;
416                 }
417               if (entry->accelerator_mods & GDK_CONTROL_MASK)
418                 {
419                   if (had_mod)
420                     g_string_append (gstring, class->mod_separator);
421                   g_string_append (gstring, class->mod_name_control);
422                   had_mod = TRUE;
423                 }
424               if (entry->accelerator_mods & GDK_MOD1_MASK)
425                 {
426                   if (had_mod)
427                     g_string_append (gstring, class->mod_separator);
428                   g_string_append (gstring, class->mod_name_alt);
429                   had_mod = TRUE;
430                 }
431               
432               if (had_mod)
433                 g_string_append (gstring, class->mod_separator);
434               if (entry->accelerator_key < 0x80 ||
435                   (entry->accelerator_key > 0x80 &&
436                    entry->accelerator_key <= 0xff &&
437                    class->latin1_to_char))
438                 {
439                   switch (entry->accelerator_key)
440                     {
441                     case ' ':
442                       g_string_append (gstring, "Space");
443                       break;
444                     case '\\':
445                       g_string_append (gstring, "Backslash");
446                       break;
447                     default:
448                       g_string_append_c (gstring, toupper (entry->accelerator_key));
449                       break;
450                     }
451                 }
452               else
453                 {
454                   gchar *tmp;
455                   
456                   tmp = gtk_accelerator_name (entry->accelerator_key, 0);
457                   if (tmp[0] != 0 && tmp[1] == 0)
458                     tmp[0] = toupper (tmp[0]);
459                   g_string_append (gstring, tmp);
460                   g_free (tmp);
461                 }
462
463               g_free (accel_label->accel_string);
464               accel_label->accel_string = gstring->str;
465               g_string_free (gstring, FALSE);
466             }
467         }
468     }
469
470   if (!accel_label->accel_string)
471     accel_label->accel_string = g_strdup ("");
472
473   if (accel_label->queue_id)
474     {
475       gtk_idle_remove (accel_label->queue_id);
476       accel_label->queue_id = 0;
477     }
478
479   gtk_widget_queue_resize (GTK_WIDGET (accel_label));
480
481   return FALSE;
482 }