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