gtklabel.c 24.8 KB
Newer Older
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4 5 6 7 8 9 10
 * 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
Owen Taylor's avatar
Owen Taylor committed
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Elliot Lee's avatar
Elliot Lee committed
12 13 14
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Owen Taylor's avatar
Owen Taylor committed
15 16
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18 19 20 21 22 23 24 25

/*
 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

26
#include <math.h>
Elliot Lee's avatar
Elliot Lee committed
27 28
#include <string.h>
#include "gtklabel.h"
29
#include "gdk/gdkkeysyms.h"
Owen Taylor's avatar
Owen Taylor committed
30
#include "gdk/gdki18n.h"
Elliot Lee's avatar
Elliot Lee committed
31

32 33 34
enum {
  ARG_0,
  ARG_LABEL,
35
  ARG_PATTERN,
36 37 38
  ARG_JUSTIFY
};

Owen Taylor's avatar
Owen Taylor committed
39 40 41 42 43
typedef struct _GtkLabelULine GtkLabelULine;
struct _GtkLabelWord
{
  GdkWChar *beginning;
  gint length;
44
  
Owen Taylor's avatar
Owen Taylor committed
45 46 47 48 49 50 51
  /* 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;
52
  gint width;
Owen Taylor's avatar
Owen Taylor committed
53 54 55 56 57
  gint x;
  gint y;
  GtkLabelWord *next;
  gint uline_y;
  GtkLabelULine *uline;
58 59
};

Owen Taylor's avatar
Owen Taylor committed
60 61 62 63 64 65 66
struct _GtkLabelULine
{
  gint x1;
  gint x2;
  gint y;
  GtkLabelULine *next;
};
67

Elliot Lee's avatar
Elliot Lee committed
68
static void gtk_label_class_init   (GtkLabelClass  *klass);
Tim Janik's avatar
Tim Janik committed
69
static void gtk_label_init	   (GtkLabel	   *label);
70
static void gtk_label_set_arg	   (GtkObject	   *object,
Tim Janik's avatar
Tim Janik committed
71 72
				    GtkArg	   *arg,
				    guint	    arg_id);
73
static void gtk_label_get_arg	   (GtkObject      *object,
Tim Janik's avatar
Tim Janik committed
74 75 76 77
				    GtkArg	   *arg,
				    guint	    arg_id);
static void gtk_label_finalize	   (GtkObject	   *object);
static void gtk_label_size_request (GtkWidget	   *widget,
Elliot Lee's avatar
Elliot Lee committed
78
				    GtkRequisition *requisition);
79 80
static void gtk_label_style_set    (GtkWidget      *widget,
				    GtkStyle       *previous_style);
Tim Janik's avatar
Tim Janik committed
81
static gint gtk_label_expose	   (GtkWidget	   *widget,
Elliot Lee's avatar
Elliot Lee committed
82
				    GdkEventExpose *event);
Owen Taylor's avatar
Owen Taylor committed
83 84 85 86 87 88 89 90

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);
Elliot Lee's avatar
Elliot Lee committed
91 92 93 94


static GtkMiscClass *parent_class = NULL;

Owen Taylor's avatar
Owen Taylor committed
95 96 97 98
static GMemChunk *word_chunk = 0;
static GtkLabelWord *free_words = 0;
static GMemChunk *uline_chunk = 0;
static GtkLabelULine *free_ulines = 0;
Elliot Lee's avatar
Elliot Lee committed
99

Tim Janik's avatar
Tim Janik committed
100
GtkType
101
gtk_label_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
102
{
Tim Janik's avatar
Tim Janik committed
103 104
  static GtkType label_type = 0;
  
Elliot Lee's avatar
Elliot Lee committed
105 106
  if (!label_type)
    {
107
      static const GtkTypeInfo label_info =
Elliot Lee's avatar
Elliot Lee committed
108 109 110 111 112 113
      {
	"GtkLabel",
	sizeof (GtkLabel),
	sizeof (GtkLabelClass),
	(GtkClassInitFunc) gtk_label_class_init,
	(GtkObjectInitFunc) gtk_label_init,
114 115
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
116
	(GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
117
      };
Tim Janik's avatar
Tim Janik committed
118
      
Elliot Lee's avatar
Elliot Lee committed
119
      label_type = gtk_type_unique (gtk_misc_get_type (), &label_info);
120
      gtk_type_set_chunk_alloc (label_type, 32);
Elliot Lee's avatar
Elliot Lee committed
121
    }
Tim Janik's avatar
Tim Janik committed
122
  
Elliot Lee's avatar
Elliot Lee committed
123 124 125
  return label_type;
}

126
static void
Elliot Lee's avatar
Elliot Lee committed
127 128 129 130
gtk_label_class_init (GtkLabelClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
Tim Janik's avatar
Tim Janik committed
131
  
Elliot Lee's avatar
Elliot Lee committed
132 133
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
Tim Janik's avatar
Tim Janik committed
134
  
Elliot Lee's avatar
Elliot Lee committed
135
  parent_class = gtk_type_class (gtk_misc_get_type ());
Tim Janik's avatar
Tim Janik committed
136
  
137
  gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
138
  gtk_object_add_arg_type ("GtkLabel::pattern", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_PATTERN);
139
  gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
140
  
141 142
  object_class->set_arg = gtk_label_set_arg;
  object_class->get_arg = gtk_label_get_arg;
143
  object_class->finalize = gtk_label_finalize;
Tim Janik's avatar
Tim Janik committed
144
  
Elliot Lee's avatar
Elliot Lee committed
145
  widget_class->size_request = gtk_label_size_request;
146
  widget_class->style_set = gtk_label_style_set;
Elliot Lee's avatar
Elliot Lee committed
147 148 149
  widget_class->expose_event = gtk_label_expose;
}

150
static void
151
gtk_label_set_arg (GtkObject	  *object,
Tim Janik's avatar
Tim Janik committed
152 153
		   GtkArg	  *arg,
		   guint	   arg_id)
154
{
155
  GtkLabel *label;
156
  
157
  label = GTK_LABEL (object);
158
  
159 160 161
  switch (arg_id)
    {
    case ARG_LABEL:
Owen Taylor's avatar
Owen Taylor committed
162
      gtk_label_set_text (label, GTK_VALUE_STRING (*arg) ? GTK_VALUE_STRING (*arg) : "");
163
      break;
164 165 166
    case ARG_PATTERN:
      gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
      break;
167 168 169
    case ARG_JUSTIFY:
      gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
      break;
Tim Janik's avatar
Tim Janik committed
170 171
    default:
      break;
172 173 174
    }
}

Tim Janik's avatar
Tim Janik committed
175
static void
176
gtk_label_get_arg (GtkObject	  *object,
Tim Janik's avatar
Tim Janik committed
177 178
		   GtkArg	  *arg,
		   guint	   arg_id)
179
{
180
  GtkLabel *label;
181
  
182
  label = GTK_LABEL (object);
183
  
184 185 186 187 188
  switch (arg_id)
    {
    case ARG_LABEL:
      GTK_VALUE_STRING (*arg) = g_strdup (label->label);
      break;
189 190 191
    case ARG_PATTERN:
      GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
      break;
192 193 194 195 196 197 198 199 200
    case ARG_JUSTIFY:
      GTK_VALUE_ENUM (*arg) = label->jtype;
      break;
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
}

201
static void
Elliot Lee's avatar
Elliot Lee committed
202 203 204
gtk_label_init (GtkLabel *label)
{
  GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
Tim Janik's avatar
Tim Janik committed
205
  
Elliot Lee's avatar
Elliot Lee committed
206
  label->label = NULL;
207 208 209
  label->label_wc = NULL;
  label->pattern = NULL;

Owen Taylor's avatar
Owen Taylor committed
210
  label->words = NULL;
211

Tim Janik's avatar
Tim Janik committed
212
  label->max_width = 0;
Elliot Lee's avatar
Elliot Lee committed
213
  label->jtype = GTK_JUSTIFY_CENTER;
Owen Taylor's avatar
Owen Taylor committed
214
  label->wrap = FALSE;
215
  
Owen Taylor's avatar
Owen Taylor committed
216
  gtk_label_set_text (label, "");
Elliot Lee's avatar
Elliot Lee committed
217 218 219
}

GtkWidget*
Owen Taylor's avatar
Owen Taylor committed
220
gtk_label_new (const char *str)
Elliot Lee's avatar
Elliot Lee committed
221 222
{
  GtkLabel *label;
223
  
Elliot Lee's avatar
Elliot Lee committed
224
  g_return_val_if_fail (str != NULL, NULL);
225
  
Elliot Lee's avatar
Elliot Lee committed
226
  label = gtk_type_new (gtk_label_get_type ());
227
  
Owen Taylor's avatar
Owen Taylor committed
228
  gtk_label_set_text (label, str);
229
  
Elliot Lee's avatar
Elliot Lee committed
230 231 232
  return GTK_WIDGET (label);
}

233 234 235 236
static void
gtk_label_set_text_internal (GtkLabel *label,
			     char     *str,
			     GdkWChar *str_wc)
Elliot Lee's avatar
Elliot Lee committed
237 238 239
{
  if (label->label)
    g_free (label->label);
Owen Taylor's avatar
Owen Taylor committed
240 241
  if (label->label_wc)
    g_free (label->label_wc);
242
  
243 244
  label->label = str;
  label->label_wc = str_wc;
245
  
Owen Taylor's avatar
Owen Taylor committed
246
  gtk_label_free_words (label);
247
  
248 249 250
  if (GTK_WIDGET_VISIBLE (label))
    {
      if (GTK_WIDGET_MAPPED (label))
251
	gtk_widget_queue_clear (GTK_WIDGET (label));
252
      
253 254 255 256
      gtk_widget_queue_resize (GTK_WIDGET (label));
    }
}

257 258 259 260 261 262 263 264 265 266 267
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);
268 269 270 271 272 273 274

  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);
275 276 277 278 279 280 281
      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);
282
    }
283 284
}

285 286 287 288 289 290 291 292 293 294
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);
Tim Janik's avatar
Tim Janik committed
295
  
Elliot Lee's avatar
Elliot Lee committed
296 297 298
  if (GTK_WIDGET_VISIBLE (label))
    {
      if (GTK_WIDGET_MAPPED (label))
299
	gtk_widget_queue_clear (GTK_WIDGET (label));
Tim Janik's avatar
Tim Janik committed
300
      
Elliot Lee's avatar
Elliot Lee committed
301 302 303 304 305
      gtk_widget_queue_resize (GTK_WIDGET (label));
    }
}

void
Owen Taylor's avatar
Owen Taylor committed
306
gtk_label_set_justify (GtkLabel *label, GtkJustification jtype)
Elliot Lee's avatar
Elliot Lee committed
307 308 309
{
  g_return_if_fail (label != NULL);
  g_return_if_fail (GTK_IS_LABEL (label));
310
  
Elliot Lee's avatar
Elliot Lee committed
311 312
  if ((GtkJustification) label->jtype != jtype)
    {
Owen Taylor's avatar
Owen Taylor committed
313 314 315
      if ((label->jtype == GTK_JUSTIFY_FILL) || 
	  (jtype == GTK_JUSTIFY_FILL))
	/* FIXME: think about this a little */
316 317
	gtk_label_free_words (label);
      
Elliot Lee's avatar
Elliot Lee committed
318 319 320
      label->jtype = jtype;
      
      if (GTK_WIDGET_VISIBLE (label))
Owen Taylor's avatar
Owen Taylor committed
321 322
        {
          if (GTK_WIDGET_MAPPED (label))
323
	    gtk_widget_queue_clear (GTK_WIDGET (label));
Owen Taylor's avatar
Owen Taylor committed
324 325 326
          
          gtk_widget_queue_resize (GTK_WIDGET (label));
        }
Elliot Lee's avatar
Elliot Lee committed
327 328 329 330
    }
}

void
Owen Taylor's avatar
Owen Taylor committed
331 332 333 334
gtk_label_set_line_wrap (GtkLabel *label, gboolean wrap)
{
  g_return_if_fail (label != NULL);
  g_return_if_fail (GTK_IS_LABEL (label));
335
  
Owen Taylor's avatar
Owen Taylor committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
  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)
Elliot Lee's avatar
Elliot Lee committed
351 352 353 354
{
  g_return_if_fail (label != NULL);
  g_return_if_fail (GTK_IS_LABEL (label));
  g_return_if_fail (str != NULL);
355
  
Elliot Lee's avatar
Elliot Lee committed
356 357 358 359
  *str = label->label;
}

static void
360
gtk_label_finalize (GtkObject *object)
Elliot Lee's avatar
Elliot Lee committed
361 362
{
  GtkLabel *label;
363
  
Elliot Lee's avatar
Elliot Lee committed
364 365
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_LABEL (object));
366
  
Elliot Lee's avatar
Elliot Lee committed
367
  label = GTK_LABEL (object);
368
  
Elliot Lee's avatar
Elliot Lee committed
369
  g_free (label->label);
Owen Taylor's avatar
Owen Taylor committed
370 371
  g_free (label->label_wc);
  
Owen Taylor's avatar
Owen Taylor committed
372 373 374
  if (label->pattern) 
    g_free (label->pattern);
  gtk_label_free_words (label);
375
  (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
Elliot Lee's avatar
Elliot Lee committed
376 377
}

378
static GtkLabelWord*
Owen Taylor's avatar
Owen Taylor committed
379
gtk_label_word_alloc ()
380
{
Owen Taylor's avatar
Owen Taylor committed
381
  GtkLabelWord * word;
382
  
Owen Taylor's avatar
Owen Taylor committed
383 384 385 386 387 388 389 390 391 392 393 394 395
  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;
    }
396
  else
Owen Taylor's avatar
Owen Taylor committed
397 398 399
    {
      word = g_mem_chunk_alloc (word_chunk);
    }
400
  
Owen Taylor's avatar
Owen Taylor committed
401 402 403 404
  word->next = 0;
  word->uline = 0;
  return word;
}
405

Owen Taylor's avatar
Owen Taylor committed
406
static void
407
gtk_label_free_words (GtkLabel *label)
Owen Taylor's avatar
Owen Taylor committed
408 409
{
  GtkLabelWord * last;
410
  
Owen Taylor's avatar
Owen Taylor committed
411 412 413 414 415 416
  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;
417
      label->words = NULL;
Owen Taylor's avatar
Owen Taylor committed
418 419
    }
}
420 421
static GtkLabelULine*
gtk_label_uline_alloc (void)
Owen Taylor's avatar
Owen Taylor committed
422 423
{
  GtkLabelULine * uline;
424
  
Owen Taylor's avatar
Owen Taylor committed
425 426 427 428 429 430 431
  if (!uline_chunk)
    {
      uline_chunk = g_mem_chunk_new ("GtkLabelWord chunk",
				     sizeof (GtkLabelULine),
				     32 * sizeof (GtkLabelULine),
				     G_ALLOC_ONLY);
    }
432
  
Owen Taylor's avatar
Owen Taylor committed
433
  if (free_ulines)
434
    {
Owen Taylor's avatar
Owen Taylor committed
435 436
      uline = free_ulines;
      free_ulines = uline->next;
437 438
    }
  else
Owen Taylor's avatar
Owen Taylor committed
439 440 441
    {
      uline = g_mem_chunk_alloc (uline_chunk);
    }
442
  
443
  uline->next = NULL;
444
  
Owen Taylor's avatar
Owen Taylor committed
445 446 447 448 449 450 451 452 453 454
  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)
455
	;
Owen Taylor's avatar
Owen Taylor committed
456 457 458 459 460
      last->next = free_ulines;
      free_ulines = word->uline;
      word->uline = 0;
    }
}
461

Owen Taylor's avatar
Owen Taylor committed
462
static gint
463
gtk_label_split_text (GtkLabel *label)
Owen Taylor's avatar
Owen Taylor committed
464 465 466 467 468
{
  GtkLabelWord *word, **tailp;
  gint space_width, line_width, max_line_width;
  GdkWChar *str, *p;
  
469 470
  g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
  
Owen Taylor's avatar
Owen Taylor committed
471
  gtk_label_free_words (label);
472 473
  if (label->label == NULL)
    return 0;
Owen Taylor's avatar
Owen Taylor committed
474
  
475
  /* Split text at new-lines. */
Owen Taylor's avatar
Owen Taylor committed
476
  space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
477
  
Owen Taylor's avatar
Owen Taylor committed
478 479 480 481
  line_width = 0;
  max_line_width = 0;
  tailp = &label->words;
  str = label->label_wc;
482
  
Owen Taylor's avatar
Owen Taylor committed
483
  while (*str)
484
    {
Owen Taylor's avatar
Owen Taylor committed
485
      word = gtk_label_word_alloc ();
486 487
      
      if (str == label->label_wc || str[-1] == '\n')
Owen Taylor's avatar
Owen Taylor committed
488 489 490
	{
	  /* Paragraph break */
	  word->space = 0;
491
	  
Owen Taylor's avatar
Owen Taylor committed
492 493 494 495 496 497 498 499 500 501 502
	  max_line_width = MAX (line_width, max_line_width);
	  line_width = 0;
	}
      else if (str[0] == ' ')
	{
	  while (str[0] == ' ')
	    {
	      str++;
	      word->space += space_width;
	    }
	}
503
      else
Owen Taylor's avatar
Owen Taylor committed
504 505 506 507
	{
	  /* Regular inter-word space */
	  word->space = space_width;
	}
508
      
Owen Taylor's avatar
Owen Taylor committed
509 510 511 512
      word->beginning = str;
      
      word->length = 0;
      p = word->beginning;
513
      while (*p && *p != '\n')
514
	{
Owen Taylor's avatar
Owen Taylor committed
515 516 517
	  word->length++;
	  p++;
	}
518
      
Owen Taylor's avatar
Owen Taylor committed
519
      word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
520
      
Owen Taylor's avatar
Owen Taylor committed
521 522
      str += word->length;
      if (*str)
523
	str++;
Owen Taylor's avatar
Owen Taylor committed
524 525
      
      line_width += word->space + word->width;
526
      
Owen Taylor's avatar
Owen Taylor committed
527 528 529
      *tailp = word;
      tailp = &word->next;
    }
530
  
531 532 533 534 535
  /* Add an empty word to represent an empty line
   */
  if ((str == label->label_wc) || (str[-1] == '\n'))
    {
      word = gtk_label_word_alloc ();
536
      
537 538 539 540 541 542 543 544
      word->space = 0;
      word->beginning = str;
      word->length = 0;
      word->width = 0;
      
      *tailp = word;
      tailp = &word->next;
    }
545
  
Owen Taylor's avatar
Owen Taylor committed
546 547 548 549
  return MAX (line_width, max_line_width);
}

static gint
550
gtk_label_split_text_wrapped (GtkLabel *label)
Owen Taylor's avatar
Owen Taylor committed
551 552 553 554 555 556
{
  /* this needs to handle white space better. */
  GtkLabelWord *word, **tailp;
  gint space_width, line_width, max_line_width;
  GdkWChar *str, *p;
  
557 558
  g_return_val_if_fail (GTK_WIDGET (label)->style->font != NULL, 0);
  
Owen Taylor's avatar
Owen Taylor committed
559
  gtk_label_free_words (label);
560 561
  if (label->label == NULL)
    return 0;
Owen Taylor's avatar
Owen Taylor committed
562
  
563
  /* Split text at new-lines.  (Or at spaces in the case of paragraphs). */
Owen Taylor's avatar
Owen Taylor committed
564
  space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
565
  
Owen Taylor's avatar
Owen Taylor committed
566 567 568 569 570 571 572
  line_width = 0;
  max_line_width = 0;
  tailp = &label->words;
  str = label->label_wc;
  while (*str)
    {
      word = gtk_label_word_alloc ();
573
      
Owen Taylor's avatar
Owen Taylor committed
574 575 576 577
      if (str == label->label_wc || str[-1] == '\n')
	{
	  /* Paragraph break */
	  word->space = 0;
578
	  
Owen Taylor's avatar
Owen Taylor committed
579 580 581 582 583 584
	  max_line_width = MAX (line_width, max_line_width);
	  line_width = 0;
	}
      else if (str[0] == ' ')
	{
	  gint nspaces = 0;
585
	  
Owen Taylor's avatar
Owen Taylor committed
586
	  while (str[0] == ' ')
587
	    {
Owen Taylor's avatar
Owen Taylor committed
588 589
	      nspaces++;
	      str++;
590
	    }
591
	  
Owen Taylor's avatar
Owen Taylor committed
592 593 594
	  if (label->jtype == GTK_JUSTIFY_FILL)
	    word->space = (space_width * 3 + 1) / 2;
	  else
595
	    word->space = space_width * nspaces;
Owen Taylor's avatar
Owen Taylor committed
596 597 598 599 600 601
	}
      else
	{
	  /* Regular inter-word space */
	  word->space = space_width;
	}
602
      
Owen Taylor's avatar
Owen Taylor committed
603 604 605
      word->beginning = str;
      word->length = 0;
      p = word->beginning;
606
      while (*p && !gdk_iswspace (*p))
Owen Taylor's avatar
Owen Taylor committed
607 608 609 610 611
	{
	  word->length++;
	  p++;
	}
      word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
612
      
Owen Taylor's avatar
Owen Taylor committed
613 614
      str += word->length;
      if (*str)
615
	str++;
Owen Taylor's avatar
Owen Taylor committed
616 617
      
      line_width += word->space + word->width;
618
      
Owen Taylor's avatar
Owen Taylor committed
619 620 621
      *tailp = word;
      tailp = &word->next;
    }
622
  
Owen Taylor's avatar
Owen Taylor committed
623 624 625
  return MAX (line_width, max_line_width);
}

626
/* gtk_label_pick_width
Owen Taylor's avatar
Owen Taylor committed
627 628 629 630 631 632 633 634 635 636 637
 *
 * 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
638 639 640
gtk_label_pick_width (GtkLabel *label,
		      gint      min_width,
		      gint      max_width)
Owen Taylor's avatar
Owen Taylor committed
641 642 643 644 645
{
  GtkLabelWord *word;
  gint width, line_width;
  
  g_return_val_if_fail (label->wrap, min_width);
646
  
Owen Taylor's avatar
Owen Taylor committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
  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;
    }
662
  
Owen Taylor's avatar
Owen Taylor committed
663 664 665
  return MAX (width, line_width);
}

666
/* Here, we finalize the lines.
Owen Taylor's avatar
Owen Taylor committed
667 668 669 670
 * This is only for non-wrap labels.  Wrapped labels
 * use gtk_label_finalize_wrap instead.
 */
static void
671 672
gtk_label_finalize_lines (GtkLabel *label,
			  gint      line_width)
Owen Taylor's avatar
Owen Taylor committed
673
{
674
  GtkLabelWord *line;
Owen Taylor's avatar
Owen Taylor committed
675 676 677 678 679 680
  gint y, baseline_skip, y_max;
  gint i, j;
  gchar *ptrn;
  
  g_return_if_fail (!label->wrap);
  ptrn = label->pattern;
681
  
Owen Taylor's avatar
Owen Taylor committed
682 683 684 685 686 687
  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)
688
	line->x = (line_width - line->width) / 2;
Owen Taylor's avatar
Owen Taylor committed
689
      else if (label->jtype == GTK_JUSTIFY_RIGHT)
690
	line->x = line_width - line->width;
Owen Taylor's avatar
Owen Taylor committed
691
      else
692 693
	line->x = 0;
      
Owen Taylor's avatar
Owen Taylor committed
694 695
      line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
      y_max = 0;
696
      
Owen Taylor's avatar
Owen Taylor committed
697
      /* now we deal with the underline stuff; */
698
      if (ptrn && ptrn[0] != '\0')
Owen Taylor's avatar
Owen Taylor committed
699
	{
700
	  for (i = 0; i < line->length; i++)
701
	    {
Owen Taylor's avatar
Owen Taylor committed
702 703 704 705 706 707 708 709 710 711
	      if (ptrn[i] == '\0')
		break;
	      else if (ptrn[i] == '_')
		{
		  gint descent;
		  gint rbearing;
		  gint lbearing;
		  gint width;
		  gint offset;
		  GtkLabelULine *uline;
712 713
		  
		  for (j = i + 1; j < line->length; j++)
Owen Taylor's avatar
Owen Taylor committed
714 715 716 717 718 719
		    {
		      if (ptrn[j] == '\0')
			break;
		      else if (ptrn[j] == ' ')
			break;
		    }
720
		  
721
		  /* good.  Now we have an underlined segment.
Owen Taylor's avatar
Owen Taylor committed
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
		   * 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;
		}
741
	    }
Owen Taylor's avatar
Owen Taylor committed
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
	  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
758 759
gtk_label_finalize_lines_wrap (GtkLabel *label,
			       gint      line_width)
Owen Taylor's avatar
Owen Taylor committed
760 761 762 763 764 765 766
{
  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);
767
  
Owen Taylor's avatar
Owen Taylor committed
768 769 770 771 772 773 774 775 776 777 778 779
  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)
780
	    break;		/* New paragraph */
Owen Taylor's avatar
Owen Taylor committed
781
	  if (next_line->space + next_line->width > extra_width)
782
	    break;
Owen Taylor's avatar
Owen Taylor committed
783 784 785
	  extra_width -= next_line->space + next_line->width;
	  space += next_line->space;
	}
786
      
Owen Taylor's avatar
Owen Taylor committed
787 788 789 790
      line->x = 0;
      line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
      x = line->width;
      add_space = 0;
791
      
Owen Taylor's avatar
Owen Taylor committed
792 793 794
      for (word = line->next; word != next_line; word = word->next)
	{
	  if (next_line && next_line->space)
795
	    {
Owen Taylor's avatar
Owen Taylor committed
796 797 798 799 800 801
	      /* 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;
	      }
802
	    }
Owen Taylor's avatar
Owen Taylor committed
803 804 805 806
	  
	  word->x = x + word->space + add_space;
	  word->y = line->y;
	  x = word->x + word->width;
807
	}
808
      
Owen Taylor's avatar
Owen Taylor committed
809
      y += (baseline_skip);
810
    }
811
  
Owen Taylor's avatar
Owen Taylor committed
812 813 814 815
  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;
816
}
817

Elliot Lee's avatar
Elliot Lee committed
818 819 820 821 822
static void
gtk_label_size_request (GtkWidget      *widget,
			GtkRequisition *requisition)
{
  GtkLabel *label;
Tim Janik's avatar
Tim Janik committed
823
  
Elliot Lee's avatar
Elliot Lee committed
824 825 826
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_LABEL (widget));
  g_return_if_fail (requisition != NULL);
827
  
Elliot Lee's avatar
Elliot Lee committed
828
  label = GTK_LABEL (widget);
829
  
Owen Taylor's avatar
Owen Taylor committed
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
  /*
   * 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.
   */
850
  
Owen Taylor's avatar
Owen Taylor committed
851
  if (label->wrap)
Elliot Lee's avatar
Elliot Lee committed
852
    {
Owen Taylor's avatar
Owen Taylor committed
853 854 855 856 857 858 859 860 861 862 863 864 865 866
      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,
867
					       "This is a good enough length for any line to have.");
Owen Taylor's avatar
Owen Taylor committed
868 869 870 871 872 873 874 875 876
	  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,
877 878
						       perfect_width,
						       label->max_width);
Owen Taylor's avatar
Owen Taylor committed
879 880 881 882
	    }
	}
      gtk_label_finalize_lines_wrap (label, label->max_width);
    }
883
  else if (label->words == NULL)
Owen Taylor's avatar
Owen Taylor committed
884 885 886 887
    {
      label->max_width = gtk_label_split_text (label);
      gtk_label_finalize_lines (label, label->max_width);
    }
888
  
Owen Taylor's avatar
Owen Taylor committed
889 890 891
  if (requisition != &widget->requisition)
    *requisition = widget->requisition;
}
892

893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
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);
}

Owen Taylor's avatar
Owen Taylor committed
911
static void
912 913 914
gtk_label_paint_word (GtkLabel     *label,
		      gint          x,
		      gint          y,
Owen Taylor's avatar
Owen Taylor committed
915 916 917 918 919 920
		      GtkLabelWord *word,
		      GdkRectangle *area)
{
  GtkWidget *widget = GTK_WIDGET (label);
  GtkLabelULine *uline;
  gchar *tmp_str;
921
  
Owen Taylor's avatar
Owen Taylor committed
922
  tmp_str = gdk_wcstombs (word->beginning);
923 924 925 926 927 928 929 930 931
  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);
    }
Owen Taylor's avatar
Owen Taylor committed
932
  
933
  for (uline = word->uline; uline; uline = uline->next)
Owen Taylor's avatar
Owen Taylor committed
934 935 936
    gtk_paint_hline (widget->style, widget->window, 
		     widget->state, area,
		     widget, "label", 
937
		     x + uline->x1, x + uline->x2, y + uline->y);
Elliot Lee's avatar
Elliot Lee committed
938
}
939

Elliot Lee's avatar
Elliot Lee committed
940
static gint
Owen Taylor's avatar
Owen Taylor committed
941
gtk_label_expose (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
942 943 944 945
		  GdkEventExpose *event)
{
  GtkLabel *label;
  GtkMisc *misc;
Owen Taylor's avatar
Owen Taylor committed
946
  GtkLabelWord *word;
Elliot Lee's avatar
Elliot Lee committed
947
  gint x, y;
948
  
Elliot Lee's avatar
Elliot Lee committed
949 950 951
  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);
952
  
Arturo Espinosa's avatar
Arturo Espinosa committed
953
  label = GTK_LABEL (widget);
Tim Janik's avatar
Tim Janik committed
954
  
955 956
  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
      label->label && (*label->label != '\0'))
Elliot Lee's avatar
Elliot Lee committed
957 958
    {
      misc = GTK_MISC (widget);
959
      
960 961
      /*
       * GC Clipping
Elliot Lee's avatar
Elliot Lee committed
962
       */
963
      gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
964
      gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
965
      
966 967 968 969 970 971 972 973 974 975
      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);

Owen Taylor's avatar
Owen Taylor committed
976
      for (word = label->words; word; word = word->next)
977
	{
Owen Taylor's avatar
Owen Taylor committed
978 979 980 981
	  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;
982
	}
983
      
984
      gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
985
      gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
986
    }
987

Elliot Lee's avatar
Elliot Lee committed
988 989
  return TRUE;
}
990

991
guint      
992 993
gtk_label_parse_uline (GtkLabel    *label,
		       const gchar *string)
994 995
{
  guint accel_key = GDK_VoidSymbol;
996
  GdkWChar *p, *q, *string_wc;
Owen Taylor's avatar
Owen Taylor committed
997 998
  gchar *r;
  gchar *pattern;
999
  gint length, wc_length;
1000
  gboolean underscore;
1001
  
1002 1003
  g_return_val_if_fail(string != NULL, GDK_VoidSymbol);

1004
  /* Convert text to wide characters */
1005
  length = strlen (string);
1006 1007
  string_wc = g_new (GdkWChar, length + 1);
  wc_length = gdk_mbstowcs (string_wc, string, length + 1);
1008 1009 1010 1011 1012 1013
  if (wc_length < 0)
    {
      g_free (string_wc);
      return GDK_VoidSymbol;
    }

1014
  string_wc[wc_length] = '\0';
1015 1016
  
  pattern = g_new (gchar, length+1);
1017
  
1018
  underscore = FALSE;
1019
  
1020
  p = q = string_wc;
1021
  r = pattern;