1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/univ/scrarrow.cpp
3 // Purpose:     wxScrollArrows class implementation
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     22.01.01
7 // Copyright:   (c) 2001 SciTech Software, Inc. (www.scitechsoft.com)
8 // Licence:     wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 #include "wx/wxprec.h"
20 
21 
22 #ifndef WX_PRECOMP
23     #include "wx/window.h"
24 #endif
25 
26 #include "wx/univ/scrtimer.h"
27 #include "wx/univ/scrarrow.h"
28 
29 #include "wx/univ/renderer.h"
30 #include "wx/univ/inphand.h"
31 #include "wx/univ/theme.h"
32 
33 #if wxUSE_SCROLLBAR
34 // ----------------------------------------------------------------------------
35 // wxScrollArrowCaptureData: contains the data used while the arrow is being
36 // pressed by the user
37 // ----------------------------------------------------------------------------
38 
39 struct wxScrollArrowCaptureData
40 {
wxScrollArrowCaptureDatawxScrollArrowCaptureData41     wxScrollArrowCaptureData()
42     {
43         m_arrowPressed = wxScrollArrows::Arrow_None;
44         m_window = NULL;
45         m_btnCapture = -1;
46 #if wxUSE_TIMER
47         m_timerScroll = NULL;
48 #endif // wxUSE_TIMER
49     }
50 
~wxScrollArrowCaptureDatawxScrollArrowCaptureData51     ~wxScrollArrowCaptureData()
52     {
53         if ( m_window )
54             m_window->ReleaseMouse();
55 
56 #if wxUSE_TIMER
57         delete m_timerScroll;
58 #endif // wxUSE_TIMER
59     }
60 
61     // the arrow being held pressed (may be Arrow_None)
62     wxScrollArrows::Arrow m_arrowPressed;
63 
64     // the mouse button which started the capture (-1 if none)
65     int m_btnCapture;
66 
67     // the window which has captured the mouse
68     wxWindow *m_window;
69 
70 #if wxUSE_TIMER
71     // the timer for generating the scroll events
72     wxScrollTimer *m_timerScroll;
73 #endif
74 };
75 
76 // ----------------------------------------------------------------------------
77 // wxScrollArrowTimer: a wxScrollTimer which calls OnArrow()
78 // ----------------------------------------------------------------------------
79 
80 #if wxUSE_TIMER
81 
82 class wxScrollArrowTimer : public wxScrollTimer
83 {
84 public:
wxScrollArrowTimer(wxControlWithArrows * control,wxScrollArrows::Arrow arrow)85     wxScrollArrowTimer(wxControlWithArrows *control,
86                        wxScrollArrows::Arrow arrow)
87     {
88         m_control = control;
89         m_arrow = arrow;
90 
91         StartAutoScroll();
92     }
93 
94 protected:
DoNotify()95     virtual bool DoNotify()
96     {
97         return m_control->OnArrow(m_arrow);
98     }
99 
100     wxControlWithArrows *m_control;
101     wxScrollArrows::Arrow m_arrow;
102 };
103 
104 #endif // wxUSE_TIMER
105 
106 // ============================================================================
107 // implementation of wxScrollArrows
108 // ============================================================================
109 
110 // ----------------------------------------------------------------------------
111 // con/destruction
112 // ----------------------------------------------------------------------------
113 
wxScrollArrows(wxControlWithArrows * control)114 wxScrollArrows::wxScrollArrows(wxControlWithArrows *control)
115 {
116     m_control = control;
117     m_captureData = NULL;
118 }
119 
~wxScrollArrows()120 wxScrollArrows::~wxScrollArrows()
121 {
122     // it should have been destroyed
123     wxASSERT_MSG( !m_captureData, wxT("memory leak in wxScrollArrows") );
124 }
125 
126 // ----------------------------------------------------------------------------
127 // drawing
128 // ----------------------------------------------------------------------------
129 
DrawArrow(Arrow arrow,wxDC & dc,const wxRect & rect,bool scrollbarLike) const130 void wxScrollArrows::DrawArrow(Arrow arrow,
131                                wxDC& dc,
132                                const wxRect& rect,
133                                bool scrollbarLike) const
134 {
135     static const wxDirection arrowDirs[2][Arrow_Max] =
136     {
137         { wxLEFT, wxRIGHT },
138         { wxUP,   wxDOWN  }
139     };
140 
141     if ( scrollbarLike )
142         m_control->GetRenderer()->DrawScrollbarArrow(
143             dc,
144             arrowDirs[m_control->IsVertical()][arrow],
145             rect,
146             m_control->GetArrowState(arrow));
147     else
148         m_control->GetRenderer()->DrawArrow(
149             dc,
150             arrowDirs[m_control->IsVertical()][arrow],
151             rect,
152             m_control->GetArrowState(arrow));
153 }
154 
155 // ----------------------------------------------------------------------------
156 // input handling
157 // ----------------------------------------------------------------------------
158 
UpdateCurrentFlag(Arrow arrow,Arrow arrowCur) const159 void wxScrollArrows::UpdateCurrentFlag(Arrow arrow, Arrow arrowCur) const
160 {
161     m_control->SetArrowFlag(arrow, wxCONTROL_CURRENT, arrow == arrowCur);
162 }
163 
HandleMouseMove(const wxMouseEvent & event) const164 bool wxScrollArrows::HandleMouseMove(const wxMouseEvent& event) const
165 {
166     Arrow arrow;
167     if ( event.Leaving() )
168     {
169         // no arrow has mouse if it left the window completely
170         arrow = Arrow_None;
171     }
172     else // Moving() or Entering(), treat them the same here
173     {
174         arrow = m_control->HitTestArrow(event.GetPosition());
175     }
176 
177 #if wxUSE_TIMER
178     if ( m_captureData && m_captureData->m_timerScroll)
179     {
180         // the mouse is captured, we may want to pause scrolling if it goes
181         // outside the arrow or to resume it if we had paused it before
182         wxTimer *timer = m_captureData->m_timerScroll;
183         if ( !timer->IsRunning() )
184         {
185             // timer is paused
186             if ( arrow == m_captureData->m_arrowPressed )
187             {
188                 // resume now
189                 m_control->SetArrowFlag(arrow, wxCONTROL_PRESSED, true);
190                 timer->Start();
191 
192                 return true;
193             }
194         }
195         else // if ( 1 ) FIXME: m_control->ShouldPauseScrolling() )
196         {
197             // we may want to stop it
198             if ( arrow != m_captureData->m_arrowPressed )
199             {
200                 // stop as the mouse left the arrow
201                 m_control->SetArrowFlag(m_captureData->m_arrowPressed,
202                                         wxCONTROL_PRESSED, false);
203                 timer->Stop();
204 
205                 return true;
206             }
207         }
208 
209         return false;
210     }
211 #endif // wxUSE_TIMER
212 
213     // reset the wxCONTROL_CURRENT flag for the arrows which don't have the
214     // mouse and set it for the one which has
215     UpdateCurrentFlag(Arrow_First, arrow);
216     UpdateCurrentFlag(Arrow_Second, arrow);
217 
218     // return true if it was really an event for an arrow
219     return !event.Leaving() && arrow != Arrow_None;
220 }
221 
HandleMouse(const wxMouseEvent & event) const222 bool wxScrollArrows::HandleMouse(const wxMouseEvent& event) const
223 {
224     int btn = event.GetButton();
225     if ( btn == -1 )
226     {
227         // we only care about button press/release events
228         return false;
229     }
230 
231     if ( event.ButtonDown() || event.ButtonDClick() )
232     {
233         if ( !m_captureData )
234         {
235             Arrow arrow = m_control->HitTestArrow(event.GetPosition());
236             if ( arrow == Arrow_None )
237             {
238                 // mouse pressed over something else
239                 return false;
240             }
241 
242             if ( m_control->GetArrowState(arrow) & wxCONTROL_DISABLED )
243             {
244                 // don't allow to press disabled arrows
245                 return true;
246             }
247 
248             wxConstCast(this, wxScrollArrows)->m_captureData =
249                 new wxScrollArrowCaptureData;
250             m_captureData->m_arrowPressed = arrow;
251             m_captureData->m_btnCapture = btn;
252             m_captureData->m_window = m_control->GetWindow();
253             m_captureData->m_window->CaptureMouse();
254 
255 #if wxUSE_TIMER
256             // start scrolling
257             wxScrollArrowTimer *tmpTimerScroll =
258                 new wxScrollArrowTimer(m_control, arrow);
259 #endif // wxUSE_TIMER
260 
261             // Because in some cases wxScrollArrowTimer can cause
262             // m_captureData to be destructed we need to test if it
263             // is still valid before using.
264             if (m_captureData)
265             {
266 #if wxUSE_TIMER
267                 m_captureData->m_timerScroll = tmpTimerScroll;
268 #endif // wxUSE_TIMER
269 
270                 m_control->SetArrowFlag(arrow, wxCONTROL_PRESSED, true);
271             }
272             else
273             {
274 #if wxUSE_TIMER
275                 delete tmpTimerScroll;
276 #endif // wxUSE_TIMER
277             }
278         }
279         //else: mouse already captured, nothing to do
280     }
281     // release mouse if the *same* button went up
282     else if ( m_captureData && (btn == m_captureData->m_btnCapture) )
283     {
284         Arrow arrow = m_captureData->m_arrowPressed;
285 
286         delete m_captureData;
287         wxConstCast(this, wxScrollArrows)->m_captureData = NULL;
288 
289         m_control->SetArrowFlag(arrow, wxCONTROL_PRESSED, false);
290     }
291     else
292     {
293         // we don't process this
294         return false;
295     }
296 
297     return true;
298 }
299 #endif // wxUSE_SCROLLBAR
300