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