gschem
|
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 }