Contents of /trunk/src/undo.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 234 - (hide annotations)
Mon Jul 20 20:15:10 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 7464 byte(s)
Ignore created_by when copying tags
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     char *msg = osm_object_string(obj);
41 harbaum 64 printf(" object %s\n", msg);
42     g_free(msg);
43    
44 harbaum 155 if(obj->ptr) {
45 harbaum 64 switch(obj->type) {
46     case NODE:
47 harbaum 155 osm_node_free(NULL, obj->node);
48 harbaum 64 break;
49 harbaum 69
50     case WAY:
51 harbaum 155 osm_way_free(obj->way);
52 harbaum 69 break;
53 harbaum 64
54     default:
55     printf("ERROR: unsupported object %s\n",
56 harbaum 155 osm_object_type_string(obj));
57 harbaum 64 g_assert(0);
58     break;
59     }
60     }
61    
62     g_free(obj);
63     }
64    
65     static void undo_op_free(undo_op_t *op) {
66     printf(" op: %s\n", undo_type_string(op->type));
67     if(op->object) undo_object_free(op->object);
68     g_free(op);
69     }
70    
71 harbaum 54 static void undo_state_free(undo_state_t *state) {
72 harbaum 64 printf(" state: %s\n", undo_type_string(state->type));
73    
74     undo_op_t *op = state->op;
75     while(op) {
76     undo_op_t *next = op->next;
77     undo_op_free(op);
78     op = next;
79     }
80    
81 harbaum 54 g_free(state);
82     }
83    
84 harbaum 64 /* free all undo states, thus forgetting the entire history */
85     /* called at program exit or e.g. at project change */
86 harbaum 54 void undo_free(undo_state_t *state) {
87 harbaum 64 printf("Freeing all UNDO states:\n");
88    
89 harbaum 54 while(state) {
90     undo_state_t *next = state->next;
91     undo_state_free(state);
92     state = next;
93     }
94     }
95    
96 harbaum 64 /* append a new state to the chain of undo states */
97 harbaum 54 static undo_state_t *undo_append_state(appdata_t *appdata) {
98     undo_state_t *new_state = NULL;
99    
100     /* create new undo state at end of undo chain */
101     int undo_chain_length = 0;
102 harbaum 68 undo_state_t **undo_stateP = &appdata->undo.state;
103 harbaum 54 while(*undo_stateP) {
104     undo_chain_length++;
105     undo_stateP = &(*undo_stateP)->next;
106     }
107    
108     /* append new entry to chain */
109     new_state = *undo_stateP = g_new0(undo_state_t, 1);
110    
111     /* delete first entry if the chain is too long */
112     if(undo_chain_length >= UNDO_QUEUE_LEN) {
113 harbaum 68 undo_state_t *second = appdata->undo.state->next;
114     undo_state_free(appdata->undo.state);
115     appdata->undo.state = second;
116 harbaum 54 }
117    
118     printf("UNDO: current chain length = %d\n", undo_chain_length);
119    
120     return new_state;
121     }
122    
123 harbaum 64 /* create a local copy of the entire object */
124 harbaum 155 static object_t *undo_object_copy(object_t *object) {
125     switch(object->type) {
126 harbaum 64 case NODE: {
127 harbaum 155 object_t *ob = g_new0(object_t, 1);
128     ob->type = object->type;
129 harbaum 69
130 harbaum 64 /* fields ignored in this copy operation: */
131     /* ways, icon_buf, map_item_chain, next */
132    
133 harbaum 155 ob->node = g_new0(node_t, 1);
134 harbaum 64 /* copy all important parts, omitting icon pointers etc. */
135 harbaum 155 ob->node->id = object->node->id;
136     ob->node->lpos = object->node->lpos;
137     ob->node->pos = object->node->pos;
138 harbaum 64 /* user is a pointer, but since the users list */
139     /* is never touched it's ok */
140 harbaum 155 ob->node->user = object->node->user;
141     ob->node->visible = object->node->visible;
142     ob->node->time = object->node->time;
143 harbaum 234 ob->node->tag = osm_tags_copy(object->node->tag);
144 harbaum 155 ob->node->flags = object->node->flags;
145     ob->node->zoom_max = object->node->zoom_max;
146 harbaum 69
147     return ob;
148 harbaum 64 } break;
149    
150 harbaum 69 case WAY: {
151 harbaum 155 object_t *ob = g_new0(object_t, 1);
152     ob->type = object->type;
153 harbaum 69
154     /* fields ignored in this copy operation: */
155    
156 harbaum 155 ob->way = g_new0(way_t, 1);
157 harbaum 69 /* copy all important parts */
158 harbaum 155 ob->way->id = object->way->id;
159 harbaum 69 /* user is a pointer, but since the users list */
160     /* is never touched it's ok */
161 harbaum 155 ob->way->user = object->way->user;
162     ob->way->visible = object->way->visible;
163     ob->way->time = object->way->time;
164 harbaum 234 ob->way->tag = osm_tags_copy(object->way->tag);
165 harbaum 155 ob->way->flags = object->way->flags;
166 harbaum 69
167     return ob;
168     } break;
169    
170 harbaum 64 default:
171 harbaum 69 printf("UNDO WARNING: ignoring unsupported object %s\n",
172 harbaum 155 osm_object_type_string(object));
173 harbaum 64 break;
174     }
175    
176 harbaum 69 return NULL;
177 harbaum 64 }
178    
179 harbaum 155 void undo_remember_delete(appdata_t *appdata, object_t *object) {
180 harbaum 69
181     /* don't do anything if undo isn't enabled */
182 harbaum 188 if(!appdata->menu_item_map_undo)
183 harbaum 69 return;
184    
185 harbaum 64 printf("UNDO: remembering delete operation for %s\n",
186 harbaum 155 osm_object_type_string(object));
187 harbaum 64
188     /* create a new undo state */
189 harbaum 54 undo_state_t *state = undo_append_state(appdata);
190 harbaum 64 state->type = UNDO_DELETE;
191 harbaum 54
192 harbaum 64 /* a simple stand-alone node deletion is just a single */
193     /* operation on the database/map so only one undo_op is saved */
194     undo_op_t *op = state->op = g_new0(undo_op_t, 1);
195     op->type = UNDO_DELETE;
196 harbaum 155 op->object = undo_object_copy(object);
197 harbaum 54 }
198 harbaum 64
199     /* undo the deletion of an object */
200 harbaum 155 static void undo_operation_object_delete(appdata_t *appdata, object_t *obj) {
201 harbaum 64
202 harbaum 155 char *msg = osm_object_string(obj);
203 harbaum 64 printf("UNDO deletion of object %s\n", msg);
204     g_free(msg);
205    
206     switch(obj->type) {
207     case NODE: {
208     /* there must be an "deleted" entry which needs to be */
209     /* removed */
210 harbaum 155 node_t *orig = osm_get_node_by_id(appdata->osm, obj->node->id);
211 harbaum 64 g_assert(orig);
212     g_assert(orig->flags & OSM_FLAG_DELETED);
213     way_chain_t *wchain =
214 harbaum 68 osm_node_delete(appdata->osm, &appdata->icon, orig, TRUE, TRUE);
215 harbaum 64 g_assert(!wchain);
216    
217     /* then restore old node */
218 harbaum 155 osm_node_dump(obj->node);
219     osm_node_restore(appdata->osm, obj->node);
220     josm_elemstyles_colorize_node(appdata->map->style, obj->node);
221     map_node_draw(appdata->map, obj->node);
222     obj->ptr = NULL;
223 harbaum 64 } break;
224    
225     default:
226     printf("Unsupported object type\n");
227     break;
228     }
229     }
230    
231     /* undo a single operation */
232     static void undo_operation(appdata_t *appdata, undo_op_t *op) {
233     printf("UNDO operation: %s\n", undo_type_string(op->type));
234    
235     switch(op->type) {
236     case UNDO_DELETE:
237     undo_operation_object_delete(appdata, op->object);
238     break;
239    
240     default:
241     printf("unsupported UNDO operation\n");
242     g_assert(0);
243     break;
244     }
245     }
246    
247     /* undo the last undo_state */
248     void undo(appdata_t *appdata) {
249 harbaum 68 undo_state_t *state = appdata->undo.state;
250 harbaum 64 printf("user selected undo\n");
251    
252     /* search last (newest) entry */
253     while(state && state->next) state = state->next;
254    
255     if(!state) {
256     banner_show_info(appdata, _("No further undo data"));
257     return;
258     }
259    
260    
261     printf("UNDO state: %s\n", undo_type_string(state->type));
262    
263     /* since the operations list was built by prepending new */
264     /* entries, just going through the list will run the operations */
265     /* in reverse order. That's exactly what we want! */
266    
267     undo_op_t *op = state->op;
268     while(op) {
269     undo_operation(appdata, op);
270     op = op->next;
271     }
272    
273     /* remove this entry from chain */
274 harbaum 68 undo_state_t **stateP = &appdata->undo.state;
275 harbaum 64 while(*stateP && (*stateP)->next) stateP = &(*stateP)->next;
276    
277     undo_state_free(*stateP);
278     *stateP = NULL;
279     }