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 #include <config.h>
21 
22 #include <stdio.h>
23 #include <ctype.h>
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 
28 #include "libgeda_priv.h"
29 
30 #ifdef HAVE_LIBDMALLOC
31 #include <dmalloc.h>
32 #endif
33 
34 /*! \file s_conn.c
35  *  \brief The connection system
36  *
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>.
40  *
41  *  Each connection object with the type <b>st_conn</b> represents a
42  *  single unidirectional relation to another object.
43  *
44  *  The following figure with two nets and a pin shows the relations
45  *  between connections and OBJECTS:
46  *
47  *  \image html s_conn_overview.png
48  *  \image latex s_conn_overview.pdf "Connection overview" width=14cm
49  */
50 
51 
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.
56  *
57  *  \return The new connection object
58  */
s_conn_return_new(OBJECT * other_object,int type,int x,int y,int whichone,int other_whichone)59 CONN *s_conn_return_new(OBJECT * other_object, int type, int x, int y,
60 			int whichone, int other_whichone)
61 {
62   CONN *new_conn;
63 
64   new_conn = (CONN *) g_malloc(sizeof(CONN));
65 
66 #if DEBUG
67   printf("** creating: %s %d %d\n", other_object->name, x, y);
68 #endif
69 
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;
76 
77   return (new_conn);
78 }
79 
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.
87  */
s_conn_uniq(GList * conn_list,CONN * input_conn)88 int s_conn_uniq(GList * conn_list, CONN * input_conn)
89 {
90   GList *c_current;
91   CONN *conn;
92 
93   c_current = conn_list;
94   while (c_current != NULL) {
95     conn = (CONN *) c_current->data;
96 
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);
101     }
102 
103     c_current = g_list_next(c_current);
104   }
105 
106   return (TRUE);
107 }
108 
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
117  */
s_conn_remove_other(TOPLEVEL * toplevel,OBJECT * other_object,OBJECT * to_remove)118 int s_conn_remove_other (TOPLEVEL *toplevel, OBJECT *other_object,
119                          OBJECT *to_remove)
120 {
121     GList *c_current = NULL;
122     CONN *conn = NULL;
123 
124     o_emit_pre_change_notify (toplevel, other_object);
125 
126     c_current = other_object->conn_list;
127     while (c_current != NULL) {
128 	conn = (CONN *) c_current->data;
129 
130 	if (conn->other_object == to_remove) {
131 	    other_object->conn_list =
132 		g_list_remove(other_object->conn_list, conn);
133 
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
139 
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 */
144 
145 	    g_free(conn);
146 
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;
151             }
152 #endif
153         s_conn_emit_conns_changed (toplevel, other_object);
154 
155 	    return (TRUE);
156 	}
157 
158 	c_current = g_list_next(c_current);
159     }
160 
161     o_emit_change_notify (toplevel, other_object);
162 
163     return (FALSE);
164 }
165 
166 /*! \brief removes a GList of OBJECTs from the connection system
167  *
168  *  \par Function Description
169  *  This function removes all connections from and to the OBJECTS
170  *  of the given GList.
171  *
172  *  \param toplevel  (currently not used)
173  *  \param obj_list  GList of OBJECTs to unconnected from all other objects
174  */
s_conn_remove_glist(TOPLEVEL * toplevel,GList * obj_list)175 static void s_conn_remove_glist (TOPLEVEL *toplevel, GList *obj_list)
176 {
177   OBJECT *o_current;
178   GList *iter;
179 
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);
183   }
184 }
185 
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
192  */
s_conn_remove_object(TOPLEVEL * toplevel,OBJECT * to_remove)193 void s_conn_remove_object (TOPLEVEL *toplevel, OBJECT *to_remove)
194 {
195   GList *c_iter;
196   CONN *conn;
197 
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;
206 
207 
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));
212 
213         c_iter->data = NULL;
214         s_conn_thaw_hooks (toplevel, conn->other_object);
215         g_free (conn);
216       }
217 
218       g_list_free (to_remove->conn_list);
219       to_remove->conn_list = NULL;
220       break;
221 
222     case OBJ_COMPLEX:
223     case OBJ_PLACEHOLDER:
224       s_conn_remove_glist (toplevel, to_remove->complex->prim_objs);
225       break;
226   }
227 
228   s_conn_emit_conns_changed (toplevel, to_remove);
229 }
230 
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.
239  */
s_conn_check_midpoint(OBJECT * o_current,int x,int y)240 OBJECT *s_conn_check_midpoint(OBJECT *o_current, int x, int y)
241 {
242   int min_x, min_y, max_x, max_y;
243 
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]);
252 
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);
262       }
263 
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]);
268 
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);
278       }
279 
280       break;
281   }
282   return(NULL);
283 }
284 
285 /*! \brief adds a GList of OBJECTs to the connection system
286  *
287  *  \par Function Description
288  *  This function adds all connections from and to the OBJECTS
289  *  of the given GList.
290  *
291  *  \param toplevel  (currently not used)
292  *  \param obj_list  GList of OBJECTs to add into the connection system
293  */
s_conn_update_glist(TOPLEVEL * toplevel,GList * obj_list)294 void s_conn_update_glist (TOPLEVEL *toplevel, GList *obj_list)
295 {
296   OBJECT *o_current;
297   GList *iter;
298 
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);
302   }
303 }
304 
305 
306 /*! \brief Checks if an object is bus, or a bus pin
307  *
308  *  \par Function Description
309  *  Checks if an object is a bus or a bus pin
310  *
311  *  \param object  The OBJECT to test
312  *  \return TRUE if the objects is a bis, or bus pin
313  */
is_bus_related(OBJECT * object)314 static int is_bus_related (OBJECT *object)
315 {
316   return (object->type == OBJ_BUS ||
317            (object->type == OBJ_PIN && object->pin_type == PIN_TYPE_BUS));
318 }
319 
320 
321 /*! \brief Checks if two objects are of compatible types to be connected
322  *
323  *  \par Function Description
324  *  Checks if two objects are legal to be connected together
325  *
326  *  \param object1  First OBJECT
327  *  \param object2  Second OBJECT
328  *  \return TRUE if the objects are compatible, FALSE if not
329  */
check_direct_compat(OBJECT * object1,OBJECT * object2)330 static int check_direct_compat (OBJECT *object1, OBJECT *object2)
331 {
332   return (is_bus_related (object1) == is_bus_related (object2));
333 }
334 
335 
add_connection(TOPLEVEL * toplevel,OBJECT * object,OBJECT * other_object,int type,int x,int y,int whichone,int other_whichone)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)
340 {
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);
351   }
352 }
353 
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
362  */
s_conn_update_line_object(TOPLEVEL * toplevel,OBJECT * object)363 static void s_conn_update_line_object (TOPLEVEL *toplevel, OBJECT *object)
364 {
365   TILE *t_current;
366   GList *tl_current;
367   GList *object_list;
368   OBJECT *other_object;
369   OBJECT *found;
370   int j, k;
371   OBJECT *complex, *other_complex;
372 
373   complex = o_get_parent (toplevel, object);
374 
375   s_conn_freeze_hooks (toplevel, object);
376 
377   /* loop over all tiles which object appears in */
378   for (tl_current = object->tiles;
379        tl_current != NULL;
380        tl_current = g_list_next (tl_current)) {
381     t_current = tl_current->data;
382 
383     for (object_list = t_current->objects;
384          object_list != NULL;
385          object_list = g_list_next (object_list)) {
386       other_object = object_list->data;
387 
388       if (object == other_object)
389         continue;
390 
391       other_complex = o_get_parent (toplevel, other_object);
392 
393       /* An object inside a symbol can only be connected up to another
394        * object if they are (a) both inside the same object, or (b)
395        * the object inside a symbol is a pin. */
396 
397       /* 1. Both objects are inside a symbol */
398       if (complex && other_complex) {
399         /* If inside different symbols, both must be pins to connect. */
400         if (complex != other_complex
401             && (object->type != OBJ_PIN || other_object->type != OBJ_PIN)) {
402           continue;
403         }
404 
405       /* 2. Updating object is inside a symbol, but other object is not. */
406       } else if (complex && !other_complex) {
407         if (object->type != OBJ_PIN) continue;
408       /* 3. Updating object not inside symbol, but other object is. */
409       } else if (!complex && other_complex) {
410         if (other_object->type != OBJ_PIN) continue;
411       }
412 
413       s_conn_freeze_hooks (toplevel, other_object);
414 
415       /* Here is where you check the end points */
416       /* Check both end points of the other object */
417       for (k = 0; k < 2; k++) {
418 
419         /* If the other object is a pin, only check the correct end */
420         if (other_object->type == OBJ_PIN && other_object->whichend != k)
421           continue;
422 
423         /* Check both end points of the object */
424         for (j = 0; j < 2; j++) {
425 
426           /* If the object is a pin, only check the correct end */
427           if (object->type == OBJ_PIN && object->whichend != j)
428             continue;
429 
430           /* Check for coincidence and compatability between
431              the objects being tested. */
432           if (object->line->x[j] == other_object->line->x[k] &&
433               object->line->y[j] == other_object->line->y[k] &&
434               check_direct_compat (object, other_object)) {
435 
436             o_emit_pre_change_notify (toplevel, other_object);
437 
438             add_connection (toplevel, object, other_object, CONN_ENDPOINT,
439                             other_object->line->x[k],
440                             other_object->line->y[k], j, k);
441 
442             add_connection (toplevel, other_object, object, CONN_ENDPOINT,
443                             object->line->x[j],
444                             object->line->y[j], k, j);
445 
446             o_emit_change_notify (toplevel, other_object);
447           }
448         }
449       }
450 
451       /* Check both end points of the object against midpoints of the other */
452       for (k = 0; k < 2; k++) {
453 
454         /* If the object is a pin, only check the correct end */
455         if (object->type == OBJ_PIN && object->whichend != k)
456           continue;
457 
458         /* check for midpoint of other object, k endpoint of current obj*/
459         found = s_conn_check_midpoint (other_object, object->line->x[k],
460                                                      object->line->y[k]);
461 
462         /* Pins are not allowed midpoint connections onto them. */
463         /* Allow nets to connect to the middle of buses. */
464         /* Allow compatible objects to connect. */
465         if (found && other_object->type != OBJ_PIN &&
466             ((object->type == OBJ_NET && other_object->type == OBJ_BUS) ||
467               check_direct_compat (object, other_object))) {
468 
469           add_connection (toplevel, object, other_object, CONN_MIDPOINT,
470                           object->line->x[k],
471                           object->line->y[k], k, -1);
472 
473           add_connection (toplevel, other_object, object, CONN_MIDPOINT,
474                           object->line->x[k],
475                           object->line->y[k], -1, k);
476 
477         }
478       }
479 
480       /* Check both end points of the other object against midpoints of the first */
481       for (k = 0; k < 2; k++) {
482 
483         /* If the other object is a pin, only check the correct end */
484         if (other_object->type == OBJ_PIN && other_object->whichend != k)
485           continue;
486 
487         /* do object's endpoints cross the middle of other_object? */
488         /* check for midpoint of other object, k endpoint of current obj*/
489         found = s_conn_check_midpoint (object, other_object->line->x[k],
490                                                other_object->line->y[k]);
491 
492         /* Pins are not allowed midpoint connections onto them. */
493         /* Allow nets to connect to the middle of buses. */
494         /* Allow compatible objects to connect. */
495         if (found && object->type != OBJ_PIN &&
496              ((object->type == OBJ_BUS && other_object->type == OBJ_NET) ||
497                check_direct_compat (object, other_object))) {
498 
499           add_connection (toplevel, object, other_object, CONN_MIDPOINT,
500                           other_object->line->x[k],
501                           other_object->line->y[k], -1, k);
502 
503           add_connection (toplevel, other_object, object, CONN_MIDPOINT,
504                           other_object->line->x[k],
505                           other_object->line->y[k], k, -1);
506         }
507       }
508 
509       s_conn_thaw_hooks (toplevel, other_object);
510     }
511   }
512 
513 #if DEBUG
514   s_conn_print(object->conn_list);
515 #endif
516 
517   s_conn_thaw_hooks (toplevel, object);
518 }
519 
520 /*! \brief add an OBJECT to the connection system
521  *
522  *  \par Function Description
523  *  This function searches for all geometrical conections of the OBJECT
524  *  <b>object</b> to all other connectable objects. It adds connections
525  *  to the object and from all other objects to this one.
526  *
527  *  \param toplevel (currently not used)
528  *  \param object OBJECT to add into the connection system
529  */
s_conn_update_object(TOPLEVEL * toplevel,OBJECT * object)530 void s_conn_update_object (TOPLEVEL *toplevel, OBJECT *object)
531 {
532   switch (object->type) {
533     case OBJ_PIN:
534     case OBJ_NET:
535     case OBJ_BUS:
536       s_conn_update_line_object (toplevel, object);
537       break;
538 
539     case OBJ_COMPLEX:
540     case OBJ_PLACEHOLDER:
541       s_conn_update_glist (toplevel, object->complex->prim_objs);
542       break;
543   }
544 }
545 
546 /*! \brief print all connections of a connection list
547  *  \par Function Description
548  *  This is a debugging function to print a List of connections.
549  *  \param conn_list GList of connection objects
550  */
s_conn_print(GList * conn_list)551 void s_conn_print(GList * conn_list)
552 {
553   CONN *conn;
554   GList *cl_current;
555 
556   printf("\nStarting s_conn_print\n");
557   cl_current = conn_list;
558   while (cl_current != NULL) {
559 
560     conn = (CONN *) cl_current->data;
561     printf("-----------------------------------\n");
562     printf("other object: %s\n", conn->other_object->name);
563     printf("type: %d\n", conn->type);
564     printf("x: %d y: %d\n", conn->x, conn->y);
565     printf("whichone: %d\n", conn->whichone);
566     printf("other_whichone: %d\n", conn->other_whichone);
567     printf("-----------------------------------\n");
568 
569     cl_current = g_list_next(cl_current);
570   }
571 
572 }
573 
574 /*! \brief Search for net in existing connections.
575  *  \par Function Description
576  *  This method searches the connection list for the first matching
577  *  connection with the given x, y, and whichone endpoint.
578  *
579  *  \param [in] new_net    Net OBJECT to compare to.
580  *  \param [in] whichone   The connection number to check.
581  *  \param [in] conn_list  List of existing connections to compare
582  *                         <B>new_net</B> to.
583  *  \return TRUE if a matching connection is found, FALSE otherwise.
584  */
s_conn_net_search(OBJECT * new_net,int whichone,GList * conn_list)585 int s_conn_net_search(OBJECT* new_net, int whichone, GList * conn_list)
586 {
587   CONN *conn;
588   GList *cl_current;
589 
590   cl_current = conn_list;
591   while (cl_current != NULL) {
592 
593     conn = (CONN *) cl_current->data;
594     if (conn != NULL && conn->whichone == whichone &&
595         conn->x == new_net->line->x[whichone] &&
596 	conn->y == new_net->line->y[whichone])
597     {
598        return TRUE;
599     }
600 
601     cl_current = g_list_next(cl_current);
602   }
603 
604   return FALSE;
605 }
606 
607 /*! \brief get a list of all objects connected to a list of OBJECTs.
608  *
609  *  \par Function Description
610  *  This function gets all other_object from the connection
611  *  list of the OBJECTs in the pased list.
612  *
613  *  \param [in] input_list GList of OBJECT's or NULL
614  *  \param [in] obj_list   The GList of OBJECT to get connections from
615  *  \return A GList of objects
616  *
617  *  \warning
618  *  Caller must g_list_free returned GList pointer.
619  *  Do not free individual data items in list.
620  */
s_conn_return_glist_others(GList * input_list,GList * obj_list)621 static GList *s_conn_return_glist_others (GList *input_list, GList *obj_list)
622 {
623   GList *return_list;
624   GList *iter;
625   OBJECT *o_current;
626 
627   return_list = input_list;
628 
629   for (iter = obj_list; iter != NULL; iter = g_list_next (iter)) {
630     o_current = iter->data;
631     return_list = s_conn_return_others (return_list, o_current);
632   }
633 
634   return return_list;
635 }
636 
637 /*! \brief get a list of all objects connected to this one
638  *
639  *  \par Function Description
640  *  This function gets all other_object from the connection list of the current object.
641  *  COMPLEX objects are entered, and their prim_objs processed. If an <b>input_list</b>
642  *  is given, the other objects are appended to that list.
643  *
644  *  \param [in] input_list   GList of OBJECT's
645  *  \param [in] object       OBJECT to get other OBJECTs from
646  *  \return A GList of OBJECTs
647  *
648  *  \warning
649  *  Caller must g_list_free returned GList pointer.
650  *  Do not free individual data items in list.
651  */
s_conn_return_others(GList * input_list,OBJECT * object)652 GList *s_conn_return_others(GList *input_list, OBJECT *object)
653 {
654   GList *c_iter;
655   GList *return_list;
656 
657   return_list = input_list;
658 
659   switch (object->type) {
660     case OBJ_PIN:
661     case OBJ_NET:
662     case OBJ_BUS:
663       for (c_iter = object->conn_list;
664            c_iter != NULL; c_iter = g_list_next (c_iter)) {
665         CONN *conn = c_iter->data;
666 
667         if (conn->other_object && conn->other_object != object) {
668           return_list = g_list_append(return_list, conn->other_object);
669         }
670       }
671       break;
672 
673     case OBJ_COMPLEX:
674     case OBJ_PLACEHOLDER:
675       return_list = s_conn_return_glist_others (return_list,
676                                                 object->complex->prim_objs);
677       break;
678   }
679 
680   return return_list;
681 }
682 
683 
684 typedef struct {
685   ConnsChangedFunc func;
686   void *data;
687 } ConnsChangedHook;
688 
689 
s_conn_append_conns_changed_hook(TOPLEVEL * toplevel,ConnsChangedFunc func,void * data)690 void s_conn_append_conns_changed_hook (TOPLEVEL *toplevel,
691                                        ConnsChangedFunc func,
692                                        void *data)
693 {
694   ConnsChangedHook *new_hook;
695 
696   new_hook = g_new0 (ConnsChangedHook, 1);
697   new_hook->func = func;
698   new_hook->data = data;
699 
700   toplevel->conns_changed_hooks =
701     g_list_append (toplevel->conns_changed_hooks, new_hook);
702 }
703 
704 
call_conns_changed_hook(gpointer data,gpointer user_data)705 static void call_conns_changed_hook (gpointer data, gpointer user_data)
706 {
707   ConnsChangedHook *hook = data;
708   OBJECT *object = user_data;
709 
710   hook->func (hook->data, object);
711 }
712 
713 
s_conn_emit_conns_changed(TOPLEVEL * toplevel,OBJECT * object)714 void s_conn_emit_conns_changed (TOPLEVEL *toplevel, OBJECT *object)
715 {
716   if (object->conn_notify_freeze_count > 0) {
717     object->conn_notify_pending = 1;
718     return;
719   }
720 
721   object->conn_notify_pending = 0;
722 
723   g_list_foreach (toplevel->conns_changed_hooks,
724                   call_conns_changed_hook, object);
725 }
726 
s_conn_freeze_hooks(TOPLEVEL * toplevel,OBJECT * object)727 void s_conn_freeze_hooks (TOPLEVEL *toplevel, OBJECT *object)
728 {
729   object->conn_notify_freeze_count += 1;
730 }
731 
s_conn_thaw_hooks(TOPLEVEL * toplevel,OBJECT * object)732 void s_conn_thaw_hooks (TOPLEVEL *toplevel, OBJECT *object)
733 {
734   g_return_if_fail (object->conn_notify_freeze_count > 0);
735 
736   object->conn_notify_freeze_count -= 1;
737 
738   if (object->conn_notify_freeze_count == 0 &&
739       object->conn_notify_pending)
740     s_conn_emit_conns_changed (toplevel, object);
741 }
742 
refresh_connectivity_cache(TOPLEVEL * toplevel,OBJECT * object)743 static void refresh_connectivity_cache (TOPLEVEL *toplevel, OBJECT *object)
744 {
745     if (object->type == OBJ_NET) {
746         /* FIXME: suboptimal to refresh cache every time */
747         /* better approach would invalidate cache without refresh */
748         /* refresh would be done on redraw of pin cues */
749         o_net_refresh_conn_cache (toplevel, object);
750     }
751 }
752 
s_conn_init_toplevel(TOPLEVEL * toplevel)753 static void s_conn_init_toplevel (TOPLEVEL *toplevel)
754 {
755     /* Connect the hooks for tracking net connectivity here */
756     s_conn_append_conns_changed_hook (toplevel,
757                                       (ConnsChangedFunc)
758                                           refresh_connectivity_cache,
759                                       toplevel);
760 
761     o_attrib_append_attribs_changed_hook (toplevel,
762                                           (AttribsChangedFunc)
763                                               refresh_connectivity_cache,
764                                           toplevel);
765 }
766 
s_conn_init(void)767 void s_conn_init (void)
768 {
769     s_toplevel_append_new_hook ((NewToplevelFunc) s_conn_init_toplevel,
770                                 NULL);
771 }
772