]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-cell-renderer-expander.c
Remove the autogen.sh script and use gnome-autogen.sh instead.
[empathy.git] / libempathy-gtk / empathy-cell-renderer-expander.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2006-2007 Imendio AB
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  * 
20  * Authors: Kristian Rietveld <kris@imendio.com>
21  */
22
23 /* To do:
24  *  - should probably cancel animation if model changes
25  *  - need to handle case where node-in-animation is removed
26  *  - it only handles a single animation at a time; but I guess users
27  *    aren't fast enough to trigger two or more animations at once anyway :P
28  *    (could guard for this by just cancelling the "old" animation, and
29  *     start the new one).
30  */
31
32 #include <gtk/gtktreeview.h>
33
34 #include "empathy-cell-renderer-expander.h"
35
36 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CELL_RENDERER_EXPANDER, EmpathyCellRendererExpanderPriv))
37
38 static void     empathy_cell_renderer_expander_init         (EmpathyCellRendererExpander      *expander);
39 static void     empathy_cell_renderer_expander_class_init   (EmpathyCellRendererExpanderClass *klass);
40 static void     empathy_cell_renderer_expander_get_property (GObject                         *object,
41                                                             guint                            param_id,
42                                                             GValue                          *value,
43                                                             GParamSpec                      *pspec);
44 static void     empathy_cell_renderer_expander_set_property (GObject                         *object,
45                                                             guint                            param_id,
46                                                             const GValue                    *value,
47                                                             GParamSpec                      *pspec);
48 static void     empathy_cell_renderer_expander_finalize     (GObject                         *object);
49 static void     empathy_cell_renderer_expander_get_size     (GtkCellRenderer                 *cell,
50                                                             GtkWidget                       *widget,
51                                                             GdkRectangle                    *cell_area,
52                                                             gint                            *x_offset,
53                                                             gint                            *y_offset,
54                                                             gint                            *width,
55                                                             gint                            *height);
56 static void     empathy_cell_renderer_expander_render       (GtkCellRenderer                 *cell,
57                                                             GdkWindow                       *window,
58                                                             GtkWidget                       *widget,
59                                                             GdkRectangle                    *background_area,
60                                                             GdkRectangle                    *cell_area,
61                                                             GdkRectangle                    *expose_area,
62                                                             GtkCellRendererState             flags);
63 static gboolean empathy_cell_renderer_expander_activate     (GtkCellRenderer                 *cell,
64                                                             GdkEvent                        *event,
65                                                             GtkWidget                       *widget,
66                                                             const gchar                     *path,
67                                                             GdkRectangle                    *background_area,
68                                                             GdkRectangle                    *cell_area,
69                                                             GtkCellRendererState             flags);
70
71 enum {
72         PROP_0,
73         PROP_EXPANDER_STYLE,
74         PROP_EXPANDER_SIZE,
75         PROP_ACTIVATABLE
76 };
77
78 typedef struct _EmpathyCellRendererExpanderPriv EmpathyCellRendererExpanderPriv;
79
80 struct _EmpathyCellRendererExpanderPriv {
81         GtkExpanderStyle     expander_style;
82         gint                 expander_size;
83
84         GtkTreeView         *animation_view;
85         GtkTreeRowReference *animation_node;
86         GtkExpanderStyle     animation_style;
87         guint                animation_timeout;
88         GdkRectangle         animation_area;
89
90         guint                activatable : 1;
91         guint                animation_expanding : 1;
92 };
93
94 G_DEFINE_TYPE (EmpathyCellRendererExpander, empathy_cell_renderer_expander, GTK_TYPE_CELL_RENDERER)
95
96 static void
97 empathy_cell_renderer_expander_init (EmpathyCellRendererExpander *expander)
98 {
99         EmpathyCellRendererExpanderPriv *priv;
100
101         priv = GET_PRIV (expander);
102
103         priv->expander_style = GTK_EXPANDER_COLLAPSED;
104         priv->expander_size = 12;
105         priv->activatable = TRUE;
106         priv->animation_node = NULL;
107
108         GTK_CELL_RENDERER (expander)->xpad = 2;
109         GTK_CELL_RENDERER (expander)->ypad = 2;
110         GTK_CELL_RENDERER (expander)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
111 }
112
113 static void
114 empathy_cell_renderer_expander_class_init (EmpathyCellRendererExpanderClass *klass)
115 {
116         GObjectClass         *object_class;
117         GtkCellRendererClass *cell_class;
118
119         object_class  = G_OBJECT_CLASS (klass);
120         cell_class = GTK_CELL_RENDERER_CLASS (klass);
121
122         object_class->finalize = empathy_cell_renderer_expander_finalize;
123
124         object_class->get_property = empathy_cell_renderer_expander_get_property;
125         object_class->set_property = empathy_cell_renderer_expander_set_property;
126
127         cell_class->get_size = empathy_cell_renderer_expander_get_size;
128         cell_class->render = empathy_cell_renderer_expander_render;
129         cell_class->activate = empathy_cell_renderer_expander_activate;
130
131         g_object_class_install_property (object_class,
132                                          PROP_EXPANDER_STYLE,
133                                          g_param_spec_enum ("expander-style",
134                                                             "Expander Style",
135                                                             "Style to use when painting the expander",
136                                                             GTK_TYPE_EXPANDER_STYLE,
137                                                             GTK_EXPANDER_COLLAPSED,
138                                                             G_PARAM_READWRITE));
139
140         g_object_class_install_property (object_class,
141                                          PROP_EXPANDER_SIZE,
142                                          g_param_spec_int ("expander-size",
143                                                            "Expander Size",
144                                                            "The size of the expander",
145                                                            0,
146                                                            G_MAXINT,
147                                                            12,
148                                                            G_PARAM_READWRITE));
149
150         g_object_class_install_property (object_class,
151                                          PROP_ACTIVATABLE,
152                                          g_param_spec_boolean ("activatable",
153                                                                "Activatable",
154                                                                "The expander can be activated",
155                                                                TRUE,
156                                                                G_PARAM_READWRITE));
157
158         g_type_class_add_private (object_class, sizeof (EmpathyCellRendererExpanderPriv));
159 }
160
161 static void
162 empathy_cell_renderer_expander_get_property (GObject    *object,
163                                             guint       param_id,
164                                             GValue     *value,
165                                             GParamSpec *pspec)
166 {
167         EmpathyCellRendererExpander     *expander;
168         EmpathyCellRendererExpanderPriv *priv;
169
170         expander = EMPATHY_CELL_RENDERER_EXPANDER (object);
171         priv = GET_PRIV (expander);
172
173         switch (param_id) {
174         case PROP_EXPANDER_STYLE:
175                 g_value_set_enum (value, priv->expander_style);
176                 break;
177
178         case PROP_EXPANDER_SIZE:
179                 g_value_set_int (value, priv->expander_size);
180                 break;
181
182         case PROP_ACTIVATABLE:
183                 g_value_set_boolean (value, priv->activatable);
184                 break;
185
186         default:
187                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
188                 break;
189         }
190 }
191
192 static void
193 empathy_cell_renderer_expander_set_property (GObject      *object,
194                                             guint         param_id,
195                                             const GValue *value,
196                                             GParamSpec   *pspec)
197 {
198         EmpathyCellRendererExpander     *expander;
199         EmpathyCellRendererExpanderPriv *priv;
200
201         expander = EMPATHY_CELL_RENDERER_EXPANDER (object);
202         priv = GET_PRIV (expander);
203
204         switch (param_id) {
205         case PROP_EXPANDER_STYLE:
206                 priv->expander_style = g_value_get_enum (value);
207                 break;
208
209         case PROP_EXPANDER_SIZE:
210                 priv->expander_size = g_value_get_int (value);
211                 break;
212
213         case PROP_ACTIVATABLE:
214                 priv->activatable = g_value_get_boolean (value);
215                 break;
216
217         default:
218                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
219                 break;
220         }
221 }
222
223 static void
224 empathy_cell_renderer_expander_finalize (GObject *object)
225 {
226         EmpathyCellRendererExpanderPriv *priv;
227
228         priv = GET_PRIV (object);
229
230         if (priv->animation_timeout) {
231                 g_source_remove (priv->animation_timeout);
232                 priv->animation_timeout = 0;
233         }
234
235         if (priv->animation_node) {
236                 gtk_tree_row_reference_free (priv->animation_node);
237         }
238
239         (* G_OBJECT_CLASS (empathy_cell_renderer_expander_parent_class)->finalize) (object);
240 }
241
242 GtkCellRenderer *
243 empathy_cell_renderer_expander_new (void)
244 {
245         return g_object_new (EMPATHY_TYPE_CELL_RENDERER_EXPANDER, NULL);
246 }
247
248 static void
249 empathy_cell_renderer_expander_get_size (GtkCellRenderer *cell,
250                                         GtkWidget       *widget,
251                                         GdkRectangle    *cell_area,
252                                         gint            *x_offset,
253                                         gint            *y_offset,
254                                         gint            *width,
255                                         gint            *height)
256 {
257         EmpathyCellRendererExpander     *expander;
258         EmpathyCellRendererExpanderPriv *priv;
259
260         expander = (EmpathyCellRendererExpander*) cell;
261         priv = GET_PRIV (expander);
262
263         if (cell_area) {
264                 if (x_offset) {
265                         *x_offset = cell->xalign * (cell_area->width - (priv->expander_size + (2 * cell->xpad)));
266                         *x_offset = MAX (*x_offset, 0);
267                 }
268
269                 if (y_offset) {
270                         *y_offset = cell->yalign * (cell_area->height - (priv->expander_size + (2 * cell->ypad)));
271                         *y_offset = MAX (*y_offset, 0);
272                 }
273         } else {
274                 if (x_offset)
275                         *x_offset = 0;
276
277                 if (y_offset)
278                         *y_offset = 0;
279         }
280
281         if (width)
282                 *width = cell->xpad * 2 + priv->expander_size;
283
284         if (height)
285                 *height = cell->ypad * 2 + priv->expander_size;
286 }
287
288 static void
289 empathy_cell_renderer_expander_render (GtkCellRenderer      *cell,
290                                       GdkWindow            *window,
291                                       GtkWidget            *widget,
292                                       GdkRectangle         *background_area,
293                                       GdkRectangle         *cell_area,
294                                       GdkRectangle         *expose_area,
295                                       GtkCellRendererState  flags)
296 {
297         EmpathyCellRendererExpander     *expander;
298         EmpathyCellRendererExpanderPriv *priv;
299         GtkExpanderStyle                expander_style;
300         gint                            x_offset, y_offset;
301
302         expander = (EmpathyCellRendererExpander*) cell;
303         priv = GET_PRIV (expander);
304
305         if (priv->animation_node) {
306                 GtkTreePath *path;
307                 GdkRectangle rect;
308
309                 /* Not sure if I like this ... */
310                 path = gtk_tree_row_reference_get_path (priv->animation_node);
311                 gtk_tree_view_get_background_area (priv->animation_view, path,
312                                                    NULL, &rect);
313                 gtk_tree_path_free (path);
314
315                 if (background_area->y == rect.y)
316                         expander_style = priv->animation_style;
317                 else
318                         expander_style = priv->expander_style;
319         } else
320                 expander_style = priv->expander_style;
321
322         empathy_cell_renderer_expander_get_size (cell, widget, cell_area,
323                                                 &x_offset, &y_offset,
324                                                 NULL, NULL);
325
326         gtk_paint_expander (widget->style,
327                             window,
328                             GTK_STATE_NORMAL,
329                             expose_area,
330                             widget,
331                             "treeview",
332                             cell_area->x + x_offset + cell->xpad + priv->expander_size / 2,
333                             cell_area->y + y_offset + cell->ypad + priv->expander_size / 2,
334                             expander_style);
335 }
336
337 static void
338 invalidate_node (GtkTreeView *tree_view,
339                  GtkTreePath *path)
340 {
341        GdkWindow    *bin_window;
342        GdkRectangle  rect;
343
344        bin_window = gtk_tree_view_get_bin_window (tree_view);
345
346        gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
347
348        rect.x = 0;
349        rect.width = GTK_WIDGET (tree_view)->allocation.width;
350
351        gdk_window_invalidate_rect (bin_window, &rect, TRUE);
352 }
353
354 static gboolean
355 do_animation (EmpathyCellRendererExpander *expander)
356 {
357         EmpathyCellRendererExpanderPriv *priv;
358         GtkTreePath                    *path;
359         gboolean                        done = FALSE;
360
361         priv = GET_PRIV (expander);
362
363         if (priv->animation_expanding) {
364                 if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED)
365                         priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
366                 else if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED) {
367                         priv->animation_style = GTK_EXPANDER_EXPANDED;
368                         done = TRUE;
369                 }
370         } else {
371                 if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED)
372                         priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
373                 else if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED) {
374                         priv->animation_style = GTK_EXPANDER_COLLAPSED;
375                         done = TRUE;
376                 }
377         }
378
379         path = gtk_tree_row_reference_get_path (priv->animation_node);
380         invalidate_node (priv->animation_view, path);
381         gtk_tree_path_free (path);
382
383         if (done) {
384                 gtk_tree_row_reference_free (priv->animation_node);
385                 priv->animation_node = NULL;
386                 priv->animation_timeout = 0;
387         }
388
389         return !done;
390 }
391
392 static gboolean
393 animation_timeout (gpointer data)
394 {
395         gboolean retval;
396
397         GDK_THREADS_ENTER ();
398
399         retval = do_animation (data);
400
401         GDK_THREADS_LEAVE ();
402
403         return retval;
404 }
405
406 static void
407 empathy_cell_renderer_expander_start_animation (EmpathyCellRendererExpander *expander,
408                                                GtkTreeView                *tree_view,
409                                                GtkTreePath                *path,
410                                                gboolean                    expanding,
411                                                GdkRectangle               *background_area)
412 {
413         EmpathyCellRendererExpanderPriv *priv;
414
415         priv = GET_PRIV (expander);
416
417         if (expanding) {
418                 priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
419         } else {
420                 priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
421         }
422
423         invalidate_node (tree_view, path);
424
425         priv->animation_expanding = expanding;
426         priv->animation_view = tree_view;
427         priv->animation_node = gtk_tree_row_reference_new (gtk_tree_view_get_model (tree_view), path);
428         priv->animation_timeout = g_timeout_add (50, animation_timeout, expander);
429 }
430
431 static gboolean
432 empathy_cell_renderer_expander_activate (GtkCellRenderer      *cell,
433                                         GdkEvent             *event,
434                                         GtkWidget            *widget,
435                                         const gchar          *path_string,
436                                         GdkRectangle         *background_area,
437                                         GdkRectangle         *cell_area,
438                                         GtkCellRendererState  flags)
439 {
440         EmpathyCellRendererExpander     *expander;
441         EmpathyCellRendererExpanderPriv *priv;
442         GtkTreePath                    *path;
443         gboolean                        animate;
444         gboolean                        expanding;
445
446         expander = EMPATHY_CELL_RENDERER_EXPANDER (cell);
447         priv = GET_PRIV (cell);
448
449         if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable)
450                 return FALSE;
451
452         path = gtk_tree_path_new_from_string (path_string);
453
454         if (gtk_tree_path_get_depth (path) > 1) {
455                 gtk_tree_path_free (path);
456                 return TRUE;
457         }
458
459         g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
460                       "gtk-enable-animations", &animate,
461                       NULL);
462
463         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
464                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path);
465                 expanding = FALSE;
466         } else {
467                 gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE);
468                 expanding = TRUE;
469         }
470
471         if (animate) {
472                 empathy_cell_renderer_expander_start_animation (expander,
473                                                                GTK_TREE_VIEW (widget),
474                                                                path,
475                                                                expanding,
476                                                                background_area);
477         }
478
479         gtk_tree_path_free (path);
480
481         return TRUE;
482 }