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;
}