]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
Set the attributes onto the PangoLayout even if they are newly created
[~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         attrs = pango_attr_list_copy (label->attrs);
517       else if (label->pattern)
518         attrs = pango_attr_list_new ();
519
520       if (label->pattern)
521         gtk_label_pattern_to_attrs (label, attrs);
522
523       if (attrs)
524         {
525           pango_layout_set_attributes (label->layout, attrs);
526           pango_attr_list_unref (attrs);
527         }
528       
529       switch (label->jtype)
530         {
531         case GTK_JUSTIFY_LEFT:
532           align = PANGO_ALIGN_LEFT;
533           break;
534         case GTK_JUSTIFY_RIGHT:
535           align = PANGO_ALIGN_RIGHT;
536           break;
537         case GTK_JUSTIFY_CENTER:
538           align = PANGO_ALIGN_LEFT;
539           break;
540         case GTK_JUSTIFY_FILL:
541           /* FIXME: This just doesn't work to do this */
542           align = PANGO_ALIGN_LEFT;
543           pango_layout_set_justify (label->layout, TRUE);
544           break;
545         default:
546           g_assert_not_reached();
547         }
548
549       pango_layout_set_alignment (label->layout, align);
550     }
551
552   if (label->wrap)
553     {
554       GtkWidgetAuxInfo *aux_info;
555       gint longest_paragraph;
556       gint width, height;
557       gint real_width;
558
559       aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
560       if (aux_info && aux_info->width > 0)
561         {
562           pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
563           pango_layout_get_extents (label->layout, NULL, &logical_rect);
564
565           requisition->width += aux_info->width;
566           requisition->height += PANGO_PIXELS (logical_rect.height);
567         }
568       else
569         {
570           pango_layout_set_width (label->layout, -1);
571           pango_layout_get_extents (label->layout, NULL, &logical_rect);
572       
573           width = logical_rect.width;
574           height = logical_rect.height;
575           
576           /* Try to guess a reasonable maximum width
577            */
578           longest_paragraph = width;
579
580           width = MIN (width,
581                        PANGO_SCALE * gdk_string_width (GTK_WIDGET (label)->style->font,
582                                                 "This long string gives a good enough length for any line to have."));
583           width = MIN (width,
584                        PANGO_SCALE * (gdk_screen_width () + 1) / 2);
585
586           pango_layout_set_width (label->layout, width);
587           pango_layout_get_extents (label->layout, NULL, &logical_rect);
588           real_width = logical_rect.width;
589           height = logical_rect.height;
590           
591           /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
592            * so we try short search for a narrower width that leaves us with the same height
593            */
594           if (longest_paragraph > 0)
595             {
596               gint nlines, perfect_width;
597
598               nlines = pango_layout_get_line_count (label->layout);
599               perfect_width = (longest_paragraph + nlines - 1) / nlines;
600               
601               if (perfect_width < width)
602                 {
603                   pango_layout_set_width (label->layout, perfect_width);
604                   pango_layout_get_extents (label->layout, NULL, &logical_rect);
605                   
606                   if (logical_rect.height <= height)
607                     {
608                       width = perfect_width;
609                       real_width = logical_rect.width;
610                       height = logical_rect.height;
611                     }
612                   else
613                     {
614                       gint mid_width = (perfect_width + width) / 2;
615
616                       if (mid_width > perfect_width)
617                         {
618                           pango_layout_set_width (label->layout, mid_width);
619                           pango_layout_get_extents (label->layout, NULL, &logical_rect);
620
621                           if (logical_rect.height <= height)
622                             {
623                               width = mid_width;
624                               real_width = logical_rect.width;
625                               height = logical_rect.height;
626                             }
627                         }
628                     }
629                 }
630             }
631           pango_layout_set_width (label->layout, width);
632
633           requisition->width += PANGO_PIXELS (real_width);
634           requisition->height += PANGO_PIXELS (height);
635         }
636     }
637   else                          /* !label->wrap */
638     {
639       pango_layout_set_width (label->layout, -1);
640       pango_layout_get_extents (label->layout, NULL, &logical_rect);
641
642       requisition->width += PANGO_PIXELS (logical_rect.width);
643       requisition->height += PANGO_PIXELS (logical_rect.height);
644     }
645 }
646
647 static void 
648 gtk_label_style_set (GtkWidget *widget,
649                      GtkStyle  *previous_style)
650 {
651   GtkLabel *label;
652
653   g_return_if_fail (GTK_IS_LABEL (widget));
654   
655   label = GTK_LABEL (widget);
656
657   if (previous_style && label->layout)
658     pango_layout_context_changed (label->layout);
659 }
660
661 static void 
662 gtk_label_direction_changed (GtkWidget        *widget,
663                              GtkTextDirection previous_dir)
664 {
665   GtkLabel *label = GTK_LABEL (widget);
666
667   if (label->layout)
668     pango_layout_context_changed (label->layout);
669
670   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
671 }
672
673 #if 0
674 static void
675 gtk_label_paint_word (GtkLabel     *label,
676                       gint          x,
677                       gint          y,
678                       GtkLabelWord *word,
679                       GdkRectangle *area)
680 {
681   GtkWidget *widget = GTK_WIDGET (label);
682   GtkLabelULine *uline;
683   gchar *tmp_str;
684   
685   tmp_str = gdk_wcstombs (word->beginning);
686   if (tmp_str)
687     {
688       gtk_paint_string (widget->style, widget->window, widget->state,
689                         area, widget, "label", 
690                         x + word->x,
691                         y + word->y,
692                         tmp_str);
693       g_free (tmp_str);
694     }
695   
696   for (uline = word->uline; uline; uline = uline->next)
697     gtk_paint_hline (widget->style, widget->window, 
698                      widget->state, area,
699                      widget, "label", 
700                      x + uline->x1, x + uline->x2, y + uline->y);
701 }
702 #endif
703
704 static gint
705 gtk_label_expose (GtkWidget      *widget,
706                   GdkEventExpose *event)
707 {
708   GtkLabel *label;
709   GtkMisc *misc;
710   gint x, y;
711   gfloat xalign;
712   
713   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
714   g_return_val_if_fail (event != NULL, FALSE);
715   
716   label = GTK_LABEL (widget);
717
718   /* if label->layout is NULL it means we got a set_text since
719    * our last size request, so a resize should be queued,
720    * which means a full expose is in the queue anyway.
721    */
722   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
723       label->layout && label->label && (*label->label != '\0'))
724     {
725       misc = GTK_MISC (widget);
726       
727       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
728         xalign = misc->xalign;
729       else
730         xalign = 1. - misc->xalign;
731       
732       x = floor (widget->allocation.x + (gint)misc->xpad
733                  + ((widget->allocation.width - widget->requisition.width) * xalign)
734                  + 0.5);
735       
736       y = floor (widget->allocation.y + (gint)misc->ypad 
737                  + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
738                  + 0.5);
739
740       
741       gtk_paint_layout (widget->style,
742                         widget->window,
743                         GTK_WIDGET_STATE (widget),
744                         &event->area,
745                         widget,
746                         "label",
747                         x, y,
748                         label->layout);
749     }
750
751   return TRUE;
752 }
753
754 guint      
755 gtk_label_parse_uline (GtkLabel    *label,
756                        const gchar *str)
757 {
758   guint accel_key = GDK_VoidSymbol;
759
760   gchar *new_str;
761   gchar *pattern;
762   const gchar *src;
763   gchar *dest, *pattern_dest;
764   gboolean underscore;
765       
766   g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
767   g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
768
769   /* Convert text to wide characters */
770
771   new_str = g_new (gchar, strlen (str) + 1);
772   pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
773   
774   underscore = FALSE;
775
776   src = str;
777   dest = new_str;
778   pattern_dest = pattern;
779   
780   while (*src)
781     {
782       gunichar c;
783       gchar *next_src;
784
785       c = g_utf8_get_char (src);
786       if (c == (gunichar)-1)
787         {
788           g_warning ("Invalid input string");
789           g_free (new_str);
790           g_free (pattern);
791           return GDK_VoidSymbol;
792         }
793       next_src = g_utf8_next_char (src);
794       
795       if (underscore)
796         {
797           if (c == '_')
798             *pattern_dest++ = ' ';
799           else
800             {
801               *pattern_dest++ = '_';
802               if (accel_key == GDK_VoidSymbol)
803                 accel_key = gdk_keyval_to_lower (c);
804             }
805
806           while (src < next_src)
807             *dest++ = *src++;
808           
809           underscore = FALSE;
810         }
811       else
812         {
813           if (c == '_')
814             {
815               underscore = TRUE;
816               src = next_src;
817             }
818           else
819             {
820               while (src < next_src)
821                 *dest++ = *src++;
822           
823               *pattern_dest++ = ' ';
824             }
825         }
826     }
827   *dest = 0;
828   *pattern_dest = 0;
829   
830   gtk_label_set_text_internal (label, new_str);
831   gtk_label_set_pattern (label, pattern);
832   
833   g_free (pattern);
834   
835   return accel_key;
836 }