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