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.

1548 lines
46 KiB

16 years ago
16 years ago
16 years ago
16 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
19 years ago
19 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
18 years ago
16 years ago
16 years ago
18 years ago
18 years ago
18 years ago
16 years ago
19 years ago
19 years ago
16 years ago
16 years ago
  1. /*****************/
  2. /* drawpanel.cpp */
  3. /*****************/
  4. #include "fctsys.h"
  5. #include "appl_wxstruct.h"
  6. #include "gr_basic.h"
  7. #include "common.h"
  8. #include "macros.h"
  9. #include "id.h"
  10. #include "class_drawpanel.h"
  11. #include "class_base_screen.h"
  12. #include "wxstruct.h"
  13. #include <wx/wupdlock.h>
  14. #include "kicad_device_context.h"
  15. #define CURSOR_SIZE 12 // Cursor size in pixels
  16. #define CLIP_BOX_PADDING 12
  17. /* Definitions for enabling and disabling debugging features in drawpanel.cpp.
  18. * Please don't forget to turn these off before making any SvN commits.
  19. */
  20. #define DEBUG_SHOW_CLIP_RECT 0 // Set to 1 to draw clipping rectangle.
  21. #define DEBUG_DUMP_CLIP_COORDS 0 // Set to 1 to dump clipping rectangle coordinates.
  22. #define DEBUG_DUMP_SCROLL_SETTINGS 0 // Set to 1 to dump scroll settings.
  23. /* Used to inhibit a response to a mouse left button release, after a
  24. * double click (when releasing the left button at the end of the second
  25. * click. Used in eeschema to inhibit a mouse left release command when
  26. * switching between hierarchical sheets on a double click.
  27. */
  28. static bool s_IgnoreNextLeftButtonRelease = false;
  29. // Events used by WinEDA_DrawPanel
  30. BEGIN_EVENT_TABLE( WinEDA_DrawPanel, wxScrolledWindow )
  31. EVT_LEAVE_WINDOW( WinEDA_DrawPanel::OnMouseLeaving )
  32. EVT_MOUSEWHEEL( WinEDA_DrawPanel::OnMouseWheel )
  33. EVT_MOUSE_EVENTS( WinEDA_DrawPanel::OnMouseEvent )
  34. EVT_CHAR( WinEDA_DrawPanel::OnKeyEvent )
  35. EVT_CHAR_HOOK( WinEDA_DrawPanel::OnKeyEvent )
  36. EVT_PAINT( WinEDA_DrawPanel::OnPaint )
  37. EVT_SIZE( WinEDA_DrawPanel::OnSize )
  38. EVT_SCROLLWIN( WinEDA_DrawPanel::OnScroll )
  39. EVT_ACTIVATE( WinEDA_DrawPanel::OnActivate )
  40. EVT_MENU_RANGE( ID_PAN_UP, ID_PAN_RIGHT, WinEDA_DrawPanel::OnPan )
  41. END_EVENT_TABLE()
  42. /***********************************************************************/
  43. /* WinEDA_DrawPanel base functions (WinEDA_DrawPanel is the main panel)*/
  44. /***********************************************************************/
  45. WinEDA_DrawPanel::WinEDA_DrawPanel( WinEDA_DrawFrame* parent, int id,
  46. const wxPoint& pos, const wxSize& size ) :
  47. wxScrolledWindow( parent, id, pos, size,
  48. wxBORDER | wxNO_FULL_REPAINT_ON_RESIZE )
  49. {
  50. m_Parent = parent;
  51. wxASSERT( m_Parent );
  52. m_scrollIncrementX = MIN( size.x / 8, 10 );
  53. m_scrollIncrementY = MIN( size.y / 8, 10 );
  54. SetBackgroundColour( wxColour( ColorRefs[g_DrawBgColor].m_Red,
  55. ColorRefs[g_DrawBgColor].m_Green,
  56. ColorRefs[g_DrawBgColor].m_Blue ) );
  57. #if defined KICAD_USE_BUFFERED_DC || defined KICAD_USE_BUFFERED_PAINTDC
  58. SetBackgroundStyle( wxBG_STYLE_CUSTOM );
  59. #endif
  60. EnableScrolling( TRUE, TRUE );
  61. m_ClipBox.SetSize( size );
  62. m_ClipBox.SetX( 0 );
  63. m_ClipBox.SetY( 0 );
  64. m_CanStartBlock = -1; // Command block can start if >= 0
  65. m_AbortEnable = m_AbortRequest = false;
  66. m_AutoPAN_Enable = TRUE;
  67. m_IgnoreMouseEvents = 0;
  68. m_DisableEraseBG = false;
  69. ManageCurseur = NULL;
  70. ForceCloseManageCurseur = NULL;
  71. if( wxGetApp().m_EDA_Config )
  72. wxGetApp().m_EDA_Config->Read( wxT( "AutoPAN" ), &m_AutoPAN_Enable,
  73. true );
  74. m_AutoPAN_Request = false;
  75. m_Block_Enable = false;
  76. m_PanelDefaultCursor = m_PanelCursor = wxCURSOR_ARROW;
  77. m_CursorLevel = 0;
  78. m_PrintIsMirrored = false;
  79. }
  80. WinEDA_DrawPanel::~WinEDA_DrawPanel()
  81. {
  82. wxGetApp().m_EDA_Config->Write( wxT( "AutoPAN" ), m_AutoPAN_Enable );
  83. }
  84. BASE_SCREEN* WinEDA_DrawPanel::GetScreen()
  85. {
  86. WinEDA_DrawFrame* parentFrame = m_Parent;
  87. return parentFrame->GetBaseScreen();
  88. }
  89. /*
  90. * Draw the schematic cursor which is usually on grid
  91. */
  92. void WinEDA_DrawPanel::DrawCursor( wxDC* aDC, int aColor )
  93. {
  94. if( m_CursorLevel != 0 || aDC == NULL )
  95. return;
  96. #ifdef __WXMAC__
  97. SetCursor(*wxCROSS_CURSOR);
  98. return;
  99. #endif
  100. wxPoint Cursor = GetScreen()->m_Curseur;
  101. GRSetDrawMode( aDC, GR_XOR );
  102. if( m_Parent->m_CursorShape == 1 ) /* Draws a crosshair. */
  103. {
  104. #ifdef USE_WX_ZOOM
  105. wxSize clientSize = GetClientSize();
  106. wxPoint lineStart = wxPoint( Cursor.x, aDC->DeviceToLogicalY( 0 ) );
  107. wxPoint lineEnd = wxPoint( Cursor.x, aDC->DeviceToLogicalY( clientSize.y ) );
  108. GRLine( &m_ClipBox, aDC, lineStart, lineEnd, 0, aColor ); // Y azis
  109. lineStart = wxPoint( aDC->DeviceToLogicalX( 0 ), Cursor.y );
  110. lineEnd = wxPoint( aDC->DeviceToLogicalX( clientSize.x ), Cursor.y );
  111. GRLine( &m_ClipBox, aDC, lineStart, lineEnd, 0, aColor ); // X azis
  112. #else
  113. int dx = GetScreen()->Unscale( m_ClipBox.GetWidth() );
  114. int dy = GetScreen()->Unscale( m_ClipBox.GetHeight() );
  115. GRLine( &m_ClipBox, aDC, Cursor.x - dx, Cursor.y,
  116. Cursor.x + dx, Cursor.y, 0, aColor ); // Y axis
  117. GRLine( &m_ClipBox, aDC, Cursor.x, Cursor.y - dx,
  118. Cursor.x, Cursor.y + dy, 0, aColor ); // X axis
  119. #endif
  120. }
  121. else
  122. {
  123. #ifdef USE_WX_ZOOM
  124. int len = aDC->DeviceToLogicalXRel( CURSOR_SIZE );
  125. #else
  126. int len = GetScreen()->Unscale( CURSOR_SIZE );
  127. #endif
  128. GRLine( &m_ClipBox, aDC, Cursor.x - len, Cursor.y,
  129. Cursor.x + len, Cursor.y, 0, aColor );
  130. GRLine( &m_ClipBox, aDC, Cursor.x, Cursor.y - len,
  131. Cursor.x, Cursor.y + len, 0, aColor );
  132. }
  133. }
  134. /*
  135. * Remove the grid cursor from the display in preparation for other drawing
  136. * operations
  137. */
  138. void WinEDA_DrawPanel::CursorOff( wxDC* DC )
  139. {
  140. DrawCursor( DC );
  141. --m_CursorLevel;
  142. }
  143. /*
  144. * Display the grid cursor
  145. */
  146. void WinEDA_DrawPanel::CursorOn( wxDC* DC )
  147. {
  148. ++m_CursorLevel;
  149. DrawCursor( DC );
  150. if( m_CursorLevel > 0 ) // Shouldn't happen, but just in case ..
  151. m_CursorLevel = 0;
  152. }
  153. int WinEDA_DrawPanel::GetZoom()
  154. {
  155. return GetScreen()->GetZoom();
  156. }
  157. void WinEDA_DrawPanel::SetZoom( int zoom )
  158. {
  159. GetScreen()->SetZoom( zoom );
  160. }
  161. wxRealPoint WinEDA_DrawPanel::GetGrid()
  162. {
  163. return GetScreen()->GetGridSize();
  164. }
  165. /**
  166. * Convert a coordinate position in device (screen) units to logical (drawing) units.
  167. *
  168. * @param aPosition = position in device (screen) units.
  169. * @return position in logical (drawing) units.
  170. */
  171. wxPoint WinEDA_DrawPanel::CursorRealPosition( const wxPoint& aPosition )
  172. {
  173. double scalar = GetScreen()->GetScalingFactor();
  174. wxPoint pos;
  175. pos.x = wxRound( (double) aPosition.x / scalar );
  176. pos.y = wxRound( (double) aPosition.y / scalar );
  177. pos += GetScreen()->m_DrawOrg;
  178. return pos;
  179. }
  180. /**
  181. * Function IsPointOnDisplay
  182. * @param ref_pos is the position to test in pixels, relative to the panel.
  183. * @return TRUE if ref_pos is a point currently visible on screen
  184. * false if ref_pos is out of screen
  185. */
  186. bool WinEDA_DrawPanel::IsPointOnDisplay( wxPoint ref_pos )
  187. {
  188. wxPoint pos;
  189. EDA_Rect display_rect;
  190. INSTALL_DC( dc, this ); // Refresh the boundary box.
  191. display_rect = m_ClipBox;
  192. // Slightly decreased the size of the useful screen area to avoid drawing
  193. // limits.
  194. #define PIXEL_MARGIN 8
  195. display_rect.Inflate( -PIXEL_MARGIN );
  196. #ifndef USE_WX_ZOOM
  197. // Convert physical coordinates.
  198. pos = CalcUnscrolledPosition( display_rect.GetPosition() );
  199. GetScreen()->Unscale( pos );
  200. pos += GetScreen()->m_DrawOrg;
  201. display_rect.m_Pos = pos;
  202. GetScreen()->Unscale( display_rect.m_Size );
  203. #endif
  204. return display_rect.Inside( ref_pos );
  205. }
  206. void WinEDA_DrawPanel::PostDirtyRect( EDA_Rect aRect )
  207. {
  208. // D( printf( "1) PostDirtyRect( x=%d, y=%d, width=%d, height=%d)\n", aRect.m_Pos.x, aRect.m_Pos.y, aRect.m_Size.x, aRect.m_Size.y ); )
  209. // Convert the rect coordinates and size to pixels (make a draw clip box):
  210. ConvertPcbUnitsToPixelsUnits( &aRect );
  211. // Ensure the rectangle is large enough after truncations.
  212. // The pcb units have finer granularity than the pixels, so it can happen
  213. // that the rectangle is not large enough for the erase portion.
  214. aRect.m_Size.x += 4; // += 1 is not enough!
  215. aRect.m_Size.y += 4;
  216. // D( printf( "2) PostDirtyRect( x=%d, y=%d, width=%d, height=%d)\n", aRect.m_Pos.x, aRect.m_Pos.y, aRect.m_Size.x, aRect.m_Size.y ); )
  217. // pass wxRect() via EDA_Rect::operator wxRect() overload
  218. RefreshRect( aRect, TRUE );
  219. }
  220. /**
  221. * Scale and offset a rectangle in drawing units to device units.
  222. *
  223. * This is the equivalent of wxDC::LogicalToDevice.
  224. *
  225. * @param aRect - Rectangle to scale.
  226. */
  227. void WinEDA_DrawPanel::ConvertPcbUnitsToPixelsUnits( EDA_Rect* aRect )
  228. {
  229. // Calculate the draw area origin in internal units:
  230. wxPoint pos = aRect->GetPosition();
  231. ConvertPcbUnitsToPixelsUnits( &pos );
  232. aRect->SetOrigin( pos ); // rect origin in pixel units
  233. double scale = GetScreen()->GetScalingFactor();
  234. aRect->m_Size.x = wxRound( (double) aRect->m_Size.x * scale );
  235. aRect->m_Size.y = wxRound( (double) aRect->m_Size.y * scale );
  236. }
  237. void WinEDA_DrawPanel::ConvertPcbUnitsToPixelsUnits( wxPoint* aPosition )
  238. {
  239. // Calculate the draw area origin in internal units:
  240. wxPoint drwOrig;
  241. int x_axis_scale, y_axis_scale;
  242. // Origin in scroll units;
  243. GetViewStart( &drwOrig.x, &drwOrig.y );
  244. GetScrollPixelsPerUnit( &x_axis_scale, &y_axis_scale );
  245. // Origin in pixels units
  246. drwOrig.x *= x_axis_scale;
  247. drwOrig.y *= y_axis_scale;
  248. double x, y;
  249. double scalar = GetScreen()->GetScalingFactor();
  250. x = (double) aPosition->x - ( ( (double) drwOrig.x / scalar )
  251. + (double) GetScreen()->m_DrawOrg.x );
  252. y = (double) aPosition->y - ( ( (double) drwOrig.y / scalar )
  253. + (double) GetScreen()->m_DrawOrg.y );
  254. aPosition->x = wxRound( x * scalar );
  255. aPosition->y = wxRound( y * scalar );
  256. }
  257. /**
  258. * Function CursorScreenPosition
  259. * @return the cursor current position in pixels in the screen draw area
  260. */
  261. wxPoint WinEDA_DrawPanel::CursorScreenPosition()
  262. {
  263. wxPoint pos = GetScreen()->m_Curseur - GetScreen()->m_DrawOrg;
  264. double scalar = GetScreen()->GetScalingFactor();
  265. pos.x = wxRound( (double) pos.x * scalar );
  266. pos.y = wxRound( (double) pos.y * scalar );
  267. return pos;
  268. }
  269. /**
  270. * Function GetScreenCenterRealPosition
  271. * @return position (in internal units) of the current area center showed
  272. * on screen
  273. */
  274. wxPoint WinEDA_DrawPanel::GetScreenCenterRealPosition( void )
  275. {
  276. int x, y, ppuX, ppuY;
  277. wxPoint pos;
  278. double scalar = GetScreen()->GetScalingFactor();
  279. GetViewStart( &x, &y );
  280. GetScrollPixelsPerUnit( &ppuX, &ppuY );
  281. x *= ppuX;
  282. y *= ppuY;
  283. pos.x = wxRound( ( (double) GetClientSize().x / 2.0 + (double) x ) / scalar );
  284. pos.y = wxRound( ( (double) GetClientSize().y / 2.0 + (double) y ) / scalar );
  285. pos += GetScreen()->m_DrawOrg;
  286. return pos;
  287. }
  288. /* Move the mouse cursor to the current schematic cursor
  289. */
  290. void WinEDA_DrawPanel::MouseToCursorSchema()
  291. {
  292. wxPoint Mouse = CursorScreenPosition();
  293. MouseTo( Mouse );
  294. }
  295. /** Move the mouse cursor to the position "Mouse"
  296. * @param Mouse = mouse cursor position, in pixels units
  297. */
  298. void WinEDA_DrawPanel::MouseTo( const wxPoint& Mouse )
  299. {
  300. int x, y, xPpu, yPpu;
  301. wxPoint screenPos, drawingPos;
  302. wxRect clientRect( wxPoint( 0, 0 ), GetClientSize() );
  303. CalcScrolledPosition( Mouse.x, Mouse.y, &screenPos.x, &screenPos.y );
  304. /* Scroll if the requested mouse position cursor is outside the drawing
  305. * area. */
  306. if( !clientRect.Contains( screenPos ) )
  307. {
  308. GetViewStart( &x, &y );
  309. GetScrollPixelsPerUnit( &xPpu, &yPpu );
  310. CalcUnscrolledPosition( screenPos.x, screenPos.y,
  311. &drawingPos.x, &drawingPos.y );
  312. wxLogDebug( wxT( "MouseTo() initial screen position(%d, %d) " ) \
  313. wxT( "rectangle(%d, %d, %d, %d) view(%d, %d)" ),
  314. screenPos.x, screenPos.y, clientRect.x, clientRect.y,
  315. clientRect.width, clientRect.height, x, y );
  316. if( screenPos.y < clientRect.GetTop() )
  317. y -= m_scrollIncrementY * yPpu;
  318. else if( screenPos.y > clientRect.GetBottom() )
  319. y += m_scrollIncrementY * yPpu;
  320. else if( clientRect.GetRight() < screenPos.x )
  321. x += m_scrollIncrementX * xPpu;
  322. else
  323. x -= m_scrollIncrementX * xPpu;
  324. Scroll( x, y );
  325. CalcScrolledPosition( drawingPos.x, drawingPos.y,
  326. &screenPos.x, &screenPos.y );
  327. wxLogDebug( wxT( "MouseTo() scrolled screen position(%d, %d) " ) \
  328. wxT( "view(%d, %d)" ), screenPos.x, screenPos.y, x, y );
  329. }
  330. WarpPointer( screenPos.x, screenPos.y );
  331. }
  332. /**
  333. * Called on window activation.
  334. * init the member m_CanStartBlock to avoid a block start command
  335. * on activation (because a left mouse button can be pressed and no block
  336. * command wanted.
  337. * This happens when enter on a hierarchy sheet on double click
  338. */
  339. void WinEDA_DrawPanel::OnActivate( wxActivateEvent& event )
  340. {
  341. m_CanStartBlock = -1; // Block Command can't start
  342. event.Skip();
  343. }
  344. void WinEDA_DrawPanel::OnScroll( wxScrollWinEvent& event )
  345. {
  346. int id = event.GetEventType();
  347. int dir;
  348. int x, y;
  349. int ppux, ppuy;
  350. int unitsX, unitsY;
  351. int maxX, maxY;
  352. GetViewStart( &x, &y );
  353. GetScrollPixelsPerUnit( &ppux, &ppuy );
  354. GetVirtualSize( &unitsX, &unitsY );
  355. maxX = unitsX;
  356. maxY = unitsY;
  357. unitsX /= ppux;
  358. unitsY /= ppuy;
  359. dir = event.GetOrientation(); // wxHORIZONTAL or wxVERTICAL
  360. if( id == wxEVT_SCROLLWIN_LINEUP )
  361. {
  362. if( dir == wxHORIZONTAL )
  363. {
  364. x -= m_scrollIncrementX;
  365. if( x < 0 )
  366. x = 0;
  367. }
  368. else
  369. {
  370. y -= m_scrollIncrementY;
  371. if( y < 0 )
  372. y = 0;
  373. }
  374. }
  375. else if( id == wxEVT_SCROLLWIN_LINEDOWN )
  376. {
  377. if( dir == wxHORIZONTAL )
  378. {
  379. x += m_scrollIncrementX;
  380. if( x > maxX )
  381. x = maxX;
  382. }
  383. else
  384. {
  385. y += m_scrollIncrementY;
  386. if( y > maxY )
  387. y = maxY;
  388. }
  389. }
  390. else if( id == wxEVT_SCROLLWIN_THUMBTRACK )
  391. {
  392. if( dir == wxHORIZONTAL )
  393. x = event.GetPosition();
  394. else
  395. y = event.GetPosition();
  396. }
  397. else
  398. {
  399. event.Skip();
  400. return;
  401. }
  402. #if DEBUG_DUMP_SCROLL_SETTINGS
  403. wxLogDebug( wxT( "Setting scroll bars ppuX=%d, ppuY=%d, unitsX=%d, unitsY=%d," \
  404. "posX=%d, posY=%d" ), ppux, ppuy, unitsX, unitsY, x, y );
  405. #endif
  406. Scroll( x/ppux, y/ppuy );
  407. event.Skip();
  408. }
  409. void WinEDA_DrawPanel::OnSize( wxSizeEvent& event )
  410. {
  411. if( IsShown() )
  412. {
  413. INSTALL_DC( dc, this ); // Update boundary box.
  414. }
  415. event.Skip();
  416. }
  417. /**
  418. * Function SetBoundaryBox
  419. * Set the clip box to the current displayed rectangle dimensions.
  420. *
  421. * When using wxDC for scaling, the clip box coordinates are in drawing (logical)
  422. * units. In other words, the area of the drawing that will be displayed on the
  423. * screen. When using Kicad's scaling, the clip box coordinates are in screen
  424. * (device) units according to the current scroll position.
  425. *
  426. * @param dc - The device context use for drawing with the correct scale and
  427. * offsets already configured. See DoPrepareDC().
  428. */
  429. void WinEDA_DrawPanel::SetBoundaryBox( wxDC* dc )
  430. {
  431. wxASSERT( dc != NULL );
  432. BASE_SCREEN* Screen = GetScreen();;
  433. if( !Screen )
  434. return;
  435. Screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
  436. m_ClipBox.SetOrigin( wxPoint( 0, 0 ) );
  437. m_ClipBox.SetSize( GetClientSize() );
  438. int scrollX, scrollY;
  439. #ifdef USE_WX_ZOOM
  440. double scalar = Screen->GetScalingFactor();
  441. scrollX = wxRound( Screen->GetGridSize().x * scalar );
  442. scrollY = wxRound( Screen->GetGridSize().y * scalar );
  443. #else
  444. scrollX = wxRound( Screen->Scale( Screen->GetGridSize().x ) );
  445. scrollY = wxRound( Screen->Scale( Screen->GetGridSize().y ) );
  446. #endif
  447. m_scrollIncrementX = MAX( GetClientSize().x / 8, scrollX );
  448. m_scrollIncrementY = MAX( GetClientSize().y / 8, scrollY );
  449. #ifdef USE_WX_ZOOM
  450. /* Using wxDC scaling requires clipping in drawing (logical) units. */
  451. m_ClipBox.SetOrigin( CalcUnscrolledPosition( wxPoint( 0, 0 ) ) );
  452. m_ClipBox.Inflate( CLIP_BOX_PADDING );
  453. m_ClipBox.m_Pos.x = wxRound( (double) m_ClipBox.m_Pos.x / scalar );
  454. m_ClipBox.m_Pos.y = wxRound( (double) m_ClipBox.m_Pos.y / scalar );
  455. m_ClipBox.m_Pos += Screen->m_DrawOrg;
  456. m_ClipBox.m_Size.x = wxRound( (double) m_ClipBox.m_Size.x / scalar );
  457. m_ClipBox.m_Size.y = wxRound( (double) m_ClipBox.m_Size.y / scalar );
  458. #endif
  459. Screen->m_ScrollbarPos.x = GetScrollPos( wxHORIZONTAL );
  460. Screen->m_ScrollbarPos.y = GetScrollPos( wxVERTICAL );
  461. }
  462. void WinEDA_DrawPanel::EraseScreen( wxDC* DC )
  463. {
  464. GRSetDrawMode( DC, GR_COPY );
  465. GRSFilledRect( &m_ClipBox, DC, m_ClipBox.GetX(), m_ClipBox.GetY(),
  466. m_ClipBox.GetRight(), m_ClipBox.GetBottom(),
  467. 0, g_DrawBgColor, g_DrawBgColor );
  468. /* Set to one (1) to draw bounding box validate bounding box calculation. */
  469. #if DEBUG_SHOW_CLIP_RECT
  470. EDA_Rect bBox = m_ClipBox;
  471. bBox.Inflate( -DC->DeviceToLogicalXRel( 1 ) );
  472. GRRect( NULL, DC, bBox.GetOrigin().x, bBox.GetOrigin().y,
  473. bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
  474. #endif
  475. }
  476. void WinEDA_DrawPanel::DoPrepareDC(wxDC& dc)
  477. {
  478. #ifdef USE_WX_ZOOM
  479. if( GetScreen() != NULL )
  480. {
  481. double scale = GetScreen()->GetScalingFactor();
  482. dc.SetUserScale( scale, scale );
  483. wxPoint pt = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
  484. dc.SetDeviceOrigin( -pt.x, -pt.y );
  485. pt = GetScreen()->m_DrawOrg;
  486. dc.SetLogicalOrigin( pt.x, pt.y );
  487. }
  488. #endif
  489. GRResetPenAndBrush( &dc );
  490. dc.SetBackgroundMode( wxTRANSPARENT );
  491. SetBoundaryBox( &dc );
  492. }
  493. void WinEDA_DrawPanel::OnPaint( wxPaintEvent& event )
  494. {
  495. if( GetScreen() == NULL )
  496. {
  497. event.Skip();
  498. return;
  499. }
  500. INSTALL_PAINTDC( paintDC, this );
  501. /* wxAutoBufferedPaintDC does not work correctly by setting the user scale and
  502. * logcial offset. The bitmap coordinates and scaling are not effected by the
  503. * code below. It appears that the wxBufferPaintDC needs to be created with the
  504. * wxBUFFER_VIRTUAL_AREA set and the wirtual method wxWindow::PrepareDC() needs
  505. * to be overridden to set up the buffered paint DC properly. The bitmap grid
  506. * draw code ( see DrawGrid() below ) will have to be fixed before this can be
  507. * implemented.
  508. */
  509. EDA_Rect tmp = m_ClipBox;
  510. // Get the union of all rectangles in the update region.
  511. wxRect PaintClipBox = GetUpdateRegion().GetBox();
  512. #if DEBUG_DUMP_CLIP_COORDS
  513. wxLogDebug( wxT( "1) PaintClipBox=(%d, %d, %d, %d), m_ClipBox=(%d, %d, %d, %d)" ),
  514. PaintClipBox.x, PaintClipBox.y, PaintClipBox.width, PaintClipBox.height,
  515. m_ClipBox.m_Pos.x, m_ClipBox.m_Pos.y, m_ClipBox.m_Size.x, m_ClipBox.m_Size.y );
  516. #endif
  517. #if defined( USE_WX_ZOOM )
  518. /* When using wxDC scaling the clipping region coordinates are in drawing
  519. * (logical) units.
  520. */
  521. double scalar = GetScreen()->GetScalingFactor();
  522. m_ClipBox.m_Pos = CalcUnscrolledPosition( PaintClipBox.GetPosition() );
  523. m_ClipBox.m_Size = PaintClipBox.GetSize();
  524. m_ClipBox.Inflate( CLIP_BOX_PADDING );
  525. m_ClipBox.m_Pos.x = wxRound( (double) m_ClipBox.m_Pos.x / scalar );
  526. m_ClipBox.m_Pos.y = wxRound( (double) m_ClipBox.m_Pos.y / scalar );
  527. m_ClipBox.m_Pos += GetScreen()->m_DrawOrg;
  528. m_ClipBox.m_Size.x = wxRound( (double) m_ClipBox.m_Size.x / scalar );
  529. m_ClipBox.m_Size.y = wxRound( (double) m_ClipBox.m_Size.y / scalar );
  530. PaintClipBox = m_ClipBox;
  531. #else
  532. /* When using Kicad's scaling the clipping region coordinates are in screen
  533. * (device) units.
  534. */
  535. m_ClipBox.SetX( PaintClipBox.GetX() );
  536. m_ClipBox.SetY( PaintClipBox.GetY() );
  537. m_ClipBox.SetWidth( PaintClipBox.GetWidth() );
  538. m_ClipBox.SetHeight( PaintClipBox.GetHeight() );
  539. // Be sure the drawpanel clipbox is bigger than the region to repair:
  540. m_ClipBox.Inflate( 1 ); // Give it one pixel more in each direction
  541. #endif
  542. #if DEBUG_DUMP_CLIP_COORDS
  543. wxLogDebug( wxT( "2) PaintClipBox=(%d, %d, %d, %d), m_ClipBox=(%d, %d, %d, %d)" ),
  544. PaintClipBox.x, PaintClipBox.y, PaintClipBox.width, PaintClipBox.height,
  545. m_ClipBox.m_Pos.x, m_ClipBox.m_Pos.y, m_ClipBox.m_Size.x, m_ClipBox.m_Size.y );
  546. #endif
  547. // call ~wxDCClipper() before ~wxPaintDC()
  548. {
  549. wxDCClipper dcclip( paintDC, PaintClipBox );
  550. ReDraw( &paintDC, m_DisableEraseBG ? false : true );
  551. }
  552. m_ClipBox = tmp;
  553. event.Skip();
  554. }
  555. void WinEDA_DrawPanel::ReDraw( wxDC* DC, bool erasebg )
  556. {
  557. BASE_SCREEN* Screen = GetScreen();
  558. if( Screen == NULL )
  559. return;
  560. if( ( g_DrawBgColor != WHITE ) && ( g_DrawBgColor != BLACK ) )
  561. g_DrawBgColor = BLACK;
  562. if( g_DrawBgColor == WHITE )
  563. {
  564. g_XorMode = GR_NXOR;
  565. g_GhostColor = BLACK;
  566. }
  567. else
  568. {
  569. g_XorMode = GR_XOR;
  570. g_GhostColor = WHITE;
  571. }
  572. if( erasebg )
  573. EraseScreen( DC );
  574. SetBackgroundColour( wxColour( ColorRefs[g_DrawBgColor].m_Red,
  575. ColorRefs[g_DrawBgColor].m_Green,
  576. ColorRefs[g_DrawBgColor].m_Blue ) );
  577. GRResetPenAndBrush( DC );
  578. DC->SetBackground( *wxBLACK_BRUSH );
  579. DC->SetBackgroundMode( wxTRANSPARENT );
  580. m_Parent->RedrawActiveWindow( DC, erasebg );
  581. }
  582. /**
  583. * Function DrawBackGround
  584. * @param DC = current Device Context
  585. * Draws (if allowed) :
  586. * the grid
  587. * X and Y axis
  588. * X and Y auxiliary axis
  589. */
  590. void WinEDA_DrawPanel::DrawBackGround( wxDC* DC )
  591. {
  592. int axis_color = BLUE;
  593. BASE_SCREEN* screen = GetScreen();
  594. GRSetDrawMode( DC, GR_COPY );
  595. if( m_Parent->IsGridVisible() )
  596. DrawGrid( DC );
  597. /* Draw axis */
  598. if( m_Parent->m_Draw_Axis )
  599. {
  600. /* Draw the Y axis */
  601. GRDashedLine( &m_ClipBox, DC, 0, -screen->ReturnPageSize().y,
  602. 0, screen->ReturnPageSize().y, 0, axis_color );
  603. /* Draw the X axis */
  604. GRDashedLine( &m_ClipBox, DC, -screen->ReturnPageSize().x, 0,
  605. screen->ReturnPageSize().x, 0, 0, axis_color );
  606. }
  607. if( m_Parent->m_Draw_Auxiliary_Axis )
  608. DrawAuxiliaryAxis( DC, GR_COPY );
  609. if( m_Parent->m_Draw_Grid_Axis )
  610. DrawGridAxis( DC, GR_COPY );
  611. }
  612. /**
  613. * Function DrawGrid
  614. * @param DC = current Device Context
  615. * draws the grid
  616. * - the grid is drawn only if the zoom level allows a good visibility
  617. * - the grid is always centered on the screen center
  618. */
  619. void WinEDA_DrawPanel::DrawGrid( wxDC* DC )
  620. {
  621. #define MIN_GRID_SIZE 10 // min grid size in pixels to allow drawing
  622. BASE_SCREEN* screen = GetScreen();
  623. int ii, jj, xg, yg;
  624. wxRealPoint screen_grid_size;
  625. wxSize size;
  626. wxPoint org;
  627. wxRealPoint dgrid;
  628. /* The grid must be visible. this is possible only is grid value
  629. * and zoom value are sufficient
  630. */
  631. screen_grid_size = screen->GetGridSize();
  632. org = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
  633. screen->m_StartVisu = org;
  634. size = GetClientSize();
  635. #ifdef USE_WX_ZOOM
  636. dgrid.x = DC->LogicalToDeviceXRel( wxRound( screen_grid_size.x ) );
  637. dgrid.y = DC->LogicalToDeviceXRel( wxRound( screen_grid_size.y ) );
  638. org = m_ClipBox.m_Pos;
  639. size = m_ClipBox.m_Size;
  640. #else
  641. dgrid = screen_grid_size;
  642. screen->Scale( dgrid ); // dgrid = grid size in pixels
  643. screen->Unscale( size );
  644. screen->Unscale( org );
  645. org += screen->m_DrawOrg;
  646. #endif
  647. // if the grid size is small ( < MIN_GRID_SIZE pixels ) do not display all points
  648. bool double_size = false;
  649. if( dgrid.x < MIN_GRID_SIZE )
  650. {
  651. double_size = true;
  652. dgrid.x *= 2;
  653. }
  654. if( dgrid.x < MIN_GRID_SIZE )
  655. return; // The X grid is too small: do not show it
  656. if( dgrid.y < MIN_GRID_SIZE )
  657. {
  658. double_size = true;
  659. dgrid.y *= 2;
  660. }
  661. if( dgrid.y < MIN_GRID_SIZE )
  662. return; // The Y grid is too small: do not show it
  663. m_Parent->PutOnGrid( &org );
  664. GRSetColorPen( DC, m_Parent->GetGridColor() );
  665. int xpos, ypos;
  666. /* When we use an double_size grid, we must align grid ord on double grid
  667. */
  668. int increment = double_size ? 2 : 1;
  669. if( double_size )
  670. {
  671. wxRealPoint dblgrid = screen_grid_size + screen_grid_size;
  672. m_Parent->PutOnGrid( &org, &dblgrid );
  673. }
  674. // Draw grid: the best algorithm depend on the platform.
  675. // under macOSX, the first method is better
  676. // under window, the second method is better
  677. // Under linux, to be tested (could be depend on linux versions
  678. // so perhaps could be necessary to set this option at run time.
  679. /* The bitmap grid drawing code below cannot be used when wxDC scaling is used
  680. * as it does not scale the grid bitmap properly. This needs to be fixed.
  681. */
  682. #if defined( __WXMAC__ ) && !defined( USE_WX_ZOOM )
  683. // Use a pixel based draw to display grid
  684. // When is not used USE_WX_ZOOM
  685. for( ii = 0; ; ii += increment )
  686. {
  687. xg = wxRound( ii * screen_grid_size.x );
  688. if( xg > size.x )
  689. break;
  690. xpos = org.x + xg;
  691. xpos = GRMapX( xpos );
  692. for( jj = 0; ; jj += increment )
  693. {
  694. yg = wxRound( jj * screen_grid_size.y );
  695. if( yg > size.y )
  696. break;
  697. ypos = org.y + yg;
  698. DC->DrawPoint( xpos, GRMapY( ypos ) );
  699. }
  700. }
  701. #endif
  702. #if defined( USE_WX_ZOOM )
  703. // Use a pixel based draw to display grid
  704. // There is a lot of calls, so the cost is hight
  705. // and grid is slowly drawn on some platforms
  706. for( ii = 0; ; ii += increment )
  707. {
  708. xg = wxRound( ii * screen_grid_size.x );
  709. if( xg > size.x )
  710. break;
  711. xpos = org.x + xg;
  712. xpos = GRMapX( xpos );
  713. if( xpos < m_ClipBox.GetOrigin().x ) // column not in active screen area.
  714. continue;
  715. if( xpos > m_ClipBox.GetEnd().x ) // end of active area reached.
  716. break;
  717. for( jj = 0; ; jj += increment )
  718. {
  719. yg = wxRound( jj * screen_grid_size.y );
  720. if( yg > size.y )
  721. break;
  722. ypos = org.y + yg;
  723. if( ypos < m_ClipBox.GetOrigin().y ) // column not in active screen area.
  724. continue;
  725. if( ypos > m_ClipBox.GetEnd().y ) // end of active area reached.
  726. break;
  727. DC->DrawPoint( xpos, GRMapY( ypos ) );
  728. }
  729. }
  730. #else
  731. /* Currently on test: Use a fast way to draw the grid
  732. * But this is fast only if the Blit function is fast. Not true on all platforms
  733. * a grid column is drawn; and then copied to others grid columns
  734. * this is possible because the grid is drawn only after clearing the screen.
  735. *
  736. * A first grid column is drawn in a temporary bitmap,
  737. * and after is duplicated using the Blit function
  738. * (copy from a screen area to an other screen area)
  739. */
  740. wxSize screenSize = GetClientSize();
  741. wxMemoryDC tmpDC;
  742. wxBitmap tmpBM( 1, screenSize.y );
  743. tmpDC.SelectObject( tmpBM );
  744. GRSetColorPen( &tmpDC, g_DrawBgColor );
  745. tmpDC.DrawLine( 0, 0, 0, screenSize.y-1 ); // init background
  746. GRSetColorPen( &tmpDC, m_Parent->GetGridColor() );
  747. for( jj = 0; ; jj += increment ) // draw grid points
  748. {
  749. yg = wxRound( jj * screen_grid_size.y );
  750. ypos = screen->Scale( yg );
  751. if( ypos > screenSize.y )
  752. break;
  753. tmpDC.DrawPoint( 0, ypos );
  754. }
  755. // Use the layer bitmap itself as a mask when blitting.
  756. // The bitmap cannot be referenced by a device context
  757. // when setting the mask.
  758. tmpDC.SelectObject( wxNullBitmap );
  759. tmpBM.SetMask( new wxMask( tmpBM, MakeColour( g_DrawBgColor ) ) );
  760. tmpDC.SelectObject( tmpBM );
  761. ypos = GRMapY( org.y );
  762. for( ii = 0; ; ii += increment )
  763. {
  764. xg = wxRound( ii * screen_grid_size.x );
  765. if( xg > size.x )
  766. break;
  767. xpos = GRMapX( org.x + xg );
  768. if( xpos < m_ClipBox.GetOrigin().x) // column not in active screen area.
  769. continue;
  770. if( xpos > m_ClipBox.GetEnd().x) // end of active area reached.
  771. break;
  772. DC->Blit( xpos, ypos, 1, screenSize.y, &tmpDC, 0, 0, wxCOPY, true );
  773. }
  774. #endif
  775. }
  776. /**
  777. * Function DrawAuxiliaryAxis
  778. * Draw the Auxiliary Axis, used in pcbnew which as origin coordinates
  779. * for gerber and excellon files
  780. * @param DC = current Device Context
  781. */
  782. void WinEDA_DrawPanel::DrawAuxiliaryAxis( wxDC* DC, int drawmode )
  783. {
  784. if( m_Parent->m_Auxiliary_Axis_Position == wxPoint( 0, 0 ) )
  785. return;
  786. int Color = DARKRED;
  787. BASE_SCREEN* screen = GetScreen();
  788. GRSetDrawMode( DC, drawmode );
  789. /* Draw the Y axis */
  790. GRDashedLine( &m_ClipBox, DC,
  791. m_Parent->m_Auxiliary_Axis_Position.x,
  792. -screen->ReturnPageSize().y,
  793. m_Parent->m_Auxiliary_Axis_Position.x,
  794. screen->ReturnPageSize().y,
  795. 0, Color );
  796. /* Draw the X axis */
  797. GRDashedLine( &m_ClipBox, DC,
  798. -screen->ReturnPageSize().x,
  799. m_Parent->m_Auxiliary_Axis_Position.y,
  800. screen->ReturnPageSize().x,
  801. m_Parent->m_Auxiliary_Axis_Position.y,
  802. 0, Color );
  803. }
  804. /********************************************************************/
  805. void WinEDA_DrawPanel::DrawGridAxis( wxDC* DC, int drawmode )
  806. /********************************************************************/
  807. {
  808. BASE_SCREEN* screen = GetScreen();
  809. if( !m_Parent->m_Draw_Grid_Axis
  810. || ( screen->m_GridOrigin.x == 0
  811. && screen->m_GridOrigin.y == 0 ) )
  812. return;
  813. int Color = m_Parent->GetGridColor();
  814. GRSetDrawMode( DC, drawmode );
  815. /* Draw the Y axis */
  816. GRDashedLine( &m_ClipBox, DC,
  817. screen->m_GridOrigin.x,
  818. -screen->ReturnPageSize().y,
  819. screen->m_GridOrigin.x,
  820. screen->ReturnPageSize().y,
  821. 0, Color );
  822. /* Draw the X axis */
  823. GRDashedLine( &m_ClipBox, DC,
  824. -screen->ReturnPageSize().x,
  825. screen->m_GridOrigin.y,
  826. screen->ReturnPageSize().x,
  827. screen->m_GridOrigin.y,
  828. 0, Color );
  829. }
  830. /** Build and display a Popup menu on a right mouse button click
  831. * @return true if a popup menu is shown, or false
  832. */
  833. bool WinEDA_DrawPanel::OnRightClick( wxMouseEvent& event )
  834. {
  835. wxPoint pos;
  836. wxMenu MasterMenu;
  837. pos = event.GetPosition();
  838. if( !m_Parent->OnRightClick( pos, &MasterMenu ) )
  839. return false;
  840. m_Parent->AddMenuZoomAndGrid( &MasterMenu );
  841. m_IgnoreMouseEvents = TRUE;
  842. PopupMenu( &MasterMenu, pos );
  843. MouseToCursorSchema();
  844. m_IgnoreMouseEvents = false;
  845. return true;
  846. }
  847. // Called when the canvas receives a mouse event leaving frame.
  848. void WinEDA_DrawPanel::OnMouseLeaving( wxMouseEvent& event )
  849. {
  850. if( ManageCurseur == NULL ) // No command in progress.
  851. m_AutoPAN_Request = false;
  852. if( !m_AutoPAN_Enable || !m_AutoPAN_Request || m_IgnoreMouseEvents )
  853. return;
  854. // Auto pan if mouse is leave working area:
  855. wxSize size = GetClientSize();
  856. if( ( size.x < event.GetX() ) || ( size.y < event.GetY() )
  857. || ( event.GetX() <= 0) || ( event.GetY() <= 0 ) )
  858. {
  859. wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER );
  860. cmd.SetEventObject( this );
  861. GetEventHandler()->ProcessEvent( cmd );
  862. }
  863. }
  864. /*
  865. * Handle mouse wheel events.
  866. *
  867. * The mouse wheel is used to provide support for zooming and panning. This
  868. * is accomplished by converting mouse wheel events in pseudo menu command
  869. * events.
  870. */
  871. void WinEDA_DrawPanel::OnMouseWheel( wxMouseEvent& event )
  872. {
  873. if( m_IgnoreMouseEvents )
  874. return;
  875. wxRect rect = wxRect( wxPoint( 0, 0 ), GetClientSize() );
  876. /* Ignore scroll events if the cursor is outside the drawing area. */
  877. if( event.GetWheelRotation() == 0 || !GetParent()->IsEnabled()
  878. || !rect.Contains( event.GetPosition() ) )
  879. {
  880. #if 0
  881. wxLogDebug( wxT( "OnMouseWheel() position(%d, %d) " ) \
  882. wxT( "rectangle(%d, %d, %d, %d)" ),
  883. event.GetPosition().x, event.GetPosition().y,
  884. rect.x, rect.y, rect.width, rect.height );
  885. #endif
  886. event.Skip();
  887. return;
  888. }
  889. GetScreen()->m_Curseur =
  890. CursorRealPosition( CalcUnscrolledPosition( event.GetPosition() ) );
  891. wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED );
  892. cmd.SetEventObject( this );
  893. // This is a zoom in or out command
  894. if( event.GetWheelRotation() > 0 )
  895. {
  896. if( event.ShiftDown() && !event.ControlDown() )
  897. cmd.SetId( ID_PAN_UP );
  898. else if( event.ControlDown() && !event.ShiftDown() )
  899. cmd.SetId( ID_PAN_LEFT );
  900. else
  901. cmd.SetId( ID_POPUP_ZOOM_IN );
  902. }
  903. else if( event.GetWheelRotation() < 0 )
  904. {
  905. if( event.ShiftDown() && !event.ControlDown() )
  906. cmd.SetId( ID_PAN_DOWN );
  907. else if( event.ControlDown() && !event.ShiftDown() )
  908. cmd.SetId( ID_PAN_RIGHT );
  909. else
  910. cmd.SetId( ID_POPUP_ZOOM_OUT );
  911. }
  912. GetEventHandler()->ProcessEvent( cmd );
  913. event.Skip();
  914. }
  915. // Called when the canvas receives a mouse event.
  916. void WinEDA_DrawPanel::OnMouseEvent( wxMouseEvent& event )
  917. {
  918. int localrealbutt = 0, localbutt = 0, localkey = 0;
  919. BASE_SCREEN* screen = GetScreen();
  920. static WinEDA_DrawPanel* LastPanel;
  921. if( !screen )
  922. return;
  923. /* Adjust value to filter mouse displacement before consider the drag
  924. * mouse is really a drag command, not just a movement while click
  925. */
  926. #define MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND 5
  927. /* Count the drag events. Used to filter mouse moves before starting a
  928. * block command. A block command can be started only if
  929. * MinDragEventCount > MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND
  930. * and m_CanStartBlock >= 0
  931. * in order to avoid spurious block commands.
  932. */
  933. static int MinDragEventCount;
  934. if( event.Leaving() )
  935. {
  936. m_CanStartBlock = -1;
  937. }
  938. if( ManageCurseur == NULL ) // No command in progress
  939. m_AutoPAN_Request = false;
  940. if( m_Parent->m_FrameIsActive )
  941. SetFocus();
  942. else
  943. return;
  944. if( !event.IsButton() && !event.Moving()
  945. && !event.Dragging() && !localkey )
  946. {
  947. return;
  948. }
  949. if( event.RightDown() )
  950. {
  951. OnRightClick( event );
  952. return;
  953. }
  954. if( m_IgnoreMouseEvents )
  955. return;
  956. if( event.LeftIsDown() )
  957. localrealbutt |= GR_M_LEFT_DOWN;
  958. if( event.MiddleIsDown() )
  959. localrealbutt |= GR_M_MIDDLE_DOWN;
  960. if( event.LeftDown() )
  961. localbutt = GR_M_LEFT_DOWN;
  962. if( event.ButtonDClick( 1 ) )
  963. localbutt = GR_M_LEFT_DOWN | GR_M_DCLICK;
  964. if( event.MiddleDown() )
  965. localbutt = GR_M_MIDDLE_DOWN;
  966. localrealbutt |= localbutt; /* compensation default wxGTK */
  967. /* Compute the cursor position in screen (device) units. */
  968. screen->m_MousePositionInPixels = CalcUnscrolledPosition( event.GetPosition() );
  969. /* Compute the cursor position in drawing (logical) units. */
  970. screen->m_MousePosition =
  971. CursorRealPosition( CalcUnscrolledPosition( event.GetPosition() ) );
  972. INSTALL_DC( DC, this );
  973. int kbstat = 0;
  974. DC.SetBackground( *wxBLACK_BRUSH );
  975. g_KeyPressed = localkey;
  976. if( event.ShiftDown() )
  977. kbstat |= GR_KB_SHIFT;
  978. if( event.ControlDown() )
  979. kbstat |= GR_KB_CTRL;
  980. if( event.AltDown() )
  981. kbstat |= GR_KB_ALT;
  982. g_MouseOldButtons = localrealbutt;
  983. // Calling Double Click and Click functions :
  984. if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
  985. {
  986. m_Parent->OnLeftDClick( &DC, screen->m_MousePositionInPixels );
  987. // inhibit a response to the mouse left button release,
  988. // because we have a double click, and we do not want a new
  989. // OnLeftClick command at end of this Double Click
  990. s_IgnoreNextLeftButtonRelease = true;
  991. }
  992. else if( event.LeftUp() )
  993. {
  994. // A block command is in progress: a left up is the end of block
  995. // or this is the end of a double click, already seen
  996. if( screen->m_BlockLocate.m_State==STATE_NO_BLOCK
  997. && !s_IgnoreNextLeftButtonRelease )
  998. m_Parent->OnLeftClick( &DC, screen->m_MousePositionInPixels );
  999. s_IgnoreNextLeftButtonRelease = false;
  1000. }
  1001. if( !event.LeftIsDown() )
  1002. {
  1003. /* be sure there is a response to a left button release command
  1004. * even when a LeftUp event is not seen. This happens when a
  1005. * double click opens a dialog box, and the release mouse button
  1006. * is made when the dialog box is open.
  1007. */
  1008. s_IgnoreNextLeftButtonRelease = false;
  1009. }
  1010. if( event.ButtonUp( wxMOUSE_BTN_MIDDLE )
  1011. && (screen->m_BlockLocate.m_State == STATE_NO_BLOCK) )
  1012. {
  1013. // The middle button has been released, with no block command:
  1014. // We use it for a zoom center at cursor position command
  1015. wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED,
  1016. ID_POPUP_ZOOM_CENTER );
  1017. cmd.SetEventObject( this );
  1018. GetEventHandler()->ProcessEvent( cmd );
  1019. }
  1020. /* Calling the general function on mouse changes (and pseudo key commands) */
  1021. m_Parent->GeneralControle( &DC, screen->m_MousePositionInPixels );
  1022. /*******************************/
  1023. /* Control of block commands : */
  1024. /*******************************/
  1025. // Command block can't start if mouse is dragging a new panel
  1026. if( LastPanel != this )
  1027. {
  1028. MinDragEventCount = 0;
  1029. m_CanStartBlock = -1;
  1030. }
  1031. /* A new command block can start after a release buttons
  1032. * and if the drag is enough
  1033. * This is to avoid a false start block when a dialog box is dismissed,
  1034. * or when changing panels in hierarchy navigation
  1035. * or when clicking while and moving mouse
  1036. */
  1037. if( !event.LeftIsDown() && !event.MiddleIsDown() )
  1038. {
  1039. MinDragEventCount = 0;
  1040. m_CanStartBlock = 0;
  1041. /* Remember the last cursor position when a drag mouse starts
  1042. * this is the last position ** before ** clicking a button
  1043. * this is useful to start a block command from the point where the
  1044. * mouse was clicked first
  1045. * (a filter creates a delay for the real block command start, and
  1046. * we must remember this point)
  1047. */
  1048. m_CursorStartPos = screen->m_Curseur;
  1049. }
  1050. if( m_Block_Enable && !(localbutt & GR_M_DCLICK) )
  1051. {
  1052. if( ( screen->m_BlockLocate.m_Command == BLOCK_IDLE )
  1053. || ( screen->m_BlockLocate.m_State == STATE_NO_BLOCK ) )
  1054. {
  1055. screen->m_BlockLocate.SetOrigin( m_CursorStartPos );
  1056. }
  1057. if( event.LeftDown() || event.MiddleDown() )
  1058. {
  1059. if( screen->m_BlockLocate.m_State == STATE_BLOCK_MOVE )
  1060. {
  1061. m_AutoPAN_Request = false;
  1062. m_Parent->HandleBlockPlace( &DC );
  1063. s_IgnoreNextLeftButtonRelease = true;
  1064. }
  1065. }
  1066. else if( ( m_CanStartBlock >= 0 )
  1067. && ( event.LeftIsDown() || event.MiddleIsDown() )
  1068. && ManageCurseur == NULL
  1069. && ForceCloseManageCurseur == NULL )
  1070. {
  1071. // Mouse is dragging: if no block in progress, start a block
  1072. // command.
  1073. if( screen->m_BlockLocate.m_State == STATE_NO_BLOCK )
  1074. {
  1075. // Start a block command
  1076. int cmd_type = kbstat;
  1077. if( event.MiddleIsDown() )
  1078. cmd_type |= MOUSE_MIDDLE;
  1079. /* A block command is started if the drag is enough. A small
  1080. * drag is ignored (it is certainly a little mouse move when
  1081. * clicking) not really a drag mouse
  1082. */
  1083. if( MinDragEventCount < MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND )
  1084. MinDragEventCount++;
  1085. else
  1086. {
  1087. if( !m_Parent->HandleBlockBegin( &DC, cmd_type,
  1088. m_CursorStartPos ) )
  1089. {
  1090. // should not occurs: error
  1091. m_Parent->DisplayToolMsg(
  1092. wxT( "WinEDA_DrawPanel::OnMouseEvent() Block Error" ) );
  1093. }
  1094. else
  1095. {
  1096. m_AutoPAN_Request = TRUE;
  1097. SetCursor( m_PanelCursor = wxCURSOR_SIZING );
  1098. }
  1099. }
  1100. }
  1101. }
  1102. if( event.ButtonUp( wxMOUSE_BTN_LEFT ) || event.ButtonUp( wxMOUSE_BTN_MIDDLE ) )
  1103. {
  1104. /* Release the mouse button: end of block.
  1105. * The command can finish (DELETE) or have a next command (MOVE,
  1106. * COPY). However the block command is canceled if the block
  1107. * size is small because a block command filtering is already
  1108. * made, this case happens, but only when the on grid cursor has
  1109. * not moved.
  1110. */
  1111. #define BLOCK_MINSIZE_LIMIT 1
  1112. bool BlockIsSmall =
  1113. ( ABS( screen->Scale( screen->m_BlockLocate.GetWidth() ) )
  1114. < BLOCK_MINSIZE_LIMIT)
  1115. && ( ABS( screen->Scale( screen->m_BlockLocate.GetHeight() ) )
  1116. < BLOCK_MINSIZE_LIMIT);
  1117. if( (screen->m_BlockLocate.m_State
  1118. != STATE_NO_BLOCK) && BlockIsSmall )
  1119. {
  1120. if( ForceCloseManageCurseur )
  1121. {
  1122. ForceCloseManageCurseur( this, &DC );
  1123. m_AutoPAN_Request = false;
  1124. }
  1125. SetCursor( m_PanelCursor = m_PanelDefaultCursor );
  1126. }
  1127. else if( screen->m_BlockLocate.m_State == STATE_BLOCK_END )
  1128. {
  1129. m_AutoPAN_Request = false;
  1130. m_Parent->HandleBlockEnd( &DC );
  1131. SetCursor( m_PanelCursor = m_PanelDefaultCursor );
  1132. if( screen->m_BlockLocate.m_State == STATE_BLOCK_MOVE )
  1133. {
  1134. m_AutoPAN_Request = TRUE;
  1135. SetCursor( m_PanelCursor = wxCURSOR_HAND );
  1136. }
  1137. }
  1138. }
  1139. }
  1140. // End of block command on a double click
  1141. // To avoid an unwanted block move command if the mouse is moved while
  1142. // double clicking
  1143. if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
  1144. {
  1145. if( screen->m_BlockLocate.m_Command != BLOCK_IDLE )
  1146. {
  1147. if( ForceCloseManageCurseur )
  1148. {
  1149. ForceCloseManageCurseur( this, &DC );
  1150. m_AutoPAN_Request = false;
  1151. }
  1152. }
  1153. }
  1154. #if 0
  1155. wxString msg_debug;
  1156. msg_debug.Printf( " block state %d, cmd %d",
  1157. screen->m_BlockLocate.m_State,
  1158. screen->m_BlockLocate.m_Command );
  1159. m_Parent->PrintMsg( msg_debug );
  1160. #endif
  1161. LastPanel = this;
  1162. }
  1163. void WinEDA_DrawPanel::OnKeyEvent( wxKeyEvent& event )
  1164. {
  1165. long key, localkey;
  1166. bool escape = false;
  1167. wxPoint pos;
  1168. key = localkey = event.GetKeyCode();
  1169. switch( localkey )
  1170. {
  1171. case WXK_CONTROL:
  1172. case WXK_CAPITAL:
  1173. case WXK_SHIFT:
  1174. case WXK_NUMLOCK:
  1175. case WXK_LBUTTON:
  1176. case WXK_RBUTTON:
  1177. case WXK_ALT:
  1178. return;
  1179. case WXK_ESCAPE:
  1180. escape = m_AbortRequest = TRUE;
  1181. break;
  1182. }
  1183. if( event.ControlDown() )
  1184. localkey |= GR_KB_CTRL;
  1185. if( event.AltDown() )
  1186. localkey |= GR_KB_ALT;
  1187. if( event.ShiftDown() && (key > 256) )
  1188. localkey |= GR_KB_SHIFT;
  1189. /* Normalize keys code to easily handle keys from Ctrl+A to Ctrl+Z
  1190. * They have an ascii code from 1 to 27 remapped
  1191. * to GR_KB_CTRL + 'A' to GR_KB_CTRL + 'Z'
  1192. */
  1193. if( (localkey > GR_KB_CTRL) && (localkey <= GR_KB_CTRL+26) )
  1194. localkey += 'A' - 1;
  1195. INSTALL_DC( DC, this );
  1196. BASE_SCREEN* Screen = GetScreen();
  1197. g_KeyPressed = localkey;
  1198. if( escape )
  1199. {
  1200. if( ManageCurseur && ForceCloseManageCurseur )
  1201. {
  1202. SetCursor( m_PanelCursor = m_PanelDefaultCursor );
  1203. ForceCloseManageCurseur( this, &DC );
  1204. SetCursor( m_PanelCursor = m_PanelDefaultCursor );
  1205. }
  1206. else
  1207. {
  1208. m_PanelCursor = m_PanelDefaultCursor = wxCURSOR_ARROW;
  1209. m_Parent->SetToolID( 0, m_PanelCursor, wxEmptyString );
  1210. }
  1211. }
  1212. /* Some key commands use the current mouse position: refresh it */
  1213. pos = CalcUnscrolledPosition( wxGetMousePosition() - GetScreenPosition() );
  1214. /* Compute cursor position in screen units (pixel) including the
  1215. * current scroll bar position. Also known as device units to wxDC. */
  1216. Screen->m_MousePositionInPixels = pos;
  1217. /* Compute the cursor position in drawing units. Also known as logical units
  1218. * to wxDC. */
  1219. Screen->m_MousePosition = CursorRealPosition( pos );
  1220. m_Parent->GeneralControle( &DC, pos );
  1221. #if 0
  1222. event.Skip(); // Allow menu shortcut processing
  1223. #endif
  1224. }
  1225. void WinEDA_DrawPanel::OnPan( wxCommandEvent& event )
  1226. {
  1227. int x, y;
  1228. int ppux, ppuy;
  1229. int unitsX, unitsY;
  1230. int maxX, maxY;
  1231. GetViewStart( &x, &y );
  1232. GetScrollPixelsPerUnit( &ppux, &ppuy );
  1233. GetVirtualSize( &unitsX, &unitsY );
  1234. maxX = unitsX;
  1235. maxY = unitsY;
  1236. unitsX /= ppux;
  1237. unitsY /= ppuy;
  1238. switch( event.GetId() )
  1239. {
  1240. case ID_PAN_UP:
  1241. y -= m_scrollIncrementY;
  1242. break;
  1243. case ID_PAN_DOWN:
  1244. y += m_scrollIncrementY;
  1245. break;
  1246. case ID_PAN_LEFT:
  1247. x -= m_scrollIncrementX;
  1248. break;
  1249. case ID_PAN_RIGHT:
  1250. x += m_scrollIncrementX;
  1251. break;
  1252. default:
  1253. wxLogDebug( wxT( "Unknown ID %d in WinEDA_DrawPanel::OnPan()." ),
  1254. event.GetId() );
  1255. }
  1256. if( x < 0 )
  1257. x = 0;
  1258. if( y < 0 )
  1259. y = 0;
  1260. if( x > maxX )
  1261. x = maxX;
  1262. if( y > maxY )
  1263. y = maxY;
  1264. Scroll( x/ppux, y/ppuy );
  1265. }
  1266. void WinEDA_DrawPanel::UnManageCursor( int id, int cursor,
  1267. const wxString& title )
  1268. {
  1269. if( ManageCurseur && ForceCloseManageCurseur )
  1270. {
  1271. INSTALL_DC( dc, this );
  1272. ForceCloseManageCurseur( this, &dc );
  1273. m_AutoPAN_Request = false;
  1274. }
  1275. if( id != -1 && cursor != -1 )
  1276. {
  1277. wxASSERT( cursor > wxCURSOR_NONE && cursor < wxCURSOR_MAX );
  1278. m_Parent->SetToolID( id, cursor, title );
  1279. }
  1280. }