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.

769 lines
24 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
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-2023 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. {
  43. }
  44. BOARD_COMMIT::BOARD_COMMIT( PCB_TOOL_BASE* aTool )
  45. {
  46. m_toolMgr = aTool->GetManager();
  47. m_isFootprintEditor = aTool->IsFootprintEditor();
  48. m_isBoardEditor = aTool->IsBoardEditor();
  49. }
  50. BOARD_COMMIT::BOARD_COMMIT( EDA_DRAW_FRAME* aFrame )
  51. {
  52. m_toolMgr = aFrame->GetToolManager();
  53. m_isFootprintEditor = aFrame->IsType( FRAME_FOOTPRINT_EDITOR );
  54. m_isBoardEditor = aFrame->IsType( FRAME_PCB_EDITOR );
  55. }
  56. BOARD_COMMIT::~BOARD_COMMIT()
  57. {
  58. }
  59. BOARD* BOARD_COMMIT::GetBoard() const
  60. {
  61. return static_cast<BOARD*>( m_toolMgr->GetModel() );
  62. }
  63. COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType )
  64. {
  65. aItem->ClearFlags( IS_MODIFIED_CHILD );
  66. // If aItem belongs a footprint, the full footprint will be saved because undo/redo does
  67. // not handle "sub items" modifications. This has implications for auto-zone-refill, so
  68. // we need to store a bit more information.
  69. if( aItem && aChangeType == CHT_MODIFY )
  70. {
  71. if( aItem->Type() == PCB_FOOTPRINT_T )
  72. {
  73. static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
  74. [&]( BOARD_ITEM* child )
  75. {
  76. child->SetFlags( IS_MODIFIED_CHILD );
  77. } );
  78. }
  79. else if( aItem->GetParent() && aItem->GetParent()->Type() == PCB_FOOTPRINT_T )
  80. {
  81. if( aItem->Type() == PCB_GROUP_T )
  82. {
  83. static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
  84. [&]( BOARD_ITEM* child )
  85. {
  86. child->SetFlags( IS_MODIFIED_CHILD );
  87. } );
  88. }
  89. else
  90. {
  91. aItem->SetFlags( IS_MODIFIED_CHILD );
  92. }
  93. aItem = aItem->GetParent();
  94. }
  95. else if( aItem->Type() == PCB_GROUP_T )
  96. {
  97. // Many operations on group (move, rotate, etc.) are applied directly to their
  98. // children, so it's the children that must be staged.
  99. static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
  100. [&]( BOARD_ITEM* child )
  101. {
  102. COMMIT::Stage( child, aChangeType );
  103. } );
  104. }
  105. }
  106. return COMMIT::Stage( aItem, aChangeType );
  107. }
  108. COMMIT& BOARD_COMMIT::Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType )
  109. {
  110. return COMMIT::Stage( container, aChangeType );
  111. }
  112. COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag )
  113. {
  114. return COMMIT::Stage( aItems, aModFlag );
  115. }
  116. void BOARD_COMMIT::dirtyIntersectingZones( BOARD_ITEM* item, int aChangeType )
  117. {
  118. wxCHECK( item, /* void */ );
  119. ZONE_FILLER_TOOL* zoneFillerTool = m_toolMgr->GetTool<ZONE_FILLER_TOOL>();
  120. if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
  121. zoneFillerTool->DirtyZone( static_cast<ZONE*>( item ) );
  122. if( item->Type() == PCB_FOOTPRINT_T )
  123. {
  124. static_cast<FOOTPRINT*>( item )->RunOnChildren(
  125. [&]( BOARD_ITEM* child )
  126. {
  127. if( aChangeType != CHT_MODIFY || ( child->GetFlags() & IS_MODIFIED_CHILD ) )
  128. dirtyIntersectingZones( child, aChangeType );
  129. child->ClearFlags( IS_MODIFIED_CHILD );
  130. } );
  131. }
  132. else if( item->Type() == PCB_GROUP_T )
  133. {
  134. static_cast<PCB_GROUP*>( item )->RunOnChildren(
  135. [&]( BOARD_ITEM* child )
  136. {
  137. dirtyIntersectingZones( child, aChangeType );
  138. child->ClearFlags( IS_MODIFIED_CHILD );
  139. } );
  140. }
  141. else
  142. {
  143. BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  144. BOX2I bbox = item->GetBoundingBox();
  145. LSET layers = item->GetLayerSet();
  146. if( layers.test( Edge_Cuts ) || layers.test( Margin ) )
  147. layers = LSET::PhysicalLayersMask();
  148. else
  149. layers &= LSET::AllCuMask();
  150. if( layers.any() )
  151. {
  152. for( ZONE* zone : board->Zones() )
  153. {
  154. if( zone->GetIsRuleArea() )
  155. continue;
  156. if( ( zone->GetLayerSet() & layers ).any()
  157. && zone->GetBoundingBox().Intersects( bbox ) )
  158. {
  159. zoneFillerTool->DirtyZone( zone );
  160. }
  161. }
  162. }
  163. item->ClearFlags( IS_MODIFIED_CHILD );
  164. }
  165. }
  166. void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
  167. {
  168. // Objects potentially interested in changes:
  169. PICKED_ITEMS_LIST undoList;
  170. KIGFX::VIEW* view = m_toolMgr->GetView();
  171. BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  172. PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
  173. std::set<EDA_ITEM*> savedModules;
  174. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  175. bool itemsDeselected = false;
  176. bool solderMaskDirty = false;
  177. bool autofillZones = false;
  178. bool selectedModified = false;
  179. if( Empty() )
  180. return;
  181. std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
  182. // Note:
  183. // frame == nullptr happens in QA tests
  184. // in this case m_isBoardEditor and m_isFootprintEditor are set to false
  185. // But we also test frame == nullptr mainly to make Coverity happy
  186. std::vector<BOARD_ITEM*> bulkAddedItems;
  187. std::vector<BOARD_ITEM*> bulkRemovedItems;
  188. std::vector<BOARD_ITEM*> itemsChanged;
  189. if( m_isBoardEditor
  190. && !( aCommitFlags & ZONE_FILL_OP )
  191. && ( frame && frame->GetPcbNewSettings()->m_AutoRefillZones ) )
  192. {
  193. autofillZones = true;
  194. for( ZONE* zone : board->Zones() )
  195. zone->CacheBoundingBox();
  196. }
  197. for( COMMIT_LINE& ent : m_changes )
  198. {
  199. int changeType = ent.m_type & CHT_TYPE;
  200. int changeFlags = ent.m_type & CHT_FLAGS;
  201. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  202. wxASSERT( ent.m_item );
  203. // Module items need to be saved in the undo buffer before modification
  204. if( m_isFootprintEditor )
  205. {
  206. // Be sure that we are storing a footprint
  207. if( ent.m_item->Type() != PCB_FOOTPRINT_T )
  208. {
  209. ent.m_item = ent.m_item->GetParent();
  210. wxASSERT( ent.m_item );
  211. }
  212. // We have not saved the footprint yet, so let's create an entry
  213. if( savedModules.count( ent.m_item ) == 0 )
  214. {
  215. if( !ent.m_copy )
  216. {
  217. wxASSERT( changeType != CHT_MODIFY ); // too late to make a copy..
  218. ent.m_copy = makeImage( ent.m_item );
  219. }
  220. wxASSERT( ent.m_item->Type() == PCB_FOOTPRINT_T );
  221. wxASSERT( ent.m_copy->Type() == PCB_FOOTPRINT_T );
  222. if( !( aCommitFlags & SKIP_UNDO ) && frame )
  223. {
  224. ITEM_PICKER itemWrapper( nullptr, ent.m_item, UNDO_REDO::CHANGED );
  225. itemWrapper.SetLink( ent.m_copy );
  226. undoList.PushItem( itemWrapper );
  227. frame->SaveCopyInUndoList( undoList, UNDO_REDO::CHANGED );
  228. }
  229. savedModules.insert( ent.m_item );
  230. }
  231. }
  232. if( boardItem->Type() == PCB_VIA_T || boardItem->Type() == PCB_FOOTPRINT_T
  233. || boardItem->IsOnLayer( F_Mask ) || boardItem->IsOnLayer( B_Mask ) )
  234. {
  235. solderMaskDirty = true;
  236. }
  237. if( boardItem->IsSelected() )
  238. selectedModified = true;
  239. // If we're the footprint editor, the boardItem will always be the containing footprint
  240. if( m_isFootprintEditor && boardItem->Type() == PCB_FOOTPRINT_T )
  241. {
  242. static_cast<FOOTPRINT*>( boardItem )->RunOnChildren(
  243. [&selectedModified]( BOARD_ITEM* aItem )
  244. {
  245. if( aItem->HasFlag( IS_MODIFIED_CHILD ) )
  246. selectedModified = true;
  247. } );
  248. }
  249. switch( changeType )
  250. {
  251. case CHT_ADD:
  252. {
  253. if( selTool && selTool->GetEnteredGroup() && !boardItem->GetParentGroup()
  254. && PCB_GROUP::IsGroupableType( boardItem->Type() ) )
  255. {
  256. selTool->GetEnteredGroup()->AddItem( boardItem );
  257. }
  258. if( m_isFootprintEditor )
  259. {
  260. // footprints inside footprints are not supported yet
  261. wxASSERT( boardItem->Type() != PCB_FOOTPRINT_T );
  262. boardItem->SetParent( board->Footprints().front() );
  263. if( !( changeFlags & CHT_DONE ) )
  264. board->Footprints().front()->Add( boardItem );
  265. }
  266. else if( boardItem->Type() == PCB_PAD_T
  267. || boardItem->Type() == PCB_FP_TEXT_T
  268. || boardItem->Type() == PCB_FP_TEXTBOX_T
  269. || boardItem->Type() == PCB_FP_SHAPE_T
  270. || boardItem->Type() == PCB_FP_DIM_ALIGNED_T
  271. || boardItem->Type() == PCB_FP_DIM_LEADER_T
  272. || boardItem->Type() == PCB_FP_DIM_CENTER_T
  273. || boardItem->Type() == PCB_FP_DIM_RADIAL_T
  274. || boardItem->Type() == PCB_FP_DIM_ORTHOGONAL_T
  275. || boardItem->Type() == PCB_FP_ZONE_T )
  276. {
  277. wxASSERT( boardItem->GetParent() &&
  278. boardItem->GetParent()->Type() == PCB_FOOTPRINT_T );
  279. }
  280. else
  281. {
  282. if( !( aCommitFlags & SKIP_UNDO ) )
  283. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::NEWITEM ) );
  284. if( !( changeFlags & CHT_DONE ) )
  285. {
  286. board->Add( boardItem, ADD_MODE::BULK_INSERT ); // handles connectivity
  287. bulkAddedItems.push_back( boardItem );
  288. }
  289. }
  290. if( autofillZones && boardItem->Type() != PCB_MARKER_T )
  291. dirtyIntersectingZones( boardItem, changeType );
  292. if( view && boardItem->Type() != PCB_NETINFO_T )
  293. view->Add( boardItem );
  294. break;
  295. }
  296. case CHT_REMOVE:
  297. {
  298. PCB_GROUP* parentGroup = boardItem->GetParentGroup();
  299. if( !m_isFootprintEditor && !( aCommitFlags & SKIP_UNDO ) )
  300. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::DELETED ) );
  301. if( boardItem->IsSelected() )
  302. {
  303. if( selTool )
  304. selTool->RemoveItemFromSel( boardItem, true /* quiet mode */ );
  305. itemsDeselected = true;
  306. }
  307. if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
  308. parentGroup->RemoveItem( boardItem );
  309. if( autofillZones )
  310. dirtyIntersectingZones( boardItem, changeType );
  311. switch( boardItem->Type() )
  312. {
  313. // Footprint items
  314. case PCB_PAD_T:
  315. case PCB_FP_SHAPE_T:
  316. case PCB_FP_TEXT_T:
  317. case PCB_FP_TEXTBOX_T:
  318. case PCB_FP_DIM_ALIGNED_T:
  319. case PCB_FP_DIM_LEADER_T:
  320. case PCB_FP_DIM_CENTER_T:
  321. case PCB_FP_DIM_RADIAL_T:
  322. case PCB_FP_DIM_ORTHOGONAL_T:
  323. case PCB_FP_ZONE_T:
  324. // This level can only handle footprint children in the footprint editor as
  325. // only in that case has the entire footprint (and all its children) already
  326. // been saved for undo.
  327. wxASSERT( m_isFootprintEditor );
  328. if( boardItem->Type() == PCB_FP_TEXT_T )
  329. {
  330. FP_TEXT* text = static_cast<FP_TEXT*>( boardItem );
  331. // don't allow deletion of Reference or Value
  332. if( text->GetType() != FP_TEXT::TEXT_is_DIVERS )
  333. break;
  334. }
  335. if( view )
  336. view->Remove( boardItem );
  337. if( !( changeFlags & CHT_DONE ) )
  338. {
  339. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem->GetParent() );
  340. wxASSERT( footprint && footprint->Type() == PCB_FOOTPRINT_T );
  341. footprint->Delete( boardItem );
  342. }
  343. break;
  344. // Board items
  345. case PCB_SHAPE_T: // a shape (normally not on copper layers)
  346. case PCB_BITMAP_T: // a bitmap on a user layer
  347. case PCB_TEXT_T: // a text on a layer
  348. case PCB_TEXTBOX_T: // a wrapped text on a layer
  349. case PCB_TRACE_T: // a track segment (segment on a copper layer)
  350. case PCB_ARC_T: // an arced track segment (segment on a copper layer)
  351. case PCB_VIA_T: // a via (like track segment on a copper layer)
  352. case PCB_DIM_ALIGNED_T: // a dimension (graphic item)
  353. case PCB_DIM_CENTER_T:
  354. case PCB_DIM_RADIAL_T:
  355. case PCB_DIM_ORTHOGONAL_T:
  356. case PCB_DIM_LEADER_T: // a leader dimension
  357. case PCB_TARGET_T: // a target (graphic item)
  358. case PCB_MARKER_T: // a marker used to show something
  359. case PCB_ZONE_T:
  360. if( view )
  361. view->Remove( boardItem );
  362. if( !( changeFlags & CHT_DONE ) )
  363. {
  364. board->Remove( boardItem, REMOVE_MODE::BULK );
  365. bulkRemovedItems.push_back( boardItem );
  366. }
  367. break;
  368. case PCB_FOOTPRINT_T:
  369. {
  370. // No support for nested footprints (yet)
  371. wxASSERT( !m_isFootprintEditor );
  372. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
  373. if( view )
  374. view->Remove( footprint );
  375. footprint->ClearFlags();
  376. if( !( changeFlags & CHT_DONE ) )
  377. {
  378. board->Remove( footprint, REMOVE_MODE::BULK ); // handles connectivity
  379. bulkRemovedItems.push_back( footprint );
  380. }
  381. }
  382. break;
  383. case PCB_GROUP_T:
  384. if( view )
  385. view->Remove( boardItem );
  386. if( !( changeFlags & CHT_DONE ) )
  387. {
  388. if( m_isFootprintEditor )
  389. board->GetFirstFootprint()->Remove( boardItem );
  390. else
  391. {
  392. board->Remove( boardItem, REMOVE_MODE::BULK );
  393. bulkRemovedItems.push_back( boardItem );
  394. }
  395. }
  396. break;
  397. // Metadata items
  398. case PCB_NETINFO_T:
  399. board->Remove( boardItem, REMOVE_MODE::BULK );
  400. bulkRemovedItems.push_back( boardItem );
  401. break;
  402. default: // other types do not need to (or should not) be handled
  403. wxASSERT( false );
  404. break;
  405. }
  406. break;
  407. }
  408. case CHT_MODIFY:
  409. {
  410. if( !m_isFootprintEditor && !( aCommitFlags & SKIP_UNDO ) )
  411. {
  412. ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
  413. wxASSERT( ent.m_copy );
  414. itemWrapper.SetLink( ent.m_copy );
  415. undoList.PushItem( itemWrapper );
  416. }
  417. if( !( aCommitFlags & SKIP_CONNECTIVITY ) )
  418. {
  419. if( ent.m_copy )
  420. connectivity->MarkItemNetAsDirty( static_cast<BOARD_ITEM*>( ent.m_copy ) );
  421. connectivity->Update( boardItem );
  422. }
  423. if( autofillZones )
  424. {
  425. dirtyIntersectingZones( static_cast<BOARD_ITEM*>( ent.m_copy ), changeType ); // before
  426. dirtyIntersectingZones( boardItem, changeType ); // after
  427. }
  428. if( view )
  429. {
  430. view->Update( boardItem );
  431. if( m_isFootprintEditor )
  432. {
  433. static_cast<FOOTPRINT*>( boardItem )->RunOnChildren(
  434. [&]( BOARD_ITEM* aChild )
  435. {
  436. view->Update( aChild );
  437. });
  438. }
  439. }
  440. itemsChanged.push_back( boardItem );
  441. // if no undo entry is needed, the copy would create a memory leak
  442. if( aCommitFlags & SKIP_UNDO )
  443. delete ent.m_copy;
  444. break;
  445. }
  446. default:
  447. wxASSERT( false );
  448. break;
  449. }
  450. }
  451. if( bulkAddedItems.size() > 0 )
  452. board->FinalizeBulkAdd( bulkAddedItems );
  453. if( bulkRemovedItems.size() > 0 )
  454. board->FinalizeBulkRemove( bulkRemovedItems );
  455. if( itemsChanged.size() > 0 )
  456. board->OnItemsChanged( itemsChanged );
  457. if( m_isBoardEditor )
  458. {
  459. size_t num_changes = m_changes.size();
  460. if( aCommitFlags & SKIP_CONNECTIVITY )
  461. {
  462. connectivity->ClearRatsnest();
  463. connectivity->ClearLocalRatsnest();
  464. }
  465. else
  466. {
  467. connectivity->RecalculateRatsnest( this );
  468. board->UpdateRatsnestExclusions();
  469. connectivity->ClearLocalRatsnest();
  470. if( frame )
  471. frame->GetCanvas()->RedrawRatsnest();
  472. }
  473. if( solderMaskDirty )
  474. {
  475. if( frame )
  476. frame->HideSolderMask();
  477. }
  478. // Log undo items for any connectivity changes
  479. for( size_t i = num_changes; i < m_changes.size(); ++i )
  480. {
  481. COMMIT_LINE& ent = m_changes[i];
  482. wxASSERT( ( ent.m_type & CHT_TYPE ) == CHT_MODIFY );
  483. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  484. if( !( aCommitFlags & SKIP_UNDO ) )
  485. {
  486. ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
  487. wxASSERT( ent.m_copy );
  488. itemWrapper.SetLink( ent.m_copy );
  489. undoList.PushItem( itemWrapper );
  490. }
  491. else
  492. {
  493. delete ent.m_copy;
  494. }
  495. if( view )
  496. view->Update( boardItem );
  497. }
  498. }
  499. if( m_isBoardEditor && !( aCommitFlags & SKIP_UNDO ) && frame )
  500. {
  501. if( aCommitFlags & APPEND_UNDO )
  502. frame->AppendCopyToUndoList( undoList, UNDO_REDO::UNSPECIFIED );
  503. else
  504. frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNSPECIFIED );
  505. }
  506. m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );
  507. if( itemsDeselected )
  508. m_toolMgr->PostEvent( EVENTS::UnselectedEvent );
  509. if( autofillZones )
  510. m_toolMgr->RunAction( PCB_ACTIONS::zoneFillDirty );
  511. if( selectedModified )
  512. m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
  513. if( frame )
  514. {
  515. if( !( aCommitFlags & SKIP_SET_DIRTY ) )
  516. frame->OnModify();
  517. else
  518. frame->Update3DView( true, frame->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
  519. }
  520. clear();
  521. }
  522. EDA_ITEM* BOARD_COMMIT::parentObject( EDA_ITEM* aItem ) const
  523. {
  524. switch( aItem->Type() )
  525. {
  526. case PCB_PAD_T:
  527. case PCB_FP_SHAPE_T:
  528. case PCB_FP_TEXT_T:
  529. case PCB_FP_TEXTBOX_T:
  530. case PCB_FP_DIM_ALIGNED_T:
  531. case PCB_FP_DIM_LEADER_T:
  532. case PCB_FP_DIM_CENTER_T:
  533. case PCB_FP_DIM_RADIAL_T:
  534. case PCB_FP_DIM_ORTHOGONAL_T:
  535. case PCB_FP_ZONE_T:
  536. return aItem->GetParent();
  537. case PCB_ZONE_T:
  538. wxASSERT( !dynamic_cast<FOOTPRINT*>( aItem->GetParent() ) );
  539. return aItem;
  540. default:
  541. break;
  542. }
  543. return aItem;
  544. }
  545. EDA_ITEM* BOARD_COMMIT::makeImage( EDA_ITEM* aItem ) const
  546. {
  547. BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( aItem->Clone() );
  548. clone->SetParentGroup( nullptr );
  549. return clone;
  550. }
  551. void BOARD_COMMIT::Revert()
  552. {
  553. PICKED_ITEMS_LIST undoList;
  554. KIGFX::VIEW* view = m_toolMgr->GetView();
  555. BOARD* board = (BOARD*) m_toolMgr->GetModel();
  556. std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
  557. board->IncrementTimeStamp(); // clear caches
  558. std::vector<BOARD_ITEM*> bulkAddedItems;
  559. std::vector<BOARD_ITEM*> bulkRemovedItems;
  560. std::vector<BOARD_ITEM*> itemsChanged;
  561. for( auto it = m_changes.rbegin(); it != m_changes.rend(); ++it )
  562. {
  563. COMMIT_LINE& ent = *it;
  564. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ent.m_item );
  565. BOARD_ITEM* copy = static_cast<BOARD_ITEM*>( ent.m_copy );
  566. int changeType = ent.m_type & CHT_TYPE;
  567. int changeFlags = ent.m_type & CHT_FLAGS;
  568. switch( changeType )
  569. {
  570. case CHT_ADD:
  571. if( !( changeFlags & CHT_DONE ) )
  572. break;
  573. view->Remove( item );
  574. connectivity->Remove( item );
  575. board->Remove( item, REMOVE_MODE::BULK );
  576. bulkRemovedItems.push_back( item );
  577. break;
  578. case CHT_REMOVE:
  579. if( !( changeFlags & CHT_DONE ) )
  580. break;
  581. view->Add( item );
  582. connectivity->Add( item );
  583. board->Add( item, ADD_MODE::INSERT );
  584. bulkAddedItems.push_back( item );
  585. break;
  586. case CHT_MODIFY:
  587. {
  588. view->Remove( item );
  589. connectivity->Remove( item );
  590. item->SwapItemData( copy );
  591. if( item->Type() == PCB_GROUP_T )
  592. {
  593. PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
  594. group->RunOnChildren( [&]( BOARD_ITEM* child )
  595. {
  596. child->SetParentGroup( group );
  597. } );
  598. }
  599. view->Add( item );
  600. connectivity->Add( item );
  601. board->OnItemChanged( item );
  602. itemsChanged.push_back( item );
  603. delete copy;
  604. break;
  605. }
  606. default:
  607. wxASSERT( false );
  608. break;
  609. }
  610. }
  611. if( bulkAddedItems.size() > 0 )
  612. board->FinalizeBulkAdd( bulkAddedItems );
  613. if( bulkRemovedItems.size() > 0 )
  614. board->FinalizeBulkRemove( bulkRemovedItems );
  615. if( itemsChanged.size() > 0 )
  616. board->OnItemsChanged( itemsChanged );
  617. if ( !m_isFootprintEditor )
  618. {
  619. connectivity->RecalculateRatsnest();
  620. board->UpdateRatsnestExclusions();
  621. }
  622. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  623. selTool->RebuildSelection();
  624. clear();
  625. }