michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIScrollBoxObject.h" michael@0: #include "nsBoxObject.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIScrollableFrame.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: class nsScrollBoxObject : public nsIScrollBoxObject, public nsBoxObject michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSISCROLLBOXOBJECT michael@0: michael@0: nsScrollBoxObject(); michael@0: virtual ~nsScrollBoxObject(); michael@0: michael@0: virtual nsIScrollableFrame* GetScrollFrame() { michael@0: return do_QueryFrame(GetFrame(false)); michael@0: } michael@0: michael@0: /* additional members */ michael@0: }; michael@0: michael@0: /* Implementation file */ michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsScrollBoxObject) michael@0: NS_INTERFACE_MAP_ENTRY(nsIScrollBoxObject) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsBoxObject) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsScrollBoxObject, nsBoxObject) michael@0: NS_IMPL_RELEASE_INHERITED(nsScrollBoxObject, nsBoxObject) michael@0: michael@0: nsScrollBoxObject::nsScrollBoxObject() michael@0: { michael@0: /* member initializers and constructor code */ michael@0: } michael@0: michael@0: nsScrollBoxObject::~nsScrollBoxObject() michael@0: { michael@0: /* destructor code */ michael@0: } michael@0: michael@0: /* void scrollTo (in long x, in long y); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::ScrollTo(int32_t x, int32_t y) michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: if (!sf) michael@0: return NS_ERROR_FAILURE; michael@0: sf->ScrollToCSSPixels(CSSIntPoint(x, y)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void scrollBy (in long dx, in long dy); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::ScrollBy(int32_t dx, int32_t dy) michael@0: { michael@0: int32_t x, y; michael@0: nsresult rv = GetPosition(&x, &y); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return ScrollTo(x + dx, y + dy); michael@0: } michael@0: michael@0: /* void scrollByLine (in long dlines); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::ScrollByLine(int32_t dlines) michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: if (!sf) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: sf->ScrollBy(nsIntPoint(0, dlines), nsIScrollableFrame::LINES, michael@0: nsIScrollableFrame::SMOOTH); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // XUL elements have a single box child element. michael@0: // Get a pointer to that box. michael@0: // Note that now that the is just a regular box michael@0: // with 'overflow:hidden', the boxobject's frame is an nsXULScrollFrame, michael@0: // the 's box frame is the scrollframe's "scrolled frame", and michael@0: // the 's child box is a child of that. michael@0: static nsIFrame* GetScrolledBox(nsBoxObject* aScrollBox) { michael@0: nsIFrame* frame = aScrollBox->GetFrame(false); michael@0: if (!frame) michael@0: return nullptr; michael@0: nsIScrollableFrame* scrollFrame = do_QueryFrame(frame); michael@0: if (!scrollFrame) { michael@0: NS_WARNING("nsIScrollBoxObject attached to something that's not a scroll frame!"); michael@0: return nullptr; michael@0: } michael@0: nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame(); michael@0: if (!scrolledFrame) michael@0: return nullptr; michael@0: return scrolledFrame->GetChildBox(); michael@0: } michael@0: michael@0: /* void scrollByIndex (in long dindexes); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::ScrollByIndex(int32_t dindexes) michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: if (!sf) michael@0: return NS_ERROR_FAILURE; michael@0: nsIFrame* scrolledBox = GetScrolledBox(this); michael@0: if (!scrolledBox) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsRect rect; michael@0: michael@0: // now get the scrolled boxes first child. michael@0: nsIFrame* child = scrolledBox->GetChildBox(); michael@0: michael@0: bool horiz = scrolledBox->IsHorizontal(); michael@0: nsPoint cp = sf->GetScrollPosition(); michael@0: nscoord diff = 0; michael@0: int32_t curIndex = 0; michael@0: bool isLTR = scrolledBox->IsNormalDirection(); michael@0: michael@0: int32_t frameWidth = 0; michael@0: if (!isLTR && horiz) { michael@0: GetWidth(&frameWidth); michael@0: nsCOMPtr shell = GetPresShell(false); michael@0: if (!shell) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: frameWidth = nsPresContext::CSSPixelsToAppUnits(frameWidth); michael@0: } michael@0: michael@0: // first find out what index we are currently at michael@0: while(child) { michael@0: rect = child->GetRect(); michael@0: if (horiz) { michael@0: // In the left-to-right case we break from the loop when the center of michael@0: // the current child rect is greater than the scrolled position of michael@0: // the left edge of the scrollbox michael@0: // In the right-to-left case we break when the center of the current michael@0: // child rect is less than the scrolled position of the right edge of michael@0: // the scrollbox. michael@0: diff = rect.x + rect.width/2; // use the center, to avoid rounding errors michael@0: if ((isLTR && diff > cp.x) || michael@0: (!isLTR && diff < cp.x + frameWidth)) { michael@0: break; michael@0: } michael@0: } else { michael@0: diff = rect.y + rect.height/2;// use the center, to avoid rounding errors michael@0: if (diff > cp.y) { michael@0: break; michael@0: } michael@0: } michael@0: child = child->GetNextBox(); michael@0: curIndex++; michael@0: } michael@0: michael@0: int32_t count = 0; michael@0: michael@0: if (dindexes == 0) michael@0: return NS_OK; michael@0: michael@0: if (dindexes > 0) { michael@0: while(child) { michael@0: child = child->GetNextBox(); michael@0: if (child) michael@0: rect = child->GetRect(); michael@0: count++; michael@0: if (count >= dindexes) michael@0: break; michael@0: } michael@0: michael@0: } else if (dindexes < 0) { michael@0: child = scrolledBox->GetChildBox(); michael@0: while(child) { michael@0: rect = child->GetRect(); michael@0: if (count >= curIndex + dindexes) michael@0: break; michael@0: michael@0: count++; michael@0: child = child->GetNextBox(); michael@0: michael@0: } michael@0: } michael@0: michael@0: nscoord csspixel = nsPresContext::CSSPixelsToAppUnits(1); michael@0: if (horiz) { michael@0: // In the left-to-right case we scroll so that the left edge of the michael@0: // selected child is scrolled to the left edge of the scrollbox. michael@0: // In the right-to-left case we scroll so that the right edge of the michael@0: // selected child is scrolled to the right edge of the scrollbox. michael@0: michael@0: nsPoint pt(isLTR ? rect.x : rect.x + rect.width - frameWidth, michael@0: cp.y); michael@0: michael@0: // Use a destination range that ensures the left edge (or right edge, michael@0: // for RTL) will indeed be visible. Also ensure that the top edge michael@0: // is visible. michael@0: nsRect range(pt.x, pt.y, csspixel, 0); michael@0: if (isLTR) { michael@0: range.x -= csspixel; michael@0: } michael@0: sf->ScrollTo(pt, nsIScrollableFrame::INSTANT, &range); michael@0: } else { michael@0: // Use a destination range that ensures the top edge will be visible. michael@0: nsRect range(cp.x, rect.y - csspixel, 0, csspixel); michael@0: sf->ScrollTo(nsPoint(cp.x, rect.y), nsIScrollableFrame::INSTANT, &range); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void scrollToLine (in long line); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::ScrollToLine(int32_t line) michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: if (!sf) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nscoord y = sf->GetLineScrollAmount().height * line; michael@0: nsRect range(0, y - nsPresContext::CSSPixelsToAppUnits(1), michael@0: 0, nsPresContext::CSSPixelsToAppUnits(1)); michael@0: sf->ScrollTo(nsPoint(0, y), nsIScrollableFrame::INSTANT, &range); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void scrollToElement (in nsIDOMElement child); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::ScrollToElement(nsIDOMElement *child) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(child); michael@0: michael@0: nsCOMPtr shell = GetPresShell(false); michael@0: if (!shell) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsCOMPtr content = do_QueryInterface(child); michael@0: shell->ScrollContentIntoView(content, michael@0: nsIPresShell::ScrollAxis( michael@0: nsIPresShell::SCROLL_TOP, michael@0: nsIPresShell::SCROLL_ALWAYS), michael@0: nsIPresShell::ScrollAxis( michael@0: nsIPresShell::SCROLL_LEFT, michael@0: nsIPresShell::SCROLL_ALWAYS), michael@0: nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY | michael@0: nsIPresShell::SCROLL_OVERFLOW_HIDDEN); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void scrollToIndex (in long index); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::ScrollToIndex(int32_t index) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /* void getPosition (out long x, out long y); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::GetPosition(int32_t *x, int32_t *y) michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: if (!sf) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: CSSIntPoint pt = sf->GetScrollPositionCSSPixels(); michael@0: *x = pt.x; michael@0: *y = pt.y; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void getScrolledSize (out long width, out long height); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::GetScrolledSize(int32_t *width, int32_t *height) michael@0: { michael@0: nsIFrame* scrolledBox = GetScrolledBox(this); michael@0: if (!scrolledBox) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsRect scrollRect = scrolledBox->GetRect(); michael@0: michael@0: *width = nsPresContext::AppUnitsToIntCSSPixels(scrollRect.width); michael@0: *height = nsPresContext::AppUnitsToIntCSSPixels(scrollRect.height); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void ensureElementIsVisible (in nsIDOMElement child); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::EnsureElementIsVisible(nsIDOMElement *child) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(child); michael@0: michael@0: nsCOMPtr shell = GetPresShell(false); michael@0: if (!shell) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsCOMPtr content = do_QueryInterface(child); michael@0: shell->ScrollContentIntoView(content, michael@0: nsIPresShell::ScrollAxis(), michael@0: nsIPresShell::ScrollAxis(), michael@0: nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY | michael@0: nsIPresShell::SCROLL_OVERFLOW_HIDDEN); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void ensureIndexIsVisible (in long index); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::EnsureIndexIsVisible(int32_t index) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /* void ensureLineIsVisible (in long line); */ michael@0: NS_IMETHODIMP nsScrollBoxObject::EnsureLineIsVisible(int32_t line) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewScrollBoxObject(nsIBoxObject** aResult) michael@0: { michael@0: *aResult = new nsScrollBoxObject; michael@0: if (!*aResult) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(*aResult); michael@0: return NS_OK; michael@0: } michael@0: