hythmbox-2.98/widgets/gossip-cell-renderer-expander.c

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 }