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);
196 label->max_width = 0;
197 label->jtype = GTK_JUSTIFY_CENTER;
198 label->pattern = NULL;
201 gtk_label_set_text (label, "");
205 gtk_label_new (const char *str)
209 g_return_val_if_fail (str != NULL, NULL);
211 label = gtk_type_new (gtk_label_get_type ());
213 gtk_label_set_text (label, str);
215 return GTK_WIDGET (label);
219 gtk_label_set_text_internal (GtkLabel *label,
224 g_free (label->label);
226 g_free (label->label_wc);
229 label->label_wc = str_wc;
231 gtk_label_free_words (label);
233 if (GTK_WIDGET_VISIBLE (label))
235 if (GTK_WIDGET_MAPPED (label))
236 gtk_widget_queue_clear (GTK_WIDGET (label));
238 gtk_widget_queue_resize (GTK_WIDGET (label));
243 gtk_label_set_text (GtkLabel *label,
250 g_return_if_fail (label != NULL);
251 g_return_if_fail (GTK_IS_LABEL (label));
252 g_return_if_fail (str != NULL);
254 /* Convert text to wide characters */
256 str_wc = g_new (GdkWChar, len + 1);
257 wc_len = gdk_mbstowcs (str_wc, str, len + 1);
258 str_wc[wc_len] = '\0';
260 gtk_label_set_text_internal (label, g_strdup (str), str_wc);
264 gtk_label_set_pattern (GtkLabel *label,
265 const gchar *pattern)
267 g_return_if_fail (label != NULL);
268 g_return_if_fail (GTK_IS_LABEL (label));
271 g_free (label->pattern);
272 label->pattern = g_strdup (pattern);
274 if (GTK_WIDGET_VISIBLE (label))
276 if (GTK_WIDGET_MAPPED (label))
277 gtk_widget_queue_clear (GTK_WIDGET (label));
279 gtk_widget_queue_resize (GTK_WIDGET (label));
284 gtk_label_set_justify (GtkLabel *label, GtkJustification jtype)
286 g_return_if_fail (label != NULL);
287 g_return_if_fail (GTK_IS_LABEL (label));
289 if ((GtkJustification) label->jtype != jtype)
291 if ((label->jtype == GTK_JUSTIFY_FILL) ||
292 (jtype == GTK_JUSTIFY_FILL))
293 /* FIXME: think about this a little */
294 gtk_label_free_words (label);
296 label->jtype = jtype;
298 if (GTK_WIDGET_VISIBLE (label))
300 if (GTK_WIDGET_MAPPED (label))
301 gtk_widget_queue_clear (GTK_WIDGET (label));
303 gtk_widget_queue_resize (GTK_WIDGET (label));
309 gtk_label_set_line_wrap (GtkLabel *label, gboolean wrap)
311 g_return_if_fail (label != NULL);
312 g_return_if_fail (GTK_IS_LABEL (label));
314 if (label->wrap != wrap) {
315 if (GTK_WIDGET_VISIBLE (label))
317 if (GTK_WIDGET_MAPPED (label))
318 gtk_widget_queue_clear (GTK_WIDGET (label));
320 gtk_widget_queue_resize (GTK_WIDGET (label));
327 gtk_label_get (GtkLabel *label,
330 g_return_if_fail (label != NULL);
331 g_return_if_fail (GTK_IS_LABEL (label));
332 g_return_if_fail (str != NULL);
338 gtk_label_finalize (GtkObject *object)
342 g_return_if_fail (object != NULL);
343 g_return_if_fail (GTK_IS_LABEL (object));
345 label = GTK_LABEL (object);
347 g_free (label->label);
349 g_free (label->pattern);
350 gtk_label_free_words (label);
351 (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
355 gtk_label_word_alloc ()
361 word_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
362 sizeof (GtkLabelWord),
363 32 * sizeof (GtkLabelWord),
370 free_words = word->next;
374 word = g_mem_chunk_alloc (word_chunk);
383 gtk_label_free_words (GtkLabel *label)
389 for (last = label->words; last->next != 0; last = last->next)
390 gtk_label_free_ulines (label->words);
391 last->next = free_words;
392 free_words = label->words;
396 static GtkLabelULine*
397 gtk_label_uline_alloc (void)
399 GtkLabelULine * uline;
403 uline_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
404 sizeof (GtkLabelULine),
405 32 * sizeof (GtkLabelULine),
412 free_ulines = uline->next;
416 uline = g_mem_chunk_alloc (uline_chunk);
425 gtk_label_free_ulines (GtkLabelWord *word)
430 for (last = word->uline; last->next != 0; last = last->next)
432 last->next = free_ulines;
433 free_ulines = word->uline;
439 gtk_label_split_text (GtkLabel *label)
441 GtkLabelWord *word, **tailp;
442 gint space_width, line_width, max_line_width;
445 g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
447 gtk_label_free_words (label);
448 if (label->label == NULL)
451 /* Split text at new-lines. */
452 space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
456 tailp = &label->words;
457 str = label->label_wc;
461 word = gtk_label_word_alloc ();
463 if (str == label->label_wc || str[-1] == '\n')
465 /* Paragraph break */
468 max_line_width = MAX (line_width, max_line_width);
471 else if (str[0] == ' ')
473 while (str[0] == ' ')
476 word->space += space_width;
481 /* Regular inter-word space */
482 word->space = space_width;
485 word->beginning = str;
489 while (*p && *p != '\n')
495 word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
501 line_width += word->space + word->width;
507 /* Add an empty word to represent an empty line
509 if ((str == label->label_wc) || (str[-1] == '\n'))
511 word = gtk_label_word_alloc ();
514 word->beginning = str;
522 return MAX (line_width, max_line_width);
526 gtk_label_split_text_wrapped (GtkLabel *label)
528 /* this needs to handle white space better. */
529 GtkLabelWord *word, **tailp;
530 gint space_width, line_width, max_line_width;
533 g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
535 gtk_label_free_words (label);
536 if (label->label == NULL)
539 /* Split text at new-lines. (Or at spaces in the case of paragraphs). */
540 space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
544 tailp = &label->words;
545 str = label->label_wc;
548 word = gtk_label_word_alloc ();
550 if (str == label->label_wc || str[-1] == '\n')
552 /* Paragraph break */
555 max_line_width = MAX (line_width, max_line_width);
558 else if (str[0] == ' ')
562 while (str[0] == ' ')
568 if (label->jtype == GTK_JUSTIFY_FILL)
569 word->space = (space_width * 3 + 1) / 2;
571 word->space = space_width * nspaces;
575 /* Regular inter-word space */
576 word->space = space_width;
579 word->beginning = str;
582 while (*p && !gdk_iswspace (*p))
587 word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
593 line_width += word->space + word->width;
599 return MAX (line_width, max_line_width);
602 /* gtk_label_pick_width
604 * Split paragraphs, trying to make each line at least min_width,
605 * and trying even harder to make each line no longer than max_width.
607 * Returns the length of the longest resulting line.
609 * (The reason we go to all this effort to pick a paragraph width is to
610 * try to avoid the lame look of a short paragraph with a
614 gtk_label_pick_width (GtkLabel *label,
619 gint width, line_width;
621 g_return_val_if_fail (label->wrap, min_width);
625 for (word = label->words; word; word = word->next)
629 && (line_width >= min_width
630 || line_width + word->width + word->space > max_width)))
633 width = MAX (width, line_width);
636 line_width += word->space + word->width;
639 return MAX (width, line_width);
642 /* Here, we finalize the lines.
643 * This is only for non-wrap labels. Wrapped labels
644 * use gtk_label_finalize_wrap instead.
647 gtk_label_finalize_lines (GtkLabel *label,
651 gint y, baseline_skip, y_max;
655 g_return_if_fail (!label->wrap);
656 ptrn = label->pattern;
659 baseline_skip = GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 2;
661 for (line = label->words; line; line = line->next)
663 if (label->jtype == GTK_JUSTIFY_CENTER)
664 line->x = (line_width - line->width) / 2;
665 else if (label->jtype == GTK_JUSTIFY_RIGHT)
666 line->x = line_width - line->width;
670 line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
673 /* now we deal with the underline stuff; */
674 if (ptrn && ptrn[0] != '\0')
676 for (i = 0; i < line->length; i++)
680 else if (ptrn[i] == '_')
687 GtkLabelULine *uline;
689 for (j = i + 1; j < line->length; j++)
693 else if (ptrn[j] == ' ')
697 /* good. Now we have an underlined segment.
698 * let's measure it and record it.
700 offset = gdk_text_width_wc (GTK_WIDGET (label)->style->font,
703 gdk_text_extents_wc (GTK_WIDGET (label)->style->font,
706 &rbearing, &width, NULL,
708 y_max = MAX (descent + 2, y_max);
709 uline = gtk_label_uline_alloc ();
710 uline->x1 = offset + line->x + lbearing - 1;
711 uline->x2 = offset + line->x + rbearing;
712 uline->y = line->y + descent + 2;
713 uline->next = line->uline;
718 if (strlen (ptrn) > line->length)
719 /* the + 1 is for line breaks. */
720 ptrn += line->length + 1;
724 y += (baseline_skip + y_max);
727 label->max_width = line_width;
728 GTK_WIDGET (label)->requisition.width = line_width + 2 * label->misc.xpad;
729 GTK_WIDGET (label)->requisition.height = y + 2 * label->misc.ypad;
732 /* this finalizes word-wrapped words */
734 gtk_label_finalize_lines_wrap (GtkLabel *label,
737 GtkLabelWord *word, *line, *next_line;
740 gint x, y, space, extra_width, add_space, baseline_skip;
742 g_return_if_fail (label->wrap);
744 ptrn = label->pattern;
746 baseline_skip = GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 1;
748 for (line = label->words; line != 0; line = next_line)
751 extra_width = line_width - line->width;
753 for (next_line = line->next; next_line; next_line = next_line->next)
755 if (next_line->space == 0)
756 break; /* New paragraph */
757 if (next_line->space + next_line->width > extra_width)
759 extra_width -= next_line->space + next_line->width;
760 space += next_line->space;
764 line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
768 for (word = line->next; word != next_line; word = word->next)
770 if (next_line && next_line->space)
772 /* Not last line of paragraph --- fill line if needed */
773 if (label->jtype == GTK_JUSTIFY_FILL) {
774 add_space = (extra_width * word->space + space / 2) / space;
775 extra_width -= add_space;
776 space -= word->space;
780 word->x = x + word->space + add_space;
782 x = word->x + word->width;
785 y += (baseline_skip);
788 label->max_width = line_width;
789 widget = GTK_WIDGET (label);
790 widget->requisition.width = line_width + 2 * label->misc.xpad;
791 widget->requisition.height = y + 2 * label->misc.ypad + 1;
795 gtk_label_size_request (GtkWidget *widget,
796 GtkRequisition *requisition)
800 g_return_if_fail (widget != NULL);
801 g_return_if_fail (GTK_IS_LABEL (widget));
802 g_return_if_fail (requisition != NULL);
804 label = GTK_LABEL (widget);
807 * There are a number of conditions which will necessitate re-filling
811 * 2. justification changed either from to to GTK_JUSTIFY_FILL.
814 * These have been detected elsewhere, and label->words will be zero,
815 * if one of the above has occured.
817 * Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
819 * 4. gtk_widget_set_usize has changed the requested width.
820 * 5. gtk_misc_set_padding has changed xpad.
821 * 6. maybe others?...
823 * Too much of a pain to detect all these case, so always re-fill. I
824 * don't think it's really that slow.
829 GtkWidgetAuxInfo *aux_info;
830 gint longest_paragraph;
832 longest_paragraph = gtk_label_split_text_wrapped (label);
834 aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
835 if (aux_info && aux_info->width > 0)
837 label->max_width = MAX(aux_info->width - 2 * label->misc.xpad, 1);
838 gtk_label_split_text_wrapped (label);
842 label->max_width = gdk_string_width (GTK_WIDGET (label)->style->font,
843 "This is a good enough length for any line to have.");
844 label->max_width = MIN (label->max_width, (gdk_screen_width () + 1) / 2);
845 label->max_width = MIN (label->max_width, longest_paragraph);
846 if (longest_paragraph > 0)
848 gint nlines, perfect_width;
850 nlines = (longest_paragraph + label->max_width - 1) / label->max_width;
851 perfect_width = (longest_paragraph + nlines - 1) / nlines;
852 label->max_width = gtk_label_pick_width (label,
857 gtk_label_finalize_lines_wrap (label, label->max_width);
859 else if (label->words == NULL)
861 label->max_width = gtk_label_split_text (label);
862 gtk_label_finalize_lines (label, label->max_width);
865 if (requisition != &widget->requisition)
866 *requisition = widget->requisition;
870 gtk_label_paint_word (GtkLabel *label,
876 GtkWidget *widget = GTK_WIDGET (label);
877 GtkLabelULine *uline;
880 tmp_str = gdk_wcstombs (word->beginning);
881 gtk_paint_string (widget->style, widget->window, widget->state,
882 area, widget, "label",
888 for (uline = word->uline; uline; uline = uline->next)
889 gtk_paint_hline (widget->style, widget->window,
892 x + uline->x1, x + uline->x2, y + uline->y);
898 gtk_label_expose (GtkWidget *widget,
899 GdkEventExpose *event)
906 g_return_val_if_fail (widget != NULL, FALSE);
907 g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
908 g_return_val_if_fail (event != NULL, FALSE);
910 label = GTK_LABEL (widget);
912 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
913 label->label && (*label->label != '\0'))
915 misc = GTK_MISC (widget);
920 gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
921 gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
923 x = widget->allocation.x + misc->xpad +
924 (widget->allocation.width - label->max_width)
925 * misc->xalign + 0.5;
927 y = (widget->allocation.y
928 + (widget->allocation.height
929 - widget->requisition.height) * misc->yalign
931 for (word = label->words; word; word = word->next)
933 gchar save = word->beginning[word->length];
934 word->beginning[word->length] = '\0';
935 gtk_label_paint_word (label, x, y, word, &event->area);
936 word->beginning[word->length] = save;
939 gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
940 gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
947 gtk_label_parse_uline (GtkLabel *label,
950 guint accel_key = GDK_VoidSymbol;
951 GdkWChar *p, *q, *string_wc;
954 gint length, wc_length;
957 /* Convert text to wide characters */
958 length = strlen (string);
959 string_wc = g_new (GdkWChar, length + 1);
960 wc_length = gdk_mbstowcs (string_wc, string, length + 1);
961 string_wc[wc_length] = '\0';
963 pattern = g_new (gchar, length+1);
979 if (accel_key == GDK_VoidSymbol)
980 accel_key = gdk_keyval_to_lower (*p);
1001 gtk_label_set_text_internal (label, gdk_wcstombs (string_wc), string_wc);
1002 gtk_label_set_pattern (label, pattern);