]> git.saurik.com Git - wxWidgets.git/blame - src/generic/scrlwing.cpp
Fixed a crash when deleting the last timer
[wxWidgets.git] / src / generic / scrlwing.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
d80cd92a 2// Name: generic/scrolwin.cpp
d32e78bd 3// Purpose: wxScrolledWindow implementation
c801d85f 4// Author: Julian Smart
566d84a7
RL
5// Modified by: Vadim Zeitlin on 31.08.00: wxScrollHelper allows to implement.
6// Ron Lee on 10.4.02: virtual size / auto scrollbars et al.
c801d85f
KB
7// Created: 01/02/97
8// RCS-ID: $Id$
77ffb593 9// Copyright: (c) wxWidgets team
65571936 10// Licence: wxWindows licence
c801d85f
KB
11/////////////////////////////////////////////////////////////////////////////
12
d80cd92a
VZ
13// ============================================================================
14// declarations
15// ============================================================================
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
bcd055ae
JJ
21#ifdef __VMS
22#define XtDisplay XTDISPLAY
23#endif
24
c801d85f
KB
25// For compilers that support precompilation, includes "wx.h".
26#include "wx/wxprec.h"
27
c801d85f 28#ifdef __BORLANDC__
d80cd92a 29 #pragma hdrstop
c801d85f
KB
30#endif
31
d80cd92a
VZ
32#include "wx/utils.h"
33#include "wx/dcclient.h"
34
1e6feb95 35#include "wx/scrolwin.h"
053f9cc1 36#include "wx/panel.h"
4b316329 37#if wxUSE_TIMER
1e6feb95 38#include "wx/timer.h"
4b316329 39#endif
95d60b24 40#include "wx/sizer.h"
2a201ef8 41#include "wx/recguard.h"
c801d85f 42
48d1144b 43#ifdef __WXMSW__
1e6feb95 44 #include <windows.h> // for DLGC_WANTARROWS
7acf6a92 45 #include "wx/msw/winundef.h"
48d1144b
JS
46#endif
47
a91b47e8
JS
48#ifdef __WXMOTIF__
49// For wxRETAINED implementation
338dd992
JJ
50#ifdef __VMS__ //VMS's Xm.h is not (yet) compatible with C++
51 //This code switches off the compiler warnings
52# pragma message disable nosimpint
53#endif
a91b47e8 54#include <Xm/Xm.h>
338dd992
JJ
55#ifdef __VMS__
56# pragma message enable nosimpint
57#endif
a91b47e8
JS
58#endif
59
066f1b7a 60/*
ca65c044
WS
61 TODO PROPERTIES
62 style wxHSCROLL | wxVSCROLL
066f1b7a
SC
63*/
64
d80cd92a 65// ----------------------------------------------------------------------------
1e6feb95
VZ
66// wxScrollHelperEvtHandler: intercept the events from the window and forward
67// them to wxScrollHelper
d80cd92a
VZ
68// ----------------------------------------------------------------------------
69
349efbaa 70class WXDLLEXPORT wxScrollHelperEvtHandler : public wxEvtHandler
1e6feb95
VZ
71{
72public:
73 wxScrollHelperEvtHandler(wxScrollHelper *scrollHelper)
74 {
75 m_scrollHelper = scrollHelper;
76 }
d80cd92a 77
1e6feb95
VZ
78 virtual bool ProcessEvent(wxEvent& event);
79
ca65c044 80 void ResetDrawnFlag() { m_hasDrawnWindow = false; }
349efbaa 81
1e6feb95
VZ
82private:
83 wxScrollHelper *m_scrollHelper;
349efbaa
VZ
84
85 bool m_hasDrawnWindow;
22f3361e
VZ
86
87 DECLARE_NO_COPY_CLASS(wxScrollHelperEvtHandler)
1e6feb95
VZ
88};
89
4b316329 90#if wxUSE_TIMER
1e6feb95
VZ
91// ----------------------------------------------------------------------------
92// wxAutoScrollTimer: the timer used to generate a stream of scroll events when
93// a captured mouse is held outside the window
94// ----------------------------------------------------------------------------
95
96class wxAutoScrollTimer : public wxTimer
97{
98public:
99 wxAutoScrollTimer(wxWindow *winToScroll, wxScrollHelper *scroll,
100 wxEventType eventTypeToSend,
101 int pos, int orient);
102
103 virtual void Notify();
104
105private:
106 wxWindow *m_win;
107 wxScrollHelper *m_scrollHelper;
108 wxEventType m_eventType;
109 int m_pos,
110 m_orient;
22f3361e
VZ
111
112 DECLARE_NO_COPY_CLASS(wxAutoScrollTimer)
1e6feb95 113};
d80cd92a
VZ
114
115// ============================================================================
116// implementation
117// ============================================================================
118
119// ----------------------------------------------------------------------------
1e6feb95 120// wxAutoScrollTimer
d80cd92a
VZ
121// ----------------------------------------------------------------------------
122
1e6feb95
VZ
123wxAutoScrollTimer::wxAutoScrollTimer(wxWindow *winToScroll,
124 wxScrollHelper *scroll,
125 wxEventType eventTypeToSend,
126 int pos, int orient)
c801d85f 127{
1e6feb95
VZ
128 m_win = winToScroll;
129 m_scrollHelper = scroll;
130 m_eventType = eventTypeToSend;
131 m_pos = pos;
132 m_orient = orient;
c801d85f
KB
133}
134
1e6feb95 135void wxAutoScrollTimer::Notify()
c801d85f 136{
1e6feb95
VZ
137 // only do all this as long as the window is capturing the mouse
138 if ( wxWindow::GetCapture() != m_win )
139 {
140 Stop();
141 }
142 else // we still capture the mouse, continue generating events
143 {
144 // first scroll the window if we are allowed to do it
145 wxScrollWinEvent event1(m_eventType, m_pos, m_orient);
146 event1.SetEventObject(m_win);
147 if ( m_scrollHelper->SendAutoScrollEvents(event1) &&
148 m_win->GetEventHandler()->ProcessEvent(event1) )
149 {
150 // and then send a pseudo mouse-move event to refresh the selection
151 wxMouseEvent event2(wxEVT_MOTION);
152 wxGetMousePosition(&event2.m_x, &event2.m_y);
153
154 // the mouse event coordinates should be client, not screen as
155 // returned by wxGetMousePosition
156 wxWindow *parentTop = m_win;
157 while ( parentTop->GetParent() )
158 parentTop = parentTop->GetParent();
159 wxPoint ptOrig = parentTop->GetPosition();
160 event2.m_x -= ptOrig.x;
161 event2.m_y -= ptOrig.y;
162
163 event2.SetEventObject(m_win);
164
165 // FIXME: we don't fill in the other members - ok?
166
167 m_win->GetEventHandler()->ProcessEvent(event2);
168 }
169 else // can't scroll further, stop
170 {
171 Stop();
172 }
173 }
174}
4b316329 175#endif
1e6feb95
VZ
176
177// ----------------------------------------------------------------------------
178// wxScrollHelperEvtHandler
179// ----------------------------------------------------------------------------
180
181bool wxScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
182{
9a268018 183 wxEventType evType = event.GetEventType();
2feed004 184
349efbaa
VZ
185 // the explanation of wxEVT_PAINT processing hack: for historic reasons
186 // there are 2 ways to process this event in classes deriving from
187 // wxScrolledWindow. The user code may
188 //
189 // 1. override wxScrolledWindow::OnDraw(dc)
190 // 2. define its own OnPaint() handler
191 //
192 // In addition, in wxUniversal wxWindow defines OnPaint() itself and
193 // always processes the draw event, so we can't just try the window
194 // OnPaint() first and call our HandleOnPaint() if it doesn't process it
195 // (the latter would never be called in wxUniversal).
196 //
197 // So the solution is to have a flag telling us whether the user code drew
198 // anything in the window. We set it to true here but reset it to false in
199 // wxScrolledWindow::OnPaint() handler (which wouldn't be called if the
200 // user code defined OnPaint() in the derived class)
ca65c044 201 m_hasDrawnWindow = true;
349efbaa 202
ff186591
VZ
203 // pass it on to the real handler
204 bool processed = wxEvtHandler::ProcessEvent(event);
205
206 // always process the size events ourselves, even if the user code handles
207 // them as well, as we need to AdjustScrollbars()
208 //
209 // NB: it is important to do it after processing the event in the normal
210 // way as HandleOnSize() may generate a wxEVT_SIZE itself if the
211 // scrollbar[s] (dis)appear and it should be seen by the user code
212 // after this one
213 if ( evType == wxEVT_SIZE )
214 {
215 m_scrollHelper->HandleOnSize((wxSizeEvent &)event);
216
ca65c044 217 return true;
ff186591
VZ
218 }
219
220 if ( processed )
349efbaa
VZ
221 {
222 // normally, nothing more to do here - except if it was a paint event
223 // which wasn't really processed, then we'll try to call our
224 // OnDraw() below (from HandleOnPaint)
c6b81ceb 225 if ( m_hasDrawnWindow || event.IsCommandEvent() )
349efbaa 226 {
ca65c044 227 return true;
349efbaa
VZ
228 }
229 }
2feed004 230
ca65c044 231 // reset the skipped flag to false as it might have been set to true in
1e6feb95 232 // ProcessEvent() above
ca65c044 233 event.Skip(false);
1e6feb95 234
e421922f
VZ
235 if ( evType == wxEVT_PAINT )
236 {
237 m_scrollHelper->HandleOnPaint((wxPaintEvent &)event);
ca65c044 238 return true;
e421922f 239 }
1e6feb95 240
e421922f
VZ
241 if ( evType == wxEVT_SCROLLWIN_TOP ||
242 evType == wxEVT_SCROLLWIN_BOTTOM ||
243 evType == wxEVT_SCROLLWIN_LINEUP ||
244 evType == wxEVT_SCROLLWIN_LINEDOWN ||
245 evType == wxEVT_SCROLLWIN_PAGEUP ||
246 evType == wxEVT_SCROLLWIN_PAGEDOWN ||
247 evType == wxEVT_SCROLLWIN_THUMBTRACK ||
248 evType == wxEVT_SCROLLWIN_THUMBRELEASE )
249 {
250 m_scrollHelper->HandleOnScroll((wxScrollWinEvent &)event);
1e6feb95 251 return !event.GetSkipped();
e421922f 252 }
1e6feb95 253
e421922f
VZ
254 if ( evType == wxEVT_ENTER_WINDOW )
255 {
256 m_scrollHelper->HandleOnMouseEnter((wxMouseEvent &)event);
257 }
258 else if ( evType == wxEVT_LEAVE_WINDOW )
259 {
260 m_scrollHelper->HandleOnMouseLeave((wxMouseEvent &)event);
261 }
262#if wxUSE_MOUSEWHEEL
263 else if ( evType == wxEVT_MOUSEWHEEL )
264 {
265 m_scrollHelper->HandleOnMouseWheel((wxMouseEvent &)event);
266 }
267#endif // wxUSE_MOUSEWHEEL
e421922f
VZ
268 else if ( evType == wxEVT_CHAR )
269 {
270 m_scrollHelper->HandleOnChar((wxKeyEvent &)event);
271 return !event.GetSkipped();
1e6feb95
VZ
272 }
273
ca65c044 274 return false;
1e6feb95
VZ
275}
276
277// ----------------------------------------------------------------------------
278// wxScrollHelper construction
279// ----------------------------------------------------------------------------
280
281wxScrollHelper::wxScrollHelper(wxWindow *win)
282{
d32e78bd
VZ
283 wxASSERT_MSG( win, _T("associated window can't be NULL in wxScrollHelper") );
284
1e6feb95
VZ
285 m_xScrollPixelsPerLine =
286 m_yScrollPixelsPerLine =
287 m_xScrollPosition =
288 m_yScrollPosition =
289 m_xScrollLines =
290 m_yScrollLines =
291 m_xScrollLinesPerPage =
139adb6a 292 m_yScrollLinesPerPage = 0;
1e6feb95
VZ
293
294 m_xScrollingEnabled =
ca65c044 295 m_yScrollingEnabled = true;
1e6feb95
VZ
296
297 m_scaleX =
139adb6a 298 m_scaleY = 1.0;
24ce4c18 299#if wxUSE_MOUSEWHEEL
d2c52078 300 m_wheelRotation = 0;
24ce4c18 301#endif
a58a12e9 302
1e6feb95
VZ
303 m_win =
304 m_targetWindow = (wxWindow *)NULL;
139adb6a 305
1e6feb95 306 m_timerAutoScroll = (wxTimer *)NULL;
d7da9756 307
349efbaa
VZ
308 m_handler = NULL;
309
d32e78bd
VZ
310 m_win = win;
311
312 // by default, the associated window is also the target window
313 DoSetTargetWindow(win);
1e6feb95 314}
d7da9756 315
1e6feb95 316wxScrollHelper::~wxScrollHelper()
d80cd92a 317{
1e6feb95
VZ
318 StopAutoScrolling();
319
349efbaa 320 DeleteEvtHandler();
d80cd92a
VZ
321}
322
323// ----------------------------------------------------------------------------
324// setting scrolling parameters
325// ----------------------------------------------------------------------------
326
1e6feb95
VZ
327void wxScrollHelper::SetScrollbars(int pixelsPerUnitX,
328 int pixelsPerUnitY,
329 int noUnitsX,
330 int noUnitsY,
331 int xPos,
332 int yPos,
333 bool noRefresh)
c801d85f 334{
b0486e0d
SN
335 int xpos, ypos;
336
337 CalcUnscrolledPosition(xPos, yPos, &xpos, &ypos);
139adb6a
RR
338 bool do_refresh =
339 (
c801d85f 340 (noUnitsX != 0 && m_xScrollLines == 0) ||
566d84a7 341 (noUnitsX < m_xScrollLines && xpos > pixelsPerUnitX * noUnitsX) ||
b0486e0d 342
c801d85f 343 (noUnitsY != 0 && m_yScrollLines == 0) ||
566d84a7 344 (noUnitsY < m_yScrollLines && ypos > pixelsPerUnitY * noUnitsY) ||
c801d85f 345 (xPos != m_xScrollPosition) ||
b0486e0d 346 (yPos != m_yScrollPosition)
139adb6a 347 );
a58a12e9 348
139adb6a
RR
349 m_xScrollPixelsPerLine = pixelsPerUnitX;
350 m_yScrollPixelsPerLine = pixelsPerUnitY;
351 m_xScrollPosition = xPos;
352 m_yScrollPosition = yPos;
a91b47e8 353
150c8d89
RL
354 int w = noUnitsX * pixelsPerUnitX;
355 int h = noUnitsY * pixelsPerUnitY;
356
2b5f62a0
VZ
357 // For better backward compatibility we set persisting limits
358 // here not just the size. It makes SetScrollbars 'sticky'
359 // emulating the old non-autoscroll behaviour.
2b43d588 360 // m_targetWindow->SetVirtualSizeHints( w, h );
a58a12e9 361
2b5f62a0
VZ
362 // The above should arguably be deprecated, this however we still need.
363
f18f464c
VZ
364 // take care not to set 0 virtual size, 0 means that we don't have any
365 // scrollbars and hence we should use the real size instead of the virtual
366 // one which is indicated by using wxDefaultCoord
367 m_targetWindow->SetVirtualSize( w ? w : wxDefaultCoord,
368 h ? h : wxDefaultCoord);
dc429f89 369
a58a12e9 370 if (do_refresh && !noRefresh)
ca65c044 371 m_targetWindow->Refresh(true, GetScrollRect());
a58a12e9 372
1f066698
VZ
373#ifndef __WXUNIVERSAL__
374 // If the target is not the same as the window with the scrollbars,
375 // then we need to update the scrollbars here, since they won't have
376 // been updated by SetVirtualSize().
377 if ( m_targetWindow != m_win )
378#endif // !__WXUNIVERSAL__
379 {
380 AdjustScrollbars();
381 }
382#ifndef __WXUNIVERSAL__
383 else
384 {
385 // otherwise this has been done by AdjustScrollbars, above
1f066698
VZ
386 }
387#endif // !__WXUNIVERSAL__
c801d85f
KB
388}
389
d80cd92a 390// ----------------------------------------------------------------------------
af4088f1 391// [target] window handling
d80cd92a 392// ----------------------------------------------------------------------------
ecab4dba 393
349efbaa 394void wxScrollHelper::DeleteEvtHandler()
ecab4dba 395{
248d771c
VZ
396 // search for m_handler in the handler list
397 if ( m_win && m_handler )
349efbaa 398 {
74f6bbf9 399 if ( m_win->RemoveEventHandler(m_handler) )
248d771c 400 {
74f6bbf9 401 delete m_handler;
248d771c 402 }
74f6bbf9
VZ
403 //else: something is very wrong, so better [maybe] leak memory than
404 // risk a crash because of double deletion
248d771c 405
74f6bbf9 406 m_handler = NULL;
349efbaa
VZ
407 }
408}
409
af4088f1 410void wxScrollHelper::DoSetTargetWindow(wxWindow *target)
349efbaa 411{
ecab4dba 412 m_targetWindow = target;
8adc196b
SC
413#ifdef __WXMAC__
414 target->MacSetClipChildren( true ) ;
415#endif
349efbaa
VZ
416
417 // install the event handler which will intercept the events we're
af4088f1
VZ
418 // interested in (but only do it for our real window, not the target window
419 // which we scroll - we don't need to hijack its events)
420 if ( m_targetWindow == m_win )
13ff9344 421 {
248d771c
VZ
422 // if we already have a handler, delete it first
423 DeleteEvtHandler();
424
13ff9344
JS
425 m_handler = new wxScrollHelperEvtHandler(this);
426 m_targetWindow->PushEventHandler(m_handler);
427 }
349efbaa
VZ
428}
429
af4088f1 430void wxScrollHelper::SetTargetWindow(wxWindow *target)
349efbaa
VZ
431{
432 wxCHECK_RET( target, wxT("target window must not be NULL") );
433
434 if ( target == m_targetWindow )
435 return;
436
af4088f1 437 DoSetTargetWindow(target);
ecab4dba
RR
438}
439
1e6feb95 440wxWindow *wxScrollHelper::GetTargetWindow() const
ecab4dba
RR
441{
442 return m_targetWindow;
443}
444
d80cd92a
VZ
445// ----------------------------------------------------------------------------
446// scrolling implementation itself
447// ----------------------------------------------------------------------------
448
1e6feb95 449void wxScrollHelper::HandleOnScroll(wxScrollWinEvent& event)
c801d85f 450{
139adb6a 451 int nScrollInc = CalcScrollInc(event);
1e6feb95 452 if ( nScrollInc == 0 )
139adb6a 453 {
1e6feb95
VZ
454 // can't scroll further
455 event.Skip();
456
457 return;
139adb6a 458 }
c801d85f 459
1e6feb95 460 int orient = event.GetOrientation();
139adb6a
RR
461 if (orient == wxHORIZONTAL)
462 {
463 m_xScrollPosition += nScrollInc;
5f3286d1 464 m_win->SetScrollPos(wxHORIZONTAL, m_xScrollPosition);
139adb6a 465 }
c801d85f 466 else
139adb6a
RR
467 {
468 m_yScrollPosition += nScrollInc;
5f3286d1 469 m_win->SetScrollPos(wxVERTICAL, m_yScrollPosition);
139adb6a 470 }
a58a12e9 471
ca65c044 472 bool needsRefresh = false;
1e6feb95
VZ
473 int dx = 0,
474 dy = 0;
139adb6a
RR
475 if (orient == wxHORIZONTAL)
476 {
1e6feb95
VZ
477 if ( m_xScrollingEnabled )
478 {
479 dx = -m_xScrollPixelsPerLine * nScrollInc;
480 }
139adb6a 481 else
1e6feb95 482 {
ca65c044 483 needsRefresh = true;
1e6feb95 484 }
139adb6a 485 }
c801d85f 486 else
139adb6a 487 {
1e6feb95
VZ
488 if ( m_yScrollingEnabled )
489 {
490 dy = -m_yScrollPixelsPerLine * nScrollInc;
491 }
139adb6a 492 else
1e6feb95 493 {
ca65c044 494 needsRefresh = true;
1e6feb95
VZ
495 }
496 }
497
498 if ( needsRefresh )
499 {
ca65c044 500 m_targetWindow->Refresh(true, GetScrollRect());
1e6feb95
VZ
501 }
502 else
503 {
d5d58a93 504 m_targetWindow->ScrollWindow(dx, dy, GetScrollRect());
3d2b9c20 505 }
c801d85f
KB
506}
507
1e6feb95 508int wxScrollHelper::CalcScrollInc(wxScrollWinEvent& event)
c801d85f 509{
ecab4dba
RR
510 int pos = event.GetPosition();
511 int orient = event.GetOrientation();
c801d85f 512
ecab4dba 513 int nScrollInc = 0;
1a8caf94 514 if (event.GetEventType() == wxEVT_SCROLLWIN_TOP)
c801d85f 515 {
ecab4dba
RR
516 if (orient == wxHORIZONTAL)
517 nScrollInc = - m_xScrollPosition;
518 else
519 nScrollInc = - m_yScrollPosition;
1a8caf94
RR
520 } else
521 if (event.GetEventType() == wxEVT_SCROLLWIN_BOTTOM)
522 {
ecab4dba
RR
523 if (orient == wxHORIZONTAL)
524 nScrollInc = m_xScrollLines - m_xScrollPosition;
525 else
526 nScrollInc = m_yScrollLines - m_yScrollPosition;
1a8caf94
RR
527 } else
528 if (event.GetEventType() == wxEVT_SCROLLWIN_LINEUP)
529 {
ecab4dba 530 nScrollInc = -1;
1a8caf94
RR
531 } else
532 if (event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN)
533 {
ecab4dba 534 nScrollInc = 1;
1a8caf94
RR
535 } else
536 if (event.GetEventType() == wxEVT_SCROLLWIN_PAGEUP)
537 {
ecab4dba
RR
538 if (orient == wxHORIZONTAL)
539 nScrollInc = -GetScrollPageSize(wxHORIZONTAL);
540 else
541 nScrollInc = -GetScrollPageSize(wxVERTICAL);
1a8caf94
RR
542 } else
543 if (event.GetEventType() == wxEVT_SCROLLWIN_PAGEDOWN)
544 {
ecab4dba
RR
545 if (orient == wxHORIZONTAL)
546 nScrollInc = GetScrollPageSize(wxHORIZONTAL);
547 else
548 nScrollInc = GetScrollPageSize(wxVERTICAL);
1a8caf94
RR
549 } else
550 if ((event.GetEventType() == wxEVT_SCROLLWIN_THUMBTRACK) ||
551 (event.GetEventType() == wxEVT_SCROLLWIN_THUMBRELEASE))
552 {
ecab4dba
RR
553 if (orient == wxHORIZONTAL)
554 nScrollInc = pos - m_xScrollPosition;
555 else
556 nScrollInc = pos - m_yScrollPosition;
c801d85f 557 }
88150e60 558
ecab4dba
RR
559 if (orient == wxHORIZONTAL)
560 {
a58a12e9 561 if (m_xScrollPixelsPerLine > 0)
ecab4dba 562 {
878ddad5
RR
563 if ( m_xScrollPosition + nScrollInc < 0 )
564 {
565 // As -ve as we can go
566 nScrollInc = -m_xScrollPosition;
567 }
568 else // check for the other bound
569 {
570 const int posMax = m_xScrollLines - m_xScrollLinesPerPage;
571 if ( m_xScrollPosition + nScrollInc > posMax )
572 {
573 // As +ve as we can go
574 nScrollInc = posMax - m_xScrollPosition;
575 }
576 }
ecab4dba
RR
577 }
578 else
ca65c044 579 m_targetWindow->Refresh(true, GetScrollRect());
9d9355c6
VZ
580 }
581 else
ecab4dba 582 {
be9b0663 583 if ( m_yScrollPixelsPerLine > 0 )
d80cd92a 584 {
be9b0663
VZ
585 if ( m_yScrollPosition + nScrollInc < 0 )
586 {
587 // As -ve as we can go
588 nScrollInc = -m_yScrollPosition;
589 }
590 else // check for the other bound
591 {
592 const int posMax = m_yScrollLines - m_yScrollLinesPerPage;
593 if ( m_yScrollPosition + nScrollInc > posMax )
594 {
595 // As +ve as we can go
596 nScrollInc = posMax - m_yScrollPosition;
597 }
598 }
ecab4dba
RR
599 }
600 else
be9b0663
VZ
601 {
602 // VZ: why do we do this? (FIXME)
ca65c044 603 m_targetWindow->Refresh(true, GetScrollRect());
be9b0663 604 }
9d9355c6 605 }
9d9355c6 606
ecab4dba 607 return nScrollInc;
c801d85f
KB
608}
609
610// Adjust the scrollbars - new version.
1e6feb95 611void wxScrollHelper::AdjustScrollbars()
c801d85f 612{
2a201ef8
VZ
613 static wxRecursionGuardFlag s_flagReentrancy;
614 wxRecursionGuard guard(s_flagReentrancy);
615 if ( guard.IsInside() )
616 {
617 // don't reenter AdjustScrollbars() while another call to
618 // AdjustScrollbars() is in progress because this may lead to calling
619 // ScrollWindow() twice and this can really happen under MSW if
620 // SetScrollbar() call below adds or removes the scrollbar which
621 // changes the window size and hence results in another
622 // AdjustScrollbars() call
623 return;
624 }
625
566d84a7
RL
626 int w = 0, h = 0;
627 int oldw, oldh;
a58a12e9 628
27d029c7
RR
629 int oldXScroll = m_xScrollPosition;
630 int oldYScroll = m_yScrollPosition;
c801d85f 631
be9b0663
VZ
632 // VZ: at least under Windows this loop is useless because when scrollbars
633 // [dis]appear we get a WM_SIZE resulting in another call to
634 // AdjustScrollbars() anyhow. As it doesn't seem to do any harm I leave
635 // it here for now but it would be better to ensure that all ports
636 // generate EVT_SIZE when scrollbars [dis]appear, emulating it if
637 // necessary, and remove it later
870c86bc
JS
638 // JACS: Stop potential infinite loop by limiting number of iterations
639 int iterationCount = 0;
640 const int iterationMax = 5;
be9b0663
VZ
641 do
642 {
870c86bc 643 iterationCount ++;
d32e78bd 644
566d84a7 645 GetTargetSize(&w, 0);
c801d85f 646
878ddad5
RR
647 // scroll lines per page: if 0, no scrolling is needed
648 int linesPerPage;
649
650 if ( m_xScrollPixelsPerLine == 0 )
566d84a7 651 {
878ddad5 652 // scrolling is disabled
566d84a7
RL
653 m_xScrollLines = 0;
654 m_xScrollPosition = 0;
878ddad5 655 linesPerPage = 0;
566d84a7 656 }
878ddad5 657 else // might need scrolling
566d84a7 658 {
878ddad5
RR
659 // Round up integer division to catch any "leftover" client space.
660 const int wVirt = m_targetWindow->GetVirtualSize().GetWidth();
661 m_xScrollLines = (wVirt + m_xScrollPixelsPerLine - 1) / m_xScrollPixelsPerLine;
566d84a7
RL
662
663 // Calculate page size i.e. number of scroll units you get on the
878ddad5
RR
664 // current client window.
665 linesPerPage = w / m_xScrollPixelsPerLine;
666
667 // Special case. When client and virtual size are very close but
668 // the client is big enough, kill scrollbar.
669 if ((linesPerPage < m_xScrollLines) && (w >= wVirt)) ++linesPerPage;
670
671 if (linesPerPage >= m_xScrollLines)
672 {
673 // we're big enough to not need scrolling
674 linesPerPage =
675 m_xScrollLines =
676 m_xScrollPosition = 0;
677 }
678 else // we do need a scrollbar
679 {
680 if ( linesPerPage < 1 )
681 linesPerPage = 1;
682
683 // Correct position if greater than extent of canvas minus
684 // the visible portion of it or if below zero
685 const int posMax = m_xScrollLines - linesPerPage;
686 if ( m_xScrollPosition > posMax )
687 m_xScrollPosition = posMax;
688 else if ( m_xScrollPosition < 0 )
689 m_xScrollPosition = 0;
690 }
566d84a7 691 }
c801d85f 692
878ddad5
RR
693 m_win->SetScrollbar(wxHORIZONTAL, m_xScrollPosition,
694 linesPerPage, m_xScrollLines);
a58a12e9 695
878ddad5
RR
696 // The amount by which we scroll when paging
697 SetScrollPageSize(wxHORIZONTAL, linesPerPage);
698
699 GetTargetSize(0, &h);
be9b0663
VZ
700
701 if ( m_yScrollPixelsPerLine == 0 )
566d84a7 702 {
be9b0663 703 // scrolling is disabled
566d84a7
RL
704 m_yScrollLines = 0;
705 m_yScrollPosition = 0;
be9b0663 706 linesPerPage = 0;
566d84a7 707 }
be9b0663 708 else // might need scrolling
566d84a7 709 {
878ddad5
RR
710 // Round up integer division to catch any "leftover" client space.
711 const int hVirt = m_targetWindow->GetVirtualSize().GetHeight();
712 m_yScrollLines = ( hVirt + m_yScrollPixelsPerLine - 1 ) / m_yScrollPixelsPerLine;
566d84a7
RL
713
714 // Calculate page size i.e. number of scroll units you get on the
878ddad5 715 // current client window.
be9b0663 716 linesPerPage = h / m_yScrollPixelsPerLine;
878ddad5
RR
717
718 // Special case. When client and virtual size are very close but
719 // the client is big enough, kill scrollbar.
720 if ((linesPerPage < m_yScrollLines) && (h >= hVirt)) ++linesPerPage;
721
722 if (linesPerPage >= m_yScrollLines)
be9b0663
VZ
723 {
724 // we're big enough to not need scrolling
725 linesPerPage =
726 m_yScrollLines =
727 m_yScrollPosition = 0;
728 }
729 else // we do need a scrollbar
730 {
731 if ( linesPerPage < 1 )
732 linesPerPage = 1;
733
734 // Correct position if greater than extent of canvas minus
735 // the visible portion of it or if below zero
736 const int posMax = m_yScrollLines - linesPerPage;
737 if ( m_yScrollPosition > posMax )
738 m_yScrollPosition = posMax;
739 else if ( m_yScrollPosition < 0 )
740 m_yScrollPosition = 0;
741 }
742 }
566d84a7 743
be9b0663
VZ
744 m_win->SetScrollbar(wxVERTICAL, m_yScrollPosition,
745 linesPerPage, m_yScrollLines);
566d84a7 746
be9b0663
VZ
747 // The amount by which we scroll when paging
748 SetScrollPageSize(wxVERTICAL, linesPerPage);
c801d85f 749
139adb6a 750
be9b0663 751 // If a scrollbar (dis)appeared as a result of this, adjust them again.
566d84a7
RL
752 oldw = w;
753 oldh = h;
754
755 GetTargetSize( &w, &h );
870c86bc 756 } while ( (w != oldw || h != oldh) && (iterationCount < iterationMax) );
566d84a7
RL
757
758#ifdef __WXMOTIF__
759 // Sorry, some Motif-specific code to implement a backing pixmap
760 // for the wxRETAINED style. Implementing a backing store can't
761 // be entirely generic because it relies on the wxWindowDC implementation
762 // to duplicate X drawing calls for the backing pixmap.
763
764 if ( m_targetWindow->GetWindowStyle() & wxRETAINED )
139adb6a 765 {
566d84a7
RL
766 Display* dpy = XtDisplay((Widget)m_targetWindow->GetMainWidget());
767
768 int totalPixelWidth = m_xScrollLines * m_xScrollPixelsPerLine;
769 int totalPixelHeight = m_yScrollLines * m_yScrollPixelsPerLine;
770 if (m_targetWindow->GetBackingPixmap() &&
771 !((m_targetWindow->GetPixmapWidth() == totalPixelWidth) &&
772 (m_targetWindow->GetPixmapHeight() == totalPixelHeight)))
773 {
774 XFreePixmap (dpy, (Pixmap) m_targetWindow->GetBackingPixmap());
775 m_targetWindow->SetBackingPixmap((WXPixmap) 0);
776 }
777
778 if (!m_targetWindow->GetBackingPixmap() &&
9b66c26a 779 (m_xScrollLines != 0) && (m_yScrollLines != 0))
566d84a7
RL
780 {
781 int depth = wxDisplayDepth();
782 m_targetWindow->SetPixmapWidth(totalPixelWidth);
783 m_targetWindow->SetPixmapHeight(totalPixelHeight);
784 m_targetWindow->SetBackingPixmap((WXPixmap) XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
785 m_targetWindow->GetPixmapWidth(), m_targetWindow->GetPixmapHeight(), depth));
786 }
787
139adb6a 788 }
566d84a7 789#endif // Motif
a58a12e9 790
27d029c7
RR
791 if (oldXScroll != m_xScrollPosition)
792 {
793 if (m_xScrollingEnabled)
566d84a7 794 m_targetWindow->ScrollWindow( m_xScrollPixelsPerLine * (oldXScroll - m_xScrollPosition), 0,
d5d58a93 795 GetScrollRect() );
27d029c7 796 else
ca65c044 797 m_targetWindow->Refresh(true, GetScrollRect());
27d029c7 798 }
a58a12e9 799
27d029c7
RR
800 if (oldYScroll != m_yScrollPosition)
801 {
802 if (m_yScrollingEnabled)
1e6feb95 803 m_targetWindow->ScrollWindow( 0, m_yScrollPixelsPerLine * (oldYScroll-m_yScrollPosition),
d5d58a93 804 GetScrollRect() );
27d029c7 805 else
ca65c044 806 m_targetWindow->Refresh(true, GetScrollRect());
27d029c7 807 }
c801d85f
KB
808}
809
1e6feb95 810void wxScrollHelper::DoPrepareDC(wxDC& dc)
c801d85f 811{
1e6feb95
VZ
812 wxPoint pt = dc.GetDeviceOrigin();
813 dc.SetDeviceOrigin( pt.x - m_xScrollPosition * m_xScrollPixelsPerLine,
814 pt.y - m_yScrollPosition * m_yScrollPixelsPerLine );
139adb6a 815 dc.SetUserScale( m_scaleX, m_scaleY );
c801d85f
KB
816}
817
566d84a7
RL
818void wxScrollHelper::SetScrollRate( int xstep, int ystep )
819{
820 int old_x = m_xScrollPixelsPerLine * m_xScrollPosition;
821 int old_y = m_yScrollPixelsPerLine * m_yScrollPosition;
822
823 m_xScrollPixelsPerLine = xstep;
824 m_yScrollPixelsPerLine = ystep;
825
826 int new_x = m_xScrollPixelsPerLine * m_xScrollPosition;
827 int new_y = m_yScrollPixelsPerLine * m_yScrollPosition;
828
829 m_win->SetScrollPos( wxHORIZONTAL, m_xScrollPosition );
830 m_win->SetScrollPos( wxVERTICAL, m_yScrollPosition );
831 m_targetWindow->ScrollWindow( old_x - new_x, old_y - new_y );
832
833 AdjustScrollbars();
834}
835
1e6feb95 836void wxScrollHelper::GetScrollPixelsPerUnit (int *x_unit, int *y_unit) const
c801d85f 837{
a0bc2c1d
VZ
838 if ( x_unit )
839 *x_unit = m_xScrollPixelsPerLine;
840 if ( y_unit )
841 *y_unit = m_yScrollPixelsPerLine;
c801d85f
KB
842}
843
1e6feb95 844int wxScrollHelper::GetScrollPageSize(int orient) const
c801d85f
KB
845{
846 if ( orient == wxHORIZONTAL )
847 return m_xScrollLinesPerPage;
848 else
849 return m_yScrollLinesPerPage;
850}
851
1e6feb95 852void wxScrollHelper::SetScrollPageSize(int orient, int pageSize)
c801d85f
KB
853{
854 if ( orient == wxHORIZONTAL )
855 m_xScrollLinesPerPage = pageSize;
856 else
857 m_yScrollLinesPerPage = pageSize;
858}
859
860/*
861 * Scroll to given position (scroll position, not pixel position)
862 */
1e6feb95 863void wxScrollHelper::Scroll( int x_pos, int y_pos )
c801d85f 864{
8b089c5e
JS
865 if (!m_targetWindow)
866 return;
867
a58a12e9 868 if (((x_pos == -1) || (x_pos == m_xScrollPosition)) &&
139adb6a 869 ((y_pos == -1) || (y_pos == m_yScrollPosition))) return;
a58a12e9 870
8d7eaf91 871 int w = 0, h = 0;
1e6feb95 872 GetTargetSize(&w, &h);
c801d85f 873
8775b357 874 if ((x_pos != -1) && (m_xScrollPixelsPerLine))
c801d85f 875 {
ed673c6a 876 int old_x = m_xScrollPosition;
139adb6a 877 m_xScrollPosition = x_pos;
a58a12e9 878
3d2b9c20
RR
879 // Calculate page size i.e. number of scroll units you get on the
880 // current client window
ecab4dba 881 int noPagePositions = (int) ( (w/(double)m_xScrollPixelsPerLine) + 0.5 );
139adb6a
RR
882 if (noPagePositions < 1) noPagePositions = 1;
883
884 // Correct position if greater than extent of canvas minus
3d2b9c20 885 // the visible portion of it or if below zero
139adb6a
RR
886 m_xScrollPosition = wxMin( m_xScrollLines-noPagePositions, m_xScrollPosition );
887 m_xScrollPosition = wxMax( 0, m_xScrollPosition );
a58a12e9 888
f6bcfd97 889 if (old_x != m_xScrollPosition) {
5f3286d1 890 m_win->SetScrollPos( wxHORIZONTAL, m_xScrollPosition );
1e6feb95 891 m_targetWindow->ScrollWindow( (old_x-m_xScrollPosition)*m_xScrollPixelsPerLine, 0,
d5d58a93 892 GetScrollRect() );
f6bcfd97 893 }
c801d85f 894 }
8775b357 895 if ((y_pos != -1) && (m_yScrollPixelsPerLine))
c801d85f 896 {
ed673c6a 897 int old_y = m_yScrollPosition;
139adb6a 898 m_yScrollPosition = y_pos;
a58a12e9 899
3d2b9c20
RR
900 // Calculate page size i.e. number of scroll units you get on the
901 // current client window
ecab4dba 902 int noPagePositions = (int) ( (h/(double)m_yScrollPixelsPerLine) + 0.5 );
139adb6a
RR
903 if (noPagePositions < 1) noPagePositions = 1;
904
905 // Correct position if greater than extent of canvas minus
3d2b9c20 906 // the visible portion of it or if below zero
139adb6a
RR
907 m_yScrollPosition = wxMin( m_yScrollLines-noPagePositions, m_yScrollPosition );
908 m_yScrollPosition = wxMax( 0, m_yScrollPosition );
d2c52078 909
f6bcfd97 910 if (old_y != m_yScrollPosition) {
5f3286d1 911 m_win->SetScrollPos( wxVERTICAL, m_yScrollPosition );
1e6feb95 912 m_targetWindow->ScrollWindow( 0, (old_y-m_yScrollPosition)*m_yScrollPixelsPerLine,
d5d58a93 913 GetScrollRect() );
f6bcfd97 914 }
c801d85f 915 }
c801d85f
KB
916}
917
1e6feb95 918void wxScrollHelper::EnableScrolling (bool x_scroll, bool y_scroll)
c801d85f 919{
139adb6a
RR
920 m_xScrollingEnabled = x_scroll;
921 m_yScrollingEnabled = y_scroll;
c801d85f
KB
922}
923
c801d85f 924// Where the current view starts from
1e6feb95 925void wxScrollHelper::GetViewStart (int *x, int *y) const
c801d85f 926{
a0bc2c1d
VZ
927 if ( x )
928 *x = m_xScrollPosition;
929 if ( y )
930 *y = m_yScrollPosition;
c801d85f
KB
931}
932
8c2f3797 933void wxScrollHelper::DoCalcScrolledPosition(int x, int y, int *xx, int *yy) const
c801d85f 934{
a0bc2c1d
VZ
935 if ( xx )
936 *xx = x - m_xScrollPosition * m_xScrollPixelsPerLine;
937 if ( yy )
938 *yy = y - m_yScrollPosition * m_yScrollPixelsPerLine;
c801d85f
KB
939}
940
8c2f3797 941void wxScrollHelper::DoCalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
c801d85f 942{
a0bc2c1d
VZ
943 if ( xx )
944 *xx = x + m_xScrollPosition * m_xScrollPixelsPerLine;
945 if ( yy )
946 *yy = y + m_yScrollPosition * m_yScrollPixelsPerLine;
c801d85f 947}
d80cd92a 948
d32e78bd
VZ
949// ----------------------------------------------------------------------------
950// geometry
951// ----------------------------------------------------------------------------
952
953bool wxScrollHelper::ScrollLayout()
954{
955 if ( m_win->GetSizer() && m_targetWindow == m_win )
956 {
957 // If we're the scroll target, take into account the
958 // virtual size and scrolled position of the window.
959
8d7eaf91 960 int x = 0, y = 0, w = 0, h = 0;
d32e78bd
VZ
961 CalcScrolledPosition(0,0, &x,&y);
962 m_win->GetVirtualSize(&w, &h);
963 m_win->GetSizer()->SetDimension(x, y, w, h);
964 return true;
965 }
966
967 // fall back to default for LayoutConstraints
968 return m_win->wxWindow::Layout();
969}
970
971void wxScrollHelper::ScrollDoSetVirtualSize(int x, int y)
972{
973 m_win->wxWindow::DoSetVirtualSize( x, y );
974 AdjustScrollbars();
975
976 if (m_win->GetAutoLayout())
977 m_win->Layout();
978}
979
980// wxWindow's GetBestVirtualSize returns the actual window size,
981// whereas we want to return the virtual size
982wxSize wxScrollHelper::ScrollGetBestVirtualSize() const
983{
984 wxSize clientSize(m_win->GetClientSize());
985 if ( m_win->GetSizer() )
986 clientSize.IncTo(m_win->GetSizer()->CalcMin());
987
988 return clientSize;
989}
990
991// return the window best size from the given best virtual size
992wxSize
993wxScrollHelper::ScrollGetWindowSizeForVirtualSize(const wxSize& size) const
994{
995 // Only use the content to set the window size in the direction
996 // where there's no scrolling; otherwise we're going to get a huge
997 // window in the direction in which scrolling is enabled
998 int ppuX, ppuY;
999 GetScrollPixelsPerUnit(&ppuX, &ppuY);
1000
1001 wxSize minSize = m_win->GetMinSize();
1002 if ( !minSize.IsFullySpecified() )
1003 minSize = m_win->GetSize();
1004
1005 wxSize best(size);
1006 if (ppuX > 0)
1007 best.x = minSize.x;
1008 if (ppuY > 0)
1009 best.y = minSize.y;
1010
1011 return best;
1012}
1013
d80cd92a
VZ
1014// ----------------------------------------------------------------------------
1015// event handlers
1016// ----------------------------------------------------------------------------
1017
1018// Default OnSize resets scrollbars, if any
1e6feb95 1019void wxScrollHelper::HandleOnSize(wxSizeEvent& WXUNUSED(event))
d80cd92a 1020{
c376d80f 1021 if ( m_targetWindow->GetAutoLayout() )
2b5f62a0 1022 {
c376d80f 1023 wxSize size = m_targetWindow->GetBestVirtualSize();
d32e78bd 1024
c376d80f 1025 // This will call ::Layout() and ::AdjustScrollbars()
168f82ce 1026 m_win->SetVirtualSize( size );
2b5f62a0
VZ
1027 }
1028 else
c376d80f 1029 {
2b5f62a0 1030 AdjustScrollbars();
c376d80f 1031 }
d80cd92a
VZ
1032}
1033
1034// This calls OnDraw, having adjusted the origin according to the current
1035// scroll position
1e6feb95 1036void wxScrollHelper::HandleOnPaint(wxPaintEvent& WXUNUSED(event))
d80cd92a 1037{
af4088f1
VZ
1038 // don't use m_targetWindow here, this is always called for ourselves
1039 wxPaintDC dc(m_win);
1e6feb95 1040 DoPrepareDC(dc);
d80cd92a
VZ
1041
1042 OnDraw(dc);
1043}
1044
438e3558
VZ
1045// kbd handling: notice that we use OnChar() and not OnKeyDown() for
1046// compatibility here - if we used OnKeyDown(), the programs which process
1047// arrows themselves in their OnChar() would never get the message and like
1048// this they always have the priority
1e6feb95 1049void wxScrollHelper::HandleOnChar(wxKeyEvent& event)
d80cd92a 1050{
8d7eaf91
MW
1051 int stx = 0, sty = 0, // view origin
1052 szx = 0, szy = 0, // view size (total)
1053 clix = 0, cliy = 0; // view size (on screen)
d80cd92a 1054
1e6feb95
VZ
1055 GetViewStart(&stx, &sty);
1056 GetTargetSize(&clix, &cliy);
566d84a7 1057 m_targetWindow->GetVirtualSize(&szx, &szy);
56dade3c
RL
1058
1059 if( m_xScrollPixelsPerLine )
1060 {
1061 clix /= m_xScrollPixelsPerLine;
d7da9756 1062 szx /= m_xScrollPixelsPerLine;
56dade3c
RL
1063 }
1064 else
1065 {
1066 clix = 0;
d7da9756 1067 szx = -1;
56dade3c
RL
1068 }
1069 if( m_yScrollPixelsPerLine )
1070 {
1071 cliy /= m_yScrollPixelsPerLine;
1072 szy /= m_yScrollPixelsPerLine;
1073 }
1074 else
1075 {
1076 cliy = 0;
d7da9756 1077 szy = -1;
56dade3c 1078 }
d80cd92a 1079
39c869a6
VZ
1080 int xScrollOld = m_xScrollPosition,
1081 yScrollOld = m_yScrollPosition;
1082
ecb01792 1083 int dsty;
4995ca80 1084 switch ( event.GetKeyCode() )
d80cd92a
VZ
1085 {
1086 case WXK_PAGEUP:
1087 case WXK_PRIOR:
ecb01792
RL
1088 dsty = sty - (5 * cliy / 6);
1089 Scroll(-1, (dsty == -1) ? 0 : dsty);
d80cd92a
VZ
1090 break;
1091
1092 case WXK_PAGEDOWN:
1093 case WXK_NEXT:
1094 Scroll(-1, sty + (5 * cliy / 6));
1095 break;
1096
d80cd92a
VZ
1097 case WXK_HOME:
1098 Scroll(0, event.ControlDown() ? 0 : -1);
1099 break;
1100
1101 case WXK_END:
4acd108c 1102 Scroll(szx - clix, event.ControlDown() ? szy - cliy : -1);
d80cd92a
VZ
1103 break;
1104
1105 case WXK_UP:
1106 Scroll(-1, sty - 1);
1107 break;
1108
1109 case WXK_DOWN:
1110 Scroll(-1, sty + 1);
1111 break;
1112
1113 case WXK_LEFT:
1114 Scroll(stx - 1, -1);
1115 break;
1116
1117 case WXK_RIGHT:
1118 Scroll(stx + 1, -1);
1119 break;
1120
1121 default:
1122 // not for us
1123 event.Skip();
1124 }
39c869a6
VZ
1125
1126 if ( m_xScrollPosition != xScrollOld )
1127 {
1128 wxScrollWinEvent event(wxEVT_SCROLLWIN_THUMBTRACK, m_xScrollPosition,
1129 wxHORIZONTAL);
1130 event.SetEventObject(m_win);
1131 m_win->GetEventHandler()->ProcessEvent(event);
1132 }
1133
1134 if ( m_yScrollPosition != yScrollOld )
1135 {
1136 wxScrollWinEvent event(wxEVT_SCROLLWIN_THUMBTRACK, m_yScrollPosition,
1137 wxVERTICAL);
1138 event.SetEventObject(m_win);
1139 m_win->GetEventHandler()->ProcessEvent(event);
1140 }
d80cd92a 1141}
d2c52078 1142
1e6feb95
VZ
1143// ----------------------------------------------------------------------------
1144// autoscroll stuff: these functions deal with sending fake scroll events when
1145// a captured mouse is being held outside the window
1146// ----------------------------------------------------------------------------
1147
1148bool wxScrollHelper::SendAutoScrollEvents(wxScrollWinEvent& event) const
1149{
1150 // only send the event if the window is scrollable in this direction
1151 wxWindow *win = (wxWindow *)event.GetEventObject();
1152 return win->HasScrollbar(event.GetOrientation());
1153}
1154
1155void wxScrollHelper::StopAutoScrolling()
1156{
4b316329 1157#if wxUSE_TIMER
1e6feb95
VZ
1158 if ( m_timerAutoScroll )
1159 {
1160 delete m_timerAutoScroll;
1161 m_timerAutoScroll = (wxTimer *)NULL;
1162 }
4b316329 1163#endif
1e6feb95 1164}
d2c52078 1165
1e6feb95 1166void wxScrollHelper::HandleOnMouseEnter(wxMouseEvent& event)
d2c52078 1167{
1e6feb95
VZ
1168 StopAutoScrolling();
1169
1170 event.Skip();
1171}
d2c52078 1172
1e6feb95
VZ
1173void wxScrollHelper::HandleOnMouseLeave(wxMouseEvent& event)
1174{
1175 // don't prevent the usual processing of the event from taking place
1176 event.Skip();
1177
1178 // when a captured mouse leave a scrolled window we start generate
1179 // scrolling events to allow, for example, extending selection beyond the
1180 // visible area in some controls
1181 if ( wxWindow::GetCapture() == m_targetWindow )
1182 {
1183 // where is the mouse leaving?
1184 int pos, orient;
1185 wxPoint pt = event.GetPosition();
1186 if ( pt.x < 0 )
1187 {
1188 orient = wxHORIZONTAL;
1189 pos = 0;
1190 }
1191 else if ( pt.y < 0 )
1192 {
1193 orient = wxVERTICAL;
1194 pos = 0;
1195 }
1196 else // we're lower or to the right of the window
1197 {
1198 wxSize size = m_targetWindow->GetClientSize();
1199 if ( pt.x > size.x )
1200 {
1201 orient = wxHORIZONTAL;
1202 pos = m_xScrollLines;
1203 }
1204 else if ( pt.y > size.y )
1205 {
1206 orient = wxVERTICAL;
1207 pos = m_yScrollLines;
1208 }
1209 else // this should be impossible
1210 {
1211 // but seems to happen sometimes under wxMSW - maybe it's a bug
1212 // there but for now just ignore it
1213
1214 //wxFAIL_MSG( _T("can't understand where has mouse gone") );
1215
1216 return;
1217 }
1218 }
1219
1220 // only start the auto scroll timer if the window can be scrolled in
1221 // this direction
1222 if ( !m_targetWindow->HasScrollbar(orient) )
1223 return;
1224
4b316329 1225#if wxUSE_TIMER
1e6feb95
VZ
1226 delete m_timerAutoScroll;
1227 m_timerAutoScroll = new wxAutoScrollTimer
1228 (
1229 m_targetWindow, this,
1230 pos == 0 ? wxEVT_SCROLLWIN_LINEUP
1231 : wxEVT_SCROLLWIN_LINEDOWN,
1232 pos,
1233 orient
1234 );
1235 m_timerAutoScroll->Start(50); // FIXME: make configurable
534b127c
WS
1236#else
1237 wxUnusedVar(pos);
4b316329 1238#endif
1e6feb95
VZ
1239 }
1240}
1241
e421922f
VZ
1242#if wxUSE_MOUSEWHEEL
1243
1e6feb95
VZ
1244void wxScrollHelper::HandleOnMouseWheel(wxMouseEvent& event)
1245{
d2c52078 1246 m_wheelRotation += event.GetWheelRotation();
1e6feb95 1247 int lines = m_wheelRotation / event.GetWheelDelta();
d2c52078
RD
1248 m_wheelRotation -= lines * event.GetWheelDelta();
1249
1e6feb95
VZ
1250 if (lines != 0)
1251 {
1e6feb95 1252
b6b85bdc
JS
1253 wxScrollWinEvent newEvent;
1254
ac6f6ffc 1255 newEvent.SetPosition(0);
b6b85bdc 1256 newEvent.SetOrientation(wxVERTICAL);
687706f5 1257 newEvent.SetEventObject(m_win);
b6b85bdc 1258
9b9337da 1259 if (event.IsPageScroll())
4b056ef5
RD
1260 {
1261 if (lines > 0)
687706f5 1262 newEvent.SetEventType(wxEVT_SCROLLWIN_PAGEUP);
4b056ef5 1263 else
687706f5 1264 newEvent.SetEventType(wxEVT_SCROLLWIN_PAGEDOWN);
4b056ef5 1265
b6b85bdc 1266 m_win->GetEventHandler()->ProcessEvent(newEvent);
4b056ef5
RD
1267 }
1268 else
1269 {
1270 lines *= event.GetLinesPerAction();
1271 if (lines > 0)
687706f5 1272 newEvent.SetEventType(wxEVT_SCROLLWIN_LINEUP);
4b056ef5 1273 else
687706f5 1274 newEvent.SetEventType(wxEVT_SCROLLWIN_LINEDOWN);
b6b85bdc 1275
4b056ef5
RD
1276 int times = abs(lines);
1277 for (; times > 0; times--)
1278 m_win->GetEventHandler()->ProcessEvent(newEvent);
1279 }
d2c52078
RD
1280 }
1281}
1282
e421922f
VZ
1283#endif // wxUSE_MOUSEWHEEL
1284
1e6feb95 1285// ----------------------------------------------------------------------------
d32e78bd 1286// wxScrolledWindow implementation
1e6feb95
VZ
1287// ----------------------------------------------------------------------------
1288
d32e78bd 1289IMPLEMENT_DYNAMIC_CLASS(wxScrolledWindow, wxPanel)
1e6feb95 1290
d32e78bd
VZ
1291BEGIN_EVENT_TABLE(wxScrolledWindow, wxPanel)
1292 EVT_PAINT(wxScrolledWindow::OnPaint)
349efbaa
VZ
1293END_EVENT_TABLE()
1294
d32e78bd 1295bool wxScrolledWindow::Create(wxWindow *parent,
1e6feb95
VZ
1296 wxWindowID id,
1297 const wxPoint& pos,
1298 const wxSize& size,
1299 long style,
1300 const wxString& name)
1301{
1302 m_targetWindow = this;
8adc196b
SC
1303#ifdef __WXMAC__
1304 MacSetClipChildren( true ) ;
1305#endif
1e6feb95 1306
04b6e2f0 1307 bool ok = wxPanel::Create(parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name);
1e6feb95 1308
1e6feb95
VZ
1309 return ok;
1310}
1311
d32e78bd 1312wxScrolledWindow::~wxScrolledWindow()
1e6feb95
VZ
1313{
1314}
d2c52078 1315
d32e78bd 1316void wxScrolledWindow::OnPaint(wxPaintEvent& event)
349efbaa
VZ
1317{
1318 // the user code didn't really draw the window if we got here, so set this
1319 // flag to try to call OnDraw() later
1320 m_handler->ResetDrawnFlag();
1321
1322 event.Skip();
1323}
1324
0cf5b099 1325#ifdef __WXMSW__
d32e78bd 1326WXLRESULT wxScrolledWindow::MSWWindowProc(WXUINT nMsg,
0cf5b099
VZ
1327 WXWPARAM wParam,
1328 WXLPARAM lParam)
1329{
c140b7e7 1330 WXLRESULT rc = wxPanel::MSWWindowProc(nMsg, wParam, lParam);
0cf5b099 1331
4676948b 1332#ifndef __WXWINCE__
0cf5b099
VZ
1333 // we need to process arrows ourselves for scrolling
1334 if ( nMsg == WM_GETDLGCODE )
1335 {
1336 rc |= DLGC_WANTARROWS;
1337 }
4676948b 1338#endif
0cf5b099
VZ
1339
1340 return rc;
1341}
1342
1343#endif // __WXMSW__
1344