layout/xul/nsScrollBoxObject.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:ac6c027cd5fd
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

mercurial