Tagged for release 2.26.0.
[empathy-mirror.git] / libempathy-gtk / empathy-cell-renderer-expander.c
blobfc9189d2daec0c7b4b1d7f0e1320dfb11d898560
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2006-2007 Imendio AB
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.
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.
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.
20 * Authors: Kristian Rietveld <kris@imendio.com>
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).
32 #include <gtk/gtktreeview.h>
34 #include <libempathy/empathy-utils.h>
35 #include "empathy-cell-renderer-expander.h"
37 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCellRendererExpander)
38 typedef struct {
39 GtkExpanderStyle expander_style;
40 gint expander_size;
42 GtkTreeView *animation_view;
43 GtkTreeRowReference *animation_node;
44 GtkExpanderStyle animation_style;
45 guint animation_timeout;
46 GdkRectangle animation_area;
48 guint activatable : 1;
49 guint animation_expanding : 1;
50 } EmpathyCellRendererExpanderPriv;
52 enum {
53 PROP_0,
54 PROP_EXPANDER_STYLE,
55 PROP_EXPANDER_SIZE,
56 PROP_ACTIVATABLE
59 static void empathy_cell_renderer_expander_get_property (GObject *object,
60 guint param_id,
61 GValue *value,
62 GParamSpec *pspec);
63 static void empathy_cell_renderer_expander_set_property (GObject *object,
64 guint param_id,
65 const GValue *value,
66 GParamSpec *pspec);
67 static void empathy_cell_renderer_expander_finalize (GObject *object);
68 static void empathy_cell_renderer_expander_get_size (GtkCellRenderer *cell,
69 GtkWidget *widget,
70 GdkRectangle *cell_area,
71 gint *x_offset,
72 gint *y_offset,
73 gint *width,
74 gint *height);
75 static void empathy_cell_renderer_expander_render (GtkCellRenderer *cell,
76 GdkWindow *window,
77 GtkWidget *widget,
78 GdkRectangle *background_area,
79 GdkRectangle *cell_area,
80 GdkRectangle *expose_area,
81 GtkCellRendererState flags);
82 static gboolean empathy_cell_renderer_expander_activate (GtkCellRenderer *cell,
83 GdkEvent *event,
84 GtkWidget *widget,
85 const gchar *path,
86 GdkRectangle *background_area,
87 GdkRectangle *cell_area,
88 GtkCellRendererState flags);
90 G_DEFINE_TYPE (EmpathyCellRendererExpander, empathy_cell_renderer_expander, GTK_TYPE_CELL_RENDERER)
92 static void
93 empathy_cell_renderer_expander_init (EmpathyCellRendererExpander *expander)
95 EmpathyCellRendererExpanderPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (expander,
96 EMPATHY_TYPE_CELL_RENDERER_EXPANDER, EmpathyCellRendererExpanderPriv);
98 expander->priv = priv;
99 priv->expander_style = GTK_EXPANDER_COLLAPSED;
100 priv->expander_size = 12;
101 priv->activatable = TRUE;
102 priv->animation_node = NULL;
104 GTK_CELL_RENDERER (expander)->xpad = 2;
105 GTK_CELL_RENDERER (expander)->ypad = 2;
106 GTK_CELL_RENDERER (expander)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
109 static void
110 empathy_cell_renderer_expander_class_init (EmpathyCellRendererExpanderClass *klass)
112 GObjectClass *object_class;
113 GtkCellRendererClass *cell_class;
115 object_class = G_OBJECT_CLASS (klass);
116 cell_class = GTK_CELL_RENDERER_CLASS (klass);
118 object_class->finalize = empathy_cell_renderer_expander_finalize;
120 object_class->get_property = empathy_cell_renderer_expander_get_property;
121 object_class->set_property = empathy_cell_renderer_expander_set_property;
123 cell_class->get_size = empathy_cell_renderer_expander_get_size;
124 cell_class->render = empathy_cell_renderer_expander_render;
125 cell_class->activate = empathy_cell_renderer_expander_activate;
127 g_object_class_install_property (object_class,
128 PROP_EXPANDER_STYLE,
129 g_param_spec_enum ("expander-style",
130 "Expander Style",
131 "Style to use when painting the expander",
132 GTK_TYPE_EXPANDER_STYLE,
133 GTK_EXPANDER_COLLAPSED,
134 G_PARAM_READWRITE));
136 g_object_class_install_property (object_class,
137 PROP_EXPANDER_SIZE,
138 g_param_spec_int ("expander-size",
139 "Expander Size",
140 "The size of the expander",
142 G_MAXINT,
144 G_PARAM_READWRITE));
146 g_object_class_install_property (object_class,
147 PROP_ACTIVATABLE,
148 g_param_spec_boolean ("activatable",
149 "Activatable",
150 "The expander can be activated",
151 TRUE,
152 G_PARAM_READWRITE));
154 g_type_class_add_private (object_class, sizeof (EmpathyCellRendererExpanderPriv));
157 static void
158 empathy_cell_renderer_expander_get_property (GObject *object,
159 guint param_id,
160 GValue *value,
161 GParamSpec *pspec)
163 EmpathyCellRendererExpander *expander;
164 EmpathyCellRendererExpanderPriv *priv;
166 expander = EMPATHY_CELL_RENDERER_EXPANDER (object);
167 priv = GET_PRIV (expander);
169 switch (param_id) {
170 case PROP_EXPANDER_STYLE:
171 g_value_set_enum (value, priv->expander_style);
172 break;
174 case PROP_EXPANDER_SIZE:
175 g_value_set_int (value, priv->expander_size);
176 break;
178 case PROP_ACTIVATABLE:
179 g_value_set_boolean (value, priv->activatable);
180 break;
182 default:
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
184 break;
188 static void
189 empathy_cell_renderer_expander_set_property (GObject *object,
190 guint param_id,
191 const GValue *value,
192 GParamSpec *pspec)
194 EmpathyCellRendererExpander *expander;
195 EmpathyCellRendererExpanderPriv *priv;
197 expander = EMPATHY_CELL_RENDERER_EXPANDER (object);
198 priv = GET_PRIV (expander);
200 switch (param_id) {
201 case PROP_EXPANDER_STYLE:
202 priv->expander_style = g_value_get_enum (value);
203 break;
205 case PROP_EXPANDER_SIZE:
206 priv->expander_size = g_value_get_int (value);
207 break;
209 case PROP_ACTIVATABLE:
210 priv->activatable = g_value_get_boolean (value);
211 break;
213 default:
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
215 break;
219 static void
220 empathy_cell_renderer_expander_finalize (GObject *object)
222 EmpathyCellRendererExpanderPriv *priv;
224 priv = GET_PRIV (object);
226 if (priv->animation_timeout) {
227 g_source_remove (priv->animation_timeout);
228 priv->animation_timeout = 0;
231 if (priv->animation_node) {
232 gtk_tree_row_reference_free (priv->animation_node);
235 (* G_OBJECT_CLASS (empathy_cell_renderer_expander_parent_class)->finalize) (object);
238 GtkCellRenderer *
239 empathy_cell_renderer_expander_new (void)
241 return g_object_new (EMPATHY_TYPE_CELL_RENDERER_EXPANDER, NULL);
244 static void
245 empathy_cell_renderer_expander_get_size (GtkCellRenderer *cell,
246 GtkWidget *widget,
247 GdkRectangle *cell_area,
248 gint *x_offset,
249 gint *y_offset,
250 gint *width,
251 gint *height)
253 EmpathyCellRendererExpander *expander;
254 EmpathyCellRendererExpanderPriv *priv;
256 expander = (EmpathyCellRendererExpander*) cell;
257 priv = GET_PRIV (expander);
259 if (cell_area) {
260 if (x_offset) {
261 *x_offset = cell->xalign * (cell_area->width - (priv->expander_size + (2 * cell->xpad)));
262 *x_offset = MAX (*x_offset, 0);
265 if (y_offset) {
266 *y_offset = cell->yalign * (cell_area->height - (priv->expander_size + (2 * cell->ypad)));
267 *y_offset = MAX (*y_offset, 0);
269 } else {
270 if (x_offset)
271 *x_offset = 0;
273 if (y_offset)
274 *y_offset = 0;
277 if (width)
278 *width = cell->xpad * 2 + priv->expander_size;
280 if (height)
281 *height = cell->ypad * 2 + priv->expander_size;
284 static void
285 empathy_cell_renderer_expander_render (GtkCellRenderer *cell,
286 GdkWindow *window,
287 GtkWidget *widget,
288 GdkRectangle *background_area,
289 GdkRectangle *cell_area,
290 GdkRectangle *expose_area,
291 GtkCellRendererState flags)
293 EmpathyCellRendererExpander *expander;
294 EmpathyCellRendererExpanderPriv *priv;
295 GtkExpanderStyle expander_style;
296 gint x_offset, y_offset;
298 expander = (EmpathyCellRendererExpander*) cell;
299 priv = GET_PRIV (expander);
301 if (priv->animation_node) {
302 GtkTreePath *path;
303 GdkRectangle rect;
305 /* Not sure if I like this ... */
306 path = gtk_tree_row_reference_get_path (priv->animation_node);
307 gtk_tree_view_get_background_area (priv->animation_view, path,
308 NULL, &rect);
309 gtk_tree_path_free (path);
311 if (background_area->y == rect.y)
312 expander_style = priv->animation_style;
313 else
314 expander_style = priv->expander_style;
315 } else
316 expander_style = priv->expander_style;
318 empathy_cell_renderer_expander_get_size (cell, widget, cell_area,
319 &x_offset, &y_offset,
320 NULL, NULL);
322 gtk_paint_expander (widget->style,
323 window,
324 GTK_STATE_NORMAL,
325 expose_area,
326 widget,
327 "treeview",
328 cell_area->x + x_offset + cell->xpad + priv->expander_size / 2,
329 cell_area->y + y_offset + cell->ypad + priv->expander_size / 2,
330 expander_style);
333 static void
334 invalidate_node (GtkTreeView *tree_view,
335 GtkTreePath *path)
337 GdkWindow *bin_window;
338 GdkRectangle rect;
340 bin_window = gtk_tree_view_get_bin_window (tree_view);
342 gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
344 rect.x = 0;
345 rect.width = GTK_WIDGET (tree_view)->allocation.width;
347 gdk_window_invalidate_rect (bin_window, &rect, TRUE);
350 static gboolean
351 do_animation (EmpathyCellRendererExpander *expander)
353 EmpathyCellRendererExpanderPriv *priv;
354 GtkTreePath *path;
355 gboolean done = FALSE;
357 priv = GET_PRIV (expander);
359 if (priv->animation_expanding) {
360 if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED)
361 priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
362 else if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED) {
363 priv->animation_style = GTK_EXPANDER_EXPANDED;
364 done = TRUE;
366 } else {
367 if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED)
368 priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
369 else if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED) {
370 priv->animation_style = GTK_EXPANDER_COLLAPSED;
371 done = TRUE;
375 path = gtk_tree_row_reference_get_path (priv->animation_node);
376 invalidate_node (priv->animation_view, path);
377 gtk_tree_path_free (path);
379 if (done) {
380 gtk_tree_row_reference_free (priv->animation_node);
381 priv->animation_node = NULL;
382 priv->animation_timeout = 0;
385 return !done;
388 static gboolean
389 animation_timeout (gpointer data)
391 gboolean retval;
393 GDK_THREADS_ENTER ();
395 retval = do_animation (data);
397 GDK_THREADS_LEAVE ();
399 return retval;
402 static void
403 empathy_cell_renderer_expander_start_animation (EmpathyCellRendererExpander *expander,
404 GtkTreeView *tree_view,
405 GtkTreePath *path,
406 gboolean expanding,
407 GdkRectangle *background_area)
409 EmpathyCellRendererExpanderPriv *priv;
411 priv = GET_PRIV (expander);
413 if (expanding) {
414 priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
415 } else {
416 priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
419 invalidate_node (tree_view, path);
421 priv->animation_expanding = expanding;
422 priv->animation_view = tree_view;
423 priv->animation_node = gtk_tree_row_reference_new (gtk_tree_view_get_model (tree_view), path);
424 priv->animation_timeout = g_timeout_add (50, animation_timeout, expander);
427 static gboolean
428 empathy_cell_renderer_expander_activate (GtkCellRenderer *cell,
429 GdkEvent *event,
430 GtkWidget *widget,
431 const gchar *path_string,
432 GdkRectangle *background_area,
433 GdkRectangle *cell_area,
434 GtkCellRendererState flags)
436 EmpathyCellRendererExpander *expander;
437 EmpathyCellRendererExpanderPriv *priv;
438 GtkTreePath *path;
439 gboolean animate;
440 gboolean expanding;
442 expander = EMPATHY_CELL_RENDERER_EXPANDER (cell);
443 priv = GET_PRIV (cell);
445 if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable)
446 return FALSE;
448 path = gtk_tree_path_new_from_string (path_string);
450 if (gtk_tree_path_get_depth (path) > 1) {
451 gtk_tree_path_free (path);
452 return TRUE;
455 g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
456 "gtk-enable-animations", &animate,
457 NULL);
459 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
460 gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path);
461 expanding = FALSE;
462 } else {
463 gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE);
464 expanding = TRUE;
467 if (animate) {
468 empathy_cell_renderer_expander_start_animation (expander,
469 GTK_TREE_VIEW (widget),
470 path,
471 expanding,
472 background_area);
475 gtk_tree_path_free (path);
477 return TRUE;