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.

665 lines
21 KiB

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