nautilus-label.c 54 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/* nautilus-label.c - A widget to display a anti aliased text.

   Copyright (C) 1999, 2000 Eazel, Inc.

   The Gnome 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.

   The Gnome Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Authors: Ramiro Estrugo <ramiro@eazel.com>
*/

#include <config.h>
26

27
28
#include "nautilus-label.h"

29
#include <libgnome/gnome-i18n.h>
30
31
#include "nautilus-gtk-macros.h"
#include "nautilus-gdk-extensions.h"
32
#include "nautilus-gtk-extensions.h"
33
#include "nautilus-gdk-pixbuf-extensions.h"
34
#include "nautilus-art-gtk-extensions.h"
35
#include "nautilus-string.h"
36
#include "nautilus-text-layout.h"
37
38
39
40
41
42
#include "nautilus-debug-drawing.h"

/* These are arbitrary constants to catch insane values */
#define MIN_SMOOTH_FONT_SIZE 5
#define MAX_SMOOTH_FONT_SIZE 64

43
#define DEFAULT_FONT_SIZE 14
44
45
46
47
48
49
50
#define LINE_WRAP_SEPARATORS _(" -_,;.?/&")
#define LINE_OFFSET 2

#define SMOOTH_FONT_MULTIPLIER 1.3

/* This magic string is copied from GtkLabel.  It lives there unlocalized as well. */
#define DEFAULT_LINE_WRAP_WIDTH_TEXT "This is a good enough length for any line to have."
51
52
53
54
55

/* Arguments */
enum
{
	ARG_0,
56
57
	
	/* Deal with the GtkLabel arguments as well */
58
	ARG_LABEL,
59
60
61
62
63
64
65
66
67
68
69
70
	ARG_WRAP,
	ARG_JUSTIFY,

	ARG_BACKGROUND_MODE,
	ARG_IS_SMOOTH,
	ARG_TEXT_OPACITY,
	ARG_SMOOTH_FONT,
	ARG_SMOOTH_FONT_SIZE,
	ARG_SMOOTH_TEXT_COLOR,
	ARG_SMOOTH_DROP_SHADOW_OFFSET,
	ARG_SMOOTH_DROP_SHADOW_COLOR,
	ARG_SMOOTH_LINE_WRAP_WIDTH,
71
	ARG_ADJUST_WRAP_ON_RESIZE,
72
73
74
75
76
77
78

	ARG_TILE_HEIGHT,
	ARG_TILE_MODE_HORIZONTAL,
	ARG_TILE_MODE_VERTICAL,
	ARG_TILE_OPACITY,
	ARG_TILE_PIXBUF,
	ARG_TILE_WIDTH
79
80
};

81
82
83
84
85
86
87
88
89
90
91
/* Signals */
typedef enum
{
	DRAW_BACKGROUND,
	SET_IS_SMOOTH,
	LAST_SIGNAL
} LabelSignal;

/* Signals */
static guint label_signals[LAST_SIGNAL] = { 0 };

92
/* Detail member struct */
93
struct _NautilusLabelDetails
94
{
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
	gboolean is_smooth;

	/* Tile attributes */
	GdkPixbuf *tile_pixbuf;
	int tile_opacity;
	int tile_width;
	int tile_height;
	NautilusSmoothTileMode tile_mode_vertical;
	NautilusSmoothTileMode tile_mode_horizontal;

 	/* Smooth attributes */
 	NautilusScalableFont *smooth_font;
 	guint smooth_font_size;
	guint32 smooth_text_color;
	guint smooth_drop_shadow_offset;
	guint32 smooth_drop_shadow_color;
111
 	int smooth_line_wrap_width;
112
	gboolean adjust_wrap_on_resize;
113

114
	/* Text */
115
  	int text_opacity;
116
117

	/* Text lines */
118
	NautilusDimensions *text_line_dimensions;
119
120
121
	int num_text_lines;
	int max_text_line_width;
	int total_text_line_height;
122
123

	/* Line wrapping */
124
125
126
127
128
129
130
	NautilusTextLayout **text_layouts;
	
	/* Background */
	NautilusSmoothBackgroundMode background_mode;
	guint32 solid_background_color;

	GdkPixbuf *solid_cache_pixbuf;
131
	gboolean never_smooth;
132
133
134
};

/* GtkObjectClass methods */
135
136
137
138
139
140
141
142
143
static void               nautilus_label_initialize_class             (NautilusLabelClass  *label_class);
static void               nautilus_label_initialize                   (NautilusLabel       *label);
static void               nautilus_label_destroy                      (GtkObject           *object);
static void               nautilus_label_set_arg                      (GtkObject           *object,
								       GtkArg              *arg,
								       guint                arg_id);
static void               nautilus_label_get_arg                      (GtkObject           *object,
								       GtkArg              *arg,
								       guint                arg_id);
144

145
/* GtkWidgetClass methods */
146
147
148
149
150
151
static void               nautilus_label_size_request                 (GtkWidget           *widget,
								       GtkRequisition      *requisition);
static void               nautilus_label_size_allocate                (GtkWidget           *widget,
								       GtkAllocation       *allocation);
static int                nautilus_label_expose_event                 (GtkWidget           *widget,
								       GdkEventExpose      *event);
152

153
/* NautilusLabel signals */
154
155
static void               nautilus_label_set_is_smooth_signal         (GtkWidget           *widget,
								       gboolean             is_smooth);
156

157
/* Private NautilusLabel things */
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
const char *              label_peek_text                             (const NautilusLabel *label);
static void               label_line_geometries_recompute             (NautilusLabel       *label);
static void               label_line_geometries_clear                 (NautilusLabel       *label);
static guint              label_get_empty_line_height                 (const NautilusLabel *label);
static guint              label_get_total_text_and_line_offset_height (const NautilusLabel *label);
static ArtIRect           label_get_text_bounds                       (const NautilusLabel *label);
static NautilusDimensions label_get_text_dimensions                   (const NautilusLabel *label);
static NautilusDimensions label_get_tile_dimensions                   (const NautilusLabel *label);
static void               label_draw_text_to_pixbuf                   (NautilusLabel       *label,
								       GdkPixbuf           *pixbuf,
								       const ArtIRect      *destination_area,
								       int                  x,
								       int                  y);
static int                label_get_default_line_wrap_width           (const NautilusLabel *label);
static void               label_solid_cache_pixbuf_clear              (NautilusLabel       *label);
static gboolean           label_can_cache_contents                    (const NautilusLabel *label);
static gboolean           label_is_smooth                             (const NautilusLabel *label);
175
176

NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusLabel, nautilus_label, GTK_TYPE_LABEL)
177
178
179
180
181

/* Class init methods */
static void
nautilus_label_initialize_class (NautilusLabelClass *label_class)
{
182
183
184
185
186
187
188
189
190
191
	GtkObjectClass *object_class = GTK_OBJECT_CLASS (label_class);
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (label_class);

	/* GtkObjectClass */
	object_class->destroy = nautilus_label_destroy;
	object_class->set_arg = nautilus_label_set_arg;
	object_class->get_arg = nautilus_label_get_arg;
	
	/* GtkWidgetClass */
	widget_class->size_request = nautilus_label_size_request;
192
	widget_class->size_allocate = nautilus_label_size_allocate;
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
	widget_class->expose_event = nautilus_label_expose_event;

	/* NautilusLabelClass */
	label_class->set_is_smooth = nautilus_label_set_is_smooth_signal;

	/* Signals */
	label_signals[DRAW_BACKGROUND] = gtk_signal_new ("draw_background",
							 GTK_RUN_LAST,
							 object_class->type,
							 0,
							 gtk_marshal_NONE__POINTER_POINTER,
							 GTK_TYPE_NONE, 
							 2,
							 GTK_TYPE_POINTER,
							 GTK_TYPE_POINTER);

	label_signals[SET_IS_SMOOTH] = gtk_signal_new ("set_is_smooth",
						       GTK_RUN_LAST,
						       object_class->type,
						       GTK_SIGNAL_OFFSET (NautilusLabelClass, set_is_smooth),
						       gtk_marshal_NONE__BOOL,
						       GTK_TYPE_NONE, 
						       1,
						       GTK_TYPE_BOOL);

	gtk_object_class_add_signals (object_class, label_signals, LAST_SIGNAL);
219
220

	/* Arguments */
221
222
	gtk_object_add_arg_type ("NautilusLabel::tile_pixbuf",
				 GTK_TYPE_POINTER,
223
				 GTK_ARG_READWRITE,
224
225
226
				 ARG_TILE_PIXBUF);
	gtk_object_add_arg_type ("NautilusLabel::tile_opacity",
				 GTK_TYPE_INT,
227
				 GTK_ARG_READWRITE,
228
229
230
231
232
233
234
235
236
237
				 ARG_TILE_OPACITY);
	gtk_object_add_arg_type ("NautilusLabel::tile_width",
				 GTK_TYPE_INT,
				 GTK_ARG_READWRITE,
				 ARG_TILE_WIDTH);
	gtk_object_add_arg_type ("NautilusLabel::tile_height",
				 GTK_TYPE_INT,
				 GTK_ARG_READWRITE,
				 ARG_TILE_HEIGHT);
	gtk_object_add_arg_type ("NautilusLabel::tile_mode_vertical",
238
239
				 GTK_TYPE_UINT,
				 GTK_ARG_READWRITE,
240
241
242
243
244
				 ARG_TILE_MODE_VERTICAL);
	gtk_object_add_arg_type ("NautilusLabel::tile_mode_horizontal",
				 GTK_TYPE_UINT,
				 GTK_ARG_READWRITE,
				 ARG_TILE_MODE_HORIZONTAL);
245
	gtk_object_add_arg_type ("NautilusLabel::label",
246
				 GTK_TYPE_STRING,
247
248
				 GTK_ARG_READWRITE,
				 ARG_LABEL);
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
279
280
281
282
283
284
285
286
287
288
289
	gtk_object_add_arg_type ("NautilusLabel::wrap",
				 GTK_TYPE_BOOL,
				 GTK_ARG_READWRITE,
				 ARG_WRAP);
	gtk_object_add_arg_type ("NautilusLabel::justify",
				 GTK_TYPE_JUSTIFICATION,
				 GTK_ARG_READWRITE,
				 ARG_JUSTIFY);
	gtk_object_add_arg_type ("NautilusLabel::is_smooth",
				 GTK_TYPE_BOOL,
				 GTK_ARG_READWRITE,
				 ARG_IS_SMOOTH);
	gtk_object_add_arg_type ("NautilusLabel::text_opacity",
				 GTK_TYPE_INT,
				 GTK_ARG_READWRITE,
				 ARG_TEXT_OPACITY);
	gtk_object_add_arg_type ("NautilusLabel::background_mode",
				 GTK_TYPE_UINT,
				 GTK_ARG_READWRITE,
				 ARG_BACKGROUND_MODE);
	gtk_object_add_arg_type ("NautilusLabel::smooth_font",
				 GTK_TYPE_OBJECT,
				 GTK_ARG_READWRITE,
				 ARG_SMOOTH_FONT);
	gtk_object_add_arg_type ("NautilusLabel::smooth_font_size",
				 GTK_TYPE_UINT,
				 GTK_ARG_READWRITE,
				 ARG_SMOOTH_FONT_SIZE);
	gtk_object_add_arg_type ("NautilusLabel::smooth_text_color",
				 GTK_TYPE_UINT,
				 GTK_ARG_READWRITE,
				 ARG_SMOOTH_TEXT_COLOR);
	gtk_object_add_arg_type ("NautilusLabel::smooth_drop_shadow_offset",
				 GTK_TYPE_UINT,
				 GTK_ARG_READWRITE,
				 ARG_SMOOTH_DROP_SHADOW_OFFSET);
	gtk_object_add_arg_type ("NautilusLabel::smooth_drop_shadow_color",
				 GTK_TYPE_UINT,
				 GTK_ARG_READWRITE,
				 ARG_SMOOTH_DROP_SHADOW_COLOR);
	gtk_object_add_arg_type ("NautilusLabel::smooth_line_wrap_width",
290
				 GTK_TYPE_INT,
291
292
				 GTK_ARG_READWRITE,
				 ARG_SMOOTH_LINE_WRAP_WIDTH);
293

294
295
	/* Make this class inherit the same kind of theme stuff as GtkLabel */
	nautilus_gtk_class_name_make_like_existing_type ("NautilusLabel", GTK_TYPE_LABEL);
296
297
298

	/* Let the smooth widget machinery know that our class can be smooth */
	nautilus_smooth_widget_register_type (NAUTILUS_TYPE_LABEL);
299
300
301
302
303
}

void
nautilus_label_initialize (NautilusLabel *label)
{
304
305
306
	GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
	GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);

307
	label->details = g_new0 (NautilusLabelDetails, 1);
308

309
310
311
312
313
314
	label->details->text_opacity = NAUTILUS_OPACITY_FULLY_OPAQUE;
	label->details->smooth_font = nautilus_scalable_font_get_default_font ();
 	label->details->smooth_font_size = DEFAULT_FONT_SIZE;
	label->details->smooth_text_color = NAUTILUS_RGBA_COLOR_PACK (0, 0, 0, 255);
	label->details->smooth_drop_shadow_color = NAUTILUS_RGBA_COLOR_PACK (255, 255, 255, 255);
	label->details->smooth_line_wrap_width = label_get_default_line_wrap_width (label);
315

316
317
318
319
320
321
	label->details->tile_opacity = NAUTILUS_OPACITY_FULLY_OPAQUE;
 	label->details->tile_width = NAUTILUS_SMOOTH_TILE_EXTENT_FULL;
 	label->details->tile_height = NAUTILUS_SMOOTH_TILE_EXTENT_FULL;
	label->details->tile_mode_vertical = NAUTILUS_SMOOTH_TILE_SELF;
	label->details->tile_mode_horizontal = NAUTILUS_SMOOTH_TILE_SELF;
	label->details->background_mode = NAUTILUS_SMOOTH_BACKGROUND_GTK;
322
323

	nautilus_smooth_widget_register (GTK_WIDGET (label));
324
325
326
327
328
329
330
331
332
333
334
335
}

/* GtkObjectClass methods */
static void
nautilus_label_destroy (GtkObject *object)
{
 	NautilusLabel *label;
	
	g_return_if_fail (NAUTILUS_IS_LABEL (object));

	label = NAUTILUS_LABEL (object);

336
337
	nautilus_gdk_pixbuf_unref_if_not_null (label->details->tile_pixbuf);
	label->details->tile_pixbuf = NULL;
338
	label_solid_cache_pixbuf_clear (label);
339

340
341
	label_line_geometries_clear (label);
		
342
	g_free (label->details);
343
344

	/* Chain destroy */
345
	NAUTILUS_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
346
347
348
}

static void
349
350
351
nautilus_label_set_arg (GtkObject *object,
			GtkArg *arg,
			guint arg_id)
352
{
353
	NautilusLabel *label;
354
355
356
357
358
359
360

	g_return_if_fail (NAUTILUS_IS_LABEL (object));

 	label = NAUTILUS_LABEL (object);

 	switch (arg_id)
	{
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375

	case ARG_TILE_OPACITY:
		nautilus_label_set_tile_opacity (label, GTK_VALUE_INT (*arg));
		break;

	case ARG_TILE_PIXBUF:
		nautilus_label_set_tile_pixbuf (label, (GdkPixbuf *) GTK_VALUE_POINTER (*arg));
		break;
		
	case ARG_TILE_WIDTH:
		nautilus_label_set_tile_width (label, GTK_VALUE_INT (*arg));
		break;

	case ARG_TILE_HEIGHT:
		nautilus_label_set_tile_height (label, GTK_VALUE_INT (*arg));
376
377
		break;

378
379
	case ARG_TILE_MODE_VERTICAL:
		nautilus_label_set_tile_mode_vertical (label, GTK_VALUE_UINT (*arg));
380
381
		break;

382
383
	case ARG_TILE_MODE_HORIZONTAL:
		nautilus_label_set_tile_mode_horizontal (label, GTK_VALUE_UINT (*arg));
384
385
386
		break;

	case ARG_LABEL:
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
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
		nautilus_label_set_text (label, GTK_VALUE_STRING (*arg));
		break;
		
	case ARG_WRAP:
		nautilus_label_set_wrap (label, GTK_VALUE_BOOL (*arg));
		break;
		
	case ARG_JUSTIFY:
		nautilus_label_set_justify (label, GTK_VALUE_ENUM (*arg));
		break;
		
	case ARG_IS_SMOOTH:
		nautilus_label_set_is_smooth (label, GTK_VALUE_BOOL (*arg));
		break;

	case ARG_TEXT_OPACITY:
		nautilus_label_set_text_opacity (label, GTK_VALUE_INT (*arg));
		break;

	case ARG_BACKGROUND_MODE:
		nautilus_label_set_background_mode (label, GTK_VALUE_UINT (*arg));
		break;

	case ARG_SMOOTH_FONT:
		nautilus_label_set_smooth_font (label, (NautilusScalableFont *) GTK_VALUE_OBJECT (*arg));
		break;

	case ARG_SMOOTH_FONT_SIZE:
		nautilus_label_set_smooth_font_size (label, GTK_VALUE_UINT (*arg));
		break;

	case ARG_SMOOTH_TEXT_COLOR:
		nautilus_label_set_text_color (label, GTK_VALUE_UINT (*arg));
		break;

	case ARG_SMOOTH_DROP_SHADOW_OFFSET:
		nautilus_label_set_smooth_drop_shadow_offset (label, GTK_VALUE_UINT (*arg));
		break;

	case ARG_SMOOTH_DROP_SHADOW_COLOR:
		nautilus_label_set_smooth_drop_shadow_color (label, GTK_VALUE_UINT (*arg));
		break;

	case ARG_SMOOTH_LINE_WRAP_WIDTH:
431
		nautilus_label_set_smooth_line_wrap_width (label, GTK_VALUE_INT (*arg));
432
433
		break;

434
435
436
437
	case ARG_ADJUST_WRAP_ON_RESIZE:
		nautilus_label_set_adjust_wrap_on_resize (label, GTK_VALUE_BOOL (*arg));
		break;

438
439
440
441
442
443
 	default:
		g_assert_not_reached ();
	}
}

static void
444
445
446
nautilus_label_get_arg (GtkObject *object,
			GtkArg *arg,
			guint arg_id)
447
448
449
450
451
452
453
454
455
{
	NautilusLabel	*label;

	g_return_if_fail (NAUTILUS_IS_LABEL (object));
	
	label = NAUTILUS_LABEL (object);

 	switch (arg_id)
	{
456
457
	case ARG_TILE_OPACITY:
		GTK_VALUE_INT (*arg) = nautilus_label_get_tile_opacity (label);
458
459
		break;
		
460
461
462
463
464
465
466
467
468
469
	case ARG_TILE_PIXBUF:
		GTK_VALUE_POINTER (*arg) = nautilus_label_get_tile_pixbuf (label);
		break;

	case ARG_TILE_WIDTH:
		GTK_VALUE_INT (*arg) = nautilus_label_get_tile_width (label);
		break;

	case ARG_TILE_HEIGHT:
		GTK_VALUE_INT (*arg) = nautilus_label_get_tile_height (label);
470
471
		break;

472
473
	case ARG_TILE_MODE_VERTICAL:
		GTK_VALUE_UINT (*arg) = nautilus_label_get_tile_mode_vertical (label);
474
475
		break;

476
477
478
479
480
	case ARG_TILE_MODE_HORIZONTAL:
		GTK_VALUE_UINT (*arg) = nautilus_label_get_tile_mode_horizontal (label);
		break;


481
	case ARG_LABEL:
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
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
		GTK_VALUE_STRING (*arg) = nautilus_label_get_text (label);
		break;

	case ARG_WRAP:
		GTK_VALUE_BOOL (*arg) = nautilus_label_get_wrap (label);
		break;

	case ARG_JUSTIFY:
		GTK_VALUE_ENUM (*arg) = nautilus_label_get_text_justify (label);
		break;

	case ARG_IS_SMOOTH:
		GTK_VALUE_BOOL (*arg) = nautilus_label_get_is_smooth (label);
		break;
		
	case ARG_TEXT_OPACITY:
		GTK_VALUE_INT (*arg) = nautilus_label_get_text_opacity (label);
		break;
		
	case ARG_BACKGROUND_MODE:
		GTK_VALUE_UINT (*arg) = nautilus_label_get_background_mode (label);
		break;

	case ARG_SMOOTH_FONT:
		GTK_VALUE_OBJECT (*arg) = (GtkObject *) nautilus_label_get_smooth_font (label);
		break;

	case ARG_SMOOTH_FONT_SIZE:
		GTK_VALUE_UINT (*arg) = nautilus_label_get_smooth_font_size (label);
		break;

	case ARG_SMOOTH_TEXT_COLOR:
		GTK_VALUE_UINT (*arg) = nautilus_label_get_text_color (label);
		break;

	case ARG_SMOOTH_DROP_SHADOW_OFFSET:
		GTK_VALUE_UINT (*arg) = nautilus_label_get_smooth_drop_shadow_offset (label);
		break;

	case ARG_SMOOTH_DROP_SHADOW_COLOR:
		GTK_VALUE_UINT (*arg) = nautilus_label_get_smooth_drop_shadow_color (label);
		break;

	case ARG_SMOOTH_LINE_WRAP_WIDTH:
526
		GTK_VALUE_INT (*arg) = nautilus_label_get_smooth_line_wrap_width (label);
527
528
		break;

529
530
531
532
	case ARG_ADJUST_WRAP_ON_RESIZE:
		GTK_VALUE_BOOL (*arg) = nautilus_label_get_adjust_wrap_on_resize (label);
		break;

533
534
535
536
537
538
539
 	default:
		g_assert_not_reached ();
	}
}

/* GtkWidgetClass methods */
static void
540
541
nautilus_label_size_request (GtkWidget *widget,
			     GtkRequisition *requisition)
542
{
543
544
	NautilusLabel *label;

545
546
547
548
	NautilusDimensions text_dimensions;
	NautilusDimensions tile_dimensions;
	NautilusDimensions preferred_dimensions;
	
549
550
551
	g_return_if_fail (NAUTILUS_IS_LABEL (widget));
	g_return_if_fail (requisition != NULL);

552
 	label = NAUTILUS_LABEL (widget);
553

554
	if (!label_is_smooth (label)) {
555
		NAUTILUS_CALL_PARENT (GTK_WIDGET_CLASS, size_request, (widget, requisition));
556
		return;
557
	}
558
	
559
560
561
562
563
564
565
566
567
568
	text_dimensions = label_get_text_dimensions (label);
	tile_dimensions = label_get_tile_dimensions (label);

	preferred_dimensions = nautilus_smooth_widget_get_preferred_dimensions (widget,
										&text_dimensions,
										&tile_dimensions,
										label->details->tile_width,
										label->details->tile_height);
   	requisition->width = preferred_dimensions.width;
   	requisition->height = preferred_dimensions.height;
569
570
}

571
572
573
574
575
576
577
578
579
580
581
582
static void
nautilus_label_size_allocate (GtkWidget *widget,
			      GtkAllocation *allocation)
{
	NautilusLabel *label;
	
 	g_return_if_fail (NAUTILUS_IS_LABEL (widget));
 	g_return_if_fail (allocation != NULL);
	
  	label = NAUTILUS_LABEL (widget);
	
	/* Pre chain size_allocate */
583
	NAUTILUS_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate, (widget, allocation));
584
585

	if (label->details->adjust_wrap_on_resize) {
586
		label->details->smooth_line_wrap_width = (int) allocation->width;
587
588
589
590
		label_line_geometries_recompute (label);
	}
}

591
/* Painting callback for non smooth case */
592
static void
593
594
595
596
597
598
599
label_paint_pixbuf_callback (GtkWidget *widget,
			     GdkDrawable *destination_drawable,
			     GdkGC *gc,
			     int source_x,
			     int source_y,
			     const ArtIRect *area,
			     gpointer callback_data)
600
601
{
	NautilusLabel *label;
602
603
604
605
606
607
608
	GdkEventExpose *event;

	g_return_if_fail (NAUTILUS_IS_LABEL (widget));
	g_return_if_fail (GTK_WIDGET_REALIZED (widget));
	g_return_if_fail (destination_drawable != NULL);
	g_return_if_fail (gc != NULL);
	g_return_if_fail (area != NULL && !art_irect_empty (area));
609
610
611

	label = NAUTILUS_LABEL (widget);

612
613
	event = (GdkEventExpose *) callback_data;

614
	NAUTILUS_CALL_PARENT (GTK_WIDGET_CLASS, expose_event, (widget, event));
615
616
}

617
/* Compositing callback for smooth case */
618
static void
619
620
621
622
623
624
625
label_composite_pixbuf_callback (GtkWidget *widget,
				 GdkPixbuf *destination_pixbuf,
				 int source_x,
				 int source_y,
				 const ArtIRect *area,
				 int opacity,
				 gpointer callback_data)
626
{
627
	NautilusLabel *label;
628
	NautilusDimensions text_dimensions;
629
630
	ArtIRect text_bounds;
	ArtIRect workaround_buffer_area;
631
	GdkPixbuf *pixbuf;
632
633
634
	GdkEventExpose *event;
	ArtIRect text_dirty_area;
	ArtIRect dirty_area;
635
	ArtIRect tmp;
636
637
638
639
640
641
642
	
	g_return_if_fail (NAUTILUS_IS_LABEL (widget));
	g_return_if_fail (GTK_WIDGET_REALIZED (widget));
	g_return_if_fail (destination_pixbuf != NULL);
	g_return_if_fail (area != NULL && !art_irect_empty (area));

	label = NAUTILUS_LABEL (widget);
643
	event = (GdkEventExpose *) callback_data;
644

645
	text_dimensions = label_get_text_dimensions (label);
646
	text_bounds = label_get_text_bounds (label);
647
	
648
	g_return_if_fail (!nautilus_dimensions_empty (&text_dimensions));
649

650
	/* Optimize the case where the background is solid */
651
	if (label_can_cache_contents (label)) {
652
		if (label->details->solid_cache_pixbuf == NULL) {
653
			ArtIRect tmp;
654
			label->details->solid_cache_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
655
656
									    FALSE,
									    8,
657
658
									    text_dimensions.width,
									    text_dimensions.height);
659
			
660
			nautilus_gdk_pixbuf_fill_rectangle_with_color (label->details->solid_cache_pixbuf,
661
								       NULL,
662
								       label->details->solid_background_color);
663
			
664
			tmp = nautilus_art_irect_assign_dimensions (0, 0, &text_dimensions);
665
			label_draw_text_to_pixbuf (label,
666
						   label->details->solid_cache_pixbuf,
667
						   &tmp,
668
669
670
						   0,
						   0);
		}
671

672
		nautilus_gdk_pixbuf_draw_to_pixbuf (label->details->solid_cache_pixbuf,
673
674
675
676
						    destination_pixbuf,
						    source_x,
						    source_y,
						    area);
677
678
679
680

		return;
	}

681
682
683
684
685
	/* We dont really need this information.  The point is to have
	 * the "smooth widget" figure it out and feed us only the final 
	 * content dirty rectangle.  We compute it now to workaround
	 * bug 2784.  See fixme below.
	 */
686
	dirty_area = nautilus_gdk_rectangle_to_art_irect (&event->area);
687
688
	art_irect_intersect (&text_dirty_area, &text_bounds, &dirty_area);

689
	/* FIXME bugzilla.eazel.com 2784: 
690
	 * The reason we use a temporary buffer instead of
691
692
	 * drawing directly into the destination_pixbuf is that
	 * there currently is a serious bug in NautilusScalalbleFont
693
694
	 * that prevents clipping from happening when drawing smooth
	 * text.
695
	 */
696
	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, text_dimensions.width, text_dimensions.height);
697

698
699
700
701
702
703
	/* Copy the dirty bits out of the destination to our temporary buffer */
 	workaround_buffer_area.x0 = text_dirty_area.x0 - text_bounds.x0;
 	workaround_buffer_area.y0 = text_dirty_area.y0 - text_bounds.y0;
 	workaround_buffer_area.x1 = workaround_buffer_area.x0 + gdk_pixbuf_get_width (destination_pixbuf);
 	workaround_buffer_area.y1 = workaround_buffer_area.y0 + gdk_pixbuf_get_height (destination_pixbuf);
	
704
705
706
707
	nautilus_gdk_pixbuf_draw_to_pixbuf (destination_pixbuf,
					    pixbuf,
					    area->x0,
					    area->y0,
708
709
710
711
712
713
					    &workaround_buffer_area);

	/* Now draw the full extent of the text to the buffer.  Once
	 * bug 2784 is fixed, we can simply pass this function a
	 * clip rectangle and lose the temporary buffer code above
	 */
714
	tmp = nautilus_art_irect_assign_dimensions (0, 0, &text_dimensions);
715
716
	label_draw_text_to_pixbuf (label,
				   pixbuf,
717
				   &tmp,
718
719
				   0,
				   0);
720
721
722
723
724
725

	/* And finally draw the composited bits back to the 
	 * destination buffer - just the dirty area.
	 * 
	 * Again, when bug 2784 is fixed this step is not needed.
	 */
726
727
	nautilus_gdk_pixbuf_draw_to_pixbuf (pixbuf,
					    destination_pixbuf,
728
729
					    text_dirty_area.x0 - text_bounds.x0,
					    text_dirty_area.y0 - text_bounds.y0,
730
731
732
733
734
735
736
737
738
739
740
741
742
					    area);
	
	gdk_pixbuf_unref (pixbuf);
}	

static int
nautilus_label_expose_event (GtkWidget *widget,
			     GdkEventExpose *event)
{
 	NautilusLabel *label;
	ArtIRect dirty_area;
	ArtIRect screen_dirty_area;
	ArtIRect smooth_text_bounds;
743
	ArtIRect widget_bounds;
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
	ArtIRect tile_bounds;

	g_return_val_if_fail (NAUTILUS_IS_LABEL (widget), TRUE);
	g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), TRUE);
	g_return_val_if_fail (event != NULL, TRUE);
	g_return_val_if_fail (event->window == widget->window, TRUE);
	
 	label = NAUTILUS_LABEL (widget);

	/* The smooth and non smooth bounds are different.  We have
	 * no way to have GtkLabel tell us what its bounds are.
	 * So, we cheat and pretend that for the non smooth case,
	 * the text bounds are the whole widget.
	 *
	 * This works because the smooth widget paint handler will
	 * properly intersect these bounds and call the paint 
	 * callback.  We feed the paint callback the actual gdk
	 * expose event so that we feed the exact exposure area
	 * to GtkLabel's expose_event.
	 */
764
	widget_bounds = nautilus_gtk_widget_get_bounds (widget);
765
766
	smooth_text_bounds = label_get_text_bounds (label);
	tile_bounds = nautilus_smooth_widget_get_tile_bounds (widget,
767
768
769
							      label->details->tile_pixbuf,
							      label->details->tile_width,
							      label->details->tile_height);
770
771
	
	/* Check for the dumb case when theres nothing to do */
772
	if (nautilus_strlen (label_peek_text (label)) == 0 && label->details->tile_pixbuf == NULL) {
773
		return TRUE;
774
	}
775
776

	/* Clip the dirty area to the screen */
777
778
	dirty_area = nautilus_gdk_rectangle_to_art_irect (&event->area);
	screen_dirty_area = nautilus_gdk_window_clip_dirty_area_to_screen (event->window,
779
										 &dirty_area);
780

781
782
783
784
	/* Make sure the area is screen visible before painting */
	if (!art_irect_empty (&screen_dirty_area)) {
		nautilus_smooth_widget_paint (widget,
					      widget->style->white_gc,
785
786
787
788
					      label_is_smooth (label),
					      label->details->background_mode,
					      label->details->solid_background_color,
					      label->details->tile_pixbuf,
789
					      &tile_bounds,
790
791
792
793
794
					      label->details->tile_opacity,
					      label->details->tile_mode_vertical,
					      label->details->tile_mode_horizontal,
					      label_is_smooth (label) ? &smooth_text_bounds : &widget_bounds,
					      label->details->text_opacity,
795
796
797
798
					      &screen_dirty_area,
					      label_paint_pixbuf_callback,
					      label_composite_pixbuf_callback,
					      event);
799
800
	}

801
802
	return TRUE;
}
803

804
805
806
807
808
809
/* NautilusLabel signals */
static void
nautilus_label_set_is_smooth_signal (GtkWidget *widget,
				     gboolean is_smooth)
{
	g_return_if_fail (NAUTILUS_IS_LABEL (widget));
Ramiro Estrugo's avatar
Ramiro Estrugo committed
810

811
812
	nautilus_label_set_is_smooth (NAUTILUS_LABEL (widget), is_smooth);
}
813

814
815
816
817
818
819
820
821
/* Private NautilusLabel things */
static void
label_draw_text_to_pixbuf (NautilusLabel *label,
			   GdkPixbuf *pixbuf,
			   const ArtIRect *destination_area,
			   int x,
			   int y)
{
822
	NautilusDimensions pixbuf_dimensions;
823
824
825
826
827
828
	
	g_return_if_fail (NAUTILUS_IS_LABEL (label));
	g_return_if_fail (destination_area != NULL);
	g_return_if_fail (nautilus_gdk_pixbuf_is_valid (pixbuf));
	g_return_if_fail (!art_irect_empty (destination_area));
	
829
	pixbuf_dimensions = nautilus_gdk_pixbuf_get_dimensions (pixbuf);
830

831
832
	g_return_if_fail (pixbuf_dimensions.width >= (destination_area->x1 - destination_area->x0));
	g_return_if_fail (pixbuf_dimensions.height >= (destination_area->y1 - destination_area->y0));
833
834

	/* Line wrapping */
835
836
	if (nautilus_label_get_wrap (label)) {
		int i;
837
		
838
839
		for (i = 0; i < label->details->num_text_lines; i++) {
			const NautilusTextLayout *text_layout = label->details->text_layouts[i];
840
			
841
			if (label->details->smooth_drop_shadow_offset > 0) {
842
				nautilus_text_layout_paint (text_layout, 
843
							    pixbuf, 
844
845
							    x + label->details->smooth_drop_shadow_offset, 
							    y + label->details->smooth_drop_shadow_offset,
846
							    nautilus_label_get_text_justify (label),
847
							    label->details->smooth_drop_shadow_color,
848
849
							    FALSE);
			}
850
			
851
			nautilus_text_layout_paint (text_layout, 
852
						    pixbuf, 
853
854
						    x, 
						    y,
855
						    nautilus_label_get_text_justify (label),
856
						    label->details->smooth_text_color,
857
						    FALSE);
858
			
859
860
861
862
863
			y += text_layout->height;
		}
	}
	/* No line wrapping */
	else {
864
		if (label->details->smooth_drop_shadow_offset > 0) {
865
			nautilus_scalable_font_draw_text_lines_with_dimensions (
866
				label->details->smooth_font,
867
				pixbuf,
868
				x + label->details->smooth_drop_shadow_offset,
869
				y + label->details->smooth_drop_shadow_offset,
870
				destination_area,
871
				label->details->smooth_font_size,
872
				label_peek_text (label),
873
				label->details->num_text_lines,
874
				label->details->text_line_dimensions,
875
876
877
				nautilus_label_get_text_justify (label),
				LINE_OFFSET,
				label_get_empty_line_height (label),
878
879
				label->details->smooth_drop_shadow_color,
				label->details->text_opacity);
Ramiro Estrugo's avatar
Ramiro Estrugo committed
880
		}
881
882
		
		nautilus_scalable_font_draw_text_lines_with_dimensions (
883
			label->details->smooth_font,
884
885
886
887
			pixbuf,
			x,
			y,
			destination_area,
888
			label->details->smooth_font_size,
889
			label_peek_text (label),
890
			label->details->num_text_lines,
891
			label->details->text_line_dimensions,
892
893
894
			nautilus_label_get_text_justify (label),
			LINE_OFFSET,
			label_get_empty_line_height (label),
895
896
			label->details->smooth_text_color,
			label->details->text_opacity);
897
898
	}
}
Ramiro Estrugo's avatar
Ramiro Estrugo committed
899

900
static int
901
902
903
904
label_get_default_line_wrap_width (const NautilusLabel *label)
{
	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), 0);
	
905
906
907
908
	return nautilus_scalable_font_text_width (label->details->smooth_font,
						  label->details->smooth_font_size,
						  DEFAULT_LINE_WRAP_WIDTH_TEXT,
						  strlen (DEFAULT_LINE_WRAP_WIDTH_TEXT));
909
910
}

911
912
static NautilusDimensions
label_get_text_dimensions (const NautilusLabel *label)
913
{
914
	NautilusDimensions text_dimensions;
915

916
	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), NAUTILUS_DIMENSIONS_EMPTY);
917

918
	text_dimensions = NAUTILUS_DIMENSIONS_EMPTY;
919

920
	if (label->details->num_text_lines > 0) {
921
		text_dimensions.width = 
922
923
			label->details->max_text_line_width
			+ label->details->smooth_drop_shadow_offset;
924
		
925
		text_dimensions.height = label_get_total_text_and_line_offset_height (label)
926
			+ label->details->smooth_drop_shadow_offset;
927
	}
928
929
	
	return text_dimensions;
930
931
932
933
934
}

static ArtIRect
label_get_text_bounds (const NautilusLabel *label)
{
935
	NautilusDimensions text_dimensions;
936
	ArtIRect text_bounds;
937
	ArtIRect bounds;
938
939
940

	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), NAUTILUS_ART_IRECT_EMPTY);

941
	text_dimensions = label_get_text_dimensions (label);
942

943
	if (nautilus_dimensions_empty (&text_dimensions)) {
944
945
946
		return NAUTILUS_ART_IRECT_EMPTY;
	}
	
947
948
	bounds = nautilus_gtk_widget_get_bounds (GTK_WIDGET (label));
	
949
	text_bounds = nautilus_art_irect_align (&bounds,
950
951
						text_dimensions.width,
						text_dimensions.height,
952
						GTK_MISC (label)->xalign,
953
						GTK_MISC (label)->yalign);
954
		
955
956
957
958

	return text_bounds;
}

959
960
static NautilusDimensions
label_get_tile_dimensions (const NautilusLabel *label)
961
{
962
	NautilusDimensions tile_dimensions;
963

964
	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), NAUTILUS_DIMENSIONS_EMPTY);
965

966
	if (!label->details->tile_pixbuf) {
967
		return NAUTILUS_DIMENSIONS_EMPTY;
968
	}
969
970
971
	
	tile_dimensions.width = gdk_pixbuf_get_width (label->details->tile_pixbuf);
	tile_dimensions.height = gdk_pixbuf_get_height (label->details->tile_pixbuf);
972

973
	return tile_dimensions;
974
975
976
977
978
979
980
}

static void
label_solid_cache_pixbuf_clear (NautilusLabel *label)
{
	g_return_if_fail (NAUTILUS_IS_LABEL (label));

981
982
	nautilus_gdk_pixbuf_unref_if_not_null (label->details->solid_cache_pixbuf);
	label->details->solid_cache_pixbuf = NULL;
983
984
985
986
987
988
989
}

static gboolean
label_can_cache_contents (const NautilusLabel *label)
{
	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), FALSE);

990
991
	return (label->details->background_mode == NAUTILUS_SMOOTH_BACKGROUND_SOLID_COLOR)
		&& !label->details->tile_pixbuf;
992
993
994
995
996
997
998
999
}

const char *
label_peek_text (const NautilusLabel *label)
{
	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), NULL);

	return GTK_LABEL (label)->label;
1000
1001
}

1002
static guint
1003
label_get_empty_line_height (const NautilusLabel *label)
1004
1005
1006
{
	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), 0);

1007
	/* If we wanted to crunch lines together, we could add a divider
1008
	 * here.  For now we just use the font size for empty lines. */
1009
	return label->details->smooth_font_size;
1010
1011
1012
}

static guint
1013
label_get_total_text_and_line_offset_height (const NautilusLabel *label)
1014
1015
1016
1017
1018
{
	guint total_height;

	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), 0);

1019
	total_height = label->details->total_text_line_height;
1020
	
1021
1022
	if (label->details->num_text_lines > 1) {
		total_height += ((label->details->num_text_lines - 1) * LINE_OFFSET);
1023
1024
1025
1026
1027
	}

	return total_height;
}

1028
static void
1029
label_line_geometries_clear (NautilusLabel *label)
1030
1031
{
	g_return_if_fail (NAUTILUS_IS_LABEL (label));
1032
	
1033
1034
	g_free (label->details->text_line_dimensions);
	label->details->text_line_dimensions = NULL;
1035

1036
	if (label->details->text_layouts != NULL) {
1037
		int i;
1038

1039
1040
1041
		for (i = 0; i < label->details->num_text_lines; i++) {
			g_assert (label->details->text_layouts[i] != NULL);
			nautilus_text_layout_free (label->details->text_layouts[i]);
1042
1043
		}

1044
1045
		g_free (label->details->text_layouts);
		label->details->text_layouts = NULL;
1046
1047
	}
	
1048
	label->details->num_text_lines = 0;
1049

1050
1051
	label->details->max_text_line_width = 0;
	label->details->total_text_line_height = 0;
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
}

static void
label_line_geometries_recompute (NautilusLabel *label)
{
	const char *text;

	g_return_if_fail (NAUTILUS_IS_LABEL (label));

	text = label_peek_text (label);

	label_solid_cache_pixbuf_clear (label);
	label_line_geometries_clear (label);
1065

1066
	if (nautilus_strlen (text) == 0) {
1067
1068
1069
		return;
	}
	
1070
	label->details->num_text_lines = nautilus_str_count_characters (text, '\n') + 1;
1071
1072

	/* Line wrapping */
1073
	if (nautilus_label_get_wrap (label)) {
1074
		char **pieces;
1075
1076
		int i;

1077
		label->details->text_layouts = g_new (NautilusTextLayout *, label->details->num_text_lines);
1078
1079

		pieces = g_strsplit (text, "\n", 0);
1080
1081
1082
1083

		for (i = 0; pieces[i] != NULL; i++) {
			char *text_piece = pieces[i];

1084
			g_assert (i < label->details->num_text_lines);
1085
1086
1087
1088
1089
1090

			/* Make empty lines appear.  A single '\n' for example. */
			if (text_piece[0] == '\0') {
				text_piece = " ";
			}

1091
1092
			label->details->text_layouts[i] = nautilus_text_layout_new (label->details->smooth_font,
										   label->details->smooth_font_size,
1093
										   text_piece,
1094
										   LINE_WRAP_SEPARATORS,
1095
										   label->details->smooth_line_wrap_width, 
1096
										   TRUE);
1097

1098
			label->details->total_text_line_height += label->details->text_layouts[i]->height;
1099
			
1100
1101
			label->details->max_text_line_width = MAX (label->details->max_text_line_width,
								   label->details->text_layouts[i]->width);
1102
1103
1104
1105
1106
1107
		}

		g_strfreev (pieces);
	}
	/* No line wrapping */
	else {
1108
		label->details->text_line_dimensions = g_new (NautilusDimensions, label->details->num_text_lines);
1109
		
1110
1111
		nautilus_scalable_font_measure_text_lines (label->details->smooth_font,
							   label->details->smooth_font_size,
1112
							   text,
1113
							   label->details->num_text_lines,
1114
							   label_get_empty_line_height (label),
1115
							   label->details->text_line_dimensions,
1116
1117
							   &label->details->max_text_line_width,
							   &label->details->total_text_line_height);
1118
1119
1120
	}
}

1121
1122
1123
1124
1125
1126
1127
1128
gboolean
label_is_smooth (const NautilusLabel *label)
{
	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), FALSE);

	return !label->details->never_smooth && label->details->is_smooth;
}

1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
/* Public NautilusLabel methods */
GtkWidget *
nautilus_label_new (const char *text)
{
	NautilusLabel *label;
	
	label = NAUTILUS_LABEL (gtk_widget_new (nautilus_label_get_type (), NULL));
	
	nautilus_label_set_text (label, text);
	
	return GTK_WIDGET (label);
}

void
nautilus_label_set_smooth_font (NautilusLabel *label,
				NautilusScalableFont *smooth_font)
{
	g_return_if_fail (NAUTILUS_IS_LABEL (label));
	g_return_if_fail (NAUTILUS_IS_SCALABLE_FONT (smooth_font));
	
1149
	if (label->details->smooth_font == smooth_font) {
1150
1151
1152
		return;
	}
	
1153
1154
	if (label->details->smooth_font != NULL) {
		gtk_object_unref (GTK_OBJECT (label->details->smooth_font));
1155
1156
1157
	}

	gtk_object_ref (GTK_OBJECT (smooth_font));
1158
	label->details->smooth_font = smooth_font;
1159
1160

	/* Update the line wrap width */
1161
	label->details->smooth_line_wrap_width = label_get_default_line_wrap_width (label);
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
	
	label_line_geometries_recompute (label);
	
	gtk_widget_queue_resize (GTK_WIDGET (label));
}

NautilusScalableFont *
nautilus_label_get_smooth_font (const NautilusLabel *label)
{
	g_return_val_if_fail (NAUTILUS_IS_LABEL (label), NULL);
	
1173
1174
	if (label->details->smooth_font != NULL) {
		gtk_object_ref (GTK_OBJECT (label->details->smooth_font));
1175
1176
	}

1177
	return label->details->smooth_font;
1178
1179
1180
1181
1182
1183
1184
1185
1186
}

void
nautilus_label_set_smooth_font_size (NautilusLabel *label,
				     guint smooth_font_size)
{
	g_return_if_fail (NAUTILUS_IS_LABEL (label));
	g_return_if_fail (smooth_font_size > MIN_SMOOTH_FONT_SIZE);

1187
	if (label->details->smooth_font_size == smooth_font_size) {
1188
1189
1190
		return;
	}

1191
	label->details->smooth_font_size = smooth_font_size;