gtklabel.c 17.2 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 20 21 22 23 24 25

/*
 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
 * 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);
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
  
Owen Taylor's avatar
Owen Taylor committed
186
  gtk_label_set_text (label, "");
Elliot Lee's avatar
Elliot Lee committed
187 188 189
}

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

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

202
static inline void
203
gtk_label_set_text_internal (GtkLabel *label,
204
			     gchar    *str)
Elliot Lee's avatar
Elliot Lee committed
205
{
206
  g_free (label->label);
207

208
  label->label = str;
209 210
  if (label->layout)
    pango_layout_set_text (label->layout, str, -1);
211
  
212
  gtk_widget_queue_resize (GTK_WIDGET (label));
213 214
}

215
void
216 217
gtk_label_set_text (GtkLabel    *label,
		    const gchar *str)
218 219
{
  g_return_if_fail (GTK_IS_LABEL (label));
220

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
  gtk_label_set_text_internal (label, g_strdup (str ? str : ""));
}

/**
 * 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)
{
236 237
  g_return_val_if_fail (label != NULL, NULL);
  g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
238 239

  return g_strdup (label->label);
240 241
}

242 243 244 245 246 247
void
gtk_label_set_pattern (GtkLabel	   *label,
		       const gchar *pattern)
{
  g_return_if_fail (GTK_IS_LABEL (label));
  
248 249 250 251
  g_free (label->pattern);
  label->pattern = g_strdup (pattern);

  gtk_widget_queue_resize (GTK_WIDGET (label));
Elliot Lee's avatar
Elliot Lee committed
252 253 254
}

void
255 256
gtk_label_set_justify (GtkLabel        *label,
		       GtkJustification jtype)
Elliot Lee's avatar
Elliot Lee committed
257 258
{
  g_return_if_fail (GTK_IS_LABEL (label));
259
  g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
260
  
Elliot Lee's avatar
Elliot Lee committed
261 262 263
  if ((GtkJustification) label->jtype != jtype)
    {
      label->jtype = jtype;
264

265 266 267
      if (label->layout)
	{
	  /* No real need to be this drastic, but easier than duplicating the code */
268
          g_object_unref (G_OBJECT (label->layout));
269 270 271
	  label->layout = NULL;
	}
      
272
      gtk_widget_queue_resize (GTK_WIDGET (label));
Elliot Lee's avatar
Elliot Lee committed
273 274 275 276
    }
}

void
277 278
gtk_label_set_line_wrap (GtkLabel *label,
			 gboolean  wrap)
Owen Taylor's avatar
Owen Taylor committed
279 280
{
  g_return_if_fail (GTK_IS_LABEL (label));
281
  
282 283 284 285 286 287 288 289
  wrap = wrap != FALSE;
  
  if (label->wrap != wrap)
    {
      label->wrap = wrap;

      gtk_widget_queue_resize (GTK_WIDGET (label));
    }
Owen Taylor's avatar
Owen Taylor committed
290 291 292
}

void
293 294
gtk_label_get (GtkLabel *label,
	       gchar   **str)
Elliot Lee's avatar
Elliot Lee committed
295 296 297 298
{
  g_return_if_fail (label != NULL);
  g_return_if_fail (GTK_IS_LABEL (label));
  g_return_if_fail (str != NULL);
299
  
Elliot Lee's avatar
Elliot Lee committed
300 301 302 303
  *str = label->label;
}

static void
304
gtk_label_finalize (GObject *object)
Elliot Lee's avatar
Elliot Lee committed
305 306
{
  GtkLabel *label;
307
  
Elliot Lee's avatar
Elliot Lee committed
308
  g_return_if_fail (GTK_IS_LABEL (object));
309
  
Elliot Lee's avatar
Elliot Lee committed
310
  label = GTK_LABEL (object);
311
  
Elliot Lee's avatar
Elliot Lee committed
312
  g_free (label->label);
313 314
  g_free (label->pattern);

315
  if (label->layout)
316
    g_object_unref (G_OBJECT (label->layout));
317

318
  G_OBJECT_CLASS (parent_class)->finalize (object);
Elliot Lee's avatar
Elliot Lee committed
319 320
}

321 322
static PangoAttrList *
gtk_label_pattern_to_attrs (GtkLabel *label)
323
{
324
  PangoAttrList *attrs = pango_attr_list_new ();
325

326
  if (label->pattern)
Owen Taylor's avatar
Owen Taylor committed
327
    {
328 329 330
      const char *start;
      const char *p = label->label;
      const char *q = label->pattern;
331

332
      while (1)
Owen Taylor's avatar
Owen Taylor committed
333
	{
334
	  while (*p && *q && *q != '_')
Owen Taylor's avatar
Owen Taylor committed
335
	    {
336
	      p = g_utf8_next_char (p);
337
	      q++;
Owen Taylor's avatar
Owen Taylor committed
338
	    }
339 340
	  start = p;
	  while (*p && *q && *q == '_')
341
	    {
342
	      p = g_utf8_next_char (p);
343
	      q++;
344
	    }
Owen Taylor's avatar
Owen Taylor committed
345

346
	  if (p > start)
347
	    {
348 349 350 351 352
	      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);
353
	    }
Owen Taylor's avatar
Owen Taylor committed
354
	  else
355
	    break;
356 357
	}
    }
358 359

  return attrs;
360
}
361

Elliot Lee's avatar
Elliot Lee committed
362 363 364 365 366
static void
gtk_label_size_request (GtkWidget      *widget,
			GtkRequisition *requisition)
{
  GtkLabel *label;
367
  PangoRectangle logical_rect;
Tim Janik's avatar
Tim Janik committed
368
  
Elliot Lee's avatar
Elliot Lee committed
369 370
  g_return_if_fail (GTK_IS_LABEL (widget));
  g_return_if_fail (requisition != NULL);
371
  
Elliot Lee's avatar
Elliot Lee committed
372
  label = GTK_LABEL (widget);
373

Owen Taylor's avatar
Owen Taylor committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
  /*
   * 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.
   */
394 395 396 397 398 399 400 401 402

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

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

403 404
      label->layout = gtk_widget_create_pango_layout (widget, label->label);

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
      pango_layout_set_attributes (label->layout, attrs);
      pango_attr_list_unref (attrs);

      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
431
  if (label->wrap)
Elliot Lee's avatar
Elliot Lee committed
432
    {
Owen Taylor's avatar
Owen Taylor committed
433 434
      GtkWidgetAuxInfo *aux_info;
      gint longest_paragraph;
435 436 437
      gint width, height;
      gint real_width;

Owen Taylor's avatar
Owen Taylor committed
438 439 440
      aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
      if (aux_info && aux_info->width > 0)
	{
441 442 443 444
	  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;
445
	  requisition->height += PANGO_PIXELS (logical_rect.height);
Owen Taylor's avatar
Owen Taylor committed
446 447 448
	}
      else
	{
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
	  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
473 474 475
	  if (longest_paragraph > 0)
	    {
	      gint nlines, perfect_width;
476 477

	      nlines = pango_layout_get_line_count (label->layout);
Owen Taylor's avatar
Owen Taylor committed
478
	      perfect_width = (longest_paragraph + nlines - 1) / nlines;
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
	      
	      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
509
	    }
510 511
	  pango_layout_set_width (label->layout, width);

512 513
	  requisition->width += PANGO_PIXELS (real_width);
	  requisition->height += PANGO_PIXELS (height);
Owen Taylor's avatar
Owen Taylor committed
514 515
	}
    }
516
  else				/* !label->wrap */
Owen Taylor's avatar
Owen Taylor committed
517
    {
518 519 520
      pango_layout_set_width (label->layout, -1);
      pango_layout_get_extents (label->layout, NULL, &logical_rect);

521 522
      requisition->width += PANGO_PIXELS (logical_rect.width);
      requisition->height += PANGO_PIXELS (logical_rect.height);
Owen Taylor's avatar
Owen Taylor committed
523 524
    }
}
525

526
static void 
527 528
gtk_label_style_set (GtkWidget *widget,
		     GtkStyle  *previous_style)
529 530 531 532
{
  GtkLabel *label;

  g_return_if_fail (GTK_IS_LABEL (widget));
533
  
534
  label = GTK_LABEL (widget);
535 536

  if (previous_style && label->layout)
537
    pango_layout_context_changed (label->layout);
538 539
}

540 541 542 543 544 545 546
static void 
gtk_label_direction_changed (GtkWidget        *widget,
			     GtkTextDirection previous_dir)
{
  GtkLabel *label = GTK_LABEL (widget);

  if (label->layout)
547
    pango_layout_context_changed (label->layout);
548 549 550 551

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

552
#if 0
Owen Taylor's avatar
Owen Taylor committed
553
static void
554 555 556
gtk_label_paint_word (GtkLabel     *label,
		      gint          x,
		      gint          y,
Owen Taylor's avatar
Owen Taylor committed
557 558 559 560 561 562
		      GtkLabelWord *word,
		      GdkRectangle *area)
{
  GtkWidget *widget = GTK_WIDGET (label);
  GtkLabelULine *uline;
  gchar *tmp_str;
563
  
Owen Taylor's avatar
Owen Taylor committed
564
  tmp_str = gdk_wcstombs (word->beginning);
565 566 567 568 569 570 571 572 573
  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
574
  
575
  for (uline = word->uline; uline; uline = uline->next)
Owen Taylor's avatar
Owen Taylor committed
576 577 578
    gtk_paint_hline (widget->style, widget->window, 
		     widget->state, area,
		     widget, "label", 
579
		     x + uline->x1, x + uline->x2, y + uline->y);
Elliot Lee's avatar
Elliot Lee committed
580
}
581
#endif
582

Elliot Lee's avatar
Elliot Lee committed
583
static gint
Owen Taylor's avatar
Owen Taylor committed
584
gtk_label_expose (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
585 586 587 588 589
		  GdkEventExpose *event)
{
  GtkLabel *label;
  GtkMisc *misc;
  gint x, y;
590
  gfloat xalign;
591
  
Elliot Lee's avatar
Elliot Lee committed
592 593
  g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
594
  
Arturo Espinosa's avatar
Arturo Espinosa committed
595
  label = GTK_LABEL (widget);
Tim Janik's avatar
Tim Janik committed
596
  
597 598
  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
      label->label && (*label->label != '\0'))
Elliot Lee's avatar
Elliot Lee committed
599 600
    {
      misc = GTK_MISC (widget);
601
      
602 603 604 605 606
      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
	xalign = misc->xalign;
      else
	xalign = 1. - misc->xalign;

607 608
      /*
       * GC Clipping
Elliot Lee's avatar
Elliot Lee committed
609
       */
610
      gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
611
      gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
612
      
613
      x = floor (widget->allocation.x + (gint)misc->xpad
614 615
		 + ((widget->allocation.width - widget->requisition.width) * xalign)
		 + 0.5);
616 617
      
      y = floor (widget->allocation.y + (gint)misc->ypad 
618 619
		 + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
		 + 0.5);
620

621
      gdk_draw_layout (widget->window, widget->style->fg_gc [widget->state], x, y, label->layout);
622
      
Elliot Lee's avatar
Elliot Lee committed
623 624
      gdk_gc_set_clip_rectangle (widget->style->white_gc, NULL);
      gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], NULL);
625
    }
626

Elliot Lee's avatar
Elliot Lee committed
627 628
  return TRUE;
}
629

630
guint      
631
gtk_label_parse_uline (GtkLabel    *label,
632
		       const gchar *str)
633 634
{
  guint accel_key = GDK_VoidSymbol;
635 636

  gchar *new_str;
Owen Taylor's avatar
Owen Taylor committed
637
  gchar *pattern;
638 639
  const gchar *src;
  gchar *dest, *pattern_dest;
640
  gboolean underscore;
641
      
642
  g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
643
  g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
644

645
  /* Convert text to wide characters */
646

647
  new_str = g_new (gchar, strlen (str) + 1);
648
  pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
649
  
650
  underscore = FALSE;
651 652 653 654

  src = str;
  dest = new_str;
  pattern_dest = pattern;
655
  
656
  while (*src)
657
    {
658
      gunichar c;
659
      gchar *next_src;
660 661 662

      c = g_utf8_get_char (src);
      if (c == (gunichar)-1)
663 664 665 666 667 668
	{
	  g_warning ("Invalid input string");
	  g_free (new_str);
	  g_free (pattern);
	  return GDK_VoidSymbol;
	}
669
      next_src = g_utf8_next_char (src);
670
      
671 672
      if (underscore)
	{
673 674
	  if (c == '_')
	    *pattern_dest++ = ' ';
675 676
	  else
	    {
677
	      *pattern_dest++ = '_';
678
	      if (accel_key == GDK_VoidSymbol)
679
		accel_key = gdk_keyval_to_lower (c);
680
	    }
681 682 683

	  while (src < next_src)
	    *dest++ = *src++;
684
	  
685 686 687 688
	  underscore = FALSE;
	}
      else
	{
689 690 691 692 693
	  if (c == '_')
	    {
	      underscore = TRUE;
	      src = next_src;
	    }
694 695
	  else
	    {
696 697 698 699
	      while (src < next_src)
		*dest++ = *src++;
	  
	      *pattern_dest++ = ' ';
700 701 702
	    }
	}
    }
703 704
  *dest = 0;
  *pattern_dest = 0;
705
  
706
  gtk_label_set_text_internal (label, new_str);
707 708 709
  gtk_label_set_pattern (label, pattern);
  
  g_free (pattern);
710
  
711 712
  return accel_key;
}