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
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 (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
),
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
;
302 if (w_current
->undo_control
== FALSE
) {
303 s_log_message(_("Undo/Redo disabled in rc file\n"));
307 if (toplevel
->page_current
->undo_current
== NULL
) {
311 if (type
== UNDO_ACTION
) {
312 u_current
= toplevel
->page_current
->undo_current
->prev
;
314 u_current
= toplevel
->page_current
->undo_current
->next
;
317 u_next
= toplevel
->page_current
->undo_current
;
319 if (u_current
== NULL
) {
323 if (u_next
->type
== UNDO_ALL
&& u_current
->type
== UNDO_VIEWPORT_ONLY
) {
325 printf("Type: %d\n", u_current
->type
);
326 printf("Current is an undo all, next is viewport only!\n");
328 find_prev_data
= TRUE
;
330 if (w_current
->undo_type
== UNDO_DISK
) {
331 u_current
->filename
= o_undo_find_prev_filename(u_current
);
333 u_current
->object_list
= o_undo_find_prev_object_head (u_current
);
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
) {
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
) {
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
;
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
,
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;
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
;
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
;
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
);
450 /*! \todo Finish function documentation!!!
452 * \par Function Description
455 void o_undo_cleanup(void)
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
);
471 /*! \todo Finish function documentation!!!
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
) {
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
;