Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 235 - (hide annotations)
Tue Jul 21 14:31:16 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 69413 byte(s)
Some little work on undo framework
1 harbaum 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     #include "appdata.h"
21    
22     #include <gdk/gdkkeysyms.h>
23    
24    
25     #undef DESTROY_WAIT_FOR_GTK
26    
27     static void map_statusbar(map_t *map, map_item_t *map_item) {
28     char *item_str = NULL;
29     item_id_t id = ID_ILLEGAL;
30     tag_t *tag = NULL;
31     char *str = NULL;
32    
33 harbaum 154 switch(map_item->object.type) {
34     case NODE:
35 harbaum 1 item_str = "Node";
36 harbaum 154 id = map_item->object.node->id;
37     tag = map_item->object.node->tag;
38 harbaum 1 break;
39    
40 harbaum 154 case WAY:
41 harbaum 1 item_str = "Way";
42 harbaum 154 id = map_item->object.way->id;
43     tag = map_item->object.way->tag;
44 harbaum 1 break;
45    
46 harbaum 154 case RELATION:
47 harbaum 153 item_str = "Relation";
48 harbaum 154 id = map_item->object.relation->id;
49     tag = map_item->object.relation->tag;
50 harbaum 153 break;
51    
52 harbaum 1 default:
53     break;
54     }
55    
56     gboolean collision = FALSE;
57     tag_t *tags = tag;
58 harbaum 195 while(tag) {
59     if(!collision && info_tag_key_collision(tags, tag))
60     collision = TRUE;
61     tag = tag->next;
62 harbaum 1 }
63 harbaum 195
64     str = osm_object_get_speaking_name(&map_item->object);
65 harbaum 1 statusbar_set(map->appdata, str, collision);
66     g_free(str);
67     }
68    
69     void map_outside_error(appdata_t *appdata) {
70     errorf(GTK_WIDGET(appdata->window),
71     _("Items must not be placed outside the working area!"));
72     }
73    
74     void map_item_chain_destroy(map_item_chain_t **chainP) {
75     if(!*chainP) {
76     printf("nothing to destroy!\n");
77     return;
78     }
79    
80     #ifdef DESTROY_WAIT_FOR_GTK
81     map_item_chain_t *chain = *chainP;
82     while(chain) {
83     map_item_chain_t *next = chain->next;
84     canvas_item_destroy(chain->map_item->item);
85     chain = next;
86     }
87    
88     /* wait until gtks event handling has actually destroyed this item */
89     printf("waiting for item destruction ");
90     while(gtk_events_pending() || *chainP) {
91     putchar('.');
92     gtk_main_iteration();
93     }
94     printf(" ok\n");
95    
96     /* the callback routine connected to this item should have been */
97     /* called by now and it has set the chain to NULL */
98    
99     #else
100     map_item_chain_t *chain = *chainP;
101     while(chain) {
102     map_item_chain_t *next = chain->next;
103     canvas_item_destroy(chain->map_item->item);
104    
105     g_free(chain);
106     chain = next;
107     }
108     *chainP = NULL;
109     #endif
110     }
111    
112     static void map_node_select(appdata_t *appdata, node_t *node) {
113     map_t *map = appdata->map;
114     map_item_t *map_item = &map->selected;
115    
116     g_assert(!map->highlight);
117    
118 harbaum 154 map_item->object.type = NODE;
119     map_item->object.node = node;
120 harbaum 1 map_item->highlight = FALSE;
121    
122     /* node may not have any visible representation at all */
123     if(node->map_item_chain)
124     map_item->item = node->map_item_chain->map_item->item;
125     else
126     map_item->item = NULL;
127    
128     map_statusbar(map, map_item);
129     icon_bar_map_item_selected(appdata, map_item, TRUE);
130    
131     /* highlight node */
132 harbaum 154 gint x = map_item->object.node->lpos.x, y = map_item->object.node->lpos.y;
133 harbaum 1
134     /* create a copy of this map item and mark it as being a highlight */
135     map_item_t *new_map_item = g_new0(map_item_t, 1);
136     memcpy(new_map_item, map_item, sizeof(map_item_t));
137     new_map_item->highlight = TRUE;
138    
139     float radius = map->style->highlight.width + map->style->node.radius;
140     if(!node->ways) radius += map->style->node.border_radius;
141 harbaum 14 if(node->icon_buf && map->style->icon.enable &&
142     !appdata->settings->no_icons) {
143 harbaum 154 gint w = gdk_pixbuf_get_width(map_item->object.node->icon_buf);
144     gint h = gdk_pixbuf_get_height(map_item->object.node->icon_buf);
145 harbaum 1 /* icons are technically square, so a radius slightly bigger */
146     /* than sqrt(2)*MAX(w,h) should fit nicely */
147 harbaum 163 radius = 0.75 * map->style->icon.scale * ((w>h)?w:h);
148 harbaum 1 }
149    
150 harbaum 163 radius *= map->state->detail;
151 harbaum 1 map_hl_circle_new(map, CANVAS_GROUP_NODES_HL, new_map_item,
152     x, y, radius, map->style->highlight.color);
153 harbaum 103
154 harbaum 1 if(!map_item->item) {
155     /* and draw a fake node */
156     new_map_item = g_new0(map_item_t, 1);
157     memcpy(new_map_item, map_item, sizeof(map_item_t));
158     new_map_item->highlight = TRUE;
159 harbaum 151 map_hl_circle_new(map, CANVAS_GROUP_NODES_IHL, new_map_item,
160 harbaum 1 x, y, map->style->node.radius,
161     map->style->highlight.node_color);
162     }
163     }
164    
165     void map_way_select(appdata_t *appdata, way_t *way) {
166     map_t *map = appdata->map;
167     map_item_t *map_item = &map->selected;
168    
169     g_assert(!map->highlight);
170    
171 harbaum 154 map_item->object.type = WAY;
172     map_item->object.way = way;
173 harbaum 1 map_item->highlight = FALSE;
174     map_item->item = way->map_item_chain->map_item->item;
175    
176     map_statusbar(map, map_item);
177     icon_bar_map_item_selected(appdata, map_item, TRUE);
178     gtk_widget_set_sensitive(appdata->menu_item_map_hide_sel, TRUE);
179    
180 harbaum 162 gint arrow_width = ((map_item->object.way->draw.flags & OSM_DRAW_FLAG_BG)?
181     map->style->highlight.width + map_item->object.way->draw.bg.width/2:
182     map->style->highlight.width + map_item->object.way->draw.width/2)
183     * map->state->detail;
184    
185 harbaum 154 node_chain_t *node_chain = map_item->object.way->node_chain;
186 harbaum 1 node_t *last = NULL;
187     while(node_chain) {
188     map_item_t item;
189 harbaum 154 item.object.type = NODE;
190     item.object.node = node_chain->node;
191 harbaum 1
192     /* draw an arrow between every two nodes */
193     if(last) {
194     /* create a new map item for every arrow */
195     map_item_t *new_map_item = g_new0(map_item_t, 1);
196 harbaum 154 new_map_item->object.type = WAY;
197     new_map_item->object.way = way;
198 harbaum 1 new_map_item->highlight = TRUE;
199    
200     struct { float x, y;} center, diff;
201     center.x = (last->lpos.x + node_chain->node->lpos.x)/2;
202     center.y = (last->lpos.y + node_chain->node->lpos.y)/2;
203     diff.x = node_chain->node->lpos.x - last->lpos.x;
204     diff.y = node_chain->node->lpos.y - last->lpos.y;
205    
206 harbaum 15 /* only draw arrow if there's sufficient space */
207     /* TODO: what if there's not enough space anywhere? */
208 harbaum 1 float len = sqrt(pow(diff.x, 2)+pow(diff.y, 2));
209     if(len > map->style->highlight.arrow_limit*arrow_width) {
210     len /= arrow_width;
211     diff.x = diff.x / len;
212     diff.y = diff.y / len;
213    
214     canvas_points_t *points = canvas_points_new(4);
215     points->coords[2*0+0] = points->coords[2*3+0] = center.x + diff.x;
216     points->coords[2*0+1] = points->coords[2*3+1] = center.y + diff.y;
217     points->coords[2*1+0] = center.x + diff.y - diff.x;
218     points->coords[2*1+1] = center.y - diff.x - diff.y;
219     points->coords[2*2+0] = center.x - diff.y - diff.x;
220     points->coords[2*2+1] = center.y + diff.x - diff.y;
221    
222 harbaum 105 map_hl_polygon_new(map, CANVAS_GROUP_WAYS_DIR, new_map_item,
223 harbaum 1 points, map->style->highlight.arrow_color);
224    
225     canvas_points_free(points);
226     }
227     }
228    
229     if(!map_hl_item_is_highlighted(map, &item)) {
230    
231     /* create a new map item for every node */
232     map_item_t *new_map_item = g_new0(map_item_t, 1);
233 harbaum 154 new_map_item->object.type = NODE;
234     new_map_item->object.node = node_chain->node;
235 harbaum 1 new_map_item->highlight = TRUE;
236    
237     gint x = node_chain->node->lpos.x;
238     gint y = node_chain->node->lpos.y;
239    
240 harbaum 151 map_hl_circle_new(map, CANVAS_GROUP_NODES_IHL, new_map_item,
241 harbaum 162 x, y, map->style->node.radius * map->state->detail,
242 harbaum 1 map->style->highlight.node_color);
243     }
244    
245     last = node_chain->node;
246     node_chain = node_chain->next;
247     }
248    
249     /* a way needs at least 2 points to be drawn */
250     guint nodes = osm_way_number_of_nodes(way);
251     if(nodes > 1) {
252    
253     /* allocate space for nodes */
254     canvas_points_t *points = canvas_points_new(nodes);
255    
256     int node = 0;
257 harbaum 154 node_chain = map_item->object.way->node_chain;
258 harbaum 1 while(node_chain) {
259     canvas_point_set_pos(points, node++, &node_chain->node->lpos);
260     node_chain = node_chain->next;
261     }
262    
263     /* create a copy of this map item and mark it as being a highlight */
264     map_item_t *new_map_item = g_new0(map_item_t, 1);
265     memcpy(new_map_item, map_item, sizeof(map_item_t));
266     new_map_item->highlight = TRUE;
267    
268     map_hl_polyline_new(map, CANVAS_GROUP_WAYS_HL, new_map_item, points,
269 harbaum 162 ((map_item->object.way->draw.flags & OSM_DRAW_FLAG_BG)?
270     2*map->style->highlight.width + map_item->object.way->draw.bg.width:
271     2*map->style->highlight.width + map_item->object.way->draw.width)
272     * map->state->detail, map->style->highlight.color);
273    
274 harbaum 1 canvas_points_free(points);
275     }
276     }
277    
278 harbaum 153 void map_relation_select(appdata_t *appdata, relation_t *relation) {
279     map_t *map = appdata->map;
280    
281 harbaum 161 printf("highlighting relation "ITEM_ID_FORMAT"\n", relation->id);
282 harbaum 153
283     g_assert(!map->highlight);
284     map_highlight_t **hl = &map->highlight;
285    
286     map_item_t *map_item = &map->selected;
287 harbaum 154 map_item->object.type = RELATION;
288     map_item->object.relation = relation;
289 harbaum 153 map_item->highlight = FALSE;
290     map_item->item = NULL;
291    
292     map_statusbar(map, map_item);
293     icon_bar_map_item_selected(appdata, map_item, TRUE);
294    
295     /* process all members */
296     member_t *member = relation->member;
297     while(member) {
298     canvas_item_t *item = NULL;
299    
300 harbaum 155 switch(member->object.type) {
301 harbaum 153
302     case NODE: {
303 harbaum 155 node_t *node = member->object.node;
304 harbaum 161 printf(" -> node "ITEM_ID_FORMAT"\n", node->id);
305 harbaum 153
306     item = canvas_circle_new(map->canvas, CANVAS_GROUP_NODES_HL,
307     node->lpos.x, node->lpos.y,
308     map->style->highlight.width + map->style->node.radius,
309     0, map->style->highlight.color, NO_COLOR);
310     } break;
311    
312     case WAY: {
313 harbaum 155 way_t *way = member->object.way;
314 harbaum 153 /* a way needs at least 2 points to be drawn */
315     guint nodes = osm_way_number_of_nodes(way);
316     if(nodes > 1) {
317    
318     /* allocate space for nodes */
319     canvas_points_t *points = canvas_points_new(nodes);
320    
321     int node = 0;
322     node_chain_t *node_chain = way->node_chain;
323     while(node_chain) {
324     canvas_point_set_pos(points, node++, &node_chain->node->lpos);
325     node_chain = node_chain->next;
326     }
327    
328     if(way->draw.flags & OSM_DRAW_FLAG_AREA)
329     item = canvas_polygon_new(map->canvas, CANVAS_GROUP_WAYS_HL, points, 0, 0,
330     map->style->highlight.color);
331     else
332     item = canvas_polyline_new(map->canvas, CANVAS_GROUP_WAYS_HL, points,
333     (way->draw.flags & OSM_DRAW_FLAG_BG)?
334     2*map->style->highlight.width + way->draw.bg.width:
335     2*map->style->highlight.width + way->draw.width,
336     map->style->highlight.color);
337    
338     canvas_points_free(points);
339     } } break;
340    
341     default:
342     break;
343     }
344    
345     /* attach item to item chain */
346     if(item) {
347     *hl = g_new0(map_highlight_t, 1);
348     (*hl)->item = item;
349     hl = &(*hl)->next;
350     }
351    
352     member = member->next;
353     }
354     }
355    
356 harbaum 154 static void map_object_select(appdata_t *appdata, object_t *object) {
357     switch(object->type) {
358     case NODE:
359     map_node_select(appdata, object->node);
360 harbaum 1 break;
361 harbaum 154 case WAY:
362     map_way_select(appdata, object->way);
363 harbaum 1 break;
364 harbaum 154 case RELATION:
365     map_relation_select(appdata, object->relation);
366     break;
367 harbaum 1 default:
368 harbaum 154 g_assert((object->type == NODE)||(object->type == RELATION)||
369     (object->type == WAY));
370 harbaum 1 break;
371     }
372     }
373    
374     void map_item_deselect(appdata_t *appdata) {
375    
376     /* save tags for "last" function in info dialog */
377 harbaum 154 if(appdata->map->selected.object.type == NODE) {
378 harbaum 1 if(appdata->map->last_node_tags)
379     osm_tags_free(appdata->map->last_node_tags);
380    
381     appdata->map->last_node_tags =
382 harbaum 234 osm_tags_copy(appdata->map->selected.object.node->tag);
383 harbaum 154 } else if(appdata->map->selected.object.type == WAY) {
384 harbaum 1 if(appdata->map->last_way_tags)
385     osm_tags_free(appdata->map->last_way_tags);
386    
387     appdata->map->last_way_tags =
388 harbaum 234 osm_tags_copy(appdata->map->selected.object.way->tag);
389 harbaum 1 }
390    
391     /* remove statusbar message */
392     statusbar_set(appdata, NULL, FALSE);
393    
394     /* disable/enable icons in icon bar */
395     icon_bar_map_item_selected(appdata, NULL, FALSE);
396     gtk_widget_set_sensitive(appdata->menu_item_map_hide_sel, FALSE);
397    
398     /* remove highlight */
399     map_hl_remove(appdata);
400    
401     /* forget about selection */
402 harbaum 154 appdata->map->selected.object.type = ILLEGAL;
403 harbaum 1 }
404    
405     /* called whenever a map item is to be destroyed */
406     static gint map_item_destroy_event(GtkWidget *widget, gpointer data) {
407     map_item_t *map_item = (map_item_t*)data;
408    
409     // printf("destroying map_item @ %p\n", map_item);
410    
411     #ifdef DESTROY_WAIT_FOR_GTK
412     /* remove item from nodes/ways map_item_chain */
413     map_item_chain_t **chain = NULL;
414 harbaum 154 if(map_item->object.type == NODE)
415     chain = &map_item->object.node->map_item_chain;
416     else if(map_item->object.type == WAY)
417     chain = &map_item->object.way->map_item_chain;
418 harbaum 1
419     /* there must be a chain with content, otherwise things are broken */
420     g_assert(chain);
421     g_assert(*chain);
422    
423     /* search current map_item, ... */
424     while(*chain && (*chain)->map_item != map_item)
425     chain = &(*chain)->next;
426    
427     g_assert(*chain);
428    
429     /* ... remove it from chain and free it */
430     map_item_chain_t *tmp = *chain;
431     *chain = (*chain)->next;
432    
433     g_free(tmp);
434     #endif
435    
436     g_free(map_item);
437     return FALSE;
438     }
439    
440     static canvas_item_t *map_node_new(map_t *map, node_t *node, gint radius,
441     gint width, canvas_color_t fill, canvas_color_t border) {
442    
443     map_item_t *map_item = g_new0(map_item_t, 1);
444 harbaum 154 map_item->object.type = NODE;
445     map_item->object.node = node;
446 harbaum 1
447 harbaum 14 if(!node->icon_buf || !map->style->icon.enable ||
448 harbaum 64 map->appdata->settings->no_icons)
449 harbaum 103 map_item->item = canvas_circle_new(map->canvas, CANVAS_GROUP_NODES,
450 harbaum 1 node->lpos.x, node->lpos.y, radius, width, fill, border);
451     else
452 harbaum 103 map_item->item = canvas_image_new(map->canvas, CANVAS_GROUP_NODES,
453 harbaum 1 node->icon_buf,
454 harbaum 162 node->lpos.x - map->style->icon.scale/2 * map->state->detail *
455 harbaum 1 gdk_pixbuf_get_width(node->icon_buf),
456 harbaum 162 node->lpos.y - map->style->icon.scale/2 * map->state->detail *
457 harbaum 1 gdk_pixbuf_get_height(node->icon_buf),
458 harbaum 162 map->state->detail * map->style->icon.scale,
459     map->state->detail * map->style->icon.scale);
460 harbaum 1
461 harbaum 162 canvas_item_set_zoom_max(map_item->item,
462     node->zoom_max / (2 * map->state->detail));
463 harbaum 1
464     /* attach map_item to nodes map_item_chain */
465     map_item_chain_t **chain = &node->map_item_chain;
466     while(*chain) chain = &(*chain)->next;
467     *chain = g_new0(map_item_chain_t, 1);
468     (*chain)->map_item = map_item;
469    
470     canvas_item_set_user_data(map_item->item, map_item);
471    
472     canvas_item_destroy_connect(map_item->item,
473     G_CALLBACK(map_item_destroy_event), map_item);
474    
475     return map_item->item;
476     }
477    
478     /* in the rare case that a way consists of only one node, it is */
479     /* drawn as a circle. This e.g. happens when drawing a new way */
480     static canvas_item_t *map_way_single_new(map_t *map, way_t *way, gint radius,
481     gint width, canvas_color_t fill, canvas_color_t border) {
482    
483     map_item_t *map_item = g_new0(map_item_t, 1);
484 harbaum 154 map_item->object.type = WAY;
485     map_item->object.way = way;
486 harbaum 103 map_item->item = canvas_circle_new(map->canvas, CANVAS_GROUP_WAYS,
487 harbaum 1 way->node_chain->node->lpos.x, way->node_chain->node->lpos.y,
488     radius, width, fill, border);
489    
490     // TODO: decide: do we need canvas_item_set_zoom_max() here too?
491    
492     /* attach map_item to nodes map_item_chain */
493     map_item_chain_t **chain = &way->map_item_chain;
494     while(*chain) chain = &(*chain)->next;
495     *chain = g_new0(map_item_chain_t, 1);
496     (*chain)->map_item = map_item;
497    
498     canvas_item_set_user_data(map_item->item, map_item);
499    
500     canvas_item_destroy_connect(map_item->item,
501     G_CALLBACK(map_item_destroy_event), map_item);
502    
503     return map_item->item;
504     }
505    
506     static canvas_item_t *map_way_new(map_t *map, canvas_group_t group,
507     way_t *way, canvas_points_t *points, gint width,
508     canvas_color_t color, canvas_color_t fill_color) {
509     map_item_t *map_item = g_new0(map_item_t, 1);
510 harbaum 154 map_item->object.type = WAY;
511     map_item->object.way = way;
512 harbaum 1
513     if(way->draw.flags & OSM_DRAW_FLAG_AREA) {
514 harbaum 22 if(map->style->area.color & 0xff)
515 harbaum 103 map_item->item = canvas_polygon_new(map->canvas, group, points,
516 harbaum 1 width, color, fill_color);
517     else
518 harbaum 103 map_item->item = canvas_polyline_new(map->canvas, group, points,
519 harbaum 1 width, color);
520     } else {
521 harbaum 103 map_item->item = canvas_polyline_new(map->canvas, group, points, width, color);
522 harbaum 1 }
523    
524 harbaum 162 canvas_item_set_zoom_max(map_item->item,
525     way->draw.zoom_max / (2 * map->state->detail));
526 harbaum 1
527 harbaum 108 /* a ways outline itself is never dashed */
528 achadwick 13 if (group != CANVAS_GROUP_WAYS_OL)
529     if (way->draw.dashed)
530 achadwick 46 canvas_item_set_dashed(map_item->item, width, way->draw.dash_length);
531 achadwick 13
532 harbaum 1 /* attach map_item to ways map_item_chain */
533     map_item_chain_t **chain = &way->map_item_chain;
534     while(*chain) chain = &(*chain)->next;
535     *chain = g_new0(map_item_chain_t, 1);
536     (*chain)->map_item = map_item;
537    
538     canvas_item_set_user_data(map_item->item, map_item);
539    
540     canvas_item_destroy_connect(map_item->item,
541     G_CALLBACK(map_item_destroy_event), map_item);
542    
543     return map_item->item;
544     }
545    
546     void map_show_node(map_t *map, node_t *node) {
547     map_node_new(map, node, map->style->node.radius, 0,
548 harbaum 22 map->style->node.color, 0);
549 harbaum 1 }
550    
551     void map_way_draw(map_t *map, way_t *way) {
552    
553     /* don't draw a way that's not there anymore */
554     if(way->flags & (OSM_FLAG_DELETED | OSM_FLAG_HIDDEN))
555     return;
556    
557     /* allocate space for nodes */
558     /* a way needs at least 2 points to be drawn */
559     guint nodes = osm_way_number_of_nodes(way);
560     if(nodes == 1) {
561     /* draw a single dot where this single node is */
562     map_way_single_new(map, way, map->style->node.radius, 0,
563 harbaum 22 map->style->node.color, 0);
564 harbaum 1 } else {
565     canvas_points_t *points = canvas_points_new(nodes);
566    
567     int node = 0;
568     node_chain_t *node_chain = way->node_chain;
569     while(node_chain) {
570     canvas_point_set_pos(points, node++, &node_chain->node->lpos);
571     node_chain = node_chain->next;
572     }
573    
574     /* draw way */
575 harbaum 162 float width = way->draw.width * map->state->detail;
576    
577 harbaum 1 if(way->draw.flags & OSM_DRAW_FLAG_AREA) {
578     map_way_new(map, CANVAS_GROUP_POLYGONS, way, points,
579 harbaum 162 width, way->draw.color, way->draw.area.color);
580 harbaum 1 } else {
581 harbaum 162
582 harbaum 108 if(way->draw.flags & OSM_DRAW_FLAG_BG) {
583     map_way_new(map, CANVAS_GROUP_WAYS_INT, way, points,
584 harbaum 162 width, way->draw.color, NO_COLOR);
585 harbaum 108
586 harbaum 1 map_way_new(map, CANVAS_GROUP_WAYS_OL, way, points,
587 harbaum 162 way->draw.bg.width * map->state->detail,
588     way->draw.bg.color, NO_COLOR);
589 harbaum 108
590     } else
591     map_way_new(map, CANVAS_GROUP_WAYS, way, points,
592 harbaum 162 width, way->draw.color, NO_COLOR);
593 harbaum 1 }
594     canvas_points_free(points);
595     }
596     }
597    
598     void map_node_draw(map_t *map, node_t *node) {
599     /* don't draw a node that's not there anymore */
600     if(node->flags & OSM_FLAG_DELETED)
601     return;
602    
603     if(!node->ways)
604     map_node_new(map, node,
605 harbaum 162 map->style->node.radius * map->state->detail,
606     map->style->node.border_radius * map->state->detail,
607 harbaum 22 map->style->node.fill_color,
608     map->style->node.color);
609 harbaum 1
610     else if(map->style->node.show_untagged || osm_node_has_tag(node))
611     map_node_new(map, node,
612 harbaum 162 map->style->node.radius * map->state->detail, 0,
613 harbaum 22 map->style->node.color, 0);
614 harbaum 1 }
615    
616     static void map_item_draw(map_t *map, map_item_t *map_item) {
617 harbaum 154 switch(map_item->object.type) {
618     case NODE:
619     map_node_draw(map, map_item->object.node);
620 harbaum 1 break;
621 harbaum 154 case WAY:
622     map_way_draw(map, map_item->object.way);
623 harbaum 1 break;
624     default:
625 harbaum 154 g_assert((map_item->object.type == NODE) ||
626     (map_item->object.type == WAY));
627 harbaum 1 }
628     }
629    
630     static void map_item_remove(map_t *map, map_item_t *map_item) {
631     map_item_chain_t **chainP = NULL;
632    
633 harbaum 154 switch(map_item->object.type) {
634     case NODE:
635     chainP = &map_item->object.node->map_item_chain;
636 harbaum 1 break;
637 harbaum 154 case WAY:
638     chainP = &map_item->object.way->map_item_chain;
639 harbaum 1 break;
640     default:
641 harbaum 154 g_assert((map_item->object.type == NODE) ||
642     (map_item->object.type == WAY));
643 harbaum 1 }
644    
645     map_item_chain_destroy(chainP);
646     }
647    
648     static void map_item_init(style_t *style, map_item_t *map_item) {
649 harbaum 154 switch (map_item->object.type){
650     case WAY:
651     josm_elemstyles_colorize_way(style, map_item->object.way);
652 harbaum 1 break;
653 harbaum 154 case NODE:
654     josm_elemstyles_colorize_node(style, map_item->object.node);
655 harbaum 1 break;
656     default:
657 harbaum 154 g_assert((map_item->object.type == NODE) ||
658     (map_item->object.type == WAY));
659 harbaum 1 }
660     }
661    
662     void map_item_redraw(appdata_t *appdata, map_item_t *map_item) {
663     map_item_t item = *map_item;
664    
665 harbaum 191 /* a relation cannot be redraws as it doesn't have a visual */
666     /* representation */
667     if(map_item->object.type == RELATION)
668     return;
669    
670 harbaum 1 /* check if the item to be redrawn is the selected one */
671     gboolean is_selected = FALSE;
672 harbaum 154 if(map_item->object.ptr == appdata->map->selected.object.ptr) {
673 harbaum 1 map_item_deselect(appdata);
674     is_selected = TRUE;
675     }
676    
677     map_item_remove(appdata->map, &item);
678     map_item_init(appdata->map->style, &item);
679     map_item_draw(appdata->map, &item);
680    
681     /* restore selection if there was one */
682     if(is_selected)
683 harbaum 154 map_object_select(appdata, &item.object);
684 harbaum 1 }
685    
686     static void map_frisket_rectangle(canvas_points_t *points,
687     gint x0, gint x1, gint y0, gint y1) {
688     points->coords[2*0+0] = points->coords[2*3+0] = points->coords[2*4+0] = x0;
689     points->coords[2*1+0] = points->coords[2*2+0] = x1;
690     points->coords[2*0+1] = points->coords[2*1+1] = points->coords[2*4+1] = y0;
691     points->coords[2*2+1] = points->coords[2*3+1] = y1;
692     }
693    
694     /* Draw the frisket area which masks off areas it'd be unsafe to edit,
695     * plus its inner edge marker line */
696     void map_frisket_draw(map_t *map, bounds_t *bounds) {
697     canvas_points_t *points = canvas_points_new(5);
698    
699     /* don't draw frisket at all if it's completely transparent */
700 harbaum 22 if(map->style->frisket.color & 0xff) {
701     elemstyle_color_t color = map->style->frisket.color;
702 harbaum 1
703     float mult = map->style->frisket.mult;
704    
705     /* top rectangle */
706     map_frisket_rectangle(points, mult*bounds->min.x, mult*bounds->max.x,
707     mult*bounds->min.y, bounds->min.y);
708 harbaum 103 canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points,
709     1, NO_COLOR, color);
710 harbaum 1
711     /* bottom rectangle */
712     map_frisket_rectangle(points, mult*bounds->min.x, mult*bounds->max.x,
713     bounds->max.y, mult*bounds->max.y);
714 harbaum 103 canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points,
715     1, NO_COLOR, color);
716 harbaum 1
717     /* left rectangle */
718     map_frisket_rectangle(points, mult*bounds->min.x, bounds->min.x,
719     mult*bounds->min.y, mult*bounds->max.y);
720 harbaum 103 canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points,
721     1, NO_COLOR, color);
722 harbaum 1
723     /* right rectangle */
724     map_frisket_rectangle(points, bounds->max.x, mult*bounds->max.x,
725     mult*bounds->min.y, mult*bounds->max.y);
726 harbaum 103 canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points,
727     1, NO_COLOR, color);
728 harbaum 1
729     }
730    
731     if(map->style->frisket.border.present) {
732     // Edge marker line
733     gint ew2 = map->style->frisket.border.width/2;
734     map_frisket_rectangle(points,
735     bounds->min.x-ew2, bounds->max.x+ew2,
736     bounds->min.y-ew2, bounds->max.y+ew2);
737    
738 harbaum 103 canvas_polyline_new(map->canvas, CANVAS_GROUP_FRISKET, points,
739 harbaum 1 map->style->frisket.border.width,
740     map->style->frisket.border.color);
741    
742     }
743     canvas_points_free(points);
744     }
745    
746     static void map_draw(map_t *map, osm_t *osm) {
747     g_assert(map->canvas);
748    
749     printf("drawing ways ...\n");
750     way_t *way = osm->way;
751     while(way) {
752     map_way_draw(map, way);
753     way = way->next;
754     }
755    
756     printf("drawing single nodes ...\n");
757     node_t *node = osm->node;
758     while(node) {
759     map_node_draw(map, node);
760     node = node->next;
761     }
762    
763     printf("drawing frisket...\n");
764     map_frisket_draw(map, osm->bounds);
765     }
766    
767     void map_state_free(map_state_t *state) {
768     if(!state) return;
769    
770     /* free state of noone else references it */
771     if(state->refcount > 1)
772     state->refcount--;
773     else
774     g_free(state);
775     }
776    
777     void map_free_map_item_chains(appdata_t *appdata) {
778     if(!appdata->osm) return;
779    
780     #ifndef DESTROY_WAIT_FOR_GTK
781     printf(" DESTROY_WAIT_FOR_GTK not set, removing all chains now\n");
782    
783     /* free all map_item_chains */
784     node_t *node = appdata->osm->node;
785     while(node) {
786     map_item_chain_t *chain = node->map_item_chain;
787     while(chain) {
788     map_item_chain_t *next = chain->next;
789     g_free(chain);
790     chain = next;
791     }
792     node->map_item_chain = NULL;
793     node = node->next;
794     }
795    
796     way_t *way = appdata->osm->way;
797     while(way) {
798     map_item_chain_t *chain = way->map_item_chain;
799     while(chain) {
800     map_item_chain_t *next = chain->next;
801     g_free(chain);
802     chain = next;
803     }
804     way->map_item_chain = NULL;
805     way = way->next;
806     }
807     #endif
808     }
809    
810     static gint map_destroy_event(GtkWidget *widget, gpointer data) {
811     appdata_t *appdata = (appdata_t*)data;
812     map_t *map = appdata->map;
813    
814     printf("destroying entire map\n");
815    
816     map_free_map_item_chains(appdata);
817    
818     /* free buffered tags */
819     if(map->last_node_tags) osm_tags_free(map->last_node_tags);
820     if(map->last_way_tags) osm_tags_free(map->last_way_tags);
821    
822     map_state_free(map->state);
823    
824     if(map->style)
825     style_free(map->style);
826    
827     /* destroy existing highlight */
828     if(map->highlight) {
829     printf("removing highlight\n");
830    
831     map_highlight_t *hl = map->highlight;
832     while(hl) {
833     map_highlight_t *next = hl->next;
834     g_free(hl);
835     hl = next;
836     }
837     }
838    
839     g_free(map);
840    
841     appdata->map = NULL;
842    
843     return FALSE;
844     }
845    
846     /* get the item at position x, y */
847     map_item_t *map_item_at(map_t *map, gint x, gint y) {
848     printf("map check at %d/%d\n", x, y);
849    
850     canvas_window2world(map->canvas, x, y, &x, &y);
851    
852     printf("world check at %d/%d\n", x, y);
853    
854     canvas_item_t *item = canvas_get_item_at(map->canvas, x, y);
855    
856     if(!item) {
857     printf(" there's no item\n");
858     return NULL;
859     }
860    
861     printf(" there's an item (%p)\n", item);
862    
863     map_item_t *map_item = canvas_item_get_user_data(item);
864    
865     if(!map_item) {
866     printf(" item has no user data!\n");
867     return NULL;
868     }
869    
870 harbaum 15 if(map_item->highlight)
871 harbaum 1 printf(" item is highlight\n");
872    
873 harbaum 154 switch(map_item->object.type) {
874     case NODE:
875 harbaum 161 printf(" item is node #"ITEM_ID_FORMAT"\n", map_item->object.node->id);
876 harbaum 1 break;
877 harbaum 154 case WAY:
878 harbaum 161 printf(" item is way #"ITEM_ID_FORMAT"\n", map_item->object.way->id);
879 harbaum 1 break;
880     default:
881     printf(" unknown item\n");
882     break;
883     }
884    
885     return map_item;
886     }
887    
888     /* get the real item (no highlight) at x, y */
889 harbaum 15 map_item_t *map_real_item_at(map_t *map, gint x, gint y) {
890 harbaum 1 map_item_t *map_item = map_item_at(map, x, y);
891    
892     /* no item or already a real one */
893     if(!map_item || !map_item->highlight) return map_item;
894    
895     /* get the item (parent) this item is the highlight of */
896     map_item_t *parent = NULL;
897 harbaum 154 switch(map_item->object.type) {
898 harbaum 1
899 harbaum 154 case NODE:
900     if(map_item->object.node->map_item_chain)
901     parent = map_item->object.node->map_item_chain->map_item;
902 harbaum 1
903     if(parent)
904 harbaum 161 printf(" using parent item node #" ITEM_ID_FORMAT "\n",
905     parent->object.node->id);
906 harbaum 1 break;
907    
908 harbaum 154 case WAY:
909     if(map_item->object.way->map_item_chain)
910     parent = map_item->object.way->map_item_chain->map_item;
911 harbaum 1
912     if(parent)
913 harbaum 161 printf(" using parent item way #" ITEM_ID_FORMAT "\n",
914     parent->object.way->id);
915 harbaum 1 break;
916    
917     default:
918 harbaum 154 g_assert((map_item->object.type == NODE) ||
919     (map_item->object.type == WAY));
920 harbaum 1 break;
921     }
922    
923     if(parent)
924     map_item = parent;
925     else
926     printf(" no parent, working on highlight itself\n");
927    
928     return map_item;
929     }
930    
931     /* Limitations on the amount by which we can scroll. Keeps part of the
932     * map visible at all times */
933 harbaum 103 static void map_limit_scroll(map_t *map, canvas_unit_t unit,
934     gint *sx, gint *sy) {
935 harbaum 1
936 harbaum 103 /* get scale factor for pixel->meter conversion. set to 1 if */
937     /* given coordinates are already in meters */
938     gdouble scale = (unit == CANVAS_UNIT_METER)?1.0:canvas_get_zoom(map->canvas);
939 harbaum 1
940 harbaum 103 /* convert pixels to meters if necessary */
941     gdouble sx_cu = *sx / scale;
942     gdouble sy_cu = *sy / scale;
943    
944     /* get size of visible area in canvas units (meters) */
945     gint aw_cu = canvas_get_viewport_width(map->canvas, CANVAS_UNIT_METER);
946     gint ah_cu = canvas_get_viewport_height(map->canvas, CANVAS_UNIT_METER);
947    
948     // Data rect minimum and maximum
949     gint min_x, min_y, max_x, max_y;
950     min_x = map->appdata->osm->bounds->min.x;
951     min_y = map->appdata->osm->bounds->min.y;
952     max_x = map->appdata->osm->bounds->max.x;
953     max_y = map->appdata->osm->bounds->max.y;
954    
955     // limit stops - prevent scrolling beyond these
956     gint min_sy_cu = 0.95*(min_y - ah_cu);
957     gint min_sx_cu = 0.95*(min_x - aw_cu);
958     gint max_sy_cu = 0.95*(max_y);
959     gint max_sx_cu = 0.95*(max_x);
960     if (sy_cu < min_sy_cu) { *sy = min_sy_cu * scale; }
961     if (sx_cu < min_sx_cu) { *sx = min_sx_cu * scale; }
962     if (sy_cu > max_sy_cu) { *sy = max_sy_cu * scale; }
963     if (sx_cu > max_sx_cu) { *sx = max_sx_cu * scale; }
964 harbaum 1 }
965    
966    
967     /* Limit a proposed zoom factor to sane ranges.
968     * Specifically the map is allowed to be no smaller than the viewport. */
969     static gboolean map_limit_zoom(map_t *map, gdouble *zoom) {
970     // Data rect minimum and maximum
971     gint min_x, min_y, max_x, max_y;
972     min_x = map->appdata->osm->bounds->min.x;
973     min_y = map->appdata->osm->bounds->min.y;
974     max_x = map->appdata->osm->bounds->max.x;
975     max_y = map->appdata->osm->bounds->max.y;
976    
977 harbaum 103 /* get size of visible area in pixels and convert to meters of intended */
978     /* zoom by deviding by zoom (which is basically pix/m) */
979     gint aw_cu =
980     canvas_get_viewport_width(map->canvas, CANVAS_UNIT_PIXEL) / *zoom;
981     gint ah_cu =
982     canvas_get_viewport_height(map->canvas, CANVAS_UNIT_PIXEL) / *zoom;
983 harbaum 1
984     gdouble oldzoom = *zoom;
985     if (ah_cu < aw_cu) {
986     gint lim_h = ah_cu*0.95;
987     if (max_y-min_y < lim_h) {
988     gdouble corr = ((gdouble)max_y-min_y) / (gdouble)lim_h;
989     *zoom /= corr;
990     }
991     }
992     else {
993     gint lim_w = aw_cu*0.95;
994     if (max_x-min_x < lim_w) {
995     gdouble corr = ((gdouble)max_x-min_x) / (gdouble)lim_w;
996     *zoom /= corr;
997     }
998     }
999     if (*zoom != oldzoom) {
1000 harbaum 222 printf("Can't zoom further out (%f)\n", *zoom);
1001 harbaum 1 return 1;
1002     }
1003     return 0;
1004     }
1005    
1006    
1007 achadwick 12 /*
1008     * Scroll the map to a point if that point is currently offscreen.
1009     */
1010     void map_scroll_to_if_offscreen(map_t *map, lpos_t *lpos) {
1011    
1012     // Ignore anything outside the working area
1013     if (!(map && map->appdata && map->appdata->osm)) {
1014     return;
1015     }
1016     gint min_x, min_y, max_x, max_y;
1017     min_x = map->appdata->osm->bounds->min.x;
1018     min_y = map->appdata->osm->bounds->min.y;
1019     max_x = map->appdata->osm->bounds->max.x;
1020     max_y = map->appdata->osm->bounds->max.y;
1021     if ( (lpos->x > max_x) || (lpos->x < min_x)
1022     || (lpos->y > max_y) || (lpos->y < min_y)) {
1023 harbaum 14 printf("cannot scroll to (%d, %d): outside the working area\n",
1024     lpos->x, lpos->y);
1025 achadwick 12 return;
1026     }
1027    
1028     // Viewport dimensions in canvas space
1029    
1030 harbaum 103 /* get size of visible area in canvas units (meters) */
1031     gdouble pix_per_meter = canvas_get_zoom(map->canvas);
1032     gdouble aw = canvas_get_viewport_width(map->canvas, CANVAS_UNIT_METER);
1033     gdouble ah = canvas_get_viewport_height(map->canvas, CANVAS_UNIT_METER);
1034    
1035 achadwick 12 // Is the point still onscreen?
1036     gboolean vert_recentre_needed = FALSE;
1037     gboolean horiz_recentre_needed = FALSE;
1038     gint sx, sy;
1039 harbaum 103 canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy);
1040     gint viewport_left = (sx/pix_per_meter);
1041     gint viewport_right = (sx/pix_per_meter)+aw;
1042     gint viewport_top = (sy/pix_per_meter);
1043     gint viewport_bottom = (sy/pix_per_meter)+ah;
1044 achadwick 12 if (lpos->x > viewport_right) {
1045     printf("** off right edge (%d > %d)\n", lpos->x, viewport_right);
1046     horiz_recentre_needed = TRUE;
1047     }
1048     if (lpos->x < viewport_left) {
1049     printf("** off left edge (%d < %d)\n", lpos->x, viewport_left);
1050     horiz_recentre_needed = TRUE;
1051     }
1052     if (lpos->y > viewport_bottom) {
1053     printf("** off bottom edge (%d > %d)\n", lpos->y, viewport_bottom);
1054     vert_recentre_needed = TRUE;
1055     }
1056     if (lpos->y < viewport_top) {
1057     printf("** off top edge (%d < %d)\n", lpos->y, viewport_top);
1058     vert_recentre_needed = TRUE;
1059     }
1060    
1061     if (horiz_recentre_needed || vert_recentre_needed) {
1062     gint new_sx, new_sy;
1063 harbaum 78
1064 achadwick 12 // Just centre both at once
1065 harbaum 103 new_sx = pix_per_meter * (lpos->x - (aw/2));
1066     new_sy = pix_per_meter * (lpos->y - (ah/2));
1067 harbaum 78
1068 harbaum 103 map_limit_scroll(map, CANVAS_UNIT_PIXEL, &new_sx, &new_sy);
1069     canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, new_sx, new_sy);
1070 achadwick 12 }
1071     }
1072    
1073 harbaum 1 /* Deselects the current way or node if its zoom_max
1074     * means that it's not going to render at the current map zoom. */
1075     void map_deselect_if_zoom_below_zoom_max(map_t *map) {
1076 harbaum 154 if (map->selected.object.type == WAY) {
1077 harbaum 1 printf("will deselect way if zoomed below %f\n",
1078 harbaum 154 map->selected.object.way->draw.zoom_max);
1079     if (map->state->zoom < map->selected.object.way->draw.zoom_max) {
1080 harbaum 1 printf(" deselecting way!\n");
1081     map_item_deselect(map->appdata);
1082     }
1083     }
1084 harbaum 154 else if (map->selected.object.type == NODE) {
1085 harbaum 1 printf("will deselect node if zoomed below %f\n",
1086 harbaum 154 map->selected.object.node->zoom_max);
1087     if (map->state->zoom < map->selected.object.node->zoom_max) {
1088 harbaum 1 printf(" deselecting node!\n");
1089     map_item_deselect(map->appdata);
1090     }
1091     }
1092     }
1093    
1094     void map_set_zoom(map_t *map, double zoom,
1095     gboolean update_scroll_offsets) {
1096     gboolean at_zoom_limit = 0;
1097     at_zoom_limit = map_limit_zoom(map, &zoom);
1098 harbaum 78
1099 harbaum 1 map->state->zoom = zoom;
1100     canvas_set_zoom(map->canvas, map->state->zoom);
1101    
1102     map_deselect_if_zoom_below_zoom_max(map);
1103    
1104 harbaum 103 if(update_scroll_offsets) {
1105 harbaum 1 if (!at_zoom_limit) {
1106     /* zooming affects the scroll offsets */
1107     gint sx, sy;
1108 harbaum 103 canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy);
1109     map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy);
1110    
1111     // keep the map visible
1112     canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy);
1113 harbaum 1 }
1114 harbaum 103
1115     canvas_scroll_get(map->canvas, CANVAS_UNIT_METER,
1116     &map->state->scroll_offset.x,
1117     &map->state->scroll_offset.y);
1118 harbaum 1 }
1119     }
1120    
1121     static gboolean map_scroll_event(GtkWidget *widget, GdkEventScroll *event,
1122     gpointer data) {
1123     appdata_t *appdata = (appdata_t*)data;
1124    
1125     if(!appdata->osm) return FALSE;
1126    
1127     if(event->type == GDK_SCROLL && appdata->map && appdata->map->state) {
1128     if(event->direction)
1129     map_set_zoom(appdata->map,
1130     appdata->map->state->zoom / ZOOM_FACTOR_WHEEL, TRUE);
1131     else
1132     map_set_zoom(appdata->map,
1133     appdata->map->state->zoom * ZOOM_FACTOR_WHEEL, TRUE);
1134     }
1135    
1136     return TRUE;
1137     }
1138    
1139     static gboolean distance_above(map_t *map, gint x, gint y, gint limit) {
1140     gint sx, sy;
1141    
1142     /* add offsets generated by mouse within map and map scrolling */
1143     sx = (x-map->pen_down.at.x);
1144     sy = (y-map->pen_down.at.y);
1145    
1146     return(sx*sx + sy*sy > limit*limit);
1147     }
1148    
1149     /* scroll with respect to two screen positions */
1150     static void map_do_scroll(map_t *map, gint x, gint y) {
1151     gint sx, sy;
1152    
1153 harbaum 103 canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy);
1154 harbaum 1 sx -= x-map->pen_down.at.x;
1155     sy -= y-map->pen_down.at.y;
1156 harbaum 103 map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy);
1157     canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy);
1158    
1159     canvas_scroll_get(map->canvas, CANVAS_UNIT_METER,
1160     &map->state->scroll_offset.x,
1161     &map->state->scroll_offset.y);
1162 harbaum 1 }
1163    
1164    
1165     /* scroll a certain step */
1166     static void map_do_scroll_step(map_t *map, gint x, gint y) {
1167     gint sx, sy;
1168 harbaum 103 canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy);
1169 harbaum 1 sx += x;
1170     sy += y;
1171 harbaum 103 map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy);
1172     canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy);
1173    
1174     canvas_scroll_get(map->canvas, CANVAS_UNIT_METER,
1175     &map->state->scroll_offset.x,
1176     &map->state->scroll_offset.y);
1177 harbaum 1 }
1178    
1179     gboolean map_item_is_selected_node(map_t *map, map_item_t *map_item) {
1180     printf("check if item is a selected node\n");
1181    
1182     if(!map_item) {
1183     printf(" no item requested\n");
1184     return FALSE;
1185     }
1186    
1187 harbaum 154 if(map->selected.object.type == ILLEGAL) {
1188 harbaum 1 printf(" nothing is selected\n");
1189     return FALSE;
1190     }
1191    
1192     /* clicked the highlight directly */
1193 harbaum 154 if(map_item->object.type != NODE) {
1194 harbaum 1 printf(" didn't click node\n");
1195     return FALSE;
1196     }
1197    
1198 harbaum 154 if(map->selected.object.type == NODE) {
1199 harbaum 1 printf(" selected item is a node\n");
1200    
1201 harbaum 154 if(map_item->object.node == map->selected.object.node) {
1202 harbaum 1 printf(" requested item is a selected node\n");
1203     return TRUE;
1204     }
1205     printf(" but it's not the requested one\n");
1206     return FALSE;
1207    
1208 harbaum 154 } else if(map->selected.object.type == WAY) {
1209 harbaum 1 printf(" selected item is a way\n");
1210    
1211 harbaum 154 node_chain_t *node_chain = map->selected.object.way->node_chain;
1212 harbaum 1 while(node_chain) {
1213 harbaum 154 if(node_chain->node == map_item->object.node) {
1214 harbaum 1 printf(" requested item is part of selected way\n");
1215     return TRUE;
1216     }
1217     node_chain = node_chain->next;
1218     }
1219     printf(" but it doesn't include the requested node\n");
1220     return FALSE;
1221    
1222     } else {
1223     printf(" selected item is unknown\n");
1224     return FALSE;
1225     }
1226    
1227     g_assert(0);
1228     return TRUE;
1229     }
1230    
1231     /* return true if the item given is the currenly selected way */
1232     /* also return false if nothing is selected or the selection is no way */
1233     gboolean map_item_is_selected_way(map_t *map, map_item_t *map_item) {
1234     printf("check if item is the selected way\n");
1235    
1236     if(!map_item) {
1237     printf(" no item requested\n");
1238     return FALSE;
1239     }
1240    
1241 harbaum 154 if(map->selected.object.type == ILLEGAL) {
1242 harbaum 1 printf(" nothing is selected\n");
1243     return FALSE;
1244     }
1245    
1246     /* clicked the highlight directly */
1247 harbaum 154 if(map_item->object.type != WAY) {
1248 harbaum 1 printf(" didn't click way\n");
1249     return FALSE;
1250     }
1251    
1252 harbaum 154 if(map->selected.object.type == WAY) {
1253 harbaum 1 printf(" selected item is a way\n");
1254    
1255 harbaum 154 if(map_item->object.way == map->selected.object.way) {
1256 harbaum 1 printf(" requested item is a selected way\n");
1257     return TRUE;
1258     }
1259     printf(" but it's not the requested one\n");
1260     return FALSE;
1261     }
1262    
1263     printf(" selected item is not a way\n");
1264     return FALSE;
1265     }
1266    
1267    
1268     void map_highlight_refresh(appdata_t *appdata) {
1269     map_t *map = appdata->map;
1270 harbaum 154 object_t old = map->selected.object;
1271 harbaum 1
1272     printf("type to refresh is %d\n", old.type);
1273 harbaum 154 if(old.type == ILLEGAL)
1274 harbaum 1 return;
1275    
1276     map_item_deselect(appdata);
1277 harbaum 154 map_object_select(appdata, &old);
1278 harbaum 1 }
1279    
1280     void map_way_delete(appdata_t *appdata, way_t *way) {
1281 harbaum 161 printf("deleting way #" ITEM_ID_FORMAT " from map and osm\n", way->id);
1282 harbaum 1
1283     /* remove it visually from the screen */
1284     map_item_chain_destroy(&way->map_item_chain);
1285    
1286     /* also remove the visible representation of all nodes (nodes with tags) */
1287     node_chain_t *chain = way->node_chain;
1288     while(chain) {
1289     if(chain->node->map_item_chain)
1290     map_item_chain_destroy(&chain->node->map_item_chain);
1291    
1292     chain = chain->next;
1293     }
1294    
1295     /* and mark it "deleted" in the database */
1296     osm_way_remove_from_relation(appdata->osm, way);
1297 harbaum 235
1298     puts("1");
1299 harbaum 1 osm_way_delete(appdata->osm, &appdata->icon, way, FALSE);
1300 harbaum 235 puts("2");
1301 harbaum 1 }
1302    
1303     static void map_handle_click(appdata_t *appdata, map_t *map) {
1304    
1305     /* problem: on_item may be the highlight itself! So store it! */
1306     map_item_t map_item;
1307     if(map->pen_down.on_item) map_item = *map->pen_down.on_item;
1308 harbaum 154 else map_item.object.type = ILLEGAL;
1309 harbaum 1
1310     /* if we aready have something selected, then de-select it */
1311     map_item_deselect(appdata);
1312    
1313     /* select the clicked item (if there was one) */
1314 harbaum 154 if(map_item.object.type != ILLEGAL) {
1315     switch(map_item.object.type) {
1316     case NODE:
1317     map_node_select(appdata, map_item.object.node);
1318 harbaum 1 break;
1319    
1320 harbaum 154 case WAY:
1321     map_way_select(appdata, map_item.object.way);
1322 harbaum 1 break;
1323    
1324     default:
1325 harbaum 154 g_assert((map_item.object.type == NODE) ||
1326     (map_item.object.type == WAY));
1327 harbaum 1 break;
1328     }
1329     }
1330     }
1331    
1332     static void map_touchnode_update(appdata_t *appdata, gint x, gint y) {
1333     map_t *map = appdata->map;
1334    
1335     map_hl_touchnode_clear(map);
1336    
1337     node_t *cur_node = NULL;
1338    
1339     /* the "current node" which is the one we are working on and which */
1340     /* should not be highlighted depends on the action */
1341     switch(map->action.type) {
1342    
1343     /* in idle mode the dragged node is not highlighted */
1344     case MAP_ACTION_IDLE:
1345     g_assert(map->pen_down.on_item);
1346 harbaum 154 g_assert(map->pen_down.on_item->object.type == NODE);
1347     cur_node = map->pen_down.on_item->object.node;
1348 harbaum 1 break;
1349    
1350     default:
1351     break;
1352     }
1353    
1354    
1355     /* check if we are close to one of the other nodes */
1356     canvas_window2world(appdata->map->canvas, x, y, &x, &y);
1357     node_t *node = appdata->osm->node;
1358     while(!map->touchnode && node) {
1359     /* don't highlight the dragged node itself and don't highlight */
1360     /* deleted ones */
1361     if((node != cur_node) && (!(node->flags & OSM_FLAG_DELETED))) {
1362 harbaum 180 gint nx = abs(x - node->lpos.x);
1363     gint ny = abs(y - node->lpos.y);
1364 harbaum 1
1365     if((nx < map->style->node.radius) && (ny < map->style->node.radius) &&
1366     (nx*nx + ny*ny < map->style->node.radius * map->style->node.radius))
1367     map_hl_touchnode_draw(map, node);
1368     }
1369     node = node->next;
1370     }
1371    
1372     /* during way creation also nodes of the new way */
1373     /* need to be searched */
1374     if(!map->touchnode && map->action.way) {
1375     node_chain_t *chain = map->action.way->node_chain;
1376     while(!map->touchnode && chain && chain->next) {
1377 harbaum 180 gint nx = abs(x - chain->node->lpos.x);
1378     gint ny = abs(y - chain->node->lpos.y);
1379 harbaum 1
1380     if((nx < map->style->node.radius) && (ny < map->style->node.radius) &&
1381     (nx*nx + ny*ny < map->style->node.radius * map->style->node.radius))
1382     map_hl_touchnode_draw(map, chain->node);
1383    
1384     chain = chain->next;
1385     }
1386     }
1387     }
1388    
1389     static void map_button_press(map_t *map, gint x, gint y) {
1390    
1391     printf("left button pressed\n");
1392     map->pen_down.is = TRUE;
1393    
1394     /* save press position */
1395     map->pen_down.at.x = x;
1396     map->pen_down.at.y = y;
1397     map->pen_down.drag = FALSE; // don't assume drag yet
1398    
1399     /* determine wether this press was on an item */
1400     map->pen_down.on_item = map_real_item_at(map, x, y);
1401    
1402     /* check if the clicked item is a highlighted node as the user */
1403     /* might want to drag that */
1404     map->pen_down.on_selected_node = FALSE;
1405     if(map->pen_down.on_item)
1406     map->pen_down.on_selected_node =
1407     map_item_is_selected_node(map, map->pen_down.on_item);
1408    
1409     /* button press */
1410     switch(map->action.type) {
1411    
1412     case MAP_ACTION_WAY_NODE_ADD:
1413     map_edit_way_node_add_highlight(map, map->pen_down.on_item, x, y);
1414     break;
1415    
1416     case MAP_ACTION_WAY_CUT:
1417     map_edit_way_cut_highlight(map, map->pen_down.on_item, x, y);
1418     break;
1419    
1420     case MAP_ACTION_NODE_ADD:
1421     map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1422     break;
1423    
1424     case MAP_ACTION_WAY_ADD:
1425     map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1426     map_touchnode_update(map->appdata, x, y);
1427     break;
1428    
1429     default:
1430     break;
1431     }
1432     }
1433    
1434     /* move the background image (wms data) during wms adjustment */
1435     static void map_bg_adjust(map_t *map, gint x, gint y) {
1436     g_assert(map->appdata);
1437     g_assert(map->appdata->osm);
1438     g_assert(map->appdata->osm->bounds);
1439    
1440     x += map->appdata->osm->bounds->min.x + map->bg.offset.x -
1441     map->pen_down.at.x;
1442     y += map->appdata->osm->bounds->min.y + map->bg.offset.y -
1443     map->pen_down.at.y;
1444    
1445     canvas_image_move(map->bg.item, x, y, map->bg.scale.x, map->bg.scale.y);
1446     }
1447    
1448     static void map_button_release(map_t *map, gint x, gint y) {
1449     map->pen_down.is = FALSE;
1450    
1451     /* before button release is handled */
1452     switch(map->action.type) {
1453     case MAP_ACTION_BG_ADJUST:
1454     map_bg_adjust(map, x, y);
1455     map->bg.offset.x += x - map->pen_down.at.x;
1456     map->bg.offset.y += y - map->pen_down.at.y;
1457     break;
1458    
1459     case MAP_ACTION_IDLE:
1460     /* check if distance to press is above drag limit */
1461     if(!map->pen_down.drag)
1462     map->pen_down.drag = distance_above(map, x, y, MAP_DRAG_LIMIT);
1463    
1464     if(!map->pen_down.drag) {
1465     printf("left button released after click\n");
1466    
1467     map_item_t old_sel = map->selected;
1468     map_handle_click(map->appdata, map);
1469    
1470 harbaum 154 if((old_sel.object.type != ILLEGAL) &&
1471     (old_sel.object.type == map->selected.object.type) &&
1472     (old_sel.object.ptr == map->selected.object.ptr)) {
1473 harbaum 1 printf("re-selected same item of type %d, "
1474 harbaum 154 "pushing it to the bottom\n", old_sel.object.type);
1475 harbaum 1
1476     if(!map->selected.item) {
1477     printf(" item has no visible representation to push\n");
1478     } else {
1479     canvas_item_to_bottom(map->selected.item);
1480    
1481     /* update clicked item, to correctly handle the click */
1482     map->pen_down.on_item =
1483     map_real_item_at(map, map->pen_down.at.x, map->pen_down.at.y);
1484    
1485     map_handle_click(map->appdata, map);
1486     }
1487     }
1488     } else {
1489     printf("left button released after drag\n");
1490    
1491     /* just scroll if we didn't drag an selected item */
1492     if(!map->pen_down.on_selected_node)
1493     map_do_scroll(map, x, y);
1494     else {
1495     printf("released after dragging node\n");
1496     map_hl_cursor_clear(map);
1497    
1498     /* now actually move the node */
1499     map_edit_node_move(map->appdata, map->pen_down.on_item, x, y);
1500     }
1501     }
1502     break;
1503    
1504     case MAP_ACTION_NODE_ADD:
1505     printf("released after NODE ADD\n");
1506     map_hl_cursor_clear(map);
1507    
1508     /* convert mouse position to canvas (world) position */
1509     canvas_window2world(map->canvas, x, y, &x, &y);
1510    
1511     node_t *node = NULL;
1512     if(!osm_position_within_bounds(map->appdata->osm, x, y))
1513     map_outside_error(map->appdata);
1514     else {
1515     node = osm_node_new(map->appdata->osm, x, y);
1516     osm_node_attach(map->appdata->osm, node);
1517     map_node_draw(map, node);
1518     }
1519     map_action_set(map->appdata, MAP_ACTION_IDLE);
1520    
1521     map_item_deselect(map->appdata);
1522    
1523     if(node) {
1524     map_node_select(map->appdata, node);
1525    
1526     /* let the user specify some tags for the new node */
1527     info_dialog(GTK_WIDGET(map->appdata->window), map->appdata, NULL);
1528     }
1529     break;
1530    
1531     case MAP_ACTION_WAY_ADD:
1532     printf("released after WAY ADD\n");
1533     map_hl_cursor_clear(map);
1534    
1535     map_edit_way_add_segment(map, x, y);
1536     break;
1537    
1538     case MAP_ACTION_WAY_NODE_ADD:
1539     printf("released after WAY NODE ADD\n");
1540     map_hl_cursor_clear(map);
1541    
1542     map_edit_way_node_add(map, x, y);
1543     break;
1544    
1545     case MAP_ACTION_WAY_CUT:
1546     printf("released after WAY CUT\n");
1547     map_hl_cursor_clear(map);
1548    
1549     map_edit_way_cut(map, x, y);
1550     break;
1551    
1552    
1553     default:
1554     break;
1555     }
1556     }
1557    
1558     static gboolean map_button_event(GtkWidget *widget, GdkEventButton *event,
1559     gpointer data) {
1560     appdata_t *appdata = (appdata_t*)data;
1561     map_t *map = appdata->map;
1562    
1563     if(!appdata->osm) return FALSE;
1564    
1565     if(event->button == 1) {
1566     gint x = event->x, y = event->y;
1567    
1568     if(event->type == GDK_BUTTON_PRESS)
1569     map_button_press(map, x, y);
1570    
1571     if(event->type == GDK_BUTTON_RELEASE)
1572     map_button_release(map, x, y);
1573     }
1574    
1575     return FALSE; /* forward to further processing */
1576     }
1577    
1578     static gboolean map_motion_notify_event(GtkWidget *widget,
1579     GdkEventMotion *event, gpointer data) {
1580     appdata_t *appdata = (appdata_t*)data;
1581     map_t *map = appdata->map;
1582     gint x, y;
1583     GdkModifierType state;
1584    
1585     if(!appdata->osm) return FALSE;
1586    
1587     #ifdef USE_HILDON
1588     /* reduce update frequency on hildon to keep screen update fluid */
1589     static guint32 last_time = 0;
1590    
1591 harbaum 24 if(event->time - last_time < 250) return FALSE;
1592 harbaum 1 last_time = event->time;
1593     #endif
1594    
1595     if(!map->pen_down.is)
1596     return FALSE;
1597    
1598 harbaum 78 #ifndef USE_GOOCANVAS
1599 harbaum 1 /* handle hints, hints are handled by goocanvas directly */
1600     if(event->is_hint)
1601     gdk_window_get_pointer(event->window, &x, &y, &state);
1602     else
1603     #endif
1604     {
1605     x = event->x;
1606     y = event->y;
1607     state = event->state;
1608     }
1609    
1610     /* check if distance to press is above drag limit */
1611     if(!map->pen_down.drag)
1612     map->pen_down.drag = distance_above(map, x, y, MAP_DRAG_LIMIT);
1613    
1614     /* drag */
1615     switch(map->action.type) {
1616     case MAP_ACTION_BG_ADJUST:
1617     map_bg_adjust(map, x, y);
1618     break;
1619    
1620     case MAP_ACTION_IDLE:
1621     if(map->pen_down.drag) {
1622     /* just scroll if we didn't drag an selected item */
1623     if(!map->pen_down.on_selected_node)
1624     map_do_scroll(map, x, y);
1625     else {
1626     map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1627     map_touchnode_update(appdata, x, y);
1628     }
1629     }
1630     break;
1631    
1632     case MAP_ACTION_NODE_ADD:
1633     map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1634     break;
1635 harbaum 15
1636 harbaum 1 case MAP_ACTION_WAY_ADD:
1637     map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1638     map_touchnode_update(appdata, x, y);
1639     break;
1640    
1641     case MAP_ACTION_WAY_NODE_ADD:
1642     map_hl_cursor_clear(map);
1643 harbaum 105 map_item_t *item = map_item_at(map, x, y);
1644 harbaum 1 if(item) map_edit_way_node_add_highlight(map, item, x, y);
1645     break;
1646    
1647     case MAP_ACTION_WAY_CUT:
1648     map_hl_cursor_clear(map);
1649 harbaum 105 item = map_item_at(map, x, y);
1650 harbaum 1 if(item) map_edit_way_cut_highlight(map, item, x, y);
1651     break;
1652    
1653     default:
1654     break;
1655     }
1656    
1657    
1658     return FALSE; /* forward to further processing */
1659     }
1660    
1661     gboolean map_key_press_event(appdata_t *appdata, GdkEventKey *event) {
1662    
1663     if(!appdata->osm) return FALSE;
1664    
1665     /* map needs to be there to handle buttons */
1666     if(!appdata->map->canvas)
1667     return FALSE;
1668    
1669     if(event->type == GDK_KEY_PRESS) {
1670     gdouble zoom = 0;
1671     switch(event->keyval) {
1672    
1673     case GDK_Left:
1674     map_do_scroll_step(appdata->map, -50, 0);
1675     break;
1676    
1677     case GDK_Right:
1678     map_do_scroll_step(appdata->map, +50, 0);
1679     break;
1680    
1681     case GDK_Up:
1682     map_do_scroll_step(appdata->map, 0, -50);
1683     break;
1684    
1685     case GDK_Down:
1686     map_do_scroll_step(appdata->map, 0, +50);
1687     break;
1688    
1689     case GDK_Return: // same as HILDON_HARDKEY_SELECT
1690     /* if the ok button is enabled, call its function */
1691     if(GTK_WIDGET_FLAGS(appdata->iconbar->ok) & GTK_SENSITIVE)
1692     map_action_ok(appdata);
1693     /* otherwise if info is enabled call that */
1694     else if(GTK_WIDGET_FLAGS(appdata->iconbar->info) & GTK_SENSITIVE)
1695     info_dialog(GTK_WIDGET(appdata->window), appdata, NULL);
1696     break;
1697    
1698     case GDK_Escape: // same as HILDON_HARDKEY_ESC
1699     /* if the cancel button is enabled, call its function */
1700     if(GTK_WIDGET_FLAGS(appdata->iconbar->cancel) & GTK_SENSITIVE)
1701     map_action_cancel(appdata);
1702     break;
1703    
1704     #ifdef USE_HILDON
1705     case HILDON_HARDKEY_INCREASE:
1706     #else
1707     case '+':
1708     #endif
1709     zoom = appdata->map->state->zoom;
1710     zoom *= ZOOM_FACTOR_BUTTON;
1711     map_set_zoom(appdata->map, zoom, TRUE);
1712     printf("zoom is now %f (1:%d)\n", zoom, (int)zoom_to_scaledn(zoom));
1713     return TRUE;
1714     break;
1715    
1716     #ifdef USE_HILDON
1717     case HILDON_HARDKEY_DECREASE:
1718     #else
1719     case '-':
1720     #endif
1721     zoom = appdata->map->state->zoom;
1722     zoom /= ZOOM_FACTOR_BUTTON;
1723     map_set_zoom(appdata->map, zoom, TRUE);
1724     printf("zoom is now %f (1:%d)\n", zoom, (int)zoom_to_scaledn(zoom));
1725     return TRUE;
1726     break;
1727    
1728     default:
1729     printf("key event %d\n", event->keyval);
1730     break;
1731     }
1732     }
1733     return FALSE;
1734     }
1735    
1736 harbaum 173 void map_state_reset(map_state_t *state) {
1737     if(!state) return;
1738    
1739     state->zoom = 0.25;
1740     state->detail = 1.0;
1741    
1742     /* todo: try to scroll to center of screen */
1743     state->scroll_offset.x = 0;
1744     state->scroll_offset.y = 0;
1745     }
1746    
1747     map_state_t *map_state_new(void) {
1748     map_state_t *state = g_new0(map_state_t, 1);
1749     map_state_reset(state);
1750     return state;
1751     }
1752    
1753 harbaum 1 GtkWidget *map_new(appdata_t *appdata) {
1754     map_t *map = appdata->map = g_new0(map_t, 1);
1755    
1756     map->style = style_load(appdata, appdata->settings->style);
1757 harbaum 22 if(!map->style) {
1758     errorf(NULL, _("Unable to load valid style, terminating."));
1759     g_free(map);
1760     return NULL;
1761     }
1762 harbaum 1
1763     if(appdata->project && appdata->project->map_state) {
1764     printf("Using projects map state\n");
1765     map->state = appdata->project->map_state;
1766     } else {
1767     printf("Creating new map state\n");
1768 harbaum 173 map->state = map_state_new();
1769 harbaum 1 }
1770    
1771     map->state->refcount++;
1772    
1773     map->pen_down.at.x = -1;
1774     map->pen_down.at.y = -1;
1775     map->appdata = appdata;
1776     map->action.type = MAP_ACTION_IDLE;
1777    
1778 harbaum 193 map->canvas = canvas_new();
1779 harbaum 1
1780 harbaum 103 GtkWidget *canvas_widget = canvas_get_widget(map->canvas);
1781 harbaum 1
1782 harbaum 103 gtk_widget_set_events(canvas_widget,
1783 harbaum 1 GDK_BUTTON_PRESS_MASK
1784     | GDK_BUTTON_RELEASE_MASK
1785     | GDK_SCROLL_MASK
1786     | GDK_POINTER_MOTION_MASK
1787     | GDK_POINTER_MOTION_HINT_MASK);
1788    
1789 harbaum 103 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1790 harbaum 1 "button_press_event", G_CALLBACK(map_button_event), appdata);
1791 harbaum 103 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1792 harbaum 1 "button_release_event", G_CALLBACK(map_button_event), appdata);
1793 harbaum 103 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1794 harbaum 1 "motion_notify_event", G_CALLBACK(map_motion_notify_event), appdata);
1795 harbaum 103 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1796 harbaum 1 "scroll_event", G_CALLBACK(map_scroll_event), appdata);
1797    
1798 harbaum 103 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1799 harbaum 1 "destroy", G_CALLBACK(map_destroy_event), appdata);
1800    
1801 harbaum 103 return canvas_widget;
1802 harbaum 1 }
1803    
1804     void map_init(appdata_t *appdata) {
1805     map_t *map = appdata->map;
1806    
1807 harbaum 193 /* update canvas background color */
1808     canvas_set_background(map->canvas, map->style->background.color);
1809    
1810 harbaum 1 /* set initial zoom */
1811     map_set_zoom(map, map->state->zoom, FALSE);
1812     josm_elemstyles_colorize_world(map->style, appdata->osm);
1813    
1814     map_draw(map, appdata->osm);
1815    
1816     float mult = appdata->map->style->frisket.mult;
1817     canvas_set_bounds(map->canvas,
1818     mult*appdata->osm->bounds->min.x,
1819     mult*appdata->osm->bounds->min.y,
1820     mult*appdata->osm->bounds->max.x,
1821     mult*appdata->osm->bounds->max.y);
1822    
1823 harbaum 103 printf("restore scroll position %d/%d\n",
1824 harbaum 1 map->state->scroll_offset.x, map->state->scroll_offset.y);
1825    
1826 harbaum 103 map_limit_scroll(map, CANVAS_UNIT_METER,
1827     &map->state->scroll_offset.x, &map->state->scroll_offset.y);
1828     canvas_scroll_to(map->canvas, CANVAS_UNIT_METER,
1829     map->state->scroll_offset.x, map->state->scroll_offset.y);
1830 harbaum 1 }
1831    
1832    
1833 harbaum 103 void map_clear(appdata_t *appdata, gint group_mask) {
1834 harbaum 1 map_t *map = appdata->map;
1835    
1836     printf("freeing map contents\n");
1837    
1838     map_free_map_item_chains(appdata);
1839    
1840     /* remove a possibly existing highlight */
1841     map_item_deselect(appdata);
1842    
1843 harbaum 103 canvas_erase(map->canvas, group_mask);
1844 harbaum 1 }
1845    
1846     void map_paint(appdata_t *appdata) {
1847     map_t *map = appdata->map;
1848    
1849     josm_elemstyles_colorize_world(map->style, appdata->osm);
1850     map_draw(map, appdata->osm);
1851     }
1852    
1853     /* called from several icons like e.g. "node_add" */
1854     void map_action_set(appdata_t *appdata, map_action_t action) {
1855     printf("map action set to %d\n", action);
1856    
1857     appdata->map->action.type = action;
1858    
1859     /* enable/disable ok/cancel buttons */
1860     // MAP_ACTION_IDLE=0, NODE_ADD, BG_ADJUST, WAY_ADD, WAY_NODE_ADD, WAY_CUT
1861     const gboolean ok_state[] = { FALSE, FALSE, TRUE, FALSE, FALSE, FALSE };
1862     const gboolean cancel_state[] = { FALSE, TRUE, TRUE, TRUE, TRUE, TRUE };
1863    
1864     g_assert(MAP_ACTION_NUM == sizeof(ok_state)/sizeof(gboolean));
1865     g_assert(action < sizeof(ok_state)/sizeof(gboolean));
1866    
1867     icon_bar_map_cancel_ok(appdata, cancel_state[action], ok_state[action]);
1868    
1869     switch(action) {
1870     case MAP_ACTION_BG_ADJUST:
1871     /* an existing selection only causes confusion ... */
1872     map_item_deselect(appdata);
1873     break;
1874    
1875     case MAP_ACTION_WAY_ADD:
1876     printf("starting new way\n");
1877    
1878     /* remember if there was a way selected */
1879     way_t *way_sel = NULL;
1880 harbaum 154 if(appdata->map->selected.object.type == WAY)
1881     way_sel = appdata->map->selected.object.way;
1882 harbaum 1
1883     map_item_deselect(appdata);
1884     map_edit_way_add_begin(appdata->map, way_sel);
1885     break;
1886    
1887     case MAP_ACTION_NODE_ADD:
1888     map_item_deselect(appdata);
1889     break;
1890    
1891     default:
1892     break;
1893     }
1894    
1895     icon_bar_map_action_idle(appdata, action == MAP_ACTION_IDLE);
1896     gtk_widget_set_sensitive(appdata->menu_item_wms_adjust,
1897     action == MAP_ACTION_IDLE);
1898    
1899     const char *str_state[] = {
1900     NULL,
1901     _("Place a node"),
1902     _("Adjust background image position"),
1903     _("Place first node of new way"),
1904     _("Place node on selected way"),
1905     _("Select segment to cut way"),
1906     };
1907    
1908     g_assert(MAP_ACTION_NUM == sizeof(str_state)/sizeof(char*));
1909    
1910     statusbar_set(appdata, str_state[action], FALSE);
1911     }
1912    
1913    
1914     void map_action_cancel(appdata_t *appdata) {
1915     map_t *map = appdata->map;
1916    
1917     switch(map->action.type) {
1918     case MAP_ACTION_WAY_ADD:
1919     map_edit_way_add_cancel(map);
1920     break;
1921    
1922     case MAP_ACTION_BG_ADJUST:
1923     /* undo all changes to bg_offset */
1924     map->bg.offset.x = appdata->project->wms_offset.x;
1925     map->bg.offset.y = appdata->project->wms_offset.y;
1926    
1927     gint x = appdata->osm->bounds->min.x + map->bg.offset.x;
1928     gint y = appdata->osm->bounds->min.y + map->bg.offset.y;
1929     canvas_image_move(map->bg.item, x, y, map->bg.scale.x, map->bg.scale.y);
1930     break;
1931    
1932     default:
1933     break;
1934     }
1935    
1936     map_action_set(appdata, MAP_ACTION_IDLE);
1937     }
1938    
1939     void map_action_ok(appdata_t *appdata) {
1940     map_t *map = appdata->map;
1941    
1942     /* reset action now as this erases the statusbar and some */
1943     /* of the actions may set it */
1944     map_action_t type = map->action.type;
1945     map_action_set(appdata, MAP_ACTION_IDLE);
1946    
1947     switch(type) {
1948     case MAP_ACTION_WAY_ADD:
1949     map_edit_way_add_ok(map);
1950     break;
1951    
1952     case MAP_ACTION_BG_ADJUST:
1953     /* save changes to bg_offset in project */
1954     appdata->project->wms_offset.x = map->bg.offset.x;
1955     appdata->project->wms_offset.y = map->bg.offset.y;
1956     break;
1957    
1958     default:
1959     break;
1960     }
1961     }
1962    
1963     /* called from icon "trash" */
1964     void map_delete_selected(appdata_t *appdata) {
1965     map_t *map = appdata->map;
1966    
1967     if(!yes_no_f(GTK_WIDGET(appdata->window),
1968     appdata, MISC_AGAIN_ID_DELETE, MISC_AGAIN_FLAG_DONT_SAVE_NO,
1969     _("Delete selected object?"),
1970     _("Do you really want to delete the selected object?")))
1971     return;
1972    
1973     /* work on local copy since de-selecting destroys the selection */
1974     map_item_t item = map->selected;
1975    
1976     /* deleting the selected item de-selects it ... */
1977     map_item_deselect(appdata);
1978    
1979 harbaum 235 undo_open_new_state(appdata, UNDO_DELETE, &item.object);
1980 harbaum 54
1981 harbaum 154 switch(item.object.type) {
1982     case NODE:
1983 harbaum 161 printf("request to delete node #" ITEM_ID_FORMAT "\n",
1984     item.object.node->id);
1985 harbaum 1
1986 harbaum 235 undo_append_object(appdata, UNDO_DELETE, &item.object);
1987    
1988 harbaum 1 /* check if this node is part of a way with two nodes only. */
1989     /* we cannot delete this as this would also delete the way */
1990 harbaum 154 way_chain_t *way_chain = osm_node_to_way(appdata->osm, item.object.node);
1991 harbaum 1 if(way_chain) {
1992     gboolean short_way = FALSE;
1993    
1994     /* free the chain of ways */
1995     while(way_chain && !short_way) {
1996     way_chain_t *next = way_chain->next;
1997    
1998     if(osm_way_number_of_nodes(way_chain->way) <= 2)
1999     short_way = TRUE;
2000    
2001     g_free(way_chain);
2002     way_chain = next;
2003     }
2004    
2005     if(short_way) {
2006     if(!yes_no_f(GTK_WIDGET(appdata->window), NULL, 0, 0,
2007     _("Delete node in short way(s)?"),
2008     _("Deleting this node will also delete one or more ways "
2009     "since they'll contain only one node afterwards. "
2010     "Do you really want this?")))
2011     return;
2012     }
2013     }
2014    
2015     /* remove it visually from the screen */
2016 harbaum 154 map_item_chain_destroy(&item.object.node->map_item_chain);
2017 harbaum 1
2018     /* and mark it "deleted" in the database */
2019 harbaum 154 osm_node_remove_from_relation(appdata->osm, item.object.node);
2020 harbaum 1 way_chain_t *chain = osm_node_delete(appdata->osm,
2021 harbaum 154 &appdata->icon, item.object.node, FALSE, TRUE);
2022 harbaum 1
2023     /* redraw all affected ways */
2024     while(chain) {
2025     way_chain_t *next = chain->next;
2026    
2027     if(osm_way_number_of_nodes(chain->way) == 1) {
2028     /* this way now only contains one node and thus isn't a valid */
2029     /* way anymore. So it'll also get deleted (which in turn may */
2030     /* cause other nodes to be deleted as well) */
2031 harbaum 235 undo_append_way(appdata, UNDO_DELETE, chain->way);
2032 harbaum 1 map_way_delete(appdata, chain->way);
2033     } else {
2034     map_item_t item;
2035 harbaum 154 item.object.type = WAY;
2036     item.object.way = chain->way;
2037 harbaum 235 undo_append_object(appdata, UNDO_MODIFY, &(item.object));
2038 harbaum 1 map_item_redraw(appdata, &item);
2039     }
2040    
2041     g_free(chain);
2042    
2043     chain = next;
2044     }
2045    
2046     break;
2047    
2048 harbaum 154 case WAY:
2049 harbaum 161 printf("request to delete way #" ITEM_ID_FORMAT "\n", item.object.way->id);
2050 harbaum 154 map_way_delete(appdata, item.object.way);
2051 harbaum 1 break;
2052    
2053     default:
2054 harbaum 154 g_assert((item.object.type == NODE) ||
2055     (item.object.type == WAY));
2056 harbaum 1 break;
2057     }
2058 harbaum 235 undo_close_state(appdata);
2059 harbaum 1 }
2060    
2061     /* ----------------------- track related stuff ----------------------- */
2062    
2063 harbaum 156 static gboolean track_pos2lpos(bounds_t *bounds, pos_t *pos, lpos_t *lpos) {
2064     pos2lpos(bounds, pos, lpos);
2065    
2066     /* check if point is within bounds */
2067     return ((lpos->x >= bounds->min.x) && (lpos->x <= bounds->max.x) &&
2068     (lpos->y >= bounds->min.y) && (lpos->y <= bounds->max.y));
2069     }
2070    
2071 harbaum 1 void map_track_draw_seg(map_t *map, track_seg_t *seg) {
2072 harbaum 156 bounds_t *bounds = map->appdata->osm->bounds;
2073    
2074 harbaum 1 /* a track_seg needs at least 2 points to be drawn */
2075     guint pnum = track_seg_points(seg);
2076     printf("seg of length %d\n", pnum);
2077    
2078 harbaum 156 if(!pnum)
2079     return;
2080 harbaum 1
2081 harbaum 156 /* nothing should have been drawn by now ... */
2082     g_assert(!seg->item_chain);
2083    
2084     track_item_chain_t **itemP = &seg->item_chain;
2085     track_point_t *track_point = seg->track_point;
2086     while(track_point) {
2087     lpos_t lpos;
2088 harbaum 1
2089 harbaum 156 /* skip all points not on screen */
2090     track_point_t *last = NULL;
2091     while(track_point && !track_pos2lpos(bounds, &track_point->pos, &lpos)) {
2092     last = track_point;
2093     track_point = track_point->next;
2094     }
2095    
2096     int visible = 0;
2097    
2098     /* count nodes that _are_ on screen */
2099     track_point_t *tmp = track_point;
2100     while(tmp && track_pos2lpos(bounds, &tmp->pos, &lpos)) {
2101     tmp = tmp->next;
2102     visible++;
2103     }
2104    
2105     /* actually start drawing with the last position that was offscreen */
2106     /* so the track nicely enters the viewing area */
2107     if(last) {
2108     track_point = last;
2109     visible++;
2110     }
2111    
2112     /* also use last one that's offscreen to nicely leave the visible area */
2113     if(tmp && tmp->next)
2114     visible++;
2115    
2116 harbaum 1 /* allocate space for nodes */
2117 harbaum 156 canvas_points_t *points = canvas_points_new(visible);
2118 harbaum 1
2119 harbaum 156 printf("visible are %d\n", visible);
2120     int point;
2121     for(point=0;point<visible;point++) {
2122     track_pos2lpos(bounds, &track_point->pos, &lpos);
2123    
2124     points->coords[2*point+0] = lpos.x;
2125     points->coords[2*point+1] = lpos.y;
2126 harbaum 1 track_point = track_point->next;
2127     }
2128    
2129 harbaum 156 *itemP = g_new0(track_item_chain_t, 1);
2130     (*itemP)->item = canvas_polyline_new(map->canvas, CANVAS_GROUP_TRACK,
2131     points, map->style->track.width, map->style->track.color);
2132     itemP = &(*itemP)->next;
2133 harbaum 1
2134     canvas_points_free(points);
2135     }
2136     }
2137    
2138 harbaum 156 /* update the last visible fragment of this segment since a */
2139     /* gps position may have been added */
2140 harbaum 1 void map_track_update_seg(map_t *map, track_seg_t *seg) {
2141 harbaum 156 bounds_t *bounds = map->appdata->osm->bounds;
2142    
2143     printf("-- APPENDING TO TRACK --\n");
2144    
2145 harbaum 1 /* a track_seg needs at least 2 points to be drawn */
2146     guint pnum = track_seg_points(seg);
2147     printf("seg of length %d\n", pnum);
2148    
2149 harbaum 156 /* there are two cases: either the second last point was on screen */
2150     /* or it wasn't. We'll have to start a new screen item if the latter */
2151     /* is the case */
2152    
2153     /* search last point */
2154     track_point_t *begin = seg->track_point, *second_last = seg->track_point;
2155     lpos_t lpos;
2156     while(second_last && second_last->next && second_last->next->next) {
2157     if(!track_pos2lpos(bounds, &second_last->pos, &lpos))
2158     begin = second_last;
2159    
2160     second_last = second_last->next;
2161     }
2162     track_point_t *last = second_last->next;
2163    
2164     /* since we are updating an existing track, it sure has at least two */
2165     /* points, second_last must be valid and its "next" (last) also */
2166     g_assert(second_last);
2167     g_assert(last);
2168    
2169     /* check if the last and second_last points are visible */
2170     gboolean last_is_visible =
2171     track_pos2lpos(bounds, &last->pos, &lpos);
2172     gboolean second_last_is_visible =
2173     track_pos2lpos(bounds, &second_last->pos, &lpos);
2174    
2175     /* if both are invisible, then nothing has changed on screen */
2176     if(!last_is_visible && !second_last_is_visible) {
2177     printf("second_last and last entry are invisible -> doing nothing\n");
2178     return;
2179     }
2180    
2181     /* search last element in item chain */
2182     track_item_chain_t *item = seg->item_chain;
2183     while(item && item->next)
2184     item = item->next;
2185    
2186     if(second_last_is_visible) {
2187     /* there must be something already on the screen and there must */
2188     /* be visible nodes in the chain */
2189     g_assert(item);
2190     g_assert(begin);
2191    
2192     printf("second_last is visible -> append\n");
2193 harbaum 1
2194 harbaum 156 /* count points to be placed */
2195     int npoints = 0;
2196     track_point_t *tmp = begin;
2197     while(tmp) {
2198     tmp = tmp->next;
2199     npoints++;
2200     }
2201    
2202     printf("updating last segment to %d points\n", npoints);
2203    
2204     canvas_points_t *points = canvas_points_new(npoints);
2205    
2206     gint point = 0;
2207     while(begin) {
2208     track_pos2lpos(bounds, &begin->pos, &lpos);
2209     canvas_point_set_pos(points, point++, &lpos);
2210     begin = begin->next;
2211     }
2212 harbaum 1
2213 harbaum 156 canvas_item_set_points(item->item, points);
2214     canvas_points_free(points);
2215    
2216     } else {
2217     printf("second last is invisible -> start new screen segment\n");
2218    
2219     /* the search for the "begin" ends with the second_last item */
2220     /* verify the last one also */
2221     if(begin->next && !track_pos2lpos(bounds, &begin->next->pos, &lpos))
2222     begin = begin->next;
2223    
2224     item->next = g_new0(track_item_chain_t, 1);
2225     item = item->next;
2226    
2227     /* count points to be placed */
2228     int npoints = 0;
2229     track_point_t *tmp = begin;
2230     while(tmp) {
2231     tmp = tmp->next;
2232     npoints++;
2233 harbaum 1 }
2234 harbaum 156
2235     printf("attaching new segment with %d points\n", npoints);
2236    
2237     canvas_points_t *points = canvas_points_new(npoints);
2238    
2239     gint point = 0;
2240     while(begin) {
2241     track_pos2lpos(bounds, &begin->pos, &lpos);
2242     canvas_point_set_pos(points, point++, &lpos);
2243     begin = begin->next;
2244     }
2245 harbaum 1
2246 harbaum 156 item->item = canvas_polyline_new(map->canvas, CANVAS_GROUP_TRACK,
2247     points, map->style->track.width, map->style->track.color);
2248    
2249 harbaum 1 canvas_points_free(points);
2250     }
2251 harbaum 156
2252 harbaum 1 }
2253    
2254     void map_track_draw(map_t *map, track_t *track) {
2255     track_seg_t *seg = track->track_seg;
2256    
2257     /* draw all segments */
2258     while(seg) {
2259     map_track_draw_seg(map, seg);
2260     seg = seg->next;
2261     }
2262     }
2263    
2264     void map_track_remove(appdata_t *appdata) {
2265     track_t *track = appdata->track.track;
2266    
2267     printf("removing track\n");
2268    
2269     g_assert(track);
2270    
2271     /* remove all segments */
2272     track_seg_t *seg = track->track_seg;
2273     while(seg) {
2274 harbaum 156 track_item_chain_t *item = seg->item_chain;
2275     while(item) {
2276     track_item_chain_t *next = item->next;
2277     canvas_item_destroy(item->item);
2278     item = next;
2279 harbaum 1 }
2280 harbaum 156
2281     seg->item_chain = NULL;
2282 harbaum 1 seg = seg->next;
2283     }
2284     }
2285    
2286 harbaum 156 void map_track_pos(appdata_t *appdata, pos_t *pos) {
2287 harbaum 1 if(appdata->track.gps_item) {
2288     canvas_item_destroy(appdata->track.gps_item);
2289     appdata->track.gps_item = NULL;
2290     }
2291    
2292 harbaum 156 if(pos) {
2293     lpos_t lpos;
2294     pos2lpos(appdata->osm->bounds, pos, &lpos);
2295    
2296 harbaum 103 appdata->track.gps_item =
2297     canvas_circle_new(appdata->map->canvas, CANVAS_GROUP_GPS,
2298 harbaum 156 lpos.x, lpos.y, appdata->map->style->track.width/2.0, 0,
2299 harbaum 22 appdata->map->style->track.gps_color, NO_COLOR);
2300 harbaum 156 }
2301 harbaum 1 }
2302    
2303     /* ------------------- map background ------------------ */
2304    
2305     void map_remove_bg_image(map_t *map) {
2306     if(!map) return;
2307    
2308     if(map->bg.item) {
2309     canvas_item_destroy(map->bg.item);
2310     map->bg.item = NULL;
2311     }
2312     }
2313    
2314     static gint map_bg_item_destroy_event(GtkWidget *widget, gpointer data) {
2315     map_t *map = (map_t*)data;
2316    
2317     /* destroying background item */
2318    
2319     map->bg.item = NULL;
2320     if(map->bg.pix) {
2321     printf("destroying background item\n");
2322     gdk_pixbuf_unref(map->bg.pix);
2323     map->bg.pix = NULL;
2324     }
2325     return FALSE;
2326     }
2327    
2328     void map_set_bg_image(map_t *map, char *filename) {
2329     bounds_t *bounds = map->appdata->osm->bounds;
2330    
2331     map_remove_bg_image(map);
2332    
2333     map->bg.pix = gdk_pixbuf_new_from_file(filename, NULL);
2334    
2335     /* calculate required scale factor */
2336     map->bg.scale.x = (float)(bounds->max.x - bounds->min.x)/
2337     (float)gdk_pixbuf_get_width(map->bg.pix);
2338     map->bg.scale.y = (float)(bounds->max.y - bounds->min.y)/
2339     (float)gdk_pixbuf_get_height(map->bg.pix);
2340    
2341 harbaum 103 map->bg.item = canvas_image_new(map->canvas, CANVAS_GROUP_BG, map->bg.pix,
2342 harbaum 1 bounds->min.x, bounds->min.y, map->bg.scale.x, map->bg.scale.y);
2343    
2344     canvas_item_destroy_connect(map->bg.item,
2345     G_CALLBACK(map_bg_item_destroy_event), map);
2346     }
2347    
2348    
2349     /* -------- hide and show objects (for performance reasons) ------- */
2350    
2351     void map_hide_selected(appdata_t *appdata) {
2352     map_t *map = appdata->map;
2353     if(!map) return;
2354    
2355 harbaum 154 if(map->selected.object.type != WAY) {
2356 harbaum 1 printf("selected item is not a way\n");
2357     return;
2358     }
2359    
2360 harbaum 154 way_t *way = map->selected.object.way;
2361 harbaum 161 printf("hiding way #" ITEM_ID_FORMAT "\n", way->id);
2362 harbaum 1
2363     map_item_deselect(appdata);
2364     way->flags |= OSM_FLAG_HIDDEN;
2365     map_item_chain_destroy(&way->map_item_chain);
2366    
2367     gtk_widget_set_sensitive(appdata->menu_item_map_show_all, TRUE);
2368     }
2369    
2370     void map_show_all(appdata_t *appdata) {
2371     map_t *map = appdata->map;
2372     if(!map) return;
2373    
2374     way_t *way = appdata->osm->way;
2375     while(way) {
2376     if(way->flags & OSM_FLAG_HIDDEN) {
2377     way->flags &= ~OSM_FLAG_HIDDEN;
2378     map_way_draw(map, way);
2379     }
2380    
2381     way = way->next;
2382     }
2383    
2384     gtk_widget_set_sensitive(appdata->menu_item_map_show_all, FALSE);
2385     }
2386 harbaum 153
2387 harbaum 162 static void map_detail_change(map_t *map, float detail) {
2388     appdata_t *appdata = map->appdata;
2389    
2390     /* deselecting anything allows us not to care about automatic deselection */
2391     /* as well as items becoming invisible by the detail change */
2392     map_item_deselect(appdata);
2393    
2394     map->state->detail = detail;
2395     printf("changing detail factor to %f\n", map->state->detail);
2396    
2397 harbaum 177 banner_busy_start(appdata, 1, _("Redrawing"));
2398 harbaum 162 map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
2399     map_paint(appdata);
2400     banner_busy_stop(appdata);
2401     }
2402    
2403     #define DETAIL_STEP 1.5
2404    
2405     void map_detail_increase(map_t *map) {
2406     if(!map) return;
2407     map_detail_change(map, map->state->detail * DETAIL_STEP);
2408     }
2409    
2410     void map_detail_decrease(map_t *map) {
2411     if(!map) return;
2412     map_detail_change(map, map->state->detail / DETAIL_STEP);
2413     }
2414    
2415     void map_detail_normal(map_t *map) {
2416     if(!map) return;
2417     map_detail_change(map, 1.0);
2418     }
2419    
2420 achadwick 13 // vim:et:ts=8:sw=2:sts=2:ai