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.

862 lines
28 KiB

2 years ago
5 years ago
5 years ago
5 years ago
4 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
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months 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 The 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 "thread_pool.h"
  26. #include <macros.h>
  27. #include <board.h>
  28. #include <footprint.h>
  29. #include <lset.h>
  30. #include <pcb_group.h>
  31. #include <pcb_track.h>
  32. #include <pcb_shape.h>
  33. #include <tool/tool_manager.h>
  34. #include <tools/pcb_selection_tool.h>
  35. #include <tools/zone_filler_tool.h>
  36. #include <view/view.h>
  37. #include <board_commit.h>
  38. #include <tools/pcb_tool_base.h>
  39. #include <tools/pcb_actions.h>
  40. #include <connectivity/connectivity_data.h>
  41. #include <teardrop/teardrop.h>
  42. #include <functional>
  43. #include <project/project_file.h>
  44. using namespace std::placeholders;
  45. BOARD_COMMIT::BOARD_COMMIT( TOOL_BASE* aTool ) :
  46. m_toolMgr( aTool->GetManager() ),
  47. m_isBoardEditor( false ),
  48. m_isFootprintEditor( false )
  49. {
  50. if( PCB_TOOL_BASE* pcb_tool = dynamic_cast<PCB_TOOL_BASE*>( aTool ) )
  51. {
  52. m_isBoardEditor = pcb_tool->IsBoardEditor();
  53. m_isFootprintEditor = pcb_tool->IsFootprintEditor();
  54. }
  55. }
  56. BOARD_COMMIT::BOARD_COMMIT( EDA_DRAW_FRAME* aFrame ) :
  57. m_toolMgr( aFrame->GetToolManager() ),
  58. m_isBoardEditor( aFrame->IsType( FRAME_PCB_EDITOR ) ),
  59. m_isFootprintEditor( aFrame->IsType( FRAME_FOOTPRINT_EDITOR ) )
  60. {
  61. }
  62. BOARD_COMMIT::BOARD_COMMIT( TOOL_MANAGER* aMgr ) :
  63. m_toolMgr( aMgr ),
  64. m_isBoardEditor( false ),
  65. m_isFootprintEditor( false )
  66. {
  67. EDA_DRAW_FRAME* frame = dynamic_cast<EDA_DRAW_FRAME*>( aMgr->GetToolHolder() );
  68. if( frame && frame->IsType( FRAME_PCB_EDITOR ) )
  69. m_isBoardEditor = true;
  70. else if( frame && frame->IsType( FRAME_FOOTPRINT_EDITOR ) )
  71. m_isFootprintEditor = true;
  72. }
  73. BOARD_COMMIT::BOARD_COMMIT( TOOL_MANAGER* aMgr, bool aIsBoardEditor ) :
  74. m_toolMgr( aMgr ),
  75. m_isBoardEditor( aIsBoardEditor ),
  76. m_isFootprintEditor( false )
  77. {
  78. }
  79. BOARD* BOARD_COMMIT::GetBoard() const
  80. {
  81. return static_cast<BOARD*>( m_toolMgr->GetModel() );
  82. }
  83. COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen )
  84. {
  85. // Many operations (move, rotate, etc.) are applied directly to a group's children, so they
  86. // must be staged as well.
  87. if( aChangeType == CHT_MODIFY )
  88. {
  89. if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( aItem ) )
  90. {
  91. group->RunOnChildren(
  92. [&]( BOARD_ITEM* child )
  93. {
  94. Stage( child, aChangeType );
  95. },
  96. RECURSE_MODE::NO_RECURSE );
  97. }
  98. }
  99. return COMMIT::Stage( aItem, aChangeType );
  100. }
  101. COMMIT& BOARD_COMMIT::Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType,
  102. BASE_SCREEN* aScreen )
  103. {
  104. return COMMIT::Stage( container, aChangeType, aScreen );
  105. }
  106. COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag,
  107. BASE_SCREEN* aScreen )
  108. {
  109. return COMMIT::Stage( aItems, aModFlag, aScreen );
  110. }
  111. void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector<ZONE*>* aStaleZones,
  112. std::vector<PCB_SHAPE*>* aStaleHatchedShapes )
  113. {
  114. wxCHECK( aChangedItem, /* void */ );
  115. if( aStaleZones && aChangedItem->Type() == PCB_ZONE_T )
  116. aStaleZones->push_back( static_cast<ZONE*>( aChangedItem ) );
  117. aChangedItem->RunOnChildren(
  118. std::bind( &BOARD_COMMIT::propagateDamage, this, _1, aStaleZones, aStaleHatchedShapes ),
  119. RECURSE_MODE::NO_RECURSE );
  120. BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  121. BOX2I damageBBox = aChangedItem->GetBoundingBox();
  122. LSET damageLayers = aChangedItem->GetLayerSet();
  123. if( aStaleZones )
  124. {
  125. if( damageLayers.test( Edge_Cuts ) || damageLayers.test( Margin ) )
  126. damageLayers = LSET::PhysicalLayersMask();
  127. else
  128. damageLayers &= LSET::AllCuMask();
  129. if( damageLayers.any() )
  130. {
  131. for( ZONE* zone : board->Zones() )
  132. {
  133. if( zone->GetIsRuleArea() )
  134. continue;
  135. if( ( zone->GetLayerSet() & damageLayers ).any()
  136. && zone->GetBoundingBox().Intersects( damageBBox ) )
  137. {
  138. aStaleZones->push_back( zone );
  139. }
  140. }
  141. }
  142. }
  143. auto checkItem =
  144. [&]( BOARD_ITEM* item )
  145. {
  146. if( item->Type() != PCB_SHAPE_T )
  147. return;
  148. PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
  149. if( !shape->IsHatchedFill() )
  150. return;
  151. if( ( shape->GetLayerSet() & damageLayers ).any()
  152. && shape->GetBoundingBox().Intersects( damageBBox ) )
  153. {
  154. aStaleHatchedShapes->push_back( shape );
  155. }
  156. };
  157. if( aStaleHatchedShapes && ( aChangedItem->Type() == PCB_FIELD_T
  158. || aChangedItem->Type() == PCB_TEXT_T
  159. || aChangedItem->Type() == PCB_TEXTBOX_T
  160. || aChangedItem->Type() == PCB_SHAPE_T ) )
  161. {
  162. if( aChangedItem->IsOnLayer( F_CrtYd ) )
  163. {
  164. damageBBox = aChangedItem->GetParentFootprint()->GetBoundingBox();
  165. damageLayers |= LSET::FrontMask();
  166. }
  167. else if( aChangedItem->IsOnLayer( B_CrtYd ) )
  168. {
  169. damageBBox = aChangedItem->GetParentFootprint()->GetBoundingBox();
  170. damageLayers |= LSET::BackMask();
  171. }
  172. for( FOOTPRINT* footprint : board->Footprints() )
  173. {
  174. footprint->RunOnChildren(
  175. [&]( BOARD_ITEM* child )
  176. {
  177. checkItem( child );
  178. },
  179. RECURSE_MODE::RECURSE );
  180. }
  181. for( BOARD_ITEM* item : board->Drawings() )
  182. {
  183. checkItem( item );
  184. }
  185. }
  186. }
  187. void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
  188. {
  189. KIGFX::VIEW* view = m_toolMgr->GetView();
  190. BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  191. PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
  192. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  193. // Notification info
  194. PICKED_ITEMS_LIST undoList;
  195. bool itemsDeselected = false;
  196. bool selectedModified = false;
  197. // Dirty flags and lists
  198. bool solderMaskDirty = false;
  199. bool autofillZones = false;
  200. std::vector<BOARD_ITEM*> staleTeardropPadsAndVias;
  201. std::set<PCB_TRACK*> staleTeardropTracks;
  202. PCB_GROUP* addedGroup = nullptr;
  203. std::vector<ZONE*> staleZonesStorage;
  204. std::vector<ZONE*>* staleZones = nullptr;
  205. std::vector<PCB_SHAPE*> staleHatchedShapes;
  206. if( Empty() )
  207. return;
  208. undoList.SetDescription( aMessage );
  209. TEARDROP_MANAGER teardropMgr( board, m_toolMgr );
  210. std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
  211. // Note: frame == nullptr happens in QA tests
  212. std::vector<BOARD_ITEM*> bulkAddedItems;
  213. std::vector<BOARD_ITEM*> bulkRemovedItems;
  214. std::vector<BOARD_ITEM*> itemsChanged;
  215. if( m_isBoardEditor
  216. && !( aCommitFlags & ZONE_FILL_OP )
  217. && ( frame && frame->GetPcbNewSettings()->m_AutoRefillZones ) )
  218. {
  219. autofillZones = true;
  220. staleZones = &staleZonesStorage;
  221. for( ZONE* zone : board->Zones() )
  222. zone->CacheBoundingBox();
  223. }
  224. for( COMMIT_LINE& ent : m_changes )
  225. {
  226. if( !ent.m_item || !ent.m_item->IsBOARD_ITEM() )
  227. continue;
  228. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  229. if( m_isBoardEditor )
  230. {
  231. if( boardItem->Type() == PCB_VIA_T || boardItem->Type() == PCB_FOOTPRINT_T
  232. || boardItem->IsOnLayer( F_Mask ) || boardItem->IsOnLayer( B_Mask ) )
  233. {
  234. solderMaskDirty = true;
  235. }
  236. if( !( aCommitFlags & SKIP_TEARDROPS ) )
  237. {
  238. if( boardItem->Type() == PCB_FOOTPRINT_T )
  239. {
  240. for( PAD* pad : static_cast<FOOTPRINT*>( boardItem )->Pads() )
  241. staleTeardropPadsAndVias.push_back( pad );
  242. }
  243. else if( boardItem->Type() == PCB_PAD_T || boardItem->Type() == PCB_VIA_T )
  244. {
  245. staleTeardropPadsAndVias.push_back( boardItem );
  246. }
  247. else if( boardItem->Type() == PCB_TRACE_T || boardItem->Type() == PCB_ARC_T )
  248. {
  249. PCB_TRACK* track = static_cast<PCB_TRACK*>( boardItem );
  250. staleTeardropTracks.insert( track );
  251. std::vector<PAD*> connectedPads;
  252. std::vector<PCB_VIA*> connectedVias;
  253. connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
  254. for( PAD* pad : connectedPads )
  255. staleTeardropPadsAndVias.push_back( pad );
  256. for( PCB_VIA* via : connectedVias )
  257. staleTeardropPadsAndVias.push_back( via );
  258. }
  259. }
  260. }
  261. if( boardItem->IsSelected() )
  262. selectedModified = true;
  263. }
  264. // Old teardrops must be removed before connectivity is rebuilt
  265. if( !staleTeardropPadsAndVias.empty() || !staleTeardropTracks.empty() )
  266. teardropMgr.RemoveTeardrops( *this, &staleTeardropPadsAndVias, &staleTeardropTracks );
  267. auto updateComponentClasses = [this]( BOARD_ITEM* boardItem )
  268. {
  269. if( boardItem->Type() != PCB_FOOTPRINT_T )
  270. return;
  271. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
  272. GetBoard()->GetComponentClassManager().RebuildRequiredCaches( footprint );
  273. };
  274. for( COMMIT_LINE& ent : m_changes )
  275. {
  276. if( !ent.m_item || !ent.m_item->IsBOARD_ITEM() )
  277. continue;
  278. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  279. int changeType = ent.m_type & CHT_TYPE;
  280. int changeFlags = ent.m_type & CHT_FLAGS;
  281. switch( changeType )
  282. {
  283. case CHT_ADD:
  284. if( selTool && selTool->GetEnteredGroup() && !boardItem->GetParentGroup()
  285. && PCB_GROUP::IsGroupableType( boardItem->Type() ) )
  286. {
  287. selTool->GetEnteredGroup()->AddItem( boardItem );
  288. }
  289. if( !( aCommitFlags & SKIP_UNDO ) )
  290. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::NEWITEM ) );
  291. if( !( changeFlags & CHT_DONE ) )
  292. {
  293. if( m_isFootprintEditor )
  294. {
  295. FOOTPRINT* parentFP = board->GetFirstFootprint();
  296. wxCHECK2_MSG( parentFP, continue, "Commit thinks this is footprint editor, but "
  297. "there is no first footprint!" );
  298. parentFP->Add( boardItem );
  299. }
  300. else if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
  301. {
  302. parentFP->Add( boardItem );
  303. }
  304. else
  305. {
  306. board->Add( boardItem, ADD_MODE::BULK_INSERT ); // handles connectivity
  307. bulkAddedItems.push_back( boardItem );
  308. }
  309. }
  310. if( boardItem->Type() == PCB_GROUP_T || boardItem->Type() == PCB_GENERATOR_T )
  311. addedGroup = static_cast<PCB_GROUP*>( boardItem );
  312. if( boardItem->Type() != PCB_MARKER_T )
  313. propagateDamage( boardItem, staleZones, &staleHatchedShapes );
  314. if( view && boardItem->Type() != PCB_NETINFO_T )
  315. view->Add( boardItem );
  316. updateComponentClasses( boardItem );
  317. break;
  318. case CHT_REMOVE:
  319. {
  320. FOOTPRINT* parentFP = boardItem->GetParentFootprint();
  321. PCB_GROUP* parentGroup = boardItem->GetParentGroup();
  322. if( !( aCommitFlags & SKIP_UNDO ) )
  323. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::DELETED ) );
  324. if( boardItem->IsSelected() )
  325. {
  326. if( selTool )
  327. selTool->RemoveItemFromSel( boardItem, true /* quiet mode */ );
  328. itemsDeselected = true;
  329. }
  330. if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
  331. parentGroup->RemoveItem( boardItem );
  332. if( parentFP && !( parentFP->GetFlags() & STRUCT_DELETED ) )
  333. ent.m_parent = parentFP->m_Uuid;
  334. if( boardItem->Type() != PCB_MARKER_T )
  335. propagateDamage( boardItem, staleZones, &staleHatchedShapes );
  336. switch( boardItem->Type() )
  337. {
  338. case PCB_FIELD_T:
  339. static_cast<PCB_FIELD*>( boardItem )->SetVisible( false );
  340. break;
  341. case PCB_TEXT_T:
  342. case PCB_PAD_T:
  343. case PCB_SHAPE_T: // a shape (normally not on copper layers)
  344. case PCB_REFERENCE_IMAGE_T: // a bitmap on an associated layer
  345. case PCB_GENERATOR_T: // a generator on a layer
  346. case PCB_TEXTBOX_T: // a line-wrapped (and optionally bordered) text item
  347. case PCB_TABLE_T: // rows and columns of tablecells
  348. case PCB_TRACE_T: // a track segment (segment on a copper layer)
  349. case PCB_ARC_T: // an arced track segment (segment on a copper layer)
  350. case PCB_VIA_T: // a via (like track segment on a copper layer)
  351. case PCB_DIM_ALIGNED_T: // a dimension (graphic item)
  352. case PCB_DIM_CENTER_T:
  353. case PCB_DIM_RADIAL_T:
  354. case PCB_DIM_ORTHOGONAL_T:
  355. case PCB_DIM_LEADER_T: // a leader dimension
  356. case PCB_TARGET_T: // a target (graphic item)
  357. case PCB_MARKER_T: // a marker used to show something
  358. case PCB_ZONE_T:
  359. case PCB_FOOTPRINT_T:
  360. case PCB_GROUP_T:
  361. if( view )
  362. view->Remove( boardItem );
  363. if( !( changeFlags & CHT_DONE ) )
  364. {
  365. if( parentFP )
  366. {
  367. parentFP->Remove( boardItem );
  368. }
  369. else
  370. {
  371. board->Remove( boardItem, REMOVE_MODE::BULK );
  372. bulkRemovedItems.push_back( boardItem );
  373. }
  374. }
  375. break;
  376. // Metadata items
  377. case PCB_NETINFO_T:
  378. board->Remove( boardItem, REMOVE_MODE::BULK );
  379. bulkRemovedItems.push_back( boardItem );
  380. break;
  381. default: // other types do not need to (or should not) be handled
  382. wxASSERT( false );
  383. break;
  384. }
  385. // The item has been removed from the board; it is now owned by undo/redo.
  386. boardItem->SetFlags( UR_TRANSIENT );
  387. break;
  388. }
  389. case CHT_UNGROUP:
  390. if( PCB_GROUP* group = boardItem->GetParentGroup() )
  391. {
  392. if( !( aCommitFlags & SKIP_UNDO ) )
  393. {
  394. ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::UNGROUP );
  395. itemWrapper.SetGroupId( group->m_Uuid );
  396. undoList.PushItem( itemWrapper );
  397. }
  398. group->RemoveItem( boardItem );
  399. }
  400. break;
  401. case CHT_GROUP:
  402. if( addedGroup )
  403. {
  404. addedGroup->AddItem( boardItem );
  405. if( !( aCommitFlags & SKIP_UNDO ) )
  406. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::REGROUP ) );
  407. }
  408. break;
  409. case CHT_MODIFY:
  410. {
  411. BOARD_ITEM* boardItemCopy = nullptr;
  412. if( ent.m_copy && ent.m_copy->IsBOARD_ITEM() )
  413. boardItemCopy = static_cast<BOARD_ITEM*>( ent.m_copy );
  414. if( !( aCommitFlags & SKIP_UNDO ) )
  415. {
  416. ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
  417. wxASSERT( boardItemCopy );
  418. itemWrapper.SetLink( boardItemCopy );
  419. undoList.PushItem( itemWrapper );
  420. }
  421. if( !( aCommitFlags & SKIP_CONNECTIVITY ) )
  422. {
  423. if( boardItemCopy )
  424. connectivity->MarkItemNetAsDirty( boardItemCopy );
  425. connectivity->Update( boardItem );
  426. }
  427. if( boardItem->Type() != PCB_MARKER_T )
  428. {
  429. propagateDamage( boardItemCopy, staleZones, &staleHatchedShapes ); // before
  430. propagateDamage( boardItem, staleZones, &staleHatchedShapes ); // after
  431. }
  432. updateComponentClasses( boardItem );
  433. if( view )
  434. view->Update( boardItem );
  435. itemsChanged.push_back( boardItem );
  436. // if no undo entry is needed, the copy would create a memory leak
  437. if( aCommitFlags & SKIP_UNDO )
  438. delete ent.m_copy;
  439. break;
  440. }
  441. default:
  442. UNIMPLEMENTED_FOR( boardItem->GetClass() );
  443. break;
  444. }
  445. boardItem->ClearEditFlags();
  446. boardItem->RunOnChildren(
  447. [&]( BOARD_ITEM* item )
  448. {
  449. item->ClearEditFlags();
  450. },
  451. RECURSE_MODE::RECURSE );
  452. } // ... and regenerate them.
  453. // Invalidate component classes
  454. board->GetComponentClassManager().InvalidateComponentClasses();
  455. if( m_isBoardEditor )
  456. {
  457. size_t num_changes = m_changes.size();
  458. if( aCommitFlags & SKIP_CONNECTIVITY )
  459. {
  460. connectivity->ClearRatsnest();
  461. connectivity->ClearLocalRatsnest();
  462. }
  463. else
  464. {
  465. connectivity->RecalculateRatsnest( this );
  466. board->UpdateRatsnestExclusions();
  467. connectivity->ClearLocalRatsnest();
  468. if( frame )
  469. frame->GetCanvas()->RedrawRatsnest();
  470. board->OnRatsnestChanged();
  471. }
  472. if( solderMaskDirty )
  473. {
  474. if( frame )
  475. frame->HideSolderMask();
  476. }
  477. if( !staleTeardropPadsAndVias.empty() || !staleTeardropTracks.empty() )
  478. {
  479. teardropMgr.UpdateTeardrops( *this, &staleTeardropPadsAndVias, &staleTeardropTracks );
  480. // UpdateTeardrops() can modify the ratsnest data. So rebuild this ratsnest data
  481. connectivity->RecalculateRatsnest( this );
  482. }
  483. // Log undo items for any connectivity or teardrop changes
  484. for( size_t i = num_changes; i < m_changes.size(); ++i )
  485. {
  486. COMMIT_LINE& ent = m_changes[i];
  487. BOARD_ITEM* boardItem = nullptr;
  488. BOARD_ITEM* boardItemCopy = nullptr;
  489. if( ent.m_item && ent.m_item->IsBOARD_ITEM() )
  490. boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  491. if( ent.m_copy && ent.m_copy->IsBOARD_ITEM() )
  492. boardItemCopy = static_cast<BOARD_ITEM*>( ent.m_copy );
  493. wxCHECK2( boardItem, continue );
  494. if( !( aCommitFlags & SKIP_UNDO ) )
  495. {
  496. ITEM_PICKER itemWrapper( nullptr, boardItem, convert( ent.m_type & CHT_TYPE ) );
  497. itemWrapper.SetLink( boardItemCopy );
  498. undoList.PushItem( itemWrapper );
  499. }
  500. else
  501. {
  502. delete ent.m_copy;
  503. }
  504. if( view )
  505. {
  506. if( ( ent.m_type & CHT_TYPE ) == CHT_ADD )
  507. view->Add( boardItem );
  508. else if( ( ent.m_type & CHT_TYPE ) == CHT_REMOVE )
  509. view->Remove( boardItem );
  510. else
  511. view->Update( boardItem );
  512. }
  513. }
  514. }
  515. if( bulkAddedItems.size() > 0 || bulkRemovedItems.size() > 0 || itemsChanged.size() > 0 )
  516. board->OnItemsCompositeUpdate( bulkAddedItems, bulkRemovedItems, itemsChanged );
  517. if( frame )
  518. {
  519. if( !( aCommitFlags & SKIP_UNDO ) )
  520. {
  521. if( aCommitFlags & APPEND_UNDO )
  522. frame->AppendCopyToUndoList( undoList, UNDO_REDO::UNSPECIFIED );
  523. else
  524. frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNSPECIFIED );
  525. }
  526. }
  527. m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );
  528. if( itemsDeselected )
  529. m_toolMgr->PostEvent( EVENTS::UnselectedEvent );
  530. if( autofillZones )
  531. {
  532. ZONE_FILLER_TOOL* zoneFillerTool = m_toolMgr->GetTool<ZONE_FILLER_TOOL>();
  533. for( ZONE* zone : *staleZones )
  534. zoneFillerTool->DirtyZone( zone );
  535. m_toolMgr->PostAction( PCB_ACTIONS::zoneFillDirty );
  536. }
  537. for( PCB_SHAPE* shape : staleHatchedShapes )
  538. {
  539. shape->SetHatchingDirty();
  540. if( view )
  541. view->Update( shape );
  542. }
  543. if( selectedModified )
  544. m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
  545. if( frame )
  546. {
  547. if( !( aCommitFlags & SKIP_SET_DIRTY ) )
  548. frame->OnModify();
  549. else
  550. frame->Update3DView( true, frame->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
  551. // Ensure the message panel is updated after committing changes.
  552. // By default (i.e. if no event posted), display the updated board info
  553. if( !itemsDeselected && !autofillZones && !selectedModified )
  554. {
  555. std::vector<MSG_PANEL_ITEM> msg_list;
  556. board->GetMsgPanelInfo( frame, msg_list );
  557. frame->SetMsgPanel( msg_list );
  558. }
  559. }
  560. clear();
  561. }
  562. EDA_ITEM* BOARD_COMMIT::parentObject( EDA_ITEM* aItem ) const
  563. {
  564. return aItem;
  565. }
  566. EDA_ITEM* BOARD_COMMIT::makeImage( EDA_ITEM* aItem ) const
  567. {
  568. return MakeImage( aItem );
  569. }
  570. EDA_ITEM* BOARD_COMMIT::MakeImage( EDA_ITEM* aItem )
  571. {
  572. EDA_ITEM* clone = aItem->Clone();
  573. if( clone->IsBOARD_ITEM() )
  574. static_cast<BOARD_ITEM*>( clone )->SetParentGroup( nullptr );
  575. clone->SetFlags( UR_TRANSIENT );
  576. return clone;
  577. }
  578. void BOARD_COMMIT::Revert()
  579. {
  580. PICKED_ITEMS_LIST undoList;
  581. KIGFX::VIEW* view = m_toolMgr->GetView();
  582. BOARD* board = (BOARD*) m_toolMgr->GetModel();
  583. std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
  584. board->IncrementTimeStamp(); // clear caches
  585. auto updateComponentClasses = [this]( BOARD_ITEM* boardItem )
  586. {
  587. if( boardItem->Type() != PCB_FOOTPRINT_T )
  588. return;
  589. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
  590. GetBoard()->GetComponentClassManager().RebuildRequiredCaches( footprint );
  591. };
  592. std::vector<BOARD_ITEM*> bulkAddedItems;
  593. std::vector<BOARD_ITEM*> bulkRemovedItems;
  594. std::vector<BOARD_ITEM*> itemsChanged;
  595. for( COMMIT_LINE& entry : m_changes )
  596. {
  597. if( !entry.m_item || !entry.m_item->IsBOARD_ITEM() )
  598. continue;
  599. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( entry.m_item );
  600. int changeType = entry.m_type & CHT_TYPE;
  601. int changeFlags = entry.m_type & CHT_FLAGS;
  602. switch( changeType )
  603. {
  604. case CHT_ADD:
  605. // Items are auto-added to the parent group by BOARD_ITEM::Duplicate(), not when
  606. // the commit is pushed.
  607. if( PCB_GROUP* parentGroup = boardItem->GetParentGroup() )
  608. {
  609. if( GetStatus( parentGroup ) == 0 )
  610. parentGroup->RemoveItem( boardItem );
  611. }
  612. if( !( changeFlags & CHT_DONE ) )
  613. break;
  614. view->Remove( boardItem );
  615. connectivity->Remove( boardItem );
  616. if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
  617. {
  618. parentFP->Remove( boardItem );
  619. }
  620. else
  621. {
  622. board->Remove( boardItem, REMOVE_MODE::BULK );
  623. bulkRemovedItems.push_back( boardItem );
  624. }
  625. break;
  626. case CHT_REMOVE:
  627. {
  628. if( !( changeFlags & CHT_DONE ) )
  629. break;
  630. view->Add( boardItem );
  631. connectivity->Add( boardItem );
  632. // Note: parent can be nullptr, because entry.m_parent is only set for children
  633. // of footprints.
  634. BOARD_ITEM* parent = board->GetItem( entry.m_parent );
  635. if( parent && parent->Type() == PCB_FOOTPRINT_T )
  636. {
  637. static_cast<FOOTPRINT*>( parent )->Add( boardItem, ADD_MODE::INSERT );
  638. }
  639. else
  640. {
  641. board->Add( boardItem, ADD_MODE::INSERT );
  642. bulkAddedItems.push_back( boardItem );
  643. }
  644. updateComponentClasses( boardItem );
  645. break;
  646. }
  647. case CHT_MODIFY:
  648. {
  649. view->Remove( boardItem );
  650. connectivity->Remove( boardItem );
  651. wxASSERT( entry.m_copy && entry.m_copy->IsBOARD_ITEM() );
  652. BOARD_ITEM* boardItemCopy = static_cast<BOARD_ITEM*>( entry.m_copy );
  653. boardItem->SwapItemData( boardItemCopy );
  654. if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( boardItem ) )
  655. {
  656. group->RunOnChildren(
  657. [&]( BOARD_ITEM* child )
  658. {
  659. child->SetParentGroup( group );
  660. },
  661. RECURSE_MODE::NO_RECURSE );
  662. }
  663. view->Add( boardItem );
  664. connectivity->Add( boardItem );
  665. itemsChanged.push_back( boardItem );
  666. updateComponentClasses( boardItem );
  667. delete entry.m_copy;
  668. break;
  669. }
  670. default:
  671. UNIMPLEMENTED_FOR( boardItem->GetClass() );
  672. break;
  673. }
  674. boardItem->ClearEditFlags();
  675. }
  676. // Invalidate component classes
  677. board->GetComponentClassManager().InvalidateComponentClasses();
  678. if( bulkAddedItems.size() > 0 || bulkRemovedItems.size() > 0 || itemsChanged.size() > 0 )
  679. board->OnItemsCompositeUpdate( bulkAddedItems, bulkRemovedItems, itemsChanged );
  680. if( m_isBoardEditor )
  681. {
  682. connectivity->RecalculateRatsnest();
  683. board->UpdateRatsnestExclusions();
  684. board->OnRatsnestChanged();
  685. }
  686. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  687. selTool->RebuildSelection();
  688. // Property panel needs to know about the reselect
  689. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  690. clear();
  691. }