Back to index

lightning-sunbird  0.9+nobinonly
nsXULTreeBuilder.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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  *   Chris Waterson <waterson@netscape.com>
00024  *   Ben Goodger <ben@netscape.com>
00025  *   Jan Varga <varga@ku.sk>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nscore.h"
00042 #include "nsIContent.h"
00043 #include "nsINodeInfo.h"
00044 #include "nsIDOMElement.h"
00045 #include "nsILocalStore.h"
00046 #include "nsIBoxObject.h"
00047 #include "nsITreeBoxObject.h"
00048 #include "nsITreeSelection.h"
00049 #include "nsITreeColumns.h"
00050 #include "nsITreeView.h"
00051 #include "nsTreeUtils.h"
00052 #include "nsIServiceManager.h"
00053 #include "nsReadableUtils.h"
00054 
00055 // For sorting
00056 #include "nsICollation.h"
00057 #include "nsILocale.h"
00058 #include "nsILocaleService.h"
00059 #include "nsCollationCID.h"
00060 #include "nsQuickSort.h"
00061 
00062 #include "nsClusterKeySet.h"
00063 #include "nsTreeRows.h"
00064 #include "nsTreeRowTestNode.h"
00065 #include "nsRDFConMemberTestNode.h"
00066 #include "nsTemplateRule.h"
00067 #include "nsXULAtoms.h"
00068 #include "nsHTMLAtoms.h"
00069 #include "nsXULContentUtils.h"
00070 #include "nsXULTemplateBuilder.h"
00071 #include "nsVoidArray.h"
00072 #include "nsUnicharUtils.h"
00073 #include "nsINameSpaceManager.h"
00074 #include "nsIDOMClassInfo.h"
00075 
00076 // For security check
00077 #include "nsIDocument.h"
00078 
00083 class nsXULTreeBuilder : public nsXULTemplateBuilder,
00084                              public nsIXULTreeBuilder,
00085                              public nsINativeTreeView
00086 {
00087 public:
00088     // nsISupports
00089     NS_DECL_ISUPPORTS_INHERITED
00090 
00091     // nsIXULTreeBuilder
00092     NS_DECL_NSIXULTREEBUILDER
00093 
00094     // nsITreeView
00095     NS_DECL_NSITREEVIEW
00096     // nsINativeTreeView: Untrusted code can use us
00097     NS_IMETHOD EnsureNative() { return NS_OK; }
00098 
00099     virtual void DocumentWillBeDestroyed(nsIDocument *aDocument);
00100 
00101 protected:
00102     friend NS_IMETHODIMP
00103     NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
00104 
00105     nsXULTreeBuilder();
00106     virtual ~nsXULTreeBuilder();
00107 
00111     nsresult
00112     Init();
00113 
00117     nsresult
00118     EnsureSortVariables();
00119 
00120     virtual nsresult
00121     InitializeRuleNetworkForSimpleRules(InnerNode** aChildNode);
00122 
00123     virtual nsresult
00124     RebuildAll();
00125 
00130     virtual nsresult
00131     CompileCondition(nsIAtom* aTag,
00132                      nsTemplateRule* aRule,
00133                      nsIContent* aCondition,
00134                      InnerNode* aParentNode,
00135                      TestNode** aResult);
00136 
00140     nsresult
00141     CompileTreeRowCondition(nsTemplateRule* aRule,
00142                                 nsIContent* aCondition,
00143                                 InnerNode* aParentNode,
00144                                 TestNode** aResult);
00145 
00150     nsresult
00151     GetTemplateActionRowFor(PRInt32 aRow, nsIContent** aResult);
00152 
00157     nsresult
00158     GetTemplateActionCellFor(PRInt32 aRow, nsITreeColumn* aCol, nsIContent** aResult);
00159 
00164     nsIRDFResource*
00165     GetResourceFor(PRInt32 aRow);
00166 
00171     nsresult
00172     OpenContainer(PRInt32 aIndex, nsIRDFResource* aContainer);
00173 
00178     nsresult
00179     OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
00180                   PRInt32 aIndex,
00181                   nsIRDFResource* aContainer,
00182                   PRInt32* aDelta);
00183 
00188     nsresult
00189     CloseContainer(PRInt32 aIndex, nsIRDFResource* aContainer);
00190 
00195     nsresult
00196     RemoveMatchesFor(nsIRDFResource* aContainer, nsIRDFResource* aMember);
00197 
00201     nsresult
00202     IsContainerOpen(nsIRDFResource* aContainer, PRBool* aResult);
00203 
00207     static int PR_CALLBACK
00208     Compare(const void* aLeft, const void* aRight, void* aClosure);
00209 
00213     PRInt32
00214     CompareMatches(nsTemplateMatch* aLeft, nsTemplateMatch* aRight);
00215 
00220     nsresult
00221     SortSubtree(nsTreeRows::Subtree* aSubtree);
00222 
00226     virtual nsresult
00227     ReplaceMatch(nsIRDFResource* aMember, const nsTemplateMatch* aOldMatch, nsTemplateMatch* aNewMatch);
00228 
00232     virtual nsresult
00233     SynchronizeMatch(nsTemplateMatch* aMatch, const VariableSet& aModifiedVars);
00234 
00238     nsCOMPtr<nsITreeBoxObject> mBoxObject;
00239 
00243     nsCOMPtr<nsITreeSelection> mSelection;
00244 
00248     nsCOMPtr<nsIRDFDataSource> mPersistStateStore;
00249 
00253     nsTreeRows mRows;
00254 
00258     PRInt32 mSortVariable;
00259 
00260     enum Direction {
00261         eDirection_Descending = -1,
00262         eDirection_Natural    =  0,
00263         eDirection_Ascending  = +1
00264     };
00265 
00269     Direction mSortDirection;
00270 
00274     nsCOMPtr<nsICollation> mCollation;
00275 
00279     nsCOMPtr<nsISupportsArray> mObservers;
00280     
00281     // pseudo-constants
00282     static PRInt32 gRefCnt;
00283     static nsIRDFResource* kRDF_type;
00284     static nsIRDFResource* kNC_BookmarkSeparator;
00285 };
00286 PRInt32         nsXULTreeBuilder::gRefCnt = 0;
00287 nsIRDFResource* nsXULTreeBuilder::kRDF_type;
00288 nsIRDFResource* nsXULTreeBuilder::kNC_BookmarkSeparator;
00289 
00290 //----------------------------------------------------------------------
00291 
00292 NS_IMETHODIMP
00293 NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
00294 {
00295     NS_PRECONDITION(aOuter == nsnull, "no aggregation");
00296     if (aOuter)
00297         return NS_ERROR_NO_AGGREGATION;
00298 
00299     nsresult rv;
00300     nsXULTreeBuilder* result = new nsXULTreeBuilder();
00301     if (! result)
00302         return NS_ERROR_OUT_OF_MEMORY;
00303 
00304     NS_ADDREF(result); // stabilize
00305 
00306     rv = result->Init();
00307 
00308     if (NS_SUCCEEDED(rv))
00309         rv = result->QueryInterface(aIID, aResult);
00310 
00311     NS_RELEASE(result);
00312     return rv;
00313 }
00314 
00315 NS_IMPL_ADDREF(nsXULTreeBuilder)
00316 NS_IMPL_RELEASE(nsXULTreeBuilder)
00317 
00318 NS_INTERFACE_MAP_BEGIN(nsXULTreeBuilder)
00319   NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder)
00320   NS_INTERFACE_MAP_ENTRY(nsITreeView)
00321   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTreeBuilder)
00322   NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(XULTreeBuilder)
00323 NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder)
00324 
00325 
00326 nsXULTreeBuilder::nsXULTreeBuilder()
00327     : mSortVariable(0),
00328       mSortDirection(eDirection_Natural)
00329 {
00330 }
00331 
00332 nsresult
00333 nsXULTreeBuilder::Init()
00334 {
00335     nsresult rv = nsXULTemplateBuilder::Init();
00336     if (NS_FAILED(rv)) return rv;
00337 
00338     if (gRefCnt++ == 0) {
00339         gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"), &kRDF_type);
00340         gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"),
00341                                  &kNC_BookmarkSeparator);
00342     }
00343 
00344     // Try to acquire a collation object for sorting
00345     nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID);
00346     if (ls) {
00347         nsCOMPtr<nsILocale> locale;
00348         ls->GetApplicationLocale(getter_AddRefs(locale));
00349 
00350         if (locale) {
00351             static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID);
00352             nsCOMPtr<nsICollationFactory> cfact =
00353                 do_CreateInstance(kCollationFactoryCID);
00354 
00355             if (cfact)
00356                 cfact->CreateCollation(locale, getter_AddRefs(mCollation));
00357         }
00358     }
00359     return rv;
00360 }
00361 
00362 nsXULTreeBuilder::~nsXULTreeBuilder()
00363 {
00364     if (--gRefCnt == 0) {
00365         NS_IF_RELEASE(kRDF_type);
00366         NS_IF_RELEASE(kNC_BookmarkSeparator);
00367     }
00368 }
00369 
00370 //----------------------------------------------------------------------
00371 //
00372 // nsIXULTreeBuilder methods
00373 //
00374 
00375 NS_IMETHODIMP
00376 nsXULTreeBuilder::GetResourceAtIndex(PRInt32 aRowIndex, nsIRDFResource** aResult)
00377 {
00378     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
00379         return NS_ERROR_INVALID_ARG;
00380 
00381     NS_IF_ADDREF(*aResult = GetResourceFor(aRowIndex));
00382     return NS_OK;
00383 }
00384 
00385 NS_IMETHODIMP
00386 nsXULTreeBuilder::GetIndexOfResource(nsIRDFResource* aResource, PRInt32* aResult)
00387 {
00388     nsTreeRows::iterator iter = mRows.Find(mConflictSet, aResource);
00389     if (iter == mRows.Last())
00390         *aResult = -1;
00391     else
00392         *aResult = iter.GetRowIndex();
00393     return NS_OK;
00394 }
00395 
00396 NS_IMETHODIMP
00397 nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver* aObserver)
00398 {
00399     nsresult rv;  
00400     if (!mObservers) {
00401         rv = NS_NewISupportsArray(getter_AddRefs(mObservers));
00402         if (NS_FAILED(rv)) return rv;
00403     }
00404 
00405     return mObservers->AppendElement(aObserver);
00406 }
00407 
00408 NS_IMETHODIMP
00409 nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver* aObserver)
00410 {
00411     return mObservers ? mObservers->RemoveElement(aObserver) : NS_ERROR_FAILURE;
00412 }
00413 
00414 NS_IMETHODIMP
00415 nsXULTreeBuilder::Sort(nsIDOMElement* aElement)
00416 {
00417     nsCOMPtr<nsIContent> header = do_QueryInterface(aElement);
00418     if (! header)
00419         return NS_ERROR_FAILURE;
00420 
00421     nsAutoString sortLocked;
00422     header->GetAttr(kNameSpaceID_None, nsXULAtoms::sortLocked, sortLocked);
00423     if (sortLocked.EqualsLiteral("true"))
00424         return NS_OK;
00425 
00426     nsAutoString sort;
00427     header->GetAttr(kNameSpaceID_None, nsXULAtoms::sort, sort);
00428 
00429     if (sort.IsEmpty())
00430         return NS_OK;
00431 
00432     // Grab the new sort variable
00433     mSortVariable = mRules.LookupSymbol(sort.get());
00434 
00435     // Cycle the sort direction
00436     nsAutoString dir;
00437     header->GetAttr(kNameSpaceID_None, nsXULAtoms::sortDirection, dir);
00438 
00439     if (dir.EqualsLiteral("ascending")) {
00440         dir.AssignLiteral("descending");
00441         mSortDirection = eDirection_Descending;
00442     }
00443     else if (dir.EqualsLiteral("descending")) {
00444         dir.AssignLiteral("natural");
00445         mSortDirection = eDirection_Natural;
00446     }
00447     else {
00448         dir.AssignLiteral("ascending");
00449         mSortDirection = eDirection_Ascending;
00450     }
00451 
00452     // Sort it.
00453     SortSubtree(mRows.GetRoot());
00454     mRows.InvalidateCachedRow();
00455     if (mBoxObject) 
00456         mBoxObject->Invalidate();
00457 
00458     nsTreeUtils::UpdateSortIndicators(header, dir);
00459 
00460     return NS_OK;
00461 }
00462 
00463 //----------------------------------------------------------------------
00464 //
00465 // nsITreeView methods
00466 //
00467 
00468 NS_IMETHODIMP
00469 nsXULTreeBuilder::GetRowCount(PRInt32* aRowCount)
00470 {
00471     *aRowCount = mRows.Count();
00472     return NS_OK;
00473 }
00474 
00475 NS_IMETHODIMP
00476 nsXULTreeBuilder::GetSelection(nsITreeSelection** aSelection)
00477 {
00478     NS_IF_ADDREF(*aSelection = mSelection.get());
00479     return NS_OK;
00480 }
00481 
00482 NS_IMETHODIMP
00483 nsXULTreeBuilder::SetSelection(nsITreeSelection* aSelection)
00484 {
00485     mSelection = aSelection;
00486     return NS_OK;
00487 }
00488 
00489 NS_IMETHODIMP
00490 nsXULTreeBuilder::GetRowProperties(PRInt32 aIndex, nsISupportsArray* aProperties)
00491 {
00492     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
00493     if (aIndex < 0 || aIndex >= mRows.Count())
00494         return NS_ERROR_INVALID_ARG;
00495 
00496     nsCOMPtr<nsIContent> row;
00497     GetTemplateActionRowFor(aIndex, getter_AddRefs(row));
00498     if (row) {
00499         nsAutoString raw;
00500         row->GetAttr(kNameSpaceID_None, nsXULAtoms::properties, raw);
00501 
00502         if (!raw.IsEmpty()) {
00503             nsAutoString cooked;
00504             SubstituteText(*(mRows[aIndex]->mMatch), raw, cooked);
00505 
00506             nsTreeUtils::TokenizeProperties(cooked, aProperties);
00507         }
00508     }
00509 
00510     return NS_OK;
00511 }
00512 
00513 NS_IMETHODIMP
00514 nsXULTreeBuilder::GetCellProperties(PRInt32 aRow, nsITreeColumn* aCol, nsISupportsArray* aProperties)
00515 {
00516     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
00517     if (aRow < 0 || aRow >= mRows.Count())
00518         return NS_ERROR_INVALID_ARG;
00519 
00520     nsCOMPtr<nsIContent> cell;
00521     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
00522     if (cell) {
00523         nsAutoString raw;
00524         cell->GetAttr(kNameSpaceID_None, nsXULAtoms::properties, raw);
00525 
00526         if (!raw.IsEmpty()) {
00527             nsAutoString cooked;
00528             SubstituteText(*(mRows[aRow]->mMatch), raw, cooked);
00529 
00530             nsTreeUtils::TokenizeProperties(cooked, aProperties);
00531         }
00532     }
00533 
00534     return NS_OK;
00535 }
00536 
00537 NS_IMETHODIMP
00538 nsXULTreeBuilder::GetColumnProperties(nsITreeColumn* aCol,
00539                                       nsISupportsArray* aProperties)
00540 {
00541     // XXX sortactive fu
00542     return NS_OK;
00543 }
00544 
00545 NS_IMETHODIMP
00546 nsXULTreeBuilder::IsContainer(PRInt32 aIndex, PRBool* aResult)
00547 {
00548     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
00549     if (aIndex < 0 || aIndex >= mRows.Count())
00550         return NS_ERROR_INVALID_ARG;
00551 
00552     nsTreeRows::iterator iter = mRows[aIndex];
00553 
00554     if (iter->mContainerType == nsTreeRows::eContainerType_Unknown) {
00555         PRBool isContainer;
00556         CheckContainer(GetResourceFor(aIndex), &isContainer, nsnull);
00557 
00558         iter->mContainerType = isContainer
00559             ? nsTreeRows::eContainerType_Container
00560             : nsTreeRows::eContainerType_Noncontainer;
00561     }
00562 
00563     *aResult = (iter->mContainerType == nsTreeRows::eContainerType_Container);
00564     return NS_OK;
00565 }
00566 
00567 NS_IMETHODIMP
00568 nsXULTreeBuilder::IsContainerOpen(PRInt32 aIndex, PRBool* aResult)
00569 {
00570     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
00571     if (aIndex < 0 || aIndex >= mRows.Count())
00572         return NS_ERROR_INVALID_ARG;
00573 
00574     nsTreeRows::iterator iter = mRows[aIndex];
00575 
00576     if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) {
00577         PRBool isOpen;
00578         IsContainerOpen(GetResourceFor(aIndex), &isOpen);
00579 
00580         iter->mContainerState = isOpen
00581             ? nsTreeRows::eContainerState_Open
00582             : nsTreeRows::eContainerState_Closed;
00583     }
00584 
00585     *aResult = (iter->mContainerState == nsTreeRows::eContainerState_Open);
00586     return NS_OK;
00587 }
00588 
00589 NS_IMETHODIMP
00590 nsXULTreeBuilder::IsContainerEmpty(PRInt32 aIndex, PRBool* aResult)
00591 {
00592     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
00593     if (aIndex < 0 || aIndex >= mRows.Count())
00594         return NS_ERROR_INVALID_ARG;
00595 
00596     nsTreeRows::iterator iter = mRows[aIndex];
00597     NS_ASSERTION(iter->mContainerType == nsTreeRows::eContainerType_Container,
00598                  "asking for empty state on non-container");
00599 
00600     if (iter->mContainerFill == nsTreeRows::eContainerFill_Unknown) {
00601         PRBool isEmpty;
00602         CheckContainer(GetResourceFor(aIndex), nsnull, &isEmpty);
00603 
00604         iter->mContainerFill = isEmpty
00605             ? nsTreeRows::eContainerFill_Empty
00606             : nsTreeRows::eContainerFill_Nonempty;
00607     }
00608 
00609     *aResult = (iter->mContainerFill == nsTreeRows::eContainerFill_Empty);
00610     return NS_OK;
00611 }
00612 
00613 NS_IMETHODIMP
00614 nsXULTreeBuilder::IsSeparator(PRInt32 aIndex, PRBool* aResult)
00615 {
00616     NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
00617     if (aIndex < 0 || aIndex >= mRows.Count())
00618         return NS_ERROR_INVALID_ARG;
00619 
00620     nsIRDFResource* resource = GetResourceFor(aIndex);
00621     mDB->HasAssertion(resource, kRDF_type, kNC_BookmarkSeparator, PR_TRUE, aResult);
00622 
00623     return NS_OK;
00624 }
00625 
00626 NS_IMETHODIMP
00627 nsXULTreeBuilder::GetParentIndex(PRInt32 aRowIndex, PRInt32* aResult)
00628 {
00629     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
00630     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
00631         return NS_ERROR_INVALID_ARG;
00632 
00633     // Construct a path to the row
00634     nsTreeRows::iterator iter = mRows[aRowIndex];
00635 
00636     // The parent of the row will be at the top of the path
00637     nsTreeRows::Subtree* parent = iter.GetParent();
00638 
00639     // Now walk through our previous siblings, subtracting off each
00640     // one's subtree size
00641     PRInt32 index = iter.GetChildIndex();
00642     while (--index >= 0)
00643         aRowIndex -= mRows.GetSubtreeSizeFor(parent, index) + 1;
00644 
00645     // Now the parent's index will be the first row's index, less one.
00646     *aResult = aRowIndex - 1;
00647     return NS_OK;
00648 }
00649 
00650 NS_IMETHODIMP
00651 nsXULTreeBuilder::HasNextSibling(PRInt32 aRowIndex, PRInt32 aAfterIndex, PRBool* aResult)
00652 {
00653     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
00654     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
00655         return NS_ERROR_INVALID_ARG;
00656 
00657     // Construct a path to the row
00658     nsTreeRows::iterator iter = mRows[aRowIndex];
00659 
00660     // The parent of the row will be at the top of the path
00661     nsTreeRows::Subtree* parent = iter.GetParent();
00662 
00663     // We have a next sibling if the child is not the last in the
00664     // subtree.
00665     *aResult = PRBool(iter.GetChildIndex() != parent->Count() - 1);
00666     return NS_OK;
00667 }
00668 
00669 NS_IMETHODIMP
00670 nsXULTreeBuilder::GetLevel(PRInt32 aRowIndex, PRInt32* aResult)
00671 {
00672     NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
00673     if (aRowIndex < 0 || aRowIndex >= mRows.Count())
00674         return NS_ERROR_INVALID_ARG;
00675 
00676     // Construct a path to the row; the ``level'' is the path length
00677     // less one.
00678     nsTreeRows::iterator iter = mRows[aRowIndex];
00679     *aResult = iter.GetDepth() - 1;
00680     return NS_OK;
00681 }
00682 
00683 NS_IMETHODIMP
00684 nsXULTreeBuilder::GetImageSrc(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
00685 {
00686     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
00687     if (aRow < 0 || aRow >= mRows.Count())
00688         return NS_ERROR_INVALID_ARG;
00689 
00690     // Find the <cell> that corresponds to the column we want.
00691     nsCOMPtr<nsIContent> cell;
00692     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
00693     if (cell) {
00694         nsAutoString raw;
00695         cell->GetAttr(kNameSpaceID_None, nsHTMLAtoms::src, raw);
00696 
00697         SubstituteText(*(mRows[aRow]->mMatch), raw, aResult);
00698     }
00699     else
00700         aResult.SetCapacity(0);
00701 
00702     return NS_OK;
00703 }
00704 
00705 
00706 NS_IMETHODIMP
00707 nsXULTreeBuilder::GetProgressMode(PRInt32 aRow, nsITreeColumn* aCol, PRInt32* aResult)
00708 {
00709     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
00710     if (aRow < 0 || aRow >= mRows.Count())
00711         return NS_ERROR_INVALID_ARG;
00712 
00713     *aResult = nsITreeView::PROGRESS_NONE;
00714 
00715     // Find the <cell> that corresponds to the column we want.
00716     nsCOMPtr<nsIContent> cell;
00717     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
00718     if (cell) {
00719         nsAutoString raw;
00720         cell->GetAttr(kNameSpaceID_None, nsXULAtoms::mode, raw);
00721 
00722         nsAutoString mode;
00723         SubstituteText(*(mRows[aRow]->mMatch), raw, mode);
00724 
00725         if (mode.EqualsLiteral("normal"))
00726             *aResult = nsITreeView::PROGRESS_NORMAL;
00727         else if (mode.EqualsLiteral("undetermined"))
00728             *aResult = nsITreeView::PROGRESS_UNDETERMINED;
00729     }
00730 
00731     return NS_OK;
00732 }
00733 
00734 NS_IMETHODIMP
00735 nsXULTreeBuilder::GetCellValue(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
00736 {
00737     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
00738     if (aRow < 0 || aRow >= mRows.Count())
00739         return NS_ERROR_INVALID_ARG;
00740 
00741     // Find the <cell> that corresponds to the column we want.
00742     nsCOMPtr<nsIContent> cell;
00743     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
00744     if (cell) {
00745         nsAutoString raw;
00746         cell->GetAttr(kNameSpaceID_None, nsXULAtoms::value, raw);
00747 
00748         SubstituteText(*(mRows[aRow]->mMatch), raw, aResult);
00749     }
00750     else
00751         aResult.SetCapacity(0);
00752 
00753     return NS_OK;
00754 }
00755 
00756 NS_IMETHODIMP
00757 nsXULTreeBuilder::GetCellText(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
00758 {
00759     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
00760     if (aRow < 0 || aRow >= mRows.Count())
00761         return NS_ERROR_INVALID_ARG;
00762 
00763     // Find the <cell> that corresponds to the column we want.
00764     nsCOMPtr<nsIContent> cell;
00765     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
00766     if (cell) {
00767         nsAutoString raw;
00768         cell->GetAttr(kNameSpaceID_None, nsXULAtoms::label, raw);
00769 
00770         SubstituteText(*(mRows[aRow]->mMatch), raw, aResult);
00771 
00772     }
00773     else
00774         aResult.SetCapacity(0);
00775 
00776     return NS_OK;
00777 }
00778 
00779 NS_IMETHODIMP
00780 nsXULTreeBuilder::SetTree(nsITreeBoxObject* tree)
00781 {
00782     NS_PRECONDITION(mRoot, "not initialized");
00783 
00784     mBoxObject = tree;
00785 
00786     // If this is teardown time, then we're done.
00787     if (! mBoxObject)
00788         return NS_OK;
00789 
00790     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
00791     NS_ASSERTION(doc, "element has no document");
00792     if (!doc)
00793         return NS_ERROR_UNEXPECTED;
00794 
00795     // Grab the doc's principal...
00796     nsIPrincipal* docPrincipal = doc->GetPrincipal();
00797     if (!docPrincipal)
00798         return NS_ERROR_FAILURE;
00799 
00800     PRBool isTrusted = PR_FALSE;
00801     nsresult rv = IsSystemPrincipal(docPrincipal, &isTrusted);
00802     if (NS_SUCCEEDED(rv) && isTrusted) {
00803         // Get the datasource we intend to use to remember open state.
00804         nsAutoString datasourceStr;
00805         mRoot->GetAttr(kNameSpaceID_None, nsXULAtoms::statedatasource, datasourceStr);
00806 
00807         // since we are trusted, use the user specified datasource
00808         // if non specified, use localstore, which gives us
00809         // persistence across sessions
00810         if (! datasourceStr.IsEmpty()) {
00811             gRDFService->GetDataSource(NS_ConvertUCS2toUTF8(datasourceStr).get(),
00812                                        getter_AddRefs(mPersistStateStore));
00813         }
00814         else {
00815             gRDFService->GetDataSource("rdf:local-store",
00816                                        getter_AddRefs(mPersistStateStore));
00817         }
00818     }
00819 
00820     // Either no specific datasource was specified, or we failed
00821     // to get one because we are not trusted.
00822     //
00823     // XXX if it were possible to ``write an arbitrary datasource
00824     // back'', then we could also allow an untrusted document to
00825     // use a statedatasource from the same codebase.
00826     if (! mPersistStateStore) {
00827         mPersistStateStore =
00828             do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource");
00829     }
00830 
00831     NS_ASSERTION(mPersistStateStore, "failed to get a persistent state store");
00832     if (! mPersistStateStore)
00833         return NS_ERROR_FAILURE;
00834 
00835     Rebuild();
00836 
00837     EnsureSortVariables();
00838     if (mSortVariable)
00839         SortSubtree(mRows.GetRoot());
00840 
00841     return NS_OK;
00842 }
00843 
00844 NS_IMETHODIMP
00845 nsXULTreeBuilder::ToggleOpenState(PRInt32 aIndex)
00846 {
00847     if (mObservers) {
00848         PRUint32 count;
00849         mObservers->Count(&count);
00850         for (PRUint32 i = 0; i < count; ++i) {
00851             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
00852             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
00853             if (observer)
00854                 observer->OnToggleOpenState(aIndex);
00855         }
00856     }
00857     
00858     if (mPersistStateStore) {
00859         PRBool isOpen;
00860         IsContainerOpen(aIndex, &isOpen);
00861 
00862         nsIRDFResource* container = GetResourceFor(aIndex);
00863         if (! container)
00864             return NS_ERROR_FAILURE;
00865 
00866         PRBool hasProperty;
00867         IsContainerOpen(container, &hasProperty);
00868 
00869         if (isOpen) {
00870             if (hasProperty) {
00871                 mPersistStateStore->Unassert(container,
00872                                              nsXULContentUtils::NC_open,
00873                                              nsXULContentUtils::true_);
00874             }
00875 
00876             CloseContainer(aIndex, container);
00877         }
00878         else {
00879             if (! hasProperty) {
00880                 mPersistStateStore->Assert(VALUE_TO_IRDFRESOURCE(container),
00881                                            nsXULContentUtils::NC_open,
00882                                            nsXULContentUtils::true_,
00883                                            PR_TRUE);
00884             }
00885 
00886             OpenContainer(aIndex, container);
00887         }
00888     }
00889 
00890     return NS_OK;
00891 }
00892 
00893 NS_IMETHODIMP
00894 nsXULTreeBuilder::CycleHeader(nsITreeColumn* aCol)
00895 {
00896     nsCOMPtr<nsIDOMElement> element;
00897     aCol->GetElement(getter_AddRefs(element));
00898 
00899     if (mObservers) {
00900         nsAutoString id;
00901         aCol->GetId(id);
00902 
00903         PRUint32 count;
00904         mObservers->Count(&count);
00905         for (PRUint32 i = 0; i < count; ++i) {
00906             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
00907             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
00908             if (observer)
00909                 observer->OnCycleHeader(id.get(), element);
00910         }
00911     }
00912 
00913     return Sort(element);
00914 }
00915 
00916 NS_IMETHODIMP
00917 nsXULTreeBuilder::SelectionChanged()
00918 {
00919     if (mObservers) {
00920         PRUint32 count;
00921         mObservers->Count(&count);
00922         for (PRUint32 i = 0; i < count; ++i) {
00923             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
00924             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
00925             if (observer)
00926                 observer->OnSelectionChanged();
00927         }
00928     }
00929 
00930     return NS_OK;
00931 }
00932 
00933 NS_IMETHODIMP
00934 nsXULTreeBuilder::CycleCell(PRInt32 row, nsITreeColumn* col)
00935 {
00936     if (mObservers) {
00937         nsAutoString id;
00938         col->GetId(id);
00939 
00940         PRUint32 count;
00941         mObservers->Count(&count);
00942         for (PRUint32 i = 0; i < count; ++i) {
00943             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
00944             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
00945             if (observer)
00946                 observer->OnCycleCell(row, id.get());
00947         }
00948     }
00949 
00950     return NS_OK;
00951 }
00952 
00953 NS_IMETHODIMP
00954 nsXULTreeBuilder::IsEditable(PRInt32 aRow, nsITreeColumn* aCol, PRBool* _retval)
00955 {
00956     NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
00957     if (aRow < 0 || aRow >= mRows.Count())
00958         return NS_ERROR_INVALID_ARG;
00959 
00960     *_retval = PR_TRUE;
00961 
00962     // Find the <cell> that corresponds to the column we want.
00963     nsCOMPtr<nsIContent> cell;
00964     GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
00965     if (cell) {
00966         nsAutoString raw;
00967         cell->GetAttr(kNameSpaceID_None, nsXULAtoms::editable, raw);
00968 
00969         nsAutoString editable;
00970         SubstituteText(*(mRows[aRow]->mMatch), raw, editable);
00971 
00972         if (editable.EqualsLiteral("false"))
00973             *_retval = PR_FALSE;
00974     }
00975 
00976     return NS_OK;
00977 }
00978 
00979 NS_IMETHODIMP
00980 nsXULTreeBuilder::SetCellValue(PRInt32 row, nsITreeColumn* col, const nsAString& value)
00981 {
00982     return NS_OK;
00983 }
00984 
00985 NS_IMETHODIMP
00986 nsXULTreeBuilder::SetCellText(PRInt32 row, nsITreeColumn* col, const nsAString& value)
00987 {
00988     return NS_OK;
00989 }
00990 
00991 NS_IMETHODIMP
00992 nsXULTreeBuilder::PerformAction(const PRUnichar* action)
00993 {
00994     if (mObservers) {  
00995         PRUint32 count;
00996         mObservers->Count(&count);
00997         for (PRUint32 i = 0; i < count; ++i) {
00998             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
00999             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
01000             if (observer)
01001                 observer->OnPerformAction(action);
01002         }
01003     }
01004 
01005     return NS_OK;
01006 }
01007 
01008 NS_IMETHODIMP
01009 nsXULTreeBuilder::PerformActionOnRow(const PRUnichar* action, PRInt32 row)
01010 {
01011     if (mObservers) {  
01012         PRUint32 count;
01013         mObservers->Count(&count);
01014         for (PRUint32 i = 0; i < count; ++i) {
01015             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
01016             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
01017             if (observer)
01018                 observer->OnPerformActionOnRow(action, row);
01019         }
01020     }
01021 
01022     return NS_OK;
01023 }
01024 
01025 NS_IMETHODIMP
01026 nsXULTreeBuilder::PerformActionOnCell(const PRUnichar* action, PRInt32 row, nsITreeColumn* col)
01027 {
01028     if (mObservers) {  
01029         nsAutoString id;
01030         col->GetId(id);
01031 
01032         PRUint32 count;
01033         mObservers->Count(&count);
01034         for (PRUint32 i = 0; i < count; ++i) {
01035             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
01036             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
01037             if (observer)
01038                 observer->OnPerformActionOnCell(action, row, id.get());
01039         }
01040     }
01041 
01042     return NS_OK;
01043 }
01044 
01045 
01046 void
01047 nsXULTreeBuilder::DocumentWillBeDestroyed(nsIDocument* aDocument)
01048 {
01049     if (mObservers)
01050         mObservers->Clear();
01051 
01052     nsXULTemplateBuilder::DocumentWillBeDestroyed(aDocument);
01053 }
01054 
01055  
01056 nsresult
01057 nsXULTreeBuilder::ReplaceMatch(nsIRDFResource* aMember,
01058                                    const nsTemplateMatch* aOldMatch,
01059                                    nsTemplateMatch* aNewMatch)
01060 {
01061     if (! mBoxObject)
01062         return NS_OK;
01063 
01064     if (aOldMatch) {
01065         // Either replacement or removal. Grovel through the rows
01066         // looking for aOldMatch.
01067         nsTreeRows::iterator iter = mRows.Find(mConflictSet, aMember);
01068 
01069         NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
01070         if (iter == mRows.Last())
01071             return NS_ERROR_FAILURE;
01072 
01073         if (aNewMatch) {
01074             // replacement
01075             iter->mMatch = aNewMatch;
01076             mBoxObject->InvalidateRow(iter.GetRowIndex());
01077         }
01078         else {
01079             // Removal. Clean up the conflict set.
01080             Value val;
01081             NS_CONST_CAST(nsTemplateMatch*, aOldMatch)->GetAssignmentFor(mConflictSet, mContainerVar, &val);
01082 
01083             nsIRDFResource* container = VALUE_TO_IRDFRESOURCE(val);
01084             RemoveMatchesFor(container, aMember);
01085 
01086             // Remove the rows from the view
01087             PRInt32 row = iter.GetRowIndex();
01088             PRInt32 delta = mRows.GetSubtreeSizeFor(iter);
01089             if (mRows.RemoveRowAt(iter) == 0 && iter.GetRowIndex() >= 0) {
01090                 // In this case iter now points to its parent
01091                 // Invalidate the row's cached fill state
01092                 iter->mContainerFill = nsTreeRows::eContainerFill_Unknown;
01093 
01094                 nsCOMPtr<nsITreeColumns> cols;
01095                 mBoxObject->GetColumns(getter_AddRefs(cols));
01096                 if (cols) {
01097                     nsCOMPtr<nsITreeColumn> primaryCol;
01098                     cols->GetPrimaryColumn(getter_AddRefs(primaryCol));
01099                     if (primaryCol)
01100                       mBoxObject->InvalidateCell(iter.GetRowIndex(), primaryCol);
01101                 }
01102             }
01103 
01104             // Notify the box object
01105             mBoxObject->RowCountChanged(row, -delta - 1);
01106         }
01107     }
01108     else if (aNewMatch) {
01109         // Insertion.
01110         Value val;
01111         aNewMatch->GetAssignmentFor(mConflictSet, mContainerVar, &val);
01112 
01113         nsIRDFResource* container = VALUE_TO_IRDFRESOURCE(val);
01114 
01115         PRInt32 row = -1;
01116         nsTreeRows::Subtree* parent = nsnull;
01117 
01118         if (container != mRows.GetRootResource()) {
01119             nsTreeRows::iterator iter =
01120                 mRows.Find(mConflictSet, container);
01121 
01122             row = iter.GetRowIndex();
01123 
01124             NS_ASSERTION(iter != mRows.Last(), "couldn't find container row");
01125             if (iter == mRows.Last())
01126                 return NS_ERROR_FAILURE;
01127 
01128             // Use the persist store to remember if the container
01129             // is open or closed.
01130             PRBool open = PR_FALSE;
01131             IsContainerOpen(row, &open);
01132 
01133             // If it's open, make sure that we've got a subtree structure ready.
01134             if (open)
01135                 parent = mRows.EnsureSubtreeFor(iter);
01136 
01137             // We know something has just been inserted into the
01138             // container, so whether its open or closed, make sure
01139             // that we've got our tree row's state correct.
01140             if ((iter->mContainerType != nsTreeRows::eContainerType_Container) ||
01141                 (iter->mContainerFill != nsTreeRows::eContainerFill_Nonempty)) {
01142                 iter->mContainerType  = nsTreeRows::eContainerType_Container;
01143                 iter->mContainerFill = nsTreeRows::eContainerFill_Nonempty;
01144                 mBoxObject->InvalidateRow(iter.GetRowIndex());
01145             }
01146         }
01147         else
01148             parent = mRows.GetRoot();
01149  
01150         if (parent) {
01151             // If we get here, then we're inserting into an open
01152             // container. By default, place the new element at the
01153             // end of the container
01154             PRInt32 index = parent->Count();
01155 
01156             if (mSortVariable) {
01157                 // Figure out where to put the new element by doing an
01158                 // insertion sort.
01159                 PRInt32 left = 0;
01160                 PRInt32 right = parent->Count();
01161 
01162                 while (left < right) {
01163                     index = (left + right) / 2;
01164                     PRInt32 cmp = CompareMatches((*parent)[index].mMatch, aNewMatch);
01165                     if (cmp < 0)
01166                         left = ++index;
01167                     else if (cmp > 0)
01168                         right = index;
01169                     else
01170                         break;
01171                 }
01172             }
01173 
01174             nsTreeRows::iterator iter =
01175                 mRows.InsertRowAt(aNewMatch, parent, index);
01176 
01177             mBoxObject->RowCountChanged(iter.GetRowIndex(), +1);
01178 
01179             // See if this newly added row is open; in which case,
01180             // recursively add its children to the tree, too.
01181             Value memberValue;
01182             aNewMatch->GetAssignmentFor(mConflictSet, mMemberVar, &memberValue);
01183 
01184             nsIRDFResource* member = VALUE_TO_IRDFRESOURCE(memberValue);
01185 
01186             PRBool open;
01187             IsContainerOpen(member, &open);
01188             if (open)
01189                 OpenContainer(iter.GetRowIndex(), member);
01190         }
01191     }
01192 
01193     return NS_OK;
01194 }
01195 
01196 nsresult
01197 nsXULTreeBuilder::SynchronizeMatch(nsTemplateMatch* aMatch, const VariableSet& aModifiedVars)
01198 {
01199     if (mBoxObject) {
01200         // XXX we could be more conservative and just invalidate the cells
01201         // that got whacked...
01202         Value val;
01203         aMatch->GetAssignmentFor(mConflictSet, aMatch->mRule->GetMemberVariable(), &val);
01204 
01205 #ifdef PR_LOGGING
01206         if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
01207             nsIRDFResource* res = VALUE_TO_IRDFRESOURCE(val);
01208 
01209             const char* str = "(null)";
01210             if (res)
01211                 res->GetValueConst(&str);
01212 
01213             PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01214                    ("xultemplate[%p] synchronizing %s (match=%p)", this, str, aMatch));
01215         }
01216 #endif
01217 
01218         nsTreeRows::iterator iter =
01219             mRows.Find(mConflictSet, VALUE_TO_IRDFRESOURCE(val));
01220 
01221         NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
01222         if (iter == mRows.Last())
01223             return NS_ERROR_FAILURE;
01224 
01225         PRInt32 row = iter.GetRowIndex();
01226         if (row >= 0)
01227             mBoxObject->InvalidateRow(row);
01228 
01229         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01230                ("xultemplate[%p]   => row %d", this, row));
01231     }
01232 
01233     return NS_OK;
01234 }
01235 
01236 //----------------------------------------------------------------------
01237 
01238 nsresult
01239 nsXULTreeBuilder::EnsureSortVariables()
01240 {
01241     // Grovel through <treecols> kids to find the <treecol>
01242     // with the sort attributes.
01243     nsCOMPtr<nsIContent> treecols;
01244  
01245     nsXULContentUtils::FindChildByTag(mRoot, kNameSpaceID_XUL, nsXULAtoms::treecols, getter_AddRefs(treecols));
01246 
01247     if (!treecols)
01248         return NS_OK;
01249 
01250     PRUint32 count = treecols->GetChildCount();
01251     for (PRUint32 i = 0; i < count; ++i) {
01252         nsIContent *child = treecols->GetChildAt(i);
01253 
01254         nsINodeInfo *ni = child->GetNodeInfo();
01255         if (ni && ni->Equals(nsXULAtoms::treecol, kNameSpaceID_XUL)) {
01256             nsAutoString sortActive;
01257             child->GetAttr(kNameSpaceID_None, nsXULAtoms::sortActive, sortActive);
01258             if (sortActive.EqualsLiteral("true")) {
01259                 nsAutoString sort;
01260                 child->GetAttr(kNameSpaceID_None, nsXULAtoms::sort, sort);
01261                 if (!sort.IsEmpty()) {
01262                     mSortVariable = mRules.LookupSymbol(sort.get(), PR_TRUE);
01263 
01264                     nsAutoString sortDirection;
01265                     child->GetAttr(kNameSpaceID_None, nsXULAtoms::sortDirection, sortDirection);
01266                     if (sortDirection.EqualsLiteral("ascending"))
01267                         mSortDirection = eDirection_Ascending;
01268                     else if (sortDirection.EqualsLiteral("descending"))
01269                         mSortDirection = eDirection_Descending;
01270                     else
01271                         mSortDirection = eDirection_Natural;
01272                 }
01273                 break;
01274             }
01275         }
01276     }
01277 
01278     return NS_OK;
01279 }
01280 
01281 nsresult
01282 nsXULTreeBuilder::InitializeRuleNetworkForSimpleRules(InnerNode** aChildNode)
01283 {
01284     // For simple rules, the rule network will start off looking
01285     // something like this:
01286     //
01287     //   (root)-->(treerow ^id ?a)-->(?a ^member ?b)
01288     //
01289     TestNode* rowtestnode =
01290         new nsTreeRowTestNode(mRules.GetRoot(),
01291                                   mConflictSet,
01292                                   mRows,
01293                                   mContainerVar);
01294 
01295     if (! rowtestnode)
01296         return NS_ERROR_OUT_OF_MEMORY;
01297 
01298     mRules.GetRoot()->AddChild(rowtestnode);
01299     mRules.AddNode(rowtestnode);
01300 
01301     // Create (?container ^member ?member)
01302     nsRDFConMemberTestNode* membernode =
01303         new nsRDFConMemberTestNode(rowtestnode,
01304                                    mConflictSet,
01305                                    mDB,
01306                                    mContainmentProperties,
01307                                    mContainerVar,
01308                                    mMemberVar);
01309 
01310     if (! membernode)
01311         return NS_ERROR_OUT_OF_MEMORY;
01312 
01313     rowtestnode->AddChild(membernode);
01314     mRules.AddNode(membernode);
01315 
01316     mRDFTests.Add(membernode);
01317 
01318     *aChildNode = membernode;
01319     return NS_OK;
01320 }
01321 
01322 nsresult
01323 nsXULTreeBuilder::RebuildAll()
01324 {
01325     NS_PRECONDITION(mRoot != nsnull, "not initialized");
01326     if (! mRoot)
01327         return NS_ERROR_NOT_INITIALIZED;
01328 
01329     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
01330 
01331     // Bail out early if we are being torn down.
01332     if (!doc)
01333         return NS_OK;
01334 
01335     PRInt32 count = mRows.Count();
01336     mRows.Clear();
01337     mConflictSet.Clear();
01338 
01339     if (mBoxObject) {
01340         mBoxObject->BeginUpdateBatch();
01341         mBoxObject->RowCountChanged(0, -count);
01342     }
01343 
01344     nsresult rv = CompileRules();
01345     if (NS_FAILED(rv)) return rv;
01346 
01347     // Seed the rule network with assignments for the tree row
01348     // variable
01349     nsCOMPtr<nsIRDFResource> root;
01350     nsXULContentUtils::GetElementRefResource(mRoot, getter_AddRefs(root));
01351     mRows.SetRootResource(root);
01352 
01353 #ifdef PR_LOGGING
01354     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
01355         const char* s = "(null)";
01356         if (root)
01357             root->GetValueConst(&s);
01358 
01359         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01360                ("xultemplate[%p] root=%s", this, s));
01361     }
01362 #endif
01363 
01364     if (root)
01365         OpenContainer(-1, root);
01366 
01367     if (mBoxObject) {
01368         mBoxObject->EndUpdateBatch();
01369     }
01370 
01371     return NS_OK;
01372 }
01373 
01374 nsresult
01375 nsXULTreeBuilder::CompileCondition(nsIAtom* aTag,
01376                                        nsTemplateRule* aRule,
01377                                        nsIContent* aCondition,
01378                                        InnerNode* aParentNode,
01379                                        TestNode** aResult)
01380 {
01381     nsresult rv;
01382 
01383     if (aTag == nsXULAtoms::content || aTag == nsXULAtoms::treeitem)
01384         rv = CompileTreeRowCondition(aRule, aCondition, aParentNode, aResult);
01385     else
01386         rv = nsXULTemplateBuilder::CompileCondition(aTag, aRule, aCondition, aParentNode, aResult);
01387 
01388     return rv;
01389 }
01390 
01391 nsresult
01392 nsXULTreeBuilder::CompileTreeRowCondition(nsTemplateRule* aRule,
01393                                                   nsIContent* aCondition,
01394                                                   InnerNode* aParentNode,
01395                                                   TestNode** aResult)
01396 {
01397     // Compile a <content> condition, which must be of the form:
01398     //
01399     //   <content uri="?uri" />
01400     //
01401     // Right now, exactly one <row> condition is required per rule. It
01402     // creates an nsTreeRowTestNode, binding the test's variable
01403     // to the global row variable that's used during match
01404     // propagation. The ``uri'' attribute must be set.
01405 
01406     nsAutoString uri;
01407     aCondition->GetAttr(kNameSpaceID_None, nsXULAtoms::uri, uri);
01408 
01409     if (uri[0] != PRUnichar('?')) {
01410         PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
01411                ("xultemplate[%p] on <row> test, expected 'uri' attribute to name a variable", this));
01412 
01413         return NS_OK;
01414     }
01415 
01416     PRInt32 urivar = mRules.LookupSymbol(uri.get());
01417     if (! urivar) {
01418         if (mContainerSymbol.IsEmpty()) {
01419             // If the container symbol was not explictly declared on
01420             // the <template> tag, or we haven't seen a previous rule
01421             // whose <content> condition defined it, then we'll
01422             // implictly define it *now*.
01423             mContainerSymbol = uri;
01424             urivar = mContainerVar;
01425         }
01426         else
01427             urivar = mRules.CreateAnonymousVariable();
01428 
01429         mRules.PutSymbol(uri.get(), urivar);
01430     }
01431 
01432     TestNode* testnode =
01433         new nsTreeRowTestNode(aParentNode,
01434                                   mConflictSet,
01435                                   mRows,
01436                                   urivar);
01437 
01438     if (! testnode)
01439         return NS_ERROR_OUT_OF_MEMORY;
01440 
01441     *aResult = testnode;
01442     return NS_OK;
01443 }
01444 
01445 nsresult
01446 nsXULTreeBuilder::GetTemplateActionRowFor(PRInt32 aRow, nsIContent** aResult)
01447 {
01448     // Get the template in the DOM from which we're supposed to
01449     // generate text
01450     nsTreeRows::Row& row = *(mRows[aRow]);
01451 
01452     nsCOMPtr<nsIContent> action;
01453     row.mMatch->mRule->GetContent(getter_AddRefs(action));
01454 
01455     nsCOMPtr<nsIContent> children;
01456     nsXULContentUtils::FindChildByTag(action, kNameSpaceID_XUL, nsXULAtoms::treechildren, getter_AddRefs(children));
01457     if (children) {
01458         nsCOMPtr<nsIContent> item;
01459         nsXULContentUtils::FindChildByTag(children, kNameSpaceID_XUL, nsXULAtoms::treeitem, getter_AddRefs(item));
01460         if (item)
01461             return nsXULContentUtils::FindChildByTag(item, kNameSpaceID_XUL, nsXULAtoms::treerow, aResult);
01462     }
01463 
01464     *aResult = nsnull;
01465     return NS_OK;
01466 }
01467 
01468 nsresult
01469 nsXULTreeBuilder::GetTemplateActionCellFor(PRInt32 aRow,
01470                                            nsITreeColumn* aCol,
01471                                            nsIContent** aResult)
01472 {
01473     *aResult = nsnull;
01474 
01475     if (!aCol) return NS_ERROR_INVALID_ARG;
01476 
01477     nsCOMPtr<nsIContent> row;
01478     GetTemplateActionRowFor(aRow, getter_AddRefs(row));
01479     if (row) {
01480         const PRUnichar* colID;
01481         PRInt32 colIndex;
01482         aCol->GetIdConst(&colID);
01483         aCol->GetIndex(&colIndex);
01484 
01485         PRUint32 count = row->GetChildCount();
01486         PRUint32 j = 0;
01487         for (PRUint32 i = 0; i < count; ++i) {
01488             nsIContent *child = row->GetChildAt(i);
01489 
01490             nsINodeInfo *ni = child->GetNodeInfo();
01491 
01492             if (ni && ni->Equals(nsXULAtoms::treecell, kNameSpaceID_XUL)) {
01493                 nsAutoString ref;
01494                 child->GetAttr(kNameSpaceID_None, nsXULAtoms::ref, ref);
01495                 if (!ref.IsEmpty() && ref.Equals(colID)) {
01496                     *aResult = child;
01497                     break;
01498                 }
01499                 else if (j == (PRUint32)colIndex)
01500                     *aResult = child;
01501                 j++;
01502             }
01503         }
01504     }
01505     NS_IF_ADDREF(*aResult);
01506 
01507     return NS_OK;
01508 }
01509 
01510 nsIRDFResource*
01511 nsXULTreeBuilder::GetResourceFor(PRInt32 aRow)
01512 {
01513     nsTreeRows::Row& row = *(mRows[aRow]);
01514 
01515     Value member;
01516     row.mMatch->GetAssignmentFor(mConflictSet, mMemberVar, &member);
01517 
01518     return VALUE_TO_IRDFRESOURCE(member); // not refcounted
01519 }
01520 
01521 nsresult
01522 nsXULTreeBuilder::OpenContainer(PRInt32 aIndex, nsIRDFResource* aContainer)
01523 {
01524     // A row index of -1 in this case means ``open tree body''
01525     NS_ASSERTION(aIndex >= -1 && aIndex < mRows.Count(), "bad row");
01526     if (aIndex < -1 || aIndex >= mRows.Count())
01527         return NS_ERROR_INVALID_ARG;
01528 
01529     nsTreeRows::Subtree* container;
01530 
01531     if (aIndex >= 0) {
01532         nsTreeRows::iterator iter = mRows[aIndex];
01533         container = mRows.EnsureSubtreeFor(iter.GetParent(),
01534                                            iter.GetChildIndex());
01535 
01536         iter->mContainerState = nsTreeRows::eContainerState_Open;
01537     }
01538     else
01539         container = mRows.GetRoot();
01540 
01541     if (! container)
01542         return NS_ERROR_OUT_OF_MEMORY;
01543 
01544     PRInt32 count;
01545     OpenSubtreeOf(container, aIndex, aContainer, &count);
01546 
01547     // Notify the box object
01548     if (mBoxObject) {
01549         if (aIndex >= 0)
01550             mBoxObject->InvalidateRow(aIndex);
01551 
01552         if (count)
01553             mBoxObject->RowCountChanged(aIndex + 1, count);
01554     }
01555 
01556     return NS_OK;
01557 }
01558 
01559 nsresult
01560 nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
01561                                     PRInt32 aIndex,
01562                                     nsIRDFResource* aContainer,
01563                                     PRInt32* aDelta)
01564 {
01565     Instantiation seed;
01566     seed.AddAssignment(mContainerVar, Value(aContainer));
01567 
01568 #ifdef PR_LOGGING
01569     static PRInt32 gNest;
01570 
01571     nsCAutoString space;
01572     {
01573         for (PRInt32 i = 0; i < gNest; ++i)
01574             space += "  ";
01575     }
01576 
01577     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
01578         const char* res;
01579         aContainer->GetValueConst(&res);
01580 
01581         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01582                ("xultemplate[%p] %sopening subtree for %s", this, space.get(), res));
01583     }
01584 
01585     ++gNest;
01586 #endif
01587 
01588     InstantiationSet instantiations;
01589     instantiations.Append(seed);
01590 
01591     // Propagate the assignments through the network
01592     nsClusterKeySet newkeys;
01593     mRules.GetRoot()->Propagate(instantiations, &newkeys);
01594 
01595     nsAutoVoidArray open;
01596     PRInt32 count = 0;
01597 
01598     // Iterate through newly added keys to determine which rules fired
01599     nsClusterKeySet::ConstIterator last = newkeys.Last();
01600     for (nsClusterKeySet::ConstIterator key = newkeys.First(); key != last; ++key) {
01601         nsConflictSet::MatchCluster* matches =
01602             mConflictSet.GetMatchesForClusterKey(*key);
01603 
01604         if (! matches)
01605             continue;
01606 
01607         nsTemplateMatch* match = 
01608             mConflictSet.GetMatchWithHighestPriority(matches);
01609 
01610         NS_ASSERTION(match != nsnull, "no best match in match set");
01611         if (! match)
01612             continue;
01613 
01614 #ifdef PR_LOGGING
01615         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01616                ("xultemplate[%p] %smatch=%p", this, space.get(), match));
01617 #endif
01618 
01619         Value val;
01620         match->GetAssignmentFor(mConflictSet,
01621                                 match->mRule->GetMemberVariable(),
01622                                 &val);
01623 
01624         // Don't allow cyclic graphs to get our knickers in a knot.
01625         PRBool cyclic = PR_FALSE;
01626 
01627         if (aIndex >= 0) {
01628             for (nsTreeRows::iterator iter = mRows[aIndex]; iter.GetDepth() > 0; iter.Pop()) {
01629                 nsTemplateMatch* parentMatch = iter->mMatch;
01630 
01631                 Value parentVal;
01632                 parentMatch->GetAssignmentFor(mConflictSet,
01633                                               parentMatch->mRule->GetMemberVariable(),
01634                                               &parentVal);
01635 
01636                 if (val == parentVal) {
01637                     cyclic = PR_TRUE;
01638                     break;
01639                 }
01640             }
01641         }
01642 
01643         if (cyclic) {
01644             NS_WARNING("outliner cannot handle cyclic graphs");
01645             continue;
01646         }
01647 
01648         // Remember that this match applied to this row
01649         mRows.InsertRowAt(match, aSubtree, count);
01650 
01651         // Remember this as the "last" match
01652         matches->mLastMatch = match;
01653 
01654         // If this is open, then remember it so we can recursively add
01655         // *its* rows to the tree.
01656         PRBool isOpen = PR_FALSE;
01657         IsContainerOpen(VALUE_TO_IRDFRESOURCE(val), &isOpen);
01658         if (isOpen)
01659             open.AppendElement((void*) count);
01660 
01661         ++count;
01662     }
01663 
01664     // Now recursively deal with any open sub-containers that just got
01665     // inserted. We need to do this back-to-front to avoid skewing offsets.
01666     for (PRInt32 i = open.Count() - 1; i >= 0; --i) {
01667         PRInt32 index = NS_PTR_TO_INT32(open[i]);
01668 
01669         nsTreeRows::Subtree* child =
01670             mRows.EnsureSubtreeFor(aSubtree, index);
01671 
01672         nsTemplateMatch* match = (*aSubtree)[index].mMatch;
01673 
01674         Value val;
01675         match->GetAssignmentFor(mConflictSet, match->mRule->GetMemberVariable(), &val);
01676 
01677         PRInt32 delta;
01678         OpenSubtreeOf(child, aIndex + index, VALUE_TO_IRDFRESOURCE(val), &delta);
01679         count += delta;
01680     }
01681 
01682 #ifdef PR_LOGGING
01683     --gNest;
01684 #endif
01685 
01686     // Sort the container.
01687     if (mSortVariable) {
01688         NS_QuickSort(mRows.GetRowsFor(aSubtree),
01689                      aSubtree->Count(),
01690                      sizeof(nsTreeRows::Row),
01691                      Compare,
01692                      this);
01693     }
01694 
01695     *aDelta = count;
01696     return NS_OK;
01697 }
01698 
01699 nsresult
01700 nsXULTreeBuilder::CloseContainer(PRInt32 aIndex, nsIRDFResource* aContainer)
01701 {
01702     NS_ASSERTION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
01703     if (aIndex < 0 || aIndex >= mRows.Count())
01704         return NS_ERROR_INVALID_ARG;
01705 
01706 #ifdef PR_LOGGING
01707     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
01708         const char* res;
01709         aContainer->GetValueConst(&res);
01710 
01711         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01712                ("xultemplate[%p] closing container %s", this, res));
01713     }
01714 #endif
01715 
01716     nsTemplateMatchSet firings(mConflictSet.GetPool());
01717     nsTemplateMatchSet retractions(mConflictSet.GetPool());
01718     mConflictSet.Remove(nsTreeRowTestNode::Element(aContainer), firings, retractions);
01719 
01720     {
01721         // Clean up the conflict set
01722         nsTemplateMatchSet::ConstIterator last = retractions.Last();
01723         nsTemplateMatchSet::ConstIterator iter;
01724 
01725         for (iter = retractions.First(); iter != last; ++iter) {
01726             PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01727                    ("xultemplate[%p] removing match %p", this, iter.operator->()));
01728 
01729             Value val;
01730             iter->GetAssignmentFor(mConflictSet, iter->mRule->GetMemberVariable(), &val);
01731 
01732             RemoveMatchesFor(aContainer, VALUE_TO_IRDFRESOURCE(val));
01733         }
01734     }
01735 
01736     {
01737         // Update the view
01738         nsTreeRows::iterator iter = mRows[aIndex];
01739 
01740         PRInt32 count = mRows.GetSubtreeSizeFor(iter);
01741         mRows.RemoveSubtreeFor(iter);
01742 
01743         iter->mContainerState = nsTreeRows::eContainerState_Closed;
01744 
01745         if (mBoxObject) {
01746             mBoxObject->InvalidateRow(aIndex);
01747 
01748             if (count)
01749                 mBoxObject->RowCountChanged(aIndex + 1, -count);
01750         }
01751     }
01752 
01753     return NS_OK;
01754 }
01755 
01756 nsresult
01757 nsXULTreeBuilder::RemoveMatchesFor(nsIRDFResource* aContainer, nsIRDFResource* aMember)
01758 {
01759     NS_PRECONDITION(aContainer != nsnull, "null ptr");
01760     if (! aContainer)
01761         return NS_ERROR_FAILURE;
01762 
01763     NS_PRECONDITION(aMember != nsnull, "null ptr");
01764     if (! aMember)
01765         return NS_ERROR_FAILURE;
01766 
01767 #ifdef PR_LOGGING
01768     static PRInt32 gNest;
01769 
01770     nsCAutoString space;
01771     for (PRInt32 i = 0; i < gNest; ++i)
01772         space += "  ";
01773 
01774     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
01775         const char* res;
01776         aMember->GetValueConst(&res);
01777 
01778         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01779                ("xultemplate[%p] %sremoving matches for %s", this, space.get(), res));
01780     }
01781 
01782     ++gNest;
01783 #endif
01784 
01785     // Pull supporting memory elements out of the conflict set. We
01786     // yank the container/member element so that it will be recreated
01787     // when the container is opened; we yank the row element so we'll
01788     // recurse to any open children.
01789     nsTemplateMatchSet firings(mConflictSet.GetPool());
01790     nsTemplateMatchSet retractions(mConflictSet.GetPool());
01791     mConflictSet.Remove(nsRDFConMemberTestNode::Element(aContainer, aMember), firings, retractions);
01792     mConflictSet.Remove(nsTreeRowTestNode::Element(aMember), firings, retractions);
01793 
01794     nsTemplateMatchSet::ConstIterator last = retractions.Last();
01795     nsTemplateMatchSet::ConstIterator iter;
01796 
01797     for (iter = retractions.First(); iter != last; ++iter) {
01798 #ifdef PR_LOGGING
01799         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01800                ("xultemplate[%p] %smatch=%p", this, space.get(), iter.operator->()));
01801 #endif
01802 
01803         Value val;
01804         iter->GetAssignmentFor(mConflictSet, iter->mRule->GetMemberVariable(), &val);
01805         RemoveMatchesFor(aMember, VALUE_TO_IRDFRESOURCE(val));
01806     }
01807 
01808 #ifdef PR_LOGGING
01809     --gNest;
01810 #endif
01811 
01812     return NS_OK;
01813 }
01814 
01815 nsresult
01816 nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aContainer, PRBool* aResult)
01817 {
01818     if (mPersistStateStore)
01819         mPersistStateStore->HasAssertion(aContainer,
01820                                          nsXULContentUtils::NC_open,
01821                                          nsXULContentUtils::true_,
01822                                          PR_TRUE,
01823                                          aResult);
01824     else
01825         *aResult = PR_FALSE;
01826 
01827     return NS_OK;
01828 }
01829 
01830 int
01831 nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure)
01832 {
01833     nsXULTreeBuilder* self = NS_STATIC_CAST(nsXULTreeBuilder*, aClosure);
01834 
01835     nsTreeRows::Row* left = NS_STATIC_CAST(nsTreeRows::Row*,
01836                                                NS_CONST_CAST(void*, aLeft));
01837 
01838     nsTreeRows::Row* right = NS_STATIC_CAST(nsTreeRows::Row*,
01839                                                 NS_CONST_CAST(void*, aRight));
01840 
01841     return self->CompareMatches(left->mMatch, right->mMatch);
01842 }
01843 
01844 PRInt32
01845 nsXULTreeBuilder::CompareMatches(nsTemplateMatch* aLeft, nsTemplateMatch* aRight)
01846 {
01847     PRInt32 result = 0;
01848 
01849     if (mSortDirection == eDirection_Natural) {
01850         // If the sort order is ``natural'', then see if the container
01851         // is an RDF sequence. If so, we'll try to use the ordinal
01852         // properties to determine order.
01853         //
01854         // XXX the problem with this is, it doesn't always get the
01855         // *real* container; e.g.,
01856         //
01857         //  <treerow uri="?uri" />
01858         //
01859         //  <triple subject="?uri"
01860         //          predicate="http://home.netscape.com/NC-rdf#subheadings"
01861         //          object="?subheadings" />
01862         //
01863         //  <member container="?subheadings" child="?subheading" />
01864         //
01865         // In this case mContainerVar is bound to ?uri, not
01866         // ?subheadings. (The ``container'' in the template sense !=
01867         // container in the RDF sense.)
01868         Value val;
01869         aLeft->GetAssignmentFor(mConflictSet, mContainerVar, &val);
01870 
01871         nsIRDFResource* container = VALUE_TO_IRDFRESOURCE(val);
01872 
01873         PRBool isSequence = PR_FALSE;
01874         gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
01875         if (! isSequence)
01876             // If it's not an RDF container, then there's no natural
01877             // order.
01878             return 0;
01879 
01880         // Determine the indices of the left and right elements in the
01881         // container.
01882         Value left;
01883         aLeft->GetAssignmentFor(mConflictSet, mMemberVar, &left);
01884 
01885         PRInt32 lindex;
01886         gRDFContainerUtils->IndexOf(mDB, container, VALUE_TO_IRDFNODE(left), &lindex);
01887         if (lindex < 0)
01888             return 0;
01889 
01890         Value right;
01891         aRight->GetAssignmentFor(mConflictSet, mMemberVar, &right);
01892 
01893         PRInt32 rindex;
01894         gRDFContainerUtils->IndexOf(mDB, container, VALUE_TO_IRDFNODE(right), &rindex);
01895         if (rindex < 0)
01896             return 0;
01897 
01898         return lindex - rindex;
01899     }
01900 
01901     // If we get here, then an ascending or descending sort order is
01902     // imposed.
01903     Value leftValue;
01904     aLeft->GetAssignmentFor(mConflictSet, mSortVariable, &leftValue);
01905     nsIRDFNode* leftNode = VALUE_TO_IRDFNODE(leftValue);
01906 
01907     Value rightValue;
01908     aRight->GetAssignmentFor(mConflictSet, mSortVariable, &rightValue);
01909     nsIRDFNode* rightNode = VALUE_TO_IRDFNODE(rightValue);
01910 
01911     {
01912         // Literals?
01913         nsCOMPtr<nsIRDFLiteral> l = do_QueryInterface(leftNode);
01914         if (l) {
01915             nsCOMPtr<nsIRDFLiteral> r = do_QueryInterface(rightNode);
01916             if (r) {
01917                 const PRUnichar *lstr, *rstr;
01918                 l->GetValueConst(&lstr);
01919                 r->GetValueConst(&rstr);
01920 
01921                 if (mCollation) {
01922                     mCollation->CompareString(nsICollation::kCollationCaseInSensitive,
01923                                               nsDependentString(lstr),
01924                                               nsDependentString(rstr),
01925                                               &result);
01926                 }
01927                 else
01928                     result = ::Compare(nsDependentString(lstr),
01929                                        nsDependentString(rstr),
01930                                        nsCaseInsensitiveStringComparator());
01931 
01932                 return result * mSortDirection;
01933             }
01934         }
01935     }
01936 
01937     {
01938         // Dates?
01939         nsCOMPtr<nsIRDFDate> l = do_QueryInterface(leftNode);
01940         if (l) {
01941             nsCOMPtr<nsIRDFDate> r = do_QueryInterface(rightNode);
01942             if (r) {
01943                 PRTime ldate, rdate;
01944                 l->GetValue(&ldate);
01945                 r->GetValue(&rdate);
01946 
01947                 PRInt64 delta;
01948                 LL_SUB(delta, ldate, rdate);
01949 
01950                 if (LL_IS_ZERO(delta))
01951                     result = 0;
01952                 else if (LL_GE_ZERO(delta))
01953                     result = 1;
01954                 else
01955                     result = -1;
01956 
01957                 return result * mSortDirection;
01958             }
01959         }
01960     }
01961 
01962     {
01963         // Integers?
01964         nsCOMPtr<nsIRDFInt> l = do_QueryInterface(leftNode);
01965         if (l) {
01966             nsCOMPtr<nsIRDFInt> r = do_QueryInterface(rightNode);
01967             if (r) {
01968                 PRInt32 lval, rval;
01969                 l->GetValue(&lval);
01970                 r->GetValue(&rval);
01971 
01972                 result = lval - rval;
01973 
01974                 return result * mSortDirection;
01975             }
01976         }
01977     }
01978 
01979     if (mCollation) {
01980         // Blobs? (We can only compare these reasonably if we have a
01981         // collation object.)
01982         nsCOMPtr<nsIRDFBlob> l = do_QueryInterface(leftNode);
01983         if (l) {
01984             nsCOMPtr<nsIRDFBlob> r = do_QueryInterface(rightNode);
01985             if (r) {
01986                 const PRUint8 *lval, *rval;
01987                 PRInt32 llen, rlen;
01988                 l->GetValue(&lval);
01989                 l->GetLength(&llen);
01990                 r->GetValue(&rval);
01991                 r->GetLength(&rlen);
01992                 
01993                 mCollation->CompareRawSortKey(lval, llen, rval, rlen, &result);
01994                 return result * mSortDirection;
01995             }
01996         }
01997     }
01998 
01999     // Ack! Apples & oranges...
02000     return 0;
02001 }
02002 
02003 nsresult
02004 nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree* aSubtree)
02005 {
02006     NS_QuickSort(mRows.GetRowsFor(aSubtree),
02007                  aSubtree->Count(),
02008                  sizeof(nsTreeRows::Row),
02009                  Compare,
02010                  this);
02011 
02012     for (PRInt32 i = aSubtree->Count() - 1; i >= 0; --i) {
02013         nsTreeRows::Subtree* child = (*aSubtree)[i].mSubtree;
02014         if (child)
02015             SortSubtree(child);
02016     }
02017 
02018     return NS_OK;
02019 }
02020 
02021 
02022 /* boolean canDrop (in long index, in long orientation); */
02023 NS_IMETHODIMP
02024 nsXULTreeBuilder::CanDrop(PRInt32 index, PRInt32 orientation, PRBool *_retval)
02025 {
02026     *_retval = PR_FALSE;
02027     if (mObservers) {
02028         PRUint32 count;
02029         mObservers->Count(&count);
02030         for (PRUint32 i = 0; i < count; ++i) {
02031             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
02032             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
02033             if (observer) {
02034                 observer->CanDrop(index, orientation, _retval);
02035                 if (*_retval)
02036                     break;
02037             }
02038         }
02039     }
02040 
02041     return NS_OK;
02042 }
02043 
02044 NS_IMETHODIMP
02045 nsXULTreeBuilder::Drop(PRInt32 row, PRInt32 orient)
02046 {
02047     if (mObservers) {
02048         PRUint32 count;
02049         mObservers->Count(&count);
02050         for (PRUint32 i = 0; i < count; ++i) {
02051             nsCOMPtr<nsIXULTreeBuilderObserver> observer;
02052             mObservers->QueryElementAt(i, NS_GET_IID(nsIXULTreeBuilderObserver), getter_AddRefs(observer));
02053             if (observer) {
02054                 PRBool canDrop = PR_FALSE;
02055                 observer->CanDrop(row, orient, &canDrop);
02056                 if (canDrop)
02057                     observer->OnDrop(row, orient);
02058             }
02059         }
02060     }
02061 
02062     return NS_OK;
02063 }
02064 
02065 NS_IMETHODIMP
02066 nsXULTreeBuilder::IsSorted(PRBool *_retval)
02067 {
02068   *_retval = mSortVariable;
02069   return NS_OK;
02070 }
02071