gtklabel.c 17 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
};

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

Elliot Lee's avatar
Elliot Lee committed
58 59 60

static GtkMiscClass *parent_class = NULL;

Tim Janik's avatar
Tim Janik committed
61
GtkType
62
gtk_label_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
63
{
Tim Janik's avatar
Tim Janik committed
64 65
  static GtkType label_type = 0;
  
Elliot Lee's avatar
Elliot Lee committed
66 67
  if (!label_type)
    {
68
      static const GTypeInfo label_info =
Elliot Lee's avatar
Elliot Lee committed
69 70
      {
	sizeof (GtkLabelClass),
71 72 73 74 75 76 77 78
	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
79
      };
80 81

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

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

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

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

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

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

Elliot Lee's avatar
Elliot Lee committed
179
  label->jtype = GTK_JUSTIFY_CENTER;
Owen Taylor's avatar
Owen Taylor committed
180
  label->wrap = FALSE;
181 182 183

  label->layout = NULL;
  label->rtl = (gtk_widget_get_direction (GTK_WIDGET (label)) == GTK_TEXT_DIR_RTL);
184
  
Owen Taylor's avatar
Owen Taylor committed
185
  gtk_label_set_text (label, "");
Elliot Lee's avatar
Elliot Lee committed
186 187 188
}

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

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

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

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

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

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
  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)
{
235 236
  g_return_val_if_fail (label != NULL, NULL);
  g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
237 238

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return attrs;
359
}
360

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

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

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

  /* Detect direction changes. FIXME: make this a signal
   */
  if (label->rtl != (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) &&
      label->layout)
    {
      label->rtl = !label->rtl;
403
      g_object_unref (G_OBJECT (label->layout));
404 405
      label->layout = NULL;
    }
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 431 432 433 434 435 436 437 438 439 440
  if (!label->layout)
    {
      PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
      PangoAttrList *attrs = gtk_label_pattern_to_attrs (label);

      label->layout = gtk_widget_create_pango_layout (widget);
      pango_layout_set_attributes (label->layout, attrs);
      pango_attr_list_unref (attrs);

      pango_layout_set_text (label->layout, label->label, -1);

      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
441
  if (label->wrap)
Elliot Lee's avatar
Elliot Lee committed
442
    {
Owen Taylor's avatar
Owen Taylor committed
443 444
      GtkWidgetAuxInfo *aux_info;
      gint longest_paragraph;
445 446 447
      gint width, height;
      gint real_width;

Owen Taylor's avatar
Owen Taylor committed
448 449 450
      aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
      if (aux_info && aux_info->width > 0)
	{
451 452 453 454 455
	  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;
	  requisition->height += logical_rect.height / PANGO_SCALE;
Owen Taylor's avatar
Owen Taylor committed
456 457 458
	}
      else
	{
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	  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
483 484 485
	  if (longest_paragraph > 0)
	    {
	      gint nlines, perfect_width;
486 487

	      nlines = pango_layout_get_line_count (label->layout);
Owen Taylor's avatar
Owen Taylor committed
488
	      perfect_width = (longest_paragraph + nlines - 1) / nlines;
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
	      
	      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
519
	    }
520 521 522 523
	  pango_layout_set_width (label->layout, width);

	  requisition->width += real_width / PANGO_SCALE;
	  requisition->height += height / PANGO_SCALE;
Owen Taylor's avatar
Owen Taylor committed
524 525
	}
    }
526
  else				/* !label->wrap */
Owen Taylor's avatar
Owen Taylor committed
527
    {
528 529 530 531 532
      pango_layout_set_width (label->layout, -1);
      pango_layout_get_extents (label->layout, NULL, &logical_rect);

      requisition->width += logical_rect.width / PANGO_SCALE;
      requisition->height += logical_rect.height / PANGO_SCALE;
Owen Taylor's avatar
Owen Taylor committed
533 534
    }
}
535

536
static void 
537 538
gtk_label_style_set (GtkWidget *widget,
		     GtkStyle  *previous_style)
539 540 541 542
{
  GtkLabel *label;

  g_return_if_fail (GTK_IS_LABEL (widget));
543
  
544
  label = GTK_LABEL (widget);
545 546 547

  if (previous_style && label->layout)
    {
548
      g_object_unref (G_OBJECT (label->layout));
549 550
      label->layout = NULL;
    }
551 552
}

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

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

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

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

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

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

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

646
  /* Convert text to wide characters */
647

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

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

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

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