void LIB_EDIT_FRAME::OnCheckComponent( wxCommandEvent& event )
{
    LIB_PART*      part = GetCurPart();

    if( !part )
        return;

    const int MIN_GRID_SIZE = 25;

    LIB_PINS pinList;

    part->GetPins( pinList );

    if( pinList.size() == 0 )
    {
        DisplayInfoMessage( this, _( "No pins!" ) );
        return;
    }

    // Sort pins by pin num, so 2 duplicate pins
    // (pins with the same number) will be consecutive in list
    sort( pinList.begin(), pinList.end(), sort_by_pin_number );

    // Test for duplicates:
    DIALOG_DISPLAY_HTML_TEXT_BASE error_display( this, wxID_ANY,
                                                 _( "Marker Information" ),
                                                 wxDefaultPosition,
                                                 wxSize( 750, 600 ) );

    int dup_error = 0;

    for( unsigned ii = 1; ii < pinList.size(); ii++ )
    {
        wxString stringPinNum, stringCurrPinNum;

        LIB_PIN* curr_pin = pinList[ii];
        LIB_PIN* pin      = pinList[ii - 1];

        if( pin->GetNumber() != curr_pin->GetNumber()
            || pin->GetConvert() != curr_pin->GetConvert()
            || pin->GetUnit() != curr_pin->GetUnit() )
            continue;

        dup_error++;
        pin->PinStringNum( stringPinNum );

        /* TODO I dare someone to find a way to make happy translators on
           this thing! Lorenzo */
        curr_pin->PinStringNum( stringCurrPinNum );

        wxString msg = wxString::Format( _(
            "<b>Duplicate pin %s</b> \"%s\" at location <b>(%.3f, %.3f)</b>"
            " conflicts with pin %s \"%s\" at location <b>(%.3f, %.3f)</b>" ),
            GetChars( stringCurrPinNum ),
            GetChars( curr_pin->GetName() ),
            curr_pin->GetPosition().x / 1000.0,
            -curr_pin->GetPosition().y / 1000.0,
            GetChars( stringPinNum ),
            GetChars( pin->GetName() ),
            pin->GetPosition().x / 1000.0,
            -pin->GetPosition().y / 1000.0
            );

        if( part->GetUnitCount() > 1 )
        {
            msg += wxString::Format( _( " in part %c" ), 'A' + curr_pin->GetUnit() - 1 );
        }

        if( m_showDeMorgan )
        {
            if( curr_pin->GetConvert() )
                msg += _( "  of converted" );
            else
                msg += _( "  of normal" );
        }

        msg += wxT( ".<br>" );

        error_display.m_htmlWindow->AppendToPage( msg );
    }

    // Test for off grid pins:
    int offgrid_error = 0;

    for( unsigned ii = 0; ii < pinList.size(); ii++ )
    {
        LIB_PIN* pin = pinList[ii];

        if( ( (pin->GetPosition().x % MIN_GRID_SIZE) == 0 ) &&
            ( (pin->GetPosition().y % MIN_GRID_SIZE) == 0 ) )
            continue;

        // "pin" is off grid here.
        offgrid_error++;
        wxString stringPinNum;
        pin->PinStringNum( stringPinNum );

        wxString msg = wxString::Format( _(
            "<b>Off grid pin %s</b> \"%s\" at location <b>(%.3f, %.3f)</b>" ),
            GetChars( stringPinNum ),
            GetChars( pin->GetName() ),
            pin->GetPosition().x / 1000.0,
            -pin->GetPosition().y / 1000.0
            );

        if( part->GetUnitCount() > 1 )
        {
            msg += wxString::Format( _( " in part %c" ), 'A' + pin->GetUnit() - 1 );
        }

        if( m_showDeMorgan )
        {
            if( pin->GetConvert() )
                msg += _( "  of converted" );
            else
                msg += _( "  of normal" );
        }

        msg += wxT( ".<br>" );

        error_display.m_htmlWindow->AppendToPage( msg );
    }

    if( !dup_error && !offgrid_error )
        DisplayInfoMessage( this, _( "No off grid or duplicate pins were found." ) );
    else
        error_display.ShowModal();
}
XNODE* NETLIST_EXPORTER_GENERIC::makeLibParts()
{
    XNODE*      xlibparts = node( wxT( "libparts" ) );   // auto_ptr
    wxString    sLibpart  = wxT( "libpart" );
    wxString    sLib      = wxT( "lib" );
    wxString    sPart     = wxT( "part" );
    wxString    sAliases  = wxT( "aliases" );
    wxString    sAlias    = wxT( "alias" );
    wxString    sPins     = wxT( "pins" );      // key for library component pins list
    wxString    sPin      = wxT( "pin" );       // key for one library component pin descr
    wxString    sPinNum   = wxT( "num" );       // key for one library component pin num
    wxString    sPinName  = wxT( "name" );      // key for one library component pin name
    wxString    sPinType  = wxT( "type" );      // key for one library component pin electrical type
    wxString    sName     = wxT( "name" );
    wxString    sField    = wxT( "field" );
    wxString    sFields   = wxT( "fields" );
    wxString    sDescr    = wxT( "description" );
    wxString    sDocs     = wxT( "docs" );
    wxString    sFprints  = wxT( "footprints" );
    wxString    sFp       = wxT( "fp" );

    LIB_PINS    pinList;
    LIB_FIELDS  fieldList;

    m_Libraries.clear();

    for( std::set<LIB_PART*>::iterator it = m_LibParts.begin(); it!=m_LibParts.end();  ++it )
    {
        LIB_PART* lcomp = *it;
        PART_LIB* library = lcomp->GetLib();

        m_Libraries.insert( library );  // inserts component's library if unique

        XNODE* xlibpart;
        xlibparts->AddChild( xlibpart = node( sLibpart ) );
        xlibpart->AddAttribute( sLib, library->GetLogicalName() );
        xlibpart->AddAttribute( sPart, lcomp->GetName()  );

        if( lcomp->GetAliasCount() )
        {
            wxArrayString aliases = lcomp->GetAliasNames( false );
            if( aliases.GetCount() )
            {
                XNODE* xaliases = node( sAliases );
                xlibpart->AddChild( xaliases );
                for( unsigned i=0;  i<aliases.GetCount();  ++i )
                {
                    xaliases->AddChild( node( sAlias, aliases[i] ) );
                }
            }
        }

        //----- show the important properties -------------------------
        if( !lcomp->GetAlias( 0 )->GetDescription().IsEmpty() )
            xlibpart->AddChild( node( sDescr, lcomp->GetAlias( 0 )->GetDescription() ) );

        if( !lcomp->GetAlias( 0 )->GetDocFileName().IsEmpty() )
            xlibpart->AddChild( node( sDocs,  lcomp->GetAlias( 0 )->GetDocFileName() ) );

        // Write the footprint list
        if( lcomp->GetFootPrints().GetCount() )
        {
            XNODE*  xfootprints;
            xlibpart->AddChild( xfootprints = node( sFprints ) );

            for( unsigned i=0; i<lcomp->GetFootPrints().GetCount(); ++i )
            {
                xfootprints->AddChild( node( sFp, lcomp->GetFootPrints()[i] ) );
            }
        }

        //----- show the fields here ----------------------------------
        fieldList.clear();
        lcomp->GetFields( fieldList );

        XNODE*     xfields;
        xlibpart->AddChild( xfields = node( sFields ) );

        for( unsigned i=0;  i<fieldList.size();  ++i )
        {
            if( !fieldList[i].GetText().IsEmpty() )
            {
                XNODE*     xfield;
                xfields->AddChild( xfield = node( sField, fieldList[i].GetText() ) );
                xfield->AddAttribute( sName, fieldList[i].GetName(false) );
            }
        }

        //----- show the pins here ------------------------------------
        pinList.clear();
        lcomp->GetPins( pinList, 0, 0 );

        /* we must erase redundant Pins references in pinList
         * These redundant pins exist because some pins
         * are found more than one time when a component has
         * multiple parts per package or has 2 representations (DeMorgan conversion)
         * For instance, a 74ls00 has DeMorgan conversion, with different pin shapes,
         * and therefore each pin  appears 2 times in the list.
         * Common pins (VCC, GND) can also be found more than once.
         */
        sort( pinList.begin(), pinList.end(), sortPinsByNumber );
        for( int ii = 0; ii < (int)pinList.size()-1; ii++ )
        {
            if( pinList[ii]->GetNumber() == pinList[ii+1]->GetNumber() )
            {   // 2 pins have the same number, remove the redundant pin at index i+1
                pinList.erase(pinList.begin() + ii + 1);
                ii--;
            }
        }

        if( pinList.size() )
        {
            XNODE*     pins;

            xlibpart->AddChild( pins = node( sPins ) );
            for( unsigned i=0; i<pinList.size();  ++i )
            {
                XNODE*     pin;

                pins->AddChild( pin = node( sPin ) );
                pin->AddAttribute( sPinNum, pinList[i]->GetNumberString() );
                pin->AddAttribute( sPinName, pinList[i]->GetName() );
                pin->AddAttribute( sPinType, pinList[i]->GetCanonicalElectricalTypeName() );

                // caution: construction work site here, drive slowly
            }
        }
    }

    return xlibparts;
}
SCH_COMPONENT* NETLIST_EXPORTER::findNextComponentAndCreatePinList( EDA_ITEM*       aItem,
                                                              SCH_SHEET_PATH* aSheetPath )
{
    wxString    ref;

    m_SortedComponentPinList.clear();

    // continue searching from the middle of a linked list (the draw list)
    for(  ; aItem;  aItem = aItem->Next() )
    {
        if( aItem->Type() != SCH_COMPONENT_T )
            continue;

        // found next component
        SCH_COMPONENT* comp = (SCH_COMPONENT*) aItem;

        // Power symbols and other components which have the reference starting
        // with "#" are not included in netlist (pseudo or virtual components)
        ref = comp->GetRef( aSheetPath );

        if( ref[0] == wxChar( '#' ) )
            continue;

        // if( Component->m_FlagControlMulti == 1 )
        //    continue;                                      /* yes */
        // removed because with multiple instances of one schematic
        // (several sheets pointing to 1 screen), this will be erroneously be
        // toggled.

        LIB_PART* part = m_libs->FindLibPart( comp->GetPartName() );

        if( !part )
            continue;

        // If component is a "multi parts per package" type
        if( part->GetUnitCount() > 1 )
        {
            // test if this reference has already been processed, and if so skip
            if( m_ReferencesAlreadyFound.Lookup( ref ) )
                continue;

            // Collect all pins for this reference designator by searching
            // the entire design for other parts with the same reference designator.
            // This is only done once, it would be too expensive otherwise.
            findAllInstancesOfComponent( comp, part, aSheetPath );
        }

        else    // entry->GetUnitCount() <= 1 means one part per package
        {
            LIB_PINS pins;      // constructed once here

            part->GetPins( pins, comp->GetUnitSelection( aSheetPath ), comp->GetConvert() );

            for( size_t i = 0; i < pins.size(); i++ )
            {
                LIB_PIN* pin = pins[i];

                wxASSERT( pin->Type() == LIB_PIN_T );

                addPinToComponentPinList( comp, aSheetPath, pin );
            }
        }

        // Sort pins in m_SortedComponentPinList by pin number
        sort( m_SortedComponentPinList.begin(),
              m_SortedComponentPinList.end(), sortPinsByNum );

        // Remove duplicate Pins in m_SortedComponentPinList
        eraseDuplicatePins( );

        // record the usage of this library component entry.
        m_LibParts.insert( part );     // rejects non-unique pointers

        return comp;
    }

    return NULL;
}