1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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>
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
32 #include <gtk/gtktreeview.h>
34 #include "gossip-cell-renderer-expander.h"
36 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CELL_RENDERER_EXPANDER, GossipCellRendererExpanderPriv))
38 static void gossip_cell_renderer_expander_init (GossipCellRendererExpander *expander);
39 static void gossip_cell_renderer_expander_class_init (GossipCellRendererExpanderClass *klass);
40 static void gossip_cell_renderer_expander_get_property (GObject *object,
44 static void gossip_cell_renderer_expander_set_property (GObject *object,
48 static void gossip_cell_renderer_expander_finalize (GObject *object);
49 static void gossip_cell_renderer_expander_get_size (GtkCellRenderer *cell,
51 GdkRectangle *cell_area,
56 static void gossip_cell_renderer_expander_render (GtkCellRenderer *cell,
59 GdkRectangle *background_area,
60 GdkRectangle *cell_area,
61 GdkRectangle *expose_area,
62 GtkCellRendererState flags);
63 static gboolean gossip_cell_renderer_expander_activate (GtkCellRenderer *cell,
67 GdkRectangle *background_area,
68 GdkRectangle *cell_area,
69 GtkCellRendererState flags);
78 typedef struct _GossipCellRendererExpanderPriv GossipCellRendererExpanderPriv;
80 struct _GossipCellRendererExpanderPriv {
81 GtkExpanderStyle expander_style;
84 GtkTreeView *animation_view;
85 GtkTreeRowReference *animation_node;
86 GtkExpanderStyle animation_style;
87 guint animation_timeout;
88 GdkRectangle animation_area;
90 guint activatable : 1;
91 guint animation_expanding : 1;
94 G_DEFINE_TYPE (GossipCellRendererExpander, gossip_cell_renderer_expander, GTK_TYPE_CELL_RENDERER)
97 gossip_cell_renderer_expander_init (GossipCellRendererExpander *expander)
99 GossipCellRendererExpanderPriv *priv;
101 priv = GET_PRIV (expander);
103 priv->expander_style = GTK_EXPANDER_COLLAPSED;
104 priv->expander_size = 12;
105 priv->activatable = TRUE;
106 priv->animation_node = NULL;
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;
114 gossip_cell_renderer_expander_class_init (GossipCellRendererExpanderClass *klass)
116 GObjectClass *object_class;
117 GtkCellRendererClass *cell_class;
119 object_class = G_OBJECT_CLASS (klass);
120 cell_class = GTK_CELL_RENDERER_CLASS (klass);
122 object_class->finalize = gossip_cell_renderer_expander_finalize;
124 object_class->get_property = gossip_cell_renderer_expander_get_property;
125 object_class->set_property = gossip_cell_renderer_expander_set_property;
127 cell_class->get_size = gossip_cell_renderer_expander_get_size;
128 cell_class->render = gossip_cell_renderer_expander_render;
129 cell_class->activate = gossip_cell_renderer_expander_activate;
131 g_object_class_install_property (object_class,
133 g_param_spec_enum ("expander-style",
135 "Style to use when painting the expander",
136 GTK_TYPE_EXPANDER_STYLE,
137 GTK_EXPANDER_COLLAPSED,
140 g_object_class_install_property (object_class,
142 g_param_spec_int ("expander-size",
144 "The size of the expander",
150 g_object_class_install_property (object_class,
152 g_param_spec_boolean ("activatable",
154 "The expander can be activated",
158 g_type_class_add_private (object_class, sizeof (GossipCellRendererExpanderPriv));
162 gossip_cell_renderer_expander_get_property (GObject *object,
167 GossipCellRendererExpander *expander;
168 GossipCellRendererExpanderPriv *priv;
170 expander = GOSSIP_CELL_RENDERER_EXPANDER (object);
171 priv = GET_PRIV (expander);
174 case PROP_EXPANDER_STYLE:
175 g_value_set_enum (value, priv->expander_style);
178 case PROP_EXPANDER_SIZE:
179 g_value_set_int (value, priv->expander_size);
182 case PROP_ACTIVATABLE:
183 g_value_set_boolean (value, priv->activatable);
187 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
193 gossip_cell_renderer_expander_set_property (GObject *object,
198 GossipCellRendererExpander *expander;
199 GossipCellRendererExpanderPriv *priv;
201 expander = GOSSIP_CELL_RENDERER_EXPANDER (object);
202 priv = GET_PRIV (expander);
205 case PROP_EXPANDER_STYLE:
206 priv->expander_style = g_value_get_enum (value);
209 case PROP_EXPANDER_SIZE:
210 priv->expander_size = g_value_get_int (value);
213 case PROP_ACTIVATABLE:
214 priv->activatable = g_value_get_boolean (value);
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
224 gossip_cell_renderer_expander_finalize (GObject *object)
226 GossipCellRendererExpanderPriv *priv;
228 priv = GET_PRIV (object);
230 if (priv->animation_timeout) {
231 g_source_remove (priv->animation_timeout);
232 priv->animation_timeout = 0;
235 if (priv->animation_node) {
236 gtk_tree_row_reference_free (priv->animation_node);
239 (* G_OBJECT_CLASS (gossip_cell_renderer_expander_parent_class)->finalize) (object);
243 gossip_cell_renderer_expander_new (void)
245 return g_object_new (GOSSIP_TYPE_CELL_RENDERER_EXPANDER, NULL);
249 gossip_cell_renderer_expander_get_size (GtkCellRenderer *cell,
251 GdkRectangle *cell_area,
257 GossipCellRendererExpander *expander;
258 GossipCellRendererExpanderPriv *priv;
260 expander = (GossipCellRendererExpander*) cell;
261 priv = GET_PRIV (expander);
265 *x_offset = cell->xalign * (cell_area->width - (priv->expander_size + (2 * cell->xpad)));
266 *x_offset = MAX (*x_offset, 0);
270 *y_offset = cell->yalign * (cell_area->height - (priv->expander_size + (2 * cell->ypad)));
271 *y_offset = MAX (*y_offset, 0);
282 *width = cell->xpad * 2 + priv->expander_size;
285 *height = cell->ypad * 2 + priv->expander_size;
289 gossip_cell_renderer_expander_render (GtkCellRenderer *cell,
292 GdkRectangle *background_area,
293 GdkRectangle *cell_area,
294 GdkRectangle *expose_area,
295 GtkCellRendererState flags)
297 GossipCellRendererExpander *expander;
298 GossipCellRendererExpanderPriv *priv;
299 GtkExpanderStyle expander_style;
300 gint x_offset, y_offset;
302 expander = (GossipCellRendererExpander*) cell;
303 priv = GET_PRIV (expander);
305 if (priv->animation_node) {
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,
313 gtk_tree_path_free (path);
315 if (background_area->y == rect.y)
316 expander_style = priv->animation_style;
318 expander_style = priv->expander_style;
320 expander_style = priv->expander_style;
322 gossip_cell_renderer_expander_get_size (cell, widget, cell_area,
323 &x_offset, &y_offset,
326 gtk_paint_expander (widget->style,
332 cell_area->x + x_offset + cell->xpad + priv->expander_size / 2,
333 cell_area->y + y_offset + cell->ypad + priv->expander_size / 2,
338 invalidate_node (GtkTreeView *tree_view,
341 GdkWindow *bin_window;
344 bin_window = gtk_tree_view_get_bin_window (tree_view);
346 gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
349 rect.width = GTK_WIDGET (tree_view)->allocation.width;
351 gdk_window_invalidate_rect (bin_window, &rect, TRUE);
355 do_animation (GossipCellRendererExpander *expander)
357 GossipCellRendererExpanderPriv *priv;
359 gboolean done = FALSE;
361 priv = GET_PRIV (expander);
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;
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;
379 path = gtk_tree_row_reference_get_path (priv->animation_node);
380 invalidate_node (priv->animation_view, path);
381 gtk_tree_path_free (path);
384 gtk_tree_row_reference_free (priv->animation_node);
385 priv->animation_node = NULL;
386 priv->animation_timeout = 0;
393 animation_timeout (gpointer data)
397 GDK_THREADS_ENTER ();
399 retval = do_animation (data);
401 GDK_THREADS_LEAVE ();
407 gossip_cell_renderer_expander_start_animation (GossipCellRendererExpander *expander,
408 GtkTreeView *tree_view,
411 GdkRectangle *background_area)
413 GossipCellRendererExpanderPriv *priv;
415 priv = GET_PRIV (expander);
418 priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
420 priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
423 invalidate_node (tree_view, path);
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);
432 gossip_cell_renderer_expander_activate (GtkCellRenderer *cell,
435 const gchar *path_string,
436 GdkRectangle *background_area,
437 GdkRectangle *cell_area,
438 GtkCellRendererState flags)
440 GossipCellRendererExpander *expander;
441 GossipCellRendererExpanderPriv *priv;
446 expander = GOSSIP_CELL_RENDERER_EXPANDER (cell);
447 priv = GET_PRIV (cell);
449 if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable)
452 path = gtk_tree_path_new_from_string (path_string);
454 if (gtk_tree_path_get_depth (path) > 1) {
455 gtk_tree_path_free (path);
459 g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
460 "gtk-enable-animations", &animate,
463 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
464 gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path);
467 gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE);
472 gossip_cell_renderer_expander_start_animation (expander,
473 GTK_TREE_VIEW (widget),
479 gtk_tree_path_free (path);