1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
29 #include "gdk/gdkkeysyms.h"
30 #include "gdk/gdki18n.h"
40 typedef struct _GtkLabelULine GtkLabelULine;
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
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,
74 static void gtk_label_get_arg (GtkObject *object,
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);
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);
92 static GtkMiscClass *parent_class = NULL;
94 static GMemChunk *word_chunk = NULL;
95 static GMemChunk *uline_chunk = NULL;
98 gtk_label_get_type (void)
100 static GtkType label_type = 0;
104 static const GtkTypeInfo label_info =
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,
116 label_type = gtk_type_unique (GTK_TYPE_MISC, &label_info);
117 gtk_type_set_chunk_alloc (label_type, 32);
124 gtk_label_class_init (GtkLabelClass *class)
126 GtkObjectClass *object_class;
127 GtkWidgetClass *widget_class;
129 object_class = (GtkObjectClass*) class;
130 widget_class = (GtkWidgetClass*) class;
132 parent_class = gtk_type_class (GTK_TYPE_MISC);
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);
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;
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;
149 gtk_label_set_arg (GtkObject *object,
155 label = GTK_LABEL (object);
160 gtk_label_set_text (label, GTK_VALUE_STRING (*arg));
163 gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
166 gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
169 gtk_label_set_line_wrap (label, GTK_VALUE_BOOL (*arg));
177 gtk_label_get_arg (GtkObject *object,
183 label = GTK_LABEL (object);
188 GTK_VALUE_STRING (*arg) = g_strdup (label->label);
191 GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
194 GTK_VALUE_ENUM (*arg) = label->jtype;
197 GTK_VALUE_BOOL (*arg) = label->wrap;
200 arg->type = GTK_TYPE_INVALID;
206 gtk_label_init (GtkLabel *label)
208 GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
211 label->label_wc = NULL;
212 label->pattern = NULL;
216 label->max_width = 0;
217 label->jtype = GTK_JUSTIFY_CENTER;
220 gtk_label_set_text (label, "");
224 gtk_label_new (const gchar *str)
228 label = gtk_type_new (GTK_TYPE_LABEL);
231 gtk_label_set_text (label, str);
233 return GTK_WIDGET (label);
237 gtk_label_set_text_internal (GtkLabel *label,
241 gtk_label_free_words (label);
243 g_free (label->label);
244 g_free (label->label_wc);
247 label->label_wc = str_wc;
249 gtk_widget_queue_resize (GTK_WIDGET (label));
253 gtk_label_set_text (GtkLabel *label,
260 g_return_if_fail (GTK_IS_LABEL (label));
264 if (!label->label || strcmp (label->label, str))
266 /* Convert text to wide characters */
268 str_wc = g_new (GdkWChar, len + 1);
269 wc_len = gdk_mbstowcs (str_wc, str, len + 1);
272 str_wc[wc_len] = '\0';
273 gtk_label_set_text_internal (label, g_strdup (str), str_wc);
281 gtk_label_set_pattern (GtkLabel *label,
282 const gchar *pattern)
284 g_return_if_fail (GTK_IS_LABEL (label));
286 gtk_label_free_words (label);
288 g_free (label->pattern);
289 label->pattern = g_strdup (pattern);
291 gtk_widget_queue_resize (GTK_WIDGET (label));
295 gtk_label_set_justify (GtkLabel *label,
296 GtkJustification jtype)
298 g_return_if_fail (GTK_IS_LABEL (label));
299 g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
301 if ((GtkJustification) label->jtype != jtype)
303 gtk_label_free_words (label);
305 label->jtype = jtype;
307 gtk_widget_queue_resize (GTK_WIDGET (label));
312 gtk_label_set_line_wrap (GtkLabel *label,
315 g_return_if_fail (GTK_IS_LABEL (label));
317 wrap = wrap != FALSE;
319 if (label->wrap != wrap)
321 gtk_label_free_words (label);
325 gtk_widget_queue_resize (GTK_WIDGET (label));
330 gtk_label_get (GtkLabel *label,
333 g_return_if_fail (label != NULL);
334 g_return_if_fail (GTK_IS_LABEL (label));
335 g_return_if_fail (str != NULL);
341 gtk_label_finalize (GtkObject *object)
345 g_return_if_fail (object != NULL);
346 g_return_if_fail (GTK_IS_LABEL (object));
348 label = GTK_LABEL (object);
350 g_free (label->label);
351 g_free (label->label_wc);
352 g_free (label->pattern);
354 gtk_label_free_words (label);
356 GTK_OBJECT_CLASS (parent_class)->finalize (object);
359 static GtkLabelULine*
360 gtk_label_uline_alloc (void)
362 GtkLabelULine *uline;
365 uline_chunk = g_mem_chunk_create (GtkLabelWord, 32, G_ALLOC_AND_FREE);
367 uline = g_chunk_new0 (GtkLabelULine, uline_chunk);
375 gtk_label_free_ulines (GtkLabelWord *word)
379 GtkLabelULine *uline = word->uline;
381 word->uline = uline->next;
382 g_chunk_free (uline, uline_chunk);
387 gtk_label_word_alloc (void)
392 word_chunk = g_mem_chunk_create (GtkLabelWord, 32, G_ALLOC_AND_FREE);
394 word = g_chunk_new0 (GtkLabelWord, word_chunk);
396 word->beginning = NULL;
404 gtk_label_free_words (GtkLabel *label)
408 GtkLabelWord *word = label->words;
410 label->words = word->next;
412 gtk_label_free_ulines (word);
414 g_chunk_free (word, word_chunk);
419 gtk_label_split_text (GtkLabel *label)
421 GtkLabelWord *word, **tailp;
422 gint space_width, line_width, max_line_width;
425 gtk_label_free_words (label);
426 if (label->label == NULL)
429 /* Split text at new-lines. */
430 space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
434 tailp = &label->words;
435 str = label->label_wc;
439 word = gtk_label_word_alloc ();
441 if (str == label->label_wc || str[-1] == '\n')
443 /* Paragraph break */
446 max_line_width = MAX (line_width, max_line_width);
449 else if (str[0] == ' ')
451 while (str[0] == ' ')
454 word->space += space_width;
459 /* Regular inter-word space */
460 word->space = space_width;
463 word->beginning = str;
467 while (*p && *p != '\n')
473 word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
479 line_width += word->space + word->width;
485 /* Add an empty word to represent an empty line
487 if (str == label->label_wc || str[-1] == '\n')
489 word = gtk_label_word_alloc ();
492 word->beginning = str;
500 return MAX (line_width, max_line_width);
503 /* this needs to handle white space better. */
505 gtk_label_split_text_wrapped (GtkLabel *label)
507 GtkLabelWord *word, **tailp;
508 gint space_width, line_width, max_line_width;
511 gtk_label_free_words (label);
512 if (label->label == NULL)
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, " ");
520 tailp = &label->words;
521 str = label->label_wc;
524 word = gtk_label_word_alloc ();
526 if (str == label->label_wc || str[-1] == '\n')
528 /* Paragraph break */
531 max_line_width = MAX (line_width, max_line_width);
534 else if (str[0] == ' ')
538 while (str[0] == ' ')
544 if (label->jtype == GTK_JUSTIFY_FILL)
545 word->space = (space_width * 3 + 1) / 2;
547 word->space = space_width * nspaces;
551 /* Regular inter-word space */
552 word->space = space_width;
555 word->beginning = str;
558 while (*p && !gdk_iswspace (*p))
563 word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
569 line_width += word->space + word->width;
575 return MAX (line_width, max_line_width);
578 /* gtk_label_pick_width
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.
583 * Returns the length of the longest resulting line.
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
590 gtk_label_pick_width (GtkLabel *label,
595 gint width, line_width;
597 g_return_val_if_fail (label->wrap, min_width);
601 for (word = label->words; word; word = word->next)
605 && (line_width >= min_width
606 || line_width + word->width + word->space > max_width)))
609 width = MAX (width, line_width);
612 line_width += word->space + word->width;
615 return MAX (width, line_width);
618 /* Here, we finalize the lines.
619 * This is only for non-wrap labels. Wrapped labels
620 * use gtk_label_finalize_wrap instead.
623 gtk_label_finalize_lines (GtkLabel *label,
624 GtkRequisition *requisition,
628 gint y, baseline_skip, y_max;
632 g_return_if_fail (!label->wrap);
633 ptrn = label->pattern;
636 baseline_skip = (GTK_WIDGET (label)->style->font->ascent +
637 GTK_WIDGET (label)->style->font->descent + 2);
639 for (line = label->words; line; line = line->next)
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;
648 line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
651 /* now we deal with the underline stuff; */
652 if (ptrn && ptrn[0] != '\0')
654 for (i = 0; i < line->length; i++)
658 else if (ptrn[i] == '_')
665 GtkLabelULine *uline;
667 for (j = i + 1; j < line->length; j++)
671 else if (ptrn[j] == ' ')
675 /* good. Now we have an underlined segment.
676 * let's measure it and record it.
678 offset = gdk_text_width_wc (GTK_WIDGET (label)->style->font,
681 gdk_text_extents_wc (GTK_WIDGET (label)->style->font,
684 &rbearing, &width, NULL,
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;
696 if (strlen (ptrn) > line->length)
697 /* the + 1 is for line breaks. */
698 ptrn += line->length + 1;
702 y += (baseline_skip + y_max);
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;
710 /* this finalizes word-wrapped words */
712 gtk_label_finalize_lines_wrap (GtkLabel *label,
713 GtkRequisition *requisition,
716 GtkLabelWord *word, *line, *next_line;
719 gint x, y, space, extra_width, add_space, baseline_skip;
721 g_return_if_fail (label->wrap);
723 ptrn = label->pattern;
725 baseline_skip = (GTK_WIDGET (label)->style->font->ascent +
726 GTK_WIDGET (label)->style->font->descent + 1);
728 for (line = label->words; line != 0; line = next_line)
731 extra_width = max_line_width - line->width;
733 for (next_line = line->next; next_line; next_line = next_line->next)
735 if (next_line->space == 0)
736 break; /* New paragraph */
737 if (next_line->space + next_line->width > extra_width)
739 extra_width -= next_line->space + next_line->width;
740 space += next_line->space;
744 line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
748 for (word = line->next; word != next_line; word = word->next)
750 if (next_line && next_line->space)
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;
760 word->x = x + word->space + add_space;
762 x = word->x + word->width;
765 y += (baseline_skip);
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;
775 gtk_label_size_request (GtkWidget *widget,
776 GtkRequisition *requisition)
780 g_return_if_fail (GTK_IS_LABEL (widget));
781 g_return_if_fail (requisition != NULL);
783 label = GTK_LABEL (widget);
786 * There are a number of conditions which will necessitate re-filling
790 * 2. justification changed either from to to GTK_JUSTIFY_FILL.
793 * These have been detected elsewhere, and label->words will be zero,
794 * if one of the above has occured.
796 * Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
798 * 4. gtk_widget_set_usize has changed the requested width.
799 * 5. gtk_misc_set_padding has changed xpad.
800 * 6. maybe others?...
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.
808 GtkWidgetAuxInfo *aux_info;
809 gint longest_paragraph;
811 longest_paragraph = gtk_label_split_text_wrapped (label);
813 aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
814 if (aux_info && aux_info->width > 0)
816 label->max_width = MAX (aux_info->width - 2 * label->misc.xpad, 1);
817 gtk_label_split_text_wrapped (label);
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)
827 gint nlines, perfect_width;
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,
836 gtk_label_finalize_lines_wrap (label, requisition, label->max_width);
838 else if (!label->words)
840 label->max_width = gtk_label_split_text (label);
841 gtk_label_finalize_lines (label, requisition, label->max_width);
846 gtk_label_style_set (GtkWidget *widget,
847 GtkStyle *previous_style)
851 g_return_if_fail (GTK_IS_LABEL (widget));
853 label = GTK_LABEL (widget);
855 /* Clear the list of words so that they are recomputed on
858 if (previous_style && label->words)
859 gtk_label_free_words (label);
863 gtk_label_paint_word (GtkLabel *label,
869 GtkWidget *widget = GTK_WIDGET (label);
870 GtkLabelULine *uline;
873 tmp_str = gdk_wcstombs (word->beginning);
876 gtk_paint_string (widget->style, widget->window, widget->state,
877 area, widget, "label",
884 for (uline = word->uline; uline; uline = uline->next)
885 gtk_paint_hline (widget->style, widget->window,
888 x + uline->x1, x + uline->x2, y + uline->y);
892 gtk_label_expose (GtkWidget *widget,
893 GdkEventExpose *event)
900 g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
901 g_return_val_if_fail (event != NULL, FALSE);
903 label = GTK_LABEL (widget);
905 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
906 label->label && (*label->label != '\0'))
908 misc = GTK_MISC (widget);
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);
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);
921 y = floor (widget->allocation.y + (gint)misc->ypad
922 + (((gint)widget->allocation.height
923 - (gint)widget->requisition.height)
924 * misc->yalign) + 0.5);
926 for (word = label->words; word; word = word->next)
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;
934 gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
935 gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
942 gtk_label_parse_uline (GtkLabel *label,
945 guint accel_key = GDK_VoidSymbol;
946 GdkWChar *p, *q, *string_wc;
949 gint length, wc_length;
952 g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
953 g_return_val_if_fail (string != NULL, GDK_VoidSymbol);
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);
962 return GDK_VoidSymbol;
965 string_wc[wc_length] = '\0';
967 pattern = g_new (gchar, length+1);
983 if (accel_key == GDK_VoidSymbol)
984 accel_key = gdk_keyval_to_lower (*p);
1005 gtk_label_set_text_internal (label, gdk_wcstombs (string_wc), string_wc);
1006 gtk_label_set_pattern (label, pattern);