]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-cell-renderer-text.c
Merge remote branch 'vminko/fix-634688-v3'
[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
26 #include <string.h>
27
28 #include <libempathy/empathy-utils.h>
29 #include "empathy-cell-renderer-text.h"
30
31 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCellRendererText)
32 typedef struct {
33         gchar    *name;
34         TpConnectionPresenceType presence_type;
35         gchar    *status;
36         gboolean  is_group;
37
38         gboolean  is_valid;
39         gboolean  is_selected;
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 };
72
73 G_DEFINE_TYPE (EmpathyCellRendererText, empathy_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT);
74
75 static void
76 cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *renderer,
77                                                                 GtkWidget *widget,
78                                                                 gint width,
79                                                                 gint *minimum_size,
80                                                                 gint *natural_size)
81 {
82         EmpathyCellRendererText *self = EMPATHY_CELL_RENDERER_TEXT (renderer);
83         EmpathyCellRendererTextPriv *priv = GET_PRIV (self);
84
85         /* Only update if not already valid so we get the right size. */
86         cell_renderer_text_update_text (self, widget, priv->is_selected);
87
88         GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->
89                         get_preferred_height_for_width (renderer, widget, width,
90                                         minimum_size, natural_size);
91 }
92
93
94 static void
95 empathy_cell_renderer_text_class_init (EmpathyCellRendererTextClass *klass)
96 {
97         GObjectClass         *object_class;
98         GtkCellRendererClass *cell_class;
99         GParamSpec           *spec;
100
101         object_class = G_OBJECT_CLASS (klass);
102         cell_class = GTK_CELL_RENDERER_CLASS (klass);
103
104         object_class->finalize = cell_renderer_text_finalize;
105
106         object_class->get_property = cell_renderer_text_get_property;
107         object_class->set_property = cell_renderer_text_set_property;
108
109         cell_class->get_preferred_height_for_width = cell_renderer_text_get_preferred_height_for_width;
110         cell_class->render = cell_renderer_text_render;
111
112         spec = g_param_spec_string ("name", "Name", "Contact name", NULL,
113                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
114         g_object_class_install_property (object_class, PROP_NAME, spec);
115
116         spec = g_param_spec_uint ("presence-type", "TpConnectionPresenceType",
117                 "The contact's presence type",
118                 0, G_MAXUINT, /* Telepathy enum, can be extended */
119                 TP_CONNECTION_PRESENCE_TYPE_UNKNOWN,
120                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
121         g_object_class_install_property (object_class, PROP_PRESENCE_TYPE,
122                 spec);
123
124         spec = g_param_spec_string ("status", "Status message",
125                 "Contact's custom status message", NULL,
126                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
127         g_object_class_install_property (object_class, PROP_STATUS, spec);
128
129         spec = g_param_spec_boolean ("is-group", "Is group",
130                 "Whether this cell is a group", FALSE,
131                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
132         g_object_class_install_property (object_class, PROP_IS_GROUP, spec);
133
134         spec = g_param_spec_boolean ("compact", "Compact",
135                 "TRUE to show the status alongside the contact name;"
136                 "FALSE to show it on its own line",
137                 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
138         g_object_class_install_property (object_class, PROP_COMPACT, spec);
139
140         g_type_class_add_private (object_class, sizeof (EmpathyCellRendererTextPriv));
141 }
142
143 static void
144 empathy_cell_renderer_text_init (EmpathyCellRendererText *cell)
145 {
146         EmpathyCellRendererTextPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (cell,
147                 EMPATHY_TYPE_CELL_RENDERER_TEXT, EmpathyCellRendererTextPriv);
148
149         cell->priv = priv;
150         g_object_set (cell,
151                       "ellipsize", PANGO_ELLIPSIZE_END,
152                       NULL);
153
154         priv->name = g_strdup ("");
155         priv->status = g_strdup ("");
156         priv->compact = FALSE;
157 }
158
159 static void
160 cell_renderer_text_finalize (GObject *object)
161 {
162         EmpathyCellRendererText     *cell;
163         EmpathyCellRendererTextPriv *priv;
164
165         cell = EMPATHY_CELL_RENDERER_TEXT (object);
166         priv = GET_PRIV (cell);
167
168         g_free (priv->name);
169         g_free (priv->status);
170
171         (G_OBJECT_CLASS (empathy_cell_renderer_text_parent_class)->finalize) (object);
172 }
173
174 static void
175 cell_renderer_text_get_property (GObject    *object,
176                                  guint       param_id,
177                                  GValue     *value,
178                                  GParamSpec *pspec)
179 {
180         EmpathyCellRendererText     *cell;
181         EmpathyCellRendererTextPriv *priv;
182
183         cell = EMPATHY_CELL_RENDERER_TEXT (object);
184         priv = GET_PRIV (cell);
185
186         switch (param_id) {
187         case PROP_NAME:
188                 g_value_set_string (value, priv->name);
189                 break;
190         case PROP_PRESENCE_TYPE:
191                 g_value_set_uint (value, priv->presence_type);
192                 break;
193         case PROP_STATUS:
194                 g_value_set_string (value, priv->status);
195                 break;
196         case PROP_IS_GROUP:
197                 g_value_set_boolean (value, priv->is_group);
198                 break;
199         case PROP_COMPACT:
200                 g_value_set_boolean (value, priv->compact);
201                 break;
202         default:
203                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
204                 break;
205         }
206 }
207
208 static void
209 cell_renderer_text_set_property (GObject      *object,
210                                  guint         param_id,
211                                  const GValue *value,
212                                  GParamSpec   *pspec)
213 {
214         EmpathyCellRendererText     *cell;
215         EmpathyCellRendererTextPriv *priv;
216         const gchar                *str;
217
218         cell = EMPATHY_CELL_RENDERER_TEXT (object);
219         priv = GET_PRIV (cell);
220
221         switch (param_id) {
222         case PROP_NAME:
223                 g_free (priv->name);
224                 str = g_value_get_string (value);
225                 priv->name = g_strdup (str ? str : "");
226                 g_strdelimit (priv->name, "\n\r\t", ' ');
227                 priv->is_valid = FALSE;
228                 break;
229         case PROP_PRESENCE_TYPE:
230                 priv->presence_type = g_value_get_uint (value);
231                 priv->is_valid = FALSE;
232                 break;
233         case PROP_STATUS:
234                 g_free (priv->status);
235                 str = g_value_get_string (value);
236                 priv->status = g_strdup (str ? str : "");
237                 g_strdelimit (priv->status, "\n\r\t", ' ');
238                 priv->is_valid = FALSE;
239                 break;
240         case PROP_IS_GROUP:
241                 priv->is_group = g_value_get_boolean (value);
242                 priv->is_valid = FALSE;
243                 break;
244         case PROP_COMPACT:
245                 priv->compact = g_value_get_boolean (value);
246                 priv->is_valid = FALSE;
247                 break;
248         default:
249                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
250                 break;
251         }
252 }
253
254 static void
255 cell_renderer_text_render (GtkCellRenderer      *cell,
256                            cairo_t *cr,
257                            GtkWidget            *widget,
258                            const GdkRectangle   *background_area,
259                            const GdkRectangle   *cell_area,
260                            GtkCellRendererState  flags)
261 {
262         EmpathyCellRendererText *celltext;
263
264         celltext = EMPATHY_CELL_RENDERER_TEXT (cell);
265
266         cell_renderer_text_update_text (celltext,
267                                         widget,
268                                         (flags & GTK_CELL_RENDERER_SELECTED));
269
270         (GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->render) (
271                 cell, cr,
272                 widget,
273                 background_area,
274                 cell_area,
275                 flags);
276 }
277
278 static void
279 cell_renderer_text_update_text (EmpathyCellRendererText *cell,
280                                 GtkWidget              *widget,
281                                 gboolean                selected)
282 {
283         EmpathyCellRendererTextPriv *priv;
284         PangoAttrList              *attr_list;
285         PangoAttribute             *attr_color, *attr_size;
286         GtkStyle                   *style;
287         gchar                      *str;
288
289         priv = GET_PRIV (cell);
290
291         if (priv->is_valid && priv->is_selected == selected) {
292                 return;
293         }
294
295         if (priv->is_group) {
296                 g_object_set (cell,
297                               "visible", TRUE,
298                               "weight", PANGO_WEIGHT_BOLD,
299                               "text", priv->name,
300                               "attributes", NULL,
301                               "xpad", 1,
302                               "ypad", 1,
303                               NULL);
304
305                 priv->is_selected = selected;
306                 priv->is_valid = TRUE;
307                 return;
308         }
309
310         style = gtk_widget_get_style (widget);
311
312         attr_list = pango_attr_list_new ();
313
314         attr_size = pango_attr_size_new (pango_font_description_get_size (style->font_desc) / 1.2);
315         attr_size->start_index = strlen (priv->name) + 1;
316         attr_size->end_index = -1;
317         pango_attr_list_insert (attr_list, attr_size);
318
319         if (!selected) {
320                 GdkColor color;
321
322                 color = style->text_aa[GTK_STATE_NORMAL];
323
324                 attr_color = pango_attr_foreground_new (color.red, color.green, color.blue);
325                 attr_color->start_index = attr_size->start_index;
326                 attr_color->end_index = -1;
327                 pango_attr_list_insert (attr_list, attr_color);
328         }
329
330         if (priv->compact) {
331                 if (EMP_STR_EMPTY (priv->status)) {
332                         str = g_strdup (priv->name);
333                 } else {
334                         str = g_strdup_printf ("%s %s", priv->name, priv->status);
335                 }
336         } else {
337                 const gchar *status = priv->status;
338
339                 if (EMP_STR_EMPTY (priv->status)) {
340                         status = empathy_presence_get_default_message (priv->presence_type);
341                 }
342
343                 if (status == NULL)
344                         str = g_strdup (priv->name);
345                 else
346                         str = g_strdup_printf ("%s\n%s", priv->name, status);
347         }
348
349         g_object_set (cell,
350                       "visible", TRUE,
351                       "weight", PANGO_WEIGHT_NORMAL,
352                       "text", str,
353                       "attributes", attr_list,
354                       "xpad", 0,
355                       "ypad", 1,
356                       NULL);
357
358         g_free (str);
359         pango_attr_list_unref (attr_list);
360
361         priv->is_selected = selected;
362         priv->is_valid = TRUE;
363 }
364
365 GtkCellRenderer *
366 empathy_cell_renderer_text_new (void)
367 {
368         return g_object_new (EMPATHY_TYPE_CELL_RENDERER_TEXT, NULL);
369 }