Back to index

lightning-sunbird  0.9+nobinonly
nsTreeBodyFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Dave Hyatt <hyatt@mozilla.org> (Original Author)
00024  *   Ben Goodger <ben@netscape.com>
00025  *   Joe Hewitt <hewitt@netscape.com>
00026  *   Jan Varga <varga@ku.sk>
00027  *   Dean Tessman <dean_tessman@hotmail.com>
00028  *   Brian Ryner <bryner@brianryner.com>
00029  *   Blake Ross <blaker@netscape.com>
00030  *   Pierre Chanial <pierrechanial@netscape.net>
00031  *   Rene Pronk <r.pronk@its.tudelft.nl>
00032  *   Mark Banner <mark@standard8.demon.co.uk>
00033  *
00034  * Alternatively, the contents of this file may be used under the terms of
00035  * either of the GNU General Public License Version 2 or later (the "GPL"),
00036  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00037  * in which case the provisions of the GPL or the LGPL are applicable instead
00038  * of those above. If you wish to allow use of your version of this file only
00039  * under the terms of either the GPL or the LGPL, and not to allow others to
00040  * use your version of this file under the terms of the MPL, indicate your
00041  * decision by deleting the provisions above and replace them with the notice
00042  * and other provisions required by the GPL or the LGPL. If you do not delete
00043  * the provisions above, a recipient may use your version of this file under
00044  * the terms of any one of the MPL, the GPL or the LGPL.
00045  *
00046  * ***** END LICENSE BLOCK ***** */
00047 
00048 #include "nsCOMPtr.h"
00049 #include "nsISupportsArray.h"
00050 #include "nsPresContext.h"
00051 #include "nsINameSpaceManager.h"
00052 #include "nsIScrollbarFrame.h"
00053 
00054 #include "nsTreeBodyFrame.h"
00055 #include "nsTreeSelection.h"
00056 
00057 #include "nsXULAtoms.h"
00058 #include "nsCSSAnonBoxes.h"
00059 #include "nsHTMLAtoms.h"
00060 
00061 #include "nsIContent.h"
00062 #include "nsStyleContext.h"
00063 #include "nsIBoxObject.h"
00064 #include "nsGUIEvent.h"
00065 #include "nsIDOMMouseEvent.h"
00066 #include "nsIDOMElement.h"
00067 #include "nsIDOMNodeList.h"
00068 #include "nsIDOMNSDocument.h"
00069 #include "nsIDOMXULElement.h"
00070 #include "nsIDocument.h"
00071 #include "nsIContent.h"
00072 #include "nsICSSStyleRule.h"
00073 #include "nsCSSRendering.h"
00074 #include "nsIFontMetrics.h"
00075 #include "nsIDeviceContext.h"
00076 #include "nsIXULTemplateBuilder.h"
00077 #include "nsXPIDLString.h"
00078 #include "nsHTMLContainerFrame.h"
00079 #include "nsIView.h"
00080 #include "nsWidgetsCID.h"
00081 #include "nsBoxFrame.h"
00082 #include "nsBoxObject.h"
00083 #include "nsIURL.h"
00084 #include "nsNetUtil.h"
00085 #include "nsBoxLayoutState.h"
00086 #include "nsIDragService.h"
00087 #include "nsTreeContentView.h"
00088 #include "nsTreeUtils.h"
00089 #include "nsChildIterator.h"
00090 #include "nsIScrollableView.h"
00091 #include "nsITheme.h"
00092 #include "nsITimelineService.h"
00093 #include "nsITimerInternal.h"
00094 #include "imgIRequest.h"
00095 #include "imgIContainer.h"
00096 #include "imgIContainerObserver.h"
00097 #include "imgILoader.h"
00098 #include "nsINodeInfo.h"
00099 #include "nsContentUtils.h"
00100 
00101 #ifdef IBMBIDI
00102 #include "nsBidiPresUtils.h"
00103 #endif
00104 
00105 #define ELLIPSIS "..."
00106 
00107 NS_IMPL_ISUPPORTS1(nsTreeReflowCallback, nsIReflowCallback)
00108 
00109 NS_IMETHODIMP
00110 nsTreeReflowCallback::ReflowFinished(nsIPresShell* aShell, PRBool* aFlushFlag)
00111 {
00112   return mFrame->ReflowFinished(aShell, aFlushFlag);
00113 }
00114 
00115 static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID);
00116 
00117 // Enumeration function that cancels all the image requests in our cache
00118 PR_STATIC_CALLBACK(PLDHashOperator)
00119 CancelImageRequest(const nsAString& aKey,
00120                    nsTreeImageCacheEntry aEntry, void* aData)
00121 {
00122   aEntry.request->Cancel(NS_BINDING_ABORTED);
00123   return PL_DHASH_NEXT;
00124 }
00125 
00126 //
00127 // NS_NewTreeFrame
00128 //
00129 // Creates a new tree frame
00130 //
00131 nsresult
00132 NS_NewTreeBodyFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00133 {
00134   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00135   if (nsnull == aNewFrame) {
00136     return NS_ERROR_NULL_POINTER;
00137   }
00138   nsTreeBodyFrame* it = new (aPresShell) nsTreeBodyFrame(aPresShell);
00139   if (!it)
00140     return NS_ERROR_OUT_OF_MEMORY;
00141 
00142   *aNewFrame = it;
00143   return NS_OK;
00144   
00145 } // NS_NewTreeFrame
00146 
00147 
00148 //
00149 // QueryInterface
00150 //
00151 NS_INTERFACE_MAP_BEGIN(nsTreeBodyFrame)
00152   NS_INTERFACE_MAP_ENTRY(nsITreeBoxObject)
00153   NS_INTERFACE_MAP_ENTRY(nsICSSPseudoComparator)
00154   NS_INTERFACE_MAP_ENTRY(nsIScrollbarMediator)
00155 NS_INTERFACE_MAP_END_INHERITING(nsLeafBoxFrame)
00156 
00157 
00158 
00159 // Constructor
00160 nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell)
00161 :nsLeafBoxFrame(aPresShell), mPresContext(nsnull),
00162  mTopRowIndex(0), mRowHeight(0), mIndentation(0), mStringWidth(-1),
00163  mFocused(PR_FALSE), mHasFixedRowCount(PR_FALSE),
00164  mVerticalOverflow(PR_FALSE), mUpdateBatchNest(0), mRowCount(0), mSlots(nsnull)
00165 {
00166   mColumns = new nsTreeColumns(nsnull);
00167   NS_NewISupportsArray(getter_AddRefs(mScratchArray));
00168 }
00169 
00170 // Destructor
00171 nsTreeBodyFrame::~nsTreeBodyFrame()
00172 {
00173   mImageCache.EnumerateRead(CancelImageRequest, nsnull);
00174   delete mSlots;
00175 }
00176 
00177 NS_IMETHODIMP_(nsrefcnt) 
00178 nsTreeBodyFrame::AddRef(void)
00179 {
00180   return NS_OK;
00181 }
00182 
00183 NS_IMETHODIMP_(nsrefcnt)
00184 nsTreeBodyFrame::Release(void)
00185 {
00186   return NS_OK;
00187 }
00188 
00189 static void
00190 GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin)
00191 {
00192   nsStyleBorderPadding  borderPaddingStyle;
00193   aContext->GetBorderPaddingFor(borderPaddingStyle);
00194   borderPaddingStyle.GetBorderPadding(aMargin);
00195 }
00196 
00197 static void
00198 AdjustForBorderPadding(nsStyleContext* aContext, nsRect& aRect)
00199 {
00200   nsMargin borderPadding(0, 0, 0, 0);
00201   GetBorderPadding(aContext, borderPadding);
00202   aRect.Deflate(borderPadding);
00203 }
00204 
00205 NS_IMETHODIMP
00206 nsTreeBodyFrame::Init(nsPresContext* aPresContext, nsIContent* aContent,
00207                       nsIFrame* aParent, nsStyleContext* aContext, nsIFrame* aPrevInFlow)
00208 {
00209   mPresContext = aPresContext;
00210   nsresult rv = nsLeafBoxFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00211   NS_ENSURE_SUCCESS(rv, rv);
00212 
00213   rv = nsBoxFrame::CreateViewForFrame(aPresContext, this, aContext, PR_TRUE);
00214   NS_ENSURE_SUCCESS(rv, rv);
00215 
00216   nsIView* view = nsLeafBoxFrame::GetView();
00217   if (!view->HasWidget()) {
00218     view->CreateWidget(kWidgetCID);
00219   }
00220 
00221   mIndentation = GetIndentation();
00222   mRowHeight = GetRowHeight();
00223 
00224   NS_ENSURE_TRUE(mImageCache.Init(16), NS_ERROR_OUT_OF_MEMORY);
00225   return rv;
00226 }
00227 
00228 NS_IMETHODIMP
00229 nsTreeBodyFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
00230 {
00231   EnsureView();
00232 
00233   nsIContent* baseElement = GetBaseElement();
00234 
00235   PRInt32 desiredRows;
00236   if (NS_UNLIKELY(!baseElement)) {
00237     desiredRows = 0;
00238   }
00239   else if (baseElement->Tag() == nsHTMLAtoms::select &&
00240            baseElement->IsContentOfType(nsIContent::eHTML)) {
00241     aSize.width = CalcMaxRowWidth();
00242     nsAutoString size;
00243     baseElement->GetAttr(kNameSpaceID_None, nsHTMLAtoms::size, size);
00244     if (!size.IsEmpty()) {
00245       PRInt32 err;
00246       desiredRows = size.ToInteger(&err);
00247       mHasFixedRowCount = PR_TRUE;
00248       mPageLength = desiredRows;
00249     }
00250     else {
00251       desiredRows = 1;
00252     }
00253   }
00254   else {
00255     // tree
00256     aSize.width = 0;
00257     nsAutoString rows;
00258     baseElement->GetAttr(kNameSpaceID_None, nsXULAtoms::rows, rows);
00259     if (!rows.IsEmpty()) {
00260       PRInt32 err;
00261       desiredRows = rows.ToInteger(&err);
00262       mPageLength = desiredRows;
00263     }
00264     else {
00265       desiredRows = 0;
00266     }
00267   }
00268 
00269   aSize.height = mRowHeight * desiredRows;
00270 
00271   AddBorderAndPadding(aSize);
00272   AddInset(aSize);
00273   nsIBox::AddCSSMinSize(aBoxLayoutState, this, aSize);
00274 
00275   return NS_OK;
00276 }
00277 
00278 nscoord
00279 nsTreeBodyFrame::CalcMaxRowWidth()
00280 {
00281   if (mStringWidth != -1)
00282     return mStringWidth;
00283 
00284   if (!mView)
00285     return 0;
00286 
00287   nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
00288   nsMargin rowMargin(0,0,0,0);
00289   GetBorderPadding(rowContext, rowMargin);
00290 
00291   nscoord rowWidth;
00292   nsTreeColumn* col;
00293 
00294   nsCOMPtr<nsIRenderingContext> rc;
00295   mPresContext->PresShell()->CreateRenderingContext(this, getter_AddRefs(rc));
00296 
00297   for (PRInt32 row = 0; row < mRowCount; ++row) {
00298     rowWidth = 0;
00299     col = mColumns->GetFirstColumn();
00300 
00301     while (col) {
00302       nscoord desiredWidth, currentWidth;
00303       GetCellWidth(row, col, rc, desiredWidth, currentWidth);
00304       rowWidth += desiredWidth;
00305       col = col->GetNext();
00306     }
00307 
00308     if (rowWidth > mStringWidth)
00309       mStringWidth = rowWidth;
00310   }
00311 
00312   mStringWidth += rowMargin.left + rowMargin.right;
00313   return mStringWidth;
00314 }
00315 
00316 NS_IMETHODIMP
00317 nsTreeBodyFrame::Destroy(nsPresContext* aPresContext)
00318 {
00319   // Make sure we cancel any posted callbacks. 
00320   if (mReflowCallback) {
00321     aPresContext->PresShell()->CancelReflowCallback(mReflowCallback);
00322     mReflowCallback = nsnull;
00323   }
00324 
00325   if (mColumns)
00326     mColumns->SetTree(nsnull);
00327 
00328   // Save off our info into the box object.
00329   EnsureBoxObject();
00330   if (mTreeBoxObject) {
00331     nsCOMPtr<nsIBoxObject> box(do_QueryInterface(mTreeBoxObject));
00332     if (mTopRowIndex > 0) {
00333       nsAutoString topRowStr; topRowStr.AssignLiteral("topRow");
00334       nsAutoString topRow;
00335       topRow.AppendInt(mTopRowIndex);
00336       box->SetProperty(topRowStr.get(), topRow.get());
00337     }
00338 
00339     // Always null out the cached tree body frame.
00340     mTreeBoxObject->ClearCachedTreeBody();
00341 
00342     mTreeBoxObject = nsnull; // Drop our ref here.
00343   }
00344 
00345   if (mView) {
00346     nsCOMPtr<nsITreeSelection> sel;
00347     mView->GetSelection(getter_AddRefs(sel));
00348     if (sel)
00349       sel->SetTree(nsnull);
00350     mView->SetTree(nsnull);
00351     mView = nsnull;
00352   }
00353 
00354   return nsLeafBoxFrame::Destroy(aPresContext);
00355 }
00356 
00357 void
00358 nsTreeBodyFrame::EnsureBoxObject()
00359 {
00360   if (!mTreeBoxObject) {
00361     nsIContent* parent = GetBaseElement();
00362     if (parent) {
00363       nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(parent->GetDocument());
00364       if (!nsDoc) // there may be no document, if we're called from Destroy()
00365         return;
00366       nsCOMPtr<nsIBoxObject> box;
00367       nsCOMPtr<nsIDOMElement> domElem = do_QueryInterface(parent);
00368       nsDoc->GetBoxObjectFor(domElem, getter_AddRefs(box));
00369       
00370       if (box) {
00371         mTreeBoxObject = do_QueryInterface(box);
00372         mColumns->SetTree(mTreeBoxObject);
00373       }
00374     }
00375   }
00376 }
00377 
00378 void
00379 nsTreeBodyFrame::EnsureView()
00380 {
00381   if (!mView) {
00382     PRBool isInReflow;
00383     GetPresContext()->PresShell()->IsReflowLocked(&isInReflow);
00384     if (isInReflow) {
00385       if (!mReflowCallback &&
00386           (mReflowCallback = new nsTreeReflowCallback(this))) {
00387         GetPresContext()->PresShell()->PostReflowCallback(mReflowCallback);
00388       }
00389       return;
00390     }
00391 
00392     PRInt32 rowIndex = 0;
00393     EnsureBoxObject();
00394     nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mTreeBoxObject);
00395     if (box) {
00396       nsCOMPtr<nsISupports> suppView;
00397       box->GetPropertyAsSupports(NS_LITERAL_STRING("view").get(),
00398                                  getter_AddRefs(suppView));
00399       nsCOMPtr<nsITreeView> treeView(do_QueryInterface(suppView));
00400 
00401       if (treeView) {
00402         nsXPIDLString rowStr;
00403         box->GetProperty(NS_LITERAL_STRING("topRow").get(),
00404                          getter_Copies(rowStr));
00405         nsAutoString rowStr2(rowStr);
00406         PRInt32 error;
00407         rowIndex = rowStr2.ToInteger(&error);
00408 
00409         nsWeakFrame weakFrame(this);
00410         // Set our view.
00411         SetView(treeView);
00412         ENSURE_TRUE(weakFrame.IsAlive());
00413 
00414         // Scroll to the given row.
00415         // XXX is this optimal if we haven't laid out yet?
00416         ScrollToRow(rowIndex);
00417 
00418         // Clear out the property info for the top row, but we always keep the
00419         // view current.
00420         box->RemoveProperty(NS_LITERAL_STRING("topRow").get());
00421         return;
00422       }
00423     }
00424 
00425     if (!mView) {
00426       // If we don't have a box object yet, or no view was set on it,
00427       // look for a XULTreeBuilder or create a content view.
00428       
00429       nsCOMPtr<nsIDOMXULElement> xulele = do_QueryInterface(mContent->GetParent());
00430       if (xulele) {
00431         nsCOMPtr<nsITreeView> view;
00432 
00433         // See if there is a XUL tree builder associated with
00434         // the parent element.
00435         nsCOMPtr<nsIXULTemplateBuilder> builder;
00436         xulele->GetBuilder(getter_AddRefs(builder));
00437         if (builder)
00438           view = do_QueryInterface(builder);
00439 
00440         if (!view) {
00441           // No tree builder, create a tree content view.
00442           nsCOMPtr<nsITreeContentView> contentView;
00443           NS_NewTreeContentView(getter_AddRefs(contentView));
00444           if (contentView)
00445             view = do_QueryInterface(contentView);
00446         }
00447 
00448         // Hook up the view.
00449         if (view)
00450           SetView(view);
00451       }
00452     }
00453   }
00454 }
00455 
00456 NS_IMETHODIMP
00457 nsTreeBodyFrame::SetBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect,
00458                            PRBool aRemoveOverflowArea)
00459 {
00460   if (aRect != mRect && !mReflowCallback &&
00461       (mReflowCallback = new nsTreeReflowCallback(this))) {
00462     GetPresContext()->PresShell()->PostReflowCallback(mReflowCallback);
00463   }
00464 
00465   return nsLeafBoxFrame::SetBounds(aBoxLayoutState, aRect, aRemoveOverflowArea);
00466 }
00467 
00468 
00469 nsresult
00470 nsTreeBodyFrame::ReflowFinished(nsIPresShell* aPresShell, PRBool* aFlushFlag)
00471 {
00472   if (!mView) {
00473     nsWeakFrame weakFrame(this);
00474     EnsureView();
00475     NS_ENSURE_TRUE(weakFrame.IsAlive(), PR_FALSE);
00476   }
00477 
00478   if (mView) {
00479     CalcInnerBox();
00480     if (!mHasFixedRowCount) {
00481       mPageLength = mInnerBox.height / mRowHeight;
00482     }
00483 
00484     PRInt32 lastPageTopRow = PR_MAX(0, mRowCount - mPageLength);
00485     ScrollParts parts = GetScrollParts();
00486     if (mTopRowIndex > lastPageTopRow)
00487       ScrollToRowInternal(parts, lastPageTopRow);
00488 
00489     // make sure that the current selected item is still
00490     // visible after the tree changes size.
00491     nsCOMPtr<nsITreeSelection> sel;
00492     mView->GetSelection(getter_AddRefs(sel));
00493     if (sel) {
00494       PRInt32 currentIndex;
00495       sel->GetCurrentIndex(&currentIndex);
00496       if (currentIndex != -1)
00497         EnsureRowIsVisibleInternal(parts, currentIndex);
00498     }
00499 
00500     if (!FullScrollbarUpdate(PR_FALSE)) {
00501       *aFlushFlag = PR_FALSE;
00502       return NS_OK;
00503     }
00504   }
00505 
00506   mReflowCallback = nsnull;
00507   *aFlushFlag = PR_FALSE;
00508 
00509   return NS_OK;
00510 }
00511 
00512 
00513 NS_IMETHODIMP nsTreeBodyFrame::GetView(nsITreeView * *aView)
00514 {
00515   EnsureView();
00516   NS_IF_ADDREF(*aView = mView);
00517   return NS_OK;
00518 }
00519 
00520 NS_IMETHODIMP nsTreeBodyFrame::SetView(nsITreeView * aView)
00521 {
00522   // First clear out the old view.
00523   EnsureBoxObject();
00524   nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mTreeBoxObject);
00525   
00526   NS_NAMED_LITERAL_STRING(view, "view");
00527   
00528   if (mView) {
00529     nsCOMPtr<nsITreeSelection> sel;
00530     mView->GetSelection(getter_AddRefs(sel));
00531     if (sel)
00532       sel->SetTree(nsnull);
00533     mView->SetTree(nsnull);
00534     mView = nsnull;
00535     if (box)
00536       box->RemoveProperty(view.get());
00537 
00538     // Only reset the top row index and delete the columns if we had an old non-null view.
00539     mTopRowIndex = 0;
00540   }
00541 
00542   // Tree, meet the view.
00543   mView = aView;
00544  
00545   // Changing the view causes us to refetch our data.  This will
00546   // necessarily entail a full invalidation of the tree.
00547   Invalidate();
00548  
00549   nsIContent *treeContent = GetBaseElement();
00550   if (treeContent) {
00551     FireDOMEvent(NS_LITERAL_STRING("TreeViewChanged"), treeContent);
00552   }
00553 
00554   if (mView) {
00555     // Give the view a new empty selection object to play with, but only if it
00556     // doesn't have one already.
00557     nsCOMPtr<nsITreeSelection> sel;
00558     mView->GetSelection(getter_AddRefs(sel));
00559     if (sel) {
00560       sel->SetTree(mTreeBoxObject);
00561     }
00562     else {
00563       NS_NewTreeSelection(mTreeBoxObject, getter_AddRefs(sel));
00564       mView->SetSelection(sel);
00565     }
00566 
00567     // View, meet the tree.
00568     mView->SetTree(mTreeBoxObject);
00569     mView->GetRowCount(&mRowCount);
00570  
00571     if (box)
00572       box->SetPropertyAsSupports(view.get(), mView);
00573 
00574     FullScrollbarUpdate(PR_FALSE);
00575   }
00576  
00577   return NS_OK;
00578 }
00579 
00580 NS_IMETHODIMP 
00581 nsTreeBodyFrame::GetFocused(PRBool* aFocused)
00582 {
00583   *aFocused = mFocused;
00584   return NS_OK;
00585 }
00586 
00587 NS_IMETHODIMP 
00588 nsTreeBodyFrame::SetFocused(PRBool aFocused)
00589 {
00590   if (mFocused != aFocused) {
00591     mFocused = aFocused;
00592     if (mView) {
00593       nsCOMPtr<nsITreeSelection> sel;
00594       mView->GetSelection(getter_AddRefs(sel));
00595       if (sel)
00596         sel->InvalidateSelection();
00597     }
00598   }
00599   return NS_OK;
00600 }
00601 
00602 NS_IMETHODIMP 
00603 nsTreeBodyFrame::GetTreeBody(nsIDOMElement** aElement)
00604 {
00605   //NS_ASSERTION(mContent, "no content, see bug #104878");
00606   if (!mContent)
00607     return NS_ERROR_NULL_POINTER;
00608 
00609   return mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aElement);
00610 }
00611 
00612 NS_IMETHODIMP 
00613 nsTreeBodyFrame::GetColumns(nsITreeColumns** aColumns)
00614 {
00615   NS_IF_ADDREF(*aColumns = mColumns);
00616   return NS_OK;
00617 }
00618 
00619 NS_IMETHODIMP
00620 nsTreeBodyFrame::GetRowHeight(PRInt32* _retval)
00621 {
00622   float t2p = mPresContext->TwipsToPixels();
00623   *_retval = NSToCoordRound((float) mRowHeight * t2p);
00624 
00625   return NS_OK;
00626 }
00627 
00628 NS_IMETHODIMP
00629 nsTreeBodyFrame::GetFirstVisibleRow(PRInt32 *_retval)
00630 {
00631   *_retval = mTopRowIndex;
00632   return NS_OK;
00633 }
00634 
00635 NS_IMETHODIMP
00636 nsTreeBodyFrame::GetLastVisibleRow(PRInt32 *_retval)
00637 {
00638   *_retval = GetLastVisibleRow();
00639   return NS_OK;
00640 }
00641 
00642 NS_IMETHODIMP
00643 nsTreeBodyFrame::GetPageLength(PRInt32 *_retval)
00644 {
00645   *_retval = mPageLength;
00646   return NS_OK;
00647 }
00648 
00649 NS_IMETHODIMP
00650 nsTreeBodyFrame::Invalidate()
00651 {
00652   if (mUpdateBatchNest)
00653     return NS_OK;
00654 
00655   nsIFrame::Invalidate(GetOverflowRect(), PR_FALSE);
00656 
00657   return NS_OK;
00658 }
00659 
00660 NS_IMETHODIMP
00661 nsTreeBodyFrame::InvalidateColumn(nsITreeColumn* aCol)
00662 {
00663   if (mUpdateBatchNest)
00664     return NS_OK;
00665 
00666   nsTreeColumn* col = NS_STATIC_CAST(nsTreeColumn*, aCol);
00667   if (col) {
00668     nsRect columnRect(col->GetX(), mInnerBox.y, col->GetWidth(), mInnerBox.height);
00669     nsIFrame::Invalidate(columnRect, PR_FALSE);
00670   }
00671 
00672   return NS_OK;
00673 }
00674 
00675 NS_IMETHODIMP
00676 nsTreeBodyFrame::InvalidateRow(PRInt32 aIndex)
00677 {
00678   if (mUpdateBatchNest)
00679     return NS_OK;
00680 
00681   aIndex -= mTopRowIndex;
00682   if (aIndex < 0 || aIndex > mPageLength)
00683     return NS_OK;
00684 
00685   nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*aIndex, mInnerBox.width, mRowHeight);
00686 #if defined(XP_MAC) || defined(XP_MACOSX)
00687   // Mac can't process the event loop during a drag, so if we're dragging,
00688   // invalidate synchronously.
00689   nsLeafBoxFrame::Invalidate(rowRect, mSlots && mSlots->mDragSession ? PR_TRUE : PR_FALSE);
00690 #else
00691   nsLeafBoxFrame::Invalidate(rowRect, PR_FALSE);
00692 #endif
00693 
00694   return NS_OK;
00695 }
00696 
00697 NS_IMETHODIMP
00698 nsTreeBodyFrame::InvalidateCell(PRInt32 aIndex, nsITreeColumn* aCol)
00699 {
00700   if (mUpdateBatchNest)
00701     return NS_OK;
00702 
00703   aIndex -= mTopRowIndex;
00704   if (aIndex < 0 || aIndex > mPageLength)
00705     return NS_OK;
00706 
00707   nsTreeColumn* col = NS_STATIC_CAST(nsTreeColumn*, aCol);
00708   if (col) {
00709     nscoord yPos = mInnerBox.y+mRowHeight*aIndex;
00710     nsRect cellRect(col->GetX(), yPos, col->GetWidth(), mRowHeight);
00711     nsIFrame::Invalidate(cellRect, PR_FALSE);
00712   }
00713 
00714   return NS_OK;
00715 }
00716 
00717 NS_IMETHODIMP
00718 nsTreeBodyFrame::InvalidateRange(PRInt32 aStart, PRInt32 aEnd)
00719 {
00720   if (mUpdateBatchNest)
00721     return NS_OK;
00722 
00723   if (aStart == aEnd)
00724     return InvalidateRow(aStart);
00725 
00726   PRInt32 last = GetLastVisibleRow();
00727   if (aEnd < mTopRowIndex || aStart > last)
00728     return NS_OK;
00729 
00730   if (aStart < mTopRowIndex)
00731     aStart = mTopRowIndex;
00732 
00733   if (aEnd > last)
00734     aEnd = last;
00735 
00736   nsRect rangeRect(mInnerBox.x, mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), mInnerBox.width, mRowHeight*(aEnd-aStart+1));
00737   nsIFrame::Invalidate(rangeRect, PR_FALSE);
00738 
00739   return NS_OK;
00740 }
00741 
00742 static void
00743 FindScrollParts(nsIFrame* aCurrFrame, nsTreeBodyFrame::ScrollParts* aResult)
00744 {
00745   nsIScrollbarFrame *sf = nsnull;
00746   CallQueryInterface(aCurrFrame, &sf);
00747   if (sf) {
00748     PRBool isHorizontal = PR_FALSE;
00749     if (NS_SUCCEEDED(aCurrFrame->GetOrientation(isHorizontal))) {
00750       if (!isHorizontal) {
00751         if (!aResult->mVScrollbar) {
00752           aResult->mVScrollbar = sf;
00753         }
00754       }
00755     }
00756     // don't bother searching inside a scrollbar
00757     return;
00758   }
00759 
00760   nsIFrame* child = aCurrFrame->GetFirstChild(nsnull);
00761   while (child && !aResult->mVScrollbar) {
00762     FindScrollParts(child, aResult);
00763     child = child->GetNextSibling();
00764   }
00765 }
00766 
00767 nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts()
00768 {
00769   nsPresContext* presContext = GetPresContext();
00770   ScrollParts result = { nsnull, nsnull };
00771   nsIFrame* treeFrame = nsnull;
00772   nsIContent* baseElement = GetBaseElement();
00773   if (baseElement)
00774     presContext->PresShell()->GetPrimaryFrameFor(baseElement, &treeFrame);
00775   if (treeFrame) {
00776     // The way we do this, searching through the entire frame subtree, is pretty
00777     // dumb! We should know where these frames are.
00778     FindScrollParts(treeFrame, &result);
00779     if (result.mVScrollbar) {
00780       result.mVScrollbar->SetScrollbarMediator(this);
00781       nsIFrame* f;
00782       CallQueryInterface(result.mVScrollbar, &f);
00783       result.mVScrollbarContent = f->GetContent();
00784     }
00785   }
00786   return result;
00787 }
00788 
00789 void
00790 nsTreeBodyFrame::UpdateScrollbar(const ScrollParts& aParts)
00791 {
00792   float t2p = mPresContext->TwipsToPixels();
00793   nscoord rowHeightAsPixels = NSToCoordRound((float)mRowHeight*t2p);
00794 
00795   if (aParts.mVScrollbar) {
00796     nsAutoString curPos;
00797     curPos.AppendInt(mTopRowIndex*rowHeightAsPixels);
00798     aParts.mVScrollbarContent->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, curPos, PR_TRUE);
00799   }
00800 }
00801 
00802 void
00803 nsTreeBodyFrame::CheckVerticalOverflow()
00804 {
00805   PRBool verticalOverflowChanged = PR_FALSE;
00806 
00807   if (!mVerticalOverflow && mRowCount > mPageLength) {
00808     mVerticalOverflow = PR_TRUE;
00809     verticalOverflowChanged = PR_TRUE;
00810   }
00811   else if (mVerticalOverflow && mRowCount <= mPageLength) {
00812     mVerticalOverflow = PR_FALSE;
00813     verticalOverflowChanged = PR_TRUE;
00814   }
00815  
00816   if (verticalOverflowChanged) {
00817     nsScrollPortEvent event(PR_TRUE, mVerticalOverflow ? NS_SCROLLPORT_OVERFLOW
00818                             : NS_SCROLLPORT_UNDERFLOW, nsnull);
00819     event.orient = nsScrollPortEvent::vertical;
00820 
00821     nsEventStatus status = nsEventStatus_eIgnore;
00822     mContent->HandleDOMEvent(mPresContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
00823   }
00824 }
00825 
00826 void
00827 nsTreeBodyFrame::InvalidateScrollbar(const ScrollParts& aParts)
00828 {
00829   if (mUpdateBatchNest || !mView || mRowCount <= mPageLength)
00830     return;
00831   nsWeakFrame weakFrame(this);
00832   if (aParts.mVScrollbar) {
00833     // Do Vertical Scrollbar
00834     nsCOMPtr<nsIContent> scrollbar = aParts.mVScrollbarContent;
00835     nsAutoString maxposStr;
00836 
00837     float t2p = GetPresContext()->TwipsToPixels();
00838     nscoord rowHeightAsPixels = NSToCoordRound((float)mRowHeight*t2p);
00839 
00840     PRInt32 size = rowHeightAsPixels * (mRowCount > mPageLength ? mRowCount - mPageLength : 0);
00841     maxposStr.AppendInt(size);
00842     scrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::maxpos, maxposStr, PR_TRUE);
00843     ENSURE_TRUE(weakFrame.IsAlive());
00844 
00845     // Also set our page increment and decrement.
00846     nscoord pageincrement = mPageLength*rowHeightAsPixels;
00847     nsAutoString pageStr;
00848     pageStr.AppendInt(pageincrement);
00849     scrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::pageincrement, pageStr, PR_TRUE);
00850   }
00851 }
00852 
00853 
00854 // Takes client x/y in pixels, converts them to twips, and massages them to be
00855 // in our coordinate system.
00856 void
00857 nsTreeBodyFrame::AdjustClientCoordsToBoxCoordSpace(PRInt32 aX, PRInt32 aY,
00858                                                    nscoord* aResultX,
00859                                                    nscoord* aResultY)
00860 {
00861   // Convert our x and y coords to twips.
00862   // XXXbz should this use IntScaledPixelsToTwips?
00863   float pixelsToTwips = mPresContext->PixelsToTwips();
00864   nsPoint point(NSToIntRound(aX * pixelsToTwips),
00865                 NSToIntRound(aY * pixelsToTwips));
00866   
00867   // Now get our client offset, in twips, and subtract if from the
00868   // point to get it in our coordinates
00869   nsPoint clientOffset;
00870   nsIView* closestView = GetClosestView(&clientOffset);
00871   point -= clientOffset;
00872   
00873   nsIView* rootView;
00874   mPresContext->GetViewManager()->GetRootView(rootView);
00875   NS_ASSERTION(closestView && rootView, "No view?");
00876   point -= closestView->GetOffsetTo(rootView);
00877 
00878   // Adjust by the inner box coords, so that we're in the inner box's
00879   // coordinate space.
00880   point -= mInnerBox.TopLeft();
00881   
00882   *aResultX = point.x;
00883   *aResultY = point.y;
00884 } // AdjustClientCoordsToBoxCoordSpace
00885 
00886 NS_IMETHODIMP
00887 nsTreeBodyFrame::GetRowAt(PRInt32 aX, PRInt32 aY, PRInt32* _retval)
00888 {
00889   if (!mView)
00890     return NS_OK;
00891 
00892   nscoord x;
00893   nscoord y;
00894   AdjustClientCoordsToBoxCoordSpace(aX, aY, &x, &y);
00895 
00896   // Check if the coordinates are above our visible space.
00897   if (y < 0) {
00898     *_retval = -1;
00899     return NS_OK;
00900   }
00901 
00902   *_retval = GetRowAt(x, y);
00903 
00904   return NS_OK;
00905 }
00906 
00907 NS_IMETHODIMP
00908 nsTreeBodyFrame::GetCellAt(PRInt32 aX, PRInt32 aY, PRInt32* aRow, nsITreeColumn** aCol,
00909                            nsACString& aChildElt)
00910 {
00911   if (!mView)
00912     return NS_OK;
00913 
00914   nscoord x;
00915   nscoord y;
00916   AdjustClientCoordsToBoxCoordSpace(aX, aY, &x, &y);
00917 
00918   // Check if the coordinates are above our visible space.
00919   if (y < 0) {
00920     *aRow = -1;
00921     return NS_OK;
00922   }
00923 
00924   nsTreeColumn* col;
00925   nsIAtom* child;
00926   GetCellAt(x, y, aRow, &col, &child);
00927 
00928   if (col) {
00929     NS_ADDREF(*aCol = col);
00930     if (child == nsCSSAnonBoxes::moztreecell)
00931       aChildElt.AssignLiteral("cell");
00932     else if (child == nsCSSAnonBoxes::moztreetwisty)
00933       aChildElt.AssignLiteral("twisty");
00934     else if (child == nsCSSAnonBoxes::moztreeimage)
00935       aChildElt.AssignLiteral("image");
00936     else if (child == nsCSSAnonBoxes::moztreecelltext)
00937       aChildElt.AssignLiteral("text");
00938   }
00939 
00940   return NS_OK;
00941 }
00942 
00943 
00944 //
00945 // GetCoordsForCellItem
00946 //
00947 // Find the x/y location and width/height (all in PIXELS) of the given object
00948 // in the given column. 
00949 //
00950 // XXX IMPORTANT XXX:
00951 // Hyatt says in the bug for this, that the following needs to be done:
00952 // (1) You need to deal with overflow when computing cell rects.  See other column 
00953 // iteration examples... if you don't deal with this, you'll mistakenly extend the 
00954 // cell into the scrollbar's rect.
00955 //
00956 // (2) You are adjusting the cell rect by the *row" border padding.  That's 
00957 // wrong.  You need to first adjust a row rect by its border/padding, and then the 
00958 // cell rect fits inside the adjusted row rect.  It also can have border/padding 
00959 // as well as margins.  The vertical direction isn't that important, but you need 
00960 // to get the horizontal direction right.
00961 //
00962 // (3) GetImageSize() does not include margins (but it does include border/padding).  
00963 // You need to make sure to add in the image's margins as well.
00964 //
00965 NS_IMETHODIMP
00966 nsTreeBodyFrame::GetCoordsForCellItem(PRInt32 aRow, nsITreeColumn* aCol, const nsACString& aElement, 
00967                                       PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight)
00968 {
00969   *aX = 0;
00970   *aY = 0;
00971   *aWidth = 0;
00972   *aHeight = 0;
00973 
00974   nscoord currX = mInnerBox.x;
00975 
00976   // The Rect for the requested item. 
00977   nsRect theRect;
00978 
00979   for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol && currX < mInnerBox.x + mInnerBox.width;
00980        currCol = currCol->GetNext()) {
00981 
00982     // The Rect for the current cell. 
00983     nsRect cellRect(currX, mInnerBox.y + mRowHeight * (aRow - mTopRowIndex), currCol->GetWidth(), mRowHeight);
00984 
00985     // Check the ID of the current column to see if it matches. If it doesn't 
00986     // increment the current X value and continue to the next column.
00987     if (currCol != aCol) {
00988       currX += cellRect.width;
00989       continue;
00990     }
00991 
00992     // Now obtain the properties for our cell.
00993     PrefillPropertyArray(aRow, currCol);
00994     mView->GetCellProperties(aRow, currCol, mScratchArray);
00995 
00996     nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
00997 
00998     // We don't want to consider any of the decorations that may be present
00999     // on the current row, so we have to deflate the rect by the border and 
01000     // padding and offset its left and top coordinates appropriately. 
01001     AdjustForBorderPadding(rowContext, cellRect);
01002 
01003     nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
01004 
01005     NS_NAMED_LITERAL_CSTRING(cell, "cell");
01006     if (currCol->IsCycler() || cell.Equals(aElement)) {
01007       // If the current Column is a Cycler, then the Rect is just the cell - the margins. 
01008       // Similarly, if we're just being asked for the cell rect, provide it. 
01009 
01010       theRect = cellRect;
01011       nsMargin cellMargin;
01012       cellContext->GetStyleMargin()->GetMargin(cellMargin);
01013       theRect.Deflate(cellMargin);
01014       break;
01015     }
01016 
01017     // Since we're not looking for the cell, and since the cell isn't a cycler,
01018     // we're looking for some subcomponent, and now we need to subtract the 
01019     // borders and padding of the cell from cellRect so this does not 
01020     // interfere with our computations.
01021     AdjustForBorderPadding(cellContext, cellRect);
01022 
01023     // Now we'll start making our way across the cell, starting at the edge of 
01024     // the cell and proceeding until we hit the right edge. |cellX| is the 
01025     // working X value that we will increment as we crawl from left to right.
01026     nscoord cellX = cellRect.x;
01027     nscoord remainWidth = cellRect.width;
01028 
01029     if (currCol->IsPrimary()) {
01030       // If the current Column is a Primary, then we need to take into account the indentation
01031       // and possibly a twisty. 
01032 
01033       // The amount of indentation is the indentation width (|mIndentation|) by the level. 
01034       PRInt32 level;
01035       mView->GetLevel(aRow, &level);
01036       cellX += mIndentation * level;
01037       remainWidth -= mIndentation * level;
01038 
01039       PRBool hasTwisty = PR_FALSE;
01040       PRBool isContainer = PR_FALSE;
01041       mView->IsContainer(aRow, &isContainer);
01042       if (isContainer) {
01043         PRBool isContainerEmpty = PR_FALSE;
01044         mView->IsContainerEmpty(aRow, &isContainerEmpty);
01045         if (!isContainerEmpty)
01046           hasTwisty = PR_TRUE;
01047       }
01048 
01049       // Find the twisty rect by computing its size. 
01050       nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
01051 
01052       // |GetImageSize| returns the rect of the twisty image, including the
01053       // borders and padding.
01054       nsRect twistyImageRect = GetImageSize(aRow, currCol, PR_TRUE, twistyContext);
01055       if (NS_LITERAL_CSTRING("twisty").Equals(aElement)) {
01056         // If we're looking for the twisty Rect, just return the result of |GetImageSize|
01057         theRect = twistyImageRect;
01058         break;
01059       }
01060       
01061       // Now we need to add in the margins of the twisty element, so that we 
01062       // can find the offset of the next element in the cell. 
01063       nsMargin twistyMargin;
01064       twistyContext->GetStyleMargin()->GetMargin(twistyMargin);
01065       twistyImageRect.Inflate(twistyMargin);
01066 
01067       // Adjust our working X value with the twisty width (image size, margins,
01068       // borders, padding. 
01069       cellX += twistyImageRect.width;
01070     }
01071 
01072     // Cell Image
01073     nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
01074 
01075     nsRect imageSize = GetImageSize(aRow, currCol, PR_FALSE, imageContext);
01076     if (NS_LITERAL_CSTRING("image").Equals(aElement)) {
01077       theRect = imageSize;
01078       theRect.x = cellX;
01079       theRect.y = cellRect.y;
01080       break;
01081     }
01082 
01083     // Add in the margins of the cell image.
01084     nsMargin imageMargin;
01085     imageContext->GetStyleMargin()->GetMargin(imageMargin);
01086     imageSize.Inflate(imageMargin);
01087 
01088     // Increment cellX by the image width
01089     cellX += imageSize.width;
01090     
01091     // Cell Text 
01092     nsAutoString cellText;
01093     mView->GetCellText(aRow, currCol, cellText);
01094 
01095     // Create a scratch rect to represent the text rectangle, with the current 
01096     // X and Y coords, and a guess at the width and height. The width is the 
01097     // remaining width we have left to traverse in the cell, which will be the
01098     // widest possible value for the text rect, and the row height. 
01099     nsRect textRect(cellX, cellRect.y, remainWidth, mRowHeight);
01100 
01101     // Measure the width of the text. If the width of the text is greater than 
01102     // the remaining width available, then we just assume that the text has 
01103     // been cropped and use the remaining rect as the text Rect. Otherwise,
01104     // we add in borders and padding to the text dimension and give that back. 
01105     nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
01106 
01107     nsCOMPtr<nsIFontMetrics> fm;
01108     mPresContext->DeviceContext()->
01109       GetMetricsFor(textContext->GetStyleFont()->mFont, *getter_AddRefs(fm));
01110     nscoord height;
01111     fm->GetHeight(height);
01112 
01113     nsMargin bp(0,0,0,0);
01114     GetBorderPadding(textContext, bp);
01115     
01116     textRect.height = height + bp.top + bp.bottom;
01117 
01118     nsCOMPtr<nsIRenderingContext> rc;
01119     mPresContext->PresShell()->CreateRenderingContext(this, getter_AddRefs(rc));
01120     rc->SetFont(fm);
01121     nscoord width;
01122     rc->GetWidth(cellText, width);
01123 
01124     nscoord totalTextWidth = width + bp.left + bp.right;
01125     if (totalTextWidth < remainWidth) {
01126       // If the text is not cropped, the text is smaller than the available 
01127       // space and we set the text rect to be that width. 
01128       textRect.width = totalTextWidth;
01129     }
01130 
01131     theRect = textRect;
01132   }
01133   
01134   float t2p = mPresContext->TwipsToPixels();
01135   
01136   *aX = NSToIntRound(theRect.x * t2p);
01137   *aY = NSToIntRound(theRect.y * t2p);
01138   *aWidth = NSToIntRound(theRect.width * t2p);
01139   *aHeight = NSToIntRound(theRect.height * t2p);
01140  
01141   return NS_OK;
01142 }
01143 
01144 PRInt32
01145 nsTreeBodyFrame::GetRowAt(PRInt32 aX, PRInt32 aY)
01146 {
01147   // Now just mod by our total inner box height and add to our top row index.
01148   PRInt32 row = (aY/mRowHeight)+mTopRowIndex;
01149 
01150   // Check if the coordinates are below our visible space (or within our visible
01151   // space but below any row).
01152   if (row > mTopRowIndex + mPageLength || row >= mRowCount)
01153     return -1;
01154 
01155   return row;
01156 }
01157 
01158 nsIAtom*
01159 nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, 
01160                                      PRInt32 aRowIndex,
01161                                      nsTreeColumn* aColumn)
01162 {
01163   // Obtain the properties for our cell.
01164   PrefillPropertyArray(aRowIndex, aColumn);
01165   mView->GetCellProperties(aRowIndex, aColumn, mScratchArray);
01166 
01167   // Resolve style for the cell.
01168   nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
01169 
01170   // Obtain the margins for the cell and then deflate our rect by that 
01171   // amount.  The cell is assumed to be contained within the deflated rect.
01172   nsRect cellRect(aCellRect);
01173   nsMargin cellMargin;
01174   cellContext->GetStyleMargin()->GetMargin(cellMargin);
01175   cellRect.Deflate(cellMargin);
01176 
01177   // Adjust the rect for its border and padding.
01178   AdjustForBorderPadding(cellContext, cellRect);
01179 
01180   if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) {
01181     // The user clicked within the cell's margins/borders/padding.  This constitutes a click on the cell.
01182     return nsCSSAnonBoxes::moztreecell;
01183   }
01184 
01185   nscoord currX = cellRect.x;
01186   nscoord remainingWidth = cellRect.width;
01187 
01188   // XXX Handle right alignment hit testing.
01189 
01190   if (aColumn->IsPrimary()) {
01191     // If we're the primary column, we have indentation and a twisty.
01192     PRInt32 level;
01193     mView->GetLevel(aRowIndex, &level);
01194 
01195     currX += mIndentation*level;
01196     remainingWidth -= mIndentation*level;
01197 
01198     if (aX < currX) {
01199       // The user clicked within the indentation.
01200       return nsCSSAnonBoxes::moztreecell;
01201     }
01202 
01203     // Always leave space for the twisty.
01204     nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
01205     PRBool hasTwisty = PR_FALSE;
01206     PRBool isContainer = PR_FALSE;
01207     mView->IsContainer(aRowIndex, &isContainer);
01208     if (isContainer) {
01209       PRBool isContainerEmpty = PR_FALSE;
01210       mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
01211       if (!isContainerEmpty)
01212         hasTwisty = PR_TRUE;
01213     }
01214 
01215     // Resolve style for the twisty.
01216     nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
01217 
01218     // We will treat a click as hitting the twisty if it happens on the margins, borders, padding,
01219     // or content of the twisty object.  By allowing a "slop" into the margin, we make it a little
01220     // bit easier for a user to hit the twisty.  (We don't want to be too picky here.)
01221     nsRect imageSize = GetImageSize(aRowIndex, aColumn, PR_TRUE, twistyContext);
01222     nsMargin twistyMargin;
01223     twistyContext->GetStyleMargin()->GetMargin(twistyMargin);
01224     imageSize.Inflate(twistyMargin);
01225     twistyRect.width = imageSize.width;
01226 
01227     // Now we test to see if aX is actually within the twistyRect.  If it is, and if the item should
01228     // have a twisty, then we return "twisty".  If it is within the rect but we shouldn't have a twisty,
01229     // then we return "cell".
01230     if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) {
01231       if (hasTwisty)
01232         return nsCSSAnonBoxes::moztreetwisty;
01233       else
01234         return nsCSSAnonBoxes::moztreecell;
01235     }
01236 
01237     currX += twistyRect.width;
01238     remainingWidth -= twistyRect.width;    
01239   }
01240   
01241   // Now test to see if the user hit the icon for the cell.
01242   nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
01243   
01244   // Resolve style for the image.
01245   nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
01246 
01247   nsRect iconSize = GetImageSize(aRowIndex, aColumn, PR_FALSE, imageContext);
01248   nsMargin imageMargin;
01249   imageContext->GetStyleMargin()->GetMargin(imageMargin);
01250   iconSize.Inflate(imageMargin);
01251   iconRect.width = iconSize.width;
01252 
01253   if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) {
01254     // The user clicked on the image.
01255     return nsCSSAnonBoxes::moztreeimage;
01256   }
01257 
01258   // Just assume "text".
01259   // XXX For marquee selection, we'll have to make this more precise and do text measurement.
01260   return nsCSSAnonBoxes::moztreecelltext;
01261 }
01262 
01263 void
01264 nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, PRInt32* aRow,
01265                            nsTreeColumn** aCol, nsIAtom** aChildElt)
01266 {
01267   *aCol = nsnull;
01268   *aChildElt = nsnull;
01269 
01270   *aRow = GetRowAt(aX, aY);
01271   if (*aRow < 0)
01272     return;
01273 
01274   // Determine the column hit.
01275   for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol && currCol->GetX() < mInnerBox.x+mInnerBox.width; 
01276        currCol = currCol->GetNext()) {
01277     nsRect cellRect(currCol->GetX(), mInnerBox.y+mRowHeight*(*aRow-mTopRowIndex), currCol->GetWidth(), mRowHeight);
01278     PRInt32 overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
01279     if (overflow > 0)
01280       cellRect.width -= overflow;
01281 
01282     if (aX >= cellRect.x && aX < cellRect.x + cellRect.width) {
01283       // We know the column hit now.
01284       if (aCol)
01285         *aCol = currCol;
01286 
01287       if (currCol->IsCycler())
01288         // Cyclers contain only images.  Fill this in immediately and return.
01289         *aChildElt = nsCSSAnonBoxes::moztreeimage;
01290       else
01291         *aChildElt = GetItemWithinCellAt(aX, cellRect, *aRow, currCol);
01292       break;
01293     }
01294   }
01295 }
01296 
01297 void
01298 nsTreeBodyFrame::GetCellWidth(PRInt32 aRow, nsTreeColumn* aCol,
01299                               nsIRenderingContext* aRenderingContext,
01300                               nscoord& aDesiredSize, nscoord& aCurrentSize)
01301 {
01302   if (aCol) {
01303     // The rect for the current cell.
01304     nsRect cellRect(0, 0, aCol->GetWidth(), mRowHeight);
01305     PRInt32 overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
01306     if (overflow > 0)
01307       cellRect.width -= overflow;
01308 
01309     // Adjust borders and padding for the cell.
01310     nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
01311     nsMargin bp(0,0,0,0);
01312     GetBorderPadding(cellContext, bp);
01313 
01314     aCurrentSize = cellRect.width;
01315     aDesiredSize = bp.left + bp.right;
01316 
01317     if (aCol->IsPrimary()) {
01318       // If the current Column is a Primary, then we need to take into account 
01319       // the indentation and possibly a twisty. 
01320 
01321       // The amount of indentation is the indentation width (|mIndentation|) by the level.
01322       PRInt32 level;
01323       mView->GetLevel(aRow, &level);
01324       aDesiredSize += mIndentation * level;
01325       
01326       // Find the twisty rect by computing its size.
01327       nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
01328 
01329       // |GetImageSize| returns the rect of the twisty image, including the 
01330       // borders and padding.
01331       nsRect twistyImageRect = GetImageSize(aRow, aCol, PR_TRUE, twistyContext);
01332       
01333       // Add in the margins of the twisty element.
01334       nsMargin twistyMargin;
01335       twistyContext->GetStyleMargin()->GetMargin(twistyMargin);
01336       twistyImageRect.Inflate(twistyMargin);
01337 
01338       aDesiredSize += twistyImageRect.width;
01339     }
01340 
01341     nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
01342 
01343     // Account for the width of the cell image.
01344     nsRect imageSize = GetImageSize(aRow, aCol, PR_FALSE, imageContext);
01345     // Add in the margins of the cell image.
01346     nsMargin imageMargin;
01347     imageContext->GetStyleMargin()->GetMargin(imageMargin);
01348     imageSize.Inflate(imageMargin);
01349 
01350     aDesiredSize += imageSize.width;
01351     
01352     // Get the cell text.
01353     nsAutoString cellText;
01354     mView->GetCellText(aRow, aCol, cellText);
01355 
01356     nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
01357 
01358     // Get the borders and padding for the text.
01359     GetBorderPadding(textContext, bp);
01360     
01361     // Get the font style for the text and pass it to the rendering context.
01362     aRenderingContext->SetFont(textContext->GetStyleFont()->mFont, nsnull);
01363 
01364     // Get the width of the text itself
01365     nscoord width;
01366     aRenderingContext->GetWidth(cellText, width);
01367     nscoord totalTextWidth = width + bp.left + bp.right;
01368     aDesiredSize += totalTextWidth;
01369   }
01370 }
01371 
01372 NS_IMETHODIMP
01373 nsTreeBodyFrame::IsCellCropped(PRInt32 aRow, nsITreeColumn* aCol, PRBool *_retval)
01374 {  
01375   nscoord currentSize, desiredSize;
01376   nsCOMPtr<nsIRenderingContext> rc;
01377   mPresContext->PresShell()->CreateRenderingContext(this, getter_AddRefs(rc));
01378 
01379   nsTreeColumn* col = NS_STATIC_CAST(nsTreeColumn*, aCol);
01380   if (!col)
01381     return NS_ERROR_FAILURE;
01382 
01383   GetCellWidth(aRow, col, rc, desiredSize, currentSize);
01384   *_retval = desiredSize > currentSize;
01385   
01386   return NS_OK;
01387 }
01388 
01389 void
01390 nsTreeBodyFrame::MarkDirtyIfSelect()
01391 {
01392   nsIContent* baseElement = GetBaseElement();
01393 
01394   if (baseElement && baseElement->Tag() == nsHTMLAtoms::select &&
01395       baseElement->IsContentOfType(nsIContent::eHTML)) {
01396     // If we are an intrinsically sized select widget, we may need to
01397     // resize, if the widest item was removed or a new item was added.
01398     // XXX optimize this more
01399 
01400     mStringWidth = -1;
01401     nsBoxLayoutState state(mPresContext);
01402     MarkDirty(state);
01403   }
01404 }
01405 
01406 nsresult
01407 nsTreeBodyFrame::CreateTimer(const nsILookAndFeel::nsMetricID aID,
01408                              nsTimerCallbackFunc aFunc, PRInt32 aType,
01409                              nsITimer** aTimer)
01410 {
01411   // Get the delay from the look and feel service.
01412   PRInt32 delay = 0;
01413   mPresContext->LookAndFeel()->GetMetric(aID, delay);
01414 
01415   nsCOMPtr<nsITimer> timer;
01416 
01417   // Create a new timer only if the delay is greater than zero.
01418   // Zero value means that this feature is completely disabled.
01419   if (delay > 0) {
01420     timer = do_CreateInstance("@mozilla.org/timer;1");
01421     if (timer) {
01422       nsCOMPtr<nsITimerInternal> timerInternal = do_QueryInterface(timer);
01423       if (timerInternal) {
01424         timerInternal->SetIdle(PR_FALSE);
01425       }
01426       timer->InitWithFuncCallback(aFunc, this, delay, aType);
01427     }
01428   }
01429 
01430   NS_IF_ADDREF(*aTimer = timer);
01431 
01432   return NS_OK;
01433 }
01434 
01435 NS_IMETHODIMP nsTreeBodyFrame::RowCountChanged(PRInt32 aIndex, PRInt32 aCount)
01436 {
01437   if (aCount == 0 || !mView)
01438     return NS_OK; // Nothing to do.
01439 
01440   // Adjust our selection.
01441   nsCOMPtr<nsITreeSelection> sel;
01442   mView->GetSelection(getter_AddRefs(sel));
01443   if (sel)
01444     sel->AdjustSelection(aIndex, aCount);
01445 
01446   if (mUpdateBatchNest)
01447     return NS_OK;
01448 
01449   mRowCount += aCount;
01450 #ifdef DEBUG
01451   PRInt32 rowCount = mRowCount;
01452   mView->GetRowCount(&rowCount);
01453   NS_ASSERTION(rowCount == mRowCount, "row count did not change by the amount suggested, check caller");
01454 #endif
01455 
01456   PRInt32 count = PR_ABS(aCount);
01457   PRInt32 last = GetLastVisibleRow();
01458   if (aIndex >= mTopRowIndex && aIndex <= last)
01459     InvalidateRange(aIndex, last);
01460 
01461   ScrollParts parts = GetScrollParts();
01462 
01463   if (mTopRowIndex == 0) {    
01464     // Just update the scrollbar and return.
01465     if (FullScrollbarUpdate(PR_FALSE)) {
01466       MarkDirtyIfSelect();
01467     }
01468     return NS_OK;
01469   }
01470 
01471   PRBool needsInvalidation = PR_FALSE;
01472   // Adjust our top row index.
01473   if (aCount > 0) {
01474     if (mTopRowIndex > aIndex) {
01475       // Rows came in above us.  Augment the top row index.
01476       mTopRowIndex += aCount;
01477     }
01478   }
01479   else if (aCount < 0) {
01480     if (mTopRowIndex > aIndex+count-1) {
01481       // No need to invalidate. The remove happened
01482       // completely above us (offscreen).
01483       mTopRowIndex -= count;
01484     }
01485     else if (mTopRowIndex >= aIndex) {
01486       // This is a full-blown invalidate.
01487       if (mTopRowIndex + mPageLength > mRowCount - 1) {
01488         mTopRowIndex = PR_MAX(0, mRowCount - 1 - mPageLength);
01489       }
01490       needsInvalidation = PR_TRUE;
01491     }
01492   }
01493 
01494   if (FullScrollbarUpdate(needsInvalidation)) {
01495     MarkDirtyIfSelect();
01496   }
01497 
01498   return NS_OK;
01499 }
01500 
01501 NS_IMETHODIMP nsTreeBodyFrame::BeginUpdateBatch()
01502 {
01503   ++mUpdateBatchNest;
01504 
01505   return NS_OK;
01506 }
01507 
01508 NS_IMETHODIMP nsTreeBodyFrame::EndUpdateBatch()
01509 {
01510   NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
01511 
01512   if (--mUpdateBatchNest == 0) {
01513     if (mView) {
01514       Invalidate();
01515       PRInt32 countBeforeUpdate = mRowCount;
01516       mView->GetRowCount(&mRowCount);
01517       if (countBeforeUpdate != mRowCount) {
01518 
01519         if (mTopRowIndex + mPageLength > mRowCount - 1) {
01520           mTopRowIndex = PR_MAX(0, mRowCount - 1 - mPageLength);
01521         }
01522         FullScrollbarUpdate(PR_FALSE);
01523       }
01524     }
01525   }
01526 
01527   return NS_OK;
01528 }
01529 
01530 void
01531 nsTreeBodyFrame::PrefillPropertyArray(PRInt32 aRowIndex, nsTreeColumn* aCol)
01532 {
01533   mScratchArray->Clear();
01534   
01535   // focus
01536   if (mFocused)
01537     mScratchArray->AppendElement(nsXULAtoms::focus);
01538 
01539   // sort
01540   PRBool sorted = PR_FALSE;
01541   mView->IsSorted(&sorted);
01542   if (sorted)
01543     mScratchArray->AppendElement(nsXULAtoms::sorted);
01544 
01545   // drag session
01546   if (mSlots && mSlots->mDragSession)
01547     mScratchArray->AppendElement(nsXULAtoms::dragSession);
01548 
01549   if (aRowIndex != -1) {
01550     nsCOMPtr<nsITreeSelection> selection;
01551     mView->GetSelection(getter_AddRefs(selection));
01552   
01553     if (selection) {
01554       // selected
01555       PRBool isSelected;
01556       selection->IsSelected(aRowIndex, &isSelected);
01557       if (isSelected)
01558         mScratchArray->AppendElement(nsHTMLAtoms::selected);
01559 
01560       // current
01561       PRInt32 currentIndex;
01562       selection->GetCurrentIndex(&currentIndex);
01563       if (aRowIndex == currentIndex)
01564         mScratchArray->AppendElement(nsXULAtoms::current);
01565     }
01566 
01567     // container or leaf
01568     PRBool isContainer = PR_FALSE;
01569     mView->IsContainer(aRowIndex, &isContainer);
01570     if (isContainer) {
01571       mScratchArray->AppendElement(nsXULAtoms::container);
01572 
01573       // open or closed
01574       PRBool isOpen = PR_FALSE;
01575       mView->IsContainerOpen(aRowIndex, &isOpen);
01576       if (isOpen)
01577         mScratchArray->AppendElement(nsXULAtoms::open);
01578       else
01579         mScratchArray->AppendElement(nsXULAtoms::closed);
01580     }
01581     else {
01582       mScratchArray->AppendElement(nsXULAtoms::leaf);
01583     }
01584 
01585     // drop orientation
01586     if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) {
01587       if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE)
01588         mScratchArray->AppendElement(nsXULAtoms::dropBefore);
01589       else if (mSlots->mDropOrient == nsITreeView::DROP_ON)
01590         mScratchArray->AppendElement(nsXULAtoms::dropOn);
01591       else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
01592         mScratchArray->AppendElement(nsXULAtoms::dropAfter);
01593     }
01594 
01595     // odd or even
01596     if (aRowIndex % 2)
01597       mScratchArray->AppendElement(nsXULAtoms::odd);
01598     else
01599       mScratchArray->AppendElement(nsXULAtoms::even);
01600   }
01601 
01602   if (aCol) {
01603     mScratchArray->AppendElement(aCol->GetAtom());
01604 
01605     if (aCol->IsPrimary())
01606       mScratchArray->AppendElement(nsXULAtoms::primary);
01607 
01608     if (aCol->GetType() == nsITreeColumn::TYPE_CHECKBOX) {
01609       mScratchArray->AppendElement(nsXULAtoms::checkbox);
01610 
01611       if (aRowIndex != -1) {
01612         nsAutoString value;
01613         mView->GetCellValue(aRowIndex, aCol, value);
01614         if (value.EqualsLiteral("true"))
01615           mScratchArray->AppendElement(nsXULAtoms::checked);
01616       }
01617     }
01618     else if (aCol->GetType() == nsITreeColumn::TYPE_PROGRESSMETER) {
01619       mScratchArray->AppendElement(nsXULAtoms::progressmeter);
01620 
01621       if (aRowIndex != -1) {
01622         PRInt32 state;
01623         mView->GetProgressMode(aRowIndex, aCol, &state);
01624         if (state == nsITreeView::PROGRESS_NORMAL)
01625           mScratchArray->AppendElement(nsXULAtoms::progressNormal);
01626         else if (state == nsITreeView::PROGRESS_UNDETERMINED)
01627           mScratchArray->AppendElement(nsXULAtoms::progressUndetermined);
01628       }
01629     }
01630 
01631     // Read special properties from attributes on the column content node
01632     nsAutoString attr;
01633     aCol->GetContent()->GetAttr(kNameSpaceID_None, nsXULAtoms::insertbefore, attr);
01634     if (attr.EqualsLiteral("true"))
01635       mScratchArray->AppendElement(nsXULAtoms::insertbefore);
01636     attr.Truncate();
01637     aCol->GetContent()->GetAttr(kNameSpaceID_None, nsXULAtoms::insertafter, attr);
01638     if (attr.EqualsLiteral("true"))
01639       mScratchArray->AppendElement(nsXULAtoms::insertafter);
01640   }
01641 }
01642 
01643 nsresult
01644 nsTreeBodyFrame::GetImage(PRInt32 aRowIndex, nsTreeColumn* aCol, PRBool aUseContext,
01645                           nsStyleContext* aStyleContext, PRBool& aAllowImageRegions, imgIContainer** aResult)
01646 {
01647   *aResult = nsnull;
01648 
01649   nsAutoString imageSrc;
01650   mView->GetImageSrc(aRowIndex, aCol, imageSrc);
01651   nsCOMPtr<imgIRequest> styleRequest;
01652   if (!aUseContext && !imageSrc.IsEmpty()) {
01653     aAllowImageRegions = PR_FALSE;
01654   }
01655   else {
01656     // Obtain the URL from the style context.
01657     aAllowImageRegions = PR_TRUE;
01658     styleRequest = aStyleContext->GetStyleList()->mListStyleImage;
01659     if (!styleRequest)
01660       return NS_OK;
01661     nsCOMPtr<nsIURI> uri;
01662     styleRequest->GetURI(getter_AddRefs(uri));
01663     nsCAutoString spec;
01664     uri->GetSpec(spec);
01665     CopyUTF8toUTF16(spec, imageSrc);
01666   }
01667 
01668   // Look the image up in our cache.
01669   nsTreeImageCacheEntry entry;
01670   if (mImageCache.Get(imageSrc, &entry)) {
01671     // Find out if the image has loaded.
01672     PRUint32 status;
01673     imgIRequest *imgReq = entry.request;
01674     imgReq->GetImageStatus(&status);
01675     imgReq->GetImage(aResult); // We hand back the image here.  The GetImage call addrefs *aResult.
01676     PRUint32 numFrames = 1;
01677     if (*aResult)
01678       (*aResult)->GetNumFrames(&numFrames);
01679 
01680     if ((!(status & imgIRequest::STATUS_LOAD_COMPLETE)) || numFrames > 1) {
01681       // We either aren't done loading, or we're animating. Add our row as a listener for invalidations.
01682       nsCOMPtr<imgIDecoderObserver> obs;
01683       imgReq->GetDecoderObserver(getter_AddRefs(obs));
01684       nsCOMPtr<nsITreeImageListener> listener(do_QueryInterface(obs));
01685       if (listener)
01686         listener->AddCell(aRowIndex, aCol);
01687       return NS_OK;
01688     }
01689   }
01690 
01691   if (!*aResult) {
01692     // Create a new nsTreeImageListener object and pass it our row and column
01693     // information.
01694     nsTreeImageListener* listener = new nsTreeImageListener(mTreeBoxObject);
01695     if (!listener)
01696       return NS_ERROR_OUT_OF_MEMORY;
01697 
01698     listener->AddCell(aRowIndex, aCol);
01699     nsCOMPtr<imgIDecoderObserver> imgDecoderObserver = listener;
01700 
01701     nsCOMPtr<imgIRequest> imageRequest;
01702     if (styleRequest) {
01703       styleRequest->Clone(imgDecoderObserver, getter_AddRefs(imageRequest));
01704     } else {
01705       nsIDocument* doc = mContent->GetDocument();
01706       if (!doc)
01707         // The page is currently being torn down.  Why bother.
01708         return NS_ERROR_FAILURE;
01709 
01710       nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
01711 
01712       nsCOMPtr<nsIURI> srcURI;
01713       nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcURI),
01714                                                 imageSrc,
01715                                                 doc,
01716                                                 baseURI);
01717       if (!srcURI)
01718         return NS_ERROR_FAILURE;
01719 
01720       if (nsContentUtils::CanLoadImage(srcURI, mContent, doc)) {
01721         nsresult rv = nsContentUtils::LoadImage(srcURI,
01722                                                 doc,
01723                                                 doc->GetDocumentURI(),
01724                                                 imgDecoderObserver,
01725                                                 nsIRequest::LOAD_NORMAL,
01726                                                 getter_AddRefs(imageRequest));
01727         NS_ENSURE_SUCCESS(rv, rv);
01728                                   
01729       }
01730     }
01731     listener->UnsuppressInvalidation();
01732 
01733     if (!imageRequest)
01734       return NS_ERROR_FAILURE;
01735 
01736     // In a case it was already cached.
01737     imageRequest->GetImage(aResult);
01738     nsTreeImageCacheEntry cacheEntry(imageRequest, imgDecoderObserver);
01739     mImageCache.Put(imageSrc, cacheEntry);
01740   }
01741   return NS_OK;
01742 }
01743 
01744 nsRect nsTreeBodyFrame::GetImageSize(PRInt32 aRowIndex, nsTreeColumn* aCol, PRBool aUseContext,
01745                                      nsStyleContext* aStyleContext)
01746 {
01747   // XXX We should respond to visibility rules for collapsed vs. hidden.
01748 
01749   // This method returns the width of the twisty INCLUDING borders and padding.
01750   // It first checks the style context for a width.  If none is found, it tries to
01751   // use the default image width for the twisty.  If no image is found, it defaults
01752   // to border+padding.
01753   nsRect r(0,0,0,0);
01754   nsMargin bp(0,0,0,0);
01755   GetBorderPadding(aStyleContext, bp);
01756   r.Inflate(bp);
01757 
01758   // Now r contains our border+padding info.  We now need to get our width and
01759   // height.
01760   PRBool needWidth = PR_FALSE;
01761   PRBool needHeight = PR_FALSE;
01762 
01763   // We have to load image even though we already have a size.
01764   // Don't change this, otherwise things start to go crazy.
01765   PRBool useImageRegion = PR_TRUE;
01766   nsCOMPtr<imgIContainer> image;
01767   GetImage(aRowIndex, aCol, aUseContext, aStyleContext, useImageRegion, getter_AddRefs(image));
01768 
01769   const nsStylePosition* myPosition = aStyleContext->GetStylePosition();
01770   const nsStyleList* myList = aStyleContext->GetStyleList();
01771 
01772   if (useImageRegion) {
01773     r.x += myList->mImageRegion.x;
01774     r.y += myList->mImageRegion.y;
01775   }
01776 
01777   if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord)  {
01778     PRInt32 val = myPosition->mWidth.GetCoordValue();
01779     r.width += val;
01780   }
01781   else if (useImageRegion && myList->mImageRegion.width > 0)
01782     r.width += myList->mImageRegion.width;
01783   else 
01784     needWidth = PR_TRUE;
01785 
01786   if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)  {
01787     PRInt32 val = myPosition->mHeight.GetCoordValue();
01788     r.height += val;
01789   }
01790   else if (useImageRegion && myList->mImageRegion.height > 0)
01791     r.height += myList->mImageRegion.height;
01792   else 
01793     needHeight = PR_TRUE;
01794 
01795   if (image) {
01796     if (needWidth || needHeight) {
01797       // Get the natural image size.
01798       float p2t = mPresContext->PixelsToTwips();
01799 
01800       if (needWidth) {
01801         // Get the size from the image.
01802         nscoord width;
01803         image->GetWidth(&width);
01804         r.width += NSIntPixelsToTwips(width, p2t); 
01805       }
01806     
01807       if (needHeight) {
01808         nscoord height;
01809         image->GetHeight(&height);
01810         r.height += NSIntPixelsToTwips(height, p2t); 
01811       }
01812     }
01813   }
01814 
01815   return r;
01816 }
01817 
01818 PRInt32 nsTreeBodyFrame::GetRowHeight()
01819 {
01820   // Look up the correct height.  It is equal to the specified height
01821   // + the specified margins.
01822   mScratchArray->Clear();
01823   nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
01824   if (rowContext) {
01825     const nsStylePosition* myPosition = rowContext->GetStylePosition();
01826 
01827     nscoord minHeight = 0;
01828     if (myPosition->mMinHeight.GetUnit() == eStyleUnit_Coord)
01829       minHeight = myPosition->mMinHeight.GetCoordValue();
01830 
01831     nscoord height = 0;
01832     if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)
01833       height = myPosition->mHeight.GetCoordValue();
01834 
01835     if (height < minHeight)
01836       height = minHeight;
01837 
01838     if (height > 0) {
01839       float t2p = mPresContext->TwipsToPixels();
01840       height = NSTwipsToIntPixels(height, t2p);
01841       height += height % 2;
01842       float p2t = mPresContext->PixelsToTwips();
01843       height = NSIntPixelsToTwips(height, p2t);
01844 
01845       // XXX Check box-sizing to determine if border/padding should augment the height
01846       // Inflate the height by our margins.
01847       nsRect rowRect(0,0,0,height);
01848       nsMargin rowMargin;
01849       rowContext->GetStyleMargin()->GetMargin(rowMargin);
01850       rowRect.Inflate(rowMargin);
01851       height = rowRect.height;
01852       return height;
01853     }
01854   }
01855 
01856   float p2t = mPresContext->PixelsToTwips();
01857   return NSIntPixelsToTwips(18, p2t); // As good a default as any.
01858 }
01859 
01860 PRInt32 nsTreeBodyFrame::GetIndentation()
01861 {
01862   // Look up the correct indentation.  It is equal to the specified indentation width.
01863   mScratchArray->Clear();
01864   nsStyleContext* indentContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeindentation);
01865   if (indentContext) {
01866     const nsStylePosition* myPosition = indentContext->GetStylePosition();
01867     if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord)  {
01868       nscoord val = myPosition->mWidth.GetCoordValue();
01869       return val;
01870     }
01871   }
01872   float p2t = mPresContext->PixelsToTwips();
01873   return NSIntPixelsToTwips(16, p2t); // As good a default as any.
01874 }
01875 
01876 void nsTreeBodyFrame::CalcInnerBox()
01877 {
01878   mInnerBox.SetRect(0, 0, mRect.width, mRect.height);
01879   AdjustForBorderPadding(mStyleContext, mInnerBox);
01880 }
01881 
01882 NS_IMETHODIMP
01883 nsTreeBodyFrame::GetCursor(const nsPoint& aPoint,
01884                            nsIFrame::Cursor& aCursor)
01885 {
01886   if (mView) {
01887     PRInt32 row;
01888     nsTreeColumn* col;
01889     nsIAtom* child;
01890     GetCellAt(aPoint.x, aPoint.y, &row, &col, &child);
01891 
01892     if (child) {
01893       // Our scratch array is already prefilled.
01894       nsStyleContext* childContext = GetPseudoStyleContext(child);
01895 
01896       FillCursorInformationFromStyle(childContext->GetStyleUserInterface(),
01897                                      aCursor);
01898       if (aCursor.mCursor == NS_STYLE_CURSOR_AUTO)
01899         aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
01900 
01901       return NS_OK;
01902     }
01903   }
01904 
01905   return nsLeafBoxFrame::GetCursor(aPoint, aCursor);
01906 }
01907 
01908 NS_IMETHODIMP
01909 nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext,
01910                              nsGUIEvent* aEvent,
01911                              nsEventStatus* aEventStatus)
01912 {
01913   if (aEvent->message == NS_DRAGDROP_ENTER) {
01914     if (!mSlots)
01915       mSlots = new Slots();
01916 
01917     // Cache several things we'll need throughout the course of our work. These
01918     // will all get released on a drag exit.
01919 
01920     if (mSlots->mTimer) {
01921       mSlots->mTimer->Cancel();
01922       mSlots->mTimer = nsnull;
01923     }
01924 
01925     // Cache the drag session.
01926     nsCOMPtr<nsIDragService> dragService = 
01927              do_GetService("@mozilla.org/widget/dragservice;1");
01928     dragService->GetCurrentSession(getter_AddRefs(mSlots->mDragSession));
01929     NS_ASSERTION(mSlots->mDragSession, "can't get drag session");
01930 
01931     if (mSlots->mDragSession)
01932       mSlots->mDragSession->GetDragAction(&mSlots->mDragAction);
01933     else
01934       mSlots->mDragAction = 0;
01935   }
01936   else if (aEvent->message == NS_DRAGDROP_OVER) {
01937     // The mouse is hovering over this tree. If we determine things are
01938     // different from the last time, invalidate the drop feedback at the old
01939     // position, query the view to see if the current location is droppable,
01940     // and then invalidate the drop feedback at the new location if it is.
01941     // The mouse may or may not have changed position from the last time
01942     // we were called, so optimize out a lot of the extra notifications by
01943     // checking if anything changed first. For drop feedback we use drop,
01944     // dropBefore and dropAfter property.
01945 
01946     if (!mView || !mSlots)
01947       return NS_OK;
01948 
01949     // Save last values, we will need them.
01950     PRInt32 lastDropRow = mSlots->mDropRow;
01951     PRInt16 lastDropOrient = mSlots->mDropOrient;
01952     PRInt16 lastScrollLines = mSlots->mScrollLines;
01953 
01954     // Find out the current drag action
01955     PRUint32 lastDragAction = mSlots->mDragAction;
01956     if (mSlots->mDragSession)
01957       mSlots->mDragSession->GetDragAction(&mSlots->mDragAction);
01958 
01959     // Compute the row mouse is over and the above/below/on state.
01960     // Below we'll use this to see if anything changed.
01961     // Also check if we want to auto-scroll.
01962     ComputeDropPosition(aEvent, &mSlots->mDropRow, &mSlots->mDropOrient, &mSlots->mScrollLines);
01963 
01964     // While we're here, handle tracking of scrolling during a drag.
01965     if (mSlots->mScrollLines) {
01966       if (mSlots->mDropAllowed) {
01967         // Invalidate primary cell at old location.
01968         mSlots->mDropAllowed = PR_FALSE;
01969         InvalidateDropFeedback(lastDropRow, lastDropOrient);
01970       }
01971 #if !defined(XP_MAC) && !defined(XP_MACOSX)
01972       if (!lastScrollLines) {
01973         // Cancel any previosly initialized timer.
01974         if (mSlots->mTimer) {
01975           mSlots->mTimer->Cancel();
01976           mSlots->mTimer = nsnull;
01977         }
01978 
01979         // Set a timer to trigger the tree scrolling.
01980         CreateTimer(nsILookAndFeel::eMetric_TreeLazyScrollDelay,
01981                     LazyScrollCallback, nsITimer::TYPE_ONE_SHOT,
01982                     getter_AddRefs(mSlots->mTimer));
01983        }
01984 #else
01985       ScrollByLines(mSlots->mScrollLines);
01986 #endif
01987       // Bail out to prevent spring loaded timer and feedback line settings.
01988       return NS_OK;
01989     }
01990 
01991     // If changed from last time, invalidate primary cell at the old location and if allowed, 
01992     // invalidate primary cell at the new location. If nothing changed, just bail.
01993     if (mSlots->mDropRow != lastDropRow ||
01994         mSlots->mDropOrient != lastDropOrient ||
01995         mSlots->mDragAction != lastDragAction) {
01996 
01997       // Invalidate row at the old location.
01998       if (mSlots->mDropAllowed) {
01999         mSlots->mDropAllowed = PR_FALSE;
02000         InvalidateDropFeedback(lastDropRow, lastDropOrient);
02001       }
02002 
02003       if (mSlots->mTimer) {
02004         // Timer is active but for a different row than the current one, kill it.
02005         mSlots->mTimer->Cancel();
02006         mSlots->mTimer = nsnull;
02007       }
02008 
02009       if (mSlots->mDropRow >= 0) {
02010         if (!mSlots->mTimer && mSlots->mDropOrient == nsITreeView::DROP_ON) {
02011           // Either there wasn't a timer running or it was just killed above.
02012           // If over a folder, start up a timer to open the folder.
02013           PRBool isContainer = PR_FALSE;
02014           mView->IsContainer(mSlots->mDropRow, &isContainer);
02015           if (isContainer) {
02016             PRBool isOpen = PR_FALSE;
02017             mView->IsContainerOpen(mSlots->mDropRow, &isOpen);
02018             if (!isOpen) {
02019               // This node isn't expanded, set a timer to expand it.
02020               CreateTimer(nsILookAndFeel::eMetric_TreeOpenDelay,
02021                           OpenCallback, nsITimer::TYPE_ONE_SHOT,
02022                           getter_AddRefs(mSlots->mTimer));
02023             }
02024           }
02025         }
02026 
02027         PRBool canDropAtNewLocation = PR_FALSE;
02028         mView->CanDrop(mSlots->mDropRow, mSlots->mDropOrient, &canDropAtNewLocation);
02029       
02030         if (canDropAtNewLocation) {
02031           // Invalidate row at the new location.
02032           mSlots->mDropAllowed = canDropAtNewLocation;
02033           InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
02034         }
02035       }
02036     }
02037 
02038     // Alert the drag session we accept the drop. We have to do this every time
02039     // since the |canDrop| attribute is reset before we're called.
02040     if (mSlots->mDropAllowed && mSlots->mDragSession)
02041       mSlots->mDragSession->SetCanDrop(PR_TRUE);
02042   }
02043   else if (aEvent->message == NS_DRAGDROP_DROP) {
02044      // this event was meant for another frame, so ignore it
02045      if (!mSlots)
02046        return NS_OK;
02047 
02048     // Tell the view where the drop happened.
02049 
02050     // Remove the drop folder and all its parents from the array.
02051     PRInt32 parentIndex;
02052     nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex);
02053     while (NS_SUCCEEDED(rv) && parentIndex >= 0) {
02054       mSlots->mValueArray.RemoveValue(parentIndex);
02055       rv = mView->GetParentIndex(parentIndex, &parentIndex);
02056     }
02057 
02058     mView->Drop(mSlots->mDropRow, mSlots->mDropOrient);
02059   }
02060   else if (aEvent->message == NS_DRAGDROP_EXIT) {
02061     // this event was meant for another frame, so ignore it
02062     if (!mSlots)
02063       return NS_OK;
02064 
02065     // Clear out all our tracking vars.
02066 
02067     if (mSlots->mDropAllowed) {
02068       mSlots->mDropAllowed = PR_FALSE;
02069       InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
02070     }
02071     else
02072       mSlots->mDropAllowed = PR_FALSE;
02073     mSlots->mDropRow = -1;
02074     mSlots->mDropOrient = -1;
02075     mSlots->mDragSession = nsnull;
02076     mSlots->mScrollLines = 0;
02077 
02078     if (mSlots->mTimer) {
02079       mSlots->mTimer->Cancel();
02080       mSlots->mTimer = nsnull;
02081     }
02082 
02083     if (mSlots->mValueArray.Count()) {
02084       // Close all spring loaded folders except the drop folder.
02085       CreateTimer(nsILookAndFeel::eMetric_TreeCloseDelay,
02086                   CloseCallback, nsITimer::TYPE_ONE_SHOT,
02087                   getter_AddRefs(mSlots->mTimer));
02088     }
02089   }
02090 
02091   return NS_OK;
02092 }
02093 
02094 
02095 nsLineStyle nsTreeBodyFrame::ConvertBorderStyleToLineStyle(PRUint8 aBorderStyle)
02096 {
02097   switch (aBorderStyle) {
02098     case NS_STYLE_BORDER_STYLE_DOTTED:
02099       return nsLineStyle_kDotted;
02100     case NS_STYLE_BORDER_STYLE_DASHED:
02101       return nsLineStyle_kDashed;
02102     default:
02103       return nsLineStyle_kSolid;
02104   }
02105 }
02106 
02107 // Painting routines
02108 NS_IMETHODIMP
02109 nsTreeBodyFrame::Paint(nsPresContext*      aPresContext,
02110                        nsIRenderingContext& aRenderingContext,
02111                        const nsRect&        aDirtyRect,
02112                        nsFramePaintLayer    aWhichLayer,
02113                        PRUint32             aFlags)
02114 {
02115   if (aWhichLayer != NS_FRAME_PAINT_LAYER_BACKGROUND &&
02116       aWhichLayer != NS_FRAME_PAINT_LAYER_FOREGROUND)
02117     return NS_OK;
02118 
02119   if (!GetStyleVisibility()->IsVisibleOrCollapsed())
02120     return NS_OK; // We're invisible.  Don't paint.
02121 
02122   // Handles painting our background, border, and outline.
02123   nsresult rv = nsLeafFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
02124   if (NS_FAILED(rv)) return rv;
02125 
02126   if (aWhichLayer == NS_FRAME_PAINT_LAYER_FOREGROUND) {
02127     if (!mView)
02128       return NS_OK;
02129 
02130     // Update our available height and our page count.
02131     CalcInnerBox();
02132     PRInt32 oldPageCount = mPageLength;
02133     if (!mHasFixedRowCount)
02134       mPageLength = mInnerBox.height/mRowHeight;
02135 
02136     if (oldPageCount != mPageLength) {
02137       // Schedule a ResizeReflow that will update our page count properly.
02138       nsBoxLayoutState state(mPresContext);
02139       MarkDirty(state);
02140     }
02141 
02142 #ifdef DEBUG
02143     PRInt32 rowCount = mRowCount;
02144     mView->GetRowCount(&mRowCount);
02145     NS_ASSERTION(mRowCount == rowCount, "row count changed unexpectedly");
02146 #endif
02147 
02148     // Loop through our columns and paint them (e.g., for sorting).  This is only
02149     // relevant when painting backgrounds, since columns contain no content.  Content
02150     // is contained in the rows.
02151     for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol && currCol->GetX() < mInnerBox.x+mInnerBox.width; 
02152          currCol = currCol->GetNext()) {
02153       // Don't paint hidden columns.
02154       if (currCol->GetWidth()) {
02155         nsRect colRect(currCol->GetX(), mInnerBox.y, currCol->GetWidth(), mInnerBox.height);
02156         PRInt32 overflow = colRect.x+colRect.width-(mInnerBox.x+mInnerBox.width);
02157         if (overflow > 0)
02158           colRect.width -= overflow;
02159         nsRect dirtyRect;
02160         if (dirtyRect.IntersectRect(aDirtyRect, colRect)) {
02161           PaintColumn(currCol, colRect, aPresContext, aRenderingContext, aDirtyRect); 
02162         }
02163       }
02164     }
02165     // Loop through our on-screen rows.
02166     for (PRInt32 i = mTopRowIndex; i < mRowCount && i <= mTopRowIndex+mPageLength; i++) {
02167       nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*(i-mTopRowIndex), mInnerBox.width, mRowHeight);
02168       nsRect dirtyRect;
02169       if (dirtyRect.IntersectRect(aDirtyRect, rowRect) && rowRect.y < (mInnerBox.y+mInnerBox.height)) {
02170         PRBool clip = (rowRect.y + rowRect.height > mInnerBox.y + mInnerBox.height);
02171         if (clip) {
02172           // We need to clip the last row, since it extends outside our inner box. Push
02173           // a clip rect down.
02174           PRInt32 overflow = (rowRect.y+rowRect.height) - (mInnerBox.y+mInnerBox.height);
02175           nsRect clipRect(rowRect.x, rowRect.y, mInnerBox.width, mRowHeight-overflow);
02176           aRenderingContext.PushState();
02177           aRenderingContext.SetClipRect(clipRect, nsClipCombine_kReplace);
02178         }
02179 
02180         PaintRow(i, rowRect, aPresContext, aRenderingContext, aDirtyRect);
02181 
02182         if (clip)
02183           aRenderingContext.PopState();
02184       }
02185     }
02186 
02187     if (mSlots && mSlots->mDropAllowed && (mSlots->mDropOrient == nsITreeView::DROP_BEFORE ||
02188         mSlots->mDropOrient == nsITreeView::DROP_AFTER)) {
02189       nscoord yPos = mInnerBox.y + mRowHeight * (mSlots->mDropRow - mTopRowIndex) - mRowHeight / 2;
02190       nsRect feedbackRect(mInnerBox.x, yPos, mInnerBox.width, mRowHeight);
02191       if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
02192         feedbackRect.y += mRowHeight;
02193 
02194       nsRect dirtyRect;
02195       if (dirtyRect.IntersectRect(aDirtyRect, feedbackRect)) {
02196         PaintDropFeedback(feedbackRect, aPresContext, aRenderingContext, aDirtyRect);
02197       }
02198     }
02199   }
02200 
02201   return NS_OK;
02202 }
02203 
02204 void
02205 nsTreeBodyFrame::PaintColumn(nsTreeColumn*        aColumn,
02206                              const nsRect&        aColumnRect,
02207                              nsPresContext*      aPresContext,
02208                              nsIRenderingContext& aRenderingContext,
02209                              const nsRect&        aDirtyRect)
02210 {
02211   // Now obtain the properties for our cell.
02212   PrefillPropertyArray(-1, aColumn);
02213   mView->GetColumnProperties(aColumn, mScratchArray);
02214 
02215   // Resolve style for the column.  It contains all the info we need to lay ourselves
02216   // out and to paint.
02217   nsStyleContext* colContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecolumn);
02218 
02219   // Obtain the margins for the cell and then deflate our rect by that 
02220   // amount.  The cell is assumed to be contained within the deflated rect.
02221   nsRect colRect(aColumnRect);
02222   nsMargin colMargin;
02223   colContext->GetStyleMargin()->GetMargin(colMargin);
02224   colRect.Deflate(colMargin);
02225 
02226   PaintBackgroundLayer(colContext, aPresContext, aRenderingContext, colRect, aDirtyRect);
02227 }
02228 
02229 void
02230 nsTreeBodyFrame::PaintRow(PRInt32              aRowIndex,
02231                           const nsRect&        aRowRect,
02232                           nsPresContext*      aPresContext,
02233                           nsIRenderingContext& aRenderingContext,
02234                           const nsRect&        aDirtyRect)
02235 {
02236   // We have been given a rect for our row.  We treat this row like a full-blown
02237   // frame, meaning that it can have borders, margins, padding, and a background.
02238   
02239   // Without a view, we have no data. Check for this up front.
02240   if (!mView)
02241     return;
02242 
02243   // Now obtain the properties for our row.
02244   // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused
02245   PrefillPropertyArray(aRowIndex, nsnull);
02246   mView->GetRowProperties(aRowIndex, mScratchArray);
02247 
02248   // Resolve style for the row.  It contains all the info we need to lay ourselves
02249   // out and to paint.
02250   nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
02251 
02252   // Obtain the margins for the row and then deflate our rect by that 
02253   // amount.  The row is assumed to be contained within the deflated rect.
02254   nsRect rowRect(aRowRect);
02255   nsMargin rowMargin;
02256   rowContext->GetStyleMargin()->GetMargin(rowMargin);
02257   rowRect.Deflate(rowMargin);
02258 
02259   // Paint our borders and background for our row rect.
02260   // If a -moz-appearance is provided, use theme drawing only if the current row
02261   // is not selected (since we draw the selection as part of drawing the background).
02262   PRBool useTheme = PR_FALSE;
02263   nsITheme *theme = nsnull;
02264   const nsStyleDisplay* displayData = rowContext->GetStyleDisplay();
02265   if (displayData->mAppearance) {
02266     theme = aPresContext->GetTheme();
02267     if (theme && theme->ThemeSupportsWidget(aPresContext, nsnull, displayData->mAppearance))
02268       useTheme = PR_TRUE;
02269   }
02270   PRBool isSelected = PR_FALSE;
02271   nsCOMPtr<nsITreeSelection> selection;
02272   mView->GetSelection(getter_AddRefs(selection));
02273   if (selection) 
02274     selection->IsSelected(aRowIndex, &isSelected);
02275   if (useTheme && !isSelected)
02276     theme->DrawWidgetBackground(&aRenderingContext, this, 
02277                                 displayData->mAppearance, rowRect, aDirtyRect);
02278   else
02279     PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, rowRect, aDirtyRect);
02280   
02281   // Adjust the rect for its border and padding.
02282   AdjustForBorderPadding(rowContext, rowRect);
02283 
02284   PRBool isSeparator = PR_FALSE;
02285   mView->IsSeparator(aRowIndex, &isSeparator);
02286   if (isSeparator) {
02287     // The row is a separator.
02288 
02289     nscoord primaryX = rowRect.x;
02290     nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
02291     if (primaryCol) {
02292       // Paint the primary cell.
02293       nsRect cellRect(primaryCol->GetX(), rowRect.y, primaryCol->GetWidth(), rowRect.height);
02294       PRInt32 overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
02295       if (overflow > 0)
02296         cellRect.width -= overflow;
02297       nsRect dirtyRect;
02298       if (dirtyRect.IntersectRect(aDirtyRect, cellRect))
02299         PaintCell(aRowIndex, primaryCol, cellRect, aPresContext, aRenderingContext, aDirtyRect, primaryX);
02300 
02301       // Paint the left side of the separator.
02302       nscoord currX;
02303       nsTreeColumn* previousCol = primaryCol->GetPrevious();
02304       if (previousCol)
02305         currX = previousCol->GetX() + previousCol->GetWidth();
02306       else
02307         currX = rowRect.x;
02308 
02309       PRInt32 level;
02310       mView->GetLevel(aRowIndex, &level);
02311       if (level == 0)
02312         currX += mIndentation;
02313 
02314       if (currX > rowRect.x) {
02315         nsRect separatorRect(rowRect);
02316         separatorRect.width -= rowRect.x + rowRect.width - currX;
02317         PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect);
02318       }
02319     }
02320 
02321     // Paint the right side (whole) separator.
02322     nsRect separatorRect(rowRect);
02323     if (primaryX > rowRect.x) {
02324       separatorRect.width -= primaryX - rowRect.x;
02325       separatorRect.x += primaryX - rowRect.x;
02326     }
02327     PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect);
02328   }
02329   else {
02330     // Now loop over our cells. Only paint a cell if it intersects with our dirty rect.
02331     for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol && currCol->GetX() < mInnerBox.x + mInnerBox.width;
02332          currCol = currCol->GetNext()) {
02333       // Don't paint cells in hidden columns.
02334       if (currCol->GetWidth()) {
02335         nsRect cellRect(currCol->GetX(), rowRect.y, currCol->GetWidth(), rowRect.height);
02336         PRInt32 overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
02337         if (overflow > 0)
02338           cellRect.width -= overflow;
02339         nsRect dirtyRect;
02340         nscoord dummy;
02341         if (dirtyRect.IntersectRect(aDirtyRect, cellRect))
02342           PaintCell(aRowIndex, currCol, cellRect, aPresContext, aRenderingContext, aDirtyRect, dummy); 
02343       }
02344     }
02345   }
02346 }
02347 
02348 void
02349 nsTreeBodyFrame::PaintSeparator(PRInt32              aRowIndex,
02350                                 const nsRect&        aSeparatorRect,
02351                                 nsPresContext*      aPresContext,
02352                                 nsIRenderingContext& aRenderingContext,
02353                                 const nsRect&        aDirtyRect)
02354 {
02355   // Resolve style for the separator.
02356   nsStyleContext* separatorContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeseparator);
02357   PRBool useTheme = PR_FALSE;
02358   nsITheme *theme = nsnull;
02359   const nsStyleDisplay* displayData = separatorContext->GetStyleDisplay();
02360   if ( displayData->mAppearance ) {
02361     theme = aPresContext->GetTheme();
02362     if (theme && theme->ThemeSupportsWidget(aPresContext, nsnull, displayData->mAppearance))
02363       useTheme = PR_TRUE;
02364   }
02365 
02366   // use -moz-appearance if provided.
02367   if (useTheme) {
02368     theme->DrawWidgetBackground(&aRenderingContext, this,
02369                                 displayData->mAppearance, aSeparatorRect, aDirtyRect); 
02370   }
02371   else {
02372     const nsStylePosition* stylePosition = separatorContext->GetStylePosition();
02373 
02374     // Obtain the height for the separator or use the default value.
02375     nscoord height;
02376     if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
02377       height = stylePosition->mHeight.GetCoordValue();
02378     else {
02379       // Use default height 2px.
02380       float p2t = mPresContext->PixelsToTwips();
02381       height = NSIntPixelsToTwips(2, p2t);
02382     }
02383 
02384     // Obtain the margins for the separator and then deflate our rect by that 
02385     // amount. The separator is assumed to be contained within the deflated rect.
02386     nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, aSeparatorRect.width, height);
02387     nsMargin separatorMargin;
02388     separatorContext->GetStyleMargin()->GetMargin(separatorMargin);
02389     separatorRect.Deflate(separatorMargin);
02390 
02391     // Center the separator.
02392     separatorRect.y += (aSeparatorRect.height - height) / 2;
02393 
02394     PaintBackgroundLayer(separatorContext, aPresContext, aRenderingContext, separatorRect, aDirtyRect);
02395   }
02396 }
02397 
02398 void
02399 nsTreeBodyFrame::PaintCell(PRInt32              aRowIndex,
02400                            nsTreeColumn*        aColumn,
02401                            const nsRect&        aCellRect,
02402                            nsPresContext*      aPresContext,
02403                            nsIRenderingContext& aRenderingContext,
02404                            const nsRect&        aDirtyRect,
02405                            nscoord&             aCurrX)
02406 {
02407   // Now obtain the properties for our cell.
02408   // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused, and the col ID.
02409   PrefillPropertyArray(aRowIndex, aColumn);
02410   mView->GetCellProperties(aRowIndex, aColumn, mScratchArray);
02411 
02412   // Resolve style for the cell.  It contains all the info we need to lay ourselves
02413   // out and to paint.
02414   nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
02415 
02416   // Obtain the margins for the cell and then deflate our rect by that 
02417   // amount.  The cell is assumed to be contained within the deflated rect.
02418   nsRect cellRect(aCellRect);
02419   nsMargin cellMargin;
02420   cellContext->GetStyleMargin()->GetMargin(cellMargin);
02421   cellRect.Deflate(cellMargin);
02422 
02423   // Paint our borders and background for our row rect.
02424   PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect, aDirtyRect);
02425 
02426   // Adjust the rect for its border and padding.
02427   AdjustForBorderPadding(cellContext, cellRect);
02428 
02429   nscoord currX = cellRect.x;
02430   nscoord remainingWidth = cellRect.width;
02431 
02432   // Now we paint the contents of the cells.
02433   // Text alignment determines the order in which we paint.  
02434   // LEFT means paint from left to right.
02435   // RIGHT means paint from right to left.
02436   // XXX Implement RIGHT alignment!
02437 
02438   if (aColumn->IsPrimary()) {
02439     // If we're the primary column, we need to indent and paint the twisty and any connecting lines
02440     // between siblings.
02441 
02442     PRInt32 level;
02443     mView->GetLevel(aRowIndex, &level);
02444 
02445     currX += mIndentation * level;
02446     remainingWidth -= mIndentation * level;
02447 
02448     // Resolve the style to use for the connecting lines.
02449     nsStyleContext* lineContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeline);
02450     
02451     if (lineContext->GetStyleVisibility()->IsVisibleOrCollapsed() && level) {
02452       // Paint the thread lines.
02453 
02454       // Get the size of the twisty. We don't want to paint the twisty
02455       // before painting of connecting lines since it would paint lines over
02456       // the twisty. But we need to leave a place for it.
02457       nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
02458 
02459       nsRect twistySize = GetImageSize(aRowIndex, aColumn, PR_TRUE, twistyContext);
02460 
02461       nsMargin twistyMargin;
02462       twistyContext->GetStyleMargin()->GetMargin(twistyMargin);
02463       twistySize.Inflate(twistyMargin);
02464 
02465       aRenderingContext.PushState();
02466 
02467       const nsStyleBorder* borderStyle = lineContext->GetStyleBorder();
02468       nscolor color;
02469       PRBool transparent; PRBool foreground;
02470       borderStyle->GetBorderColor(NS_SIDE_LEFT, color, transparent, foreground);
02471 
02472       aRenderingContext.SetColor(color);
02473       PRUint8 style;
02474       style = borderStyle->GetBorderStyle(NS_SIDE_LEFT);
02475       aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style));
02476 
02477       nscoord lineX = currX;
02478       nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight;
02479 
02480       // Compute the maximal level to paint.
02481       PRInt32 maxLevel = level;
02482       if (maxLevel > cellRect.width / mIndentation)
02483         maxLevel = cellRect.width / mIndentation;
02484 
02485       PRInt32 currentParent = aRowIndex;
02486       for (PRInt32 i = level; i > 0; i--) {
02487         if (i <= maxLevel) {
02488           lineX = currX + twistySize.width + mIndentation / 2;
02489 
02490           nscoord srcX = lineX - (level - i + 1) * mIndentation;
02491           if (srcX <= cellRect.x + cellRect.width) {
02492             // Paint full vertical line only if we have next sibling.
02493             PRBool hasNextSibling;
02494             mView->HasNextSibling(currentParent, aRowIndex, &hasNextSibling);
02495             if (hasNextSibling)
02496               aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight);
02497             else if (i == level)
02498               aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight / 2);
02499           }
02500         }
02501 
02502         PRInt32 parent;
02503         if (NS_FAILED(mView->GetParentIndex(currentParent, &parent)) || parent < 0)
02504           break;
02505         currentParent = parent;
02506       }
02507 
02508       // Don't paint off our cell.
02509       if (level == maxLevel) {
02510         nscoord srcX = lineX - mIndentation + 16;
02511         if (srcX <= cellRect.x + cellRect.width) {
02512           nscoord destX = lineX - mIndentation / 2;
02513           if (destX > cellRect.x + cellRect.width)
02514             destX = cellRect.x + cellRect.width;
02515           aRenderingContext.DrawLine(srcX, lineY + mRowHeight / 2, destX, lineY + mRowHeight / 2);
02516         }
02517       }
02518 
02519       aRenderingContext.PopState();
02520     }
02521 
02522     // Always leave space for the twisty.
02523     nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
02524     PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext, aRenderingContext, aDirtyRect,
02525                 remainingWidth, currX);
02526   }
02527   
02528   // Now paint the icon for our cell.
02529   nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
02530   nsRect dirtyRect;
02531   if (dirtyRect.IntersectRect(aDirtyRect, iconRect))
02532     PaintImage(aRowIndex, aColumn, iconRect, aPresContext, aRenderingContext, aDirtyRect,
02533                remainingWidth, currX);
02534 
02535   // Now paint our element, but only if we aren't a cycler column.
02536   // XXX until we have the ability to load images, allow the view to 
02537   // insert text into cycler columns...
02538   if (!aColumn->IsCycler()) {
02539     nsRect elementRect(currX, cellRect.y, remainingWidth, cellRect.height);
02540     nsRect dirtyRect;
02541     if (dirtyRect.IntersectRect(aDirtyRect, elementRect)) {
02542       switch (aColumn->GetType()) {
02543         case nsITreeColumn::TYPE_TEXT:
02544           PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
02545           break;
02546         case nsITreeColumn::TYPE_CHECKBOX:
02547           PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
02548           break;
02549         case nsITreeColumn::TYPE_PROGRESSMETER:
02550           PRInt32 state;
02551           mView->GetProgressMode(aRowIndex, aColumn, &state);
02552           switch (state) {
02553             case nsITreeView::PROGRESS_NORMAL:
02554             case nsITreeView::PROGRESS_UNDETERMINED:
02555               PaintProgressMeter(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
02556               break;
02557             case nsITreeView::PROGRESS_NONE:
02558             default:
02559               PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
02560               break;
02561           }
02562           break;
02563       }
02564     }
02565   }
02566 
02567   aCurrX = currX;
02568 }
02569 
02570 void
02571 nsTreeBodyFrame::PaintTwisty(PRInt32              aRowIndex,
02572                              nsTreeColumn*        aColumn,
02573                              const nsRect&        aTwistyRect,
02574                              nsPresContext*      aPresContext,
02575                              nsIRenderingContext& aRenderingContext,
02576                              const nsRect&        aDirtyRect,
02577                              nscoord&             aRemainingWidth,
02578                              nscoord&             aCurrX)
02579 {
02580   // Paint the twisty, but only if we are a non-empty container.
02581   PRBool shouldPaint = PR_FALSE;
02582   PRBool isContainer = PR_FALSE;
02583   mView->IsContainer(aRowIndex, &isContainer);
02584   if (isContainer) {
02585     PRBool isContainerEmpty = PR_FALSE;
02586     mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
02587     if (!isContainerEmpty)
02588       shouldPaint = PR_TRUE;
02589   }
02590 
02591   // Resolve style for the twisty.
02592   nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
02593 
02594   PRBool useTheme = PR_FALSE;
02595   nsITheme *theme = nsnull;
02596   const nsStyleDisplay* twistyDisplayData = twistyContext->GetStyleDisplay();
02597   if ( twistyDisplayData->mAppearance ) {
02598     theme = aPresContext->GetTheme();
02599     if (theme && theme->ThemeSupportsWidget(aPresContext, nsnull, twistyDisplayData->mAppearance))
02600       useTheme = PR_TRUE;
02601   }
02602   
02603   // Obtain the margins for the twisty and then deflate our rect by that 
02604   // amount.  The twisty is assumed to be contained within the deflated rect.
02605   nsRect twistyRect(aTwistyRect);
02606   nsMargin twistyMargin;
02607   twistyContext->GetStyleMargin()->GetMargin(twistyMargin);
02608   twistyRect.Deflate(twistyMargin);
02609 
02610   // The twisty rect extends all the way to the end of the cell.  This is incorrect.  We need to
02611   // determine the twisty rect's true width.  This is done by examining the style context for
02612   // a width first.  If it has one, we use that.  If it doesn't, we use the image's natural width.
02613   // If the image hasn't loaded and if no width is specified, then we just bail. If there is
02614   // a -moz-apperance involved, adjust the rect by the minimum widget size provided by
02615   // the theme implementation.
02616   nsRect imageSize = GetImageSize(aRowIndex, aColumn, PR_TRUE, twistyContext);
02617   if (imageSize.height > twistyRect.height)
02618     imageSize.height = twistyRect.height;
02619   if (imageSize.width > twistyRect.width)
02620     imageSize.width = twistyRect.width;
02621   else
02622     twistyRect.width = imageSize.width;
02623   if ( useTheme ) {
02624     nsSize minTwistySize(0,0);
02625     PRBool canOverride = PR_TRUE;
02626     theme->GetMinimumWidgetSize(&aRenderingContext, this, twistyDisplayData->mAppearance, &minTwistySize, &canOverride);
02627     
02628     // GMWS() returns size in pixels, we need to convert it back to twips
02629     float p2t = aPresContext->ScaledPixelsToTwips();
02630     minTwistySize.width = NSIntPixelsToTwips(minTwistySize.width, p2t);
02631     minTwistySize.height = NSIntPixelsToTwips(minTwistySize.height, p2t);
02632 
02633     if ( twistyRect.width < minTwistySize.width || !canOverride )
02634       twistyRect.width = minTwistySize.width;
02635   }
02636   
02637   // Subtract out the remaining width.  This is done even when we don't actually paint a twisty in 
02638   // this cell, so that cells in different rows still line up.
02639   nsRect copyRect(twistyRect);
02640   copyRect.Inflate(twistyMargin);
02641   aRemainingWidth -= copyRect.width;
02642   aCurrX += copyRect.width;
02643 
02644   if (shouldPaint) {
02645     // Paint our borders and background for our image rect.
02646     PaintBackgroundLayer(twistyContext, aPresContext, aRenderingContext, twistyRect, aDirtyRect);
02647 
02648     if ( useTheme ) {
02649       // yeah, i know it says we're drawing a background, but a twisty is really a fg
02650       // object since it doesn't have anything that gecko would want to draw over it. Besides,
02651       // we have to prevent imagelib from drawing it.
02652       theme->DrawWidgetBackground(&aRenderingContext, this, 
02653                                   twistyDisplayData->mAppearance, twistyRect, aDirtyRect);
02654     }
02655     else {
02656       // Time to paint the twisty.
02657       // Adjust the rect for its border and padding.
02658       nsMargin bp(0,0,0,0);
02659       GetBorderPadding(twistyContext, bp);
02660       twistyRect.Deflate(bp);
02661       imageSize.Deflate(bp);
02662 
02663       // Get the image for drawing.
02664       nsCOMPtr<imgIContainer> image;
02665       PRBool useImageRegion = PR_TRUE;
02666       GetImage(aRowIndex, aColumn, PR_TRUE, twistyContext, useImageRegion, getter_AddRefs(image));
02667       if (image) {
02668         nsRect r(twistyRect.x, twistyRect.y, imageSize.width, imageSize.height);
02669 
02670         // Center the image. XXX Obey vertical-align style prop?
02671         if (imageSize.height < twistyRect.height) {
02672           r.y += (twistyRect.height - imageSize.height)/2;
02673         }
02674           
02675         // Paint the image.
02676         aRenderingContext.DrawImage(image, imageSize, r);
02677       }
02678     }        
02679   }
02680 }
02681 
02682 void
02683 nsTreeBodyFrame::PaintImage(PRInt32              aRowIndex,
02684                             nsTreeColumn*        aColumn,
02685                             const nsRect&        aImageRect,
02686                             nsPresContext*      aPresContext,
02687                             nsIRenderingContext& aRenderingContext,
02688                             const nsRect&        aDirtyRect,
02689                             nscoord&             aRemainingWidth,
02690                             nscoord&             aCurrX)
02691 {
02692   // Resolve style for the image.
02693   nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
02694 
02695   // Obtain the margins for the twisty and then deflate our rect by that 
02696   // amount.  The twisty is assumed to be contained within the deflated rect.
02697   nsRect imageRect(aImageRect);
02698   nsMargin imageMargin;
02699   imageContext->GetStyleMargin()->GetMargin(imageMargin);
02700   imageRect.Deflate(imageMargin);
02701 
02702   // If the column isn't a cycler, the image rect extends all the way to the end of the cell.  
02703   // This is incorrect.  We need to determine the image rect's true width.  This is done by 
02704   // examining the style context for a width first.  If it has one, we use that.  If it doesn't, 
02705   // we use the image's natural width.
02706   // If the image hasn't loaded and if no width is specified, then we just bail.
02707   nsRect imageSize = GetImageSize(aRowIndex, aColumn, PR_FALSE, imageContext);
02708 
02709   if (imageSize.height > imageRect.height)
02710     imageSize.height = imageRect.height;
02711   if (imageSize.width > imageRect.width)
02712     imageSize.width = imageRect.width;
02713   else if (!aColumn->IsCycler())
02714     imageRect.width = imageSize.width;
02715 
02716   // Subtract out the remaining width.
02717   nsRect copyRect(imageRect);
02718   copyRect.Inflate(imageMargin);
02719   aRemainingWidth -= copyRect.width;
02720   aCurrX += copyRect.width;
02721 
02722   // Get the image for drawing.
02723   PRBool useImageRegion = PR_TRUE;
02724   nsCOMPtr<imgIContainer> image; 
02725   GetImage(aRowIndex, aColumn, PR_FALSE, imageContext, useImageRegion, getter_AddRefs(image));
02726   if (image) {
02727     // Paint our borders and background for our image rect
02728     PaintBackgroundLayer(imageContext, aPresContext, aRenderingContext, imageRect, aDirtyRect);
02729  
02730     // Time to paint the twisty.
02731     // Adjust the rect for its border and padding.
02732     nsMargin bp(0,0,0,0);
02733     GetBorderPadding(imageContext, bp);
02734     imageRect.Deflate(bp);
02735     imageSize.Deflate(bp);
02736  
02737     nsRect r(imageRect.x, imageRect.y, imageSize.width, imageSize.height);
02738       
02739     // Center the image. XXX Obey vertical-align style prop?
02740 
02741     if (imageSize.height < imageRect.height) {
02742       r.y += (imageRect.height - imageSize.height)/2;
02743     }
02744 
02745     // For cyclers, we also want to center the image in the column.
02746     if (aColumn->IsCycler() && imageSize.width < imageRect.width) {
02747       r.x += (imageRect.width - imageSize.width)/2;
02748     }
02749 
02750     // Paint the image.
02751     aRenderingContext.DrawImage(image, imageSize, r);
02752   }
02753 }
02754 
02755 void
02756 nsTreeBodyFrame::PaintText(PRInt32              aRowIndex,
02757                            nsTreeColumn*        aColumn,
02758                            const nsRect&        aTextRect,
02759                            nsPresContext*      aPresContext,
02760                            nsIRenderingContext& aRenderingContext,
02761                            const nsRect&        aDirtyRect,
02762                            nscoord&             aCurrX)
02763 {
02764   // Now obtain the text for our cell.
02765   nsAutoString text;
02766   mView->GetCellText(aRowIndex, aColumn, text);
02767 
02768   if (text.Length() == 0)
02769     return; // Don't paint an empty string. XXX What about background/borders? Still paint?
02770 
02771   // Resolve style for the text.  It contains all the info we need to lay ourselves
02772   // out and to paint.
02773   nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
02774 
02775   // Obtain the margins for the text and then deflate our rect by that 
02776   // amount.  The text is assumed to be contained within the deflated rect.
02777   nsRect textRect(aTextRect);
02778   nsMargin textMargin;
02779   textContext->GetStyleMargin()->GetMargin(textMargin);
02780   textRect.Deflate(textMargin);
02781 
02782   // Adjust the rect for its border and padding.
02783   nsMargin bp(0,0,0,0);
02784   GetBorderPadding(textContext, bp);
02785   textRect.Deflate(bp);
02786 
02787   // Compute our text size.
02788   nsCOMPtr<nsIFontMetrics> fontMet;
02789   aPresContext->DeviceContext()->
02790     GetMetricsFor(textContext->GetStyleFont()->mFont, *getter_AddRefs(fontMet));
02791 
02792   nscoord height, baseline;
02793   fontMet->GetHeight(height);
02794   fontMet->GetMaxAscent(baseline);
02795 
02796   // Center the text. XXX Obey vertical-align style prop?
02797   if (height < textRect.height) {
02798     textRect.y += (textRect.height - height)/2;
02799     textRect.height = height;
02800   }
02801 
02802   // Set our font.
02803   aRenderingContext.SetFont(fontMet);
02804 
02805   nscoord width;
02806   aRenderingContext.GetWidth(text, width);
02807 
02808   if (width > textRect.width) {
02809     // See if the width is even smaller than the ellipsis
02810     // If so, clear the text completely.
02811     nscoord ellipsisWidth;
02812     aRenderingContext.GetWidth(ELLIPSIS, ellipsisWidth);
02813 
02814     nscoord width = textRect.width;
02815     if (ellipsisWidth > width)
02816       text.SetLength(0);
02817     else if (ellipsisWidth == width)
02818       text.AssignLiteral(ELLIPSIS);
02819     else {
02820       // We will be drawing an ellipsis, thank you very much.
02821       // Subtract out the required width of the ellipsis.
02822       // This is the total remaining width we have to play with.
02823       width -= ellipsisWidth;
02824 
02825       // Now we crop.
02826       switch (aColumn->GetCropStyle()) {
02827         default:
02828         case 0: {
02829           // Crop right.
02830           nscoord cwidth;
02831           nscoord twidth = 0;
02832           int length = text.Length();
02833           int i;
02834           for (i = 0; i < length; ++i) {
02835             PRUnichar ch = text[i];
02836             aRenderingContext.GetWidth(ch,cwidth);
02837             if (twidth + cwidth > width)
02838               break;
02839             twidth += cwidth;
02840           }
02841           text.Truncate(i);
02842           text.AppendLiteral(ELLIPSIS);
02843         }
02844         break;
02845 
02846         case 2: {
02847           // Crop left.
02848           nscoord cwidth;
02849           nscoord twidth = 0;
02850           int length = text.Length();
02851           int i;
02852           for (i=length-1; i >= 0; --i) {
02853             PRUnichar ch = text[i];
02854             aRenderingContext.GetWidth(ch,cwidth);
02855             if (twidth + cwidth > width)
02856               break;
02857             twidth += cwidth;
02858           }
02859 
02860           nsAutoString copy;
02861           text.Right(copy, length-1-i);
02862           text.AssignLiteral(ELLIPSIS);
02863           text += copy;
02864         }
02865         break;
02866 
02867         case 1:
02868         {
02869           // Crop center.
02870           nsAutoString leftStr, rightStr;
02871           nscoord cwidth, twidth = 0;
02872           int length = text.Length();
02873           int rightPos = length - 1;
02874           for (int leftPos = 0; leftPos < rightPos; ++leftPos) {
02875             PRUnichar ch = text[leftPos];
02876             aRenderingContext.GetWidth(ch, cwidth);
02877             twidth += cwidth;
02878             if (twidth > width)
02879               break;
02880             leftStr.Append(ch);
02881 
02882             ch = text[rightPos];
02883             aRenderingContext.GetWidth(ch, cwidth);
02884             twidth += cwidth;
02885             if (twidth > width)
02886               break;
02887             rightStr.Insert(ch, 0);
02888             --rightPos;
02889           }
02890           text = leftStr + NS_LITERAL_STRING(ELLIPSIS) + rightStr;
02891         }
02892         break;
02893       }
02894     }
02895   }
02896   else {
02897     switch (aColumn->GetTextAlignment()) {
02898       case NS_STYLE_TEXT_ALIGN_RIGHT: {
02899         textRect.x += textRect.width - width;
02900       }
02901       break;
02902       case NS_STYLE_TEXT_ALIGN_CENTER: {
02903         textRect.x += (textRect.width - width) / 2;
02904       }
02905       break;
02906     }
02907   }
02908 
02909   aRenderingContext.GetWidth(text, width);
02910   textRect.width = width;
02911 
02912   // Subtract out the remaining width.
02913   nsRect copyRect(textRect);
02914   copyRect.Inflate(textMargin);
02915   aCurrX += copyRect.width;
02916 
02917   textRect.Inflate(bp);
02918   PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, textRect, aDirtyRect);
02919 
02920   // Time to paint our text.
02921   textRect.Deflate(bp);
02922 
02923   // Set our color.
02924   aRenderingContext.SetColor(textContext->GetStyleColor()->mColor);
02925 
02926   // Draw decorations.
02927   PRUint8 decorations = textContext->GetStyleTextReset()->mTextDecoration;
02928 
02929   nscoord offset;
02930   nscoord size;
02931   if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) {
02932     fontMet->GetUnderline(offset, size);
02933     if (decorations & NS_FONT_DECORATION_OVERLINE)
02934       aRenderingContext.FillRect(textRect.x, textRect.y, width, size);
02935     if (decorations & NS_FONT_DECORATION_UNDERLINE)
02936       aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, width, size);
02937   }
02938   if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
02939     fontMet->GetStrikeout(offset, size);
02940     aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, width, size);
02941   }
02942 #ifdef MOZ_TIMELINE
02943   NS_TIMELINE_START_TIMER("Render Outline Text");
02944 #endif
02945 #ifdef IBMBIDI
02946   nsresult rv = NS_ERROR_FAILURE;
02947   nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils();
02948 
02949   if (bidiUtils) {
02950     const nsStyleVisibility* vis = GetStyleVisibility();
02951     nsBidiDirection direction =
02952       (NS_STYLE_DIRECTION_RTL == vis->mDirection) ?
02953       NSBIDI_RTL : NSBIDI_LTR;
02954     rv = bidiUtils->RenderText(text.get(), text.Length(), direction,
02955                                aPresContext, aRenderingContext,
02956                                textRect.x, textRect.y + baseline);
02957   }
02958   if (NS_FAILED(rv))
02959 #endif // IBMBIDI
02960   aRenderingContext.DrawString(text, textRect.x, textRect.y + baseline);
02961 #ifdef MOZ_TIMELINE
02962   NS_TIMELINE_STOP_TIMER("Render Outline Text");
02963   NS_TIMELINE_MARK_TIMER("Render Outline Text");
02964 #endif
02965 }
02966 
02967 void
02968 nsTreeBodyFrame::PaintCheckbox(PRInt32              aRowIndex,
02969                                nsTreeColumn*        aColumn,
02970                                const nsRect&        aCheckboxRect,
02971                                nsPresContext*      aPresContext,
02972                                nsIRenderingContext& aRenderingContext,
02973                                const nsRect&        aDirtyRect)
02974 {
02975   // Resolve style for the checkbox.
02976   nsStyleContext* checkboxContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecheckbox);
02977 
02978   // Obtain the margins for the checkbox and then deflate our rect by that 
02979   // amount.  The checkbox is assumed to be contained within the deflated rect.
02980   nsRect checkboxRect(aCheckboxRect);
02981   nsMargin checkboxMargin;
02982   checkboxContext->GetStyleMargin()->GetMargin(checkboxMargin);
02983   checkboxRect.Deflate(checkboxMargin);
02984   
02985   nsRect imageSize = GetImageSize(aRowIndex, aColumn, PR_TRUE, checkboxContext);
02986 
02987   if (imageSize.height > checkboxRect.height)
02988     imageSize.height = checkboxRect.height;
02989   if (imageSize.width > checkboxRect.width)
02990     imageSize.width = checkboxRect.width;
02991 
02992   // Paint our borders and background for our image rect.
02993   PaintBackgroundLayer(checkboxContext, aPresContext, aRenderingContext, checkboxRect, aDirtyRect);
02994 
02995   // Time to paint the checkbox.
02996   // Adjust the rect for its border and padding.
02997   nsMargin bp(0,0,0,0);
02998   GetBorderPadding(checkboxContext, bp);
02999   checkboxRect.Deflate(bp);
03000 
03001   // Get the image for drawing.
03002   nsCOMPtr<imgIContainer> image;
03003   PRBool useImageRegion = PR_TRUE;
03004   GetImage(aRowIndex, aColumn, PR_TRUE, checkboxContext, useImageRegion, getter_AddRefs(image));
03005   if (image) {
03006     nsRect r(checkboxRect.x, checkboxRect.y, imageSize.width, imageSize.height);
03007           
03008     if (imageSize.height < checkboxRect.height) {
03009       r.y += (checkboxRect.height - imageSize.height)/2;
03010     }
03011 
03012     if (imageSize.width < checkboxRect.width) {
03013       r.x += (checkboxRect.width - imageSize.width)/2;
03014     }
03015 
03016     // Paint the image.
03017     aRenderingContext.DrawImage(image, imageSize, r);
03018   }
03019 }
03020 
03021 void
03022 nsTreeBodyFrame::PaintProgressMeter(PRInt32              aRowIndex,
03023                                     nsTreeColumn*        aColumn,
03024                                     const nsRect&        aProgressMeterRect,
03025                                     nsPresContext*      aPresContext,
03026                                     nsIRenderingContext& aRenderingContext,
03027                                     const nsRect&        aDirtyRect)
03028 {
03029 
03030   // Resolve style for the progress meter.  It contains all the info we need
03031   // to lay ourselves out and to paint.
03032   nsStyleContext* meterContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeprogressmeter);
03033 
03034   // Obtain the margins for the progress meter and then deflate our rect by that 
03035   // amount. The progress meter is assumed to be contained within the deflated
03036   // rect.
03037   nsRect meterRect(aProgressMeterRect);
03038   nsMargin meterMargin;
03039   meterContext->GetStyleMargin()->GetMargin(meterMargin);
03040   meterRect.Deflate(meterMargin);
03041 
03042   // Paint our borders and background for our progress meter rect.
03043   PaintBackgroundLayer(meterContext, aPresContext, aRenderingContext, meterRect, aDirtyRect);
03044 
03045   // Time to paint our progress. 
03046   PRInt32 state;
03047   mView->GetProgressMode(aRowIndex, aColumn, &state);
03048   if (state == nsITreeView::PROGRESS_NORMAL) {
03049     // Adjust the rect for its border and padding.
03050     AdjustForBorderPadding(meterContext, meterRect);
03051 
03052     // Set our color.
03053     aRenderingContext.SetColor(meterContext->GetStyleColor()->mColor);
03054 
03055     // Now obtain the value for our cell.
03056     nsAutoString value;
03057     mView->GetCellValue(aRowIndex, aColumn, value);
03058 
03059     PRInt32 rv;
03060     PRInt32 intValue = value.ToInteger(&rv);
03061     if (intValue < 0)
03062       intValue = 0;
03063     else if (intValue > 100)
03064       intValue = 100;
03065 
03066     meterRect.width = NSToCoordRound((float)intValue / 100 * meterRect.width);
03067     PRBool useImageRegion = PR_TRUE;
03068     nsCOMPtr<imgIContainer> image;
03069     GetImage(aRowIndex, aColumn, PR_TRUE, meterContext, useImageRegion, getter_AddRefs(image));
03070     if (image)
03071       aRenderingContext.DrawTile(image, 0, 0, &meterRect);
03072     else
03073       aRenderingContext.FillRect(meterRect);
03074   }
03075   else if (state == nsITreeView::PROGRESS_UNDETERMINED) {
03076     // Adjust the rect for its border and padding.
03077     AdjustForBorderPadding(meterContext, meterRect);
03078 
03079     PRBool useImageRegion = PR_TRUE;
03080     nsCOMPtr<imgIContainer> image;
03081     GetImage(aRowIndex, aColumn, PR_TRUE, meterContext, useImageRegion, getter_AddRefs(image));
03082     if (image)
03083       aRenderingContext.DrawTile(image, 0, 0, &meterRect);
03084   }
03085 }
03086 
03087 
03088 void
03089 nsTreeBodyFrame::PaintDropFeedback(const nsRect&        aDropFeedbackRect,
03090                                    nsPresContext*      aPresContext,
03091                                    nsIRenderingContext& aRenderingContext,
03092                                    const nsRect&        aDirtyRect)
03093 {
03094   // Paint the drop feedback in between rows.
03095 
03096   nscoord currX;
03097 
03098   // Adjust for the primary cell.
03099   nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
03100   if (primaryCol)
03101     currX = primaryCol->GetX();
03102   else
03103     currX = aDropFeedbackRect.x;
03104 
03105   PrefillPropertyArray(mSlots->mDropRow, primaryCol);
03106 
03107   // Resolve the style to use for the drop feedback.
03108   nsStyleContext* feedbackContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreedropfeedback);
03109 
03110   // Paint only if it is visible.
03111   if (feedbackContext->GetStyleVisibility()->IsVisibleOrCollapsed()) {
03112     PRInt32 level;
03113     mView->GetLevel(mSlots->mDropRow, &level);
03114 
03115     // If our previous or next row has greater level use that for 
03116     // correct visual indentation.
03117     if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) {
03118       if (mSlots->mDropRow > 0) {
03119         PRInt32 previousLevel;
03120         mView->GetLevel(mSlots->mDropRow - 1, &previousLevel);
03121         if (previousLevel > level)
03122           level = previousLevel;
03123       }
03124     }
03125     else {
03126       if (mSlots->mDropRow < mRowCount - 1) {
03127         PRInt32 nextLevel;
03128         mView->GetLevel(mSlots->mDropRow + 1, &nextLevel);
03129         if (nextLevel > level)
03130           level = nextLevel;
03131       }
03132     }
03133 
03134     currX += mIndentation * level;
03135 
03136     if (primaryCol){
03137       nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
03138       nsRect twistySize = GetImageSize(mSlots->mDropRow, primaryCol, PR_TRUE, twistyContext);
03139       nsMargin twistyMargin;
03140       twistyContext->GetStyleMargin()->GetMargin(twistyMargin);
03141       twistySize.Inflate(twistyMargin);
03142       currX += twistySize.width;
03143     }
03144 
03145     const nsStylePosition* stylePosition = feedbackContext->GetStylePosition();
03146 
03147     // Obtain the width for the drop feedback or use default value.
03148     nscoord width;
03149     if (stylePosition->mWidth.GetUnit() == eStyleUnit_Coord)
03150       width = stylePosition->mWidth.GetCoordValue();
03151     else {
03152       // Use default width 50px.
03153       float p2t = mPresContext->PixelsToTwips();
03154       width = NSIntPixelsToTwips(50, p2t);
03155     }
03156 
03157     // Obtain the height for the drop feedback or use default value.
03158     nscoord height;
03159     if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
03160       height = stylePosition->mHeight.GetCoordValue();
03161     else {
03162       // Use default height 2px.
03163       float p2t = mPresContext->PixelsToTwips();
03164       height = NSIntPixelsToTwips(2, p2t);
03165     }
03166 
03167     // Obtain the margins for the drop feedback and then deflate our rect
03168     // by that amount.
03169     nsRect feedbackRect(currX, aDropFeedbackRect.y, width, height);
03170     nsMargin margin;
03171     feedbackContext->GetStyleMargin()->GetMargin(margin);
03172     feedbackRect.Deflate(margin);
03173 
03174     feedbackRect.y += (aDropFeedbackRect.height - height) / 2;
03175 
03176     // Finally paint the drop feedback.
03177     PaintBackgroundLayer(feedbackContext, aPresContext, aRenderingContext, feedbackRect, aDirtyRect);
03178   }
03179 }
03180 
03181 void
03182 nsTreeBodyFrame::PaintBackgroundLayer(nsStyleContext*      aStyleContext,
03183                                       nsPresContext*      aPresContext,
03184                                       nsIRenderingContext& aRenderingContext,
03185                                       const nsRect&        aRect,
03186                                       const nsRect&        aDirtyRect)
03187 {
03188   const nsStyleBackground* myColor = aStyleContext->GetStyleBackground();
03189   const nsStyleBorder* myBorder = aStyleContext->GetStyleBorder();
03190   const nsStylePadding* myPadding = aStyleContext->GetStylePadding();
03191   const nsStyleOutline* myOutline = aStyleContext->GetStyleOutline();
03192   
03193   nsCSSRendering::PaintBackgroundWithSC(aPresContext, aRenderingContext,
03194                                         this, aDirtyRect, aRect,
03195                                         *myColor, *myBorder, *myPadding,
03196                                         PR_TRUE);
03197 
03198   nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
03199                               aDirtyRect, aRect, *myBorder, mStyleContext, 0);
03200 
03201   nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
03202                                aDirtyRect, aRect, *myBorder, *myOutline,
03203                                aStyleContext, 0);
03204 }
03205 
03206 // Scrolling
03207 NS_IMETHODIMP nsTreeBodyFrame::EnsureRowIsVisible(PRInt32 aRow)
03208 {
03209   ScrollParts parts = GetScrollParts();
03210   nsresult rv = EnsureRowIsVisibleInternal(parts, aRow);
03211   NS_ENSURE_SUCCESS(rv, rv);
03212   UpdateScrollbar(parts);
03213   return rv;
03214 }
03215 
03216 nsresult nsTreeBodyFrame::EnsureRowIsVisibleInternal(const ScrollParts& aParts, PRInt32 aRow)
03217 {
03218   if (!mView)
03219     return NS_OK;
03220 
03221   if (mTopRowIndex <= aRow && mTopRowIndex+mPageLength > aRow)
03222     return NS_OK;
03223 
03224   if (aRow < mTopRowIndex)
03225     ScrollToRowInternal(aParts, aRow);
03226   else {
03227     // Bring it just on-screen.
03228     PRInt32 distance = aRow - (mTopRowIndex+mPageLength)+1;
03229     ScrollToRowInternal(aParts, mTopRowIndex+distance);
03230   }
03231 
03232   return NS_OK;
03233 }
03234 
03235 NS_IMETHODIMP nsTreeBodyFrame::ScrollToRow(PRInt32 aRow)
03236 {
03237   ScrollParts parts = GetScrollParts();
03238   nsresult rv = ScrollToRowInternal(parts, aRow);
03239   NS_ENSURE_SUCCESS(rv, rv);
03240   UpdateScrollbar(parts);
03241   return rv;
03242 }
03243 
03244 nsresult nsTreeBodyFrame::ScrollToRowInternal(const ScrollParts& aParts, PRInt32 aRow)
03245 {
03246   ScrollInternal(aParts, aRow);
03247 
03248 #if defined(XP_MAC) || defined(XP_MACOSX)
03249   // mac can't process the event loop during a drag, so if we're dragging,
03250   // grab the scroll widget and make it paint synchronously. This is
03251   // sorta slow (having to paint the entire tree), but it works.
03252   if (mSlots && mSlots->mDragSession && aParts.mVScrollbar) {
03253     nsIFrame* frame;
03254     CallQueryInterface(aParts.mVScrollbar, &frame);
03255     nsIWidget* scrollWidget = frame->GetWindow();
03256     if (scrollWidget)
03257       scrollWidget->Invalidate(PR_TRUE);
03258   }
03259 #endif
03260 
03261   return NS_OK;
03262 }
03263 
03264 NS_IMETHODIMP nsTreeBodyFrame::ScrollByLines(PRInt32 aNumLines)
03265 {
03266   if (!mView)
03267     return NS_OK;
03268 
03269   PRInt32 newIndex = mTopRowIndex + aNumLines;
03270   if (newIndex < 0)
03271     newIndex = 0;
03272   else {
03273     PRInt32 lastPageTopRow = mRowCount - mPageLength;
03274     if (newIndex > lastPageTopRow)
03275       newIndex = lastPageTopRow;
03276   }
03277   ScrollToRow(newIndex);
03278   
03279   return NS_OK;
03280 }
03281 
03282 NS_IMETHODIMP nsTreeBodyFrame::ScrollByPages(PRInt32 aNumPages)
03283 {
03284   if (!mView)
03285     return NS_OK;
03286 
03287   PRInt32 newIndex = mTopRowIndex + aNumPages * mPageLength;
03288   if (newIndex < 0)
03289     newIndex = 0;
03290   else {
03291     PRInt32 lastPageTopRow = mRowCount - mPageLength;
03292     if (newIndex > lastPageTopRow)
03293       newIndex = lastPageTopRow;
03294   }
03295   ScrollToRow(newIndex);
03296     
03297   return NS_OK;
03298 }
03299 
03300 nsresult
03301 nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, PRInt32 aRow)
03302 {
03303   if (!mView)
03304     return NS_OK;
03305 
03306   PRInt32 delta = aRow - mTopRowIndex;
03307 
03308   if (delta > 0) {
03309     if (mTopRowIndex == (mRowCount - mPageLength + 1))
03310       return NS_OK;
03311   }
03312   else {
03313     if (mTopRowIndex == 0)
03314       return NS_OK;
03315   }
03316 
03317   mTopRowIndex += delta;
03318 
03319   // See if we have a transparent background or a background image.  
03320   // If we do, then we cannot blit.
03321   const nsStyleBackground* background = GetStyleBackground();
03322   if (background->mBackgroundImage || background->IsTransparent() || 
03323       PR_ABS(delta)*mRowHeight >= mRect.height) {
03324     Invalidate();
03325   } else {
03326     nsIWidget* widget = nsLeafBoxFrame::GetView()->GetWidget();
03327     if (widget) {
03328       float t2p = mPresContext->TwipsToPixels();
03329       nscoord rowHeightAsPixels = NSToCoordRound((float)mRowHeight*t2p);
03330       widget->Scroll(0, -delta*rowHeightAsPixels, nsnull);
03331     }
03332   }
03333 
03334   return NS_OK;
03335 }
03336 
03337 NS_IMETHODIMP
03338 nsTreeBodyFrame::ScrollbarButtonPressed(nsISupports* aScrollbar, PRInt32 aOldIndex, PRInt32 aNewIndex)
03339 {
03340   if (aNewIndex > aOldIndex)
03341     ScrollToRow(mTopRowIndex+1);
03342   else if (aNewIndex < aOldIndex)
03343     ScrollToRow(mTopRowIndex-1);
03344   return NS_OK;
03345 }
03346   
03347 NS_IMETHODIMP
03348 nsTreeBodyFrame::PositionChanged(nsISupports* aScrollbar, PRInt32 aOldIndex, PRInt32& aNewIndex)
03349 {
03350   ScrollParts parts = GetScrollParts();
03351 
03352   nsIScrollbarFrame* sf = nsnull;
03353   CallQueryInterface(aScrollbar, &sf);
03354   NS_ASSERTION(sf, "scrollbar has no frame");
03355 
03356   // Vertical Scrollbar
03357   if (parts.mVScrollbar == sf) {
03358     float t2p = mPresContext->TwipsToPixels();
03359     nscoord rh = NSToCoordRound((float)mRowHeight*t2p);
03360 
03361     nscoord oldrow = aOldIndex/rh;
03362     nscoord newrow = aNewIndex/rh;
03363 
03364     if (oldrow != newrow)
03365       ScrollInternal(parts, newrow);
03366 
03367     // Go exactly where we're supposed to
03368     // Update the scrollbar.
03369     nsAutoString curPos;
03370     curPos.AppendInt(aNewIndex);
03371     parts.mVScrollbarContent->SetAttr(kNameSpaceID_None,
03372                                       nsXULAtoms::curpos, curPos, PR_TRUE);
03373   }
03374 
03375   return NS_OK;
03376 }
03377 
03378 // The style cache.
03379 nsStyleContext*
03380 nsTreeBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement)
03381 {
03382   return mStyleCache.GetStyleContext(this, mPresContext, mContent,
03383                                      mStyleContext, aPseudoElement,
03384                                      mScratchArray);
03385 }
03386 
03387 // Our comparator for resolving our complex pseudos
03388 NS_IMETHODIMP
03389 nsTreeBodyFrame::PseudoMatches(nsIAtom* aTag, nsCSSSelector* aSelector, PRBool* aResult)
03390 {
03391   if (aSelector->mTag == aTag) {
03392     // Iterate the pseudoclass list.  For each item in the list, see if
03393     // it is contained in our scratch array.  If we have a miss, then
03394     // we aren't a match.  If all items in the pseudoclass list are
03395     // present in the scratch array, then we have a match.
03396     nsAtomStringList* curr = aSelector->mPseudoClassList;
03397     while (curr) {
03398       PRInt32 index;
03399       mScratchArray->GetIndexOf(curr->mAtom, &index);
03400       if (index == -1) {
03401         *aResult = PR_FALSE;
03402         return NS_OK;
03403       }
03404       curr = curr->mNext;
03405     }
03406     *aResult = PR_TRUE;
03407   }
03408   else 
03409     *aResult = PR_FALSE;
03410 
03411   return NS_OK;
03412 }
03413 
03414 nsIContent*
03415 nsTreeBodyFrame::GetBaseElement()
03416 {
03417   nsINodeInfo *ni;
03418   nsIContent* parent = mContent;
03419   while (parent) {
03420     ni = parent->GetNodeInfo();
03421 
03422     if (ni && (ni->Equals(nsXULAtoms::tree, kNameSpaceID_XUL) ||
03423                (ni->Equals(nsHTMLAtoms::select) &&
03424                 parent->IsContentOfType(nsIContent::eHTML)))) {
03425       break;
03426     }
03427 
03428     parent = parent->GetParent();
03429   }
03430 
03431   return parent;
03432 }
03433 
03434 NS_IMETHODIMP
03435 nsTreeBodyFrame::ClearStyleAndImageCaches()
03436 {
03437   mStyleCache.Clear();
03438   mImageCache.EnumerateRead(CancelImageRequest, nsnull);
03439   mImageCache.Clear();
03440   return NS_OK;
03441 }
03442 
03443 PRBool
03444 nsTreeBodyFrame::CanAutoScroll(PRInt32 aRowIndex)
03445 {
03446   // Check first for partially visible last row.
03447   if (aRowIndex == mRowCount - 1) {
03448     nscoord y = mInnerBox.y + (aRowIndex - mTopRowIndex) * mRowHeight;
03449     if (y < mInnerBox.height && y + mRowHeight > mInnerBox.height)
03450       return PR_TRUE;
03451   }
03452 
03453   if (aRowIndex > 0 && aRowIndex < mRowCount - 1)
03454     return PR_TRUE;
03455 
03456   return PR_FALSE;
03457 }
03458 
03459 // Given a dom event, figure out which row in the tree the mouse is over,
03460 // if we should drop before/after/on that row or we should auto-scroll.
03461 // Doesn't query the content about if the drag is allowable, that's done elsewhere.
03462 //
03463 // For containers, we break up the vertical space of the row as follows: if in
03464 // the topmost 25%, the drop is _before_ the row the mouse is over; if in the
03465 // last 25%, _after_; in the middle 50%, we consider it a drop _on_ the container.
03466 //
03467 // For non-containers, if the mouse is in the top 50% of the row, the drop is
03468 // _before_ and the bottom 50% _after_
03469 void 
03470 nsTreeBodyFrame::ComputeDropPosition(nsGUIEvent* aEvent, PRInt32* aRow, PRInt16* aOrient,
03471                                      PRInt16* aScrollLines)
03472 {
03473   *aOrient = -1;
03474   *aScrollLines = 0;
03475 
03476   // Convert the event's point to our coordinates.  The point is currently in
03477   // the coordinates of the view returned by GetOffsetFromView.  We want it in
03478   // the coordinates of our inner box's coordinates.
03479   nsPoint offsetFromView;
03480   nsIView* dummy;
03481   GetOffsetFromView(offsetFromView, &dummy);
03482   PRInt32 xTwips = aEvent->point.x - offsetFromView.x - mInnerBox.x;
03483   PRInt32 yTwips = aEvent->point.y - offsetFromView.y - mInnerBox.y;
03484 
03485   *aRow = GetRowAt(xTwips, yTwips);
03486   if (*aRow >=0) {
03487     // Compute the top/bottom of the row in question.
03488     PRInt32 yOffset = yTwips - mRowHeight * (*aRow - mTopRowIndex);
03489    
03490     PRBool isContainer = PR_FALSE;
03491     mView->IsContainer (*aRow, &isContainer);
03492     if (isContainer) {
03493       // for a container, use a 25%/50%/25% breakdown
03494       if (yOffset < mRowHeight / 4)
03495         *aOrient = nsITreeView::DROP_BEFORE;
03496       else if (yOffset > mRowHeight - (mRowHeight / 4))
03497         *aOrient = nsITreeView::DROP_AFTER;
03498       else
03499         *aOrient = nsITreeView::DROP_ON;
03500     }
03501     else {
03502       // for a non-container use a 50%/50% breakdown
03503       if (yOffset < mRowHeight / 2)
03504         *aOrient = nsITreeView::DROP_BEFORE;
03505       else
03506         *aOrient = nsITreeView::DROP_AFTER;
03507     }
03508   }
03509 
03510   if (CanAutoScroll(*aRow)) {
03511     // Get the max value from the look and feel service.
03512     PRInt32 scrollLinesMax = 0;
03513     mPresContext->LookAndFeel()->
03514       GetMetric(nsILookAndFeel::eMetric_TreeScrollLinesMax, scrollLinesMax);
03515     scrollLinesMax--;
03516     if (scrollLinesMax < 0)
03517       scrollLinesMax = 0;
03518 
03519     // Determine if we're w/in a margin of the top/bottom of the tree during a drag.
03520     // This will ultimately cause us to scroll, but that's done elsewhere.
03521     nscoord height = (3 * mRowHeight) / 4;
03522     if (yTwips < height) {
03523       // scroll up
03524       *aScrollLines = NSToIntRound(-scrollLinesMax * (1 - (float)yTwips / height) - 1);
03525     }
03526     else if (yTwips > mRect.height - height) {
03527       // scroll down
03528       *aScrollLines = NSToIntRound(scrollLinesMax * (1 - (float)(mRect.height - yTwips) / height) + 1);
03529     }
03530   }
03531 } // ComputeDropPosition
03532 
03533 void
03534 nsTreeBodyFrame::OpenCallback(nsITimer *aTimer, void *aClosure)
03535 {
03536   nsTreeBodyFrame* self = NS_STATIC_CAST(nsTreeBodyFrame*, aClosure);
03537   if (self) {
03538     aTimer->Cancel();
03539     self->mSlots->mTimer = nsnull;
03540 
03541     if (self->mSlots->mDropRow >= 0) {
03542       self->mSlots->mValueArray.AppendValue(self->mSlots->mDropRow);
03543       self->mView->ToggleOpenState(self->mSlots->mDropRow);
03544     }
03545   }
03546 }
03547 
03548 void
03549 nsTreeBodyFrame::CloseCallback(nsITimer *aTimer, void *aClosure)
03550 {
03551   nsTreeBodyFrame* self = NS_STATIC_CAST(nsTreeBodyFrame*, aClosure);
03552   if (self) {
03553     aTimer->Cancel();
03554     self->mSlots->mTimer = nsnull;
03555 
03556     for (PRInt32 i = self->mSlots->mValueArray.Count() - 1; i >= 0; i--) {
03557       if (self->mView)
03558         self->mView->ToggleOpenState(self->mSlots->mValueArray[i]);
03559       self->mSlots->mValueArray.RemoveValueAt(i);
03560     }
03561   }
03562 }
03563 
03564 void
03565 nsTreeBodyFrame::LazyScrollCallback(nsITimer *aTimer, void *aClosure)
03566 {
03567   nsTreeBodyFrame* self = NS_STATIC_CAST(nsTreeBodyFrame*, aClosure);
03568   if (self) {
03569     aTimer->Cancel();
03570     self->mSlots->mTimer = nsnull;
03571 
03572     if (self->mView) {
03573       // Set a new timer to scroll the tree repeatedly.
03574       self->CreateTimer(nsILookAndFeel::eMetric_TreeScrollDelay,
03575                         ScrollCallback, nsITimer::TYPE_REPEATING_SLACK,
03576                         getter_AddRefs(self->mSlots->mTimer));
03577       self->ScrollByLines(self->mSlots->mScrollLines);
03578       // ScrollByLines may have deleted |self|.
03579     }
03580   }
03581 }
03582 
03583 void
03584 nsTreeBodyFrame::ScrollCallback(nsITimer *aTimer, void *aClosure)
03585 {
03586   nsTreeBodyFrame* self = NS_STATIC_CAST(nsTreeBodyFrame*, aClosure);
03587   if (self) {
03588     // Don't scroll if we are already at the top or bottom of the view.
03589     if (self->mView && self->CanAutoScroll(self->mSlots->mDropRow)) {
03590       self->ScrollByLines(self->mSlots->mScrollLines);
03591     }
03592     else {
03593       aTimer->Cancel();
03594       self->mSlots->mTimer = nsnull;
03595     }
03596   }
03597 }
03598 
03599 PRBool
03600 nsTreeBodyFrame::FullScrollbarUpdate(PRBool aNeedsFullInvalidation)
03601 {
03602   ScrollParts parts = GetScrollParts();
03603   nsWeakFrame weakFrame(this);
03604   UpdateScrollbar(parts);
03605   NS_ENSURE_TRUE(weakFrame.IsAlive(), PR_FALSE);
03606   if (aNeedsFullInvalidation) {
03607     Invalidate();
03608   }
03609   InvalidateScrollbar(parts);
03610   NS_ENSURE_TRUE(weakFrame.IsAlive(), PR_FALSE);
03611   CheckVerticalOverflow();
03612   return weakFrame.IsAlive();
03613 }