Contents of /trunk/src/undo.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 236 - (hide annotations)
Tue Jul 21 20:28:09 2009 UTC (14 years, 11 months ago) by harbaum
File MIME type: text/plain
File size: 9499 byte(s)
Begin undo for complex objects
1 harbaum 54 /*
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 harbaum 236 #define UNDO_ENABLE_CHECK if(!appdata->menu_item_map_undo) return;
23    
24 harbaum 64 /* return plain text of type */
25     char *undo_type_string(type_t type) {
26     const struct { undo_type_t type; char *name; } types[] = {
27     { UNDO_DELETE, "DELETE" },
28     { UNDO_CREATE, "CREATE" },
29     { UNDO_MODIFY, "MODIFY" },
30     { 0, NULL }
31     };
32    
33     int i;
34     for(i=0;types[i].name;i++)
35     if(type == types[i].type)
36     return types[i].name;
37    
38     return NULL;
39     }
40    
41 harbaum 155 static void undo_object_free(object_t *obj) {
42 harbaum 235 if(obj->ptr) {
43     char *msg = osm_object_string(obj);
44     printf(" object %s\n", msg);
45     g_free(msg);
46     } else
47     printf(" object %s\n", osm_object_type_string(obj));
48    
49     if(obj->ptr) {
50 harbaum 64
51     switch(obj->type) {
52     case NODE:
53 harbaum 155 osm_node_free(NULL, obj->node);
54 harbaum 64 break;
55 harbaum 69
56     case WAY:
57 harbaum 155 osm_way_free(obj->way);
58 harbaum 69 break;
59 harbaum 64
60     default:
61     printf("ERROR: unsupported object %s\n",
62 harbaum 155 osm_object_type_string(obj));
63 harbaum 64 g_assert(0);
64     break;
65     }
66     }
67    
68     g_free(obj);
69     }
70    
71     static void undo_op_free(undo_op_t *op) {
72     printf(" op: %s\n", undo_type_string(op->type));
73     if(op->object) undo_object_free(op->object);
74     g_free(op);
75     }
76    
77 harbaum 54 static void undo_state_free(undo_state_t *state) {
78 harbaum 64 printf(" state: %s\n", undo_type_string(state->type));
79    
80 harbaum 235 if(state->object)
81     undo_object_free(state->object);
82    
83 harbaum 64 undo_op_t *op = state->op;
84     while(op) {
85     undo_op_t *next = op->next;
86     undo_op_free(op);
87     op = next;
88     }
89    
90 harbaum 54 g_free(state);
91     }
92    
93 harbaum 64 /* free all undo states, thus forgetting the entire history */
94     /* called at program exit or e.g. at project change */
95 harbaum 54 void undo_free(undo_state_t *state) {
96 harbaum 64 printf("Freeing all UNDO states:\n");
97    
98 harbaum 54 while(state) {
99     undo_state_t *next = state->next;
100     undo_state_free(state);
101     state = next;
102     }
103     }
104    
105 harbaum 64 /* append a new state to the chain of undo states */
106 harbaum 54 static undo_state_t *undo_append_state(appdata_t *appdata) {
107     undo_state_t *new_state = NULL;
108    
109     /* create new undo state at end of undo chain */
110     int undo_chain_length = 0;
111 harbaum 68 undo_state_t **undo_stateP = &appdata->undo.state;
112 harbaum 54 while(*undo_stateP) {
113     undo_chain_length++;
114     undo_stateP = &(*undo_stateP)->next;
115     }
116    
117     /* append new entry to chain */
118     new_state = *undo_stateP = g_new0(undo_state_t, 1);
119    
120     /* delete first entry if the chain is too long */
121     if(undo_chain_length >= UNDO_QUEUE_LEN) {
122 harbaum 68 undo_state_t *second = appdata->undo.state->next;
123     undo_state_free(appdata->undo.state);
124     appdata->undo.state = second;
125 harbaum 54 }
126    
127     printf("UNDO: current chain length = %d\n", undo_chain_length);
128    
129     return new_state;
130     }
131    
132 harbaum 64 /* create a local copy of the entire object */
133 harbaum 155 static object_t *undo_object_copy(object_t *object) {
134     switch(object->type) {
135 harbaum 64 case NODE: {
136 harbaum 155 object_t *ob = g_new0(object_t, 1);
137     ob->type = object->type;
138 harbaum 69
139 harbaum 64 /* fields ignored in this copy operation: */
140     /* ways, icon_buf, map_item_chain, next */
141    
142 harbaum 155 ob->node = g_new0(node_t, 1);
143 harbaum 64 /* copy all important parts, omitting icon pointers etc. */
144 harbaum 155 ob->node->id = object->node->id;
145     ob->node->lpos = object->node->lpos;
146     ob->node->pos = object->node->pos;
147 harbaum 64 /* user is a pointer, but since the users list */
148     /* is never touched it's ok */
149 harbaum 155 ob->node->user = object->node->user;
150     ob->node->visible = object->node->visible;
151     ob->node->time = object->node->time;
152 harbaum 234 ob->node->tag = osm_tags_copy(object->node->tag);
153 harbaum 155 ob->node->flags = object->node->flags;
154     ob->node->zoom_max = object->node->zoom_max;
155 harbaum 69
156     return ob;
157 harbaum 64 } break;
158    
159 harbaum 69 case WAY: {
160 harbaum 155 object_t *ob = g_new0(object_t, 1);
161     ob->type = object->type;
162 harbaum 69
163     /* fields ignored in this copy operation: */
164    
165 harbaum 155 ob->way = g_new0(way_t, 1);
166 harbaum 69 /* copy all important parts */
167 harbaum 155 ob->way->id = object->way->id;
168 harbaum 69 /* user is a pointer, but since the users list */
169     /* is never touched it's ok */
170 harbaum 155 ob->way->user = object->way->user;
171     ob->way->visible = object->way->visible;
172     ob->way->time = object->way->time;
173 harbaum 234 ob->way->tag = osm_tags_copy(object->way->tag);
174 harbaum 155 ob->way->flags = object->way->flags;
175 harbaum 69
176     return ob;
177     } break;
178    
179 harbaum 64 default:
180 harbaum 69 printf("UNDO WARNING: ignoring unsupported object %s\n",
181 harbaum 155 osm_object_type_string(object));
182 harbaum 64 break;
183     }
184    
185 harbaum 69 return NULL;
186 harbaum 64 }
187    
188 harbaum 235 void undo_append_object(appdata_t *appdata, undo_type_t type,
189     object_t *object) {
190 harbaum 69
191 harbaum 236 UNDO_ENABLE_CHECK;
192 harbaum 69
193 harbaum 235 g_assert(appdata->undo.open);
194    
195 harbaum 64 /* a simple stand-alone node deletion is just a single */
196     /* operation on the database/map so only one undo_op is saved */
197 harbaum 235
198     /* append new undo operation */
199 harbaum 236
200     /* search end of chain and check if object already is in chain */
201 harbaum 235 undo_op_t **op = &(appdata->undo.open->op);
202 harbaum 236 while(*op) {
203     if(osm_object_is_same((*op)->object, object)) {
204     printf("UNDO: object %s already in undo_state: ignoring\n",
205     osm_object_string(object));
206     return;
207     }
208 harbaum 235
209 harbaum 236 op = &(*op)->next;
210     }
211    
212     printf("UNDO: saving %s operation for object %s\n",
213     undo_type_string(type), osm_object_string(object));
214    
215 harbaum 235 *op = g_new0(undo_op_t, 1);
216     (*op)->type = type;
217     (*op)->object = undo_object_copy(object);
218 harbaum 236
219     /* if the deleted object is a way, then check if this affects */
220     /* a node */
221     if((type == UNDO_DELETE) && (object->type == WAY)) {
222     node_chain_t *chain = object->way->node_chain;
223     while(chain) {
224     /* this node must only be part of this way */
225     if(!osm_node_in_other_way(appdata->osm, object->way, chain->node))
226     undo_append_node(appdata, UNDO_DELETE, chain->node);
227    
228     chain = chain->next;
229     }
230     }
231 harbaum 54 }
232 harbaum 64
233 harbaum 235 void undo_append_way(appdata_t *appdata, undo_type_t type, way_t *way) {
234 harbaum 236 object_t obj = { .type = WAY };
235 harbaum 235 obj.way = way;
236    
237     undo_append_object(appdata, type, &obj);
238     }
239    
240 harbaum 236 void undo_append_node(appdata_t *appdata, undo_type_t type, node_t *node) {
241     object_t obj = { .type = NODE };
242     obj.node = node;
243    
244     undo_append_object(appdata, type, &obj);
245     }
246    
247 harbaum 235 void undo_open_new_state(struct appdata_s *appdata, undo_type_t type,
248     object_t *object) {
249 harbaum 236
250     UNDO_ENABLE_CHECK;
251    
252 harbaum 235 g_assert(!appdata->undo.open);
253    
254     printf("UNDO: open new state for %s\n",
255     osm_object_string(object));
256    
257     /* create a new undo state */
258     appdata->undo.open = undo_append_state(appdata);
259     appdata->undo.open->type = type;
260    
261     appdata->undo.open->object = undo_object_copy(object);
262     }
263    
264     void undo_close_state(appdata_t *appdata) {
265 harbaum 236 UNDO_ENABLE_CHECK;
266    
267 harbaum 235 g_assert(appdata->undo.open);
268    
269     printf("UNDO: closing state\n");
270    
271     appdata->undo.open = NULL;
272     }
273    
274    
275     /* --------------------- restoring ---------------------- */
276    
277 harbaum 64 /* undo the deletion of an object */
278 harbaum 155 static void undo_operation_object_delete(appdata_t *appdata, object_t *obj) {
279 harbaum 64
280 harbaum 155 char *msg = osm_object_string(obj);
281 harbaum 64 printf("UNDO deletion of object %s\n", msg);
282     g_free(msg);
283    
284     switch(obj->type) {
285     case NODE: {
286     /* there must be an "deleted" entry which needs to be */
287     /* removed */
288 harbaum 155 node_t *orig = osm_get_node_by_id(appdata->osm, obj->node->id);
289 harbaum 64 g_assert(orig);
290     g_assert(orig->flags & OSM_FLAG_DELETED);
291     way_chain_t *wchain =
292 harbaum 68 osm_node_delete(appdata->osm, &appdata->icon, orig, TRUE, TRUE);
293 harbaum 64 g_assert(!wchain);
294    
295     /* then restore old node */
296 harbaum 236 // osm_node_dump(obj->node);
297 harbaum 155 osm_node_restore(appdata->osm, obj->node);
298     josm_elemstyles_colorize_node(appdata->map->style, obj->node);
299     map_node_draw(appdata->map, obj->node);
300     obj->ptr = NULL;
301 harbaum 64 } break;
302    
303 harbaum 235 case WAY: {
304     way_t *orig = osm_get_way_by_id(appdata->osm, obj->way->id);
305     g_assert(orig);
306     g_assert(orig->flags & OSM_FLAG_DELETED);
307     } break;
308    
309 harbaum 64 default:
310     printf("Unsupported object type\n");
311 harbaum 235 g_assert(0);
312 harbaum 64 break;
313     }
314     }
315    
316     /* undo a single operation */
317     static void undo_operation(appdata_t *appdata, undo_op_t *op) {
318     printf("UNDO operation: %s\n", undo_type_string(op->type));
319    
320     switch(op->type) {
321     case UNDO_DELETE:
322     undo_operation_object_delete(appdata, op->object);
323     break;
324    
325     default:
326     printf("unsupported UNDO operation\n");
327     g_assert(0);
328     break;
329     }
330     }
331    
332     /* undo the last undo_state */
333     void undo(appdata_t *appdata) {
334 harbaum 68 undo_state_t *state = appdata->undo.state;
335 harbaum 64 printf("user selected undo\n");
336    
337     /* search last (newest) entry */
338     while(state && state->next) state = state->next;
339    
340     if(!state) {
341     banner_show_info(appdata, _("No further undo data"));
342     return;
343     }
344    
345    
346     printf("UNDO state: %s\n", undo_type_string(state->type));
347    
348     /* since the operations list was built by prepending new */
349     /* entries, just going through the list will run the operations */
350     /* in reverse order. That's exactly what we want! */
351     undo_op_t *op = state->op;
352     while(op) {
353     undo_operation(appdata, op);
354     op = op->next;
355     }
356    
357     /* remove this entry from chain */
358 harbaum 68 undo_state_t **stateP = &appdata->undo.state;
359 harbaum 64 while(*stateP && (*stateP)->next) stateP = &(*stateP)->next;
360    
361     undo_state_free(*stateP);
362     *stateP = NULL;
363     }