It is possible for a scrolled window to get focus events from children other than
[wxWidgets.git] / src / generic / scrlwing.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/scrlwing.cpp
3 // Purpose: wxScrolledWindow implementation
4 // Author: Julian Smart
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.
7 // Created: 01/02/97
8 // RCS-ID: $Id$
9 // Copyright: (c) wxWidgets team
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #ifdef __VMS
29 #define XtDisplay XTDISPLAY
30 #endif
31
32 #include "wx/scrolwin.h"
33
34 #ifndef WX_PRECOMP
35 #include "wx/utils.h"
36 #include "wx/panel.h"
37 #include "wx/dcclient.h"
38 #include "wx/timer.h"
39 #include "wx/sizer.h"
40 #include "wx/settings.h"
41 #endif
42
43 #ifdef __WXMAC__
44 #include "wx/scrolbar.h"
45 #endif
46
47 #include "wx/recguard.h"
48
49 #ifdef __WXMSW__
50 #include <windows.h> // for DLGC_WANTARROWS
51 #include "wx/msw/winundef.h"
52 #endif
53
54 #ifdef __WXMOTIF__
55 // For wxRETAINED implementation
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
60 #include <Xm/Xm.h>
61 #ifdef __VMS__
62 # pragma message enable nosimpint
63 #endif
64 #endif
65
66 /*
67 TODO PROPERTIES
68 style wxHSCROLL | wxVSCROLL
69 */
70
71 // ----------------------------------------------------------------------------
72 // wxScrollHelperEvtHandler: intercept the events from the window and forward
73 // them to wxScrollHelper
74 // ----------------------------------------------------------------------------
75
76 class WXDLLEXPORT wxScrollHelperEvtHandler : public wxEvtHandler
77 {
78 public:
79 wxScrollHelperEvtHandler(wxScrollHelper *scrollHelper)
80 {
81 m_scrollHelper = scrollHelper;
82 }
83
84 virtual bool ProcessEvent(wxEvent& event);
85
86 void ResetDrawnFlag() { m_hasDrawnWindow = false; }
87
88 private:
89 wxScrollHelper *m_scrollHelper;
90
91 bool m_hasDrawnWindow;
92
93 DECLARE_NO_COPY_CLASS(wxScrollHelperEvtHandler)
94 };
95
96 #if wxUSE_TIMER
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
102 class wxAutoScrollTimer : public wxTimer
103 {
104 public:
105 wxAutoScrollTimer(wxWindow *winToScroll, wxScrollHelper *scroll,
106 wxEventType eventTypeToSend,
107 int pos, int orient);
108
109 virtual void Notify();
110
111 private:
112 wxWindow *m_win;
113 wxScrollHelper *m_scrollHelper;
114 wxEventType m_eventType;
115 int m_pos,
116 m_orient;
117
118 DECLARE_NO_COPY_CLASS(wxAutoScrollTimer)
119 };
120
121 // ============================================================================
122 // implementation
123 // ============================================================================
124
125 // ----------------------------------------------------------------------------
126 // wxAutoScrollTimer
127 // ----------------------------------------------------------------------------
128
129 wxAutoScrollTimer::wxAutoScrollTimer(wxWindow *winToScroll,
130 wxScrollHelper *scroll,
131 wxEventType eventTypeToSend,
132 int pos, int orient)
133 {
134 m_win = winToScroll;
135 m_scrollHelper = scroll;
136 m_eventType = eventTypeToSend;
137 m_pos = pos;
138 m_orient = orient;
139 }
140
141 void wxAutoScrollTimer::Notify()
142 {
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 }
181 #endif
182
183 // ----------------------------------------------------------------------------
184 // wxScrollHelperEvtHandler
185 // ----------------------------------------------------------------------------
186
187 bool wxScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
188 {
189 wxEventType evType = event.GetEventType();
190
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)
207 m_hasDrawnWindow = true;
208
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
223 return true;
224 }
225
226 if ( processed )
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)
231 if ( m_hasDrawnWindow || event.IsCommandEvent() )
232 {
233 return true;
234 }
235 }
236
237 if ( evType == wxEVT_PAINT )
238 {
239 m_scrollHelper->HandleOnPaint((wxPaintEvent &)event);
240 return true;
241 }
242
243 if ( evType == wxEVT_CHILD_FOCUS )
244 {
245 m_scrollHelper->HandleOnChildFocus((wxChildFocusEvent &)event);
246 return true;
247 }
248
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
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 {
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 }
274 }
275
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
285 // Use GTK's own scroll wheel handling in GtkScrolledWindow
286 #ifndef __WXGTK20__
287 else if ( evType == wxEVT_MOUSEWHEEL )
288 {
289 m_scrollHelper->HandleOnMouseWheel((wxMouseEvent &)event);
290 return true;
291 }
292 #endif
293 #endif // wxUSE_MOUSEWHEEL
294 else if ( evType == wxEVT_CHAR )
295 {
296 m_scrollHelper->HandleOnChar((wxKeyEvent &)event);
297 if ( !event.GetSkipped() )
298 {
299 processed = true;
300 wasSkipped = false;
301 }
302 }
303
304 if ( processed )
305 event.Skip(wasSkipped);
306
307 return processed;
308 }
309
310 // ----------------------------------------------------------------------------
311 // wxScrollHelper construction
312 // ----------------------------------------------------------------------------
313
314 wxScrollHelper::wxScrollHelper(wxWindow *win)
315 {
316 wxASSERT_MSG( win, _T("associated window can't be NULL in wxScrollHelper") );
317
318 m_xScrollPixelsPerLine =
319 m_yScrollPixelsPerLine =
320 m_xScrollPosition =
321 m_yScrollPosition =
322 m_xScrollLines =
323 m_yScrollLines =
324 m_xScrollLinesPerPage =
325 m_yScrollLinesPerPage = 0;
326
327 m_xScrollingEnabled =
328 m_yScrollingEnabled = true;
329
330 m_scaleX =
331 m_scaleY = 1.0;
332 #if wxUSE_MOUSEWHEEL
333 m_wheelRotation = 0;
334 #endif
335
336 m_win =
337 m_targetWindow = (wxWindow *)NULL;
338
339 m_timerAutoScroll = (wxTimer *)NULL;
340
341 m_handler = NULL;
342
343 m_win = win;
344
345 m_win->SetScrollHelper( this );
346
347 // by default, the associated window is also the target window
348 DoSetTargetWindow(win);
349 }
350
351 wxScrollHelper::~wxScrollHelper()
352 {
353 StopAutoScrolling();
354
355 DeleteEvtHandler();
356 }
357
358 // ----------------------------------------------------------------------------
359 // setting scrolling parameters
360 // ----------------------------------------------------------------------------
361
362 void wxScrollHelper::SetScrollbars(int pixelsPerUnitX,
363 int pixelsPerUnitY,
364 int noUnitsX,
365 int noUnitsY,
366 int xPos,
367 int yPos,
368 bool noRefresh)
369 {
370 int xpos, ypos;
371
372 CalcUnscrolledPosition(xPos, yPos, &xpos, &ypos);
373 bool do_refresh =
374 (
375 (noUnitsX != 0 && m_xScrollLines == 0) ||
376 (noUnitsX < m_xScrollLines && xpos > pixelsPerUnitX * noUnitsX) ||
377
378 (noUnitsY != 0 && m_yScrollLines == 0) ||
379 (noUnitsY < m_yScrollLines && ypos > pixelsPerUnitY * noUnitsY) ||
380 (xPos != m_xScrollPosition) ||
381 (yPos != m_yScrollPosition)
382 );
383
384 m_xScrollPixelsPerLine = pixelsPerUnitX;
385 m_yScrollPixelsPerLine = pixelsPerUnitY;
386 m_xScrollPosition = xPos;
387 m_yScrollPosition = yPos;
388
389 int w = noUnitsX * pixelsPerUnitX;
390 int h = noUnitsY * pixelsPerUnitY;
391
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.
395 // m_targetWindow->SetVirtualSizeHints( w, h );
396
397 // The above should arguably be deprecated, this however we still need.
398
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);
404
405 if (do_refresh && !noRefresh)
406 m_targetWindow->Refresh(true, GetScrollRect());
407
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
421 }
422 #endif // !__WXUNIVERSAL__
423 }
424
425 // ----------------------------------------------------------------------------
426 // [target] window handling
427 // ----------------------------------------------------------------------------
428
429 void wxScrollHelper::DeleteEvtHandler()
430 {
431 // search for m_handler in the handler list
432 if ( m_win && m_handler )
433 {
434 if ( m_win->RemoveEventHandler(m_handler) )
435 {
436 delete m_handler;
437 }
438 //else: something is very wrong, so better [maybe] leak memory than
439 // risk a crash because of double deletion
440
441 m_handler = NULL;
442 }
443 }
444
445 void wxScrollHelper::DoSetTargetWindow(wxWindow *target)
446 {
447 m_targetWindow = target;
448 #ifdef __WXMAC__
449 target->MacSetClipChildren( true ) ;
450 #endif
451
452 // install the event handler which will intercept the events we're
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 )
456 {
457 // if we already have a handler, delete it first
458 DeleteEvtHandler();
459
460 m_handler = new wxScrollHelperEvtHandler(this);
461 m_targetWindow->PushEventHandler(m_handler);
462 }
463 }
464
465 void wxScrollHelper::SetTargetWindow(wxWindow *target)
466 {
467 wxCHECK_RET( target, wxT("target window must not be NULL") );
468
469 if ( target == m_targetWindow )
470 return;
471
472 DoSetTargetWindow(target);
473 }
474
475 wxWindow *wxScrollHelper::GetTargetWindow() const
476 {
477 return m_targetWindow;
478 }
479
480 // ----------------------------------------------------------------------------
481 // scrolling implementation itself
482 // ----------------------------------------------------------------------------
483
484 void wxScrollHelper::HandleOnScroll(wxScrollWinEvent& event)
485 {
486 int nScrollInc = CalcScrollInc(event);
487 if ( nScrollInc == 0 )
488 {
489 // can't scroll further
490 event.Skip();
491
492 return;
493 }
494
495 bool needsRefresh = false;
496 int dx = 0,
497 dy = 0;
498 int orient = event.GetOrientation();
499 if (orient == wxHORIZONTAL)
500 {
501 if ( m_xScrollingEnabled )
502 {
503 dx = -m_xScrollPixelsPerLine * nScrollInc;
504 }
505 else
506 {
507 needsRefresh = true;
508 }
509 }
510 else
511 {
512 if ( m_yScrollingEnabled )
513 {
514 dy = -m_yScrollPixelsPerLine * nScrollInc;
515 }
516 else
517 {
518 needsRefresh = true;
519 }
520 }
521
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
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
531 m_targetWindow->Update();
532 #endif
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
546 if ( needsRefresh )
547 {
548 m_targetWindow->Refresh(true, GetScrollRect());
549 }
550 else
551 {
552 m_targetWindow->ScrollWindow(dx, dy, GetScrollRect());
553 }
554 }
555
556 int wxScrollHelper::CalcScrollInc(wxScrollWinEvent& event)
557 {
558 int pos = event.GetPosition();
559 int orient = event.GetOrientation();
560
561 int nScrollInc = 0;
562 if (event.GetEventType() == wxEVT_SCROLLWIN_TOP)
563 {
564 if (orient == wxHORIZONTAL)
565 nScrollInc = - m_xScrollPosition;
566 else
567 nScrollInc = - m_yScrollPosition;
568 } else
569 if (event.GetEventType() == wxEVT_SCROLLWIN_BOTTOM)
570 {
571 if (orient == wxHORIZONTAL)
572 nScrollInc = m_xScrollLines - m_xScrollPosition;
573 else
574 nScrollInc = m_yScrollLines - m_yScrollPosition;
575 } else
576 if (event.GetEventType() == wxEVT_SCROLLWIN_LINEUP)
577 {
578 nScrollInc = -1;
579 } else
580 if (event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN)
581 {
582 nScrollInc = 1;
583 } else
584 if (event.GetEventType() == wxEVT_SCROLLWIN_PAGEUP)
585 {
586 if (orient == wxHORIZONTAL)
587 nScrollInc = -GetScrollPageSize(wxHORIZONTAL);
588 else
589 nScrollInc = -GetScrollPageSize(wxVERTICAL);
590 } else
591 if (event.GetEventType() == wxEVT_SCROLLWIN_PAGEDOWN)
592 {
593 if (orient == wxHORIZONTAL)
594 nScrollInc = GetScrollPageSize(wxHORIZONTAL);
595 else
596 nScrollInc = GetScrollPageSize(wxVERTICAL);
597 } else
598 if ((event.GetEventType() == wxEVT_SCROLLWIN_THUMBTRACK) ||
599 (event.GetEventType() == wxEVT_SCROLLWIN_THUMBRELEASE))
600 {
601 if (orient == wxHORIZONTAL)
602 nScrollInc = pos - m_xScrollPosition;
603 else
604 nScrollInc = pos - m_yScrollPosition;
605 }
606
607 if (orient == wxHORIZONTAL)
608 {
609 if ( m_xScrollPosition + nScrollInc < 0 )
610 {
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 )
618 {
619 // As +ve as we can go
620 nScrollInc = posMax - m_xScrollPosition;
621 }
622 }
623 }
624 else // wxVERTICAL
625 {
626 if ( m_yScrollPosition + nScrollInc < 0 )
627 {
628 // As -ve as we can go
629 nScrollInc = -m_yScrollPosition;
630 }
631 else // check for the other bound
632 {
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 }
639 }
640 }
641
642 return nScrollInc;
643 }
644
645 // Adjust the scrollbars - new version.
646 void wxScrollHelper::AdjustScrollbars()
647 {
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
661 int w = 0, h = 0;
662 int oldw, oldh;
663
664 int oldXScroll = m_xScrollPosition;
665 int oldYScroll = m_yScrollPosition;
666
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
673 // JACS: Stop potential infinite loop by limiting number of iterations
674 int iterationCount = 0;
675 const int iterationMax = 5;
676 do
677 {
678 iterationCount ++;
679
680 GetTargetSize(&w, 0);
681
682 // scroll lines per page: if 0, no scrolling is needed
683 int linesPerPage;
684
685 if ( m_xScrollPixelsPerLine == 0 )
686 {
687 // scrolling is disabled
688 m_xScrollLines = 0;
689 m_xScrollPosition = 0;
690 linesPerPage = 0;
691 }
692 else // might need scrolling
693 {
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;
697
698 // Calculate page size i.e. number of scroll units you get on the
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 }
726 }
727
728 m_win->SetScrollbar(wxHORIZONTAL, m_xScrollPosition,
729 linesPerPage, m_xScrollLines);
730
731 // The amount by which we scroll when paging
732 SetScrollPageSize(wxHORIZONTAL, linesPerPage);
733
734 GetTargetSize(0, &h);
735
736 if ( m_yScrollPixelsPerLine == 0 )
737 {
738 // scrolling is disabled
739 m_yScrollLines = 0;
740 m_yScrollPosition = 0;
741 linesPerPage = 0;
742 }
743 else // might need scrolling
744 {
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;
748
749 // Calculate page size i.e. number of scroll units you get on the
750 // current client window.
751 linesPerPage = h / m_yScrollPixelsPerLine;
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)
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 }
778
779 m_win->SetScrollbar(wxVERTICAL, m_yScrollPosition,
780 linesPerPage, m_yScrollLines);
781
782 // The amount by which we scroll when paging
783 SetScrollPageSize(wxVERTICAL, linesPerPage);
784
785
786 // If a scrollbar (dis)appeared as a result of this, adjust them again.
787 oldw = w;
788 oldh = h;
789
790 GetTargetSize( &w, &h );
791 } while ( (w != oldw || h != oldh) && (iterationCount < iterationMax) );
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 )
800 {
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() &&
814 (m_xScrollLines != 0) && (m_yScrollLines != 0))
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
823 }
824 #endif // Motif
825
826 if (oldXScroll != m_xScrollPosition)
827 {
828 if (m_xScrollingEnabled)
829 m_targetWindow->ScrollWindow( m_xScrollPixelsPerLine * (oldXScroll - m_xScrollPosition), 0,
830 GetScrollRect() );
831 else
832 m_targetWindow->Refresh(true, GetScrollRect());
833 }
834
835 if (oldYScroll != m_yScrollPosition)
836 {
837 if (m_yScrollingEnabled)
838 m_targetWindow->ScrollWindow( 0, m_yScrollPixelsPerLine * (oldYScroll-m_yScrollPosition),
839 GetScrollRect() );
840 else
841 m_targetWindow->Refresh(true, GetScrollRect());
842 }
843 }
844
845 void wxScrollHelper::DoPrepareDC(wxDC& dc)
846 {
847 wxPoint pt = dc.GetDeviceOrigin();
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 );
859 dc.SetUserScale( m_scaleX, m_scaleY );
860 }
861
862 void 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
880 void wxScrollHelper::GetScrollPixelsPerUnit (int *x_unit, int *y_unit) const
881 {
882 if ( x_unit )
883 *x_unit = m_xScrollPixelsPerLine;
884 if ( y_unit )
885 *y_unit = m_yScrollPixelsPerLine;
886 }
887
888
889 int wxScrollHelper::GetScrollLines( int orient ) const
890 {
891 if ( orient == wxHORIZONTAL )
892 return m_xScrollLines;
893 else
894 return m_yScrollLines;
895 }
896
897 int wxScrollHelper::GetScrollPageSize(int orient) const
898 {
899 if ( orient == wxHORIZONTAL )
900 return m_xScrollLinesPerPage;
901 else
902 return m_yScrollLinesPerPage;
903 }
904
905 void wxScrollHelper::SetScrollPageSize(int orient, int pageSize)
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 */
916 void wxScrollHelper::Scroll( int x_pos, int y_pos )
917 {
918 if (!m_targetWindow)
919 return;
920
921 if (((x_pos == -1) || (x_pos == m_xScrollPosition)) &&
922 ((y_pos == -1) || (y_pos == m_yScrollPosition))) return;
923
924 int w = 0, h = 0;
925 GetTargetSize(&w, &h);
926
927 // compute new position:
928 int new_x = m_xScrollPosition;
929 int new_y = m_yScrollPosition;
930
931 if ((x_pos != -1) && (m_xScrollPixelsPerLine))
932 {
933 new_x = x_pos;
934
935 // Calculate page size i.e. number of scroll units you get on the
936 // current client window
937 int noPagePositions = w/m_xScrollPixelsPerLine;
938 if (noPagePositions < 1) noPagePositions = 1;
939
940 // Correct position if greater than extent of canvas minus
941 // the visible portion of it or if below zero
942 new_x = wxMin( m_xScrollLines-noPagePositions, new_x );
943 new_x = wxMax( 0, new_x );
944 }
945 if ((y_pos != -1) && (m_yScrollPixelsPerLine))
946 {
947 new_y = y_pos;
948
949 // Calculate page size i.e. number of scroll units you get on the
950 // current client window
951 int noPagePositions = h/m_yScrollPixelsPerLine;
952 if (noPagePositions < 1) noPagePositions = 1;
953
954 // Correct position if greater than extent of canvas minus
955 // the visible portion of it or if below zero
956 new_y = wxMin( m_yScrollLines-noPagePositions, new_y );
957 new_y = wxMax( 0, new_y );
958 }
959
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() );
985 }
986 }
987
988 void wxScrollHelper::EnableScrolling (bool x_scroll, bool y_scroll)
989 {
990 m_xScrollingEnabled = x_scroll;
991 m_yScrollingEnabled = y_scroll;
992 }
993
994 // Where the current view starts from
995 void wxScrollHelper::GetViewStart (int *x, int *y) const
996 {
997 if ( x )
998 *x = m_xScrollPosition;
999 if ( y )
1000 *y = m_yScrollPosition;
1001 }
1002
1003 void wxScrollHelper::DoCalcScrolledPosition(int x, int y, int *xx, int *yy) const
1004 {
1005 if ( xx )
1006 *xx = x - m_xScrollPosition * m_xScrollPixelsPerLine;
1007 if ( yy )
1008 *yy = y - m_yScrollPosition * m_yScrollPixelsPerLine;
1009 }
1010
1011 void wxScrollHelper::DoCalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
1012 {
1013 if ( xx )
1014 *xx = x + m_xScrollPosition * m_xScrollPixelsPerLine;
1015 if ( yy )
1016 *yy = y + m_yScrollPosition * m_yScrollPixelsPerLine;
1017 }
1018
1019 // ----------------------------------------------------------------------------
1020 // geometry
1021 // ----------------------------------------------------------------------------
1022
1023 bool 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
1030 int x = 0, y = 0, w = 0, h = 0;
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
1041 void 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
1052 wxSize 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
1061 // ----------------------------------------------------------------------------
1062 // event handlers
1063 // ----------------------------------------------------------------------------
1064
1065 // Default OnSize resets scrollbars, if any
1066 void wxScrollHelper::HandleOnSize(wxSizeEvent& WXUNUSED(event))
1067 {
1068 if ( m_targetWindow->GetAutoLayout() )
1069 {
1070 wxSize size = m_targetWindow->GetBestVirtualSize();
1071
1072 // This will call ::Layout() and ::AdjustScrollbars()
1073 m_win->SetVirtualSize( size );
1074 }
1075 else
1076 {
1077 AdjustScrollbars();
1078 }
1079 }
1080
1081 // This calls OnDraw, having adjusted the origin according to the current
1082 // scroll position
1083 void wxScrollHelper::HandleOnPaint(wxPaintEvent& WXUNUSED(event))
1084 {
1085 // don't use m_targetWindow here, this is always called for ourselves
1086 wxPaintDC dc(m_win);
1087 DoPrepareDC(dc);
1088
1089 OnDraw(dc);
1090 }
1091
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
1096 void wxScrollHelper::HandleOnChar(wxKeyEvent& event)
1097 {
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)
1101
1102 GetViewStart(&stx, &sty);
1103 GetTargetSize(&clix, &cliy);
1104 m_targetWindow->GetVirtualSize(&szx, &szy);
1105
1106 if( m_xScrollPixelsPerLine )
1107 {
1108 clix /= m_xScrollPixelsPerLine;
1109 szx /= m_xScrollPixelsPerLine;
1110 }
1111 else
1112 {
1113 clix = 0;
1114 szx = -1;
1115 }
1116 if( m_yScrollPixelsPerLine )
1117 {
1118 cliy /= m_yScrollPixelsPerLine;
1119 szy /= m_yScrollPixelsPerLine;
1120 }
1121 else
1122 {
1123 cliy = 0;
1124 szy = -1;
1125 }
1126
1127 int xScrollOld = m_xScrollPosition,
1128 yScrollOld = m_yScrollPosition;
1129
1130 int dsty;
1131 switch ( event.GetKeyCode() )
1132 {
1133 case WXK_PAGEUP:
1134 dsty = sty - (5 * cliy / 6);
1135 Scroll(-1, (dsty == -1) ? 0 : dsty);
1136 break;
1137
1138 case WXK_PAGEDOWN:
1139 Scroll(-1, sty + (5 * cliy / 6));
1140 break;
1141
1142 case WXK_HOME:
1143 Scroll(0, event.ControlDown() ? 0 : -1);
1144 break;
1145
1146 case WXK_END:
1147 Scroll(szx - clix, event.ControlDown() ? szy - cliy : -1);
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 }
1170
1171 if ( m_xScrollPosition != xScrollOld )
1172 {
1173 wxScrollWinEvent evt(wxEVT_SCROLLWIN_THUMBTRACK, m_xScrollPosition,
1174 wxHORIZONTAL);
1175 evt.SetEventObject(m_win);
1176 m_win->GetEventHandler()->ProcessEvent(evt);
1177 }
1178
1179 if ( m_yScrollPosition != yScrollOld )
1180 {
1181 wxScrollWinEvent evt(wxEVT_SCROLLWIN_THUMBTRACK, m_yScrollPosition,
1182 wxVERTICAL);
1183 evt.SetEventObject(m_win);
1184 m_win->GetEventHandler()->ProcessEvent(evt);
1185 }
1186 }
1187
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
1193 bool 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
1200 void wxScrollHelper::StopAutoScrolling()
1201 {
1202 #if wxUSE_TIMER
1203 if ( m_timerAutoScroll )
1204 {
1205 delete m_timerAutoScroll;
1206 m_timerAutoScroll = (wxTimer *)NULL;
1207 }
1208 #endif
1209 }
1210
1211 void wxScrollHelper::HandleOnMouseEnter(wxMouseEvent& event)
1212 {
1213 StopAutoScrolling();
1214
1215 event.Skip();
1216 }
1217
1218 void 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
1270 #if wxUSE_TIMER
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
1281 #else
1282 wxUnusedVar(pos);
1283 #endif
1284 }
1285 }
1286
1287 #if wxUSE_MOUSEWHEEL
1288
1289 void wxScrollHelper::HandleOnMouseWheel(wxMouseEvent& event)
1290 {
1291 m_wheelRotation += event.GetWheelRotation();
1292 int lines = m_wheelRotation / event.GetWheelDelta();
1293 m_wheelRotation -= lines * event.GetWheelDelta();
1294
1295 if (lines != 0)
1296 {
1297
1298 wxScrollWinEvent newEvent;
1299
1300 newEvent.SetPosition(0);
1301 newEvent.SetOrientation( event.GetWheelAxis() == 0 ? wxVERTICAL : wxHORIZONTAL);
1302 newEvent.SetEventObject(m_win);
1303
1304 if (event.IsPageScroll())
1305 {
1306 if (lines > 0)
1307 newEvent.SetEventType(wxEVT_SCROLLWIN_PAGEUP);
1308 else
1309 newEvent.SetEventType(wxEVT_SCROLLWIN_PAGEDOWN);
1310
1311 m_win->GetEventHandler()->ProcessEvent(newEvent);
1312 }
1313 else
1314 {
1315 lines *= event.GetLinesPerAction();
1316 if (lines > 0)
1317 newEvent.SetEventType(wxEVT_SCROLLWIN_LINEUP);
1318 else
1319 newEvent.SetEventType(wxEVT_SCROLLWIN_LINEDOWN);
1320
1321 int times = abs(lines);
1322 for (; times > 0; times--)
1323 m_win->GetEventHandler()->ProcessEvent(newEvent);
1324 }
1325 }
1326 }
1327
1328 #endif // wxUSE_MOUSEWHEEL
1329
1330 void 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();
1338
1339 if ( win == m_targetWindow )
1340 return; // nothing to do
1341
1342 while ( win->GetParent() != m_targetWindow )
1343 {
1344 win = win->GetParent();
1345 if ( !win )
1346 return; // event is not from a child of the target window
1347 }
1348
1349 // if the child is not fully visible, try to scroll it into view:
1350 int stepx, stepy;
1351 GetScrollPixelsPerUnit(&stepx, &stepy);
1352
1353 // NB: we don't call CalcScrolledPosition() on win->GetPosition() here,
1354 // because children' positions are already scrolled
1355 wxRect winrect(win->GetPosition(), win->GetSize());
1356 wxSize view(m_targetWindow->GetClientSize());
1357
1358 int startx, starty;
1359 GetViewStart(&startx, &starty);
1360
1361 // first in vertical direction:
1362 if ( stepy > 0 )
1363 {
1364 int diff = 0;
1365
1366 if ( winrect.GetTop() < 0 )
1367 {
1368 diff = winrect.GetTop();
1369 }
1370 else if ( winrect.GetBottom() > view.y )
1371 {
1372 diff = winrect.GetBottom() - view.y + 1;
1373 // round up to next scroll step if we can't get exact position,
1374 // so that the window is fully visible:
1375 diff += stepy - 1;
1376 }
1377
1378 starty = (starty * stepy + diff) / stepy;
1379 }
1380
1381 // then horizontal:
1382 if ( stepx > 0 )
1383 {
1384 int diff = 0;
1385
1386 if ( winrect.GetLeft() < 0 )
1387 {
1388 diff = winrect.GetLeft();
1389 }
1390 else if ( winrect.GetRight() > view.x )
1391 {
1392 diff = winrect.GetRight() - view.x + 1;
1393 // round up to next scroll step if we can't get exact position,
1394 // so that the window is fully visible:
1395 diff += stepx - 1;
1396 }
1397
1398 startx = (startx * stepx + diff) / stepx;
1399 }
1400
1401 Scroll(startx, starty);
1402 }
1403
1404 // ----------------------------------------------------------------------------
1405 // wxScrolledWindow implementation
1406 // ----------------------------------------------------------------------------
1407
1408 IMPLEMENT_DYNAMIC_CLASS(wxScrolledWindow, wxPanel)
1409
1410 BEGIN_EVENT_TABLE(wxScrolledWindow, wxPanel)
1411 EVT_PAINT(wxScrolledWindow::OnPaint)
1412 END_EVENT_TABLE()
1413
1414 bool wxScrolledWindow::Create(wxWindow *parent,
1415 wxWindowID id,
1416 const wxPoint& pos,
1417 const wxSize& size,
1418 long style,
1419 const wxString& name)
1420 {
1421 m_targetWindow = this;
1422 #ifdef __WXMAC__
1423 MacSetClipChildren( true ) ;
1424 #endif
1425
1426 // by default, we're scrollable in both directions (but if one of the
1427 // styles is specified explicitly, we shouldn't add the other one
1428 // automatically)
1429 if ( !(style & (wxHSCROLL | wxVSCROLL)) )
1430 style |= wxHSCROLL | wxVSCROLL;
1431
1432 bool ok = wxPanel::Create(parent, id, pos, size, style, name);
1433
1434 return ok;
1435 }
1436
1437 wxScrolledWindow::~wxScrolledWindow()
1438 {
1439 }
1440
1441 void wxScrolledWindow::OnPaint(wxPaintEvent& event)
1442 {
1443 // the user code didn't really draw the window if we got here, so set this
1444 // flag to try to call OnDraw() later
1445 m_handler->ResetDrawnFlag();
1446
1447 event.Skip();
1448 }
1449
1450 wxSize wxScrolledWindow::DoGetBestSize() const
1451 {
1452 // NB: We don't do this in WX_FORWARD_TO_SCROLL_HELPER, because not
1453 // all scrollable windows should behave like this, only those that
1454 // contain children controls within scrollable area
1455 // (i.e., wxScrolledWindow) and other some scrollable windows may
1456 // have different DoGetBestSize() implementation (e.g. wxTreeCtrl).
1457
1458 wxSize best = wxPanel::DoGetBestSize();
1459
1460 if ( GetAutoLayout() )
1461 {
1462 // Only use the content to set the window size in the direction
1463 // where there's no scrolling; otherwise we're going to get a huge
1464 // window in the direction in which scrolling is enabled
1465 int ppuX, ppuY;
1466 GetScrollPixelsPerUnit(&ppuX, &ppuY);
1467
1468 // NB: This code used to use *current* size if min size wasn't
1469 // specified, presumably to get some reasonable (i.e., larger than
1470 // minimal) size. But that's a wrong thing to do in GetBestSize(),
1471 // so we use minimal size as specified. If the app needs some
1472 // minimal size for its scrolled window, it should set it and put
1473 // the window into sizer as expandable so that it can use all space
1474 // available to it.
1475 //
1476 // See also http://svn.wxwidgets.org/viewvc/wx?view=rev&revision=45864
1477
1478 wxSize minSize = GetMinSize();
1479
1480 if ( ppuX > 0 )
1481 best.x = minSize.x + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
1482
1483 if ( ppuY > 0 )
1484 best.y = minSize.y + wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
1485 }
1486
1487 return best;
1488 }
1489
1490 #ifdef __WXMSW__
1491 WXLRESULT wxScrolledWindow::MSWWindowProc(WXUINT nMsg,
1492 WXWPARAM wParam,
1493 WXLPARAM lParam)
1494 {
1495 WXLRESULT rc = wxPanel::MSWWindowProc(nMsg, wParam, lParam);
1496
1497 #ifndef __WXWINCE__
1498 // we need to process arrows ourselves for scrolling
1499 if ( nMsg == WM_GETDLGCODE )
1500 {
1501 rc |= DLGC_WANTARROWS;
1502 }
1503 #endif
1504
1505 return rc;
1506 }
1507
1508 #endif // __WXMSW__