gtklabel.c 20.4 KB
Newer Older
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Owen Taylor's avatar
Owen Taylor committed
15 16
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18 19

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

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

Elliot Lee's avatar
Elliot Lee committed
33

34 35 36
enum {
  ARG_0,
  ARG_LABEL,
37
  ARG_PATTERN,
38 39
  ARG_JUSTIFY,
  ARG_WRAP
40 41
};

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
static void gtk_label_class_init        (GtkLabelClass    *klass);
static void gtk_label_init              (GtkLabel         *label);
static void gtk_label_set_arg           (GtkObject        *object,
					 GtkArg           *arg,
					 guint             arg_id);
static void gtk_label_get_arg           (GtkObject        *object,
					 GtkArg           *arg,
					 guint             arg_id);
static void gtk_label_finalize          (GObject          *object);
static void gtk_label_size_request      (GtkWidget        *widget,
					 GtkRequisition   *requisition);
static void gtk_label_style_set         (GtkWidget        *widget,
					 GtkStyle         *previous_style);
static void gtk_label_direction_changed (GtkWidget        *widget,
					 GtkTextDirection  previous_dir);
static gint gtk_label_expose            (GtkWidget        *widget,
					 GdkEventExpose   *event);
Elliot Lee's avatar
Elliot Lee committed
59 60 61

static GtkMiscClass *parent_class = NULL;

Tim Janik's avatar
Tim Janik committed
62
GtkType
63
gtk_label_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
64
{
Tim Janik's avatar
Tim Janik committed
65 66
  static GtkType label_type = 0;
  
Elliot Lee's avatar
Elliot Lee committed
67 68
  if (!label_type)
    {
69
      static const GTypeInfo label_info =
Elliot Lee's avatar
Elliot Lee committed
70 71
      {
	sizeof (GtkLabelClass),
72 73 74 75 76 77 78 79
	NULL,           /* base_init */
	NULL,           /* base_finalize */
	(GClassInitFunc) gtk_label_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data */
	sizeof (GtkLabel),
	32,             /* n_preallocs */
	(GInstanceInitFunc) gtk_label_init,
Elliot Lee's avatar
Elliot Lee committed
80
      };
81

82
      label_type = g_type_register_static (GTK_TYPE_MISC, "GtkLabel", &label_info, 0);
Elliot Lee's avatar
Elliot Lee committed
83
    }
Tim Janik's avatar
Tim Janik committed
84
  
Elliot Lee's avatar
Elliot Lee committed
85 86 87
  return label_type;
}

88
static void
Elliot Lee's avatar
Elliot Lee committed
89 90
gtk_label_class_init (GtkLabelClass *class)
{
91
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
92 93
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
Tim Janik's avatar
Tim Janik committed
94
  
Elliot Lee's avatar
Elliot Lee committed
95 96
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
Tim Janik's avatar
Tim Janik committed
97
  
98
  parent_class = gtk_type_class (GTK_TYPE_MISC);
Tim Janik's avatar
Tim Janik committed
99
  
100
  gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
101
  gtk_object_add_arg_type ("GtkLabel::pattern", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_PATTERN);
102
  gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
103
  gtk_object_add_arg_type ("GtkLabel::wrap", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_WRAP);
104
  
105 106
  gobject_class->finalize = gtk_label_finalize;

107 108
  object_class->set_arg = gtk_label_set_arg;
  object_class->get_arg = gtk_label_get_arg;
Tim Janik's avatar
Tim Janik committed
109
  
Elliot Lee's avatar
Elliot Lee committed
110
  widget_class->size_request = gtk_label_size_request;
111
  widget_class->style_set = gtk_label_style_set;
112
  widget_class->direction_changed = gtk_label_direction_changed;
Elliot Lee's avatar
Elliot Lee committed
113 114 115
  widget_class->expose_event = gtk_label_expose;
}

116
static void
117
gtk_label_set_arg (GtkObject	  *object,
Tim Janik's avatar
Tim Janik committed
118 119
		   GtkArg	  *arg,
		   guint	   arg_id)
120
{
121
  GtkLabel *label;
122
  
123
  label = GTK_LABEL (object);
124
  
125 126 127
  switch (arg_id)
    {
    case ARG_LABEL:
128
      gtk_label_set_text (label, GTK_VALUE_STRING (*arg));
129
      break;
130 131 132
    case ARG_PATTERN:
      gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
      break;
133 134 135
    case ARG_JUSTIFY:
      gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
      break;
136 137 138
    case ARG_WRAP:
      gtk_label_set_line_wrap (label, GTK_VALUE_BOOL (*arg));
      break;	  
Tim Janik's avatar
Tim Janik committed
139 140
    default:
      break;
141 142 143
    }
}

Tim Janik's avatar
Tim Janik committed
144
static void
145
gtk_label_get_arg (GtkObject	  *object,
Tim Janik's avatar
Tim Janik committed
146 147
		   GtkArg	  *arg,
		   guint	   arg_id)
148
{
149
  GtkLabel *label;
150
  
151
  label = GTK_LABEL (object);
152
  
153 154 155 156 157
  switch (arg_id)
    {
    case ARG_LABEL:
      GTK_VALUE_STRING (*arg) = g_strdup (label->label);
      break;
158 159 160
    case ARG_PATTERN:
      GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
      break;
161 162 163
    case ARG_JUSTIFY:
      GTK_VALUE_ENUM (*arg) = label->jtype;
      break;
164 165 166
    case ARG_WRAP:
      GTK_VALUE_BOOL (*arg) = label->wrap;
      break;
167 168 169 170 171 172
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
}

173
static void
Elliot Lee's avatar
Elliot Lee committed
174 175 176
gtk_label_init (GtkLabel *label)
{
  GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
Tim Janik's avatar
Tim Janik committed
177
  
Elliot Lee's avatar
Elliot Lee committed
178
  label->label = NULL;
179 180
  label->pattern = NULL;

Elliot Lee's avatar
Elliot Lee committed
181
  label->jtype = GTK_JUSTIFY_CENTER;
Owen Taylor's avatar
Owen Taylor committed
182
  label->wrap = FALSE;
183
  
184
  label->layout = NULL;
185
  label->attrs = NULL;
186
  
Owen Taylor's avatar
Owen Taylor committed
187
  gtk_label_set_text (label, "");
Elliot Lee's avatar
Elliot Lee committed
188 189 190
}

GtkWidget*
191
gtk_label_new (const gchar *str)
Elliot Lee's avatar
Elliot Lee committed
192 193
{
  GtkLabel *label;
194
  
195 196 197 198
  label = gtk_type_new (GTK_TYPE_LABEL);

  if (str && *str)
    gtk_label_set_text (label, str);
199
  
Elliot Lee's avatar
Elliot Lee committed
200 201 202
  return GTK_WIDGET (label);
}

203
static inline void
204
gtk_label_set_text_internal (GtkLabel *label,
205
			     gchar    *str)
Elliot Lee's avatar
Elliot Lee committed
206
{
207
  g_free (label->label);
208
  
209
  label->label = str;
210
  if (label->layout)
211 212 213 214
    {
      g_object_unref (G_OBJECT (label->layout));
      label->layout = NULL;
    }
215
  
216
  gtk_widget_queue_resize (GTK_WIDGET (label));
217 218
}

219
void
220 221
gtk_label_set_text (GtkLabel    *label,
		    const gchar *str)
222 223
{
  g_return_if_fail (GTK_IS_LABEL (label));
224
  
225 226 227
  gtk_label_set_text_internal (label, g_strdup (str ? str : ""));
}

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
/**
 * gtk_label_set_attributes:
 * @label: a #GtkLabel
 * @attrs: a #PangoAttrList
 * 
 * Sets a #PangoAttrList; the attributes in the list are applied to the
 * label text.
 **/
void
gtk_label_set_attributes (GtkLabel         *label,
                          PangoAttrList    *attrs)
{
  g_return_if_fail (GTK_IS_LABEL (label));

  if (attrs)
    pango_attr_list_ref (attrs);
  
  if (label->attrs)
    pango_attr_list_unref (label->attrs);

  label->attrs = attrs;
}

static guint
set_markup (GtkLabel    *label,
            const gchar *str,
            gboolean     with_uline)
{
  gchar *text = NULL;
  GError *error = NULL;
  PangoAttrList *attrs = NULL;
  gunichar accel_char = 0;

  if (str == NULL)
    str = "";
  
  if (!pango_parse_markup (str,
                           -1,
                           with_uline ? '_' : 0,
                           &attrs,
                           &text,
                           with_uline ? &accel_char : NULL,
                           &error))
    {
      g_warning ("Failed to set label from markup due to error parsing markup: %s",
                 error->message);
      g_error_free (error);
      return 0;
    }

  if (text)
279
    gtk_label_set_text_internal (label, text);
280 281 282 283 284 285 286 287

  if (attrs)
    {
      gtk_label_set_attributes (label, attrs);
      pango_attr_list_unref (attrs);
    }

  if (accel_char != 0)
288
    return gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
  else
    return GDK_VoidSymbol;
}

/**
 * gtk_label_set_markup:
 * @label: a #GtkLabel
 * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
 * 
 * Parses @str which is marked up with the Pango text markup language,
 * setting the label's text and attribute list based on the parse results.
 **/
void
gtk_label_set_markup (GtkLabel    *label,
                      const gchar *str)
{  
  g_return_if_fail (GTK_IS_LABEL (label));

  set_markup (label, str, FALSE);
}

/**
311
 * gtk_label_set_markup_with_accel:
312 313 314 315 316 317 318 319 320
 * @label: a #GtkLabel
 * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
 * 
 * Parses @str which is marked up with the Pango text markup language,
 * setting the label's text and attribute list based on the parse results.
 * If characters in @str are preceded by an underscore, they are underlined
 * indicating that they represent a keyboard accelerator, and the GDK
 * keyval for the first underlined accelerator is returned. If there are
 * no underlines in the text, GDK_VoidSymbol will be returned.
321 322
 *
 * Return value: GDK keyval for accelerator
323 324 325 326 327
 **/
guint
gtk_label_set_markup_with_accel (GtkLabel    *label,
                                 const gchar *str)
{
Tor Lillqvist's avatar
Tor Lillqvist committed
328
  g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
329 330 331 332

  return set_markup (label, str, TRUE);
}

333 334 335 336 337 338 339 340 341
/**
 * gtk_label_get_text:
 * @label: a #GtkLabel
 * 
 * Fetches the text from a label widget
 * 
 * Return value: the text in the label widget. This value must
 * be freed with g_free().
 **/
342
G_CONST_RETURN gchar *
343 344
gtk_label_get_text (GtkLabel *label)
{
345 346
  g_return_val_if_fail (label != NULL, NULL);
  g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
347

348
  return label->label;
349 350
}

351 352 353 354 355 356
void
gtk_label_set_pattern (GtkLabel	   *label,
		       const gchar *pattern)
{
  g_return_if_fail (GTK_IS_LABEL (label));
  
357 358 359 360
  g_free (label->pattern);
  label->pattern = g_strdup (pattern);

  gtk_widget_queue_resize (GTK_WIDGET (label));
Elliot Lee's avatar
Elliot Lee committed
361 362 363
}

void
364 365
gtk_label_set_justify (GtkLabel        *label,
		       GtkJustification jtype)
Elliot Lee's avatar
Elliot Lee committed
366 367
{
  g_return_if_fail (GTK_IS_LABEL (label));
368
  g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
369
  
Elliot Lee's avatar
Elliot Lee committed
370 371 372
  if ((GtkJustification) label->jtype != jtype)
    {
      label->jtype = jtype;
373

374 375 376
      if (label->layout)
	{
	  /* No real need to be this drastic, but easier than duplicating the code */
377
          g_object_unref (G_OBJECT (label->layout));
378 379 380
	  label->layout = NULL;
	}
      
381
      gtk_widget_queue_resize (GTK_WIDGET (label));
Elliot Lee's avatar
Elliot Lee committed
382 383 384 385
    }
}

void
386 387
gtk_label_set_line_wrap (GtkLabel *label,
			 gboolean  wrap)
Owen Taylor's avatar
Owen Taylor committed
388 389
{
  g_return_if_fail (GTK_IS_LABEL (label));
390
  
391 392 393 394 395 396 397 398
  wrap = wrap != FALSE;
  
  if (label->wrap != wrap)
    {
      label->wrap = wrap;

      gtk_widget_queue_resize (GTK_WIDGET (label));
    }
Owen Taylor's avatar
Owen Taylor committed
399 400 401
}

void
402 403
gtk_label_get (GtkLabel *label,
	       gchar   **str)
Elliot Lee's avatar
Elliot Lee committed
404 405 406 407
{
  g_return_if_fail (label != NULL);
  g_return_if_fail (GTK_IS_LABEL (label));
  g_return_if_fail (str != NULL);
408
  
Elliot Lee's avatar
Elliot Lee committed
409 410 411 412
  *str = label->label;
}

static void
413
gtk_label_finalize (GObject *object)
Elliot Lee's avatar
Elliot Lee committed
414 415
{
  GtkLabel *label;
416
  
Elliot Lee's avatar
Elliot Lee committed
417
  g_return_if_fail (GTK_IS_LABEL (object));
418
  
Elliot Lee's avatar
Elliot Lee committed
419
  label = GTK_LABEL (object);
420
  
Elliot Lee's avatar
Elliot Lee committed
421
  g_free (label->label);
422 423
  g_free (label->pattern);

424
  if (label->layout)
425
    g_object_unref (G_OBJECT (label->layout));
426

427 428 429
  if (label->attrs)
    pango_attr_list_unref (label->attrs);
  
430
  G_OBJECT_CLASS (parent_class)->finalize (object);
Elliot Lee's avatar
Elliot Lee committed
431 432
}

433 434 435
static void
gtk_label_pattern_to_attrs (GtkLabel      *label,
                            PangoAttrList *attrs)
436
{
437
  if (label->pattern)
Owen Taylor's avatar
Owen Taylor committed
438
    {
439 440 441
      const char *start;
      const char *p = label->label;
      const char *q = label->pattern;
442

443
      while (1)
Owen Taylor's avatar
Owen Taylor committed
444
	{
445
	  while (*p && *q && *q != '_')
Owen Taylor's avatar
Owen Taylor committed
446
	    {
447
	      p = g_utf8_next_char (p);
448
	      q++;
Owen Taylor's avatar
Owen Taylor committed
449
	    }
450 451
	  start = p;
	  while (*p && *q && *q == '_')
452
	    {
453
	      p = g_utf8_next_char (p);
454
	      q++;
455
	    }
Owen Taylor's avatar
Owen Taylor committed
456

457
	  if (p > start)
458
	    {
459 460 461 462 463
	      PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
	      attr->start_index = start - label->label;
	      attr->end_index = p - label->label;

	      pango_attr_list_insert (attrs, attr);
464
	    }
Owen Taylor's avatar
Owen Taylor committed
465
	  else
466
	    break;
467 468 469
	}
    }
}
470

Elliot Lee's avatar
Elliot Lee committed
471 472 473 474 475
static void
gtk_label_size_request (GtkWidget      *widget,
			GtkRequisition *requisition)
{
  GtkLabel *label;
476
  PangoRectangle logical_rect;
Tim Janik's avatar
Tim Janik committed
477
  
Elliot Lee's avatar
Elliot Lee committed
478 479
  g_return_if_fail (GTK_IS_LABEL (widget));
  g_return_if_fail (requisition != NULL);
480
  
Elliot Lee's avatar
Elliot Lee committed
481
  label = GTK_LABEL (widget);
482

Owen Taylor's avatar
Owen Taylor committed
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
  /*
   * 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.
   */
503

Owen Taylor's avatar
Owen Taylor committed
504 505
  requisition->width = label->misc.xpad * 2;
  requisition->height = label->misc.ypad * 2;
506 507 508 509

  if (!label->layout)
    {
      PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
510
      PangoAttrList *attrs = NULL;
511

512 513
      label->layout = gtk_widget_create_pango_layout (widget, label->label);

514 515 516 517
      /* FIXME move to a model where the pattern isn't stored
       * permanently, and just modifes or creates the AttrList
       */
      if (label->attrs)
518 519
	attrs = pango_attr_list_copy (label->attrs);
      else if (label->pattern)
520
        attrs = pango_attr_list_new ();
521 522 523 524 525 526 527 528 529

      if (label->pattern)
	gtk_label_pattern_to_attrs (label, attrs);

      if (attrs)
	{
	  pango_layout_set_attributes (label->layout, attrs);
	  pango_attr_list_unref (attrs);
	}
530
      
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
      switch (label->jtype)
	{
	case GTK_JUSTIFY_LEFT:
	  align = PANGO_ALIGN_LEFT;
	  break;
	case GTK_JUSTIFY_RIGHT:
	  align = PANGO_ALIGN_RIGHT;
	  break;
	case GTK_JUSTIFY_CENTER:
	  align = PANGO_ALIGN_LEFT;
	  break;
	case GTK_JUSTIFY_FILL:
	  /* FIXME: This just doesn't work to do this */
	  align = PANGO_ALIGN_LEFT;
	  pango_layout_set_justify (label->layout, TRUE);
	  break;
	default:
	  g_assert_not_reached();
	}

      pango_layout_set_alignment (label->layout, align);
    }

Owen Taylor's avatar
Owen Taylor committed
554
  if (label->wrap)
Elliot Lee's avatar
Elliot Lee committed
555
    {
Owen Taylor's avatar
Owen Taylor committed
556 557
      GtkWidgetAuxInfo *aux_info;
      gint longest_paragraph;
558 559 560
      gint width, height;
      gint real_width;

Owen Taylor's avatar
Owen Taylor committed
561 562 563
      aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
      if (aux_info && aux_info->width > 0)
	{
564 565 566 567
	  pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
	  pango_layout_get_extents (label->layout, NULL, &logical_rect);

	  requisition->width += aux_info->width;
568
	  requisition->height += PANGO_PIXELS (logical_rect.height);
Owen Taylor's avatar
Owen Taylor committed
569 570 571
	}
      else
	{
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
	  pango_layout_set_width (label->layout, -1);
	  pango_layout_get_extents (label->layout, NULL, &logical_rect);
      
	  width = logical_rect.width;
	  height = logical_rect.height;
	  
	  /* Try to guess a reasonable maximum width
	   */
	  longest_paragraph = width;

	  width = MIN (width,
		       PANGO_SCALE * gdk_string_width (GTK_WIDGET (label)->style->font,
						"This long string gives a good enough length for any line to have."));
	  width = MIN (width,
		       PANGO_SCALE * (gdk_screen_width () + 1) / 2);

	  pango_layout_set_width (label->layout, width);
	  pango_layout_get_extents (label->layout, NULL, &logical_rect);
	  real_width = logical_rect.width;
	  height = logical_rect.height;
	  
	  /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
	   * so we try short search for a narrower width that leaves us with the same height
	   */
Owen Taylor's avatar
Owen Taylor committed
596 597 598
	  if (longest_paragraph > 0)
	    {
	      gint nlines, perfect_width;
599 600

	      nlines = pango_layout_get_line_count (label->layout);
Owen Taylor's avatar
Owen Taylor committed
601
	      perfect_width = (longest_paragraph + nlines - 1) / nlines;
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
	      
	      if (perfect_width < width)
		{
		  pango_layout_set_width (label->layout, perfect_width);
		  pango_layout_get_extents (label->layout, NULL, &logical_rect);
		  
		  if (logical_rect.height <= height)
		    {
		      width = perfect_width;
		      real_width = logical_rect.width;
		      height = logical_rect.height;
		    }
		  else
		    {
		      gint mid_width = (perfect_width + width) / 2;

		      if (mid_width > perfect_width)
			{
			  pango_layout_set_width (label->layout, mid_width);
			  pango_layout_get_extents (label->layout, NULL, &logical_rect);

			  if (logical_rect.height <= height)
			    {
			      width = mid_width;
			      real_width = logical_rect.width;
			      height = logical_rect.height;
			    }
			}
		    }
		}
Owen Taylor's avatar
Owen Taylor committed
632
	    }
633 634
	  pango_layout_set_width (label->layout, width);

635 636
	  requisition->width += PANGO_PIXELS (real_width);
	  requisition->height += PANGO_PIXELS (height);
Owen Taylor's avatar
Owen Taylor committed
637 638
	}
    }
639
  else				/* !label->wrap */
Owen Taylor's avatar
Owen Taylor committed
640
    {
641 642 643
      pango_layout_set_width (label->layout, -1);
      pango_layout_get_extents (label->layout, NULL, &logical_rect);

644 645
      requisition->width += PANGO_PIXELS (logical_rect.width);
      requisition->height += PANGO_PIXELS (logical_rect.height);
Owen Taylor's avatar
Owen Taylor committed
646 647
    }
}
648

649
static void 
650 651
gtk_label_style_set (GtkWidget *widget,
		     GtkStyle  *previous_style)
652 653 654 655
{
  GtkLabel *label;

  g_return_if_fail (GTK_IS_LABEL (widget));
656
  
657
  label = GTK_LABEL (widget);
658 659

  if (previous_style && label->layout)
660
    pango_layout_context_changed (label->layout);
661 662
}

663 664 665 666 667 668 669
static void 
gtk_label_direction_changed (GtkWidget        *widget,
			     GtkTextDirection previous_dir)
{
  GtkLabel *label = GTK_LABEL (widget);

  if (label->layout)
670
    pango_layout_context_changed (label->layout);
671 672 673 674

  GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
}

675
#if 0
Owen Taylor's avatar
Owen Taylor committed
676
static void
677 678 679
gtk_label_paint_word (GtkLabel     *label,
		      gint          x,
		      gint          y,
Owen Taylor's avatar
Owen Taylor committed
680 681 682 683 684 685
		      GtkLabelWord *word,
		      GdkRectangle *area)
{
  GtkWidget *widget = GTK_WIDGET (label);
  GtkLabelULine *uline;
  gchar *tmp_str;
686
  
Owen Taylor's avatar
Owen Taylor committed
687
  tmp_str = gdk_wcstombs (word->beginning);
688 689 690 691 692 693 694 695 696
  if (tmp_str)
    {
      gtk_paint_string (widget->style, widget->window, widget->state,
			area, widget, "label", 
			x + word->x,
			y + word->y,
			tmp_str);
      g_free (tmp_str);
    }
Owen Taylor's avatar
Owen Taylor committed
697
  
698
  for (uline = word->uline; uline; uline = uline->next)
Owen Taylor's avatar
Owen Taylor committed
699 700 701
    gtk_paint_hline (widget->style, widget->window, 
		     widget->state, area,
		     widget, "label", 
702
		     x + uline->x1, x + uline->x2, y + uline->y);
Elliot Lee's avatar
Elliot Lee committed
703
}
704
#endif
705

Elliot Lee's avatar
Elliot Lee committed
706
static gint
Owen Taylor's avatar
Owen Taylor committed
707
gtk_label_expose (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
708 709 710 711 712
		  GdkEventExpose *event)
{
  GtkLabel *label;
  GtkMisc *misc;
  gint x, y;
713
  gfloat xalign;
714
  
Elliot Lee's avatar
Elliot Lee committed
715 716
  g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
717
  
Arturo Espinosa's avatar
Arturo Espinosa committed
718
  label = GTK_LABEL (widget);
719 720 721 722 723

  /* if label->layout is NULL it means we got a set_text since
   * our last size request, so a resize should be queued,
   * which means a full expose is in the queue anyway.
   */
724
  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
725
      label->layout && label->label && (*label->label != '\0'))
Elliot Lee's avatar
Elliot Lee committed
726 727
    {
      misc = GTK_MISC (widget);
728
      
729 730 731 732
      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
	xalign = misc->xalign;
      else
	xalign = 1. - misc->xalign;
733
      
734
      x = floor (widget->allocation.x + (gint)misc->xpad
735 736
		 + ((widget->allocation.width - widget->requisition.width) * xalign)
		 + 0.5);
737 738
      
      y = floor (widget->allocation.y + (gint)misc->ypad 
739 740
		 + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
		 + 0.5);
741

742
      
743 744 745 746 747 748 749 750
      gtk_paint_layout (widget->style,
                        widget->window,
                        GTK_WIDGET_STATE (widget),
                        &event->area,
                        widget,
                        "label",
                        x, y,
                        label->layout);
751
    }
752

Elliot Lee's avatar
Elliot Lee committed
753 754
  return TRUE;
}
755

756
guint      
757
gtk_label_parse_uline (GtkLabel    *label,
758
		       const gchar *str)
759 760
{
  guint accel_key = GDK_VoidSymbol;
761 762

  gchar *new_str;
Owen Taylor's avatar
Owen Taylor committed
763
  gchar *pattern;
764 765
  const gchar *src;
  gchar *dest, *pattern_dest;
766
  gboolean underscore;
767
      
768
  g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
769
  g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
770

771
  /* Convert text to wide characters */
772

773
  new_str = g_new (gchar, strlen (str) + 1);
774
  pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
775
  
776
  underscore = FALSE;
777 778 779 780

  src = str;
  dest = new_str;
  pattern_dest = pattern;
781
  
782
  while (*src)
783
    {
784
      gunichar c;
785
      gchar *next_src;
786 787 788

      c = g_utf8_get_char (src);
      if (c == (gunichar)-1)
789 790 791 792 793 794
	{
	  g_warning ("Invalid input string");
	  g_free (new_str);
	  g_free (pattern);
	  return GDK_VoidSymbol;
	}
795
      next_src = g_utf8_next_char (src);
796
      
797 798
      if (underscore)
	{
799 800
	  if (c == '_')
	    *pattern_dest++ = ' ';
801 802
	  else
	    {
803
	      *pattern_dest++ = '_';
804
	      if (accel_key == GDK_VoidSymbol)
805
		accel_key = gdk_keyval_to_lower (c);
806
	    }
807 808 809

	  while (src < next_src)
	    *dest++ = *src++;
810
	  
811 812 813 814
	  underscore = FALSE;
	}
      else
	{
815 816 817 818 819
	  if (c == '_')
	    {
	      underscore = TRUE;
	      src = next_src;
	    }
820 821
	  else
	    {
822 823 824 825
	      while (src < next_src)
		*dest++ = *src++;
	  
	      *pattern_dest++ = ' ';
826 827 828
	    }
	}
    }
829 830
  *dest = 0;
  *pattern_dest = 0;
831
  
832
  gtk_label_set_text_internal (label, new_str);
833 834 835
  gtk_label_set_pattern (label, pattern);
  
  g_free (pattern);
836
  
837 838
  return accel_key;
}