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.

1469 lines
43 KiB

  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-2020 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 <class_libentry.h>
  25. #include <core/typeinfo.h>
  26. #include <ee_actions.h>
  27. #include <ee_collectors.h>
  28. #include <ee_selection_tool.h>
  29. #include <eeschema_id.h> // For MAX_SELECT_ITEM_IDS
  30. #include <lib_edit_frame.h>
  31. #include <lib_item.h>
  32. #include <lib_view_frame.h>
  33. #include <math/util.h>
  34. #include <menus_helpers.h>
  35. #include <painter.h>
  36. #include <preview_items/selection_area.h>
  37. #include <sch_base_frame.h>
  38. #include <sch_component.h>
  39. #include <sch_edit_frame.h>
  40. #include <sch_field.h>
  41. #include <sch_item.h>
  42. #include <sch_line.h>
  43. #include <sch_sheet.h>
  44. #include <schematic.h>
  45. #include <tool/tool_event.h>
  46. #include <tool/tool_manager.h>
  47. #include <tools/sch_line_wire_bus_tool.h>
  48. #include <view/view.h>
  49. #include <view/view_controls.h>
  50. #include <view/view_group.h>
  51. SELECTION_CONDITION EE_CONDITIONS::Empty = [] (const SELECTION& aSelection )
  52. {
  53. return aSelection.Empty();
  54. };
  55. SELECTION_CONDITION EE_CONDITIONS::Idle = [] (const SELECTION& aSelection )
  56. {
  57. return ( !aSelection.Front() || aSelection.Front()->GetEditFlags() == 0 );
  58. };
  59. SELECTION_CONDITION EE_CONDITIONS::IdleSelection = [] (const SELECTION& aSelection )
  60. {
  61. return ( aSelection.Front() && aSelection.Front()->GetEditFlags() == 0 );
  62. };
  63. SELECTION_CONDITION EE_CONDITIONS::SingleSymbol = [] (const SELECTION& aSel )
  64. {
  65. if( aSel.GetSize() == 1 )
  66. {
  67. SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
  68. if( comp )
  69. return !comp->GetPartRef() || !comp->GetPartRef()->IsPower();
  70. }
  71. return false;
  72. };
  73. SELECTION_CONDITION EE_CONDITIONS::SingleDeMorganSymbol = [] ( const SELECTION& aSel )
  74. {
  75. if( aSel.GetSize() == 1 )
  76. {
  77. SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
  78. if( comp )
  79. return comp->GetPartRef() && comp->GetPartRef()->HasConversion();
  80. }
  81. return false;
  82. };
  83. SELECTION_CONDITION EE_CONDITIONS::SingleMultiUnitSymbol = [] ( const SELECTION& aSel )
  84. {
  85. if( aSel.GetSize() == 1 )
  86. {
  87. SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
  88. if( comp )
  89. return comp->GetPartRef() && comp->GetPartRef()->GetUnitCount() >= 2;
  90. }
  91. return false;
  92. };
  93. #define HITTEST_THRESHOLD_PIXELS 5
  94. EE_SELECTION_TOOL::EE_SELECTION_TOOL() :
  95. TOOL_INTERACTIVE( "eeschema.InteractiveSelection" ),
  96. m_frame( nullptr ),
  97. m_additive( false ),
  98. m_subtractive( false ),
  99. m_exclusive_or( false ),
  100. m_multiple( false ),
  101. m_skip_heuristics( false ),
  102. m_isLibEdit( false ),
  103. m_isLibView( false ),
  104. m_unit( 0 ),
  105. m_convert( 0 )
  106. {
  107. }
  108. EE_SELECTION_TOOL::~EE_SELECTION_TOOL()
  109. {
  110. getView()->Remove( &m_selection );
  111. }
  112. using E_C = EE_CONDITIONS;
  113. bool EE_SELECTION_TOOL::Init()
  114. {
  115. m_frame = getEditFrame<SCH_BASE_FRAME>();
  116. LIB_VIEW_FRAME* libViewFrame = dynamic_cast<LIB_VIEW_FRAME*>( m_frame );
  117. LIB_EDIT_FRAME* libEditFrame = dynamic_cast<LIB_EDIT_FRAME*>( m_frame );
  118. if( libEditFrame )
  119. {
  120. m_isLibEdit = true;
  121. m_unit = libEditFrame->GetUnit();
  122. m_convert = libEditFrame->GetConvert();
  123. }
  124. else
  125. m_isLibView = libViewFrame != nullptr;
  126. static KICAD_T wireOrBusTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
  127. auto wireSelection = E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_LINE_LOCATE_WIRE_T );
  128. auto busSelection = E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_LINE_LOCATE_BUS_T );
  129. auto wireOrBusSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( wireOrBusTypes );
  130. auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyType( SCH_SHEET_T );
  131. auto schEditCondition = [this] ( const SELECTION& aSel ) {
  132. return !m_isLibEdit && !m_isLibView;
  133. };
  134. auto belowRootSheetCondition =
  135. [&]( const SELECTION& aSel )
  136. {
  137. SCH_EDIT_FRAME* schEditFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  138. return ( schEditFrame&&
  139. schEditFrame->GetCurrentSheet().Last() !=
  140. &schEditFrame->Schematic().Root() );
  141. };
  142. auto havePartCondition =
  143. [&]( const SELECTION& sel )
  144. {
  145. return m_isLibEdit && static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart();
  146. };
  147. auto& menu = m_menu.GetMenu();
  148. menu.AddItem( EE_ACTIONS::enterSheet, sheetSelection && EE_CONDITIONS::Idle, 1 );
  149. menu.AddItem( EE_ACTIONS::explicitCrossProbe, sheetSelection && EE_CONDITIONS::Idle, 1 );
  150. menu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 1 );
  151. menu.AddSeparator( 100 );
  152. menu.AddItem( EE_ACTIONS::drawWire, schEditCondition && EE_CONDITIONS::Empty, 100 );
  153. menu.AddItem( EE_ACTIONS::drawBus, schEditCondition && EE_CONDITIONS::Empty, 100 );
  154. menu.AddSeparator( 100 );
  155. menu.AddItem( EE_ACTIONS::finishWire, SCH_LINE_WIRE_BUS_TOOL::IsDrawingWire, 100 );
  156. menu.AddSeparator( 100 );
  157. menu.AddItem( EE_ACTIONS::finishBus, SCH_LINE_WIRE_BUS_TOOL::IsDrawingBus, 100 );
  158. menu.AddSeparator( 200 );
  159. menu.AddItem( EE_ACTIONS::selectConnection, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  160. menu.AddItem( EE_ACTIONS::placeJunction, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  161. menu.AddItem( EE_ACTIONS::placeLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  162. menu.AddItem( EE_ACTIONS::placeGlobalLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  163. menu.AddItem( EE_ACTIONS::placeHierLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  164. menu.AddItem( EE_ACTIONS::breakWire, wireSelection && EE_CONDITIONS::Idle, 250 );
  165. menu.AddItem( EE_ACTIONS::breakBus, busSelection && EE_CONDITIONS::Idle, 250 );
  166. menu.AddItem( EE_ACTIONS::importSheetPin, sheetSelection && EE_CONDITIONS::Idle, 250 );
  167. menu.AddSeparator( 400 );
  168. menu.AddItem( EE_ACTIONS::symbolProperties, havePartCondition && EE_CONDITIONS::Empty, 400 );
  169. menu.AddItem( EE_ACTIONS::pinTable, havePartCondition && EE_CONDITIONS::Empty, 400 );
  170. menu.AddSeparator( 1000 );
  171. m_frame->AddStandardSubMenus( m_menu );
  172. return true;
  173. }
  174. void EE_SELECTION_TOOL::Reset( RESET_REASON aReason )
  175. {
  176. m_frame = getEditFrame<SCH_BASE_FRAME>();
  177. if( aReason == TOOL_BASE::MODEL_RELOAD )
  178. {
  179. // Remove pointers to the selected items from containers without changing their
  180. // properties (as they are already deleted while a new sheet is loaded)
  181. m_selection.Clear();
  182. getView()->GetPainter()->GetSettings()->SetHighlight( false );
  183. LIB_EDIT_FRAME* libEditFrame = dynamic_cast<LIB_EDIT_FRAME*>( m_frame );
  184. LIB_VIEW_FRAME* libViewFrame = dynamic_cast<LIB_VIEW_FRAME*>( m_frame );
  185. if( libEditFrame )
  186. {
  187. m_isLibEdit = true;
  188. m_unit = libEditFrame->GetUnit();
  189. m_convert = libEditFrame->GetConvert();
  190. }
  191. else
  192. m_isLibView = libViewFrame != nullptr;
  193. }
  194. else
  195. // Restore previous properties of selected items and remove them from containers
  196. ClearSelection();
  197. // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
  198. getView()->Remove( &m_selection );
  199. getView()->Add( &m_selection );
  200. }
  201. int EE_SELECTION_TOOL::UpdateMenu( const TOOL_EVENT& aEvent )
  202. {
  203. ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
  204. CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
  205. if( conditionalMenu )
  206. conditionalMenu->Evaluate( m_selection );
  207. if( actionMenu )
  208. actionMenu->UpdateAll();
  209. return 0;
  210. }
  211. const KICAD_T movableSchematicItems[] =
  212. {
  213. SCH_MARKER_T,
  214. SCH_JUNCTION_T,
  215. SCH_NO_CONNECT_T,
  216. SCH_BUS_BUS_ENTRY_T,
  217. SCH_BUS_WIRE_ENTRY_T,
  218. SCH_LINE_T,
  219. SCH_BITMAP_T,
  220. SCH_TEXT_T,
  221. SCH_LABEL_T,
  222. SCH_GLOBAL_LABEL_T,
  223. SCH_HIER_LABEL_T,
  224. SCH_FIELD_T,
  225. SCH_COMPONENT_T,
  226. SCH_SHEET_PIN_T,
  227. SCH_SHEET_T,
  228. EOT
  229. };
  230. const KICAD_T movableSymbolItems[] =
  231. {
  232. LIB_ARC_T,
  233. LIB_CIRCLE_T,
  234. LIB_TEXT_T,
  235. LIB_RECTANGLE_T,
  236. LIB_POLYLINE_T,
  237. LIB_BEZIER_T,
  238. LIB_PIN_T,
  239. LIB_FIELD_T,
  240. EOT
  241. };
  242. int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
  243. {
  244. // Main loop: keep receiving events
  245. while( TOOL_EVENT* evt = Wait() )
  246. {
  247. if( m_frame->ToolStackIsEmpty() )
  248. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
  249. m_additive = m_subtractive = m_exclusive_or = false;
  250. if( evt->Modifier( MD_SHIFT ) && evt->Modifier( MD_CTRL ) )
  251. m_subtractive = true;
  252. else if( evt->Modifier( MD_SHIFT ) )
  253. m_additive = true;
  254. else if( evt->Modifier( MD_CTRL ) )
  255. m_exclusive_or = true;
  256. // Is the user requesting that the selection list include all possible
  257. // items without removing less likely selection candidates
  258. m_skip_heuristics = !!evt->Modifier( MD_ALT );
  259. // Single click? Select single object
  260. if( evt->IsClick( BUT_LEFT ) )
  261. {
  262. if( auto schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  263. schframe->FocusOnItem( nullptr );
  264. SelectPoint( evt->Position(), EE_COLLECTOR::AllItems, nullptr, nullptr, false,
  265. m_additive, m_subtractive, m_exclusive_or );
  266. }
  267. // right click? if there is any object - show the context menu
  268. else if( evt->IsClick( BUT_RIGHT ) )
  269. {
  270. bool selectionCancelled = false;
  271. if( m_selection.Empty() ||
  272. !m_selection.GetBoundingBox().Contains( wxPoint( evt->Position() ) ) )
  273. {
  274. ClearSelection();
  275. SelectPoint( evt->Position(), EE_COLLECTOR::AllItems, nullptr,
  276. &selectionCancelled );
  277. m_selection.SetIsHover( true );
  278. }
  279. if( !selectionCancelled )
  280. m_menu.ShowContextMenu( m_selection );
  281. }
  282. // double click? Display the properties window
  283. else if( evt->IsDblClick( BUT_LEFT ) )
  284. {
  285. if( auto schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  286. schframe->FocusOnItem( nullptr );
  287. if( m_selection.Empty() )
  288. SelectPoint( evt->Position());
  289. EDA_ITEM* item = m_selection.Front();
  290. if( item && item->Type() == SCH_SHEET_T )
  291. m_toolMgr->RunAction( EE_ACTIONS::enterSheet );
  292. else
  293. m_toolMgr->RunAction( EE_ACTIONS::properties );
  294. }
  295. // drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
  296. else if( evt->IsDrag( BUT_LEFT ) )
  297. {
  298. if( auto schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  299. schframe->FocusOnItem( nullptr );
  300. if( m_additive || m_subtractive || m_exclusive_or || m_frame->GetDragSelects() )
  301. {
  302. selectMultiple();
  303. }
  304. else
  305. {
  306. // selection is empty? try to start dragging the item under the point where drag
  307. // started
  308. if( m_isLibEdit && m_selection.Empty() )
  309. m_selection = RequestSelection( movableSymbolItems );
  310. else if( m_selection.Empty() )
  311. m_selection = RequestSelection( movableSchematicItems );
  312. // Check if dragging has started within any of selected items bounding box
  313. if( selectionContains( evt->Position() ) )
  314. {
  315. // Yes -> run the move tool and wait till it finishes
  316. if( m_isLibEdit )
  317. m_toolMgr->InvokeTool( "eeschema.SymbolMoveTool" );
  318. else
  319. m_toolMgr->InvokeTool( "eeschema.InteractiveMove" );
  320. }
  321. else
  322. {
  323. // No -> drag a selection box
  324. selectMultiple();
  325. }
  326. }
  327. }
  328. // context sub-menu selection? Handle unit selection or bus unfolding
  329. else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
  330. {
  331. if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
  332. && evt->GetCommandId().get() <= ID_POPUP_SCH_SELECT_UNIT_CMP_MAX )
  333. {
  334. SCH_COMPONENT* component = dynamic_cast<SCH_COMPONENT*>( m_selection.Front() );
  335. int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
  336. if( component )
  337. static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( component, unit );
  338. }
  339. else if( evt->GetCommandId().get() >= ID_POPUP_SCH_UNFOLD_BUS
  340. && evt->GetCommandId().get() <= ID_POPUP_SCH_UNFOLD_BUS_END )
  341. {
  342. wxString* net = new wxString( *evt->Parameter<wxString*>() );
  343. m_toolMgr->RunAction( EE_ACTIONS::unfoldBus, true, net );
  344. }
  345. }
  346. else if( evt->IsCancelInteractive() )
  347. {
  348. if( auto schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  349. schframe->FocusOnItem( nullptr );
  350. ClearSelection();
  351. }
  352. else if( evt->Action() == TA_UNDO_REDO_PRE )
  353. {
  354. if( auto schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  355. schframe->FocusOnItem( nullptr );
  356. ClearSelection();
  357. }
  358. else
  359. evt->SetPassEvent();
  360. }
  361. return 0;
  362. }
  363. EE_SELECTION& EE_SELECTION_TOOL::GetSelection()
  364. {
  365. return m_selection;
  366. }
  367. bool EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T* aFilterList,
  368. EDA_ITEM** aItem, bool* aSelectionCancelledFlag, bool aCheckLocked, bool aAdd,
  369. bool aSubtract, bool aExclusiveOr )
  370. {
  371. EE_COLLECTOR collector;
  372. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  373. if( m_isLibEdit )
  374. {
  375. auto part = static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart();
  376. if( !part )
  377. return false;
  378. collector.Collect( part->GetDrawItems(), aFilterList, (wxPoint) aWhere, m_unit, m_convert );
  379. }
  380. else
  381. collector.Collect( m_frame->GetScreen(), aFilterList, (wxPoint) aWhere, m_unit, m_convert );
  382. // Post-process collected items
  383. for( int i = collector.GetCount() - 1; i >= 0; --i )
  384. {
  385. if( !Selectable( collector[ i ] ) )
  386. {
  387. collector.Remove( i );
  388. continue;
  389. }
  390. if( aCheckLocked && collector[ i ]->IsLocked() )
  391. {
  392. collector.Remove( i );
  393. continue;
  394. }
  395. // SelectPoint, unlike other selection routines, can select line ends
  396. if( collector[ i ]->Type() == SCH_LINE_T )
  397. {
  398. SCH_LINE* line = (SCH_LINE*) collector[ i ];
  399. line->ClearFlags( STARTPOINT | ENDPOINT );
  400. if( HitTestPoints( line->GetStartPoint(), (wxPoint) aWhere, collector.m_Threshold ) )
  401. line->SetFlags( STARTPOINT );
  402. else if (HitTestPoints( line->GetEndPoint(), (wxPoint) aWhere, collector.m_Threshold ) )
  403. line->SetFlags( ENDPOINT );
  404. else
  405. line->SetFlags( STARTPOINT | ENDPOINT );
  406. }
  407. }
  408. m_selection.ClearReferencePoint();
  409. // Apply some ugly heuristics to avoid disambiguation menus whenever possible
  410. if( collector.GetCount() > 1 && !m_skip_heuristics )
  411. {
  412. GuessSelectionCandidates( collector, aWhere );
  413. }
  414. // If still more than one item we're going to have to ask the user.
  415. if( collector.GetCount() > 1 )
  416. {
  417. collector.m_MenuTitle = _( "Clarify Selection" );
  418. // Must call selectionMenu via RunAction() to avoid event-loop contention
  419. m_toolMgr->RunAction( EE_ACTIONS::selectionMenu, true, &collector );
  420. if( collector.m_MenuCancelled )
  421. {
  422. if( aSelectionCancelledFlag )
  423. *aSelectionCancelledFlag = true;
  424. return false;
  425. }
  426. }
  427. if( !aAdd && !aSubtract && !aExclusiveOr )
  428. ClearSelection();
  429. bool anyAdded = false;
  430. bool anySubtracted = false;
  431. if( collector.GetCount() > 0 )
  432. {
  433. for( int i = 0; i < collector.GetCount(); ++i )
  434. {
  435. if( aSubtract || ( aExclusiveOr && collector[i]->IsSelected() ) )
  436. {
  437. unselect( collector[i] );
  438. anySubtracted = true;
  439. }
  440. else
  441. {
  442. select( collector[i] );
  443. anyAdded = true;
  444. }
  445. }
  446. }
  447. if( anyAdded )
  448. {
  449. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  450. if( aItem && collector.GetCount() == 1 )
  451. *aItem = collector[0];
  452. return true;
  453. }
  454. else if( anySubtracted )
  455. {
  456. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  457. return true;
  458. }
  459. return false;
  460. }
  461. void EE_SELECTION_TOOL::GuessSelectionCandidates( EE_COLLECTOR& collector, const VECTOR2I& aPos )
  462. {
  463. // There are certain parent/child and enclosure combinations that can be handled
  464. // automatically.
  465. // Prefer exact hits to sloppy ones
  466. int exactHits = 0;
  467. for( int i = collector.GetCount() - 1; i >= 0; --i )
  468. {
  469. EDA_ITEM* item = collector[ i ];
  470. if( item->HitTest( (wxPoint) aPos, 0 ) )
  471. exactHits++;
  472. }
  473. if( exactHits > 0 && exactHits < collector.GetCount() )
  474. {
  475. for( int i = collector.GetCount() - 1; i >= 0; --i )
  476. {
  477. EDA_ITEM* item = collector[ i ];
  478. if( !item->HitTest( (wxPoint) aPos, 0 ) )
  479. collector.Remove( item );
  480. }
  481. }
  482. // Prefer a non-sheet to a sheet
  483. for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
  484. {
  485. EDA_ITEM* item = collector[ i ];
  486. EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
  487. if( item->Type() != SCH_SHEET_T && other->Type() == SCH_SHEET_T )
  488. collector.Remove( other );
  489. }
  490. // Prefer a symbol to a pin or the opposite, when both a symbol and a pin are selected
  491. // We need to be able to select only a pin:
  492. // - to display its characteristics (especially if an ERC is attached to the pin)
  493. // - for cross probing, to select the corresponding pad.
  494. // Note also the case happens only in schematic editor. In symbol editor, the symbol
  495. // itself is never selected
  496. for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
  497. {
  498. EDA_ITEM* item = collector[ i ];
  499. EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
  500. if( item->Type() == SCH_COMPONENT_T && other->Type() == SCH_PIN_T )
  501. {
  502. if( !m_isLibEdit && m_frame->eeconfig()->m_Selection.select_pin_selects_symbol )
  503. collector.Remove( other );
  504. else
  505. collector.Remove( item );
  506. }
  507. }
  508. // Prefer a field to a symbol
  509. for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
  510. {
  511. EDA_ITEM* item = collector[ i ];
  512. EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
  513. if( item->Type() == SCH_FIELD_T && other->Type() == SCH_COMPONENT_T )
  514. collector.Remove( other );
  515. }
  516. // No need for multiple wires at a single point; if there's a junction select that;
  517. // otherwise any of the wires will do
  518. bool junction = false;
  519. bool wiresOnly = true;
  520. for( EDA_ITEM* item : collector )
  521. {
  522. if( item->Type() == SCH_JUNCTION_T )
  523. junction = true;
  524. else if( item->Type() != SCH_LINE_T )
  525. wiresOnly = false;
  526. }
  527. if( wiresOnly )
  528. {
  529. for( int j = collector.GetCount() - 1; j >= 0; --j )
  530. {
  531. if( junction && collector[ j ]->Type() != SCH_JUNCTION_T )
  532. collector.Remove( j );
  533. else if( !junction && j > 0 )
  534. collector.Remove( j );
  535. }
  536. }
  537. }
  538. EE_SELECTION& EE_SELECTION_TOOL::RequestSelection( const KICAD_T aFilterList[] )
  539. {
  540. if( m_selection.Empty() )
  541. {
  542. VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
  543. ClearSelection();
  544. SelectPoint( cursorPos, aFilterList );
  545. m_selection.SetIsHover( true );
  546. m_selection.ClearReferencePoint();
  547. }
  548. else // Trim an existing selection by aFilterList
  549. {
  550. for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
  551. {
  552. EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
  553. if( !item->IsType( aFilterList ) )
  554. {
  555. unselect( item );
  556. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  557. }
  558. }
  559. }
  560. updateReferencePoint();
  561. return m_selection;
  562. }
  563. void EE_SELECTION_TOOL::updateReferencePoint()
  564. {
  565. VECTOR2I refP( 0, 0 );
  566. if( m_selection.Size() > 0 )
  567. {
  568. if( m_isLibEdit )
  569. refP = static_cast<LIB_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
  570. else
  571. refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
  572. }
  573. m_selection.SetReferencePoint( refP );
  574. }
  575. bool EE_SELECTION_TOOL::selectMultiple()
  576. {
  577. bool cancelled = false; // Was the tool cancelled while it was running?
  578. m_multiple = true; // Multiple selection mode is active
  579. KIGFX::VIEW* view = getView();
  580. KIGFX::PREVIEW::SELECTION_AREA area;
  581. view->Add( &area );
  582. while( TOOL_EVENT* evt = Wait() )
  583. {
  584. if( evt->IsCancelInteractive() || evt->IsActivate() )
  585. {
  586. cancelled = true;
  587. break;
  588. }
  589. if( evt->IsDrag( BUT_LEFT ) )
  590. {
  591. if( !m_additive && !m_subtractive && !m_exclusive_or )
  592. ClearSelection();
  593. // Start drawing a selection box
  594. area.SetOrigin( evt->DragOrigin() );
  595. area.SetEnd( evt->Position() );
  596. area.SetAdditive( m_additive );
  597. area.SetSubtractive( m_subtractive );
  598. area.SetExclusiveOr( m_exclusive_or );
  599. view->SetVisible( &area, true );
  600. view->Update( &area );
  601. getViewControls()->SetAutoPan( true );
  602. }
  603. if( evt->IsMouseUp( BUT_LEFT ) )
  604. {
  605. getViewControls()->SetAutoPan( false );
  606. // End drawing the selection box
  607. view->SetVisible( &area, false );
  608. // Mark items within the selection box as selected
  609. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
  610. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> sheetPins;
  611. // Filter the view items based on the selection box
  612. BOX2I selectionBox = area.ViewBBox();
  613. view->Query( selectionBox, selectedItems ); // Get the list of selected items
  614. // Sheet pins aren't in the view; add them by hand
  615. for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
  616. {
  617. SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
  618. if( sheet )
  619. {
  620. int layer = pair.second;
  621. for( SCH_SHEET_PIN* pin : sheet->GetPins() )
  622. sheetPins.emplace_back( KIGFX::VIEW::LAYER_ITEM_PAIR( pin, layer ) );
  623. }
  624. }
  625. selectedItems.insert( selectedItems.end(), sheetPins.begin(), sheetPins.end() );
  626. int width = area.GetEnd().x - area.GetOrigin().x;
  627. int height = area.GetEnd().y - area.GetOrigin().y;
  628. /* Selection mode depends on direction of drag-selection:
  629. * Left > Right : Select objects that are fully enclosed by selection
  630. * Right > Left : Select objects that are crossed by selection
  631. */
  632. bool windowSelection = width >= 0;
  633. bool anyAdded = false;
  634. bool anySubtracted = false;
  635. if( view->IsMirroredX() )
  636. windowSelection = !windowSelection;
  637. // Construct an EDA_RECT to determine EDA_ITEM selection
  638. EDA_RECT selectionRect( (wxPoint) area.GetOrigin(), wxSize( width, height ) );
  639. selectionRect.Normalize();
  640. for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
  641. {
  642. EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first );
  643. if( item && Selectable( item ) && item->HitTest( selectionRect, windowSelection ) )
  644. {
  645. if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
  646. {
  647. unselect( item );
  648. anySubtracted = true;
  649. }
  650. else
  651. {
  652. select( item );
  653. item->SetFlags( STARTPOINT | ENDPOINT );
  654. anyAdded = true;
  655. }
  656. }
  657. }
  658. m_selection.SetIsHover( false );
  659. // Inform other potentially interested tools
  660. if( anyAdded )
  661. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  662. if( anySubtracted )
  663. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  664. break; // Stop waiting for events
  665. }
  666. }
  667. getViewControls()->SetAutoPan( false );
  668. // Stop drawing the selection box
  669. view->Remove( &area );
  670. m_multiple = false; // Multiple selection mode is inactive
  671. if( !cancelled )
  672. m_selection.ClearReferencePoint();
  673. return cancelled;
  674. }
  675. static KICAD_T nodeTypes[] =
  676. {
  677. SCH_COMPONENT_LOCATE_POWER_T,
  678. SCH_PIN_T,
  679. SCH_LINE_LOCATE_WIRE_T,
  680. SCH_LINE_LOCATE_BUS_T,
  681. SCH_BUS_WIRE_ENTRY_T,
  682. SCH_BUS_BUS_ENTRY_T,
  683. SCH_LABEL_T,
  684. SCH_HIER_LABEL_T,
  685. SCH_GLOBAL_LABEL_T,
  686. SCH_SHEET_PIN_T,
  687. SCH_JUNCTION_T,
  688. EOT
  689. };
  690. EDA_ITEM* EE_SELECTION_TOOL::GetNode( VECTOR2I aPosition )
  691. {
  692. EE_COLLECTOR collector;
  693. //TODO(snh): Reimplement after exposing KNN interface
  694. int thresholdMax = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  695. for( int threshold : { 0, thresholdMax/2, thresholdMax } )
  696. {
  697. collector.m_Threshold = threshold;
  698. collector.Collect( m_frame->GetScreen(), nodeTypes, (wxPoint) aPosition );
  699. if( collector.GetCount() > 0 )
  700. break;
  701. }
  702. return collector.GetCount() ? collector[ 0 ] : nullptr;
  703. }
  704. int EE_SELECTION_TOOL::SelectNode( const TOOL_EVENT& aEvent )
  705. {
  706. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !aEvent.Modifier( MD_ALT ) );
  707. SelectPoint( cursorPos, nodeTypes );
  708. return 0;
  709. }
  710. int EE_SELECTION_TOOL::SelectConnection( const TOOL_EVENT& aEvent )
  711. {
  712. static KICAD_T wiresAndBuses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
  713. RequestSelection( wiresAndBuses );
  714. if( m_selection.Empty() )
  715. return 0;
  716. SCH_LINE* line = (SCH_LINE*) m_selection.Front();
  717. EDA_ITEMS items;
  718. m_frame->GetScreen()->ClearDrawingState();
  719. auto conns = m_frame->GetScreen()->MarkConnections( line );
  720. for( auto item : conns )
  721. select( item );
  722. if( m_selection.GetSize() > 1 )
  723. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  724. return 0;
  725. }
  726. int EE_SELECTION_TOOL::AddItemToSel( const TOOL_EVENT& aEvent )
  727. {
  728. AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
  729. m_selection.SetIsHover( false );
  730. return 0;
  731. }
  732. void EE_SELECTION_TOOL::AddItemToSel( EDA_ITEM* aItem, bool aQuietMode )
  733. {
  734. if( aItem )
  735. {
  736. select( aItem );
  737. // Inform other potentially interested tools
  738. if( !aQuietMode )
  739. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  740. }
  741. }
  742. int EE_SELECTION_TOOL::AddItemsToSel( const TOOL_EVENT& aEvent )
  743. {
  744. AddItemsToSel( aEvent.Parameter<EDA_ITEMS*>(), false );
  745. m_selection.SetIsHover( false );
  746. return 0;
  747. }
  748. void EE_SELECTION_TOOL::AddItemsToSel( EDA_ITEMS* aList, bool aQuietMode )
  749. {
  750. if( aList )
  751. {
  752. for( EDA_ITEM* item : *aList )
  753. select( item );
  754. // Inform other potentially interested tools
  755. if( !aQuietMode )
  756. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  757. }
  758. }
  759. int EE_SELECTION_TOOL::RemoveItemFromSel( const TOOL_EVENT& aEvent )
  760. {
  761. RemoveItemFromSel( aEvent.Parameter<EDA_ITEM*>() );
  762. m_selection.SetIsHover( false );
  763. return 0;
  764. }
  765. void EE_SELECTION_TOOL::RemoveItemFromSel( EDA_ITEM* aItem, bool aQuietMode )
  766. {
  767. if( aItem )
  768. {
  769. unselect( aItem );
  770. // Inform other potentially interested tools
  771. if( !aQuietMode )
  772. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  773. }
  774. }
  775. int EE_SELECTION_TOOL::RemoveItemsFromSel( const TOOL_EVENT& aEvent )
  776. {
  777. RemoveItemsFromSel( aEvent.Parameter<EDA_ITEMS*>(), false );
  778. m_selection.SetIsHover( false );
  779. return 0;
  780. }
  781. void EE_SELECTION_TOOL::RemoveItemsFromSel( EDA_ITEMS* aList, bool aQuietMode )
  782. {
  783. if( aList )
  784. {
  785. for( EDA_ITEM* item : *aList )
  786. unselect( item );
  787. // Inform other potentially interested tools
  788. if( !aQuietMode )
  789. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  790. }
  791. }
  792. void EE_SELECTION_TOOL::BrightenItem( EDA_ITEM* aItem )
  793. {
  794. highlight( aItem, BRIGHTENED );
  795. }
  796. void EE_SELECTION_TOOL::UnbrightenItem( EDA_ITEM* aItem )
  797. {
  798. unhighlight( aItem, BRIGHTENED );
  799. }
  800. int EE_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
  801. {
  802. ClearSelection();
  803. return 0;
  804. }
  805. void EE_SELECTION_TOOL::RebuildSelection()
  806. {
  807. m_selection.Clear();
  808. if( m_isLibEdit )
  809. {
  810. LIB_PART* start = static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart();
  811. for( LIB_ITEM& item : start->GetDrawItems() )
  812. {
  813. if( item.IsSelected() )
  814. select( static_cast<EDA_ITEM*>( &item ) );
  815. }
  816. }
  817. else
  818. {
  819. for( auto item : m_frame->GetScreen()->Items() )
  820. {
  821. // If the field and component are selected, only use the component
  822. if( item->IsSelected() )
  823. {
  824. select( item );
  825. }
  826. else
  827. {
  828. if( item->Type() == SCH_COMPONENT_T )
  829. {
  830. for( SCH_FIELD& field : static_cast<SCH_COMPONENT*>( item )->GetFields() )
  831. {
  832. if( field.IsSelected() )
  833. select( &field );
  834. }
  835. }
  836. if( item->Type() == SCH_SHEET_T )
  837. {
  838. for( SCH_FIELD& field : static_cast<SCH_SHEET*>( item )->GetFields() )
  839. {
  840. if( field.IsSelected() )
  841. select( &field );
  842. }
  843. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
  844. {
  845. if( pin->IsSelected() )
  846. select( pin );
  847. }
  848. }
  849. }
  850. }
  851. }
  852. updateReferencePoint();
  853. // Inform other potentially interested tools
  854. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  855. }
  856. int EE_SELECTION_TOOL::SelectionMenu( const TOOL_EVENT& aEvent )
  857. {
  858. EE_COLLECTOR* collector = aEvent.Parameter<EE_COLLECTOR*>();
  859. if( !doSelectionMenu( collector ) )
  860. collector->m_MenuCancelled = true;
  861. return 0;
  862. }
  863. bool EE_SELECTION_TOOL::doSelectionMenu( EE_COLLECTOR* aCollector )
  864. {
  865. EDA_ITEM* current = nullptr;
  866. ACTION_MENU menu( true );
  867. bool selectAll = false;
  868. int limit = std::min( MAX_SELECT_ITEM_IDS, aCollector->GetCount() );
  869. for( int i = 0; i < limit; ++i )
  870. {
  871. wxString text;
  872. EDA_ITEM* item = ( *aCollector )[i];
  873. text = item->GetSelectMenuText( m_frame->GetUserUnits() );
  874. wxString menuText = wxString::Format( "&%d. %s\t%d", i + 1, text, i + 1 );
  875. menu.Add( menuText, i + 1, item->GetMenuImage() );
  876. }
  877. menu.AppendSeparator();
  878. menu.Add( _( "Select &All\tA" ), limit + 1, net_highlight_schematic_xpm );
  879. if( aCollector->m_MenuTitle.Length() )
  880. menu.SetTitle( aCollector->m_MenuTitle );
  881. menu.SetIcon( info_xpm );
  882. menu.DisplayTitle( true );
  883. SetContextMenu( &menu, CMENU_NOW );
  884. while( TOOL_EVENT* evt = Wait() )
  885. {
  886. if( evt->Action() == TA_CHOICE_MENU_UPDATE )
  887. {
  888. if( selectAll )
  889. {
  890. for( int i = 0; i < aCollector->GetCount(); ++i )
  891. unhighlight( ( *aCollector )[i], BRIGHTENED );
  892. }
  893. else if( current )
  894. {
  895. unhighlight( current, BRIGHTENED );
  896. }
  897. int id = *evt->GetCommandId();
  898. // User has pointed an item, so show it in a different way
  899. if( id > 0 && id <= limit )
  900. {
  901. current = ( *aCollector )[id - 1];
  902. highlight( current, BRIGHTENED );
  903. }
  904. else
  905. {
  906. current = nullptr;
  907. }
  908. // User has pointed on the "Select All" option
  909. if( id == limit + 1 )
  910. {
  911. for( int i = 0; i < aCollector->GetCount(); ++i )
  912. highlight( ( *aCollector )[i], BRIGHTENED );
  913. selectAll = true;
  914. }
  915. else
  916. {
  917. selectAll = false;
  918. }
  919. }
  920. else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
  921. {
  922. if( selectAll )
  923. {
  924. for( int i = 0; i < aCollector->GetCount(); ++i )
  925. unhighlight( ( *aCollector )[i], BRIGHTENED );
  926. }
  927. else if( current )
  928. unhighlight( current, BRIGHTENED );
  929. OPT<int> id = evt->GetCommandId();
  930. // User has selected the "Select All" option
  931. if( id == limit + 1 )
  932. {
  933. selectAll = true;
  934. current = nullptr;
  935. }
  936. // User has selected an item, so this one will be returned
  937. else if( id && ( *id > 0 ) && ( *id <= limit ) )
  938. {
  939. selectAll = false;
  940. current = ( *aCollector )[*id - 1];
  941. }
  942. else
  943. {
  944. selectAll = false;
  945. current = nullptr;
  946. }
  947. break;
  948. }
  949. getView()->UpdateItems();
  950. m_frame->GetCanvas()->Refresh();
  951. }
  952. if( selectAll )
  953. return true;
  954. else if( current )
  955. {
  956. unhighlight( current, BRIGHTENED );
  957. getView()->UpdateItems();
  958. m_frame->GetCanvas()->Refresh();
  959. aCollector->Empty();
  960. aCollector->Append( current );
  961. return true;
  962. }
  963. return false;
  964. }
  965. bool EE_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, bool checkVisibilityOnly ) const
  966. {
  967. // NOTE: in the future this is where eeschema layer/itemtype visibility will be handled
  968. LIB_EDIT_FRAME* symbeditFrame = dynamic_cast< LIB_EDIT_FRAME* >( m_frame );
  969. switch( aItem->Type() )
  970. {
  971. case SCH_PIN_T:
  972. if( !static_cast<const SCH_PIN*>( aItem )->IsVisible() && !m_frame->GetShowAllPins() )
  973. return false;
  974. break;
  975. case LIB_PART_T: // In libedit we do not want to select the symbol itself.
  976. return false;
  977. case LIB_FIELD_T:
  978. {
  979. if( symbeditFrame )
  980. {
  981. LIB_PART* currentPart = symbeditFrame->GetCurPart();
  982. // Nothing in derived symbols is editable at the moment.
  983. if( currentPart && currentPart->IsAlias() )
  984. return false;
  985. }
  986. break;
  987. }
  988. case LIB_ARC_T:
  989. case LIB_CIRCLE_T:
  990. case LIB_TEXT_T:
  991. case LIB_RECTANGLE_T:
  992. case LIB_POLYLINE_T:
  993. case LIB_BEZIER_T:
  994. case LIB_PIN_T:
  995. {
  996. if( symbeditFrame )
  997. {
  998. LIB_ITEM* lib_item = (LIB_ITEM*) aItem;
  999. if( lib_item->GetUnit() && lib_item->GetUnit() != symbeditFrame->GetUnit() )
  1000. return false;
  1001. if( lib_item->GetConvert() && lib_item->GetConvert() != symbeditFrame->GetConvert() )
  1002. return false;
  1003. }
  1004. break;
  1005. }
  1006. case SCH_MARKER_T: // Always selectable
  1007. return true;
  1008. default: // Suppress warnings
  1009. break;
  1010. }
  1011. return true;
  1012. }
  1013. void EE_SELECTION_TOOL::ClearSelection()
  1014. {
  1015. if( m_selection.Empty() )
  1016. return;
  1017. while( m_selection.GetSize() )
  1018. unhighlight( (EDA_ITEM*) m_selection.Front(), SELECTED, &m_selection );
  1019. getView()->Update( &m_selection );
  1020. m_selection.SetIsHover( false );
  1021. m_selection.ClearReferencePoint();
  1022. // Inform other potentially interested tools
  1023. m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
  1024. }
  1025. void EE_SELECTION_TOOL::select( EDA_ITEM* aItem )
  1026. {
  1027. highlight( aItem, SELECTED, &m_selection );
  1028. }
  1029. void EE_SELECTION_TOOL::unselect( EDA_ITEM* aItem )
  1030. {
  1031. unhighlight( aItem, SELECTED, &m_selection );
  1032. }
  1033. void EE_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, EE_SELECTION* aGroup )
  1034. {
  1035. KICAD_T itemType = aItem->Type();
  1036. if( aMode == SELECTED )
  1037. aItem->SetSelected();
  1038. else if( aMode == BRIGHTENED )
  1039. aItem->SetBrightened();
  1040. if( aGroup )
  1041. aGroup->Add( aItem );
  1042. // Highlight pins and fields. (All the other component children are currently only
  1043. // represented in the LIB_PART and will inherit the settings of the parent component.)
  1044. if( itemType == SCH_COMPONENT_T )
  1045. {
  1046. if( auto schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  1047. {
  1048. SCH_PIN_PTRS pins = static_cast<SCH_COMPONENT*>( aItem )->GetSchPins(
  1049. &schframe->GetCurrentSheet() );
  1050. for( SCH_PIN* pin : pins )
  1051. {
  1052. if( aMode == SELECTED )
  1053. pin->SetSelected();
  1054. else if( aMode == BRIGHTENED )
  1055. pin->SetBrightened();
  1056. }
  1057. }
  1058. for( SCH_FIELD& field : static_cast<SCH_COMPONENT*>( aItem )->GetFields() )
  1059. {
  1060. if( aMode == SELECTED )
  1061. field.SetSelected();
  1062. else if( aMode == BRIGHTENED )
  1063. field.SetBrightened();
  1064. }
  1065. }
  1066. else if( itemType == SCH_SHEET_T )
  1067. {
  1068. for( SCH_FIELD& field : static_cast<SCH_SHEET*>( aItem )->GetFields() )
  1069. {
  1070. if( aMode == SELECTED )
  1071. field.SetSelected();
  1072. else if( aMode == BRIGHTENED )
  1073. field.SetBrightened();
  1074. }
  1075. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( aItem )->GetPins() )
  1076. {
  1077. if( aMode == SELECTED )
  1078. pin->SetSelected();
  1079. else if( aMode == BRIGHTENED )
  1080. pin->SetBrightened();
  1081. }
  1082. }
  1083. if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
  1084. getView()->Update( aItem->GetParent() );
  1085. else
  1086. getView()->Update( aItem );
  1087. }
  1088. void EE_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, EE_SELECTION* aGroup )
  1089. {
  1090. KICAD_T itemType = aItem->Type();
  1091. if( aMode == SELECTED )
  1092. aItem->ClearSelected();
  1093. else if( aMode == BRIGHTENED )
  1094. aItem->ClearBrightened();
  1095. if( aGroup )
  1096. aGroup->Remove( aItem );
  1097. // Unhighlight pins and fields. (All the other component children are currently only
  1098. // represented in the LIB_PART.)
  1099. if( itemType == SCH_COMPONENT_T )
  1100. {
  1101. if( auto schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  1102. {
  1103. SCH_PIN_PTRS pins = static_cast<SCH_COMPONENT*>( aItem )->GetSchPins(
  1104. &schframe->GetCurrentSheet() );
  1105. for( SCH_PIN* pin : pins )
  1106. {
  1107. if( aMode == SELECTED )
  1108. pin->ClearSelected();
  1109. else if( aMode == BRIGHTENED )
  1110. pin->ClearBrightened();
  1111. }
  1112. }
  1113. for( SCH_FIELD& field : static_cast<SCH_COMPONENT*>( aItem )->GetFields() )
  1114. {
  1115. if( aMode == SELECTED )
  1116. field.ClearSelected();
  1117. else if( aMode == BRIGHTENED )
  1118. field.ClearBrightened();
  1119. }
  1120. }
  1121. else if( itemType == SCH_SHEET_T )
  1122. {
  1123. for( SCH_FIELD& field : static_cast<SCH_SHEET*>( aItem )->GetFields() )
  1124. {
  1125. if( aMode == SELECTED )
  1126. field.ClearSelected();
  1127. else if( aMode == BRIGHTENED )
  1128. field.ClearBrightened();
  1129. }
  1130. for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( aItem )->GetPins() )
  1131. {
  1132. if( aMode == SELECTED )
  1133. pin->ClearSelected();
  1134. else if( aMode == BRIGHTENED )
  1135. pin->ClearBrightened();
  1136. }
  1137. }
  1138. if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
  1139. getView()->Update( aItem->GetParent() );
  1140. else
  1141. getView()->Update( aItem );
  1142. }
  1143. bool EE_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
  1144. {
  1145. const unsigned GRIP_MARGIN = 20;
  1146. VECTOR2I margin = getView()->ToWorld( VECTOR2I( GRIP_MARGIN, GRIP_MARGIN ), false );
  1147. // Check if the point is located within any of the currently selected items bounding boxes
  1148. for( auto item : m_selection )
  1149. {
  1150. BOX2I itemBox = item->ViewBBox();
  1151. itemBox.Inflate( margin.x, margin.y ); // Give some margin for gripping an item
  1152. if( itemBox.Contains( aPoint ) )
  1153. return true;
  1154. }
  1155. return false;
  1156. }
  1157. void EE_SELECTION_TOOL::setTransitions()
  1158. {
  1159. Go( &EE_SELECTION_TOOL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
  1160. Go( &EE_SELECTION_TOOL::Main, EE_ACTIONS::selectionActivate.MakeEvent() );
  1161. Go( &EE_SELECTION_TOOL::SelectNode, EE_ACTIONS::selectNode.MakeEvent() );
  1162. Go( &EE_SELECTION_TOOL::SelectConnection, EE_ACTIONS::selectConnection.MakeEvent() );
  1163. Go( &EE_SELECTION_TOOL::ClearSelection, EE_ACTIONS::clearSelection.MakeEvent() );
  1164. Go( &EE_SELECTION_TOOL::AddItemToSel, EE_ACTIONS::addItemToSel.MakeEvent() );
  1165. Go( &EE_SELECTION_TOOL::AddItemsToSel, EE_ACTIONS::addItemsToSel.MakeEvent() );
  1166. Go( &EE_SELECTION_TOOL::RemoveItemFromSel, EE_ACTIONS::removeItemFromSel.MakeEvent() );
  1167. Go( &EE_SELECTION_TOOL::RemoveItemsFromSel, EE_ACTIONS::removeItemsFromSel.MakeEvent() );
  1168. Go( &EE_SELECTION_TOOL::SelectionMenu, EE_ACTIONS::selectionMenu.MakeEvent() );
  1169. }