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