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.

497 lines
16 KiB

5 years ago
5 years ago
5 years ago
5 years ago
9 years ago
5 years ago
5 years ago
5 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. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <board.h>
  25. #include <footprint.h>
  26. #include <pcb_edit_frame.h>
  27. #include <tool/tool_manager.h>
  28. #include <tools/pcb_selection_tool.h>
  29. #include <view/view.h>
  30. #include <board_commit.h>
  31. #include <tools/pcb_tool_base.h>
  32. #include <tools/pcb_actions.h>
  33. #include <connectivity/connectivity_data.h>
  34. #include <functional>
  35. using namespace std::placeholders;
  36. BOARD_COMMIT::BOARD_COMMIT( PCB_TOOL_BASE* aTool )
  37. {
  38. m_toolMgr = aTool->GetManager();
  39. m_isFootprintEditor = aTool->IsFootprintEditor();
  40. }
  41. BOARD_COMMIT::BOARD_COMMIT( EDA_DRAW_FRAME* aFrame )
  42. {
  43. m_toolMgr = aFrame->GetToolManager();
  44. m_isFootprintEditor = aFrame->IsType( FRAME_FOOTPRINT_EDITOR );
  45. }
  46. BOARD_COMMIT::~BOARD_COMMIT()
  47. {
  48. }
  49. COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType )
  50. {
  51. // if aItem belongs a footprint, the full footprint will be saved
  52. // because undo/redo does not handle "sub items" modifications
  53. if( aItem && aItem->Type() != PCB_FOOTPRINT_T && aChangeType == CHT_MODIFY )
  54. {
  55. EDA_ITEM* item = aItem->GetParent();
  56. if( item && item->Type() == PCB_FOOTPRINT_T ) // means aItem belongs a footprint
  57. aItem = item;
  58. }
  59. return COMMIT::Stage( aItem, aChangeType );
  60. }
  61. COMMIT& BOARD_COMMIT::Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType )
  62. {
  63. return COMMIT::Stage( container, aChangeType );
  64. }
  65. COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag )
  66. {
  67. return COMMIT::Stage( aItems, aModFlag );
  68. }
  69. void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool aSetDirtyBit )
  70. {
  71. // Objects potentially interested in changes:
  72. PICKED_ITEMS_LIST undoList;
  73. KIGFX::VIEW* view = m_toolMgr->GetView();
  74. BOARD* board = (BOARD*) m_toolMgr->GetModel();
  75. PCB_BASE_FRAME* frame = (PCB_BASE_FRAME*) m_toolMgr->GetToolHolder();
  76. auto connectivity = board->GetConnectivity();
  77. std::set<EDA_ITEM*> savedModules;
  78. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  79. bool itemsDeselected = false;
  80. std::vector<BOARD_ITEM*> bulkAddedItems;
  81. std::vector<BOARD_ITEM*> bulkRemovedItems;
  82. std::vector<BOARD_ITEM*> itemsChanged;
  83. if( Empty() )
  84. return;
  85. for( COMMIT_LINE& ent : m_changes )
  86. {
  87. int changeType = ent.m_type & CHT_TYPE;
  88. int changeFlags = ent.m_type & CHT_FLAGS;
  89. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  90. // Module items need to be saved in the undo buffer before modification
  91. if( m_isFootprintEditor )
  92. {
  93. // Be sure that we are storing a footprint
  94. if( ent.m_item->Type() != PCB_FOOTPRINT_T )
  95. ent.m_item = ent.m_item->GetParent();
  96. // We have not saved the footprint yet, so let's create an entry
  97. if( savedModules.count( ent.m_item ) == 0 )
  98. {
  99. if( !ent.m_copy )
  100. {
  101. wxASSERT( changeType != CHT_MODIFY ); // too late to make a copy..
  102. ent.m_copy = ent.m_item->Clone();
  103. }
  104. wxASSERT( ent.m_item->Type() == PCB_FOOTPRINT_T );
  105. wxASSERT( ent.m_copy->Type() == PCB_FOOTPRINT_T );
  106. if( aCreateUndoEntry )
  107. {
  108. ITEM_PICKER itemWrapper( nullptr, ent.m_item, UNDO_REDO::CHANGED );
  109. itemWrapper.SetLink( ent.m_copy );
  110. undoList.PushItem( itemWrapper );
  111. frame->SaveCopyInUndoList( undoList, UNDO_REDO::CHANGED );
  112. }
  113. savedModules.insert( ent.m_item );
  114. static_cast<FOOTPRINT*>( ent.m_item )->SetLastEditTime();
  115. }
  116. }
  117. switch( changeType )
  118. {
  119. case CHT_ADD:
  120. {
  121. if( m_isFootprintEditor )
  122. {
  123. // footprints inside footprints are not supported yet
  124. wxASSERT( boardItem->Type() != PCB_FOOTPRINT_T );
  125. boardItem->SetParent( board->Footprints().front() );
  126. if( !( changeFlags & CHT_DONE ) )
  127. board->Footprints().front()->Add( boardItem );
  128. }
  129. else if( boardItem->Type() == PCB_FP_TEXT_T ||
  130. boardItem->Type() == PCB_FP_SHAPE_T ||
  131. boardItem->Type() == PCB_FP_ZONE_T )
  132. {
  133. wxASSERT( boardItem->GetParent() &&
  134. boardItem->GetParent()->Type() == PCB_FOOTPRINT_T );
  135. }
  136. else
  137. {
  138. if( aCreateUndoEntry )
  139. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::NEWITEM ) );
  140. if( !( changeFlags & CHT_DONE ) )
  141. {
  142. board->Add( boardItem, ADD_MODE::BULK_INSERT ); // handles connectivity
  143. bulkAddedItems.push_back( boardItem );
  144. }
  145. }
  146. if( boardItem->Type() != PCB_NETINFO_T )
  147. view->Add( boardItem );
  148. break;
  149. }
  150. case CHT_REMOVE:
  151. {
  152. if( !m_isFootprintEditor && aCreateUndoEntry )
  153. undoList.PushItem( ITEM_PICKER( nullptr, boardItem, UNDO_REDO::DELETED ) );
  154. if( boardItem->IsSelected() )
  155. {
  156. selTool->RemoveItemFromSel( boardItem, true /* quiet mode */ );
  157. itemsDeselected = true;
  158. }
  159. switch( boardItem->Type() )
  160. {
  161. // Module items
  162. case PCB_PAD_T:
  163. case PCB_FP_SHAPE_T:
  164. case PCB_FP_TEXT_T:
  165. case PCB_FP_ZONE_T:
  166. // This level can only handle footprint children when editing footprints
  167. wxASSERT( m_isFootprintEditor );
  168. if( boardItem->Type() == PCB_FP_TEXT_T )
  169. {
  170. FP_TEXT* text = static_cast<FP_TEXT*>( boardItem );
  171. // don't allow deletion of Reference or Value
  172. if( text->GetType() != FP_TEXT::TEXT_is_DIVERS )
  173. break;
  174. }
  175. view->Remove( boardItem );
  176. if( !( changeFlags & CHT_DONE ) )
  177. {
  178. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem->GetParent() );
  179. wxASSERT( footprint && footprint->Type() == PCB_FOOTPRINT_T );
  180. footprint->Delete( boardItem );
  181. }
  182. break;
  183. // Board items
  184. case PCB_SHAPE_T: // a shape (normally not on copper layers)
  185. case PCB_TEXT_T: // a text on a layer
  186. case PCB_TRACE_T: // a track segment (segment on a copper layer)
  187. case PCB_ARC_T: // an arced track segment (segment on a copper layer)
  188. case PCB_VIA_T: // a via (like track segment on a copper layer)
  189. case PCB_DIM_ALIGNED_T: // a dimension (graphic item)
  190. case PCB_DIM_CENTER_T:
  191. case PCB_DIM_ORTHOGONAL_T:
  192. case PCB_DIM_LEADER_T: // a leader dimension
  193. case PCB_TARGET_T: // a target (graphic item)
  194. case PCB_MARKER_T: // a marker used to show something
  195. case PCB_ZONE_T:
  196. view->Remove( boardItem );
  197. if( !( changeFlags & CHT_DONE ) )
  198. {
  199. board->Remove( boardItem, REMOVE_MODE::BULK );
  200. bulkRemovedItems.push_back( boardItem );
  201. }
  202. break;
  203. case PCB_FOOTPRINT_T:
  204. {
  205. // No support for nested footprints (yet)
  206. wxASSERT( !m_isFootprintEditor );
  207. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
  208. view->Remove( footprint );
  209. footprint->ClearFlags();
  210. if( !( changeFlags & CHT_DONE ) )
  211. {
  212. board->Remove( footprint, REMOVE_MODE::BULK ); // handles connectivity
  213. bulkRemovedItems.push_back( footprint );
  214. }
  215. }
  216. break;
  217. case PCB_GROUP_T:
  218. view->Remove( boardItem );
  219. if( !( changeFlags & CHT_DONE ) )
  220. {
  221. if( m_isFootprintEditor )
  222. board->GetFirstFootprint()->Remove( boardItem );
  223. else
  224. {
  225. board->Remove( boardItem, REMOVE_MODE::BULK );
  226. bulkRemovedItems.push_back( boardItem );
  227. }
  228. }
  229. break;
  230. // Metadata items
  231. case PCB_NETINFO_T:
  232. board->Remove( boardItem, REMOVE_MODE::BULK );
  233. bulkRemovedItems.push_back( boardItem );
  234. break;
  235. default: // other types do not need to (or should not) be handled
  236. wxASSERT( false );
  237. break;
  238. }
  239. break;
  240. }
  241. case CHT_MODIFY:
  242. {
  243. if( !m_isFootprintEditor && aCreateUndoEntry )
  244. {
  245. ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
  246. wxASSERT( ent.m_copy );
  247. itemWrapper.SetLink( ent.m_copy );
  248. undoList.PushItem( itemWrapper );
  249. }
  250. if( ent.m_copy )
  251. connectivity->MarkItemNetAsDirty( static_cast<BOARD_ITEM*>( ent.m_copy ) );
  252. connectivity->Update( boardItem );
  253. view->Update( boardItem );
  254. if( m_isFootprintEditor )
  255. {
  256. static_cast<FOOTPRINT*>( boardItem )->RunOnChildren(
  257. [&]( BOARD_ITEM* aChild )
  258. {
  259. view->Update( aChild );
  260. });
  261. }
  262. itemsChanged.push_back( boardItem );
  263. // if no undo entry is needed, the copy would create a memory leak
  264. if( !aCreateUndoEntry )
  265. delete ent.m_copy;
  266. break;
  267. }
  268. default:
  269. wxASSERT( false );
  270. break;
  271. }
  272. }
  273. if( bulkAddedItems.size() > 0 )
  274. board->FinalizeBulkAdd( bulkAddedItems );
  275. if( bulkRemovedItems.size() > 0 )
  276. board->FinalizeBulkRemove( bulkRemovedItems );
  277. if( itemsChanged.size() > 0 )
  278. board->OnItemsChanged( itemsChanged );
  279. if( !m_isFootprintEditor )
  280. {
  281. size_t num_changes = m_changes.size();
  282. connectivity->RecalculateRatsnest( this );
  283. connectivity->ClearDynamicRatsnest();
  284. frame->GetCanvas()->RedrawRatsnest();
  285. if( m_changes.size() > num_changes )
  286. {
  287. for( size_t i = num_changes; i < m_changes.size(); ++i )
  288. {
  289. COMMIT_LINE& ent = m_changes[i];
  290. // This should only be modifications from the connectivity algo
  291. wxASSERT( ( ent.m_type & CHT_TYPE ) == CHT_MODIFY );
  292. auto boardItem = static_cast<BOARD_ITEM*>( ent.m_item );
  293. if( aCreateUndoEntry )
  294. {
  295. ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED );
  296. wxASSERT( ent.m_copy );
  297. itemWrapper.SetLink( ent.m_copy );
  298. undoList.PushItem( itemWrapper );
  299. }
  300. else
  301. {
  302. delete ent.m_copy;
  303. }
  304. view->Update( boardItem );
  305. }
  306. }
  307. }
  308. if( !m_isFootprintEditor && aCreateUndoEntry )
  309. frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNSPECIFIED );
  310. m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } );
  311. if( itemsDeselected )
  312. m_toolMgr->PostEvent( EVENTS::UnselectedEvent );
  313. if( aSetDirtyBit )
  314. frame->OnModify();
  315. else
  316. frame->Update3DView( true );
  317. clear();
  318. }
  319. EDA_ITEM* BOARD_COMMIT::parentObject( EDA_ITEM* aItem ) const
  320. {
  321. switch( aItem->Type() )
  322. {
  323. case PCB_PAD_T:
  324. case PCB_FP_SHAPE_T:
  325. case PCB_FP_TEXT_T:
  326. case PCB_FP_ZONE_T:
  327. return aItem->GetParent();
  328. case PCB_ZONE_T:
  329. wxASSERT( !dynamic_cast<FOOTPRINT*>( aItem->GetParent() ) );
  330. return aItem;
  331. default:
  332. break;
  333. }
  334. return aItem;
  335. }
  336. void BOARD_COMMIT::Revert()
  337. {
  338. PICKED_ITEMS_LIST undoList;
  339. KIGFX::VIEW* view = m_toolMgr->GetView();
  340. BOARD* board = (BOARD*) m_toolMgr->GetModel();
  341. auto connectivity = board->GetConnectivity();
  342. std::vector<BOARD_ITEM*> bulkAddedItems;
  343. std::vector<BOARD_ITEM*> bulkRemovedItems;
  344. std::vector<BOARD_ITEM*> itemsChanged;
  345. for( auto it = m_changes.rbegin(); it != m_changes.rend(); ++it )
  346. {
  347. COMMIT_LINE& ent = *it;
  348. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ent.m_item );
  349. BOARD_ITEM* copy = static_cast<BOARD_ITEM*>( ent.m_copy );
  350. int changeType = ent.m_type & CHT_TYPE;
  351. int changeFlags = ent.m_type & CHT_FLAGS;
  352. switch( changeType )
  353. {
  354. case CHT_ADD:
  355. if( !( changeFlags & CHT_DONE ) )
  356. break;
  357. view->Remove( item );
  358. connectivity->Remove( item );
  359. board->Remove( item, REMOVE_MODE::BULK );
  360. bulkRemovedItems.push_back( item );
  361. break;
  362. case CHT_REMOVE:
  363. if( !( changeFlags & CHT_DONE ) )
  364. break;
  365. view->Add( item );
  366. connectivity->Add( item );
  367. board->Add( item, ADD_MODE::INSERT );
  368. bulkAddedItems.push_back( item );
  369. break;
  370. case CHT_MODIFY:
  371. {
  372. view->Remove( item );
  373. connectivity->Remove( item );
  374. item->SwapData( copy );
  375. view->Add( item );
  376. connectivity->Add( item );
  377. board->OnItemChanged( item );
  378. itemsChanged.push_back( item );
  379. delete copy;
  380. break;
  381. }
  382. default:
  383. wxASSERT( false );
  384. break;
  385. }
  386. }
  387. if( bulkAddedItems.size() > 0 )
  388. board->FinalizeBulkAdd( bulkAddedItems );
  389. if( bulkRemovedItems.size() > 0 )
  390. board->FinalizeBulkRemove( bulkRemovedItems );
  391. if( itemsChanged.size() > 0 )
  392. board->OnItemsChanged( itemsChanged );
  393. if ( !m_isFootprintEditor )
  394. connectivity->RecalculateRatsnest();
  395. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  396. selTool->RebuildSelection();
  397. clear();
  398. }