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.

2011 lines
68 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
4 years ago
5 years ago
5 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-2022 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 <core/typeinfo.h>
  25. #include <core/kicad_algo.h>
  26. #include <geometry/shape_compound.h>
  27. #include <ee_actions.h>
  28. #include <ee_collectors.h>
  29. #include <ee_selection_tool.h>
  30. #include <eeschema_id.h>
  31. #include <symbol_edit_frame.h>
  32. #include <lib_item.h>
  33. #include <symbol_viewer_frame.h>
  34. #include <math/util.h>
  35. #include <geometry/shape_rect.h>
  36. #include <menus_helpers.h>
  37. #include <sch_painter.h>
  38. #include <preview_items/selection_area.h>
  39. #include <sch_base_frame.h>
  40. #include <sch_symbol.h>
  41. #include <sch_field.h>
  42. #include <sch_edit_frame.h>
  43. #include <sch_item.h>
  44. #include <sch_line.h>
  45. #include <sch_bus_entry.h>
  46. #include <sch_junction.h>
  47. #include <sch_marker.h>
  48. #include <sch_sheet.h>
  49. #include <sch_sheet_pin.h>
  50. #include <lib_shape.h>
  51. #include <schematic.h>
  52. #include <tool/tool_event.h>
  53. #include <tool/tool_manager.h>
  54. #include <tools/ee_grid_helper.h>
  55. #include <tools/ee_point_editor.h>
  56. #include <tools/sch_line_wire_bus_tool.h>
  57. #include <tools/sch_editor_control.h>
  58. #include <trigo.h>
  59. #include <view/view.h>
  60. #include <view/view_controls.h>
  61. #include <wx/log.h>
  62. SELECTION_CONDITION EE_CONDITIONS::SingleSymbol = []( const SELECTION& aSel )
  63. {
  64. if( aSel.GetSize() == 1 )
  65. {
  66. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
  67. if( symbol )
  68. return !symbol->GetLibSymbolRef() || !symbol->GetLibSymbolRef()->IsPower();
  69. }
  70. return false;
  71. };
  72. SELECTION_CONDITION EE_CONDITIONS::SingleSymbolOrPower = []( const SELECTION& aSel )
  73. {
  74. return aSel.GetSize() == 1 && aSel.Front()->Type() == SCH_SYMBOL_T;
  75. };
  76. SELECTION_CONDITION EE_CONDITIONS::SingleDeMorganSymbol = []( const SELECTION& aSel )
  77. {
  78. if( aSel.GetSize() == 1 )
  79. {
  80. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
  81. if( symbol )
  82. return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->HasConversion();
  83. }
  84. return false;
  85. };
  86. SELECTION_CONDITION EE_CONDITIONS::SingleMultiUnitSymbol = []( const SELECTION& aSel )
  87. {
  88. if( aSel.GetSize() == 1 )
  89. {
  90. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
  91. if( symbol )
  92. return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->GetUnitCount() >= 2;
  93. }
  94. return false;
  95. };
  96. SELECTION_CONDITION EE_CONDITIONS::SingleNonExcludedMarker = []( const SELECTION& aSel )
  97. {
  98. if( aSel.CountType( SCH_MARKER_T ) != 1 )
  99. return false;
  100. return !static_cast<SCH_MARKER*>( aSel.Front() )->IsExcluded();
  101. };
  102. SELECTION_CONDITION EE_CONDITIONS::MultipleSymbolsOrPower = []( const SELECTION& aSel )
  103. {
  104. return aSel.GetSize() > 1 && aSel.OnlyContains( { SCH_SYMBOL_T } );
  105. };
  106. #define HITTEST_THRESHOLD_PIXELS 5
  107. EE_SELECTION_TOOL::EE_SELECTION_TOOL() :
  108. SELECTION_TOOL( "eeschema.InteractiveSelection" ),
  109. m_frame( nullptr ),
  110. m_nonModifiedCursor( KICURSOR::ARROW ),
  111. m_isSymbolEditor( false ),
  112. m_isSymbolViewer( false ),
  113. m_unit( 0 ),
  114. m_convert( 0 )
  115. {
  116. m_selection.Clear();
  117. }
  118. EE_SELECTION_TOOL::~EE_SELECTION_TOOL()
  119. {
  120. getView()->Remove( &m_selection );
  121. }
  122. using E_C = EE_CONDITIONS;
  123. static std::vector<KICAD_T> connectedTypes =
  124. {
  125. SCH_SYMBOL_LOCATE_POWER_T,
  126. SCH_PIN_T,
  127. SCH_ITEM_LOCATE_WIRE_T,
  128. SCH_ITEM_LOCATE_BUS_T,
  129. SCH_BUS_WIRE_ENTRY_T,
  130. SCH_BUS_BUS_ENTRY_T,
  131. SCH_LABEL_T,
  132. SCH_HIER_LABEL_T,
  133. SCH_GLOBAL_LABEL_T,
  134. SCH_SHEET_PIN_T,
  135. SCH_DIRECTIVE_LABEL_T,
  136. SCH_JUNCTION_T
  137. };
  138. bool EE_SELECTION_TOOL::Init()
  139. {
  140. m_frame = getEditFrame<SCH_BASE_FRAME>();
  141. SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
  142. SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  143. if( symbolEditorFrame )
  144. {
  145. m_isSymbolEditor = true;
  146. m_unit = symbolEditorFrame->GetUnit();
  147. m_convert = symbolEditorFrame->GetConvert();
  148. }
  149. else
  150. {
  151. m_isSymbolViewer = symbolViewerFrame != nullptr;
  152. }
  153. auto linesSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T,
  154. SCH_ITEM_LOCATE_GRAPHIC_LINE_T } );
  155. auto wireOrBusSelection = E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T } );
  156. auto connectedSelection = E_C::Count( 1 ) && E_C::OnlyTypes( connectedTypes );
  157. auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyTypes( { SCH_SHEET_T } );
  158. auto crossProbingSelection = E_C::MoreThan( 0 ) && E_C::HasTypes( { SCH_SYMBOL_T, SCH_PIN_T, SCH_SHEET_T } );
  159. auto schEditSheetPageNumberCondition =
  160. [&] ( const SELECTION& aSel )
  161. {
  162. if( m_isSymbolEditor || m_isSymbolViewer )
  163. return false;
  164. return E_C::LessThan( 2 )( aSel ) && E_C::OnlyTypes( { SCH_SHEET_T } )( aSel );
  165. };
  166. auto schEditCondition =
  167. [this] ( const SELECTION& aSel )
  168. {
  169. return !m_isSymbolEditor && !m_isSymbolViewer;
  170. };
  171. auto belowRootSheetCondition =
  172. [&]( const SELECTION& aSel )
  173. {
  174. SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  175. return editFrame
  176. && editFrame->GetCurrentSheet().Last() != &editFrame->Schematic().Root();
  177. };
  178. auto haveHighlight =
  179. [&]( const SELECTION& sel )
  180. {
  181. SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  182. return editFrame && editFrame->GetHighlightedConnection() != nullptr;
  183. };
  184. auto haveSymbol =
  185. [&]( const SELECTION& sel )
  186. {
  187. return m_isSymbolEditor &&
  188. static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
  189. };
  190. auto symbolDisplayNameIsEditable =
  191. [&]( const SELECTION& sel )
  192. {
  193. if ( !m_isSymbolEditor )
  194. return false;
  195. SYMBOL_EDIT_FRAME* symbEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  196. return symbEditorFrame
  197. && symbEditorFrame->GetCurSymbol()
  198. && symbEditorFrame->GetCurSymbol()->IsMulti()
  199. && symbEditorFrame->IsSymbolEditable()
  200. && !symbEditorFrame->IsSymbolAlias();
  201. };
  202. auto& menu = m_menu.GetMenu();
  203. menu.AddItem( EE_ACTIONS::clearHighlight, haveHighlight && EE_CONDITIONS::Idle, 1 );
  204. menu.AddSeparator( haveHighlight && EE_CONDITIONS::Idle, 1 );
  205. menu.AddItem( EE_ACTIONS::enterSheet, sheetSelection && EE_CONDITIONS::Idle, 2 );
  206. menu.AddItem( EE_ACTIONS::selectOnPCB, crossProbingSelection && EE_CONDITIONS::Idle, 2 );
  207. menu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 2 );
  208. menu.AddSeparator( 100 );
  209. menu.AddItem( EE_ACTIONS::drawWire, schEditCondition && EE_CONDITIONS::Empty, 100 );
  210. menu.AddItem( EE_ACTIONS::drawBus, schEditCondition && EE_CONDITIONS::Empty, 100 );
  211. menu.AddSeparator( 100 );
  212. menu.AddItem( EE_ACTIONS::finishWire, SCH_LINE_WIRE_BUS_TOOL::IsDrawingWire, 100 );
  213. menu.AddSeparator( 100 );
  214. menu.AddItem( EE_ACTIONS::finishBus, SCH_LINE_WIRE_BUS_TOOL::IsDrawingBus, 100 );
  215. menu.AddSeparator( 200 );
  216. menu.AddItem( EE_ACTIONS::selectConnection, connectedSelection && EE_CONDITIONS::Idle, 250 );
  217. menu.AddItem( EE_ACTIONS::placeJunction, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  218. menu.AddItem( EE_ACTIONS::placeLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  219. menu.AddItem( EE_ACTIONS::placeClassLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  220. menu.AddItem( EE_ACTIONS::placeGlobalLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  221. menu.AddItem( EE_ACTIONS::placeHierLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  222. menu.AddItem( EE_ACTIONS::breakWire, linesSelection && EE_CONDITIONS::Idle, 250 );
  223. menu.AddItem( EE_ACTIONS::slice, linesSelection && EE_CONDITIONS::Idle, 250 );
  224. menu.AddItem( EE_ACTIONS::importSheetPin, sheetSelection && EE_CONDITIONS::Idle, 250 );
  225. menu.AddItem( EE_ACTIONS::assignNetclass, connectedSelection && EE_CONDITIONS::Idle, 250 );
  226. menu.AddItem( EE_ACTIONS::editPageNumber, schEditSheetPageNumberCondition, 250 );
  227. menu.AddSeparator( 400 );
  228. menu.AddItem( EE_ACTIONS::symbolProperties, haveSymbol && EE_CONDITIONS::Empty, 400 );
  229. menu.AddItem( EE_ACTIONS::pinTable, haveSymbol && EE_CONDITIONS::Empty, 400 );
  230. menu.AddItem( EE_ACTIONS::setUnitDisplayName,
  231. haveSymbol && symbolDisplayNameIsEditable && EE_CONDITIONS::Empty, 400 );
  232. menu.AddSeparator( 1000 );
  233. m_frame->AddStandardSubMenus( m_menu );
  234. m_disambiguateTimer.SetOwner( this );
  235. Connect( wxEVT_TIMER, wxTimerEventHandler( EE_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
  236. return true;
  237. }
  238. void EE_SELECTION_TOOL::Reset( RESET_REASON aReason )
  239. {
  240. m_frame = getEditFrame<SCH_BASE_FRAME>();
  241. if( aReason == TOOL_BASE::MODEL_RELOAD )
  242. {
  243. // Remove pointers to the selected items from containers without changing their
  244. // properties (as they are already deleted while a new sheet is loaded)
  245. m_selection.Clear();
  246. getView()->GetPainter()->GetSettings()->SetHighlight( false );
  247. SYMBOL_EDIT_FRAME* symbolEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  248. SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
  249. if( symbolEditFrame )
  250. {
  251. m_isSymbolEditor = true;
  252. m_unit = symbolEditFrame->GetUnit();
  253. m_convert = symbolEditFrame->GetConvert();
  254. }
  255. else
  256. {
  257. m_isSymbolViewer = symbolViewerFrame != nullptr;
  258. }
  259. }
  260. else
  261. {
  262. // Restore previous properties of selected items and remove them from containers
  263. ClearSelection();
  264. }
  265. // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
  266. getView()->Remove( &m_selection );
  267. getView()->Add( &m_selection );
  268. }
  269. int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
  270. {
  271. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  272. KIID lastRolloverItem = niluuid;
  273. EE_GRID_HELPER grid( m_toolMgr );
  274. // Main loop: keep receiving events
  275. while( TOOL_EVENT* evt = Wait() )
  276. {
  277. bool selCancelled = false;
  278. bool displayWireCursor = false;
  279. bool displayBusCursor = false;
  280. bool displayLineCursor = false;
  281. KIID rolloverItem = lastRolloverItem;
  282. // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
  283. setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
  284. evt->Modifier( MD_ALT ) );
  285. MOUSE_DRAG_ACTION drag_action = m_frame->GetDragAction();
  286. if( evt->IsMouseDown( BUT_LEFT ) )
  287. {
  288. // Avoid triggering when running under other tools
  289. // Distinguish point editor from selection modification by checking modifiers
  290. if( m_frame->ToolStackIsEmpty() && m_toolMgr->GetTool<EE_POINT_EDITOR>()
  291. && ( !m_toolMgr->GetTool<EE_POINT_EDITOR>()->HasPoint() || hasModifier() ) )
  292. {
  293. m_originalCursor = m_toolMgr->GetMousePosition();
  294. m_disambiguateTimer.StartOnce( 500 );
  295. }
  296. }
  297. // Single click? Select single object
  298. else if( evt->IsClick( BUT_LEFT ) )
  299. {
  300. // If the timer has stopped, then we have already run the disambiguate routine
  301. // and we don't want to register an extra click here
  302. if( !m_disambiguateTimer.IsRunning() )
  303. {
  304. evt->SetPassEvent();
  305. continue;
  306. }
  307. m_disambiguateTimer.Stop();
  308. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  309. schframe->FocusOnItem( nullptr );
  310. // Collect items at the clicked location (doesn't select them yet)
  311. EE_COLLECTOR collector;
  312. CollectHits( collector, evt->Position() );
  313. narrowSelection( collector, evt->Position(), false );
  314. if( collector.GetCount() == 1 && !m_isSymbolEditor && !hasModifier() )
  315. {
  316. OPT_TOOL_EVENT autostart = autostartEvent( evt, grid, collector[0] );
  317. if( autostart )
  318. {
  319. DRAW_SEGMENT_EVENT_PARAMS* params = new DRAW_SEGMENT_EVENT_PARAMS();
  320. params->layer = autostart->Parameter<DRAW_SEGMENT_EVENT_PARAMS*>()->layer;
  321. params->quitOnDraw = true;
  322. params->sourceSegment = dynamic_cast<SCH_LINE*>( collector[0] );
  323. autostart->SetParameter( params );
  324. m_toolMgr->ProcessEvent( *autostart );
  325. selCancelled = true;
  326. }
  327. else if( collector[0]->IsHypertext() )
  328. {
  329. collector[ 0 ]->DoHypertextAction( m_frame );
  330. selCancelled = true;
  331. }
  332. }
  333. if( !selCancelled )
  334. {
  335. selectPoint( collector, evt->Position(), nullptr, nullptr, m_additive,
  336. m_subtractive, m_exclusive_or );
  337. m_selection.SetIsHover( false );
  338. }
  339. }
  340. else if( evt->IsClick( BUT_RIGHT ) )
  341. {
  342. m_disambiguateTimer.Stop();
  343. // right click? if there is any object - show the context menu
  344. if( m_selection.Empty() )
  345. {
  346. ClearSelection();
  347. SelectPoint( evt->Position(), { SCH_LOCATE_ANY_T }, nullptr, &selCancelled );
  348. m_selection.SetIsHover( true );
  349. }
  350. // If the cursor has moved off the bounding box of the selection by more than
  351. // a grid square, check to see if there is another item available for selection
  352. // under the cursor. If there is, the user likely meant to get the context menu
  353. // for that item. If there is no new item, then keep the original selection and
  354. // show the context menu for it.
  355. else if( !m_selection.GetBoundingBox().Inflate( grid.GetGrid().x, grid.GetGrid().y )
  356. .Contains( evt->Position() ) )
  357. {
  358. EE_COLLECTOR collector;
  359. if( CollectHits( collector, evt->Position(), { SCH_LOCATE_ANY_T } ) )
  360. {
  361. ClearSelection();
  362. SelectPoint( evt->Position(), { SCH_LOCATE_ANY_T }, nullptr, &selCancelled );
  363. m_selection.SetIsHover( true );
  364. }
  365. }
  366. if( !selCancelled )
  367. m_menu.ShowContextMenu( m_selection );
  368. }
  369. else if( evt->IsDblClick( BUT_LEFT ) )
  370. {
  371. m_disambiguateTimer.Stop();
  372. // double click? Display the properties window
  373. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  374. schframe->FocusOnItem( nullptr );
  375. if( m_selection.Empty() )
  376. SelectPoint( evt->Position() );
  377. EDA_ITEM* item = m_selection.Front();
  378. if( item && item->Type() == SCH_SHEET_T )
  379. m_toolMgr->RunAction( EE_ACTIONS::enterSheet );
  380. else
  381. m_toolMgr->RunAction( EE_ACTIONS::properties );
  382. }
  383. else if( evt->IsDblClick( BUT_MIDDLE ) )
  384. {
  385. m_disambiguateTimer.Stop();
  386. // Middle double click? Do zoom to fit or zoom to objects
  387. if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
  388. m_toolMgr->RunAction( ACTIONS::zoomFitObjects, true );
  389. else
  390. m_toolMgr->RunAction( ACTIONS::zoomFitScreen, true );
  391. }
  392. else if( evt->IsDrag( BUT_LEFT ) )
  393. {
  394. m_disambiguateTimer.Stop();
  395. // Is another tool already moving a new object? Don't allow a drag start
  396. if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
  397. {
  398. evt->SetPassEvent();
  399. continue;
  400. }
  401. // drag with LMB? Select multiple objects (or at least draw a selection box) or
  402. // drag them
  403. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  404. schframe->FocusOnItem( nullptr );
  405. if( hasModifier() || drag_action == MOUSE_DRAG_ACTION::SELECT )
  406. {
  407. selectMultiple();
  408. }
  409. else if( m_selection.Empty() && drag_action != MOUSE_DRAG_ACTION::DRAG_ANY )
  410. {
  411. selectMultiple();
  412. }
  413. else
  414. {
  415. if( m_isSymbolEditor )
  416. {
  417. if( static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->IsSymbolAlias() )
  418. {
  419. m_selection = RequestSelection( { LIB_FIELD_T } );
  420. }
  421. else
  422. {
  423. m_selection = RequestSelection( { LIB_SHAPE_T,
  424. LIB_TEXT_T,
  425. LIB_TEXTBOX_T,
  426. LIB_PIN_T,
  427. LIB_FIELD_T } );
  428. }
  429. }
  430. else
  431. {
  432. m_selection = RequestSelection( EE_COLLECTOR::MovableItems );
  433. }
  434. // Check if dragging has started within any of selected items bounding box
  435. if( selectionContains( evt->DragOrigin() ) )
  436. {
  437. // Yes -> run the move tool and wait till it finishes
  438. if( m_isSymbolEditor )
  439. m_toolMgr->InvokeTool( "eeschema.SymbolMoveTool" );
  440. else
  441. m_toolMgr->InvokeTool( "eeschema.InteractiveMove" );
  442. }
  443. else
  444. {
  445. // No -> drag a selection box
  446. selectMultiple();
  447. }
  448. }
  449. }
  450. else if( evt->IsMouseDown( BUT_AUX1 ) )
  451. {
  452. m_toolMgr->RunAction( EE_ACTIONS::navigateBack, true );
  453. }
  454. else if( evt->IsMouseDown( BUT_AUX2 ) )
  455. {
  456. m_toolMgr->RunAction( EE_ACTIONS::navigateForward, true );
  457. }
  458. else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
  459. {
  460. m_disambiguateTimer.Stop();
  461. // context sub-menu selection? Handle unit selection or bus unfolding
  462. if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_UNIT_CMP
  463. && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_UNIT_SYM_MAX )
  464. {
  465. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
  466. int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT_CMP;
  467. if( symbol )
  468. static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit );
  469. }
  470. else if( *evt->GetCommandId() >= ID_POPUP_SCH_UNFOLD_BUS
  471. && *evt->GetCommandId() <= ID_POPUP_SCH_UNFOLD_BUS_END )
  472. {
  473. wxString* net = new wxString( *evt->Parameter<wxString*>() );
  474. m_toolMgr->RunAction( EE_ACTIONS::unfoldBus, true, net );
  475. }
  476. }
  477. else if( evt->IsCancelInteractive() )
  478. {
  479. m_disambiguateTimer.Stop();
  480. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  481. schframe->FocusOnItem( nullptr );
  482. if( !GetSelection().Empty() )
  483. {
  484. ClearSelection();
  485. }
  486. else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
  487. {
  488. SCH_EDITOR_CONTROL* editor = m_toolMgr->GetTool<SCH_EDITOR_CONTROL>();
  489. if( editor && m_frame->eeconfig()->m_Input.esc_clears_net_highlight )
  490. editor->ClearHighlight( *evt );
  491. }
  492. }
  493. else if( evt->Action() == TA_UNDO_REDO_PRE )
  494. {
  495. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  496. schframe->FocusOnItem( nullptr );
  497. ClearSelection();
  498. }
  499. else if( evt->IsMotion() && !m_isSymbolEditor && evt->FirstResponder() == this )
  500. {
  501. // Update cursor and rollover item
  502. rolloverItem = niluuid;
  503. EE_COLLECTOR collector;
  504. getViewControls()->ForceCursorPosition( false );
  505. if( CollectHits( collector, evt->Position() ) )
  506. {
  507. narrowSelection( collector, evt->Position(), false );
  508. if( collector.GetCount() == 1 && !hasModifier() )
  509. {
  510. OPT_TOOL_EVENT autostartEvt = autostartEvent( evt, grid, collector[0] );
  511. if( autostartEvt )
  512. {
  513. if( autostartEvt->Matches( EE_ACTIONS::drawBus.MakeEvent() ) )
  514. displayBusCursor = true;
  515. else if( autostartEvt->Matches( EE_ACTIONS::drawWire.MakeEvent() ) )
  516. displayWireCursor = true;
  517. else if( autostartEvt->Matches( EE_ACTIONS::drawLines.MakeEvent() ) )
  518. displayLineCursor = true;
  519. }
  520. else if( collector[0]->IsHypertext() && !collector[0]->IsSelected() )
  521. {
  522. rolloverItem = collector[0]->m_Uuid;
  523. }
  524. }
  525. }
  526. }
  527. else
  528. {
  529. evt->SetPassEvent();
  530. }
  531. if( rolloverItem != lastRolloverItem )
  532. {
  533. if( EDA_ITEM* item = m_frame->GetItem( lastRolloverItem ) )
  534. {
  535. item->ClearFlags( IS_ROLLOVER );
  536. lastRolloverItem = niluuid;
  537. if( item->Type() == SCH_FIELD_T )
  538. m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
  539. else
  540. m_frame->GetCanvas()->GetView()->Update( item );
  541. }
  542. }
  543. if( EDA_ITEM* item = m_frame->GetItem( rolloverItem ) )
  544. {
  545. if( !( item->GetFlags() & IS_ROLLOVER ) )
  546. {
  547. item->SetFlags( IS_ROLLOVER );
  548. lastRolloverItem = rolloverItem;
  549. if( item->Type() == SCH_FIELD_T )
  550. m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
  551. else
  552. m_frame->GetCanvas()->GetView()->Update( item );
  553. }
  554. }
  555. if( m_frame->ToolStackIsEmpty() )
  556. {
  557. if( displayWireCursor )
  558. {
  559. m_nonModifiedCursor = KICURSOR::LINE_WIRE_ADD;
  560. }
  561. else if( displayBusCursor )
  562. {
  563. m_nonModifiedCursor = KICURSOR::LINE_BUS;
  564. }
  565. else if( displayLineCursor )
  566. {
  567. m_nonModifiedCursor = KICURSOR::LINE_GRAPHIC;
  568. }
  569. else if( rolloverItem != niluuid )
  570. {
  571. m_nonModifiedCursor = KICURSOR::HAND;
  572. }
  573. else if( !m_selection.Empty()
  574. && drag_action == MOUSE_DRAG_ACTION::DRAG_SELECTED
  575. && evt->HasPosition()
  576. && selectionContains( evt->Position() ) ) //move/drag option prediction
  577. {
  578. m_nonModifiedCursor = KICURSOR::MOVING;
  579. }
  580. else
  581. {
  582. m_nonModifiedCursor = KICURSOR::ARROW;
  583. }
  584. }
  585. }
  586. m_disambiguateTimer.Stop();
  587. // Shutting down; clear the selection
  588. m_selection.Clear();
  589. return 0;
  590. }
  591. OPT_TOOL_EVENT EE_SELECTION_TOOL::autostartEvent( TOOL_EVENT* aEvent, EE_GRID_HELPER& aGrid,
  592. SCH_ITEM* aItem )
  593. {
  594. VECTOR2I pos = aGrid.BestSnapAnchor( aEvent->Position(), LAYER_CONNECTABLE );
  595. if( m_frame->eeconfig()->m_Drawing.auto_start_wires
  596. && !m_toolMgr->GetTool<EE_POINT_EDITOR>()->HasPoint()
  597. && aItem->IsPointClickableAnchor( pos ) )
  598. {
  599. OPT_TOOL_EVENT newEvt = EE_ACTIONS::drawWire.MakeEvent();
  600. if( aItem->Type() == SCH_BUS_BUS_ENTRY_T )
  601. {
  602. newEvt = EE_ACTIONS::drawBus.MakeEvent();
  603. }
  604. else if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
  605. {
  606. SCH_BUS_WIRE_ENTRY* busEntry = static_cast<SCH_BUS_WIRE_ENTRY*>( aItem );
  607. if( !busEntry->m_connected_bus_item )
  608. newEvt = EE_ACTIONS::drawBus.MakeEvent();
  609. }
  610. else if( aItem->Type() == SCH_LINE_T )
  611. {
  612. SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
  613. if( line->IsBus() )
  614. newEvt = EE_ACTIONS::drawBus.MakeEvent();
  615. else if( line->IsGraphicLine() )
  616. newEvt = EE_ACTIONS::drawLines.MakeEvent();
  617. }
  618. else if( aItem->Type() == SCH_LABEL_T || aItem->Type() == SCH_HIER_LABEL_T
  619. || aItem->Type() == SCH_SHEET_PIN_T )
  620. {
  621. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
  622. SCH_CONNECTION possibleConnection( label->Schematic()->ConnectionGraph() );
  623. possibleConnection.ConfigureFromLabel( label->GetText() );
  624. if( possibleConnection.IsBus() )
  625. newEvt = EE_ACTIONS::drawBus.MakeEvent();
  626. }
  627. newEvt->SetMousePosition( pos );
  628. newEvt->SetHasPosition( true );
  629. newEvt->SetForceImmediate( true );
  630. getViewControls()->ForceCursorPosition( true, pos );
  631. return newEvt;
  632. }
  633. return OPT_TOOL_EVENT();
  634. }
  635. int EE_SELECTION_TOOL::disambiguateCursor( const TOOL_EVENT& aEvent )
  636. {
  637. wxMouseState keyboardState = wxGetMouseState();
  638. setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
  639. keyboardState.AltDown() );
  640. m_skip_heuristics = true;
  641. SelectPoint( m_originalCursor, { SCH_LOCATE_ANY_T }, nullptr, &m_canceledMenu, false,
  642. m_additive, m_subtractive, m_exclusive_or );
  643. m_skip_heuristics = false;
  644. return 0;
  645. }
  646. void EE_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
  647. {
  648. if( m_frame->ToolStackIsEmpty() && !m_multiple )
  649. {
  650. wxMouseState keyboardState = wxGetMouseState();
  651. setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
  652. keyboardState.AltDown() );
  653. if( m_additive )
  654. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
  655. else if( m_subtractive )
  656. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
  657. else if( m_exclusive_or )
  658. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
  659. else
  660. m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
  661. }
  662. }
  663. EE_SELECTION& EE_SELECTION_TOOL::GetSelection()
  664. {
  665. return m_selection;
  666. }
  667. bool EE_SELECTION_TOOL::CollectHits( EE_COLLECTOR& aCollector, const VECTOR2I& aWhere,
  668. const std::vector<KICAD_T>& aScanTypes )
  669. {
  670. int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  671. int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() / 2 );
  672. aCollector.m_Threshold = std::max( pixelThreshold, gridThreshold );
  673. aCollector.m_ShowPinElectricalTypes = m_frame->GetRenderSettings()->m_ShowPinsElectricalType;
  674. if( m_isSymbolEditor )
  675. {
  676. LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
  677. if( !symbol )
  678. return false;
  679. aCollector.Collect( symbol->GetDrawItems(), aScanTypes, aWhere, m_unit, m_convert );
  680. }
  681. else
  682. {
  683. aCollector.Collect( m_frame->GetScreen(), aScanTypes, aWhere, m_unit, m_convert );
  684. if( m_frame->eeconfig()->m_Selection.select_pin_selects_symbol )
  685. {
  686. int originalCount = aCollector.GetCount();
  687. for( int ii = 0; ii < originalCount; ++ii )
  688. {
  689. if( aCollector[ii]->Type() == SCH_PIN_T )
  690. {
  691. SCH_PIN* pin = static_cast<SCH_PIN*>( aCollector[ii] );
  692. if( !aCollector.HasItem( pin->GetParentSymbol() ) )
  693. aCollector.Append( pin->GetParentSymbol() );
  694. }
  695. }
  696. }
  697. }
  698. return aCollector.GetCount() > 0;
  699. }
  700. void EE_SELECTION_TOOL::narrowSelection( EE_COLLECTOR& collector, const VECTOR2I& aWhere,
  701. bool aCheckLocked, bool aSelectedOnly )
  702. {
  703. for( int i = collector.GetCount() - 1; i >= 0; --i )
  704. {
  705. if( !Selectable( collector[i], &aWhere ) )
  706. {
  707. collector.Remove( i );
  708. continue;
  709. }
  710. if( aCheckLocked && collector[i]->IsLocked() )
  711. {
  712. collector.Remove( i );
  713. continue;
  714. }
  715. if( aSelectedOnly && !collector[i]->IsSelected() )
  716. {
  717. collector.Remove( i );
  718. continue;
  719. }
  720. }
  721. // Apply some ugly heuristics to avoid disambiguation menus whenever possible
  722. if( collector.GetCount() > 1 && !m_skip_heuristics )
  723. GuessSelectionCandidates( collector, aWhere );
  724. }
  725. bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, const VECTOR2I& aWhere,
  726. EDA_ITEM** aItem, bool* aSelectionCancelledFlag, bool aAdd,
  727. bool aSubtract, bool aExclusiveOr )
  728. {
  729. m_selection.ClearReferencePoint();
  730. // If still more than one item we're going to have to ask the user.
  731. if( aCollector.GetCount() > 1 )
  732. {
  733. // Try to call selectionMenu via RunAction() to avoid event-loop contention
  734. // But it we cannot handle the event, then we don't have an active tool loop, so
  735. // handle it directly.
  736. if( !m_toolMgr->RunAction( EE_ACTIONS::selectionMenu, true, &aCollector ) )
  737. {
  738. if( !doSelectionMenu( &aCollector ) )
  739. aCollector.m_MenuCancelled = true;
  740. }
  741. if( aCollector.m_MenuCancelled )
  742. {
  743. if( aSelectionCancelledFlag )
  744. *aSelectionCancelledFlag = true;
  745. return false;
  746. }
  747. }
  748. if( !aAdd && !aSubtract && !aExclusiveOr )
  749. ClearSelection();
  750. int addedCount = 0;
  751. bool anySubtracted = false;
  752. if( aCollector.GetCount() > 0 )
  753. {
  754. for( int i = 0; i < aCollector.GetCount(); ++i )
  755. {
  756. EDA_ITEM_FLAGS flags = 0;
  757. bool isLine = aCollector[i]->Type() == SCH_LINE_T;
  758. // Handle line ends specially
  759. if( isLine )
  760. {
  761. SCH_LINE* line = (SCH_LINE*) aCollector[i];
  762. if( HitTestPoints( line->GetStartPoint(), aWhere, aCollector.m_Threshold ) )
  763. flags = STARTPOINT;
  764. else if( HitTestPoints( line->GetEndPoint(), aWhere, aCollector.m_Threshold ) )
  765. flags = ENDPOINT;
  766. else
  767. flags = STARTPOINT | ENDPOINT;
  768. }
  769. if( aSubtract
  770. || ( aExclusiveOr && aCollector[i]->IsSelected()
  771. && ( !isLine || ( isLine && aCollector[i]->HasFlag( flags ) ) ) ) )
  772. {
  773. aCollector[i]->ClearFlags( flags );
  774. // Need to update end shadows after ctrl-click unselecting one of two selected endpoints
  775. if( isLine )
  776. getView()->Update( aCollector[i] );
  777. if( !aCollector[i]->HasFlag( STARTPOINT ) && !aCollector[i]->HasFlag( ENDPOINT ) )
  778. {
  779. unselect( aCollector[i] );
  780. anySubtracted = true;
  781. }
  782. }
  783. else
  784. {
  785. aCollector[i]->SetFlags( flags );
  786. select( aCollector[i] );
  787. addedCount++;
  788. }
  789. }
  790. }
  791. if( addedCount == 1 )
  792. {
  793. m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
  794. if( aItem && aCollector.GetCount() == 1 )
  795. *aItem = aCollector[0];
  796. return true;
  797. }
  798. else if( addedCount > 1 )
  799. {
  800. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  801. return true;
  802. }
  803. else if( anySubtracted )
  804. {
  805. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  806. return true;
  807. }
  808. return false;
  809. }
  810. bool EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere,
  811. const std::vector<KICAD_T>& aScanTypes,
  812. EDA_ITEM** aItem, bool* aSelectionCancelledFlag,
  813. bool aCheckLocked, bool aAdd, bool aSubtract,
  814. bool aExclusiveOr )
  815. {
  816. EE_COLLECTOR collector;
  817. if( !CollectHits( collector, aWhere, aScanTypes ) )
  818. return false;
  819. narrowSelection( collector, aWhere, aCheckLocked, aSubtract );
  820. return selectPoint( collector, aWhere, aItem, aSelectionCancelledFlag, aAdd, aSubtract,
  821. aExclusiveOr );
  822. }
  823. int EE_SELECTION_TOOL::SelectAll( const TOOL_EVENT& aEvent )
  824. {
  825. m_multiple = true; // Multiple selection mode is active
  826. KIGFX::VIEW* view = getView();
  827. // hold all visible items
  828. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
  829. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> sheetPins;
  830. // Filter the view items based on the selection box
  831. BOX2I selectionBox;
  832. selectionBox.SetMaximum();
  833. view->Query( selectionBox, selectedItems ); // Get the list of selected items
  834. // Sheet pins aren't in the view; add them by hand
  835. for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
  836. {
  837. SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
  838. if( sheet )
  839. {
  840. int layer = pair.second;
  841. for( SCH_SHEET_PIN* pin : sheet->GetPins() )
  842. sheetPins.emplace_back( KIGFX::VIEW::LAYER_ITEM_PAIR( pin, layer ) );
  843. }
  844. }
  845. selectedItems.insert( selectedItems.end(), sheetPins.begin(), sheetPins.end() );
  846. for( const std::pair<KIGFX::VIEW_ITEM*, int>& item_pair : selectedItems )
  847. {
  848. if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( item_pair.first ) )
  849. {
  850. if( Selectable( item ) )
  851. {
  852. if( item->Type() == SCH_LINE_T )
  853. item->SetFlags( STARTPOINT | ENDPOINT );
  854. select( item );
  855. }
  856. }
  857. }
  858. m_multiple = false;
  859. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  860. return 0;
  861. }
  862. void EE_SELECTION_TOOL::GuessSelectionCandidates( EE_COLLECTOR& collector, const VECTOR2I& aPos )
  863. {
  864. // Prefer exact hits to sloppy ones
  865. std::set<EDA_ITEM*> exactHits;
  866. for( int i = collector.GetCount() - 1; i >= 0; --i )
  867. {
  868. EDA_ITEM* item = collector[ i ];
  869. SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
  870. LIB_SHAPE* shape = dynamic_cast<LIB_SHAPE*>( item );
  871. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
  872. // Lines are hard to hit. Give them a bit more slop to still be considered "exact".
  873. if( line || ( shape && shape->GetShape() == SHAPE_T::POLY )
  874. || ( shape && shape->GetShape() == SHAPE_T::ARC ) )
  875. {
  876. int pixelThreshold = KiROUND( getView()->ToWorld( 6 ) );
  877. if( item->HitTest( aPos, pixelThreshold ) )
  878. exactHits.insert( item );
  879. }
  880. else if( symbol && m_frame->eeconfig()->m_Selection.select_pin_selects_symbol )
  881. {
  882. if( symbol->GetBodyAndPinsBoundingBox().Contains( aPos ) )
  883. exactHits.insert( item );
  884. }
  885. else
  886. {
  887. if( item->HitTest( aPos, 0 ) )
  888. exactHits.insert( item );
  889. }
  890. }
  891. if( exactHits.size() > 0 && exactHits.size() < (unsigned) collector.GetCount() )
  892. {
  893. for( int i = collector.GetCount() - 1; i >= 0; --i )
  894. {
  895. EDA_ITEM* item = collector[ i ];
  896. if( !exactHits.count( item ) )
  897. collector.Transfer( item );
  898. }
  899. }
  900. // Find the closest item. (Note that at this point all hits are either exact or non-exact.)
  901. VECTOR2I pos( aPos );
  902. SEG poss( m_isSymbolEditor ? mapCoords( pos ) : pos,
  903. m_isSymbolEditor ? mapCoords( pos ) : pos );
  904. EDA_ITEM* closest = nullptr;
  905. int closestDist = INT_MAX / 2;
  906. for( EDA_ITEM* item : collector )
  907. {
  908. BOX2I bbox = item->GetBoundingBox();
  909. int dist = INT_MAX / 2;
  910. if( exactHits.count( item ) )
  911. {
  912. if( item->Type() == SCH_PIN_T || item->Type() == SCH_JUNCTION_T )
  913. {
  914. closest = item;
  915. break;
  916. }
  917. SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
  918. EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
  919. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
  920. if( line )
  921. {
  922. dist = DistanceLinePoint( line->GetStartPoint(), line->GetEndPoint(), pos );
  923. }
  924. else if( text )
  925. {
  926. if( SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( text ) )
  927. {
  928. if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T )
  929. {
  930. symbol = static_cast<SCH_SYMBOL*>( field->GetParent() );
  931. VECTOR2I relPos = pos - symbol->GetPosition();
  932. relPos = symbol->GetTransform().InverseTransform().TransformCoordinate( relPos );
  933. pos = relPos + symbol->GetPosition();
  934. poss = SEG( pos, pos );
  935. }
  936. }
  937. text->GetEffectiveTextShape()->Collide( poss, closestDist, &dist );
  938. }
  939. else if( symbol )
  940. {
  941. try
  942. {
  943. bbox = symbol->GetBodyBoundingBox();
  944. }
  945. catch( const boost::bad_pointer& exc )
  946. {
  947. // This may be overkill and could be an assertion but we are more likely to
  948. // find any boost pointer container errors this way.
  949. wxLogError( wxT( "Boost bad pointer exception '%s' occurred." ), exc.what() );
  950. }
  951. SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
  952. if( bbox.Contains( pos ) )
  953. dist = EuclideanNorm( bbox.GetCenter() - pos );
  954. else
  955. rect.Collide( poss, closestDist, &dist );
  956. }
  957. else
  958. {
  959. dist = EuclideanNorm( bbox.GetCenter() - pos );
  960. }
  961. }
  962. else
  963. {
  964. SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
  965. rect.Collide( poss, collector.m_Threshold, &dist );
  966. }
  967. if( dist == closestDist )
  968. {
  969. if( item->GetParent() == closest )
  970. closest = item;
  971. }
  972. else if( dist < closestDist )
  973. {
  974. closestDist = dist;
  975. closest = item;
  976. }
  977. }
  978. // Construct a tight box (1/2 height and width) around the center of the closest item.
  979. // All items which exist at least partly outside this box have sufficient other areas
  980. // for selection and can be dropped.
  981. if( closest ) // Don't try and get a tight bbox if nothing is near the mouse pointer
  982. {
  983. BOX2I tightBox = closest->GetBoundingBox();
  984. tightBox.Inflate( -tightBox.GetWidth() / 4, -tightBox.GetHeight() / 4 );
  985. for( int i = collector.GetCount() - 1; i >= 0; --i )
  986. {
  987. EDA_ITEM* item = collector[i];
  988. if( item == closest )
  989. continue;
  990. if( !item->HitTest( tightBox, true ) )
  991. collector.Transfer( item );
  992. }
  993. }
  994. }
  995. EE_SELECTION& EE_SELECTION_TOOL::RequestSelection( const std::vector<KICAD_T>& aScanTypes )
  996. {
  997. if( m_selection.Empty() )
  998. {
  999. VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
  1000. ClearSelection();
  1001. SelectPoint( cursorPos, aScanTypes );
  1002. m_selection.SetIsHover( true );
  1003. m_selection.ClearReferencePoint();
  1004. }
  1005. else // Trim an existing selection by aFilterList
  1006. {
  1007. bool isMoving = false;
  1008. bool anyUnselected = false;
  1009. for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
  1010. {
  1011. EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
  1012. isMoving |= static_cast<SCH_ITEM*>( item )->IsMoving();
  1013. if( !item->IsType( aScanTypes ) )
  1014. {
  1015. unselect( item );
  1016. anyUnselected = true;
  1017. }
  1018. }
  1019. if( anyUnselected )
  1020. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  1021. if( !isMoving )
  1022. updateReferencePoint();
  1023. }
  1024. return m_selection;
  1025. }
  1026. void EE_SELECTION_TOOL::updateReferencePoint()
  1027. {
  1028. VECTOR2I refP( 0, 0 );
  1029. if( m_selection.Size() > 0 )
  1030. {
  1031. if( m_isSymbolEditor )
  1032. refP = static_cast<LIB_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
  1033. else
  1034. refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
  1035. }
  1036. m_selection.SetReferencePoint( refP );
  1037. }
  1038. // Some navigation actions are allowed in selectMultiple
  1039. const TOOL_ACTION* allowedActions[] = { &ACTIONS::panUp, &ACTIONS::panDown,
  1040. &ACTIONS::panLeft, &ACTIONS::panRight,
  1041. &ACTIONS::cursorUp, &ACTIONS::cursorDown,
  1042. &ACTIONS::cursorLeft, &ACTIONS::cursorRight,
  1043. &ACTIONS::cursorUpFast, &ACTIONS::cursorDownFast,
  1044. &ACTIONS::cursorLeftFast, &ACTIONS::cursorRightFast,
  1045. &ACTIONS::zoomIn, &ACTIONS::zoomOut,
  1046. &ACTIONS::zoomInCenter, &ACTIONS::zoomOutCenter,
  1047. &ACTIONS::zoomCenter, &ACTIONS::zoomFitScreen,
  1048. &ACTIONS::zoomFitObjects, nullptr };
  1049. bool EE_SELECTION_TOOL::selectMultiple()
  1050. {
  1051. bool cancelled = false; // Was the tool canceled while it was running?
  1052. m_multiple = true; // Multiple selection mode is active
  1053. KIGFX::VIEW* view = getView();
  1054. KIGFX::PREVIEW::SELECTION_AREA area;
  1055. view->Add( &area );
  1056. while( TOOL_EVENT* evt = Wait() )
  1057. {
  1058. int width = area.GetEnd().x - area.GetOrigin().x;
  1059. int height = area.GetEnd().y - area.GetOrigin().y;
  1060. /* Selection mode depends on direction of drag-selection:
  1061. * Left > Right : Select objects that are fully enclosed by selection
  1062. * Right > Left : Select objects that are crossed by selection
  1063. */
  1064. bool isGreedy = width < 0;
  1065. if( view->IsMirroredX() )
  1066. isGreedy = !isGreedy;
  1067. m_frame->GetCanvas()->SetCurrentCursor( isGreedy ? KICURSOR::SELECT_LASSO
  1068. : KICURSOR::SELECT_WINDOW );
  1069. if( evt->IsCancelInteractive() || evt->IsActivate() )
  1070. {
  1071. cancelled = true;
  1072. break;
  1073. }
  1074. if( evt->IsDrag( BUT_LEFT ) )
  1075. {
  1076. if( !m_drag_additive && !m_drag_subtractive )
  1077. ClearSelection();
  1078. // Start drawing a selection box
  1079. area.SetOrigin( evt->DragOrigin() );
  1080. area.SetEnd( evt->Position() );
  1081. area.SetAdditive( m_drag_additive );
  1082. area.SetSubtractive( m_drag_subtractive );
  1083. area.SetExclusiveOr( false );
  1084. view->SetVisible( &area, true );
  1085. view->Update( &area );
  1086. getViewControls()->SetAutoPan( true );
  1087. }
  1088. if( evt->IsMouseUp( BUT_LEFT ) )
  1089. {
  1090. getViewControls()->SetAutoPan( false );
  1091. // End drawing the selection box
  1092. view->SetVisible( &area, false );
  1093. // Fetch items from the RTree that are in our area of interest
  1094. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> nearbyViewItems;
  1095. view->Query( area.ViewBBox(), nearbyViewItems );
  1096. // Build lists of nearby items and their children
  1097. std::unordered_set<EDA_ITEM*> nearbyItems;
  1098. std::vector<EDA_ITEM*> nearbyChildren;
  1099. std::vector<EDA_ITEM*> flaggedItems;
  1100. for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : nearbyViewItems )
  1101. {
  1102. if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first ) )
  1103. {
  1104. if( nearbyItems.insert( item ).second )
  1105. {
  1106. item->ClearFlags( CANDIDATE );
  1107. if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item ) )
  1108. {
  1109. sch_item->RunOnChildren(
  1110. [&]( SCH_ITEM* aChild )
  1111. {
  1112. // Filter pins by unit
  1113. if( SCH_PIN* pin = dyn_cast<SCH_PIN*>( aChild ) )
  1114. {
  1115. int unit = pin->GetLibPin()->GetUnit();
  1116. if( unit && unit != pin->GetParentSymbol()->GetUnit() )
  1117. return;
  1118. }
  1119. nearbyChildren.push_back( aChild );
  1120. } );
  1121. }
  1122. }
  1123. }
  1124. }
  1125. BOX2I selectionRect( area.GetOrigin(), VECTOR2I( width, height ) );
  1126. selectionRect.Normalize();
  1127. bool anyAdded = false;
  1128. bool anySubtracted = false;
  1129. auto selectItem =
  1130. [&]( EDA_ITEM* aItem, EDA_ITEM_FLAGS flags )
  1131. {
  1132. if( m_subtractive || ( m_exclusive_or && aItem->IsSelected() ) )
  1133. {
  1134. if ( m_exclusive_or )
  1135. aItem->XorFlags( flags );
  1136. else
  1137. aItem->ClearFlags( flags );
  1138. if( !aItem->HasFlag( STARTPOINT ) && !aItem->HasFlag( ENDPOINT ) )
  1139. {
  1140. unselect( aItem );
  1141. anySubtracted = true;
  1142. }
  1143. // We changed one line endpoint on a selected line,
  1144. // update the view at least.
  1145. if( flags && !anySubtracted )
  1146. {
  1147. getView()->Update( aItem );
  1148. }
  1149. }
  1150. else
  1151. {
  1152. aItem->SetFlags( flags );
  1153. select( aItem );
  1154. anyAdded = true;
  1155. }
  1156. };
  1157. for( EDA_ITEM* item : nearbyItems )
  1158. {
  1159. bool selected = false;
  1160. EDA_ITEM_FLAGS flags = 0;
  1161. if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
  1162. item->SetFlags( SHOW_ELEC_TYPE );
  1163. if( Selectable( item ) )
  1164. {
  1165. if( item->Type() == SCH_LINE_T )
  1166. {
  1167. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  1168. if( ( isGreedy && line->HitTest( selectionRect, false ) )
  1169. || ( selectionRect.Contains( line->GetEndPoint() )
  1170. && selectionRect.Contains( line->GetStartPoint() ) ) )
  1171. {
  1172. selected = true;
  1173. flags |= STARTPOINT | ENDPOINT;
  1174. }
  1175. else if( !isGreedy )
  1176. {
  1177. if( selectionRect.Contains( line->GetStartPoint() )
  1178. && line->IsStartDangling() )
  1179. {
  1180. selected = true;
  1181. flags |= STARTPOINT;
  1182. }
  1183. if( selectionRect.Contains( line->GetEndPoint() )
  1184. && line->IsEndDangling() )
  1185. {
  1186. selected = true;
  1187. flags |= ENDPOINT;
  1188. }
  1189. }
  1190. }
  1191. else
  1192. {
  1193. selected = item->HitTest( selectionRect, !isGreedy );
  1194. }
  1195. }
  1196. if( selected )
  1197. {
  1198. item->SetFlags( CANDIDATE );
  1199. flaggedItems.push_back( item );
  1200. selectItem( item, flags );
  1201. }
  1202. item->ClearFlags( SHOW_ELEC_TYPE );
  1203. }
  1204. for( EDA_ITEM* item : nearbyChildren )
  1205. {
  1206. if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
  1207. item->SetFlags( SHOW_ELEC_TYPE );
  1208. if( Selectable( item )
  1209. && !item->GetParent()->HasFlag( CANDIDATE )
  1210. && item->HitTest( selectionRect, !isGreedy ) )
  1211. {
  1212. selectItem( item, 0 );
  1213. }
  1214. item->ClearFlags( SHOW_ELEC_TYPE );
  1215. }
  1216. for( EDA_ITEM* item : flaggedItems )
  1217. item->ClearFlags( CANDIDATE );
  1218. m_selection.SetIsHover( false );
  1219. // Inform other potentially interested tools
  1220. if( anyAdded )
  1221. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1222. if( anySubtracted )
  1223. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  1224. break; // Stop waiting for events
  1225. }
  1226. // Allow some actions for navigation
  1227. for( int i = 0; allowedActions[i]; ++i )
  1228. {
  1229. if( evt->IsAction( allowedActions[i] ) )
  1230. {
  1231. evt->SetPassEvent();
  1232. break;
  1233. }
  1234. }
  1235. }
  1236. getViewControls()->SetAutoPan( false );
  1237. // Stop drawing the selection box
  1238. view->Remove( &area );
  1239. m_multiple = false; // Multiple selection mode is inactive
  1240. if( !cancelled )
  1241. m_selection.ClearReferencePoint();
  1242. return cancelled;
  1243. }
  1244. EDA_ITEM* EE_SELECTION_TOOL::GetNode( VECTOR2I aPosition )
  1245. {
  1246. EE_COLLECTOR collector;
  1247. //TODO(snh): Reimplement after exposing KNN interface
  1248. int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  1249. int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() );
  1250. int thresholdMax = std::max( pixelThreshold, gridThreshold );
  1251. for( int threshold : { 0, thresholdMax/4, thresholdMax/2, thresholdMax } )
  1252. {
  1253. collector.m_Threshold = threshold;
  1254. collector.Collect( m_frame->GetScreen(), connectedTypes, aPosition );
  1255. if( collector.GetCount() > 0 )
  1256. break;
  1257. }
  1258. return collector.GetCount() ? collector[ 0 ] : nullptr;
  1259. }
  1260. int EE_SELECTION_TOOL::SelectNode( const TOOL_EVENT& aEvent )
  1261. {
  1262. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
  1263. SelectPoint( cursorPos, connectedTypes );
  1264. return 0;
  1265. }
  1266. int EE_SELECTION_TOOL::SelectConnection( const TOOL_EVENT& aEvent )
  1267. {
  1268. RequestSelection( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T } );
  1269. if( m_selection.Empty() )
  1270. return 0;
  1271. SCH_LINE* line = (SCH_LINE*) m_selection.Front();
  1272. unsigned done = false;
  1273. m_frame->GetScreen()->ClearDrawingState();
  1274. std::set<SCH_ITEM*> conns = m_frame->GetScreen()->MarkConnections( line, false );
  1275. for( SCH_ITEM* item : conns )
  1276. {
  1277. if( item->IsType( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T } )
  1278. && !item->IsSelected() )
  1279. {
  1280. done = true;
  1281. }
  1282. select( item );
  1283. }
  1284. if( !done )
  1285. {
  1286. conns = m_frame->GetScreen()->MarkConnections( line, true );
  1287. for( SCH_ITEM* item : conns )
  1288. select( item );
  1289. }
  1290. if( m_selection.GetSize() > 1 )
  1291. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1292. return 0;
  1293. }
  1294. int EE_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
  1295. {
  1296. ClearSelection();
  1297. return 0;
  1298. }
  1299. void EE_SELECTION_TOOL::ZoomFitCrossProbeBBox( const BOX2I& aBBox )
  1300. {
  1301. if( aBBox.GetWidth() == 0 )
  1302. return;
  1303. BOX2I bbox = aBBox;
  1304. bbox.Normalize();
  1305. VECTOR2I bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
  1306. VECTOR2D screenSize = getView()->GetViewport().GetSize();
  1307. // This code tries to come up with a zoom factor that doesn't simply zoom in
  1308. // to the cross probed symbol, but instead shows a reasonable amount of the
  1309. // circuit around it to provide context. This reduces or eliminates the need
  1310. // to manually change the zoom because it's too close.
  1311. // Using the default text height as a constant to compare against, use the
  1312. // height of the bounding box of visible items for a footprint to figure out
  1313. // if this is a big symbol (like a processor) or a small symbol (like a resistor).
  1314. // This ratio is not useful by itself as a scaling factor. It must be "bent" to
  1315. // provide good scaling at varying symbol sizes. Bigger symbols need less
  1316. // scaling than small ones.
  1317. double currTextHeight = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
  1318. double compRatio = bbSize.y / currTextHeight; // Ratio of symbol to text height
  1319. double compRatioBent = 1.0;
  1320. // LUT to scale zoom ratio to provide reasonable schematic context. Must work
  1321. // with symbols of varying sizes (e.g. 0402 package and 200 pin BGA).
  1322. // "first" is used as the input and "second" as the output
  1323. //
  1324. // "first" = compRatio (symbol height / default text height)
  1325. // "second" = Amount to scale ratio by
  1326. std::vector<std::pair<double, double>> lut{ { 1.25, 16 }, // 32
  1327. { 2.5, 12 }, //24
  1328. { 5, 8 }, // 16
  1329. { 6, 6 }, //
  1330. { 10, 4 }, //8
  1331. { 20, 2 }, //4
  1332. { 40, 1.5 }, // 2
  1333. { 100, 1 } };
  1334. std::vector<std::pair<double, double>>::iterator it;
  1335. // Large symbol default is last LUT entry (1:1).
  1336. compRatioBent = lut.back().second;
  1337. // Use LUT to do linear interpolation of "compRatio" within "first", then
  1338. // use that result to linearly interpolate "second" which gives the scaling
  1339. // factor needed.
  1340. if( compRatio >= lut.front().first )
  1341. {
  1342. for( it = lut.begin(); it < lut.end() - 1; it++ )
  1343. {
  1344. if( it->first <= compRatio && next( it )->first >= compRatio )
  1345. {
  1346. double diffx = compRatio - it->first;
  1347. double diffn = next( it )->first - it->first;
  1348. compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
  1349. break; // We have our interpolated value
  1350. }
  1351. }
  1352. }
  1353. else
  1354. {
  1355. compRatioBent = lut.front().second; // Small symbol default is first entry
  1356. }
  1357. // This is similar to the original KiCad code that scaled the zoom to make sure
  1358. // symbols were visible on screen. It's simply a ratio of screen size to
  1359. // symbol size, and its job is to zoom in to make the component fullscreen.
  1360. // Earlier in the code the symbol BBox is given a 20% margin to add some
  1361. // breathing room. We compare the height of this enlarged symbol bbox to the
  1362. // default text height. If a symbol will end up with the sides clipped, we
  1363. // adjust later to make sure it fits on screen.
  1364. screenSize.x = std::max( 10.0, screenSize.x );
  1365. screenSize.y = std::max( 10.0, screenSize.y );
  1366. double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
  1367. // Original KiCad code for how much to scale the zoom
  1368. double kicadRatio =
  1369. std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
  1370. // If the width of the part we're probing is bigger than what the screen width
  1371. // will be after the zoom, then punt and use the KiCad zoom algorithm since it
  1372. // guarantees the part's width will be encompassed within the screen.
  1373. if( bbSize.x > screenSize.x * ratio * compRatioBent )
  1374. {
  1375. // Use standard KiCad zoom for parts too wide to fit on screen/
  1376. ratio = kicadRatio;
  1377. compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
  1378. wxLogTrace( "CROSS_PROBE_SCALE",
  1379. "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
  1380. }
  1381. // Now that "compRatioBent" holds our final scaling factor we apply it to the
  1382. // original fullscreen zoom ratio to arrive at the final ratio itself.
  1383. ratio *= compRatioBent;
  1384. bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
  1385. // Try not to zoom on every cross-probe; it gets very noisy
  1386. if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
  1387. getView()->SetScale( getView()->GetScale() / ratio );
  1388. }
  1389. int EE_SELECTION_TOOL::SyncSelection( std::optional<SCH_SHEET_PATH> targetSheetPath,
  1390. SCH_ITEM* focusItem, std::vector<SCH_ITEM*> items )
  1391. {
  1392. SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  1393. if( !editFrame || m_isSymbolEditor || m_isSymbolViewer )
  1394. return 0;
  1395. if( targetSheetPath && targetSheetPath != editFrame->Schematic().CurrentSheet() )
  1396. {
  1397. editFrame->Schematic().SetCurrentSheet( *targetSheetPath );
  1398. editFrame->DisplayCurrentSheet();
  1399. }
  1400. ClearSelection( items.size() > 0 ? true /*quiet mode*/ : false );
  1401. // Perform individual selection of each item before processing the event.
  1402. for( SCH_ITEM* item : items )
  1403. select( item );
  1404. BOX2I bbox = m_selection.GetBoundingBox();
  1405. if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
  1406. {
  1407. if( m_frame->eeconfig()->m_CrossProbing.center_on_items )
  1408. {
  1409. if( m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
  1410. ZoomFitCrossProbeBBox( bbox );
  1411. editFrame->FocusOnItem( focusItem );
  1412. if( !focusItem )
  1413. editFrame->FocusOnLocation( bbox.Centre() );
  1414. }
  1415. }
  1416. if( m_selection.Size() > 0 )
  1417. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1418. return 0;
  1419. }
  1420. void EE_SELECTION_TOOL::RebuildSelection()
  1421. {
  1422. m_selection.Clear();
  1423. if( m_isSymbolEditor )
  1424. {
  1425. LIB_SYMBOL* start = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
  1426. for( LIB_ITEM& item : start->GetDrawItems() )
  1427. {
  1428. if( item.IsSelected() )
  1429. select( &item );
  1430. }
  1431. }
  1432. else
  1433. {
  1434. for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
  1435. {
  1436. // If the field and symbol are selected, only use the symbol
  1437. if( item->IsSelected() )
  1438. {
  1439. select( item );
  1440. }
  1441. else
  1442. {
  1443. item->RunOnChildren(
  1444. [&]( SCH_ITEM* aChild )
  1445. {
  1446. if( aChild->IsSelected() )
  1447. select( aChild );
  1448. } );
  1449. }
  1450. }
  1451. }
  1452. updateReferencePoint();
  1453. // Inform other potentially interested tools
  1454. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1455. }
  1456. bool EE_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, const VECTOR2I* aPos,
  1457. bool checkVisibilityOnly ) const
  1458. {
  1459. // NOTE: in the future this is where Eeschema layer/itemtype visibility will be handled
  1460. SYMBOL_EDIT_FRAME* symEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  1461. // Do not allow selection of anything except fields when the current symbol in the symbol
  1462. // editor is a derived symbol.
  1463. if( symEditFrame && symEditFrame->IsSymbolAlias() && aItem->Type() != LIB_FIELD_T )
  1464. return false;
  1465. switch( aItem->Type() )
  1466. {
  1467. case SCH_PIN_T:
  1468. {
  1469. const SCH_PIN* pin = static_cast<const SCH_PIN*>( aItem );
  1470. if( !pin->IsVisible() && !m_frame->GetShowAllPins() )
  1471. return false;
  1472. if( m_frame->eeconfig()->m_Selection.select_pin_selects_symbol )
  1473. {
  1474. // Pin anchors have to be allowed for auto-starting wires.
  1475. if( aPos )
  1476. {
  1477. EE_GRID_HELPER grid( m_toolMgr );
  1478. if( pin->IsPointClickableAnchor( grid.BestSnapAnchor( *aPos, LAYER_CONNECTABLE ) ) )
  1479. return true;
  1480. }
  1481. return false;
  1482. }
  1483. break;
  1484. }
  1485. case LIB_SYMBOL_T: // In symbol_editor we do not want to select the symbol itself.
  1486. return false;
  1487. case LIB_FIELD_T: // LIB_FIELD object can always be edited.
  1488. break;
  1489. case LIB_SHAPE_T:
  1490. case LIB_TEXT_T:
  1491. case LIB_PIN_T:
  1492. if( symEditFrame )
  1493. {
  1494. LIB_ITEM* lib_item = (LIB_ITEM*) aItem;
  1495. if( lib_item->GetUnit() && lib_item->GetUnit() != symEditFrame->GetUnit() )
  1496. return false;
  1497. if( lib_item->GetConvert() && lib_item->GetConvert() != symEditFrame->GetConvert() )
  1498. return false;
  1499. }
  1500. break;
  1501. case SCH_MARKER_T: // Always selectable
  1502. return true;
  1503. default: // Suppress warnings
  1504. break;
  1505. }
  1506. return true;
  1507. }
  1508. void EE_SELECTION_TOOL::ClearSelection( bool aQuietMode )
  1509. {
  1510. if( m_selection.Empty() )
  1511. return;
  1512. while( m_selection.GetSize() )
  1513. unhighlight( m_selection.Front(), SELECTED, &m_selection );
  1514. getView()->Update( &m_selection );
  1515. m_selection.SetIsHover( false );
  1516. m_selection.ClearReferencePoint();
  1517. // Inform other potentially interested tools
  1518. if( !aQuietMode )
  1519. m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
  1520. }
  1521. void EE_SELECTION_TOOL::select( EDA_ITEM* aItem )
  1522. {
  1523. highlight( aItem, SELECTED, &m_selection );
  1524. }
  1525. void EE_SELECTION_TOOL::unselect( EDA_ITEM* aItem )
  1526. {
  1527. unhighlight( aItem, SELECTED, &m_selection );
  1528. }
  1529. void EE_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
  1530. {
  1531. KICAD_T itemType = aItem->Type();
  1532. if( aMode == SELECTED )
  1533. aItem->SetSelected();
  1534. else if( aMode == BRIGHTENED )
  1535. aItem->SetBrightened();
  1536. if( aGroup )
  1537. aGroup->Add( aItem );
  1538. // Highlight pins and fields. (All the other symbol children are currently only
  1539. // represented in the LIB_SYMBOL and will inherit the settings of the parent symbol.)
  1540. if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
  1541. {
  1542. sch_item->RunOnChildren(
  1543. [&]( SCH_ITEM* aChild )
  1544. {
  1545. if( aMode == SELECTED )
  1546. {
  1547. aChild->SetSelected();
  1548. getView()->Hide( aChild, true );
  1549. }
  1550. else if( aMode == BRIGHTENED )
  1551. {
  1552. aChild->SetBrightened();
  1553. }
  1554. } );
  1555. }
  1556. if( aGroup && aMode != BRIGHTENED )
  1557. getView()->Hide( aItem, true );
  1558. if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
  1559. getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
  1560. else
  1561. getView()->Update( aItem, KIGFX::REPAINT );
  1562. }
  1563. void EE_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
  1564. {
  1565. KICAD_T itemType = aItem->Type();
  1566. if( aMode == SELECTED )
  1567. {
  1568. aItem->ClearSelected();
  1569. // Lines need endpoints cleared here
  1570. if( aItem->Type() == SCH_LINE_T )
  1571. aItem->ClearFlags( STARTPOINT | ENDPOINT );
  1572. if( aMode != BRIGHTENED )
  1573. getView()->Hide( aItem, false );
  1574. }
  1575. else if( aMode == BRIGHTENED )
  1576. {
  1577. aItem->ClearBrightened();
  1578. }
  1579. if( aGroup )
  1580. aGroup->Remove( aItem );
  1581. // Unhighlight pins and fields. (All the other symbol children are currently only
  1582. // represented in the LIB_SYMBOL.)
  1583. if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
  1584. {
  1585. sch_item->RunOnChildren(
  1586. [&]( SCH_ITEM* aChild )
  1587. {
  1588. if( aMode == SELECTED )
  1589. {
  1590. aChild->ClearSelected();
  1591. getView()->Hide( aChild, false );
  1592. }
  1593. else if( aMode == BRIGHTENED )
  1594. {
  1595. aChild->ClearBrightened();
  1596. }
  1597. } );
  1598. }
  1599. if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
  1600. getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
  1601. else
  1602. getView()->Update( aItem, KIGFX::REPAINT );
  1603. }
  1604. bool EE_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
  1605. {
  1606. const unsigned GRIP_MARGIN = 20;
  1607. double margin = getView()->ToWorld( GRIP_MARGIN );
  1608. // Check if the point is located within any of the currently selected items bounding boxes
  1609. for( EDA_ITEM* item : m_selection )
  1610. {
  1611. BOX2I itemBox = item->ViewBBox();
  1612. itemBox.Inflate( margin ); // Give some margin for gripping an item
  1613. if( itemBox.Contains( aPoint ) )
  1614. return true;
  1615. }
  1616. return false;
  1617. }
  1618. void EE_SELECTION_TOOL::setTransitions()
  1619. {
  1620. Go( &EE_SELECTION_TOOL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
  1621. Go( &EE_SELECTION_TOOL::Main, EE_ACTIONS::selectionActivate.MakeEvent() );
  1622. Go( &EE_SELECTION_TOOL::SelectNode, EE_ACTIONS::selectNode.MakeEvent() );
  1623. Go( &EE_SELECTION_TOOL::SelectConnection, EE_ACTIONS::selectConnection.MakeEvent() );
  1624. Go( &EE_SELECTION_TOOL::ClearSelection, EE_ACTIONS::clearSelection.MakeEvent() );
  1625. Go( &EE_SELECTION_TOOL::AddItemToSel, EE_ACTIONS::addItemToSel.MakeEvent() );
  1626. Go( &EE_SELECTION_TOOL::AddItemsToSel, EE_ACTIONS::addItemsToSel.MakeEvent() );
  1627. Go( &EE_SELECTION_TOOL::RemoveItemFromSel, EE_ACTIONS::removeItemFromSel.MakeEvent() );
  1628. Go( &EE_SELECTION_TOOL::RemoveItemsFromSel, EE_ACTIONS::removeItemsFromSel.MakeEvent() );
  1629. Go( &EE_SELECTION_TOOL::SelectionMenu, EE_ACTIONS::selectionMenu.MakeEvent() );
  1630. Go( &EE_SELECTION_TOOL::SelectAll, EE_ACTIONS::selectAll.MakeEvent() );
  1631. Go( &EE_SELECTION_TOOL::disambiguateCursor, EVENTS::DisambiguatePoint );
  1632. }