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);