Back to index

lightning-sunbird  0.9+nobinonly
nsTreeContentView.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.org code.
00016  *
00017  * The Initial Developer of the Original Code is Jan Varga.
00018  * Portions created by the Initial Developer are Copyright (C) 2001
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Brian Ryner <bryner@brianryner.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsINameSpaceManager.h"
00039 #include "nsHTMLAtoms.h"
00040 #include "nsXULAtoms.h"
00041 #include "nsIBoxObject.h"
00042 #include "nsTreeUtils.h"
00043 #include "nsTreeContentView.h"
00044 #include "nsChildIterator.h"
00045 #include "nsIDOMHTMLOptionElement.h"
00046 #include "nsIDOMHTMLOptGroupElement.h"
00047 #include "nsIDOMClassInfo.h"
00048 #include "nsIEventStateManager.h"
00049 #include "nsINodeInfo.h"
00050 
00051 // A content model view implementation for the tree.
00052 
00053 #define ROW_FLAG_CONTAINER      0x01
00054 #define ROW_FLAG_OPEN           0x02
00055 #define ROW_FLAG_EMPTY          0x04
00056 #define ROW_FLAG_SEPARATOR      0x08
00057 
00058 class Row
00059 {
00060   public:
00061     static Row*
00062     Create(nsFixedSizeAllocator& aAllocator,
00063            nsIContent* aContent, PRInt32 aParentIndex) {
00064       void* place = aAllocator.Alloc(sizeof(Row));
00065       return place ? ::new(place) Row(aContent, aParentIndex) : nsnull;
00066     }
00067 
00068     static void
00069     Destroy(nsFixedSizeAllocator& aAllocator, Row* aRow) {
00070       aRow->~Row();
00071       aAllocator.Free(aRow, sizeof(*aRow));
00072     }
00073 
00074     Row(nsIContent* aContent, PRInt32 aParentIndex)
00075       : mContent(aContent), mParentIndex(aParentIndex),
00076         mSubtreeSize(0), mFlags(0) {
00077     }
00078 
00079     ~Row() {
00080     }
00081 
00082     void SetContainer(PRBool aContainer) {
00083       aContainer ? mFlags |= ROW_FLAG_CONTAINER : mFlags &= ~ROW_FLAG_CONTAINER;
00084     }
00085     PRBool IsContainer() { return mFlags & ROW_FLAG_CONTAINER; }
00086 
00087     void SetOpen(PRBool aOpen) {
00088       aOpen ? mFlags |= ROW_FLAG_OPEN : mFlags &= ~ROW_FLAG_OPEN;
00089     }
00090     PRBool IsOpen() { return mFlags & ROW_FLAG_OPEN; }
00091 
00092     void SetEmpty(PRBool aEmpty) {
00093       aEmpty ? mFlags |= ROW_FLAG_EMPTY : mFlags &= ~ROW_FLAG_EMPTY;
00094     }
00095     PRBool IsEmpty() { return mFlags & ROW_FLAG_EMPTY; }
00096 
00097     void SetSeparator(PRBool aSeparator) {
00098       aSeparator ? mFlags |= ROW_FLAG_SEPARATOR : mFlags &= ~ROW_FLAG_SEPARATOR;
00099     }
00100     PRBool IsSeparator() { return mFlags & ROW_FLAG_SEPARATOR; }
00101 
00102     // Weak reference to a content item.
00103     nsIContent*         mContent;
00104 
00105     // The parent index of the item, set to -1 for the top level items.
00106     PRInt32             mParentIndex;
00107 
00108     // Subtree size for this item.
00109     PRInt32             mSubtreeSize;
00110 
00111   private:
00112     // Hide so that only Create() and Destroy() can be used to
00113     // allocate and deallocate from the heap
00114     static void* operator new(size_t) CPP_THROW_NEW { return 0; } 
00115     static void operator delete(void*, size_t) {}
00116 
00117     // State flags
00118     PRInt8           mFlags;
00119 };
00120 
00121 
00122 // We don't reference count the reference to the document
00123 // If the document goes away first, we'll be informed and we
00124 // can drop our reference.
00125 // If we go away first, we'll get rid of ourselves from the
00126 // document's observer list.
00127 
00128 nsTreeContentView::nsTreeContentView(void) :
00129   mBoxObject(nsnull),
00130   mSelection(nsnull),
00131   mRoot(nsnull),
00132   mDocument(nsnull),
00133   mUpdateSelection(PR_FALSE)
00134 {
00135   static const size_t kBucketSizes[] = {
00136     sizeof(Row)
00137   };
00138   static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t);
00139   static const PRInt32 kInitialSize = 16;
00140 
00141   mAllocator.Init("nsTreeContentView", kBucketSizes, kNumBuckets, kInitialSize);
00142 }
00143 
00144 nsTreeContentView::~nsTreeContentView(void)
00145 {
00146   // Remove ourselves from mDocument's observers.
00147   if (mDocument)
00148     mDocument->RemoveObserver(this);
00149 }
00150 
00151 nsresult
00152 NS_NewTreeContentView(nsITreeContentView** aResult)
00153 {
00154   *aResult = new nsTreeContentView;
00155   if (! *aResult)
00156     return NS_ERROR_OUT_OF_MEMORY;
00157   NS_ADDREF(*aResult);
00158   return NS_OK;
00159 }
00160 
00161 NS_IMPL_ADDREF(nsTreeContentView)
00162 NS_IMPL_RELEASE(nsTreeContentView)
00163 
00164 NS_INTERFACE_MAP_BEGIN(nsTreeContentView)
00165   NS_INTERFACE_MAP_ENTRY(nsITreeView)
00166   NS_INTERFACE_MAP_ENTRY(nsITreeContentView)
00167   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
00168   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITreeContentView)
00169   NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(TreeContentView)
00170 NS_INTERFACE_MAP_END
00171 
00172 NS_IMETHODIMP
00173 nsTreeContentView::GetRowCount(PRInt32* aRowCount)
00174 {
00175   *aRowCount = mRows.Count();
00176 
00177   return NS_OK;
00178 }
00179 
00180 NS_IMETHODIMP
00181 nsTreeContentView::GetSelection(nsITreeSelection** aSelection)
00182 {
00183   NS_IF_ADDREF(*aSelection = mSelection);
00184 
00185   return NS_OK;
00186 }
00187 
00188 NS_IMETHODIMP
00189 nsTreeContentView::SetSelection(nsITreeSelection* aSelection)
00190 {
00191   mSelection = aSelection;
00192 
00193   if (mUpdateSelection) {
00194     mUpdateSelection = PR_FALSE;
00195 
00196     mSelection->SetSelectEventsSuppressed(PR_TRUE);
00197     for (PRInt32 i = 0; i < mRows.Count(); ++i) {
00198       Row* row = (Row*)mRows[i];
00199       nsCOMPtr<nsIDOMHTMLOptionElement> optEl = do_QueryInterface(row->mContent);
00200       if (optEl) {
00201         PRBool isSelected;
00202         optEl->GetSelected(&isSelected);
00203         if (isSelected)
00204           mSelection->ToggleSelect(i);
00205       }
00206     }
00207     mSelection->SetSelectEventsSuppressed(PR_FALSE);
00208   }
00209 
00210   return NS_OK;
00211 }
00212 
00213 NS_IMETHODIMP
00214 nsTreeContentView::GetRowProperties(PRInt32 aIndex, nsISupportsArray* aProperties)
00215 {
00216   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00217   if (aIndex < 0 || aIndex >= mRows.Count())
00218     return NS_ERROR_INVALID_ARG;   
00219 
00220   Row* row = (Row*)mRows[aIndex];
00221   nsCOMPtr<nsIContent> realRow;
00222   if (row->IsSeparator())
00223     realRow = row->mContent;
00224   else
00225     nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow, getter_AddRefs(realRow));
00226 
00227   if (realRow) {
00228     nsAutoString properties;
00229     realRow->GetAttr(kNameSpaceID_None, nsXULAtoms::properties, properties);
00230     if (!properties.IsEmpty())
00231       nsTreeUtils::TokenizeProperties(properties, aProperties);
00232   }
00233 
00234   return NS_OK;
00235 }
00236 
00237 NS_IMETHODIMP
00238 nsTreeContentView::GetCellProperties(PRInt32 aRow, nsITreeColumn* aCol, nsISupportsArray* aProperties)
00239 {
00240   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00241   if (aRow < 0 || aRow >= mRows.Count())
00242     return NS_ERROR_INVALID_ARG;   
00243 
00244   Row* row = (Row*)mRows[aRow];
00245   nsCOMPtr<nsIContent> realRow;
00246   nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow, getter_AddRefs(realRow));
00247   if (realRow) {
00248     nsIContent* cell = GetCell(realRow, aCol);
00249     if (cell) {
00250       nsAutoString properties;
00251       cell->GetAttr(kNameSpaceID_None, nsXULAtoms::properties, properties);
00252       if (!properties.IsEmpty())
00253         nsTreeUtils::TokenizeProperties(properties, aProperties);
00254     }
00255   }
00256 
00257   return NS_OK;
00258 }
00259 
00260 NS_IMETHODIMP
00261 nsTreeContentView::GetColumnProperties(nsITreeColumn* aCol, nsISupportsArray* aProperties)
00262 {
00263   nsCOMPtr<nsIDOMElement> element;
00264   aCol->GetElement(getter_AddRefs(element));
00265 
00266   nsAutoString properties;
00267   element->GetAttribute(NS_LITERAL_STRING("properties"), properties);
00268 
00269   if (!properties.IsEmpty())
00270     nsTreeUtils::TokenizeProperties(properties, aProperties);
00271 
00272   return NS_OK;
00273 }
00274 
00275 NS_IMETHODIMP
00276 nsTreeContentView::IsContainer(PRInt32 aIndex, PRBool* _retval)
00277 {
00278   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00279   if (aIndex < 0 || aIndex >= mRows.Count())
00280     return NS_ERROR_INVALID_ARG;   
00281 
00282   *_retval = ((Row*)mRows[aIndex])->IsContainer();
00283 
00284   return NS_OK;
00285 }
00286 
00287 NS_IMETHODIMP
00288 nsTreeContentView::IsContainerOpen(PRInt32 aIndex, PRBool* _retval)
00289 {
00290   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00291   if (aIndex < 0 || aIndex >= mRows.Count())
00292     return NS_ERROR_INVALID_ARG;   
00293 
00294   *_retval = ((Row*)mRows[aIndex])->IsOpen();
00295 
00296   return NS_OK;
00297 }
00298 
00299 NS_IMETHODIMP
00300 nsTreeContentView::IsContainerEmpty(PRInt32 aIndex, PRBool* _retval)
00301 {
00302   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00303   if (aIndex < 0 || aIndex >= mRows.Count())
00304     return NS_ERROR_INVALID_ARG;   
00305 
00306   *_retval = ((Row*)mRows[aIndex])->IsEmpty();
00307 
00308   return NS_OK;
00309 }
00310 
00311 NS_IMETHODIMP
00312 nsTreeContentView::IsSeparator(PRInt32 aIndex, PRBool *_retval)
00313 {
00314   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00315   if (aIndex < 0 || aIndex >= mRows.Count())
00316     return NS_ERROR_INVALID_ARG;   
00317 
00318   *_retval = ((Row*)mRows[aIndex])->IsSeparator();
00319 
00320   return NS_OK;
00321 }
00322 
00323 NS_IMETHODIMP
00324 nsTreeContentView::IsSorted(PRBool *_retval)
00325 {
00326   *_retval = PR_FALSE;
00327 
00328   return NS_OK;
00329 }
00330 
00331 NS_IMETHODIMP
00332 nsTreeContentView::CanDrop(PRInt32 aIndex, PRInt32 aOrientation, PRBool *_retval)
00333 {
00334   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00335   if (aIndex < 0 || aIndex >= mRows.Count())
00336     return NS_ERROR_INVALID_ARG;   
00337 
00338   *_retval = PR_FALSE;
00339  
00340   return NS_OK;
00341 }
00342  
00343 NS_IMETHODIMP
00344 nsTreeContentView::Drop(PRInt32 aRow, PRInt32 aOrientation)
00345 {
00346   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00347   if (aRow < 0 || aRow >= mRows.Count())
00348     return NS_ERROR_INVALID_ARG;   
00349 
00350   return NS_OK;
00351 }
00352 
00353 NS_IMETHODIMP
00354 nsTreeContentView::GetParentIndex(PRInt32 aRowIndex, PRInt32* _retval)
00355 {
00356   NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row index");
00357   if (aRowIndex < 0 || aRowIndex >= mRows.Count())
00358     return NS_ERROR_INVALID_ARG;   
00359 
00360   *_retval = ((Row*)mRows[aRowIndex])->mParentIndex;
00361 
00362   return NS_OK;
00363 }
00364 
00365 NS_IMETHODIMP
00366 nsTreeContentView::HasNextSibling(PRInt32 aRowIndex, PRInt32 aAfterIndex, PRBool* _retval)
00367 {
00368   NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row index");
00369   if (aRowIndex < 0 || aRowIndex >= mRows.Count())
00370     return NS_ERROR_INVALID_ARG;   
00371 
00372   // We have a next sibling if the row is not the last in the subtree.
00373   PRInt32 parentIndex = ((Row*)mRows[aRowIndex])->mParentIndex;
00374   if (parentIndex >= 0) {
00375     // Compute the last index in this subtree.
00376     PRInt32 lastIndex = parentIndex + ((Row*)mRows[parentIndex])->mSubtreeSize;
00377     Row* row = (Row*)mRows[lastIndex];
00378     while (row->mParentIndex != parentIndex) {
00379       lastIndex = row->mParentIndex;
00380       row = (Row*)mRows[lastIndex];
00381     }
00382 
00383     *_retval = aRowIndex < lastIndex;
00384   }
00385   else {
00386     *_retval = aRowIndex < mRows.Count() - 1;
00387   }
00388 
00389   return NS_OK;
00390 }
00391 
00392 NS_IMETHODIMP
00393 nsTreeContentView::GetLevel(PRInt32 aIndex, PRInt32* _retval)
00394 {
00395   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00396   if (aIndex < 0 || aIndex >= mRows.Count())
00397     return NS_ERROR_INVALID_ARG;   
00398 
00399   PRInt32 level = 0;
00400   Row* row = (Row*)mRows[aIndex];
00401   while (row->mParentIndex >= 0) {
00402     level++;
00403     row = (Row*)mRows[row->mParentIndex];
00404   }
00405   *_retval = level;
00406 
00407   return NS_OK;
00408 }
00409 
00410  NS_IMETHODIMP
00411 nsTreeContentView::GetImageSrc(PRInt32 aRow, nsITreeColumn* aCol, nsAString& _retval)
00412 {
00413   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00414   if (aRow < 0 || aRow >= mRows.Count())
00415     return NS_ERROR_INVALID_ARG;   
00416 
00417   _retval.SetCapacity(0);
00418 
00419   Row* row = (Row*)mRows[aRow];
00420 
00421   nsCOMPtr<nsIContent> realRow;
00422   nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow, getter_AddRefs(realRow));
00423   if (realRow) {
00424     nsIContent* cell = GetCell(realRow, aCol);
00425     if (cell)
00426       cell->GetAttr(kNameSpaceID_None, nsHTMLAtoms::src, _retval);
00427   }
00428 
00429   return NS_OK;
00430 }
00431 
00432 NS_IMETHODIMP
00433 nsTreeContentView::GetProgressMode(PRInt32 aRow, nsITreeColumn* aCol, PRInt32* _retval)
00434 {
00435   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00436   if (aRow < 0 || aRow >= mRows.Count())
00437     return NS_ERROR_INVALID_ARG;   
00438 
00439   *_retval = nsITreeView::PROGRESS_NONE;
00440 
00441   Row* row = (Row*)mRows[aRow];
00442 
00443   nsCOMPtr<nsIContent> realRow;
00444   nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow, getter_AddRefs(realRow));
00445   if (realRow) {
00446     nsIContent* cell = GetCell(realRow, aCol);
00447     if (cell) {
00448       nsAutoString state;
00449       cell->GetAttr(kNameSpaceID_None, nsXULAtoms::mode, state);
00450       if (state.EqualsLiteral("normal"))
00451         *_retval = nsITreeView::PROGRESS_NORMAL;
00452       else if (state.EqualsLiteral("undetermined"))
00453         *_retval = nsITreeView::PROGRESS_UNDETERMINED;
00454     }
00455   }
00456 
00457   return NS_OK;
00458 }
00459 
00460 NS_IMETHODIMP
00461 nsTreeContentView::GetCellValue(PRInt32 aRow, nsITreeColumn* aCol, nsAString& _retval)
00462 {
00463   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00464   if (aRow < 0 || aRow >= mRows.Count())
00465     return NS_ERROR_INVALID_ARG;   
00466 
00467   _retval.SetCapacity(0);
00468 
00469   Row* row = (Row*)mRows[aRow];
00470 
00471   nsCOMPtr<nsIContent> realRow;
00472   nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow, getter_AddRefs(realRow));
00473   if (realRow) {
00474     nsIContent* cell = GetCell(realRow, aCol);
00475     if (cell)
00476       cell->GetAttr(kNameSpaceID_None, nsHTMLAtoms::value, _retval);
00477   }
00478 
00479   return NS_OK;
00480 }
00481 
00482 NS_IMETHODIMP
00483 nsTreeContentView::GetCellText(PRInt32 aRow, nsITreeColumn* aCol, nsAString& _retval)
00484 {
00485   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00486   NS_PRECONDITION(aCol, "bad column");
00487 
00488   if (aRow < 0 || aRow >= mRows.Count() || !aCol)
00489     return NS_ERROR_INVALID_ARG;
00490 
00491   _retval.SetCapacity(0);
00492 
00493   Row* row = (Row*)mRows[aRow];
00494 
00495   // Check for a "label" attribute - this is valid on an <treeitem>
00496   // or an <option>, with a single implied column.
00497   if (row->mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::label, _retval)
00498       && !_retval.IsEmpty())
00499     return NS_OK;
00500 
00501   nsIAtom *rowTag = row->mContent->Tag();
00502   if (rowTag == nsHTMLAtoms::option &&
00503       row->mContent->IsContentOfType(nsIContent::eHTML)) {
00504     // Use the text node child as the label
00505     nsCOMPtr<nsIDOMHTMLOptionElement> elem = do_QueryInterface(row->mContent);
00506     elem->GetText(_retval);
00507   }
00508   else if (rowTag == nsHTMLAtoms::optgroup &&
00509            row->mContent->IsContentOfType(nsIContent::eHTML)) {
00510     nsCOMPtr<nsIDOMHTMLOptGroupElement> elem = do_QueryInterface(row->mContent);
00511     elem->GetLabel(_retval);
00512   }
00513   else if (rowTag == nsXULAtoms::treeitem &&
00514            row->mContent->IsContentOfType(nsIContent::eXUL)) {
00515     nsCOMPtr<nsIContent> realRow;
00516     nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow,
00517                                    getter_AddRefs(realRow));
00518     if (realRow) {
00519       nsIContent* cell = GetCell(realRow, aCol);
00520       if (cell)
00521         cell->GetAttr(kNameSpaceID_None, nsHTMLAtoms::label, _retval);
00522     }
00523   }
00524 
00525   return NS_OK;
00526 }
00527 
00528 NS_IMETHODIMP
00529 nsTreeContentView::SetTree(nsITreeBoxObject* aTree)
00530 {
00531   mBoxObject = aTree;
00532 
00533   if (aTree && !mRoot) {
00534     // Get our root element
00535     nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mBoxObject);
00536     nsCOMPtr<nsIDOMElement> element;
00537     boxObject->GetElement(getter_AddRefs(element));
00538 
00539     mRoot = do_QueryInterface(element);
00540 
00541     // Add ourselves to document's observers.
00542     nsIDocument* document = mRoot->GetDocument();
00543     if (document) {
00544       document->AddObserver(this);
00545       mDocument = document;
00546     }
00547 
00548     nsCOMPtr<nsIDOMElement> bodyElement;
00549     mBoxObject->GetTreeBody(getter_AddRefs(bodyElement));
00550     if (bodyElement) {
00551       nsCOMPtr<nsIContent> bodyContent = do_QueryInterface(bodyElement);
00552       PRInt32 index = 0;
00553       Serialize(bodyContent, -1, &index, mRows);
00554     }
00555   }
00556 
00557   return NS_OK;
00558 }
00559 
00560 NS_IMETHODIMP
00561 nsTreeContentView::ToggleOpenState(PRInt32 aIndex)
00562 {
00563   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00564   if (aIndex < 0 || aIndex >= mRows.Count())
00565     return NS_ERROR_INVALID_ARG;   
00566 
00567   // We don't serialize content right here, since content might be generated
00568   // lazily.
00569   Row* row = (Row*)mRows[aIndex];
00570 
00571   if (row->mContent->Tag() == nsHTMLAtoms::optgroup &&
00572       row->mContent->IsContentOfType(nsIContent::eHTML)) {
00573     // we don't use an attribute for optgroup's open state
00574     if (row->IsOpen())
00575       CloseContainer(aIndex);
00576     else
00577       OpenContainer(aIndex);
00578   }
00579   else {
00580     if (row->IsOpen())
00581       row->mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::open, NS_LITERAL_STRING("false"), PR_TRUE);
00582     else
00583       row->mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::open, NS_LITERAL_STRING("true"), PR_TRUE);
00584   }
00585 
00586   return NS_OK;
00587 }
00588 
00589 NS_IMETHODIMP
00590 nsTreeContentView::CycleHeader(nsITreeColumn* aCol)
00591 {
00592   return NS_OK;
00593 }
00594 
00595 NS_IMETHODIMP
00596 nsTreeContentView::SelectionChanged()
00597 {
00598   return NS_OK;
00599 }
00600 
00601 NS_IMETHODIMP
00602 nsTreeContentView::CycleCell(PRInt32 aRow, nsITreeColumn* aCol)
00603 {
00604   return NS_OK;
00605 }
00606 
00607 NS_IMETHODIMP
00608 nsTreeContentView::IsEditable(PRInt32 aRow, nsITreeColumn* aCol, PRBool* _retval)
00609 {
00610   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00611   if (aRow < 0 || aRow >= mRows.Count())
00612     return NS_ERROR_INVALID_ARG;   
00613 
00614   *_retval = PR_TRUE;
00615 
00616   Row* row = (Row*)mRows[aRow];
00617 
00618   nsCOMPtr<nsIContent> realRow;
00619   nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow, getter_AddRefs(realRow));
00620   if (realRow) {
00621     nsIContent* cell = GetCell(realRow, aCol);
00622     if (cell) {
00623       nsAutoString editable;
00624       cell->GetAttr(kNameSpaceID_None, nsXULAtoms::editable, editable);
00625       if (editable.EqualsLiteral("false"))
00626         *_retval = PR_FALSE;
00627     }
00628   }
00629 
00630   return NS_OK;
00631 }
00632 
00633 NS_IMETHODIMP
00634 nsTreeContentView::SetCellValue(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
00635 {
00636   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00637   if (aRow < 0 || aRow >= mRows.Count())
00638     return NS_ERROR_INVALID_ARG;   
00639 
00640   Row* row = (Row*)mRows[aRow];
00641 
00642   nsCOMPtr<nsIContent> realRow;
00643   nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow, getter_AddRefs(realRow));
00644   if (realRow) {
00645     nsIContent* cell = GetCell(realRow, aCol);
00646     if (cell)
00647       cell->SetAttr(kNameSpaceID_None, nsHTMLAtoms::value, aValue, PR_TRUE);
00648   }
00649 
00650   return NS_OK;
00651 }
00652 
00653 NS_IMETHODIMP
00654 nsTreeContentView::SetCellText(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
00655 {
00656   NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00657   if (aRow < 0 || aRow >= mRows.Count())
00658     return NS_ERROR_INVALID_ARG;   
00659 
00660   Row* row = (Row*)mRows[aRow];
00661 
00662   nsCOMPtr<nsIContent> realRow;
00663   nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treerow, getter_AddRefs(realRow));
00664   if (realRow) {
00665     nsIContent* cell = GetCell(realRow, aCol);
00666     if (cell)
00667       cell->SetAttr(kNameSpaceID_None, nsHTMLAtoms::label, aValue, PR_TRUE);
00668   }
00669 
00670   return NS_OK;
00671 }
00672 
00673 NS_IMETHODIMP
00674 nsTreeContentView::PerformAction(const PRUnichar* aAction)
00675 {
00676   return NS_OK;
00677 }
00678 
00679 NS_IMETHODIMP
00680 nsTreeContentView::PerformActionOnRow(const PRUnichar* aAction, PRInt32 aRow)
00681 {
00682   return NS_OK;
00683 }
00684 
00685 NS_IMETHODIMP
00686 nsTreeContentView::PerformActionOnCell(const PRUnichar* aAction, PRInt32 aRow, nsITreeColumn* aCol)
00687 {
00688   return NS_OK;
00689 }
00690 
00691 
00692 NS_IMETHODIMP
00693 nsTreeContentView::GetItemAtIndex(PRInt32 aIndex, nsIDOMElement** _retval)
00694 {
00695   NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad index");
00696   if (aIndex < 0 || aIndex >= mRows.Count())
00697     return NS_ERROR_INVALID_ARG;   
00698 
00699   Row* row = (Row*)mRows[aIndex];
00700   row->mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)_retval);
00701 
00702   return NS_OK;
00703 }
00704 
00705 NS_IMETHODIMP
00706 nsTreeContentView::GetIndexOfItem(nsIDOMElement* aItem, PRInt32* _retval)
00707 {
00708   nsCOMPtr<nsIContent> content = do_QueryInterface(aItem);
00709   *_retval = FindContent(content);
00710 
00711   return NS_OK;
00712 }
00713 
00714 void
00715 nsTreeContentView::ContentStatesChanged(nsIDocument* aDocument,
00716                                         nsIContent* aContent1,
00717                                         nsIContent* aContent2,
00718                                         PRInt32 aStateMask)
00719 {
00720   if (!aContent1 || !mSelection ||
00721       !aContent1->IsContentOfType(nsIContent::eHTML) ||
00722       !(aStateMask & NS_EVENT_STATE_CHECKED))
00723     return;
00724 
00725   if (aContent1->Tag() == nsHTMLAtoms::option) {
00726     // update the selected state for this node
00727     PRInt32 index = FindContent(aContent1);
00728     if (index >= 0)
00729       mSelection->ToggleSelect(index);
00730   }
00731 }
00732 
00733 void
00734 nsTreeContentView::AttributeChanged(nsIDocument *aDocument,
00735                                     nsIContent*  aContent,
00736                                     PRInt32      aNameSpaceID,
00737                                     nsIAtom*     aAttribute,
00738                                     PRInt32      aModType)
00739 {
00740   // Make sure this notification concerns us.
00741   // First check the tag to see if it's one that we care about.
00742   nsIAtom *tag = aContent->Tag();
00743 
00744   if (aContent->IsContentOfType(nsIContent::eXUL)) {
00745     if (tag != nsXULAtoms::treecol &&
00746         tag != nsXULAtoms::treeitem &&
00747         tag != nsXULAtoms::treeseparator &&
00748         tag != nsXULAtoms::treerow &&
00749         tag != nsXULAtoms::treecell)
00750       return;
00751   }
00752   else {
00753     return;
00754   }
00755 
00756   // If we have a legal tag, go up to the tree and make sure that it's ours.
00757   nsCOMPtr<nsIContent> parent = aContent;
00758   nsINodeInfo *ni = nsnull;
00759   do {
00760     parent = parent->GetParent();
00761     if (parent)
00762       ni = parent->GetNodeInfo();
00763   } while (parent && !ni->Equals(nsXULAtoms::tree, kNameSpaceID_XUL));
00764 
00765   if (parent != mRoot) {
00766     // This is not for us, we can bail out.
00767     return;
00768   }
00769 
00770   // Handle changes of the hidden attribute.
00771   if (aAttribute == nsHTMLAtoms::hidden &&
00772      (tag == nsXULAtoms::treeitem || tag == nsXULAtoms::treeseparator)) {
00773     nsAutoString hiddenString;
00774     aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::hidden, hiddenString);
00775     PRBool hidden = hiddenString.EqualsLiteral("true");
00776  
00777     PRInt32 index = FindContent(aContent);
00778     if (hidden && index >= 0) {
00779       // Hide this row along with its children.
00780       PRInt32 count = RemoveRow(index);
00781       if (mBoxObject)
00782         mBoxObject->RowCountChanged(index, -count);
00783     }
00784     else if (!hidden && index < 0) {
00785       // Show this row along with its children.
00786       nsCOMPtr<nsIContent> parent = aContent->GetParent();
00787       if (parent) {
00788         InsertRowFor(parent, aContent);
00789       }
00790     }
00791 
00792     return;
00793   }
00794 
00795   if (tag == nsXULAtoms::treecol) {
00796     if (aAttribute == nsXULAtoms::properties) {
00797       if (mBoxObject) {
00798         nsCOMPtr<nsITreeColumns> cols;
00799         mBoxObject->GetColumns(getter_AddRefs(cols));
00800         if (cols) {
00801           nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
00802           nsCOMPtr<nsITreeColumn> col;
00803           cols->GetColumnFor(element, getter_AddRefs(col));
00804           mBoxObject->InvalidateColumn(col);
00805         }
00806       }
00807     }
00808   }
00809   else if (tag == nsXULAtoms::treeitem) {
00810     PRInt32 index = FindContent(aContent);
00811     if (index >= 0) {
00812       Row* row = (Row*)mRows[index];
00813       if (aAttribute == nsXULAtoms::container) {
00814         nsAutoString container;
00815         aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::container, container);
00816         PRBool isContainer = container.EqualsLiteral("true");
00817         row->SetContainer(isContainer);
00818         if (mBoxObject)
00819           mBoxObject->InvalidateRow(index);
00820       }
00821       else if (aAttribute == nsXULAtoms::open) {
00822         nsAutoString open;
00823         aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open);
00824         PRBool isOpen = open.EqualsLiteral("true");
00825         PRBool wasOpen = row->IsOpen();
00826         if (! isOpen && wasOpen)
00827           CloseContainer(index);
00828         else if (isOpen && ! wasOpen)
00829           OpenContainer(index);
00830       }
00831       else if (aAttribute == nsXULAtoms::empty) {
00832         nsAutoString empty;
00833         aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::empty, empty);
00834         PRBool isEmpty = empty.EqualsLiteral("true");
00835         row->SetEmpty(isEmpty);
00836         if (mBoxObject)
00837           mBoxObject->InvalidateRow(index);
00838       }
00839     }
00840   }
00841   else if (tag == nsXULAtoms::treeseparator) {
00842     PRInt32 index = FindContent(aContent);
00843     if (index >= 0) {
00844       if (aAttribute == nsXULAtoms::properties && mBoxObject) {
00845         mBoxObject->InvalidateRow(index);
00846       }
00847     }
00848   }
00849   else if (tag == nsXULAtoms::treerow) {
00850     if (aAttribute == nsXULAtoms::properties) {
00851       nsCOMPtr<nsIContent> parent = aContent->GetParent();
00852       if (parent) {
00853         PRInt32 index = FindContent(parent);
00854         if (index >= 0 && mBoxObject) {
00855           mBoxObject->InvalidateRow(index);
00856         }
00857       }
00858     }
00859   }
00860   else if (tag == nsXULAtoms::treecell) {
00861     if (aAttribute == nsXULAtoms::ref ||
00862         aAttribute == nsXULAtoms::properties ||
00863         aAttribute == nsXULAtoms::mode ||
00864         aAttribute == nsHTMLAtoms::src ||
00865         aAttribute == nsHTMLAtoms::value ||
00866         aAttribute == nsHTMLAtoms::label) {
00867       nsIContent* parent = aContent->GetParent();
00868       if (parent) {
00869         nsCOMPtr<nsIContent> grandParent = parent->GetParent();
00870         if (grandParent) {
00871           PRInt32 index = FindContent(grandParent);
00872           if (index >= 0 && mBoxObject) {
00873             // XXX Should we make an effort to invalidate only cell ?
00874             mBoxObject->InvalidateRow(index);
00875           }
00876         }
00877       }
00878     }
00879   }
00880 }
00881 
00882 void
00883 nsTreeContentView::ContentAppended(nsIDocument *aDocument,
00884                                    nsIContent* aContainer,
00885                                    PRInt32     aNewIndexInContainer)
00886 {
00887   PRUint32 childCount = aContainer->GetChildCount();
00888   while ((PRUint32)aNewIndexInContainer < childCount) {
00889     nsIContent *child = aContainer->GetChildAt(aNewIndexInContainer);
00890     ContentInserted(aDocument, aContainer, child, aNewIndexInContainer);
00891     aNewIndexInContainer++;
00892   }
00893 }
00894 
00895 void
00896 nsTreeContentView::ContentInserted(nsIDocument *aDocument,
00897                                    nsIContent* aContainer,
00898                                    nsIContent* aChild,
00899                                    PRInt32 aIndexInContainer)
00900 {
00901   NS_ASSERTION(aChild, "null ptr");
00902 
00903   // Make sure this notification concerns us.
00904   // First check the tag to see if it's one that we care about.
00905   nsIAtom *childTag = aChild->Tag();
00906 
00907   if (aChild->IsContentOfType(nsIContent::eHTML)) {
00908     if (childTag != nsHTMLAtoms::option &&
00909         childTag != nsHTMLAtoms::optgroup)
00910       return;
00911   }
00912   else if (aChild->IsContentOfType(nsIContent::eXUL)) {
00913     if (childTag != nsXULAtoms::treeitem &&
00914         childTag != nsXULAtoms::treeseparator &&
00915         childTag != nsXULAtoms::treechildren &&
00916         childTag != nsXULAtoms::treerow &&
00917         childTag != nsXULAtoms::treecell)
00918       return;
00919   }
00920   else {
00921     return;
00922   }
00923 
00924   // If we have a legal tag, go up to the tree/select and make sure
00925   // that it's ours.
00926 
00927   for (nsIContent* element = aContainer; element != mRoot; element = element->GetParent()) {
00928     if (!element)
00929       return; // this is not for us
00930     nsIAtom *parentTag = element->Tag();
00931     if ((element->IsContentOfType(nsIContent::eXUL) && parentTag == nsXULAtoms::tree) ||
00932         (element->IsContentOfType(nsIContent::eHTML) && parentTag == nsHTMLAtoms::select))
00933       return; // this is not for us
00934   }
00935 
00936   if (childTag == nsXULAtoms::treechildren) {
00937     PRInt32 index = FindContent(aContainer);
00938     if (index >= 0) {
00939       Row* row = (Row*)mRows[index];
00940       row->SetEmpty(PR_FALSE);
00941       if (mBoxObject)
00942         mBoxObject->InvalidateRow(index);
00943       if (row->IsContainer() && row->IsOpen()) {
00944         PRInt32 count = EnsureSubtree(index);
00945         if (mBoxObject)
00946           mBoxObject->RowCountChanged(index + 1, count);
00947       }
00948     }
00949   }
00950   else if (childTag == nsXULAtoms::treeitem ||
00951            childTag == nsXULAtoms::treeseparator) {
00952     InsertRowFor(aContainer, aChild);
00953   }
00954   else if (childTag == nsXULAtoms::treerow) {
00955     PRInt32 index = FindContent(aContainer);
00956     if (index >= 0 && mBoxObject)
00957       mBoxObject->InvalidateRow(index);
00958   }
00959   else if (childTag == nsXULAtoms::treecell) {
00960     nsCOMPtr<nsIContent> parent = aContainer->GetParent();
00961     if (parent) {
00962       PRInt32 index = FindContent(parent);
00963       if (index >= 0 && mBoxObject)
00964         mBoxObject->InvalidateRow(index);
00965     }
00966   }
00967   else if (childTag == nsHTMLAtoms::optgroup) {
00968     InsertRowFor(aContainer, aChild);
00969   }
00970   else if (childTag == nsHTMLAtoms::option) {
00971     PRInt32 parentIndex = FindContent(aContainer);
00972     PRInt32 count = InsertRow(parentIndex, aIndexInContainer, aChild);
00973     if (mBoxObject)
00974       mBoxObject->RowCountChanged(parentIndex + aIndexInContainer + 1, count);
00975   }
00976 }
00977 
00978 void
00979 nsTreeContentView::ContentRemoved(nsIDocument *aDocument,
00980                                      nsIContent* aContainer,
00981                                      nsIContent* aChild,
00982                                      PRInt32 aIndexInContainer)
00983 {
00984   NS_ASSERTION(aChild, "null ptr");
00985 
00986   // Make sure this notification concerns us.
00987   // First check the tag to see if it's one that we care about.
00988   nsIAtom *tag = aChild->Tag();
00989 
00990   if (aChild->IsContentOfType(nsIContent::eHTML)) {
00991     if (tag != nsHTMLAtoms::option &&
00992         tag != nsHTMLAtoms::optgroup)
00993       return;
00994   }
00995   else if (aChild->IsContentOfType(nsIContent::eXUL)) {
00996     if (tag != nsXULAtoms::treeitem &&
00997         tag != nsXULAtoms::treeseparator &&
00998         tag != nsXULAtoms::treechildren &&
00999         tag != nsXULAtoms::treerow &&
01000         tag != nsXULAtoms::treecell)
01001       return;
01002   }
01003   else {
01004     return;
01005   }
01006 
01007   // If we have a legal tag, go up to the tree/select and make sure
01008   // that it's ours.
01009 
01010   for (nsIContent* element = aContainer; element != mRoot; element = element->GetParent()) {
01011     if (!element)
01012       return; // this is not for us
01013     nsIAtom *parentTag = element->Tag();
01014     if ((element->IsContentOfType(nsIContent::eXUL) && parentTag == nsXULAtoms::tree) || 
01015         (element->IsContentOfType(nsIContent::eHTML) && parentTag == nsHTMLAtoms::select))
01016       return; // this is not for us
01017   }
01018 
01019   if (tag == nsXULAtoms::treechildren) {
01020     PRInt32 index = FindContent(aContainer);
01021     if (index >= 0) {
01022       Row* row = (Row*)mRows[index];
01023       row->SetEmpty(PR_TRUE);
01024       PRInt32 count = RemoveSubtree(index);
01025       // Invalidate also the row to update twisty.
01026       if (mBoxObject) {
01027         mBoxObject->InvalidateRow(index);
01028         mBoxObject->RowCountChanged(index + 1, -count);
01029       }
01030     }
01031     else if (aContainer->Tag() == nsXULAtoms::tree) {
01032       PRInt32 count = mRows.Count();
01033       ClearRows();
01034       if (count && mBoxObject)
01035         mBoxObject->RowCountChanged(0, -count);
01036     }
01037   }
01038   else if (tag == nsXULAtoms::treeitem ||
01039            tag == nsXULAtoms::treeseparator ||
01040            tag == nsHTMLAtoms::option ||
01041            tag == nsHTMLAtoms::optgroup
01042           ) {
01043     PRInt32 index = FindContent(aChild);
01044     if (index >= 0) {
01045       PRInt32 count = RemoveRow(index);
01046       if (mBoxObject)
01047         mBoxObject->RowCountChanged(index, -count);
01048     }
01049   }
01050   else if (tag == nsXULAtoms::treerow) {
01051     PRInt32 index = FindContent(aContainer);
01052     if (index >= 0 && mBoxObject)
01053       mBoxObject->InvalidateRow(index);
01054   }
01055   else if (tag == nsXULAtoms::treecell) {
01056     nsCOMPtr<nsIContent> parent = aContainer->GetParent();
01057     if (parent) {
01058       PRInt32 index = FindContent(parent);
01059       if (index >= 0 && mBoxObject)
01060         mBoxObject->InvalidateRow(index);
01061     }
01062   }
01063 }
01064 
01065 void
01066 nsTreeContentView::DocumentWillBeDestroyed(nsIDocument *aDocument)
01067 {
01068   // Remove ourselves from mDocument's observers.
01069   if (mDocument) {
01070     mDocument->RemoveObserver(this);
01071     mDocument = nsnull;
01072   }
01073 
01074   ClearRows();
01075 }
01076 
01077 
01078 // Recursively serialize content, starting with aContent.
01079 void
01080 nsTreeContentView::Serialize(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows)
01081 {
01082   ChildIterator iter, last;
01083   for (ChildIterator::Init(aContent, &iter, &last); iter != last; ++iter) {
01084     nsCOMPtr<nsIContent> content = *iter;
01085     nsIAtom *tag = content->Tag();
01086     PRInt32 count = aRows.Count();
01087 
01088     if (content->IsContentOfType(nsIContent::eXUL)) {
01089       if (tag == nsXULAtoms::treeitem)
01090         SerializeItem(content, aParentIndex, aIndex, aRows);
01091       else if (tag == nsXULAtoms::treeseparator)
01092         SerializeSeparator(content, aParentIndex, aIndex, aRows);
01093     }
01094     else if (content->IsContentOfType(nsIContent::eHTML)) {
01095       if (tag == nsHTMLAtoms::option)
01096         SerializeOption(content, aParentIndex, aIndex, aRows);
01097       else if (tag == nsHTMLAtoms::optgroup)
01098         SerializeOptGroup(content, aParentIndex, aIndex, aRows);
01099     }
01100     *aIndex += aRows.Count() - count;
01101   }
01102 }
01103 
01104 void
01105 nsTreeContentView::SerializeItem(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows)
01106 {
01107   nsAutoString hidden;
01108   aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::hidden, hidden);
01109   if (hidden.EqualsLiteral("true"))
01110     return;
01111 
01112   Row* row = Row::Create(mAllocator, aContent, aParentIndex);
01113   aRows.AppendElement(row);
01114 
01115   nsAutoString container;
01116   aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::container, container);
01117   if (container.EqualsLiteral("true")) {
01118     row->SetContainer(PR_TRUE);
01119     nsAutoString open;
01120     aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open);
01121     if (open.EqualsLiteral("true")) {
01122       row->SetOpen(PR_TRUE);
01123       nsCOMPtr<nsIContent> child;
01124       nsTreeUtils::GetImmediateChild(aContent, nsXULAtoms::treechildren, getter_AddRefs(child));
01125       if (child) {
01126         // Now, recursively serialize our child.
01127         PRInt32 count = aRows.Count();
01128         PRInt32 index = 0;
01129         Serialize(child, aParentIndex + *aIndex + 1, &index, aRows);
01130         row->mSubtreeSize += aRows.Count() - count;
01131       }
01132       else
01133         row->SetEmpty(PR_TRUE);
01134     } else {
01135       nsAutoString empty;
01136       aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::empty, empty);
01137       if (empty.EqualsLiteral("true"))
01138         row->SetEmpty(PR_TRUE);
01139     }
01140   } 
01141 }
01142 
01143 void
01144 nsTreeContentView::SerializeSeparator(nsIContent* aContent, PRInt32 aParentIndex, PRInt32* aIndex, nsVoidArray& aRows)
01145 {
01146   nsAutoString hidden;
01147   aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::hidden, hidden);
01148   if (hidden.EqualsLiteral("true"))
01149     return;
01150 
01151   Row* row = Row::Create(mAllocator, aContent, aParentIndex);
01152   row->SetSeparator(PR_TRUE);
01153   aRows.AppendElement(row);
01154 }
01155 
01156 void
01157 nsTreeContentView::SerializeOption(nsIContent* aContent, PRInt32 aParentIndex,
01158                                    PRInt32* aIndex, nsVoidArray& aRows)
01159 {
01160   Row* row = Row::Create(mAllocator, aContent, aParentIndex);
01161   aRows.AppendElement(row);
01162 
01163   // This will happen before the TreeSelection is hooked up.  So, cache the selected
01164   // state in the row properties and update the selection when it is attached.
01165 
01166   nsCOMPtr<nsIDOMHTMLOptionElement> optEl = do_QueryInterface(aContent);
01167   PRBool isSelected;
01168   optEl->GetSelected(&isSelected);
01169   if (isSelected)
01170     mUpdateSelection = PR_TRUE;
01171 }
01172 
01173 void
01174 nsTreeContentView::SerializeOptGroup(nsIContent* aContent, PRInt32 aParentIndex,
01175                                      PRInt32* aIndex, nsVoidArray& aRows)
01176 {
01177   Row* row = Row::Create(mAllocator, aContent, aParentIndex);
01178   aRows.AppendElement(row);
01179   row->SetContainer(PR_TRUE);
01180   row->SetOpen(PR_TRUE);
01181 
01182   nsCOMPtr<nsIContent> child;
01183   nsTreeUtils::GetImmediateChild(aContent, nsHTMLAtoms::option, getter_AddRefs(child));
01184   if (child) {
01185     // Now, recursively serialize our child.
01186     PRInt32 count = aRows.Count();
01187     PRInt32 index = 0;
01188     Serialize(aContent, aParentIndex + *aIndex + 1, &index, aRows);
01189     row->mSubtreeSize += aRows.Count() - count;
01190   }
01191   else
01192     row->SetEmpty(PR_TRUE);
01193 }
01194 
01195 void
01196 nsTreeContentView::GetIndexInSubtree(nsIContent* aContainer,
01197                                      nsIContent* aContent, PRInt32* aIndex)
01198 {
01199   PRUint32 childCount = aContainer->GetChildCount();
01200   for (PRUint32 i = 0; i < childCount; i++) {
01201     nsIContent *content = aContainer->GetChildAt(i);
01202 
01203     if (content == aContent)
01204       break;
01205 
01206     nsIAtom *tag = content->Tag();
01207 
01208     if (content->IsContentOfType(nsIContent::eXUL)) {
01209       if (tag == nsXULAtoms::treeitem) {
01210         nsAutoString hidden;
01211         content->GetAttr(kNameSpaceID_None, nsHTMLAtoms::hidden, hidden);
01212         if (! hidden.EqualsLiteral("true")) {
01213           (*aIndex)++;
01214           nsAutoString container;
01215           content->GetAttr(kNameSpaceID_None, nsXULAtoms::container, container);
01216           if (container.EqualsLiteral("true")) {
01217             nsAutoString open;
01218             content->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open);
01219             if (open.EqualsLiteral("true")) {
01220               nsCOMPtr<nsIContent> child;
01221               nsTreeUtils::GetImmediateChild(content, nsXULAtoms::treechildren, getter_AddRefs(child));
01222               if (child)
01223                 GetIndexInSubtree(child, aContent, aIndex);
01224             }
01225           }
01226         }
01227       }
01228       else if (tag == nsXULAtoms::treeseparator) {
01229         nsAutoString hidden;
01230         content->GetAttr(kNameSpaceID_None, nsHTMLAtoms::hidden, hidden);
01231         if (! hidden.EqualsLiteral("true"))
01232           (*aIndex)++;
01233       }
01234     }
01235     else if (content->IsContentOfType(nsIContent::eHTML)) {
01236       if (tag == nsHTMLAtoms::optgroup) {
01237         (*aIndex)++;
01238         GetIndexInSubtree(content, aContent, aIndex);
01239       }
01240       else if (tag == nsHTMLAtoms::option)
01241         (*aIndex)++;
01242     }
01243   }
01244 }
01245 
01246 PRInt32
01247 nsTreeContentView::EnsureSubtree(PRInt32 aIndex)
01248 {
01249   Row* row = (Row*)mRows[aIndex];
01250 
01251   nsCOMPtr<nsIContent> child;
01252   if (row->mContent->Tag() == nsHTMLAtoms::optgroup)
01253     child = row->mContent;
01254   else {
01255     nsTreeUtils::GetImmediateChild(row->mContent, nsXULAtoms::treechildren, getter_AddRefs(child));
01256     if (! child) {
01257       return 0;
01258     }
01259   }
01260 
01261   nsAutoVoidArray rows;
01262   PRInt32 index = 0;
01263   Serialize(child, aIndex, &index, rows);
01264   mRows.InsertElementsAt(rows, aIndex + 1);
01265   PRInt32 count = rows.Count();
01266 
01267   row->mSubtreeSize += count;
01268   UpdateSubtreeSizes(row->mParentIndex, count);
01269 
01270   // Update parent indexes, but skip newly added rows.
01271   // They already have correct values.
01272   UpdateParentIndexes(aIndex, count + 1, count);
01273 
01274   return count;
01275 }
01276 
01277 PRInt32
01278 nsTreeContentView::RemoveSubtree(PRInt32 aIndex)
01279 {
01280   Row* row = (Row*)mRows[aIndex];
01281   PRInt32 count = row->mSubtreeSize;
01282 
01283   for(PRInt32 i = 0; i < count; i++) {
01284     Row* nextRow = (Row*)mRows[aIndex + i + 1];
01285     Row::Destroy(mAllocator, nextRow);
01286   }
01287   mRows.RemoveElementsAt(aIndex + 1, count);
01288 
01289   row->mSubtreeSize -= count;
01290   UpdateSubtreeSizes(row->mParentIndex, -count);
01291 
01292   UpdateParentIndexes(aIndex, 0, -count);
01293 
01294   return count;
01295 }
01296 
01297 void
01298 nsTreeContentView::InsertRowFor(nsIContent* aParent, nsIContent* aChild)
01299 {
01300   PRInt32 grandParentIndex = -1;
01301   PRBool insertRow = PR_FALSE;
01302 
01303   nsCOMPtr<nsIContent> grandParent = aParent->GetParent();
01304   nsIAtom* grandParentTag = grandParent->Tag();
01305 
01306   if ((grandParent->IsContentOfType(nsIContent::eXUL) && grandParentTag == nsXULAtoms::tree) ||
01307       (grandParent->IsContentOfType(nsIContent::eHTML) && grandParentTag == nsHTMLAtoms::select)
01308      ) {
01309     // Allow insertion to the outermost container.
01310     insertRow = PR_TRUE;
01311   }
01312   else {
01313     // Test insertion to an inner container.
01314 
01315     // First try to find this parent in our array of rows, if we find one
01316     // we can be sure that all other parents are open too.
01317     grandParentIndex = FindContent(grandParent);
01318     if (grandParentIndex >= 0) {
01319       // Got it, now test if it is open.
01320       if (((Row*)mRows[grandParentIndex])->IsOpen())
01321         insertRow = PR_TRUE;
01322     }
01323   }
01324 
01325   if (insertRow) {
01326     PRInt32 index = 0;
01327     GetIndexInSubtree(aParent, aChild, &index);
01328 
01329     PRInt32 count = InsertRow(grandParentIndex, index, aChild);
01330     if (mBoxObject)
01331       mBoxObject->RowCountChanged(grandParentIndex + index + 1, count);
01332   }
01333 }
01334 
01335 PRInt32
01336 nsTreeContentView::InsertRow(PRInt32 aParentIndex, PRInt32 aIndex, nsIContent* aContent)
01337 {
01338   nsAutoVoidArray rows;
01339   nsIAtom *tag = aContent->Tag();
01340   if (aContent->IsContentOfType(nsIContent::eXUL)) {
01341     if (tag == nsXULAtoms::treeitem)
01342       SerializeItem(aContent, aParentIndex, &aIndex, rows);
01343     else if (tag == nsXULAtoms::treeseparator)
01344       SerializeSeparator(aContent, aParentIndex, &aIndex, rows);
01345   }
01346   else if (aContent->IsContentOfType(nsIContent::eHTML)) {
01347     if (tag == nsHTMLAtoms::option)
01348       SerializeOption(aContent, aParentIndex, &aIndex, rows);
01349     else if (tag == nsHTMLAtoms::optgroup)
01350       SerializeOptGroup(aContent, aParentIndex, &aIndex, rows);
01351   }
01352 
01353   mRows.InsertElementsAt(rows, aParentIndex + aIndex + 1);
01354   PRInt32 count = rows.Count();
01355 
01356   UpdateSubtreeSizes(aParentIndex, count);
01357 
01358   // Update parent indexes, but skip added rows.
01359   // They already have correct values.
01360   UpdateParentIndexes(aParentIndex + aIndex, count + 1, count);
01361 
01362   return count;
01363 }
01364 
01365 PRInt32
01366 nsTreeContentView::RemoveRow(PRInt32 aIndex)
01367 {
01368   Row* row = (Row*)mRows[aIndex];
01369   PRInt32 count = row->mSubtreeSize + 1;
01370   PRInt32 parentIndex = row->mParentIndex;
01371 
01372   Row::Destroy(mAllocator, row);
01373   for(PRInt32 i = 1; i < count; i++) {
01374     Row* nextRow = (Row*)mRows[aIndex + i];
01375     Row::Destroy(mAllocator, nextRow);
01376   }
01377   mRows.RemoveElementsAt(aIndex, count);
01378 
01379   UpdateSubtreeSizes(parentIndex, -count);
01380   
01381   UpdateParentIndexes(aIndex, 0, -count);
01382 
01383   return count;
01384 }
01385 
01386 void
01387 nsTreeContentView::ClearRows()
01388 {
01389   for (PRInt32 i = 0; i < mRows.Count(); i++)
01390     Row::Destroy(mAllocator, (Row*)mRows[i]);
01391   mRows.Clear();
01392   mRoot = nsnull;
01393 } 
01394 
01395 void
01396 nsTreeContentView::OpenContainer(PRInt32 aIndex)
01397 {
01398   Row* row = (Row*)mRows[aIndex];
01399   row->SetOpen(PR_TRUE);
01400 
01401   PRInt32 count = EnsureSubtree(aIndex);
01402   if (mBoxObject) {
01403     mBoxObject->InvalidateRow(aIndex);
01404     mBoxObject->RowCountChanged(aIndex + 1, count);
01405   }
01406 }
01407 
01408 void
01409 nsTreeContentView::CloseContainer(PRInt32 aIndex)
01410 {
01411   Row* row = (Row*)mRows[aIndex];
01412   row->SetOpen(PR_FALSE);
01413 
01414   PRInt32 count = RemoveSubtree(aIndex);
01415   if (mBoxObject) {
01416     mBoxObject->InvalidateRow(aIndex);
01417     mBoxObject->RowCountChanged(aIndex + 1, -count);
01418   }
01419 }
01420 
01421 PRInt32
01422 nsTreeContentView::FindContent(nsIContent* aContent)
01423 {
01424   for (PRInt32 i = 0; i < mRows.Count(); i++) {
01425     if (((Row*)mRows[i])->mContent == aContent) {
01426       return i;
01427     }
01428   }
01429 
01430   return -1;
01431 }
01432 
01433 void
01434 nsTreeContentView::UpdateSubtreeSizes(PRInt32 aParentIndex, PRInt32 count)
01435 {
01436   while (aParentIndex >= 0) {
01437     Row* row = (Row*)mRows[aParentIndex];
01438     row->mSubtreeSize += count;
01439     aParentIndex = row->mParentIndex;
01440   }
01441 }
01442 
01443 void
01444 nsTreeContentView::UpdateParentIndexes(PRInt32 aIndex, PRInt32 aSkip, PRInt32 aCount)
01445 {
01446   PRInt32 count = mRows.Count();
01447   for (PRInt32 i = aIndex + aSkip; i < count; i++) {
01448     Row* row = (Row*)mRows[i];
01449     if (row->mParentIndex > aIndex) {
01450       row->mParentIndex += aCount;
01451     }
01452   }
01453 }
01454 
01455 nsIContent*
01456 nsTreeContentView::GetCell(nsIContent* aContainer, nsITreeColumn* aCol)
01457 {
01458   const PRUnichar* colID;
01459   PRInt32 colIndex;
01460   aCol->GetIdConst(&colID);
01461   aCol->GetIndex(&colIndex);
01462 
01463   // Traverse through cells, try to find the cell by "ref" attribute or by cell
01464   // index in a row. "ref" attribute has higher priority.
01465   nsIContent* result = nsnull;
01466   PRInt32 j = 0;
01467   ChildIterator iter, last;
01468   for (ChildIterator::Init(aContainer, &iter, &last); iter != last; ++iter) {
01469     nsCOMPtr<nsIContent> cell = *iter;
01470 
01471     if (cell->Tag() == nsXULAtoms::treecell) {
01472       nsAutoString ref;
01473       cell->GetAttr(kNameSpaceID_None, nsXULAtoms::ref, ref);
01474       if (!ref.IsEmpty() && ref.Equals(colID)) {
01475         result = cell;
01476         break;
01477       }
01478       else if (j == colIndex) {
01479         result = cell;
01480       }
01481       j++;
01482     }
01483   }
01484 
01485   return result;
01486 }