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.

352 lines
11 KiB

2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017-2023 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 "pcb_tool_base.h"
  24. #include <tool/tool_manager.h>
  25. #include <board_commit.h>
  26. #include <gal/graphics_abstraction_layer.h>
  27. #include <footprint.h>
  28. #include <pcb_draw_panel_gal.h>
  29. #include <pgm_base.h>
  30. #include <settings/settings_manager.h>
  31. #include <pcbnew_settings.h>
  32. #include <footprint_editor_settings.h>
  33. #include <tools/pcb_grid_helper.h>
  34. #include <tools/pcb_selection_tool.h>
  35. #include <tools/pcb_actions.h>
  36. #include <tools/tool_event_utils.h>
  37. #include <tools/zone_filler_tool.h>
  38. #include <view/view_controls.h>
  39. void PCB_TOOL_BASE::doInteractiveItemPlacement( const TOOL_EVENT& aTool,
  40. INTERACTIVE_PLACER_BASE* aPlacer,
  41. const wxString& aCommitMessage, int aOptions )
  42. {
  43. using namespace std::placeholders;
  44. std::unique_ptr<BOARD_ITEM> newItem;
  45. frame()->PushTool( aTool );
  46. BOARD_COMMIT commit( frame() );
  47. GetManager()->RunAction( PCB_ACTIONS::selectionClear );
  48. Activate();
  49. // Must be done after Activate() so that it gets set into the correct context
  50. controls()->ShowCursor( true );
  51. controls()->ForceCursorPosition( false );
  52. // do not capture or auto-pan until we start placing an item
  53. PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
  54. // Add a VIEW_GROUP that serves as a preview for the new item
  55. PCB_SELECTION preview;
  56. view()->Add( &preview );
  57. aPlacer->m_board = board();
  58. aPlacer->m_frame = frame();
  59. aPlacer->m_modifiers = 0;
  60. auto makeNewItem =
  61. [&]( const VECTOR2I& aPosition )
  62. {
  63. if( frame()->GetModel() )
  64. newItem = aPlacer->CreateItem();
  65. if( newItem )
  66. {
  67. newItem->SetPosition( aPosition );
  68. preview.Add( newItem.get() );
  69. // footprints have more drawable parts
  70. if( FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( newItem.get() ) )
  71. fp->RunOnChildren( std::bind( &KIGFX::VIEW_GROUP::Add, &preview, _1 ) );
  72. }
  73. };
  74. if( aOptions & IPO_SINGLE_CLICK )
  75. makeNewItem( controls()->GetCursorPosition() );
  76. auto setCursor =
  77. [&]()
  78. {
  79. if( !newItem )
  80. frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  81. else
  82. frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
  83. };
  84. // Set initial cursor
  85. setCursor();
  86. // Main loop: keep receiving events
  87. while( TOOL_EVENT* evt = Wait() )
  88. {
  89. setCursor();
  90. grid.SetSnap( false ); // Interactive placement tools need to set their own item snaps
  91. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  92. VECTOR2I cursorPos = grid.BestSnapAnchor( controls()->GetMousePosition(), nullptr );
  93. aPlacer->m_modifiers = evt->Modifier();
  94. auto cleanup =
  95. [&] ()
  96. {
  97. newItem = nullptr;
  98. preview.Clear();
  99. view()->Update( &preview );
  100. controls()->SetAutoPan( false );
  101. controls()->CaptureCursor( false );
  102. controls()->ShowCursor( true );
  103. controls()->ForceCursorPosition( false );
  104. };
  105. if( evt->IsCancelInteractive() )
  106. {
  107. if( aOptions & IPO_SINGLE_CLICK )
  108. {
  109. cleanup();
  110. frame()->PopTool( aTool );
  111. break;
  112. }
  113. else if( newItem )
  114. {
  115. cleanup();
  116. }
  117. else
  118. {
  119. frame()->PopTool( aTool );
  120. break;
  121. }
  122. }
  123. else if( evt->IsActivate() )
  124. {
  125. if( newItem )
  126. cleanup();
  127. if( evt->IsPointEditor() )
  128. {
  129. // don't exit (the point editor runs in the background)
  130. }
  131. else if( evt->IsMoveTool() )
  132. {
  133. // leave ourselves on the stack so we come back after the move
  134. break;
  135. }
  136. else
  137. {
  138. frame()->PopTool( aTool );
  139. break;
  140. }
  141. }
  142. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  143. {
  144. if( !newItem )
  145. {
  146. // create the item if possible
  147. makeNewItem( cursorPos );
  148. // no item created, so wait for another click
  149. if( !newItem )
  150. continue;
  151. controls()->CaptureCursor( true );
  152. controls()->SetAutoPan( true );
  153. }
  154. else
  155. {
  156. BOARD_ITEM* newBoardItem = newItem.release();
  157. EDA_ITEM_FLAGS oldFlags = newBoardItem->GetFlags();
  158. newBoardItem->ClearFlags();
  159. if( !aPlacer->PlaceItem( newBoardItem, commit ) )
  160. {
  161. newBoardItem->SetFlags( oldFlags );
  162. newItem.reset( newBoardItem );
  163. continue;
  164. }
  165. preview.Clear();
  166. commit.Push( aCommitMessage );
  167. controls()->CaptureCursor( false );
  168. controls()->SetAutoPan( false );
  169. controls()->ShowCursor( true );
  170. if( !( aOptions & IPO_REPEAT ) )
  171. break;
  172. if( aOptions & IPO_SINGLE_CLICK )
  173. makeNewItem( controls()->GetCursorPosition() );
  174. setCursor();
  175. }
  176. }
  177. else if( evt->IsClick( BUT_RIGHT ) )
  178. {
  179. m_menu.ShowContextMenu( selection() );
  180. }
  181. else if( evt->IsAction( &PCB_ACTIONS::trackViaSizeChanged ) )
  182. {
  183. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  184. }
  185. else if( newItem && evt->Category() == TC_COMMAND )
  186. {
  187. /*
  188. * Handle any events that can affect the item as we move it around
  189. */
  190. if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) && ( aOptions & IPO_ROTATE ) )
  191. {
  192. EDA_ANGLE rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *frame(), *evt );
  193. newItem->Rotate( newItem->GetPosition(), rotationAngle );
  194. view()->Update( &preview );
  195. }
  196. else if( evt->IsAction( &PCB_ACTIONS::flip ) && ( aOptions & IPO_FLIP ) )
  197. {
  198. newItem->Flip( newItem->GetPosition(), frame()->GetPcbNewSettings()->m_FlipLeftRight );
  199. view()->Update( &preview );
  200. }
  201. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  202. {
  203. frame()->OnEditItemRequest( newItem.get() );
  204. // Notify other tools of the changes
  205. m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
  206. }
  207. else if( evt->IsAction( &ACTIONS::refreshPreview ) )
  208. {
  209. preview.Clear();
  210. newItem.reset();
  211. makeNewItem( cursorPos );
  212. aPlacer->SnapItem( newItem.get() );
  213. view()->Update( &preview );
  214. }
  215. else
  216. {
  217. evt->SetPassEvent();
  218. }
  219. }
  220. else if( newItem && evt->IsMotion() )
  221. {
  222. // track the cursor
  223. newItem->SetPosition( cursorPos );
  224. aPlacer->SnapItem( newItem.get() );
  225. // Show a preview of the item
  226. view()->Update( &preview );
  227. }
  228. else
  229. {
  230. evt->SetPassEvent();
  231. }
  232. }
  233. view()->Remove( &preview );
  234. frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  235. controls()->SetAutoPan( false );
  236. controls()->CaptureCursor( false );
  237. controls()->ForceCursorPosition( false );
  238. }
  239. bool PCB_TOOL_BASE::Init()
  240. {
  241. // A basic context manu. Many (but not all) tools will choose to override this.
  242. CONDITIONAL_MENU& ctxMenu = m_menu.GetMenu();
  243. // cancel current tool goes in main context menu at the top if present
  244. ctxMenu.AddItem( ACTIONS::cancelInteractive, SELECTION_CONDITIONS::ShowAlways, 1 );
  245. ctxMenu.AddSeparator( 1 );
  246. // Finally, add the standard zoom/grid items
  247. getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( m_menu );
  248. return true;
  249. }
  250. void PCB_TOOL_BASE::Reset( RESET_REASON aReason )
  251. {
  252. }
  253. void PCB_TOOL_BASE::setTransitions()
  254. {
  255. }
  256. PCBNEW_SETTINGS::DISPLAY_OPTIONS& PCB_TOOL_BASE::displayOptions() const
  257. {
  258. return frame()->GetPcbNewSettings()->m_Display;
  259. }
  260. PCB_DRAW_PANEL_GAL* PCB_TOOL_BASE::canvas() const
  261. {
  262. return static_cast<PCB_DRAW_PANEL_GAL*>( frame()->GetCanvas() );
  263. }
  264. const PCB_SELECTION& PCB_TOOL_BASE::selection() const
  265. {
  266. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  267. return selTool->GetSelection();
  268. }
  269. PCB_SELECTION& PCB_TOOL_BASE::selection()
  270. {
  271. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  272. return selTool->GetSelection();
  273. }
  274. bool PCB_TOOL_BASE::Is45Limited() const
  275. {
  276. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  277. if( frame()->IsType( FRAME_PCB_EDITOR ) )
  278. return mgr.GetAppSettings<PCBNEW_SETTINGS>()->m_Use45DegreeLimit;
  279. else
  280. return mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>()->m_Use45Limit;
  281. }
  282. void INTERACTIVE_PLACER_BASE::SnapItem( BOARD_ITEM *aItem )
  283. {
  284. // Base implementation performs no snapping
  285. }
  286. bool INTERACTIVE_PLACER_BASE::PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit )
  287. {
  288. aCommit.Add( aItem );
  289. return true;
  290. }