Contents of /trunk/src/undo.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 64 - (hide annotations)
Mon Feb 9 13:23:17 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 6755 byte(s)
First real undo tests
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     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 harbaum 54 static void undo_state_free(undo_state_t *state) {
68 harbaum 64 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 harbaum 54 g_free(state);
78     }
79    
80 harbaum 64 /* free all undo states, thus forgetting the entire history */
81     /* called at program exit or e.g. at project change */
82 harbaum 54 void undo_free(undo_state_t *state) {
83 harbaum 64 printf("Freeing all UNDO states:\n");
84    
85 harbaum 54 while(state) {
86     undo_state_t *next = state->next;
87     undo_state_free(state);
88     state = next;
89     }
90     }
91    
92 harbaum 64 /* append a new state to the chain of undo states */
93 harbaum 54 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 harbaum 64 /* 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 harbaum 54
124 harbaum 64 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 harbaum 54 undo_state_t *state = undo_append_state(appdata);
161 harbaum 64 state->type = UNDO_DELETE;
162 harbaum 54
163 harbaum 64 /* 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 harbaum 54 }
169 harbaum 64
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, NULL, 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     map_node_draw(appdata->map, node);
195     obj->data.ptr = NULL;
196     } break;
197    
198     default:
199     printf("Unsupported object type\n");
200     break;
201     }
202     }
203    
204     /* undo a single operation */
205     static void undo_operation(appdata_t *appdata, undo_op_t *op) {
206     printf("UNDO operation: %s\n", undo_type_string(op->type));
207    
208     switch(op->type) {
209     case UNDO_DELETE:
210     undo_operation_object_delete(appdata, op->object);
211     break;
212    
213     default:
214     printf("unsupported UNDO operation\n");
215     g_assert(0);
216     break;
217     }
218     }
219    
220     /* undo the last undo_state */
221     void undo(appdata_t *appdata) {
222     undo_state_t *state = appdata->undo_state;
223     printf("user selected undo\n");
224    
225     /* search last (newest) entry */
226     while(state && state->next) state = state->next;
227    
228     if(!state) {
229     banner_show_info(appdata, _("No further undo data"));
230     return;
231     }
232    
233    
234     printf("UNDO state: %s\n", undo_type_string(state->type));
235    
236     /* since the operations list was built by prepending new */
237     /* entries, just going through the list will run the operations */
238     /* in reverse order. That's exactly what we want! */
239    
240     undo_op_t *op = state->op;
241     while(op) {
242     undo_operation(appdata, op);
243     op = op->next;
244     }
245    
246     /* remove this entry from chain */
247     undo_state_t **stateP = &appdata->undo_state;
248     while(*stateP && (*stateP)->next) stateP = &(*stateP)->next;
249    
250     undo_state_free(*stateP);
251     *stateP = NULL;
252     }