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.

473 lines
13 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 <functional>
  25. using namespace std::placeholders;
  26. #include <tool/tool_manager.h>
  27. #include <tool/actions.h>
  28. #include <view/view_controls.h>
  29. #include <gal/graphics_abstraction_layer.h>
  30. #include <geometry/seg.h>
  31. #include <confirm.h>
  32. #include <bitmaps.h>
  33. #include <status_popup.h>
  34. #include <page_layout/ws_draw_item.h>
  35. #include <page_layout/ws_data_item.h>
  36. #include <widgets/progress_reporter.h>
  37. #include "pl_editor_frame.h"
  38. #include "pl_editor_id.h"
  39. #include "pl_point_editor.h"
  40. #include "properties_frame.h"
  41. #include "tools/pl_actions.h"
  42. #include "tools/pl_selection_tool.h"
  43. // Few constants to avoid using bare numbers for point indices
  44. enum RECTANGLE_POINTS
  45. {
  46. RECT_TOPLEFT, RECT_TOPRIGHT, RECT_BOTLEFT, RECT_BOTRIGHT
  47. };
  48. enum LINE_POINTS
  49. {
  50. LINE_START, LINE_END
  51. };
  52. class EDIT_POINTS_FACTORY
  53. {
  54. public:
  55. static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem )
  56. {
  57. std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
  58. if( !aItem )
  59. return points;
  60. // Generate list of edit points based on the item type
  61. switch( aItem->Type() )
  62. {
  63. case WSG_LINE_T:
  64. {
  65. WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) aItem;
  66. points->AddPoint( line->GetStart() );
  67. points->AddPoint( line->GetEnd() );
  68. break;
  69. }
  70. case WSG_RECT_T:
  71. {
  72. WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) aItem;
  73. wxPoint topLeft = rect->GetStart();
  74. wxPoint botRight = rect->GetEnd();
  75. points->AddPoint( (VECTOR2I) topLeft );
  76. points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
  77. points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
  78. points->AddPoint( (VECTOR2I) botRight );
  79. break;
  80. }
  81. default:
  82. points.reset();
  83. break;
  84. }
  85. return points;
  86. }
  87. private:
  88. EDIT_POINTS_FACTORY() {};
  89. };
  90. PL_POINT_EDITOR::PL_POINT_EDITOR() :
  91. TOOL_INTERACTIVE( "plEditor.PointEditor" ),
  92. m_frame( nullptr ),
  93. m_selectionTool( nullptr ),
  94. m_editedPoint( nullptr )
  95. {
  96. }
  97. void PL_POINT_EDITOR::Reset( RESET_REASON aReason )
  98. {
  99. if( aReason == MODEL_RELOAD )
  100. {
  101. // Init variables used by every drawing tool
  102. m_frame = getEditFrame<PL_EDITOR_FRAME>();
  103. }
  104. m_editPoints.reset();
  105. }
  106. bool PL_POINT_EDITOR::Init()
  107. {
  108. m_frame = getEditFrame<PL_EDITOR_FRAME>();
  109. m_selectionTool = m_toolMgr->GetTool<PL_SELECTION_TOOL>();
  110. return true;
  111. }
  112. void PL_POINT_EDITOR::updateEditedPoint( const TOOL_EVENT& aEvent )
  113. {
  114. EDIT_POINT* point = m_editedPoint;
  115. if( aEvent.IsMotion() )
  116. {
  117. point = m_editPoints->FindPoint( aEvent.Position(), getView() );
  118. }
  119. else if( aEvent.IsDrag( BUT_LEFT ) )
  120. {
  121. point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
  122. }
  123. else
  124. {
  125. point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
  126. }
  127. if( m_editedPoint != point )
  128. setEditedPoint( point );
  129. }
  130. int PL_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
  131. {
  132. static KICAD_T pointTypes[] = { WSG_LINE_T, WSG_RECT_T, EOT };
  133. if( !m_selectionTool )
  134. return 0;
  135. const PL_SELECTION& selection = m_selectionTool->GetSelection();
  136. if( selection.Size() != 1 || !selection.Front()->IsType( pointTypes ) )
  137. return 0;
  138. // Wait till drawing tool is done
  139. if( selection.Front()->IsNew() )
  140. return 0;
  141. Activate();
  142. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  143. KIGFX::VIEW* view = getView();
  144. EDA_ITEM* item = (EDA_ITEM*) selection.Front();
  145. controls->ShowCursor( true );
  146. m_editPoints = EDIT_POINTS_FACTORY::Make( item );
  147. if( !m_editPoints )
  148. return 0;
  149. view->Add( m_editPoints.get() );
  150. setEditedPoint( nullptr );
  151. updateEditedPoint( aEvent );
  152. bool inDrag = false;
  153. bool modified = false;
  154. // Main loop: keep receiving events
  155. while( TOOL_EVENT* evt = Wait() )
  156. {
  157. if( !m_editPoints || evt->IsSelectionEvent() )
  158. break;
  159. if ( !inDrag )
  160. updateEditedPoint( *evt );
  161. if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
  162. {
  163. if( !inDrag )
  164. {
  165. m_frame->SaveCopyInUndoList();
  166. controls->ForceCursorPosition( false );
  167. inDrag = true;
  168. modified = true;
  169. }
  170. m_editedPoint->SetPosition( controls->GetCursorPosition( !evt->Modifier( MD_ALT ) ) );
  171. updateItem();
  172. updatePoints();
  173. }
  174. else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
  175. {
  176. controls->SetAutoPan( false );
  177. inDrag = false;
  178. }
  179. else if( evt->IsCancelInteractive() || evt->IsActivate() )
  180. {
  181. if( inDrag ) // Restore the last change
  182. {
  183. m_frame->RollbackFromUndo();
  184. inDrag = false;
  185. modified = false;
  186. }
  187. else if( evt->IsCancelInteractive() )
  188. break;
  189. if( evt->IsActivate() && !evt->IsMoveTool() )
  190. break;
  191. }
  192. else
  193. evt->SetPassEvent();
  194. controls->SetAutoPan( inDrag );
  195. controls->CaptureCursor( inDrag );
  196. }
  197. controls->SetAutoPan( false );
  198. controls->CaptureCursor( false );
  199. if( m_editPoints )
  200. {
  201. view->Remove( m_editPoints.get() );
  202. if( modified )
  203. m_frame->OnModify();
  204. m_editPoints.reset();
  205. m_frame->GetCanvas()->Refresh();
  206. }
  207. return 0;
  208. }
  209. void pinEditedCorner( int editedPointIndex, int minWidth, int minHeight, VECTOR2I& topLeft,
  210. VECTOR2I& topRight, VECTOR2I& botLeft, VECTOR2I& botRight )
  211. {
  212. switch( editedPointIndex )
  213. {
  214. case RECT_TOPLEFT:
  215. // pin edited point within opposite corner
  216. topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
  217. topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
  218. // push edited point edges to adjacent corners
  219. topRight.y = topLeft.y;
  220. botLeft.x = topLeft.x;
  221. break;
  222. case RECT_TOPRIGHT:
  223. // pin edited point within opposite corner
  224. topRight.x = std::max( topRight.x, botLeft.x + minWidth );
  225. topRight.y = std::min( topRight.y, botLeft.y - minHeight );
  226. // push edited point edges to adjacent corners
  227. topLeft.y = topRight.y;
  228. botRight.x = topRight.x;
  229. break;
  230. case RECT_BOTLEFT:
  231. // pin edited point within opposite corner
  232. botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
  233. botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
  234. // push edited point edges to adjacent corners
  235. botRight.y = botLeft.y;
  236. topLeft.x = botLeft.x;
  237. break;
  238. case RECT_BOTRIGHT:
  239. // pin edited point within opposite corner
  240. botRight.x = std::max( botRight.x, topLeft.x + minWidth );
  241. botRight.y = std::max( botRight.y, topLeft.y + minHeight );
  242. // push edited point edges to adjacent corners
  243. botLeft.y = botRight.y;
  244. topRight.x = botRight.x;
  245. break;
  246. }
  247. }
  248. void PL_POINT_EDITOR::updateItem() const
  249. {
  250. EDA_ITEM* item = m_editPoints->GetParent();
  251. if( !item )
  252. return;
  253. WS_DATA_ITEM* dataItem = static_cast<WS_DRAW_ITEM_BASE*>( item )->GetPeer();
  254. // the current item is perhaps not the main item if we have a set of
  255. // repeated items.
  256. // So we change the coordinate references in dataItem using move vectors
  257. // of the start and end points that are the same for each repeated item
  258. switch( item->Type() )
  259. {
  260. case WSG_LINE_T:
  261. {
  262. WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) item;
  263. wxPoint move_startpoint = (wxPoint) m_editPoints->Point( LINE_START ).GetPosition()
  264. - line->GetStart();
  265. wxPoint move_endpoint = (wxPoint) m_editPoints->Point( LINE_END ).GetPosition()
  266. - line->GetEnd();
  267. dataItem->MoveStartPointToUi( dataItem->GetStartPosUi() + move_startpoint );
  268. dataItem->MoveEndPointToUi( dataItem->GetEndPosUi() + move_endpoint );
  269. for( WS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
  270. {
  271. WS_DRAW_ITEM_LINE* draw_line = static_cast<WS_DRAW_ITEM_LINE*>( draw_item );
  272. draw_line->SetStart( draw_line->GetStart() + move_startpoint );
  273. draw_line->SetEnd( draw_line->GetEnd() + move_endpoint );
  274. getView()->Update( draw_item );
  275. }
  276. break;
  277. }
  278. case WSG_RECT_T:
  279. {
  280. WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) item;
  281. VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
  282. VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
  283. VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
  284. VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
  285. pinEditedCorner( getEditedPointIndex(), Mils2iu( 1 ), Mils2iu( 1 ),
  286. topLeft, topRight, botLeft, botRight );
  287. wxPoint move_startpoint = (wxPoint) topLeft - rect->GetStart();
  288. wxPoint move_endpoint = (wxPoint) botRight - rect->GetEnd();
  289. dataItem->MoveStartPointToUi( dataItem->GetStartPosUi() + move_startpoint );
  290. dataItem->MoveEndPointToUi( dataItem->GetEndPosUi() + move_endpoint );
  291. for( WS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
  292. {
  293. WS_DRAW_ITEM_RECT* draw_rect = (WS_DRAW_ITEM_RECT*) draw_item;
  294. draw_rect->SetStart( draw_rect->GetStart() + move_startpoint );
  295. draw_rect->SetEnd( draw_rect->GetEnd() + move_endpoint );
  296. getView()->Update( draw_item );
  297. }
  298. break;
  299. }
  300. default:
  301. break;
  302. }
  303. m_frame->SetMsgPanel( item );
  304. // The Properties frame will be updated. Avoid flicker during update:
  305. m_frame->GetPropertiesFrame()->Freeze();
  306. m_frame->GetPropertiesFrame()->CopyPrmsFromItemToPanel( dataItem );
  307. m_frame->GetPropertiesFrame()->Thaw();
  308. }
  309. void PL_POINT_EDITOR::updatePoints()
  310. {
  311. if( !m_editPoints )
  312. return;
  313. EDA_ITEM* item = m_editPoints->GetParent();
  314. if( !item )
  315. return;
  316. switch( item->Type() )
  317. {
  318. case WSG_LINE_T:
  319. {
  320. WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) item;
  321. m_editPoints->Point( LINE_START ).SetPosition( line->GetStart() );
  322. m_editPoints->Point( LINE_END ).SetPosition( line->GetEnd() );
  323. break;
  324. }
  325. case WSG_RECT_T:
  326. {
  327. WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) item;
  328. wxPoint topLeft = rect->GetPosition();
  329. wxPoint botRight = rect->GetEnd();
  330. m_editPoints->Point( RECT_TOPLEFT ).SetPosition( (VECTOR2I) topLeft );
  331. m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
  332. m_editPoints->Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
  333. m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( (VECTOR2I) botRight );
  334. break;
  335. }
  336. default:
  337. break;
  338. }
  339. getView()->Update( m_editPoints.get() );
  340. }
  341. void PL_POINT_EDITOR::setEditedPoint( EDIT_POINT* aPoint )
  342. {
  343. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  344. if( aPoint )
  345. {
  346. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  347. controls->ForceCursorPosition( true, aPoint->GetPosition() );
  348. controls->ShowCursor( true );
  349. }
  350. else
  351. {
  352. if( m_frame->ToolStackIsEmpty() )
  353. controls->ShowCursor( false );
  354. controls->ForceCursorPosition( false );
  355. }
  356. m_editedPoint = aPoint;
  357. }
  358. int PL_POINT_EDITOR::modifiedSelection( const TOOL_EVENT& aEvent )
  359. {
  360. updatePoints();
  361. return 0;
  362. }
  363. void PL_POINT_EDITOR::setTransitions()
  364. {
  365. Go( &PL_POINT_EDITOR::Main, EVENTS::SelectedEvent );
  366. Go( &PL_POINT_EDITOR::Main, ACTIONS::activatePointEditor.MakeEvent() );
  367. Go( &PL_POINT_EDITOR::modifiedSelection, EVENTS::SelectedItemsModified );
  368. }