|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 #ifndef nsListControlFrame_h___ |
|
6 #define nsListControlFrame_h___ |
|
7 |
|
8 #ifdef DEBUG_evaughan |
|
9 //#define DEBUG_rods |
|
10 #endif |
|
11 |
|
12 #ifdef DEBUG_rods |
|
13 //#define DO_REFLOW_DEBUG |
|
14 //#define DO_REFLOW_COUNTER |
|
15 //#define DO_UNCONSTRAINED_CHECK |
|
16 //#define DO_PIXELS |
|
17 #endif |
|
18 |
|
19 #include "mozilla/Attributes.h" |
|
20 #include "nsGfxScrollFrame.h" |
|
21 #include "nsIFormControlFrame.h" |
|
22 #include "nsIListControlFrame.h" |
|
23 #include "nsISelectControlFrame.h" |
|
24 #include "nsAutoPtr.h" |
|
25 #include "nsSelectsAreaFrame.h" |
|
26 |
|
27 // X.h defines KeyPress |
|
28 #ifdef KeyPress |
|
29 #undef KeyPress |
|
30 #endif |
|
31 |
|
32 class nsIDOMHTMLSelectElement; |
|
33 class nsIDOMHTMLOptionsCollection; |
|
34 class nsIComboboxControlFrame; |
|
35 class nsPresContext; |
|
36 class nsListEventListener; |
|
37 |
|
38 namespace mozilla { |
|
39 namespace dom { |
|
40 class HTMLOptionElement; |
|
41 class HTMLOptionsCollection; |
|
42 } // namespace dom |
|
43 } // namespace mozilla |
|
44 |
|
45 /** |
|
46 * Frame-based listbox. |
|
47 */ |
|
48 |
|
49 class nsListControlFrame : public nsHTMLScrollFrame, |
|
50 public nsIFormControlFrame, |
|
51 public nsIListControlFrame, |
|
52 public nsISelectControlFrame |
|
53 { |
|
54 public: |
|
55 friend nsIFrame* NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); |
|
56 |
|
57 NS_DECL_QUERYFRAME |
|
58 NS_DECL_FRAMEARENA_HELPERS |
|
59 |
|
60 // nsIFrame |
|
61 virtual nsresult HandleEvent(nsPresContext* aPresContext, |
|
62 mozilla::WidgetGUIEvent* aEvent, |
|
63 nsEventStatus* aEventStatus) MOZ_OVERRIDE; |
|
64 |
|
65 virtual nsresult SetInitialChildList(ChildListID aListID, |
|
66 nsFrameList& aChildList) MOZ_OVERRIDE; |
|
67 |
|
68 virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; |
|
69 virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; |
|
70 |
|
71 virtual nsresult Reflow(nsPresContext* aCX, |
|
72 nsHTMLReflowMetrics& aDesiredSize, |
|
73 const nsHTMLReflowState& aReflowState, |
|
74 nsReflowStatus& aStatus) MOZ_OVERRIDE; |
|
75 |
|
76 virtual void Init(nsIContent* aContent, |
|
77 nsIFrame* aParent, |
|
78 nsIFrame* aPrevInFlow) MOZ_OVERRIDE; |
|
79 |
|
80 virtual nsresult DidReflow(nsPresContext* aPresContext, |
|
81 const nsHTMLReflowState* aReflowState, |
|
82 nsDidReflowStatus aStatus) MOZ_OVERRIDE; |
|
83 virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; |
|
84 |
|
85 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
86 const nsRect& aDirtyRect, |
|
87 const nsDisplayListSet& aLists) MOZ_OVERRIDE; |
|
88 |
|
89 virtual nsIFrame* GetContentInsertionFrame() MOZ_OVERRIDE; |
|
90 |
|
91 /** |
|
92 * Get the "type" of the frame |
|
93 * |
|
94 * @see nsGkAtoms::scrollFrame |
|
95 */ |
|
96 virtual nsIAtom* GetType() const MOZ_OVERRIDE; |
|
97 |
|
98 virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE |
|
99 { |
|
100 return nsHTMLScrollFrame::IsFrameOfType(aFlags & |
|
101 ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); |
|
102 } |
|
103 |
|
104 #ifdef DEBUG_FRAME_DUMP |
|
105 virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; |
|
106 #endif |
|
107 |
|
108 // nsIFormControlFrame |
|
109 virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) MOZ_OVERRIDE; |
|
110 virtual void SetFocus(bool aOn = true, bool aRepaint = false) MOZ_OVERRIDE; |
|
111 |
|
112 virtual mozilla::ScrollbarStyles GetScrollbarStyles() const MOZ_OVERRIDE; |
|
113 virtual bool ShouldPropagateComputedHeightToScrolledContent() const MOZ_OVERRIDE; |
|
114 |
|
115 // for accessibility purposes |
|
116 #ifdef ACCESSIBILITY |
|
117 virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE; |
|
118 #endif |
|
119 |
|
120 // nsIListControlFrame |
|
121 virtual void SetComboboxFrame(nsIFrame* aComboboxFrame) MOZ_OVERRIDE; |
|
122 virtual int32_t GetSelectedIndex() MOZ_OVERRIDE; |
|
123 virtual mozilla::dom::HTMLOptionElement* GetCurrentOption() MOZ_OVERRIDE; |
|
124 |
|
125 /** |
|
126 * Gets the text of the currently selected item. |
|
127 * If the there are zero items then an empty string is returned |
|
128 * If there is nothing selected, then the 0th item's text is returned. |
|
129 */ |
|
130 virtual void GetOptionText(uint32_t aIndex, nsAString& aStr) MOZ_OVERRIDE; |
|
131 |
|
132 virtual void CaptureMouseEvents(bool aGrabMouseEvents) MOZ_OVERRIDE; |
|
133 virtual nscoord GetHeightOfARow() MOZ_OVERRIDE; |
|
134 virtual uint32_t GetNumberOfOptions() MOZ_OVERRIDE; |
|
135 virtual void AboutToDropDown() MOZ_OVERRIDE; |
|
136 |
|
137 /** |
|
138 * @note This method might destroy the frame, pres shell and other objects. |
|
139 */ |
|
140 virtual void AboutToRollup() MOZ_OVERRIDE; |
|
141 |
|
142 /** |
|
143 * Dispatch a DOM onchange event synchroniously. |
|
144 * @note This method might destroy the frame, pres shell and other objects. |
|
145 */ |
|
146 virtual void FireOnChange() MOZ_OVERRIDE; |
|
147 |
|
148 /** |
|
149 * Makes aIndex the selected option of a combobox list. |
|
150 * @note This method might destroy the frame, pres shell and other objects. |
|
151 */ |
|
152 virtual void ComboboxFinish(int32_t aIndex) MOZ_OVERRIDE; |
|
153 virtual void OnContentReset() MOZ_OVERRIDE; |
|
154 |
|
155 // nsISelectControlFrame |
|
156 NS_IMETHOD AddOption(int32_t index) MOZ_OVERRIDE; |
|
157 NS_IMETHOD RemoveOption(int32_t index) MOZ_OVERRIDE; |
|
158 NS_IMETHOD DoneAddingChildren(bool aIsDone) MOZ_OVERRIDE; |
|
159 |
|
160 /** |
|
161 * Gets the content (an option) by index and then set it as |
|
162 * being selected or not selected. |
|
163 */ |
|
164 NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) MOZ_OVERRIDE; |
|
165 NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) MOZ_OVERRIDE; |
|
166 |
|
167 /** |
|
168 * Mouse event listeners. |
|
169 * @note These methods might destroy the frame, pres shell and other objects. |
|
170 */ |
|
171 nsresult MouseDown(nsIDOMEvent* aMouseEvent); |
|
172 nsresult MouseUp(nsIDOMEvent* aMouseEvent); |
|
173 nsresult MouseMove(nsIDOMEvent* aMouseEvent); |
|
174 nsresult DragMove(nsIDOMEvent* aMouseEvent); |
|
175 nsresult KeyDown(nsIDOMEvent* aKeyEvent); |
|
176 nsresult KeyPress(nsIDOMEvent* aKeyEvent); |
|
177 |
|
178 /** |
|
179 * Returns the options collection for mContent, if any. |
|
180 */ |
|
181 mozilla::dom::HTMLOptionsCollection* GetOptions() const; |
|
182 /** |
|
183 * Returns the HTMLOptionElement for a given index in mContent's collection. |
|
184 */ |
|
185 mozilla::dom::HTMLOptionElement* GetOption(uint32_t aIndex) const; |
|
186 |
|
187 static void ComboboxFocusSet(); |
|
188 |
|
189 // Helper |
|
190 bool IsFocused() { return this == mFocused; } |
|
191 |
|
192 /** |
|
193 * Function to paint the focus rect when our nsSelectsAreaFrame is painting. |
|
194 * @param aPt the offset of this frame, relative to the rendering reference |
|
195 * frame |
|
196 */ |
|
197 void PaintFocus(nsRenderingContext& aRC, nsPoint aPt); |
|
198 /** |
|
199 * If this frame IsFocused(), invalidates an area that includes anything |
|
200 * that PaintFocus will or could have painted --- basically the whole |
|
201 * GetOptionsContainer, plus some extra stuff if there are no options. This |
|
202 * must be called every time mEndSelectionIndex changes. |
|
203 */ |
|
204 void InvalidateFocus(); |
|
205 |
|
206 /** |
|
207 * Function to calculate the height a row, for use with the "size" attribute. |
|
208 * Can't be const because GetNumberOfOptions() isn't const. |
|
209 */ |
|
210 nscoord CalcHeightOfARow(); |
|
211 |
|
212 /** |
|
213 * Function to ask whether we're currently in what might be the |
|
214 * first pass of a two-pass reflow. |
|
215 */ |
|
216 bool MightNeedSecondPass() const { |
|
217 return mMightNeedSecondPass; |
|
218 } |
|
219 |
|
220 void SetSuppressScrollbarUpdate(bool aSuppress) { |
|
221 nsHTMLScrollFrame::SetSuppressScrollbarUpdate(aSuppress); |
|
222 } |
|
223 |
|
224 /** |
|
225 * Return whether the list is in dropdown mode. |
|
226 */ |
|
227 bool IsInDropDownMode() const; |
|
228 |
|
229 /** |
|
230 * Return the number of displayed rows in the list. |
|
231 */ |
|
232 uint32_t GetNumDisplayRows() const { return mNumDisplayRows; } |
|
233 |
|
234 /** |
|
235 * Return true if the drop-down list can display more rows. |
|
236 * (always false if not in drop-down mode) |
|
237 */ |
|
238 bool GetDropdownCanGrow() const { return mDropdownCanGrow; } |
|
239 |
|
240 /** |
|
241 * Dropdowns need views |
|
242 */ |
|
243 virtual bool NeedsView() MOZ_OVERRIDE { return IsInDropDownMode(); } |
|
244 |
|
245 /** |
|
246 * Frees statics owned by this class. |
|
247 */ |
|
248 static void Shutdown(); |
|
249 |
|
250 #ifdef ACCESSIBILITY |
|
251 /** |
|
252 * Post a custom DOM event for the change, so that accessibility can |
|
253 * fire a native focus event for accessibility |
|
254 * (Some 3rd party products need to track our focus) |
|
255 */ |
|
256 void FireMenuItemActiveEvent(); // Inform assistive tech what got focused |
|
257 #endif |
|
258 |
|
259 protected: |
|
260 /** |
|
261 * Updates the selected text in a combobox and then calls FireOnChange(). |
|
262 * @note This method might destroy the frame, pres shell and other objects. |
|
263 * Returns false if calling it destroyed |this|. |
|
264 */ |
|
265 bool UpdateSelection(); |
|
266 |
|
267 /** |
|
268 * Returns whether mContent supports multiple selection. |
|
269 */ |
|
270 bool GetMultiple() const { |
|
271 return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple); |
|
272 } |
|
273 |
|
274 |
|
275 /** |
|
276 * Toggles (show/hide) the combobox dropdown menu. |
|
277 * @note This method might destroy the frame, pres shell and other objects. |
|
278 */ |
|
279 void DropDownToggleKey(nsIDOMEvent* aKeyEvent); |
|
280 |
|
281 nsresult IsOptionDisabled(int32_t anIndex, bool &aIsDisabled); |
|
282 /** |
|
283 * @note This method might destroy the frame, pres shell and other objects. |
|
284 */ |
|
285 void ScrollToFrame(mozilla::dom::HTMLOptionElement& aOptElement); |
|
286 /** |
|
287 * @note This method might destroy the frame, pres shell and other objects. |
|
288 */ |
|
289 void ScrollToIndex(int32_t anIndex); |
|
290 |
|
291 /** |
|
292 * When the user clicks on the comboboxframe to show the dropdown |
|
293 * listbox, they then have to move the mouse into the list. We don't |
|
294 * want to process those mouse events as selection events (i.e., to |
|
295 * scroll list items into view). So we ignore the events until |
|
296 * the mouse moves below our border-inner-edge, when |
|
297 * mItemSelectionStarted is set. |
|
298 * |
|
299 * @param aPoint relative to this frame |
|
300 */ |
|
301 bool IgnoreMouseEventForSelection(nsIDOMEvent* aEvent); |
|
302 |
|
303 /** |
|
304 * If the dropdown is showing and the mouse has moved below our |
|
305 * border-inner-edge, then set mItemSelectionStarted. |
|
306 */ |
|
307 void UpdateInListState(nsIDOMEvent* aEvent); |
|
308 void AdjustIndexForDisabledOpt(int32_t aStartIndex, int32_t &anNewIndex, |
|
309 int32_t aNumOptions, int32_t aDoAdjustInc, int32_t aDoAdjustIncNext); |
|
310 |
|
311 /** |
|
312 * Resets the select back to it's original default values; |
|
313 * those values as determined by the original HTML |
|
314 */ |
|
315 virtual void ResetList(bool aAllowScrolling); |
|
316 |
|
317 nsListControlFrame(nsIPresShell* aShell, nsIDocument* aDocument, nsStyleContext* aContext); |
|
318 virtual ~nsListControlFrame(); |
|
319 |
|
320 /** |
|
321 * Sets the mSelectedIndex and mOldSelectedIndex from figuring out what |
|
322 * item was selected using content |
|
323 * @param aPoint the event point, in listcontrolframe coordinates |
|
324 * @return NS_OK if it successfully found the selection |
|
325 */ |
|
326 nsresult GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, int32_t& aCurIndex); |
|
327 |
|
328 bool CheckIfAllFramesHere(); |
|
329 bool IsLeftButton(nsIDOMEvent* aMouseEvent); |
|
330 |
|
331 // guess at a row height based on our own style. |
|
332 nscoord CalcFallbackRowHeight(float aFontSizeInflation); |
|
333 |
|
334 // CalcIntrinsicHeight computes our intrinsic height (taking the "size" |
|
335 // attribute into account). This should only be called in non-dropdown mode. |
|
336 nscoord CalcIntrinsicHeight(nscoord aHeightOfARow, int32_t aNumberOfOptions); |
|
337 |
|
338 // Dropped down stuff |
|
339 void SetComboboxItem(int32_t aIndex); |
|
340 |
|
341 /** |
|
342 * Method to reflow ourselves as a dropdown list. This differs from |
|
343 * reflow as a listbox because the criteria for needing a second |
|
344 * pass are different. This will be called from Reflow() as needed. |
|
345 */ |
|
346 nsresult ReflowAsDropdown(nsPresContext* aPresContext, |
|
347 nsHTMLReflowMetrics& aDesiredSize, |
|
348 const nsHTMLReflowState& aReflowState, |
|
349 nsReflowStatus& aStatus); |
|
350 |
|
351 // Selection |
|
352 bool SetOptionsSelectedFromFrame(int32_t aStartIndex, |
|
353 int32_t aEndIndex, |
|
354 bool aValue, |
|
355 bool aClearAll); |
|
356 bool ToggleOptionSelectedFromFrame(int32_t aIndex); |
|
357 /** |
|
358 * @note This method might destroy the frame, pres shell and other objects. |
|
359 */ |
|
360 bool SingleSelection(int32_t aClickedIndex, bool aDoToggle); |
|
361 bool ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex, |
|
362 bool aClearAll); |
|
363 /** |
|
364 * @note This method might destroy the frame, pres shell and other objects. |
|
365 */ |
|
366 bool PerformSelection(int32_t aClickedIndex, bool aIsShift, |
|
367 bool aIsControl); |
|
368 /** |
|
369 * @note This method might destroy the frame, pres shell and other objects. |
|
370 */ |
|
371 bool HandleListSelection(nsIDOMEvent * aDOMEvent, int32_t selectedIndex); |
|
372 void InitSelectionRange(int32_t aClickedIndex); |
|
373 void PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode, |
|
374 bool aIsShift, bool aIsControlOrMeta); |
|
375 |
|
376 public: |
|
377 nsSelectsAreaFrame* GetOptionsContainer() const { |
|
378 return static_cast<nsSelectsAreaFrame*>(GetScrolledFrame()); |
|
379 } |
|
380 |
|
381 protected: |
|
382 nscoord HeightOfARow() { |
|
383 return GetOptionsContainer()->HeightOfARow(); |
|
384 } |
|
385 |
|
386 /** |
|
387 * @return how many displayable options/optgroups this frame has. |
|
388 */ |
|
389 uint32_t GetNumberOfRows(); |
|
390 |
|
391 // Data Members |
|
392 int32_t mStartSelectionIndex; |
|
393 int32_t mEndSelectionIndex; |
|
394 |
|
395 nsIComboboxControlFrame *mComboboxFrame; |
|
396 uint32_t mNumDisplayRows; |
|
397 bool mChangesSinceDragStart:1; |
|
398 bool mButtonDown:1; |
|
399 // Has the user selected a visible item since we showed the |
|
400 // dropdown? |
|
401 bool mItemSelectionStarted:1; |
|
402 |
|
403 bool mIsAllContentHere:1; |
|
404 bool mIsAllFramesHere:1; |
|
405 bool mHasBeenInitialized:1; |
|
406 bool mNeedToReset:1; |
|
407 bool mPostChildrenLoadedReset:1; |
|
408 |
|
409 //bool value for multiple discontiguous selection |
|
410 bool mControlSelectMode:1; |
|
411 |
|
412 // True if we're in the middle of a reflow and might need a second |
|
413 // pass. This only happens for auto heights. |
|
414 bool mMightNeedSecondPass:1; |
|
415 |
|
416 /** |
|
417 * Set to aPresContext->HasPendingInterrupt() at the start of Reflow. |
|
418 * Set to false at the end of DidReflow. |
|
419 */ |
|
420 bool mHasPendingInterruptAtStartOfReflow:1; |
|
421 |
|
422 // True if the drop-down can show more rows. Always false if this list |
|
423 // is not in drop-down mode. |
|
424 bool mDropdownCanGrow:1; |
|
425 |
|
426 // The last computed height we reflowed at if we're a combobox dropdown. |
|
427 // XXXbz should we be using a subclass here? Or just not worry |
|
428 // about the extra member on listboxes? |
|
429 nscoord mLastDropdownComputedHeight; |
|
430 |
|
431 // At the time of our last dropdown, the backstop color to draw in case we |
|
432 // are translucent. |
|
433 nscolor mLastDropdownBackstopColor; |
|
434 |
|
435 nsRefPtr<nsListEventListener> mEventListener; |
|
436 |
|
437 static nsListControlFrame * mFocused; |
|
438 static nsString * sIncrementalString; |
|
439 |
|
440 #ifdef DO_REFLOW_COUNTER |
|
441 int32_t mReflowId; |
|
442 #endif |
|
443 |
|
444 private: |
|
445 // for incremental typing navigation |
|
446 static nsAString& GetIncrementalString (); |
|
447 static DOMTimeStamp gLastKeyTime; |
|
448 |
|
449 class MOZ_STACK_CLASS AutoIncrementalSearchResetter |
|
450 { |
|
451 public: |
|
452 AutoIncrementalSearchResetter() : |
|
453 mCancelled(false) |
|
454 { |
|
455 } |
|
456 ~AutoIncrementalSearchResetter() |
|
457 { |
|
458 if (!mCancelled) { |
|
459 nsListControlFrame::GetIncrementalString().Truncate(); |
|
460 } |
|
461 } |
|
462 void Cancel() |
|
463 { |
|
464 mCancelled = true; |
|
465 } |
|
466 private: |
|
467 bool mCancelled; |
|
468 }; |
|
469 }; |
|
470 |
|
471 #endif /* nsListControlFrame_h___ */ |
|
472 |