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
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
32 * \image html s_page_overview.png
33 * \image latex s_page_overview.pdf "page overview" width=14cm
49 #include <sys/types.h>
53 #include "libgeda_priv.h"
55 #ifdef HAVE_LIBDMALLOC
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
69 PAGE
*s_page_new (TOPLEVEL
*toplevel
, const gchar
*filename
)
73 /* Now create a blank page */
74 page
= (PAGE
*)g_new0 (PAGE
, 1);
76 page
->pid
= global_pid
++;
80 /* big assumption here that page_filename isn't null */
81 if (g_path_is_absolute (filename
)) {
82 page
->page_filename
= g_strdup (filename
);
84 gchar
*pwd
= g_get_current_dir ();
85 page
->page_filename
= g_build_filename (pwd
, filename
, NULL
);
89 g_assert (toplevel
->init_bottom
!= 0);
90 page
->coord_aspectratio
= (
91 ((float) toplevel
->init_right
) / ((float) toplevel
->init_bottom
));
94 page
->page_control
= 0;
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 */
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
);
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
141 void s_page_delete (TOPLEVEL
*toplevel
, PAGE
*page
)
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:
155 /* save page_current and switch to page */
156 if (page
== toplevel
->page_current
) {
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
);
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."),
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
;
197 printf("Freeing page: %s\n", page
->page_filename
);
198 s_tile_print(toplevel
);
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 */
209 g_free (page
->page_filename
);
211 geda_list_remove( toplevel
->pages
, page
);
214 s_tile_print (toplevel
);
219 /* restore page_current */
221 s_page_goto (toplevel
, tmp
);
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
;
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
)
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?
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
)
295 for ( iter
= geda_list_get_glist( toplevel
->pages
);
297 iter
= g_list_next( iter
) ) {
299 page
= (PAGE
*)iter
->data
;
300 if ( g_strcasecmp( page
->page_filename
, filename
) == 0 )
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
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
)
320 for ( iter
= geda_list_get_glist (list
);
322 iter
= g_list_next (iter
) ) {
323 PAGE
*page
= (PAGE
*)iter
->data
;
324 if (page
->pid
== pid
) {
332 /*! \brief Print full TOPLEVEL structure.
333 * \par Function Description
334 * This function prints the internal structure of <B>toplevel</B>'s
337 * \param [in] toplevel The TOPLEVEL object to print.
339 void s_page_print_all (TOPLEVEL
*toplevel
)
344 for ( iter
= geda_list_get_glist( toplevel
->pages
);
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
)
364 PAGE
*p_save
, *p_current
;
367 /* save current page */
368 p_save
= toplevel
->page_current
;
370 for ( iter
= geda_list_get_glist( toplevel
->pages
);
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;
386 s_log_message (_("Could NOT save [%s]\n"),
387 toplevel
->page_current
->page_filename
);
388 /* increase the error counter */
394 /* restore current page */
397 s_page_goto (toplevel
, p_save
);
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>
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
)
416 for ( iter
= geda_list_get_glist( list
);
418 iter
= g_list_next( iter
) ) {
420 p_current
= (PAGE
*)iter
->data
;
421 if (p_current
->CHANGED
) {
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
)
440 for ( iter
= geda_list_get_glist( list
);
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
,
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
)
481 if (toplevel
== NULL
) {
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
);
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
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
)
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
)
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
);
651 list
= g_list_reverse (list
);