From 13395d34dd0f004bdf92d076ea8ef3f47b17f4d8 Mon Sep 17 00:00:00 2001 From: Jean-Samuel Reynaud Date: Mon, 6 Feb 2017 08:39:32 +0100 Subject: [PATCH] Action plugins: handle undo/redo feature when running an action plugin. --- .../action_plugin_test_undoredo.py | 157 ++++++++++++++ pcbnew/swig/pcbnew_action_plugins.cpp | 196 +++++++++++++++++- 2 files changed, 344 insertions(+), 9 deletions(-) create mode 100644 demos/python_scripts_examples/action_plugin_test_undoredo.py diff --git a/demos/python_scripts_examples/action_plugin_test_undoredo.py b/demos/python_scripts_examples/action_plugin_test_undoredo.py new file mode 100644 index 0000000000..ca052ad255 --- /dev/null +++ b/demos/python_scripts_examples/action_plugin_test_undoredo.py @@ -0,0 +1,157 @@ + +from pcbnew import * +import random +import pprint + + +class testundoredo0(ActionPlugin): + + def defaults(self): + self.name = "Test Undo/Redo: Remove footprints" + self.category = "Test Undo/Redo" + self.description = "" + + def Run(self): + pcb = GetBoard() + + for module in pcb.GetModules(): + pcb.RemoveNative(module) + + +class testundoredo1(ActionPlugin): + + def defaults(self): + self.name = "Test Undo/Redo: Remove all board items" + self.category = "Test Undo/Redo" + self.description = "" + + def Run(self): + pcb = GetBoard() + + while pcb.GetAreaCount() > 0: + area = pcb.GetArea(0) + pcb.RemoveNative(area) + + for module in pcb.GetModules(): + pcb.RemoveNative(module) + + for track in pcb.GetTracks(): + pcb.RemoveNative(track) + + for draw in pcb.m_Drawings: + pcb.RemoveNative(draw) + + +class testundoredo2(ActionPlugin): + + def defaults(self): + self.name = "Test Undo/Redo: Generate random content" + self.category = "Test Undo/Redo" + self.description = "" + + def createFPCXModule(self,pads): + size_025_160mm = wxSizeMM(0.25,1.6) + size_150_200mm = wxSizeMM(1.50,2.0) + # create a new module, it's parent is our previously created pcb + module = MODULE(self.pcb) + module.SetReference("FPC"+str(pads)) # give it a reference name + module.Reference().SetPosition(wxPointMM(-1,-1)) + self.pcb.Add(module) # add it to our pcb + m_pos = wxPointMM(0,0)#random.randint(10,200),random.randint(10,200)) + module.SetPosition(m_pos) + + # create a pad array and add it to the module + + + def smdRectPad(module,size,pos,name): + pad = D_PAD(module) + pad.SetSize(size) + pad.SetShape(PAD_SHAPE_RECT) + pad.SetAttribute(PAD_ATTRIB_SMD) + pad.SetLayerSet(pad.SMDMask()) + pad.SetPosition(pos) + pad.SetPadName(name) + return pad + + for n in range (0,pads): + pad = smdRectPad(module,size_025_160mm,wxPointMM(0.5*n,0),str(n+1)) + module.Add(pad) + + + pad_s0 = smdRectPad(module,size_150_200mm,wxPointMM(-1.6,1.3),"0") + pad_s1 = smdRectPad(module,size_150_200mm,wxPointMM((pads-1)*0.5+1.6,1.3),"0") + module.Add(pad_s0) + module.Add(pad_s1) + + e = EDGE_MODULE(module) + e.SetStart0(wxPointMM(-1,0)) + e.SetEnd0(wxPointMM(0,0)) + e.SetWidth(FromMM(0.2)) + e.SetLayer(F_SilkS) + e.SetShape(S_SEGMENT) + module.Add(e) + module.SetPosition(wxPointMM(random.randint(20,200),random.randint(20,150))) + return module + + + + def Run(self): + self.pcb = GetBoard() + random.seed() + + for i in range(10): + seg = DRAWSEGMENT() + seg.SetLayer( random.choice([Edge_Cuts,Cmts_User,Eco1_User,Eco2_User]) ) + seg.SetStart( wxPointMM( random.randint(10,100), + random.randint(10,100) ) ) + seg.SetEnd( wxPointMM( random.randint(10,100), + random.randint(10,100) ) ) + self.pcb.Add( seg ) + + if i%2 == 0: + t = TRACK(None) + else: + t = VIA(None) + #t.SetLayerPair(segments['layerPair'][0],segments['layerPair'][1]) + t.SetViaType(VIA_THROUGH) + t.SetDrill(FromMM(random.randint(1,20)/10.0)) + t.SetStart(wxPointMM(random.randint(100,150),random.randint(100,150))) + t.SetEnd(wxPointMM(random.randint(100,150),random.randint(100,150))) + t.SetWidth(FromMM(random.randint(1,15)/10.0)) + t.SetLayer(random.choice([F_Cu,B_Cu])) + self.pcb.Add(t) + + self.createFPCXModule(random.randint(2,40)) + + +class testundoredo3(ActionPlugin): + + def defaults(self): + self.name = "Test Undo/Redo: Move elements randomly" + self.category = "Test Undo/Redo" + self.description = "" + + def Run(self): + pcb = GetBoard() + + for i in range(0,pcb.GetAreaCount()): + area = pcb.GetArea(i) + area.Move(wxPointMM(random.randint(-20,20),random.randint(-20,20))) + + for module in pcb.GetModules(): + module.Move(wxPointMM(random.randint(-20,20),random.randint(-20,20))) + if random.randint(0,10) > 5: + module.Flip(module.GetPosition()) + + for track in pcb.GetTracks(): + track.Move(wxPointMM(random.randint(-20,20),random.randint(-20,20))) + + for draw in pcb.m_Drawings: + draw.Move(wxPointMM(random.randint(-20,20),random.randint(-20,20))) + + + +testundoredo0().register() +testundoredo1().register() +testundoredo2().register() +testundoredo3().register() diff --git a/pcbnew/swig/pcbnew_action_plugins.cpp b/pcbnew/swig/pcbnew_action_plugins.cpp index 7118f7f283..2ecfbfb157 100644 --- a/pcbnew/swig/pcbnew_action_plugins.cpp +++ b/pcbnew/swig/pcbnew_action_plugins.cpp @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include @@ -176,14 +178,190 @@ void PCB_EDIT_FRAME::OnActionPlugin( wxCommandEvent& aEvent ) if( actionPlugin ) { - // TODO: Adding recovery point for jobs - // BOARD_COMMIT commit( this ); - // commit.Push( _( "External plugin" ) ); + PICKED_ITEMS_LIST itemsList; + BOARD* currentPcb = GetBoard(); + bool fromEmpty = false; - actionPlugin->Run(); + itemsList.m_Status = UR_CHANGED; OnModify(); + // Append tracks: + for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() ) + { + ITEM_PICKER picker( item, UR_CHANGED ); + itemsList.PushItem( picker ); + } + + // Append modules: + for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() ) + { + ITEM_PICKER picker( item, UR_CHANGED ); + itemsList.PushItem( picker ); + } + + // Append drawings + for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() ) + { + ITEM_PICKER picker( item, UR_CHANGED ); + itemsList.PushItem( picker ); + } + + // Append zones outlines + for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ ) + { + ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea( + ii ), UR_CHANGED ); + itemsList.PushItem( picker ); + } + + // Append zones segm: + for( BOARD_ITEM* item = currentPcb->m_Zone; item != NULL; item = item->Next() ) + { + ITEM_PICKER picker( item, UR_CHANGED ); + itemsList.PushItem( picker ); + } + + if( itemsList.GetCount() > 0 ) + SaveCopyInUndoList( itemsList, UR_CHANGED, wxPoint( 0.0, 0.0 ) ); + else + fromEmpty = true; + + itemsList.ClearItemsList(); + + // Execute plugin himself... + actionPlugin->Run(); + + currentPcb->m_Status_Pcb = 0; + + // Get back the undo buffer to fix some modifications + PICKED_ITEMS_LIST* oldBuffer = NULL; + + if( fromEmpty ) + { + oldBuffer = new PICKED_ITEMS_LIST(); + oldBuffer->m_Status = UR_NEW; + } + else + { + oldBuffer = GetScreen()->PopCommandFromUndoList(); + wxASSERT( oldBuffer ); + } + + // Try do discover what was modified + + PICKED_ITEMS_LIST deletedItemsList; + + // Found deleted modules + for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ ) + { + BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i ); + ITEM_PICKER picker( item, UR_DELETED ); + + wxASSERT( item ); + + switch( item->Type() ) + { + case PCB_NETINFO_T: + case PCB_MARKER_T: + case PCB_MODULE_T: + case PCB_TRACE_T: + case PCB_VIA_T: + case PCB_LINE_T: + case PCB_TEXT_T: + case PCB_DIMENSION_T: + case PCB_TARGET_T: + case PCB_ZONE_T: + + // If item has a list it's mean that the element is on the board + if( item->GetList() == NULL ) + { + deletedItemsList.PushItem( picker ); + } + + break; + + case PCB_ZONE_AREA_T: + { + bool zoneFound = false; + + for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ ) + zoneFound |= currentPcb->GetArea( ii ) == item; + + if( !zoneFound ) + { + deletedItemsList.PushItem( picker ); + } + + break; + } + + default: + wxString msg; + msg.Printf( wxT( "(PCB_EDIT_FRAME::OnActionPlugin) needs work: " + "BOARD_ITEM type (%d) not handled" ), + item->Type() ); + wxFAIL_MSG( msg ); + break; + } + } + + // Mark deleted elements in undolist + for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ ) + { + oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) ); + } + + // Find new modules + for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() ) + { + if( !oldBuffer->ContainsItem( item ) ) + { + ITEM_PICKER picker( item, UR_NEW ); + oldBuffer->PushItem( picker ); + } + } + + for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() ) + { + if( !oldBuffer->ContainsItem( item ) ) + { + ITEM_PICKER picker( item, UR_NEW ); + oldBuffer->PushItem( picker ); + } + } + + for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() ) + { + if( !oldBuffer->ContainsItem( item ) ) + { + ITEM_PICKER picker( item, UR_NEW ); + oldBuffer->PushItem( picker ); + } + } + + for( BOARD_ITEM* item = currentPcb->m_Zone; item != NULL; item = item->Next() ) + { + if( !oldBuffer->ContainsItem( item ) ) + { + ITEM_PICKER picker( item, UR_NEW ); + oldBuffer->PushItem( picker ); + } + } + + for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ ) + { + if( !oldBuffer->ContainsItem( (EDA_ITEM*) currentPcb->GetArea( ii ) ) ) + { + ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea( + ii ), UR_NEW ); + oldBuffer->PushItem( picker ); + } + } + + + GetScreen()->PushCommandToUndoList( oldBuffer ); + if( IsGalCanvasActive() ) { UseGalCanvas( GetGalCanvas() ); @@ -201,7 +379,7 @@ void PCB_EDIT_FRAME::RebuildActionPluginMenus() { wxMenu* actionMenu = GetMenuBar()->FindItem( ID_TOOLBARH_PCB_ACTION_PLUGIN )->GetSubMenu(); - if( !actionMenu ) // Should not occur. + if( !actionMenu ) // Should not occur. return; // First, remove existing submenus, if they are too many @@ -235,7 +413,7 @@ void PCB_EDIT_FRAME::RebuildActionPluginMenus() { wxMenuItem* item; - if( ii < (int)available_menus.size() ) + if( ii < (int) available_menus.size() ) { item = available_menus[ii]; item->SetItemLabel( ACTION_PLUGINS::GetAction( ii )->GetName() ); @@ -248,9 +426,9 @@ void PCB_EDIT_FRAME::RebuildActionPluginMenus() ACTION_PLUGINS::GetAction( ii )->GetDescription(), KiBitmap( hammer_xpm ) ); - Connect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED, - (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) & - PCB_EDIT_FRAME::OnActionPlugin ); + Connect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED, + (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) & + PCB_EDIT_FRAME::OnActionPlugin ); } ACTION_PLUGINS::SetActionMenu( ii, item->GetId() );