/* * Copyright (C) 2007 Kevin Ollivier <kevino@theolliviers.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ScrollView.h" #include "FloatRect.h" #include "IntRect.h" #include "NotImplemented.h" #include "PlatformWheelEvent.h" #include "Scrollbar.h" #include <algorithm> #include <stdio.h> #include <wx/defs.h> #include <wx/scrolbar.h> #include <wx/scrolwin.h> #include <wx/event.h> using namespace std; namespace WebCore { class ScrollView::ScrollViewPrivate : public wxEvtHandler { public: ScrollViewPrivate(ScrollView* scrollView) : wxEvtHandler() , m_scrollView(scrollView) , vScrollbarMode(ScrollbarAuto) , hScrollbarMode(ScrollbarAuto) , viewStart(0, 0) { } void bindEvents(wxWindow* win) { // TODO: is there an easier way to Connect to a range of events? these are contiguous. win->Connect(wxEVT_SCROLLWIN_TOP, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_BOTTOM, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_LINEUP, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_LINEDOWN, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_PAGEUP, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_PAGEDOWN, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); } void OnScrollWinEvents(wxScrollWinEvent& e) { wxEventType scrollType(e.GetEventType()); bool horiz = e.GetOrientation() == wxHORIZONTAL; wxPoint pos(viewStart); if (scrollType == wxEVT_SCROLLWIN_THUMBTRACK || scrollType == wxEVT_SCROLLWIN_THUMBRELEASE) { if (horiz) pos.x = e.GetPosition(); else pos.y = e.GetPosition(); } else if (scrollType == wxEVT_SCROLLWIN_LINEDOWN) { if (horiz) pos.x += Scrollbar::pixelsPerLineStep(); else pos.y += Scrollbar::pixelsPerLineStep(); } else if (scrollType == wxEVT_SCROLLWIN_LINEUP) { if (horiz) pos.x -= Scrollbar::pixelsPerLineStep(); else pos.y -= Scrollbar::pixelsPerLineStep(); } else if (scrollType == wxEVT_SCROLLWIN_PAGEUP) { if (horiz) pos.x -= max<int>(m_scrollView->visibleWidth() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleWidth() - Scrollbar::maxOverlapBetweenPages()); else pos.y -= max<int>(m_scrollView->visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleHeight() - Scrollbar::maxOverlapBetweenPages()); } else if (scrollType == wxEVT_SCROLLWIN_PAGEDOWN) { if (horiz) pos.x += max<int>(m_scrollView->visibleWidth() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleWidth() - Scrollbar::maxOverlapBetweenPages()); else pos.y += max<int>(m_scrollView->visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleHeight() - Scrollbar::maxOverlapBetweenPages()); } else return e.Skip(); m_scrollView->setScrollPosition(IntPoint(pos.x, pos.y)); } ScrollView* m_scrollView; ScrollbarMode vScrollbarMode; ScrollbarMode hScrollbarMode; wxPoint viewStart; }; void ScrollView::platformInit() { m_data = new ScrollViewPrivate(this); } void ScrollView::platformDestroy() { delete m_data; } void ScrollView::setPlatformWidget(wxWindow* win) { Widget::setPlatformWidget(win); m_data->bindEvents(win); } void ScrollView::platformRepaintContentRectangle(const IntRect& updateRect, bool now) { // we need to convert coordinates to scrolled position wxRect contentsRect = updateRect; contentsRect.Offset(-scrollX(), -scrollY()); wxWindow* win = platformWidget(); if (win) { win->RefreshRect(contentsRect, true); if (now) win->Update(); } } IntRect ScrollView::platformVisibleContentRect(bool includeScrollbars) const { wxWindow* win = platformWidget(); if (!win) return IntRect(); int width, height; if (includeScrollbars) win->GetSize(&width, &height); else win->GetClientSize(&width, &height); return IntRect(m_data->viewStart.x, m_data->viewStart.y, width, height); } IntSize ScrollView::platformContentsSize() const { int width = 0; int height = 0; if (platformWidget()) { platformWidget()->GetVirtualSize(&width, &height); ASSERT(width >= 0 && height >= 0); } return IntSize(width, height); } void ScrollView::platformSetScrollPosition(const IntPoint& scrollPoint) { wxWindow* win = platformWidget(); wxPoint scrollOffset = m_data->viewStart; wxPoint orig(scrollOffset); wxPoint newScrollOffset(scrollPoint); wxRect vRect(win->GetVirtualSize()); wxRect cRect(win->GetClientSize()); // clamp to scroll area if (newScrollOffset.x < 0) newScrollOffset.x = 0; else if (newScrollOffset.x + cRect.width > vRect.width) newScrollOffset.x = max(0, vRect.width - cRect.width); if (newScrollOffset.y < 0) newScrollOffset.y = 0; else if (newScrollOffset.y + cRect.height > vRect.height) newScrollOffset.y = max(0, vRect.height - cRect.height); if (newScrollOffset == scrollOffset) return; m_data->viewStart = newScrollOffset; wxPoint delta(orig - newScrollOffset); if (canBlitOnScroll()) win->ScrollWindow(delta.x, delta.y); else win->Refresh(); adjustScrollbars(); } bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity) { notImplemented(); return true; } void ScrollView::platformSetContentsSize() { wxWindow* win = platformWidget(); if (!win) return; win->SetVirtualSize(m_contentsSize.width(), m_contentsSize.height()); adjustScrollbars(); } void ScrollView::adjustScrollbars(int x, int y, bool refresh) { wxWindow* win = platformWidget(); if (!win) return; wxRect crect(win->GetClientRect()), vrect(win->GetVirtualSize()); if (x == -1) x = m_data->viewStart.x; if (y == -1) y = m_data->viewStart.y; long style = win->GetWindowStyle(); // by setting the wxALWAYS_SHOW_SB wxWindow flag before // each SetScrollbar call, we can control the scrollbars // visibility individually. // horizontal scrollbar switch (m_data->hScrollbarMode) { case ScrollbarAlwaysOff: win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB); win->SetScrollbar(wxHORIZONTAL, 0, 0, 0, refresh); break; case ScrollbarAuto: win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB); win->SetScrollbar(wxHORIZONTAL, x, crect.width, vrect.width, refresh); break; default: // ScrollbarAlwaysOn win->SetWindowStyleFlag(style | wxALWAYS_SHOW_SB); win->SetScrollbar(wxHORIZONTAL, x, crect.width, vrect.width, refresh); break; } // vertical scrollbar switch (m_data->vScrollbarMode) { case ScrollbarAlwaysOff: win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB); win->SetScrollbar(wxVERTICAL, 0, 0, 0, refresh); break; case ScrollbarAlwaysOn: win->SetWindowStyleFlag(style | wxALWAYS_SHOW_SB); win->SetScrollbar(wxVERTICAL, y, crect.height, vrect.height, refresh); break; default: // case ScrollbarAuto: win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB); win->SetScrollbar(wxVERTICAL, y, crect.height, vrect.height, refresh); } } void ScrollView::platformSetScrollbarModes() { bool needsAdjust = false; if (m_data->hScrollbarMode != horizontalScrollbarMode() ) { m_data->hScrollbarMode = horizontalScrollbarMode(); needsAdjust = true; } if (m_data->vScrollbarMode != verticalScrollbarMode() ) { m_data->vScrollbarMode = verticalScrollbarMode(); needsAdjust = true; } if (needsAdjust) adjustScrollbars(); } void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const { horizontal = m_data->hScrollbarMode; vertical = m_data->vScrollbarMode; } void ScrollView::platformSetCanBlitOnScroll(bool canBlitOnScroll) { m_canBlitOnScroll = canBlitOnScroll; } bool ScrollView::platformCanBlitOnScroll() const { return m_canBlitOnScroll; } // used for subframes support void ScrollView::platformAddChild(Widget* widget) { // NB: In all cases I'm aware of, // by the time this is called the ScrollView is already a child // of its parent Widget by wx port APIs, so I don't think // we need to do anything here. } void ScrollView::platformRemoveChild(Widget* widget) { if (platformWidget()) { platformWidget()->RemoveChild(widget->platformWidget()); // FIXME: Is this the right place to do deletion? I see // detachFromParent2/3/4, initiated by FrameLoader::detachFromParent, // but I'm not sure if it's better to handle there or not. widget->platformWidget()->Destroy(); } } IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const { if (platformWidget()) { wxRect wxrect = rect; platformWidget()->ClientToScreen(&wxrect.x, &wxrect.y); return wxrect; } return IntRect(); } IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const { if (platformWidget()) { return platformWidget()->ScreenToClient(point); } return IntPoint(); } bool ScrollView::platformIsOffscreen() const { return !platformWidget() || !platformWidget()->IsShownOnScreen(); } }