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
21 /*! \file o_complex_basic.c
22 * \brief Functions for complex objects
23 *
24 * Complex objects are collections of primary objects.
25 */
26
27 #include <config.h>
28
29 #include <stdio.h>
30 #include <math.h>
31 #ifdef HAVE_STRING_H
32 #include <string.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include "libgeda_priv.h"
39
40 #ifdef HAVE_LIBDMALLOC
41 #include <dmalloc.h>
42 #endif
43
44
45 /*! \brief Return the bounds of the given object.
46 * \par Given an object, calculate the bounds coordinates.
47 * \param [in] toplevel The toplevel structure.
48 * \param [in] o_current The object to look the bounds for.
49 * \param [out] rleft pointer to the left coordinate of the object.
50 * \param [out] rtop pointer to the top coordinate of the object.
51 * \param [out] rright pointer to the right coordinate of the object.
52 * \param [out] rbottom pointer to the bottom coordinate of the object.
53 * \return If any bounds were found for the object
54 * \retval 0 No bound was found
55 * \retval 1 Bound was found
56 */
world_get_single_object_bounds(TOPLEVEL * toplevel,OBJECT * o_current,int * rleft,int * rtop,int * rright,int * rbottom)57 int world_get_single_object_bounds(TOPLEVEL *toplevel, OBJECT *o_current,
58 int *rleft, int *rtop, int *rright, int *rbottom)
59 {
60 if (o_current != NULL) {
61 switch(o_current->type) {
62 case(OBJ_TEXT):
63 /* only do bounding boxes for visible or doing show_hidden_text*/
64 /* you might lose some attrs though */
65 if (! (o_is_visible (toplevel, o_current) ||
66 toplevel->show_hidden_text)) {
67 return 0;
68 }
69 /* This case falls through intentionally */
70 case(OBJ_LINE):
71 case(OBJ_NET):
72 case(OBJ_BUS):
73 case(OBJ_BOX):
74 case(OBJ_PICTURE):
75 case(OBJ_CIRCLE):
76 case(OBJ_PATH):
77 case(OBJ_PIN):
78 case(OBJ_ARC):
79 case(OBJ_COMPLEX):
80 case(OBJ_PLACEHOLDER):
81 if (!o_current->w_bounds_valid) {
82 o_recalc_single_object (toplevel, o_current);
83 if (!o_current->w_bounds_valid) {
84 return 0;
85 }
86 }
87 *rleft = o_current->w_left;
88 *rtop = o_current->w_top;
89 *rright = o_current->w_right;
90 *rbottom = o_current->w_bottom;
91 return 1;
92
93 default:
94 break;
95 }
96 }
97 return 0;
98 }
99
100
101 /*! \brief Return the bounds of the given GList of objects.
102 * \par Given a list of objects, calcule the bounds coordinates.
103 * \param [in] toplevel The TOPLEVEL structure.
104 * \param [in] head The list of objects to look the bounds for.
105 * \param [out] left pointer to the left coordinate of the object.
106 * \param [out] top pointer to the top coordinate of the object.
107 * \param [out] right pointer to the right coordinate of the object.
108 * \param [out] bottom pointer to the bottom coordinate of the object.
109 * \return If any bounds were found for the list of objects
110 * \retval 0 No bounds were found
111 * \retval 1 Bound was found
112 */
world_get_object_glist_bounds(TOPLEVEL * toplevel,const GList * head,int * left,int * top,int * right,int * bottom)113 int world_get_object_glist_bounds(TOPLEVEL *toplevel, const GList *head,
114 int *left, int *top, int *right, int *bottom)
115 {
116 const GList *s_current=NULL;
117 OBJECT *o_current=NULL;
118 int rleft, rtop, rright, rbottom;
119 int found = 0;
120
121 s_current = head;
122
123 /* Find the first object with bounds, and set the bounds variables, then expand as necessary */
124 while ( s_current != NULL ) {
125 o_current = (OBJECT *) s_current->data;
126
127 /* Sanity check */
128 g_return_val_if_fail ((o_current != NULL), found);
129
130 if ( world_get_single_object_bounds( toplevel, o_current, &rleft, &rtop, &rright, &rbottom) ) {
131 if ( found ) {
132 *left = min( *left, rleft );
133 *top = min( *top, rtop );
134 *right = max( *right, rright );
135 *bottom = max( *bottom, rbottom );
136 } else {
137 *left = rleft;
138 *top = rtop;
139 *right = rright;
140 *bottom = rbottom;
141 found = 1;
142 }
143 }
144 s_current = g_list_next (s_current);
145 }
146 return found;
147 }
148
149 /*! \brief Queries the bounds of a complex object.
150 * \par Function Description
151 * This function returns the bounding box of the complex object
152 * <B>object</B>.
153 *
154 * \param [in] toplevel The toplevel environment.
155 * \param [in] complex The complex object.
156 * \param [out] left The leftmost edge of the bounding box (in
157 * world units).
158 * \param [out] top The upper edge of the bounding box (in
159 * world units).
160 * \param [out] right The rightmost edge of the bounding box (in
161 * world units).
162 * \param [out] bottom The bottom edge of the bounding box (in
163 * screen units).
164 */
world_get_complex_bounds(TOPLEVEL * toplevel,OBJECT * complex,int * left,int * top,int * right,int * bottom)165 void world_get_complex_bounds(TOPLEVEL *toplevel, OBJECT *complex,
166 int *left, int *top, int *right, int *bottom)
167 {
168 g_return_if_fail (complex != NULL &&
169 (complex->type == OBJ_COMPLEX ||
170 complex->type == OBJ_PLACEHOLDER) &&
171 complex->complex != NULL);
172
173 world_get_object_glist_bounds (toplevel, complex->complex->prim_objs,
174 left, top, right, bottom);
175 }
176
177 /*! \brief get the position of the complex base point
178 * \par Function Description
179 * This function gets the position of the base point of a complex object.
180 *
181 * \param [in] toplevel The toplevel environment.
182 * \param [out] x pointer to the x-position
183 * \param [out] y pointer to the y-position
184 * \param [in] object The object to get the position.
185 * \return TRUE if successfully determined the position, FALSE otherwise
186 */
o_complex_get_position(TOPLEVEL * toplevel,gint * x,gint * y,OBJECT * object)187 gboolean o_complex_get_position (TOPLEVEL *toplevel, gint *x, gint *y,
188 OBJECT *object)
189 {
190 *x = object->complex->x;
191 *y = object->complex->y;
192 return TRUE;
193 }
194
195 /*! \brief check whether an object is a attributes
196 * \par Function Description
197 * This function checks if an object should be promoted.
198 * An attribute object is promotable if it's promoted by default, or the user
199 * has configered it to promote an attribute.
200 *
201 * \param [in] toplevel The TOPLEVEL object
202 * \param [in] object The attribute object to check
203 * \return TRUE if the object is a eligible attribute, FALSE otherwise
204 */
o_complex_is_eligible_attribute(TOPLEVEL * toplevel,OBJECT * object)205 static int o_complex_is_eligible_attribute (TOPLEVEL *toplevel, OBJECT *object)
206 {
207 char *name = NULL;
208 int promotableAttribute = FALSE;
209
210 /* always promote symversion= attribute, even if it is invisible */
211 if (strncmp(object->text->string, "symversion=", 11) == 0)
212 return TRUE;
213
214 /* check list against attributes which can be promoted */
215 if (toplevel->always_promote_attributes != NULL) {
216 if (o_attrib_get_name_value (object, &name, NULL)) {
217 if (g_list_find_custom(toplevel->always_promote_attributes,
218 name, (GCompareFunc) strcmp) != NULL) {
219 /* Name of the attribute was in the always promote attributes list */
220 promotableAttribute = TRUE;
221 }
222
223 g_free(name);
224 if (promotableAttribute)
225 return TRUE;
226 }
227 }
228
229 /* object is invisible and we do not want to promote invisible text */
230 if ((!o_is_visible (toplevel, object)) &&
231 (toplevel->promote_invisible == FALSE))
232 return FALSE; /* attribute not eligible for promotion */
233
234 /* yup, attribute can be promoted */
235 return TRUE;
236 }
237
238 /*! \brief get the embedded state of an complex object
239 * \par Function Description
240 * Checks and returns the status of the complex object.
241 *
242 * \param o_current The object to check
243 * \return 1 if embedded, 0 otherwise
244 */
o_complex_is_embedded(OBJECT * o_current)245 int o_complex_is_embedded(OBJECT *o_current)
246 {
247 g_return_val_if_fail(o_current != NULL, 0);
248
249 if(o_current->complex == NULL)
250 return 0;
251
252 if (o_current->complex_embedded) {
253 return 1;
254 } else {
255 return 0;
256 }
257 }
258
259
260 /*! \brief Get attributes eligible for promotion from inside a complex
261 *
262 * \par Function Description
263 * Returns a GList of OBJECTs which are eligible for promotion from
264 * within the passed complex OBJECT.
265 *
266 * If detach is TRUE, the function removes these attribute objects
267 * from the prim_objs of the complex. If detach is FALSE, the
268 * OBJECTs are left in place.
269 *
270 * \param [in] toplevel The toplevel environment.
271 * \param [in] object The complex object being modified.
272 * \param [in] detach Should the attributes be detached?
273 * \returns A linked list of OBJECTs to promote.
274 */
o_complex_get_promotable(TOPLEVEL * toplevel,OBJECT * object,int detach)275 GList *o_complex_get_promotable (TOPLEVEL *toplevel, OBJECT *object, int detach)
276 {
277 GList *promoted = NULL;
278 GList *attribs;
279 GList *iter;
280 OBJECT *tmp;
281
282 if (!toplevel->attribute_promotion) /* controlled through rc file */
283 return NULL;
284
285 attribs = o_attrib_find_floating_attribs (object->complex->prim_objs);
286
287 for (iter = attribs; iter != NULL; iter = g_list_next (iter)) {
288 tmp = iter->data;
289
290 /* Is it an attribute we want to promote? */
291 if (!o_complex_is_eligible_attribute(toplevel, tmp))
292 continue;
293
294 if (detach) {
295 tmp->parent = NULL;
296 object->complex->prim_objs =
297 g_list_remove (object->complex->prim_objs, tmp);
298 }
299
300 promoted = g_list_prepend (promoted, tmp);
301 }
302
303 g_list_free (attribs);
304
305 promoted = g_list_reverse (promoted);
306 return promoted;
307 }
308
309
310 /*! \brief Promote attributes from a complex OBJECT
311 * \par Function Description
312 * Selects promotable attributes from \a object, and returns a new
313 * #GList containing them (suitable for appending to a #PAGE).
314 *
315 * \param [in] toplevel The #TOPLEVEL environment.
316 * \param [in] object The complex #OBJECT to promote from.
317 * \return A #GList of promoted attributes.
318 */
o_complex_promote_attribs(TOPLEVEL * toplevel,OBJECT * object)319 GList *o_complex_promote_attribs (TOPLEVEL *toplevel, OBJECT *object)
320 {
321 GList *promoted = NULL;
322 GList *promotable = NULL;
323 GList *iter = NULL;
324
325 promotable = o_complex_get_promotable (toplevel, object, FALSE);
326
327 /* Run through the attributes deciding if we want to keep them (in
328 * which case we copy them and make them invisible) or if we want to
329 * remove them. */
330 if (toplevel->keep_invisible) {
331 for (iter = promotable; iter != NULL; iter = g_list_next (iter)) {
332 OBJECT *o_kept = (OBJECT *) iter->data;
333 OBJECT *o_copy = o_object_copy (toplevel, o_kept);
334 o_set_visibility (toplevel, o_kept, INVISIBLE);
335 o_copy->parent = NULL;
336 promoted = g_list_prepend (promoted, o_copy);
337 }
338 promoted = g_list_reverse (promoted);
339 } else {
340 for (iter = promotable; iter != NULL; iter = g_list_next (iter)) {
341 OBJECT *o_removed = (OBJECT *) iter->data;
342 o_removed->parent = NULL;
343 object->complex->prim_objs =
344 g_list_remove (object->complex->prim_objs, o_removed);
345 }
346 promoted = promotable;
347 /* Invalidate the object's bounds since we may have
348 * stolen objects from inside it. */
349 o_bounds_invalidate (toplevel, object);
350 }
351
352 /* Attach promoted attributes to the original complex object */
353 o_attrib_attach_list (toplevel, promoted, object, TRUE);
354
355 return promoted;
356 }
357
358
359 /*! \brief Delete or hide promotable from the passed OBJECT
360 *
361 * \par Function Description
362 * Deletes or hides promotable attributes from the passed OBJECT.
363 * This is used when loading symbols during the load of a schematic from
364 * disk. The schematic will already contain local copies of symbol's
365 * promotable objects, so we delete or hide the symbol's copies.
366 *
367 * Deletion / hiding is dependant on the setting of
368 * toplevel->keep_invisible. If true, attributes eligible for
369 * promotion are kept in memory but flagged as invisible.
370 *
371 * \param [in] toplevel The toplevel environment.
372 * \param [in] object The complex object being altered.
373 */
o_complex_remove_promotable_attribs(TOPLEVEL * toplevel,OBJECT * object)374 static void o_complex_remove_promotable_attribs (TOPLEVEL *toplevel, OBJECT *object)
375 {
376 GList *promotable, *iter;
377
378 promotable = o_complex_get_promotable (toplevel, object, FALSE);
379
380 if (promotable == NULL)
381 return;
382
383 for (iter = promotable; iter != NULL; iter = g_list_next (iter)) {
384 OBJECT *a_object = iter->data;
385 if (toplevel->keep_invisible == TRUE) { /* Hide promotable attributes */
386 o_set_visibility (toplevel, a_object, INVISIBLE);
387 } else { /* Delete promotable attributes */
388 object->complex->prim_objs =
389 g_list_remove (object->complex->prim_objs, a_object);
390 s_delete_object (toplevel, a_object);
391 }
392 }
393
394 o_bounds_invalidate (toplevel, object);
395 g_list_free (promotable);
396 }
397
create_placeholder(TOPLEVEL * toplevel,OBJECT * new_node,int x,int y)398 static void create_placeholder(TOPLEVEL * toplevel, OBJECT * new_node, int x, int y)
399 {
400 OBJECT *new_prim_obj;
401 char *not_found_text = NULL;
402 int left, right, top, bottom;
403 int x_offset, y_offset;
404
405 /* Put placeholder into object list. Changed by SDB on
406 * 1.19.2005 to fix problem that symbols were silently
407 * deleted by gattrib when RC files were messed up. */
408 new_node->type = OBJ_PLACEHOLDER;
409
410 /* Mark the origin of the missing component */
411 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
412 DETACHED_ATTRIBUTE_COLOR,
413 x - 50, y, x + 50, y);
414 new_node->complex->prim_objs = g_list_prepend (new_node->complex->prim_objs, new_prim_obj);
415 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
416 DETACHED_ATTRIBUTE_COLOR,
417 x, y + 50, x, y - 50);
418 new_node->complex->prim_objs = g_list_prepend (new_node->complex->prim_objs, new_prim_obj);
419
420 /* Add some useful text */
421 not_found_text =
422 g_strdup_printf (_("Component not found:\n %s"),
423 new_node->complex_basename);
424 new_prim_obj = o_text_new(toplevel,
425 OBJ_TEXT, DETACHED_ATTRIBUTE_COLOR,
426 x + NOT_FOUND_TEXT_X,
427 y + NOT_FOUND_TEXT_Y, LOWER_LEFT, 0,
428 not_found_text, 8,
429 VISIBLE, SHOW_NAME_VALUE);
430 new_node->complex->prim_objs = g_list_prepend (new_node->complex->prim_objs, new_prim_obj);
431 g_free(not_found_text);
432
433 /* figure out where to put the hazard triangle */
434 world_get_text_bounds (toplevel, new_prim_obj, &left, &top, &right, &bottom);
435 x_offset = (right - left) / 4;
436 y_offset = bottom - top + 100; /* 100 is just an additional offset */
437
438 /* add hazard triangle */
439 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
440 DETACHED_ATTRIBUTE_COLOR,
441 x + NOT_FOUND_TEXT_X + x_offset,
442 y + NOT_FOUND_TEXT_Y + y_offset,
443 x + NOT_FOUND_TEXT_X + x_offset + 600,
444 y + NOT_FOUND_TEXT_Y + y_offset);
445 o_set_line_options(toplevel, new_prim_obj, END_ROUND, TYPE_SOLID,
446 50, -1, -1);
447 new_node->complex->prim_objs = g_list_prepend (new_node->complex->prim_objs, new_prim_obj);
448 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
449 DETACHED_ATTRIBUTE_COLOR,
450 x + NOT_FOUND_TEXT_X + x_offset,
451 y + NOT_FOUND_TEXT_Y + y_offset,
452 x + NOT_FOUND_TEXT_X + x_offset + 300,
453 y + NOT_FOUND_TEXT_Y + y_offset + 500);
454 o_set_line_options(toplevel, new_prim_obj, END_ROUND, TYPE_SOLID,
455 50, -1, -1);
456 new_node->complex->prim_objs = g_list_prepend (new_node->complex->prim_objs, new_prim_obj);
457 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
458 DETACHED_ATTRIBUTE_COLOR,
459 x + NOT_FOUND_TEXT_X + x_offset + 300,
460 y + NOT_FOUND_TEXT_Y + y_offset + 500,
461 x + NOT_FOUND_TEXT_X + x_offset + 600,
462 y + NOT_FOUND_TEXT_Y + y_offset);
463 o_set_line_options(toplevel, new_prim_obj, END_ROUND, TYPE_SOLID,
464 50, -1, -1);
465 new_node->complex->prim_objs = g_list_prepend (new_node->complex->prim_objs, new_prim_obj);
466 new_prim_obj = o_text_new(toplevel,
467 OBJ_TEXT, DETACHED_ATTRIBUTE_COLOR,
468 x + NOT_FOUND_TEXT_X + x_offset + 270,
469 y + NOT_FOUND_TEXT_Y + y_offset + 90,
470 LOWER_LEFT, 0, "!", 18,
471 VISIBLE, SHOW_NAME_VALUE);
472 new_node->complex->prim_objs = g_list_prepend (new_node->complex->prim_objs, new_prim_obj);
473 new_node->complex->prim_objs = g_list_reverse(new_node->complex->prim_objs);
474 }
475
476 /* Done */
477 /*! \brief
478 * \par Function Description
479 *
480 */
o_complex_new(TOPLEVEL * toplevel,char type,int color,int x,int y,int angle,int mirror,const CLibSymbol * clib,const gchar * basename,int selectable)481 OBJECT *o_complex_new(TOPLEVEL *toplevel,
482 char type,
483 int color, int x, int y, int angle,
484 int mirror, const CLibSymbol *clib,
485 const gchar *basename,
486 int selectable)
487 {
488 OBJECT *new_node=NULL;
489 GList *iter;
490 gchar *buffer = NULL;
491
492 new_node = s_basic_new_object(type, "complex");
493
494 if (clib != NULL) {
495 new_node->complex_basename = g_strdup (s_clib_symbol_get_name (clib));
496 } else {
497 new_node->complex_basename = g_strdup (basename);
498 }
499
500
501 new_node->complex_embedded = FALSE;
502 new_node->color = color;
503 new_node->selectable = selectable;
504
505 new_node->complex = (COMPLEX *) g_malloc(sizeof(COMPLEX));
506 new_node->complex->prim_objs = NULL;
507 new_node->complex->angle = angle;
508 new_node->complex->mirror = mirror;
509 new_node->complex->x = x;
510 new_node->complex->y = y;
511
512 /* get the symbol data */
513 if (clib != NULL) {
514 buffer = s_clib_symbol_get_data (clib);
515 }
516
517 if (clib == NULL || buffer == NULL)
518 create_placeholder(toplevel, new_node, x, y);
519 else {
520 GError * err = NULL;
521
522 /* add connections till translated */
523 new_node->complex->prim_objs = o_read_buffer (toplevel, NULL, buffer, -1, new_node->complex_basename, &err);
524 if (err) {
525 g_error_free(err);
526 /* If reading fails, replace with placeholder object */
527 create_placeholder(toplevel, new_node, x, y);
528 }
529 else {
530 if (mirror) {
531 o_glist_mirror_world (toplevel, 0, 0, new_node->complex->prim_objs);
532 }
533
534 o_glist_rotate_world (toplevel, 0, 0, angle, new_node->complex->prim_objs);
535 o_glist_translate_world (toplevel, x, y, new_node->complex->prim_objs);
536 }
537
538 g_free (buffer);
539
540 }
541
542 /* set the parent field now */
543 for (iter = new_node->complex->prim_objs; iter != NULL; iter = g_list_next (iter)) {
544 OBJECT *tmp = iter->data;
545 tmp->parent = new_node;
546 }
547
548 o_complex_recalc(toplevel, new_node);
549
550 return new_node;
551 }
552
553 /*! \brief create a new embedded object
554 * \par Function Description
555 * This function creates a new embedded object.
556 *
557 * \param [in] toplevel The TOPLEVEL object
558 * \param [in] type The type of the object (usually OBJ_COMLEX)
559 * \param [in] color The color of the object
560 * \param [in] x The x location of the complex object
561 * \param [in] y The y location of the complex object
562 * \param [in] angle The rotation angle
563 * \param [in] mirror The mirror status
564 * \param [in] basename The basic name the embedded was created of
565 * \param [in] selectable whether the object can be selected with the mouse
566 * \return a new complex object
567 */
o_complex_new_embedded(TOPLEVEL * toplevel,char type,int color,int x,int y,int angle,int mirror,const gchar * basename,int selectable)568 OBJECT *o_complex_new_embedded(TOPLEVEL *toplevel,
569 char type, int color, int x, int y, int angle, int mirror,
570 const gchar *basename, int selectable)
571 {
572 OBJECT *new_node=NULL;
573
574 new_node = s_basic_new_object(type, "complex");
575
576 new_node->complex = (COMPLEX *) g_malloc(sizeof(COMPLEX));
577 new_node->complex->x = x;
578 new_node->complex->y = y;
579
580 new_node->complex->angle = angle;
581 new_node->complex->mirror = mirror;
582
583 new_node->complex_basename = g_strdup(basename);
584
585 new_node->complex_embedded = TRUE;
586
587 new_node->color = color;
588 new_node->selectable = selectable;
589
590 new_node->complex->prim_objs = NULL;
591
592 /* don't have to translate/rotate/mirror here at all since the */
593 /* object is in place */
594 return new_node;
595 }
596
597 /*! \brief update the visual boundaries of the complex object
598 * \par Function Description
599 * This function updates the boundaries of the object \a o_current.
600 *
601 * \param [in] toplevel The TOPLEVEL object
602 * \param [in] o_current The OBJECT to update
603 */
o_complex_recalc(TOPLEVEL * toplevel,OBJECT * o_current)604 void o_complex_recalc(TOPLEVEL *toplevel, OBJECT *o_current)
605 {
606 int left, right, top, bottom;
607
608 /* realc routine Add this somewhere */
609 /* libhack */
610 /* o_recalc(toplevel, o_current->complex);*/
611
612 if ((!o_current) || (o_current->type != OBJ_COMPLEX && o_current->type != OBJ_PLACEHOLDER))
613 return;
614
615 if (o_current->complex->prim_objs == NULL)
616 return;
617
618 world_get_complex_bounds(toplevel, o_current, &left, &top, &right, &bottom);
619 o_current->w_left = left;
620 o_current->w_top = top;
621 o_current->w_right = right;
622 o_current->w_bottom = bottom;
623 o_current->w_bounds_valid = TRUE;
624 }
625
626 /*! \brief read a complex object from a char buffer
627 * \par Function Description
628 * This function reads a complex object from the buffer \a buf.
629 * If the complex object was read successfully, a new object is
630 * allocated and appended to the \a object_list.
631 *
632 * \param [in] toplevel The TOPLEVEL object
633 * \param [in] buf a text buffer (usually a line of a schematic file)
634 * \param [in] release_ver The release number gEDA
635 * \param [in] fileformat_ver a integer value of the file format
636 * \return The object list, or NULL on error.
637 */
o_complex_read(TOPLEVEL * toplevel,const char buf[],unsigned int release_ver,unsigned int fileformat_ver,GError ** err)638 OBJECT *o_complex_read (TOPLEVEL *toplevel,
639 const char buf[], unsigned int release_ver,
640 unsigned int fileformat_ver, GError **err)
641 {
642 OBJECT *new_obj;
643 char type;
644 int x1, y1;
645 int angle;
646
647 char *basename = g_malloc (1 + strlen (buf));
648
649 int selectable;
650 int mirror;
651
652 if (sscanf(buf, "%c %d %d %d %d %d %s\n",
653 &type, &x1, &y1, &selectable, &angle, &mirror, basename) != 7) {
654 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse complex object"));
655 return NULL;
656 }
657
658 switch(angle) {
659
660 case(0):
661 case(90):
662 case(180):
663 case(270):
664 break;
665
666 default:
667 s_log_message(_("Found a component with an invalid rotation [ %c %d %d %d %d %d %s ]\n"), type, x1, y1, selectable, angle, mirror, basename);
668 s_log_message (_("Setting angle to 0\n"));
669 angle = 0;
670 }
671
672 switch(mirror) {
673
674 case(0):
675 case(1):
676
677 break;
678
679 default:
680 s_log_message(_("Found a component with an invalid mirror flag [ %c %d %d %d %d %d %s ]\n"), type, x1, y1, selectable, angle, mirror, basename);
681 s_log_message (_("Setting mirror to 0\n"));
682 mirror = 0;
683 }
684 if (strncmp(basename, "EMBEDDED", 8) == 0) {
685
686 new_obj = o_complex_new_embedded(toplevel, type,
687 DEFAULT_COLOR, x1, y1, angle, mirror,
688 basename + 8,
689 selectable);
690 } else {
691
692 const CLibSymbol *clib = s_clib_get_symbol_by_name (basename);
693
694 new_obj = o_complex_new(toplevel, type,
695 DEFAULT_COLOR,
696 x1, y1,
697 angle, mirror, clib,
698 basename, selectable);
699 /* Delete or hide attributes eligible for promotion inside the complex */
700 if (new_obj)
701 o_complex_remove_promotable_attribs (toplevel, new_obj);
702 }
703
704 g_free (basename);
705
706 return new_obj;
707 }
708
709 /*! \brief Create a string representation of the complex object
710 * \par Function Description
711 * This function takes a complex \a object and return a string
712 * according to the file format definition.
713 *
714 * \param [in] toplevel a TOPLEVEL structure
715 * \param [in] object a complex OBJECT
716 * \return the string representation of the complex OBJECT
717 */
o_complex_save(TOPLEVEL * toplevel,OBJECT * object)718 char *o_complex_save(TOPLEVEL *toplevel, OBJECT *object)
719 {
720 int selectable;
721 char *buf = NULL;
722 char *basename;
723
724 g_return_val_if_fail (object != NULL, NULL);
725
726 selectable = (object->selectable) ? 1 : 0;
727
728 if ((object->type == OBJ_COMPLEX) || (object->type == OBJ_PLACEHOLDER)) {
729 basename = g_strdup_printf ("%s%s",
730 object->complex_embedded ? "EMBEDDED" : "",
731 object->complex_basename);
732 /* We force the object type to be output as OBJ_COMPLEX for both
733 * these object types. */
734 buf = g_strdup_printf("%c %d %d %d %d %d %s", OBJ_COMPLEX,
735 object->complex->x, object->complex->y,
736 selectable, object->complex->angle,
737 object->complex->mirror, basename);
738 g_free (basename);
739 }
740
741 return(buf);
742 }
743
744 /*! \brief move a complex object
745 * \par Function Description
746 * This function changes the position of a complex \a object.
747 *
748 * \param [in] toplevel The TOPLEVEL object
749 * \param [in] dx The x-distance to move the object
750 * \param [in] dy The y-distance to move the object
751 * \param [in] object The complex OBJECT to be moved
752 */
o_complex_translate_world(TOPLEVEL * toplevel,int dx,int dy,OBJECT * object)753 void o_complex_translate_world(TOPLEVEL *toplevel, int dx, int dy,
754 OBJECT *object)
755 {
756 g_return_if_fail (object != NULL &&
757 (object->type == OBJ_COMPLEX ||
758 object->type == OBJ_PLACEHOLDER));
759
760 object->complex->x = object->complex->x + dx;
761 object->complex->y = object->complex->y + dy;
762
763 o_glist_translate_world (toplevel, dx, dy, object->complex->prim_objs);
764
765 o_complex_recalc (toplevel, object);
766 }
767
768 /*! \brief Create a copy of a COMPLEX object
769 * \par Function Description
770 * This function creates a copy of the complex object \a o_current.
771 *
772 * \param [in] toplevel The TOPLEVEL object
773 * \param [in] o_current The object that is copied
774 * \return a new COMPLEX object
775 */
o_complex_copy(TOPLEVEL * toplevel,OBJECT * o_current)776 OBJECT *o_complex_copy(TOPLEVEL *toplevel, OBJECT *o_current)
777 {
778 OBJECT *o_new;
779 GList *iter;
780
781 g_return_val_if_fail(o_current != NULL, NULL);
782
783 o_new = s_basic_new_object(o_current->type, "complex");
784 o_new->color = o_current->color;
785 o_new->selectable = o_current->selectable;
786 o_new->complex_basename = g_strdup(o_current->complex_basename);
787 o_new->complex_embedded = o_current->complex_embedded;
788
789 o_new->complex = g_malloc0(sizeof(COMPLEX));
790 o_new->complex->x = o_current->complex->x;
791 o_new->complex->y = o_current->complex->y;
792 o_new->complex->angle = o_current->complex->angle;
793 o_new->complex->mirror = o_current->complex->mirror;
794
795 /* Copy contents and set the parent pointers on the copied objects. */
796 o_new->complex->prim_objs =
797 o_glist_copy_all (toplevel, o_current->complex->prim_objs,
798 NULL);
799
800 for (iter = o_new->complex->prim_objs;
801 iter != NULL;
802 iter = g_list_next (iter)) {
803 ((OBJECT*) iter->data)->parent = o_new;
804 }
805
806 /* Recalculate bounds */
807 o_complex_recalc(toplevel, o_new);
808
809 /* Delete or hide attributes eligible for promotion inside the complex */
810 o_complex_remove_promotable_attribs (toplevel, o_new);
811
812 s_slot_update_object (toplevel, o_new);
813
814 /* deal with stuff that has changed */
815
816 /* here you need to create a list of attributes which need to be
817 * connected to the new list, probably make an attribute list and
818 * fill it with sid's of the attributes */
819
820 return o_new;
821 }
822
823
824 /*! \todo Finish function documentation!!!
825 * \brief
826 * \par Function Description
827 *
828 */
o_complex_rotate_world(TOPLEVEL * toplevel,int centerx,int centery,int angle,OBJECT * object)829 void o_complex_rotate_world(TOPLEVEL *toplevel,
830 int centerx, int centery,
831 int angle, OBJECT *object)
832 {
833 int x, y;
834 int newx, newy;
835
836 g_return_if_fail (object!=NULL);
837 g_return_if_fail ((object->type == OBJ_COMPLEX) ||
838 (object->type == OBJ_PLACEHOLDER));
839
840 x = object->complex->x + (-centerx);
841 y = object->complex->y + (-centery);
842
843 rotate_point_90(x, y, angle, &newx, &newy);
844
845 x = newx + (centerx);
846 y = newy + (centery);
847
848 o_complex_translate_world(toplevel,
849 -object->complex->x,
850 -object->complex->y, object);
851 o_glist_rotate_world (toplevel, 0, 0, angle, object->complex->prim_objs);
852
853 object->complex->x = 0;
854 object->complex->y = 0;
855
856 o_complex_translate_world(toplevel, x, y, object);
857
858 object->complex->angle = ( object->complex->angle + angle ) % 360;
859 }
860
861
862 /*! \todo Finish function documentation!!!
863 * \brief
864 * \par Function Description
865 *
866 */
o_complex_mirror_world(TOPLEVEL * toplevel,int world_centerx,int world_centery,OBJECT * object)867 void o_complex_mirror_world(TOPLEVEL *toplevel,
868 int world_centerx, int world_centery,
869 OBJECT *object)
870 {
871 int x, y;
872
873 g_return_if_fail( object != NULL );
874 g_return_if_fail( (object->type == OBJ_COMPLEX ||
875 object->type == OBJ_PLACEHOLDER) );
876 g_return_if_fail( object->complex != NULL );
877
878 x = 2 * world_centerx - object->complex->x;
879 y = object->complex->y;
880
881 o_complex_translate_world(toplevel,
882 -object->complex->x,
883 -object->complex->y, object);
884
885 o_glist_mirror_world (toplevel, 0, 0, object->complex->prim_objs);
886
887 switch(object->complex->angle) {
888 case(90):
889 object->complex->angle = 270;
890 break;
891
892 case(270):
893 object->complex->angle = 90;
894 break;
895
896 }
897
898 object->complex->mirror = !object->complex->mirror;
899
900 o_complex_translate_world(toplevel, x, y, object);
901 }
902
903
904 /*! \brief Find a pin with a particular attribute.
905 * \par Function Description
906 * Search for a pin inside the given complex which has an attribute
907 * matching those passed.
908 *
909 * \param [in] object complex OBJECT whos pins to search.
910 * \param [in] name the attribute name to search for.
911 * \param [in] wanted_value the attribute value to search for.
912 * \return The pin OBJECT with the given attribute, NULL otherwise.
913 */
o_complex_find_pin_by_attribute(OBJECT * object,char * name,char * wanted_value)914 OBJECT *o_complex_find_pin_by_attribute (OBJECT *object, char *name, char *wanted_value)
915 {
916 GList *iter;
917 OBJECT *o_current;
918 char *value;
919 int found;
920
921 g_return_val_if_fail (object != NULL, NULL);
922 g_return_val_if_fail (object->type == OBJ_COMPLEX ||
923 object->type == OBJ_PLACEHOLDER, NULL);
924
925 for (iter = object->complex->prim_objs; iter != NULL;
926 iter = g_list_next (iter)) {
927 o_current = iter->data;
928
929 if (o_current->type != OBJ_PIN)
930 continue;
931
932 value = o_attrib_search_object_attribs_by_name (o_current, name, 0);
933 found = (value != NULL && strcmp (value, wanted_value) == 0);
934 g_free (value);
935
936 if (found)
937 return o_current;
938 }
939
940 return NULL;
941 }
942
943
944 /*! \brief check the symversion of a complex object
945 * \par Function Description
946 * This function compares the symversion of a symbol with it's
947 * earlier saved symversion in a schematic.
948 * Major symversion changes are added to the toplevel object
949 * (toplevel->major_changed_refdes), minor changes are reported
950 * to the messaging system.
951 *
952 * \param toplevel The TOPLEVEL object
953 * \param object The complex OBJECT
954 */
955 void
o_complex_check_symversion(TOPLEVEL * toplevel,OBJECT * object)956 o_complex_check_symversion(TOPLEVEL* toplevel, OBJECT* object)
957 {
958 char *inside = NULL;
959 char *outside = NULL;
960 char *refdes = NULL;
961 double inside_value = -1.0;
962 double outside_value = -1.0;
963 char *err_check = NULL;
964 int inside_present = FALSE;
965 int outside_present = FALSE;
966 double inside_major, inside_minor;
967 double outside_major, outside_minor;
968
969 g_return_if_fail (object != NULL);
970 g_return_if_fail ((object->type == OBJ_COMPLEX ||
971 object->type == OBJ_PLACEHOLDER));
972 g_return_if_fail (object->complex != NULL);
973
974 /* first look on the inside for the symversion= attribute */
975 inside = o_attrib_search_inherited_attribs_by_name (object, "symversion", 0);
976
977 /* now look for the symversion= attached to object */
978 outside = o_attrib_search_attached_attribs_by_name (object, "symversion", 0);
979
980 /* get the uref for future use */
981 refdes = o_attrib_search_object_attribs_by_name(object, "refdes", 0);
982 if (!refdes)
983 {
984 refdes = g_strdup ("unknown");
985 }
986
987 if (inside)
988 {
989 inside_value = strtod(inside, &err_check);
990 if (inside_value == 0 && inside == err_check)
991 {
992 if (inside)
993 {
994 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
995 "\tCould not parse symbol file symversion=%s\n"),
996 refdes, inside);
997 } else {
998 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
999 "\tCould not parse symbol file symversion=\n"),
1000 refdes);
1001 }
1002 goto done;
1003 }
1004 inside_present = TRUE;
1005 } else {
1006 inside_present = FALSE; /* attribute not inside */
1007 }
1008
1009 if (outside)
1010 {
1011 outside_value = strtod(outside, &err_check);
1012 if (outside_value == 0 && outside == err_check)
1013 {
1014 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1015 "\tCould not parse attached symversion=%s\n"),
1016 refdes, outside);
1017 goto done;
1018 }
1019 outside_present = TRUE;
1020 } else {
1021 outside_present = FALSE; /* attribute not outside */
1022 }
1023
1024 #if DEBUG
1025 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object->name,
1026 inside_value, outside_value);
1027 #endif
1028
1029 /* symversion= is not present anywhere */
1030 if (!inside_present && !outside_present)
1031 {
1032 /* symbol is legacy and versioned okay */
1033 goto done;
1034 }
1035
1036 /* No symversion inside, but a version is outside, this is a weird case */
1037 if (!inside_present && outside_present)
1038 {
1039 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1040 "\tsymversion=%s attached to instantiated symbol, "
1041 "but no symversion= inside symbol file\n"),
1042 refdes, outside);
1043 goto done;
1044 }
1045
1046 /* inside & not outside is a valid case, means symbol in library is newer */
1047 /* also if inside_value is greater than outside_value, then symbol in */
1048 /* library is newer */
1049 if ((inside_present && !outside_present) ||
1050 ((inside_present && outside_present) && (inside_value > outside_value)))
1051 {
1052
1053 s_log_message(_("WARNING: Symbol version mismatch on refdes %s (%s):\n"
1054 "\tSymbol in library is newer than "
1055 "instantiated symbol\n"),
1056 refdes, object->complex_basename);
1057
1058 /* break up the version values into major.minor numbers */
1059 inside_major = floor(inside_value);
1060 inside_minor = inside_value - inside_major;
1061
1062 if (outside_present)
1063 {
1064 outside_major = floor(outside_value);
1065 outside_minor = outside_value - outside_major;
1066 } else {
1067 /* symversion was not attached to the symbol, set all to zero */
1068 outside_major = 0.0;
1069 outside_minor = 0.0;
1070 outside_value = 0.0;
1071 }
1072
1073 #if DEBUG
1074 printf("i: %f %f %f\n", inside_value, inside_major, inside_minor);
1075 printf("o: %f %f %f\n", outside_value, outside_major, outside_minor);
1076 #endif
1077
1078 if (inside_major > outside_major)
1079 {
1080 char* refdes_copy;
1081 s_log_message(_("\tMAJOR VERSION CHANGE (file %.3f, "
1082 "instantiated %.3f, %s)!\n"),
1083 inside_value, outside_value, refdes);
1084
1085 /* add the refdes to the major_changed_refdes GList */
1086 /* make sure refdes_copy is freed somewhere */
1087 refdes_copy = g_strconcat (refdes, " (",
1088 object->complex_basename,
1089 ")", NULL);
1090 toplevel->major_changed_refdes =
1091 g_list_append(toplevel->major_changed_refdes, refdes_copy);
1092
1093 /* don't bother checking minor changes if there are major ones*/
1094 goto done;
1095 }
1096
1097 if (inside_minor > outside_minor)
1098 {
1099 s_log_message(_("\tMinor version change (file %.3f, "
1100 "instantiated %.3f)\n"),
1101 inside_value, outside_value);
1102 }
1103
1104 goto done;
1105 }
1106
1107 /* outside value is greater than inside value, this is weird case */
1108 if ((inside_present && outside_present) && (outside_value > inside_value))
1109 {
1110 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1111 "\tInstantiated symbol is newer than "
1112 "symbol in library\n"),
1113 refdes);
1114 goto done;
1115 }
1116
1117 /* if inside_value and outside_value match, then symbol versions are okay */
1118
1119 done:
1120 g_free(inside);
1121 g_free(outside);
1122 g_free(refdes);
1123 }
1124
1125 /*! \brief Calculates the distance between the given point and the closest
1126 * point on an object within the complex object.
1127 *
1128 * \note When querying the distance to our child objects, we always
1129 * force treating them as solid filled.
1130 * We ignore the force_solid argument to this function.
1131 *
1132 * \param [in] object The complex OBJECT.
1133 * \param [in] x The x coordinate of the given point.
1134 * \param [in] y The y coordinate of the given point.
1135 * \param [in] force_solid If true, force treating the object as solid.
1136 * \return The shortest distance from the object to the point. If the
1137 * distance cannot be calculated, this function returns a really large
1138 * number (G_MAXDOUBLE). With an invalid parameter, this function returns
1139 * G_MAXDOUBLE.
1140 */
o_complex_shortest_distance(OBJECT * object,int x,int y,int force_solid)1141 double o_complex_shortest_distance (OBJECT *object, int x, int y,
1142 int force_solid)
1143 {
1144 double shortest_distance = G_MAXDOUBLE;
1145 double distance;
1146 int found_line_bounds = 0;
1147 BOX line_bounds;
1148 GList *iter;
1149
1150 g_return_val_if_fail (object->complex != NULL, G_MAXDOUBLE);
1151
1152 for (iter = object->complex->prim_objs;
1153 iter != NULL; iter= g_list_next (iter)) {
1154 OBJECT *obj = iter->data;
1155
1156 /* Collect the bounds of any lines and arcs in the symbol */
1157 if ((obj->type == OBJ_LINE || obj->type == OBJ_ARC) &&
1158 obj->w_bounds_valid) {
1159
1160 if (found_line_bounds) {
1161 line_bounds.lower_x = min (line_bounds.lower_x, obj->w_left);
1162 line_bounds.lower_y = min (line_bounds.lower_y, obj->w_top);
1163 line_bounds.upper_x = max (line_bounds.upper_x, obj->w_right);
1164 line_bounds.upper_y = max (line_bounds.upper_y, obj->w_bottom);
1165 } else {
1166 line_bounds.lower_x = obj->w_left;
1167 line_bounds.lower_y = obj->w_top;
1168 line_bounds.upper_x = obj->w_right;
1169 line_bounds.upper_y = obj->w_bottom;
1170 found_line_bounds = 1;
1171 }
1172 } else {
1173 distance = o_shortest_distance_full (obj, x, y, TRUE);
1174 shortest_distance = min (shortest_distance, distance);
1175 }
1176
1177 if (shortest_distance == 0.0)
1178 return shortest_distance;
1179 }
1180
1181 if (found_line_bounds) {
1182 distance = m_box_shortest_distance (&line_bounds, x, y, TRUE);
1183 shortest_distance = min (shortest_distance, distance);
1184 }
1185
1186 return shortest_distance;
1187 }
1188