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