Contents of /trunk/src/undo.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 234 - (show annotations)
Mon Jul 20 20:15:10 2009 UTC (14 years, 9 months ago) by harbaum
File MIME type: text/plain
File size: 7464 byte(s)
Ignore created_by when copying tags
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 /* 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 static void undo_object_free(object_t *obj) {
40 char *msg = osm_object_string(obj);
41 printf(" object %s\n", msg);
42 g_free(msg);
43
44 if(obj->ptr) {
45 switch(obj->type) {
46 case NODE:
47 osm_node_free(NULL, obj->node);
48 break;
49
50 case WAY:
51 osm_way_free(obj->way);
52 break;
53
54 default:
55 printf("ERROR: unsupported object %s\n",
56 osm_object_type_string(obj));
57 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 static void undo_state_free(undo_state_t *state) {
72 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 g_free(state);
82 }
83
84 /* free all undo states, thus forgetting the entire history */
85 /* called at program exit or e.g. at project change */
86 void undo_free(undo_state_t *state) {
87 printf("Freeing all UNDO states:\n");
88
89 while(state) {
90 undo_state_t *next = state->next;
91 undo_state_free(state);
92 state = next;
93 }
94 }
95
96 /* append a new state to the chain of undo states */
97 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 undo_state_t **undo_stateP = &appdata->undo.state;
103 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 undo_state_t *second = appdata->undo.state->next;
114 undo_state_free(appdata->undo.state);
115 appdata->undo.state = second;
116 }
117
118 printf("UNDO: current chain length = %d\n", undo_chain_length);
119
120 return new_state;
121 }
122
123 /* create a local copy of the entire object */
124 static object_t *undo_object_copy(object_t *object) {
125 switch(object->type) {
126 case NODE: {
127 object_t *ob = g_new0(object_t, 1);
128 ob->type = object->type;
129
130 /* fields ignored in this copy operation: */
131 /* ways, icon_buf, map_item_chain, next */
132
133 ob->node = g_new0(node_t, 1);
134 /* copy all important parts, omitting icon pointers etc. */
135 ob->node->id = object->node->id;
136 ob->node->lpos = object->node->lpos;
137 ob->node->pos = object->node->pos;
138 /* user is a pointer, but since the users list */
139 /* is never touched it's ok */
140 ob->node->user = object->node->user;
141 ob->node->visible = object->node->visible;
142 ob->node->time = object->node->time;
143 ob->node->tag = osm_tags_copy(object->node->tag);
144 ob->node->flags = object->node->flags;
145 ob->node->zoom_max = object->node->zoom_max;
146
147 return ob;
148 } break;
149
150 case WAY: {
151 object_t *ob = g_new0(object_t, 1);
152 ob->type = object->type;
153
154 /* fields ignored in this copy operation: */
155
156 ob->way = g_new0(way_t, 1);
157 /* copy all important parts */
158 ob->way->id = object->way->id;
159 /* user is a pointer, but since the users list */
160 /* is never touched it's ok */
161 ob->way->user = object->way->user;
162 ob->way->visible = object->way->visible;
163 ob->way->time = object->way->time;
164 ob->way->tag = osm_tags_copy(object->way->tag);
165 ob->way->flags = object->way->flags;
166
167 return ob;
168 } break;
169
170 default:
171 printf("UNDO WARNING: ignoring unsupported object %s\n",
172 osm_object_type_string(object));
173 break;
174 }
175
176 return NULL;
177 }
178
179 void undo_remember_delete(appdata_t *appdata, object_t *object) {
180
181 /* don't do anything if undo isn't enabled */
182 if(!appdata->menu_item_map_undo)
183 return;
184
185 printf("UNDO: remembering delete operation for %s\n",
186 osm_object_type_string(object));
187
188 /* create a new undo state */
189 undo_state_t *state = undo_append_state(appdata);
190 state->type = UNDO_DELETE;
191
192 /* 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 op->object = undo_object_copy(object);
197 }
198
199 /* undo the deletion of an object */
200 static void undo_operation_object_delete(appdata_t *appdata, object_t *obj) {
201
202 char *msg = osm_object_string(obj);
203 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 node_t *orig = osm_get_node_by_id(appdata->osm, obj->node->id);
211 g_assert(orig);
212 g_assert(orig->flags & OSM_FLAG_DELETED);
213 way_chain_t *wchain =
214 osm_node_delete(appdata->osm, &appdata->icon, orig, TRUE, TRUE);
215 g_assert(!wchain);
216
217 /* then restore old node */
218 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 } 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 undo_state_t *state = appdata->undo.state;
250 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 undo_state_t **stateP = &appdata->undo.state;
275 while(*stateP && (*stateP)->next) stateP = &(*stateP)->next;
276
277 undo_state_free(*stateP);
278 *stateP = NULL;
279 }