+
+#ifdef wxHAS_GENERIC_SCROLLWIN
+
+// ----------------------------------------------------------------------------
+// wxScrollHelper implementation
+// ----------------------------------------------------------------------------
+
+wxScrollHelper::wxScrollHelper(wxWindow *winToScroll)
+ : wxScrollHelperBase(winToScroll)
+{
+ m_xVisibility =
+ m_yVisibility = wxSHOW_SB_DEFAULT;
+}
+
+void wxScrollHelper::DoShowScrollbars(wxScrollbarVisibility horz,
+ wxScrollbarVisibility vert)
+{
+ if ( horz != m_xVisibility || vert != m_yVisibility )
+ {
+ m_xVisibility = horz;
+ m_yVisibility = vert;
+
+ AdjustScrollbars();
+ }
+}
+
+void
+wxScrollHelper::DoAdjustScrollbar(int orient,
+ int clientSize,
+ int virtSize,
+ int pixelsPerUnit,
+ int& scrollUnits,
+ int& scrollPosition,
+ int& scrollLinesPerPage,
+ wxScrollbarVisibility visibility)
+{
+ // scroll lines per page: if 0, no scrolling is needed
+ // check if we need scrollbar in this direction at all
+ if ( pixelsPerUnit == 0 || clientSize >= virtSize )
+ {
+ // scrolling is disabled or unnecessary
+ scrollUnits =
+ scrollPosition = 0;
+ scrollLinesPerPage = 0;
+ }
+ else // might need scrolling
+ {
+ // Round up integer division to catch any "leftover" client space.
+ scrollUnits = (virtSize + pixelsPerUnit - 1) / pixelsPerUnit;
+
+ // Calculate the number of fully scroll units
+ scrollLinesPerPage = clientSize / pixelsPerUnit;
+
+ if ( scrollLinesPerPage >= scrollUnits )
+ {
+ // we're big enough to not need scrolling
+ scrollUnits =
+ scrollPosition = 0;
+ scrollLinesPerPage = 0;
+ }
+ else // we do need a scrollbar
+ {
+ if ( scrollLinesPerPage < 1 )
+ scrollLinesPerPage = 1;
+
+ // Correct position if greater than extent of canvas minus
+ // the visible portion of it or if below zero
+ const int posMax = scrollUnits - scrollLinesPerPage;
+ if ( scrollPosition > posMax )
+ scrollPosition = posMax;
+ else if ( scrollPosition < 0 )
+ scrollPosition = 0;
+ }
+ }
+
+ // in wxSHOW_SB_NEVER case don't show the scrollbar even if it's needed, in
+ // wxSHOW_SB_ALWAYS case show the scrollbar even if it's not needed by
+ // passing a special range value to SetScrollbar()
+ int range wxDUMMY_INITIALIZE(0);
+ switch ( visibility )
+ {
+ case wxSHOW_SB_NEVER:
+ range = 0;
+ break;
+
+ case wxSHOW_SB_DEFAULT:
+ range = scrollUnits;
+ break;
+
+ case wxSHOW_SB_ALWAYS:
+ range = scrollUnits ? scrollUnits : -1;
+ break;
+ }
+
+ m_win->SetScrollbar(orient, scrollPosition, scrollLinesPerPage, range);
+}
+
+void wxScrollHelper::AdjustScrollbars()
+{
+ static wxRecursionGuardFlag s_flagReentrancy;
+ wxRecursionGuard guard(s_flagReentrancy);
+ if ( guard.IsInside() )
+ {
+ // don't reenter AdjustScrollbars() while another call to
+ // AdjustScrollbars() is in progress because this may lead to calling
+ // ScrollWindow() twice and this can really happen under MSW if
+ // SetScrollbar() call below adds or removes the scrollbar which
+ // changes the window size and hence results in another
+ // AdjustScrollbars() call
+ return;
+ }
+
+ int oldXScroll = m_xScrollPosition;
+ int oldYScroll = m_yScrollPosition;
+
+ // we may need to readjust the scrollbars several times as enabling one of
+ // them reduces the area available for the window contents and so can make
+ // the other scrollbar necessary now although it wasn't necessary before
+ //
+ // VZ: normally this loop should be over in at most 2 iterations, I don't
+ // know why do we need 5 of them
+ for ( int iterationCount = 0; iterationCount < 5; iterationCount++ )
+ {
+ wxSize clientSize = GetTargetSize();
+ const wxSize virtSize = m_targetWindow->GetVirtualSize();
+
+ // this block of code tries to work around the following problem: the
+ // window could have been just resized to have enough space to show its
+ // full contents without the scrollbars, but its client size could be
+ // not big enough because it does have the scrollbars right now and so
+ // the scrollbars would remain even though we don't need them any more
+ //
+ // to prevent this from happening, check if we have enough space for
+ // everything without the scrollbars and explicitly disable them then
+ const wxSize availSize = GetSizeAvailableForScrollTarget(
+ m_win->GetSize() - m_win->GetWindowBorderSize());
+ if ( availSize != clientSize )
+ {
+ if ( availSize.x >= virtSize.x && availSize.y >= virtSize.y )
+ {
+ // this will be enough to make the scrollbars disappear below
+ // and then the client size will indeed become equal to the
+ // full available size
+ clientSize = availSize;
+ }
+ }
+
+
+ DoAdjustScrollbar(wxHORIZONTAL,
+ clientSize.x,
+ virtSize.x,
+ m_xScrollPixelsPerLine,
+ m_xScrollLines,
+ m_xScrollPosition,
+ m_xScrollLinesPerPage,
+ m_xVisibility);
+
+ DoAdjustScrollbar(wxVERTICAL,
+ clientSize.y,
+ virtSize.y,
+ m_yScrollPixelsPerLine,
+ m_yScrollLines,
+ m_yScrollPosition,
+ m_yScrollLinesPerPage,
+ m_yVisibility);
+
+
+ // If a scrollbar (dis)appeared as a result of this, we need to adjust
+ // them again but if the client size didn't change, then we're done
+ if ( GetTargetSize() == clientSize )
+ break;
+ }
+
+#ifdef __WXMOTIF__
+ // Sorry, some Motif-specific code to implement a backing pixmap
+ // for the wxRETAINED style. Implementing a backing store can't
+ // be entirely generic because it relies on the wxWindowDC implementation
+ // to duplicate X drawing calls for the backing pixmap.
+
+ if ( m_targetWindow->GetWindowStyle() & wxRETAINED )
+ {
+ Display* dpy = XtDisplay((Widget)m_targetWindow->GetMainWidget());
+
+ int totalPixelWidth = m_xScrollLines * m_xScrollPixelsPerLine;
+ int totalPixelHeight = m_yScrollLines * m_yScrollPixelsPerLine;
+ if (m_targetWindow->GetBackingPixmap() &&
+ !((m_targetWindow->GetPixmapWidth() == totalPixelWidth) &&
+ (m_targetWindow->GetPixmapHeight() == totalPixelHeight)))
+ {
+ XFreePixmap (dpy, (Pixmap) m_targetWindow->GetBackingPixmap());
+ m_targetWindow->SetBackingPixmap((WXPixmap) 0);
+ }
+
+ if (!m_targetWindow->GetBackingPixmap() &&
+ (m_xScrollLines != 0) && (m_yScrollLines != 0))
+ {
+ int depth = wxDisplayDepth();
+ m_targetWindow->SetPixmapWidth(totalPixelWidth);
+ m_targetWindow->SetPixmapHeight(totalPixelHeight);
+ m_targetWindow->SetBackingPixmap((WXPixmap) XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
+ m_targetWindow->GetPixmapWidth(), m_targetWindow->GetPixmapHeight(), depth));
+ }
+
+ }
+#endif // Motif
+
+ if (oldXScroll != m_xScrollPosition)
+ {
+ if (m_xScrollingEnabled)
+ m_targetWindow->ScrollWindow( m_xScrollPixelsPerLine * (oldXScroll - m_xScrollPosition), 0,
+ GetScrollRect() );
+ else
+ m_targetWindow->Refresh(true, GetScrollRect());
+ }
+
+ if (oldYScroll != m_yScrollPosition)
+ {
+ if (m_yScrollingEnabled)
+ m_targetWindow->ScrollWindow( 0, m_yScrollPixelsPerLine * (oldYScroll-m_yScrollPosition),
+ GetScrollRect() );
+ else
+ m_targetWindow->Refresh(true, GetScrollRect());
+ }
+}
+
+void wxScrollHelper::DoScroll( int x_pos, int y_pos )
+{
+ if (!m_targetWindow)
+ return;
+
+ if (((x_pos == -1) || (x_pos == m_xScrollPosition)) &&
+ ((y_pos == -1) || (y_pos == m_yScrollPosition))) return;
+
+ int w = 0, h = 0;
+ GetTargetSize(&w, &h);
+
+ // compute new position:
+ int new_x = m_xScrollPosition;
+ int new_y = m_yScrollPosition;
+
+ if ((x_pos != -1) && (m_xScrollPixelsPerLine))
+ {
+ new_x = x_pos;
+
+ // Calculate page size i.e. number of scroll units you get on the
+ // current client window
+ int noPagePositions = w/m_xScrollPixelsPerLine;
+ if (noPagePositions < 1) noPagePositions = 1;
+
+ // Correct position if greater than extent of canvas minus
+ // the visible portion of it or if below zero
+ new_x = wxMin( m_xScrollLines-noPagePositions, new_x );
+ new_x = wxMax( 0, new_x );
+ }
+ if ((y_pos != -1) && (m_yScrollPixelsPerLine))
+ {
+ new_y = y_pos;
+
+ // Calculate page size i.e. number of scroll units you get on the
+ // current client window
+ int noPagePositions = h/m_yScrollPixelsPerLine;
+ if (noPagePositions < 1) noPagePositions = 1;
+
+ // Correct position if greater than extent of canvas minus
+ // the visible portion of it or if below zero
+ new_y = wxMin( m_yScrollLines-noPagePositions, new_y );
+ new_y = wxMax( 0, new_y );
+ }
+
+ if ( new_x == m_xScrollPosition && new_y == m_yScrollPosition )
+ return; // nothing to do, the position didn't change
+
+ // flush all pending repaints before we change m_{x,y}ScrollPosition, as
+ // otherwise invalidated area could be updated incorrectly later when
+ // ScrollWindow() makes sure they're repainted before scrolling them
+ m_targetWindow->Update();
+
+ // update the position and scroll the window now:
+ if (m_xScrollPosition != new_x)
+ {
+ int old_x = m_xScrollPosition;
+ m_xScrollPosition = new_x;
+ m_win->SetScrollPos( wxHORIZONTAL, new_x );
+ m_targetWindow->ScrollWindow( (old_x-new_x)*m_xScrollPixelsPerLine, 0,
+ GetScrollRect() );
+ }
+
+ if (m_yScrollPosition != new_y)
+ {
+ int old_y = m_yScrollPosition;
+ m_yScrollPosition = new_y;
+ m_win->SetScrollPos( wxVERTICAL, new_y );
+ m_targetWindow->ScrollWindow( 0, (old_y-new_y)*m_yScrollPixelsPerLine,
+ GetScrollRect() );
+ }
+}
+
+#endif // wxHAS_GENERIC_SCROLLWIN
+