1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Chris Waterson <waterson@netscape.com>
24 * Ben Goodger <ben@netscape.com>
25 * Jan Varga <varga@ku.sk>
26 * Neil Deakin <enndeakin@sympatico.ca>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
43 #include "nsIContent.h"
44 #include "nsINodeInfo.h"
45 #include "nsIDOMElement.h"
46 #include "nsILocalStore.h"
47 #include "nsIBoxObject.h"
48 #include "nsITreeBoxObject.h"
49 #include "nsITreeSelection.h"
50 #include "nsITreeColumns.h"
51 #include "nsITreeView.h"
52 #include "nsTreeUtils.h"
53 #include "nsIServiceManager.h"
54 #include "nsReadableUtils.h"
55 #include "nsQuickSort.h"
56 #include "nsTreeRows.h"
57 #include "nsTemplateRule.h"
58 #include "nsTemplateMatch.h"
59 #include "nsGkAtoms.h"
60 #include "nsXULContentUtils.h"
61 #include "nsXULTemplateBuilder.h"
62 #include "nsVoidArray.h"
63 #include "nsUnicharUtils.h"
64 #include "nsINameSpaceManager.h"
65 #include "nsIDOMClassInfo.h"
68 #include "nsIDocument.h"
71 * A XUL template builder that serves as an tree view, allowing
72 * (pretty much) arbitrary RDF to be presented in an tree.
74 class nsXULTreeBuilder
: public nsXULTemplateBuilder
,
75 public nsIXULTreeBuilder
,
76 public nsINativeTreeView
80 NS_DECL_ISUPPORTS_INHERITED
83 NS_DECL_NSIXULTREEBUILDER
87 // nsINativeTreeView: Untrusted code can use us
88 NS_IMETHOD
EnsureNative() { return NS_OK
; }
90 virtual void NodeWillBeDestroyed(const nsINode
* aNode
);
94 NS_NewXULTreeBuilder(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
);
99 * Uninitialize the template builder
101 virtual void Uninit(PRBool aIsFinal
);
104 * Get sort variables from the active <treecol>
107 EnsureSortVariables();
113 * Given a row, use the row's match to figure out the appropriate
114 * <treerow> in the rule's <action>.
117 GetTemplateActionRowFor(PRInt32 aRow
, nsIContent
** aResult
);
120 * Given a row and a column ID, use the row's match to figure out
121 * the appropriate <treecell> in the rule's <action>.
124 GetTemplateActionCellFor(PRInt32 aRow
, nsITreeColumn
* aCol
, nsIContent
** aResult
);
127 * Return the resource corresponding to a row in the tree.
130 GetResourceFor(PRInt32 aRow
, nsIRDFResource
** aResource
);
133 * Open a container row, inserting the container's children into
137 OpenContainer(PRInt32 aIndex
, nsIXULTemplateResult
* aResult
);
140 * Helper for OpenContainer, recursively open subtrees, remembering
141 * persisted ``open'' state
144 OpenSubtreeOf(nsTreeRows::Subtree
* aSubtree
,
146 nsIXULTemplateResult
*aResult
,
150 OpenSubtreeForQuerySet(nsTreeRows::Subtree
* aSubtree
,
152 nsIXULTemplateResult
*aResult
,
153 nsTemplateQuerySet
* aQuerySet
,
155 nsAutoVoidArray
& open
);
158 * Close a container row, removing the container's childrem from
162 CloseContainer(PRInt32 aIndex
);
165 * Remove the matches for the rows in a subtree
168 RemoveMatchesFor(nsTreeRows::Subtree
& subtree
);
171 * Helper methods that determine if the specified container is open.
174 IsContainerOpen(nsIXULTemplateResult
*aResult
, PRBool
* aOpen
);
177 IsContainerOpen(nsIRDFResource
* aResource
, PRBool
* aOpen
);
180 * A sorting callback for NS_QuickSort().
183 Compare(const void* aLeft
, const void* aRight
, void* aClosure
);
186 * The real sort routine
189 CompareResults(nsIXULTemplateResult
* aLeft
, nsIXULTemplateResult
* aRight
);
192 * Sort the specified subtree, and recursively sort any subtrees
196 SortSubtree(nsTreeRows::Subtree
* aSubtree
);
199 HasGeneratedContent(nsIRDFResource
* aResource
,
203 // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited
204 // from nsXULTemplateBuilder
207 * Return true if the result can be inserted into the template as a new
211 GetInsertionLocations(nsIXULTemplateResult
* aResult
,
212 nsCOMArray
<nsIContent
>** aLocations
);
215 * Implement result replacement
218 ReplaceMatch(nsIXULTemplateResult
* aOldResult
,
219 nsTemplateMatch
* aNewMatch
,
220 nsTemplateRule
* aNewMatchRule
,
224 * Implement match synchronization
227 SynchronizeResult(nsIXULTemplateResult
* aResult
);
230 * The tree's box object, used to communicate with the front-end.
232 nsCOMPtr
<nsITreeBoxObject
> mBoxObject
;
235 * The tree's selection object.
237 nsCOMPtr
<nsITreeSelection
> mSelection
;
240 * The datasource that's used to persist open folder information
242 nsCOMPtr
<nsIRDFDataSource
> mPersistStateStore
;
245 * The rows in the view
250 * The currently active sort variable
252 nsCOMPtr
<nsIAtom
> mSortVariable
;
255 eDirection_Descending
= -1,
256 eDirection_Natural
= 0,
257 eDirection_Ascending
= +1
261 * The currently active sort order
263 Direction mSortDirection
;
266 * The builder observers.
268 nsCOMPtr
<nsISupportsArray
> mObservers
;
271 //----------------------------------------------------------------------
274 NS_NewXULTreeBuilder(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
)
278 NS_PRECONDITION(aOuter
== nsnull
, "no aggregation");
280 return NS_ERROR_NO_AGGREGATION
;
283 nsXULTreeBuilder
* result
= new nsXULTreeBuilder();
285 return NS_ERROR_OUT_OF_MEMORY
;
287 NS_ADDREF(result
); // stabilize
289 rv
= result
->InitGlobals();
291 if (NS_SUCCEEDED(rv
))
292 rv
= result
->QueryInterface(aIID
, aResult
);
298 NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder
, nsXULTemplateBuilder
)
299 NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder
, nsXULTemplateBuilder
)
301 NS_INTERFACE_MAP_BEGIN(nsXULTreeBuilder
)
302 NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder
)
303 NS_INTERFACE_MAP_ENTRY(nsITreeView
)
304 NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(XULTreeBuilder
)
305 NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder
)
308 nsXULTreeBuilder::nsXULTreeBuilder()
309 : mSortDirection(eDirection_Natural
)
314 nsXULTreeBuilder::Uninit(PRBool aIsFinal
)
316 PRInt32 count
= mRows
.Count();
320 mBoxObject
->BeginUpdateBatch();
321 mBoxObject
->RowCountChanged(0, -count
);
324 nsXULTemplateBuilder::Uninit(aIsFinal
);
328 //----------------------------------------------------------------------
330 // nsIXULTreeBuilder methods
334 nsXULTreeBuilder::GetResourceAtIndex(PRInt32 aRowIndex
, nsIRDFResource
** aResult
)
336 if (aRowIndex
< 0 || aRowIndex
>= mRows
.Count())
337 return NS_ERROR_INVALID_ARG
;
339 return GetResourceFor(aRowIndex
, aResult
);
343 nsXULTreeBuilder::GetIndexOfResource(nsIRDFResource
* aResource
, PRInt32
* aResult
)
345 NS_ENSURE_ARG_POINTER(aResource
);
346 nsTreeRows::iterator iter
= mRows
.FindByResource(aResource
);
347 if (iter
== mRows
.Last())
350 *aResult
= iter
.GetRowIndex();
355 nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver
* aObserver
)
359 rv
= NS_NewISupportsArray(getter_AddRefs(mObservers
));
364 return mObservers
->AppendElement(aObserver
);
368 nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver
* aObserver
)
370 return mObservers
? mObservers
->RemoveElement(aObserver
) : NS_ERROR_FAILURE
;
374 nsXULTreeBuilder::Sort(nsIDOMElement
* aElement
)
376 nsCOMPtr
<nsIContent
> header
= do_QueryInterface(aElement
);
378 return NS_ERROR_FAILURE
;
380 if (header
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::sortLocked
,
381 nsGkAtoms::_true
, eCaseMatters
))
385 header
->GetAttr(kNameSpaceID_None
, nsGkAtoms::sort
, sort
);
390 // Grab the new sort variable
391 mSortVariable
= do_GetAtom(sort
);
393 // Cycle the sort direction
395 header
->GetAttr(kNameSpaceID_None
, nsGkAtoms::sortDirection
, dir
);
397 if (dir
.EqualsLiteral("ascending")) {
398 dir
.AssignLiteral("descending");
399 mSortDirection
= eDirection_Descending
;
401 else if (dir
.EqualsLiteral("descending")) {
402 dir
.AssignLiteral("natural");
403 mSortDirection
= eDirection_Natural
;
406 dir
.AssignLiteral("ascending");
407 mSortDirection
= eDirection_Ascending
;
411 SortSubtree(mRows
.GetRoot());
412 mRows
.InvalidateCachedRow();
414 mBoxObject
->Invalidate();
416 nsTreeUtils::UpdateSortIndicators(header
, dir
);
421 //----------------------------------------------------------------------
423 // nsITreeView methods
427 nsXULTreeBuilder::GetRowCount(PRInt32
* aRowCount
)
429 *aRowCount
= mRows
.Count();
434 nsXULTreeBuilder::GetSelection(nsITreeSelection
** aSelection
)
436 NS_IF_ADDREF(*aSelection
= mSelection
.get());
441 nsXULTreeBuilder::SetSelection(nsITreeSelection
* aSelection
)
443 mSelection
= aSelection
;
448 nsXULTreeBuilder::GetRowProperties(PRInt32 aIndex
, nsISupportsArray
* aProperties
)
450 NS_ENSURE_ARG_POINTER(aProperties
);
451 NS_PRECONDITION(aIndex
>= 0 && aIndex
< mRows
.Count(), "bad row");
452 if (aIndex
< 0 || aIndex
>= mRows
.Count())
453 return NS_ERROR_INVALID_ARG
;
455 nsCOMPtr
<nsIContent
> row
;
456 GetTemplateActionRowFor(aIndex
, getter_AddRefs(row
));
459 row
->GetAttr(kNameSpaceID_None
, nsGkAtoms::properties
, raw
);
461 if (!raw
.IsEmpty()) {
463 SubstituteText(mRows
[aIndex
]->mMatch
->mResult
, raw
, cooked
);
465 nsTreeUtils::TokenizeProperties(cooked
, aProperties
);
473 nsXULTreeBuilder::GetCellProperties(PRInt32 aRow
, nsITreeColumn
* aCol
, nsISupportsArray
* aProperties
)
475 NS_ENSURE_ARG_POINTER(aCol
);
476 NS_ENSURE_ARG_POINTER(aProperties
);
477 NS_PRECONDITION(aRow
>= 0 && aRow
< mRows
.Count(), "bad row");
478 if (aRow
< 0 || aRow
>= mRows
.Count())
479 return NS_ERROR_INVALID_ARG
;
481 nsCOMPtr
<nsIContent
> cell
;
482 GetTemplateActionCellFor(aRow
, aCol
, getter_AddRefs(cell
));
485 cell
->GetAttr(kNameSpaceID_None
, nsGkAtoms::properties
, raw
);
487 if (!raw
.IsEmpty()) {
489 SubstituteText(mRows
[aRow
]->mMatch
->mResult
, raw
, cooked
);
491 nsTreeUtils::TokenizeProperties(cooked
, aProperties
);
499 nsXULTreeBuilder::GetColumnProperties(nsITreeColumn
* aCol
,
500 nsISupportsArray
* aProperties
)
502 NS_ENSURE_ARG_POINTER(aCol
);
503 NS_ENSURE_ARG_POINTER(aProperties
);
509 nsXULTreeBuilder::IsContainer(PRInt32 aIndex
, PRBool
* aResult
)
511 NS_PRECONDITION(aIndex
>= 0 && aIndex
< mRows
.Count(), "bad row");
512 if (aIndex
< 0 || aIndex
>= mRows
.Count())
513 return NS_ERROR_INVALID_ARG
;
515 nsTreeRows::iterator iter
= mRows
[aIndex
];
517 if (iter
->mContainerType
== nsTreeRows::eContainerType_Unknown
) {
519 iter
->mMatch
->mResult
->GetIsContainer(&isContainer
);
521 iter
->mContainerType
= isContainer
522 ? nsTreeRows::eContainerType_Container
523 : nsTreeRows::eContainerType_Noncontainer
;
526 *aResult
= (iter
->mContainerType
== nsTreeRows::eContainerType_Container
);
531 nsXULTreeBuilder::IsContainerOpen(PRInt32 aIndex
, PRBool
* aOpen
)
533 NS_PRECONDITION(aIndex
>= 0 && aIndex
< mRows
.Count(), "bad row");
534 if (aIndex
< 0 || aIndex
>= mRows
.Count())
535 return NS_ERROR_INVALID_ARG
;
537 nsTreeRows::iterator iter
= mRows
[aIndex
];
539 if (iter
->mContainerState
== nsTreeRows::eContainerState_Unknown
) {
541 IsContainerOpen(iter
->mMatch
->mResult
, &isOpen
);
543 iter
->mContainerState
= isOpen
544 ? nsTreeRows::eContainerState_Open
545 : nsTreeRows::eContainerState_Closed
;
548 *aOpen
= (iter
->mContainerState
== nsTreeRows::eContainerState_Open
);
553 nsXULTreeBuilder::IsContainerEmpty(PRInt32 aIndex
, PRBool
* aResult
)
555 NS_PRECONDITION(aIndex
>= 0 && aIndex
< mRows
.Count(), "bad row");
556 if (aIndex
< 0 || aIndex
>= mRows
.Count())
557 return NS_ERROR_INVALID_ARG
;
559 nsTreeRows::iterator iter
= mRows
[aIndex
];
560 NS_ASSERTION(iter
->mContainerType
== nsTreeRows::eContainerType_Container
,
561 "asking for empty state on non-container");
563 // if recursion is disabled, pretend that the container is empty. This
564 // ensures that folders are still displayed as such, yet won't display
566 if ((mFlags
& eDontRecurse
) && (iter
->mMatch
->mResult
!= mRootResult
)) {
571 if (iter
->mContainerFill
== nsTreeRows::eContainerFill_Unknown
) {
573 iter
->mMatch
->mResult
->GetIsEmpty(&isEmpty
);
575 iter
->mContainerFill
= isEmpty
576 ? nsTreeRows::eContainerFill_Empty
577 : nsTreeRows::eContainerFill_Nonempty
;
580 *aResult
= (iter
->mContainerFill
== nsTreeRows::eContainerFill_Empty
);
585 nsXULTreeBuilder::IsSeparator(PRInt32 aIndex
, PRBool
* aResult
)
587 NS_PRECONDITION(aIndex
>= 0 && aIndex
< mRows
.Count(), "bad row");
588 if (aIndex
< 0 || aIndex
>= mRows
.Count())
589 return NS_ERROR_INVALID_ARG
;
592 nsTreeRows::Row
& row
= *(mRows
[aIndex
]);
593 row
.mMatch
->mResult
->GetType(type
);
595 *aResult
= type
.EqualsLiteral("separator");
601 nsXULTreeBuilder::GetParentIndex(PRInt32 aRowIndex
, PRInt32
* aResult
)
603 NS_PRECONDITION(aRowIndex
>= 0 && aRowIndex
< mRows
.Count(), "bad row");
604 if (aRowIndex
< 0 || aRowIndex
>= mRows
.Count())
605 return NS_ERROR_INVALID_ARG
;
607 // Construct a path to the row
608 nsTreeRows::iterator iter
= mRows
[aRowIndex
];
610 // The parent of the row will be at the top of the path
611 nsTreeRows::Subtree
* parent
= iter
.GetParent();
613 // Now walk through our previous siblings, subtracting off each
614 // one's subtree size
615 PRInt32 index
= iter
.GetChildIndex();
617 aRowIndex
-= mRows
.GetSubtreeSizeFor(parent
, index
) + 1;
619 // Now the parent's index will be the first row's index, less one.
620 *aResult
= aRowIndex
- 1;
625 nsXULTreeBuilder::HasNextSibling(PRInt32 aRowIndex
, PRInt32 aAfterIndex
, PRBool
* aResult
)
627 NS_PRECONDITION(aRowIndex
>= 0 && aRowIndex
< mRows
.Count(), "bad row");
628 if (aRowIndex
< 0 || aRowIndex
>= mRows
.Count())
629 return NS_ERROR_INVALID_ARG
;
631 // Construct a path to the row
632 nsTreeRows::iterator iter
= mRows
[aRowIndex
];
634 // The parent of the row will be at the top of the path
635 nsTreeRows::Subtree
* parent
= iter
.GetParent();
637 // We have a next sibling if the child is not the last in the
639 *aResult
= iter
.GetChildIndex() != parent
->Count() - 1;
644 nsXULTreeBuilder::GetLevel(PRInt32 aRowIndex
, PRInt32
* aResult
)
646 NS_PRECONDITION(aRowIndex
>= 0 && aRowIndex
< mRows
.Count(), "bad row");
647 if (aRowIndex
< 0 || aRowIndex
>= mRows
.Count())
648 return NS_ERROR_INVALID_ARG
;
650 // Construct a path to the row; the ``level'' is the path length
652 nsTreeRows::iterator iter
= mRows
[aRowIndex
];
653 *aResult
= iter
.GetDepth() - 1;
658 nsXULTreeBuilder::GetImageSrc(PRInt32 aRow
, nsITreeColumn
* aCol
, nsAString
& aResult
)
660 NS_ENSURE_ARG_POINTER(aCol
);
661 NS_PRECONDITION(aRow
>= 0 && aRow
< mRows
.Count(), "bad index");
662 if (aRow
< 0 || aRow
>= mRows
.Count())
663 return NS_ERROR_INVALID_ARG
;
665 // Find the <cell> that corresponds to the column we want.
666 nsCOMPtr
<nsIContent
> cell
;
667 GetTemplateActionCellFor(aRow
, aCol
, getter_AddRefs(cell
));
670 cell
->GetAttr(kNameSpaceID_None
, nsGkAtoms::src
, raw
);
672 SubstituteText(mRows
[aRow
]->mMatch
->mResult
, raw
, aResult
);
682 nsXULTreeBuilder::GetProgressMode(PRInt32 aRow
, nsITreeColumn
* aCol
, PRInt32
* aResult
)
684 NS_ENSURE_ARG_POINTER(aCol
);
685 NS_PRECONDITION(aRow
>= 0 && aRow
< mRows
.Count(), "bad index");
686 if (aRow
< 0 || aRow
>= mRows
.Count())
687 return NS_ERROR_INVALID_ARG
;
689 *aResult
= nsITreeView::PROGRESS_NONE
;
691 // Find the <cell> that corresponds to the column we want.
692 nsCOMPtr
<nsIContent
> cell
;
693 GetTemplateActionCellFor(aRow
, aCol
, getter_AddRefs(cell
));
696 cell
->GetAttr(kNameSpaceID_None
, nsGkAtoms::mode
, raw
);
699 SubstituteText(mRows
[aRow
]->mMatch
->mResult
, raw
, mode
);
701 if (mode
.EqualsLiteral("normal"))
702 *aResult
= nsITreeView::PROGRESS_NORMAL
;
703 else if (mode
.EqualsLiteral("undetermined"))
704 *aResult
= nsITreeView::PROGRESS_UNDETERMINED
;
711 nsXULTreeBuilder::GetCellValue(PRInt32 aRow
, nsITreeColumn
* aCol
, nsAString
& aResult
)
713 NS_ENSURE_ARG_POINTER(aCol
);
714 NS_PRECONDITION(aRow
>= 0 && aRow
< mRows
.Count(), "bad index");
715 if (aRow
< 0 || aRow
>= mRows
.Count())
716 return NS_ERROR_INVALID_ARG
;
718 // Find the <cell> that corresponds to the column we want.
719 nsCOMPtr
<nsIContent
> cell
;
720 GetTemplateActionCellFor(aRow
, aCol
, getter_AddRefs(cell
));
723 cell
->GetAttr(kNameSpaceID_None
, nsGkAtoms::value
, raw
);
725 SubstituteText(mRows
[aRow
]->mMatch
->mResult
, raw
, aResult
);
734 nsXULTreeBuilder::GetCellText(PRInt32 aRow
, nsITreeColumn
* aCol
, nsAString
& aResult
)
736 NS_ENSURE_ARG_POINTER(aCol
);
737 NS_PRECONDITION(aRow
>= 0 && aRow
< mRows
.Count(), "bad index");
738 if (aRow
< 0 || aRow
>= mRows
.Count())
739 return NS_ERROR_INVALID_ARG
;
741 // Find the <cell> that corresponds to the column we want.
742 nsCOMPtr
<nsIContent
> cell
;
743 GetTemplateActionCellFor(aRow
, aCol
, getter_AddRefs(cell
));
746 cell
->GetAttr(kNameSpaceID_None
, nsGkAtoms::label
, raw
);
748 SubstituteText(mRows
[aRow
]->mMatch
->mResult
, raw
, aResult
);
758 nsXULTreeBuilder::SetTree(nsITreeBoxObject
* aTree
)
762 // If this is teardown time, then we're done.
767 NS_ENSURE_TRUE(mRoot
, NS_ERROR_NOT_INITIALIZED
);
769 // Is our root's principal trusted?
770 PRBool isTrusted
= PR_FALSE
;
771 nsresult rv
= IsSystemPrincipal(mRoot
->NodePrincipal(), &isTrusted
);
772 if (NS_SUCCEEDED(rv
) && isTrusted
) {
773 // Get the datasource we intend to use to remember open state.
774 nsAutoString datasourceStr
;
775 mRoot
->GetAttr(kNameSpaceID_None
, nsGkAtoms::statedatasource
, datasourceStr
);
777 // since we are trusted, use the user specified datasource
778 // if non specified, use localstore, which gives us
779 // persistence across sessions
780 if (! datasourceStr
.IsEmpty()) {
781 gRDFService
->GetDataSource(NS_ConvertUTF16toUTF8(datasourceStr
).get(),
782 getter_AddRefs(mPersistStateStore
));
785 gRDFService
->GetDataSource("rdf:local-store",
786 getter_AddRefs(mPersistStateStore
));
790 // Either no specific datasource was specified, or we failed
791 // to get one because we are not trusted.
793 // XXX if it were possible to ``write an arbitrary datasource
794 // back'', then we could also allow an untrusted document to
795 // use a statedatasource from the same codebase.
796 if (! mPersistStateStore
) {
798 do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource");
801 NS_ASSERTION(mPersistStateStore
, "failed to get a persistent state store");
802 if (! mPersistStateStore
)
803 return NS_ERROR_FAILURE
;
807 EnsureSortVariables();
809 SortSubtree(mRows
.GetRoot());
815 nsXULTreeBuilder::ToggleOpenState(PRInt32 aIndex
)
817 if (aIndex
< 0 || aIndex
>= mRows
.Count())
818 return NS_ERROR_INVALID_ARG
;
820 nsIXULTemplateResult
* result
= mRows
[aIndex
]->mMatch
->mResult
;
822 return NS_ERROR_FAILURE
;
824 if (mFlags
& eDontRecurse
)
827 if (result
&& result
!= mRootResult
) {
828 // don't open containers if child processing isn't allowed
829 PRBool mayProcessChildren
;
830 nsresult rv
= result
->GetMayProcessChildren(&mayProcessChildren
);
831 if (NS_FAILED(rv
) || !mayProcessChildren
)
837 mObservers
->Count(&count
);
838 for (PRUint32 i
= 0; i
< count
; ++i
) {
839 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
841 observer
->OnToggleOpenState(aIndex
);
845 if (mPersistStateStore
) {
847 IsContainerOpen(aIndex
, &isOpen
);
849 nsCOMPtr
<nsIRDFResource
> container
;
850 GetResourceFor(aIndex
, getter_AddRefs(container
));
852 return NS_ERROR_FAILURE
;
855 IsContainerOpen(container
, &hasProperty
);
859 mPersistStateStore
->Unassert(container
,
860 nsXULContentUtils::NC_open
,
861 nsXULContentUtils::true_
);
864 CloseContainer(aIndex
);
868 mPersistStateStore
->Assert(container
,
869 nsXULContentUtils::NC_open
,
870 nsXULContentUtils::true_
,
874 OpenContainer(aIndex
, result
);
882 nsXULTreeBuilder::CycleHeader(nsITreeColumn
* aCol
)
884 NS_ENSURE_ARG_POINTER(aCol
);
885 nsCOMPtr
<nsIDOMElement
> element
;
886 aCol
->GetElement(getter_AddRefs(element
));
893 mObservers
->Count(&count
);
894 for (PRUint32 i
= 0; i
< count
; ++i
) {
895 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
897 observer
->OnCycleHeader(id
.get(), element
);
901 return Sort(element
);
905 nsXULTreeBuilder::SelectionChanged()
909 mObservers
->Count(&count
);
910 for (PRUint32 i
= 0; i
< count
; ++i
) {
911 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
913 observer
->OnSelectionChanged();
921 nsXULTreeBuilder::CycleCell(PRInt32 aRow
, nsITreeColumn
* aCol
)
923 NS_ENSURE_ARG_POINTER(aCol
);
929 mObservers
->Count(&count
);
930 for (PRUint32 i
= 0; i
< count
; ++i
) {
931 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
933 observer
->OnCycleCell(aRow
, id
.get());
941 nsXULTreeBuilder::IsEditable(PRInt32 aRow
, nsITreeColumn
* aCol
, PRBool
* _retval
)
944 NS_ENSURE_ARG_POINTER(aCol
);
945 NS_PRECONDITION(aRow
>= 0 && aRow
< mRows
.Count(), "bad index");
946 if (aRow
< 0 || aRow
>= mRows
.Count())
947 return NS_ERROR_INVALID_ARG
;
949 // Find the <cell> that corresponds to the column we want.
950 nsCOMPtr
<nsIContent
> cell
;
951 GetTemplateActionCellFor(aRow
, aCol
, getter_AddRefs(cell
));
954 cell
->GetAttr(kNameSpaceID_None
, nsGkAtoms::editable
, raw
);
956 nsAutoString editable
;
957 SubstituteText(mRows
[aRow
]->mMatch
->mResult
, raw
, editable
);
959 if (editable
.EqualsLiteral("false"))
967 nsXULTreeBuilder::IsSelectable(PRInt32 aRow
, nsITreeColumn
* aCol
, PRBool
* _retval
)
969 NS_PRECONDITION(aRow
>= 0 && aRow
< mRows
.Count(), "bad index");
970 if (aRow
< 0 || aRow
>= mRows
.Count())
971 return NS_ERROR_INVALID_ARG
;
975 // Find the <cell> that corresponds to the column we want.
976 nsCOMPtr
<nsIContent
> cell
;
977 GetTemplateActionCellFor(aRow
, aCol
, getter_AddRefs(cell
));
980 cell
->GetAttr(kNameSpaceID_None
, nsGkAtoms::selectable
, raw
);
982 nsAutoString selectable
;
983 SubstituteText(mRows
[aRow
]->mMatch
->mResult
, raw
, selectable
);
985 if (selectable
.EqualsLiteral("false"))
993 nsXULTreeBuilder::SetCellValue(PRInt32 aRow
, nsITreeColumn
* aCol
, const nsAString
& aValue
)
995 NS_ENSURE_ARG_POINTER(aCol
);
1000 nsXULTreeBuilder::SetCellText(PRInt32 aRow
, nsITreeColumn
* aCol
, const nsAString
& aValue
)
1002 NS_ENSURE_ARG_POINTER(aCol
);
1007 nsXULTreeBuilder::PerformAction(const PRUnichar
* aAction
)
1011 mObservers
->Count(&count
);
1012 for (PRUint32 i
= 0; i
< count
; ++i
) {
1013 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
1015 observer
->OnPerformAction(aAction
);
1023 nsXULTreeBuilder::PerformActionOnRow(const PRUnichar
* aAction
, PRInt32 aRow
)
1027 mObservers
->Count(&count
);
1028 for (PRUint32 i
= 0; i
< count
; ++i
) {
1029 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
1031 observer
->OnPerformActionOnRow(aAction
, aRow
);
1039 nsXULTreeBuilder::PerformActionOnCell(const PRUnichar
* aAction
, PRInt32 aRow
, nsITreeColumn
* aCol
)
1041 NS_ENSURE_ARG_POINTER(aCol
);
1047 mObservers
->Count(&count
);
1048 for (PRUint32 i
= 0; i
< count
; ++i
) {
1049 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
1051 observer
->OnPerformActionOnCell(aAction
, aRow
, id
.get());
1060 nsXULTreeBuilder::NodeWillBeDestroyed(const nsINode
* aNode
)
1063 mObservers
->Clear();
1065 nsXULTemplateBuilder::NodeWillBeDestroyed(aNode
);
1069 nsXULTreeBuilder::HasGeneratedContent(nsIRDFResource
* aResource
,
1073 *aGenerated
= PR_FALSE
;
1074 NS_ENSURE_ARG_POINTER(aResource
);
1079 nsCOMPtr
<nsIRDFResource
> rootresource
;
1080 nsresult rv
= mRootResult
->GetResource(getter_AddRefs(rootresource
));
1084 if (aResource
== rootresource
||
1085 mRows
.FindByResource(aResource
) != mRows
.Last())
1086 *aGenerated
= PR_TRUE
;
1092 nsXULTreeBuilder::GetInsertionLocations(nsIXULTemplateResult
* aResult
,
1093 nsCOMArray
<nsIContent
>** aLocations
)
1095 *aLocations
= nsnull
;
1097 // Get the reference point and check if it is an open container. Rows
1098 // should not be generated otherwise.
1101 nsresult rv
= aResult
->GetBindingFor(mRefVariable
, ref
);
1102 if (NS_FAILED(rv
) || ref
.IsEmpty())
1105 nsCOMPtr
<nsIRDFResource
> container
;
1106 rv
= gRDFService
->GetUnicodeResource(ref
, getter_AddRefs(container
));
1110 // Can always insert into the root resource
1111 if (container
== mRows
.GetRootResource())
1114 nsTreeRows::iterator iter
= mRows
.FindByResource(container
);
1115 if (iter
== mRows
.Last())
1118 return (iter
->mContainerState
== nsTreeRows::eContainerState_Open
);
1122 nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult
* aOldResult
,
1123 nsTemplateMatch
* aNewMatch
,
1124 nsTemplateRule
* aNewMatchRule
,
1131 // Grovel through the rows looking for oldresult.
1132 nsTreeRows::iterator iter
= mRows
.Find(aOldResult
);
1134 NS_ASSERTION(iter
!= mRows
.Last(), "couldn't find row");
1135 if (iter
== mRows
.Last())
1136 return NS_ERROR_FAILURE
;
1138 // Remove the rows from the view
1139 PRInt32 row
= iter
.GetRowIndex();
1141 // If the row contains children, remove the matches from the
1142 // children so that they can be regenerated again if the element
1144 PRInt32 delta
= mRows
.GetSubtreeSizeFor(iter
);
1146 RemoveMatchesFor(*(iter
->mSubtree
));
1148 if (mRows
.RemoveRowAt(iter
) == 0 && iter
.GetRowIndex() >= 0) {
1150 // In this case iter now points to its parent
1151 // Invalidate the row's cached fill state
1152 iter
->mContainerFill
= nsTreeRows::eContainerFill_Unknown
;
1154 nsCOMPtr
<nsITreeColumns
> cols
;
1155 mBoxObject
->GetColumns(getter_AddRefs(cols
));
1157 nsCOMPtr
<nsITreeColumn
> primaryCol
;
1158 cols
->GetPrimaryColumn(getter_AddRefs(primaryCol
));
1160 mBoxObject
->InvalidateCell(iter
.GetRowIndex(), primaryCol
);
1164 // Notify the box object
1165 mBoxObject
->RowCountChanged(row
, -delta
- 1);
1168 if (aNewMatch
&& aNewMatch
->mResult
) {
1171 nsTreeRows::Subtree
* parent
= nsnull
;
1172 nsIXULTemplateResult
* result
= aNewMatch
->mResult
;
1175 nsresult rv
= result
->GetBindingFor(mRefVariable
, ref
);
1176 if (NS_FAILED(rv
) || ref
.IsEmpty())
1179 nsCOMPtr
<nsIRDFResource
> container
;
1180 rv
= gRDFService
->GetUnicodeResource(ref
, getter_AddRefs(container
));
1184 if (container
!= mRows
.GetRootResource()) {
1185 nsTreeRows::iterator iter
= mRows
.FindByResource(container
);
1186 row
= iter
.GetRowIndex();
1188 NS_ASSERTION(iter
!= mRows
.Last(), "couldn't find container row");
1189 if (iter
== mRows
.Last())
1190 return NS_ERROR_FAILURE
;
1192 // Use the persist store to remember if the container
1193 // is open or closed.
1194 PRBool open
= PR_FALSE
;
1195 IsContainerOpen(row
, &open
);
1197 // If it's open, make sure that we've got a subtree structure ready.
1199 parent
= mRows
.EnsureSubtreeFor(iter
);
1201 // We know something has just been inserted into the
1202 // container, so whether its open or closed, make sure
1203 // that we've got our tree row's state correct.
1204 if ((iter
->mContainerType
!= nsTreeRows::eContainerType_Container
) ||
1205 (iter
->mContainerFill
!= nsTreeRows::eContainerFill_Nonempty
)) {
1206 iter
->mContainerType
= nsTreeRows::eContainerType_Container
;
1207 iter
->mContainerFill
= nsTreeRows::eContainerFill_Nonempty
;
1208 mBoxObject
->InvalidateRow(iter
.GetRowIndex());
1212 parent
= mRows
.GetRoot();
1216 // If we get here, then we're inserting into an open
1217 // container. By default, place the new element at the
1218 // end of the container
1219 PRInt32 index
= parent
->Count();
1221 if (mSortVariable
) {
1222 // Figure out where to put the new element by doing an
1225 PRInt32 right
= index
;
1227 while (left
< right
) {
1228 index
= (left
+ right
) / 2;
1229 PRInt32 cmp
= CompareResults((*parent
)[index
].mMatch
->mResult
, result
);
1239 nsTreeRows::iterator iter
=
1240 mRows
.InsertRowAt(aNewMatch
, parent
, index
);
1242 mBoxObject
->RowCountChanged(iter
.GetRowIndex(), +1);
1244 // See if this newly added row is open; in which case,
1245 // recursively add its children to the tree, too.
1247 if (mFlags
& eDontRecurse
)
1250 if (result
&& (result
!= mRootResult
)) {
1251 // don't open containers if child processing isn't allowed
1252 PRBool mayProcessChildren
;
1253 nsresult rv
= result
->GetMayProcessChildren(&mayProcessChildren
);
1254 if (NS_FAILED(rv
) || ! mayProcessChildren
) return NS_OK
;
1258 IsContainerOpen(result
, &open
);
1260 OpenContainer(iter
.GetRowIndex(), result
);
1268 nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult
* aResult
)
1271 // XXX we could be more conservative and just invalidate the cells
1272 // that got whacked...
1274 nsTreeRows::iterator iter
= mRows
.Find(aResult
);
1276 NS_ASSERTION(iter
!= mRows
.Last(), "couldn't find row");
1277 if (iter
== mRows
.Last())
1278 return NS_ERROR_FAILURE
;
1280 PRInt32 row
= iter
.GetRowIndex();
1282 mBoxObject
->InvalidateRow(row
);
1284 PR_LOG(gXULTemplateLog
, PR_LOG_DEBUG
,
1285 ("xultemplate[%p] => row %d", this, row
));
1291 //----------------------------------------------------------------------
1294 nsXULTreeBuilder::EnsureSortVariables()
1296 // Grovel through <treecols> kids to find the <treecol>
1297 // with the sort attributes.
1298 nsCOMPtr
<nsIContent
> treecols
;
1300 nsXULContentUtils::FindChildByTag(mRoot
, kNameSpaceID_XUL
,
1301 nsGkAtoms::treecols
,
1302 getter_AddRefs(treecols
));
1307 PRUint32 count
= treecols
->GetChildCount();
1308 for (PRUint32 i
= 0; i
< count
; ++i
) {
1309 nsIContent
*child
= treecols
->GetChildAt(i
);
1311 if (child
->NodeInfo()->Equals(nsGkAtoms::treecol
,
1312 kNameSpaceID_XUL
)) {
1313 if (child
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::sortActive
,
1314 nsGkAtoms::_true
, eCaseMatters
)) {
1316 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::sort
, sort
);
1317 if (! sort
.IsEmpty()) {
1318 mSortVariable
= do_GetAtom(sort
);
1320 static nsIContent::AttrValuesArray strings
[] =
1321 {&nsGkAtoms::ascending
, &nsGkAtoms::descending
, nsnull
};
1322 switch (child
->FindAttrValueIn(kNameSpaceID_None
,
1323 nsGkAtoms::sortDirection
,
1324 strings
, eCaseMatters
)) {
1325 case 0: mSortDirection
= eDirection_Ascending
; break;
1326 case 1: mSortDirection
= eDirection_Descending
; break;
1327 default: mSortDirection
= eDirection_Natural
; break;
1339 nsXULTreeBuilder::RebuildAll()
1341 NS_ENSURE_TRUE(mRoot
, NS_ERROR_NOT_INITIALIZED
);
1343 nsCOMPtr
<nsIDocument
> doc
= mRoot
->GetDocument();
1345 // Bail out early if we are being torn down.
1349 if (! mQueryProcessor
)
1352 if (mQueriesCompiled
) {
1355 else if (mBoxObject
) {
1356 PRInt32 count
= mRows
.Count();
1358 mBoxObject
->BeginUpdateBatch();
1359 mBoxObject
->RowCountChanged(0, -count
);
1362 nsresult rv
= CompileQueries();
1366 if (mQuerySets
.Length() == 0)
1369 // Seed the rule network with assignments for the tree row variable
1371 mRoot
->GetAttr(kNameSpaceID_None
, nsGkAtoms::ref
, ref
);
1373 if (! ref
.IsEmpty()) {
1374 rv
= mQueryProcessor
->TranslateRef(mDataSource
, ref
,
1375 getter_AddRefs(mRootResult
));
1380 OpenContainer(-1, mRootResult
);
1382 nsCOMPtr
<nsIRDFResource
> rootResource
;
1383 GetResultResource(mRootResult
, getter_AddRefs(rootResource
));
1385 mRows
.SetRootResource(rootResource
);
1390 mBoxObject
->EndUpdateBatch();
1397 nsXULTreeBuilder::GetTemplateActionRowFor(PRInt32 aRow
, nsIContent
** aResult
)
1399 // Get the template in the DOM from which we're supposed to
1401 nsTreeRows::Row
& row
= *(mRows
[aRow
]);
1403 // The match stores the indices of the rule and query to use. Use these
1404 // to look up the right nsTemplateRule and use that rule's action to get
1405 // the treerow in the template.
1406 PRInt16 ruleindex
= row
.mMatch
->RuleIndex();
1407 if (ruleindex
>= 0) {
1408 nsTemplateQuerySet
* qs
= mQuerySets
[row
.mMatch
->QuerySetPriority()];
1409 nsTemplateRule
* rule
= qs
->GetRuleAt(ruleindex
);
1411 nsCOMPtr
<nsIContent
> children
;
1412 nsXULContentUtils::FindChildByTag(rule
->GetAction(), kNameSpaceID_XUL
,
1413 nsGkAtoms::treechildren
,
1414 getter_AddRefs(children
));
1416 nsCOMPtr
<nsIContent
> item
;
1417 nsXULContentUtils::FindChildByTag(children
, kNameSpaceID_XUL
,
1418 nsGkAtoms::treeitem
,
1419 getter_AddRefs(item
));
1421 return nsXULContentUtils::FindChildByTag(item
,
1434 nsXULTreeBuilder::GetTemplateActionCellFor(PRInt32 aRow
,
1435 nsITreeColumn
* aCol
,
1436 nsIContent
** aResult
)
1440 if (!aCol
) return NS_ERROR_INVALID_ARG
;
1442 nsCOMPtr
<nsIContent
> row
;
1443 GetTemplateActionRowFor(aRow
, getter_AddRefs(row
));
1445 nsCOMPtr
<nsIAtom
> colAtom
;
1447 aCol
->GetAtom(getter_AddRefs(colAtom
));
1448 aCol
->GetIndex(&colIndex
);
1450 PRUint32 count
= row
->GetChildCount();
1452 for (PRUint32 i
= 0; i
< count
; ++i
) {
1453 nsIContent
*child
= row
->GetChildAt(i
);
1455 if (child
->NodeInfo()->Equals(nsGkAtoms::treecell
,
1456 kNameSpaceID_XUL
)) {
1458 child
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::ref
,
1459 colAtom
, eCaseMatters
)) {
1463 else if (j
== (PRUint32
)colIndex
)
1469 NS_IF_ADDREF(*aResult
);
1475 nsXULTreeBuilder::GetResourceFor(PRInt32 aRow
, nsIRDFResource
** aResource
)
1477 nsTreeRows::Row
& row
= *(mRows
[aRow
]);
1478 return GetResultResource(row
.mMatch
->mResult
, aResource
);
1482 nsXULTreeBuilder::OpenContainer(PRInt32 aIndex
, nsIXULTemplateResult
* aResult
)
1484 // A row index of -1 in this case means ``open tree body''
1485 NS_ASSERTION(aIndex
>= -1 && aIndex
< mRows
.Count(), "bad row");
1486 if (aIndex
< -1 || aIndex
>= mRows
.Count())
1487 return NS_ERROR_INVALID_ARG
;
1489 nsTreeRows::Subtree
* container
;
1492 nsTreeRows::iterator iter
= mRows
[aIndex
];
1493 container
= mRows
.EnsureSubtreeFor(iter
.GetParent(),
1494 iter
.GetChildIndex());
1496 iter
->mContainerState
= nsTreeRows::eContainerState_Open
;
1499 container
= mRows
.GetRoot();
1502 return NS_ERROR_OUT_OF_MEMORY
;
1505 OpenSubtreeOf(container
, aIndex
, aResult
, &count
);
1507 // Notify the box object
1510 mBoxObject
->InvalidateRow(aIndex
);
1513 mBoxObject
->RowCountChanged(aIndex
+ 1, count
);
1520 nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree
* aSubtree
,
1522 nsIXULTemplateResult
*aResult
,
1525 nsAutoVoidArray open
;
1528 PRInt32 rulecount
= mQuerySets
.Length();
1530 for (PRInt32 r
= 0; r
< rulecount
; r
++) {
1531 nsTemplateQuerySet
* queryset
= mQuerySets
[r
];
1532 OpenSubtreeForQuerySet(aSubtree
, aIndex
, aResult
, queryset
, &count
, open
);
1535 // Now recursively deal with any open sub-containers that just got
1536 // inserted. We need to do this back-to-front to avoid skewing offsets.
1537 for (PRInt32 i
= open
.Count() - 1; i
>= 0; --i
) {
1538 PRInt32 index
= NS_PTR_TO_INT32(open
[i
]);
1540 nsTreeRows::Subtree
* child
=
1541 mRows
.EnsureSubtreeFor(aSubtree
, index
);
1543 nsIXULTemplateResult
* result
= (*aSubtree
)[index
].mMatch
->mResult
;
1546 OpenSubtreeOf(child
, aIndex
+ index
, result
, &delta
);
1550 // Sort the container.
1551 if (mSortVariable
) {
1552 NS_QuickSort(mRows
.GetRowsFor(aSubtree
),
1554 sizeof(nsTreeRows::Row
),
1564 nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree
* aSubtree
,
1566 nsIXULTemplateResult
* aResult
,
1567 nsTemplateQuerySet
* aQuerySet
,
1569 nsAutoVoidArray
& open
)
1571 PRInt32 count
= *aDelta
;
1573 nsCOMPtr
<nsISimpleEnumerator
> results
;
1574 nsresult rv
= mQueryProcessor
->GenerateResults(mDataSource
, aResult
,
1575 aQuerySet
->mCompiledQuery
,
1576 getter_AddRefs(results
));
1580 PRBool hasMoreResults
;
1581 rv
= results
->HasMoreElements(&hasMoreResults
);
1583 for (; NS_SUCCEEDED(rv
) && hasMoreResults
;
1584 rv
= results
->HasMoreElements(&hasMoreResults
)) {
1585 nsCOMPtr
<nsISupports
> nr
;
1586 rv
= results
->GetNext(getter_AddRefs(nr
));
1590 nsCOMPtr
<nsIXULTemplateResult
> nextresult
= do_QueryInterface(nr
);
1592 return NS_ERROR_UNEXPECTED
;
1594 nsCOMPtr
<nsIRDFResource
> resultid
;
1595 rv
= GetResultResource(nextresult
, getter_AddRefs(resultid
));
1602 // check if there is already an existing match. If so, a previous
1603 // query already generated content so the match is just added to the
1604 // end of the set of matches.
1606 PRBool generateContent
= PR_TRUE
;
1608 nsTemplateMatch
* prevmatch
= nsnull
;
1609 nsTemplateMatch
* existingmatch
= nsnull
;
1610 if (mMatchMap
.Get(resultid
, &existingmatch
)){
1611 // check if there is an existing match that matched a rule
1612 while (existingmatch
) {
1613 if (existingmatch
->IsActive())
1614 generateContent
= PR_FALSE
;
1615 prevmatch
= existingmatch
;
1616 existingmatch
= existingmatch
->mNext
;
1620 nsTemplateMatch
*newmatch
=
1621 nsTemplateMatch::Create(mPool
, aQuerySet
->Priority(),
1622 nextresult
, nsnull
);
1624 return NS_ERROR_OUT_OF_MEMORY
;
1626 if (generateContent
) {
1627 // Don't allow cyclic graphs to get our knickers in a knot.
1628 PRBool cyclic
= PR_FALSE
;
1631 for (nsTreeRows::iterator iter
= mRows
[aIndex
]; iter
.GetDepth() > 0; iter
.Pop()) {
1632 nsCOMPtr
<nsIRDFResource
> parentid
;
1633 rv
= GetResultResource(iter
->mMatch
->mResult
, getter_AddRefs(parentid
));
1634 if (NS_FAILED(rv
)) {
1635 nsTemplateMatch::Destroy(mPool
, newmatch
, PR_FALSE
);
1639 if (resultid
== parentid
) {
1647 NS_WARNING("tree cannot handle cyclic graphs");
1648 nsTemplateMatch::Destroy(mPool
, newmatch
, PR_FALSE
);
1653 nsTemplateRule
* matchedrule
= nsnull
;
1654 rv
= DetermineMatchedRule(nsnull
, nextresult
, aQuerySet
,
1655 &matchedrule
, &ruleindex
);
1656 if (NS_FAILED(rv
)) {
1657 nsTemplateMatch::Destroy(mPool
, newmatch
, PR_FALSE
);
1662 rv
= newmatch
->RuleMatched(aQuerySet
, matchedrule
, ruleindex
,
1664 if (NS_FAILED(rv
)) {
1665 nsTemplateMatch::Destroy(mPool
, newmatch
, PR_FALSE
);
1669 // Remember that this match applied to this row
1670 mRows
.InsertRowAt(newmatch
, aSubtree
, count
);
1672 // If this is open, then remember it so we can recursively add
1673 // *its* rows to the tree.
1674 PRBool isOpen
= PR_FALSE
;
1675 IsContainerOpen(nextresult
, &isOpen
);
1677 if (!open
.AppendElement(NS_INT32_TO_PTR(count
)))
1678 return NS_ERROR_OUT_OF_MEMORY
;
1686 prevmatch
->mNext
= newmatch
;
1688 else if (!mMatchMap
.Put(resultid
, newmatch
)) {
1689 nsTemplateMatch::Destroy(mPool
, newmatch
, PR_TRUE
);
1690 return NS_ERROR_OUT_OF_MEMORY
;
1699 nsXULTreeBuilder::CloseContainer(PRInt32 aIndex
)
1701 NS_ASSERTION(aIndex
>= 0 && aIndex
< mRows
.Count(), "bad row");
1702 if (aIndex
< 0 || aIndex
>= mRows
.Count())
1703 return NS_ERROR_INVALID_ARG
;
1705 nsTreeRows::iterator iter
= mRows
[aIndex
];
1707 nsTreeRows::Subtree
& subtree
= *(iter
->mSubtree
);
1709 RemoveMatchesFor(subtree
);
1712 iter
= mRows
[aIndex
];
1714 PRInt32 count
= mRows
.GetSubtreeSizeFor(iter
);
1715 mRows
.RemoveSubtreeFor(iter
);
1717 iter
->mContainerState
= nsTreeRows::eContainerState_Closed
;
1720 mBoxObject
->InvalidateRow(aIndex
);
1723 mBoxObject
->RowCountChanged(aIndex
+ 1, -count
);
1730 nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree
& subtree
)
1732 for (PRInt32 i
= subtree
.Count() - 1; i
>= 0; --i
) {
1733 nsTreeRows::Row
& row
= subtree
[i
];
1735 nsTemplateMatch
* match
= row
.mMatch
;
1737 nsCOMPtr
<nsIRDFResource
> id
;
1738 nsresult rv
= GetResultResource(match
->mResult
, getter_AddRefs(id
));
1742 nsTemplateMatch
* existingmatch
;
1743 if (mMatchMap
.Get(id
, &existingmatch
)) {
1744 while (existingmatch
) {
1745 nsTemplateMatch
* nextmatch
= existingmatch
->mNext
;
1746 nsTemplateMatch::Destroy(mPool
, existingmatch
, PR_TRUE
);
1747 existingmatch
= nextmatch
;
1750 mMatchMap
.Remove(id
);
1753 if ((row
.mContainerState
== nsTreeRows::eContainerState_Open
) && row
.mSubtree
)
1754 RemoveMatchesFor(*(row
.mSubtree
));
1761 nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult
*aResult
, PRBool
* aOpen
)
1763 // items are never open if recursion is disabled
1764 if ((mFlags
& eDontRecurse
) && aResult
!= mRootResult
) {
1769 nsCOMPtr
<nsIRDFResource
> id
;
1770 nsresult rv
= GetResultResource(aResult
, getter_AddRefs(id
));
1774 return IsContainerOpen(id
, aOpen
);
1778 nsXULTreeBuilder::IsContainerOpen(nsIRDFResource
* aResource
, PRBool
* aOpen
)
1780 if (mPersistStateStore
)
1781 mPersistStateStore
->HasAssertion(aResource
,
1782 nsXULContentUtils::NC_open
,
1783 nsXULContentUtils::true_
,
1793 nsXULTreeBuilder::Compare(const void* aLeft
, const void* aRight
, void* aClosure
)
1795 nsXULTreeBuilder
* self
= static_cast<nsXULTreeBuilder
*>(aClosure
);
1797 nsTreeRows::Row
* left
= static_cast<nsTreeRows::Row
*>
1798 (const_cast<void*>(aLeft
));
1800 nsTreeRows::Row
* right
= static_cast<nsTreeRows::Row
*>
1801 (const_cast<void*>(aRight
));
1803 return self
->CompareResults(left
->mMatch
->mResult
, right
->mMatch
->mResult
);
1807 nsXULTreeBuilder::CompareResults(nsIXULTemplateResult
* aLeft
, nsIXULTemplateResult
* aRight
)
1809 // this is an extra check done for RDF queries such that results appear in
1810 // the order they appear in their containing Seq
1811 if (mSortDirection
== eDirection_Natural
&& mDB
) {
1812 // If the sort order is ``natural'', then see if the container
1813 // is an RDF sequence. If so, we'll try to use the ordinal
1814 // properties to determine order.
1816 // XXX the problem with this is, it doesn't always get the
1817 // *real* container; e.g.,
1819 // <treerow uri="?uri" />
1821 // <triple subject="?uri"
1822 // predicate="http://home.netscape.com/NC-rdf#subheadings"
1823 // object="?subheadings" />
1825 // <member container="?subheadings" child="?subheading" />
1827 // In this case mRefVariable is bound to ?uri, not
1828 // ?subheadings. (The ``container'' in the template sense !=
1829 // container in the RDF sense.)
1831 nsCOMPtr
<nsISupports
> ref
;
1832 nsresult rv
= aLeft
->GetBindingObjectFor(mRefVariable
, getter_AddRefs(ref
));
1836 nsCOMPtr
<nsIRDFResource
> container
= do_QueryInterface(ref
);
1838 PRBool isSequence
= PR_FALSE
;
1839 gRDFContainerUtils
->IsSeq(mDB
, container
, &isSequence
);
1841 // Determine the indices of the left and right elements
1842 // in the container.
1843 PRInt32 lindex
= 0, rindex
= 0;
1845 nsCOMPtr
<nsIRDFResource
> leftitem
;
1846 aLeft
->GetResource(getter_AddRefs(leftitem
));
1848 gRDFContainerUtils
->IndexOf(mDB
, container
, leftitem
, &lindex
);
1853 nsCOMPtr
<nsIRDFResource
> rightitem
;
1854 aRight
->GetResource(getter_AddRefs(rightitem
));
1856 gRDFContainerUtils
->IndexOf(mDB
, container
, rightitem
, &rindex
);
1861 return lindex
- rindex
;
1867 mQueryProcessor
->CompareResults(aLeft
, aRight
, mSortVariable
, &sortorder
);
1870 sortorder
= sortorder
* mSortDirection
;
1875 nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree
* aSubtree
)
1877 NS_QuickSort(mRows
.GetRowsFor(aSubtree
),
1879 sizeof(nsTreeRows::Row
),
1883 for (PRInt32 i
= aSubtree
->Count() - 1; i
>= 0; --i
) {
1884 nsTreeRows::Subtree
* child
= (*aSubtree
)[i
].mSubtree
;
1893 /* boolean canDrop (in long index, in long orientation); */
1895 nsXULTreeBuilder::CanDrop(PRInt32 index
, PRInt32 orientation
, PRBool
*_retval
)
1897 *_retval
= PR_FALSE
;
1900 mObservers
->Count(&count
);
1901 for (PRUint32 i
= 0; i
< count
; ++i
) {
1902 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
1904 observer
->CanDrop(index
, orientation
, _retval
);
1915 nsXULTreeBuilder::Drop(PRInt32 row
, PRInt32 orient
)
1919 mObservers
->Count(&count
);
1920 for (PRUint32 i
= 0; i
< count
; ++i
) {
1921 nsCOMPtr
<nsIXULTreeBuilderObserver
> observer
= do_QueryElementAt(mObservers
, i
);
1923 PRBool canDrop
= PR_FALSE
;
1924 observer
->CanDrop(row
, orient
, &canDrop
);
1926 observer
->OnDrop(row
, orient
);
1935 nsXULTreeBuilder::IsSorted(PRBool
*_retval
)
1937 *_retval
= (mSortVariable
!= nsnull
);