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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "libgeda_priv.h"
29 #ifdef HAVE_LIBDMALLOC
34 static int page_control_counter
=0;
36 /*! \todo Finish function documentation!!!
37 * \brief Search for schematic associated source files and load them.
38 * \par Function Description
39 * This function searches the associated source file refered by the
40 * <B>filename</B> and loads it. If the <B>flag</B> is set to
41 * <B>HIERARCHY_NORMAL_LOAD</B> and the page is allready in the list of
42 * pages it will return the <B>pid</B> of that page.
43 * If the <B>flag</B> is set to <B>HIERARCHY_FORCE_LOAD</B> then this
44 * function will load the page again with a new page id. The second case
45 * is mainly used by gnetlist where pushed down schematics MUST be unique.
47 * \param [in] toplevel The TOPLEVEL object.
48 * \param [in] filename Schematic file name.
49 * \param [in] parent The parent page of the schematic.
50 * \param [in] page_control
52 * \return The page loaded, or NULL if failed.
55 * This function goes and finds the associated source files and
57 * It only works for schematic files though
58 * this is basically push
59 * flag can either be HIERARCHY_NORMAL_LOAD or HIERARCHY_FORCE_LOAD
60 * flag is mainly used by gnetlist where pushed down schematics MUST be unique
63 s_hierarchy_down_schematic_single(TOPLEVEL
*toplevel
, const gchar
*filename
,
64 PAGE
*parent
, int page_control
, int flag
)
70 g_return_val_if_fail ((toplevel
!= NULL
), NULL
);
71 g_return_val_if_fail ((filename
!= NULL
), NULL
);
72 g_return_val_if_fail ((parent
!= NULL
), NULL
);
74 string
= s_slib_search_single(filename
);
80 case HIERARCHY_NORMAL_LOAD
:
82 gchar
*filename
= f_normalize_filename (string
, NULL
);
83 found
= s_page_search (toplevel
, filename
);
87 /* check whether this page is in the parents list */
88 for (forbear
= parent
;
89 forbear
!= NULL
&& found
->pid
!= forbear
->pid
&& forbear
->up
>= 0;
90 forbear
= s_page_search_by_page_id (toplevel
->pages
, forbear
->up
))
93 if (found
->pid
== forbear
->pid
) {
94 s_log_message(_("hierarchy loop detected while visiting page:\n"
95 " \"%s\"\n"), found
->page_filename
);
96 return NULL
; /* error signal */
98 s_page_goto (toplevel
, found
);
99 if (page_control
!= 0) {
100 found
->page_control
= page_control
;
102 found
->up
= parent
->pid
;
107 found
= s_page_new (toplevel
, string
);
109 f_open (toplevel
, found
, found
->page_filename
, NULL
);
113 case HIERARCHY_FORCE_LOAD
:
115 found
= s_page_new (toplevel
, string
);
116 f_open (toplevel
, found
, found
->page_filename
, NULL
);
121 if (page_control
== 0) {
122 page_control_counter
++;
123 found
->page_control
= page_control_counter
;
125 found
->page_control
= page_control
;
128 found
->up
= parent
->pid
;
135 /*! \todo Finish function documentation!!!
137 * \par Function Description
141 s_hierarchy_down_symbol (TOPLEVEL
*toplevel
, const CLibSymbol
*symbol
,
147 filename
= s_clib_symbol_get_filename (symbol
);
149 page
= s_page_search (toplevel
, filename
);
151 s_page_goto (toplevel
, page
);
156 page
= s_page_new (toplevel
, filename
);
159 s_page_goto (toplevel
, page
);
161 f_open(toplevel
, page
, page
->page_filename
, NULL
);
163 page
->up
= parent
->pid
;
164 page_control_counter
++;
165 page
->page_control
= page_control_counter
;
169 /*! \brief Search for the parent page of a page in hierarchy.
170 * \par Function Description
171 * This function searches the parent page of page \a page in the
172 * hierarchy. It checks all the pages in the list \a page_list.
174 * It returns a pointer on the page if found, NULL otherwise.
177 * The page \a current_page must be in the list \a page_list.
179 * \param [in] page_list The list of pages in which to search.
180 * \param [in] current_page The reference page for the search.
181 * \returns A pointer on the page found or NULL if not found.
184 s_hierarchy_find_up_page (GedaPageList
*page_list
, PAGE
*current_page
)
186 if (current_page
->up
< 0) {
187 s_log_message(_("There are no schematics above the current one!\n"));
191 return s_page_search_by_page_id (page_list
, current_page
->up
);
194 /*! \brief Find page hierarchy below a page.
195 * \par Function Description
196 * This function traverses the hierarchy tree of pages and returns a
197 * flat list of pages that are below \a p_current. There are two \a
198 * flags that can be used to control the way that the return value is
199 * constructed: <B>HIERARCHY_NODUPS</B> returns a list without
200 * duplicate pages, and <B>HIERARCHY_POSTORDER</B> traverses the
201 * hierarchy tree and returns a postorder list instead of preorder.
203 * \param toplevel The TOPLEVEL structure.
204 * \param p_current The PAGE to traverse hierarchy for.
205 * \param flags Flags controlling form of return value.
206 * \return A GList of PAGE pointers.
209 * Caller must destroy returned GList with g_list_free().
212 s_hierarchy_traversepages (TOPLEVEL
*toplevel
, PAGE
*p_current
, gint flags
)
216 char *filename
= NULL
;
217 static GList
*pages
= NULL
;
220 g_return_val_if_fail ((toplevel
!= NULL
), NULL
);
221 g_return_val_if_fail ((p_current
!= NULL
), NULL
);
223 /* init static variables the first time*/
224 if (!(flags
& HIERARCHY_INNERLOOP
)) {
228 /* preorder traversing */
229 if (!(flags
& HIERARCHY_POSTORDER
)) {
230 /* check whether we already visited this page */
231 if ((flags
& HIERARCHY_NODUPS
)
232 && (g_list_find (pages
, p_current
) != NULL
)) {
233 return pages
; /* drop the page subtree */
235 pages
= g_list_append (pages
, p_current
);
238 /* walk throught the page objects and search for underlaying schematics */
239 for (iter
= s_page_objects (p_current
);
241 iter
= g_list_next (iter
)) {
242 o_current
= (OBJECT
*)iter
->data
;
244 /* only complex things like symbols can contain attributes */
245 if (o_current
->type
!= OBJ_COMPLEX
) continue;
248 o_attrib_search_attached_attribs_by_name (o_current
, "source", 0);
250 /* if above is NULL, then look inside symbol */
251 if (filename
== NULL
) {
253 o_attrib_search_inherited_attribs_by_name (o_current
, "source", 0);
256 if (filename
== NULL
) continue;
258 /* we got a schematic source attribute
259 lets load the page and dive into it */
261 s_hierarchy_down_schematic_single (toplevel
, filename
, p_current
, 0,
262 HIERARCHY_NORMAL_LOAD
);
263 if (child_page
!= NULL
) {
264 /* call the recursive function */
265 s_hierarchy_traversepages (toplevel
, child_page
, flags
| HIERARCHY_INNERLOOP
);
267 s_log_message (_("ERROR in s_hierarchy_traverse: "
268 "schematic not found: %s\n"),
276 /* postorder traversing */
277 if (flags
& HIERARCHY_POSTORDER
) {
278 /* check whether we already visited this page */
279 if ((flags
& HIERARCHY_NODUPS
)
280 && (g_list_find (pages
, p_current
) != NULL
)) {
281 return pages
; /* don't append it */
283 pages
= g_list_append (pages
, p_current
);
289 /*! \todo Finish function documentation!!!
291 * \par Function Description
294 * Test function which only prints the name of a page and it's number.
297 s_hierarchy_print_page (PAGE
*p_current
, void * data
)
299 printf("pagefilename: %s pageid: %d\n",
300 p_current
->page_filename
, p_current
->pid
);
304 /*! \brief Search for a page preceding a given page in hierarchy.
305 * \par Function Description
306 * This function searches the previous sibling of page \a page in the
307 * hierarchy. It checks all the pages preceding \a page in the list
310 * It returns a pointer on the page if found, NULL otherwise.
313 * The page \a current_page must be in the list \a page_list.
315 * \param [in] page_list The list of pages in which to search.
316 * \param [in] current_page The reference page for the search.
317 * \returns A pointer on the page found or NULL if not found.
320 s_hierarchy_find_prev_page (GedaPageList
*page_list
, PAGE
*current_page
)
324 iter
= g_list_find (geda_list_get_glist (page_list
), current_page
);
325 for (iter
= g_list_previous (iter
);
327 iter
= g_list_previous (iter
)) {
329 PAGE
*page
= (PAGE
*)iter
->data
;
330 if (page
->page_control
== current_page
->page_control
) {
338 /*! \brief Search for a page following a given page in hierarchy.
339 * \par Function Description
340 * This function searches the next sibling of page \a page in the
341 * hierarchy. It checks all the pages following \a page in the list
344 * It returns a pointer on the page if found, NULL otherwise.
347 * The page \a current_page must be in the list \a page_list.
349 * \param [in] page_list The list of pages in which to search.
350 * \param [in] current_page The reference page for the search.
351 * \returns A pointer on the page found or NULL if not found.
354 s_hierarchy_find_next_page (GedaPageList
*page_list
, PAGE
*current_page
)
358 iter
= g_list_find (geda_list_get_glist (page_list
), current_page
);
359 for (iter
= g_list_next (iter
);
361 iter
= g_list_next (iter
)) {
363 PAGE
*page
= (PAGE
*)iter
->data
;
364 if (page
->page_control
== current_page
->page_control
) {