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