~bcurtiswx/ubuntu/precise/empathy/3.4.2.1-0ubuntu1

« back to all changes in this revision

Viewing changes to libempathy-gtk/gossip-cell-renderer-expander.c

  • Committer: Bazaar Package Importer
  • Author(s): Sjoerd Simons
  • Date: 2007-05-20 15:31:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070520153142-r3auwguxdgxhktqb
Tags: upstream-0.4
ImportĀ upstreamĀ versionĀ 0.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 * Copyright (C) 2006-2007 Imendio AB
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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.
 
19
 * 
 
20
 * Authors: Kristian Rietveld <kris@imendio.com>
 
21
 */
 
22
 
 
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).
 
30
 */
 
31
 
 
32
#include <gtk/gtktreeview.h>
 
33
 
 
34
#include "gossip-cell-renderer-expander.h"
 
35
 
 
36
#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CELL_RENDERER_EXPANDER, GossipCellRendererExpanderPriv))
 
37
 
 
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,
 
41
                                                            guint                            param_id,
 
42
                                                            GValue                          *value,
 
43
                                                            GParamSpec                      *pspec);
 
44
static void     gossip_cell_renderer_expander_set_property (GObject                         *object,
 
45
                                                            guint                            param_id,
 
46
                                                            const GValue                    *value,
 
47
                                                            GParamSpec                      *pspec);
 
48
static void     gossip_cell_renderer_expander_finalize     (GObject                         *object);
 
49
static void     gossip_cell_renderer_expander_get_size     (GtkCellRenderer                 *cell,
 
50
                                                            GtkWidget                       *widget,
 
51
                                                            GdkRectangle                    *cell_area,
 
52
                                                            gint                            *x_offset,
 
53
                                                            gint                            *y_offset,
 
54
                                                            gint                            *width,
 
55
                                                            gint                            *height);
 
56
static void     gossip_cell_renderer_expander_render       (GtkCellRenderer                 *cell,
 
57
                                                            GdkWindow                       *window,
 
58
                                                            GtkWidget                       *widget,
 
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,
 
64
                                                            GdkEvent                        *event,
 
65
                                                            GtkWidget                       *widget,
 
66
                                                            const gchar                     *path,
 
67
                                                            GdkRectangle                    *background_area,
 
68
                                                            GdkRectangle                    *cell_area,
 
69
                                                            GtkCellRendererState             flags);
 
70
 
 
71
enum {
 
72
        PROP_0,
 
73
        PROP_EXPANDER_STYLE,
 
74
        PROP_EXPANDER_SIZE,
 
75
        PROP_ACTIVATABLE
 
76
};
 
77
 
 
78
typedef struct _GossipCellRendererExpanderPriv GossipCellRendererExpanderPriv;
 
79
 
 
80
struct _GossipCellRendererExpanderPriv {
 
81
        GtkExpanderStyle     expander_style;
 
82
        gint                 expander_size;
 
83
 
 
84
        GtkTreeView         *animation_view;
 
85
        GtkTreeRowReference *animation_node;
 
86
        GtkExpanderStyle     animation_style;
 
87
        guint                animation_timeout;
 
88
        GdkRectangle         animation_area;
 
89
 
 
90
        guint                activatable : 1;
 
91
        guint                animation_expanding : 1;
 
92
};
 
93
 
 
94
G_DEFINE_TYPE (GossipCellRendererExpander, gossip_cell_renderer_expander, GTK_TYPE_CELL_RENDERER)
 
95
 
 
96
static void
 
97
gossip_cell_renderer_expander_init (GossipCellRendererExpander *expander)
 
98
{
 
99
        GossipCellRendererExpanderPriv *priv;
 
100
 
 
101
        priv = GET_PRIV (expander);
 
102
 
 
103
        priv->expander_style = GTK_EXPANDER_COLLAPSED;
 
104
        priv->expander_size = 12;
 
105
        priv->activatable = TRUE;
 
106
        priv->animation_node = NULL;
 
107
 
 
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;
 
111
}
 
112
 
 
113
static void
 
114
gossip_cell_renderer_expander_class_init (GossipCellRendererExpanderClass *klass)
 
115
{
 
116
        GObjectClass         *object_class;
 
117
        GtkCellRendererClass *cell_class;
 
118
 
 
119
        object_class  = G_OBJECT_CLASS (klass);
 
120
        cell_class = GTK_CELL_RENDERER_CLASS (klass);
 
121
 
 
122
        object_class->finalize = gossip_cell_renderer_expander_finalize;
 
123
 
 
124
        object_class->get_property = gossip_cell_renderer_expander_get_property;
 
125
        object_class->set_property = gossip_cell_renderer_expander_set_property;
 
126
 
 
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;
 
130
 
 
131
        g_object_class_install_property (object_class,
 
132
                                         PROP_EXPANDER_STYLE,
 
133
                                         g_param_spec_enum ("expander-style",
 
134
                                                            "Expander Style",
 
135
                                                            "Style to use when painting the expander",
 
136
                                                            GTK_TYPE_EXPANDER_STYLE,
 
137
                                                            GTK_EXPANDER_COLLAPSED,
 
138
                                                            G_PARAM_READWRITE));
 
139
 
 
140
        g_object_class_install_property (object_class,
 
141
                                         PROP_EXPANDER_SIZE,
 
142
                                         g_param_spec_int ("expander-size",
 
143
                                                           "Expander Size",
 
144
                                                           "The size of the expander",
 
145
                                                           0,
 
146
                                                           G_MAXINT,
 
147
                                                           12,
 
148
                                                           G_PARAM_READWRITE));
 
149
 
 
150
        g_object_class_install_property (object_class,
 
151
                                         PROP_ACTIVATABLE,
 
152
                                         g_param_spec_boolean ("activatable",
 
153
                                                               "Activatable",
 
154
                                                               "The expander can be activated",
 
155
                                                               TRUE,
 
156
                                                               G_PARAM_READWRITE));
 
157
 
 
158
        g_type_class_add_private (object_class, sizeof (GossipCellRendererExpanderPriv));
 
159
}
 
160
 
 
161
static void
 
162
gossip_cell_renderer_expander_get_property (GObject    *object,
 
163
                                            guint       param_id,
 
164
                                            GValue     *value,
 
165
                                            GParamSpec *pspec)
 
166
{
 
167
        GossipCellRendererExpander     *expander;
 
168
        GossipCellRendererExpanderPriv *priv;
 
169
 
 
170
        expander = GOSSIP_CELL_RENDERER_EXPANDER (object);
 
171
        priv = GET_PRIV (expander);
 
172
 
 
173
        switch (param_id) {
 
174
        case PROP_EXPANDER_STYLE:
 
175
                g_value_set_enum (value, priv->expander_style);
 
176
                break;
 
177
 
 
178
        case PROP_EXPANDER_SIZE:
 
179
                g_value_set_int (value, priv->expander_size);
 
180
                break;
 
181
 
 
182
        case PROP_ACTIVATABLE:
 
183
                g_value_set_boolean (value, priv->activatable);
 
184
                break;
 
185
 
 
186
        default:
 
187
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 
188
                break;
 
189
        }
 
190
}
 
191
 
 
192
static void
 
193
gossip_cell_renderer_expander_set_property (GObject      *object,
 
194
                                            guint         param_id,
 
195
                                            const GValue *value,
 
196
                                            GParamSpec   *pspec)
 
197
{
 
198
        GossipCellRendererExpander     *expander;
 
199
        GossipCellRendererExpanderPriv *priv;
 
200
 
 
201
        expander = GOSSIP_CELL_RENDERER_EXPANDER (object);
 
202
        priv = GET_PRIV (expander);
 
203
 
 
204
        switch (param_id) {
 
205
        case PROP_EXPANDER_STYLE:
 
206
                priv->expander_style = g_value_get_enum (value);
 
207
                break;
 
208
 
 
209
        case PROP_EXPANDER_SIZE:
 
210
                priv->expander_size = g_value_get_int (value);
 
211
                break;
 
212
 
 
213
        case PROP_ACTIVATABLE:
 
214
                priv->activatable = g_value_get_boolean (value);
 
215
                break;
 
216
 
 
217
        default:
 
218
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 
219
                break;
 
220
        }
 
221
}
 
222
 
 
223
static void
 
224
gossip_cell_renderer_expander_finalize (GObject *object)
 
225
{
 
226
        GossipCellRendererExpanderPriv *priv;
 
227
 
 
228
        priv = GET_PRIV (object);
 
229
 
 
230
        if (priv->animation_timeout) {
 
231
                g_source_remove (priv->animation_timeout);
 
232
                priv->animation_timeout = 0;
 
233
        }
 
234
 
 
235
        if (priv->animation_node) {
 
236
                gtk_tree_row_reference_free (priv->animation_node);
 
237
        }
 
238
 
 
239
        (* G_OBJECT_CLASS (gossip_cell_renderer_expander_parent_class)->finalize) (object);
 
240
}
 
241
 
 
242
GtkCellRenderer *
 
243
gossip_cell_renderer_expander_new (void)
 
244
{
 
245
        return g_object_new (GOSSIP_TYPE_CELL_RENDERER_EXPANDER, NULL);
 
246
}
 
247
 
 
248
static void
 
249
gossip_cell_renderer_expander_get_size (GtkCellRenderer *cell,
 
250
                                        GtkWidget       *widget,
 
251
                                        GdkRectangle    *cell_area,
 
252
                                        gint            *x_offset,
 
253
                                        gint            *y_offset,
 
254
                                        gint            *width,
 
255
                                        gint            *height)
 
256
{
 
257
        GossipCellRendererExpander     *expander;
 
258
        GossipCellRendererExpanderPriv *priv;
 
259
 
 
260
        expander = (GossipCellRendererExpander*) cell;
 
261
        priv = GET_PRIV (expander);
 
262
 
 
263
        if (cell_area) {
 
264
                if (x_offset) {
 
265
                        *x_offset = cell->xalign * (cell_area->width - (priv->expander_size + (2 * cell->xpad)));
 
266
                        *x_offset = MAX (*x_offset, 0);
 
267
                }
 
268
 
 
269
                if (y_offset) {
 
270
                        *y_offset = cell->yalign * (cell_area->height - (priv->expander_size + (2 * cell->ypad)));
 
271
                        *y_offset = MAX (*y_offset, 0);
 
272
                }
 
273
        } else {
 
274
                if (x_offset)
 
275
                        *x_offset = 0;
 
276
 
 
277
                if (y_offset)
 
278
                        *y_offset = 0;
 
279
        }
 
280
 
 
281
        if (width)
 
282
                *width = cell->xpad * 2 + priv->expander_size;
 
283
 
 
284
        if (height)
 
285
                *height = cell->ypad * 2 + priv->expander_size;
 
286
}
 
287
 
 
288
static void
 
289
gossip_cell_renderer_expander_render (GtkCellRenderer      *cell,
 
290
                                      GdkWindow            *window,
 
291
                                      GtkWidget            *widget,
 
292
                                      GdkRectangle         *background_area,
 
293
                                      GdkRectangle         *cell_area,
 
294
                                      GdkRectangle         *expose_area,
 
295
                                      GtkCellRendererState  flags)
 
296
{
 
297
        GossipCellRendererExpander     *expander;
 
298
        GossipCellRendererExpanderPriv *priv;
 
299
        GtkExpanderStyle                expander_style;
 
300
        gint                            x_offset, y_offset;
 
301
 
 
302
        expander = (GossipCellRendererExpander*) cell;
 
303
        priv = GET_PRIV (expander);
 
304
 
 
305
        if (priv->animation_node) {
 
306
                GtkTreePath *path;
 
307
                GdkRectangle rect;
 
308
 
 
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,
 
312
                                                   NULL, &rect);
 
313
                gtk_tree_path_free (path);
 
314
 
 
315
                if (background_area->y == rect.y)
 
316
                        expander_style = priv->animation_style;
 
317
                else
 
318
                        expander_style = priv->expander_style;
 
319
        } else
 
320
                expander_style = priv->expander_style;
 
321
 
 
322
        gossip_cell_renderer_expander_get_size (cell, widget, cell_area,
 
323
                                                &x_offset, &y_offset,
 
324
                                                NULL, NULL);
 
325
 
 
326
        gtk_paint_expander (widget->style,
 
327
                            window,
 
328
                            GTK_STATE_NORMAL,
 
329
                            expose_area,
 
330
                            widget,
 
331
                            "treeview",
 
332
                            cell_area->x + x_offset + cell->xpad + priv->expander_size / 2,
 
333
                            cell_area->y + y_offset + cell->ypad + priv->expander_size / 2,
 
334
                            expander_style);
 
335
}
 
336
 
 
337
static void
 
338
invalidate_node (GtkTreeView *tree_view,
 
339
                 GtkTreePath *path)
 
340
{
 
341
       GdkWindow    *bin_window;
 
342
       GdkRectangle  rect;
 
343
 
 
344
       bin_window = gtk_tree_view_get_bin_window (tree_view);
 
345
 
 
346
       gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
 
347
 
 
348
       rect.x = 0;
 
349
       rect.width = GTK_WIDGET (tree_view)->allocation.width;
 
350
 
 
351
       gdk_window_invalidate_rect (bin_window, &rect, TRUE);
 
352
}
 
353
 
 
354
static gboolean
 
355
do_animation (GossipCellRendererExpander *expander)
 
356
{
 
357
        GossipCellRendererExpanderPriv *priv;
 
358
        GtkTreePath                    *path;
 
359
        gboolean                        done = FALSE;
 
360
 
 
361
        priv = GET_PRIV (expander);
 
362
 
 
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;
 
368
                        done = TRUE;
 
369
                }
 
370
        } else {
 
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;
 
375
                        done = TRUE;
 
376
                }
 
377
        }
 
378
 
 
379
        path = gtk_tree_row_reference_get_path (priv->animation_node);
 
380
        invalidate_node (priv->animation_view, path);
 
381
        gtk_tree_path_free (path);
 
382
 
 
383
        if (done) {
 
384
                gtk_tree_row_reference_free (priv->animation_node);
 
385
                priv->animation_node = NULL;
 
386
                priv->animation_timeout = 0;
 
387
        }
 
388
 
 
389
        return !done;
 
390
}
 
391
 
 
392
static gboolean
 
393
animation_timeout (gpointer data)
 
394
{
 
395
        gboolean retval;
 
396
 
 
397
        GDK_THREADS_ENTER ();
 
398
 
 
399
        retval = do_animation (data);
 
400
 
 
401
        GDK_THREADS_LEAVE ();
 
402
 
 
403
        return retval;
 
404
}
 
405
 
 
406
static void
 
407
gossip_cell_renderer_expander_start_animation (GossipCellRendererExpander *expander,
 
408
                                               GtkTreeView                *tree_view,
 
409
                                               GtkTreePath                *path,
 
410
                                               gboolean                    expanding,
 
411
                                               GdkRectangle               *background_area)
 
412
{
 
413
        GossipCellRendererExpanderPriv *priv;
 
414
 
 
415
        priv = GET_PRIV (expander);
 
416
 
 
417
        if (expanding) {
 
418
                priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
 
419
        } else {
 
420
                priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
 
421
        }
 
422
 
 
423
        invalidate_node (tree_view, path);
 
424
 
 
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);
 
429
}
 
430
 
 
431
static gboolean
 
432
gossip_cell_renderer_expander_activate (GtkCellRenderer      *cell,
 
433
                                        GdkEvent             *event,
 
434
                                        GtkWidget            *widget,
 
435
                                        const gchar          *path_string,
 
436
                                        GdkRectangle         *background_area,
 
437
                                        GdkRectangle         *cell_area,
 
438
                                        GtkCellRendererState  flags)
 
439
{
 
440
        GossipCellRendererExpander     *expander;
 
441
        GossipCellRendererExpanderPriv *priv;
 
442
        GtkTreePath                    *path;
 
443
        gboolean                        animate;
 
444
        gboolean                        expanding;
 
445
 
 
446
        expander = GOSSIP_CELL_RENDERER_EXPANDER (cell);
 
447
        priv = GET_PRIV (cell);
 
448
 
 
449
        if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable)
 
450
                return FALSE;
 
451
 
 
452
        path = gtk_tree_path_new_from_string (path_string);
 
453
 
 
454
        if (gtk_tree_path_get_depth (path) > 1) {
 
455
                gtk_tree_path_free (path);
 
456
                return TRUE;
 
457
        }
 
458
 
 
459
        g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
 
460
                      "gtk-enable-animations", &animate,
 
461
                      NULL);
 
462
 
 
463
        if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
 
464
                gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path);
 
465
                expanding = FALSE;
 
466
        } else {
 
467
                gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE);
 
468
                expanding = TRUE;
 
469
        }
 
470
 
 
471
        if (animate) {
 
472
                gossip_cell_renderer_expander_start_animation (expander,
 
473
                                                               GTK_TREE_VIEW (widget),
 
474
                                                               path,
 
475
                                                               expanding,
 
476
                                                               background_area);
 
477
        }
 
478
 
 
479
        gtk_tree_path_free (path);
 
480
 
 
481
        return TRUE;
 
482
}