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.

901 lines
28 KiB

6 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 CERN
  5. * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
  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 <tool/picker_tool.h>
  25. #include <tools/ee_selection_tool.h>
  26. #include <tools/symbol_editor_pin_tool.h>
  27. #include <tools/symbol_editor_drawing_tools.h>
  28. #include <tools/symbol_editor_move_tool.h>
  29. #include <ee_actions.h>
  30. #include <string_utils.h>
  31. #include <symbol_edit_frame.h>
  32. #include <sch_commit.h>
  33. #include <dialogs/dialog_lib_shape_properties.h>
  34. #include <dialogs/dialog_lib_text_properties.h>
  35. #include <dialogs/dialog_lib_textbox_properties.h>
  36. #include <dialogs/dialog_field_properties.h>
  37. #include <dialogs/dialog_lib_symbol_properties.h>
  38. #include <dialogs/dialog_lib_edit_pin_table.h>
  39. #include <dialogs/dialog_update_symbol_fields.h>
  40. #include <sch_plugins/kicad/sch_sexpr_plugin.h>
  41. #include <lib_text.h>
  42. #include <lib_textbox.h>
  43. #include "symbol_editor_edit_tool.h"
  44. #include <wx/textdlg.h> // for wxTextEntryDialog
  45. #include <math/util.h> // for KiROUND
  46. SYMBOL_EDITOR_EDIT_TOOL::SYMBOL_EDITOR_EDIT_TOOL() :
  47. EE_TOOL_BASE( "eeschema.SymbolEditTool" ),
  48. m_pickerItem( nullptr )
  49. {
  50. }
  51. bool SYMBOL_EDITOR_EDIT_TOOL::Init()
  52. {
  53. EE_TOOL_BASE::Init();
  54. SYMBOL_EDITOR_DRAWING_TOOLS* drawingTools = m_toolMgr->GetTool<SYMBOL_EDITOR_DRAWING_TOOLS>();
  55. SYMBOL_EDITOR_MOVE_TOOL* moveTool = m_toolMgr->GetTool<SYMBOL_EDITOR_MOVE_TOOL>();
  56. wxASSERT_MSG( drawingTools, "eeschema.SymbolDrawing tool is not available" );
  57. auto haveSymbolCondition =
  58. [&]( const SELECTION& sel )
  59. {
  60. return m_isSymbolEditor &&
  61. static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
  62. };
  63. auto canEdit =
  64. [&]( const SELECTION& sel )
  65. {
  66. SYMBOL_EDIT_FRAME* editor = static_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  67. wxCHECK( editor, false );
  68. if( !editor->IsSymbolEditable() )
  69. return false;
  70. if( editor->IsSymbolAlias() )
  71. {
  72. for( EDA_ITEM* item : sel )
  73. {
  74. if( item->Type() != LIB_FIELD_T )
  75. return false;
  76. }
  77. }
  78. return true;
  79. };
  80. // Add edit actions to the move tool menu
  81. if( moveTool )
  82. {
  83. CONDITIONAL_MENU& moveMenu = moveTool->GetToolMenu().GetMenu();
  84. moveMenu.AddSeparator( 200 );
  85. moveMenu.AddItem( EE_ACTIONS::rotateCCW, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  86. moveMenu.AddItem( EE_ACTIONS::rotateCW, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  87. moveMenu.AddItem( EE_ACTIONS::mirrorV, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  88. moveMenu.AddItem( EE_ACTIONS::mirrorH, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  89. moveMenu.AddItem( EE_ACTIONS::properties, canEdit && EE_CONDITIONS::Count( 1 ), 200 );
  90. moveMenu.AddSeparator( 300 );
  91. moveMenu.AddItem( ACTIONS::cut, EE_CONDITIONS::IdleSelection, 300 );
  92. moveMenu.AddItem( ACTIONS::copy, EE_CONDITIONS::IdleSelection, 300 );
  93. moveMenu.AddItem( ACTIONS::duplicate, canEdit && EE_CONDITIONS::NotEmpty, 300 );
  94. moveMenu.AddItem( ACTIONS::doDelete, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  95. moveMenu.AddSeparator( 400 );
  96. moveMenu.AddItem( ACTIONS::selectAll, haveSymbolCondition, 400 );
  97. moveMenu.AddItem( ACTIONS::unselectAll, haveSymbolCondition, 400 );
  98. }
  99. // Add editing actions to the drawing tool menu
  100. CONDITIONAL_MENU& drawMenu = drawingTools->GetToolMenu().GetMenu();
  101. drawMenu.AddSeparator( 200 );
  102. drawMenu.AddItem( EE_ACTIONS::rotateCCW, canEdit && EE_CONDITIONS::IdleSelection, 200 );
  103. drawMenu.AddItem( EE_ACTIONS::rotateCW, canEdit && EE_CONDITIONS::IdleSelection, 200 );
  104. drawMenu.AddItem( EE_ACTIONS::mirrorV, canEdit && EE_CONDITIONS::IdleSelection, 200 );
  105. drawMenu.AddItem( EE_ACTIONS::mirrorH, canEdit && EE_CONDITIONS::IdleSelection, 200 );
  106. drawMenu.AddItem( EE_ACTIONS::properties, canEdit && EE_CONDITIONS::Count( 1 ), 200 );
  107. // Add editing actions to the selection tool menu
  108. CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
  109. selToolMenu.AddItem( EE_ACTIONS::rotateCCW, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  110. selToolMenu.AddItem( EE_ACTIONS::rotateCW, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  111. selToolMenu.AddItem( EE_ACTIONS::mirrorV, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  112. selToolMenu.AddItem( EE_ACTIONS::mirrorH, canEdit && EE_CONDITIONS::NotEmpty, 200 );
  113. selToolMenu.AddItem( EE_ACTIONS::properties, canEdit && EE_CONDITIONS::Count( 1 ), 200 );
  114. selToolMenu.AddSeparator( 300 );
  115. selToolMenu.AddItem( ACTIONS::cut, EE_CONDITIONS::IdleSelection, 300 );
  116. selToolMenu.AddItem( ACTIONS::copy, EE_CONDITIONS::IdleSelection, 300 );
  117. selToolMenu.AddItem( ACTIONS::paste, canEdit && EE_CONDITIONS::Idle, 300 );
  118. selToolMenu.AddItem( ACTIONS::duplicate, canEdit && EE_CONDITIONS::NotEmpty, 300 );
  119. selToolMenu.AddItem( ACTIONS::doDelete, canEdit && EE_CONDITIONS::NotEmpty, 300 );
  120. selToolMenu.AddSeparator( 400 );
  121. selToolMenu.AddItem( ACTIONS::selectAll, haveSymbolCondition, 400 );
  122. selToolMenu.AddItem( ACTIONS::unselectAll, haveSymbolCondition, 400 );
  123. return true;
  124. }
  125. int SYMBOL_EDITOR_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
  126. {
  127. EE_SELECTION& selection = m_selectionTool->RequestSelection();
  128. if( selection.GetSize() == 0 )
  129. return 0;
  130. VECTOR2I rotPoint;
  131. bool ccw = ( aEvent.Matches( EE_ACTIONS::rotateCCW.MakeEvent() ) );
  132. LIB_ITEM* item = static_cast<LIB_ITEM*>( selection.Front() );
  133. SCH_COMMIT localCommit( m_toolMgr );
  134. SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
  135. if( !commit )
  136. commit = &localCommit;
  137. if( !item->IsMoving() )
  138. commit->Modify( m_frame->GetCurSymbol(), m_frame->GetScreen() );
  139. if( selection.GetSize() == 1 )
  140. rotPoint = item->GetPosition();
  141. else
  142. rotPoint = m_frame->GetNearestHalfGridPosition( mapCoords( selection.GetCenter() ) );
  143. for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
  144. {
  145. item = static_cast<LIB_ITEM*>( selection.GetItem( ii ) );
  146. item->Rotate( rotPoint, ccw );
  147. m_frame->UpdateItem( item, false, true );
  148. }
  149. if( item->IsMoving() )
  150. {
  151. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  152. }
  153. else
  154. {
  155. if( selection.IsHover() )
  156. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  157. if( !localCommit.Empty() )
  158. localCommit.Push( _( "Rotate" ) );
  159. }
  160. return 0;
  161. }
  162. int SYMBOL_EDITOR_EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
  163. {
  164. EE_SELECTION& selection = m_selectionTool->RequestSelection();
  165. if( selection.GetSize() == 0 )
  166. return 0;
  167. VECTOR2I mirrorPoint;
  168. bool xAxis = ( aEvent.Matches( EE_ACTIONS::mirrorV.MakeEvent() ) );
  169. LIB_ITEM* item = static_cast<LIB_ITEM*>( selection.Front() );
  170. if( !item->IsMoving() )
  171. saveCopyInUndoList( m_frame->GetCurSymbol(), UNDO_REDO::LIBEDIT );
  172. if( selection.GetSize() == 1 )
  173. mirrorPoint = item->GetPosition();
  174. else
  175. mirrorPoint = m_frame->GetNearestHalfGridPosition( mapCoords( selection.GetCenter() ) );
  176. for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
  177. {
  178. item = static_cast<LIB_ITEM*>( selection.GetItem( ii ) );
  179. if( xAxis )
  180. item->MirrorVertical( mirrorPoint );
  181. else
  182. item->MirrorHorizontal( mirrorPoint );
  183. m_frame->UpdateItem( item, false, true );
  184. }
  185. if( item->IsMoving() )
  186. {
  187. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  188. }
  189. else
  190. {
  191. if( selection.IsHover() )
  192. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  193. m_frame->OnModify();
  194. }
  195. return 0;
  196. }
  197. static std::vector<KICAD_T> nonFields =
  198. {
  199. LIB_SYMBOL_T,
  200. LIB_SHAPE_T,
  201. LIB_TEXT_T,
  202. LIB_TEXTBOX_T,
  203. LIB_PIN_T
  204. };
  205. int SYMBOL_EDITOR_EDIT_TOOL::DoDelete( const TOOL_EVENT& aEvent )
  206. {
  207. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  208. std::deque<EDA_ITEM*> items = m_selectionTool->RequestSelection( nonFields ).GetItems();
  209. SCH_COMMIT commit( m_frame );
  210. if( items.empty() )
  211. return 0;
  212. // Don't leave a freed pointer in the selection
  213. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  214. commit.Modify( symbol, m_frame->GetScreen() );
  215. std::set<LIB_ITEM*> toDelete;
  216. for( EDA_ITEM* item : items )
  217. {
  218. if( item->Type() == LIB_PIN_T )
  219. {
  220. LIB_PIN* curr_pin = static_cast<LIB_PIN*>( item );
  221. VECTOR2I pos = curr_pin->GetPosition();
  222. toDelete.insert( curr_pin );
  223. // when pin editing is synchronized, pins in the same position, with the same name
  224. // in different units are also removed. But only one pin per unit (matching)
  225. if( m_frame->SynchronizePins() )
  226. {
  227. std::vector<bool> got_unit( symbol->GetUnitCount() + 1 );
  228. got_unit[curr_pin->GetUnit()] = true;
  229. int curr_convert = curr_pin->GetConvert();
  230. ELECTRICAL_PINTYPE etype = curr_pin->GetType();
  231. wxString name = curr_pin->GetName();
  232. std::vector<LIB_PIN*> pins = symbol->GetAllLibPins();
  233. for( LIB_PIN* pin : pins )
  234. {
  235. if( got_unit[pin->GetUnit()] )
  236. continue;
  237. if( pin->GetPosition() != pos )
  238. continue;
  239. if( pin->GetConvert() != curr_convert )
  240. continue;
  241. if( pin->GetType() != etype )
  242. continue;
  243. if( pin->GetName() != name )
  244. continue;
  245. toDelete.insert( pin );
  246. got_unit[pin->GetUnit()] = true;
  247. }
  248. }
  249. }
  250. else
  251. {
  252. toDelete.insert( (LIB_ITEM*) item );
  253. }
  254. }
  255. for( LIB_ITEM* item : toDelete )
  256. symbol->RemoveDrawItem( item );
  257. commit.Push( _( "Delete" ) );
  258. m_frame->RebuildView();
  259. return 0;
  260. }
  261. #define HITTEST_THRESHOLD_PIXELS 5
  262. int SYMBOL_EDITOR_EDIT_TOOL::DeleteItemCursor( const TOOL_EVENT& aEvent )
  263. {
  264. PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
  265. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  266. m_pickerItem = nullptr;
  267. // Deactivate other tools; particularly important if another PICKER is currently running
  268. Activate();
  269. picker->SetCursor( KICURSOR::REMOVE );
  270. picker->SetClickHandler(
  271. [this]( const VECTOR2D& aPosition ) -> bool
  272. {
  273. if( m_pickerItem )
  274. {
  275. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  276. selectionTool->UnbrightenItem( m_pickerItem );
  277. selectionTool->AddItemToSel( m_pickerItem, true /*quiet mode*/ );
  278. m_toolMgr->RunAction( ACTIONS::doDelete );
  279. m_pickerItem = nullptr;
  280. }
  281. return true;
  282. } );
  283. picker->SetMotionHandler(
  284. [this]( const VECTOR2D& aPos )
  285. {
  286. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  287. EE_COLLECTOR collector;
  288. selectionTool->CollectHits( collector, aPos, nonFields );
  289. // Remove unselectable items
  290. for( int i = collector.GetCount() - 1; i >= 0; --i )
  291. {
  292. if( !selectionTool->Selectable( collector[ i ] ) )
  293. collector.Remove( i );
  294. }
  295. if( collector.GetCount() > 1 )
  296. selectionTool->GuessSelectionCandidates( collector, aPos );
  297. EDA_ITEM* item = collector.GetCount() == 1 ? collector[ 0 ] : nullptr;
  298. if( m_pickerItem != item )
  299. {
  300. if( m_pickerItem )
  301. selectionTool->UnbrightenItem( m_pickerItem );
  302. m_pickerItem = item;
  303. if( m_pickerItem )
  304. selectionTool->BrightenItem( m_pickerItem );
  305. }
  306. } );
  307. picker->SetFinalizeHandler(
  308. [this]( const int& aFinalState )
  309. {
  310. if( m_pickerItem )
  311. m_toolMgr->GetTool<EE_SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
  312. // Wake the selection tool after exiting to ensure the cursor gets updated
  313. m_toolMgr->PostAction( EE_ACTIONS::selectionActivate );
  314. } );
  315. m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
  316. return 0;
  317. }
  318. int SYMBOL_EDITOR_EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
  319. {
  320. EE_SELECTION& selection = m_selectionTool->RequestSelection();
  321. if( selection.Empty() || aEvent.IsAction( &EE_ACTIONS::symbolProperties ) )
  322. {
  323. if( m_frame->GetCurSymbol() )
  324. editSymbolProperties();
  325. }
  326. else if( selection.Size() == 1 )
  327. {
  328. LIB_ITEM* item = (LIB_ITEM*) selection.Front();
  329. // Save copy for undo if not in edit (edit command already handle the save copy)
  330. if( item->GetEditFlags() == 0 )
  331. saveCopyInUndoList( item->GetParent(), UNDO_REDO::LIBEDIT );
  332. switch( item->Type() )
  333. {
  334. case LIB_PIN_T:
  335. {
  336. SYMBOL_EDITOR_PIN_TOOL* pinTool = m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>();
  337. if( pinTool )
  338. pinTool->EditPinProperties( (LIB_PIN*) item );
  339. }
  340. break;
  341. case LIB_SHAPE_T:
  342. editShapeProperties( static_cast<LIB_SHAPE*>( item ) );
  343. break;
  344. case LIB_TEXT_T:
  345. editTextProperties( item );
  346. break;
  347. case LIB_TEXTBOX_T:
  348. editTextBoxProperties( item );
  349. break;
  350. case LIB_FIELD_T:
  351. editFieldProperties( (LIB_FIELD*) item );
  352. break;
  353. default:
  354. wxFAIL_MSG( wxT( "Unhandled item <" ) + item->GetClass() + wxT( ">" ) );
  355. break;
  356. }
  357. }
  358. if( selection.IsHover() )
  359. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  360. return 0;
  361. }
  362. void SYMBOL_EDITOR_EDIT_TOOL::editShapeProperties( LIB_SHAPE* aShape )
  363. {
  364. DIALOG_LIB_SHAPE_PROPERTIES dlg( m_frame, aShape );
  365. if( dlg.ShowModal() != wxID_OK )
  366. return;
  367. updateItem( aShape, true );
  368. m_frame->GetCanvas()->Refresh();
  369. m_frame->OnModify();
  370. SYMBOL_EDITOR_DRAWING_TOOLS* drawingTools = m_toolMgr->GetTool<SYMBOL_EDITOR_DRAWING_TOOLS>();
  371. drawingTools->SetDrawSpecificConvert( !dlg.GetApplyToAllConversions() );
  372. drawingTools->SetDrawSpecificUnit( !dlg.GetApplyToAllUnits() );
  373. std::vector<MSG_PANEL_ITEM> items;
  374. aShape->GetMsgPanelInfo( m_frame, items );
  375. m_frame->SetMsgPanel( items );
  376. }
  377. void SYMBOL_EDITOR_EDIT_TOOL::editTextProperties( LIB_ITEM* aItem )
  378. {
  379. if ( aItem->Type() != LIB_TEXT_T )
  380. return;
  381. DIALOG_LIB_TEXT_PROPERTIES dlg( m_frame, static_cast<LIB_TEXT*>( aItem ) );
  382. if( dlg.ShowModal() != wxID_OK )
  383. return;
  384. updateItem( aItem, true );
  385. m_frame->GetCanvas()->Refresh();
  386. m_frame->OnModify( );
  387. }
  388. void SYMBOL_EDITOR_EDIT_TOOL::editTextBoxProperties( LIB_ITEM* aItem )
  389. {
  390. if ( aItem->Type() != LIB_TEXTBOX_T )
  391. return;
  392. DIALOG_LIB_TEXTBOX_PROPERTIES dlg( m_frame, static_cast<LIB_TEXTBOX*>( aItem ) );
  393. if( dlg.ShowModal() != wxID_OK )
  394. return;
  395. updateItem( aItem, true );
  396. m_frame->GetCanvas()->Refresh();
  397. m_frame->OnModify( );
  398. }
  399. void SYMBOL_EDITOR_EDIT_TOOL::editFieldProperties( LIB_FIELD* aField )
  400. {
  401. if( aField == nullptr )
  402. return;
  403. wxString caption;
  404. LIB_SYMBOL* parent = aField->GetParent();
  405. wxCHECK( parent, /* void */ );
  406. if( aField->GetId() >= 0 && aField->GetId() < MANDATORY_FIELDS )
  407. caption.Printf( _( "Edit %s Field" ), TitleCaps( aField->GetName() ) );
  408. else
  409. caption.Printf( _( "Edit '%s' Field" ), aField->GetName() );
  410. DIALOG_LIB_FIELD_PROPERTIES dlg( m_frame, caption, aField );
  411. // The dialog may invoke a kiway player for footprint fields
  412. // so we must use a quasimodal dialog.
  413. if( dlg.ShowQuasiModal() != wxID_OK )
  414. return;
  415. wxString newFieldValue = EscapeString( dlg.GetText(), CTX_LIBID );
  416. wxString oldFieldValue = aField->GetFullText( m_frame->GetUnit() );
  417. saveCopyInUndoList( parent, UNDO_REDO::LIBEDIT );
  418. dlg.UpdateField( aField );
  419. updateItem( aField, true );
  420. m_frame->GetCanvas()->Refresh();
  421. m_frame->OnModify();
  422. m_frame->UpdateSymbolMsgPanelInfo();
  423. }
  424. void SYMBOL_EDITOR_EDIT_TOOL::editSymbolProperties()
  425. {
  426. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  427. bool partLocked = symbol->UnitsLocked();
  428. m_toolMgr->RunAction( ACTIONS::cancelInteractive );
  429. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  430. DIALOG_LIB_SYMBOL_PROPERTIES dlg( m_frame, symbol );
  431. // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
  432. // frame. Therefore this dialog as a modal frame parent, MUST be run under
  433. // quasimodal mode for the quasimodal frame support to work. So don't use
  434. // the QUASIMODAL macros here.
  435. if( dlg.ShowQuasiModal() != wxID_OK )
  436. return;
  437. m_frame->OnModify();
  438. // if m_UnitSelectionLocked has changed, set some edit options or defaults
  439. // to the best value
  440. if( partLocked != symbol->UnitsLocked() )
  441. {
  442. SYMBOL_EDITOR_DRAWING_TOOLS* tools = m_toolMgr->GetTool<SYMBOL_EDITOR_DRAWING_TOOLS>();
  443. // Enable synchronized pin edit mode for symbols with interchangeable units
  444. m_frame->m_SyncPinEdit = !symbol->UnitsLocked();
  445. // also set default edit options to the better value
  446. // Usually if units are locked, graphic items are specific to each unit
  447. // and if units are interchangeable, graphic items are common to units
  448. tools->SetDrawSpecificUnit( symbol->UnitsLocked() );
  449. }
  450. }
  451. void SYMBOL_EDITOR_EDIT_TOOL::handlePinDuplication( LIB_PIN* aOldPin, LIB_PIN* aNewPin,
  452. int& aSymbolLastPinNumber )
  453. {
  454. if( !aNewPin->GetNumber().IsEmpty() )
  455. {
  456. // when duplicating a pin in symbol editor, assigning identical pin number
  457. // to the old one does not makes any sense, so assign the next unassigned number to it
  458. aSymbolLastPinNumber++;
  459. aNewPin->SetNumber( wxString::Format( wxT( "%i" ), aSymbolLastPinNumber ) );
  460. }
  461. }
  462. int SYMBOL_EDITOR_EDIT_TOOL::PinTable( const TOOL_EVENT& aEvent )
  463. {
  464. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  465. if( !symbol )
  466. return 0;
  467. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  468. saveCopyInUndoList( symbol, UNDO_REDO::LIBEDIT );
  469. DIALOG_LIB_EDIT_PIN_TABLE dlg( m_frame, symbol );
  470. if( dlg.ShowModal() == wxID_CANCEL )
  471. return -1;
  472. m_frame->RebuildView();
  473. m_frame->OnModify();
  474. return 0;
  475. }
  476. int SYMBOL_EDITOR_EDIT_TOOL::UpdateSymbolFields( const TOOL_EVENT& aEvent )
  477. {
  478. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  479. if( !symbol )
  480. return 0;
  481. if( !symbol->IsAlias() )
  482. {
  483. m_frame->ShowInfoBarError( _( "Symbol is not derived from another symbol." ) );
  484. }
  485. else
  486. {
  487. DIALOG_UPDATE_SYMBOL_FIELDS dlg( m_frame, symbol );
  488. if( dlg.ShowModal() == wxID_CANCEL )
  489. return -1;
  490. }
  491. return 0;
  492. }
  493. int SYMBOL_EDITOR_EDIT_TOOL::SetUnitDisplayName( const TOOL_EVENT& aEvent )
  494. {
  495. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  496. if( !symbol )
  497. return 0;
  498. int unitid = m_frame->GetUnit();
  499. if( unitid == 0 )
  500. {
  501. return -1;
  502. }
  503. wxString promptText = wxString::Format( _( "Enter display name for unit %s" ),
  504. symbol->GetUnitReference( unitid ) );
  505. wxString currentvalue;
  506. if( symbol->HasUnitDisplayName( unitid ) )
  507. {
  508. currentvalue = symbol->GetUnitDisplayName( unitid );
  509. }
  510. wxTextEntryDialog dlg( m_frame, promptText, _( "Set Unit Display Name" ), currentvalue );
  511. if( dlg.ShowModal() == wxID_OK )
  512. {
  513. saveCopyInUndoList( symbol, UNDO_REDO::LIBEDIT );
  514. symbol->SetUnitDisplayName( unitid, dlg.GetValue() );
  515. m_frame->RebuildSymbolUnitsList();
  516. m_frame->OnModify();
  517. }
  518. else
  519. {
  520. return -1;
  521. }
  522. return 0;
  523. }
  524. int SYMBOL_EDITOR_EDIT_TOOL::Undo( const TOOL_EVENT& aEvent )
  525. {
  526. m_frame->GetSymbolFromUndoList();
  527. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  528. selTool->RebuildSelection();
  529. return 0;
  530. }
  531. int SYMBOL_EDITOR_EDIT_TOOL::Redo( const TOOL_EVENT& aEvent )
  532. {
  533. m_frame->GetSymbolFromRedoList();
  534. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  535. selTool->RebuildSelection();
  536. return 0;
  537. }
  538. int SYMBOL_EDITOR_EDIT_TOOL::Cut( const TOOL_EVENT& aEvent )
  539. {
  540. int retVal = Copy( aEvent );
  541. if( retVal == 0 )
  542. retVal = DoDelete( aEvent );
  543. return retVal;
  544. }
  545. int SYMBOL_EDITOR_EDIT_TOOL::Copy( const TOOL_EVENT& aEvent )
  546. {
  547. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  548. EE_SELECTION& selection = m_selectionTool->RequestSelection( nonFields );
  549. if( !symbol || !selection.GetSize() )
  550. return 0;
  551. for( LIB_ITEM& item : symbol->GetDrawItems() )
  552. {
  553. if( item.Type() == LIB_FIELD_T )
  554. continue;
  555. wxASSERT( !item.HasFlag( STRUCT_DELETED ) );
  556. if( !item.IsSelected() )
  557. item.SetFlags( STRUCT_DELETED );
  558. }
  559. LIB_SYMBOL* partCopy = new LIB_SYMBOL( *symbol );
  560. STRING_FORMATTER formatter;
  561. SCH_SEXPR_PLUGIN::FormatLibSymbol( partCopy, formatter );
  562. delete partCopy;
  563. for( LIB_ITEM& item : symbol->GetDrawItems() )
  564. item.ClearFlags( STRUCT_DELETED );
  565. if( m_toolMgr->SaveClipboard( formatter.GetString() ) )
  566. return 0;
  567. else
  568. return -1;
  569. }
  570. int SYMBOL_EDITOR_EDIT_TOOL::Paste( const TOOL_EVENT& aEvent )
  571. {
  572. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  573. LIB_SYMBOL* newPart = nullptr;
  574. if( !symbol || symbol->IsAlias() )
  575. return 0;
  576. std::string clipboardData = m_toolMgr->GetClipboardUTF8();
  577. try
  578. {
  579. std::vector<LIB_SYMBOL*> newParts = SCH_SEXPR_PLUGIN::ParseLibSymbols( clipboardData, "Clipboard" );
  580. if( newParts.empty() || !newParts[0] )
  581. return -1;
  582. newPart = newParts[0];
  583. }
  584. catch( IO_ERROR& )
  585. {
  586. // If it's not a symbol then paste as text
  587. newPart = new LIB_SYMBOL( "dummy_part" );
  588. LIB_TEXT* newText = new LIB_TEXT( newPart );
  589. newText->SetText( clipboardData );
  590. newPart->AddDrawItem( newText );
  591. }
  592. SCH_COMMIT commit( m_toolMgr );
  593. commit.Modify( symbol );
  594. m_selectionTool->ClearSelection();
  595. for( LIB_ITEM& item : symbol->GetDrawItems() )
  596. item.ClearFlags( IS_NEW | IS_PASTED | SELECTED );
  597. for( LIB_ITEM& item : newPart->GetDrawItems() )
  598. {
  599. if( item.Type() == LIB_FIELD_T )
  600. continue;
  601. LIB_ITEM* newItem = (LIB_ITEM*) item.Duplicate();
  602. newItem->SetParent( symbol );
  603. newItem->SetFlags( IS_NEW | IS_PASTED | SELECTED );
  604. newItem->SetUnit( newItem->GetUnit() ? m_frame->GetUnit() : 0 );
  605. newItem->SetConvert( newItem->GetConvert() ? m_frame->GetConvert() : 0 );
  606. symbol->AddDrawItem( newItem );
  607. getView()->Add( newItem );
  608. }
  609. delete newPart;
  610. m_selectionTool->RebuildSelection();
  611. EE_SELECTION& selection = m_selectionTool->GetSelection();
  612. if( !selection.Empty() )
  613. {
  614. selection.SetReferencePoint( getViewControls()->GetCursorPosition( true ) );
  615. if( m_toolMgr->RunSynchronousAction( EE_ACTIONS::move, &commit ) )
  616. commit.Push( _( "Paste" ) );
  617. else
  618. commit.Revert();
  619. }
  620. return 0;
  621. }
  622. int SYMBOL_EDITOR_EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
  623. {
  624. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  625. EE_SELECTION& selection = m_selectionTool->RequestSelection( nonFields );
  626. SCH_COMMIT commit( m_toolMgr );
  627. if( selection.GetSize() == 0 )
  628. return 0;
  629. commit.Modify( symbol, m_frame->GetScreen() );
  630. EDA_ITEMS newItems;
  631. for( unsigned ii = 0; ii < selection.GetSize(); ++ii )
  632. {
  633. LIB_ITEM* oldItem = static_cast<LIB_ITEM*>( selection.GetItem( ii ) );
  634. LIB_ITEM* newItem = static_cast<LIB_ITEM*>( oldItem->Duplicate() );
  635. if( newItem->Type() == LIB_PIN_T )
  636. {
  637. LIB_PIN* newPin = static_cast<LIB_PIN*>( newItem );
  638. if( !newPin->GetNumber().IsEmpty() )
  639. newPin->SetNumber( wxString::Format( wxT( "%i" ), symbol->GetMaxPinNumber() + 1 ) );
  640. }
  641. oldItem->ClearFlags( IS_NEW | IS_PASTED | SELECTED );
  642. newItem->SetFlags( IS_NEW | IS_PASTED | SELECTED );
  643. newItem->SetParent( symbol );
  644. newItems.push_back( newItem );
  645. symbol->AddDrawItem( newItem );
  646. getView()->Add( newItem );
  647. }
  648. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  649. m_toolMgr->RunAction<EDA_ITEMS*>( EE_ACTIONS::addItemsToSel, &newItems );
  650. selection.SetReferencePoint( mapCoords( getViewControls()->GetCursorPosition( true ) ) );
  651. if( m_toolMgr->RunSynchronousAction( EE_ACTIONS::move, &commit ) )
  652. commit.Push( _( "Duplicate" ) );
  653. else
  654. commit.Revert();
  655. return 0;
  656. }
  657. void SYMBOL_EDITOR_EDIT_TOOL::setTransitions()
  658. {
  659. Go( &SYMBOL_EDITOR_EDIT_TOOL::Undo, ACTIONS::undo.MakeEvent() );
  660. Go( &SYMBOL_EDITOR_EDIT_TOOL::Redo, ACTIONS::redo.MakeEvent() );
  661. Go( &SYMBOL_EDITOR_EDIT_TOOL::Cut, ACTIONS::cut.MakeEvent() );
  662. Go( &SYMBOL_EDITOR_EDIT_TOOL::Copy, ACTIONS::copy.MakeEvent() );
  663. Go( &SYMBOL_EDITOR_EDIT_TOOL::Paste, ACTIONS::paste.MakeEvent() );
  664. Go( &SYMBOL_EDITOR_EDIT_TOOL::Duplicate, ACTIONS::duplicate.MakeEvent() );
  665. Go( &SYMBOL_EDITOR_EDIT_TOOL::Rotate, EE_ACTIONS::rotateCW.MakeEvent() );
  666. Go( &SYMBOL_EDITOR_EDIT_TOOL::Rotate, EE_ACTIONS::rotateCCW.MakeEvent() );
  667. Go( &SYMBOL_EDITOR_EDIT_TOOL::Mirror, EE_ACTIONS::mirrorV.MakeEvent() );
  668. Go( &SYMBOL_EDITOR_EDIT_TOOL::Mirror, EE_ACTIONS::mirrorH.MakeEvent() );
  669. Go( &SYMBOL_EDITOR_EDIT_TOOL::DoDelete, ACTIONS::doDelete.MakeEvent() );
  670. Go( &SYMBOL_EDITOR_EDIT_TOOL::DeleteItemCursor, ACTIONS::deleteTool.MakeEvent() );
  671. Go( &SYMBOL_EDITOR_EDIT_TOOL::Properties, EE_ACTIONS::properties.MakeEvent() );
  672. Go( &SYMBOL_EDITOR_EDIT_TOOL::Properties, EE_ACTIONS::symbolProperties.MakeEvent() );
  673. Go( &SYMBOL_EDITOR_EDIT_TOOL::PinTable, EE_ACTIONS::pinTable.MakeEvent() );
  674. Go( &SYMBOL_EDITOR_EDIT_TOOL::UpdateSymbolFields, EE_ACTIONS::updateSymbolFields.MakeEvent() );
  675. Go( &SYMBOL_EDITOR_EDIT_TOOL::SetUnitDisplayName, EE_ACTIONS::setUnitDisplayName.MakeEvent() );
  676. }