Print the desired object's description.
[geda-gaf/berndj.git] / libgeda / src / o_complex_basic.c
blob0adab5e081b75cdbf3f6e68650bb676bcb9ccdbf
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21 /*! \file o_complex_basic.c
22 * \brief Functions for complex objects
24 * Complex objects are collections of primary objects.
27 #include <config.h>
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
38 #include "factory.h"
39 #include "libgeda_priv.h"
41 #ifdef HAVE_LIBDMALLOC
42 #include <dmalloc.h>
43 #endif
45 static int world_get_complex_bounds(OBJECT *complex,
46 int *left, int *top, int *right, int *bottom);
47 static void o_complex_recalc(OBJECT *o_current);
48 static void o_complex_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
49 double scale, GArray *unicode_table);
50 static void o_complex_grip_foreach(OBJECT *o,
51 gboolean (*fn)(OBJECT *o,
52 int grip_x, int grip_y,
53 enum grip_t whichone,
54 void *userdata),
55 void *userdata);
56 static void o_complex_embed(TOPLEVEL *toplevel, OBJECT *o, char yes);
57 static enum visit_result
58 o_complex_visit(OBJECT *o_current,
59 enum visit_result (*fn)(OBJECT *, void *), void *context,
60 enum visit_order order, int depth);
61 static OBJECT *o_complex_copy(TOPLEVEL *toplevel, OBJECT *o_current);
62 static OBJECT *o_complex_copy_embedded(TOPLEVEL *toplevel, OBJECT *o_current);
64 /*! Default setting for complex draw function. */
65 void (*complex_draw_func)() = NULL;
67 /*! \brief Return the bounds of the given object.
68 * \par Given an object, calculate the bounds coordinates.
69 * \param [in] toplevel The toplevel structure.
70 * \param [in] o_current The object to look the bounds for.
71 * \param [out] rleft pointer to the left coordinate of the object.
72 * \param [out] rtop pointer to the top coordinate of the object.
73 * \param [out] rright pointer to the right coordinate of the object.
74 * \param [out] rbottom pointer to the bottom coordinate of the object.
75 * \return If any bounds were found for the object
76 * \retval 0 No bound was found
77 * \retval 1 Bound was found
79 int world_get_single_object_bounds(OBJECT *o_current,
80 int *rleft, int *rtop, int *rright, int *rbottom)
82 g_return_val_if_fail(o_current, 0);
84 if (!o_current->w_bounds_valid) {
85 o_recalc_single_object(o_current);
86 if (!o_current->w_bounds_valid) {
87 return 0;
90 *rleft = o_current->w_left;
91 *rtop = o_current->w_top;
92 *rright = o_current->w_right;
93 *rbottom = o_current->w_bottom;
94 return 1;
97 struct get_bounds_context {
98 int found;
99 int *left, *top, *right, *bottom;
102 static enum visit_result get_bounds_one(OBJECT *o_current, void *userdata)
104 struct get_bounds_context *ctx = userdata;
105 int rleft, rtop, rright, rbottom;
107 if (world_get_single_object_bounds(o_current, &rleft, &rtop, &rright, &rbottom)) {
108 if (ctx->found) {
109 *ctx->left = min(*ctx->left, rleft);
110 *ctx->top = min(*ctx->top, rtop);
111 *ctx->right = max(*ctx->right, rright);
112 *ctx->bottom = max(*ctx->bottom, rbottom);
113 } else {
114 *ctx->left = rleft;
115 *ctx->top = rtop;
116 *ctx->right = rright;
117 *ctx->bottom = rbottom;
118 ctx->found = 1;
122 return VISIT_RES_OK;
125 /*! \brief Return the bounds of the given list of objects.
126 * \par Given a list of objects, calculate the bounds coordinates.
127 * \param [in] complex The list of objects to look the bounds for.
128 * \param [out] left pointer to the left coordinate of the object.
129 * \param [out] top pointer to the top coordinate of the object.
130 * \param [out] right pointer to the right coordinate of the object.
131 * \param [out] bottom pointer to the bottom coordinate of the object.
132 * \return If any bounds were found for the list of objects
133 * \retval 0 No bounds were found
134 * \retval 1 Bound was found
137 world_get_object_list_bounds(OBJECT *complex, enum list_kind kind,
138 int *left, int *top, int *right, int *bottom)
140 struct get_bounds_context context = {
141 .found = 0,
142 .left = left,
143 .top = top,
144 .right = right,
145 .bottom = bottom,
148 s_visit_list(complex, kind, &get_bounds_one, &context, VISIT_ANY, 1);
150 return context.found;
153 /*! \brief Return the bounds of the given GList of objects.
154 * \par Given a list of objects, calculate the bounds coordinates.
155 * \param [in] head The list of objects to look the bounds for.
156 * \param [out] left pointer to the left coordinate of the object.
157 * \param [out] top pointer to the top coordinate of the object.
158 * \param [out] right pointer to the right coordinate of the object.
159 * \param [out] bottom pointer to the bottom coordinate of the object.
160 * \return If any bounds were found for the list of objects
161 * \retval 0 No bounds were found
162 * \retval 1 Bound was found
164 int world_get_object_glist_bounds(GList const *head,
165 int *left, int *top, int *right, int *bottom)
167 GList const *s_current=NULL;
168 OBJECT *o_current=NULL;
169 int rleft, rtop, rright, rbottom;
170 int found = 0;
172 s_current = head;
174 /* Find the first object with bounds, and set the bounds variables, then expand as necessary. */
175 while ( s_current != NULL ) {
176 o_current = s_current->data;
177 g_assert (o_current != NULL);
178 if (world_get_single_object_bounds(o_current, &rleft, &rtop, &rright, &rbottom)) {
179 if ( found ) {
180 *left = min( *left, rleft );
181 *top = min( *top, rtop );
182 *right = max( *right, rright );
183 *bottom = max( *bottom, rbottom );
184 } else {
185 *left = rleft;
186 *top = rtop;
187 *right = rright;
188 *bottom = rbottom;
189 found = 1;
192 s_current = g_list_next (s_current);
194 return found;
197 /*! \brief Queries the bounds of a complex object.
198 * \par Function Description
199 * This function returns the bounding box of the complex object
200 * <B>object</B>.
202 * \param [in] complex The complex object.
203 * \param [out] left The leftmost edge of the bounding box (in
204 * world units).
205 * \param [out] top The upper edge of the bounding box (in
206 * world units).
207 * \param [out] right The rightmost edge of the bounding box (in
208 * world units).
209 * \param [out] bottom The bottom edge of the bounding box (in
210 * screen units).
211 * \return If any bounds were found for the list of objects
212 * \retval 0 No bounds were found
213 * \retval 1 Bound was found
215 static int world_get_complex_bounds(OBJECT *complex,
216 int *left, int *top, int *right, int *bottom)
218 g_return_if_fail (complex != NULL &&
219 (complex->type == OBJ_COMPLEX ||
220 complex->type == OBJ_PLACEHOLDER) &&
221 complex->complex != NULL);
223 return world_get_object_list_bounds(complex->complex->prim_objs, LIST_KIND_HEAD,
224 left, top, right, bottom);
227 /*! \brief create a new head object
228 * \par Function Description
229 * This function creates a <b>complex_head</b> OBJECT. This OBJECT
230 * is just a special empty object. This object is never modified.
232 * \return new head OBJECT
234 OBJECT *new_head(TOPLEVEL *toplevel)
236 OBJECT *new_node=NULL;
238 new_node = s_toplevel_new_object(toplevel, OBJ_HEAD, "complex_head");
240 return new_node;
243 char *o_complex_get_refdes(OBJECT const *o_current,
244 char const *default_value)
246 static GHashTable *objects_warned = NULL;
247 char *refdes;
249 /* Casting away const is okay because we don't use return_objects. */
250 refdes = o_attrib_search_name_single((OBJECT *) o_current, "refdes", NULL);
251 if (refdes == NULL) {
252 refdes = o_attrib_search_name_single((OBJECT *) o_current, "uref", NULL);
253 if (refdes) {
254 /* Warn once about a symbol using uref instead of refdes. */
255 if (!objects_warned) {
256 objects_warned = g_hash_table_new(&g_direct_hash, &g_direct_equal);
257 libgeda_caches = g_list_prepend(libgeda_caches, objects_warned);
259 if (!g_hash_table_lookup_extended(objects_warned,
260 GINT_TO_POINTER(o_current->sid),
261 NULL, NULL)) {
262 g_warning(_("Found uref=%s; please use refdes=\n"), refdes);
263 g_hash_table_replace(objects_warned,
264 GINT_TO_POINTER(o_current->sid),
265 GINT_TO_POINTER(o_current->sid));
267 } else {
268 refdes = default_value ? g_strdup(default_value) : NULL;
272 return refdes;
275 /*! \brief check whether an object is a attributes
276 * \par Function Description
277 * This function checks if an object should be promoted.
278 * An attribute object is promotable if it's promoted by default, or the user
279 * has configured it to promote an attribute.
281 * \param [in] toplevel The TOPLEVEL object
282 * \param [in] object The attribute object to check
283 * \return TRUE if the object is a eligible attribute, FALSE otherwise
285 static int o_complex_is_eligible_attribute (TOPLEVEL *toplevel, OBJECT *object)
287 char *name = NULL;
288 int promotableAttribute = FALSE;
290 g_return_val_if_fail(object->type == OBJ_TEXT, FALSE);
292 /* check list against attributes which can be promoted */
293 if (toplevel->always_promote_attributes != NULL) {
294 if (o_attrib_get_name_value(o_text_get_string(object), &name, NULL)) {
295 if (g_list_find_custom(toplevel->always_promote_attributes,
296 name, (GCompareFunc) strcmp) != NULL) {
297 /* Name of the attribute was in the always promote attributes list */
298 promotableAttribute = TRUE;
301 g_free(name);
302 if (promotableAttribute)
303 return TRUE;
307 /* object is invisible and we do not want to promote invisible text */
308 if (!o_text_printing_p(object) && toplevel->promote_invisible == FALSE)
309 return FALSE; /* attribute not eligible for promotion */
311 /* yup, attribute can be promoted */
312 return TRUE;
315 /*! \brief get the embedded state of an complex object
316 * \par Function Description
317 * Checks and returns the status of the complex object.
319 * \param o_current The object to check
320 * \return 1 if embedded, 0 otherwise
322 int o_complex_is_embedded(OBJECT const *o_current)
324 g_return_val_if_fail(o_current != NULL, 0);
326 if(o_current->complex == NULL)
327 return 0;
329 if (o_current->complex->is_embedded) {
330 return 1;
331 } else {
332 return 0;
336 static enum visit_result get_toplevel_attribs_one(OBJECT *o_current,
337 void *userdata)
339 GList **o_list = userdata;
341 if (o_current->type == OBJ_TEXT &&
342 o_current->attached_to == NULL &&
343 o_attrib_get_name_value(o_text_get_string(o_current), NULL, NULL)) {
344 *o_list = g_list_prepend(*o_list, o_current);
347 return VISIT_RES_OK;
350 /*! \brief Get a list of all toplevel attributes of and object list
352 * \par Function Description
353 * Returns a GList of all attribute OBJECTs
355 * \param [in] o_head The head of the object list
356 * \returns A GList of attribute OBJECTs
358 GList *o_complex_get_toplevel_attribs(OBJECT *o_head)
360 GList *o_list = NULL;
362 s_visit_list(o_head, LIST_KIND_HEAD, &get_toplevel_attribs_one, &o_list,
363 VISIT_DETERMINISTIC, 1);
364 o_list = g_list_reverse (o_list);
366 return o_list;
370 /*! \brief Get attributes eligible for promotion from inside a complex
372 * \par Function Description
373 * Returns a GList of OBJECTs which are eligible for promotion from
374 * within the passed complex OBJECT.
376 * If detach is TRUE, the function removes these attribute objects from
377 * the prim_objs of the complex. It detached, the returned OBJECTs are
378 * isolated from each other, having their next and prev pointers set to NULL.
380 * If detach is FALSE, the OBJECTs are left in place. Their next and prev
381 * pointers form part of the complex's prim_objs linked list.
383 * \param [in] toplevel The toplevel environment.
384 * \param [in] object The complex object being modified.
385 * \param [in] detach Should the attributes be detached?
386 * \returns A linked list of OBJECTs to promote.
388 GList *o_complex_get_promotable (TOPLEVEL *toplevel, OBJECT *object, int detach)
390 GList *promoted = NULL;
391 GList *attribs;
392 GList *iter;
393 OBJECT *tmp;
395 if (!toplevel->attribute_promotion) /* controlled through rc file */
396 return NULL;
398 attribs = o_complex_get_toplevel_attribs(object->complex->prim_objs);
400 for (iter = attribs; iter != NULL; iter = g_list_next (iter)) {
401 tmp = iter->data;
403 /* Is it an attribute we want to promote? */
404 if (!o_complex_is_eligible_attribute(toplevel, tmp))
405 continue;
407 if (detach) {
408 /* Remove and isolate it from the complex list */
409 s_basic_unlink_object(tmp);
410 tmp->complex_parent = NULL;
413 promoted = g_list_prepend (promoted, tmp);
416 g_list_free (attribs);
418 promoted = g_list_reverse (promoted);
419 return promoted;
422 static enum visit_result signal_add_object_one(OBJECT *o, void *userdata)
424 PAGE *page = userdata;
426 g_signal_emit_by_name(page, "add-object", o);
428 return VISIT_RES_OK;
431 /*! \brief Promote attributes from the passed OBJECT
433 * \par Function Description
434 * Promotes attributes from the passed OBJECT, linking them into the
435 * OBJECT linked list immediately prior to the passed OBJECT.
437 * \param [in] toplevel The toplevel environment.
438 * \param [in] page The PAGE on which \a object exists.
439 * \param [in] object The complex object whose attributes are being promoted.
441 void o_complex_promote_attribs(TOPLEVEL *toplevel, PAGE *page, OBJECT *object)
443 GList *promoted;
444 OBJECT *first_promoted, *last_promoted;
446 promoted = o_complex_get_promotable (toplevel, object, TRUE);
448 if (promoted == NULL)
449 return;
451 /* Link the promoted OBJECTs together */
452 o_glist_relink_objects (promoted);
454 first_promoted = promoted->data;
455 last_promoted = g_list_last (promoted)->data;
457 /* Insert promoted attributes before the complex in the object list */
458 s_basic_splice(object, first_promoted, last_promoted);
459 if (page) {
460 /* XXX Emit signal only after the objects are on the page. */
461 s_visit_list(first_promoted, LIST_KIND_REST, &signal_add_object_one, page,
462 VISIT_ANY, 1);
465 /* Attach promoted attributes to the original complex object */
466 o_attrib_attach_list (toplevel, promoted, object);
468 g_list_free (promoted);
472 /*! \brief Delete or hide promotable from the passed OBJECT
474 * \par Function Description
475 * Deletes or hides promotable attributes from the passed OBJECT.
476 * This is used when loading symbols during the load of a schematic from
477 * disk. The schematic will already contain local copies of symbol's
478 * promotable objects, so we delete or hide the symbol's copies.
480 * Deletion / hiding is dependant on the setting of
481 * toplevel->keep_invisible. If true, attributes eligible for
482 * promotion are kept in memory but flagged as invisible.
484 * \param [in] toplevel The toplevel environment.
485 * \param [in] object The complex object being altered.
487 void o_complex_remove_promotable_attribs (TOPLEVEL *toplevel, OBJECT *object)
489 GList *promotable, *iter;
491 promotable = o_complex_get_promotable (toplevel, object, FALSE);
493 if (promotable == NULL)
494 return;
496 for (iter = promotable; iter != NULL; iter = g_list_next (iter)) {
497 OBJECT *a_object = iter->data;
498 g_warn_if_fail(a_object->type == OBJ_TEXT);
499 if (toplevel->keep_invisible == TRUE) {
500 /* Hide promotable attributes */
501 o_text_change(a_object, NULL, INVISIBLE, LEAVE_NAME_VALUE_ALONE);
502 } else {
503 s_delete (toplevel, a_object); /* Delete promotable attributes */
507 g_list_free (promotable);
510 static void o_complex_notice_destroy(OBJECT *subject, void *userdata)
512 TOPLEVEL *toplevel = userdata;
514 /* Evict all slotted symbols. */
515 s_slot_destroy_complex(toplevel, subject);
517 /* Detach from any owning slot. */
518 s_slot_unlink(subject);
521 static enum visit_result set_complex_parent_one(OBJECT *tmp, void *userdata)
523 OBJECT *new_node = userdata;
525 tmp->complex_parent = new_node;
527 return VISIT_RES_OK;
530 /* Done */
531 /*! \brief
532 * \par Function Description
535 OBJECT *o_complex_new(TOPLEVEL *toplevel,
536 char type,
537 int color, int x, int y, int angle,
538 int mirror, const CLibSymbol *clib,
539 const gchar *basename,
540 int selectable)
542 OBJECT *new_node=NULL;
543 OBJECT *prim_objs=NULL;
544 OBJECT *new_prim_obj;
545 int save_adding_sel = 0;
546 int loaded_normally = FALSE;
548 gchar *buffer;
550 new_node = s_toplevel_new_object(toplevel, type, "complex");
551 g_signal_connect(G_OBJECT(new_node), "remove",
552 G_CALLBACK(o_complex_notice_destroy), toplevel);
554 if (clib != NULL) {
555 new_node->complex_basename = g_strdup (s_clib_symbol_get_name (clib));
556 } else {
557 new_node->complex_basename = g_strdup (basename);
560 new_node->color = color;
561 new_node->selectable = selectable;
563 new_node->complex = g_malloc(sizeof (COMPLEX));
565 new_node->complex->angle = angle;
566 new_node->complex->mirror = mirror;
567 new_node->complex->is_embedded = FALSE;
569 new_node->complex->x = x;
570 new_node->complex->y = y;
572 new_node->copy_func = &o_complex_copy;
573 new_node->bounds_recalc_func = o_complex_recalc;
574 new_node->draw_func = complex_draw_func;
575 new_node->psprint_func = &o_complex_print;
576 new_node->grip_foreach_func = &o_complex_grip_foreach;
577 new_node->embed_func = &o_complex_embed;
578 new_node->visit_func = &o_complex_visit;
580 /* this was at the beginning and p_complex was = to complex */
581 prim_objs = new_head(toplevel);
583 /* get the symbol data */
584 if (clib != NULL) {
585 buffer = s_clib_symbol_get_data (clib);
588 save_adding_sel = toplevel->ADDING_SEL;
589 toplevel->ADDING_SEL = 1; /* name is hack, don't want to */
591 if (clib == NULL || buffer == NULL) {
592 OBJECT *save_prim_objs;
593 char *not_found_text = NULL;
594 int left, right, top, bottom;
595 int x_offset, y_offset;
597 /* filename was NOT found */
598 loaded_normally = FALSE;
600 /* save the prim_objs pointer, since the following code modifies it */
601 save_prim_objs = prim_objs;
603 /* Put placeholder into object list. Changed by SDB on
604 * 1.19.2005 to fix problem that symbols were silently
605 * deleted by gattrib when RC files were messed up. */
606 new_node->type = OBJ_PLACEHOLDER;
608 /* Mark the origin of the missing component */
609 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
610 DETACHED_ATTRIBUTE_COLOR,
611 x - 50, y, x + 50, y);
612 prim_objs = s_basic_link_object(new_prim_obj, prim_objs);
613 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
614 DETACHED_ATTRIBUTE_COLOR,
615 x, y + 50, x, y - 50);
616 prim_objs = s_basic_link_object(new_prim_obj, prim_objs);
618 /* Add some useful text */
619 not_found_text =
620 g_strdup_printf (_("Component not found:\n %s"),
621 new_node->complex_basename);
622 new_prim_obj = o_text_new(toplevel,
623 OBJ_TEXT, DETACHED_ATTRIBUTE_COLOR,
624 x + NOT_FOUND_TEXT_X,
625 y + NOT_FOUND_TEXT_Y, LOWER_LEFT, 0,
626 not_found_text, 8,
627 VISIBLE, SHOW_NAME_VALUE);
628 prim_objs = s_basic_link_object(new_prim_obj, prim_objs);
629 g_free(not_found_text);
631 /* figure out where to put the hazard triangle */
632 world_get_text_bounds(prim_objs, &left, &top, &right, &bottom);
633 x_offset = (right - left) / 4;
634 y_offset = bottom - top + 100; /* 100 is just an additional offset */
636 /* TODO: set buffer to string constant that defines the triangle. */
637 /* add hazard triangle */
638 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
639 DETACHED_ATTRIBUTE_COLOR,
640 x + NOT_FOUND_TEXT_X + x_offset,
641 y + NOT_FOUND_TEXT_Y + y_offset,
642 x + NOT_FOUND_TEXT_X + x_offset + 600,
643 y + NOT_FOUND_TEXT_Y + y_offset);
644 o_set_line_options(new_prim_obj, END_ROUND, TYPE_SOLID, 50, -1, -1);
645 prim_objs = s_basic_link_object(new_prim_obj, prim_objs);
646 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
647 DETACHED_ATTRIBUTE_COLOR,
648 x + NOT_FOUND_TEXT_X + x_offset,
649 y + NOT_FOUND_TEXT_Y + y_offset,
650 x + NOT_FOUND_TEXT_X + x_offset + 300,
651 y + NOT_FOUND_TEXT_Y + y_offset + 500);
652 o_set_line_options(new_prim_obj, END_ROUND, TYPE_SOLID, 50, -1, -1);
653 prim_objs = s_basic_link_object(new_prim_obj, prim_objs);
654 new_prim_obj = o_line_new(toplevel, OBJ_LINE,
655 DETACHED_ATTRIBUTE_COLOR,
656 x + NOT_FOUND_TEXT_X + x_offset + 300,
657 y + NOT_FOUND_TEXT_Y + y_offset + 500,
658 x + NOT_FOUND_TEXT_X + x_offset + 600,
659 y + NOT_FOUND_TEXT_Y + y_offset);
660 o_set_line_options(new_prim_obj, END_ROUND, TYPE_SOLID, 50, -1, -1);
661 prim_objs = s_basic_link_object(new_prim_obj, prim_objs);
662 new_prim_obj = o_text_new(toplevel,
663 OBJ_TEXT, DETACHED_ATTRIBUTE_COLOR,
664 x + NOT_FOUND_TEXT_X + x_offset + 270,
665 y + NOT_FOUND_TEXT_Y + y_offset + 90,
666 LOWER_LEFT, 0, "!", 18,
667 VISIBLE, SHOW_NAME_VALUE);
668 prim_objs = s_basic_link_object(new_prim_obj, prim_objs);
669 prim_objs = save_prim_objs;
670 } else {
671 /* filename was found */
672 loaded_normally = TRUE;
674 /* add connections till translated */
675 o_read_buffer(toplevel, prim_objs, buffer, -1, new_node->complex_basename);
677 g_free (buffer);
679 toplevel->ADDING_SEL = save_adding_sel;
681 /* do not mirror/rotate/translate/connect the primitive objects if the
682 * component was not loaded via o_read
684 if (loaded_normally == TRUE) {
685 if (mirror) {
686 o_list_mirror_world(0, 0, prim_objs);
689 o_list_rotate_world(0, 0, angle, prim_objs);
690 o_list_translate_world(x, y, prim_objs);
693 new_node->complex->prim_objs = prim_objs;
695 /* XXX s_visit_list skips the OBJ_HEAD node. */
696 set_complex_parent_one(prim_objs, new_node);
697 s_visit_list(prim_objs, LIST_KIND_HEAD, &set_complex_parent_one, new_node,
698 VISIT_ANY, 1);
700 o_complex_recalc(new_node);
702 s_slot_setup_complex(toplevel, new_node);
704 return new_node;
707 /*! \brief create a new embedded object
708 * \par Function Description
709 * This function creates a new embedded object.
711 * \param [in] toplevel The TOPLEVEL object
712 * \param [in] type The type of the object (usually OBJ_COMLEX)
713 * \param [in] color The color of the object
714 * \param [in] x The x location of the complex object
715 * \param [in] y The y location of the complex object
716 * \param [in] angle The rotation angle
717 * \param [in] mirror The mirror status
718 * \param [in] basename The basic name the embedded was created of
719 * \param [in] selectable whether the object can be selected with the mouse
720 * \return a new complex object
722 OBJECT *o_complex_new_embedded(TOPLEVEL *toplevel,
723 char type, int color, int x, int y, int angle, int mirror,
724 const gchar *basename, int selectable)
726 OBJECT *prim_objs=NULL;
727 OBJECT *new_node=NULL;
729 new_node = s_toplevel_new_object(toplevel, type, "complex");
731 new_node->complex = g_malloc(sizeof (COMPLEX));
732 new_node->complex->x = x;
733 new_node->complex->y = y;
735 new_node->complex->angle = angle;
736 new_node->complex->mirror = mirror;
737 new_node->complex->is_embedded = TRUE;
739 new_node->complex_basename = g_strdup(basename);
741 new_node->color = color;
742 new_node->selectable = selectable;
744 new_node->copy_func = &o_complex_copy_embedded;
745 new_node->bounds_recalc_func = o_complex_recalc;
746 new_node->draw_func = complex_draw_func;
747 new_node->psprint_func = &o_complex_print;
748 new_node->grip_foreach_func = &o_complex_grip_foreach;
749 new_node->embed_func = &o_complex_embed;
750 new_node->visit_func = &o_complex_visit;
752 /* this was at the beginning and p_complex was = to complex */
753 prim_objs = new_head(toplevel);
754 new_node->complex->prim_objs = prim_objs;
756 set_complex_parent_one(prim_objs, new_node);
757 /* XXX Embedded prim_objs will get reparented in o_read_buffer. */
759 /* don't have to translate/rotate/mirror here at all since the */
760 /* object is in place */
761 return new_node;
764 /*! \brief update the visual boundaries of the complex object
765 * \par Function Description
766 * This function updates the boundaries of the object \a o_current.
768 * \param [in] toplevel The TOPLEVEL object
769 * \param [in] o_current The OBJECT to update
771 static void o_complex_recalc(OBJECT *o_current)
773 int left, right, top, bottom;
775 /* recalc routine Add this somewhere */
776 /* libhack */
777 /* o_recalc(o_current->complex);*/
779 if ((!o_current) || (o_current->type != OBJ_COMPLEX && o_current->type != OBJ_PLACEHOLDER))
780 return;
782 if (o_current->complex->x == -1 && o_current->complex->y == -1) {
783 /* Off-schematic components have no bounding box. */
784 return;
787 if (world_get_complex_bounds(o_current, &left, &top, &right, &bottom)) {
788 o_current->w_left = left;
789 o_current->w_top = top;
790 o_current->w_right = right;
791 o_current->w_bottom = bottom;
792 o_current->w_bounds_valid = TRUE;
796 /*! \brief read a complex object from a char buffer
797 * \par Function Description
798 * This function reads a complex object from the buffer \a buf.
800 * \param [in] toplevel The TOPLEVEL object
801 * \param [in] buf a text buffer (usually a line of a schematic file)
802 * \param [in] release_ver The release number gEDA
803 * \param [in] fileformat_ver a integer value of the file format
804 * \return The new object
806 * \todo Don't use fixed-length string for symbol basename
808 OBJECT *o_complex_read(TOPLEVEL *toplevel,
809 char buf[], unsigned int release_ver,
810 unsigned int fileformat_ver)
812 OBJECT *new_obj;
813 char type;
814 int x1, y1;
815 int angle;
817 char basename[256]; /* FIXME This is a hack */
819 int selectable;
820 int mirror;
822 sscanf(buf, "%c %d %d %d %d %d %s\n",
823 &type, &x1, &y1, &selectable, &angle, &mirror, basename);
825 switch(angle) {
826 case(0):
827 case(90):
828 case(180):
829 case(270):
830 break;
832 default:
833 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);
834 break;
837 switch(mirror) {
838 case(0):
839 case(1):
840 break;
842 default:
843 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);
844 break;
846 if (strncmp(basename, "EMBEDDED", 8) == 0) {
847 new_obj = o_complex_new_embedded(toplevel, type,
848 WHITE, x1, y1, angle, mirror,
849 basename + 8,
850 selectable);
851 } else {
852 const CLibSymbol *clib = s_clib_get_symbol_by_name (basename);
854 new_obj = o_complex_new(toplevel, type,
855 WHITE,
856 x1, y1,
857 angle, mirror, clib,
858 basename, selectable);
859 /* Delete or hide attributes eligible for promotion inside the complex */
860 o_complex_remove_promotable_attribs (toplevel, new_obj);
863 return new_obj;
866 /*! \brief Create a string representation of the complex object
867 * \par Function Description
868 * This function takes a complex \a object and return a string
869 * according to the file format definition.
871 * \param [in] object a complex OBJECT
872 * \return the string representation of the complex OBJECT
874 char *o_complex_save(OBJECT *object)
876 char *buf = NULL;
877 char *basename;
879 g_return_val_if_fail (object != NULL, NULL);
881 if ((object->type == OBJ_COMPLEX) || (object->type == OBJ_PLACEHOLDER)) {
882 basename = g_strdup_printf ("%s%s",
883 object->complex->is_embedded ? "EMBEDDED" : "",
884 object->complex_basename);
885 /* We force the object type to be output as OBJ_COMPLEX for both
886 * these object types. */
887 buf = g_strdup_printf("%c %d %d %d %d %d %s", OBJ_COMPLEX,
888 object->complex->x, object->complex->y,
889 object->selectable, object->complex->angle,
890 object->complex->mirror, basename);
891 g_free (basename);
894 return(buf);
897 /*! \brief move a complex object
898 * \par Function Description
899 * This function changes the position of a complex \a object.
901 * \param [in] dx The x-distance to move the object
902 * \param [in] dy The y-distance to move the object
903 * \param [in] object The complex OBJECT to be moved
905 void o_complex_translate_world(int dx, int dy, OBJECT *object)
907 g_return_if_fail (object != NULL &&
908 (object->type == OBJ_COMPLEX ||
909 object->type == OBJ_PLACEHOLDER));
911 object->complex->x = object->complex->x + dx;
912 object->complex->y = object->complex->y + dy;
914 o_list_translate_world(dx, dy, object->complex->prim_objs);
916 o_complex_recalc(object);
919 static void o_complex_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
920 double scale, GArray *unicode_table)
922 fprintf(fp, "gsave\n");
923 f_print_objects(toplevel, fp,
924 o_current->complex->prim_objs,
925 0, 0, scale,
926 unicode_table);
927 fprintf(fp, "grestore\n");
930 static void o_complex_grip_foreach(OBJECT *o,
931 gboolean (*fn)(OBJECT *o,
932 int grip_x, int grip_y,
933 enum grip_t whichone,
934 void *userdata),
935 void *userdata)
937 const GRIP complex_grips[] = {
939 .x = o->complex->x,
940 .y = o->complex->y,
941 .whichone = GRIP_COMPLEX_ORIGIN,
943 { .whichone = GRIP_NONE }
946 s_basic_grip_foreach_helper(o, complex_grips, fn, userdata);
949 static void o_complex_embed(TOPLEVEL *toplevel, OBJECT *o, char yes)
952 g_assert(o->type == OBJ_COMPLEX || o->type == OBJ_PLACEHOLDER);
954 if (!o_complex_is_embedded(o) == !yes) {
955 return;
958 if (yes) {
959 /* Set the embedded flag. */
960 o->complex->is_embedded = TRUE;
961 o->copy_func = &o_complex_copy_embedded;
963 s_log_message("Component [%s] has been embedded\n",
964 o->complex_basename);
965 } else {
966 const CLibSymbol *sym;
968 /* search for the symbol in the library */
969 sym = s_clib_get_symbol_by_name(o->complex_basename);
971 if (sym == NULL) {
972 /* symbol not found in the symbol library: signal an error */
973 s_log_message(_("Could not find component [%s], while trying to "
974 "unembed. Component is still embedded\n"),
975 o->complex_basename);
976 return;
979 /* clear the embedded flag */
980 o->complex->is_embedded = FALSE;
981 o->copy_func = &o_complex_copy;
983 s_log_message(_("Component [%s] has been successfully unembedded\n"),
984 o->complex_basename);
987 toplevel->page_current->CHANGED = 1; /* FIXME: Do this from an observer. */
990 static enum visit_result
991 o_complex_visit(OBJECT *o_current,
992 enum visit_result (*fn)(OBJECT *, void *), void *context,
993 enum visit_order order, int depth)
995 g_return_if_fail(o_current->type == OBJ_COMPLEX);
996 g_return_if_fail(o_current->complex->prim_objs != NULL);
998 return (s_visit_list(o_current->complex->prim_objs, LIST_KIND_HEAD, fn,
999 context, order, depth));
1002 /*! \brief create a copy of a complex object
1003 * \par Function Description
1004 * This function creates a copy of the complex object \a o_current.
1006 * \param [in] toplevel The TOPLEVEL object
1007 * \param [in] o_current The object that is copied
1008 * \return a new complex object
1010 static OBJECT *o_complex_copy(TOPLEVEL *toplevel, OBJECT *o_current)
1012 OBJECT *new_obj=NULL;
1013 int color;
1014 const CLibSymbol *clib = NULL;
1016 g_return_val_if_fail(o_current != NULL, NULL);
1018 if (o_current->saved_color == -1) {
1019 color = o_current->color;
1020 } else {
1021 color = o_current->saved_color;
1025 * FIXME If the component library path changes at runtime, then a copy
1026 * might not be an identical one. Maybe rather copy prim_objs directly?
1028 clib = s_clib_get_symbol_by_name (o_current->complex_basename);
1030 new_obj = o_complex_new (toplevel, o_current->type, color,
1031 o_current->complex->x, o_current->complex->y,
1032 o_current->complex->angle,
1033 o_current->complex->mirror,
1034 clib, o_current->complex_basename,
1035 o_current->selectable);
1037 /* Delete or hide attributes eligible for promotion inside the complex */
1038 o_complex_remove_promotable_attribs (toplevel, new_obj);
1040 o_attrib_slot_copy(toplevel, o_current, new_obj);
1042 /* deal with stuff that has changed */
1044 /* here you need to create a list of attributes which need to be
1045 * connected to the new list, probably make an attribute list and
1046 * fill it with sid's of the attributes */
1048 return new_obj;
1051 /*! \brief create a copy of a embedded complex object
1052 * \par Function Description
1053 * This function creates a copy of an embedded complex object \a o_current.
1055 * \param [in] toplevel The TOPLEVEL object
1056 * \param [in] o_current The object that is copied
1057 * \return a new complex object
1059 static OBJECT *o_complex_copy_embedded(TOPLEVEL *toplevel, OBJECT *o_current)
1061 OBJECT *new_obj=NULL;
1062 OBJECT *temp_list;
1063 int color;
1065 g_return_val_if_fail(o_current != NULL, NULL);
1067 if (o_current->saved_color == -1) {
1068 color = o_current->color;
1069 } else {
1070 color = o_current->saved_color;
1073 new_obj = o_complex_new_embedded (toplevel, o_current->type, color,
1074 o_current->complex->x, o_current->complex->y,
1075 o_current->complex->angle,
1076 o_current->complex->mirror,
1077 o_current->complex_basename,
1078 o_current->selectable);
1080 /* deal with stuff that has changed */
1082 temp_list = o_list_copy_all(toplevel,
1083 o_current->complex->prim_objs,
1084 new_obj->complex->prim_objs,
1085 toplevel->ADDING_SEL);
1087 new_obj->complex->prim_objs = return_head(temp_list);
1089 /* XXX s_visit_list skips the OBJ_HEAD node. */
1090 set_complex_parent_one(new_obj->complex->prim_objs, new_obj);
1091 s_visit_list(new_obj->complex->prim_objs, LIST_KIND_HEAD,
1092 &set_complex_parent_one, new_obj, VISIT_ANY, 1);
1094 o_complex_recalc(new_obj);
1096 /* here you need to create a list of attributes which need to be
1097 * connected to the new list, probably make an attribute list and
1098 * fill it with sid's of the attributes */
1100 return new_obj;
1103 static enum visit_result set_color_one(OBJECT *o_current, void *userdata)
1105 int *color = userdata;
1107 o_current->color = *color;
1109 return VISIT_RES_OK;
1112 /*! \brief Change the color of an object
1113 * \par Function Description
1114 * This function changes the the color of an object
1116 * \param [in] object The object
1117 * \param [in] color The new color
1119 * \note This function is mainly used to change the color of text objects
1121 void o_complex_set_color(OBJECT *object, int color)
1123 s_visit(object, &set_color_one, &color, VISIT_ANY, VISIT_DEPTH_FOREVER);
1126 static enum visit_result set_color_save_one(OBJECT *o_current, void *userdata)
1128 int const *color = userdata;
1130 o_current->saved_color = o_current->color;
1131 o_current->color = *color;
1133 return VISIT_RES_OK;
1136 /*! \todo Finish function documentation!!!
1137 * \brief
1138 * \par Function Description
1141 void o_complex_set_color_save(OBJECT *o_current, int color)
1143 s_visit(o_current, &set_color_save_one, &color, VISIT_ANY,
1144 VISIT_DEPTH_FOREVER);
1147 static enum visit_result unset_color_one(OBJECT *o_current, void *userdata)
1149 o_current->color = o_current->saved_color;
1150 o_current->saved_color = -1;
1152 return VISIT_RES_OK;
1155 /*! \todo Finish function documentation!!!
1156 * \brief
1157 * \par Function Description
1160 void o_complex_unset_color(OBJECT *o_current)
1162 s_visit(o_current, &unset_color_one, NULL, VISIT_ANY, VISIT_DEPTH_FOREVER);
1165 static enum visit_result set_color_save_only_one(OBJECT *o_current,
1166 void *userdata)
1168 int const *color = userdata;
1170 o_current->saved_color = *color;
1172 return VISIT_RES_OK;
1175 /*! \todo Finish function documentation!!!
1176 * \brief
1177 * \par Function Description
1180 void o_complex_set_saved_color_only(OBJECT *o_current, int color)
1182 s_visit(o_current, &set_color_save_only_one, &color, VISIT_ANY,
1183 VISIT_DEPTH_FOREVER);
1186 /*! \todo Finish function documentation!!!
1187 * \brief
1188 * \par Function Description
1191 void o_complex_rotate_world(int centerx, int centery,
1192 int angle, OBJECT *object)
1194 int x, y;
1195 int newx, newy;
1197 g_return_if_fail (object!=NULL);
1198 g_return_if_fail ((object->type == OBJ_COMPLEX) ||
1199 (object->type == OBJ_PLACEHOLDER));
1201 x = object->complex->x + (-centerx);
1202 y = object->complex->y + (-centery);
1204 rotate_point_90(x, y, angle, &newx, &newy);
1206 x = newx + (centerx);
1207 y = newy + (centery);
1209 o_complex_translate_world(-object->complex->x,
1210 -object->complex->y, object);
1211 o_list_rotate_world(0, 0, angle, object->complex->prim_objs);
1213 object->complex->x = 0;
1214 object->complex->y = 0;
1216 o_complex_translate_world(x, y, object);
1218 object->complex->angle = ( object->complex->angle + angle ) % 360;
1222 /*! \todo Finish function documentation!!!
1223 * \brief
1224 * \par Function Description
1227 void o_complex_mirror_world(int world_centerx, int world_centery,
1228 OBJECT *object)
1230 int x, y;
1232 g_return_if_fail( object != NULL );
1233 g_return_if_fail( (object->type == OBJ_COMPLEX ||
1234 object->type == OBJ_PLACEHOLDER) );
1235 g_return_if_fail( object->complex != NULL );
1237 x = 2 * world_centerx - object->complex->x;
1238 y = object->complex->y;
1240 o_complex_translate_world(-object->complex->x,
1241 -object->complex->y, object);
1243 o_list_mirror_world(0, 0, object->complex->prim_objs );
1245 switch(object->complex->angle) {
1246 case(90):
1247 object->complex->angle = 270;
1248 break;
1250 case(270):
1251 object->complex->angle = 90;
1252 break;
1256 object->complex->mirror = !object->complex->mirror;
1258 o_complex_translate_world(x, y, object);
1261 struct return_pin_context {
1262 char const *pin;
1263 OBJECT *found;
1266 static enum visit_result return_pin_one(OBJECT *o_current, void *userdata)
1268 struct return_pin_context *ctx = userdata;
1269 OBJECT *found;
1271 switch(o_current->type) {
1272 case(OBJ_PIN):
1273 /* Search for the pin making sure that */
1274 /* any found attribute starts with "pinnumber" */
1275 found = o_attrib_search_attrib_value(o_current->attribs, ctx->pin,
1276 "pinnumber", 0);
1277 if (found) {
1278 if (GEDA_DEBUG) {
1279 printf("%s: %s\n", found->name, o_text_get_string(found));
1281 ctx->found = o_current;
1282 return VISIT_RES_EARLY;
1284 break;
1287 return VISIT_RES_OK;
1290 /*! \brief search the pin with a given pin number
1291 * \par Function Description
1292 * This function searches a pin object inside the complex \a object.
1293 * The pin name is a character string \a pin.
1295 * \param object The complex object
1296 * \param pin The pin number (string) to find
1297 * \return a pin object if found, NULL otherwise
1299 OBJECT *o_complex_return_pin_object(OBJECT *object, char *pin)
1301 struct return_pin_context ctx = {
1302 .pin = pin,
1303 .found = NULL,
1307 * It is a semantic error to have multiple pins with the same pinnumber, so
1308 * VISIT_ANY is ok here.
1310 s_visit_object(object, &return_pin_one, &ctx, VISIT_ANY, 1);
1312 return ctx.found;
1315 /*! \brief check the symversion of a complex object
1316 * \par Function Description
1317 * This function compares the symversion of a symbol with it's
1318 * earlier saved symversion in a schematic.
1319 * Major symversion changes are added to the toplevel object
1320 * (toplevel->major_changed_refdes), minor changes are reported
1321 * to the messaging system.
1323 * \param toplevel The TOPLEVEL object
1324 * \param object The complex OBJECT
1326 void
1327 o_complex_check_symversion(TOPLEVEL *toplevel, OBJECT const *object)
1329 char *inside = NULL;
1330 char *outside = NULL;
1331 char *refdes = NULL;
1332 double inside_value = -1.0;
1333 double outside_value = -1.0;
1334 char *err_check = NULL;
1335 int inside_present = FALSE;
1336 int outside_present = FALSE;
1337 double inside_major, inside_minor;
1338 double outside_major, outside_minor;
1340 g_return_if_fail (object != NULL);
1341 g_return_if_fail ((object->type == OBJ_COMPLEX ||
1342 object->type == OBJ_PLACEHOLDER));
1343 g_return_if_fail (object->complex != NULL);
1345 /* first look on the inside for the symversion= attribute */
1346 inside = o_attrib_search_object(object, "symversion", 0);
1348 /* now look for the symversion= attached to object */
1349 outside = o_attrib_search_attrib_name(object->attribs, "symversion", 0);
1351 /* get the uref for future use */
1352 refdes = o_attrib_search_attrib_name(object->attribs, "refdes", 0);
1353 if (!refdes)
1355 refdes = g_strdup ("unknown");
1358 if (inside)
1360 inside_value = strtod(inside, &err_check);
1361 if (inside_value == 0 && inside == err_check)
1363 if (inside)
1365 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1366 "\tCould not parse symbol file symversion=%s\n"),
1367 refdes, inside);
1368 } else {
1369 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1370 "\tCould not parse symbol file symversion=\n"),
1371 refdes);
1373 goto done;
1375 inside_present = TRUE;
1376 } else {
1377 inside_present = FALSE; /* attribute not inside */
1380 if (outside)
1382 outside_value = strtod(outside, &err_check);
1383 if (outside_value == 0 && outside == err_check)
1385 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1386 "\tCould not parse attached symversion=%s\n"),
1387 refdes, outside);
1388 goto done;
1390 outside_present = TRUE;
1391 } else {
1392 outside_present = FALSE; /* attribute not outside */
1395 if (GEDA_DEBUG) {
1396 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object->name,
1397 inside_value, outside_value);
1400 /* symversion= is not present anywhere */
1401 if (!inside_present && !outside_present)
1403 /* symbol is legacy and versioned okay */
1404 goto done;
1407 /* No symversion inside, but a version is outside, this is a weird case */
1408 if (!inside_present && outside_present)
1410 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1411 "\tsymversion=%s attached to instantiated symbol, "
1412 "but no symversion= inside symbol file\n"),
1413 refdes, outside);
1414 goto done;
1417 /* inside & not outside is a valid case, means symbol in library is newer */
1418 /* also if inside_value is greater than outside_value, then symbol in */
1419 /* library is newer */
1420 if ((inside_present && !outside_present) ||
1421 ((inside_present && outside_present) && (inside_value > outside_value)))
1424 s_log_message(_("WARNING: Symbol version mismatch on refdes %s (%s):\n"
1425 "\tSymbol in library is newer than "
1426 "instantiated symbol\n"),
1427 refdes, object->complex_basename);
1429 /* break up the version values into major.minor numbers */
1430 inside_major = floor(inside_value);
1431 inside_minor = inside_value - inside_major;
1433 if (outside_present)
1435 outside_major = floor(outside_value);
1436 outside_minor = outside_value - outside_major;
1437 } else {
1438 /* symversion was not attached to the symbol, set all to zero */
1439 outside_major = 0.0;
1440 outside_minor = 0.0;
1441 outside_value = 0.0;
1444 if (GEDA_DEBUG) {
1445 printf("i: %f %f %f\n", inside_value, inside_major, inside_minor);
1446 printf("o: %f %f %f\n", outside_value, outside_major, outside_minor);
1449 if (inside_major > outside_major)
1451 char* refdes_copy;
1452 s_log_message(_("\tMAJOR VERSION CHANGE (file %.3f, "
1453 "instantiated %.3f, %s)!\n"),
1454 inside_value, outside_value, refdes);
1456 /* add the refdes to the major_changed_refdes GList */
1457 /* make sure refdes_copy is freed somewhere */
1458 refdes_copy = g_strconcat (refdes, " (",
1459 object->complex_basename,
1460 ")", NULL);
1461 toplevel->major_changed_refdes =
1462 g_list_append(toplevel->major_changed_refdes, refdes_copy);
1464 /* don't bother checking minor changes if there are major ones*/
1465 goto done;
1468 if (inside_minor > outside_minor)
1470 s_log_message(_("\tMinor version change (file %.3f, "
1471 "instantiated %.3f)\n"),
1472 inside_value, outside_value);
1475 goto done;
1478 /* outside value is greater than inside value, this is weird case */
1479 if ((inside_present && outside_present) && (outside_value > inside_value))
1481 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1482 "\tInstantiated symbol is newer than "
1483 "symbol in library\n"),
1484 refdes);
1485 goto done;
1488 /* if inside_value and outside_value match, then symbol versions are okay */
1490 done:
1491 g_free(inside);
1492 g_free(outside);
1493 g_free(refdes);
1496 /*! \brief Calculates the distance between the given point and the closest
1497 * point on an object within the complex object.
1499 * \param [in] complex The complex of the OBJECT
1500 * \param [in] x The x coordinate of the given point.
1501 * \param [in] y The y coordinate of the given point.
1502 * \return The shortest distance from the object to the point. If the
1503 * distance cannot be calculated, this function returns a really large
1504 * number (G_MAXDOUBLE). With an invalid parameter, this function returns
1505 * G_MAXDOUBLE.
1507 gdouble o_complex_shortest_distance(OBJECT *o_current, gint x, gint y)
1509 gdouble distance;
1510 gdouble shortest_distance = G_MAXDOUBLE;
1511 OBJECT *temp;
1513 g_return_val_if_fail(o_current->complex, G_MAXDOUBLE);
1515 temp = o_current->complex->prim_objs;
1517 if (temp != NULL) {
1518 temp = temp->next;
1521 while (temp != NULL) {
1522 distance = o_shortest_distance(temp, x, y);
1524 shortest_distance = min(shortest_distance, distance);
1526 temp = temp->next;
1529 return shortest_distance;