gschem

o_undo.c

Go to the documentation of this file.
00001 /* gEDA - GPL Electronic Design Automation
00002  * gschem - gEDA Schematic Capture
00003  * Copyright (C) 1998-2010 Ales Hvezda
00004  * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019  */
00020 #include <config.h>
00021 
00022 #include <stdio.h>
00023 #include <math.h>
00024 #ifdef HAVE_STRING_H
00025 #include <string.h>
00026 #endif
00027 #ifdef HAVE_UNISTD_H
00028 #include <unistd.h>
00029 #endif
00030 
00031 #include "gschem.h"
00032 
00033 #ifdef HAVE_LIBDMALLOC
00034 #include <dmalloc.h>
00035 #endif
00036 
00037 static int undo_file_index=0;
00038 static int prog_pid=0;
00039 
00040 static char* tmp_path = NULL;
00041 
00042 /* this is additional number of levels (or history) at which point the */
00043 /* undo stack will be trimmed, it's used a safety to prevent running out */ 
00044 /* of entries to free */
00045 #define UNDO_PADDING  5
00046 
00052 void o_undo_init(void)
00053 {
00054   prog_pid = getpid();
00055 
00056   tmp_path = g_strdup (getenv("TMP"));
00057   if (tmp_path == NULL) {
00058      tmp_path = g_strdup ("/tmp");
00059   }
00060 #if DEBUG
00061   printf("%s\n", tmp_path);
00062 #endif
00063 }
00064 
00076 void o_undo_savestate(GSCHEM_TOPLEVEL *w_current, int flag)
00077 {
00078   TOPLEVEL *toplevel = w_current->toplevel;
00079   char *filename = NULL;
00080   GList *object_list = NULL;
00081   int levels;
00082   UNDO *u_current;
00083   UNDO *u_current_next;
00084 
00085   /* save autosave backups if necessary */
00086   o_autosave_backups(w_current);
00087 
00088   if (w_current->undo_control == FALSE) {
00089     return;
00090   }
00091 
00092   if (flag == UNDO_ALL) {
00093 
00094     /* Increment the number of operations since last backup if 
00095        auto-save is enabled */
00096     if (toplevel->auto_save_interval != 0) {
00097       toplevel->page_current->ops_since_last_backup++;
00098     }
00099 
00100     /* HACK */
00101     /* Before we save the undo state, consolidate nets as necessary */
00102 
00103     /* This is where the net consolidation call would have been
00104      * triggered before it was removed from o_save_buffer().
00105      */
00106     if (toplevel->net_consolidate == TRUE)
00107       o_net_consolidate (toplevel, toplevel->page_current);
00108   }
00109 
00110   if (w_current->undo_type == UNDO_DISK && flag == UNDO_ALL) {
00111 
00112     filename = g_strdup_printf("%s%cgschem.save%d_%d.sch",
00113                                tmp_path, G_DIR_SEPARATOR,
00114                                prog_pid, undo_file_index++);
00115 
00116     /* Changed from f_save to o_save when adding backup copy creation. */
00117     /* f_save manages the creaton of backup copies. 
00118        This way, f_save is called only when saving a file, and not when
00119        saving an undo backup copy */
00120     o_save (toplevel, s_page_objects (toplevel->page_current), filename, NULL);
00121 
00122   } else if (w_current->undo_type == UNDO_MEMORY && flag == UNDO_ALL) {
00123     object_list = o_glist_copy_all (toplevel,
00124                                     s_page_objects (toplevel->page_current),
00125                                     object_list);
00126   }
00127 
00128   /* Clear Anything above current */
00129   if (toplevel->page_current->undo_current) {
00130     s_undo_remove_rest(toplevel,
00131                        toplevel->page_current->undo_current->next);
00132     toplevel->page_current->undo_current->next = NULL;
00133   } else { /* undo current is NULL */
00134     s_undo_remove_rest(toplevel,
00135                        toplevel->page_current->undo_bottom);
00136     toplevel->page_current->undo_bottom = NULL;
00137   }
00138 
00139   toplevel->page_current->undo_tos = toplevel->page_current->undo_current;
00140 
00141   toplevel->page_current->undo_tos =
00142   s_undo_add(toplevel->page_current->undo_tos,
00143              flag, filename, object_list,
00144              toplevel->page_current->left,
00145              toplevel->page_current->top,
00146              toplevel->page_current->right,
00147              toplevel->page_current->bottom,
00148              toplevel->page_current->page_control,
00149              toplevel->page_current->up);
00150 
00151   toplevel->page_current->undo_current =
00152       toplevel->page_current->undo_tos;
00153 
00154   if (toplevel->page_current->undo_bottom == NULL) {
00155     toplevel->page_current->undo_bottom =
00156         toplevel->page_current->undo_tos;
00157   }
00158 
00159 #if DEBUG
00160   printf("\n\n---Undo----\n");
00161   s_undo_print_all(toplevel->page_current->undo_bottom);
00162   printf("BOTTOM: %s\n", toplevel->page_current->undo_bottom->filename);
00163   printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
00164   printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
00165   printf("----\n");
00166 #endif
00167 
00168   g_free(filename);
00169 
00170   /* Now go through and see if we need to free/remove some undo levels */ 
00171   /* so we stay within the limits */
00172 
00173   /* only check history every 10 undo savestates */
00174   if (undo_file_index % 10) {
00175     return;
00176   }
00177 
00178   levels = s_undo_levels(toplevel->page_current->undo_bottom);
00179 
00180 #if DEBUG
00181   printf("levels: %d\n", levels);
00182 #endif
00183 
00184   if (levels >= w_current->undo_levels + UNDO_PADDING) {
00185     levels = levels - w_current->undo_levels;
00186 
00187 #if DEBUG
00188     printf("Trimming: %d levels\n", levels);
00189 #endif
00190 
00191     u_current = toplevel->page_current->undo_bottom;
00192 
00193     while (levels > 0) {
00194       /* Because we use a pad you are always guaranteed to never */
00195       /* exhaust the list */
00196       g_assert (u_current != NULL);
00197 
00198       u_current_next = u_current->next;
00199 
00200       if (u_current->filename) {
00201 #if DEBUG
00202         printf("Freeing: %s\n", u_current->filename);
00203 #endif
00204         unlink(u_current->filename);
00205         g_free(u_current->filename);
00206       }
00207 
00208       if (u_current->object_list) {
00209         s_delete_object_glist (toplevel, u_current->object_list);
00210         u_current->object_list = NULL;
00211       }
00212 
00213       u_current->next = NULL;
00214       u_current->prev = NULL;
00215       g_free(u_current);
00216 
00217       u_current = u_current_next;
00218       levels--;
00219     }
00220 
00221     g_assert (u_current != NULL);
00222     u_current->prev = NULL;
00223     toplevel->page_current->undo_bottom = u_current;
00224 
00225 #if DEBUG
00226     printf("New current is: %s\n", u_current->filename);
00227 #endif
00228   }
00229 
00230 #if DEBUG
00231   printf("\n\n---Undo----\n");
00232   s_undo_print_all(toplevel->page_current->undo_bottom);
00233   printf("BOTTOM: %s\n", toplevel->page_current->undo_bottom->filename);
00234   printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
00235   printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
00236   printf("----\n");
00237 #endif
00238 
00239 }
00240 
00246 char *o_undo_find_prev_filename(UNDO *start)
00247 {
00248   UNDO *u_current;
00249 
00250   u_current = start->prev;
00251 
00252   while(u_current) {
00253     if (u_current->filename) {
00254       return(u_current->filename);
00255     }
00256     u_current = u_current->prev;
00257   }
00258 
00259   return(NULL); 
00260 }
00261 
00267 GList *o_undo_find_prev_object_head (UNDO *start)
00268 {
00269   UNDO *u_current;
00270 
00271   u_current = start->prev;
00272 
00273   while(u_current) {
00274     if (u_current->object_list) {
00275       return u_current->object_list;
00276     }
00277     u_current = u_current->prev;
00278   }
00279 
00280   return(NULL); 
00281 }
00282 
00293 void o_undo_callback(GSCHEM_TOPLEVEL *w_current, int type)
00294 {
00295   TOPLEVEL *toplevel = w_current->toplevel;
00296   UNDO *u_current;
00297   UNDO *u_next;
00298   UNDO *save_bottom;
00299   UNDO *save_tos;
00300   UNDO *save_current;
00301   int save_logging;
00302   int find_prev_data=FALSE;
00303 
00304   char *save_filename;
00305 
00306   if (w_current->undo_control == FALSE) {
00307     s_log_message(_("Undo/Redo disabled in rc file\n"));
00308     return;
00309   }
00310 
00311   if (toplevel->page_current->undo_current == NULL) {
00312     return;
00313   }
00314 
00315   if (type == UNDO_ACTION) {
00316     u_current = toplevel->page_current->undo_current->prev;
00317   } else {
00318     u_current = toplevel->page_current->undo_current->next;
00319   }
00320 
00321   u_next = toplevel->page_current->undo_current;
00322 
00323   if (u_current == NULL) {
00324     return;
00325   }
00326 
00327   if (u_next->type == UNDO_ALL && u_current->type == UNDO_VIEWPORT_ONLY) {
00328 #if DEBUG
00329     printf("Type: %d\n", u_current->type);
00330     printf("Current is an undo all, next is viewport only!\n");
00331 #endif
00332     find_prev_data = TRUE;
00333 
00334     if (w_current->undo_type == UNDO_DISK) {
00335       u_current->filename = o_undo_find_prev_filename(u_current);
00336     } else {
00337       u_current->object_list = o_undo_find_prev_object_head (u_current);
00338     }
00339   }
00340 
00341   /* save filename */
00342   save_filename = g_strdup (toplevel->page_current->page_filename);
00343 
00344   /* save structure so it's not nuked */
00345   save_bottom = toplevel->page_current->undo_bottom;
00346   save_tos = toplevel->page_current->undo_tos;
00347   save_current = toplevel->page_current->undo_current;
00348   toplevel->page_current->undo_bottom = NULL;
00349   toplevel->page_current->undo_tos = NULL;
00350   toplevel->page_current->undo_current = NULL;
00351 
00352   if (w_current->undo_type == UNDO_DISK && u_current->filename) {
00353     PAGE *p_new;
00354     s_page_delete (toplevel, toplevel->page_current);
00355     p_new = s_page_new(toplevel, u_current->filename);
00356     s_page_goto (toplevel, p_new);
00357   } else if (w_current->undo_type == UNDO_MEMORY && u_current->object_list) {
00358     PAGE *p_new;
00359     s_page_delete (toplevel, toplevel->page_current);
00360     p_new = s_page_new (toplevel, save_filename);
00361     s_page_goto (toplevel, p_new);
00362   }
00363 
00364   /* temporarily disable logging */
00365   save_logging = do_logging;
00366   do_logging = FALSE;
00367 
00368   if (w_current->undo_type == UNDO_DISK && u_current->filename) {
00369 
00370     f_open(toplevel, toplevel->page_current, u_current->filename, NULL);
00371 
00372     x_manual_resize(w_current);
00373     toplevel->page_current->page_control = u_current->page_control;
00374     toplevel->page_current->up = u_current->up;
00375     toplevel->page_current->CHANGED=1;
00376 
00377   } else if (w_current->undo_type == UNDO_MEMORY && u_current->object_list) {
00378 
00379     s_page_delete_objects (toplevel, toplevel->page_current);
00380 
00381     s_page_append_list (toplevel, toplevel->page_current,
00382                         o_glist_copy_all (toplevel, u_current->object_list,
00383                                           NULL));
00384 
00385     x_manual_resize(w_current);
00386     toplevel->page_current->page_control = u_current->page_control;
00387     toplevel->page_current->up = u_current->up;
00388     toplevel->page_current->CHANGED=1;
00389   }
00390 
00391   /* do misc setups */
00392   set_window(toplevel, toplevel->page_current,
00393              u_current->left, u_current->right,
00394              u_current->top, u_current->bottom);
00395   x_hscrollbar_update(w_current);
00396   x_vscrollbar_update(w_current);
00397 
00398   /* restore logging */
00399   do_logging = save_logging;
00400 
00401   /* set filename right */
00402   g_free(toplevel->page_current->page_filename);
00403   toplevel->page_current->page_filename = save_filename;
00404 
00405   /* final redraw */
00406   x_pagesel_update (w_current);
00407   x_multiattrib_update (w_current);
00408 
00409   /* Let the caller to decide if redraw or not */
00410   o_invalidate_all (w_current);
00411   i_update_menus(w_current);
00412 
00413   /* restore saved undo structures */
00414   toplevel->page_current->undo_bottom = save_bottom;
00415   toplevel->page_current->undo_tos = save_tos;
00416   toplevel->page_current->undo_current = save_current;
00417 
00418   if (type == UNDO_ACTION) {
00419     if (toplevel->page_current->undo_current) {
00420       toplevel->page_current->undo_current =
00421           toplevel->page_current->undo_current->prev;
00422       if (toplevel->page_current->undo_current == NULL) {
00423         toplevel->page_current->undo_current =
00424             toplevel->page_current->undo_bottom;
00425       }
00426     }
00427   } else { /* type is REDO_ACTION */
00428     if (toplevel->page_current->undo_current) {
00429       toplevel->page_current->undo_current =
00430           toplevel->page_current->undo_current->next;
00431       if (toplevel->page_current->undo_current == NULL) {
00432         toplevel->page_current->undo_current =
00433             toplevel->page_current->undo_tos;
00434       }
00435     }
00436   }
00437 
00438   /* don't have to free data here since filename, object_list are */
00439   /* just pointers to the real data (lower in the stack) */
00440   if (find_prev_data) {
00441     u_current->filename = NULL;
00442     u_current->object_list = NULL;
00443   }
00444 
00445 #if DEBUG
00446   printf("\n\n---Undo----\n");
00447   s_undo_print_all(toplevel->page_current->undo_bottom);
00448   printf("TOS: %s\n", toplevel->page_current->undo_tos->filename);
00449   printf("CURRENT: %s\n", toplevel->page_current->undo_current->filename);
00450   printf("----\n");
00451 #endif
00452 }
00453 
00459 void o_undo_cleanup(void)
00460 {
00461   int i;
00462   char *filename;
00463 
00464   for (i = 0 ; i < undo_file_index; i++) {
00465     filename = g_strdup_printf("%s%cgschem.save%d_%d.sch", tmp_path,
00466                                G_DIR_SEPARATOR, prog_pid, i);
00467     unlink(filename);
00468     g_free(filename);
00469   }
00470 
00471   g_free(tmp_path);
00472   tmp_path = NULL;
00473 }
00474 
00480 void o_undo_remove_last_undo(GSCHEM_TOPLEVEL *w_current)
00481 {
00482   TOPLEVEL *toplevel = w_current->toplevel;
00483   if (toplevel->page_current->undo_current == NULL) {
00484     return;
00485   }
00486 
00487   if (toplevel->page_current->undo_current) {
00488     toplevel->page_current->undo_current =
00489         toplevel->page_current->undo_current->prev;
00490     if (toplevel->page_current->undo_current == NULL) {
00491       toplevel->page_current->undo_current =
00492           toplevel->page_current->undo_bottom;
00493     }
00494   }
00495 
00496 
00497 
00498 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines