Contents of /trunk/src/canvas_goocanvas.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 309 - (show annotations)
Sat Oct 17 18:55:03 2009 UTC (14 years, 7 months ago) by harbaum
File MIME type: text/plain
File size: 14724 byte(s)
Applied Rolfs patches
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go 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 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSM2Go 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 OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifndef USE_GOOCANVAS
21 #error "Config error!"
22 #endif
23
24 #include "appdata.h"
25
26 /* ------------------- creating and destroying the canvas ----------------- */
27
28 static gint canvas_destroy_event(GtkWidget *widget, gpointer data) {
29 canvas_t *canvas = data;
30 g_free(canvas);
31 return FALSE;
32 }
33
34 /* create a new canvas */
35 canvas_t *canvas_new(void) {
36 canvas_t *canvas = g_new0(canvas_t, 1);
37
38 canvas->widget = goo_canvas_new();
39
40 #if 0 // overlay test
41 /* create a static root item */
42 GooCanvasItem *item = goo_canvas_ellipse_new(NULL,
43 20.0, 20.0,
44 10.0, 10.0,
45 "line-width", 2.0,
46 "stroke-color", "blue",
47 "fill-color", "yellow",
48 NULL);
49
50 goo_canvas_set_static_root_item(GOO_CANVAS(canvas->widget), item);
51 #endif
52
53 g_object_set_data(G_OBJECT(canvas->widget), "canvas-pointer", canvas);
54
55 g_object_set(G_OBJECT(canvas->widget),
56 "anchor", GTK_ANCHOR_CENTER,
57 NULL);
58
59 GooCanvasItem *root = goo_canvas_get_root_item(GOO_CANVAS(canvas->widget));
60
61 /* create the groups */
62 canvas_group_t group;
63 for(group = 0; group < CANVAS_GROUPS; group++)
64 canvas->group[group] = goo_canvas_group_new(root, NULL);
65
66
67 gtk_signal_connect(GTK_OBJECT(canvas->widget),
68 "destroy", G_CALLBACK(canvas_destroy_event), canvas);
69
70 return canvas;
71 }
72
73 GtkWidget *canvas_get_widget(canvas_t *canvas) {
74 return canvas->widget;
75 }
76
77 /* ------------------------ accessing the canvas ---------------------- */
78
79 void canvas_set_background(canvas_t *canvas, canvas_color_t bg_color) {
80 g_object_set(G_OBJECT(canvas->widget),
81 "background-color-rgb", bg_color >> 8,
82 NULL);
83 }
84
85 void canvas_set_antialias(canvas_t *canvas, gboolean antialias) {
86 GooCanvasItem *root = goo_canvas_get_root_item(GOO_CANVAS(canvas->widget));
87 g_object_set(G_OBJECT(root), "antialias",
88 antialias?CAIRO_ANTIALIAS_DEFAULT:CAIRO_ANTIALIAS_NONE, NULL);
89 }
90
91 void canvas_window2world(canvas_t *canvas,
92 gint x, gint y, gint *wx, gint *wy) {
93 double sx = x, sy = y;
94 goo_canvas_convert_from_pixels(GOO_CANVAS(canvas->widget), &sx, &sy);
95 *wx = sx; *wy = sy;
96 }
97
98 canvas_item_t *canvas_get_item_at(canvas_t *canvas, gint x, gint y) {
99 #ifdef CANVAS_CUSTOM_ITEM_AT
100 return canvas_item_info_get_at(canvas, x, y);
101 #else
102 GList *list =
103 goo_canvas_get_items_at(GOO_CANVAS(canvas->widget), x, y, TRUE);
104
105 GList *litem = g_list_first(list);
106 while(litem) {
107 canvas_item_t *item = (canvas_item_t*)litem->data;
108
109 /* only return objects that have a "user data" attached */
110 if(g_object_get_data(G_OBJECT(item), "user data")) {
111 /* check if this is in one of the "selectable" groups */
112 canvas_item_t *parent = goo_canvas_item_get_parent(item);
113 canvas_group_t group;
114 for(group=0;group<CANVAS_GROUPS;group++) {
115 if((CANVAS_SELECTABLE & (1<<group)) &&
116 (canvas->group[group] == parent)) {
117 g_list_free(list);
118 return item;
119 }
120 }
121 }
122 litem = g_list_next(litem);
123 }
124
125 g_list_free(list);
126 return NULL;
127 #endif
128 }
129
130 void canvas_set_zoom(canvas_t *canvas, gdouble zoom) {
131 goo_canvas_set_scale(GOO_CANVAS(canvas->widget), zoom);
132 }
133
134 gdouble canvas_get_zoom(canvas_t *canvas) {
135 return goo_canvas_get_scale(GOO_CANVAS(canvas->widget));
136 }
137
138 gdouble canvas_get_viewport_width(canvas_t *canvas, canvas_unit_t unit) {
139 // Canvas viewport dimensions
140
141 GtkAllocation *a = &(canvas->widget)->allocation;
142 if(unit == CANVAS_UNIT_PIXEL) return a->width;
143
144 /* convert to meters by dividing by zoom */
145 gdouble zoom = goo_canvas_get_scale(GOO_CANVAS(canvas->widget));
146 return a->width / zoom;
147 }
148
149 gdouble canvas_get_viewport_height(canvas_t *canvas, canvas_unit_t unit) {
150 // Canvas viewport dimensions
151 GtkAllocation *a = &(canvas->widget)->allocation;
152 if(unit == CANVAS_UNIT_PIXEL) return a->height;
153
154 /* convert to meters by dividing by zoom */
155 gdouble zoom = goo_canvas_get_scale(GOO_CANVAS(canvas->widget));
156 return a->height / zoom;
157 }
158
159 /* get scroll position in meters/pixels */
160 void canvas_scroll_get(canvas_t *canvas, canvas_unit_t unit,
161 gint *sx, gint *sy) {
162 gdouble zoom = goo_canvas_get_scale(GOO_CANVAS(canvas->widget));
163
164 GtkAdjustment *hadj = ((struct _GooCanvas*)(canvas->widget))->hadjustment;
165 GtkAdjustment *vadj = ((struct _GooCanvas*)(canvas->widget))->vadjustment;
166
167 gdouble hs = gtk_adjustment_get_value(hadj);
168 gdouble vs = gtk_adjustment_get_value(vadj);
169 goo_canvas_convert_from_pixels(GOO_CANVAS(canvas->widget), &hs, &vs);
170
171 /* convert to position relative to screen center */
172 hs += canvas->widget->allocation.width/(2*zoom);
173 vs += canvas->widget->allocation.height/(2*zoom);
174
175 if(unit == CANVAS_UNIT_PIXEL) {
176 /* make values zoom independant */
177 *sx = hs * zoom;
178 *sy = vs * zoom;
179 } else {
180 *sx = hs;
181 *sy = vs;
182 }
183 }
184
185 /* set scroll position in meters/pixels */
186 void canvas_scroll_to(canvas_t *canvas, canvas_unit_t unit, gint sx, gint sy) {
187 gdouble zoom = goo_canvas_get_scale(GOO_CANVAS(canvas->widget));
188
189 if(unit != CANVAS_UNIT_METER) {
190 sx /= zoom; sy /= zoom;
191 }
192
193 /* adjust to screen center */
194 sx -= canvas->widget->allocation.width/(2*zoom);
195 sy -= canvas->widget->allocation.height/(2*zoom);
196
197 goo_canvas_scroll_to(GOO_CANVAS(canvas->widget), sx, sy);
198 }
199
200 void canvas_set_bounds(canvas_t *canvas, gint minx, gint miny,
201 gint maxx, gint maxy) {
202 goo_canvas_set_bounds(GOO_CANVAS(canvas->widget), minx, miny, maxx, maxy);
203 }
204
205 /* ------------------- creating and destroying objects ---------------- */
206
207 void canvas_erase(canvas_t *canvas, gint group_mask) {
208 canvas_group_t group;
209 for(group=0;group<CANVAS_GROUPS;group++) {
210
211 if(group_mask & (1<<group)) {
212 gint children = goo_canvas_item_get_n_children(canvas->group[group]);
213 printf("Removing %d children from group %d\n", children, group);
214 while(children--)
215 goo_canvas_item_remove_child(canvas->group[group], children);
216 }
217 }
218 }
219
220
221 canvas_item_t *canvas_circle_new(canvas_t *canvas, canvas_group_t group,
222 gint x, gint y, gint radius, gint border,
223 canvas_color_t fill_col, canvas_color_t border_col) {
224
225 canvas_item_t *item =
226 goo_canvas_ellipse_new(canvas->group[group],
227 (gdouble) x, (gdouble) y,
228 (gdouble) radius, (gdouble) radius,
229 "line-width", (double)border,
230 "stroke-color-rgba", border_col,
231 "fill-color-rgba", fill_col,
232 NULL);
233
234 #ifdef CANVAS_CUSTOM_ITEM_AT
235 if(CANVAS_SELECTABLE & (1<<group))
236 canvas_item_info_attach_circle(canvas, group, item, x, y, radius + border);
237 #endif
238
239 return item;
240 }
241
242 canvas_points_t *canvas_points_new(gint points) {
243 return goo_canvas_points_new(points);
244 }
245
246 void canvas_point_set_pos(canvas_points_t *points, gint index, lpos_t *lpos) {
247 points->coords[2*index+0] = lpos->x;
248 points->coords[2*index+1] = lpos->y;
249 }
250
251 void canvas_points_free(canvas_points_t *points) {
252 goo_canvas_points_unref(points);
253 }
254
255 gint canvas_points_num(canvas_points_t *points) {
256 return points->num_points;
257 }
258
259 void canvas_point_get_lpos(canvas_points_t *points, gint index, lpos_t *lpos) {
260 lpos->x = points->coords[2*index+0];
261 lpos->y = points->coords[2*index+1];
262 }
263
264 canvas_item_t *canvas_polyline_new(canvas_t *canvas, canvas_group_t group,
265 canvas_points_t *points, gint width, canvas_color_t color) {
266 canvas_item_t *item =
267 goo_canvas_polyline_new(canvas->group[group], FALSE, 0,
268 "points", points,
269 "line-width", (double)width,
270 "stroke-color-rgba", color,
271 "line-join", CAIRO_LINE_JOIN_ROUND,
272 "line-cap", CAIRO_LINE_CAP_ROUND,
273 NULL);
274
275 #ifdef CANVAS_CUSTOM_ITEM_AT
276 if(CANVAS_SELECTABLE & (1<<group))
277 canvas_item_info_attach_poly(canvas, group, item, FALSE, points, width);
278 #endif
279
280 return item;
281 }
282
283 canvas_item_t *canvas_polygon_new(canvas_t *canvas, canvas_group_t group,
284 canvas_points_t *points, gint width, canvas_color_t color,
285 canvas_color_t fill) {
286 canvas_item_t *item =
287 goo_canvas_polyline_new(canvas->group[group], TRUE, 0,
288 "points", points,
289 "line-width", (double)width,
290 "stroke-color-rgba", color,
291 "fill-color-rgba", fill,
292 "line-join", CAIRO_LINE_JOIN_ROUND,
293 "line-cap", CAIRO_LINE_CAP_ROUND,
294 NULL);
295
296 #ifdef CANVAS_CUSTOM_ITEM_AT
297 if(CANVAS_SELECTABLE & (1<<group))
298 canvas_item_info_attach_poly(canvas, group, item, TRUE, points, width);
299 #endif
300
301 return item;
302 }
303
304 /* place the image in pix centered on x/y on the canvas */
305 canvas_item_t *canvas_image_new(canvas_t *canvas, canvas_group_t group,
306 GdkPixbuf *pix, gint x, gint y, float hscale, float vscale) {
307
308 canvas_item_t *item = goo_canvas_image_new(canvas->group[group], pix,
309 x/hscale - gdk_pixbuf_get_width(pix)/2,
310 y/vscale - gdk_pixbuf_get_height(pix)/2, NULL);
311 goo_canvas_item_scale(item, hscale, vscale);
312
313 #ifdef CANVAS_CUSTOM_ITEM_AT
314 if(CANVAS_SELECTABLE & (1<<group)) {
315 gint radius = 0.75 * hscale * MAX(gdk_pixbuf_get_width(pix), gdk_pixbuf_get_height(pix)); /* hscale and vscale are the same */
316 canvas_item_info_attach_circle(canvas, group, item, x, y, radius);
317 }
318 #endif
319
320 return item;
321 }
322
323 void canvas_item_destroy(canvas_item_t *item) {
324 goo_canvas_item_remove(item);
325 }
326
327 /* ------------------------ accessing items ---------------------- */
328
329 void canvas_item_set_points(canvas_item_t *item, canvas_points_t *points) {
330 g_object_set(G_OBJECT(item), "points", points, NULL);
331 }
332
333 void canvas_item_set_pos(canvas_item_t *item, lpos_t *lpos) {
334 g_object_set(G_OBJECT(item),
335 "center-x", (gdouble)lpos->x,
336 "center-y", (gdouble)lpos->y,
337 NULL);
338 }
339
340 void canvas_item_set_radius(canvas_item_t *item, gint radius) {
341 g_object_set(G_OBJECT(item),
342 "radius-x", (gdouble)radius,
343 "radius-y", (gdouble)radius,
344 NULL);
345 }
346
347 void canvas_item_to_bottom(canvas_item_t *item) {
348
349
350 goo_canvas_item_lower(item, NULL);
351 #ifdef CANVAS_CUSTOM_ITEM_AT
352 canvas_t *canvas =
353 g_object_get_data(G_OBJECT(goo_canvas_item_get_canvas(item)),
354 "canvas-pointer");
355
356 g_assert(canvas);
357 canvas_item_info_push(canvas, item);
358 #endif
359 }
360
361 void canvas_item_set_zoom_max(canvas_item_t *item, float zoom_max) {
362 gdouble vis_thres = zoom_max;
363 GooCanvasItemVisibility vis
364 = GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD;
365 if (vis_thres < 0) {
366 vis_thres = 0;
367 vis = GOO_CANVAS_ITEM_VISIBLE;
368 }
369 g_object_set(G_OBJECT(item),
370 "visibility", vis,
371 "visibility-threshold", vis_thres,
372 NULL);
373 }
374
375 void canvas_item_set_dashed(canvas_item_t *item,
376 gint line_width, gint dash_length) {
377 GooCanvasLineDash *dash;
378 if (dash_length <= 0) {
379 dash_length = line_width + 1;
380 }
381 gfloat off_len = dash_length;
382 gfloat on_len = dash_length;
383 guint cap = CAIRO_LINE_CAP_BUTT;
384 if (dash_length > line_width) {
385 off_len += ((gfloat)line_width)/2;
386 on_len -= ((gfloat)line_width)/2;
387 cap = CAIRO_LINE_CAP_ROUND;
388 }
389 dash = goo_canvas_line_dash_new(2, on_len, off_len, 0);
390 goo_canvas_line_dash_ref(dash);
391 g_object_set(G_OBJECT(item),
392 "line-dash", dash,
393 "line-cap", cap,
394 NULL);
395 }
396
397 void canvas_item_set_user_data(canvas_item_t *item, void *data) {
398 g_object_set_data(G_OBJECT(item), "user data", data);
399 }
400
401 void *canvas_item_get_user_data(canvas_item_t *item) {
402 return g_object_get_data(G_OBJECT(item), "user data");
403 }
404
405 typedef struct {
406 GCallback c_handler;
407 gpointer data;
408 } weak_t;
409
410 static void canvas_item_weak_notify(gpointer data, GObject *invalid) {
411 weak_t *weak = data;
412
413 ((void(*)(GtkWidget*, gpointer))weak->c_handler) (NULL, weak->data);
414 g_free(weak);
415 }
416
417 void canvas_item_destroy_connect(canvas_item_t *item,
418 GCallback c_handler, gpointer data) {
419 weak_t *weak = g_new(weak_t,1);
420 weak->data = data;
421 weak->c_handler = c_handler;
422
423 g_object_weak_ref(G_OBJECT(item), canvas_item_weak_notify, weak);
424 }
425
426 void canvas_image_move(canvas_item_t *item, gint x, gint y,
427 float hscale, float vscale) {
428
429 g_object_set(G_OBJECT(item),
430 "x", (gdouble)x / hscale,
431 "y", (gdouble)y / vscale,
432 NULL);
433 }
434
435 /* get the polygon/polyway segment a certain coordinate is over */
436 gint canvas_item_get_segment(canvas_item_t *item, gint x, gint y) {
437
438 canvas_points_t *points = NULL;
439 double line_width = 0;
440
441 g_object_get(G_OBJECT(item),
442 "points", &points,
443 "line-width", &line_width,
444 NULL);
445
446 if(!points) return -1;
447
448 gint retval = -1, i;
449 double mindist = 100;
450 for(i=0;i<points->num_points-1;i++) {
451
452 #define AX (points->coords[2*i+0])
453 #define AY (points->coords[2*i+1])
454 #define BX (points->coords[2*i+2])
455 #define BY (points->coords[2*i+3])
456 #define CX ((double)x)
457 #define CY ((double)y)
458
459 double len2 = pow(BY-AY,2)+pow(BX-AX,2);
460 double m = ((CX-AX)*(BX-AX)+(CY-AY)*(BY-AY)) / len2;
461
462 /* this is a possible candidate */
463 if((m >= 0.0) && (m <= 1.0)) {
464
465 double n;
466 if(fabs(BX-AX) > fabs(BY-AY))
467 n = fabs(sqrt(len2) * (AY+m*(BY-AY)-CY)/(BX-AX));
468 else
469 n = fabs(sqrt(len2) * -(AX+m*(BX-AX)-CX)/(BY-AY));
470
471 /* check if this is actually on the line and closer than anything */
472 /* we found so far */
473 if((n <= line_width/2) && (n < mindist)) {
474 retval = i;
475 mindist = n;
476 }
477 }
478 }
479
480 /* the last and first point are identical for polygons in osm2go. */
481 /* goocanvas doesn't need that, but that's how OSM works and it saves */
482 /* us from having to check the last->first connection for polygons */
483 /* seperately */
484
485 return retval;
486 }
487
488 void canvas_item_get_segment_pos(canvas_item_t *item, gint seg,
489 gint *x0, gint *y0, gint *x1, gint *y1) {
490 printf("get segment %d of item %p\n", seg, item);
491
492 canvas_points_t *points = NULL;
493 g_object_get(G_OBJECT(item), "points", &points, NULL);
494
495 g_assert(points);
496 g_assert(seg < points->num_points-1);
497
498 *x0 = points->coords[2 * seg + 0];
499 *y0 = points->coords[2 * seg + 1];
500 *x1 = points->coords[2 * seg + 2];
501 *y1 = points->coords[2 * seg + 3];
502 }