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.

883 lines
28 KiB

2 years ago
2 years ago
2 years ago
2 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 <ee_actions.h>
  25. #include <optional>
  26. #include <symbol_edit_frame.h>
  27. #include <sch_commit.h>
  28. #include <gal/graphics_abstraction_layer.h>
  29. #include <tools/symbol_editor_drawing_tools.h>
  30. #include <tools/symbol_editor_pin_tool.h>
  31. #include <tools/ee_grid_helper.h>
  32. #include <dialogs/dialog_text_properties.h>
  33. #include <sch_shape.h>
  34. #include <sch_textbox.h>
  35. #include <pgm_base.h>
  36. #include <view/view_controls.h>
  37. #include <symbol_editor/symbol_editor_settings.h>
  38. #include <settings/settings_manager.h>
  39. #include <string_utils.h>
  40. #include <wx/msgdlg.h>
  41. #include <import_gfx/dialog_import_gfx_sch.h>
  42. KIID SYMBOL_EDITOR_DRAWING_TOOLS::g_lastPin;
  43. SYMBOL_EDITOR_DRAWING_TOOLS::SYMBOL_EDITOR_DRAWING_TOOLS() :
  44. EE_TOOL_BASE<SYMBOL_EDIT_FRAME>( "eeschema.SymbolDrawing" ),
  45. m_lastTextBold( false ),
  46. m_lastTextItalic( false ),
  47. m_lastTextAngle( ANGLE_HORIZONTAL ),
  48. m_lastTextJust( GR_TEXT_H_ALIGN_LEFT ),
  49. m_lastFillStyle( FILL_T::NO_FILL ),
  50. m_lastFillColor( COLOR4D::UNSPECIFIED ),
  51. m_lastStroke( 0, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
  52. m_drawSpecificBodyStyle( true ),
  53. m_drawSpecificUnit( false ),
  54. m_inDrawShape( false ),
  55. m_inTwoClickPlace( false )
  56. {
  57. }
  58. bool SYMBOL_EDITOR_DRAWING_TOOLS::Init()
  59. {
  60. EE_TOOL_BASE::Init();
  61. auto isDrawingCondition =
  62. [] ( const SELECTION& aSel )
  63. {
  64. SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aSel.Front() );
  65. return item && item->IsNew();
  66. };
  67. m_menu->GetMenu().AddItem( ACTIONS::finishInteractive, isDrawingCondition, 2 );
  68. return true;
  69. }
  70. int SYMBOL_EDITOR_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
  71. {
  72. KICAD_T type = aEvent.Parameter<KICAD_T>();
  73. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  74. SYMBOL_EDITOR_SETTINGS* cfg = mgr.GetAppSettings<SYMBOL_EDITOR_SETTINGS>( "symbol_editor" );
  75. SYMBOL_EDITOR_PIN_TOOL* pinTool = type == SCH_PIN_T
  76. ? m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>()
  77. : nullptr;
  78. if( m_inTwoClickPlace )
  79. return 0;
  80. REENTRANCY_GUARD guard( &m_inTwoClickPlace );
  81. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  82. EE_GRID_HELPER grid( m_toolMgr );
  83. VECTOR2I cursorPos;
  84. bool ignorePrimePosition = false;
  85. SCH_ITEM* item = nullptr;
  86. bool isText = aEvent.IsAction( &EE_ACTIONS::placeSymbolText );
  87. COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
  88. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  89. m_frame->PushTool( aEvent );
  90. auto setCursor =
  91. [&]()
  92. {
  93. if( item )
  94. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
  95. else if( isText )
  96. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
  97. else
  98. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  99. };
  100. auto cleanup =
  101. [&] ()
  102. {
  103. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  104. m_view->ClearPreview();
  105. delete item;
  106. item = nullptr;
  107. };
  108. Activate();
  109. // Must be done after Activate() so that it gets set into the correct context
  110. controls->ShowCursor( true );
  111. // Set initial cursor
  112. setCursor();
  113. if( aEvent.HasPosition() )
  114. {
  115. m_toolMgr->PrimeTool( aEvent.Position() );
  116. }
  117. else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
  118. {
  119. m_toolMgr->PrimeTool( { 0, 0 } );
  120. ignorePrimePosition = true;
  121. }
  122. // Main loop: keep receiving events
  123. while( TOOL_EVENT* evt = Wait() )
  124. {
  125. setCursor();
  126. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  127. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  128. cursorPos = grid.Align( controls->GetMousePosition(), grid.GetItemGrid( item ) );
  129. controls->ForceCursorPosition( true, cursorPos );
  130. // The tool hotkey is interpreted as a click when drawing
  131. bool isSyntheticClick = item && evt->IsActivate() && evt->HasPosition()
  132. && evt->Matches( aEvent );
  133. if( evt->IsCancelInteractive() )
  134. {
  135. m_frame->GetInfoBar()->Dismiss();
  136. if( item )
  137. {
  138. cleanup();
  139. }
  140. else
  141. {
  142. m_frame->PopTool( aEvent );
  143. break;
  144. }
  145. }
  146. else if( evt->IsActivate() && !isSyntheticClick )
  147. {
  148. if( item && evt->IsMoveTool() )
  149. {
  150. // we're already moving our own item; ignore the move tool
  151. evt->SetPassEvent( false );
  152. continue;
  153. }
  154. if( item )
  155. {
  156. m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel item creation." ) );
  157. evt->SetPassEvent( false );
  158. continue;
  159. }
  160. if( evt->IsPointEditor() )
  161. {
  162. // don't exit (the point editor runs in the background)
  163. }
  164. else if( evt->IsMoveTool() )
  165. {
  166. break;
  167. }
  168. else
  169. {
  170. m_frame->PopTool( aEvent );
  171. break;
  172. }
  173. }
  174. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) || isSyntheticClick )
  175. {
  176. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  177. if( !symbol )
  178. continue;
  179. // First click creates...
  180. if( !item )
  181. {
  182. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  183. switch( type )
  184. {
  185. case SCH_PIN_T:
  186. {
  187. item = pinTool->CreatePin( cursorPos, symbol );
  188. if( item )
  189. g_lastPin = item->m_Uuid;
  190. break;
  191. }
  192. case SCH_TEXT_T:
  193. {
  194. SCH_TEXT* text = new SCH_TEXT( cursorPos, wxEmptyString, LAYER_DEVICE );
  195. text->SetParent( symbol );
  196. if( m_drawSpecificUnit )
  197. text->SetUnit( m_frame->GetUnit() );
  198. if( m_drawSpecificBodyStyle )
  199. text->SetBodyStyle( m_frame->GetBodyStyle() );
  200. text->SetTextSize( VECTOR2I( schIUScale.MilsToIU( cfg->m_Defaults.text_size ),
  201. schIUScale.MilsToIU( cfg->m_Defaults.text_size ) ) );
  202. text->SetTextAngle( m_lastTextAngle );
  203. DIALOG_TEXT_PROPERTIES dlg( m_frame, text );
  204. if( dlg.ShowModal() != wxID_OK || NoPrintableChars( text->GetText() ) )
  205. delete text;
  206. else
  207. item = text;
  208. break;
  209. }
  210. default:
  211. wxFAIL_MSG( "TwoClickPlace(): unknown type" );
  212. }
  213. // If we started with a hotkey which has a position then warp back to that.
  214. // Otherwise update to the current mouse position pinned inside the autoscroll
  215. // boundaries.
  216. if( evt->IsPrime() && !ignorePrimePosition )
  217. {
  218. cursorPos = grid.Align( evt->Position(), grid.GetItemGrid( item ) );
  219. getViewControls()->WarpMouseCursor( cursorPos, true );
  220. }
  221. else
  222. {
  223. getViewControls()->PinCursorInsideNonAutoscrollArea( true );
  224. cursorPos = getViewControls()->GetMousePosition();
  225. }
  226. if( item )
  227. {
  228. item->SetPosition( VECTOR2I( cursorPos.x, -cursorPos.y ) );
  229. item->SetFlags( IS_NEW | IS_MOVING );
  230. m_view->ClearPreview();
  231. m_view->AddToPreview( item->Clone() );
  232. m_selectionTool->AddItemToSel( item );
  233. // update the cursor so it looks correct before another event
  234. setCursor();
  235. }
  236. controls->SetCursorPosition( cursorPos, false );
  237. }
  238. // ... and second click places:
  239. else
  240. {
  241. SCH_COMMIT commit( m_toolMgr );
  242. commit.Modify( symbol, m_frame->GetScreen() );
  243. switch( item->Type() )
  244. {
  245. case SCH_PIN_T:
  246. pinTool->PlacePin( static_cast<SCH_PIN*>( item ) );
  247. item->ClearEditFlags();
  248. commit.Push( _( "Place Pin" ) );
  249. break;
  250. case SCH_TEXT_T:
  251. symbol->AddDrawItem( static_cast<SCH_TEXT*>( item ) );
  252. item->ClearEditFlags();
  253. commit.Push( _( "Draw Text" ) );
  254. break;
  255. default:
  256. wxFAIL_MSG( "TwoClickPlace(): unknown type" );
  257. }
  258. item = nullptr;
  259. m_view->ClearPreview();
  260. m_frame->RebuildView();
  261. }
  262. }
  263. else if( evt->IsClick( BUT_RIGHT ) )
  264. {
  265. // Warp after context menu only if dragging...
  266. if( !item )
  267. m_toolMgr->VetoContextMenuMouseWarp();
  268. m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
  269. }
  270. else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  271. {
  272. item->SetPosition( VECTOR2I( cursorPos.x, cursorPos.y ) );
  273. m_view->ClearPreview();
  274. m_view->AddToPreview( item->Clone() );
  275. }
  276. else
  277. {
  278. evt->SetPassEvent();
  279. }
  280. // Enable autopanning and cursor capture only when there is an item to be placed
  281. controls->SetAutoPan( item != nullptr );
  282. controls->CaptureCursor( item != nullptr );
  283. }
  284. controls->SetAutoPan( false );
  285. controls->CaptureCursor( false );
  286. controls->ForceCursorPosition( false );
  287. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  288. return 0;
  289. }
  290. int SYMBOL_EDITOR_DRAWING_TOOLS::DrawShape( const TOOL_EVENT& aEvent )
  291. {
  292. SHAPE_T requestedShape = aEvent.Parameter<SHAPE_T>();
  293. return doDrawShape( aEvent, requestedShape );
  294. }
  295. int SYMBOL_EDITOR_DRAWING_TOOLS::DrawSymbolTextBox( const TOOL_EVENT& aEvent )
  296. {
  297. return doDrawShape( aEvent, std::nullopt /* Draw text box */ );
  298. }
  299. int SYMBOL_EDITOR_DRAWING_TOOLS::doDrawShape( const TOOL_EVENT& aEvent, std::optional<SHAPE_T> aDrawingShape )
  300. {
  301. bool isTextBox = !aDrawingShape.has_value();
  302. SHAPE_T toolType = aDrawingShape.value_or( SHAPE_T::SEGMENT );
  303. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  304. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  305. SYMBOL_EDITOR_SETTINGS* cfg = mgr.GetAppSettings<SYMBOL_EDITOR_SETTINGS>( "symbol_editor" );
  306. EE_GRID_HELPER grid( m_toolMgr );
  307. VECTOR2I cursorPos;
  308. SHAPE_T shapeType = toolType == SHAPE_T::SEGMENT ? SHAPE_T::POLY : toolType;
  309. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  310. SCH_SHAPE* item = nullptr;
  311. wxString description;
  312. if( m_inDrawShape )
  313. return 0;
  314. REENTRANCY_GUARD guard( &m_inDrawShape );
  315. // We might be running as the same shape in another co-routine. Make sure that one
  316. // gets whacked.
  317. m_toolMgr->DeactivateTool();
  318. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  319. m_frame->PushTool( aEvent );
  320. auto setCursor =
  321. [&]()
  322. {
  323. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  324. };
  325. auto cleanup =
  326. [&] ()
  327. {
  328. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  329. m_view->ClearPreview();
  330. delete item;
  331. item = nullptr;
  332. };
  333. Activate();
  334. // Must be done after Activate() so that it gets set into the correct context
  335. controls->ShowCursor( true );
  336. // Set initial cursor
  337. setCursor();
  338. if( aEvent.HasPosition() )
  339. m_toolMgr->PrimeTool( aEvent.Position() );
  340. // Main loop: keep receiving events
  341. while( TOOL_EVENT* evt = Wait() )
  342. {
  343. setCursor();
  344. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  345. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  346. cursorPos = grid.Align( controls->GetMousePosition(), grid.GetItemGrid( item ) );
  347. controls->ForceCursorPosition( true, cursorPos );
  348. // The tool hotkey is interpreted as a click when drawing
  349. bool isSyntheticClick = item && evt->IsActivate() && evt->HasPosition()
  350. && evt->Matches( aEvent );
  351. if( evt->IsCancelInteractive() )
  352. {
  353. if( item )
  354. {
  355. cleanup();
  356. }
  357. else
  358. {
  359. m_frame->PopTool( aEvent );
  360. break;
  361. }
  362. }
  363. else if( evt->IsActivate() && !isSyntheticClick )
  364. {
  365. if( item )
  366. cleanup();
  367. if( evt->IsPointEditor() )
  368. {
  369. // don't exit (the point editor runs in the background)
  370. }
  371. else if( evt->IsMoveTool() )
  372. {
  373. // leave ourselves on the stack so we come back after the move
  374. break;
  375. }
  376. else
  377. {
  378. m_frame->PopTool( aEvent );
  379. break;
  380. }
  381. }
  382. else if( evt->IsClick( BUT_LEFT ) && !item )
  383. {
  384. // Update in case the symbol was changed while the tool was running
  385. symbol = m_frame->GetCurSymbol();
  386. if( !symbol )
  387. continue;
  388. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  389. int lineWidth = schIUScale.MilsToIU( cfg->m_Defaults.line_width );
  390. if( isTextBox )
  391. {
  392. SCH_TEXTBOX* textbox = new SCH_TEXTBOX( LAYER_DEVICE, lineWidth, m_lastFillStyle );
  393. textbox->SetParent( symbol );
  394. textbox->SetTextSize( VECTOR2I( schIUScale.MilsToIU( cfg->m_Defaults.text_size ),
  395. schIUScale.MilsToIU( cfg->m_Defaults.text_size ) ) );
  396. // Must be after SetTextSize()
  397. textbox->SetBold( m_lastTextBold );
  398. textbox->SetItalic( m_lastTextItalic );
  399. textbox->SetTextAngle( m_lastTextAngle );
  400. textbox->SetHorizJustify( m_lastTextJust );
  401. item = textbox;
  402. description = _( "Add Text Box" );
  403. }
  404. else
  405. {
  406. item = new SCH_SHAPE( shapeType, LAYER_DEVICE, lineWidth, m_lastFillStyle );
  407. item->SetParent( symbol );
  408. description = wxString::Format( _( "Add %s" ), item->GetFriendlyName() );
  409. }
  410. item->SetStroke( m_lastStroke );
  411. item->SetFillColor( m_lastFillColor );
  412. item->SetFlags( IS_NEW );
  413. item->BeginEdit( cursorPos );
  414. if( m_drawSpecificUnit )
  415. item->SetUnit( m_frame->GetUnit() );
  416. if( m_drawSpecificBodyStyle )
  417. item->SetBodyStyle( m_frame->GetBodyStyle() );
  418. m_selectionTool->AddItemToSel( item );
  419. }
  420. else if( item && ( evt->IsClick( BUT_LEFT )
  421. || evt->IsDblClick( BUT_LEFT )
  422. || isSyntheticClick
  423. || evt->IsAction( &ACTIONS::finishInteractive ) ) )
  424. {
  425. if( symbol != m_frame->GetCurSymbol() )
  426. {
  427. symbol = m_frame->GetCurSymbol();
  428. item->SetParent( symbol );
  429. }
  430. if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::finishInteractive )
  431. || !item->ContinueEdit( VECTOR2I( cursorPos.x, cursorPos.y ) ) )
  432. {
  433. if( toolType == SHAPE_T::POLY )
  434. {
  435. item->CalcEdit( item->GetPosition() ); // Close shape
  436. item->EndEdit( true );
  437. }
  438. else
  439. {
  440. item->EndEdit();
  441. }
  442. item->ClearEditFlags();
  443. if( isTextBox )
  444. {
  445. SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( item );
  446. DIALOG_TEXT_PROPERTIES dlg( m_frame, static_cast<SCH_TEXTBOX*>( item ) );
  447. // QuasiModal required for syntax help and Scintilla auto-complete
  448. if( dlg.ShowQuasiModal() != wxID_OK )
  449. {
  450. cleanup();
  451. continue;
  452. }
  453. m_lastTextBold = textbox->IsBold();
  454. m_lastTextItalic = textbox->IsItalic();
  455. m_lastTextAngle = textbox->GetTextAngle();
  456. m_lastTextJust = textbox->GetHorizJustify();
  457. }
  458. m_lastStroke = item->GetStroke();
  459. m_lastFillStyle = item->GetFillMode();
  460. m_lastFillColor = item->GetFillColor();
  461. m_view->ClearPreview();
  462. SCH_COMMIT commit( m_toolMgr );
  463. commit.Modify( symbol, m_frame->GetScreen() );
  464. symbol->AddDrawItem( item );
  465. item = nullptr;
  466. commit.Push( description );
  467. m_frame->RebuildView();
  468. m_toolMgr->PostAction( ACTIONS::activatePointEditor );
  469. }
  470. }
  471. else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  472. {
  473. item->CalcEdit( cursorPos );
  474. m_view->ClearPreview();
  475. m_view->AddToPreview( item->Clone() );
  476. }
  477. else if( evt->IsDblClick( BUT_LEFT ) && !item )
  478. {
  479. m_toolMgr->RunAction( EE_ACTIONS::properties );
  480. }
  481. else if( evt->IsClick( BUT_RIGHT ) )
  482. {
  483. // Warp after context menu only if dragging...
  484. if( !item )
  485. m_toolMgr->VetoContextMenuMouseWarp();
  486. m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
  487. }
  488. else
  489. {
  490. evt->SetPassEvent();
  491. }
  492. // Enable autopanning and cursor capture only when there is a shape being drawn
  493. controls->SetAutoPan( item != nullptr );
  494. controls->CaptureCursor( item != nullptr );
  495. }
  496. controls->SetAutoPan( false );
  497. controls->CaptureCursor( false );
  498. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  499. return 0;
  500. }
  501. int SYMBOL_EDITOR_DRAWING_TOOLS::PlaceAnchor( const TOOL_EVENT& aEvent )
  502. {
  503. m_frame->PushTool( aEvent );
  504. auto setCursor =
  505. [&]()
  506. {
  507. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
  508. };
  509. Activate();
  510. // Must be done after Activate() so that it gets set into the correct context
  511. getViewControls()->ShowCursor( true );
  512. // Set initial cursor
  513. setCursor();
  514. // Main loop: keep receiving events
  515. while( TOOL_EVENT* evt = Wait() )
  516. {
  517. setCursor();
  518. if( evt->IsCancelInteractive() )
  519. {
  520. m_frame->PopTool( aEvent );
  521. break;
  522. }
  523. else if( evt->IsActivate() )
  524. {
  525. m_frame->PopTool( aEvent );
  526. break;
  527. }
  528. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  529. {
  530. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  531. if( !symbol )
  532. continue;
  533. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
  534. symbol->Move( -cursorPos );
  535. // Refresh the view without changing the viewport
  536. m_view->SetCenter( m_view->GetCenter() + cursorPos );
  537. m_view->RecacheAllItems();
  538. m_frame->OnModify();
  539. }
  540. else if( evt->IsClick( BUT_RIGHT ) )
  541. {
  542. m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
  543. }
  544. else
  545. {
  546. evt->SetPassEvent();
  547. }
  548. }
  549. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  550. return 0;
  551. }
  552. int SYMBOL_EDITOR_DRAWING_TOOLS::ImportGraphics( const TOOL_EVENT& aEvent )
  553. {
  554. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  555. if( !symbol )
  556. return 0;
  557. // Note: PlaceImportedGraphics() will convert PCB_SHAPE_T and PCB_TEXT_T to footprint
  558. // items if needed
  559. DIALOG_IMPORT_GFX_SCH dlg( m_frame );
  560. int dlgResult = dlg.ShowModal();
  561. std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
  562. if( dlgResult != wxID_OK )
  563. return 0;
  564. // Ensure the list is not empty:
  565. if( list.empty() )
  566. {
  567. wxMessageBox( _( "No graphic items found in file." ) );
  568. return 0;
  569. }
  570. m_toolMgr->RunAction( ACTIONS::cancelInteractive );
  571. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  572. std::vector<SCH_ITEM*> newItems; // all new items, including group
  573. std::vector<SCH_ITEM*> selectedItems; // the group, or newItems if no group
  574. EE_SELECTION preview;
  575. SCH_COMMIT commit( m_toolMgr );
  576. for( std::unique_ptr<EDA_ITEM>& ptr : list )
  577. {
  578. SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( ptr.get() );
  579. wxCHECK2( item, continue );
  580. newItems.push_back( item );
  581. selectedItems.push_back( item );
  582. preview.Add( item );
  583. ptr.release();
  584. }
  585. if( !dlg.IsPlacementInteractive() )
  586. {
  587. commit.Modify( symbol, m_frame->GetScreen() );
  588. // Place the imported drawings
  589. for( SCH_ITEM* item : newItems )
  590. {
  591. symbol->AddDrawItem( item );
  592. item->ClearEditFlags();
  593. }
  594. commit.Push( _( "Import Graphic" ) );
  595. m_frame->RebuildView();
  596. return 0;
  597. }
  598. m_view->Add( &preview );
  599. // Clear the current selection then select the drawings so that edit tools work on them
  600. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  601. EDA_ITEMS selItems( selectedItems.begin(), selectedItems.end() );
  602. m_toolMgr->RunAction<EDA_ITEMS*>( EE_ACTIONS::addItemsToSel, &selItems );
  603. m_frame->PushTool( aEvent );
  604. auto setCursor = [&]()
  605. {
  606. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
  607. };
  608. Activate();
  609. // Must be done after Activate() so that it gets set into the correct context
  610. controls->ShowCursor( true );
  611. controls->ForceCursorPosition( false );
  612. // Set initial cursor
  613. setCursor();
  614. //SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
  615. EE_GRID_HELPER grid( m_toolMgr );
  616. // Now move the new items to the current cursor position:
  617. VECTOR2I cursorPos = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
  618. VECTOR2I delta = cursorPos;
  619. VECTOR2I currentOffset;
  620. for( SCH_ITEM* item : selectedItems )
  621. item->Move( delta );
  622. currentOffset += delta;
  623. m_view->Update( &preview );
  624. // Main loop: keep receiving events
  625. while( TOOL_EVENT* evt = Wait() )
  626. {
  627. setCursor();
  628. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  629. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  630. cursorPos = grid.Align( controls->GetMousePosition(), GRID_GRAPHICS );
  631. controls->ForceCursorPosition( true, cursorPos );
  632. if( evt->IsCancelInteractive() || evt->IsActivate() )
  633. {
  634. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  635. for( SCH_ITEM* item : newItems )
  636. delete item;
  637. break;
  638. }
  639. else if( evt->IsMotion() )
  640. {
  641. delta = cursorPos - currentOffset;
  642. for( SCH_ITEM* item : selectedItems )
  643. item->Move( delta );
  644. currentOffset += delta;
  645. m_view->Update( &preview );
  646. }
  647. else if( evt->IsClick( BUT_RIGHT ) )
  648. {
  649. m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
  650. }
  651. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  652. {
  653. commit.Modify( symbol, m_frame->GetScreen() );
  654. // Place the imported drawings
  655. for( SCH_ITEM* item : newItems )
  656. {
  657. symbol->AddDrawItem( item );
  658. item->ClearEditFlags();
  659. }
  660. commit.Push( _( "Import Graphic" ) );
  661. break; // This is a one-shot command, not a tool
  662. }
  663. else
  664. {
  665. evt->SetPassEvent();
  666. }
  667. }
  668. preview.Clear();
  669. m_view->Remove( &preview );
  670. m_frame->RebuildView();
  671. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  672. controls->ForceCursorPosition( false );
  673. m_frame->PopTool( aEvent );
  674. return 0;
  675. }
  676. int SYMBOL_EDITOR_DRAWING_TOOLS::RepeatDrawItem( const TOOL_EVENT& aEvent )
  677. {
  678. SYMBOL_EDITOR_PIN_TOOL* pinTool = m_toolMgr->GetTool<SYMBOL_EDITOR_PIN_TOOL>();
  679. LIB_SYMBOL* symbol = m_frame->GetCurSymbol();
  680. SCH_PIN* sourcePin = nullptr;
  681. if( !symbol )
  682. return 0;
  683. for( SCH_PIN* test : symbol->GetPins() )
  684. {
  685. if( test->m_Uuid == g_lastPin )
  686. {
  687. sourcePin = test;
  688. break;
  689. }
  690. }
  691. if( sourcePin )
  692. {
  693. SCH_PIN* pin = pinTool->RepeatPin( sourcePin );
  694. if( pin )
  695. g_lastPin = pin->m_Uuid;
  696. m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
  697. if( pin )
  698. m_toolMgr->RunAction<EDA_ITEM*>( EE_ACTIONS::addItemToSel, pin );
  699. }
  700. return 0;
  701. }
  702. void SYMBOL_EDITOR_DRAWING_TOOLS::setTransitions()
  703. {
  704. // clang-format off
  705. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSymbolPin.MakeEvent() );
  706. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSymbolText.MakeEvent() );
  707. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawRectangle.MakeEvent() );
  708. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawCircle.MakeEvent() );
  709. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawArc.MakeEvent() );
  710. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawBezier.MakeEvent() );
  711. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolLines.MakeEvent() );
  712. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolPolygon.MakeEvent() );
  713. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::DrawSymbolTextBox, EE_ACTIONS::drawSymbolTextBox.MakeEvent() );
  714. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::PlaceAnchor, EE_ACTIONS::placeSymbolAnchor.MakeEvent() );
  715. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::ImportGraphics, EE_ACTIONS::importGraphics.MakeEvent() );
  716. Go( &SYMBOL_EDITOR_DRAWING_TOOLS::RepeatDrawItem, EE_ACTIONS::repeatDrawItem.MakeEvent() );
  717. // clang-format on
  718. }