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.

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