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.

478 lines
15 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 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 <ee_actions.h>
  24. #include <lib_edit_frame.h>
  25. #include <sch_view.h>
  26. #include <eeschema_id.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. // Enable autopanning and cursor capture only when there is an item to be placed
  188. getViewControls()->SetAutoPan( item != nullptr );
  189. getViewControls()->CaptureCursor( item != nullptr );
  190. }
  191. return 0;
  192. }
  193. int LIB_DRAWING_TOOLS::DrawShape( const TOOL_EVENT& aEvent )
  194. {
  195. KICAD_T type = aEvent.Parameter<KICAD_T>();
  196. EE_POINT_EDITOR* pointEditor = m_toolMgr->GetTool<EE_POINT_EDITOR>();
  197. // We might be running as the same shape in another co-routine. Make sure that one
  198. // gets whacked.
  199. m_toolMgr->DeactivateTool();
  200. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  201. getViewControls()->ShowCursor( true );
  202. std::string tool = aEvent.GetCommandStr().get();
  203. m_frame->PushTool( tool );
  204. Activate();
  205. LIB_PART* part = m_frame->GetCurPart();
  206. LIB_ITEM* item = nullptr;
  207. // Prime the pump
  208. if( aEvent.HasPosition() )
  209. m_toolMgr->RunAction( ACTIONS::cursorClick );
  210. // Main loop: keep receiving events
  211. while( TOOL_EVENT* evt = Wait() )
  212. {
  213. if( !pointEditor->HasPoint() )
  214. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  215. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  216. auto cleanup = [&] () {
  217. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  218. m_view->ClearPreview();
  219. delete item;
  220. item = nullptr;
  221. };
  222. if( evt->IsCancelInteractive() )
  223. {
  224. if( item )
  225. cleanup();
  226. else
  227. {
  228. m_frame->PopTool( tool );
  229. break;
  230. }
  231. }
  232. else if( evt->IsActivate() )
  233. {
  234. if( item )
  235. cleanup();
  236. if( evt->IsPointEditor() )
  237. {
  238. // don't exit (the point editor runs in the background)
  239. }
  240. else if( evt->IsMoveTool() )
  241. {
  242. // leave ourselves on the stack so we come back after the move
  243. break;
  244. }
  245. else
  246. {
  247. m_frame->PopTool( tool );
  248. break;
  249. }
  250. }
  251. else if( evt->IsClick( BUT_LEFT ) && !item )
  252. {
  253. if( !part )
  254. continue;
  255. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  256. switch( type )
  257. {
  258. case LIB_ARC_T: item = new LIB_ARC( part ); break;
  259. case LIB_CIRCLE_T: item = new LIB_CIRCLE( part ); break;
  260. case LIB_POLYLINE_T: item = new LIB_POLYLINE( part ); break;
  261. case LIB_RECTANGLE_T: item = new LIB_RECTANGLE( part ); break;
  262. default: break; // keep compiler quiet
  263. }
  264. wxASSERT( item );
  265. item->SetWidth( LIB_EDIT_FRAME::g_LastLineWidth );
  266. item->SetFillMode( LIB_EDIT_FRAME::g_LastFillStyle );
  267. item->SetFlags( IS_NEW );
  268. item->BeginEdit( wxPoint( cursorPos.x, -cursorPos.y ) );
  269. if( m_frame->m_DrawSpecificUnit )
  270. item->SetUnit( m_frame->GetUnit() );
  271. if( m_frame->m_DrawSpecificConvert )
  272. item->SetConvert( m_frame->GetConvert() );
  273. m_selectionTool->AddItemToSel( item );
  274. }
  275. else if( item && ( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
  276. || evt->IsAction( &EE_ACTIONS::finishDrawing ) ) )
  277. {
  278. if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &EE_ACTIONS::finishDrawing )
  279. || !item->ContinueEdit( wxPoint( cursorPos.x, -cursorPos.y ) ) )
  280. {
  281. item->EndEdit();
  282. item->ClearEditFlags();
  283. m_view->ClearPreview();
  284. m_frame->SaveCopyInUndoList( part );
  285. part->AddDrawItem( item );
  286. item = nullptr;
  287. m_frame->RebuildView();
  288. m_frame->OnModify();
  289. m_toolMgr->RunAction( ACTIONS::activatePointEditor );
  290. }
  291. }
  292. else if( item && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
  293. {
  294. item->CalcEdit( wxPoint( cursorPos.x, -cursorPos.y) );
  295. m_view->ClearPreview();
  296. m_view->AddToPreview( item->Clone() );
  297. }
  298. else if( evt->IsDblClick( BUT_LEFT ) && !item )
  299. {
  300. m_toolMgr->RunAction( EE_ACTIONS::properties, true );
  301. }
  302. else if( evt->IsClick( BUT_RIGHT ) )
  303. {
  304. // Warp after context menu only if dragging...
  305. if( !item )
  306. m_toolMgr->VetoContextMenuMouseWarp();
  307. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  308. }
  309. // Enable autopanning and cursor capture only when there is a shape being drawn
  310. getViewControls()->SetAutoPan( item != nullptr );
  311. getViewControls()->CaptureCursor( item != nullptr );
  312. }
  313. return 0;
  314. }
  315. int LIB_DRAWING_TOOLS::PlaceAnchor( const TOOL_EVENT& aEvent )
  316. {
  317. getViewControls()->ShowCursor( true );
  318. getViewControls()->SetSnapping( true );
  319. std::string tool = aEvent.GetCommandStr().get();
  320. m_frame->PushTool( tool );
  321. Activate();
  322. // Main loop: keep receiving events
  323. while( TOOL_EVENT* evt = Wait() )
  324. {
  325. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_BULLSEYE );
  326. if( evt->IsCancelInteractive() )
  327. {
  328. m_frame->PopTool( tool );
  329. break;
  330. }
  331. else if( evt->IsActivate() )
  332. {
  333. m_frame->PopTool( tool );
  334. break;
  335. }
  336. else if( evt->IsClick( BUT_LEFT ) )
  337. {
  338. LIB_PART* part = m_frame->GetCurPart();
  339. if( !part )
  340. continue;
  341. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  342. wxPoint offset( -cursorPos.x, cursorPos.y );
  343. part->SetOffset( offset );
  344. // Refresh the view without changing the viewport
  345. auto center = m_view->GetCenter();
  346. center.x += offset.x;
  347. center.y -= offset.y;
  348. m_view->SetCenter( center );
  349. m_view->RecacheAllItems();
  350. m_frame->OnModify();
  351. }
  352. else if( evt->IsClick( BUT_RIGHT ) )
  353. {
  354. m_menu.ShowContextMenu( m_selectionTool->GetSelection() );
  355. }
  356. }
  357. return 0;
  358. }
  359. int LIB_DRAWING_TOOLS::RepeatDrawItem( const TOOL_EVENT& aEvent )
  360. {
  361. LIB_PIN_TOOL* pinTool = m_toolMgr->GetTool<LIB_PIN_TOOL>();
  362. LIB_PART* part = m_frame->GetCurPart();
  363. LIB_PIN* sourcePin = nullptr;
  364. if( !part )
  365. return 0;
  366. // See if we have a pin matching our weak ptr
  367. for( LIB_PIN* test = part->GetNextPin(); test; test = part->GetNextPin( test ) )
  368. {
  369. if( (void*) test == g_lastPinWeakPtr )
  370. sourcePin = test;
  371. }
  372. if( sourcePin )
  373. {
  374. LIB_PIN* pin = pinTool->RepeatPin( sourcePin );
  375. g_lastPinWeakPtr = pin;
  376. m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
  377. if( pin )
  378. m_toolMgr->RunAction( EE_ACTIONS::addItemToSel, true, pin );
  379. }
  380. return 0;
  381. }
  382. void LIB_DRAWING_TOOLS::setTransitions()
  383. {
  384. Go( &LIB_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSymbolPin.MakeEvent() );
  385. Go( &LIB_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSymbolText.MakeEvent() );
  386. Go( &LIB_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolRectangle.MakeEvent() );
  387. Go( &LIB_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolCircle.MakeEvent() );
  388. Go( &LIB_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolArc.MakeEvent() );
  389. Go( &LIB_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawSymbolLines.MakeEvent() );
  390. Go( &LIB_DRAWING_TOOLS::PlaceAnchor, EE_ACTIONS::placeSymbolAnchor.MakeEvent() );
  391. Go( &LIB_DRAWING_TOOLS::RepeatDrawItem, EE_ACTIONS::repeatDrawItem.MakeEvent() );
  392. }