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