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.

912 lines
28 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 "ee_point_editor.h"
  27. #include <tool/tool_manager.h>
  28. #include <view/view_controls.h>
  29. #include <geometry/seg.h>
  30. #include <tools/ee_actions.h>
  31. #include <tools/ee_selection_tool.h>
  32. #include <bitmaps.h>
  33. #include <sch_edit_frame.h>
  34. #include <sch_line.h>
  35. #include <sch_bitmap.h>
  36. #include <sch_sheet.h>
  37. #include <symbol_edit_frame.h>
  38. #include <lib_arc.h>
  39. #include <lib_circle.h>
  40. #include <lib_rectangle.h>
  41. #include <lib_polyline.h>
  42. // Few constants to avoid using bare numbers for point indices
  43. enum ARC_POINTS
  44. {
  45. ARC_CENTER, ARC_START, ARC_END
  46. };
  47. enum CIRCLE_POINTS
  48. {
  49. CIRC_CENTER, CIRC_END
  50. };
  51. enum RECTANGLE_POINTS
  52. {
  53. RECT_TOPLEFT, RECT_TOPRIGHT, RECT_BOTLEFT, RECT_BOTRIGHT
  54. };
  55. enum LINE_POINTS
  56. {
  57. LINE_START, LINE_END
  58. };
  59. class EDIT_POINTS_FACTORY
  60. {
  61. public:
  62. static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem, SCH_BASE_FRAME* frame )
  63. {
  64. std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
  65. if( !aItem )
  66. return points;
  67. // Generate list of edit points based on the item type
  68. switch( aItem->Type() )
  69. {
  70. case LIB_ARC_T:
  71. {
  72. LIB_ARC* arc = (LIB_ARC*) aItem;
  73. points->AddPoint( mapCoords( arc->GetPosition() ) );
  74. points->AddPoint( mapCoords( arc->GetStart() ) );
  75. points->AddPoint( mapCoords( arc->GetEnd() ) );
  76. break;
  77. }
  78. case LIB_CIRCLE_T:
  79. {
  80. LIB_CIRCLE* circle = (LIB_CIRCLE*) aItem;
  81. points->AddPoint( mapCoords( circle->GetPosition() ) );
  82. points->AddPoint( mapCoords( circle->GetEnd() ) );
  83. break;
  84. }
  85. case LIB_POLYLINE_T:
  86. {
  87. LIB_POLYLINE* lines = (LIB_POLYLINE*) aItem;
  88. const std::vector<wxPoint>& pts = lines->GetPolyPoints();
  89. for( wxPoint pt : pts )
  90. points->AddPoint( mapCoords( pt ) );
  91. break;
  92. }
  93. case LIB_RECTANGLE_T:
  94. {
  95. LIB_RECTANGLE* rect = (LIB_RECTANGLE*) aItem;
  96. // point editor works only with rectangles having width and height > 0
  97. // Some symbols can have rectangles with width or height < 0
  98. // So normalize the size:
  99. BOX2I dummy;
  100. dummy.SetOrigin( mapCoords( rect->GetPosition() ) );
  101. dummy.SetEnd( mapCoords( rect->GetEnd() ) );
  102. dummy.Normalize();
  103. VECTOR2I topLeft = dummy.GetPosition();
  104. VECTOR2I botRight = dummy.GetEnd();
  105. points->AddPoint( topLeft );
  106. points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
  107. points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
  108. points->AddPoint( botRight );
  109. break;
  110. }
  111. case SCH_SHEET_T:
  112. {
  113. SCH_SHEET* sheet = (SCH_SHEET*) aItem;
  114. wxPoint topLeft = sheet->GetPosition();
  115. wxPoint botRight = sheet->GetPosition() + sheet->GetSize();
  116. points->AddPoint( (wxPoint) topLeft );
  117. points->AddPoint( wxPoint( botRight.x, topLeft.y ) );
  118. points->AddPoint( wxPoint( topLeft.x, botRight.y ) );
  119. points->AddPoint( (wxPoint) botRight );
  120. break;
  121. }
  122. case SCH_BITMAP_T:
  123. {
  124. SCH_BITMAP* bitmap = (SCH_BITMAP*) aItem;
  125. wxPoint topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
  126. wxPoint botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
  127. points->AddPoint( (wxPoint) topLeft );
  128. points->AddPoint( wxPoint( botRight.x, topLeft.y ) );
  129. points->AddPoint( wxPoint( topLeft.x, botRight.y ) );
  130. points->AddPoint( (wxPoint) botRight );
  131. break;
  132. }
  133. case SCH_LINE_T:
  134. {
  135. SCH_LINE* line = (SCH_LINE*) aItem;
  136. std::pair<EDA_ITEM*, int> connectedStart = { nullptr, STARTPOINT };
  137. std::pair<EDA_ITEM*, int> connectedEnd = { nullptr, STARTPOINT };
  138. for( SCH_ITEM* test : frame->GetScreen()->Items().OfType( SCH_LINE_T ) )
  139. {
  140. if( test->GetLayer() != LAYER_NOTES )
  141. continue;
  142. if( test == aItem )
  143. continue;
  144. SCH_LINE* testLine = static_cast<SCH_LINE*>( test );
  145. if( testLine->GetStartPoint() == line->GetStartPoint() )
  146. {
  147. connectedStart = { testLine, STARTPOINT };
  148. }
  149. else if( testLine->GetEndPoint() == line->GetStartPoint() )
  150. {
  151. connectedStart = { testLine, ENDPOINT };
  152. }
  153. else if( testLine->GetStartPoint() == line->GetEndPoint() )
  154. {
  155. connectedEnd = { testLine, STARTPOINT };
  156. }
  157. else if( testLine->GetEndPoint() == line->GetEndPoint() )
  158. {
  159. connectedEnd = { testLine, ENDPOINT };
  160. }
  161. }
  162. points->AddPoint( line->GetStartPoint(), connectedStart );
  163. points->AddPoint( line->GetEndPoint(), connectedEnd );
  164. break;
  165. }
  166. default:
  167. points.reset();
  168. break;
  169. }
  170. return points;
  171. }
  172. private:
  173. EDIT_POINTS_FACTORY() {};
  174. };
  175. EE_POINT_EDITOR::EE_POINT_EDITOR() :
  176. EE_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.PointEditor" ),
  177. m_editedPoint( nullptr )
  178. {
  179. }
  180. void EE_POINT_EDITOR::Reset( RESET_REASON aReason )
  181. {
  182. EE_TOOL_BASE::Reset( aReason );
  183. m_editPoints.reset();
  184. }
  185. bool EE_POINT_EDITOR::Init()
  186. {
  187. EE_TOOL_BASE::Init();
  188. auto& menu = m_selectionTool->GetToolMenu().GetMenu();
  189. menu.AddItem( EE_ACTIONS::pointEditorAddCorner,
  190. std::bind( &EE_POINT_EDITOR::addCornerCondition, this, _1 ) );
  191. menu.AddItem( EE_ACTIONS::pointEditorRemoveCorner,
  192. std::bind( &EE_POINT_EDITOR::removeCornerCondition, this, _1 ) );
  193. return true;
  194. }
  195. void EE_POINT_EDITOR::updateEditedPoint( const TOOL_EVENT& aEvent )
  196. {
  197. EDIT_POINT* point = m_editedPoint;
  198. if( aEvent.IsMotion() )
  199. {
  200. point = m_editPoints->FindPoint( aEvent.Position(), getView() );
  201. }
  202. else if( aEvent.IsDrag( BUT_LEFT ) )
  203. {
  204. point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
  205. }
  206. else
  207. {
  208. point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
  209. }
  210. if( m_editedPoint != point )
  211. setEditedPoint( point );
  212. }
  213. int EE_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
  214. {
  215. static KICAD_T supportedTypes[] = {
  216. LIB_ARC_T,
  217. LIB_CIRCLE_T,
  218. LIB_POLYLINE_T,
  219. LIB_RECTANGLE_T,
  220. SCH_SHEET_T,
  221. SCH_LINE_LOCATE_GRAPHIC_LINE_T,
  222. SCH_BITMAP_T,
  223. EOT
  224. };
  225. if( !m_selectionTool )
  226. return 0;
  227. const EE_SELECTION& selection = m_selectionTool->GetSelection();
  228. if( selection.Size() != 1 || !selection.Front()->IsType( supportedTypes ) )
  229. return 0;
  230. // Wait till drawing tool is done
  231. if( selection.Front()->IsNew() )
  232. return 0;
  233. Activate();
  234. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  235. KIGFX::VIEW* view = getView();
  236. EDA_ITEM* item = (EDA_ITEM*) selection.Front();
  237. controls->ShowCursor( true );
  238. m_editPoints = EDIT_POINTS_FACTORY::Make( item, m_frame );
  239. view->Add( m_editPoints.get() );
  240. setEditedPoint( nullptr );
  241. updateEditedPoint( aEvent );
  242. bool inDrag = false;
  243. bool modified = false;
  244. // Main loop: keep receiving events
  245. while( TOOL_EVENT* evt = Wait() )
  246. {
  247. if( !m_editPoints || evt->IsSelectionEvent() )
  248. break;
  249. if ( !inDrag )
  250. updateEditedPoint( *evt );
  251. if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
  252. {
  253. if( !inDrag )
  254. {
  255. saveItemsToUndo();
  256. controls->ForceCursorPosition( false );
  257. inDrag = true;
  258. modified = true;
  259. }
  260. bool snap = !evt->Modifier( MD_ALT );
  261. if( item->Type() == LIB_ARC_T && getEditedPointIndex() == ARC_CENTER )
  262. snap = false;
  263. m_editedPoint->SetPosition( controls->GetCursorPosition( snap ) );
  264. updateParentItem();
  265. updatePoints();
  266. }
  267. else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
  268. {
  269. controls->SetAutoPan( false );
  270. inDrag = false;
  271. }
  272. else if( evt->IsCancelInteractive() || evt->IsActivate() )
  273. {
  274. if( inDrag ) // Restore the last change
  275. {
  276. rollbackFromUndo();
  277. inDrag = false;
  278. modified = false;
  279. break;
  280. }
  281. else if( evt->IsCancelInteractive() )
  282. break;
  283. if( evt->IsActivate() && !evt->IsMoveTool() )
  284. break;
  285. }
  286. else
  287. evt->SetPassEvent();
  288. controls->SetAutoPan( inDrag );
  289. controls->CaptureCursor( inDrag );
  290. }
  291. controls->SetAutoPan( false );
  292. controls->CaptureCursor( false );
  293. if( m_editPoints )
  294. {
  295. view->Remove( m_editPoints.get() );
  296. if( modified )
  297. m_frame->OnModify();
  298. m_editPoints.reset();
  299. m_frame->GetCanvas()->Refresh();
  300. }
  301. return 0;
  302. }
  303. /**
  304. * Update the coordinates of 4 corners of a rectangle, accordint to constraints
  305. * and the moved corner
  306. * @param aEditedPointIndex is the corner id
  307. * @param minWidth is the minimal width constraint
  308. * @param minHeight is the minimal height constraint
  309. * @param topLeft is the RECT_TOPLEFT to constraint
  310. * @param topRight is the RECT_TOPRIGHT to constraint
  311. * @param botLeft is the RECT_BOTLEFT to constraint
  312. * @param botRight is the RECT_BOTRIGHT to constraint
  313. * @param aGridSize is the a constraint: if > 1 new coordinates are on this grid
  314. */
  315. static void pinEditedCorner( int aEditedPointIndex, int minWidth, int minHeight,
  316. VECTOR2I& topLeft, VECTOR2I& topRight,
  317. VECTOR2I& botLeft, VECTOR2I& botRight,
  318. int aGridSize = 0 )
  319. {
  320. // A macro to keep a coordinate on the grid:
  321. #define MOVE_TO_GRID(z) { z.x = ( (z.x +1 ) / aGridSize ) * aGridSize;\
  322. z.y = ( (z.y +1 ) / aGridSize ) * aGridSize; }
  323. switch( aEditedPointIndex )
  324. {
  325. case RECT_TOPLEFT:
  326. // pin edited point within opposite corner
  327. topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
  328. topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
  329. if( aGridSize > 1 ) // Keep point on specified grid size
  330. {
  331. topLeft.x = ( topLeft.x / aGridSize ) * aGridSize;
  332. topLeft.y = ( topLeft.y / aGridSize ) * aGridSize;
  333. }
  334. // push edited point edges to adjacent corners
  335. topRight.y = topLeft.y;
  336. botLeft.x = topLeft.x;
  337. break;
  338. case RECT_TOPRIGHT:
  339. // pin edited point within opposite corner
  340. topRight.x = std::max( topRight.x, botLeft.x + minWidth );
  341. topRight.y = std::min( topRight.y, botLeft.y - minHeight );
  342. if( aGridSize > 1 ) // Keep point on specified grid size
  343. {
  344. topRight.x = ( ( topRight.x+1 ) / aGridSize ) * aGridSize;
  345. topRight.y = ( topRight.y / aGridSize ) * aGridSize;
  346. }
  347. // push edited point edges to adjacent corners
  348. topLeft.y = topRight.y;
  349. botRight.x = topRight.x;
  350. break;
  351. case RECT_BOTLEFT:
  352. // pin edited point within opposite corner
  353. botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
  354. botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
  355. if( aGridSize > 1 ) // Keep point on specified grid size
  356. {
  357. botLeft.x = ( botLeft.x / aGridSize ) * aGridSize;
  358. botLeft.y = ( ( botLeft.y+1 ) / aGridSize ) * aGridSize;
  359. }
  360. // push edited point edges to adjacent corners
  361. botRight.y = botLeft.y;
  362. topLeft.x = botLeft.x;
  363. break;
  364. case RECT_BOTRIGHT:
  365. // pin edited point within opposite corner
  366. botRight.x = std::max( botRight.x, topLeft.x + minWidth );
  367. botRight.y = std::max( botRight.y, topLeft.y + minHeight );
  368. if( aGridSize > 1 ) // Keep point on specified grid size
  369. {
  370. botRight.x = ( ( botRight.x+1 ) / aGridSize ) * aGridSize;
  371. botRight.y = ( ( botRight.y+1 ) / aGridSize ) * aGridSize;
  372. }
  373. // push edited point edges to adjacent corners
  374. botLeft.y = botRight.y;
  375. topRight.x = botRight.x;
  376. break;
  377. }
  378. }
  379. void EE_POINT_EDITOR::updateParentItem() const
  380. {
  381. EDA_ITEM* item = m_editPoints->GetParent();
  382. if( !item )
  383. return;
  384. switch( item->Type() )
  385. {
  386. case LIB_ARC_T:
  387. {
  388. LIB_ARC* arc = (LIB_ARC*) item;
  389. int i = getEditedPointIndex();
  390. if( i == ARC_CENTER )
  391. {
  392. arc->SetEditState( 4 );
  393. arc->CalcEdit( mapCoords( m_editPoints->Point( ARC_CENTER ).GetPosition() ) );
  394. }
  395. else if( i == ARC_START )
  396. {
  397. arc->SetEditState( 2 );
  398. arc->CalcEdit( mapCoords( m_editPoints->Point( ARC_START ).GetPosition() ) );
  399. }
  400. else if( i == ARC_END )
  401. {
  402. arc->SetEditState( 3 );
  403. arc->CalcEdit( mapCoords( m_editPoints->Point( ARC_END ).GetPosition() ) );
  404. }
  405. break;
  406. }
  407. case LIB_CIRCLE_T:
  408. {
  409. LIB_CIRCLE* circle = (LIB_CIRCLE*) item;
  410. circle->SetPosition( mapCoords( m_editPoints->Point( CIRC_CENTER ).GetPosition() ) );
  411. circle->SetEnd( mapCoords( m_editPoints->Point( CIRC_END ).GetPosition() ) );
  412. break;
  413. }
  414. case LIB_POLYLINE_T:
  415. {
  416. LIB_POLYLINE* lines = (LIB_POLYLINE*) item;
  417. lines->ClearPoints();
  418. for( unsigned i = 0; i < m_editPoints->PointsSize(); ++i )
  419. lines->AddPoint( mapCoords( m_editPoints->Point( i ).GetPosition() ) );
  420. break;
  421. }
  422. case LIB_RECTANGLE_T:
  423. {
  424. VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
  425. VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
  426. VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
  427. VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
  428. pinEditedCorner( getEditedPointIndex(), Mils2iu( 1 ), Mils2iu( 1 ),
  429. topLeft, topRight, botLeft, botRight );
  430. LIB_RECTANGLE* rect = (LIB_RECTANGLE*) item;
  431. rect->SetPosition( mapCoords( topLeft ) );
  432. rect->SetEnd( mapCoords( botRight ) );
  433. break;
  434. }
  435. case SCH_BITMAP_T:
  436. {
  437. SCH_BITMAP* bitmap = (SCH_BITMAP*) item;
  438. VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
  439. VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
  440. VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
  441. VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
  442. pinEditedCorner( getEditedPointIndex(), Mils2iu( 50 ), Mils2iu( 50 ),
  443. topLeft, topRight, botLeft, botRight );
  444. double oldWidth = bitmap->GetSize().x;
  445. double newWidth = topRight.x - topLeft.x;
  446. double widthRatio = newWidth / oldWidth;
  447. double oldHeight = bitmap->GetSize().y;
  448. double newHeight = botLeft.y - topLeft.y;
  449. double heightRatio = newHeight / oldHeight;
  450. bitmap->SetImageScale( bitmap->GetImageScale() * std::min( widthRatio, heightRatio ) );
  451. break;
  452. }
  453. case SCH_SHEET_T:
  454. {
  455. SCH_SHEET* sheet = (SCH_SHEET*) item;
  456. VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
  457. VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
  458. VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
  459. VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
  460. // The grid size used to place connected items. because a sheet contains
  461. // connected items (sheet pins), keep corners coordinates on this grid.
  462. // Otherwise, some sheet pins can be moved off grid
  463. int grid_size = Mils2iu( 50 );
  464. pinEditedCorner( getEditedPointIndex(), sheet->GetMinWidth(), sheet->GetMinHeight(),
  465. topLeft, topRight, botLeft, botRight, grid_size );
  466. // Pin positions are relative to origin. Attempt to leave them where they
  467. // are if the origin moves.
  468. wxPoint originDelta = sheet->GetPosition() - (wxPoint) topLeft;
  469. sheet->SetPosition( (wxPoint) topLeft );
  470. sheet->SetSize( wxSize( botRight.x - topLeft.x, botRight.y - topLeft.y ) );
  471. // Update the fields if we're in autoplace mode
  472. if( sheet->GetFieldsAutoplaced() == FIELDS_AUTOPLACED_AUTO )
  473. sheet->AutoplaceFields( /* aScreen */ NULL, /* aManual */ false );
  474. // Keep sheet pins attached to edges:
  475. for( SCH_SHEET_PIN* pin : sheet->GetPins() )
  476. {
  477. wxPoint pos = pin->GetPosition();
  478. pos += originDelta;
  479. switch( pin->GetEdge() )
  480. {
  481. case SHEET_LEFT_SIDE: pos.x = topLeft.x; break;
  482. case SHEET_RIGHT_SIDE: pos.x = topRight.x; break;
  483. case SHEET_TOP_SIDE: pos.y = topLeft.y; break;
  484. case SHEET_BOTTOM_SIDE: pos.y = botLeft.y; break;
  485. case SHEET_UNDEFINED_SIDE: break;
  486. }
  487. pin->SetPosition( pos );
  488. }
  489. break;
  490. }
  491. case SCH_LINE_T:
  492. {
  493. SCH_LINE* line = (SCH_LINE*) item;
  494. line->SetStartPoint( (wxPoint) m_editPoints->Point( LINE_START ).GetPosition() );
  495. line->SetEndPoint( (wxPoint) m_editPoints->Point( LINE_END ).GetPosition() );
  496. std::pair<EDA_ITEM*, int> connected = m_editPoints->Point( LINE_START ).GetConnected();
  497. if( connected.first )
  498. {
  499. if( connected.second == STARTPOINT )
  500. static_cast<SCH_LINE*>( connected.first )->SetStartPoint( line->GetPosition() );
  501. else if( connected.second == ENDPOINT )
  502. static_cast<SCH_LINE*>( connected.first )->SetEndPoint( line->GetPosition() );
  503. getView()->Update( connected.first, KIGFX::GEOMETRY );
  504. }
  505. connected = m_editPoints->Point( LINE_END ).GetConnected();
  506. if( connected.first )
  507. {
  508. if( connected.second == STARTPOINT )
  509. static_cast<SCH_LINE*>( connected.first )->SetStartPoint( line->GetEndPoint() );
  510. else if( connected.second == ENDPOINT )
  511. static_cast<SCH_LINE*>( connected.first )->SetEndPoint( line->GetEndPoint() );
  512. getView()->Update( connected.first, KIGFX::GEOMETRY );
  513. }
  514. break;
  515. }
  516. default:
  517. break;
  518. }
  519. updateItem( item, true );
  520. m_frame->SetMsgPanel( item );
  521. }
  522. void EE_POINT_EDITOR::updatePoints()
  523. {
  524. if( !m_editPoints )
  525. return;
  526. EDA_ITEM* item = m_editPoints->GetParent();
  527. if( !item )
  528. return;
  529. switch( item->Type() )
  530. {
  531. case LIB_ARC_T:
  532. {
  533. LIB_ARC* arc = (LIB_ARC*) item;
  534. m_editPoints->Point( ARC_CENTER ).SetPosition( mapCoords( arc->GetPosition() ) );
  535. m_editPoints->Point( ARC_START ).SetPosition( mapCoords( arc->GetStart() ) );
  536. m_editPoints->Point( ARC_END ).SetPosition( mapCoords( arc->GetEnd() ) );
  537. break;
  538. }
  539. case LIB_CIRCLE_T:
  540. {
  541. LIB_CIRCLE* circle = (LIB_CIRCLE*) item;
  542. m_editPoints->Point( CIRC_CENTER ).SetPosition( mapCoords( circle->GetPosition() ) );
  543. m_editPoints->Point( CIRC_END ).SetPosition( mapCoords( circle->GetEnd() ) );
  544. break;
  545. }
  546. case LIB_POLYLINE_T:
  547. {
  548. LIB_POLYLINE* lines = (LIB_POLYLINE*) item;
  549. const std::vector<wxPoint>& pts = lines->GetPolyPoints();
  550. if( m_editPoints->PointsSize() != (unsigned) pts.size() )
  551. {
  552. getView()->Remove( m_editPoints.get() );
  553. m_editedPoint = nullptr;
  554. m_editPoints = EDIT_POINTS_FACTORY::Make( item, m_frame );
  555. getView()->Add(m_editPoints.get() );
  556. }
  557. else
  558. {
  559. for( unsigned i = 0; i < pts.size(); i++ )
  560. m_editPoints->Point( i ).SetPosition( mapCoords( pts[i] ) );
  561. }
  562. break;
  563. }
  564. case LIB_RECTANGLE_T:
  565. {
  566. LIB_RECTANGLE* rect = (LIB_RECTANGLE*) item;
  567. // point editor works only with rectangles having width and height > 0
  568. // Some symbols can have rectangles with width or height < 0
  569. // So normalize the size:
  570. BOX2I dummy;
  571. dummy.SetOrigin( mapCoords( rect->GetPosition() ) );
  572. dummy.SetEnd( mapCoords( rect->GetEnd() ) );
  573. dummy.Normalize();
  574. VECTOR2I topLeft = dummy.GetPosition();
  575. VECTOR2I botRight = dummy.GetEnd();
  576. m_editPoints->Point( RECT_TOPLEFT ).SetPosition( topLeft );
  577. m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
  578. m_editPoints->Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
  579. m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( botRight );
  580. break;
  581. }
  582. case SCH_BITMAP_T:
  583. {
  584. SCH_BITMAP* bitmap = (SCH_BITMAP*) item;
  585. wxPoint topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
  586. wxPoint botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
  587. m_editPoints->Point( RECT_TOPLEFT ).SetPosition( topLeft );
  588. m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( botRight.x, topLeft.y );
  589. m_editPoints->Point( RECT_BOTLEFT ).SetPosition( topLeft.x, botRight.y );
  590. m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( botRight );
  591. break;
  592. }
  593. case SCH_SHEET_T:
  594. {
  595. SCH_SHEET* sheet = (SCH_SHEET*) item;
  596. wxPoint topLeft = sheet->GetPosition();
  597. wxPoint botRight = sheet->GetPosition() + sheet->GetSize();
  598. m_editPoints->Point( RECT_TOPLEFT ).SetPosition( topLeft );
  599. m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( botRight.x, topLeft.y );
  600. m_editPoints->Point( RECT_BOTLEFT ).SetPosition( topLeft.x, botRight.y );
  601. m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( botRight );
  602. break;
  603. }
  604. case SCH_LINE_T:
  605. {
  606. SCH_LINE* line = (SCH_LINE*) item;
  607. m_editPoints->Point( LINE_START ).SetPosition( line->GetStartPoint() );
  608. m_editPoints->Point( LINE_END ).SetPosition( line->GetEndPoint() );
  609. break;
  610. }
  611. default:
  612. break;
  613. }
  614. getView()->Update( m_editPoints.get() );
  615. }
  616. void EE_POINT_EDITOR::setEditedPoint( EDIT_POINT* aPoint )
  617. {
  618. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  619. if( aPoint )
  620. {
  621. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  622. controls->ForceCursorPosition( true, aPoint->GetPosition() );
  623. controls->ShowCursor( true );
  624. }
  625. else
  626. {
  627. if( m_frame->ToolStackIsEmpty() )
  628. controls->ShowCursor( false );
  629. controls->ForceCursorPosition( false );
  630. }
  631. m_editedPoint = aPoint;
  632. }
  633. bool EE_POINT_EDITOR::removeCornerCondition( const SELECTION& )
  634. {
  635. if( !m_editPoints || !m_editedPoint )
  636. return false;
  637. LIB_POLYLINE* polyLine = dynamic_cast<LIB_POLYLINE*>( m_editPoints->GetParent() );
  638. if( !polyLine || polyLine->GetCornerCount() < 3 )
  639. return false;
  640. const std::vector<wxPoint>& pts = polyLine->GetPolyPoints();
  641. for( unsigned i = 0; i < polyLine->GetCornerCount(); ++i )
  642. {
  643. if( pts[i] == mapCoords( m_editedPoint->GetPosition() ) )
  644. return true;
  645. }
  646. return false;
  647. }
  648. bool EE_POINT_EDITOR::addCornerCondition( const SELECTION& )
  649. {
  650. if( !m_editPoints || !m_editedPoint )
  651. return false;
  652. LIB_POLYLINE* polyLine = dynamic_cast<LIB_POLYLINE*>( m_editPoints->GetParent() );
  653. if( !polyLine )
  654. return false;
  655. VECTOR2I cursorPos = getViewControls()->GetCursorPosition();
  656. double threshold = getView()->ToWorld( EDIT_POINT::POINT_SIZE );
  657. return polyLine->HitTest( mapCoords( cursorPos ), (int) threshold );
  658. }
  659. int EE_POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
  660. {
  661. if( !m_editPoints )
  662. return 0;
  663. LIB_POLYLINE* polyLine = dynamic_cast<LIB_POLYLINE*>( m_editPoints->GetParent() );
  664. if( !polyLine )
  665. return false;
  666. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !aEvent.Modifier( MD_ALT ) );
  667. polyLine->AddCorner( mapCoords( cursorPos ) );
  668. updateItem( polyLine, true );
  669. updatePoints();
  670. return 0;
  671. }
  672. int EE_POINT_EDITOR::removeCorner( const TOOL_EVENT& aEvent )
  673. {
  674. if( !m_editPoints || !m_editedPoint )
  675. return 0;
  676. LIB_POLYLINE* polyLine = dynamic_cast<LIB_POLYLINE*>( m_editPoints->GetParent() );
  677. if( !polyLine || polyLine->GetCornerCount() < 3 )
  678. return 0;
  679. polyLine->RemoveCorner( getEditedPointIndex() );
  680. updateItem( polyLine, true );
  681. updatePoints();
  682. return 0;
  683. }
  684. int EE_POINT_EDITOR::modifiedSelection( const TOOL_EVENT& aEvent )
  685. {
  686. updatePoints();
  687. return 0;
  688. }
  689. void EE_POINT_EDITOR::saveItemsToUndo()
  690. {
  691. if( m_isSymbolEditor )
  692. {
  693. saveCopyInUndoList( m_editPoints->GetParent()->GetParent(), UNDO_REDO::LIBEDIT );
  694. }
  695. else
  696. {
  697. saveCopyInUndoList( (SCH_ITEM*) m_editPoints->GetParent(), UNDO_REDO::CHANGED );
  698. if( m_editPoints->GetParent()->Type() == SCH_LINE_T )
  699. {
  700. std::pair<EDA_ITEM*, int> connected = m_editPoints->Point( LINE_START ).GetConnected();
  701. if( connected.first )
  702. saveCopyInUndoList( (SCH_ITEM*) connected.first, UNDO_REDO::CHANGED, true );
  703. connected = m_editPoints->Point( LINE_END ).GetConnected();
  704. if( connected.first )
  705. saveCopyInUndoList( (SCH_ITEM*) connected.first, UNDO_REDO::CHANGED, true );
  706. }
  707. }
  708. }
  709. void EE_POINT_EDITOR::rollbackFromUndo()
  710. {
  711. if( m_isSymbolEditor )
  712. static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->RollbackSymbolFromUndo();
  713. else
  714. static_cast<SCH_EDIT_FRAME*>( m_frame )->RollbackSchematicFromUndo();
  715. }
  716. void EE_POINT_EDITOR::setTransitions()
  717. {
  718. Go( &EE_POINT_EDITOR::Main, EVENTS::SelectedEvent );
  719. Go( &EE_POINT_EDITOR::Main, ACTIONS::activatePointEditor.MakeEvent() );
  720. Go( &EE_POINT_EDITOR::addCorner, EE_ACTIONS::pointEditorAddCorner.MakeEvent() );
  721. Go( &EE_POINT_EDITOR::removeCorner, EE_ACTIONS::pointEditorRemoveCorner.MakeEvent() );
  722. Go( &EE_POINT_EDITOR::modifiedSelection, EVENTS::SelectedItemsModified );
  723. }