| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2006, 2007 Apple 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 | |
| 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 "Chrome.h" | |
| 30 #include "ChromeClient.h" | |
| 31 #include "FloatRect.h" | |
| 32 #include "FocusController.h" | |
| 33 #include "Frame.h" | |
| 34 #include "FrameView.h" | |
| 35 #include "GraphicsContext.h" | |
| 36 #include "IntRect.h" | |
| 37 #include "NotImplemented.h" | |
| 38 #include "Page.h" | |
| 39 #include "PlatformScrollBar.h" | |
| 40 #include "PlatformMouseEvent.h" | |
| 41 #include "PlatformWheelEvent.h" | |
| 42 #include "Range.h" | |
| 43 #include "RenderTheme.h" | |
| 44 #include "ScrollBar.h" | |
| 45 #include "SkiaUtils.h" | |
| 46 #include "WidgetClientWin.h" | |
| 47 #include <algorithm> | |
| 48 #include <wtf/Assertions.h> | |
| 49 #include <wtf/HashSet.h> | |
| 50 | |
| 51 #undef LOG | |
| 52 #include "base/gfx/platform_canvas_win.h" | |
| 53 #include "webkit/glue/webframe_impl.h" | |
| 54 #include "webkit/glue/webview_impl.h" | |
| 55 | |
| 56 using namespace std; | |
| 57 | |
| 58 namespace WebCore { | |
| 59 | |
| 60 class ScrollView::ScrollViewPrivate : public ScrollbarClient { | |
| 61 public: | |
| 62 ScrollViewPrivate(ScrollView* view) | |
| 63 : m_view(view) | |
| 64 , m_hasStaticBackground(false) | |
| 65 , m_scrollbarsSuppressed(false) | |
| 66 , m_inUpdateScrollbars(false) | |
| 67 , m_scrollbarsAvoidingResizer(0) | |
| 68 , m_vScrollbarMode(ScrollbarAuto) | |
| 69 , m_hScrollbarMode(ScrollbarAuto) | |
| 70 , m_visible(false) | |
| 71 , m_attachedToWindow(false) | |
| 72 , m_panScrollIconPoint(0,0) | |
| 73 , m_drawPanScrollIcon(false) | |
| 74 { | |
| 75 } | |
| 76 | |
| 77 ~ScrollViewPrivate() | |
| 78 { | |
| 79 setHasHorizontalScrollbar(false); | |
| 80 setHasVerticalScrollbar(false); | |
| 81 } | |
| 82 | |
| 83 void setHasHorizontalScrollbar(bool hasBar); | |
| 84 void setHasVerticalScrollbar(bool hasBar); | |
| 85 | |
| 86 virtual void valueChanged(Scrollbar*); | |
| 87 virtual IntRect windowClipRect() const; | |
| 88 virtual bool isActive() const; | |
| 89 | |
| 90 void scrollBackingStore(const IntSize& scrollDelta); | |
| 91 | |
| 92 // Get the vector containing the result from the FindInPage operation. | |
| 93 const Vector<RefPtr<Range> >* getTickmarks() const; | |
| 94 | |
| 95 // Retrieves the index of the active tickmark for a given frame. If the | |
| 96 // frame does not have an active tickmark (for example if the active | |
| 97 // tickmark resides in another frame) this function returns kNoTickmark. | |
| 98 size_t ScrollView::ScrollViewPrivate::getActiveTickmarkIndex() const; | |
| 99 | |
| 100 // This is a helper function for accessing the bitmaps that have been cached | |
| 101 // in the renderer. | |
| 102 const SkBitmap* GetPreloadedBitmapFromRenderer(int resource_id) const; | |
| 103 | |
| 104 // Highlight the matches found during FindInPage operation. | |
| 105 void highlightMatches(GraphicsContext* context) const; | |
| 106 | |
| 107 // Highlights the node selected in the DOM inspector. | |
| 108 void highlightInspectedNode(GraphicsContext* context, Frame* frame) const; | |
| 109 | |
| 110 // Highlight a certain Range on the page. | |
| 111 void highlightRange(HDC hdc, HDC mem_dc, RefPtr<Range> range) const; | |
| 112 | |
| 113 void setAllowsScrolling(bool); | |
| 114 bool allowsScrolling() const; | |
| 115 | |
| 116 ScrollView* m_view; | |
| 117 IntSize m_scrollOffset; | |
| 118 IntSize m_contentsSize; | |
| 119 bool m_hasStaticBackground; | |
| 120 bool m_scrollbarsSuppressed; | |
| 121 bool m_inUpdateScrollbars; | |
| 122 int m_scrollbarsAvoidingResizer; | |
| 123 ScrollbarMode m_vScrollbarMode; | |
| 124 ScrollbarMode m_hScrollbarMode; | |
| 125 RefPtr<PlatformScrollbar> m_vBar; | |
| 126 RefPtr<PlatformScrollbar> m_hBar; | |
| 127 HRGN m_dirtyRegion; | |
| 128 HashSet<Widget*> m_children; | |
| 129 bool m_visible; | |
| 130 bool m_attachedToWindow; | |
| 131 IntPoint m_panScrollIconPoint; | |
| 132 bool m_drawPanScrollIcon; | |
| 133 }; | |
| 134 | |
| 135 const int panIconSizeLength = 20; | |
| 136 | |
| 137 void ScrollView::ScrollViewPrivate::setHasHorizontalScrollbar(bool hasBar) | |
| 138 { | |
| 139 if (Scrollbar::hasPlatformScrollbars()) { | |
| 140 if (hasBar && !m_hBar) { | |
| 141 m_hBar = PlatformScrollbar::create(this, HorizontalScrollbar, Regula
rScrollbar); | |
| 142 m_view->addChild(m_hBar.get()); | |
| 143 } else if (!hasBar && m_hBar) { | |
| 144 m_view->removeChild(m_hBar.get()); | |
| 145 m_hBar = 0; | |
| 146 } | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 void ScrollView::ScrollViewPrivate::setHasVerticalScrollbar(bool hasBar) | |
| 151 { | |
| 152 if (Scrollbar::hasPlatformScrollbars()) { | |
| 153 if (hasBar && !m_vBar) { | |
| 154 m_vBar = PlatformScrollbar::create(this, VerticalScrollbar, RegularS
crollbar); | |
| 155 m_view->addChild(m_vBar.get()); | |
| 156 } else if (!hasBar && m_vBar) { | |
| 157 m_view->removeChild(m_vBar.get()); | |
| 158 m_vBar = 0; | |
| 159 } | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 void ScrollView::ScrollViewPrivate::valueChanged(Scrollbar* bar) | |
| 164 { | |
| 165 // Figure out if we really moved. | |
| 166 IntSize newOffset = m_scrollOffset; | |
| 167 if (bar) { | |
| 168 if (bar == m_hBar) | |
| 169 newOffset.setWidth(bar->value()); | |
| 170 else if (bar == m_vBar) | |
| 171 newOffset.setHeight(bar->value()); | |
| 172 } | |
| 173 IntSize scrollDelta = newOffset - m_scrollOffset; | |
| 174 if (scrollDelta == IntSize()) | |
| 175 return; | |
| 176 m_scrollOffset = newOffset; | |
| 177 | |
| 178 if (m_scrollbarsSuppressed) | |
| 179 return; | |
| 180 | |
| 181 scrollBackingStore(scrollDelta); | |
| 182 | |
| 183 if (Frame* frame = static_cast<FrameView*>(m_view)->frame()) { | |
| 184 frame->sendScrollEvent(); | |
| 185 | |
| 186 // Inform the delegate that the scroll position has changed. | |
| 187 WidgetClientWin* client = | |
| 188 static_cast<WidgetClientWin*>(m_view->client()); | |
| 189 if (client) | |
| 190 client->onScrollPositionChanged(m_view); | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 void ScrollView::ScrollViewPrivate::scrollBackingStore(const IntSize& scrollDelt
a) | |
| 195 { | |
| 196 // Since scrolling is double buffered, we will be blitting the scroll view's
intersection | |
| 197 // with the clip rect every time to keep it smooth. | |
| 198 | |
| 199 IntRect clipRect = m_view->windowClipRect(); | |
| 200 IntRect scrollViewRect = m_view->convertToContainingWindow(IntRect(0, 0, m_v
iew->visibleWidth(), m_view->visibleHeight())); | |
| 201 | |
| 202 // Negative when our frame is smaller than the min scrollbar width. | |
| 203 if (scrollViewRect.width() < 0) | |
| 204 scrollViewRect.setWidth(0); | |
| 205 if (scrollViewRect.height() < 0) | |
| 206 scrollViewRect.setHeight(0); | |
| 207 | |
| 208 if (!m_hasStaticBackground) { // The main frame can just blit the WebView wi
ndow | |
| 209 // FIXME: Find a way to blit subframes without blitting overlapping cont
ent | |
| 210 m_view->scrollBackingStore(-scrollDelta.width(), -scrollDelta.height(),
scrollViewRect, clipRect); | |
| 211 } else { | |
| 212 IntRect updateRect = clipRect; | |
| 213 updateRect.intersect(scrollViewRect); | |
| 214 | |
| 215 // We need to go ahead and repaint the entire backing store. Do it now
before moving the | |
| 216 // plugins. | |
| 217 m_view->addToDirtyRegion(updateRect); | |
| 218 m_view->updateBackingStore(); | |
| 219 } | |
| 220 | |
| 221 // This call will move child HWNDs (plugins) and invalidate them as well. | |
| 222 m_view->geometryChanged(); | |
| 223 } | |
| 224 | |
| 225 void ScrollView::ScrollViewPrivate::setAllowsScrolling(bool flag) | |
| 226 { | |
| 227 if (flag && m_vScrollbarMode == ScrollbarAlwaysOff) | |
| 228 m_vScrollbarMode = ScrollbarAuto; | |
| 229 else if (!flag) | |
| 230 m_vScrollbarMode = ScrollbarAlwaysOff; | |
| 231 | |
| 232 if (flag && m_hScrollbarMode == ScrollbarAlwaysOff) | |
| 233 m_hScrollbarMode = ScrollbarAuto; | |
| 234 else if (!flag) | |
| 235 m_hScrollbarMode = ScrollbarAlwaysOff; | |
| 236 | |
| 237 m_view->updateScrollbars(m_scrollOffset); | |
| 238 } | |
| 239 | |
| 240 bool ScrollView::ScrollViewPrivate::allowsScrolling() const | |
| 241 { | |
| 242 // Return YES if either horizontal or vertical scrolling is allowed. | |
| 243 return m_hScrollbarMode != ScrollbarAlwaysOff || m_vScrollbarMode != Scrollb
arAlwaysOff; | |
| 244 } | |
| 245 | |
| 246 IntRect ScrollView::ScrollViewPrivate::windowClipRect() const | |
| 247 { | |
| 248 // FrameView::windowClipRect() will exclude the scrollbars, but here we | |
| 249 // want to include them, so we are forced to cast to FrameView in order to | |
| 250 // call the non-virtual version of windowClipRect :-( | |
| 251 // | |
| 252 // The non-frame case exists to support FramelessScrollView. | |
| 253 | |
| 254 const FrameView* frameView = static_cast<const FrameView*>(m_view); | |
| 255 if (frameView->frame()) | |
| 256 return frameView->windowClipRect(false); | |
| 257 | |
| 258 return m_view->windowClipRect(); | |
| 259 } | |
| 260 | |
| 261 bool ScrollView::ScrollViewPrivate::isActive() const | |
| 262 { | |
| 263 Page* page = static_cast<const FrameView*>(m_view)->frame()->page(); | |
| 264 return page && page->focusController()->isActive(); | |
| 265 } | |
| 266 | |
| 267 const Vector<RefPtr<Range> >* ScrollView::ScrollViewPrivate::getTickmarks() cons
t | |
| 268 { | |
| 269 FrameView* view = static_cast<FrameView*>(m_view); | |
| 270 ASSERT(view); | |
| 271 Frame* frame = view->frame(); | |
| 272 | |
| 273 if (!frame) | |
| 274 return NULL; // NOTE: Frame can be null for dropdown boxes. | |
| 275 | |
| 276 WidgetClientWin* c = static_cast<WidgetClientWin*>(m_view->client()); | |
| 277 ASSERT(c); | |
| 278 return c->getTickmarks(view->frame()); | |
| 279 } | |
| 280 | |
| 281 size_t ScrollView::ScrollViewPrivate::getActiveTickmarkIndex() const | |
| 282 { | |
| 283 FrameView* view = static_cast<FrameView*>(m_view); | |
| 284 ASSERT(view); | |
| 285 Frame* frame = view->frame(); | |
| 286 | |
| 287 // NOTE: Frame can be null for dropdown boxes. | |
| 288 if (!frame) | |
| 289 return WidgetClientWin::kNoTickmark; | |
| 290 | |
| 291 WidgetClientWin* c = static_cast<WidgetClientWin*>(m_view->client()); | |
| 292 ASSERT(c); | |
| 293 return c->getActiveTickmarkIndex(view->frame()); | |
| 294 } | |
| 295 | |
| 296 const SkBitmap* ScrollView::ScrollViewPrivate::GetPreloadedBitmapFromRenderer( | |
| 297 int resource_id) const | |
| 298 { | |
| 299 WidgetClientWin* c = static_cast<WidgetClientWin*>(m_view->client()); | |
| 300 if (!c) | |
| 301 return NULL; | |
| 302 | |
| 303 return c->getPreloadedResourceBitmap(resource_id); | |
| 304 } | |
| 305 | |
| 306 void ScrollView::ScrollViewPrivate::highlightMatches( | |
| 307 GraphicsContext* context) const | |
| 308 { | |
| 309 if (context->paintingDisabled()) | |
| 310 return; | |
| 311 | |
| 312 const Vector<RefPtr<Range> >* tickmarks = getTickmarks(); | |
| 313 if (!tickmarks || tickmarks->isEmpty()) | |
| 314 return; | |
| 315 | |
| 316 context->save(); | |
| 317 context->translate(m_view->x(), m_view->y()); | |
| 318 | |
| 319 // NOTE: We tolerate the platformContext() call here because the scrollbars | |
| 320 // will not be serialized, i.e. composition is done in the renderer and | |
| 321 // never in the browser. | |
| 322 // Prepare for drawing the arrows along the scroll bar. | |
| 323 gfx::PlatformCanvas* canvas = context->platformContext()->canvas(); | |
| 324 | |
| 325 int horz_start = 0; | |
| 326 int horz_end = m_view->width(); | |
| 327 int vert_start = 0; | |
| 328 int vert_end = m_view->height(); | |
| 329 | |
| 330 if (m_vBar) { | |
| 331 // Account for the amount of scrolling on the vertical scroll bar. | |
| 332 vert_start += m_scrollOffset.height(); | |
| 333 vert_end += m_scrollOffset.height(); | |
| 334 // Don't draw atop the vertical scrollbar. | |
| 335 horz_end -= PlatformScrollbar::verticalScrollbarWidth() + 1; | |
| 336 } | |
| 337 | |
| 338 if (m_hBar) { | |
| 339 // Account for the amount of scrolling on the horizontal scroll bar. | |
| 340 horz_start += m_scrollOffset.width(); | |
| 341 horz_end += m_scrollOffset.width(); | |
| 342 // Don't draw atop the horizontal scrollbar. | |
| 343 vert_end -= PlatformScrollbar::horizontalScrollbarHeight() + 1; | |
| 344 } | |
| 345 | |
| 346 IntRect view_rect(IntPoint(), m_view->size()); | |
| 347 HDC hdc = context->getWindowsContext(view_rect); | |
| 348 | |
| 349 // We create a memory DC, copy the bits we want to highlight to the DC and | |
| 350 // then MERGE_COPY pieces of it back with a yellow brush selected (which | |
| 351 // gives them yellow highlighting). | |
| 352 HDC mem_dc = CreateCompatibleDC(hdc); | |
| 353 HBITMAP mem_bmp = CreateCompatibleBitmap(hdc, m_view->width(), | |
| 354 m_view->height()); | |
| 355 HGDIOBJ old_bmp = SelectObject(mem_dc, mem_bmp); | |
| 356 | |
| 357 // Now create a brush for hit highlighting. This is needed for the MERGECOPY | |
| 358 // to paint a yellow highlight onto the matches found. For more details, see | |
| 359 // the documentation for BitBlt. | |
| 360 static const COLORREF kFillColor = RGB(255, 250, 150); // Light yellow. | |
| 361 HGDIOBJ inactive_brush = CreateSolidBrush(kFillColor); | |
| 362 static const COLORREF kFillColorActive = RGB(255, 150, 50); // Orange. | |
| 363 HGDIOBJ active_brush = CreateSolidBrush(kFillColorActive); | |
| 364 HGDIOBJ old_brush = SelectObject(hdc, inactive_brush); | |
| 365 | |
| 366 // Keep a copy of what's on screen, so we can MERGECOPY it back later for | |
| 367 // the purpose of highlighting the text. | |
| 368 BitBlt(mem_dc, 0, 0, m_view->width(), m_view->height(), | |
| 369 hdc, 0, 0, SRCCOPY); | |
| 370 | |
| 371 const size_t active_tickmark = getActiveTickmarkIndex(); | |
| 372 for (Vector<RefPtr<Range> >::const_iterator i = tickmarks->begin(); | |
| 373 i != tickmarks->end(); ++i) { | |
| 374 const RefPtr<Range> range = (*i); | |
| 375 const IntRect& bounds = range->boundingBox(); | |
| 376 // To highlight the word, we check if the rectangle boundary is within | |
| 377 // the bounds vertically as well as horizontally. | |
| 378 if (bounds.bottomRight().y() > vert_start && | |
| 379 bounds.topLeft().y() < vert_end && | |
| 380 bounds.bottomRight().x() > horz_start && | |
| 381 bounds.topLeft().x() < horz_end && | |
| 382 WebFrameImpl::RangeShouldBeHighlighted(range.get())) { | |
| 383 // We highlight the active tick-mark with a green color instead | |
| 384 // of the normal yellow color. | |
| 385 SelectObject(hdc, ((i - tickmarks->begin()) == active_tickmark) ? | |
| 386 active_brush : inactive_brush); | |
| 387 highlightRange(hdc, mem_dc, range); | |
| 388 } | |
| 389 } | |
| 390 | |
| 391 SelectObject(mem_dc, old_brush); | |
| 392 DeleteObject(active_brush); | |
| 393 DeleteObject(inactive_brush); | |
| 394 | |
| 395 SelectObject(mem_dc, old_bmp); | |
| 396 DeleteObject(mem_bmp); | |
| 397 | |
| 398 DeleteDC(mem_dc); | |
| 399 | |
| 400 context->releaseWindowsContext(hdc, view_rect); | |
| 401 context->restore(); | |
| 402 } | |
| 403 | |
| 404 // TODO(ojan): http://b/1143983 make this work for inline elements as they can | |
| 405 // wrap (use highlightRange instead?) | |
| 406 void ScrollView::ScrollViewPrivate::highlightInspectedNode( | |
| 407 GraphicsContext* context, Frame* frame) const | |
| 408 { | |
| 409 WebViewImpl* c = static_cast<WebViewImpl*>(m_view->client()); | |
| 410 const WebCore::Node* inspected_node = c->getInspectedNode(frame); | |
| 411 | |
| 412 if (!inspected_node) | |
| 413 return; | |
| 414 | |
| 415 SkPaint paint; | |
| 416 paint.setARGB(122, 255, 225, 0); // Yellow | |
| 417 | |
| 418 // TODO(ojan): http://b/1143991 Once we sync a Skia version that supports | |
| 419 // it, use SkPorterDuff::kScreenMode and remove the transparency. | |
| 420 // Then port highlightMatches/highlightRanges to use this as well. | |
| 421 // Although, perhaps the web inspector really should be using | |
| 422 // an alpha overlay? It's less pretty, but more clear what node | |
| 423 // is being overlayed. In this case, the TODO is to make | |
| 424 // highlightMatches/Ranges use Skia and to leave this as is. | |
| 425 // | |
| 426 // paint.setPorterDuffXfermode(SkPorterDuff::kScreenMode); | |
| 427 | |
| 428 // TODO(ojan): http://b/1143975 Draw the padding/border/margin boxes in | |
| 429 // different colors. | |
| 430 context->platformContext()->paintSkPaint(inspected_node->getRect(), paint); | |
| 431 } | |
| 432 | |
| 433 void ScrollView::ScrollViewPrivate::highlightRange(HDC hdc, HDC mem_dc, | |
| 434 RefPtr<Range> range) const { | |
| 435 // We need to figure out whether the match that we want to | |
| 436 // highlight is on a single line or on multiple lines. | |
| 437 IntRect start = VisiblePosition(range->startPosition()).caretRect(); | |
| 438 IntRect end = VisiblePosition(range->endPosition()).caretRect(); | |
| 439 IntRect bounds = range->boundingBox(); | |
| 440 | |
| 441 // Multi-line bounds have different y pos for start and end. | |
| 442 if (start.y() == end.y()) { | |
| 443 int x = bounds.topLeft().x() - m_scrollOffset.width(); | |
| 444 int y = bounds.topLeft().y() - m_scrollOffset.height(); | |
| 445 int w = bounds.bottomRight().x() - bounds.topLeft().x() + 1; | |
| 446 int h = bounds.bottomRight().y() - bounds.topLeft().y() + 1; | |
| 447 | |
| 448 // MERGECOPY the relevant bits back, creating a highlight. | |
| 449 BitBlt(hdc, x, y, w, h, mem_dc, x, y, MERGECOPY); | |
| 450 } else { | |
| 451 // Multi line bounds, for example, when we need to highlight | |
| 452 // all the numbers (and only the numbers) in this block of | |
| 453 // text: | |
| 454 // | |
| 455 // xxxxxxxxxxxxxxxx11111111 | |
| 456 // 222222222222222222222222 | |
| 457 // 222222222222222222222222 | |
| 458 // 333333333333333xxxxxxxxx | |
| 459 // | |
| 460 // In this case, the bounding box will contain all the text, | |
| 461 // (including the exes (x)). We highlight in three steps. | |
| 462 // First we highlight the segment containing the ones (1) | |
| 463 // above. Then the whole middle section is highlighted, or the | |
| 464 // twos (2), and finally the remaining segment consisting of | |
| 465 // the threes (3) is highlighted. | |
| 466 | |
| 467 const int row_height = start.height(); | |
| 468 int x = 0, y = 0, w = 0, h = 0; | |
| 469 | |
| 470 // The start and end caret can be outside the bounding box, for leading | |
| 471 // and trailing whitespace and we should not highlight those. | |
| 472 if (start.intersects(bounds)) { | |
| 473 // Highlight the first segment. | |
| 474 x = start.x() - m_scrollOffset.width(); | |
| 475 y = start.y() - m_scrollOffset.height(); | |
| 476 w = bounds.topRight().x() - start.x() + 1; | |
| 477 h = row_height; | |
| 478 | |
| 479 BitBlt(hdc, x, y, w, h, mem_dc, x, y, MERGECOPY); | |
| 480 } | |
| 481 | |
| 482 // Figure out how large the middle section is. | |
| 483 int rows_between = (end.y() - start.y()) / row_height - 1; | |
| 484 | |
| 485 if (rows_between > 0) { | |
| 486 // Highlight the middle segment. | |
| 487 x = bounds.x() - m_scrollOffset.width(); | |
| 488 y = bounds.y() - m_scrollOffset.height() + row_height; | |
| 489 w = bounds.width(); | |
| 490 h = rows_between * row_height; | |
| 491 | |
| 492 BitBlt(hdc, x, y, w, h, mem_dc, x, y, MERGECOPY); | |
| 493 } | |
| 494 | |
| 495 // The end caret might not intersect the bounding box, for example | |
| 496 // when highlighting the last letter of a line that wraps. In that | |
| 497 // case the end caret is set to the beginning of the next line, and | |
| 498 // since it doesn't intersect with the bounding box we don't need to | |
| 499 // highlight. | |
| 500 if (end.intersects(bounds)) { | |
| 501 // Highlight the remaining segment. | |
| 502 x = bounds.bottomLeft().x() - m_scrollOffset.width(); | |
| 503 y = bounds.bottomLeft().y() - m_scrollOffset.height() - | |
| 504 row_height + 1; | |
| 505 w = end.x() - bounds.bottomLeft().x(); | |
| 506 h = row_height; | |
| 507 | |
| 508 BitBlt(hdc, x, y, w, h, mem_dc, x, y, MERGECOPY); | |
| 509 } | |
| 510 } | |
| 511 } | |
| 512 | |
| 513 ScrollView::ScrollView() | |
| 514 { | |
| 515 m_data = new ScrollViewPrivate(this); | |
| 516 } | |
| 517 | |
| 518 ScrollView::~ScrollView() | |
| 519 { | |
| 520 delete m_data; | |
| 521 } | |
| 522 | |
| 523 void ScrollView::updateContents(const IntRect& rect, bool now) | |
| 524 { | |
| 525 if (rect.isEmpty()) | |
| 526 return; | |
| 527 | |
| 528 IntRect containingWindowRect = contentsToWindow(rect); | |
| 529 | |
| 530 if (containingWindowRect.x() < 0) | |
| 531 containingWindowRect.setX(0); | |
| 532 if (containingWindowRect.y() < 0) | |
| 533 containingWindowRect.setY(0); | |
| 534 | |
| 535 updateWindowRect(containingWindowRect, now); | |
| 536 } | |
| 537 | |
| 538 void ScrollView::updateWindowRect(const IntRect& rect, bool now) | |
| 539 { | |
| 540 // TODO(dglazkov): make sure this is actually the right way to do this | |
| 541 | |
| 542 // Cache the dirty spot. | |
| 543 addToDirtyRegion(rect); | |
| 544 | |
| 545 // since painting always happens asynchronously, we don't have a way to | |
| 546 // honor the "now" parameter. it is unclear if it matters. | |
| 547 if (now) { | |
| 548 // TODO(iyengar): Should we force a layout to occur here? | |
| 549 geometryChanged(); | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 void ScrollView::update() | |
| 554 { | |
| 555 // TODO(iyengar): Should we force a layout to occur here? | |
| 556 geometryChanged(); | |
| 557 } | |
| 558 | |
| 559 int ScrollView::visibleWidth() const | |
| 560 { | |
| 561 return width() - (m_data->m_vBar ? m_data->m_vBar->width() : 0); | |
| 562 } | |
| 563 | |
| 564 int ScrollView::visibleHeight() const | |
| 565 { | |
| 566 return height() - (m_data->m_hBar ? m_data->m_hBar->height() : 0); | |
| 567 } | |
| 568 | |
| 569 FloatRect ScrollView::visibleContentRect() const | |
| 570 { | |
| 571 return FloatRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); | |
| 572 } | |
| 573 | |
| 574 FloatRect ScrollView::visibleContentRectConsideringExternalScrollers() const | |
| 575 { | |
| 576 // external scrollers not supported for now | |
| 577 return visibleContentRect(); | |
| 578 } | |
| 579 | |
| 580 void ScrollView::setContentsPos(int newX, int newY) | |
| 581 { | |
| 582 int dx = newX - contentsX(); | |
| 583 int dy = newY - contentsY(); | |
| 584 scrollBy(dx, dy); | |
| 585 } | |
| 586 | |
| 587 void ScrollView::resizeContents(int w, int h) | |
| 588 { | |
| 589 IntSize newContentsSize(w, h); | |
| 590 if (m_data->m_contentsSize != newContentsSize) { | |
| 591 m_data->m_contentsSize = newContentsSize; | |
| 592 updateScrollbars(m_data->m_scrollOffset); | |
| 593 } | |
| 594 } | |
| 595 | |
| 596 void ScrollView::setFrameGeometry(const IntRect& newGeometry) | |
| 597 { | |
| 598 IntRect normalizedNewGeometry = newGeometry; | |
| 599 | |
| 600 // Webkit sometimes attempts to set negative sizes due to | |
| 601 // sloppy calculations of width with margins and such. | |
| 602 // (RenderPart:updateWidgetPosition is one example.) | |
| 603 // Safeguard against this and prevent negative heights/widths. | |
| 604 if (normalizedNewGeometry.width() < 0) | |
| 605 normalizedNewGeometry.setWidth(0); | |
| 606 if (normalizedNewGeometry.height() < 0) | |
| 607 normalizedNewGeometry.setHeight(0); | |
| 608 | |
| 609 IntRect oldGeometry = frameGeometry(); | |
| 610 Widget::setFrameGeometry(normalizedNewGeometry); | |
| 611 | |
| 612 if (normalizedNewGeometry == oldGeometry) | |
| 613 return; | |
| 614 | |
| 615 if (normalizedNewGeometry.width() != oldGeometry.width() || | |
| 616 normalizedNewGeometry.height() != oldGeometry.height()) { | |
| 617 updateScrollbars(m_data->m_scrollOffset); | |
| 618 | |
| 619 // when used to display a popup menu, we do not have a frame | |
| 620 FrameView* frameView = static_cast<FrameView*>(this); | |
| 621 if (frameView->frame()) | |
| 622 frameView->setNeedsLayout(); | |
| 623 } | |
| 624 | |
| 625 geometryChanged(); | |
| 626 } | |
| 627 | |
| 628 int ScrollView::contentsX() const | |
| 629 { | |
| 630 return scrollOffset().width(); | |
| 631 } | |
| 632 | |
| 633 int ScrollView::contentsY() const | |
| 634 { | |
| 635 return scrollOffset().height(); | |
| 636 } | |
| 637 | |
| 638 int ScrollView::contentsWidth() const | |
| 639 { | |
| 640 return m_data->m_contentsSize.width(); | |
| 641 } | |
| 642 | |
| 643 int ScrollView::contentsHeight() const | |
| 644 { | |
| 645 return m_data->m_contentsSize.height(); | |
| 646 } | |
| 647 | |
| 648 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const | |
| 649 { | |
| 650 IntPoint viewPoint = convertFromContainingWindow(windowPoint); | |
| 651 return viewPoint + scrollOffset(); | |
| 652 } | |
| 653 | |
| 654 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const | |
| 655 { | |
| 656 IntPoint viewPoint = contentsPoint - scrollOffset(); | |
| 657 return convertToContainingWindow(viewPoint); | |
| 658 } | |
| 659 | |
| 660 IntPoint ScrollView::convertChildToSelf(const Widget* child, const IntPoint& poi
nt) const | |
| 661 { | |
| 662 IntPoint newPoint = point; | |
| 663 if (child != m_data->m_hBar && child != m_data->m_vBar) | |
| 664 newPoint = point - scrollOffset(); | |
| 665 return Widget::convertChildToSelf(child, newPoint); | |
| 666 } | |
| 667 | |
| 668 IntPoint ScrollView::convertSelfToChild(const Widget* child, const IntPoint& poi
nt) const | |
| 669 { | |
| 670 IntPoint newPoint = point; | |
| 671 if (child != m_data->m_hBar && child != m_data->m_vBar) | |
| 672 newPoint = point + scrollOffset(); | |
| 673 return Widget::convertSelfToChild(child, newPoint); | |
| 674 } | |
| 675 | |
| 676 IntSize ScrollView::scrollOffset() const | |
| 677 { | |
| 678 return m_data->m_scrollOffset; | |
| 679 } | |
| 680 | |
| 681 IntSize ScrollView::maximumScroll() const | |
| 682 { | |
| 683 // We should not check whether scrolling is allowed for this view before cal
culating | |
| 684 // the maximumScroll. Please refer to http://b/issue?id=1164704, where in sc
rolling | |
| 685 // would not work on a scrollview created with scrollbars disabled. The curr
ent | |
| 686 // behavior mirrors Safari's webkit implementation. Firefox also behaves sim
ilarly. | |
| 687 IntSize delta = (m_data->m_contentsSize - IntSize(visibleWidth(), visibleHei
ght())) - scrollOffset(); | |
| 688 delta.clampNegativeToZero(); | |
| 689 return delta; | |
| 690 } | |
| 691 | |
| 692 void ScrollView::scrollBy(int dx, int dy) | |
| 693 { | |
| 694 IntSize scrollOffset = m_data->m_scrollOffset; | |
| 695 IntSize newScrollOffset = scrollOffset + IntSize(dx, dy).shrunkTo(maximumScr
oll()); | |
| 696 newScrollOffset.clampNegativeToZero(); | |
| 697 | |
| 698 if (newScrollOffset == scrollOffset) | |
| 699 return; | |
| 700 | |
| 701 updateScrollbars(newScrollOffset); | |
| 702 } | |
| 703 | |
| 704 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r) | |
| 705 { | |
| 706 IntPoint p(max(0, r.x()), max(0, r.y())); | |
| 707 ScrollView* view = this; | |
| 708 while (view) { | |
| 709 view->setContentsPos(p.x(), p.y()); | |
| 710 p.move(view->x() - view->scrollOffset().width(), view->y() - view->scrol
lOffset().height()); | |
| 711 view = static_cast<ScrollView*>(view->parent()); | |
| 712 } | |
| 713 } | |
| 714 | |
| 715 WebCore::ScrollbarMode ScrollView::hScrollbarMode() const | |
| 716 { | |
| 717 return m_data->m_hScrollbarMode; | |
| 718 } | |
| 719 | |
| 720 WebCore::ScrollbarMode ScrollView::vScrollbarMode() const | |
| 721 { | |
| 722 return m_data->m_vScrollbarMode; | |
| 723 } | |
| 724 | |
| 725 void ScrollView::suppressScrollbars(bool suppressed, bool repaintOnSuppress) | |
| 726 { | |
| 727 m_data->m_scrollbarsSuppressed = suppressed; | |
| 728 if (repaintOnSuppress && !suppressed) { | |
| 729 if (m_data->m_hBar) | |
| 730 m_data->m_hBar->invalidate(); | |
| 731 if (m_data->m_vBar) | |
| 732 m_data->m_vBar->invalidate(); | |
| 733 | |
| 734 // Invalidate the scroll corner too on unsuppress. | |
| 735 IntRect hCorner; | |
| 736 if (m_data->m_hBar && width() - m_data->m_hBar->width() > 0) { | |
| 737 hCorner = IntRect(m_data->m_hBar->width(), | |
| 738 height() - m_data->m_hBar->height(), | |
| 739 width() - m_data->m_hBar->width(), | |
| 740 m_data->m_hBar->height()); | |
| 741 invalidateRect(hCorner); | |
| 742 } | |
| 743 | |
| 744 if (m_data->m_vBar && height() - m_data->m_vBar->height() > 0) { | |
| 745 IntRect vCorner(width() - m_data->m_vBar->width(), | |
| 746 m_data->m_vBar->height(), | |
| 747 m_data->m_vBar->width(), | |
| 748 height() - m_data->m_vBar->height()); | |
| 749 if (vCorner != hCorner) | |
| 750 invalidateRect(vCorner); | |
| 751 } | |
| 752 } | |
| 753 } | |
| 754 | |
| 755 void ScrollView::setHScrollbarMode(ScrollbarMode newMode) | |
| 756 { | |
| 757 if (m_data->m_hScrollbarMode != newMode) { | |
| 758 m_data->m_hScrollbarMode = newMode; | |
| 759 updateScrollbars(m_data->m_scrollOffset); | |
| 760 } | |
| 761 } | |
| 762 | |
| 763 void ScrollView::setVScrollbarMode(ScrollbarMode newMode) | |
| 764 { | |
| 765 if (m_data->m_vScrollbarMode != newMode) { | |
| 766 m_data->m_vScrollbarMode = newMode; | |
| 767 updateScrollbars(m_data->m_scrollOffset); | |
| 768 } | |
| 769 } | |
| 770 | |
| 771 void ScrollView::setScrollbarsMode(ScrollbarMode newMode) | |
| 772 { | |
| 773 if (m_data->m_hScrollbarMode != newMode || | |
| 774 m_data->m_vScrollbarMode != newMode) { | |
| 775 m_data->m_hScrollbarMode = m_data->m_vScrollbarMode = newMode; | |
| 776 updateScrollbars(m_data->m_scrollOffset); | |
| 777 } | |
| 778 } | |
| 779 | |
| 780 void ScrollView::setStaticBackground(bool flag) | |
| 781 { | |
| 782 m_data->m_hasStaticBackground = flag; | |
| 783 } | |
| 784 | |
| 785 void ScrollView::updateScrollbars(const IntSize& desiredOffset) | |
| 786 { | |
| 787 // Don't allow re-entrancy into this function. | |
| 788 if (m_data->m_inUpdateScrollbars) | |
| 789 return; | |
| 790 | |
| 791 m_data->m_inUpdateScrollbars = true; | |
| 792 | |
| 793 bool hasVerticalScrollbar = m_data->m_vBar; | |
| 794 bool hasHorizontalScrollbar = m_data->m_hBar; | |
| 795 bool oldHasVertical = hasVerticalScrollbar; | |
| 796 bool oldHasHorizontal = hasHorizontalScrollbar; | |
| 797 ScrollbarMode hScroll = m_data->m_hScrollbarMode; | |
| 798 ScrollbarMode vScroll = m_data->m_vScrollbarMode; | |
| 799 | |
| 800 const int cVerticalWidth = PlatformScrollbar::verticalScrollbarWidth(); | |
| 801 const int cHorizontalHeight = PlatformScrollbar::horizontalScrollbarHeight()
; | |
| 802 | |
| 803 // we may not be able to support scrollbars due to our frame geometry | |
| 804 if (width() < cVerticalWidth) | |
| 805 vScroll = ScrollbarAlwaysOff; | |
| 806 if (height() < cHorizontalHeight) | |
| 807 hScroll = ScrollbarAlwaysOff; | |
| 808 | |
| 809 for (int pass = 0; pass < 2; pass++) { | |
| 810 bool scrollsVertically; | |
| 811 bool scrollsHorizontally; | |
| 812 | |
| 813 if (!m_data->m_scrollbarsSuppressed && (hScroll == ScrollbarAuto || vScr
oll == ScrollbarAuto)) { | |
| 814 // Do a layout if pending before checking if scrollbars are needed. | |
| 815 if (hasVerticalScrollbar != oldHasVertical || hasHorizontalScrollbar
!= oldHasHorizontal) | |
| 816 static_cast<FrameView*>(this)->layout(); | |
| 817 | |
| 818 scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == Sc
rollbarAuto && contentsHeight() > height()); | |
| 819 if (scrollsVertically) | |
| 820 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll
== ScrollbarAuto && contentsWidth() + cVerticalWidth > width()); | |
| 821 else { | |
| 822 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll
== ScrollbarAuto && contentsWidth() > width()); | |
| 823 if (scrollsHorizontally) | |
| 824 scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScro
ll == ScrollbarAuto && contentsHeight() + cHorizontalHeight > height()); | |
| 825 } | |
| 826 } | |
| 827 else { | |
| 828 scrollsHorizontally = (hScroll == ScrollbarAuto) ? hasHorizontalScro
llbar : (hScroll == ScrollbarAlwaysOn); | |
| 829 scrollsVertically = (vScroll == ScrollbarAuto) ? hasVerticalScrollba
r : (vScroll == ScrollbarAlwaysOn); | |
| 830 } | |
| 831 | |
| 832 if (hasVerticalScrollbar != scrollsVertically) { | |
| 833 m_data->setHasVerticalScrollbar(scrollsVertically); | |
| 834 hasVerticalScrollbar = scrollsVertically; | |
| 835 } | |
| 836 | |
| 837 if (hasHorizontalScrollbar != scrollsHorizontally) { | |
| 838 m_data->setHasHorizontalScrollbar(scrollsHorizontally); | |
| 839 hasHorizontalScrollbar = scrollsHorizontally; | |
| 840 } | |
| 841 } | |
| 842 | |
| 843 // Set up the range (and page step/line step). | |
| 844 IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight()
- visibleHeight()); | |
| 845 IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition); | |
| 846 scroll.clampNegativeToZero(); | |
| 847 | |
| 848 if (m_data->m_hBar) { | |
| 849 int clientWidth = visibleWidth(); | |
| 850 m_data->m_hBar->setEnabled(contentsWidth() > clientWidth); | |
| 851 int pageStep = (clientWidth - PAGE_KEEP); | |
| 852 if (pageStep < 0) pageStep = clientWidth; | |
| 853 IntRect oldRect(m_data->m_hBar->frameGeometry()); | |
| 854 IntRect hBarRect = IntRect(0, | |
| 855 height() - m_data->m_hBar->height(), | |
| 856 width() - (m_data->m_vBar ? m_data->m_vBar->w
idth() : 0), | |
| 857 m_data->m_hBar->height()); | |
| 858 m_data->m_hBar->setRect(hBarRect); | |
| 859 if (!m_data->m_scrollbarsSuppressed && oldRect != m_data->m_hBar->frameG
eometry()) | |
| 860 m_data->m_hBar->invalidate(); | |
| 861 | |
| 862 if (m_data->m_scrollbarsSuppressed) | |
| 863 m_data->m_hBar->setSuppressInvalidation(true); | |
| 864 m_data->m_hBar->setSteps(LINE_STEP, pageStep); | |
| 865 m_data->m_hBar->setProportion(clientWidth, contentsWidth()); | |
| 866 m_data->m_hBar->setValue(scroll.width()); | |
| 867 if (m_data->m_scrollbarsSuppressed) | |
| 868 m_data->m_hBar->setSuppressInvalidation(false); | |
| 869 } | |
| 870 | |
| 871 if (m_data->m_vBar) { | |
| 872 int clientHeight = visibleHeight(); | |
| 873 m_data->m_vBar->setEnabled(contentsHeight() > clientHeight); | |
| 874 int pageStep = (clientHeight - PAGE_KEEP); | |
| 875 if (pageStep < 0) pageStep = clientHeight; | |
| 876 IntRect oldRect(m_data->m_vBar->frameGeometry()); | |
| 877 IntRect vBarRect = IntRect(width() - m_data->m_vBar->width(), | |
| 878 0, | |
| 879 m_data->m_vBar->width(), | |
| 880 height() - (m_data->m_hBar ? m_data->m_hBar->
height() : 0)); | |
| 881 m_data->m_vBar->setRect(vBarRect); | |
| 882 if (!m_data->m_scrollbarsSuppressed && oldRect != m_data->m_vBar->frameG
eometry()) | |
| 883 m_data->m_vBar->invalidate(); | |
| 884 | |
| 885 if (m_data->m_scrollbarsSuppressed) | |
| 886 m_data->m_vBar->setSuppressInvalidation(true); | |
| 887 m_data->m_vBar->setSteps(LINE_STEP, pageStep); | |
| 888 m_data->m_vBar->setProportion(clientHeight, contentsHeight()); | |
| 889 m_data->m_vBar->setValue(scroll.height()); | |
| 890 if (m_data->m_scrollbarsSuppressed) | |
| 891 m_data->m_vBar->setSuppressInvalidation(false); | |
| 892 } | |
| 893 | |
| 894 if (oldHasVertical != (m_data->m_vBar != 0) || oldHasHorizontal != (m_data->
m_hBar != 0)) | |
| 895 geometryChanged(); | |
| 896 | |
| 897 // See if our offset has changed in a situation where we might not have scro
llbars. | |
| 898 // This can happen when editing a body with overflow:hidden and scrolling to
reveal selection. | |
| 899 // It can also happen when maximizing a window that has scrollbars (but the
new maximized result | |
| 900 // does not). | |
| 901 IntSize scrollDelta = scroll - m_data->m_scrollOffset; | |
| 902 if (scrollDelta != IntSize()) { | |
| 903 m_data->m_scrollOffset = scroll; | |
| 904 m_data->scrollBackingStore(scrollDelta); | |
| 905 | |
| 906 // Inform the delegate that the scroll position has changed. | |
| 907 WidgetClientWin* c = static_cast<WidgetClientWin*>(client()); | |
| 908 if (c) | |
| 909 c->onScrollPositionChanged(this); | |
| 910 } | |
| 911 | |
| 912 m_data->m_inUpdateScrollbars = false; | |
| 913 | |
| 914 ASSERT(visibleWidth() >= 0); | |
| 915 ASSERT(visibleHeight() >= 0); | |
| 916 } | |
| 917 | |
| 918 PlatformScrollbar* ScrollView::scrollbarUnderMouse(const PlatformMouseEvent& mou
seEvent) | |
| 919 { | |
| 920 IntPoint viewPoint = convertFromContainingWindow(mouseEvent.pos()); | |
| 921 if (m_data->m_hBar && m_data->m_hBar->frameGeometry().contains(viewPoint)) | |
| 922 return m_data->m_hBar.get(); | |
| 923 if (m_data->m_vBar && m_data->m_vBar->frameGeometry().contains(viewPoint)) | |
| 924 return m_data->m_vBar.get(); | |
| 925 return 0; | |
| 926 } | |
| 927 | |
| 928 void ScrollView::addChild(Widget* child) | |
| 929 { | |
| 930 child->setParent(this); | |
| 931 | |
| 932 // There is only one global widget client (which should be the WebViewImpl). | |
| 933 // It is responsible for things like capturing the mouse. | |
| 934 child->setClient(client()); | |
| 935 | |
| 936 m_data->m_children.add(child); | |
| 937 } | |
| 938 | |
| 939 void ScrollView::removeChild(Widget* child) | |
| 940 { | |
| 941 child->setParent(0); | |
| 942 m_data->m_children.remove(child); | |
| 943 } | |
| 944 | |
| 945 void ScrollView::paint(GraphicsContext* context, const IntRect& rect) | |
| 946 { | |
| 947 // FIXME: This code is here so we don't have to fork FrameView.h/.cpp. | |
| 948 // In the end, FrameView should just merge with ScrollView. | |
| 949 ASSERT(isFrameView()); | |
| 950 | |
| 951 if (context->paintingDisabled()) | |
| 952 return; | |
| 953 | |
| 954 if (Frame* frame = static_cast<FrameView*>(this)->frame()) { | |
| 955 IntRect documentDirtyRect = rect; | |
| 956 documentDirtyRect.intersect(frameGeometry()); | |
| 957 | |
| 958 context->save(); | |
| 959 | |
| 960 context->translate(x(), y()); | |
| 961 documentDirtyRect.move(-x(), -y()); | |
| 962 | |
| 963 context->translate(-contentsX(), -contentsY()); | |
| 964 documentDirtyRect.move(contentsX(), contentsY()); | |
| 965 | |
| 966 // do not allow painting outside of the dirty rect | |
| 967 context->clip(documentDirtyRect); | |
| 968 | |
| 969 frame->paint(context, documentDirtyRect); | |
| 970 | |
| 971 // Highlights the node selected in the DOM inspector. | |
| 972 m_data->highlightInspectedNode(context, frame); | |
| 973 | |
| 974 context->restore(); | |
| 975 } | |
| 976 | |
| 977 // Highlight the matches found on the page, during a FindInPage operation. | |
| 978 m_data->highlightMatches(context); | |
| 979 | |
| 980 // Now paint the scrollbars. | |
| 981 if (!m_data->m_scrollbarsSuppressed && (m_data->m_hBar || m_data->m_vBar)) { | |
| 982 context->save(); | |
| 983 IntRect scrollViewDirtyRect = rect; | |
| 984 scrollViewDirtyRect.intersect(frameGeometry()); | |
| 985 context->translate(x(), y()); | |
| 986 scrollViewDirtyRect.move(-x(), -y()); | |
| 987 if (m_data->m_hBar) | |
| 988 m_data->m_hBar->paint(context, scrollViewDirtyRect); | |
| 989 if (m_data->m_vBar) | |
| 990 m_data->m_vBar->paint(context, scrollViewDirtyRect); | |
| 991 | |
| 992 // Fill the scroll corner with white. | |
| 993 IntRect hCorner; | |
| 994 if (m_data->m_hBar && width() - m_data->m_hBar->width() > 0) { | |
| 995 hCorner = IntRect(m_data->m_hBar->width(), | |
| 996 height() - m_data->m_hBar->height(), | |
| 997 width() - m_data->m_hBar->width(), | |
| 998 m_data->m_hBar->height()); | |
| 999 if (hCorner.intersects(scrollViewDirtyRect)) | |
| 1000 context->fillRect(hCorner, Color::white); | |
| 1001 } | |
| 1002 | |
| 1003 if (m_data->m_vBar && height() - m_data->m_vBar->height() > 0) { | |
| 1004 IntRect vCorner(width() - m_data->m_vBar->width(), | |
| 1005 m_data->m_vBar->height(), | |
| 1006 m_data->m_vBar->width(), | |
| 1007 height() - m_data->m_vBar->height()); | |
| 1008 if (vCorner != hCorner && vCorner.intersects(scrollViewDirtyRect)) | |
| 1009 context->fillRect(vCorner, Color::white); | |
| 1010 } | |
| 1011 | |
| 1012 context->restore(); | |
| 1013 } | |
| 1014 } | |
| 1015 | |
| 1016 void ScrollView::themeChanged() | |
| 1017 { | |
| 1018 PlatformScrollbar::themeChanged(); | |
| 1019 theme()->themeChanged(); | |
| 1020 invalidate(); | |
| 1021 } | |
| 1022 | |
| 1023 void ScrollView::wheelEvent(PlatformWheelEvent& e) | |
| 1024 { | |
| 1025 if (!m_data->allowsScrolling()) | |
| 1026 return; | |
| 1027 | |
| 1028 // Determine how much we want to scroll. If we can move at all, we will acc
ept the event. | |
| 1029 IntSize maxScrollDelta = maximumScroll(); | |
| 1030 if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) || | |
| 1031 (e.deltaX() > 0 && scrollOffset().width() > 0) || | |
| 1032 (e.deltaY() < 0 && maxScrollDelta.height() > 0) || | |
| 1033 (e.deltaY() > 0 && scrollOffset().height() > 0)) { | |
| 1034 e.accept(); | |
| 1035 scrollBy(-e.deltaX() * LINE_STEP, -e.deltaY() * LINE_STEP); | |
| 1036 } | |
| 1037 } | |
| 1038 | |
| 1039 HashSet<Widget*>* ScrollView::children() | |
| 1040 { | |
| 1041 return &(m_data->m_children); | |
| 1042 } | |
| 1043 | |
| 1044 void ScrollView::geometryChanged() const | |
| 1045 { | |
| 1046 HashSet<Widget*>::const_iterator end = m_data->m_children.end(); | |
| 1047 for (HashSet<Widget*>::const_iterator current = m_data->m_children.begin();
current != end; ++current) | |
| 1048 (*current)->geometryChanged(); | |
| 1049 } | |
| 1050 | |
| 1051 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity
) | |
| 1052 { | |
| 1053 if (direction == ScrollUp || direction == ScrollDown) { | |
| 1054 if (m_data->m_vBar) | |
| 1055 return m_data->m_vBar->scroll(direction, granularity); | |
| 1056 } else { | |
| 1057 if (m_data->m_hBar) | |
| 1058 return m_data->m_hBar->scroll(direction, granularity); | |
| 1059 } | |
| 1060 return false; | |
| 1061 } | |
| 1062 | |
| 1063 IntRect ScrollView::windowResizerRect() | |
| 1064 { | |
| 1065 return IntRect(); | |
| 1066 } | |
| 1067 | |
| 1068 bool ScrollView::resizerOverlapsContent() const | |
| 1069 { | |
| 1070 return !m_data->m_scrollbarsAvoidingResizer; | |
| 1071 } | |
| 1072 | |
| 1073 void ScrollView::adjustOverlappingScrollbarCount(int overlapDelta) | |
| 1074 { | |
| 1075 int oldCount = m_data->m_scrollbarsAvoidingResizer; | |
| 1076 m_data->m_scrollbarsAvoidingResizer += overlapDelta; | |
| 1077 if (parent() && parent()->isFrameView()) | |
| 1078 static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(overl
apDelta); | |
| 1079 else if (!m_data->m_scrollbarsSuppressed) { | |
| 1080 // If we went from n to 0 or from 0 to n and we're the outermost view, | |
| 1081 // we need to invalidate the windowResizerRect(), since it will now need
to paint | |
| 1082 // differently. | |
| 1083 if (oldCount > 0 && m_data->m_scrollbarsAvoidingResizer == 0 || | |
| 1084 oldCount == 0 && m_data->m_scrollbarsAvoidingResizer > 0) | |
| 1085 invalidateRect(windowResizerRect()); | |
| 1086 } | |
| 1087 } | |
| 1088 | |
| 1089 void ScrollView::setParent(ScrollView* parentView) | |
| 1090 { | |
| 1091 if (!parentView && m_data->m_scrollbarsAvoidingResizer && parent() && parent
()->isFrameView()) | |
| 1092 static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(false
); | |
| 1093 Widget::setParent(parentView); | |
| 1094 } | |
| 1095 | |
| 1096 void ScrollView::addToDirtyRegion(const IntRect& containingWindowRect) | |
| 1097 { | |
| 1098 WidgetClientWin* c = static_cast<WidgetClientWin*>(client()); | |
| 1099 if (c) | |
| 1100 c->invalidateRect(containingWindowRect); | |
| 1101 } | |
| 1102 | |
| 1103 void ScrollView::scrollBackingStore(int dx, int dy, const IntRect& scrollViewRec
t, const IntRect& clipRect) | |
| 1104 { | |
| 1105 // We don't know how to scroll in two directions at once. | |
| 1106 if (dx && dy) { | |
| 1107 IntRect updateRect = clipRect; | |
| 1108 updateRect.intersect(scrollViewRect); | |
| 1109 addToDirtyRegion(updateRect); | |
| 1110 return; | |
| 1111 } | |
| 1112 | |
| 1113 WidgetClientWin* c = static_cast<WidgetClientWin*>(client()); | |
| 1114 if (c) { | |
| 1115 // TODO(ericroman): would be better to pass both the scroll rect | |
| 1116 // and clip rect up to the client and let them decide how best to | |
| 1117 // scroll the backing store. | |
| 1118 IntRect clippedScrollRect = scrollViewRect; | |
| 1119 clippedScrollRect.intersect(clipRect); | |
| 1120 c->scrollRect(dx, dy, clippedScrollRect); | |
| 1121 } | |
| 1122 } | |
| 1123 | |
| 1124 void ScrollView::updateBackingStore() | |
| 1125 { | |
| 1126 // nothing to do. painting happens asynchronously. | |
| 1127 } | |
| 1128 | |
| 1129 bool ScrollView::inWindow() const | |
| 1130 { | |
| 1131 WidgetClientWin* c = static_cast<WidgetClientWin*>(client()); | |
| 1132 if (!c) | |
| 1133 return false; | |
| 1134 | |
| 1135 return !c->isHidden(); | |
| 1136 } | |
| 1137 | |
| 1138 void ScrollView::attachToWindow() | |
| 1139 { | |
| 1140 if (m_data->m_attachedToWindow) | |
| 1141 return; | |
| 1142 | |
| 1143 m_data->m_attachedToWindow = true; | |
| 1144 | |
| 1145 if (m_data->m_visible) { | |
| 1146 HashSet<Widget*>::iterator end = m_data->m_children.end(); | |
| 1147 for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it != e
nd; ++it) | |
| 1148 (*it)->attachToWindow(); | |
| 1149 } | |
| 1150 } | |
| 1151 | |
| 1152 void ScrollView::detachFromWindow() | |
| 1153 { | |
| 1154 if (!m_data->m_attachedToWindow) | |
| 1155 return; | |
| 1156 | |
| 1157 if (m_data->m_visible) { | |
| 1158 HashSet<Widget*>::iterator end = m_data->m_children.end(); | |
| 1159 for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it != e
nd; ++it) | |
| 1160 (*it)->detachFromWindow(); | |
| 1161 } | |
| 1162 | |
| 1163 m_data->m_attachedToWindow = false; | |
| 1164 } | |
| 1165 | |
| 1166 void ScrollView::show() | |
| 1167 { | |
| 1168 if (!m_data->m_visible) { | |
| 1169 m_data->m_visible = true; | |
| 1170 if (isAttachedToWindow()) { | |
| 1171 HashSet<Widget*>::iterator end = m_data->m_children.end(); | |
| 1172 for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it
!= end; ++it) | |
| 1173 (*it)->attachToWindow(); | |
| 1174 } | |
| 1175 } | |
| 1176 | |
| 1177 Widget::show(); | |
| 1178 } | |
| 1179 | |
| 1180 void ScrollView::hide() | |
| 1181 { | |
| 1182 if (m_data->m_visible) { | |
| 1183 if (isAttachedToWindow()) { | |
| 1184 HashSet<Widget*>::iterator end = m_data->m_children.end(); | |
| 1185 for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it
!= end; ++it) | |
| 1186 (*it)->detachFromWindow(); | |
| 1187 } | |
| 1188 m_data->m_visible = false; | |
| 1189 } | |
| 1190 | |
| 1191 Widget::hide(); | |
| 1192 } | |
| 1193 | |
| 1194 bool ScrollView::isAttachedToWindow() const | |
| 1195 { | |
| 1196 return m_data->m_attachedToWindow; | |
| 1197 } | |
| 1198 | |
| 1199 void ScrollView::setAllowsScrolling(bool flag) | |
| 1200 { | |
| 1201 m_data->setAllowsScrolling(flag); | |
| 1202 } | |
| 1203 | |
| 1204 bool ScrollView::allowsScrolling() const | |
| 1205 { | |
| 1206 return m_data->allowsScrolling(); | |
| 1207 } | |
| 1208 | |
| 1209 void ScrollView::printPanScrollIcon(const IntPoint& iconPosition) | |
| 1210 { | |
| 1211 m_data->m_drawPanScrollIcon = true; | |
| 1212 m_data->m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength
/ 2 , iconPosition.y() - panIconSizeLength / 2) ; | |
| 1213 | |
| 1214 updateWindowRect(IntRect(m_data->m_panScrollIconPoint, IntSize(panIconSizeLe
ngth,panIconSizeLength)), true); | |
| 1215 } | |
| 1216 | |
| 1217 void ScrollView::removePanScrollIcon() | |
| 1218 { | |
| 1219 m_data->m_drawPanScrollIcon = false; | |
| 1220 | |
| 1221 updateWindowRect(IntRect(m_data->m_panScrollIconPoint, IntSize(panIconSizeLe
ngth, panIconSizeLength)), true); | |
| 1222 } | |
| 1223 | |
| 1224 bool ScrollView::isScrollable() | |
| 1225 { | |
| 1226 return m_data->m_vBar != 0 || m_data->m_hBar != 0; | |
| 1227 } | |
| 1228 | |
| 1229 } // namespace WebCore | |
| OLD | NEW |