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
28 #include "libgeda_priv.h"
30 #ifdef HAVE_LIBDMALLOC
35 * \brief The connection system
37 * The connection system stores and tracks the connections between
38 * connected <b>OBJECTS</b>. The connected OBJECTS are either
39 * <b>pins</b>, <b>nets</b> and <b>busses</b>.
41 * Each connection object with the type <b>st_conn</b> represents a
42 * single unidirectional relation to another object.
44 * The following figure with two nets and a pin shows the relations
45 * between connections and OBJECTS:
47 * \image html s_conn_overview.png
48 * \image latex s_conn_overview.pdf "Connection overview" width=14cm
52 /*! \brief create a new connection object
53 * \par Function Description
54 * create a single st_conn object and initialize it with the
57 * \return The new connection object
59 CONN
*s_conn_return_new(OBJECT
* other_object
, int type
, int x
, int y
,
60 int whichone
, int other_whichone
)
64 new_conn
= (CONN
*) g_malloc(sizeof(CONN
));
67 printf("** creating: %s %d %d\n", other_object
->name
, x
, y
);
70 new_conn
->other_object
= other_object
;
71 new_conn
->type
= type
;
74 new_conn
->whichone
= whichone
;
75 new_conn
->other_whichone
= other_whichone
;
80 /*! \brief check if a connection is uniq in a list
81 * \par Function Description
82 * This function checks if there's no identical connection
83 * in the list of connections.
84 * \param conn_list list of connection objects
85 * \param input_conn single connection object.
86 * \return TRUE if the CONN structure is unique, FALSE otherwise.
88 int s_conn_uniq(GList
* conn_list
, CONN
* input_conn
)
93 c_current
= conn_list
;
94 while (c_current
!= NULL
) {
95 conn
= (CONN
*) c_current
->data
;
97 if (conn
->other_object
== input_conn
->other_object
&&
98 conn
->x
== input_conn
->x
&& conn
->y
== input_conn
->y
&&
99 conn
->type
== input_conn
->type
) {
103 c_current
= g_list_next(c_current
);
109 /*! \brief remove a object from the connection list of another object
110 * \par Function Description
111 * This function removes the OBJECT <b>to_remove</b> from the connection
112 * list of the OBJECT <b>other_object</b>.
113 * \param toplevel (currently not used)
114 * \param other_object OBJECT from that the to_remove OBJECT needs to be removed
115 * \param to_remove OBJECT to remove
116 * \return TRUE if a connection has been deleted, FALSE otherwise
118 int s_conn_remove_other (TOPLEVEL
*toplevel
, OBJECT
*other_object
,
121 GList
*c_current
= NULL
;
124 o_emit_pre_change_notify (toplevel
, other_object
);
126 c_current
= other_object
->conn_list
;
127 while (c_current
!= NULL
) {
128 conn
= (CONN
*) c_current
->data
;
130 if (conn
->other_object
== to_remove
) {
131 other_object
->conn_list
=
132 g_list_remove(other_object
->conn_list
, conn
);
135 printf("Found other_object in remove_other\n");
136 printf("Freeing other: %s %d %d\n", conn
->other_object
->name
,
140 /* Do not write modify c_current like this, since this will cause */
141 /* very nasty data corruption and upset glib's memory slice */
143 /* c_current->data = NULL; Do not comment in */
147 #if 0 /* this does not work right */
148 if (other_object
->type
== OBJ_BUS
&&
149 other_object
->conn_list
== NULL
) {
150 other_object
->bus_ripper_direction
= 0;
153 s_conn_emit_conns_changed (toplevel
, other_object
);
158 c_current
= g_list_next(c_current
);
161 o_emit_change_notify (toplevel
, other_object
);
166 /*! \brief removes a GList of OBJECTs from the connection system
168 * \par Function Description
169 * This function removes all connections from and to the OBJECTS
170 * of the given GList.
172 * \param toplevel (currently not used)
173 * \param obj_list GList of OBJECTs to unconnected from all other objects
175 static void s_conn_remove_glist (TOPLEVEL
*toplevel
, GList
*obj_list
)
180 for (iter
= obj_list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
181 o_current
= iter
->data
;
182 s_conn_remove_object (toplevel
, o_current
);
186 /*! \brief remove an OBJECT from the connection system
187 * \par Function Description
188 * This function removes all connections from and to the OBJECT
190 * \param toplevel (currently not used)
191 * \param to_remove OBJECT to unconnected from all other objects
193 void s_conn_remove_object (TOPLEVEL
*toplevel
, OBJECT
*to_remove
)
198 switch (to_remove
->type
) {
202 for (c_iter
= to_remove
->conn_list
;
204 c_iter
= g_list_next (c_iter
)) {
208 s_conn_freeze_hooks (toplevel
, conn
->other_object
);
209 /* keep calling this till it returns false (all refs removed) */
210 /* there is NO body to this while loop */
211 while (s_conn_remove_other (toplevel
, conn
->other_object
, to_remove
));
214 s_conn_thaw_hooks (toplevel
, conn
->other_object
);
218 g_list_free (to_remove
->conn_list
);
219 to_remove
->conn_list
= NULL
;
223 case OBJ_PLACEHOLDER
:
224 s_conn_remove_glist (toplevel
, to_remove
->complex->prim_objs
);
228 s_conn_emit_conns_changed (toplevel
, to_remove
);
231 /*! \brief Checks if a point is a midpoint of an OBJECT
232 * \par Function Description
233 * Checks if the point (<b>x</b>,<b>y</b>) is on the OBJECT
234 * and between it's endpoints.
235 * \return TRUE if the point is a midpoint of the OBJECT. FALSE
236 * if the point is not a midpoinit or if the OBJECT is not a
237 * NET a PIN or a BUS or if the OBJECT
238 * has neither horizontal nor vertical orientation.
240 OBJECT
*s_conn_check_midpoint(OBJECT
*o_current
, int x
, int y
)
242 int min_x
, min_y
, max_x
, max_y
;
244 switch(o_current
->type
) {
248 min_y
= min(o_current
->line
->y
[0],
249 o_current
->line
->y
[1]);
250 max_y
= max(o_current
->line
->y
[0],
251 o_current
->line
->y
[1]);
254 if ( (o_current
->line
->x
[0] == x
) &&
255 (y
> min_y
) && (y
< max_y
) &&
256 (o_current
->line
->x
[0] ==
257 o_current
->line
->x
[1]) ) {
259 printf("Found vertical point\n");
264 min_x
= min(o_current
->line
->x
[0],
265 o_current
->line
->x
[1]);
266 max_x
= max(o_current
->line
->x
[0],
267 o_current
->line
->x
[1]);
270 if ( (o_current
->line
->y
[0] == y
) &&
271 (x
> min_x
) && (x
< max_x
) &&
272 (o_current
->line
->y
[0] ==
273 o_current
->line
->y
[1]) ) {
275 printf("Found horizontal point\n");
285 /*! \brief adds a GList of OBJECTs to the connection system
287 * \par Function Description
288 * This function adds all connections from and to the OBJECTS
289 * of the given GList.
291 * \param toplevel (currently not used)
292 * \param obj_list GList of OBJECTs to add into the connection system
294 void s_conn_update_glist (TOPLEVEL
*toplevel
, GList
*obj_list
)
299 for (iter
= obj_list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
300 o_current
= iter
->data
;
301 s_conn_update_object (toplevel
, o_current
);
306 /*! \brief Checks if an object is bus, or a bus pin
308 * \par Function Description
309 * Checks if an object is a bus or a bus pin
311 * \param object The OBJECT to test
312 * \return TRUE if the objects is a bis, or bus pin
314 static int is_bus_related (OBJECT
*object
)
316 return (object
->type
== OBJ_BUS
||
317 (object
->type
== OBJ_PIN
&& object
->pin_type
== PIN_TYPE_BUS
));
321 /*! \brief Checks if two objects are of compatible types to be connected
323 * \par Function Description
324 * Checks if two objects are legal to be connected together
326 * \param object1 First OBJECT
327 * \param object2 Second OBJECT
328 * \return TRUE if the objects are compatible, FALSE if not
330 static int check_direct_compat (OBJECT
*object1
, OBJECT
*object2
)
332 return (is_bus_related (object1
) == is_bus_related (object2
));
336 static void add_connection (TOPLEVEL
*toplevel
,
337 OBJECT
*object
, OBJECT
*other_object
,
338 int type
, int x
, int y
,
339 int whichone
, int other_whichone
)
341 /* Describe the connection */
342 CONN
*new_conn
= s_conn_return_new (other_object
, type
, x
, y
,
343 whichone
, other_whichone
);
344 /* Do uniqness check */
345 if (s_conn_uniq (object
->conn_list
, new_conn
)) {
346 object
->conn_list
= g_list_append (object
->conn_list
, new_conn
);
347 s_conn_emit_conns_changed (toplevel
, object
);
348 s_conn_emit_conns_changed (toplevel
, other_object
);
354 /*! \brief add a line OBJECT to the connection system
355 * \par Function Description
356 * This function searches for all geometrical conections of the OBJECT
357 * <b>object</b> to all other connectable objects. It adds connections
358 * to the object and from all other
359 * objects to this one.
360 * \param toplevel (currently not used)
361 * \param object OBJECT to add into the connection system
363 static void s_conn_update_line_object (TOPLEVEL
*toplevel
, OBJECT
*object
)
368 OBJECT
*other_object
;
372 s_conn_freeze_hooks (toplevel
, object
);
374 /* loop over all tiles which object appears in */
375 for (tl_current
= object
->tiles
;
377 tl_current
= g_list_next (tl_current
)) {
378 t_current
= tl_current
->data
;
380 for (object_list
= t_current
->objects
;
382 object_list
= g_list_next (object_list
)) {
383 other_object
= object_list
->data
;
385 if (object
== other_object
)
388 s_conn_freeze_hooks (toplevel
, other_object
);
390 /* Here is where you check the end points */
391 /* Check both end points of the other object */
392 for (k
= 0; k
< 2; k
++) {
394 /* If the other object is a pin, only check the correct end */
395 if (other_object
->type
== OBJ_PIN
&& other_object
->whichend
!= k
)
398 /* Check both end points of the object */
399 for (j
= 0; j
< 2; j
++) {
401 /* If the object is a pin, only check the correct end */
402 if (object
->type
== OBJ_PIN
&& object
->whichend
!= j
)
405 /* Check for coincidence and compatability between
406 the objects being tested. */
407 if (object
->line
->x
[j
] == other_object
->line
->x
[k
] &&
408 object
->line
->y
[j
] == other_object
->line
->y
[k
] &&
409 check_direct_compat (object
, other_object
)) {
411 o_emit_pre_change_notify (toplevel
, other_object
);
413 add_connection (toplevel
, object
, other_object
, CONN_ENDPOINT
,
414 other_object
->line
->x
[k
],
415 other_object
->line
->y
[k
], j
, k
);
417 add_connection (toplevel
, other_object
, object
, CONN_ENDPOINT
,
419 object
->line
->y
[j
], k
, j
);
421 o_emit_change_notify (toplevel
, other_object
);
426 /* Check both end points of the object against midpoints of the other */
427 for (k
= 0; k
< 2; k
++) {
429 /* If the object is a pin, only check the correct end */
430 if (object
->type
== OBJ_PIN
&& object
->whichend
!= k
)
433 /* check for midpoint of other object, k endpoint of current obj*/
434 found
= s_conn_check_midpoint (other_object
, object
->line
->x
[k
],
437 /* Pins are not allowed midpoint connections onto them. */
438 /* Allow nets to connect to the middle of buses. */
439 /* Allow compatible objects to connect. */
440 if (found
&& other_object
->type
!= OBJ_PIN
&&
441 ((object
->type
== OBJ_NET
&& other_object
->type
== OBJ_BUS
) ||
442 check_direct_compat (object
, other_object
))) {
444 add_connection (toplevel
, object
, other_object
, CONN_MIDPOINT
,
446 object
->line
->y
[k
], k
, -1);
448 add_connection (toplevel
, other_object
, object
, CONN_MIDPOINT
,
450 object
->line
->y
[k
], -1, k
);
455 /* Check both end points of the other object against midpoints of the first */
456 for (k
= 0; k
< 2; k
++) {
458 /* If the other object is a pin, only check the correct end */
459 if (other_object
->type
== OBJ_PIN
&& other_object
->whichend
!= k
)
462 /* do object's endpoints cross the middle of other_object? */
463 /* check for midpoint of other object, k endpoint of current obj*/
464 found
= s_conn_check_midpoint (object
, other_object
->line
->x
[k
],
465 other_object
->line
->y
[k
]);
467 /* Pins are not allowed midpoint connections onto them. */
468 /* Allow nets to connect to the middle of buses. */
469 /* Allow compatible objects to connect. */
470 if (found
&& object
->type
!= OBJ_PIN
&&
471 ((object
->type
== OBJ_BUS
&& other_object
->type
== OBJ_NET
) ||
472 check_direct_compat (object
, other_object
))) {
474 add_connection (toplevel
, object
, other_object
, CONN_MIDPOINT
,
475 other_object
->line
->x
[k
],
476 other_object
->line
->y
[k
], -1, k
);
478 add_connection (toplevel
, other_object
, object
, CONN_MIDPOINT
,
479 other_object
->line
->x
[k
],
480 other_object
->line
->y
[k
], k
, -1);
484 s_conn_thaw_hooks (toplevel
, other_object
);
489 s_conn_print(object
->conn_list
);
492 s_conn_thaw_hooks (toplevel
, object
);
495 /*! \brief add an OBJECT to the connection system
497 * \par Function Description
498 * This function searches for all geometrical conections of the OBJECT
499 * <b>object</b> to all other connectable objects. It adds connections
500 * to the object and from all other objects to this one.
502 * \param toplevel (currently not used)
503 * \param object OBJECT to add into the connection system
505 void s_conn_update_object (TOPLEVEL
*toplevel
, OBJECT
*object
)
507 switch (object
->type
) {
511 s_conn_update_line_object (toplevel
, object
);
515 case OBJ_PLACEHOLDER
:
516 s_conn_update_glist (toplevel
, object
->complex->prim_objs
);
521 /*! \brief print all connections of a connection list
522 * \par Function Description
523 * This is a debugging function to print a List of connections.
524 * \param conn_list GList of connection objects
526 void s_conn_print(GList
* conn_list
)
531 printf("\nStarting s_conn_print\n");
532 cl_current
= conn_list
;
533 while (cl_current
!= NULL
) {
535 conn
= (CONN
*) cl_current
->data
;
536 printf("-----------------------------------\n");
537 printf("other object: %s\n", conn
->other_object
->name
);
538 printf("type: %d\n", conn
->type
);
539 printf("x: %d y: %d\n", conn
->x
, conn
->y
);
540 printf("whichone: %d\n", conn
->whichone
);
541 printf("other_whichone: %d\n", conn
->other_whichone
);
542 printf("-----------------------------------\n");
544 cl_current
= g_list_next(cl_current
);
549 /*! \brief Search for net in existing connections.
550 * \par Function Description
551 * This method searches the connection list for the first matching
552 * connection with the given x, y, and whichone endpoint.
554 * \param [in] new_net Net OBJECT to compare to.
555 * \param [in] whichone The connection number to check.
556 * \param [in] conn_list List of existing connections to compare
558 * \return TRUE if a matching connection is found, FALSE otherwise.
560 int s_conn_net_search(OBJECT
* new_net
, int whichone
, GList
* conn_list
)
565 cl_current
= conn_list
;
566 while (cl_current
!= NULL
) {
568 conn
= (CONN
*) cl_current
->data
;
569 if (conn
!= NULL
&& conn
->whichone
== whichone
&&
570 conn
->x
== new_net
->line
->x
[whichone
] &&
571 conn
->y
== new_net
->line
->y
[whichone
])
576 cl_current
= g_list_next(cl_current
);
582 /*! \brief get a list of all objects connected to a list of OBJECTs.
584 * \par Function Description
585 * This function gets all other_object from the connection
586 * list of the OBJECTs in the pased list.
588 * \param [in] input_list GList of OBJECT's or NULL
589 * \param [in] obj_list The GList of OBJECT to get connections from
590 * \return A GList of objects
593 * Caller must g_list_free returned GList pointer.
594 * Do not free individual data items in list.
596 static GList
*s_conn_return_glist_others (GList
*input_list
, GList
*obj_list
)
602 return_list
= input_list
;
604 for (iter
= obj_list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
605 o_current
= iter
->data
;
606 return_list
= s_conn_return_others (return_list
, o_current
);
612 /*! \brief get a list of all objects connected to this one
614 * \par Function Description
615 * This function gets all other_object from the connection list of the current object.
616 * COMPLEX objects are entered, and their prim_objs processed. If an <b>input_list</b>
617 * is given, the other objects are appended to that list.
619 * \param [in] input_list GList of OBJECT's
620 * \param [in] object OBJECT to get other OBJECTs from
621 * \return A GList of OBJECTs
624 * Caller must g_list_free returned GList pointer.
625 * Do not free individual data items in list.
627 GList
*s_conn_return_others(GList
*input_list
, OBJECT
*object
)
632 return_list
= input_list
;
634 switch (object
->type
) {
638 for (c_iter
= object
->conn_list
;
639 c_iter
!= NULL
; c_iter
= g_list_next (c_iter
)) {
640 CONN
*conn
= c_iter
->data
;
642 if (conn
->other_object
&& conn
->other_object
!= object
) {
643 return_list
= g_list_append(return_list
, conn
->other_object
);
649 case OBJ_PLACEHOLDER
:
650 return_list
= s_conn_return_glist_others (return_list
,
651 object
->complex->prim_objs
);
660 ConnsChangedFunc func
;
665 void s_conn_append_conns_changed_hook (TOPLEVEL
*toplevel
,
666 ConnsChangedFunc func
,
669 ConnsChangedHook
*new_hook
;
671 new_hook
= g_new0 (ConnsChangedHook
, 1);
672 new_hook
->func
= func
;
673 new_hook
->data
= data
;
675 toplevel
->conns_changed_hooks
=
676 g_list_append (toplevel
->conns_changed_hooks
, new_hook
);
680 static void call_conns_changed_hook (gpointer data
, gpointer user_data
)
682 ConnsChangedHook
*hook
= data
;
683 OBJECT
*object
= user_data
;
685 hook
->func (hook
->data
, object
);
689 void s_conn_emit_conns_changed (TOPLEVEL
*toplevel
, OBJECT
*object
)
691 if (object
->conn_notify_freeze_count
> 0) {
692 object
->conn_notify_pending
= 1;
696 object
->conn_notify_pending
= 0;
698 g_list_foreach (toplevel
->conns_changed_hooks
,
699 call_conns_changed_hook
, object
);
702 void s_conn_freeze_hooks (TOPLEVEL
*toplevel
, OBJECT
*object
)
704 object
->conn_notify_freeze_count
+= 1;
707 void s_conn_thaw_hooks (TOPLEVEL
*toplevel
, OBJECT
*object
)
709 g_return_if_fail (object
->conn_notify_freeze_count
> 0);
711 object
->conn_notify_freeze_count
-= 1;
713 if (object
->conn_notify_freeze_count
== 0 &&
714 object
->conn_notify_pending
)
715 s_conn_emit_conns_changed (toplevel
, object
);
718 static void refresh_connectivity_cache (TOPLEVEL
*toplevel
, OBJECT
*object
)
720 if (object
->type
== OBJ_NET
) {
721 /* FIXME: suboptimal to refresh cache every time */
722 /* better approach would invalidate cache without refresh */
723 /* refresh would be done on redraw of pin cues */
724 o_net_refresh_conn_cache (toplevel
, object
);
728 static void s_conn_init_toplevel (TOPLEVEL
*toplevel
)
730 /* Connect the hooks for tracking net connectivity here */
731 s_conn_append_conns_changed_hook (toplevel
,
733 refresh_connectivity_cache
,
736 o_attrib_append_attribs_changed_hook (toplevel
,
738 refresh_connectivity_cache
,
742 void s_conn_init (void)
744 s_toplevel_append_new_hook ((NewToplevelFunc
) s_conn_init_toplevel
,