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