]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-cell-renderer-text.c
factor out start_gnome_contacts()
[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         gchar   **types;
42
43         gboolean  compact;
44 } EmpathyCellRendererTextPriv;
45
46 static void cell_renderer_text_finalize          (GObject                     *object);
47 static void cell_renderer_text_get_property      (GObject                     *object,
48                                                   guint                        param_id,
49                                                   GValue                      *value,
50                                                   GParamSpec                  *pspec);
51 static void cell_renderer_text_set_property      (GObject                     *object,
52                                                   guint                        param_id,
53                                                   const GValue                *value,
54                                                   GParamSpec                  *pspec);
55 static void cell_renderer_text_render            (GtkCellRenderer             *cell,
56                                                   cairo_t *cr,
57                                                   GtkWidget                   *widget,
58                                                   const GdkRectangle          *background_area,
59                                                   const GdkRectangle          *cell_area,
60                                                   GtkCellRendererState         flags);
61 static void cell_renderer_text_update_text       (EmpathyCellRendererText      *cell,
62                                                   GtkWidget                   *widget,
63                                                   gboolean                     selected);
64
65 /* Properties */
66 enum {
67         PROP_0,
68         PROP_NAME,
69         PROP_PRESENCE_TYPE,
70         PROP_STATUS,
71         PROP_IS_GROUP,
72         PROP_COMPACT,
73         PROP_CLIENT_TYPES
74 };
75
76 G_DEFINE_TYPE (EmpathyCellRendererText, empathy_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT);
77
78 static void
79 cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *renderer,
80                                                                 GtkWidget *widget,
81                                                                 gint width,
82                                                                 gint *minimum_size,
83                                                                 gint *natural_size)
84 {
85         EmpathyCellRendererText *self = EMPATHY_CELL_RENDERER_TEXT (renderer);
86         EmpathyCellRendererTextPriv *priv = GET_PRIV (self);
87
88         /* Only update if not already valid so we get the right size. */
89         cell_renderer_text_update_text (self, widget, priv->is_selected);
90
91         GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->
92                         get_preferred_height_for_width (renderer, widget, width,
93                                         minimum_size, natural_size);
94 }
95
96
97 static void
98 empathy_cell_renderer_text_class_init (EmpathyCellRendererTextClass *klass)
99 {
100         GObjectClass         *object_class;
101         GtkCellRendererClass *cell_class;
102         GParamSpec           *spec;
103
104         object_class = G_OBJECT_CLASS (klass);
105         cell_class = GTK_CELL_RENDERER_CLASS (klass);
106
107         object_class->finalize = cell_renderer_text_finalize;
108
109         object_class->get_property = cell_renderer_text_get_property;
110         object_class->set_property = cell_renderer_text_set_property;
111
112         cell_class->get_preferred_height_for_width = cell_renderer_text_get_preferred_height_for_width;
113         cell_class->render = cell_renderer_text_render;
114
115         spec = g_param_spec_string ("name", "Name", "Contact name", NULL,
116                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
117         g_object_class_install_property (object_class, PROP_NAME, spec);
118
119         spec = g_param_spec_uint ("presence-type", "TpConnectionPresenceType",
120                 "The contact's presence type",
121                 0, G_MAXUINT, /* Telepathy enum, can be extended */
122                 TP_CONNECTION_PRESENCE_TYPE_UNKNOWN,
123                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
124         g_object_class_install_property (object_class, PROP_PRESENCE_TYPE,
125                 spec);
126
127         spec = g_param_spec_string ("status", "Status message",
128                 "Contact's custom status message", NULL,
129                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
130         g_object_class_install_property (object_class, PROP_STATUS, spec);
131
132         spec = g_param_spec_boolean ("is-group", "Is group",
133                 "Whether this cell is a group", FALSE,
134                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
135         g_object_class_install_property (object_class, PROP_IS_GROUP, spec);
136
137         spec = g_param_spec_boolean ("compact", "Compact",
138                 "TRUE to show the status alongside the contact name;"
139                 "FALSE to show it on its own line",
140                 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
141         g_object_class_install_property (object_class, PROP_COMPACT, spec);
142
143         spec = g_param_spec_boxed ("client-types", "Contact client types",
144                 "Client types of the contact",
145                 G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
146         g_object_class_install_property (object_class, PROP_CLIENT_TYPES, spec);
147
148         g_type_class_add_private (object_class, sizeof (EmpathyCellRendererTextPriv));
149 }
150
151 static void
152 empathy_cell_renderer_text_init (EmpathyCellRendererText *cell)
153 {
154         EmpathyCellRendererTextPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (cell,
155                 EMPATHY_TYPE_CELL_RENDERER_TEXT, EmpathyCellRendererTextPriv);
156
157         cell->priv = priv;
158         g_object_set (cell,
159                       "ellipsize", PANGO_ELLIPSIZE_END,
160                       NULL);
161
162         priv->name = g_strdup ("");
163         priv->status = g_strdup ("");
164         priv->compact = FALSE;
165 }
166
167 static void
168 cell_renderer_text_finalize (GObject *object)
169 {
170         EmpathyCellRendererText     *cell;
171         EmpathyCellRendererTextPriv *priv;
172
173         cell = EMPATHY_CELL_RENDERER_TEXT (object);
174         priv = GET_PRIV (cell);
175
176         g_free (priv->name);
177         g_free (priv->status);
178         g_strfreev (priv->types);
179
180         (G_OBJECT_CLASS (empathy_cell_renderer_text_parent_class)->finalize) (object);
181 }
182
183 static void
184 cell_renderer_text_get_property (GObject    *object,
185                                  guint       param_id,
186                                  GValue     *value,
187                                  GParamSpec *pspec)
188 {
189         EmpathyCellRendererText     *cell;
190         EmpathyCellRendererTextPriv *priv;
191
192         cell = EMPATHY_CELL_RENDERER_TEXT (object);
193         priv = GET_PRIV (cell);
194
195         switch (param_id) {
196         case PROP_NAME:
197                 g_value_set_string (value, priv->name);
198                 break;
199         case PROP_PRESENCE_TYPE:
200                 g_value_set_uint (value, priv->presence_type);
201                 break;
202         case PROP_STATUS:
203                 g_value_set_string (value, priv->status);
204                 break;
205         case PROP_IS_GROUP:
206                 g_value_set_boolean (value, priv->is_group);
207                 break;
208         case PROP_COMPACT:
209                 g_value_set_boolean (value, priv->compact);
210                 break;
211         case PROP_CLIENT_TYPES:
212                 g_value_set_boxed (value, priv->types);
213                 break;
214         default:
215                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
216                 break;
217         }
218 }
219
220 static void
221 cell_renderer_text_set_property (GObject      *object,
222                                  guint         param_id,
223                                  const GValue *value,
224                                  GParamSpec   *pspec)
225 {
226         EmpathyCellRendererText     *cell;
227         EmpathyCellRendererTextPriv *priv;
228         const gchar                *str;
229
230         cell = EMPATHY_CELL_RENDERER_TEXT (object);
231         priv = GET_PRIV (cell);
232
233         switch (param_id) {
234         case PROP_NAME:
235                 g_free (priv->name);
236                 str = g_value_get_string (value);
237                 priv->name = g_strdup (str ? str : "");
238                 g_strdelimit (priv->name, "\n\r\t", ' ');
239                 priv->is_valid = FALSE;
240                 break;
241         case PROP_PRESENCE_TYPE:
242                 priv->presence_type = g_value_get_uint (value);
243                 priv->is_valid = FALSE;
244                 break;
245         case PROP_STATUS:
246                 g_free (priv->status);
247                 str = g_value_get_string (value);
248                 priv->status = g_strdup (str ? str : "");
249                 g_strdelimit (priv->status, "\n\r\t", ' ');
250                 priv->is_valid = FALSE;
251                 break;
252         case PROP_IS_GROUP:
253                 priv->is_group = g_value_get_boolean (value);
254                 priv->is_valid = FALSE;
255                 break;
256         case PROP_COMPACT:
257                 priv->compact = g_value_get_boolean (value);
258                 priv->is_valid = FALSE;
259                 break;
260         case PROP_CLIENT_TYPES:
261                 g_strfreev (priv->types);
262                 priv->types = g_value_dup_boxed (value);
263                 priv->is_valid = FALSE;
264                 break;
265         default:
266                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
267                 break;
268         }
269 }
270
271 static void
272 cell_renderer_text_render (GtkCellRenderer      *cell,
273                            cairo_t *cr,
274                            GtkWidget            *widget,
275                            const GdkRectangle   *background_area,
276                            const GdkRectangle   *cell_area,
277                            GtkCellRendererState  flags)
278 {
279         EmpathyCellRendererText *celltext;
280
281         celltext = EMPATHY_CELL_RENDERER_TEXT (cell);
282
283         cell_renderer_text_update_text (celltext,
284                                         widget,
285                                         (flags & GTK_CELL_RENDERER_SELECTED));
286
287         (GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->render) (
288                 cell, cr,
289                 widget,
290                 background_area,
291                 cell_area,
292                 flags);
293 }
294
295 static void
296 cell_renderer_text_update_text (EmpathyCellRendererText *cell,
297                                 GtkWidget              *widget,
298                                 gboolean                selected)
299 {
300         EmpathyCellRendererTextPriv *priv;
301         const PangoFontDescription *font_desc;
302         PangoAttrList              *attr_list;
303         PangoAttribute             *attr_color = NULL, *attr_size;
304         GtkStyleContext            *style;
305         gchar                      *str;
306         gint                        font_size;
307
308         priv = GET_PRIV (cell);
309
310         if (priv->is_valid && priv->is_selected == selected) {
311                 return;
312         }
313
314         if (priv->is_group) {
315                 g_object_set (cell,
316                               "visible", TRUE,
317                               "weight", PANGO_WEIGHT_BOLD,
318                               "text", priv->name,
319                               "attributes", NULL,
320                               "xpad", 1,
321                               "ypad", 1,
322                               NULL);
323
324                 priv->is_selected = selected;
325                 priv->is_valid = TRUE;
326                 return;
327         }
328
329         style = gtk_widget_get_style_context (widget);
330
331         attr_list = pango_attr_list_new ();
332
333         font_desc = gtk_style_context_get_font (style, GTK_STATE_FLAG_NORMAL);
334         font_size = pango_font_description_get_size (font_desc);
335         attr_size = pango_attr_size_new (font_size / 1.2);
336         attr_size->start_index = strlen (priv->name) + 1;
337         attr_size->end_index = -1;
338         pango_attr_list_insert (attr_list, attr_size);
339
340         if (!selected) {
341                 GdkRGBA color;
342
343                 gtk_style_context_get_color (style, 0, &color);
344
345                 attr_color = pango_attr_foreground_new (color.red * 0xffff,
346                                                         color.green * 0xffff,
347                                                         color.blue * 0xffff);
348                 attr_color->start_index = attr_size->start_index;
349                 attr_color->end_index = -1;
350                 pango_attr_list_insert (attr_list, attr_color);
351         }
352
353         if (priv->compact) {
354                 if (EMP_STR_EMPTY (priv->status)) {
355                         str = g_strdup (priv->name);
356                 } else {
357                         str = g_strdup_printf ("%s %s", priv->name, priv->status);
358                 }
359         } else {
360                 const gchar *status = priv->status;
361                 gboolean on_a_phone = FALSE;
362
363                 if (EMP_STR_EMPTY (priv->status)) {
364                         status = empathy_presence_get_default_message (priv->presence_type);
365                 }
366
367                 if (!priv->is_group && priv->types != NULL && g_strv_length (priv->types) > 0
368                     && !tp_strdiff (priv->types[0], "phone")) {
369                         on_a_phone = TRUE;
370                         /* We want the phone black. */
371                         if (attr_color)
372                                 attr_color->start_index += 3;
373                 }
374
375                 if (status == NULL)
376                         str = g_strdup (priv->name);
377                 else
378                         str = g_strdup_printf ("%s\n%s%s", priv->name,
379                                                on_a_phone ? "☎  " : "",
380                                                status);
381         }
382
383         g_object_set (cell,
384                       "visible", TRUE,
385                       "weight", PANGO_WEIGHT_NORMAL,
386                       "text", str,
387                       "attributes", attr_list,
388                       "xpad", 0,
389                       "ypad", 1,
390                       NULL);
391
392         g_free (str);
393         pango_attr_list_unref (attr_list);
394
395         priv->is_selected = selected;
396         priv->is_valid = TRUE;
397 }
398
399 GtkCellRenderer *
400 empathy_cell_renderer_text_new (void)
401 {
402         return g_object_new (EMPATHY_TYPE_CELL_RENDERER_TEXT, NULL);
403 }