Example #1
0
int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
{
    const SELECTION& selection = m_selectionTool->GetSelection();

    if( !hoverSelection( selection ) )
        return 0;

    // Get a copy of the selected items set
    PICKED_ITEMS_LIST selectedItems = selection.items;
    PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();

    // As we are about to remove items, they have to be removed from the selection first
    m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );

    // Save them
    for( unsigned int i = 0; i < selectedItems.GetCount(); ++i )
        selectedItems.SetPickedItemStatus( UR_DELETED, i );

    editFrame->OnModify();
    editFrame->SaveCopyInUndoList( selectedItems, UR_DELETED );

    // And now remove
    for( unsigned int i = 0; i < selectedItems.GetCount(); ++i )
        remove( static_cast<BOARD_ITEM*>( selectedItems.GetPickedItem( i ) ) );

    getModel<BOARD>()->GetRatsnest()->Recalculate();

    return 0;
}
void POINT_EDITOR::removeCorner( EDIT_POINT* aPoint )
{
    EDA_ITEM* item = m_editPoints->GetParent();

    if( item->Type() == PCB_ZONE_AREA_T )
    {
        const SELECTION& selection = m_selectionTool->GetSelection();
        PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();

        ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
        CPolyLine* outline = zone->Outline();

        for( int i = 0; i < outline->GetCornersCount(); ++i )
        {
            if( VECTOR2I( outline->GetPos( i ) ) == aPoint->GetPosition() )
            {
                frame->OnModify();
                frame->SaveCopyInUndoList( selection.items, UR_CHANGED );
                outline->DeleteCorner( i );
                setEditedPoint( NULL );
                break;
            }
        }
    }
}
Example #3
0
int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent )
{
    const SELECTION& selection = m_selectionTool->GetSelection();

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

    if( !hoverSelection( selection ) )
        return 0;

    wxPoint translation;
    double rotation = 0;

    PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();

    DIALOG_MOVE_EXACT dialog( editFrame, translation, rotation );
    int ret = dialog.ShowModal();

    if( ret == wxID_OK )
    {
        if( !isUndoInhibited() )
        {
            editFrame->OnModify();
            // Record an action of move and rotate
            editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
        }

        VECTOR2I rp = selection.GetCenter();
        wxPoint rotPoint( rp.x, rp.y );

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

            item->Move( translation );
            item->Rotate( rotPoint, rotation );

            if( !m_dragging )
                item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
        }

        updateRatsnest( m_dragging );

        if( m_dragging )
            selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
        else
            getModel<BOARD>()->GetRatsnest()->Recalculate();

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

        m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
    }

    return 0;
}
int EDIT_TOOL::Flip( TOOL_EVENT& aEvent )
{
    const SELECTION& selection = m_selectionTool->GetSelection();
    PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();

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

    if( !makeSelection( selection ) || m_selectionTool->CheckLock() )
    {
        setTransitions();

        return 0;
    }

    wxPoint flipPoint = getModificationPoint( selection );

    if( !m_dragging )   // If it is being dragged, then it is already saved with UR_CHANGED flag
    {
        editFrame->OnModify();
        editFrame->SaveCopyInUndoList( selection.items, UR_FLIPPED, flipPoint );
    }

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

        item->Flip( flipPoint );

        if( !m_dragging )
            item->ViewUpdate( KIGFX::VIEW_ITEM::LAYERS );
    }

    updateRatsnest( m_dragging );

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

    if( m_dragging )
        selection.group->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
    else
        getModel<BOARD>()->GetRatsnest()->Recalculate();

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

    m_toolMgr->RunAction( COMMON_ACTIONS::pointEditorUpdate, true );
    setTransitions();

    return 0;
}
Example #5
0
int EDIT_TOOL::CreateArray( const TOOL_EVENT& aEvent )
{
    // first, check if we have a selection, or try to get one
    SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
    const SELECTION& selection = selTool->GetSelection();

    // Be sure that there is at least one item that we can modify
    if( !hoverSelection( selection ) )
        return 0;

    bool originalItemsModified = false;

    // we have a selection to work on now, so start the tool process

    PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
    editFrame->OnModify();

    if( m_editModules )
    {
        // Module editors do their undo point upfront for the whole module
        editFrame->SaveCopyInUndoList( editFrame->GetBoard()->m_Modules, UR_MODEDIT );
    }
    else
    {
        // We may also change the original item
        editFrame->SaveCopyInUndoList( selection.items, UR_CHANGED );
    }

    DIALOG_CREATE_ARRAY::ARRAY_OPTIONS* array_opts = NULL;

    VECTOR2I rp = selection.GetCenter();
    const wxPoint rotPoint( rp.x, rp.y );

    DIALOG_CREATE_ARRAY dialog( editFrame, rotPoint, &array_opts );
    int ret = dialog.ShowModal();

    if( ret == wxID_OK && array_opts != NULL )
    {
        PICKED_ITEMS_LIST newItemList;

        for( int i = 0; i < selection.Size(); ++i )
        {
            BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );

            if( !item )
                continue;

            wxString cachedString;

            if( item->Type() == PCB_MODULE_T )
            {
                cachedString = static_cast<MODULE*>( item )->GetReferencePrefix();
            }
            else if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
            {
                // Copy the text (not just take a reference
                cachedString = text->GetText();
            }

            // iterate across the array, laying out the item at the
            // correct position
            const unsigned nPoints = array_opts->GetArraySize();

            for( unsigned ptN = 0; ptN < nPoints; ++ptN )
            {
                BOARD_ITEM* newItem = NULL;

                if( ptN == 0 )
                    newItem = item;
                else
                {
                    // if renumbering, no need to increment
                    const bool increment = !array_opts->ShouldRenumberItems();

                    // Some items cannot be duplicated
                    // i.e. the ref and value fields of a footprint or zones
                    // therefore newItem can be null

                    if( m_editModules )
                        newItem = editFrame->GetBoard()->m_Modules->DuplicateAndAddItem( item, increment );
                    else
                    {
#if 0
                        // @TODO: see if we allow zone duplication here
                        // Duplicate zones is especially tricky (overlaping zones must be merged)
                        // so zones are not duplicated
                        if( item->Type() == PCB_ZONE_AREA_T )
                            newItem = NULL;
                        else
#endif
                            newItem = editFrame->GetBoard()->DuplicateAndAddItem( item, increment );
                    }

                    if( newItem )
                    {
                        array_opts->TransformItem( ptN, newItem, rotPoint );

                        m_toolMgr->RunAction( COMMON_ACTIONS::unselectItem, true, newItem );

                        newItemList.PushItem( newItem );

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

                        editFrame->GetGalCanvas()->GetView()->Add( newItem );
                        getModel<BOARD>()->GetRatsnest()->Update( newItem );
                    }
                }

                // set the number if needed:
                if( newItem && array_opts->ShouldRenumberItems() )
                {
                    switch( newItem->Type() )
                    {
                    case PCB_PAD_T:
                    {
                        const wxString padName = array_opts->GetItemNumber( ptN );
                        static_cast<D_PAD*>( newItem )->SetPadName( padName );

                        originalItemsModified = true;
                        break;
                    }
                    case PCB_MODULE_T:
                    {
                        const wxString moduleName = array_opts->GetItemNumber( ptN );
                        MODULE* module = static_cast<MODULE*>( newItem );
                        module->SetReference( cachedString + moduleName );

                        originalItemsModified = true;
                        break;
                    }
                    case PCB_MODULE_TEXT_T:
                    case PCB_TEXT_T:
                    {
                        EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( newItem );
                        if( text )
                            text->SetText( array_opts->InterpolateNumberIntoString( ptN, cachedString ) );

                        originalItemsModified = true;
                        break;
                    }
                    default:
                        // no renumbering of other items
                        break;
                    }
                }
            }
        }

        if( !m_editModules )
        {
            if( originalItemsModified )
            {
                // Update the appearance of the original items
                selection.group->ItemsViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
            }

            // Add all items as a single undo point for PCB editors
            // TODO: Can this be merged into the previous undo point (where
            //       we saved the original items)
            editFrame->SaveCopyInUndoList( newItemList, UR_NEW );
        }
    }

    getModel<BOARD>()->GetRatsnest()->Recalculate();

    return 0;
}
Example #6
0
int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
{
    bool increment = aEvent.IsAction( &COMMON_ACTIONS::duplicateIncrement );

    // first, check if we have a selection, or try to get one
    SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
    const SELECTION& selection = selTool->GetSelection();

    // Be sure that there is at least one item that we can modify
    if( !hoverSelection( selection ) )
        return 0;

    // we have a selection to work on now, so start the tool process

    PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
    editFrame->OnModify();

    // prevent other tools making undo points while the duplicate is going on
    // so that if you cancel, you don't get a duplicate object hiding over
    // the original
    incUndoInhibit();

    if( m_editModules )
        editFrame->SaveCopyInUndoList( editFrame->GetBoard()->m_Modules, UR_MODEDIT );

    std::vector<BOARD_ITEM*> old_items;

    for( int i = 0; i < selection.Size(); ++i )
    {
        BOARD_ITEM* item = selection.Item<BOARD_ITEM>( i );

        if( item )
            old_items.push_back( item );
    }

    for( unsigned i = 0; i < old_items.size(); ++i )
    {
        BOARD_ITEM* item = old_items[i];

        // Unselect the item, so we won't pick it up again
        // Do this first, so a single-item duplicate will correctly call
        // SetCurItem and show the item properties
        m_toolMgr->RunAction( COMMON_ACTIONS::unselectItem, true, item );

        BOARD_ITEM* new_item = NULL;

        if( m_editModules )
            new_item = editFrame->GetBoard()->m_Modules->DuplicateAndAddItem( item, increment );
        else
        {
#if 0
            // @TODO: see if we allow zone duplication here
            // Duplicate zones is especially tricky (overlaping zones must be merged)
            // so zones are not duplicated
            if( item->Type() != PCB_ZONE_AREA_T )
#endif
                new_item = editFrame->GetBoard()->DuplicateAndAddItem( item, increment );
        }

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

            editFrame->GetGalCanvas()->GetView()->Add( new_item );

            // Select the new item, so we can pick it up
            m_toolMgr->RunAction( COMMON_ACTIONS::selectItem, true, new_item );
        }
    }

    // record the new items as added
    if( !m_editModules )
        editFrame->SaveCopyInUndoList( selection.items, UR_NEW );

    editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
                               (int) old_items.size() ) );

    // pick up the selected item(s) and start moving
    // this works well for "dropping" copies around
    TOOL_EVENT evt = COMMON_ACTIONS::editActivate.MakeEvent();
    Main( evt );

    // and re-enable undos
    decUndoInhibit();

    return 0;
}
void DIALOG_PAD_PROPERTIES::PadPropertiesAccept( wxCommandEvent& event )
{
    if( !padValuesOK() )
        return;

    bool rastnestIsChanged = false;
    int  isign = m_isFlipped ? -1 : 1;

    transferDataToPad( m_padMaster );
    // m_padMaster is a pattern: ensure there is no net for this pad:
    m_padMaster->SetNetCode( NETINFO_LIST::UNCONNECTED );

    if( m_currentPad )   // Set current Pad parameters
    {
        wxSize  size;
        MODULE* module = m_currentPad->GetParent();

        m_parent->SaveCopyInUndoList( module, UR_CHANGED );
        module->SetLastEditTime();

        // redraw the area where the pad was, without pad (delete pad on screen)
        m_currentPad->SetFlags( DO_NOT_DRAW );
        m_parent->GetCanvas()->RefreshDrawingRect( m_currentPad->GetBoundingBox() );
        m_currentPad->ClearFlags( DO_NOT_DRAW );

        // Update values
        m_currentPad->SetShape( m_padMaster->GetShape() );
        m_currentPad->SetAttribute( m_padMaster->GetAttribute() );

        if( m_currentPad->GetPosition() != m_padMaster->GetPosition() )
        {
            m_currentPad->SetPosition( m_padMaster->GetPosition() );
            rastnestIsChanged = true;
        }

        // compute the pos 0 value, i.e. pad position for module with orientation = 0
        // i.e. relative to module origin (module position)
        wxPoint pt = m_currentPad->GetPosition() - module->GetPosition();

        RotatePoint( &pt, -module->GetOrientation() );

        m_currentPad->SetPos0( pt );

        m_currentPad->SetOrientation( m_padMaster->GetOrientation() * isign + module->GetOrientation() );

        m_currentPad->SetSize( m_padMaster->GetSize() );

        size = m_padMaster->GetDelta();
        size.y *= isign;
        m_currentPad->SetDelta( size );

        m_currentPad->SetDrillSize( m_padMaster->GetDrillSize() );
        m_currentPad->SetDrillShape( m_padMaster->GetDrillShape() );

        wxPoint offset = m_padMaster->GetOffset();
        offset.y *= isign;
        m_currentPad->SetOffset( offset );

        m_currentPad->SetPadToDieLength( m_padMaster->GetPadToDieLength() );

        if( m_currentPad->GetLayerSet() != m_padMaster->GetLayerSet() )
        {
            rastnestIsChanged = true;
            m_currentPad->SetLayerSet( m_padMaster->GetLayerSet() );
        }

        if( m_isFlipped )
            m_currentPad->SetLayerSet( FlipLayerMask( m_currentPad->GetLayerSet() ) );

        m_currentPad->SetPadName( m_padMaster->GetPadName() );

        wxString padNetname;

        // For PAD_HOLE_NOT_PLATED, ensure there is no net name selected
        if( m_padMaster->GetAttribute() != PAD_HOLE_NOT_PLATED  )
            padNetname = m_PadNetNameCtrl->GetValue();

        if( m_currentPad->GetNetname() != padNetname )
        {
            const NETINFO_ITEM* netinfo = m_board->FindNet( padNetname );

            if( !padNetname.IsEmpty() &&  netinfo == NULL )
            {
                DisplayError( NULL, _( "Unknown netname, netname not changed" ) );
            }
            else
            {
                rastnestIsChanged = true;
                m_currentPad->SetNetCode( netinfo->GetNet() );
            }
        }

        m_currentPad->SetLocalClearance( m_padMaster->GetLocalClearance() );
        m_currentPad->SetLocalSolderMaskMargin( m_padMaster->GetLocalSolderMaskMargin() );
        m_currentPad->SetLocalSolderPasteMargin( m_padMaster->GetLocalSolderPasteMargin() );
        m_currentPad->SetLocalSolderPasteMarginRatio( m_padMaster->GetLocalSolderPasteMarginRatio() );
        m_currentPad->SetZoneConnection( m_padMaster->GetZoneConnection() );
        m_currentPad->SetThermalWidth( m_padMaster->GetThermalWidth() );
        m_currentPad->SetThermalGap( m_padMaster->GetThermalGap() );

        module->CalculateBoundingBox();
        m_parent->SetMsgPanel( m_currentPad );

        // redraw the area where the pad was
        m_parent->GetCanvas()->RefreshDrawingRect( m_currentPad->GetBoundingBox() );
        m_parent->OnModify();
    }

    EndModal( wxID_OK );

    if( rastnestIsChanged )  // The net ratsnest must be recalculated
        m_board->m_Status_Pcb = 0;
}
void POINT_EDITOR::addCorner( const VECTOR2I& aBreakPoint )
{
    EDA_ITEM* item = m_editPoints->GetParent();
    const SELECTION& selection = m_selectionTool->GetSelection();

    if( item->Type() == PCB_ZONE_AREA_T )
    {
        getEditFrame<PCB_BASE_FRAME>()->OnModify();
        getEditFrame<PCB_BASE_FRAME>()->SaveCopyInUndoList( selection.items, UR_CHANGED );

        ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
        CPolyLine* outline = zone->Outline();

        // Handle the last segment, so other segments can be easily handled in a loop
        unsigned int nearestIdx = outline->GetCornersCount() - 1, nextNearestIdx = 0;
        SEG side( VECTOR2I( outline->GetPos( nearestIdx ) ),
                  VECTOR2I( outline->GetPos( nextNearestIdx ) ) );
        unsigned int nearestDist = side.Distance( aBreakPoint );

        for( int i = 0; i < outline->GetCornersCount() - 1; ++i )
        {
            side = SEG( VECTOR2I( outline->GetPos( i ) ), VECTOR2I( outline->GetPos( i + 1 ) ) );

            unsigned int distance = side.Distance( aBreakPoint );
            if( distance < nearestDist )
            {
                nearestDist = distance;
                nearestIdx = i;
                nextNearestIdx = i + 1;
            }
        }

        // Find the point on the closest segment
        VECTOR2I sideOrigin( outline->GetPos( nearestIdx ) );
        VECTOR2I sideEnd( outline->GetPos( nextNearestIdx ) );
        SEG nearestSide( sideOrigin, sideEnd );
        VECTOR2I nearestPoint = nearestSide.NearestPoint( aBreakPoint );

        // Do not add points that have the same coordinates as ones that already belong to polygon
        // instead, add a point in the middle of the side
        if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
            nearestPoint = ( sideOrigin + sideEnd ) / 2;

        outline->InsertCorner( nearestIdx, nearestPoint.x, nearestPoint.y );
    }

    else if( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T )
    {
        bool moduleEdge = item->Type() == PCB_MODULE_EDGE_T;
        PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();

        frame->OnModify();

        if( moduleEdge )
            frame->SaveCopyInUndoList( getModel<BOARD>()->m_Modules, UR_MODEDIT );
        else
            frame->SaveCopyInUndoList( selection.items, UR_CHANGED );

        DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );

        if( segment->GetShape() == S_SEGMENT )
        {
            SEG seg( segment->GetStart(), segment->GetEnd() );
            VECTOR2I nearestPoint = seg.NearestPoint( aBreakPoint );

            // Move the end of the line to the break point..
            segment->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );

            // and add another one starting from the break point
            DRAWSEGMENT* newSegment;

            if( moduleEdge )
            {
                EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( segment );
                assert( segment->GetParent()->Type() == PCB_MODULE_T );
                newSegment = new EDGE_MODULE( *edge );
                edge->SetLocalCoord();
            }
            else
            {
                newSegment = new DRAWSEGMENT( *segment );
            }

            newSegment->ClearSelected();
            newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
            newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );

            if( moduleEdge )
            {
                static_cast<EDGE_MODULE*>( newSegment )->SetLocalCoord();
                getModel<BOARD>()->m_Modules->Add( newSegment );
            }
            else
            {
                getModel<BOARD>()->Add( newSegment );
            }

            getView()->Add( newSegment );
        }
    }
}
void DialogEditModuleText::OnOkClick( wxCommandEvent& event )
{
    wxString msg;

    if ( m_module)
        m_parent->SaveCopyInUndoList( m_module, UR_CHANGED );

#ifndef USE_WX_OVERLAY
    if( m_dc )     //Erase old text on screen
    {
        m_currentText->Draw( m_parent->GetCanvas(), m_dc, GR_XOR,
                             (m_currentText->IsMoving()) ? MoveVector : wxPoint( 0, 0 ) );
    }
#endif

    m_currentText->SetText( m_Name->GetValue() );
    m_currentText->SetItalic( m_Style->GetSelection() == 1 );

    wxPoint tmp;

    msg = m_TxtPosCtrlX->GetValue();
    tmp.x = ValueFromString( g_UserUnit, msg );

    msg = m_TxtPosCtrlY->GetValue();
    tmp.y = ValueFromString( g_UserUnit, msg );

    m_currentText->SetPos0( tmp );

    wxSize textSize( wxSize( ValueFromString( g_UserUnit, m_TxtSizeCtrlX->GetValue() ),
                             ValueFromString( g_UserUnit, m_TxtSizeCtrlY->GetValue() ) ) );

    // Test for a reasonnable size:
    if( textSize.x < TEXTS_MIN_SIZE )
        textSize.x = TEXTS_MIN_SIZE;
    if( textSize.y < TEXTS_MIN_SIZE )
        textSize.y = TEXTS_MIN_SIZE;

    m_currentText->SetSize( textSize ),

    msg = m_TxtWidthCtlr->GetValue();
    int width = ValueFromString( g_UserUnit, msg );

    // Test for a reasonnable width:
    if( width <= 1 )
        width = 1;

    int maxthickness = Clamp_Text_PenSize(width, m_currentText->GetSize() );

    if( width > maxthickness )
    {
        DisplayError( NULL,
                      _( "The text thickness is too large for the text size. It will be clamped" ) );
        width = maxthickness;
    }

    m_currentText->SetThickness( width );

    m_currentText->SetVisible( m_Show->GetSelection() == 0 );

    int text_orient = (m_Orient->GetSelection() == 0) ? 0 : 900;
    m_currentText->SetOrientation( text_orient );

    m_currentText->SetDrawCoord();

#ifndef USE_WX_OVERLAY
    if( m_dc )     // Display new text
    {
        m_currentText->Draw( m_parent->GetCanvas(), m_dc, GR_XOR,
                             (m_currentText->IsMoving()) ? MoveVector : wxPoint( 0, 0 ) );
    }
#else
    m_parent->Refresh();
#endif

    m_parent->OnModify();

    if( m_module )
        m_module->SetLastEditTime();

    EndModal(1);
}
Example #10
0
void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool aSetDirtyBit )
{
    // 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();
    auto              connectivity = board->GetConnectivity();
    std::set<EDA_ITEM*>      savedModules;
    std::vector<BOARD_ITEM*> itemsToDeselect;

    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 )
                {
                    wxASSERT( changeType != CHT_MODIFY );     // too late to make a copy..
                    ent.m_copy = ent.m_item->Clone();
                }

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

                if( aCreateUndoEntry )
                {
                    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 )
                {
                    if( aCreateUndoEntry )
                    {
                        undoList.PushItem( ITEM_PICKER( boardItem, UR_NEW ) );
                    }

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

                }
                else
                {
                    // modules inside modules are not supported yet
                    wxASSERT( boardItem->Type() != PCB_MODULE_T );

                    boardItem->SetParent( board->m_Modules.GetFirst() );

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

                view->Add( boardItem );
                break;
            }

            case CHT_REMOVE:
            {
                if( !m_editModules && aCreateUndoEntry )
                    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:
                    // This level can only handle module items when editing modules
                    if( !m_editModules )
                        break;

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

                        // don't allow deletion of Reference or Value
                        if( text->GetType() != TEXTE_MODULE::TEXT_is_DIVERS )
                            break;
                    }

                    view->Remove( boardItem );

                    if( !( changeFlags & CHT_DONE ) )
                    {
                        MODULE* module = static_cast<MODULE*>( boardItem->GetParent() );
                        wxASSERT( 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_AREA_T:
                    itemsToDeselect.push_back( boardItem );

                    view->Remove( boardItem );

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

                    break;

                case PCB_MODULE_T:
                {
                    itemsToDeselect.push_back( boardItem );

                    // There are no modules inside a module yet
                    wxASSERT( !m_editModules );

                    MODULE* module = static_cast<MODULE*>( boardItem );
                    view->Remove( module );
                    module->ClearFlags();

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

                    // 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
                    wxASSERT( false );
                    break;
                }

                break;
            }

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

                if( ent.m_copy )
                    connectivity->MarkItemNetAsDirty( static_cast<BOARD_ITEM*>( ent.m_copy ) );

                connectivity->Update( boardItem );
                view->Update( boardItem );

                // if no undo entry is needed, the copy would create a memory leak
                if( !aCreateUndoEntry )
                    delete ent.m_copy;

                break;
            }

            default:
                wxASSERT( false );
                break;
        }
    }

    // Removing an item should trigger the unselect action
    // but only after all items are removed otherwise we can get
    // flickering depending on the system
    if( itemsToDeselect.size() > 0 )
        m_toolMgr->RunAction( PCB_ACTIONS::unselectItems, true, &itemsToDeselect );

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

    if( TOOL_MANAGER* toolMgr = frame->GetToolManager() )
        toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );

    if ( !m_editModules )
    {
        auto panel = static_cast<PCB_DRAW_PANEL_GAL*>( frame->GetGalCanvas() );
        connectivity->RecalculateRatsnest();
        connectivity->ClearDynamicRatsnest();
        panel->RedrawRatsnest();
    }

    if( aSetDirtyBit )
        frame->OnModify();

    frame->UpdateMsgPanel();

    clear();
}
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();
}