]> git.saurik.com Git - wxWidgets.git/blob - src/generic/scrolwin.cpp
066e25a2f2fc55b33079140891169eab42d0c527
[wxWidgets.git] / src / generic / scrolwin.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: generic/scrolwin.cpp
3 // Purpose: wxScrolledWindow implementation
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "scrolwin.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #include "wx/utils.h"
32 #include "wx/dcclient.h"
33
34 #include "wx/generic/scrolwin.h"
35 #include "wx/panel.h"
36
37 #ifdef __WXMSW__
38 #include "windows.h"
39 #endif
40
41 #ifdef __WXMOTIF__
42 // For wxRETAINED implementation
43 #ifdef __VMS__ //VMS's Xm.h is not (yet) compatible with C++
44 //This code switches off the compiler warnings
45 # pragma message disable nosimpint
46 #endif
47 #include <Xm/Xm.h>
48 #ifdef __VMS__
49 # pragma message enable nosimpint
50 #endif
51 #endif
52
53 // ----------------------------------------------------------------------------
54 // event tables
55 // ----------------------------------------------------------------------------
56
57 BEGIN_EVENT_TABLE(wxScrolledWindow, wxPanel)
58 EVT_SCROLLWIN(wxScrolledWindow::OnScroll)
59 EVT_SIZE(wxScrolledWindow::OnSize)
60 EVT_PAINT(wxScrolledWindow::OnPaint)
61 EVT_KEY_DOWN(wxScrolledWindow::OnKeyDown)
62 END_EVENT_TABLE()
63
64 IMPLEMENT_DYNAMIC_CLASS(wxScrolledWindow, wxPanel)
65
66 // ============================================================================
67 // implementation
68 // ============================================================================
69
70 // ----------------------------------------------------------------------------
71 // wxScrolledWindow creation
72 // ----------------------------------------------------------------------------
73
74 wxScrolledWindow::wxScrolledWindow()
75 {
76 m_xScrollPixelsPerLine = 0;
77 m_yScrollPixelsPerLine = 0;
78 m_xScrollingEnabled = TRUE;
79 m_yScrollingEnabled = TRUE;
80 m_xScrollPosition = 0;
81 m_yScrollPosition = 0;
82 m_xScrollLines = 0;
83 m_yScrollLines = 0;
84 m_xScrollLinesPerPage = 0;
85 m_yScrollLinesPerPage = 0;
86 m_scaleX = 1.0;
87 m_scaleY = 1.0;
88 }
89
90 bool wxScrolledWindow::Create(wxWindow *parent,
91 wxWindowID id,
92 const wxPoint& pos,
93 const wxSize& size,
94 long style,
95 const wxString& name)
96 {
97 m_xScrollPixelsPerLine = 0;
98 m_yScrollPixelsPerLine = 0;
99 m_xScrollingEnabled = TRUE;
100 m_yScrollingEnabled = TRUE;
101 m_xScrollPosition = 0;
102 m_yScrollPosition = 0;
103 m_xScrollLines = 0;
104 m_yScrollLines = 0;
105 m_xScrollLinesPerPage = 0;
106 m_yScrollLinesPerPage = 0;
107 m_scaleX = 1.0;
108 m_scaleY = 1.0;
109
110 m_targetWindow = this;
111
112 return wxPanel::Create(parent, id, pos, size, style, name);
113 }
114
115 wxScrolledWindow::~wxScrolledWindow()
116 {
117 }
118
119 // ----------------------------------------------------------------------------
120 // setting scrolling parameters
121 // ----------------------------------------------------------------------------
122
123 /*
124 * pixelsPerUnitX/pixelsPerUnitY: number of pixels per unit (e.g. pixels per text line)
125 * noUnitsX/noUnitsY: : no. units per scrollbar
126 */
127 void wxScrolledWindow::SetScrollbars (int pixelsPerUnitX, int pixelsPerUnitY,
128 int noUnitsX, int noUnitsY,
129 int xPos, int yPos, bool noRefresh )
130 {
131 bool do_refresh =
132 (
133 (noUnitsX != 0 && m_xScrollLines == 0) ||
134 (noUnitsX < m_xScrollLines) ||
135 (noUnitsY != 0 && m_yScrollLines == 0) ||
136 (noUnitsY < m_yScrollLines) ||
137 (xPos != m_xScrollPosition) ||
138 (yPos != m_yScrollPosition) ||
139 (pixelsPerUnitX != m_xScrollPixelsPerLine) ||
140 (pixelsPerUnitY != m_yScrollPixelsPerLine)
141 );
142
143 m_xScrollPixelsPerLine = pixelsPerUnitX;
144 m_yScrollPixelsPerLine = pixelsPerUnitY;
145 m_xScrollPosition = xPos;
146 m_yScrollPosition = yPos;
147 m_xScrollLines = noUnitsX;
148 m_yScrollLines = noUnitsY;
149
150 #ifdef __WXMOTIF__
151 // Sorry, some Motif-specific code to implement a backing pixmap
152 // for the wxRETAINED style. Implementing a backing store can't
153 // be entirely generic because it relies on the wxWindowDC implementation
154 // to duplicate X drawing calls for the backing pixmap.
155
156 if ((m_windowStyle & wxRETAINED) == wxRETAINED)
157 {
158 Display* dpy = XtDisplay((Widget) GetMainWidget());
159
160 int totalPixelWidth = m_xScrollLines * m_xScrollPixelsPerLine;
161 int totalPixelHeight = m_yScrollLines * m_yScrollPixelsPerLine;
162 if (m_backingPixmap &&
163 !((m_pixmapWidth == totalPixelWidth) &&
164 (m_pixmapHeight == totalPixelHeight)))
165 {
166 XFreePixmap (dpy, (Pixmap) m_backingPixmap);
167 m_backingPixmap = (WXPixmap) 0;
168 }
169
170 if (!m_backingPixmap &&
171 (noUnitsX != 0) && (noUnitsY != 0))
172 {
173 int depth = wxDisplayDepth();
174 m_pixmapWidth = totalPixelWidth;
175 m_pixmapHeight = totalPixelHeight;
176 m_backingPixmap = (WXPixmap) XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
177 m_pixmapWidth, m_pixmapHeight, depth);
178 }
179
180 }
181 #endif // Motif
182
183 AdjustScrollbars();
184
185 if (do_refresh && !noRefresh)
186 m_targetWindow->Refresh();
187
188 #ifdef __WXMSW__
189 // GRG: if this turns out to be really necessary, we could
190 // at least move it to the above if { ... } so that it is
191 // only done if noRefresh = FALSE (the default). OTOH, if
192 // this doesn't break anything, which seems to be the
193 // case, we could just leave it out.
194
195 // Necessary?
196 // UpdateWindow ((HWND) m_targetWindow->GetHWND());
197 #endif
198 #ifdef __WXMAC__
199 m_targetWindow->MacUpdateImmediately() ;
200 #endif
201 }
202
203 // ----------------------------------------------------------------------------
204 // target window handling
205 // ----------------------------------------------------------------------------
206
207 void wxScrolledWindow::SetTargetWindow( wxWindow *target )
208 {
209 wxASSERT_MSG( target, wxT("target window must not be NULL") );
210 m_targetWindow = target;
211 }
212
213 wxWindow *wxScrolledWindow::GetTargetWindow()
214 {
215 return m_targetWindow;
216 }
217
218 // ----------------------------------------------------------------------------
219 // scrolling implementation itself
220 // ----------------------------------------------------------------------------
221
222 void wxScrolledWindow::OnScroll(wxScrollWinEvent& event)
223 {
224 int orient = event.GetOrientation();
225
226 int nScrollInc = CalcScrollInc(event);
227 if (nScrollInc == 0) return;
228
229 if (orient == wxHORIZONTAL)
230 {
231 int newPos = m_xScrollPosition + nScrollInc;
232 SetScrollPos(wxHORIZONTAL, newPos, TRUE );
233 }
234 else
235 {
236 int newPos = m_yScrollPosition + nScrollInc;
237 SetScrollPos(wxVERTICAL, newPos, TRUE );
238 }
239
240 if (orient == wxHORIZONTAL)
241 {
242 m_xScrollPosition += nScrollInc;
243 }
244 else
245 {
246 m_yScrollPosition += nScrollInc;
247 }
248
249 if (orient == wxHORIZONTAL)
250 {
251 if (m_xScrollingEnabled)
252 m_targetWindow->ScrollWindow(-m_xScrollPixelsPerLine * nScrollInc, 0, (const wxRect *) NULL);
253 else
254 m_targetWindow->Refresh();
255 }
256 else
257 {
258 if (m_yScrollingEnabled)
259 m_targetWindow->ScrollWindow(0, -m_yScrollPixelsPerLine * nScrollInc, (const wxRect *) NULL);
260 else
261 m_targetWindow->Refresh();
262 }
263 #ifdef __WXMAC__
264 m_targetWindow->MacUpdateImmediately() ;
265 #endif
266 }
267
268 int wxScrolledWindow::CalcScrollInc(wxScrollWinEvent& event)
269 {
270 int pos = event.GetPosition();
271 int orient = event.GetOrientation();
272
273 int nScrollInc = 0;
274 switch (event.GetEventType())
275 {
276 case wxEVT_SCROLLWIN_TOP:
277 {
278 if (orient == wxHORIZONTAL)
279 nScrollInc = - m_xScrollPosition;
280 else
281 nScrollInc = - m_yScrollPosition;
282 break;
283 }
284 case wxEVT_SCROLLWIN_BOTTOM:
285 {
286 if (orient == wxHORIZONTAL)
287 nScrollInc = m_xScrollLines - m_xScrollPosition;
288 else
289 nScrollInc = m_yScrollLines - m_yScrollPosition;
290 break;
291 }
292 case wxEVT_SCROLLWIN_LINEUP:
293 {
294 nScrollInc = -1;
295 break;
296 }
297 case wxEVT_SCROLLWIN_LINEDOWN:
298 {
299 nScrollInc = 1;
300 break;
301 }
302 case wxEVT_SCROLLWIN_PAGEUP:
303 {
304 if (orient == wxHORIZONTAL)
305 nScrollInc = -GetScrollPageSize(wxHORIZONTAL);
306 else
307 nScrollInc = -GetScrollPageSize(wxVERTICAL);
308 break;
309 }
310 case wxEVT_SCROLLWIN_PAGEDOWN:
311 {
312 if (orient == wxHORIZONTAL)
313 nScrollInc = GetScrollPageSize(wxHORIZONTAL);
314 else
315 nScrollInc = GetScrollPageSize(wxVERTICAL);
316 break;
317 }
318 case wxEVT_SCROLLWIN_THUMBTRACK:
319 {
320 if (orient == wxHORIZONTAL)
321 nScrollInc = pos - m_xScrollPosition;
322 else
323 nScrollInc = pos - m_yScrollPosition;
324 break;
325 }
326 default:
327 {
328 break;
329 }
330 }
331
332 if (orient == wxHORIZONTAL)
333 {
334 if (m_xScrollPixelsPerLine > 0)
335 {
336 int w, h;
337 m_targetWindow->GetClientSize(&w, &h);
338
339 int nMaxWidth = m_xScrollLines*m_xScrollPixelsPerLine;
340 int noPositions = (int) ( ((nMaxWidth - w)/(double)m_xScrollPixelsPerLine) + 0.5 );
341 if (noPositions < 0)
342 noPositions = 0;
343
344 if ( (m_xScrollPosition + nScrollInc) < 0 )
345 nScrollInc = -m_xScrollPosition; // As -ve as we can go
346 else if ( (m_xScrollPosition + nScrollInc) > noPositions )
347 nScrollInc = noPositions - m_xScrollPosition; // As +ve as we can go
348 }
349 else
350 m_targetWindow->Refresh();
351 }
352 else
353 {
354 if (m_yScrollPixelsPerLine > 0)
355 {
356 int w, h;
357 m_targetWindow->GetClientSize(&w, &h);
358
359 int nMaxHeight = m_yScrollLines*m_yScrollPixelsPerLine;
360 int noPositions = (int) ( ((nMaxHeight - h)/(double)m_yScrollPixelsPerLine) + 0.5 );
361 if (noPositions < 0)
362 noPositions = 0;
363
364 if ( (m_yScrollPosition + nScrollInc) < 0 )
365 nScrollInc = -m_yScrollPosition; // As -ve as we can go
366 else if ( (m_yScrollPosition + nScrollInc) > noPositions )
367 nScrollInc = noPositions - m_yScrollPosition; // As +ve as we can go
368 }
369 else
370 m_targetWindow->Refresh();
371 }
372
373 return nScrollInc;
374 }
375
376 // Adjust the scrollbars - new version.
377 void wxScrolledWindow::AdjustScrollbars()
378 {
379 int w, h;
380 m_targetWindow->GetClientSize(&w, &h);
381
382 int oldXScroll = m_xScrollPosition;
383 int oldYScroll = m_yScrollPosition;
384
385 if (m_xScrollLines > 0)
386 {
387 // Calculate page size i.e. number of scroll units you get on the
388 // current client window
389 int noPagePositions = (int) ( (w/(double)m_xScrollPixelsPerLine) + 0.5 );
390 if (noPagePositions < 1) noPagePositions = 1;
391
392 // Correct position if greater than extent of canvas minus
393 // the visible portion of it or if below zero
394 m_xScrollPosition = wxMin( m_xScrollLines-noPagePositions, m_xScrollPosition);
395 m_xScrollPosition = wxMax( 0, m_xScrollPosition );
396
397 SetScrollbar(wxHORIZONTAL, m_xScrollPosition, noPagePositions, m_xScrollLines);
398 // The amount by which we scroll when paging
399 SetScrollPageSize(wxHORIZONTAL, noPagePositions);
400 }
401 else
402 {
403 m_xScrollPosition = 0;
404 SetScrollbar (wxHORIZONTAL, 0, 0, 0, FALSE);
405 }
406
407 if (m_yScrollLines > 0)
408 {
409 // Calculate page size i.e. number of scroll units you get on the
410 // current client window
411 int noPagePositions = (int) ( (h/(double)m_yScrollPixelsPerLine) + 0.5 );
412 if (noPagePositions < 1) noPagePositions = 1;
413
414 // Correct position if greater than extent of canvas minus
415 // the visible portion of it or if below zero
416 m_yScrollPosition = wxMin( m_yScrollLines-noPagePositions, m_yScrollPosition );
417 m_yScrollPosition = wxMax( 0, m_yScrollPosition );
418
419 SetScrollbar(wxVERTICAL, m_yScrollPosition, noPagePositions, m_yScrollLines);
420 // The amount by which we scroll when paging
421 SetScrollPageSize(wxVERTICAL, noPagePositions);
422 }
423 else
424 {
425 m_yScrollPosition = 0;
426 SetScrollbar (wxVERTICAL, 0, 0, 0, FALSE);
427 }
428
429 if (oldXScroll != m_xScrollPosition)
430 {
431 if (m_xScrollingEnabled)
432 m_targetWindow->ScrollWindow( m_xScrollPixelsPerLine * (oldXScroll-m_xScrollPosition), 0, (const wxRect *) NULL );
433 else
434 m_targetWindow->Refresh();
435 }
436
437 if (oldYScroll != m_yScrollPosition)
438 {
439 if (m_yScrollingEnabled)
440 m_targetWindow->ScrollWindow( 0, m_yScrollPixelsPerLine * (oldYScroll-m_yScrollPosition), (const wxRect *) NULL );
441 else
442 m_targetWindow->Refresh();
443 }
444 }
445
446 // Override this function if you don't want to have wxScrolledWindow
447 // automatically change the origin according to the scroll position.
448 void wxScrolledWindow::PrepareDC(wxDC& dc)
449 {
450 dc.SetDeviceOrigin( -m_xScrollPosition * m_xScrollPixelsPerLine,
451 -m_yScrollPosition * m_yScrollPixelsPerLine );
452 dc.SetUserScale( m_scaleX, m_scaleY );
453 }
454
455 #if WXWIN_COMPATIBILITY
456 void wxScrolledWindow::GetScrollUnitsPerPage (int *x_page, int *y_page) const
457 {
458 *x_page = GetScrollPageSize(wxHORIZONTAL);
459 *y_page = GetScrollPageSize(wxVERTICAL);
460 }
461
462 void wxScrolledWindow::CalcUnscrolledPosition(int x, int y, float *xx, float *yy) const
463 {
464 if ( xx )
465 *xx = (float)(x + m_xScrollPosition * m_xScrollPixelsPerLine);
466 if ( yy )
467 *yy = (float)(y + m_yScrollPosition * m_yScrollPixelsPerLine);
468 }
469 #endif // WXWIN_COMPATIBILITY
470
471 void wxScrolledWindow::GetScrollPixelsPerUnit (int *x_unit, int *y_unit) const
472 {
473 if ( x_unit )
474 *x_unit = m_xScrollPixelsPerLine;
475 if ( y_unit )
476 *y_unit = m_yScrollPixelsPerLine;
477 }
478
479 int wxScrolledWindow::GetScrollPageSize(int orient) const
480 {
481 if ( orient == wxHORIZONTAL )
482 return m_xScrollLinesPerPage;
483 else
484 return m_yScrollLinesPerPage;
485 }
486
487 void wxScrolledWindow::SetScrollPageSize(int orient, int pageSize)
488 {
489 if ( orient == wxHORIZONTAL )
490 m_xScrollLinesPerPage = pageSize;
491 else
492 m_yScrollLinesPerPage = pageSize;
493 }
494
495 /*
496 * Scroll to given position (scroll position, not pixel position)
497 */
498 void wxScrolledWindow::Scroll( int x_pos, int y_pos )
499 {
500 if (((x_pos == -1) || (x_pos == m_xScrollPosition)) &&
501 ((y_pos == -1) || (y_pos == m_yScrollPosition))) return;
502
503 int w, h;
504 m_targetWindow->GetClientSize(&w, &h);
505
506 if (x_pos != -1)
507 {
508 int old_x = m_xScrollPosition;
509 m_xScrollPosition = x_pos;
510
511 // Calculate page size i.e. number of scroll units you get on the
512 // current client window
513 int noPagePositions = (int) ( (w/(double)m_xScrollPixelsPerLine) + 0.5 );
514 if (noPagePositions < 1) noPagePositions = 1;
515
516 // Correct position if greater than extent of canvas minus
517 // the visible portion of it or if below zero
518 m_xScrollPosition = wxMin( m_xScrollLines-noPagePositions, m_xScrollPosition );
519 m_xScrollPosition = wxMax( 0, m_xScrollPosition );
520
521 m_targetWindow->SetScrollPos( wxHORIZONTAL, m_xScrollPosition, TRUE );
522
523 m_targetWindow->ScrollWindow( (old_x-m_xScrollPosition)*m_xScrollPixelsPerLine, 0 );
524 }
525 if (y_pos != -1)
526 {
527 int old_y = m_yScrollPosition;
528 m_yScrollPosition = y_pos;
529
530 // Calculate page size i.e. number of scroll units you get on the
531 // current client window
532 int noPagePositions = (int) ( (h/(double)m_yScrollPixelsPerLine) + 0.5 );
533 if (noPagePositions < 1) noPagePositions = 1;
534
535 // Correct position if greater than extent of canvas minus
536 // the visible portion of it or if below zero
537 m_yScrollPosition = wxMin( m_yScrollLines-noPagePositions, m_yScrollPosition );
538 m_yScrollPosition = wxMax( 0, m_yScrollPosition );
539
540 m_targetWindow->SetScrollPos( wxVERTICAL, m_yScrollPosition, TRUE );
541
542 m_targetWindow->ScrollWindow( 0, (old_y-m_yScrollPosition)*m_yScrollPixelsPerLine );
543 }
544
545
546 #ifdef __WXMAC__
547 m_targetWindow->MacUpdateImmediately() ;
548 #endif
549 }
550
551 void wxScrolledWindow::EnableScrolling (bool x_scroll, bool y_scroll)
552 {
553 m_xScrollingEnabled = x_scroll;
554 m_yScrollingEnabled = y_scroll;
555 }
556
557 void wxScrolledWindow::GetVirtualSize (int *x, int *y) const
558 {
559 if ( x )
560 *x = m_xScrollPixelsPerLine * m_xScrollLines;
561 if ( y )
562 *y = m_yScrollPixelsPerLine * m_yScrollLines;
563 }
564
565 // Where the current view starts from
566 void wxScrolledWindow::GetViewStart (int *x, int *y) const
567 {
568 if ( x )
569 *x = m_xScrollPosition;
570 if ( y )
571 *y = m_yScrollPosition;
572 }
573
574 void wxScrolledWindow::CalcScrolledPosition(int x, int y, int *xx, int *yy) const
575 {
576 if ( xx )
577 *xx = x - m_xScrollPosition * m_xScrollPixelsPerLine;
578 if ( yy )
579 *yy = y - m_yScrollPosition * m_yScrollPixelsPerLine;
580 }
581
582 void wxScrolledWindow::CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
583 {
584 if ( xx )
585 *xx = x + m_xScrollPosition * m_xScrollPixelsPerLine;
586 if ( yy )
587 *yy = y + m_yScrollPosition * m_yScrollPixelsPerLine;
588 }
589
590 // ----------------------------------------------------------------------------
591 // event handlers
592 // ----------------------------------------------------------------------------
593
594 // Default OnSize resets scrollbars, if any
595 void wxScrolledWindow::OnSize(wxSizeEvent& WXUNUSED(event))
596 {
597 #if wxUSE_CONSTRAINTS
598 if (GetAutoLayout())
599 Layout();
600 #endif
601
602 AdjustScrollbars();
603 }
604
605 // This calls OnDraw, having adjusted the origin according to the current
606 // scroll position
607 void wxScrolledWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
608 {
609 wxPaintDC dc(this);
610 PrepareDC(dc);
611
612 OnDraw(dc);
613 }
614
615 // kbd handling
616 void wxScrolledWindow::OnKeyDown(wxKeyEvent& event)
617 {
618 int stx, sty, // view origin
619 szx, szy, // view size (total)
620 clix, cliy; // view size (on screen)
621
622 ViewStart(&stx, &sty);
623 GetClientSize(&clix, &cliy);
624 clix /= m_xScrollPixelsPerLine;
625 cliy /= m_yScrollPixelsPerLine;
626 GetVirtualSize(&szx, &szy);
627 szx /= m_xScrollPixelsPerLine;
628 szy /= m_yScrollPixelsPerLine;
629
630 switch ( event.KeyCode() )
631 {
632 case WXK_PAGEUP:
633 case WXK_PRIOR:
634 Scroll(-1, sty - (5 * cliy / 6));
635 break;
636
637 case WXK_PAGEDOWN:
638 case WXK_NEXT:
639 Scroll(-1, sty + (5 * cliy / 6));
640 break;
641
642 // notice that handling of Ctrl-Home/End is asymmetrical: Home goes to
643 // the beginning of the current line, Ctrl-Home returns to the origin
644 // while End goes to the bottom without changing horizontal position
645 // and only Ctrl-End does go to the most rightmost position as well
646 case WXK_HOME:
647 Scroll(0, event.ControlDown() ? 0 : -1);
648 break;
649
650 case WXK_END:
651 Scroll(event.ControlDown() ? szx - clix : -1, szy - cliy);
652 break;
653
654 case WXK_UP:
655 Scroll(-1, sty - 1);
656 break;
657
658 case WXK_DOWN:
659 Scroll(-1, sty + 1);
660 break;
661
662 case WXK_LEFT:
663 Scroll(stx - 1, -1);
664 break;
665
666 case WXK_RIGHT:
667 Scroll(stx + 1, -1);
668 break;
669
670 default:
671 // not for us
672 event.Skip();
673 }
674 }