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
33 #ifdef HAVE_LIBDMALLOC
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!!!
49 * \par Function Description
52 void o_undo_init(void)
56 tmp_path
= g_strdup (getenv("TMP"));
57 if (tmp_path
== NULL
) {
58 tmp_path
= g_strdup ("/tmp");
61 printf("%s\n", tmp_path
);
65 /*! \todo Finish function documentation!!!
67 * \par Function Description
70 * <B>flag</B> can be one of the following values:
72 * <DT>*</DT><DD>UNDO_ALL
73 * <DT>*</DT><DD>UNDO_VIEWPORT_ONLY
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
;
85 /* save autosave backups if necessary */
86 o_autosave_backups(w_current
);
88 if (w_current
->undo_control
== FALSE
) {
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
++;
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
;
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
);
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) {
178 levels
= s_undo_levels(toplevel
->page_current
->undo_bottom
);
181 printf("levels: %d\n", levels
);
184 if (levels
>= w_current
->undo_levels
+ UNDO_PADDING
) {
185 levels
= levels
- w_current
->undo_levels
;
188 printf("Trimming: %d levels\n", levels
);
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
) {
197 printf("Freeing: %s\n", u_current
->filename
);
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
;
212 u_current
= u_current_next
;
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
;
222 printf("New current is: %s\n", u_current
->filename
);
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
);
237 /*! \todo Finish function documentation!!!
239 * \par Function Description
242 char *o_undo_find_prev_filename(UNDO
*start
)
246 u_current
= start
->prev
;
249 if (u_current
->filename
) {
250 return(u_current
->filename
);
252 u_current
= u_current
->prev
;
258 /*! \todo Finish function documentation!!!
260 * \par Function Description
263 GList
*o_undo_find_prev_object_head (UNDO
*start
)
267 u_current
= start
->prev
;
270 if (u_current
->object_list
) {
271 return u_current
->object_list
;
273 u_current
= u_current
->prev
;
279 /*! \todo Finish function documentation!!!
281 * \par Function Description
283 * <B>type</B> can be one of the following values:
285 * <DT>*</DT><DD>UNDO_ACTION
286 * <DT>*</DT><DD>REDO_ACTION
289 void o_undo_callback(GSCHEM_TOPLEVEL
*w_current
, int type
)
291 TOPLEVEL
*toplevel
= w_current
->toplevel
;
298 int find_prev_data
=FALSE
;
303 if (w_current
->undo_control
== FALSE
) {
304 s_log_message(_("Undo/Redo disabled in rc file\n"));
308 if (toplevel
->page_current
->undo_current
== NULL
) {
312 if (type
== UNDO_ACTION
) {
313 u_current
= toplevel
->page_current
->undo_current
->prev
;
315 u_current
= toplevel
->page_current
->undo_current
->next
;
318 u_next
= toplevel
->page_current
->undo_current
;
320 if (u_current
== NULL
) {
324 if (u_next
->type
== UNDO_ALL
&& u_current
->type
== UNDO_VIEWPORT_ONLY
) {
326 printf("Type: %d\n", u_current
->type
);
327 printf("Current is an undo all, next is viewport only!\n");
329 find_prev_data
= TRUE
;
331 if (w_current
->undo_type
== UNDO_DISK
) {
332 u_current
->filename
= o_undo_find_prev_filename(u_current
);
334 u_current
->object_list
= o_undo_find_prev_object_head (u_current
);
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
) {
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
) {
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;
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
,
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;
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
;
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
;
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
);
458 /*! \todo Finish function documentation!!!
460 * \par Function Description
463 void o_undo_cleanup(void)
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
);
479 /*! \todo Finish function documentation!!!
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
) {
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
;