Contents of /trunk/src/undo.c

Parent Directory Parent Directory | Revision Log Revision Log


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