No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2006 Kristian Rietveld <kris@babi-pangang.org>
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
21 #include "config.h"
22
23 #include <gtk/gtk.h>
24
25 #include "gossip-cell-renderer-expander.h"
26
27 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CELL_RENDERER_EXPANDER, GossipCellRendererExpanderPriv))
28
29 static void gossip_cell_renderer_expander_init (GossipCellRendererExpander *expander);
30 static void gossip_cell_renderer_expander_class_init (GossipCellRendererExpanderClass *klass);
31 static void gossip_cell_renderer_expander_get_property (GObject *object,
32 guint param_id,
33 GValue *value,
34 GParamSpec *pspec);
35 static void gossip_cell_renderer_expander_set_property (GObject *object,
36 guint param_id,
37 const GValue *value,
38 GParamSpec *pspec);
39 static void gossip_cell_renderer_expander_get_size (GtkCellRenderer *cell,
40 GtkWidget *widget,
41 const GdkRectangle *cell_area,
42 gint *x_offset,
43 gint *y_offset,
44 gint *width,
45 gint *height);
46 static void gossip_cell_renderer_expander_render (GtkCellRenderer *cell,
47 cairo_t *cr,
48 GtkWidget *widget,
49 const GdkRectangle *background_area,
50 const GdkRectangle *cell_area,
51 GtkCellRendererState flags);
52 static gboolean gossip_cell_renderer_expander_activate (GtkCellRenderer *cell,
53 GdkEvent *event,
54 GtkWidget *widget,
55 const gchar *path,
56 const GdkRectangle *background_area,
57 const GdkRectangle *cell_area,
58 GtkCellRendererState flags);
59
60 /* Properties */
61 enum {
62 PROP_0,
63 PROP_EXPANDER_STYLE,
64 PROP_EXPANDER_SIZE,
65 PROP_ACTIVATABLE
66 };
67
68 typedef struct _GossipCellRendererExpanderPriv GossipCellRendererExpanderPriv;
69
70 struct _GossipCellRendererExpanderPriv {
71 gint expander_size;
72
73 guint activatable : 1;
74 GtkExpanderStyle expander_style;
75 };
76
77 G_DEFINE_TYPE (GossipCellRendererExpander, gossip_cell_renderer_expander, GTK_TYPE_CELL_RENDERER)
78
79 static void
80 gossip_cell_renderer_expander_init (GossipCellRendererExpander *expander)
81 {
82 GossipCellRendererExpanderPriv *priv;
83
84 priv = GET_PRIV (expander);
85
86 priv->expander_size = 12;
87 priv->activatable = TRUE;
88
89 gtk_cell_renderer_set_padding (GTK_CELL_RENDERER (expander), 2, 2);
90 g_object_set (expander,
91 "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
92 NULL);
93 }
94
95 static void
96 gossip_cell_renderer_expander_class_init (GossipCellRendererExpanderClass *klass)
97 {
98 GObjectClass *object_class;
99 GtkCellRendererClass *cell_class;
100
101 object_class = G_OBJECT_CLASS (klass);
102 cell_class = GTK_CELL_RENDERER_CLASS (klass);
103
104 object_class->get_property = gossip_cell_renderer_expander_get_property;
105 object_class->set_property = gossip_cell_renderer_expander_set_property;
106
107 cell_class->get_size = gossip_cell_renderer_expander_get_size;
108 cell_class->render = gossip_cell_renderer_expander_render;
109 cell_class->activate = gossip_cell_renderer_expander_activate;
110
111 g_object_class_install_property (object_class,
112 PROP_EXPANDER_STYLE,
113 g_param_spec_enum ("expander-style",
114 "Expander Style",
115 "Style to use when painting the expander",
116 GTK_TYPE_EXPANDER_STYLE,
117 GTK_EXPANDER_COLLAPSED,
118 G_PARAM_READWRITE));
119
120 g_object_class_install_property (object_class,
121 PROP_EXPANDER_SIZE,
122 g_param_spec_int ("expander-size",
123 "Expander Size",
124 "The size of the expander",
125 0,
126 G_MAXINT,
127 12,
128 G_PARAM_READWRITE));
129
130 g_object_class_install_property (object_class,
131 PROP_ACTIVATABLE,
132 g_param_spec_boolean ("activatable",
133 "Activatable",
134 "The expander can be activated",
135 TRUE,
136 G_PARAM_READWRITE));
137
138 g_type_class_add_private (object_class, sizeof (GossipCellRendererExpanderPriv));
139 }
140
141 static void
142 gossip_cell_renderer_expander_get_property (GObject *object,
143 guint param_id,
144 GValue *value,
145 GParamSpec *pspec)
146 {
147 GossipCellRendererExpander *expander;
148 GossipCellRendererExpanderPriv *priv;
149
150 expander = GOSSIP_CELL_RENDERER_EXPANDER (object);
151 priv = GET_PRIV (expander);
152
153 switch (param_id) {
154 case PROP_EXPANDER_STYLE:
155 g_value_set_enum (value, priv->expander_style);
156 break;
157
158 case PROP_EXPANDER_SIZE:
159 g_value_set_int (value, priv->expander_size);
160 break;
161
162 case PROP_ACTIVATABLE:
163 g_value_set_boolean (value, priv->activatable);
164 break;
165
166 default:
167 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
168 break;
169 }
170 }
171
172 static void
173 gossip_cell_renderer_expander_set_property (GObject *object,
174 guint param_id,
175 const GValue *value,
176 GParamSpec *pspec)
177 {
178 GossipCellRendererExpander *expander;
179 GossipCellRendererExpanderPriv *priv;
180
181 expander = GOSSIP_CELL_RENDERER_EXPANDER (object);
182 priv = GET_PRIV (expander);
183
184 switch (param_id) {
185 case PROP_EXPANDER_STYLE:
186 priv->expander_style = g_value_get_enum (value);
187 break;
188
189 case PROP_EXPANDER_SIZE:
190 priv->expander_size = g_value_get_int (value);
191 break;
192
193 case PROP_ACTIVATABLE:
194 priv->activatable = g_value_get_boolean (value);
195 break;
196
197 default:
198 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
199 break;
200 }
201 }
202
203 GtkCellRenderer *
204 gossip_cell_renderer_expander_new (void)
205 {
206 return g_object_new (GOSSIP_TYPE_CELL_RENDERER_EXPANDER, NULL);
207 }
208
209 /* XXX implement preferred height/width/h-f-w/w-f-h */
210 static void
211 gossip_cell_renderer_expander_get_size (GtkCellRenderer *cell,
212 GtkWidget *widget,
213 const GdkRectangle *cell_area,
214 gint *x_offset,
215 gint *y_offset,
216 gint *width,
217 gint *height)
218 {
219 GossipCellRendererExpander *expander;
220 GossipCellRendererExpanderPriv *priv;
221 gint xpad, ypad;
222 gfloat xalign, yalign;
223
224 expander = (GossipCellRendererExpander*) cell;
225 priv = GET_PRIV (expander);
226 gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
227
228 if (cell_area) {
229
230 gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
231
232 if (x_offset) {
233 *x_offset = xalign * (cell_area->width - (priv->expander_size + (2 * xpad)));
234 *x_offset = MAX (*x_offset, 0);
235 }
236
237 if (y_offset) {
238 *y_offset = yalign * (cell_area->height - (priv->expander_size + (2 * ypad)));
239 *y_offset = MAX (*y_offset, 0);
240 }
241 } else {
242 if (x_offset)
243 *x_offset = 0;
244
245 if (y_offset)
246 *y_offset = 0;
247 }
248
249 if (width)
250 *width = xpad * 2 + priv->expander_size;
251
252 if (height)
253 *height = ypad * 2 + priv->expander_size;
254 }
255
256 static void
257 gossip_cell_renderer_expander_render (GtkCellRenderer *cell,
258 cairo_t *cr,
259 GtkWidget *widget,
260 const GdkRectangle *background_area,
261 const GdkRectangle *cell_area,
262 GtkCellRendererState flags)
263 {
264 GossipCellRendererExpander *expander;
265 GossipCellRendererExpanderPriv *priv;
266 GtkStyleContext *style_context;
267 gint x_offset, y_offset;
268 gint xpad, ypad;
269 GtkStateFlags state;
270
271 expander = (GossipCellRendererExpander*) cell;
272 priv = GET_PRIV (expander);
273
274 gossip_cell_renderer_expander_get_size (cell, widget, cell_area,
275 &x_offset, &y_offset,
276 NULL, NULL);
277 gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
278
279 style_context = gtk_widget_get_style_context (widget);
280
281 gtk_style_context_save (style_context);
282 gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_EXPANDER);
283
284 state = gtk_cell_renderer_get_state (cell, widget, flags);
285
286 if (priv->expander_style == GTK_EXPANDER_COLLAPSED) {
287 state |= GTK_STATE_FLAG_NORMAL;
288 } else {
289 state |= GTK_STATE_FLAG_ACTIVE;
290 }
291
292 gtk_style_context_set_state (style_context, state);
293
294 gtk_render_expander (style_context,
295 cr,
296 cell_area->x + x_offset + xpad,
297 cell_area->y + y_offset + ypad,
298 priv->expander_size,
299 priv->expander_size);
300
301 gtk_style_context_restore (style_context);
302 }
303
304 static gboolean
305 gossip_cell_renderer_expander_activate (GtkCellRenderer *cell,
306 GdkEvent *event,
307 GtkWidget *widget,
308 const gchar *path_string,
309 const GdkRectangle *background_area,
310 const GdkRectangle *cell_area,
311 GtkCellRendererState flags)
312 {
313 GossipCellRendererExpanderPriv *priv;
314 GtkTreePath *path;
315 gboolean in_cell;
316 int mouse_x;
317 int mouse_y;
318
319 priv = GET_PRIV (cell);
320
321 if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable)
322 return FALSE;
323
324 path = gtk_tree_path_new_from_string (path_string);
325
326 gdk_window_get_device_position (gtk_widget_get_window (widget),
327 gdk_event_get_source_device (event),
328 &mouse_x,
329 &mouse_y,
330 NULL);
331 gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (widget),
332 mouse_x, mouse_y,
333 &mouse_x, &mouse_y);
334
335 /* check if click is within the cell */
336 if (mouse_x - cell_area->x >= 0
337 && mouse_x - cell_area->x <= cell_area->width) {
338 in_cell = TRUE;
339 } else {
340 in_cell = FALSE;
341 }
342
343 if (! in_cell) {
344 gtk_tree_path_free (path);
345 return FALSE;
346 }
347
348 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
349 gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path);
350 } else {
351 gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE);
352 }
353
354 gtk_tree_path_free (path);
355
356 return TRUE;
357 }