1 /////////////////////////////////////////////////////////////////////////////
2 // Name: plotctrl.cpp
3 // Purpose: wxPlotCtrl
4 // Author: John Labenski, Robert Roebling
5 // Modified by:
6 // Created: 8/27/2002
7 // Copyright: (c) John Labenski, Robert Roebling
8 // Licence: wxWindows license
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #ifndef WX_PRECOMP
19 #include "wx/panel.h"
20 #include "wx/scrolbar.h"
21 #include "wx/event.h"
22 #include "wx/timer.h"
23 #include "wx/dcmemory.h"
24 #include "wx/msgdlg.h"
25 #include "wx/geometry.h"
26 #include "wx/sizer.h"
27 #include "wx/dcscreen.h"
28 #include "wx/dcclient.h"
29 #include "wx/textctrl.h"
30 #endif // WX_PRECOMP
31
32 #include "wx/splitter.h"
33 #include "wx/math.h"
34 #include "wx/image.h"
35
36 #include "wx/plotctrl/plotctrl.h"
37
38 #include <cmath>
39 #include <cfloat>
40 #include <climits>
41
42 // MSVC hogs global namespace with these min/max macros - remove them
43 #ifdef max
44 #undef max
45 #endif
46 #ifdef min
47 #undef min
48 #endif
49 #ifdef GetYValue // Visual Studio 7 defines this
50 #undef GetYValue
51 #endif
52
53 #ifdef wxFinite
54 #undef wxFinite
55 #define wxFinite(x) std::isfinite(x)
56 #endif // wxFinite
57
58
59 //#define LONG_TO_WXCOLOUR(c) wxColour((unsigned char)((c>>16)&0xFF), (unsigned char)((c>>8 )&0xFF), (unsigned char)((c)&0xFF))
60 //#define WXCOLOUR_TO_LONG(c) ((c.Red()<<16)|(c.Green()<<8)|(c.Blue()))
61
62 #define RINT(x) int((x) >= 0 ? ((x) + 0.5) : ((x) - 0.5))
63
64 #if !wxCHECK_VERSION(2,5,0)
WXRECT2DDOUBLE_EQUAL(const wxRect2DDouble & r1,const wxRect2DDouble & r2)65 bool WXRECT2DDOUBLE_EQUAL(const wxRect2DDouble& r1, const wxRect2DDouble& r2)
66 {
67 return (r1.m_x == r1.m_x) && (r1.m_y == r1.m_y) &&
68 (r1.m_width == r1.m_width) && (r1.m_height == r1.m_height);
69 }
70 #else
71 #define WXRECT2DDOUBLE_EQUAL(r1, r2) ((r1) == (r2))
72 #endif // wxCHECK_VERSION(2,5,0)
73
74 #if !wxCHECK_VERSION(2,6,0)
75 #define wxIMAGE_OPTION_CUR_HOTSPOT_X wxCUR_HOTSPOT_X
76 #define wxIMAGE_OPTION_CUR_HOTSPOT_Y wxCUR_HOTSPOT_Y
77 #endif //wxCHECK_VERSION(2,6,0)
78
79 //-----------------------------------------------------------------------------
80 // Consts
81 //-----------------------------------------------------------------------------
82
83 #define MAX_PLOT_ZOOMS 5
84 #define TIC_STEPS 3
85
86 std::numeric_limits<wxDouble> wxDouble_limits;
87 const wxDouble wxPlot_MIN_DBL = wxDouble_limits.min()*10;
88 const wxDouble wxPlot_MAX_DBL = wxDouble_limits.max()/10;
89 const wxDouble wxPlot_MAX_RANGE = wxDouble_limits.max()/5;
90
91 //#define BORDER 4
92
93 // Draw borders around the axes, title, and labels for sizing testing
94 //#define DRAW_BORDERS
95
96 #include "wx/arrimpl.cpp"
97 WX_DEFINE_OBJARRAY( wxArrayPoint2DDouble );
98 WX_DEFINE_OBJARRAY( wxArrayRect2DDouble );
99 WX_DEFINE_OBJARRAY( wxArrayPlotCurve );
100
101 #include "../art/ledgrey.xpm"
102 #include "../art/ledgreen.xpm"
103
104 #include "../art/hand.xpm"
105 #include "../art/grab.xpm"
106
107 static wxCursor s_handCursor;
108 static wxCursor s_grabCursor;
109
110 // Skip the wxWidgets drawing routines since they calc an unnecessary bounding rect
111 // You may turn this off by defining wxPLOT_FAST_GRAPHICS=0 to the compilier
112 #ifndef wxPLOT_FAST_GRAPHICS
113 #define wxPLOT_FAST_GRAPHICS 0
114 #endif // wxPLOT_FAST_GRAPHICS
115
116 #if defined(__WXGTK__) && wxPLOT_FAST_GRAPHICS
117
118 extern "C" {
119 #include <gdk/gdk.h>
120 }
121 #define INITIALIZE_FAST_GRAPHICS \
122 double dc_scale_x = 1, dc_scale_y = 1; \
123 dc->GetUserScale( &dc_scale_x, &dc_scale_y ); \
124 wxPoint dc_origin = dc->GetDeviceOrigin(); \
125 wxWindowDC *winDC = wxDynamicCast(dc, wxWindowDC); \
126 GdkWindow *window = NULL; \
127 GdkGC *pen = NULL; \
128 if (winDC && (dc_scale_x == 1.0) && (dc_scale_y == 1.0) && (dc_origin == wxPoint(0,0))) \
129 { \
130 window = winDC->m_window; \
131 pen = winDC->m_penGC; \
132 }
133
134 // inline void wxPLOT_DRAW_LINE(wxDC *dc, GdkWindow *win, GdkGC *pen, int x0, int y0, int x1, int y1)
135 #define wxPLOT_DRAW_LINE(dc, win, pen, x0, y0, x1, y1) \
136 if (win && pen) \
137 gdk_draw_line( win, pen, x0, y0, x1, y1 ); \
138 else \
139 dc->DrawLine( x0, y0, x1, y1 );
140
141 // note : need to draw outline since the filled part isn't really a circle
142 // gdk_draw_arc( win, pen, false, x0-2, y0-2, 4, 4, 0, 360*64 ); // false for outline, true for inside
143 //inline void wxPLOT_DRAW_CIRCLE(wxDC *dc, GdkWindow *win, GdkGC *pen, int x0, int y0)
144 #define wxPLOT_DRAW_CIRCLE(dc, win, pen, x0, y0) \
145 if (win && pen) \
146 gdk_draw_arc( win, pen, false, x0-2, y0-2, 4, 4, 0, 360*64 ); \
147 else \
148 dc->DrawCircle(x0, y0, 2);
149
150 #elif defined(__WXMSW__) && wxPLOT_FAST_GRAPHICS
151
152 #define INITIALIZE_FAST_GRAPHICS \
153 double dc_scale_x = 1, dc_scale_y = 1; \
154 dc->GetUserScale( &dc_scale_x, &dc_scale_y ); \
155 wxPoint dc_origin = dc->GetDeviceOrigin(); \
156 HDC window = 0; \
157 if ((dc_scale_x == 1.0) && (dc_scale_y == 1.0) && (dc_origin == wxPoint(0,0))) \
158 window = (HDC)dc->GetHDC(); \
159 int pen = 0; pen = 0; // no unused var warning
160
161 //inline void wxPLOT_DRAW_LINE(wxDC *dc, HDC win, int pen, int x0, int y0, int x1, int y1)
162 #define wxPLOT_DRAW_LINE(dc, win, pen, x0, y0, x1, y1) \
163 if (win) \
164 { \
165 (void)MoveToEx(win, x0, y0, NULL); \
166 (void)LineTo(win, x1, y1); \
167 } \
168 else \
169 dc->DrawLine( x0, y0, x1, y1 );
170
171 //inline void wxPLOT_DRAW_CIRCLE(wxDC *dc, HDC win, int pen, int x0, int y0)
172 #define wxPLOT_DRAW_CIRCLE(dc, win, pen, x0, y0) \
173 if (win) \
174 (void)Ellipse(win, x0-2, y0-2, x0+4, y0+4); \
175 else \
176 dc->DrawCircle(x0, y0, 2);
177
178 #else // !wxPLOT_FAST_GRAPHICS or not gtk/msw
179
180 #define INITIALIZE_FAST_GRAPHICS \
181 int window = 0; window = 0; \
182 int pen = 0; pen = 0;
183
184 //inline void wxPLOT_DRAW_LINE(wxDC *dc, int win, int pen, int x0, int y0, int x1, int y1)
185 #define wxPLOT_DRAW_LINE(dc, win, pen, x0, y0, x1, y1) \
186 dc->DrawLine( x0, y0, x1, y1 );
187
188 //inline void wxPLOT_DRAW_CIRCLE(wxDC *dc, int win, int pen, int x0, int y0)
189 #define wxPLOT_DRAW_CIRCLE(dc, win, pen, x0, y0) \
190 dc->DrawCircle(x0, y0, 2);
191
192 #endif // wxPLOT_FAST_GRAPHICS
193
194
195 // differs from wxRect2DDouble::Intersects by allowing for 0 width or height
wxPlotRect2DDoubleIntersects(const wxRect2DDouble & a,const wxRect2DDouble & b)196 inline bool wxPlotRect2DDoubleIntersects(const wxRect2DDouble &a, const wxRect2DDouble &b)
197 {
198 return (wxMax(a.m_x, b.m_x) <= wxMin(a.GetRight(), b.GetRight())) &&
199 (wxMax(a.m_y, b.m_y) <= wxMin(a.GetBottom(), b.GetBottom()));
200 }
201
202 // same as wxPlotRect2DDouble::Contains, but doesn't convert to wxPoint2DDouble
wxPlotRect2DDoubleContains(double x,double y,const wxRect2DDouble & rect)203 inline bool wxPlotRect2DDoubleContains(double x, double y, const wxRect2DDouble &rect)
204 {
205 return ((x>=rect.m_x) && (y>=rect.m_y) && (x<=rect.GetRight()) && (y<=rect.GetBottom()));
206 }
207
208 // differs from wxRect2DDouble::GetOutCode by swaping top and bottom for plot origin
209 //inline wxOutCode wxPlotRect2DDoubleOutCode( double x, double y, const wxRect2DDouble &rect )
210 //{
211 // return wxOutCode((x < rect.m_x ? wxOutLeft :
212 // (x > rect.GetRight() ? wxOutRight : wxInside )) +
213 // (y < rect.m_y ? wxOutTop :
214 // (y > rect.GetBottom() ? wxOutBottom : wxInside )) );
215 //}
216 #define wxPlotRect2DDoubleOutCode( x, y, rect ) \
217 wxOutCode((x < rect.m_x ? wxOutLeft : \
218 (x > rect.GetRight() ? wxOutRight : wxInside )) + \
219 (y < rect.m_y ? wxOutTop : \
220 (y > rect.GetBottom() ? wxOutBottom : wxInside )) )
221
222
223 // modified Cohen-Sutherland Algorithm for line clipping in at most two passes
224 // the the original endless loop is too unstable
225 // http://www.cc.gatech.edu/grads/h/Hao-wei.Hsieh/Haowei.Hsieh/code1.html for psuedo code
226 // The line connecting (x0,y0)-(x1,y1) is clipped to rect and which
227 // points were clipped is returned.
228
229 enum ClipLine_Type
230 {
231 ClippedNeither = 0x0000,
232 ClippedFirstX = 0x0001,
233 ClippedFirstY = 0x0002,
234 ClippedFirst = ClippedFirstX | ClippedFirstY,
235 ClippedSecondX = 0x0010,
236 ClippedSecondY = 0x0020,
237 ClippedSecond = ClippedSecondX | ClippedSecondY,
238 ClippedBoth = ClippedFirst | ClippedSecond,
239 ClippedOut = 0x0100 // no intersection, so can't clip
240 };
241
242 // in plotdraw.cpp
243 extern int ClipLineToRect( double &x0, double &y0,
244 double &x1, double &y1,
245 const wxRect2DDouble &rect );
246
247 //***************************************************************************
248
249 #include "wx/plotctrl/plotdraw.h"
250
251 //----------------------------------------------------------------------------
252 // Event types
253 //----------------------------------------------------------------------------
254
255 // wxPlotEvent
256 DEFINE_EVENT_TYPE(wxEVT_PLOT_ADD_CURVE)
DEFINE_EVENT_TYPE(wxEVT_PLOT_DELETING_CURVE)257 DEFINE_EVENT_TYPE(wxEVT_PLOT_DELETING_CURVE)
258 DEFINE_EVENT_TYPE(wxEVT_PLOT_DELETED_CURVE)
259
260 DEFINE_EVENT_TYPE(wxEVT_PLOT_CURVE_SEL_CHANGING)
261 DEFINE_EVENT_TYPE(wxEVT_PLOT_CURVE_SEL_CHANGED)
262
263 DEFINE_EVENT_TYPE(wxEVT_PLOT_MOUSE_MOTION)
264 DEFINE_EVENT_TYPE(wxEVT_PLOT_CLICKED)
265 DEFINE_EVENT_TYPE(wxEVT_PLOT_DOUBLECLICKED)
266 DEFINE_EVENT_TYPE(wxEVT_PLOT_POINT_CLICKED)
267 DEFINE_EVENT_TYPE(wxEVT_PLOT_POINT_DOUBLECLICKED)
268
269 DEFINE_EVENT_TYPE(wxEVT_PLOT_AREA_SEL_CREATING)
270 DEFINE_EVENT_TYPE(wxEVT_PLOT_AREA_SEL_CHANGING)
271 DEFINE_EVENT_TYPE(wxEVT_PLOT_AREA_SEL_CREATED)
272
273 DEFINE_EVENT_TYPE(wxEVT_PLOT_VIEW_CHANGING)
274 DEFINE_EVENT_TYPE(wxEVT_PLOT_VIEW_CHANGED)
275
276 DEFINE_EVENT_TYPE(wxEVT_PLOT_CURSOR_CHANGING)
277 DEFINE_EVENT_TYPE(wxEVT_PLOT_CURSOR_CHANGED)
278
279 DEFINE_EVENT_TYPE(wxEVT_PLOT_ERROR)
280
281 DEFINE_EVENT_TYPE(wxEVT_PLOT_BEGIN_TITLE_EDIT)
282 DEFINE_EVENT_TYPE(wxEVT_PLOT_END_TITLE_EDIT)
283 DEFINE_EVENT_TYPE(wxEVT_PLOT_BEGIN_X_LABEL_EDIT)
284 DEFINE_EVENT_TYPE(wxEVT_PLOT_END_X_LABEL_EDIT)
285 DEFINE_EVENT_TYPE(wxEVT_PLOT_BEGIN_Y_LABEL_EDIT)
286 DEFINE_EVENT_TYPE(wxEVT_PLOT_END_Y_LABEL_EDIT)
287
288 DEFINE_EVENT_TYPE(wxEVT_PLOT_MOUSE_FUNC_CHANGING)
289 DEFINE_EVENT_TYPE(wxEVT_PLOT_MOUSE_FUNC_CHANGED)
290
291 // wxPlotSelectionEvent
292 //DEFINE_EVENT_TYPE(wxEVT_PLOT_RANGE_SEL_CREATING)
293 //DEFINE_EVENT_TYPE(wxEVT_PLOT_RANGE_SEL_CREATED)
294 //DEFINE_EVENT_TYPE(wxEVT_PLOT_RANGE_SEL_CHANGING)
295 DEFINE_EVENT_TYPE(wxEVT_PLOT_RANGE_SEL_CHANGED)
296
297 /*
298 DEFINE_EVENT_TYPE(wxEVT_PLOT_VALUE_SEL_CREATING)
299 DEFINE_EVENT_TYPE(wxEVT_PLOT_VALUE_SEL_CREATED)
300 DEFINE_EVENT_TYPE(wxEVT_PLOT_VALUE_SEL_CHANGING)
301 DEFINE_EVENT_TYPE(wxEVT_PLOT_VALUE_SEL_CHANGED)
302 DEFINE_EVENT_TYPE(wxEVT_PLOT_AREA_SEL_CHANGED)
303
304 DEFINE_EVENT_TYPE(wxEVT_PLOT_AREA_CREATE)
305 */
306
307 // The code below translates the event.GetEventType to a string name for debugging
308 #define aDEFINE_LOCAL_EVENT_TYPE(t) if (event.GetEventType() == t) return wxT(#t);
309
310 wxString GetEventName(const wxPlotEvent& event)
311 {
312 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_ADD_CURVE)
313 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_DELETING_CURVE)
314 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_DELETED_CURVE)
315
316 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_CURVE_SEL_CHANGING)
317 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_CURVE_SEL_CHANGED)
318
319 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_MOUSE_MOTION)
320 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_CLICKED)
321 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_DOUBLECLICKED)
322 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_POINT_CLICKED)
323 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_POINT_DOUBLECLICKED)
324
325 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_AREA_SEL_CREATING)
326 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_AREA_SEL_CHANGING)
327 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_AREA_SEL_CREATED)
328
329 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_VIEW_CHANGING)
330 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_VIEW_CHANGED)
331
332 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_CURSOR_CHANGING)
333 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_CURSOR_CHANGED)
334
335 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_ERROR)
336
337 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_BEGIN_TITLE_EDIT)
338 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_END_TITLE_EDIT)
339 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_BEGIN_X_LABEL_EDIT)
340 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_END_X_LABEL_EDIT)
341 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_BEGIN_Y_LABEL_EDIT)
342 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_END_Y_LABEL_EDIT)
343
344 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_MOUSE_FUNC_CHANGING)
345 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_MOUSE_FUNC_CHANGED)
346
347 // wxPlotSelectionEvent
348 //DEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_RANGE_SEL_CREATING)
349 //DEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_RANGE_SEL_CREATED)
350 //DEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_RANGE_SEL_CHANGING)
351 aDEFINE_LOCAL_EVENT_TYPE(wxEVT_PLOT_RANGE_SEL_CHANGED)
352
353 return wxT("Unknown Event Type");
354 }
355
356 //-----------------------------------------------------------------------------
357 // wxPlotEvent
358 //-----------------------------------------------------------------------------
359
IMPLEMENT_ABSTRACT_CLASS(wxPlotEvent,wxNotifyEvent)360 IMPLEMENT_ABSTRACT_CLASS(wxPlotEvent, wxNotifyEvent)
361
362 wxPlotEvent::wxPlotEvent(wxEventType commandType, int id, wxPlotCtrl *window)
363 :wxNotifyEvent(commandType, id), m_curve(NULL), m_curveIndex(-1),
364 m_curveData_index(-1), m_x(0), m_y(0)
365 {
366 SetEventObject( (wxObject*)window );
367 }
368
IMPLEMENT_ABSTRACT_CLASS(wxPlotSelectionEvent,wxPlotEvent)369 IMPLEMENT_ABSTRACT_CLASS(wxPlotSelectionEvent, wxPlotEvent)
370
371 wxPlotSelectionEvent::wxPlotSelectionEvent(wxEventType commandType, int id,
372 wxPlotCtrl *window)
373 :wxPlotEvent(commandType, id, window), m_selecting(false)
374 {
375 }
376
377 //-----------------------------------------------------------------------------
378 // wxPlotArea
379 //-----------------------------------------------------------------------------
IMPLEMENT_CLASS(wxPlotArea,wxWindow)380 IMPLEMENT_CLASS(wxPlotArea, wxWindow)
381
382 BEGIN_EVENT_TABLE(wxPlotArea, wxWindow)
383 EVT_ERASE_BACKGROUND( wxPlotArea::OnEraseBackground )
384 EVT_PAINT ( wxPlotArea::OnPaint )
385 EVT_MOUSE_EVENTS ( wxPlotArea::OnMouse )
386 EVT_CHAR ( wxPlotArea::OnChar )
387 EVT_KEY_DOWN ( wxPlotArea::OnKeyDown )
388 EVT_KEY_UP ( wxPlotArea::OnKeyUp )
389 END_EVENT_TABLE()
390
391 bool wxPlotArea::Create( wxWindow *parent, wxWindowID win_id )
392 {
393 if (!wxWindow::Create(parent, win_id, wxDefaultPosition, wxSize(100,100),
394 wxNO_BORDER|wxWANTS_CHARS|wxCLIP_CHILDREN, wxT("wxPlotArea")))
395 return false;
396
397 m_owner = wxDynamicCast(parent, wxPlotCtrl);
398 return true;
399 }
400
Init()401 void wxPlotArea::Init()
402 {
403 m_owner = NULL;
404 }
405
OnChar(wxKeyEvent & event)406 void wxPlotArea::OnChar( wxKeyEvent &event )
407 {
408 if (m_owner) m_owner->ProcessAreaEVT_CHAR(event);
409 }
OnKeyDown(wxKeyEvent & event)410 void wxPlotArea::OnKeyDown( wxKeyEvent &event )
411 {
412 if (m_owner) m_owner->ProcessAreaEVT_KEY_DOWN(event);
413 }
OnKeyUp(wxKeyEvent & event)414 void wxPlotArea::OnKeyUp( wxKeyEvent &event )
415 {
416 if (m_owner) m_owner->ProcessAreaEVT_KEY_UP(event);
417 }
OnMouse(wxMouseEvent & event)418 void wxPlotArea::OnMouse( wxMouseEvent &event )
419 {
420 if (m_owner) m_owner->ProcessAreaEVT_MOUSE_EVENTS(event);
421 }
422
OnPaint(wxPaintEvent & WXUNUSED (event))423 void wxPlotArea::OnPaint( wxPaintEvent &WXUNUSED(event) )
424 {
425 wxPaintDC dc( this );
426
427 if (!m_owner || (m_owner->GetRedrawType() & wxPLOT_REDRAW_BLOCKER))
428 return;
429
430 int redraw_type = m_owner->GetRedrawType();
431
432 /*
433 wxRegionIterator upd( GetUpdateRegion() );
434 while (upd)
435 {
436 //printf("Region %d %d %d %d \n", upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() ); fflush(stdout);
437 Paint( &dc, upd.GetRect() );
438 upd++;
439 }
440 */
441
442 if (redraw_type & wxPLOT_REDRAW_PLOT)
443 {
444 CreateBitmap( m_owner->GetPlotAreaRect() );
445 m_owner->SetRedrawType(redraw_type & ~wxPLOT_REDRAW_PLOT);
446 }
447
448 if (m_bitmap.Ok())
449 dc.DrawBitmap(m_bitmap, 0, 0, false);
450
451 #if wxCHECK_VERSION(2,7,0)
452 if (m_owner->GetCrossHairCursor() && m_owner->GetPlotAreaRect().Contains(m_mousePt))
453 #else
454 if (m_owner->GetCrossHairCursor() && m_owner->GetPlotAreaRect().Inside(m_mousePt))
455 #endif
456 m_owner->DrawCrosshairCursor( &dc, m_mousePt );
457
458
459 m_owner->DrawMouseMarker(&dc, m_owner->GetAreaMouseMarker(), m_mouseRect);
460 }
461
CreateBitmap(const wxRect & rect)462 void wxPlotArea::CreateBitmap( const wxRect &rect )
463 {
464 if (!m_owner)
465 return;
466
467 wxRect refreshRect(rect);
468 wxRect clientRect(m_owner->GetPlotAreaRect());
469 refreshRect.Intersect(clientRect);
470
471 if ((refreshRect.width == 0) || (refreshRect.height == 0)) return;
472
473 // if the bitmap need to be recreated then refresh everything
474 if (!m_bitmap.Ok() || (clientRect.width != m_bitmap.GetWidth()) ||
475 (clientRect.height != m_bitmap.GetHeight()) )
476 {
477 m_bitmap.Create(clientRect.width, clientRect.height);
478 refreshRect = clientRect;
479 }
480
481 wxMemoryDC mdc;
482 mdc.SelectObject( m_bitmap );
483 m_owner->DrawAreaWindow( &mdc, refreshRect );
484 mdc.SelectObject( wxNullBitmap );
485 }
486
487 //-----------------------------------------------------------------------------
488 // wxPlotAxis
489 //-----------------------------------------------------------------------------
IMPLEMENT_CLASS(wxPlotAxis,wxWindow)490 IMPLEMENT_CLASS(wxPlotAxis, wxWindow)
491
492 BEGIN_EVENT_TABLE(wxPlotAxis, wxWindow)
493 EVT_ERASE_BACKGROUND( wxPlotAxis::OnEraseBackground )
494 EVT_PAINT ( wxPlotAxis::OnPaint )
495 EVT_MOUSE_EVENTS ( wxPlotAxis::OnMouse )
496 EVT_CHAR ( wxPlotAxis::OnChar )
497 END_EVENT_TABLE()
498
499 bool wxPlotAxis::Create( wxWindow *parent, wxWindowID win_id, wxPlotAxis_Type style )
500 {
501 if (!wxWindow::Create(parent, win_id, wxDefaultPosition, wxDefaultSize,
502 wxNO_BORDER|wxWANTS_CHARS|wxCLIP_CHILDREN, wxT("wxPlotAxis")))
503 return false;
504
505 m_style = style;
506 m_owner = wxDynamicCast(parent, wxPlotCtrl);
507
508 if (style == wxPLOT_Y_AXIS)
509 SetCursor( wxCursor(wxCURSOR_SIZENS) );
510 else
511 SetCursor( wxCursor(wxCURSOR_SIZEWE) );
512
513 return true;
514 }
515
Init()516 void wxPlotAxis::Init()
517 {
518 m_style = wxPLOT_X_AXIS;
519 m_owner = NULL;
520 }
521
OnChar(wxKeyEvent & event)522 void wxPlotAxis::OnChar( wxKeyEvent &event )
523 {
524 if (m_owner) m_owner->ProcessAxisEVT_CHAR(event);
525 }
OnMouse(wxMouseEvent & event)526 void wxPlotAxis::OnMouse( wxMouseEvent &event )
527 {
528 if (m_owner) m_owner->ProcessAxisEVT_MOUSE_EVENTS(event);
529 }
530
OnPaint(wxPaintEvent & WXUNUSED (event))531 void wxPlotAxis::OnPaint( wxPaintEvent &WXUNUSED(event) )
532 {
533 wxPaintDC dc( this );
534 int redraw_type = m_owner->GetRedrawType();
535 if (!m_owner || (redraw_type & wxPLOT_REDRAW_BLOCKER))
536 return;
537
538 if (IsXAxis() && ((redraw_type & wxPLOT_REDRAW_XAXIS) != 0))
539 {
540 m_owner->SetRedrawType(redraw_type & ~wxPLOT_REDRAW_XAXIS);
541 CreateBitmap();
542 }
543 else if (!IsXAxis() && ((redraw_type & wxPLOT_REDRAW_YAXIS) != 0))
544 {
545 m_owner->SetRedrawType(redraw_type & ~wxPLOT_REDRAW_YAXIS);
546 CreateBitmap();
547 }
548
549 if (m_bitmap.Ok())
550 dc.DrawBitmap(m_bitmap, 0, 0, false);
551 }
552
CreateBitmap()553 void wxPlotAxis::CreateBitmap()
554 {
555 if (!m_owner)
556 return;
557
558 m_owner->UpdateWindowSize();
559 wxSize clientSize = GetClientSize();
560 if ((clientSize.x < 2) || (clientSize.y < 2)) return;
561
562 if (!m_bitmap.Ok() || (clientSize.x != m_bitmap.GetWidth()) ||
563 (clientSize.y != m_bitmap.GetHeight()) )
564 {
565 m_bitmap.Create(clientSize.x, clientSize.y);
566 }
567
568 wxMemoryDC mdc;
569 mdc.SelectObject( m_bitmap );
570 if (IsXAxis())
571 m_owner->DrawXAxis( &mdc, true );
572 else
573 m_owner->DrawYAxis( &mdc, true );
574
575 mdc.SelectObject( wxNullBitmap );
576 }
577
578 //-----------------------------------------------------------------------------
579 // wxPlotCtrl
580 //-----------------------------------------------------------------------------
IMPLEMENT_ABSTRACT_CLASS(wxPlotCtrl,wxWindow)581 IMPLEMENT_ABSTRACT_CLASS(wxPlotCtrl, wxWindow )
582
583 BEGIN_EVENT_TABLE(wxPlotCtrl, wxWindow )
584 //EVT_ERASE_BACKGROUND ( wxPlotCtrl::OnEraseBackground ) // clear for MSW
585 EVT_SIZE ( wxPlotCtrl::OnSize )
586 EVT_PAINT ( wxPlotCtrl::OnPaint )
587 EVT_CHAR ( wxPlotCtrl::OnChar )
588 EVT_SCROLL ( wxPlotCtrl::OnScroll )
589 EVT_IDLE ( wxPlotCtrl::OnIdle )
590 EVT_MOUSE_EVENTS ( wxPlotCtrl::OnMouse )
591 EVT_TIMER ( wxID_ANY, wxPlotCtrl::OnTimer )
592
593 EVT_TEXT_ENTER ( wxID_ANY, wxPlotCtrl::OnTextEnter)
594 END_EVENT_TABLE()
595
596 void wxPlotCtrl::Init()
597 {
598 m_activeCurve = NULL;
599 m_active_index = -1;
600
601 m_cursorMarker.CreateEllipseMarker(wxPoint2DDouble(0,0),
602 wxSize(2, 2),
603 wxGenericPen(wxGenericColour(0, 255, 0)));
604 m_cursor_curve = -1;
605 m_cursor_index = -1;
606
607 m_selection_type = wxPLOT_SELECT_MULTIPLE;
608
609 m_show_key = true;
610
611 m_show_title = m_show_xlabel = m_show_ylabel = false;
612 m_title = wxT("Title");
613 m_xLabel = wxT("X-Axis");
614 m_yLabel = wxT("Y-Axis");
615
616 m_titleFont = *wxSWISS_FONT;
617 m_titleColour = *wxBLACK;
618 m_borderColour = *wxBLACK;
619
620 m_scroll_on_thumb_release = false;
621 m_crosshair_cursor = false;
622 m_draw_symbols = true;
623 m_draw_lines = true;
624 m_draw_spline = false;
625 m_draw_grid = true;
626 m_fit_on_new_curve = true;
627 m_show_xAxis = true;
628 m_show_yAxis = true;
629
630 m_zoom = wxPoint2DDouble( 1.0, 1.0 );
631 m_history_views_index = -1;
632
633 m_fix_aspectratio = false;
634 m_aspectratio = 1.0;
635
636 m_viewRect = m_defaultPlotRect;
637 m_curveBoundingRect = m_defaultPlotRect;
638 m_defaultPlotRect = wxRect2DDouble( -10.0, -10.0, 20.0, 20.0 );
639 m_areaClientRect = wxRect(0, 0, 10, 10);
640
641 m_xAxisTickFormat = m_yAxisTickFormat = wxT("%lf");
642 m_xAxisTick_step = m_yAxisTick_step = 1.0;
643 m_xAxisTick_count = m_yAxisTick_count = 4;
644 m_correct_ticks = true;
645
646 m_areaDrawer = NULL;
647 m_xAxisDrawer = NULL;
648 m_yAxisDrawer = NULL;
649 m_keyDrawer = NULL;
650 m_curveDrawer = NULL;
651 m_dataCurveDrawer = NULL;
652 m_markerDrawer = NULL;
653
654 m_xAxis = NULL;
655 m_yAxis = NULL;
656 m_area = NULL;
657 m_xAxisScrollbar = NULL;
658 m_yAxisScrollbar = NULL;
659 m_textCtrl = NULL;
660
661 m_activeBitmap = new wxBitmap(ledgreen_xpm);
662 m_inactiveBitmap = new wxBitmap(ledgrey_xpm);
663 m_focused = false;
664 m_greedy_focus = false;
665
666 m_redraw_type = wxPLOT_REDRAW_BLOCKER;
667 m_batch_count = 0;
668
669 m_axisFontSize.x = 6;
670 m_axisFontSize.y = 12;
671 m_y_axis_text_width = 60;
672 m_area_border_width = 1;
673 m_border = 4;
674 m_min_exponential = 1000;
675 m_pen_print_width = 0.4;
676
677 m_timer = NULL;
678 m_winCapture = NULL;
679
680 m_area_mouse_marker = wxPLOT_MARKER_RECT;
681 m_area_mouse_func = wxPLOT_MOUSE_ZOOM;
682 m_area_mouse_cursorid = wxCURSOR_CROSS;
683
684 m_mouse_cursorid = wxCURSOR_ARROW;
685 }
686
Create(wxWindow * parent,wxWindowID win_id,const wxPoint & pos,const wxSize & size,wxPlotAxis_Type WXUNUSED (flag),const wxString & name)687 bool wxPlotCtrl::Create( wxWindow *parent, wxWindowID win_id,
688 const wxPoint &pos, const wxSize &size,
689 wxPlotAxis_Type WXUNUSED(flag),
690 const wxString &name )
691 {
692 m_redraw_type = wxPLOT_REDRAW_BLOCKER; // no paints until finished
693
694 if (!wxWindow::Create(parent, win_id, pos, size,
695 wxWANTS_CHARS|wxCLIP_CHILDREN, name))
696 return false;
697
698 //SetSizeHints( 100, 100 ); // Don't allow window to get smaller than this!
699
700 if (!s_handCursor.Ok())
701 {
702 wxImage image(wxBitmap(hand_xpm).ConvertToImage());
703 image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, image.GetWidth()/2);
704 image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, image.GetHeight()/2);
705 s_handCursor = wxCursor(image);
706 }
707 if (!s_grabCursor.Ok())
708 {
709 wxImage image(wxBitmap(grab_xpm).ConvertToImage());
710 image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, image.GetWidth()/2);
711 image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, image.GetHeight()/2);
712 s_grabCursor = wxCursor(image);
713 }
714
715 m_areaDrawer = new wxPlotDrawerArea(this);
716 m_xAxisDrawer = new wxPlotDrawerXAxis(this);
717 m_yAxisDrawer = new wxPlotDrawerYAxis(this);
718 m_keyDrawer = new wxPlotDrawerKey(this);
719 m_curveDrawer = new wxPlotDrawerCurve(this);
720 m_dataCurveDrawer = new wxPlotDrawerDataCurve(this);
721 m_markerDrawer = new wxPlotDrawerMarker(this);
722
723 m_xAxis = new wxPlotAxis( this, ID_PLOTCTRL_X_AXIS, wxPLOT_X_AXIS );
724 m_yAxis = new wxPlotAxis( this, ID_PLOTCTRL_Y_AXIS, wxPLOT_Y_AXIS );
725 m_area = new wxPlotArea( this, ID_PLOTCTRL_AREA );
726 m_xAxisScrollbar = new wxScrollBar(this, ID_PLOTCTRL_X_SCROLLBAR,
727 wxDefaultPosition, wxDefaultSize, wxSB_HORIZONTAL);
728 m_yAxisScrollbar = new wxScrollBar(this, ID_PLOTCTRL_Y_SCROLLBAR,
729 wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
730
731 wxFont axisFont(m_xAxis->GetFont());
732 GetTextExtent(wxT("5"), &m_axisFontSize.x, &m_axisFontSize.y, NULL, NULL, &axisFont);
733 if ((m_axisFontSize.x < 2) || (m_axisFontSize.y < 2)) // don't want to divide by 0
734 {
735 m_axisFontSize.x = 6;
736 m_axisFontSize.y = 12;
737 wxFAIL_MSG(wxT("Can't determine the font size for the axis! I'll guess.\n"
738 "Thef display might be corrupted, however you may continue."));
739 }
740
741 m_xAxisDrawer->SetTickFont(axisFont);
742 m_yAxisDrawer->SetTickFont(axisFont);
743 // m_xAxisDrawer->SetLabelFont(*wxSWISS_FONT); // needs to be rotated
744 // m_yAxisDrawer->SetLabelFont(*wxSWISS_FONT); // swiss works
745
746 m_area->SetCursor( wxCURSOR_CROSS );
747 m_area->SetBackgroundColour(*wxWHITE);
748 m_xAxis->SetBackgroundColour(*wxWHITE);
749 m_yAxis->SetBackgroundColour(*wxWHITE);
750 wxWindow::SetBackgroundColour(*wxWHITE);
751
752 m_area->SetForegroundColour(*wxLIGHT_GREY);
753
754 m_redraw_type = 0; // redraw when all done
755 Redraw(wxPLOT_REDRAW_WHOLEPLOT);
756
757 return true;
758 }
759
~wxPlotCtrl()760 wxPlotCtrl::~wxPlotCtrl()
761 {
762 delete m_activeBitmap;
763 delete m_inactiveBitmap;
764
765 delete m_areaDrawer;
766 delete m_xAxisDrawer;
767 delete m_yAxisDrawer;
768 delete m_keyDrawer;
769 delete m_curveDrawer;
770 delete m_dataCurveDrawer;
771 delete m_markerDrawer;
772 }
773
OnPaint(wxPaintEvent & WXUNUSED (event))774 void wxPlotCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
775 {
776 wxPaintDC dc(this);
777
778 DrawActiveBitmap(&dc);
779 DrawPlotCtrl(&dc);
780 }
781
DrawActiveBitmap(wxDC * dc)782 void wxPlotCtrl::DrawActiveBitmap( wxDC* dc )
783 {
784 if (m_xAxisScrollbar && m_yAxisScrollbar)
785 {
786 wxSize size = GetClientSize();
787 int left = m_xAxisScrollbar->GetRect().GetRight();
788 int top = m_yAxisScrollbar->GetRect().GetBottom();
789 wxRect rect(left, top, size.x - left, size.y - top);
790 // clear background
791 dc->SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
792 dc->SetPen(*wxTRANSPARENT_PEN);
793 dc->DrawRectangle(rect);
794 // center the bitmap
795 wxPoint pt(rect.x + (rect.width - 15)/2, rect.y + (rect.width - 15)/2);
796 dc->DrawBitmap(m_focused ? *m_activeBitmap : *m_inactiveBitmap,
797 pt.x, pt.y, true);
798 }
799 }
DrawPlotCtrl(wxDC * dc)800 void wxPlotCtrl::DrawPlotCtrl( wxDC *dc )
801 {
802 wxCHECK_RET(dc, wxT("invalid window"));
803
804 if (m_show_title && !m_title.IsEmpty())
805 {
806 dc->SetFont(GetPlotTitleFont());
807 dc->SetTextForeground(GetPlotTitleColour());
808 dc->DrawText(m_title, m_titleRect.x, m_titleRect.y);
809 }
810
811 bool draw_xlabel = (m_show_xlabel && !m_xLabel.IsEmpty());
812 bool draw_ylabel = (m_show_ylabel && !m_yLabel.IsEmpty());
813
814 if (draw_xlabel || draw_ylabel)
815 {
816 dc->SetFont(GetAxisLabelFont());
817 dc->SetTextForeground(GetAxisLabelColour());
818
819 if (draw_xlabel)
820 dc->DrawText(m_xLabel, m_xLabelRect.x, m_xLabelRect.y);
821 if (draw_ylabel)
822 dc->DrawRotatedText(m_yLabel, m_yLabelRect.x, m_yLabelRect.y + m_yLabelRect.height, 90);
823 }
824
825 #ifdef DRAW_BORDERS
826 // Test code for sizing to show the extent of the axes
827 dc->SetBrush( *wxTRANSPARENT_BRUSH );
828 dc->SetPen( wxPen(GetBorderColour(), 1, wxSOLID) );
829 dc->DrawRectangle(m_titleRect);
830 dc->DrawRectangle(m_xLabelRect);
831 dc->DrawRectangle(m_yLabelRect);
832 #endif // DRAW_BORDERS
833 }
834
SetPlotWinMouseCursor(int cursorid)835 void wxPlotCtrl::SetPlotWinMouseCursor(int cursorid)
836 {
837 if (cursorid == m_mouse_cursorid) return;
838 m_mouse_cursorid = cursorid;
839 SetCursor(wxCursor(cursorid));
840 }
841
OnMouse(wxMouseEvent & event)842 void wxPlotCtrl::OnMouse( wxMouseEvent &event )
843 {
844 if (event.ButtonDown() && IsTextCtrlShown())
845 {
846 HideTextCtrl(true, true);
847 return;
848 }
849
850 /*wxSize size(*/GetClientSize();/*);*/
851 wxPoint mousePt(event.GetPosition());
852
853 #if wxCHECK_VERSION(2,7,0)
854 if ((m_show_title && m_titleRect.Contains(mousePt)) ||
855 (m_show_xlabel && m_xLabelRect.Contains(mousePt)) ||
856 (m_show_ylabel && m_yLabelRect.Contains(mousePt)))
857 #else
858 if ((m_show_title && m_titleRect.Inside(mousePt)) ||
859 (m_show_xlabel && m_xLabelRect.Inside(mousePt)) ||
860 (m_show_ylabel && m_yLabelRect.Inside(mousePt)))
861 #endif
862 {
863 SetPlotWinMouseCursor(wxCURSOR_IBEAM);
864 }
865 else
866 SetPlotWinMouseCursor(wxCURSOR_ARROW);
867
868 if (event.ButtonDClick(1) && !IsTextCtrlShown())
869 {
870 #if wxCHECK_VERSION(2,7,0)
871 if (m_show_title && m_titleRect.Contains(mousePt))
872 ShowTextCtrl(wxPLOT_EDIT_TITLE, true);
873 else if (m_show_xlabel && m_xLabelRect.Contains(mousePt))
874 ShowTextCtrl(wxPLOT_EDIT_XAXIS, true);
875 else if (m_show_ylabel && m_yLabelRect.Contains(mousePt))
876 ShowTextCtrl(wxPLOT_EDIT_YAXIS, true);
877 #else
878 if (m_show_title && m_titleRect.Inside(mousePt))
879 ShowTextCtrl(wxPLOT_EDIT_TITLE, true);
880 else if (m_show_xlabel && m_xLabelRect.Inside(mousePt))
881 ShowTextCtrl(wxPLOT_EDIT_XAXIS, true);
882 else if (m_show_ylabel && m_yLabelRect.Inside(mousePt))
883 ShowTextCtrl(wxPLOT_EDIT_YAXIS, true);
884 #endif
885 }
886 }
887
ShowTextCtrl(wxPlotCtrlTextCtrl_Type type,bool send_event)888 void wxPlotCtrl::ShowTextCtrl(wxPlotCtrlTextCtrl_Type type, bool send_event)
889 {
890 switch (type)
891 {
892 case wxPLOT_EDIT_TITLE :
893 {
894 if (m_textCtrl)
895 {
896 if (m_textCtrl->GetId() != wxEVT_PLOT_END_TITLE_EDIT)
897 HideTextCtrl(true, true);
898 else
899 return; // already shown
900 }
901
902 if (send_event)
903 {
904 wxPlotEvent pevent(wxEVT_PLOT_BEGIN_TITLE_EDIT, GetId(), this);
905 pevent.SetString(m_title);
906 if (!DoSendEvent(pevent)) return;
907 }
908
909 m_textCtrl = new wxTextCtrl(this, wxEVT_PLOT_END_TITLE_EDIT, GetPlotTitle(),
910 wxPoint(m_areaRect.x, 0),
911 wxSize(m_areaRect.width, m_titleRect.height+2*m_border),
912 wxTE_PROCESS_ENTER);
913
914 m_textCtrl->SetFont(GetPlotTitleFont());
915 m_textCtrl->SetForegroundColour(GetPlotTitleColour());
916 m_textCtrl->SetBackgroundColour(GetBackgroundColour());
917 break;
918 }
919 case wxPLOT_EDIT_XAXIS :
920 {
921 if (m_textCtrl)
922 {
923 if (m_textCtrl->GetId() != wxEVT_PLOT_END_X_LABEL_EDIT)
924 HideTextCtrl(true, true);
925 else
926 return; // already shown
927 }
928
929 if (send_event)
930 {
931 wxPlotEvent pevent(wxEVT_PLOT_BEGIN_X_LABEL_EDIT, GetId(), this);
932 pevent.SetString(m_xLabel);
933 if (!DoSendEvent(pevent)) return;
934 }
935
936 m_textCtrl = new wxTextCtrl(this, wxEVT_PLOT_END_X_LABEL_EDIT, GetXAxisLabel(),
937 wxPoint(m_areaRect.x, m_xAxisRect.GetBottom()),
938 wxSize(m_areaRect.width, m_xLabelRect.height+2*m_border),
939 wxTE_PROCESS_ENTER);
940
941 m_textCtrl->SetFont(GetAxisLabelFont());
942 m_textCtrl->SetForegroundColour(GetAxisLabelColour());
943 m_textCtrl->SetBackgroundColour(GetBackgroundColour());
944 break;
945 }
946 case wxPLOT_EDIT_YAXIS :
947 {
948 if (m_textCtrl)
949 {
950 if (m_textCtrl->GetId() != wxEVT_PLOT_END_Y_LABEL_EDIT)
951 HideTextCtrl(true, true);
952 else
953 return; // already shown
954 }
955
956 if (send_event)
957 {
958 wxPlotEvent pevent(wxEVT_PLOT_BEGIN_Y_LABEL_EDIT, GetId(), this);
959 pevent.SetString(m_yLabel);
960 if (!DoSendEvent(pevent)) return;
961 }
962
963 m_textCtrl = new wxTextCtrl(this, wxEVT_PLOT_END_Y_LABEL_EDIT, GetYAxisLabel(),
964 wxPoint(0, m_areaRect.y+m_areaRect.height/2),
965 wxSize(m_clientRect.width - m_axisFontSize.y/2, m_yLabelRect.width+2*m_border),
966 wxTE_PROCESS_ENTER);
967
968 m_textCtrl->SetFont(GetAxisLabelFont());
969 m_textCtrl->SetForegroundColour(GetAxisLabelColour());
970 m_textCtrl->SetBackgroundColour(GetBackgroundColour());
971 break;
972 }
973 }
974 }
975
HideTextCtrl(bool save_value,bool send_event)976 void wxPlotCtrl::HideTextCtrl(bool save_value, bool send_event)
977 {
978 wxCHECK_RET(m_textCtrl, wxT("HideTextCtrl, but textctrl is not shown"));
979
980 long event_type = m_textCtrl->GetId();
981 wxString value = m_textCtrl->GetValue();
982
983 m_textCtrl->Destroy();
984 m_textCtrl = NULL;
985
986 if (!save_value)
987 return;
988
989 bool changed = false;
990
991 if (event_type == wxEVT_PLOT_END_TITLE_EDIT)
992 changed = (value != GetPlotTitle());
993 else if (event_type == wxEVT_PLOT_END_X_LABEL_EDIT)
994 changed = (value != GetXAxisLabel());
995 else if (event_type == wxEVT_PLOT_END_Y_LABEL_EDIT)
996 changed = (value != GetYAxisLabel());
997
998 if (!changed)
999 return;
1000
1001 if (send_event)
1002 {
1003 wxPlotEvent event(event_type, GetId(), this);
1004 event.SetString(value);
1005 if (!DoSendEvent(event)) return;
1006 }
1007
1008 if (event_type == wxEVT_PLOT_END_TITLE_EDIT)
1009 SetPlotTitle(value);
1010 else if (event_type == wxEVT_PLOT_END_X_LABEL_EDIT)
1011 SetXAxisLabel(value);
1012 else if (event_type == wxEVT_PLOT_END_Y_LABEL_EDIT)
1013 SetYAxisLabel(value);
1014 }
1015
IsTextCtrlShown() const1016 bool wxPlotCtrl::IsTextCtrlShown() const
1017 {
1018 return m_textCtrl && m_textCtrl->IsShown();
1019 }
1020
OnTextEnter(wxCommandEvent & event)1021 void wxPlotCtrl::OnTextEnter( wxCommandEvent &event )
1022 {
1023 if (event.GetId() == 1)
1024 HideTextCtrl(true, true);
1025 else
1026 {
1027 wxCommandEvent newevt(wxEVT_COMMAND_TEXT_ENTER, 1);
1028 GetEventHandler()->AddPendingEvent( newevt );
1029 }
1030 }
1031
OnIdle(wxIdleEvent & event)1032 void wxPlotCtrl::OnIdle( wxIdleEvent &event )
1033 {
1034 CheckFocus();
1035 event.Skip();
1036 }
1037
CheckFocus()1038 bool wxPlotCtrl::CheckFocus()
1039 {
1040 wxWindow *win = FindFocus();
1041 if ((win==m_area)||(win==m_xAxis)||(win==m_yAxis)||(win==this))
1042 {
1043 if (!m_focused)
1044 {
1045 m_focused = true;
1046 wxClientDC dc(this);
1047 wxSize size = GetClientSize();
1048 dc.DrawBitmap(*m_activeBitmap, size.GetWidth()-15, size.GetHeight()-15,true);
1049 }
1050 }
1051 else if (m_focused)
1052 {
1053 m_focused = false;
1054 wxClientDC dc(this);
1055 wxSize size = GetClientSize();
1056 dc.DrawBitmap(*m_inactiveBitmap, size.GetWidth()-15, size.GetHeight()-15,true);
1057 }
1058 return m_focused;
1059 }
1060
EndBatch(bool force_refresh)1061 void wxPlotCtrl::EndBatch(bool force_refresh)
1062 {
1063 if ( m_batch_count > 0 )
1064 {
1065 m_batch_count--;
1066 if ( (m_batch_count <= 0) && force_refresh )
1067 {
1068 Redraw(wxPLOT_REDRAW_WHOLEPLOT);
1069 AdjustScrollBars();
1070 }
1071 }
1072 }
1073
SetBackgroundColour(const wxColour & colour)1074 bool wxPlotCtrl::SetBackgroundColour( const wxColour &colour )
1075 {
1076 wxCHECK_MSG(colour.Ok(), false, wxT("invalid colour"));
1077 m_area->SetBackgroundColour(colour);
1078 m_xAxis->SetBackgroundColour(colour);
1079 m_yAxis->SetBackgroundColour(colour);
1080 wxWindow::SetBackgroundColour(colour);
1081
1082 Redraw(wxPLOT_REDRAW_EVERYTHING);
1083 return true;
1084 }
SetGridColour(const wxColour & colour)1085 void wxPlotCtrl::SetGridColour( const wxColour &colour )
1086 {
1087 wxCHECK_RET(colour.Ok(), wxT("invalid colour"));
1088 m_area->SetForegroundColour(colour);
1089 Redraw(wxPLOT_REDRAW_PLOT);
1090 }
SetBorderColour(const wxColour & colour)1091 void wxPlotCtrl::SetBorderColour( const wxColour &colour )
1092 {
1093 wxCHECK_RET(colour.Ok(), wxT("invalid colour"));
1094 m_borderColour = colour;
1095 Redraw(wxPLOT_REDRAW_PLOT);
1096 }
SetCursorColour(const wxColour & colour)1097 void wxPlotCtrl::SetCursorColour( const wxColour &colour )
1098 {
1099 wxCHECK_RET(colour.Ok(), wxT("invalid colour"));
1100 m_cursorMarker.GetPen().SetColour(colour);
1101 wxClientDC dc(m_area);
1102 DrawCurveCursor( &dc );
1103 }
GetCursorColour() const1104 wxColour wxPlotCtrl::GetCursorColour() const
1105 {
1106 return m_cursorMarker.GetPen().GetColour();
1107 }
GetCursorSize() const1108 int wxPlotCtrl::GetCursorSize() const
1109 {
1110 return m_cursorMarker.GetSize().x;
1111 }
SetCursorSize(int size)1112 void wxPlotCtrl::SetCursorSize(int size)
1113 {
1114 m_cursorMarker.SetSize(wxSize(size, size));
1115 }
1116
GetAxisFont() const1117 wxFont wxPlotCtrl::GetAxisFont() const
1118 {
1119 return m_xAxisDrawer->m_tickFont; // FIXME
1120 }
GetAxisColour() const1121 wxColour wxPlotCtrl::GetAxisColour() const
1122 {
1123 return m_xAxisDrawer->m_tickColour.GetColour(); // FIXME
1124 }
1125
SetAxisFont(const wxFont & font)1126 void wxPlotCtrl::SetAxisFont( const wxFont &font )
1127 {
1128 wxCHECK_RET(font.Ok(), wxT("invalid font"));
1129
1130 if (m_xAxisDrawer) m_xAxisDrawer->SetTickFont(font);
1131 if (m_yAxisDrawer) m_yAxisDrawer->SetTickFont(font);
1132
1133 int x=6, y=12, decent=0, leading=0;
1134
1135 GetTextExtent(wxT("5"), &x, &y, &decent, &leading, &font);
1136 m_axisFontSize.x = x+leading;
1137 m_axisFontSize.y = y+decent;
1138
1139 GetTextExtent(wxT("-5.5e+555"), &x, &y, &decent, &leading, &font);
1140 m_y_axis_text_width = x + leading;
1141
1142 //m_axisFontSize.x = m_xAxis->GetCharWidth();
1143 //m_axisFontSize.y = m_xAxis->GetCharHeight();
1144 if ((m_axisFontSize.x < 2) || (m_axisFontSize.y < 2)) // don't want to divide by 0
1145 {
1146 static bool first_try = false;
1147
1148 m_axisFontSize.x = 6;
1149 m_axisFontSize.y = 12;
1150 wxMessageBox(wxT("Can't determine the font size for the axis.\n")
1151 wxT("Reverting to a default font."),
1152 wxT("Font error"));
1153
1154 if (!first_try)
1155 {
1156 first_try = true;
1157 SetAxisFont(*wxNORMAL_FONT);
1158 }
1159 else
1160 first_try = false;
1161 }
1162
1163 DoSize();
1164 Redraw(wxPLOT_REDRAW_XAXIS|wxPLOT_REDRAW_YAXIS);
1165 }
SetAxisColour(const wxColour & colour)1166 void wxPlotCtrl::SetAxisColour( const wxColour &colour )
1167 {
1168 wxCHECK_RET(colour.Ok(), wxT("invalid colour"));
1169 if (m_xAxisDrawer) m_xAxisDrawer->SetTickColour(colour);
1170 if (m_yAxisDrawer) m_yAxisDrawer->SetTickColour(colour);
1171 Redraw(wxPLOT_REDRAW_XAXIS|wxPLOT_REDRAW_YAXIS);
1172 }
1173
GetAxisLabelFont() const1174 wxFont wxPlotCtrl::GetAxisLabelFont() const
1175 {
1176 return m_xAxisDrawer->m_labelFont; // FIXME
1177 }
GetAxisLabelColour() const1178 wxColour wxPlotCtrl::GetAxisLabelColour() const
1179 {
1180 return m_xAxisDrawer->m_labelColour.GetColour(); // FIXME
1181 }
SetAxisLabelFont(const wxFont & font)1182 void wxPlotCtrl::SetAxisLabelFont( const wxFont &font )
1183 {
1184 wxCHECK_RET(font.Ok(), wxT("invalid font"));
1185 if (m_xAxisDrawer) m_xAxisDrawer->SetLabelFont(font);
1186 if (m_yAxisDrawer) m_yAxisDrawer->SetLabelFont(font);
1187 SetXAxisLabel(GetXAxisLabel()); // FIXME - lazy hack
1188 SetYAxisLabel(GetYAxisLabel());
1189 }
SetAxisLabelColour(const wxColour & colour)1190 void wxPlotCtrl::SetAxisLabelColour( const wxColour &colour )
1191 {
1192 wxCHECK_RET(colour.Ok(), wxT("invalid colour"));
1193 if (m_xAxisDrawer) m_xAxisDrawer->SetLabelColour(colour);
1194 if (m_yAxisDrawer) m_yAxisDrawer->SetLabelColour(colour);
1195 SetXAxisLabel(GetXAxisLabel()); // FIXME - lazy hack
1196 SetYAxisLabel(GetYAxisLabel());
1197 }
SetPlotTitleFont(const wxFont & font)1198 void wxPlotCtrl::SetPlotTitleFont(const wxFont &font)
1199 {
1200 wxCHECK_RET(font.Ok(), wxT("invalid font"));
1201 m_titleFont = font;
1202 SetPlotTitle(GetPlotTitle());
1203 }
SetPlotTitleColour(const wxColour & colour)1204 void wxPlotCtrl::SetPlotTitleColour(const wxColour &colour)
1205 {
1206 wxCHECK_RET(colour.Ok(), wxT("invalid colour"));
1207 m_titleColour = colour;
1208 SetPlotTitle(GetPlotTitle());
1209 }
1210
GetKeyFont() const1211 wxFont wxPlotCtrl::GetKeyFont() const
1212 {
1213 return m_keyDrawer->m_font; // FIXME
1214 }
GetKeyColour() const1215 wxColour wxPlotCtrl::GetKeyColour() const
1216 {
1217 return m_keyDrawer->m_fontColour.GetColour(); // FIXME
1218 }
SetKeyFont(const wxFont & font)1219 void wxPlotCtrl::SetKeyFont( const wxFont &font )
1220 {
1221 wxCHECK_RET(font.Ok(), wxT("invalid font"));
1222 m_keyDrawer->SetFont(font);
1223 Redraw(wxPLOT_REDRAW_PLOT);
1224 }
SetKeyColour(const wxColour & colour)1225 void wxPlotCtrl::SetKeyColour( const wxColour & colour )
1226 {
1227 wxCHECK_RET(colour.Ok(), wxT("invalid colour"));
1228 m_keyDrawer->SetFontColour(colour);
1229 Redraw(wxPLOT_REDRAW_PLOT);
1230 }
1231
1232 // ------------------------------------------------------------------------
1233 // Title, axis labels, and key
1234 // ------------------------------------------------------------------------
1235
SetXAxisLabel(const wxString & label)1236 void wxPlotCtrl::SetXAxisLabel(const wxString &label)
1237 {
1238 if (label.IsEmpty())
1239 m_xLabel = wxT("X - Axis");
1240 else
1241 m_xLabel = label;
1242
1243 wxFont font = GetAxisLabelFont();
1244 GetTextExtent(m_xLabel, &m_xLabelRect.width, &m_xLabelRect.height, NULL, NULL, &font);
1245
1246 m_xLabel = label;
1247 Refresh();
1248 DoSize();
1249 }
1250
SetYAxisLabel(const wxString & label)1251 void wxPlotCtrl::SetYAxisLabel(const wxString &label)
1252 {
1253 if (label.IsEmpty())
1254 m_yLabel = wxT("Y - Axis");
1255 else
1256 m_yLabel = label;
1257
1258 wxFont font = GetAxisLabelFont();
1259 GetTextExtent(m_yLabel, &m_yLabelRect.height, &m_yLabelRect.width, NULL, NULL, &font);
1260
1261 m_yLabel = label;
1262
1263 Refresh();
1264 DoSize();
1265 }
1266
SetPlotTitle(const wxString & title)1267 void wxPlotCtrl::SetPlotTitle(const wxString &title)
1268 {
1269 if (title.IsEmpty())
1270 m_title = wxT("Title");
1271 else
1272 m_title = title;
1273
1274 wxFont font = GetPlotTitleFont();
1275 GetTextExtent(m_title, &m_titleRect.width, &m_titleRect.height, NULL, NULL, &font);
1276
1277 m_title = title;
1278
1279 Refresh();
1280 DoSize();
1281 }
1282
GetKeyPosition() const1283 wxPoint wxPlotCtrl::GetKeyPosition() const
1284 {
1285 return m_keyDrawer->m_keyPosition;
1286 }
GetKeyInside() const1287 bool wxPlotCtrl::GetKeyInside() const
1288 {
1289 return m_keyDrawer->m_key_inside;
1290 }
SetKeyPosition(const wxPoint & pos,bool stay_inside)1291 void wxPlotCtrl::SetKeyPosition(const wxPoint &pos, bool stay_inside)
1292 {
1293 m_keyDrawer->m_keyPosition = pos;
1294 m_keyDrawer->m_key_inside = stay_inside;
1295 Redraw(wxPLOT_REDRAW_PLOT);
1296 }
1297
CreateKeyString()1298 void wxPlotCtrl::CreateKeyString()
1299 {
1300 m_keyString.Clear();
1301 int n, count = m_curves.GetCount();
1302 for (n = 0; n < count; n++)
1303 {
1304 wxString key;
1305 if (GetDataCurve(n))
1306 key = GetDataCurve(n)->GetFilename();
1307 else if (GetFunctionCurve(n))
1308 key = GetFunctionCurve(n)->GetFunctionString();
1309 else
1310 key.Printf(wxT("Curve %d"), n);
1311
1312 m_keyString += (key + wxT("\n"));
1313 }
1314 }
1315
1316 // ------------------------------------------------------------------------
1317 // Curve Accessors
1318 // ------------------------------------------------------------------------
1319
AddCurve(wxPlotCurve * curve,bool select,bool send_event)1320 bool wxPlotCtrl::AddCurve( wxPlotCurve *curve, bool select, bool send_event )
1321 {
1322 if (!curve || !curve->Ok())
1323 {
1324 if (curve) delete curve;
1325 wxCHECK_MSG( false, false, wxT("Invalid curve") );
1326 }
1327
1328 m_curves.Add( curve );
1329 m_curveSelections.Add(new wxRangeDoubleSelection());
1330 m_dataSelections.Add(new wxRangeIntSelection());
1331
1332 CalcBoundingPlotRect();
1333 CreateKeyString();
1334
1335 if (send_event)
1336 {
1337 wxPlotEvent event(wxEVT_PLOT_ADD_CURVE, GetId(), this);
1338 event.SetCurve(curve, m_curves.GetCount()-1);
1339 (void)DoSendEvent( event );
1340 }
1341
1342 m_batch_count++;
1343 if (select) SetActiveCurve(curve, send_event);
1344 m_batch_count--;
1345
1346 if (m_fit_on_new_curve)
1347 SetZoom( -1, -1, 0, 0, true );
1348 else
1349 Redraw(wxPLOT_REDRAW_PLOT);
1350
1351 return true;
1352 }
1353
AddCurve(const wxPlotCurve & curve,bool select,bool send_event)1354 bool wxPlotCtrl::AddCurve( const wxPlotCurve &curve, bool select, bool send_event )
1355 {
1356 wxCHECK_MSG(curve.Ok(), false, wxT("invalid wxPlotCurve"));
1357
1358 if (wxDynamicCast(&curve, wxPlotData))
1359 return AddCurve(new wxPlotData(*wxDynamicCast(&curve, wxPlotData)), select, send_event);
1360 if (wxDynamicCast(&curve, wxPlotFunction))
1361 return AddCurve(new wxPlotFunction(*wxDynamicCast(&curve, wxPlotFunction)), select, send_event);
1362
1363 wxFAIL_MSG(wxT("Unable to ref curve type added to plot"));
1364 return false;
1365 }
1366
DeleteCurve(wxPlotCurve * curve,bool send_event)1367 bool wxPlotCtrl::DeleteCurve( wxPlotCurve* curve, bool send_event )
1368 {
1369 wxCHECK_MSG(curve, false, wxT("invalid plotcurve"));
1370
1371 int index = m_curves.Index( *curve );
1372 wxCHECK_MSG( index != wxNOT_FOUND, false, wxT("Unknown PlotCurve") );
1373
1374 return DeleteCurve(index, send_event);
1375 }
1376
DeleteCurve(int n,bool send_event)1377 bool wxPlotCtrl::DeleteCurve( int n, bool send_event )
1378 {
1379 wxCHECK_MSG((n>=-1)&&(n<int(m_curves.GetCount())), false, wxT("Invalid curve index"));
1380
1381 if (send_event)
1382 {
1383 wxPlotEvent event( wxEVT_PLOT_DELETING_CURVE, GetId(), this );
1384 event.SetCurveIndex(n);
1385 if (!DoSendEvent(event)) return false;
1386 }
1387
1388 BeginBatch(); // don't redraw yet
1389
1390 if (n < 0)
1391 {
1392 InvalidateCursor(send_event);
1393 ClearSelectedRanges(-1, send_event);
1394 m_dataSelections.Clear();
1395 m_curveSelections.Clear();
1396 m_curves.Clear();
1397 }
1398 else
1399 {
1400 if (m_cursor_curve == n)
1401 InvalidateCursor(send_event);
1402 else if (m_cursor_curve > n)
1403 m_cursor_curve--;
1404
1405 ClearSelectedRanges(n, send_event);
1406 m_dataSelections.RemoveAt(n);
1407 m_curveSelections.RemoveAt(n);
1408 m_curves.RemoveAt(n);
1409 }
1410
1411
1412 if (m_active_index >= int(m_curves.GetCount()))
1413 {
1414 // force this invalid, can't override this, the curve is "gone"
1415 m_active_index = -1;
1416 m_activeCurve = NULL;
1417 SetActiveIndex( m_curves.GetCount() - 1, send_event );
1418 }
1419 else if (m_active_index >= 0)
1420 {
1421 SetActiveIndex( m_active_index, send_event );
1422 }
1423
1424 EndBatch(false); // still don't redraw
1425
1426 CalcBoundingPlotRect();
1427 CreateKeyString();
1428 Redraw(wxPLOT_REDRAW_PLOT);
1429
1430 if (send_event)
1431 {
1432 wxPlotEvent event1( wxEVT_PLOT_DELETED_CURVE, GetId(), this );
1433 event1.SetCurveIndex(n);
1434 (void)DoSendEvent( event1 );
1435 }
1436
1437 return true;
1438 }
1439
GetCurve(int n) const1440 wxPlotCurve* wxPlotCtrl::GetCurve( int n ) const
1441 {
1442 wxCHECK_MSG((n >= 0) && (n < GetCurveCount()), NULL, wxT("Invalid index"));
1443 return &(m_curves.Item(n));
1444 }
1445
SetActiveCurve(wxPlotCurve * current,bool send_event)1446 void wxPlotCtrl::SetActiveCurve( wxPlotCurve* current, bool send_event )
1447 {
1448 wxCHECK_RET(current, wxT("Invalid curve"));
1449
1450 int index = m_curves.Index( *current );
1451 wxCHECK_RET( index != wxNOT_FOUND, wxT("Unknown PlotCurve") );
1452
1453 SetActiveIndex( index, send_event );
1454 }
1455
SetActiveIndex(int curve_index,bool send_event)1456 void wxPlotCtrl::SetActiveIndex( int curve_index, bool send_event )
1457 {
1458 wxCHECK_RET((curve_index < GetCurveCount()), wxT("Invalid index"));
1459
1460 if (send_event)
1461 {
1462 wxPlotEvent event( wxEVT_PLOT_CURVE_SEL_CHANGING, GetId(), this);
1463 event.SetCurve(m_activeCurve, m_active_index);
1464 if (!DoSendEvent(event)) return;
1465 }
1466
1467 if ((curve_index >= 0) && m_curves.Item(curve_index).Ok())
1468 {
1469 m_active_index = curve_index;
1470 m_activeCurve = &(m_curves.Item(curve_index));
1471 }
1472 else
1473 {
1474 m_active_index = -1;
1475 m_activeCurve = NULL;
1476 }
1477
1478 if (send_event)
1479 {
1480 wxPlotEvent event( wxEVT_PLOT_CURVE_SEL_CHANGED, GetId(), this);
1481 event.SetCurve(m_activeCurve, m_active_index);
1482 (void)DoSendEvent( event );
1483 }
1484
1485 Redraw(wxPLOT_REDRAW_PLOT);
1486 }
1487
GetPlotDataIndexes() const1488 wxArrayInt wxPlotCtrl::GetPlotDataIndexes() const
1489 {
1490 wxArrayInt array;
1491 size_t n, count = m_curves.GetCount();
1492 for (n=0; n<count; n++)
1493 {
1494 if (wxDynamicCast(&m_curves.Item(n), wxPlotData))
1495 array.Add(n);
1496 }
1497 return array;
1498 }
GetPlotFunctionIndexes() const1499 wxArrayInt wxPlotCtrl::GetPlotFunctionIndexes() const
1500 {
1501 wxArrayInt array;
1502 size_t n, count = m_curves.GetCount();
1503 for (n=0; n<count; n++)
1504 {
1505 if (wxDynamicCast(&m_curves.Item(n), wxPlotFunction))
1506 array.Add(n);
1507 }
1508 return array;
1509 }
1510
1511 //-------------------------------------------------------------------------
1512 // Markers
1513 //-------------------------------------------------------------------------
1514
AddMarker(const wxPlotMarker & marker)1515 int wxPlotCtrl::AddMarker( const wxPlotMarker& marker )
1516 {
1517 m_plotMarkers.Add(marker);
1518 return m_plotMarkers.GetCount() - 1;
1519 }
1520
RemoveMarker(int marker)1521 void wxPlotCtrl::RemoveMarker(int marker)
1522 {
1523 wxCHECK_RET((marker >= 0) && (marker < (int)m_plotMarkers.GetCount()), wxT("Invalid marker number"));
1524 m_plotMarkers.RemoveAt(marker);
1525 }
1526
ClearMarkers()1527 void wxPlotCtrl::ClearMarkers()
1528 {
1529 m_plotMarkers.Clear();
1530 }
1531
GetMarker(int marker) const1532 wxPlotMarker wxPlotCtrl::GetMarker(int marker) const
1533 {
1534 wxCHECK_MSG((marker >= 0) && (marker < (int)m_plotMarkers.GetCount()), wxPlotMarker(),
1535 wxT("Invalid marker number"));
1536 return m_plotMarkers[marker];
1537 }
1538
1539 //-------------------------------------------------------------------------
1540 // Cursor position
1541 //-------------------------------------------------------------------------
1542
InvalidateCursor(bool send_event)1543 void wxPlotCtrl::InvalidateCursor(bool send_event)
1544 {
1545 bool changed = m_cursor_curve >= 0;
1546 m_cursor_curve = -1;
1547 m_cursor_index = -1;
1548 m_cursorMarker.SetPlotPosition(wxPoint2DDouble(0, 0));
1549
1550 if (send_event && changed)
1551 {
1552 wxPlotEvent plotEvent(wxEVT_PLOT_CURSOR_CHANGED, GetId(), this );
1553 (void)DoSendEvent( plotEvent );
1554 }
1555 }
IsCursorValid()1556 bool wxPlotCtrl::IsCursorValid()
1557 {
1558 if (m_cursor_curve < 0) return false;
1559
1560 // sanity check
1561 if (m_cursor_curve >= int(m_curves.GetCount()))
1562 {
1563 wxFAIL_MSG(wxT("Invalid cursor index"));
1564 InvalidateCursor(true);
1565 return false;
1566 }
1567
1568 wxPlotData *plotData = GetDataCurve(m_cursor_curve);
1569 if (plotData)
1570 {
1571 // sanity check
1572 if (m_cursor_index < 0)
1573 {
1574 wxFAIL_MSG(wxT("Invalid cursor data index"));
1575 InvalidateCursor(true);
1576 return false;
1577 }
1578 // if the curve shrinks or is bad
1579 if (!plotData->Ok() || (m_cursor_index >= (int)plotData->GetCount()))
1580 {
1581 InvalidateCursor(true);
1582 return false;
1583 }
1584
1585 m_cursorMarker.SetPlotPosition(plotData->GetPoint(m_cursor_index));
1586 }
1587 else
1588 {
1589 wxDouble x = m_cursorMarker.GetPlotRect().m_x;
1590 m_cursorMarker.GetPlotRect().m_y = GetCurve(m_cursor_curve)->GetY(x);
1591 }
1592
1593 return true;
1594 }
1595
GetCursorPoint()1596 wxPoint2DDouble wxPlotCtrl::GetCursorPoint()
1597 {
1598 wxCHECK_MSG(IsCursorValid(), wxPoint2DDouble(0, 0), wxT("invalid cursor"));
1599 return m_cursorMarker.GetPlotPosition();
1600 }
1601
SetCursorDataIndex(int curve_index,int cursor_index,bool send_event)1602 bool wxPlotCtrl::SetCursorDataIndex(int curve_index, int cursor_index, bool send_event)
1603 {
1604 wxCHECK_MSG(CurveIndexOk(curve_index) && GetDataCurve(curve_index),
1605 false, wxT("invalid curve index"));
1606
1607 wxPlotData *plotData = GetDataCurve(curve_index);
1608
1609 wxCHECK_MSG((cursor_index>=0) && plotData->Ok() && (cursor_index < (int)plotData->GetCount()),
1610 false, wxT("invalid index"));
1611
1612 // do nothing if already set
1613 if ((m_cursor_curve == curve_index) && (m_cursor_index == cursor_index))
1614 return false;
1615
1616 wxPoint2DDouble cursorPt(plotData->GetPoint(cursor_index));
1617
1618 if (send_event)
1619 {
1620 wxPlotEvent cursor_event(wxEVT_PLOT_CURSOR_CHANGING, GetId(), this );
1621 cursor_event.SetPosition( cursorPt.m_x, cursorPt.m_y );
1622 cursor_event.SetCurve( plotData, curve_index );
1623 cursor_event.SetCurveDataIndex(cursor_index);
1624 if (!DoSendEvent( cursor_event )) return false;
1625 }
1626
1627 int old_cursor_curve = m_cursor_curve;
1628 int old_cursor_index = m_cursor_index;
1629 m_cursorMarker.SetPlotPosition(cursorPt);
1630 m_cursor_curve = curve_index;
1631 m_cursor_index = cursor_index;
1632
1633 if (send_event)
1634 {
1635 wxPlotEvent cursor_event(wxEVT_PLOT_CURSOR_CHANGED, GetId(), this );
1636 cursor_event.SetPosition( cursorPt.m_x, cursorPt.m_y );
1637 cursor_event.SetCurve( plotData, curve_index );
1638 cursor_event.SetCurveDataIndex(cursor_index);
1639 (void)DoSendEvent( cursor_event );
1640 }
1641
1642 if ((m_active_index == old_cursor_curve) && (m_active_index == m_cursor_curve))
1643 {
1644 RedrawDataCurve(curve_index, old_cursor_index, old_cursor_index);
1645 RedrawDataCurve(curve_index, m_cursor_index, m_cursor_index);
1646 }
1647 else
1648 Redraw(wxPLOT_REDRAW_PLOT);
1649
1650 return true;
1651 }
SetCursorXPoint(int curve_index,double x,bool send_event)1652 bool wxPlotCtrl::SetCursorXPoint(int curve_index, double x, bool send_event)
1653 {
1654 wxCHECK_MSG(CurveIndexOk(curve_index), false, wxT("invalid curve index"));
1655
1656 if (GetDataCurve(curve_index))
1657 return SetCursorDataIndex(curve_index, GetDataCurve(curve_index)->GetIndexFromX(x), send_event);
1658
1659 // do nothing if already set
1660 if ((m_cursor_curve == curve_index) && (m_cursorMarker.GetPlotRect().m_x == x))
1661 return false;
1662
1663 wxPlotCurve *plotCurve = GetCurve(curve_index);
1664 wxPoint2DDouble cursorPt(x, plotCurve->GetY(x));
1665
1666 if (send_event)
1667 {
1668 wxPlotEvent cursor_event(wxEVT_PLOT_CURSOR_CHANGING, GetId(), this );
1669 cursor_event.SetPosition(cursorPt.m_x, cursorPt.m_y);
1670 cursor_event.SetCurve( plotCurve, curve_index );
1671 if (!DoSendEvent( cursor_event )) return false;
1672 }
1673
1674 m_cursorMarker.SetPlotPosition(cursorPt);
1675 m_cursor_curve = curve_index;
1676 m_cursor_index = -1;
1677
1678 if (send_event)
1679 {
1680 wxPlotEvent cursor_event(wxEVT_PLOT_CURSOR_CHANGED, GetId(), this );
1681 cursor_event.SetPosition(cursorPt.m_x, cursorPt.m_y);
1682 cursor_event.SetCurve( plotCurve, curve_index );
1683 (void)DoSendEvent( cursor_event );
1684 }
1685
1686 Redraw(wxPLOT_REDRAW_PLOT);
1687 return true;
1688 }
1689
MakeCursorVisible(bool center,bool send_event)1690 void wxPlotCtrl::MakeCursorVisible(bool center, bool send_event)
1691 {
1692 wxCHECK_RET(IsCursorValid(), wxT("invalid plot cursor"));
1693
1694 if (center)
1695 {
1696 wxPoint2DDouble origin = m_viewRect.GetLeftTop() -
1697 m_viewRect.GetCentre() +
1698 GetCursorPoint();
1699
1700 SetOrigin(origin.m_x, origin.m_y, send_event);
1701 return;
1702 }
1703
1704 wxPoint2DDouble origin = GetCursorPoint();
1705
1706 if (m_viewRect.Contains(origin))
1707 return;
1708
1709 double dx = 4/m_zoom.m_x;
1710 double dy = 4/m_zoom.m_y;
1711
1712 if (origin.m_x < m_viewRect.m_x)
1713 origin.m_x -= dx;
1714 else if (origin.m_x > m_viewRect.GetRight())
1715 origin.m_x = m_viewRect.m_x + (origin.m_x - m_viewRect.GetRight()) + dx;
1716 else
1717 origin.m_x = m_viewRect.m_x;
1718
1719 if (origin.m_y < m_viewRect.m_y)
1720 origin.m_y -= dy;
1721 else if (origin.m_y > m_viewRect.GetBottom())
1722 origin.m_y = m_viewRect.m_y + (origin.m_y - m_viewRect.GetBottom()) + dy;
1723 else
1724 origin.m_y = m_viewRect.m_y;
1725
1726 SetOrigin(origin.m_x, origin.m_y, send_event);
1727 }
1728
1729 //-------------------------------------------------------------------------
1730 // Selected points, data curves use
1731 //-------------------------------------------------------------------------
HasSelection(int curve_index) const1732 bool wxPlotCtrl::HasSelection(int curve_index) const
1733 {
1734 if (curve_index == -1)
1735 {
1736 int n, count = m_curveSelections.GetCount();
1737 for ( n = 0; n < count; n++ )
1738 {
1739 if ((m_curveSelections[n].GetCount() > 0) ||
1740 (m_dataSelections[n].GetCount() > 0))
1741 return true;
1742 }
1743 return false;
1744 }
1745
1746 wxCHECK_MSG(CurveIndexOk(curve_index), false, wxT("invalid curve index"));
1747 return (m_curveSelections[curve_index].GetCount() > 0) ||
1748 (m_dataSelections[curve_index].GetCount() > 0);
1749 }
1750
GetCurveSelection(int curve_index) const1751 wxRangeDoubleSelection *wxPlotCtrl::GetCurveSelection(int curve_index) const
1752 {
1753 wxCHECK_MSG(CurveIndexOk(curve_index), NULL, wxT("invalid curve index"));
1754 return &m_curveSelections[curve_index];
1755 }
1756
GetDataCurveSelection(int curve_index) const1757 wxRangeIntSelection *wxPlotCtrl::GetDataCurveSelection(int curve_index) const
1758 {
1759 wxCHECK_MSG(CurveIndexOk(curve_index), NULL, wxT("invalid curve index"));
1760 return &m_dataSelections[curve_index];
1761 }
1762
UpdateSelectionState(int curve_index,bool send_event)1763 bool wxPlotCtrl::UpdateSelectionState(int curve_index, bool send_event)
1764 {
1765 wxCHECK_MSG(CurveIndexOk(curve_index), false, wxT("invalid curve index"));
1766 switch (m_selection_type)
1767 {
1768 case wxPLOT_SELECT_NONE : break; // should have been handled
1769 case wxPLOT_SELECT_SINGLE :
1770 {
1771 if (HasSelection())
1772 return ClearSelectedRanges(-1, send_event);
1773
1774 break;
1775 }
1776 case wxPLOT_SELECT_SINGLE_CURVE :
1777 {
1778 int n, count = m_curves.GetCount();
1779 bool done = false;
1780 for ( n = 0; n < count; n++ )
1781 {
1782 if (n == curve_index) continue;
1783 if (HasSelection(n))
1784 done |= ClearSelectedRanges(n, send_event);
1785 }
1786 return done;
1787 }
1788 case wxPLOT_SELECT_SINGLE_PER_CURVE :
1789 {
1790 if (HasSelection(curve_index))
1791 return ClearSelectedRanges(curve_index, send_event);
1792
1793 break;
1794 }
1795 case wxPLOT_SELECT_MULTIPLE : break; // anything goes
1796 default : break;
1797 }
1798
1799 return false;
1800 }
1801
DoSelectRectangle(int curve_index,const wxRect2DDouble & rect,bool select,bool send_event)1802 bool wxPlotCtrl::DoSelectRectangle(int curve_index, const wxRect2DDouble &rect,
1803 bool select, bool send_event)
1804 {
1805 wxCHECK_MSG((curve_index >= -1) && (curve_index<int(m_curves.GetCount())),
1806 false, wxT("invalid plotcurve index"));
1807 wxCHECK_MSG((rect.m_width > 0) || (rect.m_height > 0), false, wxT("invalid selection range"));
1808
1809 if (m_selection_type == wxPLOT_SELECT_NONE)
1810 return false;
1811
1812 if (!IsFinite(rect.m_x, wxT("Selection x is NaN")) ||
1813 !IsFinite(rect.m_y, wxT("Selection y is NaN")) ||
1814 !IsFinite(rect.m_width, wxT("Selection width is NaN")) ||
1815 !IsFinite(rect.m_height, wxT("Selection height is NaN")) )
1816 return false;
1817
1818 bool done = false;
1819
1820 // Run this code for all the curves if curve == -1 then exit
1821 if (curve_index == -1)
1822 {
1823 size_t n, curve_count = m_curves.GetCount();
1824
1825 for (n = 0; n < curve_count; n++)
1826 done |= DoSelectRectangle(n, rect, select, send_event);
1827
1828 return done;
1829 }
1830
1831 // check the selection type and clear previous selections if necessary
1832 if (select)
1833 UpdateSelectionState(curve_index, send_event);
1834
1835 bool is_x_range = rect.m_height <= 0;
1836 bool is_y_range = rect.m_width <= 0;
1837 wxRangeDouble xRange(rect.m_x, rect.GetRight());
1838 wxRangeDouble yRange(rect.m_y, rect.GetBottom());
1839
1840 wxPlotData *plotData = GetDataCurve(curve_index);
1841 if (plotData)
1842 {
1843 wxCHECK_MSG(plotData->Ok(), false, wxT("Invalid data curve"));
1844 wxRect2DDouble r(plotData->GetBoundingRect());
1845
1846 if ((xRange.m_max < r.GetLeft()) || (xRange.m_min > r.GetRight()))
1847 return false;
1848
1849 if (is_x_range && plotData->GetIsXOrdered())
1850 {
1851 int min_ = plotData->GetIndexFromX(xRange.m_min);
1852 int max_ = plotData->GetIndexFromX(xRange.m_max);
1853 int count = plotData->GetCount();
1854
1855 if ( (plotData->GetXValue(min_) > xRange.m_min) && (min_ > 0) &&
1856 (plotData->GetXValue(min_-1) > xRange.m_min) )
1857 min_--;
1858 if ( (plotData->GetXValue(min_) < xRange.m_min) && (min_ < count -1) )
1859 min_++;
1860
1861 if ( (plotData->GetXValue(max_) > xRange.m_max) && (max_ > 0) )
1862 max_--;
1863 if ( (plotData->GetXValue(max_) < xRange.m_max) && (max_ < count-1) &&
1864 (plotData->GetXValue(max_+1) < xRange.m_max) )
1865 max_++;
1866
1867 wxRangeInt sel(min_, max_); // always check if max < min! - not a bug
1868
1869 if (!sel.IsEmpty())
1870 {
1871 if (select)
1872 m_curveSelections[curve_index].SelectRange(wxRangeDouble(rect.m_x, rect.GetRight()));
1873 else
1874 m_curveSelections[curve_index].DeselectRange(wxRangeDouble(rect.m_x, rect.GetRight()));
1875
1876 return DoSelectDataRange(curve_index, sel, select, send_event);
1877 }
1878 else
1879 return false;
1880 }
1881 else // not ordered or not just an x selection
1882 {
1883 int i, count = plotData->GetCount();
1884 int first_sel = -1;
1885 double *x_data = plotData->GetXData();
1886 double *y_data = plotData->GetYData();
1887
1888 int min_ = plotData->GetCount()-1, max_ = 0;
1889
1890 wxRangeIntSelection ranges;
1891
1892 for (i=0; i<count; i++)
1893 {
1894 if ((is_x_range && xRange.Contains(*x_data)) ||
1895 (is_y_range && yRange.Contains(*y_data)) ||
1896 (!is_x_range && !is_y_range && wxPlotRect2DDoubleContains(*x_data, *y_data, rect)))
1897 {
1898 if (select)
1899 {
1900 if (m_dataSelections[curve_index].SelectRange(wxRangeInt(i,i)))
1901 {
1902 ranges.SelectRange(wxRangeInt(i,i));
1903 done = true;
1904 }
1905 }
1906 else
1907 {
1908 if (m_dataSelections[curve_index].DeselectRange(wxRangeInt(i,i)))
1909 {
1910 ranges.SelectRange(wxRangeInt(i,i));
1911 done = true;
1912 }
1913 }
1914
1915 min_ = wxMin(min_, i);
1916 max_ = wxMin(max_, i);
1917
1918 if (done && (first_sel == -1))
1919 first_sel = i;
1920 }
1921
1922 x_data++;
1923 y_data++;
1924 }
1925
1926 if (done && (min_ <= max_))
1927 RedrawDataCurve(curve_index, min_, max_);
1928
1929 if (done)
1930 {
1931 if (select)
1932 m_curveSelections[curve_index].SelectRange(wxRangeDouble(rect.m_x, rect.GetRight()));
1933 else
1934 m_curveSelections[curve_index].DeselectRange(wxRangeDouble(rect.m_x, rect.GetRight()));
1935 }
1936
1937 if (send_event && done)
1938 {
1939 wxPlotSelectionEvent event( wxEVT_PLOT_RANGE_SEL_CHANGED, GetId(), this);
1940 event.SetCurve(GetCurve(curve_index), curve_index);
1941 event.SetDataSelectionRange( wxRangeInt(first_sel, first_sel), select );
1942 event.SetDataSelections(ranges);
1943 (void)DoSendEvent( event );
1944 }
1945
1946 return done;
1947 }
1948 }
1949 else
1950 {
1951 if (select)
1952 done = m_curveSelections[curve_index].SelectRange(wxRangeDouble(rect.m_x, rect.GetRight()));
1953 else
1954 done = m_curveSelections[curve_index].DeselectRange(wxRangeDouble(rect.m_x, rect.GetRight()));
1955
1956 if (send_event && done)
1957 {
1958 wxPlotSelectionEvent event( wxEVT_PLOT_RANGE_SEL_CHANGED, GetId(), this);
1959 event.SetCurve(GetCurve(curve_index), curve_index);
1960 event.SetCurveSelectionRange( xRange, select );
1961 (void)DoSendEvent( event );
1962 }
1963
1964 if (done) RedrawCurve(curve_index, xRange.m_min, xRange.m_max);
1965
1966 return done;
1967 }
1968 }
1969
DoSelectDataRange(int curve_index,const wxRangeInt & range,bool select,bool send_event)1970 bool wxPlotCtrl::DoSelectDataRange(int curve_index, const wxRangeInt &range,
1971 bool select, bool send_event)
1972 {
1973 wxCHECK_MSG(CurveIndexOk(curve_index), false, wxT("invalid plotcurve index"));
1974 wxCHECK_MSG(!range.IsEmpty(), false, wxT("invalid selection range"));
1975
1976 if (m_selection_type == wxPLOT_SELECT_NONE)
1977 return false;
1978
1979 wxPlotData *plotData = GetDataCurve(curve_index);
1980 wxCHECK_MSG(plotData && (range.m_min >= 0) && (range.m_max < (int)plotData->GetCount()), false, wxT("invalid index"));
1981
1982 // check the selection type and clear previous selections if necessary
1983 if (select)
1984 UpdateSelectionState(curve_index, send_event);
1985
1986 bool done = false;
1987
1988 if (select)
1989 done = m_dataSelections[curve_index].SelectRange(range);
1990 else
1991 done = m_dataSelections[curve_index].DeselectRange(range);
1992
1993 wxPrintf(wxT("Do sel %d %d %d\n"), range.m_min, range.m_max, done);
1994
1995 if (send_event && done)
1996 {
1997 wxPlotSelectionEvent event( wxEVT_PLOT_RANGE_SEL_CHANGED, GetId(), this);
1998 event.SetCurve(GetCurve(curve_index), curve_index);
1999 event.SetDataSelectionRange( range, select );
2000 event.m_dataSelection.SelectRange(range);
2001 (void)DoSendEvent( event );
2002 }
2003
2004 if (done) RedrawDataCurve(curve_index, range.m_min, range.m_max);
2005
2006 return done;
2007 }
2008
GetSelectedRangeCount(int curve_index) const2009 int wxPlotCtrl::GetSelectedRangeCount(int curve_index) const
2010 {
2011 wxCHECK_MSG(CurveIndexOk(curve_index), 0, wxT("invalid plotcurve index"));
2012
2013 if (GetDataCurve(curve_index))
2014 return m_dataSelections[curve_index].GetCount();
2015 else
2016 return m_curveSelections[curve_index].GetCount();
2017 }
2018
ClearSelectedRanges(int curve_index,bool send_event)2019 bool wxPlotCtrl::ClearSelectedRanges(int curve_index, bool send_event)
2020 {
2021 wxCHECK_MSG((curve_index >= -1) && (curve_index<int(m_curves.GetCount())),
2022 false, wxT("invalid plotcurve index"));
2023
2024 bool done = false;
2025
2026 if (curve_index == -1)
2027 {
2028 for (size_t n=0; n<m_curves.GetCount(); n++)
2029 done |= ClearSelectedRanges(n, send_event);
2030
2031 return done;
2032 }
2033 else
2034 {
2035 if (IsDataCurve(curve_index))
2036 {
2037 done = m_dataSelections[curve_index].GetCount() > 0;
2038 m_dataSelections[curve_index].Clear();
2039 m_curveSelections[curve_index].Clear();
2040 if (done)
2041 RedrawDataCurve(curve_index, 0, GetDataCurve(curve_index)->GetCount()-1);
2042 }
2043 else
2044 {
2045 done = m_curveSelections[curve_index].GetCount() > 0;
2046 m_curveSelections[curve_index].Clear();
2047 m_dataSelections[curve_index].Clear();
2048 if (done)
2049 RedrawCurve(curve_index, m_viewRect.m_x, m_viewRect.GetRight());
2050 }
2051 }
2052
2053 if (send_event && done)
2054 {
2055 wxPlotSelectionEvent event( wxEVT_PLOT_RANGE_SEL_CHANGED, GetId(), this);
2056 event.SetCurve(GetCurve(curve_index), curve_index);
2057
2058 if (IsDataCurve(curve_index))
2059 event.SetDataSelectionRange(wxRangeInt(0, GetDataCurve(curve_index)->GetCount()-1), false);
2060 else
2061 event.SetCurveSelectionRange(wxEmptyRangeDouble, false);
2062
2063 (void)DoSendEvent( event );
2064 }
2065 return done;
2066 }
2067
2068 // ------------------------------------------------------------------------
2069 // Get/Set origin, size, and Zoom in/out of view, set scaling, size...
2070 // ------------------------------------------------------------------------
2071 /*
2072
2073 // FIXME - can't shift the bitmap due to off by one errors in ClipLineToRect
2074
2075 void wxPlotCtrl::ShiftOrigin( int dx, int dy, bool send_event )
2076 {
2077 if ((dx == 0) && (dy == 0)) return;
2078
2079 if (send_event)
2080 {
2081 wxPlotEvent event( wxEVT_PLOT_VIEW_CHANGING, GetId(), this);
2082 event.SetCurve(m_activeCurve, m_active_index);
2083 if (DoSendEvent(event)) return;
2084 }
2085
2086 {
2087 wxBitmap tempBitmap(m_areaClientRect.width, m_areaClientRect.height);
2088 wxMemoryDC mdc;
2089 mdc.SelectObject(tempBitmap);
2090 mdc.DrawBitmap( m_area->m_bitmap, dx, dy, false );
2091 mdc.SelectObject(wxNullBitmap);
2092 m_area->m_bitmap = tempBitmap;
2093 }
2094 wxRect rx, ry;
2095
2096 m_viewRect.m_x -= dx / m_zoom.m_x;
2097 m_viewRect.m_y += dy / m_zoom.m_y;
2098
2099 if (dx != 0)
2100 {
2101 rx = wxRect((dx>0 ? -5 : m_areaClientRect.width+dx-5), 0, labs(dx)+10, m_areaClientRect.height);
2102 RedrawXAxis(false);
2103 }
2104 if (dy != 0)
2105 {
2106 ry = wxRect(0, (dy>0 ? -5 : m_areaClientRect.height+dy-5), m_areaClientRect.width, labs(dy)+10);
2107 RedrawYAxis(false);
2108 }
2109
2110 printf("Shift %d %d rx %d %d %d %d, ry %d %d %d %d\n", dx, dy, rx.x, rx.y, rx.width, rx.height, ry.x, ry.y, ry.width, ry.height); fflush(stdout);
2111
2112 if (rx.width > 0) m_area->CreateBitmap( rx );
2113 //m_area->Refresh(false, &rx);
2114 if (ry.height > 0) m_area->CreateBitmap( ry );
2115 //m_area->Refresh(false, &ry);
2116
2117 {
2118 wxClientDC cdc(m_area);
2119 cdc.DrawBitmap(m_area->m_bitmap, 0, 0);
2120 }
2121
2122 AdjustScrollBars();
2123
2124 if (send_event)
2125 {
2126 wxPlotEvent event( wxEVT_PLOT_VIEW_CHANGED, GetId(), this);
2127 event.SetCurve(m_activeCurve, m_active_index);
2128 (void)DoSendEvent( event );
2129 }
2130 }
2131 */
2132
MakeCurveVisible(int curve_index,bool send_event)2133 bool wxPlotCtrl::MakeCurveVisible(int curve_index, bool send_event)
2134 {
2135 if (curve_index < 0)
2136 return SetZoom( -1, -1, 0, 0, send_event );
2137
2138 wxCHECK_MSG(curve_index < GetCurveCount(), false, wxT("Invalid curve index"));
2139 wxPlotCurve *curve = GetCurve(curve_index);
2140 wxCHECK_MSG(curve && curve->Ok(), false, wxT("Invalid curve"));
2141 return SetViewRect(curve->GetBoundingRect(), send_event);
2142 }
2143
SetViewRect(const wxRect2DDouble & view,bool send_event)2144 bool wxPlotCtrl::SetViewRect(const wxRect2DDouble &view, bool send_event)
2145 {
2146 double zoom_x = m_areaClientRect.width/view.m_width;
2147 double zoom_y = m_areaClientRect.height/view.m_height;
2148 return SetZoom(zoom_x, zoom_y, view.m_x, view.m_y, send_event);
2149 }
2150
SetZoom(const wxPoint2DDouble & zoom,bool around_center,bool send_event)2151 bool wxPlotCtrl::SetZoom( const wxPoint2DDouble &zoom, bool around_center, bool send_event )
2152 {
2153 if (around_center && (zoom.m_x > 0) && (zoom.m_y > 0))
2154 {
2155 double origin_x = (m_viewRect.GetLeft() + m_viewRect.m_width/2.0);
2156 origin_x -= (m_viewRect.m_width/2.0)*m_zoom.m_x/zoom.m_x;
2157 double origin_y = (m_viewRect.GetTop() + m_viewRect.m_height/2.0);
2158 origin_y -= (m_viewRect.m_height/2.0)*m_zoom.m_y/zoom.m_y;
2159 return SetZoom( zoom.m_x, zoom.m_y, origin_x, origin_y, send_event );
2160 }
2161 else
2162 return SetZoom( zoom.m_x, zoom.m_y, m_viewRect.GetLeft(), m_viewRect.GetTop(), send_event );
2163 }
2164
SetZoom(const wxRect & window,bool send_event)2165 bool wxPlotCtrl::SetZoom( const wxRect &window, bool send_event )
2166 {
2167 if ((window.GetHeight()<1) || (window.GetWidth()<1)) return false;
2168
2169 double origin_x = GetPlotCoordFromClientX(window.GetX());
2170 double origin_y = GetPlotCoordFromClientY(window.GetY()+window.GetHeight());
2171 double zoom_x = m_zoom.m_x * double(m_areaClientRect.width) /(window.GetWidth());
2172 double zoom_y = m_zoom.m_y * double(m_areaClientRect.height)/(window.GetHeight());
2173
2174 bool ok = SetZoom( zoom_x, zoom_y, origin_x, origin_y, send_event );
2175 if (ok) AddHistoryView();
2176 return ok;
2177 }
2178
SetZoom(double zoom_x,double zoom_y,double origin_x,double origin_y,bool send_event)2179 bool wxPlotCtrl::SetZoom( double zoom_x, double zoom_y,
2180 double origin_x, double origin_y, bool send_event )
2181 {
2182 // fit to window if zoom <= 0
2183 if (zoom_x <= 0)
2184 {
2185 zoom_x = double(m_areaClientRect.width)/(m_curveBoundingRect.m_width);
2186 origin_x = m_curveBoundingRect.m_x;
2187 }
2188 if (zoom_y <= 0)
2189 {
2190 zoom_y = double(m_areaClientRect.height)/(m_curveBoundingRect.m_height);
2191 origin_y = m_curveBoundingRect.m_y;
2192 }
2193
2194 if (m_fix_aspectratio)
2195 FixAspectRatio( &zoom_x, &zoom_y, &origin_x, &origin_y );
2196
2197 double view_width = m_areaClientRect.width/zoom_x;
2198 double view_height = m_areaClientRect.height/zoom_y;
2199
2200 if (!IsFinite(zoom_x, wxT("X zoom is NaN"))) return false;
2201 if (!IsFinite(zoom_y, wxT("Y zoom is NaN"))) return false;
2202 if (!IsFinite(origin_x, wxT("X origin is not finite"))) return false;
2203 if (!IsFinite(origin_y, wxT("Y origin is not finite"))) return false;
2204 if (!IsFinite(view_width, wxT("Plot width is NaN"))) return false;
2205 if (!IsFinite(view_height, wxT("Plot height is NaN"))) return false;
2206
2207 bool x_changed = false, y_changed = false;
2208
2209 if ((m_viewRect.m_x != origin_x) || (m_zoom.m_x != zoom_x))
2210 x_changed = true;
2211 if ((m_viewRect.m_y != origin_y) || (m_zoom.m_y != zoom_y))
2212 y_changed = true;
2213
2214 if (x_changed || y_changed)
2215 {
2216 if (send_event)
2217 {
2218 wxPlotEvent event( wxEVT_PLOT_VIEW_CHANGING, GetId(), this);
2219 event.SetCurve(m_activeCurve, m_active_index);
2220 if (!DoSendEvent(event)) return false;
2221 }
2222
2223 m_zoom.m_x = zoom_x;
2224 m_zoom.m_y = zoom_y;
2225
2226 m_viewRect.m_x = origin_x;
2227 m_viewRect.m_y = origin_y;
2228 m_viewRect.m_width = view_width;
2229 m_viewRect.m_height = view_height;
2230 }
2231
2232 // redraw even if unchanged since we expect that it should be different
2233 Redraw(wxPLOT_REDRAW_PLOT | (x_changed?wxPLOT_REDRAW_XAXIS:0) |
2234 (y_changed?wxPLOT_REDRAW_YAXIS:0));
2235
2236 if (!m_batch_count)
2237 AdjustScrollBars();
2238
2239 if (send_event && (x_changed || y_changed))
2240 {
2241 wxPlotEvent event( wxEVT_PLOT_VIEW_CHANGED, GetId(), this);
2242 event.SetCurve(m_activeCurve, m_active_index);
2243 (void)DoSendEvent( event );
2244 }
2245
2246 return true;
2247 }
2248
SetFixAspectRatio(bool fix,double ratio)2249 void wxPlotCtrl::SetFixAspectRatio(bool fix, double ratio)
2250 {
2251 wxCHECK_RET(ratio > 0, wxT("Invalid aspect ratio"));
2252 m_fix_aspectratio = fix;
2253 m_aspectratio = ratio;
2254 }
2255
FixAspectRatio(double * zoom_x,double * zoom_y,double * origin_x,double * origin_y)2256 void wxPlotCtrl::FixAspectRatio( double *zoom_x, double *zoom_y, double *origin_x, double *origin_y )
2257 {
2258 wxCHECK_RET(zoom_x && zoom_y && origin_x && origin_y, wxT("Invalid parameters"));
2259
2260 //get the width and height of the view in plot coordinates
2261 double viewWidth = m_areaClientRect.width / (*zoom_x);
2262 double viewHeight = m_areaClientRect.height / (*zoom_y);
2263
2264 //get the centre of the visible area in plot coordinates
2265 double xCentre = (*origin_x) + viewWidth / 2;
2266 double yCentre = (*origin_y) + viewHeight / 2;
2267
2268 //if zoom in one direction is more than in the other, reduce both to the lower value
2269 if( (*zoom_x) * m_aspectratio > (*zoom_y) )
2270 {
2271 (*zoom_x) = (*zoom_y) * m_aspectratio;
2272 (*zoom_y) = (*zoom_y);
2273 }
2274 else
2275 {
2276 (*zoom_x) = (*zoom_x);
2277 (*zoom_y) = (*zoom_x) / m_aspectratio;
2278 }
2279
2280 //update the plot coordinate view width and height based on the new zooms
2281 viewWidth = m_areaClientRect.width / (*zoom_x);
2282 viewHeight = m_areaClientRect.height / (*zoom_y);
2283
2284 //create the new bottom-left corner of the view in plot coordinates
2285 *origin_x = xCentre - (viewWidth / 2);
2286 *origin_y = yCentre - (viewHeight / 2);
2287 }
2288
SetDefaultBoundingRect(const wxRect2DDouble & rect,bool send_event)2289 void wxPlotCtrl::SetDefaultBoundingRect( const wxRect2DDouble &rect, bool send_event )
2290 {
2291 wxCHECK_RET(wxFinite(rect.m_x)&&wxFinite(rect.m_y)&&wxFinite(rect.GetRight())&&wxFinite(rect.GetBottom()), wxT("bounding rect is NaN"));
2292 wxCHECK_RET((rect.m_width > 0) && (rect.m_height > 0), wxT("Plot Size < 0"));
2293 m_defaultPlotRect = rect;
2294 CalcBoundingPlotRect();
2295 SetZoom( m_areaClientRect.width/rect.m_width,
2296 m_areaClientRect.height/rect.m_height,
2297 rect.m_x, rect.m_y, send_event);
2298 }
2299
AddHistoryView()2300 void wxPlotCtrl::AddHistoryView()
2301 {
2302 if (!(wxFinite(m_viewRect.GetLeft())&&wxFinite(m_viewRect.GetRight())&&wxFinite(m_viewRect.GetTop())&&wxFinite(m_viewRect.GetBottom()))) return;
2303
2304 if ((m_history_views_index >= 0)
2305 && (m_history_views_index < int(m_historyViews.GetCount()))
2306 && WXRECT2DDOUBLE_EQUAL(m_viewRect, m_historyViews[m_history_views_index]))
2307 return;
2308
2309 if (int(m_historyViews.GetCount()) >= MAX_PLOT_ZOOMS)
2310 {
2311 if (m_history_views_index < int(m_historyViews.GetCount())-1)
2312 {
2313 m_historyViews[m_history_views_index] = m_viewRect;
2314 }
2315 else
2316 {
2317 m_historyViews.RemoveAt(0);
2318 m_historyViews.Add(m_viewRect);
2319 }
2320 }
2321 else
2322 {
2323 m_historyViews.Add(m_viewRect);
2324 m_history_views_index++;
2325 }
2326 }
2327
NextHistoryView(bool foward,bool send_event)2328 void wxPlotCtrl::NextHistoryView(bool foward, bool send_event)
2329 {
2330 int count = m_historyViews.GetCount();
2331
2332 // try to set it to the "current" history view
2333 if ((m_history_views_index > -1) && (m_history_views_index < count))
2334 {
2335 if (!WXRECT2DDOUBLE_EQUAL(m_viewRect, m_historyViews[m_history_views_index]))
2336 SetViewRect(m_historyViews[m_history_views_index], send_event);
2337 }
2338
2339 if (foward)
2340 {
2341 if ((count > 0) && (m_history_views_index < count - 1))
2342 {
2343 m_history_views_index++;
2344 SetViewRect(m_historyViews[m_history_views_index], send_event);
2345 }
2346 }
2347 else
2348 {
2349 if (m_history_views_index > 0)
2350 {
2351 m_history_views_index--;
2352 SetViewRect(m_historyViews[m_history_views_index], send_event);
2353 }
2354 else
2355 SetZoom(-1, -1, 0, 0, send_event);
2356 }
2357 }
2358
SetAreaMouseFunction(wxPlotMouse_Type func,bool send_event)2359 void wxPlotCtrl::SetAreaMouseFunction( wxPlotMouse_Type func, bool send_event )
2360 {
2361 if (func == m_area_mouse_func) return;
2362
2363 if (send_event)
2364 {
2365 wxPlotEvent event1(wxEVT_PLOT_MOUSE_FUNC_CHANGING, GetId(), this);
2366 event1.SetMouseFunction(func);
2367 if (!DoSendEvent(event1))
2368 return;
2369 }
2370
2371 m_area_mouse_func = func;
2372
2373 switch (func)
2374 {
2375 case wxPLOT_MOUSE_ZOOM :
2376 {
2377 SetAreaMouseCursor(wxCURSOR_MAGNIFIER); //wxCURSOR_CROSS);
2378 break;
2379 }
2380 case wxPLOT_MOUSE_SELECT :
2381 case wxPLOT_MOUSE_DESELECT :
2382 {
2383 SetAreaMouseCursor(wxCURSOR_ARROW);
2384 break;
2385 }
2386 case wxPLOT_MOUSE_PAN :
2387 {
2388 SetAreaMouseCursor(wxCURSOR_HAND);
2389 SetAreaMouseMarker(wxPLOT_MARKER_NONE);
2390 break;
2391 }
2392 case wxPLOT_MOUSE_NOTHING :
2393 default :
2394 {
2395 SetAreaMouseCursor(wxCURSOR_CROSS);
2396 SetAreaMouseMarker(wxPLOT_MARKER_NONE);
2397 break;
2398 }
2399 }
2400
2401 if (send_event)
2402 {
2403 wxPlotEvent event2(wxEVT_PLOT_MOUSE_FUNC_CHANGED, GetId(), this);
2404 event2.SetMouseFunction(func);
2405 (void)DoSendEvent(event2);
2406 }
2407 }
2408
SetAreaMouseMarker(wxPlotMarker_Type type)2409 void wxPlotCtrl::SetAreaMouseMarker( wxPlotMarker_Type type )
2410 {
2411 if (type == m_area_mouse_marker)
2412 return;
2413
2414 wxClientDC dc(m_area);
2415 DrawMouseMarker( &dc, m_area_mouse_marker, m_area->m_mouseRect );
2416 m_area_mouse_marker = type;
2417 DrawMouseMarker( &dc, m_area_mouse_marker, m_area->m_mouseRect );
2418 }
2419
SetAreaMouseCursor(int cursorid)2420 void wxPlotCtrl::SetAreaMouseCursor(int cursorid)
2421 {
2422 if (cursorid == m_area_mouse_cursorid)
2423 return;
2424
2425 m_area_mouse_cursorid = cursorid;
2426
2427 if (cursorid == wxCURSOR_HAND)
2428 m_area->SetCursor(s_handCursor);
2429 else if (cursorid == CURSOR_GRAB)
2430 m_area->SetCursor(s_grabCursor);
2431 else
2432 m_area->SetCursor(wxCursor(cursorid));
2433 }
2434
OnSize(wxSizeEvent &)2435 void wxPlotCtrl::OnSize( wxSizeEvent& )
2436 {
2437 DoSize();
2438 }
2439
DoSize(const wxRect & boundingRect)2440 void wxPlotCtrl::DoSize(const wxRect &boundingRect)
2441 {
2442 if (!m_yAxisScrollbar) return; // we're not created yet
2443
2444 m_redraw_type = wxPLOT_REDRAW_BLOCKER; // block OnPaints until done
2445
2446 wxSize size;
2447
2448 if(boundingRect == wxRect(0, 0, 0, 0))
2449 {
2450 UpdateWindowSize();
2451 size = GetClientSize();
2452 }
2453 else
2454 {
2455 size.x = boundingRect.width;
2456 size.y = boundingRect.height;
2457 }
2458
2459 int sb_width = m_yAxisScrollbar->GetSize().GetWidth();
2460
2461 m_clientRect = wxRect(0, 0, size.x-sb_width, size.y-sb_width);
2462
2463 // scrollbar to right and bottom
2464 m_yAxisScrollbar->SetSize(m_clientRect.width, 0, sb_width, m_clientRect.height );
2465 m_xAxisScrollbar->SetSize(0, m_clientRect.height, m_clientRect.width, sb_width );
2466
2467 // title and label positions, add padding here
2468 wxRect titleRect = m_show_title ? wxRect(m_titleRect).Inflate(m_border) : wxRect(0,0,1,1);
2469 wxRect xLabelRect = m_show_xlabel ? wxRect(m_xLabelRect).Inflate(m_border) : wxRect(0,0,1,1);
2470 wxRect yLabelRect = m_show_ylabel ? wxRect(m_yLabelRect).Inflate(m_border) : wxRect(0,0,1,1);
2471
2472 // this is the border around the area, it lets you see about 1 digit extra on axis
2473 int area_border = m_axisFontSize.y/2;
2474
2475 // use the area_border between top of y-axis and area as bottom border of title
2476 if (m_show_title) titleRect.height -= m_border;
2477
2478 int yaxis_width = GetShowYAxis() ? m_y_axis_text_width : 1;
2479 int xaxis_height = GetShowXAxis() ? m_axisFontSize.y : 0;
2480
2481 int area_width = m_clientRect.width - yLabelRect.GetRight() - yaxis_width - 2*area_border;
2482 int area_height = m_clientRect.height - titleRect.GetBottom() - xaxis_height - xLabelRect.height - area_border;
2483
2484 m_yAxisRect = wxRect(yLabelRect.GetRight(),
2485 titleRect.GetBottom(),
2486 yaxis_width,
2487 area_height + 2*area_border);
2488
2489 m_xAxisRect = wxRect(m_yAxisRect.GetRight(),
2490 m_yAxisRect.GetBottom() - area_border + 1,
2491 area_width + 2*area_border,
2492 xaxis_height);
2493
2494 m_areaRect = wxRect(m_yAxisRect.GetRight() + area_border,
2495 m_yAxisRect.GetTop() + area_border,
2496 area_width,
2497 area_height);
2498
2499 m_yAxis->SetSize(m_yAxisRect);
2500 m_xAxis->SetSize(m_xAxisRect);
2501 m_area->SetSize(m_areaRect);
2502
2503 m_titleRect.x = m_areaRect.x + ( m_areaRect.width - m_titleRect.GetWidth() ) / 2;
2504 //m_titleRect.x = m_clientRect.width/2-m_titleRect.GetWidth()/2; center on whole plot
2505 m_titleRect.y = m_border;
2506
2507 m_xLabelRect.x = m_areaRect.x + m_areaRect.width/2 - m_xLabelRect.width/2;
2508 m_xLabelRect.y = m_xAxisRect.GetBottom() + m_border;
2509
2510 m_yLabelRect.x = m_border;
2511 m_yLabelRect.y = m_areaRect.y + m_areaRect.height/2 - m_yLabelRect.height/2;
2512
2513 UpdateWindowSize();
2514
2515 double zoom_x = m_areaClientRect.width/m_viewRect.m_width;
2516 double zoom_y = m_areaClientRect.height/m_viewRect.m_height;
2517 if (!IsFinite(zoom_x, wxT("Plot zoom is NaN"))) return;
2518 if (!IsFinite(zoom_y, wxT("Plot zoom is NaN"))) return;
2519
2520 if (m_fix_aspectratio)
2521 {
2522 double x = m_viewRect.m_x, y = m_viewRect.m_y;
2523 //FixAspectRatio( &zoom_x, &zoom_y, &m_viewRect.m_x, &m_viewRect.m_y );
2524 FixAspectRatio(&zoom_x, &zoom_y, &x, &y);
2525 m_viewRect.m_x = x;
2526 m_viewRect.m_y = y;
2527
2528 m_viewRect.m_width = m_areaClientRect.width/zoom_x;
2529 m_viewRect.m_height = m_areaClientRect.height/zoom_y;
2530 }
2531
2532 m_zoom.m_x = zoom_x;
2533 m_zoom.m_y = zoom_y;
2534
2535 wxPlotEvent event( wxEVT_PLOT_VIEW_CHANGED, GetId(), this);
2536 event.SetCurve(m_activeCurve, m_active_index);
2537 (void)DoSendEvent( event );
2538
2539 m_redraw_type = 0;
2540 Redraw(wxPLOT_REDRAW_WHOLEPLOT);
2541 }
2542
CalcBoundingPlotRect()2543 void wxPlotCtrl::CalcBoundingPlotRect()
2544 {
2545 int i, count = m_curves.GetCount();
2546
2547 if (count > 0)
2548 {
2549 bool valid_rect = false;
2550
2551 wxRect2DDouble rect = m_curves[0].GetBoundingRect();
2552
2553 if ( IsFinite(rect.m_x, wxT("left curve boundary is NaN")) &&
2554 IsFinite(rect.m_y, wxT("bottom curve boundary is NaN")) &&
2555 IsFinite(rect.GetRight(), wxT("right curve boundary is NaN")) &&
2556 IsFinite(rect.GetBottom(), wxT("top curve boundary is NaN")) &&
2557 (rect.m_width >= 0) && (rect.m_height >= 0) )
2558 {
2559 valid_rect = true;
2560 }
2561 else
2562 rect = wxRect2DDouble(0, 0, 0, 0);
2563
2564 for (i=1; i<count; i++)
2565 {
2566 wxRect2DDouble curveRect = m_curves[i].GetBoundingRect();
2567
2568 if ((curveRect.m_width) <= 0 || (curveRect.m_height <= 0))
2569 continue;
2570
2571 wxRect2DDouble newRect;
2572 if (!valid_rect)
2573 newRect = curveRect;
2574 else
2575 newRect = rect.CreateUnion(curveRect);
2576
2577 if ( IsFinite(newRect.m_x, wxT("left curve boundary is NaN")) &&
2578 IsFinite(newRect.m_y, wxT("bottom curve boundary is NaN")) &&
2579 IsFinite(newRect.GetRight(), wxT("right curve boundary is NaN")) &&
2580 IsFinite(newRect.GetBottom(), wxT("top curve boundary is NaN")) &&
2581 (newRect.m_width >= 0) && (newRect.m_height >= 0))
2582 {
2583 if (!valid_rect) valid_rect = true;
2584 rect = newRect;
2585 }
2586 }
2587
2588 // maybe just a single point, center it using default size
2589 bool zeroWidth = false, zeroHeight = false;
2590
2591 if (rect.m_width == 0.0)
2592 {
2593 zeroWidth = true;
2594 rect.m_x = m_defaultPlotRect.m_x;
2595 rect.m_width = m_defaultPlotRect.m_width;
2596 }
2597 if (rect.m_height == 0.0)
2598 {
2599 zeroHeight = true;
2600 rect.m_y = m_defaultPlotRect.m_y;
2601 rect.m_height = m_defaultPlotRect.m_height;
2602 }
2603
2604 m_curveBoundingRect = rect;
2605
2606 // add some padding so the edge points can be seen
2607 double w = (!zeroWidth) ? rect.m_width/50.0 : 0.0;
2608 double h = (!zeroHeight) ? rect.m_height/50.0 : 0.0;
2609 m_curveBoundingRect.Inset(-w, -h, -w, -h);
2610 }
2611 else
2612 m_curveBoundingRect = m_defaultPlotRect;
2613
2614 AdjustScrollBars();
2615 }
2616
Redraw(int type)2617 void wxPlotCtrl::Redraw(int type)
2618 {
2619 if (m_batch_count) return;
2620
2621 if (type & wxPLOT_REDRAW_XAXIS)
2622 {
2623 m_redraw_type |= wxPLOT_REDRAW_XAXIS;
2624 AutoCalcXAxisTicks();
2625 if (m_correct_ticks == true)
2626 CorrectXAxisTicks();
2627 CalcXAxisTickPositions();
2628 }
2629 if (type & wxPLOT_REDRAW_YAXIS)
2630 {
2631 m_redraw_type |= wxPLOT_REDRAW_YAXIS;
2632 AutoCalcYAxisTicks();
2633 if (m_correct_ticks == true)
2634 CorrectYAxisTicks();
2635 CalcYAxisTickPositions();
2636 }
2637
2638 if (type & wxPLOT_REDRAW_PLOT)
2639 {
2640 m_redraw_type |= wxPLOT_REDRAW_PLOT;
2641 m_area->Refresh(false);
2642 }
2643
2644 if (type & wxPLOT_REDRAW_XAXIS)
2645 m_xAxis->Refresh(false);
2646 if (type & wxPLOT_REDRAW_YAXIS)
2647 m_yAxis->Refresh(false);
2648
2649 if (type & wxPLOT_REDRAW_WINDOW)
2650 Refresh();
2651 }
2652
DrawAreaWindow(wxDC * dc,const wxRect & rect)2653 void wxPlotCtrl::DrawAreaWindow( wxDC *dc, const wxRect &rect )
2654 {
2655 wxCHECK_RET(dc, wxT("invalid dc"));
2656
2657 // GTK doesn't like invalid parameters
2658 wxRect refreshRect = rect;
2659 wxRect clientRect(GetPlotAreaRect());
2660 refreshRect.Intersect(clientRect);
2661
2662 if ((refreshRect.width == 0) || (refreshRect.height == 0)) return;
2663
2664 dc->SetClippingRegion(refreshRect);
2665
2666 dc->SetBrush( wxBrush(GetBackgroundColour(), wxSOLID) );
2667 dc->SetPen( wxPen(GetBorderColour(), m_area_border_width, wxSOLID) );
2668 dc->DrawRectangle(clientRect);
2669
2670 DrawTickMarks( dc, refreshRect );
2671 DrawMarkers( dc, refreshRect );
2672
2673 dc->DestroyClippingRegion();
2674
2675 int i;
2676 wxPlotCurve *curve;
2677 wxPlotCurve *activeCurve = GetActiveCurve();
2678 for(i=0; i<GetCurveCount(); i++)
2679 {
2680 curve = GetCurve(i);
2681
2682 if (curve != activeCurve)
2683 {
2684 if (wxDynamicCast(curve, wxPlotData))
2685 DrawDataCurve( dc, wxDynamicCast(curve, wxPlotData), i, refreshRect );
2686 else
2687 DrawCurve( dc, curve, i, refreshRect );
2688 }
2689 }
2690 // active curve is drawn on top
2691 if (activeCurve)
2692 {
2693 if (wxDynamicCast(activeCurve, wxPlotData))
2694 DrawDataCurve( dc, wxDynamicCast(activeCurve, wxPlotData), GetActiveIndex(), refreshRect );
2695 else
2696 DrawCurve( dc, activeCurve, GetActiveIndex(), refreshRect );
2697 }
2698
2699 DrawCurveCursor( dc );
2700 DrawKey( dc );
2701
2702 /*
2703 int count = m_owner->m_plotMarkers.GetCount();
2704 mdc.SetPen( *wxRED_PEN );
2705 for (i=0; i<count; i++)
2706 {
2707 int x = m_owner->GetClientCoordFromPlotX( m_owner->m_plotMarkers.Item(i).m_x );
2708 int y = m_owner->GetClientCoordFromPlotY( m_owner->m_plotMarkers.Item(i).m_y );
2709 if (m_owner->GetPlotAreaRect().Inside(x, y))
2710 {
2711 mdc.DrawLine( x-10, y, x+10, y );
2712 mdc.DrawLine( x, y-10, x, y+10 );
2713 }
2714 }
2715 */
2716 // refresh border
2717 dc->SetBrush( *wxTRANSPARENT_BRUSH );
2718 dc->SetPen( wxPen(GetBorderColour(), m_area_border_width, wxSOLID) );
2719 dc->DrawRectangle(clientRect);
2720
2721 dc->SetPen( wxNullPen );
2722 dc->SetBrush( wxNullBrush );
2723 }
2724
DrawMouseMarker(wxDC * dc,int type,const wxRect & rect)2725 void wxPlotCtrl::DrawMouseMarker( wxDC *dc, int type, const wxRect &rect )
2726 {
2727 wxCHECK_RET(dc, wxT("invalid window"));
2728
2729 if ((rect.width == 0) || (rect.height == 0))
2730 return;
2731
2732 #if wxCHECK_VERSION(2, 9, 0)
2733 wxRasterOperationMode logical_fn = dc->GetLogicalFunction();
2734 #else
2735 int logical_fn = dc->GetLogicalFunction();
2736 #endif /* wxCHECK_VERSION */
2737 dc->SetLogicalFunction( wxINVERT );
2738 dc->SetBrush( *wxTRANSPARENT_BRUSH );
2739 dc->SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 1, wxDOT));
2740
2741 switch (type)
2742 {
2743 case wxPLOT_MARKER_NONE : break;
2744
2745 case wxPLOT_MARKER_RECT :
2746 {
2747 // rects are drawn to width and height - 1, doesn't line up w/ cursor, who cares?
2748 dc->DrawRectangle( rect.x, rect.y, rect.width, rect.height );
2749 break;
2750 }
2751 case wxPLOT_MARKER_VERT :
2752 {
2753 if (rect.width != 0)
2754 {
2755 int height = GetClientSize().y;
2756 dc->DrawLine(rect.x, 1, rect.x, height-2);
2757 dc->DrawLine(rect.GetRight()+1, 1, rect.GetRight()+1, height-2);
2758 }
2759 break;
2760 }
2761 case wxPLOT_MARKER_HORIZ :
2762 {
2763 if (rect.height != 0)
2764 {
2765 int width = GetClientSize().x;
2766 dc->DrawLine(1, rect.y, width-2, rect.y);
2767 dc->DrawLine(1, rect.GetBottom()+1, width-2, rect.GetBottom()+1);
2768 }
2769 break;
2770 }
2771 default : break;
2772 }
2773
2774 dc->SetBrush( wxNullBrush );
2775 dc->SetPen( wxNullPen );
2776 dc->SetLogicalFunction( logical_fn );
2777 }
2778
DrawCrosshairCursor(wxDC * dc,const wxPoint & pos)2779 void wxPlotCtrl::DrawCrosshairCursor( wxDC *dc, const wxPoint &pos )
2780 {
2781 wxCHECK_RET(dc, wxT("invalid window"));
2782
2783 dc->SetPen(*wxBLACK_PEN);
2784 #if wxCHECK_VERSION(2, 9, 0)
2785 wxRasterOperationMode logical_fn = dc->GetLogicalFunction();
2786 #else
2787 int logical_fn = dc->GetLogicalFunction();
2788 #endif /* wxCHECK_VERSION */
2789 dc->SetLogicalFunction( wxINVERT );
2790
2791 dc->CrossHair(pos.x, pos.y);
2792
2793 dc->SetPen( wxNullPen );
2794 dc->SetLogicalFunction( logical_fn );
2795 }
2796
DrawDataCurve(wxDC * dc,wxPlotData * curve,int curve_index,const wxRect & rect)2797 void wxPlotCtrl::DrawDataCurve( wxDC *dc, wxPlotData *curve, int curve_index, const wxRect &rect )
2798 {
2799 wxCHECK_RET(dc && m_dataCurveDrawer && curve && curve->Ok(), wxT("invalid curve"));
2800
2801 m_dataCurveDrawer->SetDCRect(rect);
2802 m_dataCurveDrawer->SetPlotViewRect(m_viewRect);
2803 m_dataCurveDrawer->Draw(dc, curve, curve_index);
2804 }
2805
DrawCurve(wxDC * dc,wxPlotCurve * curve,int curve_index,const wxRect & rect)2806 void wxPlotCtrl::DrawCurve( wxDC *dc, wxPlotCurve *curve, int curve_index, const wxRect &rect )
2807 {
2808 wxCHECK_RET(dc && m_curveDrawer && curve && curve->Ok(), wxT("invalid curve"));
2809
2810 m_curveDrawer->SetDCRect(rect);
2811 m_curveDrawer->SetPlotViewRect(m_viewRect);
2812 m_curveDrawer->Draw(dc, curve, curve_index);
2813 }
2814
RedrawDataCurve(int index,int min_index,int max_index)2815 void wxPlotCtrl::RedrawDataCurve(int index, int min_index, int max_index)
2816 {
2817 if (m_batch_count) return;
2818
2819 wxCHECK_RET((index>=0)&&(index<(int)m_curves.GetCount()), wxT("invalid curve index"));
2820
2821 wxPlotData *plotData = GetDataCurve(index);
2822 wxCHECK_RET(plotData, wxT("not a data curve"));
2823
2824 int count = plotData->GetCount();
2825 wxCHECK_RET((min_index<=max_index)&&(min_index>=0)&&(max_index>=0)&&(min_index<count)&&(max_index<count), wxT("invalid data index"));
2826
2827 wxRect rect(m_areaClientRect);
2828 int cursor_size = GetCursorSize();
2829
2830 if (plotData->GetIsXOrdered())
2831 {
2832 double x = plotData->GetXValue(wxMax(min_index-1, 0));
2833
2834 if (x > m_viewRect.GetRight())
2835 return;
2836 else if (x < m_viewRect.m_x)
2837 rect.x = 0;
2838 else
2839 rect.x = GetClientCoordFromPlotX(x) - cursor_size/2 - 1;
2840
2841 x = plotData->GetXValue(wxMin(max_index+1, (int)plotData->GetCount()-1));
2842
2843 if (x < m_viewRect.m_x)
2844 return;
2845 else if (x > m_viewRect.GetRight())
2846 rect.SetRight(m_areaClientRect.width);
2847 else
2848 rect.SetRight(GetClientCoordFromPlotX(x) + cursor_size/2 + 1);
2849
2850 rect.Intersect(m_areaClientRect);
2851 }
2852
2853 wxMemoryDC mdc;
2854 mdc.SelectObject( m_area->m_bitmap );
2855 DrawDataCurve( &mdc, plotData, index, rect );
2856 DrawCurveCursor(&mdc);
2857 wxClientDC dc(m_area);
2858 dc.Blit(rect.x, rect.y, rect.width, rect.height, &mdc, rect.x, rect.y);
2859 mdc.SelectObject( wxNullBitmap );
2860 }
2861
RedrawCurve(int index,double min_x,double max_x)2862 void wxPlotCtrl::RedrawCurve(int index, double min_x, double max_x)
2863 {
2864 if (m_batch_count) return;
2865
2866 wxCHECK_RET((min_x<=max_x)&&(index>=0)&&(index<(int)m_curves.GetCount()), wxT("invalid curve index"));
2867 wxCHECK_RET(!GetDataCurve(index), wxT("invalid curve"));
2868 wxRect rect(m_areaClientRect);
2869
2870 if (min_x > m_viewRect.GetRight())
2871 return;
2872 if (min_x < m_viewRect.m_x)
2873 min_x = m_viewRect.m_x;
2874
2875 rect.x = GetClientCoordFromPlotX(min_x);
2876
2877 if (max_x < m_viewRect.m_x)
2878 return;
2879 if (max_x > m_viewRect.GetRight())
2880 max_x = m_viewRect.GetRight();
2881
2882 rect.width = GetClientCoordFromPlotX(max_x) - rect.x;
2883
2884 if (rect.width < 1) return;
2885
2886 wxMemoryDC mdc;
2887 mdc.SelectObject( m_area->m_bitmap );
2888 DrawCurve(&mdc, GetCurve(index), index, rect);
2889 DrawCurveCursor(&mdc);
2890 wxClientDC dc(m_area);
2891 dc.Blit(rect.x, rect.y, rect.width, rect.height, &mdc, rect.x, rect.y);
2892 mdc.SelectObject( wxNullBitmap );
2893 }
2894
DrawKey(wxDC * dc)2895 void wxPlotCtrl::DrawKey( wxDC *dc )
2896 {
2897 wxCHECK_RET(dc && m_keyDrawer, wxT("invalid window"));
2898 if (!GetShowKey() || m_keyString.IsEmpty())
2899 return;
2900
2901 wxRect dcRect(wxPoint(0, 0), GetPlotAreaRect().GetSize());
2902 m_keyDrawer->SetDCRect(dcRect);
2903 m_keyDrawer->SetPlotViewRect(m_viewRect);
2904 m_keyDrawer->Draw(dc, m_keyString);
2905 }
2906
DrawCurveCursor(wxDC * dc)2907 void wxPlotCtrl::DrawCurveCursor( wxDC *dc )
2908 {
2909 wxCHECK_RET(dc, wxT("invalid window"));
2910 if (!IsCursorValid()) return;
2911
2912 m_markerDrawer->SetPlotViewRect(m_viewRect);
2913 m_markerDrawer->SetDCRect(wxRect(wxPoint(0,0), m_area->GetClientSize()));
2914 m_markerDrawer->Draw(dc, m_cursorMarker);
2915
2916 /*
2917 wxPoint2DDouble cursor = GetCursorPoint();
2918
2919
2920 if ((GetCursorSize() > 0) &&
2921 wxPlotRect2DDoubleContains(cursor.m_x, cursor.m_y, m_viewRect))
2922 {
2923 dc->SetPen( wxPen(GetCursorColour(), 1, wxSOLID) );
2924 int i = GetClientCoordFromPlotX(cursor.m_x);
2925 int j = GetClientCoordFromPlotY(cursor.m_y);
2926 dc->DrawCircle(i, j, m_cursor_size);
2927 if (m_cursor_size > 2)
2928 {
2929 dc->DrawLine(i - (m_cursor_size - 1), j, i + (m_cursor_size - 1), j);
2930 dc->DrawLine(i, j - (m_cursor_size - 1), i, j + (m_cursor_size - 1));
2931 }
2932 }
2933 */
2934 }
2935
DrawTickMarks(wxDC * dc,const wxRect & rect)2936 void wxPlotCtrl::DrawTickMarks( wxDC *dc, const wxRect& rect )
2937 {
2938 wxRect clientRect(GetPlotAreaRect());
2939 dc->SetPen( wxPen(GetGridColour(), 1, wxSOLID) );
2940
2941 int xtick_length = GetDrawGrid() ? clientRect.height : 10;
2942 int ytick_length = GetDrawGrid() ? clientRect.width : 10;
2943
2944 int tick_pos, i;
2945 // X-axis ticks
2946 int tick_count = m_xAxisTicks.GetCount();
2947 for (i=0; i<tick_count; i++)
2948 {
2949 tick_pos = m_xAxisTicks[i];
2950 if (tick_pos < rect.x)
2951 continue;
2952 else if (tick_pos > rect.GetRight())
2953 break;
2954
2955 dc->DrawLine(tick_pos, clientRect.height, tick_pos, clientRect.height - xtick_length);
2956 }
2957
2958 // Y-axis ticks
2959 tick_count = m_yAxisTicks.GetCount();
2960 for (i=0; i<tick_count; i++)
2961 {
2962 tick_pos = m_yAxisTicks[i];
2963 if (tick_pos < rect.y)
2964 break;
2965 else if (tick_pos > rect.GetBottom())
2966 continue;
2967
2968 dc->DrawLine(0, tick_pos, ytick_length, tick_pos);
2969 }
2970 }
2971
DrawMarkers(wxDC * dc,const wxRect & rect)2972 void wxPlotCtrl::DrawMarkers( wxDC *dc, const wxRect& rect )
2973 {
2974 wxCHECK_RET(m_markerDrawer, wxT("Invalid marker drawer"));
2975 m_markerDrawer->SetPlotViewRect(m_viewRect);
2976 m_markerDrawer->SetDCRect(rect);
2977 m_markerDrawer->Draw(dc, m_plotMarkers);
2978 }
2979
DrawXAxis(wxDC * dc,bool refresh)2980 void wxPlotCtrl::DrawXAxis( wxDC *dc, bool refresh )
2981 {
2982 wxCHECK_RET(m_xAxisDrawer, wxT("Invalid x axis drawer"));
2983
2984 m_xAxisDrawer->SetTickPositions( m_xAxisTicks );
2985 m_xAxisDrawer->SetTickLabels( m_xAxisTickLabels );
2986 m_xAxisDrawer->SetPlotViewRect(m_viewRect);
2987 wxSize clientSize = m_xAxisRect.GetSize();
2988 m_xAxisDrawer->SetDCRect(wxRect(wxPoint(0,0),clientSize));
2989 m_xAxisDrawer->Draw(dc, refresh);
2990 }
2991
DrawYAxis(wxDC * dc,bool refresh)2992 void wxPlotCtrl::DrawYAxis( wxDC *dc, bool refresh )
2993 {
2994 wxCHECK_RET(m_yAxisDrawer, wxT("Invalid y axis drawer"));
2995
2996 m_yAxisDrawer->SetTickPositions( m_yAxisTicks );
2997 m_yAxisDrawer->SetTickLabels( m_yAxisTickLabels );
2998 m_yAxisDrawer->SetPlotViewRect(m_viewRect);
2999 wxSize clientSize = m_yAxisRect.GetSize();
3000 m_yAxisDrawer->SetDCRect(wxRect(wxPoint(0,0),clientSize));
3001 m_yAxisDrawer->Draw(dc, refresh);
3002 }
3003
ScaleRect(const wxRect & rect,double x_scale,double y_scale)3004 wxRect ScaleRect(const wxRect& rect, double x_scale, double y_scale)
3005 {
3006 return wxRect( int(rect.x*x_scale+0.5), int(rect.y*y_scale+0.5),
3007 int(rect.width*x_scale+0.5), int(rect.height*y_scale+0.5) );
3008 }
3009
DrawWholePlot(wxDC * dc,const wxRect & boundingRect,int dpi)3010 void wxPlotCtrl::DrawWholePlot( wxDC *dc, const wxRect &boundingRect, int dpi )
3011 {
3012 wxCHECK_RET(dc, wxT("invalid dc"));
3013 wxCHECK_RET(dpi > 0, wxT("Invalid dpi for plot drawing"));
3014
3015 wxSize clientSize = GetClientSize();
3016 int sb_width = m_yAxisScrollbar->GetSize().GetWidth();
3017 clientSize.x -= sb_width;
3018 clientSize.y -= sb_width;
3019
3020 //set font scale so 1pt = 1pixel at 72dpi
3021 double fontScale = (double)dpi / 72.0;
3022 //one pixel wide line equals (m_pen_print_width) millimeters wide
3023 double penScale = (m_pen_print_width / 25.4) * dpi;
3024
3025 //save old values
3026 wxFont oldAxisLabelFont = GetAxisLabelFont();
3027 wxFont oldPlotTitleFont = GetPlotTitleFont();
3028 int old_area_border_width = m_area_border_width;
3029 int old_border = m_border;
3030 int old_cursor_size = m_cursorMarker.GetSize().x;
3031 wxPoint2DDouble old_zoom = m_zoom;
3032 wxRect2DDouble old_view = m_viewRect;
3033
3034 //resize border and border pen
3035 m_area_border_width = RINT(m_area_border_width * penScale);
3036 m_border = RINT(m_border * penScale);
3037
3038 //resize the curve cursor
3039 m_cursorMarker.SetSize(wxSize(int(old_cursor_size * penScale), int(old_cursor_size * penScale)));
3040
3041 //resize the fonts
3042 wxFont axisLabelFont = GetAxisLabelFont();
3043 axisLabelFont.SetPointSize( wxMax(2, RINT(axisLabelFont.GetPointSize() * fontScale)) );
3044 SetAxisLabelFont( axisLabelFont );
3045
3046 wxFont plotTitleFont = GetPlotTitleFont();
3047 plotTitleFont.SetPointSize( wxMax(2, RINT(plotTitleFont.GetPointSize() * fontScale)) );
3048 SetPlotTitleFont( plotTitleFont );
3049
3050 //reload the original zoom and view rect in case it was changed by any of the font changes
3051 m_zoom = old_zoom;
3052 m_viewRect = old_view;
3053
3054 //resize all window component rects to the bounding rect
3055 DoSize( boundingRect );
3056 //AutoCalcTicks(); // don't reset ticks since it might not be WYSIWYG
3057
3058 //draw all components to the provided dc
3059 dc->SetDeviceOrigin(long(boundingRect.x+m_xAxisRect.GetLeft()),
3060 long(boundingRect.y+m_xAxisRect.GetTop()));
3061 CalcXAxisTickPositions();
3062 DrawXAxis(dc, false);
3063
3064 dc->SetDeviceOrigin(long(boundingRect.x+m_yAxisRect.GetLeft()),
3065 long(boundingRect.y+m_yAxisRect.GetTop()));
3066 CalcYAxisTickPositions();
3067 DrawYAxis(dc, false);
3068
3069 dc->SetDeviceOrigin(long(boundingRect.x+m_areaRect.GetLeft()),
3070 long(boundingRect.y+m_areaRect.GetTop()));
3071 DrawAreaWindow(dc, m_areaClientRect);
3072
3073 dc->SetDeviceOrigin(boundingRect.x, boundingRect.y);
3074 DrawPlotCtrl(dc);
3075
3076 //restore old values
3077
3078 m_area_border_width = old_area_border_width;
3079 m_border = old_border;
3080 m_cursorMarker.SetSize(wxSize(old_cursor_size, old_cursor_size));
3081
3082 SetAxisLabelFont( oldAxisLabelFont );
3083 SetPlotTitleFont( oldPlotTitleFont );
3084 m_zoom = old_zoom;
3085 m_viewRect = old_view;
3086
3087 //update to window instead of printer
3088 UpdateWindowSize();
3089 Redraw(wxPLOT_REDRAW_WHOLEPLOT); // recalc ticks for this window
3090 }
3091
3092 // ----------------------------------------------------------------------------
3093 // Axis tick calculations
3094 // ----------------------------------------------------------------------------
3095
DoAutoCalcTicks(bool x_axis)3096 void wxPlotCtrl::DoAutoCalcTicks(bool x_axis)
3097 {
3098 double start = 0.0, end = 1.0;
3099 int i, n, window = 100;
3100
3101 double *tick_step = NULL;
3102 int *tick_count = NULL;
3103 wxString *tickFormat = NULL;
3104
3105 if (x_axis)
3106 {
3107 tick_step = &m_xAxisTick_step;
3108 tick_count = &m_xAxisTick_count;
3109 tickFormat = &m_xAxisTickFormat;
3110
3111 window = GetPlotAreaRect().width;
3112 m_xAxisTicks.Clear(); // kill it in case something goes wrong
3113 start = m_viewRect.GetLeft();
3114 end = m_viewRect.GetRight();
3115 *tick_count = window/(m_axisFontSize.x*10);
3116 }
3117 else
3118 {
3119 tick_step = &m_yAxisTick_step;
3120 tick_count = &m_yAxisTick_count;
3121 tickFormat = &m_yAxisTickFormat;
3122
3123 window = GetPlotAreaRect().height;
3124 m_yAxisTicks.Clear();
3125 start = m_viewRect.GetTop();
3126 end = m_viewRect.GetBottom();
3127 double tick_count_scale = window/(m_axisFontSize.y*2.0) > 2.0 ? 2.0 : 1.5;
3128 *tick_count = int(window/(m_axisFontSize.y*tick_count_scale) + 0.5);
3129 }
3130
3131 if (window < 5) return; // FIXME
3132
3133 if (!IsFinite(start, wxT("axis range is not finite")) ||
3134 !IsFinite(end, wxT("axis range is not finite")) )
3135 {
3136 *tick_count = 0;
3137 return;
3138 }
3139
3140 double range = end - start;
3141 double max = fabs(start) > fabs(end) ? fabs(start) : fabs(end);
3142 double min = fabs(start) < fabs(end) ? fabs(start) : fabs(end);
3143 bool exponential = (min >= m_min_exponential) || (max < 1.0/m_min_exponential) ? true : false;
3144 int places = exponential ? 1 : int(floor(fabs(log10(max))));
3145
3146 if (!IsFinite(range, wxT("axis range is not finite")) ||
3147 !IsFinite(min, wxT("axis range is not finite")) ||
3148 !IsFinite(max, wxT("axis range is not finite")) )
3149 {
3150 *tick_count = 0;
3151 return;
3152 }
3153
3154 *tick_step = 1.0;
3155 int int_log_range = int( log10(range) );
3156 if (int_log_range > 0) { for (i=0; i < int_log_range; i++) (*tick_step) *= 10; }
3157 else if (int_log_range < 0) { for (i=0; i < -int_log_range; i++) (*tick_step) /= 10; }
3158
3159 double stepsizes[TIC_STEPS] = { .1, .2, .5 };
3160 double step10 = (*tick_step) / 10.0;
3161 int sigFigs = 0;
3162 int digits = 0;
3163
3164 for (n=0; n<4; n++)
3165 {
3166 for (i=0; i<TIC_STEPS; i++)
3167 {
3168 *tick_step = step10 * stepsizes[i];
3169
3170 if (exponential)
3171 sigFigs = labs(int(log10(max)) - int(log10(*tick_step)));
3172 else
3173 sigFigs = (*tick_step) >= 1.0 ? 0 : int(ceil(-log10(*tick_step)));
3174
3175 if (x_axis)
3176 {
3177 digits = 1 + places + (sigFigs > 0 ? 1+sigFigs : 0) + (exponential ? 4 : 0);
3178 *tick_count = int(double(window)/double((digits+3)*m_axisFontSize.x) + 0.5);
3179 }
3180
3181 if ((range/(*tick_step)) <= (*tick_count)) break;
3182 }
3183 if ((range/(*tick_step)) <= (*tick_count)) break;
3184 step10 *= 10.0;
3185 }
3186
3187 //if (!x_axis) wxPrintf(wxT("Ticks %d %lf, %d\n"), n, *tick_step, *tick_count);
3188
3189 if (sigFigs > 9) sigFigs = 9; // FIXME
3190
3191 if (exponential) tickFormat->Printf( wxT("%%.%dle"), sigFigs );
3192 else tickFormat->Printf( wxT("%%.%dlf"), sigFigs );
3193
3194 *tick_count = int(ceil(range/(*tick_step))) + 1;
3195
3196 // note : first_tick = ceil(start / tick_step) * tick_step;
3197 }
3198
CorrectXAxisTicks()3199 void wxPlotCtrl::CorrectXAxisTicks()
3200 {
3201 double start = ceil(m_viewRect.GetLeft() / m_xAxisTick_step) * m_xAxisTick_step;
3202 wxString label;
3203 label.Printf( m_xAxisTickFormat.c_str(), start);
3204 if (label.ToDouble( &start ))
3205 {
3206 double x = GetClientCoordFromPlotX( start );
3207 double zoom_x = (GetClientCoordFromPlotX(start+m_xAxisTick_step)-x)/m_xAxisTick_step;
3208 double origin_x = start - x/zoom_x;
3209 BeginBatch();
3210 if (!SetZoom( zoom_x, m_zoom.m_y, origin_x, m_viewRect.GetTop(), true ))
3211 m_xAxisTick_count = 0; // oops
3212
3213 EndBatch(false); // don't draw just block
3214 }
3215 }
CorrectYAxisTicks()3216 void wxPlotCtrl::CorrectYAxisTicks()
3217 {
3218 double start = ceil(m_viewRect.GetTop() / m_yAxisTick_step) * m_yAxisTick_step;
3219 wxString label;
3220 label.Printf( m_yAxisTickFormat.c_str(), start);
3221 if (label.ToDouble( &start ))
3222 {
3223 double y = GetClientCoordFromPlotY( start );
3224 double zoom_y = (y-GetClientCoordFromPlotY(start+m_yAxisTick_step))/m_yAxisTick_step;
3225 double origin_y = start - (GetPlotAreaRect().height - y)/zoom_y;
3226 BeginBatch();
3227 if (!SetZoom( m_zoom.m_x, zoom_y, m_viewRect.GetLeft(), origin_y, true ))
3228 m_yAxisTick_count = 0; // oops
3229
3230 EndBatch(false);
3231 }
3232 }
3233
CalcXAxisTickPositions()3234 void wxPlotCtrl::CalcXAxisTickPositions()
3235 {
3236 double current = ceil(m_viewRect.GetLeft() / m_xAxisTick_step) * m_xAxisTick_step;
3237 m_xAxisTicks.Clear();
3238 m_xAxisTickLabels.Clear();
3239 int i, x, windowWidth = GetPlotAreaRect().width;
3240 for (i=0; i<m_xAxisTick_count; i++)
3241 {
3242 if (!IsFinite(current, wxT("axis label is not finite"))) return;
3243
3244 x = GetClientCoordFromPlotX( current );
3245
3246 if ((x >= -1) && (x < windowWidth+2))
3247 {
3248 m_xAxisTicks.Add(x);
3249 m_xAxisTickLabels.Add(wxString::Format(m_xAxisTickFormat.c_str(), current));
3250 }
3251
3252 current += m_xAxisTick_step;
3253 }
3254 }
CalcYAxisTickPositions()3255 void wxPlotCtrl::CalcYAxisTickPositions()
3256 {
3257 double current = ceil(m_viewRect.GetTop() / m_yAxisTick_step) * m_yAxisTick_step;
3258 m_yAxisTicks.Clear();
3259 m_yAxisTickLabels.Clear();
3260 int i, y, windowWidth = GetPlotAreaRect().height;
3261 for (i=0; i<m_yAxisTick_count; i++)
3262 {
3263 if (!IsFinite(current, wxT("axis label is not finite")))
3264 return;
3265
3266 y = GetClientCoordFromPlotY( current );
3267
3268 if ((y >= -1) && (y < windowWidth+2))
3269 {
3270 m_yAxisTicks.Add(y);
3271 m_yAxisTickLabels.Add(wxString::Format(m_yAxisTickFormat.c_str(), current));
3272 }
3273
3274 current += m_yAxisTick_step;
3275 }
3276 }
3277
3278 // ----------------------------------------------------------------------------
3279 // Event processing
3280 // ----------------------------------------------------------------------------
3281
ProcessAreaEVT_MOUSE_EVENTS(wxMouseEvent & event)3282 void wxPlotCtrl::ProcessAreaEVT_MOUSE_EVENTS( wxMouseEvent &event )
3283 {
3284 wxPoint& m_mousePt = m_area->m_mousePt;
3285 wxRect& m_mouseRect = m_area->m_mouseRect;
3286
3287 wxPoint lastMousePt = m_mousePt;
3288 m_mousePt = event.GetPosition();
3289
3290 if (event.ButtonDown() && IsTextCtrlShown())
3291 {
3292 HideTextCtrl(true, true);
3293 return;
3294 }
3295
3296 if (GetGreedyFocus() && (FindFocus() != m_area))
3297 m_area->SetFocus();
3298
3299 double plotX = GetPlotCoordFromClientX(m_mousePt.x),
3300 plotY = GetPlotCoordFromClientY(m_mousePt.y);
3301
3302 wxClientDC dc(m_area);
3303
3304 // Mouse motion
3305 if (lastMousePt != m_area->m_mousePt)
3306 {
3307 wxPlotEvent evt_motion(wxEVT_PLOT_MOUSE_MOTION, GetId(), this);
3308 evt_motion.SetPosition( plotX, plotY );
3309 (void)DoSendEvent( evt_motion );
3310
3311 // Draw the crosshair cursor
3312 if (GetCrossHairCursor())
3313 {
3314 if (!event.Entering() || m_area->HasCapture())
3315 DrawCrosshairCursor( &dc, lastMousePt );
3316 if (!event.Leaving() || m_area->HasCapture())
3317 DrawCrosshairCursor( &dc, m_mousePt );
3318 }
3319 }
3320
3321 // Wheel scrolling up and down
3322 if (event.GetWheelRotation() != 0)
3323 {
3324 double dir = event.GetWheelRotation() > 0 ? 0.25 : -0.25;
3325 SetOrigin( m_viewRect.GetLeft(),
3326 m_viewRect.GetTop() + dir*m_viewRect.m_height, true);
3327 }
3328
3329 int active_index = GetActiveIndex();
3330
3331 // Initial Left down selection
3332 if (event.LeftDown() || event.LeftDClick())
3333 {
3334 if (FindFocus() != m_area) // fixme MSW focus problems
3335 m_area->SetFocus();
3336
3337 if (m_area_mouse_cursorid == wxCURSOR_HAND)
3338 SetAreaMouseCursor(CURSOR_GRAB);
3339
3340 // send a click or doubleclick event
3341 wxPlotEvent click_event( event.ButtonDClick() ? wxEVT_PLOT_DOUBLECLICKED : wxEVT_PLOT_CLICKED,
3342 GetId(), this );
3343 click_event.SetPosition( plotX, plotY );
3344 (void)DoSendEvent( click_event );
3345
3346 if (!event.ButtonDClick())
3347 m_mouseRect = wxRect(m_mousePt, wxSize(0, 0));
3348
3349 int data_index = -1;
3350 int curve_index = -1;
3351
3352 wxPoint2DDouble dpt(2.0/m_zoom.m_x, 2.0/m_zoom.m_y);
3353 wxPoint2DDouble curvePt;
3354
3355 if (FindCurve(wxPoint2DDouble(plotX, plotY), dpt, curve_index, data_index, &curvePt))
3356 {
3357 wxPlotCurve *plotCurve = GetCurve(curve_index);
3358 wxPlotData *plotData = wxDynamicCast(plotCurve, wxPlotData);
3359
3360 if (plotCurve)
3361 {
3362 wxPlotEvent pt_click_event( event.ButtonDClick() ? wxEVT_PLOT_POINT_DOUBLECLICKED : wxEVT_PLOT_POINT_CLICKED,
3363 GetId(), this );
3364 pt_click_event.SetPosition( curvePt.m_x, curvePt.m_y );
3365 pt_click_event.SetCurve( plotCurve, curve_index );
3366 pt_click_event.SetCurveDataIndex(data_index);
3367 (void)DoSendEvent( pt_click_event );
3368
3369 // send curve selection switched event
3370 if (curve_index != GetActiveIndex())
3371 SetActiveIndex( curve_index, true );
3372
3373 if (!event.LeftDClick() && (m_area_mouse_func == wxPLOT_MOUSE_SELECT))
3374 {
3375 if (plotData)
3376 SelectDataRange(curve_index, wxRangeInt(data_index,data_index), true);
3377 else
3378 SelectXRange(curve_index, wxRangeDouble(curvePt.m_x,curvePt.m_x), true);
3379 }
3380 else if (!event.LeftDClick() && (m_area_mouse_func == wxPLOT_MOUSE_DESELECT))
3381 {
3382 if (plotData)
3383 DeselectDataRange(curve_index, wxRangeInt(data_index,data_index), true);
3384 else
3385 DeselectXRange(curve_index, wxRangeDouble(curvePt.m_x,curvePt.m_x), true);
3386 }
3387 else
3388 {
3389 if (plotData)
3390 SetCursorDataIndex(curve_index, data_index, true);
3391 else
3392 SetCursorXPoint(curve_index, curvePt.m_x, true);
3393 }
3394
3395 return;
3396 }
3397 }
3398 }
3399 // Finished marking rectangle or scrolling, perhaps
3400 else if (event.LeftUp())
3401 {
3402 SetCaptureWindow(NULL);
3403
3404 if (m_area_mouse_cursorid == CURSOR_GRAB)
3405 SetAreaMouseCursor(wxCURSOR_HAND);
3406
3407 StopMouseTimer();
3408
3409 if (m_mouseRect == wxRect(0,0,0,0))
3410 return;
3411
3412 wxRect rightedRect = m_mouseRect;
3413
3414 // rightedRect always goes from upper-left to lower-right
3415 // don't fix m_mouseRect since redrawing will be off
3416 if (rightedRect.width < 0)
3417 {
3418 rightedRect.x += rightedRect.width;
3419 rightedRect.width = -rightedRect.width;
3420 }
3421 if (rightedRect.height < 0)
3422 {
3423 rightedRect.y += rightedRect.height;
3424 rightedRect.height = -rightedRect.height;
3425 }
3426
3427 // Zoom into image
3428 if (m_area_mouse_func == wxPLOT_MOUSE_ZOOM)
3429 {
3430 if ((m_area_mouse_marker == wxPLOT_MARKER_RECT) &&
3431 ((rightedRect.width > 10) && (rightedRect.height > 10)))
3432 SetZoom( rightedRect, true );
3433 else if ((m_area_mouse_marker == wxPLOT_MARKER_VERT) &&
3434 (rightedRect.width > 10) )
3435 SetZoom( wxRect(rightedRect.x, 0, rightedRect.width, m_areaClientRect.height), true );
3436 else if ((m_area_mouse_marker == wxPLOT_MARKER_HORIZ) &&
3437 (rightedRect.height > 10) )
3438 SetZoom( wxRect(0, rightedRect.y, m_areaClientRect.width, rightedRect.height ), true );
3439 else
3440 DrawMouseMarker( &dc, m_area_mouse_marker, m_mouseRect );
3441 }
3442 // Select a range of points
3443 else if ((m_area_mouse_func == wxPLOT_MOUSE_SELECT) && (active_index >= 0))
3444 {
3445 BeginBatch(); // if you select nothing, you don't get a refresh
3446
3447 wxRect2DDouble plotRect = GetPlotRectFromClientRect(rightedRect);
3448
3449 if ((m_area_mouse_marker == wxPLOT_MARKER_VERT) && (plotRect.m_width > 0))
3450 SelectXRange(active_index, wxRangeDouble(plotRect.m_x, plotRect.GetRight()), true );
3451 else if ((m_area_mouse_marker == wxPLOT_MARKER_HORIZ) && (plotRect.m_height > 0))
3452 SelectYRange(active_index, wxRangeDouble(plotRect.m_y, plotRect.GetBottom()), true );
3453 else if ((plotRect.m_width > 0) || (plotRect.m_height > 0))
3454 SelectRectangle(active_index, plotRect, true );
3455
3456 m_mouseRect = wxRect(0,0,0,0);
3457 EndBatch();
3458 }
3459 // Deselect a range of points
3460 else if ((m_area_mouse_func == wxPLOT_MOUSE_DESELECT) && (active_index >= 0))
3461 {
3462 BeginBatch();
3463
3464 wxRect2DDouble plotRect = GetPlotRectFromClientRect(rightedRect);
3465
3466 if ((m_area_mouse_marker == wxPLOT_MARKER_VERT) && (plotRect.m_width > 0))
3467 DeselectXRange(active_index, wxRangeDouble(plotRect.m_x, plotRect.GetRight()), true );
3468 else if ((m_area_mouse_marker == wxPLOT_MARKER_HORIZ) && (plotRect.m_height > 0))
3469 DeselectYRange(active_index, wxRangeDouble(plotRect.m_y, plotRect.GetBottom()), true );
3470 else if ((plotRect.m_width > 0) || (plotRect.m_height > 0))
3471 DeselectRectangle(active_index, plotRect, true );
3472
3473 m_mouseRect = wxRect(0,0,0,0);
3474 EndBatch();
3475 }
3476 // Nothing to do - erase the rect
3477 else
3478 {
3479 DrawMouseMarker(&dc, m_area_mouse_marker, m_mouseRect);
3480 }
3481
3482 m_mouseRect = wxRect(0,0,0,0);
3483 return;
3484 }
3485 // Marking the rectangle or panning around
3486 else if ( event.LeftIsDown() && event.Dragging() )
3487 {
3488 SetCaptureWindow(m_area);
3489
3490 if (m_area_mouse_cursorid == wxCURSOR_HAND)
3491 SetAreaMouseCursor(CURSOR_GRAB);
3492
3493 // Move the origin
3494 if (m_area_mouse_func == wxPLOT_MOUSE_PAN)
3495 {
3496 #if wxCHECK_VERSION(2,7,0)
3497 if (!m_areaClientRect.Contains(event.GetPosition()))
3498 #else
3499 if (!m_areaClientRect.Inside(event.GetPosition()))
3500 #endif
3501 {
3502 StartMouseTimer(ID_AREA_TIMER);
3503 }
3504
3505 m_mouseRect = wxRect(0,0,0,0); // no marker
3506
3507 double dx = m_mousePt.x - lastMousePt.x;
3508 double dy = m_mousePt.y - lastMousePt.y;
3509 SetOrigin(m_viewRect.GetLeft() - dx/m_zoom.m_x,
3510 m_viewRect.GetTop() + dy/m_zoom.m_y, true );
3511 return;
3512 }
3513 else
3514 {
3515 if (m_mouseRect != wxRect(0,0,0,0))
3516 DrawMouseMarker(&dc, m_area_mouse_marker, m_mouseRect);
3517 else
3518 m_mouseRect = wxRect(m_mousePt, wxSize(1, 1));
3519
3520 m_mouseRect.width = m_mousePt.x - m_mouseRect.x;
3521 m_mouseRect.height = m_mousePt.y - m_mouseRect.y;
3522
3523 DrawMouseMarker(&dc, m_area_mouse_marker, m_mouseRect);
3524 }
3525
3526 return;
3527 }
3528 return;
3529 }
3530
ProcessAxisEVT_MOUSE_EVENTS(wxMouseEvent & event)3531 void wxPlotCtrl::ProcessAxisEVT_MOUSE_EVENTS( wxMouseEvent &event )
3532 {
3533 if (event.ButtonDown() && IsTextCtrlShown())
3534 {
3535 HideTextCtrl(true, true);
3536 return;
3537 }
3538
3539 wxPoint pos = event.GetPosition();
3540 wxPlotAxis *axisWin = (wxPlotAxis*)event.GetEventObject();
3541 wxCHECK_RET(axisWin, wxT("Unknown window"));
3542
3543 wxPoint& m_mousePt = axisWin->m_mousePt;
3544
3545 if (event.LeftIsDown() && (axisWin != GetCaptureWindow()))
3546 {
3547 SetCaptureWindow(axisWin);
3548 m_mousePt = pos;
3549 return;
3550 }
3551 else if (!event.LeftIsDown())
3552 {
3553 SetCaptureWindow(NULL);
3554 StopMouseTimer();
3555 }
3556 else if (event.LeftIsDown())
3557 {
3558 wxSize winSize = axisWin->GetSize();
3559
3560 if (( axisWin->IsXAxis() && ((pos.x < 0) || (pos.x > winSize.x))) ||
3561 (!axisWin->IsXAxis() && ((pos.y < 0) || (pos.y > winSize.y))) )
3562 {
3563 m_mousePt = pos;
3564 StartMouseTimer(axisWin->IsXAxis() ? ID_XAXIS_TIMER : ID_YAXIS_TIMER);
3565 }
3566 else if (IsTimerRunning())
3567 m_mousePt = pos;
3568 }
3569
3570 int wheel = event.GetWheelRotation();
3571
3572 if (wheel != 0)
3573 {
3574 wheel = wheel > 0 ? 1 : wheel < 0 ? -1 : 0;
3575 double dx = 0, dy = 0;
3576
3577 if (axisWin->IsXAxis())
3578 dx = wheel*m_viewRect.m_width/4.0;
3579 else
3580 dy = wheel*m_viewRect.m_height/4.0;
3581
3582 SetOrigin(m_viewRect.GetLeft() + dx,
3583 m_viewRect.GetTop() + dy, true);
3584 }
3585
3586 if ((!GetScrollOnThumbRelease() && event.LeftIsDown() && event.Dragging()) ||
3587 (GetScrollOnThumbRelease() && event.LeftUp()))
3588 {
3589 double x = m_viewRect.GetLeft();
3590 double y = m_viewRect.GetTop();
3591
3592 if (axisWin->IsXAxis())
3593 x += (pos.x - m_mousePt.x)/m_zoom.m_x;
3594 else
3595 y += (m_mousePt.y-pos.y)/m_zoom.m_y;
3596
3597 SetOrigin(x, y, true);
3598 }
3599
3600 if (!GetScrollOnThumbRelease())
3601 m_mousePt = pos;
3602 }
3603
ProcessAreaEVT_KEY_DOWN(wxKeyEvent & event)3604 void wxPlotCtrl::ProcessAreaEVT_KEY_DOWN( wxKeyEvent &event )
3605 {
3606 //wxPrintf(wxT("wxPlotCtrl::ProcessAreaEVT_KEY_DOWN %d `%c` S%dC%dA%d\n"), int(event.GetKeyCode()), (wxChar)event.GetKeyCode(), event.ShiftDown(), event.ControlDown(), event.AltDown());
3607 event.Skip(true);
3608
3609 int code = event.GetKeyCode();
3610 bool alt = event.AltDown() || (code == WXK_ALT);
3611 bool ctrl = event.ControlDown() || (code == WXK_CONTROL);
3612 bool shift = event.ShiftDown() || (code == WXK_SHIFT);
3613
3614 if (shift && !alt && !ctrl)
3615 SetAreaMouseFunction(wxPLOT_MOUSE_SELECT, true);
3616 else if (!shift && ctrl && !alt)
3617 SetAreaMouseFunction(wxPLOT_MOUSE_DESELECT, true);
3618 else if (ctrl && shift && alt)
3619 SetAreaMouseFunction(wxPLOT_MOUSE_PAN, true);
3620 else // if (!ctrl || !shift || !alt)
3621 SetAreaMouseFunction(wxPLOT_MOUSE_ZOOM, true);
3622 }
3623
ProcessAreaEVT_KEY_UP(wxKeyEvent & event)3624 void wxPlotCtrl::ProcessAreaEVT_KEY_UP( wxKeyEvent &event )
3625 {
3626 //wxPrintf(wxT("wxPlotCtrl::ProcessAreaEVT_KEY_UP %d `%c` S%dC%dA%d\n"), int(event.GetKeyCode()), (wxChar)event.GetKeyCode(), event.ShiftDown(), event.ControlDown(), event.AltDown());
3627 event.Skip(true);
3628
3629 int code = event.GetKeyCode();
3630 bool alt = event.AltDown() && (code != WXK_ALT);
3631 bool ctrl = event.ControlDown() && (code != WXK_CONTROL);
3632 bool shift = event.ShiftDown() && (code != WXK_SHIFT);
3633
3634 if (shift && !ctrl && !alt)
3635 SetAreaMouseFunction(wxPLOT_MOUSE_SELECT, true);
3636 else if (!shift && ctrl && !alt)
3637 SetAreaMouseFunction(wxPLOT_MOUSE_DESELECT, true);
3638 else if (shift && ctrl && alt)
3639 SetAreaMouseFunction(wxPLOT_MOUSE_PAN, true);
3640 else // if (!shift && !ctrl && !alt)
3641 SetAreaMouseFunction(wxPLOT_MOUSE_ZOOM, true);
3642 }
3643
OnChar(wxKeyEvent & event)3644 void wxPlotCtrl::OnChar(wxKeyEvent &event)
3645 {
3646 //wxPrintf(wxT("wxPlotCtrl::OnChar %d `%c` S%dC%dA%d\n"), int(event.GetKeyCode()), (wxChar)event.GetKeyCode(), event.ShiftDown(), event.ControlDown(), event.AltDown());
3647
3648 // select the next curve if possible, or cursor point like left mouse
3649 if (event.GetKeyCode() == WXK_SPACE)
3650 {
3651 if (event.ShiftDown() || event.ControlDown())
3652 {
3653 if (IsCursorValid())
3654 {
3655 if (GetDataCurve(m_cursor_curve))
3656 DoSelectDataRange(m_cursor_curve, wxRangeInt(m_cursor_index,m_cursor_index), !event.ControlDown(), true);
3657 else
3658 {
3659 wxPoint2DDouble pt(m_cursorMarker.GetPlotPosition());
3660 DoSelectRectangle(m_cursor_curve, wxRect2DDouble(pt.m_x,0,pt.m_x,0), !event.ControlDown(), true);
3661 }
3662 }
3663 }
3664 else
3665 {
3666 int count = GetCurveCount();
3667 if ((count < 1) || ((count == 1) && (m_active_index == 0))) return;
3668 int index = (m_active_index + 1 > count - 1) ? 0 : m_active_index + 1;
3669 SetActiveIndex( index, true );
3670 }
3671 return;
3672 }
3673
3674 // These are reserved for the program
3675 if (event.ControlDown() || event.AltDown())
3676 {
3677 event.Skip(true);
3678 return;
3679 }
3680
3681 switch (event.GetKeyCode())
3682 {
3683 // cursor keys moves the plot origin around
3684 case WXK_LEFT : SetOrigin(m_viewRect.GetLeft() - m_viewRect.m_width/10.0, m_viewRect.GetTop()); return;
3685 case WXK_RIGHT : SetOrigin(m_viewRect.GetLeft() + m_viewRect.m_width/10.0, m_viewRect.GetTop()); return;
3686 case WXK_UP : SetOrigin(m_viewRect.GetLeft(), m_viewRect.GetTop() + m_viewRect.m_height/10.0); return;
3687 case WXK_DOWN : SetOrigin(m_viewRect.GetLeft(), m_viewRect.GetTop() - m_viewRect.m_height/10.0); return;
3688 case WXK_PAGEUP : SetOrigin(m_viewRect.GetLeft(), m_viewRect.GetTop() + m_viewRect.m_height/2.0); return;
3689 case WXK_PAGEDOWN : SetOrigin(m_viewRect.GetLeft(), m_viewRect.GetTop() - m_viewRect.m_height/2.0); return;
3690
3691 // Center the plot on the cursor point, or 0,0
3692 case WXK_HOME :
3693 {
3694 if (IsCursorValid())
3695 MakeCursorVisible(true, true);
3696 else
3697 SetOrigin(-m_viewRect.m_width/2.0, -m_viewRect.m_height/2.0, true);
3698
3699 return;
3700 }
3701 // try to make the current curve fully visible
3702 case WXK_END :
3703 {
3704 wxPlotData *plotData = GetActiveDataCurve();
3705 if (plotData)
3706 {
3707 wxRect2DDouble bound = plotData->GetBoundingRect();
3708 bound.Inset(-bound.m_width/80.0, -bound.m_height/80.0);
3709 SetViewRect(bound, true);
3710 }
3711 else if (GetActiveCurve())
3712 {
3713 wxPlotCurve *curve = GetActiveCurve();
3714 double y, min, max;
3715
3716 y = max = min = curve->GetY(GetPlotCoordFromClientX(0));
3717
3718 for (int i=1; i<m_areaClientRect.width; i++)
3719 {
3720 y = curve->GetY(GetPlotCoordFromClientX(i));
3721
3722 if (wxFinite(y) != 0)
3723 {
3724 if (y > max) max = y;
3725 if (y < min) min = y;
3726 }
3727 }
3728
3729 if (max == min)
3730 {
3731 min -= 5;
3732 max += 5;
3733 }
3734
3735 wxRect2DDouble bound(m_viewRect.m_x, min, m_viewRect.m_width, max-min);
3736 SetViewRect(bound, true);
3737 }
3738
3739 return;
3740 }
3741
3742 // zoom in and out
3743 case wxT('a'): SetZoom( wxPoint2DDouble(m_zoom.m_x/1.5, m_zoom.m_y ), true ); return;
3744 case wxT('d'): SetZoom( wxPoint2DDouble(m_zoom.m_x*1.5, m_zoom.m_y ), true ); return;
3745 case wxT('w'): SetZoom( wxPoint2DDouble(m_zoom.m_x, m_zoom.m_y*1.5), true ); return;
3746 case wxT('x'): SetZoom( wxPoint2DDouble(m_zoom.m_x, m_zoom.m_y/1.5), true ); return;
3747
3748 case wxT('q'): SetZoom( wxPoint2DDouble(m_zoom.m_x/1.5, m_zoom.m_y*1.5), true ); return;
3749 case wxT('e'): SetZoom( wxPoint2DDouble(m_zoom.m_x*1.5, m_zoom.m_y*1.5), true ); return;
3750 case wxT('z'): SetZoom( wxPoint2DDouble(m_zoom.m_x/1.5, m_zoom.m_y/1.5), true ); return;
3751 case wxT('c'): SetZoom( wxPoint2DDouble(m_zoom.m_x*1.5, m_zoom.m_y/1.5), true ); return;
3752
3753 case wxT('s'): MakeCurveVisible(GetActiveIndex(), true); break;
3754
3755 // Select previous/next point in a curve
3756 case wxT('<'): case wxT(','):
3757 {
3758 double x = GetPlotCoordFromClientX(m_areaClientRect.width-1);
3759 wxPlotData *plotData = GetActiveDataCurve();
3760 if (plotData)
3761 {
3762 if (!IsCursorValid())
3763 SetCursorDataIndex(m_active_index, plotData->GetIndexFromX(x, wxPlotData::index_floor), true);
3764 else if (m_cursor_index > 0)
3765 SetCursorDataIndex(m_cursor_curve, m_cursor_index-1, true);
3766 }
3767 else if (m_active_index >= 0)
3768 {
3769 if (!IsCursorValid())
3770 SetCursorXPoint(m_active_index, x, true);
3771 else
3772 {
3773 x = GetPlotCoordFromClientX((GetClientCoordFromPlotX(m_cursorMarker.GetPlotRect().m_x)-1));
3774 SetCursorXPoint(m_cursor_curve, x, true);
3775 }
3776 }
3777
3778 MakeCursorVisible(false, true);
3779
3780 return;
3781 }
3782 case wxT('>'): case wxT('.'):
3783 {
3784 double x = GetPlotCoordFromClientX(0);
3785 wxPlotData *plotData = GetActiveDataCurve();
3786 if (plotData)
3787 {
3788 int count = plotData->GetCount();
3789
3790 if (!IsCursorValid())
3791 SetCursorDataIndex(m_active_index, plotData->GetIndexFromX(x, wxPlotData::index_ceil), true);
3792 else if (m_cursor_index < count - 1)
3793 SetCursorDataIndex(m_cursor_curve, m_cursor_index+1, true);
3794 }
3795 else if (m_active_index >= 0)
3796 {
3797 if (!IsCursorValid())
3798 SetCursorXPoint(m_active_index, x, true);
3799 else
3800 {
3801 x = GetPlotCoordFromClientX((GetClientCoordFromPlotX(m_cursorMarker.GetPlotRect().m_x)+1));
3802 SetCursorXPoint(m_cursor_curve, x, true);
3803 }
3804 }
3805
3806 MakeCursorVisible(false, true);
3807
3808 return;
3809 }
3810
3811 // go to the last or next zoom
3812 case wxT('[') : NextHistoryView(false, true); return;
3813 case wxT(']') : NextHistoryView(true, true); return;
3814
3815 // delete the selected curve
3816 case WXK_DELETE:
3817 {
3818 if (m_activeCurve) DeleteCurve(m_activeCurve, true);
3819 return;
3820 }
3821 // delete current selection or go to next curve and delete it's selection
3822 // finally invalidate cursor
3823 case WXK_ESCAPE :
3824 {
3825 BeginBatch();
3826 if ((m_active_index >= 0) && (GetSelectedRangeCount(m_active_index) > 0))
3827 {
3828 ClearSelectedRanges(m_active_index, true);
3829 }
3830 else
3831 {
3832 bool has_cleared = false;
3833
3834 for (int i=0; i<GetCurveCount(); i++)
3835 {
3836 if (GetSelectedRangeCount(i) > 0)
3837 {
3838 ClearSelectedRanges(i, true);
3839 has_cleared = true;
3840 break;
3841 }
3842 }
3843
3844 if (!has_cleared)
3845 {
3846 if (IsCursorValid())
3847 InvalidateCursor(true);
3848 else if (m_active_index > -1)
3849 SetActiveIndex( -1, true );
3850 }
3851 }
3852 EndBatch(); // ESC is also a generic clean up routine too!
3853 break;
3854 }
3855
3856 default: event.Skip(true); break;
3857 }
3858 }
3859
UpdateWindowSize()3860 void wxPlotCtrl::UpdateWindowSize()
3861 {
3862 m_areaClientRect = wxRect(wxPoint(0,0), m_area->GetClientSize());
3863 // If something happens to make these true, there's a problem
3864 if (m_areaClientRect.width < 10) m_areaClientRect.width = 10;
3865 if (m_areaClientRect.height < 10) m_areaClientRect.height = 10;
3866 }
3867
AdjustScrollBars()3868 void wxPlotCtrl::AdjustScrollBars()
3869 {
3870 double range, thumbsize, position;
3871 double pagesize;
3872
3873 range = (m_curveBoundingRect.m_width * m_zoom.m_x);
3874 if (!IsFinite(range, wxT("plot's x range is NaN"))) return;
3875 if (range > 32000) range = 32000; else if (range < 1) range = 1;
3876
3877 thumbsize = (range * (m_viewRect.m_width/m_curveBoundingRect.m_width));
3878 if (!IsFinite(thumbsize, wxT("plot's x range is NaN"))) return;
3879 if (thumbsize > range) thumbsize = range; else if (thumbsize < 1) thumbsize = 1;
3880
3881 position = (range * ((m_viewRect.GetLeft() - m_curveBoundingRect.GetLeft())/m_curveBoundingRect.m_width));
3882 if (!IsFinite(position, wxT("plot's x range is NaN"))) return;
3883 if (position > range - thumbsize) position = range - thumbsize; else if (position < 0) position = 0;
3884 pagesize = thumbsize;
3885
3886 m_xAxisScrollbar->SetScrollbar( int(position), int(thumbsize), int(range), int(pagesize) );
3887
3888 range = (m_curveBoundingRect.m_height * m_zoom.m_y);
3889 if (!IsFinite(range, wxT("plot's y range is NaN"))) return;
3890 if (range > 32000) range = 32000; else if (range < 1) range = 1;
3891
3892 thumbsize = (range * (m_viewRect.m_height/m_curveBoundingRect.m_height));
3893 if (!IsFinite(thumbsize, wxT("plot's x range is NaN"))) return;
3894 if (thumbsize > range) thumbsize = range; else if (thumbsize < 1) thumbsize = 1;
3895
3896 position = (range - range * ((m_viewRect.GetTop() - m_curveBoundingRect.GetTop())/m_curveBoundingRect.m_height) - thumbsize);
3897 if (!IsFinite(position, wxT("plot's x range is NaN"))) return;
3898 if (position > range - thumbsize) position = range - thumbsize; else if (position < 0) position = 0;
3899 pagesize = thumbsize;
3900
3901 m_yAxisScrollbar->SetScrollbar( int(position), int(thumbsize), int(range), int(pagesize) );
3902 }
3903
OnScroll(wxScrollEvent & event)3904 void wxPlotCtrl::OnScroll(wxScrollEvent& event)
3905 {
3906 if (m_scroll_on_thumb_release && (event.GetEventType() == wxEVT_SCROLL_THUMBTRACK))
3907 return;
3908
3909 if (event.GetId() == wxPlotCtrl::ID_PLOTCTRL_X_SCROLLBAR)
3910 {
3911 double range = m_xAxisScrollbar->GetRange();
3912 if (range < 1) return;
3913 double position = m_xAxisScrollbar->GetThumbPosition();
3914 double origin_x = m_curveBoundingRect.GetLeft() + m_curveBoundingRect.m_width*(position/range);
3915 if (!IsFinite(origin_x, wxT("plot's x-origin is NaN"))) return;
3916 m_viewRect.m_x = origin_x;
3917 Redraw(wxPLOT_REDRAW_PLOT|wxPLOT_REDRAW_XAXIS);
3918 }
3919 else if (event.GetId() == wxPlotCtrl::ID_PLOTCTRL_Y_SCROLLBAR)
3920 {
3921 double range = m_yAxisScrollbar->GetRange();
3922 if (range < 1) return;
3923 double position = m_yAxisScrollbar->GetThumbPosition();
3924 double thumbsize = m_yAxisScrollbar->GetThumbSize();
3925 double origin_y = m_curveBoundingRect.GetTop() + m_curveBoundingRect.m_height*((range-position-thumbsize)/range);
3926 if (!IsFinite(origin_y, wxT("plot's y-origin is NaN"))) return;
3927 m_viewRect.m_y = origin_y;
3928 Redraw(wxPLOT_REDRAW_PLOT|wxPLOT_REDRAW_YAXIS);
3929 }
3930 }
3931
IsFinite(double n,const wxString & msg) const3932 bool wxPlotCtrl::IsFinite(double n, const wxString &msg) const
3933 {
3934 if (!wxFinite(n))
3935 {
3936 if (!msg.IsEmpty())
3937 {
3938 wxPlotEvent event(wxEVT_PLOT_ERROR, GetId(), (wxPlotCtrl*)this);
3939 event.SetString(msg);
3940 (void)DoSendEvent( event );
3941 }
3942
3943 return false;
3944 }
3945
3946 return true;
3947 }
3948
FindCurve(const wxPoint2DDouble & pt,const wxPoint2DDouble & dpt,int & curve_index,int & data_index,wxPoint2DDouble * curvePt) const3949 bool wxPlotCtrl::FindCurve(const wxPoint2DDouble &pt, const wxPoint2DDouble &dpt,
3950 int &curve_index, int &data_index, wxPoint2DDouble *curvePt) const
3951 {
3952 curve_index = data_index = -1;
3953
3954 if (!IsFinite(pt.m_x, wxT("point is not finite"))) return false;
3955 if (!IsFinite(pt.m_y, wxT("point is not finite"))) return false;
3956 if (!IsFinite(dpt.m_x, wxT("point is not finite"))) return false;
3957 if (!IsFinite(dpt.m_y, wxT("point is not finite"))) return false;
3958
3959 int curve_count = GetCurveCount();
3960 if (curve_count < 1) return false;
3961
3962 for (int n=-1; n<curve_count; n++)
3963 {
3964 // find the point in the selected curve first
3965 if (n == -1)
3966 {
3967 if (m_active_index >= 0)
3968 n = m_active_index;
3969 else
3970 n = 0;
3971 }
3972 else if (n == m_active_index)
3973 continue;
3974
3975 wxPlotCurve *plotCurve = GetCurve(n);
3976 wxPlotData *plotData = wxDynamicCast(plotCurve, wxPlotData);
3977
3978 // find the index of the closest point in a wxPlotData curve
3979 if (plotData)
3980 {
3981 // check if curve has BoundingRect
3982 wxRect2DDouble rect = plotData->GetBoundingRect();
3983 if ( ((rect.m_width > 0) &&
3984 ((pt.m_x+dpt.m_x < rect.GetLeft()) || (pt.m_x-dpt.m_x > rect.GetRight()))) ||
3985 ((rect.m_height > 0) &&
3986 ((pt.m_y+dpt.m_y < rect.GetTop()) || (pt.m_y-dpt.m_y > rect.GetBottom()))) )
3987 {
3988 if ((n == m_active_index) && (n > 0)) n = -1; // start back at 0
3989 continue;
3990 }
3991
3992 int index = plotData->GetIndexFromXY(pt.m_x, pt.m_y, dpt.m_x);
3993
3994 double x = plotData->GetXValue(index);
3995 double y = plotData->GetYValue(index);
3996
3997 if ((fabs(x-pt.m_x) <= dpt.m_x) && (fabs(y-pt.m_y) <= dpt.m_y))
3998 {
3999 curve_index = n;
4000 data_index = index;
4001 if (curvePt) *curvePt = wxPoint2DDouble(x, y);
4002 return true;
4003 }
4004 }
4005 else // not a data curve, just find y at this x pos
4006 {
4007 wxRect2DDouble rect = plotCurve->GetBoundingRect();
4008 if ((rect.m_width <= 0) || ((pt.m_x+dpt.m_x >= rect.GetLeft()) && (pt.m_x-dpt.m_x <= rect.GetRight())))
4009 {
4010 if ((rect.m_height <= 0) || ((pt.m_y >= rect.GetTop()) && (pt.m_y-dpt.m_y <= rect.GetBottom())))
4011 {
4012 double y = plotCurve->GetY(pt.m_x);
4013 if (fabs(y - pt.m_y) <= dpt.m_y)
4014 {
4015 curve_index = n;
4016 if (curvePt) *curvePt = wxPoint2DDouble(pt.m_x, y);
4017 return true;
4018 }
4019 }
4020 }
4021 }
4022
4023 // continue searching through curves
4024 // if on the current then start back at the beginning if not already at 0
4025 if ((n == m_active_index) && (n > 0)) n = -1;
4026 }
4027 return false;
4028 }
4029
DoSendEvent(wxPlotEvent & event) const4030 bool wxPlotCtrl::DoSendEvent(wxPlotEvent &event) const
4031 {
4032 /*
4033 if (event.GetEventType() != wxEVT_PLOT_MOUSE_MOTION)
4034 {
4035 wxLogDebug(wxT("wxPlotEvent '%s' CurveIndex: %d, DataIndex: %d, Pos: %lf %lf, MouseFn %d"),
4036 GetEventName(event).c_str(),
4037 event.GetCurveIndex(), event.GetCurveDataIndex(),
4038 event.GetX(), event.GetY(), event.GetMouseFunction());
4039 }
4040 */
4041 return !GetEventHandler()->ProcessEvent(event) || event.IsAllowed();
4042 }
4043
StartMouseTimer(wxWindowID win_id)4044 void wxPlotCtrl::StartMouseTimer(wxWindowID win_id)
4045 {
4046 #if wxCHECK_VERSION(2,5,0)
4047 if (m_timer && (m_timer->GetId() != win_id))
4048 StopMouseTimer();
4049 #else
4050 StopMouseTimer(); // always stop it I guess
4051 #endif // wxCHECK_VERSION(2,5,0)
4052
4053 if (!m_timer)
4054 m_timer = new wxTimer(this, win_id);
4055
4056 if (!m_timer->IsRunning())
4057 m_timer->Start(200, true); // one shot timer
4058 }
StopMouseTimer()4059 void wxPlotCtrl::StopMouseTimer()
4060 {
4061 if (m_timer)
4062 {
4063 if (m_timer->IsRunning())
4064 m_timer->Stop();
4065
4066 delete m_timer;
4067 m_timer = NULL;
4068 }
4069 }
4070
IsTimerRunning()4071 bool wxPlotCtrl::IsTimerRunning()
4072 {
4073 return (m_timer && m_timer->IsRunning());
4074 }
4075
OnTimer(wxTimerEvent & event)4076 void wxPlotCtrl::OnTimer( wxTimerEvent &event )
4077 {
4078 wxPoint mousePt;
4079
4080 switch (event.GetId())
4081 {
4082 case ID_AREA_TIMER : mousePt = m_area->m_mousePt; break;
4083 case ID_XAXIS_TIMER : mousePt = m_xAxis->m_mousePt; break;
4084 case ID_YAXIS_TIMER : mousePt = m_yAxis->m_mousePt; break;
4085 default :
4086 {
4087 event.Skip(); // someone else's timer?
4088 return;
4089 }
4090 }
4091
4092 double dx = (mousePt.x<0) ? -20 : (mousePt.x>GetPlotAreaRect().width ) ? 20 : 0;
4093 double dy = (mousePt.y<0) ? 20 : (mousePt.y>GetPlotAreaRect().height) ? -20 : 0;
4094 dx /= m_zoom.m_x;
4095 dy /= m_zoom.m_y;
4096
4097 if (((dx == 0) && (dy == 0)) ||
4098 !SetOrigin(GetViewRect().GetLeft() + dx, GetViewRect().GetTop() + dy, true) )
4099 {
4100 StopMouseTimer();
4101 }
4102 else
4103 StartMouseTimer(event.GetId()); // restart timer for another round
4104 }
4105
SetCaptureWindow(wxWindow * win)4106 void wxPlotCtrl::SetCaptureWindow(wxWindow *win)
4107 {
4108 if (m_winCapture && (m_winCapture != win) && m_winCapture->HasCapture())
4109 m_winCapture->ReleaseMouse();
4110
4111 m_winCapture = win;
4112
4113 if (m_winCapture && (!m_winCapture->HasCapture()))
4114 m_winCapture->CaptureMouse();
4115 }
4116