]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
e443c2dfa7554d7114b722a40f4f1e2bf264b60e
[~andy/gtk] / gtk / gtklabel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /*
20  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
24  */
25
26 #include <math.h>
27 #include <string.h>
28 #include "gtklabel.h"
29 #include "gdk/gdkkeysyms.h"
30 #include "gdk/gdki18n.h"
31 #include <pango/pango.h>
32
33
34 enum {
35   ARG_0,
36   ARG_LABEL,
37   ARG_PATTERN,
38   ARG_JUSTIFY,
39   ARG_WRAP
40 };
41
42 static void gtk_label_class_init        (GtkLabelClass    *klass);
43 static void gtk_label_init              (GtkLabel         *label);
44 static void gtk_label_set_arg           (GtkObject        *object,
45                                          GtkArg           *arg,
46                                          guint             arg_id);
47 static void gtk_label_get_arg           (GtkObject        *object,
48                                          GtkArg           *arg,
49                                          guint             arg_id);
50 static void gtk_label_finalize          (GObject          *object);
51 static void gtk_label_size_request      (GtkWidget        *widget,
52                                          GtkRequisition   *requisition);
53 static void gtk_label_style_set         (GtkWidget        *widget,
54                                          GtkStyle         *previous_style);
55 static void gtk_label_direction_changed (GtkWidget        *widget,
56                                          GtkTextDirection  previous_dir);
57 static gint gtk_label_expose            (GtkWidget        *widget,
58                                          GdkEventExpose   *event);
59
60 static GtkMiscClass *parent_class = NULL;
61
62 GtkType
63 gtk_label_get_type (void)
64 {
65   static GtkType label_type = 0;
66   
67   if (!label_type)
68     {
69       static const GTypeInfo label_info =
70       {
71         sizeof (GtkLabelClass),
72         NULL,           /* base_init */
73         NULL,           /* base_finalize */
74         (GClassInitFunc) gtk_label_class_init,
75         NULL,           /* class_finalize */
76         NULL,           /* class_data */
77         sizeof (GtkLabel),
78         32,             /* n_preallocs */
79         (GInstanceInitFunc) gtk_label_init,
80       };
81
82       label_type = g_type_register_static (GTK_TYPE_MISC, "GtkLabel", &label_info, 0);
83     }
84   
85   return label_type;
86 }
87
88 static void
89 gtk_label_class_init (GtkLabelClass *class)
90 {
91   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
92   GtkObjectClass *object_class;
93   GtkWidgetClass *widget_class;
94   
95   object_class = (GtkObjectClass*) class;
96   widget_class = (GtkWidgetClass*) class;
97   
98   parent_class = gtk_type_class (GTK_TYPE_MISC);
99   
100   gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
101   gtk_object_add_arg_type ("GtkLabel::pattern", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_PATTERN);
102   gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
103   gtk_object_add_arg_type ("GtkLabel::wrap", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_WRAP);
104   
105   gobject_class->finalize = gtk_label_finalize;
106
107   object_class->set_arg = gtk_label_set_arg;
108   object_class->get_arg = gtk_label_get_arg;
109   
110   widget_class->size_request = gtk_label_size_request;
111   widget_class->style_set = gtk_label_style_set;
112   widget_class->direction_changed = gtk_label_direction_changed;
113   widget_class->expose_event = gtk_label_expose;
114 }
115
116 static void
117 gtk_label_set_arg (GtkObject      *object,
118                    GtkArg         *arg,
119                    guint           arg_id)
120 {
121   GtkLabel *label;
122   
123   label = GTK_LABEL (object);
124   
125   switch (arg_id)
126     {
127     case ARG_LABEL:
128       gtk_label_set_text (label, GTK_VALUE_STRING (*arg));
129       break;
130     case ARG_PATTERN:
131       gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
132       break;
133     case ARG_JUSTIFY:
134       gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
135       break;
136     case ARG_WRAP:
137       gtk_label_set_line_wrap (label, GTK_VALUE_BOOL (*arg));
138       break;      
139     default:
140       break;
141     }
142 }
143
144 static void
145 gtk_label_get_arg (GtkObject      *object,
146                    GtkArg         *arg,
147                    guint           arg_id)
148 {
149   GtkLabel *label;
150   
151   label = GTK_LABEL (object);
152   
153   switch (arg_id)
154     {
155     case ARG_LABEL:
156       GTK_VALUE_STRING (*arg) = g_strdup (label->label);
157       break;
158     case ARG_PATTERN:
159       GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
160       break;
161     case ARG_JUSTIFY:
162       GTK_VALUE_ENUM (*arg) = label->jtype;
163       break;
164     case ARG_WRAP:
165       GTK_VALUE_BOOL (*arg) = label->wrap;
166       break;
167     default:
168       arg->type = GTK_TYPE_INVALID;
169       break;
170     }
171 }
172
173 static void
174 gtk_label_init (GtkLabel *label)
175 {
176   GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
177   
178   label->label = NULL;
179   label->pattern = NULL;
180
181   label->jtype = GTK_JUSTIFY_CENTER;
182   label->wrap = FALSE;
183   
184   label->layout = NULL;
185   label->attrs = NULL;
186   
187   gtk_label_set_text (label, "");
188 }
189
190 GtkWidget*
191 gtk_label_new (const gchar *str)
192 {
193   GtkLabel *label;
194   
195   label = gtk_type_new (GTK_TYPE_LABEL);
196
197   if (str && *str)
198     gtk_label_set_text (label, str);
199   
200   return GTK_WIDGET (label);
201 }
202
203 static inline void
204 gtk_label_set_text_internal (GtkLabel *label,
205                              gchar    *str)
206 {
207   g_free (label->label);
208   
209   label->label = str;
210   if (label->layout)
211     {
212       g_object_unref (G_OBJECT (label->layout));
213       label->layout = NULL;
214     }
215   
216   gtk_widget_queue_resize (GTK_WIDGET (label));
217 }
218
219 void
220 gtk_label_set_text (GtkLabel    *label,
221                     const gchar *str)
222 {
223   g_return_if_fail (GTK_IS_LABEL (label));
224   
225   gtk_label_set_text_internal (label, g_strdup (str ? str : ""));
226 }
227
228 /**
229  * gtk_label_set_attributes:
230  * @label: a #GtkLabel
231  * @attrs: a #PangoAttrList
232  * 
233  * Sets a #PangoAttrList; the attributes in the list are applied to the
234  * label text.
235  **/
236 void
237 gtk_label_set_attributes (GtkLabel         *label,
238                           PangoAttrList    *attrs)
239 {
240   g_return_if_fail (GTK_IS_LABEL (label));
241
242   if (attrs)
243     pango_attr_list_ref (attrs);
244   
245   if (label->attrs)
246     pango_attr_list_unref (label->attrs);
247
248   label->attrs = attrs;
249 }
250
251 static guint
252 set_markup (GtkLabel    *label,
253             const gchar *str,
254             gboolean     with_uline)
255 {
256   gchar *text = NULL;
257   GError *error = NULL;
258   PangoAttrList *attrs = NULL;
259   gunichar accel_char = 0;
260
261   if (str == NULL)
262     str = "";
263   
264   if (!pango_parse_markup (str,
265                            -1,
266                            with_uline ? '_' : 0,
267                            &attrs,
268                            &text,
269                            with_uline ? &accel_char : NULL,
270                            &error))
271     {
272       g_warning ("Failed to set label from markup due to error parsing markup: %s",
273                  error->message);
274       g_error_free (error);
275       return 0;
276     }
277
278   if (text)
279     gtk_label_set_text (label, text);
280
281   if (attrs)
282     {
283       gtk_label_set_attributes (label, attrs);
284       pango_attr_list_unref (attrs);
285     }
286
287   if (accel_char != 0)
288     return gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
289   else
290     return GDK_VoidSymbol;
291 }
292
293 /**
294  * gtk_label_set_markup:
295  * @label: a #GtkLabel
296  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
297  * 
298  * Parses @str which is marked up with the Pango text markup language,
299  * setting the label's text and attribute list based on the parse results.
300  **/
301 void
302 gtk_label_set_markup (GtkLabel    *label,
303                       const gchar *str)
304 {  
305   g_return_if_fail (GTK_IS_LABEL (label));
306
307   set_markup (label, str, FALSE);
308 }
309
310 /**
311  * gtk_label_set_markup:
312  * @label: a #GtkLabel
313  * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
314  * 
315  * Parses @str which is marked up with the Pango text markup language,
316  * setting the label's text and attribute list based on the parse results.
317  * If characters in @str are preceded by an underscore, they are underlined
318  * indicating that they represent a keyboard accelerator, and the GDK
319  * keyval for the first underlined accelerator is returned. If there are
320  * no underlines in the text, GDK_VoidSymbol will be returned.
321  **/
322 guint
323 gtk_label_set_markup_with_accel (GtkLabel    *label,
324                                  const gchar *str)
325 {
326   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
327
328   return set_markup (label, str, TRUE);
329 }
330
331 /**
332  * gtk_label_get_text:
333  * @label: a #GtkLabel
334  * 
335  * Fetches the text from a label widget
336  * 
337  * Return value: the text in the label widget. This value must
338  * be freed with g_free().
339  **/
340 gchar *
341 gtk_label_get_text (GtkLabel *label)
342 {
343   g_return_val_if_fail (label != NULL, NULL);
344   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
345
346   return g_strdup (label->label);
347 }
348
349 void
350 gtk_label_set_pattern (GtkLabel    *label,
351                        const gchar *pattern)
352 {
353   g_return_if_fail (GTK_IS_LABEL (label));
354   
355   g_free (label->pattern);
356   label->pattern = g_strdup (pattern);
357
358   gtk_widget_queue_resize (GTK_WIDGET (label));
359 }
360
361 void
362 gtk_label_set_justify (GtkLabel        *label,
363                        GtkJustification jtype)
364 {
365   g_return_if_fail (GTK_IS_LABEL (label));
366   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
367   
368   if ((GtkJustification) label->jtype != jtype)
369     {
370       label->jtype = jtype;
371
372       if (label->layout)
373         {
374           /* No real need to be this drastic, but easier than duplicating the code */
375           g_object_unref (G_OBJECT (label->layout));
376           label->layout = NULL;
377         }
378       
379       gtk_widget_queue_resize (GTK_WIDGET (label));
380     }
381 }
382
383 void
384 gtk_label_set_line_wrap (GtkLabel *label,
385                          gboolean  wrap)
386 {
387   g_return_if_fail (GTK_IS_LABEL (label));
388   
389   wrap = wrap != FALSE;
390   
391   if (label->wrap != wrap)
392     {
393       label->wrap = wrap;
394
395       gtk_widget_queue_resize (GTK_WIDGET (label));
396     }
397 }
398
399 void
400 gtk_label_get (GtkLabel *label,
401                gchar   **str)
402 {
403   g_return_if_fail (label != NULL);
404   g_return_if_fail (GTK_IS_LABEL (label));
405   g_return_if_fail (str != NULL);
406   
407   *str = label->label;
408 }
409
410 static void
411 gtk_label_finalize (GObject *object)
412 {
413   GtkLabel *label;
414   
415   g_return_if_fail (GTK_IS_LABEL (object));
416   
417   label = GTK_LABEL (object);
418   
419   g_free (label->label);
420   g_free (label->pattern);
421
422   if (label->layout)
423     g_object_unref (G_OBJECT (label->layout));
424
425   if (label->attrs)
426     pango_attr_list_unref (label->attrs);
427   
428   G_OBJECT_CLASS (parent_class)->finalize (object);
429 }
430
431 static void
432 gtk_label_pattern_to_attrs (GtkLabel      *label,
433                             PangoAttrList *attrs)
434 {
435   if (label->pattern)
436     {
437       const char *start;
438       const char *p = label->label;
439       const char *q = label->pattern;
440
441       while (1)
442         {
443           while (*p && *q && *q != '_')
444             {
445               p = g_utf8_next_char (p);
446               q++;
447             }
448           start = p;
449           while (*p && *q && *q == '_')
450             {
451               p = g_utf8_next_char (p);
452               q++;
453             }
454
455           if (p > start)
456             {
457               PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
458               attr->start_index = start - label->label;
459               attr->end_index = p - label->label;
460
461               pango_attr_list_insert (attrs, attr);
462             }
463           else
464             break;
465         }
466     }
467 }
468
469 static void
470 gtk_label_size_request (GtkWidget      *widget,
471                         GtkRequisition *requisition)
472 {
473   GtkLabel *label;
474   PangoRectangle logical_rect;
475   
476   g_return_if_fail (GTK_IS_LABEL (widget));
477   g_return_if_fail (requisition != NULL);
478   
479   label = GTK_LABEL (widget);
480
481   /*
482    * There are a number of conditions which will necessitate re-filling
483    * our text:
484    *
485    *     1. text changed.
486    *     2. justification changed either from to to GTK_JUSTIFY_FILL.
487    *     3. font changed.
488    *
489    * These have been detected elsewhere, and label->words will be zero,
490    * if one of the above has occured.
491    *
492    * Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
493    *
494    *     4. gtk_widget_set_usize has changed the requested width.
495    *     5. gtk_misc_set_padding has changed xpad.
496    *     6.  maybe others?...
497    *
498    * Too much of a pain to detect all these case, so always re-fill.  I
499    * don't think it's really that slow.
500    */
501
502   requisition->width = label->misc.xpad;
503   requisition->height = label->misc.ypad;
504
505   if (!label->layout)
506     {
507       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
508       PangoAttrList *attrs = NULL;
509
510       label->layout = gtk_widget_create_pango_layout (widget, label->label);
511
512       /* FIXME move to a model where the pattern isn't stored
513        * permanently, and just modifes or creates the AttrList
514        */
515       if (label->attrs)
516         {
517           attrs = pango_attr_list_copy (label->attrs);
518           pango_layout_set_attributes (label->layout, attrs);
519         }
520       else
521         attrs = pango_attr_list_new ();
522       
523       gtk_label_pattern_to_attrs (label, attrs);
524       pango_attr_list_unref (attrs);
525       
526       switch (label->jtype)
527         {
528         case GTK_JUSTIFY_LEFT:
529           align = PANGO_ALIGN_LEFT;
530           break;
531         case GTK_JUSTIFY_RIGHT:
532           align = PANGO_ALIGN_RIGHT;
533           break;
534         case GTK_JUSTIFY_CENTER:
535           align = PANGO_ALIGN_LEFT;
536           break;
537         case GTK_JUSTIFY_FILL:
538           /* FIXME: This just doesn't work to do this */
539           align = PANGO_ALIGN_LEFT;
540           pango_layout_set_justify (label->layout, TRUE);
541           break;
542         default:
543           g_assert_not_reached();
544         }
545
546       pango_layout_set_alignment (label->layout, align);
547     }
548
549   if (label->wrap)
550     {
551       GtkWidgetAuxInfo *aux_info;
552       gint longest_paragraph;
553       gint width, height;
554       gint real_width;
555
556       aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
557       if (aux_info && aux_info->width > 0)
558         {
559           pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
560           pango_layout_get_extents (label->layout, NULL, &logical_rect);
561
562           requisition->width += aux_info->width;
563           requisition->height += PANGO_PIXELS (logical_rect.height);
564         }
565       else
566         {
567           pango_layout_set_width (label->layout, -1);
568           pango_layout_get_extents (label->layout, NULL, &logical_rect);
569       
570           width = logical_rect.width;
571           height = logical_rect.height;
572           
573           /* Try to guess a reasonable maximum width
574            */
575           longest_paragraph = width;
576
577           width = MIN (width,
578                        PANGO_SCALE * gdk_string_width (GTK_WIDGET (label)->style->font,
579                                                 "This long string gives a good enough length for any line to have."));
580           width = MIN (width,
581                        PANGO_SCALE * (gdk_screen_width () + 1) / 2);
582
583           pango_layout_set_width (label->layout, width);
584           pango_layout_get_extents (label->layout, NULL, &logical_rect);
585           real_width = logical_rect.width;
586           height = logical_rect.height;
587           
588           /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
589            * so we try short search for a narrower width that leaves us with the same height
590            */
591           if (longest_paragraph > 0)
592             {
593               gint nlines, perfect_width;
594
595               nlines = pango_layout_get_line_count (label->layout);
596               perfect_width = (longest_paragraph + nlines - 1) / nlines;
597               
598               if (perfect_width < width)
599                 {
600                   pango_layout_set_width (label->layout, perfect_width);
601                   pango_layout_get_extents (label->layout, NULL, &logical_rect);
602                   
603                   if (logical_rect.height <= height)
604                     {
605                       width = perfect_width;
606                       real_width = logical_rect.width;
607                       height = logical_rect.height;
608                     }
609                   else
610                     {
611                       gint mid_width = (perfect_width + width) / 2;
612
613                       if (mid_width > perfect_width)
614                         {
615                           pango_layout_set_width (label->layout, mid_width);
616                           pango_layout_get_extents (label->layout, NULL, &logical_rect);
617
618                           if (logical_rect.height <= height)
619                             {
620                               width = mid_width;
621                               real_width = logical_rect.width;
622                               height = logical_rect.height;
623                             }
624                         }
625                     }
626                 }
627             }
628           pango_layout_set_width (label->layout, width);
629
630           requisition->width += PANGO_PIXELS (real_width);
631           requisition->height += PANGO_PIXELS (height);
632         }
633     }
634   else                          /* !label->wrap */
635     {
636       pango_layout_set_width (label->layout, -1);
637       pango_layout_get_extents (label->layout, NULL, &logical_rect);
638
639       requisition->width += PANGO_PIXELS (logical_rect.width);
640       requisition->height += PANGO_PIXELS (logical_rect.height);
641     }
642 }
643
644 static void 
645 gtk_label_style_set (GtkWidget *widget,
646                      GtkStyle  *previous_style)
647 {
648   GtkLabel *label;
649
650   g_return_if_fail (GTK_IS_LABEL (widget));
651   
652   label = GTK_LABEL (widget);
653
654   if (previous_style && label->layout)
655     pango_layout_context_changed (label->layout);
656 }
657
658 static void 
659 gtk_label_direction_changed (GtkWidget        *widget,
660                              GtkTextDirection previous_dir)
661 {
662   GtkLabel *label = GTK_LABEL (widget);
663
664   if (label->layout)
665     pango_layout_context_changed (label->layout);
666
667   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
668 }
669
670 #if 0
671 static void
672 gtk_label_paint_word (GtkLabel     *label,
673                       gint          x,
674                       gint          y,
675                       GtkLabelWord *word,
676                       GdkRectangle *area)
677 {
678   GtkWidget *widget = GTK_WIDGET (label);
679   GtkLabelULine *uline;
680   gchar *tmp_str;
681   
682   tmp_str = gdk_wcstombs (word->beginning);
683   if (tmp_str)
684     {
685       gtk_paint_string (widget->style, widget->window, widget->state,
686                         area, widget, "label", 
687                         x + word->x,
688                         y + word->y,
689                         tmp_str);
690       g_free (tmp_str);
691     }
692   
693   for (uline = word->uline; uline; uline = uline->next)
694     gtk_paint_hline (widget->style, widget->window, 
695                      widget->state, area,
696                      widget, "label", 
697                      x + uline->x1, x + uline->x2, y + uline->y);
698 }
699 #endif
700
701 static gint
702 gtk_label_expose (GtkWidget      *widget,
703                   GdkEventExpose *event)
704 {
705   GtkLabel *label;
706   GtkMisc *misc;
707   gint x, y;
708   gfloat xalign;
709   
710   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
711   g_return_val_if_fail (event != NULL, FALSE);
712   
713   label = GTK_LABEL (widget);
714   
715   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
716       label->label && (*label->label != '\0'))
717     {
718       misc = GTK_MISC (widget);
719       
720       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
721         xalign = misc->xalign;
722       else
723         xalign = 1. - misc->xalign;
724       
725       x = floor (widget->allocation.x + (gint)misc->xpad
726                  + ((widget->allocation.width - widget->requisition.width) * xalign)
727                  + 0.5);
728       
729       y = floor (widget->allocation.y + (gint)misc->ypad 
730                  + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
731                  + 0.5);
732
733       
734       gtk_paint_layout (widget->style,
735                         widget->window,
736                         GTK_WIDGET_STATE (widget),
737                         &event->area,
738                         widget,
739                         "label",
740                         x, y,
741                         label->layout);
742     }
743
744   return TRUE;
745 }
746
747 guint      
748 gtk_label_parse_uline (GtkLabel    *label,
749                        const gchar *str)
750 {
751   guint accel_key = GDK_VoidSymbol;
752
753   gchar *new_str;
754   gchar *pattern;
755   const gchar *src;
756   gchar *dest, *pattern_dest;
757   gboolean underscore;
758       
759   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
760   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
761
762   /* Convert text to wide characters */
763
764   new_str = g_new (gchar, strlen (str) + 1);
765   pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
766   
767   underscore = FALSE;
768
769   src = str;
770   dest = new_str;
771   pattern_dest = pattern;
772   
773   while (*src)
774     {
775       gunichar c;
776       gchar *next_src;
777
778       c = g_utf8_get_char (src);
779       if (c == (gunichar)-1)
780         {
781           g_warning ("Invalid input string");
782           g_free (new_str);
783           g_free (pattern);
784           return GDK_VoidSymbol;
785         }
786       next_src = g_utf8_next_char (src);
787       
788       if (underscore)
789         {
790           if (c == '_')
791             *pattern_dest++ = ' ';
792           else
793             {
794               *pattern_dest++ = '_';
795               if (accel_key == GDK_VoidSymbol)
796                 accel_key = gdk_keyval_to_lower (c);
797             }
798
799           while (src < next_src)
800             *dest++ = *src++;
801           
802           underscore = FALSE;
803         }
804       else
805         {
806           if (c == '_')
807             {
808               underscore = TRUE;
809               src = next_src;
810             }
811           else
812             {
813               while (src < next_src)
814                 *dest++ = *src++;
815           
816               *pattern_dest++ = ' ';
817             }
818         }
819     }
820   *dest = 0;
821   *pattern_dest = 0;
822   
823   gtk_label_set_text_internal (label, new_str);
824   gtk_label_set_pattern (label, pattern);
825   
826   g_free (pattern);
827   
828   return accel_key;
829 }