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