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 #include "gdk/gdkkeysyms.h"
21 #include "gdk/gdki18n.h"
30 typedef struct _GtkLabelULine GtkLabelULine;
37 * We need (space,width) only before we've set (x,y), so to save
38 * memory, these pairs should really be wrapped in a union.
39 * I haven't yet figured out how to do this without making the code
59 static void gtk_label_class_init (GtkLabelClass *klass);
60 static void gtk_label_init (GtkLabel *label);
61 static void gtk_label_set_arg (GtkObject *object,
64 static void gtk_label_get_arg (GtkObject *object,
67 static void gtk_label_finalize (GtkObject *object);
68 static void gtk_label_size_request (GtkWidget *widget,
69 GtkRequisition *requisition);
70 static gint gtk_label_expose (GtkWidget *widget,
71 GdkEventExpose *event);
73 static GtkLabelWord * gtk_label_word_alloc (void);
74 static GtkLabelULine * gtk_label_uline_alloc (void);
75 static void gtk_label_free_words (GtkLabel *label);
76 static void gtk_label_free_ulines (GtkLabelWord *word);
77 static gint gtk_label_split_text (GtkLabel * label);
78 static void gtk_label_finalize_lines (GtkLabel * label, gint line_width);
79 static void gtk_label_finalize_lines_wrap(GtkLabel * label, gint line_width);
82 static GtkMiscClass *parent_class = NULL;
84 static GMemChunk *word_chunk = 0;
85 static GtkLabelWord *free_words = 0;
86 static GMemChunk *uline_chunk = 0;
87 static GtkLabelULine *free_ulines = 0;
90 gtk_label_get_type (void)
92 static GtkType label_type = 0;
96 static const GtkTypeInfo label_info =
100 sizeof (GtkLabelClass),
101 (GtkClassInitFunc) gtk_label_class_init,
102 (GtkObjectInitFunc) gtk_label_init,
103 /* reserved_1 */ NULL,
104 /* reserved_2 */ NULL,
105 (GtkClassInitFunc) NULL,
108 label_type = gtk_type_unique (gtk_misc_get_type (), &label_info);
109 gtk_type_set_chunk_alloc (label_type, 32);
116 gtk_label_class_init (GtkLabelClass *class)
118 GtkObjectClass *object_class;
119 GtkWidgetClass *widget_class;
121 object_class = (GtkObjectClass*) class;
122 widget_class = (GtkWidgetClass*) class;
124 parent_class = gtk_type_class (gtk_misc_get_type ());
126 gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
127 gtk_object_add_arg_type ("GtkLabel::pattern", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_PATTERN);
128 gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
130 object_class->set_arg = gtk_label_set_arg;
131 object_class->get_arg = gtk_label_get_arg;
132 object_class->finalize = gtk_label_finalize;
134 widget_class->size_request = gtk_label_size_request;
135 widget_class->expose_event = gtk_label_expose;
139 gtk_label_set_arg (GtkObject *object,
145 label = GTK_LABEL (object);
150 gtk_label_set_text (label, GTK_VALUE_STRING (*arg) ? GTK_VALUE_STRING (*arg) : "");
153 gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
156 gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
164 gtk_label_get_arg (GtkObject *object,
170 label = GTK_LABEL (object);
175 GTK_VALUE_STRING (*arg) = g_strdup (label->label);
178 GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
181 GTK_VALUE_ENUM (*arg) = label->jtype;
184 arg->type = GTK_TYPE_INVALID;
190 gtk_label_init (GtkLabel *label)
192 GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
195 label->label_wc = NULL;
196 label->pattern = NULL;
200 label->max_width = 0;
201 label->jtype = GTK_JUSTIFY_CENTER;
204 gtk_label_set_text (label, "");
208 gtk_label_new (const char *str)
212 g_return_val_if_fail (str != NULL, NULL);
214 label = gtk_type_new (gtk_label_get_type ());
216 gtk_label_set_text (label, str);
218 return GTK_WIDGET (label);
222 gtk_label_set_text_internal (GtkLabel *label,
227 g_free (label->label);
229 g_free (label->label_wc);
232 label->label_wc = str_wc;
234 gtk_label_free_words (label);
236 if (GTK_WIDGET_VISIBLE (label))
238 if (GTK_WIDGET_MAPPED (label))
239 gtk_widget_queue_clear (GTK_WIDGET (label));
241 gtk_widget_queue_resize (GTK_WIDGET (label));
246 gtk_label_set_text (GtkLabel *label,
253 g_return_if_fail (label != NULL);
254 g_return_if_fail (GTK_IS_LABEL (label));
255 g_return_if_fail (str != NULL);
257 if (!label->label || strcmp (label->label, str))
259 /* Convert text to wide characters */
261 str_wc = g_new (GdkWChar, len + 1);
262 wc_len = gdk_mbstowcs (str_wc, str, len + 1);
263 str_wc[wc_len] = '\0';
265 gtk_label_set_text_internal (label, g_strdup (str), str_wc);
270 gtk_label_set_pattern (GtkLabel *label,
271 const gchar *pattern)
273 g_return_if_fail (label != NULL);
274 g_return_if_fail (GTK_IS_LABEL (label));
277 g_free (label->pattern);
278 label->pattern = g_strdup (pattern);
280 if (GTK_WIDGET_VISIBLE (label))
282 if (GTK_WIDGET_MAPPED (label))
283 gtk_widget_queue_clear (GTK_WIDGET (label));
285 gtk_widget_queue_resize (GTK_WIDGET (label));
290 gtk_label_set_justify (GtkLabel *label, GtkJustification jtype)
292 g_return_if_fail (label != NULL);
293 g_return_if_fail (GTK_IS_LABEL (label));
295 if ((GtkJustification) label->jtype != jtype)
297 if ((label->jtype == GTK_JUSTIFY_FILL) ||
298 (jtype == GTK_JUSTIFY_FILL))
299 /* FIXME: think about this a little */
300 gtk_label_free_words (label);
302 label->jtype = jtype;
304 if (GTK_WIDGET_VISIBLE (label))
306 if (GTK_WIDGET_MAPPED (label))
307 gtk_widget_queue_clear (GTK_WIDGET (label));
309 gtk_widget_queue_resize (GTK_WIDGET (label));
315 gtk_label_set_line_wrap (GtkLabel *label, gboolean wrap)
317 g_return_if_fail (label != NULL);
318 g_return_if_fail (GTK_IS_LABEL (label));
320 if (label->wrap != wrap) {
321 if (GTK_WIDGET_VISIBLE (label))
323 if (GTK_WIDGET_MAPPED (label))
324 gtk_widget_queue_clear (GTK_WIDGET (label));
326 gtk_widget_queue_resize (GTK_WIDGET (label));
333 gtk_label_get (GtkLabel *label,
336 g_return_if_fail (label != NULL);
337 g_return_if_fail (GTK_IS_LABEL (label));
338 g_return_if_fail (str != NULL);
344 gtk_label_finalize (GtkObject *object)
348 g_return_if_fail (object != NULL);
349 g_return_if_fail (GTK_IS_LABEL (object));
351 label = GTK_LABEL (object);
353 g_free (label->label);
355 g_free (label->pattern);
356 gtk_label_free_words (label);
357 (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
361 gtk_label_word_alloc ()
367 word_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
368 sizeof (GtkLabelWord),
369 32 * sizeof (GtkLabelWord),
376 free_words = word->next;
380 word = g_mem_chunk_alloc (word_chunk);
389 gtk_label_free_words (GtkLabel *label)
395 for (last = label->words; last->next != 0; last = last->next)
396 gtk_label_free_ulines (label->words);
397 last->next = free_words;
398 free_words = label->words;
402 static GtkLabelULine*
403 gtk_label_uline_alloc (void)
405 GtkLabelULine * uline;
409 uline_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
410 sizeof (GtkLabelULine),
411 32 * sizeof (GtkLabelULine),
418 free_ulines = uline->next;
422 uline = g_mem_chunk_alloc (uline_chunk);
431 gtk_label_free_ulines (GtkLabelWord *word)
436 for (last = word->uline; last->next != 0; last = last->next)
438 last->next = free_ulines;
439 free_ulines = word->uline;
445 gtk_label_split_text (GtkLabel *label)
447 GtkLabelWord *word, **tailp;
448 gint space_width, line_width, max_line_width;
451 g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
453 gtk_label_free_words (label);
454 if (label->label == NULL)
457 /* Split text at new-lines. */
458 space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
462 tailp = &label->words;
463 str = label->label_wc;
467 word = gtk_label_word_alloc ();
469 if (str == label->label_wc || str[-1] == '\n')
471 /* Paragraph break */
474 max_line_width = MAX (line_width, max_line_width);
477 else if (str[0] == ' ')
479 while (str[0] == ' ')
482 word->space += space_width;
487 /* Regular inter-word space */
488 word->space = space_width;
491 word->beginning = str;
495 while (*p && *p != '\n')
501 word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
507 line_width += word->space + word->width;
513 /* Add an empty word to represent an empty line
515 if ((str == label->label_wc) || (str[-1] == '\n'))
517 word = gtk_label_word_alloc ();
520 word->beginning = str;
528 return MAX (line_width, max_line_width);
532 gtk_label_split_text_wrapped (GtkLabel *label)
534 /* this needs to handle white space better. */
535 GtkLabelWord *word, **tailp;
536 gint space_width, line_width, max_line_width;
539 g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
541 gtk_label_free_words (label);
542 if (label->label == NULL)
545 /* Split text at new-lines. (Or at spaces in the case of paragraphs). */
546 space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
550 tailp = &label->words;
551 str = label->label_wc;
554 word = gtk_label_word_alloc ();
556 if (str == label->label_wc || str[-1] == '\n')
558 /* Paragraph break */
561 max_line_width = MAX (line_width, max_line_width);
564 else if (str[0] == ' ')
568 while (str[0] == ' ')
574 if (label->jtype == GTK_JUSTIFY_FILL)
575 word->space = (space_width * 3 + 1) / 2;
577 word->space = space_width * nspaces;
581 /* Regular inter-word space */
582 word->space = space_width;
585 word->beginning = str;
588 while (*p && !gdk_iswspace (*p))
593 word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
599 line_width += word->space + word->width;
605 return MAX (line_width, max_line_width);
608 /* gtk_label_pick_width
610 * Split paragraphs, trying to make each line at least min_width,
611 * and trying even harder to make each line no longer than max_width.
613 * Returns the length of the longest resulting line.
615 * (The reason we go to all this effort to pick a paragraph width is to
616 * try to avoid the lame look of a short paragraph with a
620 gtk_label_pick_width (GtkLabel *label,
625 gint width, line_width;
627 g_return_val_if_fail (label->wrap, min_width);
631 for (word = label->words; word; word = word->next)
635 && (line_width >= min_width
636 || line_width + word->width + word->space > max_width)))
639 width = MAX (width, line_width);
642 line_width += word->space + word->width;
645 return MAX (width, line_width);
648 /* Here, we finalize the lines.
649 * This is only for non-wrap labels. Wrapped labels
650 * use gtk_label_finalize_wrap instead.
653 gtk_label_finalize_lines (GtkLabel *label,
657 gint y, baseline_skip, y_max;
661 g_return_if_fail (!label->wrap);
662 ptrn = label->pattern;
665 baseline_skip = GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 2;
667 for (line = label->words; line; line = line->next)
669 if (label->jtype == GTK_JUSTIFY_CENTER)
670 line->x = (line_width - line->width) / 2;
671 else if (label->jtype == GTK_JUSTIFY_RIGHT)
672 line->x = line_width - line->width;
676 line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
679 /* now we deal with the underline stuff; */
680 if (ptrn && ptrn[0] != '\0')
682 for (i = 0; i < line->length; i++)
686 else if (ptrn[i] == '_')
693 GtkLabelULine *uline;
695 for (j = i + 1; j < line->length; j++)
699 else if (ptrn[j] == ' ')
703 /* good. Now we have an underlined segment.
704 * let's measure it and record it.
706 offset = gdk_text_width_wc (GTK_WIDGET (label)->style->font,
709 gdk_text_extents_wc (GTK_WIDGET (label)->style->font,
712 &rbearing, &width, NULL,
714 y_max = MAX (descent + 2, y_max);
715 uline = gtk_label_uline_alloc ();
716 uline->x1 = offset + line->x + lbearing - 1;
717 uline->x2 = offset + line->x + rbearing;
718 uline->y = line->y + descent + 2;
719 uline->next = line->uline;
724 if (strlen (ptrn) > line->length)
725 /* the + 1 is for line breaks. */
726 ptrn += line->length + 1;
730 y += (baseline_skip + y_max);
733 label->max_width = line_width;
734 GTK_WIDGET (label)->requisition.width = line_width + 2 * label->misc.xpad;
735 GTK_WIDGET (label)->requisition.height = y + 2 * label->misc.ypad;
738 /* this finalizes word-wrapped words */
740 gtk_label_finalize_lines_wrap (GtkLabel *label,
743 GtkLabelWord *word, *line, *next_line;
746 gint x, y, space, extra_width, add_space, baseline_skip;
748 g_return_if_fail (label->wrap);
750 ptrn = label->pattern;
752 baseline_skip = GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 1;
754 for (line = label->words; line != 0; line = next_line)
757 extra_width = line_width - line->width;
759 for (next_line = line->next; next_line; next_line = next_line->next)
761 if (next_line->space == 0)
762 break; /* New paragraph */
763 if (next_line->space + next_line->width > extra_width)
765 extra_width -= next_line->space + next_line->width;
766 space += next_line->space;
770 line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
774 for (word = line->next; word != next_line; word = word->next)
776 if (next_line && next_line->space)
778 /* Not last line of paragraph --- fill line if needed */
779 if (label->jtype == GTK_JUSTIFY_FILL) {
780 add_space = (extra_width * word->space + space / 2) / space;
781 extra_width -= add_space;
782 space -= word->space;
786 word->x = x + word->space + add_space;
788 x = word->x + word->width;
791 y += (baseline_skip);
794 label->max_width = line_width;
795 widget = GTK_WIDGET (label);
796 widget->requisition.width = line_width + 2 * label->misc.xpad;
797 widget->requisition.height = y + 2 * label->misc.ypad + 1;
801 gtk_label_size_request (GtkWidget *widget,
802 GtkRequisition *requisition)
806 g_return_if_fail (widget != NULL);
807 g_return_if_fail (GTK_IS_LABEL (widget));
808 g_return_if_fail (requisition != NULL);
810 label = GTK_LABEL (widget);
813 * There are a number of conditions which will necessitate re-filling
817 * 2. justification changed either from to to GTK_JUSTIFY_FILL.
820 * These have been detected elsewhere, and label->words will be zero,
821 * if one of the above has occured.
823 * Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
825 * 4. gtk_widget_set_usize has changed the requested width.
826 * 5. gtk_misc_set_padding has changed xpad.
827 * 6. maybe others?...
829 * Too much of a pain to detect all these case, so always re-fill. I
830 * don't think it's really that slow.
835 GtkWidgetAuxInfo *aux_info;
836 gint longest_paragraph;
838 longest_paragraph = gtk_label_split_text_wrapped (label);
840 aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
841 if (aux_info && aux_info->width > 0)
843 label->max_width = MAX(aux_info->width - 2 * label->misc.xpad, 1);
844 gtk_label_split_text_wrapped (label);
848 label->max_width = gdk_string_width (GTK_WIDGET (label)->style->font,
849 "This is a good enough length for any line to have.");
850 label->max_width = MIN (label->max_width, (gdk_screen_width () + 1) / 2);
851 label->max_width = MIN (label->max_width, longest_paragraph);
852 if (longest_paragraph > 0)
854 gint nlines, perfect_width;
856 nlines = (longest_paragraph + label->max_width - 1) / label->max_width;
857 perfect_width = (longest_paragraph + nlines - 1) / nlines;
858 label->max_width = gtk_label_pick_width (label,
863 gtk_label_finalize_lines_wrap (label, label->max_width);
865 else if (label->words == NULL)
867 label->max_width = gtk_label_split_text (label);
868 gtk_label_finalize_lines (label, label->max_width);
871 if (requisition != &widget->requisition)
872 *requisition = widget->requisition;
876 gtk_label_paint_word (GtkLabel *label,
882 GtkWidget *widget = GTK_WIDGET (label);
883 GtkLabelULine *uline;
886 tmp_str = gdk_wcstombs (word->beginning);
887 gtk_paint_string (widget->style, widget->window, widget->state,
888 area, widget, "label",
894 for (uline = word->uline; uline; uline = uline->next)
895 gtk_paint_hline (widget->style, widget->window,
898 x + uline->x1, x + uline->x2, y + uline->y);
904 gtk_label_expose (GtkWidget *widget,
905 GdkEventExpose *event)
912 g_return_val_if_fail (widget != NULL, FALSE);
913 g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
914 g_return_val_if_fail (event != NULL, FALSE);
916 label = GTK_LABEL (widget);
918 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
919 label->label && (*label->label != '\0'))
921 misc = GTK_MISC (widget);
926 gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
927 gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
929 x = widget->allocation.x + misc->xpad +
930 (widget->allocation.width - label->max_width - 2 * misc->xpad)
931 * misc->xalign + 0.5;
933 y = (widget->allocation.y
934 + (widget->allocation.height
935 - widget->requisition.height) * misc->yalign
937 for (word = label->words; word; word = word->next)
939 gchar save = word->beginning[word->length];
940 word->beginning[word->length] = '\0';
941 gtk_label_paint_word (label, x, y, word, &event->area);
942 word->beginning[word->length] = save;
945 gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
946 gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
953 gtk_label_parse_uline (GtkLabel *label,
956 guint accel_key = GDK_VoidSymbol;
957 GdkWChar *p, *q, *string_wc;
960 gint length, wc_length;
963 g_return_val_if_fail(string != NULL, GDK_VoidSymbol);
965 /* Convert text to wide characters */
966 length = strlen (string);
967 string_wc = g_new (GdkWChar, length + 1);
968 wc_length = gdk_mbstowcs (string_wc, string, length + 1);
969 string_wc[wc_length] = '\0';
971 pattern = g_new (gchar, length+1);
987 if (accel_key == GDK_VoidSymbol)
988 accel_key = gdk_keyval_to_lower (*p);
1009 gtk_label_set_text_internal (label, gdk_wcstombs (string_wc), string_wc);
1010 gtk_label_set_pattern (label, pattern);