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.

1271 lines
38 KiB

7 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <ee_actions.h>
  24. #include <core/typeinfo.h>
  25. #include <sch_item.h>
  26. #include <ee_selection_tool.h>
  27. #include <sch_base_frame.h>
  28. #include <sch_edit_frame.h>
  29. #include <lib_edit_frame.h>
  30. #include <viewlib_frame.h>
  31. #include <sch_component.h>
  32. #include <sch_sheet.h>
  33. #include <sch_field.h>
  34. #include <view/view.h>
  35. #include <view/view_controls.h>
  36. #include <view/view_group.h>
  37. #include <preview_items/selection_area.h>
  38. #include <tool/tool_event.h>
  39. #include <tool/tool_manager.h>
  40. #include <tools/sch_wire_bus_tool.h>
  41. #include <ee_collectors.h>
  42. #include <painter.h>
  43. #include <eeschema_id.h>
  44. #include <menus_helpers.h>
  45. #include <ee_hotkeys.h>
  46. // Selection tool actions
  47. TOOL_ACTION EE_ACTIONS::selectionActivate( "eeschema.InteractiveSelection",
  48. AS_GLOBAL, 0, "", "", // No description, not shown anywhere
  49. nullptr, AF_ACTIVATE );
  50. TOOL_ACTION EE_ACTIONS::selectionTool( "eeschema.InteractiveSelection.selectionTool",
  51. AS_GLOBAL, 0, _( "Select item(s)" ), "",
  52. cursor_xpm, AF_ACTIVATE );
  53. TOOL_ACTION EE_ACTIONS::selectNode( "eeschema.InteractiveSelection.SelectNode",
  54. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SELECT_NODE ),
  55. _( "Select Node" ), _( "Select a connection item under the cursor" ) );
  56. TOOL_ACTION EE_ACTIONS::selectConnection( "eeschema.InteractiveSelection.SelectConnection",
  57. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SELECT_CONNECTION ),
  58. _( "Select Connection" ), _( "Select a complete connection" ) );
  59. TOOL_ACTION EE_ACTIONS::selectionMenu( "eeschema.InteractiveSelection.SelectionMenu",
  60. AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere
  61. TOOL_ACTION EE_ACTIONS::addItemToSel( "eeschema.InteractiveSelection.AddItemToSel",
  62. AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere
  63. TOOL_ACTION EE_ACTIONS::addItemsToSel( "eeschema.InteractiveSelection.AddItemsToSel",
  64. AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere
  65. TOOL_ACTION EE_ACTIONS::removeItemFromSel( "eeschema.InteractiveSelection.RemoveItemFromSel",
  66. AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere
  67. TOOL_ACTION EE_ACTIONS::removeItemsFromSel( "eeschema.InteractiveSelection.RemoveItemsFromSel",
  68. AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere
  69. TOOL_ACTION EE_ACTIONS::clearSelection( "eeschema.InteractiveSelection.ClearSelection",
  70. AS_GLOBAL, 0, "", "" ); // No description, it is not supposed to be shown anywhere
  71. SELECTION_CONDITION EE_CONDITIONS::Empty = [] (const SELECTION& aSelection )
  72. {
  73. return aSelection.Empty();
  74. };
  75. SELECTION_CONDITION EE_CONDITIONS::Idle = [] (const SELECTION& aSelection )
  76. {
  77. return ( !aSelection.Front() || aSelection.Front()->GetEditFlags() == 0 );
  78. };
  79. SELECTION_CONDITION EE_CONDITIONS::IdleSelection = [] (const SELECTION& aSelection )
  80. {
  81. return ( aSelection.Front() && aSelection.Front()->GetEditFlags() == 0 );
  82. };
  83. SELECTION_CONDITION EE_CONDITIONS::SingleSymbol = [] (const SELECTION& aSel )
  84. {
  85. if( aSel.GetSize() == 1 )
  86. {
  87. SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
  88. if( comp )
  89. {
  90. auto partRef = comp->GetPartRef().lock();
  91. return !partRef || !partRef->IsPower();
  92. }
  93. }
  94. return false;
  95. };
  96. SELECTION_CONDITION EE_CONDITIONS::SingleDeMorganSymbol = [] ( const SELECTION& aSel )
  97. {
  98. if( aSel.GetSize() == 1 )
  99. {
  100. SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
  101. if( comp )
  102. {
  103. auto partRef = comp->GetPartRef().lock();
  104. return partRef && partRef->HasConversion();
  105. }
  106. }
  107. return false;
  108. };
  109. SELECTION_CONDITION EE_CONDITIONS::SingleMultiUnitSymbol = [] ( const SELECTION& aSel )
  110. {
  111. if( aSel.GetSize() == 1 )
  112. {
  113. SCH_COMPONENT* comp = dynamic_cast<SCH_COMPONENT*>( aSel.Front() );
  114. if( comp )
  115. {
  116. auto partRef = comp->GetPartRef().lock();
  117. return partRef && partRef->GetUnitCount() >= 2;
  118. }
  119. }
  120. return false;
  121. };
  122. #define HITTEST_THRESHOLD_PIXELS 5
  123. EE_SELECTION_TOOL::EE_SELECTION_TOOL() :
  124. TOOL_INTERACTIVE( "eeschema.InteractiveSelection" ),
  125. m_frame( nullptr ),
  126. m_additive( false ),
  127. m_subtractive( false ),
  128. m_multiple( false ),
  129. m_skip_heuristics( false ),
  130. m_isLibEdit( false ),
  131. m_isLibView( false ),
  132. m_unit( 0 ),
  133. m_convert( 0 ),
  134. m_menu( *this )
  135. {
  136. }
  137. EE_SELECTION_TOOL::~EE_SELECTION_TOOL()
  138. {
  139. getView()->Remove( &m_selection );
  140. }
  141. using E_C = EE_CONDITIONS;
  142. bool EE_SELECTION_TOOL::Init()
  143. {
  144. m_frame = getEditFrame<SCH_BASE_FRAME>();
  145. LIB_VIEW_FRAME* libViewFrame = dynamic_cast<LIB_VIEW_FRAME*>( m_frame );
  146. LIB_EDIT_FRAME* libEditFrame = dynamic_cast<LIB_EDIT_FRAME*>( m_frame );
  147. if( libEditFrame )
  148. {
  149. m_isLibEdit = true;
  150. m_unit = libEditFrame->GetUnit();
  151. m_convert = libEditFrame->GetConvert();
  152. }
  153. else
  154. m_isLibView = libViewFrame != nullptr;
  155. static KICAD_T wireOrBusTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
  156. auto wireSelection = E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_LINE_LOCATE_WIRE_T );
  157. auto busSelection = E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_LINE_LOCATE_BUS_T );
  158. auto wireOrBusSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( wireOrBusTypes );
  159. auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyType( SCH_SHEET_T );
  160. auto schEditCondition = [this] ( const SELECTION& aSel ) {
  161. return !m_isLibEdit && !m_isLibView;
  162. };
  163. auto belowRootSheetCondition = [this] ( const SELECTION& aSel ) {
  164. return !m_isLibEdit && !m_isLibView && g_CurrentSheet->Last() != g_RootSheet;
  165. };
  166. auto havePartCondition = [ this ] ( const SELECTION& sel ) {
  167. return m_isLibEdit && ( (LIB_EDIT_FRAME*) m_frame )->GetCurPart();
  168. };
  169. auto& menu = m_menu.GetMenu();
  170. menu.AddItem( EE_ACTIONS::enterSheet, sheetSelection && EE_CONDITIONS::Idle, 1 );
  171. menu.AddItem( EE_ACTIONS::explicitCrossProbe, sheetSelection && EE_CONDITIONS::Idle, 1 );
  172. menu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 1 );
  173. menu.AddSeparator( EE_CONDITIONS::Empty, 100 );
  174. menu.AddItem( EE_ACTIONS::startWire, schEditCondition && EE_CONDITIONS::Empty, 100 );
  175. menu.AddItem( EE_ACTIONS::startBus, schEditCondition && EE_CONDITIONS::Empty, 100 );
  176. menu.AddSeparator( SCH_WIRE_BUS_TOOL::IsDrawingWire, 100 );
  177. menu.AddItem( EE_ACTIONS::finishWire, SCH_WIRE_BUS_TOOL::IsDrawingWire, 100 );
  178. menu.AddSeparator( SCH_WIRE_BUS_TOOL::IsDrawingBus, 100 );
  179. menu.AddItem( EE_ACTIONS::finishBus, SCH_WIRE_BUS_TOOL::IsDrawingBus, 100 );
  180. menu.AddSeparator( EE_CONDITIONS::NotEmpty, 200 );
  181. menu.AddItem( EE_ACTIONS::selectConnection, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  182. menu.AddItem( EE_ACTIONS::addJunction, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  183. menu.AddItem( EE_ACTIONS::addLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  184. menu.AddItem( EE_ACTIONS::addGlobalLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  185. menu.AddItem( EE_ACTIONS::addHierLabel, wireOrBusSelection && EE_CONDITIONS::Idle, 250 );
  186. menu.AddItem( EE_ACTIONS::breakWire, wireSelection && EE_CONDITIONS::Idle, 250 );
  187. menu.AddItem( EE_ACTIONS::breakBus, busSelection && EE_CONDITIONS::Idle, 250 );
  188. menu.AddItem( EE_ACTIONS::addSheetPin, sheetSelection && EE_CONDITIONS::Idle, 250 );
  189. menu.AddItem( EE_ACTIONS::addImportedSheetPin,sheetSelection && EE_CONDITIONS::Idle, 250 );
  190. menu.AddSeparator( havePartCondition && EE_CONDITIONS::Empty, 400 );
  191. menu.AddItem( EE_ACTIONS::symbolProperties, havePartCondition && EE_CONDITIONS::Empty, 400 );
  192. menu.AddItem( EE_ACTIONS::pinTable, havePartCondition && EE_CONDITIONS::Empty, 400 );
  193. menu.AddSeparator( SELECTION_CONDITIONS::ShowAlways, 1000 );
  194. m_menu.AddStandardSubMenus( m_frame );
  195. return true;
  196. }
  197. void EE_SELECTION_TOOL::Reset( RESET_REASON aReason )
  198. {
  199. m_frame = getEditFrame<SCH_BASE_FRAME>();
  200. if( aReason == TOOL_BASE::MODEL_RELOAD )
  201. {
  202. // Remove pointers to the selected items from containers without changing their
  203. // properties (as they are already deleted while a new sheet is loaded)
  204. m_selection.Clear();
  205. getView()->GetPainter()->GetSettings()->SetHighlight( false );
  206. LIB_EDIT_FRAME* libEditFrame = dynamic_cast<LIB_EDIT_FRAME*>( m_frame );
  207. LIB_VIEW_FRAME* libViewFrame = dynamic_cast<LIB_VIEW_FRAME*>( m_frame );
  208. if( libEditFrame )
  209. {
  210. m_isLibEdit = true;
  211. m_unit = libEditFrame->GetUnit();
  212. m_convert = libEditFrame->GetConvert();
  213. }
  214. else
  215. m_isLibView = libViewFrame != nullptr;
  216. }
  217. else
  218. // Restore previous properties of selected items and remove them from containers
  219. ClearSelection();
  220. // Reinsert the VIEW_GROUP, in case it was removed from the VIEW
  221. getView()->Remove( &m_selection );
  222. getView()->Add( &m_selection );
  223. }
  224. int EE_SELECTION_TOOL::UpdateMenu( const TOOL_EVENT& aEvent )
  225. {
  226. ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
  227. CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
  228. if( conditionalMenu )
  229. conditionalMenu->Evaluate( m_selection );
  230. if( actionMenu )
  231. actionMenu->UpdateAll();
  232. return 0;
  233. }
  234. int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
  235. {
  236. const KICAD_T movableItems[] =
  237. {
  238. SCH_MARKER_T,
  239. SCH_JUNCTION_T,
  240. SCH_NO_CONNECT_T,
  241. SCH_BUS_BUS_ENTRY_T,
  242. SCH_BUS_WIRE_ENTRY_T,
  243. SCH_LINE_T,
  244. SCH_BITMAP_T,
  245. SCH_TEXT_T,
  246. SCH_LABEL_T,
  247. SCH_GLOBAL_LABEL_T,
  248. SCH_HIER_LABEL_T,
  249. SCH_FIELD_T,
  250. SCH_COMPONENT_T,
  251. SCH_SHEET_PIN_T,
  252. SCH_SHEET_T,
  253. EOT
  254. };
  255. // Main loop: keep receiving events
  256. while( OPT_TOOL_EVENT evt = Wait() )
  257. {
  258. // Should selected items be added to the current selection or
  259. // become the new selection (discarding previously selected items)
  260. m_additive = evt->Modifier( MD_SHIFT );
  261. // Should selected items be REMOVED from the current selection?
  262. // This will be ignored if the SHIFT modifier is pressed
  263. m_subtractive = !m_additive && evt->Modifier( MD_CTRL );
  264. // Is the user requesting that the selection list include all possible
  265. // items without removing less likely selection candidates
  266. m_skip_heuristics = !!evt->Modifier( MD_ALT );
  267. // Single click? Select single object
  268. if( evt->IsClick( BUT_LEFT ) )
  269. {
  270. // JEY TODO: this is a hack, but I can't figure out why it's needed to
  271. // keep from getting the first click when running the Place Symbol tool.
  272. if( m_frame->GetToolId() != ID_NO_TOOL_SELECTED )
  273. continue;
  274. if( evt->Modifier( MD_CTRL ) && dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  275. {
  276. m_toolMgr->RunAction( EE_ACTIONS::highlightNet, true );
  277. }
  278. else
  279. {
  280. // If no modifier keys are pressed, clear the selection
  281. if( !m_additive )
  282. ClearSelection();
  283. SelectPoint( evt->Position());
  284. }
  285. }
  286. // right click? if there is any object - show the context menu
  287. else if( evt->IsClick( BUT_RIGHT ) )
  288. {
  289. bool selectionCancelled = false;
  290. if( m_selection.Empty() )
  291. {
  292. SelectPoint( evt->Position(), EE_COLLECTOR::AllItems, &selectionCancelled );
  293. m_selection.SetIsHover( true );
  294. }
  295. if( !selectionCancelled )
  296. m_menu.ShowContextMenu( m_selection );
  297. }
  298. // double click? Display the properties window
  299. else if( evt->IsDblClick( BUT_LEFT ) )
  300. {
  301. if( m_selection.Empty() )
  302. SelectPoint( evt->Position());
  303. EDA_ITEM* item = m_selection.Front();
  304. if( item && item->Type() == SCH_SHEET_T )
  305. m_toolMgr->RunAction( EE_ACTIONS::enterSheet );
  306. else
  307. m_toolMgr->RunAction( EE_ACTIONS::properties );
  308. }
  309. // drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
  310. else if( evt->IsDrag( BUT_LEFT ) )
  311. {
  312. bool empty = m_selection.Empty();
  313. // selection is empty? try to start dragging the item under the point where drag started
  314. if( empty )
  315. {
  316. m_selection = RequestSelection( movableItems );
  317. empty = m_selection.Empty();
  318. }
  319. // selection STILL empty? attempt a rectangle multi-selection
  320. if( m_additive || m_subtractive || empty || m_frame->GetDragAlwaysSelects() )
  321. {
  322. selectMultiple();
  323. }
  324. else
  325. {
  326. // Check if dragging has started within any of selected items bounding box
  327. if( selectionContains( evt->Position() ) )
  328. {
  329. // Yes -> run the move tool and wait till it finishes
  330. m_toolMgr->InvokeTool( "eeschema.InteractiveMove" );
  331. }
  332. else
  333. {
  334. // No -> clear the selection list
  335. ClearSelection();
  336. }
  337. }
  338. }
  339. // context sub-menu selection? Handle unit selection or bus unfolding
  340. else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CONTEXT_MENU_CHOICE )
  341. {
  342. if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
  343. && evt->GetCommandId().get() <= ID_POPUP_SCH_SELECT_UNIT_CMP_MAX )
  344. {
  345. SCH_COMPONENT* component = dynamic_cast<SCH_COMPONENT*>( m_selection.Front() );
  346. int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
  347. if( component )
  348. static_cast<SCH_EDIT_FRAME*>( m_frame )->SelectUnit( component, unit );
  349. }
  350. else if( evt->GetCommandId().get() >= ID_POPUP_SCH_UNFOLD_BUS
  351. && evt->GetCommandId().get() <= ID_POPUP_SCH_UNFOLD_BUS_END )
  352. {
  353. wxString* net = new wxString( *evt->Parameter<wxString*>() );
  354. m_toolMgr->RunAction( EE_ACTIONS::unfoldBus, true, net );
  355. }
  356. }
  357. else if( evt->IsAction( &ACTIONS::cancelInteractive ) || evt->IsCancel() )
  358. {
  359. ClearSelection();
  360. m_toolMgr->RunAction( EE_ACTIONS::clearHighlight, true );
  361. }
  362. else if( evt->Action() == TA_UNDO_REDO_PRE )
  363. {
  364. ClearSelection();
  365. }
  366. else if( evt->Action() == TA_CONTEXT_MENU_CLOSED )
  367. {
  368. m_menu.CloseContextMenu( evt );
  369. }
  370. }
  371. // This tool is supposed to be active forever
  372. assert( false );
  373. return 0;
  374. }
  375. SELECTION& EE_SELECTION_TOOL::GetSelection()
  376. {
  377. return m_selection;
  378. }
  379. EDA_ITEM* EE_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, const KICAD_T* aFilterList,
  380. bool* aSelectionCancelledFlag, bool aCheckLocked )
  381. {
  382. EDA_ITEM* start;
  383. EE_COLLECTOR collector;
  384. if( m_isLibEdit )
  385. start = static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart();
  386. else
  387. start = m_frame->GetScreen()->GetDrawItems();
  388. // Empty schematics have no draw items
  389. if( !start )
  390. return nullptr;
  391. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  392. collector.Collect( start, aFilterList, (wxPoint) aWhere, m_unit, m_convert );
  393. bool anyCollected = collector.GetCount() != 0;
  394. // Remove unselectable items
  395. for( int i = collector.GetCount() - 1; i >= 0; --i )
  396. {
  397. if( !selectable( collector[ i ] ) )
  398. collector.Remove( i );
  399. if( aCheckLocked && collector[ i ]->IsLocked() )
  400. collector.Remove( i );
  401. }
  402. m_selection.ClearReferencePoint();
  403. // Apply some ugly heuristics to avoid disambiguation menus whenever possible
  404. if( collector.GetCount() > 1 && !m_skip_heuristics )
  405. {
  406. guessSelectionCandidates( collector, aWhere );
  407. }
  408. // If still more than one item we're going to have to ask the user.
  409. if( collector.GetCount() > 1 )
  410. {
  411. collector.m_MenuTitle = _( "Clarify Selection" );
  412. // Must call selectionMenu via RunAction() to avoid event-loop contention
  413. m_toolMgr->RunAction( EE_ACTIONS::selectionMenu, true, &collector );
  414. if( collector.m_MenuCancelled )
  415. {
  416. if( aSelectionCancelledFlag )
  417. *aSelectionCancelledFlag = true;
  418. return nullptr;
  419. }
  420. }
  421. if( collector.GetCount() == 1 )
  422. {
  423. EDA_ITEM* item = collector[ 0 ];
  424. toggleSelection( item );
  425. return item;
  426. }
  427. if( !m_additive && anyCollected )
  428. ClearSelection();
  429. return nullptr;
  430. }
  431. void EE_SELECTION_TOOL::guessSelectionCandidates( EE_COLLECTOR& collector, const VECTOR2I& aPos )
  432. {
  433. // There are certain parent/child and enclosure combinations that can be handled
  434. // automatically.
  435. // Prefer a non-sheet to a sheet
  436. for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
  437. {
  438. EDA_ITEM* item = collector[ i ];
  439. EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
  440. if( item->Type() != SCH_SHEET_T && other->Type() == SCH_SHEET_T )
  441. collector.Remove( other );
  442. }
  443. // Prefer a symbol to a pin
  444. for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
  445. {
  446. EDA_ITEM* item = collector[ i ];
  447. EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
  448. if( item->Type() == SCH_COMPONENT_T && other->Type() == SCH_PIN_T )
  449. collector.Remove( other );
  450. }
  451. // Prefer an exact hit to a sloppy one
  452. for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
  453. {
  454. EDA_ITEM* item = collector[ i ];
  455. EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
  456. if( item->HitTest( (wxPoint) aPos, 0 ) && !other->HitTest( (wxPoint) aPos, 0 ) )
  457. collector.Remove( other );
  458. }
  459. // Prefer a field to a symbol
  460. for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
  461. {
  462. EDA_ITEM* item = collector[ i ];
  463. EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
  464. if( item->Type() == SCH_FIELD_T && other->Type() == SCH_COMPONENT_T )
  465. collector.Remove( other );
  466. }
  467. }
  468. SELECTION& EE_SELECTION_TOOL::RequestSelection( const KICAD_T aFilterList[] )
  469. {
  470. // Filter an existing selection
  471. if( !m_selection.Empty() )
  472. {
  473. for( int i = m_selection.GetSize() - 1; i >= 0; --i )
  474. {
  475. EDA_ITEM* item = (EDA_ITEM*) m_selection.GetItem( i );
  476. if( !item->IsType( aFilterList ) )
  477. toggleSelection( item );
  478. }
  479. }
  480. // If nothing was selected, or we filtered everything out, do a hover selection
  481. if( m_selection.Empty() )
  482. {
  483. VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
  484. ClearSelection();
  485. SelectPoint( cursorPos, aFilterList );
  486. m_selection.SetIsHover( true );
  487. m_selection.ClearReferencePoint();
  488. }
  489. VECTOR2I refP( 0, 0 );
  490. if( m_selection.Size() > 0 )
  491. {
  492. if( m_isLibEdit )
  493. refP = static_cast<LIB_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
  494. else
  495. refP = static_cast<SCH_ITEM*>( m_selection.GetTopLeftItem() )->GetPosition();
  496. }
  497. m_selection.SetReferencePoint( refP );
  498. return m_selection;
  499. }
  500. bool EE_SELECTION_TOOL::selectMultiple()
  501. {
  502. bool cancelled = false; // Was the tool cancelled while it was running?
  503. m_multiple = true; // Multiple selection mode is active
  504. KIGFX::VIEW* view = getView();
  505. KIGFX::PREVIEW::SELECTION_AREA area;
  506. view->Add( &area );
  507. while( OPT_TOOL_EVENT evt = Wait() )
  508. {
  509. if( evt->IsAction( &ACTIONS::cancelInteractive ) || evt->IsActivate() || evt->IsCancel() )
  510. {
  511. cancelled = true;
  512. break;
  513. }
  514. if( evt->IsDrag( BUT_LEFT ) )
  515. {
  516. // Start drawing a selection box
  517. area.SetOrigin( evt->DragOrigin() );
  518. area.SetEnd( evt->Position() );
  519. area.SetAdditive( m_additive );
  520. area.SetSubtractive( m_subtractive );
  521. view->SetVisible( &area, true );
  522. view->Update( &area );
  523. getViewControls()->SetAutoPan( true );
  524. }
  525. if( evt->IsMouseUp( BUT_LEFT ) )
  526. {
  527. getViewControls()->SetAutoPan( false );
  528. // End drawing the selection box
  529. view->SetVisible( &area, false );
  530. // Mark items within the selection box as selected
  531. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
  532. // Filter the view items based on the selection box
  533. BOX2I selectionBox = area.ViewBBox();
  534. view->Query( selectionBox, selectedItems ); // Get the list of selected items
  535. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
  536. int width = area.GetEnd().x - area.GetOrigin().x;
  537. int height = area.GetEnd().y - area.GetOrigin().y;
  538. /* Selection mode depends on direction of drag-selection:
  539. * Left > Right : Select objects that are fully enclosed by selection
  540. * Right > Left : Select objects that are crossed by selection
  541. */
  542. bool windowSelection = width >= 0 ? true : false;
  543. if( view->IsMirroredX() )
  544. windowSelection = !windowSelection;
  545. // Construct an EDA_RECT to determine EDA_ITEM selection
  546. EDA_RECT selectionRect( (wxPoint)area.GetOrigin(), wxSize( width, height ) );
  547. selectionRect.Normalize();
  548. for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
  549. {
  550. EDA_ITEM* item = static_cast<EDA_ITEM*>( it->first );
  551. if( !item || !selectable( item ) )
  552. continue;
  553. if( item->HitTest( selectionRect, windowSelection ) )
  554. {
  555. if( m_subtractive )
  556. unselect( item );
  557. else
  558. select( item );
  559. }
  560. }
  561. // Inform other potentially interested tools
  562. if( !m_selection.Empty() )
  563. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  564. break; // Stop waiting for events
  565. }
  566. }
  567. getViewControls()->SetAutoPan( false );
  568. // Stop drawing the selection box
  569. view->Remove( &area );
  570. m_multiple = false; // Multiple selection mode is inactive
  571. if( !cancelled )
  572. m_selection.ClearReferencePoint();
  573. return cancelled;
  574. }
  575. static KICAD_T nodeTypes[] =
  576. {
  577. SCH_LINE_LOCATE_WIRE_T,
  578. SCH_LINE_LOCATE_BUS_T,
  579. SCH_BUS_WIRE_ENTRY_T,
  580. SCH_BUS_BUS_ENTRY_T,
  581. SCH_LABEL_T,
  582. SCH_HIER_LABEL_T,
  583. SCH_GLOBAL_LABEL_T,
  584. SCH_SHEET_PIN_T,
  585. SCH_JUNCTION_T,
  586. EOT
  587. };
  588. EDA_ITEM* EE_SELECTION_TOOL::GetNode( VECTOR2I aPosition )
  589. {
  590. EE_COLLECTOR collector;
  591. int thresholdMax = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  592. for( int threshold : { 0, thresholdMax/2, thresholdMax } )
  593. {
  594. collector.m_Threshold = threshold;
  595. collector.Collect( m_frame->GetScreen()->GetDrawItems(), nodeTypes, (wxPoint) aPosition );
  596. if( collector.GetCount() > 0 )
  597. break;
  598. }
  599. return collector.GetCount() ? collector[ 0 ] : nullptr;
  600. }
  601. int EE_SELECTION_TOOL::SelectNode( const TOOL_EVENT& aEvent )
  602. {
  603. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !aEvent.Modifier( MD_ALT ) );
  604. SelectPoint( cursorPos, nodeTypes );
  605. return 0;
  606. }
  607. int EE_SELECTION_TOOL::SelectConnection( const TOOL_EVENT& aEvent )
  608. {
  609. static KICAD_T wiresAndBusses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
  610. RequestSelection( wiresAndBusses );
  611. if( m_selection.Empty() )
  612. return 0;
  613. SCH_LINE* line = (SCH_LINE*) m_selection.Front();
  614. EDA_ITEMS items;
  615. m_frame->GetScreen()->ClearDrawingState();
  616. m_frame->GetScreen()->MarkConnections( line );
  617. for( EDA_ITEM* item = m_frame->GetScreen()->GetDrawItems(); item; item = item->Next() )
  618. {
  619. if( item->GetFlags() & CANDIDATE )
  620. select( item );
  621. }
  622. if( m_selection.GetSize() > 1 )
  623. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  624. return 0;
  625. }
  626. int EE_SELECTION_TOOL::AddItemToSel( const TOOL_EVENT& aEvent )
  627. {
  628. AddItemToSel( aEvent.Parameter<EDA_ITEM*>() );
  629. return 0;
  630. }
  631. void EE_SELECTION_TOOL::AddItemToSel( EDA_ITEM* aItem, bool aQuietMode )
  632. {
  633. if( aItem )
  634. {
  635. select( aItem );
  636. // Inform other potentially interested tools
  637. if( !aQuietMode )
  638. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  639. }
  640. }
  641. int EE_SELECTION_TOOL::AddItemsToSel( const TOOL_EVENT& aEvent )
  642. {
  643. AddItemsToSel( aEvent.Parameter<EDA_ITEMS*>(), false );
  644. return 0;
  645. }
  646. void EE_SELECTION_TOOL::AddItemsToSel( EDA_ITEMS* aList, bool aQuietMode )
  647. {
  648. if( aList )
  649. {
  650. for( EDA_ITEM* item : *aList )
  651. select( item );
  652. // Inform other potentially interested tools
  653. if( !aQuietMode )
  654. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  655. }
  656. }
  657. int EE_SELECTION_TOOL::RemoveItemFromSel( const TOOL_EVENT& aEvent )
  658. {
  659. RemoveItemFromSel( aEvent.Parameter<EDA_ITEM*>() );
  660. return 0;
  661. }
  662. void EE_SELECTION_TOOL::RemoveItemFromSel( EDA_ITEM* aItem, bool aQuietMode )
  663. {
  664. if( aItem )
  665. {
  666. unselect( aItem );
  667. // Inform other potentially interested tools
  668. if( !aQuietMode )
  669. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  670. }
  671. }
  672. int EE_SELECTION_TOOL::RemoveItemsFromSel( const TOOL_EVENT& aEvent )
  673. {
  674. RemoveItemsFromSel( aEvent.Parameter<EDA_ITEMS*>(), false );
  675. return 0;
  676. }
  677. void EE_SELECTION_TOOL::RemoveItemsFromSel( EDA_ITEMS* aList, bool aQuietMode )
  678. {
  679. if( aList )
  680. {
  681. for( EDA_ITEM* item : *aList )
  682. unselect( item );
  683. // Inform other potentially interested tools
  684. if( !aQuietMode )
  685. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  686. }
  687. }
  688. void EE_SELECTION_TOOL::BrightenItem( EDA_ITEM* aItem )
  689. {
  690. highlight( aItem, BRIGHTENED );
  691. }
  692. void EE_SELECTION_TOOL::UnbrightenItem( EDA_ITEM* aItem )
  693. {
  694. unhighlight( aItem, BRIGHTENED );
  695. }
  696. int EE_SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
  697. {
  698. ClearSelection();
  699. return 0;
  700. }
  701. void EE_SELECTION_TOOL::RebuildSelection()
  702. {
  703. m_selection.Clear();
  704. EDA_ITEM* start = nullptr;
  705. if( m_isLibEdit )
  706. start = static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart();
  707. else
  708. start = m_frame->GetScreen()->GetDrawItems();
  709. INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData )
  710. {
  711. if( item->IsSelected() )
  712. select( item );
  713. return SEARCH_CONTINUE;
  714. };
  715. EDA_ITEM::IterateForward( start, inspector, nullptr, EE_COLLECTOR::AllItems );
  716. }
  717. int EE_SELECTION_TOOL::SelectionMenu( const TOOL_EVENT& aEvent )
  718. {
  719. EE_COLLECTOR* collector = aEvent.Parameter<EE_COLLECTOR*>();
  720. if( !doSelectionMenu( collector ) )
  721. collector->m_MenuCancelled = true;
  722. return 0;
  723. }
  724. bool EE_SELECTION_TOOL::doSelectionMenu( EE_COLLECTOR* aCollector )
  725. {
  726. EDA_ITEM* current = nullptr;
  727. ACTION_MENU menu;
  728. int limit = std::min( MAX_SELECT_ITEM_IDS, aCollector->GetCount() );
  729. for( int i = 0; i < limit; ++i )
  730. {
  731. wxString text;
  732. EDA_ITEM* item = ( *aCollector )[i];
  733. text = item->GetSelectMenuText( m_frame->GetUserUnits() );
  734. wxString menuText = wxString::Format("&%d. %s", i + 1, text );
  735. menu.Add( menuText, i + 1, item->GetMenuImage() );
  736. }
  737. if( aCollector->m_MenuTitle.Length() )
  738. menu.SetTitle( aCollector->m_MenuTitle );
  739. menu.SetIcon( info_xpm );
  740. menu.DisplayTitle( true );
  741. SetContextMenu( &menu, CMENU_NOW );
  742. while( OPT_TOOL_EVENT evt = Wait() )
  743. {
  744. if( evt->Action() == TA_CONTEXT_MENU_UPDATE )
  745. {
  746. if( current )
  747. unhighlight( current, BRIGHTENED );
  748. int id = *evt->GetCommandId();
  749. // User has pointed an item, so show it in a different way
  750. if( id > 0 && id <= limit )
  751. {
  752. current = ( *aCollector )[id - 1];
  753. highlight( current, BRIGHTENED );
  754. }
  755. else
  756. {
  757. current = NULL;
  758. }
  759. }
  760. else if( evt->Action() == TA_CONTEXT_MENU_CHOICE )
  761. {
  762. if( current )
  763. unhighlight( current, BRIGHTENED );
  764. OPT<int> id = evt->GetCommandId();
  765. // User has selected an item, so this one will be returned
  766. if( id && ( *id > 0 ) )
  767. current = ( *aCollector )[*id - 1];
  768. else
  769. current = NULL;
  770. break;
  771. }
  772. getView()->UpdateItems();
  773. m_frame->GetCanvas()->Refresh();
  774. }
  775. if( current )
  776. {
  777. unhighlight( current, BRIGHTENED );
  778. getView()->UpdateItems();
  779. m_frame->GetCanvas()->Refresh();
  780. aCollector->Empty();
  781. aCollector->Append( current );
  782. return true;
  783. }
  784. return false;
  785. }
  786. bool EE_SELECTION_TOOL::selectable( const EDA_ITEM* aItem, bool checkVisibilityOnly ) const
  787. {
  788. // NOTE: in the future this is where eeschema layer/itemtype visibility will be handled
  789. switch( aItem->Type() )
  790. {
  791. case SCH_PIN_T:
  792. if( !static_cast<const SCH_PIN*>( aItem )->IsVisible() && !m_frame->GetShowAllPins() )
  793. return false;
  794. break;
  795. case LIB_PART_T: // In libedit we do not want to select the symbol itself.
  796. return false;
  797. case LIB_PIN_T:
  798. {
  799. LIB_EDIT_FRAME* editFrame = (LIB_EDIT_FRAME*) m_frame;
  800. LIB_PIN* pin = (LIB_PIN*) aItem;
  801. if( ( pin->GetUnit() && pin->GetUnit() != editFrame->GetUnit() )
  802. || ( pin->GetConvert() && pin->GetConvert() != editFrame->GetConvert() ) )
  803. {
  804. // Specific rules for pins:
  805. // - do not select pins in other units when synchronized pin edit mode is disabled
  806. // - do not select pins in other units when units are not interchangeable
  807. // - in other cases verify if the pin belongs to the requested DeMorgan variant
  808. if( !editFrame->SynchronizePins()
  809. || editFrame->GetCurPart()->UnitsLocked()
  810. || ( pin->GetConvert() && pin->GetConvert() != editFrame->GetConvert() ) )
  811. {
  812. return false;
  813. }
  814. }
  815. break;
  816. }
  817. case SCH_MARKER_T: // Always selectable
  818. return true;
  819. default: // Suppress warnings
  820. break;
  821. }
  822. return true;
  823. }
  824. void EE_SELECTION_TOOL::ClearSelection()
  825. {
  826. if( m_selection.Empty() )
  827. return;
  828. while( m_selection.GetSize() )
  829. unhighlight( (EDA_ITEM*) m_selection.Front(), SELECTED, &m_selection );
  830. getView()->Update( &m_selection );
  831. m_selection.SetIsHover( false );
  832. m_selection.ClearReferencePoint();
  833. // Inform other potentially interested tools
  834. m_toolMgr->ProcessEvent( EVENTS::ClearedEvent );
  835. }
  836. void EE_SELECTION_TOOL::toggleSelection( EDA_ITEM* aItem, bool aForce )
  837. {
  838. if( aItem->IsSelected() )
  839. {
  840. unselect( aItem );
  841. // Inform other potentially interested tools
  842. m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
  843. }
  844. else
  845. {
  846. if( !m_additive )
  847. ClearSelection();
  848. // Prevent selection of invisible or inactive items
  849. if( aForce || selectable( aItem ) )
  850. {
  851. select( aItem );
  852. // Inform other potentially interested tools
  853. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  854. }
  855. }
  856. if( m_frame )
  857. m_frame->GetGalCanvas()->ForceRefresh();
  858. }
  859. void EE_SELECTION_TOOL::select( EDA_ITEM* aItem )
  860. {
  861. highlight( aItem, SELECTED, &m_selection );
  862. }
  863. void EE_SELECTION_TOOL::unselect( EDA_ITEM* aItem )
  864. {
  865. unhighlight( aItem, SELECTED, &m_selection );
  866. }
  867. void EE_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
  868. {
  869. KICAD_T itemType = aItem->Type();
  870. if( aMode == SELECTED )
  871. aItem->SetSelected();
  872. else if( aMode == BRIGHTENED )
  873. aItem->SetBrightened();
  874. if( aGroup )
  875. aGroup->Add( aItem );
  876. // Highlight pins and fields. (All the other component children are currently only
  877. // represented in the LIB_PART and will inherit the settings of the parent component.)
  878. if( itemType == SCH_COMPONENT_T )
  879. {
  880. SCH_PINS& pins = static_cast<SCH_COMPONENT*>( aItem )->GetPins();
  881. for( SCH_PIN& pin : pins )
  882. {
  883. if( aMode == SELECTED )
  884. pin.SetSelected();
  885. else if( aMode == BRIGHTENED )
  886. pin.SetBrightened();
  887. }
  888. std::vector<SCH_FIELD*> fields;
  889. static_cast<SCH_COMPONENT*>( aItem )->GetFields( fields, false );
  890. for( SCH_FIELD* field : fields )
  891. {
  892. if( aMode == SELECTED )
  893. field->SetSelected();
  894. else if( aMode == BRIGHTENED )
  895. field->SetBrightened();
  896. }
  897. }
  898. else if( itemType == SCH_SHEET_T )
  899. {
  900. SCH_SHEET_PINS& pins = static_cast<SCH_SHEET*>( aItem )->GetPins();
  901. for( SCH_SHEET_PIN& pin : pins )
  902. {
  903. if( aMode == SELECTED )
  904. pin.SetSelected();
  905. else if( aMode == BRIGHTENED )
  906. pin.SetBrightened();
  907. }
  908. }
  909. if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
  910. getView()->Update( aItem->GetParent() );
  911. else
  912. getView()->Update( aItem );
  913. }
  914. void EE_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
  915. {
  916. KICAD_T itemType = aItem->Type();
  917. if( aMode == SELECTED )
  918. aItem->ClearSelected();
  919. else if( aMode == BRIGHTENED )
  920. aItem->ClearBrightened();
  921. if( aGroup )
  922. aGroup->Remove( aItem );
  923. // Unhighlight pins and fields. (All the other component children are currently only
  924. // represented in the LIB_PART.)
  925. if( itemType == SCH_COMPONENT_T )
  926. {
  927. SCH_PINS& pins = static_cast<SCH_COMPONENT*>( aItem )->GetPins();
  928. for( SCH_PIN& pin : pins )
  929. {
  930. if( aMode == SELECTED )
  931. pin.ClearSelected();
  932. else if( aMode == BRIGHTENED )
  933. pin.ClearBrightened();
  934. }
  935. std::vector<SCH_FIELD*> fields;
  936. static_cast<SCH_COMPONENT*>( aItem )->GetFields( fields, false );
  937. for( SCH_FIELD* field : fields )
  938. {
  939. if( aMode == SELECTED )
  940. field->ClearSelected();
  941. else if( aMode == BRIGHTENED )
  942. field->ClearBrightened();
  943. }
  944. }
  945. else if( itemType == SCH_SHEET_T )
  946. {
  947. SCH_SHEET_PINS& pins = static_cast<SCH_SHEET*>( aItem )->GetPins();
  948. for( SCH_SHEET_PIN& pin : pins )
  949. {
  950. if( aMode == SELECTED )
  951. pin.ClearSelected();
  952. else if( aMode == BRIGHTENED )
  953. pin.ClearBrightened();
  954. }
  955. }
  956. if( itemType == SCH_PIN_T || itemType == SCH_FIELD_T || itemType == SCH_SHEET_PIN_T )
  957. getView()->Update( aItem->GetParent() );
  958. else
  959. getView()->Update( aItem );
  960. }
  961. bool EE_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
  962. {
  963. const unsigned GRIP_MARGIN = 20;
  964. VECTOR2I margin = getView()->ToWorld( VECTOR2I( GRIP_MARGIN, GRIP_MARGIN ), false );
  965. // Check if the point is located within any of the currently selected items bounding boxes
  966. for( auto item : m_selection )
  967. {
  968. BOX2I itemBox = item->ViewBBox();
  969. itemBox.Inflate( margin.x, margin.y ); // Give some margin for gripping an item
  970. if( itemBox.Contains( aPoint ) )
  971. return true;
  972. }
  973. return false;
  974. }
  975. void EE_SELECTION_TOOL::setTransitions()
  976. {
  977. Go( &EE_SELECTION_TOOL::UpdateMenu, ACTIONS::updateMenu.MakeEvent() );
  978. Go( &EE_SELECTION_TOOL::Main, EE_ACTIONS::selectionActivate.MakeEvent() );
  979. Go( &EE_SELECTION_TOOL::SelectNode, EE_ACTIONS::selectNode.MakeEvent() );
  980. Go( &EE_SELECTION_TOOL::SelectConnection, EE_ACTIONS::selectConnection.MakeEvent() );
  981. Go( &EE_SELECTION_TOOL::ClearSelection, EE_ACTIONS::clearSelection.MakeEvent() );
  982. Go( &EE_SELECTION_TOOL::AddItemToSel, EE_ACTIONS::addItemToSel.MakeEvent() );
  983. Go( &EE_SELECTION_TOOL::AddItemsToSel, EE_ACTIONS::addItemsToSel.MakeEvent() );
  984. Go( &EE_SELECTION_TOOL::RemoveItemFromSel, EE_ACTIONS::removeItemFromSel.MakeEvent() );
  985. Go( &EE_SELECTION_TOOL::RemoveItemsFromSel, EE_ACTIONS::removeItemsFromSel.MakeEvent() );
  986. Go( &EE_SELECTION_TOOL::SelectionMenu, EE_ACTIONS::selectionMenu.MakeEvent() );
  987. }