refdes_renum: warn of possible number clash with non-conforming values
[geda-gaf/whiteaudio.git] / libgeda / src / s_conn.c
blob518bc8eede2e32e82bef57f1e0268dc132819865
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
20 #include <config.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
28 #include "libgeda_priv.h"
30 #ifdef HAVE_LIBDMALLOC
31 #include <dmalloc.h>
32 #endif
34 /*! \file s_conn.c
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
55 * given parameters.
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)
62 CONN *new_conn;
64 new_conn = (CONN *) g_malloc(sizeof(CONN));
66 #if DEBUG
67 printf("** creating: %s %d %d\n", other_object->name, x, y);
68 #endif
70 new_conn->other_object = other_object;
71 new_conn->type = type;
72 new_conn->x = x;
73 new_conn->y = y;
74 new_conn->whichone = whichone;
75 new_conn->other_whichone = other_whichone;
77 return (new_conn);
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)
90 GList *c_current;
91 CONN *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) {
100 return (FALSE);
103 c_current = g_list_next(c_current);
106 return (TRUE);
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,
119 OBJECT *to_remove)
121 GList *c_current = NULL;
122 CONN *conn = 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);
134 #if DEBUG
135 printf("Found other_object in remove_other\n");
136 printf("Freeing other: %s %d %d\n", conn->other_object->name,
137 conn->x, conn->y);
138 #endif
140 /* Do not write modify c_current like this, since this will cause */
141 /* very nasty data corruption and upset glib's memory slice */
142 /* allocator. */
143 /* c_current->data = NULL; Do not comment in */
145 g_free(conn);
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;
152 #endif
153 s_conn_emit_conns_changed (toplevel, other_object);
155 return (TRUE);
158 c_current = g_list_next(c_current);
161 o_emit_change_notify (toplevel, other_object);
163 return (FALSE);
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)
177 OBJECT *o_current;
178 GList *iter;
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
189 * <b>to_remove</b>.
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)
195 GList *c_iter;
196 CONN *conn;
198 switch (to_remove->type) {
199 case OBJ_PIN:
200 case OBJ_NET:
201 case OBJ_BUS:
202 for (c_iter = to_remove->conn_list;
203 c_iter != NULL;
204 c_iter = g_list_next (c_iter)) {
205 conn = c_iter->data;
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));
213 c_iter->data = NULL;
214 s_conn_thaw_hooks (toplevel, conn->other_object);
215 g_free (conn);
218 g_list_free (to_remove->conn_list);
219 to_remove->conn_list = NULL;
220 break;
222 case OBJ_COMPLEX:
223 case OBJ_PLACEHOLDER:
224 s_conn_remove_glist (toplevel, to_remove->complex->prim_objs);
225 break;
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) {
245 case(OBJ_NET):
246 case(OBJ_PIN):
247 case(OBJ_BUS):
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]);
253 /* vertical */
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]) ) {
258 #if DEBUG
259 printf("Found vertical point\n");
260 #endif
261 return(o_current);
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]);
269 /* horizontal */
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]) ) {
274 #if DEBUG
275 printf("Found horizontal point\n");
276 #endif
277 return(o_current);
280 break;
282 return(NULL);
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)
296 OBJECT *o_current;
297 GList *iter;
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);
349 } else {
350 g_free (new_conn);
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)
365 TILE *t_current;
366 GList *tl_current;
367 GList *object_list;
368 OBJECT *other_object;
369 OBJECT *found;
370 int j, k;
372 s_conn_freeze_hooks (toplevel, object);
374 /* loop over all tiles which object appears in */
375 for (tl_current = object->tiles;
376 tl_current != NULL;
377 tl_current = g_list_next (tl_current)) {
378 t_current = tl_current->data;
380 for (object_list = t_current->objects;
381 object_list != NULL;
382 object_list = g_list_next (object_list)) {
383 other_object = object_list->data;
385 if (object == other_object)
386 continue;
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)
396 continue;
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)
403 continue;
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,
418 object->line->x[j],
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)
431 continue;
433 /* check for midpoint of other object, k endpoint of current obj*/
434 found = s_conn_check_midpoint (other_object, object->line->x[k],
435 object->line->y[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,
445 object->line->x[k],
446 object->line->y[k], k, -1);
448 add_connection (toplevel, other_object, object, CONN_MIDPOINT,
449 object->line->x[k],
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)
460 continue;
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);
488 #if DEBUG
489 s_conn_print(object->conn_list);
490 #endif
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) {
508 case OBJ_PIN:
509 case OBJ_NET:
510 case OBJ_BUS:
511 s_conn_update_line_object (toplevel, object);
512 break;
514 case OBJ_COMPLEX:
515 case OBJ_PLACEHOLDER:
516 s_conn_update_glist (toplevel, object->complex->prim_objs);
517 break;
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)
528 CONN *conn;
529 GList *cl_current;
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
557 * <B>new_net</B> to.
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)
562 CONN *conn;
563 GList *cl_current;
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])
573 return TRUE;
576 cl_current = g_list_next(cl_current);
579 return FALSE;
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
592 * \warning
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)
598 GList *return_list;
599 GList *iter;
600 OBJECT *o_current;
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);
609 return return_list;
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
623 * \warning
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)
629 GList *c_iter;
630 GList *return_list;
632 return_list = input_list;
634 switch (object->type) {
635 case OBJ_PIN:
636 case OBJ_NET:
637 case OBJ_BUS:
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);
646 break;
648 case OBJ_COMPLEX:
649 case OBJ_PLACEHOLDER:
650 return_list = s_conn_return_glist_others (return_list,
651 object->complex->prim_objs);
652 break;
655 return return_list;
659 typedef struct {
660 ConnsChangedFunc func;
661 void *data;
662 } ConnsChangedHook;
665 void s_conn_append_conns_changed_hook (TOPLEVEL *toplevel,
666 ConnsChangedFunc func,
667 void *data)
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;
693 return;
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,
732 (ConnsChangedFunc)
733 refresh_connectivity_cache,
734 toplevel);
736 o_attrib_append_attribs_changed_hook (toplevel,
737 (AttribsChangedFunc)
738 refresh_connectivity_cache,
739 toplevel);
742 void s_conn_init (void)
744 s_toplevel_append_new_hook ((NewToplevelFunc) s_conn_init_toplevel,
745 NULL);