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