|
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 #include "nsCOMPtr.h" |
|
7 #include "nsIScrollBoxObject.h" |
|
8 #include "nsBoxObject.h" |
|
9 #include "nsIPresShell.h" |
|
10 #include "nsIContent.h" |
|
11 #include "nsIDOMElement.h" |
|
12 #include "nsPresContext.h" |
|
13 #include "nsIFrame.h" |
|
14 #include "nsIScrollableFrame.h" |
|
15 |
|
16 using namespace mozilla; |
|
17 |
|
18 class nsScrollBoxObject : public nsIScrollBoxObject, public nsBoxObject |
|
19 { |
|
20 public: |
|
21 NS_DECL_ISUPPORTS_INHERITED |
|
22 NS_DECL_NSISCROLLBOXOBJECT |
|
23 |
|
24 nsScrollBoxObject(); |
|
25 virtual ~nsScrollBoxObject(); |
|
26 |
|
27 virtual nsIScrollableFrame* GetScrollFrame() { |
|
28 return do_QueryFrame(GetFrame(false)); |
|
29 } |
|
30 |
|
31 /* additional members */ |
|
32 }; |
|
33 |
|
34 /* Implementation file */ |
|
35 |
|
36 NS_INTERFACE_MAP_BEGIN(nsScrollBoxObject) |
|
37 NS_INTERFACE_MAP_ENTRY(nsIScrollBoxObject) |
|
38 NS_INTERFACE_MAP_END_INHERITING(nsBoxObject) |
|
39 |
|
40 NS_IMPL_ADDREF_INHERITED(nsScrollBoxObject, nsBoxObject) |
|
41 NS_IMPL_RELEASE_INHERITED(nsScrollBoxObject, nsBoxObject) |
|
42 |
|
43 nsScrollBoxObject::nsScrollBoxObject() |
|
44 { |
|
45 /* member initializers and constructor code */ |
|
46 } |
|
47 |
|
48 nsScrollBoxObject::~nsScrollBoxObject() |
|
49 { |
|
50 /* destructor code */ |
|
51 } |
|
52 |
|
53 /* void scrollTo (in long x, in long y); */ |
|
54 NS_IMETHODIMP nsScrollBoxObject::ScrollTo(int32_t x, int32_t y) |
|
55 { |
|
56 nsIScrollableFrame* sf = GetScrollFrame(); |
|
57 if (!sf) |
|
58 return NS_ERROR_FAILURE; |
|
59 sf->ScrollToCSSPixels(CSSIntPoint(x, y)); |
|
60 return NS_OK; |
|
61 } |
|
62 |
|
63 /* void scrollBy (in long dx, in long dy); */ |
|
64 NS_IMETHODIMP nsScrollBoxObject::ScrollBy(int32_t dx, int32_t dy) |
|
65 { |
|
66 int32_t x, y; |
|
67 nsresult rv = GetPosition(&x, &y); |
|
68 if (NS_FAILED(rv)) |
|
69 return rv; |
|
70 |
|
71 return ScrollTo(x + dx, y + dy); |
|
72 } |
|
73 |
|
74 /* void scrollByLine (in long dlines); */ |
|
75 NS_IMETHODIMP nsScrollBoxObject::ScrollByLine(int32_t dlines) |
|
76 { |
|
77 nsIScrollableFrame* sf = GetScrollFrame(); |
|
78 if (!sf) |
|
79 return NS_ERROR_FAILURE; |
|
80 |
|
81 sf->ScrollBy(nsIntPoint(0, dlines), nsIScrollableFrame::LINES, |
|
82 nsIScrollableFrame::SMOOTH); |
|
83 return NS_OK; |
|
84 } |
|
85 |
|
86 // XUL <scrollbox> elements have a single box child element. |
|
87 // Get a pointer to that box. |
|
88 // Note that now that the <scrollbox> is just a regular box |
|
89 // with 'overflow:hidden', the boxobject's frame is an nsXULScrollFrame, |
|
90 // the <scrollbox>'s box frame is the scrollframe's "scrolled frame", and |
|
91 // the <scrollbox>'s child box is a child of that. |
|
92 static nsIFrame* GetScrolledBox(nsBoxObject* aScrollBox) { |
|
93 nsIFrame* frame = aScrollBox->GetFrame(false); |
|
94 if (!frame) |
|
95 return nullptr; |
|
96 nsIScrollableFrame* scrollFrame = do_QueryFrame(frame); |
|
97 if (!scrollFrame) { |
|
98 NS_WARNING("nsIScrollBoxObject attached to something that's not a scroll frame!"); |
|
99 return nullptr; |
|
100 } |
|
101 nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame(); |
|
102 if (!scrolledFrame) |
|
103 return nullptr; |
|
104 return scrolledFrame->GetChildBox(); |
|
105 } |
|
106 |
|
107 /* void scrollByIndex (in long dindexes); */ |
|
108 NS_IMETHODIMP nsScrollBoxObject::ScrollByIndex(int32_t dindexes) |
|
109 { |
|
110 nsIScrollableFrame* sf = GetScrollFrame(); |
|
111 if (!sf) |
|
112 return NS_ERROR_FAILURE; |
|
113 nsIFrame* scrolledBox = GetScrolledBox(this); |
|
114 if (!scrolledBox) |
|
115 return NS_ERROR_FAILURE; |
|
116 |
|
117 nsRect rect; |
|
118 |
|
119 // now get the scrolled boxes first child. |
|
120 nsIFrame* child = scrolledBox->GetChildBox(); |
|
121 |
|
122 bool horiz = scrolledBox->IsHorizontal(); |
|
123 nsPoint cp = sf->GetScrollPosition(); |
|
124 nscoord diff = 0; |
|
125 int32_t curIndex = 0; |
|
126 bool isLTR = scrolledBox->IsNormalDirection(); |
|
127 |
|
128 int32_t frameWidth = 0; |
|
129 if (!isLTR && horiz) { |
|
130 GetWidth(&frameWidth); |
|
131 nsCOMPtr<nsIPresShell> shell = GetPresShell(false); |
|
132 if (!shell) { |
|
133 return NS_ERROR_UNEXPECTED; |
|
134 } |
|
135 frameWidth = nsPresContext::CSSPixelsToAppUnits(frameWidth); |
|
136 } |
|
137 |
|
138 // first find out what index we are currently at |
|
139 while(child) { |
|
140 rect = child->GetRect(); |
|
141 if (horiz) { |
|
142 // In the left-to-right case we break from the loop when the center of |
|
143 // the current child rect is greater than the scrolled position of |
|
144 // the left edge of the scrollbox |
|
145 // In the right-to-left case we break when the center of the current |
|
146 // child rect is less than the scrolled position of the right edge of |
|
147 // the scrollbox. |
|
148 diff = rect.x + rect.width/2; // use the center, to avoid rounding errors |
|
149 if ((isLTR && diff > cp.x) || |
|
150 (!isLTR && diff < cp.x + frameWidth)) { |
|
151 break; |
|
152 } |
|
153 } else { |
|
154 diff = rect.y + rect.height/2;// use the center, to avoid rounding errors |
|
155 if (diff > cp.y) { |
|
156 break; |
|
157 } |
|
158 } |
|
159 child = child->GetNextBox(); |
|
160 curIndex++; |
|
161 } |
|
162 |
|
163 int32_t count = 0; |
|
164 |
|
165 if (dindexes == 0) |
|
166 return NS_OK; |
|
167 |
|
168 if (dindexes > 0) { |
|
169 while(child) { |
|
170 child = child->GetNextBox(); |
|
171 if (child) |
|
172 rect = child->GetRect(); |
|
173 count++; |
|
174 if (count >= dindexes) |
|
175 break; |
|
176 } |
|
177 |
|
178 } else if (dindexes < 0) { |
|
179 child = scrolledBox->GetChildBox(); |
|
180 while(child) { |
|
181 rect = child->GetRect(); |
|
182 if (count >= curIndex + dindexes) |
|
183 break; |
|
184 |
|
185 count++; |
|
186 child = child->GetNextBox(); |
|
187 |
|
188 } |
|
189 } |
|
190 |
|
191 nscoord csspixel = nsPresContext::CSSPixelsToAppUnits(1); |
|
192 if (horiz) { |
|
193 // In the left-to-right case we scroll so that the left edge of the |
|
194 // selected child is scrolled to the left edge of the scrollbox. |
|
195 // In the right-to-left case we scroll so that the right edge of the |
|
196 // selected child is scrolled to the right edge of the scrollbox. |
|
197 |
|
198 nsPoint pt(isLTR ? rect.x : rect.x + rect.width - frameWidth, |
|
199 cp.y); |
|
200 |
|
201 // Use a destination range that ensures the left edge (or right edge, |
|
202 // for RTL) will indeed be visible. Also ensure that the top edge |
|
203 // is visible. |
|
204 nsRect range(pt.x, pt.y, csspixel, 0); |
|
205 if (isLTR) { |
|
206 range.x -= csspixel; |
|
207 } |
|
208 sf->ScrollTo(pt, nsIScrollableFrame::INSTANT, &range); |
|
209 } else { |
|
210 // Use a destination range that ensures the top edge will be visible. |
|
211 nsRect range(cp.x, rect.y - csspixel, 0, csspixel); |
|
212 sf->ScrollTo(nsPoint(cp.x, rect.y), nsIScrollableFrame::INSTANT, &range); |
|
213 } |
|
214 |
|
215 return NS_OK; |
|
216 } |
|
217 |
|
218 /* void scrollToLine (in long line); */ |
|
219 NS_IMETHODIMP nsScrollBoxObject::ScrollToLine(int32_t line) |
|
220 { |
|
221 nsIScrollableFrame* sf = GetScrollFrame(); |
|
222 if (!sf) |
|
223 return NS_ERROR_FAILURE; |
|
224 |
|
225 nscoord y = sf->GetLineScrollAmount().height * line; |
|
226 nsRect range(0, y - nsPresContext::CSSPixelsToAppUnits(1), |
|
227 0, nsPresContext::CSSPixelsToAppUnits(1)); |
|
228 sf->ScrollTo(nsPoint(0, y), nsIScrollableFrame::INSTANT, &range); |
|
229 return NS_OK; |
|
230 } |
|
231 |
|
232 /* void scrollToElement (in nsIDOMElement child); */ |
|
233 NS_IMETHODIMP nsScrollBoxObject::ScrollToElement(nsIDOMElement *child) |
|
234 { |
|
235 NS_ENSURE_ARG_POINTER(child); |
|
236 |
|
237 nsCOMPtr<nsIPresShell> shell = GetPresShell(false); |
|
238 if (!shell) { |
|
239 return NS_ERROR_UNEXPECTED; |
|
240 } |
|
241 |
|
242 nsCOMPtr<nsIContent> content = do_QueryInterface(child); |
|
243 shell->ScrollContentIntoView(content, |
|
244 nsIPresShell::ScrollAxis( |
|
245 nsIPresShell::SCROLL_TOP, |
|
246 nsIPresShell::SCROLL_ALWAYS), |
|
247 nsIPresShell::ScrollAxis( |
|
248 nsIPresShell::SCROLL_LEFT, |
|
249 nsIPresShell::SCROLL_ALWAYS), |
|
250 nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY | |
|
251 nsIPresShell::SCROLL_OVERFLOW_HIDDEN); |
|
252 return NS_OK; |
|
253 } |
|
254 |
|
255 /* void scrollToIndex (in long index); */ |
|
256 NS_IMETHODIMP nsScrollBoxObject::ScrollToIndex(int32_t index) |
|
257 { |
|
258 return NS_ERROR_NOT_IMPLEMENTED; |
|
259 } |
|
260 |
|
261 /* void getPosition (out long x, out long y); */ |
|
262 NS_IMETHODIMP nsScrollBoxObject::GetPosition(int32_t *x, int32_t *y) |
|
263 { |
|
264 nsIScrollableFrame* sf = GetScrollFrame(); |
|
265 if (!sf) |
|
266 return NS_ERROR_FAILURE; |
|
267 |
|
268 CSSIntPoint pt = sf->GetScrollPositionCSSPixels(); |
|
269 *x = pt.x; |
|
270 *y = pt.y; |
|
271 |
|
272 return NS_OK; |
|
273 } |
|
274 |
|
275 /* void getScrolledSize (out long width, out long height); */ |
|
276 NS_IMETHODIMP nsScrollBoxObject::GetScrolledSize(int32_t *width, int32_t *height) |
|
277 { |
|
278 nsIFrame* scrolledBox = GetScrolledBox(this); |
|
279 if (!scrolledBox) |
|
280 return NS_ERROR_FAILURE; |
|
281 |
|
282 nsRect scrollRect = scrolledBox->GetRect(); |
|
283 |
|
284 *width = nsPresContext::AppUnitsToIntCSSPixels(scrollRect.width); |
|
285 *height = nsPresContext::AppUnitsToIntCSSPixels(scrollRect.height); |
|
286 |
|
287 return NS_OK; |
|
288 } |
|
289 |
|
290 /* void ensureElementIsVisible (in nsIDOMElement child); */ |
|
291 NS_IMETHODIMP nsScrollBoxObject::EnsureElementIsVisible(nsIDOMElement *child) |
|
292 { |
|
293 NS_ENSURE_ARG_POINTER(child); |
|
294 |
|
295 nsCOMPtr<nsIPresShell> shell = GetPresShell(false); |
|
296 if (!shell) { |
|
297 return NS_ERROR_UNEXPECTED; |
|
298 } |
|
299 |
|
300 nsCOMPtr<nsIContent> content = do_QueryInterface(child); |
|
301 shell->ScrollContentIntoView(content, |
|
302 nsIPresShell::ScrollAxis(), |
|
303 nsIPresShell::ScrollAxis(), |
|
304 nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY | |
|
305 nsIPresShell::SCROLL_OVERFLOW_HIDDEN); |
|
306 return NS_OK; |
|
307 } |
|
308 |
|
309 /* void ensureIndexIsVisible (in long index); */ |
|
310 NS_IMETHODIMP nsScrollBoxObject::EnsureIndexIsVisible(int32_t index) |
|
311 { |
|
312 return NS_ERROR_NOT_IMPLEMENTED; |
|
313 } |
|
314 |
|
315 /* void ensureLineIsVisible (in long line); */ |
|
316 NS_IMETHODIMP nsScrollBoxObject::EnsureLineIsVisible(int32_t line) |
|
317 { |
|
318 return NS_ERROR_NOT_IMPLEMENTED; |
|
319 } |
|
320 |
|
321 nsresult |
|
322 NS_NewScrollBoxObject(nsIBoxObject** aResult) |
|
323 { |
|
324 *aResult = new nsScrollBoxObject; |
|
325 if (!*aResult) |
|
326 return NS_ERROR_OUT_OF_MEMORY; |
|
327 NS_ADDREF(*aResult); |
|
328 return NS_OK; |
|
329 } |
|
330 |