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 "nsPresContext.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsButtonBoxFrame.h" michael@0: #include "nsITimer.h" michael@0: #include "nsRepeatService.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "nsIContent.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: class nsAutoRepeatBoxFrame : public nsButtonBoxFrame michael@0: { michael@0: public: michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: friend nsIFrame* NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell, michael@0: nsStyleContext* aContext); michael@0: michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult HandleEvent(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) MOZ_OVERRIDE; michael@0: michael@0: NS_IMETHOD HandlePress(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) MOZ_OVERRIDE; michael@0: michael@0: NS_IMETHOD HandleRelease(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: nsAutoRepeatBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext): michael@0: nsButtonBoxFrame(aPresShell, aContext) {} michael@0: michael@0: void StartRepeat() { michael@0: if (IsActivatedOnHover()) { michael@0: // No initial delay on hover. michael@0: nsRepeatService::GetInstance()->Start(Notify, this, 0); michael@0: } else { michael@0: nsRepeatService::GetInstance()->Start(Notify, this); michael@0: } michael@0: } michael@0: void StopRepeat() { michael@0: nsRepeatService::GetInstance()->Stop(Notify, this); michael@0: } michael@0: void Notify(); michael@0: static void Notify(void* aData) { michael@0: static_cast(aData)->Notify(); michael@0: } michael@0: michael@0: bool mTrustedEvent; michael@0: michael@0: bool IsActivatedOnHover(); michael@0: }; michael@0: michael@0: nsIFrame* michael@0: NS_NewAutoRepeatBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsAutoRepeatBoxFrame (aPresShell, aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsAutoRepeatBoxFrame) michael@0: michael@0: nsresult michael@0: nsAutoRepeatBoxFrame::HandleEvent(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aEventStatus); michael@0: if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: switch(aEvent->message) michael@0: { michael@0: // repeat mode may be "hover" for repeating while the mouse is hovering michael@0: // over the element, otherwise repetition is done while the element is michael@0: // active (pressed). michael@0: case NS_MOUSE_ENTER: michael@0: case NS_MOUSE_ENTER_SYNTH: michael@0: if (IsActivatedOnHover()) { michael@0: StartRepeat(); michael@0: mTrustedEvent = aEvent->mFlags.mIsTrusted; michael@0: } michael@0: break; michael@0: michael@0: case NS_MOUSE_EXIT: michael@0: case NS_MOUSE_EXIT_SYNTH: michael@0: // always stop on mouse exit michael@0: StopRepeat(); michael@0: // Not really necessary but do this to be safe michael@0: mTrustedEvent = false; michael@0: break; michael@0: michael@0: case NS_MOUSE_CLICK: { michael@0: WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); michael@0: if (mouseEvent->IsLeftClickEvent()) { michael@0: // skip button frame handling to prevent click handling michael@0: return nsBoxFrame::HandleEvent(aPresContext, mouseEvent, aEventStatus); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return nsButtonBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAutoRepeatBoxFrame::HandlePress(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: if (!IsActivatedOnHover()) { michael@0: StartRepeat(); michael@0: mTrustedEvent = aEvent->mFlags.mIsTrusted; michael@0: DoMouseClick(aEvent, mTrustedEvent); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAutoRepeatBoxFrame::HandleRelease(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: if (!IsActivatedOnHover()) { michael@0: StopRepeat(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsAutoRepeatBoxFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aAttribute == nsGkAtoms::type) { michael@0: StopRepeat(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsAutoRepeatBoxFrame::Notify() michael@0: { michael@0: DoMouseClick(nullptr, mTrustedEvent); michael@0: } michael@0: michael@0: void michael@0: nsAutoRepeatBoxFrame::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: } michael@0: michael@0: bool michael@0: nsAutoRepeatBoxFrame::IsActivatedOnHover() michael@0: { michael@0: return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::repeat, michael@0: nsGkAtoms::hover, eCaseMatters); michael@0: }