| /* |
| * 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); |
| } |
| |
| 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(); |
| } |
| |
| } |