/src/mozilla-central/layout/xul/nsDeckFrame.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // |
8 | | // Eric Vaughan |
9 | | // Netscape Communications |
10 | | // |
11 | | // See documentation in associated header file |
12 | | // |
13 | | |
14 | | #include "nsDeckFrame.h" |
15 | | #include "mozilla/ComputedStyle.h" |
16 | | #include "nsPresContext.h" |
17 | | #include "nsIContent.h" |
18 | | #include "nsCOMPtr.h" |
19 | | #include "nsNameSpaceManager.h" |
20 | | #include "nsGkAtoms.h" |
21 | | #include "nsHTMLParts.h" |
22 | | #include "nsIPresShell.h" |
23 | | #include "nsCSSRendering.h" |
24 | | #include "nsViewManager.h" |
25 | | #include "nsBoxLayoutState.h" |
26 | | #include "nsStackLayout.h" |
27 | | #include "nsDisplayList.h" |
28 | | #include "nsContainerFrame.h" |
29 | | #include "nsContentUtils.h" |
30 | | #include "nsXULPopupManager.h" |
31 | | |
32 | | #ifdef ACCESSIBILITY |
33 | | #include "nsAccessibilityService.h" |
34 | | #endif |
35 | | |
36 | | nsIFrame* |
37 | | NS_NewDeckFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
38 | 0 | { |
39 | 0 | return new (aPresShell) nsDeckFrame(aStyle); |
40 | 0 | } |
41 | | |
42 | | NS_IMPL_FRAMEARENA_HELPERS(nsDeckFrame) |
43 | | |
44 | 0 | NS_QUERYFRAME_HEAD(nsDeckFrame) |
45 | 0 | NS_QUERYFRAME_ENTRY(nsDeckFrame) |
46 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) |
47 | | |
48 | | nsDeckFrame::nsDeckFrame(ComputedStyle* aStyle) |
49 | | : nsBoxFrame(aStyle, kClassID) |
50 | | , mIndex(0) |
51 | 0 | { |
52 | 0 | nsCOMPtr<nsBoxLayout> layout; |
53 | 0 | NS_NewStackLayout(layout); |
54 | 0 | SetXULLayoutManager(layout); |
55 | 0 | } |
56 | | |
57 | | nsresult |
58 | | nsDeckFrame::AttributeChanged(int32_t aNameSpaceID, |
59 | | nsAtom* aAttribute, |
60 | | int32_t aModType) |
61 | 0 | { |
62 | 0 | nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, |
63 | 0 | aModType); |
64 | 0 |
|
65 | 0 |
|
66 | 0 | // if the index changed hide the old element and make the new element visible |
67 | 0 | if (aAttribute == nsGkAtoms::selectedIndex) { |
68 | 0 | IndexChanged(); |
69 | 0 | } |
70 | 0 |
|
71 | 0 | return rv; |
72 | 0 | } |
73 | | |
74 | | void |
75 | | nsDeckFrame::Init(nsIContent* aContent, |
76 | | nsContainerFrame* aParent, |
77 | | nsIFrame* aPrevInFlow) |
78 | 0 | { |
79 | 0 | nsBoxFrame::Init(aContent, aParent, aPrevInFlow); |
80 | 0 |
|
81 | 0 | mIndex = GetSelectedIndex(); |
82 | 0 | } |
83 | | |
84 | | void |
85 | | nsDeckFrame::HideBox(nsIFrame* aBox) |
86 | 0 | { |
87 | 0 | nsIPresShell::ClearMouseCapture(aBox); |
88 | 0 | } |
89 | | |
90 | | void |
91 | | nsDeckFrame::IndexChanged() |
92 | 0 | { |
93 | 0 | //did the index change? |
94 | 0 | int32_t index = GetSelectedIndex(); |
95 | 0 | if (index == mIndex) |
96 | 0 | return; |
97 | 0 | |
98 | 0 | // redraw |
99 | 0 | InvalidateFrame(); |
100 | 0 |
|
101 | 0 | // hide the currently showing box |
102 | 0 | nsIFrame* currentBox = GetSelectedBox(); |
103 | 0 | if (currentBox) // only hide if it exists |
104 | 0 | HideBox(currentBox); |
105 | 0 |
|
106 | 0 | mIndex = index; |
107 | 0 |
|
108 | 0 | #ifdef ACCESSIBILITY |
109 | 0 | nsAccessibilityService* accService = GetAccService(); |
110 | 0 | if (accService) { |
111 | 0 | accService->DeckPanelSwitched(PresContext()->GetPresShell(), mContent, |
112 | 0 | currentBox, GetSelectedBox()); |
113 | 0 | } |
114 | 0 | #endif |
115 | 0 |
|
116 | 0 | // Force any popups that might be anchored on elements within hidden |
117 | 0 | // box to update. |
118 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
119 | 0 | if (pm && currentBox) { |
120 | 0 | pm->UpdatePopupPositions(currentBox->PresContext()->RefreshDriver()); |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | int32_t |
125 | | nsDeckFrame::GetSelectedIndex() |
126 | 0 | { |
127 | 0 | // default index is 0 |
128 | 0 | int32_t index = 0; |
129 | 0 |
|
130 | 0 | // get the index attribute |
131 | 0 | nsAutoString value; |
132 | 0 | if (mContent->AsElement()->GetAttr(kNameSpaceID_None, |
133 | 0 | nsGkAtoms::selectedIndex, value)) { |
134 | 0 | nsresult error; |
135 | 0 |
|
136 | 0 | // convert it to an integer |
137 | 0 | index = value.ToInteger(&error); |
138 | 0 | } |
139 | 0 |
|
140 | 0 | return index; |
141 | 0 | } |
142 | | |
143 | | nsIFrame* |
144 | | nsDeckFrame::GetSelectedBox() |
145 | 0 | { |
146 | 0 | return (mIndex >= 0) ? mFrames.FrameAt(mIndex) : nullptr; |
147 | 0 | } |
148 | | |
149 | | void |
150 | | nsDeckFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
151 | | const nsDisplayListSet& aLists) |
152 | 0 | { |
153 | 0 | // if a tab is hidden all its children are too. |
154 | 0 | if (!StyleVisibility()->mVisible) |
155 | 0 | return; |
156 | 0 | |
157 | 0 | nsBoxFrame::BuildDisplayList(aBuilder, aLists); |
158 | 0 | } |
159 | | |
160 | | void |
161 | | nsDeckFrame::RemoveFrame(ChildListID aListID, |
162 | | nsIFrame* aOldFrame) |
163 | 0 | { |
164 | 0 | nsIFrame* currentFrame = GetSelectedBox(); |
165 | 0 | if (currentFrame && |
166 | 0 | aOldFrame && |
167 | 0 | currentFrame != aOldFrame) { |
168 | 0 | // If the frame we're removing is at an index that's less |
169 | 0 | // than mIndex, that means we're going to be shifting indexes |
170 | 0 | // by 1. |
171 | 0 | // |
172 | 0 | // We attempt to keep the same child displayed by automatically |
173 | 0 | // updating our internal notion of the current index. |
174 | 0 | int32_t removedIndex = mFrames.IndexOf(aOldFrame); |
175 | 0 | MOZ_ASSERT(removedIndex >= 0, |
176 | 0 | "A deck child was removed that was not in mFrames."); |
177 | 0 | if (removedIndex < mIndex) { |
178 | 0 | mIndex--; |
179 | 0 | // This is going to cause us to handle the index change in IndexedChanged, |
180 | 0 | // but since the new index will match mIndex, it's essentially a noop. |
181 | 0 | nsContentUtils::AddScriptRunner(new nsSetAttrRunnable( |
182 | 0 | mContent->AsElement(), nsGkAtoms::selectedIndex, mIndex)); |
183 | 0 | } |
184 | 0 | } |
185 | 0 | nsBoxFrame::RemoveFrame(aListID, aOldFrame); |
186 | 0 | } |
187 | | |
188 | | void |
189 | | nsDeckFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, |
190 | | const nsDisplayListSet& aLists) |
191 | 0 | { |
192 | 0 | // only paint the selected box |
193 | 0 | nsIFrame* box = GetSelectedBox(); |
194 | 0 | if (!box) |
195 | 0 | return; |
196 | 0 | |
197 | 0 | // Putting the child in the background list. This is a little weird but |
198 | 0 | // it matches what we were doing before. |
199 | 0 | nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds()); |
200 | 0 | BuildDisplayListForChild(aBuilder, box, set); |
201 | 0 | } |
202 | | |
203 | | NS_IMETHODIMP |
204 | | nsDeckFrame::DoXULLayout(nsBoxLayoutState& aState) |
205 | 0 | { |
206 | 0 | // Make sure we tweak the state so it does not resize our children. |
207 | 0 | // We will do that. |
208 | 0 | uint32_t oldFlags = aState.LayoutFlags(); |
209 | 0 | aState.SetLayoutFlags(NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_VISIBILITY); |
210 | 0 |
|
211 | 0 | // do a normal layout |
212 | 0 | nsresult rv = nsBoxFrame::DoXULLayout(aState); |
213 | 0 |
|
214 | 0 | // run though each child. Hide all but the selected one |
215 | 0 | nsIFrame* box = nsBox::GetChildXULBox(this); |
216 | 0 |
|
217 | 0 | nscoord count = 0; |
218 | 0 | while (box) |
219 | 0 | { |
220 | 0 | // make collapsed children not show up |
221 | 0 | if (count != mIndex) |
222 | 0 | HideBox(box); |
223 | 0 |
|
224 | 0 | box = GetNextXULBox(box); |
225 | 0 | count++; |
226 | 0 | } |
227 | 0 |
|
228 | 0 | aState.SetLayoutFlags(oldFlags); |
229 | 0 |
|
230 | 0 | return rv; |
231 | 0 | } |
232 | | |