Home | History | Annotate | Download | only in wx
      1 /*
      2  * Copyright (C) 2007 Kevin Ollivier <kevino (at) theolliviers.com>
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "ScrollView.h"
     28 
     29 #include "FloatRect.h"
     30 #include "IntRect.h"
     31 #include "NotImplemented.h"
     32 #include "PlatformWheelEvent.h"
     33 #include "Scrollbar.h"
     34 
     35 #include <algorithm>
     36 #include <stdio.h>
     37 
     38 #include <wx/defs.h>
     39 #include <wx/scrolbar.h>
     40 #include <wx/scrolwin.h>
     41 #include <wx/event.h>
     42 
     43 using namespace std;
     44 
     45 namespace WebCore {
     46 
     47 class ScrollView::ScrollViewPrivate : public wxEvtHandler {
     48 
     49 public:
     50     ScrollViewPrivate(ScrollView* scrollView)
     51         : wxEvtHandler()
     52         , m_scrollView(scrollView)
     53         , vScrollbarMode(ScrollbarAuto)
     54         , hScrollbarMode(ScrollbarAuto)
     55         , viewStart(0, 0)
     56     {
     57     }
     58 
     59     void bindEvents(wxWindow* win)
     60     {
     61         // TODO: is there an easier way to Connect to a range of events? these are contiguous.
     62         win->Connect(wxEVT_SCROLLWIN_TOP,          wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
     63         win->Connect(wxEVT_SCROLLWIN_BOTTOM,       wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
     64         win->Connect(wxEVT_SCROLLWIN_LINEUP,       wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
     65         win->Connect(wxEVT_SCROLLWIN_LINEDOWN,     wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
     66         win->Connect(wxEVT_SCROLLWIN_PAGEUP,       wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
     67         win->Connect(wxEVT_SCROLLWIN_PAGEDOWN,     wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
     68         win->Connect(wxEVT_SCROLLWIN_THUMBTRACK,   wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
     69         win->Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
     70     }
     71 
     72     void OnScrollWinEvents(wxScrollWinEvent& e)
     73     {
     74         wxEventType scrollType(e.GetEventType());
     75         bool horiz = e.GetOrientation() == wxHORIZONTAL;
     76 
     77         wxPoint pos(viewStart);
     78 
     79         if (scrollType == wxEVT_SCROLLWIN_THUMBTRACK || scrollType == wxEVT_SCROLLWIN_THUMBRELEASE) {
     80             if (horiz)
     81                 pos.x = e.GetPosition();
     82             else
     83                 pos.y = e.GetPosition();
     84         }
     85         else if (scrollType == wxEVT_SCROLLWIN_LINEDOWN) {
     86             if (horiz)
     87                 pos.x += Scrollbar::pixelsPerLineStep();
     88             else
     89                 pos.y += Scrollbar::pixelsPerLineStep();
     90         }
     91         else if (scrollType == wxEVT_SCROLLWIN_LINEUP) {
     92             if (horiz)
     93                 pos.x -= Scrollbar::pixelsPerLineStep();
     94             else
     95                 pos.y -= Scrollbar::pixelsPerLineStep();
     96         }
     97         else if (scrollType == wxEVT_SCROLLWIN_PAGEUP) {
     98             if (horiz)
     99                 pos.x -= max<int>(m_scrollView->visibleWidth() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleWidth() - Scrollbar::maxOverlapBetweenPages());
    100             else
    101                 pos.y -= max<int>(m_scrollView->visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleHeight() - Scrollbar::maxOverlapBetweenPages());
    102         }
    103         else if (scrollType == wxEVT_SCROLLWIN_PAGEDOWN) {
    104             if (horiz)
    105                 pos.x += max<int>(m_scrollView->visibleWidth() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleWidth() - Scrollbar::maxOverlapBetweenPages());
    106             else
    107                 pos.y += max<int>(m_scrollView->visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleHeight() - Scrollbar::maxOverlapBetweenPages());
    108         }
    109         else
    110             return e.Skip();
    111 
    112         m_scrollView->setScrollPosition(IntPoint(pos.x, pos.y));
    113     }
    114 
    115     ScrollView* m_scrollView;
    116 
    117     ScrollbarMode vScrollbarMode;
    118     ScrollbarMode hScrollbarMode;
    119     wxPoint viewStart;
    120 };
    121 
    122 void ScrollView::platformInit()
    123 {
    124     m_data = new ScrollViewPrivate(this);
    125 }
    126 
    127 
    128 void ScrollView::platformDestroy()
    129 {
    130     delete m_data;
    131 }
    132 
    133 void ScrollView::setPlatformWidget(wxWindow* win)
    134 {
    135     Widget::setPlatformWidget(win);
    136     m_data->bindEvents(win);
    137 }
    138 
    139 void ScrollView::platformRepaintContentRectangle(const IntRect& updateRect, bool now)
    140 {
    141     // we need to convert coordinates to scrolled position
    142     wxRect contentsRect = updateRect;
    143     contentsRect.Offset(-scrollX(), -scrollY());
    144     wxWindow* win = platformWidget();
    145     if (win) {
    146         win->RefreshRect(contentsRect, true);
    147         if (now)
    148             win->Update();
    149     }
    150 }
    151 
    152 IntRect ScrollView::platformVisibleContentRect(bool includeScrollbars) const
    153 {
    154     wxWindow* win = platformWidget();
    155     if (!win)
    156         return IntRect();
    157 
    158     int width, height;
    159 
    160     if (includeScrollbars)
    161         win->GetSize(&width, &height);
    162     else
    163         win->GetClientSize(&width, &height);
    164 
    165     return IntRect(m_data->viewStart.x, m_data->viewStart.y, width, height);
    166 }
    167 
    168 IntSize ScrollView::platformContentsSize() const
    169 {
    170     int width = 0;
    171     int height = 0;
    172     if (platformWidget()) {
    173         platformWidget()->GetVirtualSize(&width, &height);
    174         ASSERT(width >= 0 && height >= 0);
    175     }
    176     return IntSize(width, height);
    177 }
    178 
    179 void ScrollView::platformSetScrollPosition(const IntPoint& scrollPoint)
    180 {
    181     wxWindow* win = platformWidget();
    182 
    183     wxPoint scrollOffset = m_data->viewStart;
    184     wxPoint orig(scrollOffset);
    185     wxPoint newScrollOffset(scrollPoint);
    186 
    187     wxRect vRect(win->GetVirtualSize());
    188     wxRect cRect(win->GetClientSize());
    189 
    190     // clamp to scroll area
    191     if (newScrollOffset.x < 0)
    192         newScrollOffset.x = 0;
    193     else if (newScrollOffset.x + cRect.width > vRect.width)
    194         newScrollOffset.x = max(0, vRect.width - cRect.width);
    195 
    196     if (newScrollOffset.y < 0)
    197         newScrollOffset.y = 0;
    198     else if (newScrollOffset.y + cRect.height > vRect.height)
    199         newScrollOffset.y = max(0, vRect.height - cRect.height);
    200 
    201     if (newScrollOffset == scrollOffset)
    202         return;
    203 
    204     m_data->viewStart = newScrollOffset;
    205 
    206     wxPoint delta(orig - newScrollOffset);
    207 
    208     if (canBlitOnScroll())
    209         win->ScrollWindow(delta.x, delta.y);
    210     else
    211         win->Refresh();
    212 
    213     adjustScrollbars();
    214 }
    215 
    216 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
    217 {
    218     notImplemented();
    219     return true;
    220 }
    221 
    222 void ScrollView::platformSetContentsSize()
    223 {
    224     wxWindow* win = platformWidget();
    225     if (!win)
    226         return;
    227 
    228     win->SetVirtualSize(m_contentsSize.width(), m_contentsSize.height());
    229     adjustScrollbars();
    230 }
    231 
    232 void ScrollView::adjustScrollbars(int x, int y, bool refresh)
    233 {
    234     wxWindow* win = platformWidget();
    235     if (!win)
    236         return;
    237 
    238     wxRect crect(win->GetClientRect()), vrect(win->GetVirtualSize());
    239 
    240     if (x == -1) x = m_data->viewStart.x;
    241     if (y == -1) y = m_data->viewStart.y;
    242 
    243     long style = win->GetWindowStyle();
    244 
    245     // by setting the wxALWAYS_SHOW_SB wxWindow flag before
    246     // each SetScrollbar call, we can control the scrollbars
    247     // visibility individually.
    248 
    249     // horizontal scrollbar
    250     switch (m_data->hScrollbarMode) {
    251         case ScrollbarAlwaysOff:
    252             win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB);
    253             win->SetScrollbar(wxHORIZONTAL, 0, 0, 0, refresh);
    254             break;
    255 
    256         case ScrollbarAuto:
    257             win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB);
    258             win->SetScrollbar(wxHORIZONTAL, x, crect.width, vrect.width, refresh);
    259             break;
    260 
    261         default: // ScrollbarAlwaysOn
    262             win->SetWindowStyleFlag(style | wxALWAYS_SHOW_SB);
    263             win->SetScrollbar(wxHORIZONTAL, x, crect.width, vrect.width, refresh);
    264             break;
    265     }
    266 
    267     // vertical scrollbar
    268     switch (m_data->vScrollbarMode) {
    269         case ScrollbarAlwaysOff:
    270             win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB);
    271             win->SetScrollbar(wxVERTICAL, 0, 0, 0, refresh);
    272             break;
    273 
    274         case ScrollbarAlwaysOn:
    275             win->SetWindowStyleFlag(style | wxALWAYS_SHOW_SB);
    276             win->SetScrollbar(wxVERTICAL, y, crect.height, vrect.height, refresh);
    277             break;
    278 
    279         default: // case ScrollbarAuto:
    280             win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB);
    281             win->SetScrollbar(wxVERTICAL, y, crect.height, vrect.height, refresh);
    282     }
    283 }
    284 
    285 void ScrollView::platformSetScrollbarModes()
    286 {
    287     bool needsAdjust = false;
    288 
    289     if (m_data->hScrollbarMode != horizontalScrollbarMode() ) {
    290         m_data->hScrollbarMode = horizontalScrollbarMode();
    291         needsAdjust = true;
    292     }
    293 
    294     if (m_data->vScrollbarMode != verticalScrollbarMode() ) {
    295         m_data->vScrollbarMode = verticalScrollbarMode();
    296         needsAdjust = true;
    297     }
    298 
    299     if (needsAdjust)
    300         adjustScrollbars();
    301 }
    302 
    303 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
    304 {
    305     horizontal = m_data->hScrollbarMode;
    306     vertical = m_data->vScrollbarMode;
    307 }
    308 
    309 void ScrollView::platformSetCanBlitOnScroll(bool canBlitOnScroll)
    310 {
    311     m_canBlitOnScroll = canBlitOnScroll;
    312 }
    313 
    314 bool ScrollView::platformCanBlitOnScroll() const
    315 {
    316     return m_canBlitOnScroll;
    317 }
    318 
    319 // used for subframes support
    320 void ScrollView::platformAddChild(Widget* widget)
    321 {
    322     // NB: In all cases I'm aware of,
    323     // by the time this is called the ScrollView is already a child
    324     // of its parent Widget by wx port APIs, so I don't think
    325     // we need to do anything here.
    326 }
    327 
    328 void ScrollView::platformRemoveChild(Widget* widget)
    329 {
    330     if (platformWidget()) {
    331         platformWidget()->RemoveChild(widget->platformWidget());
    332         // FIXME: Is this the right place to do deletion? I see
    333         // detachFromParent2/3/4, initiated by FrameLoader::detachFromParent,
    334         // but I'm not sure if it's better to handle there or not.
    335         widget->platformWidget()->Destroy();
    336     }
    337 }
    338 
    339 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
    340 {
    341     if (platformWidget()) {
    342         wxRect wxrect = rect;
    343         platformWidget()->ClientToScreen(&wxrect.x, &wxrect.y);
    344         return wxrect;
    345     }
    346     return IntRect();
    347 }
    348 
    349 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
    350 {
    351     if (platformWidget()) {
    352         return platformWidget()->ScreenToClient(point);
    353     }
    354     return IntPoint();
    355 }
    356 
    357 bool ScrollView::platformIsOffscreen() const
    358 {
    359     return !platformWidget() || !platformWidget()->IsShownOnScreen();
    360 }
    361 
    362 }
    363