|
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 |
|
6 // |
|
7 // nsMenuFrame |
|
8 // |
|
9 |
|
10 #ifndef nsMenuFrame_h__ |
|
11 #define nsMenuFrame_h__ |
|
12 |
|
13 #include "nsIAtom.h" |
|
14 #include "nsCOMPtr.h" |
|
15 |
|
16 #include "nsBoxFrame.h" |
|
17 #include "nsFrameList.h" |
|
18 #include "nsGkAtoms.h" |
|
19 #include "nsMenuParent.h" |
|
20 #include "nsXULPopupManager.h" |
|
21 #include "nsITimer.h" |
|
22 #include "mozilla/Attributes.h" |
|
23 |
|
24 nsIFrame* NS_NewMenuFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); |
|
25 nsIFrame* NS_NewMenuItemFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); |
|
26 |
|
27 class nsIContent; |
|
28 class nsMenuBarFrame; |
|
29 |
|
30 #define NS_STATE_ACCELTEXT_IS_DERIVED NS_STATE_BOX_CHILD_RESERVED |
|
31 |
|
32 // the type of menuitem |
|
33 enum nsMenuType { |
|
34 // a normal menuitem where a command is carried out when activated |
|
35 eMenuType_Normal = 0, |
|
36 // a menuitem with a checkmark that toggles when activated |
|
37 eMenuType_Checkbox = 1, |
|
38 // a radio menuitem where only one of it and its siblings with the same |
|
39 // name attribute can be checked at a time |
|
40 eMenuType_Radio = 2 |
|
41 }; |
|
42 |
|
43 enum nsMenuListType { |
|
44 eNotMenuList, // not a menulist |
|
45 eReadonlyMenuList, // <menulist/> |
|
46 eEditableMenuList // <menulist editable="true"/> |
|
47 }; |
|
48 |
|
49 class nsMenuFrame; |
|
50 |
|
51 /** |
|
52 * nsMenuTimerMediator is a wrapper around an nsMenuFrame which can be safely |
|
53 * passed to timers. The class is reference counted unlike the underlying |
|
54 * nsMenuFrame, so that it will exist as long as the timer holds a reference |
|
55 * to it. The callback is delegated to the contained nsMenuFrame as long as |
|
56 * the contained nsMenuFrame has not been destroyed. |
|
57 */ |
|
58 class nsMenuTimerMediator MOZ_FINAL : public nsITimerCallback |
|
59 { |
|
60 public: |
|
61 nsMenuTimerMediator(nsMenuFrame* aFrame); |
|
62 ~nsMenuTimerMediator(); |
|
63 |
|
64 NS_DECL_ISUPPORTS |
|
65 NS_DECL_NSITIMERCALLBACK |
|
66 |
|
67 void ClearFrame(); |
|
68 |
|
69 private: |
|
70 |
|
71 // Pointer to the wrapped frame. |
|
72 nsMenuFrame* mFrame; |
|
73 }; |
|
74 |
|
75 class nsMenuFrame : public nsBoxFrame |
|
76 { |
|
77 public: |
|
78 nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext); |
|
79 |
|
80 NS_DECL_QUERYFRAME_TARGET(nsMenuFrame) |
|
81 NS_DECL_QUERYFRAME |
|
82 NS_DECL_FRAMEARENA_HELPERS |
|
83 |
|
84 NS_IMETHOD DoLayout(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; |
|
85 virtual nsSize GetMinSize(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; |
|
86 virtual nsSize GetPrefSize(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; |
|
87 |
|
88 virtual void Init(nsIContent* aContent, |
|
89 nsIFrame* aParent, |
|
90 nsIFrame* aPrevInFlow) MOZ_OVERRIDE; |
|
91 |
|
92 #ifdef DEBUG_LAYOUT |
|
93 virtual nsresult SetDebug(nsBoxLayoutState& aState, bool aDebug) MOZ_OVERRIDE; |
|
94 #endif |
|
95 |
|
96 // The following methods are all overridden so that the menupopup |
|
97 // can be stored in a separate list, so that it doesn't impact reflow of the |
|
98 // actual menu item at all. |
|
99 virtual const nsFrameList& GetChildList(ChildListID aList) const MOZ_OVERRIDE; |
|
100 virtual void GetChildLists(nsTArray<ChildList>* aLists) const MOZ_OVERRIDE; |
|
101 virtual nsresult SetInitialChildList(ChildListID aListID, |
|
102 nsFrameList& aChildList) MOZ_OVERRIDE; |
|
103 virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; |
|
104 |
|
105 // Overridden to prevent events from going to children of the menu. |
|
106 virtual void BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, |
|
107 const nsRect& aDirtyRect, |
|
108 const nsDisplayListSet& aLists) MOZ_OVERRIDE; |
|
109 |
|
110 // this method can destroy the frame |
|
111 virtual nsresult HandleEvent(nsPresContext* aPresContext, |
|
112 mozilla::WidgetGUIEvent* aEvent, |
|
113 nsEventStatus* aEventStatus) MOZ_OVERRIDE; |
|
114 |
|
115 virtual nsresult AppendFrames(ChildListID aListID, |
|
116 nsFrameList& aFrameList) MOZ_OVERRIDE; |
|
117 |
|
118 virtual nsresult InsertFrames(ChildListID aListID, |
|
119 nsIFrame* aPrevFrame, |
|
120 nsFrameList& aFrameList) MOZ_OVERRIDE; |
|
121 |
|
122 virtual nsresult RemoveFrame(ChildListID aListID, |
|
123 nsIFrame* aOldFrame) MOZ_OVERRIDE; |
|
124 |
|
125 virtual nsIAtom* GetType() const MOZ_OVERRIDE { return nsGkAtoms::menuFrame; } |
|
126 |
|
127 NS_IMETHOD SelectMenu(bool aActivateFlag); |
|
128 |
|
129 virtual nsIScrollableFrame* GetScrollTargetFrame() MOZ_OVERRIDE; |
|
130 |
|
131 // Retrieve the element that the menu should be anchored to. By default this is |
|
132 // the menu itself. However, the anchor attribute may refer to the value of an |
|
133 // anonid within the menu's binding, or, if not found, the id of an element in |
|
134 // the document. |
|
135 nsIContent* GetAnchor(); |
|
136 |
|
137 /** |
|
138 * NOTE: OpenMenu will open the menu asynchronously. |
|
139 */ |
|
140 void OpenMenu(bool aSelectFirstItem); |
|
141 // CloseMenu closes the menu asynchronously |
|
142 void CloseMenu(bool aDeselectMenu); |
|
143 |
|
144 bool IsChecked() { return mChecked; } |
|
145 |
|
146 NS_IMETHOD GetActiveChild(nsIDOMElement** aResult); |
|
147 NS_IMETHOD SetActiveChild(nsIDOMElement* aChild); |
|
148 |
|
149 // called when the Enter key is pressed while the menuitem is the current |
|
150 // one in its parent popup. This will carry out the command attached to |
|
151 // the menuitem. If the menu should be opened, this frame will be returned, |
|
152 // otherwise null will be returned. |
|
153 nsMenuFrame* Enter(mozilla::WidgetGUIEvent* aEvent); |
|
154 |
|
155 virtual void SetParent(nsIFrame* aParent) MOZ_OVERRIDE; |
|
156 |
|
157 virtual nsMenuParent *GetMenuParent() { return mMenuParent; } |
|
158 const nsAString& GetRadioGroupName() { return mGroupName; } |
|
159 nsMenuType GetMenuType() { return mType; } |
|
160 nsMenuPopupFrame* GetPopup(); |
|
161 |
|
162 /** |
|
163 * @return true if this frame has a popup child frame. |
|
164 */ |
|
165 bool HasPopup() const |
|
166 { |
|
167 return (GetStateBits() & NS_STATE_MENU_HAS_POPUP_LIST) != 0; |
|
168 } |
|
169 |
|
170 |
|
171 // nsMenuFrame methods |
|
172 |
|
173 bool IsOnMenuBar() { return mMenuParent && mMenuParent->IsMenuBar(); } |
|
174 bool IsOnActiveMenuBar() { return IsOnMenuBar() && mMenuParent->IsActive(); } |
|
175 virtual bool IsOpen(); |
|
176 virtual bool IsMenu(); |
|
177 nsMenuListType GetParentMenuListType(); |
|
178 bool IsDisabled(); |
|
179 void ToggleMenuState(); |
|
180 |
|
181 // indiciate that the menu's popup has just been opened, so that the menu |
|
182 // can update its open state. This method modifies the open attribute on |
|
183 // the menu, so the frames could be gone after this call. |
|
184 void PopupOpened(); |
|
185 // indiciate that the menu's popup has just been closed, so that the menu |
|
186 // can update its open state. The menu should be unhighlighted if |
|
187 // aDeselectedMenu is true. This method modifies the open attribute on |
|
188 // the menu, so the frames could be gone after this call. |
|
189 void PopupClosed(bool aDeselectMenu); |
|
190 |
|
191 // returns true if this is a menu on another menu popup. A menu is a submenu |
|
192 // if it has a parent popup or menupopup. |
|
193 bool IsOnMenu() { return mMenuParent && mMenuParent->IsMenu(); } |
|
194 void SetIsMenu(bool aIsMenu) { mIsMenu = aIsMenu; } |
|
195 |
|
196 #ifdef DEBUG_FRAME_DUMP |
|
197 virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE |
|
198 { |
|
199 return MakeFrameName(NS_LITERAL_STRING("Menu"), aResult); |
|
200 } |
|
201 #endif |
|
202 |
|
203 static bool IsSizedToPopup(nsIContent* aContent, bool aRequireAlways); |
|
204 |
|
205 protected: |
|
206 friend class nsMenuTimerMediator; |
|
207 friend class nsASyncMenuInitialization; |
|
208 friend class nsMenuAttributeChangedEvent; |
|
209 |
|
210 /** |
|
211 * Initialize the popup list to the first popup frame within |
|
212 * aChildList. Removes the popup, if any, from aChildList. |
|
213 */ |
|
214 void SetPopupFrame(nsFrameList& aChildList); |
|
215 |
|
216 /** |
|
217 * Get the popup frame list from the frame property. |
|
218 * @return the property value if it exists, nullptr otherwise. |
|
219 */ |
|
220 nsFrameList* GetPopupList() const; |
|
221 |
|
222 /** |
|
223 * Destroy the popup list property. The list must exist and be empty. |
|
224 */ |
|
225 void DestroyPopupList(); |
|
226 |
|
227 // set mMenuParent to the nearest enclosing menu bar or menupopup frame of |
|
228 // aParent (or aParent itself). This is called when initializing the frame, |
|
229 // so aParent should be the expected parent of this frame. |
|
230 void InitMenuParent(nsIFrame* aParent); |
|
231 |
|
232 // Update the menu's type (normal, checkbox, radio). |
|
233 // This method can destroy the frame. |
|
234 void UpdateMenuType(nsPresContext* aPresContext); |
|
235 // Update the checked state of the menu, and for radios, clear any other |
|
236 // checked items. This method can destroy the frame. |
|
237 void UpdateMenuSpecialState(nsPresContext* aPresContext); |
|
238 |
|
239 // Examines the key node and builds the accelerator. |
|
240 void BuildAcceleratorText(bool aNotify); |
|
241 |
|
242 // Called to execute our command handler. This method can destroy the frame. |
|
243 void Execute(mozilla::WidgetGUIEvent *aEvent); |
|
244 |
|
245 // This method can destroy the frame |
|
246 virtual nsresult AttributeChanged(int32_t aNameSpaceID, |
|
247 nsIAtom* aAttribute, |
|
248 int32_t aModType) MOZ_OVERRIDE; |
|
249 virtual ~nsMenuFrame() { } |
|
250 |
|
251 bool SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize); |
|
252 |
|
253 bool ShouldBlink(); |
|
254 void StartBlinking(mozilla::WidgetGUIEvent* aEvent, bool aFlipChecked); |
|
255 void StopBlinking(); |
|
256 void CreateMenuCommandEvent(mozilla::WidgetGUIEvent* aEvent, |
|
257 bool aFlipChecked); |
|
258 void PassMenuCommandEventToPopupManager(); |
|
259 |
|
260 protected: |
|
261 #ifdef DEBUG_LAYOUT |
|
262 nsresult SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, bool aDebug); |
|
263 #endif |
|
264 NS_HIDDEN_(nsresult) Notify(nsITimer* aTimer); |
|
265 |
|
266 bool mIsMenu; // Whether or not we can even have children or not. |
|
267 bool mChecked; // are we checked? |
|
268 bool mIgnoreAccelTextChange; // temporarily set while determining the accelerator key |
|
269 nsMenuType mType; |
|
270 |
|
271 nsMenuParent* mMenuParent; // Our parent menu. |
|
272 |
|
273 // Reference to the mediator which wraps this frame. |
|
274 nsRefPtr<nsMenuTimerMediator> mTimerMediator; |
|
275 |
|
276 nsCOMPtr<nsITimer> mOpenTimer; |
|
277 nsCOMPtr<nsITimer> mBlinkTimer; |
|
278 |
|
279 uint8_t mBlinkState; // 0: not blinking, 1: off, 2: on |
|
280 nsRefPtr<nsXULMenuCommandEvent> mDelayedMenuCommandEvent; |
|
281 |
|
282 nsString mGroupName; |
|
283 |
|
284 }; // class nsMenuFrame |
|
285 |
|
286 #endif |