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.

1754 lines
57 KiB

7 years ago
6 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 CERN
  5. * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <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 <widgets/infobar.h>
  31. #include <ee_actions.h>
  32. #include <bitmaps.h>
  33. #include <confirm.h>
  34. #include <eda_item.h>
  35. #include <reporter.h>
  36. #include <kicad_string.h>
  37. #include <sch_item.h>
  38. #include <sch_symbol.h>
  39. #include <sch_sheet.h>
  40. #include <sch_sheet_pin.h>
  41. #include <sch_text.h>
  42. #include <sch_bitmap.h>
  43. #include <sch_view.h>
  44. #include <sch_line.h>
  45. #include <sch_bus_entry.h>
  46. #include <sch_junction.h>
  47. #include <sch_edit_frame.h>
  48. #include <schematic.h>
  49. #include <drawing_sheet/ds_proxy_view_item.h>
  50. #include <drawing_sheet/ds_proxy_undo_item.h>
  51. #include <eeschema_id.h>
  52. #include <status_popup.h>
  53. #include <wx/gdicmn.h>
  54. #include <dialogs/dialog_change_symbols.h>
  55. #include <dialogs/dialog_image_editor.h>
  56. #include <dialogs/dialog_line_wire_bus_properties.h>
  57. #include <dialogs/dialog_symbol_properties.h>
  58. #include <dialogs/dialog_sheet_pin_properties.h>
  59. #include <dialogs/dialog_edit_one_field.h>
  60. #include <dialogs/dialog_junction_props.h>
  61. #include "sch_drawing_tools.h"
  62. #include <math/util.h> // for KiROUND
  63. #include <pgm_base.h>
  64. #include <settings/settings_manager.h>
  65. #include <symbol_editor_settings.h>
  66. #include <dialogs/dialog_edit_label.h>
  67. #include <core/kicad_algo.h>
  68. //#include <wx/filedlg.h>
  69. #include <wx/textdlg.h>
  70. class SYMBOL_UNIT_MENU : public ACTION_MENU
  71. {
  72. public:
  73. SYMBOL_UNIT_MENU() :
  74. ACTION_MENU( true )
  75. {
  76. SetIcon( BITMAPS::component_select_unit );
  77. SetTitle( _( "Symbol Unit" ) );
  78. }
  79. protected:
  80. ACTION_MENU* create() const override
  81. {
  82. return new SYMBOL_UNIT_MENU();
  83. }
  84. private:
  85. void update() override
  86. {
  87. EE_SELECTION_TOOL* selTool = getToolManager()->GetTool<EE_SELECTION_TOOL>();
  88. EE_SELECTION& selection = selTool->GetSelection();
  89. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
  90. Clear();
  91. if( !symbol )
  92. {
  93. Append( ID_POPUP_SCH_UNFOLD_BUS, _( "no symbol selected" ), wxEmptyString );
  94. Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
  95. return;
  96. }
  97. int unit = symbol->GetUnit();
  98. if( !symbol->GetLibSymbolRef() || symbol->GetLibSymbolRef()->GetUnitCount() < 2 )
  99. {
  100. Append( ID_POPUP_SCH_UNFOLD_BUS, _( "symbol is not multi-unit" ), wxEmptyString );
  101. Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
  102. return;
  103. }
  104. for( int ii = 0; ii < symbol->GetLibSymbolRef()->GetUnitCount(); ii++ )
  105. {
  106. wxString num_unit;
  107. num_unit.Printf( _( "Unit %s" ), LIB_SYMBOL::SubReference( ii + 1, false ) );
  108. wxMenuItem * item = Append( ID_POPUP_SCH_SELECT_UNIT1 + ii, num_unit, wxEmptyString,
  109. wxITEM_CHECK );
  110. if( unit == ii + 1 )
  111. item->Check(true);
  112. // The ID max for these submenus is ID_POPUP_SCH_SELECT_UNIT_SYM_MAX
  113. // See eeschema_id to modify this value.
  114. if( ii >= (ID_POPUP_SCH_SELECT_UNIT_SYM_MAX - ID_POPUP_SCH_SELECT_UNIT1) )
  115. break; // We have used all IDs for these submenus
  116. }
  117. }
  118. };
  119. SCH_EDIT_TOOL::SCH_EDIT_TOOL() :
  120. EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveEdit" )
  121. {
  122. m_pickerItem = nullptr;
  123. }
  124. using E_C = EE_CONDITIONS;
  125. bool SCH_EDIT_TOOL::Init()
  126. {
  127. EE_TOOL_BASE::Init();
  128. SCH_DRAWING_TOOLS* drawingTools = m_toolMgr->GetTool<SCH_DRAWING_TOOLS>();
  129. SCH_MOVE_TOOL* moveTool = m_toolMgr->GetTool<SCH_MOVE_TOOL>();
  130. wxASSERT_MSG( drawingTools, "eeshema.InteractiveDrawing tool is not available" );
  131. auto hasElements =
  132. [ this ] ( const SELECTION& aSel )
  133. {
  134. return !m_frame->GetScreen()->Items().empty();
  135. };
  136. auto sheetTool =
  137. [ this ] ( const SELECTION& aSel )
  138. {
  139. return ( m_frame->IsCurrentTool( EE_ACTIONS::drawSheet ) );
  140. };
  141. auto anyTextTool =
  142. [ this ] ( const SELECTION& aSel )
  143. {
  144. return ( m_frame->IsCurrentTool( EE_ACTIONS::placeLabel )
  145. || m_frame->IsCurrentTool( EE_ACTIONS::placeGlobalLabel )
  146. || m_frame->IsCurrentTool( EE_ACTIONS::placeHierLabel )
  147. || m_frame->IsCurrentTool( EE_ACTIONS::placeSchematicText ) );
  148. };
  149. auto duplicateCondition =
  150. [] ( const SELECTION& aSel )
  151. {
  152. if( SCH_LINE_WIRE_BUS_TOOL::IsDrawingLineWireOrBus( aSel ) )
  153. return false;
  154. return true;
  155. };
  156. auto orientCondition =
  157. [] ( const SELECTION& aSel )
  158. {
  159. if( aSel.Empty() )
  160. return false;
  161. if( SCH_LINE_WIRE_BUS_TOOL::IsDrawingLineWireOrBus( aSel ) )
  162. return false;
  163. SCH_ITEM* item = (SCH_ITEM*) aSel.Front();
  164. if( aSel.GetSize() > 1 )
  165. return true;
  166. switch( item->Type() )
  167. {
  168. case SCH_MARKER_T:
  169. case SCH_JUNCTION_T:
  170. case SCH_NO_CONNECT_T:
  171. case SCH_PIN_T:
  172. return false;
  173. case SCH_LINE_T:
  174. return item->GetLayer() != LAYER_WIRE && item->GetLayer() != LAYER_BUS;
  175. default:
  176. return true;
  177. }
  178. };
  179. auto propertiesCondition =
  180. [&]( const SELECTION& aSel )
  181. {
  182. if( aSel.GetSize() == 0 )
  183. {
  184. if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
  185. {
  186. DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
  187. VECTOR2D cursor = getViewControls()->GetCursorPosition( false );
  188. if( ds && ds->HitTestDrawingSheetItems( getView(), (wxPoint) cursor ) )
  189. return true;
  190. }
  191. return false;
  192. }
  193. SCH_ITEM* firstItem = dynamic_cast<SCH_ITEM*>( aSel.Front() );
  194. const EE_SELECTION* eeSelection = dynamic_cast<const EE_SELECTION*>( &aSel );
  195. if( !firstItem || !eeSelection )
  196. return false;
  197. switch( firstItem->Type() )
  198. {
  199. case SCH_SYMBOL_T:
  200. case SCH_SHEET_T:
  201. case SCH_SHEET_PIN_T:
  202. case SCH_TEXT_T:
  203. case SCH_LABEL_T:
  204. case SCH_GLOBAL_LABEL_T:
  205. case SCH_HIER_LABEL_T:
  206. case SCH_FIELD_T:
  207. case SCH_BITMAP_T:
  208. return aSel.GetSize() == 1;
  209. case SCH_LINE_T:
  210. case SCH_BUS_WIRE_ENTRY_T:
  211. return eeSelection->AllItemsHaveLineStroke();
  212. case SCH_JUNCTION_T:
  213. return eeSelection->AreAllItemsIdentical();
  214. default:
  215. return false;
  216. }
  217. };
  218. static KICAD_T toLabelTypes[] = { SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_TEXT_T, EOT };
  219. auto toLabelCondition = E_C::Count( 1 ) && E_C::OnlyTypes( toLabelTypes );
  220. static KICAD_T toHLableTypes[] = { SCH_LABEL_T, SCH_GLOBAL_LABEL_T, SCH_TEXT_T, EOT };
  221. auto toHLabelCondition = E_C::Count( 1 ) && E_C::OnlyTypes( toHLableTypes );
  222. static KICAD_T toGLableTypes[] = { SCH_LABEL_T, SCH_HIER_LABEL_T, SCH_TEXT_T, EOT };
  223. auto toGLabelCondition = E_C::Count( 1 ) && E_C::OnlyTypes( toGLableTypes );
  224. static KICAD_T toTextTypes[] = { SCH_LABEL_T, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, EOT };
  225. auto toTextlCondition = E_C::Count( 1 ) && E_C::OnlyTypes( toTextTypes );
  226. static KICAD_T entryTypes[] = { SCH_BUS_WIRE_ENTRY_T, SCH_BUS_BUS_ENTRY_T, EOT };
  227. auto entryCondition = E_C::MoreThan( 0 ) && E_C::OnlyTypes( entryTypes );
  228. static KICAD_T fieldParentTypes[] = { SCH_SYMBOL_T, SCH_SHEET_T, SCH_GLOBAL_LABEL_T, EOT };
  229. auto singleFieldParentCondition = E_C::Count( 1 ) && E_C::OnlyTypes( fieldParentTypes );
  230. auto singleSheetCondition = E_C::Count( 1 ) && E_C::OnlyType( SCH_SHEET_T );
  231. //
  232. // Add edit actions to the move tool menu
  233. //
  234. if( moveTool )
  235. {
  236. CONDITIONAL_MENU& moveMenu = moveTool->GetToolMenu().GetMenu();
  237. moveMenu.AddSeparator();
  238. moveMenu.AddItem( EE_ACTIONS::rotateCCW, orientCondition );
  239. moveMenu.AddItem( EE_ACTIONS::rotateCW, orientCondition );
  240. moveMenu.AddItem( EE_ACTIONS::mirrorV, orientCondition );
  241. moveMenu.AddItem( EE_ACTIONS::mirrorH, orientCondition );
  242. moveMenu.AddItem( EE_ACTIONS::properties, propertiesCondition );
  243. moveMenu.AddItem( EE_ACTIONS::editReference, E_C::SingleSymbol );
  244. moveMenu.AddItem( EE_ACTIONS::editValue, E_C::SingleSymbol );
  245. moveMenu.AddItem( EE_ACTIONS::editFootprint, E_C::SingleSymbol );
  246. moveMenu.AddItem( EE_ACTIONS::toggleDeMorgan, E_C::SingleDeMorganSymbol );
  247. std::shared_ptr<SYMBOL_UNIT_MENU> symUnitMenu = std::make_shared<SYMBOL_UNIT_MENU>();
  248. symUnitMenu->SetTool( this );
  249. m_menu.AddSubMenu( symUnitMenu );
  250. moveMenu.AddMenu( symUnitMenu.get(), E_C::SingleMultiUnitSymbol, 1 );
  251. moveMenu.AddSeparator();
  252. moveMenu.AddItem( ACTIONS::cut, E_C::IdleSelection );
  253. moveMenu.AddItem( ACTIONS::copy, E_C::IdleSelection );
  254. moveMenu.AddItem( ACTIONS::doDelete, E_C::NotEmpty );
  255. moveMenu.AddItem( ACTIONS::duplicate, duplicateCondition );
  256. moveMenu.AddSeparator();
  257. moveMenu.AddItem( ACTIONS::selectAll, hasElements );
  258. }
  259. //
  260. // Add editing actions to the drawing tool menu
  261. //
  262. CONDITIONAL_MENU& drawMenu = drawingTools->GetToolMenu().GetMenu();
  263. drawMenu.AddItem( EE_ACTIONS::rotateCCW, orientCondition, 200 );
  264. drawMenu.AddItem( EE_ACTIONS::rotateCW, orientCondition, 200 );
  265. drawMenu.AddItem( EE_ACTIONS::mirrorV, orientCondition, 200 );
  266. drawMenu.AddItem( EE_ACTIONS::mirrorH, orientCondition, 200 );
  267. drawMenu.AddItem( EE_ACTIONS::properties, propertiesCondition, 200 );
  268. drawMenu.AddItem( EE_ACTIONS::editReference, E_C::SingleSymbol, 200 );
  269. drawMenu.AddItem( EE_ACTIONS::editValue, E_C::SingleSymbol, 200 );
  270. drawMenu.AddItem( EE_ACTIONS::editFootprint, E_C::SingleSymbol, 200 );
  271. drawMenu.AddItem( EE_ACTIONS::autoplaceFields, singleFieldParentCondition, 200 );
  272. drawMenu.AddItem( EE_ACTIONS::toggleDeMorgan, E_C::SingleDeMorganSymbol, 200 );
  273. std::shared_ptr<SYMBOL_UNIT_MENU> symUnitMenu2 = std::make_shared<SYMBOL_UNIT_MENU>();
  274. symUnitMenu2->SetTool( drawingTools );
  275. drawingTools->GetToolMenu().AddSubMenu( symUnitMenu2 );
  276. drawMenu.AddMenu( symUnitMenu2.get(), E_C::SingleMultiUnitSymbol, 1 );
  277. drawMenu.AddItem( EE_ACTIONS::editWithLibEdit, E_C::SingleSymbolOrPower && E_C::Idle, 200 );
  278. drawMenu.AddItem( EE_ACTIONS::toLabel, anyTextTool && E_C::Idle, 200 );
  279. drawMenu.AddItem( EE_ACTIONS::toHLabel, anyTextTool && E_C::Idle, 200 );
  280. drawMenu.AddItem( EE_ACTIONS::toGLabel, anyTextTool && E_C::Idle, 200 );
  281. drawMenu.AddItem( EE_ACTIONS::toText, anyTextTool && E_C::Idle, 200 );
  282. drawMenu.AddItem( EE_ACTIONS::cleanupSheetPins, sheetTool && E_C::Idle, 250 );
  283. //
  284. // Add editing actions to the selection tool menu
  285. //
  286. CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
  287. selToolMenu.AddItem( EE_ACTIONS::rotateCCW, orientCondition, 200 );
  288. selToolMenu.AddItem( EE_ACTIONS::rotateCW, orientCondition, 200 );
  289. selToolMenu.AddItem( EE_ACTIONS::mirrorV, orientCondition, 200 );
  290. selToolMenu.AddItem( EE_ACTIONS::mirrorH, orientCondition, 200 );
  291. selToolMenu.AddItem( EE_ACTIONS::properties, propertiesCondition, 200 );
  292. selToolMenu.AddItem( EE_ACTIONS::editReference, E_C::SingleSymbol, 200 );
  293. selToolMenu.AddItem( EE_ACTIONS::editValue, E_C::SingleSymbol, 200 );
  294. selToolMenu.AddItem( EE_ACTIONS::editFootprint, E_C::SingleSymbol, 200 );
  295. selToolMenu.AddItem( EE_ACTIONS::autoplaceFields, singleFieldParentCondition, 200 );
  296. selToolMenu.AddItem( EE_ACTIONS::toggleDeMorgan, E_C::SingleSymbol, 200 );
  297. std::shared_ptr<SYMBOL_UNIT_MENU> symUnitMenu3 = std::make_shared<SYMBOL_UNIT_MENU>();
  298. symUnitMenu3->SetTool( m_selectionTool );
  299. m_selectionTool->GetToolMenu().AddSubMenu( symUnitMenu3 );
  300. selToolMenu.AddMenu( symUnitMenu3.get(), E_C::SingleMultiUnitSymbol, 1 );
  301. selToolMenu.AddItem( EE_ACTIONS::editWithLibEdit, E_C::SingleSymbolOrPower && E_C::Idle, 200 );
  302. selToolMenu.AddItem( EE_ACTIONS::changeSymbol, E_C::SingleSymbolOrPower, 200 );
  303. selToolMenu.AddItem( EE_ACTIONS::updateSymbol, E_C::SingleSymbolOrPower, 200 );
  304. selToolMenu.AddItem( EE_ACTIONS::toLabel, toLabelCondition, 200 );
  305. selToolMenu.AddItem( EE_ACTIONS::toHLabel, toHLabelCondition, 200 );
  306. selToolMenu.AddItem( EE_ACTIONS::toGLabel, toGLabelCondition, 200 );
  307. selToolMenu.AddItem( EE_ACTIONS::toText, toTextlCondition, 200 );
  308. selToolMenu.AddItem( EE_ACTIONS::cleanupSheetPins, singleSheetCondition, 250 );
  309. selToolMenu.AddSeparator( 300 );
  310. selToolMenu.AddItem( ACTIONS::cut, E_C::IdleSelection, 300 );
  311. selToolMenu.AddItem( ACTIONS::copy, E_C::IdleSelection, 300 );
  312. selToolMenu.AddItem( ACTIONS::paste, E_C::Idle, 300 );
  313. selToolMenu.AddItem( ACTIONS::pasteSpecial, E_C::Idle, 300 );
  314. selToolMenu.AddItem( ACTIONS::doDelete, E_C::NotEmpty, 300 );
  315. selToolMenu.AddItem( ACTIONS::duplicate, duplicateCondition, 300 );
  316. selToolMenu.AddSeparator( 400 );
  317. selToolMenu.AddItem( ACTIONS::selectAll, hasElements, 400 );
  318. return true;
  319. }
  320. const KICAD_T rotatableItems[] = {
  321. SCH_TEXT_T,
  322. SCH_LABEL_T,
  323. SCH_GLOBAL_LABEL_T,
  324. SCH_HIER_LABEL_T,
  325. SCH_FIELD_T,
  326. SCH_SYMBOL_T,
  327. SCH_SHEET_PIN_T,
  328. SCH_SHEET_T,
  329. SCH_BITMAP_T,
  330. SCH_BUS_BUS_ENTRY_T,
  331. SCH_BUS_WIRE_ENTRY_T,
  332. SCH_LINE_T,
  333. SCH_JUNCTION_T,
  334. SCH_NO_CONNECT_T,
  335. EOT
  336. };
  337. int SCH_EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
  338. {
  339. bool clockwise = ( aEvent.Matches( EE_ACTIONS::rotateCW.MakeEvent() ) );
  340. EE_SELECTION& selection = m_selectionTool->RequestSelection( rotatableItems );
  341. if( selection.GetSize() == 0 )
  342. return 0;
  343. SCH_ITEM* head = nullptr;
  344. int principalItemCount = 0; // User-selected items (as opposed to connected wires)
  345. wxPoint rotPoint;
  346. bool moving = false;
  347. bool connections = false;
  348. for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
  349. {
  350. SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
  351. if( item->HasFlag( TEMP_SELECTED ) )
  352. continue;
  353. principalItemCount++;
  354. if( !head )
  355. head = item;
  356. }
  357. if( head && head->IsMoving() )
  358. moving = true;
  359. if( principalItemCount == 1 )
  360. {
  361. rotPoint = head->GetPosition();
  362. if( !moving )
  363. saveCopyInUndoList( head, UNDO_REDO::CHANGED );
  364. switch( head->Type() )
  365. {
  366. case SCH_SYMBOL_T:
  367. {
  368. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( head );
  369. if( clockwise )
  370. symbol->SetOrientation( SYM_ROTATE_CLOCKWISE );
  371. else
  372. symbol->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
  373. if( m_frame->eeconfig()->m_AutoplaceFields.enable )
  374. symbol->AutoAutoplaceFields( m_frame->GetScreen() );
  375. break;
  376. }
  377. case SCH_TEXT_T:
  378. case SCH_LABEL_T:
  379. case SCH_GLOBAL_LABEL_T:
  380. case SCH_HIER_LABEL_T:
  381. {
  382. SCH_TEXT* textItem = static_cast<SCH_TEXT*>( head );
  383. textItem->Rotate90( clockwise );
  384. break;
  385. }
  386. case SCH_SHEET_PIN_T:
  387. {
  388. // Rotate pin within parent sheet
  389. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( head );
  390. SCH_SHEET* sheet = pin->GetParent();
  391. for( int i = 0; clockwise ? i < 1 : i < 3; ++i )
  392. pin->Rotate( sheet->GetBoundingBox().GetCenter() );
  393. break;
  394. }
  395. case SCH_LINE_T:
  396. case SCH_BUS_BUS_ENTRY_T:
  397. case SCH_BUS_WIRE_ENTRY_T:
  398. for( int i = 0; clockwise ? i < 1 : i < 3; ++i )
  399. head->Rotate( rotPoint );
  400. break;
  401. case SCH_FIELD_T:
  402. {
  403. SCH_FIELD* field = static_cast<SCH_FIELD*>( head );
  404. if( field->GetTextAngle() == TEXT_ANGLE_HORIZ )
  405. field->SetTextAngle( TEXT_ANGLE_VERT );
  406. else
  407. field->SetTextAngle( TEXT_ANGLE_HORIZ );
  408. // Now that we're moving a field, they're no longer autoplaced.
  409. static_cast<SCH_ITEM*>( head->GetParent() )->ClearFieldsAutoplaced();
  410. break;
  411. }
  412. case SCH_BITMAP_T:
  413. for( int i = 0; clockwise ? i < 1 : i < 3; ++i )
  414. head->Rotate( rotPoint );
  415. // The bitmap is cached in Opengl: clear the cache to redraw
  416. getView()->RecacheAllItems();
  417. break;
  418. case SCH_SHEET_T:
  419. {
  420. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( head );
  421. rotPoint = m_frame->GetNearestGridPosition( sheet->GetRotationCenter() );
  422. // Rotate the sheet on itself. Sheets do not have an anchor point.
  423. for( int i = 0; clockwise ? i < 3 : i < 1; ++i )
  424. sheet->Rotate( rotPoint );
  425. break;
  426. }
  427. default:
  428. break;
  429. }
  430. connections = head->IsConnectable();
  431. m_frame->UpdateItem( head );
  432. }
  433. else
  434. {
  435. rotPoint = m_frame->GetNearestGridPosition( (wxPoint)selection.GetCenter() );
  436. }
  437. for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
  438. {
  439. SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
  440. // We've already rotated the user selected item if there was only one. We're just
  441. // here to rotate the ends of wires that were attached to it.
  442. if( principalItemCount == 1 && !item->HasFlag( TEMP_SELECTED ) )
  443. continue;
  444. if( !moving )
  445. saveCopyInUndoList( item, UNDO_REDO::CHANGED, ii > 0 );
  446. for( int i = 0; clockwise ? i < 1 : i < 3; ++i )
  447. {
  448. if( item->Type() == SCH_LINE_T )
  449. {
  450. SCH_LINE* line = (SCH_LINE*) item;
  451. if( item->HasFlag( STARTPOINT ) )
  452. line->RotateStart( rotPoint );
  453. if( item->HasFlag( ENDPOINT ) )
  454. line->RotateEnd( rotPoint );
  455. }
  456. else if( item->Type() == SCH_SHEET_PIN_T )
  457. {
  458. if( item->GetParent()->IsSelected() )
  459. {
  460. // parent will rotate us
  461. }
  462. else
  463. {
  464. // rotate within parent
  465. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
  466. SCH_SHEET* sheet = pin->GetParent();
  467. pin->Rotate( sheet->GetBoundingBox().GetCenter() );
  468. }
  469. }
  470. else if( item->Type() == SCH_FIELD_T )
  471. {
  472. if( item->GetParent()->IsSelected() )
  473. {
  474. // parent will rotate us
  475. }
  476. else
  477. {
  478. SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
  479. field->Rotate( rotPoint );
  480. if( field->GetTextAngle() == TEXT_ANGLE_HORIZ )
  481. field->SetTextAngle( TEXT_ANGLE_VERT );
  482. else
  483. field->SetTextAngle( TEXT_ANGLE_HORIZ );
  484. // Now that we're moving a field, they're no longer autoplaced.
  485. static_cast<SCH_ITEM*>( field->GetParent() )->ClearFieldsAutoplaced();
  486. }
  487. }
  488. else
  489. {
  490. item->Rotate( rotPoint );
  491. }
  492. }
  493. connections |= item->IsConnectable();
  494. m_frame->UpdateItem( item );
  495. }
  496. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  497. if( moving )
  498. {
  499. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  500. }
  501. else
  502. {
  503. if( selection.IsHover() )
  504. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  505. if( connections )
  506. m_frame->TestDanglingEnds();
  507. m_frame->OnModify();
  508. }
  509. return 0;
  510. }
  511. int SCH_EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
  512. {
  513. EE_SELECTION& selection = m_selectionTool->RequestSelection( rotatableItems );
  514. if( selection.GetSize() == 0 )
  515. return 0;
  516. wxPoint mirrorPoint;
  517. bool vertical = ( aEvent.Matches( EE_ACTIONS::mirrorV.MakeEvent() ) );
  518. SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
  519. bool connections = false;
  520. bool moving = item->IsMoving();
  521. if( selection.GetSize() == 1 )
  522. {
  523. if( !moving )
  524. saveCopyInUndoList( item, UNDO_REDO::CHANGED );
  525. switch( item->Type() )
  526. {
  527. case SCH_SYMBOL_T:
  528. {
  529. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  530. if( vertical )
  531. symbol->SetOrientation( SYM_MIRROR_X );
  532. else
  533. symbol->SetOrientation( SYM_MIRROR_Y );
  534. if( m_frame->eeconfig()->m_AutoplaceFields.enable )
  535. symbol->AutoAutoplaceFields( m_frame->GetScreen() );
  536. break;
  537. }
  538. case SCH_TEXT_T:
  539. case SCH_LABEL_T:
  540. case SCH_GLOBAL_LABEL_T:
  541. case SCH_HIER_LABEL_T:
  542. {
  543. SCH_TEXT* textItem = static_cast<SCH_TEXT*>( item );
  544. textItem->MirrorSpinStyle( !vertical );
  545. break;
  546. }
  547. case SCH_SHEET_PIN_T:
  548. {
  549. // mirror within parent sheet
  550. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
  551. SCH_SHEET* sheet = pin->GetParent();
  552. if( vertical )
  553. pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
  554. else
  555. pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
  556. break;
  557. }
  558. case SCH_BUS_BUS_ENTRY_T:
  559. case SCH_BUS_WIRE_ENTRY_T:
  560. if( vertical )
  561. item->MirrorVertically( item->GetPosition().y );
  562. else
  563. item->MirrorHorizontally( item->GetPosition().x );
  564. break;
  565. case SCH_FIELD_T:
  566. {
  567. SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
  568. if( vertical )
  569. field->SetVertJustify( (EDA_TEXT_VJUSTIFY_T)-field->GetVertJustify() );
  570. else
  571. field->SetHorizJustify( (EDA_TEXT_HJUSTIFY_T)-field->GetHorizJustify() );
  572. // Now that we're re-justifying a field, they're no longer autoplaced.
  573. static_cast<SCH_ITEM*>( item->GetParent() )->ClearFieldsAutoplaced();
  574. break;
  575. }
  576. case SCH_BITMAP_T:
  577. if( vertical )
  578. item->MirrorVertically( item->GetPosition().y );
  579. else
  580. item->MirrorHorizontally( item->GetPosition().x );
  581. // The bitmap is cached in Opengl: clear the cache to redraw
  582. getView()->RecacheAllItems();
  583. break;
  584. case SCH_SHEET_T:
  585. // Mirror the sheet on itself. Sheets do not have a anchor point.
  586. mirrorPoint = m_frame->GetNearestHalfGridPosition( item->GetBoundingBox().Centre() );
  587. if( vertical )
  588. item->MirrorVertically( mirrorPoint.y );
  589. else
  590. item->MirrorHorizontally( mirrorPoint.x );
  591. break;
  592. default:
  593. break;
  594. }
  595. connections = item->IsConnectable();
  596. m_frame->UpdateItem( item );
  597. }
  598. else if( selection.GetSize() > 1 )
  599. {
  600. mirrorPoint = m_frame->GetNearestHalfGridPosition( (wxPoint)selection.GetCenter() );
  601. for( unsigned ii = 0; ii < selection.GetSize(); ii++ )
  602. {
  603. item = static_cast<SCH_ITEM*>( selection.GetItem( ii ) );
  604. if( !moving )
  605. saveCopyInUndoList( item, UNDO_REDO::CHANGED, ii > 0 );
  606. if( item->Type() == SCH_SHEET_PIN_T )
  607. {
  608. if( item->GetParent()->IsSelected() )
  609. {
  610. // parent will mirror us
  611. }
  612. else
  613. {
  614. // mirror within parent sheet
  615. SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( item );
  616. SCH_SHEET* sheet = pin->GetParent();
  617. if( vertical )
  618. pin->MirrorVertically( sheet->GetBoundingBox().GetCenter().y );
  619. else
  620. pin->MirrorHorizontally( sheet->GetBoundingBox().GetCenter().x );
  621. }
  622. }
  623. else
  624. {
  625. if( vertical )
  626. item->MirrorVertically( mirrorPoint.y );
  627. else
  628. item->MirrorHorizontally( mirrorPoint.x );
  629. }
  630. connections |= item->IsConnectable();
  631. m_frame->UpdateItem( item );
  632. }
  633. }
  634. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  635. // Update R-Tree for modified items
  636. for( EDA_ITEM* selected : selection )
  637. updateItem( selected, true );
  638. if( item->IsMoving() )
  639. {
  640. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  641. }
  642. else
  643. {
  644. if( selection.IsHover() )
  645. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  646. if( connections )
  647. m_frame->TestDanglingEnds();
  648. m_frame->OnModify();
  649. }
  650. return 0;
  651. }
  652. int SCH_EDIT_TOOL::RepeatDrawItem( const TOOL_EVENT& aEvent )
  653. {
  654. SCH_ITEM* sourceItem = m_frame->GetRepeatItem();
  655. if( !sourceItem )
  656. return 0;
  657. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  658. SCH_ITEM* newItem = sourceItem->Duplicate();
  659. bool performDrag = false;
  660. // If cloning a symbol then put into 'move' mode.
  661. if( newItem->Type() == SCH_SYMBOL_T )
  662. {
  663. wxPoint cursorPos = (wxPoint) getViewControls()->GetCursorPosition( true );
  664. newItem->Move( cursorPos - newItem->GetPosition() );
  665. performDrag = true;
  666. }
  667. else
  668. {
  669. if( m_isSymbolEditor )
  670. {
  671. auto* cfg = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
  672. if( dynamic_cast<SCH_TEXT*>( newItem ) )
  673. {
  674. SCH_TEXT* text = static_cast<SCH_TEXT*>( newItem );
  675. text->IncrementLabel( cfg->m_Repeat.label_delta );
  676. }
  677. newItem->Move( wxPoint( Mils2iu( cfg->m_Repeat.x_step ),
  678. Mils2iu( cfg->m_Repeat.y_step ) ) );
  679. }
  680. else
  681. {
  682. EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
  683. if( dynamic_cast<SCH_TEXT*>( newItem ) )
  684. {
  685. SCH_TEXT* text = static_cast<SCH_TEXT*>( newItem );
  686. // If incrementing tries to go below zero, tell user why the value is repeated
  687. if( !text->IncrementLabel( cfg->m_Drawing.repeat_label_increment ) )
  688. m_frame->ShowInfoBarWarning( _( "Label value cannot go below zero" ), true );
  689. }
  690. newItem->Move( wxPoint( Mils2iu( cfg->m_Drawing.default_repeat_offset_x ),
  691. Mils2iu( cfg->m_Drawing.default_repeat_offset_y ) ) );
  692. }
  693. }
  694. newItem->SetFlags( IS_NEW );
  695. m_frame->AddToScreen( newItem, m_frame->GetScreen() );
  696. m_frame->SaveCopyInUndoList( m_frame->GetScreen(), newItem, UNDO_REDO::NEWITEM, false );
  697. // Symbols need to be handled by the move tool. The move tool will handle schematic
  698. // cleanup routines
  699. if( performDrag )
  700. m_toolMgr->RunAction( EE_ACTIONS::move, true );
  701. newItem->ClearFlags();
  702. if( !performDrag && newItem->IsConnectable() )
  703. {
  704. EE_SELECTION new_sel;
  705. new_sel.Add( newItem );
  706. m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &new_sel );
  707. m_frame->RecalculateConnections( LOCAL_CLEANUP );
  708. m_frame->TestDanglingEnds();
  709. }
  710. m_frame->GetCanvas()->Refresh();
  711. m_frame->OnModify();
  712. // Save newItem at the new position.
  713. m_frame->SaveCopyForRepeatItem( newItem );
  714. return 0;
  715. }
  716. static KICAD_T deletableItems[] =
  717. {
  718. SCH_MARKER_T,
  719. SCH_JUNCTION_T,
  720. SCH_LINE_T,
  721. SCH_BUS_BUS_ENTRY_T,
  722. SCH_BUS_WIRE_ENTRY_T,
  723. SCH_TEXT_T,
  724. SCH_LABEL_T,
  725. SCH_GLOBAL_LABEL_T,
  726. SCH_HIER_LABEL_T,
  727. SCH_NO_CONNECT_T,
  728. SCH_SHEET_T,
  729. SCH_SHEET_PIN_T,
  730. SCH_SYMBOL_T,
  731. SCH_BITMAP_T,
  732. EOT
  733. };
  734. int SCH_EDIT_TOOL::DoDelete( const TOOL_EVENT& aEvent )
  735. {
  736. SCH_SCREEN* screen = m_frame->GetScreen();
  737. auto items = m_selectionTool->RequestSelection( deletableItems ).GetItems();
  738. bool appendToUndo = false;
  739. std::vector<wxPoint> pts;
  740. if( items.empty() )
  741. return 0;
  742. // Don't leave a freed pointer in the selection
  743. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  744. for( EDA_ITEM* item : items )
  745. item->ClearFlags( STRUCT_DELETED );
  746. for( EDA_ITEM* item : items )
  747. {
  748. SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item );
  749. if( !sch_item )
  750. continue;
  751. if( sch_item->IsConnectable() )
  752. {
  753. std::vector<wxPoint> tmp_pts = sch_item->GetConnectionPoints();
  754. pts.insert( pts.end(), tmp_pts.begin(), tmp_pts.end() );
  755. }
  756. if( sch_item->Type() == SCH_JUNCTION_T )
  757. {
  758. sch_item->SetFlags( STRUCT_DELETED );
  759. // clean up junctions at the end
  760. }
  761. else if( sch_item->Type() == SCH_SHEET_PIN_T )
  762. {
  763. SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) sch_item;
  764. SCH_SHEET* sheet = pin->GetParent();
  765. if( !alg::contains( items, sheet ) )
  766. {
  767. pin->SetFlags( STRUCT_DELETED );
  768. saveCopyInUndoList( item, UNDO_REDO::DELETED, appendToUndo );
  769. appendToUndo = true;
  770. updateItem( pin, false );
  771. sheet->RemovePin( pin );
  772. }
  773. }
  774. else
  775. {
  776. sch_item->SetFlags( STRUCT_DELETED );
  777. saveCopyInUndoList( item, UNDO_REDO::DELETED, appendToUndo );
  778. appendToUndo = true;
  779. updateItem( sch_item, false );
  780. m_frame->RemoveFromScreen( sch_item, m_frame->GetScreen() );
  781. if( sch_item->Type() == SCH_SHEET_T )
  782. m_frame->UpdateHierarchyNavigator();
  783. }
  784. }
  785. for( auto point : pts )
  786. {
  787. SCH_ITEM* junction = screen->GetItem( point, 0, SCH_JUNCTION_T );
  788. if( !junction )
  789. continue;
  790. if( junction->HasFlag( STRUCT_DELETED ) || !screen->IsJunctionNeeded( point ) )
  791. m_frame->DeleteJunction( junction, appendToUndo );
  792. }
  793. m_frame->TestDanglingEnds();
  794. m_frame->GetCanvas()->Refresh();
  795. m_frame->OnModify();
  796. return 0;
  797. }
  798. #define HITTEST_THRESHOLD_PIXELS 5
  799. int SCH_EDIT_TOOL::DeleteItemCursor( const TOOL_EVENT& aEvent )
  800. {
  801. std::string tool = aEvent.GetCommandStr().get();
  802. PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
  803. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  804. m_pickerItem = nullptr;
  805. // Deactivate other tools; particularly important if another PICKER is currently running
  806. Activate();
  807. picker->SetCursor( KICURSOR::REMOVE );
  808. picker->SetSnapping( false );
  809. picker->SetClickHandler(
  810. [this]( const VECTOR2D& aPosition ) -> bool
  811. {
  812. if( m_pickerItem )
  813. {
  814. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  815. selectionTool->UnbrightenItem( m_pickerItem );
  816. selectionTool->AddItemToSel( m_pickerItem, true /*quiet mode*/ );
  817. m_toolMgr->RunAction( ACTIONS::doDelete, true );
  818. m_pickerItem = nullptr;
  819. }
  820. return true;
  821. } );
  822. picker->SetMotionHandler(
  823. [this]( const VECTOR2D& aPos )
  824. {
  825. EE_COLLECTOR collector;
  826. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  827. collector.Collect( m_frame->GetScreen(), deletableItems, (wxPoint) aPos );
  828. EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  829. selectionTool->GuessSelectionCandidates( collector, aPos );
  830. EDA_ITEM* item = collector.GetCount() == 1 ? collector[ 0 ] : nullptr;
  831. if( m_pickerItem != item )
  832. {
  833. if( m_pickerItem )
  834. selectionTool->UnbrightenItem( m_pickerItem );
  835. m_pickerItem = item;
  836. if( m_pickerItem )
  837. selectionTool->BrightenItem( m_pickerItem );
  838. }
  839. } );
  840. picker->SetFinalizeHandler(
  841. [this]( const int& aFinalState )
  842. {
  843. if( m_pickerItem )
  844. m_toolMgr->GetTool<EE_SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
  845. // Wake the selection tool after exiting to ensure the cursor gets updated
  846. m_toolMgr->RunAction( EE_ACTIONS::selectionActivate, false );
  847. } );
  848. m_toolMgr->RunAction( ACTIONS::pickerTool, true, &tool );
  849. return 0;
  850. }
  851. void SCH_EDIT_TOOL::editFieldText( SCH_FIELD* aField )
  852. {
  853. // Save old symbol in undo list if not already in edit, or moving.
  854. if( aField->GetEditFlags() == 0 ) // i.e. not edited, or moved
  855. saveCopyInUndoList( aField, UNDO_REDO::CHANGED );
  856. wxString title = wxString::Format( _( "Edit %s Field" ), TitleCaps( aField->GetName() ) );
  857. DIALOG_SCH_EDIT_ONE_FIELD dlg( m_frame, title, aField );
  858. // The footprint field dialog can invoke a KIWAY_PLAYER so we must use a quasi-modal
  859. if( dlg.ShowQuasiModal() != wxID_OK )
  860. return;
  861. dlg.UpdateField( aField, &m_frame->GetCurrentSheet() );
  862. if( m_frame->eeconfig()->m_AutoplaceFields.enable || aField->GetParent()->Type() == SCH_SHEET_T )
  863. static_cast<SCH_ITEM*>( aField->GetParent() )->AutoAutoplaceFields( m_frame->GetScreen() );
  864. m_frame->UpdateItem( aField );
  865. m_frame->OnModify();
  866. // This must go after OnModify() so that the connectivity graph will have been updated.
  867. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  868. }
  869. int SCH_EDIT_TOOL::EditField( const TOOL_EVENT& aEvent )
  870. {
  871. static KICAD_T Nothing[] = { EOT };
  872. static KICAD_T CmpOrReference[] = { SCH_FIELD_LOCATE_REFERENCE_T, SCH_SYMBOL_T, EOT };
  873. static KICAD_T CmpOrValue[] = { SCH_FIELD_LOCATE_VALUE_T, SCH_SYMBOL_T, EOT };
  874. static KICAD_T CmpOrFootprint[] = { SCH_FIELD_LOCATE_FOOTPRINT_T, SCH_SYMBOL_T, EOT };
  875. KICAD_T* filter = Nothing;
  876. if( aEvent.IsAction( &EE_ACTIONS::editReference ) )
  877. filter = CmpOrReference;
  878. else if( aEvent.IsAction( &EE_ACTIONS::editValue ) )
  879. filter = CmpOrValue;
  880. else if( aEvent.IsAction( &EE_ACTIONS::editFootprint ) )
  881. filter = CmpOrFootprint;
  882. EE_SELECTION& selection = m_selectionTool->RequestSelection( filter );
  883. if( selection.Size() != 1 )
  884. return 0;
  885. SCH_ITEM* item = (SCH_ITEM*) selection.Front();
  886. if( item->Type() == SCH_SYMBOL_T )
  887. {
  888. SCH_SYMBOL* symbol = (SCH_SYMBOL*) item;
  889. if( aEvent.IsAction( &EE_ACTIONS::editReference ) )
  890. editFieldText( symbol->GetField( REFERENCE_FIELD ) );
  891. else if( aEvent.IsAction( &EE_ACTIONS::editValue ) )
  892. editFieldText( symbol->GetField( VALUE_FIELD ) );
  893. else if( aEvent.IsAction( &EE_ACTIONS::editFootprint ) )
  894. editFieldText( symbol->GetField( FOOTPRINT_FIELD ) );
  895. }
  896. else if( item->Type() == SCH_FIELD_T )
  897. {
  898. editFieldText( (SCH_FIELD*) item );
  899. }
  900. if( selection.IsHover() )
  901. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  902. return 0;
  903. }
  904. int SCH_EDIT_TOOL::AutoplaceFields( const TOOL_EVENT& aEvent )
  905. {
  906. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::FieldOwners );
  907. if( selection.Empty() )
  908. return 0;
  909. SCH_ITEM* item = static_cast<SCH_ITEM*>( selection.Front() );
  910. if( !item->IsNew() )
  911. saveCopyInUndoList( item, UNDO_REDO::CHANGED );
  912. item->AutoplaceFields( m_frame->GetScreen(), /* aManual */ true );
  913. updateItem( item, true );
  914. m_frame->OnModify();
  915. if( selection.IsHover() )
  916. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  917. return 0;
  918. }
  919. int SCH_EDIT_TOOL::ChangeSymbols( const TOOL_EVENT& aEvent )
  920. {
  921. SCH_SYMBOL* selectedSymbol = nullptr;
  922. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::SymbolsOnly );
  923. if( !selection.Empty() )
  924. selectedSymbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
  925. DIALOG_CHANGE_SYMBOLS::MODE mode = DIALOG_CHANGE_SYMBOLS::MODE::UPDATE;
  926. if( aEvent.IsAction( &EE_ACTIONS::changeSymbol )
  927. || aEvent.IsAction( &EE_ACTIONS::changeSymbols ) )
  928. {
  929. mode = DIALOG_CHANGE_SYMBOLS::MODE::CHANGE;
  930. }
  931. DIALOG_CHANGE_SYMBOLS dlg( m_frame, selectedSymbol, mode );
  932. dlg.ShowQuasiModal();
  933. return 0;
  934. }
  935. int SCH_EDIT_TOOL::ConvertDeMorgan( const TOOL_EVENT& aEvent )
  936. {
  937. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::SymbolsOnly );
  938. if( selection.Empty() )
  939. return 0;
  940. SCH_SYMBOL* symbol = (SCH_SYMBOL*) selection.Front();
  941. if( aEvent.IsAction( &EE_ACTIONS::showDeMorganStandard )
  942. && symbol->GetConvert() == LIB_ITEM::LIB_CONVERT::BASE )
  943. {
  944. return 0;
  945. }
  946. if( aEvent.IsAction( &EE_ACTIONS::showDeMorganAlternate )
  947. && symbol->GetConvert() != LIB_ITEM::LIB_CONVERT::DEMORGAN )
  948. {
  949. return 0;
  950. }
  951. if( !symbol->IsNew() )
  952. saveCopyInUndoList( symbol, UNDO_REDO::CHANGED );
  953. m_frame->ConvertPart( symbol );
  954. if( symbol->IsNew() )
  955. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  956. if( selection.IsHover() )
  957. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  958. return 0;
  959. }
  960. int SCH_EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
  961. {
  962. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::EditableItems );
  963. if( selection.Empty() )
  964. {
  965. if( getView()->IsLayerVisible( LAYER_SCHEMATIC_DRAWINGSHEET ) )
  966. {
  967. DS_PROXY_VIEW_ITEM* ds = m_frame->GetCanvas()->GetView()->GetDrawingSheet();
  968. VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
  969. if( ds && ds->HitTestDrawingSheetItems( getView(), (wxPoint) cursorPos ) )
  970. m_toolMgr->RunAction( ACTIONS::pageSettings );
  971. }
  972. return 0;
  973. }
  974. SCH_ITEM* item = (SCH_ITEM*) selection.Front();
  975. switch( item->Type() )
  976. {
  977. case SCH_LINE_T:
  978. case SCH_BUS_WIRE_ENTRY_T:
  979. if( !selection.AllItemsHaveLineStroke() )
  980. return 0;
  981. break;
  982. case SCH_JUNCTION_T:
  983. if( !selection.AreAllItemsIdentical() )
  984. return 0;
  985. break;
  986. default:
  987. if( selection.Size() > 1 )
  988. return 0;
  989. break;
  990. }
  991. switch( item->Type() )
  992. {
  993. case SCH_SYMBOL_T:
  994. {
  995. SCH_SYMBOL* symbol = (SCH_SYMBOL*) item;
  996. DIALOG_SYMBOL_PROPERTIES symbolPropsDialog( m_frame, symbol );
  997. // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
  998. // frame. Therefore this dialog as a modal frame parent, MUST be run under
  999. // quasimodal mode for the quasimodal frame support to work. So don't use
  1000. // the QUASIMODAL macros here.
  1001. int retval = symbolPropsDialog.ShowQuasiModal();
  1002. if( retval == SYMBOL_PROPS_EDIT_OK )
  1003. {
  1004. if( m_frame->eeconfig()->m_AutoplaceFields.enable )
  1005. symbol->AutoAutoplaceFields( m_frame->GetScreen() );
  1006. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  1007. m_frame->OnModify();
  1008. }
  1009. else if( retval == SYMBOL_PROPS_EDIT_SCHEMATIC_SYMBOL )
  1010. {
  1011. auto editor = (SYMBOL_EDIT_FRAME*) m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR,
  1012. true );
  1013. editor->LoadSymbolFromSchematic( symbol );
  1014. editor->Show( true );
  1015. editor->Raise();
  1016. }
  1017. else if( retval == SYMBOL_PROPS_EDIT_LIBRARY_SYMBOL )
  1018. {
  1019. auto editor = (SYMBOL_EDIT_FRAME*) m_frame->Kiway().Player( FRAME_SCH_SYMBOL_EDITOR,
  1020. true );
  1021. editor->LoadSymbol( symbol->GetLibId(), symbol->GetUnit(), symbol->GetConvert() );
  1022. editor->Show( true );
  1023. editor->Raise();
  1024. }
  1025. else if( retval == SYMBOL_PROPS_WANT_UPDATE_SYMBOL )
  1026. {
  1027. DIALOG_CHANGE_SYMBOLS dlg( m_frame, symbol, DIALOG_CHANGE_SYMBOLS::MODE::UPDATE );
  1028. dlg.ShowQuasiModal();
  1029. }
  1030. else if( retval == SYMBOL_PROPS_WANT_EXCHANGE_SYMBOL )
  1031. {
  1032. DIALOG_CHANGE_SYMBOLS dlg( m_frame, symbol, DIALOG_CHANGE_SYMBOLS::MODE::CHANGE );
  1033. dlg.ShowQuasiModal();
  1034. }
  1035. }
  1036. break;
  1037. case SCH_SHEET_T:
  1038. {
  1039. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
  1040. bool doClearAnnotation;
  1041. bool doRefresh = false;
  1042. // Keep track of existing sheet paths. EditSheet() can modify this list.
  1043. // Note that we use the validity checking/repairing version here just to make sure
  1044. // we've got a valid hierarchy to begin with.
  1045. SCH_SHEET_LIST initial_sheetpathList( &m_frame->Schematic().Root(), true );
  1046. doRefresh = m_frame->EditSheetProperties( sheet, &m_frame->GetCurrentSheet(),
  1047. &doClearAnnotation );
  1048. // If the sheet file is changed and new sheet contents are loaded then we have to
  1049. // clear the annotations on the new content (as it may have been set from some other
  1050. // sheet path reference)
  1051. if( doClearAnnotation )
  1052. {
  1053. SCH_SCREENS screensList( &m_frame->Schematic().Root() );
  1054. // We clear annotation of new sheet paths here:
  1055. screensList.ClearAnnotationOfNewSheetPaths( initial_sheetpathList );
  1056. // Clear annotation of g_CurrentSheet itself, because its sheetpath is not a new
  1057. // path, but symbols managed by its sheet path must have their annotation cleared
  1058. // because they are new:
  1059. sheet->GetScreen()->ClearAnnotation( &m_frame->GetCurrentSheet() );
  1060. }
  1061. if( doRefresh )
  1062. {
  1063. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  1064. m_frame->GetCanvas()->Refresh();
  1065. m_frame->UpdateHierarchyNavigator();
  1066. }
  1067. break;
  1068. }
  1069. case SCH_SHEET_PIN_T:
  1070. {
  1071. SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) item;
  1072. DIALOG_SHEET_PIN_PROPERTIES dlg( m_frame, pin );
  1073. // QuasiModal required for help dialog
  1074. if( dlg.ShowQuasiModal() == wxID_OK )
  1075. {
  1076. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  1077. m_frame->OnModify();
  1078. }
  1079. }
  1080. break;
  1081. case SCH_TEXT_T:
  1082. case SCH_LABEL_T:
  1083. case SCH_GLOBAL_LABEL_T:
  1084. case SCH_HIER_LABEL_T:
  1085. {
  1086. DIALOG_LABEL_EDITOR dlg( m_frame, (SCH_TEXT*) item );
  1087. // Must be quasi modal for syntax help
  1088. if( dlg.ShowQuasiModal() == wxID_OK )
  1089. {
  1090. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  1091. m_frame->OnModify();
  1092. }
  1093. }
  1094. break;
  1095. case SCH_FIELD_T:
  1096. editFieldText( (SCH_FIELD*) item );
  1097. break;
  1098. case SCH_BITMAP_T:
  1099. {
  1100. SCH_BITMAP* bitmap = (SCH_BITMAP*) item;
  1101. DIALOG_IMAGE_EDITOR dlg( m_frame, bitmap->GetImage() );
  1102. if( dlg.ShowModal() == wxID_OK )
  1103. {
  1104. // save old image in undo list if not already in edit
  1105. if( bitmap->GetEditFlags() == 0 )
  1106. saveCopyInUndoList( bitmap, UNDO_REDO::CHANGED );
  1107. dlg.TransferToImage( bitmap->GetImage() );
  1108. // The bitmap is cached in Opengl: clear the cache in case it has become invalid
  1109. getView()->RecacheAllItems();
  1110. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  1111. m_frame->OnModify();
  1112. }
  1113. }
  1114. break;
  1115. case SCH_LINE_T:
  1116. case SCH_BUS_WIRE_ENTRY_T:
  1117. {
  1118. std::deque<SCH_ITEM*> strokeItems;
  1119. for( auto selItem : selection.Items() )
  1120. {
  1121. SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( selItem );
  1122. if( schItem && schItem->HasLineStroke() )
  1123. strokeItems.push_back( schItem );
  1124. else
  1125. return 0;
  1126. }
  1127. DIALOG_LINE_WIRE_BUS_PROPERTIES dlg( m_frame, strokeItems );
  1128. if( dlg.ShowModal() == wxID_OK )
  1129. {
  1130. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  1131. m_frame->OnModify();
  1132. }
  1133. }
  1134. break;
  1135. case SCH_JUNCTION_T:
  1136. {
  1137. std::deque<SCH_JUNCTION*> junctions;
  1138. for( auto selItem : selection.Items() )
  1139. {
  1140. SCH_JUNCTION* junction = dynamic_cast<SCH_JUNCTION*>( selItem );
  1141. wxCHECK( junction, 0 );
  1142. junctions.push_back( junction );
  1143. }
  1144. DIALOG_JUNCTION_PROPS dlg( m_frame, junctions );
  1145. if( dlg.ShowModal() == wxID_OK )
  1146. {
  1147. m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
  1148. m_frame->OnModify();
  1149. }
  1150. }
  1151. break;
  1152. case SCH_MARKER_T: // These items have no properties to edit
  1153. case SCH_NO_CONNECT_T:
  1154. break;
  1155. default: // Unexpected item
  1156. wxFAIL_MSG( wxString( "Cannot edit schematic item type " ) + item->GetClass() );
  1157. }
  1158. updateItem( item, true );
  1159. if( selection.IsHover() )
  1160. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  1161. return 0;
  1162. }
  1163. int SCH_EDIT_TOOL::ChangeTextType( const TOOL_EVENT& aEvent )
  1164. {
  1165. KICAD_T convertTo = aEvent.Parameter<KICAD_T>();
  1166. KICAD_T allTextTypes[] = { SCH_LABEL_T, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_TEXT_T, EOT };
  1167. EE_SELECTION& selection = m_selectionTool->RequestSelection( allTextTypes );
  1168. for( unsigned int i = 0; i < selection.GetSize(); ++i )
  1169. {
  1170. SCH_TEXT* text = dynamic_cast<SCH_TEXT*>( selection.GetItem( i ) );
  1171. if( text && text->Type() != convertTo )
  1172. {
  1173. bool selected = text->IsSelected();
  1174. SCH_TEXT* newtext = nullptr;
  1175. const wxPoint& position = text->GetPosition();
  1176. LABEL_SPIN_STYLE orientation = text->GetLabelSpinStyle();
  1177. wxString txt = UnescapeString( text->GetText() );
  1178. // There can be characters in a SCH_TEXT object that can break labels so we have to
  1179. // fix them here.
  1180. if( text->Type() == SCH_TEXT_T )
  1181. {
  1182. txt.Replace( "\n", "_" );
  1183. txt.Replace( "\r", "_" );
  1184. txt.Replace( "\t", "_" );
  1185. txt.Replace( " ", "_" );
  1186. }
  1187. // label strings are "escaped" i.e. a '/' is replaced by "{slash}"
  1188. if( convertTo != SCH_TEXT_T )
  1189. txt = EscapeString( txt, CTX_NETNAME );
  1190. switch( convertTo )
  1191. {
  1192. case SCH_LABEL_T: newtext = new SCH_LABEL( position, txt ); break;
  1193. case SCH_GLOBAL_LABEL_T: newtext = new SCH_GLOBALLABEL( position, txt ); break;
  1194. case SCH_HIER_LABEL_T: newtext = new SCH_HIERLABEL( position, txt ); break;
  1195. case SCH_TEXT_T: newtext = new SCH_TEXT( position, txt ); break;
  1196. default:
  1197. wxFAIL_MSG( wxString::Format( "Invalid text type: %d.", convertTo ) );
  1198. return 0;
  1199. }
  1200. // Copy the old text item settings to the new one. Justifications are not copied
  1201. // because they are not used in labels. Justifications will be set to default value
  1202. // in the new text item type.
  1203. //
  1204. newtext->SetFlags( text->GetEditFlags() );
  1205. newtext->SetShape( text->GetShape() );
  1206. newtext->SetLabelSpinStyle( orientation );
  1207. newtext->SetTextSize( text->GetTextSize() );
  1208. newtext->SetTextThickness( text->GetTextThickness() );
  1209. newtext->SetItalic( text->IsItalic() );
  1210. newtext->SetBold( text->IsBold() );
  1211. newtext->SetIsDangling( text->IsDangling() );
  1212. if( selected )
  1213. m_toolMgr->RunAction( EE_ACTIONS::removeItemFromSel, true, text );
  1214. if( !text->IsNew() )
  1215. {
  1216. saveCopyInUndoList( text, UNDO_REDO::DELETED );
  1217. saveCopyInUndoList( newtext, UNDO_REDO::NEWITEM, true );
  1218. m_frame->RemoveFromScreen( text, m_frame->GetScreen() );
  1219. m_frame->AddToScreen( newtext, m_frame->GetScreen() );
  1220. if( convertTo == SCH_GLOBAL_LABEL_T )
  1221. static_cast<SCH_GLOBALLABEL*>( newtext )->UpdateIntersheetRefProps();
  1222. }
  1223. if( selected )
  1224. m_toolMgr->RunAction( EE_ACTIONS::addItemToSel, true, newtext );
  1225. // Otherwise, pointer is owned by the undo stack
  1226. if( text->IsNew() )
  1227. delete text;
  1228. if( convertTo == SCH_TEXT_T )
  1229. {
  1230. if( newtext->IsDangling() )
  1231. {
  1232. newtext->SetIsDangling( false );
  1233. getView()->Update( newtext, KIGFX::REPAINT );
  1234. }
  1235. }
  1236. else
  1237. m_frame->TestDanglingEnds();
  1238. m_frame->OnModify();
  1239. }
  1240. }
  1241. if( selection.IsHover() )
  1242. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  1243. return 0;
  1244. }
  1245. int SCH_EDIT_TOOL::BreakWire( const TOOL_EVENT& aEvent )
  1246. {
  1247. wxPoint cursorPos = (wxPoint) getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  1248. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::WiresOnly );
  1249. std::vector<SCH_LINE*> lines;
  1250. for( auto& item : selection )
  1251. {
  1252. if( SCH_LINE* line = dyn_cast<SCH_LINE*>( item ) )
  1253. {
  1254. if( !line->IsEndPoint( cursorPos ) )
  1255. lines.push_back( line );
  1256. }
  1257. }
  1258. m_selectionTool->ClearSelection();
  1259. m_frame->StartNewUndo();
  1260. for( SCH_LINE* line : lines )
  1261. m_frame->BreakSegment( line, cursorPos );
  1262. if( !lines.empty() )
  1263. {
  1264. if( m_frame->GetScreen()->IsJunctionNeeded( cursorPos, true ) )
  1265. m_frame->AddJunction( m_frame->GetScreen(), cursorPos, true, false );
  1266. m_frame->TestDanglingEnds();
  1267. m_frame->OnModify();
  1268. m_frame->GetCanvas()->Refresh();
  1269. m_toolMgr->RunAction( EE_ACTIONS::drag );
  1270. }
  1271. return 0;
  1272. }
  1273. int SCH_EDIT_TOOL::CleanupSheetPins( const TOOL_EVENT& aEvent )
  1274. {
  1275. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::SheetsOnly );
  1276. SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
  1277. if( !sheet )
  1278. return 0;
  1279. if( !sheet->HasUndefinedPins() )
  1280. {
  1281. DisplayInfoMessage( m_frame, _( "There are no unreferenced pins in this sheet to remove." ) );
  1282. return 0;
  1283. }
  1284. if( !IsOK( m_frame, _( "Do you wish to delete the unreferenced pins from this sheet?" ) ) )
  1285. return 0;
  1286. saveCopyInUndoList( sheet, UNDO_REDO::CHANGED );
  1287. sheet->CleanupSheet();
  1288. updateItem( sheet, true );
  1289. m_frame->OnModify();
  1290. if( selection.IsHover() )
  1291. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  1292. return 0;
  1293. }
  1294. int SCH_EDIT_TOOL::EditPageNumber( const TOOL_EVENT& aEvent )
  1295. {
  1296. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::SheetsOnly );
  1297. if( selection.GetSize() > 1 )
  1298. return 0;
  1299. SCH_SHEET* sheet = (SCH_SHEET*) selection.Front();
  1300. SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
  1301. SCH_SCREEN* screen;
  1302. if( sheet )
  1303. {
  1304. // When changing the page number of a selected sheet, the current screen owns the sheet.
  1305. screen = m_frame->GetScreen();
  1306. instance.push_back( sheet );
  1307. }
  1308. else
  1309. {
  1310. SCH_SHEET_PATH prevInstance = instance;
  1311. // When change the page number in the screen, the previous screen owns the sheet.
  1312. if( prevInstance.size() )
  1313. {
  1314. prevInstance.pop_back();
  1315. screen = prevInstance.LastScreen();
  1316. }
  1317. else
  1318. {
  1319. // The root sheet and root screen are effectively the same thing.
  1320. screen = m_frame->GetScreen();
  1321. }
  1322. sheet = m_frame->GetCurrentSheet().Last();
  1323. }
  1324. wxString msg;
  1325. wxString sheetPath = instance.PathHumanReadable( false );
  1326. wxString pageNumber = instance.GetPageNumber();
  1327. msg.Printf( _( "Enter page number for sheet path%s" ),
  1328. ( sheetPath.Length() > 20 ) ? "\n" + sheetPath : " " + sheetPath );
  1329. wxTextEntryDialog dlg( m_frame, msg, _( "Edit Sheet Page Number" ), pageNumber );
  1330. dlg.SetTextValidator( wxFILTER_ALPHANUMERIC ); // No white space.
  1331. if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == instance.GetPageNumber() )
  1332. return 0;
  1333. m_frame->SaveCopyInUndoList( screen, sheet, UNDO_REDO::CHANGED, false );
  1334. instance.SetPageNumber( dlg.GetValue() );
  1335. if( instance == m_frame->GetCurrentSheet() )
  1336. {
  1337. m_frame->GetScreen()->SetPageNumber( dlg.GetValue() );
  1338. m_frame->OnPageSettingsChange();
  1339. }
  1340. m_frame->OnModify();
  1341. return 0;
  1342. }
  1343. void SCH_EDIT_TOOL::setTransitions()
  1344. {
  1345. Go( &SCH_EDIT_TOOL::RepeatDrawItem, EE_ACTIONS::repeatDrawItem.MakeEvent() );
  1346. Go( &SCH_EDIT_TOOL::Rotate, EE_ACTIONS::rotateCW.MakeEvent() );
  1347. Go( &SCH_EDIT_TOOL::Rotate, EE_ACTIONS::rotateCCW.MakeEvent() );
  1348. Go( &SCH_EDIT_TOOL::Mirror, EE_ACTIONS::mirrorV.MakeEvent() );
  1349. Go( &SCH_EDIT_TOOL::Mirror, EE_ACTIONS::mirrorH.MakeEvent() );
  1350. Go( &SCH_EDIT_TOOL::DoDelete, ACTIONS::doDelete.MakeEvent() );
  1351. Go( &SCH_EDIT_TOOL::DeleteItemCursor, ACTIONS::deleteTool.MakeEvent() );
  1352. Go( &SCH_EDIT_TOOL::Properties, EE_ACTIONS::properties.MakeEvent() );
  1353. Go( &SCH_EDIT_TOOL::EditField, EE_ACTIONS::editReference.MakeEvent() );
  1354. Go( &SCH_EDIT_TOOL::EditField, EE_ACTIONS::editValue.MakeEvent() );
  1355. Go( &SCH_EDIT_TOOL::EditField, EE_ACTIONS::editFootprint.MakeEvent() );
  1356. Go( &SCH_EDIT_TOOL::AutoplaceFields, EE_ACTIONS::autoplaceFields.MakeEvent() );
  1357. Go( &SCH_EDIT_TOOL::ChangeSymbols, EE_ACTIONS::changeSymbols.MakeEvent() );
  1358. Go( &SCH_EDIT_TOOL::ChangeSymbols, EE_ACTIONS::updateSymbols.MakeEvent() );
  1359. Go( &SCH_EDIT_TOOL::ChangeSymbols, EE_ACTIONS::changeSymbol.MakeEvent() );
  1360. Go( &SCH_EDIT_TOOL::ChangeSymbols, EE_ACTIONS::updateSymbol.MakeEvent() );
  1361. Go( &SCH_EDIT_TOOL::ConvertDeMorgan, EE_ACTIONS::toggleDeMorgan.MakeEvent() );
  1362. Go( &SCH_EDIT_TOOL::ConvertDeMorgan, EE_ACTIONS::showDeMorganStandard.MakeEvent() );
  1363. Go( &SCH_EDIT_TOOL::ConvertDeMorgan, EE_ACTIONS::showDeMorganAlternate.MakeEvent() );
  1364. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toLabel.MakeEvent() );
  1365. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toHLabel.MakeEvent() );
  1366. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toGLabel.MakeEvent() );
  1367. Go( &SCH_EDIT_TOOL::ChangeTextType, EE_ACTIONS::toText.MakeEvent() );
  1368. Go( &SCH_EDIT_TOOL::BreakWire, EE_ACTIONS::breakWire.MakeEvent() );
  1369. Go( &SCH_EDIT_TOOL::BreakWire, EE_ACTIONS::breakBus.MakeEvent() );
  1370. Go( &SCH_EDIT_TOOL::CleanupSheetPins, EE_ACTIONS::cleanupSheetPins.MakeEvent() );
  1371. Go( &SCH_EDIT_TOOL::GlobalEdit, EE_ACTIONS::editTextAndGraphics.MakeEvent() );
  1372. Go( &SCH_EDIT_TOOL::EditPageNumber, EE_ACTIONS::editPageNumber.MakeEvent() );
  1373. }