gtklabel.c 24.6 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
#include <math.h>
Elliot Lee's avatar
Elliot Lee committed
19 20
#include <string.h>
#include "gtklabel.h"
21
#include "gdk/gdkkeysyms.h"
Owen Taylor's avatar
Owen Taylor committed
22
#include "gdk/gdki18n.h"
Elliot Lee's avatar
Elliot Lee committed
23

24 25 26
enum {
  ARG_0,
  ARG_LABEL,
27
  ARG_PATTERN,
28 29 30
  ARG_JUSTIFY
};

Owen Taylor's avatar
Owen Taylor committed
31 32 33 34 35
typedef struct _GtkLabelULine GtkLabelULine;
struct _GtkLabelWord
{
  GdkWChar *beginning;
  gint length;
36
  
Owen Taylor's avatar
Owen Taylor committed
37 38 39 40 41 42 43
  /* 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;
44
  gint width;
Owen Taylor's avatar
Owen Taylor committed
45 46 47 48 49
  gint x;
  gint y;
  GtkLabelWord *next;
  gint uline_y;
  GtkLabelULine *uline;
50 51
};

Owen Taylor's avatar
Owen Taylor committed
52 53 54 55 56 57 58
struct _GtkLabelULine
{
  gint x1;
  gint x2;
  gint y;
  GtkLabelULine *next;
};
59

Elliot Lee's avatar
Elliot Lee committed
60
static void gtk_label_class_init   (GtkLabelClass  *klass);
Tim Janik's avatar
Tim Janik committed
61
static void gtk_label_init	   (GtkLabel	   *label);
62
static void gtk_label_set_arg	   (GtkObject	   *object,
Tim Janik's avatar
Tim Janik committed
63 64
				    GtkArg	   *arg,
				    guint	    arg_id);
65
static void gtk_label_get_arg	   (GtkObject      *object,
Tim Janik's avatar
Tim Janik committed
66 67 68 69
				    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
70
				    GtkRequisition *requisition);
71 72
static void gtk_label_style_set    (GtkWidget      *widget,
				    GtkStyle       *previous_style);
Tim Janik's avatar
Tim Janik committed
73
static gint gtk_label_expose	   (GtkWidget	   *widget,
Elliot Lee's avatar
Elliot Lee committed
74
				    GdkEventExpose *event);
Owen Taylor's avatar
Owen Taylor committed
75 76 77 78 79 80 81 82

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
83 84 85 86


static GtkMiscClass *parent_class = NULL;

Owen Taylor's avatar
Owen Taylor committed
87 88 89 90
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
91

Tim Janik's avatar
Tim Janik committed
92
GtkType
93
gtk_label_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
94
{
Tim Janik's avatar
Tim Janik committed
95 96
  static GtkType label_type = 0;
  
Elliot Lee's avatar
Elliot Lee committed
97 98
  if (!label_type)
    {
99
      static const GtkTypeInfo label_info =
Elliot Lee's avatar
Elliot Lee committed
100 101 102 103 104 105
      {
	"GtkLabel",
	sizeof (GtkLabel),
	sizeof (GtkLabelClass),
	(GtkClassInitFunc) gtk_label_class_init,
	(GtkObjectInitFunc) gtk_label_init,
106 107
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
108
	(GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
109
      };
Tim Janik's avatar
Tim Janik committed
110
      
Elliot Lee's avatar
Elliot Lee committed
111
      label_type = gtk_type_unique (gtk_misc_get_type (), &label_info);
112
      gtk_type_set_chunk_alloc (label_type, 32);
Elliot Lee's avatar
Elliot Lee committed
113
    }
Tim Janik's avatar
Tim Janik committed
114
  
Elliot Lee's avatar
Elliot Lee committed
115 116 117
  return label_type;
}

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

142
static void
143
gtk_label_set_arg (GtkObject	  *object,
Tim Janik's avatar
Tim Janik committed
144 145
		   GtkArg	  *arg,
		   guint	   arg_id)
146
{
147
  GtkLabel *label;
148
  
149
  label = GTK_LABEL (object);
150
  
151 152 153
  switch (arg_id)
    {
    case ARG_LABEL:
Owen Taylor's avatar
Owen Taylor committed
154
      gtk_label_set_text (label, GTK_VALUE_STRING (*arg) ? GTK_VALUE_STRING (*arg) : "");
155
      break;
156 157 158
    case ARG_PATTERN:
      gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
      break;
159 160 161
    case ARG_JUSTIFY:
      gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
      break;
Tim Janik's avatar
Tim Janik committed
162 163
    default:
      break;
164 165 166
    }
}

Tim Janik's avatar
Tim Janik committed
167
static void
168
gtk_label_get_arg (GtkObject	  *object,
Tim Janik's avatar
Tim Janik committed
169 170
		   GtkArg	  *arg,
		   guint	   arg_id)
171
{
172
  GtkLabel *label;
173
  
174
  label = GTK_LABEL (object);
175
  
176 177 178 179 180
  switch (arg_id)
    {
    case ARG_LABEL:
      GTK_VALUE_STRING (*arg) = g_strdup (label->label);
      break;
181 182 183
    case ARG_PATTERN:
      GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
      break;
184 185 186 187 188 189 190 191 192
    case ARG_JUSTIFY:
      GTK_VALUE_ENUM (*arg) = label->jtype;
      break;
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
}

193
static void
Elliot Lee's avatar
Elliot Lee committed
194 195 196
gtk_label_init (GtkLabel *label)
{
  GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
Tim Janik's avatar
Tim Janik committed
197
  
Elliot Lee's avatar
Elliot Lee committed
198
  label->label = NULL;
199 200 201
  label->label_wc = NULL;
  label->pattern = NULL;

Owen Taylor's avatar
Owen Taylor committed
202
  label->words = NULL;
203

Tim Janik's avatar
Tim Janik committed
204
  label->max_width = 0;
Elliot Lee's avatar
Elliot Lee committed
205
  label->jtype = GTK_JUSTIFY_CENTER;
Owen Taylor's avatar
Owen Taylor committed
206
  label->wrap = FALSE;
207
  
Owen Taylor's avatar
Owen Taylor committed
208
  gtk_label_set_text (label, "");
Elliot Lee's avatar
Elliot Lee committed
209 210 211
}

GtkWidget*
Owen Taylor's avatar
Owen Taylor committed
212
gtk_label_new (const char *str)
Elliot Lee's avatar
Elliot Lee committed
213 214
{
  GtkLabel *label;
215
  
Elliot Lee's avatar
Elliot Lee committed
216
  g_return_val_if_fail (str != NULL, NULL);
217
  
Elliot Lee's avatar
Elliot Lee committed
218
  label = gtk_type_new (gtk_label_get_type ());
219
  
Owen Taylor's avatar
Owen Taylor committed
220
  gtk_label_set_text (label, str);
221
  
Elliot Lee's avatar
Elliot Lee committed
222 223 224
  return GTK_WIDGET (label);
}

225 226 227 228
static void
gtk_label_set_text_internal (GtkLabel *label,
			     char     *str,
			     GdkWChar *str_wc)
Elliot Lee's avatar
Elliot Lee committed
229 230 231
{
  if (label->label)
    g_free (label->label);
Owen Taylor's avatar
Owen Taylor committed
232 233
  if (label->label_wc)
    g_free (label->label_wc);
234
  
235 236
  label->label = str;
  label->label_wc = str_wc;
237
  
Owen Taylor's avatar
Owen Taylor committed
238
  gtk_label_free_words (label);
239
  
240 241 242
  if (GTK_WIDGET_VISIBLE (label))
    {
      if (GTK_WIDGET_MAPPED (label))
243
	gtk_widget_queue_clear (GTK_WIDGET (label));
244
      
245 246 247 248
      gtk_widget_queue_resize (GTK_WIDGET (label));
    }
}

249 250 251 252 253 254 255 256 257 258 259
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);
260 261 262 263 264 265 266

  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);
267 268 269 270 271 272 273
      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);
274
    }
275 276
}

277 278 279 280 281 282 283 284 285 286
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
287
  
Elliot Lee's avatar
Elliot Lee committed
288 289 290
  if (GTK_WIDGET_VISIBLE (label))
    {
      if (GTK_WIDGET_MAPPED (label))
291
	gtk_widget_queue_clear (GTK_WIDGET (label));
Tim Janik's avatar
Tim Janik committed
292
      
Elliot Lee's avatar
Elliot Lee committed
293 294 295 296 297
      gtk_widget_queue_resize (GTK_WIDGET (label));
    }
}

void
Owen Taylor's avatar
Owen Taylor committed
298
gtk_label_set_justify (GtkLabel *label, GtkJustification jtype)
Elliot Lee's avatar
Elliot Lee committed
299 300 301
{
  g_return_if_fail (label != NULL);
  g_return_if_fail (GTK_IS_LABEL (label));
302
  
Elliot Lee's avatar
Elliot Lee committed
303 304
  if ((GtkJustification) label->jtype != jtype)
    {
Owen Taylor's avatar
Owen Taylor committed
305 306 307
      if ((label->jtype == GTK_JUSTIFY_FILL) || 
	  (jtype == GTK_JUSTIFY_FILL))
	/* FIXME: think about this a little */
308 309
	gtk_label_free_words (label);
      
Elliot Lee's avatar
Elliot Lee committed
310 311 312
      label->jtype = jtype;
      
      if (GTK_WIDGET_VISIBLE (label))
Owen Taylor's avatar
Owen Taylor committed
313 314
        {
          if (GTK_WIDGET_MAPPED (label))
315
	    gtk_widget_queue_clear (GTK_WIDGET (label));
Owen Taylor's avatar
Owen Taylor committed
316 317 318
          
          gtk_widget_queue_resize (GTK_WIDGET (label));
        }
Elliot Lee's avatar
Elliot Lee committed
319 320 321 322
    }
}

void
Owen Taylor's avatar
Owen Taylor committed
323 324 325 326
gtk_label_set_line_wrap (GtkLabel *label, gboolean wrap)
{
  g_return_if_fail (label != NULL);
  g_return_if_fail (GTK_IS_LABEL (label));
327
  
Owen Taylor's avatar
Owen Taylor committed
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
  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
343 344 345 346
{
  g_return_if_fail (label != NULL);
  g_return_if_fail (GTK_IS_LABEL (label));
  g_return_if_fail (str != NULL);
347
  
Elliot Lee's avatar
Elliot Lee committed
348 349 350 351
  *str = label->label;
}

static void
352
gtk_label_finalize (GtkObject *object)
Elliot Lee's avatar
Elliot Lee committed
353 354
{
  GtkLabel *label;
355
  
Elliot Lee's avatar
Elliot Lee committed
356 357
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_LABEL (object));
358
  
Elliot Lee's avatar
Elliot Lee committed
359
  label = GTK_LABEL (object);
360
  
Elliot Lee's avatar
Elliot Lee committed
361
  g_free (label->label);
Owen Taylor's avatar
Owen Taylor committed
362 363
  g_free (label->label_wc);
  
Owen Taylor's avatar
Owen Taylor committed
364 365 366
  if (label->pattern) 
    g_free (label->pattern);
  gtk_label_free_words (label);
367
  (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
Elliot Lee's avatar
Elliot Lee committed
368 369
}

370
static GtkLabelWord*
Owen Taylor's avatar
Owen Taylor committed
371
gtk_label_word_alloc ()
372
{
Owen Taylor's avatar
Owen Taylor committed
373
  GtkLabelWord * word;
374
  
Owen Taylor's avatar
Owen Taylor committed
375 376 377 378 379 380 381 382 383 384 385 386 387
  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;
    }
388
  else
Owen Taylor's avatar
Owen Taylor committed
389 390 391
    {
      word = g_mem_chunk_alloc (word_chunk);
    }
392
  
Owen Taylor's avatar
Owen Taylor committed
393 394 395 396
  word->next = 0;
  word->uline = 0;
  return word;
}
397

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

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

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

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

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

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

885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
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
903
static void
904 905 906
gtk_label_paint_word (GtkLabel     *label,
		      gint          x,
		      gint          y,
Owen Taylor's avatar
Owen Taylor committed
907 908 909 910 911 912
		      GtkLabelWord *word,
		      GdkRectangle *area)
{
  GtkWidget *widget = GTK_WIDGET (label);
  GtkLabelULine *uline;
  gchar *tmp_str;
913
  
Owen Taylor's avatar
Owen Taylor committed
914
  tmp_str = gdk_wcstombs (word->beginning);
915 916 917 918 919 920 921 922 923
  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
924
  
925
  for (uline = word->uline; uline; uline = uline->next)
Owen Taylor's avatar
Owen Taylor committed
926 927 928
    gtk_paint_hline (widget->style, widget->window, 
		     widget->state, area,
		     widget, "label", 
929
		     x + uline->x1, x + uline->x2, y + uline->y);
Elliot Lee's avatar
Elliot Lee committed
930
}
931

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

Elliot Lee's avatar
Elliot Lee committed
980 981
  return TRUE;
}
982

983
guint      
984 985
gtk_label_parse_uline (GtkLabel    *label,
		       const gchar *string)
986 987
{
  guint accel_key = GDK_VoidSymbol;
988
  GdkWChar *p, *q, *string_wc;
Owen Taylor's avatar
Owen Taylor committed
989 990
  gchar *r;
  gchar *pattern;
991
  gint length, wc_length;
992
  gboolean underscore;
993
  
994 995
  g_return_val_if_fail(string != NULL, GDK_VoidSymbol);

996
  /* Convert text to wide characters */
997
  length = strlen (string);
998 999
  string_wc = g_new (GdkWChar, length + 1);
  wc_length = gdk_mbstowcs (string_wc, string, length + 1);
1000 1001 1002 1003 1004 1005
  if (wc_length < 0)
    {
      g_free (string_wc);
      return GDK_VoidSymbol;
    }

1006
  string_wc[wc_length] = '\0';
1007 1008
  
  pattern = g_new (gchar, length+1);
1009
  
1010
  underscore = FALSE;
1011
  
1012
  p = q = string_wc;
1013
  r = pattern;
1014
  
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024