libgeda
|
00001 /* gEDA - GPL Electronic Design Automation 00002 * libgeda - gEDA's library 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 00036 #include <config.h> 00037 00038 #include <stdio.h> 00039 #ifdef HAVE_STDLIB_H 00040 #include <stdlib.h> 00041 #endif 00042 #ifdef HAVE_UNISTD_H 00043 #include <unistd.h> 00044 #endif 00045 #ifdef HAVE_STRING_H 00046 #include <string.h> 00047 #endif 00048 00049 #include <sys/types.h> 00050 #include <sys/stat.h> 00051 #include <unistd.h> 00052 00053 #include "libgeda_priv.h" 00054 00055 #ifdef HAVE_LIBDMALLOC 00056 #include <dmalloc.h> 00057 #endif 00058 00059 static gint global_pid = 0; 00060 00061 /* Called just before removing an OBJECT from a PAGE. */ 00062 static void 00063 object_added (TOPLEVEL *toplevel, PAGE *page, OBJECT *object) 00064 { 00065 /* Set up object parent pointer */ 00066 #ifndef NDEBUG 00067 if (object->page != NULL) { 00068 g_critical ("Object %p already has parent page %p!", object, object->page); 00069 } 00070 #endif 00071 object->page = page; 00072 00073 /* Add object to tile system. */ 00074 s_tile_add_object (toplevel, object); 00075 00076 /* Update object connection tracking */ 00077 s_conn_update_object (toplevel, object); 00078 00079 o_emit_change_notify (toplevel, object); 00080 } 00081 00082 /* Called just before removing an OBJECT from a PAGE. */ 00083 static void 00084 pre_object_removed (TOPLEVEL *toplevel, PAGE *page, OBJECT *object) 00085 { 00086 o_emit_pre_change_notify (toplevel, object); 00087 00088 /* Clear object parent pointer */ 00089 #ifndef NDEBUG 00090 if (object->page == NULL) { 00091 g_critical ("Object %p has NULL parent page!", object); 00092 } 00093 #endif 00094 object->page = NULL; 00095 00096 /* Clear page's object_lastplace pointer if set */ 00097 if (page->object_lastplace == object) { 00098 page->object_lastplace = NULL; 00099 } 00100 00101 /* Remove object from connection system */ 00102 s_conn_remove_object (toplevel, object); 00103 00104 /* Remove object from tile system */ 00105 s_tile_remove_object (object); 00106 } 00107 00116 PAGE *s_page_new (TOPLEVEL *toplevel, const gchar *filename) 00117 { 00118 PAGE *page; 00119 00120 /* Now create a blank page */ 00121 page = (PAGE*)g_new0 (PAGE, 1); 00122 00123 page->pid = global_pid++; 00124 00125 page->CHANGED = 0; 00126 00127 /* big assumption here that page_filename isn't null */ 00128 if (g_path_is_absolute (filename)) { 00129 page->page_filename = g_strdup (filename); 00130 } else { 00131 gchar *pwd = g_get_current_dir (); 00132 page->page_filename = g_build_filename (pwd, filename, NULL); 00133 g_free (pwd); 00134 } 00135 00136 g_assert (toplevel->init_bottom != 0); 00137 page->coord_aspectratio = ( 00138 ((float) toplevel->init_right) / ((float) toplevel->init_bottom)); 00139 00140 page->up = -2; 00141 page->page_control = 0; 00142 00143 /* Init tile array */ 00144 s_tile_init (toplevel, page); 00145 00146 /* Init the object list */ 00147 page->_object_list = NULL; 00148 00149 /* new selection mechanism */ 00150 page->selection_list = o_selection_new(); 00151 00152 page->place_list = NULL; 00153 00154 /* init undo struct pointers */ 00155 s_undo_init(page); 00156 00157 page->object_lastplace = NULL; 00158 00159 page->weak_refs = NULL; 00160 00161 set_window (toplevel, page, 00162 toplevel->init_left, toplevel->init_right, 00163 toplevel->init_top, toplevel->init_bottom); 00164 00165 /* Backup variables */ 00166 g_get_current_time (&page->last_load_or_save_time); 00167 page->ops_since_last_backup = 0; 00168 page->saved_since_first_loaded = 0; 00169 page->do_autosave_backup = 0; 00170 00171 /* now append page to page list of toplevel */ 00172 geda_list_add( toplevel->pages, page ); 00173 00174 return page; 00175 } 00176 00187 void s_page_delete (TOPLEVEL *toplevel, PAGE *page) 00188 { 00189 PAGE *tmp; 00190 gchar *backup_filename; 00191 gchar *real_filename; 00192 00193 /* We need to temporarily make the page being deleted current because 00194 * various functions called below (some indirectly) assume they are 00195 * deleting objects from the current page. 00196 * 00197 * These functions are known to include: 00198 * s_delete_object () 00199 */ 00200 00201 /* save page_current and switch to page */ 00202 if (page == toplevel->page_current) { 00203 tmp = NULL; 00204 } else { 00205 tmp = toplevel->page_current; 00206 s_page_goto (toplevel, page); 00207 } 00208 00209 /* Get the real filename and file permissions */ 00210 real_filename = follow_symlinks (page->page_filename, NULL); 00211 00212 if (real_filename == NULL) { 00213 s_log_message (_("s_page_delete: Can't get the real filename of %s."), 00214 page->page_filename); 00215 } 00216 else { 00217 backup_filename = f_get_autosave_filename (real_filename); 00218 00219 /* Delete the backup file */ 00220 if ( (g_file_test (backup_filename, G_FILE_TEST_EXISTS)) && 00221 (!g_file_test(backup_filename, G_FILE_TEST_IS_DIR)) ) 00222 { 00223 if (unlink(backup_filename) != 0) { 00224 s_log_message(_("s_page_delete: Unable to delete backup file %s."), 00225 backup_filename); 00226 } 00227 } 00228 g_free (backup_filename); 00229 } 00230 g_free(real_filename); 00231 00232 /* Free the selection object */ 00233 g_object_unref( page->selection_list ); 00234 00235 /* then delete objects of page */ 00236 s_page_delete_objects (toplevel, page); 00237 00238 /* Free the objects in the place list. */ 00239 s_delete_object_glist (toplevel, page->place_list); 00240 page->place_list = NULL; 00241 00242 #if DEBUG 00243 printf("Freeing page: %s\n", page->page_filename); 00244 s_tile_print(toplevel, page); 00245 #endif 00246 s_tile_free_all (page); 00247 00248 /* free current page undo structs */ 00249 s_undo_free_all (toplevel, page); 00250 00251 /* ouch, deal with parents going away and the children still around */ 00252 page->up = -2; 00253 g_free (page->page_filename); 00254 00255 geda_list_remove( toplevel->pages, page ); 00256 00257 #if DEBUG 00258 s_tile_print (toplevel, page); 00259 #endif 00260 00261 s_weakref_notify (page, page->weak_refs); 00262 00263 g_free (page); 00264 00265 /* restore page_current */ 00266 if (tmp != NULL) { 00267 s_page_goto (toplevel, tmp); 00268 } else { 00269 /* page was page_current */ 00270 toplevel->page_current = NULL; 00271 /* page_current must be updated by calling function */ 00272 } 00273 00274 } 00275 00276 00284 void s_page_delete_list(TOPLEVEL *toplevel) 00285 { 00286 GList *list_copy, *iter; 00287 PAGE *page; 00288 00289 /* s_page_delete removes items from the page list, so make a copy */ 00290 list_copy = g_list_copy (geda_list_get_glist (toplevel->pages)); 00291 00292 for (iter = list_copy; iter != NULL; iter = g_list_next (iter)) { 00293 page = (PAGE *)iter->data; 00294 00295 s_page_delete (toplevel, page); 00296 } 00297 00298 g_list_free (list_copy); 00299 00300 /* reset toplevel fields */ 00301 toplevel->page_current = NULL; 00302 } 00303 00316 void 00317 s_page_weak_ref (PAGE *page, 00318 void (*notify_func)(void *, void *), 00319 void *user_data) 00320 { 00321 g_return_if_fail (page != NULL); 00322 page->weak_refs = s_weakref_add (page->weak_refs, notify_func, user_data); 00323 } 00324 00335 void 00336 s_page_weak_unref (PAGE *page, 00337 void (*notify_func)(void *, void *), 00338 void *user_data) 00339 { 00340 g_return_if_fail (page != NULL); 00341 page->weak_refs = s_weakref_remove (page->weak_refs, 00342 notify_func, user_data); 00343 } 00344 00356 void 00357 s_page_add_weak_ptr (PAGE *page, 00358 void *weak_pointer_loc) 00359 { 00360 g_return_if_fail (page != NULL); 00361 page->weak_refs = s_weakref_add_ptr (page->weak_refs, weak_pointer_loc); 00362 } 00363 00373 void 00374 s_page_remove_weak_ptr (PAGE *page, 00375 void *weak_pointer_loc) 00376 { 00377 g_return_if_fail (page != NULL); 00378 page->weak_refs = s_weakref_remove_ptr (page->weak_refs, 00379 weak_pointer_loc); 00380 } 00381 00389 void s_page_goto (TOPLEVEL *toplevel, PAGE *p_new) 00390 { 00391 gchar *dirname; 00392 00393 toplevel->page_current = p_new; 00394 00395 dirname = g_dirname (p_new->page_filename); 00396 if (chdir (dirname)) { 00397 /* An error occured with chdir */ 00398 #warning FIXME: What do we do? 00399 } 00400 g_free (dirname); 00401 00402 } 00403 00414 PAGE *s_page_search (TOPLEVEL *toplevel, const gchar *filename) 00415 { 00416 const GList *iter; 00417 PAGE *page; 00418 00419 for ( iter = geda_list_get_glist( toplevel->pages ); 00420 iter != NULL; 00421 iter = g_list_next( iter ) ) { 00422 00423 page = (PAGE *)iter->data; 00424 if ( g_strcasecmp( page->page_filename, filename ) == 0 ) 00425 return page; 00426 } 00427 return NULL; 00428 } 00429 00440 PAGE *s_page_search_by_page_id (GedaPageList *list, int pid) 00441 { 00442 const GList *iter; 00443 00444 for ( iter = geda_list_get_glist (list); 00445 iter != NULL; 00446 iter = g_list_next (iter) ) { 00447 PAGE *page = (PAGE *)iter->data; 00448 if (page->pid == pid) { 00449 return page; 00450 } 00451 } 00452 00453 return NULL; 00454 } 00455 00463 void s_page_print_all (TOPLEVEL *toplevel) 00464 { 00465 const GList *iter; 00466 PAGE *page; 00467 00468 for ( iter = geda_list_get_glist( toplevel->pages ); 00469 iter != NULL; 00470 iter = g_list_next( iter ) ) { 00471 00472 page = (PAGE *)iter->data; 00473 printf ("FILENAME: %s\n", page->page_filename); 00474 print_struct_forw (page->_object_list); 00475 } 00476 } 00477 00485 gint s_page_save_all (TOPLEVEL *toplevel) 00486 { 00487 const GList *iter; 00488 PAGE *p_current; 00489 gint status = 0; 00490 00491 for ( iter = geda_list_get_glist( toplevel->pages ); 00492 iter != NULL; 00493 iter = g_list_next( iter ) ) { 00494 00495 p_current = (PAGE *)iter->data; 00496 00497 if (f_save (toplevel, p_current, 00498 p_current->page_filename, NULL)) { 00499 s_log_message (_("Saved [%s]\n"), 00500 p_current->page_filename); 00501 /* reset the CHANGED flag of p_current */ 00502 p_current->CHANGED = 0; 00503 00504 } else { 00505 s_log_message (_("Could NOT save [%s]\n"), 00506 p_current->page_filename); 00507 /* increase the error counter */ 00508 status++; 00509 } 00510 00511 } 00512 00513 return status; 00514 } 00515 00524 gboolean s_page_check_changed (GedaPageList *list) 00525 { 00526 const GList *iter; 00527 PAGE *p_current; 00528 00529 for ( iter = geda_list_get_glist( list ); 00530 iter != NULL; 00531 iter = g_list_next( iter ) ) { 00532 00533 p_current = (PAGE *)iter->data; 00534 if (p_current->CHANGED) { 00535 return TRUE; 00536 } 00537 } 00538 00539 return FALSE; 00540 } 00541 00548 void s_page_clear_changed (GedaPageList *list) 00549 { 00550 const GList *iter; 00551 PAGE *p_current; 00552 00553 for ( iter = geda_list_get_glist( list ); 00554 iter != NULL; 00555 iter = g_list_next( iter ) ) { 00556 00557 p_current = (PAGE *)iter->data; 00558 p_current->CHANGED = 0; 00559 } 00560 } 00561 00568 void s_page_autosave_init(TOPLEVEL *toplevel) 00569 { 00570 if (toplevel->auto_save_interval != 0) { 00571 00572 /* 1000 converts seconds into milliseconds */ 00573 toplevel->auto_save_timeout = 00574 g_timeout_add(toplevel->auto_save_interval*1000, 00575 (GSourceFunc) s_page_autosave, 00576 toplevel); 00577 } 00578 } 00579 00589 gint s_page_autosave (TOPLEVEL *toplevel) 00590 { 00591 const GList *iter; 00592 PAGE *p_current; 00593 00594 if (toplevel == NULL) { 00595 return 0; 00596 } 00597 00598 /* Do nothing if the interval is 0 */ 00599 if (toplevel->auto_save_interval == 0) { 00600 return toplevel->auto_save_interval; 00601 } 00602 00603 /* Should we just disable the autosave timeout returning 0 or 00604 just wait for more pages to be added? */ 00605 if ( toplevel->pages == NULL) 00606 return toplevel->auto_save_interval; 00607 00608 for ( iter = geda_list_get_glist( toplevel->pages ); 00609 iter != NULL; 00610 iter = g_list_next( iter ) ) { 00611 00612 p_current = (PAGE *)iter->data; 00613 00614 if (p_current->ops_since_last_backup != 0) { 00615 /* Real autosave is done in o_undo_savestate */ 00616 p_current->do_autosave_backup = 1; 00617 } 00618 } 00619 00620 return toplevel->auto_save_interval; 00621 } 00622 00633 void s_page_append (TOPLEVEL *toplevel, PAGE *page, OBJECT *object) 00634 { 00635 page->_object_list = g_list_append (page->_object_list, object); 00636 object_added (toplevel, page, object); 00637 } 00638 00649 void s_page_append_list (TOPLEVEL *toplevel, PAGE *page, GList *obj_list) 00650 { 00651 GList *iter; 00652 page->_object_list = g_list_concat (page->_object_list, obj_list); 00653 for (iter = obj_list; iter != NULL; iter = g_list_next (iter)) { 00654 object_added (toplevel, page, iter->data); 00655 } 00656 } 00657 00668 void s_page_remove (TOPLEVEL *toplevel, PAGE *page, OBJECT *object) 00669 { 00670 pre_object_removed (toplevel, page, object); 00671 page->_object_list = g_list_remove (page->_object_list, object); 00672 } 00673 00686 void 00687 s_page_replace (TOPLEVEL *toplevel, PAGE *page, 00688 OBJECT *object1, OBJECT *object2) 00689 { 00690 GList *iter = g_list_find (page->_object_list, object1); 00691 00692 /* If object1 not found, append object2 */ 00693 if (iter == NULL) { 00694 s_page_append (toplevel, page, object2); 00695 return; 00696 } 00697 00698 pre_object_removed (toplevel, page, object1); 00699 iter->data = object2; 00700 object_added (toplevel, page, object2); 00701 } 00702 00711 void s_page_delete_objects (TOPLEVEL *toplevel, PAGE *page) 00712 { 00713 GList *objects = page->_object_list; 00714 GList *iter; 00715 for (iter = objects; iter != NULL; iter = g_list_next (iter)) { 00716 pre_object_removed (toplevel, page, iter->data); 00717 } 00718 page->_object_list = NULL; 00719 s_delete_object_glist (toplevel, objects); 00720 } 00721 00722 00734 const GList *s_page_objects (PAGE *page) 00735 { 00736 return page->_object_list; 00737 } 00738 00739 00754 GList *s_page_objects_in_region (TOPLEVEL *toplevel, PAGE *page, 00755 int min_x, int min_y, int max_x, int max_y) 00756 { 00757 BOX rect; 00758 00759 rect.lower_x = min_x; 00760 rect.lower_y = min_y; 00761 rect.upper_x = max_x; 00762 rect.upper_y = max_y; 00763 00764 return s_page_objects_in_regions (toplevel, page, &rect, 1); 00765 } 00766 00779 GList *s_page_objects_in_regions (TOPLEVEL *toplevel, PAGE *page, 00780 BOX *rects, int n_rects) 00781 { 00782 GList *iter; 00783 GList *list = NULL; 00784 int i; 00785 00786 for (iter = page->_object_list; iter != NULL; iter = g_list_next (iter)) { 00787 OBJECT *object = iter->data; 00788 int left, top, right, bottom; 00789 int visible; 00790 00791 visible = world_get_single_object_bounds (toplevel, object, 00792 &left, &top, &right, &bottom); 00793 if (visible) { 00794 for (i = 0; i < n_rects; i++) { 00795 if (right >= rects[i].lower_x && 00796 left <= rects[i].upper_x && 00797 top <= rects[i].upper_y && 00798 bottom >= rects[i].lower_y) { 00799 list = g_list_prepend (list, object); 00800 break; 00801 } 00802 } 00803 } 00804 } 00805 00806 list = g_list_reverse (list); 00807 return list; 00808 }