~ubuntu-branches/ubuntu/utopic/rhythmbox/utopic-proposed

« back to all changes in this revision

Viewing changes to lib/disclosure-widget.c

Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

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
 
 *  arch-tag: Implementation of the "disclosure" widget
4
 
 *
5
 
 *  Authors: Iain Holmes <iain@ximian.com>
6
 
 *
7
 
 *  Copyright 2002 Iain Holmes
8
 
 *
9
 
 *  This program is free software; you can redistribute it and/or modify
10
 
 *  it under the terms of the GNU General Public License as published by
11
 
 *  the Free Software Foundation; either version 2 of the License, or
12
 
 *  (at your option) any later version.
13
 
 *
14
 
 *  This program is distributed in the hope that it will be useful,
15
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 
 *  GNU General Public License for more details.
18
 
 *
19
 
 *  You should have received a copy of the GNU General Public License
20
 
 *  along with this program; if not, write to the Free Software
21
 
 *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
22
 
 *
23
 
 */
24
 
 
25
 
#ifdef HAVE_CONFIG_H
26
 
#include <config.h>
27
 
#endif
28
 
 
29
 
#include <gtk/gtktogglebutton.h>
30
 
#include <libgnome/gnome-i18n.h>
31
 
 
32
 
#include "disclosure-widget.h"
33
 
 
34
 
static GtkCheckButtonClass *parent_class = NULL;
35
 
 
36
 
struct _CDDBDisclosurePrivate {
37
 
        GtkWidget *container;
38
 
        char *shown;
39
 
        char *hidden;
40
 
        
41
 
        guint32 expand_id;
42
 
        GtkExpanderStyle style;
43
 
 
44
 
        int expander_size;
45
 
        int direction;
46
 
};
47
 
 
48
 
static void
49
 
finalize (GObject *object)
50
 
{
51
 
        CDDBDisclosure *disclosure;
52
 
 
53
 
        disclosure = CDDB_DISCLOSURE (object);
54
 
        if (disclosure->priv == NULL) {
55
 
                return;
56
 
        }
57
 
 
58
 
        g_free (disclosure->priv->hidden);
59
 
        g_free (disclosure->priv->shown);
60
 
 
61
 
        if (disclosure->priv->container != NULL) {
62
 
                g_object_unref (G_OBJECT (disclosure->priv->container));
63
 
        }
64
 
        
65
 
        g_free (disclosure->priv);
66
 
        disclosure->priv = NULL;
67
 
 
68
 
        G_OBJECT_CLASS (parent_class)->finalize (object);
69
 
}
70
 
 
71
 
static void
72
 
cddb_disclosure_destroy (GtkObject *obj)
73
 
{
74
 
        CDDBDisclosure *disclosure = CDDB_DISCLOSURE (obj);
75
 
 
76
 
        if (disclosure->priv->expand_id) {
77
 
                g_source_remove (disclosure->priv->expand_id);
78
 
                disclosure->priv->expand_id = 0;
79
 
        }
80
 
}
81
 
 
82
 
static void
83
 
get_x_y (CDDBDisclosure *disclosure,
84
 
         int *x,
85
 
         int *y,
86
 
         GtkStateType *state_type)
87
 
{
88
 
        GtkCheckButton *check_button;
89
 
        int indicator_size, indicator_spacing;
90
 
        int focus_width;
91
 
        int focus_pad;
92
 
        gboolean interior_focus;
93
 
        GtkWidget *widget = GTK_WIDGET (disclosure);
94
 
        GtkBin *bin = GTK_BIN (disclosure);
95
 
        int width;
96
 
        
97
 
        if (GTK_WIDGET_VISIBLE (disclosure) &&
98
 
            GTK_WIDGET_MAPPED (disclosure)) {
99
 
                check_button = GTK_CHECK_BUTTON (disclosure);
100
 
                
101
 
                gtk_widget_style_get (GTK_WIDGET (check_button),
102
 
                                      "indicator_size", &indicator_size,
103
 
                                      "indicator_spacing", &indicator_spacing,
104
 
                                      NULL);
105
 
 
106
 
                gtk_widget_style_get (widget,
107
 
                                      "interior_focus", &interior_focus,
108
 
                                      "focus-line-width", &focus_width,
109
 
                                      "focus-padding", &focus_pad,
110
 
                                      NULL);
111
 
                
112
 
                *state_type = GTK_WIDGET_STATE (widget);
113
 
                if ((*state_type != GTK_STATE_NORMAL) &&
114
 
                    (*state_type != GTK_STATE_PRELIGHT)) {
115
 
                        *state_type = GTK_STATE_NORMAL;
116
 
                }
117
 
 
118
 
                if (bin->child) {
119
 
                        width = indicator_spacing * 3 + indicator_size ;
120
 
                } else {
121
 
                        width = widget->allocation.width - 2 * GTK_CONTAINER (widget)->border_width;
122
 
                }
123
 
                
124
 
                *x = widget->allocation.x + GTK_CONTAINER (widget)->border_width + (width) / 2;
125
 
                *y = widget->allocation.y + widget->allocation.height / 2;
126
 
 
127
 
                if (interior_focus == FALSE) {
128
 
                        *x += focus_width + focus_pad;
129
 
                }
130
 
 
131
 
                *state_type = GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE ? GTK_STATE_NORMAL : GTK_WIDGET_STATE (widget);
132
 
 
133
 
                if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) {
134
 
                        *x = widget->allocation.x + widget->allocation.width - (*x - widget->allocation.x);
135
 
                }
136
 
        } else {
137
 
                *x = 0;
138
 
                *y = 0;
139
 
                *state_type = GTK_STATE_NORMAL;
140
 
        }
141
 
}
142
 
 
143
 
static gboolean
144
 
expand_collapse_timeout (gpointer data)
145
 
{
146
 
        GtkWidget *widget = data;
147
 
        CDDBDisclosure *disclosure = data;
148
 
        GtkStateType state_type;
149
 
        int x, y;
150
 
        gboolean ret = TRUE;
151
 
 
152
 
        GDK_THREADS_ENTER ();
153
 
 
154
 
        g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
155
 
        g_return_val_if_fail (IS_CDDB_DISCLOSURE (disclosure), FALSE);
156
 
        
157
 
        if (widget->window) {
158
 
                gdk_window_invalidate_rect (widget->window, &widget->allocation, TRUE);
159
 
                get_x_y (disclosure, &x, &y, &state_type);
160
 
                
161
 
                gtk_paint_expander (widget->style,
162
 
                                    widget->window,
163
 
                                    state_type,
164
 
                                    &widget->allocation,
165
 
                                    widget,
166
 
                                    "disclosure",
167
 
                                    x, y,
168
 
                                    disclosure->priv->style);
169
 
        }
170
 
 
171
 
        disclosure->priv->style += disclosure->priv->direction;
172
 
        if ((int) disclosure->priv->style > (int) GTK_EXPANDER_EXPANDED) {
173
 
                disclosure->priv->style = GTK_EXPANDER_EXPANDED;
174
 
 
175
 
                if (disclosure->priv->container != NULL) {
176
 
                        gtk_widget_show (disclosure->priv->container);
177
 
                }
178
 
 
179
 
                g_object_set (G_OBJECT (disclosure),
180
 
                              "label", disclosure->priv->hidden,
181
 
                              NULL);
182
 
                              
183
 
                ret = FALSE;
184
 
        } else if ((int) disclosure->priv->style < (int) GTK_EXPANDER_COLLAPSED) {
185
 
                disclosure->priv->style = GTK_EXPANDER_COLLAPSED;
186
 
 
187
 
                if (disclosure->priv->container != NULL) {
188
 
                        gtk_widget_hide (disclosure->priv->container);
189
 
                                       
190
 
                }
191
 
 
192
 
                g_object_set (G_OBJECT (disclosure),
193
 
                              "label", disclosure->priv->shown,
194
 
                              NULL);
195
 
 
196
 
                ret = FALSE;
197
 
        } 
198
 
 
199
 
        if (ret == FALSE)
200
 
                disclosure->priv->expand_id = 0;
201
 
 
202
 
        GDK_THREADS_LEAVE ();
203
 
        return ret;
204
 
}
205
 
 
206
 
static void
207
 
do_animation (CDDBDisclosure *disclosure,
208
 
              gboolean opening)
209
 
{
210
 
        g_return_if_fail (IS_CDDB_DISCLOSURE (disclosure));
211
 
 
212
 
        if (disclosure->priv->expand_id > 0) {
213
 
                g_source_remove (disclosure->priv->expand_id);
214
 
                disclosure->priv->expand_id = 0;
215
 
        }
216
 
 
217
 
        disclosure->priv->direction = opening ? 1 : -1;
218
 
        disclosure->priv->expand_id = g_timeout_add (50, expand_collapse_timeout, disclosure);
219
 
}
220
 
 
221
 
static void
222
 
toggled (GtkToggleButton *tb)
223
 
{
224
 
        CDDBDisclosure *disclosure;
225
 
 
226
 
        disclosure = CDDB_DISCLOSURE (tb);
227
 
        do_animation (disclosure, gtk_toggle_button_get_active (tb));
228
 
 
229
 
        if (disclosure->priv->container == NULL) {
230
 
                return;
231
 
        }
232
 
}
233
 
 
234
 
static void
235
 
draw_indicator (GtkCheckButton *check,
236
 
                GdkRectangle *area)
237
 
{
238
 
        GtkWidget *widget = GTK_WIDGET (check);
239
 
        CDDBDisclosure *disclosure = CDDB_DISCLOSURE (check);
240
 
        GtkStateType state_type;
241
 
        int x, y;
242
 
 
243
 
        /* GtkCheckButton-like prelighting, unsurprisingly from gtkcheckbutton.c */
244
 
        if (GTK_WIDGET_STATE (check) == GTK_STATE_PRELIGHT)
245
 
        {
246
 
                GdkRectangle restrict_area;
247
 
                GdkRectangle new_area;
248
 
 
249
 
                restrict_area.x = widget->allocation.x + GTK_CONTAINER (widget)->border_width;
250
 
                restrict_area.y = widget->allocation.y + GTK_CONTAINER (widget)->border_width;
251
 
                restrict_area.width = widget->allocation.width - (2 * GTK_CONTAINER (widget)->border_width);
252
 
                restrict_area.height = widget->allocation.height - (2 * GTK_CONTAINER (widget)->border_width);
253
 
 
254
 
                if (gdk_rectangle_intersect (area, &restrict_area, &new_area))
255
 
                {
256
 
                        gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_PRELIGHT,
257
 
                                        GTK_SHADOW_ETCHED_OUT, 
258
 
                                        area, widget, "checkbutton",
259
 
                                        new_area.x, new_area.y,
260
 
                                        new_area.width, new_area.height);
261
 
                }
262
 
        }
263
 
 
264
 
        get_x_y (disclosure, &x, &y, &state_type);
265
 
        gtk_paint_expander (widget->style,
266
 
                            widget->window,
267
 
                            state_type,
268
 
                            area,
269
 
                            widget,
270
 
                            "treeview",
271
 
                            x, y,
272
 
                            disclosure->priv->style);
273
 
}
274
 
 
275
 
static void
276
 
class_init (CDDBDisclosureClass *klass)
277
 
{
278
 
        GObjectClass *object_class;
279
 
        GtkObjectClass *gtk_object_class = (GtkObjectClass *) klass;
280
 
        GtkWidgetClass *widget_class;
281
 
        GtkCheckButtonClass *button_class;
282
 
        GtkToggleButtonClass *toggle_class;
283
 
        
284
 
        object_class = G_OBJECT_CLASS (klass);
285
 
        widget_class = GTK_WIDGET_CLASS (klass);
286
 
        button_class = GTK_CHECK_BUTTON_CLASS (klass);
287
 
        toggle_class = GTK_TOGGLE_BUTTON_CLASS (klass);
288
 
        
289
 
        toggle_class->toggled = toggled;
290
 
        button_class->draw_indicator = draw_indicator;
291
 
 
292
 
        object_class->finalize = finalize;
293
 
 
294
 
        gtk_object_class->destroy = cddb_disclosure_destroy;
295
 
 
296
 
        parent_class = g_type_class_peek_parent (klass);
297
 
 
298
 
        gtk_widget_class_install_style_property (widget_class,
299
 
                                                 g_param_spec_int ("expander_size",
300
 
                                                                   _("Expander Size"),
301
 
                                                                   _("Size of the expander arrow"),
302
 
                                                                   0, G_MAXINT,
303
 
                                                                   10, G_PARAM_READABLE));
304
 
}
305
 
 
306
 
static void
307
 
init (CDDBDisclosure *disclosure)
308
 
{
309
 
        disclosure->priv = g_new0 (CDDBDisclosurePrivate, 1);
310
 
        disclosure->priv->expander_size = 10;
311
 
}
312
 
 
313
 
GType
314
 
cddb_disclosure_get_type (void)
315
 
{
316
 
        static GType type = 0;
317
 
 
318
 
        if (type == 0) {
319
 
                GTypeInfo info = {
320
 
                        sizeof (CDDBDisclosureClass),
321
 
                        NULL, NULL, (GClassInitFunc) class_init, NULL, NULL,
322
 
                        sizeof (CDDBDisclosure), 0, (GInstanceInitFunc) init
323
 
                };
324
 
 
325
 
                type = g_type_register_static (GTK_TYPE_CHECK_BUTTON, "CDDBDisclosure", &info, 0);
326
 
        }
327
 
 
328
 
        return type;
329
 
}
330
 
 
331
 
GtkWidget *
332
 
cddb_disclosure_new (const char *shown,
333
 
                     const char *hidden)
334
 
{
335
 
        CDDBDisclosure *disclosure;
336
 
 
337
 
        disclosure = g_object_new (cddb_disclosure_get_type (), "label", shown, "use_underline", TRUE, NULL);
338
 
 
339
 
        disclosure->priv->shown = g_strdup (shown);
340
 
        disclosure->priv->hidden = g_strdup (hidden);
341
 
        return GTK_WIDGET (disclosure);
342
 
}
343
 
 
344
 
void
345
 
cddb_disclosure_set_container   (CDDBDisclosure *cddb, GtkWidget *widget)
346
 
{
347
 
        if (widget != NULL) {
348
 
                g_object_ref (widget);
349
 
        }
350
 
        if (cddb->priv->container != NULL) {
351
 
                g_object_unref (cddb->priv->container);
352
 
        }
353
 
        cddb->priv->container = widget;
354
 
}
355
 
 
356
 
/* Strings for custom glade widgets (see below) can't be translated, so provide
357
 
 * this function for setting translatable labels after the widget has been
358
 
 * created by glade.
359
 
 */
360
 
void
361
 
cddb_disclosure_set_labels      (CDDBDisclosure *cddb,
362
 
                                 const char *label_when_shown,
363
 
                                 const char *label_when_hidden)
364
 
{
365
 
        gboolean active;
366
 
 
367
 
        g_free (cddb->priv->shown);     
368
 
        g_free (cddb->priv->hidden);    
369
 
        cddb->priv->shown = g_strdup (label_when_shown);
370
 
        cddb->priv->hidden = g_strdup (label_when_hidden);
371
 
 
372
 
        /* update the correct label text depending on button state */
373
 
        active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(cddb));
374
 
        g_object_set (G_OBJECT(cddb),
375
 
                      "label", active ? cddb->priv->shown : cddb->priv->hidden,
376
 
                      NULL);
377
 
}
378
 
 
379
 
/* Custom widget creation function for glade */
380
 
GtkWidget *
381
 
cddb_disclosure_new_from_glade  (gchar *widget_name,
382
 
                                 gchar *string1, gchar *string2,
383
 
                                 gint int1, gint int2)
384
 
{
385
 
        GtkWidget *w = cddb_disclosure_new ("", "");
386
 
        gtk_widget_set_name (w, widget_name);
387
 
        gtk_widget_show (w);
388
 
        return w;
389
 
}