]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
reworked the redrawing heuristics somewhat, this fixed a bunch of existing
[~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
869 static void
870 gtk_label_paint_word (GtkLabel     *label,
871                       gint          x,
872                       gint          y,
873                       GtkLabelWord *word,
874                       GdkRectangle *area)
875 {
876   GtkWidget *widget = GTK_WIDGET (label);
877   GtkLabelULine *uline;
878   gchar *tmp_str;
879   
880   tmp_str = gdk_wcstombs (word->beginning);
881   gtk_paint_string (widget->style, widget->window, widget->state,
882                     area, widget, "label", 
883                     x + word->x,
884                     y + word->y,
885                     tmp_str);
886   g_free (tmp_str);
887   
888   for (uline = word->uline; uline; uline = uline->next)
889     gtk_paint_hline (widget->style, widget->window, 
890                      widget->state, area,
891                      widget, "label", 
892                      x + uline->x1, x + uline->x2, y + uline->y);
893   
894   
895 }
896
897 static gint
898 gtk_label_expose (GtkWidget      *widget,
899                   GdkEventExpose *event)
900 {
901   GtkLabel *label;
902   GtkMisc *misc;
903   GtkLabelWord *word;
904   gint x, y;
905   
906   g_return_val_if_fail (widget != NULL, FALSE);
907   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
908   g_return_val_if_fail (event != NULL, FALSE);
909   
910   label = GTK_LABEL (widget);
911   
912   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
913       label->label && (*label->label != '\0'))
914     {
915       misc = GTK_MISC (widget);
916       
917       /*
918        * GC Clipping
919        */
920       gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
921       gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
922       
923       x = widget->allocation.x + misc->xpad +
924         (widget->allocation.width - label->max_width) 
925         * misc->xalign + 0.5;
926       
927       y = (widget->allocation.y
928            + (widget->allocation.height
929               - widget->requisition.height) * misc->yalign
930            + misc->ypad + 0.5);
931       for (word = label->words; word; word = word->next)
932         {
933           gchar save = word->beginning[word->length];
934           word->beginning[word->length] = '\0';
935           gtk_label_paint_word (label, x, y, word, &event->area);
936           word->beginning[word->length] = save;
937         }
938       
939       gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
940       gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
941     }
942
943   return TRUE;
944 }
945
946 guint      
947 gtk_label_parse_uline (GtkLabel    *label,
948                        const gchar *string)
949 {
950   guint accel_key = GDK_VoidSymbol;
951   GdkWChar *p, *q, *string_wc;
952   gchar *r;
953   gchar *pattern;
954   gint length, wc_length;
955   gboolean underscore;
956   
957   /* Convert text to wide characters */
958   length = strlen (string);
959   string_wc = g_new (GdkWChar, length + 1);
960   wc_length = gdk_mbstowcs (string_wc, string, length + 1);
961   string_wc[wc_length] = '\0';
962   
963   pattern = g_new (gchar, length+1);
964   
965   underscore = FALSE;
966   
967   p = q = string_wc;
968   r = pattern;
969   
970   while (*p)
971     {
972       if (underscore)
973         {
974           if (*p == '_')
975             *r++ = ' ';
976           else
977             {
978               *r++ = '_';
979               if (accel_key == GDK_VoidSymbol)
980                 accel_key = gdk_keyval_to_lower (*p);
981             }
982           
983           *q++ = *p;
984           underscore = FALSE;
985         }
986       else
987         {
988           if (*p == '_')
989             underscore = TRUE;
990           else
991             {
992               *q++ = *p;
993               *r++ = ' ';
994             }
995         }
996       p++;
997     }
998   *q = 0;
999   *r = 0;
1000   
1001   gtk_label_set_text_internal (label, gdk_wcstombs (string_wc), string_wc);
1002   gtk_label_set_pattern (label, pattern);
1003   
1004   g_free (pattern);
1005   
1006   return accel_key;
1007 }