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.

615 lines
20 KiB

9 years ago
9 years ago
9 years ago
9 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
  5. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2016 CERN
  7. * @author Maciej Suminski <maciej.suminski@cern.ch>
  8. * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. #include <functional>
  28. using namespace std::placeholders;
  29. #include <fctsys.h>
  30. #include <class_draw_panel_gal.h>
  31. #include <macros.h>
  32. #include <pcbnew.h>
  33. #include <pcb_edit_frame.h>
  34. #include <class_board.h>
  35. #include <class_track.h>
  36. #include <class_drawsegment.h>
  37. #include <class_pcb_text.h>
  38. #include <class_pcb_target.h>
  39. #include <class_module.h>
  40. #include <class_dimension.h>
  41. #include <class_zone.h>
  42. #include <class_edge_mod.h>
  43. #include <origin_viewitem.h>
  44. #include <connectivity/connectivity_data.h>
  45. #include <tool/tool_manager.h>
  46. #include <tool/actions.h>
  47. #include <tools/selection_tool.h>
  48. #include <tools/pcbnew_control.h>
  49. #include <tools/pcb_editor_control.h>
  50. #include <view/view.h>
  51. #include <ws_proxy_undo_item.h>
  52. /* Functions to undo and redo edit commands.
  53. * commands to undo are stored in CurrentScreen->m_UndoList
  54. * commands to redo are stored in CurrentScreen->m_RedoList
  55. *
  56. * m_UndoList and m_RedoList handle a std::vector of PICKED_ITEMS_LIST
  57. * Each PICKED_ITEMS_LIST handle a std::vector of pickers (class ITEM_PICKER),
  58. * that store the list of schematic items that are concerned by the command to undo or redo
  59. * and is created for each command to undo (handle also a command to redo).
  60. * each picker has a pointer pointing to an item to undo or redo (in fact: deleted, added or
  61. * modified),
  62. * and has a pointer to a copy of this item, when this item has been modified
  63. * (the old values of parameters are therefore saved)
  64. *
  65. * there are 3 cases:
  66. * - delete item(s) command
  67. * - change item(s) command
  68. * - add item(s) command
  69. *
  70. * Undo command
  71. * - delete item(s) command:
  72. * => deleted items are moved in undo list
  73. *
  74. * - change item(s) command
  75. * => A copy of item(s) is made (a DrawPickedStruct list of wrappers)
  76. * the .m_Link member of each wrapper points the modified item.
  77. * the .m_Item member of each wrapper points the old copy of this item.
  78. *
  79. * - add item(s) command
  80. * =>A list of item(s) is made. The .m_Item member of each wrapper points the new item.
  81. *
  82. * Redo command
  83. * - delete item(s) old command:
  84. * => deleted items are moved in EEDrawList list, and in
  85. *
  86. * - change item(s) command
  87. * => the copy of item(s) is moved in Undo list
  88. *
  89. * - add item(s) command
  90. * => The list of item(s) is used to create a deleted list in undo list(same as a delete
  91. * command)
  92. *
  93. * Some block operations that change items can be undone without memorize items, just the
  94. * coordinates of the transform:
  95. * move list of items (undo/redo is made by moving with the opposite move vector)
  96. * mirror (Y) and flip list of items (undo/redo is made by mirror or flip items)
  97. * so they are handled specifically.
  98. *
  99. */
  100. /**
  101. * Function TestForExistingItem
  102. * test if aItem exists somewhere in lists of items
  103. * This is a function used by PutDataInPreviousState to be sure an item was not deleted
  104. * since an undo or redo.
  105. * This could be possible:
  106. * - if a call to SaveCopyInUndoList was forgotten in Pcbnew
  107. * - in zones outlines, when a change in one zone merges this zone with an other
  108. * This function avoids a Pcbnew crash
  109. * Before using this function to test existence of items,
  110. * it must be called with aItem = NULL to prepare the list
  111. * @param aPcb = board to test
  112. * @param aItem = item to find
  113. * = NULL to build the list of existing items
  114. */
  115. static bool TestForExistingItem( BOARD* aPcb, BOARD_ITEM* aItem )
  116. {
  117. for( auto item : aPcb->Tracks() )
  118. {
  119. if( aItem == static_cast<BOARD_ITEM*>( item ) )
  120. return true;
  121. }
  122. // Append modules:
  123. for( auto item : aPcb->Modules() )
  124. {
  125. if( aItem == static_cast<BOARD_ITEM*>( item ) )
  126. return true;
  127. }
  128. // Append drawings
  129. for( auto item : aPcb->Drawings() )
  130. {
  131. if( aItem == static_cast<BOARD_ITEM*>( item ) )
  132. return true;
  133. }
  134. // Append zones outlines
  135. for( auto item : aPcb->Zones() )
  136. {
  137. if( aItem == static_cast<BOARD_ITEM*>( item ) )
  138. return true;
  139. }
  140. NETINFO_LIST& netInfo = aPcb->GetNetInfo();
  141. for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
  142. {
  143. if( aItem == static_cast<BOARD_ITEM*>( *i ) )
  144. return true;
  145. }
  146. return false;
  147. }
  148. static void SwapItemData( BOARD_ITEM* aItem, BOARD_ITEM* aImage )
  149. {
  150. if( aImage == NULL )
  151. return;
  152. wxASSERT( aItem->Type() == aImage->Type() );
  153. // Remark: to create images of edited items to undo, we are using Clone method
  154. // which can duplication of items foe copy, but does not clone all members
  155. // mainly pointers in chain and time stamp, which is set to new, unique value.
  156. // So we have to use the current values of these parameters.
  157. timestamp_t timestamp = aItem->GetTimeStamp();
  158. EDA_ITEM* parent = aItem->GetParent();
  159. aItem->SwapData( aImage );
  160. // Restore pointers and time stamp, to be sure they are not broken
  161. aItem->SetTimeStamp( timestamp );
  162. aItem->SetParent( parent );
  163. }
  164. void PCB_BASE_EDIT_FRAME::SaveCopyInUndoList( BOARD_ITEM* aItem, UNDO_REDO_T aCommandType,
  165. const wxPoint& aTransformPoint )
  166. {
  167. PICKED_ITEMS_LIST commandToUndo;
  168. commandToUndo.PushItem( ITEM_PICKER( aItem, aCommandType ) );
  169. SaveCopyInUndoList( commandToUndo, aCommandType, aTransformPoint );
  170. }
  171. void PCB_BASE_EDIT_FRAME::SaveCopyInUndoList( const PICKED_ITEMS_LIST& aItemsList,
  172. UNDO_REDO_T aTypeCommand,
  173. const wxPoint& aTransformPoint )
  174. {
  175. static KICAD_T moduleChildren[] = { PCB_MODULE_TEXT_T, PCB_MODULE_EDGE_T, PCB_PAD_T, EOT };
  176. PICKED_ITEMS_LIST* commandToUndo = new PICKED_ITEMS_LIST();
  177. commandToUndo->m_TransformPoint = aTransformPoint;
  178. // First, filter unnecessary stuff from the list (i.e. for multiple pads / labels modified),
  179. // take the first occurence of the module (we save copies of modules when one of its subitems
  180. // is changed).
  181. for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
  182. {
  183. ITEM_PICKER curr_picker = aItemsList.GetItemWrapper(ii);
  184. BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( aItemsList.GetPickedItem( ii ) );
  185. // For items belonging to modules, we need to save state of the parent module
  186. if( item && item->IsType( moduleChildren ) )
  187. {
  188. // Item to be stored in the undo buffer is the parent module
  189. item = item->GetParent();
  190. wxASSERT( item && item->Type() == PCB_MODULE_T );
  191. if( item == NULL )
  192. continue;
  193. // Check if the parent module has already been saved in another entry
  194. bool found = false;
  195. for( unsigned j = 0; j < commandToUndo->GetCount(); j++ )
  196. {
  197. if( commandToUndo->GetPickedItem( j ) == item
  198. && commandToUndo->GetPickedItemStatus( j ) == UR_CHANGED )
  199. {
  200. found = true;
  201. break;
  202. }
  203. }
  204. if( !found )
  205. {
  206. // Create a clean copy of the parent module
  207. MODULE* orig = static_cast<MODULE*>( item );
  208. MODULE* clone = new MODULE( *orig );
  209. clone->SetParent( GetBoard() );
  210. // Clear current flags (which can be temporary set by a current edit command)
  211. for( auto child : clone->GraphicalItems() )
  212. child->ClearEditFlags();
  213. for( auto pad : clone->Pads() )
  214. pad->ClearEditFlags();
  215. clone->Reference().ClearEditFlags();
  216. clone->Value().ClearEditFlags();
  217. ITEM_PICKER picker( item, UR_CHANGED );
  218. picker.SetLink( clone );
  219. commandToUndo->PushItem( picker );
  220. orig->SetLastEditTime();
  221. }
  222. else
  223. {
  224. continue;
  225. }
  226. }
  227. else
  228. {
  229. // Normal case: all other BOARD_ITEMs, are simply copied to the new list
  230. commandToUndo->PushItem( curr_picker );
  231. }
  232. }
  233. for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
  234. {
  235. EDA_ITEM* item = aItemsList.GetPickedItem( ii );
  236. UNDO_REDO_T command = commandToUndo->GetPickedItemStatus( ii );
  237. if( command == UR_UNSPECIFIED )
  238. {
  239. command = aTypeCommand;
  240. commandToUndo->SetPickedItemStatus( command, ii );
  241. }
  242. wxASSERT( item );
  243. switch( command )
  244. {
  245. case UR_CHANGED:
  246. case UR_DRILLORIGIN:
  247. case UR_GRIDORIGIN:
  248. /* If needed, create a copy of item, and put in undo list
  249. * in the picker, as link
  250. * If this link is not null, the copy is already done
  251. */
  252. if( commandToUndo->GetPickedItemLink( ii ) == NULL )
  253. {
  254. EDA_ITEM* cloned = item->Clone();
  255. commandToUndo->SetPickedItemLink( cloned, ii );
  256. }
  257. break;
  258. case UR_MOVED:
  259. case UR_ROTATED:
  260. case UR_ROTATED_CLOCKWISE:
  261. case UR_FLIPPED:
  262. case UR_NEW:
  263. case UR_DELETED:
  264. case UR_PAGESETTINGS:
  265. break;
  266. default:
  267. {
  268. wxLogDebug( wxT( "SaveCopyInUndoList() error (unknown code %X)" ), command );
  269. }
  270. break;
  271. }
  272. }
  273. if( commandToUndo->GetCount() )
  274. {
  275. /* Save the copy in undo list */
  276. GetScreen()->PushCommandToUndoList( commandToUndo );
  277. /* Clear redo list, because after a new command one cannot redo a command */
  278. GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
  279. }
  280. else
  281. {
  282. // Should not occur
  283. wxASSERT( false );
  284. delete commandToUndo;
  285. }
  286. }
  287. void PCB_BASE_EDIT_FRAME::RestoreCopyFromUndoList( wxCommandEvent& aEvent )
  288. {
  289. if( UndoRedoBlocked() )
  290. return;
  291. if( GetScreen()->GetUndoCommandCount() <= 0 )
  292. return;
  293. // Inform tools that undo command was issued
  294. m_toolManager->ProcessEvent( { TC_MESSAGE, TA_UNDO_REDO_PRE, AS_GLOBAL } );
  295. // Get the old list
  296. PICKED_ITEMS_LIST* List = GetScreen()->PopCommandFromUndoList();
  297. // Undo the command
  298. PutDataInPreviousState( List, false );
  299. // Put the old list in RedoList
  300. List->ReversePickersListOrder();
  301. GetScreen()->PushCommandToRedoList( List );
  302. OnModify();
  303. m_toolManager->ProcessEvent( { TC_MESSAGE, TA_UNDO_REDO_POST, AS_GLOBAL } );
  304. GetCanvas()->Refresh();
  305. }
  306. void PCB_BASE_EDIT_FRAME::RestoreCopyFromRedoList( wxCommandEvent& aEvent )
  307. {
  308. if( UndoRedoBlocked() )
  309. return;
  310. if( GetScreen()->GetRedoCommandCount() == 0 )
  311. return;
  312. // Inform tools that redo command was issued
  313. m_toolManager->ProcessEvent( { TC_MESSAGE, TA_UNDO_REDO_PRE, AS_GLOBAL } );
  314. // Get the old list
  315. PICKED_ITEMS_LIST* List = GetScreen()->PopCommandFromRedoList();
  316. // Redo the command
  317. PutDataInPreviousState( List, true );
  318. // Put the old list in UndoList
  319. List->ReversePickersListOrder();
  320. GetScreen()->PushCommandToUndoList( List );
  321. OnModify();
  322. m_toolManager->ProcessEvent( { TC_MESSAGE, TA_UNDO_REDO_POST, AS_GLOBAL } );
  323. GetCanvas()->Refresh();
  324. }
  325. void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool aRedoCommand,
  326. bool aRebuildRatsnet )
  327. {
  328. bool not_found = false;
  329. bool reBuild_ratsnest = false;
  330. bool deep_reBuild_ratsnest = false; // true later if pointers must be rebuilt
  331. auto view = GetCanvas()->GetView();
  332. auto connectivity = GetBoard()->GetConnectivity();
  333. // Undo in the reverse order of list creation: (this can allow stacked changes
  334. // like the same item can be changes and deleted in the same complex command
  335. // Restore changes in reverse order
  336. for( int ii = aList->GetCount() - 1; ii >= 0 ; ii-- )
  337. {
  338. EDA_ITEM* eda_item = aList->GetPickedItem( (unsigned) ii );
  339. /* Test for existence of item on board.
  340. * It could be deleted, and no more on board:
  341. * - if a call to SaveCopyInUndoList was forgotten in Pcbnew
  342. * - in zones outlines, when a change in one zone merges this zone with an other
  343. * This test avoids a Pcbnew crash
  344. * Obviously, this test is not made for deleted items
  345. */
  346. UNDO_REDO_T status = aList->GetPickedItemStatus( ii );
  347. if( status != UR_DELETED
  348. && status != UR_DRILLORIGIN // origin markers never on board
  349. && status != UR_GRIDORIGIN // origin markers never on board
  350. && status != UR_PAGESETTINGS ) // nor are page settings proxy items
  351. {
  352. if( !TestForExistingItem( GetBoard(), (BOARD_ITEM*) eda_item ) )
  353. {
  354. // Checking if it ever happens
  355. wxASSERT_MSG( false, "Item in the undo buffer does not exist" );
  356. // Remove this non existent item
  357. aList->RemovePicker( ii );
  358. ii++; // the current item was removed, ii points now the next item
  359. // decrement it because it will be incremented later
  360. not_found = true;
  361. if( aList->GetCount() == 0 )
  362. break;
  363. continue;
  364. }
  365. }
  366. // see if we must rebuild ratsnets and pointers lists
  367. switch( eda_item->Type() )
  368. {
  369. case PCB_MODULE_T:
  370. deep_reBuild_ratsnest = true; // Pointers on pads can be invalid
  371. // Fall through
  372. case PCB_ZONE_AREA_T:
  373. case PCB_TRACE_T:
  374. case PCB_VIA_T:
  375. reBuild_ratsnest = true;
  376. break;
  377. case PCB_NETINFO_T:
  378. reBuild_ratsnest = true;
  379. deep_reBuild_ratsnest = true;
  380. break;
  381. default:
  382. break;
  383. }
  384. switch( aList->GetPickedItemStatus( ii ) )
  385. {
  386. case UR_CHANGED: /* Exchange old and new data for each item */
  387. {
  388. BOARD_ITEM* item = (BOARD_ITEM*) eda_item;
  389. BOARD_ITEM* image = (BOARD_ITEM*) aList->GetPickedItemLink( ii );
  390. // Remove all pads/drawings/texts, as they become invalid
  391. // for the VIEW after SwapData() called for modules
  392. view->Remove( eda_item );
  393. connectivity->Remove( item );
  394. SwapItemData( item, image );
  395. view->Add( eda_item );
  396. connectivity->Add( item );
  397. }
  398. break;
  399. case UR_NEW: /* new items are deleted */
  400. aList->SetPickedItemStatus( UR_DELETED, ii );
  401. GetModel()->Remove( (BOARD_ITEM*) eda_item );
  402. view->Remove( eda_item );
  403. break;
  404. case UR_DELETED: /* deleted items are put in List, as new items */
  405. aList->SetPickedItemStatus( UR_NEW, ii );
  406. GetModel()->Add( (BOARD_ITEM*) eda_item );
  407. view->Add( eda_item );
  408. break;
  409. case UR_MOVED:
  410. {
  411. BOARD_ITEM* item = (BOARD_ITEM*) eda_item;
  412. item->Move( aRedoCommand ? aList->m_TransformPoint : -aList->m_TransformPoint );
  413. view->Update( item, KIGFX::GEOMETRY );
  414. connectivity->Update( item );
  415. }
  416. break;
  417. case UR_ROTATED:
  418. {
  419. BOARD_ITEM* item = (BOARD_ITEM*) eda_item;
  420. item->Rotate( aList->m_TransformPoint,
  421. aRedoCommand ? m_rotationAngle : -m_rotationAngle );
  422. view->Update( item, KIGFX::GEOMETRY );
  423. connectivity->Update( item );
  424. }
  425. break;
  426. case UR_ROTATED_CLOCKWISE:
  427. {
  428. BOARD_ITEM* item = (BOARD_ITEM*) eda_item;
  429. item->Rotate( aList->m_TransformPoint,
  430. aRedoCommand ? -m_rotationAngle : m_rotationAngle );
  431. view->Update( item, KIGFX::GEOMETRY );
  432. connectivity->Update( item );
  433. }
  434. break;
  435. case UR_FLIPPED:
  436. {
  437. BOARD_ITEM* item = (BOARD_ITEM*) eda_item;
  438. item->Flip( aList->m_TransformPoint );
  439. view->Update( item, KIGFX::LAYERS );
  440. connectivity->Update( item );
  441. }
  442. break;
  443. case UR_DRILLORIGIN:
  444. case UR_GRIDORIGIN:
  445. {
  446. BOARD_ITEM* item = (BOARD_ITEM*) eda_item;
  447. BOARD_ITEM* image = (BOARD_ITEM*) aList->GetPickedItemLink( ii );
  448. VECTOR2D origin = image->GetPosition();
  449. image->SetPosition( item->GetPosition() );
  450. if( aList->GetPickedItemStatus( ii ) == UR_DRILLORIGIN )
  451. PCB_EDITOR_CONTROL::DoSetDrillOrigin( view, this, item, origin );
  452. else
  453. PCBNEW_CONTROL::DoSetGridOrigin( view, this, item, origin );
  454. }
  455. break;
  456. case UR_PAGESETTINGS:
  457. {
  458. // swap current settings with stored settings
  459. WS_PROXY_UNDO_ITEM alt_item( this );
  460. WS_PROXY_UNDO_ITEM* item = (WS_PROXY_UNDO_ITEM*) eda_item;
  461. item->Restore( this );
  462. *item = alt_item;
  463. GetToolManager()->RunAction( ACTIONS::zoomFitScreen, true );
  464. }
  465. break;
  466. default:
  467. wxLogDebug( wxT( "PutDataInPreviousState() error (unknown code %X)" ),
  468. aList->GetPickedItemStatus( ii ) );
  469. break;
  470. }
  471. }
  472. if( not_found )
  473. wxMessageBox( _( "Incomplete undo/redo operation: some items not found" ) );
  474. // Rebuild pointers and connectivity that can be changed.
  475. // connectivity can be rebuilt only in the board editor frame
  476. if( IsType( FRAME_PCB ) && ( reBuild_ratsnest || deep_reBuild_ratsnest ) )
  477. {
  478. Compile_Ratsnest( false );
  479. }
  480. SELECTION_TOOL* selTool = m_toolManager->GetTool<SELECTION_TOOL>();
  481. selTool->RebuildSelection();
  482. GetBoard()->SanitizeNetcodes();
  483. }
  484. void PCB_SCREEN::ClearUndoORRedoList( UNDO_REDO_CONTAINER& aList, int aItemCount )
  485. {
  486. if( aItemCount == 0 )
  487. return;
  488. unsigned icnt = aList.m_CommandsList.size();
  489. if( aItemCount > 0 )
  490. icnt = aItemCount;
  491. for( unsigned ii = 0; ii < icnt; ii++ )
  492. {
  493. if( aList.m_CommandsList.size() == 0 )
  494. break;
  495. PICKED_ITEMS_LIST* curr_cmd = aList.m_CommandsList[0];
  496. aList.m_CommandsList.erase( aList.m_CommandsList.begin() );
  497. curr_cmd->ClearListAndDeleteItems();
  498. delete curr_cmd; // Delete command
  499. }
  500. }
  501. void PCB_BASE_EDIT_FRAME::RollbackFromUndo()
  502. {
  503. PICKED_ITEMS_LIST* undo = GetScreen()->PopCommandFromUndoList();
  504. PutDataInPreviousState( undo, false );
  505. undo->ClearListAndDeleteItems();
  506. delete undo;
  507. GetCanvas()->Refresh();
  508. }