1 /*
2  * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
3  * All rights reserved.
4  *
5  * This file is part of the Gnome Library.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * published by the Free Software Foundation; either the
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
18  */
19 /*
20   @NOTATION@
21  */
22 /* Widget item type for GnomeCanvas widget
23  *
24  * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas
25  * widget.  Tk is copyrighted by the Regents of the University of California,
26  * Sun Microsystems, and other parties.
27  *
28  *
29  * Author: Federico Mena <federico@nuclecu.unam.mx>
30  */
31 
32 #include "evolution-config.h"
33 
34 #include <math.h>
35 #include "gnome-canvas-widget.h"
36 
37 enum {
38 	PROP_0,
39 	PROP_WIDGET,
40 	PROP_X,
41 	PROP_Y,
42 	PROP_WIDTH,
43 	PROP_HEIGHT,
44 	PROP_SIZE_PIXELS
45 };
46 
47 static void gnome_canvas_widget_dispose    (GnomeCanvasItem      *object);
48 static void gnome_canvas_widget_get_property (GObject            *object,
49 					      guint               param_id,
50 					      GValue             *value,
51 					      GParamSpec         *pspec);
52 static void gnome_canvas_widget_set_property (GObject            *object,
53 					      guint               param_id,
54 					      const GValue       *value,
55 					      GParamSpec         *pspec);
56 
57 static void	gnome_canvas_widget_update	(GnomeCanvasItem *item,
58 						 const cairo_matrix_t *matrix,
59 						 gint flags);
60 static GnomeCanvasItem *gnome_canvas_widget_point (GnomeCanvasItem *item,
61 						 gdouble x,
62 						 gdouble y,
63 						 gint cx,
64 						 gint cy);
65 static void	gnome_canvas_widget_bounds	(GnomeCanvasItem *item,
66 						 gdouble *x1,
67 						 gdouble *y1,
68 						 gdouble *x2,
69 						 gdouble *y2);
70 
71 static void	gnome_canvas_widget_draw	(GnomeCanvasItem *item,
72 						 cairo_t *cr,
73 						 gint x,
74 						 gint y,
75 						 gint width,
76 						 gint height);
77 
G_DEFINE_TYPE(GnomeCanvasWidget,gnome_canvas_widget,GNOME_TYPE_CANVAS_ITEM)78 G_DEFINE_TYPE (
79 	GnomeCanvasWidget,
80 	gnome_canvas_widget,
81 	GNOME_TYPE_CANVAS_ITEM)
82 
83 static void
84 gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class)
85 {
86 	GObjectClass *gobject_class;
87 	GnomeCanvasItemClass *item_class;
88 
89 	gobject_class = (GObjectClass *) class;
90 	item_class = (GnomeCanvasItemClass *) class;
91 
92 	gobject_class->set_property = gnome_canvas_widget_set_property;
93 	gobject_class->get_property = gnome_canvas_widget_get_property;
94 
95 	g_object_class_install_property
96 		(gobject_class,
97 		 PROP_WIDGET,
98 		 g_param_spec_object ("widget", NULL, NULL,
99 				      GTK_TYPE_WIDGET,
100 				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
101 	g_object_class_install_property
102 		(gobject_class,
103 		 PROP_X,
104 		 g_param_spec_double ("x", NULL, NULL,
105 				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
106 				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
107 	g_object_class_install_property
108 		(gobject_class,
109 		 PROP_Y,
110 		 g_param_spec_double ("y", NULL, NULL,
111 				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
112 				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
113 	g_object_class_install_property
114 		(gobject_class,
115 		 PROP_WIDTH,
116 		 g_param_spec_double ("width", NULL, NULL,
117 				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
118 				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
119 	g_object_class_install_property
120 		(gobject_class,
121 		 PROP_HEIGHT,
122 		 g_param_spec_double ("height", NULL, NULL,
123 				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
124 				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
125 	g_object_class_install_property
126 		(gobject_class,
127 		 PROP_SIZE_PIXELS,
128 		 g_param_spec_boolean ("size_pixels", NULL, NULL,
129 				       FALSE,
130 				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
131 
132 	item_class->dispose = gnome_canvas_widget_dispose;
133 	item_class->update = gnome_canvas_widget_update;
134 	item_class->point = gnome_canvas_widget_point;
135 	item_class->bounds = gnome_canvas_widget_bounds;
136 	item_class->draw = gnome_canvas_widget_draw;
137 }
138 
139 static void
do_destroy(gpointer data,GObject * gone_object)140 do_destroy (gpointer data,
141             GObject *gone_object)
142 {
143 	GnomeCanvasWidget *witem;
144 
145 	witem = data;
146 
147 	if (!witem->in_destroy) {
148 		witem->in_destroy = TRUE;
149 		g_object_run_dispose (G_OBJECT (witem));
150 	}
151 }
152 
153 static void
gnome_canvas_widget_init(GnomeCanvasWidget * witem)154 gnome_canvas_widget_init (GnomeCanvasWidget *witem)
155 {
156 	witem->x = 0.0;
157 	witem->y = 0.0;
158 	witem->width = 0.0;
159 	witem->height = 0.0;
160 	witem->size_pixels = FALSE;
161 }
162 
163 static void
gnome_canvas_widget_dispose(GnomeCanvasItem * object)164 gnome_canvas_widget_dispose (GnomeCanvasItem *object)
165 {
166 	GnomeCanvasWidget *witem;
167 
168 	g_return_if_fail (object != NULL);
169 	g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
170 
171 	witem = GNOME_CANVAS_WIDGET (object);
172 
173 	if (witem->widget && !witem->in_destroy) {
174 		g_object_weak_unref (G_OBJECT (witem->widget), do_destroy, witem);
175 		gtk_widget_destroy (witem->widget);
176 		witem->widget = NULL;
177 	}
178 
179 	GNOME_CANVAS_ITEM_CLASS (gnome_canvas_widget_parent_class)->
180 		dispose (object);
181 }
182 
183 static gboolean
reposition_widget_cb(gpointer user_data)184 reposition_widget_cb (gpointer user_data)
185 {
186 	GnomeCanvasWidget *witem = user_data;
187 
188 	g_return_val_if_fail (GNOME_IS_CANVAS_WIDGET (witem), FALSE);
189 
190 	if (witem->widget)
191 		gtk_widget_queue_resize (witem->widget);
192 
193 	return FALSE;
194 }
195 
196 static void
recalc_bounds(GnomeCanvasWidget * witem)197 recalc_bounds (GnomeCanvasWidget *witem)
198 {
199 	GnomeCanvasItem *item;
200 	gdouble wx, wy;
201 
202 	item = GNOME_CANVAS_ITEM (witem);
203 
204 	/* Get world coordinates */
205 
206 	wx = witem->x;
207 	wy = witem->y;
208 	gnome_canvas_item_i2w (item, &wx, &wy);
209 
210 	/* Get canvas pixel coordinates */
211 
212 	gnome_canvas_w2c (item->canvas, wx, wy, &witem->cx, &witem->cy);
213 
214 	/* Bounds */
215 
216 	item->x1 = witem->cx;
217 	item->y1 = witem->cy;
218 	item->x2 = witem->cx + witem->cwidth;
219 	item->y2 = witem->cy + witem->cheight;
220 
221 	if (witem->widget) {
222 		gint current_x = 0, current_y = 0;
223 
224 		gtk_container_child_get (GTK_CONTAINER (item->canvas), witem->widget,
225 			"x", &current_x,
226 			"y", &current_y,
227 			NULL);
228 
229 		if (current_x != ((gint) (witem->cx + item->canvas->zoom_xofs)) ||
230 		    current_y != ((gint) (witem->cy + item->canvas->zoom_yofs))) {
231 			gtk_layout_move (
232 				GTK_LAYOUT (item->canvas), witem->widget,
233 				witem->cx + item->canvas->zoom_xofs,
234 				witem->cy + item->canvas->zoom_yofs);
235 
236 			/* This is needed, because the gtk_layout_move() calls gtk_widget_queue_resize(),
237 			   which can be silently ignored when called inside "size-allocate" handler, causing
238 			   misposition of the child widget. */
239 			g_idle_add_full (G_PRIORITY_HIGH_IDLE,
240 				reposition_widget_cb, g_object_ref (witem), g_object_unref);
241 		}
242 	}
243 }
244 
245 static void
gnome_canvas_widget_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)246 gnome_canvas_widget_set_property (GObject *object,
247                                   guint param_id,
248                                   const GValue *value,
249                                   GParamSpec *pspec)
250 {
251 	GnomeCanvasItem *item;
252 	GnomeCanvasWidget *witem;
253 	GObject *obj;
254 	gint update;
255 	gint calc_bounds;
256 
257 	g_return_if_fail (object != NULL);
258 	g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
259 
260 	item = GNOME_CANVAS_ITEM (object);
261 	witem = GNOME_CANVAS_WIDGET (object);
262 
263 	update = FALSE;
264 	calc_bounds = FALSE;
265 
266 	switch (param_id) {
267 	case PROP_WIDGET:
268 		if (witem->widget) {
269 			g_object_weak_unref (G_OBJECT (witem->widget), do_destroy, witem);
270 			gtk_container_remove (GTK_CONTAINER (item->canvas), witem->widget);
271 		}
272 
273 		obj = g_value_get_object (value);
274 		if (obj) {
275 			witem->widget = GTK_WIDGET (obj);
276 			g_object_weak_ref (obj, do_destroy, witem);
277 			gtk_layout_put (
278 				GTK_LAYOUT (item->canvas), witem->widget,
279 				witem->cx + item->canvas->zoom_xofs,
280 				witem->cy + item->canvas->zoom_yofs);
281 		}
282 
283 		update = TRUE;
284 		break;
285 
286 	case PROP_X:
287 		if (witem->x != g_value_get_double (value))
288 		{
289 			witem->x = g_value_get_double (value);
290 			calc_bounds = TRUE;
291 		}
292 		break;
293 
294 	case PROP_Y:
295 		if (witem->y != g_value_get_double (value))
296 		{
297 			witem->y = g_value_get_double (value);
298 			calc_bounds = TRUE;
299 		}
300 		break;
301 
302 	case PROP_WIDTH:
303 		if (witem->width != fabs (g_value_get_double (value)))
304 		{
305 			witem->width = fabs (g_value_get_double (value));
306 			update = TRUE;
307 		}
308 		break;
309 
310 	case PROP_HEIGHT:
311 		if (witem->height != fabs (g_value_get_double (value)))
312 		{
313 			witem->height = fabs (g_value_get_double (value));
314 			update = TRUE;
315 		}
316 		break;
317 
318 	case PROP_SIZE_PIXELS:
319 		if (witem->size_pixels != g_value_get_boolean (value))
320 		{
321 			witem->size_pixels = g_value_get_boolean (value);
322 			update = TRUE;
323 		}
324 		break;
325 
326 	default:
327 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
328 		break;
329 	}
330 
331 	if (update)
332 		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->update) (item, NULL, 0);
333 
334 	if (calc_bounds)
335 		recalc_bounds (witem);
336 }
337 
338 static void
gnome_canvas_widget_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)339 gnome_canvas_widget_get_property (GObject *object,
340                                   guint param_id,
341                                   GValue *value,
342                                   GParamSpec *pspec)
343 {
344 	GnomeCanvasWidget *witem;
345 
346 	g_return_if_fail (object != NULL);
347 	g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
348 
349 	witem = GNOME_CANVAS_WIDGET (object);
350 
351 	switch (param_id) {
352 	case PROP_WIDGET:
353 		g_value_set_object (value, (GObject *) witem->widget);
354 		break;
355 
356 	case PROP_X:
357 		g_value_set_double (value, witem->x);
358 		break;
359 
360 	case PROP_Y:
361 		g_value_set_double (value, witem->y);
362 		break;
363 
364 	case PROP_WIDTH:
365 		g_value_set_double (value, witem->width);
366 		break;
367 
368 	case PROP_HEIGHT:
369 		g_value_set_double (value, witem->height);
370 		break;
371 
372 	case PROP_SIZE_PIXELS:
373 		g_value_set_boolean (value, witem->size_pixels);
374 		break;
375 
376 	default:
377 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
378 		break;
379 	}
380 }
381 
382 static void
gnome_canvas_widget_update(GnomeCanvasItem * item,const cairo_matrix_t * matrix,gint flags)383 gnome_canvas_widget_update (GnomeCanvasItem *item,
384                             const cairo_matrix_t *matrix,
385                             gint flags)
386 {
387 	GnomeCanvasWidget *witem;
388 
389 	witem = GNOME_CANVAS_WIDGET (item);
390 
391 	GNOME_CANVAS_ITEM_CLASS (gnome_canvas_widget_parent_class)->
392 		update (item, matrix, flags);
393 
394 	if (witem->widget) {
395 		witem->cwidth = (gint) (witem->width + 0.5);
396 		witem->cheight = (gint) (witem->height + 0.5);
397 
398 		gtk_widget_set_size_request (witem->widget, witem->cwidth, witem->cheight);
399 	} else {
400 		witem->cwidth = 0.0;
401 		witem->cheight = 0.0;
402 	}
403 
404 	recalc_bounds (witem);
405 }
406 
407 static void
gnome_canvas_widget_draw(GnomeCanvasItem * item,cairo_t * cr,gint x,gint y,gint width,gint height)408 gnome_canvas_widget_draw (GnomeCanvasItem *item,
409                           cairo_t *cr,
410                           gint x,
411                           gint y,
412                           gint width,
413                           gint height)
414 {
415 #if 0
416 	GnomeCanvasWidget *witem;
417 
418 	witem = GNOME_CANVAS_WIDGET (item);
419 
420 	if (witem->widget)
421 		gtk_widget_queue_draw (witem->widget);
422 #endif
423 }
424 
425 static GnomeCanvasItem *
gnome_canvas_widget_point(GnomeCanvasItem * item,gdouble x,gdouble y,gint cx,gint cy)426 gnome_canvas_widget_point (GnomeCanvasItem *item,
427                            gdouble x,
428                            gdouble y,
429                            gint cx,
430                            gint cy)
431 {
432 	GnomeCanvasWidget *witem;
433 	gdouble x1, y1, x2, y2;
434 
435 	witem = GNOME_CANVAS_WIDGET (item);
436 
437 	gnome_canvas_c2w (item->canvas, witem->cx, witem->cy, &x1, &y1);
438 
439 	x2 = x1 + (witem->cwidth - 1);
440 	y2 = y1 + (witem->cheight - 1);
441 
442 	/* Is point inside widget bounds? */
443 
444 	if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2))
445 		return item;
446 
447 	/* Point is outside widget bounds */
448 	return NULL;
449 }
450 
451 static void
gnome_canvas_widget_bounds(GnomeCanvasItem * item,gdouble * x1,gdouble * y1,gdouble * x2,gdouble * y2)452 gnome_canvas_widget_bounds (GnomeCanvasItem *item,
453                             gdouble *x1,
454                             gdouble *y1,
455                             gdouble *x2,
456                             gdouble *y2)
457 {
458 	GnomeCanvasWidget *witem;
459 
460 	witem = GNOME_CANVAS_WIDGET (item);
461 
462 	*x1 = witem->x;
463 	*y1 = witem->y;
464 
465 	*x2 = *x1 + witem->width;
466 	*y2 = *y1 + witem->height;
467 }
468