2
* Copyright (C) 2006-2007 Imendio AB
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License as
6
* published by the Free Software Foundation; either version 2 of the
7
* License, or (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* General Public License for more details.
14
* You should have received a copy of the GNU General Public
15
* License along with this program; if not, write to the
16
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
19
* Authors: Kristian Rietveld <kris@imendio.com>
23
* - should probably cancel animation if model changes
24
* - need to handle case where node-in-animation is removed
25
* - it only handles a single animation at a time; but I guess users
26
* aren't fast enough to trigger two or more animations at once anyway :P
27
* (could guard for this by just cancelling the "old" animation, and
31
#include <gtk/gtktreeview.h>
33
#include "gmcellrendererexpander.h"
35
#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GM_TYPE_CELL_RENDERER_EXPANDER, GmCellRendererExpanderPriv))
37
static void gm_cell_renderer_expander_init (GmCellRendererExpander *expander);
38
static void gm_cell_renderer_expander_class_init (GmCellRendererExpanderClass *klass);
39
static void gm_cell_renderer_expander_get_property (GObject *object,
43
static void gm_cell_renderer_expander_set_property (GObject *object,
47
static void gm_cell_renderer_expander_finalize (GObject *object);
48
static void gm_cell_renderer_expander_get_size (GtkCellRenderer *cell,
50
GdkRectangle *cell_area,
55
static void gm_cell_renderer_expander_render (GtkCellRenderer *cell,
58
GdkRectangle *background_area,
59
GdkRectangle *cell_area,
60
GdkRectangle *expose_area,
61
GtkCellRendererState flags);
62
static gboolean gm_cell_renderer_expander_activate (GtkCellRenderer *cell,
66
GdkRectangle *background_area,
67
GdkRectangle *cell_area,
68
GtkCellRendererState flags);
77
typedef struct _GmCellRendererExpanderPriv GmCellRendererExpanderPriv;
79
struct _GmCellRendererExpanderPriv {
80
GtkExpanderStyle expander_style;
83
GtkTreeView *animation_view;
84
GtkTreeRowReference *animation_node;
85
GtkExpanderStyle animation_style;
86
guint animation_timeout;
87
GdkRectangle animation_area;
89
guint activatable : 1;
90
guint animation_expanding : 1;
93
G_DEFINE_TYPE (GmCellRendererExpander, gm_cell_renderer_expander, GTK_TYPE_CELL_RENDERER);
96
gm_cell_renderer_expander_init (GmCellRendererExpander *expander)
98
GmCellRendererExpanderPriv *priv;
100
priv = GET_PRIV (expander);
102
priv->expander_style = GTK_EXPANDER_COLLAPSED;
103
priv->expander_size = 12;
104
priv->activatable = TRUE;
105
priv->animation_node = NULL;
107
GTK_CELL_RENDERER (expander)->xpad = 2;
108
GTK_CELL_RENDERER (expander)->ypad = 2;
109
GTK_CELL_RENDERER (expander)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
113
gm_cell_renderer_expander_class_init (GmCellRendererExpanderClass *klass)
115
GObjectClass *object_class;
116
GtkCellRendererClass *cell_class;
118
object_class = G_OBJECT_CLASS (klass);
119
cell_class = GTK_CELL_RENDERER_CLASS (klass);
121
object_class->finalize = gm_cell_renderer_expander_finalize;
123
object_class->get_property = gm_cell_renderer_expander_get_property;
124
object_class->set_property = gm_cell_renderer_expander_set_property;
126
cell_class->get_size = gm_cell_renderer_expander_get_size;
127
cell_class->render = gm_cell_renderer_expander_render;
128
cell_class->activate = gm_cell_renderer_expander_activate;
130
g_object_class_install_property (object_class,
132
g_param_spec_enum ("expander-style",
134
"Style to use when painting the expander",
135
GTK_TYPE_EXPANDER_STYLE,
136
GTK_EXPANDER_COLLAPSED,
139
g_object_class_install_property (object_class,
141
g_param_spec_int ("expander-size",
143
"The size of the expander",
149
g_object_class_install_property (object_class,
151
g_param_spec_boolean ("activatable",
153
"The expander can be activated",
157
g_type_class_add_private (object_class, sizeof (GmCellRendererExpanderPriv));
161
gm_cell_renderer_expander_get_property (GObject *object,
166
GmCellRendererExpander *expander;
167
GmCellRendererExpanderPriv *priv;
169
expander = GM_CELL_RENDERER_EXPANDER (object);
170
priv = GET_PRIV (expander);
173
case PROP_EXPANDER_STYLE:
174
g_value_set_enum (value, priv->expander_style);
177
case PROP_EXPANDER_SIZE:
178
g_value_set_int (value, priv->expander_size);
181
case PROP_ACTIVATABLE:
182
g_value_set_boolean (value, priv->activatable);
186
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
192
gm_cell_renderer_expander_set_property (GObject *object,
197
GmCellRendererExpander *expander;
198
GmCellRendererExpanderPriv *priv;
200
expander = GM_CELL_RENDERER_EXPANDER (object);
201
priv = GET_PRIV (expander);
204
case PROP_EXPANDER_STYLE:
205
priv->expander_style = g_value_get_enum (value);
208
case PROP_EXPANDER_SIZE:
209
priv->expander_size = g_value_get_int (value);
212
case PROP_ACTIVATABLE:
213
priv->activatable = g_value_get_boolean (value);
217
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
223
gm_cell_renderer_expander_finalize (GObject *object)
225
GmCellRendererExpanderPriv *priv;
227
priv = GET_PRIV (object);
229
if (priv->animation_timeout) {
230
g_source_remove (priv->animation_timeout);
231
priv->animation_timeout = 0;
234
if (priv->animation_node) {
235
gtk_tree_row_reference_free (priv->animation_node);
238
(* G_OBJECT_CLASS (gm_cell_renderer_expander_parent_class)->finalize) (object);
242
gm_cell_renderer_expander_new (void)
244
return g_object_new (GM_TYPE_CELL_RENDERER_EXPANDER, NULL);
248
gm_cell_renderer_expander_get_size (GtkCellRenderer *cell,
249
G_GNUC_UNUSED GtkWidget *widget,
250
GdkRectangle *cell_area,
256
GmCellRendererExpander *expander;
257
GmCellRendererExpanderPriv *priv;
259
expander = (GmCellRendererExpander*) cell;
260
priv = GET_PRIV (expander);
264
*x_offset = cell->xalign * (cell_area->width - (priv->expander_size + (2 * cell->xpad)));
265
*x_offset = MAX (*x_offset, 0);
269
*y_offset = cell->yalign * (cell_area->height - (priv->expander_size + (2 * cell->ypad)));
270
*y_offset = MAX (*y_offset, 0);
281
*width = cell->xpad * 2 + priv->expander_size;
284
*height = cell->ypad * 2 + priv->expander_size;
288
gm_cell_renderer_expander_render (GtkCellRenderer *cell,
291
GdkRectangle *background_area,
292
GdkRectangle *cell_area,
293
GdkRectangle *expose_area,
294
G_GNUC_UNUSED GtkCellRendererState flags)
296
GmCellRendererExpander *expander;
297
GmCellRendererExpanderPriv *priv;
298
GtkExpanderStyle expander_style;
299
gint x_offset, y_offset;
301
expander = (GmCellRendererExpander*) cell;
302
priv = GET_PRIV (expander);
304
if (priv->animation_node) {
308
/* Not sure if I like this ... */
309
path = gtk_tree_row_reference_get_path (priv->animation_node);
310
gtk_tree_view_get_background_area (priv->animation_view, path,
312
gtk_tree_path_free (path);
314
if (background_area->y == rect.y)
315
expander_style = priv->animation_style;
317
expander_style = priv->expander_style;
319
expander_style = priv->expander_style;
321
gm_cell_renderer_expander_get_size (cell, widget, cell_area,
322
&x_offset, &y_offset,
325
gtk_paint_expander (widget->style,
331
cell_area->x + x_offset + cell->xpad + priv->expander_size / 2,
332
cell_area->y + y_offset + cell->ypad + priv->expander_size / 2,
334
gtk_paint_hline (widget->style, window, GTK_STATE_NORMAL, NULL, widget, NULL, 0,
335
widget->allocation.width, cell_area->y + cell_area->height);
339
invalidate_node (GtkTreeView *tree_view,
342
GdkWindow *bin_window;
345
bin_window = gtk_tree_view_get_bin_window (tree_view);
347
gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
350
rect.width = GTK_WIDGET (tree_view)->allocation.width;
352
gdk_window_invalidate_rect (bin_window, &rect, TRUE);
356
do_animation (GmCellRendererExpander *expander)
358
GmCellRendererExpanderPriv *priv;
360
gboolean done = FALSE;
362
priv = GET_PRIV (expander);
364
if (priv->animation_expanding) {
365
if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED)
366
priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
367
else if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED) {
368
priv->animation_style = GTK_EXPANDER_EXPANDED;
372
if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED)
373
priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
374
else if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED) {
375
priv->animation_style = GTK_EXPANDER_COLLAPSED;
380
path = gtk_tree_row_reference_get_path (priv->animation_node);
381
invalidate_node (priv->animation_view, path);
382
gtk_tree_path_free (path);
385
gtk_tree_row_reference_free (priv->animation_node);
386
priv->animation_node = NULL;
387
priv->animation_timeout = 0;
394
animation_timeout (gpointer data)
398
GDK_THREADS_ENTER ();
400
retval = do_animation (data);
402
GDK_THREADS_LEAVE ();
408
gm_cell_renderer_expander_start_animation (GmCellRendererExpander *expander,
409
GtkTreeView *tree_view,
412
G_GNUC_UNUSED GdkRectangle *background_area)
414
GmCellRendererExpanderPriv *priv;
416
priv = GET_PRIV (expander);
419
priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
421
priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
424
invalidate_node (tree_view, path);
426
priv->animation_expanding = expanding;
427
priv->animation_view = tree_view;
428
priv->animation_node = gtk_tree_row_reference_new (gtk_tree_view_get_model (tree_view), path);
429
priv->animation_timeout = g_timeout_add (50, animation_timeout, expander);
433
gm_cell_renderer_expander_activate (GtkCellRenderer *cell,
434
G_GNUC_UNUSED GdkEvent *event,
436
const gchar *path_string,
437
GdkRectangle *background_area,
438
G_GNUC_UNUSED GdkRectangle *cell_area,
439
G_GNUC_UNUSED GtkCellRendererState flags)
441
GmCellRendererExpander *expander;
442
GmCellRendererExpanderPriv *priv;
447
expander = GM_CELL_RENDERER_EXPANDER (cell);
448
priv = GET_PRIV (cell);
450
if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable)
453
path = gtk_tree_path_new_from_string (path_string);
455
if (gtk_tree_path_get_depth (path) > 2) {
456
gtk_tree_path_free (path);
460
g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
461
"gtk-enable-animations", &animate,
464
if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
465
gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path);
468
gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE);
473
gm_cell_renderer_expander_start_animation (expander,
474
GTK_TREE_VIEW (widget),
480
gtk_tree_path_free (path);