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