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