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.

275 lines
8.1 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 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.h"
  24. #include <view/view_controls.h>
  25. #include <view/view.h>
  26. #include <tool/tool_manager.h>
  27. #include <board_commit.h>
  28. #include <class_module.h>
  29. #include <pcb_draw_panel_gal.h>
  30. #include "selection_tool.h"
  31. #include "pcb_actions.h"
  32. #include "tool_event_utils.h"
  33. void PCB_TOOL::doInteractiveItemPlacement( INTERACTIVE_PLACER_BASE* aPlacer,
  34. const wxString& aCommitMessage,
  35. int aOptions )
  36. {
  37. using namespace std::placeholders;
  38. std::unique_ptr<BOARD_ITEM> newItem;
  39. Activate();
  40. BOARD_COMMIT commit( frame() );
  41. GetManager()->RunAction( PCB_ACTIONS::selectionClear, true );
  42. // do not capture or auto-pan until we start placing an item
  43. controls()->ShowCursor( true );
  44. controls()->SetSnapping( true );
  45. // Add a VIEW_GROUP that serves as a preview for the new item
  46. SELECTION preview;
  47. view()->Add( &preview );
  48. aPlacer->m_board = board();
  49. aPlacer->m_frame = frame();
  50. aPlacer->m_modifiers = 0;
  51. if( aOptions & IPO_SINGLE_CLICK && !( aOptions & IPO_PROPERTIES ) )
  52. {
  53. VECTOR2I cursorPos = controls()->GetCursorPosition();
  54. newItem = aPlacer->CreateItem();
  55. if( newItem )
  56. {
  57. newItem->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  58. preview.Add( newItem.get() );
  59. }
  60. }
  61. // Main loop: keep receiving events
  62. while( OPT_TOOL_EVENT evt = Wait() )
  63. {
  64. VECTOR2I cursorPos = controls()->GetCursorPosition();
  65. aPlacer->m_modifiers = evt->Modifier();
  66. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  67. {
  68. if( newItem )
  69. {
  70. // Delete the old item and have another try
  71. newItem = nullptr;
  72. preview.Clear();
  73. if( aOptions & IPO_SINGLE_CLICK )
  74. break;
  75. controls()->SetAutoPan( false );
  76. controls()->CaptureCursor( false );
  77. controls()->ShowCursor( true );
  78. }
  79. else
  80. {
  81. break;
  82. }
  83. if( evt->IsActivate() ) // now finish unconditionally
  84. break;
  85. }
  86. else if( evt->IsClick( BUT_LEFT ) )
  87. {
  88. if( !newItem )
  89. {
  90. // create the item if possible
  91. newItem = aPlacer->CreateItem();
  92. // no item created, so wait for another click
  93. if( !newItem )
  94. continue;
  95. controls()->CaptureCursor( true );
  96. controls()->SetAutoPan( true );
  97. newItem->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  98. preview.Add( newItem.get() );
  99. if( newItem->Type() == PCB_MODULE_T )
  100. {
  101. auto module = dyn_cast<MODULE*>( newItem.get() );
  102. // modules have more drawable parts
  103. module->RunOnChildren( std::bind( &KIGFX::VIEW_GROUP::Add, &preview, _1 ) );
  104. }
  105. }
  106. else
  107. {
  108. auto oldFlags = newItem->GetFlags();
  109. newItem->ClearFlags();
  110. if( !aPlacer->PlaceItem( newItem.get(), commit ) )
  111. {
  112. newItem->SetFlags( oldFlags );
  113. continue;
  114. }
  115. preview.Remove( newItem.get() );
  116. if( newItem->Type() == PCB_MODULE_T )
  117. {
  118. auto module = dyn_cast<MODULE*>( newItem.get() );
  119. module->RunOnChildren( std::bind( &KIGFX::VIEW_GROUP::Remove, &preview, _1 ) );
  120. }
  121. newItem.release();
  122. commit.Push( aCommitMessage );
  123. controls()->CaptureCursor( false );
  124. controls()->SetAutoPan( false );
  125. controls()->ShowCursor( true );
  126. if( !( aOptions & IPO_REPEAT ) )
  127. break;
  128. if( aOptions & IPO_SINGLE_CLICK && !( aOptions & IPO_PROPERTIES ) )
  129. {
  130. VECTOR2I pos = controls()->GetCursorPosition();
  131. newItem = aPlacer->CreateItem();
  132. if( newItem )
  133. {
  134. newItem->SetPosition( wxPoint( pos.x, pos.y ) );
  135. preview.Add( newItem.get() );
  136. }
  137. }
  138. }
  139. }
  140. else if( evt->IsClick( BUT_RIGHT ) )
  141. {
  142. m_menu.ShowContextMenu();
  143. }
  144. else if( newItem && evt->Category() == TC_COMMAND )
  145. {
  146. /*
  147. * Handle any events that can affect the item as we move
  148. * it around, eg rotate and flip
  149. */
  150. if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) && ( aOptions & IPO_ROTATE ) )
  151. {
  152. const int rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *frame(), *evt );
  153. newItem->Rotate( newItem->GetPosition(), rotationAngle );
  154. view()->Update( &preview );
  155. }
  156. else if( evt->IsAction( &PCB_ACTIONS::flip ) && ( aOptions & IPO_FLIP ) )
  157. {
  158. newItem->Flip( newItem->GetPosition() );
  159. view()->Update( &preview );
  160. }
  161. }
  162. else if( newItem && evt->IsMotion() )
  163. {
  164. // track the cursor
  165. newItem->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  166. aPlacer->SnapItem( newItem.get() );
  167. // Show a preview of the item
  168. view()->Update( &preview );
  169. }
  170. }
  171. view()->Remove( &preview );
  172. }
  173. bool PCB_TOOL::Init()
  174. {
  175. // A basic context manu. Many (but not all) tools will choose to override this.
  176. auto& ctxMenu = m_menu.GetMenu();
  177. // cancel current tool goes in main context menu at the top if present
  178. ctxMenu.AddItem( ACTIONS::cancelInteractive, SELECTION_CONDITIONS::ShowAlways, 1 );
  179. ctxMenu.AddSeparator( SELECTION_CONDITIONS::ShowAlways, 1 );
  180. // Finally, add the standard zoom/grid items
  181. m_menu.AddStandardSubMenus( *getEditFrame<PCB_BASE_FRAME>() );
  182. return true;
  183. }
  184. void PCB_TOOL::Reset( RESET_REASON aReason )
  185. {
  186. }
  187. void PCB_TOOL::setTransitions()
  188. {
  189. }
  190. PCB_DISPLAY_OPTIONS* PCB_TOOL::displayOptions() const
  191. {
  192. return static_cast<PCB_DISPLAY_OPTIONS*>( frame()->GetDisplayOptions() );
  193. }
  194. PCB_DRAW_PANEL_GAL* PCB_TOOL::canvas() const
  195. {
  196. return static_cast<PCB_DRAW_PANEL_GAL*>( frame()->GetGalCanvas() );
  197. }
  198. const SELECTION& PCB_TOOL::selection() const
  199. {
  200. auto selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  201. const auto& selection = selTool->GetSelection();
  202. return selection;
  203. }
  204. SELECTION& PCB_TOOL::selection()
  205. {
  206. auto selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  207. auto& selection = selTool->GetSelection();
  208. return selection;
  209. }
  210. void INTERACTIVE_PLACER_BASE::SnapItem( BOARD_ITEM *aItem )
  211. {
  212. // Base implementation performs no snapping
  213. }
  214. bool INTERACTIVE_PLACER_BASE::PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit )
  215. {
  216. aCommit.Add( aItem );
  217. return true;
  218. }