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

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

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

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

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

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


static GtkMiscClass *parent_class = NULL;

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

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

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

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

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

189
static void
Elliot Lee's avatar
Elliot Lee committed
190 191 192
gtk_label_init (GtkLabel *label)
{
  GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
Tim Janik's avatar
Tim Janik committed
193
  
Elliot Lee's avatar
Elliot Lee committed
194
  label->label = NULL;
Owen Taylor's avatar
Owen Taylor committed
195
  label->words = NULL;
Tim Janik's avatar
Tim Janik committed
196
  label->max_width = 0;
Elliot Lee's avatar
Elliot Lee committed
197
  label->jtype = GTK_JUSTIFY_CENTER;
198
  label->pattern = NULL;
Owen Taylor's avatar
Owen Taylor committed
199
  label->wrap = FALSE;
200
  
Owen Taylor's avatar
Owen Taylor committed
201
  gtk_label_set_text (label, "");
Elliot Lee's avatar
Elliot Lee committed
202 203 204
}

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

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

242 243 244 245 246 247 248 249 250 251 252
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);
253 254 255 256 257 258 259 260 261 262 263

  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);
    }
264 265
}

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

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

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

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

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

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

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

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

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

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

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

Owen Taylor's avatar
Owen Taylor committed
872
static void
873 874 875
gtk_label_paint_word (GtkLabel     *label,
		      gint          x,
		      gint          y,
Owen Taylor's avatar
Owen Taylor committed
876 877 878 879 880 881
		      GtkLabelWord *word,
		      GdkRectangle *area)
{
  GtkWidget *widget = GTK_WIDGET (label);
  GtkLabelULine *uline;
  gchar *tmp_str;
882
  
Owen Taylor's avatar
Owen Taylor committed
883 884 885 886 887 888 889 890
  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);
  
891
  for (uline = word->uline; uline; uline = uline->next)
Owen Taylor's avatar
Owen Taylor committed
892 893 894
    gtk_paint_hline (widget->style, widget->window, 
		     widget->state, area,
		     widget, "label", 
895 896 897
		     x + uline->x1, x + uline->x2, y + uline->y);
  
  
Elliot Lee's avatar
Elliot Lee committed
898
}
899

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

Elliot Lee's avatar
Elliot Lee committed
946 947
  return TRUE;
}
948

949
guint      
950 951
gtk_label_parse_uline (GtkLabel    *label,
		       const gchar *string)
952 953
{
  guint accel_key = GDK_VoidSymbol;
954
  GdkWChar *p, *q, *string_wc;
Owen Taylor's avatar
Owen Taylor committed
955 956
  gchar *r;
  gchar *pattern;
957
  gint length, wc_length;
958
  gboolean underscore;
959
  
960 961
  g_return_val_if_fail(string != NULL, GDK_VoidSymbol);

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