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