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.

375 lines
12 KiB

  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 <class_draw_panel_gal.h>
  25. #include <confirm.h>
  26. #include <view/view_controls.h>
  27. #include <tool/tool_manager.h>
  28. #include <bitmaps.h>
  29. #include <drawing_sheet/ds_draw_item.h>
  30. #include <drawing_sheet/ds_data_item.h>
  31. #include "pl_editor_frame.h"
  32. #include "tools/pl_actions.h"
  33. #include "tools/pl_selection_tool.h"
  34. #include "tools/pl_drawing_tools.h"
  35. #include "pgm_base.h"
  36. PL_DRAWING_TOOLS::PL_DRAWING_TOOLS() :
  37. TOOL_INTERACTIVE( "plEditor.InteractiveDrawing" ),
  38. m_frame( nullptr ),
  39. m_selectionTool( nullptr )
  40. {
  41. }
  42. bool PL_DRAWING_TOOLS::Init()
  43. {
  44. m_frame = getEditFrame<PL_EDITOR_FRAME>();
  45. m_selectionTool = m_toolMgr->GetTool<PL_SELECTION_TOOL>();
  46. auto& ctxMenu = m_menu->GetMenu();
  47. // cancel current tool goes in main context menu at the top if present
  48. ctxMenu.AddItem( ACTIONS::cancelInteractive, SELECTION_CONDITIONS::ShowAlways, 1 );
  49. ctxMenu.AddSeparator( 1 );
  50. // Finally, add the standard zoom/grid items
  51. m_frame->AddStandardSubMenus( *m_menu.get() );
  52. return true;
  53. }
  54. void PL_DRAWING_TOOLS::Reset( RESET_REASON aReason )
  55. {
  56. if( aReason == MODEL_RELOAD )
  57. m_frame = getEditFrame<PL_EDITOR_FRAME>();
  58. }
  59. int PL_DRAWING_TOOLS::PlaceItem( const TOOL_EVENT& aEvent )
  60. {
  61. DS_DATA_ITEM::DS_ITEM_TYPE type = aEvent.Parameter<DS_DATA_ITEM::DS_ITEM_TYPE>();
  62. VECTOR2I cursorPos;
  63. DS_DRAW_ITEM_BASE* item = nullptr;
  64. bool isText = aEvent.IsAction( &PL_ACTIONS::placeText );
  65. m_toolMgr->RunAction( PL_ACTIONS::clearSelection );
  66. m_frame->PushTool( aEvent );
  67. auto setCursor =
  68. [&]()
  69. {
  70. if( item )
  71. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PLACE );
  72. else if( isText )
  73. {
  74. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
  75. }
  76. else if( aEvent.IsAction( &PL_ACTIONS::placeImage ) )
  77. {
  78. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  79. }
  80. else
  81. {
  82. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  83. }
  84. };
  85. auto cleanup =
  86. [&] ()
  87. {
  88. m_toolMgr->RunAction( PL_ACTIONS::clearSelection );
  89. item = nullptr;
  90. // There's nothing to roll-back, but we still need to pop the undo stack
  91. // This also deletes the item being placed.
  92. m_frame->RollbackFromUndo();
  93. };
  94. Activate();
  95. // Must be done after Activate() so that it gets set into the correct context
  96. getViewControls()->ShowCursor( true );
  97. // Set initial cursor
  98. setCursor();
  99. if( aEvent.HasPosition() )
  100. m_toolMgr->PrimeTool( aEvent.Position() );
  101. // Main loop: keep receiving events
  102. while( TOOL_EVENT* evt = Wait() )
  103. {
  104. setCursor();
  105. cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
  106. if( evt->IsCancelInteractive() || ( item && evt->IsAction( &ACTIONS::undo ) ) )
  107. {
  108. if( item )
  109. {
  110. cleanup();
  111. }
  112. else
  113. {
  114. m_frame->PopTool( aEvent );
  115. break;
  116. }
  117. }
  118. else if( evt->IsActivate() )
  119. {
  120. if( item )
  121. cleanup();
  122. if( evt->IsMoveTool() )
  123. {
  124. // leave ourselves on the stack so we come back after the move
  125. break;
  126. }
  127. else
  128. {
  129. m_frame->PopTool( aEvent );
  130. break;
  131. }
  132. }
  133. else if( evt->IsClick( BUT_LEFT ) )
  134. {
  135. bool placeItem = true;
  136. if( !item )
  137. {
  138. DS_DATA_ITEM* dataItem = m_frame->AddDrawingSheetItem( type );
  139. if( dataItem ) // dataItem = nullptr can happens if the command was cancelled
  140. {
  141. m_frame->SaveCopyInUndoList();
  142. m_toolMgr->RunAction( PL_ACTIONS::clearSelection );
  143. item = dataItem->GetDrawItems()[0];
  144. item->SetFlags( IS_NEW | IS_MOVING );
  145. // Select the item but don't inform other tools (to prevent the Properties
  146. // panel from updating the item before it has been placed)
  147. m_selectionTool->AddItemToSel( item, true );
  148. // update the cursor so it looks correct before another event
  149. setCursor();
  150. // Text is a single-click-place; all others are first-click-creates,
  151. // second-click-places.
  152. placeItem = dataItem->GetType() == DS_DATA_ITEM::DS_TEXT;
  153. }
  154. }
  155. if( item && placeItem )
  156. {
  157. item->GetPeer()->MoveStartPointToIU( cursorPos );
  158. item->SetPosition( item->GetPeer()->GetStartPosIU( 0 ) );
  159. item->ClearEditFlags();
  160. getView()->Update( item );
  161. // Now we re-select and inform other tools, so that the Properties panel
  162. // is updated.
  163. m_toolMgr->RunAction( PL_ACTIONS::clearSelection );
  164. m_selectionTool->AddItemToSel( item, false );
  165. item = nullptr;
  166. m_frame->OnModify();
  167. }
  168. }
  169. else if( evt->IsClick( BUT_RIGHT ) )
  170. {
  171. // Warp after context menu only if dragging...
  172. if( !item )
  173. m_toolMgr->VetoContextMenuMouseWarp();
  174. m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
  175. }
  176. else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  177. {
  178. item->GetPeer()->MoveStartPointToIU( cursorPos );
  179. item->SetPosition( item->GetPeer()->GetStartPosIU( 0 ) );
  180. getView()->Update( item );
  181. }
  182. else
  183. {
  184. evt->SetPassEvent();
  185. }
  186. // Enable autopanning and cursor capture only when there is an item to be placed
  187. getViewControls()->SetAutoPan( item != nullptr );
  188. getViewControls()->CaptureCursor( item != nullptr );
  189. }
  190. getViewControls()->SetAutoPan( false );
  191. getViewControls()->CaptureCursor( false );
  192. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  193. return 0;
  194. }
  195. int PL_DRAWING_TOOLS::DrawShape( const TOOL_EVENT& aEvent )
  196. {
  197. DS_DATA_ITEM::DS_ITEM_TYPE type = aEvent.Parameter<DS_DATA_ITEM::DS_ITEM_TYPE>();
  198. DS_DRAW_ITEM_BASE* item = nullptr;
  199. // We might be running as the same shape in another co-routine. Make sure that one
  200. // gets whacked.
  201. m_toolMgr->DeactivateTool();
  202. m_toolMgr->RunAction( PL_ACTIONS::clearSelection );
  203. m_frame->PushTool( aEvent );
  204. auto setCursor =
  205. [&]()
  206. {
  207. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
  208. };
  209. Activate();
  210. // Must be done after Activate() so that it gets set into the correct context
  211. getViewControls()->ShowCursor( true );
  212. // Set initial cursor
  213. setCursor();
  214. if( aEvent.HasPosition() )
  215. m_toolMgr->PrimeTool( aEvent.Position() );
  216. // Main loop: keep receiving events
  217. while( TOOL_EVENT* evt = Wait() )
  218. {
  219. setCursor();
  220. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
  221. if( evt->IsCancelInteractive() || ( item && evt->IsAction( &ACTIONS::undo ) ) )
  222. {
  223. m_toolMgr->RunAction( PL_ACTIONS::clearSelection );
  224. if( item )
  225. {
  226. item = nullptr;
  227. // Pop the undo stack and delete the item being placed
  228. m_frame->RollbackFromUndo();
  229. }
  230. else
  231. {
  232. break;
  233. }
  234. }
  235. else if( evt->IsActivate() )
  236. {
  237. if( item )
  238. {
  239. item = nullptr;
  240. // Pop the undo stack and delete the item being placed
  241. m_frame->RollbackFromUndo();
  242. }
  243. if( evt->IsPointEditor() || evt->IsMoveTool() )
  244. {
  245. // leave ourselves on the stack so we come back after the move
  246. break;
  247. }
  248. }
  249. else if( evt->IsClick( BUT_LEFT ) )
  250. {
  251. if( !item ) // start drawing
  252. {
  253. m_frame->SaveCopyInUndoList();
  254. m_toolMgr->RunAction( PL_ACTIONS::clearSelection );
  255. DS_DATA_ITEM* dataItem = m_frame->AddDrawingSheetItem( type );
  256. dataItem->MoveToIU( cursorPos );
  257. item = dataItem->GetDrawItems()[0];
  258. item->SetFlags( IS_NEW );
  259. // Select the item but don't inform other tools (to prevent the Properties
  260. // panel from updating the item before it has been placed)
  261. m_selectionTool->AddItemToSel( item, true );
  262. }
  263. else // finish drawing
  264. {
  265. // Now we re-select and inform other tools, so that the Properties panel
  266. // is updated.
  267. m_toolMgr->RunAction( PL_ACTIONS::clearSelection );
  268. m_selectionTool->AddItemToSel( item, false );
  269. item->ClearEditFlags();
  270. item = nullptr;
  271. // Activate point editor immediately to allow resizing of the item just created
  272. m_toolMgr->RunAction( ACTIONS::activatePointEditor );
  273. m_frame->OnModify();
  274. }
  275. }
  276. else if( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() )
  277. {
  278. if( item )
  279. {
  280. item->GetPeer()->MoveEndPointToIU( cursorPos );
  281. item->SetEnd( item->GetPeer()->GetEndPosIU( 0 ) );
  282. getView()->Update( item );
  283. }
  284. }
  285. else if( evt->IsClick( BUT_RIGHT ) )
  286. {
  287. // Warp after context menu only if dragging...
  288. if( !item )
  289. m_toolMgr->VetoContextMenuMouseWarp();
  290. m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
  291. }
  292. else
  293. {
  294. evt->SetPassEvent();
  295. }
  296. // Enable autopanning and cursor capture only when there is a shape being drawn
  297. getViewControls()->SetAutoPan( item != nullptr );
  298. getViewControls()->CaptureCursor( item != nullptr );
  299. }
  300. getViewControls()->SetAutoPan( false );
  301. getViewControls()->CaptureCursor( false );
  302. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  303. m_frame->PopTool( aEvent );
  304. return 0;
  305. }
  306. void PL_DRAWING_TOOLS::setTransitions()
  307. {
  308. Go( &PL_DRAWING_TOOLS::DrawShape, PL_ACTIONS::drawLine.MakeEvent() );
  309. Go( &PL_DRAWING_TOOLS::DrawShape, PL_ACTIONS::drawRectangle.MakeEvent() );
  310. Go( &PL_DRAWING_TOOLS::PlaceItem, PL_ACTIONS::placeText.MakeEvent() );
  311. Go( &PL_DRAWING_TOOLS::PlaceItem, PL_ACTIONS::placeImage.MakeEvent() );
  312. }