Esempio n. 1
0
MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern,
                    PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage )
{
    /* Build a microwave inductor footprint.
     * - Length Mself.lng
     * - Extremities Mself.m_Start and Mself.m_End
     * We must determine:
     * Mself.nbrin = number of segments perpendicular to the direction
     * (The coil nbrin will demicercles + 1 + 2 1 / 4 circle)
     * Mself.lbrin = length of a strand
     * Mself.radius = radius of rounded parts of the coil
     * Mself.delta = segments extremities connection between him and the coil even
     *
     * The equations are
     * Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin
     * Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius
     * Mself.lng = 2 * Mself.delta / / connections to the coil
     + (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last
     + (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded
     * Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit
     *
     * The constraints are:
     * Nbrin >= 2
     * Mself.radius < Mself.m_Size.x
     * Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord
     * Mself.lbrin> Mself.radius * 2
     *
     * The calculation is conducted in the following way:
     * Initially:
     * Nbrin = 2
     * Radius = 4 * m_Size.x (arbitrarily fixed value)
     * Then:
     * Increasing the number of segments to the desired length
     * (Radius decreases if necessary)
     */

    D_PAD*   pad;
    int      ll;
    wxString msg;

    auto pt = inductorPattern.m_End - inductorPattern.m_Start;
    int     min_len = KiROUND( EuclideanNorm( pt ) );
    inductorPattern.m_length = min_len;

    // Enter the desired length.
    msg = StringFromValue( g_UserUnit, inductorPattern.m_length );
    wxTextEntryDialog dlg( nullptr, wxEmptyString, _( "Length of Trace:" ), msg );

    if( dlg.ShowModal() != wxID_OK )
        return nullptr; // canceled by user

    msg = dlg.GetValue();
    inductorPattern.m_length = ValueFromString( g_UserUnit, msg );

    // Control values (ii = minimum length)
    if( inductorPattern.m_length < min_len )
    {
        aErrorMessage = _( "Requested length < minimum length" );
        return nullptr;
    }

    // Calculate the elements.
    std::vector <wxPoint> buffer;
    ll = BuildCornersList_S_Shape( buffer, inductorPattern.m_Start,
                                   inductorPattern.m_End, inductorPattern.m_length,
                                   inductorPattern.m_Width );

    if( !ll )
    {
        aErrorMessage = _( "Requested length too large" );
        return nullptr;
    }

    // Generate footprint. the value is also used as footprint name.
    msg = "L";
    wxTextEntryDialog cmpdlg( nullptr, wxEmptyString, _( "Component Value:" ), msg );
    cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );

    if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
        return nullptr;    //  Aborted by user

    MODULE* module = aPcbFrame->CreateNewModule( msg );

    // here the module is already in the BOARD, CreateNewModule() does that.
    module->SetFPID( LIB_ID( wxString( "mw_inductor" ) ) );
    module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
    module->ClearFlags();
    module->SetPosition( inductorPattern.m_End );

    // Generate segments
    for( unsigned jj = 1; jj < buffer.size(); jj++ )
    {
        EDGE_MODULE* PtSegm;
        PtSegm = new EDGE_MODULE( module );
        PtSegm->SetStart( buffer[jj - 1] );
        PtSegm->SetEnd( buffer[jj] );
        PtSegm->SetWidth( inductorPattern.m_Width );
        PtSegm->SetLayer( module->GetLayer() );
        PtSegm->SetShape( S_SEGMENT );
        PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
        PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
        module->GraphicalItemsList().PushBack( PtSegm );
    }

    // Place a pad on each end of coil.
    pad = new D_PAD( module );

    module->PadsList().PushFront( pad );

    pad->SetName( "1" );
    pad->SetPosition( inductorPattern.m_End );
    pad->SetPos0( pad->GetPosition() - module->GetPosition() );

    pad->SetSize( wxSize( inductorPattern.m_Width, inductorPattern.m_Width ) );

    pad->SetLayerSet( LSET( module->GetLayer() ) );
    pad->SetAttribute( PAD_ATTRIB_SMD );
    pad->SetShape( PAD_SHAPE_CIRCLE );

    D_PAD* newpad = new D_PAD( *pad );

    module->PadsList().Insert( newpad, pad->Next() );

    pad = newpad;
    pad->SetName( "2" );
    pad->SetPosition( inductorPattern.m_Start );
    pad->SetPos0( pad->GetPosition() - module->GetPosition() );

    // Modify text positions.
    wxPoint refPos( ( inductorPattern.m_Start.x + inductorPattern.m_End.x ) / 2,
                    ( inductorPattern.m_Start.y + inductorPattern.m_End.y ) / 2 );

    wxPoint valPos = refPos;

    refPos.y -= module->Reference().GetTextSize().y;
    module->Reference().SetPosition( refPos );
    valPos.y += module->Value().GetTextSize().y;
    module->Value().SetPosition( valPos );

    module->CalculateBoundingBox();
    return module;
}
Esempio n. 2
0
MODULE* PCB_EDIT_FRAME::Create_MuWaveComponent( int shape_type )
{
    int      oX;
    D_PAD*   pad;
    MODULE*  module;
    wxString msg, cmp_name;
    int      pad_count = 2;
    int      angle     = 0;
    // Ref and value text size (O = use board default value.
    // will be set to a value depending on the footprint size, if possible
    int      text_size = 0;

    // Enter the size of the gap or stub
    int      gap_size = GetDesignSettings().GetCurrentTrackWidth();

    switch( shape_type )
    {
    case 0:
        msg = _( "Gap" );
        cmp_name = wxT( "muwave_gap" );
        text_size = gap_size;
        break;

    case 1:
        msg = _( "Stub" );
        cmp_name  = wxT( "muwave_stub" );
        text_size = gap_size;
        pad_count = 2;
        break;

    case 2:
        msg = _( "Arc Stub" );
        cmp_name  = wxT( "muwave_arcstub" );
        pad_count = 1;
        break;

    default:
        msg = wxT( "???" );
        break;
    }

    wxString          value = StringFromValue( g_UserUnit, gap_size );
    wxTextEntryDialog dlg( this, msg, _( "Create microwave module" ), value );

    if( dlg.ShowModal() != wxID_OK )
    {
        m_canvas->MoveCursorToCrossHair();
        return NULL; // cancelled by user
    }

    value    = dlg.GetValue();
    gap_size = ValueFromString( g_UserUnit, value );

    bool abort = false;

    if( shape_type == 2 )
    {
        double            fcoeff = 10.0, fval;
        msg.Printf( wxT( "%3.1f" ), angle / fcoeff );
        wxTextEntryDialog angledlg( this, _( "Angle in degrees:" ),
                                    _( "Create microwave module" ), msg );

        if( angledlg.ShowModal() != wxID_OK )
        {
            m_canvas->MoveCursorToCrossHair();
            return NULL; // cancelled by user
        }

        msg = angledlg.GetValue();

        if( !msg.ToDouble( &fval ) )
        {
            DisplayError( this, _( "Incorrect number, abort" ) );
            abort = true;
        }

        angle = std::abs( KiROUND( fval * fcoeff ) );

        if( angle > 1800 )
            angle = 1800;
    }

    if( abort )
    {
        m_canvas->MoveCursorToCrossHair();
        return NULL;
    }

    module = CreateMuWaveBaseFootprint( cmp_name, text_size, pad_count );
    pad    = module->Pads();

    switch( shape_type )
    {
    case 0:     //Gap :
        oX = -( gap_size + pad->GetSize().x ) / 2;
        pad->SetX0( oX );

        pad->SetX( pad->GetPos0().x + pad->GetPosition().x );

        pad = pad->Next();

        pad->SetX0( oX + gap_size + pad->GetSize().x );
        pad->SetX( pad->GetPos0().x + pad->GetPosition().x );
        break;

    case 1:     //Stub :
        pad->SetPadName( wxT( "1" ) );
        pad = pad->Next();
        pad->SetY0( -( gap_size + pad->GetSize().y ) / 2 );
        pad->SetSize( wxSize( pad->GetSize().x, gap_size ) );
        pad->SetY( pad->GetPos0().y + pad->GetPosition().y );
        break;

    case 2:     // Arc Stub created by a polygonal approach:
    {
        EDGE_MODULE* edge = new EDGE_MODULE( module );
        module->GraphicalItems().PushFront( edge );

        edge->SetShape( S_POLYGON );
        edge->SetLayer( F_Cu );

        int numPoints = (angle / 50) + 3;     // Note: angles are in 0.1 degrees
        std::vector<wxPoint>& polyPoints = edge->GetPolyPoints();
        polyPoints.reserve( numPoints );

        edge->m_Start0.y = -pad->GetSize().y / 2;

        polyPoints.push_back( wxPoint( 0, 0 ) );

        int theta = -angle / 2;

        for( int ii = 1; ii<numPoints - 1; ii++ )
        {
            wxPoint pt( 0, -gap_size );

            RotatePoint( &pt.x, &pt.y, theta );

            polyPoints.push_back( pt );

            theta += 50;

            if( theta > angle / 2 )
                theta = angle / 2;
        }

        // Close the polygon:
        polyPoints.push_back( polyPoints[0] );
    }
        break;

    default:
        break;
    }

    module->CalculateBoundingBox();
    GetBoard()->m_Status_Pcb = 0;
    OnModify();
    return module;
}
Esempio n. 3
0
MODULE* PCB_EDIT_FRAME::Genere_Self( wxDC* DC )
{
    D_PAD*   pad;
    int      ll;
    wxString msg;

    m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
    m_canvas->SetMouseCapture( NULL, NULL );

    if( s_inductor_pattern.m_Flag == false )
    {
        DisplayError( this, wxT( "Starting point not init.." ) );
        return NULL;
    }

    s_inductor_pattern.m_Flag = false;

    s_inductor_pattern.m_End = GetCrossHairPosition();

    wxPoint pt = s_inductor_pattern.m_End - s_inductor_pattern.m_Start;
    int     min_len = KiROUND( EuclideanNorm( pt ) );
    s_inductor_pattern.m_lenght = min_len;

    // Enter the desired length.
    msg = StringFromValue( g_UserUnit, s_inductor_pattern.m_lenght );
    wxTextEntryDialog dlg( this, wxEmptyString, _( "Length of Trace:" ), msg );

    if( dlg.ShowModal() != wxID_OK )
        return NULL; // canceled by user

    msg = dlg.GetValue();
    s_inductor_pattern.m_lenght = ValueFromString( g_UserUnit, msg );

    // Control values (ii = minimum length)
    if( s_inductor_pattern.m_lenght < min_len )
    {
        DisplayError( this, _( "Requested length < minimum length" ) );
        return NULL;
    }

    // Calculate the elements.
    s_inductor_pattern.m_Width = GetDesignSettings().GetCurrentTrackWidth();

    std::vector <wxPoint> buffer;
    ll = BuildCornersList_S_Shape( buffer, s_inductor_pattern.m_Start,
                                   s_inductor_pattern.m_End, s_inductor_pattern.m_lenght,
                                   s_inductor_pattern.m_Width );

    if( !ll )
    {
        DisplayError( this, _( "Requested length too large" ) );
        return NULL;
    }

    // Generate footprint. the value is also used as footprint name.
    msg.Empty();
    wxTextEntryDialog cmpdlg( this, wxEmptyString, _( "Component Value:" ), msg );
    cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );

    if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
        return NULL;    //  Aborted by user

    MODULE* module = CreateNewModule( msg );

    // here the module is already in the BOARD, CreateNewModule() does that.
    module->SetFPID( FPID( std::string( "mw_inductor" ) ) );
    module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
    module->ClearFlags();
    module->SetPosition( s_inductor_pattern.m_End );

    // Generate segments
    for( unsigned jj = 1; jj < buffer.size(); jj++ )
    {
        EDGE_MODULE* PtSegm;
        PtSegm = new EDGE_MODULE( module );
        PtSegm->SetStart( buffer[jj - 1] );
        PtSegm->SetEnd( buffer[jj] );
        PtSegm->SetWidth( s_inductor_pattern.m_Width );
        PtSegm->SetLayer( module->GetLayer() );
        PtSegm->SetShape( S_SEGMENT );
        PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
        PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
        module->GraphicalItems().PushBack( PtSegm );
    }

    // Place a pad on each end of coil.
    pad = new D_PAD( module );

    module->Pads().PushFront( pad );

    pad->SetPadName( wxT( "1" ) );
    pad->SetPosition( s_inductor_pattern.m_End );
    pad->SetPos0( pad->GetPosition() - module->GetPosition() );

    pad->SetSize( wxSize( s_inductor_pattern.m_Width, s_inductor_pattern.m_Width ) );

    pad->SetLayerSet( LSET( module->GetLayer() ) );
    pad->SetAttribute( PAD_ATTRIB_SMD );
    pad->SetShape( PAD_SHAPE_CIRCLE );

    D_PAD* newpad = new D_PAD( *pad );

    module->Pads().Insert( newpad, pad->Next() );

    pad = newpad;
    pad->SetPadName( wxT( "2" ) );
    pad->SetPosition( s_inductor_pattern.m_Start );
    pad->SetPos0( pad->GetPosition() - module->GetPosition() );

    // Modify text positions.
    SetMsgPanel( module );

    wxPoint refPos( ( s_inductor_pattern.m_Start.x + s_inductor_pattern.m_End.x ) / 2,
                    ( s_inductor_pattern.m_Start.y + s_inductor_pattern.m_End.y ) / 2 );

    wxPoint valPos = refPos;

    refPos.y -= module->Reference().GetSize().y;
    module->Reference().SetPosition( refPos );
    valPos.y += module->Value().GetSize().y;
    module->Value().SetPosition( valPos );

    module->CalculateBoundingBox();
    module->Draw( m_canvas, DC, GR_OR );

    return module;
}
Esempio n. 4
0
MODULE* PCB_EDIT_FRAME::Genere_Self( wxDC* DC )
{
    D_PAD*   pad;
    int      ll;
    wxString msg;

    m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
    m_canvas->SetMouseCapture( NULL, NULL );

    if( Self_On == 0 )
    {
        DisplayError( this, wxT( "Starting point not init.." ) );
        return NULL;
    }

    Self_On = 0;

    Mself.m_End = GetCrossHairPosition();

    wxPoint pt = Mself.m_End - Mself.m_Start;
    int     min_len = KiROUND( EuclideanNorm( pt ) );
    Mself.lng = min_len;

    // Enter the desired length.
    msg = StringFromValue( g_UserUnit, Mself.lng );
    wxTextEntryDialog dlg( this, _( "Length:" ), _( "Length" ), msg );

    if( dlg.ShowModal() != wxID_OK )
        return NULL; // canceled by user

    msg = dlg.GetValue();
    Mself.lng = ValueFromString( g_UserUnit, msg );

    // Control values (ii = minimum length)
    if( Mself.lng < min_len )
    {
        DisplayError( this, _( "Requested length < minimum length" ) );
        return NULL;
    }

    // Calculate the elements.
    Mself.m_Width = GetBoard()->GetCurrentTrackWidth();

    std::vector <wxPoint> buffer;
    ll = BuildCornersList_S_Shape( buffer, Mself.m_Start, Mself.m_End, Mself.lng, Mself.m_Width );

    if( !ll )
    {
        DisplayError( this, _( "Requested length too large" ) );
        return NULL;
    }

    // Generate module.
    MODULE* module;
    module = Create_1_Module( wxEmptyString );

    if( module == NULL )
        return NULL;

    // here the module is already in the BOARD, Create_1_Module() does that.
    module->SetFPID( FPID( std::string( "MuSelf" ) ) );
    module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
    module->ClearFlags();
    module->SetPosition( Mself.m_End );

    // Generate segments
    for( unsigned jj = 1; jj < buffer.size(); jj++ )
    {
        EDGE_MODULE* PtSegm;
        PtSegm = new EDGE_MODULE( module );
        PtSegm->SetStart( buffer[jj - 1] );
        PtSegm->SetEnd( buffer[jj] );
        PtSegm->SetWidth( Mself.m_Width );
        PtSegm->SetLayer( module->GetLayer() );
        PtSegm->SetShape( S_SEGMENT );
        PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
        PtSegm->SetEnd0(   PtSegm->GetEnd()   - module->GetPosition() );
        module->GraphicalItems().PushBack( PtSegm );
    }

    // Place a pad on each end of coil.
    pad = new D_PAD( module );

    module->Pads().PushFront( pad );

    pad->SetPadName( wxT( "1" ) );
    pad->SetPosition( Mself.m_End );
    pad->SetPos0( pad->GetPosition() - module->GetPosition() );

    pad->SetSize( wxSize( Mself.m_Width, Mself.m_Width ) );

    pad->SetLayerMask( GetLayerMask( module->GetLayer() ) );
    pad->SetAttribute( PAD_SMD );
    pad->SetShape( PAD_CIRCLE );

    D_PAD* newpad = new D_PAD( *pad );

    module->Pads().Insert( newpad, pad->Next() );

    pad = newpad;
    pad->SetPadName( wxT( "2" ) );
    pad->SetPosition( Mself.m_Start );
    pad->SetPos0( pad->GetPosition() - module->GetPosition() );

    // Modify text positions.
    SetMsgPanel( module );

    wxPoint refPos( ( Mself.m_Start.x + Mself.m_End.x ) / 2,
                    ( Mself.m_Start.y + Mself.m_End.y ) / 2 );

    wxPoint valPos = refPos;

    refPos.y -= module->Reference().GetSize().y;
    module->Reference().SetTextPosition( refPos );
    valPos.y += module->Value().GetSize().y;
    module->Value().SetTextPosition( valPos );
    module->Reference().SetPos0( module->Reference().GetTextPosition() - module->GetPosition() );
    module->Value().SetPos0( module->Value().GetTextPosition() - module->GetPosition() );

    module->CalculateBoundingBox();
    module->Draw( m_canvas, DC, GR_OR );

    return module;
}
Esempio n. 5
0
int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent )
{
    if( !m_frame->GetModel() )
        return 0;

    DIALOG_DXF_IMPORT dlg( m_frame );
    int dlgResult = dlg.ShowModal();

    const std::list<BOARD_ITEM*>& list = dlg.GetImportedItems();

    if( dlgResult != wxID_OK || list.empty() )
        return 0;

    VECTOR2I cursorPos = m_controls->GetCursorPosition();
    VECTOR2I delta = cursorPos - list.front()->GetPosition();

    // Add a VIEW_GROUP that serves as a preview for the new item
    SELECTION preview( m_view );
    BOARD_COMMIT commit( m_frame );

    // Build the undo list & add items to the current view
    for( auto item : list )
    {
        assert( item->Type() == PCB_LINE_T || item->Type() == PCB_TEXT_T );

        preview.Add( item );
    }

    BOARD_ITEM* firstItem = preview.Front();
    m_view->Add( &preview );

    m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
    m_controls->ShowCursor( true );
    m_controls->SetSnapping( true );

    SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );

    Activate();

    // Main loop: keep receiving events
    while( OPT_TOOL_EVENT evt = Wait() )
    {
        cursorPos = m_controls->GetCursorPosition();

        if( evt->IsMotion() )
        {
            delta = cursorPos - firstItem->GetPosition();

            for( auto item : preview )
                item->Move( wxPoint( delta.x, delta.y ) );

            m_view->Update( &preview );
        }

        else if( evt->Category() == TC_COMMAND )
        {
            // TODO it should be handled by EDIT_TOOL, so add items and select?
            if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
            {
                for( auto item : preview )
                    item->Rotate( wxPoint( cursorPos.x, cursorPos.y ),
                                 m_frame->GetRotationAngle() );

                m_view->Update( &preview );
            }
            else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
            {
                for( auto item : preview )
                    item->Flip( wxPoint( cursorPos.x, cursorPos.y ) );

                m_view->Update( &preview );
            }
            else if( evt->IsCancel() || evt->IsActivate() )
            {
                preview.FreeItems();
                break;
            }
        }

        else if ( evt->IsClick( BUT_RIGHT ) )
        {
            showContextMenu();
        }

        else if( evt->IsClick( BUT_LEFT ) )
        {
            // Place the drawing
            BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();

            for( auto item : preview )
            {
                if( m_editModules )
                {
                    // Modules use different types for the same things,
                    // so we need to convert imported items to appropriate classes.
                    BOARD_ITEM* converted = NULL;

                    switch( item->Type() )
                    {
                    case PCB_TEXT_T:
                    {
                        TEXTE_PCB* text = static_cast<TEXTE_PCB*>( item );
                        TEXTE_MODULE* textMod = new TEXTE_MODULE( (MODULE*) parent );
                        // Assignment operator also copies the item PCB_TEXT_T type,
                        // so it cannot be added to a module which handles PCB_MODULE_TEXT_T
                        textMod->SetPosition( text->GetPosition() );
                        textMod->SetText( text->GetText() );
                        textMod->SetSize( text->GetSize() );
                        textMod->SetThickness( text->GetThickness() );
                        textMod->SetOrientation( text->GetOrientation() );
                        textMod->SetTextPosition( text->GetTextPosition() );
                        textMod->SetSize( text->GetSize() );
                        textMod->SetMirrored( text->IsMirrored() );
                        textMod->SetAttributes( text->GetAttributes() );
                        textMod->SetItalic( text->IsItalic() );
                        textMod->SetBold( text->IsBold() );
                        textMod->SetHorizJustify( text->GetHorizJustify() );
                        textMod->SetVertJustify( text->GetVertJustify() );
                        textMod->SetMultilineAllowed( text->IsMultilineAllowed() );
                        converted = textMod;
                        break;
                    }

                    case PCB_LINE_T:
                    {
                        DRAWSEGMENT* seg = static_cast<DRAWSEGMENT*>( item );
                        EDGE_MODULE* modSeg = new EDGE_MODULE( (MODULE*) parent );

                        // Assignment operator also copies the item PCB_LINE_T type,
                        // so it cannot be added to a module which handles PCB_MODULE_EDGE_T
                        modSeg->SetWidth( seg->GetWidth() );
                        modSeg->SetStart( seg->GetStart() );
                        modSeg->SetEnd( seg->GetEnd() );
                        modSeg->SetAngle( seg->GetAngle() );
                        modSeg->SetShape( seg->GetShape() );
                        modSeg->SetType( seg->GetType() );
                        modSeg->SetBezControl1( seg->GetBezControl1() );
                        modSeg->SetBezControl2( seg->GetBezControl2() );
                        modSeg->SetBezierPoints( seg->GetBezierPoints() );
                        modSeg->SetPolyPoints( seg->GetPolyPoints() );
                        converted = modSeg;
                        break;
                    }

                    default:
                        assert( false );
                        break;
                    }

                    if( converted )
                        converted->SetLayer( item->GetLayer() );

                    delete item;
                    item = converted;
                }

                if( item )
                    commit.Add( item );
            }

            commit.Push( _( "Place a DXF drawing" ) );
            break;
        }
    }

    preview.Clear();

    m_controls->ShowCursor( false );
    m_controls->SetSnapping( false );
    m_controls->SetAutoPan( false );
    m_controls->CaptureCursor( false );
    m_view->Remove( &preview );

    return 0;
}
MODULE* GPCB_FPL_CACHE::parseMODULE( LINE_READER* aLineReader ) throw( IO_ERROR, PARSE_ERROR )
{
    #define TEXT_DEFAULT_SIZE  ( 40*IU_PER_MILS )
    #define OLD_GPCB_UNIT_CONV IU_PER_MILS

    // Old version unit = 1 mil, so conv_unit is 10 or 0.1
    #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS )

    int                   paramCnt;
    double                conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
    wxPoint               textPos;
    wxString              msg;
    wxArrayString         parameters;
    std::auto_ptr<MODULE> module( new MODULE( NULL ) );


    if( aLineReader->ReadLine() == NULL )
        THROW_IO_ERROR( "unexpected end of file" );

    parameters.Clear();
    parseParameters( parameters, aLineReader );
    paramCnt = parameters.GetCount();

    /* From the Geda PCB documentation, valid Element definitions:
     *   Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
     *   Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
     *   Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
     *   Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
     *   Element ("Desc" "Name" TX TY TDir TScale TNFlags)
     */

    if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
    {
        msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) );
        THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
                           aLineReader->LineNumber(), 0 );
    }

    if( paramCnt < 10 || paramCnt > 14 )
    {
        msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
        THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
                           aLineReader->LineNumber(), 0 );
    }

    // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
    if( parameters[1] == wxT( "(" ) )
        conv_unit = OLD_GPCB_UNIT_CONV;

    if( paramCnt > 10 )
    {
        module->SetDescription( parameters[3] );
        module->SetReference( parameters[4] );
    }
    else
    {
        module->SetDescription( parameters[2] );
        module->SetReference( parameters[3] );
    }

    // Read value
    if( paramCnt > 10 )
        module->SetValue( parameters[5] );
    // With gEDA/pcb, value is meaningful after instantiation, only, so it's
    // often empty in bare footprints.
    if( module->Value().GetText().IsEmpty() )
        module->Value().SetText( wxT( "Val**" ) );


    if( paramCnt == 14 )
    {
        textPos = wxPoint( parseInt( parameters[8], conv_unit ),
                           parseInt( parameters[9], conv_unit ) );
    }
    else
    {
        textPos = wxPoint( parseInt( parameters[6], conv_unit ),
                           parseInt( parameters[7], conv_unit ) );
    }

    int orientation = parseInt( parameters[paramCnt-4], 1.0 );
    module->Reference().SetOrientation( (orientation % 2) ? 900 : 0 );

    // Calculate size: default height is 40 mils, width 30 mil.
    // real size is:  default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
    int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
    thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
    int twsize = thsize * 30 / 40;
    int thickness = thsize / 8;

    // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
    // Compensate for this by shifting the insertion point instead of the
    // aligment, because alignment isn't changeable in the GUI.
    textPos.x = textPos.x + twsize * module->GetReference().Len() / 2;
    textPos.y += thsize / 2;

    // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
    // high/right. Compensate for similar appearance.
    textPos.x -= thsize / 10;
    textPos.y += thsize / 2;

    module->Reference().SetTextPosition( textPos );
    module->Reference().SetPos0( textPos );
    module->Reference().SetSize( wxSize( twsize, thsize ) );
    module->Reference().SetThickness( thickness );

    // gEDA/pcb shows only one of value/reference/description at a time. Which
    // one is selectable by a global menu setting. pcbnew needs reference as
    // well as value visible, so place the value right below the reference.
    module->Value().SetOrientation( module->Reference().GetOrientation() );
    module->Value().SetSize( module->Reference().GetSize() );
    module->Value().SetThickness( module->Reference().GetThickness() );
    textPos.y += thsize * 13 / 10;  // 130% line height
    module->Value().SetTextPosition( textPos );
    module->Value().SetPos0( textPos );

    while( aLineReader->ReadLine() )
    {
        parameters.Clear();
        parseParameters( parameters, aLineReader );

        if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
            continue;

        if( parameters[0] == wxT( ")" ) )
            break;

        paramCnt = parameters.GetCount();

        // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
        if( paramCnt > 3 )
        {
            if( parameters[1] == wxT( "(" ) )
                conv_unit = OLD_GPCB_UNIT_CONV;
            else
                conv_unit = NEW_GPCB_UNIT_CONV;
        }

        wxLogTrace( traceFootprintLibrary, wxT( "%s parameter count = %d." ),
                    GetChars( parameters[0] ), paramCnt );

        // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
        if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
        {
            if( paramCnt != 8 )
            {
                msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
                THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
                                   aLineReader->LineNumber(), 0 );
            }

            EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
            drawSeg->SetLayer( F_SilkS );
            drawSeg->SetShape( S_SEGMENT );
            drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
                                         parseInt( parameters[3], conv_unit ) ) );
            drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
                                       parseInt( parameters[5], conv_unit ) ) );
            drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) );
            drawSeg->SetDrawCoord();
            module->GraphicalItems().PushBack( drawSeg );
            continue;
        }

        // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
        if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
        {
            if( paramCnt != 10 )
            {
                msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
                THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
                                   aLineReader->LineNumber(), 0 );
            }

            // Pcbnew does know ellipse so we must have Width = Height
            EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
            drawSeg->SetLayer( F_SilkS );
            drawSeg->SetShape( S_ARC );
            module->GraphicalItems().PushBack( drawSeg );

            // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
            int     radius = ( parseInt( parameters[4], conv_unit ) +
                               parseInt( parameters[5], conv_unit ) ) / 2;

            wxPoint centre( parseInt( parameters[2], conv_unit ),
                            parseInt( parameters[3], conv_unit ) );

            drawSeg->SetStart0( centre );

            // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
            double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;

            // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
            double sweep_angle = parseInt( parameters[7], -10.0 );

            // Geda PCB does not support circles.
            if( sweep_angle == -3600.0 )
                drawSeg->SetShape( S_CIRCLE );

            // Angle value is clockwise in gpcb and Pcbnew.
            drawSeg->SetAngle( sweep_angle );
            drawSeg->SetEnd0( wxPoint( radius, 0 ) );

            // Calculate start point coordinate of arc
            wxPoint arcStart( drawSeg->GetEnd0() );
            RotatePoint( &arcStart, -start_angle );
            drawSeg->SetEnd0( centre + arcStart );
            drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) );
            drawSeg->SetDrawCoord();
            continue;
        }

        // Parse a Pad with no hole with format:
        //   Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
        //   Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
        //   Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
        //   Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
        if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
        {
            if( paramCnt < 10 || paramCnt > 13 )
            {
                msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
                THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
                                   aLineReader->LineNumber(), 0 );
            }

            D_PAD* pad = new D_PAD( module.get() );

            static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
            static const LSET pad_back(  3, B_Cu, B_Mask, B_Paste );

            pad->SetShape( PAD_SHAPE_RECT );
            pad->SetAttribute( PAD_ATTRIB_SMD );
            pad->SetLayerSet( pad_front );

            if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
                pad->SetLayerSet( pad_back );

            // Set the pad name:
            // Pcbnew pad name is used for electrical connection calculations.
            // Accordingly it should be mapped to gEDA's pin/pad number,
            // which is used for the same purpose.
            // gEDA also features a pin/pad "name", which is an arbitrary string
            // and set to the pin name of the netlist on instantiation. Many gEDA
            // bare footprints use identical strings for name and number, so this
            // can be a bit confusing.
            pad->SetPadName( parameters[paramCnt-3] );

            int x1 = parseInt( parameters[2], conv_unit );
            int x2 = parseInt( parameters[4], conv_unit );
            int y1 = parseInt( parameters[3], conv_unit );
            int y2 = parseInt( parameters[5], conv_unit );
            int width = parseInt( parameters[6], conv_unit );
            wxPoint delta( x2 - x1, y2 - y1 );
            double angle = atan2( (double)delta.y, (double)delta.x );

            // Get the pad clearance and the solder mask clearance.
            if( paramCnt == 13 )
            {
                int clearance = parseInt( parameters[7], conv_unit );
                // One of gEDA's oddities is that clearance between pad and polygon
                // is given as the gap on both sides of the pad together, so for
                // KiCad it has to halfed.
                pad->SetLocalClearance( clearance / 2 );

                // In GEDA, the mask value is the size of the hole in this
                // solder mask. In Pcbnew, it is a margin, therefore the distance
                // between the copper and the mask
                int maskMargin = parseInt( parameters[8], conv_unit );
                maskMargin = ( maskMargin - width ) / 2;
                pad->SetLocalSolderMaskMargin( maskMargin );
            }

            // Negate angle (due to Y reversed axis) and convert it to internal units
            angle = - RAD2DECIDEG( angle );
            pad->SetOrientation( KiROUND( angle ) );

            wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );

            pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
                                  width ) );

            padPos += module->GetPosition();
            pad->SetPos0( padPos );
            pad->SetPosition( padPos );

            if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
            {
                if( pad->GetSize().x == pad->GetSize().y )
                    pad->SetShape( PAD_SHAPE_CIRCLE );
                else
                    pad->SetShape( PAD_SHAPE_OVAL );
            }

            module->Add( pad );
            continue;
        }

        // Parse a Pin with through hole with format:
        //    Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
        //    Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
        //    Pin (aX aY Thickness Drill "Name" "Number" NFlags)
        //    Pin (aX aY Thickness Drill "Name" NFlags)
        //    Pin (aX aY Thickness "Name" NFlags)
        if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
        {
            if( paramCnt < 8 || paramCnt > 12 )
            {
                msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
                THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
                                   aLineReader->LineNumber(), 0 );
            }

            D_PAD* pad = new D_PAD( module.get() );

            pad->SetShape( PAD_SHAPE_CIRCLE );

            static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );

            pad->SetLayerSet( pad_set );

            if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
                pad->SetShape( PAD_SHAPE_RECT );

            // Set the pad name:
            // Pcbnew pad name is used for electrical connection calculations.
            // Accordingly it should be mapped to gEDA's pin/pad number,
            // which is used for the same purpose.
            pad->SetPadName( parameters[paramCnt-3] );

            wxPoint padPos( parseInt( parameters[2], conv_unit ),
                            parseInt( parameters[3], conv_unit ) );

            int padSize = parseInt( parameters[4], conv_unit );

            pad->SetSize( wxSize( padSize, padSize ) );

            int drillSize = 0;

            // Get the pad clearance, solder mask clearance, and drill size.
            if( paramCnt == 12 )
            {
                int clearance = parseInt( parameters[5], conv_unit );
                // One of gEDA's oddities is that clearance between pad and polygon
                // is given as the gap on both sides of the pad together, so for
                // KiCad it has to halfed.
                pad->SetLocalClearance( clearance / 2 );

                // In GEDA, the mask value is the size of the hole in this
                // solder mask. In Pcbnew, it is a margin, therefore the distance
                // between the copper and the mask
                int maskMargin = parseInt( parameters[6], conv_unit );
                maskMargin = ( maskMargin - padSize ) / 2;
                pad->SetLocalSolderMaskMargin( maskMargin );

                drillSize = parseInt( parameters[7], conv_unit );
            }
            else
            {
                drillSize = parseInt( parameters[5], conv_unit );
            }

            pad->SetDrillSize( wxSize( drillSize, drillSize ) );
            padPos += module->GetPosition();
            pad->SetPos0( padPos );
            pad->SetPosition( padPos );

            if( pad->GetShape() == PAD_SHAPE_CIRCLE  &&  pad->GetSize().x != pad->GetSize().y )
                pad->SetShape( PAD_SHAPE_OVAL );

            module->Add( pad );
            continue;
        }
    }

    // Recalculate the bounding box
    module->CalculateBoundingBox();
    return module.release();
}