Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / content / xul / templates / src / nsXULTreeBuilder.cpp
blob4d254eb602c48a3ab8f3ebc1e9ee2ef255f67847
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
42 #include "nscore.h"
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"
67 // For security check
68 #include "nsIDocument.h"
70 /**
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
78 public:
79 // nsISupports
80 NS_DECL_ISUPPORTS_INHERITED
82 // nsIXULTreeBuilder
83 NS_DECL_NSIXULTREEBUILDER
85 // nsITreeView
86 NS_DECL_NSITREEVIEW
87 // nsINativeTreeView: Untrusted code can use us
88 NS_IMETHOD EnsureNative() { return NS_OK; }
90 virtual void NodeWillBeDestroyed(const nsINode* aNode);
92 protected:
93 friend NS_IMETHODIMP
94 NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
96 nsXULTreeBuilder();
98 /**
99 * Uninitialize the template builder
101 virtual void Uninit(PRBool aIsFinal);
104 * Get sort variables from the active <treecol>
106 nsresult
107 EnsureSortVariables();
109 virtual nsresult
110 RebuildAll();
113 * Given a row, use the row's match to figure out the appropriate
114 * <treerow> in the rule's <action>.
116 nsresult
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>.
123 nsresult
124 GetTemplateActionCellFor(PRInt32 aRow, nsITreeColumn* aCol, nsIContent** aResult);
127 * Return the resource corresponding to a row in the tree.
129 nsresult
130 GetResourceFor(PRInt32 aRow, nsIRDFResource** aResource);
133 * Open a container row, inserting the container's children into
134 * the view.
136 nsresult
137 OpenContainer(PRInt32 aIndex, nsIXULTemplateResult* aResult);
140 * Helper for OpenContainer, recursively open subtrees, remembering
141 * persisted ``open'' state
143 nsresult
144 OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
145 PRInt32 aIndex,
146 nsIXULTemplateResult *aResult,
147 PRInt32* aDelta);
149 nsresult
150 OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
151 PRInt32 aIndex,
152 nsIXULTemplateResult *aResult,
153 nsTemplateQuerySet* aQuerySet,
154 PRInt32* aDelta,
155 nsAutoVoidArray& open);
158 * Close a container row, removing the container's childrem from
159 * the view.
161 nsresult
162 CloseContainer(PRInt32 aIndex);
165 * Remove the matches for the rows in a subtree
167 nsresult
168 RemoveMatchesFor(nsTreeRows::Subtree& subtree);
171 * Helper methods that determine if the specified container is open.
173 nsresult
174 IsContainerOpen(nsIXULTemplateResult *aResult, PRBool* aOpen);
176 nsresult
177 IsContainerOpen(nsIRDFResource* aResource, PRBool* aOpen);
180 * A sorting callback for NS_QuickSort().
182 static int
183 Compare(const void* aLeft, const void* aRight, void* aClosure);
186 * The real sort routine
188 PRInt32
189 CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight);
192 * Sort the specified subtree, and recursively sort any subtrees
193 * beneath it.
195 nsresult
196 SortSubtree(nsTreeRows::Subtree* aSubtree);
198 NS_IMETHOD
199 HasGeneratedContent(nsIRDFResource* aResource,
200 nsIAtom* aTag,
201 PRBool* aGenerated);
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
208 * row.
210 PRBool
211 GetInsertionLocations(nsIXULTemplateResult* aResult,
212 nsCOMArray<nsIContent>** aLocations);
215 * Implement result replacement
217 virtual nsresult
218 ReplaceMatch(nsIXULTemplateResult* aOldResult,
219 nsTemplateMatch* aNewMatch,
220 nsTemplateRule* aNewMatchRule,
221 void *aContext);
224 * Implement match synchronization
226 virtual nsresult
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
247 nsTreeRows mRows;
250 * The currently active sort variable
252 nsCOMPtr<nsIAtom> mSortVariable;
254 enum Direction {
255 eDirection_Descending = -1,
256 eDirection_Natural = 0,
257 eDirection_Ascending = +1
261 * The currently active sort order
263 Direction mSortDirection;
265 /**
266 * The builder observers.
268 nsCOMPtr<nsISupportsArray> mObservers;
271 //----------------------------------------------------------------------
273 NS_IMETHODIMP
274 NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
276 *aResult = nsnull;
278 NS_PRECONDITION(aOuter == nsnull, "no aggregation");
279 if (aOuter)
280 return NS_ERROR_NO_AGGREGATION;
282 nsresult rv;
283 nsXULTreeBuilder* result = new nsXULTreeBuilder();
284 if (! result)
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);
294 NS_RELEASE(result);
295 return rv;
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)
313 void
314 nsXULTreeBuilder::Uninit(PRBool aIsFinal)
316 PRInt32 count = mRows.Count();
317 mRows.Clear();
319 if (mBoxObject) {
320 mBoxObject->BeginUpdateBatch();
321 mBoxObject->RowCountChanged(0, -count);
324 nsXULTemplateBuilder::Uninit(aIsFinal);
328 //----------------------------------------------------------------------
330 // nsIXULTreeBuilder methods
333 NS_IMETHODIMP
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);
342 NS_IMETHODIMP
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())
348 *aResult = -1;
349 else
350 *aResult = iter.GetRowIndex();
351 return NS_OK;
354 NS_IMETHODIMP
355 nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver* aObserver)
357 nsresult rv;
358 if (!mObservers) {
359 rv = NS_NewISupportsArray(getter_AddRefs(mObservers));
360 if (NS_FAILED(rv))
361 return rv;
364 return mObservers->AppendElement(aObserver);
367 NS_IMETHODIMP
368 nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver* aObserver)
370 return mObservers ? mObservers->RemoveElement(aObserver) : NS_ERROR_FAILURE;
373 NS_IMETHODIMP
374 nsXULTreeBuilder::Sort(nsIDOMElement* aElement)
376 nsCOMPtr<nsIContent> header = do_QueryInterface(aElement);
377 if (! header)
378 return NS_ERROR_FAILURE;
380 if (header->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortLocked,
381 nsGkAtoms::_true, eCaseMatters))
382 return NS_OK;
384 nsAutoString sort;
385 header->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
387 if (sort.IsEmpty())
388 return NS_OK;
390 // Grab the new sort variable
391 mSortVariable = do_GetAtom(sort);
393 // Cycle the sort direction
394 nsAutoString dir;
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;
405 else {
406 dir.AssignLiteral("ascending");
407 mSortDirection = eDirection_Ascending;
410 // Sort it.
411 SortSubtree(mRows.GetRoot());
412 mRows.InvalidateCachedRow();
413 if (mBoxObject)
414 mBoxObject->Invalidate();
416 nsTreeUtils::UpdateSortIndicators(header, dir);
418 return NS_OK;
421 //----------------------------------------------------------------------
423 // nsITreeView methods
426 NS_IMETHODIMP
427 nsXULTreeBuilder::GetRowCount(PRInt32* aRowCount)
429 *aRowCount = mRows.Count();
430 return NS_OK;
433 NS_IMETHODIMP
434 nsXULTreeBuilder::GetSelection(nsITreeSelection** aSelection)
436 NS_IF_ADDREF(*aSelection = mSelection.get());
437 return NS_OK;
440 NS_IMETHODIMP
441 nsXULTreeBuilder::SetSelection(nsITreeSelection* aSelection)
443 mSelection = aSelection;
444 return NS_OK;
447 NS_IMETHODIMP
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));
457 if (row) {
458 nsAutoString raw;
459 row->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
461 if (!raw.IsEmpty()) {
462 nsAutoString cooked;
463 SubstituteText(mRows[aIndex]->mMatch->mResult, raw, cooked);
465 nsTreeUtils::TokenizeProperties(cooked, aProperties);
469 return NS_OK;
472 NS_IMETHODIMP
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));
483 if (cell) {
484 nsAutoString raw;
485 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
487 if (!raw.IsEmpty()) {
488 nsAutoString cooked;
489 SubstituteText(mRows[aRow]->mMatch->mResult, raw, cooked);
491 nsTreeUtils::TokenizeProperties(cooked, aProperties);
495 return NS_OK;
498 NS_IMETHODIMP
499 nsXULTreeBuilder::GetColumnProperties(nsITreeColumn* aCol,
500 nsISupportsArray* aProperties)
502 NS_ENSURE_ARG_POINTER(aCol);
503 NS_ENSURE_ARG_POINTER(aProperties);
504 // XXX sortactive fu
505 return NS_OK;
508 NS_IMETHODIMP
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) {
518 PRBool isContainer;
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);
527 return NS_OK;
530 NS_IMETHODIMP
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) {
540 PRBool isOpen;
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);
549 return NS_OK;
552 NS_IMETHODIMP
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
565 // their children
566 if ((mFlags & eDontRecurse) && (iter->mMatch->mResult != mRootResult)) {
567 *aResult = PR_TRUE;
568 return NS_OK;
571 if (iter->mContainerFill == nsTreeRows::eContainerFill_Unknown) {
572 PRBool isEmpty;
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);
581 return NS_OK;
584 NS_IMETHODIMP
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;
591 nsAutoString type;
592 nsTreeRows::Row& row = *(mRows[aIndex]);
593 row.mMatch->mResult->GetType(type);
595 *aResult = type.EqualsLiteral("separator");
597 return NS_OK;
600 NS_IMETHODIMP
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();
616 while (--index >= 0)
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;
621 return NS_OK;
624 NS_IMETHODIMP
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
638 // subtree.
639 *aResult = iter.GetChildIndex() != parent->Count() - 1;
640 return NS_OK;
643 NS_IMETHODIMP
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
651 // less one.
652 nsTreeRows::iterator iter = mRows[aRowIndex];
653 *aResult = iter.GetDepth() - 1;
654 return NS_OK;
657 NS_IMETHODIMP
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));
668 if (cell) {
669 nsAutoString raw;
670 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, raw);
672 SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
674 else
675 aResult.Truncate();
677 return NS_OK;
681 NS_IMETHODIMP
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));
694 if (cell) {
695 nsAutoString raw;
696 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::mode, raw);
698 nsAutoString mode;
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;
707 return NS_OK;
710 NS_IMETHODIMP
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));
721 if (cell) {
722 nsAutoString raw;
723 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, raw);
725 SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
727 else
728 aResult.Truncate();
730 return NS_OK;
733 NS_IMETHODIMP
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));
744 if (cell) {
745 nsAutoString raw;
746 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, raw);
748 SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
751 else
752 aResult.Truncate();
754 return NS_OK;
757 NS_IMETHODIMP
758 nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree)
760 mBoxObject = aTree;
762 // If this is teardown time, then we're done.
763 if (!mBoxObject) {
764 Uninit(PR_FALSE);
765 return NS_OK;
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));
784 else {
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) {
797 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;
805 Rebuild();
807 EnsureSortVariables();
808 if (mSortVariable)
809 SortSubtree(mRows.GetRoot());
811 return NS_OK;
814 NS_IMETHODIMP
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;
821 if (! result)
822 return NS_ERROR_FAILURE;
824 if (mFlags & eDontRecurse)
825 return NS_OK;
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)
832 return rv;
835 if (mObservers) {
836 PRUint32 count;
837 mObservers->Count(&count);
838 for (PRUint32 i = 0; i < count; ++i) {
839 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
840 if (observer)
841 observer->OnToggleOpenState(aIndex);
845 if (mPersistStateStore) {
846 PRBool isOpen;
847 IsContainerOpen(aIndex, &isOpen);
849 nsCOMPtr<nsIRDFResource> container;
850 GetResourceFor(aIndex, getter_AddRefs(container));
851 if (! container)
852 return NS_ERROR_FAILURE;
854 PRBool hasProperty;
855 IsContainerOpen(container, &hasProperty);
857 if (isOpen) {
858 if (hasProperty) {
859 mPersistStateStore->Unassert(container,
860 nsXULContentUtils::NC_open,
861 nsXULContentUtils::true_);
864 CloseContainer(aIndex);
866 else {
867 if (! hasProperty) {
868 mPersistStateStore->Assert(container,
869 nsXULContentUtils::NC_open,
870 nsXULContentUtils::true_,
871 PR_TRUE);
874 OpenContainer(aIndex, result);
878 return NS_OK;
881 NS_IMETHODIMP
882 nsXULTreeBuilder::CycleHeader(nsITreeColumn* aCol)
884 NS_ENSURE_ARG_POINTER(aCol);
885 nsCOMPtr<nsIDOMElement> element;
886 aCol->GetElement(getter_AddRefs(element));
888 if (mObservers) {
889 nsAutoString id;
890 aCol->GetId(id);
892 PRUint32 count;
893 mObservers->Count(&count);
894 for (PRUint32 i = 0; i < count; ++i) {
895 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
896 if (observer)
897 observer->OnCycleHeader(id.get(), element);
901 return Sort(element);
904 NS_IMETHODIMP
905 nsXULTreeBuilder::SelectionChanged()
907 if (mObservers) {
908 PRUint32 count;
909 mObservers->Count(&count);
910 for (PRUint32 i = 0; i < count; ++i) {
911 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
912 if (observer)
913 observer->OnSelectionChanged();
917 return NS_OK;
920 NS_IMETHODIMP
921 nsXULTreeBuilder::CycleCell(PRInt32 aRow, nsITreeColumn* aCol)
923 NS_ENSURE_ARG_POINTER(aCol);
924 if (mObservers) {
925 nsAutoString id;
926 aCol->GetId(id);
928 PRUint32 count;
929 mObservers->Count(&count);
930 for (PRUint32 i = 0; i < count; ++i) {
931 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
932 if (observer)
933 observer->OnCycleCell(aRow, id.get());
937 return NS_OK;
940 NS_IMETHODIMP
941 nsXULTreeBuilder::IsEditable(PRInt32 aRow, nsITreeColumn* aCol, PRBool* _retval)
943 *_retval = PR_TRUE;
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));
952 if (cell) {
953 nsAutoString raw;
954 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::editable, raw);
956 nsAutoString editable;
957 SubstituteText(mRows[aRow]->mMatch->mResult, raw, editable);
959 if (editable.EqualsLiteral("false"))
960 *_retval = PR_FALSE;
963 return NS_OK;
966 NS_IMETHODIMP
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;
973 *_retval = PR_TRUE;
975 // Find the <cell> that corresponds to the column we want.
976 nsCOMPtr<nsIContent> cell;
977 GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
978 if (cell) {
979 nsAutoString raw;
980 cell->GetAttr(kNameSpaceID_None, nsGkAtoms::selectable, raw);
982 nsAutoString selectable;
983 SubstituteText(mRows[aRow]->mMatch->mResult, raw, selectable);
985 if (selectable.EqualsLiteral("false"))
986 *_retval = PR_FALSE;
989 return NS_OK;
992 NS_IMETHODIMP
993 nsXULTreeBuilder::SetCellValue(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
995 NS_ENSURE_ARG_POINTER(aCol);
996 return NS_OK;
999 NS_IMETHODIMP
1000 nsXULTreeBuilder::SetCellText(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
1002 NS_ENSURE_ARG_POINTER(aCol);
1003 return NS_OK;
1006 NS_IMETHODIMP
1007 nsXULTreeBuilder::PerformAction(const PRUnichar* aAction)
1009 if (mObservers) {
1010 PRUint32 count;
1011 mObservers->Count(&count);
1012 for (PRUint32 i = 0; i < count; ++i) {
1013 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1014 if (observer)
1015 observer->OnPerformAction(aAction);
1019 return NS_OK;
1022 NS_IMETHODIMP
1023 nsXULTreeBuilder::PerformActionOnRow(const PRUnichar* aAction, PRInt32 aRow)
1025 if (mObservers) {
1026 PRUint32 count;
1027 mObservers->Count(&count);
1028 for (PRUint32 i = 0; i < count; ++i) {
1029 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1030 if (observer)
1031 observer->OnPerformActionOnRow(aAction, aRow);
1035 return NS_OK;
1038 NS_IMETHODIMP
1039 nsXULTreeBuilder::PerformActionOnCell(const PRUnichar* aAction, PRInt32 aRow, nsITreeColumn* aCol)
1041 NS_ENSURE_ARG_POINTER(aCol);
1042 if (mObservers) {
1043 nsAutoString id;
1044 aCol->GetId(id);
1046 PRUint32 count;
1047 mObservers->Count(&count);
1048 for (PRUint32 i = 0; i < count; ++i) {
1049 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1050 if (observer)
1051 observer->OnPerformActionOnCell(aAction, aRow, id.get());
1055 return NS_OK;
1059 void
1060 nsXULTreeBuilder::NodeWillBeDestroyed(const nsINode* aNode)
1062 if (mObservers)
1063 mObservers->Clear();
1065 nsXULTemplateBuilder::NodeWillBeDestroyed(aNode);
1068 NS_IMETHODIMP
1069 nsXULTreeBuilder::HasGeneratedContent(nsIRDFResource* aResource,
1070 nsIAtom* aTag,
1071 PRBool* aGenerated)
1073 *aGenerated = PR_FALSE;
1074 NS_ENSURE_ARG_POINTER(aResource);
1076 if (!mRootResult)
1077 return NS_OK;
1079 nsCOMPtr<nsIRDFResource> rootresource;
1080 nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource));
1081 if (NS_FAILED(rv))
1082 return rv;
1084 if (aResource == rootresource ||
1085 mRows.FindByResource(aResource) != mRows.Last())
1086 *aGenerated = PR_TRUE;
1088 return NS_OK;
1091 PRBool
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.
1100 nsAutoString ref;
1101 nsresult rv = aResult->GetBindingFor(mRefVariable, ref);
1102 if (NS_FAILED(rv) || ref.IsEmpty())
1103 return PR_FALSE;
1105 nsCOMPtr<nsIRDFResource> container;
1106 rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
1107 if (NS_FAILED(rv))
1108 return PR_FALSE;
1110 // Can always insert into the root resource
1111 if (container == mRows.GetRootResource())
1112 return PR_TRUE;
1114 nsTreeRows::iterator iter = mRows.FindByResource(container);
1115 if (iter == mRows.Last())
1116 return PR_FALSE;
1118 return (iter->mContainerState == nsTreeRows::eContainerState_Open);
1121 nsresult
1122 nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
1123 nsTemplateMatch* aNewMatch,
1124 nsTemplateRule* aNewMatchRule,
1125 void *aLocation)
1127 if (! mBoxObject)
1128 return NS_OK;
1130 if (aOldResult) {
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
1143 // gets added back.
1144 PRInt32 delta = mRows.GetSubtreeSizeFor(iter);
1145 if (delta)
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));
1156 if (cols) {
1157 nsCOMPtr<nsITreeColumn> primaryCol;
1158 cols->GetPrimaryColumn(getter_AddRefs(primaryCol));
1159 if (primaryCol)
1160 mBoxObject->InvalidateCell(iter.GetRowIndex(), primaryCol);
1164 // Notify the box object
1165 mBoxObject->RowCountChanged(row, -delta - 1);
1168 if (aNewMatch && aNewMatch->mResult) {
1169 // Insertion.
1170 PRInt32 row = -1;
1171 nsTreeRows::Subtree* parent = nsnull;
1172 nsIXULTemplateResult* result = aNewMatch->mResult;
1174 nsAutoString ref;
1175 nsresult rv = result->GetBindingFor(mRefVariable, ref);
1176 if (NS_FAILED(rv) || ref.IsEmpty())
1177 return rv;
1179 nsCOMPtr<nsIRDFResource> container;
1180 rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
1181 if (NS_FAILED(rv))
1182 return rv;
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.
1198 if (open)
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());
1211 else {
1212 parent = mRows.GetRoot();
1215 if (parent) {
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
1223 // insertion sort.
1224 PRInt32 left = 0;
1225 PRInt32 right = index;
1227 while (left < right) {
1228 index = (left + right) / 2;
1229 PRInt32 cmp = CompareResults((*parent)[index].mMatch->mResult, result);
1230 if (cmp < 0)
1231 left = ++index;
1232 else if (cmp > 0)
1233 right = index;
1234 else
1235 break;
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)
1248 return NS_OK;
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;
1257 PRBool open;
1258 IsContainerOpen(result, &open);
1259 if (open)
1260 OpenContainer(iter.GetRowIndex(), result);
1264 return NS_OK;
1267 nsresult
1268 nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult* aResult)
1270 if (mBoxObject) {
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();
1281 if (row >= 0)
1282 mBoxObject->InvalidateRow(row);
1284 PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
1285 ("xultemplate[%p] => row %d", this, row));
1288 return NS_OK;
1291 //----------------------------------------------------------------------
1293 nsresult
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));
1304 if (!treecols)
1305 return NS_OK;
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)) {
1315 nsAutoString sort;
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;
1330 break;
1335 return NS_OK;
1338 nsresult
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.
1346 if (!doc)
1347 return NS_OK;
1349 if (! mQueryProcessor)
1350 return NS_OK;
1352 if (mQueriesCompiled) {
1353 Uninit(PR_FALSE);
1355 else if (mBoxObject) {
1356 PRInt32 count = mRows.Count();
1357 mRows.Clear();
1358 mBoxObject->BeginUpdateBatch();
1359 mBoxObject->RowCountChanged(0, -count);
1362 nsresult rv = CompileQueries();
1363 if (NS_FAILED(rv))
1364 return rv;
1366 if (mQuerySets.Length() == 0)
1367 return NS_OK;
1369 // Seed the rule network with assignments for the tree row variable
1370 nsAutoString ref;
1371 mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref);
1373 if (! ref.IsEmpty()) {
1374 rv = mQueryProcessor->TranslateRef(mDataSource, ref,
1375 getter_AddRefs(mRootResult));
1376 if (NS_FAILED(rv))
1377 return rv;
1379 if (mRootResult) {
1380 OpenContainer(-1, mRootResult);
1382 nsCOMPtr<nsIRDFResource> rootResource;
1383 GetResultResource(mRootResult, getter_AddRefs(rootResource));
1385 mRows.SetRootResource(rootResource);
1389 if (mBoxObject) {
1390 mBoxObject->EndUpdateBatch();
1393 return NS_OK;
1396 nsresult
1397 nsXULTreeBuilder::GetTemplateActionRowFor(PRInt32 aRow, nsIContent** aResult)
1399 // Get the template in the DOM from which we're supposed to
1400 // generate text
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);
1410 if (rule) {
1411 nsCOMPtr<nsIContent> children;
1412 nsXULContentUtils::FindChildByTag(rule->GetAction(), kNameSpaceID_XUL,
1413 nsGkAtoms::treechildren,
1414 getter_AddRefs(children));
1415 if (children) {
1416 nsCOMPtr<nsIContent> item;
1417 nsXULContentUtils::FindChildByTag(children, kNameSpaceID_XUL,
1418 nsGkAtoms::treeitem,
1419 getter_AddRefs(item));
1420 if (item)
1421 return nsXULContentUtils::FindChildByTag(item,
1422 kNameSpaceID_XUL,
1423 nsGkAtoms::treerow,
1424 aResult);
1429 *aResult = nsnull;
1430 return NS_OK;
1433 nsresult
1434 nsXULTreeBuilder::GetTemplateActionCellFor(PRInt32 aRow,
1435 nsITreeColumn* aCol,
1436 nsIContent** aResult)
1438 *aResult = nsnull;
1440 if (!aCol) return NS_ERROR_INVALID_ARG;
1442 nsCOMPtr<nsIContent> row;
1443 GetTemplateActionRowFor(aRow, getter_AddRefs(row));
1444 if (row) {
1445 nsCOMPtr<nsIAtom> colAtom;
1446 PRInt32 colIndex;
1447 aCol->GetAtom(getter_AddRefs(colAtom));
1448 aCol->GetIndex(&colIndex);
1450 PRUint32 count = row->GetChildCount();
1451 PRUint32 j = 0;
1452 for (PRUint32 i = 0; i < count; ++i) {
1453 nsIContent *child = row->GetChildAt(i);
1455 if (child->NodeInfo()->Equals(nsGkAtoms::treecell,
1456 kNameSpaceID_XUL)) {
1457 if (colAtom &&
1458 child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
1459 colAtom, eCaseMatters)) {
1460 *aResult = child;
1461 break;
1463 else if (j == (PRUint32)colIndex)
1464 *aResult = child;
1465 j++;
1469 NS_IF_ADDREF(*aResult);
1471 return NS_OK;
1474 nsresult
1475 nsXULTreeBuilder::GetResourceFor(PRInt32 aRow, nsIRDFResource** aResource)
1477 nsTreeRows::Row& row = *(mRows[aRow]);
1478 return GetResultResource(row.mMatch->mResult, aResource);
1481 nsresult
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;
1491 if (aIndex >= 0) {
1492 nsTreeRows::iterator iter = mRows[aIndex];
1493 container = mRows.EnsureSubtreeFor(iter.GetParent(),
1494 iter.GetChildIndex());
1496 iter->mContainerState = nsTreeRows::eContainerState_Open;
1498 else
1499 container = mRows.GetRoot();
1501 if (! container)
1502 return NS_ERROR_OUT_OF_MEMORY;
1504 PRInt32 count;
1505 OpenSubtreeOf(container, aIndex, aResult, &count);
1507 // Notify the box object
1508 if (mBoxObject) {
1509 if (aIndex >= 0)
1510 mBoxObject->InvalidateRow(aIndex);
1512 if (count)
1513 mBoxObject->RowCountChanged(aIndex + 1, count);
1516 return NS_OK;
1519 nsresult
1520 nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
1521 PRInt32 aIndex,
1522 nsIXULTemplateResult *aResult,
1523 PRInt32* aDelta)
1525 nsAutoVoidArray open;
1526 PRInt32 count = 0;
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;
1545 PRInt32 delta;
1546 OpenSubtreeOf(child, aIndex + index, result, &delta);
1547 count += delta;
1550 // Sort the container.
1551 if (mSortVariable) {
1552 NS_QuickSort(mRows.GetRowsFor(aSubtree),
1553 aSubtree->Count(),
1554 sizeof(nsTreeRows::Row),
1555 Compare,
1556 this);
1559 *aDelta = count;
1560 return NS_OK;
1563 nsresult
1564 nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
1565 PRInt32 aIndex,
1566 nsIXULTemplateResult* aResult,
1567 nsTemplateQuerySet* aQuerySet,
1568 PRInt32* aDelta,
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));
1577 if (NS_FAILED(rv))
1578 return rv;
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));
1587 if (NS_FAILED(rv))
1588 return rv;
1590 nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr);
1591 if (!nextresult)
1592 return NS_ERROR_UNEXPECTED;
1594 nsCOMPtr<nsIRDFResource> resultid;
1595 rv = GetResultResource(nextresult, getter_AddRefs(resultid));
1596 if (NS_FAILED(rv))
1597 return rv;
1599 if (! resultid)
1600 continue;
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);
1623 if (!newmatch)
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;
1630 if (aIndex >= 0) {
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);
1636 return rv;
1639 if (resultid == parentid) {
1640 cyclic = PR_TRUE;
1641 break;
1646 if (cyclic) {
1647 NS_WARNING("tree cannot handle cyclic graphs");
1648 nsTemplateMatch::Destroy(mPool, newmatch, PR_FALSE);
1649 continue;
1652 PRInt16 ruleindex;
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);
1658 return rv;
1661 if (matchedrule) {
1662 rv = newmatch->RuleMatched(aQuerySet, matchedrule, ruleindex,
1663 nextresult);
1664 if (NS_FAILED(rv)) {
1665 nsTemplateMatch::Destroy(mPool, newmatch, PR_FALSE);
1666 return rv;
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);
1676 if (isOpen) {
1677 if (!open.AppendElement(NS_INT32_TO_PTR(count)))
1678 return NS_ERROR_OUT_OF_MEMORY;
1681 ++count;
1685 if (prevmatch) {
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;
1694 *aDelta = count;
1695 return rv;
1698 nsresult
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);
1711 // Update the view
1712 iter = mRows[aIndex];
1714 PRInt32 count = mRows.GetSubtreeSizeFor(iter);
1715 mRows.RemoveSubtreeFor(iter);
1717 iter->mContainerState = nsTreeRows::eContainerState_Closed;
1719 if (mBoxObject) {
1720 mBoxObject->InvalidateRow(aIndex);
1722 if (count)
1723 mBoxObject->RowCountChanged(aIndex + 1, -count);
1726 return NS_OK;
1729 nsresult
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));
1739 if (NS_FAILED(rv))
1740 return rv;
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));
1757 return NS_OK;
1760 nsresult
1761 nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult, PRBool* aOpen)
1763 // items are never open if recursion is disabled
1764 if ((mFlags & eDontRecurse) && aResult != mRootResult) {
1765 *aOpen = PR_FALSE;
1766 return NS_OK;
1769 nsCOMPtr<nsIRDFResource> id;
1770 nsresult rv = GetResultResource(aResult, getter_AddRefs(id));
1771 if (NS_FAILED(rv))
1772 return rv;
1774 return IsContainerOpen(id, aOpen);
1777 nsresult
1778 nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aResource, PRBool* aOpen)
1780 if (mPersistStateStore)
1781 mPersistStateStore->HasAssertion(aResource,
1782 nsXULContentUtils::NC_open,
1783 nsXULContentUtils::true_,
1784 PR_TRUE,
1785 aOpen);
1786 else
1787 *aOpen = PR_FALSE;
1789 return NS_OK;
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);
1806 PRInt32
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));
1833 if (NS_FAILED(rv))
1834 return rv;
1836 nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
1837 if (container) {
1838 PRBool isSequence = PR_FALSE;
1839 gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
1840 if (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));
1847 if (leftitem) {
1848 gRDFContainerUtils->IndexOf(mDB, container, leftitem, &lindex);
1849 if (lindex < 0)
1850 return 0;
1853 nsCOMPtr<nsIRDFResource> rightitem;
1854 aRight->GetResource(getter_AddRefs(rightitem));
1855 if (rightitem) {
1856 gRDFContainerUtils->IndexOf(mDB, container, rightitem, &rindex);
1857 if (rindex < 0)
1858 return 0;
1861 return lindex - rindex;
1866 PRInt32 sortorder;
1867 mQueryProcessor->CompareResults(aLeft, aRight, mSortVariable, &sortorder);
1869 if (sortorder)
1870 sortorder = sortorder * mSortDirection;
1871 return sortorder;
1874 nsresult
1875 nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree* aSubtree)
1877 NS_QuickSort(mRows.GetRowsFor(aSubtree),
1878 aSubtree->Count(),
1879 sizeof(nsTreeRows::Row),
1880 Compare,
1881 this);
1883 for (PRInt32 i = aSubtree->Count() - 1; i >= 0; --i) {
1884 nsTreeRows::Subtree* child = (*aSubtree)[i].mSubtree;
1885 if (child)
1886 SortSubtree(child);
1889 return NS_OK;
1893 /* boolean canDrop (in long index, in long orientation); */
1894 NS_IMETHODIMP
1895 nsXULTreeBuilder::CanDrop(PRInt32 index, PRInt32 orientation, PRBool *_retval)
1897 *_retval = PR_FALSE;
1898 if (mObservers) {
1899 PRUint32 count;
1900 mObservers->Count(&count);
1901 for (PRUint32 i = 0; i < count; ++i) {
1902 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1903 if (observer) {
1904 observer->CanDrop(index, orientation, _retval);
1905 if (*_retval)
1906 break;
1911 return NS_OK;
1914 NS_IMETHODIMP
1915 nsXULTreeBuilder::Drop(PRInt32 row, PRInt32 orient)
1917 if (mObservers) {
1918 PRUint32 count;
1919 mObservers->Count(&count);
1920 for (PRUint32 i = 0; i < count; ++i) {
1921 nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1922 if (observer) {
1923 PRBool canDrop = PR_FALSE;
1924 observer->CanDrop(row, orient, &canDrop);
1925 if (canDrop)
1926 observer->OnDrop(row, orient);
1931 return NS_OK;
1934 NS_IMETHODIMP
1935 nsXULTreeBuilder::IsSorted(PRBool *_retval)
1937 *_retval = (mSortVariable != nsnull);
1938 return NS_OK;