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.

486 lines
15 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 (C) 2019 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 <ee_actions.h>
  25. #include <lib_edit_frame.h>
  26. #include <sch_view.h>
  27. #include <confirm.h>
  28. #include <view/view_controls.h>
  29. #include <view/view.h>
  30. #include <tool/tool_manager.h>
  31. #include <tools/ee_selection_tool.h>
  32. #include <tools/lib_drawing_tools.h>
  33. #include <tools/lib_pin_tool.h>
  34. #include <class_libentry.h>
  35. #include <bitmaps.h>
  36. #include <lib_text.h>
  37. #include <dialogs/dialog_lib_edit_text.h>
  38. #include <lib_arc.h>
  39. #include <lib_circle.h>
  40. #include <lib_polyline.h>
  41. #include <lib_rectangle.h>
  42. #include "ee_point_editor.h"
  43. static void* g_lastPinWeakPtr;
  44. LIB_DRAWING_TOOLS::LIB_DRAWING_TOOLS() :
  45. EE_TOOL_BASE<LIB_EDIT_FRAME>( "eeschema.SymbolDrawing" )
  46. {
  47. }
  48. bool LIB_DRAWING_TOOLS::Init()
  49. {
  50. EE_TOOL_BASE::Init();
  51. auto isDrawingCondition = [] ( const SELECTION& aSel ) {
  52. LIB_ITEM* item = (LIB_ITEM*) aSel.Front();
  53. return item && item->IsNew();
  54. };
  55. m_menu.GetMenu().AddItem( EE_ACTIONS::finishDrawing, isDrawingCondition, 2 );
  56. return true;
  57. }
  58. int LIB_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
  59. {
  60. KICAD_T type = aEvent.Parameter<KICAD_T>();
  61. LIB_PIN_TOOL* pinTool = type == LIB_PIN_T ? m_toolMgr->GetTool<LIB_PIN_TOOL>() : nullptr;
  62. VECTOR2I cursorPos;
  63. EDA_ITEM* item = nullptr;
  64. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  65. getViewControls()->ShowCursor( true );
  66. std::string tool = aEvent.GetCommandStr().get();
  67. m_frame->PushTool( tool );
  68. Activate();
  69. // Prime the pump
  70. if( aEvent.HasPosition() )
  71. m_toolMgr->RunAction( ACTIONS::cursorClick );
  72. // Main loop: keep receiving events
  73. while( TOOL_EVENT* evt = Wait() )
  74. {
  75. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  76. cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  77. auto cleanup = [&] () {
  78. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  79. m_view->ClearPreview();
  80. delete item;
  81. item = nullptr;
  82. };
  83. if( evt->IsCancelInteractive() )
  84. {
  85. if( item )
  86. cleanup();
  87. else
  88. {
  89. m_frame->PopTool( tool );
  90. break;
  91. }
  92. }
  93. else if( evt->IsActivate() )
  94. {
  95. if( item )
  96. cleanup();
  97. if( evt->IsMoveTool() )
  98. {
  99. // leave ourselves on the stack so we come back after the move
  100. break;
  101. }
  102. else
  103. {
  104. m_frame->PopTool( tool );
  105. break;
  106. }
  107. }
  108. else if( evt->IsClick( BUT_LEFT ) )
  109. {
  110. LIB_PART* part = m_frame->GetCurPart();
  111. if( !part )
  112. continue;
  113. // First click creates...
  114. if( !item )
  115. {
  116. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  117. switch( type )
  118. {
  119. case LIB_PIN_T:
  120. {
  121. item = pinTool->CreatePin( wxPoint( cursorPos.x, -cursorPos.y ), part );
  122. g_lastPinWeakPtr = item;
  123. break;
  124. }
  125. case LIB_TEXT_T:
  126. {
  127. LIB_TEXT* text = new LIB_TEXT( part );
  128. text->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
  129. text->SetTextSize( wxSize( m_frame->g_LastTextSize, m_frame->g_LastTextSize ) );
  130. text->SetTextAngle( m_frame->g_LastTextAngle );
  131. DIALOG_LIB_EDIT_TEXT dlg( m_frame, text );
  132. if( dlg.ShowModal() != wxID_OK )
  133. delete text;
  134. else
  135. item = text;
  136. break;
  137. }
  138. default:
  139. wxFAIL_MSG( "TwoClickPlace(): unknown type" );
  140. }
  141. // Restore cursor after dialog
  142. getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
  143. if( item )
  144. {
  145. item->SetFlags( IS_NEW | IS_MOVED );
  146. m_view->ClearPreview();
  147. m_view->AddToPreview( item->Clone() );
  148. m_selectionTool->AddItemToSel( item );
  149. }
  150. getViewControls()->SetCursorPosition( cursorPos, false );
  151. }
  152. // ... and second click places:
  153. else
  154. {
  155. m_frame->SaveCopyInUndoList( part );
  156. switch( item->Type() )
  157. {
  158. case LIB_PIN_T:
  159. pinTool->PlacePin( (LIB_PIN*) item );
  160. break;
  161. case LIB_TEXT_T:
  162. part->AddDrawItem( (LIB_TEXT*) item );
  163. break;
  164. default:
  165. wxFAIL_MSG( "TwoClickPlace(): unknown type" );
  166. }
  167. item->ClearEditFlags();
  168. item = nullptr;
  169. m_view->ClearPreview();
  170. m_frame->RebuildView();
  171. m_frame->OnModify();
  172. }
  173. }
  174. else if( evt->IsClick( BUT_RIGHT ) )
  175. {
  176. // Warp after context menu only if dragging...
  177. if( !item )
  178. m_toolMgr->VetoContextMenuMouseWarp();
  179. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  180. }
  181. else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  182. {
  183. static_cast<LIB_ITEM*>( item )->SetPosition( wxPoint( cursorPos.x, -cursorPos.y ) );
  184. m_view->ClearPreview();
  185. m_view->AddToPreview( item->Clone() );
  186. }
  187. else
  188. evt->SetPassEvent();
  189. // Enable autopanning and cursor capture only when there is an item to be placed
  190. getViewControls()->SetAutoPan( item != nullptr );
  191. getViewControls()->CaptureCursor( item != nullptr );
  192. }
  193. return 0;
  194. }
  195. int LIB_DRAWING_TOOLS::DrawShape( const TOOL_EVENT& aEvent )
  196. {
  197. KICAD_T type = aEvent.Parameter<KICAD_T>();
  198. EE_POINT_EDITOR* pointEditor = m_toolMgr->GetTool<EE_POINT_EDITOR>();
  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( EE_ACTIONS::clearSelection, true );
  203. getViewControls()->ShowCursor( true );
  204. std::string tool = aEvent.GetCommandStr().get();
  205. m_frame->PushTool( tool );
  206. Activate();
  207. LIB_PART* part = m_frame->GetCurPart();
  208. LIB_ITEM* item = nullptr;
  209. // Prime the pump
  210. if( aEvent.HasPosition() )
  211. m_toolMgr->RunAction( ACTIONS::cursorClick );
  212. // Main loop: keep receiving events
  213. while( TOOL_EVENT* evt = Wait() )
  214. {
  215. if( !pointEditor->HasPoint() )
  216. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  217. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  218. auto cleanup = [&] () {
  219. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  220. m_view->ClearPreview();
  221. delete item;
  222. item = nullptr;
  223. };
  224. if( evt->IsCancelInteractive() )
  225. {
  226. if( item )
  227. cleanup();
  228. else
  229. {
  230. m_frame->PopTool( tool );
  231. break;
  232. }
  233. }
  234. else if( evt->IsActivate() )
  235. {
  236. if( item )
  237. cleanup();
  238. if( evt->IsPointEditor() )
  239. {
  240. // don't exit (the point editor runs in the background)
  241. }
  242. else if( evt->IsMoveTool() )
  243. {
  244. // leave ourselves on the stack so we come back after the move
  245. break;
  246. }
  247. else
  248. {
  249. m_frame->PopTool( tool );
  250. break;
  251. }
  252. }
  253. else if( evt->IsClick( BUT_LEFT ) && !item )
  254. {
  255. if( !part )
  256. continue;
  257. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  258. switch( type )
  259. {
  260. case LIB_ARC_T: item = new LIB_ARC( part ); break;
  261. case LIB_CIRCLE_T: item = new LIB_CIRCLE( part ); break;
  262. case LIB_POLYLINE_T: item = new LIB_POLYLINE( part ); break;
  263. case LIB_RECTANGLE_T: item = new LIB_RECTANGLE( part ); break;
  264. default: break; // keep compiler quiet
  265. }
  266. wxASSERT( item );
  267. item->SetWidth( LIB_EDIT_FRAME::g_LastLineWidth );
  268. item->SetFillMode( LIB_EDIT_FRAME::g_LastFillStyle );
  269. item->SetFlags( IS_NEW );
  270. item->BeginEdit( wxPoint( cursorPos.x, -cursorPos.y ) );
  271. if( m_frame->m_DrawSpecificUnit )
  272. item->SetUnit( m_frame->GetUnit() );
  273. if( m_frame->m_DrawSpecificConvert )
  274. item->SetConvert( m_frame->GetConvert() );
  275. m_selectionTool->AddItemToSel( item );
  276. }
  277. else if( item && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
  278. || evt->IsAction( &EE_ACTIONS::finishDrawing ) ) )
  279. {
  280. if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &EE_ACTIONS::finishDrawing )
  281. || !item->ContinueEdit( wxPoint( cursorPos.x, -cursorPos.y ) ) )
  282. {
  283. item->EndEdit();
  284. item->ClearEditFlags();
  285. m_view->ClearPreview();
  286. m_frame->SaveCopyInUndoList( part );
  287. part->AddDrawItem( item );
  288. item = nullptr;
  289. m_frame->RebuildView();
  290. m_frame->OnModify();
  291. m_toolMgr->RunAction( ACTIONS::activatePointEditor );
  292. }
  293. }
  294. else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  295. {
  296. item->CalcEdit( wxPoint( cursorPos.x, -cursorPos.y) );
  297. m_view->ClearPreview();
  298. m_view->AddToPreview( item->Clone() );
  299. }
  300. else if( evt->IsDblClick( BUT_LEFT ) && !item )
  301. {
  302. m_toolMgr->RunAction( EE_ACTIONS::properties, true );
  303. }
  304. else if( evt->IsClick( BUT_RIGHT ) )
  305. {
  306. // Warp after context menu only if dragging...
  307. if( !item )
  308. m_toolMgr->VetoContextMenuMouseWarp();
  309. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  310. }
  311. else
  312. evt->SetPassEvent();
  313. // Enable autopanning and cursor capture only when there is a shape being drawn
  314. getViewControls()->SetAutoPan( item != nullptr );
  315. getViewControls()->CaptureCursor( item != nullptr );
  316. }
  317. return 0;
  318. }
  319. int LIB_DRAWING_TOOLS::PlaceAnchor( const TOOL_EVENT& aEvent )
  320. {
  321. getViewControls()->ShowCursor( true );
  322. getViewControls()->SetSnapping( true );
  323. std::string tool = aEvent.GetCommandStr().get();
  324. m_frame->PushTool( tool );
  325. Activate();
  326. // Main loop: keep receiving events
  327. while( TOOL_EVENT* evt = Wait() )
  328. {
  329. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_BULLSEYE );
  330. if( evt->IsCancelInteractive() )
  331. {
  332. m_frame->PopTool( tool );
  333. break;
  334. }
  335. else if( evt->IsActivate() )
  336. {
  337. m_frame->PopTool( tool );
  338. break;
  339. }
  340. else if( evt->IsClick( BUT_LEFT ) )
  341. {
  342. LIB_PART* part = m_frame->GetCurPart();
  343. if( !part )
  344. continue;
  345. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  346. wxPoint offset( -cursorPos.x, cursorPos.y );
  347. part->SetOffset( offset );
  348. // Refresh the view without changing the viewport
  349. auto center = m_view->GetCenter();
  350. center.x += offset.x;
  351. center.y -= offset.y;
  352. m_view->SetCenter( center );
  353. m_view->RecacheAllItems();
  354. m_frame->OnModify();
  355. }
  356. else if( evt->IsClick( BUT_RIGHT ) )
  357. {
  358. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  359. }
  360. else
  361. evt->SetPassEvent();
  362. }
  363. return 0;
  364. }
  365. int LIB_DRAWING_TOOLS::RepeatDrawItem( const TOOL_EVENT& aEvent )
  366. {
  367. LIB_PIN_TOOL* pinTool = m_toolMgr->GetTool<LIB_PIN_TOOL>();
  368. LIB_PART* part = m_frame->GetCurPart();
  369. LIB_PIN* sourcePin = nullptr;
  370. if( !part )
  371. return 0;
  372. // See if we have a pin matching our weak ptr
  373. for( LIB_PIN* test = part->GetNextPin(); test; test = part->GetNextPin( test ) )
  374. {
  375. if( (void*) test == g_lastPinWeakPtr )
  376. sourcePin = test;
  377. }
  378. if( sourcePin )
  379. {
  380. LIB_PIN* pin = pinTool->RepeatPin( sourcePin );
  381. g_lastPinWeakPtr = pin;
  382. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  383. if( pin )
  384. m_toolMgr->RunAction( EE_ACTIONS::addItemToSel, true, pin );
  385. }
  386. return 0;
  387. }
  388. void LIB_DRAWING_TOOLS::setTransitions()
  389. {
  390. Go( &LIB_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSymbolPin.MakeEvent() );
  391. Go( &LIB_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSymbolText.MakeEvent() );
  392. Go( &LIB_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolRectangle.MakeEvent() );
  393. Go( &LIB_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolCircle.MakeEvent() );
  394. Go( &LIB_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolArc.MakeEvent() );
  395. Go( &LIB_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolLines.MakeEvent() );
  396. Go( &LIB_DRAWING_TOOLS::PlaceAnchor, EE_ACTIONS::placeSymbolAnchor.MakeEvent() );
  397. Go( &LIB_DRAWING_TOOLS::RepeatDrawItem, EE_ACTIONS::repeatDrawItem.MakeEvent() );
  398. }