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