1
/*
2
 * This file is part of the hildon-application-manager.
3
 *
4
 * Copyright (C) 2005, 2006, 2007, 2008 Nokia Corporation.  All Rights reserved.
5
 *
6
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License version
10
 * 2 as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
 * 02110-1301 USA
21
 *
22
 */
23
24
#include <gtk/gtkwidget.h>
25
#include <gtk/gtkcellrenderertext.h>
26
27
#include "package-info-cell-renderer.h"
28
29
#define DEFAULT_ICON_SIZE 30
30
#define DEFAULT_MARGIN 6
31
32
static GObjectClass *parent_class = NULL;
33
34
enum {
35
  PROP_ZERO,
36
  PROP_PKG_NAME,
37
  PROP_PKG_DESCRIPTION
38
};
39
40
41
typedef struct _PackageInfoCellRendererPrivate PackageInfoCellRendererPrivate;
42
43
struct _PackageInfoCellRendererPrivate
44
{
45
  gchar *pkg_name;
46
  gchar *pkg_description;
47
48
  gint single_line_height;
49
  gint double_line_height;
50
51
  PangoAttrList *scale_medium_attr_list;
52
  PangoAttrList *scale_small_attr_list;
53
};
54
55
#define PACKAGE_INFO_CELL_RENDERER_GET_PRIVATE(o)	\
56
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_PACKAGE_INFO_CELL_RENDERER, PackageInfoCellRendererPrivate))
57
58
/* static guint signals[LAST_SIGNAL] = {0}; */
59
60
/* static functions: GObject */
61
static void package_info_cell_renderer_instance_init (GTypeInstance *instance, gpointer g_class);
62
static void package_info_cell_renderer_finalize      (GObject *object);
63
static void package_info_cell_renderer_class_init    (PackageInfoCellRendererClass *klass);
64
65
static void package_info_cell_renderer_get_property (GObject              *object,
66
                                                     guint                param_id,
67
                                                     GValue               *value,
68
                                                     GParamSpec           *pspec);
69
70
static void package_info_cell_renderer_set_property (GObject              *object,
71
                                                     guint                param_id,
72
                                                     const GValue         *value,
73
                                                     GParamSpec           *pspec);
74
75
/* static functions: GtkCellRenderer */
76
static void package_info_cell_renderer_get_size     (GtkCellRenderer      *cell,
77
                                                     GtkWidget            *widget,
78
                                                     GdkRectangle         *rectangle,
79
                                                     gint                 *x_offset,
80
                                                     gint                 *y_offset,
81
                                                     gint                 *width,
82
                                                     gint                 *height);
83
84
static void package_info_cell_renderer_render       (GtkCellRenderer      *cell,
85
                                                     GdkDrawable          *window,
86
                                                     GtkWidget            *widget,
87
                                                     GdkRectangle         *background_area,
88
                                                     GdkRectangle         *cell_area,
89
                                                     GdkRectangle         *expose_area,
90
                                                     GtkCellRendererState flags);
91
92
93
/**
94
 * package_info_cell_renderer_new:
95
 *
96
 * Return value: a new #PackageInfoCellRenderer instance implemented for Gtk+
97
 **/
98
GtkCellRenderer*
99
package_info_cell_renderer_new (void)
100
{
101
  PackageInfoCellRenderer *self = g_object_new (TYPE_PACKAGE_INFO_CELL_RENDERER, NULL);
102
103
  return GTK_CELL_RENDERER (self);
104
}
105
106
static void
107
package_info_cell_renderer_instance_init (GTypeInstance *instance, gpointer g_class)
108
{
109
  PackageInfoCellRendererPrivate *priv = PACKAGE_INFO_CELL_RENDERER_GET_PRIVATE (instance);
110
  PangoAttribute *normal_attr, *small_attr;
111
112
  priv->pkg_name = NULL;
113
  priv->pkg_description = NULL;
114
115
  priv->single_line_height = -1;
116
  priv->double_line_height = -1;
117
118
  normal_attr = pango_attr_scale_new (PANGO_SCALE_MEDIUM);
119
  normal_attr->start_index = 0;
120
  normal_attr->end_index = G_MAXINT;
121
122
  small_attr = pango_attr_scale_new (PANGO_SCALE_SMALL);
123
  small_attr->start_index = 0;
124
  small_attr->end_index = G_MAXINT;
125
126
  priv->scale_medium_attr_list = pango_attr_list_new ();
127
  pango_attr_list_insert (priv->scale_medium_attr_list,
128
                          normal_attr);
129
130
  priv->scale_small_attr_list = pango_attr_list_new ();
131
  pango_attr_list_insert (priv->scale_small_attr_list,
132
                          small_attr);
133
134
  return;
135
}
136
137
static void
138
package_info_cell_renderer_finalize (GObject *object)
139
{
140
  PackageInfoCellRendererPrivate *priv = PACKAGE_INFO_CELL_RENDERER_GET_PRIVATE (object);
141
142
  if (priv->pkg_name)
143
    g_free (priv->pkg_name);
144
145
  if (priv->pkg_description)
146
    g_free (priv->pkg_description);
147
148
  pango_attr_list_unref (priv->scale_medium_attr_list);
149
  pango_attr_list_unref (priv->scale_small_attr_list);
150
151
  (*parent_class->finalize) (object);
152
153
  return;
154
}
155
156
static void
157
package_info_cell_renderer_class_init (PackageInfoCellRendererClass *klass)
158
{
159
  GObjectClass *object_class;
160
  GtkCellRendererClass *renderer_class;
161
162
  parent_class = g_type_class_peek_parent (klass);
163
  object_class = (GObjectClass*) klass;
164
  renderer_class = (GtkCellRendererClass*) klass;
165
166
  object_class->finalize = package_info_cell_renderer_finalize;
167
  object_class->get_property = package_info_cell_renderer_get_property;
168
  object_class->set_property = package_info_cell_renderer_set_property;
169
170
  renderer_class->get_size = package_info_cell_renderer_get_size;
171
  renderer_class->render = package_info_cell_renderer_render;
172
173
  g_object_class_install_property (object_class,
174
                                   PROP_PKG_NAME,
175
                                   g_param_spec_string ("package-name",
176
                                                        "Package Name",
177
                                                        "The name of the package",
178
                                                        NULL,
179
                                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
180
181
  g_object_class_install_property (object_class,
182
                                   PROP_PKG_DESCRIPTION,
183
                                   g_param_spec_string ("package-description",
184
                                                        "Package description",
185
                                                        "The description of the package",
186
                                                        NULL,
187
                                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
188
189
  g_type_class_add_private (object_class, sizeof (PackageInfoCellRendererPrivate));
190
191
  return;
192
}
193
194
static void
195
package_info_cell_renderer_get_property (GObject              *object,
196
                                         guint                param_id,
197
                                         GValue               *value,
198
                                         GParamSpec           *pspec)
199
{
200
  PackageInfoCellRendererPrivate *priv = PACKAGE_INFO_CELL_RENDERER_GET_PRIVATE (object);
201
202
  switch (param_id)
203
  {
204
  case PROP_PKG_NAME:
205
    g_value_set_string (value, priv->pkg_name);
206
    break;
207
  case PROP_PKG_DESCRIPTION:
208
    g_value_set_string (value, priv->pkg_description);
209
    break;
210
  default:
211
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
212
    break;
213
  }
214
}
215
216
static void
217
package_info_cell_renderer_set_property (GObject              *object,
218
                                         guint                param_id,
219
                                         const GValue         *value,
220
                                         GParamSpec           *pspec)
221
{
222
  PackageInfoCellRendererPrivate *priv = PACKAGE_INFO_CELL_RENDERER_GET_PRIVATE (object);
223
224
  switch (param_id)
225
    {
226
    case PROP_PKG_NAME:
227
      if (priv->pkg_name)
228
        g_free (priv->pkg_name);
229
      priv->pkg_name = g_value_dup_string (value);
230
      break;
231
    case PROP_PKG_DESCRIPTION:
232
      if (priv->pkg_description)
233
        g_free (priv->pkg_description);
234
      priv->pkg_description = g_value_dup_string (value);
235
      if (priv->pkg_description != NULL)
236
        g_strstrip (priv->pkg_description);
237
      break;
238
    default:
239
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
240
      break;
241
    }
242
}
243
244
GType
245
package_info_cell_renderer_get_type (void)
246
{
247
  static GType type = 0;
248
249
  if (G_UNLIKELY(type == 0))
250
    {
251
      static const GTypeInfo info =
252
        {
253
            sizeof (PackageInfoCellRendererClass),
254
            NULL,   /* base_init */
255
            NULL,   /* base_finalize */
256
            (GClassInitFunc) package_info_cell_renderer_class_init,   /* class_init */
257
            NULL,   /* class_finalize */
258
            NULL,   /* class_data */
259
            sizeof (PackageInfoCellRenderer),
260
            0,      /* n_preallocs */
261
            package_info_cell_renderer_instance_init    /* instance_init */
262
        };
263
264
      type = g_type_register_static (GTK_TYPE_CELL_RENDERER,
265
                                     "PackageInfoCellRenderer",
266
                                     &info, 0);
267
    }
268
269
  return type;
270
}
271
272
static void
273
package_info_cell_renderer_get_size     (GtkCellRenderer      *cell,
274
                                         GtkWidget            *widget,
275
                                         GdkRectangle         *rectangle,
276
                                         gint                 *x_offset,
277
                                         gint                 *y_offset,
278
                                         gint                 *width,
279
                                         gint                 *height)
280
281
{
282
  PackageInfoCellRendererPrivate *priv;
283
284
  priv = PACKAGE_INFO_CELL_RENDERER_GET_PRIVATE (cell);
285
286
  /* only height will be set */
287
  if (height)
288
    {
289
      PangoContext *context;
290
      PangoFontMetrics *metrics;
291
      PangoFontDescription *font_desc;
292
      gint row_height;
293
294
      if (priv->single_line_height < 0)
295
        {
296
          font_desc = pango_font_description_copy_static (widget->style->font_desc);
297
298
          /* one line, PANGO_SCALE_MEDIUM */
299
          pango_font_description_set_size (font_desc,
300
              PANGO_SCALE_MEDIUM * pango_font_description_get_size (font_desc));
301
302
          context = gtk_widget_get_pango_context (widget);
303
304
          metrics = pango_context_get_metrics (context,
305
              font_desc,
306
              pango_context_get_language (context));
307
308
          row_height = (pango_font_metrics_get_ascent (metrics) +
309
              pango_font_metrics_get_descent (metrics));
310
311
          pango_font_metrics_unref (metrics);
312
          pango_font_description_free (font_desc);
313
314
          priv->single_line_height = 2 * cell->ypad + PANGO_PIXELS (row_height);
315
        }
316
317
      if ((priv->pkg_description == NULL) || (priv->pkg_description[0] == '\0'))
318
        {
319
          *height = priv->single_line_height;
320
          return;
321
        }
322
      else
323
        {
324
          if (priv->double_line_height < 0)
325
            {
326
              font_desc = pango_font_description_copy_static (widget->style->font_desc);
327
328
              /* two lines, PANGO_SCALE_MEDIUM and PANGO_SCALE_SMALL*/
329
              pango_font_description_set_size (font_desc,
330
                  PANGO_SCALE_SMALL * pango_font_description_get_size (font_desc));
331
332
              context = gtk_widget_get_pango_context (widget);
333
334
              metrics = pango_context_get_metrics (context,
335
                  font_desc,
336
                  pango_context_get_language (context));
337
338
              row_height = (pango_font_metrics_get_ascent (metrics) +
339
                  pango_font_metrics_get_descent (metrics));
340
341
              pango_font_metrics_unref (metrics);
342
              pango_font_description_free (font_desc);
343
344
              priv->double_line_height = priv->single_line_height + cell->ypad + PANGO_PIXELS (row_height);
345
            }
346
347
          *height = priv->double_line_height;
348
          return;
349
        }
350
    }
351
}
352
353
static GtkStateType
354
cell_get_state (GtkCellRenderer *cell,
355
                GtkWidget *widget, GtkCellRendererState flags)
356
{
357
  GtkStateType state;
358
359
  if (!cell->sensitive)
360
    {
361
      state = GTK_STATE_INSENSITIVE;
362
    }
363
  else if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
364
    {
365
      if (GTK_WIDGET_HAS_FOCUS (widget))
366
        state = GTK_STATE_SELECTED;
367
      else
368
        state = GTK_STATE_ACTIVE;
369
    }
370
  else if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT &&
371
	   GTK_WIDGET_STATE (widget) == GTK_STATE_PRELIGHT)
372
    {
373
#ifdef MAEMO_CHANGES
374
      state = GTK_STATE_NORMAL;
375
#else
376
      state = GTK_STATE_PRELIGHT;
377
#endif /* MAEMO_CHANGES */
378
    }
379
  else
380
    {
381
      if (GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE)
382
        state = GTK_STATE_INSENSITIVE;
383
      else
384
        state = GTK_STATE_NORMAL;
385
    }
386
387
  return state;
388
}
389
390
static PangoLayout *
391
maybe_make_layout (GtkWidget *widget,
392
      	      	   const char *str,
393
		   PangoAttrList *attrs,
394
		   PangoAlignment align,
395
		   gint *width,
396
		   gint *height)
397
{
398
  PangoLayout *layout = NULL;
399
400
  if (str && str[0] != '\0')
401
    {
402
      layout = gtk_widget_create_pango_layout (widget, str);
403
      pango_layout_set_attributes (layout, attrs);
404
      pango_layout_get_pixel_size (layout, width, height);
405
      pango_layout_set_alignment (layout, align);
406
    }
407
408
  return layout;
409
}
410
411
static void
412
paint_row (PangoLayout *layout,
413
           int width, int height,
414
           PangoAttrList *attrs,
415
           GtkCellRenderer *cell,
416
           GdkDrawable *window,
417
           GtkWidget *widget,
418
           GdkRectangle *cell_area,
419
           GdkRectangle *expose_area,
420
           GtkStateType state,
421
           int y_coord,
422
           gboolean is_above_offset)
423
{
424
  if (layout)
425
    {
426
      gint available_width;
427
428
      available_width = cell_area->width - 2 * DEFAULT_MARGIN;
429
      if (width > available_width)
430
        width = available_width;
431
432
      pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
433
      pango_layout_set_width (layout, width * PANGO_SCALE);
434
435
      gtk_paint_layout (widget->style,
436
                        window,
437
                        state,
438
                        TRUE,
439
                        expose_area,
440
                        widget,
441
                        "cellrenderertext",
442
                        cell_area->x + DEFAULT_MARGIN,
443
                        y_coord - (is_above_offset ? height : 0),
444
                        layout);
445
446
      g_object_unref (layout);
447
    }
448
}
449
450
static void
451
package_info_cell_renderer_render       (GtkCellRenderer      *cell,
452
                                         GdkDrawable          *window,
453
                                         GtkWidget            *widget,
454
                                         GdkRectangle         *background_area,
455
                                         GdkRectangle         *cell_area,
456
                                         GdkRectangle         *expose_area,
457
                                         GtkCellRendererState flags)
458
{
459
  PackageInfoCellRendererPrivate *priv;
460
461
  /* example code from eog-pixbuf-cell-renderer.c : */
462
  gint name_w, name_h;
463
  gint description_w, description_h;
464
  gint y_coord;
465
  PangoLayout *name_layout, *description_layout;
466
  GtkStateType state;
467
468
  name_w = name_h = 0;
469
  description_w = description_h = 0;
470
471
  priv = PACKAGE_INFO_CELL_RENDERER_GET_PRIVATE (cell);
472
473
  state = cell_get_state (cell, widget, flags);
474
475
  name_layout = maybe_make_layout (widget,
476
                                   priv->pkg_name,
477
                                   priv->scale_medium_attr_list,
478
                                   PANGO_ALIGN_LEFT,
479
                                   &name_w, &name_h);
480
481
  description_layout = maybe_make_layout (widget,
482
                                          priv->pkg_description,
483
                                          priv->scale_small_attr_list,
484
                                          PANGO_ALIGN_LEFT,
485
                                          &description_w, &description_h);
486
  y_coord =
487
    cell_area->y + (cell_area->height - (name_h + description_h)) / 2 + name_h;
488
489
  paint_row (name_layout, name_w, name_h,
490
             priv->scale_medium_attr_list,
491
             cell, window, widget,
492
             cell_area, expose_area,
493
             state, y_coord, TRUE);
494
495
  paint_row (description_layout, description_w, description_h,
496
             priv->scale_small_attr_list,
497
             cell, window, widget,
498
             cell_area, expose_area,
499
             state, y_coord, FALSE);
500
}
501
502
static void
503
style_set (PackageInfoCellRenderer *cr,
504
      	   GtkStyle *old_style,
505
	   GtkWidget *widget)
506
{
507
  GtkStyle *style = gtk_widget_get_style (widget);
508
509
  if (style)
510
    {
511
      GdkColor clr;
512
513
      if (gtk_style_lookup_color (style, "SecondaryTextColor", &clr))
514
      	{
515
	  PackageInfoCellRendererPrivate *priv =
516
            PACKAGE_INFO_CELL_RENDERER_GET_PRIVATE (cr);
517
518
      	  if (priv->scale_small_attr_list)
519
	    {
520
	      PangoAttribute *small_attr, *clr_attr = NULL;
521
522
	      small_attr = pango_attr_scale_new (PANGO_SCALE_SMALL);
523
	      small_attr->start_index = 0;
524
	      small_attr->end_index = G_MAXINT;
525
526
	      clr_attr = pango_attr_foreground_new (clr.red,
527
	      	      	      	      	      	    clr.green,
528
						    clr.blue);
529
	      clr_attr->start_index = 0;
530
	      clr_attr->end_index = G_MAXINT;
531
532
	      pango_attr_list_unref (priv->scale_small_attr_list);
533
	      priv->scale_small_attr_list = pango_attr_list_new ();
534
	      pango_attr_list_insert (priv->scale_small_attr_list,
535
	      	      	      	      small_attr);
536
	      pango_attr_list_insert (priv->scale_small_attr_list,
537
	      	      	      	      clr_attr);
538
	    }
539
	}
540
    }
541
}
542
543
544
545
void
546
package_info_cell_renderer_listen_style (PackageInfoCellRenderer *cr,
547
      	      	      	      	      	 GtkWidget *widget)
548
{
549
  style_set (cr, NULL, widget);
550
  g_signal_connect_swapped (G_OBJECT (widget), "style-set",
551
                            (GCallback) style_set, cr);
552
}