forked from AuroraMiddleware/gtk
0101a46dcd
Mon Feb 15 19:36:57 1999 Owen Taylor <otaylor@redhat.com> * gtk/gtklabel.c (gtk_label_finalize): Free label->label_wc.
1054 lines
25 KiB
C
1054 lines
25 KiB
C
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include "gtklabel.h"
|
|
#include "gdk/gdkkeysyms.h"
|
|
#include "gdk/gdki18n.h"
|
|
|
|
enum {
|
|
ARG_0,
|
|
ARG_LABEL,
|
|
ARG_PATTERN,
|
|
ARG_JUSTIFY
|
|
};
|
|
|
|
typedef struct _GtkLabelULine GtkLabelULine;
|
|
struct _GtkLabelWord
|
|
{
|
|
GdkWChar *beginning;
|
|
gint length;
|
|
|
|
/* FIXME:
|
|
* We need (space,width) only before we've set (x,y), so to save
|
|
* memory, these pairs should really be wrapped in a union.
|
|
* I haven't yet figured out how to do this without making the code
|
|
* look ugly.
|
|
*/
|
|
gint space;
|
|
gint width;
|
|
gint x;
|
|
gint y;
|
|
GtkLabelWord *next;
|
|
gint uline_y;
|
|
GtkLabelULine *uline;
|
|
};
|
|
|
|
struct _GtkLabelULine
|
|
{
|
|
gint x1;
|
|
gint x2;
|
|
gint y;
|
|
GtkLabelULine *next;
|
|
};
|
|
|
|
static void gtk_label_class_init (GtkLabelClass *klass);
|
|
static void gtk_label_init (GtkLabel *label);
|
|
static void gtk_label_set_arg (GtkObject *object,
|
|
GtkArg *arg,
|
|
guint arg_id);
|
|
static void gtk_label_get_arg (GtkObject *object,
|
|
GtkArg *arg,
|
|
guint arg_id);
|
|
static void gtk_label_finalize (GtkObject *object);
|
|
static void gtk_label_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition);
|
|
static void gtk_label_style_set (GtkWidget *widget,
|
|
GtkStyle *previous_style);
|
|
static gint gtk_label_expose (GtkWidget *widget,
|
|
GdkEventExpose *event);
|
|
|
|
static GtkLabelWord * gtk_label_word_alloc (void);
|
|
static GtkLabelULine * gtk_label_uline_alloc (void);
|
|
static void gtk_label_free_words (GtkLabel *label);
|
|
static void gtk_label_free_ulines (GtkLabelWord *word);
|
|
static gint gtk_label_split_text (GtkLabel * label);
|
|
static void gtk_label_finalize_lines (GtkLabel * label, gint line_width);
|
|
static void gtk_label_finalize_lines_wrap(GtkLabel * label, gint line_width);
|
|
|
|
|
|
static GtkMiscClass *parent_class = NULL;
|
|
|
|
static GMemChunk *word_chunk = 0;
|
|
static GtkLabelWord *free_words = 0;
|
|
static GMemChunk *uline_chunk = 0;
|
|
static GtkLabelULine *free_ulines = 0;
|
|
|
|
GtkType
|
|
gtk_label_get_type (void)
|
|
{
|
|
static GtkType label_type = 0;
|
|
|
|
if (!label_type)
|
|
{
|
|
static const GtkTypeInfo label_info =
|
|
{
|
|
"GtkLabel",
|
|
sizeof (GtkLabel),
|
|
sizeof (GtkLabelClass),
|
|
(GtkClassInitFunc) gtk_label_class_init,
|
|
(GtkObjectInitFunc) gtk_label_init,
|
|
/* reserved_1 */ NULL,
|
|
/* reserved_2 */ NULL,
|
|
(GtkClassInitFunc) NULL,
|
|
};
|
|
|
|
label_type = gtk_type_unique (gtk_misc_get_type (), &label_info);
|
|
gtk_type_set_chunk_alloc (label_type, 32);
|
|
}
|
|
|
|
return label_type;
|
|
}
|
|
|
|
static void
|
|
gtk_label_class_init (GtkLabelClass *class)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
|
|
object_class = (GtkObjectClass*) class;
|
|
widget_class = (GtkWidgetClass*) class;
|
|
|
|
parent_class = gtk_type_class (gtk_misc_get_type ());
|
|
|
|
gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
|
|
gtk_object_add_arg_type ("GtkLabel::pattern", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_PATTERN);
|
|
gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
|
|
|
|
object_class->set_arg = gtk_label_set_arg;
|
|
object_class->get_arg = gtk_label_get_arg;
|
|
object_class->finalize = gtk_label_finalize;
|
|
|
|
widget_class->size_request = gtk_label_size_request;
|
|
widget_class->style_set = gtk_label_style_set;
|
|
widget_class->expose_event = gtk_label_expose;
|
|
}
|
|
|
|
static void
|
|
gtk_label_set_arg (GtkObject *object,
|
|
GtkArg *arg,
|
|
guint arg_id)
|
|
{
|
|
GtkLabel *label;
|
|
|
|
label = GTK_LABEL (object);
|
|
|
|
switch (arg_id)
|
|
{
|
|
case ARG_LABEL:
|
|
gtk_label_set_text (label, GTK_VALUE_STRING (*arg) ? GTK_VALUE_STRING (*arg) : "");
|
|
break;
|
|
case ARG_PATTERN:
|
|
gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
|
|
break;
|
|
case ARG_JUSTIFY:
|
|
gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_label_get_arg (GtkObject *object,
|
|
GtkArg *arg,
|
|
guint arg_id)
|
|
{
|
|
GtkLabel *label;
|
|
|
|
label = GTK_LABEL (object);
|
|
|
|
switch (arg_id)
|
|
{
|
|
case ARG_LABEL:
|
|
GTK_VALUE_STRING (*arg) = g_strdup (label->label);
|
|
break;
|
|
case ARG_PATTERN:
|
|
GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
|
|
break;
|
|
case ARG_JUSTIFY:
|
|
GTK_VALUE_ENUM (*arg) = label->jtype;
|
|
break;
|
|
default:
|
|
arg->type = GTK_TYPE_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_label_init (GtkLabel *label)
|
|
{
|
|
GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
|
|
|
|
label->label = NULL;
|
|
label->label_wc = NULL;
|
|
label->pattern = NULL;
|
|
|
|
label->words = NULL;
|
|
|
|
label->max_width = 0;
|
|
label->jtype = GTK_JUSTIFY_CENTER;
|
|
label->wrap = FALSE;
|
|
|
|
gtk_label_set_text (label, "");
|
|
}
|
|
|
|
GtkWidget*
|
|
gtk_label_new (const char *str)
|
|
{
|
|
GtkLabel *label;
|
|
|
|
g_return_val_if_fail (str != NULL, NULL);
|
|
|
|
label = gtk_type_new (gtk_label_get_type ());
|
|
|
|
gtk_label_set_text (label, str);
|
|
|
|
return GTK_WIDGET (label);
|
|
}
|
|
|
|
static void
|
|
gtk_label_set_text_internal (GtkLabel *label,
|
|
char *str,
|
|
GdkWChar *str_wc)
|
|
{
|
|
if (label->label)
|
|
g_free (label->label);
|
|
if (label->label_wc)
|
|
g_free (label->label_wc);
|
|
|
|
label->label = str;
|
|
label->label_wc = str_wc;
|
|
|
|
gtk_label_free_words (label);
|
|
|
|
if (GTK_WIDGET_VISIBLE (label))
|
|
{
|
|
if (GTK_WIDGET_MAPPED (label))
|
|
gtk_widget_queue_clear (GTK_WIDGET (label));
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (label));
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_label_set_text (GtkLabel *label,
|
|
const char *str)
|
|
{
|
|
GdkWChar *str_wc;
|
|
gint len;
|
|
gint wc_len;
|
|
|
|
g_return_if_fail (label != NULL);
|
|
g_return_if_fail (GTK_IS_LABEL (label));
|
|
g_return_if_fail (str != NULL);
|
|
|
|
if (!label->label || strcmp (label->label, str))
|
|
{
|
|
/* Convert text to wide characters */
|
|
len = strlen (str);
|
|
str_wc = g_new (GdkWChar, len + 1);
|
|
wc_len = gdk_mbstowcs (str_wc, str, len + 1);
|
|
if (wc_len >= 0)
|
|
{
|
|
str_wc[wc_len] = '\0';
|
|
gtk_label_set_text_internal (label, g_strdup (str), str_wc);
|
|
}
|
|
else
|
|
g_free (str_wc);
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_label_set_pattern (GtkLabel *label,
|
|
const gchar *pattern)
|
|
{
|
|
g_return_if_fail (label != NULL);
|
|
g_return_if_fail (GTK_IS_LABEL (label));
|
|
|
|
if (label->pattern)
|
|
g_free (label->pattern);
|
|
label->pattern = g_strdup (pattern);
|
|
|
|
if (GTK_WIDGET_VISIBLE (label))
|
|
{
|
|
if (GTK_WIDGET_MAPPED (label))
|
|
gtk_widget_queue_clear (GTK_WIDGET (label));
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (label));
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_label_set_justify (GtkLabel *label, GtkJustification jtype)
|
|
{
|
|
g_return_if_fail (label != NULL);
|
|
g_return_if_fail (GTK_IS_LABEL (label));
|
|
|
|
if ((GtkJustification) label->jtype != jtype)
|
|
{
|
|
if ((label->jtype == GTK_JUSTIFY_FILL) ||
|
|
(jtype == GTK_JUSTIFY_FILL))
|
|
/* FIXME: think about this a little */
|
|
gtk_label_free_words (label);
|
|
|
|
label->jtype = jtype;
|
|
|
|
if (GTK_WIDGET_VISIBLE (label))
|
|
{
|
|
if (GTK_WIDGET_MAPPED (label))
|
|
gtk_widget_queue_clear (GTK_WIDGET (label));
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (label));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_label_set_line_wrap (GtkLabel *label, gboolean wrap)
|
|
{
|
|
g_return_if_fail (label != NULL);
|
|
g_return_if_fail (GTK_IS_LABEL (label));
|
|
|
|
if (label->wrap != wrap) {
|
|
if (GTK_WIDGET_VISIBLE (label))
|
|
{
|
|
if (GTK_WIDGET_MAPPED (label))
|
|
gtk_widget_queue_clear (GTK_WIDGET (label));
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (label));
|
|
}
|
|
label->wrap = wrap;
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_label_get (GtkLabel *label,
|
|
char **str)
|
|
{
|
|
g_return_if_fail (label != NULL);
|
|
g_return_if_fail (GTK_IS_LABEL (label));
|
|
g_return_if_fail (str != NULL);
|
|
|
|
*str = label->label;
|
|
}
|
|
|
|
static void
|
|
gtk_label_finalize (GtkObject *object)
|
|
{
|
|
GtkLabel *label;
|
|
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (GTK_IS_LABEL (object));
|
|
|
|
label = GTK_LABEL (object);
|
|
|
|
g_free (label->label);
|
|
g_free (label->label_wc);
|
|
|
|
if (label->pattern)
|
|
g_free (label->pattern);
|
|
gtk_label_free_words (label);
|
|
(* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
|
|
}
|
|
|
|
static GtkLabelWord*
|
|
gtk_label_word_alloc ()
|
|
{
|
|
GtkLabelWord * word;
|
|
|
|
if (!word_chunk)
|
|
{
|
|
word_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
|
|
sizeof (GtkLabelWord),
|
|
32 * sizeof (GtkLabelWord),
|
|
G_ALLOC_ONLY);
|
|
}
|
|
|
|
if (free_words)
|
|
{
|
|
word = free_words;
|
|
free_words = word->next;
|
|
}
|
|
else
|
|
{
|
|
word = g_mem_chunk_alloc (word_chunk);
|
|
}
|
|
|
|
word->next = 0;
|
|
word->uline = 0;
|
|
return word;
|
|
}
|
|
|
|
static void
|
|
gtk_label_free_words (GtkLabel *label)
|
|
{
|
|
GtkLabelWord * last;
|
|
|
|
if (label->words)
|
|
{
|
|
for (last = label->words; last->next != 0; last = last->next)
|
|
gtk_label_free_ulines (label->words);
|
|
last->next = free_words;
|
|
free_words = label->words;
|
|
label->words = NULL;
|
|
}
|
|
}
|
|
static GtkLabelULine*
|
|
gtk_label_uline_alloc (void)
|
|
{
|
|
GtkLabelULine * uline;
|
|
|
|
if (!uline_chunk)
|
|
{
|
|
uline_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
|
|
sizeof (GtkLabelULine),
|
|
32 * sizeof (GtkLabelULine),
|
|
G_ALLOC_ONLY);
|
|
}
|
|
|
|
if (free_ulines)
|
|
{
|
|
uline = free_ulines;
|
|
free_ulines = uline->next;
|
|
}
|
|
else
|
|
{
|
|
uline = g_mem_chunk_alloc (uline_chunk);
|
|
}
|
|
|
|
uline->next = NULL;
|
|
|
|
return uline;
|
|
}
|
|
|
|
static void
|
|
gtk_label_free_ulines (GtkLabelWord *word)
|
|
{
|
|
GtkLabelULine *last;
|
|
if (word->uline)
|
|
{
|
|
for (last = word->uline; last->next != 0; last = last->next)
|
|
;
|
|
last->next = free_ulines;
|
|
free_ulines = word->uline;
|
|
word->uline = 0;
|
|
}
|
|
}
|
|
|
|
static gint
|
|
gtk_label_split_text (GtkLabel *label)
|
|
{
|
|
GtkLabelWord *word, **tailp;
|
|
gint space_width, line_width, max_line_width;
|
|
GdkWChar *str, *p;
|
|
|
|
g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
|
|
|
|
gtk_label_free_words (label);
|
|
if (label->label == NULL)
|
|
return 0;
|
|
|
|
/* Split text at new-lines. */
|
|
space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
|
|
|
|
line_width = 0;
|
|
max_line_width = 0;
|
|
tailp = &label->words;
|
|
str = label->label_wc;
|
|
|
|
while (*str)
|
|
{
|
|
word = gtk_label_word_alloc ();
|
|
|
|
if (str == label->label_wc || str[-1] == '\n')
|
|
{
|
|
/* Paragraph break */
|
|
word->space = 0;
|
|
|
|
max_line_width = MAX (line_width, max_line_width);
|
|
line_width = 0;
|
|
}
|
|
else if (str[0] == ' ')
|
|
{
|
|
while (str[0] == ' ')
|
|
{
|
|
str++;
|
|
word->space += space_width;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Regular inter-word space */
|
|
word->space = space_width;
|
|
}
|
|
|
|
word->beginning = str;
|
|
|
|
word->length = 0;
|
|
p = word->beginning;
|
|
while (*p && *p != '\n')
|
|
{
|
|
word->length++;
|
|
p++;
|
|
}
|
|
|
|
word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
|
|
|
|
str += word->length;
|
|
if (*str)
|
|
str++;
|
|
|
|
line_width += word->space + word->width;
|
|
|
|
*tailp = word;
|
|
tailp = &word->next;
|
|
}
|
|
|
|
/* Add an empty word to represent an empty line
|
|
*/
|
|
if ((str == label->label_wc) || (str[-1] == '\n'))
|
|
{
|
|
word = gtk_label_word_alloc ();
|
|
|
|
word->space = 0;
|
|
word->beginning = str;
|
|
word->length = 0;
|
|
word->width = 0;
|
|
|
|
*tailp = word;
|
|
tailp = &word->next;
|
|
}
|
|
|
|
return MAX (line_width, max_line_width);
|
|
}
|
|
|
|
static gint
|
|
gtk_label_split_text_wrapped (GtkLabel *label)
|
|
{
|
|
/* this needs to handle white space better. */
|
|
GtkLabelWord *word, **tailp;
|
|
gint space_width, line_width, max_line_width;
|
|
GdkWChar *str, *p;
|
|
|
|
g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
|
|
|
|
gtk_label_free_words (label);
|
|
if (label->label == NULL)
|
|
return 0;
|
|
|
|
/* Split text at new-lines. (Or at spaces in the case of paragraphs). */
|
|
space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
|
|
|
|
line_width = 0;
|
|
max_line_width = 0;
|
|
tailp = &label->words;
|
|
str = label->label_wc;
|
|
while (*str)
|
|
{
|
|
word = gtk_label_word_alloc ();
|
|
|
|
if (str == label->label_wc || str[-1] == '\n')
|
|
{
|
|
/* Paragraph break */
|
|
word->space = 0;
|
|
|
|
max_line_width = MAX (line_width, max_line_width);
|
|
line_width = 0;
|
|
}
|
|
else if (str[0] == ' ')
|
|
{
|
|
gint nspaces = 0;
|
|
|
|
while (str[0] == ' ')
|
|
{
|
|
nspaces++;
|
|
str++;
|
|
}
|
|
|
|
if (label->jtype == GTK_JUSTIFY_FILL)
|
|
word->space = (space_width * 3 + 1) / 2;
|
|
else
|
|
word->space = space_width * nspaces;
|
|
}
|
|
else
|
|
{
|
|
/* Regular inter-word space */
|
|
word->space = space_width;
|
|
}
|
|
|
|
word->beginning = str;
|
|
word->length = 0;
|
|
p = word->beginning;
|
|
while (*p && !gdk_iswspace (*p))
|
|
{
|
|
word->length++;
|
|
p++;
|
|
}
|
|
word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
|
|
|
|
str += word->length;
|
|
if (*str)
|
|
str++;
|
|
|
|
line_width += word->space + word->width;
|
|
|
|
*tailp = word;
|
|
tailp = &word->next;
|
|
}
|
|
|
|
return MAX (line_width, max_line_width);
|
|
}
|
|
|
|
/* gtk_label_pick_width
|
|
*
|
|
* Split paragraphs, trying to make each line at least min_width,
|
|
* and trying even harder to make each line no longer than max_width.
|
|
*
|
|
* Returns the length of the longest resulting line.
|
|
*
|
|
* (The reason we go to all this effort to pick a paragraph width is to
|
|
* try to avoid the lame look of a short paragraph with a
|
|
* short final line.)
|
|
*/
|
|
static gint
|
|
gtk_label_pick_width (GtkLabel *label,
|
|
gint min_width,
|
|
gint max_width)
|
|
{
|
|
GtkLabelWord *word;
|
|
gint width, line_width;
|
|
|
|
g_return_val_if_fail (label->wrap, min_width);
|
|
|
|
line_width = 0;
|
|
width = 0;
|
|
for (word = label->words; word; word = word->next)
|
|
{
|
|
if (word->space == 0
|
|
|| (line_width
|
|
&& (line_width >= min_width
|
|
|| line_width + word->width + word->space > max_width)))
|
|
{
|
|
/* New line */
|
|
width = MAX (width, line_width);
|
|
line_width = 0;
|
|
}
|
|
line_width += word->space + word->width;
|
|
}
|
|
|
|
return MAX (width, line_width);
|
|
}
|
|
|
|
/* Here, we finalize the lines.
|
|
* This is only for non-wrap labels. Wrapped labels
|
|
* use gtk_label_finalize_wrap instead.
|
|
*/
|
|
static void
|
|
gtk_label_finalize_lines (GtkLabel *label,
|
|
gint line_width)
|
|
{
|
|
GtkLabelWord *line;
|
|
gint y, baseline_skip, y_max;
|
|
gint i, j;
|
|
gchar *ptrn;
|
|
|
|
g_return_if_fail (!label->wrap);
|
|
ptrn = label->pattern;
|
|
|
|
y = 0;
|
|
baseline_skip = GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 2;
|
|
|
|
for (line = label->words; line; line = line->next)
|
|
{
|
|
if (label->jtype == GTK_JUSTIFY_CENTER)
|
|
line->x = (line_width - line->width) / 2;
|
|
else if (label->jtype == GTK_JUSTIFY_RIGHT)
|
|
line->x = line_width - line->width;
|
|
else
|
|
line->x = 0;
|
|
|
|
line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
|
|
y_max = 0;
|
|
|
|
/* now we deal with the underline stuff; */
|
|
if (ptrn && ptrn[0] != '\0')
|
|
{
|
|
for (i = 0; i < line->length; i++)
|
|
{
|
|
if (ptrn[i] == '\0')
|
|
break;
|
|
else if (ptrn[i] == '_')
|
|
{
|
|
gint descent;
|
|
gint rbearing;
|
|
gint lbearing;
|
|
gint width;
|
|
gint offset;
|
|
GtkLabelULine *uline;
|
|
|
|
for (j = i + 1; j < line->length; j++)
|
|
{
|
|
if (ptrn[j] == '\0')
|
|
break;
|
|
else if (ptrn[j] == ' ')
|
|
break;
|
|
}
|
|
|
|
/* good. Now we have an underlined segment.
|
|
* let's measure it and record it.
|
|
*/
|
|
offset = gdk_text_width_wc (GTK_WIDGET (label)->style->font,
|
|
line->beginning,
|
|
i);
|
|
gdk_text_extents_wc (GTK_WIDGET (label)->style->font,
|
|
line->beginning+i,
|
|
j-i, &lbearing,
|
|
&rbearing, &width, NULL,
|
|
&descent);
|
|
y_max = MAX (descent + 2, y_max);
|
|
uline = gtk_label_uline_alloc ();
|
|
uline->x1 = offset + line->x + lbearing - 1;
|
|
uline->x2 = offset + line->x + rbearing;
|
|
uline->y = line->y + descent + 2;
|
|
uline->next = line->uline;
|
|
line->uline = uline;
|
|
i = j - 1;
|
|
}
|
|
}
|
|
if (strlen (ptrn) > line->length)
|
|
/* the + 1 is for line breaks. */
|
|
ptrn += line->length + 1;
|
|
else
|
|
ptrn = NULL;
|
|
}
|
|
y += (baseline_skip + y_max);
|
|
}
|
|
|
|
label->max_width = line_width;
|
|
GTK_WIDGET (label)->requisition.width = line_width + 2 * label->misc.xpad;
|
|
GTK_WIDGET (label)->requisition.height = y + 2 * label->misc.ypad;
|
|
}
|
|
|
|
/* this finalizes word-wrapped words */
|
|
static void
|
|
gtk_label_finalize_lines_wrap (GtkLabel *label,
|
|
gint line_width)
|
|
{
|
|
GtkLabelWord *word, *line, *next_line;
|
|
GtkWidget *widget;
|
|
gchar *ptrn;
|
|
gint x, y, space, extra_width, add_space, baseline_skip;
|
|
|
|
g_return_if_fail (label->wrap);
|
|
|
|
ptrn = label->pattern;
|
|
y = 0;
|
|
baseline_skip = GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 1;
|
|
|
|
for (line = label->words; line != 0; line = next_line)
|
|
{
|
|
space = 0;
|
|
extra_width = line_width - line->width;
|
|
|
|
for (next_line = line->next; next_line; next_line = next_line->next)
|
|
{
|
|
if (next_line->space == 0)
|
|
break; /* New paragraph */
|
|
if (next_line->space + next_line->width > extra_width)
|
|
break;
|
|
extra_width -= next_line->space + next_line->width;
|
|
space += next_line->space;
|
|
}
|
|
|
|
line->x = 0;
|
|
line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
|
|
x = line->width;
|
|
add_space = 0;
|
|
|
|
for (word = line->next; word != next_line; word = word->next)
|
|
{
|
|
if (next_line && next_line->space)
|
|
{
|
|
/* Not last line of paragraph --- fill line if needed */
|
|
if (label->jtype == GTK_JUSTIFY_FILL) {
|
|
add_space = (extra_width * word->space + space / 2) / space;
|
|
extra_width -= add_space;
|
|
space -= word->space;
|
|
}
|
|
}
|
|
|
|
word->x = x + word->space + add_space;
|
|
word->y = line->y;
|
|
x = word->x + word->width;
|
|
}
|
|
|
|
y += (baseline_skip);
|
|
}
|
|
|
|
label->max_width = line_width;
|
|
widget = GTK_WIDGET (label);
|
|
widget->requisition.width = line_width + 2 * label->misc.xpad;
|
|
widget->requisition.height = y + 2 * label->misc.ypad + 1;
|
|
}
|
|
|
|
static void
|
|
gtk_label_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition)
|
|
{
|
|
GtkLabel *label;
|
|
|
|
g_return_if_fail (widget != NULL);
|
|
g_return_if_fail (GTK_IS_LABEL (widget));
|
|
g_return_if_fail (requisition != NULL);
|
|
|
|
label = GTK_LABEL (widget);
|
|
|
|
/*
|
|
* There are a number of conditions which will necessitate re-filling
|
|
* our text:
|
|
*
|
|
* 1. text changed.
|
|
* 2. justification changed either from to to GTK_JUSTIFY_FILL.
|
|
* 3. font changed.
|
|
*
|
|
* These have been detected elsewhere, and label->words will be zero,
|
|
* if one of the above has occured.
|
|
*
|
|
* Additionally, though, if GTK_JUSTIFY_FILL, we need to re-fill if:
|
|
*
|
|
* 4. gtk_widget_set_usize has changed the requested width.
|
|
* 5. gtk_misc_set_padding has changed xpad.
|
|
* 6. maybe others?...
|
|
*
|
|
* Too much of a pain to detect all these case, so always re-fill. I
|
|
* don't think it's really that slow.
|
|
*/
|
|
|
|
if (label->wrap)
|
|
{
|
|
GtkWidgetAuxInfo *aux_info;
|
|
gint longest_paragraph;
|
|
|
|
longest_paragraph = gtk_label_split_text_wrapped (label);
|
|
|
|
aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
|
|
if (aux_info && aux_info->width > 0)
|
|
{
|
|
label->max_width = MAX(aux_info->width - 2 * label->misc.xpad, 1);
|
|
gtk_label_split_text_wrapped (label);
|
|
}
|
|
else
|
|
{
|
|
label->max_width = gdk_string_width (GTK_WIDGET (label)->style->font,
|
|
"This is a good enough length for any line to have.");
|
|
label->max_width = MIN (label->max_width, (gdk_screen_width () + 1) / 2);
|
|
label->max_width = MIN (label->max_width, longest_paragraph);
|
|
if (longest_paragraph > 0)
|
|
{
|
|
gint nlines, perfect_width;
|
|
|
|
nlines = (longest_paragraph + label->max_width - 1) / label->max_width;
|
|
perfect_width = (longest_paragraph + nlines - 1) / nlines;
|
|
label->max_width = gtk_label_pick_width (label,
|
|
perfect_width,
|
|
label->max_width);
|
|
}
|
|
}
|
|
gtk_label_finalize_lines_wrap (label, label->max_width);
|
|
}
|
|
else if (label->words == NULL)
|
|
{
|
|
label->max_width = gtk_label_split_text (label);
|
|
gtk_label_finalize_lines (label, label->max_width);
|
|
}
|
|
|
|
if (requisition != &widget->requisition)
|
|
*requisition = widget->requisition;
|
|
}
|
|
|
|
static void
|
|
gtk_label_style_set (GtkWidget *widget,
|
|
GtkStyle *previous_style)
|
|
{
|
|
GtkLabel *label;
|
|
|
|
g_return_if_fail (widget != NULL);
|
|
g_return_if_fail (GTK_IS_LABEL (widget));
|
|
|
|
label = GTK_LABEL (widget);
|
|
|
|
if (previous_style && label->words)
|
|
/* Clear the list of words so that they are recomputed on
|
|
* size_request
|
|
*/
|
|
gtk_label_free_words (label);
|
|
}
|
|
|
|
static void
|
|
gtk_label_paint_word (GtkLabel *label,
|
|
gint x,
|
|
gint y,
|
|
GtkLabelWord *word,
|
|
GdkRectangle *area)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (label);
|
|
GtkLabelULine *uline;
|
|
gchar *tmp_str;
|
|
|
|
tmp_str = gdk_wcstombs (word->beginning);
|
|
if (tmp_str)
|
|
{
|
|
gtk_paint_string (widget->style, widget->window, widget->state,
|
|
area, widget, "label",
|
|
x + word->x,
|
|
y + word->y,
|
|
tmp_str);
|
|
g_free (tmp_str);
|
|
}
|
|
|
|
for (uline = word->uline; uline; uline = uline->next)
|
|
gtk_paint_hline (widget->style, widget->window,
|
|
widget->state, area,
|
|
widget, "label",
|
|
x + uline->x1, x + uline->x2, y + uline->y);
|
|
}
|
|
|
|
static gint
|
|
gtk_label_expose (GtkWidget *widget,
|
|
GdkEventExpose *event)
|
|
{
|
|
GtkLabel *label;
|
|
GtkMisc *misc;
|
|
GtkLabelWord *word;
|
|
gint x, y;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
label = GTK_LABEL (widget);
|
|
|
|
if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
|
|
label->label && (*label->label != '\0'))
|
|
{
|
|
misc = GTK_MISC (widget);
|
|
|
|
/*
|
|
* GC Clipping
|
|
*/
|
|
gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
|
|
gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
|
|
|
|
x = floor (widget->allocation.x + (gint)misc->xpad
|
|
+ (((gint)widget->allocation.width -
|
|
(gint)label->max_width - 2 * (gint)misc->xpad)
|
|
* misc->xalign) + 0.5);
|
|
|
|
y = floor (widget->allocation.y + (gint)misc->ypad
|
|
+ (((gint)widget->allocation.height
|
|
- (gint)widget->requisition.height)
|
|
* misc->yalign) + 0.5);
|
|
|
|
for (word = label->words; word; word = word->next)
|
|
{
|
|
gchar save = word->beginning[word->length];
|
|
word->beginning[word->length] = '\0';
|
|
gtk_label_paint_word (label, x, y, word, &event->area);
|
|
word->beginning[word->length] = save;
|
|
}
|
|
|
|
gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
|
|
gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
guint
|
|
gtk_label_parse_uline (GtkLabel *label,
|
|
const gchar *string)
|
|
{
|
|
guint accel_key = GDK_VoidSymbol;
|
|
GdkWChar *p, *q, *string_wc;
|
|
gchar *r;
|
|
gchar *pattern;
|
|
gint length, wc_length;
|
|
gboolean underscore;
|
|
|
|
g_return_val_if_fail(string != NULL, GDK_VoidSymbol);
|
|
|
|
/* Convert text to wide characters */
|
|
length = strlen (string);
|
|
string_wc = g_new (GdkWChar, length + 1);
|
|
wc_length = gdk_mbstowcs (string_wc, string, length + 1);
|
|
if (wc_length < 0)
|
|
{
|
|
g_free (string_wc);
|
|
return GDK_VoidSymbol;
|
|
}
|
|
|
|
string_wc[wc_length] = '\0';
|
|
|
|
pattern = g_new (gchar, length+1);
|
|
|
|
underscore = FALSE;
|
|
|
|
p = q = string_wc;
|
|
r = pattern;
|
|
|
|
while (*p)
|
|
{
|
|
if (underscore)
|
|
{
|
|
if (*p == '_')
|
|
*r++ = ' ';
|
|
else
|
|
{
|
|
*r++ = '_';
|
|
if (accel_key == GDK_VoidSymbol)
|
|
accel_key = gdk_keyval_to_lower (*p);
|
|
}
|
|
|
|
*q++ = *p;
|
|
underscore = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (*p == '_')
|
|
underscore = TRUE;
|
|
else
|
|
{
|
|
*q++ = *p;
|
|
*r++ = ' ';
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
*q = 0;
|
|
*r = 0;
|
|
|
|
gtk_label_set_text_internal (label, gdk_wcstombs (string_wc), string_wc);
|
|
gtk_label_set_pattern (label, pattern);
|
|
|
|
g_free (pattern);
|
|
|
|
return accel_key;
|
|
}
|
|
|