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