int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent )
{
    BOARD* board = getModel<BOARD>();
    RN_DATA* ratsnest = board->GetRatsnest();

    BOARD_COMMIT commit( this );

    for( int i = 0; i < board->GetAreaCount(); ++i )
    {
        ZONE_CONTAINER* zone = board->GetArea( i );

        commit.Modify( zone );

        zone->SetIsFilled( false );
        zone->ClearFilledPolysList();
        ratsnest->Update( zone );
        getView()->Update( zone );
    }

    commit.Push( _( "Unfill All Zones" ) );

    ratsnest->Recalculate();

    return 0;
}
int PCB_EDITOR_CONTROL::ZoneUnfill( const TOOL_EVENT& aEvent )
{
    auto selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
    const auto& selection = selTool->GetSelection();
    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();

    BOARD_COMMIT commit( this );

    for( auto item : selection )
    {
        assert( item->Type() == PCB_ZONE_AREA_T );

        ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );

        commit.Modify( zone );

        zone->SetIsFilled( false );
        zone->ClearFilledPolysList();
        ratsnest->Update( zone );
        getView()->Update( zone );
    }

    commit.Push( _( "Unfill Zone" ) );

    ratsnest->Recalculate();

    return 0;
}
Exemple #3
0
int SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent )
{
    if( !selectCursor( true ) )
        return 0;

    auto item = dynamic_cast<BOARD_CONNECTED_ITEM*> ( m_selection.Front() );

    if( !item )
        return 0;

    std::list<BOARD_CONNECTED_ITEM*> itemsList;
    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
    int netCode = item->GetNetCode();

    clearSelection();
    ratsnest->GetNetItems( netCode, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) );

    for( BOARD_CONNECTED_ITEM* i : itemsList )
        select( i );

    // Inform other potentially interested tools
    if( itemsList.size() > 0 )
        m_toolMgr->ProcessEvent( SelectedEvent );

    return 0;
}
void EDIT_TOOL::updateRatsnest( bool aRedraw )
{
    const SELECTION& selection = m_selectionTool->GetSelection();
    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();

    ratsnest->ClearSimple();
    for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
    {
        BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );

        ratsnest->Update( item );

        if( aRedraw )
            ratsnest->AddSimple( item );
    }
}
int PCB_EDITOR_CONTROL::UpdateSelectionRatsnest( const TOOL_EVENT& aEvent )
{
    SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
    const SELECTION& selection = selTool->GetSelection();
    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();

    // Update "simple" ratsnest, computed for currently modified items
    ratsnest->ClearSimple();

    for( auto item : selection )
    {
        ratsnest->Update( static_cast<BOARD_ITEM*>( item ) );
        ratsnest->AddSimple( static_cast<BOARD_ITEM*>( item ) );
    }

    return 0;
}
int PCB_EDITOR_CONTROL::ZoneUnfillAll( const TOOL_EVENT& aEvent )
{
    BOARD* board = getModel<BOARD>();
    RN_DATA* ratsnest = board->GetRatsnest();

    for( int i = 0; i < board->GetAreaCount(); ++i )
    {
        ZONE_CONTAINER* zone = board->GetArea( i );
        zone->SetIsFilled( false );
        zone->ClearFilledPolysList();
        ratsnest->Update( zone );
        zone->ViewUpdate();
    }

    ratsnest->Recalculate();

    return 0;
}
Exemple #7
0
void EDIT_TOOL::processChanges( const PICKED_ITEMS_LIST* aList )
{
    KIGFX::VIEW* view = getView();
    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();

    for( unsigned int i = 0; i < aList->GetCount(); ++i )
    {
        UNDO_REDO_T operation = aList->GetPickedItemStatus( i );
        BOARD_ITEM* updItem = static_cast<BOARD_ITEM*>( aList->GetPickedItem( i ) );

        switch( operation )
        {
        case UR_CHANGED:
            ratsnest->Update( updItem );
            // fall through

        case UR_MODEDIT:
            updItem->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            break;

        case UR_DELETED:
            if( updItem->Type() == PCB_MODULE_T )
                static_cast<MODULE*>( updItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove,
                                                                             view, _1 ) );

            view->Remove( updItem );
            //ratsnest->Remove( updItem );  // this is done in BOARD::Remove
            break;

        case UR_NEW:
            if( updItem->Type() == PCB_MODULE_T )
                static_cast<MODULE*>( updItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Add,
                                                                             view, _1 ) );

            view->Add( updItem );
            //ratsnest->Add( updItem );     // this is done in BOARD::Add
            break;

        default:
            assert( false );    // Not handled
            break;
        }
    }
}
int PCB_EDITOR_CONTROL::ZoneUnfill( const TOOL_EVENT& aEvent )
{
    SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
    const SELECTION& selection = selTool->GetSelection();
    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();

    for( int i = 0; i < selection.Size(); ++i )
    {
        assert( selection.Item<BOARD_ITEM>( i )->Type() == PCB_ZONE_AREA_T );

        ZONE_CONTAINER* zone = selection.Item<ZONE_CONTAINER>( i );
        zone->SetIsFilled( false );
        zone->ClearFilledPolysList();
        ratsnest->Update( zone );
        zone->ViewUpdate();
    }

    ratsnest->Recalculate();

    return 0;
}
int SELECTION_TOOL::selectCopper( const TOOL_EVENT& aEvent )
{
    std::list<BOARD_CONNECTED_ITEM*> itemsList;
    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
    BOARD_CONNECTED_ITEM* item = m_selection.Item<BOARD_CONNECTED_ITEM>( 0 );

    clearSelection();
    ratsnest->GetConnectedItems( item, itemsList, (RN_ITEM_TYPE)( RN_TRACKS | RN_VIAS ) );

    BOOST_FOREACH( BOARD_CONNECTED_ITEM* i, itemsList )
        select( i );

    // Inform other potentially interested tools
    if( itemsList.size() > 0 )
    {
        TOOL_EVENT selectEvent( SelectedEvent );
        m_toolMgr->ProcessEvent( selectEvent );
    }

    return 0;
}
int PCB_EDITOR_CONTROL::ZoneMerge( const TOOL_EVENT& aEvent )
{
    SELECTION selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
    BOARD* board = getModel<BOARD>();
    RN_DATA* ratsnest = board->GetRatsnest();
    KIGFX::VIEW* view = getView();

    if( selection.Size() < 2 )
        return 0;

    PICKED_ITEMS_LIST changes;
    int netcode = -1;

    // Loop through all combinations
    for( int ia1 = 0; ia1 < selection.Size() - 1; ++ia1 )
    {
        ZONE_CONTAINER* curr_area = dynamic_cast<ZONE_CONTAINER*>( selection.Item<EDA_ITEM>( ia1 ) );

        if( !curr_area )
            continue;

        netcode = curr_area->GetNetCode();

        EDA_RECT b1 = curr_area->Outline()->GetBoundingBox();
        bool mod_ia1 = false;

        for( int ia2 = selection.Size() - 1; ia2 > ia1; --ia2 )
        {
            ZONE_CONTAINER* area2 = dynamic_cast<ZONE_CONTAINER*>( selection.Item<EDA_ITEM>( ia2 ) );

            if( !area2 )
                continue;

            if( area2->GetNetCode() != netcode )
                continue;

            if( curr_area->GetPriority() != area2->GetPriority() )
                continue;

            if( curr_area->GetIsKeepout() != area2->GetIsKeepout() )
                continue;

            if( curr_area->GetLayer() != area2->GetLayer() )
                continue;

            EDA_RECT b2 = area2->Outline()->GetBoundingBox();

            if( b1.Intersects( b2 ) )
            {
                EDA_ITEM* backup = curr_area->Clone();
                bool ret = board->TestAreaIntersection( curr_area, area2 );

                if( ret && board->CombineAreas( &changes, curr_area, area2 ) )
                {
                    mod_ia1 = true;
                    selection.items.RemovePicker( ia2 );

                    ITEM_PICKER picker( curr_area, UR_CHANGED );
                    picker.SetLink( backup );
                    changes.PushItem( picker );
                }
                else
                {
                    delete backup;
                }
            }
        }

        if( mod_ia1 )
            --ia1;     // if modified, we need to check it again
    }

    m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
    m_frame->SaveCopyInUndoList( changes, UR_UNSPECIFIED );

    for( unsigned i = 0; i < changes.GetCount(); ++i )
    {
        ITEM_PICKER picker = changes.GetItemWrapper( i );
        BOARD_ITEM* item = static_cast<BOARD_ITEM*>( picker.GetItem() );

        if( picker.GetStatus() == UR_DELETED )
        {
            view->Remove( item );
            ratsnest->Remove( item );
        }
        else if( picker.GetStatus() == UR_CHANGED )
        {
            item->ViewUpdate( KIGFX::VIEW_ITEM::ALL );
            m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, item );
        }
    }

    return 0;
}
Exemple #11
0
void PCB_EDIT_FRAME::ViewReloadBoard( const BOARD* aBoard ) const
{
    KIGFX::VIEW* view = GetGalCanvas()->GetView();
    view->Clear();

    // All of PCB drawing elements should be added to the VIEW
    // in order to be displayed

    // Load zones
    for( int i = 0; i < aBoard->GetAreaCount(); ++i )
    {
        view->Add( (KIGFX::VIEW_ITEM*) ( aBoard->GetArea( i ) ) );
    }

    // Load drawings
    for( BOARD_ITEM* drawing = aBoard->m_Drawings; drawing; drawing = drawing->Next() )
    {
        view->Add( drawing );
    }

    // Load tracks
    for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
    {
        view->Add( track );
    }

    // Load modules and its additional elements
    for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
    {
        // Load module's pads
        for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() )
        {
            view->Add( pad );
        }

        // Load module's drawing (mostly silkscreen)
        for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing;
                drawing = drawing->Next() )
        {
            view->Add( drawing );
        }

        // Load module's texts (name and value)
        view->Add( &module->Reference() );
        view->Add( &module->Value() );

        // Add the module itself
        view->Add( module );
    }

    // Segzones (equivalent of ZONE_CONTAINER for legacy boards)
    for( SEGZONE* zone = aBoard->m_Zone; zone; zone = zone->Next() )
    {
        view->Add( zone );
    }

    // Add an entry for the worksheet layout
    KIGFX::WORKSHEET_VIEWITEM* worksheet = new KIGFX::WORKSHEET_VIEWITEM(
        std::string( aBoard->GetFileName().mb_str() ),
        std::string( GetScreenDesc().mb_str() ),
        &GetPageSettings(), &GetTitleBlock() );
    BASE_SCREEN* screen = GetScreen();
    if( screen != NULL )
    {
        worksheet->SetSheetNumber( GetScreen()->m_ScreenNumber );
        worksheet->SetSheetCount( GetScreen()->m_NumberOfScreens );
    }

    view->Add( worksheet );

    // Add an entry for the ratsnest
    RN_DATA* ratsnest = aBoard->GetRatsnest();
    ratsnest->ProcessBoard();
    ratsnest->Recalculate();
    view->Add( new KIGFX::RATSNEST_VIEWITEM( ratsnest ) );

    view->SetPanBoundary( worksheet->ViewBBox() );
    view->RecacheAllItems( true );

    if( IsGalCanvasActive() )
        GetGalCanvas()->Refresh();
}
Exemple #12
0
int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
{
    const SELECTION& selection = m_selectionTool->GetSelection();
    PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();

    // Shall the selection be cleared at the end?
    bool unselect = selection.Empty();

    if( !hoverSelection( selection, false ) )
        return 0;

    // Tracks & vias are treated in a special way:
    if( ( SELECTION_CONDITIONS::OnlyTypes( m_tracksViasType ) )( selection ) )
    {
        DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection );

        if( dlg.ShowModal() )
        {
            RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();

            editFrame->OnModify();
            editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
            dlg.Apply();

            selection.ForAll<KIGFX::VIEW_ITEM>( boost::bind( &KIGFX::VIEW_ITEM::ViewUpdate, _1,
                                                KIGFX::VIEW_ITEM::ALL ) );
            selection.ForAll<BOARD_ITEM>( boost::bind( &RN_DATA::Update, ratsnest, _1 ) );
            ratsnest->Recalculate();
        }
    }
    else if( selection.Size() == 1 ) // Properties are displayed when there is only one item selected
    {
        // Display properties dialog
        BOARD_ITEM* item = selection.Item<BOARD_ITEM>( 0 );

        // Store the head of the undo list to compare if anything has changed
        std::vector<PICKED_ITEMS_LIST*>& undoList = editFrame->GetScreen()->m_UndoList.m_CommandsList;

        // Some of properties dialogs alter pointers, so we should deselect them
        m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
        STATUS_FLAGS flags = item->GetFlags();
        item->ClearFlags();

        // It is necessary to determine if anything has changed, so store the current undo save point
        PICKED_ITEMS_LIST* undoSavePoint = undoList.empty() ? NULL : undoList.back();

        // Display properties dialog provided by the legacy canvas frame
        editFrame->OnEditItemRequest( NULL, item );

        if( !undoList.empty() && undoList.back() != undoSavePoint )      // Undo buffer has changed
        {
            // Process changes stored after undoSavePoint
            processUndoBuffer( undoSavePoint );

            // Update the modified item
            item->ViewUpdate();
            RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
            ratsnest->Recalculate();

            // TODO OBSERVER! I miss you so much..
            m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
        }

        item->SetFlags( flags );
    }

    if( unselect )
        m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );

    return 0;
}
Exemple #13
0
int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
{
    KIGFX::VIEW_CONTROLS* controls = getViewControls();
    PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();

    VECTOR2I originalCursorPos = controls->GetCursorPosition();
    const SELECTION& selection = m_selectionTool->GetSelection();

    // Shall the selection be cleared at the end?
    bool unselect = selection.Empty();

    // Be sure that there is at least one item that we can modify. If nothing was selected before,
    // try looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection)
    if( !hoverSelection( selection ) )
        return 0;

    Activate();

    m_dragging = false;         // Are selected items being dragged?
    bool restore = false;       // Should items' state be restored when finishing the tool?
    bool lockOverride = false;

    // By default, modified items need to update their geometry
    m_updateFlag = KIGFX::VIEW_ITEM::GEOMETRY;

    controls->ShowCursor( true );

    // cumulative translation
    wxPoint totalMovement( 0, 0 );

    GRID_HELPER grid( editFrame );
    OPT_TOOL_EVENT evt = aEvent;

    // Main loop: keep receiving events
    do
    {
        if( evt->IsCancel() )
        {
            restore = true; // Cancelling the tool means that items have to be restored
            break;          // Finish
        }

        else if( evt->Action() == TA_UNDO_REDO )
        {
            unselect = true;
            break;
        }

        else if( evt->IsAction( &COMMON_ACTIONS::editActivate )
                 || evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
        {
            BOARD_ITEM* item = selection.Item<BOARD_ITEM>( 0 );

            if( m_dragging && evt->Category() == TC_MOUSE )
            {
                m_cursor = grid.BestSnapAnchor( evt->Position(), item );
                controls->ForceCursorPosition( true, m_cursor );

                wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) - item->GetPosition();
                totalMovement += movement;

                // Drag items to the current cursor position
                for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
                    selection.Item<BOARD_ITEM>( i )->Move( movement + m_offset );

                updateRatsnest( true );
            }
            else if( !m_dragging )    // Prepare to start dragging
            {
                if( !invokeInlineRouter() )
                {
                    m_selectionTool->SanitizeSelection();

                    if( selection.Empty() )
                        break;

                    // deal with locked items (override lock or abort the operation)
                    SELECTION_LOCK_FLAGS lockFlags = m_selectionTool->CheckLock();

                    if( lockFlags == SELECTION_LOCKED )
                        break;
                    else if( lockFlags == SELECTION_LOCK_OVERRIDE )
                        lockOverride = true;

                    // Save items, so changes can be undone
                    if( !isUndoInhibited() )
                    {
                        editFrame->OnModify();
                        editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
                    }

                    m_cursor = controls->GetCursorPosition();

                    if( selection.Size() == 1 )
                    {
                        // Set the current cursor position to the first dragged item origin, so the
                        // movement vector could be computed later
                        m_cursor = grid.BestDragOrigin( originalCursorPos, item );
                        grid.SetAuxAxes( true, m_cursor );
                    }
                    else
                    {
                        m_cursor = grid.Align( m_cursor );
                    }

                    controls->ForceCursorPosition( true, m_cursor );
                    controls->WarpCursor( m_cursor, true );

                    VECTOR2I o = VECTOR2I( item->GetPosition() );
                    m_offset.x = o.x - m_cursor.x;
                    m_offset.y = o.y - m_cursor.y;

                    controls->SetAutoPan( true );
                    m_dragging = true;
                    incUndoInhibit();
                }
            }

            selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
        }

        // Dispatch TOOL_ACTIONs
        else if( evt->Category() == TC_COMMAND )
        {
            if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
            {
                Rotate( aEvent );
            }
            else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
            {
                Flip( aEvent );

                // Flip causes change of layers
                enableUpdateFlag( KIGFX::VIEW_ITEM::LAYERS );
            }
            else if( evt->IsAction( &COMMON_ACTIONS::remove ) )
            {
                Remove( aEvent );

                break;       // exit the loop, as there is no further processing for removed items
            }
            else if( evt->IsAction( &COMMON_ACTIONS::duplicate ) )
            {
                // On duplicate, stop moving this item
                // The duplicate tool should then select the new item and start
                // a new move procedure
                break;
            }
            else if( evt->IsAction( &COMMON_ACTIONS::moveExact ) )
            {
                // Can't do this, because the selection will then contain
                // stale pointers and it will all go horribly wrong...
                //editFrame->RestoreCopyFromUndoList( dummy );
                //
                // So, instead, reset the position manually
                for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
                {
                    BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );
                    item->SetPosition( item->GetPosition() - totalMovement );

                    // And what about flipping and rotation?
                    // for now, they won't be undone, but maybe that is how
                    // it should be, so you can flip and move exact in the
                    // same action?
                }

                // This causes a double event, so we will get the dialogue
                // correctly, somehow - why does Rotate not?
                //MoveExact( aEvent );
                break;      // exit the loop - we move exactly, so we have finished moving
            }
        }

        else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
        {
            if( !lockOverride )
                break; // Finish

            lockOverride = false;
        }
    } while( evt = Wait() );

    if( m_dragging )
        decUndoInhibit();

    m_dragging = false;
    m_offset.x = 0;
    m_offset.y = 0;

    if( restore )
    {
        // Modifications have to be rollbacked, so restore the previous state of items
        wxCommandEvent dummy;
        editFrame->RestoreCopyFromUndoList( dummy );
    }
    else
    {
        // Changes are applied, so update the items
        selection.group->ItemsViewUpdate( m_updateFlag );
    }

    if( unselect )
        m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );

    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
    ratsnest->ClearSimple();
    ratsnest->Recalculate();

    controls->ShowCursor( false );
    controls->SetAutoPan( false );

    return 0;
}
int EDIT_TOOL::Main( TOOL_EVENT& aEvent )
{
    const SELECTION& selection = m_selectionTool->GetSelection();

    // Shall the selection be cleared at the end?
    bool unselect = selection.Empty();

    // Be sure that there is at least one item that we can modify
    if( !makeSelection( selection ) )
    {
        setTransitions();

        return 0;
    }

    Activate();

    m_dragging = false;         // Are selected items being dragged?
    bool restore = false;       // Should items' state be restored when finishing the tool?

    // By default, modified items need to update their geometry
    m_updateFlag = KIGFX::VIEW_ITEM::GEOMETRY;

    KIGFX::VIEW_CONTROLS* controls = getViewControls();
    PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
    controls->ShowCursor( true );
    controls->SetSnapping( true );
    controls->ForceCursorPosition( false );

    // Main loop: keep receiving events
    while( OPT_TOOL_EVENT evt = Wait() )
    {
        if( evt->IsCancel() )
        {
            restore = true; // Cancelling the tool means that items have to be restored
            break;          // Finish
        }

        else if( evt->Action() == TA_UNDO_REDO )
        {
            unselect = true;
            break;
        }

        // Dispatch TOOL_ACTIONs
        else if( evt->Category() == TC_COMMAND )
        {
            if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
            {
                Rotate( aEvent );
            }
            else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
            {
                Flip( aEvent );

                // Flip causes change of layers
                enableUpdateFlag( KIGFX::VIEW_ITEM::LAYERS );
            }
            else if( evt->IsAction( &COMMON_ACTIONS::remove ) )
            {
                Remove( aEvent );

                break;       // exit the loop, as there is no further processing for removed items
            }
        }

        else if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
        {
            m_cursor = controls->GetCursorPosition();

            if( m_dragging )
            {
                wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) -
                                   selection.Item<BOARD_ITEM>( 0 )->GetPosition();

                // Drag items to the current cursor position
                for( unsigned int i = 0; i < selection.items.GetCount(); ++i )
                    selection.Item<BOARD_ITEM>( i )->Move( movement + m_offset );

                updateRatsnest( true );
            }
            else    // Prepare to start dragging
            {
                if( m_selectionTool->CheckLock() || selection.Empty() )
                    break;

                // Save items, so changes can be undone
                editFrame->OnModify();
                editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );

                if( selection.Size() == 1 )
                {
                    // Set the current cursor position to the first dragged item origin, so the
                    // movement vector could be computed later
                    m_cursor = VECTOR2I( selection.Item<BOARD_ITEM>( 0 )->GetPosition() );
                    m_offset.x = 0;
                    m_offset.y = 0;
                }
                else
                {
                    VECTOR2D origin;

                    if( evt->IsDrag( BUT_LEFT ) )
                        origin = getView()->GetGAL()->GetGridPoint( evt->DragOrigin() );
                    else
                        origin = getViewControls()->GetCursorPosition();

                    // Update dragging offset (distance between cursor and the first dragged item)
                    m_offset = static_cast<BOARD_ITEM*>( selection.items.GetPickedItem( 0 ) )->GetPosition() -
                                                         wxPoint( origin.x, origin.y );
                }

                controls->SetAutoPan( true );
                m_dragging = true;
            }

            selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
        }

        else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
            break; // Finish
    }

    m_dragging = false;
    m_offset.x = 0;
    m_offset.y = 0;

    if( restore )
    {
        // Modifications have to be rollbacked, so restore the previous state of items
        wxCommandEvent dummy;
        editFrame->RestoreCopyFromUndoList( dummy );
    }
    else
    {
        // Changes are applied, so update the items
        selection.group->ItemsViewUpdate( m_updateFlag );
    }

    if( unselect )
        m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );

    RN_DATA* ratsnest = getModel<BOARD>()->GetRatsnest();
    ratsnest->ClearSimple();
    ratsnest->Recalculate();

    controls->ShowCursor( false );
    controls->SetSnapping( false );
    controls->SetAutoPan( false );

    setTransitions();

    return 0;
}
void DIALOG_GLOBAL_DELETION::AcceptPcbDelete( )
{
    bool gen_rastnest = false;

    m_Parent->SetCurItem( NULL );

    if( m_DelAlls->GetValue() )
    {
        m_Parent->Clear_Pcb( true );
    }
    else
    {
        if( !IsOK( this, _( "Are you sure you want to delete the selected items?" ) ) )
            return;

        BOARD*            pcb = m_Parent->GetBoard();
        PICKED_ITEMS_LIST pickersList;
        ITEM_PICKER       itemPicker( NULL, UR_DELETED );
        BOARD_ITEM*       item;
        BOARD_ITEM*       nextitem;
        RN_DATA*          ratsnest = pcb->GetRatsnest();

        LSET layers_filter = LSET().set();

        if( m_rbLayersOption->GetSelection() != 0 )     // Use current layer only
            layers_filter = LSET( ToLAYER_ID( m_currentLayer ) );

        if( m_DelZones->GetValue() )
        {
            int area_index = 0;
            item = pcb->GetArea( area_index );

            while( item )
            {
                if( layers_filter[item->GetLayer()] )
                {
                    itemPicker.SetItem( item );
                    pickersList.PushItem( itemPicker );
                    pcb->Remove( item );
                    item->ViewRelease();
                    ratsnest->Remove( item );
                    gen_rastnest = true;
                }
                else
                {
                    area_index++;
                }

                item = pcb->GetArea( area_index );
            }
        }

        if( m_DelDrawings->GetValue() || m_DelBoardEdges->GetValue() )
        {
            LSET masque_layer;

            if( m_DelDrawings->GetValue() )
                 masque_layer = LSET::AllNonCuMask().set( Edge_Cuts, false );

            if( m_DelBoardEdges->GetValue() )
                 masque_layer.set( Edge_Cuts );

            masque_layer &= layers_filter;

            for( item = pcb->m_Drawings; item; item = nextitem )
            {
                nextitem = item->Next();

                if( item->Type() == PCB_LINE_T  &&  masque_layer[item->GetLayer()] )
                {
                    itemPicker.SetItem( item );
                    pickersList.PushItem( itemPicker );
                    item->ViewRelease();
                    item->UnLink();
                }
            }
        }

        if( m_DelTexts->GetValue() )
        {
            LSET del_text_layers = layers_filter;

            for( item = pcb->m_Drawings; item; item = nextitem )
            {
                nextitem = item->Next();

                if( item->Type() == PCB_TEXT_T  &&  del_text_layers[item->GetLayer()] )
                {
                    itemPicker.SetItem( item );
                    pickersList.PushItem( itemPicker );
                    item->ViewRelease();
                    item->UnLink();
                }
            }
        }

        if( m_DelModules->GetValue() )
        {
            for( item = pcb->m_Modules; item; item = nextitem )
            {
                nextitem = item->Next();

                if( layers_filter[item->GetLayer()] &&
                    ( ( m_ModuleFilterNormal->GetValue() && !item->IsLocked() ) ||
                      ( m_ModuleFilterLocked->GetValue() && item->IsLocked() ) ) )
                {
                    itemPicker.SetItem( item );
                    pickersList.PushItem( itemPicker );
                    static_cast<MODULE*>( item )->RunOnChildren(
                            boost::bind( &KIGFX::VIEW_ITEM::ViewRelease, _1 ) );
                    ratsnest->Remove( item );
                    item->ViewRelease();
                    item->UnLink();
                    gen_rastnest = true;
                }
            }
        }

        if( m_DelTracks->GetValue() )
        {
            STATUS_FLAGS track_mask_filter = 0;

            if( !m_TrackFilterLocked->GetValue() )
                track_mask_filter |= TRACK_LOCKED;

            if( !m_TrackFilterAR->GetValue() )
                track_mask_filter |= TRACK_AR;

            TRACK* nexttrack;

            for( TRACK *track = pcb->m_Track; track; track = nexttrack )
            {
                nexttrack = track->Next();

                if( ( track->GetState( TRACK_LOCKED | TRACK_AR ) & track_mask_filter ) != 0 )
                    continue;

                if( ( track->GetState( TRACK_LOCKED | TRACK_AR ) == 0 ) &&
                    !m_TrackFilterNormal->GetValue() )
                    continue;

                if( ( track->Type() == PCB_VIA_T ) && !m_TrackFilterVias->GetValue() )
                    continue;

                if( ( track->GetLayerSet() & layers_filter ) == 0 )
                    continue;

                itemPicker.SetItem( track );
                pickersList.PushItem( itemPicker );
                track->ViewRelease();
                ratsnest->Remove( track );
                track->UnLink();
                gen_rastnest = true;
            }
        }

        if( pickersList.GetCount() )
            m_Parent->SaveCopyInUndoList( pickersList, UR_DELETED );

        if( m_DelMarkers->GetValue() )
            pcb->DeleteMARKERs();

        if( gen_rastnest )
            m_Parent->Compile_Ratsnest( NULL, true );

        if( m_Parent->IsGalCanvasActive() )
            pcb->GetRatsnest()->Recalculate();

    }

    m_Parent->GetCanvas()->Refresh();
    m_Parent->OnModify();

    EndModal( 1 );
}
void PCB_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool aRedoCommand,
                                             bool aRebuildRatsnet )
{
    BOARD_ITEM* item;
    bool        not_found = false;
    bool        reBuild_ratsnest = false;
    KIGFX::VIEW* view = GetGalCanvas()->GetView();
    RN_DATA* ratsnest = GetBoard()->GetRatsnest();

    // Undo in the reverse order of list creation: (this can allow stacked changes
    // like the same item can be changes and deleted in the same complex command

    bool build_item_list = true;    // if true the list of existing items must be rebuilt

    for( int ii = aList->GetCount() - 1; ii >= 0 ; ii-- )
    {
        item = (BOARD_ITEM*) aList->GetPickedItem( ii );
        wxASSERT( item );

        /* Test for existence of item on board.
         * It could be deleted, and no more on board:
         *   - if a call to SaveCopyInUndoList was forgotten in Pcbnew
         *   - in zones outlines, when a change in one zone merges this zone with an other
         * This test avoids a Pcbnew crash
         * Obviously, this test is not made for deleted items
         */
        UNDO_REDO_T status = aList->GetPickedItemStatus( ii );

        if( status != UR_DELETED )
        {
            if( build_item_list )
                // Build list of existing items, for integrity test
                TestForExistingItem( GetBoard(), NULL );

            build_item_list = false;

            if( !TestForExistingItem( GetBoard(), item ) )
            {
                // Remove this non existent item
                aList->RemovePicker( ii );
                ii++;       // the current item was removed, ii points now the next item
                            // decrement it because it will be incremented later
                not_found = true;
                continue;
            }
        }

        item->ClearFlags();

        // see if we must rebuild ratsnets and pointers lists
        switch( item->Type() )
        {
        case PCB_MODULE_T:
        case PCB_ZONE_AREA_T:
        case PCB_TRACE_T:
        case PCB_VIA_T:
            reBuild_ratsnest = true;
            break;

        default:
            break;
        }

        switch( aList->GetPickedItemStatus( ii ) )
        {
        case UR_CHANGED:    /* Exchange old and new data for each item */
        {
            BOARD_ITEM* image = (BOARD_ITEM*) aList->GetPickedItemLink( ii );

            // Remove all pads/drawings/texts, as they become invalid
            // for the VIEW after SwapData() called for modules
            if( item->Type() == PCB_MODULE_T )
            {
                MODULE* oldModule = static_cast<MODULE*>( item );
                oldModule->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, view, _1 ) );
            }
            ratsnest->Remove( item );

            item->SwapData( image );

            // Update all pads/drawings/texts, as they become invalid
            // for the VIEW after SwapData() called for modules
            if( item->Type() == PCB_MODULE_T )
            {
                MODULE* newModule = static_cast<MODULE*>( item );
                newModule->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1 ) );
            }
            ratsnest->Add( item );

            item->ClearFlags( SELECTED );
            item->ViewUpdate( KIGFX::VIEW_ITEM::LAYERS );
        }
        break;

        case UR_NEW:        /* new items are deleted */
            aList->SetPickedItemStatus( UR_DELETED, ii );
            GetBoard()->Remove( item );

            if( item->Type() == PCB_MODULE_T )
            {
                MODULE* module = static_cast<MODULE*>( item );
                module->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, view, _1 ) );
            }
            view->Remove( item );

            item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            break;

        case UR_DELETED:    /* deleted items are put in List, as new items */
            aList->SetPickedItemStatus( UR_NEW, ii );
            GetBoard()->Add( item );

            if( item->Type() == PCB_MODULE_T )
            {
                MODULE* module = static_cast<MODULE*>( item );
                module->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1) );
            }
            view->Add( item );

            item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            build_item_list = true;
            break;

        case UR_MOVED:
            item->Move( aRedoCommand ? aList->m_TransformPoint : -aList->m_TransformPoint );
            item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            ratsnest->Update( item );
            break;

        case UR_ROTATED:
            item->Rotate( aList->m_TransformPoint,
                          aRedoCommand ? m_rotationAngle : -m_rotationAngle );
            item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            ratsnest->Update( item );
            break;

        case UR_ROTATED_CLOCKWISE:
            item->Rotate( aList->m_TransformPoint,
                          aRedoCommand ? -m_rotationAngle : m_rotationAngle );
            item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            ratsnest->Update( item );
            break;

        case UR_FLIPPED:
            item->Flip( aList->m_TransformPoint );
            item->ViewUpdate( KIGFX::VIEW_ITEM::LAYERS );
            ratsnest->Update( item );
            break;

        default:
        {
            wxString msg;
            msg.Printf( wxT( "PutDataInPreviousState() error (unknown code %X)" ),
                        aList->GetPickedItemStatus( ii ) );
            wxMessageBox( msg );
        }
        break;
        }
    }

    if( not_found )
        wxMessageBox( wxT( "Incomplete undo/redo operation: some items not found" ) );

    // Rebuild pointers and ratsnest that can be changed.
    if( reBuild_ratsnest && aRebuildRatsnet )
    {
        if( IsGalCanvasActive() )
            ratsnest->Recalculate();
        else
            Compile_Ratsnest( NULL, true );
    }
}
Exemple #17
0
void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event )
{
    int        id = event.GetId();
    wxPoint    pos;

    INSTALL_UNBUFFERED_DC( dc, m_canvas );

    wxGetMousePosition( &pos.x, &pos.y );

    pos.y += 20;

    switch( id )
    {
    case wxID_CUT:
    case wxID_COPY:
    case ID_TOOLBARH_PCB_SELECT_LAYER:
    case ID_MODEDIT_PAD_SETTINGS:
    case ID_PCB_USER_GRID_SETUP:
    case ID_POPUP_PCB_ROTATE_TEXTEPCB:
    case ID_POPUP_PCB_EDIT_TEXTEPCB:
    case ID_POPUP_PCB_ROTATE_TEXTMODULE:
    case ID_POPUP_PCB_ROTATE_MODULE_CLOCKWISE:
    case ID_POPUP_PCB_ROTATE_MODULE_COUNTERCLOCKWISE:
    case ID_POPUP_PCB_EDIT_TEXTMODULE:
    case ID_POPUP_PCB_IMPORT_PAD_SETTINGS:
    case ID_POPUP_PCB_EXPORT_PAD_SETTINGS:
    case ID_POPUP_PCB_GLOBAL_IMPORT_PAD_SETTINGS:
    case ID_POPUP_PCB_STOP_CURRENT_DRAWING:
    case ID_POPUP_MODEDIT_EDIT_BODY_ITEM:
    case ID_POPUP_MODEDIT_EDIT_WIDTH_ALL_EDGE:
    case ID_POPUP_MODEDIT_EDIT_LAYER_ALL_EDGE:
    case ID_POPUP_MODEDIT_ENTER_EDGE_WIDTH:
    case ID_POPUP_PCB_DELETE_EDGE:
    case ID_POPUP_PCB_DELETE_TEXTMODULE:
    case ID_POPUP_PCB_DELETE_PAD:
    case ID_POPUP_DELETE_BLOCK:
    case ID_POPUP_PLACE_BLOCK:
    case ID_POPUP_ZOOM_BLOCK:
    case ID_POPUP_MIRROR_X_BLOCK:
    case ID_POPUP_ROTATE_BLOCK:
    case ID_POPUP_COPY_BLOCK:
        break;

    case ID_POPUP_CANCEL_CURRENT_COMMAND:
    default:
        if( m_canvas->IsMouseCaptured() )
        {
            //  for all other commands: stop the move in progress
            m_canvas->CallEndMouseCapture( &dc );
        }

        if( id != ID_POPUP_CANCEL_CURRENT_COMMAND )
            SetToolID( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor(), wxEmptyString );

        break;
    }

    switch( id )
    {
    case ID_EXIT:
        Close( true );
        break;

    case ID_MODEDIT_SELECT_CURRENT_LIB:
        {
            wxString library = SelectLibrary( GetCurrentLib() );

            if( library.size() )
            {
                Prj().SetRString( PROJECT::PCB_LIB_NICKNAME, library );
                updateTitle();
            }
        }
        break;

    case ID_OPEN_MODULE_VIEWER:
        {
            FOOTPRINT_VIEWER_FRAME* viewer = (FOOTPRINT_VIEWER_FRAME*) Kiway().Player( FRAME_PCB_MODULE_VIEWER, false );

            if( !viewer )
            {
                viewer = (FOOTPRINT_VIEWER_FRAME*) Kiway().Player( FRAME_PCB_MODULE_VIEWER, true );
                viewer->Show( true );
                viewer->Zoom_Automatique( false );
            }
            else
            {
                // On Windows, Raise() does not bring the window on screen, when iconized
                if( viewer->IsIconized() )
                    viewer->Iconize( false );

                viewer->Raise();

                // Raising the window does not set the focus on Linux.  This should work on
                // any platform.
                if( wxWindow::FindFocus() != viewer )
                    viewer->SetFocus();
            }
        }
        break;

    case ID_MODEDIT_DELETE_PART:
        DeleteModuleFromCurrentLibrary();
        break;

    case ID_MODEDIT_NEW_MODULE:
        {
            if( !Clear_Pcb( true ) )
                break;

            SetCrossHairPosition( wxPoint( 0, 0 ) );

            MODULE* module = CreateNewModule( wxEmptyString );

            if( module )        // i.e. if create module command not aborted
            {
                // Initialize data relative to nets and netclasses (for a new
                // module the defaults are used)
                // This is mandatory to handle and draw pads
                GetBoard()->BuildListOfNets();
                module->SetPosition( wxPoint( 0, 0 ) );

                if( GetBoard()->m_Modules )
                    GetBoard()->m_Modules->ClearFlags();

                Zoom_Automatique( false );
            }

            updateView();
            m_canvas->Refresh();

            GetScreen()->ClrModify();
        }
        break;

    case ID_MODEDIT_NEW_MODULE_FROM_WIZARD:
        {
            if( GetScreen()->IsModify() && !GetBoard()->IsEmpty() )
            {
                if( !IsOK( this,
                           _( "Current Footprint will be lost and this operation cannot be undone. Continue ?" ) ) )
                    break;
            }

            FOOTPRINT_WIZARD_FRAME* wizard = (FOOTPRINT_WIZARD_FRAME*) Kiway().Player(
                        FRAME_PCB_FOOTPRINT_WIZARD_MODAL, true );

            if( wizard->ShowModal( NULL, this ) )
            {
                // Creates the new footprint from python script wizard
                MODULE* module = wizard->GetBuiltFootprint();

                if( module == NULL )        // i.e. if create module command aborted
                    break;

                Clear_Pcb( false );

                SetCrossHairPosition( wxPoint( 0, 0 ) );

                //  Add the new object to board
                GetBoard()->Add( module, ADD_APPEND );

                // Initialize data relative to nets and netclasses (for a new
                // module the defaults are used)
                // This is mandatory to handle and draw pads
                GetBoard()->BuildListOfNets();
                module->SetPosition( wxPoint( 0, 0 ) );
                module->ClearFlags();

                Zoom_Automatique( false );
                updateView();
                m_canvas->Refresh();

                if( m_Draw3DFrame )
                    m_Draw3DFrame->NewDisplay();

                GetScreen()->ClrModify();
            }

            wizard->Destroy();
        }
        break;

    case ID_MODEDIT_SAVE_LIBMODULE:
        if( GetBoard()->m_Modules && GetCurrentLib().size() )
        {
            SaveFootprintInLibrary( GetCurrentLib(), GetBoard()->m_Modules, true, true );
            GetScreen()->ClrModify();
        }
        break;

    case ID_MODEDIT_INSERT_MODULE_IN_BOARD:
    case ID_MODEDIT_UPDATE_MODULE_IN_BOARD:
        {
            // update module in the current board,
            // not just add it to the board with total disregard for the netlist...
            PCB_EDIT_FRAME* pcbframe = (PCB_EDIT_FRAME*) Kiway().Player( FRAME_PCB, false );

            if( pcbframe == NULL )      // happens when the board editor is not active (or closed)
            {
                wxMessageBox( _("No board currently edited" ) );
                break;
            }

            BOARD*          mainpcb  = pcbframe->GetBoard();
            MODULE*         source_module  = NULL;
            MODULE*         module_in_edit = GetBoard()->m_Modules;

            // Search the old module (source) if exists
            // Because this source could be deleted when editing the main board...
            if( module_in_edit->GetLink() )        // this is not a new module ...
            {
                source_module = mainpcb->m_Modules;

                for( ; source_module != NULL; source_module = (MODULE*) source_module->Next() )
                {
                    if( module_in_edit->GetLink() == source_module->GetTimeStamp() )
                        break;
                }
            }

            if( ( source_module == NULL )
              && ( id == ID_MODEDIT_UPDATE_MODULE_IN_BOARD ) ) // source not found
            {
                wxString msg;
                msg.Printf( _( "Unable to find the footprint source on the main board" ) );
                msg << _( "\nCannot update the footprint" );
                DisplayError( this, msg );
                break;
            }

            if( ( source_module != NULL )
              && ( id == ID_MODEDIT_INSERT_MODULE_IN_BOARD ) ) // source not found
            {
                wxString msg;
                msg.Printf( _( "A footprint source was found on the main board" ) );
                msg << _( "\nCannot insert this footprint" );
                DisplayError( this, msg );
                break;
            }

            m_toolManager->RunAction( COMMON_ACTIONS::selectionClear, true );

            // Create the "new" module
            MODULE* newmodule = new MODULE( *module_in_edit );
            newmodule->SetParent( mainpcb );
            newmodule->SetLink( 0 );

            // Put the footprint in the main pcb linked list.
            mainpcb->Add( newmodule );

            if( source_module )         // this is an update command
            {
                // In the main board,
                // the new module replace the old module (pos, orient, ref, value
                // and connexions are kept)
                // and the source_module (old module) is deleted
                PICKED_ITEMS_LIST pickList;

                if( pcbframe->IsGalCanvasActive() )
                {
                    KIGFX::VIEW* view = pcbframe->GetGalCanvas()->GetView();
                    source_module->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, view, _1 ) );
                    view->Remove( source_module );
                }

                pcbframe->Exchange_Module( source_module, newmodule, &pickList );
                newmodule->SetTimeStamp( module_in_edit->GetLink() );

                if( pickList.GetCount() )
                    pcbframe->SaveCopyInUndoList( pickList, UR_UNSPECIFIED );
            }
            else        // This is an insert command
            {
                wxPoint cursor_pos = pcbframe->GetCrossHairPosition();

                pcbframe->SetCrossHairPosition( wxPoint( 0, 0 ) );
                pcbframe->PlaceModule( newmodule, NULL );
                newmodule->SetPosition( wxPoint( 0, 0 ) );
                pcbframe->SetCrossHairPosition( cursor_pos );
                newmodule->SetTimeStamp( GetNewTimeStamp() );
                pcbframe->SaveCopyInUndoList( newmodule, UR_NEW );
            }

            newmodule->ClearFlags();
            GetScreen()->ClrModify();
            pcbframe->SetCurItem( NULL );
            mainpcb->m_Status_Pcb = 0;

            if( pcbframe->IsGalCanvasActive() )
            {
                RN_DATA* ratsnest = pcbframe->GetBoard()->GetRatsnest();
                ratsnest->Update( newmodule );
                ratsnest->Recalculate();

                KIGFX::VIEW* view = pcbframe->GetGalCanvas()->GetView();
                newmodule->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1 ) );
                view->Add( newmodule );
                pcbframe->GetGalCanvas()->ForceRefresh();
            }
        }
        break;

    case ID_MODEDIT_IMPORT_PART:
        if( ! Clear_Pcb( true ) )
            break;                  // //this command is aborted

        SetCrossHairPosition( wxPoint( 0, 0 ) );
        Import_Module();

        if( GetBoard()->m_Modules )
            GetBoard()->m_Modules->ClearFlags();

        GetScreen()->ClrModify();
        Zoom_Automatique( false );
        m_canvas->Refresh();

        if( m_Draw3DFrame )
            m_Draw3DFrame->NewDisplay();

        break;

    case ID_MODEDIT_EXPORT_PART:
        if( GetBoard()->m_Modules )
            Export_Module( GetBoard()->m_Modules );
        break;

    case ID_MODEDIT_CREATE_NEW_LIB_AND_SAVE_CURRENT_PART:
        if( GetBoard()->m_Modules )
        {
            // CreateModuleLibrary() only creates a new library, does not save footprint
            wxString libPath = CreateNewLibrary();
            if( libPath.size() )
                SaveCurrentModule( &libPath );
        }
        break;

    case ID_MODEDIT_SHEET_SET:
        break;

    case ID_MODEDIT_LOAD_MODULE:
        wxLogDebug( wxT( "Loading module from library " ) + getLibPath() );

        if( ! Clear_Pcb( true ) )
            break;

        SetCrossHairPosition( wxPoint( 0, 0 ) );

        LoadModuleFromLibrary( GetCurrentLib(), Prj().PcbFootprintLibs(), true );

        if( GetBoard() && GetBoard()->m_Modules )
        {
            GetBoard()->m_Modules->ClearFlags();

            // if either m_Reference or m_Value are gone, reinstall them -
            // otherwise you cannot see what you are doing on board
            TEXTE_MODULE* ref = &GetBoard()->m_Modules->Reference();
            TEXTE_MODULE* val = &GetBoard()->m_Modules->Value();

            if( val && ref )
            {
                ref->SetType( TEXTE_MODULE::TEXT_is_REFERENCE );    // just in case ...

                if( ref->GetLength() == 0 )
                    ref->SetText( wxT( "Ref**" ) );

                val->SetType( TEXTE_MODULE::TEXT_is_VALUE );        // just in case ...

                if( val->GetLength() == 0 )
                    val->SetText( wxT( "Val**" ) );
            }
        }

        Zoom_Automatique( false );

        if( m_Draw3DFrame )
            m_Draw3DFrame->NewDisplay();

        GetScreen()->ClrModify();

        updateView();
        m_canvas->Refresh();

        break;

    case ID_MODEDIT_PAD_SETTINGS:
        InstallPadOptionsFrame( NULL );
        break;

    case ID_MODEDIT_CHECK:
        // Currently: not implemented
        break;

    case ID_MODEDIT_EDIT_MODULE_PROPERTIES:
        if( GetBoard()->m_Modules )
        {
            SetCurItem( GetBoard()->m_Modules );

            DIALOG_MODULE_MODULE_EDITOR dialog( this, (MODULE*) GetScreen()->GetCurItem() );

            int ret = dialog.ShowModal();
            GetScreen()->GetCurItem()->ClearFlags();
            GetBoard()->m_Modules.GetFirst()->ViewUpdate();

            if( ret > 0 )
                m_canvas->Refresh();
        }
        break;

    case ID_POPUP_CLOSE_CURRENT_TOOL:
        break;

    case ID_POPUP_CANCEL_CURRENT_COMMAND:
        break;

    case ID_POPUP_PCB_ROTATE_MODULE_COUNTERCLOCKWISE:
        m_canvas->MoveCursorToCrossHair();
        Rotate_Module( NULL, (MODULE*) GetScreen()->GetCurItem(), 900, true );
        m_canvas->Refresh();
        break;

    case ID_POPUP_PCB_ROTATE_MODULE_CLOCKWISE:
        m_canvas->MoveCursorToCrossHair();
        Rotate_Module( NULL, (MODULE*) GetScreen()->GetCurItem(), -900, true );
        m_canvas->Refresh();
        break;

    case ID_POPUP_PCB_EDIT_MODULE_PRMS:
        {
            DIALOG_MODULE_MODULE_EDITOR dialog( this, (MODULE*) GetScreen()->GetCurItem() );
            dialog.ShowModal();
            GetScreen()->GetCurItem()->ClearFlags();
            m_canvas->MoveCursorToCrossHair();
            m_canvas->Refresh();
        }
        break;

    case ID_POPUP_PCB_MOVE_PAD_REQUEST:
        m_canvas->MoveCursorToCrossHair();
        StartMovePad( (D_PAD*) GetScreen()->GetCurItem(), &dc, false );
        break;

    case ID_POPUP_PCB_EDIT_PAD:
        InstallPadOptionsFrame( (D_PAD*) GetScreen()->GetCurItem() );
        m_canvas->MoveCursorToCrossHair();
    break;

    case ID_POPUP_PCB_DELETE_PAD:
        SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
        DeletePad( (D_PAD*) GetScreen()->GetCurItem(), false );
        SetCurItem( NULL );
        m_canvas->MoveCursorToCrossHair();
        break;

    case ID_POPUP_PCB_DUPLICATE_ITEM:
        duplicateItems( false );
        break;

    case ID_POPUP_PCB_DUPLICATE_ITEM_AND_INCREMENT:
        duplicateItems( true );
        break;

    case ID_POPUP_PCB_MOVE_EXACT:
        moveExact();
        break;

    case ID_POPUP_PCB_CREATE_ARRAY:
        createArray();
        break;

    case ID_POPUP_PCB_IMPORT_PAD_SETTINGS:
        SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
        m_canvas->MoveCursorToCrossHair();
        Import_Pad_Settings( (D_PAD*) GetScreen()->GetCurItem(), true );
        break;

    case ID_POPUP_PCB_GLOBAL_IMPORT_PAD_SETTINGS:
        SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
        // Calls the global change dialog:
        DlgGlobalChange_PadSettings( (D_PAD*) GetScreen()->GetCurItem() );
        m_canvas->MoveCursorToCrossHair();
        break;

    case ID_POPUP_PCB_EXPORT_PAD_SETTINGS:
        m_canvas->MoveCursorToCrossHair();
        Export_Pad_Settings( (D_PAD*) GetScreen()->GetCurItem() );
        break;

    case ID_POPUP_PCB_EDIT_TEXTMODULE:
        InstallTextModOptionsFrame( static_cast<TEXTE_MODULE*>( GetScreen()->GetCurItem() ), &dc );
        m_canvas->MoveCursorToCrossHair();
        break;

    case ID_POPUP_PCB_MOVE_TEXTMODULE_REQUEST:
        m_canvas->MoveCursorToCrossHair();
        StartMoveTexteModule( static_cast<TEXTE_MODULE*>( GetScreen()->GetCurItem() ), &dc );
        break;

    case ID_POPUP_PCB_ROTATE_TEXTMODULE:
        RotateTextModule( static_cast<TEXTE_MODULE*>( GetScreen()->GetCurItem() ), &dc );
        m_canvas->MoveCursorToCrossHair();
        break;

    case ID_POPUP_PCB_DELETE_TEXTMODULE:
        SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
        DeleteTextModule( static_cast<TEXTE_MODULE*>( GetScreen()->GetCurItem() ) );
        SetCurItem( NULL );
        m_canvas->MoveCursorToCrossHair();
        break;

    case ID_POPUP_PCB_MOVE_EDGE:
        Start_Move_EdgeMod( static_cast<EDGE_MODULE*>( GetScreen()->GetCurItem() ), &dc );
        m_canvas->MoveCursorToCrossHair();
        break;

    case ID_POPUP_PCB_STOP_CURRENT_DRAWING:
        m_canvas->MoveCursorToCrossHair();

        if( GetScreen()->GetCurItem()->IsNew() )
        {
            End_Edge_Module( (EDGE_MODULE*) GetScreen()->GetCurItem() );
            SetCurItem( NULL );
        }
        break;

    case ID_POPUP_MODEDIT_ENTER_EDGE_WIDTH:
        {
            EDGE_MODULE* edge = NULL;

            if( GetScreen()->GetCurItem()
              && ( GetScreen()->GetCurItem()->Type() == PCB_MODULE_EDGE_T ) )
            {
                edge = (EDGE_MODULE*) GetScreen()->GetCurItem();
            }

            Enter_Edge_Width( edge );
            m_canvas->MoveCursorToCrossHair();

            if( edge )
                m_canvas->Refresh();
        }
        break;

    case  ID_POPUP_MODEDIT_EDIT_BODY_ITEM :
        m_canvas->MoveCursorToCrossHair();
        InstallFootprintBodyItemPropertiesDlg( (EDGE_MODULE*) GetScreen()->GetCurItem() );
        m_canvas->Refresh();
        break;

    case ID_POPUP_MODEDIT_EDIT_WIDTH_ALL_EDGE:
        m_canvas->MoveCursorToCrossHair();
        Edit_Edge_Width( NULL );
        m_canvas->Refresh();
        break;

    case ID_POPUP_MODEDIT_EDIT_LAYER_ALL_EDGE:
        m_canvas->MoveCursorToCrossHair();
        Edit_Edge_Layer( NULL );
        m_canvas->Refresh();
        break;

    case ID_POPUP_PCB_DELETE_EDGE:
        SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
        m_canvas->MoveCursorToCrossHair();
        RemoveStruct( GetScreen()->GetCurItem() );
        SetCurItem( NULL );
        break;

    case ID_MODEDIT_MODULE_ROTATE:
    case ID_MODEDIT_MODULE_MIRROR:
    case ID_MODEDIT_MODULE_MOVE_EXACT:
        SaveCopyInUndoList( GetBoard()->m_Modules, UR_MODEDIT );
        Transform( (MODULE*) GetScreen()->GetCurItem(), id );
        m_canvas->Refresh();
        break;

    case ID_PCB_DRAWINGS_WIDTHS_SETUP:
        InstallOptionsFrame( pos );
        break;

    case ID_PCB_PAD_SETUP:
        {
            BOARD_ITEM* item = GetCurItem();

            if( item )
            {
                if( item->Type() != PCB_PAD_T )
                    item = NULL;
            }

            InstallPadOptionsFrame( (D_PAD*) item );
        }
        break;

    case ID_PCB_USER_GRID_SETUP:
        InvokeDialogGrid();
        break;

    case ID_POPUP_PLACE_BLOCK:
        GetScreen()->m_BlockLocate.SetCommand( BLOCK_MOVE );
        m_canvas->SetAutoPanRequest( false );
        HandleBlockPlace( &dc );
        break;

    case ID_POPUP_COPY_BLOCK:
        GetScreen()->m_BlockLocate.SetCommand( BLOCK_COPY );
        GetScreen()->m_BlockLocate.SetMessageBlock( this );
        m_canvas->SetAutoPanRequest( false );
        HandleBlockPlace( &dc );
        break;

    case ID_POPUP_ZOOM_BLOCK:
        GetScreen()->m_BlockLocate.SetCommand( BLOCK_ZOOM );
        GetScreen()->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
        break;

    case ID_POPUP_DELETE_BLOCK:
        GetScreen()->m_BlockLocate.SetCommand( BLOCK_DELETE );
        GetScreen()->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
        break;

    case ID_POPUP_ROTATE_BLOCK:
        GetScreen()->m_BlockLocate.SetCommand( BLOCK_ROTATE );
        GetScreen()->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
        break;

    case ID_POPUP_MIRROR_X_BLOCK:
        GetScreen()->m_BlockLocate.SetCommand( BLOCK_MIRROR_X );
        GetScreen()->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
        break;

    case ID_POPUP_MOVE_BLOCK_EXACT:
        GetScreen()->m_BlockLocate.SetCommand( BLOCK_MOVE_EXACT );
        GetScreen()->m_BlockLocate.SetMessageBlock( this );
        HandleBlockEnd( &dc );
        break;

    case ID_GEN_IMPORT_DXF_FILE:
        if( GetBoard()->m_Modules )
        {
            InvokeDXFDialogModuleImport( this, GetBoard()->m_Modules );
            m_canvas->Refresh();
        }
        break;

    default:
        DisplayError( this,
                      wxT( "FOOTPRINT_EDIT_FRAME::Process_Special_Functions error" ) );
        break;
    }
}
void BOARD_COMMIT::Push( const wxString& aMessage )
{
    // Objects potentially interested in changes:
    PICKED_ITEMS_LIST undoList;
    KIGFX::VIEW* view = m_toolMgr->GetView();
    BOARD* board = (BOARD*) m_toolMgr->GetModel();
    PCB_BASE_FRAME* frame = (PCB_BASE_FRAME*) m_toolMgr->GetEditFrame();
    RN_DATA* ratsnest = board->GetRatsnest();
    std::set<EDA_ITEM*> savedModules;

    if( Empty() )
        return;

    for( COMMIT_LINE& ent : m_changes )
    {
        int changeType = ent.m_type & CHT_TYPE;
        int changeFlags = ent.m_type & CHT_FLAGS;
        BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );

        // Module items need to be saved in the undo buffer before modification
        if( m_editModules )
        {
            // Be sure that we are storing a module
            if( ent.m_item->Type() != PCB_MODULE_T )
                ent.m_item = ent.m_item->GetParent();

            // We have not saved the module yet, so let's create an entry
            if( savedModules.count( ent.m_item ) == 0 )
            {
                if( !ent.m_copy )
                {
                    assert( changeType != CHT_MODIFY );     // too late to make a copy..
                    ent.m_copy = ent.m_item->Clone();
                }

                assert( ent.m_item->Type() == PCB_MODULE_T );
                assert( ent.m_copy->Type() == PCB_MODULE_T );

                ITEM_PICKER itemWrapper( ent.m_item, UR_CHANGED );
                itemWrapper.SetLink( ent.m_copy );
                undoList.PushItem( itemWrapper );
                frame->SaveCopyInUndoList( undoList, UR_CHANGED );

                savedModules.insert( ent.m_item );
                static_cast<MODULE*>( ent.m_item )->SetLastEditTime();
            }
        }

        switch( changeType )
        {
            case CHT_ADD:
            {
                if( !m_editModules )
                {
                    undoList.PushItem( ITEM_PICKER( boardItem, UR_NEW ) );

                    if( !( changeFlags & CHT_DONE ) )
                        board->Add( boardItem );

                    //ratsnest->Add( boardItem );       // TODO currently done by BOARD::Add()

                    if( boardItem->Type() == PCB_MODULE_T )
                    {
                        MODULE* mod = static_cast<MODULE*>( boardItem );
                        mod->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1 ) );
                    }
                }
                else
                {
                    // modules inside modules are not supported yet
                    assert( boardItem->Type() != PCB_MODULE_T );

                    if( !( changeFlags & CHT_DONE ) )
                        board->m_Modules->Add( boardItem );
                }

                view->Add( boardItem );
                break;
            }

            case CHT_REMOVE:
            {
                if( !m_editModules )
                {
                    undoList.PushItem( ITEM_PICKER( boardItem, UR_DELETED ) );
                }

                switch( boardItem->Type() )
                {
                // Module items
                case PCB_PAD_T:
                case PCB_MODULE_EDGE_T:
                case PCB_MODULE_TEXT_T:
                {
                    // Do not allow footprint text removal when not editing a module
                    if( !m_editModules )
                        break;

                    bool remove = true;

                    if( boardItem->Type() == PCB_MODULE_TEXT_T )
                    {
                        TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( boardItem );

                        switch( text->GetType() )
                        {
                            case TEXTE_MODULE::TEXT_is_REFERENCE:
                                //DisplayError( frame, _( "Cannot delete component reference." ) );
                                remove = false;
                                break;

                            case TEXTE_MODULE::TEXT_is_VALUE:
                                //DisplayError( frame, _( "Cannot delete component value." ) );
                                remove = false;
                                break;

                            case TEXTE_MODULE::TEXT_is_DIVERS:    // suppress warnings
                                break;

                            default:
                                assert( false );
                                break;
                        }
                    }

                    if( remove )
                    {
                        view->Remove( boardItem );

                        if( !( changeFlags & CHT_DONE ) )
                        {
                            MODULE* module = static_cast<MODULE*>( boardItem->GetParent() );
                            assert( module && module->Type() == PCB_MODULE_T );
                            module->Delete( boardItem );
                        }

                        board->m_Status_Pcb = 0; // it is done in the legacy view (ratsnest perhaps?)
                    }

                    break;
                }

                // Board items
                case PCB_LINE_T:                // a segment not on copper layers
                case PCB_TEXT_T:                // a text on a layer
                case PCB_TRACE_T:               // a track segment (segment on a copper layer)
                case PCB_VIA_T:                 // a via (like track segment on a copper layer)
                case PCB_DIMENSION_T:           // a dimension (graphic item)
                case PCB_TARGET_T:              // a target (graphic item)
                case PCB_MARKER_T:              // a marker used to show something
                case PCB_ZONE_T:                // SEG_ZONE items are now deprecated
                case PCB_ZONE_AREA_T:
                    view->Remove( boardItem );

                    if( !( changeFlags & CHT_DONE ) )
                        board->Remove( boardItem );

                    //ratsnest->Remove( boardItem );    // currently done by BOARD::Remove()
                    break;

                case PCB_MODULE_T:
                {
                    // There are no modules inside a module yet
                    assert( !m_editModules );

                    MODULE* module = static_cast<MODULE*>( boardItem );
                    module->ClearFlags();
                    module->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, view, _1 ) );

                    view->Remove( module );

                    if( !( changeFlags & CHT_DONE ) )
                        board->Remove( module );

                    // Clear flags to indicate, that the ratsnest, list of nets & pads are not valid anymore
                    board->m_Status_Pcb = 0;
                }
                break;

                default:                        // other types do not need to (or should not) be handled
                    assert( false );
                    break;
                }
                break;
            }

            case CHT_MODIFY:
            {
                if( !m_editModules )
                {
                    ITEM_PICKER itemWrapper( boardItem, UR_CHANGED );
                    assert( ent.m_copy );
                    itemWrapper.SetLink( ent.m_copy );
                    undoList.PushItem( itemWrapper );
                }

                boardItem->ViewUpdate( KIGFX::VIEW_ITEM::ALL );
                ratsnest->Update( boardItem );
                break;
            }

            default:
                assert( false );
                break;
        }
    }

    if( !m_editModules )
        frame->SaveCopyInUndoList( undoList, UR_UNSPECIFIED );

    frame->OnModify();
    ratsnest->Recalculate();

    clear();
}
void BOARD_COMMIT::Revert()
{
    PICKED_ITEMS_LIST undoList;
    KIGFX::VIEW* view = m_toolMgr->GetView();
    BOARD* board = (BOARD*) m_toolMgr->GetModel();
    RN_DATA* ratsnest = board->GetRatsnest();

    for( auto it = m_changes.rbegin(); it != m_changes.rend(); ++it )
    {
        COMMIT_LINE& ent = *it;
        BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ent.m_item );
        BOARD_ITEM* copy = static_cast<BOARD_ITEM*>( ent.m_copy );

        switch( ent.m_type )
        {
        case CHT_ADD:
            if( item->Type() == PCB_MODULE_T )
            {
                MODULE* oldModule = static_cast<MODULE*>( item );
                oldModule->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, view, _1 ) );
            }

            view->Remove( item );
            ratsnest->Remove( item );
            break;

        case CHT_REMOVE:
            if( item->Type() == PCB_MODULE_T )
            {
                MODULE* newModule = static_cast<MODULE*>( item );
                newModule->RunOnChildren( boost::bind( &EDA_ITEM::ClearFlags, _1, SELECTED ) );
                newModule->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1 ) );
            }

            view->Add( item );
            ratsnest->Add( item );
            break;

        case CHT_MODIFY:
        {
            if( item->Type() == PCB_MODULE_T )
            {
                MODULE* oldModule = static_cast<MODULE*>( item );
                oldModule->RunOnChildren( boost::bind( &KIGFX::VIEW::Remove, view, _1 ) );
            }

            view->Remove( item );
            ratsnest->Remove( item );

            item->SwapData( copy );

            item->ClearFlags( SELECTED );

            // Update all pads/drawings/texts, as they become invalid
            // for the VIEW after SwapData() called for modules
            if( item->Type() == PCB_MODULE_T )
            {
                MODULE* newModule = static_cast<MODULE*>( item );
                newModule->RunOnChildren( boost::bind( &EDA_ITEM::ClearFlags, _1, SELECTED ) );
                newModule->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, view, _1 ) );
            }

            view->Add( item );
            ratsnest->Add( item );
            delete copy;
            break;
        }

        default:
            assert( false );
            break;
        }
    }

    ratsnest->Recalculate();

    clear();
}