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.

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