|
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 "mozilla/AsyncEventDispatcher.h" |
|
7 #include "mozilla/ContentEvents.h" |
|
8 #include "mozilla/DebugOnly.h" |
|
9 #include "mozilla/EventDispatcher.h" |
|
10 #include "mozilla/MathAlgorithms.h" |
|
11 #include "mozilla/MouseEvents.h" |
|
12 #include "mozilla/Likely.h" |
|
13 |
|
14 #include "nsCOMPtr.h" |
|
15 #include "nsPresContext.h" |
|
16 #include "nsNameSpaceManager.h" |
|
17 |
|
18 #include "nsTreeBodyFrame.h" |
|
19 #include "nsTreeSelection.h" |
|
20 #include "nsTreeImageListener.h" |
|
21 |
|
22 #include "nsGkAtoms.h" |
|
23 #include "nsCSSAnonBoxes.h" |
|
24 |
|
25 #include "nsIContent.h" |
|
26 #include "nsStyleContext.h" |
|
27 #include "nsIBoxObject.h" |
|
28 #include "nsIDOMCustomEvent.h" |
|
29 #include "nsIDOMMouseEvent.h" |
|
30 #include "nsIDOMElement.h" |
|
31 #include "nsIDOMNodeList.h" |
|
32 #include "nsIDOMDocument.h" |
|
33 #include "nsIDOMXULElement.h" |
|
34 #include "nsIDocument.h" |
|
35 #include "mozilla/css/StyleRule.h" |
|
36 #include "nsCSSRendering.h" |
|
37 #include "nsIXULTemplateBuilder.h" |
|
38 #include "nsXPIDLString.h" |
|
39 #include "nsContainerFrame.h" |
|
40 #include "nsView.h" |
|
41 #include "nsViewManager.h" |
|
42 #include "nsWidgetsCID.h" |
|
43 #include "nsBoxFrame.h" |
|
44 #include "nsBoxObject.h" |
|
45 #include "nsIURL.h" |
|
46 #include "nsNetUtil.h" |
|
47 #include "nsBoxLayoutState.h" |
|
48 #include "nsTreeContentView.h" |
|
49 #include "nsTreeUtils.h" |
|
50 #include "nsITheme.h" |
|
51 #include "imgIRequest.h" |
|
52 #include "imgIContainer.h" |
|
53 #include "imgILoader.h" |
|
54 #include "nsINodeInfo.h" |
|
55 #include "nsContentUtils.h" |
|
56 #include "nsLayoutUtils.h" |
|
57 #include "nsIScrollableFrame.h" |
|
58 #include "nsDisplayList.h" |
|
59 #include "nsTreeBoxObject.h" |
|
60 #include "nsRenderingContext.h" |
|
61 #include "nsIScriptableRegion.h" |
|
62 #include <algorithm> |
|
63 #include "ScrollbarActivity.h" |
|
64 |
|
65 #ifdef ACCESSIBILITY |
|
66 #include "nsAccessibilityService.h" |
|
67 #include "nsIWritablePropertyBag2.h" |
|
68 #endif |
|
69 #include "nsBidiUtils.h" |
|
70 |
|
71 using namespace mozilla; |
|
72 using namespace mozilla::layout; |
|
73 |
|
74 // Enumeration function that cancels all the image requests in our cache |
|
75 static PLDHashOperator |
|
76 CancelImageRequest(const nsAString& aKey, |
|
77 nsTreeImageCacheEntry aEntry, void* aData) |
|
78 { |
|
79 |
|
80 // If our imgIRequest object was registered with the refresh driver, |
|
81 // then we need to deregister it. |
|
82 nsTreeBodyFrame* frame = static_cast<nsTreeBodyFrame*>(aData); |
|
83 |
|
84 nsLayoutUtils::DeregisterImageRequest(frame->PresContext(), aEntry.request, |
|
85 nullptr); |
|
86 |
|
87 aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED); |
|
88 return PL_DHASH_NEXT; |
|
89 } |
|
90 |
|
91 // |
|
92 // NS_NewTreeFrame |
|
93 // |
|
94 // Creates a new tree frame |
|
95 // |
|
96 nsIFrame* |
|
97 NS_NewTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
98 { |
|
99 return new (aPresShell) nsTreeBodyFrame(aPresShell, aContext); |
|
100 } |
|
101 |
|
102 NS_IMPL_FRAMEARENA_HELPERS(nsTreeBodyFrame) |
|
103 |
|
104 NS_QUERYFRAME_HEAD(nsTreeBodyFrame) |
|
105 NS_QUERYFRAME_ENTRY(nsIScrollbarMediator) |
|
106 NS_QUERYFRAME_ENTRY(nsIScrollbarOwner) |
|
107 NS_QUERYFRAME_ENTRY(nsTreeBodyFrame) |
|
108 NS_QUERYFRAME_TAIL_INHERITING(nsLeafBoxFrame) |
|
109 |
|
110 // Constructor |
|
111 nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
112 :nsLeafBoxFrame(aPresShell, aContext), |
|
113 mSlots(nullptr), |
|
114 mImageCache(16), |
|
115 mTopRowIndex(0), |
|
116 mPageLength(0), |
|
117 mHorzPosition(0), |
|
118 mOriginalHorzWidth(-1), |
|
119 mHorzWidth(0), |
|
120 mAdjustWidth(0), |
|
121 mRowHeight(0), |
|
122 mIndentation(0), |
|
123 mStringWidth(-1), |
|
124 mUpdateBatchNest(0), |
|
125 mRowCount(0), |
|
126 mMouseOverRow(-1), |
|
127 mFocused(false), |
|
128 mHasFixedRowCount(false), |
|
129 mVerticalOverflow(false), |
|
130 mHorizontalOverflow(false), |
|
131 mReflowCallbackPosted(false), |
|
132 mCheckingOverflow(false) |
|
133 { |
|
134 mColumns = new nsTreeColumns(this); |
|
135 } |
|
136 |
|
137 // Destructor |
|
138 nsTreeBodyFrame::~nsTreeBodyFrame() |
|
139 { |
|
140 mImageCache.EnumerateRead(CancelImageRequest, this); |
|
141 DetachImageListeners(); |
|
142 delete mSlots; |
|
143 } |
|
144 |
|
145 static void |
|
146 GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin) |
|
147 { |
|
148 aMargin.SizeTo(0, 0, 0, 0); |
|
149 if (!aContext->StylePadding()->GetPadding(aMargin)) { |
|
150 NS_NOTYETIMPLEMENTED("percentage padding"); |
|
151 } |
|
152 aMargin += aContext->StyleBorder()->GetComputedBorder(); |
|
153 } |
|
154 |
|
155 static void |
|
156 AdjustForBorderPadding(nsStyleContext* aContext, nsRect& aRect) |
|
157 { |
|
158 nsMargin borderPadding(0, 0, 0, 0); |
|
159 GetBorderPadding(aContext, borderPadding); |
|
160 aRect.Deflate(borderPadding); |
|
161 } |
|
162 |
|
163 void |
|
164 nsTreeBodyFrame::Init(nsIContent* aContent, |
|
165 nsIFrame* aParent, |
|
166 nsIFrame* aPrevInFlow) |
|
167 { |
|
168 nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow); |
|
169 |
|
170 mIndentation = GetIndentation(); |
|
171 mRowHeight = GetRowHeight(); |
|
172 |
|
173 EnsureBoxObject(); |
|
174 |
|
175 if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) { |
|
176 mScrollbarActivity = new ScrollbarActivity( |
|
177 static_cast<nsIScrollbarOwner*>(this)); |
|
178 } |
|
179 } |
|
180 |
|
181 nsSize |
|
182 nsTreeBodyFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState) |
|
183 { |
|
184 EnsureView(); |
|
185 |
|
186 nsIContent* baseElement = GetBaseElement(); |
|
187 |
|
188 nsSize min(0,0); |
|
189 int32_t desiredRows; |
|
190 if (MOZ_UNLIKELY(!baseElement)) { |
|
191 desiredRows = 0; |
|
192 } |
|
193 else if (baseElement->Tag() == nsGkAtoms::select && |
|
194 baseElement->IsHTML()) { |
|
195 min.width = CalcMaxRowWidth(); |
|
196 nsAutoString size; |
|
197 baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::size, size); |
|
198 if (!size.IsEmpty()) { |
|
199 nsresult err; |
|
200 desiredRows = size.ToInteger(&err); |
|
201 mHasFixedRowCount = true; |
|
202 mPageLength = desiredRows; |
|
203 } |
|
204 else { |
|
205 desiredRows = 1; |
|
206 } |
|
207 } |
|
208 else { |
|
209 // tree |
|
210 nsAutoString rows; |
|
211 baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows); |
|
212 if (!rows.IsEmpty()) { |
|
213 nsresult err; |
|
214 desiredRows = rows.ToInteger(&err); |
|
215 mPageLength = desiredRows; |
|
216 } |
|
217 else { |
|
218 desiredRows = 0; |
|
219 } |
|
220 } |
|
221 |
|
222 min.height = mRowHeight * desiredRows; |
|
223 |
|
224 AddBorderAndPadding(min); |
|
225 bool widthSet, heightSet; |
|
226 nsIFrame::AddCSSMinSize(aBoxLayoutState, this, min, widthSet, heightSet); |
|
227 |
|
228 return min; |
|
229 } |
|
230 |
|
231 nscoord |
|
232 nsTreeBodyFrame::CalcMaxRowWidth() |
|
233 { |
|
234 if (mStringWidth != -1) |
|
235 return mStringWidth; |
|
236 |
|
237 if (!mView) |
|
238 return 0; |
|
239 |
|
240 nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); |
|
241 nsMargin rowMargin(0,0,0,0); |
|
242 GetBorderPadding(rowContext, rowMargin); |
|
243 |
|
244 nscoord rowWidth; |
|
245 nsTreeColumn* col; |
|
246 |
|
247 nsRefPtr<nsRenderingContext> rc = |
|
248 PresContext()->PresShell()->CreateReferenceRenderingContext(); |
|
249 |
|
250 for (int32_t row = 0; row < mRowCount; ++row) { |
|
251 rowWidth = 0; |
|
252 |
|
253 for (col = mColumns->GetFirstColumn(); col; col = col->GetNext()) { |
|
254 nscoord desiredWidth, currentWidth; |
|
255 nsresult rv = GetCellWidth(row, col, rc, desiredWidth, currentWidth); |
|
256 if (NS_FAILED(rv)) { |
|
257 NS_NOTREACHED("invalid column"); |
|
258 continue; |
|
259 } |
|
260 rowWidth += desiredWidth; |
|
261 } |
|
262 |
|
263 if (rowWidth > mStringWidth) |
|
264 mStringWidth = rowWidth; |
|
265 } |
|
266 |
|
267 mStringWidth += rowMargin.left + rowMargin.right; |
|
268 return mStringWidth; |
|
269 } |
|
270 |
|
271 void |
|
272 nsTreeBodyFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
273 { |
|
274 if (mScrollbarActivity) { |
|
275 mScrollbarActivity->Destroy(); |
|
276 mScrollbarActivity = nullptr; |
|
277 } |
|
278 |
|
279 mScrollEvent.Revoke(); |
|
280 // Make sure we cancel any posted callbacks. |
|
281 if (mReflowCallbackPosted) { |
|
282 PresContext()->PresShell()->CancelReflowCallback(this); |
|
283 mReflowCallbackPosted = false; |
|
284 } |
|
285 |
|
286 if (mColumns) |
|
287 mColumns->SetTree(nullptr); |
|
288 |
|
289 // Save off our info into the box object. |
|
290 nsCOMPtr<nsPIBoxObject> box(do_QueryInterface(mTreeBoxObject)); |
|
291 if (box) { |
|
292 if (mTopRowIndex > 0) { |
|
293 nsAutoString topRowStr; topRowStr.AssignLiteral("topRow"); |
|
294 nsAutoString topRow; |
|
295 topRow.AppendInt(mTopRowIndex); |
|
296 box->SetProperty(topRowStr.get(), topRow.get()); |
|
297 } |
|
298 |
|
299 // Always null out the cached tree body frame. |
|
300 box->ClearCachedValues(); |
|
301 |
|
302 mTreeBoxObject = nullptr; // Drop our ref here. |
|
303 } |
|
304 |
|
305 if (mView) { |
|
306 nsCOMPtr<nsITreeSelection> sel; |
|
307 mView->GetSelection(getter_AddRefs(sel)); |
|
308 if (sel) |
|
309 sel->SetTree(nullptr); |
|
310 mView->SetTree(nullptr); |
|
311 mView = nullptr; |
|
312 } |
|
313 |
|
314 nsLeafBoxFrame::DestroyFrom(aDestructRoot); |
|
315 } |
|
316 |
|
317 void |
|
318 nsTreeBodyFrame::EnsureBoxObject() |
|
319 { |
|
320 if (!mTreeBoxObject) { |
|
321 nsIContent* parent = GetBaseElement(); |
|
322 if (parent) { |
|
323 nsIDocument* nsDoc = parent->GetDocument(); |
|
324 if (!nsDoc) // there may be no document, if we're called from Destroy() |
|
325 return; |
|
326 ErrorResult ignored; |
|
327 nsCOMPtr<nsIBoxObject> box = |
|
328 nsDoc->GetBoxObjectFor(parent->AsElement(), ignored); |
|
329 // Ensure that we got a native box object. |
|
330 nsCOMPtr<nsPIBoxObject> pBox = do_QueryInterface(box); |
|
331 if (pBox) { |
|
332 nsCOMPtr<nsITreeBoxObject> realTreeBoxObject = do_QueryInterface(pBox); |
|
333 if (realTreeBoxObject) { |
|
334 nsTreeBodyFrame* innerTreeBoxObject = |
|
335 static_cast<nsTreeBoxObject*>(realTreeBoxObject.get()) |
|
336 ->GetCachedTreeBody(); |
|
337 ENSURE_TRUE(!innerTreeBoxObject || innerTreeBoxObject == this); |
|
338 mTreeBoxObject = realTreeBoxObject; |
|
339 } |
|
340 } |
|
341 } |
|
342 } |
|
343 } |
|
344 |
|
345 void |
|
346 nsTreeBodyFrame::EnsureView() |
|
347 { |
|
348 if (!mView) { |
|
349 if (PresContext()->PresShell()->IsReflowLocked()) { |
|
350 if (!mReflowCallbackPosted) { |
|
351 mReflowCallbackPosted = true; |
|
352 PresContext()->PresShell()->PostReflowCallback(this); |
|
353 } |
|
354 return; |
|
355 } |
|
356 nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mTreeBoxObject); |
|
357 if (box) { |
|
358 nsWeakFrame weakFrame(this); |
|
359 nsCOMPtr<nsITreeView> treeView; |
|
360 mTreeBoxObject->GetView(getter_AddRefs(treeView)); |
|
361 if (treeView && weakFrame.IsAlive()) { |
|
362 nsXPIDLString rowStr; |
|
363 box->GetProperty(MOZ_UTF16("topRow"), |
|
364 getter_Copies(rowStr)); |
|
365 nsAutoString rowStr2(rowStr); |
|
366 nsresult error; |
|
367 int32_t rowIndex = rowStr2.ToInteger(&error); |
|
368 |
|
369 // Set our view. |
|
370 SetView(treeView); |
|
371 ENSURE_TRUE(weakFrame.IsAlive()); |
|
372 |
|
373 // Scroll to the given row. |
|
374 // XXX is this optimal if we haven't laid out yet? |
|
375 ScrollToRow(rowIndex); |
|
376 ENSURE_TRUE(weakFrame.IsAlive()); |
|
377 |
|
378 // Clear out the property info for the top row, but we always keep the |
|
379 // view current. |
|
380 box->RemoveProperty(MOZ_UTF16("topRow")); |
|
381 } |
|
382 } |
|
383 } |
|
384 } |
|
385 |
|
386 void |
|
387 nsTreeBodyFrame::ManageReflowCallback(const nsRect& aRect, nscoord aHorzWidth) |
|
388 { |
|
389 if (!mReflowCallbackPosted && |
|
390 (!aRect.IsEqualEdges(mRect) || mHorzWidth != aHorzWidth)) { |
|
391 PresContext()->PresShell()->PostReflowCallback(this); |
|
392 mReflowCallbackPosted = true; |
|
393 mOriginalHorzWidth = mHorzWidth; |
|
394 } |
|
395 else if (mReflowCallbackPosted && |
|
396 mHorzWidth != aHorzWidth && mOriginalHorzWidth == aHorzWidth) { |
|
397 PresContext()->PresShell()->CancelReflowCallback(this); |
|
398 mReflowCallbackPosted = false; |
|
399 mOriginalHorzWidth = -1; |
|
400 } |
|
401 } |
|
402 |
|
403 void |
|
404 nsTreeBodyFrame::SetBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect, |
|
405 bool aRemoveOverflowArea) |
|
406 { |
|
407 nscoord horzWidth = CalcHorzWidth(GetScrollParts()); |
|
408 ManageReflowCallback(aRect, horzWidth); |
|
409 mHorzWidth = horzWidth; |
|
410 |
|
411 nsLeafBoxFrame::SetBounds(aBoxLayoutState, aRect, aRemoveOverflowArea); |
|
412 } |
|
413 |
|
414 |
|
415 bool |
|
416 nsTreeBodyFrame::ReflowFinished() |
|
417 { |
|
418 if (!mView) { |
|
419 nsWeakFrame weakFrame(this); |
|
420 EnsureView(); |
|
421 NS_ENSURE_TRUE(weakFrame.IsAlive(), false); |
|
422 } |
|
423 if (mView) { |
|
424 CalcInnerBox(); |
|
425 ScrollParts parts = GetScrollParts(); |
|
426 mHorzWidth = CalcHorzWidth(parts); |
|
427 if (!mHasFixedRowCount) { |
|
428 mPageLength = mInnerBox.height / mRowHeight; |
|
429 } |
|
430 |
|
431 int32_t lastPageTopRow = std::max(0, mRowCount - mPageLength); |
|
432 if (mTopRowIndex > lastPageTopRow) |
|
433 ScrollToRowInternal(parts, lastPageTopRow); |
|
434 |
|
435 nsIContent *treeContent = GetBaseElement(); |
|
436 if (treeContent && |
|
437 treeContent->AttrValueIs(kNameSpaceID_None, |
|
438 nsGkAtoms::keepcurrentinview, |
|
439 nsGkAtoms::_true, eCaseMatters)) { |
|
440 // make sure that the current selected item is still |
|
441 // visible after the tree changes size. |
|
442 nsCOMPtr<nsITreeSelection> sel; |
|
443 mView->GetSelection(getter_AddRefs(sel)); |
|
444 if (sel) { |
|
445 int32_t currentIndex; |
|
446 sel->GetCurrentIndex(¤tIndex); |
|
447 if (currentIndex != -1) |
|
448 EnsureRowIsVisibleInternal(parts, currentIndex); |
|
449 } |
|
450 } |
|
451 |
|
452 if (!FullScrollbarsUpdate(false)) { |
|
453 return false; |
|
454 } |
|
455 } |
|
456 |
|
457 mReflowCallbackPosted = false; |
|
458 return false; |
|
459 } |
|
460 |
|
461 void |
|
462 nsTreeBodyFrame::ReflowCallbackCanceled() |
|
463 { |
|
464 mReflowCallbackPosted = false; |
|
465 } |
|
466 |
|
467 nsresult |
|
468 nsTreeBodyFrame::GetView(nsITreeView * *aView) |
|
469 { |
|
470 *aView = nullptr; |
|
471 nsWeakFrame weakFrame(this); |
|
472 EnsureView(); |
|
473 NS_ENSURE_STATE(weakFrame.IsAlive()); |
|
474 NS_IF_ADDREF(*aView = mView); |
|
475 return NS_OK; |
|
476 } |
|
477 |
|
478 nsresult |
|
479 nsTreeBodyFrame::SetView(nsITreeView * aView) |
|
480 { |
|
481 // First clear out the old view. |
|
482 if (mView) { |
|
483 nsCOMPtr<nsITreeSelection> sel; |
|
484 mView->GetSelection(getter_AddRefs(sel)); |
|
485 if (sel) |
|
486 sel->SetTree(nullptr); |
|
487 mView->SetTree(nullptr); |
|
488 |
|
489 // Only reset the top row index and delete the columns if we had an old non-null view. |
|
490 mTopRowIndex = 0; |
|
491 } |
|
492 |
|
493 // Tree, meet the view. |
|
494 mView = aView; |
|
495 |
|
496 // Changing the view causes us to refetch our data. This will |
|
497 // necessarily entail a full invalidation of the tree. |
|
498 Invalidate(); |
|
499 |
|
500 nsIContent *treeContent = GetBaseElement(); |
|
501 if (treeContent) { |
|
502 #ifdef ACCESSIBILITY |
|
503 nsAccessibilityService* accService = nsIPresShell::AccService(); |
|
504 if (accService) |
|
505 accService->TreeViewChanged(PresContext()->GetPresShell(), treeContent, mView); |
|
506 #endif |
|
507 FireDOMEvent(NS_LITERAL_STRING("TreeViewChanged"), treeContent); |
|
508 } |
|
509 |
|
510 if (mView) { |
|
511 // Give the view a new empty selection object to play with, but only if it |
|
512 // doesn't have one already. |
|
513 nsCOMPtr<nsITreeSelection> sel; |
|
514 mView->GetSelection(getter_AddRefs(sel)); |
|
515 if (sel) { |
|
516 sel->SetTree(mTreeBoxObject); |
|
517 } |
|
518 else { |
|
519 NS_NewTreeSelection(mTreeBoxObject, getter_AddRefs(sel)); |
|
520 mView->SetSelection(sel); |
|
521 } |
|
522 |
|
523 // View, meet the tree. |
|
524 nsWeakFrame weakFrame(this); |
|
525 mView->SetTree(mTreeBoxObject); |
|
526 NS_ENSURE_STATE(weakFrame.IsAlive()); |
|
527 mView->GetRowCount(&mRowCount); |
|
528 |
|
529 if (!PresContext()->PresShell()->IsReflowLocked()) { |
|
530 // The scrollbar will need to be updated. |
|
531 FullScrollbarsUpdate(false); |
|
532 } else if (!mReflowCallbackPosted) { |
|
533 mReflowCallbackPosted = true; |
|
534 PresContext()->PresShell()->PostReflowCallback(this); |
|
535 } |
|
536 } |
|
537 |
|
538 return NS_OK; |
|
539 } |
|
540 |
|
541 nsresult |
|
542 nsTreeBodyFrame::GetFocused(bool* aFocused) |
|
543 { |
|
544 *aFocused = mFocused; |
|
545 return NS_OK; |
|
546 } |
|
547 |
|
548 nsresult |
|
549 nsTreeBodyFrame::SetFocused(bool aFocused) |
|
550 { |
|
551 if (mFocused != aFocused) { |
|
552 mFocused = aFocused; |
|
553 if (mView) { |
|
554 nsCOMPtr<nsITreeSelection> sel; |
|
555 mView->GetSelection(getter_AddRefs(sel)); |
|
556 if (sel) |
|
557 sel->InvalidateSelection(); |
|
558 } |
|
559 } |
|
560 return NS_OK; |
|
561 } |
|
562 |
|
563 nsresult |
|
564 nsTreeBodyFrame::GetTreeBody(nsIDOMElement** aElement) |
|
565 { |
|
566 //NS_ASSERTION(mContent, "no content, see bug #104878"); |
|
567 if (!mContent) |
|
568 return NS_ERROR_NULL_POINTER; |
|
569 |
|
570 return mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aElement); |
|
571 } |
|
572 |
|
573 nsresult |
|
574 nsTreeBodyFrame::GetRowHeight(int32_t* _retval) |
|
575 { |
|
576 *_retval = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); |
|
577 return NS_OK; |
|
578 } |
|
579 |
|
580 nsresult |
|
581 nsTreeBodyFrame::GetRowWidth(int32_t *aRowWidth) |
|
582 { |
|
583 *aRowWidth = nsPresContext::AppUnitsToIntCSSPixels(CalcHorzWidth(GetScrollParts())); |
|
584 return NS_OK; |
|
585 } |
|
586 |
|
587 nsresult |
|
588 nsTreeBodyFrame::GetHorizontalPosition(int32_t *aHorizontalPosition) |
|
589 { |
|
590 *aHorizontalPosition = nsPresContext::AppUnitsToIntCSSPixels(mHorzPosition); |
|
591 return NS_OK; |
|
592 } |
|
593 |
|
594 nsresult |
|
595 nsTreeBodyFrame::GetSelectionRegion(nsIScriptableRegion **aRegion) |
|
596 { |
|
597 *aRegion = nullptr; |
|
598 |
|
599 nsCOMPtr<nsITreeSelection> selection; |
|
600 mView->GetSelection(getter_AddRefs(selection)); |
|
601 NS_ENSURE_TRUE(selection, NS_OK); |
|
602 |
|
603 nsCOMPtr<nsIScriptableRegion> region = do_CreateInstance("@mozilla.org/gfx/region;1"); |
|
604 NS_ENSURE_TRUE(region, NS_ERROR_FAILURE); |
|
605 region->Init(); |
|
606 |
|
607 nsRefPtr<nsPresContext> presContext = PresContext(); |
|
608 nsIntRect rect = mRect.ToOutsidePixels(presContext->AppUnitsPerCSSPixel()); |
|
609 |
|
610 nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame(); |
|
611 nsPoint origin = GetOffsetTo(rootFrame); |
|
612 |
|
613 // iterate through the visible rows and add the selected ones to the |
|
614 // drag region |
|
615 int32_t x = nsPresContext::AppUnitsToIntCSSPixels(origin.x); |
|
616 int32_t y = nsPresContext::AppUnitsToIntCSSPixels(origin.y); |
|
617 int32_t top = y; |
|
618 int32_t end = LastVisibleRow(); |
|
619 int32_t rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); |
|
620 for (int32_t i = mTopRowIndex; i <= end; i++) { |
|
621 bool isSelected; |
|
622 selection->IsSelected(i, &isSelected); |
|
623 if (isSelected) |
|
624 region->UnionRect(x, y, rect.width, rowHeight); |
|
625 y += rowHeight; |
|
626 } |
|
627 |
|
628 // clip to the tree boundary in case one row extends past it |
|
629 region->IntersectRect(x, top, rect.width, rect.height); |
|
630 |
|
631 NS_ADDREF(*aRegion = region); |
|
632 return NS_OK; |
|
633 } |
|
634 |
|
635 nsresult |
|
636 nsTreeBodyFrame::Invalidate() |
|
637 { |
|
638 if (mUpdateBatchNest) |
|
639 return NS_OK; |
|
640 |
|
641 InvalidateFrame(); |
|
642 |
|
643 return NS_OK; |
|
644 } |
|
645 |
|
646 nsresult |
|
647 nsTreeBodyFrame::InvalidateColumn(nsITreeColumn* aCol) |
|
648 { |
|
649 if (mUpdateBatchNest) |
|
650 return NS_OK; |
|
651 |
|
652 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol); |
|
653 if (!col) |
|
654 return NS_ERROR_INVALID_ARG; |
|
655 |
|
656 #ifdef ACCESSIBILITY |
|
657 if (nsIPresShell::IsAccessibilityActive()) |
|
658 FireInvalidateEvent(-1, -1, aCol, aCol); |
|
659 #endif |
|
660 |
|
661 nsRect columnRect; |
|
662 nsresult rv = col->GetRect(this, mInnerBox.y, mInnerBox.height, &columnRect); |
|
663 NS_ENSURE_SUCCESS(rv, rv); |
|
664 |
|
665 // When false then column is out of view |
|
666 if (OffsetForHorzScroll(columnRect, true)) |
|
667 InvalidateFrameWithRect(columnRect); |
|
668 |
|
669 return NS_OK; |
|
670 } |
|
671 |
|
672 nsresult |
|
673 nsTreeBodyFrame::InvalidateRow(int32_t aIndex) |
|
674 { |
|
675 if (mUpdateBatchNest) |
|
676 return NS_OK; |
|
677 |
|
678 #ifdef ACCESSIBILITY |
|
679 if (nsIPresShell::IsAccessibilityActive()) |
|
680 FireInvalidateEvent(aIndex, aIndex, nullptr, nullptr); |
|
681 #endif |
|
682 |
|
683 aIndex -= mTopRowIndex; |
|
684 if (aIndex < 0 || aIndex > mPageLength) |
|
685 return NS_OK; |
|
686 |
|
687 nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*aIndex, mInnerBox.width, mRowHeight); |
|
688 InvalidateFrameWithRect(rowRect); |
|
689 |
|
690 return NS_OK; |
|
691 } |
|
692 |
|
693 nsresult |
|
694 nsTreeBodyFrame::InvalidateCell(int32_t aIndex, nsITreeColumn* aCol) |
|
695 { |
|
696 if (mUpdateBatchNest) |
|
697 return NS_OK; |
|
698 |
|
699 #ifdef ACCESSIBILITY |
|
700 if (nsIPresShell::IsAccessibilityActive()) |
|
701 FireInvalidateEvent(aIndex, aIndex, aCol, aCol); |
|
702 #endif |
|
703 |
|
704 aIndex -= mTopRowIndex; |
|
705 if (aIndex < 0 || aIndex > mPageLength) |
|
706 return NS_OK; |
|
707 |
|
708 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol); |
|
709 if (!col) |
|
710 return NS_ERROR_INVALID_ARG; |
|
711 |
|
712 nsRect cellRect; |
|
713 nsresult rv = col->GetRect(this, mInnerBox.y+mRowHeight*aIndex, mRowHeight, |
|
714 &cellRect); |
|
715 NS_ENSURE_SUCCESS(rv, rv); |
|
716 |
|
717 if (OffsetForHorzScroll(cellRect, true)) |
|
718 InvalidateFrameWithRect(cellRect); |
|
719 |
|
720 return NS_OK; |
|
721 } |
|
722 |
|
723 nsresult |
|
724 nsTreeBodyFrame::InvalidateRange(int32_t aStart, int32_t aEnd) |
|
725 { |
|
726 if (mUpdateBatchNest) |
|
727 return NS_OK; |
|
728 |
|
729 if (aStart == aEnd) |
|
730 return InvalidateRow(aStart); |
|
731 |
|
732 int32_t last = LastVisibleRow(); |
|
733 if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last) |
|
734 return NS_OK; |
|
735 |
|
736 if (aStart < mTopRowIndex) |
|
737 aStart = mTopRowIndex; |
|
738 |
|
739 if (aEnd > last) |
|
740 aEnd = last; |
|
741 |
|
742 #ifdef ACCESSIBILITY |
|
743 if (nsIPresShell::IsAccessibilityActive()) { |
|
744 int32_t end = |
|
745 mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0; |
|
746 FireInvalidateEvent(aStart, end, nullptr, nullptr); |
|
747 } |
|
748 #endif |
|
749 |
|
750 nsRect rangeRect(mInnerBox.x, mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), mInnerBox.width, mRowHeight*(aEnd-aStart+1)); |
|
751 InvalidateFrameWithRect(rangeRect); |
|
752 |
|
753 return NS_OK; |
|
754 } |
|
755 |
|
756 nsresult |
|
757 nsTreeBodyFrame::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol) |
|
758 { |
|
759 if (mUpdateBatchNest) |
|
760 return NS_OK; |
|
761 |
|
762 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol); |
|
763 if (!col) |
|
764 return NS_ERROR_INVALID_ARG; |
|
765 |
|
766 if (aStart == aEnd) |
|
767 return InvalidateCell(aStart, col); |
|
768 |
|
769 int32_t last = LastVisibleRow(); |
|
770 if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last) |
|
771 return NS_OK; |
|
772 |
|
773 if (aStart < mTopRowIndex) |
|
774 aStart = mTopRowIndex; |
|
775 |
|
776 if (aEnd > last) |
|
777 aEnd = last; |
|
778 |
|
779 #ifdef ACCESSIBILITY |
|
780 if (nsIPresShell::IsAccessibilityActive()) { |
|
781 int32_t end = |
|
782 mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0; |
|
783 FireInvalidateEvent(aStart, end, aCol, aCol); |
|
784 } |
|
785 #endif |
|
786 |
|
787 nsRect rangeRect; |
|
788 nsresult rv = col->GetRect(this, |
|
789 mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), |
|
790 mRowHeight*(aEnd-aStart+1), |
|
791 &rangeRect); |
|
792 NS_ENSURE_SUCCESS(rv, rv); |
|
793 |
|
794 InvalidateFrameWithRect(rangeRect); |
|
795 |
|
796 return NS_OK; |
|
797 } |
|
798 |
|
799 static void |
|
800 FindScrollParts(nsIFrame* aCurrFrame, nsTreeBodyFrame::ScrollParts* aResult) |
|
801 { |
|
802 if (!aResult->mColumnsScrollFrame) { |
|
803 nsIScrollableFrame* f = do_QueryFrame(aCurrFrame); |
|
804 if (f) { |
|
805 aResult->mColumnsFrame = aCurrFrame; |
|
806 aResult->mColumnsScrollFrame = f; |
|
807 } |
|
808 } |
|
809 |
|
810 nsScrollbarFrame *sf = do_QueryFrame(aCurrFrame); |
|
811 if (sf) { |
|
812 if (!aCurrFrame->IsHorizontal()) { |
|
813 if (!aResult->mVScrollbar) { |
|
814 aResult->mVScrollbar = sf; |
|
815 } |
|
816 } else { |
|
817 if (!aResult->mHScrollbar) { |
|
818 aResult->mHScrollbar = sf; |
|
819 } |
|
820 } |
|
821 // don't bother searching inside a scrollbar |
|
822 return; |
|
823 } |
|
824 |
|
825 nsIFrame* child = aCurrFrame->GetFirstPrincipalChild(); |
|
826 while (child && |
|
827 !child->GetContent()->IsRootOfNativeAnonymousSubtree() && |
|
828 (!aResult->mVScrollbar || !aResult->mHScrollbar || |
|
829 !aResult->mColumnsScrollFrame)) { |
|
830 FindScrollParts(child, aResult); |
|
831 child = child->GetNextSibling(); |
|
832 } |
|
833 } |
|
834 |
|
835 nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts() |
|
836 { |
|
837 ScrollParts result = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; |
|
838 nsIContent* baseElement = GetBaseElement(); |
|
839 nsIFrame* treeFrame = |
|
840 baseElement ? baseElement->GetPrimaryFrame() : nullptr; |
|
841 if (treeFrame) { |
|
842 // The way we do this, searching through the entire frame subtree, is pretty |
|
843 // dumb! We should know where these frames are. |
|
844 FindScrollParts(treeFrame, &result); |
|
845 if (result.mHScrollbar) { |
|
846 result.mHScrollbar->SetScrollbarMediatorContent(GetContent()); |
|
847 nsIFrame* f = do_QueryFrame(result.mHScrollbar); |
|
848 result.mHScrollbarContent = f->GetContent(); |
|
849 } |
|
850 if (result.mVScrollbar) { |
|
851 result.mVScrollbar->SetScrollbarMediatorContent(GetContent()); |
|
852 nsIFrame* f = do_QueryFrame(result.mVScrollbar); |
|
853 result.mVScrollbarContent = f->GetContent(); |
|
854 } |
|
855 } |
|
856 return result; |
|
857 } |
|
858 |
|
859 void |
|
860 nsTreeBodyFrame::UpdateScrollbars(const ScrollParts& aParts) |
|
861 { |
|
862 nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); |
|
863 |
|
864 nsWeakFrame weakFrame(this); |
|
865 |
|
866 if (aParts.mVScrollbar) { |
|
867 nsAutoString curPos; |
|
868 curPos.AppendInt(mTopRowIndex*rowHeightAsPixels); |
|
869 aParts.mVScrollbarContent-> |
|
870 SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true); |
|
871 // 'this' might be deleted here |
|
872 } |
|
873 |
|
874 if (weakFrame.IsAlive() && aParts.mHScrollbar) { |
|
875 nsAutoString curPos; |
|
876 curPos.AppendInt(mHorzPosition); |
|
877 aParts.mHScrollbarContent-> |
|
878 SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true); |
|
879 // 'this' might be deleted here |
|
880 } |
|
881 |
|
882 if (weakFrame.IsAlive() && mScrollbarActivity) { |
|
883 mScrollbarActivity->ActivityOccurred(); |
|
884 } |
|
885 } |
|
886 |
|
887 void |
|
888 nsTreeBodyFrame::CheckOverflow(const ScrollParts& aParts) |
|
889 { |
|
890 bool verticalOverflowChanged = false; |
|
891 bool horizontalOverflowChanged = false; |
|
892 |
|
893 if (!mVerticalOverflow && mRowCount > mPageLength) { |
|
894 mVerticalOverflow = true; |
|
895 verticalOverflowChanged = true; |
|
896 } |
|
897 else if (mVerticalOverflow && mRowCount <= mPageLength) { |
|
898 mVerticalOverflow = false; |
|
899 verticalOverflowChanged = true; |
|
900 } |
|
901 |
|
902 if (aParts.mColumnsFrame) { |
|
903 nsRect bounds = aParts.mColumnsFrame->GetRect(); |
|
904 if (bounds.width != 0) { |
|
905 /* Ignore overflows that are less than half a pixel. Yes these happen |
|
906 all over the place when flex boxes are compressed real small. |
|
907 Probably a result of a rounding errors somewhere in the layout code. */ |
|
908 bounds.width += nsPresContext::CSSPixelsToAppUnits(0.5f); |
|
909 if (!mHorizontalOverflow && bounds.width < mHorzWidth) { |
|
910 mHorizontalOverflow = true; |
|
911 horizontalOverflowChanged = true; |
|
912 } else if (mHorizontalOverflow && bounds.width >= mHorzWidth) { |
|
913 mHorizontalOverflow = false; |
|
914 horizontalOverflowChanged = true; |
|
915 } |
|
916 } |
|
917 } |
|
918 |
|
919 nsWeakFrame weakFrame(this); |
|
920 |
|
921 nsRefPtr<nsPresContext> presContext = PresContext(); |
|
922 nsCOMPtr<nsIPresShell> presShell = presContext->GetPresShell(); |
|
923 nsCOMPtr<nsIContent> content = mContent; |
|
924 |
|
925 if (verticalOverflowChanged) { |
|
926 InternalScrollPortEvent event(true, |
|
927 mVerticalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW, |
|
928 nullptr); |
|
929 event.orient = InternalScrollPortEvent::vertical; |
|
930 EventDispatcher::Dispatch(content, presContext, &event); |
|
931 } |
|
932 |
|
933 if (horizontalOverflowChanged) { |
|
934 InternalScrollPortEvent event(true, |
|
935 mHorizontalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW, |
|
936 nullptr); |
|
937 event.orient = InternalScrollPortEvent::horizontal; |
|
938 EventDispatcher::Dispatch(content, presContext, &event); |
|
939 } |
|
940 |
|
941 // The synchronous event dispatch above can trigger reflow notifications. |
|
942 // Flush those explicitly now, so that we can guard against potential infinite |
|
943 // recursion. See bug 905909. |
|
944 if (!weakFrame.IsAlive()) { |
|
945 return; |
|
946 } |
|
947 NS_ASSERTION(!mCheckingOverflow, "mCheckingOverflow should not already be set"); |
|
948 // Don't use AutoRestore since we want to not touch mCheckingOverflow if we fail |
|
949 // the weakFrame.IsAlive() check below |
|
950 mCheckingOverflow = true; |
|
951 presShell->FlushPendingNotifications(Flush_Layout); |
|
952 if (!weakFrame.IsAlive()) { |
|
953 return; |
|
954 } |
|
955 mCheckingOverflow = false; |
|
956 } |
|
957 |
|
958 void |
|
959 nsTreeBodyFrame::InvalidateScrollbars(const ScrollParts& aParts, nsWeakFrame& aWeakColumnsFrame) |
|
960 { |
|
961 if (mUpdateBatchNest || !mView) |
|
962 return; |
|
963 nsWeakFrame weakFrame(this); |
|
964 |
|
965 if (aParts.mVScrollbar) { |
|
966 // Do Vertical Scrollbar |
|
967 nsAutoString maxposStr; |
|
968 |
|
969 nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); |
|
970 |
|
971 int32_t size = rowHeightAsPixels * (mRowCount > mPageLength ? mRowCount - mPageLength : 0); |
|
972 maxposStr.AppendInt(size); |
|
973 aParts.mVScrollbarContent-> |
|
974 SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true); |
|
975 ENSURE_TRUE(weakFrame.IsAlive()); |
|
976 |
|
977 // Also set our page increment and decrement. |
|
978 nscoord pageincrement = mPageLength*rowHeightAsPixels; |
|
979 nsAutoString pageStr; |
|
980 pageStr.AppendInt(pageincrement); |
|
981 aParts.mVScrollbarContent-> |
|
982 SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true); |
|
983 ENSURE_TRUE(weakFrame.IsAlive()); |
|
984 } |
|
985 |
|
986 if (aParts.mHScrollbar && aParts.mColumnsFrame && aWeakColumnsFrame.IsAlive()) { |
|
987 // And now Horizontal scrollbar |
|
988 nsRect bounds = aParts.mColumnsFrame->GetRect(); |
|
989 nsAutoString maxposStr; |
|
990 |
|
991 maxposStr.AppendInt(mHorzWidth > bounds.width ? mHorzWidth - bounds.width : 0); |
|
992 aParts.mHScrollbarContent-> |
|
993 SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true); |
|
994 ENSURE_TRUE(weakFrame.IsAlive()); |
|
995 |
|
996 nsAutoString pageStr; |
|
997 pageStr.AppendInt(bounds.width); |
|
998 aParts.mHScrollbarContent-> |
|
999 SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true); |
|
1000 ENSURE_TRUE(weakFrame.IsAlive()); |
|
1001 |
|
1002 pageStr.Truncate(); |
|
1003 pageStr.AppendInt(nsPresContext::CSSPixelsToAppUnits(16)); |
|
1004 aParts.mHScrollbarContent-> |
|
1005 SetAttr(kNameSpaceID_None, nsGkAtoms::increment, pageStr, true); |
|
1006 } |
|
1007 |
|
1008 if (weakFrame.IsAlive() && mScrollbarActivity) { |
|
1009 mScrollbarActivity->ActivityOccurred(); |
|
1010 } |
|
1011 } |
|
1012 |
|
1013 // Takes client x/y in pixels, converts them to appunits, and converts into |
|
1014 // values relative to this nsTreeBodyFrame frame. |
|
1015 nsPoint |
|
1016 nsTreeBodyFrame::AdjustClientCoordsToBoxCoordSpace(int32_t aX, int32_t aY) |
|
1017 { |
|
1018 nsPoint point(nsPresContext::CSSPixelsToAppUnits(aX), |
|
1019 nsPresContext::CSSPixelsToAppUnits(aY)); |
|
1020 |
|
1021 nsPresContext* presContext = PresContext(); |
|
1022 point -= GetOffsetTo(presContext->GetPresShell()->GetRootFrame()); |
|
1023 |
|
1024 // Adjust by the inner box coords, so that we're in the inner box's |
|
1025 // coordinate space. |
|
1026 point -= mInnerBox.TopLeft(); |
|
1027 return point; |
|
1028 } // AdjustClientCoordsToBoxCoordSpace |
|
1029 |
|
1030 nsresult |
|
1031 nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY, int32_t* _retval) |
|
1032 { |
|
1033 if (!mView) |
|
1034 return NS_OK; |
|
1035 |
|
1036 nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY); |
|
1037 |
|
1038 // Check if the coordinates are above our visible space. |
|
1039 if (point.y < 0) { |
|
1040 *_retval = -1; |
|
1041 return NS_OK; |
|
1042 } |
|
1043 |
|
1044 *_retval = GetRowAt(point.x, point.y); |
|
1045 |
|
1046 return NS_OK; |
|
1047 } |
|
1048 |
|
1049 nsresult |
|
1050 nsTreeBodyFrame::GetCellAt(int32_t aX, int32_t aY, int32_t* aRow, nsITreeColumn** aCol, |
|
1051 nsACString& aChildElt) |
|
1052 { |
|
1053 if (!mView) |
|
1054 return NS_OK; |
|
1055 |
|
1056 nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY); |
|
1057 |
|
1058 // Check if the coordinates are above our visible space. |
|
1059 if (point.y < 0) { |
|
1060 *aRow = -1; |
|
1061 return NS_OK; |
|
1062 } |
|
1063 |
|
1064 nsTreeColumn* col; |
|
1065 nsIAtom* child; |
|
1066 GetCellAt(point.x, point.y, aRow, &col, &child); |
|
1067 |
|
1068 if (col) { |
|
1069 NS_ADDREF(*aCol = col); |
|
1070 if (child == nsCSSAnonBoxes::moztreecell) |
|
1071 aChildElt.AssignLiteral("cell"); |
|
1072 else if (child == nsCSSAnonBoxes::moztreetwisty) |
|
1073 aChildElt.AssignLiteral("twisty"); |
|
1074 else if (child == nsCSSAnonBoxes::moztreeimage) |
|
1075 aChildElt.AssignLiteral("image"); |
|
1076 else if (child == nsCSSAnonBoxes::moztreecelltext) |
|
1077 aChildElt.AssignLiteral("text"); |
|
1078 } |
|
1079 |
|
1080 return NS_OK; |
|
1081 } |
|
1082 |
|
1083 |
|
1084 // |
|
1085 // GetCoordsForCellItem |
|
1086 // |
|
1087 // Find the x/y location and width/height (all in PIXELS) of the given object |
|
1088 // in the given column. |
|
1089 // |
|
1090 // XXX IMPORTANT XXX: |
|
1091 // Hyatt says in the bug for this, that the following needs to be done: |
|
1092 // (1) You need to deal with overflow when computing cell rects. See other column |
|
1093 // iteration examples... if you don't deal with this, you'll mistakenly extend the |
|
1094 // cell into the scrollbar's rect. |
|
1095 // |
|
1096 // (2) You are adjusting the cell rect by the *row" border padding. That's |
|
1097 // wrong. You need to first adjust a row rect by its border/padding, and then the |
|
1098 // cell rect fits inside the adjusted row rect. It also can have border/padding |
|
1099 // as well as margins. The vertical direction isn't that important, but you need |
|
1100 // to get the horizontal direction right. |
|
1101 // |
|
1102 // (3) GetImageSize() does not include margins (but it does include border/padding). |
|
1103 // You need to make sure to add in the image's margins as well. |
|
1104 // |
|
1105 nsresult |
|
1106 nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsACString& aElement, |
|
1107 int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight) |
|
1108 { |
|
1109 *aX = 0; |
|
1110 *aY = 0; |
|
1111 *aWidth = 0; |
|
1112 *aHeight = 0; |
|
1113 |
|
1114 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
|
1115 nscoord currX = mInnerBox.x - mHorzPosition; |
|
1116 |
|
1117 // The Rect for the requested item. |
|
1118 nsRect theRect; |
|
1119 |
|
1120 nsPresContext* presContext = PresContext(); |
|
1121 |
|
1122 for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; currCol = currCol->GetNext()) { |
|
1123 |
|
1124 // The Rect for the current cell. |
|
1125 nscoord colWidth; |
|
1126 #ifdef DEBUG |
|
1127 nsresult rv = |
|
1128 #endif |
|
1129 currCol->GetWidthInTwips(this, &colWidth); |
|
1130 NS_ASSERTION(NS_SUCCEEDED(rv), "invalid column"); |
|
1131 |
|
1132 nsRect cellRect(currX, mInnerBox.y + mRowHeight * (aRow - mTopRowIndex), |
|
1133 colWidth, mRowHeight); |
|
1134 |
|
1135 // Check the ID of the current column to see if it matches. If it doesn't |
|
1136 // increment the current X value and continue to the next column. |
|
1137 if (currCol != aCol) { |
|
1138 currX += cellRect.width; |
|
1139 continue; |
|
1140 } |
|
1141 // Now obtain the properties for our cell. |
|
1142 PrefillPropertyArray(aRow, currCol); |
|
1143 |
|
1144 nsAutoString properties; |
|
1145 mView->GetCellProperties(aRow, currCol, properties); |
|
1146 nsTreeUtils::TokenizeProperties(properties, mScratchArray); |
|
1147 |
|
1148 nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); |
|
1149 |
|
1150 // We don't want to consider any of the decorations that may be present |
|
1151 // on the current row, so we have to deflate the rect by the border and |
|
1152 // padding and offset its left and top coordinates appropriately. |
|
1153 AdjustForBorderPadding(rowContext, cellRect); |
|
1154 |
|
1155 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); |
|
1156 |
|
1157 NS_NAMED_LITERAL_CSTRING(cell, "cell"); |
|
1158 if (currCol->IsCycler() || cell.Equals(aElement)) { |
|
1159 // If the current Column is a Cycler, then the Rect is just the cell - the margins. |
|
1160 // Similarly, if we're just being asked for the cell rect, provide it. |
|
1161 |
|
1162 theRect = cellRect; |
|
1163 nsMargin cellMargin; |
|
1164 cellContext->StyleMargin()->GetMargin(cellMargin); |
|
1165 theRect.Deflate(cellMargin); |
|
1166 break; |
|
1167 } |
|
1168 |
|
1169 // Since we're not looking for the cell, and since the cell isn't a cycler, |
|
1170 // we're looking for some subcomponent, and now we need to subtract the |
|
1171 // borders and padding of the cell from cellRect so this does not |
|
1172 // interfere with our computations. |
|
1173 AdjustForBorderPadding(cellContext, cellRect); |
|
1174 |
|
1175 nsRefPtr<nsRenderingContext> rc = |
|
1176 presContext->PresShell()->CreateReferenceRenderingContext(); |
|
1177 |
|
1178 // Now we'll start making our way across the cell, starting at the edge of |
|
1179 // the cell and proceeding until we hit the right edge. |cellX| is the |
|
1180 // working X value that we will increment as we crawl from left to right. |
|
1181 nscoord cellX = cellRect.x; |
|
1182 nscoord remainWidth = cellRect.width; |
|
1183 |
|
1184 if (currCol->IsPrimary()) { |
|
1185 // If the current Column is a Primary, then we need to take into account the indentation |
|
1186 // and possibly a twisty. |
|
1187 |
|
1188 // The amount of indentation is the indentation width (|mIndentation|) by the level. |
|
1189 int32_t level; |
|
1190 mView->GetLevel(aRow, &level); |
|
1191 if (!isRTL) |
|
1192 cellX += mIndentation * level; |
|
1193 remainWidth -= mIndentation * level; |
|
1194 |
|
1195 // Find the twisty rect by computing its size. |
|
1196 nsRect imageRect; |
|
1197 nsRect twistyRect(cellRect); |
|
1198 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); |
|
1199 GetTwistyRect(aRow, currCol, imageRect, twistyRect, presContext, |
|
1200 *rc, twistyContext); |
|
1201 |
|
1202 if (NS_LITERAL_CSTRING("twisty").Equals(aElement)) { |
|
1203 // If we're looking for the twisty Rect, just return the size |
|
1204 theRect = twistyRect; |
|
1205 break; |
|
1206 } |
|
1207 |
|
1208 // Now we need to add in the margins of the twisty element, so that we |
|
1209 // can find the offset of the next element in the cell. |
|
1210 nsMargin twistyMargin; |
|
1211 twistyContext->StyleMargin()->GetMargin(twistyMargin); |
|
1212 twistyRect.Inflate(twistyMargin); |
|
1213 |
|
1214 // Adjust our working X value with the twisty width (image size, margins, |
|
1215 // borders, padding. |
|
1216 if (!isRTL) |
|
1217 cellX += twistyRect.width; |
|
1218 } |
|
1219 |
|
1220 // Cell Image |
|
1221 nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); |
|
1222 |
|
1223 nsRect imageSize = GetImageSize(aRow, currCol, false, imageContext); |
|
1224 if (NS_LITERAL_CSTRING("image").Equals(aElement)) { |
|
1225 theRect = imageSize; |
|
1226 theRect.x = cellX; |
|
1227 theRect.y = cellRect.y; |
|
1228 break; |
|
1229 } |
|
1230 |
|
1231 // Add in the margins of the cell image. |
|
1232 nsMargin imageMargin; |
|
1233 imageContext->StyleMargin()->GetMargin(imageMargin); |
|
1234 imageSize.Inflate(imageMargin); |
|
1235 |
|
1236 // Increment cellX by the image width |
|
1237 if (!isRTL) |
|
1238 cellX += imageSize.width; |
|
1239 |
|
1240 // Cell Text |
|
1241 nsAutoString cellText; |
|
1242 mView->GetCellText(aRow, currCol, cellText); |
|
1243 // We're going to measure this text so we need to ensure bidi is enabled if |
|
1244 // necessary |
|
1245 CheckTextForBidi(cellText); |
|
1246 |
|
1247 // Create a scratch rect to represent the text rectangle, with the current |
|
1248 // X and Y coords, and a guess at the width and height. The width is the |
|
1249 // remaining width we have left to traverse in the cell, which will be the |
|
1250 // widest possible value for the text rect, and the row height. |
|
1251 nsRect textRect(cellX, cellRect.y, remainWidth, cellRect.height); |
|
1252 |
|
1253 // Measure the width of the text. If the width of the text is greater than |
|
1254 // the remaining width available, then we just assume that the text has |
|
1255 // been cropped and use the remaining rect as the text Rect. Otherwise, |
|
1256 // we add in borders and padding to the text dimension and give that back. |
|
1257 nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); |
|
1258 |
|
1259 nsRefPtr<nsFontMetrics> fm; |
|
1260 nsLayoutUtils::GetFontMetricsForStyleContext(textContext, |
|
1261 getter_AddRefs(fm)); |
|
1262 nscoord height = fm->MaxHeight(); |
|
1263 |
|
1264 nsMargin textMargin; |
|
1265 textContext->StyleMargin()->GetMargin(textMargin); |
|
1266 textRect.Deflate(textMargin); |
|
1267 |
|
1268 // Center the text. XXX Obey vertical-align style prop? |
|
1269 if (height < textRect.height) { |
|
1270 textRect.y += (textRect.height - height) / 2; |
|
1271 textRect.height = height; |
|
1272 } |
|
1273 |
|
1274 nsMargin bp(0,0,0,0); |
|
1275 GetBorderPadding(textContext, bp); |
|
1276 textRect.height += bp.top + bp.bottom; |
|
1277 |
|
1278 rc->SetFont(fm); |
|
1279 AdjustForCellText(cellText, aRow, currCol, *rc, textRect); |
|
1280 |
|
1281 theRect = textRect; |
|
1282 } |
|
1283 |
|
1284 if (isRTL) |
|
1285 theRect.x = mInnerBox.width - theRect.x - theRect.width; |
|
1286 |
|
1287 *aX = nsPresContext::AppUnitsToIntCSSPixels(theRect.x); |
|
1288 *aY = nsPresContext::AppUnitsToIntCSSPixels(theRect.y); |
|
1289 *aWidth = nsPresContext::AppUnitsToIntCSSPixels(theRect.width); |
|
1290 *aHeight = nsPresContext::AppUnitsToIntCSSPixels(theRect.height); |
|
1291 |
|
1292 return NS_OK; |
|
1293 } |
|
1294 |
|
1295 int32_t |
|
1296 nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY) |
|
1297 { |
|
1298 // Now just mod by our total inner box height and add to our top row index. |
|
1299 int32_t row = (aY/mRowHeight)+mTopRowIndex; |
|
1300 |
|
1301 // Check if the coordinates are below our visible space (or within our visible |
|
1302 // space but below any row). |
|
1303 if (row > mTopRowIndex + mPageLength || row >= mRowCount) |
|
1304 return -1; |
|
1305 |
|
1306 return row; |
|
1307 } |
|
1308 |
|
1309 void |
|
1310 nsTreeBodyFrame::CheckTextForBidi(nsAutoString& aText) |
|
1311 { |
|
1312 // We could check to see whether the prescontext already has bidi enabled, |
|
1313 // but usually it won't, so it's probably faster to avoid the call to |
|
1314 // GetPresContext() when it's not needed. |
|
1315 if (HasRTLChars(aText)) { |
|
1316 PresContext()->SetBidiEnabled(); |
|
1317 } |
|
1318 } |
|
1319 |
|
1320 void |
|
1321 nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText, |
|
1322 int32_t aRowIndex, nsTreeColumn* aColumn, |
|
1323 nsRenderingContext& aRenderingContext, |
|
1324 nsRect& aTextRect) |
|
1325 { |
|
1326 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
1327 |
|
1328 nscoord width = |
|
1329 nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aText.get(), aText.Length()); |
|
1330 nscoord maxWidth = aTextRect.width; |
|
1331 |
|
1332 if (aColumn->Overflow()) { |
|
1333 DebugOnly<nsresult> rv; |
|
1334 nsTreeColumn* nextColumn = aColumn->GetNext(); |
|
1335 while (nextColumn && width > maxWidth) { |
|
1336 while (nextColumn) { |
|
1337 nscoord width; |
|
1338 rv = nextColumn->GetWidthInTwips(this, &width); |
|
1339 NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid"); |
|
1340 |
|
1341 if (width != 0) |
|
1342 break; |
|
1343 |
|
1344 nextColumn = nextColumn->GetNext(); |
|
1345 } |
|
1346 |
|
1347 if (nextColumn) { |
|
1348 nsAutoString nextText; |
|
1349 mView->GetCellText(aRowIndex, nextColumn, nextText); |
|
1350 // We don't measure or draw this text so no need to check it for |
|
1351 // bidi-ness |
|
1352 |
|
1353 if (nextText.Length() == 0) { |
|
1354 nscoord width; |
|
1355 rv = nextColumn->GetWidthInTwips(this, &width); |
|
1356 NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid"); |
|
1357 |
|
1358 maxWidth += width; |
|
1359 |
|
1360 nextColumn = nextColumn->GetNext(); |
|
1361 } |
|
1362 else { |
|
1363 nextColumn = nullptr; |
|
1364 } |
|
1365 } |
|
1366 } |
|
1367 } |
|
1368 |
|
1369 if (width > maxWidth) { |
|
1370 // See if the width is even smaller than the ellipsis |
|
1371 // If so, clear the text completely. |
|
1372 const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis(); |
|
1373 aRenderingContext.SetTextRunRTL(false); |
|
1374 nscoord ellipsisWidth = aRenderingContext.GetWidth(kEllipsis); |
|
1375 |
|
1376 width = maxWidth; |
|
1377 if (ellipsisWidth > width) |
|
1378 aText.SetLength(0); |
|
1379 else if (ellipsisWidth == width) |
|
1380 aText.Assign(kEllipsis); |
|
1381 else { |
|
1382 // We will be drawing an ellipsis, thank you very much. |
|
1383 // Subtract out the required width of the ellipsis. |
|
1384 // This is the total remaining width we have to play with. |
|
1385 width -= ellipsisWidth; |
|
1386 |
|
1387 // Now we crop. |
|
1388 switch (aColumn->GetCropStyle()) { |
|
1389 default: |
|
1390 case 0: { |
|
1391 // Crop right. |
|
1392 nscoord cwidth; |
|
1393 nscoord twidth = 0; |
|
1394 uint32_t length = aText.Length(); |
|
1395 uint32_t i; |
|
1396 for (i = 0; i < length; ++i) { |
|
1397 char16_t ch = aText[i]; |
|
1398 // XXX this is horrible and doesn't handle clusters |
|
1399 cwidth = aRenderingContext.GetWidth(ch); |
|
1400 if (twidth + cwidth > width) |
|
1401 break; |
|
1402 twidth += cwidth; |
|
1403 } |
|
1404 aText.Truncate(i); |
|
1405 aText.Append(kEllipsis); |
|
1406 } |
|
1407 break; |
|
1408 |
|
1409 case 2: { |
|
1410 // Crop left. |
|
1411 nscoord cwidth; |
|
1412 nscoord twidth = 0; |
|
1413 int32_t length = aText.Length(); |
|
1414 int32_t i; |
|
1415 for (i=length-1; i >= 0; --i) { |
|
1416 char16_t ch = aText[i]; |
|
1417 cwidth = aRenderingContext.GetWidth(ch); |
|
1418 if (twidth + cwidth > width) |
|
1419 break; |
|
1420 twidth += cwidth; |
|
1421 } |
|
1422 |
|
1423 nsAutoString copy; |
|
1424 aText.Right(copy, length-1-i); |
|
1425 aText.Assign(kEllipsis); |
|
1426 aText += copy; |
|
1427 } |
|
1428 break; |
|
1429 |
|
1430 case 1: |
|
1431 { |
|
1432 // Crop center. |
|
1433 nsAutoString leftStr, rightStr; |
|
1434 nscoord cwidth, twidth = 0; |
|
1435 int32_t length = aText.Length(); |
|
1436 int32_t rightPos = length - 1; |
|
1437 for (int32_t leftPos = 0; leftPos < rightPos; ++leftPos) { |
|
1438 char16_t ch = aText[leftPos]; |
|
1439 cwidth = aRenderingContext.GetWidth(ch); |
|
1440 twidth += cwidth; |
|
1441 if (twidth > width) |
|
1442 break; |
|
1443 leftStr.Append(ch); |
|
1444 |
|
1445 ch = aText[rightPos]; |
|
1446 cwidth = aRenderingContext.GetWidth(ch); |
|
1447 twidth += cwidth; |
|
1448 if (twidth > width) |
|
1449 break; |
|
1450 rightStr.Insert(ch, 0); |
|
1451 --rightPos; |
|
1452 } |
|
1453 aText = leftStr; |
|
1454 aText.Append(kEllipsis); |
|
1455 aText += rightStr; |
|
1456 } |
|
1457 break; |
|
1458 } |
|
1459 } |
|
1460 |
|
1461 width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aText.get(), aText.Length()); |
|
1462 } |
|
1463 |
|
1464 switch (aColumn->GetTextAlignment()) { |
|
1465 case NS_STYLE_TEXT_ALIGN_RIGHT: { |
|
1466 aTextRect.x += aTextRect.width - width; |
|
1467 } |
|
1468 break; |
|
1469 case NS_STYLE_TEXT_ALIGN_CENTER: { |
|
1470 aTextRect.x += (aTextRect.width - width) / 2; |
|
1471 } |
|
1472 break; |
|
1473 } |
|
1474 |
|
1475 aTextRect.width = width; |
|
1476 } |
|
1477 |
|
1478 nsIAtom* |
|
1479 nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, |
|
1480 int32_t aRowIndex, |
|
1481 nsTreeColumn* aColumn) |
|
1482 { |
|
1483 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
1484 |
|
1485 // Obtain the properties for our cell. |
|
1486 PrefillPropertyArray(aRowIndex, aColumn); |
|
1487 nsAutoString properties; |
|
1488 mView->GetCellProperties(aRowIndex, aColumn, properties); |
|
1489 nsTreeUtils::TokenizeProperties(properties, mScratchArray); |
|
1490 |
|
1491 // Resolve style for the cell. |
|
1492 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); |
|
1493 |
|
1494 // Obtain the margins for the cell and then deflate our rect by that |
|
1495 // amount. The cell is assumed to be contained within the deflated rect. |
|
1496 nsRect cellRect(aCellRect); |
|
1497 nsMargin cellMargin; |
|
1498 cellContext->StyleMargin()->GetMargin(cellMargin); |
|
1499 cellRect.Deflate(cellMargin); |
|
1500 |
|
1501 // Adjust the rect for its border and padding. |
|
1502 AdjustForBorderPadding(cellContext, cellRect); |
|
1503 |
|
1504 if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) { |
|
1505 // The user clicked within the cell's margins/borders/padding. This constitutes a click on the cell. |
|
1506 return nsCSSAnonBoxes::moztreecell; |
|
1507 } |
|
1508 |
|
1509 nscoord currX = cellRect.x; |
|
1510 nscoord remainingWidth = cellRect.width; |
|
1511 |
|
1512 // Handle right alignment hit testing. |
|
1513 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
|
1514 |
|
1515 nsPresContext* presContext = PresContext(); |
|
1516 nsRefPtr<nsRenderingContext> rc = |
|
1517 presContext->PresShell()->CreateReferenceRenderingContext(); |
|
1518 |
|
1519 if (aColumn->IsPrimary()) { |
|
1520 // If we're the primary column, we have indentation and a twisty. |
|
1521 int32_t level; |
|
1522 mView->GetLevel(aRowIndex, &level); |
|
1523 |
|
1524 if (!isRTL) |
|
1525 currX += mIndentation*level; |
|
1526 remainingWidth -= mIndentation*level; |
|
1527 |
|
1528 if ((isRTL && aX > currX + remainingWidth) || |
|
1529 (!isRTL && aX < currX)) { |
|
1530 // The user clicked within the indentation. |
|
1531 return nsCSSAnonBoxes::moztreecell; |
|
1532 } |
|
1533 |
|
1534 // Always leave space for the twisty. |
|
1535 nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height); |
|
1536 bool hasTwisty = false; |
|
1537 bool isContainer = false; |
|
1538 mView->IsContainer(aRowIndex, &isContainer); |
|
1539 if (isContainer) { |
|
1540 bool isContainerEmpty = false; |
|
1541 mView->IsContainerEmpty(aRowIndex, &isContainerEmpty); |
|
1542 if (!isContainerEmpty) |
|
1543 hasTwisty = true; |
|
1544 } |
|
1545 |
|
1546 // Resolve style for the twisty. |
|
1547 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); |
|
1548 |
|
1549 nsRect imageSize; |
|
1550 GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, presContext, |
|
1551 *rc, twistyContext); |
|
1552 |
|
1553 // We will treat a click as hitting the twisty if it happens on the margins, borders, padding, |
|
1554 // or content of the twisty object. By allowing a "slop" into the margin, we make it a little |
|
1555 // bit easier for a user to hit the twisty. (We don't want to be too picky here.) |
|
1556 nsMargin twistyMargin; |
|
1557 twistyContext->StyleMargin()->GetMargin(twistyMargin); |
|
1558 twistyRect.Inflate(twistyMargin); |
|
1559 if (isRTL) |
|
1560 twistyRect.x = currX + remainingWidth - twistyRect.width; |
|
1561 |
|
1562 // Now we test to see if aX is actually within the twistyRect. If it is, and if the item should |
|
1563 // have a twisty, then we return "twisty". If it is within the rect but we shouldn't have a twisty, |
|
1564 // then we return "cell". |
|
1565 if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) { |
|
1566 if (hasTwisty) |
|
1567 return nsCSSAnonBoxes::moztreetwisty; |
|
1568 else |
|
1569 return nsCSSAnonBoxes::moztreecell; |
|
1570 } |
|
1571 |
|
1572 if (!isRTL) |
|
1573 currX += twistyRect.width; |
|
1574 remainingWidth -= twistyRect.width; |
|
1575 } |
|
1576 |
|
1577 // Now test to see if the user hit the icon for the cell. |
|
1578 nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height); |
|
1579 |
|
1580 // Resolve style for the image. |
|
1581 nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); |
|
1582 |
|
1583 nsRect iconSize = GetImageSize(aRowIndex, aColumn, false, imageContext); |
|
1584 nsMargin imageMargin; |
|
1585 imageContext->StyleMargin()->GetMargin(imageMargin); |
|
1586 iconSize.Inflate(imageMargin); |
|
1587 iconRect.width = iconSize.width; |
|
1588 if (isRTL) |
|
1589 iconRect.x = currX + remainingWidth - iconRect.width; |
|
1590 |
|
1591 if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) { |
|
1592 // The user clicked on the image. |
|
1593 return nsCSSAnonBoxes::moztreeimage; |
|
1594 } |
|
1595 |
|
1596 if (!isRTL) |
|
1597 currX += iconRect.width; |
|
1598 remainingWidth -= iconRect.width; |
|
1599 |
|
1600 nsAutoString cellText; |
|
1601 mView->GetCellText(aRowIndex, aColumn, cellText); |
|
1602 // We're going to measure this text so we need to ensure bidi is enabled if |
|
1603 // necessary |
|
1604 CheckTextForBidi(cellText); |
|
1605 |
|
1606 nsRect textRect(currX, cellRect.y, remainingWidth, cellRect.height); |
|
1607 |
|
1608 nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); |
|
1609 |
|
1610 nsMargin textMargin; |
|
1611 textContext->StyleMargin()->GetMargin(textMargin); |
|
1612 textRect.Deflate(textMargin); |
|
1613 |
|
1614 AdjustForBorderPadding(textContext, textRect); |
|
1615 |
|
1616 nsRefPtr<nsFontMetrics> fm; |
|
1617 nsLayoutUtils::GetFontMetricsForStyleContext(textContext, |
|
1618 getter_AddRefs(fm)); |
|
1619 rc->SetFont(fm); |
|
1620 |
|
1621 AdjustForCellText(cellText, aRowIndex, aColumn, *rc, textRect); |
|
1622 |
|
1623 if (aX >= textRect.x && aX < textRect.x + textRect.width) |
|
1624 return nsCSSAnonBoxes::moztreecelltext; |
|
1625 else |
|
1626 return nsCSSAnonBoxes::moztreecell; |
|
1627 } |
|
1628 |
|
1629 void |
|
1630 nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, int32_t* aRow, |
|
1631 nsTreeColumn** aCol, nsIAtom** aChildElt) |
|
1632 { |
|
1633 *aCol = nullptr; |
|
1634 *aChildElt = nullptr; |
|
1635 |
|
1636 *aRow = GetRowAt(aX, aY); |
|
1637 if (*aRow < 0) |
|
1638 return; |
|
1639 |
|
1640 // Determine the column hit. |
|
1641 for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; |
|
1642 currCol = currCol->GetNext()) { |
|
1643 nsRect cellRect; |
|
1644 nsresult rv = currCol->GetRect(this, |
|
1645 mInnerBox.y + |
|
1646 mRowHeight * (*aRow - mTopRowIndex), |
|
1647 mRowHeight, |
|
1648 &cellRect); |
|
1649 if (NS_FAILED(rv)) { |
|
1650 NS_NOTREACHED("column has no frame"); |
|
1651 continue; |
|
1652 } |
|
1653 |
|
1654 if (!OffsetForHorzScroll(cellRect, false)) |
|
1655 continue; |
|
1656 |
|
1657 if (aX >= cellRect.x && aX < cellRect.x + cellRect.width) { |
|
1658 // We know the column hit now. |
|
1659 *aCol = currCol; |
|
1660 |
|
1661 if (currCol->IsCycler()) |
|
1662 // Cyclers contain only images. Fill this in immediately and return. |
|
1663 *aChildElt = nsCSSAnonBoxes::moztreeimage; |
|
1664 else |
|
1665 *aChildElt = GetItemWithinCellAt(aX, cellRect, *aRow, currCol); |
|
1666 break; |
|
1667 } |
|
1668 } |
|
1669 } |
|
1670 |
|
1671 nsresult |
|
1672 nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol, |
|
1673 nsRenderingContext* aRenderingContext, |
|
1674 nscoord& aDesiredSize, nscoord& aCurrentSize) |
|
1675 { |
|
1676 NS_PRECONDITION(aCol, "aCol must not be null"); |
|
1677 NS_PRECONDITION(aRenderingContext, "aRenderingContext must not be null"); |
|
1678 |
|
1679 // The rect for the current cell. |
|
1680 nscoord colWidth; |
|
1681 nsresult rv = aCol->GetWidthInTwips(this, &colWidth); |
|
1682 NS_ENSURE_SUCCESS(rv, rv); |
|
1683 |
|
1684 nsRect cellRect(0, 0, colWidth, mRowHeight); |
|
1685 |
|
1686 int32_t overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width); |
|
1687 if (overflow > 0) |
|
1688 cellRect.width -= overflow; |
|
1689 |
|
1690 // Adjust borders and padding for the cell. |
|
1691 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); |
|
1692 nsMargin bp(0,0,0,0); |
|
1693 GetBorderPadding(cellContext, bp); |
|
1694 |
|
1695 aCurrentSize = cellRect.width; |
|
1696 aDesiredSize = bp.left + bp.right; |
|
1697 |
|
1698 if (aCol->IsPrimary()) { |
|
1699 // If the current Column is a Primary, then we need to take into account |
|
1700 // the indentation and possibly a twisty. |
|
1701 |
|
1702 // The amount of indentation is the indentation width (|mIndentation|) by the level. |
|
1703 int32_t level; |
|
1704 mView->GetLevel(aRow, &level); |
|
1705 aDesiredSize += mIndentation * level; |
|
1706 |
|
1707 // Find the twisty rect by computing its size. |
|
1708 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); |
|
1709 |
|
1710 nsRect imageSize; |
|
1711 nsRect twistyRect(cellRect); |
|
1712 GetTwistyRect(aRow, aCol, imageSize, twistyRect, PresContext(), |
|
1713 *aRenderingContext, twistyContext); |
|
1714 |
|
1715 // Add in the margins of the twisty element. |
|
1716 nsMargin twistyMargin; |
|
1717 twistyContext->StyleMargin()->GetMargin(twistyMargin); |
|
1718 twistyRect.Inflate(twistyMargin); |
|
1719 |
|
1720 aDesiredSize += twistyRect.width; |
|
1721 } |
|
1722 |
|
1723 nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); |
|
1724 |
|
1725 // Account for the width of the cell image. |
|
1726 nsRect imageSize = GetImageSize(aRow, aCol, false, imageContext); |
|
1727 // Add in the margins of the cell image. |
|
1728 nsMargin imageMargin; |
|
1729 imageContext->StyleMargin()->GetMargin(imageMargin); |
|
1730 imageSize.Inflate(imageMargin); |
|
1731 |
|
1732 aDesiredSize += imageSize.width; |
|
1733 |
|
1734 // Get the cell text. |
|
1735 nsAutoString cellText; |
|
1736 mView->GetCellText(aRow, aCol, cellText); |
|
1737 // We're going to measure this text so we need to ensure bidi is enabled if |
|
1738 // necessary |
|
1739 CheckTextForBidi(cellText); |
|
1740 |
|
1741 nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); |
|
1742 |
|
1743 // Get the borders and padding for the text. |
|
1744 GetBorderPadding(textContext, bp); |
|
1745 |
|
1746 nsRefPtr<nsFontMetrics> fm; |
|
1747 nsLayoutUtils::GetFontMetricsForStyleContext(textContext, |
|
1748 getter_AddRefs(fm)); |
|
1749 aRenderingContext->SetFont(fm); |
|
1750 |
|
1751 // Get the width of the text itself |
|
1752 nscoord width = |
|
1753 nsLayoutUtils::GetStringWidth(this, aRenderingContext, cellText.get(), cellText.Length()); |
|
1754 nscoord totalTextWidth = width + bp.left + bp.right; |
|
1755 aDesiredSize += totalTextWidth; |
|
1756 return NS_OK; |
|
1757 } |
|
1758 |
|
1759 nsresult |
|
1760 nsTreeBodyFrame::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *_retval) |
|
1761 { |
|
1762 nscoord currentSize, desiredSize; |
|
1763 nsresult rv; |
|
1764 |
|
1765 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol); |
|
1766 if (!col) |
|
1767 return NS_ERROR_INVALID_ARG; |
|
1768 |
|
1769 nsRefPtr<nsRenderingContext> rc = |
|
1770 PresContext()->PresShell()->CreateReferenceRenderingContext(); |
|
1771 |
|
1772 rv = GetCellWidth(aRow, col, rc, desiredSize, currentSize); |
|
1773 NS_ENSURE_SUCCESS(rv, rv); |
|
1774 |
|
1775 *_retval = desiredSize > currentSize; |
|
1776 |
|
1777 return NS_OK; |
|
1778 } |
|
1779 |
|
1780 void |
|
1781 nsTreeBodyFrame::MarkDirtyIfSelect() |
|
1782 { |
|
1783 nsIContent* baseElement = GetBaseElement(); |
|
1784 |
|
1785 if (baseElement && baseElement->Tag() == nsGkAtoms::select && |
|
1786 baseElement->IsHTML()) { |
|
1787 // If we are an intrinsically sized select widget, we may need to |
|
1788 // resize, if the widest item was removed or a new item was added. |
|
1789 // XXX optimize this more |
|
1790 |
|
1791 mStringWidth = -1; |
|
1792 PresContext()->PresShell()->FrameNeedsReflow(this, |
|
1793 nsIPresShell::eTreeChange, |
|
1794 NS_FRAME_IS_DIRTY); |
|
1795 } |
|
1796 } |
|
1797 |
|
1798 nsresult |
|
1799 nsTreeBodyFrame::CreateTimer(const LookAndFeel::IntID aID, |
|
1800 nsTimerCallbackFunc aFunc, int32_t aType, |
|
1801 nsITimer** aTimer) |
|
1802 { |
|
1803 // Get the delay from the look and feel service. |
|
1804 int32_t delay = LookAndFeel::GetInt(aID, 0); |
|
1805 |
|
1806 nsCOMPtr<nsITimer> timer; |
|
1807 |
|
1808 // Create a new timer only if the delay is greater than zero. |
|
1809 // Zero value means that this feature is completely disabled. |
|
1810 if (delay > 0) { |
|
1811 timer = do_CreateInstance("@mozilla.org/timer;1"); |
|
1812 if (timer) |
|
1813 timer->InitWithFuncCallback(aFunc, this, delay, aType); |
|
1814 } |
|
1815 |
|
1816 NS_IF_ADDREF(*aTimer = timer); |
|
1817 |
|
1818 return NS_OK; |
|
1819 } |
|
1820 |
|
1821 nsresult |
|
1822 nsTreeBodyFrame::RowCountChanged(int32_t aIndex, int32_t aCount) |
|
1823 { |
|
1824 if (aCount == 0 || !mView) |
|
1825 return NS_OK; // Nothing to do. |
|
1826 |
|
1827 #ifdef ACCESSIBILITY |
|
1828 if (nsIPresShell::IsAccessibilityActive()) |
|
1829 FireRowCountChangedEvent(aIndex, aCount); |
|
1830 #endif |
|
1831 |
|
1832 // Adjust our selection. |
|
1833 nsCOMPtr<nsITreeSelection> sel; |
|
1834 mView->GetSelection(getter_AddRefs(sel)); |
|
1835 if (sel) |
|
1836 sel->AdjustSelection(aIndex, aCount); |
|
1837 |
|
1838 if (mUpdateBatchNest) |
|
1839 return NS_OK; |
|
1840 |
|
1841 mRowCount += aCount; |
|
1842 #ifdef DEBUG |
|
1843 int32_t rowCount = mRowCount; |
|
1844 mView->GetRowCount(&rowCount); |
|
1845 NS_ASSERTION(rowCount == mRowCount, "row count did not change by the amount suggested, check caller"); |
|
1846 #endif |
|
1847 |
|
1848 int32_t count = Abs(aCount); |
|
1849 int32_t last = LastVisibleRow(); |
|
1850 if (aIndex >= mTopRowIndex && aIndex <= last) |
|
1851 InvalidateRange(aIndex, last); |
|
1852 |
|
1853 ScrollParts parts = GetScrollParts(); |
|
1854 |
|
1855 if (mTopRowIndex == 0) { |
|
1856 // Just update the scrollbar and return. |
|
1857 if (FullScrollbarsUpdate(false)) { |
|
1858 MarkDirtyIfSelect(); |
|
1859 } |
|
1860 return NS_OK; |
|
1861 } |
|
1862 |
|
1863 bool needsInvalidation = false; |
|
1864 // Adjust our top row index. |
|
1865 if (aCount > 0) { |
|
1866 if (mTopRowIndex > aIndex) { |
|
1867 // Rows came in above us. Augment the top row index. |
|
1868 mTopRowIndex += aCount; |
|
1869 } |
|
1870 } |
|
1871 else if (aCount < 0) { |
|
1872 if (mTopRowIndex > aIndex+count-1) { |
|
1873 // No need to invalidate. The remove happened |
|
1874 // completely above us (offscreen). |
|
1875 mTopRowIndex -= count; |
|
1876 } |
|
1877 else if (mTopRowIndex >= aIndex) { |
|
1878 // This is a full-blown invalidate. |
|
1879 if (mTopRowIndex + mPageLength > mRowCount - 1) { |
|
1880 mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength); |
|
1881 } |
|
1882 needsInvalidation = true; |
|
1883 } |
|
1884 } |
|
1885 |
|
1886 if (FullScrollbarsUpdate(needsInvalidation)) { |
|
1887 MarkDirtyIfSelect(); |
|
1888 } |
|
1889 return NS_OK; |
|
1890 } |
|
1891 |
|
1892 nsresult |
|
1893 nsTreeBodyFrame::BeginUpdateBatch() |
|
1894 { |
|
1895 ++mUpdateBatchNest; |
|
1896 |
|
1897 return NS_OK; |
|
1898 } |
|
1899 |
|
1900 nsresult |
|
1901 nsTreeBodyFrame::EndUpdateBatch() |
|
1902 { |
|
1903 NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch"); |
|
1904 |
|
1905 if (--mUpdateBatchNest == 0) { |
|
1906 if (mView) { |
|
1907 Invalidate(); |
|
1908 int32_t countBeforeUpdate = mRowCount; |
|
1909 mView->GetRowCount(&mRowCount); |
|
1910 if (countBeforeUpdate != mRowCount) { |
|
1911 if (mTopRowIndex + mPageLength > mRowCount - 1) { |
|
1912 mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength); |
|
1913 } |
|
1914 FullScrollbarsUpdate(false); |
|
1915 } |
|
1916 } |
|
1917 } |
|
1918 |
|
1919 return NS_OK; |
|
1920 } |
|
1921 |
|
1922 void |
|
1923 nsTreeBodyFrame::PrefillPropertyArray(int32_t aRowIndex, nsTreeColumn* aCol) |
|
1924 { |
|
1925 NS_PRECONDITION(!aCol || aCol->GetFrame(), "invalid column passed"); |
|
1926 mScratchArray.Clear(); |
|
1927 |
|
1928 // focus |
|
1929 if (mFocused) |
|
1930 mScratchArray.AppendElement(nsGkAtoms::focus); |
|
1931 |
|
1932 // sort |
|
1933 bool sorted = false; |
|
1934 mView->IsSorted(&sorted); |
|
1935 if (sorted) |
|
1936 mScratchArray.AppendElement(nsGkAtoms::sorted); |
|
1937 |
|
1938 // drag session |
|
1939 if (mSlots && mSlots->mIsDragging) |
|
1940 mScratchArray.AppendElement(nsGkAtoms::dragSession); |
|
1941 |
|
1942 if (aRowIndex != -1) { |
|
1943 if (aRowIndex == mMouseOverRow) |
|
1944 mScratchArray.AppendElement(nsGkAtoms::hover); |
|
1945 |
|
1946 nsCOMPtr<nsITreeSelection> selection; |
|
1947 mView->GetSelection(getter_AddRefs(selection)); |
|
1948 |
|
1949 if (selection) { |
|
1950 // selected |
|
1951 bool isSelected; |
|
1952 selection->IsSelected(aRowIndex, &isSelected); |
|
1953 if (isSelected) |
|
1954 mScratchArray.AppendElement(nsGkAtoms::selected); |
|
1955 |
|
1956 // current |
|
1957 int32_t currentIndex; |
|
1958 selection->GetCurrentIndex(¤tIndex); |
|
1959 if (aRowIndex == currentIndex) |
|
1960 mScratchArray.AppendElement(nsGkAtoms::current); |
|
1961 |
|
1962 // active |
|
1963 if (aCol) { |
|
1964 nsCOMPtr<nsITreeColumn> currentColumn; |
|
1965 selection->GetCurrentColumn(getter_AddRefs(currentColumn)); |
|
1966 if (aCol == currentColumn) |
|
1967 mScratchArray.AppendElement(nsGkAtoms::active); |
|
1968 } |
|
1969 } |
|
1970 |
|
1971 // container or leaf |
|
1972 bool isContainer = false; |
|
1973 mView->IsContainer(aRowIndex, &isContainer); |
|
1974 if (isContainer) { |
|
1975 mScratchArray.AppendElement(nsGkAtoms::container); |
|
1976 |
|
1977 // open or closed |
|
1978 bool isOpen = false; |
|
1979 mView->IsContainerOpen(aRowIndex, &isOpen); |
|
1980 if (isOpen) |
|
1981 mScratchArray.AppendElement(nsGkAtoms::open); |
|
1982 else |
|
1983 mScratchArray.AppendElement(nsGkAtoms::closed); |
|
1984 } |
|
1985 else { |
|
1986 mScratchArray.AppendElement(nsGkAtoms::leaf); |
|
1987 } |
|
1988 |
|
1989 // drop orientation |
|
1990 if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) { |
|
1991 if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) |
|
1992 mScratchArray.AppendElement(nsGkAtoms::dropBefore); |
|
1993 else if (mSlots->mDropOrient == nsITreeView::DROP_ON) |
|
1994 mScratchArray.AppendElement(nsGkAtoms::dropOn); |
|
1995 else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER) |
|
1996 mScratchArray.AppendElement(nsGkAtoms::dropAfter); |
|
1997 } |
|
1998 |
|
1999 // odd or even |
|
2000 if (aRowIndex % 2) |
|
2001 mScratchArray.AppendElement(nsGkAtoms::odd); |
|
2002 else |
|
2003 mScratchArray.AppendElement(nsGkAtoms::even); |
|
2004 |
|
2005 nsIContent* baseContent = GetBaseElement(); |
|
2006 if (baseContent && baseContent->HasAttr(kNameSpaceID_None, nsGkAtoms::editing)) |
|
2007 mScratchArray.AppendElement(nsGkAtoms::editing); |
|
2008 |
|
2009 // multiple columns |
|
2010 if (mColumns->GetColumnAt(1)) |
|
2011 mScratchArray.AppendElement(nsGkAtoms::multicol); |
|
2012 } |
|
2013 |
|
2014 if (aCol) { |
|
2015 mScratchArray.AppendElement(aCol->GetAtom()); |
|
2016 |
|
2017 if (aCol->IsPrimary()) |
|
2018 mScratchArray.AppendElement(nsGkAtoms::primary); |
|
2019 |
|
2020 if (aCol->GetType() == nsITreeColumn::TYPE_CHECKBOX) { |
|
2021 mScratchArray.AppendElement(nsGkAtoms::checkbox); |
|
2022 |
|
2023 if (aRowIndex != -1) { |
|
2024 nsAutoString value; |
|
2025 mView->GetCellValue(aRowIndex, aCol, value); |
|
2026 if (value.EqualsLiteral("true")) |
|
2027 mScratchArray.AppendElement(nsGkAtoms::checked); |
|
2028 } |
|
2029 } |
|
2030 else if (aCol->GetType() == nsITreeColumn::TYPE_PROGRESSMETER) { |
|
2031 mScratchArray.AppendElement(nsGkAtoms::progressmeter); |
|
2032 |
|
2033 if (aRowIndex != -1) { |
|
2034 int32_t state; |
|
2035 mView->GetProgressMode(aRowIndex, aCol, &state); |
|
2036 if (state == nsITreeView::PROGRESS_NORMAL) |
|
2037 mScratchArray.AppendElement(nsGkAtoms::progressNormal); |
|
2038 else if (state == nsITreeView::PROGRESS_UNDETERMINED) |
|
2039 mScratchArray.AppendElement(nsGkAtoms::progressUndetermined); |
|
2040 } |
|
2041 } |
|
2042 |
|
2043 // Read special properties from attributes on the column content node |
|
2044 if (aCol->mContent->AttrValueIs(kNameSpaceID_None, |
|
2045 nsGkAtoms::insertbefore, |
|
2046 nsGkAtoms::_true, eCaseMatters)) |
|
2047 mScratchArray.AppendElement(nsGkAtoms::insertbefore); |
|
2048 if (aCol->mContent->AttrValueIs(kNameSpaceID_None, |
|
2049 nsGkAtoms::insertafter, |
|
2050 nsGkAtoms::_true, eCaseMatters)) |
|
2051 mScratchArray.AppendElement(nsGkAtoms::insertafter); |
|
2052 } |
|
2053 } |
|
2054 |
|
2055 nsITheme* |
|
2056 nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex, |
|
2057 nsTreeColumn* aColumn, |
|
2058 nsRect& aImageRect, |
|
2059 nsRect& aTwistyRect, |
|
2060 nsPresContext* aPresContext, |
|
2061 nsRenderingContext& aRenderingContext, |
|
2062 nsStyleContext* aTwistyContext) |
|
2063 { |
|
2064 // The twisty rect extends all the way to the end of the cell. This is incorrect. We need to |
|
2065 // determine the twisty rect's true width. This is done by examining the style context for |
|
2066 // a width first. If it has one, we use that. If it doesn't, we use the image's natural width. |
|
2067 // If the image hasn't loaded and if no width is specified, then we just bail. If there is |
|
2068 // a -moz-appearance involved, adjust the rect by the minimum widget size provided by |
|
2069 // the theme implementation. |
|
2070 aImageRect = GetImageSize(aRowIndex, aColumn, true, aTwistyContext); |
|
2071 if (aImageRect.height > aTwistyRect.height) |
|
2072 aImageRect.height = aTwistyRect.height; |
|
2073 if (aImageRect.width > aTwistyRect.width) |
|
2074 aImageRect.width = aTwistyRect.width; |
|
2075 else |
|
2076 aTwistyRect.width = aImageRect.width; |
|
2077 |
|
2078 bool useTheme = false; |
|
2079 nsITheme *theme = nullptr; |
|
2080 const nsStyleDisplay* twistyDisplayData = aTwistyContext->StyleDisplay(); |
|
2081 if (twistyDisplayData->mAppearance) { |
|
2082 theme = aPresContext->GetTheme(); |
|
2083 if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, twistyDisplayData->mAppearance)) |
|
2084 useTheme = true; |
|
2085 } |
|
2086 |
|
2087 if (useTheme) { |
|
2088 nsIntSize minTwistySizePx(0,0); |
|
2089 bool canOverride = true; |
|
2090 theme->GetMinimumWidgetSize(&aRenderingContext, this, twistyDisplayData->mAppearance, |
|
2091 &minTwistySizePx, &canOverride); |
|
2092 |
|
2093 // GMWS() returns size in pixels, we need to convert it back to app units |
|
2094 nsSize minTwistySize; |
|
2095 minTwistySize.width = aPresContext->DevPixelsToAppUnits(minTwistySizePx.width); |
|
2096 minTwistySize.height = aPresContext->DevPixelsToAppUnits(minTwistySizePx.height); |
|
2097 |
|
2098 if (aTwistyRect.width < minTwistySize.width || !canOverride) |
|
2099 aTwistyRect.width = minTwistySize.width; |
|
2100 } |
|
2101 |
|
2102 return useTheme ? theme : nullptr; |
|
2103 } |
|
2104 |
|
2105 nsresult |
|
2106 nsTreeBodyFrame::GetImage(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext, |
|
2107 nsStyleContext* aStyleContext, bool& aAllowImageRegions, imgIContainer** aResult) |
|
2108 { |
|
2109 *aResult = nullptr; |
|
2110 |
|
2111 nsAutoString imageSrc; |
|
2112 mView->GetImageSrc(aRowIndex, aCol, imageSrc); |
|
2113 nsRefPtr<imgRequestProxy> styleRequest; |
|
2114 if (!aUseContext && !imageSrc.IsEmpty()) { |
|
2115 aAllowImageRegions = false; |
|
2116 } |
|
2117 else { |
|
2118 // Obtain the URL from the style context. |
|
2119 aAllowImageRegions = true; |
|
2120 styleRequest = aStyleContext->StyleList()->GetListStyleImage(); |
|
2121 if (!styleRequest) |
|
2122 return NS_OK; |
|
2123 nsCOMPtr<nsIURI> uri; |
|
2124 styleRequest->GetURI(getter_AddRefs(uri)); |
|
2125 nsAutoCString spec; |
|
2126 uri->GetSpec(spec); |
|
2127 CopyUTF8toUTF16(spec, imageSrc); |
|
2128 } |
|
2129 |
|
2130 // Look the image up in our cache. |
|
2131 nsTreeImageCacheEntry entry; |
|
2132 if (mImageCache.Get(imageSrc, &entry)) { |
|
2133 // Find out if the image has loaded. |
|
2134 uint32_t status; |
|
2135 imgIRequest *imgReq = entry.request; |
|
2136 imgReq->GetImageStatus(&status); |
|
2137 imgReq->GetImage(aResult); // We hand back the image here. The GetImage call addrefs *aResult. |
|
2138 bool animated = true; // Assuming animated is the safe option |
|
2139 |
|
2140 // We can only call GetAnimated if we're decoded |
|
2141 if (*aResult && (status & imgIRequest::STATUS_DECODE_COMPLETE)) |
|
2142 (*aResult)->GetAnimated(&animated); |
|
2143 |
|
2144 if ((!(status & imgIRequest::STATUS_LOAD_COMPLETE)) || animated) { |
|
2145 // We either aren't done loading, or we're animating. Add our row as a listener for invalidations. |
|
2146 nsCOMPtr<imgINotificationObserver> obs; |
|
2147 imgReq->GetNotificationObserver(getter_AddRefs(obs)); |
|
2148 |
|
2149 if (obs) { |
|
2150 static_cast<nsTreeImageListener*> (obs.get())->AddCell(aRowIndex, aCol); |
|
2151 } |
|
2152 |
|
2153 return NS_OK; |
|
2154 } |
|
2155 } |
|
2156 |
|
2157 if (!*aResult) { |
|
2158 // Create a new nsTreeImageListener object and pass it our row and column |
|
2159 // information. |
|
2160 nsTreeImageListener* listener = new nsTreeImageListener(this); |
|
2161 if (!listener) |
|
2162 return NS_ERROR_OUT_OF_MEMORY; |
|
2163 |
|
2164 if (!mCreatedListeners.PutEntry(listener)) { |
|
2165 return NS_ERROR_FAILURE; |
|
2166 } |
|
2167 |
|
2168 listener->AddCell(aRowIndex, aCol); |
|
2169 nsCOMPtr<imgINotificationObserver> imgNotificationObserver = listener; |
|
2170 |
|
2171 nsRefPtr<imgRequestProxy> imageRequest; |
|
2172 if (styleRequest) { |
|
2173 styleRequest->Clone(imgNotificationObserver, getter_AddRefs(imageRequest)); |
|
2174 } else { |
|
2175 nsIDocument* doc = mContent->GetDocument(); |
|
2176 if (!doc) |
|
2177 // The page is currently being torn down. Why bother. |
|
2178 return NS_ERROR_FAILURE; |
|
2179 |
|
2180 nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI(); |
|
2181 |
|
2182 nsCOMPtr<nsIURI> srcURI; |
|
2183 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcURI), |
|
2184 imageSrc, |
|
2185 doc, |
|
2186 baseURI); |
|
2187 if (!srcURI) |
|
2188 return NS_ERROR_FAILURE; |
|
2189 |
|
2190 // XXXbz what's the origin principal for this stuff that comes from our |
|
2191 // view? I guess we should assume that it's the node's principal... |
|
2192 if (nsContentUtils::CanLoadImage(srcURI, mContent, doc, |
|
2193 mContent->NodePrincipal())) { |
|
2194 nsresult rv = nsContentUtils::LoadImage(srcURI, |
|
2195 doc, |
|
2196 mContent->NodePrincipal(), |
|
2197 doc->GetDocumentURI(), |
|
2198 imgNotificationObserver, |
|
2199 nsIRequest::LOAD_NORMAL, |
|
2200 EmptyString(), |
|
2201 getter_AddRefs(imageRequest)); |
|
2202 NS_ENSURE_SUCCESS(rv, rv); |
|
2203 |
|
2204 } |
|
2205 } |
|
2206 listener->UnsuppressInvalidation(); |
|
2207 |
|
2208 if (!imageRequest) |
|
2209 return NS_ERROR_FAILURE; |
|
2210 |
|
2211 // We don't want discarding/decode-on-draw for xul images |
|
2212 imageRequest->StartDecoding(); |
|
2213 imageRequest->LockImage(); |
|
2214 |
|
2215 // In a case it was already cached. |
|
2216 imageRequest->GetImage(aResult); |
|
2217 nsTreeImageCacheEntry cacheEntry(imageRequest, imgNotificationObserver); |
|
2218 mImageCache.Put(imageSrc, cacheEntry); |
|
2219 } |
|
2220 return NS_OK; |
|
2221 } |
|
2222 |
|
2223 nsRect nsTreeBodyFrame::GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext, |
|
2224 nsStyleContext* aStyleContext) |
|
2225 { |
|
2226 // XXX We should respond to visibility rules for collapsed vs. hidden. |
|
2227 |
|
2228 // This method returns the width of the twisty INCLUDING borders and padding. |
|
2229 // It first checks the style context for a width. If none is found, it tries to |
|
2230 // use the default image width for the twisty. If no image is found, it defaults |
|
2231 // to border+padding. |
|
2232 nsRect r(0,0,0,0); |
|
2233 nsMargin bp(0,0,0,0); |
|
2234 GetBorderPadding(aStyleContext, bp); |
|
2235 r.Inflate(bp); |
|
2236 |
|
2237 // Now r contains our border+padding info. We now need to get our width and |
|
2238 // height. |
|
2239 bool needWidth = false; |
|
2240 bool needHeight = false; |
|
2241 |
|
2242 // We have to load image even though we already have a size. |
|
2243 // Don't change this, otherwise things start to go crazy. |
|
2244 bool useImageRegion = true; |
|
2245 nsCOMPtr<imgIContainer> image; |
|
2246 GetImage(aRowIndex, aCol, aUseContext, aStyleContext, useImageRegion, getter_AddRefs(image)); |
|
2247 |
|
2248 const nsStylePosition* myPosition = aStyleContext->StylePosition(); |
|
2249 const nsStyleList* myList = aStyleContext->StyleList(); |
|
2250 |
|
2251 if (useImageRegion) { |
|
2252 r.x += myList->mImageRegion.x; |
|
2253 r.y += myList->mImageRegion.y; |
|
2254 } |
|
2255 |
|
2256 if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) { |
|
2257 int32_t val = myPosition->mWidth.GetCoordValue(); |
|
2258 r.width += val; |
|
2259 } |
|
2260 else if (useImageRegion && myList->mImageRegion.width > 0) |
|
2261 r.width += myList->mImageRegion.width; |
|
2262 else |
|
2263 needWidth = true; |
|
2264 |
|
2265 if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) { |
|
2266 int32_t val = myPosition->mHeight.GetCoordValue(); |
|
2267 r.height += val; |
|
2268 } |
|
2269 else if (useImageRegion && myList->mImageRegion.height > 0) |
|
2270 r.height += myList->mImageRegion.height; |
|
2271 else |
|
2272 needHeight = true; |
|
2273 |
|
2274 if (image) { |
|
2275 if (needWidth || needHeight) { |
|
2276 // Get the natural image size. |
|
2277 |
|
2278 if (needWidth) { |
|
2279 // Get the size from the image. |
|
2280 nscoord width; |
|
2281 image->GetWidth(&width); |
|
2282 r.width += nsPresContext::CSSPixelsToAppUnits(width); |
|
2283 } |
|
2284 |
|
2285 if (needHeight) { |
|
2286 nscoord height; |
|
2287 image->GetHeight(&height); |
|
2288 r.height += nsPresContext::CSSPixelsToAppUnits(height); |
|
2289 } |
|
2290 } |
|
2291 } |
|
2292 |
|
2293 return r; |
|
2294 } |
|
2295 |
|
2296 // GetImageDestSize returns the destination size of the image. |
|
2297 // The width and height do not include borders and padding. |
|
2298 // The width and height have not been adjusted to fit in the row height |
|
2299 // or cell width. |
|
2300 // The width and height reflect the destination size specified in CSS, |
|
2301 // or the image region specified in CSS, or the natural size of the |
|
2302 // image. |
|
2303 // If only the destination width has been specified in CSS, the height is |
|
2304 // calculated to maintain the aspect ratio of the image. |
|
2305 // If only the destination height has been specified in CSS, the width is |
|
2306 // calculated to maintain the aspect ratio of the image. |
|
2307 nsSize |
|
2308 nsTreeBodyFrame::GetImageDestSize(nsStyleContext* aStyleContext, |
|
2309 bool useImageRegion, |
|
2310 imgIContainer* image) |
|
2311 { |
|
2312 nsSize size(0,0); |
|
2313 |
|
2314 // We need to get the width and height. |
|
2315 bool needWidth = false; |
|
2316 bool needHeight = false; |
|
2317 |
|
2318 // Get the style position to see if the CSS has specified the |
|
2319 // destination width/height. |
|
2320 const nsStylePosition* myPosition = aStyleContext->StylePosition(); |
|
2321 |
|
2322 if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) { |
|
2323 // CSS has specified the destination width. |
|
2324 size.width = myPosition->mWidth.GetCoordValue(); |
|
2325 } |
|
2326 else { |
|
2327 // We'll need to get the width of the image/region. |
|
2328 needWidth = true; |
|
2329 } |
|
2330 |
|
2331 if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) { |
|
2332 // CSS has specified the destination height. |
|
2333 size.height = myPosition->mHeight.GetCoordValue(); |
|
2334 } |
|
2335 else { |
|
2336 // We'll need to get the height of the image/region. |
|
2337 needHeight = true; |
|
2338 } |
|
2339 |
|
2340 if (needWidth || needHeight) { |
|
2341 // We need to get the size of the image/region. |
|
2342 nsSize imageSize(0,0); |
|
2343 |
|
2344 const nsStyleList* myList = aStyleContext->StyleList(); |
|
2345 |
|
2346 if (useImageRegion && myList->mImageRegion.width > 0) { |
|
2347 // CSS has specified an image region. |
|
2348 // Use the width of the image region. |
|
2349 imageSize.width = myList->mImageRegion.width; |
|
2350 } |
|
2351 else if (image) { |
|
2352 nscoord width; |
|
2353 image->GetWidth(&width); |
|
2354 imageSize.width = nsPresContext::CSSPixelsToAppUnits(width); |
|
2355 } |
|
2356 |
|
2357 if (useImageRegion && myList->mImageRegion.height > 0) { |
|
2358 // CSS has specified an image region. |
|
2359 // Use the height of the image region. |
|
2360 imageSize.height = myList->mImageRegion.height; |
|
2361 } |
|
2362 else if (image) { |
|
2363 nscoord height; |
|
2364 image->GetHeight(&height); |
|
2365 imageSize.height = nsPresContext::CSSPixelsToAppUnits(height); |
|
2366 } |
|
2367 |
|
2368 if (needWidth) { |
|
2369 if (!needHeight && imageSize.height != 0) { |
|
2370 // The CSS specified the destination height, but not the destination |
|
2371 // width. We need to calculate the width so that we maintain the |
|
2372 // image's aspect ratio. |
|
2373 size.width = imageSize.width * size.height / imageSize.height; |
|
2374 } |
|
2375 else { |
|
2376 size.width = imageSize.width; |
|
2377 } |
|
2378 } |
|
2379 |
|
2380 if (needHeight) { |
|
2381 if (!needWidth && imageSize.width != 0) { |
|
2382 // The CSS specified the destination width, but not the destination |
|
2383 // height. We need to calculate the height so that we maintain the |
|
2384 // image's aspect ratio. |
|
2385 size.height = imageSize.height * size.width / imageSize.width; |
|
2386 } |
|
2387 else { |
|
2388 size.height = imageSize.height; |
|
2389 } |
|
2390 } |
|
2391 } |
|
2392 |
|
2393 return size; |
|
2394 } |
|
2395 |
|
2396 // GetImageSourceRect returns the source rectangle of the image to be |
|
2397 // displayed. |
|
2398 // The width and height reflect the image region specified in CSS, or |
|
2399 // the natural size of the image. |
|
2400 // The width and height do not include borders and padding. |
|
2401 // The width and height do not reflect the destination size specified |
|
2402 // in CSS. |
|
2403 nsRect |
|
2404 nsTreeBodyFrame::GetImageSourceRect(nsStyleContext* aStyleContext, |
|
2405 bool useImageRegion, |
|
2406 imgIContainer* image) |
|
2407 { |
|
2408 nsRect r(0,0,0,0); |
|
2409 |
|
2410 const nsStyleList* myList = aStyleContext->StyleList(); |
|
2411 |
|
2412 if (useImageRegion && |
|
2413 (myList->mImageRegion.width > 0 || myList->mImageRegion.height > 0)) { |
|
2414 // CSS has specified an image region. |
|
2415 r = myList->mImageRegion; |
|
2416 } |
|
2417 else if (image) { |
|
2418 // Use the actual image size. |
|
2419 nscoord coord; |
|
2420 image->GetWidth(&coord); |
|
2421 r.width = nsPresContext::CSSPixelsToAppUnits(coord); |
|
2422 image->GetHeight(&coord); |
|
2423 r.height = nsPresContext::CSSPixelsToAppUnits(coord); |
|
2424 } |
|
2425 |
|
2426 return r; |
|
2427 } |
|
2428 |
|
2429 int32_t nsTreeBodyFrame::GetRowHeight() |
|
2430 { |
|
2431 // Look up the correct height. It is equal to the specified height |
|
2432 // + the specified margins. |
|
2433 mScratchArray.Clear(); |
|
2434 nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); |
|
2435 if (rowContext) { |
|
2436 const nsStylePosition* myPosition = rowContext->StylePosition(); |
|
2437 |
|
2438 nscoord minHeight = 0; |
|
2439 if (myPosition->mMinHeight.GetUnit() == eStyleUnit_Coord) |
|
2440 minHeight = myPosition->mMinHeight.GetCoordValue(); |
|
2441 |
|
2442 nscoord height = 0; |
|
2443 if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) |
|
2444 height = myPosition->mHeight.GetCoordValue(); |
|
2445 |
|
2446 if (height < minHeight) |
|
2447 height = minHeight; |
|
2448 |
|
2449 if (height > 0) { |
|
2450 height = nsPresContext::AppUnitsToIntCSSPixels(height); |
|
2451 height += height % 2; |
|
2452 height = nsPresContext::CSSPixelsToAppUnits(height); |
|
2453 |
|
2454 // XXX Check box-sizing to determine if border/padding should augment the height |
|
2455 // Inflate the height by our margins. |
|
2456 nsRect rowRect(0,0,0,height); |
|
2457 nsMargin rowMargin; |
|
2458 rowContext->StyleMargin()->GetMargin(rowMargin); |
|
2459 rowRect.Inflate(rowMargin); |
|
2460 height = rowRect.height; |
|
2461 return height; |
|
2462 } |
|
2463 } |
|
2464 |
|
2465 return nsPresContext::CSSPixelsToAppUnits(18); // As good a default as any. |
|
2466 } |
|
2467 |
|
2468 int32_t nsTreeBodyFrame::GetIndentation() |
|
2469 { |
|
2470 // Look up the correct indentation. It is equal to the specified indentation width. |
|
2471 mScratchArray.Clear(); |
|
2472 nsStyleContext* indentContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeindentation); |
|
2473 if (indentContext) { |
|
2474 const nsStylePosition* myPosition = indentContext->StylePosition(); |
|
2475 if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) { |
|
2476 nscoord val = myPosition->mWidth.GetCoordValue(); |
|
2477 return val; |
|
2478 } |
|
2479 } |
|
2480 |
|
2481 return nsPresContext::CSSPixelsToAppUnits(16); // As good a default as any. |
|
2482 } |
|
2483 |
|
2484 void nsTreeBodyFrame::CalcInnerBox() |
|
2485 { |
|
2486 mInnerBox.SetRect(0, 0, mRect.width, mRect.height); |
|
2487 AdjustForBorderPadding(mStyleContext, mInnerBox); |
|
2488 } |
|
2489 |
|
2490 nscoord |
|
2491 nsTreeBodyFrame::CalcHorzWidth(const ScrollParts& aParts) |
|
2492 { |
|
2493 // Compute the adjustment to the last column. This varies depending on the |
|
2494 // visibility of the columnpicker and the scrollbar. |
|
2495 if (aParts.mColumnsFrame) |
|
2496 mAdjustWidth = mRect.width - aParts.mColumnsFrame->GetRect().width; |
|
2497 else |
|
2498 mAdjustWidth = 0; |
|
2499 |
|
2500 nscoord width = 0; |
|
2501 |
|
2502 // We calculate this from the scrollable frame, so that it |
|
2503 // properly covers all contingencies of what could be |
|
2504 // scrollable (columns, body, etc...) |
|
2505 |
|
2506 if (aParts.mColumnsScrollFrame) { |
|
2507 width = aParts.mColumnsScrollFrame->GetScrollRange().width + |
|
2508 aParts.mColumnsScrollFrame->GetScrollPortRect().width; |
|
2509 } |
|
2510 |
|
2511 // If no horz scrolling periphery is present, then just return our width |
|
2512 if (width == 0) |
|
2513 width = mRect.width; |
|
2514 |
|
2515 return width; |
|
2516 } |
|
2517 |
|
2518 nsresult |
|
2519 nsTreeBodyFrame::GetCursor(const nsPoint& aPoint, |
|
2520 nsIFrame::Cursor& aCursor) |
|
2521 { |
|
2522 // Check the GetScriptHandlingObject so we don't end up running code when |
|
2523 // the document is a zombie. |
|
2524 bool dummy; |
|
2525 if (mView && GetContent()->GetCurrentDoc()->GetScriptHandlingObject(dummy)) { |
|
2526 int32_t row; |
|
2527 nsTreeColumn* col; |
|
2528 nsIAtom* child; |
|
2529 GetCellAt(aPoint.x, aPoint.y, &row, &col, &child); |
|
2530 |
|
2531 if (child) { |
|
2532 // Our scratch array is already prefilled. |
|
2533 nsStyleContext* childContext = GetPseudoStyleContext(child); |
|
2534 |
|
2535 FillCursorInformationFromStyle(childContext->StyleUserInterface(), |
|
2536 aCursor); |
|
2537 if (aCursor.mCursor == NS_STYLE_CURSOR_AUTO) |
|
2538 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT; |
|
2539 |
|
2540 return NS_OK; |
|
2541 } |
|
2542 } |
|
2543 |
|
2544 return nsLeafBoxFrame::GetCursor(aPoint, aCursor); |
|
2545 } |
|
2546 |
|
2547 static uint32_t GetDropEffect(WidgetGUIEvent* aEvent) |
|
2548 { |
|
2549 NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type"); |
|
2550 WidgetDragEvent* dragEvent = aEvent->AsDragEvent(); |
|
2551 nsContentUtils::SetDataTransferInEvent(dragEvent); |
|
2552 |
|
2553 uint32_t action = 0; |
|
2554 if (dragEvent->dataTransfer) |
|
2555 dragEvent->dataTransfer->GetDropEffectInt(&action); |
|
2556 return action; |
|
2557 } |
|
2558 |
|
2559 nsresult |
|
2560 nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext, |
|
2561 WidgetGUIEvent* aEvent, |
|
2562 nsEventStatus* aEventStatus) |
|
2563 { |
|
2564 if (aEvent->message == NS_MOUSE_ENTER_SYNTH || aEvent->message == NS_MOUSE_MOVE) { |
|
2565 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); |
|
2566 int32_t xTwips = pt.x - mInnerBox.x; |
|
2567 int32_t yTwips = pt.y - mInnerBox.y; |
|
2568 int32_t newrow = GetRowAt(xTwips, yTwips); |
|
2569 if (mMouseOverRow != newrow) { |
|
2570 // redraw the old and the new row |
|
2571 if (mMouseOverRow != -1) |
|
2572 InvalidateRow(mMouseOverRow); |
|
2573 mMouseOverRow = newrow; |
|
2574 if (mMouseOverRow != -1) |
|
2575 InvalidateRow(mMouseOverRow); |
|
2576 } |
|
2577 } |
|
2578 else if (aEvent->message == NS_MOUSE_EXIT_SYNTH) { |
|
2579 if (mMouseOverRow != -1) { |
|
2580 InvalidateRow(mMouseOverRow); |
|
2581 mMouseOverRow = -1; |
|
2582 } |
|
2583 } |
|
2584 else if (aEvent->message == NS_DRAGDROP_ENTER) { |
|
2585 if (!mSlots) |
|
2586 mSlots = new Slots(); |
|
2587 |
|
2588 // Cache several things we'll need throughout the course of our work. These |
|
2589 // will all get released on a drag exit. |
|
2590 |
|
2591 if (mSlots->mTimer) { |
|
2592 mSlots->mTimer->Cancel(); |
|
2593 mSlots->mTimer = nullptr; |
|
2594 } |
|
2595 |
|
2596 // Cache the drag session. |
|
2597 mSlots->mIsDragging = true; |
|
2598 mSlots->mDropRow = -1; |
|
2599 mSlots->mDropOrient = -1; |
|
2600 mSlots->mDragAction = GetDropEffect(aEvent); |
|
2601 } |
|
2602 else if (aEvent->message == NS_DRAGDROP_OVER) { |
|
2603 // The mouse is hovering over this tree. If we determine things are |
|
2604 // different from the last time, invalidate the drop feedback at the old |
|
2605 // position, query the view to see if the current location is droppable, |
|
2606 // and then invalidate the drop feedback at the new location if it is. |
|
2607 // The mouse may or may not have changed position from the last time |
|
2608 // we were called, so optimize out a lot of the extra notifications by |
|
2609 // checking if anything changed first. For drop feedback we use drop, |
|
2610 // dropBefore and dropAfter property. |
|
2611 |
|
2612 if (!mView || !mSlots) |
|
2613 return NS_OK; |
|
2614 |
|
2615 // Save last values, we will need them. |
|
2616 int32_t lastDropRow = mSlots->mDropRow; |
|
2617 int16_t lastDropOrient = mSlots->mDropOrient; |
|
2618 #ifndef XP_MACOSX |
|
2619 int16_t lastScrollLines = mSlots->mScrollLines; |
|
2620 #endif |
|
2621 |
|
2622 // Find out the current drag action |
|
2623 uint32_t lastDragAction = mSlots->mDragAction; |
|
2624 mSlots->mDragAction = GetDropEffect(aEvent); |
|
2625 |
|
2626 // Compute the row mouse is over and the above/below/on state. |
|
2627 // Below we'll use this to see if anything changed. |
|
2628 // Also check if we want to auto-scroll. |
|
2629 ComputeDropPosition(aEvent, &mSlots->mDropRow, &mSlots->mDropOrient, &mSlots->mScrollLines); |
|
2630 |
|
2631 // While we're here, handle tracking of scrolling during a drag. |
|
2632 if (mSlots->mScrollLines) { |
|
2633 if (mSlots->mDropAllowed) { |
|
2634 // Invalidate primary cell at old location. |
|
2635 mSlots->mDropAllowed = false; |
|
2636 InvalidateDropFeedback(lastDropRow, lastDropOrient); |
|
2637 } |
|
2638 #ifdef XP_MACOSX |
|
2639 ScrollByLines(mSlots->mScrollLines); |
|
2640 #else |
|
2641 if (!lastScrollLines) { |
|
2642 // Cancel any previously initialized timer. |
|
2643 if (mSlots->mTimer) { |
|
2644 mSlots->mTimer->Cancel(); |
|
2645 mSlots->mTimer = nullptr; |
|
2646 } |
|
2647 |
|
2648 // Set a timer to trigger the tree scrolling. |
|
2649 CreateTimer(LookAndFeel::eIntID_TreeLazyScrollDelay, |
|
2650 LazyScrollCallback, nsITimer::TYPE_ONE_SHOT, |
|
2651 getter_AddRefs(mSlots->mTimer)); |
|
2652 } |
|
2653 #endif |
|
2654 // Bail out to prevent spring loaded timer and feedback line settings. |
|
2655 return NS_OK; |
|
2656 } |
|
2657 |
|
2658 // If changed from last time, invalidate primary cell at the old location and if allowed, |
|
2659 // invalidate primary cell at the new location. If nothing changed, just bail. |
|
2660 if (mSlots->mDropRow != lastDropRow || |
|
2661 mSlots->mDropOrient != lastDropOrient || |
|
2662 mSlots->mDragAction != lastDragAction) { |
|
2663 |
|
2664 // Invalidate row at the old location. |
|
2665 if (mSlots->mDropAllowed) { |
|
2666 mSlots->mDropAllowed = false; |
|
2667 InvalidateDropFeedback(lastDropRow, lastDropOrient); |
|
2668 } |
|
2669 |
|
2670 if (mSlots->mTimer) { |
|
2671 // Timer is active but for a different row than the current one, kill it. |
|
2672 mSlots->mTimer->Cancel(); |
|
2673 mSlots->mTimer = nullptr; |
|
2674 } |
|
2675 |
|
2676 if (mSlots->mDropRow >= 0) { |
|
2677 if (!mSlots->mTimer && mSlots->mDropOrient == nsITreeView::DROP_ON) { |
|
2678 // Either there wasn't a timer running or it was just killed above. |
|
2679 // If over a folder, start up a timer to open the folder. |
|
2680 bool isContainer = false; |
|
2681 mView->IsContainer(mSlots->mDropRow, &isContainer); |
|
2682 if (isContainer) { |
|
2683 bool isOpen = false; |
|
2684 mView->IsContainerOpen(mSlots->mDropRow, &isOpen); |
|
2685 if (!isOpen) { |
|
2686 // This node isn't expanded, set a timer to expand it. |
|
2687 CreateTimer(LookAndFeel::eIntID_TreeOpenDelay, |
|
2688 OpenCallback, nsITimer::TYPE_ONE_SHOT, |
|
2689 getter_AddRefs(mSlots->mTimer)); |
|
2690 } |
|
2691 } |
|
2692 } |
|
2693 |
|
2694 // The dataTransfer was initialized by the call to GetDropEffect above. |
|
2695 bool canDropAtNewLocation = false; |
|
2696 mView->CanDrop(mSlots->mDropRow, mSlots->mDropOrient, |
|
2697 aEvent->AsDragEvent()->dataTransfer, |
|
2698 &canDropAtNewLocation); |
|
2699 |
|
2700 if (canDropAtNewLocation) { |
|
2701 // Invalidate row at the new location. |
|
2702 mSlots->mDropAllowed = canDropAtNewLocation; |
|
2703 InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient); |
|
2704 } |
|
2705 } |
|
2706 } |
|
2707 |
|
2708 // Indicate that the drop is allowed by preventing the default behaviour. |
|
2709 if (mSlots->mDropAllowed) |
|
2710 *aEventStatus = nsEventStatus_eConsumeNoDefault; |
|
2711 } |
|
2712 else if (aEvent->message == NS_DRAGDROP_DROP) { |
|
2713 // this event was meant for another frame, so ignore it |
|
2714 if (!mSlots) |
|
2715 return NS_OK; |
|
2716 |
|
2717 // Tell the view where the drop happened. |
|
2718 |
|
2719 // Remove the drop folder and all its parents from the array. |
|
2720 int32_t parentIndex; |
|
2721 nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex); |
|
2722 while (NS_SUCCEEDED(rv) && parentIndex >= 0) { |
|
2723 mSlots->mArray.RemoveElement(parentIndex); |
|
2724 rv = mView->GetParentIndex(parentIndex, &parentIndex); |
|
2725 } |
|
2726 |
|
2727 NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type"); |
|
2728 WidgetDragEvent* dragEvent = aEvent->AsDragEvent(); |
|
2729 nsContentUtils::SetDataTransferInEvent(dragEvent); |
|
2730 |
|
2731 mView->Drop(mSlots->mDropRow, mSlots->mDropOrient, dragEvent->dataTransfer); |
|
2732 mSlots->mDropRow = -1; |
|
2733 mSlots->mDropOrient = -1; |
|
2734 mSlots->mIsDragging = false; |
|
2735 *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop |
|
2736 } |
|
2737 else if (aEvent->message == NS_DRAGDROP_EXIT) { |
|
2738 // this event was meant for another frame, so ignore it |
|
2739 if (!mSlots) |
|
2740 return NS_OK; |
|
2741 |
|
2742 // Clear out all our tracking vars. |
|
2743 |
|
2744 if (mSlots->mDropAllowed) { |
|
2745 mSlots->mDropAllowed = false; |
|
2746 InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient); |
|
2747 } |
|
2748 else |
|
2749 mSlots->mDropAllowed = false; |
|
2750 mSlots->mIsDragging = false; |
|
2751 mSlots->mScrollLines = 0; |
|
2752 // If a drop is occuring, the exit event will fire just before the drop |
|
2753 // event, so don't reset mDropRow or mDropOrient as these fields are used |
|
2754 // by the drop event. |
|
2755 if (mSlots->mTimer) { |
|
2756 mSlots->mTimer->Cancel(); |
|
2757 mSlots->mTimer = nullptr; |
|
2758 } |
|
2759 |
|
2760 if (!mSlots->mArray.IsEmpty()) { |
|
2761 // Close all spring loaded folders except the drop folder. |
|
2762 CreateTimer(LookAndFeel::eIntID_TreeCloseDelay, |
|
2763 CloseCallback, nsITimer::TYPE_ONE_SHOT, |
|
2764 getter_AddRefs(mSlots->mTimer)); |
|
2765 } |
|
2766 } |
|
2767 |
|
2768 return NS_OK; |
|
2769 } |
|
2770 |
|
2771 static nsLineStyle |
|
2772 ConvertBorderStyleToLineStyle(uint8_t aBorderStyle) |
|
2773 { |
|
2774 switch (aBorderStyle) { |
|
2775 case NS_STYLE_BORDER_STYLE_DOTTED: |
|
2776 return nsLineStyle_kDotted; |
|
2777 case NS_STYLE_BORDER_STYLE_DASHED: |
|
2778 return nsLineStyle_kDashed; |
|
2779 default: |
|
2780 return nsLineStyle_kSolid; |
|
2781 } |
|
2782 } |
|
2783 |
|
2784 static void |
|
2785 PaintTreeBody(nsIFrame* aFrame, nsRenderingContext* aCtx, |
|
2786 const nsRect& aDirtyRect, nsPoint aPt) |
|
2787 { |
|
2788 static_cast<nsTreeBodyFrame*>(aFrame)->PaintTreeBody(*aCtx, aDirtyRect, aPt); |
|
2789 } |
|
2790 |
|
2791 // Painting routines |
|
2792 void |
|
2793 nsTreeBodyFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
2794 const nsRect& aDirtyRect, |
|
2795 const nsDisplayListSet& aLists) |
|
2796 { |
|
2797 // REVIEW: why did we paint if we were collapsed? that makes no sense! |
|
2798 if (!IsVisibleForPainting(aBuilder)) |
|
2799 return; // We're invisible. Don't paint. |
|
2800 |
|
2801 // Handles painting our background, border, and outline. |
|
2802 nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); |
|
2803 |
|
2804 // Bail out now if there's no view or we can't run script because the |
|
2805 // document is a zombie |
|
2806 if (!mView || !GetContent()->GetCurrentDoc()->GetWindow()) |
|
2807 return; |
|
2808 |
|
2809 aLists.Content()->AppendNewToTop(new (aBuilder) |
|
2810 nsDisplayGeneric(aBuilder, this, ::PaintTreeBody, "XULTreeBody", |
|
2811 nsDisplayItem::TYPE_XUL_TREE_BODY)); |
|
2812 } |
|
2813 |
|
2814 void |
|
2815 nsTreeBodyFrame::PaintTreeBody(nsRenderingContext& aRenderingContext, |
|
2816 const nsRect& aDirtyRect, nsPoint aPt) |
|
2817 { |
|
2818 // Update our available height and our page count. |
|
2819 CalcInnerBox(); |
|
2820 aRenderingContext.PushState(); |
|
2821 aRenderingContext.IntersectClip(mInnerBox + aPt); |
|
2822 int32_t oldPageCount = mPageLength; |
|
2823 if (!mHasFixedRowCount) |
|
2824 mPageLength = mInnerBox.height/mRowHeight; |
|
2825 |
|
2826 if (oldPageCount != mPageLength || mHorzWidth != CalcHorzWidth(GetScrollParts())) { |
|
2827 // Schedule a ResizeReflow that will update our info properly. |
|
2828 PresContext()->PresShell()-> |
|
2829 FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); |
|
2830 } |
|
2831 #ifdef DEBUG |
|
2832 int32_t rowCount = mRowCount; |
|
2833 mView->GetRowCount(&rowCount); |
|
2834 NS_WARN_IF_FALSE(mRowCount == rowCount, "row count changed unexpectedly"); |
|
2835 #endif |
|
2836 |
|
2837 // Loop through our columns and paint them (e.g., for sorting). This is only |
|
2838 // relevant when painting backgrounds, since columns contain no content. Content |
|
2839 // is contained in the rows. |
|
2840 for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; |
|
2841 currCol = currCol->GetNext()) { |
|
2842 nsRect colRect; |
|
2843 nsresult rv = currCol->GetRect(this, mInnerBox.y, mInnerBox.height, |
|
2844 &colRect); |
|
2845 // Don't paint hidden columns. |
|
2846 if (NS_FAILED(rv) || colRect.width == 0) continue; |
|
2847 |
|
2848 if (OffsetForHorzScroll(colRect, false)) { |
|
2849 nsRect dirtyRect; |
|
2850 colRect += aPt; |
|
2851 if (dirtyRect.IntersectRect(aDirtyRect, colRect)) { |
|
2852 PaintColumn(currCol, colRect, PresContext(), aRenderingContext, aDirtyRect); |
|
2853 } |
|
2854 } |
|
2855 } |
|
2856 // Loop through our on-screen rows. |
|
2857 for (int32_t i = mTopRowIndex; i < mRowCount && i <= mTopRowIndex+mPageLength; i++) { |
|
2858 nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*(i-mTopRowIndex), mInnerBox.width, mRowHeight); |
|
2859 nsRect dirtyRect; |
|
2860 if (dirtyRect.IntersectRect(aDirtyRect, rowRect + aPt) && |
|
2861 rowRect.y < (mInnerBox.y+mInnerBox.height)) { |
|
2862 PaintRow(i, rowRect + aPt, PresContext(), aRenderingContext, aDirtyRect, aPt); |
|
2863 } |
|
2864 } |
|
2865 |
|
2866 if (mSlots && mSlots->mDropAllowed && (mSlots->mDropOrient == nsITreeView::DROP_BEFORE || |
|
2867 mSlots->mDropOrient == nsITreeView::DROP_AFTER)) { |
|
2868 nscoord yPos = mInnerBox.y + mRowHeight * (mSlots->mDropRow - mTopRowIndex) - mRowHeight / 2; |
|
2869 nsRect feedbackRect(mInnerBox.x, yPos, mInnerBox.width, mRowHeight); |
|
2870 if (mSlots->mDropOrient == nsITreeView::DROP_AFTER) |
|
2871 feedbackRect.y += mRowHeight; |
|
2872 |
|
2873 nsRect dirtyRect; |
|
2874 feedbackRect += aPt; |
|
2875 if (dirtyRect.IntersectRect(aDirtyRect, feedbackRect)) { |
|
2876 PaintDropFeedback(feedbackRect, PresContext(), aRenderingContext, aDirtyRect, aPt); |
|
2877 } |
|
2878 } |
|
2879 aRenderingContext.PopState(); |
|
2880 } |
|
2881 |
|
2882 |
|
2883 |
|
2884 void |
|
2885 nsTreeBodyFrame::PaintColumn(nsTreeColumn* aColumn, |
|
2886 const nsRect& aColumnRect, |
|
2887 nsPresContext* aPresContext, |
|
2888 nsRenderingContext& aRenderingContext, |
|
2889 const nsRect& aDirtyRect) |
|
2890 { |
|
2891 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
2892 |
|
2893 // Now obtain the properties for our cell. |
|
2894 PrefillPropertyArray(-1, aColumn); |
|
2895 nsAutoString properties; |
|
2896 mView->GetColumnProperties(aColumn, properties); |
|
2897 nsTreeUtils::TokenizeProperties(properties, mScratchArray); |
|
2898 |
|
2899 // Resolve style for the column. It contains all the info we need to lay ourselves |
|
2900 // out and to paint. |
|
2901 nsStyleContext* colContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecolumn); |
|
2902 |
|
2903 // Obtain the margins for the cell and then deflate our rect by that |
|
2904 // amount. The cell is assumed to be contained within the deflated rect. |
|
2905 nsRect colRect(aColumnRect); |
|
2906 nsMargin colMargin; |
|
2907 colContext->StyleMargin()->GetMargin(colMargin); |
|
2908 colRect.Deflate(colMargin); |
|
2909 |
|
2910 PaintBackgroundLayer(colContext, aPresContext, aRenderingContext, colRect, aDirtyRect); |
|
2911 } |
|
2912 |
|
2913 void |
|
2914 nsTreeBodyFrame::PaintRow(int32_t aRowIndex, |
|
2915 const nsRect& aRowRect, |
|
2916 nsPresContext* aPresContext, |
|
2917 nsRenderingContext& aRenderingContext, |
|
2918 const nsRect& aDirtyRect, |
|
2919 nsPoint aPt) |
|
2920 { |
|
2921 // We have been given a rect for our row. We treat this row like a full-blown |
|
2922 // frame, meaning that it can have borders, margins, padding, and a background. |
|
2923 |
|
2924 // Without a view, we have no data. Check for this up front. |
|
2925 if (!mView) |
|
2926 return; |
|
2927 |
|
2928 nsresult rv; |
|
2929 |
|
2930 // Now obtain the properties for our row. |
|
2931 // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused |
|
2932 PrefillPropertyArray(aRowIndex, nullptr); |
|
2933 |
|
2934 nsAutoString properties; |
|
2935 mView->GetRowProperties(aRowIndex, properties); |
|
2936 nsTreeUtils::TokenizeProperties(properties, mScratchArray); |
|
2937 |
|
2938 // Resolve style for the row. It contains all the info we need to lay ourselves |
|
2939 // out and to paint. |
|
2940 nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow); |
|
2941 |
|
2942 // Obtain the margins for the row and then deflate our rect by that |
|
2943 // amount. The row is assumed to be contained within the deflated rect. |
|
2944 nsRect rowRect(aRowRect); |
|
2945 nsMargin rowMargin; |
|
2946 rowContext->StyleMargin()->GetMargin(rowMargin); |
|
2947 rowRect.Deflate(rowMargin); |
|
2948 |
|
2949 // Paint our borders and background for our row rect. |
|
2950 // If a -moz-appearance is provided, use theme drawing only if the current row |
|
2951 // is not selected (since we draw the selection as part of drawing the background). |
|
2952 bool useTheme = false; |
|
2953 nsITheme *theme = nullptr; |
|
2954 const nsStyleDisplay* displayData = rowContext->StyleDisplay(); |
|
2955 if (displayData->mAppearance) { |
|
2956 theme = aPresContext->GetTheme(); |
|
2957 if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance)) |
|
2958 useTheme = true; |
|
2959 } |
|
2960 bool isSelected = false; |
|
2961 nsCOMPtr<nsITreeSelection> selection; |
|
2962 mView->GetSelection(getter_AddRefs(selection)); |
|
2963 if (selection) |
|
2964 selection->IsSelected(aRowIndex, &isSelected); |
|
2965 if (useTheme && !isSelected) { |
|
2966 nsRect dirty; |
|
2967 dirty.IntersectRect(rowRect, aDirtyRect); |
|
2968 theme->DrawWidgetBackground(&aRenderingContext, this, |
|
2969 displayData->mAppearance, rowRect, dirty); |
|
2970 } else { |
|
2971 PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, rowRect, aDirtyRect); |
|
2972 } |
|
2973 |
|
2974 // Adjust the rect for its border and padding. |
|
2975 nsRect originalRowRect = rowRect; |
|
2976 AdjustForBorderPadding(rowContext, rowRect); |
|
2977 |
|
2978 bool isSeparator = false; |
|
2979 mView->IsSeparator(aRowIndex, &isSeparator); |
|
2980 if (isSeparator) { |
|
2981 // The row is a separator. |
|
2982 |
|
2983 nscoord primaryX = rowRect.x; |
|
2984 nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn(); |
|
2985 if (primaryCol) { |
|
2986 // Paint the primary cell. |
|
2987 nsRect cellRect; |
|
2988 rv = primaryCol->GetRect(this, rowRect.y, rowRect.height, &cellRect); |
|
2989 if (NS_FAILED(rv)) { |
|
2990 NS_NOTREACHED("primary column is invalid"); |
|
2991 return; |
|
2992 } |
|
2993 |
|
2994 if (OffsetForHorzScroll(cellRect, false)) { |
|
2995 cellRect.x += aPt.x; |
|
2996 nsRect dirtyRect; |
|
2997 nsRect checkRect(cellRect.x, originalRowRect.y, |
|
2998 cellRect.width, originalRowRect.height); |
|
2999 if (dirtyRect.IntersectRect(aDirtyRect, checkRect)) |
|
3000 PaintCell(aRowIndex, primaryCol, cellRect, aPresContext, |
|
3001 aRenderingContext, aDirtyRect, primaryX, aPt); |
|
3002 } |
|
3003 |
|
3004 // Paint the left side of the separator. |
|
3005 nscoord currX; |
|
3006 nsTreeColumn* previousCol = primaryCol->GetPrevious(); |
|
3007 if (previousCol) { |
|
3008 nsRect prevColRect; |
|
3009 rv = previousCol->GetRect(this, 0, 0, &prevColRect); |
|
3010 if (NS_SUCCEEDED(rv)) { |
|
3011 currX = (prevColRect.x - mHorzPosition) + prevColRect.width + aPt.x; |
|
3012 } else { |
|
3013 NS_NOTREACHED("The column before the primary column is invalid"); |
|
3014 currX = rowRect.x; |
|
3015 } |
|
3016 } else { |
|
3017 currX = rowRect.x; |
|
3018 } |
|
3019 |
|
3020 int32_t level; |
|
3021 mView->GetLevel(aRowIndex, &level); |
|
3022 if (level == 0) |
|
3023 currX += mIndentation; |
|
3024 |
|
3025 if (currX > rowRect.x) { |
|
3026 nsRect separatorRect(rowRect); |
|
3027 separatorRect.width -= rowRect.x + rowRect.width - currX; |
|
3028 PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect); |
|
3029 } |
|
3030 } |
|
3031 |
|
3032 // Paint the right side (whole) separator. |
|
3033 nsRect separatorRect(rowRect); |
|
3034 if (primaryX > rowRect.x) { |
|
3035 separatorRect.width -= primaryX - rowRect.x; |
|
3036 separatorRect.x += primaryX - rowRect.x; |
|
3037 } |
|
3038 PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect); |
|
3039 } |
|
3040 else { |
|
3041 // Now loop over our cells. Only paint a cell if it intersects with our dirty rect. |
|
3042 for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; |
|
3043 currCol = currCol->GetNext()) { |
|
3044 nsRect cellRect; |
|
3045 rv = currCol->GetRect(this, rowRect.y, rowRect.height, &cellRect); |
|
3046 // Don't paint cells in hidden columns. |
|
3047 if (NS_FAILED(rv) || cellRect.width == 0) |
|
3048 continue; |
|
3049 |
|
3050 if (OffsetForHorzScroll(cellRect, false)) { |
|
3051 cellRect.x += aPt.x; |
|
3052 |
|
3053 // for primary columns, use the row's vertical size so that the |
|
3054 // lines get drawn properly |
|
3055 nsRect checkRect = cellRect; |
|
3056 if (currCol->IsPrimary()) |
|
3057 checkRect = nsRect(cellRect.x, originalRowRect.y, |
|
3058 cellRect.width, originalRowRect.height); |
|
3059 |
|
3060 nsRect dirtyRect; |
|
3061 nscoord dummy; |
|
3062 if (dirtyRect.IntersectRect(aDirtyRect, checkRect)) |
|
3063 PaintCell(aRowIndex, currCol, cellRect, aPresContext, |
|
3064 aRenderingContext, aDirtyRect, dummy, aPt); |
|
3065 } |
|
3066 } |
|
3067 } |
|
3068 } |
|
3069 |
|
3070 void |
|
3071 nsTreeBodyFrame::PaintSeparator(int32_t aRowIndex, |
|
3072 const nsRect& aSeparatorRect, |
|
3073 nsPresContext* aPresContext, |
|
3074 nsRenderingContext& aRenderingContext, |
|
3075 const nsRect& aDirtyRect) |
|
3076 { |
|
3077 // Resolve style for the separator. |
|
3078 nsStyleContext* separatorContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeseparator); |
|
3079 bool useTheme = false; |
|
3080 nsITheme *theme = nullptr; |
|
3081 const nsStyleDisplay* displayData = separatorContext->StyleDisplay(); |
|
3082 if ( displayData->mAppearance ) { |
|
3083 theme = aPresContext->GetTheme(); |
|
3084 if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance)) |
|
3085 useTheme = true; |
|
3086 } |
|
3087 |
|
3088 // use -moz-appearance if provided. |
|
3089 if (useTheme) { |
|
3090 nsRect dirty; |
|
3091 dirty.IntersectRect(aSeparatorRect, aDirtyRect); |
|
3092 theme->DrawWidgetBackground(&aRenderingContext, this, |
|
3093 displayData->mAppearance, aSeparatorRect, dirty); |
|
3094 } |
|
3095 else { |
|
3096 const nsStylePosition* stylePosition = separatorContext->StylePosition(); |
|
3097 |
|
3098 // Obtain the height for the separator or use the default value. |
|
3099 nscoord height; |
|
3100 if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord) |
|
3101 height = stylePosition->mHeight.GetCoordValue(); |
|
3102 else { |
|
3103 // Use default height 2px. |
|
3104 height = nsPresContext::CSSPixelsToAppUnits(2); |
|
3105 } |
|
3106 |
|
3107 // Obtain the margins for the separator and then deflate our rect by that |
|
3108 // amount. The separator is assumed to be contained within the deflated rect. |
|
3109 nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, aSeparatorRect.width, height); |
|
3110 nsMargin separatorMargin; |
|
3111 separatorContext->StyleMargin()->GetMargin(separatorMargin); |
|
3112 separatorRect.Deflate(separatorMargin); |
|
3113 |
|
3114 // Center the separator. |
|
3115 separatorRect.y += (aSeparatorRect.height - height) / 2; |
|
3116 |
|
3117 PaintBackgroundLayer(separatorContext, aPresContext, aRenderingContext, separatorRect, aDirtyRect); |
|
3118 } |
|
3119 } |
|
3120 |
|
3121 void |
|
3122 nsTreeBodyFrame::PaintCell(int32_t aRowIndex, |
|
3123 nsTreeColumn* aColumn, |
|
3124 const nsRect& aCellRect, |
|
3125 nsPresContext* aPresContext, |
|
3126 nsRenderingContext& aRenderingContext, |
|
3127 const nsRect& aDirtyRect, |
|
3128 nscoord& aCurrX, |
|
3129 nsPoint aPt) |
|
3130 { |
|
3131 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
3132 |
|
3133 // Now obtain the properties for our cell. |
|
3134 // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused, and the col ID. |
|
3135 PrefillPropertyArray(aRowIndex, aColumn); |
|
3136 nsAutoString properties; |
|
3137 mView->GetCellProperties(aRowIndex, aColumn, properties); |
|
3138 nsTreeUtils::TokenizeProperties(properties, mScratchArray); |
|
3139 |
|
3140 // Resolve style for the cell. It contains all the info we need to lay ourselves |
|
3141 // out and to paint. |
|
3142 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); |
|
3143 |
|
3144 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
|
3145 |
|
3146 // Obtain the margins for the cell and then deflate our rect by that |
|
3147 // amount. The cell is assumed to be contained within the deflated rect. |
|
3148 nsRect cellRect(aCellRect); |
|
3149 nsMargin cellMargin; |
|
3150 cellContext->StyleMargin()->GetMargin(cellMargin); |
|
3151 cellRect.Deflate(cellMargin); |
|
3152 |
|
3153 // Paint our borders and background for our row rect. |
|
3154 PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect, aDirtyRect); |
|
3155 |
|
3156 // Adjust the rect for its border and padding. |
|
3157 AdjustForBorderPadding(cellContext, cellRect); |
|
3158 |
|
3159 nscoord currX = cellRect.x; |
|
3160 nscoord remainingWidth = cellRect.width; |
|
3161 |
|
3162 // Now we paint the contents of the cells. |
|
3163 // Directionality of the tree determines the order in which we paint. |
|
3164 // NS_STYLE_DIRECTION_LTR means paint from left to right. |
|
3165 // NS_STYLE_DIRECTION_RTL means paint from right to left. |
|
3166 |
|
3167 if (aColumn->IsPrimary()) { |
|
3168 // If we're the primary column, we need to indent and paint the twisty and any connecting lines |
|
3169 // between siblings. |
|
3170 |
|
3171 int32_t level; |
|
3172 mView->GetLevel(aRowIndex, &level); |
|
3173 |
|
3174 if (!isRTL) |
|
3175 currX += mIndentation * level; |
|
3176 remainingWidth -= mIndentation * level; |
|
3177 |
|
3178 // Resolve the style to use for the connecting lines. |
|
3179 nsStyleContext* lineContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeline); |
|
3180 |
|
3181 if (mIndentation && level && |
|
3182 lineContext->StyleVisibility()->IsVisibleOrCollapsed()) { |
|
3183 // Paint the thread lines. |
|
3184 |
|
3185 // Get the size of the twisty. We don't want to paint the twisty |
|
3186 // before painting of connecting lines since it would paint lines over |
|
3187 // the twisty. But we need to leave a place for it. |
|
3188 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); |
|
3189 |
|
3190 nsRect imageSize; |
|
3191 nsRect twistyRect(aCellRect); |
|
3192 GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext, |
|
3193 aRenderingContext, twistyContext); |
|
3194 |
|
3195 nsMargin twistyMargin; |
|
3196 twistyContext->StyleMargin()->GetMargin(twistyMargin); |
|
3197 twistyRect.Inflate(twistyMargin); |
|
3198 |
|
3199 aRenderingContext.PushState(); |
|
3200 |
|
3201 const nsStyleBorder* borderStyle = lineContext->StyleBorder(); |
|
3202 nscolor color; |
|
3203 bool foreground; |
|
3204 borderStyle->GetBorderColor(NS_SIDE_LEFT, color, foreground); |
|
3205 if (foreground) { |
|
3206 // GetBorderColor didn't touch color, thus grab it from the treeline context |
|
3207 color = lineContext->StyleColor()->mColor; |
|
3208 } |
|
3209 aRenderingContext.SetColor(color); |
|
3210 uint8_t style; |
|
3211 style = borderStyle->GetBorderStyle(NS_SIDE_LEFT); |
|
3212 aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style)); |
|
3213 |
|
3214 nscoord srcX = currX + twistyRect.width - mIndentation / 2; |
|
3215 nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight + aPt.y; |
|
3216 |
|
3217 // Don't paint off our cell. |
|
3218 if (srcX <= cellRect.x + cellRect.width) { |
|
3219 nscoord destX = currX + twistyRect.width; |
|
3220 if (destX > cellRect.x + cellRect.width) |
|
3221 destX = cellRect.x + cellRect.width; |
|
3222 if (isRTL) { |
|
3223 srcX = currX + remainingWidth - (srcX - cellRect.x); |
|
3224 destX = currX + remainingWidth - (destX - cellRect.x); |
|
3225 } |
|
3226 aRenderingContext.DrawLine(srcX, lineY + mRowHeight / 2, destX, lineY + mRowHeight / 2); |
|
3227 } |
|
3228 |
|
3229 int32_t currentParent = aRowIndex; |
|
3230 for (int32_t i = level; i > 0; i--) { |
|
3231 if (srcX <= cellRect.x + cellRect.width) { |
|
3232 // Paint full vertical line only if we have next sibling. |
|
3233 bool hasNextSibling; |
|
3234 mView->HasNextSibling(currentParent, aRowIndex, &hasNextSibling); |
|
3235 if (hasNextSibling) |
|
3236 aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight); |
|
3237 else if (i == level) |
|
3238 aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight / 2); |
|
3239 } |
|
3240 |
|
3241 int32_t parent; |
|
3242 if (NS_FAILED(mView->GetParentIndex(currentParent, &parent)) || parent < 0) |
|
3243 break; |
|
3244 currentParent = parent; |
|
3245 srcX -= mIndentation; |
|
3246 } |
|
3247 |
|
3248 aRenderingContext.PopState(); |
|
3249 } |
|
3250 |
|
3251 // Always leave space for the twisty. |
|
3252 nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height); |
|
3253 PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext, aRenderingContext, aDirtyRect, |
|
3254 remainingWidth, currX); |
|
3255 } |
|
3256 |
|
3257 // Now paint the icon for our cell. |
|
3258 nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height); |
|
3259 nsRect dirtyRect; |
|
3260 if (dirtyRect.IntersectRect(aDirtyRect, iconRect)) |
|
3261 PaintImage(aRowIndex, aColumn, iconRect, aPresContext, aRenderingContext, aDirtyRect, |
|
3262 remainingWidth, currX); |
|
3263 |
|
3264 // Now paint our element, but only if we aren't a cycler column. |
|
3265 // XXX until we have the ability to load images, allow the view to |
|
3266 // insert text into cycler columns... |
|
3267 if (!aColumn->IsCycler()) { |
|
3268 nsRect elementRect(currX, cellRect.y, remainingWidth, cellRect.height); |
|
3269 nsRect dirtyRect; |
|
3270 if (dirtyRect.IntersectRect(aDirtyRect, elementRect)) { |
|
3271 switch (aColumn->GetType()) { |
|
3272 case nsITreeColumn::TYPE_TEXT: |
|
3273 PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX); |
|
3274 break; |
|
3275 case nsITreeColumn::TYPE_CHECKBOX: |
|
3276 PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect); |
|
3277 break; |
|
3278 case nsITreeColumn::TYPE_PROGRESSMETER: |
|
3279 int32_t state; |
|
3280 mView->GetProgressMode(aRowIndex, aColumn, &state); |
|
3281 switch (state) { |
|
3282 case nsITreeView::PROGRESS_NORMAL: |
|
3283 case nsITreeView::PROGRESS_UNDETERMINED: |
|
3284 PaintProgressMeter(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect); |
|
3285 break; |
|
3286 case nsITreeView::PROGRESS_NONE: |
|
3287 default: |
|
3288 PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX); |
|
3289 break; |
|
3290 } |
|
3291 break; |
|
3292 } |
|
3293 } |
|
3294 } |
|
3295 |
|
3296 aCurrX = currX; |
|
3297 } |
|
3298 |
|
3299 void |
|
3300 nsTreeBodyFrame::PaintTwisty(int32_t aRowIndex, |
|
3301 nsTreeColumn* aColumn, |
|
3302 const nsRect& aTwistyRect, |
|
3303 nsPresContext* aPresContext, |
|
3304 nsRenderingContext& aRenderingContext, |
|
3305 const nsRect& aDirtyRect, |
|
3306 nscoord& aRemainingWidth, |
|
3307 nscoord& aCurrX) |
|
3308 { |
|
3309 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
3310 |
|
3311 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
|
3312 nscoord rightEdge = aCurrX + aRemainingWidth; |
|
3313 // Paint the twisty, but only if we are a non-empty container. |
|
3314 bool shouldPaint = false; |
|
3315 bool isContainer = false; |
|
3316 mView->IsContainer(aRowIndex, &isContainer); |
|
3317 if (isContainer) { |
|
3318 bool isContainerEmpty = false; |
|
3319 mView->IsContainerEmpty(aRowIndex, &isContainerEmpty); |
|
3320 if (!isContainerEmpty) |
|
3321 shouldPaint = true; |
|
3322 } |
|
3323 |
|
3324 // Resolve style for the twisty. |
|
3325 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); |
|
3326 |
|
3327 // Obtain the margins for the twisty and then deflate our rect by that |
|
3328 // amount. The twisty is assumed to be contained within the deflated rect. |
|
3329 nsRect twistyRect(aTwistyRect); |
|
3330 nsMargin twistyMargin; |
|
3331 twistyContext->StyleMargin()->GetMargin(twistyMargin); |
|
3332 twistyRect.Deflate(twistyMargin); |
|
3333 |
|
3334 nsRect imageSize; |
|
3335 nsITheme* theme = GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, |
|
3336 aPresContext, aRenderingContext, twistyContext); |
|
3337 |
|
3338 // Subtract out the remaining width. This is done even when we don't actually paint a twisty in |
|
3339 // this cell, so that cells in different rows still line up. |
|
3340 nsRect copyRect(twistyRect); |
|
3341 copyRect.Inflate(twistyMargin); |
|
3342 aRemainingWidth -= copyRect.width; |
|
3343 if (!isRTL) |
|
3344 aCurrX += copyRect.width; |
|
3345 |
|
3346 if (shouldPaint) { |
|
3347 // Paint our borders and background for our image rect. |
|
3348 PaintBackgroundLayer(twistyContext, aPresContext, aRenderingContext, twistyRect, aDirtyRect); |
|
3349 |
|
3350 if (theme) { |
|
3351 if (isRTL) |
|
3352 twistyRect.x = rightEdge - twistyRect.width; |
|
3353 // yeah, I know it says we're drawing a background, but a twisty is really a fg |
|
3354 // object since it doesn't have anything that gecko would want to draw over it. Besides, |
|
3355 // we have to prevent imagelib from drawing it. |
|
3356 nsRect dirty; |
|
3357 dirty.IntersectRect(twistyRect, aDirtyRect); |
|
3358 theme->DrawWidgetBackground(&aRenderingContext, this, |
|
3359 twistyContext->StyleDisplay()->mAppearance, twistyRect, dirty); |
|
3360 } |
|
3361 else { |
|
3362 // Time to paint the twisty. |
|
3363 // Adjust the rect for its border and padding. |
|
3364 nsMargin bp(0,0,0,0); |
|
3365 GetBorderPadding(twistyContext, bp); |
|
3366 twistyRect.Deflate(bp); |
|
3367 if (isRTL) |
|
3368 twistyRect.x = rightEdge - twistyRect.width; |
|
3369 imageSize.Deflate(bp); |
|
3370 |
|
3371 // Get the image for drawing. |
|
3372 nsCOMPtr<imgIContainer> image; |
|
3373 bool useImageRegion = true; |
|
3374 GetImage(aRowIndex, aColumn, true, twistyContext, useImageRegion, getter_AddRefs(image)); |
|
3375 if (image) { |
|
3376 nsPoint pt = twistyRect.TopLeft(); |
|
3377 |
|
3378 // Center the image. XXX Obey vertical-align style prop? |
|
3379 if (imageSize.height < twistyRect.height) { |
|
3380 pt.y += (twistyRect.height - imageSize.height)/2; |
|
3381 } |
|
3382 |
|
3383 // Paint the image. |
|
3384 nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image, |
|
3385 GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect, |
|
3386 imgIContainer::FLAG_NONE, &imageSize); |
|
3387 } |
|
3388 } |
|
3389 } |
|
3390 } |
|
3391 |
|
3392 void |
|
3393 nsTreeBodyFrame::PaintImage(int32_t aRowIndex, |
|
3394 nsTreeColumn* aColumn, |
|
3395 const nsRect& aImageRect, |
|
3396 nsPresContext* aPresContext, |
|
3397 nsRenderingContext& aRenderingContext, |
|
3398 const nsRect& aDirtyRect, |
|
3399 nscoord& aRemainingWidth, |
|
3400 nscoord& aCurrX) |
|
3401 { |
|
3402 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
3403 |
|
3404 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
|
3405 nscoord rightEdge = aCurrX + aRemainingWidth; |
|
3406 // Resolve style for the image. |
|
3407 nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); |
|
3408 |
|
3409 // Obtain opacity value for the image. |
|
3410 float opacity = imageContext->StyleDisplay()->mOpacity; |
|
3411 |
|
3412 // Obtain the margins for the image and then deflate our rect by that |
|
3413 // amount. The image is assumed to be contained within the deflated rect. |
|
3414 nsRect imageRect(aImageRect); |
|
3415 nsMargin imageMargin; |
|
3416 imageContext->StyleMargin()->GetMargin(imageMargin); |
|
3417 imageRect.Deflate(imageMargin); |
|
3418 |
|
3419 // Get the image. |
|
3420 bool useImageRegion = true; |
|
3421 nsCOMPtr<imgIContainer> image; |
|
3422 GetImage(aRowIndex, aColumn, false, imageContext, useImageRegion, getter_AddRefs(image)); |
|
3423 |
|
3424 // Get the image destination size. |
|
3425 nsSize imageDestSize = GetImageDestSize(imageContext, useImageRegion, image); |
|
3426 if (!imageDestSize.width || !imageDestSize.height) |
|
3427 return; |
|
3428 |
|
3429 // Get the borders and padding. |
|
3430 nsMargin bp(0,0,0,0); |
|
3431 GetBorderPadding(imageContext, bp); |
|
3432 |
|
3433 // destRect will be passed as the aDestRect argument in the DrawImage method. |
|
3434 // Start with the imageDestSize width and height. |
|
3435 nsRect destRect(0, 0, imageDestSize.width, imageDestSize.height); |
|
3436 // Inflate destRect for borders and padding so that we can compare/adjust |
|
3437 // with respect to imageRect. |
|
3438 destRect.Inflate(bp); |
|
3439 |
|
3440 // The destRect width and height have not been adjusted to fit within the |
|
3441 // cell width and height. |
|
3442 // We must adjust the width even if image is null, because the width is used |
|
3443 // to update the aRemainingWidth and aCurrX values. |
|
3444 // Since the height isn't used unless the image is not null, we will adjust |
|
3445 // the height inside the if (image) block below. |
|
3446 |
|
3447 if (destRect.width > imageRect.width) { |
|
3448 // The destRect is too wide to fit within the cell width. |
|
3449 // Adjust destRect width to fit within the cell width. |
|
3450 destRect.width = imageRect.width; |
|
3451 } |
|
3452 else { |
|
3453 // The cell is wider than the destRect. |
|
3454 // In a cycler column, the image is centered horizontally. |
|
3455 if (!aColumn->IsCycler()) { |
|
3456 // If this column is not a cycler, we won't center the image horizontally. |
|
3457 // We adjust the imageRect width so that the image is placed at the start |
|
3458 // of the cell. |
|
3459 imageRect.width = destRect.width; |
|
3460 } |
|
3461 } |
|
3462 |
|
3463 if (image) { |
|
3464 if (isRTL) |
|
3465 imageRect.x = rightEdge - imageRect.width; |
|
3466 // Paint our borders and background for our image rect |
|
3467 PaintBackgroundLayer(imageContext, aPresContext, aRenderingContext, imageRect, aDirtyRect); |
|
3468 |
|
3469 // The destRect x and y have not been set yet. Let's do that now. |
|
3470 // Initially, we use the imageRect x and y. |
|
3471 destRect.x = imageRect.x; |
|
3472 destRect.y = imageRect.y; |
|
3473 |
|
3474 if (destRect.width < imageRect.width) { |
|
3475 // The destRect width is smaller than the cell width. |
|
3476 // Center the image horizontally in the cell. |
|
3477 // Adjust the destRect x accordingly. |
|
3478 destRect.x += (imageRect.width - destRect.width)/2; |
|
3479 } |
|
3480 |
|
3481 // Now it's time to adjust the destRect height to fit within the cell height. |
|
3482 if (destRect.height > imageRect.height) { |
|
3483 // The destRect height is larger than the cell height. |
|
3484 // Adjust destRect height to fit within the cell height. |
|
3485 destRect.height = imageRect.height; |
|
3486 } |
|
3487 else if (destRect.height < imageRect.height) { |
|
3488 // The destRect height is smaller than the cell height. |
|
3489 // Center the image vertically in the cell. |
|
3490 // Adjust the destRect y accordingly. |
|
3491 destRect.y += (imageRect.height - destRect.height)/2; |
|
3492 } |
|
3493 |
|
3494 // It's almost time to paint the image. |
|
3495 // Deflate destRect for the border and padding. |
|
3496 destRect.Deflate(bp); |
|
3497 |
|
3498 // Get the image source rectangle - the rectangle containing the part of |
|
3499 // the image that we are going to display. |
|
3500 // sourceRect will be passed as the aSrcRect argument in the DrawImage method. |
|
3501 nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image); |
|
3502 |
|
3503 // Let's say that the image is 100 pixels tall and |
|
3504 // that the CSS has specified that the destination height should be 50 |
|
3505 // pixels tall. Let's say that the cell height is only 20 pixels. So, in |
|
3506 // those 20 visible pixels, we want to see the top 20/50ths of the image. |
|
3507 // So, the sourceRect.height should be 100 * 20 / 50, which is 40 pixels. |
|
3508 // Essentially, we are scaling the image as dictated by the CSS destination |
|
3509 // height and width, and we are then clipping the scaled image by the cell |
|
3510 // width and height. |
|
3511 nsIntSize rawImageSize; |
|
3512 image->GetWidth(&rawImageSize.width); |
|
3513 image->GetHeight(&rawImageSize.height); |
|
3514 nsRect wholeImageDest = |
|
3515 nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect, |
|
3516 nsRect(destRect.TopLeft(), imageDestSize)); |
|
3517 |
|
3518 gfxContext* ctx = aRenderingContext.ThebesContext(); |
|
3519 if (opacity != 1.0f) { |
|
3520 ctx->PushGroup(gfxContentType::COLOR_ALPHA); |
|
3521 } |
|
3522 |
|
3523 nsLayoutUtils::DrawImage(&aRenderingContext, image, |
|
3524 nsLayoutUtils::GetGraphicsFilterForFrame(this), |
|
3525 wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect, |
|
3526 imgIContainer::FLAG_NONE); |
|
3527 |
|
3528 if (opacity != 1.0f) { |
|
3529 ctx->PopGroupToSource(); |
|
3530 ctx->Paint(opacity); |
|
3531 } |
|
3532 } |
|
3533 |
|
3534 // Update the aRemainingWidth and aCurrX values. |
|
3535 imageRect.Inflate(imageMargin); |
|
3536 aRemainingWidth -= imageRect.width; |
|
3537 if (!isRTL) |
|
3538 aCurrX += imageRect.width; |
|
3539 } |
|
3540 |
|
3541 void |
|
3542 nsTreeBodyFrame::PaintText(int32_t aRowIndex, |
|
3543 nsTreeColumn* aColumn, |
|
3544 const nsRect& aTextRect, |
|
3545 nsPresContext* aPresContext, |
|
3546 nsRenderingContext& aRenderingContext, |
|
3547 const nsRect& aDirtyRect, |
|
3548 nscoord& aCurrX) |
|
3549 { |
|
3550 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
3551 |
|
3552 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
|
3553 |
|
3554 // Now obtain the text for our cell. |
|
3555 nsAutoString text; |
|
3556 mView->GetCellText(aRowIndex, aColumn, text); |
|
3557 // We're going to paint this text so we need to ensure bidi is enabled if |
|
3558 // necessary |
|
3559 CheckTextForBidi(text); |
|
3560 |
|
3561 if (text.Length() == 0) |
|
3562 return; // Don't paint an empty string. XXX What about background/borders? Still paint? |
|
3563 |
|
3564 // Resolve style for the text. It contains all the info we need to lay ourselves |
|
3565 // out and to paint. |
|
3566 nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); |
|
3567 |
|
3568 // Obtain opacity value for the image. |
|
3569 float opacity = textContext->StyleDisplay()->mOpacity; |
|
3570 |
|
3571 // Obtain the margins for the text and then deflate our rect by that |
|
3572 // amount. The text is assumed to be contained within the deflated rect. |
|
3573 nsRect textRect(aTextRect); |
|
3574 nsMargin textMargin; |
|
3575 textContext->StyleMargin()->GetMargin(textMargin); |
|
3576 textRect.Deflate(textMargin); |
|
3577 |
|
3578 // Adjust the rect for its border and padding. |
|
3579 nsMargin bp(0,0,0,0); |
|
3580 GetBorderPadding(textContext, bp); |
|
3581 textRect.Deflate(bp); |
|
3582 |
|
3583 // Compute our text size. |
|
3584 nsRefPtr<nsFontMetrics> fontMet; |
|
3585 nsLayoutUtils::GetFontMetricsForStyleContext(textContext, |
|
3586 getter_AddRefs(fontMet)); |
|
3587 |
|
3588 nscoord height = fontMet->MaxHeight(); |
|
3589 nscoord baseline = fontMet->MaxAscent(); |
|
3590 |
|
3591 // Center the text. XXX Obey vertical-align style prop? |
|
3592 if (height < textRect.height) { |
|
3593 textRect.y += (textRect.height - height)/2; |
|
3594 textRect.height = height; |
|
3595 } |
|
3596 |
|
3597 // Set our font. |
|
3598 aRenderingContext.SetFont(fontMet); |
|
3599 |
|
3600 AdjustForCellText(text, aRowIndex, aColumn, aRenderingContext, textRect); |
|
3601 textRect.Inflate(bp); |
|
3602 |
|
3603 // Subtract out the remaining width. |
|
3604 if (!isRTL) |
|
3605 aCurrX += textRect.width + textMargin.LeftRight(); |
|
3606 |
|
3607 PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, textRect, aDirtyRect); |
|
3608 |
|
3609 // Time to paint our text. |
|
3610 textRect.Deflate(bp); |
|
3611 |
|
3612 // Set our color. |
|
3613 aRenderingContext.SetColor(textContext->StyleColor()->mColor); |
|
3614 |
|
3615 // Draw decorations. |
|
3616 uint8_t decorations = textContext->StyleTextReset()->mTextDecorationLine; |
|
3617 |
|
3618 nscoord offset; |
|
3619 nscoord size; |
|
3620 if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) { |
|
3621 fontMet->GetUnderline(offset, size); |
|
3622 if (decorations & NS_FONT_DECORATION_OVERLINE) |
|
3623 aRenderingContext.FillRect(textRect.x, textRect.y, textRect.width, size); |
|
3624 if (decorations & NS_FONT_DECORATION_UNDERLINE) |
|
3625 aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size); |
|
3626 } |
|
3627 if (decorations & NS_FONT_DECORATION_LINE_THROUGH) { |
|
3628 fontMet->GetStrikeout(offset, size); |
|
3629 aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size); |
|
3630 } |
|
3631 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell); |
|
3632 |
|
3633 gfxContext* ctx = aRenderingContext.ThebesContext(); |
|
3634 if (opacity != 1.0f) { |
|
3635 ctx->PushGroup(gfxContentType::COLOR_ALPHA); |
|
3636 } |
|
3637 |
|
3638 nsLayoutUtils::DrawString(this, &aRenderingContext, text.get(), text.Length(), |
|
3639 textRect.TopLeft() + nsPoint(0, baseline), cellContext); |
|
3640 |
|
3641 if (opacity != 1.0f) { |
|
3642 ctx->PopGroupToSource(); |
|
3643 ctx->Paint(opacity); |
|
3644 } |
|
3645 |
|
3646 } |
|
3647 |
|
3648 void |
|
3649 nsTreeBodyFrame::PaintCheckbox(int32_t aRowIndex, |
|
3650 nsTreeColumn* aColumn, |
|
3651 const nsRect& aCheckboxRect, |
|
3652 nsPresContext* aPresContext, |
|
3653 nsRenderingContext& aRenderingContext, |
|
3654 const nsRect& aDirtyRect) |
|
3655 { |
|
3656 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
3657 |
|
3658 // Resolve style for the checkbox. |
|
3659 nsStyleContext* checkboxContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecheckbox); |
|
3660 |
|
3661 nscoord rightEdge = aCheckboxRect.XMost(); |
|
3662 |
|
3663 // Obtain the margins for the checkbox and then deflate our rect by that |
|
3664 // amount. The checkbox is assumed to be contained within the deflated rect. |
|
3665 nsRect checkboxRect(aCheckboxRect); |
|
3666 nsMargin checkboxMargin; |
|
3667 checkboxContext->StyleMargin()->GetMargin(checkboxMargin); |
|
3668 checkboxRect.Deflate(checkboxMargin); |
|
3669 |
|
3670 nsRect imageSize = GetImageSize(aRowIndex, aColumn, true, checkboxContext); |
|
3671 |
|
3672 if (imageSize.height > checkboxRect.height) |
|
3673 imageSize.height = checkboxRect.height; |
|
3674 if (imageSize.width > checkboxRect.width) |
|
3675 imageSize.width = checkboxRect.width; |
|
3676 |
|
3677 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) |
|
3678 checkboxRect.x = rightEdge - checkboxRect.width; |
|
3679 |
|
3680 // Paint our borders and background for our image rect. |
|
3681 PaintBackgroundLayer(checkboxContext, aPresContext, aRenderingContext, checkboxRect, aDirtyRect); |
|
3682 |
|
3683 // Time to paint the checkbox. |
|
3684 // Adjust the rect for its border and padding. |
|
3685 nsMargin bp(0,0,0,0); |
|
3686 GetBorderPadding(checkboxContext, bp); |
|
3687 checkboxRect.Deflate(bp); |
|
3688 |
|
3689 // Get the image for drawing. |
|
3690 nsCOMPtr<imgIContainer> image; |
|
3691 bool useImageRegion = true; |
|
3692 GetImage(aRowIndex, aColumn, true, checkboxContext, useImageRegion, getter_AddRefs(image)); |
|
3693 if (image) { |
|
3694 nsPoint pt = checkboxRect.TopLeft(); |
|
3695 |
|
3696 if (imageSize.height < checkboxRect.height) { |
|
3697 pt.y += (checkboxRect.height - imageSize.height)/2; |
|
3698 } |
|
3699 |
|
3700 if (imageSize.width < checkboxRect.width) { |
|
3701 pt.x += (checkboxRect.width - imageSize.width)/2; |
|
3702 } |
|
3703 |
|
3704 // Paint the image. |
|
3705 nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image, |
|
3706 GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect, |
|
3707 imgIContainer::FLAG_NONE, &imageSize); |
|
3708 } |
|
3709 } |
|
3710 |
|
3711 void |
|
3712 nsTreeBodyFrame::PaintProgressMeter(int32_t aRowIndex, |
|
3713 nsTreeColumn* aColumn, |
|
3714 const nsRect& aProgressMeterRect, |
|
3715 nsPresContext* aPresContext, |
|
3716 nsRenderingContext& aRenderingContext, |
|
3717 const nsRect& aDirtyRect) |
|
3718 { |
|
3719 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); |
|
3720 |
|
3721 // Resolve style for the progress meter. It contains all the info we need |
|
3722 // to lay ourselves out and to paint. |
|
3723 nsStyleContext* meterContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeprogressmeter); |
|
3724 |
|
3725 // Obtain the margins for the progress meter and then deflate our rect by that |
|
3726 // amount. The progress meter is assumed to be contained within the deflated |
|
3727 // rect. |
|
3728 nsRect meterRect(aProgressMeterRect); |
|
3729 nsMargin meterMargin; |
|
3730 meterContext->StyleMargin()->GetMargin(meterMargin); |
|
3731 meterRect.Deflate(meterMargin); |
|
3732 |
|
3733 // Paint our borders and background for our progress meter rect. |
|
3734 PaintBackgroundLayer(meterContext, aPresContext, aRenderingContext, meterRect, aDirtyRect); |
|
3735 |
|
3736 // Time to paint our progress. |
|
3737 int32_t state; |
|
3738 mView->GetProgressMode(aRowIndex, aColumn, &state); |
|
3739 if (state == nsITreeView::PROGRESS_NORMAL) { |
|
3740 // Adjust the rect for its border and padding. |
|
3741 AdjustForBorderPadding(meterContext, meterRect); |
|
3742 |
|
3743 // Set our color. |
|
3744 aRenderingContext.SetColor(meterContext->StyleColor()->mColor); |
|
3745 |
|
3746 // Now obtain the value for our cell. |
|
3747 nsAutoString value; |
|
3748 mView->GetCellValue(aRowIndex, aColumn, value); |
|
3749 |
|
3750 nsresult rv; |
|
3751 int32_t intValue = value.ToInteger(&rv); |
|
3752 if (intValue < 0) |
|
3753 intValue = 0; |
|
3754 else if (intValue > 100) |
|
3755 intValue = 100; |
|
3756 |
|
3757 nscoord meterWidth = NSToCoordRound((float)intValue / 100 * meterRect.width); |
|
3758 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) |
|
3759 meterRect.x += meterRect.width - meterWidth; // right align |
|
3760 meterRect.width = meterWidth; |
|
3761 bool useImageRegion = true; |
|
3762 nsCOMPtr<imgIContainer> image; |
|
3763 GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image)); |
|
3764 if (image) { |
|
3765 int32_t width, height; |
|
3766 image->GetWidth(&width); |
|
3767 image->GetHeight(&height); |
|
3768 nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(), |
|
3769 height*nsDeviceContext::AppUnitsPerCSSPixel()); |
|
3770 nsLayoutUtils::DrawImage(&aRenderingContext, image, |
|
3771 nsLayoutUtils::GetGraphicsFilterForFrame(this), |
|
3772 nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), |
|
3773 aDirtyRect, imgIContainer::FLAG_NONE); |
|
3774 } else { |
|
3775 aRenderingContext.FillRect(meterRect); |
|
3776 } |
|
3777 } |
|
3778 else if (state == nsITreeView::PROGRESS_UNDETERMINED) { |
|
3779 // Adjust the rect for its border and padding. |
|
3780 AdjustForBorderPadding(meterContext, meterRect); |
|
3781 |
|
3782 bool useImageRegion = true; |
|
3783 nsCOMPtr<imgIContainer> image; |
|
3784 GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image)); |
|
3785 if (image) { |
|
3786 int32_t width, height; |
|
3787 image->GetWidth(&width); |
|
3788 image->GetHeight(&height); |
|
3789 nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(), |
|
3790 height*nsDeviceContext::AppUnitsPerCSSPixel()); |
|
3791 nsLayoutUtils::DrawImage(&aRenderingContext, image, |
|
3792 nsLayoutUtils::GetGraphicsFilterForFrame(this), |
|
3793 nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), |
|
3794 aDirtyRect, imgIContainer::FLAG_NONE); |
|
3795 } |
|
3796 } |
|
3797 } |
|
3798 |
|
3799 |
|
3800 void |
|
3801 nsTreeBodyFrame::PaintDropFeedback(const nsRect& aDropFeedbackRect, |
|
3802 nsPresContext* aPresContext, |
|
3803 nsRenderingContext& aRenderingContext, |
|
3804 const nsRect& aDirtyRect, |
|
3805 nsPoint aPt) |
|
3806 { |
|
3807 // Paint the drop feedback in between rows. |
|
3808 |
|
3809 nscoord currX; |
|
3810 |
|
3811 // Adjust for the primary cell. |
|
3812 nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn(); |
|
3813 |
|
3814 if (primaryCol) { |
|
3815 #ifdef DEBUG |
|
3816 nsresult rv = |
|
3817 #endif |
|
3818 primaryCol->GetXInTwips(this, &currX); |
|
3819 NS_ASSERTION(NS_SUCCEEDED(rv), "primary column is invalid?"); |
|
3820 |
|
3821 currX += aPt.x - mHorzPosition; |
|
3822 } else { |
|
3823 currX = aDropFeedbackRect.x; |
|
3824 } |
|
3825 |
|
3826 PrefillPropertyArray(mSlots->mDropRow, primaryCol); |
|
3827 |
|
3828 // Resolve the style to use for the drop feedback. |
|
3829 nsStyleContext* feedbackContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreedropfeedback); |
|
3830 |
|
3831 // Paint only if it is visible. |
|
3832 if (feedbackContext->StyleVisibility()->IsVisibleOrCollapsed()) { |
|
3833 int32_t level; |
|
3834 mView->GetLevel(mSlots->mDropRow, &level); |
|
3835 |
|
3836 // If our previous or next row has greater level use that for |
|
3837 // correct visual indentation. |
|
3838 if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) { |
|
3839 if (mSlots->mDropRow > 0) { |
|
3840 int32_t previousLevel; |
|
3841 mView->GetLevel(mSlots->mDropRow - 1, &previousLevel); |
|
3842 if (previousLevel > level) |
|
3843 level = previousLevel; |
|
3844 } |
|
3845 } |
|
3846 else { |
|
3847 if (mSlots->mDropRow < mRowCount - 1) { |
|
3848 int32_t nextLevel; |
|
3849 mView->GetLevel(mSlots->mDropRow + 1, &nextLevel); |
|
3850 if (nextLevel > level) |
|
3851 level = nextLevel; |
|
3852 } |
|
3853 } |
|
3854 |
|
3855 currX += mIndentation * level; |
|
3856 |
|
3857 if (primaryCol){ |
|
3858 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); |
|
3859 nsRect imageSize; |
|
3860 nsRect twistyRect; |
|
3861 GetTwistyRect(mSlots->mDropRow, primaryCol, imageSize, twistyRect, aPresContext, |
|
3862 aRenderingContext, twistyContext); |
|
3863 nsMargin twistyMargin; |
|
3864 twistyContext->StyleMargin()->GetMargin(twistyMargin); |
|
3865 twistyRect.Inflate(twistyMargin); |
|
3866 currX += twistyRect.width; |
|
3867 } |
|
3868 |
|
3869 const nsStylePosition* stylePosition = feedbackContext->StylePosition(); |
|
3870 |
|
3871 // Obtain the width for the drop feedback or use default value. |
|
3872 nscoord width; |
|
3873 if (stylePosition->mWidth.GetUnit() == eStyleUnit_Coord) |
|
3874 width = stylePosition->mWidth.GetCoordValue(); |
|
3875 else { |
|
3876 // Use default width 50px. |
|
3877 width = nsPresContext::CSSPixelsToAppUnits(50); |
|
3878 } |
|
3879 |
|
3880 // Obtain the height for the drop feedback or use default value. |
|
3881 nscoord height; |
|
3882 if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord) |
|
3883 height = stylePosition->mHeight.GetCoordValue(); |
|
3884 else { |
|
3885 // Use default height 2px. |
|
3886 height = nsPresContext::CSSPixelsToAppUnits(2); |
|
3887 } |
|
3888 |
|
3889 // Obtain the margins for the drop feedback and then deflate our rect |
|
3890 // by that amount. |
|
3891 nsRect feedbackRect(currX, aDropFeedbackRect.y, width, height); |
|
3892 nsMargin margin; |
|
3893 feedbackContext->StyleMargin()->GetMargin(margin); |
|
3894 feedbackRect.Deflate(margin); |
|
3895 |
|
3896 feedbackRect.y += (aDropFeedbackRect.height - height) / 2; |
|
3897 |
|
3898 // Finally paint the drop feedback. |
|
3899 PaintBackgroundLayer(feedbackContext, aPresContext, aRenderingContext, feedbackRect, aDirtyRect); |
|
3900 } |
|
3901 } |
|
3902 |
|
3903 void |
|
3904 nsTreeBodyFrame::PaintBackgroundLayer(nsStyleContext* aStyleContext, |
|
3905 nsPresContext* aPresContext, |
|
3906 nsRenderingContext& aRenderingContext, |
|
3907 const nsRect& aRect, |
|
3908 const nsRect& aDirtyRect) |
|
3909 { |
|
3910 const nsStyleBorder* myBorder = aStyleContext->StyleBorder(); |
|
3911 |
|
3912 nsCSSRendering::PaintBackgroundWithSC(aPresContext, aRenderingContext, |
|
3913 this, aDirtyRect, aRect, |
|
3914 aStyleContext, *myBorder, |
|
3915 nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES); |
|
3916 |
|
3917 nsCSSRendering::PaintBorderWithStyleBorder(aPresContext, aRenderingContext, |
|
3918 this, aDirtyRect, aRect, |
|
3919 *myBorder, mStyleContext); |
|
3920 |
|
3921 nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this, |
|
3922 aDirtyRect, aRect, aStyleContext); |
|
3923 } |
|
3924 |
|
3925 // Scrolling |
|
3926 nsresult |
|
3927 nsTreeBodyFrame::EnsureRowIsVisible(int32_t aRow) |
|
3928 { |
|
3929 ScrollParts parts = GetScrollParts(); |
|
3930 nsresult rv = EnsureRowIsVisibleInternal(parts, aRow); |
|
3931 NS_ENSURE_SUCCESS(rv, rv); |
|
3932 UpdateScrollbars(parts); |
|
3933 return rv; |
|
3934 } |
|
3935 |
|
3936 nsresult nsTreeBodyFrame::EnsureRowIsVisibleInternal(const ScrollParts& aParts, int32_t aRow) |
|
3937 { |
|
3938 if (!mView || !mPageLength) |
|
3939 return NS_OK; |
|
3940 |
|
3941 if (mTopRowIndex <= aRow && mTopRowIndex+mPageLength > aRow) |
|
3942 return NS_OK; |
|
3943 |
|
3944 if (aRow < mTopRowIndex) |
|
3945 ScrollToRowInternal(aParts, aRow); |
|
3946 else { |
|
3947 // Bring it just on-screen. |
|
3948 int32_t distance = aRow - (mTopRowIndex+mPageLength)+1; |
|
3949 ScrollToRowInternal(aParts, mTopRowIndex+distance); |
|
3950 } |
|
3951 |
|
3952 return NS_OK; |
|
3953 } |
|
3954 |
|
3955 nsresult |
|
3956 nsTreeBodyFrame::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol) |
|
3957 { |
|
3958 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol); |
|
3959 if (!col) |
|
3960 return NS_ERROR_INVALID_ARG; |
|
3961 |
|
3962 ScrollParts parts = GetScrollParts(); |
|
3963 |
|
3964 nscoord result = -1; |
|
3965 nsresult rv; |
|
3966 |
|
3967 nscoord columnPos; |
|
3968 rv = col->GetXInTwips(this, &columnPos); |
|
3969 if(NS_FAILED(rv)) return rv; |
|
3970 |
|
3971 nscoord columnWidth; |
|
3972 rv = col->GetWidthInTwips(this, &columnWidth); |
|
3973 if(NS_FAILED(rv)) return rv; |
|
3974 |
|
3975 // If the start of the column is before the |
|
3976 // start of the horizontal view, then scroll |
|
3977 if (columnPos < mHorzPosition) |
|
3978 result = columnPos; |
|
3979 // If the end of the column is past the end of |
|
3980 // the horizontal view, then scroll |
|
3981 else if ((columnPos + columnWidth) > (mHorzPosition + mInnerBox.width)) |
|
3982 result = ((columnPos + columnWidth) - (mHorzPosition + mInnerBox.width)) + mHorzPosition; |
|
3983 |
|
3984 if (result != -1) { |
|
3985 rv = ScrollHorzInternal(parts, result); |
|
3986 if(NS_FAILED(rv)) return rv; |
|
3987 } |
|
3988 |
|
3989 rv = EnsureRowIsVisibleInternal(parts, aRow); |
|
3990 NS_ENSURE_SUCCESS(rv, rv); |
|
3991 UpdateScrollbars(parts); |
|
3992 return rv; |
|
3993 } |
|
3994 |
|
3995 nsresult |
|
3996 nsTreeBodyFrame::ScrollToCell(int32_t aRow, nsITreeColumn* aCol) |
|
3997 { |
|
3998 ScrollParts parts = GetScrollParts(); |
|
3999 nsresult rv = ScrollToRowInternal(parts, aRow); |
|
4000 NS_ENSURE_SUCCESS(rv, rv); |
|
4001 |
|
4002 rv = ScrollToColumnInternal(parts, aCol); |
|
4003 NS_ENSURE_SUCCESS(rv, rv); |
|
4004 |
|
4005 UpdateScrollbars(parts); |
|
4006 return rv; |
|
4007 } |
|
4008 |
|
4009 nsresult |
|
4010 nsTreeBodyFrame::ScrollToColumn(nsITreeColumn* aCol) |
|
4011 { |
|
4012 ScrollParts parts = GetScrollParts(); |
|
4013 nsresult rv = ScrollToColumnInternal(parts, aCol); |
|
4014 NS_ENSURE_SUCCESS(rv, rv); |
|
4015 UpdateScrollbars(parts); |
|
4016 return rv; |
|
4017 } |
|
4018 |
|
4019 nsresult nsTreeBodyFrame::ScrollToColumnInternal(const ScrollParts& aParts, |
|
4020 nsITreeColumn* aCol) |
|
4021 { |
|
4022 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol); |
|
4023 if (!col) |
|
4024 return NS_ERROR_INVALID_ARG; |
|
4025 |
|
4026 nscoord x; |
|
4027 nsresult rv = col->GetXInTwips(this, &x); |
|
4028 if (NS_FAILED(rv)) |
|
4029 return rv; |
|
4030 |
|
4031 return ScrollHorzInternal(aParts, x); |
|
4032 } |
|
4033 |
|
4034 nsresult |
|
4035 nsTreeBodyFrame::ScrollToHorizontalPosition(int32_t aHorizontalPosition) |
|
4036 { |
|
4037 ScrollParts parts = GetScrollParts(); |
|
4038 int32_t position = nsPresContext::CSSPixelsToAppUnits(aHorizontalPosition); |
|
4039 nsresult rv = ScrollHorzInternal(parts, position); |
|
4040 NS_ENSURE_SUCCESS(rv, rv); |
|
4041 UpdateScrollbars(parts); |
|
4042 return rv; |
|
4043 } |
|
4044 |
|
4045 nsresult |
|
4046 nsTreeBodyFrame::ScrollToRow(int32_t aRow) |
|
4047 { |
|
4048 ScrollParts parts = GetScrollParts(); |
|
4049 nsresult rv = ScrollToRowInternal(parts, aRow); |
|
4050 NS_ENSURE_SUCCESS(rv, rv); |
|
4051 UpdateScrollbars(parts); |
|
4052 return rv; |
|
4053 } |
|
4054 |
|
4055 nsresult nsTreeBodyFrame::ScrollToRowInternal(const ScrollParts& aParts, int32_t aRow) |
|
4056 { |
|
4057 ScrollInternal(aParts, aRow); |
|
4058 |
|
4059 return NS_OK; |
|
4060 } |
|
4061 |
|
4062 nsresult |
|
4063 nsTreeBodyFrame::ScrollByLines(int32_t aNumLines) |
|
4064 { |
|
4065 if (!mView) |
|
4066 return NS_OK; |
|
4067 |
|
4068 int32_t newIndex = mTopRowIndex + aNumLines; |
|
4069 if (newIndex < 0) |
|
4070 newIndex = 0; |
|
4071 else { |
|
4072 int32_t lastPageTopRow = mRowCount - mPageLength; |
|
4073 if (newIndex > lastPageTopRow) |
|
4074 newIndex = lastPageTopRow; |
|
4075 } |
|
4076 ScrollToRow(newIndex); |
|
4077 |
|
4078 return NS_OK; |
|
4079 } |
|
4080 |
|
4081 nsresult |
|
4082 nsTreeBodyFrame::ScrollByPages(int32_t aNumPages) |
|
4083 { |
|
4084 if (!mView) |
|
4085 return NS_OK; |
|
4086 |
|
4087 int32_t newIndex = mTopRowIndex + aNumPages * mPageLength; |
|
4088 if (newIndex < 0) |
|
4089 newIndex = 0; |
|
4090 else { |
|
4091 int32_t lastPageTopRow = mRowCount - mPageLength; |
|
4092 if (newIndex > lastPageTopRow) |
|
4093 newIndex = lastPageTopRow; |
|
4094 } |
|
4095 ScrollToRow(newIndex); |
|
4096 |
|
4097 return NS_OK; |
|
4098 } |
|
4099 |
|
4100 nsresult |
|
4101 nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, int32_t aRow) |
|
4102 { |
|
4103 if (!mView) |
|
4104 return NS_OK; |
|
4105 |
|
4106 int32_t delta = aRow - mTopRowIndex; |
|
4107 |
|
4108 if (delta > 0) { |
|
4109 if (mTopRowIndex == (mRowCount - mPageLength + 1)) |
|
4110 return NS_OK; |
|
4111 } |
|
4112 else { |
|
4113 if (mTopRowIndex == 0) |
|
4114 return NS_OK; |
|
4115 } |
|
4116 |
|
4117 mTopRowIndex += delta; |
|
4118 |
|
4119 Invalidate(); |
|
4120 |
|
4121 PostScrollEvent(); |
|
4122 return NS_OK; |
|
4123 } |
|
4124 |
|
4125 nsresult |
|
4126 nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, int32_t aPosition) |
|
4127 { |
|
4128 if (!mView || !aParts.mColumnsScrollFrame || !aParts.mHScrollbar) |
|
4129 return NS_OK; |
|
4130 |
|
4131 if (aPosition == mHorzPosition) |
|
4132 return NS_OK; |
|
4133 |
|
4134 if (aPosition < 0 || aPosition > mHorzWidth) |
|
4135 return NS_OK; |
|
4136 |
|
4137 nsRect bounds = aParts.mColumnsFrame->GetRect(); |
|
4138 if (aPosition > (mHorzWidth - bounds.width)) |
|
4139 aPosition = mHorzWidth - bounds.width; |
|
4140 |
|
4141 mHorzPosition = aPosition; |
|
4142 |
|
4143 Invalidate(); |
|
4144 |
|
4145 // Update the column scroll view |
|
4146 nsWeakFrame weakFrame(this); |
|
4147 aParts.mColumnsScrollFrame->ScrollTo(nsPoint(mHorzPosition, 0), |
|
4148 nsIScrollableFrame::INSTANT); |
|
4149 if (!weakFrame.IsAlive()) { |
|
4150 return NS_ERROR_FAILURE; |
|
4151 } |
|
4152 // And fire off an event about it all |
|
4153 PostScrollEvent(); |
|
4154 return NS_OK; |
|
4155 } |
|
4156 |
|
4157 NS_IMETHODIMP |
|
4158 nsTreeBodyFrame::ScrollbarButtonPressed(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t aNewIndex) |
|
4159 { |
|
4160 ScrollParts parts = GetScrollParts(); |
|
4161 |
|
4162 if (aScrollbar == parts.mVScrollbar) { |
|
4163 if (aNewIndex > aOldIndex) |
|
4164 ScrollToRowInternal(parts, mTopRowIndex+1); |
|
4165 else if (aNewIndex < aOldIndex) |
|
4166 ScrollToRowInternal(parts, mTopRowIndex-1); |
|
4167 } else { |
|
4168 nsresult rv = ScrollHorzInternal(parts, aNewIndex); |
|
4169 if (NS_FAILED(rv)) return rv; |
|
4170 } |
|
4171 |
|
4172 UpdateScrollbars(parts); |
|
4173 |
|
4174 return NS_OK; |
|
4175 } |
|
4176 |
|
4177 NS_IMETHODIMP |
|
4178 nsTreeBodyFrame::PositionChanged(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t& aNewIndex) |
|
4179 { |
|
4180 ScrollParts parts = GetScrollParts(); |
|
4181 |
|
4182 if (aOldIndex == aNewIndex) |
|
4183 return NS_OK; |
|
4184 |
|
4185 // Vertical Scrollbar |
|
4186 if (parts.mVScrollbar == aScrollbar) { |
|
4187 nscoord rh = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight); |
|
4188 |
|
4189 nscoord newrow = aNewIndex/rh; |
|
4190 ScrollInternal(parts, newrow); |
|
4191 // Horizontal Scrollbar |
|
4192 } else if (parts.mHScrollbar == aScrollbar) { |
|
4193 nsresult rv = ScrollHorzInternal(parts, aNewIndex); |
|
4194 if (NS_FAILED(rv)) return rv; |
|
4195 } |
|
4196 |
|
4197 UpdateScrollbars(parts); |
|
4198 return NS_OK; |
|
4199 } |
|
4200 |
|
4201 // The style cache. |
|
4202 nsStyleContext* |
|
4203 nsTreeBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement) |
|
4204 { |
|
4205 return mStyleCache.GetStyleContext(this, PresContext(), mContent, |
|
4206 mStyleContext, aPseudoElement, |
|
4207 mScratchArray); |
|
4208 } |
|
4209 |
|
4210 // Our comparator for resolving our complex pseudos |
|
4211 bool |
|
4212 nsTreeBodyFrame::PseudoMatches(nsCSSSelector* aSelector) |
|
4213 { |
|
4214 // Iterate the class list. For each item in the list, see if |
|
4215 // it is contained in our scratch array. If we have a miss, then |
|
4216 // we aren't a match. If all items in the class list are |
|
4217 // present in the scratch array, then we have a match. |
|
4218 nsAtomList* curr = aSelector->mClassList; |
|
4219 while (curr) { |
|
4220 if (!mScratchArray.Contains(curr->mAtom)) |
|
4221 return false; |
|
4222 curr = curr->mNext; |
|
4223 } |
|
4224 return true; |
|
4225 } |
|
4226 |
|
4227 nsIContent* |
|
4228 nsTreeBodyFrame::GetBaseElement() |
|
4229 { |
|
4230 nsIFrame* parent = GetParent(); |
|
4231 while (parent) { |
|
4232 nsIContent* content = parent->GetContent(); |
|
4233 if (content) { |
|
4234 nsINodeInfo* ni = content->NodeInfo(); |
|
4235 |
|
4236 if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL) || |
|
4237 (ni->Equals(nsGkAtoms::select) && |
|
4238 content->IsHTML())) |
|
4239 return content; |
|
4240 } |
|
4241 |
|
4242 parent = parent->GetParent(); |
|
4243 } |
|
4244 |
|
4245 return nullptr; |
|
4246 } |
|
4247 |
|
4248 nsresult |
|
4249 nsTreeBodyFrame::ClearStyleAndImageCaches() |
|
4250 { |
|
4251 mStyleCache.Clear(); |
|
4252 mImageCache.EnumerateRead(CancelImageRequest, this); |
|
4253 mImageCache.Clear(); |
|
4254 return NS_OK; |
|
4255 } |
|
4256 |
|
4257 /* virtual */ void |
|
4258 nsTreeBodyFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) |
|
4259 { |
|
4260 nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext); |
|
4261 |
|
4262 // Clear the style cache; the pointers are no longer even valid |
|
4263 mStyleCache.Clear(); |
|
4264 // XXX The following is hacky, but it's not incorrect, |
|
4265 // and appears to fix a few bugs with style changes, like text zoom and |
|
4266 // dpi changes |
|
4267 mIndentation = GetIndentation(); |
|
4268 mRowHeight = GetRowHeight(); |
|
4269 mStringWidth = -1; |
|
4270 } |
|
4271 |
|
4272 bool |
|
4273 nsTreeBodyFrame::OffsetForHorzScroll(nsRect& rect, bool clip) |
|
4274 { |
|
4275 rect.x -= mHorzPosition; |
|
4276 |
|
4277 // Scrolled out before |
|
4278 if (rect.XMost() <= mInnerBox.x) |
|
4279 return false; |
|
4280 |
|
4281 // Scrolled out after |
|
4282 if (rect.x > mInnerBox.XMost()) |
|
4283 return false; |
|
4284 |
|
4285 if (clip) { |
|
4286 nscoord leftEdge = std::max(rect.x, mInnerBox.x); |
|
4287 nscoord rightEdge = std::min(rect.XMost(), mInnerBox.XMost()); |
|
4288 rect.x = leftEdge; |
|
4289 rect.width = rightEdge - leftEdge; |
|
4290 |
|
4291 // Should have returned false above |
|
4292 NS_ASSERTION(rect.width >= 0, "horz scroll code out of sync"); |
|
4293 } |
|
4294 |
|
4295 return true; |
|
4296 } |
|
4297 |
|
4298 bool |
|
4299 nsTreeBodyFrame::CanAutoScroll(int32_t aRowIndex) |
|
4300 { |
|
4301 // Check first for partially visible last row. |
|
4302 if (aRowIndex == mRowCount - 1) { |
|
4303 nscoord y = mInnerBox.y + (aRowIndex - mTopRowIndex) * mRowHeight; |
|
4304 if (y < mInnerBox.height && y + mRowHeight > mInnerBox.height) |
|
4305 return true; |
|
4306 } |
|
4307 |
|
4308 if (aRowIndex > 0 && aRowIndex < mRowCount - 1) |
|
4309 return true; |
|
4310 |
|
4311 return false; |
|
4312 } |
|
4313 |
|
4314 // Given a dom event, figure out which row in the tree the mouse is over, |
|
4315 // if we should drop before/after/on that row or we should auto-scroll. |
|
4316 // Doesn't query the content about if the drag is allowable, that's done elsewhere. |
|
4317 // |
|
4318 // For containers, we break up the vertical space of the row as follows: if in |
|
4319 // the topmost 25%, the drop is _before_ the row the mouse is over; if in the |
|
4320 // last 25%, _after_; in the middle 50%, we consider it a drop _on_ the container. |
|
4321 // |
|
4322 // For non-containers, if the mouse is in the top 50% of the row, the drop is |
|
4323 // _before_ and the bottom 50% _after_ |
|
4324 void |
|
4325 nsTreeBodyFrame::ComputeDropPosition(WidgetGUIEvent* aEvent, |
|
4326 int32_t* aRow, |
|
4327 int16_t* aOrient, |
|
4328 int16_t* aScrollLines) |
|
4329 { |
|
4330 *aOrient = -1; |
|
4331 *aScrollLines = 0; |
|
4332 |
|
4333 // Convert the event's point to our coordinates. We want it in |
|
4334 // the coordinates of our inner box's coordinates. |
|
4335 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); |
|
4336 int32_t xTwips = pt.x - mInnerBox.x; |
|
4337 int32_t yTwips = pt.y - mInnerBox.y; |
|
4338 |
|
4339 *aRow = GetRowAt(xTwips, yTwips); |
|
4340 if (*aRow >=0) { |
|
4341 // Compute the top/bottom of the row in question. |
|
4342 int32_t yOffset = yTwips - mRowHeight * (*aRow - mTopRowIndex); |
|
4343 |
|
4344 bool isContainer = false; |
|
4345 mView->IsContainer (*aRow, &isContainer); |
|
4346 if (isContainer) { |
|
4347 // for a container, use a 25%/50%/25% breakdown |
|
4348 if (yOffset < mRowHeight / 4) |
|
4349 *aOrient = nsITreeView::DROP_BEFORE; |
|
4350 else if (yOffset > mRowHeight - (mRowHeight / 4)) |
|
4351 *aOrient = nsITreeView::DROP_AFTER; |
|
4352 else |
|
4353 *aOrient = nsITreeView::DROP_ON; |
|
4354 } |
|
4355 else { |
|
4356 // for a non-container use a 50%/50% breakdown |
|
4357 if (yOffset < mRowHeight / 2) |
|
4358 *aOrient = nsITreeView::DROP_BEFORE; |
|
4359 else |
|
4360 *aOrient = nsITreeView::DROP_AFTER; |
|
4361 } |
|
4362 } |
|
4363 |
|
4364 if (CanAutoScroll(*aRow)) { |
|
4365 // Get the max value from the look and feel service. |
|
4366 int32_t scrollLinesMax = |
|
4367 LookAndFeel::GetInt(LookAndFeel::eIntID_TreeScrollLinesMax, 0); |
|
4368 scrollLinesMax--; |
|
4369 if (scrollLinesMax < 0) |
|
4370 scrollLinesMax = 0; |
|
4371 |
|
4372 // Determine if we're w/in a margin of the top/bottom of the tree during a drag. |
|
4373 // This will ultimately cause us to scroll, but that's done elsewhere. |
|
4374 nscoord height = (3 * mRowHeight) / 4; |
|
4375 if (yTwips < height) { |
|
4376 // scroll up |
|
4377 *aScrollLines = NSToIntRound(-scrollLinesMax * (1 - (float)yTwips / height) - 1); |
|
4378 } |
|
4379 else if (yTwips > mRect.height - height) { |
|
4380 // scroll down |
|
4381 *aScrollLines = NSToIntRound(scrollLinesMax * (1 - (float)(mRect.height - yTwips) / height) + 1); |
|
4382 } |
|
4383 } |
|
4384 } // ComputeDropPosition |
|
4385 |
|
4386 void |
|
4387 nsTreeBodyFrame::OpenCallback(nsITimer *aTimer, void *aClosure) |
|
4388 { |
|
4389 nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure); |
|
4390 if (self) { |
|
4391 aTimer->Cancel(); |
|
4392 self->mSlots->mTimer = nullptr; |
|
4393 |
|
4394 if (self->mSlots->mDropRow >= 0) { |
|
4395 self->mSlots->mArray.AppendElement(self->mSlots->mDropRow); |
|
4396 self->mView->ToggleOpenState(self->mSlots->mDropRow); |
|
4397 } |
|
4398 } |
|
4399 } |
|
4400 |
|
4401 void |
|
4402 nsTreeBodyFrame::CloseCallback(nsITimer *aTimer, void *aClosure) |
|
4403 { |
|
4404 nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure); |
|
4405 if (self) { |
|
4406 aTimer->Cancel(); |
|
4407 self->mSlots->mTimer = nullptr; |
|
4408 |
|
4409 for (uint32_t i = self->mSlots->mArray.Length(); i--; ) { |
|
4410 if (self->mView) |
|
4411 self->mView->ToggleOpenState(self->mSlots->mArray[i]); |
|
4412 } |
|
4413 self->mSlots->mArray.Clear(); |
|
4414 } |
|
4415 } |
|
4416 |
|
4417 void |
|
4418 nsTreeBodyFrame::LazyScrollCallback(nsITimer *aTimer, void *aClosure) |
|
4419 { |
|
4420 nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure); |
|
4421 if (self) { |
|
4422 aTimer->Cancel(); |
|
4423 self->mSlots->mTimer = nullptr; |
|
4424 |
|
4425 if (self->mView) { |
|
4426 // Set a new timer to scroll the tree repeatedly. |
|
4427 self->CreateTimer(LookAndFeel::eIntID_TreeScrollDelay, |
|
4428 ScrollCallback, nsITimer::TYPE_REPEATING_SLACK, |
|
4429 getter_AddRefs(self->mSlots->mTimer)); |
|
4430 self->ScrollByLines(self->mSlots->mScrollLines); |
|
4431 // ScrollByLines may have deleted |self|. |
|
4432 } |
|
4433 } |
|
4434 } |
|
4435 |
|
4436 void |
|
4437 nsTreeBodyFrame::ScrollCallback(nsITimer *aTimer, void *aClosure) |
|
4438 { |
|
4439 nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure); |
|
4440 if (self) { |
|
4441 // Don't scroll if we are already at the top or bottom of the view. |
|
4442 if (self->mView && self->CanAutoScroll(self->mSlots->mDropRow)) { |
|
4443 self->ScrollByLines(self->mSlots->mScrollLines); |
|
4444 } |
|
4445 else { |
|
4446 aTimer->Cancel(); |
|
4447 self->mSlots->mTimer = nullptr; |
|
4448 } |
|
4449 } |
|
4450 } |
|
4451 |
|
4452 NS_IMETHODIMP |
|
4453 nsTreeBodyFrame::ScrollEvent::Run() |
|
4454 { |
|
4455 if (mInner) { |
|
4456 mInner->FireScrollEvent(); |
|
4457 } |
|
4458 return NS_OK; |
|
4459 } |
|
4460 |
|
4461 |
|
4462 void |
|
4463 nsTreeBodyFrame::FireScrollEvent() |
|
4464 { |
|
4465 mScrollEvent.Forget(); |
|
4466 WidgetGUIEvent event(true, NS_SCROLL_EVENT, nullptr); |
|
4467 // scroll events fired at elements don't bubble |
|
4468 event.mFlags.mBubbles = false; |
|
4469 EventDispatcher::Dispatch(GetContent(), PresContext(), &event); |
|
4470 } |
|
4471 |
|
4472 void |
|
4473 nsTreeBodyFrame::PostScrollEvent() |
|
4474 { |
|
4475 if (mScrollEvent.IsPending()) |
|
4476 return; |
|
4477 |
|
4478 nsRefPtr<ScrollEvent> ev = new ScrollEvent(this); |
|
4479 if (NS_FAILED(NS_DispatchToCurrentThread(ev))) { |
|
4480 NS_WARNING("failed to dispatch ScrollEvent"); |
|
4481 } else { |
|
4482 mScrollEvent = ev; |
|
4483 } |
|
4484 } |
|
4485 |
|
4486 void |
|
4487 nsTreeBodyFrame::ScrollbarActivityStarted() const |
|
4488 { |
|
4489 if (mScrollbarActivity) { |
|
4490 mScrollbarActivity->ActivityStarted(); |
|
4491 } |
|
4492 } |
|
4493 |
|
4494 void |
|
4495 nsTreeBodyFrame::ScrollbarActivityStopped() const |
|
4496 { |
|
4497 if (mScrollbarActivity) { |
|
4498 mScrollbarActivity->ActivityStopped(); |
|
4499 } |
|
4500 } |
|
4501 |
|
4502 void |
|
4503 nsTreeBodyFrame::DetachImageListeners() |
|
4504 { |
|
4505 mCreatedListeners.Clear(); |
|
4506 } |
|
4507 |
|
4508 void |
|
4509 nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener) |
|
4510 { |
|
4511 if (aListener) { |
|
4512 mCreatedListeners.RemoveEntry(aListener); |
|
4513 } |
|
4514 } |
|
4515 |
|
4516 #ifdef ACCESSIBILITY |
|
4517 void |
|
4518 nsTreeBodyFrame::FireRowCountChangedEvent(int32_t aIndex, int32_t aCount) |
|
4519 { |
|
4520 nsCOMPtr<nsIContent> content(GetBaseElement()); |
|
4521 if (!content) |
|
4522 return; |
|
4523 |
|
4524 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->OwnerDoc()); |
|
4525 if (!domDoc) |
|
4526 return; |
|
4527 |
|
4528 nsCOMPtr<nsIDOMEvent> event; |
|
4529 domDoc->CreateEvent(NS_LITERAL_STRING("customevent"), |
|
4530 getter_AddRefs(event)); |
|
4531 |
|
4532 nsCOMPtr<nsIDOMCustomEvent> treeEvent(do_QueryInterface(event)); |
|
4533 if (!treeEvent) |
|
4534 return; |
|
4535 |
|
4536 nsCOMPtr<nsIWritablePropertyBag2> propBag( |
|
4537 do_CreateInstance("@mozilla.org/hash-property-bag;1")); |
|
4538 if (!propBag) |
|
4539 return; |
|
4540 |
|
4541 // Set 'index' data - the row index rows are changed from. |
|
4542 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("index"), aIndex); |
|
4543 |
|
4544 // Set 'count' data - the number of changed rows. |
|
4545 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("count"), aCount); |
|
4546 |
|
4547 nsCOMPtr<nsIWritableVariant> detailVariant( |
|
4548 do_CreateInstance("@mozilla.org/variant;1")); |
|
4549 if (!detailVariant) |
|
4550 return; |
|
4551 |
|
4552 detailVariant->SetAsISupports(propBag); |
|
4553 treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeRowCountChanged"), |
|
4554 true, false, detailVariant); |
|
4555 |
|
4556 event->SetTrusted(true); |
|
4557 |
|
4558 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
4559 new AsyncEventDispatcher(content, event); |
|
4560 asyncDispatcher->PostDOMEvent(); |
|
4561 } |
|
4562 |
|
4563 void |
|
4564 nsTreeBodyFrame::FireInvalidateEvent(int32_t aStartRowIdx, int32_t aEndRowIdx, |
|
4565 nsITreeColumn *aStartCol, |
|
4566 nsITreeColumn *aEndCol) |
|
4567 { |
|
4568 nsCOMPtr<nsIContent> content(GetBaseElement()); |
|
4569 if (!content) |
|
4570 return; |
|
4571 |
|
4572 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->OwnerDoc()); |
|
4573 if (!domDoc) |
|
4574 return; |
|
4575 |
|
4576 nsCOMPtr<nsIDOMEvent> event; |
|
4577 domDoc->CreateEvent(NS_LITERAL_STRING("customevent"), |
|
4578 getter_AddRefs(event)); |
|
4579 |
|
4580 nsCOMPtr<nsIDOMCustomEvent> treeEvent(do_QueryInterface(event)); |
|
4581 if (!treeEvent) |
|
4582 return; |
|
4583 |
|
4584 nsCOMPtr<nsIWritablePropertyBag2> propBag( |
|
4585 do_CreateInstance("@mozilla.org/hash-property-bag;1")); |
|
4586 if (!propBag) |
|
4587 return; |
|
4588 |
|
4589 if (aStartRowIdx != -1 && aEndRowIdx != -1) { |
|
4590 // Set 'startrow' data - the start index of invalidated rows. |
|
4591 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startrow"), |
|
4592 aStartRowIdx); |
|
4593 |
|
4594 // Set 'endrow' data - the end index of invalidated rows. |
|
4595 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endrow"), |
|
4596 aEndRowIdx); |
|
4597 } |
|
4598 |
|
4599 if (aStartCol && aEndCol) { |
|
4600 // Set 'startcolumn' data - the start index of invalidated rows. |
|
4601 int32_t startColIdx = 0; |
|
4602 nsresult rv = aStartCol->GetIndex(&startColIdx); |
|
4603 if (NS_FAILED(rv)) |
|
4604 return; |
|
4605 |
|
4606 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"), |
|
4607 startColIdx); |
|
4608 |
|
4609 // Set 'endcolumn' data - the start index of invalidated rows. |
|
4610 int32_t endColIdx = 0; |
|
4611 rv = aEndCol->GetIndex(&endColIdx); |
|
4612 if (NS_FAILED(rv)) |
|
4613 return; |
|
4614 |
|
4615 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"), |
|
4616 endColIdx); |
|
4617 } |
|
4618 |
|
4619 nsCOMPtr<nsIWritableVariant> detailVariant( |
|
4620 do_CreateInstance("@mozilla.org/variant;1")); |
|
4621 if (!detailVariant) |
|
4622 return; |
|
4623 |
|
4624 detailVariant->SetAsISupports(propBag); |
|
4625 treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeInvalidated"), |
|
4626 true, false, detailVariant); |
|
4627 |
|
4628 event->SetTrusted(true); |
|
4629 |
|
4630 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
4631 new AsyncEventDispatcher(content, event); |
|
4632 asyncDispatcher->PostDOMEvent(); |
|
4633 } |
|
4634 #endif |
|
4635 |
|
4636 class nsOverflowChecker : public nsRunnable |
|
4637 { |
|
4638 public: |
|
4639 nsOverflowChecker(nsTreeBodyFrame* aFrame) : mFrame(aFrame) {} |
|
4640 NS_IMETHOD Run() MOZ_OVERRIDE |
|
4641 { |
|
4642 if (mFrame.IsAlive()) { |
|
4643 nsTreeBodyFrame* tree = static_cast<nsTreeBodyFrame*>(mFrame.GetFrame()); |
|
4644 nsTreeBodyFrame::ScrollParts parts = tree->GetScrollParts(); |
|
4645 tree->CheckOverflow(parts); |
|
4646 } |
|
4647 return NS_OK; |
|
4648 } |
|
4649 private: |
|
4650 nsWeakFrame mFrame; |
|
4651 }; |
|
4652 |
|
4653 bool |
|
4654 nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation) |
|
4655 { |
|
4656 ScrollParts parts = GetScrollParts(); |
|
4657 nsWeakFrame weakFrame(this); |
|
4658 nsWeakFrame weakColumnsFrame(parts.mColumnsFrame); |
|
4659 UpdateScrollbars(parts); |
|
4660 NS_ENSURE_TRUE(weakFrame.IsAlive(), false); |
|
4661 if (aNeedsFullInvalidation) { |
|
4662 Invalidate(); |
|
4663 } |
|
4664 InvalidateScrollbars(parts, weakColumnsFrame); |
|
4665 NS_ENSURE_TRUE(weakFrame.IsAlive(), false); |
|
4666 |
|
4667 // Overflow checking dispatches synchronous events, which can cause infinite |
|
4668 // recursion during reflow. Do the first overflow check synchronously, but |
|
4669 // force any nested checks to round-trip through the event loop. See bug |
|
4670 // 905909. |
|
4671 nsRefPtr<nsOverflowChecker> checker = new nsOverflowChecker(this); |
|
4672 if (!mCheckingOverflow) { |
|
4673 nsContentUtils::AddScriptRunner(checker); |
|
4674 } else { |
|
4675 NS_DispatchToCurrentThread(checker); |
|
4676 } |
|
4677 return weakFrame.IsAlive(); |
|
4678 } |
|
4679 |
|
4680 nsresult |
|
4681 nsTreeBodyFrame::OnImageIsAnimated(imgIRequest* aRequest) |
|
4682 { |
|
4683 nsLayoutUtils::RegisterImageRequest(PresContext(), |
|
4684 aRequest, nullptr); |
|
4685 |
|
4686 return NS_OK; |
|
4687 } |