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