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.

351 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 The 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( 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. RECURSE_MODE::NO_RECURSE );
  73. }
  74. };
  75. if( aOptions & IPO_SINGLE_CLICK )
  76. makeNewItem( controls()->GetCursorPosition() );
  77. auto setCursor =
  78. [&]()
  79. {
  80. if( !newItem )
  81. frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  82. else
  83. frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
  84. };
  85. // Set initial cursor
  86. setCursor();
  87. // Main loop: keep receiving events
  88. while( TOOL_EVENT* evt = Wait() )
  89. {
  90. setCursor();
  91. grid.SetSnap( false ); // Interactive placement tools need to set their own item snaps
  92. grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
  93. VECTOR2I cursorPos = grid.BestSnapAnchor( controls()->GetMousePosition(), nullptr );
  94. aPlacer->m_modifiers = evt->Modifier();
  95. auto cleanup =
  96. [&] ()
  97. {
  98. newItem = nullptr;
  99. preview.Clear();
  100. view()->Update( &preview );
  101. controls()->SetAutoPan( false );
  102. controls()->CaptureCursor( false );
  103. controls()->ShowCursor( true );
  104. controls()->ForceCursorPosition( false );
  105. };
  106. if( evt->IsCancelInteractive() )
  107. {
  108. if( aOptions & IPO_SINGLE_CLICK )
  109. {
  110. cleanup();
  111. frame()->PopTool( aTool );
  112. break;
  113. }
  114. else if( newItem )
  115. {
  116. cleanup();
  117. }
  118. else
  119. {
  120. frame()->PopTool( aTool );
  121. break;
  122. }
  123. }
  124. else if( evt->IsActivate() )
  125. {
  126. if( newItem )
  127. cleanup();
  128. if( evt->IsPointEditor() )
  129. {
  130. // don't exit (the point editor runs in the background)
  131. }
  132. else if( evt->IsMoveTool() )
  133. {
  134. // leave ourselves on the stack so we come back after the move
  135. break;
  136. }
  137. else
  138. {
  139. frame()->PopTool( aTool );
  140. break;
  141. }
  142. }
  143. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  144. {
  145. if( !newItem )
  146. {
  147. // create the item if possible
  148. makeNewItem( cursorPos );
  149. // no item created, so wait for another click
  150. if( !newItem )
  151. continue;
  152. controls()->CaptureCursor( true );
  153. controls()->SetAutoPan( true );
  154. }
  155. else
  156. {
  157. BOARD_ITEM* newBoardItem = newItem.release();
  158. EDA_ITEM_FLAGS oldFlags = newBoardItem->GetFlags();
  159. newBoardItem->ClearFlags();
  160. if( !aPlacer->PlaceItem( newBoardItem, commit ) )
  161. {
  162. newBoardItem->SetFlags( oldFlags );
  163. newItem.reset( newBoardItem );
  164. continue;
  165. }
  166. preview.Clear();
  167. commit.Push( aCommitMessage );
  168. controls()->CaptureCursor( false );
  169. controls()->SetAutoPan( false );
  170. controls()->ShowCursor( true );
  171. if( !( aOptions & IPO_REPEAT ) )
  172. break;
  173. if( aOptions & IPO_SINGLE_CLICK )
  174. makeNewItem( controls()->GetCursorPosition() );
  175. setCursor();
  176. }
  177. }
  178. else if( evt->IsClick( BUT_RIGHT ) )
  179. {
  180. m_menu->ShowContextMenu( selection() );
  181. }
  182. else if( evt->IsAction( &PCB_ACTIONS::trackViaSizeChanged ) )
  183. {
  184. m_toolMgr->PostAction( ACTIONS::refreshPreview );
  185. }
  186. else if( newItem && evt->Category() == TC_COMMAND )
  187. {
  188. /*
  189. * Handle any events that can affect the item as we move it around
  190. */
  191. if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) && ( aOptions & IPO_ROTATE ) )
  192. {
  193. EDA_ANGLE rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *frame(), *evt );
  194. newItem->Rotate( newItem->GetPosition(), rotationAngle );
  195. view()->Update( &preview );
  196. }
  197. else if( evt->IsAction( &PCB_ACTIONS::flip ) && ( aOptions & IPO_FLIP ) )
  198. {
  199. newItem->Flip( newItem->GetPosition(), frame()->GetPcbNewSettings()->m_FlipDirection );
  200. view()->Update( &preview );
  201. }
  202. else if( evt->IsAction( &PCB_ACTIONS::properties ) )
  203. {
  204. frame()->OnEditItemRequest( newItem.get() );
  205. // Notify other tools of the changes
  206. m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
  207. }
  208. else if( evt->IsAction( &ACTIONS::refreshPreview ) )
  209. {
  210. preview.Clear();
  211. newItem.reset();
  212. makeNewItem( cursorPos );
  213. aPlacer->SnapItem( newItem.get() );
  214. view()->Update( &preview );
  215. }
  216. else
  217. {
  218. evt->SetPassEvent();
  219. }
  220. }
  221. else if( newItem && evt->IsMotion() )
  222. {
  223. // track the cursor
  224. newItem->SetPosition( cursorPos );
  225. aPlacer->SnapItem( newItem.get() );
  226. // Show a preview of the item
  227. view()->Update( &preview );
  228. }
  229. else
  230. {
  231. evt->SetPassEvent();
  232. }
  233. }
  234. view()->Remove( &preview );
  235. frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  236. controls()->SetAutoPan( false );
  237. controls()->CaptureCursor( false );
  238. controls()->ForceCursorPosition( false );
  239. }
  240. bool PCB_TOOL_BASE::Init()
  241. {
  242. // A basic context manu. Many (but not all) tools will choose to override this.
  243. CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
  244. // cancel current tool goes in main context menu at the top if present
  245. ctxMenu.AddItem( ACTIONS::cancelInteractive, SELECTION_CONDITIONS::ShowAlways, 1 );
  246. ctxMenu.AddSeparator( 1 );
  247. // Finally, add the standard zoom/grid items
  248. getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
  249. return true;
  250. }
  251. void PCB_TOOL_BASE::Reset( RESET_REASON aReason )
  252. {
  253. }
  254. void PCB_TOOL_BASE::setTransitions()
  255. {
  256. }
  257. PCBNEW_SETTINGS::DISPLAY_OPTIONS& PCB_TOOL_BASE::displayOptions() const
  258. {
  259. return frame<PCB_BASE_FRAME>()->GetPcbNewSettings()->m_Display;
  260. }
  261. PCB_DRAW_PANEL_GAL* PCB_TOOL_BASE::canvas() const
  262. {
  263. return static_cast<PCB_DRAW_PANEL_GAL*>( frame<PCB_BASE_FRAME>()->GetCanvas() );
  264. }
  265. const PCB_SELECTION& PCB_TOOL_BASE::selection() const
  266. {
  267. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  268. return selTool->GetSelection();
  269. }
  270. PCB_SELECTION& PCB_TOOL_BASE::selection()
  271. {
  272. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  273. return selTool->GetSelection();
  274. }
  275. bool PCB_TOOL_BASE::Is45Limited() const
  276. {
  277. if( frame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
  278. return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
  279. else
  280. return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->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. }