1 /*
2  *      cellrendererbreakicon.c
3  *
4  *      Copyright 2012 Alexander Petukhov <devel(at)apetukhov.ru>
5  *
6  *      This program is free software; you can redistribute it and/or modify
7  *      it under the terms of the GNU General Public License as published by
8  *      the Free Software Foundation; either version 2 of the License, or
9  *      (at your option) any later version.
10  *
11  *      This program is distributed in the hope that it will be useful,
12  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *      GNU General Public License for more details.
15  *
16  *      You should have received a copy of the GNU General Public License
17  *      along with this program; if not, write to the Free Software
18  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *      MA 02110-1301, USA.
20  */
21 
22 /*
23  * 		cell renderer class that renders breakpoint icon, that in turn depends on a hitscount,condition and whether
24  * 		a breakpoint is enabled or disabled
25  */
26 
27 #include <string.h>
28 #include <geanyplugin.h>
29 
30 #include "cellrendererbreakicon.h"
31 
32 enum {
33   PROP_0,
34   PROP_PIXBUF_ENABLED,
35   PROP_PIXBUF_DISABLED,
36   PROP_PIXBUF_CONDITIONAL,
37   PROP_PIXBUF_FILE,
38   PROP_ENABLED,
39   PROP_CONDITION,
40   PROP_HITSCOUNT
41 };
42 
43 struct _CellRendererBreakIcon
44 {
45   GtkCellRenderer parent;
46 
47   guint enabled;
48   const gchar* condition;
49   guint hitscount;
50 
51   GdkPixbuf *pixbuf_enabled;
52   GdkPixbuf *pixbuf_disabled;
53   GdkPixbuf *pixbuf_conditional;
54   GdkPixbuf *pixbuf_file;
55 };
56 
57 struct _CellRendererBreakIconClass
58 {
59   GtkCellRendererClass parent_class;
60 
61   void (*clicked)(CellRendererBreakIcon *cell_renderer_toggle, const gchar *path);
62 };
63 
64 static gpointer parent_class;
65 static guint clicked_signal;
66 
67 /*
68  * property getter
69  */
cell_renderer_break_icon_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)70 static void cell_renderer_break_icon_get_property(GObject *object, guint param_id, GValue *value, GParamSpec *pspec)
71 {
72 	CellRendererBreakIcon *cellbreakpoint = CELL_RENDERER_BREAK_ICON(object);
73 	switch (param_id)
74 	{
75 		case PROP_PIXBUF_ENABLED:
76 			g_value_set_object (value, cellbreakpoint->pixbuf_enabled);
77 			break;
78 		case PROP_PIXBUF_DISABLED:
79 			g_value_set_object (value, cellbreakpoint->pixbuf_disabled);
80 			break;
81 		case PROP_PIXBUF_CONDITIONAL:
82 			  g_value_set_object (value, cellbreakpoint->pixbuf_conditional);
83 			  break;
84 		case PROP_PIXBUF_FILE:
85 			  g_value_set_object (value, cellbreakpoint->pixbuf_file);
86 			  break;
87 		case PROP_ENABLED:
88 			g_value_set_boolean(value, cellbreakpoint->enabled);
89 			break;
90 		case PROP_CONDITION:
91 			g_value_set_string(value, cellbreakpoint->condition);
92 			break;
93 		case PROP_HITSCOUNT:
94 			g_value_set_int(value, cellbreakpoint->hitscount);
95 			break;
96 		default:
97 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
98 			break;
99 	}
100 }
101 
102 /*
103  * property setter
104  */
cell_renderer_break_icon_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)105 static void cell_renderer_break_icon_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec)
106 {
107 	CellRendererBreakIcon *cellbreakpoint = CELL_RENDERER_BREAK_ICON(object);
108 	switch (param_id)
109 	{
110 		case PROP_PIXBUF_ENABLED:
111 			if (cellbreakpoint->pixbuf_enabled)
112 			{
113 				g_object_unref(cellbreakpoint->pixbuf_enabled);
114 			}
115 			cellbreakpoint->pixbuf_enabled = (GdkPixbuf*)g_value_dup_object(value);
116 			break;
117 		case PROP_PIXBUF_DISABLED:
118 			if (cellbreakpoint->pixbuf_disabled)
119 			{
120 				g_object_unref(cellbreakpoint->pixbuf_disabled);
121 			}
122 			cellbreakpoint->pixbuf_disabled = (GdkPixbuf*)g_value_dup_object(value);
123 			break;
124 		case PROP_PIXBUF_CONDITIONAL:
125 			if (cellbreakpoint->pixbuf_conditional)
126 			{
127 				g_object_unref(cellbreakpoint->pixbuf_conditional);
128 			}
129 			cellbreakpoint->pixbuf_conditional = (GdkPixbuf*)g_value_dup_object(value);
130 			break;
131 		case PROP_PIXBUF_FILE:
132 			if (cellbreakpoint->pixbuf_file)
133 			{
134 				g_object_unref(cellbreakpoint->pixbuf_file);
135 			}
136 			cellbreakpoint->pixbuf_file = (GdkPixbuf*)g_value_dup_object(value);
137 			break;
138 		case PROP_ENABLED:
139 			cellbreakpoint->enabled = g_value_get_boolean(value);
140 			break;
141 		case PROP_CONDITION:
142 			cellbreakpoint->condition = g_value_get_string(value);
143 			if (cellbreakpoint->condition)
144 			{
145 				cellbreakpoint->condition = g_strdup(cellbreakpoint->condition);
146 			}
147 			break;
148 		case PROP_HITSCOUNT:
149 			cellbreakpoint->hitscount = g_value_get_int(value);
150 			break;
151 		default:
152 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
153 			break;
154 	}
155 }
156 
157 /*
158  * get size of a cell
159  */
160 #if GTK_CHECK_VERSION(3, 0, 0)
cell_renderer_break_icon_get_size(GtkCellRenderer * cell,GtkWidget * widget,const GdkRectangle * cell_area,gint * x_offset,gint * y_offset,gint * width,gint * height)161 static void cell_renderer_break_icon_get_size(GtkCellRenderer *cell, GtkWidget *widget, const GdkRectangle *cell_area,
162 	gint *x_offset, gint *y_offset, gint *width, gint *height)
163 #else
164 static void cell_renderer_break_icon_get_size(GtkCellRenderer *cell, GtkWidget *widget, GdkRectangle *cell_area,
165 	gint *x_offset, gint *y_offset, gint *width, gint *height)
166 #endif
167 {
168 	CellRendererBreakIcon *cellbreakpoint = (CellRendererBreakIcon *) cell;
169 	gint pixbuf_width  = 0;
170 	gint pixbuf_height = 0;
171 	gint calc_width;
172 	gint calc_height;
173 	gint xpad;
174 	gint ypad;
175 	gfloat xalign;
176 	gfloat yalign;
177 
178 	if (cellbreakpoint->pixbuf_enabled)
179 	{
180 		pixbuf_width  = gdk_pixbuf_get_width (cellbreakpoint->pixbuf_enabled);
181 		pixbuf_height = gdk_pixbuf_get_height (cellbreakpoint->pixbuf_enabled);
182 	}
183 	if (cellbreakpoint->pixbuf_disabled)
184 	{
185 		pixbuf_width  = MAX (pixbuf_width, gdk_pixbuf_get_width (cellbreakpoint->pixbuf_disabled));
186 		pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (cellbreakpoint->pixbuf_disabled));
187 	}
188 	if (cellbreakpoint->pixbuf_conditional)
189 	{
190 		pixbuf_width  = MAX (pixbuf_width, gdk_pixbuf_get_width (cellbreakpoint->pixbuf_conditional));
191 		pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (cellbreakpoint->pixbuf_conditional));
192 	}
193 	if (cellbreakpoint->pixbuf_file)
194 	{
195 		pixbuf_width  = MAX (pixbuf_width, gdk_pixbuf_get_width (cellbreakpoint->pixbuf_file));
196 		pixbuf_height = MAX (pixbuf_height, gdk_pixbuf_get_height (cellbreakpoint->pixbuf_file));
197 	}
198 
199 	gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
200 	calc_width  = xpad * 2 + pixbuf_width;
201 	calc_height = ypad * 2 + pixbuf_height;
202 
203 	gtk_cell_renderer_get_alignment(cell, &xalign, &yalign);
204 
205 	if (cell_area && pixbuf_width > 0 && pixbuf_height > 0)
206 	{
207 		if (x_offset)
208 		{
209 			*x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
210 				(1.0 - xalign) : xalign) *
211 				(cell_area->width - calc_width));
212 			*x_offset = MAX (*x_offset, 0);
213 		}
214 		if (y_offset)
215 		{
216 			*y_offset = (yalign * (cell_area->height - calc_height));
217 			*y_offset = MAX (*y_offset, 0);
218 		}
219 	}
220 	else
221 	{
222 		if (x_offset) *x_offset = 0;
223 		if (y_offset) *y_offset = 0;
224 	}
225 
226 	if (width)
227 		*width = calc_width;
228 
229 	if (height)
230 		*height = calc_height;
231 }
232 
233 /*
234  * render a cell
235  */
236 #if GTK_CHECK_VERSION(3, 0, 0)
cell_renderer_break_icon_render(GtkCellRenderer * cell,cairo_t * cr,GtkWidget * widget,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)237 static void cell_renderer_break_icon_render(GtkCellRenderer *cell, cairo_t *cr, GtkWidget *widget,
238 	const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags)
239 #else
240 static void cell_renderer_break_icon_render(GtkCellRenderer *cell, GdkDrawable *window, GtkWidget *widget,
241 	GdkRectangle *background_area, GdkRectangle *cell_area, GdkRectangle *expose_area, GtkCellRendererState flags)
242 #endif
243 {
244 	CellRendererBreakIcon *cellbreakpoint = (CellRendererBreakIcon*) cell;
245 
246 	GdkPixbuf *pixbuf = NULL;
247 
248 	GdkRectangle pix_rect;
249 	GdkRectangle draw_rect;
250 
251 	gint xpad;
252 	gint ypad;
253 
254 	gboolean is_expander;
255 
256 #if !GTK_CHECK_VERSION(3, 0, 0)
257 	cairo_t *cr;
258 #endif
259 
260 	cell_renderer_break_icon_get_size (cell, widget, cell_area,
261 		&pix_rect.x,
262 		&pix_rect.y,
263 		&pix_rect.width,
264 		&pix_rect.height);
265 
266 	gtk_cell_renderer_get_padding(cell, &xpad, &ypad);
267 	pix_rect.x += cell_area->x + xpad;
268 	pix_rect.y += cell_area->y + ypad;
269 	pix_rect.width  -= xpad * 2;
270 	pix_rect.height -= ypad * 2;
271 
272 #if GTK_CHECK_VERSION(3, 0, 0)
273 	if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect))
274 #else
275 	if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect) ||
276 		!gdk_rectangle_intersect (expose_area, &draw_rect, &draw_rect))
277 #endif
278 		return;
279 
280 	g_object_get(cell, "is-expander", &is_expander, NULL);
281 
282 	if (is_expander)
283 	{
284 		pixbuf = cellbreakpoint->pixbuf_file;
285 	}
286 	else if (!cellbreakpoint->enabled)
287 	{
288 		pixbuf = cellbreakpoint->pixbuf_disabled;
289 	}
290 	else if ((cellbreakpoint->condition && strlen(cellbreakpoint->condition)) || cellbreakpoint->hitscount)
291 	{
292 		pixbuf = cellbreakpoint->pixbuf_conditional;
293 	}
294 	else
295 	{
296 		pixbuf = cellbreakpoint->pixbuf_enabled;
297 	}
298 
299 	if (!pixbuf)
300 		return;
301 
302 #if !GTK_CHECK_VERSION(3, 0, 0)
303 	cr = gdk_cairo_create (window);
304 #endif
305 
306 	gdk_cairo_set_source_pixbuf (cr, pixbuf, pix_rect.x, pix_rect.y);
307 	gdk_cairo_rectangle (cr, &draw_rect);
308 	cairo_fill (cr);
309 
310 #if !GTK_CHECK_VERSION(3, 0, 0)
311 	cairo_destroy (cr);
312 #endif
313 }
314 
315 /*
316  * activate callback
317  */
318 #if GTK_CHECK_VERSION(3, 0, 0)
cell_renderer_break_icon_activate(GtkCellRenderer * cell,GdkEvent * event,GtkWidget * widget,const gchar * path,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)319 static gint cell_renderer_break_icon_activate(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path,
320 	const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags)
321 #else
322 static gint cell_renderer_break_icon_activate(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path,
323 	GdkRectangle *background_area, GdkRectangle *cell_area, GtkCellRendererState  flags)
324 #endif
325 {
326 	if (!event ||
327 		(
328 			event->button.x >= cell_area->x &&
329 			event->button.x < (cell_area->x + cell_area->width)
330 		)
331 	)
332 	{
333 		g_signal_emit (cell, clicked_signal, 0, path);
334 	}
335 	return TRUE;
336 }
337 
338 /*
339  * init instance
340  */
cell_renderer_break_icon_init(CellRendererBreakIcon * cell)341 static void cell_renderer_break_icon_init (CellRendererBreakIcon *cell)
342 {
343 	GtkCellRenderer *cell_renderer = (GtkCellRenderer*)cell;
344 
345 	cell->enabled = TRUE;
346 	cell->condition = NULL;
347 	cell->hitscount = 0;
348 
349 	g_object_set(cell_renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
350 
351 	cell->pixbuf_enabled = cell->pixbuf_disabled = cell->pixbuf_conditional = cell->pixbuf_file = 0;
352 }
353 
354 /*
355  * finalizes an instance (frees pixbuffers)
356  */
cell_renderer_break_icon_finalize(GObject * object)357 static void cell_renderer_break_icon_finalize (GObject *object)
358 {
359 	CellRendererBreakIcon *cell = (CellRendererBreakIcon*)object;
360 	GdkPixbuf *pixbufs[4];
361 	int i;
362 
363 	pixbufs[0] = cell->pixbuf_enabled;
364 	pixbufs[1] = cell->pixbuf_disabled;
365 	pixbufs[2] = cell->pixbuf_conditional;
366 	pixbufs[3] = cell->pixbuf_file;
367 
368 	for(i = 0; i < 4; i++)
369 	{
370 		if (pixbufs[i])
371 		{
372 			g_object_unref(pixbufs[i]);
373 		}
374 	}
375 	if (cell->condition)
376 	{
377 		g_free((void*)cell->condition);
378 	}
379 
380 	(*G_OBJECT_CLASS (parent_class)->finalize) (object);
381 }
382 
383 /*
384  * init class
385  */
cell_renderer_break_icon_class_init(CellRendererBreakIconClass * class)386 static void cell_renderer_break_icon_class_init (CellRendererBreakIconClass *class)
387 {
388 	GObjectClass *object_class = G_OBJECT_CLASS(class);
389 	GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class);
390 
391 	parent_class = g_type_class_peek_parent(class);
392 
393 	object_class->get_property = cell_renderer_break_icon_get_property;
394 	object_class->set_property = cell_renderer_break_icon_set_property;
395 
396 	object_class->finalize = cell_renderer_break_icon_finalize;
397 
398 	cell_class->get_size = cell_renderer_break_icon_get_size;
399 	cell_class->render = cell_renderer_break_icon_render;
400 
401 	cell_class->activate = cell_renderer_break_icon_activate;
402 
403 	g_object_class_install_property (object_class,
404 		PROP_PIXBUF_ENABLED,
405 		g_param_spec_object (
406 			"pixbuf_enabled",
407 			"Pixbuf Object",
408 			"Enabled break image",
409 			GDK_TYPE_PIXBUF,
410 			G_PARAM_READWRITE)
411 	);
412 
413 	g_object_class_install_property (object_class,
414 		PROP_PIXBUF_DISABLED,
415 		g_param_spec_object (
416 			"pixbuf_disabled",
417 			"Pixbuf Object",
418 			"Disabled break image",
419 			GDK_TYPE_PIXBUF,
420 			G_PARAM_READWRITE)
421 	);
422 
423 	g_object_class_install_property (object_class,
424 		PROP_PIXBUF_CONDITIONAL,
425 		g_param_spec_object (
426 			"pixbuf_conditional",
427 			"Pixbuf Object",
428 			"Conditional break image",
429 			GDK_TYPE_PIXBUF,
430 			G_PARAM_READWRITE)
431 	);
432 
433 	g_object_class_install_property (object_class,
434 		PROP_PIXBUF_FILE,
435 		g_param_spec_object (
436 			"pixbuf_file",
437 			"Pixbuf Object",
438 			"File image",
439 			GDK_TYPE_PIXBUF,
440 			G_PARAM_READWRITE)
441 	);
442 
443 	g_object_class_install_property (object_class,
444 		PROP_ENABLED,
445 		g_param_spec_boolean ("enabled", "Activeness", "The active state of the breakpoint", FALSE, G_PARAM_READWRITE)
446 	);
447 
448 	g_object_class_install_property (object_class,
449 		PROP_CONDITION,
450 		g_param_spec_string ("condition", "Breakpoint condition", "Whether a brealpoint has a condition", FALSE, G_PARAM_READWRITE)
451 	);
452 
453 	g_object_class_install_property (object_class,
454 		PROP_HITSCOUNT,
455 		g_param_spec_int (
456 			"hitscount",
457 			"Breakpoint hitscount",
458 			"Number of passes to wait until stop at a breakpoint",
459 			G_MININT,
460 			G_MAXINT,
461 			0,
462 			G_PARAM_READWRITE
463 		)
464 	);
465 
466 	clicked_signal = g_signal_new ("clicked",
467 		G_OBJECT_CLASS_TYPE (object_class),
468 		G_SIGNAL_RUN_LAST,
469 		G_STRUCT_OFFSET (CellRendererBreakIconClass, clicked),
470 		NULL, NULL,
471 		g_cclosure_marshal_VOID__STRING,
472 		G_TYPE_NONE, 1,
473 		G_TYPE_STRING);
474 }
475 
476 /*
477  * registers a type
478  */
cell_renderer_break_icon_get_type(void)479 GType cell_renderer_break_icon_get_type(void)
480 {
481 	static GType cell_break_icon_type = 0;
482 
483 	if(0 == cell_break_icon_type)
484 	{
485 		if ( (cell_break_icon_type = g_type_from_name("CellRendererBreakIcon")) )
486 		{
487 			parent_class = g_type_class_peek_static(g_type_parent(cell_break_icon_type));
488 			clicked_signal = g_signal_lookup("clicked", cell_break_icon_type);
489 		}
490 		else
491 		{
492 			static const GTypeInfo cell_break_icon_info =
493 			{
494 				sizeof (CellRendererBreakIconClass),
495 				NULL,                                                     /* base_init */
496 				NULL,                                                     /* base_finalize */
497 				(GClassInitFunc) cell_renderer_break_icon_class_init,
498 				NULL,                                                     /* class_finalize */
499 				NULL,                                                     /* class_data */
500 				sizeof (CellRendererBreakIcon),
501 				0,                                                        /* n_preallocs */
502 				(GInstanceInitFunc) cell_renderer_break_icon_init,
503 			};
504 
505 			/* Derive from GtkCellRenderer */
506 			cell_break_icon_type = g_type_register_static (GTK_TYPE_CELL_RENDERER,
507 				"CellRendererBreakIcon",
508 				&cell_break_icon_info,
509 				0);
510 		}
511 	}
512 
513 	return cell_break_icon_type;
514 }
515 
516 /*
517  * creates new renderer
518  */
cell_renderer_break_icon_new(void)519 GtkCellRenderer* cell_renderer_break_icon_new(void)
520 {
521   return g_object_new(TYPE_CELL_RENDERER_BREAK_ICON, NULL);
522 }
523