1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "WebScrollbarImpl.h" 33 34 #include "GraphicsContext.h" 35 #include "KeyboardCodes.h" 36 #include "painting/GraphicsContextBuilder.h" 37 #include "Scrollbar.h" 38 #include "ScrollbarTheme.h" 39 #include "ScrollTypes.h" 40 #include "WebCanvas.h" 41 #include "WebInputEvent.h" 42 #include "WebInputEventConversion.h" 43 #include "WebRect.h" 44 #include "WebScrollbarClient.h" 45 #include "WebVector.h" 46 #include "WebViewImpl.h" 47 48 using namespace std; 49 using namespace WebCore; 50 51 namespace WebKit { 52 53 WebScrollbar* WebScrollbar::create(WebScrollbarClient* client, Orientation orientation) 54 { 55 return new WebScrollbarImpl(client, orientation); 56 } 57 58 int WebScrollbar::defaultThickness() 59 { 60 return ScrollbarTheme::nativeTheme()->scrollbarThickness(); 61 } 62 63 WebScrollbarImpl::WebScrollbarImpl(WebScrollbarClient* client, Orientation orientation) 64 : m_client(client) 65 , m_scrollOffset(0) 66 { 67 m_scrollbar = Scrollbar::createNativeScrollbar( 68 static_cast<ScrollableArea*>(this), 69 static_cast<ScrollbarOrientation>(orientation), 70 RegularScrollbar); 71 } 72 73 WebScrollbarImpl::~WebScrollbarImpl() 74 { 75 } 76 77 void WebScrollbarImpl::setLocation(const WebRect& rect) 78 { 79 IntRect oldRect = m_scrollbar->frameRect(); 80 m_scrollbar->setFrameRect(rect); 81 if (WebRect(oldRect) != rect) 82 m_scrollbar->invalidate(); 83 84 int length = m_scrollbar->orientation() == HorizontalScrollbar ? m_scrollbar->width() : m_scrollbar->height(); 85 int pageStep = max(max(static_cast<int>(static_cast<float>(length) * Scrollbar::minFractionToStepWhenPaging()), length - Scrollbar::maxOverlapBetweenPages()), 1); 86 m_scrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); 87 m_scrollbar->setEnabled(m_scrollbar->totalSize() > length); 88 m_scrollbar->setProportion(length, m_scrollbar->totalSize()); 89 } 90 91 int WebScrollbarImpl::value() const 92 { 93 return m_scrollOffset; 94 } 95 96 void WebScrollbarImpl::setValue(int position) 97 { 98 ScrollableArea::scrollToOffsetWithoutAnimation(m_scrollbar->orientation(), static_cast<float>(position)); 99 } 100 101 void WebScrollbarImpl::setDocumentSize(int size) 102 { 103 int length = m_scrollbar->orientation() == HorizontalScrollbar ? m_scrollbar->width() : m_scrollbar->height(); 104 m_scrollbar->setEnabled(size > length); 105 m_scrollbar->setProportion(length, size); 106 } 107 108 void WebScrollbarImpl::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) 109 { 110 WebCore::ScrollDirection dir; 111 bool horizontal = m_scrollbar->orientation() == HorizontalScrollbar; 112 if (direction == ScrollForward) 113 dir = horizontal ? ScrollRight : ScrollDown; 114 else 115 dir = horizontal ? ScrollLeft : ScrollUp; 116 117 WebCore::ScrollableArea::scroll(dir, static_cast<WebCore::ScrollGranularity>(granularity), multiplier); 118 } 119 120 void WebScrollbarImpl::paint(WebCanvas* canvas, const WebRect& rect) 121 { 122 m_scrollbar->paint(&GraphicsContextBuilder(canvas).context(), rect); 123 } 124 125 bool WebScrollbarImpl::handleInputEvent(const WebInputEvent& event) 126 { 127 switch (event.type) { 128 case WebInputEvent::MouseDown: 129 return onMouseDown(event); 130 case WebInputEvent::MouseUp: 131 return onMouseUp(event); 132 case WebInputEvent::MouseMove: 133 return onMouseMove(event); 134 case WebInputEvent::MouseLeave: 135 return onMouseLeave(event); 136 case WebInputEvent::MouseWheel: 137 return onMouseWheel(event); 138 case WebInputEvent::KeyDown: 139 return onKeyDown(event); 140 case WebInputEvent::Undefined: 141 case WebInputEvent::MouseEnter: 142 case WebInputEvent::RawKeyDown: 143 case WebInputEvent::KeyUp: 144 case WebInputEvent::Char: 145 case WebInputEvent::TouchStart: 146 case WebInputEvent::TouchMove: 147 case WebInputEvent::TouchEnd: 148 case WebInputEvent::TouchCancel: 149 default: 150 break; 151 } 152 return false; 153 } 154 155 bool WebScrollbarImpl::onMouseDown(const WebInputEvent& event) 156 { 157 WebMouseEvent mousedown = *static_cast<const WebMouseEvent*>(&event); 158 if (!m_scrollbar->frameRect().contains(mousedown.x, mousedown.y)) 159 return false; 160 161 mousedown.x -= m_scrollbar->x(); 162 mousedown.y -= m_scrollbar->y(); 163 m_scrollbar->mouseDown(PlatformMouseEventBuilder(m_scrollbar.get(), mousedown)); 164 return true; 165 } 166 167 bool WebScrollbarImpl::onMouseUp(const WebInputEvent& event) 168 { 169 if (m_scrollbar->pressedPart() == NoPart) 170 return false; 171 172 return m_scrollbar->mouseUp(); 173 } 174 175 bool WebScrollbarImpl::onMouseMove(const WebInputEvent& event) 176 { 177 WebMouseEvent mousemove = *static_cast<const WebMouseEvent*>(&event); 178 if (m_scrollbar->frameRect().contains(mousemove.x, mousemove.y) 179 || m_scrollbar->pressedPart() != NoPart) { 180 mousemove.x -= m_scrollbar->x(); 181 mousemove.y -= m_scrollbar->y(); 182 return m_scrollbar->mouseMoved(PlatformMouseEventBuilder(m_scrollbar.get(), mousemove)); 183 } 184 185 if (m_scrollbar->hoveredPart() != NoPart) 186 m_scrollbar->mouseExited(); 187 return false; 188 } 189 190 bool WebScrollbarImpl::onMouseLeave(const WebInputEvent& event) 191 { 192 if (m_scrollbar->hoveredPart() == NoPart) 193 return false; 194 195 return m_scrollbar->mouseExited(); 196 } 197 198 bool WebScrollbarImpl::onMouseWheel(const WebInputEvent& event) 199 { 200 // Same logic as in Scrollview.cpp. If we can move at all, we'll accept the event. 201 WebMouseWheelEvent mousewheel = *static_cast<const WebMouseWheelEvent*>(&event); 202 int maxScrollDelta = m_scrollbar->maximum() - m_scrollbar->value(); 203 float delta = m_scrollbar->orientation() == HorizontalScrollbar ? mousewheel.deltaX : mousewheel.deltaY; 204 if ((delta < 0 && maxScrollDelta > 0) || (delta > 0 && m_scrollbar->value() > 0)) { 205 if (mousewheel.scrollByPage) { 206 ASSERT(m_scrollbar->orientation() == VerticalScrollbar); 207 bool negative = delta < 0; 208 delta = max(max(static_cast<float>(m_scrollbar->visibleSize()) * Scrollbar::minFractionToStepWhenPaging(), static_cast<float>(m_scrollbar->visibleSize() - Scrollbar::maxOverlapBetweenPages())), 1.0f); 209 if (negative) 210 delta *= -1; 211 } 212 ScrollableArea::scroll((m_scrollbar->orientation() == HorizontalScrollbar) ? WebCore::ScrollLeft : WebCore::ScrollUp, WebCore::ScrollByPixel, delta); 213 return true; 214 } 215 216 return false; 217 } 218 219 bool WebScrollbarImpl::onKeyDown(const WebInputEvent& event) 220 { 221 WebKeyboardEvent keyboard = *static_cast<const WebKeyboardEvent*>(&event); 222 int keyCode; 223 // We have to duplicate this logic from WebViewImpl because there it uses 224 // Char and RawKeyDown events, which don't exist at this point. 225 if (keyboard.windowsKeyCode == VKEY_SPACE) 226 keyCode = ((keyboard.modifiers & WebInputEvent::ShiftKey) ? VKEY_PRIOR : VKEY_NEXT); 227 else { 228 if (keyboard.modifiers == WebInputEvent::ControlKey) { 229 // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl 230 // key combinations which affect scrolling. Safari is buggy in the 231 // sense that it scrolls the page for all Ctrl+scrolling key 232 // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc. 233 switch (keyboard.windowsKeyCode) { 234 case VKEY_HOME: 235 case VKEY_END: 236 break; 237 default: 238 return false; 239 } 240 } 241 242 if (keyboard.isSystemKey || (keyboard.modifiers & WebInputEvent::ShiftKey)) 243 return false; 244 245 keyCode = keyboard.windowsKeyCode; 246 } 247 WebCore::ScrollDirection scrollDirection; 248 WebCore::ScrollGranularity scrollGranularity; 249 if (WebViewImpl::mapKeyCodeForScroll(keyCode, &scrollDirection, &scrollGranularity)) { 250 // Will return false if scroll direction wasn't compatible with this scrollbar. 251 return ScrollableArea::scroll(scrollDirection, scrollGranularity); 252 } 253 return false; 254 } 255 256 int WebScrollbarImpl::scrollSize(WebCore::ScrollbarOrientation orientation) const 257 { 258 return (orientation == m_scrollbar->orientation()) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0; 259 } 260 261 int WebScrollbarImpl::scrollPosition(Scrollbar*) const 262 { 263 return m_scrollOffset; 264 } 265 266 void WebScrollbarImpl::setScrollOffset(const IntPoint& offset) 267 { 268 if (m_scrollbar->orientation() == HorizontalScrollbar) 269 m_scrollOffset = offset.x(); 270 else 271 m_scrollOffset = offset.y(); 272 273 m_client->valueChanged(this); 274 } 275 276 void WebScrollbarImpl::invalidateScrollbarRect(Scrollbar*, const IntRect& rect) 277 { 278 WebRect webrect(rect); 279 webrect.x += m_scrollbar->x(); 280 webrect.y += m_scrollbar->y(); 281 m_client->invalidateScrollbarRect(this, webrect); 282 } 283 284 void WebScrollbarImpl::invalidateScrollCornerRect(const IntRect&) 285 { 286 } 287 288 bool WebScrollbarImpl::isActive() const 289 { 290 return true; 291 } 292 293 bool WebScrollbarImpl::isScrollCornerVisible() const 294 { 295 return false; 296 } 297 298 void WebScrollbarImpl::getTickmarks(Vector<IntRect>& tickmarks) const 299 { 300 WebVector<WebRect> ticks; 301 m_client->getTickmarks(const_cast<WebScrollbarImpl*>(this), &ticks); 302 tickmarks.resize(ticks.size()); 303 for (size_t i = 0; i < ticks.size(); ++i) 304 tickmarks[i] = ticks[i]; 305 } 306 307 Scrollbar* WebScrollbarImpl::horizontalScrollbar() const 308 { 309 return m_scrollbar->orientation() == HorizontalScrollbar ? m_scrollbar.get() : 0; 310 } 311 312 Scrollbar* WebScrollbarImpl::verticalScrollbar() const 313 { 314 return m_scrollbar->orientation() == VerticalScrollbar ? m_scrollbar.get() : 0; 315 } 316 317 } // namespace WebKit 318