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