wxRectTracker
LineTracker.cpp
Go to the documentation of this file.
00001 /* ****************************************************************************
00002  * LineTracker.cpp                                                            *
00003  *                                                                            *
00004  * (c) 2004-2012 - Rémi Peyronnet <remi+rphoto@via.ecp.fr>                    *
00005  * LineTracker was originally designed for RPhoto, but can be used separately *
00006  *                                                                            *
00007  * Licence : wxWindows (based on L-GPL)                                       *
00008  *                                                                            *
00009  * ************************************************************************** */
00010 
00011 
00012 #include "LineTracker.h"
00013 #include <wx/dcbuffer.h>
00014 
00015 /// Helpers
00016 
00017 static wxRect NormalizeRect(wxRect rect)
00018 {
00019     if (rect.width < 0) { rect.x += rect.width ; rect.width = -rect.width; }
00020     if (rect.height < 0) { rect.y += rect.height ; rect.height = -rect.height; }
00021     return rect;
00022 }
00023 
00024 /// wxLineTracker
00025 
00026 IMPLEMENT_CLASS(wxLineTracker, wxRectTracker)
00027 
00028 wxLineTracker::wxLineTracker(wxWindow * parent, wxFrame * frame) 
00029             : wxRectTracker(parent, frame)
00030 {
00031 }
00032 
00033 wxLineTracker::~wxLineTracker()
00034 {
00035 }
00036 
00037 
00038 void wxLineTracker::OnPaint(wxPaintEvent & event)
00039 {
00040     if ((m_state & RT_STATE_DISABLED) != 0) return;
00041     if ((m_state & RT_STATE_DRAGGING) != 0) return;
00042     this->GetNextHandler()->ProcessEvent(event);
00043     wxClientDC dc(m_wnd);
00044     if (wxDynamicCast(m_wnd,wxScrolledWindow))
00045     {
00046         wxDynamicCast(m_wnd,wxScrolledWindow)->DoPrepareDC(dc);
00047     }   
00048     DrawLine(dc, GetPosBegin(), GetPosEnd());
00049 }
00050 
00051 // DrawLine operates with a wxPaintDC => Window coordinates
00052 void wxLineTracker::DrawLine(wxDC & dc, wxPoint begin, wxPoint end)
00053 {
00054     // Rect
00055     dc.SetBrush(*wxTRANSPARENT_BRUSH);
00056     dc.SetPen(*wxBLACK_PEN);
00057     dc.DrawLine(begin.x, begin.y, end.x, end.y);
00058     dc.SetPen(wxPen(*wxWHITE,1,wxDOT));
00059     dc.DrawLine(begin.x, begin.y, end.x, end.y);
00060 
00061     // Handlers
00062     int z = m_iHandlerWidth;
00063     if (m_iHandlerMask != RT_MASK_NONE)
00064     {
00065         dc.SetBrush(*wxBLACK_BRUSH);
00066         dc.SetPen(*wxBLACK_PEN);
00067         
00068         if (m_iHandlerMask & RT_LINE_MASK_BEGIN) dc.DrawRectangle(begin.x-(z/2), begin.y-(z/2), z, z);
00069         if (m_iHandlerMask & RT_LINE_MASK_END  ) dc.DrawRectangle(end.x-(z/2), end.y-(z/2), z, z);
00070     }       
00071 }
00072 
00073 // DrawTracker operates with the parent's wxWindowDC => Parent coordinates
00074 void wxLineTracker::DrawTracker(wxDC & dc, wxPoint begin, wxPoint end)
00075 {
00076 
00077     int x1, y1, x2, y2;
00078     x1 = begin.x; y1 = begin.y;
00079     x2 = end.x;   y2 = end.y;
00080     // Convert coordinates if scrolled
00081     if (wxDynamicCast(m_wnd,wxScrolledWindow) != NULL)
00082     {
00083         wxDynamicCast(m_wnd,wxScrolledWindow)->CalcScrolledPosition(x1, y1, &x1, &y1);
00084         wxDynamicCast(m_wnd,wxScrolledWindow)->CalcScrolledPosition(x2, y2, &x2, &y2);
00085     }
00086 
00087     // Inverted Line
00088     dc.SetLogicalFunction(wxINVERT);
00089     dc.SetBrush(*wxTRANSPARENT_BRUSH);
00090     dc.SetPen(*wxGREY_PEN);
00091     dc.DrawLine(begin.x,begin.y,end.x,end.y);
00092     dc.SetLogicalFunction(wxCOPY);
00093 }
00094 
00095 
00096 void wxLineTracker::OnKey(wxKeyEvent & event)
00097 {
00098     
00099     if ((m_state & RT_STATE_DISABLED) != 0) return;
00100     
00101     int incr = 0, dx = 0, dy = 0;
00102     int handler;
00103     wxRect tmpRect;
00104     tmpRect = GetUnscrolledRect();
00105     
00106     if (event.ControlDown()) incr = 10; else incr = 1;
00107 
00108     switch(event.GetKeyCode())
00109     {
00110     case WXK_ESCAPE :
00111             if ((m_state & RT_STATE_MOUSE_CAPTURED) != 0)
00112             {
00113                 m_wnd->ReleaseMouse();
00114                 m_state ^= RT_STATE_MOUSE_CAPTURED;
00115                 Update();
00116             }
00117             else
00118             {
00119                 Hide();
00120             }
00121             break;
00122     case WXK_LEFT   : dx -= incr; break;
00123     case WXK_UP     : dy -= incr; break;
00124     case WXK_RIGHT  : dx += incr; break;
00125     case WXK_DOWN   : dy += incr; break;
00126     default:
00127         event.Skip();
00128     }
00129 
00130     if ((dx != 0) || (dy != 0))
00131     {
00132         if (event.ShiftDown())
00133         {
00134             m_pCurEnd.x += dx;
00135             m_pCurEnd.y += dy;
00136             handler = RT_LINE_HANDLER_END;
00137         }
00138         else
00139         {
00140             m_pCurBegin.x += dx;    m_pCurEnd.x += dx;
00141             m_pCurBegin.y += dy;    m_pCurEnd.y += dy;
00142             handler = 0;
00143         }
00144         AdjustLineTracker(m_pCurBegin, m_pCurEnd, handler);
00145         SetTrackerPosition(m_pCurBegin, m_pCurEnd);
00146         Update();
00147     }
00148 
00149 }
00150 
00151 void wxLineTracker::OnMouseMotion(wxMouseEvent & event)
00152 {
00153     int hit;
00154     int dx, dy;
00155 
00156     if ((m_state & RT_STATE_DISABLED) != 0) return;
00157 
00158     // Just moving ?
00159     if (!event.Dragging())
00160     {
00161         hit = HitTest(event.m_x, event.m_y);
00162         switch (hit)
00163         {
00164         case RT_LINE_HANDLER_END:
00165         case RT_LINE_HANDLER_BEGIN:
00166         case RT_LINE_HANDLER_ON_LINE:
00167             m_wnd->SetCursor(*m_cursorMove);
00168             break;
00169         default:
00170             m_wnd->SetCursor(wxCursor(wxCURSOR_ARROW));
00171             break;
00172         }
00173     }
00174     else if (event.LeftIsDown())
00175     {
00176         // Dragging
00177 
00178         wxASSERT(m_wnd!=NULL);
00179 
00180         // Drawing Tracker Rect
00181         wxClientDC dc(m_wnd);
00182 
00183         if ((m_state & RT_STATE_DRAGGING) == 0)
00184         {
00185             m_state |= RT_STATE_DRAGGING;
00186             m_pCurBegin = GetPosBegin();
00187             m_pCurEnd = GetPosEnd();
00188         }
00189         else
00190         {
00191             // Erase previous Tracker
00192             DrawTracker(dc, m_pCurBegin, m_pCurEnd);
00193         }
00194 
00195         // Update the new position
00196         // - Which Tracker ?
00197         hit = HitTest(m_leftClick.x, m_leftClick.y);
00198         // - Default Rect values
00199         
00200         dx = (event.m_x - m_leftClick.x);
00201         dy = (event.m_y - m_leftClick.y);
00202 
00203         if ( (hit & RT_LINE_HANDLER_BEGIN) != 0)
00204         {
00205             m_pCurBegin.x = GetPosBegin().x + dx;
00206             m_pCurBegin.y = GetPosBegin().y + dy;
00207         }
00208         if ( (hit & RT_LINE_HANDLER_END) != 0)
00209         {
00210             m_pCurEnd.x = GetPosEnd().x + dx;
00211             m_pCurEnd.y = GetPosEnd().y + dy;
00212         }
00213 
00214         // Adjust current Tracker size
00215         AdjustLineTracker(m_pCurBegin, m_pCurEnd, hit);
00216 
00217         // Draw current Tracker
00218         DrawTracker(dc, m_pCurBegin, m_pCurEnd);
00219 
00220         // Update Parent's Status Bar
00221         wxCommandEvent evt(wxEVT_TRACKER_CHANGING, m_wnd->GetId());
00222         m_wnd->GetEventHandler()->ProcessEvent(evt);
00223 
00224         //dc.EndDrawingOnTop();
00225     }
00226     
00227     // Check there is no abuse mouse capture
00228     if (!(event.LeftIsDown()) && ((m_state & RT_STATE_MOUSE_CAPTURED) != 0))
00229     {
00230         m_wnd->ReleaseMouse();
00231         m_state ^= RT_STATE_MOUSE_CAPTURED;
00232     }
00233 
00234     // Update prev_move
00235     m_prevMove = event.GetPosition();
00236 }
00237 
00238 void wxLineTracker::OnMouseLeftDown(wxMouseEvent & event)
00239 {
00240     if (HitTest(event.m_x, event.m_y) == RT_LINE_HANDLER_NONE)
00241     {
00242         m_pCurBegin = wxPoint(event.m_x, event.m_y);
00243         m_pCurEnd = m_pCurBegin;
00244         SetTrackerPosition(m_pCurBegin, m_pCurEnd);
00245     }
00246     wxRectTracker::OnMouseLeftDown(event);
00247 }
00248 
00249 void wxLineTracker::OnMouseLeftUp(wxMouseEvent & event)
00250 {
00251     if ((m_state & RT_STATE_MOUSE_CAPTURED) != 0)
00252     {
00253         m_wnd->ReleaseMouse();
00254         m_state ^= RT_STATE_MOUSE_CAPTURED;
00255     }
00256     if ((m_state & RT_STATE_DRAGGING) != 0)
00257     {
00258         SetTrackerPosition(m_pCurBegin, m_pCurEnd);
00259         m_state ^= RT_STATE_DRAGGING;
00260         Update();
00261     }
00262 }
00263 
00264 int wxLineTracker::HitTest(int x, int y) const
00265 {
00266     wxRect curRect;
00267     int z = m_iHandlerWidth;
00268     int hit;
00269 
00270     hit = wxRectTracker::HitTest(x, y);
00271 
00272     // if ( (y < 0) || (y > h) || (x < 0) || (x > w) ) return RT_HANDLER_OUTSIDE;
00273     
00274     if ( hit == m_iEndHandler)      return RT_LINE_HANDLER_END;
00275     if ( hit == m_iBeginHandler)    return RT_LINE_HANDLER_BEGIN;
00276 
00277     // Distance entre le point et le segment (a = begin ; b = end ; c = x,y )
00278     int xac = x - GetPosBegin().x; // + m_Rect.GetPosition().x;
00279     int yac = y - GetPosBegin().y; // + m_Rect.GetPosition().y;
00280     int xab = GetPosEnd().x - GetPosBegin().x;
00281     int yab = GetPosEnd().y - GetPosBegin().y;
00282 
00283     if ( (xab != 0) || (yab != 0) )
00284         if ( ( (((double) - xac * yab + yac * xab ) * ((double)  - xac * yab + yac * xab )) 
00285              / ((double) (xab * xab) + (yab * yab))
00286                  ) < ((double) z * z) )
00287                 return RT_LINE_HANDLER_ON_LINE;
00288     
00289     return RT_HANDLER_NONE;
00290 }
00291 
00292 void wxLineTracker::AdjustLineTracker(wxPoint & begin, wxPoint & end, int handler)
00293 {
00294     AdjustLineTrackerMax(begin, end, handler);
00295 }
00296 
00297 void wxLineTracker::AdjustLineTrackerMax(wxPoint & begin, wxPoint & end, int handler)
00298 {
00299     
00300     // Adjust m_maxRect 
00301     if ((m_maxRect.width < 0) || (m_maxRect.height < 0)) return;
00302 
00303     if (begin.x < m_maxRect.x) begin.x = m_maxRect.x;
00304     if (begin.x > m_maxRect.x + m_maxRect.width) begin.x = m_maxRect.x + m_maxRect.width;
00305     if (begin.y < m_maxRect.y) begin.y = m_maxRect.y;
00306     if (begin.y > m_maxRect.y + m_maxRect.height) begin.y = m_maxRect.x + m_maxRect.height;
00307 
00308     if (end.x < m_maxRect.x) end.x = m_maxRect.x;
00309     if (end.x > m_maxRect.x + m_maxRect.width) end.x = m_maxRect.x + m_maxRect.width;
00310     if (end.y < m_maxRect.y) end.y = m_maxRect.y;
00311     if (end.y > m_maxRect.y + m_maxRect.height) end.y = m_maxRect.x + m_maxRect.height;
00312 
00313 }
00314 
00315 void wxLineTracker::SetTrackerPosition(wxPoint begin, wxPoint end)
00316 {
00317     if (begin.x <= end.x)
00318     {
00319         if (begin.y <= end.y)
00320         {
00321             m_iBeginHandler = RT_HANDLER_TOP_LEFT;
00322             m_iEndHandler = RT_HANDLER_BOTTOM_RIGHT;
00323         }
00324         else
00325         {
00326             m_iBeginHandler = RT_HANDLER_BOTTOM_LEFT;
00327             m_iEndHandler = RT_HANDLER_TOP_RIGHT;
00328         }
00329     }
00330     else
00331     {
00332         if (begin.y <= end.y)
00333         {
00334             m_iBeginHandler = RT_HANDLER_TOP_RIGHT;
00335             m_iEndHandler = RT_HANDLER_BOTTOM_LEFT;
00336         }
00337         else
00338         {
00339             m_iBeginHandler = RT_HANDLER_BOTTOM_RIGHT;
00340             m_iEndHandler = RT_HANDLER_TOP_LEFT;
00341         }
00342     }
00343     SetUnscrolledRect(NormalizeRect(wxRect(begin, end)));
00344 }
00345 
00346 
00347 void wxLineTracker::Update()
00348 {
00349     m_wnd->Refresh();
00350     wxCommandEvent evt(wxEVT_TRACKER_CHANGED, m_wnd->GetId());
00351     m_wnd->GetEventHandler()->ProcessEvent(evt);
00352 }
00353 
00354 wxPoint wxLineTracker::GetPosBegin() const
00355 {
00356     return GetPosHandler(m_iBeginHandler);
00357 }
00358 
00359 wxPoint wxLineTracker::GetPosEnd() const
00360 {
00361     return GetPosHandler(m_iEndHandler);
00362 }
00363 
00364 wxPoint wxLineTracker::GetPosHandler(enum RT_HANDLER handler) const
00365 {
00366     wxPoint pos;
00367     switch(handler)
00368     {
00369     case RT_HANDLER_TOP_MID:        pos = wxPoint(m_Rect.GetWidth() / 2, 0); break;
00370     case RT_HANDLER_MID_RIGHT:      pos = wxPoint(m_Rect.GetWidth() - 1, m_Rect.GetHeight() / 2); break;
00371     case RT_HANDLER_BOTTOM_MID:     pos = wxPoint(m_Rect.GetWidth() / 2, m_Rect.GetHeight() - 1); break;
00372     case RT_HANDLER_MID_LEFT:       pos = wxPoint(0, m_Rect.GetHeight() / 2); break;
00373     case RT_HANDLER_TOP_LEFT:       pos = wxPoint(0, 0); break;
00374     case RT_HANDLER_TOP_RIGHT:      pos = wxPoint(m_Rect.GetWidth() - 1, 0); break;
00375     case RT_HANDLER_BOTTOM_RIGHT:   pos = wxPoint(m_Rect.GetWidth() - 1, m_Rect.GetHeight() - 1); break;
00376     case RT_HANDLER_BOTTOM_LEFT:    pos = wxPoint(0, m_Rect.GetHeight() - 1); break;
00377     case RT_HANDLER_OUTSIDE:
00378     case RT_HANDLER_NONE:
00379     default:
00380         return wxPoint(-1,-1);
00381     }
00382     return pos + m_Rect.GetPosition();
00383 }
00384