Updated copyright text/header in most source files.
[geda-gaf/peter-b.git] / gschem / src / o_undo.c
blobb1b1406b3bbc759efb2135d0fb69fd8ca3cab5ad
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
11 * This program 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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
20 #include <config.h>
22 #include <stdio.h>
23 #include <math.h>
24 #ifdef HAVE_STRING_H
25 #include <string.h>
26 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
31 #include "gschem.h"
33 #ifdef HAVE_LIBDMALLOC
34 #include <dmalloc.h>
35 #endif
37 static int undo_file_index=0;
38 static int prog_pid=0;
40 static char* tmp_path = NULL;
42 /* this is additional number of levels (or history) at which point the */
43 /* undo stack will be trimmed, it's used a safety to prevent running out */
44 /* of entries to free */
45 #define UNDO_PADDING 5
47 /*! \todo Finish function documentation!!!
48 * \brief
49 * \par Function Description
52 void o_undo_init(void)
54 prog_pid = getpid();
56 tmp_path = g_strdup (getenv("TMP"));
57 if (tmp_path == NULL) {
58 tmp_path = g_strdup ("/tmp");
60 #if DEBUG
61 printf("%s\n", tmp_path);
62 #endif
65 /*! \todo Finish function documentation!!!
66 * \brief
67 * \par Function Description
70 * <B>flag</B> can be one of the following values:
71 * <DL>
72 * <DT>*</DT><DD>UNDO_ALL
73 * <DT>*</DT><DD>UNDO_VIEWPORT_ONLY
74 * </DL>
76 void o_undo_savestate(GSCHEM_TOPLEVEL *w_current, int flag)
78 TOPLEVEL *toplevel = w_current->toplevel;
79 char *filename = NULL;
80 GList *object_list = NULL;
81 int levels;
82 UNDO *u_current;
83 UNDO *u_current_next;
85 /* save autosave backups if necessary */
86 o_autosave_backups(w_current);
88 if (w_current->undo_control == FALSE) {
89 return;
92 if (flag == UNDO_ALL) {
94 /* Increment the number of operations since last backup if
95 auto-save is enabled */
96 if (toplevel->auto_save_interval != 0) {
97 toplevel->page_current->ops_since_last_backup++;
100 /* HACK */
101 /* Before we save the undo state, consolidate nets as necessary */
103 /* This is where the net consolidation call would have been
104 * triggered before it was removed from o_save_buffer().
106 if (toplevel->net_consolidate == TRUE)
107 o_net_consolidate (w_current->toplevel);
110 if (w_current->undo_type == UNDO_DISK && flag == UNDO_ALL) {
112 filename = g_strdup_printf("%s%cgschem.save%d_%d.sch",
113 tmp_path, G_DIR_SEPARATOR,
114 prog_pid, undo_file_index++);
116 /* Changed from f_save to o_save when adding backup copy creation. */
117 /* f_save manages the creaton of backup copies.
118 This way, f_save is called only when saving a file, and not when
119 saving an undo backup copy */
120 o_save_curr_page (toplevel, filename);
122 } else if (w_current->undo_type == UNDO_MEMORY && flag == UNDO_ALL) {
123 object_list = o_glist_copy_all (toplevel,
124 s_page_objects (toplevel->page_current),
125 object_list, SELECTION_FLAG);
128 /* Clear Anything above current */
129 if (toplevel->page_current->undo_current) {
130 s_undo_remove_rest(toplevel,
131 toplevel->page_current->undo_current->next);
132 toplevel->page_current->undo_current->next = NULL;
133 } else { /* undo current is NULL */
134 s_undo_remove_rest(toplevel,
135 toplevel->page_current->undo_bottom);
136 toplevel->page_current->undo_bottom = NULL;
139 toplevel->page_current->undo_tos = toplevel->page_current->undo_current;
141 toplevel->page_current->undo_tos =
142 s_undo_add(toplevel->page_current->undo_tos,
143 flag, filename, object_list,
144 toplevel->page_current->left,
145 toplevel->page_current->top,
146 toplevel->page_current->right,
147 toplevel->page_current->bottom,
148 toplevel->page_current->page_control,
149 toplevel->page_current->up);
151 toplevel->page_current->undo_current =
152 toplevel->page_current->undo_tos;
154 if (toplevel->page_current->undo_bottom == NULL) {
155 toplevel->page_current->undo_bottom =
156 toplevel->page_current->undo_tos;
159 #if DEBUG
160 printf("\n\n---Undo----\n");
161 s_undo_print_all(toplevel->page_current->undo_bottom);
162 printf("BOTTOM: %s\n", toplevel->page_current->undo_bottom->filename);
163 printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
164 printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
165 printf("----\n");
166 #endif
168 g_free(filename);
170 /* Now go through and see if we need to free/remove some undo levels */
171 /* so we stay within the limits */
173 /* only check history every 10 undo savestates */
174 if (undo_file_index % 10) {
175 return;
178 levels = s_undo_levels(toplevel->page_current->undo_bottom);
180 #if DEBUG
181 printf("levels: %d\n", levels);
182 #endif
184 if (levels >= w_current->undo_levels + UNDO_PADDING) {
185 levels = levels - w_current->undo_levels;
187 #if DEBUG
188 printf("Trimming: %d levels\n", levels);
189 #endif
191 u_current = toplevel->page_current->undo_bottom;
192 while(u_current && levels > 0) {
193 u_current_next = u_current->next;
195 if (u_current->filename) {
196 #if DEBUG
197 printf("Freeing: %s\n", u_current->filename);
198 #endif
199 unlink(u_current->filename);
200 g_free(u_current->filename);
203 if (u_current->object_list) {
204 s_delete_object_glist (toplevel, u_current->object_list);
205 u_current->object_list = NULL;
208 u_current->next = NULL;
209 u_current->prev = NULL;
210 g_free(u_current);
212 u_current = u_current_next;
213 levels--;
216 /* Because we use a pad you are always garanteed to never */
217 /* exhaust the list */
218 u_current->prev = NULL;
219 toplevel->page_current->undo_bottom = u_current;
221 #if DEBUG
222 printf("New current is: %s\n", u_current->filename);
223 #endif
226 #if DEBUG
227 printf("\n\n---Undo----\n");
228 s_undo_print_all(toplevel->page_current->undo_bottom);
229 printf("BOTTOM: %s\n", toplevel->page_current->undo_bottom->filename);
230 printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
231 printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
232 printf("----\n");
233 #endif
237 /*! \todo Finish function documentation!!!
238 * \brief
239 * \par Function Description
242 char *o_undo_find_prev_filename(UNDO *start)
244 UNDO *u_current;
246 u_current = start->prev;
248 while(u_current) {
249 if (u_current->filename) {
250 return(u_current->filename);
252 u_current = u_current->prev;
255 return(NULL);
258 /*! \todo Finish function documentation!!!
259 * \brief
260 * \par Function Description
263 GList *o_undo_find_prev_object_head (UNDO *start)
265 UNDO *u_current;
267 u_current = start->prev;
269 while(u_current) {
270 if (u_current->object_list) {
271 return u_current->object_list;
273 u_current = u_current->prev;
276 return(NULL);
279 /*! \todo Finish function documentation!!!
280 * \brief
281 * \par Function Description
283 * <B>type</B> can be one of the following values:
284 * <DL>
285 * <DT>*</DT><DD>UNDO_ACTION
286 * <DT>*</DT><DD>REDO_ACTION
287 * </DL>
289 void o_undo_callback(GSCHEM_TOPLEVEL *w_current, int type)
291 TOPLEVEL *toplevel = w_current->toplevel;
292 UNDO *u_current;
293 UNDO *u_next;
294 UNDO *save_bottom;
295 UNDO *save_tos;
296 UNDO *save_current;
297 int save_logging;
298 int find_prev_data=FALSE;
299 int prev_status;
301 char *save_filename;
303 if (w_current->undo_control == FALSE) {
304 s_log_message(_("Undo/Redo disabled in rc file\n"));
305 return;
308 if (toplevel->page_current->undo_current == NULL) {
309 return;
312 if (type == UNDO_ACTION) {
313 u_current = toplevel->page_current->undo_current->prev;
314 } else {
315 u_current = toplevel->page_current->undo_current->next;
318 u_next = toplevel->page_current->undo_current;
320 if (u_current == NULL) {
321 return;
324 if (u_next->type == UNDO_ALL && u_current->type == UNDO_VIEWPORT_ONLY) {
325 #if DEBUG
326 printf("Type: %d\n", u_current->type);
327 printf("Current is an undo all, next is viewport only!\n");
328 #endif
329 find_prev_data = TRUE;
331 if (w_current->undo_type == UNDO_DISK) {
332 u_current->filename = o_undo_find_prev_filename(u_current);
333 } else {
334 u_current->object_list = o_undo_find_prev_object_head (u_current);
338 /* save filename */
339 save_filename = g_strdup (toplevel->page_current->page_filename);
341 /* save structure so it's not nuked */
342 save_bottom = toplevel->page_current->undo_bottom;
343 save_tos = toplevel->page_current->undo_tos;
344 save_current = toplevel->page_current->undo_current;
345 toplevel->page_current->undo_bottom = NULL;
346 toplevel->page_current->undo_tos = NULL;
347 toplevel->page_current->undo_current = NULL;
349 if (w_current->undo_type == UNDO_DISK && u_current->filename) {
350 PAGE *p_new;
351 s_page_delete (toplevel, toplevel->page_current);
352 p_new = s_page_new(toplevel, u_current->filename);
353 s_page_goto (toplevel, p_new);
354 } else if (w_current->undo_type == UNDO_MEMORY && u_current->object_list) {
355 PAGE *p_new;
356 s_page_delete (toplevel, toplevel->page_current);
357 p_new = s_page_new (toplevel, save_filename);
358 s_page_goto (toplevel, p_new);
361 /* temporarily disable logging */
362 save_logging = do_logging;
363 prev_status = toplevel->DONT_REDRAW;
364 toplevel->DONT_REDRAW = 1;
365 do_logging = FALSE;
367 if (w_current->undo_type == UNDO_DISK && u_current->filename) {
369 f_open(toplevel, u_current->filename, NULL);
371 x_manual_resize(w_current);
372 toplevel->page_current->page_control = u_current->page_control;
373 toplevel->page_current->up = u_current->up;
374 toplevel->page_current->CHANGED=1;
376 } else if (w_current->undo_type == UNDO_MEMORY && u_current->object_list) {
378 s_page_delete_objects (toplevel, toplevel->page_current);
380 s_page_append_list (toplevel, toplevel->page_current,
381 o_glist_copy_all (toplevel, u_current->object_list,
382 NULL, NORMAL_FLAG));
384 x_manual_resize(w_current);
385 toplevel->page_current->page_control = u_current->page_control;
386 toplevel->page_current->up = u_current->up;
387 toplevel->page_current->CHANGED=1;
390 /* do misc setups */
391 set_window(toplevel, toplevel->page_current,
392 u_current->left, u_current->right,
393 u_current->top, u_current->bottom);
394 x_hscrollbar_update(w_current);
395 x_vscrollbar_update(w_current);
397 /* restore logging */
398 do_logging = save_logging;
400 /* set filename right */
401 g_free(toplevel->page_current->page_filename);
402 toplevel->page_current->page_filename = save_filename;
404 /* final redraw */
405 x_pagesel_update (w_current);
406 x_multiattrib_update (w_current);
408 /* Let the caller to decide if redraw or not */
409 /* toplevel->DONT_REDRAW = 0; */
410 toplevel->DONT_REDRAW = prev_status;
412 if (!toplevel->DONT_REDRAW) {
413 o_invalidate_all (w_current);
415 i_update_menus(w_current);
417 /* restore saved undo structures */
418 toplevel->page_current->undo_bottom = save_bottom;
419 toplevel->page_current->undo_tos = save_tos;
420 toplevel->page_current->undo_current = save_current;
422 if (type == UNDO_ACTION) {
423 if (toplevel->page_current->undo_current) {
424 toplevel->page_current->undo_current =
425 toplevel->page_current->undo_current->prev;
426 if (toplevel->page_current->undo_current == NULL) {
427 toplevel->page_current->undo_current =
428 toplevel->page_current->undo_bottom;
431 } else { /* type is REDO_ACTION */
432 if (toplevel->page_current->undo_current) {
433 toplevel->page_current->undo_current =
434 toplevel->page_current->undo_current->next;
435 if (toplevel->page_current->undo_current == NULL) {
436 toplevel->page_current->undo_current =
437 toplevel->page_current->undo_tos;
442 /* don't have to free data here since filename, object_list are */
443 /* just pointers to the real data (lower in the stack) */
444 if (find_prev_data) {
445 u_current->filename = NULL;
446 u_current->object_list = NULL;
449 #if DEBUG
450 printf("\n\n---Undo----\n");
451 s_undo_print_all(toplevel->page_current->undo_bottom);
452 printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
453 printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
454 printf("----\n");
455 #endif
458 /*! \todo Finish function documentation!!!
459 * \brief
460 * \par Function Description
463 void o_undo_cleanup(void)
465 int i;
466 char *filename;
468 for (i = 0 ; i < undo_file_index; i++) {
469 filename = g_strdup_printf("%s%cgschem.save%d_%d.sch", tmp_path,
470 G_DIR_SEPARATOR, prog_pid, i);
471 unlink(filename);
472 g_free(filename);
475 g_free(tmp_path);
476 tmp_path = NULL;
479 /*! \todo Finish function documentation!!!
480 * \brief
481 * \par Function Description
484 void o_undo_remove_last_undo(GSCHEM_TOPLEVEL *w_current)
486 TOPLEVEL *toplevel = w_current->toplevel;
487 if (toplevel->page_current->undo_current == NULL) {
488 return;
491 if (toplevel->page_current->undo_current) {
492 toplevel->page_current->undo_current =
493 toplevel->page_current->undo_current->prev;
494 if (toplevel->page_current->undo_current == NULL) {
495 toplevel->page_current->undo_current =
496 toplevel->page_current->undo_bottom;