Updated copyright text/header in most source files.
[geda-gaf/peter-b.git] / libgeda / src / s_page.c
blob0306da1bebd9c84421b6c95e42e3554675fade53
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
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
21 /*! \file s_page.c
22 * \brief The page system
24 * libgeda can handle multiple schematic or symbol pages. libgeda keeps
25 * track of the currently opened pages with a managed _GedaList.
26 * The currently used page is refered with an extra pointer.
28 * Each page carries a list of the objects that are on the page.
29 * The first and the last element are referenced by the head and tail
30 * pointers.
32 * \image html s_page_overview.png
33 * \image latex s_page_overview.pdf "page overview" width=14cm
36 #include <config.h>
38 #include <stdio.h>
39 #ifdef HAVE_STDLIB_H
40 #include <stdlib.h>
41 #endif
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 #ifdef HAVE_STRING_H
46 #include <string.h>
47 #endif
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
53 #include "libgeda_priv.h"
55 #ifdef HAVE_LIBDMALLOC
56 #include <dmalloc.h>
57 #endif
59 static gint global_pid = 0;
61 /*! \brief create a new page object
62 * \par Function Description
63 * Creates a new page and add it to <B>toplevel</B>'s list of pages.
65 * It initializes the #PAGE structure and set its <B>page_filename</B>
66 * to <B>filename</B>. <B>toplevel</B>'s current page is not changed by
67 * this function.
69 PAGE *s_page_new (TOPLEVEL *toplevel, const gchar *filename)
71 PAGE *page;
73 /* Now create a blank page */
74 page = (PAGE*)g_new0 (PAGE, 1);
76 page->pid = global_pid++;
78 page->CHANGED = 0;
80 /* big assumption here that page_filename isn't null */
81 if (g_path_is_absolute (filename)) {
82 page->page_filename = g_strdup (filename);
83 } else {
84 gchar *pwd = g_get_current_dir ();
85 page->page_filename = g_build_filename (pwd, filename, NULL);
86 g_free (pwd);
89 g_assert (toplevel->init_bottom != 0);
90 page->coord_aspectratio = (
91 ((float) toplevel->init_right) / ((float) toplevel->init_bottom));
93 page->up = -2;
94 page->page_control = 0;
96 /* Init tile array */
97 s_tile_init (toplevel, page);
99 /* Init the object list */
100 page->_object_list = NULL;
102 /* new selection mechanism */
103 page->selection_list = o_selection_new();
105 /* net/pin/bus stretch when doing moves */
106 page->stretch_list = NULL;
108 page->place_list = NULL;
110 /* init undo struct pointers */
111 s_undo_init(page);
113 page->object_lastplace = NULL;
115 set_window (toplevel, page,
116 toplevel->init_left, toplevel->init_right,
117 toplevel->init_top, toplevel->init_bottom);
119 /* Backup variables */
120 g_get_current_time (&page->last_load_or_save_time);
121 page->ops_since_last_backup = 0;
122 page->saved_since_first_loaded = 0;
123 page->do_autosave_backup = 0;
125 /* now append page to page list of toplevel */
126 geda_list_add( toplevel->pages, page );
128 return page;
131 /*! \brief delete a page and it's contents
132 * \par Function Description
133 * Deletes a single page <B>page</B> from <B>toplevel</B>'s list of pages.
135 * See #s_page_delete_list() to delete all pages of a <B>toplevel</B>
137 * If the current page of toplevel is given as parameter <B>page</B>,
138 * the function sets the field <B>page_current</B> of the TOPLEVEL
139 * struct to NULL.
141 void s_page_delete (TOPLEVEL *toplevel, PAGE *page)
143 PAGE *tmp;
144 gchar *backup_filename;
145 gchar *real_filename;
147 /* We need to temporarily make the page being deleted current because
148 * various functions called below (some indirectly) assume they are
149 * deleting objects from the current page.
151 * These functions are known to include:
152 * s_delete_object ()
155 /* save page_current and switch to page */
156 if (page == toplevel->page_current) {
157 tmp = NULL;
158 } else {
159 tmp = toplevel->page_current;
160 s_page_goto (toplevel, page);
163 /* Get the real filename and file permissions */
164 real_filename = follow_symlinks (page->page_filename, NULL);
166 if (real_filename == NULL) {
167 s_log_message (_("s_page_delete: Can't get the real filename of %s."),
168 page->page_filename);
170 else {
171 backup_filename = f_get_autosave_filename (real_filename);
173 /* Delete the backup file */
174 if ( (g_file_test (backup_filename, G_FILE_TEST_EXISTS)) &&
175 (!g_file_test(backup_filename, G_FILE_TEST_IS_DIR)) )
177 if (unlink(backup_filename) != 0) {
178 s_log_message(_("s_page_delete: Unable to delete backup file %s."),
179 backup_filename);
182 g_free (backup_filename);
184 g_free(real_filename);
186 /* Free the selection object */
187 g_object_unref( page->selection_list );
189 /* then delete objects of page */
190 s_page_delete_objects (toplevel, page);
192 /* Free the objects in the place list. */
193 s_delete_object_glist (toplevel, page->place_list);
194 page->place_list = NULL;
196 #if DEBUG
197 printf("Freeing page: %s\n", page->page_filename);
198 s_tile_print(toplevel);
199 #endif
200 s_tile_free_all (page);
202 s_stretch_destroy_all (page->stretch_list);
204 /* free current page undo structs */
205 s_undo_free_all (toplevel, page);
207 /* ouch, deal with parents going away and the children still around */
208 page->up = -2;
209 g_free (page->page_filename);
211 geda_list_remove( toplevel->pages, page );
213 #if DEBUG
214 s_tile_print (toplevel);
215 #endif
217 g_free (page);
219 /* restore page_current */
220 if (tmp != NULL) {
221 s_page_goto (toplevel, tmp);
222 } else {
223 /* page was page_current */
224 toplevel->page_current = NULL;
225 /* page_current must be updated by calling function */
231 /*! \brief Deletes the list of pages of <B>toplevel</B>.
232 * \par Function Description
233 * Deletes the list of pages of <B>toplevel</B>.
234 * This function should only be called when you are finishing up.
236 * \param toplevel The TOPLEVEL object.
238 void s_page_delete_list(TOPLEVEL *toplevel)
240 GList *list_copy, *iter;
241 PAGE *page;
243 /* s_page_delete removes items from the page list, so make a copy */
244 list_copy = g_list_copy (geda_list_get_glist (toplevel->pages));
246 for (iter = list_copy; iter != NULL; iter = g_list_next (iter)) {
247 page = (PAGE *)iter->data;
249 s_page_delete (toplevel, page);
252 g_list_free (list_copy);
254 /* reset toplevel fields */
255 toplevel->page_current = NULL;
258 /*! \brief changes the current page in toplevel
259 * \par Function Description
260 * Changes the current page in \a toplevel to the page \a p_new.
262 * \param toplevel The TOPLEVEL object
263 * \param p_new The PAGE to go to
265 void s_page_goto (TOPLEVEL *toplevel, PAGE *p_new)
267 gchar *dirname;
269 toplevel->page_current = p_new;
271 dirname = g_dirname (p_new->page_filename);
272 if (chdir (dirname)) {
273 /* An error occured with chdir */
274 #warning FIXME: What do we do?
276 g_free (dirname);
280 /*! \brief Search for pages by filename.
281 * \par Function Description
282 * Searches in \a toplevel's list of pages for a page with a filename
283 * equal to \a filename.
285 * \param toplevel The TOPLEVEL object
286 * \param filename The filename string to search for
288 * \return PAGE pointer to a matching page, NULL otherwise.
290 PAGE *s_page_search (TOPLEVEL *toplevel, const gchar *filename)
292 const GList *iter;
293 PAGE *page;
295 for ( iter = geda_list_get_glist( toplevel->pages );
296 iter != NULL;
297 iter = g_list_next( iter ) ) {
299 page = (PAGE *)iter->data;
300 if ( g_strcasecmp( page->page_filename, filename ) == 0 )
301 return page;
303 return NULL;
306 /*! \brief Search for a page given its page id in a page list.
307 * \par Function Description
308 * This functions returns the page that have the page id \a pid in
309 * the list of pages starting at \a page_list, or NULL if there is no
310 * such page.
312 * \param [in] list The list of page to search the page in.
313 * \param [in] pid The ID of the page to find.
314 * \returns A pointer on the page found or NULL if not found.
316 PAGE *s_page_search_by_page_id (GedaPageList *list, int pid)
318 const GList *iter;
320 for ( iter = geda_list_get_glist (list);
321 iter != NULL;
322 iter = g_list_next (iter) ) {
323 PAGE *page = (PAGE *)iter->data;
324 if (page->pid == pid) {
325 return page;
329 return NULL;
332 /*! \brief Print full TOPLEVEL structure.
333 * \par Function Description
334 * This function prints the internal structure of <B>toplevel</B>'s
335 * list of pages.
337 * \param [in] toplevel The TOPLEVEL object to print.
339 void s_page_print_all (TOPLEVEL *toplevel)
341 const GList *iter;
342 PAGE *page;
344 for ( iter = geda_list_get_glist( toplevel->pages );
345 iter != NULL;
346 iter = g_list_next( iter ) ) {
348 page = (PAGE *)iter->data;
349 printf ("FILENAME: %s\n", page->page_filename);
350 print_struct_forw (page->_object_list);
354 /*! \brief Saves all the pages of a TOPLEVEL object.
355 * \par Function Description
356 * Saves all the pages in the <B>toplevel</B> parameter.
358 * \param [in] toplevel The TOPLEVEL to save pages from.
359 * \return The number of failed tries to save a page.
361 gint s_page_save_all (TOPLEVEL *toplevel)
363 const GList *iter;
364 PAGE *p_save, *p_current;
365 gint status = 0;
367 /* save current page */
368 p_save = toplevel->page_current;
370 for ( iter = geda_list_get_glist( toplevel->pages );
371 iter != NULL;
372 iter = g_list_next( iter ) ) {
374 p_current = (PAGE *)iter->data;
376 /* make p_current the current page of toplevel */
377 s_page_goto (toplevel, p_current);
379 if (f_save (toplevel, p_current->page_filename)) {
380 s_log_message (_("Saved [%s]\n"),
381 toplevel->page_current->page_filename);
382 /* reset the CHANGED flag of p_current */
383 p_current->CHANGED = 0;
385 } else {
386 s_log_message (_("Could NOT save [%s]\n"),
387 toplevel->page_current->page_filename);
388 /* increase the error counter */
389 status++;
394 /* restore current page */
395 if (p_save != NULL)
397 s_page_goto (toplevel, p_save);
400 return status;
403 /*! \brief Check if CHANGED flag is set for any page in list.
404 * \par Function Description
405 * This function checks the CHANGED flag for all pages in the <B>list</B>
406 * object.
408 * \param [in] list GedaPageList to check CHANGED flag in.
409 * \return 1 if any page has the CHANGED flag set, 0 otherwise.
411 gboolean s_page_check_changed (GedaPageList *list)
413 const GList *iter;
414 PAGE *p_current;
416 for ( iter = geda_list_get_glist( list );
417 iter != NULL;
418 iter = g_list_next( iter ) ) {
420 p_current = (PAGE *)iter->data;
421 if (p_current->CHANGED) {
422 return TRUE;
426 return FALSE;
429 /*! \brief Reset the CHANGED flag of all pages.
430 * \par Function Description
431 * This function resets the CHANGED flag of each page following \a head.
433 * \param [in,out] list PAGE list to set CHANGED flags in.
435 void s_page_clear_changed (GedaPageList *list)
437 const GList *iter;
438 PAGE *p_current;
440 for ( iter = geda_list_get_glist( list );
441 iter != NULL;
442 iter = g_list_next( iter ) ) {
444 p_current = (PAGE *)iter->data;
445 p_current->CHANGED = 0;
449 /*! \brief Autosave initialization function.
450 * \par Function Description
451 * This function sets up the autosave callback function.
453 * \param [in] toplevel The TOPLEVEL object.
455 void s_page_autosave_init(TOPLEVEL *toplevel)
457 if (toplevel->auto_save_interval != 0) {
459 /* 1000 converts seconds into milliseconds */
460 toplevel->auto_save_timeout =
461 g_timeout_add(toplevel->auto_save_interval*1000,
462 (GSourceFunc) s_page_autosave,
463 toplevel);
467 /*! \brief Autosave callback function.
468 * \par Function Description
469 * This function is a callback of the glib g_timeout functions.
470 * It is called every "interval" milliseconds and it sets a flag to save
471 * a backup copy of the opened pages.
473 * \param [in] toplevel The TOPLEVEL object.
474 * \return The length in milliseconds to set for next interval.
476 gint s_page_autosave (TOPLEVEL *toplevel)
478 const GList *iter;
479 PAGE *p_current;
481 if (toplevel == NULL) {
482 return 0;
485 /* Do nothing if the interval is 0 */
486 if (toplevel->auto_save_interval == 0) {
487 return toplevel->auto_save_interval;
490 /* Should we just disable the autosave timeout returning 0 or
491 just wait for more pages to be added? */
492 if ( toplevel->pages == NULL)
493 return toplevel->auto_save_interval;
495 for ( iter = geda_list_get_glist( toplevel->pages );
496 iter != NULL;
497 iter = g_list_next( iter ) ) {
499 p_current = (PAGE *)iter->data;
501 if (p_current->ops_since_last_backup != 0) {
502 /* Real autosave is done in o_undo_savestate */
503 p_current->do_autosave_backup = 1;
507 return toplevel->auto_save_interval;
510 /*! \brief Append an OBJECT to the PAGE
512 * \par Function Description
513 * Links the passed OBJECT to the end of the PAGE's
514 * linked list of objects.
516 * \param [in] toplevel The TOPLEVEL object.
517 * \param [in] page The PAGE the object is being added to.
518 * \param [in] object The OBJECT being added to the page.
520 void s_page_append (TOPLEVEL *toplevel, PAGE *page, OBJECT *object)
522 page->_object_list = g_list_append (page->_object_list, object);
525 /*! \brief Append a GList of OBJECTs to the PAGE
527 * \par Function Description
528 * Links the passed OBJECT GList to the end of the PAGE's
529 * object_list.
531 * \param [in] toplevel The TOPLEVEL object.
532 * \param [in] page The PAGE the objects are being added to.
533 * \param [in] obj_list The OBJECT list being added to the page.
535 void s_page_append_list (TOPLEVEL *toplevel, PAGE *page, GList *obj_list)
537 page->_object_list = g_list_concat (page->_object_list, obj_list);
540 /*! \brief Remove an OBJECT from the PAGE
542 * \par Function Description
543 * Removes the passed OBJECT from the PAGE's
544 * linked list of objects.
546 * \param [in] toplevel The TOPLEVEL object.
547 * \param [in] page The PAGE the object is being removed from.
548 * \param [in] object The OBJECT being removed from the page.
550 void s_page_remove (TOPLEVEL *toplevel, PAGE *page, OBJECT *object)
552 page->_object_list = g_list_remove (page->_object_list, object);
555 /*! \brief Remove and free all OBJECTs from the PAGE
557 * \par Function Description
558 * Removes and frees all OBJECTs from the PAGE.
560 * \param [in] toplevel The TOPLEVEL object.
561 * \param [in] page The PAGE being cleared.
563 void s_page_delete_objects (TOPLEVEL *toplevel, PAGE *page)
565 s_delete_object_glist (toplevel, page->_object_list);
566 page->_object_list = NULL;
570 /*! \brief Return a GList of OBJECTs on the PAGE
572 * \par Function Description
573 * An accessor for the PAGE's GList of objects.
575 * NB: This GList is owned by the PAGE, and must not be
576 * free'd or modified by the caller.
578 * \param [in] page The PAGE to get objects on.
579 * \returns a const pointer to the PAGE's GList of objects
581 const GList *s_page_objects (PAGE *page)
583 return page->_object_list;
587 /*! \brief Find the objects in a given region
589 * \par Function Description
590 * Finds the objects which are inside, or intersect
591 * the passed box shaped region.
593 * \param [in] toplevel The TOPLEVEL object.
594 * \param [in] page The PAGE to find objects on.
595 * \param [in] min_x The smaller X coordinate of the region.
596 * \param [in] min_y The smaller Y coordinate of the region.
597 * \param [in] max_x The larger X coordinate of the region.
598 * \param [in] max_y The larger Y coordinate of the region.
599 * \return The GList of OBJECTs in the region.
601 GList *s_page_objects_in_region (TOPLEVEL *toplevel, PAGE *page,
602 int min_x, int min_y, int max_x, int max_y)
604 BOX rect;
606 rect.lower_x = min_x;
607 rect.lower_y = min_y;
608 rect.upper_x = max_x;
609 rect.upper_y = max_y;
611 return s_page_objects_in_regions (toplevel, page, &rect, 1);
614 /*! \brief Find the objects in a given region
616 * \par Function Description
617 * Finds the objects which are inside, or intersect
618 * the passed box shaped region.
620 * \param [in] toplevel The TOPLEVEL object.
621 * \param [in] page The PAGE to find objects on.
622 * \param [in] rects The BOX regions to check.
623 * \param [in] n_rects The number of regions.
624 * \return The GList of OBJECTs in the region.
626 GList *s_page_objects_in_regions (TOPLEVEL *toplevel, PAGE *page,
627 BOX *rects, int n_rects)
629 GList *iter;
630 GList *list = NULL;
631 int i;
633 for (iter = page->_object_list; iter != NULL; iter = g_list_next (iter)) {
634 OBJECT *object = iter->data;
636 for (i = 0; i < n_rects; i++) {
637 int left, top, right, bottom;
639 if (world_get_single_object_bounds (toplevel, object,
640 &left, &top, &right, &bottom) &&
641 right >= rects[i].lower_x &&
642 left <= rects[i].upper_x &&
643 top <= rects[i].upper_y &&
644 bottom >= rects[i].lower_y) {
645 list = g_list_prepend (list, object);
646 break;
651 list = g_list_reverse (list);
652 return list;