gtklabel.c 20.3 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 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
  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);
}

/**
 * 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.
 * 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.
 **/
guint
gtk_label_set_markup_with_accel (GtkLabel    *label,
                                 const gchar *str)
{
Tor Lillqvist's avatar
Tor Lillqvist committed
326
  g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
327 328 329 330

  return set_markup (label, str, TRUE);
}

331 332 333 334 335 336 337 338 339 340 341 342
/**
 * 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().
 **/
gchar *
gtk_label_get_text (GtkLabel *label)
{
343 344
  g_return_val_if_fail (label != NULL, NULL);
  g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
345 346

  return g_strdup (label->label);
347 348
}

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

  gtk_widget_queue_resize (GTK_WIDGET (label));
Elliot Lee's avatar
Elliot Lee committed
359 360 361
}

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

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

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

      gtk_widget_queue_resize (GTK_WIDGET (label));
    }
Owen Taylor's avatar
Owen Taylor committed
397 398 399
}

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

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

422
  if (label->layout)
423
    g_object_unref (G_OBJECT (label->layout));
424

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

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

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

455
	  if (p > start)
456
	    {
457 458 459 460 461
	      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);
462
	    }
Owen Taylor's avatar
Owen Taylor committed
463
	  else
464
	    break;
465 466 467
	}
    }
}
468

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

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

  requisition->width = label->misc.xpad;
  requisition->height = label->misc.ypad;

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

510 511
      label->layout = gtk_widget_create_pango_layout (widget, label->label);

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

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

      if (attrs)
	{
	  pango_layout_set_attributes (label->layout, attrs);
	  pango_attr_list_unref (attrs);
	}
528
      
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
      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
552
  if (label->wrap)
Elliot Lee's avatar
Elliot Lee committed
553
    {
Owen Taylor's avatar
Owen Taylor committed
554 555
      GtkWidgetAuxInfo *aux_info;
      gint longest_paragraph;
556 557 558
      gint width, height;
      gint real_width;

Owen Taylor's avatar
Owen Taylor committed
559 560 561
      aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
      if (aux_info && aux_info->width > 0)
	{
562 563 564 565
	  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;
566
	  requisition->height += PANGO_PIXELS (logical_rect.height);
Owen Taylor's avatar
Owen Taylor committed
567 568 569
	}
      else
	{
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
	  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
594 595 596
	  if (longest_paragraph > 0)
	    {
	      gint nlines, perfect_width;
597 598

	      nlines = pango_layout_get_line_count (label->layout);
Owen Taylor's avatar
Owen Taylor committed
599
	      perfect_width = (longest_paragraph + nlines - 1) / nlines;
600 601 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
	      
	      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
630
	    }
631 632
	  pango_layout_set_width (label->layout, width);

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

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

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

  g_return_if_fail (GTK_IS_LABEL (widget));
654
  
655
  label = GTK_LABEL (widget);
656 657

  if (previous_style && label->layout)
658
    pango_layout_context_changed (label->layout);
659 660
}

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

  if (label->layout)
668
    pango_layout_context_changed (label->layout);
669 670 671 672

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

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

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

  /* 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.
   */
722
  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
723
      label->layout && label->label && (*label->label != '\0'))
Elliot Lee's avatar
Elliot Lee committed
724 725
    {
      misc = GTK_MISC (widget);
726
      
727 728 729 730
      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
	xalign = misc->xalign;
      else
	xalign = 1. - misc->xalign;
731
      
732
      x = floor (widget->allocation.x + (gint)misc->xpad
733 734
		 + ((widget->allocation.width - widget->requisition.width) * xalign)
		 + 0.5);
735 736
      
      y = floor (widget->allocation.y + (gint)misc->ypad 
737 738
		 + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
		 + 0.5);
739

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

Elliot Lee's avatar
Elliot Lee committed
751 752
  return TRUE;
}
753

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

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

769
  /* Convert text to wide characters */
770

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

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

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

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