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.

2812 lines
93 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
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
5 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 CERN
  5. * Copyright (C) 2019-2024 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <advanced_config.h>
  25. #include <core/typeinfo.h>
  26. #include <core/kicad_algo.h>
  27. #include <gal/graphics_abstraction_layer.h>
  28. #include <geometry/shape_compound.h>
  29. #include <ee_actions.h>
  30. #include <ee_collectors.h>
  31. #include <ee_selection_tool.h>
  32. #include <eeschema_id.h>
  33. #include <symbol_edit_frame.h>
  34. #include <symbol_viewer_frame.h>
  35. #include <math/util.h>
  36. #include <geometry/shape_rect.h>
  37. #include <sch_painter.h>
  38. #include <preview_items/selection_area.h>
  39. #include <sch_base_frame.h>
  40. #include <sch_commit.h>
  41. #include <sch_symbol.h>
  42. #include <sch_field.h>
  43. #include <sch_edit_frame.h>
  44. #include <sch_item.h>
  45. #include <sch_line.h>
  46. #include <sch_bus_entry.h>
  47. #include <sch_marker.h>
  48. #include <sch_no_connect.h>
  49. #include <sch_render_settings.h>
  50. #include <sch_sheet.h>
  51. #include <sch_sheet_pin.h>
  52. #include <sch_table.h>
  53. #include <sch_tablecell.h>
  54. #include <schematic.h>
  55. #include <tool/tool_event.h>
  56. #include <tool/tool_manager.h>
  57. #include <tools/ee_grid_helper.h>
  58. #include <tools/ee_point_editor.h>
  59. #include <tools/sch_line_wire_bus_tool.h>
  60. #include <tools/sch_editor_control.h>
  61. #include <trigo.h>
  62. #include <view/view.h>
  63. #include <view/view_controls.h>
  64. #include <wx/log.h>
  65. #include "symb_transforms_utils.h"
  66. SELECTION_CONDITION EE_CONDITIONS::SingleSymbol = []( const SELECTION& aSel )
  67. {
  68. if( aSel.GetSize() == 1 )
  69. {
  70. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
  71. if( symbol )
  72. return !symbol->GetLibSymbolRef() || !symbol->GetLibSymbolRef()->IsPower();
  73. }
  74. return false;
  75. };
  76. SELECTION_CONDITION EE_CONDITIONS::SingleSymbolOrPower = []( const SELECTION& aSel )
  77. {
  78. return aSel.GetSize() == 1 && aSel.Front()->Type() == SCH_SYMBOL_T;
  79. };
  80. SELECTION_CONDITION EE_CONDITIONS::SingleDeMorganSymbol = []( const SELECTION& aSel )
  81. {
  82. if( aSel.GetSize() == 1 )
  83. {
  84. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
  85. if( symbol )
  86. return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->HasAlternateBodyStyle();
  87. }
  88. return false;
  89. };
  90. SELECTION_CONDITION EE_CONDITIONS::SingleMultiUnitSymbol = []( const SELECTION& aSel )
  91. {
  92. if( aSel.GetSize() == 1 )
  93. {
  94. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( aSel.Front() );
  95. if( symbol )
  96. return symbol->GetLibSymbolRef() && symbol->GetLibSymbolRef()->GetUnitCount() >= 2;
  97. }
  98. return false;
  99. };
  100. SELECTION_CONDITION EE_CONDITIONS::SingleMultiFunctionPin = []( const SELECTION& aSel )
  101. {
  102. if( aSel.GetSize() == 1 )
  103. {
  104. SCH_PIN* pin = dynamic_cast<SCH_PIN*>( aSel.Front() );
  105. if( pin && pin->GetLibPin() )
  106. return !pin->GetLibPin()->GetAlternates().empty();
  107. }
  108. return false;
  109. };
  110. SELECTION_CONDITION EE_CONDITIONS::SingleNonExcludedMarker = []( const SELECTION& aSel )
  111. {
  112. if( aSel.CountType( SCH_MARKER_T ) != 1 )
  113. return false;
  114. return !static_cast<SCH_MARKER*>( aSel.Front() )->IsExcluded();
  115. };
  116. SELECTION_CONDITION EE_CONDITIONS::MultipleSymbolsOrPower = []( const SELECTION& aSel )
  117. {
  118. return aSel.GetSize() > 1 && aSel.OnlyContains( { SCH_SYMBOL_T } );
  119. };
  120. SELECTION_CONDITION EE_CONDITIONS::AllPins = []( const SELECTION& aSel )
  121. {
  122. return aSel.GetSize() >= 1 && aSel.OnlyContains( { SCH_PIN_T } );
  123. };
  124. SELECTION_CONDITION EE_CONDITIONS::AllPinsOrSheetPins = []( const SELECTION& aSel )
  125. {
  126. return aSel.GetSize() >= 1 && aSel.OnlyContains( { SCH_PIN_T, SCH_SHEET_PIN_T } );
  127. };
  128. #define HITTEST_THRESHOLD_PIXELS 5
  129. EE_SELECTION_TOOL::EE_SELECTION_TOOL() :
  130. SELECTION_TOOL( "eeschema.InteractiveSelection" ),
  131. m_frame( nullptr ),
  132. m_nonModifiedCursor( KICURSOR::ARROW ),
  133. m_isSymbolEditor( false ),
  134. m_isSymbolViewer( false ),
  135. m_unit( 0 ),
  136. m_bodyStyle( 0 )
  137. {
  138. m_filter.SetDefaults();
  139. m_selection.Clear();
  140. }
  141. EE_SELECTION_TOOL::~EE_SELECTION_TOOL()
  142. {
  143. getView()->Remove( &m_selection );
  144. }
  145. using E_C = EE_CONDITIONS;
  146. static std::vector<KICAD_T> connectedTypes =
  147. {
  148. SCH_SYMBOL_LOCATE_POWER_T,
  149. SCH_PIN_T,
  150. SCH_ITEM_LOCATE_WIRE_T,
  151. SCH_ITEM_LOCATE_BUS_T,
  152. SCH_BUS_WIRE_ENTRY_T,
  153. SCH_BUS_BUS_ENTRY_T,
  154. SCH_LABEL_T,
  155. SCH_HIER_LABEL_T,
  156. SCH_GLOBAL_LABEL_T,
  157. SCH_SHEET_PIN_T,
  158. SCH_DIRECTIVE_LABEL_T,
  159. SCH_JUNCTION_T
  160. };
  161. static std::vector<KICAD_T> connectedLineTypes =
  162. {
  163. SCH_ITEM_LOCATE_WIRE_T,
  164. SCH_ITEM_LOCATE_BUS_T
  165. };
  166. static std::vector<KICAD_T> crossProbingTypes =
  167. {
  168. SCH_SYMBOL_T,
  169. SCH_PIN_T,
  170. SCH_SHEET_T
  171. };
  172. static std::vector<KICAD_T> lineTypes = { SCH_LINE_T };
  173. static std::vector<KICAD_T> sheetTypes = { SCH_SHEET_T };
  174. static std::vector<KICAD_T> tableCellTypes = { SCH_TABLECELL_T };
  175. bool EE_SELECTION_TOOL::Init()
  176. {
  177. m_frame = getEditFrame<SCH_BASE_FRAME>();
  178. SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
  179. SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  180. if( symbolEditorFrame )
  181. {
  182. m_isSymbolEditor = true;
  183. m_unit = symbolEditorFrame->GetUnit();
  184. m_bodyStyle = symbolEditorFrame->GetBodyStyle();
  185. }
  186. else
  187. {
  188. m_isSymbolViewer = symbolViewerFrame != nullptr;
  189. }
  190. auto linesSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( lineTypes );
  191. auto wireOrBusSelection = E_C::Count( 1 ) && E_C::OnlyTypes( connectedLineTypes );
  192. auto connectedSelection = E_C::Count( 1 ) && E_C::OnlyTypes( connectedTypes );
  193. auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyTypes( sheetTypes );
  194. auto crossProbingSelection = E_C::MoreThan( 0 ) && E_C::HasTypes( crossProbingTypes );
  195. auto tableCellSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( tableCellTypes );
  196. auto schEditSheetPageNumberCondition =
  197. [&] ( const SELECTION& aSel )
  198. {
  199. if( m_isSymbolEditor || m_isSymbolViewer )
  200. return false;
  201. return E_C::LessThan( 2 )( aSel ) && E_C::OnlyTypes( sheetTypes )( aSel );
  202. };
  203. auto schEditCondition =
  204. [this] ( const SELECTION& aSel )
  205. {
  206. return !m_isSymbolEditor && !m_isSymbolViewer;
  207. };
  208. auto belowRootSheetCondition =
  209. [&]( const SELECTION& aSel )
  210. {
  211. SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  212. return editFrame
  213. && editFrame->GetCurrentSheet().Last() != &editFrame->Schematic().Root();
  214. };
  215. auto haveHighlight =
  216. [&]( const SELECTION& sel )
  217. {
  218. SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  219. return editFrame && !editFrame->GetHighlightedConnection().IsEmpty();
  220. };
  221. auto haveSymbol =
  222. [&]( const SELECTION& sel )
  223. {
  224. return m_isSymbolEditor &&
  225. static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
  226. };
  227. auto symbolDisplayNameIsEditable =
  228. [&]( const SELECTION& sel )
  229. {
  230. if ( !m_isSymbolEditor )
  231. return false;
  232. SYMBOL_EDIT_FRAME* symbEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  233. return symbEditorFrame
  234. && symbEditorFrame->GetCurSymbol()
  235. && symbEditorFrame->GetCurSymbol()->IsMulti()
  236. && symbEditorFrame->IsSymbolEditable()
  237. && !symbEditorFrame->IsSymbolAlias();
  238. };
  239. auto& menu = m_menu.GetMenu();
  240. menu.AddItem( EE_ACTIONS::clearHighlight, haveHighlight && EE_CONDITIONS::Idle, 1 );
  241. menu.AddSeparator( haveHighlight && EE_CONDITIONS::Idle, 1 );
  242. menu.AddItem( ACTIONS::selectColumns, tableCellSelection && EE_CONDITIONS::Idle, 2 );
  243. menu.AddItem( ACTIONS::selectRows, tableCellSelection && EE_CONDITIONS::Idle, 2 );
  244. menu.AddItem( ACTIONS::selectTable, tableCellSelection && EE_CONDITIONS::Idle, 2 );
  245. menu.AddSeparator( 100 );
  246. menu.AddItem( EE_ACTIONS::drawWire, schEditCondition && EE_CONDITIONS::Empty, 100 );
  247. menu.AddItem( EE_ACTIONS::drawBus, schEditCondition && EE_CONDITIONS::Empty, 100 );
  248. menu.AddSeparator( 100 );
  249. menu.AddItem( ACTIONS::finishInteractive,
  250. SCH_LINE_WIRE_BUS_TOOL::IsDrawingLineWireOrBus, 100 );
  251. menu.AddItem( EE_ACTIONS::enterSheet, sheetSelection && EE_CONDITIONS::Idle, 150 );
  252. menu.AddItem( EE_ACTIONS::selectOnPCB,
  253. crossProbingSelection && EE_CONDITIONS::Idle, 150 );
  254. menu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 150 );
  255. menu.AddSeparator( 200 );
  256. menu.AddItem( EE_ACTIONS::placeJunction, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  257. menu.AddItem( EE_ACTIONS::placeLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  258. menu.AddItem( EE_ACTIONS::placeClassLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  259. menu.AddItem( EE_ACTIONS::placeGlobalLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  260. menu.AddItem( EE_ACTIONS::placeHierLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  261. menu.AddItem( EE_ACTIONS::breakWire, linesSelection && EE_CONDITIONS::Idle, 250 );
  262. menu.AddItem( EE_ACTIONS::slice, linesSelection && EE_CONDITIONS::Idle, 250 );
  263. menu.AddItem( EE_ACTIONS::placeSheetPin, sheetSelection && EE_CONDITIONS::Idle, 250 );
  264. menu.AddItem( EE_ACTIONS::syncSheetPins, sheetSelection && EE_CONDITIONS::Idle, 250 );
  265. menu.AddItem( EE_ACTIONS::assignNetclass, connectedSelection && EE_CONDITIONS::Idle, 250 );
  266. menu.AddItem( EE_ACTIONS::editPageNumber, schEditSheetPageNumberCondition, 250 );
  267. menu.AddSeparator( 400 );
  268. menu.AddItem( EE_ACTIONS::symbolProperties, haveSymbol && EE_CONDITIONS::Empty, 400 );
  269. menu.AddItem( EE_ACTIONS::pinTable, haveSymbol && EE_CONDITIONS::Empty, 400 );
  270. menu.AddItem( EE_ACTIONS::setUnitDisplayName,
  271. haveSymbol && symbolDisplayNameIsEditable && EE_CONDITIONS::Empty, 400 );
  272. menu.AddSeparator( 1000 );
  273. m_frame->AddStandardSubMenus( m_menu );
  274. m_disambiguateTimer.SetOwner( this );
  275. Connect( wxEVT_TIMER, wxTimerEventHandler( EE_SELECTION_TOOL::onDisambiguationExpire ),
  276. nullptr, this );
  277. return true;
  278. }
  279. void EE_SELECTION_TOOL::Reset( RESET_REASON aReason )
  280. {
  281. m_frame = getEditFrame<SCH_BASE_FRAME>();
  282. if( aReason != TOOL_BASE::REDRAW )
  283. {
  284. // Remove pointers to the selected items from containers without changing their
  285. // properties (as they are already deleted while a new sheet is loaded)
  286. m_selection.Clear();
  287. }
  288. if( aReason == RESET_REASON::SHUTDOWN )
  289. return;
  290. if( aReason == TOOL_BASE::MODEL_RELOAD || aReason == TOOL_BASE::SUPERMODEL_RELOAD )
  291. {
  292. getView()->GetPainter()->GetSettings()->SetHighlight( false );
  293. SYMBOL_EDIT_FRAME* symbolEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  294. SYMBOL_VIEWER_FRAME* symbolViewerFrame = dynamic_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
  295. if( symbolEditFrame )
  296. {
  297. m_isSymbolEditor = true;
  298. m_unit = symbolEditFrame->GetUnit();
  299. m_bodyStyle = symbolEditFrame->GetBodyStyle();
  300. }
  301. else
  302. {
  303. m_isSymbolViewer = symbolViewerFrame != nullptr;
  304. }
  305. }
  306. // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
  307. getView()->Remove( &m_selection );
  308. getView()->Add( &m_selection );
  309. }
  310. int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
  311. {
  312. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  313. KIID lastRolloverItem = niluuid;
  314. EE_GRID_HELPER grid( m_toolMgr );
  315. auto pinOrientation =
  316. []( EDA_ITEM* aItem )
  317. {
  318. SCH_PIN* pin = dynamic_cast<SCH_PIN*>( aItem );
  319. if( pin )
  320. {
  321. const SCH_SYMBOL* parent = dynamic_cast<const SCH_SYMBOL*>( pin->GetParentSymbol() );
  322. if( !parent )
  323. return pin->GetOrientation();
  324. else
  325. {
  326. SCH_PIN dummy( *pin );
  327. RotateAndMirrorPin( dummy, parent->GetOrientation() );
  328. return dummy.GetOrientation();
  329. }
  330. }
  331. SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( aItem );
  332. if( sheetPin )
  333. {
  334. switch( sheetPin->GetSide() )
  335. {
  336. default:
  337. case SHEET_SIDE::LEFT: return PIN_ORIENTATION::PIN_RIGHT;
  338. case SHEET_SIDE::RIGHT: return PIN_ORIENTATION::PIN_LEFT;
  339. case SHEET_SIDE::TOP: return PIN_ORIENTATION::PIN_DOWN;
  340. case SHEET_SIDE::BOTTOM: return PIN_ORIENTATION::PIN_UP;
  341. }
  342. }
  343. return PIN_ORIENTATION::PIN_LEFT;
  344. };
  345. // Main loop: keep receiving events
  346. while( TOOL_EVENT* evt = Wait() )
  347. {
  348. bool selCancelled = false;
  349. bool displayWireCursor = false;
  350. bool displayBusCursor = false;
  351. bool displayLineCursor = false;
  352. KIID rolloverItem = lastRolloverItem;
  353. // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
  354. setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
  355. evt->Modifier( MD_ALT ) );
  356. MOUSE_DRAG_ACTION drag_action = m_frame->GetDragAction();
  357. if( evt->IsMouseDown( BUT_LEFT ) )
  358. {
  359. if( !m_frame->ToolStackIsEmpty() )
  360. {
  361. // Avoid triggering when running under other tools
  362. }
  363. else if( m_toolMgr->GetTool<EE_POINT_EDITOR>()
  364. && m_toolMgr->GetTool<EE_POINT_EDITOR>()->HasPoint() )
  365. {
  366. // Distinguish point editor from selection modification by checking modifiers
  367. if( hasModifier() )
  368. {
  369. m_originalCursor = m_toolMgr->GetMousePosition();
  370. m_disambiguateTimer.StartOnce( ADVANCED_CFG::GetCfg().m_DisambiguationMenuDelay );
  371. }
  372. }
  373. else
  374. {
  375. m_originalCursor = m_toolMgr->GetMousePosition();
  376. m_disambiguateTimer.StartOnce( ADVANCED_CFG::GetCfg().m_DisambiguationMenuDelay );
  377. }
  378. }
  379. // Single click? Select single object
  380. else if( evt->IsClick( BUT_LEFT ) )
  381. {
  382. // If the timer has stopped, then we have already run the disambiguate routine
  383. // and we don't want to register an extra click here
  384. if( !m_disambiguateTimer.IsRunning() )
  385. {
  386. evt->SetPassEvent();
  387. continue;
  388. }
  389. m_disambiguateTimer.Stop();
  390. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  391. schframe->FocusOnItem( nullptr );
  392. // Collect items at the clicked location (doesn't select them yet)
  393. EE_COLLECTOR collector;
  394. CollectHits( collector, evt->Position() );
  395. narrowSelection( collector, evt->Position(), false );
  396. if( collector.GetCount() == 1 && !m_isSymbolEditor && !hasModifier() )
  397. {
  398. OPT_TOOL_EVENT autostart = autostartEvent( evt, grid, collector[0] );
  399. if( autostart )
  400. {
  401. DRAW_SEGMENT_EVENT_PARAMS* params = new DRAW_SEGMENT_EVENT_PARAMS();
  402. params->layer = autostart->Parameter<const DRAW_SEGMENT_EVENT_PARAMS*>()->layer;
  403. params->quitOnDraw = true;
  404. params->sourceSegment = dynamic_cast<SCH_LINE*>( collector[0] );
  405. autostart->SetParameter<const DRAW_SEGMENT_EVENT_PARAMS*>( params );
  406. m_toolMgr->ProcessEvent( *autostart );
  407. selCancelled = true;
  408. }
  409. else if( collector[0]->IsHypertext() )
  410. {
  411. collector[ 0 ]->DoHypertextAction( m_frame );
  412. selCancelled = true;
  413. }
  414. else if( collector[0]->IsBrightened() )
  415. {
  416. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  417. {
  418. NET_NAVIGATOR_ITEM_DATA itemData( schframe->GetCurrentSheet(),
  419. collector[0] );
  420. schframe->SelectNetNavigatorItem( &itemData );
  421. }
  422. }
  423. }
  424. if( !selCancelled )
  425. {
  426. selectPoint( collector, evt->Position(), nullptr, nullptr, m_additive,
  427. m_subtractive, m_exclusive_or );
  428. m_selection.SetIsHover( false );
  429. }
  430. }
  431. else if( evt->IsClick( BUT_RIGHT ) )
  432. {
  433. m_disambiguateTimer.Stop();
  434. // right click? if there is any object - show the context menu
  435. if( m_selection.Empty() )
  436. {
  437. ClearSelection();
  438. SelectPoint( evt->Position(), { SCH_LOCATE_ANY_T }, nullptr, &selCancelled );
  439. m_selection.SetIsHover( true );
  440. }
  441. // If the cursor has moved off the bounding box of the selection by more than
  442. // a grid square, check to see if there is another item available for selection
  443. // under the cursor. If there is, the user likely meant to get the context menu
  444. // for that item. If there is no new item, then keep the original selection and
  445. // show the context menu for it.
  446. else if( !m_selection.GetBoundingBox().Inflate( grid.GetGrid().x, grid.GetGrid().y )
  447. .Contains( evt->Position() ) )
  448. {
  449. EE_COLLECTOR collector;
  450. if( CollectHits( collector, evt->Position(), { SCH_LOCATE_ANY_T } ) )
  451. {
  452. ClearSelection();
  453. SelectPoint( evt->Position(), { SCH_LOCATE_ANY_T }, nullptr, &selCancelled );
  454. m_selection.SetIsHover( true );
  455. }
  456. }
  457. if( !selCancelled )
  458. m_menu.ShowContextMenu( m_selection );
  459. }
  460. else if( evt->IsDblClick( BUT_LEFT ) )
  461. {
  462. m_disambiguateTimer.Stop();
  463. // double click? Display the properties window
  464. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  465. schframe->FocusOnItem( nullptr );
  466. if( m_selection.Empty() )
  467. SelectPoint( evt->Position() );
  468. EDA_ITEM* item = m_selection.Front();
  469. if( item && item->Type() == SCH_SHEET_T )
  470. m_toolMgr->PostAction( EE_ACTIONS::enterSheet );
  471. else
  472. m_toolMgr->PostAction( EE_ACTIONS::properties );
  473. }
  474. else if( evt->IsDblClick( BUT_MIDDLE ) )
  475. {
  476. m_disambiguateTimer.Stop();
  477. // Middle double click? Do zoom to fit or zoom to objects
  478. if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
  479. m_toolMgr->RunAction( ACTIONS::zoomFitObjects );
  480. else
  481. m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
  482. }
  483. else if( evt->IsDrag( BUT_LEFT ) )
  484. {
  485. m_disambiguateTimer.Stop();
  486. // Is another tool already moving a new object? Don't allow a drag start
  487. if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
  488. {
  489. evt->SetPassEvent();
  490. continue;
  491. }
  492. // drag with LMB? Select multiple objects (or at least draw a selection box) or
  493. // drag them
  494. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  495. schframe->FocusOnItem( nullptr );
  496. EE_COLLECTOR collector;
  497. if( CollectHits( collector, evt->DragOrigin(), { SCH_TABLECELL_T } ) )
  498. {
  499. selectTableCells( static_cast<SCH_TABLE*>( collector[0]->GetParent() ) );
  500. }
  501. else if( hasModifier() || drag_action == MOUSE_DRAG_ACTION::SELECT )
  502. {
  503. selectMultiple();
  504. }
  505. else if( m_selection.Empty() && drag_action != MOUSE_DRAG_ACTION::DRAG_ANY )
  506. {
  507. selectMultiple();
  508. }
  509. else
  510. {
  511. if( m_isSymbolEditor )
  512. {
  513. if( static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->IsSymbolAlias() )
  514. {
  515. m_selection = RequestSelection( { SCH_FIELD_T } );
  516. }
  517. else
  518. {
  519. m_selection = RequestSelection( { SCH_SHAPE_T,
  520. SCH_TEXT_T,
  521. SCH_TEXTBOX_T,
  522. SCH_PIN_T,
  523. SCH_FIELD_T } );
  524. }
  525. }
  526. else
  527. {
  528. m_selection = RequestSelection( EE_COLLECTOR::MovableItems );
  529. }
  530. // Check if dragging has started within any of selected items bounding box
  531. if( selectionContains( evt->DragOrigin() ) )
  532. {
  533. // drag_is_move option exists only in schematic editor, not in symbol editor
  534. // (m_frame->eeconfig() returns nullptr in Symbol Editor)
  535. if( m_isSymbolEditor || m_frame->eeconfig()->m_Input.drag_is_move )
  536. m_toolMgr->RunAction( EE_ACTIONS::move );
  537. else
  538. m_toolMgr->RunAction( EE_ACTIONS::drag );
  539. }
  540. else
  541. {
  542. // No -> drag a selection box
  543. selectMultiple();
  544. }
  545. }
  546. }
  547. else if( evt->IsMouseDown( BUT_AUX1 ) )
  548. {
  549. m_toolMgr->RunAction( EE_ACTIONS::navigateBack );
  550. }
  551. else if( evt->IsMouseDown( BUT_AUX2 ) )
  552. {
  553. m_toolMgr->RunAction( EE_ACTIONS::navigateForward );
  554. }
  555. else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
  556. {
  557. m_disambiguateTimer.Stop();
  558. // context sub-menu selection? Handle unit selection or bus unfolding
  559. if( *evt->GetCommandId() >= ID_POPUP_SCH_SELECT_UNIT
  560. && *evt->GetCommandId() <= ID_POPUP_SCH_SELECT_UNIT_END )
  561. {
  562. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( m_selection.Front() );
  563. int unit = *evt->GetCommandId() - ID_POPUP_SCH_SELECT_UNIT;
  564. if( symbol )
  565. static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( symbol, unit );
  566. }
  567. else if( *evt->GetCommandId() >= ID_POPUP_SCH_ALT_PIN_FUNCTION
  568. && *evt->GetCommandId() <= ID_POPUP_SCH_ALT_PIN_FUNCTION_END )
  569. {
  570. SCH_PIN* pin = dynamic_cast<SCH_PIN*>( m_selection.Front() );
  571. wxString alt = *evt->Parameter<wxString*>();
  572. if( pin )
  573. static_cast<SCH_EDIT_FRAME*>( m_frame )->SetAltPinFunction( pin, alt );
  574. }
  575. else if( *evt->GetCommandId() >= ID_POPUP_SCH_PIN_TRICKS_START
  576. && *evt->GetCommandId() <= ID_POPUP_SCH_PIN_TRICKS_END )
  577. {
  578. if( !m_selection.OnlyContains( { SCH_PIN_T, SCH_SHEET_PIN_T } )
  579. || m_selection.Empty() )
  580. {
  581. return 0;
  582. }
  583. // Keep track of new items so we make them the new selection at the end
  584. EDA_ITEMS newItems;
  585. SCH_COMMIT commit( static_cast<SCH_EDIT_FRAME*>( m_frame ) );
  586. if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_NO_CONNECT )
  587. {
  588. for( EDA_ITEM* item : m_selection )
  589. {
  590. SCH_NO_CONNECT* nc = new SCH_NO_CONNECT( item->GetPosition() );
  591. commit.Add( nc, m_frame->GetScreen() );
  592. newItems.push_back( nc );
  593. }
  594. commit.Push( wxS( "No Connect Pins" ) );
  595. ClearSelection();
  596. }
  597. else if( *evt->GetCommandId() == ID_POPUP_SCH_PIN_TRICKS_WIRE )
  598. {
  599. VECTOR2I wireGrid = grid.GetGridSize( GRID_HELPER_GRIDS::GRID_WIRES );
  600. for( EDA_ITEM* item : m_selection )
  601. {
  602. SCH_LINE* wire = new SCH_LINE( item->GetPosition(), LAYER_WIRE );
  603. // Add some length to the wire as nothing in our code base handles
  604. // 0 length wires very well, least of all the ortho drag algorithm
  605. VECTOR2I stub;
  606. switch( pinOrientation( item ) )
  607. {
  608. default:
  609. case PIN_ORIENTATION::PIN_RIGHT:
  610. stub = VECTOR2I( -1 * wireGrid.x, 0 );
  611. break;
  612. case PIN_ORIENTATION::PIN_LEFT:
  613. stub = VECTOR2I( 1 * wireGrid.x, 0 );
  614. break;
  615. case PIN_ORIENTATION::PIN_UP:
  616. stub = VECTOR2I( 0, 1 * wireGrid.y );
  617. break;
  618. case PIN_ORIENTATION::PIN_DOWN:
  619. stub = VECTOR2I( 0, -1 * wireGrid.y );
  620. break;
  621. }
  622. wire->SetEndPoint( item->GetPosition() + stub );
  623. m_frame->AddToScreen( wire, m_frame->GetScreen() );
  624. commit.Added( wire, m_frame->GetScreen() );
  625. newItems.push_back( wire );
  626. }
  627. ClearSelection();
  628. AddItemsToSel( &newItems );
  629. // Select only the ends so we can immediately start dragging them
  630. for( EDA_ITEM* item : newItems )
  631. static_cast<SCH_LINE*>( item )->SetFlags( ENDPOINT );
  632. // Put the mouse on the nearest point of the first wire
  633. SCH_LINE* first = static_cast<SCH_LINE*>( newItems[0] );
  634. getViewControls()->SetCrossHairCursorPosition( first->GetEndPoint(), false );
  635. getViewControls()->WarpMouseCursor( getViewControls()->GetCursorPosition(),
  636. true );
  637. // Start the drag tool, canceling will remove the wires
  638. if( m_toolMgr->RunSynchronousAction( EE_ACTIONS::drag, &commit, false ) )
  639. commit.Push( wxS( "Wire Pins" ) );
  640. else
  641. commit.Revert();
  642. }
  643. else
  644. {
  645. // For every pin in the selection, add a label according to menu item
  646. // selected by the user
  647. for( EDA_ITEM* item : m_selection )
  648. {
  649. SCH_PIN* pin = dynamic_cast<SCH_PIN*>( item );
  650. SCH_SHEET_PIN* sheetPin = dynamic_cast<SCH_SHEET_PIN*>( item );
  651. SCH_EDIT_FRAME* sf = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  652. SCH_LABEL_BASE* label = nullptr;
  653. wxString labelText;
  654. if( pin )
  655. labelText = pin->GetShownName();
  656. else if( sheetPin && sf )
  657. labelText = sheetPin->GetShownText( &sf->GetCurrentSheet(), false );
  658. switch( *evt->GetCommandId() )
  659. {
  660. case ID_POPUP_SCH_PIN_TRICKS_NET_LABEL:
  661. label = new SCH_LABEL( item->GetPosition(), labelText );
  662. break;
  663. case ID_POPUP_SCH_PIN_TRICKS_HIER_LABEL:
  664. label = new SCH_HIERLABEL( item->GetPosition(), labelText );
  665. break;
  666. case ID_POPUP_SCH_PIN_TRICKS_GLOBAL_LABEL:
  667. label = new SCH_GLOBALLABEL( item->GetPosition(), labelText );
  668. break;
  669. default: continue;
  670. }
  671. switch( pinOrientation( item ) )
  672. {
  673. default:
  674. case PIN_ORIENTATION::PIN_RIGHT:
  675. label->SetSpinStyle( SPIN_STYLE::SPIN::LEFT );
  676. break;
  677. case PIN_ORIENTATION::PIN_LEFT:
  678. label->SetSpinStyle( SPIN_STYLE::SPIN::RIGHT );
  679. break;
  680. case PIN_ORIENTATION::PIN_UP:
  681. label->SetSpinStyle( SPIN_STYLE::SPIN::BOTTOM );
  682. break;
  683. case PIN_ORIENTATION::PIN_DOWN:
  684. label->SetSpinStyle( SPIN_STYLE::SPIN::UP );
  685. break;
  686. }
  687. ELECTRICAL_PINTYPE pinType = ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
  688. if( pin )
  689. {
  690. pinType = pin->GetType();
  691. }
  692. else if( sheetPin )
  693. {
  694. switch( sheetPin->GetLabelShape() )
  695. {
  696. case LABEL_INPUT: pinType = ELECTRICAL_PINTYPE::PT_INPUT; break;
  697. case LABEL_OUTPUT: pinType = ELECTRICAL_PINTYPE::PT_OUTPUT; break;
  698. case LABEL_BIDI: pinType = ELECTRICAL_PINTYPE::PT_BIDI; break;
  699. case LABEL_TRISTATE: pinType = ELECTRICAL_PINTYPE::PT_TRISTATE; break;
  700. case LABEL_PASSIVE: pinType = ELECTRICAL_PINTYPE::PT_PASSIVE; break;
  701. }
  702. }
  703. switch( pinType )
  704. {
  705. case ELECTRICAL_PINTYPE::PT_BIDI:
  706. label->SetShape( LABEL_FLAG_SHAPE::L_BIDI );
  707. break;
  708. case ELECTRICAL_PINTYPE::PT_INPUT:
  709. label->SetShape( LABEL_FLAG_SHAPE::L_INPUT );
  710. break;
  711. case ELECTRICAL_PINTYPE::PT_OUTPUT:
  712. label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT );
  713. break;
  714. case ELECTRICAL_PINTYPE::PT_TRISTATE:
  715. label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE );
  716. break;
  717. case ELECTRICAL_PINTYPE::PT_UNSPECIFIED:
  718. label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED );
  719. break;
  720. default:
  721. label->SetShape( LABEL_FLAG_SHAPE::L_INPUT );
  722. }
  723. commit.Add( label, m_frame->GetScreen() );
  724. newItems.push_back( label );
  725. }
  726. commit.Push( wxS( "Label Pins" ) );
  727. // Many users will want to drag these items to wire off of the pins, so
  728. // pre-select them.
  729. ClearSelection();
  730. AddItemsToSel( &newItems );
  731. }
  732. }
  733. else if( *evt->GetCommandId() >= ID_POPUP_SCH_UNFOLD_BUS
  734. && *evt->GetCommandId() <= ID_POPUP_SCH_UNFOLD_BUS_END )
  735. {
  736. wxString* net = new wxString( *evt->Parameter<wxString*>() );
  737. m_toolMgr->RunAction<wxString*>( EE_ACTIONS::unfoldBus, net );
  738. }
  739. }
  740. else if( evt->IsCancelInteractive() )
  741. {
  742. m_disambiguateTimer.Stop();
  743. // We didn't set these, but we have reports that they leak out of some other tools,
  744. // so we clear them here.
  745. getViewControls()->SetAutoPan( false );
  746. getViewControls()->CaptureCursor( false );
  747. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  748. schframe->FocusOnItem( nullptr );
  749. if( !GetSelection().Empty() )
  750. {
  751. ClearSelection();
  752. }
  753. else if( evt->FirstResponder() == this && evt->GetCommandId() == (int) WXK_ESCAPE )
  754. {
  755. SCH_EDITOR_CONTROL* editor = m_toolMgr->GetTool<SCH_EDITOR_CONTROL>();
  756. if( editor && m_frame->eeconfig()->m_Input.esc_clears_net_highlight )
  757. editor->ClearHighlight( *evt );
  758. }
  759. }
  760. else if( evt->Action() == TA_UNDO_REDO_PRE )
  761. {
  762. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  763. schframe->FocusOnItem( nullptr );
  764. }
  765. else if( evt->IsMotion() && !m_isSymbolEditor && evt->FirstResponder() == this )
  766. {
  767. // Update cursor and rollover item
  768. rolloverItem = niluuid;
  769. EE_COLLECTOR collector;
  770. getViewControls()->ForceCursorPosition( false );
  771. if( CollectHits( collector, evt->Position() ) )
  772. {
  773. narrowSelection( collector, evt->Position(), false );
  774. if( collector.GetCount() == 1 && !hasModifier() )
  775. {
  776. OPT_TOOL_EVENT autostartEvt = autostartEvent( evt, grid, collector[0] );
  777. if( autostartEvt )
  778. {
  779. if( autostartEvt->Matches( EE_ACTIONS::drawBus.MakeEvent() ) )
  780. displayBusCursor = true;
  781. else if( autostartEvt->Matches( EE_ACTIONS::drawWire.MakeEvent() ) )
  782. displayWireCursor = true;
  783. else if( autostartEvt->Matches( EE_ACTIONS::drawLines.MakeEvent() ) )
  784. displayLineCursor = true;
  785. }
  786. else if( collector[0]->IsHypertext() && !collector[0]->IsSelected() )
  787. {
  788. rolloverItem = collector[0]->m_Uuid;
  789. }
  790. }
  791. }
  792. }
  793. else
  794. {
  795. evt->SetPassEvent();
  796. }
  797. if( rolloverItem != lastRolloverItem )
  798. {
  799. if( EDA_ITEM* item = m_frame->GetItem( lastRolloverItem ) )
  800. {
  801. item->ClearFlags( IS_ROLLOVER );
  802. lastRolloverItem = niluuid;
  803. if( item->Type() == SCH_FIELD_T )
  804. m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
  805. else
  806. m_frame->GetCanvas()->GetView()->Update( item );
  807. }
  808. }
  809. if( rolloverItem != niluuid )
  810. {
  811. EDA_ITEM* item = m_frame->GetItem( rolloverItem );
  812. if( item && !( item->GetFlags() & IS_ROLLOVER ) )
  813. {
  814. item->SetFlags( IS_ROLLOVER );
  815. lastRolloverItem = rolloverItem;
  816. if( item->Type() == SCH_FIELD_T )
  817. m_frame->GetCanvas()->GetView()->Update( item->GetParent() );
  818. else
  819. m_frame->GetCanvas()->GetView()->Update( item );
  820. }
  821. }
  822. if( m_frame->ToolStackIsEmpty() )
  823. {
  824. if( displayWireCursor )
  825. {
  826. m_nonModifiedCursor = KICURSOR::LINE_WIRE_ADD;
  827. }
  828. else if( displayBusCursor )
  829. {
  830. m_nonModifiedCursor = KICURSOR::LINE_BUS;
  831. }
  832. else if( displayLineCursor )
  833. {
  834. m_nonModifiedCursor = KICURSOR::LINE_GRAPHIC;
  835. }
  836. else if( rolloverItem != niluuid )
  837. {
  838. m_nonModifiedCursor = KICURSOR::HAND;
  839. }
  840. else if( !m_selection.Empty()
  841. && drag_action == MOUSE_DRAG_ACTION::DRAG_SELECTED
  842. && evt->HasPosition()
  843. && selectionContains( evt->Position() ) ) //move/drag option prediction
  844. {
  845. m_nonModifiedCursor = KICURSOR::MOVING;
  846. }
  847. else
  848. {
  849. m_nonModifiedCursor = KICURSOR::ARROW;
  850. }
  851. }
  852. }
  853. m_disambiguateTimer.Stop();
  854. // Shutting down; clear the selection
  855. m_selection.Clear();
  856. return 0;
  857. }
  858. OPT_TOOL_EVENT EE_SELECTION_TOOL::autostartEvent( TOOL_EVENT* aEvent, EE_GRID_HELPER& aGrid,
  859. SCH_ITEM* aItem )
  860. {
  861. VECTOR2I pos = aGrid.BestSnapAnchor( aEvent->Position(), aGrid.GetItemGrid( aItem ) );
  862. if( m_frame->eeconfig()->m_Drawing.auto_start_wires
  863. && !m_toolMgr->GetTool<EE_POINT_EDITOR>()->HasPoint()
  864. && aItem->IsPointClickableAnchor( pos ) )
  865. {
  866. OPT_TOOL_EVENT newEvt = EE_ACTIONS::drawWire.MakeEvent();
  867. if( aItem->Type() == SCH_BUS_BUS_ENTRY_T )
  868. {
  869. newEvt = EE_ACTIONS::drawBus.MakeEvent();
  870. }
  871. else if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
  872. {
  873. SCH_BUS_WIRE_ENTRY* busEntry = static_cast<SCH_BUS_WIRE_ENTRY*>( aItem );
  874. if( !busEntry->m_connected_bus_item )
  875. newEvt = EE_ACTIONS::drawBus.MakeEvent();
  876. }
  877. else if( aItem->Type() == SCH_LINE_T )
  878. {
  879. SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
  880. if( line->IsBus() )
  881. newEvt = EE_ACTIONS::drawBus.MakeEvent();
  882. else if( line->IsGraphicLine() )
  883. newEvt = EE_ACTIONS::drawLines.MakeEvent();
  884. }
  885. else if( aItem->Type() == SCH_LABEL_T || aItem->Type() == SCH_HIER_LABEL_T
  886. || aItem->Type() == SCH_SHEET_PIN_T )
  887. {
  888. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
  889. SCH_CONNECTION possibleConnection( label->Schematic()->ConnectionGraph() );
  890. possibleConnection.ConfigureFromLabel( label->GetShownText( false ) );
  891. if( possibleConnection.IsBus() )
  892. newEvt = EE_ACTIONS::drawBus.MakeEvent();
  893. }
  894. else if( aItem->Type() == SCH_SYMBOL_T )
  895. {
  896. const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( aItem );
  897. const SCH_PIN* pin = symbol->GetPin( pos );
  898. if( !pin )
  899. return OPT_TOOL_EVENT();
  900. if( !pin->IsVisible()
  901. && !( m_frame->eeconfig()->m_Appearance.show_hidden_pins
  902. || m_frame->GetRenderSettings()->m_ShowHiddenPins ) )
  903. {
  904. return OPT_TOOL_EVENT();
  905. }
  906. }
  907. newEvt->SetMousePosition( pos );
  908. newEvt->SetHasPosition( true );
  909. newEvt->SetForceImmediate( true );
  910. getViewControls()->ForceCursorPosition( true, pos );
  911. return newEvt;
  912. }
  913. return OPT_TOOL_EVENT();
  914. }
  915. int EE_SELECTION_TOOL::disambiguateCursor( const TOOL_EVENT& aEvent )
  916. {
  917. wxMouseState keyboardState = wxGetMouseState();
  918. setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
  919. keyboardState.AltDown() );
  920. m_skip_heuristics = true;
  921. SelectPoint( m_originalCursor, { SCH_LOCATE_ANY_T }, nullptr, &m_canceledMenu, false,
  922. m_additive, m_subtractive, m_exclusive_or );
  923. m_skip_heuristics = false;
  924. return 0;
  925. }
  926. void EE_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
  927. {
  928. if( m_frame->ToolStackIsEmpty() && !m_multiple )
  929. {
  930. wxMouseState keyboardState = wxGetMouseState();
  931. setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
  932. keyboardState.AltDown() );
  933. if( m_additive )
  934. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
  935. else if( m_subtractive )
  936. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
  937. else if( m_exclusive_or )
  938. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
  939. else
  940. m_frame->GetCanvas()->SetCurrentCursor( m_nonModifiedCursor );
  941. }
  942. }
  943. EE_SELECTION& EE_SELECTION_TOOL::GetSelection()
  944. {
  945. return m_selection;
  946. }
  947. bool EE_SELECTION_TOOL::CollectHits( EE_COLLECTOR& aCollector, const VECTOR2I& aWhere,
  948. const std::vector<KICAD_T>& aScanTypes )
  949. {
  950. int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  951. int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() / 2 );
  952. aCollector.m_Threshold = std::max( pixelThreshold, gridThreshold );
  953. aCollector.m_ShowPinElectricalTypes = m_frame->GetRenderSettings()->m_ShowPinsElectricalType;
  954. if( m_isSymbolEditor )
  955. {
  956. LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
  957. if( !symbol )
  958. return false;
  959. aCollector.Collect( symbol->GetDrawItems(), aScanTypes, aWhere, m_unit, m_bodyStyle );
  960. }
  961. else
  962. {
  963. aCollector.Collect( m_frame->GetScreen(), aScanTypes, aWhere, m_unit, m_bodyStyle );
  964. // If pins are disabled in the filter, they will be removed later. Let's add the parent
  965. // so that people can use pins to select symbols in this case.
  966. if( !m_filter.pins )
  967. {
  968. int originalCount = aCollector.GetCount();
  969. for( int ii = 0; ii < originalCount; ++ii )
  970. {
  971. if( aCollector[ii]->Type() == SCH_PIN_T )
  972. {
  973. SCH_PIN* pin = static_cast<SCH_PIN*>( aCollector[ii] );
  974. if( !aCollector.HasItem( pin->GetParentSymbol() ) )
  975. aCollector.Append( pin->GetParentSymbol() );
  976. }
  977. }
  978. }
  979. }
  980. return aCollector.GetCount() > 0;
  981. }
  982. void EE_SELECTION_TOOL::narrowSelection( EE_COLLECTOR& collector, const VECTOR2I& aWhere,
  983. bool aCheckLocked, bool aSelectedOnly )
  984. {
  985. SYMBOL_EDIT_FRAME* symbolEditorFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  986. for( int i = collector.GetCount() - 1; i >= 0; --i )
  987. {
  988. if( symbolEditorFrame )
  989. {
  990. // Do not select invisible items if they are not displayed
  991. EDA_ITEM* item = collector[i];
  992. if( item->Type() == SCH_FIELD_T )
  993. {
  994. if( !static_cast<SCH_FIELD*>( item )->IsVisible()
  995. && !symbolEditorFrame->GetShowInvisibleFields() )
  996. {
  997. collector.Remove( i );
  998. continue;
  999. }
  1000. }
  1001. else if( item->Type() == SCH_PIN_T )
  1002. {
  1003. if( !static_cast<SCH_PIN*>( item )->IsVisible()
  1004. && !symbolEditorFrame->GetShowInvisiblePins() )
  1005. {
  1006. collector.Remove( i );
  1007. continue;
  1008. }
  1009. }
  1010. }
  1011. if( !Selectable( collector[i], &aWhere ) )
  1012. {
  1013. collector.Remove( i );
  1014. continue;
  1015. }
  1016. if( aCheckLocked && collector[i]->IsLocked() )
  1017. {
  1018. collector.Remove( i );
  1019. continue;
  1020. }
  1021. if( !itemPassesFilter( collector[i] ) )
  1022. {
  1023. collector.Remove( i );
  1024. continue;
  1025. }
  1026. if( aSelectedOnly && !collector[i]->IsSelected() )
  1027. {
  1028. collector.Remove( i );
  1029. continue;
  1030. }
  1031. }
  1032. // Apply some ugly heuristics to avoid disambiguation menus whenever possible
  1033. if( collector.GetCount() > 1 && !m_skip_heuristics )
  1034. GuessSelectionCandidates( collector, aWhere );
  1035. }
  1036. bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, const VECTOR2I& aWhere,
  1037. EDA_ITEM** aItem, bool* aSelectionCancelledFlag, bool aAdd,
  1038. bool aSubtract, bool aExclusiveOr )
  1039. {
  1040. m_selection.ClearReferencePoint();
  1041. // If still more than one item we're going to have to ask the user.
  1042. if( aCollector.GetCount() > 1 )
  1043. {
  1044. // Try to call selectionMenu via RunAction() to avoid event-loop contention
  1045. // But it we cannot handle the event, then we don't have an active tool loop, so
  1046. // handle it directly.
  1047. if( !m_toolMgr->RunAction<COLLECTOR*>( EE_ACTIONS::selectionMenu, &aCollector ) )
  1048. {
  1049. if( !doSelectionMenu( &aCollector ) )
  1050. aCollector.m_MenuCancelled = true;
  1051. }
  1052. if( aCollector.m_MenuCancelled )
  1053. {
  1054. if( aSelectionCancelledFlag )
  1055. *aSelectionCancelledFlag = true;
  1056. return false;
  1057. }
  1058. }
  1059. if( !aAdd && !aSubtract && !aExclusiveOr )
  1060. ClearSelection();
  1061. int addedCount = 0;
  1062. bool anySubtracted = false;
  1063. if( aCollector.GetCount() > 0 )
  1064. {
  1065. for( int i = 0; i < aCollector.GetCount(); ++i )
  1066. {
  1067. EDA_ITEM_FLAGS flags = 0;
  1068. bool isLine = aCollector[i]->Type() == SCH_LINE_T;
  1069. // Handle line ends specially
  1070. if( isLine )
  1071. {
  1072. SCH_LINE* line = (SCH_LINE*) aCollector[i];
  1073. if( line->GetStartPoint().Distance( aWhere ) <= aCollector.m_Threshold )
  1074. flags = STARTPOINT;
  1075. else if( line->GetEndPoint().Distance( aWhere ) <= aCollector.m_Threshold )
  1076. flags = ENDPOINT;
  1077. else
  1078. flags = STARTPOINT | ENDPOINT;
  1079. }
  1080. if( aSubtract
  1081. || ( aExclusiveOr && aCollector[i]->IsSelected()
  1082. && ( !isLine || ( isLine && aCollector[i]->HasFlag( flags ) ) ) ) )
  1083. {
  1084. aCollector[i]->ClearFlags( flags );
  1085. // Need to update end shadows after ctrl-click unselecting one of two selected
  1086. // endpoints.
  1087. if( isLine )
  1088. getView()->Update( aCollector[i] );
  1089. if( !aCollector[i]->HasFlag( STARTPOINT ) && !aCollector[i]->HasFlag( ENDPOINT ) )
  1090. {
  1091. unselect( aCollector[i] );
  1092. anySubtracted = true;
  1093. }
  1094. }
  1095. else
  1096. {
  1097. aCollector[i]->SetFlags( flags );
  1098. select( aCollector[i] );
  1099. addedCount++;
  1100. }
  1101. }
  1102. }
  1103. if( addedCount == 1 )
  1104. {
  1105. m_toolMgr->ProcessEvent( EVENTS::PointSelectedEvent );
  1106. if( aItem && aCollector.GetCount() == 1 )
  1107. *aItem = aCollector[0];
  1108. return true;
  1109. }
  1110. else if( addedCount > 1 )
  1111. {
  1112. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1113. return true;
  1114. }
  1115. else if( anySubtracted )
  1116. {
  1117. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  1118. return true;
  1119. }
  1120. return false;
  1121. }
  1122. bool EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere,
  1123. const std::vector<KICAD_T>& aScanTypes,
  1124. EDA_ITEM** aItem, bool* aSelectionCancelledFlag,
  1125. bool aCheckLocked, bool aAdd, bool aSubtract,
  1126. bool aExclusiveOr )
  1127. {
  1128. EE_COLLECTOR collector;
  1129. if( !CollectHits( collector, aWhere, aScanTypes ) )
  1130. return false;
  1131. narrowSelection( collector, aWhere, aCheckLocked, aSubtract );
  1132. return selectPoint( collector, aWhere, aItem, aSelectionCancelledFlag, aAdd, aSubtract,
  1133. aExclusiveOr );
  1134. }
  1135. int EE_SELECTION_TOOL::SelectAll( const TOOL_EVENT& aEvent )
  1136. {
  1137. m_multiple = true; // Multiple selection mode is active
  1138. KIGFX::VIEW* view = getView();
  1139. // hold all visible items
  1140. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
  1141. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> sheetPins;
  1142. // Filter the view items based on the selection box
  1143. BOX2I selectionBox;
  1144. selectionBox.SetMaximum();
  1145. view->Query( selectionBox, selectedItems ); // Get the list of selected items
  1146. // Sheet pins aren't in the view; add them by hand
  1147. for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
  1148. {
  1149. SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
  1150. if( sheet )
  1151. {
  1152. int layer = pair.second;
  1153. for( SCH_SHEET_PIN* pin : sheet->GetPins() )
  1154. sheetPins.emplace_back( KIGFX::VIEW::LAYER_ITEM_PAIR( pin, layer ) );
  1155. }
  1156. }
  1157. selectedItems.insert( selectedItems.end(), sheetPins.begin(), sheetPins.end() );
  1158. for( const std::pair<KIGFX::VIEW_ITEM*, int>& item_pair : selectedItems )
  1159. {
  1160. if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( item_pair.first ) )
  1161. {
  1162. if( Selectable( item ) && itemPassesFilter( item ) )
  1163. {
  1164. if( item->Type() == SCH_LINE_T )
  1165. item->SetFlags( STARTPOINT | ENDPOINT );
  1166. select( item );
  1167. }
  1168. }
  1169. }
  1170. m_multiple = false;
  1171. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1172. return 0;
  1173. }
  1174. int EE_SELECTION_TOOL::UnselectAll( const TOOL_EVENT& aEvent )
  1175. {
  1176. m_multiple = true; // Multiple selection mode is active
  1177. KIGFX::VIEW* view = getView();
  1178. // hold all visible items
  1179. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
  1180. // Filter the view items based on the selection box
  1181. BOX2I selectionBox;
  1182. selectionBox.SetMaximum();
  1183. view->Query( selectionBox, selectedItems ); // Get the list of selected items
  1184. for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : selectedItems )
  1185. {
  1186. SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( pair.first );
  1187. if( sheet )
  1188. {
  1189. for( SCH_SHEET_PIN* pin : sheet->GetPins() )
  1190. {
  1191. EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pin );
  1192. if( item && Selectable( item ) )
  1193. unselect( item );
  1194. }
  1195. }
  1196. if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first ) )
  1197. {
  1198. if( Selectable( item ) )
  1199. unselect( item );
  1200. }
  1201. }
  1202. m_multiple = false;
  1203. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  1204. return 0;
  1205. }
  1206. void EE_SELECTION_TOOL::GuessSelectionCandidates( EE_COLLECTOR& collector, const VECTOR2I& aPos )
  1207. {
  1208. // Prefer exact hits to sloppy ones
  1209. std::set<EDA_ITEM*> exactHits;
  1210. for( int i = collector.GetCount() - 1; i >= 0; --i )
  1211. {
  1212. EDA_ITEM* item = collector[ i ];
  1213. SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
  1214. SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item );
  1215. SCH_TABLE* table = dynamic_cast<SCH_TABLE*>( item );
  1216. // Lines are hard to hit. Give them a bit more slop to still be considered "exact".
  1217. if( line || ( shape && shape->GetShape() == SHAPE_T::POLY )
  1218. || ( shape && shape->GetShape() == SHAPE_T::ARC ) )
  1219. {
  1220. int pixelThreshold = KiROUND( getView()->ToWorld( 6 ) );
  1221. if( item->HitTest( aPos, pixelThreshold ) )
  1222. exactHits.insert( item );
  1223. }
  1224. else if( table )
  1225. {
  1226. // Consider table cells exact, but not the table itself
  1227. }
  1228. else
  1229. {
  1230. if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
  1231. item->SetFlags( SHOW_ELEC_TYPE );
  1232. if( item->HitTest( aPos, 0 ) )
  1233. exactHits.insert( item );
  1234. item->ClearFlags( SHOW_ELEC_TYPE );
  1235. }
  1236. }
  1237. if( exactHits.size() > 0 && exactHits.size() < (unsigned) collector.GetCount() )
  1238. {
  1239. for( int i = collector.GetCount() - 1; i >= 0; --i )
  1240. {
  1241. EDA_ITEM* item = collector[ i ];
  1242. if( !exactHits.contains( item ) )
  1243. collector.Transfer( item );
  1244. }
  1245. }
  1246. // Find the closest item. (Note that at this point all hits are either exact or non-exact.)
  1247. SEG poss( aPos, aPos );
  1248. EDA_ITEM* closest = nullptr;
  1249. int closestDist = INT_MAX / 4;
  1250. for( EDA_ITEM* item : collector )
  1251. {
  1252. BOX2I bbox = item->GetBoundingBox();
  1253. int dist = INT_MAX / 4;
  1254. // A dominating item is one that would unfairly win distance tests
  1255. // and mask out other items. For example, a filled rectangle "wins"
  1256. // with a zero distance over anything inside it.
  1257. bool dominating = false;
  1258. if( exactHits.contains( item ) )
  1259. {
  1260. if( item->Type() == SCH_PIN_T || item->Type() == SCH_JUNCTION_T )
  1261. {
  1262. closest = item;
  1263. break;
  1264. }
  1265. SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
  1266. SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( item );
  1267. EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
  1268. EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( item );
  1269. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
  1270. if( line )
  1271. {
  1272. dist = line->GetSeg().Distance( aPos );
  1273. }
  1274. else if( field )
  1275. {
  1276. BOX2I box = field->GetBoundingBox();
  1277. EDA_ANGLE orient = field->GetTextAngle();
  1278. if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T )
  1279. {
  1280. if( static_cast<SCH_SYMBOL*>( field->GetParent() )->GetTransform().y1 )
  1281. {
  1282. if( orient.IsHorizontal() )
  1283. orient = ANGLE_VERTICAL;
  1284. else
  1285. orient = ANGLE_HORIZONTAL;
  1286. }
  1287. }
  1288. field->GetEffectiveTextShape( false, box, orient )
  1289. ->Collide( poss, INT_MAX / 4, &dist );
  1290. }
  1291. else if( text )
  1292. {
  1293. text->GetEffectiveTextShape( false )->Collide( poss, INT_MAX / 4, &dist );
  1294. }
  1295. else if( shape )
  1296. {
  1297. std::vector<SHAPE*> shapes = shape->MakeEffectiveShapes();
  1298. for( SHAPE* s : shapes )
  1299. {
  1300. int shapeDist = dist;
  1301. s->Collide( poss, INT_MAX / 4, &shapeDist );
  1302. if( shapeDist < dist )
  1303. dist = shapeDist;
  1304. delete s;
  1305. }
  1306. // Filled shapes win hit tests anywhere inside them
  1307. dominating = shape->IsFilled();
  1308. }
  1309. else if( symbol )
  1310. {
  1311. bbox = symbol->GetBodyBoundingBox();
  1312. SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
  1313. if( bbox.Contains( aPos ) )
  1314. dist = bbox.GetCenter().Distance( aPos );
  1315. else
  1316. rect.Collide( poss, closestDist, &dist );
  1317. }
  1318. else
  1319. {
  1320. dist = bbox.GetCenter().Distance( aPos );
  1321. }
  1322. }
  1323. else
  1324. {
  1325. SHAPE_RECT rect( bbox.GetPosition(), bbox.GetWidth(), bbox.GetHeight() );
  1326. rect.Collide( poss, collector.m_Threshold, &dist );
  1327. }
  1328. // Don't promote dominating items to be the closest item
  1329. // (they'll always win) - they'll still be available for selection, but they
  1330. // won't boot out worthy competitors.
  1331. if ( !dominating )
  1332. {
  1333. if( dist == closestDist )
  1334. {
  1335. if( item->GetParent() == closest )
  1336. closest = item;
  1337. }
  1338. else if( dist < closestDist )
  1339. {
  1340. closestDist = dist;
  1341. closest = item;
  1342. }
  1343. }
  1344. }
  1345. // Construct a tight box (1/2 height and width) around the center of the closest item.
  1346. // All items which exist at least partly outside this box have sufficient other areas
  1347. // for selection and can be dropped.
  1348. if( closest ) // Don't try and get a tight bbox if nothing is near the mouse pointer
  1349. {
  1350. BOX2I tightBox = closest->GetBoundingBox();
  1351. tightBox.Inflate( -tightBox.GetWidth() / 4, -tightBox.GetHeight() / 4 );
  1352. for( int i = collector.GetCount() - 1; i >= 0; --i )
  1353. {
  1354. EDA_ITEM* item = collector[i];
  1355. if( item == closest )
  1356. continue;
  1357. if( !item->HitTest( tightBox, true ) )
  1358. collector.Transfer( item );
  1359. }
  1360. }
  1361. }
  1362. EE_SELECTION& EE_SELECTION_TOOL::RequestSelection( const std::vector<KICAD_T>& aScanTypes,
  1363. bool aPromoteCellSelections )
  1364. {
  1365. bool anyUnselected = false;
  1366. bool anySelected = false;
  1367. if( m_selection.Empty() )
  1368. {
  1369. VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
  1370. ClearSelection();
  1371. SelectPoint( cursorPos, aScanTypes );
  1372. m_selection.SetIsHover( true );
  1373. m_selection.ClearReferencePoint();
  1374. }
  1375. else // Trim an existing selection by aFilterList
  1376. {
  1377. bool isMoving = false;
  1378. for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
  1379. {
  1380. EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
  1381. isMoving |= static_cast<SCH_ITEM*>( item )->IsMoving();
  1382. if( !item->IsType( aScanTypes ) )
  1383. {
  1384. unselect( item );
  1385. anyUnselected = true;
  1386. }
  1387. }
  1388. if( !isMoving )
  1389. updateReferencePoint();
  1390. }
  1391. if( aPromoteCellSelections )
  1392. {
  1393. std::set<EDA_ITEM*> parents;
  1394. for( int i = (int) m_selection.GetSize() - 1; i >= 0; --i )
  1395. {
  1396. EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
  1397. if( item->Type() == SCH_TABLECELL_T )
  1398. {
  1399. parents.insert( item->GetParent() );
  1400. unselect( item );
  1401. anyUnselected = true;
  1402. }
  1403. }
  1404. for( EDA_ITEM* parent : parents )
  1405. {
  1406. if( !parent->IsSelected() )
  1407. {
  1408. select( parent );
  1409. anySelected = true;
  1410. }
  1411. }
  1412. }
  1413. if( anyUnselected )
  1414. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  1415. if( anySelected )
  1416. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1417. return m_selection;
  1418. }
  1419. bool EE_SELECTION_TOOL::itemPassesFilter( EDA_ITEM* aItem )
  1420. {
  1421. if( !aItem )
  1422. return false;
  1423. // Locking is not yet exposed uniformly in the schematic
  1424. #if 0
  1425. if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( aItem ) )
  1426. {
  1427. if( schItem->IsLocked() && !m_filter.lockedItems )
  1428. return false;
  1429. }
  1430. #endif
  1431. switch( aItem->Type() )
  1432. {
  1433. case SCH_SYMBOL_T:
  1434. case SCH_SHEET_T:
  1435. if( !m_filter.symbols )
  1436. return false;
  1437. break;
  1438. case SCH_PIN_T:
  1439. case SCH_SHEET_PIN_T:
  1440. if( !m_filter.pins )
  1441. return false;
  1442. break;
  1443. case SCH_LINE_T:
  1444. {
  1445. switch( static_cast<SCH_LINE*>( aItem )->GetLayer() )
  1446. {
  1447. case LAYER_WIRE:
  1448. case LAYER_BUS:
  1449. if( !m_filter.wires )
  1450. return false;
  1451. break;
  1452. default:
  1453. if( !m_filter.graphics )
  1454. return false;
  1455. }
  1456. break;
  1457. }
  1458. case SCH_SHAPE_T:
  1459. if( !m_filter.graphics )
  1460. return false;
  1461. break;
  1462. case SCH_TEXT_T:
  1463. case SCH_TEXTBOX_T:
  1464. case SCH_TABLE_T:
  1465. case SCH_TABLECELL_T:
  1466. case SCH_FIELD_T:
  1467. if( !m_filter.text )
  1468. return false;
  1469. break;
  1470. case SCH_LABEL_T:
  1471. case SCH_GLOBAL_LABEL_T:
  1472. case SCH_HIER_LABEL_T:
  1473. if( !m_filter.labels )
  1474. return false;
  1475. break;
  1476. case SCH_BITMAP_T:
  1477. if( !m_filter.images )
  1478. return false;
  1479. break;
  1480. default:
  1481. if( !m_filter.otherItems )
  1482. return false;
  1483. break;
  1484. }
  1485. return true;
  1486. }
  1487. void EE_SELECTION_TOOL::updateReferencePoint()
  1488. {
  1489. VECTOR2I refP( 0, 0 );
  1490. if( m_selection.Size() > 0 )
  1491. refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
  1492. m_selection.SetReferencePoint( refP );
  1493. }
  1494. // Some navigation actions are allowed in selectMultiple
  1495. const TOOL_ACTION* allowedActions[] = { &ACTIONS::panUp, &ACTIONS::panDown,
  1496. &ACTIONS::panLeft, &ACTIONS::panRight,
  1497. &ACTIONS::cursorUp, &ACTIONS::cursorDown,
  1498. &ACTIONS::cursorLeft, &ACTIONS::cursorRight,
  1499. &ACTIONS::cursorUpFast, &ACTIONS::cursorDownFast,
  1500. &ACTIONS::cursorLeftFast, &ACTIONS::cursorRightFast,
  1501. &ACTIONS::zoomIn, &ACTIONS::zoomOut,
  1502. &ACTIONS::zoomInCenter, &ACTIONS::zoomOutCenter,
  1503. &ACTIONS::zoomCenter, &ACTIONS::zoomFitScreen,
  1504. &ACTIONS::zoomFitObjects, nullptr };
  1505. bool EE_SELECTION_TOOL::selectMultiple()
  1506. {
  1507. bool cancelled = false; // Was the tool canceled while it was running?
  1508. m_multiple = true; // Multiple selection mode is active
  1509. KIGFX::VIEW* view = getView();
  1510. KIGFX::PREVIEW::SELECTION_AREA area;
  1511. view->Add( &area );
  1512. while( TOOL_EVENT* evt = Wait() )
  1513. {
  1514. int width = area.GetEnd().x - area.GetOrigin().x;
  1515. int height = area.GetEnd().y - area.GetOrigin().y;
  1516. /* Selection mode depends on direction of drag-selection:
  1517. * Left > Right : Select objects that are fully enclosed by selection
  1518. * Right > Left : Select objects that are crossed by selection
  1519. */
  1520. bool isGreedy = width < 0;
  1521. if( view->IsMirroredX() )
  1522. isGreedy = !isGreedy;
  1523. m_frame->GetCanvas()->SetCurrentCursor( isGreedy ? KICURSOR::SELECT_LASSO
  1524. : KICURSOR::SELECT_WINDOW );
  1525. if( evt->IsCancelInteractive() || evt->IsActivate() )
  1526. {
  1527. cancelled = true;
  1528. break;
  1529. }
  1530. if( evt->IsDrag( BUT_LEFT ) )
  1531. {
  1532. if( !m_drag_additive && !m_drag_subtractive )
  1533. ClearSelection();
  1534. // Start drawing a selection box
  1535. area.SetOrigin( evt->DragOrigin() );
  1536. area.SetEnd( evt->Position() );
  1537. area.SetAdditive( m_drag_additive );
  1538. area.SetSubtractive( m_drag_subtractive );
  1539. area.SetExclusiveOr( false );
  1540. view->SetVisible( &area, true );
  1541. view->Update( &area );
  1542. getViewControls()->SetAutoPan( true );
  1543. }
  1544. if( evt->IsMouseUp( BUT_LEFT ) )
  1545. {
  1546. getViewControls()->SetAutoPan( false );
  1547. // End drawing the selection box
  1548. view->SetVisible( &area, false );
  1549. // Fetch items from the RTree that are in our area of interest
  1550. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> nearbyViewItems;
  1551. view->Query( area.ViewBBox(), nearbyViewItems );
  1552. // Build lists of nearby items and their children
  1553. std::unordered_set<EDA_ITEM*> nearbyItems;
  1554. std::vector<EDA_ITEM*> nearbyChildren;
  1555. std::vector<EDA_ITEM*> flaggedItems;
  1556. for( KIGFX::VIEW::LAYER_ITEM_PAIR& pair : nearbyViewItems )
  1557. {
  1558. if( EDA_ITEM* item = dynamic_cast<EDA_ITEM*>( pair.first ) )
  1559. {
  1560. if( nearbyItems.insert( item ).second )
  1561. {
  1562. item->ClearFlags( CANDIDATE );
  1563. if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( item ) )
  1564. {
  1565. sch_item->RunOnChildren(
  1566. [&]( SCH_ITEM* aChild )
  1567. {
  1568. // Filter pins by unit
  1569. if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( aChild ) )
  1570. {
  1571. int unit = pin->GetLibPin()->GetUnit();
  1572. if( unit && unit != pin->GetParentSymbol()->GetUnit() )
  1573. return;
  1574. }
  1575. nearbyChildren.push_back( aChild );
  1576. } );
  1577. }
  1578. }
  1579. }
  1580. }
  1581. BOX2I selectionRect( area.GetOrigin(), VECTOR2I( width, height ) );
  1582. selectionRect.Normalize();
  1583. bool anyAdded = false;
  1584. bool anySubtracted = false;
  1585. auto selectItem =
  1586. [&]( EDA_ITEM* aItem, EDA_ITEM_FLAGS flags )
  1587. {
  1588. if( m_subtractive || ( m_exclusive_or && aItem->IsSelected() ) )
  1589. {
  1590. if ( m_exclusive_or )
  1591. aItem->XorFlags( flags );
  1592. else
  1593. aItem->ClearFlags( flags );
  1594. if( !aItem->HasFlag( STARTPOINT ) && !aItem->HasFlag( ENDPOINT ) )
  1595. {
  1596. unselect( aItem );
  1597. anySubtracted = true;
  1598. }
  1599. // We changed one line endpoint on a selected line,
  1600. // update the view at least.
  1601. if( flags && !anySubtracted )
  1602. getView()->Update( aItem );
  1603. }
  1604. else
  1605. {
  1606. aItem->SetFlags( flags );
  1607. select( aItem );
  1608. anyAdded = true;
  1609. }
  1610. };
  1611. for( EDA_ITEM* item : nearbyItems )
  1612. {
  1613. bool selected = false;
  1614. EDA_ITEM_FLAGS flags = 0;
  1615. if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
  1616. item->SetFlags( SHOW_ELEC_TYPE );
  1617. if( Selectable( item ) && itemPassesFilter( item ) )
  1618. {
  1619. if( item->Type() == SCH_LINE_T )
  1620. {
  1621. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  1622. if( ( isGreedy && line->HitTest( selectionRect, false ) )
  1623. || ( selectionRect.Contains( line->GetEndPoint() )
  1624. && selectionRect.Contains( line->GetStartPoint() ) ) )
  1625. {
  1626. selected = true;
  1627. flags |= STARTPOINT | ENDPOINT;
  1628. }
  1629. else if( !isGreedy )
  1630. {
  1631. if( selectionRect.Contains( line->GetStartPoint() )
  1632. && line->IsStartDangling() )
  1633. {
  1634. selected = true;
  1635. flags |= STARTPOINT;
  1636. }
  1637. if( selectionRect.Contains( line->GetEndPoint() )
  1638. && line->IsEndDangling() )
  1639. {
  1640. selected = true;
  1641. flags |= ENDPOINT;
  1642. }
  1643. }
  1644. }
  1645. else
  1646. {
  1647. selected = item->HitTest( selectionRect, !isGreedy );
  1648. }
  1649. }
  1650. if( selected )
  1651. {
  1652. item->SetFlags( CANDIDATE );
  1653. flaggedItems.push_back( item );
  1654. selectItem( item, flags );
  1655. }
  1656. item->ClearFlags( SHOW_ELEC_TYPE );
  1657. }
  1658. for( EDA_ITEM* item : nearbyChildren )
  1659. {
  1660. if( m_frame->GetRenderSettings()->m_ShowPinsElectricalType )
  1661. item->SetFlags( SHOW_ELEC_TYPE );
  1662. if( Selectable( item )
  1663. && itemPassesFilter( item )
  1664. && !item->GetParent()->HasFlag( CANDIDATE )
  1665. && item->HitTest( selectionRect, !isGreedy ) )
  1666. {
  1667. selectItem( item, 0 );
  1668. }
  1669. item->ClearFlags( SHOW_ELEC_TYPE );
  1670. }
  1671. for( EDA_ITEM* item : flaggedItems )
  1672. item->ClearFlags( CANDIDATE );
  1673. m_selection.SetIsHover( false );
  1674. // Inform other potentially interested tools
  1675. if( anyAdded )
  1676. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1677. if( anySubtracted )
  1678. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  1679. break; // Stop waiting for events
  1680. }
  1681. // Allow some actions for navigation
  1682. for( int i = 0; allowedActions[i]; ++i )
  1683. {
  1684. if( evt->IsAction( allowedActions[i] ) )
  1685. {
  1686. evt->SetPassEvent();
  1687. break;
  1688. }
  1689. }
  1690. }
  1691. getViewControls()->SetAutoPan( false );
  1692. // Stop drawing the selection box
  1693. view->Remove( &area );
  1694. m_multiple = false; // Multiple selection mode is inactive
  1695. if( !cancelled )
  1696. m_selection.ClearReferencePoint();
  1697. return cancelled;
  1698. }
  1699. bool EE_SELECTION_TOOL::selectTableCells( SCH_TABLE* aTable )
  1700. {
  1701. bool cancelled = false; // Was the tool canceled while it was running?
  1702. m_multiple = true; // Multiple selection mode is active
  1703. for( SCH_TABLECELL* cell : aTable->GetCells() )
  1704. {
  1705. if( cell->IsSelected() )
  1706. cell->SetFlags( CANDIDATE );
  1707. else
  1708. cell->ClearFlags( CANDIDATE );
  1709. }
  1710. auto wasSelected =
  1711. []( EDA_ITEM* aItem )
  1712. {
  1713. return ( aItem->GetFlags() & CANDIDATE ) > 0;
  1714. };
  1715. while( TOOL_EVENT* evt = Wait() )
  1716. {
  1717. if( evt->IsCancelInteractive() || evt->IsActivate() )
  1718. {
  1719. cancelled = true;
  1720. break;
  1721. }
  1722. else if( evt->IsDrag( BUT_LEFT ) )
  1723. {
  1724. getViewControls()->SetAutoPan( true );
  1725. BOX2I selectionRect( evt->DragOrigin(), evt->Position() - evt->DragOrigin() );
  1726. selectionRect.Normalize();
  1727. for( SCH_TABLECELL* cell : aTable->GetCells() )
  1728. {
  1729. bool doSelect = false;
  1730. if( cell->HitTest( selectionRect, false ) )
  1731. {
  1732. if( m_subtractive )
  1733. doSelect = false;
  1734. else if( m_exclusive_or )
  1735. doSelect = !wasSelected( cell );
  1736. else
  1737. doSelect = true;
  1738. }
  1739. else if( wasSelected( cell ) )
  1740. {
  1741. doSelect = m_additive || m_subtractive || m_exclusive_or;
  1742. }
  1743. if( doSelect && !cell->IsSelected() )
  1744. select( cell );
  1745. else if( !doSelect && cell->IsSelected() )
  1746. unselect( cell );
  1747. }
  1748. }
  1749. else if( evt->IsMouseUp( BUT_LEFT ) )
  1750. {
  1751. m_selection.SetIsHover( false );
  1752. bool anyAdded = false;
  1753. bool anySubtracted = false;
  1754. for( SCH_TABLECELL* cell : aTable->GetCells() )
  1755. {
  1756. if( cell->IsSelected() && !wasSelected( cell ) )
  1757. anyAdded = true;
  1758. else if( wasSelected( cell ) && !cell->IsSelected() )
  1759. anySubtracted = true;
  1760. }
  1761. // Inform other potentially interested tools
  1762. if( anyAdded )
  1763. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1764. if( anySubtracted )
  1765. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  1766. break; // Stop waiting for events
  1767. }
  1768. else
  1769. {
  1770. // Allow some actions for navigation
  1771. for( int i = 0; allowedActions[i]; ++i )
  1772. {
  1773. if( evt->IsAction( allowedActions[i] ) )
  1774. {
  1775. evt->SetPassEvent();
  1776. break;
  1777. }
  1778. }
  1779. }
  1780. }
  1781. getViewControls()->SetAutoPan( false );
  1782. m_multiple = false; // Multiple selection mode is inactive
  1783. if( !cancelled )
  1784. m_selection.ClearReferencePoint();
  1785. return cancelled;
  1786. }
  1787. EDA_ITEM* EE_SELECTION_TOOL::GetNode( const VECTOR2I& aPosition )
  1788. {
  1789. EE_COLLECTOR collector;
  1790. //TODO(snh): Reimplement after exposing KNN interface
  1791. int pixelThreshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  1792. int gridThreshold = KiROUND( getView()->GetGAL()->GetGridSize().EuclideanNorm() );
  1793. int thresholdMax = std::max( pixelThreshold, gridThreshold );
  1794. for( int threshold : { 0, thresholdMax/4, thresholdMax/2, thresholdMax } )
  1795. {
  1796. collector.m_Threshold = threshold;
  1797. collector.Collect( m_frame->GetScreen(), connectedTypes, aPosition );
  1798. if( collector.GetCount() > 0 )
  1799. break;
  1800. }
  1801. return collector.GetCount() ? collector[ 0 ] : nullptr;
  1802. }
  1803. int EE_SELECTION_TOOL::SelectNode( const TOOL_EVENT& aEvent )
  1804. {
  1805. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
  1806. SelectPoint( cursorPos, connectedTypes );
  1807. return 0;
  1808. }
  1809. int EE_SELECTION_TOOL::SelectConnection( const TOOL_EVENT& aEvent )
  1810. {
  1811. RequestSelection( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T,
  1812. SCH_ITEM_LOCATE_GRAPHIC_LINE_T } );
  1813. if( m_selection.Empty() )
  1814. return 0;
  1815. unsigned done = false;
  1816. m_frame->GetScreen()->ClearDrawingState();
  1817. for( EDA_ITEM* selItem : m_selection.GetItems() )
  1818. {
  1819. if( selItem->Type() != SCH_LINE_T )
  1820. continue;
  1821. SCH_LINE* line = static_cast<SCH_LINE*>( selItem );
  1822. std::set<SCH_ITEM*> conns = m_frame->GetScreen()->MarkConnections( line, false );
  1823. for( SCH_ITEM* item : conns )
  1824. {
  1825. if( item->IsType( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T,
  1826. SCH_ITEM_LOCATE_GRAPHIC_LINE_T } )
  1827. && !item->IsSelected() )
  1828. {
  1829. done = true;
  1830. }
  1831. select( item );
  1832. }
  1833. if( !done )
  1834. {
  1835. conns = m_frame->GetScreen()->MarkConnections( line, true );
  1836. for( SCH_ITEM* item : conns )
  1837. select( item );
  1838. }
  1839. }
  1840. if( m_selection.GetSize() > 1 )
  1841. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1842. return 0;
  1843. }
  1844. int EE_SELECTION_TOOL::SelectColumns( const TOOL_EVENT& aEvent )
  1845. {
  1846. std::set<std::pair<SCH_TABLE*, int>> columns;
  1847. bool added = false;
  1848. for( EDA_ITEM* item : m_selection )
  1849. {
  1850. if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
  1851. {
  1852. SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
  1853. columns.insert( std::make_pair( table, cell->GetColumn() ) );
  1854. }
  1855. }
  1856. for( auto& [ table, col ] : columns )
  1857. {
  1858. for( int row = 0; row < table->GetRowCount(); ++row )
  1859. {
  1860. SCH_TABLECELL* cell = table->GetCell( row, col );
  1861. if( !cell->IsSelected() )
  1862. {
  1863. select( table->GetCell( row, col ) );
  1864. added = true;
  1865. }
  1866. }
  1867. }
  1868. if( added )
  1869. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1870. return 0;
  1871. }
  1872. int EE_SELECTION_TOOL::SelectRows( const TOOL_EVENT& aEvent )
  1873. {
  1874. std::set<std::pair<SCH_TABLE*, int>> rows;
  1875. bool added = false;
  1876. for( EDA_ITEM* item : m_selection )
  1877. {
  1878. if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
  1879. {
  1880. SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
  1881. rows.insert( std::make_pair( table, cell->GetRow() ) );
  1882. }
  1883. }
  1884. for( auto& [ table, row ] : rows )
  1885. {
  1886. for( int col = 0; col < table->GetColCount(); ++col )
  1887. {
  1888. SCH_TABLECELL* cell = table->GetCell( row, col );
  1889. if( !cell->IsSelected() )
  1890. {
  1891. select( table->GetCell( row, col ) );
  1892. added = true;
  1893. }
  1894. }
  1895. }
  1896. if( added )
  1897. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1898. return 0;
  1899. }
  1900. int EE_SELECTION_TOOL::SelectTable( const TOOL_EVENT& aEvent )
  1901. {
  1902. std::set<SCH_TABLE*> tables;
  1903. bool added = false;
  1904. for( EDA_ITEM* item : m_selection )
  1905. {
  1906. if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
  1907. tables.insert( static_cast<SCH_TABLE*>( cell->GetParent() ) );
  1908. }
  1909. ClearSelection();
  1910. for( SCH_TABLE* table : tables )
  1911. {
  1912. if( !table->IsSelected() )
  1913. {
  1914. select( table );
  1915. added = true;
  1916. }
  1917. }
  1918. if( added )
  1919. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1920. return 0;
  1921. }
  1922. int EE_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
  1923. {
  1924. ClearSelection();
  1925. return 0;
  1926. }
  1927. void EE_SELECTION_TOOL::ZoomFitCrossProbeBBox( const BOX2I& aBBox )
  1928. {
  1929. if( aBBox.GetWidth() == 0 )
  1930. return;
  1931. BOX2I bbox = aBBox;
  1932. bbox.Normalize();
  1933. VECTOR2I bbSize = bbox.Inflate( KiROUND( bbox.GetWidth() * 0.2f ) ).GetSize();
  1934. VECTOR2D screenSize = getView()->GetViewport().GetSize();
  1935. // This code tries to come up with a zoom factor that doesn't simply zoom in to the cross
  1936. // probed symbol, but instead shows a reasonable amount of the circuit around it to provide
  1937. // context. This reduces the need to manually change the zoom because it's too close.
  1938. // Using the default text height as a constant to compare against, use the height of the
  1939. // bounding box of visible items for a footprint to figure out if this is a big symbol (like
  1940. // a processor) or a small symbol (like a resistor). This ratio is not useful by itself as a
  1941. // scaling factor. It must be "bent" to provide good scaling at varying symbol sizes. Bigger
  1942. // symbols need less scaling than small ones.
  1943. double currTextHeight = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
  1944. double compRatio = bbSize.y / currTextHeight; // Ratio of symbol to text height
  1945. double compRatioBent = 1.0;
  1946. // LUT to scale zoom ratio to provide reasonable schematic context. Must work with symbols
  1947. // of varying sizes (e.g. 0402 package and 200 pin BGA).
  1948. // Each entry represents a compRatio (symbol height / default text height) and an amount to
  1949. // scale by.
  1950. std::vector<std::pair<double, double>> lut{ { 1.25, 16 },
  1951. { 2.5, 12 },
  1952. { 5, 8 },
  1953. { 6, 6 },
  1954. { 10, 4 },
  1955. { 20, 2 },
  1956. { 40, 1.5 },
  1957. { 100, 1 } };
  1958. std::vector<std::pair<double, double>>::iterator it;
  1959. // Large symbol default is last LUT entry (1:1).
  1960. compRatioBent = lut.back().second;
  1961. // Use LUT to do linear interpolation of "compRatio" within "first", then use that result to
  1962. // linearly interpolate "second" which gives the scaling factor needed.
  1963. if( compRatio >= lut.front().first )
  1964. {
  1965. for( it = lut.begin(); it < lut.end() - 1; ++it )
  1966. {
  1967. if( it->first <= compRatio && next( it )->first >= compRatio )
  1968. {
  1969. double diffx = compRatio - it->first;
  1970. double diffn = next( it )->first - it->first;
  1971. compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
  1972. break; // We have our interpolated value
  1973. }
  1974. }
  1975. }
  1976. else
  1977. {
  1978. compRatioBent = lut.front().second; // Small symbol default is first entry
  1979. }
  1980. // This is similar to the original KiCad code that scaled the zoom to make sure symbols were
  1981. // visible on screen. It's simply a ratio of screen size to symbol size, and its job is to
  1982. // zoom in to make the component fullscreen. Earlier in the code the symbol BBox is given a
  1983. // 20% margin to add some breathing room. We compare the height of this enlarged symbol bbox
  1984. // to the default text height. If a symbol will end up with the sides clipped, we adjust
  1985. // later to make sure it fits on screen.
  1986. screenSize.x = std::max( 10.0, screenSize.x );
  1987. screenSize.y = std::max( 10.0, screenSize.y );
  1988. double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
  1989. // Original KiCad code for how much to scale the zoom
  1990. double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
  1991. fabs( bbSize.y / screenSize.y ) );
  1992. // If the width of the part we're probing is bigger than what the screen width will be after
  1993. // the zoom, then punt and use the KiCad zoom algorithm since it guarantees the part's width
  1994. // will be encompassed within the screen.
  1995. if( bbSize.x > screenSize.x * ratio * compRatioBent )
  1996. {
  1997. // Use standard KiCad zoom for parts too wide to fit on screen/
  1998. ratio = kicadRatio;
  1999. compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
  2000. wxLogTrace( "CROSS_PROBE_SCALE",
  2001. "Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
  2002. }
  2003. // Now that "compRatioBent" holds our final scaling factor we apply it to the original
  2004. // fullscreen zoom ratio to arrive at the final ratio itself.
  2005. ratio *= compRatioBent;
  2006. bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
  2007. // Try not to zoom on every cross-probe; it gets very noisy
  2008. if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
  2009. getView()->SetScale( getView()->GetScale() / ratio );
  2010. }
  2011. void EE_SELECTION_TOOL::SyncSelection( const std::optional<SCH_SHEET_PATH>& targetSheetPath,
  2012. SCH_ITEM* focusItem, const std::vector<SCH_ITEM*>& items )
  2013. {
  2014. SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
  2015. if( !editFrame )
  2016. return;
  2017. if( targetSheetPath && targetSheetPath != editFrame->Schematic().CurrentSheet() )
  2018. {
  2019. editFrame->Schematic().SetCurrentSheet( *targetSheetPath );
  2020. editFrame->DisplayCurrentSheet();
  2021. }
  2022. ClearSelection( items.size() > 0 ? true /*quiet mode*/ : false );
  2023. // Perform individual selection of each item before processing the event.
  2024. for( SCH_ITEM* item : items )
  2025. {
  2026. SCH_ITEM* parent = dynamic_cast<SCH_ITEM*>( item->GetParent() );
  2027. // Make sure we only select items on the current screen
  2028. if( m_frame->GetScreen()->CheckIfOnDrawList( item )
  2029. || ( parent && m_frame->GetScreen()->CheckIfOnDrawList( parent ) ) )
  2030. {
  2031. select( item );
  2032. }
  2033. }
  2034. BOX2I bbox = m_selection.GetBoundingBox();
  2035. if( bbox.GetWidth() != 0 && bbox.GetHeight() != 0 )
  2036. {
  2037. if( m_frame->eeconfig()->m_CrossProbing.center_on_items )
  2038. {
  2039. if( m_frame->eeconfig()->m_CrossProbing.zoom_to_fit )
  2040. ZoomFitCrossProbeBBox( bbox );
  2041. editFrame->FocusOnItem( focusItem );
  2042. if( !focusItem )
  2043. editFrame->FocusOnLocation( bbox.Centre() );
  2044. }
  2045. }
  2046. if( m_selection.Size() > 0 )
  2047. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  2048. }
  2049. void EE_SELECTION_TOOL::RebuildSelection()
  2050. {
  2051. m_selection.Clear();
  2052. if( m_isSymbolEditor )
  2053. {
  2054. LIB_SYMBOL* start = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
  2055. for( SCH_ITEM& item : start->GetDrawItems() )
  2056. {
  2057. if( item.IsSelected() )
  2058. select( &item );
  2059. }
  2060. }
  2061. else
  2062. {
  2063. for( SCH_ITEM* item : m_frame->GetScreen()->Items() )
  2064. {
  2065. // If the field and symbol are selected, only use the symbol
  2066. if( item->IsSelected() )
  2067. {
  2068. select( item );
  2069. }
  2070. else
  2071. {
  2072. item->RunOnChildren(
  2073. [&]( SCH_ITEM* aChild )
  2074. {
  2075. if( aChild->IsSelected() )
  2076. select( aChild );
  2077. } );
  2078. }
  2079. }
  2080. }
  2081. updateReferencePoint();
  2082. // Inform other potentially interested tools
  2083. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  2084. }
  2085. bool EE_SELECTION_TOOL::Selectable( const EDA_ITEM* aItem, const VECTOR2I* aPos,
  2086. bool checkVisibilityOnly ) const
  2087. {
  2088. // NOTE: in the future this is where Eeschema layer/itemtype visibility will be handled
  2089. SYMBOL_EDIT_FRAME* symEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
  2090. // Do not allow selection of anything except fields when the current symbol in the symbol
  2091. // editor is a derived symbol.
  2092. if( symEditFrame && symEditFrame->IsSymbolAlias() && aItem->Type() != SCH_FIELD_T )
  2093. return false;
  2094. switch( aItem->Type() )
  2095. {
  2096. case SCH_PIN_T:
  2097. {
  2098. const SCH_PIN* pin = static_cast<const SCH_PIN*>( aItem );
  2099. if( symEditFrame )
  2100. {
  2101. if( pin->GetUnit() && pin->GetUnit() != symEditFrame->GetUnit() )
  2102. return false;
  2103. if( pin->GetBodyStyle() && pin->GetBodyStyle() != symEditFrame->GetBodyStyle() )
  2104. return false;
  2105. }
  2106. if( !pin->IsVisible() && !m_frame->GetShowAllPins() )
  2107. return false;
  2108. if( !m_filter.pins )
  2109. {
  2110. // Pin anchors have to be allowed for auto-starting wires.
  2111. if( aPos )
  2112. {
  2113. EE_GRID_HELPER grid( m_toolMgr );
  2114. GRID_HELPER_GRIDS pinGrid = grid.GetItemGrid( pin );
  2115. if( pin->IsPointClickableAnchor( grid.BestSnapAnchor( *aPos, pinGrid ) ) )
  2116. return true;
  2117. }
  2118. return false;
  2119. }
  2120. break;
  2121. }
  2122. case SCH_DIRECTIVE_LABEL_T:
  2123. if( !m_frame->eeconfig()->m_Appearance.show_directive_labels )
  2124. return false;
  2125. break;
  2126. case LIB_SYMBOL_T: // In symbol_editor we do not want to select the symbol itself.
  2127. return false;
  2128. case SCH_FIELD_T: // SCH_FIELD objects are not unit/body-style-specific.
  2129. {
  2130. const SCH_FIELD* field = static_cast<const SCH_FIELD*>( aItem );
  2131. if( !field->IsVisible() && !( symEditFrame && symEditFrame->GetShowInvisibleFields() ) )
  2132. return false;
  2133. break;
  2134. }
  2135. case SCH_SHAPE_T:
  2136. case SCH_TEXT_T:
  2137. case SCH_TEXTBOX_T:
  2138. if( symEditFrame )
  2139. {
  2140. const SCH_ITEM* sch_item = static_cast<const SCH_ITEM*>( aItem );
  2141. if( sch_item->GetUnit() && sch_item->GetUnit() != symEditFrame->GetUnit() )
  2142. return false;
  2143. if( sch_item->GetBodyStyle() && sch_item->GetBodyStyle() != symEditFrame->GetBodyStyle() )
  2144. return false;
  2145. }
  2146. break;
  2147. case SCH_MARKER_T: // Always selectable
  2148. return true;
  2149. case SCH_TABLECELL_T:
  2150. {
  2151. const SCH_TABLECELL* cell = static_cast<const SCH_TABLECELL*>( aItem );
  2152. if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
  2153. return false;
  2154. break;
  2155. }
  2156. default: // Suppress warnings
  2157. break;
  2158. }
  2159. return true;
  2160. }
  2161. void EE_SELECTION_TOOL::ClearSelection( bool aQuietMode )
  2162. {
  2163. if( m_selection.Empty() )
  2164. return;
  2165. while( m_selection.GetSize() )
  2166. unhighlight( m_selection.Front(), SELECTED, &m_selection );
  2167. getView()->Update( &m_selection );
  2168. m_selection.SetIsHover( false );
  2169. m_selection.ClearReferencePoint();
  2170. // Inform other potentially interested tools
  2171. if( !aQuietMode )
  2172. m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
  2173. }
  2174. void EE_SELECTION_TOOL::select( EDA_ITEM* aItem )
  2175. {
  2176. highlight( aItem, SELECTED, &m_selection );
  2177. }
  2178. void EE_SELECTION_TOOL::unselect( EDA_ITEM* aItem )
  2179. {
  2180. unhighlight( aItem, SELECTED, &m_selection );
  2181. }
  2182. void EE_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
  2183. {
  2184. if( aMode == SELECTED )
  2185. aItem->SetSelected();
  2186. else if( aMode == BRIGHTENED )
  2187. aItem->SetBrightened();
  2188. if( aGroup )
  2189. aGroup->Add( aItem );
  2190. // Highlight pins and fields. (All the other symbol children are currently only
  2191. // represented in the LIB_SYMBOL and will inherit the settings of the parent symbol.)
  2192. if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
  2193. {
  2194. sch_item->RunOnChildren(
  2195. [&]( SCH_ITEM* aChild )
  2196. {
  2197. if( aMode == SELECTED )
  2198. {
  2199. aChild->SetSelected();
  2200. getView()->Hide( aChild, true );
  2201. }
  2202. else if( aMode == BRIGHTENED )
  2203. {
  2204. aChild->SetBrightened();
  2205. }
  2206. } );
  2207. }
  2208. if( aGroup && aMode != BRIGHTENED )
  2209. getView()->Hide( aItem, true );
  2210. if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
  2211. getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
  2212. getView()->Update( aItem, KIGFX::REPAINT );
  2213. }
  2214. void EE_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
  2215. {
  2216. if( aMode == SELECTED )
  2217. {
  2218. aItem->ClearSelected();
  2219. // Lines need endpoints cleared here
  2220. if( aItem->Type() == SCH_LINE_T )
  2221. aItem->ClearFlags( STARTPOINT | ENDPOINT );
  2222. if( aMode != BRIGHTENED )
  2223. getView()->Hide( aItem, false );
  2224. }
  2225. else if( aMode == BRIGHTENED )
  2226. {
  2227. aItem->ClearBrightened();
  2228. }
  2229. if( aGroup )
  2230. aGroup->Remove( aItem );
  2231. // Unhighlight pins and fields. (All the other symbol children are currently only
  2232. // represented in the LIB_SYMBOL.)
  2233. if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
  2234. {
  2235. sch_item->RunOnChildren(
  2236. [&]( SCH_ITEM* aChild )
  2237. {
  2238. if( aMode == SELECTED )
  2239. {
  2240. aChild->ClearSelected();
  2241. getView()->Hide( aChild, false );
  2242. }
  2243. else if( aMode == BRIGHTENED )
  2244. {
  2245. aChild->ClearBrightened();
  2246. }
  2247. if( aGroup )
  2248. aGroup->Remove( aChild );
  2249. } );
  2250. }
  2251. if( aItem->GetParent() && aItem->GetParent()->Type() != SCHEMATIC_T )
  2252. getView()->Update( aItem->GetParent(), KIGFX::REPAINT );
  2253. getView()->Update( aItem, KIGFX::REPAINT );
  2254. }
  2255. bool EE_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
  2256. {
  2257. const unsigned GRIP_MARGIN = 20;
  2258. int margin = KiROUND( getView()->ToWorld( GRIP_MARGIN ) );
  2259. // Check if the point is located within any of the currently selected items bounding boxes
  2260. for( EDA_ITEM* item : m_selection )
  2261. {
  2262. BOX2I itemBox = item->ViewBBox();
  2263. itemBox.Inflate( margin ); // Give some margin for gripping an item
  2264. if( itemBox.Contains( aPoint ) )
  2265. return true;
  2266. }
  2267. return false;
  2268. }
  2269. void EE_SELECTION_TOOL::setTransitions()
  2270. {
  2271. Go( &EE_SELECTION_TOOL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
  2272. Go( &EE_SELECTION_TOOL::Main, EE_ACTIONS::selectionActivate.MakeEvent() );
  2273. Go( &EE_SELECTION_TOOL::SelectNode, EE_ACTIONS::selectNode.MakeEvent() );
  2274. Go( &EE_SELECTION_TOOL::SelectConnection, EE_ACTIONS::selectConnection.MakeEvent() );
  2275. Go( &EE_SELECTION_TOOL::SelectColumns, ACTIONS::selectColumns.MakeEvent() );
  2276. Go( &EE_SELECTION_TOOL::SelectRows, ACTIONS::selectRows.MakeEvent() );
  2277. Go( &EE_SELECTION_TOOL::SelectTable, ACTIONS::selectTable.MakeEvent() );
  2278. Go( &EE_SELECTION_TOOL::ClearSelection, EE_ACTIONS::clearSelection.MakeEvent() );
  2279. Go( &EE_SELECTION_TOOL::AddItemToSel, EE_ACTIONS::addItemToSel.MakeEvent() );
  2280. Go( &EE_SELECTION_TOOL::AddItemsToSel, EE_ACTIONS::addItemsToSel.MakeEvent() );
  2281. Go( &EE_SELECTION_TOOL::RemoveItemFromSel, EE_ACTIONS::removeItemFromSel.MakeEvent() );
  2282. Go( &EE_SELECTION_TOOL::RemoveItemsFromSel, EE_ACTIONS::removeItemsFromSel.MakeEvent() );
  2283. Go( &EE_SELECTION_TOOL::SelectionMenu, EE_ACTIONS::selectionMenu.MakeEvent() );
  2284. Go( &EE_SELECTION_TOOL::SelectAll, EE_ACTIONS::selectAll.MakeEvent() );
  2285. Go( &EE_SELECTION_TOOL::UnselectAll, EE_ACTIONS::unselectAll.MakeEvent() );
  2286. Go( &EE_SELECTION_TOOL::disambiguateCursor, EVENTS::DisambiguatePoint );
  2287. }