summaryrefslogtreecommitdiff
path: root/layout/forms/nsListControlFrame.h
blob: fa939d6c80d0798a6889a789e6176b5bb1207715 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsListControlFrame_h___
#define nsListControlFrame_h___

#ifdef DEBUG_evaughan
//#define DEBUG_rods
#endif

#ifdef DEBUG_rods
//#define DO_REFLOW_DEBUG
//#define DO_REFLOW_COUNTER
//#define DO_UNCONSTRAINED_CHECK
//#define DO_PIXELS
#endif

#include "mozilla/Attributes.h"
#include "nsGfxScrollFrame.h"
#include "nsIFormControlFrame.h"
#include "nsIListControlFrame.h"
#include "nsISelectControlFrame.h"
#include "nsSelectsAreaFrame.h"

// X.h defines KeyPress
#ifdef KeyPress
#undef KeyPress
#endif

class nsIComboboxControlFrame;
class nsPresContext;
class nsListEventListener;

namespace mozilla {
namespace dom {
class HTMLOptionElement;
class HTMLOptionsCollection;
} // namespace dom
} // namespace mozilla

/**
 * Frame-based listbox.
 */

class nsListControlFrame final : public nsHTMLScrollFrame,
                                 public nsIFormControlFrame,
                                 public nsIListControlFrame,
                                 public nsISelectControlFrame
{
public:
  friend nsContainerFrame* NS_NewListControlFrame(nsIPresShell* aPresShell,
                                                  nsStyleContext* aContext);

  NS_DECL_QUERYFRAME
  NS_DECL_FRAMEARENA_HELPERS

    // nsIFrame
  virtual nsresult HandleEvent(nsPresContext* aPresContext,
                               mozilla::WidgetGUIEvent* aEvent,
                               nsEventStatus* aEventStatus) override;

  virtual void SetInitialChildList(ChildListID     aListID,
                                   nsFrameList&    aChildList) override;

  virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
  virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;

  virtual void Reflow(nsPresContext*           aCX,
                      ReflowOutput&     aDesiredSize,
                      const ReflowInput& aReflowInput,
                      nsReflowStatus&          aStatus) override;

  virtual void Init(nsIContent*       aContent,
                    nsContainerFrame* aParent,
                    nsIFrame*         aPrevInFlow) override;

  virtual void DidReflow(nsPresContext*            aPresContext,
                         const ReflowInput*  aReflowInput,
                         nsDidReflowStatus         aStatus) override;
  virtual void DestroyFrom(nsIFrame* aDestructRoot) override;

  virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                const nsDisplayListSet& aLists) override;

  virtual nsContainerFrame* GetContentInsertionFrame() override;

  /**
   * Get the "type" of the frame
   *
   * @see nsGkAtoms::scrollFrame
   */
  virtual nsIAtom* GetType() const override;

  virtual bool IsFrameOfType(uint32_t aFlags) const override
  {
    return nsHTMLScrollFrame::IsFrameOfType(aFlags &
      ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
  }

#ifdef DEBUG_FRAME_DUMP
  virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif

    // nsIFormControlFrame
  virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
  virtual void SetFocus(bool aOn = true, bool aRepaint = false) override;

  virtual mozilla::ScrollStyles GetScrollStyles() const override;
  virtual bool ShouldPropagateComputedBSizeToScrolledContent() const override;

    // for accessibility purposes
#ifdef ACCESSIBILITY
  virtual mozilla::a11y::AccType AccessibleType() override;
#endif

    // nsIListControlFrame
  virtual void SetComboboxFrame(nsIFrame* aComboboxFrame) override;
  virtual int32_t GetSelectedIndex() override;
  virtual mozilla::dom::HTMLOptionElement* GetCurrentOption() override;

  /**
   * Gets the text of the currently selected item.
   * If the there are zero items then an empty string is returned
   * If there is nothing selected, then the 0th item's text is returned.
   */
  virtual void GetOptionText(uint32_t aIndex, nsAString& aStr) override;

  virtual void CaptureMouseEvents(bool aGrabMouseEvents) override;
  virtual nscoord GetBSizeOfARow() override;
  virtual uint32_t GetNumberOfOptions() override;
  virtual void AboutToDropDown() override;

  /**
   * @note This method might destroy the frame, pres shell and other objects.
   */
  virtual void AboutToRollup() override;

  /**
   * Dispatch a DOM oninput and onchange event synchroniously.
   * @note This method might destroy the frame, pres shell and other objects.
   */
  virtual void FireOnInputAndOnChange() override;

  /**
   * Makes aIndex the selected option of a combobox list.
   * @note This method might destroy the frame, pres shell and other objects.
   */
  virtual void ComboboxFinish(int32_t aIndex) override;
  virtual void OnContentReset() override;

  // nsISelectControlFrame
  NS_IMETHOD AddOption(int32_t index) override;
  NS_IMETHOD RemoveOption(int32_t index) override;
  NS_IMETHOD DoneAddingChildren(bool aIsDone) override;

  /**
   * Gets the content (an option) by index and then set it as
   * being selected or not selected.
   */
  NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) override;
  NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) override;

  /**
   * Mouse event listeners.
   * @note These methods might destroy the frame, pres shell and other objects.
   */
  nsresult MouseDown(nsIDOMEvent* aMouseEvent);
  nsresult MouseUp(nsIDOMEvent* aMouseEvent);
  nsresult MouseMove(nsIDOMEvent* aMouseEvent);
  nsresult DragMove(nsIDOMEvent* aMouseEvent);
  nsresult KeyDown(nsIDOMEvent* aKeyEvent);
  nsresult KeyPress(nsIDOMEvent* aKeyEvent);

  /**
   * Returns the options collection for mContent, if any.
   */
  mozilla::dom::HTMLOptionsCollection* GetOptions() const;
  /**
   * Returns the HTMLOptionElement for a given index in mContent's collection.
   */
  mozilla::dom::HTMLOptionElement* GetOption(uint32_t aIndex) const;

  static void ComboboxFocusSet();

  // Helper
  bool IsFocused() { return this == mFocused; }

  /**
   * Function to paint the focus rect when our nsSelectsAreaFrame is painting.
   * @param aPt the offset of this frame, relative to the rendering reference
   * frame
   */
  void PaintFocus(mozilla::gfx::DrawTarget* aDrawTarget, nsPoint aPt);

  /**
   * If this frame IsFocused(), invalidates an area that includes anything
   * that PaintFocus will or could have painted --- basically the whole
   * GetOptionsContainer, plus some extra stuff if there are no options. This
   * must be called every time mEndSelectionIndex changes.
   */
  void InvalidateFocus();

  /**
   * Function to calculate the block size of a row, for use with the
   * "size" attribute.
   * Can't be const because GetNumberOfOptions() isn't const.
   */
  nscoord CalcBSizeOfARow();

  /**
   * Function to ask whether we're currently in what might be the
   * first pass of a two-pass reflow.
   */
  bool MightNeedSecondPass() const {
    return mMightNeedSecondPass;
  }

  void SetSuppressScrollbarUpdate(bool aSuppress) {
    nsHTMLScrollFrame::SetSuppressScrollbarUpdate(aSuppress);
  }

  /**
   * Return whether the list is in dropdown mode.
   */
  bool IsInDropDownMode() const;

  /**
   * Return the number of displayed rows in the list.
   */
  uint32_t GetNumDisplayRows() const { return mNumDisplayRows; }

  /**
   * Return true if the drop-down list can display more rows.
   * (always false if not in drop-down mode)
   */
  bool GetDropdownCanGrow() const { return mDropdownCanGrow; }

  /**
   * Dropdowns need views
   */
  virtual bool NeedsView() override { return IsInDropDownMode(); }

  /**
   * Frees statics owned by this class.
   */
  static void Shutdown();

#ifdef ACCESSIBILITY
  /**
   * Post a custom DOM event for the change, so that accessibility can
   * fire a native focus event for accessibility
   * (Some 3rd party products need to track our focus)
   */
  void FireMenuItemActiveEvent(); // Inform assistive tech what got focused
#endif

protected:
  /**
   * Updates the selected text in a combobox and then calls FireOnChange().
   * @note This method might destroy the frame, pres shell and other objects.
   * Returns false if calling it destroyed |this|.
   */
  bool       UpdateSelection();

  /**
   * Returns whether mContent supports multiple selection.
   */
  bool       GetMultiple() const {
    return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
  }


  /**
   * Toggles (show/hide) the combobox dropdown menu.
   * @note This method might destroy the frame, pres shell and other objects.
   */
  void       DropDownToggleKey(nsIDOMEvent* aKeyEvent);

  nsresult   IsOptionDisabled(int32_t anIndex, bool &aIsDisabled);
  /**
   * @note This method might destroy the frame, pres shell and other objects.
   */
  void ScrollToFrame(mozilla::dom::HTMLOptionElement& aOptElement);
  /**
   * @note This method might destroy the frame, pres shell and other objects.
   */
  void ScrollToIndex(int32_t anIndex);

  /**
   * When the user clicks on the comboboxframe to show the dropdown
   * listbox, they then have to move the mouse into the list. We don't
   * want to process those mouse events as selection events (i.e., to
   * scroll list items into view). So we ignore the events until
   * the mouse moves below our border-inner-edge, when
   * mItemSelectionStarted is set.
   *
   * @param aPoint relative to this frame
   */
  bool       IgnoreMouseEventForSelection(nsIDOMEvent* aEvent);

  /**
   * If the dropdown is showing and the mouse has moved below our
   * border-inner-edge, then set mItemSelectionStarted.
   */
  void       UpdateInListState(nsIDOMEvent* aEvent);
  void       AdjustIndexForDisabledOpt(int32_t aStartIndex, int32_t &anNewIndex,
                                       int32_t aNumOptions, int32_t aDoAdjustInc, int32_t aDoAdjustIncNext);

  /**
   * Resets the select back to it's original default values;
   * those values as determined by the original HTML
   */
  virtual void ResetList(bool aAllowScrolling);

  explicit nsListControlFrame(nsStyleContext* aContext);
  virtual ~nsListControlFrame();

  /**
   * Sets the mSelectedIndex and mOldSelectedIndex from figuring out what
   * item was selected using content
   * @param aPoint the event point, in listcontrolframe coordinates
   * @return NS_OK if it successfully found the selection
   */
  nsresult GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, int32_t& aCurIndex);

  bool     CheckIfAllFramesHere();
  bool     IsLeftButton(nsIDOMEvent* aMouseEvent);

  // guess at a row block size based on our own style.
  nscoord  CalcFallbackRowBSize(float aFontSizeInflation);

  // CalcIntrinsicBSize computes our intrinsic block size (taking the
  // "size" attribute into account).  This should only be called in
  // non-dropdown mode.
  nscoord CalcIntrinsicBSize(nscoord aBSizeOfARow, int32_t aNumberOfOptions);

  // Dropped down stuff
  void     SetComboboxItem(int32_t aIndex);

  /**
   * Method to reflow ourselves as a dropdown list.  This differs from
   * reflow as a listbox because the criteria for needing a second
   * pass are different.  This will be called from Reflow() as needed.
   */
  void ReflowAsDropdown(nsPresContext*           aPresContext,
                        ReflowOutput&     aDesiredSize,
                        const ReflowInput& aReflowInput,
                        nsReflowStatus&          aStatus);

  // Selection
  bool     SetOptionsSelectedFromFrame(int32_t aStartIndex,
                                       int32_t aEndIndex,
                                       bool aValue,
                                       bool aClearAll);
  bool     ToggleOptionSelectedFromFrame(int32_t aIndex);
  /**
   * @note This method might destroy the frame, pres shell and other objects.
   */
  bool     SingleSelection(int32_t aClickedIndex, bool aDoToggle);
  bool     ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex,
                             bool aClearAll);
  /**
   * @note This method might destroy the frame, pres shell and other objects.
   */
  bool     PerformSelection(int32_t aClickedIndex, bool aIsShift,
                            bool aIsControl);
  /**
   * @note This method might destroy the frame, pres shell and other objects.
   */
  bool     HandleListSelection(nsIDOMEvent * aDOMEvent, int32_t selectedIndex);
  void     InitSelectionRange(int32_t aClickedIndex);
  void     PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode,
                              bool aIsShift, bool aIsControlOrMeta);

public:
  nsSelectsAreaFrame* GetOptionsContainer() const {
    return static_cast<nsSelectsAreaFrame*>(GetScrolledFrame());
  }

protected:
  nscoord BSizeOfARow() {
    return GetOptionsContainer()->BSizeOfARow();
  }

  /**
   * @return how many displayable options/optgroups this frame has.
   */
  uint32_t GetNumberOfRows();

  // Data Members
  int32_t      mStartSelectionIndex;
  int32_t      mEndSelectionIndex;

  nsIComboboxControlFrame *mComboboxFrame;
  uint32_t     mNumDisplayRows;
  bool mChangesSinceDragStart:1;
  bool mButtonDown:1;
  // Has the user selected a visible item since we showed the
  // dropdown?
  bool mItemSelectionStarted:1;

  bool mIsAllContentHere:1;
  bool mIsAllFramesHere:1;
  bool mHasBeenInitialized:1;
  bool mNeedToReset:1;
  bool mPostChildrenLoadedReset:1;

  //bool value for multiple discontiguous selection
  bool mControlSelectMode:1;

  // True if we're in the middle of a reflow and might need a second
  // pass.  This only happens for auto heights.
  bool mMightNeedSecondPass:1;

  /**
   * Set to aPresContext->HasPendingInterrupt() at the start of Reflow.
   * Set to false at the end of DidReflow.
   */
  bool mHasPendingInterruptAtStartOfReflow:1;

  // True if the drop-down can show more rows.  Always false if this list
  // is not in drop-down mode.
  bool mDropdownCanGrow:1;

  // True if the selection can be set to nothing or disabled options.
  bool mForceSelection:1;

  // The last computed block size we reflowed at if we're a combobox
  // dropdown.
  // XXXbz should we be using a subclass here?  Or just not worry
  // about the extra member on listboxes?
  nscoord mLastDropdownComputedBSize;

  // At the time of our last dropdown, the backstop color to draw in case we
  // are translucent.
  nscolor mLastDropdownBackstopColor;

  RefPtr<nsListEventListener> mEventListener;

  static nsListControlFrame * mFocused;
  static nsString * sIncrementalString;

#ifdef DO_REFLOW_COUNTER
  int32_t mReflowId;
#endif

private:
  // for incremental typing navigation
  static nsAString& GetIncrementalString ();
  static DOMTimeStamp gLastKeyTime;

  class MOZ_RAII AutoIncrementalSearchResetter
  {
  public:
    AutoIncrementalSearchResetter() :
      mCancelled(false)
    {
    }
    ~AutoIncrementalSearchResetter()
    {
      if (!mCancelled) {
        nsListControlFrame::GetIncrementalString().Truncate();
      }
    }
    void Cancel()
    {
      mCancelled = true;
    }
  private:
    bool mCancelled;
  };
};

#endif /* nsListControlFrame_h___ */