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;
195 196 197
  label->label_wc = NULL;
  label->pattern = NULL;

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

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

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

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

245 246 247 248 249 250 251 252 253 254 255
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);
256 257 258 259 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);
      str_wc[wc_len] = '\0';
      
      gtk_label_set_text_internal (label, g_strdup (str), str_wc);
    }
267 268
}

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

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

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

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

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

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

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

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

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

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

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

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

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

Elliot Lee's avatar
Elliot Lee committed
949 950
  return TRUE;
}
951

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

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