File o_complex_basic.c
**********************

File header
===========

     <<o_complex_basic.c : *>>=
     <<o_complex_basic.c : copyright and license -- o_complex_basic.c - copyright and license>>

     /* DO NOT read or edit this file ! Use ../noweb/o_complex_basic.nw instead */

     <<o_complex_basic.c : include directives -- o_complex_basic.c - include directives>>
     <<o_complex_basic.c : get_complex_bounds() -- o_complex_basic.c - get_complex_bounds()>>
     <<o_complex_basic.c : get_complex_bounds_selection() -- o_complex_basic.c - get_complex_bounds_selection()>>
     <<o_complex_basic.c : world_get_complex_bounds() -- o_complex_basic.c - world_get_complex_bounds()>>
     <<o_complex_basic.c : add_head() -- o_complex_basic.c - add_head()>>
     <<o_complex_basic.c : o_complex_is_eligible_attribute() -- o_complex_basic.c - o_complex_is_eligible_attribute()>>
     <<o_complex_basic.c : o_complex_is_embedded() -- o_complex_basic.c - o_complex_is_embedded()>>           /* Done */
     <<o_complex_basic.c : o_complex_add() -- o_complex_basic.c - o_complex_add()>>
     <<o_complex_basic.c : o_complex_add_embedded() -- o_complex_basic.c - o_complex_add_embedded()>>
     <<o_complex_basic.c : o_complex_recalc() -- o_complex_basic.c - o_complex_recalc()>>
     <<o_complex_basic.c : o_complex_read() -- o_complex_basic.c - o_complex_read()>>
     <<o_complex_basic.c : o_complex_save() -- o_complex_basic.c - o_complex_save()>>
     <<o_complex_basic.c : o_complex_set_filename() -- o_complex_basic.c - o_complex_set_filename()>>
     <<o_complex_basic.c : o_complex_free_filename() -- o_complex_basic.c - o_complex_free_filename()>>
     <<o_complex_basic.c : o_complex_translate() -- o_complex_basic.c - o_complex_translate()>>
     <<o_complex_basic.c : o_complex_world_translate() -- o_complex_basic.c - o_complex_world_translate()>>
     <<o_complex_basic.c : o_complex_world_translate_toplevel() -- o_complex_basic.c - o_complex_world_translate_toplevel()>>
     <<o_complex_basic.c : o_complex_copy() -- o_complex_basic.c - o_complex_copy()>>
     <<o_complex_basic.c : o_complex_copy_embedded() -- o_complex_basic.c - o_complex_copy_embedded()>>
     <<o_complex_basic.c : o_complex_delete() -- o_complex_basic.c - o_complex_delete()>>
     <<o_complex_basic.c : o_complex_set_color() -- o_complex_basic.c - o_complex_set_color()>>
     <<o_complex_basic.c : o_complex_set_color_single() -- o_complex_basic.c - o_complex_set_color_single()>>
     <<o_complex_basic.c : o_complex_set_color_save() -- o_complex_basic.c - o_complex_set_color_save()>>
     <<o_complex_basic.c : o_complex_unset_color() -- o_complex_basic.c - o_complex_unset_color()>>
     <<o_complex_basic.c : o_complex_unset_color_single() -- o_complex_basic.c - o_complex_unset_color_single()>>
     <<o_complex_basic.c : o_complex_set_saved_color_only() -- o_complex_basic.c - o_complex_set_saved_color_only()>>
     <<o_complex_basic.c : o_complex_return_nth_pin() -- o_complex_basic.c - o_complex_return_nth_pin()>>
     <<o_complex_basic.c : o_complex_rotate_lowlevel() -- o_complex_basic.c - o_complex_rotate_lowlevel()>>
     <<o_complex_basic.c : o_complex_mirror_lowlevel() -- o_complex_basic.c - o_complex_mirror_lowlevel()>>
     <<o_complex_basic.c : o_complex_return_pin_object() -- o_complex_basic.c - o_complex_return_pin_object()>>
     <<o_complex_basic.c : o_complex_check_symversion() -- o_complex_basic.c - o_complex_check_symversion()>>

     <<o_complex_basic.c : copyright and license>>=
     /* gEDA - GPL Electronic Design Automation
      * libgeda - gEDA's library
      * Copyright (C) 1998-2000 Ales V. Hvezda
      *
      * This program is free software; you can redistribute it and/or modify
      * it under the terms of the GNU General Public License as published by
      * the Free Software Foundation; either version 2 of the License, or
      * (at your option) any later version.
      *
      * This program is distributed in the hope that it will be useful,
      * but WITHOUT ANY WARRANTY; without even the implied warranty of
      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      * GNU General Public License for more details.
      *
      * You should have received a copy of the GNU General Public License
      * along with this program; if not, write to the Free Software
      * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
      */

     <<o_complex_basic.c : include directives>>=
     #include <config.h>

     #include <stdio.h>
     #include <math.h>
     #ifdef HAVE_STRING_H
     #include <string.h>
     #endif
     #ifdef HAVE_UNISTD_H
     #include <unistd.h>
     #endif

     #include <gtk/gtk.h>
     #include <libguile.h>

     #include "defines.h"
     #include "struct.h"
     #include "globals.h"
     #include "o_types.h"
     #include "colors.h"
     #include "funcs.h"

     #include "../include/prototype.h"

     #ifdef HAVE_LIBDMALLOC
     #include <dmalloc.h>
     #endif

Function get_complex_bounds()
=============================

 -- Function: get_complex_bounds w_current complex left top right bottom

     <<o_complex_basic.c : get_complex_bounds()>>=
     void
     get_complex_bounds(TOPLEVEL *w_current, OBJECT *complex,
                        int *left, int *top, int *right, int *bottom)
     {
       OBJECT *o_current=NULL;
       int rleft, rtop, rright, rbottom;

       *left = rleft = 999999;
       *top = rtop = 9999999;
       *right = rright = 0;
       *bottom = rbottom = 0;

       o_current = complex;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
             get_line_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_NET):
             /* same as a line (diff name)*/
             get_net_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_BUS):
             /* same as a line (diff name)*/
             get_bus_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_BOX):
             get_box_bounds(w_current, o_current->box, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_PICTURE):
     #ifndef HAS_GTK12
             get_picture_bounds(w_current, o_current->picture, &rleft, &rtop, &rright, &rbottom);
     #endif
             break;

           case(OBJ_CIRCLE):
             get_circle_bounds(w_current, o_current->circle, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             /* recursive objects ?*/
             get_complex_bounds(w_current, o_current->complex->prim_objs, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_TEXT):
             /* only do bounding boxes for visible or doing show_hidden_text*/
             /* you might lose some attrs though */
             if (o_current->visibility == VISIBLE ||
                 (o_current->visibility == INVISIBLE && w_current->show_hidden_text)) {
               get_text_bounds(w_current, o_current, &rleft, &rtop, &rright, &rbottom);
             }
             break;

           case(OBJ_PIN):
             get_pin_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_ARC):
             get_arc_bounds(w_current, o_current, &rleft, &rtop, &rright, &rbottom);
             break;

           default:
             break;
         }

         if (rleft < *left) *left = rleft;
         if (rtop < *top) *top = rtop;
         if (rright > *right) *right = rright;
         if (rbottom > *bottom) *bottom = rbottom;

         o_current=o_current->next;
       }

     }

          Defines :
          get_complex_bounds,
           -- o_complex_basic.c - o_complex_world_translate_toplevel(),
           -- o_complex_basic.c - o_complex_recalc(),
           -- o_complex_basic.c - get_complex_bounds_selection().

     }

Function get_complex_bounds_selection()
=======================================

 -- Function: get_complex_bounds_selection w_current head left top right bottom

     <<o_complex_basic.c : get_complex_bounds_selection()>>=
     void
     get_complex_bounds_selection(TOPLEVEL *w_current, SELECTION *head,
                                  int *left, int *top, int *right, int *bottom)
     {
       SELECTION *s_current=NULL;
       OBJECT *o_current=NULL;
       int rleft, rtop, rright, rbottom;

       *left = rleft = 999999;
       *top = rtop = 9999999;
       *right = rright = 0;
       *bottom = rbottom = 0;

       s_current = head;

       while ( s_current != NULL ) {

         o_current = s_current->selected_object;

         if (!o_current) {
           fprintf(stderr, "Got NULL in get_complex_bounds_selection\n");
           exit(-1);
         }

         switch(o_current->type) {
           case(OBJ_LINE):
             get_line_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_NET):
             /* same as a line (diff name)*/
             get_net_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_BUS):
             /* same as a line (diff name)*/
             get_bus_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_BOX):
             get_box_bounds(w_current, o_current->box, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_PICTURE):
     #ifndef HAS_GTK12
             get_picture_bounds(w_current, o_current->picture, &rleft, &rtop, &rright, &rbottom);
     #endif
             break;

           case(OBJ_CIRCLE):
             get_circle_bounds(w_current, o_current->circle, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             /* recursive objects ?*/
             get_complex_bounds(w_current, o_current->complex->prim_objs, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_TEXT):
             /* only do bounding boxes for visible or doing show hidden text */
             /* you might lose some attrs though */
             if (o_current->visibility == VISIBLE ||
                 (o_current->visibility == INVISIBLE && w_current->show_hidden_text)) {
               get_text_bounds(w_current, o_current, &rleft, &rtop, &rright, &rbottom);
             }
             break;

           case(OBJ_PIN):
             get_pin_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_ARC):
             get_arc_bounds(w_current, o_current, &rleft, &rtop, &rright, &rbottom);
             break;

           default:
             break;
         }

         if (rleft < *left) *left = rleft;
         if (rtop < *top) *top = rtop;
         if (rright > *right) *right = rright;
         if (rbottom > *bottom) *bottom = rbottom;

         s_current=s_current->next;
       }

     }

          Defines :
          .

          Uses :
          get_complex_bounds,  -- o_complex_basic.c - get_complex_bounds().

     }

Function world_get_complex_bounds()
===================================

 -- Function: world_get_complex_bounds w_current complex left top right bottom

     <<o_complex_basic.c : world_get_complex_bounds()>>=
     void
     world_get_complex_bounds(TOPLEVEL *w_current, OBJECT *complex,
                              int *left, int *top, int *right, int *bottom)
     {
       OBJECT *o_current=NULL;
       int rleft, rtop, rright, rbottom;

       *left = rleft = w_current->init_right;
       *top = rtop = w_current->init_bottom;;
       *right = rright = 0;
       *bottom = rbottom = 0;

       o_current = complex;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
             world_get_line_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_NET):
             /* same as a line (diff name)*/
             world_get_net_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_BUS):
             /* same as a line (diff name)*/
             world_get_bus_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_BOX):
             world_get_box_bounds(w_current, o_current->box, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_PICTURE):
     #ifndef HAS_GTK12
             world_get_picture_bounds(w_current, o_current->picture, &rleft, &rtop, &rright, &rbottom);
     #endif
             break;

           case(OBJ_CIRCLE):
             world_get_circle_bounds(w_current, o_current->circle, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             /* recursive objects ?*/
             world_get_complex_bounds(w_current, o_current->complex->prim_objs, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_TEXT):
             /* only do bounding boxes for visible or doing show hidden text */
             /* you might lose some attrs though */
             if (o_current->visibility == VISIBLE ||
                 (o_current->visibility == INVISIBLE && w_current->show_hidden_text)) {
               world_get_text_bounds(w_current, o_current, &rleft, &rtop, &rright, &rbottom);
             }
             break;

           case(OBJ_PIN):
             world_get_pin_bounds(w_current, o_current->line, &rleft, &rtop, &rright, &rbottom);
             break;

           case(OBJ_ARC):
             world_get_arc_bounds(w_current, o_current, &rleft, &rtop, &rright, &rbottom);
             break;

           default:
             break;
         }

         if (rleft < *left) *left = rleft;
         if (rtop < *top) *top = rtop;
         if (rright > *right) *right = rright;
         if (rbottom > *bottom) *bottom = rbottom;

         o_current=o_current->next;
       }

     }

          Defines :
          .

     }

Function add_head()
===================

 -- Function: add_head

     <<o_complex_basic.c : add_head()>>=
     OBJECT *
     add_head(void)
     {
       OBJECT *new_node=NULL;

       new_node = s_basic_init_object("complex_head");

       new_node->type = OBJ_HEAD; /* make this of head type hack */

       /* don't need to do this for head nodes */
       /* ret = (OBJECT *) s_basic_link_object(new_node, NULL);*/
       return(new_node);
     }

          Defines :
          add_head,
           -- o_complex_basic.c - o_complex_add_embedded(),
           -- o_complex_basic.c - o_complex_add().

     }

Function o_complex_is_eligible_attribute()
==========================================

 -- Function: o_complex_is_eligible_attribute object promote_invisible

     <<o_complex_basic.c : o_complex_is_eligible_attribute()>>=
     /* The promote_invisible flag is either TRUE or FALSE */
     /* It controls if invisible text is promoted (TRUE) or not (FALSE) */
     int
     o_complex_is_eligible_attribute (OBJECT *object, int promote_invisible)
     {
       char *ptr;

       if (object->type != OBJ_TEXT || object->attribute || object->attached_to)
       {
         return 0; /* not a text item or is already attached */
       }

       /* Make sure text item is an attribute */
       ptr = strchr(object->text->string, '=');
       if (!ptr || ptr[1] == '\0' || ptr[1] == ' ')
       {
         return 0;  /* not an attribute */
       }

       /* always promote symversion= attribute, even if it is invisible */
       if (strncmp(object->text->string, "symversion=", 11) == 0)
       {
         return 1;
       }

       /* object is invisible and we do not want to promote invisible text */
       if (object->visibility == INVISIBLE && promote_invisible == FALSE)
       {
         return 0; /* attribute not eligible for promotion */
       }

       /* yup, attribute can be promoted */
       return 1;
     }

          Defines :
          o_complex_is_eligible_attribute,
           -- o_complex_basic.c - o_complex_add().

     }

Function o_complex_is_embedded()
================================

 -- Function: o_complex_is_embedded o_current
     This function returns 1 - true - if the o_current pointed object is an embedded complex, 0 - false - otherwise. It checks wether o_current is a COMPLEX by comparing the complex entry of the OBJECT structure to NULL. If it is NULL, it returns 0.

     <<o_complex_basic.c : o_complex_is_embedded()>>=
     int
     o_complex_is_embedded(OBJECT *o_current)
     {
       if(o_current->complex == NULL)
       return 0;

       if (o_current->complex_clib &&
           strncmp(o_current->complex_clib, "EMBEDDED", 8) == 0) {
         return 1;
       } else {
         return 0;
       }

     }

          Defines :
          .

     }

Function o_complex_add()
========================

 -- Function: o_complex_add w_current object_list type color x y angle mirror clib basename selectable attribute_promotion

     <<o_complex_basic.c : o_complex_add()>>=
     OBJECT *
     o_complex_add(TOPLEVEL *w_current, OBJECT *object_list, char type,
                   int color, int x, int y, int angle, int mirror, char *clib,
                   char *basename, int selectable, int attribute_promotion)
     {
       OBJECT *new_node=NULL;
       OBJECT *prim_objs=NULL;
       OBJECT *temp_tail=NULL;
       OBJECT *temp_parent=NULL;
       int save_adding_sel = 0;
       int loaded_normally = FALSE;

       char *filename;

       new_node = s_basic_init_object("complex");
       new_node->type = type;

       new_node->complex_basename = strdup(basename);
       if (clib)
         new_node->complex_clib = strdup(clib);
       else
         new_node->complex_clib = NULL;

       new_node->color = color;

       new_node->complex = (COMPLEX *) malloc(sizeof(COMPLEX));

       new_node->complex->angle = angle;
       new_node->complex->mirror = mirror;

       new_node->complex->x = x;
       new_node->complex->y = y;
       WORLDtoSCREEN(w_current, x, y,
                     &new_node->complex->screen_x,
                     &new_node->complex->screen_y);

       /* TODO: questionable caste? */
       new_node->draw_func = (void *) complex_draw_func;

       if (selectable) {
         new_node->sel_func = (void *) select_func;
       } else {
         new_node->sel_func = NULL;
       }

       /* this was at the beginning and p_complex was = to complex */
       prim_objs = (OBJECT *) add_head();

       /* set the parent field now */
       prim_objs->complex_parent = new_node;

       /* is the bit with the temp and object_tail needed? */
       /* I don't like this at all hack */
       /* careful whenever you select, you get a read from disk */
       /* for the objects, UGG! there foreattribs are being copied */
       /* you need to override them if there are attached ones */
       /* on the main list */
       temp_tail = w_current->page_current->object_tail;
       temp_parent = w_current->page_current->object_parent;
       w_current->page_current->object_parent = prim_objs;
       /* reason this works is because it has a head, see add_head above */

       /* check for validity */
       if (clib && basename)
         filename = g_strdup_printf("%s%c%s", clib, G_DIR_SEPARATOR, basename);
       else
         filename = g_strdup("unknown");

       save_adding_sel = w_current->ADDING_SEL;
       w_current->ADDING_SEL = 1;    /* name is hack, don't want to */

       if (access(filename, R_OK)) {

         OBJECT *save_prim_objs;
         char *not_found_text = NULL;
         int left, right, top, bottom;
         int x_offset, y_offset;

         if (clib == NULL) {
           s_log_message("Component library was not found or specified\n");
         } else if (basename == NULL) {
           s_log_message("Basename (component) was not found or specified\n");
         } else {
           s_log_message("Could not open symbol file [%s]\n", filename);
         }

         /* filename was NOT found */
         loaded_normally = FALSE;

         /* save the prim_objs pointer, since the following code modifies it */
         save_prim_objs = prim_objs;

         /* Put placeholder into object list.  Changed by SDB on
          * 1.19.2005 to fix problem that symbols were silently
          * deleted by gattrib when RC files were messed up.  */
         new_node->type = OBJ_PLACEHOLDER;

         /* Mark the origin of the missing component */
         prim_objs = o_line_add(w_current, prim_objs, OBJ_LINE,
                                DETACHED_ATTRIBUTE_COLOR,
                                x - 50, y, x + 50, y);
         prim_objs = o_line_add(w_current, prim_objs, OBJ_LINE,
                                DETACHED_ATTRIBUTE_COLOR,
                                x, y + 50, x, y - 50);

         /* Add some useful text */
         not_found_text =
           g_strdup_printf ("Component not found:\n %s", basename);
         prim_objs = o_text_add(w_current, prim_objs,
                                OBJ_TEXT, DETACHED_ATTRIBUTE_COLOR,
                                x + NOT_FOUND_TEXT_X,
                                y + NOT_FOUND_TEXT_Y, LOWER_LEFT, 0,
                                not_found_text, 8,
                                VISIBLE, SHOW_NAME_VALUE);
         free(not_found_text);

         /* figure out where to put the hazard triangle */
         world_get_text_bounds(w_current, prim_objs,
                               &left, &top, &right, &bottom);
         x_offset = (right - left) / 4;
         y_offset = bottom - top + 100;  /* 100 is just an additional offset */

         /* add hazard triangle */
         prim_objs = o_line_add(w_current, prim_objs, OBJ_LINE,
                                DETACHED_ATTRIBUTE_COLOR,
                                x + NOT_FOUND_TEXT_X + x_offset,
                                y + NOT_FOUND_TEXT_Y + y_offset,
                                x + NOT_FOUND_TEXT_X + x_offset + 600,
                                y + NOT_FOUND_TEXT_Y + y_offset);
         o_set_line_options(w_current, prim_objs, END_ROUND, TYPE_SOLID,
                            50, -1, -1);
         prim_objs = o_line_add(w_current, prim_objs, OBJ_LINE,
                                DETACHED_ATTRIBUTE_COLOR,
                                x + NOT_FOUND_TEXT_X + x_offset,
                                y + NOT_FOUND_TEXT_Y + y_offset,
                                x + NOT_FOUND_TEXT_X + x_offset + 300,
                                y + NOT_FOUND_TEXT_Y + y_offset + 500);
         o_set_line_options(w_current, prim_objs, END_ROUND, TYPE_SOLID,
                            50, -1, -1);
         prim_objs = o_line_add(w_current, prim_objs, OBJ_LINE,
                                DETACHED_ATTRIBUTE_COLOR,
                                x + NOT_FOUND_TEXT_X + x_offset + 300,
                                y + NOT_FOUND_TEXT_Y + y_offset + 500,
                                x + NOT_FOUND_TEXT_X + x_offset + 600,
                                y + NOT_FOUND_TEXT_Y + y_offset);
         o_set_line_options(w_current, prim_objs, END_ROUND, TYPE_SOLID,
                            50, -1, -1);
         prim_objs = o_text_add(w_current, prim_objs,
                                OBJ_TEXT, DETACHED_ATTRIBUTE_COLOR,
                                x + NOT_FOUND_TEXT_X + x_offset + 270,
                                y + NOT_FOUND_TEXT_Y + y_offset + 90,
                                LOWER_LEFT, 0, "!", 18,
                                VISIBLE, SHOW_NAME_VALUE);
         prim_objs = save_prim_objs;

       } else {

         /* filename was found */
         loaded_normally = TRUE;

         /* add connections till translated */
         o_read(w_current, prim_objs, filename);

       }
       w_current->ADDING_SEL = save_adding_sel;

       if (w_current->attribute_promotion) { /* controlled through rc file */

         OBJECT *tmp,*next;

         for (tmp=prim_objs->next;tmp;tmp=next) {

           next=tmp->next;

           /* valid floating attrib? */
           if (o_complex_is_eligible_attribute(tmp, w_current->promote_invisible))
           {
             /* Is attribute promotion called for? (passed in parameter) */
             if (attribute_promotion)
             {
               /* remove tmp from the complex list */
               if (tmp->next)
                 tmp->next->prev=tmp->prev;
               if (tmp->prev)
                 tmp->prev->next=tmp->next;

               /* Isolate tmp completely, now that it's removed from list */
               tmp->next=tmp->prev=NULL;

               object_list = (OBJECT *) s_basic_link_object(tmp, object_list);
               o_attrib_attach (w_current, object_list, tmp, new_node);
               o_text_translate_world(w_current, x, y, tmp);

             } else { /* not promoting now, but deal with floating attribs */

               if (w_current->keep_invisible == TRUE) {
                 /* if we are not promoting invisible attribs, keep them */
                 /* around */
                 tmp->visibility = INVISIBLE;
               } else {
                 /* else do the default behavior of deleting the original */
                 /* object */
                 s_delete(w_current, tmp);
               }

             }
           }
         }
       }

       w_current->page_current->object_tail = temp_tail;
       w_current->page_current->object_parent = temp_parent;

       object_list = (OBJECT *) s_basic_link_object(new_node, object_list);
       object_list->complex->prim_objs = prim_objs;

       /* do not mirror/rotate/translate/connect the primitive objects if the
        * component was not loaded via o_read
        */
       if (loaded_normally == TRUE) {
         if (mirror) {
           o_complex_mirror_lowlevel(w_current, x, y, object_list);
         }

         o_complex_rotate_lowlevel(w_current, x, y, angle, angle, object_list);
         o_complex_world_translate(w_current, x, y, prim_objs);

         if (!w_current->ADDING_SEL) {
          s_conn_update_complex(w_current, prim_objs);
         }
       }

       free(filename);
       return(object_list);
     }

          Uses :
          add_head,  -- o_complex_basic.c - add_head().
          o_complex_is_eligible_attribute,  -- o_complex_basic.c - o_complex_is_eligible_attribute().
          o_complex_mirror_lowlevel,  -- o_complex_basic.c - o_complex_mirror_lowlevel().
          o_complex_rotate_lowlevel,  -- o_complex_basic.c - o_complex_rotate_lowlevel().

     }

Function o_complex_add_embedded()
=================================

 -- Function: o_complex_add_embedded w_current object_list type color x y angle clib basename selectable

     <<o_complex_basic.c : o_complex_add_embedded()>>=
     OBJECT *
     o_complex_add_embedded(TOPLEVEL *w_current, OBJECT *object_list,
                                                char type, int color, int x, int y, int angle,
                                                char *clib, char *basename, int selectable)
     {
       OBJECT *prim_objs=NULL;
       OBJECT *new_node=NULL;

       new_node = s_basic_init_object("complex");
       new_node->type = type;

       new_node->complex = (COMPLEX *) malloc(sizeof(COMPLEX));
       new_node->complex->x = x;
       new_node->complex->y = y;
       WORLDtoSCREEN(w_current, x, y,
                     &new_node->complex->screen_x,
                     &new_node->complex->screen_y);

       new_node->complex->angle = angle;
       new_node->complex->mirror = 0;

       new_node->complex_basename = strdup(basename);
       if (clib)
         new_node->complex_clib = strdup(clib);
       else
         new_node->complex_clib = NULL;

       new_node->color = color;

       /* TODO: questionable cast */
       new_node->draw_func = (void *) complex_draw_func;

       /* (for a title block) an object that isn't selectable */
       if (selectable) {
         new_node->sel_func = (void *) select_func;
       } else {
         new_node->sel_func = NULL;
       }

       object_list = (OBJECT *) s_basic_link_object(new_node, object_list);

       /* this was at the beginning and p_complex was = to complex */
       prim_objs = (OBJECT *) add_head();
       object_list->complex->prim_objs = prim_objs;

       /* set the parent field now */
       prim_objs->complex_parent = object_list;

       /* don't have to translate/rotate/mirror here at all since the */
       /* object is in place */
       return(object_list);
     }

          Defines :
          o_complex_add_embedded,
           -- o_complex_read() - add an embedded complex,
           -- o_complex_basic.c - o_complex_copy_embedded().

          Uses :
          add_head,  -- o_complex_basic.c - add_head().

     }

Function o_complex_recalc()
===========================

 -- Function: o_complex_recalc w_current o_current

     <<o_complex_basic.c : o_complex_recalc()>>=
     void
     o_complex_recalc(TOPLEVEL *w_current, OBJECT *o_current)
     {
       int left, right, top, bottom;

       /* realc routine Add this somewhere */
       /* libhack */
       /* o_recalc(w_current, o_current->complex);*/

       get_complex_bounds(w_current, o_current->complex->prim_objs, &left, &top, &right, &bottom);
       o_current->left = left;
       o_current->top = top;
       o_current->right = right;
       o_current->bottom = bottom;

       WORLDtoSCREEN(w_current,
                     o_current->complex->x,
                     o_current->complex->y,
                     &o_current->complex->screen_x,
                     &o_current->complex->screen_y);
     }

          Defines :
          .

          Uses :
          get_complex_bounds,  -- o_complex_basic.c - get_complex_bounds().

     }

Function o_complex_read()
=========================

 -- Function: o_complex_read w_current object_list buf version

     <<o_complex_basic.c : o_complex_read()>>=
     OBJECT *
     o_complex_read(TOPLEVEL *w_current, OBJECT *object_list,
                    char buf[], unsigned int release_ver,
                    unsigned int fileformat_ver)
     {
       char type;
       int x1, y1;
       int angle;

       char basename[256]; /* hack */
       char *clib=NULL;

       int selectable;
       int mirror;

       sscanf(buf, "%c %d %d %d %d %d %s\n",
              &type, &x1, &y1, &selectable, &angle, &mirror, basename);

       switch(angle) {

         case(0):
         case(90):
         case(180):
         case(270):
           break;

         default:
           fprintf(stderr, "Found a component with an invalid rotation [ %c %d %d %d %d %d %s ]\n", type, x1, y1, selectable, angle, mirror, basename);
           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);
           break;
       }

       switch(mirror) {

         case(0):
         case(1):

         break;

         default:
         fprintf(stderr, "Found a component with an invalid mirror flag [ %c %d %d %d %d %d %s ]\n", type, x1, y1, selectable, angle, mirror, basename);
         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);
         break;
       }
       if (strncmp(basename, "EMBEDDED", 8) == 0) {
         <<o_complex_read() : add an embedded complex -- o_complex_read() - add an embedded complex>>
       } else {
         <<o_complex_read() : add a complex from library -- o_complex_read() - add a complex from library>>
       }

       return object_list;
     }

          Defines :
          .

     }

     <<o_complex_read() : add an embedded complex>>=

       object_list = o_complex_add_embedded(w_current,
                                            object_list, type,
                                            WHITE, x1, y1, angle,
                                            "EMBEDDED", basename,
                                            selectable);

          Uses :
          o_complex_add_embedded,  -- o_complex_basic.c - o_complex_add_embedded().

     }

     <<o_complex_read() : add a complex from library>>=
     const GSList *clibs = s_clib_search_basename (basename);
     if (clibs == NULL) {
       s_log_message("Component [%s] was not found in any component library\n",
                     basename);
       fprintf(stderr,
               "Component [%s] was not found in any component library\n",
               basename);
       clib = NULL;
     } else {
       if (g_slist_next (clibs)) {
         s_log_message ("More than one component found with name [%s]\n",
                        basename);
         /* PB: for now, use the first directory in clibs */
         /* PB: maybe open a dialog to select the right one? */
       }
       clib = (gchar*)clibs->data;
     }

     object_list = o_complex_add(w_current, object_list, type,
                                 WHITE,
                                 x1, y1,
                                 angle, mirror,
                                 clib, basename, selectable, FALSE);

Function o_complex_save()
=========================

 -- Function: o_complex_save buf object

     <<o_complex_basic.c : o_complex_save()>>=
     char *
     o_complex_save(OBJECT *object)
     {
       int selectable;
       char *buf = NULL;

       if (object->sel_func != NULL)
       selectable = 1;
       else
       selectable = 0;

       if (object->type == OBJ_COMPLEX) {
         buf = g_strdup_printf("%c %d %d %d %d %d %s", object->type,
                               object->complex->x, object->complex->y, selectable,
                               object->complex->angle, object->complex->mirror,
                               object->complex_basename);
       } else if (object->type == OBJ_PLACEHOLDER) {
         buf = g_strdup_printf("C %d %d %d %d %d %s",
                               object->complex->x, object->complex->y, selectable,
                               object->complex->angle, object->complex->mirror,
                               object->complex_basename);  /* write 'C' manually */
       }

       return(buf);
     }

          Defines :
          .

     }

Function o_complex_set_filename()
=================================

 -- Function: o_complex_set_filename w_current clib basename

     <<o_complex_basic.c : o_complex_set_filename()>>=
     void
     o_complex_set_filename(TOPLEVEL *w_current, char *clib, char *basename)
     {
       int len;

       if (basename == NULL) {
         fprintf(stderr, "Got NULL basename in o_complex_set_filename!\n");
         exit(-1);
       }

       if (clib == NULL) {
         fprintf(stderr, "Got NULL clib in o_complex_set_filename!\n");
         exit(-1);
       }

       if (w_current->internal_basename) {
         free(w_current->internal_basename);
       }

       if (w_current->internal_clib) {
         free(w_current->internal_clib);
       }

       len = strlen(basename);
       w_current->internal_basename = (char *) malloc(sizeof(char)*len+1);

       len = strlen(clib) + 1;
       w_current->internal_clib = (char *) malloc(sizeof(char)*len+1);

       strcpy(w_current->internal_basename, basename);
       strcpy(w_current->internal_clib, clib);
     }

          Defines :
          .

     }

Function o_complex_free_filename()
==================================

 -- Function: o_complex_free_filename w_current

     <<o_complex_basic.c : o_complex_free_filename()>>=
     void
     o_complex_free_filename(TOPLEVEL *w_current)
     {
       if (w_current->internal_basename) {
         free(w_current->internal_basename);
       }

       if (w_current->internal_clib) {
         free(w_current->internal_clib);
       }
     }

          Defines :
          .

     }

Function o_complex_translate()
==============================

 -- Function: o_complex_translate w_current dx dy object

     <<o_complex_basic.c : o_complex_translate()>>=
     /* now I think it works fine */
     /* no there is a bug with snap locking.  Basically if you don't snap/lock in */
     /* PCtoW, then this doesn't work... :(  I don't know why yet */
     void
     o_complex_translate(TOPLEVEL *w_current, int dx, int dy, OBJECT *object)
     {
       int x, y;
       int prevx, prevy;

       if (object == NULL) {
         printf("cmpt NO!\n");
         return;
       }

       object->complex->screen_x = object->complex->screen_x + dx;
       object->complex->screen_y = object->complex->screen_y + dy;

       /* this fixing makes me nervious hack */
       SCREENtoWORLD(w_current, object->complex->screen_x,
                     object->complex->screen_y, &x, &y);

       prevx = object->complex->x;
       prevy = object->complex->y;
       object->complex->x = snap_grid(w_current, x);
       object->complex->y = snap_grid(w_current, y);

       o_complex_world_translate(w_current, x - prevx,y - prevy,
                                 object->complex->prim_objs);
     }

          Defines :
          .

     }

Function o_complex_world_translate()
====================================

 -- Function: o_complex_world_translate w_current x1 y1 prim_objs

     <<o_complex_basic.c : o_complex_world_translate()>>=
     /* this needs work remove display stuff */
     /* libhack */
     /* and recalc stuff */
     /* this function takes in a complex list */
     void
     o_complex_world_translate(TOPLEVEL *w_current, int x1, int y1,
                               OBJECT *prim_objs)
     {
       OBJECT *o_current=NULL;
       OBJECT *one=NULL;
       OBJECT *two=NULL;
       unsigned long temp_color;

       o_current = prim_objs;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
             o_line_translate_world(w_current, x1, y1, o_current);
             break;

           case(OBJ_NET):
                                     /* same as a line, don't do this */
             o_line_translate_world(w_current, x1, y1, o_current);
             temp_color = w_current->override_color;
             w_current->override_color = -1;
             o_redraw_single(w_current, one); /* trying loop? hack*/
             o_redraw_single(w_current, two);
             w_current->override_color = temp_color;
             break;

           case(OBJ_BUS):
                                     /* same as a line, don't do this */
             o_line_translate_world(w_current, x1, y1, o_current);
             temp_color = w_current->override_color;
             w_current->override_color = -1;
             o_redraw_single(w_current, one); /* trying loop? hack*/
             o_redraw_single(w_current, two);
             w_current->override_color = temp_color;
             break;

           case(OBJ_BOX):
             o_box_translate_world(w_current, x1, y1, o_current);
             break;

           case(OBJ_PICTURE):
             o_picture_translate_world(w_current, x1, y1, o_current);
             break;

           case(OBJ_CIRCLE):
             o_circle_translate_world(w_current, x1, y1, o_current);
             break;

           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             o_complex_world_translate_toplevel(w_current, x1, y1, o_current);
             break;

           case(OBJ_TEXT):
             o_text_translate_world(w_current, x1, y1, o_current);
             break;

             /* same note as above */
           case(OBJ_PIN):
             o_pin_translate_world(w_current, x1, y1, o_current);
             break;

           case(OBJ_ARC):
             o_arc_translate_world(w_current, x1, y1, o_current);
             break;
         }
         o_current=o_current->next;
       }
     }

          Uses :
          o_complex_world_translate_toplevel,  -- o_complex_basic.c - o_complex_world_translate_toplevel().

     }

o_complex_translate_world

Function o_complex_world_translate_toplevel()
=============================================

 -- Function: o_complex_world_translate_toplevel w_current x1 y1 object

     <<o_complex_basic.c : o_complex_world_translate_toplevel()>>=
     /* this function takes the toplevel object and then also translates the
      * complex */
     void
     o_complex_world_translate_toplevel(TOPLEVEL *w_current, int x1, int y1, OBJECT *object)
     {
       int left, right, top, bottom;

       object->complex->x = object->complex->x + x1;
       object->complex->y = object->complex->y + y1;

       WORLDtoSCREEN(w_current, object->complex->x,
                     object->complex->y,
                     &object->complex->screen_x,
                     &object->complex->screen_y);

       o_complex_world_translate(w_current, x1, y1,
                                 object->complex->prim_objs);

       get_complex_bounds(w_current, object->complex->prim_objs,
                          &left, &top, &right, &bottom);

       object->left = left;
       object->top = top;
       object->right = right;
       object->bottom = bottom;
     }

          Defines :
          o_complex_world_translate_toplevel,
           -- o_complex_basic.c - o_complex_world_translate().

          Uses :
          get_complex_bounds,  -- o_complex_basic.c - get_complex_bounds().

     }

Function o_complex_copy()
=========================

 -- Function: o_complex_copy w_current list_tail o_current

     <<o_complex_basic.c : o_complex_copy()>>=
     OBJECT *
     o_complex_copy(TOPLEVEL *w_current, OBJECT *list_tail, OBJECT *o_current)
     {
       OBJECT *new_obj=NULL;
       ATTRIB *a_current;
       int color;
       int selectable;

       if (o_current->saved_color == -1) {
         color = o_current->color;
       } else {
         color = o_current->saved_color;
       }

       if (o_current->sel_func) {
         selectable = TRUE;
       } else {
         selectable = FALSE;
       }

       new_obj = o_complex_add(w_current, list_tail, o_current->type, color,
                               o_current->complex->x, o_current->complex->y,
                               o_current->complex->angle, o_current->complex->mirror,
                               o_current->complex_clib, o_current->complex_basename,
                               selectable, FALSE);

       o_attrib_slot_copy(w_current, o_current, new_obj);

       /* deal with stuff that has changed */

       /* here you need to create a list of attributes which need to be
        * connected to the new list, probably make an attribute list and
        * fill it with sid's of the attributes */
       a_current = o_current->attribs;
       if (a_current) {
         while ( a_current ) {

           /* head attrib node has prev = NULL */
           if (a_current->prev != NULL) {
             a_current->copied_to = new_obj;
           }

           a_current = a_current->next;
         }
       }

       return(new_obj);
     }

          Defines :
          .

     }

Function o_complex_copy_embedded()
==================================

 -- Function: o_complex_copy_embedded w_current list_tail o_current

     <<o_complex_basic.c : o_complex_copy_embedded()>>=
     OBJECT *
     o_complex_copy_embedded(TOPLEVEL *w_current, OBJECT *list_tail, OBJECT *o_current)
     {
       OBJECT *new_obj=NULL;
       OBJECT *temp_list;
       ATTRIB *a_current;
       int color;
       int selectable;

       if (o_current->saved_color == -1) {
         color = o_current->color;
       } else {
         color = o_current->saved_color;
       }

       if (o_current->sel_func) {
         selectable = TRUE;
       } else {
         selectable = FALSE;
       }

       new_obj = o_complex_add_embedded(w_current, list_tail, o_current->type,
                                        color,
                                        o_current->complex->x, o_current->complex->y,
                                        o_current->complex->angle,
                                        o_current->complex_clib,
                                        o_current->complex_basename,
                                        selectable);
       /* deal with stuff that has changed */

       temp_list = o_list_copy_all(w_current,
                                   o_current->complex->prim_objs->next,
                                   new_obj->complex->prim_objs,
                                   NORMAL_FLAG);

       new_obj->complex->prim_objs = return_head(temp_list);

       /* here you need to create a list of attributes which need to be
        * connected to the new list, probably make an attribute list and
        * fill it with sid's of the attributes */
       a_current = o_current->attribs;
       if (a_current) {
         while ( a_current ) {

           /* head attrib node has prev = NULL */
           if (a_current->prev != NULL) {
             a_current->copied_to = new_obj;
           }

           a_current = a_current->next;
         }
       }

       return(new_obj);
     }

          Defines :
          .

          Uses :
          o_complex_add_embedded,  -- o_complex_basic.c - o_complex_add_embedded().

     }

Function o_complex_delete()
===========================

 -- Function: o_complex_delete w_current delete

     <<o_complex_basic.c : o_complex_delete()>>=
     void
     o_complex_delete(TOPLEVEL *w_current, OBJECT *delete)
     {
       /* first remove complex pointer */
       if (delete->complex) {
         if (delete->complex->prim_objs) {
           s_delete_list_fromstart(w_current,
                                   delete->complex->prim_objs);
         }
         delete->complex->prim_objs = NULL;
       }

       /* then remove the actual node */
       s_delete(w_current, delete);
       delete = NULL;
       w_current->page_current->object_tail = (OBJECT *)
       return_tail(w_current->page_current->object_head);
     }

          Defines :
          .

     }

Function o_complex_set_color()
==============================

 -- Function: o_complex_set_color prim_objs color

     <<o_complex_basic.c : o_complex_set_color()>>=
     void
     o_complex_set_color(OBJECT *prim_objs, int color)
     {
       OBJECT *o_current=NULL;

       o_current = prim_objs;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
           case(OBJ_NET):
           case(OBJ_BUS):
           case(OBJ_BOX):
           case(OBJ_PICTURE):
           case(OBJ_CIRCLE):
           case(OBJ_PIN):
           case(OBJ_ARC):
             o_current->color = color;
             break;

           case(OBJ_TEXT):
             o_current->color = color;
             o_complex_set_color(o_current->text->prim_objs, color);
             break;

           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             o_current->color = color;
             o_complex_set_color(o_current->complex->prim_objs, color);
             break;

         }
         o_current=o_current->next;
       }
     }

          Defines :
          o_complex_set_color,
           -- o_complex_basic.c - o_complex_set_color_single().

     }

Function o_complex_set_color_single()
=====================================

 -- Function: o_complex_set_color_single o_current color

     <<o_complex_basic.c : o_complex_set_color_single()>>=
     void
     o_complex_set_color_single(OBJECT *o_current, int color)
     {
       switch(o_current->type) {
         case(OBJ_LINE):
         case(OBJ_NET):
         case(OBJ_BUS):
         case(OBJ_BOX):
         case(OBJ_PICTURE):
         case(OBJ_CIRCLE):
         case(OBJ_PIN):
         case(OBJ_ARC):
         o_current->color = color;
         break;

         case(OBJ_TEXT):
         o_current->color = color;
         o_complex_set_color(o_current->text->prim_objs, color);
         break;

         case(OBJ_COMPLEX):
         case(OBJ_PLACEHOLDER):
         o_current->color = color;
         o_complex_set_color(o_current->complex->prim_objs,
                             color);
         break;

       }
     }

          Defines :
          .

          Uses :
          o_complex_set_color,  -- o_complex_basic.c - o_complex_set_color().

     }

Function o_complex_set_color_save()
===================================

 -- Function: o_complex_set_color_save complex color

     <<o_complex_basic.c : o_complex_set_color_save()>>=
     void
     o_complex_set_color_save(OBJECT *complex, int color)
     {
       OBJECT *o_current=NULL;

       o_current = complex;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
           case(OBJ_NET):
           case(OBJ_BUS):
           case(OBJ_BOX):
           case(OBJ_PICTURE):
           case(OBJ_CIRCLE):
           case(OBJ_PIN):
           case(OBJ_ARC):
             o_current->saved_color = o_current->color;
             o_current->color = color;
             break;

           case(OBJ_TEXT):
             o_current->saved_color = o_current->color;
             o_current->color = color;
             o_complex_set_color_save(
                                      o_current->text->prim_objs,
                                      color);
             break;

           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             o_current->saved_color = o_current->color;
             o_current->color = color;
             o_complex_set_color_save(o_current->complex->
                                      prim_objs,
                                      color);
             break;

         }
         o_current=o_current->next;
       }
     }

          Defines :
          .

     }

Function o_complex_unset_color()
================================

 -- Function: o_complex_unset_color complex

     <<o_complex_basic.c : o_complex_unset_color()>>=
     void
     o_complex_unset_color(OBJECT *complex)
     {
       OBJECT *o_current=NULL;

       o_current = complex;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
           case(OBJ_NET):
           case(OBJ_BUS):
           case(OBJ_BOX):
           case(OBJ_PICTURE):
           case(OBJ_CIRCLE):
           case(OBJ_PIN):
           case(OBJ_ARC):
             o_current->color = o_current->saved_color;
             o_current->saved_color = -1;
             break;

           case(OBJ_TEXT):
             o_current->color = o_current->saved_color;
             o_current->saved_color = -1;
             o_complex_unset_color(o_current->text->prim_objs);
             break;

           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             o_current->color = o_current->saved_color;
             o_current->saved_color = -1;
             o_complex_unset_color(o_current->complex->
                                   prim_objs);

             break;

         }
         o_current=o_current->next;
       }
     }

          Defines :
          o_complex_unset_color,
           -- o_complex_basic.c - o_complex_unset_color_single().

     }

Function o_complex_unset_color_single()
=======================================

 -- Function: o_complex_unset_color_single o_current

     <<o_complex_basic.c : o_complex_unset_color_single()>>=
     void
     o_complex_unset_color_single(OBJECT *o_current)
     {
       switch(o_current->type) {
         case(OBJ_LINE):
         case(OBJ_NET):
         case(OBJ_BUS):
         case(OBJ_BOX):
         case(OBJ_PICTURE):
         case(OBJ_CIRCLE):
         case(OBJ_PIN):
         case(OBJ_ARC):
         o_current->color = o_current->saved_color;
         o_current->saved_color = -1;
         break;

         case(OBJ_TEXT):
         o_current->color = o_current->saved_color;
         o_current->saved_color = -1;
         o_complex_unset_color(o_current->text->prim_objs);
         break;

         case(OBJ_COMPLEX):
         case(OBJ_PLACEHOLDER):
         o_current->color = o_current->saved_color;
         o_current->saved_color = -1;
         o_complex_unset_color(o_current->complex->prim_objs);

         break;
       }
     }

          Defines :
          .

          Uses :
          o_complex_unset_color,  -- o_complex_basic.c - o_complex_unset_color().

     }

Function o_complex_set_saved_color_only()
=========================================

 -- Function: o_complex_set_saved_color_only complex color

     <<o_complex_basic.c : o_complex_set_saved_color_only()>>=
     void
     o_complex_set_saved_color_only(OBJECT *complex, int color)
     {
       OBJECT *o_current=NULL;

       o_current = complex;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
           case(OBJ_NET):
           case(OBJ_BUS):
           case(OBJ_BOX):
           case(OBJ_PICTURE):
           case(OBJ_CIRCLE):
           case(OBJ_PIN):
           case(OBJ_ARC):
             o_current->saved_color = color;
             break;

           case(OBJ_TEXT):
             o_current->saved_color = color;
             o_complex_set_saved_color_only(o_current->text->prim_objs, color);
             break;

           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             o_current->saved_color = color;
             o_complex_set_saved_color_only(o_current->complex->prim_objs, color);
             break;

         }
         o_current=o_current->next;
       }
     }

          Defines :
          .

     }

Function o_complex_return_nth_pin()
===================================

 -- Function: o_complex_return_nth_pin o_lits counter

     <<o_complex_basic.c : o_complex_return_nth_pin()>>=
     /* returns the counter'th pin in o_list */
     /* NULL if there is no more pins */
     OBJECT *
     o_complex_return_nth_pin(OBJECT *o_list, int counter)
     {
       OBJECT *o_current;
       int internal_counter=0;

       o_current = o_list;

       while (o_current != NULL) {
         if (o_current->type == OBJ_PIN) {

           if (counter == internal_counter) {
             return(o_current);
           } else {
             internal_counter++;
           }
         }
         o_current = o_current->next;
       }

       return(NULL);
     }

          Defines :
          .

     }

Function o_complex_rotate_lowlevel()
====================================

 -- Function: o_complex_rotate_lowlevel w_current world_centerx world_centery angle angle_change object

     <<o_complex_basic.c : o_complex_rotate_lowlevel()>>=
     /* pass in top level object */
     void
     o_complex_rotate_lowlevel(TOPLEVEL *w_current, int world_centerx,
             int world_centery,
             int angle,
             int angle_change,
             OBJECT *object)
     {
       OBJECT *o_current=NULL;

     #if DEBUG
       printf("------- a %d ac %d\n", angle, angle_change);
     #endif

       /* do individual complex objects */
       o_current = object->complex->prim_objs;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
             o_line_rotate_world(w_current, 0, 0, angle_change, o_current);
             break;

           case(OBJ_NET):
             o_net_rotate_world(w_current, 0, 0, angle_change, o_current);
             break;

           case(OBJ_BUS):
             o_bus_rotate_world(w_current, 0, 0, angle_change, o_current);
             break;

           case(OBJ_BOX):
             o_box_rotate_world(w_current, 0, 0, angle_change, o_current);
             break;

           case(OBJ_PICTURE):
     #ifndef HAS_GTK12
             o_picture_rotate_world(w_current, 0, 0, angle_change, o_current);
     #endif
             break;

           case(OBJ_CIRCLE):
             o_circle_rotate_world(w_current, 0, 0, angle_change, o_current);
             break;

           case(OBJ_PIN):
             o_pin_rotate_world(w_current, 0, 0, angle_change, o_current);
             break;

           case(OBJ_ARC):
             o_arc_rotate_world(w_current, 0, 0, angle_change, o_current);
             break;

     #if 0   /* complex within a complex? not right now */
           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             break;
     #endif

           case(OBJ_TEXT):
             o_text_rotate_world(w_current, 0, 0, angle, angle_change, o_current);
             break;

         }
         o_current=o_current->next;
       }
     }

          Defines :
          o_complex_rotate_lowlevel,
           -- o_complex_basic.c - o_complex_add().

     }

Function o_complex_mirror_lowlevel()
====================================

 -- Function: o_complex_mirror_lowlevel w_current world_centerx world_centery object

     <<o_complex_basic.c : o_complex_mirror_lowlevel()>>=
     /* pass in top level object */
     void
     o_complex_mirror_lowlevel(TOPLEVEL *w_current,
             int world_centerx, int world_centery, OBJECT *object)
     {
       OBJECT *o_current=NULL;

       /* do individual complex objects */
       o_current = object->complex->prim_objs;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
             o_line_mirror_world(w_current, 0, 0, o_current);
             break;

           case(OBJ_NET):
             o_net_mirror_world(w_current, 0, 0, o_current);
             break;

           case(OBJ_BUS):
             o_bus_mirror_world(w_current, 0, 0, o_current);
             break;

           case(OBJ_BOX):
             o_box_mirror_world(w_current, 0, 0, o_current);
             break;

           case(OBJ_PICTURE):
     #ifndef HAS_GTK12
             o_picture_mirror_world(w_current, 0, 0, o_current);
     #endif
             break;

           case(OBJ_CIRCLE):
             o_circle_mirror_world(w_current, 0, 0, o_current);
             break;

           case(OBJ_PIN):
             o_pin_mirror_world(w_current, 0, 0, o_current);
             break;

           case(OBJ_ARC):
             o_arc_mirror_world(w_current, 0, 0, o_current);
             break;

     #if 0   /* complex within a complex? not right now */
           case(OBJ_COMPLEX):
           case(OBJ_PLACEHOLDER):
             break;
     #endif

           case(OBJ_TEXT):
             o_text_mirror_world(w_current, 0, 0, o_current);
             break;

         }
         o_current=o_current->next;
       }

       /* mirror origin point */
       /*    object->x = -object->x;*/
     }

          Defines :
          o_complex_mirror_lowlevel,
           -- o_complex_basic.c - o_complex_add().

     }

Function o_complex_return_pin_object()
======================================

 -- Function: o_complex_return_pin_object object pin

     <<o_complex_basic.c : o_complex_return_pin_object()>>=
     /* pass in top level object */
     OBJECT *
     o_complex_return_pin_object(OBJECT *object, char *pin)
     {
       OBJECT *o_current=NULL;
       OBJECT *found;

       /* go inside complex objects */
       o_current = object->complex->prim_objs;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_PIN):

             /* Search for the pin making sure that */
             /* any found attribute starts with "pinnumber" */
             found = o_attrib_search_attrib_value(o_current->attribs, pin,
                                                  "pinnumber", 0);
             if (found) {
     #if DEBUG
               printf("%s: %s\n", found->name,
                      found->text->string);
     #endif
               return(o_current);
             }
             break;
         }
         o_current=o_current->next;
       }
       return(NULL);
     }

          Defines :
          .

     }

Function o_complex_check_symversion()
=====================================

 -- Function: o_complex_check_symversion w_current object

     <<o_complex_basic.c : o_complex_check_symversion()>>=
     void
     o_complex_check_symversion(TOPLEVEL* w_current, OBJECT* object)
     {
       char *inside = NULL;
       char *outside = NULL;
       char *refdes = NULL;
       double inside_value = -1.0;
       double outside_value = -1.0;
       char *err_check = NULL;
       int inside_present = FALSE;
       int outside_present = FALSE;
       double inside_major, inside_minor;
       double outside_major, outside_minor;

       if (object->type != OBJ_COMPLEX && object->type != OBJ_PLACEHOLDER)
       {
         return;
       }

       /* first look on the inside for the symversion= attribute */
       inside = o_attrib_search_name(object->complex->prim_objs, "symversion", 0);

       /* now look for the symversion= attached to object */
       outside = o_attrib_search_attrib_name(object->attribs, "symversion", 0);

       /* get the uref for future use */
       refdes = o_attrib_search_attrib_name(object->attribs, "refdes", 0);
       if (!refdes)
       {
         refdes = g_strdup ("unknown");
       }

       if (inside)
       {
         inside_value = strtod(inside, &err_check);
         if (inside_value == 0 && inside == err_check)
         {
           if (inside)
           {
             s_log_message("WARNING: Symbol version parse error on refdes %s:\n"
                         "\tCould not parse symbol file symversion=%s\n",
                          refdes, inside);
           } else {
             s_log_message("WARNING: Symbol version parse error on refdes %s:\n"
                         "\tCould not parse symbol file symversion=\n",
                          refdes);
           }
           goto done;
         }
         inside_present = TRUE;
       } else {
         inside_present = FALSE;  /* attribute not inside */
       }

       if (outside)
       {
         outside_value = strtod(outside, &err_check);
         if (outside_value == 0 && outside == err_check)
         {
           s_log_message("WARNING: Symbol version parse error on refdes %s:\n"
                         "\tCould not parse attached symversion=%s\n",
                         refdes, outside);
           goto done;
         }
         outside_present = TRUE;
       } else {
         outside_present = FALSE;  /* attribute not outside */
       }

     #if DEBUG
       printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object->name,
              inside_value, outside_value);
     #endif

       /* symversion= is not present anywhere */
       if (!inside_present && !outside_present)
       {
         /* symbol is legacy and versioned okay */
         goto done;
       }

       /* No symversion inside, but a version is outside, this is a weird case */
       if (!inside_present && outside_present)
       {
         s_log_message("WARNING: Symbol version oddity on refdes %s:\n"
                       "\tsymversion=%s attached to instantiated symbol, "
                       "but no symversion= inside symbol file\n",
                       refdes, outside);
         goto done;
       }

       /* inside & not outside is a valid case, means symbol in library is newer */
       /* also if inside_value is greater than outside_value, then symbol in */
       /* library is newer */
       if ((inside_present && !outside_present) ||
           ((inside_present && outside_present) && (inside_value > outside_value)))
       {

         fprintf(stderr, "WARNING: Symbol version mismatch on refdes %s (%s):\n"
                 "\tSymbol in library is newer than "
                 "instantiated symbol\n",
                 refdes, object->complex_basename);
         s_log_message("WARNING: Symbol version mismatch on refdes %s (%s):\n"
                       "\tSymbol in library is newer than "
                       "instantiated symbol\n",
                       refdes, object->complex_basename);

         /* break up the version values into major.minor numbers */
         inside_major = floor(inside_value);
         inside_minor = inside_value - inside_major;

         if (outside_present)
         {
           outside_major = floor(outside_value);
           outside_minor = outside_value - outside_major;
         } else {
           /* symversion was not attached to the symbol, set all to zero */
           outside_major = 0.0;
           outside_minor = 0.0;
           outside_value = 0.0;
         }

     #if DEBUG
         printf("i: %f %f %f\n", inside_value, inside_major, inside_minor);
         printf("o: %f %f %f\n", outside_value, outside_major, outside_minor);
     #endif

         if (inside_major > outside_major)
         {
           char* refdes_copy;
           fprintf(stderr, "\tMAJOR VERSION CHANGE (file %.3f, "
                   "instantiated %.3f, %s)!\n",
                   inside_value, outside_value, refdes);
           s_log_message("\tMAJOR VERSION CHANGE (file %.3f, "
                         "instantiated %.3f)!\n",
                         inside_value, outside_value);

           /* add the refdes to the major_changed_refdes GList */
           /* make sure refdes_copy is freed somewhere */
           refdes_copy = g_strconcat (refdes, " (",
                                      object->complex_basename,
                                      ")", NULL);
           w_current->major_changed_refdes =
             g_list_append(w_current->major_changed_refdes, refdes_copy);

           /* don't bother checking minor changes if there are major ones*/
           goto done;
         }

         if (inside_minor > outside_minor)
         {
           fprintf(stderr, "\tMinor version change (file %.3f, "
                   "instantiated %.3f)\n",
                   inside_value, outside_value);
           s_log_message("\tMinor version change (file %.3f, "
                         "instantiated %.3f)\n",
                         inside_value, outside_value);
         }

         goto done;
       }

       /* outside value is greater than inside value, this is weird case */
       if ((inside_present && outside_present) && (outside_value > inside_value))
       {
         s_log_message("WARNING: Symbol version oddity on refdes %s:\n"
                       "\tInstanciated symbol is newer than "
                       "symbol in library\n",
                       refdes);
         goto done;
       }

       /* if inside_value and outside_value match, then symbol versions are okay */

     done:
       if (inside) free(inside);
       if (outside) free(outside);
       if (refdes) free(refdes);
     }

          Defines :
          .

     }