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.

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