]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
label->label should be the mb text _after_ removing the underlines, not
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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 #include <string.h>
19 #include "gtklabel.h"
20 #include "gdk/gdkkeysyms.h"
21 #include "gdk/gdki18n.h"
22
23 enum {
24   ARG_0,
25   ARG_LABEL,
26   ARG_PATTERN,
27   ARG_JUSTIFY
28 };
29
30 typedef struct _GtkLabelULine GtkLabelULine;
31 struct _GtkLabelWord
32 {
33   GdkWChar *beginning;
34   gint length;
35
36   /* FIXME:
37    * We need (space,width) only before we've set (x,y), so to save
38    * memory, these pairs should really be wrapped in a union.
39    * I haven't yet figured out how to do this without making the code
40    * look ugly. 
41    */
42   gint space;
43   gint width;
44   gint x;
45   gint y;
46   GtkLabelWord *next;
47   gint uline_y;
48   GtkLabelULine *uline;
49 };
50
51 struct _GtkLabelULine
52 {
53   gint x1;
54   gint x2;
55   gint y;
56   GtkLabelULine *next;
57 };
58
59 static void gtk_label_class_init   (GtkLabelClass  *klass);
60 static void gtk_label_init         (GtkLabel       *label);
61 static void gtk_label_set_arg      (GtkObject      *object,
62                                     GtkArg         *arg,
63                                     guint           arg_id);
64 static void gtk_label_get_arg      (GtkObject      *object,
65                                     GtkArg         *arg,
66                                     guint           arg_id);
67 static void gtk_label_finalize     (GtkObject      *object);
68 static void gtk_label_size_request (GtkWidget      *widget,
69                                     GtkRequisition *requisition);
70 static gint gtk_label_expose       (GtkWidget      *widget,
71                                     GdkEventExpose *event);
72
73 static GtkLabelWord * gtk_label_word_alloc (void);
74 static GtkLabelULine * gtk_label_uline_alloc (void);
75 static void gtk_label_free_words   (GtkLabel       *label);
76 static void gtk_label_free_ulines   (GtkLabelWord *word);
77 static gint gtk_label_split_text   (GtkLabel * label);
78 static void gtk_label_finalize_lines    (GtkLabel * label, gint line_width);
79 static void gtk_label_finalize_lines_wrap(GtkLabel * label, gint line_width);
80
81
82 static GtkMiscClass *parent_class = NULL;
83
84 static GMemChunk *word_chunk = 0;
85 static GtkLabelWord *free_words = 0;
86 static GMemChunk *uline_chunk = 0;
87 static GtkLabelULine *free_ulines = 0;
88
89 GtkType
90 gtk_label_get_type (void)
91 {
92   static GtkType label_type = 0;
93   
94   if (!label_type)
95     {
96       static const GtkTypeInfo label_info =
97       {
98         "GtkLabel",
99         sizeof (GtkLabel),
100         sizeof (GtkLabelClass),
101         (GtkClassInitFunc) gtk_label_class_init,
102         (GtkObjectInitFunc) gtk_label_init,
103         /* reserved_1 */ NULL,
104         /* reserved_2 */ NULL,
105         (GtkClassInitFunc) NULL,
106       };
107       
108       label_type = gtk_type_unique (gtk_misc_get_type (), &label_info);
109       gtk_type_set_chunk_alloc (label_type, 32);
110     }
111   
112   return label_type;
113 }
114
115 static void
116 gtk_label_class_init (GtkLabelClass *class)
117 {
118   GtkObjectClass *object_class;
119   GtkWidgetClass *widget_class;
120   
121   object_class = (GtkObjectClass*) class;
122   widget_class = (GtkWidgetClass*) class;
123   
124   parent_class = gtk_type_class (gtk_misc_get_type ());
125   
126   gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
127   gtk_object_add_arg_type ("GtkLabel::pattern", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_PATTERN);
128   gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
129
130   object_class->set_arg = gtk_label_set_arg;
131   object_class->get_arg = gtk_label_get_arg;
132   object_class->finalize = gtk_label_finalize;
133   
134   widget_class->size_request = gtk_label_size_request;
135   widget_class->expose_event = gtk_label_expose;
136 }
137
138 static void
139 gtk_label_set_arg (GtkObject      *object,
140                    GtkArg         *arg,
141                    guint           arg_id)
142 {
143   GtkLabel *label;
144
145   label = GTK_LABEL (object);
146
147   switch (arg_id)
148     {
149     case ARG_LABEL:
150       gtk_label_set_text (label, GTK_VALUE_STRING (*arg) ? GTK_VALUE_STRING (*arg) : "");
151       break;
152     case ARG_PATTERN:
153       gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
154       break;
155     case ARG_JUSTIFY:
156       gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
157       break;
158     default:
159       break;
160     }
161 }
162
163 static void
164 gtk_label_get_arg (GtkObject      *object,
165                    GtkArg         *arg,
166                    guint           arg_id)
167 {
168   GtkLabel *label;
169
170   label = GTK_LABEL (object);
171
172   switch (arg_id)
173     {
174     case ARG_LABEL:
175       GTK_VALUE_STRING (*arg) = g_strdup (label->label);
176       break;
177     case ARG_PATTERN:
178       GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
179       break;
180     case ARG_JUSTIFY:
181       GTK_VALUE_ENUM (*arg) = label->jtype;
182       break;
183     default:
184       arg->type = GTK_TYPE_INVALID;
185       break;
186     }
187 }
188
189 static void
190 gtk_label_init (GtkLabel *label)
191 {
192   GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
193   
194   label->label = NULL;
195   label->words = NULL;
196   label->max_width = 0;
197   label->jtype = GTK_JUSTIFY_CENTER;
198   label->pattern = NULL;
199   label->wrap = FALSE;
200
201   gtk_label_set_text (label, "");
202 }
203
204 GtkWidget*
205 gtk_label_new (const char *str)
206 {
207   GtkLabel *label;
208
209   g_return_val_if_fail (str != NULL, NULL);
210
211   label = gtk_type_new (gtk_label_get_type ());
212
213   gtk_label_set_text (label, str);
214
215   return GTK_WIDGET (label);
216 }
217
218 static void
219 gtk_label_set_text_internal (GtkLabel *label,
220                              char     *str,
221                              GdkWChar *str_wc)
222 {
223   if (label->label)
224     g_free (label->label);
225   if (label->label_wc)
226     g_free (label->label_wc);
227
228   label->label = str;
229   label->label_wc = str_wc;
230
231   gtk_label_free_words (label);
232
233   if (GTK_WIDGET_VISIBLE (label))
234     {
235       if (GTK_WIDGET_MAPPED (label))
236         gtk_widget_queue_clear (GTK_WIDGET (label));
237
238       gtk_widget_queue_resize (GTK_WIDGET (label));
239     }
240 }
241
242 void
243 gtk_label_set_text (GtkLabel *label,
244                     const char *str)
245 {
246   GdkWChar *str_wc;
247   gint len;
248   gint wc_len;
249   
250   g_return_if_fail (label != NULL);
251   g_return_if_fail (GTK_IS_LABEL (label));
252   g_return_if_fail (str != NULL);
253
254   /* Convert text to wide characters */
255   len = strlen (str);
256   str_wc = g_new (GdkWChar, len + 1);
257   wc_len = gdk_mbstowcs (str_wc, str, len + 1);
258   str_wc[wc_len] = '\0';
259
260   gtk_label_set_text_internal (label, g_strdup (str), str_wc);
261 }
262
263 void
264 gtk_label_set_pattern (GtkLabel    *label,
265                        const gchar *pattern)
266 {
267   g_return_if_fail (label != NULL);
268   g_return_if_fail (GTK_IS_LABEL (label));
269   
270   if (label->pattern)
271     g_free (label->pattern);
272   label->pattern = g_strdup (pattern);
273   
274   if (GTK_WIDGET_VISIBLE (label))
275     {
276       if (GTK_WIDGET_MAPPED (label))
277         gtk_widget_queue_clear (GTK_WIDGET (label));
278       
279       gtk_widget_queue_resize (GTK_WIDGET (label));
280     }
281 }
282
283 void
284 gtk_label_set_justify (GtkLabel *label, GtkJustification jtype)
285 {
286   g_return_if_fail (label != NULL);
287   g_return_if_fail (GTK_IS_LABEL (label));
288
289   if ((GtkJustification) label->jtype != jtype)
290     {
291       if ((label->jtype == GTK_JUSTIFY_FILL) || 
292           (jtype == GTK_JUSTIFY_FILL))
293         /* FIXME: think about this a little */
294           gtk_label_free_words (label);
295           
296       label->jtype = jtype;
297       
298       if (GTK_WIDGET_VISIBLE (label))
299         {
300           if (GTK_WIDGET_MAPPED (label))
301             gtk_widget_queue_clear (GTK_WIDGET (label));
302           
303           gtk_widget_queue_resize (GTK_WIDGET (label));
304         }
305     }
306 }
307
308 void
309 gtk_label_set_line_wrap (GtkLabel *label, gboolean wrap)
310 {
311   g_return_if_fail (label != NULL);
312   g_return_if_fail (GTK_IS_LABEL (label));
313
314   if (label->wrap != wrap) {
315     if (GTK_WIDGET_VISIBLE (label))
316       {
317         if (GTK_WIDGET_MAPPED (label))
318           gtk_widget_queue_clear (GTK_WIDGET (label));
319         
320         gtk_widget_queue_resize (GTK_WIDGET (label));
321       }
322     label->wrap = wrap;
323   }
324 }
325
326 void
327 gtk_label_get (GtkLabel  *label,
328                char     **str)
329 {
330   g_return_if_fail (label != NULL);
331   g_return_if_fail (GTK_IS_LABEL (label));
332   g_return_if_fail (str != NULL);
333
334   *str = label->label;
335 }
336
337 static void
338 gtk_label_finalize (GtkObject *object)
339 {
340   GtkLabel *label;
341   
342   g_return_if_fail (object != NULL);
343   g_return_if_fail (GTK_IS_LABEL (object));
344   
345   label = GTK_LABEL (object);
346   
347   g_free (label->label);
348   if (label->pattern) 
349     g_free (label->pattern);
350   gtk_label_free_words (label);
351   (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
352 }
353
354 static GtkLabelWord*
355 gtk_label_word_alloc ()
356 {
357   GtkLabelWord * word;
358
359   if (!word_chunk)
360     {
361       word_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
362                                     sizeof (GtkLabelWord),
363                                     32 * sizeof (GtkLabelWord),
364                                     G_ALLOC_ONLY);
365     }
366   
367   if (free_words)
368     {
369       word = free_words;
370       free_words = word->next;
371     }
372   else
373     {
374       word = g_mem_chunk_alloc (word_chunk);
375     }
376     
377   word->next = 0;
378   word->uline = 0;
379   return word;
380 }
381
382 static void
383 gtk_label_free_words (GtkLabel *label)
384 {
385   GtkLabelWord * last;
386
387   if (label->words)
388     {
389       for (last = label->words; last->next != 0; last = last->next)
390         gtk_label_free_ulines (label->words);
391       last->next = free_words;
392       free_words = label->words;
393       label->words = NULL;
394     }
395 }
396 static GtkLabelULine*
397 gtk_label_uline_alloc (void)
398 {
399   GtkLabelULine * uline;
400
401   if (!uline_chunk)
402     {
403       uline_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
404                                      sizeof (GtkLabelULine),
405                                      32 * sizeof (GtkLabelULine),
406                                      G_ALLOC_ONLY);
407     }
408   
409   if (free_ulines)
410     {
411       uline = free_ulines;
412       free_ulines = uline->next;
413     }
414   else
415     {
416       uline = g_mem_chunk_alloc (uline_chunk);
417     }
418     
419   uline->next = NULL;
420
421   return uline;
422 }
423
424 static void
425 gtk_label_free_ulines (GtkLabelWord *word)
426 {
427   GtkLabelULine *last;
428   if (word->uline)
429     {
430       for (last = word->uline; last->next != 0; last = last->next)
431           ;
432       last->next = free_ulines;
433       free_ulines = word->uline;
434       word->uline = 0;
435     }
436 }
437
438 static gint
439 gtk_label_split_text (GtkLabel *label)
440 {
441   GtkLabelWord *word, **tailp;
442   gint space_width, line_width, max_line_width;
443   GdkWChar *str, *p;
444   
445   g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
446   
447   gtk_label_free_words (label);
448   if (label->label == NULL)
449     return 0;
450   
451   /* Split text at new-lines. */
452   space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
453   
454   line_width = 0;
455   max_line_width = 0;
456   tailp = &label->words;
457   str = label->label_wc;
458
459   while (*str)
460     {
461       word = gtk_label_word_alloc ();
462       
463       if (str == label->label_wc || str[-1] == '\n')
464         {
465           /* Paragraph break */
466           word->space = 0;
467           
468           max_line_width = MAX (line_width, max_line_width);
469           line_width = 0;
470         }
471       else if (str[0] == ' ')
472         {
473           while (str[0] == ' ')
474             {
475               str++;
476               word->space += space_width;
477             }
478         }
479       else
480         {
481           /* Regular inter-word space */
482           word->space = space_width;
483         }
484       
485       word->beginning = str;
486       
487       word->length = 0;
488       p = word->beginning;
489       while (*p && *p != '\n')
490         {
491           word->length++;
492           p++;
493         }
494       
495       word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
496       
497       str += word->length;
498       if (*str)
499         str++;
500       
501       line_width += word->space + word->width;
502       
503       *tailp = word;
504       tailp = &word->next;
505     }
506
507   /* Add an empty word to represent an empty line
508    */
509   if ((str == label->label_wc) || (str[-1] == '\n'))
510     {
511       word = gtk_label_word_alloc ();
512
513       word->space = 0;
514       word->beginning = str;
515       word->length = 0;
516       word->width = 0;
517       
518       *tailp = word;
519       tailp = &word->next;
520     }
521   
522   return MAX (line_width, max_line_width);
523 }
524
525 static gint
526 gtk_label_split_text_wrapped (GtkLabel *label)
527 {
528   /* this needs to handle white space better. */
529   GtkLabelWord *word, **tailp;
530   gint space_width, line_width, max_line_width;
531   GdkWChar *str, *p;
532   
533   g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
534   
535   gtk_label_free_words (label);
536   if (label->label == NULL)
537     return 0;
538   
539   /* Split text at new-lines.  (Or at spaces in the case of paragraphs). */
540   space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
541   
542   line_width = 0;
543   max_line_width = 0;
544   tailp = &label->words;
545   str = label->label_wc;
546   while (*str)
547     {
548       word = gtk_label_word_alloc ();
549       
550       if (str == label->label_wc || str[-1] == '\n')
551         {
552           /* Paragraph break */
553           word->space = 0;
554           
555           max_line_width = MAX (line_width, max_line_width);
556           line_width = 0;
557         }
558       else if (str[0] == ' ')
559         {
560           gint nspaces = 0;
561           
562           while (str[0] == ' ')
563             {
564               nspaces++;
565               str++;
566             }
567           
568           if (label->jtype == GTK_JUSTIFY_FILL)
569             word->space = (space_width * 3 + 1) / 2;
570           else
571             word->space += space_width * nspaces;
572         }
573       else
574         {
575           /* Regular inter-word space */
576           word->space = space_width;
577         }
578       
579       word->beginning = str;
580       word->length = 0;
581       p = word->beginning;
582       while (*p && !gdk_iswspace (*p))
583         {
584           word->length++;
585           p++;
586         }
587       word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
588       
589       str += word->length;
590       if (*str)
591         str++;
592       
593       line_width += word->space + word->width;
594       
595       *tailp = word;
596       tailp = &word->next;
597     }
598   
599   return MAX (line_width, max_line_width);
600 }
601
602 /* gtk_label_pick_width
603  *
604  * Split paragraphs, trying to make each line at least min_width,
605  * and trying even harder to make each line no longer than max_width.
606  *
607  * Returns the length of the longest resulting line.
608  *
609  * (The reason we go to all this effort to pick a paragraph width is to
610  * try to avoid the lame look of a short paragraph with a
611  * short final line.)
612  */
613 static gint
614 gtk_label_pick_width (GtkLabel *label,
615                       gint      min_width,
616                       gint      max_width)
617 {
618   GtkLabelWord *word;
619   gint width, line_width;
620   
621   g_return_val_if_fail (label->wrap, min_width);
622   
623   line_width = 0;
624   width = 0;
625   for (word = label->words; word; word = word->next)
626     {
627       if (word->space == 0
628           || (line_width
629               && (line_width >= min_width
630                   || line_width + word->width + word->space > max_width)))
631         {
632           /* New line */
633           width = MAX (width, line_width);
634           line_width = 0;
635         }
636       line_width += word->space + word->width;
637     }
638   
639   return MAX (width, line_width);
640 }
641
642 /* Here, we finalize the lines.
643  * This is only for non-wrap labels.  Wrapped labels
644  * use gtk_label_finalize_wrap instead.
645  */
646 static void
647 gtk_label_finalize_lines (GtkLabel *label,
648                           gint      line_width)
649 {
650   GtkLabelWord *line;
651   gint y, baseline_skip, y_max;
652   gint i, j;
653   gchar *ptrn;
654   
655   g_return_if_fail (!label->wrap);
656   ptrn = label->pattern;
657
658   y = 0;
659   baseline_skip = GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 2;
660   
661   for (line = label->words; line; line = line->next)
662     {
663       if (label->jtype == GTK_JUSTIFY_CENTER)
664           line->x = (line_width - line->width) / 2;
665       else if (label->jtype == GTK_JUSTIFY_RIGHT)
666           line->x = line_width - line->width;
667       else
668           line->x = 0;
669
670       line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
671       y_max = 0;
672       
673       /* now we deal with the underline stuff; */
674       if (ptrn && ptrn[0] != '\0')
675         {
676           for (i = 0; i < line->length; i++)
677             {
678               if (ptrn[i] == '\0')
679                 break;
680               else if (ptrn[i] == '_')
681                 {
682                   gint descent;
683                   gint rbearing;
684                   gint lbearing;
685                   gint width;
686                   gint offset;
687                   GtkLabelULine *uline;
688                   
689                   for (j = i + 1; j < line->length; j++)
690                     {
691                       if (ptrn[j] == '\0')
692                         break;
693                       else if (ptrn[j] == ' ')
694                         break;
695                     }
696
697                   /* good.  Now we have an underlined segment.
698                    * let's measure it and record it.
699                    */
700                   offset = gdk_text_width_wc (GTK_WIDGET (label)->style->font,
701                                               line->beginning,
702                                               i);
703                   gdk_text_extents_wc (GTK_WIDGET (label)->style->font,
704                                        line->beginning+i,
705                                        j-i, &lbearing,
706                                        &rbearing, &width, NULL,
707                                        &descent);
708                   y_max = MAX (descent + 2, y_max);
709                   uline = gtk_label_uline_alloc ();
710                   uline->x1 = offset + line->x + lbearing - 1;
711                   uline->x2 = offset + line->x + rbearing;
712                   uline->y = line->y + descent + 2;
713                   uline->next = line->uline;
714                   line->uline = uline;
715                   i = j - 1;
716                 }
717             }
718           if (strlen (ptrn) > line->length)
719             /* the + 1 is for line breaks. */
720             ptrn += line->length + 1;
721           else
722             ptrn = NULL;
723         }
724       y += (baseline_skip + y_max);
725     }
726   
727   label->max_width = line_width;
728   GTK_WIDGET (label)->requisition.width = line_width + 2 * label->misc.xpad;
729   GTK_WIDGET (label)->requisition.height = y + 2 * label->misc.ypad;
730 }
731
732 /* this finalizes word-wrapped words */
733 static void
734 gtk_label_finalize_lines_wrap (GtkLabel *label,
735                                gint      line_width)
736 {
737   GtkLabelWord *word, *line, *next_line;
738   GtkWidget *widget;
739   gchar *ptrn;
740   gint x, y, space, extra_width, add_space, baseline_skip;
741   
742   g_return_if_fail (label->wrap);
743
744   ptrn = label->pattern;
745   y = 0;
746   baseline_skip = GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 1;
747   
748   for (line = label->words; line != 0; line = next_line)
749     {
750       space = 0;
751       extra_width = line_width - line->width;
752       
753       for (next_line = line->next; next_line; next_line = next_line->next)
754         {
755           if (next_line->space == 0)
756               break;            /* New paragraph */
757           if (next_line->space + next_line->width > extra_width)
758               break;
759           extra_width -= next_line->space + next_line->width;
760           space += next_line->space;
761         }
762
763       line->x = 0;
764       line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
765       x = line->width;
766       add_space = 0;
767
768       for (word = line->next; word != next_line; word = word->next)
769         {
770           if (next_line && next_line->space)
771             {
772               /* Not last line of paragraph --- fill line if needed */
773               if (label->jtype == GTK_JUSTIFY_FILL) {
774                 add_space = (extra_width * word->space + space / 2) / space;
775                 extra_width -= add_space;
776                 space -= word->space;
777               }
778             }
779           
780           word->x = x + word->space + add_space;
781           word->y = line->y;
782           x = word->x + word->width;
783         }
784
785       y += (baseline_skip);
786     }
787
788   label->max_width = line_width;
789   widget = GTK_WIDGET (label);
790   widget->requisition.width = line_width + 2 * label->misc.xpad;
791   widget->requisition.height = y + 2 * label->misc.ypad + 1;
792 }
793       
794 static void
795 gtk_label_size_request (GtkWidget      *widget,
796                         GtkRequisition *requisition)
797 {
798   GtkLabel *label;
799   
800   g_return_if_fail (widget != NULL);
801   g_return_if_fail (GTK_IS_LABEL (widget));
802   g_return_if_fail (requisition != NULL);
803
804   label = GTK_LABEL (widget);
805
806   /*
807    * There are a number of conditions which will necessitate re-filling
808    * our text:
809    *
810    *     1. text changed.
811    *     2. justification changed either from to to GTK_JUSTIFY_FILL.
812    *     3. font changed.
813    *
814    * These have been detected elsewhere, and label->words will be zero,
815    * if one of the above has occured.
816    *
817    * Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
818    *
819    *     4. gtk_widget_set_usize has changed the requested width.
820    *     5. gtk_misc_set_padding has changed xpad.
821    *     6.  maybe others?...
822    *
823    * Too much of a pain to detect all these case, so always re-fill.  I
824    * don't think it's really that slow.
825    */
826
827   if (label->wrap)
828     {
829       GtkWidgetAuxInfo *aux_info;
830       gint longest_paragraph;
831       
832       longest_paragraph = gtk_label_split_text_wrapped (label);
833       
834       aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
835       if (aux_info && aux_info->width > 0)
836         {
837           label->max_width = MAX(aux_info->width - 2 * label->misc.xpad, 1);
838           gtk_label_split_text_wrapped (label);
839         }
840       else
841         {
842           label->max_width = gdk_string_width (GTK_WIDGET (label)->style->font,
843                                         "This is a good enough length for any line to have.");
844           label->max_width = MIN (label->max_width, (gdk_screen_width () + 1) / 2);
845           label->max_width = MIN (label->max_width, longest_paragraph);
846           if (longest_paragraph > 0)
847             {
848               gint nlines, perfect_width;
849               
850               nlines = (longest_paragraph + label->max_width - 1) / label->max_width;
851               perfect_width = (longest_paragraph + nlines - 1) / nlines;
852               label->max_width = gtk_label_pick_width (label,
853                                                  perfect_width,
854                                                  label->max_width);
855             }
856         }
857       gtk_label_finalize_lines_wrap (label, label->max_width);
858     }
859   else if (label->words == NULL)
860     {
861       label->max_width = gtk_label_split_text (label);
862       gtk_label_finalize_lines (label, label->max_width);
863     }
864
865   if (requisition != &widget->requisition)
866     *requisition = widget->requisition;
867 }
868 static void
869 gtk_label_paint_word (GtkLabel *label,
870                       gint x,
871                       gint y,
872                       GtkLabelWord *word,
873                       GdkRectangle *area)
874 {
875   GtkWidget *widget = GTK_WIDGET (label);
876   GtkLabelULine *uline;
877   gchar *tmp_str;
878
879   tmp_str = gdk_wcstombs (word->beginning);
880   gtk_paint_string (widget->style, widget->window, widget->state,
881                     area, widget, "label", 
882                     x + word->x,
883                     y + word->y,
884                     tmp_str);
885   g_free (tmp_str);
886   
887   for (uline = word->uline;uline;uline = uline->next)
888     gtk_paint_hline (widget->style, widget->window, 
889                      widget->state, area,
890                      widget, "label", 
891                      x + uline->x1, x +uline->x2, y + uline->y);
892
893
894 }
895 static gint
896 gtk_label_expose (GtkWidget      *widget,
897                   GdkEventExpose *event)
898 {
899   GtkLabel *label;
900   GtkMisc *misc;
901   GtkLabelWord *word;
902   gint x, y;
903
904   g_return_val_if_fail (widget != NULL, FALSE);
905   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
906   g_return_val_if_fail (event != NULL, FALSE);
907
908   label = GTK_LABEL (widget);
909   
910   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
911       label->label && (*label->label != '\0'))
912     {
913       misc = GTK_MISC (widget);
914
915       /*
916        * GC Clipping
917        */
918       gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
919       gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
920
921       x = widget->allocation.x + misc->xpad +
922         (widget->allocation.width - label->max_width) 
923         * misc->xalign + 0.5;
924
925       y = (widget->allocation.y
926            + (widget->allocation.height
927               - widget->requisition.height) * misc->yalign
928            + misc->ypad + 0.5);
929       for (word = label->words; word; word = word->next)
930         {
931           gchar save = word->beginning[word->length];
932           word->beginning[word->length] = '\0';
933           gtk_label_paint_word (label, x, y, word, &event->area);
934           word->beginning[word->length] = save;
935         }
936
937       gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
938       gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
939     }
940   return TRUE;
941 }
942 guint      
943 gtk_label_parse_uline (GtkLabel         *label,
944                        const gchar      *string)
945 {
946   guint accel_key = GDK_VoidSymbol;
947   GdkWChar *p, *q, *string_wc;
948   gchar *r;
949   gchar *pattern;
950   gint length, wc_length;
951   gboolean underscore;
952
953   /* Convert text to wide characters */
954   length = strlen (string);
955   string_wc = g_new (GdkWChar, length + 1);
956   wc_length = gdk_mbstowcs (string_wc, string, length + 1);
957   string_wc[wc_length] = '\0';
958   
959   pattern = g_new (gchar, length+1);
960
961   underscore = FALSE;
962
963   p = q = string_wc;
964   r = pattern;
965
966   while (*p)
967     {
968       if (underscore)
969         {
970           if (*p == '_')
971             *r++ = ' ';
972           else
973             {
974               *r++ = '_';
975               if (accel_key == GDK_VoidSymbol)
976                 accel_key = gdk_keyval_to_lower (*p);
977             }
978
979           *q++ = *p;
980           underscore = FALSE;
981         }
982       else
983         {
984           if (*p == '_')
985             underscore = TRUE;
986           else
987             {
988               *q++ = *p;
989               *r++ = ' ';
990             }
991         }
992       p++;
993     }
994   *q = 0;
995   *r = 0;
996
997   gtk_label_set_text_internal (label, gdk_wcstombs (string_wc), string_wc);
998   gtk_label_set_pattern (label, pattern);
999   
1000   g_free (pattern);
1001
1002   return accel_key;
1003 }