merge gnomecanvas into ardour tree, so that we can fix our own bugs and not wait...
[ardour.git] / libs / gnomecanvas / libgnomecanvas / gnome-canvas-widget.c
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  * The Gnome Library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * The Gnome Library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 /*
23   @NOTATION@
24  */
25 /* Widget item type for GnomeCanvas widget
26  *
27  * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
28  * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
29  *
30  *
31  * Author: Federico Mena <federico@nuclecu.unam.mx>
32  */
33
34 #include <config.h>
35 #include <math.h>
36 #include <gtk/gtksignal.h>
37 #include "gnome-canvas-widget.h"
38
39 enum {
40         PROP_0,
41         PROP_WIDGET,
42         PROP_X,
43         PROP_Y,
44         PROP_WIDTH,
45         PROP_HEIGHT,
46         PROP_ANCHOR,
47         PROP_SIZE_PIXELS
48 };
49
50
51 static void gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class);
52 static void gnome_canvas_widget_init       (GnomeCanvasWidget      *witem);
53 static void gnome_canvas_widget_destroy    (GtkObject              *object);
54 static void gnome_canvas_widget_get_property (GObject            *object,
55                                               guint               param_id,
56                                               GValue             *value,
57                                               GParamSpec         *pspec);
58 static void gnome_canvas_widget_set_property (GObject            *object,
59                                               guint               param_id,
60                                               const GValue       *value,
61                                               GParamSpec         *pspec);
62
63 static void   gnome_canvas_widget_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
64 static double gnome_canvas_widget_point       (GnomeCanvasItem *item, double x, double y,
65                                                int cx, int cy, GnomeCanvasItem **actual_item);
66 static void   gnome_canvas_widget_bounds      (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
67
68 static void gnome_canvas_widget_render (GnomeCanvasItem *item,
69                                         GnomeCanvasBuf *buf);
70 static void gnome_canvas_widget_draw (GnomeCanvasItem *item,
71                                       GdkDrawable *drawable,
72                                       int x, int y,
73                                       int width, int height);
74
75 static GnomeCanvasItemClass *parent_class;
76
77
78 GType
79 gnome_canvas_widget_get_type (void)
80 {
81         static GType widget_type;
82
83         if (!widget_type) {
84                 const GTypeInfo object_info = {
85                         sizeof (GnomeCanvasWidgetClass),
86                         (GBaseInitFunc) NULL,
87                         (GBaseFinalizeFunc) NULL,
88                         (GClassInitFunc) gnome_canvas_widget_class_init,
89                         (GClassFinalizeFunc) NULL,
90                         NULL,                   /* class_data */
91                         sizeof (GnomeCanvasWidget),
92                         0,                      /* n_preallocs */
93                         (GInstanceInitFunc) gnome_canvas_widget_init,
94                         NULL                    /* value_table */
95                 };
96
97                 widget_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWidget",
98                                                       &object_info, 0);
99         }
100
101         return widget_type;
102 }
103
104 static void
105 gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class)
106 {
107         GObjectClass *gobject_class;
108         GtkObjectClass *object_class;
109         GnomeCanvasItemClass *item_class;
110
111         gobject_class = (GObjectClass *) class;
112         object_class = (GtkObjectClass *) class;
113         item_class = (GnomeCanvasItemClass *) class;
114
115         parent_class = g_type_class_peek_parent (class);
116
117         gobject_class->set_property = gnome_canvas_widget_set_property;
118         gobject_class->get_property = gnome_canvas_widget_get_property;
119
120         g_object_class_install_property
121                 (gobject_class,
122                  PROP_WIDGET,
123                  g_param_spec_object ("widget", NULL, NULL,
124                                       GTK_TYPE_WIDGET,
125                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
126         g_object_class_install_property
127                 (gobject_class,
128                  PROP_X,
129                  g_param_spec_double ("x", NULL, NULL,
130                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
131                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
132         g_object_class_install_property
133                 (gobject_class,
134                  PROP_Y,
135                  g_param_spec_double ("y", NULL, NULL,
136                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
137                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
138         g_object_class_install_property
139                 (gobject_class,
140                  PROP_WIDTH,
141                  g_param_spec_double ("width", NULL, NULL,
142                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
143                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
144         g_object_class_install_property
145                 (gobject_class,
146                  PROP_HEIGHT,
147                  g_param_spec_double ("height", NULL, NULL,
148                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
149                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
150         g_object_class_install_property
151                 (gobject_class,
152                  PROP_ANCHOR,
153                  g_param_spec_enum ("anchor", NULL, NULL,
154                                     GTK_TYPE_ANCHOR_TYPE,
155                                     GTK_ANCHOR_NW,
156                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
157         g_object_class_install_property
158                 (gobject_class,
159                  PROP_SIZE_PIXELS,
160                  g_param_spec_boolean ("size_pixels", NULL, NULL,
161                                        FALSE,
162                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
163
164         object_class->destroy = gnome_canvas_widget_destroy;
165
166         item_class->update = gnome_canvas_widget_update;
167         item_class->point = gnome_canvas_widget_point;
168         item_class->bounds = gnome_canvas_widget_bounds;
169         item_class->render = gnome_canvas_widget_render;
170         item_class->draw = gnome_canvas_widget_draw;
171 }
172
173 static void
174 gnome_canvas_widget_init (GnomeCanvasWidget *witem)
175 {
176         witem->x = 0.0;
177         witem->y = 0.0;
178         witem->width = 0.0;
179         witem->height = 0.0;
180         witem->anchor = GTK_ANCHOR_NW;
181         witem->size_pixels = FALSE;
182 }
183
184 static void
185 gnome_canvas_widget_destroy (GtkObject *object)
186 {
187         GnomeCanvasWidget *witem;
188
189         g_return_if_fail (object != NULL);
190         g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
191
192         witem = GNOME_CANVAS_WIDGET (object);
193
194         if (witem->widget && !witem->in_destroy) {
195                 g_signal_handler_disconnect (witem->widget, witem->destroy_id);
196                 gtk_widget_destroy (witem->widget);
197                 witem->widget = NULL;
198         }
199
200         if (GTK_OBJECT_CLASS (parent_class)->destroy)
201                 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
202 }
203
204 static void
205 recalc_bounds (GnomeCanvasWidget *witem)
206 {
207         GnomeCanvasItem *item;
208         double wx, wy;
209
210         item = GNOME_CANVAS_ITEM (witem);
211
212         /* Get world coordinates */
213
214         wx = witem->x;
215         wy = witem->y;
216         gnome_canvas_item_i2w (item, &wx, &wy);
217
218         /* Get canvas pixel coordinates */
219
220         gnome_canvas_w2c (item->canvas, wx, wy, &witem->cx, &witem->cy);
221
222         /* Anchor widget item */
223
224         switch (witem->anchor) {
225         case GTK_ANCHOR_NW:
226         case GTK_ANCHOR_W:
227         case GTK_ANCHOR_SW:
228                 break;
229
230         case GTK_ANCHOR_N:
231         case GTK_ANCHOR_CENTER:
232         case GTK_ANCHOR_S:
233                 witem->cx -= witem->cwidth / 2;
234                 break;
235
236         case GTK_ANCHOR_NE:
237         case GTK_ANCHOR_E:
238         case GTK_ANCHOR_SE:
239                 witem->cx -= witem->cwidth;
240                 break;
241
242         default:
243                 break;
244         }
245
246         switch (witem->anchor) {
247         case GTK_ANCHOR_NW:
248         case GTK_ANCHOR_N:
249         case GTK_ANCHOR_NE:
250                 break;
251
252         case GTK_ANCHOR_W:
253         case GTK_ANCHOR_CENTER:
254         case GTK_ANCHOR_E:
255                 witem->cy -= witem->cheight / 2;
256                 break;
257
258         case GTK_ANCHOR_SW:
259         case GTK_ANCHOR_S:
260         case GTK_ANCHOR_SE:
261                 witem->cy -= witem->cheight;
262                 break;
263
264         default:
265                 break;
266         }
267
268         /* Bounds */
269
270         item->x1 = witem->cx;
271         item->y1 = witem->cy;
272         item->x2 = witem->cx + witem->cwidth;
273         item->y2 = witem->cy + witem->cheight;
274
275         if (witem->widget)
276                 gtk_layout_move (GTK_LAYOUT (item->canvas), witem->widget,
277                                  witem->cx + item->canvas->zoom_xofs,
278                                  witem->cy + item->canvas->zoom_yofs);
279 }
280
281 static void
282 do_destroy (GtkObject *object, gpointer data)
283 {
284         GnomeCanvasWidget *witem;
285
286         witem = data;
287
288         witem->in_destroy = TRUE;
289
290         gtk_object_destroy (data);
291 }
292
293 static void
294 gnome_canvas_widget_set_property (GObject            *object,
295                                   guint               param_id,
296                                   const GValue       *value,
297                                   GParamSpec         *pspec)
298 {
299         GnomeCanvasItem *item;
300         GnomeCanvasWidget *witem;
301         GObject *obj;
302         int update;
303         int calc_bounds;
304
305         g_return_if_fail (object != NULL);
306         g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
307
308         item = GNOME_CANVAS_ITEM (object);
309         witem = GNOME_CANVAS_WIDGET (object);
310
311         update = FALSE;
312         calc_bounds = FALSE;
313
314         switch (param_id) {
315         case PROP_WIDGET:
316                 if (witem->widget) {
317                         g_signal_handler_disconnect (witem->widget, witem->destroy_id);
318                         gtk_container_remove (GTK_CONTAINER (item->canvas), witem->widget);
319                 }
320
321                 obj = g_value_get_object (value);
322                 if (obj) {
323                         witem->widget = GTK_WIDGET (obj);
324                         witem->destroy_id = g_signal_connect (obj, "destroy",
325                                                               G_CALLBACK (do_destroy),
326                                                               witem);
327                         gtk_layout_put (GTK_LAYOUT (item->canvas), witem->widget,
328                                         witem->cx + item->canvas->zoom_xofs,
329                                         witem->cy + item->canvas->zoom_yofs);
330                 }
331
332                 update = TRUE;
333                 break;
334
335         case PROP_X:
336                 if (witem->x != g_value_get_double (value))
337                 {
338                         witem->x = g_value_get_double (value);
339                         calc_bounds = TRUE;
340                 }
341                 break;
342
343         case PROP_Y:
344                 if (witem->y != g_value_get_double (value))
345                 {
346                         witem->y = g_value_get_double (value);
347                         calc_bounds = TRUE;
348                 }
349                 break;
350
351         case PROP_WIDTH:
352                 if (witem->width != fabs (g_value_get_double (value)))
353                 {
354                         witem->width = fabs (g_value_get_double (value));
355                         update = TRUE;
356                 }
357                 break;
358
359         case PROP_HEIGHT:
360                 if (witem->height != fabs (g_value_get_double (value)))
361                 {
362                         witem->height = fabs (g_value_get_double (value));
363                         update = TRUE;
364                 }
365                 break;
366
367         case PROP_ANCHOR:
368                 if (witem->anchor != g_value_get_enum (value))
369                 {
370                         witem->anchor = g_value_get_enum (value);
371                         update = TRUE;
372                 }
373                 break;
374
375         case PROP_SIZE_PIXELS:
376                 if (witem->size_pixels != g_value_get_boolean (value))
377                 {
378                         witem->size_pixels = g_value_get_boolean (value);
379                         update = TRUE;
380                 }
381                 break;
382
383         default:
384                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
385                 break;
386         }
387
388         if (update)
389                 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->update) (item, NULL, NULL, 0);
390
391         if (calc_bounds)
392                 recalc_bounds (witem);
393 }
394
395 static void
396 gnome_canvas_widget_get_property (GObject            *object,
397                                   guint               param_id,
398                                   GValue             *value,
399                                   GParamSpec         *pspec)
400 {
401         GnomeCanvasWidget *witem;
402
403         g_return_if_fail (object != NULL);
404         g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
405
406         witem = GNOME_CANVAS_WIDGET (object);
407
408         switch (param_id) {
409         case PROP_WIDGET:
410                 g_value_set_object (value, (GObject *) witem->widget);
411                 break;
412
413         case PROP_X:
414                 g_value_set_double (value, witem->x);
415                 break;
416
417         case PROP_Y:
418                 g_value_set_double (value, witem->y);
419                 break;
420
421         case PROP_WIDTH:
422                 g_value_set_double (value, witem->width);
423                 break;
424
425         case PROP_HEIGHT:
426                 g_value_set_double (value, witem->height);
427                 break;
428
429         case PROP_ANCHOR:
430                 g_value_set_enum (value, witem->anchor);
431                 break;
432
433         case PROP_SIZE_PIXELS:
434                 g_value_set_boolean (value, witem->size_pixels);
435                 break;
436
437         default:
438                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
439                 break;
440         }
441 }
442
443 static void
444 gnome_canvas_widget_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
445 {
446         GnomeCanvasWidget *witem;
447
448         witem = GNOME_CANVAS_WIDGET (item);
449
450         if (parent_class->update)
451                 (* parent_class->update) (item, affine, clip_path, flags);
452
453         if (witem->widget) {
454                 if (witem->size_pixels) {
455                         witem->cwidth = (int) (witem->width + 0.5);
456                         witem->cheight = (int) (witem->height + 0.5);
457                 } else {
458                         witem->cwidth = (int) (witem->width * item->canvas->pixels_per_unit + 0.5);
459                         witem->cheight = (int) (witem->height * item->canvas->pixels_per_unit + 0.5);
460                 }
461
462                 gtk_widget_set_size_request (witem->widget, witem->cwidth, witem->cheight);
463         } else {
464                 witem->cwidth = 0.0;
465                 witem->cheight = 0.0;
466         }
467
468         recalc_bounds (witem);
469 }
470
471 static void
472 gnome_canvas_widget_render (GnomeCanvasItem *item,
473                             GnomeCanvasBuf *buf)
474 {
475 #if 0
476         GnomeCanvasWidget *witem;
477
478         witem = GNOME_CANVAS_WIDGET (item);
479
480         if (witem->widget) 
481                 gtk_widget_queue_draw (witem->widget);
482 #endif
483
484 }
485
486 static void
487 gnome_canvas_widget_draw (GnomeCanvasItem *item,
488                           GdkDrawable *drawable,
489                           int x, int y,
490                           int width, int height)
491 {
492 #if 0
493         GnomeCanvasWidget *witem;
494
495         witem = GNOME_CANVAS_WIDGET (item);
496
497         if (witem->widget)
498                 gtk_widget_queue_draw (witem->widget);
499 #endif
500 }
501
502 static double
503 gnome_canvas_widget_point (GnomeCanvasItem *item, double x, double y,
504                            int cx, int cy, GnomeCanvasItem **actual_item)
505 {
506         GnomeCanvasWidget *witem;
507         double x1, y1, x2, y2;
508         double dx, dy;
509
510         witem = GNOME_CANVAS_WIDGET (item);
511
512         *actual_item = item;
513
514         gnome_canvas_c2w (item->canvas, witem->cx, witem->cy, &x1, &y1);
515
516         x2 = x1 + (witem->cwidth - 1) / item->canvas->pixels_per_unit;
517         y2 = y1 + (witem->cheight - 1) / item->canvas->pixels_per_unit;
518
519         /* Is point inside widget bounds? */
520
521         if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2))
522                 return 0.0;
523
524         /* Point is outside widget bounds */
525
526         if (x < x1)
527                 dx = x1 - x;
528         else if (x > x2)
529                 dx = x - x2;
530         else
531                 dx = 0.0;
532
533         if (y < y1)
534                 dy = y1 - y;
535         else if (y > y2)
536                 dy = y - y2;
537         else
538                 dy = 0.0;
539
540         return sqrt (dx * dx + dy * dy);
541 }
542
543 static void
544 gnome_canvas_widget_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
545 {
546         GnomeCanvasWidget *witem;
547
548         witem = GNOME_CANVAS_WIDGET (item);
549
550         *x1 = witem->x;
551         *y1 = witem->y;
552
553         switch (witem->anchor) {
554         case GTK_ANCHOR_NW:
555         case GTK_ANCHOR_W:
556         case GTK_ANCHOR_SW:
557                 break;
558
559         case GTK_ANCHOR_N:
560         case GTK_ANCHOR_CENTER:
561         case GTK_ANCHOR_S:
562                 *x1 -= witem->width / 2.0;
563                 break;
564
565         case GTK_ANCHOR_NE:
566         case GTK_ANCHOR_E:
567         case GTK_ANCHOR_SE:
568                 *x1 -= witem->width;
569                 break;
570
571         default:
572                 break;
573         }
574
575         switch (witem->anchor) {
576         case GTK_ANCHOR_NW:
577         case GTK_ANCHOR_N:
578         case GTK_ANCHOR_NE:
579                 break;
580
581         case GTK_ANCHOR_W:
582         case GTK_ANCHOR_CENTER:
583         case GTK_ANCHOR_E:
584                 *y1 -= witem->height / 2.0;
585                 break;
586
587         case GTK_ANCHOR_SW:
588         case GTK_ANCHOR_S:
589         case GTK_ANCHOR_SE:
590                 *y1 -= witem->height;
591                 break;
592
593         default:
594                 break;
595         }
596
597         *x2 = *x1 + witem->width;
598         *y2 = *y1 + witem->height;
599 }