gtklabel.c 23.9 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);
Tim Janik's avatar
Tim Janik committed
71
static gint gtk_label_expose	   (GtkWidget	   *widget,
Elliot Lee's avatar
Elliot Lee committed
72
				    GdkEventExpose *event);
Owen Taylor's avatar
Owen Taylor committed
73 74 75 76 77 78 79 80

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
81 82 83 84


static GtkMiscClass *parent_class = NULL;

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

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

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

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

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

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

Owen Taylor's avatar
Owen Taylor committed
199
  label->words = NULL;
200

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

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

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

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

  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);
      str_wc[wc_len] = '\0';
      
      gtk_label_set_text_internal (label, g_strdup (str), str_wc);
    }
268 269
}

270 271 272 273 274 275 276 277 278 279
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
280
  
Elliot Lee's avatar
Elliot Lee committed
281 282 283
  if (GTK_WIDGET_VISIBLE (label))
    {
      if (GTK_WIDGET_MAPPED (label))
284
	gtk_widget_queue_clear (GTK_WIDGET (label));
Tim Janik's avatar
Tim Janik committed
285
      
Elliot Lee's avatar
Elliot Lee committed
286 287 288 289 290
      gtk_widget_queue_resize (GTK_WIDGET (label));
    }
}

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

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

static void
345
gtk_label_finalize (GtkObject *object)
Elliot Lee's avatar
Elliot Lee committed
346 347
{
  GtkLabel *label;
348
  
Elliot Lee's avatar
Elliot Lee committed
349 350
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_LABEL (object));
351
  
Elliot Lee's avatar
Elliot Lee committed
352
  label = GTK_LABEL (object);
353
  
Elliot Lee's avatar
Elliot Lee committed
354
  g_free (label->label);
Owen Taylor's avatar
Owen Taylor committed
355 356 357
  if (label->pattern) 
    g_free (label->pattern);
  gtk_label_free_words (label);
358
  (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
Elliot Lee's avatar
Elliot Lee committed
359 360
}

361
static GtkLabelWord*
Owen Taylor's avatar
Owen Taylor committed
362
gtk_label_word_alloc ()
363
{
Owen Taylor's avatar
Owen Taylor committed
364
  GtkLabelWord * word;
365
  
Owen Taylor's avatar
Owen Taylor committed
366 367 368 369 370 371 372 373 374 375 376 377 378
  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;
    }
379
  else
Owen Taylor's avatar
Owen Taylor committed
380 381 382
    {
      word = g_mem_chunk_alloc (word_chunk);
    }
383
  
Owen Taylor's avatar
Owen Taylor committed
384 385 386 387
  word->next = 0;
  word->uline = 0;
  return word;
}
388

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

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

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

609
/* gtk_label_pick_width
Owen Taylor's avatar
Owen Taylor committed
610 611 612 613 614 615 616 617 618 619 620
 *
 * 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
621 622 623
gtk_label_pick_width (GtkLabel *label,
		      gint      min_width,
		      gint      max_width)
Owen Taylor's avatar
Owen Taylor committed
624 625 626 627 628
{
  GtkLabelWord *word;
  gint width, line_width;
  
  g_return_val_if_fail (label->wrap, min_width);
629
  
Owen Taylor's avatar
Owen Taylor committed
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
  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;
    }
645
  
Owen Taylor's avatar
Owen Taylor committed
646 647 648
  return MAX (width, line_width);
}

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

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

Owen Taylor's avatar
Owen Taylor committed
876
static void
877 878 879
gtk_label_paint_word (GtkLabel     *label,
		      gint          x,
		      gint          y,
Owen Taylor's avatar
Owen Taylor committed
880 881 882 883 884 885
		      GtkLabelWord *word,
		      GdkRectangle *area)
{
  GtkWidget *widget = GTK_WIDGET (label);
  GtkLabelULine *uline;
  gchar *tmp_str;
886
  
Owen Taylor's avatar
Owen Taylor committed
887 888 889 890 891 892 893 894
  tmp_str = gdk_wcstombs (word->beginning);
  gtk_paint_string (widget->style, widget->window, widget->state,
		    area, widget, "label", 
		    x + word->x,
		    y + word->y,
		    tmp_str);
  g_free (tmp_str);
  
895
  for (uline = word->uline; uline; uline = uline->next)
Owen Taylor's avatar
Owen Taylor committed
896 897 898
    gtk_paint_hline (widget->style, widget->window, 
		     widget->state, area,
		     widget, "label", 
899 900 901
		     x + uline->x1, x + uline->x2, y + uline->y);
  
  
Elliot Lee's avatar
Elliot Lee committed
902
}
903

Elliot Lee's avatar
Elliot Lee committed
904
static gint
Owen Taylor's avatar
Owen Taylor committed
905
gtk_label_expose (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
906 907 908 909
		  GdkEventExpose *event)
{
  GtkLabel *label;
  GtkMisc *misc;
Owen Taylor's avatar
Owen Taylor committed
910
  GtkLabelWord *word;
Elliot Lee's avatar
Elliot Lee committed
911
  gint x, y;
912
  
Elliot Lee's avatar
Elliot Lee committed
913 914 915
  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);
916
  
Arturo Espinosa's avatar
Arturo Espinosa committed
917
  label = GTK_LABEL (widget);
Tim Janik's avatar
Tim Janik committed
918
  
919 920
  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
      label->label && (*label->label != '\0'))
Elliot Lee's avatar
Elliot Lee committed
921 922
    {
      misc = GTK_MISC (widget);
923
      
924 925
      /*
       * GC Clipping
Elliot Lee's avatar
Elliot Lee committed
926
       */
927
      gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
928
      gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
929
      
930 931 932 933 934 935 936 937 938 939
      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
940
      for (word = label->words; word; word = word->next)
941
	{
Owen Taylor's avatar
Owen Taylor committed
942 943 944 945
	  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;
946
	}
947
      
948
      gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
949
      gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
950
    }
951

Elliot Lee's avatar
Elliot Lee committed
952 953
  return TRUE;
}
954

955
guint      
956 957
gtk_label_parse_uline (GtkLabel    *label,
		       const gchar *string)
958 959
{
  guint accel_key = GDK_VoidSymbol;
960
  GdkWChar *p, *q, *string_wc;
Owen Taylor's avatar
Owen Taylor committed
961 962
  gchar *r;
  gchar *pattern;
963
  gint length, wc_length;
964
  gboolean underscore;
965
  
966 967
  g_return_val_if_fail(string != NULL, GDK_VoidSymbol);

968
  /* Convert text to wide characters */
969
  length = strlen (string);
970 971 972
  string_wc = g_new (GdkWChar, length + 1);
  wc_length = gdk_mbstowcs (string_wc, string, length + 1);
  string_wc[wc_length] = '\0';
973 974
  
  pattern = g_new (gchar, length+1);
975
  
976
  underscore = FALSE;
977
  
978
  p = q = string_wc;
979
  r = pattern;
980
  
981 982 983 984 985 986 987 988 989 990 991 992
  while (*p)
    {
      if (underscore)
	{
	  if (*p == '_')
	    *r++ = ' ';
	  else
	    {
	      *r++ = '_';
	      if (accel_key == GDK_VoidSymbol)
		accel_key = gdk_keyval_to_lower (*p);
	    }
993
	  
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
	  *q++ = *p;
	  underscore = FALSE;
	}
      else
	{
	  if (*p == '_')
	    underscore = TRUE;
	  else
	    {
	      *q++ = *p;
	      *r++ = ' ';
	    }
	}
      p++;
    }
  *q = 0;
1010
  *r = 0;
1011
  
1012
  gtk_label_set_text_internal (label, gdk_wcstombs (string_wc), string_wc);
1013 1014 1015
  gtk_label_set_pattern (label, pattern);
  
  g_free (pattern);
1016
  
1017 1018
  return accel_key;
}