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.

921 lines
31 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 CERN
  5. * Copyright (C) 2019 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 "sch_drawing_tools.h"
  25. #include "ee_selection_tool.h"
  26. #include "ee_point_editor.h"
  27. #include <ee_actions.h>
  28. #include <sch_edit_frame.h>
  29. #include <sch_view.h>
  30. #include <class_draw_panel_gal.h>
  31. #include <project.h>
  32. #include <id.h>
  33. #include <eeschema_id.h>
  34. #include <confirm.h>
  35. #include <view/view_controls.h>
  36. #include <view/view.h>
  37. #include <sch_component.h>
  38. #include <sch_no_connect.h>
  39. #include <sch_line.h>
  40. #include <sch_junction.h>
  41. #include <sch_bus_entry.h>
  42. #include <sch_text.h>
  43. #include <sch_sheet.h>
  44. #include <sch_bitmap.h>
  45. #include <class_library.h>
  46. SCH_DRAWING_TOOLS::SCH_DRAWING_TOOLS() :
  47. EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveDrawing" )
  48. {
  49. }
  50. bool SCH_DRAWING_TOOLS::Init()
  51. {
  52. EE_TOOL_BASE::Init();
  53. auto belowRootSheetCondition = [] ( const SELECTION& aSel ) {
  54. return g_CurrentSheet->Last() != g_RootSheet;
  55. };
  56. auto& ctxMenu = m_menu.GetMenu();
  57. ctxMenu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 2 );
  58. return true;
  59. }
  60. // History lists for PlaceComponent()
  61. static SCH_BASE_FRAME::HISTORY_LIST s_SymbolHistoryList;
  62. static SCH_BASE_FRAME::HISTORY_LIST s_PowerHistoryList;
  63. int SCH_DRAWING_TOOLS::PlaceComponent( const TOOL_EVENT& aEvent )
  64. {
  65. SCH_COMPONENT* component = aEvent.Parameter<SCH_COMPONENT*>();
  66. SCHLIB_FILTER filter;
  67. SCH_BASE_FRAME::HISTORY_LIST* historyList = nullptr;
  68. if( aEvent.IsAction( &EE_ACTIONS::placeSymbol ) )
  69. historyList = &s_SymbolHistoryList;
  70. else if (aEvent.IsAction( &EE_ACTIONS::placePower ) )
  71. {
  72. historyList = &s_PowerHistoryList;
  73. filter.FilterPowerParts( true );
  74. }
  75. else
  76. wxFAIL_MSG( "PlaceCompontent(): unexpected request" );
  77. getViewControls()->ShowCursor( true );
  78. // If a component was passed in get it ready for placement.
  79. if( component )
  80. {
  81. component->SetFlags( IS_NEW | IS_MOVED );
  82. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  83. m_selectionTool->AddItemToSel( component );
  84. }
  85. std::string tool = aEvent.GetCommandStr().get();
  86. m_frame->PushTool( tool );
  87. Activate();
  88. // Prime the pump
  89. if( component )
  90. {
  91. getViewControls()->WarpCursor( getViewControls()->GetMousePosition( false ) );
  92. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  93. }
  94. else if( aEvent.HasPosition() )
  95. m_toolMgr->RunAction( EE_ACTIONS::cursorClick );
  96. // Main loop: keep receiving events
  97. while( TOOL_EVENT* evt = Wait() )
  98. {
  99. m_frame->GetCanvas()->SetCurrentCursor( component ? wxCURSOR_ARROW : wxCURSOR_PENCIL );
  100. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  101. auto cleanup = [&] () {
  102. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  103. m_view->ClearPreview();
  104. delete component;
  105. component = nullptr;
  106. };
  107. if( evt->IsCancelInteractive() )
  108. {
  109. if( component )
  110. cleanup();
  111. else
  112. {
  113. m_frame->PopTool( tool );
  114. break;
  115. }
  116. }
  117. else if( evt->IsActivate() )
  118. {
  119. if( component )
  120. cleanup();
  121. if( evt->IsMoveTool() )
  122. {
  123. // leave ourselves on the stack so we come back after the move
  124. break;
  125. }
  126. else
  127. {
  128. m_frame->PopTool( tool );
  129. break;
  130. }
  131. }
  132. else if( evt->IsClick( BUT_LEFT ) )
  133. {
  134. if( !component )
  135. {
  136. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  137. // Pick the module to be placed
  138. auto sel = m_frame->SelectCompFromLibTree( &filter, *historyList, true, 1, 1,
  139. m_frame->GetShowFootprintPreviews());
  140. // Restore cursor after dialog
  141. getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
  142. LIB_PART* part = sel.LibId.IsValid() ? m_frame->GetLibPart( sel.LibId ) : nullptr;
  143. if( !part )
  144. continue;
  145. component = new SCH_COMPONENT( *part, g_CurrentSheet, sel, (wxPoint) cursorPos );
  146. component->SetFlags( IS_NEW | IS_MOVED );
  147. // Be sure the link to the corresponding LIB_PART is OK:
  148. component->Resolve( *m_frame->Prj().SchSymbolLibTable() );
  149. if( m_frame->GetAutoplaceFields() )
  150. component->AutoplaceFields( /* aScreen */ NULL, /* aManual */ false );
  151. m_frame->SaveCopyForRepeatItem( component );
  152. m_view->ClearPreview();
  153. m_view->AddToPreview( component->Clone() );
  154. m_selectionTool->AddItemToSel( component );
  155. }
  156. else
  157. {
  158. SCH_COMPONENT* next_comp = nullptr;
  159. m_view->ClearPreview();
  160. m_frame->AddItemToScreenAndUndoList( component );
  161. EE_SELECTION new_sel;
  162. new_sel.Add( component );
  163. m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &new_sel );
  164. m_frame->OnModify();
  165. if( m_frame->GetUseAllUnits() || m_frame->GetRepeatComponent() )
  166. {
  167. int new_unit = component->GetUnit();
  168. if( m_frame->GetUseAllUnits()
  169. && component->GetUnit() < component->GetUnitCount() )
  170. new_unit++;
  171. else
  172. new_unit = 1;
  173. // We are either stepping to the next unit or next component
  174. if( m_frame->GetRepeatComponent() || new_unit > 1 )
  175. {
  176. next_comp = static_cast<SCH_COMPONENT*>( component->Duplicate() );
  177. next_comp->SetFlags( IS_NEW | IS_MOVED );
  178. next_comp->SetUnit( new_unit );
  179. next_comp->SetUnitSelection( g_CurrentSheet, new_unit );
  180. if( m_frame->GetAutoplaceFields() )
  181. component->AutoplaceFields( /* aScreen */ NULL, /* aManual */ false );
  182. m_frame->SaveCopyForRepeatItem( next_comp );
  183. m_view->AddToPreview( next_comp->Clone() );
  184. m_selectionTool->AddItemToSel( next_comp );
  185. }
  186. }
  187. component = next_comp;
  188. }
  189. }
  190. else if( evt->IsClick( BUT_RIGHT ) )
  191. {
  192. // Warp after context menu only if dragging...
  193. if( !component )
  194. m_toolMgr->VetoContextMenuMouseWarp();
  195. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  196. }
  197. else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
  198. {
  199. if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
  200. && evt->GetCommandId().get() <= ID_POPUP_SCH_SELECT_UNIT_CMP_MAX )
  201. {
  202. int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
  203. if( component )
  204. {
  205. m_frame->SelectUnit( component, unit );
  206. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  207. }
  208. }
  209. }
  210. else if( component && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  211. {
  212. component->SetPosition( (wxPoint)cursorPos );
  213. m_view->ClearPreview();
  214. m_view->AddToPreview( component->Clone() );
  215. }
  216. else
  217. evt->SetPassEvent();
  218. // Enable autopanning and cursor capture only when there is a module to be placed
  219. getViewControls()->SetAutoPan( component != nullptr );
  220. getViewControls()->CaptureCursor( component != nullptr );
  221. }
  222. return 0;
  223. }
  224. int SCH_DRAWING_TOOLS::PlaceImage( const TOOL_EVENT& aEvent )
  225. {
  226. SCH_BITMAP* image = aEvent.Parameter<SCH_BITMAP*>();
  227. bool immediateMode = image;
  228. VECTOR2I cursorPos = getViewControls()->GetCursorPosition();
  229. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  230. getViewControls()->ShowCursor( true );
  231. // Add all the drawable parts to preview
  232. if( image )
  233. {
  234. image->SetPosition( (wxPoint)cursorPos );
  235. m_view->ClearPreview();
  236. m_view->AddToPreview( image->Clone() );
  237. }
  238. std::string tool = aEvent.GetCommandStr().get();
  239. m_frame->PushTool( tool );
  240. Activate();
  241. // Prime the pump
  242. if( image )
  243. m_toolMgr->RunAction( ACTIONS::refreshPreview );
  244. else if( aEvent.HasPosition() )
  245. m_toolMgr->RunAction( ACTIONS::cursorClick );
  246. // Main loop: keep receiving events
  247. while( TOOL_EVENT* evt = Wait() )
  248. {
  249. m_frame->GetCanvas()->SetCurrentCursor( image ? wxCURSOR_ARROW : wxCURSOR_PENCIL );
  250. cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  251. auto cleanup = [&] () {
  252. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  253. m_view->ClearPreview();
  254. delete image;
  255. image = nullptr;
  256. };
  257. if( evt->IsCancelInteractive() )
  258. {
  259. if( image )
  260. cleanup();
  261. else
  262. {
  263. m_frame->PopTool( tool );
  264. break;
  265. }
  266. if( immediateMode )
  267. {
  268. m_frame->PopTool( tool );
  269. break;
  270. }
  271. }
  272. else if( evt->IsActivate() )
  273. {
  274. if( image )
  275. cleanup();
  276. if( evt->IsMoveTool() )
  277. {
  278. // leave ourselves on the stack so we come back after the move
  279. break;
  280. }
  281. else
  282. {
  283. m_frame->PopTool( tool );
  284. break;
  285. }
  286. }
  287. else if( evt->IsClick( BUT_LEFT ) )
  288. {
  289. if( !image )
  290. {
  291. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  292. wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
  293. _( "Image Files " ) + wxImage::GetImageExtWildcard(), wxFD_OPEN );
  294. if( dlg.ShowModal() != wxID_OK )
  295. continue;
  296. // Restore cursor after dialog
  297. getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
  298. wxString fullFilename = dlg.GetPath();
  299. if( wxFileExists( fullFilename ) )
  300. image = new SCH_BITMAP( (wxPoint)cursorPos );
  301. if( !image || !image->ReadImageFile( fullFilename ) )
  302. {
  303. wxMessageBox( _( "Couldn't load image from \"%s\"" ), fullFilename );
  304. delete image;
  305. image = nullptr;
  306. continue;
  307. }
  308. image->SetFlags( IS_NEW | IS_MOVED );
  309. m_frame->SaveCopyForRepeatItem( image );
  310. m_view->ClearPreview();
  311. m_view->AddToPreview( image->Clone() );
  312. m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
  313. m_selectionTool->AddItemToSel( image );
  314. getViewControls()->SetCursorPosition( cursorPos, false );
  315. }
  316. else
  317. {
  318. m_frame->AddItemToScreenAndUndoList( image );
  319. image = nullptr;
  320. m_toolMgr->RunAction( ACTIONS::activatePointEditor );
  321. m_view->ClearPreview();
  322. if( immediateMode )
  323. {
  324. m_frame->PopTool( tool );
  325. break;
  326. }
  327. }
  328. }
  329. else if( evt->IsClick( BUT_RIGHT ) )
  330. {
  331. // Warp after context menu only if dragging...
  332. if( !image )
  333. m_toolMgr->VetoContextMenuMouseWarp();
  334. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  335. }
  336. else if( image && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  337. {
  338. image->SetPosition( (wxPoint)cursorPos );
  339. m_view->ClearPreview();
  340. m_view->AddToPreview( image->Clone() );
  341. m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
  342. }
  343. else
  344. evt->SetPassEvent();
  345. // Enable autopanning and cursor capture only when there is a module to be placed
  346. getViewControls()->SetAutoPan( image != nullptr );
  347. getViewControls()->CaptureCursor( image != nullptr );
  348. }
  349. return 0;
  350. }
  351. int SCH_DRAWING_TOOLS::SingleClickPlace( const TOOL_EVENT& aEvent )
  352. {
  353. wxPoint cursorPos;
  354. KICAD_T type = aEvent.Parameter<KICAD_T>();
  355. if( type == SCH_JUNCTION_T && aEvent.HasPosition() )
  356. {
  357. EE_SELECTION& selection = m_selectionTool->GetSelection();
  358. SCH_LINE* wire = dynamic_cast<SCH_LINE*>( selection.Front() );
  359. if( wire )
  360. {
  361. SEG seg( wire->GetStartPoint(), wire->GetEndPoint() );
  362. VECTOR2I nearest = seg.NearestPoint( getViewControls()->GetCursorPosition() );
  363. getViewControls()->SetCrossHairCursorPosition( nearest, false );
  364. getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
  365. }
  366. }
  367. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  368. getViewControls()->ShowCursor( true );
  369. getViewControls()->SetSnapping( true );
  370. SCH_ITEM* previewItem;
  371. switch( type )
  372. {
  373. case SCH_NO_CONNECT_T:
  374. previewItem = new SCH_NO_CONNECT( cursorPos );
  375. break;
  376. case SCH_JUNCTION_T:
  377. previewItem = new SCH_JUNCTION( cursorPos );
  378. break;
  379. case SCH_BUS_WIRE_ENTRY_T:
  380. previewItem = new SCH_BUS_WIRE_ENTRY( cursorPos, g_lastBusEntryShape );
  381. break;
  382. case SCH_BUS_BUS_ENTRY_T:
  383. previewItem = new SCH_BUS_BUS_ENTRY( cursorPos, g_lastBusEntryShape );
  384. break;
  385. default:
  386. wxASSERT_MSG( false, "Unknown item type in SCH_DRAWING_TOOLS::SingleClickPlace" );
  387. return 0;
  388. }
  389. m_view->ClearPreview();
  390. m_view->AddToPreview( previewItem->Clone() );
  391. std::string tool = aEvent.GetCommandStr().get();
  392. m_frame->PushTool( tool );
  393. Activate();
  394. // Prime the pump
  395. if( aEvent.HasPosition() )
  396. m_toolMgr->RunAction( ACTIONS::cursorClick );
  397. // Main loop: keep receiving events
  398. while( TOOL_EVENT* evt = Wait() )
  399. {
  400. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
  401. cursorPos = (wxPoint) getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  402. if( evt->IsCancelInteractive() )
  403. {
  404. m_frame->PopTool( tool );
  405. break;
  406. }
  407. else if( evt->IsActivate() )
  408. {
  409. if( evt->IsMoveTool() )
  410. {
  411. // leave ourselves on the stack so we come back after the move
  412. break;
  413. }
  414. else
  415. {
  416. m_frame->PopTool( tool );
  417. break;
  418. }
  419. }
  420. else if( evt->IsClick( BUT_LEFT ) )
  421. {
  422. if( !m_frame->GetScreen()->GetItem( cursorPos, 0, type ) )
  423. {
  424. if( type == SCH_JUNCTION_T )
  425. m_frame->AddJunction( cursorPos );
  426. else
  427. {
  428. SCH_ITEM* newItem = static_cast<SCH_ITEM*>( previewItem->Clone() );
  429. newItem->SetPosition( cursorPos );
  430. newItem->SetFlags( IS_NEW );
  431. m_frame->AddItemToScreenAndUndoList( newItem );
  432. m_frame->SaveCopyForRepeatItem( newItem );
  433. m_frame->SchematicCleanUp();
  434. m_frame->TestDanglingEnds();
  435. m_frame->OnModify();
  436. }
  437. }
  438. }
  439. else if( evt->IsClick( BUT_RIGHT ) )
  440. {
  441. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  442. }
  443. else if( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() )
  444. {
  445. previewItem->SetPosition( (wxPoint)cursorPos );
  446. m_view->ClearPreview();
  447. m_view->AddToPreview( previewItem->Clone() );
  448. }
  449. else if( evt->Category() == TC_COMMAND )
  450. {
  451. if( ( type == SCH_BUS_BUS_ENTRY_T || type == SCH_BUS_WIRE_ENTRY_T )
  452. && ( evt->IsAction( &EE_ACTIONS::rotateCW )
  453. || evt->IsAction( &EE_ACTIONS::rotateCCW )
  454. || evt->IsAction( &EE_ACTIONS::mirrorX )
  455. || evt->IsAction( &EE_ACTIONS::mirrorY )
  456. || evt->IsAction( &EE_ACTIONS::toShapeBackslash )
  457. || evt->IsAction( &EE_ACTIONS::toShapeSlash ) ) )
  458. {
  459. // Update the shape of the bus entry
  460. if( evt->IsAction( &EE_ACTIONS::toShapeSlash ) )
  461. g_lastBusEntryShape = '/';
  462. else if( evt->IsAction( &EE_ACTIONS::toShapeBackslash ) )
  463. g_lastBusEntryShape = '\\';
  464. SCH_BUS_ENTRY_BASE* busItem = static_cast<SCH_BUS_ENTRY_BASE*>( previewItem );
  465. // The bus entries only rotate in one direction
  466. if( evt->IsAction( &EE_ACTIONS::rotateCW )
  467. || evt->IsAction( &EE_ACTIONS::rotateCCW ) )
  468. busItem->Rotate( busItem->GetPosition() );
  469. else if( evt->IsAction( &EE_ACTIONS::mirrorX ) )
  470. busItem->MirrorX( busItem->GetPosition().x );
  471. else if( evt->IsAction( &EE_ACTIONS::mirrorY ) )
  472. busItem->MirrorY( busItem->GetPosition().y );
  473. else if( evt->IsAction( &EE_ACTIONS::toShapeBackslash )
  474. || evt->IsAction( &EE_ACTIONS::toShapeSlash ) )
  475. busItem->SetBusEntryShape( g_lastBusEntryShape );
  476. m_view->ClearPreview();
  477. m_view->AddToPreview( previewItem->Clone() );
  478. }
  479. }
  480. else
  481. evt->SetPassEvent();
  482. }
  483. delete previewItem;
  484. m_view->ClearPreview();
  485. return 0;
  486. }
  487. int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
  488. {
  489. EDA_ITEM* item = nullptr;
  490. bool importMode = aEvent.IsAction( &EE_ACTIONS::importSheetPin );
  491. KICAD_T type = aEvent.Parameter<KICAD_T>();
  492. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  493. getViewControls()->ShowCursor( true );
  494. std::string tool = aEvent.GetCommandStr().get();
  495. m_frame->PushTool( tool );
  496. Activate();
  497. // Prime the pump
  498. if( aEvent.HasPosition() )
  499. m_toolMgr->RunAction( ACTIONS::cursorClick );
  500. // Main loop: keep receiving events
  501. while( TOOL_EVENT* evt = Wait() )
  502. {
  503. m_frame->GetCanvas()->SetCurrentCursor( item ? wxCURSOR_ARROW : wxCURSOR_PENCIL );
  504. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  505. auto cleanup = [&] () {
  506. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  507. m_view->ClearPreview();
  508. delete item;
  509. item = nullptr;
  510. };
  511. if( evt->IsCancelInteractive() )
  512. {
  513. if( item )
  514. cleanup();
  515. else
  516. {
  517. m_frame->PopTool( tool );
  518. break;
  519. }
  520. }
  521. else if( evt->IsActivate() )
  522. {
  523. if( item )
  524. cleanup();
  525. if( evt->IsPointEditor() )
  526. {
  527. // don't exit (the point editor runs in the background)
  528. }
  529. else if( evt->IsMoveTool() )
  530. {
  531. // leave ourselves on the stack so we come back after the move
  532. break;
  533. }
  534. else
  535. {
  536. m_frame->PopTool( tool );
  537. break;
  538. }
  539. }
  540. else if( evt->IsClick( BUT_LEFT ) )
  541. {
  542. // First click creates...
  543. if( !item )
  544. {
  545. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  546. switch( type )
  547. {
  548. case SCH_LABEL_T:
  549. item = m_frame->CreateNewText( LAYER_LOCLABEL );
  550. break;
  551. case SCH_HIER_LABEL_T:
  552. item = m_frame->CreateNewText( LAYER_HIERLABEL );
  553. break;
  554. case SCH_GLOBAL_LABEL_T:
  555. item = m_frame->CreateNewText( LAYER_GLOBLABEL );
  556. break;
  557. case SCH_TEXT_T:
  558. item = m_frame->CreateNewText( LAYER_NOTES );
  559. break;
  560. case SCH_SHEET_PIN_T:
  561. {
  562. SCH_HIERLABEL* label = nullptr;
  563. SCH_SHEET* sheet = (SCH_SHEET*) m_selectionTool->SelectPoint( cursorPos,
  564. EE_COLLECTOR::SheetsOnly );
  565. if( !sheet )
  566. {
  567. m_statusPopup.reset( new STATUS_TEXT_POPUP( m_frame ) );
  568. m_statusPopup->SetText( _( "Click over a sheet." ) );
  569. m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
  570. m_statusPopup->PopupFor( 2000 );
  571. break;
  572. }
  573. if( importMode )
  574. {
  575. label = m_frame->ImportHierLabel( sheet );
  576. if( !label )
  577. {
  578. m_statusPopup.reset( new STATUS_TEXT_POPUP( m_frame ) );
  579. m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
  580. m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
  581. m_statusPopup->PopupFor( 2000 );
  582. break;
  583. }
  584. }
  585. item = m_frame->CreateSheetPin( sheet, label );
  586. break;
  587. }
  588. default:
  589. break;
  590. }
  591. // Restore cursor after dialog
  592. getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
  593. if( item )
  594. {
  595. item->SetFlags( IS_NEW | IS_MOVED );
  596. m_view->ClearPreview();
  597. m_view->AddToPreview( item->Clone() );
  598. m_selectionTool->AddItemToSel( item );
  599. }
  600. getViewControls()->SetCursorPosition( cursorPos, false );
  601. }
  602. // ... and second click places:
  603. else
  604. {
  605. item->ClearFlags( IS_MOVED );
  606. m_frame->AddItemToScreenAndUndoList( (SCH_ITEM*) item );
  607. item = m_frame->GetNextNewText();
  608. if( item )
  609. {
  610. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  611. item->SetFlags( IS_NEW | IS_MOVED );
  612. m_view->ClearPreview();
  613. m_view->AddToPreview( item->Clone() );
  614. m_selectionTool->AddItemToSel( item );
  615. }
  616. else
  617. {
  618. m_view->ClearPreview();
  619. }
  620. }
  621. }
  622. else if( evt->IsClick( BUT_RIGHT ) )
  623. {
  624. // Warp after context menu only if dragging...
  625. if( !item )
  626. m_toolMgr->VetoContextMenuMouseWarp();
  627. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  628. }
  629. else if( item && evt->IsSelectionEvent() )
  630. {
  631. // This happens if our text was replaced out from under us by ConvertTextType()
  632. EE_SELECTION& selection = m_selectionTool->GetSelection();
  633. if( selection.GetSize() == 1 )
  634. {
  635. item = (SCH_ITEM*) selection.Front();
  636. m_view->ClearPreview();
  637. m_view->AddToPreview( item->Clone() );
  638. }
  639. else
  640. item = nullptr;
  641. }
  642. else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  643. {
  644. static_cast<SCH_ITEM*>( item )->SetPosition( (wxPoint) cursorPos );
  645. m_view->ClearPreview();
  646. m_view->AddToPreview( item->Clone() );
  647. }
  648. else
  649. evt->SetPassEvent();
  650. // Enable autopanning and cursor capture only when there is a module to be placed
  651. getViewControls()->SetAutoPan( item != nullptr );
  652. getViewControls()->CaptureCursor( item != nullptr );
  653. }
  654. return 0;
  655. }
  656. int SCH_DRAWING_TOOLS::DrawSheet( const TOOL_EVENT& aEvent )
  657. {
  658. EE_POINT_EDITOR* pointEditor = m_toolMgr->GetTool<EE_POINT_EDITOR>();
  659. SCH_SHEET* sheet = nullptr;
  660. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  661. getViewControls()->ShowCursor( true );
  662. std::string tool = aEvent.GetCommandStr().get();
  663. m_frame->PushTool( tool );
  664. Activate();
  665. // Prime the pump
  666. if( aEvent.HasPosition() )
  667. m_toolMgr->RunAction( ACTIONS::cursorClick );
  668. // Main loop: keep receiving events
  669. while( TOOL_EVENT* evt = Wait() )
  670. {
  671. if( !pointEditor->HasPoint() )
  672. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  673. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  674. auto cleanup = [&] () {
  675. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  676. m_view->ClearPreview();
  677. delete sheet;
  678. sheet = nullptr;
  679. };
  680. if( evt->IsCancelInteractive() )
  681. {
  682. if( sheet )
  683. cleanup();
  684. else
  685. {
  686. m_frame->PopTool( tool );
  687. break;
  688. }
  689. }
  690. else if( evt->IsActivate() )
  691. {
  692. if( sheet )
  693. cleanup();
  694. if( evt->IsPointEditor() )
  695. {
  696. // don't exit (the point editor runs in the background)
  697. }
  698. else if( evt->IsMoveTool() )
  699. {
  700. // leave ourselves on the stack so we come back after the move
  701. break;
  702. }
  703. else
  704. {
  705. m_frame->PopTool( tool );
  706. break;
  707. }
  708. }
  709. else if( evt->IsClick( BUT_LEFT ) && !sheet )
  710. {
  711. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  712. sheet = new SCH_SHEET( (wxPoint) cursorPos );
  713. sheet->SetFlags( IS_NEW | IS_RESIZED );
  714. sheet->SetParent( m_frame->GetScreen() );
  715. sheet->SetScreen( NULL );
  716. sizeSheet( sheet, cursorPos );
  717. m_view->ClearPreview();
  718. m_view->AddToPreview( sheet->Clone() );
  719. }
  720. else if( sheet && ( evt->IsClick( BUT_LEFT )
  721. || evt->IsAction( &EE_ACTIONS::finishSheet ) ) )
  722. {
  723. m_view->ClearPreview();
  724. getViewControls()->SetAutoPan( false );
  725. getViewControls()->CaptureCursor( false );
  726. if( m_frame->EditSheetProperties((SCH_SHEET*) sheet, g_CurrentSheet, nullptr ) )
  727. {
  728. sheet->AutoplaceFields( /* aScreen */ NULL, /* aManual */ false );
  729. m_frame->AddItemToScreenAndUndoList( sheet );
  730. m_frame->UpdateHierarchyNavigator();
  731. m_selectionTool->AddItemToSel( sheet );
  732. }
  733. else
  734. {
  735. delete sheet;
  736. }
  737. sheet = nullptr;
  738. }
  739. else if( sheet && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  740. {
  741. sizeSheet( sheet, cursorPos );
  742. m_view->ClearPreview();
  743. m_view->AddToPreview( sheet->Clone() );
  744. }
  745. else if( evt->IsClick( BUT_RIGHT ) )
  746. {
  747. // Warp after context menu only if dragging...
  748. if( !sheet )
  749. m_toolMgr->VetoContextMenuMouseWarp();
  750. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  751. }
  752. else
  753. evt->SetPassEvent();
  754. // Enable autopanning and cursor capture only when there is a sheet to be placed
  755. getViewControls()->SetAutoPan( sheet != nullptr );
  756. getViewControls()->CaptureCursor( sheet != nullptr );
  757. }
  758. return 0;
  759. }
  760. void SCH_DRAWING_TOOLS::sizeSheet( SCH_SHEET* aSheet, VECTOR2I aPos )
  761. {
  762. wxPoint pos = aSheet->GetPosition();
  763. wxPoint size = (wxPoint) aPos - pos;
  764. size.x = std::max( size.x, MIN_SHEET_WIDTH );
  765. size.y = std::max( size.y, MIN_SHEET_HEIGHT );
  766. wxPoint grid = m_frame->GetNearestGridPosition( pos + size );
  767. aSheet->Resize( wxSize( grid.x - pos.x, grid.y - pos.y ) );
  768. }
  769. void SCH_DRAWING_TOOLS::setTransitions()
  770. {
  771. Go( &SCH_DRAWING_TOOLS::PlaceComponent, EE_ACTIONS::placeSymbol.MakeEvent() );
  772. Go( &SCH_DRAWING_TOOLS::PlaceComponent, EE_ACTIONS::placePower.MakeEvent() );
  773. Go( &SCH_DRAWING_TOOLS::SingleClickPlace, EE_ACTIONS::placeNoConnect.MakeEvent() );
  774. Go( &SCH_DRAWING_TOOLS::SingleClickPlace, EE_ACTIONS::placeJunction.MakeEvent() );
  775. Go( &SCH_DRAWING_TOOLS::SingleClickPlace, EE_ACTIONS::placeBusWireEntry.MakeEvent() );
  776. Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeLabel.MakeEvent() );
  777. Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeHierLabel.MakeEvent() );
  778. Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeGlobalLabel.MakeEvent() );
  779. Go( &SCH_DRAWING_TOOLS::DrawSheet, EE_ACTIONS::drawSheet.MakeEvent() );
  780. Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::importSheetPin.MakeEvent() );
  781. Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSchematicText.MakeEvent() );
  782. Go( &SCH_DRAWING_TOOLS::PlaceImage, EE_ACTIONS::placeImage.MakeEvent() );
  783. }