You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

708 lines
22 KiB

5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
3 years ago
9 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 CERN
  5. * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <board.h>
  26. #include <footprint.h>
  27. #include <pcb_group.h>
  28. #include <tool/tool_manager.h>
  29. #include <tools/pcb_selection_tool.h>
  30. #include <tools/zone_filler_tool.h>
  31. #include <view/view.h>
  32. #include <board_commit.h>
  33. #include <tools/pcb_tool_base.h>
  34. #include <tools/pcb_actions.h>
  35. #include <connectivity/connectivity_data.h>
  36. #include <functional>
  37. using namespace std::placeholders;
  38. BOARD_COMMIT::BOARD_COMMIT( TOOL_MANAGER* aToolMgr ) :
  39. m_toolMgr( aToolMgr ),
  40. m_isFootprintEditor( false ),
  41. m_isBoardEditor( false ),
  42. m_resolveNetConflicts( false )
  43. {
  44. }
  45. BOARD_COMMIT::BOARD_COMMIT( PCB_TOOL_BASE* aTool ) :
  46. m_resolveNetConflicts( false )
  47. {
  48. m_toolMgr = aTool->GetManager();
  49. m_isFootprintEditor = aTool->IsFootprintEditor();
  50. m_isBoardEditor = aTool->IsBoardEditor();
  51. }
  52. BOARD_COMMIT::BOARD_COMMIT( EDA_DRAW_FRAME* aFrame ) :
  53. m_resolveNetConflicts( false )
  54. {
  55. m_toolMgr = aFrame->GetToolManager();
  56. m_isFootprintEditor = aFrame->IsType( FRAME_FOOTPRINT_EDITOR );
  57. m_isBoardEditor = aFrame->IsType( FRAME_PCB_EDITOR );
  58. }
  59. BOARD_COMMIT::~BOARD_COMMIT()
  60. {
  61. }
  62. BOARD* BOARD_COMMIT::GetBoard() const
  63. {
  64. return static_cast<BOARD*>( m_toolMgr->GetModel() );
  65. }
  66. COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType )
  67. {
  68. // if aItem belongs a footprint, the full footprint will be saved
  69. // because undo/redo does not handle "sub items" modifications
  70. if( aItem && aItem->Type() != PCB_FOOTPRINT_T && aChangeType == CHT_MODIFY )
  71. {
  72. EDA_ITEM* item = aItem->GetParent();
  73. if( item && item->Type() == PCB_FOOTPRINT_T ) // means aItem belongs a footprint
  74. aItem = item;
  75. }
  76. return COMMIT::Stage( aItem, aChangeType );
  77. }
  78. COMMIT& BOARD_COMMIT::Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType )
  79. {
  80. return COMMIT::Stage( container, aChangeType );
  81. }
  82. COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag )
  83. {
  84. return COMMIT::Stage( aItems, aModFlag );
  85. }
  86. void BOARD_COMMIT::dirtyIntersectingZones( BOARD_ITEM* item )
  87. {
  88. wxCHECK( item, /* void */ );
  89. ZONE_FILLER_TOOL* zoneFillerTool = m_toolMgr->GetTool<ZONE_FILLER_TOOL>();
  90. if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
  91. zoneFillerTool->DirtyZone( static_cast<ZONE*>( item ) );
  92. if( item->Type() == PCB_FOOTPRINT_T )
  93. {
  94. static_cast<FOOTPRINT*>( item )->RunOnChildren(
  95. [&]( BOARD_ITEM* child )
  96. {
  97. dirtyIntersectingZones( child );
  98. } );
  99. }
  100. else if( item->Type() == PCB_GROUP_T )
  101. {
  102. static_cast<PCB_GROUP*>( item )->RunOnChildren(
  103. [&]( BOARD_ITEM* child )
  104. {
  105. dirtyIntersectingZones( child );
  106. } );
  107. }
  108. else
  109. {
  110. BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  111. BOX2I bbox = item->GetBoundingBox();
  112. LSET layers = item->GetLayerSet();
  113. if( layers.test( Edge_Cuts ) || layers.test( Margin ) )
  114. layers = LSET::PhysicalLayersMask();
  115. else
  116. layers &= LSET::AllCuMask();
  117. if( layers.any() )
  118. {
  119. for( ZONE* zone : board->Zones() )
  120. {
  121. if( zone->GetIsRuleArea() )
  122. continue;
  123. if( ( zone->GetLayerSet() & layers ).any()
  124. && zone->GetBoundingBox().Intersects( bbox ) )
  125. {
  126. zoneFillerTool->DirtyZone( zone );
  127. }
  128. }
  129. }
  130. }
  131. }
  132. void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
  133. {
  134. // Objects potentially interested in changes:
  135. PICKED_ITEMS_LIST undoList;
  136. KIGFX::VIEW* view = m_toolMgr->GetView();
  137. BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  138. PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
  139. std::set<EDA_ITEM*> savedModules;
  140. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  141. bool itemsDeselected = false;
  142. bool solderMaskDirty = false;
  143. bool autofillZones = false;
  144. if( Empty() )
  145. return;
  146. // Note:
  147. // frame == nullptr happens in QA tests
  148. // in this case m_isBoardEditor and m_isFootprintEditor are set to false
  149. // But we also test frame == nullptr mainly to make Coverity happy
  150. std::vector<BOARD_ITEM*> bulkAddedItems;
  151. std::vector<BOARD_ITEM*> bulkRemovedItems;
  152. std::vector<BOARD_ITEM*> itemsChanged;
  153. if( m_isBoardEditor
  154. && !( aCommitFlags & ZONE_FILL_OP )
  155. && ( frame && frame->GetPcbNewSettings()->m_AutoRefillZones ) )
  156. {
  157. autofillZones = true;
  158. for( ZONE* zone : board->Zones() )
  159. zone->CacheBoundingBox();
  160. }
  161. for( COMMIT_LINE& ent : m_changes )
  162. {
  163. int changeType = ent.m_type & CHT_TYPE;
  164. int changeFlags = ent.m_type & CHT_FLAGS;
  165. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  166. wxASSERT( ent.m_item );
  167. // Module items need to be saved in the undo buffer before modification
  168. if( m_isFootprintEditor )
  169. {
  170. // Be sure that we are storing a footprint
  171. if( ent.m_item->Type() != PCB_FOOTPRINT_T )
  172. {
  173. ent.m_item = ent.m_item->GetParent();
  174. wxASSERT( ent.m_item );
  175. }
  176. // We have not saved the footprint yet, so let's create an entry
  177. if( savedModules.count( ent.m_item ) == 0 )
  178. {
  179. if( !ent.m_copy )
  180. {
  181. wxASSERT( changeType != CHT_MODIFY ); // too late to make a copy..
  182. ent.m_copy = makeImage( ent.m_item );
  183. }
  184. wxASSERT( ent.m_item->Type() == PCB_FOOTPRINT_T );
  185. wxASSERT( ent.m_copy->Type() == PCB_FOOTPRINT_T );
  186. if( !( aCommitFlags & SKIP_UNDO ) && frame )
  187. {
  188. ITEM_PICKER itemWrapper( nullptr, ent.m_item, UNDO_REDO::CHANGED );
  189. itemWrapper.SetLink( ent.m_copy );
  190. undoList.PushItem( itemWrapper );
  191. frame->SaveCopyInUndoList( undoList, UNDO_REDO::CHANGED );
  192. }
  193. savedModules.insert( ent.m_item );
  194. }
  195. }
  196. if( boardItem->Type() == PCB_VIA_T || boardItem->Type() == PCB_FOOTPRINT_T
  197. || boardItem->IsOnLayer( F_Mask ) || boardItem->IsOnLayer( B_Mask ) )
  198. {
  199. solderMaskDirty = true;
  200. }
  201. switch( changeType )
  202. {
  203. case CHT_ADD:
  204. {
  205. if( selTool && selTool->GetEnteredGroup() && !boardItem->GetParentGroup() )
  206. selTool->GetEnteredGroup()->AddItem( boardItem );
  207. if( m_isFootprintEditor )
  208. {
  209. // footprints inside footprints are not supported yet
  210. wxASSERT( boardItem->Type() != PCB_FOOTPRINT_T );
  211. boardItem->SetParent( board->Footprints().front() );
  212. if( !( changeFlags & CHT_DONE ) )
  213. board->Footprints().front()->Add( boardItem );
  214. }
  215. else if( boardItem->Type() == PCB_PAD_T
  216. || boardItem->Type() == PCB_FP_TEXT_T
  217. || boardItem->Type() == PCB_FP_TEXTBOX_T
  218. || boardItem->Type() == PCB_FP_SHAPE_T
  219. || boardItem->Type() == PCB_FP_DIM_ALIGNED_T
  220. || boardItem->Type() == PCB_FP_DIM_LEADER_T
  221. || boardItem->Type() == PCB_FP_DIM_CENTER_T
  222. || boardItem->Type() == PCB_FP_DIM_RADIAL_T
  223. || boardItem->Type() == PCB_FP_DIM_ORTHOGONAL_T
  224. || boardItem->Type() == PCB_FP_ZONE_T )
  225. {
  226. wxASSERT( boardItem->GetParent() &&
  227. boardItem->GetParent()->Type() == PCB_FOOTPRINT_T );
  228. }
  229. else
  230. {
  231. if( !( aCommitFlags & SKIP_UNDO ) )
  232. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::NEWITEM ) );
  233. if( !( changeFlags & CHT_DONE ) )
  234. {
  235. board->Add( boardItem, ADD_MODE::BULK_INSERT ); // handles connectivity
  236. bulkAddedItems.push_back( boardItem );
  237. }
  238. }
  239. if( autofillZones && boardItem->Type() != PCB_MARKER_T )
  240. dirtyIntersectingZones( boardItem );
  241. if( view && boardItem->Type() != PCB_NETINFO_T )
  242. view->Add( boardItem );
  243. break;
  244. }
  245. case CHT_REMOVE:
  246. {
  247. PCB_GROUP* parentGroup = boardItem->GetParentGroup();
  248. if( !m_isFootprintEditor && !( aCommitFlags & SKIP_UNDO ) )
  249. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::DELETED ) );
  250. if( boardItem->IsSelected() )
  251. {
  252. if( selTool )
  253. selTool->RemoveItemFromSel( boardItem, true /* quiet mode */ );
  254. itemsDeselected = true;
  255. }
  256. if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
  257. parentGroup->RemoveItem( boardItem );
  258. if( autofillZones )
  259. dirtyIntersectingZones( boardItem );
  260. switch( boardItem->Type() )
  261. {
  262. // Footprint items
  263. case PCB_PAD_T:
  264. case PCB_FP_SHAPE_T:
  265. case PCB_FP_TEXT_T:
  266. case PCB_FP_TEXTBOX_T:
  267. case PCB_FP_DIM_ALIGNED_T:
  268. case PCB_FP_DIM_LEADER_T:
  269. case PCB_FP_DIM_CENTER_T:
  270. case PCB_FP_DIM_RADIAL_T:
  271. case PCB_FP_DIM_ORTHOGONAL_T:
  272. case PCB_FP_ZONE_T:
  273. // This level can only handle footprint children in the footprint editor as
  274. // only in that case has the entire footprint (and all its children) already
  275. // been saved for undo.
  276. wxASSERT( m_isFootprintEditor );
  277. if( boardItem->Type() == PCB_FP_TEXT_T )
  278. {
  279. FP_TEXT* text = static_cast<FP_TEXT*>( boardItem );
  280. // don't allow deletion of Reference or Value
  281. if( text->GetType() != FP_TEXT::TEXT_is_DIVERS )
  282. break;
  283. }
  284. if( view )
  285. view->Remove( boardItem );
  286. if( !( changeFlags & CHT_DONE ) )
  287. {
  288. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem->GetParent() );
  289. wxASSERT( footprint && footprint->Type() == PCB_FOOTPRINT_T );
  290. footprint->Delete( boardItem );
  291. }
  292. break;
  293. // Board items
  294. case PCB_SHAPE_T: // a shape (normally not on copper layers)
  295. case PCB_BITMAP_T: // a bitmap on a user layer
  296. case PCB_TEXT_T: // a text on a layer
  297. case PCB_TEXTBOX_T: // a wrapped text on a layer
  298. case PCB_TRACE_T: // a track segment (segment on a copper layer)
  299. case PCB_ARC_T: // an arced track segment (segment on a copper layer)
  300. case PCB_VIA_T: // a via (like track segment on a copper layer)
  301. case PCB_DIM_ALIGNED_T: // a dimension (graphic item)
  302. case PCB_DIM_CENTER_T:
  303. case PCB_DIM_RADIAL_T:
  304. case PCB_DIM_ORTHOGONAL_T:
  305. case PCB_DIM_LEADER_T: // a leader dimension
  306. case PCB_TARGET_T: // a target (graphic item)
  307. case PCB_MARKER_T: // a marker used to show something
  308. case PCB_ZONE_T:
  309. if( view )
  310. view->Remove( boardItem );
  311. if( !( changeFlags & CHT_DONE ) )
  312. {
  313. board->Remove( boardItem, REMOVE_MODE::BULK );
  314. bulkRemovedItems.push_back( boardItem );
  315. }
  316. break;
  317. case PCB_FOOTPRINT_T:
  318. {
  319. // No support for nested footprints (yet)
  320. wxASSERT( !m_isFootprintEditor );
  321. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
  322. if( view )
  323. view->Remove( footprint );
  324. footprint->ClearFlags();
  325. if( !( changeFlags & CHT_DONE ) )
  326. {
  327. board->Remove( footprint, REMOVE_MODE::BULK ); // handles connectivity
  328. bulkRemovedItems.push_back( footprint );
  329. }
  330. }
  331. break;
  332. case PCB_GROUP_T:
  333. if( view )
  334. view->Remove( boardItem );
  335. if( !( changeFlags & CHT_DONE ) )
  336. {
  337. if( m_isFootprintEditor )
  338. board->GetFirstFootprint()->Remove( boardItem );
  339. else
  340. {
  341. board->Remove( boardItem, REMOVE_MODE::BULK );
  342. bulkRemovedItems.push_back( boardItem );
  343. }
  344. }
  345. break;
  346. // Metadata items
  347. case PCB_NETINFO_T:
  348. board->Remove( boardItem, REMOVE_MODE::BULK );
  349. bulkRemovedItems.push_back( boardItem );
  350. break;
  351. default: // other types do not need to (or should not) be handled
  352. wxASSERT( false );
  353. break;
  354. }
  355. break;
  356. }
  357. case CHT_MODIFY:
  358. {
  359. if( !m_isFootprintEditor && !( aCommitFlags & SKIP_UNDO ) )
  360. {
  361. ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
  362. wxASSERT( ent.m_copy );
  363. itemWrapper.SetLink( ent.m_copy );
  364. undoList.PushItem( itemWrapper );
  365. }
  366. if( !( aCommitFlags & SKIP_CONNECTIVITY ) )
  367. {
  368. std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
  369. if( ent.m_copy )
  370. connectivity->MarkItemNetAsDirty( static_cast<BOARD_ITEM*>( ent.m_copy ) );
  371. connectivity->Update( boardItem );
  372. }
  373. if( autofillZones )
  374. {
  375. dirtyIntersectingZones( static_cast<BOARD_ITEM*>( ent.m_copy )); // before
  376. dirtyIntersectingZones( boardItem ); // after
  377. }
  378. if( view )
  379. {
  380. view->Update( boardItem );
  381. if( m_isFootprintEditor )
  382. {
  383. static_cast<FOOTPRINT*>( boardItem )->RunOnChildren(
  384. [&]( BOARD_ITEM* aChild )
  385. {
  386. view->Update( aChild );
  387. });
  388. }
  389. }
  390. itemsChanged.push_back( boardItem );
  391. // if no undo entry is needed, the copy would create a memory leak
  392. if( aCommitFlags & SKIP_UNDO )
  393. delete ent.m_copy;
  394. break;
  395. }
  396. default:
  397. wxASSERT( false );
  398. break;
  399. }
  400. }
  401. if( bulkAddedItems.size() > 0 )
  402. board->FinalizeBulkAdd( bulkAddedItems );
  403. if( bulkRemovedItems.size() > 0 )
  404. board->FinalizeBulkRemove( bulkRemovedItems );
  405. if( itemsChanged.size() > 0 )
  406. board->OnItemsChanged( itemsChanged );
  407. if( m_isBoardEditor )
  408. {
  409. size_t num_changes = m_changes.size();
  410. if( !( aCommitFlags & SKIP_CONNECTIVITY ) )
  411. {
  412. std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
  413. if( m_resolveNetConflicts )
  414. connectivity->PropagateNets( this, PROPAGATE_MODE::RESOLVE_CONFLICTS );
  415. connectivity->RecalculateRatsnest( this );
  416. board->UpdateRatsnestExclusions();
  417. connectivity->ClearLocalRatsnest();
  418. }
  419. if( frame )
  420. {
  421. if( solderMaskDirty )
  422. frame->HideSolderMask();
  423. frame->GetCanvas()->RedrawRatsnest();
  424. }
  425. // Log undo items for any connectivity changes
  426. for( size_t i = num_changes; i < m_changes.size(); ++i )
  427. {
  428. COMMIT_LINE& ent = m_changes[i];
  429. wxASSERT( ( ent.m_type & CHT_TYPE ) == CHT_MODIFY );
  430. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  431. if( !( aCommitFlags & SKIP_UNDO ) )
  432. {
  433. ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
  434. wxASSERT( ent.m_copy );
  435. itemWrapper.SetLink( ent.m_copy );
  436. undoList.PushItem( itemWrapper );
  437. }
  438. else
  439. {
  440. delete ent.m_copy;
  441. }
  442. if( view )
  443. view->Update( boardItem );
  444. }
  445. }
  446. if( m_isBoardEditor && !( aCommitFlags & SKIP_UNDO ) && frame )
  447. {
  448. if( aCommitFlags & APPEND_UNDO )
  449. frame->AppendCopyToUndoList( undoList, UNDO_REDO::UNSPECIFIED );
  450. else
  451. frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNSPECIFIED );
  452. }
  453. m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );
  454. if( itemsDeselected )
  455. m_toolMgr->PostEvent( EVENTS::UnselectedEvent );
  456. if( autofillZones )
  457. m_toolMgr->RunAction( PCB_ACTIONS::zoneFillDirty );
  458. if( frame )
  459. {
  460. if( !( aCommitFlags & SKIP_SET_DIRTY ) )
  461. frame->OnModify();
  462. else
  463. frame->Update3DView( true, frame->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
  464. }
  465. clear();
  466. }
  467. EDA_ITEM* BOARD_COMMIT::parentObject( EDA_ITEM* aItem ) const
  468. {
  469. switch( aItem->Type() )
  470. {
  471. case PCB_PAD_T:
  472. case PCB_FP_SHAPE_T:
  473. case PCB_FP_TEXT_T:
  474. case PCB_FP_TEXTBOX_T:
  475. case PCB_FP_DIM_ALIGNED_T:
  476. case PCB_FP_DIM_LEADER_T:
  477. case PCB_FP_DIM_CENTER_T:
  478. case PCB_FP_DIM_RADIAL_T:
  479. case PCB_FP_DIM_ORTHOGONAL_T:
  480. case PCB_FP_ZONE_T:
  481. return aItem->GetParent();
  482. case PCB_ZONE_T:
  483. wxASSERT( !dynamic_cast<FOOTPRINT*>( aItem->GetParent() ) );
  484. return aItem;
  485. default:
  486. break;
  487. }
  488. return aItem;
  489. }
  490. EDA_ITEM* BOARD_COMMIT::makeImage( EDA_ITEM* aItem ) const
  491. {
  492. BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( aItem->Clone() );
  493. clone->SetParentGroup( nullptr );
  494. return clone;
  495. }
  496. void BOARD_COMMIT::Revert()
  497. {
  498. PICKED_ITEMS_LIST undoList;
  499. KIGFX::VIEW* view = m_toolMgr->GetView();
  500. BOARD* board = (BOARD*) m_toolMgr->GetModel();
  501. std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
  502. std::vector<BOARD_ITEM*> bulkAddedItems;
  503. std::vector<BOARD_ITEM*> bulkRemovedItems;
  504. std::vector<BOARD_ITEM*> itemsChanged;
  505. for( auto it = m_changes.rbegin(); it != m_changes.rend(); ++it )
  506. {
  507. COMMIT_LINE& ent = *it;
  508. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ent.m_item );
  509. BOARD_ITEM* copy = static_cast<BOARD_ITEM*>( ent.m_copy );
  510. int changeType = ent.m_type & CHT_TYPE;
  511. int changeFlags = ent.m_type & CHT_FLAGS;
  512. switch( changeType )
  513. {
  514. case CHT_ADD:
  515. if( !( changeFlags & CHT_DONE ) )
  516. break;
  517. view->Remove( item );
  518. connectivity->Remove( item );
  519. board->Remove( item, REMOVE_MODE::BULK );
  520. bulkRemovedItems.push_back( item );
  521. break;
  522. case CHT_REMOVE:
  523. if( !( changeFlags & CHT_DONE ) )
  524. break;
  525. view->Add( item );
  526. connectivity->Add( item );
  527. board->Add( item, ADD_MODE::INSERT );
  528. bulkAddedItems.push_back( item );
  529. break;
  530. case CHT_MODIFY:
  531. {
  532. view->Remove( item );
  533. connectivity->Remove( item );
  534. item->SwapItemData( copy );
  535. if( item->Type() == PCB_GROUP_T )
  536. {
  537. PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
  538. group->RunOnChildren( [&]( BOARD_ITEM* child )
  539. {
  540. child->SetParentGroup( group );
  541. } );
  542. }
  543. view->Add( item );
  544. connectivity->Add( item );
  545. board->OnItemChanged( item );
  546. itemsChanged.push_back( item );
  547. delete copy;
  548. break;
  549. }
  550. default:
  551. wxASSERT( false );
  552. break;
  553. }
  554. }
  555. if( bulkAddedItems.size() > 0 )
  556. board->FinalizeBulkAdd( bulkAddedItems );
  557. if( bulkRemovedItems.size() > 0 )
  558. board->FinalizeBulkRemove( bulkRemovedItems );
  559. if( itemsChanged.size() > 0 )
  560. board->OnItemsChanged( itemsChanged );
  561. if ( !m_isFootprintEditor )
  562. {
  563. connectivity->RecalculateRatsnest();
  564. board->UpdateRatsnestExclusions();
  565. }
  566. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  567. selTool->RebuildSelection();
  568. clear();
  569. }