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 <libempathy/empathy-utils.h>
35 #include "empathy-cell-renderer-expander.h"
37 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCellRendererExpander)
39 GtkExpanderStyle expander_style;
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;
59 static void empathy_cell_renderer_expander_get_property (GObject *object,
63 static void empathy_cell_renderer_expander_set_property (GObject *object,
67 static void empathy_cell_renderer_expander_finalize (GObject *object);
68 static void empathy_cell_renderer_expander_get_size (GtkCellRenderer *cell,
70 GdkRectangle *cell_area,
75 static void empathy_cell_renderer_expander_render (GtkCellRenderer *cell,
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,
86 GdkRectangle *background_area,
87 GdkRectangle *cell_area,
88 GtkCellRendererState flags);
90 G_DEFINE_TYPE (EmpathyCellRendererExpander, empathy_cell_renderer_expander, GTK_TYPE_CELL_RENDERER)
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;
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,
129 g_param_spec_enum ("expander-style",
131 "Style to use when painting the expander",
132 GTK_TYPE_EXPANDER_STYLE,
133 GTK_EXPANDER_COLLAPSED,
136 g_object_class_install_property (object_class,
138 g_param_spec_int ("expander-size",
140 "The size of the expander",
146 g_object_class_install_property (object_class,
148 g_param_spec_boolean ("activatable",
150 "The expander can be activated",
154 g_type_class_add_private (object_class, sizeof (EmpathyCellRendererExpanderPriv));
158 empathy_cell_renderer_expander_get_property (GObject *object,
163 EmpathyCellRendererExpander *expander;
164 EmpathyCellRendererExpanderPriv *priv;
166 expander = EMPATHY_CELL_RENDERER_EXPANDER (object);
167 priv = GET_PRIV (expander);
170 case PROP_EXPANDER_STYLE:
171 g_value_set_enum (value, priv->expander_style);
174 case PROP_EXPANDER_SIZE:
175 g_value_set_int (value, priv->expander_size);
178 case PROP_ACTIVATABLE:
179 g_value_set_boolean (value, priv->activatable);
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
189 empathy_cell_renderer_expander_set_property (GObject *object,
194 EmpathyCellRendererExpander *expander;
195 EmpathyCellRendererExpanderPriv *priv;
197 expander = EMPATHY_CELL_RENDERER_EXPANDER (object);
198 priv = GET_PRIV (expander);
201 case PROP_EXPANDER_STYLE:
202 priv->expander_style = g_value_get_enum (value);
205 case PROP_EXPANDER_SIZE:
206 priv->expander_size = g_value_get_int (value);
209 case PROP_ACTIVATABLE:
210 priv->activatable = g_value_get_boolean (value);
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
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);
239 empathy_cell_renderer_expander_new (void)
241 return g_object_new (EMPATHY_TYPE_CELL_RENDERER_EXPANDER, NULL);
245 empathy_cell_renderer_expander_get_size (GtkCellRenderer *cell,
247 GdkRectangle *cell_area,
253 EmpathyCellRendererExpander *expander;
254 EmpathyCellRendererExpanderPriv *priv;
256 expander = (EmpathyCellRendererExpander*) cell;
257 priv = GET_PRIV (expander);
261 *x_offset = cell->xalign * (cell_area->width - (priv->expander_size + (2 * cell->xpad)));
262 *x_offset = MAX (*x_offset, 0);
266 *y_offset = cell->yalign * (cell_area->height - (priv->expander_size + (2 * cell->ypad)));
267 *y_offset = MAX (*y_offset, 0);
278 *width = cell->xpad * 2 + priv->expander_size;
281 *height = cell->ypad * 2 + priv->expander_size;
285 empathy_cell_renderer_expander_render (GtkCellRenderer *cell,
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) {
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,
309 gtk_tree_path_free (path);
311 if (background_area->y == rect.y)
312 expander_style = priv->animation_style;
314 expander_style = priv->expander_style;
316 expander_style = priv->expander_style;
318 empathy_cell_renderer_expander_get_size (cell, widget, cell_area,
319 &x_offset, &y_offset,
322 gtk_paint_expander (widget->style,
328 cell_area->x + x_offset + cell->xpad + priv->expander_size / 2,
329 cell_area->y + y_offset + cell->ypad + priv->expander_size / 2,
334 invalidate_node (GtkTreeView *tree_view,
337 GdkWindow *bin_window;
340 bin_window = gtk_tree_view_get_bin_window (tree_view);
342 gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
345 rect.width = GTK_WIDGET (tree_view)->allocation.width;
347 gdk_window_invalidate_rect (bin_window, &rect, TRUE);
351 do_animation (EmpathyCellRendererExpander *expander)
353 EmpathyCellRendererExpanderPriv *priv;
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;
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;
375 path = gtk_tree_row_reference_get_path (priv->animation_node);
376 invalidate_node (priv->animation_view, path);
377 gtk_tree_path_free (path);
380 gtk_tree_row_reference_free (priv->animation_node);
381 priv->animation_node = NULL;
382 priv->animation_timeout = 0;
389 animation_timeout (gpointer data)
393 GDK_THREADS_ENTER ();
395 retval = do_animation (data);
397 GDK_THREADS_LEAVE ();
403 empathy_cell_renderer_expander_start_animation (EmpathyCellRendererExpander *expander,
404 GtkTreeView *tree_view,
407 GdkRectangle *background_area)
409 EmpathyCellRendererExpanderPriv *priv;
411 priv = GET_PRIV (expander);
414 priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
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);
428 empathy_cell_renderer_expander_activate (GtkCellRenderer *cell,
431 const gchar *path_string,
432 GdkRectangle *background_area,
433 GdkRectangle *cell_area,
434 GtkCellRendererState flags)
436 EmpathyCellRendererExpander *expander;
437 EmpathyCellRendererExpanderPriv *priv;
442 expander = EMPATHY_CELL_RENDERER_EXPANDER (cell);
443 priv = GET_PRIV (cell);
445 if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable)
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);
455 g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
456 "gtk-enable-animations", &animate,
459 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
460 gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path);
463 gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE);
468 empathy_cell_renderer_expander_start_animation (expander,
469 GTK_TREE_VIEW (widget),
475 gtk_tree_path_free (path);