Parent Directory | Revision Log
More undo handling
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 | /* | ||
21 | * diff.c - generate and restore changes on the current data set | ||
22 | */ | ||
23 | |||
24 | #include "appdata.h" | ||
25 | |||
26 | #include <libxml/parser.h> | ||
27 | #include <libxml/tree.h> | ||
28 | |||
29 | #if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED) | ||
30 | #error "libxml doesn't support required tree or output" | ||
31 | #endif | ||
32 | |||
33 | static void diff_save_tags(tag_t *tag, xmlNodePtr node) { | ||
34 | while(tag) { | ||
35 | xmlNodePtr tag_node = xmlNewChild(node, NULL, | ||
36 | BAD_CAST "tag", NULL); | ||
37 | xmlNewProp(tag_node, BAD_CAST "k", BAD_CAST tag->key); | ||
38 | xmlNewProp(tag_node, BAD_CAST "v", BAD_CAST tag->value); | ||
39 | tag = tag->next; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | static void diff_save_state_n_id(int flags, xmlNodePtr node, item_id_t id) { | ||
44 | if(flags & OSM_FLAG_DELETED) | ||
45 | xmlNewProp(node, BAD_CAST "state", BAD_CAST "deleted"); | ||
46 | else if(flags & OSM_FLAG_NEW) | ||
47 | xmlNewProp(node, BAD_CAST "state", BAD_CAST "new"); | ||
48 | |||
49 | /* all items need an id */ | ||
50 | harbaum | 161 | char *id_str = g_strdup_printf(ITEM_ID_FORMAT, id); |
51 | harbaum | 1 | xmlNewProp(node, BAD_CAST "id", BAD_CAST id_str); |
52 | g_free(id_str); | ||
53 | } | ||
54 | |||
55 | static void diff_save_nodes(node_t *node, xmlNodePtr root_node) { | ||
56 | /* store all modfied nodes */ | ||
57 | while(node) { | ||
58 | if(node->flags) { | ||
59 | xmlNodePtr node_node = xmlNewChild(root_node, NULL, | ||
60 | BAD_CAST "node", NULL); | ||
61 | |||
62 | harbaum | 239 | diff_save_state_n_id(node->flags, node_node, OSM_ID(node)); |
63 | harbaum | 1 | |
64 | if(!(node->flags & OSM_FLAG_DELETED)) { | ||
65 | char str[32]; | ||
66 | |||
67 | /* additional info is only required if the node hasn't been deleted */ | ||
68 | harbaum | 156 | g_ascii_formatd(str, sizeof(str), LL_FORMAT, node->pos.lat); |
69 | harbaum | 1 | xmlNewProp(node_node, BAD_CAST "lat", BAD_CAST str); |
70 | harbaum | 156 | g_ascii_formatd(str, sizeof(str), LL_FORMAT, node->pos.lon); |
71 | harbaum | 1 | xmlNewProp(node_node, BAD_CAST "lon", BAD_CAST str); |
72 | harbaum | 239 | snprintf(str, sizeof(str), "%lu", OSM_TIME(node)); |
73 | harbaum | 1 | xmlNewProp(node_node, BAD_CAST "time", BAD_CAST str); |
74 | |||
75 | diff_save_tags(node->tag, node_node); | ||
76 | } | ||
77 | } | ||
78 | node = node->next; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void diff_save_ways(way_t *way, xmlNodePtr root_node) { | ||
83 | |||
84 | /* store all modfied ways */ | ||
85 | while(way) { | ||
86 | if(way->flags) { | ||
87 | xmlNodePtr node_way = xmlNewChild(root_node, NULL, | ||
88 | BAD_CAST "way", NULL); | ||
89 | |||
90 | harbaum | 239 | diff_save_state_n_id(way->flags, node_way, OSM_ID(way)); |
91 | harbaum | 1 | |
92 | if(way->flags & OSM_FLAG_HIDDEN) | ||
93 | xmlNewProp(node_way, BAD_CAST "hidden", BAD_CAST "true"); | ||
94 | |||
95 | /* additional info is only required if the way hasn't been deleted */ | ||
96 | /* and of the dirty or new flags are set. (otherwise e.g. only */ | ||
97 | /* the hidden flag may be set) */ | ||
98 | if((!(way->flags & OSM_FLAG_DELETED)) && | ||
99 | (way->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW))) { | ||
100 | node_chain_t *node_chain = way->node_chain; | ||
101 | while(node_chain) { | ||
102 | xmlNodePtr node_node = xmlNewChild(node_way, NULL, | ||
103 | BAD_CAST "nd", NULL); | ||
104 | harbaum | 239 | char *id = g_strdup_printf(ITEM_ID_FORMAT, OSM_ID(node_chain->node)); |
105 | harbaum | 1 | xmlNewProp(node_node, BAD_CAST "ref", BAD_CAST id); |
106 | g_free(id); | ||
107 | node_chain = node_chain->next; | ||
108 | } | ||
109 | diff_save_tags(way->tag, node_way); | ||
110 | } | ||
111 | } | ||
112 | way = way->next; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | static void diff_save_relations(relation_t *relation, xmlNodePtr root_node) { | ||
117 | |||
118 | /* store all modfied relations */ | ||
119 | while(relation) { | ||
120 | if(relation->flags) { | ||
121 | xmlNodePtr node_rel = xmlNewChild(root_node, NULL, | ||
122 | BAD_CAST "relation", NULL); | ||
123 | |||
124 | harbaum | 239 | diff_save_state_n_id(relation->flags, node_rel, OSM_ID(relation)); |
125 | harbaum | 1 | |
126 | if(!(relation->flags & OSM_FLAG_DELETED)) { | ||
127 | /* additional info is only required if the relation */ | ||
128 | /* hasn't been deleted */ | ||
129 | member_t *member = relation->member; | ||
130 | while(member) { | ||
131 | xmlNodePtr node_member = xmlNewChild(node_rel, NULL, | ||
132 | BAD_CAST "member", NULL); | ||
133 | |||
134 | char *ref = NULL; | ||
135 | harbaum | 155 | switch(member->object.type) { |
136 | harbaum | 1 | case NODE: |
137 | xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "node"); | ||
138 | harbaum | 239 | ref = g_strdup_printf(ITEM_ID_FORMAT, OBJECT_ID(member->object)); |
139 | harbaum | 1 | break; |
140 | case WAY: | ||
141 | xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "way"); | ||
142 | harbaum | 239 | ref = g_strdup_printf(ITEM_ID_FORMAT, OBJECT_ID(member->object)); |
143 | harbaum | 1 | break; |
144 | case RELATION: | ||
145 | xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "relation"); | ||
146 | harbaum | 239 | ref = g_strdup_printf(ITEM_ID_FORMAT, OBJECT_ID(member->object)); |
147 | harbaum | 1 | break; |
148 | |||
149 | /* XXX_ID's are used if this is a reference to an item not */ | ||
150 | /* stored in this xml data set */ | ||
151 | case NODE_ID: | ||
152 | xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "node"); | ||
153 | harbaum | 161 | ref = g_strdup_printf(ITEM_ID_FORMAT, member->object.id); |
154 | harbaum | 1 | break; |
155 | case WAY_ID: | ||
156 | xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "way"); | ||
157 | harbaum | 161 | ref = g_strdup_printf(ITEM_ID_FORMAT, member->object.id); |
158 | harbaum | 1 | break; |
159 | case RELATION_ID: | ||
160 | xmlNewProp(node_member, BAD_CAST "type", BAD_CAST "relation"); | ||
161 | harbaum | 161 | ref = g_strdup_printf(ITEM_ID_FORMAT, member->object.id); |
162 | harbaum | 1 | break; |
163 | |||
164 | default: | ||
165 | harbaum | 155 | printf("unexpected member type %d\n", member->object.type); |
166 | harbaum | 1 | break; |
167 | } | ||
168 | |||
169 | g_assert(ref); | ||
170 | xmlNewProp(node_member, BAD_CAST "ref", BAD_CAST ref); | ||
171 | g_free(ref); | ||
172 | |||
173 | if(member->role) | ||
174 | xmlNewProp(node_member, BAD_CAST "role", BAD_CAST member->role); | ||
175 | |||
176 | member = member->next; | ||
177 | } | ||
178 | diff_save_tags(relation->tag, node_rel); | ||
179 | } | ||
180 | } | ||
181 | relation = relation->next; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | |||
186 | /* return true if no diff needs to be saved */ | ||
187 | gboolean diff_is_clean(osm_t *osm, gboolean honor_hidden_flags) { | ||
188 | gboolean clean = TRUE; | ||
189 | |||
190 | /* check if a diff is necessary */ | ||
191 | node_t *node = osm->node; | ||
192 | while(node && clean) { | ||
193 | if(node->flags) clean = FALSE; | ||
194 | node = node->next; | ||
195 | } | ||
196 | |||
197 | way_t *way = osm->way; | ||
198 | while(way && clean) { | ||
199 | if(honor_hidden_flags) { | ||
200 | if(way->flags) clean = FALSE; | ||
201 | } else | ||
202 | if(way->flags & ~OSM_FLAG_HIDDEN) | ||
203 | clean = FALSE; | ||
204 | |||
205 | way = way->next; | ||
206 | } | ||
207 | |||
208 | relation_t *relation = osm->relation; | ||
209 | while(relation && clean) { | ||
210 | if(relation->flags) clean = FALSE; | ||
211 | relation = relation->next; | ||
212 | } | ||
213 | |||
214 | return clean; | ||
215 | } | ||
216 | |||
217 | void diff_save(project_t *project, osm_t *osm) { | ||
218 | harbaum | 4 | if(!project || !osm) return; |
219 | harbaum | 1 | |
220 | char *diff_name = | ||
221 | g_strdup_printf("%s/%s.diff", project->path, project->name); | ||
222 | |||
223 | if(diff_is_clean(osm, TRUE)) { | ||
224 | printf("data set is clean, removing diff if present\n"); | ||
225 | g_remove(diff_name); | ||
226 | g_free(diff_name); | ||
227 | return; | ||
228 | } | ||
229 | |||
230 | printf("data set is dirty, generating diff\n"); | ||
231 | |||
232 | LIBXML_TEST_VERSION; | ||
233 | |||
234 | xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); | ||
235 | xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "diff"); | ||
236 | xmlNewProp(root_node, BAD_CAST "name", BAD_CAST project->name); | ||
237 | xmlDocSetRootElement(doc, root_node); | ||
238 | |||
239 | diff_save_nodes(osm->node, root_node); | ||
240 | diff_save_ways(osm->way, root_node); | ||
241 | diff_save_relations(osm->relation, root_node); | ||
242 | |||
243 | xmlSaveFormatFileEnc(diff_name, doc, "UTF-8", 1); | ||
244 | xmlFreeDoc(doc); | ||
245 | xmlCleanupParser(); | ||
246 | |||
247 | g_free(diff_name); | ||
248 | } | ||
249 | |||
250 | static int xml_get_prop_int(xmlNode *node, char *prop, int def) { | ||
251 | char *str = (char*)xmlGetProp(node, BAD_CAST prop); | ||
252 | int value = def; | ||
253 | |||
254 | if(str) { | ||
255 | value = strtoul(str, NULL, 10); | ||
256 | xmlFree(str); | ||
257 | } | ||
258 | |||
259 | return value; | ||
260 | } | ||
261 | |||
262 | static int xml_get_prop_state(xmlNode *node, char *prop) { | ||
263 | char *str = (char*)xmlGetProp(node, BAD_CAST prop); | ||
264 | |||
265 | if(str) { | ||
266 | if(strcasecmp(str, "new") == 0) { | ||
267 | xmlFree(str); | ||
268 | return OSM_FLAG_NEW; | ||
269 | } | ||
270 | |||
271 | if(strcasecmp(str, "deleted") == 0) { | ||
272 | xmlFree(str); | ||
273 | return OSM_FLAG_DELETED; | ||
274 | } | ||
275 | |||
276 | g_assert(0); | ||
277 | } | ||
278 | |||
279 | return OSM_FLAG_DIRTY; | ||
280 | } | ||
281 | |||
282 | static pos_t *xml_get_prop_pos(xmlNode *node) { | ||
283 | char *str_lat = (char*)xmlGetProp(node, BAD_CAST "lat"); | ||
284 | char *str_lon = (char*)xmlGetProp(node, BAD_CAST "lon"); | ||
285 | |||
286 | if(!str_lon || !str_lat) { | ||
287 | if(!str_lon) xmlFree(str_lon); | ||
288 | if(!str_lat) xmlFree(str_lat); | ||
289 | return NULL; | ||
290 | } | ||
291 | |||
292 | pos_t *pos = g_new0(pos_t, 1); | ||
293 | pos->lat = g_ascii_strtod(str_lat, NULL); | ||
294 | pos->lon = g_ascii_strtod(str_lon, NULL); | ||
295 | |||
296 | xmlFree(str_lon); | ||
297 | xmlFree(str_lat); | ||
298 | |||
299 | return pos; | ||
300 | } | ||
301 | |||
302 | static tag_t *xml_scan_tags(xmlDoc *doc, xmlNodePtr node, osm_t *osm) { | ||
303 | /* scan for tags */ | ||
304 | tag_t *first_tag = NULL; | ||
305 | tag_t **tag = &first_tag; | ||
306 | |||
307 | while(node) { | ||
308 | if(node->type == XML_ELEMENT_NODE) { | ||
309 | if(strcasecmp((char*)node->name, "tag") == 0) { | ||
310 | /* attach tag to node/way */ | ||
311 | *tag = osm_parse_osm_tag(osm, doc, node); | ||
312 | if(*tag) tag = &((*tag)->next); | ||
313 | } | ||
314 | } | ||
315 | node = node->next; | ||
316 | } | ||
317 | return first_tag; | ||
318 | } | ||
319 | |||
320 | void diff_restore_node(xmlDoc *doc, xmlNodePtr node_node, osm_t *osm) { | ||
321 | printf("Restoring node\n"); | ||
322 | |||
323 | /* read properties */ | ||
324 | item_id_t id = xml_get_prop_int(node_node, "id", ID_ILLEGAL); | ||
325 | if(id == ID_ILLEGAL) { | ||
326 | printf(" Node entry missing id\n"); | ||
327 | return; | ||
328 | } | ||
329 | |||
330 | int state = xml_get_prop_state(node_node, "state"); | ||
331 | pos_t *pos = xml_get_prop_pos(node_node); | ||
332 | |||
333 | if(!(state & OSM_FLAG_DELETED) && !pos) { | ||
334 | printf(" Node not deleted, but no valid position\n"); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | /* evaluate properties */ | ||
339 | node_t *node = NULL; | ||
340 | |||
341 | switch(state) { | ||
342 | case OSM_FLAG_NEW: | ||
343 | printf(" Restoring NEW node\n"); | ||
344 | |||
345 | node = g_new0(node_t, 1); | ||
346 | node->visible = TRUE; | ||
347 | node->flags = OSM_FLAG_NEW; | ||
348 | harbaum | 239 | OSM_TIME(node) = xml_get_prop_int(node_node, "time", 0); |
349 | if(!OSM_TIME(node)) OSM_TIME(node) = time(NULL); | ||
350 | harbaum | 1 | |
351 | /* attach to end of node list */ | ||
352 | node_t **lnode = &osm->node; | ||
353 | while(*lnode) lnode = &(*lnode)->next; | ||
354 | *lnode = node; | ||
355 | break; | ||
356 | |||
357 | case OSM_FLAG_DELETED: | ||
358 | printf(" Restoring DELETE flag\n"); | ||
359 | |||
360 | if((node = osm_get_node_by_id(osm, id))) | ||
361 | node->flags |= OSM_FLAG_DELETED; | ||
362 | else | ||
363 | printf(" WARNING: no node with that id found\n"); | ||
364 | break; | ||
365 | |||
366 | case OSM_FLAG_DIRTY: | ||
367 | printf(" Valid id/position (DIRTY)\n"); | ||
368 | |||
369 | if((node = osm_get_node_by_id(osm, id))) | ||
370 | node->flags |= OSM_FLAG_DIRTY; | ||
371 | else | ||
372 | printf(" WARNING: no node with that id found\n"); | ||
373 | break; | ||
374 | |||
375 | default: | ||
376 | printf(" Illegal node entry\n"); | ||
377 | return; | ||
378 | break; | ||
379 | } | ||
380 | |||
381 | if(!node) { | ||
382 | printf(" no valid node\n"); | ||
383 | return; | ||
384 | } | ||
385 | |||
386 | /* update id and position from diff */ | ||
387 | harbaum | 239 | OSM_ID(node) = id; |
388 | harbaum | 1 | if(pos) { |
389 | node->pos.lat = pos->lat; | ||
390 | node->pos.lon = pos->lon; | ||
391 | |||
392 | pos2lpos(osm->bounds, &node->pos, &node->lpos); | ||
393 | |||
394 | g_free(pos); | ||
395 | } | ||
396 | |||
397 | /* node may be an existing node, so remove tags to */ | ||
398 | /* make space for new ones */ | ||
399 | if(node->tag) { | ||
400 | printf(" removing existing tags for diff tags\n"); | ||
401 | osm_tag_free(node->tag); | ||
402 | node->tag = NULL; | ||
403 | } | ||
404 | |||
405 | node->tag = xml_scan_tags(doc, node_node->children, osm); | ||
406 | } | ||
407 | |||
408 | void diff_restore_way(xmlDoc *doc, xmlNodePtr node_node, osm_t *osm) { | ||
409 | printf("Restoring way\n"); | ||
410 | |||
411 | item_id_t id = xml_get_prop_int(node_node, "id", ID_ILLEGAL); | ||
412 | if(id == ID_ILLEGAL) { | ||
413 | printf(" entry missing id\n"); | ||
414 | return; | ||
415 | } | ||
416 | |||
417 | int state = xml_get_prop_state(node_node, "state"); | ||
418 | |||
419 | /* handle hidden flag */ | ||
420 | gboolean hidden = FALSE; | ||
421 | char *str = (char*)xmlGetProp(node_node, BAD_CAST "hidden"); | ||
422 | if(str) { | ||
423 | if(strcasecmp(str, "true") == 0) | ||
424 | hidden = TRUE; | ||
425 | |||
426 | xmlFree(str); | ||
427 | } | ||
428 | |||
429 | |||
430 | /* evaluate properties */ | ||
431 | way_t *way = NULL; | ||
432 | switch(state) { | ||
433 | case OSM_FLAG_NEW: | ||
434 | printf(" Restoring NEW way\n"); | ||
435 | |||
436 | way = g_new0(way_t, 1); | ||
437 | way->visible = TRUE; | ||
438 | way->flags = OSM_FLAG_NEW; | ||
439 | harbaum | 239 | OSM_TIME(way) = xml_get_prop_int(node_node, "time", 0); |
440 | if(!OSM_TIME(way)) OSM_TIME(way) = time(NULL); | ||
441 | harbaum | 1 | |
442 | /* attach to end of way list */ | ||
443 | way_t **lway = &osm->way; | ||
444 | while(*lway) lway = &(*lway)->next; | ||
445 | *lway = way; | ||
446 | break; | ||
447 | |||
448 | case OSM_FLAG_DELETED: | ||
449 | printf(" Restoring DELETE flag\n"); | ||
450 | |||
451 | if((way = osm_get_way_by_id(osm, id))) | ||
452 | way->flags |= OSM_FLAG_DELETED; | ||
453 | else | ||
454 | printf(" WARNING: no way with that id found\n"); | ||
455 | break; | ||
456 | |||
457 | case OSM_FLAG_DIRTY: | ||
458 | printf(" Valid id (DIRTY)\n"); | ||
459 | |||
460 | if((way = osm_get_way_by_id(osm, id))) | ||
461 | way->flags |= OSM_FLAG_DIRTY; | ||
462 | else | ||
463 | printf(" WARNING: no way with that id found\n"); | ||
464 | break; | ||
465 | |||
466 | default: | ||
467 | printf(" Illegal way entry\n"); | ||
468 | return; | ||
469 | } | ||
470 | |||
471 | if(!way) { | ||
472 | printf(" no valid way\n"); | ||
473 | return; | ||
474 | } | ||
475 | |||
476 | /* update id from diff */ | ||
477 | harbaum | 239 | OSM_ID(way) = id; |
478 | harbaum | 1 | |
479 | /* update node_chain */ | ||
480 | if(hidden) | ||
481 | way->flags |= OSM_FLAG_HIDDEN; | ||
482 | |||
483 | gboolean installed_new_nodes = FALSE; | ||
484 | |||
485 | /* scan for nodes */ | ||
486 | node_chain_t **node_chain = &way->node_chain; | ||
487 | xmlNode *nd_node = NULL; | ||
488 | for(nd_node = node_node->children; nd_node; nd_node = nd_node->next) { | ||
489 | if(nd_node->type == XML_ELEMENT_NODE) { | ||
490 | if(strcasecmp((char*)nd_node->name, "nd") == 0) { | ||
491 | |||
492 | /* only replace the original nodes if new nodes have actually been */ | ||
493 | /* found. */ | ||
494 | if(!installed_new_nodes) { | ||
495 | /* way may be an existing way, so remove nodes to */ | ||
496 | /* make space for new ones */ | ||
497 | if(way->node_chain) { | ||
498 | printf(" removing existing nodes for diff nodes\n"); | ||
499 | osm_node_chain_free(way->node_chain); | ||
500 | way->node_chain = NULL; | ||
501 | } | ||
502 | |||
503 | installed_new_nodes = TRUE; | ||
504 | } | ||
505 | |||
506 | /* attach node to node_chain */ | ||
507 | *node_chain = osm_parse_osm_way_nd(osm, doc, nd_node); | ||
508 | if(*node_chain) | ||
509 | node_chain = &((*node_chain)->next); | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | |||
514 | /* only replace tags if nodes have been found before. if no nodes */ | ||
515 | /* were found this wasn't a dirty entry but e.g. only the hidden */ | ||
516 | /* flag had been set */ | ||
517 | if(installed_new_nodes) { | ||
518 | |||
519 | /* node may be an existing node, so remove tags to */ | ||
520 | /* make space for new ones */ | ||
521 | if(way->tag) { | ||
522 | printf(" removing existing tags for diff tags\n"); | ||
523 | osm_tag_free(way->tag); | ||
524 | way->tag = NULL; | ||
525 | } | ||
526 | |||
527 | way->tag = xml_scan_tags(doc, node_node->children, osm); | ||
528 | } else { | ||
529 | printf(" no nodes restored, way isn't dirty!\n"); | ||
530 | way->flags &= ~OSM_FLAG_DIRTY; | ||
531 | } | ||
532 | } | ||
533 | |||
534 | void diff_restore_relation(xmlDoc *doc, xmlNodePtr node_rel, osm_t *osm) { | ||
535 | printf("Restoring relation\n"); | ||
536 | |||
537 | item_id_t id = xml_get_prop_int(node_rel, "id", ID_ILLEGAL); | ||
538 | if(id == ID_ILLEGAL) { | ||
539 | printf(" entry missing id\n"); | ||
540 | return; | ||
541 | } | ||
542 | |||
543 | int state = xml_get_prop_state(node_rel, "state"); | ||
544 | |||
545 | /* evaluate properties */ | ||
546 | relation_t *relation = NULL; | ||
547 | switch(state) { | ||
548 | case OSM_FLAG_NEW: | ||
549 | printf(" Restoring NEW relation\n"); | ||
550 | |||
551 | relation = g_new0(relation_t, 1); | ||
552 | relation->visible = TRUE; | ||
553 | relation->flags = OSM_FLAG_NEW; | ||
554 | harbaum | 239 | OSM_TIME(relation) = xml_get_prop_int(node_rel, "time", 0); |
555 | if(!OSM_TIME(relation)) OSM_TIME(relation) = time(NULL); | ||
556 | harbaum | 1 | |
557 | /* attach to end of relation list */ | ||
558 | relation_t **lrelation = &osm->relation; | ||
559 | while(*lrelation) lrelation = &(*lrelation)->next; | ||
560 | *lrelation = relation; | ||
561 | break; | ||
562 | |||
563 | case OSM_FLAG_DELETED: | ||
564 | printf(" Restoring DELETE flag\n"); | ||
565 | |||
566 | if((relation = osm_get_relation_by_id(osm, id))) | ||
567 | relation->flags |= OSM_FLAG_DELETED; | ||
568 | else | ||
569 | printf(" WARNING: no relation with that id found\n"); | ||
570 | break; | ||
571 | |||
572 | case OSM_FLAG_DIRTY: | ||
573 | printf(" Valid id (DIRTY)\n"); | ||
574 | |||
575 | if((relation = osm_get_relation_by_id(osm, id))) | ||
576 | relation->flags |= OSM_FLAG_DIRTY; | ||
577 | else | ||
578 | printf(" WARNING: no relation with that id found\n"); | ||
579 | break; | ||
580 | |||
581 | default: | ||
582 | printf(" Illegal relation entry\n"); | ||
583 | return; | ||
584 | } | ||
585 | |||
586 | if(!relation) { | ||
587 | printf(" no valid relation\n"); | ||
588 | return; | ||
589 | } | ||
590 | |||
591 | /* update id from diff */ | ||
592 | harbaum | 239 | OSM_ID(relation) = id; |
593 | harbaum | 1 | |
594 | /* update members */ | ||
595 | |||
596 | /* this may be an existing relation, so remove members to */ | ||
597 | /* make space for new ones */ | ||
598 | if(relation->member) { | ||
599 | printf(" removing existing members for diff members\n"); | ||
600 | osm_members_free(relation->member); | ||
601 | relation->member = NULL; | ||
602 | } | ||
603 | |||
604 | /* scan for members */ | ||
605 | member_t **member = &relation->member; | ||
606 | xmlNode *member_node = NULL; | ||
607 | for(member_node = node_rel->children; member_node; | ||
608 | member_node = member_node->next) { | ||
609 | if(member_node->type == XML_ELEMENT_NODE) { | ||
610 | if(strcasecmp((char*)member_node->name, "member") == 0) { | ||
611 | /* attach member to member_chain */ | ||
612 | *member = osm_parse_osm_relation_member(osm, doc, member_node); | ||
613 | if(*member) | ||
614 | member = &((*member)->next); | ||
615 | } | ||
616 | } | ||
617 | } | ||
618 | |||
619 | /* node may be an existing node, so remove tags to */ | ||
620 | /* make space for new ones */ | ||
621 | if(relation->tag) { | ||
622 | printf(" removing existing tags for diff tags\n"); | ||
623 | osm_tag_free(relation->tag); | ||
624 | relation->tag = NULL; | ||
625 | } | ||
626 | |||
627 | relation->tag = xml_scan_tags(doc, node_rel->children, osm); | ||
628 | } | ||
629 | |||
630 | void diff_restore(appdata_t *appdata, project_t *project, osm_t *osm) { | ||
631 | harbaum | 4 | if(!project || !osm) return; |
632 | |||
633 | harbaum | 1 | char *diff_name = g_strdup_printf("%s/%s.diff", project->path, project->name); |
634 | harbaum | 4 | |
635 | harbaum | 1 | if(!g_file_test(diff_name, G_FILE_TEST_EXISTS)) { |
636 | printf("no diff present!\n"); | ||
637 | g_free(diff_name); | ||
638 | return; | ||
639 | } | ||
640 | |||
641 | printf("diff found, applying ...\n"); | ||
642 | |||
643 | xmlDoc *doc = NULL; | ||
644 | xmlNode *root_element = NULL; | ||
645 | |||
646 | /* parse the file and get the DOM */ | ||
647 | if((doc = xmlReadFile(diff_name, NULL, 0)) == NULL) { | ||
648 | errorf(GTK_WIDGET(appdata->window), | ||
649 | "Error: could not parse file %s\n", diff_name); | ||
650 | g_free(diff_name); | ||
651 | return; | ||
652 | } | ||
653 | |||
654 | /* Get the root element node */ | ||
655 | root_element = xmlDocGetRootElement(doc); | ||
656 | |||
657 | xmlNode *cur_node = NULL; | ||
658 | for (cur_node = root_element; cur_node; cur_node = cur_node->next) { | ||
659 | if (cur_node->type == XML_ELEMENT_NODE) { | ||
660 | if(strcasecmp((char*)cur_node->name, "diff") == 0) { | ||
661 | char *str = (char*)xmlGetProp(cur_node, BAD_CAST "name"); | ||
662 | if(str) { | ||
663 | printf("diff for project %s\n", str); | ||
664 | if(strcmp(project->name, str) != 0) { | ||
665 | messagef(GTK_WIDGET(appdata->window), _("Warning"), | ||
666 | "Diff name (%s) does not match project name (%s)", | ||
667 | str, project->name); | ||
668 | } | ||
669 | xmlFree(str); | ||
670 | } | ||
671 | |||
672 | xmlNodePtr node_node = cur_node->children; | ||
673 | while(node_node) { | ||
674 | if(node_node->type == XML_ELEMENT_NODE) { | ||
675 | |||
676 | if(strcasecmp((char*)node_node->name, "node") == 0) | ||
677 | diff_restore_node(doc, node_node, osm); | ||
678 | |||
679 | else if(strcasecmp((char*)node_node->name, "way") == 0) | ||
680 | diff_restore_way(doc, node_node, osm); | ||
681 | |||
682 | else if(strcasecmp((char*)node_node->name, "relation") == 0) | ||
683 | diff_restore_relation(doc, node_node, osm); | ||
684 | |||
685 | else | ||
686 | printf("WARNING: item %s not restored\n", node_node->name); | ||
687 | } | ||
688 | node_node = node_node->next; | ||
689 | } | ||
690 | } | ||
691 | } | ||
692 | } | ||
693 | |||
694 | g_free(diff_name); | ||
695 | |||
696 | xmlFreeDoc(doc); | ||
697 | xmlCleanupParser(); | ||
698 | |||
699 | /* check for hidden ways and update menu accordingly */ | ||
700 | gboolean something_is_hidden = FALSE; | ||
701 | way_t *way = osm->way; | ||
702 | while(!something_is_hidden && way) { | ||
703 | if(way->flags & OSM_FLAG_HIDDEN) | ||
704 | something_is_hidden = TRUE; | ||
705 | |||
706 | way = way->next; | ||
707 | } | ||
708 | |||
709 | if(something_is_hidden) { | ||
710 | printf("hidden flags have been restored, enable show_add menu\n"); | ||
711 | |||
712 | achadwick | 28 | statusbar_set(appdata, _("Some objects are hidden"), TRUE); |
713 | harbaum | 1 | gtk_widget_set_sensitive(appdata->menu_item_map_show_all, TRUE); |
714 | } | ||
715 | } | ||
716 | |||
717 | gboolean diff_present(project_t *project) { | ||
718 | char *diff_name = g_strdup_printf("%s/%s.diff", project->path, project->name); | ||
719 | |||
720 | if(!g_file_test(diff_name, G_FILE_TEST_EXISTS)) { | ||
721 | printf("no diff present!\n"); | ||
722 | g_free(diff_name); | ||
723 | return FALSE; | ||
724 | } | ||
725 | |||
726 | g_free(diff_name); | ||
727 | return TRUE; | ||
728 | } | ||
729 | |||
730 | void diff_remove(project_t *project) { | ||
731 | char *diff_name = g_strdup_printf("%s/%s.diff", project->path, project->name); | ||
732 | g_remove(diff_name); | ||
733 | g_free(diff_name); | ||
734 | } |