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: // michael@0: // Eric Vaughan michael@0: // Netscape Communications michael@0: // michael@0: // See documentation in associated header file michael@0: // michael@0: michael@0: #include "nsScrollbarButtonFrame.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIContent.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsSliderFrame.h" michael@0: #include "nsScrollbarFrame.h" michael@0: #include "nsIScrollbarMediator.h" michael@0: #include "nsRepeatService.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // michael@0: // NS_NewToolbarFrame michael@0: // michael@0: // Creates a new Toolbar frame and returns it michael@0: // michael@0: nsIFrame* michael@0: NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsScrollbarButtonFrame(aPresShell, aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsScrollbarButtonFrame) michael@0: michael@0: nsresult michael@0: nsScrollbarButtonFrame::HandleEvent(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aEventStatus); michael@0: michael@0: // If a web page calls event.preventDefault() we still want to michael@0: // scroll when scroll arrow is clicked. See bug 511075. michael@0: if (!mContent->IsInNativeAnonymousSubtree() && michael@0: nsEventStatus_eConsumeNoDefault == *aEventStatus) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: switch (aEvent->message) { michael@0: case NS_MOUSE_BUTTON_DOWN: michael@0: mCursorOnThis = true; michael@0: // if we didn't handle the press ourselves, pass it on to the superclass michael@0: if (HandleButtonPress(aPresContext, aEvent, aEventStatus)) { michael@0: return NS_OK; michael@0: } michael@0: break; michael@0: case NS_MOUSE_BUTTON_UP: michael@0: HandleRelease(aPresContext, aEvent, aEventStatus); michael@0: break; michael@0: case NS_MOUSE_EXIT_SYNTH: michael@0: mCursorOnThis = false; michael@0: break; michael@0: case NS_MOUSE_MOVE: { michael@0: nsPoint cursor = michael@0: nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); michael@0: nsRect frameRect(nsPoint(0, 0), GetSize()); michael@0: mCursorOnThis = frameRect.Contains(cursor); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return nsButtonBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); michael@0: } michael@0: michael@0: michael@0: bool michael@0: nsScrollbarButtonFrame::HandleButtonPress(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: // Get the desired action for the scrollbar button. michael@0: LookAndFeel::IntID tmpAction; michael@0: uint16_t button = aEvent->AsMouseEvent()->button; michael@0: if (button == WidgetMouseEvent::eLeftButton) { michael@0: tmpAction = LookAndFeel::eIntID_ScrollButtonLeftMouseButtonAction; michael@0: } else if (button == WidgetMouseEvent::eMiddleButton) { michael@0: tmpAction = LookAndFeel::eIntID_ScrollButtonMiddleMouseButtonAction; michael@0: } else if (button == WidgetMouseEvent::eRightButton) { michael@0: tmpAction = LookAndFeel::eIntID_ScrollButtonRightMouseButtonAction; michael@0: } else { michael@0: return false; michael@0: } michael@0: michael@0: // Get the button action metric from the pres. shell. michael@0: int32_t pressedButtonAction; michael@0: if (NS_FAILED(LookAndFeel::GetInt(tmpAction, &pressedButtonAction))) { michael@0: return false; michael@0: } michael@0: michael@0: // get the scrollbar control michael@0: nsIFrame* scrollbar; michael@0: GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar); michael@0: michael@0: if (scrollbar == nullptr) michael@0: return false; michael@0: michael@0: // get the scrollbars content node michael@0: nsIContent* content = scrollbar->GetContent(); michael@0: michael@0: static nsIContent::AttrValuesArray strings[] = { &nsGkAtoms::increment, michael@0: &nsGkAtoms::decrement, michael@0: nullptr }; michael@0: int32_t index = mContent->FindAttrValueIn(kNameSpaceID_None, michael@0: nsGkAtoms::type, michael@0: strings, eCaseMatters); michael@0: int32_t direction; michael@0: if (index == 0) michael@0: direction = 1; michael@0: else if (index == 1) michael@0: direction = -1; michael@0: else michael@0: return false; michael@0: michael@0: // Whether or not to repeat the click action. michael@0: bool repeat = true; michael@0: // Use smooth scrolling by default. michael@0: bool smoothScroll = true; michael@0: switch (pressedButtonAction) { michael@0: case 0: michael@0: mIncrement = direction * nsSliderFrame::GetIncrement(content); michael@0: break; michael@0: case 1: michael@0: mIncrement = direction * nsSliderFrame::GetPageIncrement(content); michael@0: break; michael@0: case 2: michael@0: if (direction == -1) michael@0: mIncrement = -nsSliderFrame::GetCurrentPosition(content); michael@0: else michael@0: mIncrement = nsSliderFrame::GetMaxPosition(content) - michael@0: nsSliderFrame::GetCurrentPosition(content); michael@0: // Don't repeat or use smooth scrolling if scrolling to beginning or end michael@0: // of a page. michael@0: repeat = smoothScroll = false; michael@0: break; michael@0: case 3: michael@0: default: michael@0: // We were told to ignore this click, or someone assigned a non-standard michael@0: // value to the button's action. michael@0: return false; michael@0: } michael@0: // set this attribute so we can style it later michael@0: nsWeakFrame weakFrame(this); michael@0: mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::active, NS_LITERAL_STRING("true"), true); michael@0: michael@0: nsIPresShell::SetCapturingContent(mContent, CAPTURE_IGNOREALLOWED); michael@0: michael@0: if (weakFrame.IsAlive()) { michael@0: DoButtonAction(smoothScroll); michael@0: } michael@0: if (repeat) michael@0: StartRepeat(); michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScrollbarButtonFrame::HandleRelease(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: nsIPresShell::SetCapturingContent(nullptr, 0); michael@0: // we're not active anymore michael@0: mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, true); michael@0: StopRepeat(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsScrollbarButtonFrame::Notify() michael@0: { michael@0: // Since this is only going to get called if we're scrolling a page length michael@0: // or a line increment, we will always use smooth scrolling. michael@0: if (mCursorOnThis || michael@0: LookAndFeel::GetInt( michael@0: LookAndFeel::eIntID_ScrollbarButtonAutoRepeatBehavior, 0)) { michael@0: DoButtonAction(true); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsScrollbarButtonFrame::MouseClicked(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent) michael@0: { michael@0: nsButtonBoxFrame::MouseClicked(aPresContext, aEvent); michael@0: //MouseClicked(); michael@0: } michael@0: michael@0: void michael@0: nsScrollbarButtonFrame::DoButtonAction(bool aSmoothScroll) michael@0: { michael@0: // get the scrollbar control michael@0: nsIFrame* scrollbar; michael@0: GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar); michael@0: michael@0: if (scrollbar == nullptr) michael@0: return; michael@0: michael@0: // get the scrollbars content node michael@0: nsCOMPtr content = scrollbar->GetContent(); michael@0: michael@0: // get the current pos michael@0: int32_t curpos = nsSliderFrame::GetCurrentPosition(content); michael@0: int32_t oldpos = curpos; michael@0: michael@0: // get the max pos michael@0: int32_t maxpos = nsSliderFrame::GetMaxPosition(content); michael@0: michael@0: // increment the given amount michael@0: if (mIncrement) michael@0: curpos += mIncrement; michael@0: michael@0: // make sure the current position is between the current and max positions michael@0: if (curpos < 0) michael@0: curpos = 0; michael@0: else if (curpos > maxpos) michael@0: curpos = maxpos; michael@0: michael@0: nsScrollbarFrame* sb = do_QueryFrame(scrollbar); michael@0: if (sb) { michael@0: nsIScrollbarMediator* m = sb->GetScrollbarMediator(); michael@0: if (m) { michael@0: m->ScrollbarButtonPressed(sb, oldpos, curpos); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // set the current position of the slider. michael@0: nsAutoString curposStr; michael@0: curposStr.AppendInt(curpos); michael@0: michael@0: if (aSmoothScroll) michael@0: content->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, NS_LITERAL_STRING("true"), false); michael@0: content->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curposStr, true); michael@0: if (aSmoothScroll) michael@0: content->UnsetAttr(kNameSpaceID_None, nsGkAtoms::smooth, false); michael@0: } michael@0: michael@0: nsresult michael@0: nsScrollbarButtonFrame::GetChildWithTag(nsPresContext* aPresContext, michael@0: nsIAtom* atom, nsIFrame* start, michael@0: nsIFrame*& result) michael@0: { michael@0: // recursively search our children michael@0: nsIFrame* childFrame = start->GetFirstPrincipalChild(); michael@0: while (nullptr != childFrame) michael@0: { michael@0: // get the content node michael@0: nsIContent* child = childFrame->GetContent(); michael@0: michael@0: if (child) { michael@0: // see if it is the child michael@0: if (child->Tag() == atom) michael@0: { michael@0: result = childFrame; michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // recursive search the child michael@0: GetChildWithTag(aPresContext, atom, childFrame, result); michael@0: if (result != nullptr) michael@0: return NS_OK; michael@0: michael@0: childFrame = childFrame->GetNextSibling(); michael@0: } michael@0: michael@0: result = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScrollbarButtonFrame::GetParentWithTag(nsIAtom* toFind, nsIFrame* start, michael@0: nsIFrame*& result) michael@0: { michael@0: while (start) michael@0: { michael@0: start = start->GetParent(); michael@0: michael@0: if (start) { michael@0: // get the content node michael@0: nsIContent* child = start->GetContent(); michael@0: michael@0: if (child && child->Tag() == toFind) { michael@0: result = start; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: result = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsScrollbarButtonFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: // Ensure our repeat service isn't going... it's possible that a scrollbar can disappear out michael@0: // from under you while you're in the process of scrolling. michael@0: StopRepeat(); michael@0: nsButtonBoxFrame::DestroyFrom(aDestructRoot); michael@0: }