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.

2686 lines
92 KiB

7 years ago
3 years ago
3 years ago
2 years ago
3 years ago
5 years ago
3 years ago
3 years ago
3 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 CERN
  5. * Copyright (C) 2019-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 <kiway.h>
  25. #include <tool/picker_tool.h>
  26. #include <tools/sch_edit_tool.h>
  27. #include <tools/ee_selection_tool.h>
  28. #include <tools/sch_line_wire_bus_tool.h>
  29. #include <tools/sch_move_tool.h>
  30. #include <tools/sch_drawing_tools.h>
  31. #include <ee_actions.h>
  32. #include <confirm.h>
  33. #include <string_utils.h>
  34. #include <sch_item.h>
  35. #include <sch_symbol.h>
  36. #include <sch_shape.h>
  37. #include <sch_sheet.h>
  38. #include <sch_sheet_pin.h>
  39. #include <sch_text.h>
  40. #include <sch_textbox.h>
  41. #include <sch_bitmap.h>
  42. #include <sch_view.h>
  43. #include <sch_line.h>
  44. #include <sch_bus_entry.h>
  45. #include <sch_junction.h>
  46. #include <sch_edit_frame.h>
  47. #include <schematic.h>
  48. #include <sch_commit.h>
  49. #include <drawing_sheet/ds_proxy_view_item.h>
  50. #include <eeschema_id.h>
  51. #include <dialogs/dialog_change_symbols.h>
  52. #include <dialogs/dialog_image_properties.h>
  53. #include <dialogs/dialog_line_properties.h>
  54. #include <dialogs/dialog_wire_bus_properties.h>
  55. #include <dialogs/dialog_symbol_properties.h>
  56. #include <dialogs/dialog_sheet_pin_properties.h>
  57. #include <dialogs/dialog_field_properties.h>
  58. #include <dialogs/dialog_junction_props.h>
  59. #include <dialogs/dialog_shape_properties.h>
  60. #include <dialogs/dialog_label_properties.h>
  61. #include <dialogs/dialog_text_properties.h>
  62. #include <pgm_base.h>
  63. #include <settings/settings_manager.h>
  64. #include <symbol_editor_settings.h>
  65. #include <core/kicad_algo.h>
  66. #include <wx/textdlg.h>
  67. #include <project/net_settings.h>
  68. class SYMBOL_UNIT_MENU : public ACTION_MENU
  69. {
  70. public:
  71. SYMBOL_UNIT_MENU() :
  72. ACTION_MENU( true )
  73. {
  74. SetIcon( BITMAPS::component_select_unit );
  75. SetTitle( _( "Symbol Unit" ) );
  76. }
  77. protected:
  78. ACTION_MENU* create() const override
  79. {
  80. return new SYMBOL_UNIT_MENU();
  81. }
  82. private:
  83. void update() override
  84. {
  85. EE_SELECTION_TOOL* selTool = getToolManager()->GetTool<EE_SELECTION_TOOL>();
  86. EE_SELECTION& selection = selTool->GetSelection();
  87. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
  88. Clear();
  89. wxCHECK( symbol, /* void */ );
  90. int unit = symbol->GetUnit();
  91. for( int ii = 0; ii < symbol->GetLibSymbolRef()->GetUnitCount(); ii++ )
  92. {
  93. wxString num_unit;
  94. num_unit.Printf( _( "Unit %s" ), symbol->SubReference( ii + 1, false ) );
  95. wxMenuItem* item = Append( ID_POPUP_SCH_SELECT_UNIT1 + ii, num_unit, wxEmptyString,
  96. wxITEM_CHECK );
  97. if( unit == ii + 1 )
  98. item->Check( true );
  99. // The ID max for these submenus is ID_POPUP_SCH_SELECT_UNIT_END
  100. // See eeschema_id to modify this value.
  101. if( ii >= ( ID_POPUP_SCH_SELECT_UNIT_END - ID_POPUP_SCH_SELECT_UNIT1) )
  102. break; // We have used all IDs for these submenus
  103. }
  104. }
  105. };
  106. class ALT_PIN_FUNCTION_MENU : public ACTION_MENU
  107. {
  108. public:
  109. ALT_PIN_FUNCTION_MENU() :
  110. ACTION_MENU( true )
  111. {
  112. SetIcon( BITMAPS::component_select_unit );
  113. SetTitle( _( "Pin Function" ) );
  114. }
  115. protected:
  116. ACTION_MENU* create() const override
  117. {
  118. return new ALT_PIN_FUNCTION_MENU();
  119. }
  120. private:
  121. void update() override
  122. {
  123. EE_SELECTION_TOOL* selTool = getToolManager()->GetTool<EE_SELECTION_TOOL>();
  124. EE_SELECTION& selection = selTool->GetSelection();
  125. SCH_PIN* pin = dynamic_cast<SCH_PIN*>( selection.Front() );
  126. Clear();
  127. wxCHECK( pin, /* void */ );
  128. wxMenuItem* item = Append( ID_POPUP_SCH_ALT_PIN_FUNCTION, pin->GetName(), wxEmptyString,
  129. wxITEM_CHECK );
  130. if( pin->GetAlt().IsEmpty() )
  131. item->Check( true );
  132. int ii = 1;
  133. for( const auto& [ name, definition ] : pin->GetLibPin()->GetAlternates() )
  134. {
  135. item = Append( ID_POPUP_SCH_ALT_PIN_FUNCTION + ii, name, wxEmptyString, wxITEM_CHECK );
  136. if( name == pin->GetAlt() )
  137. item->Check( true );
  138. // The ID max for these submenus is ID_POPUP_SCH_ALT_PIN_FUNCTION_END
  139. // See eeschema_id to modify this value.
  140. if( ++ii >= ( ID_POPUP_SCH_ALT_PIN_FUNCTION_END - ID_POPUP_SCH_SELECT_UNIT ) )
  141. break; // We have used all IDs for these submenus
  142. }
  143. }
  144. };
  145. class PIN_TRICKS_MENU : public ACTION_MENU
  146. {
  147. public:
  148. PIN_TRICKS_MENU() : ACTION_MENU( true )
  149. {
  150. SetIcon( BITMAPS::pin );
  151. SetTitle( _( "Pin Helpers" ) );
  152. }
  153. protected:
  154. ACTION_MENU* create() const override { return new PIN_TRICKS_MENU(); }
  155. private:
  156. void update() override
  157. {
  158. EE_SELECTION_TOOL* selTool = getToolManager()->GetTool<EE_SELECTION_TOOL>();
  159. EE_SELECTION& selection = selTool->GetSelection();
  160. SCH_PIN* pin = dynamic_cast<SCH_PIN*>( selection.Front() );
  161. Clear();
  162. if( !pin )
  163. return;
  164. Add( _( "Wire" ), ID_POPUP_SCH_PIN_TRICKS_WIRE, BITMAPS::add_line );
  165. Add( _( "No Connect" ), ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT, BITMAPS::noconn );
  166. Add( _( "Net Label" ), ID_POPUP_SCH_PIN_TRICKS_NET_LABEL, BITMAPS::add_label );
  167. Add( _( "Hierarchical Label" ), ID_POPUP_SCH_PIN_TRICKS_HIER_LABEL, BITMAPS::add_hierarchical_label );
  168. Add( _( "Global Label" ), ID_POPUP_SCH_PIN_TRICKS_GLOBAL_LABEL, BITMAPS::add_glabel );
  169. }
  170. };
  171. SCH_EDIT_TOOL::SCH_EDIT_TOOL() :
  172. EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveEdit" )
  173. {
  174. m_pickerItem = nullptr;
  175. }
  176. using E_C = EE_CONDITIONS;
  177. bool SCH_EDIT_TOOL::Init()
  178. {
  179. EE_TOOL_BASE::Init();
  180. SCH_DRAWING_TOOLS* drawingTools = m_toolMgr->GetTool<SCH_DRAWING_TOOLS>();
  181. SCH_MOVE_TOOL* moveTool = m_toolMgr->GetTool<SCH_MOVE_TOOL>();
  182. wxASSERT_MSG( drawingTools, "eeshema.InteractiveDrawing tool is not available" );
  183. auto hasElements =
  184. [this]( const SELECTION& aSel )
  185. {
  186. return !m_frame->GetScreen()->Items().empty();
  187. };
  188. auto sheetHasUndefinedPins =
  189. []( const SELECTION& aSel )
  190. {
  191. if( aSel.Size() == 1 && aSel.Front()->Type() == SCH_SHEET_T )
  192. return static_cast<SCH_SHEET*>( aSel.Front() )->HasUndefinedPins();
  193. return false;
  194. };
  195. auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_SHEET_T } );
  196. auto haveHighlight =
  197. [&]( const SELECTION& sel )
  198. {
  199. SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  200. return editFrame && !editFrame->GetHighlightedConnection().IsEmpty();
  201. };
  202. auto anyTextTool =
  203. [this]( const SELECTION& aSel )
  204. {
  205. return ( m_frame->IsCurrentTool( EE_ACTIONS::placeLabel )
  206. || m_frame->IsCurrentTool( EE_ACTIONS::placeClassLabel )
  207. || m_frame->IsCurrentTool( EE_ACTIONS::placeGlobalLabel )
  208. || m_frame->IsCurrentTool( EE_ACTIONS::placeHierLabel )
  209. || m_frame->IsCurrentTool( EE_ACTIONS::placeSchematicText ) );
  210. };
  211. auto duplicateCondition =
  212. []( const SELECTION& aSel )
  213. {
  214. if( SCH_LINE_WIRE_BUS_TOOL::IsDrawingLineWireOrBus( aSel ) )
  215. return false;
  216. return true;
  217. };
  218. auto orientCondition =
  219. []( const SELECTION& aSel )
  220. {
  221. if( SCH_LINE_WIRE_BUS_TOOL::IsDrawingLineWireOrBus( aSel ) )
  222. return false;
  223. return SELECTION_CONDITIONS::HasTypes( SCH_EDIT_TOOL::RotatableItems )( aSel );
  224. };
  225. auto propertiesCondition =
  226. [&]( const SELECTION& aSel )
  227. {
  228. if( aSel.GetSize() == 0 )
  229. {
  230. if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
  231. {
  232. DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
  233. VECTOR2D cursor = getViewControls()->GetCursorPosition( false );
  234. if( ds && ds->HitTestDrawingSheetItems( getView(), cursor ) )
  235. return true;
  236. }
  237. return false;
  238. }
  239. SCH_ITEM* firstItem = dynamic_cast<SCH_ITEM*>( aSel.Front() );
  240. const EE_SELECTION* eeSelection = dynamic_cast<const EE_SELECTION*>( &aSel );
  241. if( !firstItem || !eeSelection )
  242. return false;
  243. switch( firstItem->Type() )
  244. {
  245. case SCH_SYMBOL_T:
  246. case SCH_SHEET_T:
  247. case SCH_SHEET_PIN_T:
  248. case SCH_TEXT_T:
  249. case SCH_TEXTBOX_T:
  250. case SCH_LABEL_T:
  251. case SCH_GLOBAL_LABEL_T:
  252. case SCH_HIER_LABEL_T:
  253. case SCH_DIRECTIVE_LABEL_T:
  254. case SCH_FIELD_T:
  255. case SCH_SHAPE_T:
  256. case SCH_BITMAP_T:
  257. return aSel.GetSize() == 1;
  258. case SCH_LINE_T:
  259. case SCH_BUS_WIRE_ENTRY_T:
  260. case SCH_JUNCTION_T:
  261. if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
  262. [&]( const EDA_ITEM* item )
  263. {
  264. return item->Type() == SCH_LINE_T
  265. && static_cast<const SCH_LINE*>( item )->IsGraphicLine();
  266. } ) )
  267. {
  268. return true;
  269. }
  270. else if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
  271. [&]( const EDA_ITEM* item )
  272. {
  273. return item->Type() == SCH_JUNCTION_T;
  274. } ) )
  275. {
  276. return true;
  277. }
  278. else if( std::all_of( aSel.Items().begin(), aSel.Items().end(),
  279. [&]( const EDA_ITEM* item )
  280. {
  281. const SCH_ITEM* schItem = dynamic_cast<const SCH_ITEM*>( item );
  282. wxCHECK( schItem, false );
  283. return ( schItem->HasLineStroke() && schItem->IsConnectable() )
  284. || item->Type() == SCH_JUNCTION_T;
  285. } ) )
  286. {
  287. return true;
  288. }
  289. return false;
  290. default:
  291. return false;
  292. }
  293. };
  294. auto autoplaceCondition =
  295. []( const SELECTION& aSel )
  296. {
  297. for( const EDA_ITEM* item : aSel )
  298. {
  299. if( item->IsType( EE_COLLECTOR::FieldOwners ) )
  300. return true;
  301. }
  302. return false;
  303. };
  304. // allTextTypes does not include SCH_SHEET_PIN_T because one cannot convert other
  305. // types to/from this type, living only in a SHEET
  306. static std::vector<KICAD_T> allTextTypes = { SCH_LABEL_T,
  307. SCH_DIRECTIVE_LABEL_T,
  308. SCH_GLOBAL_LABEL_T,
  309. SCH_HIER_LABEL_T,
  310. SCH_TEXT_T,
  311. SCH_TEXTBOX_T };
  312. auto toChangeCondition = ( E_C::OnlyTypes( allTextTypes ) );
  313. auto toLabelCondition = ( E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_DIRECTIVE_LABEL_T,
  314. SCH_GLOBAL_LABEL_T,
  315. SCH_HIER_LABEL_T,
  316. SCH_TEXT_T,
  317. SCH_TEXTBOX_T } ) )
  318. || ( E_C::MoreThan( 1 ) && E_C::OnlyTypes( allTextTypes ) );
  319. auto toCLabelCondition = ( E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_LABEL_T,
  320. SCH_HIER_LABEL_T,
  321. SCH_GLOBAL_LABEL_T,
  322. SCH_TEXT_T,
  323. SCH_TEXTBOX_T } ) )
  324. || ( E_C::MoreThan( 1 ) && E_C::OnlyTypes( allTextTypes ) );
  325. auto toHLabelCondition = ( E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_LABEL_T,
  326. SCH_DIRECTIVE_LABEL_T,
  327. SCH_GLOBAL_LABEL_T,
  328. SCH_TEXT_T,
  329. SCH_TEXTBOX_T } ) )
  330. || ( E_C::MoreThan( 1 ) && E_C::OnlyTypes( allTextTypes ) );
  331. auto toGLabelCondition = ( E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_LABEL_T,
  332. SCH_DIRECTIVE_LABEL_T,
  333. SCH_HIER_LABEL_T,
  334. SCH_TEXT_T,
  335. SCH_TEXTBOX_T } ) )
  336. || ( E_C::MoreThan( 1 ) && E_C::OnlyTypes( allTextTypes ) );
  337. auto toTextCondition = ( E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_LABEL_T,
  338. SCH_DIRECTIVE_LABEL_T,
  339. SCH_GLOBAL_LABEL_T,
  340. SCH_HIER_LABEL_T,
  341. SCH_TEXTBOX_T } ) )
  342. || ( E_C::MoreThan( 1 ) && E_C::OnlyTypes( allTextTypes ) );
  343. auto toTextBoxCondition = ( E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_LABEL_T,
  344. SCH_DIRECTIVE_LABEL_T,
  345. SCH_GLOBAL_LABEL_T,
  346. SCH_HIER_LABEL_T,
  347. SCH_TEXT_T } ) )
  348. || ( E_C::MoreThan( 1 ) && E_C::OnlyTypes( allTextTypes ) );
  349. auto entryCondition = E_C::MoreThan( 0 ) && E_C::OnlyTypes( { SCH_BUS_WIRE_ENTRY_T,
  350. SCH_BUS_BUS_ENTRY_T} );
  351. auto singleSheetCondition = E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_SHEET_T } );
  352. auto makeSymbolUnitMenu =
  353. [&]( TOOL_INTERACTIVE* tool )
  354. {
  355. std::shared_ptr<SYMBOL_UNIT_MENU> menu = std::make_shared<SYMBOL_UNIT_MENU>();
  356. menu->SetTool( tool );
  357. tool->GetToolMenu().RegisterSubMenu( menu );
  358. return menu.get();
  359. };
  360. auto makePinFunctionMenu =
  361. [&]( TOOL_INTERACTIVE* tool )
  362. {
  363. std::shared_ptr<ALT_PIN_FUNCTION_MENU> menu = std::make_shared<ALT_PIN_FUNCTION_MENU>();
  364. menu->SetTool( tool );
  365. tool->GetToolMenu().RegisterSubMenu( menu );
  366. return menu.get();
  367. };
  368. auto makePinTricksMenu =
  369. [&]( TOOL_INTERACTIVE* tool )
  370. {
  371. std::shared_ptr<PIN_TRICKS_MENU> menu = std::make_shared<PIN_TRICKS_MENU>();
  372. menu->SetTool( tool );
  373. tool->GetToolMenu().RegisterSubMenu( menu );
  374. return menu.get();
  375. };
  376. auto makeTransformMenu =
  377. [&]()
  378. {
  379. CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( moveTool );
  380. menu->SetTitle( _( "Transform Selection" ) );
  381. menu->AddItem( EE_ACTIONS::rotateCCW, orientCondition );
  382. menu->AddItem( EE_ACTIONS::rotateCW, orientCondition );
  383. menu->AddItem( EE_ACTIONS::mirrorV, orientCondition );
  384. menu->AddItem( EE_ACTIONS::mirrorH, orientCondition );
  385. return menu;
  386. };
  387. auto makeAttributesMenu =
  388. [&]()
  389. {
  390. CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( moveTool );
  391. menu->SetTitle( _( "Attributes" ) );
  392. menu->AddItem( EE_ACTIONS::setExcludeFromSimulation, E_C::ShowAlways );
  393. menu->AddItem( EE_ACTIONS::unsetExcludeFromSimulation, E_C::ShowAlways );
  394. menu->AddItem( EE_ACTIONS::toggleExcludeFromSimulation, E_C::ShowAlways );
  395. menu->AddSeparator();
  396. menu->AddItem( EE_ACTIONS::setExcludeFromBOM, E_C::ShowAlways );
  397. menu->AddItem( EE_ACTIONS::unsetExcludeFromBOM, E_C::ShowAlways );
  398. menu->AddItem( EE_ACTIONS::toggleExcludeFromBOM, E_C::ShowAlways );
  399. menu->AddSeparator();
  400. menu->AddItem( EE_ACTIONS::setExcludeFromBoard, E_C::ShowAlways );
  401. menu->AddItem( EE_ACTIONS::unsetExcludeFromBoard, E_C::ShowAlways );
  402. menu->AddItem( EE_ACTIONS::toggleExcludeFromBoard, E_C::ShowAlways );
  403. menu->AddSeparator();
  404. menu->AddItem( EE_ACTIONS::setDNP, E_C::ShowAlways );
  405. menu->AddItem( EE_ACTIONS::unsetDNP, E_C::ShowAlways );
  406. menu->AddItem( EE_ACTIONS::toggleDNP, E_C::ShowAlways );
  407. return menu;
  408. };
  409. auto makeEditFieldsMenu =
  410. [&]()
  411. {
  412. CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( m_selectionTool );
  413. menu->SetTitle( _( "Edit Main Fields" ) );
  414. menu->AddItem( EE_ACTIONS::editReference, E_C::SingleSymbol, 200 );
  415. menu->AddItem( EE_ACTIONS::editValue, E_C::SingleSymbol, 200 );
  416. menu->AddItem( EE_ACTIONS::editFootprint, E_C::SingleSymbol, 200 );
  417. return menu;
  418. };
  419. auto makeConvertToMenu =
  420. [&]()
  421. {
  422. CONDITIONAL_MENU* menu = new CONDITIONAL_MENU( m_selectionTool );
  423. menu->SetTitle( _( "Change To" ) );
  424. menu->SetIcon( BITMAPS::right );
  425. menu->AddItem( EE_ACTIONS::toLabel, toLabelCondition );
  426. menu->AddItem( EE_ACTIONS::toCLabel, toCLabelCondition );
  427. menu->AddItem( EE_ACTIONS::toHLabel, toHLabelCondition );
  428. menu->AddItem( EE_ACTIONS::toGLabel, toGLabelCondition );
  429. menu->AddItem( EE_ACTIONS::toText, toTextCondition );
  430. menu->AddItem( EE_ACTIONS::toTextBox, toTextBoxCondition );
  431. return menu;
  432. };
  433. //
  434. // Add edit actions to the move tool menu
  435. //
  436. CONDITIONAL_MENU& moveMenu = moveTool->GetToolMenu().GetMenu();
  437. moveMenu.AddSeparator();
  438. moveMenu.AddMenu( makeSymbolUnitMenu( moveTool ), E_C::SingleMultiUnitSymbol, 1 );
  439. moveMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
  440. moveMenu.AddMenu( makeAttributesMenu(), E_C::HasType( SCH_SYMBOL_T ), 200 );
  441. moveMenu.AddItem( EE_ACTIONS::swap, SELECTION_CONDITIONS::MoreThan( 1 ), 200);
  442. moveMenu.AddItem( EE_ACTIONS::properties, propertiesCondition, 200 );
  443. moveMenu.AddMenu( makeEditFieldsMenu(), E_C::SingleSymbol, 200 );
  444. moveMenu.AddSeparator();
  445. moveMenu.AddItem( ACTIONS::cut, E_C::IdleSelection );
  446. moveMenu.AddItem( ACTIONS::copy, E_C::IdleSelection );
  447. moveMenu.AddItem( ACTIONS::doDelete, E_C::NotEmpty );
  448. moveMenu.AddItem( ACTIONS::duplicate, duplicateCondition );
  449. //
  450. // Add editing actions to the drawing tool menu
  451. //
  452. CONDITIONAL_MENU& drawMenu = drawingTools->GetToolMenu().GetMenu();
  453. drawMenu.AddItem( EE_ACTIONS::clearHighlight, haveHighlight && EE_CONDITIONS::Idle, 1 );
  454. drawMenu.AddSeparator( haveHighlight && EE_CONDITIONS::Idle, 1 );
  455. drawMenu.AddItem( EE_ACTIONS::enterSheet, sheetSelection && EE_CONDITIONS::Idle, 1 );
  456. drawMenu.AddSeparator( sheetSelection && EE_CONDITIONS::Idle, 1 );
  457. drawMenu.AddMenu( makeSymbolUnitMenu( drawingTools ), E_C::SingleMultiUnitSymbol, 1 );
  458. drawMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
  459. drawMenu.AddMenu( makeAttributesMenu(), E_C::HasType( SCH_SYMBOL_T ), 200 );
  460. drawMenu.AddItem( EE_ACTIONS::properties, propertiesCondition, 200 );
  461. drawMenu.AddMenu( makeEditFieldsMenu(), E_C::SingleSymbol, 200 );
  462. drawMenu.AddItem( EE_ACTIONS::autoplaceFields, autoplaceCondition, 200 );
  463. drawMenu.AddItem( EE_ACTIONS::editWithLibEdit, E_C::SingleSymbolOrPower && E_C::Idle, 200 );
  464. drawMenu.AddItem( EE_ACTIONS::toLabel, anyTextTool && E_C::Idle, 200 );
  465. drawMenu.AddItem( EE_ACTIONS::toHLabel, anyTextTool && E_C::Idle, 200 );
  466. drawMenu.AddItem( EE_ACTIONS::toGLabel, anyTextTool && E_C::Idle, 200 );
  467. drawMenu.AddItem( EE_ACTIONS::toText, anyTextTool && E_C::Idle, 200 );
  468. drawMenu.AddItem( EE_ACTIONS::toTextBox, anyTextTool && E_C::Idle, 200 );
  469. //
  470. // Add editing actions to the selection tool menu
  471. //
  472. CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
  473. selToolMenu.AddMenu( makeSymbolUnitMenu( m_selectionTool ), E_C::SingleMultiUnitSymbol, 1 );
  474. selToolMenu.AddMenu( makePinFunctionMenu( m_selectionTool ), E_C::SingleMultiFunctionPin, 1 );
  475. selToolMenu.AddMenu( makePinTricksMenu( m_selectionTool ), E_C::AllPins, 1 );
  476. selToolMenu.AddMenu( makeTransformMenu(), orientCondition, 200 );
  477. selToolMenu.AddMenu( makeAttributesMenu(), E_C::HasType( SCH_SYMBOL_T ), 200 );
  478. selToolMenu.AddItem( EE_ACTIONS::swap, SELECTION_CONDITIONS::MoreThan( 1 ), 200 );
  479. selToolMenu.AddItem( EE_ACTIONS::properties, propertiesCondition, 200 );
  480. selToolMenu.AddMenu( makeEditFieldsMenu(), E_C::SingleSymbol, 200 );
  481. selToolMenu.AddItem( EE_ACTIONS::autoplaceFields, autoplaceCondition, 200 );
  482. selToolMenu.AddItem( EE_ACTIONS::editWithLibEdit, E_C::SingleSymbolOrPower && E_C::Idle, 200 );
  483. selToolMenu.AddItem( EE_ACTIONS::changeSymbol, E_C::SingleSymbolOrPower, 200 );
  484. selToolMenu.AddItem( EE_ACTIONS::updateSymbol, E_C::SingleSymbolOrPower, 200 );
  485. selToolMenu.AddItem( EE_ACTIONS::changeSymbols, E_C::MultipleSymbolsOrPower, 200 );
  486. selToolMenu.AddItem( EE_ACTIONS::updateSymbols, E_C::MultipleSymbolsOrPower, 200 );
  487. selToolMenu.AddMenu( makeConvertToMenu(), toChangeCondition, 200 );
  488. selToolMenu.AddItem( EE_ACTIONS::cleanupSheetPins, sheetHasUndefinedPins, 250 );
  489. selToolMenu.AddSeparator( 300 );
  490. selToolMenu.AddItem( ACTIONS::cut, E_C::IdleSelection, 300 );
  491. selToolMenu.AddItem( ACTIONS::copy, E_C::IdleSelection, 300 );
  492. selToolMenu.AddItem( ACTIONS::paste, E_C::Idle, 300 );
  493. selToolMenu.AddItem( ACTIONS::pasteSpecial, E_C::Idle, 300 );
  494. selToolMenu.AddItem( ACTIONS::doDelete, E_C::NotEmpty, 300 );
  495. selToolMenu.AddItem( ACTIONS::duplicate, duplicateCondition, 300 );
  496. selToolMenu.AddSeparator( 400 );
  497. selToolMenu.AddItem( ACTIONS::selectAll, hasElements, 400 );
  498. selToolMenu.AddItem( ACTIONS::unselectAll, hasElements, 400 );
  499. return true;
  500. }
  501. const std::vector<KICAD_T> SCH_EDIT_TOOL::RotatableItems = {
  502. SCH_SHAPE_T,
  503. SCH_TEXT_T,
  504. SCH_TEXTBOX_T,
  505. SCH_LABEL_T,
  506. SCH_GLOBAL_LABEL_T,
  507. SCH_HIER_LABEL_T,
  508. SCH_DIRECTIVE_LABEL_T,
  509. SCH_FIELD_T,
  510. SCH_SYMBOL_T,
  511. SCH_SHEET_PIN_T,
  512. SCH_SHEET_T,
  513. SCH_BITMAP_T,
  514. SCH_BUS_BUS_ENTRY_T,
  515. SCH_BUS_WIRE_ENTRY_T,
  516. SCH_LINE_T,
  517. SCH_JUNCTION_T,
  518. SCH_NO_CONNECT_T
  519. };
  520. int SCH_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
  521. {
  522. bool clockwise = ( aEvent.Matches( EE_ACTIONS::rotateCW.MakeEvent() ) );
  523. EE_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems );
  524. if( selection.GetSize() == 0 )
  525. return 0;
  526. SCH_ITEM* head = nullptr;
  527. int principalItemCount = 0; // User-selected items (as opposed to connected wires)
  528. VECTOR2I rotPoint;
  529. bool moving = false;
  530. SCH_COMMIT localCommit( m_toolMgr );
  531. SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
  532. if( !commit )
  533. commit = &localCommit;
  534. for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
  535. {
  536. SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
  537. if( item->HasFlag( SELECTED_BY_DRAG ) )
  538. continue;
  539. principalItemCount++;
  540. if( !head )
  541. head = item;
  542. }
  543. if( head && head->IsMoving() )
  544. moving = true;
  545. if( principalItemCount == 1 )
  546. {
  547. if( moving && selection.HasReferencePoint() )
  548. rotPoint = selection.GetReferencePoint();
  549. else if( head->IsConnectable() )
  550. rotPoint = head->GetPosition();
  551. else
  552. rotPoint = m_frame->GetNearestHalfGridPosition( head->GetBoundingBox().GetCenter() );
  553. if( !moving )
  554. commit->Modify( head, m_frame->GetScreen() );
  555. switch( head->Type() )
  556. {
  557. case SCH_SYMBOL_T:
  558. {
  559. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( head );
  560. for( int i = 0; clockwise ? i < 3 : i < 1; ++i )
  561. symbol->Rotate( rotPoint );
  562. if( m_frame->eeconfig()->m_AutoplaceFields.enable )
  563. symbol->AutoAutoplaceFields( m_frame->GetScreen() );
  564. break;
  565. }
  566. case SCH_TEXT_T:
  567. case SCH_LABEL_T:
  568. case SCH_GLOBAL_LABEL_T:
  569. case SCH_HIER_LABEL_T:
  570. case SCH_DIRECTIVE_LABEL_T:
  571. {
  572. SCH_TEXT* textItem = static_cast<SCH_TEXT*>( head );
  573. textItem->Rotate90( clockwise );
  574. break;
  575. }
  576. case SCH_SHEET_PIN_T:
  577. {
  578. // Rotate pin within parent sheet
  579. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( head );
  580. SCH_SHEET* sheet = pin->GetParent();
  581. for( int i = 0; clockwise ? i < 3 : i < 1; ++i )
  582. pin->Rotate( sheet->GetBodyBoundingBox().GetCenter() );
  583. break;
  584. }
  585. case SCH_LINE_T:
  586. {
  587. SCH_LINE* line = static_cast<SCH_LINE*>( head );
  588. // Equal checks for both and neither. We need this because on undo
  589. // the item will have both flags cleared, but will be selected, so it is possible
  590. // for the user to get a selected line with neither endpoint selected. We
  591. // set flags to make sure Rotate() works when we call it.
  592. if( line->HasFlag( STARTPOINT ) == line->HasFlag( ENDPOINT ) )
  593. {
  594. line->SetFlags( STARTPOINT | ENDPOINT );
  595. // When we allow off grid items, the rotPoint should be set to the midpoint
  596. // of the line to allow rotation around the center, and the next if
  597. // should become an else-if
  598. }
  599. if( line->HasFlag( STARTPOINT ) )
  600. rotPoint = line->GetEndPoint();
  601. else if( line->HasFlag( ENDPOINT ) )
  602. rotPoint = line->GetStartPoint();
  603. }
  604. KI_FALLTHROUGH;
  605. case SCH_JUNCTION_T:
  606. case SCH_NO_CONNECT_T:
  607. case SCH_BUS_BUS_ENTRY_T:
  608. case SCH_BUS_WIRE_ENTRY_T:
  609. for( int i = 0; clockwise ? i < 3 : i < 1; ++i )
  610. head->Rotate( rotPoint );
  611. break;
  612. case SCH_FIELD_T:
  613. {
  614. SCH_FIELD* field = static_cast<SCH_FIELD*>( head );
  615. if( field->GetTextAngle().IsHorizontal() )
  616. field->SetTextAngle( ANGLE_VERTICAL );
  617. else
  618. field->SetTextAngle( ANGLE_HORIZONTAL );
  619. // Now that we're moving a field, they're no longer autoplaced.
  620. static_cast<SCH_ITEM*>( head->GetParent() )->ClearFieldsAutoplaced();
  621. break;
  622. }
  623. case SCH_SHAPE_T:
  624. case SCH_TEXTBOX_T:
  625. for( int i = 0; clockwise ? i < 3 : i < 1; ++i )
  626. head->Rotate( rotPoint );
  627. break;
  628. case SCH_BITMAP_T:
  629. for( int i = 0; clockwise ? i < 3 : i < 1; ++i )
  630. head->Rotate( rotPoint );
  631. // The bitmap is cached in Opengl: clear the cache to redraw
  632. getView()->RecacheAllItems();
  633. break;
  634. case SCH_SHEET_T:
  635. {
  636. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( head );
  637. rotPoint = m_frame->GetNearestHalfGridPosition( sheet->GetRotationCenter() );
  638. // Rotate the sheet on itself. Sheets do not have an anchor point.
  639. for( int i = 0; clockwise ? i < 3 : i < 1; ++i )
  640. sheet->Rotate( rotPoint );
  641. break;
  642. }
  643. default:
  644. UNIMPLEMENTED_FOR( head->GetClass() );
  645. }
  646. m_frame->UpdateItem( head, false, true );
  647. }
  648. else
  649. {
  650. if( moving && selection.HasReferencePoint() )
  651. rotPoint = selection.GetReferencePoint();
  652. else
  653. rotPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
  654. }
  655. for( EDA_ITEM* edaItem : selection )
  656. {
  657. SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
  658. // We've already rotated the user selected item if there was only one. We're just
  659. // here to rotate the ends of wires that were attached to it.
  660. if( principalItemCount == 1 && !item->HasFlag( SELECTED_BY_DRAG ) )
  661. continue;
  662. if( !moving )
  663. commit->Modify( item, m_frame->GetScreen() );
  664. for( int i = 0; clockwise ? i < 3 : i < 1; ++i )
  665. {
  666. if( item->Type() == SCH_LINE_T )
  667. {
  668. SCH_LINE* line = (SCH_LINE*) item;
  669. // If we are rotating more than one item, we do not have start/end
  670. // points separately selected
  671. if( item->HasFlag( STARTPOINT ) )
  672. line->RotateStart( rotPoint );
  673. if( item->HasFlag( ENDPOINT ) )
  674. line->RotateEnd( rotPoint );
  675. }
  676. else if( item->Type() == SCH_SHEET_PIN_T )
  677. {
  678. if( item->GetParent()->IsSelected() )
  679. {
  680. // parent will rotate us
  681. }
  682. else
  683. {
  684. // rotate within parent
  685. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
  686. SCH_SHEET* sheet = pin->GetParent();
  687. pin->Rotate( sheet->GetBodyBoundingBox().GetCenter() );
  688. }
  689. }
  690. else if( item->Type() == SCH_FIELD_T )
  691. {
  692. if( item->GetParent()->IsSelected() )
  693. {
  694. // parent will rotate us
  695. }
  696. else
  697. {
  698. SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
  699. field->Rotate( rotPoint );
  700. if( field->GetTextAngle().IsHorizontal() )
  701. field->SetTextAngle( ANGLE_VERTICAL );
  702. else
  703. field->SetTextAngle( ANGLE_HORIZONTAL );
  704. // Now that we're moving a field, they're no longer autoplaced.
  705. static_cast<SCH_ITEM*>( field->GetParent() )->ClearFieldsAutoplaced();
  706. }
  707. }
  708. else
  709. {
  710. item->Rotate( rotPoint );
  711. }
  712. }
  713. m_frame->UpdateItem( item, false, true );
  714. updateItem( item, true );
  715. }
  716. if( moving )
  717. {
  718. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  719. }
  720. else
  721. {
  722. EE_SELECTION selectionCopy = selection;
  723. if( selection.IsHover() )
  724. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  725. SCH_LINE_WIRE_BUS_TOOL* lwbTool = m_toolMgr->GetTool<SCH_LINE_WIRE_BUS_TOOL>();
  726. lwbTool->TrimOverLappingWires( commit, &selectionCopy );
  727. lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
  728. m_frame->SchematicCleanUp( commit );
  729. if( !localCommit.Empty() )
  730. localCommit.Push( _( "Rotate" ) );
  731. }
  732. return 0;
  733. }
  734. int SCH_EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
  735. {
  736. EE_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems );
  737. if( selection.GetSize() == 0 )
  738. return 0;
  739. bool vertical = ( aEvent.Matches( EE_ACTIONS::mirrorV.MakeEvent() ) );
  740. SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
  741. bool connections = false;
  742. bool moving = item->IsMoving();
  743. SCH_COMMIT localCommit( m_toolMgr );
  744. SCH_COMMIT* commit = dynamic_cast<SCH_COMMIT*>( aEvent.Commit() );
  745. if( !commit )
  746. commit = &localCommit;
  747. if( selection.GetSize() == 1 )
  748. {
  749. if( !moving )
  750. commit->Modify( item, m_frame->GetScreen() );
  751. switch( item->Type() )
  752. {
  753. case SCH_SYMBOL_T:
  754. {
  755. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  756. if( vertical )
  757. symbol->SetOrientation( SYM_MIRROR_X );
  758. else
  759. symbol->SetOrientation( SYM_MIRROR_Y );
  760. symbol->ClearFieldsAutoplaced();
  761. break;
  762. }
  763. case SCH_TEXT_T:
  764. case SCH_LABEL_T:
  765. case SCH_GLOBAL_LABEL_T:
  766. case SCH_HIER_LABEL_T:
  767. case SCH_DIRECTIVE_LABEL_T:
  768. {
  769. SCH_TEXT* textItem = static_cast<SCH_TEXT*>( item );
  770. textItem->MirrorSpinStyle( !vertical );
  771. break;
  772. }
  773. case SCH_SHEET_PIN_T:
  774. {
  775. // mirror within parent sheet
  776. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
  777. SCH_SHEET* sheet = pin->GetParent();
  778. if( vertical )
  779. pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
  780. else
  781. pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
  782. break;
  783. }
  784. case SCH_FIELD_T:
  785. {
  786. SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
  787. if( vertical )
  788. field->SetVertJustify( TO_VJUSTIFY( -field->GetVertJustify() ) );
  789. else
  790. field->SetHorizJustify( TO_HJUSTIFY( -field->GetHorizJustify() ) );
  791. // Now that we're re-justifying a field, they're no longer autoplaced.
  792. static_cast<SCH_ITEM*>( field->GetParent() )->ClearFieldsAutoplaced();
  793. break;
  794. }
  795. case SCH_BITMAP_T:
  796. if( vertical )
  797. item->MirrorVertically( item->GetPosition().y );
  798. else
  799. item->MirrorHorizontally( item->GetPosition().x );
  800. // The bitmap is cached in Opengl: clear the cache to redraw
  801. getView()->RecacheAllItems();
  802. break;
  803. case SCH_SHEET_T:
  804. {
  805. // Mirror the sheet on itself. Sheets do not have a anchor point.
  806. VECTOR2I mirrorPoint = m_frame->GetNearestHalfGridPosition( item->GetBoundingBox().Centre() );
  807. if( vertical )
  808. item->MirrorVertically( mirrorPoint.y );
  809. else
  810. item->MirrorHorizontally( mirrorPoint.x );
  811. break;
  812. }
  813. default:
  814. if( vertical )
  815. item->MirrorVertically( item->GetPosition().y );
  816. else
  817. item->MirrorHorizontally( item->GetPosition().x );
  818. break;
  819. }
  820. connections = item->IsConnectable();
  821. m_frame->UpdateItem( item, false, true );
  822. }
  823. else if( selection.GetSize() > 1 )
  824. {
  825. VECTOR2I mirrorPoint = m_frame->GetNearestHalfGridPosition( selection.GetCenter() );
  826. for( EDA_ITEM* edaItem : selection )
  827. {
  828. item = static_cast<SCH_ITEM*>( edaItem );
  829. if( !moving )
  830. commit->Modify( item, m_frame->GetScreen() );
  831. if( item->Type() == SCH_SHEET_PIN_T )
  832. {
  833. if( item->GetParent()->IsSelected() )
  834. {
  835. // parent will mirror us
  836. }
  837. else
  838. {
  839. // mirror within parent sheet
  840. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
  841. SCH_SHEET* sheet = pin->GetParent();
  842. if( vertical )
  843. pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
  844. else
  845. pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
  846. }
  847. }
  848. else if( item->Type() == SCH_FIELD_T )
  849. {
  850. SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
  851. if( vertical )
  852. field->SetVertJustify( TO_VJUSTIFY( -field->GetVertJustify() ) );
  853. else
  854. field->SetHorizJustify( TO_HJUSTIFY( -field->GetHorizJustify() ) );
  855. // Now that we're re-justifying a field, they're no longer autoplaced.
  856. static_cast<SCH_ITEM*>( field->GetParent() )->ClearFieldsAutoplaced();
  857. }
  858. else
  859. {
  860. if( vertical )
  861. item->MirrorVertically( mirrorPoint.y );
  862. else
  863. item->MirrorHorizontally( mirrorPoint.x );
  864. }
  865. connections |= item->IsConnectable();
  866. m_frame->UpdateItem( item, false, true );
  867. }
  868. }
  869. // Update R-Tree for modified items
  870. for( EDA_ITEM* selected : selection )
  871. updateItem( selected, true );
  872. if( item->IsMoving() )
  873. {
  874. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  875. }
  876. else
  877. {
  878. EE_SELECTION selectionCopy = selection;
  879. if( selection.IsHover() )
  880. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  881. if( connections )
  882. {
  883. SCH_LINE_WIRE_BUS_TOOL* lwbTool = m_toolMgr->GetTool<SCH_LINE_WIRE_BUS_TOOL>();
  884. lwbTool->TrimOverLappingWires( commit, &selectionCopy );
  885. lwbTool->AddJunctionsIfNeeded( commit, &selectionCopy );
  886. m_frame->SchematicCleanUp( commit );
  887. }
  888. if( !localCommit.Empty() )
  889. localCommit.Push( _( "Mirror" ) );
  890. }
  891. return 0;
  892. }
  893. const std::vector<KICAD_T> swappableItems = {
  894. SCH_SHAPE_T,
  895. SCH_TEXT_T,
  896. SCH_TEXTBOX_T,
  897. SCH_LABEL_T,
  898. SCH_SHEET_PIN_T,
  899. SCH_GLOBAL_LABEL_T,
  900. SCH_HIER_LABEL_T,
  901. SCH_DIRECTIVE_LABEL_T,
  902. SCH_FIELD_T,
  903. SCH_SYMBOL_T,
  904. SCH_SHEET_T,
  905. SCH_BITMAP_T,
  906. SCH_JUNCTION_T,
  907. SCH_NO_CONNECT_T
  908. };
  909. int SCH_EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
  910. {
  911. EE_SELECTION& selection = m_selectionTool->RequestSelection( swappableItems );
  912. std::vector<EDA_ITEM*> sorted = selection.GetItemsSortedBySelectionOrder();
  913. // Sheet pins are special, we need to make sure if we have any sheet pins,
  914. // that we only have sheet pins, and that they have the same parent
  915. if( selection.CountType( SCH_SHEET_PIN_T ) > 0 )
  916. {
  917. if( !selection.OnlyContains( { SCH_SHEET_PIN_T } ) )
  918. return 0;
  919. SCH_SHEET_PIN* firstPin = static_cast<SCH_SHEET_PIN*>( selection.Front() );
  920. SCH_SHEET* parent = firstPin->GetParent();
  921. for( EDA_ITEM* item : selection )
  922. {
  923. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
  924. if( pin->GetParent() != parent )
  925. return 0;
  926. }
  927. }
  928. if( selection.Size() < 2 )
  929. return 0;
  930. bool isMoving = selection.Front()->IsMoving();
  931. bool appendUndo = isMoving;
  932. bool connections = false;
  933. SCH_SCREEN* screen = this->m_frame->GetScreen();
  934. for( size_t i = 0; i < sorted.size() - 1; i++ )
  935. {
  936. SCH_ITEM* a = static_cast<SCH_ITEM*>( sorted[i] );
  937. SCH_ITEM* b = static_cast<SCH_ITEM*>( sorted[( i + 1 ) % sorted.size()] );
  938. VECTOR2I aPos = a->GetPosition(), bPos = b->GetPosition();
  939. std::swap( aPos, bPos );
  940. saveCopyInUndoList( a, UNDO_REDO::CHANGED, appendUndo );
  941. appendUndo = true;
  942. saveCopyInUndoList( b, UNDO_REDO::CHANGED, appendUndo );
  943. // Sheet pins need to have their sides swapped before we change their
  944. // positions
  945. if( a->Type() == SCH_SHEET_PIN_T )
  946. {
  947. SCH_SHEET_PIN* aPin = static_cast<SCH_SHEET_PIN*>( a );
  948. SCH_SHEET_PIN* bPin = static_cast<SCH_SHEET_PIN*>( b );
  949. SHEET_SIDE aSide = aPin->GetSide(), bSide = bPin->GetSide();
  950. std::swap( aSide, bSide );
  951. aPin->SetSide( aSide );
  952. bPin->SetSide( bSide );
  953. }
  954. a->SetPosition( aPos );
  955. b->SetPosition( bPos );
  956. if( a->Type() == b->Type() )
  957. {
  958. switch( a->Type() )
  959. {
  960. case SCH_LABEL_T:
  961. case SCH_GLOBAL_LABEL_T:
  962. case SCH_HIER_LABEL_T:
  963. case SCH_DIRECTIVE_LABEL_T:
  964. m_frame->AutoRotateItem( screen, a );
  965. m_frame->AutoRotateItem( screen, b );
  966. break;
  967. case SCH_SYMBOL_T:
  968. {
  969. SCH_SYMBOL* aSymbol = static_cast<SCH_SYMBOL*>( a );
  970. SCH_SYMBOL* bSymbol = static_cast<SCH_SYMBOL*>( b );
  971. int aOrient = aSymbol->GetOrientation(), bOrient = bSymbol->GetOrientation();
  972. std::swap( aOrient, bOrient );
  973. aSymbol->SetOrientation( aOrient );
  974. bSymbol->SetOrientation( bOrient );
  975. break;
  976. }
  977. default: break;
  978. }
  979. }
  980. connections |= a->IsConnectable();
  981. connections |= b->IsConnectable();
  982. m_frame->UpdateItem( a, false, true );
  983. m_frame->UpdateItem( b, false, true );
  984. }
  985. // Update R-Tree for modified items
  986. for( EDA_ITEM* selected : selection )
  987. updateItem( selected, true );
  988. if( isMoving )
  989. {
  990. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  991. }
  992. else
  993. {
  994. if( selection.IsHover() )
  995. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  996. if( connections )
  997. m_frame->TestDanglingEnds();
  998. m_frame->OnModify();
  999. }
  1000. return 0;
  1001. }
  1002. int SCH_EDIT_TOOL::RepeatDrawItem( const TOOL_EVENT& aEvent )
  1003. {
  1004. const std::vector<std::unique_ptr<SCH_ITEM>>& sourceItems = m_frame->GetRepeatItems();
  1005. if( sourceItems.empty() )
  1006. return 0;
  1007. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1008. SCH_COMMIT commit( m_toolMgr );
  1009. EE_SELECTION newItems;
  1010. for( const std::unique_ptr<SCH_ITEM>& item : sourceItems )
  1011. {
  1012. SCH_ITEM* newItem = item->Duplicate();
  1013. EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
  1014. bool restore_state = false;
  1015. if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( newItem ) )
  1016. {
  1017. // If incrementing tries to go below zero, tell user why the value is repeated
  1018. if( !label->IncrementLabel( cfg->m_Drawing.repeat_label_increment ) )
  1019. m_frame->ShowInfoBarWarning( _( "Label value cannot go below zero" ), true );
  1020. }
  1021. // If cloning a symbol then put into 'move' mode.
  1022. if( newItem->Type() == SCH_SYMBOL_T )
  1023. {
  1024. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( true );
  1025. newItem->Move( cursorPos - newItem->GetPosition() );
  1026. }
  1027. else
  1028. {
  1029. newItem->Move( VECTOR2I( schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_x ),
  1030. schIUScale.MilsToIU( cfg->m_Drawing.default_repeat_offset_y ) ) );
  1031. }
  1032. // If cloning a sheet, check that we aren't going to create recursion
  1033. if( newItem->Type() == SCH_SHEET_T )
  1034. {
  1035. SCH_SHEET_PATH* currentSheet = &m_frame->GetCurrentSheet();
  1036. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( newItem );
  1037. if( m_frame->CheckSheetForRecursion( sheet, currentSheet ) )
  1038. {
  1039. // Clear out the filename so that the user can pick a new one
  1040. sheet->SetFileName( wxEmptyString );
  1041. sheet->GetScreen()->SetFileName( wxEmptyString );
  1042. restore_state = !m_frame->EditSheetProperties( sheet, currentSheet, nullptr );
  1043. }
  1044. }
  1045. m_toolMgr->RunAction<EDA_ITEM*>( EE_ACTIONS::addItemToSel, newItem );
  1046. newItem->SetFlags( IS_NEW );
  1047. m_frame->AddToScreen( newItem, m_frame->GetScreen() );
  1048. commit.Added( newItem, m_frame->GetScreen() );
  1049. if( newItem->Type() == SCH_SYMBOL_T )
  1050. {
  1051. EESCHEMA_SETTINGS::PANEL_ANNOTATE& annotate = m_frame->eeconfig()->m_AnnotatePanel;
  1052. SCHEMATIC_SETTINGS& projSettings = m_frame->Schematic().Settings();
  1053. int annotateStartNum = projSettings.m_AnnotateStartNum;
  1054. if( annotate.automatic )
  1055. {
  1056. static_cast<SCH_SYMBOL*>( newItem )->ClearAnnotation( nullptr, false );
  1057. NULL_REPORTER reporter;
  1058. m_frame->AnnotateSymbols( &commit, ANNOTATE_SELECTION,
  1059. (ANNOTATE_ORDER_T) annotate.sort_order,
  1060. (ANNOTATE_ALGO_T) annotate.method, true /* recursive */,
  1061. annotateStartNum, false, false, reporter );
  1062. }
  1063. restore_state = !m_toolMgr->RunSynchronousAction( EE_ACTIONS::move, &commit );
  1064. }
  1065. if( restore_state )
  1066. {
  1067. commit.Revert();
  1068. }
  1069. else
  1070. {
  1071. newItems.Add( newItem );
  1072. SCH_LINE_WIRE_BUS_TOOL* lwbTool = m_toolMgr->GetTool<SCH_LINE_WIRE_BUS_TOOL>();
  1073. lwbTool->TrimOverLappingWires( &commit, &newItems );
  1074. lwbTool->AddJunctionsIfNeeded( &commit, &newItems );
  1075. m_frame->SchematicCleanUp( &commit );
  1076. commit.Push( _( "Repeat Item" ) );
  1077. }
  1078. }
  1079. if( !newItems.Empty() )
  1080. m_frame->SaveCopyForRepeatItem( static_cast<SCH_ITEM*>( newItems[0] ) );
  1081. for( size_t ii = 1; ii < newItems.GetSize(); ++ii )
  1082. m_frame->AddCopyForRepeatItem( static_cast<SCH_ITEM*>( newItems[ii] ) );
  1083. return 0;
  1084. }
  1085. static std::vector<KICAD_T> deletableItems =
  1086. {
  1087. SCH_MARKER_T,
  1088. SCH_JUNCTION_T,
  1089. SCH_LINE_T,
  1090. SCH_BUS_BUS_ENTRY_T,
  1091. SCH_BUS_WIRE_ENTRY_T,
  1092. SCH_SHAPE_T,
  1093. SCH_TEXT_T,
  1094. SCH_TEXTBOX_T,
  1095. SCH_LABEL_T,
  1096. SCH_GLOBAL_LABEL_T,
  1097. SCH_HIER_LABEL_T,
  1098. SCH_DIRECTIVE_LABEL_T,
  1099. SCH_NO_CONNECT_T,
  1100. SCH_SHEET_T,
  1101. SCH_SHEET_PIN_T,
  1102. SCH_SYMBOL_T,
  1103. SCH_FIELD_T, // Will be hidden
  1104. SCH_BITMAP_T
  1105. };
  1106. int SCH_EDIT_TOOL::DoDelete( const TOOL_EVENT& aEvent )
  1107. {
  1108. SCH_SCREEN* screen = m_frame->GetScreen();
  1109. std::deque<EDA_ITEM*> items = m_selectionTool->RequestSelection( deletableItems ).GetItems();
  1110. SCH_COMMIT commit( m_toolMgr );
  1111. std::vector<VECTOR2I> pts;
  1112. bool updateHierarchy = false;
  1113. if( items.empty() )
  1114. return 0;
  1115. // Don't leave a freed pointer in the selection
  1116. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1117. for( EDA_ITEM* item : items )
  1118. item->ClearFlags( STRUCT_DELETED );
  1119. for( EDA_ITEM* item : items )
  1120. {
  1121. SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item );
  1122. if( !sch_item )
  1123. continue;
  1124. if( sch_item->IsConnectable() )
  1125. {
  1126. std::vector<VECTOR2I> tmp_pts = sch_item->GetConnectionPoints();
  1127. pts.insert( pts.end(), tmp_pts.begin(), tmp_pts.end() );
  1128. }
  1129. if( sch_item->Type() == SCH_JUNCTION_T )
  1130. {
  1131. sch_item->SetFlags( STRUCT_DELETED );
  1132. // clean up junctions at the end
  1133. }
  1134. else if( sch_item->Type() == SCH_SHEET_PIN_T )
  1135. {
  1136. SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) sch_item;
  1137. SCH_SHEET* sheet = pin->GetParent();
  1138. if( !alg::contains( items, sheet ) )
  1139. {
  1140. commit.Modify( sheet, m_frame->GetScreen() );
  1141. sheet->RemovePin( pin );
  1142. }
  1143. }
  1144. else if( sch_item->Type() == SCH_FIELD_T )
  1145. {
  1146. commit.Modify( item, m_frame->GetScreen() );
  1147. static_cast<SCH_FIELD*>( sch_item )->SetVisible( false );
  1148. }
  1149. else
  1150. {
  1151. sch_item->SetFlags( STRUCT_DELETED );
  1152. commit.Remove( item, m_frame->GetScreen() );
  1153. updateHierarchy |= ( sch_item->Type() == SCH_SHEET_T );
  1154. }
  1155. }
  1156. for( const VECTOR2I& point : pts )
  1157. {
  1158. SCH_ITEM* junction = screen->GetItem( point, 0, SCH_JUNCTION_T );
  1159. if( !junction )
  1160. continue;
  1161. if( junction->HasFlag( STRUCT_DELETED ) || !screen->IsExplicitJunction( point ) )
  1162. m_frame->DeleteJunction( &commit, junction );
  1163. }
  1164. commit.Push( _( "Delete" ) );
  1165. if( updateHierarchy )
  1166. m_frame->UpdateHierarchyNavigator();
  1167. return 0;
  1168. }
  1169. #define HITTEST_THRESHOLD_PIXELS 5
  1170. int SCH_EDIT_TOOL::InteractiveDelete( const TOOL_EVENT& aEvent )
  1171. {
  1172. PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
  1173. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1174. m_pickerItem = nullptr;
  1175. // Deactivate other tools; particularly important if another PICKER is currently running
  1176. Activate();
  1177. picker->SetCursor( KICURSOR::REMOVE );
  1178. picker->SetSnapping( false );
  1179. picker->SetClickHandler(
  1180. [this]( const VECTOR2D& aPosition ) -> bool
  1181. {
  1182. if( m_pickerItem )
  1183. {
  1184. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  1185. selectionTool->UnbrightenItem( m_pickerItem );
  1186. selectionTool->AddItemToSel( m_pickerItem, true /*quiet mode*/ );
  1187. m_toolMgr->RunAction( ACTIONS::doDelete );
  1188. m_pickerItem = nullptr;
  1189. }
  1190. return true;
  1191. } );
  1192. picker->SetMotionHandler(
  1193. [this]( const VECTOR2D& aPos )
  1194. {
  1195. EE_COLLECTOR collector;
  1196. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  1197. collector.Collect( m_frame->GetScreen(), deletableItems, aPos );
  1198. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  1199. selectionTool->GuessSelectionCandidates( collector, aPos );
  1200. EDA_ITEM* item = collector.GetCount() == 1 ? collector[ 0 ] : nullptr;
  1201. if( m_pickerItem != item )
  1202. {
  1203. if( m_pickerItem )
  1204. selectionTool->UnbrightenItem( m_pickerItem );
  1205. m_pickerItem = item;
  1206. if( m_pickerItem )
  1207. selectionTool->BrightenItem( m_pickerItem );
  1208. }
  1209. } );
  1210. picker->SetFinalizeHandler(
  1211. [this]( const int& aFinalState )
  1212. {
  1213. if( m_pickerItem )
  1214. m_toolMgr->GetTool<EE_SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
  1215. // Wake the selection tool after exiting to ensure the cursor gets updated
  1216. m_toolMgr->PostAction( EE_ACTIONS::selectionActivate );
  1217. } );
  1218. m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
  1219. return 0;
  1220. }
  1221. void SCH_EDIT_TOOL::editFieldText( SCH_FIELD* aField )
  1222. {
  1223. KICAD_T parentType = aField->GetParent() ? aField->GetParent()->Type() : SCHEMATIC_T;
  1224. SCH_COMMIT commit( m_toolMgr );
  1225. // Save old symbol in undo list if not already in edit, or moving.
  1226. if( aField->GetEditFlags() == 0 ) // i.e. not edited, or moved
  1227. commit.Modify( aField, m_frame->GetScreen() );
  1228. if( parentType == SCH_SYMBOL_T && aField->GetId() == REFERENCE_FIELD )
  1229. static_cast<SCH_ITEM*>( aField->GetParent() )->SetConnectivityDirty();
  1230. wxString caption;
  1231. // Use title caps for mandatory fields. "Edit Sheet name Field" looks dorky.
  1232. if( parentType == SCH_SYMBOL_T && aField->GetId() >= 0 && aField->GetId() < MANDATORY_FIELDS )
  1233. {
  1234. wxString translated_fieldname;
  1235. translated_fieldname = TEMPLATE_FIELDNAME::GetDefaultFieldName( aField->GetId(),
  1236. DO_TRANSLATE );
  1237. caption.Printf( _( "Edit %s Field" ), TitleCaps( translated_fieldname ) );
  1238. }
  1239. else if( parentType == SCH_SHEET_T && aField->GetId() < SHEET_MANDATORY_FIELDS )
  1240. caption.Printf( _( "Edit %s Field" ), TitleCaps( aField->GetName() ) );
  1241. else
  1242. caption.Printf( _( "Edit '%s' Field" ), aField->GetName() );
  1243. DIALOG_SCH_FIELD_PROPERTIES dlg( m_frame, caption, aField );
  1244. // The footprint field dialog can invoke a KIWAY_PLAYER so we must use a quasi-modal
  1245. if( dlg.ShowQuasiModal() != wxID_OK )
  1246. return;
  1247. dlg.UpdateField( &commit, aField, &m_frame->GetCurrentSheet() );
  1248. if( m_frame->eeconfig()->m_AutoplaceFields.enable || parentType == SCH_SHEET_T )
  1249. static_cast<SCH_ITEM*>( aField->GetParent() )->AutoAutoplaceFields( m_frame->GetScreen() );
  1250. if( !commit.Empty() )
  1251. commit.Push( caption, SKIP_CONNECTIVITY );
  1252. }
  1253. int SCH_EDIT_TOOL::EditField( const TOOL_EVENT& aEvent )
  1254. {
  1255. EE_SELECTION sel;
  1256. if( aEvent.IsAction( &EE_ACTIONS::editReference ) )
  1257. sel = m_selectionTool->RequestSelection( { SCH_FIELD_LOCATE_REFERENCE_T, SCH_SYMBOL_T } );
  1258. else if( aEvent.IsAction( &EE_ACTIONS::editValue ) )
  1259. sel = m_selectionTool->RequestSelection( { SCH_FIELD_LOCATE_VALUE_T, SCH_SYMBOL_T } );
  1260. else if( aEvent.IsAction( &EE_ACTIONS::editFootprint ) )
  1261. sel = m_selectionTool->RequestSelection( { SCH_FIELD_LOCATE_FOOTPRINT_T, SCH_SYMBOL_T } );
  1262. if( sel.Size() != 1 )
  1263. return 0;
  1264. bool clearSelection = sel.IsHover();
  1265. EDA_ITEM* item = sel.Front();
  1266. if( item->Type() == SCH_SYMBOL_T )
  1267. {
  1268. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  1269. if( aEvent.IsAction( &EE_ACTIONS::editReference ) )
  1270. {
  1271. editFieldText( symbol->GetField( REFERENCE_FIELD ) );
  1272. }
  1273. else if( aEvent.IsAction( &EE_ACTIONS::editValue ) )
  1274. {
  1275. editFieldText( symbol->GetField( VALUE_FIELD ) );
  1276. }
  1277. else if( aEvent.IsAction( &EE_ACTIONS::editFootprint ) )
  1278. {
  1279. if( !symbol->IsPower() )
  1280. editFieldText( symbol->GetField( FOOTPRINT_FIELD ) );
  1281. }
  1282. }
  1283. else if( item->Type() == SCH_FIELD_T )
  1284. {
  1285. SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
  1286. editFieldText( field );
  1287. if( !field->IsVisible() )
  1288. clearSelection = true;
  1289. }
  1290. if( clearSelection )
  1291. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1292. return 0;
  1293. }
  1294. int SCH_EDIT_TOOL::AutoplaceFields( const TOOL_EVENT& aEvent )
  1295. {
  1296. EE_SELECTION& selection = m_selectionTool->RequestSelection( RotatableItems );
  1297. SCH_COMMIT commit( m_toolMgr );
  1298. SCH_ITEM* head = static_cast<SCH_ITEM*>( selection.Front() );
  1299. bool moving = head && head->IsMoving();
  1300. if( selection.Empty() )
  1301. return 0;
  1302. std::vector<SCH_ITEM*> autoplaceItems;
  1303. for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
  1304. {
  1305. SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
  1306. if( item->IsType( EE_COLLECTOR::FieldOwners ) )
  1307. autoplaceItems.push_back( item );
  1308. else if( item->GetParent() && item->GetParent()->IsType( EE_COLLECTOR::FieldOwners ) )
  1309. autoplaceItems.push_back( static_cast<SCH_ITEM*>( item->GetParent() ) );
  1310. }
  1311. for( SCH_ITEM* sch_item : autoplaceItems )
  1312. {
  1313. if( !moving && !sch_item->IsNew() )
  1314. commit.Modify( sch_item, m_frame->GetScreen() );
  1315. sch_item->AutoplaceFields( m_frame->GetScreen(), /* aManual */ true );
  1316. updateItem( sch_item, true );
  1317. }
  1318. if( moving )
  1319. {
  1320. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  1321. }
  1322. else
  1323. {
  1324. if( !commit.Empty() )
  1325. commit.Push( _( "Autoplace Fields" ) );
  1326. if( selection.IsHover() )
  1327. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1328. }
  1329. return 0;
  1330. }
  1331. int SCH_EDIT_TOOL::ChangeSymbols( const TOOL_EVENT& aEvent )
  1332. {
  1333. SCH_SYMBOL* selectedSymbol = nullptr;
  1334. EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
  1335. if( !selection.Empty() )
  1336. selectedSymbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
  1337. DIALOG_CHANGE_SYMBOLS::MODE mode = DIALOG_CHANGE_SYMBOLS::MODE::UPDATE;
  1338. if( aEvent.IsAction( &EE_ACTIONS::changeSymbol )
  1339. || aEvent.IsAction( &EE_ACTIONS::changeSymbols ) )
  1340. {
  1341. mode = DIALOG_CHANGE_SYMBOLS::MODE::CHANGE;
  1342. }
  1343. DIALOG_CHANGE_SYMBOLS dlg( m_frame, selectedSymbol, mode );
  1344. dlg.ShowQuasiModal();
  1345. return 0;
  1346. }
  1347. int SCH_EDIT_TOOL::ChangeBodyStyle( const TOOL_EVENT& aEvent )
  1348. {
  1349. EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
  1350. if( selection.Empty() )
  1351. return 0;
  1352. SCH_SYMBOL* symbol = (SCH_SYMBOL*) selection.Front();
  1353. if( aEvent.IsAction( &EE_ACTIONS::showDeMorganStandard )
  1354. && symbol->GetBodyStyle() == LIB_ITEM::BODY_STYLE::BASE )
  1355. {
  1356. return 0;
  1357. }
  1358. if( aEvent.IsAction( &EE_ACTIONS::showDeMorganAlternate )
  1359. && symbol->GetBodyStyle() == LIB_ITEM::BODY_STYLE::DEMORGAN )
  1360. {
  1361. return 0;
  1362. }
  1363. SCH_COMMIT commit( m_toolMgr );
  1364. if( !symbol->IsNew() )
  1365. commit.Modify( symbol, m_frame->GetScreen() );
  1366. m_frame->FlipBodyStyle( symbol );
  1367. if( symbol->IsNew() )
  1368. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  1369. // TODO: 9.0 It would be better as "Change Body Style", but we're past string freeze so
  1370. // this (existing) string will have to do....
  1371. if( !commit.Empty() )
  1372. commit.Push( _( "Convert Symbol" ) );
  1373. if( selection.IsHover() )
  1374. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1375. return 0;
  1376. }
  1377. int SCH_EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
  1378. {
  1379. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::EditableItems );
  1380. bool clearSelection = selection.IsHover();
  1381. if( selection.Empty() )
  1382. {
  1383. if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
  1384. {
  1385. DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
  1386. VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
  1387. if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
  1388. m_toolMgr->PostAction( ACTIONS::pageSettings );
  1389. }
  1390. return 0;
  1391. }
  1392. EDA_ITEM* curr_item = selection.Front();
  1393. switch( curr_item->Type() )
  1394. {
  1395. case SCH_LINE_T:
  1396. case SCH_BUS_WIRE_ENTRY_T:
  1397. case SCH_JUNCTION_T:
  1398. break;
  1399. default:
  1400. if( selection.Size() > 1 )
  1401. return 0;
  1402. break;
  1403. }
  1404. switch( curr_item->Type() )
  1405. {
  1406. case SCH_SYMBOL_T:
  1407. {
  1408. int retval;
  1409. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( curr_item );
  1410. // This needs to be scoped so the dialog destructor removes blocking status
  1411. // before we launch the next dialog.
  1412. {
  1413. DIALOG_SYMBOL_PROPERTIES symbolPropsDialog( m_frame, symbol );
  1414. // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
  1415. // frame. Therefore this dialog as a modal frame parent, MUST be run under
  1416. // quasimodal mode for the quasimodal frame support to work. So don't use
  1417. // the QUASIMODAL macros here.
  1418. retval = symbolPropsDialog.ShowQuasiModal();
  1419. }
  1420. if( retval == SYMBOL_PROPS_EDIT_OK )
  1421. {
  1422. if( m_frame->eeconfig()->m_AutoplaceFields.enable )
  1423. symbol->AutoAutoplaceFields( m_frame->GetScreen() );
  1424. m_frame->OnModify();
  1425. }
  1426. else if( retval == SYMBOL_PROPS_EDIT_SCHEMATIC_SYMBOL )
  1427. {
  1428. auto editor = (SYMBOL_EDIT_FRAME*) m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR,
  1429. true );
  1430. wxCHECK( editor, 0 );
  1431. if( wxWindow* blocking_win = editor->Kiway().GetBlockingDialog() )
  1432. blocking_win->Close( true );
  1433. // The broken library symbol link indicator cannot be edited.
  1434. if( symbol->IsMissingLibSymbol() )
  1435. return 0;
  1436. editor->LoadSymbolFromSchematic( symbol );
  1437. editor->Show( true );
  1438. editor->Raise();
  1439. }
  1440. else if( retval == SYMBOL_PROPS_EDIT_LIBRARY_SYMBOL )
  1441. {
  1442. auto editor = (SYMBOL_EDIT_FRAME*) m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR,
  1443. true );
  1444. wxCHECK( editor, 0 );
  1445. if( wxWindow* blocking_win = editor->Kiway().GetBlockingDialog() )
  1446. blocking_win->Close( true );
  1447. editor->LoadSymbol( symbol->GetLibId(), symbol->GetUnit(), symbol->GetBodyStyle() );
  1448. editor->Show( true );
  1449. editor->Raise();
  1450. }
  1451. else if( retval == SYMBOL_PROPS_WANT_UPDATE_SYMBOL )
  1452. {
  1453. DIALOG_CHANGE_SYMBOLS dlg( m_frame, symbol, DIALOG_CHANGE_SYMBOLS::MODE::UPDATE );
  1454. dlg.ShowQuasiModal();
  1455. }
  1456. else if( retval == SYMBOL_PROPS_WANT_EXCHANGE_SYMBOL )
  1457. {
  1458. DIALOG_CHANGE_SYMBOLS dlg( m_frame, symbol, DIALOG_CHANGE_SYMBOLS::MODE::CHANGE );
  1459. dlg.ShowQuasiModal();
  1460. }
  1461. break;
  1462. }
  1463. case SCH_SHEET_T:
  1464. {
  1465. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( curr_item );
  1466. bool doClearAnnotation;
  1467. bool doRefresh = false;
  1468. // Keep track of existing sheet paths. EditSheet() can modify this list.
  1469. // Note that we use the validity checking/repairing version here just to make sure
  1470. // we've got a valid hierarchy to begin with.
  1471. SCH_SHEET_LIST originalHierarchy( &m_frame->Schematic().Root(), true );
  1472. doRefresh = m_frame->EditSheetProperties( sheet, &m_frame->GetCurrentSheet(),
  1473. &doClearAnnotation );
  1474. // If the sheet file is changed and new sheet contents are loaded then we have to
  1475. // clear the annotations on the new content (as it may have been set from some other
  1476. // sheet path reference)
  1477. if( doClearAnnotation )
  1478. {
  1479. SCH_SCREENS screensList( &m_frame->Schematic().Root() );
  1480. // We clear annotation of new sheet paths here:
  1481. screensList.ClearAnnotationOfNewSheetPaths( originalHierarchy );
  1482. // Clear annotation of g_CurrentSheet itself, because its sheetpath is not a new
  1483. // path, but symbols managed by its sheet path must have their annotation cleared
  1484. // because they are new:
  1485. sheet->GetScreen()->ClearAnnotation( &m_frame->GetCurrentSheet(), false );
  1486. }
  1487. if( doRefresh )
  1488. {
  1489. m_frame->GetCanvas()->Refresh();
  1490. m_frame->UpdateHierarchyNavigator();
  1491. }
  1492. break;
  1493. }
  1494. case SCH_SHEET_PIN_T:
  1495. {
  1496. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( curr_item );
  1497. DIALOG_SHEET_PIN_PROPERTIES dlg( m_frame, pin );
  1498. // QuasiModal required for help dialog
  1499. if( dlg.ShowQuasiModal() == wxID_OK )
  1500. m_frame->OnModify();
  1501. break;
  1502. }
  1503. case SCH_TEXT_T:
  1504. case SCH_TEXTBOX_T:
  1505. {
  1506. DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_ITEM*>( curr_item ) );
  1507. // QuasiModal required for syntax help and Scintilla auto-complete
  1508. if( dlg.ShowQuasiModal() == wxID_OK )
  1509. m_frame->OnModify();
  1510. }
  1511. break;
  1512. case SCH_LABEL_T:
  1513. case SCH_GLOBAL_LABEL_T:
  1514. case SCH_HIER_LABEL_T:
  1515. case SCH_DIRECTIVE_LABEL_T:
  1516. {
  1517. DIALOG_LABEL_PROPERTIES dlg( m_frame, static_cast<SCH_LABEL_BASE*>( curr_item ) );
  1518. // Must be quasi modal for syntax help
  1519. if( dlg.ShowQuasiModal() == wxID_OK )
  1520. m_frame->OnModify();
  1521. break;
  1522. }
  1523. case SCH_FIELD_T:
  1524. {
  1525. SCH_FIELD* field = static_cast<SCH_FIELD*>( curr_item );
  1526. editFieldText( field );
  1527. if( !field->IsVisible() )
  1528. clearSelection = true;
  1529. break;
  1530. }
  1531. case SCH_SHAPE_T:
  1532. {
  1533. DIALOG_SHAPE_PROPERTIES dlg( m_frame, static_cast<SCH_SHAPE*>( curr_item ) );
  1534. if( dlg.ShowModal() == wxID_OK )
  1535. m_frame->OnModify();
  1536. break;
  1537. }
  1538. case SCH_BITMAP_T:
  1539. {
  1540. SCH_BITMAP* bitmap = static_cast<SCH_BITMAP*>( curr_item );
  1541. DIALOG_IMAGE_PROPERTIES dlg( m_frame, bitmap );
  1542. if( dlg.ShowModal() == wxID_OK )
  1543. {
  1544. // The bitmap is cached in Opengl: clear the cache in case it has become invalid
  1545. getView()->RecacheAllItems();
  1546. m_frame->OnModify();
  1547. }
  1548. break;
  1549. }
  1550. case SCH_LINE_T:
  1551. case SCH_BUS_WIRE_ENTRY_T:
  1552. case SCH_JUNCTION_T:
  1553. if( std::all_of( selection.Items().begin(), selection.Items().end(),
  1554. [&]( const EDA_ITEM* item )
  1555. {
  1556. return item->Type() == SCH_LINE_T
  1557. && static_cast<const SCH_LINE*>( item )->IsGraphicLine();
  1558. } ) )
  1559. {
  1560. std::deque<SCH_LINE*> lines;
  1561. for( EDA_ITEM* selItem : selection.Items() )
  1562. lines.push_back( static_cast<SCH_LINE*>( selItem ) );
  1563. DIALOG_LINE_PROPERTIES dlg( m_frame, lines );
  1564. if( dlg.ShowModal() == wxID_OK )
  1565. m_frame->OnModify();
  1566. }
  1567. else if( std::all_of( selection.Items().begin(), selection.Items().end(),
  1568. [&]( const EDA_ITEM* item )
  1569. {
  1570. return item->Type() == SCH_JUNCTION_T;
  1571. } ) )
  1572. {
  1573. std::deque<SCH_JUNCTION*> junctions;
  1574. for( EDA_ITEM* selItem : selection.Items() )
  1575. junctions.push_back( static_cast<SCH_JUNCTION*>( selItem ) );
  1576. DIALOG_JUNCTION_PROPS dlg( m_frame, junctions );
  1577. if( dlg.ShowModal() == wxID_OK )
  1578. m_frame->OnModify();
  1579. }
  1580. else if( std::all_of( selection.Items().begin(), selection.Items().end(),
  1581. [&]( const EDA_ITEM* item )
  1582. {
  1583. const SCH_ITEM* schItem = dynamic_cast<const SCH_ITEM*>( item );
  1584. wxCHECK( schItem, false );
  1585. return ( schItem->HasLineStroke() && schItem->IsConnectable() )
  1586. || item->Type() == SCH_JUNCTION_T;
  1587. } ) )
  1588. {
  1589. std::deque<SCH_ITEM*> items;
  1590. for( EDA_ITEM* selItem : selection.Items() )
  1591. items.push_back( static_cast<SCH_ITEM*>( selItem ) );
  1592. DIALOG_WIRE_BUS_PROPERTIES dlg( m_frame, items );
  1593. if( dlg.ShowModal() == wxID_OK )
  1594. m_frame->OnModify();
  1595. }
  1596. else
  1597. {
  1598. return 0;
  1599. }
  1600. break;
  1601. case SCH_MARKER_T: // These items have no properties to edit
  1602. case SCH_NO_CONNECT_T:
  1603. break;
  1604. default: // Unexpected item
  1605. wxFAIL_MSG( wxString( "Cannot edit schematic item type " ) + curr_item->GetClass() );
  1606. }
  1607. updateItem( curr_item, true );
  1608. if( clearSelection )
  1609. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1610. return 0;
  1611. }
  1612. int SCH_EDIT_TOOL::ChangeTextType( const TOOL_EVENT& aEvent )
  1613. {
  1614. KICAD_T convertTo = aEvent.Parameter<KICAD_T>();
  1615. EE_SELECTION selection = m_selectionTool->RequestSelection( { SCH_LABEL_LOCATE_ANY_T,
  1616. SCH_TEXT_T,
  1617. SCH_TEXTBOX_T } );
  1618. SCH_COMMIT commit( m_toolMgr );
  1619. for( unsigned int i = 0; i < selection.GetSize(); ++i )
  1620. {
  1621. SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( selection.GetItem( i ) );
  1622. if( item && item->Type() != convertTo )
  1623. {
  1624. EDA_TEXT* sourceText = dynamic_cast<EDA_TEXT*>( item );
  1625. bool selected = item->IsSelected();
  1626. SCH_ITEM* newtext = nullptr;
  1627. VECTOR2I position = item->GetPosition();
  1628. wxString txt;
  1629. wxString href;
  1630. SPIN_STYLE spinStyle = SPIN_STYLE::SPIN::RIGHT;
  1631. LABEL_FLAG_SHAPE shape = LABEL_FLAG_SHAPE::L_UNSPECIFIED;
  1632. switch( item->Type() )
  1633. {
  1634. case SCH_LABEL_T:
  1635. case SCH_GLOBAL_LABEL_T:
  1636. case SCH_HIER_LABEL_T:
  1637. {
  1638. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
  1639. txt = UnescapeString( label->GetText() );
  1640. spinStyle = label->GetSpinStyle();
  1641. shape = label->GetShape();
  1642. href = label->GetHyperlink();
  1643. break;
  1644. }
  1645. case SCH_DIRECTIVE_LABEL_T:
  1646. {
  1647. SCH_DIRECTIVE_LABEL* dirlabel = static_cast<SCH_DIRECTIVE_LABEL*>( item );
  1648. // a SCH_DIRECTIVE_LABEL has no text
  1649. txt = _( "<empty>" );
  1650. spinStyle = dirlabel->GetSpinStyle();
  1651. href = dirlabel->GetHyperlink();
  1652. break;
  1653. }
  1654. case SCH_TEXT_T:
  1655. {
  1656. SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
  1657. txt = text->GetText();
  1658. href = text->GetHyperlink();
  1659. break;
  1660. }
  1661. case SCH_TEXTBOX_T:
  1662. {
  1663. SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( item );
  1664. BOX2I bbox = textbox->GetBoundingBox();
  1665. bbox.Inflate( -textbox->GetTextMargin() );
  1666. if( convertTo == SCH_LABEL_T
  1667. || convertTo == SCH_HIER_LABEL_T
  1668. || convertTo == SCH_GLOBAL_LABEL_T )
  1669. {
  1670. EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
  1671. wxCHECK( text, 0 );
  1672. int textSize = text->GetTextSize().y;
  1673. bbox.Inflate( KiROUND( item->Schematic()->Settings().m_LabelSizeRatio * textSize ) );
  1674. }
  1675. txt = textbox->GetText();
  1676. if( textbox->GetTextAngle().IsVertical() )
  1677. {
  1678. if( textbox->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  1679. {
  1680. spinStyle = SPIN_STYLE::SPIN::BOTTOM;
  1681. position = VECTOR2I( bbox.Centre().x, bbox.GetOrigin().y );
  1682. }
  1683. else
  1684. {
  1685. spinStyle = SPIN_STYLE::SPIN::UP;
  1686. position = VECTOR2I( bbox.Centre().x, bbox.GetEnd().y );
  1687. }
  1688. }
  1689. else
  1690. {
  1691. if( textbox->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  1692. {
  1693. spinStyle = SPIN_STYLE::SPIN::LEFT;
  1694. position = VECTOR2I( bbox.GetEnd().x, bbox.Centre().y );
  1695. }
  1696. else
  1697. {
  1698. spinStyle = SPIN_STYLE::SPIN::RIGHT;
  1699. position = VECTOR2I( bbox.GetOrigin().x, bbox.Centre().y );
  1700. }
  1701. }
  1702. position = m_frame->GetNearestGridPosition( position );
  1703. href = textbox->GetHyperlink();
  1704. break;
  1705. }
  1706. default:
  1707. UNIMPLEMENTED_FOR( item->GetClass() );
  1708. break;
  1709. }
  1710. auto getValidNetname =
  1711. []( const wxString& aText )
  1712. {
  1713. wxString local_txt = aText;
  1714. local_txt.Replace( "\n", "_" );
  1715. local_txt.Replace( "\r", "_" );
  1716. local_txt.Replace( "\t", "_" );
  1717. // Bus groups can have spaces; bus vectors and signal names cannot
  1718. if( !NET_SETTINGS::ParseBusGroup( aText, nullptr, nullptr ) )
  1719. local_txt.Replace( " ", "_" );
  1720. // label strings are "escaped" i.e. a '/' is replaced by "{slash}"
  1721. local_txt = EscapeString( local_txt, CTX_NETNAME );
  1722. if( local_txt.IsEmpty() )
  1723. return _( "<empty>" );
  1724. else
  1725. return local_txt;
  1726. };
  1727. switch( convertTo )
  1728. {
  1729. case SCH_LABEL_T:
  1730. {
  1731. SCH_LABEL_BASE* new_label = new SCH_LABEL( position, getValidNetname( txt ) );
  1732. new_label->SetShape( shape );
  1733. new_label->SetAttributes( *sourceText, false );
  1734. new_label->SetSpinStyle( spinStyle );
  1735. new_label->SetHyperlink( href );
  1736. newtext = new_label;
  1737. break;
  1738. }
  1739. case SCH_GLOBAL_LABEL_T:
  1740. {
  1741. SCH_LABEL_BASE* new_label = new SCH_GLOBALLABEL( position, getValidNetname( txt ) );
  1742. new_label->SetShape( shape );
  1743. new_label->SetAttributes( *sourceText, false );
  1744. new_label->SetSpinStyle( spinStyle );
  1745. new_label->SetHyperlink( href );
  1746. newtext = new_label;
  1747. break;
  1748. }
  1749. case SCH_HIER_LABEL_T:
  1750. {
  1751. SCH_LABEL_BASE* new_label = new SCH_HIERLABEL( position, getValidNetname( txt ) );
  1752. new_label->SetShape( shape );
  1753. new_label->SetAttributes( *sourceText, false );
  1754. new_label->SetSpinStyle( spinStyle );
  1755. new_label->SetHyperlink( href );
  1756. newtext = new_label;
  1757. break;
  1758. }
  1759. case SCH_DIRECTIVE_LABEL_T:
  1760. {
  1761. SCH_LABEL_BASE* new_label = new SCH_DIRECTIVE_LABEL( position );
  1762. // A SCH_DIRECTIVE_LABEL usually has at least one field containing the net class
  1763. // name. If we're copying from a text object assume the text is the netclass
  1764. // name. Otherwise, we'll just copy the fields which will either have a netclass
  1765. // or not.
  1766. if( !dynamic_cast<SCH_LABEL_BASE*>( item ) )
  1767. {
  1768. SCH_FIELD netclass( position, 0, new_label, wxT( "Netclass" ) );
  1769. netclass.SetText( txt );
  1770. netclass.SetVisible( true );
  1771. new_label->GetFields().push_back( netclass );
  1772. }
  1773. new_label->SetShape( LABEL_FLAG_SHAPE::F_ROUND );
  1774. new_label->SetAttributes( *sourceText, false );
  1775. new_label->SetSpinStyle( spinStyle );
  1776. new_label->SetHyperlink( href );
  1777. newtext = new_label;
  1778. break;
  1779. }
  1780. case SCH_TEXT_T:
  1781. {
  1782. SCH_TEXT* new_text = new SCH_TEXT( position, txt );
  1783. new_text->SetAttributes( *sourceText, false );
  1784. new_text->SetHyperlink( href );
  1785. newtext = new_text;
  1786. break;
  1787. }
  1788. case SCH_TEXTBOX_T:
  1789. {
  1790. SCH_TEXTBOX* new_textbox = new SCH_TEXTBOX( 0, FILL_T::NO_FILL, txt );
  1791. BOX2I bbox = item->GetBoundingBox();
  1792. if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
  1793. bbox.Inflate( -label->GetLabelBoxExpansion() );
  1794. new_textbox->SetAttributes( *sourceText, false );
  1795. int margin = new_textbox->GetTextMargin();
  1796. bbox.Inflate( margin );
  1797. VECTOR2I topLeft = bbox.GetPosition();
  1798. VECTOR2I botRight = bbox.GetEnd();
  1799. // Add 1/20 of the margin at the end to reduce line-breaking changes.
  1800. int slop = margin / 20;
  1801. if( sourceText->GetTextAngle() == ANGLE_VERTICAL )
  1802. {
  1803. if( sourceText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  1804. botRight.y += slop;
  1805. else
  1806. topLeft.y -= slop;
  1807. }
  1808. else
  1809. {
  1810. if( sourceText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  1811. topLeft.x -= slop;
  1812. else
  1813. botRight.x += slop;
  1814. }
  1815. new_textbox->SetPosition( topLeft );
  1816. new_textbox->SetEnd( botRight );
  1817. new_textbox->SetHyperlink( href );
  1818. newtext = new_textbox;
  1819. break;
  1820. }
  1821. default:
  1822. UNIMPLEMENTED_FOR( wxString::Format( "%d.", convertTo ) );
  1823. break;
  1824. }
  1825. wxCHECK2( newtext, continue );
  1826. // Copy the old text item settings to the new one. Justifications are not copied
  1827. // because they are not used in labels. Justifications will be set to default value
  1828. // in the new text item type.
  1829. //
  1830. newtext->SetFlags( item->GetEditFlags() );
  1831. EDA_TEXT* eda_text = dynamic_cast<EDA_TEXT*>( item );
  1832. EDA_TEXT* new_eda_text = dynamic_cast<EDA_TEXT*>( newtext );
  1833. wxCHECK2( eda_text && new_eda_text, continue );
  1834. new_eda_text->SetFont( eda_text->GetFont() );
  1835. new_eda_text->SetTextSize( eda_text->GetTextSize() );
  1836. new_eda_text->SetTextThickness( eda_text->GetTextThickness() );
  1837. new_eda_text->SetItalic( eda_text->IsItalic() );
  1838. new_eda_text->SetBold( eda_text->IsBold() );
  1839. newtext->AutoplaceFields( m_frame->GetScreen(), false );
  1840. SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item );
  1841. SCH_LABEL_BASE* new_label = dynamic_cast<SCH_LABEL_BASE*>( newtext );
  1842. if( label && new_label )
  1843. {
  1844. new_label->AddFields( label->GetFields() );
  1845. // A SCH_GLOBALLABEL has a specific field, that has no meaning for
  1846. // other labels, and expected to be the first field in list.
  1847. // It is the first field in list for this kind of label
  1848. // So remove field named "Intersheetrefs" if exists for other labels
  1849. int min_idx = new_label->Type() == SCH_GLOBAL_LABEL_T ? 1 : 0;
  1850. std::vector<SCH_FIELD>& fields = new_label->GetFields();
  1851. for( int ii = fields.size()-1; ii >= min_idx; ii-- )
  1852. {
  1853. if( fields[ii].GetCanonicalName() == wxT( "Intersheetrefs" ) )
  1854. fields.erase( fields.begin() + ii );
  1855. }
  1856. }
  1857. if( selected )
  1858. m_toolMgr->RunAction<EDA_ITEM*>( EE_ACTIONS::removeItemFromSel, item );
  1859. if( !item->IsNew() )
  1860. {
  1861. m_frame->RemoveFromScreen( item, m_frame->GetScreen() );
  1862. commit.Removed( item, m_frame->GetScreen() );
  1863. m_frame->AddToScreen( newtext, m_frame->GetScreen() );
  1864. commit.Added( newtext, m_frame->GetScreen() );
  1865. }
  1866. if( selected )
  1867. m_toolMgr->RunAction<EDA_ITEM*>( EE_ACTIONS::addItemToSel, newtext );
  1868. // Otherwise, pointer is owned by the undo stack
  1869. if( item->IsNew() )
  1870. delete item;
  1871. }
  1872. }
  1873. if( !commit.Empty() )
  1874. commit.Push( _( "Change To" ) );
  1875. if( selection.IsHover() )
  1876. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1877. return 0;
  1878. }
  1879. int SCH_EDIT_TOOL::BreakWire( const TOOL_EVENT& aEvent )
  1880. {
  1881. bool isSlice = aEvent.Matches( EE_ACTIONS::slice.MakeEvent() );
  1882. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  1883. EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_LINE_T } );
  1884. SCH_SCREEN* screen = m_frame->GetScreen();
  1885. SCH_COMMIT commit( m_toolMgr );
  1886. std::vector<SCH_LINE*> lines;
  1887. for( EDA_ITEM* item : selection )
  1888. {
  1889. if( item->Type() == SCH_LINE_T )
  1890. {
  1891. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  1892. if( !line->IsEndPoint( cursorPos ) )
  1893. lines.push_back( line );
  1894. }
  1895. }
  1896. m_selectionTool->ClearSelection();
  1897. for( SCH_LINE* line : lines )
  1898. {
  1899. SCH_LINE* newLine;
  1900. // We let the user select the break point if they're on a single line
  1901. if( lines.size() == 1 && line->HitTest( cursorPos ) )
  1902. m_frame->BreakSegment( &commit, line, cursorPos, &newLine, screen );
  1903. else
  1904. m_frame->BreakSegment( &commit, line, line->GetMidPoint(), &newLine, screen );
  1905. // Make sure both endpoints are deselected
  1906. newLine->ClearFlags();
  1907. m_selectionTool->AddItemToSel( line );
  1908. line->SetFlags( ENDPOINT );
  1909. // If we're a break, we want to drag both wires.
  1910. // Side note: the drag/move tool only checks whether the first item is
  1911. // new to determine if it should append undo or not, someday this should
  1912. // be cleaned up and explictly controlled but for now the newLine
  1913. // selection addition must be after the existing line.
  1914. if( !isSlice )
  1915. {
  1916. m_selectionTool->AddItemToSel( newLine );
  1917. newLine->SetFlags( STARTPOINT );
  1918. }
  1919. }
  1920. if( !lines.empty() )
  1921. {
  1922. m_frame->TestDanglingEnds();
  1923. if( m_toolMgr->RunSynchronousAction( EE_ACTIONS::drag, &commit, isSlice ) )
  1924. commit.Push( isSlice ? _( "Slice Wire" ) : _( "Break Wire" ) );
  1925. else
  1926. commit.Revert();
  1927. }
  1928. return 0;
  1929. }
  1930. int SCH_EDIT_TOOL::CleanupSheetPins( const TOOL_EVENT& aEvent )
  1931. {
  1932. EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SHEET_T } );
  1933. SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
  1934. SCH_COMMIT commit( m_toolMgr );
  1935. if( !sheet || !sheet->HasUndefinedPins() )
  1936. return 0;
  1937. if( !IsOK( m_frame, _( "Do you wish to delete the unreferenced pins from this sheet?" ) ) )
  1938. return 0;
  1939. commit.Modify( sheet, m_frame->GetScreen() );
  1940. sheet->CleanupSheet();
  1941. updateItem( sheet, true );
  1942. commit.Push( _( "Cleanup Sheet Pins" ) );
  1943. if( selection.IsHover() )
  1944. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  1945. return 0;
  1946. }
  1947. int SCH_EDIT_TOOL::EditPageNumber( const TOOL_EVENT& aEvent )
  1948. {
  1949. EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SHEET_T } );
  1950. if( selection.GetSize() > 1 )
  1951. return 0;
  1952. SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
  1953. SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
  1954. SCH_SCREEN* screen;
  1955. if( sheet )
  1956. {
  1957. // When changing the page number of a selected sheet, the current screen owns the sheet.
  1958. screen = m_frame->GetScreen();
  1959. instance.push_back( sheet );
  1960. }
  1961. else
  1962. {
  1963. SCH_SHEET_PATH prevInstance = instance;
  1964. // When change the page number in the screen, the previous screen owns the sheet.
  1965. if( prevInstance.size() )
  1966. {
  1967. prevInstance.pop_back();
  1968. screen = prevInstance.LastScreen();
  1969. }
  1970. else
  1971. {
  1972. // The root sheet and root screen are effectively the same thing.
  1973. screen = m_frame->GetScreen();
  1974. }
  1975. sheet = m_frame->GetCurrentSheet().Last();
  1976. }
  1977. wxString msg;
  1978. wxString sheetPath = instance.PathHumanReadable( false );
  1979. wxString pageNumber = instance.GetPageNumber();
  1980. msg.Printf( _( "Enter page number for sheet path%s" ),
  1981. ( sheetPath.Length() > 20 ) ? "\n" + sheetPath : " " + sheetPath );
  1982. wxTextEntryDialog dlg( m_frame, msg, _( "Edit Sheet Page Number" ), pageNumber );
  1983. dlg.SetTextValidator( wxFILTER_ALPHANUMERIC ); // No white space.
  1984. if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == instance.GetPageNumber() )
  1985. return 0;
  1986. m_frame->SaveCopyInUndoList( screen, sheet, UNDO_REDO::CHANGED, false );
  1987. instance.SetPageNumber( dlg.GetValue() );
  1988. if( instance == m_frame->GetCurrentSheet() )
  1989. {
  1990. m_frame->GetScreen()->SetPageNumber( dlg.GetValue() );
  1991. m_frame->OnPageSettingsChange();
  1992. }
  1993. m_frame->OnModify();
  1994. // Update the hierarchy navigator labels if needed
  1995. if( pageNumber != dlg.GetValue() )
  1996. m_frame->UpdateLabelsHierarchyNavigator();
  1997. return 0;
  1998. }
  1999. int SCH_EDIT_TOOL::DdAppendFile( const TOOL_EVENT& aEvent )
  2000. {
  2001. wxString aFileName = *aEvent.Parameter<wxString*>();
  2002. return ( m_frame->AddSheetAndUpdateDisplay( aFileName ) ? 0 : 1 );
  2003. }
  2004. int SCH_EDIT_TOOL::SetAttribute( const TOOL_EVENT& aEvent )
  2005. {
  2006. EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
  2007. SCH_COMMIT commit( m_toolMgr );
  2008. if( selection.Empty() )
  2009. return 0;
  2010. for( EDA_ITEM* item : selection )
  2011. {
  2012. if( item->Type() == SCH_SYMBOL_T )
  2013. {
  2014. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  2015. commit.Modify( symbol, m_frame->GetScreen() );
  2016. if( aEvent.IsAction( &EE_ACTIONS::setDNP ) )
  2017. symbol->SetDNP( true );
  2018. if( aEvent.IsAction( &EE_ACTIONS::setExcludeFromSimulation ) )
  2019. symbol->SetExcludedFromSim( true );
  2020. if( aEvent.IsAction( &EE_ACTIONS::setExcludeFromBOM ) )
  2021. symbol->SetExcludedFromBOM( true );
  2022. if( aEvent.IsAction( &EE_ACTIONS::setExcludeFromBoard ) )
  2023. symbol->SetExcludedFromBoard( true );
  2024. }
  2025. }
  2026. if( !commit.Empty() )
  2027. commit.Push( _( "Set Attribute" ) );
  2028. return 0;
  2029. }
  2030. int SCH_EDIT_TOOL::UnsetAttribute( const TOOL_EVENT& aEvent )
  2031. {
  2032. EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
  2033. SCH_COMMIT commit( m_toolMgr );
  2034. if( selection.Empty() )
  2035. return 0;
  2036. for( EDA_ITEM* item : selection )
  2037. {
  2038. if( item->Type() == SCH_SYMBOL_T )
  2039. {
  2040. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  2041. commit.Modify( symbol, m_frame->GetScreen() );
  2042. if( aEvent.IsAction( &EE_ACTIONS::unsetDNP ) )
  2043. symbol->SetDNP( false );
  2044. if( aEvent.IsAction( &EE_ACTIONS::unsetExcludeFromSimulation ) )
  2045. symbol->SetExcludedFromSim( false );
  2046. if( aEvent.IsAction( &EE_ACTIONS::unsetExcludeFromBOM ) )
  2047. symbol->SetExcludedFromBOM( false );
  2048. if( aEvent.IsAction( &EE_ACTIONS::unsetExcludeFromBoard ) )
  2049. symbol->SetExcludedFromBoard( false );
  2050. }
  2051. }
  2052. if( !commit.Empty() )
  2053. commit.Push( _( "Clear Attribute" ) );
  2054. return 0;
  2055. }
  2056. int SCH_EDIT_TOOL::ToggleAttribute( const TOOL_EVENT& aEvent )
  2057. {
  2058. EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
  2059. SCH_COMMIT commit( m_toolMgr );
  2060. if( selection.Empty() )
  2061. return 0;
  2062. for( EDA_ITEM* item : selection )
  2063. {
  2064. if( item->Type() == SCH_SYMBOL_T )
  2065. {
  2066. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  2067. commit.Modify( symbol, m_frame->GetScreen() );
  2068. if( aEvent.IsAction( &EE_ACTIONS::toggleDNP ) )
  2069. symbol->SetDNP( !symbol->GetDNP() );
  2070. if( aEvent.IsAction( &EE_ACTIONS::toggleExcludeFromSimulation ) )
  2071. symbol->SetExcludedFromSim( !symbol->GetExcludedFromSim() );
  2072. if( aEvent.IsAction( &EE_ACTIONS::toggleExcludeFromBOM ) )
  2073. symbol->SetExcludedFromBOM( !symbol->GetExcludedFromBOM() );
  2074. if( aEvent.IsAction( &EE_ACTIONS::toggleExcludeFromBoard ) )
  2075. symbol->SetExcludedFromBoard( !symbol->GetExcludedFromBoard() );
  2076. }
  2077. }
  2078. if( !commit.Empty() )
  2079. commit.Push( _( "Toggle Attribute" ) );
  2080. return 0;
  2081. }
  2082. void SCH_EDIT_TOOL::setTransitions()
  2083. {
  2084. Go( &SCH_EDIT_TOOL::RepeatDrawItem, EE_ACTIONS::repeatDrawItem.MakeEvent() );
  2085. Go( &SCH_EDIT_TOOL::Rotate, EE_ACTIONS::rotateCW.MakeEvent() );
  2086. Go( &SCH_EDIT_TOOL::Rotate, EE_ACTIONS::rotateCCW.MakeEvent() );
  2087. Go( &SCH_EDIT_TOOL::Mirror, EE_ACTIONS::mirrorV.MakeEvent() );
  2088. Go( &SCH_EDIT_TOOL::Mirror, EE_ACTIONS::mirrorH.MakeEvent() );
  2089. Go( &SCH_EDIT_TOOL::Swap, EE_ACTIONS::swap.MakeEvent() );
  2090. Go( &SCH_EDIT_TOOL::DoDelete, ACTIONS::doDelete.MakeEvent() );
  2091. Go( &SCH_EDIT_TOOL::InteractiveDelete, ACTIONS::deleteTool.MakeEvent() );
  2092. Go( &SCH_EDIT_TOOL::Properties, EE_ACTIONS::properties.MakeEvent() );
  2093. Go( &SCH_EDIT_TOOL::EditField, EE_ACTIONS::editReference.MakeEvent() );
  2094. Go( &SCH_EDIT_TOOL::EditField, EE_ACTIONS::editValue.MakeEvent() );
  2095. Go( &SCH_EDIT_TOOL::EditField, EE_ACTIONS::editFootprint.MakeEvent() );
  2096. Go( &SCH_EDIT_TOOL::AutoplaceFields, EE_ACTIONS::autoplaceFields.MakeEvent() );
  2097. Go( &SCH_EDIT_TOOL::ChangeSymbols, EE_ACTIONS::changeSymbols.MakeEvent() );
  2098. Go( &SCH_EDIT_TOOL::ChangeSymbols, EE_ACTIONS::updateSymbols.MakeEvent() );
  2099. Go( &SCH_EDIT_TOOL::ChangeSymbols, EE_ACTIONS::changeSymbol.MakeEvent() );
  2100. Go( &SCH_EDIT_TOOL::ChangeSymbols, EE_ACTIONS::updateSymbol.MakeEvent() );
  2101. Go( &SCH_EDIT_TOOL::ChangeBodyStyle, EE_ACTIONS::toggleDeMorgan.MakeEvent() );
  2102. Go( &SCH_EDIT_TOOL::ChangeBodyStyle, EE_ACTIONS::showDeMorganStandard.MakeEvent() );
  2103. Go( &SCH_EDIT_TOOL::ChangeBodyStyle, EE_ACTIONS::showDeMorganAlternate.MakeEvent() );
  2104. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toLabel.MakeEvent() );
  2105. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toHLabel.MakeEvent() );
  2106. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toGLabel.MakeEvent() );
  2107. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toCLabel.MakeEvent() );
  2108. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toText.MakeEvent() );
  2109. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toTextBox.MakeEvent() );
  2110. Go( &SCH_EDIT_TOOL::BreakWire, EE_ACTIONS::breakWire.MakeEvent() );
  2111. Go( &SCH_EDIT_TOOL::BreakWire, EE_ACTIONS::slice.MakeEvent() );
  2112. Go( &SCH_EDIT_TOOL::SetAttribute, EE_ACTIONS::setDNP.MakeEvent() );
  2113. Go( &SCH_EDIT_TOOL::SetAttribute, EE_ACTIONS::setExcludeFromBOM.MakeEvent() );
  2114. Go( &SCH_EDIT_TOOL::SetAttribute, EE_ACTIONS::setExcludeFromBoard.MakeEvent() );
  2115. Go( &SCH_EDIT_TOOL::SetAttribute, EE_ACTIONS::setExcludeFromSimulation.MakeEvent() );
  2116. Go( &SCH_EDIT_TOOL::UnsetAttribute, EE_ACTIONS::unsetDNP.MakeEvent() );
  2117. Go( &SCH_EDIT_TOOL::UnsetAttribute, EE_ACTIONS::unsetExcludeFromBOM.MakeEvent() );
  2118. Go( &SCH_EDIT_TOOL::UnsetAttribute, EE_ACTIONS::unsetExcludeFromBoard.MakeEvent() );
  2119. Go( &SCH_EDIT_TOOL::UnsetAttribute, EE_ACTIONS::unsetExcludeFromSimulation.MakeEvent() );
  2120. Go( &SCH_EDIT_TOOL::ToggleAttribute, EE_ACTIONS::toggleDNP.MakeEvent() );
  2121. Go( &SCH_EDIT_TOOL::ToggleAttribute, EE_ACTIONS::toggleExcludeFromBOM.MakeEvent() );
  2122. Go( &SCH_EDIT_TOOL::ToggleAttribute, EE_ACTIONS::toggleExcludeFromBoard.MakeEvent() );
  2123. Go( &SCH_EDIT_TOOL::ToggleAttribute, EE_ACTIONS::toggleExcludeFromSimulation.MakeEvent() );
  2124. Go( &SCH_EDIT_TOOL::CleanupSheetPins, EE_ACTIONS::cleanupSheetPins.MakeEvent() );
  2125. Go( &SCH_EDIT_TOOL::GlobalEdit, EE_ACTIONS::editTextAndGraphics.MakeEvent() );
  2126. Go( &SCH_EDIT_TOOL::EditPageNumber, EE_ACTIONS::editPageNumber.MakeEvent() );
  2127. Go( &SCH_EDIT_TOOL::DdAppendFile, EE_ACTIONS::ddAppendFile.MakeEvent() );
  2128. }