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.

2281 lines
75 KiB

5 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
7 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) 1992-2023 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 <symbol_library.h>
  25. #include <confirm.h>
  26. #include <connection_graph.h>
  27. #include <dialogs/dialog_symbol_fields_table.h>
  28. #include <dialogs/dialog_eeschema_page_settings.h>
  29. #include <dialogs/dialog_paste_special.h>
  30. #include <dialogs/dialog_plot_schematic.h>
  31. #include <dialogs/dialog_symbol_remap.h>
  32. #include <dialogs/dialog_assign_netclass.h>
  33. #include <project_rescue.h>
  34. #include <erc.h>
  35. #include <fmt.h>
  36. #include <invoke_sch_dialog.h>
  37. #include <string_utils.h>
  38. #include <kiway.h>
  39. #include <kiway_player.h>
  40. #include <netlist_exporters/netlist_exporter_spice.h>
  41. #include <paths.h>
  42. #include <project/project_file.h>
  43. #include <project/net_settings.h>
  44. #include <sch_edit_frame.h>
  45. #include <sch_plugins/kicad/sch_sexpr_plugin.h>
  46. #include <sch_line.h>
  47. #include <sch_junction.h>
  48. #include <sch_bus_entry.h>
  49. #include <sch_shape.h>
  50. #include <sch_painter.h>
  51. #include <sch_sheet.h>
  52. #include <sch_sheet_pin.h>
  53. #include <sch_view.h>
  54. #include <schematic.h>
  55. #include <advanced_config.h>
  56. #include <sim/sim_plot_frame.h>
  57. #include <sim/spice_generator.h>
  58. #include <sim/sim_lib_mgr.h>
  59. #include "symbol_library_manager.h"
  60. #include <symbol_viewer_frame.h>
  61. #include <status_popup.h>
  62. #include <tool/picker_tool.h>
  63. #include <tool/tool_manager.h>
  64. #include <tools/ee_actions.h>
  65. #include <tools/ee_selection.h>
  66. #include <tools/ee_selection_tool.h>
  67. #include <tools/sch_editor_control.h>
  68. #include <drawing_sheet/ds_proxy_undo_item.h>
  69. #include <dialog_update_from_pcb.h>
  70. #include <eda_list_dialog.h>
  71. #include <wildcards_and_files_ext.h>
  72. #include <wx_filename.h>
  73. #include <sch_sheet_path.h>
  74. #include <wx/filedlg.h>
  75. int SCH_EDITOR_CONTROL::New( const TOOL_EVENT& aEvent )
  76. {
  77. m_frame->NewProject();
  78. return 0;
  79. }
  80. int SCH_EDITOR_CONTROL::Open( const TOOL_EVENT& aEvent )
  81. {
  82. m_frame->LoadProject();
  83. return 0;
  84. }
  85. int SCH_EDITOR_CONTROL::Save( const TOOL_EVENT& aEvent )
  86. {
  87. m_frame->SaveProject();
  88. return 0;
  89. }
  90. int SCH_EDITOR_CONTROL::SaveAs( const TOOL_EVENT& aEvent )
  91. {
  92. m_frame->SaveProject( true );
  93. return 0;
  94. }
  95. int SCH_EDITOR_CONTROL::SaveCurrSheetCopyAs( const TOOL_EVENT& aEvent )
  96. {
  97. SCH_SHEET* curr_sheet = m_frame->GetCurrentSheet().Last();
  98. wxFileName curr_fn = curr_sheet->GetFileName();
  99. wxFileDialog dlg( m_frame, _( "Schematic Files" ), curr_fn.GetPath(),
  100. curr_fn.GetFullName(), KiCadSchematicFileWildcard(),
  101. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  102. if( dlg.ShowModal() == wxID_CANCEL )
  103. return false;
  104. wxFileName newFileName = dlg.GetPath();
  105. newFileName.SetExt( KiCadSchematicFileExtension );
  106. m_frame->saveSchematicFile( curr_sheet, newFileName.GetFullPath() );
  107. return 0;
  108. }
  109. int SCH_EDITOR_CONTROL::Revert( const TOOL_EVENT& aEvent )
  110. {
  111. SCHEMATIC& schematic = m_frame->Schematic();
  112. SCH_SHEET& root = schematic.Root();
  113. if( m_frame->GetCurrentSheet().Last() != &root )
  114. {
  115. m_toolMgr->RunAction( ACTIONS::cancelInteractive, true );
  116. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  117. // Store the current zoom level into the current screen before switching
  118. m_frame->GetScreen()->m_LastZoomLevel = m_frame->GetCanvas()->GetView()->GetScale();
  119. SCH_SHEET_PATH rootSheetPath;
  120. rootSheetPath.push_back( &root );
  121. m_frame->SetCurrentSheet( rootSheetPath );
  122. m_frame->DisplayCurrentSheet();
  123. wxSafeYield();
  124. }
  125. wxString msg;
  126. msg.Printf( _( "Revert '%s' (and all sub-sheets) to last version saved?" ),
  127. schematic.GetFileName() );
  128. if( !IsOK( m_frame, msg ) )
  129. return false;
  130. SCH_SCREENS screenList( schematic.Root() );
  131. for( SCH_SCREEN* screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
  132. screen->SetContentModified( false ); // do not prompt the user for changes
  133. m_frame->ReleaseFile();
  134. m_frame->OpenProjectFiles( std::vector<wxString>( 1, schematic.GetFileName() ) );
  135. return 0;
  136. }
  137. int SCH_EDITOR_CONTROL::ShowSchematicSetup( const TOOL_EVENT& aEvent )
  138. {
  139. m_frame->ShowSchematicSetupDialog();
  140. return 0;
  141. }
  142. int SCH_EDITOR_CONTROL::PageSetup( const TOOL_EVENT& aEvent )
  143. {
  144. PICKED_ITEMS_LIST undoCmd;
  145. DS_PROXY_UNDO_ITEM* undoItem = new DS_PROXY_UNDO_ITEM( m_frame );
  146. ITEM_PICKER wrapper( m_frame->GetScreen(), undoItem, UNDO_REDO::PAGESETTINGS );
  147. undoCmd.PushItem( wrapper );
  148. m_frame->SaveCopyInUndoList( undoCmd, UNDO_REDO::PAGESETTINGS, false, false );
  149. DIALOG_EESCHEMA_PAGE_SETTINGS dlg( m_frame, wxSize( MAX_PAGE_SIZE_EESCHEMA_MILS,
  150. MAX_PAGE_SIZE_EESCHEMA_MILS ) );
  151. dlg.SetWksFileName( BASE_SCREEN::m_DrawingSheetFileName );
  152. if( dlg.ShowModal() == wxID_OK )
  153. {
  154. // Update text variables
  155. m_frame->GetCanvas()->GetView()->MarkDirty();
  156. m_frame->GetCanvas()->GetView()->UpdateAllItems( KIGFX::REPAINT );
  157. m_frame->GetCanvas()->Refresh();
  158. m_frame->OnModify();
  159. }
  160. else
  161. {
  162. m_frame->RollbackSchematicFromUndo();
  163. }
  164. return 0;
  165. }
  166. int SCH_EDITOR_CONTROL::RescueSymbols( const TOOL_EVENT& aEvent )
  167. {
  168. SCH_SCREENS schematic( m_frame->Schematic().Root() );
  169. if( schematic.HasNoFullyDefinedLibIds() )
  170. RescueLegacyProject( true );
  171. else
  172. RescueSymbolLibTableProject( true );
  173. return 0;
  174. }
  175. bool SCH_EDITOR_CONTROL::RescueLegacyProject( bool aRunningOnDemand )
  176. {
  177. LEGACY_RESCUER rescuer( m_frame->Prj(), &m_frame->Schematic(), &m_frame->GetCurrentSheet(),
  178. m_frame->GetCanvas()->GetBackend() );
  179. return rescueProject( rescuer, aRunningOnDemand );
  180. }
  181. bool SCH_EDITOR_CONTROL::RescueSymbolLibTableProject( bool aRunningOnDemand )
  182. {
  183. SYMBOL_LIB_TABLE_RESCUER rescuer( m_frame->Prj(), &m_frame->Schematic(),
  184. &m_frame->GetCurrentSheet(),
  185. m_frame->GetCanvas()->GetBackend() );
  186. return rescueProject( rescuer, aRunningOnDemand );
  187. }
  188. bool SCH_EDITOR_CONTROL::rescueProject( RESCUER& aRescuer, bool aRunningOnDemand )
  189. {
  190. if( !RESCUER::RescueProject( m_frame, aRescuer, aRunningOnDemand ) )
  191. return false;
  192. if( aRescuer.GetCandidateCount() )
  193. {
  194. KIWAY_PLAYER* viewer = m_frame->Kiway().Player( FRAME_SCH_VIEWER, false );
  195. if( viewer )
  196. static_cast<SYMBOL_VIEWER_FRAME*>( viewer )->ReCreateLibList();
  197. if( aRunningOnDemand )
  198. {
  199. SCH_SCREENS schematic( m_frame->Schematic().Root() );
  200. schematic.UpdateSymbolLinks();
  201. m_frame->RecalculateConnections( GLOBAL_CLEANUP );
  202. }
  203. m_frame->ClearUndoRedoList();
  204. m_frame->SyncView();
  205. m_frame->GetCanvas()->Refresh();
  206. m_frame->OnModify();
  207. }
  208. return true;
  209. }
  210. int SCH_EDITOR_CONTROL::RemapSymbols( const TOOL_EVENT& aEvent )
  211. {
  212. DIALOG_SYMBOL_REMAP dlgRemap( m_frame );
  213. dlgRemap.ShowQuasiModal();
  214. m_frame->GetCanvas()->Refresh( true );
  215. return 0;
  216. }
  217. int SCH_EDITOR_CONTROL::Print( const TOOL_EVENT& aEvent )
  218. {
  219. InvokeDialogPrintUsingPrinter( m_frame );
  220. wxFileName fn = m_frame->Prj().AbsolutePath( m_frame->Schematic().RootScreen()->GetFileName() );
  221. if( fn.GetName() != NAMELESS_PROJECT )
  222. m_frame->SaveProjectSettings();
  223. return 0;
  224. }
  225. int SCH_EDITOR_CONTROL::Plot( const TOOL_EVENT& aEvent )
  226. {
  227. DIALOG_PLOT_SCHEMATIC dlg( m_frame );
  228. dlg.ShowModal();
  229. // save project config if the prj config has changed:
  230. if( dlg.PrjConfigChanged() )
  231. m_frame->SaveProjectSettings();
  232. return 0;
  233. }
  234. int SCH_EDITOR_CONTROL::Quit( const TOOL_EVENT& aEvent )
  235. {
  236. m_frame->Close( false );
  237. return 0;
  238. }
  239. int SCH_EDITOR_CONTROL::CrossProbeToPcb( const TOOL_EVENT& aEvent )
  240. {
  241. doCrossProbeSchToPcb( aEvent, false );
  242. return 0;
  243. }
  244. int SCH_EDITOR_CONTROL::ExplicitCrossProbeToPcb( const TOOL_EVENT& aEvent )
  245. {
  246. doCrossProbeSchToPcb( aEvent, true );
  247. return 0;
  248. }
  249. void SCH_EDITOR_CONTROL::doCrossProbeSchToPcb( const TOOL_EVENT& aEvent, bool aForce )
  250. {
  251. // Don't get in an infinite loop SCH -> PCB -> SCH -> PCB -> SCH -> ...
  252. if( m_probingPcbToSch || m_frame->IsSyncingSelection() )
  253. return;
  254. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  255. EE_SELECTION& selection = aForce ? selTool->RequestSelection() : selTool->GetSelection();
  256. m_frame->SendSelectItemsToPcb( selection.GetItemsSortedBySelectionOrder(), aForce );
  257. }
  258. int SCH_EDITOR_CONTROL::ExportSymbolsToLibrary( const TOOL_EVENT& aEvent )
  259. {
  260. bool createNew = aEvent.IsAction( &EE_ACTIONS::exportSymbolsToNewLibrary );
  261. SCH_REFERENCE_LIST symbols;
  262. m_frame->Schematic().GetSheets().GetSymbols( symbols, false );
  263. std::map<LIB_ID, LIB_SYMBOL*> libSymbols;
  264. std::map<LIB_ID, std::vector<SCH_SYMBOL*>> symbolMap;
  265. for( size_t i = 0; i < symbols.GetCount(); ++i )
  266. {
  267. SCH_SYMBOL* symbol = symbols[i].GetSymbol();
  268. LIB_SYMBOL* libSymbol = symbol->GetLibSymbolRef().get();
  269. LIB_ID id = libSymbol->GetLibId();
  270. if( libSymbols.count( id ) )
  271. {
  272. wxASSERT_MSG( libSymbols[id]->Compare( *libSymbol, LIB_ITEM::COMPARE_FLAGS::ERC ) == 0,
  273. "Two symbols have the same LIB_ID but are different!" );
  274. }
  275. else
  276. {
  277. libSymbols[id] = libSymbol;
  278. }
  279. symbolMap[id].emplace_back( symbol );
  280. }
  281. SYMBOL_LIBRARY_MANAGER mgr( *m_frame );
  282. wxString targetLib;
  283. if( createNew )
  284. {
  285. wxFileName fn;
  286. SYMBOL_LIB_TABLE* libTable = m_frame->SelectSymLibTable();
  287. if( !m_frame->LibraryFileBrowser( false, fn, KiCadSymbolLibFileWildcard(),
  288. KiCadSymbolLibFileExtension, false,
  289. ( libTable == &SYMBOL_LIB_TABLE::GetGlobalLibTable() ),
  290. PATHS::GetDefaultUserSymbolsPath() ) )
  291. {
  292. return 0;
  293. }
  294. targetLib = fn.GetName();
  295. if( libTable->HasLibrary( targetLib, false ) )
  296. {
  297. DisplayError( m_frame, wxString::Format( _( "Library '%s' already exists." ),
  298. targetLib ) );
  299. return 0;
  300. }
  301. if( !mgr.CreateLibrary( fn.GetFullPath(), libTable ) )
  302. {
  303. DisplayError( m_frame, wxString::Format( _( "Could not add library '%s'." ),
  304. targetLib ) );
  305. return 0;
  306. }
  307. }
  308. else
  309. {
  310. targetLib = m_frame->SelectLibraryFromList();
  311. }
  312. if( targetLib.IsEmpty() )
  313. return 0;
  314. bool map = IsOK( m_frame, _( "Update symbols in schematic to refer to new library?" ) );
  315. bool append = false;
  316. SYMBOL_LIB_TABLE_ROW* row = mgr.GetLibrary( targetLib );
  317. SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::EnumFromStr( row->GetType() );
  318. SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( type ) );
  319. wxFileName dest = row->GetFullURI( true );
  320. dest.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
  321. for( const std::pair<const LIB_ID, LIB_SYMBOL*>& it : libSymbols )
  322. {
  323. LIB_SYMBOL* origSym = it.second;
  324. LIB_SYMBOL* newSym = origSym->Flatten().release();
  325. pi->SaveSymbol( dest.GetFullPath(), newSym );
  326. if( map )
  327. {
  328. LIB_ID id = it.first;
  329. id.SetLibNickname( targetLib );
  330. for( SCH_SYMBOL* symbol : symbolMap[it.first] )
  331. {
  332. m_frame->SaveCopyInUndoList( m_frame->GetScreen(), symbol, UNDO_REDO::CHANGED, append, false);
  333. symbol->SetLibId( id );
  334. append = true;
  335. }
  336. }
  337. }
  338. if( append )
  339. m_frame->OnModify();
  340. return 0;
  341. }
  342. #ifdef KICAD_SPICE
  343. #define HITTEST_THRESHOLD_PIXELS 5
  344. int SCH_EDITOR_CONTROL::SimProbe( const TOOL_EVENT& aEvent )
  345. {
  346. PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
  347. SIM_PLOT_FRAME* simFrame = (SIM_PLOT_FRAME*) m_frame->Kiway().Player( FRAME_SIMULATOR, false );
  348. if( !simFrame ) // Defensive coding; shouldn't happen.
  349. return 0;
  350. if( wxWindow* blocking_win = simFrame->Kiway().GetBlockingDialog() )
  351. blocking_win->Close( true );
  352. // Deactivate other tools; particularly important if another PICKER is currently running
  353. Activate();
  354. picker->SetCursor( KICURSOR::VOLTAGE_PROBE );
  355. picker->SetSnapping( false );
  356. picker->SetClickHandler(
  357. [this, simFrame]( const VECTOR2D& aPosition )
  358. {
  359. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  360. EDA_ITEM* item = selTool->GetNode( aPosition );
  361. SCH_SHEET_PATH& sheet = m_frame->GetCurrentSheet();
  362. if( !item )
  363. return false;
  364. if( item->Type() == SCH_PIN_T )
  365. {
  366. try
  367. {
  368. LIB_PIN* pin = static_cast<SCH_PIN*>( item )->GetLibPin();
  369. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item->GetParent() );
  370. wxString msg;
  371. WX_STRING_REPORTER reporter( &msg );
  372. SIM_LIB_MGR mgr( &m_frame->Prj(), &reporter );
  373. SIM_MODEL& model = mgr.CreateModel( &sheet, *symbol ).model;
  374. if( reporter.HasMessage() )
  375. THROW_IO_ERROR( msg );
  376. SPICE_ITEM spiceItem;
  377. spiceItem.refName = std::string( symbol->GetRef( &sheet ).ToUTF8() );
  378. std::vector<std::string> currentNames =
  379. model.SpiceGenerator().CurrentNames( spiceItem );
  380. if( currentNames.size() == 0 )
  381. return true;
  382. else if( currentNames.size() == 1 )
  383. {
  384. simFrame->AddCurrentPlot( currentNames.at( 0 ) );
  385. return true;
  386. }
  387. int modelPinIndex =
  388. model.FindModelPinIndex( std::string( pin->GetNumber().ToUTF8() ) );
  389. if( modelPinIndex != SIM_MODEL::PIN::NOT_CONNECTED )
  390. {
  391. wxString name = currentNames.at( modelPinIndex );
  392. simFrame->AddCurrentPlot( name );
  393. }
  394. }
  395. catch( const IO_ERROR& e )
  396. {
  397. DisplayErrorMessage( m_frame, e.What() );
  398. }
  399. }
  400. else if( item->IsType( { SCH_ITEM_LOCATE_WIRE_T } ) )
  401. {
  402. if( SCH_CONNECTION* conn = static_cast<SCH_ITEM*>( item )->Connection() )
  403. {
  404. std::string spiceNet = std::string( UnescapeString( conn->Name() ).ToUTF8() );
  405. NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( spiceNet );
  406. simFrame->AddVoltagePlot( wxString::Format( "V(%s)", spiceNet ) );
  407. }
  408. }
  409. return true;
  410. } );
  411. picker->SetMotionHandler(
  412. [this, picker]( const VECTOR2D& aPos )
  413. {
  414. EE_COLLECTOR collector;
  415. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  416. collector.Collect( m_frame->GetScreen(), { SCH_ITEM_LOCATE_WIRE_T,
  417. SCH_PIN_T,
  418. SCH_SHEET_PIN_T }, aPos );
  419. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  420. selectionTool->GuessSelectionCandidates( collector, aPos );
  421. EDA_ITEM* item = collector.GetCount() == 1 ? collector[ 0 ] : nullptr;
  422. SCH_LINE* wire = dynamic_cast<SCH_LINE*>( item );
  423. const SCH_CONNECTION* conn = nullptr;
  424. if( wire )
  425. {
  426. item = nullptr;
  427. conn = wire->Connection();
  428. }
  429. if( item && item->Type() == SCH_PIN_T )
  430. picker->SetCursor( KICURSOR::CURRENT_PROBE );
  431. else
  432. picker->SetCursor( KICURSOR::VOLTAGE_PROBE );
  433. if( m_pickerItem != item )
  434. {
  435. if( m_pickerItem )
  436. selectionTool->UnbrightenItem( m_pickerItem );
  437. m_pickerItem = item;
  438. if( m_pickerItem )
  439. selectionTool->BrightenItem( m_pickerItem );
  440. }
  441. if( m_frame->GetHighlightedConnection() != conn )
  442. {
  443. m_frame->SetHighlightedConnection( conn );
  444. TOOL_EVENT dummyEvent;
  445. UpdateNetHighlighting( dummyEvent );
  446. }
  447. } );
  448. picker->SetFinalizeHandler(
  449. [this]( const int& aFinalState )
  450. {
  451. if( m_pickerItem )
  452. m_toolMgr->GetTool<EE_SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
  453. if( m_frame->GetHighlightedConnection() )
  454. {
  455. m_frame->SetHighlightedConnection( nullptr );
  456. TOOL_EVENT dummyEvent;
  457. UpdateNetHighlighting( dummyEvent );
  458. }
  459. // Wake the selection tool after exiting to ensure the cursor gets updated
  460. m_toolMgr->RunAction( EE_ACTIONS::selectionActivate, false );
  461. } );
  462. m_toolMgr->RunAction( ACTIONS::pickerTool, true );
  463. return 0;
  464. }
  465. int SCH_EDITOR_CONTROL::SimTune( const TOOL_EVENT& aEvent )
  466. {
  467. PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
  468. // Deactivate other tools; particularly important if another PICKER is currently running
  469. Activate();
  470. picker->SetCursor( KICURSOR::TUNE );
  471. picker->SetSnapping( false );
  472. picker->SetClickHandler(
  473. [this]( const VECTOR2D& aPosition )
  474. {
  475. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  476. EDA_ITEM* item = nullptr;
  477. selTool->SelectPoint( aPosition, { SCH_SYMBOL_T, SCH_FIELD_T }, &item );
  478. if( !item )
  479. return false;
  480. if( item->Type() != SCH_SYMBOL_T )
  481. {
  482. item = item->GetParent();
  483. if( item->Type() != SCH_SYMBOL_T )
  484. return false;
  485. }
  486. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  487. SCH_SHEET_PATH sheetPath = symbol->Schematic()->CurrentSheet();
  488. KIWAY_PLAYER* simFrame = m_frame->Kiway().Player( FRAME_SIMULATOR, false );
  489. if( simFrame )
  490. {
  491. if( wxWindow* blocking_win = simFrame->Kiway().GetBlockingDialog() )
  492. blocking_win->Close( true );
  493. static_cast<SIM_PLOT_FRAME*>( simFrame )->AddTuner( sheetPath, symbol );
  494. }
  495. // We do not really want to keep a symbol selected in schematic,
  496. // so clear the current selection
  497. selTool->ClearSelection();
  498. return true;
  499. } );
  500. picker->SetMotionHandler(
  501. [this]( const VECTOR2D& aPos )
  502. {
  503. EE_COLLECTOR collector;
  504. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  505. collector.Collect( m_frame->GetScreen(), { SCH_SYMBOL_T, SCH_FIELD_T }, aPos );
  506. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  507. selectionTool->GuessSelectionCandidates( collector, aPos );
  508. EDA_ITEM* item = collector.GetCount() == 1 ? collector[ 0 ] : nullptr;
  509. if( m_pickerItem != item )
  510. {
  511. if( m_pickerItem )
  512. selectionTool->UnbrightenItem( m_pickerItem );
  513. m_pickerItem = item;
  514. if( m_pickerItem )
  515. selectionTool->BrightenItem( m_pickerItem );
  516. }
  517. } );
  518. picker->SetFinalizeHandler(
  519. [this]( const int& aFinalState )
  520. {
  521. if( m_pickerItem )
  522. m_toolMgr->GetTool<EE_SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
  523. // Wake the selection tool after exiting to ensure the cursor gets updated
  524. // and deselect previous selection from simulator to avoid any issue
  525. // ( avoid crash in some cases when the SimTune tool is deselected )
  526. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  527. selectionTool->ClearSelection();
  528. m_toolMgr->RunAction( EE_ACTIONS::selectionActivate, false );
  529. } );
  530. m_toolMgr->RunAction( ACTIONS::pickerTool, true );
  531. return 0;
  532. }
  533. #endif /* KICAD_SPICE */
  534. // A singleton reference for clearing the highlight
  535. static VECTOR2D CLEAR;
  536. static bool highlightNet( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition )
  537. {
  538. SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( aToolMgr->GetToolHolder() );
  539. EE_SELECTION_TOOL* selTool = aToolMgr->GetTool<EE_SELECTION_TOOL>();
  540. SCH_EDITOR_CONTROL* editorControl = aToolMgr->GetTool<SCH_EDITOR_CONTROL>();
  541. SCH_CONNECTION* conn = nullptr;
  542. bool retVal = true;
  543. if( aPosition != CLEAR )
  544. {
  545. ERC_TESTER erc( &editFrame->Schematic() );
  546. if( erc.TestDuplicateSheetNames( false ) > 0 )
  547. {
  548. wxMessageBox( _( "Error: duplicate sub-sheet names found in current sheet." ) );
  549. retVal = false;
  550. }
  551. else
  552. {
  553. SCH_ITEM* item = static_cast<SCH_ITEM*>( selTool->GetNode( aPosition ) );
  554. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
  555. if( item )
  556. {
  557. if( item->IsConnectivityDirty() )
  558. {
  559. editFrame->RecalculateConnections( NO_CLEANUP );
  560. }
  561. if( item->Type() == SCH_FIELD_T )
  562. symbol = dynamic_cast<SCH_SYMBOL*>( item->GetParent() );
  563. if( symbol && symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->IsPower() )
  564. {
  565. std::vector<SCH_PIN*> pins = symbol->GetPins();
  566. if( pins.size() == 1 )
  567. conn = pins[0]->Connection();
  568. }
  569. else
  570. {
  571. conn = item->Connection();
  572. }
  573. }
  574. }
  575. }
  576. if( !conn || conn == editFrame->GetHighlightedConnection() )
  577. {
  578. editFrame->SetStatusText( wxT( "" ) );
  579. editFrame->SendCrossProbeClearHighlight();
  580. editFrame->SetHighlightedConnection( nullptr );
  581. }
  582. else
  583. {
  584. editFrame->SetCrossProbeConnection( conn );
  585. editFrame->SetHighlightedConnection( conn );
  586. }
  587. editFrame->UpdateNetHighlightStatus();
  588. TOOL_EVENT dummy;
  589. editorControl->UpdateNetHighlighting( dummy );
  590. return retVal;
  591. }
  592. int SCH_EDITOR_CONTROL::HighlightNet( const TOOL_EVENT& aEvent )
  593. {
  594. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  595. VECTOR2D cursorPos = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
  596. highlightNet( m_toolMgr, cursorPos );
  597. return 0;
  598. }
  599. int SCH_EDITOR_CONTROL::ClearHighlight( const TOOL_EVENT& aEvent )
  600. {
  601. highlightNet( m_toolMgr, CLEAR );
  602. return 0;
  603. }
  604. int SCH_EDITOR_CONTROL::AssignNetclass( const TOOL_EVENT& aEvent )
  605. {
  606. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  607. SCHEMATIC& schematic = m_frame->Schematic();
  608. SCH_SCREEN* screen = m_frame->GetCurrentSheet().LastScreen();
  609. const SCH_CONNECTION* conn = nullptr;
  610. VECTOR2D connPos;
  611. for( EDA_ITEM* item : selectionTool->GetSelection() )
  612. {
  613. conn = static_cast<SCH_ITEM*>( item )->Connection();
  614. connPos = item->GetPosition();
  615. if( conn )
  616. break;
  617. }
  618. if( !conn )
  619. {
  620. m_frame->ShowInfoBarError( _( "No net selected." ) );
  621. return 0;
  622. }
  623. // Remove selection in favor of highlighting so the whole net is highlighted
  624. selectionTool->ClearSelection();
  625. highlightNet( m_toolMgr, connPos );
  626. wxString netName = conn->Name();
  627. if( conn->IsBus() )
  628. {
  629. wxString prefix;
  630. if( NET_SETTINGS::ParseBusVector( netName, &prefix, nullptr ) )
  631. {
  632. netName = prefix + wxT( "*" );
  633. }
  634. else if( NET_SETTINGS::ParseBusGroup( netName, &prefix, nullptr ) )
  635. {
  636. netName = prefix + wxT( ".*" );
  637. }
  638. }
  639. else if( !conn->Driver() || CONNECTION_SUBGRAPH::GetDriverPriority( conn->Driver() )
  640. < CONNECTION_SUBGRAPH::PRIORITY::SHEET_PIN )
  641. {
  642. m_frame->ShowInfoBarError( _( "Net must be labeled to assign a netclass." ) );
  643. highlightNet( m_toolMgr, CLEAR );
  644. return 0;
  645. }
  646. DIALOG_ASSIGN_NETCLASS dlg( m_frame, netName, schematic.GetNetClassAssignmentCandidates(),
  647. [&]( const std::vector<wxString>& aNetNames )
  648. {
  649. for( SCH_ITEM* item : screen->Items() )
  650. {
  651. bool redraw = item->IsBrightened();
  652. SCH_CONNECTION* itemConn = item->Connection();
  653. if( itemConn && alg::contains( aNetNames, itemConn->Name() ) )
  654. item->SetBrightened();
  655. else
  656. item->ClearBrightened();
  657. redraw |= item->IsBrightened();
  658. if( item->Type() == SCH_SYMBOL_T )
  659. {
  660. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  661. redraw |= symbol->HasBrightenedPins();
  662. symbol->ClearBrightenedPins();
  663. for( SCH_PIN* pin : symbol->GetPins() )
  664. {
  665. SCH_CONNECTION* pin_conn = pin->Connection();
  666. if( pin_conn && alg::contains( aNetNames, pin_conn->Name() ) )
  667. {
  668. pin->SetBrightened();
  669. redraw = true;
  670. }
  671. }
  672. }
  673. else if( item->Type() == SCH_SHEET_T )
  674. {
  675. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
  676. {
  677. SCH_CONNECTION* pin_conn = pin->Connection();
  678. redraw |= pin->IsBrightened();
  679. if( pin_conn && alg::contains( aNetNames, pin_conn->Name() ) )
  680. pin->SetBrightened();
  681. else
  682. pin->ClearBrightened();
  683. redraw |= pin->IsBrightened();
  684. }
  685. }
  686. if( redraw )
  687. getView()->Update( item, KIGFX::VIEW_UPDATE_FLAGS::REPAINT );
  688. }
  689. m_frame->GetCanvas()->ForceRefresh();
  690. } );
  691. if( dlg.ShowModal() )
  692. {
  693. getView()->UpdateAllItemsConditionally(
  694. []( KIGFX::VIEW_ITEM* aItem ) -> int
  695. {
  696. // Netclass coloured items
  697. //
  698. if( dynamic_cast<SCH_LINE*>( aItem ) )
  699. return KIGFX::REPAINT;
  700. else if( dynamic_cast<SCH_JUNCTION*>( aItem ) )
  701. return KIGFX::REPAINT;
  702. else if( dynamic_cast<SCH_BUS_ENTRY_BASE*>( aItem ) )
  703. return KIGFX::REPAINT;
  704. // Items that might reference an item's netclass name
  705. //
  706. EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aItem );
  707. if( text && text->HasTextVars() )
  708. {
  709. text->ClearRenderCache();
  710. text->ClearBoundingBoxCache();
  711. return KIGFX::GEOMETRY | KIGFX::REPAINT;
  712. }
  713. return 0;
  714. } );
  715. }
  716. highlightNet( m_toolMgr, CLEAR );
  717. return 0;
  718. }
  719. int SCH_EDITOR_CONTROL::UpdateNetHighlighting( const TOOL_EVENT& aEvent )
  720. {
  721. SCH_SCREEN* screen = m_frame->GetCurrentSheet().LastScreen();
  722. CONNECTION_GRAPH* connectionGraph = m_frame->Schematic().ConnectionGraph();
  723. std::vector<EDA_ITEM*> itemsToRedraw;
  724. const SCH_CONNECTION* selectedConn = m_frame->GetHighlightedConnection();
  725. if( !screen )
  726. return 0;
  727. bool selectedIsBus = selectedConn ? selectedConn->IsBus() : false;
  728. wxString selectedName = selectedConn ? selectedConn->Name() : wxString( wxS( "" ) );
  729. bool selectedIsNoNet = false;
  730. CONNECTION_SUBGRAPH* selectedSubgraph = nullptr;
  731. if( selectedConn && selectedConn->Driver() == nullptr )
  732. {
  733. selectedIsNoNet = true;
  734. selectedSubgraph = connectionGraph->GetSubgraphForItem( selectedConn->Parent() );
  735. }
  736. for( SCH_ITEM* item : screen->Items() )
  737. {
  738. bool redraw = item->IsBrightened();
  739. bool highlight = false;
  740. if( selectedConn )
  741. {
  742. SCH_CONNECTION* itemConn = nullptr;
  743. SCH_SYMBOL* symbol = nullptr;
  744. if( item->Type() == SCH_SYMBOL_T )
  745. symbol = static_cast<SCH_SYMBOL*>( item );
  746. if( symbol && symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->IsPower() )
  747. itemConn = symbol->Connection();
  748. else
  749. itemConn = item->Connection();
  750. if( selectedIsNoNet && selectedSubgraph )
  751. {
  752. for( SCH_ITEM* subgraphItem : selectedSubgraph->m_items )
  753. {
  754. if( item == subgraphItem )
  755. {
  756. highlight = true;
  757. break;
  758. }
  759. }
  760. }
  761. else if( selectedIsBus && itemConn && itemConn->IsNet() )
  762. {
  763. for( const std::shared_ptr<SCH_CONNECTION>& member : selectedConn->Members() )
  764. {
  765. if( member->Name() == itemConn->Name() )
  766. {
  767. highlight = true;
  768. break;
  769. }
  770. else if( member->IsBus() )
  771. {
  772. for( const std::shared_ptr<SCH_CONNECTION>& bus_member : member->Members() )
  773. {
  774. if( bus_member->Name() == itemConn->Name() )
  775. {
  776. highlight = true;
  777. break;
  778. }
  779. }
  780. }
  781. }
  782. }
  783. else if( selectedConn && itemConn && selectedName == itemConn->Name() )
  784. {
  785. highlight = true;
  786. }
  787. }
  788. if( highlight )
  789. item->SetBrightened();
  790. else
  791. item->ClearBrightened();
  792. redraw |= item->IsBrightened();
  793. if( item->Type() == SCH_SYMBOL_T )
  794. {
  795. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  796. redraw |= symbol->HasBrightenedPins();
  797. symbol->ClearBrightenedPins();
  798. for( SCH_PIN* pin : symbol->GetPins() )
  799. {
  800. SCH_CONNECTION* pin_conn = pin->Connection();
  801. if( pin_conn && pin_conn->Name() == selectedName )
  802. {
  803. pin->SetBrightened();
  804. redraw = true;
  805. }
  806. }
  807. if( symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->IsPower() )
  808. {
  809. std::vector<SCH_FIELD>& fields = symbol->GetFields();
  810. for( int id : { REFERENCE_FIELD, VALUE_FIELD } )
  811. {
  812. if( item->IsBrightened() && fields[id].IsVisible() )
  813. fields[id].SetBrightened();
  814. else
  815. fields[id].ClearBrightened();
  816. }
  817. }
  818. }
  819. else if( item->Type() == SCH_SHEET_T )
  820. {
  821. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
  822. {
  823. SCH_CONNECTION* pin_conn = pin->Connection();
  824. bool redrawPin = pin->IsBrightened();
  825. if( pin_conn && pin_conn->Name() == selectedName )
  826. pin->SetBrightened();
  827. else
  828. pin->ClearBrightened();
  829. redrawPin ^= pin->IsBrightened();
  830. redraw |= redrawPin;
  831. }
  832. }
  833. if( redraw )
  834. itemsToRedraw.push_back( item );
  835. }
  836. // Be sure highlight change will be redrawn
  837. KIGFX::VIEW* view = getView();
  838. for( EDA_ITEM* redrawItem : itemsToRedraw )
  839. view->Update( (KIGFX::VIEW_ITEM*)redrawItem, KIGFX::VIEW_UPDATE_FLAGS::REPAINT );
  840. m_frame->GetCanvas()->Refresh();
  841. return 0;
  842. }
  843. int SCH_EDITOR_CONTROL::HighlightNetCursor( const TOOL_EVENT& aEvent )
  844. {
  845. PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
  846. // Deactivate other tools; particularly important if another PICKER is currently running
  847. Activate();
  848. picker->SetCursor( KICURSOR::BULLSEYE );
  849. picker->SetSnapping( false );
  850. picker->SetClickHandler(
  851. [this] ( const VECTOR2D& aPos )
  852. {
  853. return highlightNet( m_toolMgr, aPos );
  854. } );
  855. m_toolMgr->RunAction( ACTIONS::pickerTool, true );
  856. return 0;
  857. }
  858. int SCH_EDITOR_CONTROL::Undo( const TOOL_EVENT& aEvent )
  859. {
  860. if( m_frame->GetUndoCommandCount() <= 0 )
  861. return 0;
  862. // Inform tools that undo command was issued
  863. m_toolMgr->ProcessEvent( { TC_MESSAGE, TA_UNDO_REDO_PRE, AS_GLOBAL } );
  864. // Get the old list
  865. PICKED_ITEMS_LIST* List = m_frame->PopCommandFromUndoList();
  866. size_t num_undos = m_frame->m_undoList.m_CommandsList.size();
  867. // The cleanup routines normally run after an operation and so attempt to append their
  868. // undo items onto the operation's list. However, in this case that's going be the list
  869. // under us, which we don't want, so we push a dummy list onto the stack.
  870. PICKED_ITEMS_LIST* dummy = new PICKED_ITEMS_LIST();
  871. m_frame->PushCommandToUndoList( dummy );
  872. m_frame->PutDataInPreviousState( List );
  873. m_frame->SetSheetNumberAndCount();
  874. m_frame->TestDanglingEnds();
  875. m_frame->OnPageSettingsChange();
  876. // The cleanup routines *should* have appended to our dummy list, but just to be doubly
  877. // sure pop any other new lists off the stack as well
  878. while( m_frame->m_undoList.m_CommandsList.size() > num_undos )
  879. delete m_frame->PopCommandFromUndoList();
  880. // Now push the old command to the RedoList
  881. List->ReversePickersListOrder();
  882. m_frame->PushCommandToRedoList( List );
  883. m_toolMgr->GetTool<EE_SELECTION_TOOL>()->RebuildSelection();
  884. m_frame->SyncView();
  885. m_frame->GetCanvas()->Refresh();
  886. m_frame->OnModify();
  887. return 0;
  888. }
  889. int SCH_EDITOR_CONTROL::Redo( const TOOL_EVENT& aEvent )
  890. {
  891. if( m_frame->GetRedoCommandCount() == 0 )
  892. return 0;
  893. // Inform tools that undo command was issued
  894. m_toolMgr->ProcessEvent( { TC_MESSAGE, TA_UNDO_REDO_PRE, AS_GLOBAL } );
  895. /* Get the old list */
  896. PICKED_ITEMS_LIST* list = m_frame->PopCommandFromRedoList();
  897. /* Redo the command: */
  898. m_frame->PutDataInPreviousState( list );
  899. /* Put the old list in UndoList */
  900. list->ReversePickersListOrder();
  901. m_frame->PushCommandToUndoList( list );
  902. m_frame->SetSheetNumberAndCount();
  903. m_frame->TestDanglingEnds();
  904. m_frame->OnPageSettingsChange();
  905. m_toolMgr->GetTool<EE_SELECTION_TOOL>()->RebuildSelection();
  906. m_frame->SyncView();
  907. m_frame->GetCanvas()->Refresh();
  908. m_frame->OnModify();
  909. return 0;
  910. }
  911. bool SCH_EDITOR_CONTROL::doCopy( bool aUseDuplicateClipboard )
  912. {
  913. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  914. EE_SELECTION& selection = selTool->RequestSelection();
  915. SCHEMATIC& schematic = m_frame->Schematic();
  916. if( selection.Empty() )
  917. return false;
  918. if( aUseDuplicateClipboard )
  919. m_duplicateIsHoverSelection = selection.IsHover();
  920. selection.SetScreen( m_frame->GetScreen() );
  921. m_supplementaryClipboard.clear();
  922. for( EDA_ITEM* item : selection )
  923. {
  924. if( item->Type() == SCH_SHEET_T )
  925. {
  926. SCH_SHEET* sheet = (SCH_SHEET*) item;
  927. m_supplementaryClipboard[ sheet->GetFileName() ] = sheet->GetScreen();
  928. }
  929. else if( item->Type() == SCH_FIELD_T && selection.IsHover() )
  930. {
  931. // Most of the time the user is trying to duplicate the parent symbol
  932. // and the field text is in it
  933. selection.Add( item->GetParent() );
  934. }
  935. }
  936. STRING_FORMATTER formatter;
  937. SCH_SEXPR_PLUGIN plugin;
  938. SCH_SHEET_LIST hierarchy = schematic.GetSheets();
  939. SCH_SHEET_PATH selPath = m_frame->GetCurrentSheet();
  940. plugin.Format( &selection, &selPath, schematic, &formatter, true );
  941. if( selection.IsHover() )
  942. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  943. if( aUseDuplicateClipboard )
  944. {
  945. m_duplicateClipboard = formatter.GetString();
  946. return true;
  947. }
  948. return m_toolMgr->SaveClipboard( formatter.GetString() );
  949. }
  950. bool SCH_EDITOR_CONTROL::searchSupplementaryClipboard( const wxString& aSheetFilename,
  951. SCH_SCREEN** aScreen )
  952. {
  953. if( m_supplementaryClipboard.count( aSheetFilename ) > 0 )
  954. {
  955. *aScreen = m_supplementaryClipboard[ aSheetFilename ];
  956. return true;
  957. }
  958. return false;
  959. }
  960. int SCH_EDITOR_CONTROL::Duplicate( const TOOL_EVENT& aEvent )
  961. {
  962. doCopy( true ); // Use the local clipboard
  963. Paste( aEvent );
  964. return 0;
  965. }
  966. int SCH_EDITOR_CONTROL::Cut( const TOOL_EVENT& aEvent )
  967. {
  968. wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( wxWindow::FindFocus() );
  969. if( textEntry )
  970. {
  971. textEntry->Cut();
  972. return 0;
  973. }
  974. if( doCopy() )
  975. m_toolMgr->RunAction( ACTIONS::doDelete, true );
  976. return 0;
  977. }
  978. int SCH_EDITOR_CONTROL::Copy( const TOOL_EVENT& aEvent )
  979. {
  980. wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( wxWindow::FindFocus() );
  981. if( textEntry )
  982. {
  983. textEntry->Copy();
  984. return 0;
  985. }
  986. doCopy();
  987. return 0;
  988. }
  989. void SCH_EDITOR_CONTROL::updatePastedSymbol( SCH_SYMBOL* aSymbol, SCH_SCREEN* aPasteScreen,
  990. const SCH_SHEET_PATH& aPastePath,
  991. const KIID_PATH& aClipPath,
  992. bool aForceKeepAnnotations )
  993. {
  994. wxCHECK( aSymbol && aPasteScreen, /* void */ );
  995. KIID_PATH clipItemPath = aClipPath;
  996. wxString reference, value, footprint;
  997. int unit;
  998. if( m_clipboardSymbolInstances.count( clipItemPath ) > 0 )
  999. {
  1000. SCH_SYMBOL_INSTANCE instance = m_clipboardSymbolInstances.at( clipItemPath );
  1001. unit = instance.m_Unit;
  1002. reference = instance.m_Reference;
  1003. }
  1004. else
  1005. {
  1006. // Some legacy versions saved value fields escaped. While we still do in the symbol
  1007. // editor, we don't anymore in the schematic, so be sure to unescape them.
  1008. SCH_FIELD* valueField = aSymbol->GetField( VALUE_FIELD );
  1009. valueField->SetText( UnescapeString( valueField->GetText() ) );
  1010. // Pasted from notepad or an older instance of eeschema. Use the values in the fields
  1011. // instead.
  1012. reference = aSymbol->GetField( REFERENCE_FIELD )->GetText();
  1013. value = aSymbol->GetField( VALUE_FIELD )->GetText();
  1014. footprint = aSymbol->GetField( FOOTPRINT_FIELD )->GetText();
  1015. unit = aSymbol->GetUnit();
  1016. }
  1017. if( aForceKeepAnnotations && !reference.IsEmpty() )
  1018. aSymbol->SetRef( &aPastePath, reference );
  1019. else
  1020. aSymbol->ClearAnnotation( &aPastePath, false );
  1021. // We might clear annotations but always leave the original unit number from the paste.
  1022. aSymbol->SetUnitSelection( &aPastePath, unit );
  1023. aSymbol->SetUnit( unit );
  1024. }
  1025. SCH_SHEET_PATH SCH_EDITOR_CONTROL::updatePastedSheet( const SCH_SHEET_PATH& aPastePath,
  1026. const KIID_PATH& aClipPath, SCH_SHEET* aSheet,
  1027. bool aForceKeepAnnotations,
  1028. SCH_SHEET_LIST* aPastedSheetsSoFar,
  1029. SCH_REFERENCE_LIST* aPastedSymbolsSoFar )
  1030. {
  1031. wxCHECK( aSheet && aPastedSheetsSoFar && aPastedSymbolsSoFar, aPastePath );
  1032. SCH_SHEET_PATH sheetPath = aPastePath;
  1033. sheetPath.push_back( aSheet );
  1034. aPastedSheetsSoFar->push_back( sheetPath );
  1035. if( aSheet->GetScreen() == nullptr )
  1036. return sheetPath; // We can only really set the page number but not load any items
  1037. for( SCH_ITEM* item : aSheet->GetScreen()->Items() )
  1038. {
  1039. if( item->Type() == SCH_SYMBOL_T )
  1040. {
  1041. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  1042. updatePastedSymbol( symbol, aSheet->GetScreen(), sheetPath, aClipPath,
  1043. aForceKeepAnnotations );
  1044. }
  1045. else if( item->Type() == SCH_SHEET_T )
  1046. {
  1047. SCH_SHEET* subsheet = static_cast<SCH_SHEET*>( item );
  1048. KIID_PATH newClipPath = aClipPath;
  1049. newClipPath.push_back( subsheet->m_Uuid );
  1050. updatePastedSheet( sheetPath, newClipPath, subsheet, aForceKeepAnnotations,
  1051. aPastedSheetsSoFar, aPastedSymbolsSoFar );
  1052. SCH_SHEET_PATH subSheetPath = sheetPath;
  1053. subSheetPath.push_back( subsheet );
  1054. subSheetPath.GetSymbols( *aPastedSymbolsSoFar );
  1055. }
  1056. }
  1057. return sheetPath;
  1058. }
  1059. void SCH_EDITOR_CONTROL::setClipboardInstances( const SCH_SCREEN* aPastedScreen )
  1060. {
  1061. m_clipboardSheetInstances.clear();
  1062. for( const SCH_SHEET_INSTANCE& sheet : aPastedScreen->GetSheetInstances() )
  1063. m_clipboardSheetInstances[sheet.m_Path] = sheet;
  1064. m_clipboardSymbolInstances.clear();
  1065. for( const SCH_SYMBOL_INSTANCE& symbol : aPastedScreen->GetSymbolInstances() )
  1066. m_clipboardSymbolInstances[symbol.m_Path] = symbol;
  1067. }
  1068. int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
  1069. {
  1070. wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( wxWindow::FindFocus() );
  1071. if( textEntry )
  1072. {
  1073. textEntry->Paste();
  1074. return 0;
  1075. }
  1076. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  1077. std::string content;
  1078. VECTOR2I eventPos;
  1079. if( aEvent.IsAction( &ACTIONS::duplicate ) )
  1080. content = m_duplicateClipboard;
  1081. else
  1082. content = m_toolMgr->GetClipboardUTF8();
  1083. if( content.empty() )
  1084. return 0;
  1085. if( aEvent.IsAction( &ACTIONS::duplicate ) )
  1086. eventPos = getViewControls()->GetCursorPosition( false );
  1087. STRING_LINE_READER reader( content, "Clipboard" );
  1088. SCH_SEXPR_PLUGIN plugin;
  1089. SCH_SHEET tempSheet;
  1090. SCH_SCREEN* tempScreen = new SCH_SCREEN( &m_frame->Schematic() );
  1091. EESCHEMA_SETTINGS::PANEL_ANNOTATE& annotate = m_frame->eeconfig()->m_AnnotatePanel;
  1092. int annotateStartNum = m_frame->Schematic().Settings().m_AnnotateStartNum;
  1093. // Screen object on heap is owned by the sheet.
  1094. tempSheet.SetScreen( tempScreen );
  1095. try
  1096. {
  1097. plugin.LoadContent( reader, &tempSheet );
  1098. }
  1099. catch( IO_ERROR& )
  1100. {
  1101. // If it wasn't content, then paste as text object.
  1102. SCH_TEXT* text_item = new SCH_TEXT( wxPoint( 0, 0 ), content );
  1103. text_item->SetTextSpinStyle( TEXT_SPIN_STYLE::RIGHT ); // Left alignment
  1104. tempScreen->Append( text_item );
  1105. }
  1106. // Save loaded screen instances to m_clipboardSheetInstances
  1107. setClipboardInstances( tempScreen );
  1108. tempScreen->MigrateSimModels();
  1109. PASTE_MODE pasteMode = annotate.automatic ? PASTE_MODE::RESPECT_OPTIONS
  1110. : PASTE_MODE::REMOVE_ANNOTATIONS;
  1111. if( aEvent.IsAction( &ACTIONS::pasteSpecial ) )
  1112. {
  1113. DIALOG_PASTE_SPECIAL dlg( m_frame, &pasteMode );
  1114. if( dlg.ShowModal() == wxID_CANCEL )
  1115. return 0;
  1116. }
  1117. bool forceKeepAnnotations = pasteMode != PASTE_MODE::REMOVE_ANNOTATIONS;
  1118. // SCH_SEXP_PLUGIN added the items to the paste screen, but not to the view or anything
  1119. // else. Pull them back out to start with.
  1120. EDA_ITEMS loadedItems;
  1121. bool sheetsPasted = false;
  1122. SCH_SHEET_LIST hierarchy = m_frame->Schematic().GetSheets();
  1123. SCH_SHEET_PATH& pasteRoot = m_frame->GetCurrentSheet();
  1124. wxFileName destFn = pasteRoot.Last()->GetFileName();
  1125. if( destFn.IsRelative() )
  1126. destFn.MakeAbsolute( m_frame->Prj().GetProjectPath() );
  1127. // List of paths in the hierarchy that refer to the destination sheet of the paste
  1128. SCH_SHEET_LIST pasteInstances = hierarchy.FindAllSheetsForScreen( pasteRoot.LastScreen() );
  1129. pasteInstances.SortByPageNumbers();
  1130. // Build a list of screens from the current design (to avoid loading sheets that already exist)
  1131. std::map<wxString, SCH_SCREEN*> loadedScreens;
  1132. for( const SCH_SHEET_PATH& item : hierarchy )
  1133. {
  1134. if( item.LastScreen() )
  1135. loadedScreens[item.Last()->GetFileName()] = item.LastScreen();
  1136. }
  1137. // Build symbol list for reannotation of duplicates
  1138. SCH_REFERENCE_LIST existingRefs;
  1139. hierarchy.GetSymbols( existingRefs );
  1140. existingRefs.SortByReferenceOnly();
  1141. // Build UUID map for fetching last-resolved-properties
  1142. std::map<KIID, EDA_ITEM*> itemMap;
  1143. hierarchy.FillItemMap( itemMap );
  1144. // Keep track of pasted sheets and symbols for the different paths to the hierarchy.
  1145. std::map<SCH_SHEET_PATH, SCH_REFERENCE_LIST> pastedSymbols;
  1146. std::map<SCH_SHEET_PATH, SCH_SHEET_LIST> pastedSheets;
  1147. for( SCH_ITEM* item : tempScreen->Items() )
  1148. {
  1149. loadedItems.push_back( item );
  1150. //@todo: we might want to sort the sheets by page number before adding to loadedItems
  1151. if( item->Type() == SCH_SHEET_T )
  1152. {
  1153. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
  1154. wxFileName srcFn = sheet->GetFileName();
  1155. if( srcFn.IsRelative() )
  1156. srcFn.MakeAbsolute( m_frame->Prj().GetProjectPath() );
  1157. SCH_SHEET_LIST sheetHierarchy( sheet );
  1158. if( hierarchy.TestForRecursion( sheetHierarchy, destFn.GetFullPath( wxPATH_UNIX ) ) )
  1159. {
  1160. auto msg = wxString::Format( _( "The pasted sheet '%s'\n"
  1161. "was dropped because the destination already has "
  1162. "the sheet or one of its subsheets as a parent." ),
  1163. sheet->GetFileName() );
  1164. DisplayError( m_frame, msg );
  1165. loadedItems.pop_back();
  1166. }
  1167. }
  1168. }
  1169. // Remove the references from our temporary screen to prevent freeing on the DTOR
  1170. tempScreen->Clear( false );
  1171. for( unsigned i = 0; i < loadedItems.size(); ++i )
  1172. {
  1173. EDA_ITEM* item = loadedItems[i];
  1174. KIID_PATH clipPath( wxT( "/" ) ); // clipboard is at root
  1175. if( item->Type() == SCH_SYMBOL_T )
  1176. {
  1177. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  1178. // The library symbol gets set from the cached library symbols in the current
  1179. // schematic not the symbol libraries. The cached library symbol may have
  1180. // changed from the original library symbol which would cause the copy to
  1181. // be incorrect.
  1182. SCH_SCREEN* currentScreen = m_frame->GetScreen();
  1183. wxCHECK2( currentScreen, continue );
  1184. auto it = currentScreen->GetLibSymbols().find( symbol->GetSchSymbolLibraryName() );
  1185. auto end = currentScreen->GetLibSymbols().end();
  1186. if( it == end )
  1187. {
  1188. // If can't find library definition in the design, use the pasted library
  1189. it = tempScreen->GetLibSymbols().find( symbol->GetSchSymbolLibraryName() );
  1190. end = tempScreen->GetLibSymbols().end();
  1191. }
  1192. LIB_SYMBOL* libSymbol = nullptr;
  1193. if( it != end )
  1194. {
  1195. libSymbol = new LIB_SYMBOL( *it->second );
  1196. symbol->SetLibSymbol( libSymbol );
  1197. }
  1198. for( SCH_SHEET_PATH& instance : pasteInstances )
  1199. updatePastedSymbol( symbol, tempScreen, instance, clipPath, forceKeepAnnotations );
  1200. // Assign a new KIID
  1201. const_cast<KIID&>( item->m_Uuid ) = KIID();
  1202. // Make sure pins get a new UUID
  1203. for( SCH_PIN* pin : symbol->GetPins() )
  1204. const_cast<KIID&>( pin->m_Uuid ) = KIID();
  1205. for( SCH_SHEET_PATH& instance : pasteInstances )
  1206. {
  1207. // Ignore symbols from a non-existant library.
  1208. if( libSymbol )
  1209. {
  1210. SCH_REFERENCE schReference( symbol, libSymbol, instance );
  1211. schReference.SetSheetNumber( instance.GetVirtualPageNumber() );
  1212. pastedSymbols[instance].AddItem( schReference );
  1213. }
  1214. }
  1215. }
  1216. else if( item->Type() == SCH_SHEET_T )
  1217. {
  1218. SCH_SHEET* sheet = (SCH_SHEET*) item;
  1219. SCH_FIELD& nameField = sheet->GetFields()[SHEETNAME];
  1220. wxString baseName = nameField.GetText();
  1221. wxString candidateName = baseName;
  1222. wxString number;
  1223. while( !baseName.IsEmpty() && wxIsdigit( baseName.Last() ) )
  1224. {
  1225. number = baseName.Last() + number;
  1226. baseName.RemoveLast();
  1227. }
  1228. // Update hierarchy to include any other sheets we already added, avoiding
  1229. // duplicate sheet names
  1230. hierarchy = m_frame->Schematic().GetSheets();
  1231. //@todo: it might be better to just iterate through the sheet names
  1232. // in this screen instead of the whole hierarchy.
  1233. int uniquifier = std::max( 0, wxAtoi( number ) ) + 1;
  1234. while( hierarchy.NameExists( candidateName ) )
  1235. candidateName = wxString::Format( wxT( "%s%d" ), baseName, uniquifier++ );
  1236. nameField.SetText( candidateName );
  1237. wxFileName fn = sheet->GetFileName();
  1238. SCH_SCREEN* existingScreen = nullptr;
  1239. sheet->SetParent( pasteRoot.Last() );
  1240. sheet->SetScreen( nullptr );
  1241. if( !fn.IsAbsolute() )
  1242. {
  1243. wxFileName currentSheetFileName = pasteRoot.LastScreen()->GetFileName();
  1244. fn.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS,
  1245. currentSheetFileName.GetPath() );
  1246. }
  1247. // Try to find the screen for the pasted sheet by several means
  1248. if( !m_frame->Schematic().Root().SearchHierarchy( fn.GetFullPath( wxPATH_UNIX ),
  1249. &existingScreen ) )
  1250. {
  1251. if( loadedScreens.count( sheet->GetFileName() ) > 0 )
  1252. existingScreen = loadedScreens.at( sheet->GetFileName() );
  1253. else
  1254. searchSupplementaryClipboard( sheet->GetFileName(), &existingScreen );
  1255. }
  1256. if( existingScreen )
  1257. {
  1258. sheet->SetScreen( existingScreen );
  1259. }
  1260. else
  1261. {
  1262. if( !m_frame->LoadSheetFromFile( sheet, &pasteRoot, fn.GetFullPath() ) )
  1263. m_frame->InitSheet( sheet, sheet->GetFileName() );
  1264. }
  1265. sheetsPasted = true;
  1266. // Push it to the clipboard path while it still has its old KIID
  1267. clipPath.push_back( sheet->m_Uuid );
  1268. // Assign a new KIID to the pasted sheet
  1269. const_cast<KIID&>( sheet->m_Uuid ) = KIID();
  1270. // Make sure pins get a new UUID
  1271. for( SCH_SHEET_PIN* pin : sheet->GetPins() )
  1272. const_cast<KIID&>( pin->m_Uuid ) = KIID();
  1273. // Once we have our new KIID we can update all pasted instances. This will either
  1274. // reset the annotations or copy "kept" annotations from the supplementary clipboard.
  1275. for( SCH_SHEET_PATH& instance : pasteInstances )
  1276. {
  1277. SCH_SHEET_PATH sheetPath = updatePastedSheet( instance, clipPath, sheet,
  1278. ( forceKeepAnnotations && annotate.automatic ),
  1279. &pastedSheets[instance],
  1280. &pastedSymbols[instance] );
  1281. sheetPath.GetSymbols( pastedSymbols[instance] );
  1282. }
  1283. }
  1284. else
  1285. {
  1286. SCH_ITEM* srcItem = dynamic_cast<SCH_ITEM*>( itemMap[ item->m_Uuid ] );
  1287. SCH_ITEM* destItem = dynamic_cast<SCH_ITEM*>( item );
  1288. // Everything gets a new KIID
  1289. const_cast<KIID&>( item->m_Uuid ) = KIID();
  1290. if( srcItem && destItem )
  1291. {
  1292. destItem->SetConnectivityDirty( true );
  1293. destItem->SetLastResolvedState( srcItem );
  1294. }
  1295. }
  1296. // Lines need both ends selected for a move after paste so the whole line moves.
  1297. if( item->Type() == SCH_LINE_T )
  1298. item->SetFlags( STARTPOINT | ENDPOINT );
  1299. item->SetFlags( IS_NEW | IS_PASTED | IS_MOVING );
  1300. m_frame->AddItemToScreenAndUndoList( m_frame->GetScreen(), (SCH_ITEM*) item, i > 0 );
  1301. // Reset flags for subsequent move operation
  1302. item->SetFlags( IS_NEW | IS_PASTED | IS_MOVING );
  1303. // Start out hidden so the pasted items aren't "ghosted" in their original location
  1304. // before being moved to the current location.
  1305. getView()->Hide( item, true );
  1306. }
  1307. pasteInstances.SortByPageNumbers();
  1308. if( sheetsPasted )
  1309. {
  1310. // Update page numbers: Find next free numeric page number
  1311. for( SCH_SHEET_PATH& instance : pasteInstances )
  1312. {
  1313. pastedSheets[instance].SortByPageNumbers();
  1314. for( SCH_SHEET_PATH& pastedSheet : pastedSheets[instance] )
  1315. {
  1316. int page = 1;
  1317. wxString pageNum = wxString::Format( "%d", page );
  1318. while( hierarchy.PageNumberExists( pageNum ) )
  1319. pageNum = wxString::Format( "%d", ++page );
  1320. pastedSheet.SetPageNumber( pageNum );
  1321. hierarchy.push_back( pastedSheet );
  1322. }
  1323. }
  1324. m_frame->SetSheetNumberAndCount();
  1325. m_frame->UpdateHierarchyNavigator();
  1326. // Get a version with correct sheet numbers since we've pasted sheets,
  1327. // we'll need this when annotating next
  1328. hierarchy = m_frame->Schematic().GetSheets();
  1329. }
  1330. std::map<SCH_SHEET_PATH, SCH_REFERENCE_LIST> annotatedSymbols;
  1331. // Update the list of symbol instances that satisfy the annotation criteria.
  1332. for( const SCH_SHEET_PATH& sheetPath : pasteInstances )
  1333. {
  1334. for( size_t i = 0; i < pastedSymbols[sheetPath].GetCount(); i++ )
  1335. {
  1336. if( pasteMode == PASTE_MODE::UNIQUE_ANNOTATIONS
  1337. || pasteMode == PASTE_MODE::RESPECT_OPTIONS
  1338. || pastedSymbols[sheetPath][i].AlwaysAnnotate() )
  1339. {
  1340. annotatedSymbols[sheetPath].AddItem( pastedSymbols[sheetPath][i] );
  1341. }
  1342. }
  1343. }
  1344. if( !annotatedSymbols.empty() )
  1345. {
  1346. for( SCH_SHEET_PATH& instance : pasteInstances )
  1347. {
  1348. annotatedSymbols[instance].SortByReferenceOnly();
  1349. if( pasteMode == PASTE_MODE::UNIQUE_ANNOTATIONS )
  1350. annotatedSymbols[instance].ReannotateDuplicates( existingRefs );
  1351. else
  1352. annotatedSymbols[instance].ReannotateByOptions( (ANNOTATE_ORDER_T) annotate.sort_order,
  1353. (ANNOTATE_ALGO_T) annotate.method,
  1354. annotateStartNum, existingRefs,
  1355. true,
  1356. &hierarchy );
  1357. annotatedSymbols[instance].UpdateAnnotation();
  1358. // Update existing refs for next iteration
  1359. for( size_t i = 0; i < annotatedSymbols[instance].GetCount(); i++ )
  1360. existingRefs.AddItem( annotatedSymbols[instance][i] );
  1361. }
  1362. }
  1363. m_frame->GetCurrentSheet().UpdateAllScreenReferences();
  1364. // Now clear the previous selection, select the pasted items, and fire up the "move" tool.
  1365. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  1366. m_toolMgr->RunAction( EE_ACTIONS::addItemsToSel, true, &loadedItems );
  1367. EE_SELECTION& selection = selTool->GetSelection();
  1368. if( !selection.Empty() )
  1369. {
  1370. if( aEvent.IsAction( &ACTIONS::duplicate ) )
  1371. {
  1372. int closest_dist = INT_MAX;
  1373. auto processPt =
  1374. [&]( const VECTOR2I& pt )
  1375. {
  1376. int dist = ( eventPos - pt ).EuclideanNorm();
  1377. if( dist < closest_dist )
  1378. {
  1379. selection.SetReferencePoint( pt );
  1380. closest_dist = dist;
  1381. }
  1382. };
  1383. // Prefer connection points (which should remain on grid)
  1384. for( EDA_ITEM* item : selection.Items() )
  1385. {
  1386. SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item );
  1387. LIB_PIN* lib_pin = dynamic_cast<LIB_PIN*>( item );
  1388. if( sch_item && sch_item->IsConnectable() )
  1389. {
  1390. for( const VECTOR2I& pt : sch_item->GetConnectionPoints() )
  1391. processPt( pt );
  1392. }
  1393. else if( lib_pin )
  1394. {
  1395. processPt( lib_pin->GetPosition() );
  1396. }
  1397. }
  1398. // Only process other points if we didn't find any connection points
  1399. if( closest_dist == INT_MAX )
  1400. {
  1401. for( EDA_ITEM* item : selection.Items() )
  1402. {
  1403. switch( item->Type() )
  1404. {
  1405. case SCH_LINE_T:
  1406. processPt( static_cast<SCH_LINE*>( item )->GetStartPoint() );
  1407. processPt( static_cast<SCH_LINE*>( item )->GetEndPoint() );
  1408. break;
  1409. case SCH_SHAPE_T:
  1410. {
  1411. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
  1412. switch( shape->GetShape() )
  1413. {
  1414. case SHAPE_T::RECT:
  1415. for( const VECTOR2I& pt : shape->GetRectCorners() )
  1416. processPt( pt );
  1417. break;
  1418. case SHAPE_T::CIRCLE:
  1419. processPt( shape->GetCenter() );
  1420. break;
  1421. case SHAPE_T::POLY:
  1422. for( int ii = 0; ii < shape->GetPolyShape().TotalVertices(); ++ii )
  1423. processPt( shape->GetPolyShape().CVertex( ii ) );
  1424. break;
  1425. default:
  1426. processPt( shape->GetStart() );
  1427. processPt( shape->GetEnd() );
  1428. break;
  1429. }
  1430. break;
  1431. }
  1432. default:
  1433. processPt( item->GetPosition() );
  1434. break;
  1435. }
  1436. }
  1437. }
  1438. selection.SetIsHover( m_duplicateIsHoverSelection );
  1439. }
  1440. else
  1441. {
  1442. SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetTopLeftItem() );
  1443. selection.SetReferencePoint( item->GetPosition() );
  1444. }
  1445. m_toolMgr->RunAction( EE_ACTIONS::move, false );
  1446. }
  1447. return 0;
  1448. }
  1449. int SCH_EDITOR_CONTROL::EditWithSymbolEditor( const TOOL_EVENT& aEvent )
  1450. {
  1451. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  1452. EE_SELECTION& selection = selTool->RequestSelection( { SCH_SYMBOL_T } );
  1453. SCH_SYMBOL* symbol = nullptr;
  1454. SYMBOL_EDIT_FRAME* symbolEditor;
  1455. if( selection.GetSize() >= 1 )
  1456. symbol = (SCH_SYMBOL*) selection.Front();
  1457. if( selection.IsHover() )
  1458. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  1459. if( !symbol || symbol->GetEditFlags() != 0 )
  1460. return 0;
  1461. if( symbol->IsMissingLibSymbol() )
  1462. {
  1463. m_frame->ShowInfoBarError( _( "Symbols with broken library symbol links cannot "
  1464. "be edited." ) );
  1465. return 0;
  1466. }
  1467. m_toolMgr->RunAction( ACTIONS::showSymbolEditor, true );
  1468. symbolEditor = (SYMBOL_EDIT_FRAME*) m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, false );
  1469. if( symbolEditor )
  1470. {
  1471. if( wxWindow* blocking_win = symbolEditor->Kiway().GetBlockingDialog() )
  1472. blocking_win->Close( true );
  1473. if( aEvent.IsAction( &EE_ACTIONS::editWithLibEdit ) )
  1474. {
  1475. symbolEditor->LoadSymbolFromSchematic( symbol );
  1476. }
  1477. else if( aEvent.IsAction( &EE_ACTIONS::editLibSymbolWithLibEdit ) )
  1478. {
  1479. symbolEditor->LoadSymbol( symbol->GetLibId(), symbol->GetUnit(), symbol->GetConvert() );
  1480. if( !symbolEditor->IsSymbolTreeShown() )
  1481. {
  1482. wxCommandEvent evt;
  1483. symbolEditor->OnToggleSymbolTree( evt );
  1484. }
  1485. }
  1486. }
  1487. return 0;
  1488. }
  1489. int SCH_EDITOR_CONTROL::Annotate( const TOOL_EVENT& aEvent )
  1490. {
  1491. wxCommandEvent dummy;
  1492. m_frame->OnAnnotate( dummy );
  1493. return 0;
  1494. }
  1495. int SCH_EDITOR_CONTROL::ShowCvpcb( const TOOL_EVENT& aEvent )
  1496. {
  1497. wxCommandEvent dummy;
  1498. m_frame->OnOpenCvpcb( dummy );
  1499. return 0;
  1500. }
  1501. int SCH_EDITOR_CONTROL::EditSymbolFields( const TOOL_EVENT& aEvent )
  1502. {
  1503. DIALOG_SYMBOL_FIELDS_TABLE dlg( m_frame );
  1504. dlg.ShowQuasiModal();
  1505. return 0;
  1506. }
  1507. int SCH_EDITOR_CONTROL::EditSymbolLibraryLinks( const TOOL_EVENT& aEvent )
  1508. {
  1509. if( InvokeDialogEditSymbolsLibId( m_frame ) )
  1510. m_frame->HardRedraw();
  1511. return 0;
  1512. }
  1513. int SCH_EDITOR_CONTROL::ShowPcbNew( const TOOL_EVENT& aEvent )
  1514. {
  1515. wxCommandEvent dummy;
  1516. m_frame->OnOpenPcbnew( dummy );
  1517. return 0;
  1518. }
  1519. int SCH_EDITOR_CONTROL::UpdatePCB( const TOOL_EVENT& aEvent )
  1520. {
  1521. wxCommandEvent dummy;
  1522. m_frame->OnUpdatePCB( dummy );
  1523. return 0;
  1524. }
  1525. int SCH_EDITOR_CONTROL::UpdateFromPCB( const TOOL_EVENT& aEvent )
  1526. {
  1527. DIALOG_UPDATE_FROM_PCB dlg( m_frame );
  1528. dlg.ShowModal();
  1529. return 0;
  1530. }
  1531. int SCH_EDITOR_CONTROL::ExportNetlist( const TOOL_EVENT& aEvent )
  1532. {
  1533. int result = NET_PLUGIN_CHANGE;
  1534. // If a plugin is removed or added, rebuild and reopen the new dialog
  1535. while( result == NET_PLUGIN_CHANGE )
  1536. result = InvokeDialogNetList( m_frame );
  1537. return 0;
  1538. }
  1539. int SCH_EDITOR_CONTROL::GenerateBOM( const TOOL_EVENT& aEvent )
  1540. {
  1541. InvokeDialogCreateBOM( m_frame );
  1542. return 0;
  1543. }
  1544. int SCH_EDITOR_CONTROL::DrawSheetOnClipboard( const TOOL_EVENT& aEvent )
  1545. {
  1546. m_frame->RecalculateConnections( LOCAL_CLEANUP );
  1547. m_frame->DrawCurrentSheetToClipboard();
  1548. return 0;
  1549. }
  1550. int SCH_EDITOR_CONTROL::ShowHierarchy( const TOOL_EVENT& aEvent )
  1551. {
  1552. getEditFrame<SCH_EDIT_FRAME>()->ToggleSchematicHierarchy();
  1553. return 0;
  1554. }
  1555. int SCH_EDITOR_CONTROL::ToggleHiddenPins( const TOOL_EVENT& aEvent )
  1556. {
  1557. EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
  1558. cfg->m_Appearance.show_hidden_pins = !cfg->m_Appearance.show_hidden_pins;
  1559. getView()->UpdateAllItems( KIGFX::REPAINT );
  1560. m_frame->GetCanvas()->Refresh();
  1561. return 0;
  1562. }
  1563. int SCH_EDITOR_CONTROL::ToggleHiddenFields( const TOOL_EVENT& aEvent )
  1564. {
  1565. EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
  1566. cfg->m_Appearance.show_hidden_fields = !cfg->m_Appearance.show_hidden_fields;
  1567. getView()->UpdateAllItems( KIGFX::REPAINT );
  1568. m_frame->GetCanvas()->Refresh();
  1569. return 0;
  1570. }
  1571. int SCH_EDITOR_CONTROL::ToggleERCWarnings( const TOOL_EVENT& aEvent )
  1572. {
  1573. EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
  1574. cfg->m_Appearance.show_erc_warnings = !cfg->m_Appearance.show_erc_warnings;
  1575. getView()->SetLayerVisible( LAYER_ERC_WARN, cfg->m_Appearance.show_erc_warnings );
  1576. m_frame->GetCanvas()->Refresh();
  1577. return 0;
  1578. }
  1579. int SCH_EDITOR_CONTROL::ToggleERCErrors( const TOOL_EVENT& aEvent )
  1580. {
  1581. EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
  1582. cfg->m_Appearance.show_erc_errors = !cfg->m_Appearance.show_erc_errors;
  1583. getView()->SetLayerVisible( LAYER_ERC_ERR, cfg->m_Appearance.show_erc_errors );
  1584. m_frame->GetCanvas()->Refresh();
  1585. return 0;
  1586. }
  1587. int SCH_EDITOR_CONTROL::ToggleERCExclusions( const TOOL_EVENT& aEvent )
  1588. {
  1589. EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
  1590. cfg->m_Appearance.show_erc_exclusions = !cfg->m_Appearance.show_erc_exclusions;
  1591. getView()->SetLayerVisible( LAYER_ERC_EXCLUSION, cfg->m_Appearance.show_erc_exclusions );
  1592. m_frame->GetCanvas()->Refresh();
  1593. return 0;
  1594. }
  1595. int SCH_EDITOR_CONTROL::ChangeLineMode( const TOOL_EVENT& aEvent )
  1596. {
  1597. m_frame->eeconfig()->m_Drawing.line_mode = aEvent.Parameter<int>();
  1598. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  1599. return 0;
  1600. }
  1601. int SCH_EDITOR_CONTROL::NextLineMode( const TOOL_EVENT& aEvent )
  1602. {
  1603. m_frame->eeconfig()->m_Drawing.line_mode++;
  1604. m_frame->eeconfig()->m_Drawing.line_mode %= LINE_MODE::LINE_MODE_COUNT;
  1605. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  1606. return 0;
  1607. }
  1608. int SCH_EDITOR_CONTROL::ToggleAnnotateAuto( const TOOL_EVENT& aEvent )
  1609. {
  1610. EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
  1611. cfg->m_AnnotatePanel.automatic = !cfg->m_AnnotatePanel.automatic;
  1612. return 0;
  1613. }
  1614. int SCH_EDITOR_CONTROL::ToggleAnnotateRecursive( const TOOL_EVENT& aEvent )
  1615. {
  1616. EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
  1617. cfg->m_AnnotatePanel.recursive = !cfg->m_AnnotatePanel.recursive;
  1618. return 0;
  1619. }
  1620. int SCH_EDITOR_CONTROL::TogglePythonConsole( const TOOL_EVENT& aEvent )
  1621. {
  1622. m_frame->ScriptingConsoleEnableDisable();
  1623. return 0;
  1624. }
  1625. int SCH_EDITOR_CONTROL::RepairSchematic( const TOOL_EVENT& aEvent )
  1626. {
  1627. int errors = 0;
  1628. wxString details;
  1629. bool quiet = aEvent.Parameter<bool>();
  1630. // Repair duplicate IDs.
  1631. std::map<KIID, EDA_ITEM*> ids;
  1632. int duplicates = 0;
  1633. auto processItem =
  1634. [&]( EDA_ITEM* aItem )
  1635. {
  1636. auto it = ids.find( aItem->m_Uuid );
  1637. if( it != ids.end() && it->second != aItem )
  1638. {
  1639. duplicates++;
  1640. const_cast<KIID&>( aItem->m_Uuid ) = KIID();
  1641. }
  1642. ids[ aItem->m_Uuid ] = aItem;
  1643. };
  1644. // Symbol IDs are the most important, so give them the first crack at "claiming" a
  1645. // particular KIID.
  1646. for( const SCH_SHEET_PATH& sheet : m_frame->Schematic().GetSheets() )
  1647. {
  1648. SCH_SCREEN* screen = sheet.LastScreen();
  1649. for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
  1650. {
  1651. processItem( item );
  1652. for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( item )->GetPins( &sheet ) )
  1653. processItem( pin );
  1654. }
  1655. }
  1656. for( const SCH_SHEET_PATH& sheet : m_frame->Schematic().GetSheets() )
  1657. {
  1658. SCH_SCREEN* screen = sheet.LastScreen();
  1659. for( SCH_ITEM* item : screen->Items() )
  1660. {
  1661. processItem( item );
  1662. item->RunOnChildren(
  1663. [&]( SCH_ITEM* aChild )
  1664. {
  1665. processItem( item );
  1666. } );
  1667. }
  1668. }
  1669. /*******************************
  1670. * Your test here
  1671. */
  1672. /*******************************
  1673. * Inform the user
  1674. */
  1675. if( duplicates )
  1676. {
  1677. errors += duplicates;
  1678. details += wxString::Format( _( "%d duplicate IDs replaced.\n" ), duplicates );
  1679. }
  1680. if( errors )
  1681. {
  1682. m_frame->OnModify();
  1683. wxString msg = wxString::Format( _( "%d potential problems repaired." ), errors );
  1684. if( !quiet )
  1685. DisplayInfoMessage( m_frame, msg, details );
  1686. }
  1687. else if( !quiet )
  1688. {
  1689. DisplayInfoMessage( m_frame, _( "No errors found." ) );
  1690. }
  1691. return 0;
  1692. }
  1693. void SCH_EDITOR_CONTROL::setTransitions()
  1694. {
  1695. Go( &SCH_EDITOR_CONTROL::New, ACTIONS::doNew.MakeEvent() );
  1696. Go( &SCH_EDITOR_CONTROL::Open, ACTIONS::open.MakeEvent() );
  1697. Go( &SCH_EDITOR_CONTROL::Save, ACTIONS::save.MakeEvent() );
  1698. Go( &SCH_EDITOR_CONTROL::SaveAs, ACTIONS::saveAs.MakeEvent() );
  1699. Go( &SCH_EDITOR_CONTROL::SaveCurrSheetCopyAs, EE_ACTIONS::saveCurrSheetCopyAs.MakeEvent() );
  1700. Go( &SCH_EDITOR_CONTROL::Revert, ACTIONS::revert.MakeEvent() );
  1701. Go( &SCH_EDITOR_CONTROL::ShowSchematicSetup, EE_ACTIONS::schematicSetup.MakeEvent() );
  1702. Go( &SCH_EDITOR_CONTROL::PageSetup, ACTIONS::pageSettings.MakeEvent() );
  1703. Go( &SCH_EDITOR_CONTROL::Print, ACTIONS::print.MakeEvent() );
  1704. Go( &SCH_EDITOR_CONTROL::Plot, ACTIONS::plot.MakeEvent() );
  1705. Go( &SCH_EDITOR_CONTROL::Quit, ACTIONS::quit.MakeEvent() );
  1706. Go( &SCH_EDITOR_CONTROL::RescueSymbols, EE_ACTIONS::rescueSymbols.MakeEvent() );
  1707. Go( &SCH_EDITOR_CONTROL::RemapSymbols, EE_ACTIONS::remapSymbols.MakeEvent() );
  1708. Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::PointSelectedEvent );
  1709. Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::SelectedEvent );
  1710. Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::UnselectedEvent );
  1711. Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::ClearedEvent );
  1712. Go( &SCH_EDITOR_CONTROL::ExplicitCrossProbeToPcb, EE_ACTIONS::selectOnPCB.MakeEvent() );
  1713. #ifdef KICAD_SPICE
  1714. Go( &SCH_EDITOR_CONTROL::SimProbe, EE_ACTIONS::simProbe.MakeEvent() );
  1715. Go( &SCH_EDITOR_CONTROL::SimTune, EE_ACTIONS::simTune.MakeEvent() );
  1716. #endif /* KICAD_SPICE */
  1717. Go( &SCH_EDITOR_CONTROL::HighlightNet, EE_ACTIONS::highlightNet.MakeEvent() );
  1718. Go( &SCH_EDITOR_CONTROL::ClearHighlight, EE_ACTIONS::clearHighlight.MakeEvent() );
  1719. Go( &SCH_EDITOR_CONTROL::HighlightNetCursor, EE_ACTIONS::highlightNetTool.MakeEvent() );
  1720. Go( &SCH_EDITOR_CONTROL::UpdateNetHighlighting, EVENTS::SelectedItemsModified );
  1721. Go( &SCH_EDITOR_CONTROL::UpdateNetHighlighting, EE_ACTIONS::updateNetHighlighting.MakeEvent() );
  1722. Go( &SCH_EDITOR_CONTROL::AssignNetclass, EE_ACTIONS::assignNetclass.MakeEvent() );
  1723. Go( &SCH_EDITOR_CONTROL::Undo, ACTIONS::undo.MakeEvent() );
  1724. Go( &SCH_EDITOR_CONTROL::Redo, ACTIONS::redo.MakeEvent() );
  1725. Go( &SCH_EDITOR_CONTROL::Cut, ACTIONS::cut.MakeEvent() );
  1726. Go( &SCH_EDITOR_CONTROL::Copy, ACTIONS::copy.MakeEvent() );
  1727. Go( &SCH_EDITOR_CONTROL::Paste, ACTIONS::paste.MakeEvent() );
  1728. Go( &SCH_EDITOR_CONTROL::Paste, ACTIONS::pasteSpecial.MakeEvent() );
  1729. Go( &SCH_EDITOR_CONTROL::Duplicate, ACTIONS::duplicate.MakeEvent() );
  1730. Go( &SCH_EDITOR_CONTROL::EditWithSymbolEditor, EE_ACTIONS::editWithLibEdit.MakeEvent() );
  1731. Go( &SCH_EDITOR_CONTROL::EditWithSymbolEditor, EE_ACTIONS::editLibSymbolWithLibEdit.MakeEvent() );
  1732. Go( &SCH_EDITOR_CONTROL::ShowCvpcb, EE_ACTIONS::assignFootprints.MakeEvent() );
  1733. Go( &SCH_EDITOR_CONTROL::ImportFPAssignments, EE_ACTIONS::importFPAssignments.MakeEvent() );
  1734. Go( &SCH_EDITOR_CONTROL::Annotate, EE_ACTIONS::annotate.MakeEvent() );
  1735. Go( &SCH_EDITOR_CONTROL::EditSymbolFields, EE_ACTIONS::editSymbolFields.MakeEvent() );
  1736. Go( &SCH_EDITOR_CONTROL::EditSymbolLibraryLinks,EE_ACTIONS::editSymbolLibraryLinks.MakeEvent() );
  1737. Go( &SCH_EDITOR_CONTROL::ShowPcbNew, EE_ACTIONS::showPcbNew.MakeEvent() );
  1738. Go( &SCH_EDITOR_CONTROL::UpdatePCB, ACTIONS::updatePcbFromSchematic.MakeEvent() );
  1739. Go( &SCH_EDITOR_CONTROL::UpdateFromPCB, ACTIONS::updateSchematicFromPcb.MakeEvent() );
  1740. Go( &SCH_EDITOR_CONTROL::ExportNetlist, EE_ACTIONS::exportNetlist.MakeEvent() );
  1741. Go( &SCH_EDITOR_CONTROL::GenerateBOM, EE_ACTIONS::generateBOM.MakeEvent() );
  1742. Go( &SCH_EDITOR_CONTROL::DrawSheetOnClipboard, EE_ACTIONS::drawSheetOnClipboard.MakeEvent() );
  1743. Go( &SCH_EDITOR_CONTROL::ShowHierarchy, EE_ACTIONS::showHierarchy.MakeEvent() );
  1744. Go( &SCH_EDITOR_CONTROL::ToggleHiddenPins, EE_ACTIONS::toggleHiddenPins.MakeEvent() );
  1745. Go( &SCH_EDITOR_CONTROL::ToggleHiddenFields, EE_ACTIONS::toggleHiddenFields.MakeEvent() );
  1746. Go( &SCH_EDITOR_CONTROL::ToggleERCWarnings, EE_ACTIONS::toggleERCWarnings.MakeEvent() );
  1747. Go( &SCH_EDITOR_CONTROL::ToggleERCErrors, EE_ACTIONS::toggleERCErrors.MakeEvent() );
  1748. Go( &SCH_EDITOR_CONTROL::ToggleERCExclusions, EE_ACTIONS::toggleERCExclusions.MakeEvent() );
  1749. Go( &SCH_EDITOR_CONTROL::ChangeLineMode, EE_ACTIONS::lineModeFree.MakeEvent() );
  1750. Go( &SCH_EDITOR_CONTROL::ChangeLineMode, EE_ACTIONS::lineMode90.MakeEvent() );
  1751. Go( &SCH_EDITOR_CONTROL::ChangeLineMode, EE_ACTIONS::lineMode45.MakeEvent() );
  1752. Go( &SCH_EDITOR_CONTROL::NextLineMode, EE_ACTIONS::lineModeNext.MakeEvent() );
  1753. Go( &SCH_EDITOR_CONTROL::ToggleAnnotateAuto, EE_ACTIONS::toggleAnnotateAuto.MakeEvent() );
  1754. Go( &SCH_EDITOR_CONTROL::TogglePythonConsole, EE_ACTIONS::showPythonConsole.MakeEvent() );
  1755. Go( &SCH_EDITOR_CONTROL::RepairSchematic, EE_ACTIONS::repairSchematic.MakeEvent() );
  1756. Go( &SCH_EDITOR_CONTROL::ExportSymbolsToLibrary, EE_ACTIONS::exportSymbolsToLibrary.MakeEvent() );
  1757. Go( &SCH_EDITOR_CONTROL::ExportSymbolsToLibrary, EE_ACTIONS::exportSymbolsToNewLibrary.MakeEvent() );
  1758. }