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.

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