]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-cell-renderer-text.c
GNOME Goal: Update icon names
[empathy.git] / libempathy-gtk / empathy-cell-renderer-text.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2004-2007 Imendio AB
4  * Copyright (C) 2010 Collabora Ltd
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  *
21  * Authors: Mikael Hallendal <micke@imendio.com>
22  */
23
24 #include "config.h"
25 #include "empathy-cell-renderer-text.h"
26
27 #include "empathy-utils.h"
28
29 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCellRendererText)
30 typedef struct {
31         gchar    *name;
32         TpConnectionPresenceType presence_type;
33         gchar    *status;
34         gboolean  is_group;
35
36         gboolean  is_valid;
37         gboolean  is_selected;
38
39         gchar   **types;
40
41         gboolean  compact;
42 } EmpathyCellRendererTextPriv;
43
44 static void cell_renderer_text_finalize          (GObject                     *object);
45 static void cell_renderer_text_get_property      (GObject                     *object,
46                                                   guint                        param_id,
47                                                   GValue                      *value,
48                                                   GParamSpec                  *pspec);
49 static void cell_renderer_text_set_property      (GObject                     *object,
50                                                   guint                        param_id,
51                                                   const GValue                *value,
52                                                   GParamSpec                  *pspec);
53 static void cell_renderer_text_render            (GtkCellRenderer             *cell,
54                                                   cairo_t *cr,
55                                                   GtkWidget                   *widget,
56                                                   const GdkRectangle          *background_area,
57                                                   const GdkRectangle          *cell_area,
58                                                   GtkCellRendererState         flags);
59 static void cell_renderer_text_update_text       (EmpathyCellRendererText      *cell,
60                                                   GtkWidget                   *widget,
61                                                   gboolean                     selected);
62
63 /* Properties */
64 enum {
65         PROP_0,
66         PROP_NAME,
67         PROP_PRESENCE_TYPE,
68         PROP_STATUS,
69         PROP_IS_GROUP,
70         PROP_COMPACT,
71         PROP_CLIENT_TYPES
72 };
73
74 G_DEFINE_TYPE (EmpathyCellRendererText, empathy_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT);
75
76 static void
77 cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *renderer,
78                                                                 GtkWidget *widget,
79                                                                 gint width,
80                                                                 gint *minimum_size,
81                                                                 gint *natural_size)
82 {
83         EmpathyCellRendererText *self = EMPATHY_CELL_RENDERER_TEXT (renderer);
84         EmpathyCellRendererTextPriv *priv = GET_PRIV (self);
85
86         /* Only update if not already valid so we get the right size. */
87         cell_renderer_text_update_text (self, widget, priv->is_selected);
88
89         GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->
90                         get_preferred_height_for_width (renderer, widget, width,
91                                         minimum_size, natural_size);
92 }
93
94
95 static void
96 empathy_cell_renderer_text_class_init (EmpathyCellRendererTextClass *klass)
97 {
98         GObjectClass         *object_class;
99         GtkCellRendererClass *cell_class;
100         GParamSpec           *spec;
101
102         object_class = G_OBJECT_CLASS (klass);
103         cell_class = GTK_CELL_RENDERER_CLASS (klass);
104
105         object_class->finalize = cell_renderer_text_finalize;
106
107         object_class->get_property = cell_renderer_text_get_property;
108         object_class->set_property = cell_renderer_text_set_property;
109
110         cell_class->get_preferred_height_for_width = cell_renderer_text_get_preferred_height_for_width;
111         cell_class->render = cell_renderer_text_render;
112
113         spec = g_param_spec_string ("name", "Name", "Contact name", NULL,
114                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
115         g_object_class_install_property (object_class, PROP_NAME, spec);
116
117         spec = g_param_spec_uint ("presence-type", "TpConnectionPresenceType",
118                 "The contact's presence type",
119                 0, G_MAXUINT, /* Telepathy enum, can be extended */
120                 TP_CONNECTION_PRESENCE_TYPE_UNKNOWN,
121                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
122         g_object_class_install_property (object_class, PROP_PRESENCE_TYPE,
123                 spec);
124
125         spec = g_param_spec_string ("status", "Status message",
126                 "Contact's custom status message", NULL,
127                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
128         g_object_class_install_property (object_class, PROP_STATUS, spec);
129
130         spec = g_param_spec_boolean ("is-group", "Is group",
131                 "Whether this cell is a group", FALSE,
132                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
133         g_object_class_install_property (object_class, PROP_IS_GROUP, spec);
134
135         spec = g_param_spec_boolean ("compact", "Compact",
136                 "TRUE to show the status alongside the contact name;"
137                 "FALSE to show it on its own line",
138                 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
139         g_object_class_install_property (object_class, PROP_COMPACT, spec);
140
141         spec = g_param_spec_boxed ("client-types", "Contact client types",
142                 "Client types of the contact",
143                 G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
144         g_object_class_install_property (object_class, PROP_CLIENT_TYPES, spec);
145
146         g_type_class_add_private (object_class, sizeof (EmpathyCellRendererTextPriv));
147 }
148
149 static void
150 empathy_cell_renderer_text_init (EmpathyCellRendererText *cell)
151 {
152         EmpathyCellRendererTextPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (cell,
153                 EMPATHY_TYPE_CELL_RENDERER_TEXT, EmpathyCellRendererTextPriv);
154
155         cell->priv = priv;
156         g_object_set (cell,
157                       "ellipsize", PANGO_ELLIPSIZE_END,
158                       NULL);
159
160         priv->name = g_strdup ("");
161         priv->status = g_strdup ("");
162         priv->compact = FALSE;
163 }
164
165 static void
166 cell_renderer_text_finalize (GObject *object)
167 {
168         EmpathyCellRendererText     *cell;
169         EmpathyCellRendererTextPriv *priv;
170
171         cell = EMPATHY_CELL_RENDERER_TEXT (object);
172         priv = GET_PRIV (cell);
173
174         g_free (priv->name);
175         g_free (priv->status);
176         g_strfreev (priv->types);
177
178         (G_OBJECT_CLASS (empathy_cell_renderer_text_parent_class)->finalize) (object);
179 }
180
181 static void
182 cell_renderer_text_get_property (GObject    *object,
183                                  guint       param_id,
184                                  GValue     *value,
185                                  GParamSpec *pspec)
186 {
187         EmpathyCellRendererText     *cell;
188         EmpathyCellRendererTextPriv *priv;
189
190         cell = EMPATHY_CELL_RENDERER_TEXT (object);
191         priv = GET_PRIV (cell);
192
193         switch (param_id) {
194         case PROP_NAME:
195                 g_value_set_string (value, priv->name);
196                 break;
197         case PROP_PRESENCE_TYPE:
198                 g_value_set_uint (value, priv->presence_type);
199                 break;
200         case PROP_STATUS:
201                 g_value_set_string (value, priv->status);
202                 break;
203         case PROP_IS_GROUP:
204                 g_value_set_boolean (value, priv->is_group);
205                 break;
206         case PROP_COMPACT:
207                 g_value_set_boolean (value, priv->compact);
208                 break;
209         case PROP_CLIENT_TYPES:
210                 g_value_set_boxed (value, priv->types);
211                 break;
212         default:
213                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
214                 break;
215         }
216 }
217
218 static void
219 cell_renderer_text_set_property (GObject      *object,
220                                  guint         param_id,
221                                  const GValue *value,
222                                  GParamSpec   *pspec)
223 {
224         EmpathyCellRendererText     *cell;
225         EmpathyCellRendererTextPriv *priv;
226         const gchar                *str;
227
228         cell = EMPATHY_CELL_RENDERER_TEXT (object);
229         priv = GET_PRIV (cell);
230
231         switch (param_id) {
232         case PROP_NAME:
233                 g_free (priv->name);
234                 str = g_value_get_string (value);
235                 priv->name = g_strdup (str ? str : "");
236                 g_strdelimit (priv->name, "\n\r\t", ' ');
237                 priv->is_valid = FALSE;
238                 break;
239         case PROP_PRESENCE_TYPE:
240                 priv->presence_type = g_value_get_uint (value);
241                 priv->is_valid = FALSE;
242                 break;
243         case PROP_STATUS:
244                 g_free (priv->status);
245                 str = g_value_get_string (value);
246                 priv->status = g_strdup (str ? str : "");
247                 g_strdelimit (priv->status, "\n\r\t", ' ');
248                 priv->is_valid = FALSE;
249                 break;
250         case PROP_IS_GROUP:
251                 priv->is_group = g_value_get_boolean (value);
252                 priv->is_valid = FALSE;
253                 break;
254         case PROP_COMPACT:
255                 priv->compact = g_value_get_boolean (value);
256                 priv->is_valid = FALSE;
257                 break;
258         case PROP_CLIENT_TYPES:
259                 g_strfreev (priv->types);
260                 priv->types = g_value_dup_boxed (value);
261                 priv->is_valid = FALSE;
262                 break;
263         default:
264                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
265                 break;
266         }
267 }
268
269 static void
270 cell_renderer_text_render (GtkCellRenderer      *cell,
271                            cairo_t *cr,
272                            GtkWidget            *widget,
273                            const GdkRectangle   *background_area,
274                            const GdkRectangle   *cell_area,
275                            GtkCellRendererState  flags)
276 {
277         EmpathyCellRendererText *celltext;
278
279         celltext = EMPATHY_CELL_RENDERER_TEXT (cell);
280
281         cell_renderer_text_update_text (celltext,
282                                         widget,
283                                         (flags & GTK_CELL_RENDERER_SELECTED));
284
285         (GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->render) (
286                 cell, cr,
287                 widget,
288                 background_area,
289                 cell_area,
290                 flags);
291 }
292
293 static void
294 cell_renderer_text_update_text (EmpathyCellRendererText *cell,
295                                 GtkWidget              *widget,
296                                 gboolean                selected)
297 {
298         EmpathyCellRendererTextPriv *priv;
299         const PangoFontDescription *font_desc;
300         PangoAttrList              *attr_list;
301         PangoAttribute             *attr_color = NULL, *attr_size;
302         GtkStyleContext            *style;
303         gchar                      *str;
304         gint                        font_size;
305
306         priv = GET_PRIV (cell);
307
308         if (priv->is_valid && priv->is_selected == selected) {
309                 return;
310         }
311
312         if (priv->is_group) {
313                 g_object_set (cell,
314                               "visible", TRUE,
315                               "weight", PANGO_WEIGHT_BOLD,
316                               "text", priv->name,
317                               "attributes", NULL,
318                               "xpad", 1,
319                               "ypad", 1,
320                               NULL);
321
322                 priv->is_selected = selected;
323                 priv->is_valid = TRUE;
324                 return;
325         }
326
327         style = gtk_widget_get_style_context (widget);
328
329         attr_list = pango_attr_list_new ();
330
331         font_desc = gtk_style_context_get_font (style, GTK_STATE_FLAG_NORMAL);
332         font_size = pango_font_description_get_size (font_desc);
333         attr_size = pango_attr_size_new (font_size / 1.2);
334         attr_size->start_index = strlen (priv->name) + 1;
335         attr_size->end_index = -1;
336         pango_attr_list_insert (attr_list, attr_size);
337
338         if (!selected) {
339                 GdkRGBA color;
340
341                 gtk_style_context_get_color (style, 0, &color);
342
343                 attr_color = pango_attr_foreground_new (color.red * 0xffff,
344                                                         color.green * 0xffff,
345                                                         color.blue * 0xffff);
346                 attr_color->start_index = attr_size->start_index;
347                 attr_color->end_index = -1;
348                 pango_attr_list_insert (attr_list, attr_color);
349         }
350
351         if (priv->compact) {
352                 if (EMP_STR_EMPTY (priv->status)) {
353                         str = g_strdup (priv->name);
354                 } else {
355                         str = g_strdup_printf ("%s %s", priv->name, priv->status);
356                 }
357         } else {
358                 const gchar *status = priv->status;
359                 gboolean on_a_phone = FALSE;
360
361                 if (EMP_STR_EMPTY (priv->status)) {
362                         status = empathy_presence_get_default_message (priv->presence_type);
363                 }
364
365                 if (!priv->is_group &&
366                                 empathy_client_types_contains_mobile_device (priv->types)) {
367                         on_a_phone = TRUE;
368                         /* We want the phone black. */
369                         if (attr_color)
370                                 attr_color->start_index += 3;
371                 }
372
373                 if (status == NULL)
374                         str = g_strdup (priv->name);
375                 else
376                         str = g_strdup_printf ("%s\n%s%s", priv->name,
377                                                on_a_phone ? "☎  " : "",
378                                                status);
379         }
380
381         g_object_set (cell,
382                       "visible", TRUE,
383                       "weight", PANGO_WEIGHT_NORMAL,
384                       "text", str,
385                       "attributes", attr_list,
386                       "xpad", 0,
387                       "ypad", 1,
388                       NULL);
389
390         g_free (str);
391         pango_attr_list_unref (attr_list);
392
393         priv->is_selected = selected;
394         priv->is_valid = TRUE;
395 }
396
397 GtkCellRenderer *
398 empathy_cell_renderer_text_new (void)
399 {
400         return g_object_new (EMPATHY_TYPE_CELL_RENDERER_TEXT, NULL);
401 }