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: #ifndef nsListControlFrame_h___ michael@0: #define nsListControlFrame_h___ michael@0: michael@0: #ifdef DEBUG_evaughan michael@0: //#define DEBUG_rods michael@0: #endif michael@0: michael@0: #ifdef DEBUG_rods michael@0: //#define DO_REFLOW_DEBUG michael@0: //#define DO_REFLOW_COUNTER michael@0: //#define DO_UNCONSTRAINED_CHECK michael@0: //#define DO_PIXELS michael@0: #endif michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsGfxScrollFrame.h" michael@0: #include "nsIFormControlFrame.h" michael@0: #include "nsIListControlFrame.h" michael@0: #include "nsISelectControlFrame.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsSelectsAreaFrame.h" michael@0: michael@0: // X.h defines KeyPress michael@0: #ifdef KeyPress michael@0: #undef KeyPress michael@0: #endif michael@0: michael@0: class nsIDOMHTMLSelectElement; michael@0: class nsIDOMHTMLOptionsCollection; michael@0: class nsIComboboxControlFrame; michael@0: class nsPresContext; michael@0: class nsListEventListener; michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class HTMLOptionElement; michael@0: class HTMLOptionsCollection; michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: /** michael@0: * Frame-based listbox. michael@0: */ michael@0: michael@0: class nsListControlFrame : public nsHTMLScrollFrame, michael@0: public nsIFormControlFrame, michael@0: public nsIListControlFrame, michael@0: public nsISelectControlFrame michael@0: { michael@0: public: michael@0: friend nsIFrame* NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: NS_DECL_QUERYFRAME michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: // nsIFrame michael@0: virtual nsresult HandleEvent(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) MOZ_OVERRIDE; michael@0: michael@0: virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult Reflow(nsPresContext* aCX, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) MOZ_OVERRIDE; michael@0: michael@0: virtual void Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult DidReflow(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState* aReflowState, michael@0: nsDidReflowStatus aStatus) MOZ_OVERRIDE; michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: michael@0: virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) MOZ_OVERRIDE; michael@0: michael@0: virtual nsIFrame* GetContentInsertionFrame() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Get the "type" of the frame michael@0: * michael@0: * @see nsGkAtoms::scrollFrame michael@0: */ michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE michael@0: { michael@0: return nsHTMLScrollFrame::IsFrameOfType(aFlags & michael@0: ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: // nsIFormControlFrame michael@0: virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) MOZ_OVERRIDE; michael@0: virtual void SetFocus(bool aOn = true, bool aRepaint = false) MOZ_OVERRIDE; michael@0: michael@0: virtual mozilla::ScrollbarStyles GetScrollbarStyles() const MOZ_OVERRIDE; michael@0: virtual bool ShouldPropagateComputedHeightToScrolledContent() const MOZ_OVERRIDE; michael@0: michael@0: // for accessibility purposes michael@0: #ifdef ACCESSIBILITY michael@0: virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: // nsIListControlFrame michael@0: virtual void SetComboboxFrame(nsIFrame* aComboboxFrame) MOZ_OVERRIDE; michael@0: virtual int32_t GetSelectedIndex() MOZ_OVERRIDE; michael@0: virtual mozilla::dom::HTMLOptionElement* GetCurrentOption() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Gets the text of the currently selected item. michael@0: * If the there are zero items then an empty string is returned michael@0: * If there is nothing selected, then the 0th item's text is returned. michael@0: */ michael@0: virtual void GetOptionText(uint32_t aIndex, nsAString& aStr) MOZ_OVERRIDE; michael@0: michael@0: virtual void CaptureMouseEvents(bool aGrabMouseEvents) MOZ_OVERRIDE; michael@0: virtual nscoord GetHeightOfARow() MOZ_OVERRIDE; michael@0: virtual uint32_t GetNumberOfOptions() MOZ_OVERRIDE; michael@0: virtual void AboutToDropDown() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: virtual void AboutToRollup() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Dispatch a DOM onchange event synchroniously. michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: virtual void FireOnChange() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Makes aIndex the selected option of a combobox list. michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: virtual void ComboboxFinish(int32_t aIndex) MOZ_OVERRIDE; michael@0: virtual void OnContentReset() MOZ_OVERRIDE; michael@0: michael@0: // nsISelectControlFrame michael@0: NS_IMETHOD AddOption(int32_t index) MOZ_OVERRIDE; michael@0: NS_IMETHOD RemoveOption(int32_t index) MOZ_OVERRIDE; michael@0: NS_IMETHOD DoneAddingChildren(bool aIsDone) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Gets the content (an option) by index and then set it as michael@0: * being selected or not selected. michael@0: */ michael@0: NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) MOZ_OVERRIDE; michael@0: NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Mouse event listeners. michael@0: * @note These methods might destroy the frame, pres shell and other objects. michael@0: */ michael@0: nsresult MouseDown(nsIDOMEvent* aMouseEvent); michael@0: nsresult MouseUp(nsIDOMEvent* aMouseEvent); michael@0: nsresult MouseMove(nsIDOMEvent* aMouseEvent); michael@0: nsresult DragMove(nsIDOMEvent* aMouseEvent); michael@0: nsresult KeyDown(nsIDOMEvent* aKeyEvent); michael@0: nsresult KeyPress(nsIDOMEvent* aKeyEvent); michael@0: michael@0: /** michael@0: * Returns the options collection for mContent, if any. michael@0: */ michael@0: mozilla::dom::HTMLOptionsCollection* GetOptions() const; michael@0: /** michael@0: * Returns the HTMLOptionElement for a given index in mContent's collection. michael@0: */ michael@0: mozilla::dom::HTMLOptionElement* GetOption(uint32_t aIndex) const; michael@0: michael@0: static void ComboboxFocusSet(); michael@0: michael@0: // Helper michael@0: bool IsFocused() { return this == mFocused; } michael@0: michael@0: /** michael@0: * Function to paint the focus rect when our nsSelectsAreaFrame is painting. michael@0: * @param aPt the offset of this frame, relative to the rendering reference michael@0: * frame michael@0: */ michael@0: void PaintFocus(nsRenderingContext& aRC, nsPoint aPt); michael@0: /** michael@0: * If this frame IsFocused(), invalidates an area that includes anything michael@0: * that PaintFocus will or could have painted --- basically the whole michael@0: * GetOptionsContainer, plus some extra stuff if there are no options. This michael@0: * must be called every time mEndSelectionIndex changes. michael@0: */ michael@0: void InvalidateFocus(); michael@0: michael@0: /** michael@0: * Function to calculate the height a row, for use with the "size" attribute. michael@0: * Can't be const because GetNumberOfOptions() isn't const. michael@0: */ michael@0: nscoord CalcHeightOfARow(); michael@0: michael@0: /** michael@0: * Function to ask whether we're currently in what might be the michael@0: * first pass of a two-pass reflow. michael@0: */ michael@0: bool MightNeedSecondPass() const { michael@0: return mMightNeedSecondPass; michael@0: } michael@0: michael@0: void SetSuppressScrollbarUpdate(bool aSuppress) { michael@0: nsHTMLScrollFrame::SetSuppressScrollbarUpdate(aSuppress); michael@0: } michael@0: michael@0: /** michael@0: * Return whether the list is in dropdown mode. michael@0: */ michael@0: bool IsInDropDownMode() const; michael@0: michael@0: /** michael@0: * Return the number of displayed rows in the list. michael@0: */ michael@0: uint32_t GetNumDisplayRows() const { return mNumDisplayRows; } michael@0: michael@0: /** michael@0: * Return true if the drop-down list can display more rows. michael@0: * (always false if not in drop-down mode) michael@0: */ michael@0: bool GetDropdownCanGrow() const { return mDropdownCanGrow; } michael@0: michael@0: /** michael@0: * Dropdowns need views michael@0: */ michael@0: virtual bool NeedsView() MOZ_OVERRIDE { return IsInDropDownMode(); } michael@0: michael@0: /** michael@0: * Frees statics owned by this class. michael@0: */ michael@0: static void Shutdown(); michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: /** michael@0: * Post a custom DOM event for the change, so that accessibility can michael@0: * fire a native focus event for accessibility michael@0: * (Some 3rd party products need to track our focus) michael@0: */ michael@0: void FireMenuItemActiveEvent(); // Inform assistive tech what got focused michael@0: #endif michael@0: michael@0: protected: michael@0: /** michael@0: * Updates the selected text in a combobox and then calls FireOnChange(). michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: * Returns false if calling it destroyed |this|. michael@0: */ michael@0: bool UpdateSelection(); michael@0: michael@0: /** michael@0: * Returns whether mContent supports multiple selection. michael@0: */ michael@0: bool GetMultiple() const { michael@0: return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Toggles (show/hide) the combobox dropdown menu. michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: void DropDownToggleKey(nsIDOMEvent* aKeyEvent); michael@0: michael@0: nsresult IsOptionDisabled(int32_t anIndex, bool &aIsDisabled); michael@0: /** michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: void ScrollToFrame(mozilla::dom::HTMLOptionElement& aOptElement); michael@0: /** michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: void ScrollToIndex(int32_t anIndex); michael@0: michael@0: /** michael@0: * When the user clicks on the comboboxframe to show the dropdown michael@0: * listbox, they then have to move the mouse into the list. We don't michael@0: * want to process those mouse events as selection events (i.e., to michael@0: * scroll list items into view). So we ignore the events until michael@0: * the mouse moves below our border-inner-edge, when michael@0: * mItemSelectionStarted is set. michael@0: * michael@0: * @param aPoint relative to this frame michael@0: */ michael@0: bool IgnoreMouseEventForSelection(nsIDOMEvent* aEvent); michael@0: michael@0: /** michael@0: * If the dropdown is showing and the mouse has moved below our michael@0: * border-inner-edge, then set mItemSelectionStarted. michael@0: */ michael@0: void UpdateInListState(nsIDOMEvent* aEvent); michael@0: void AdjustIndexForDisabledOpt(int32_t aStartIndex, int32_t &anNewIndex, michael@0: int32_t aNumOptions, int32_t aDoAdjustInc, int32_t aDoAdjustIncNext); michael@0: michael@0: /** michael@0: * Resets the select back to it's original default values; michael@0: * those values as determined by the original HTML michael@0: */ michael@0: virtual void ResetList(bool aAllowScrolling); michael@0: michael@0: nsListControlFrame(nsIPresShell* aShell, nsIDocument* aDocument, nsStyleContext* aContext); michael@0: virtual ~nsListControlFrame(); michael@0: michael@0: /** michael@0: * Sets the mSelectedIndex and mOldSelectedIndex from figuring out what michael@0: * item was selected using content michael@0: * @param aPoint the event point, in listcontrolframe coordinates michael@0: * @return NS_OK if it successfully found the selection michael@0: */ michael@0: nsresult GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, int32_t& aCurIndex); michael@0: michael@0: bool CheckIfAllFramesHere(); michael@0: bool IsLeftButton(nsIDOMEvent* aMouseEvent); michael@0: michael@0: // guess at a row height based on our own style. michael@0: nscoord CalcFallbackRowHeight(float aFontSizeInflation); michael@0: michael@0: // CalcIntrinsicHeight computes our intrinsic height (taking the "size" michael@0: // attribute into account). This should only be called in non-dropdown mode. michael@0: nscoord CalcIntrinsicHeight(nscoord aHeightOfARow, int32_t aNumberOfOptions); michael@0: michael@0: // Dropped down stuff michael@0: void SetComboboxItem(int32_t aIndex); michael@0: michael@0: /** michael@0: * Method to reflow ourselves as a dropdown list. This differs from michael@0: * reflow as a listbox because the criteria for needing a second michael@0: * pass are different. This will be called from Reflow() as needed. michael@0: */ michael@0: nsresult ReflowAsDropdown(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus); michael@0: michael@0: // Selection michael@0: bool SetOptionsSelectedFromFrame(int32_t aStartIndex, michael@0: int32_t aEndIndex, michael@0: bool aValue, michael@0: bool aClearAll); michael@0: bool ToggleOptionSelectedFromFrame(int32_t aIndex); michael@0: /** michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: bool SingleSelection(int32_t aClickedIndex, bool aDoToggle); michael@0: bool ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex, michael@0: bool aClearAll); michael@0: /** michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: bool PerformSelection(int32_t aClickedIndex, bool aIsShift, michael@0: bool aIsControl); michael@0: /** michael@0: * @note This method might destroy the frame, pres shell and other objects. michael@0: */ michael@0: bool HandleListSelection(nsIDOMEvent * aDOMEvent, int32_t selectedIndex); michael@0: void InitSelectionRange(int32_t aClickedIndex); michael@0: void PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode, michael@0: bool aIsShift, bool aIsControlOrMeta); michael@0: michael@0: public: michael@0: nsSelectsAreaFrame* GetOptionsContainer() const { michael@0: return static_cast(GetScrolledFrame()); michael@0: } michael@0: michael@0: protected: michael@0: nscoord HeightOfARow() { michael@0: return GetOptionsContainer()->HeightOfARow(); michael@0: } michael@0: michael@0: /** michael@0: * @return how many displayable options/optgroups this frame has. michael@0: */ michael@0: uint32_t GetNumberOfRows(); michael@0: michael@0: // Data Members michael@0: int32_t mStartSelectionIndex; michael@0: int32_t mEndSelectionIndex; michael@0: michael@0: nsIComboboxControlFrame *mComboboxFrame; michael@0: uint32_t mNumDisplayRows; michael@0: bool mChangesSinceDragStart:1; michael@0: bool mButtonDown:1; michael@0: // Has the user selected a visible item since we showed the michael@0: // dropdown? michael@0: bool mItemSelectionStarted:1; michael@0: michael@0: bool mIsAllContentHere:1; michael@0: bool mIsAllFramesHere:1; michael@0: bool mHasBeenInitialized:1; michael@0: bool mNeedToReset:1; michael@0: bool mPostChildrenLoadedReset:1; michael@0: michael@0: //bool value for multiple discontiguous selection michael@0: bool mControlSelectMode:1; michael@0: michael@0: // True if we're in the middle of a reflow and might need a second michael@0: // pass. This only happens for auto heights. michael@0: bool mMightNeedSecondPass:1; michael@0: michael@0: /** michael@0: * Set to aPresContext->HasPendingInterrupt() at the start of Reflow. michael@0: * Set to false at the end of DidReflow. michael@0: */ michael@0: bool mHasPendingInterruptAtStartOfReflow:1; michael@0: michael@0: // True if the drop-down can show more rows. Always false if this list michael@0: // is not in drop-down mode. michael@0: bool mDropdownCanGrow:1; michael@0: michael@0: // The last computed height we reflowed at if we're a combobox dropdown. michael@0: // XXXbz should we be using a subclass here? Or just not worry michael@0: // about the extra member on listboxes? michael@0: nscoord mLastDropdownComputedHeight; michael@0: michael@0: // At the time of our last dropdown, the backstop color to draw in case we michael@0: // are translucent. michael@0: nscolor mLastDropdownBackstopColor; michael@0: michael@0: nsRefPtr mEventListener; michael@0: michael@0: static nsListControlFrame * mFocused; michael@0: static nsString * sIncrementalString; michael@0: michael@0: #ifdef DO_REFLOW_COUNTER michael@0: int32_t mReflowId; michael@0: #endif michael@0: michael@0: private: michael@0: // for incremental typing navigation michael@0: static nsAString& GetIncrementalString (); michael@0: static DOMTimeStamp gLastKeyTime; michael@0: michael@0: class MOZ_STACK_CLASS AutoIncrementalSearchResetter michael@0: { michael@0: public: michael@0: AutoIncrementalSearchResetter() : michael@0: mCancelled(false) michael@0: { michael@0: } michael@0: ~AutoIncrementalSearchResetter() michael@0: { michael@0: if (!mCancelled) { michael@0: nsListControlFrame::GetIncrementalString().Truncate(); michael@0: } michael@0: } michael@0: void Cancel() michael@0: { michael@0: mCancelled = true; michael@0: } michael@0: private: michael@0: bool mCancelled; michael@0: }; michael@0: }; michael@0: michael@0: #endif /* nsListControlFrame_h___ */ michael@0: