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)
5  *
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.
10  *
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.
15  *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*! \file s_page.c
22  *  \brief The page system
23  *
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.
27  *
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.
31  *
32  *  \image html s_page_overview.png
33  *  \image latex s_page_overview.pdf "page overview" width=14cm
34  */
35 
36 #include <config.h>
37 
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
48 
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 
53 #include "libgeda_priv.h"
54 
55 #ifdef HAVE_LIBDMALLOC
56 #include <dmalloc.h>
57 #endif
58 
59 static gint global_pid = 0;
60 
61 /* Called just before removing an OBJECT from a PAGE. */
62 static void
object_added(TOPLEVEL * toplevel,PAGE * page,OBJECT * object)63 object_added (TOPLEVEL *toplevel, PAGE *page, OBJECT *object)
64 {
65   /* Set up object parent pointer */
66 #ifndef NDEBUG
67   if (object->page != NULL) {
68     g_critical ("Object %p already has parent page %p!", object, object->page);
69   }
70 #endif
71   object->page = page;
72 
73   /* Add object to tile system. */
74   s_tile_add_object (toplevel, object);
75 
76   /* Update object connection tracking */
77   s_conn_update_object (toplevel, object);
78 
79   o_emit_change_notify (toplevel, object);
80 }
81 
82 /* Called just before removing an OBJECT from a PAGE. */
83 static void
pre_object_removed(TOPLEVEL * toplevel,PAGE * page,OBJECT * object)84 pre_object_removed (TOPLEVEL *toplevel, PAGE *page, OBJECT *object)
85 {
86   o_emit_pre_change_notify (toplevel, object);
87 
88   /* Clear object parent pointer */
89 #ifndef NDEBUG
90   if (object->page == NULL) {
91     g_critical ("Object %p has NULL parent page!", object);
92   }
93 #endif
94   object->page = NULL;
95 
96   /* Clear page's object_lastplace pointer if set */
97   if (page->object_lastplace == object) {
98     page->object_lastplace = NULL;
99   }
100 
101   /* Remove object from connection system */
102   s_conn_remove_object (toplevel, object);
103 
104   /* Remove object from tile system */
105   s_tile_remove_object (object);
106 }
107 
108 /*! \brief create a new page object
109  *  \par Function Description
110  *  Creates a new page and add it to <B>toplevel</B>'s list of pages.
111  *
112  *  It initializes the #PAGE structure and set its <B>page_filename</B>
113  *  to <B>filename</B>. <B>toplevel</B>'s current page is not changed by
114  *  this function.
115  */
s_page_new(TOPLEVEL * toplevel,const gchar * filename)116 PAGE *s_page_new (TOPLEVEL *toplevel, const gchar *filename)
117 {
118   PAGE *page;
119 
120   /* Now create a blank page */
121   page = (PAGE*)g_new0 (PAGE, 1);
122 
123   page->pid = global_pid++;
124 
125   page->CHANGED = 0;
126 
127   /* big assumption here that page_filename isn't null */
128   if (g_path_is_absolute (filename)) {
129     page->page_filename = g_strdup (filename);
130   } else {
131     gchar *pwd = g_get_current_dir ();
132     page->page_filename = g_build_filename (pwd, filename, NULL);
133     g_free (pwd);
134   }
135 
136   g_assert (toplevel->init_bottom != 0);
137   page->coord_aspectratio = (
138     ((float) toplevel->init_right) / ((float) toplevel->init_bottom));
139 
140   page->up = -2;
141   page->page_control = 0;
142 
143   /* Init tile array */
144   s_tile_init (toplevel, page);
145 
146   /* Init the object list */
147   page->_object_list = NULL;
148 
149   /* new selection mechanism */
150   page->selection_list = o_selection_new();
151 
152   page->place_list = NULL;
153 
154   /* init undo struct pointers */
155   s_undo_init(page);
156 
157   page->object_lastplace = NULL;
158 
159   page->weak_refs = NULL;
160 
161   set_window (toplevel, page,
162               toplevel->init_left, toplevel->init_right,
163               toplevel->init_top,  toplevel->init_bottom);
164 
165   /* Backup variables */
166   g_get_current_time (&page->last_load_or_save_time);
167   page->ops_since_last_backup = 0;
168   page->saved_since_first_loaded = 0;
169   page->do_autosave_backup = 0;
170 
171   /* now append page to page list of toplevel */
172   geda_list_add( toplevel->pages, page );
173 
174   return page;
175 }
176 
177 /*! \brief delete a page and it's contents
178  *  \par Function Description
179  *  Deletes a single page <B>page</B> from <B>toplevel</B>'s list of pages.
180  *
181  *  See #s_page_delete_list() to delete all pages of a <B>toplevel</B>
182  *
183  *  If the current page of toplevel is given as parameter <B>page</B>,
184  *  the function sets the field <B>page_current</B> of the TOPLEVEL
185  *  struct to NULL.
186  */
s_page_delete(TOPLEVEL * toplevel,PAGE * page)187 void s_page_delete (TOPLEVEL *toplevel, PAGE *page)
188 {
189   PAGE *tmp;
190   gchar *backup_filename;
191   gchar *real_filename;
192 
193   /* We need to temporarily make the page being deleted current because
194    * various functions called below (some indirectly) assume they are
195    * deleting objects from the current page.
196    *
197    * These functions are known to include:
198    *   s_delete_object ()
199    */
200 
201   /* save page_current and switch to page */
202   if (page == toplevel->page_current) {
203     tmp = NULL;
204   } else {
205     tmp = toplevel->page_current;
206     s_page_goto (toplevel, page);
207   }
208 
209   /* Get the real filename and file permissions */
210   real_filename = follow_symlinks (page->page_filename, NULL);
211 
212   if (real_filename == NULL) {
213     s_log_message (_("s_page_delete: Can't get the real filename of %s."),
214                    page->page_filename);
215   }
216   else {
217     backup_filename = f_get_autosave_filename (real_filename);
218 
219     /* Delete the backup file */
220     if ( (g_file_test (backup_filename, G_FILE_TEST_EXISTS)) &&
221 	 (!g_file_test(backup_filename, G_FILE_TEST_IS_DIR)) )
222     {
223       if (unlink(backup_filename) != 0) {
224 	s_log_message(_("s_page_delete: Unable to delete backup file %s."),
225                       backup_filename);
226       }
227     }
228     g_free (backup_filename);
229   }
230   g_free(real_filename);
231 
232   /* Free the selection object */
233   g_object_unref( page->selection_list );
234 
235   /* then delete objects of page */
236   s_page_delete_objects (toplevel, page);
237 
238   /* Free the objects in the place list. */
239   s_delete_object_glist (toplevel, page->place_list);
240   page->place_list = NULL;
241 
242 #if DEBUG
243   printf("Freeing page: %s\n", page->page_filename);
244   s_tile_print(toplevel, page);
245 #endif
246   s_tile_free_all (page);
247 
248   /* free current page undo structs */
249   s_undo_free_all (toplevel, page);
250 
251   /* ouch, deal with parents going away and the children still around */
252   page->up = -2;
253   g_free (page->page_filename);
254 
255   geda_list_remove( toplevel->pages, page );
256 
257 #if DEBUG
258   s_tile_print (toplevel, page);
259 #endif
260 
261   s_weakref_notify (page, page->weak_refs);
262 
263   g_free (page);
264 
265   /* restore page_current */
266   if (tmp != NULL) {
267     s_page_goto (toplevel, tmp);
268   } else {
269     /* page was page_current */
270     toplevel->page_current = NULL;
271     /* page_current must be updated by calling function */
272   }
273 
274 }
275 
276 
277 /*! \brief Deletes the list of pages of <B>toplevel</B>.
278  *  \par Function Description
279  *  Deletes the list of pages of <B>toplevel</B>.
280  *  This function should only be called when you are finishing up.
281  *
282  *  \param toplevel  The TOPLEVEL object.
283  */
s_page_delete_list(TOPLEVEL * toplevel)284 void s_page_delete_list(TOPLEVEL *toplevel)
285 {
286   GList *list_copy, *iter;
287   PAGE *page;
288 
289   /* s_page_delete removes items from the page list, so make a copy */
290   list_copy = g_list_copy (geda_list_get_glist (toplevel->pages));
291 
292   for (iter = list_copy; iter != NULL; iter = g_list_next (iter)) {
293     page = (PAGE *)iter->data;
294 
295     s_page_delete (toplevel, page);
296   }
297 
298   g_list_free (list_copy);
299 
300   /* reset toplevel fields */
301   toplevel->page_current = NULL;
302 }
303 
304 /*! \brief Add a weak reference watcher to an PAGE.
305  * \par Function Description
306  * Adds the weak reference callback \a notify_func to \a page.  When
307  * \a page is destroyed, \a notify_func will be called with two
308  * arguments: the \a page, and the \a user_data.
309  *
310  * \sa s_page_weak_unref
311  *
312  * \param [in,out] page       Page to weak-reference.
313  * \param [in] notify_func    Weak reference notify function.
314  * \param [in] user_data      Data to be passed to \a notify_func.
315  */
316 void
s_page_weak_ref(PAGE * page,void (* notify_func)(void *,void *),void * user_data)317 s_page_weak_ref (PAGE *page,
318                  void (*notify_func)(void *, void *),
319                  void *user_data)
320 {
321   g_return_if_fail (page != NULL);
322   page->weak_refs = s_weakref_add (page->weak_refs, notify_func, user_data);
323 }
324 
325 /*! \brief Remove a weak reference watcher from an PAGE.
326  * \par Function Description
327  * Removes the weak reference callback \a notify_func from \a page.
328  *
329  * \sa s_page_weak_ref()
330  *
331  * \param [in,out] page       Page to weak-reference.
332  * \param [in] notify_func    Notify function to search for.
333  * \param [in] user_data      Data to to search for.
334  */
335 void
s_page_weak_unref(PAGE * page,void (* notify_func)(void *,void *),void * user_data)336 s_page_weak_unref (PAGE *page,
337                    void (*notify_func)(void *, void *),
338                    void *user_data)
339 {
340   g_return_if_fail (page != NULL);
341   page->weak_refs = s_weakref_remove (page->weak_refs,
342                                       notify_func, user_data);
343 }
344 
345 /*! \brief Add a weak pointer to an PAGE.
346  * \par Function Description
347  * Adds the weak pointer at \a weak_pointer_loc to \a page. The
348  * value of \a weak_pointer_loc will be set to NULL when \a page is
349  * destroyed.
350  *
351  * \sa s_page_remove_weak_ptr
352  *
353  * \param [in,out] page          Page to weak-reference.
354  * \param [in] weak_pointer_loc  Memory address of a pointer.
355  */
356 void
s_page_add_weak_ptr(PAGE * page,void * weak_pointer_loc)357 s_page_add_weak_ptr (PAGE *page,
358                      void *weak_pointer_loc)
359 {
360   g_return_if_fail (page != NULL);
361   page->weak_refs = s_weakref_add_ptr (page->weak_refs, weak_pointer_loc);
362 }
363 
364 /*! \brief Remove a weak pointer from an PAGE.
365  * \par Function Description
366  * Removes the weak pointer at \a weak_pointer_loc from \a page.
367  *
368  * \sa s_page_add_weak_ptr()
369  *
370  * \param [in,out] page          Page to weak-reference.
371  * \param [in] weak_pointer_loc  Memory address of a pointer.
372  */
373 void
s_page_remove_weak_ptr(PAGE * page,void * weak_pointer_loc)374 s_page_remove_weak_ptr (PAGE *page,
375                         void *weak_pointer_loc)
376 {
377   g_return_if_fail (page != NULL);
378   page->weak_refs = s_weakref_remove_ptr (page->weak_refs,
379                                           weak_pointer_loc);
380 }
381 
382 /*! \brief changes the current page in toplevel
383  *  \par Function Description
384  *  Changes the current page in \a toplevel to the page \a p_new.
385  *
386  *  \param toplevel  The TOPLEVEL object
387  *  \param p_new     The PAGE to go to
388  */
s_page_goto(TOPLEVEL * toplevel,PAGE * p_new)389 void s_page_goto (TOPLEVEL *toplevel, PAGE *p_new)
390 {
391   gchar *dirname;
392 
393   toplevel->page_current = p_new;
394 
395   dirname = g_dirname (p_new->page_filename);
396   if (chdir (dirname)) {
397     /* An error occured with chdir */
398 #warning FIXME: What do we do?
399   }
400   g_free (dirname);
401 
402 }
403 
404 /*! \brief Search for pages by filename.
405  *  \par Function Description
406  *  Searches in \a toplevel's list of pages for a page with a filename
407  *  equal to \a filename.
408  *
409  *  \param toplevel  The TOPLEVEL object
410  *  \param filename  The filename string to search for
411  *
412  *  \return PAGE pointer to a matching page, NULL otherwise.
413  */
s_page_search(TOPLEVEL * toplevel,const gchar * filename)414 PAGE *s_page_search (TOPLEVEL *toplevel, const gchar *filename)
415 {
416   const GList *iter;
417   PAGE *page;
418 
419   for ( iter = geda_list_get_glist( toplevel->pages );
420         iter != NULL;
421         iter = g_list_next( iter ) ) {
422 
423     page = (PAGE *)iter->data;
424     if ( g_strcasecmp( page->page_filename, filename ) == 0 )
425       return page;
426   }
427   return NULL;
428 }
429 
430 /*! \brief Search for a page given its page id in a page list.
431  *  \par Function Description
432  *  This functions returns the page that have the page id \a pid in
433  *  the list of pages starting at \a page_list, or NULL if there is no
434  *  such page.
435  *
436  *  \param [in] list      The list of page to search the page in.
437  *  \param [in] pid       The ID of the page to find.
438  *  \returns A pointer on the page found or NULL if not found.
439  */
s_page_search_by_page_id(GedaPageList * list,int pid)440 PAGE *s_page_search_by_page_id (GedaPageList *list, int pid)
441 {
442   const GList *iter;
443 
444   for ( iter = geda_list_get_glist (list);
445         iter != NULL;
446         iter = g_list_next (iter) ) {
447     PAGE *page = (PAGE *)iter->data;
448     if (page->pid == pid) {
449       return page;
450     }
451   }
452 
453   return NULL;
454 }
455 
456 /*! \brief Print full TOPLEVEL structure.
457  *  \par Function Description
458  *  This function prints the internal structure of <B>toplevel</B>'s
459  *  list of pages.
460  *
461  *  \param [in] toplevel  The TOPLEVEL object to print.
462  */
s_page_print_all(TOPLEVEL * toplevel)463 void s_page_print_all (TOPLEVEL *toplevel)
464 {
465   const GList *iter;
466   PAGE *page;
467 
468   for ( iter = geda_list_get_glist( toplevel->pages );
469         iter != NULL;
470         iter = g_list_next( iter ) ) {
471 
472     page = (PAGE *)iter->data;
473     printf ("FILENAME: %s\n", page->page_filename);
474     print_struct_forw (page->_object_list);
475   }
476 }
477 
478 /*! \brief Saves all the pages of a TOPLEVEL object.
479  *  \par Function Description
480  *  Saves all the pages in the <B>toplevel</B> parameter.
481  *
482  *  \param [in] toplevel  The TOPLEVEL to save pages from.
483  *  \return The number of failed tries to save a page.
484  */
s_page_save_all(TOPLEVEL * toplevel)485 gint s_page_save_all (TOPLEVEL *toplevel)
486 {
487   const GList *iter;
488   PAGE *p_current;
489   gint status = 0;
490 
491   for ( iter = geda_list_get_glist( toplevel->pages );
492         iter != NULL;
493         iter = g_list_next( iter ) ) {
494 
495     p_current = (PAGE *)iter->data;
496 
497     if (f_save (toplevel, p_current,
498                 p_current->page_filename, NULL)) {
499       s_log_message (_("Saved [%s]\n"),
500                      p_current->page_filename);
501       /* reset the CHANGED flag of p_current */
502       p_current->CHANGED = 0;
503 
504     } else {
505       s_log_message (_("Could NOT save [%s]\n"),
506                      p_current->page_filename);
507       /* increase the error counter */
508       status++;
509     }
510 
511   }
512 
513   return status;
514 }
515 
516 /*! \brief Check if CHANGED flag is set for any page in list.
517  *  \par Function Description
518  *  This function checks the CHANGED flag for all pages in the <B>list</B>
519  *  object.
520  *
521  *  \param [in] list  GedaPageList to check CHANGED flag in.
522  *  \return 1 if any page has the CHANGED flag set, 0 otherwise.
523  */
s_page_check_changed(GedaPageList * list)524 gboolean s_page_check_changed (GedaPageList *list)
525 {
526   const GList *iter;
527   PAGE *p_current;
528 
529   for ( iter = geda_list_get_glist( list );
530         iter != NULL;
531         iter = g_list_next( iter ) ) {
532 
533     p_current = (PAGE *)iter->data;
534     if (p_current->CHANGED) {
535       return TRUE;
536     }
537   }
538 
539   return FALSE;
540 }
541 
542 /*! \brief Reset the CHANGED flag of all pages.
543  *  \par Function Description
544  *  This function resets the CHANGED flag of each page following \a head.
545  *
546  *  \param [in,out] list  PAGE list to set CHANGED flags in.
547  */
s_page_clear_changed(GedaPageList * list)548 void s_page_clear_changed (GedaPageList *list)
549 {
550   const GList *iter;
551   PAGE *p_current;
552 
553   for ( iter = geda_list_get_glist( list );
554         iter != NULL;
555         iter = g_list_next( iter ) ) {
556 
557     p_current = (PAGE *)iter->data;
558     p_current->CHANGED = 0;
559   }
560 }
561 
562 /*! \brief Autosave initialization function.
563  *  \par Function Description
564  *  This function sets up the autosave callback function.
565  *
566  *  \param [in] toplevel  The TOPLEVEL object.
567  */
s_page_autosave_init(TOPLEVEL * toplevel)568 void s_page_autosave_init(TOPLEVEL *toplevel)
569 {
570   if (toplevel->auto_save_interval != 0) {
571 
572     /* 1000 converts seconds into milliseconds */
573     toplevel->auto_save_timeout =
574       g_timeout_add(toplevel->auto_save_interval*1000,
575                     (GSourceFunc) s_page_autosave,
576                     toplevel);
577   }
578 }
579 
580 /*! \brief Autosave callback function.
581  *  \par Function Description
582  *  This function is a callback of the glib g_timeout functions.
583  *  It is called every "interval" milliseconds and it sets a flag to save
584  *  a backup copy of the opened pages.
585  *
586  *  \param [in] toplevel  The TOPLEVEL object.
587  *  \return The length in milliseconds to set for next interval.
588  */
s_page_autosave(TOPLEVEL * toplevel)589 gint s_page_autosave (TOPLEVEL *toplevel)
590 {
591   const GList *iter;
592   PAGE *p_current;
593 
594   if (toplevel == NULL) {
595     return 0;
596   }
597 
598   /* Do nothing if the interval is 0 */
599   if (toplevel->auto_save_interval == 0) {
600     return toplevel->auto_save_interval;
601   }
602 
603   /* Should we just disable the autosave timeout returning 0 or
604      just wait for more pages to be added? */
605   if ( toplevel->pages == NULL)
606     return toplevel->auto_save_interval;
607 
608   for ( iter = geda_list_get_glist( toplevel->pages );
609         iter != NULL;
610         iter = g_list_next( iter ) ) {
611 
612     p_current = (PAGE *)iter->data;
613 
614     if (p_current->ops_since_last_backup != 0) {
615       /* Real autosave is done in o_undo_savestate */
616       p_current->do_autosave_backup = 1;
617     }
618   }
619 
620   return toplevel->auto_save_interval;
621 }
622 
623 /*! \brief Append an OBJECT to the PAGE
624  *
625  *  \par Function Description
626  *  Links the passed OBJECT to the end of the PAGE's
627  *  linked list of objects.
628  *
629  *  \param [in] toplevel  The TOPLEVEL object.
630  *  \param [in] page      The PAGE the object is being added to.
631  *  \param [in] object    The OBJECT being added to the page.
632  */
s_page_append(TOPLEVEL * toplevel,PAGE * page,OBJECT * object)633 void s_page_append (TOPLEVEL *toplevel, PAGE *page, OBJECT *object)
634 {
635   page->_object_list = g_list_append (page->_object_list, object);
636   object_added (toplevel, page, object);
637 }
638 
639 /*! \brief Append a GList of OBJECTs to the PAGE
640  *
641  *  \par Function Description
642  *  Links the passed OBJECT GList to the end of the PAGE's
643  *  object_list.
644  *
645  *  \param [in] toplevel  The TOPLEVEL object.
646  *  \param [in] page      The PAGE the objects are being added to.
647  *  \param [in] obj_list  The OBJECT list being added to the page.
648  */
s_page_append_list(TOPLEVEL * toplevel,PAGE * page,GList * obj_list)649 void s_page_append_list (TOPLEVEL *toplevel, PAGE *page, GList *obj_list)
650 {
651   GList *iter;
652   page->_object_list = g_list_concat (page->_object_list, obj_list);
653   for (iter = obj_list; iter != NULL; iter = g_list_next (iter)) {
654     object_added (toplevel, page, iter->data);
655   }
656 }
657 
658 /*! \brief Remove an OBJECT from the PAGE
659  *
660  *  \par Function Description
661  *  Removes the passed OBJECT from the PAGE's
662  *  linked list of objects.
663  *
664  *  \param [in] toplevel  The TOPLEVEL object.
665  *  \param [in] page      The PAGE the object is being removed from.
666  *  \param [in] object    The OBJECT being removed from the page.
667  */
s_page_remove(TOPLEVEL * toplevel,PAGE * page,OBJECT * object)668 void s_page_remove (TOPLEVEL *toplevel, PAGE *page, OBJECT *object)
669 {
670   pre_object_removed (toplevel, page, object);
671   page->_object_list = g_list_remove (page->_object_list, object);
672 }
673 
674 /*! \brief Replace an OBJECT in a PAGE, in the same list position.
675  *
676  * \par Function Description
677  * Removes \a object1 from \a page's linked list of objects, and puts
678  * \a object2 in the position thus vacated. If \a object1 is not in \a
679  * page, object2 is appended to \a page.
680  *
681  * \param [in] toplevel  The TOPLEVEL object.
682  * \param [in] page      The PAGE to be modified.
683  * \param [in] object1   The OBJECT being removed from the page.
684  * \param [in] object2   The OBJECT being added to the page.
685  */
686 void
s_page_replace(TOPLEVEL * toplevel,PAGE * page,OBJECT * object1,OBJECT * object2)687 s_page_replace (TOPLEVEL *toplevel, PAGE *page,
688                 OBJECT *object1, OBJECT *object2)
689 {
690   GList *iter = g_list_find (page->_object_list, object1);
691 
692   /* If object1 not found, append object2 */
693   if (iter == NULL) {
694     s_page_append (toplevel, page, object2);
695     return;
696   }
697 
698   pre_object_removed (toplevel, page, object1);
699   iter->data = object2;
700   object_added (toplevel, page, object2);
701 }
702 
703 /*! \brief Remove and free all OBJECTs from the PAGE
704  *
705  *  \par Function Description
706  *  Removes and frees all OBJECTs from the PAGE.
707  *
708  *  \param [in] toplevel  The TOPLEVEL object.
709  *  \param [in] page      The PAGE being cleared.
710  */
s_page_delete_objects(TOPLEVEL * toplevel,PAGE * page)711 void s_page_delete_objects (TOPLEVEL *toplevel, PAGE *page)
712 {
713   GList *objects = page->_object_list;
714   GList *iter;
715   for (iter = objects; iter != NULL; iter = g_list_next (iter)) {
716     pre_object_removed (toplevel, page, iter->data);
717   }
718   page->_object_list = NULL;
719   s_delete_object_glist (toplevel, objects);
720 }
721 
722 
723 /*! \brief Return a GList of OBJECTs on the PAGE
724  *
725  *  \par Function Description
726  *  An accessor for the PAGE's GList of objects.
727  *
728  *  NB: This GList is owned by the PAGE, and must not be
729  *      free'd or modified by the caller.
730  *
731  *  \param [in] page      The PAGE to get objects on.
732  *  \returns a const pointer to the PAGE's GList of objects
733  */
s_page_objects(PAGE * page)734 const GList *s_page_objects (PAGE *page)
735 {
736   return page->_object_list;
737 }
738 
739 
740 /*! \brief Find the objects in a given region
741  *
742  *  \par Function Description
743  *  Finds the objects which are inside, or intersect
744  *  the passed box shaped region.
745  *
746  *  \param [in] toplevel  The TOPLEVEL object.
747  *  \param [in] page      The PAGE to find objects on.
748  *  \param [in] min_x     The smaller X coordinate of the region.
749  *  \param [in] min_y     The smaller Y coordinate of the region.
750  *  \param [in] max_x     The larger  X coordinate of the region.
751  *  \param [in] max_y     The larger  Y coordinate of the region.
752  *  \return The GList of OBJECTs in the region.
753  */
s_page_objects_in_region(TOPLEVEL * toplevel,PAGE * page,int min_x,int min_y,int max_x,int max_y)754 GList *s_page_objects_in_region (TOPLEVEL *toplevel, PAGE *page,
755                                  int min_x, int min_y, int max_x, int max_y)
756 {
757   BOX rect;
758 
759   rect.lower_x = min_x;
760   rect.lower_y = min_y;
761   rect.upper_x = max_x;
762   rect.upper_y = max_y;
763 
764   return s_page_objects_in_regions (toplevel, page, &rect, 1);
765 }
766 
767 /*! \brief Find the objects in a given region
768  *
769  *  \par Function Description
770  *  Finds the objects which are inside, or intersect
771  *  the passed box shaped region.
772  *
773  *  \param [in] toplevel  The TOPLEVEL object.
774  *  \param [in] page      The PAGE to find objects on.
775  *  \param [in] rects     The BOX regions to check.
776  *  \param [in] n_rects   The number of regions.
777  *  \return The GList of OBJECTs in the region.
778  */
s_page_objects_in_regions(TOPLEVEL * toplevel,PAGE * page,BOX * rects,int n_rects)779 GList *s_page_objects_in_regions (TOPLEVEL *toplevel, PAGE *page,
780                                   BOX *rects, int n_rects)
781 {
782   GList *iter;
783   GList *list = NULL;
784   int i;
785 
786   for (iter = page->_object_list; iter != NULL; iter = g_list_next (iter)) {
787     OBJECT *object = iter->data;
788     int left, top, right, bottom;
789     int visible;
790 
791     visible = world_get_single_object_bounds (toplevel, object,
792                                               &left, &top, &right, &bottom);
793     if (visible) {
794       for (i = 0; i < n_rects; i++) {
795         if (right  >= rects[i].lower_x &&
796             left   <= rects[i].upper_x &&
797             top    <= rects[i].upper_y &&
798             bottom >= rects[i].lower_y) {
799           list = g_list_prepend (list, object);
800           break;
801         }
802       }
803     }
804   }
805 
806   list = g_list_reverse (list);
807   return list;
808 }
809