libgeda: add GError argument to f_save and o_save
[geda-gaf/whiteaudio.git] / gschem / src / o_undo.c
blobdc30c36285360f86ce15be0605705211c19d6c22
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 (toplevel, toplevel->page_current);
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 (toplevel, s_page_objects (toplevel->page_current), filename, NULL);
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);
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;
300 char *save_filename;
302 if (w_current->undo_control == FALSE) {
303 s_log_message(_("Undo/Redo disabled in rc file\n"));
304 return;
307 if (toplevel->page_current->undo_current == NULL) {
308 return;
311 if (type == UNDO_ACTION) {
312 u_current = toplevel->page_current->undo_current->prev;
313 } else {
314 u_current = toplevel->page_current->undo_current->next;
317 u_next = toplevel->page_current->undo_current;
319 if (u_current == NULL) {
320 return;
323 if (u_next->type == UNDO_ALL && u_current->type == UNDO_VIEWPORT_ONLY) {
324 #if DEBUG
325 printf("Type: %d\n", u_current->type);
326 printf("Current is an undo all, next is viewport only!\n");
327 #endif
328 find_prev_data = TRUE;
330 if (w_current->undo_type == UNDO_DISK) {
331 u_current->filename = o_undo_find_prev_filename(u_current);
332 } else {
333 u_current->object_list = o_undo_find_prev_object_head (u_current);
337 /* save filename */
338 save_filename = g_strdup (toplevel->page_current->page_filename);
340 /* save structure so it's not nuked */
341 save_bottom = toplevel->page_current->undo_bottom;
342 save_tos = toplevel->page_current->undo_tos;
343 save_current = toplevel->page_current->undo_current;
344 toplevel->page_current->undo_bottom = NULL;
345 toplevel->page_current->undo_tos = NULL;
346 toplevel->page_current->undo_current = NULL;
348 if (w_current->undo_type == UNDO_DISK && u_current->filename) {
349 PAGE *p_new;
350 s_page_delete (toplevel, toplevel->page_current);
351 p_new = s_page_new(toplevel, u_current->filename);
352 s_page_goto (toplevel, p_new);
353 } else if (w_current->undo_type == UNDO_MEMORY && u_current->object_list) {
354 PAGE *p_new;
355 s_page_delete (toplevel, toplevel->page_current);
356 p_new = s_page_new (toplevel, save_filename);
357 s_page_goto (toplevel, p_new);
360 /* temporarily disable logging */
361 save_logging = do_logging;
362 do_logging = FALSE;
364 if (w_current->undo_type == UNDO_DISK && u_current->filename) {
366 f_open(toplevel, toplevel->page_current, u_current->filename, NULL);
368 x_manual_resize(w_current);
369 toplevel->page_current->page_control = u_current->page_control;
370 toplevel->page_current->up = u_current->up;
371 toplevel->page_current->CHANGED=1;
373 } else if (w_current->undo_type == UNDO_MEMORY && u_current->object_list) {
375 s_page_delete_objects (toplevel, toplevel->page_current);
377 s_page_append_list (toplevel, toplevel->page_current,
378 o_glist_copy_all (toplevel, u_current->object_list,
379 NULL));
381 x_manual_resize(w_current);
382 toplevel->page_current->page_control = u_current->page_control;
383 toplevel->page_current->up = u_current->up;
384 toplevel->page_current->CHANGED=1;
387 /* do misc setups */
388 set_window(toplevel, toplevel->page_current,
389 u_current->left, u_current->right,
390 u_current->top, u_current->bottom);
391 x_hscrollbar_update(w_current);
392 x_vscrollbar_update(w_current);
394 /* restore logging */
395 do_logging = save_logging;
397 /* set filename right */
398 g_free(toplevel->page_current->page_filename);
399 toplevel->page_current->page_filename = save_filename;
401 /* final redraw */
402 x_pagesel_update (w_current);
403 x_multiattrib_update (w_current);
405 /* Let the caller to decide if redraw or not */
406 o_invalidate_all (w_current);
407 i_update_menus(w_current);
409 /* restore saved undo structures */
410 toplevel->page_current->undo_bottom = save_bottom;
411 toplevel->page_current->undo_tos = save_tos;
412 toplevel->page_current->undo_current = save_current;
414 if (type == UNDO_ACTION) {
415 if (toplevel->page_current->undo_current) {
416 toplevel->page_current->undo_current =
417 toplevel->page_current->undo_current->prev;
418 if (toplevel->page_current->undo_current == NULL) {
419 toplevel->page_current->undo_current =
420 toplevel->page_current->undo_bottom;
423 } else { /* type is REDO_ACTION */
424 if (toplevel->page_current->undo_current) {
425 toplevel->page_current->undo_current =
426 toplevel->page_current->undo_current->next;
427 if (toplevel->page_current->undo_current == NULL) {
428 toplevel->page_current->undo_current =
429 toplevel->page_current->undo_tos;
434 /* don't have to free data here since filename, object_list are */
435 /* just pointers to the real data (lower in the stack) */
436 if (find_prev_data) {
437 u_current->filename = NULL;
438 u_current->object_list = NULL;
441 #if DEBUG
442 printf("\n\n---Undo----\n");
443 s_undo_print_all(toplevel->page_current->undo_bottom);
444 printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
445 printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
446 printf("----\n");
447 #endif
450 /*! \todo Finish function documentation!!!
451 * \brief
452 * \par Function Description
455 void o_undo_cleanup(void)
457 int i;
458 char *filename;
460 for (i = 0 ; i < undo_file_index; i++) {
461 filename = g_strdup_printf("%s%cgschem.save%d_%d.sch", tmp_path,
462 G_DIR_SEPARATOR, prog_pid, i);
463 unlink(filename);
464 g_free(filename);
467 g_free(tmp_path);
468 tmp_path = NULL;
471 /*! \todo Finish function documentation!!!
472 * \brief
473 * \par Function Description
476 void o_undo_remove_last_undo(GSCHEM_TOPLEVEL *w_current)
478 TOPLEVEL *toplevel = w_current->toplevel;
479 if (toplevel->page_current->undo_current == NULL) {
480 return;
483 if (toplevel->page_current->undo_current) {
484 toplevel->page_current->undo_current =
485 toplevel->page_current->undo_current->prev;
486 if (toplevel->page_current->undo_current == NULL) {
487 toplevel->page_current->undo_current =
488 toplevel->page_current->undo_bottom;