2
* Copyright (c) 2011 Red Hat, Inc.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or (at your
7
* option) any later version.
9
* This program is distributed in the hope that it will be useful, but
10
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12
* License for more details.
14
* You should have received a copy of the GNU Lesser General Public License
15
* along with this program; if not, write to the Free Software Foundation,
16
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
* Author: Cosimo Cecchi <cosimoc@redhat.com>
22
#include "gd-two-lines-renderer.h"
25
G_DEFINE_TYPE (GdTwoLinesRenderer, gd_two_lines_renderer, GTK_TYPE_CELL_RENDERER_TEXT)
27
struct _GdTwoLinesRendererPrivate {
38
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
41
create_layout_with_attrs (GtkWidget *widget,
42
const GdkRectangle *cell_area,
43
GdTwoLinesRenderer *self,
44
PangoEllipsizeMode ellipsize)
47
gint wrap_width, xpad;
48
PangoWrapMode wrap_mode;
49
PangoAlignment alignment;
52
"wrap-width", &wrap_width,
53
"wrap-mode", &wrap_mode,
54
"alignment", &alignment,
58
layout = pango_layout_new (gtk_widget_get_pango_context (widget));
60
pango_layout_set_ellipsize (layout, ellipsize);
61
pango_layout_set_alignment (layout, alignment);
65
pango_layout_set_width (layout, wrap_width * PANGO_SCALE);
66
pango_layout_set_wrap (layout, wrap_mode);
70
if (cell_area != NULL)
71
pango_layout_set_width (layout, (cell_area->width - 2 * xpad) * PANGO_SCALE);
73
pango_layout_set_width (layout, -1);
75
pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
82
gd_two_lines_renderer_prepare_layouts (GdTwoLinesRenderer *self,
83
const GdkRectangle *cell_area,
85
PangoLayout **layout_one,
86
PangoLayout **layout_two)
88
PangoLayout *line_one;
89
PangoLayout *line_two = NULL;
96
line_one = create_layout_with_attrs (widget, cell_area,
97
self, PANGO_ELLIPSIZE_MIDDLE);
99
if (self->priv->line_two == NULL ||
100
g_strcmp0 (self->priv->line_two, "") == 0)
102
pango_layout_set_height (line_one, - (self->priv->text_lines));
105
pango_layout_set_text (line_one, text, -1);
109
line_two = create_layout_with_attrs (widget, cell_area,
110
self, PANGO_ELLIPSIZE_END);
112
pango_layout_set_height (line_one, - (self->priv->text_lines - 1));
113
pango_layout_set_height (line_two, -1);
114
pango_layout_set_text (line_two, self->priv->line_two, -1);
117
pango_layout_set_text (line_one, text, -1);
121
*layout_one = line_one;
123
*layout_two = line_two;
129
gd_two_lines_renderer_get_size (GtkCellRenderer *cell,
131
PangoLayout *layout_1,
132
PangoLayout *layout_2,
135
const GdkRectangle *cell_area,
140
GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell);
142
PangoLayout *layout_one, *layout_two;
143
GdkRectangle layout_one_rect, layout_two_rect, layout_union;
145
if (layout_1 == NULL)
147
gd_two_lines_renderer_prepare_layouts (self, cell_area, widget, &layout_one, &layout_two);
151
layout_one = g_object_ref (layout_1);
153
if (layout_2 != NULL)
154
layout_two = g_object_ref (layout_2);
159
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
160
pango_layout_get_pixel_extents (layout_one, NULL, (PangoRectangle *) &layout_one_rect);
162
if (layout_two != NULL)
164
pango_layout_get_pixel_extents (layout_two, NULL, (PangoRectangle *) &layout_two_rect);
166
layout_union.width = MAX (layout_one_rect.width, layout_two_rect.width);
167
layout_union.height = layout_one_rect.height + layout_two_rect.height;
171
layout_union = layout_one_rect;
176
gfloat xalign, yalign;
178
gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
180
layout_union.width = MIN (layout_union.width, cell_area->width - 2 * xpad);
181
layout_union.height = MIN (layout_union.height, cell_area->height - 2 * ypad);
185
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
186
*x_offset_1 = (1.0 - xalign) * (cell_area->width - (layout_one_rect.width + (2 * xpad)));
188
*x_offset_1 = xalign * (cell_area->width - (layout_one_rect.width + (2 * xpad)));
190
*x_offset_1 = MAX (*x_offset_1, 0);
194
if (layout_two != NULL)
196
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
197
*x_offset_2 = (1.0 - xalign) * (cell_area->width - (layout_two_rect.width + (2 * xpad)));
199
*x_offset_2 = xalign * (cell_area->width - (layout_two_rect.width + (2 * xpad)));
201
*x_offset_2 = MAX (*x_offset_2, 0);
211
*y_offset = yalign * (cell_area->height - (layout_union.height + (2 * ypad)));
212
*y_offset = MAX (*y_offset, 0);
217
if (x_offset_1) *x_offset_1 = 0;
218
if (x_offset_2) *x_offset_2 = 0;
219
if (y_offset) *y_offset = 0;
222
g_clear_object (&layout_one);
223
g_clear_object (&layout_two);
226
*height = ypad * 2 + layout_union.height;
229
*width = xpad * 2 + layout_union.width;
233
gd_two_lines_renderer_render (GtkCellRenderer *cell,
236
const GdkRectangle *background_area,
237
const GdkRectangle *cell_area,
238
GtkCellRendererState flags)
240
GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell);
241
GtkStyleContext *context;
242
gint line_one_height;
244
GdkRectangle area, render_area = *cell_area;
245
gint xpad, ypad, x_offset_1, x_offset_2, y_offset;
246
PangoLayout *layout_one, *layout_two;
247
PangoRectangle layout_rect;
249
/* fetch common information */
250
context = gtk_widget_get_style_context (widget);
251
gd_two_lines_renderer_prepare_layouts (self, cell_area, widget, &layout_one, &layout_two);
252
gd_two_lines_renderer_get_size (cell, widget,
253
layout_one, layout_two,
256
&x_offset_1, &x_offset_2, &y_offset);
257
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
263
/* now render the first layout */
264
pango_layout_get_pixel_extents (layout_one, NULL, &layout_rect);
267
render_area.x += x_offset_1 - layout_rect.x;
269
gtk_render_layout (context, cr,
274
/* render the second layout */
275
if (layout_two != NULL)
277
pango_layout_get_pixel_size (layout_one,
278
NULL, &line_one_height);
280
gtk_style_context_save (context);
281
gtk_style_context_add_class (context, "dim-label");
283
state = gtk_cell_renderer_get_state (cell, widget, flags);
284
gtk_style_context_set_state (context, state);
286
pango_layout_get_pixel_extents (layout_two, NULL, &layout_rect);
289
render_area.x += x_offset_2 - layout_rect.x;
290
render_area.y += line_one_height;
292
gtk_render_layout (context, cr,
297
gtk_style_context_restore (context);
300
g_clear_object (&layout_one);
301
g_clear_object (&layout_two);
305
gd_two_lines_renderer_get_preferred_width (GtkCellRenderer *cell,
310
PangoContext *context;
311
PangoFontMetrics *metrics;
312
const PangoFontDescription *font_desc;
313
GtkStyleContext *style_context;
314
gint nat_width, min_width;
315
gint xpad, char_width, wrap_width, text_width;
316
gint width_chars, ellipsize_chars;
320
"width-chars", &width_chars,
321
"wrap-width", &wrap_width,
323
style_context = gtk_widget_get_style_context (widget);
324
gtk_cell_renderer_get_padding (cell, &xpad, NULL);
326
gd_two_lines_renderer_get_size (cell, widget,
332
/* Fetch the average size of a charachter */
333
context = gtk_widget_get_pango_context (widget);
334
font_desc = gtk_style_context_get_font (style_context, 0);
335
metrics = pango_context_get_metrics (context, font_desc,
336
pango_context_get_language (context));
338
char_width = pango_font_metrics_get_approximate_char_width (metrics);
340
pango_font_metrics_unref (metrics);
342
/* enforce minimum width for ellipsized labels at ~3 chars */
345
/* If no width-chars set, minimum for wrapping text will be the wrap-width */
347
min_width = xpad * 2 + MIN (text_width, wrap_width);
349
min_width = xpad * 2 +
351
(PANGO_PIXELS (char_width) * MAX (width_chars, ellipsize_chars)));
354
nat_width = xpad * 2 +
355
MAX ((PANGO_PIXELS (char_width) * width_chars), text_width);
357
nat_width = xpad * 2 + text_width;
359
nat_width = MAX (nat_width, min_width);
362
*minimum_size = min_width;
365
*natural_size = nat_width;
369
gd_two_lines_renderer_get_preferred_height_for_width (GtkCellRenderer *cell,
375
GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell);
376
PangoLayout *layout_one, *layout_two;
377
gint text_height, wrap_width;
380
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
381
g_object_get (cell, "wrap-width", &wrap_width, NULL);
382
gd_two_lines_renderer_prepare_layouts (self, NULL, widget, &layout_one, &layout_two);
384
if (wrap_width != -1)
385
wrap_width = MIN (width - 2 * xpad, wrap_width);
387
wrap_width = width - 2 * xpad;
389
pango_layout_set_width (layout_one, wrap_width);
390
if (layout_two != NULL)
391
pango_layout_set_width (layout_two, wrap_width);
393
gd_two_lines_renderer_get_size (cell, widget,
394
layout_one, layout_two,
399
text_height += 2 * ypad;
401
if (minimum_size != NULL)
402
*minimum_size = text_height;
404
if (natural_size != NULL)
405
*natural_size = text_height;
407
g_clear_object (&layout_one);
408
g_clear_object (&layout_two);
412
gd_two_lines_renderer_get_preferred_height (GtkCellRenderer *cell,
419
gtk_cell_renderer_get_preferred_width (cell, widget, &min_width, NULL);
420
gd_two_lines_renderer_get_preferred_height_for_width (cell, widget, min_width,
421
minimum_size, natural_size);
425
gd_two_lines_renderer_get_aligned_area (GtkCellRenderer *cell,
427
GtkCellRendererState flags,
428
const GdkRectangle *cell_area,
429
GdkRectangle *aligned_area)
431
GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell);
432
gint x_offset, x_offset_1, x_offset_2, y_offset;
433
PangoLayout *layout_one, *layout_two;
435
/* fetch common information */
436
gd_two_lines_renderer_prepare_layouts (self, cell_area, widget, &layout_one, &layout_two);
437
gd_two_lines_renderer_get_size (cell, widget,
438
layout_one, layout_two,
439
&aligned_area->width, &aligned_area->height,
441
&x_offset_1, &x_offset_2, &y_offset);
443
x_offset = MIN (x_offset_1, x_offset_2);
445
aligned_area->x = cell_area->x + x_offset;
446
aligned_area->y = cell_area->y;
448
g_clear_object (&layout_one);
449
g_clear_object (&layout_two);
453
gd_two_lines_renderer_set_line_two (GdTwoLinesRenderer *self,
454
const gchar *line_two)
456
if (g_strcmp0 (self->priv->line_two, line_two) == 0)
459
g_free (self->priv->line_two);
460
self->priv->line_two = g_strdup (line_two);
462
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LINE_TWO]);
466
gd_two_lines_renderer_set_text_lines (GdTwoLinesRenderer *self,
469
if (self->priv->text_lines == text_lines)
472
self->priv->text_lines = text_lines;
473
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT_LINES]);
477
gd_two_lines_renderer_set_property (GObject *object,
482
GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object);
486
case PROP_TEXT_LINES:
487
gd_two_lines_renderer_set_text_lines (self, g_value_get_int (value));
490
gd_two_lines_renderer_set_line_two (self, g_value_get_string (value));
493
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
499
gd_two_lines_renderer_get_property (GObject *object,
504
GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object);
508
case PROP_TEXT_LINES:
509
g_value_set_int (value, self->priv->text_lines);
512
g_value_set_string (value, self->priv->line_two);
515
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
521
gd_two_lines_renderer_finalize (GObject *object)
523
GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object);
525
g_free (self->priv->line_two);
527
G_OBJECT_CLASS (gd_two_lines_renderer_parent_class)->finalize (object);
531
gd_two_lines_renderer_class_init (GdTwoLinesRendererClass *klass)
533
GtkCellRendererClass *cclass = GTK_CELL_RENDERER_CLASS (klass);
534
GObjectClass *oclass = G_OBJECT_CLASS (klass);
536
cclass->render = gd_two_lines_renderer_render;
537
cclass->get_preferred_width = gd_two_lines_renderer_get_preferred_width;
538
cclass->get_preferred_height = gd_two_lines_renderer_get_preferred_height;
539
cclass->get_preferred_height_for_width = gd_two_lines_renderer_get_preferred_height_for_width;
540
cclass->get_aligned_area = gd_two_lines_renderer_get_aligned_area;
542
oclass->set_property = gd_two_lines_renderer_set_property;
543
oclass->get_property = gd_two_lines_renderer_get_property;
544
oclass->finalize = gd_two_lines_renderer_finalize;
546
properties[PROP_TEXT_LINES] =
547
g_param_spec_int ("text-lines",
549
"The total number of lines to be displayed",
551
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
553
properties[PROP_LINE_TWO] =
554
g_param_spec_string ("line-two",
558
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
560
g_type_class_add_private (klass, sizeof (GdTwoLinesRendererPrivate));
561
g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
565
gd_two_lines_renderer_init (GdTwoLinesRenderer *self)
567
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_TWO_LINES_RENDERER,
568
GdTwoLinesRendererPrivate);
572
gd_two_lines_renderer_new (void)
574
return g_object_new (GD_TYPE_TWO_LINES_RENDERER, NULL);