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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
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.
21 : *
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>
27 : *
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.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
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 "nsIXULSortService.h"
63 : #include "nsTArray.h"
64 : #include "nsUnicharUtils.h"
65 : #include "nsINameSpaceManager.h"
66 : #include "nsDOMClassInfoID.h"
67 : #include "nsWhitespaceTokenizer.h"
68 : #include "nsTreeContentView.h"
69 :
70 : // For security check
71 : #include "nsIDocument.h"
72 :
73 : /**
74 : * A XUL template builder that serves as an tree view, allowing
75 : * (pretty much) arbitrary RDF to be presented in an tree.
76 : */
77 : class nsXULTreeBuilder : public nsXULTemplateBuilder,
78 : public nsIXULTreeBuilder,
79 : public nsINativeTreeView
80 0 : {
81 : public:
82 : // nsISupports
83 : NS_DECL_ISUPPORTS_INHERITED
84 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
85 :
86 : // nsIXULTreeBuilder
87 : NS_DECL_NSIXULTREEBUILDER
88 :
89 : // nsITreeView
90 : NS_DECL_NSITREEVIEW
91 : // nsINativeTreeView: Untrusted code can use us
92 0 : NS_IMETHOD EnsureNative() { return NS_OK; }
93 :
94 : // nsIMutationObserver
95 : NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
96 :
97 : protected:
98 : friend nsresult
99 : NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
100 :
101 : nsXULTreeBuilder();
102 :
103 : /**
104 : * Uninitialize the template builder
105 : */
106 : virtual void Uninit(bool aIsFinal);
107 :
108 : /**
109 : * Get sort variables from the active <treecol>
110 : */
111 : nsresult
112 : EnsureSortVariables();
113 :
114 : virtual nsresult
115 : RebuildAll();
116 :
117 : /**
118 : * Given a row, use the row's match to figure out the appropriate
119 : * <treerow> in the rule's <action>.
120 : */
121 : nsresult
122 : GetTemplateActionRowFor(PRInt32 aRow, nsIContent** aResult);
123 :
124 : /**
125 : * Given a row and a column ID, use the row's match to figure out
126 : * the appropriate <treecell> in the rule's <action>.
127 : */
128 : nsresult
129 : GetTemplateActionCellFor(PRInt32 aRow, nsITreeColumn* aCol, nsIContent** aResult);
130 :
131 : /**
132 : * Return the resource corresponding to a row in the tree.
133 : */
134 : nsresult
135 : GetResourceFor(PRInt32 aRow, nsIRDFResource** aResource);
136 :
137 : /**
138 : * Open a container row, inserting the container's children into
139 : * the view.
140 : */
141 : nsresult
142 : OpenContainer(PRInt32 aIndex, nsIXULTemplateResult* aResult);
143 :
144 : /**
145 : * Helper for OpenContainer, recursively open subtrees, remembering
146 : * persisted ``open'' state
147 : */
148 : nsresult
149 : OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
150 : PRInt32 aIndex,
151 : nsIXULTemplateResult *aResult,
152 : PRInt32* aDelta);
153 :
154 : nsresult
155 : OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
156 : PRInt32 aIndex,
157 : nsIXULTemplateResult *aResult,
158 : nsTemplateQuerySet* aQuerySet,
159 : PRInt32* aDelta,
160 : nsTArray<PRInt32>& open);
161 :
162 : /**
163 : * Close a container row, removing the container's childrem from
164 : * the view.
165 : */
166 : nsresult
167 : CloseContainer(PRInt32 aIndex);
168 :
169 : /**
170 : * Remove the matches for the rows in a subtree
171 : */
172 : nsresult
173 : RemoveMatchesFor(nsTreeRows::Subtree& subtree);
174 :
175 : /**
176 : * Helper methods that determine if the specified container is open.
177 : */
178 : nsresult
179 : IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen);
180 :
181 : nsresult
182 : IsContainerOpen(nsIRDFResource* aResource, bool* aOpen);
183 :
184 : /**
185 : * A sorting callback for NS_QuickSort().
186 : */
187 : static int
188 : Compare(const void* aLeft, const void* aRight, void* aClosure);
189 :
190 : /**
191 : * The real sort routine
192 : */
193 : PRInt32
194 : CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight);
195 :
196 : /**
197 : * Sort the specified subtree, and recursively sort any subtrees
198 : * beneath it.
199 : */
200 : nsresult
201 : SortSubtree(nsTreeRows::Subtree* aSubtree);
202 :
203 : NS_IMETHOD
204 : HasGeneratedContent(nsIRDFResource* aResource,
205 : nsIAtom* aTag,
206 : bool* aGenerated);
207 :
208 : // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited
209 : // from nsXULTemplateBuilder
210 :
211 : /**
212 : * Return true if the result can be inserted into the template as a new
213 : * row.
214 : */
215 : bool
216 : GetInsertionLocations(nsIXULTemplateResult* aResult,
217 : nsCOMArray<nsIContent>** aLocations);
218 :
219 : /**
220 : * Implement result replacement
221 : */
222 : virtual nsresult
223 : ReplaceMatch(nsIXULTemplateResult* aOldResult,
224 : nsTemplateMatch* aNewMatch,
225 : nsTemplateRule* aNewMatchRule,
226 : void *aContext);
227 :
228 : /**
229 : * Implement match synchronization
230 : */
231 : virtual nsresult
232 : SynchronizeResult(nsIXULTemplateResult* aResult);
233 :
234 : /**
235 : * The tree's box object, used to communicate with the front-end.
236 : */
237 : nsCOMPtr<nsITreeBoxObject> mBoxObject;
238 :
239 : /**
240 : * The tree's selection object.
241 : */
242 : nsCOMPtr<nsITreeSelection> mSelection;
243 :
244 : /**
245 : * The datasource that's used to persist open folder information
246 : */
247 : nsCOMPtr<nsIRDFDataSource> mPersistStateStore;
248 :
249 : /**
250 : * The rows in the view
251 : */
252 : nsTreeRows mRows;
253 :
254 : /**
255 : * The currently active sort variable
256 : */
257 : nsCOMPtr<nsIAtom> mSortVariable;
258 :
259 : enum Direction {
260 : eDirection_Descending = -1,
261 : eDirection_Natural = 0,
262 : eDirection_Ascending = +1
263 : };
264 :
265 : /**
266 : * The currently active sort order
267 : */
268 : Direction mSortDirection;
269 :
270 : /*
271 : * Sort hints (compare case, etc)
272 : */
273 : PRUint32 mSortHints;
274 :
275 : /**
276 : * The builder observers.
277 : */
278 : nsCOMPtr<nsISupportsArray> mObservers;
279 : };
280 :
281 : //----------------------------------------------------------------------
282 :
283 : nsresult
284 0 : NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
285 : {
286 0 : *aResult = nsnull;
287 :
288 0 : NS_PRECONDITION(aOuter == nsnull, "no aggregation");
289 0 : if (aOuter)
290 0 : return NS_ERROR_NO_AGGREGATION;
291 :
292 : nsresult rv;
293 0 : nsXULTreeBuilder* result = new nsXULTreeBuilder();
294 0 : if (! result)
295 0 : return NS_ERROR_OUT_OF_MEMORY;
296 :
297 0 : NS_ADDREF(result); // stabilize
298 :
299 0 : rv = result->InitGlobals();
300 :
301 0 : if (NS_SUCCEEDED(rv))
302 0 : rv = result->QueryInterface(aIID, aResult);
303 :
304 0 : NS_RELEASE(result);
305 0 : return rv;
306 : }
307 :
308 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTreeBuilder)
309 :
310 0 : NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
311 0 : NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
312 :
313 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
314 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mBoxObject)
315 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSelection)
316 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPersistStateStore)
317 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mObservers)
318 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
319 :
320 0 : static bool TraverseObservers(nsISupports* aElement, void *aData)
321 : {
322 : nsCycleCollectionTraversalCallback *cb =
323 0 : static_cast<nsCycleCollectionTraversalCallback*>(aData);
324 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mObservers[i]");
325 0 : cb->NoteXPCOMChild(aElement);
326 0 : return true;
327 : }
328 :
329 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
330 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mBoxObject)
331 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSelection)
332 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPersistStateStore)
333 0 : if (tmp->mObservers) {
334 0 : tmp->mObservers->EnumerateForwards(TraverseObservers, &cb);
335 : }
336 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
337 :
338 : DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
339 :
340 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder)
341 0 : NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder)
342 0 : NS_INTERFACE_MAP_ENTRY(nsITreeView)
343 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTreeBuilder)
344 0 : NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder)
345 :
346 :
347 0 : nsXULTreeBuilder::nsXULTreeBuilder()
348 0 : : mSortDirection(eDirection_Natural), mSortHints(0)
349 : {
350 0 : }
351 :
352 : void
353 0 : nsXULTreeBuilder::Uninit(bool aIsFinal)
354 : {
355 0 : PRInt32 count = mRows.Count();
356 0 : mRows.Clear();
357 :
358 0 : if (mBoxObject) {
359 0 : mBoxObject->BeginUpdateBatch();
360 0 : mBoxObject->RowCountChanged(0, -count);
361 0 : if (mBoxObject) {
362 0 : mBoxObject->EndUpdateBatch();
363 : }
364 : }
365 :
366 0 : nsXULTemplateBuilder::Uninit(aIsFinal);
367 0 : }
368 :
369 :
370 : //----------------------------------------------------------------------
371 : //
372 : // nsIXULTreeBuilder methods
373 : //
374 :
375 : NS_IMETHODIMP
376 0 : nsXULTreeBuilder::GetResourceAtIndex(PRInt32 aRowIndex, nsIRDFResource** aResult)
377 : {
378 0 : if (aRowIndex < 0 || aRowIndex >= mRows.Count())
379 0 : return NS_ERROR_INVALID_ARG;
380 :
381 0 : return GetResourceFor(aRowIndex, aResult);
382 : }
383 :
384 : NS_IMETHODIMP
385 0 : nsXULTreeBuilder::GetIndexOfResource(nsIRDFResource* aResource, PRInt32* aResult)
386 : {
387 0 : NS_ENSURE_ARG_POINTER(aResource);
388 0 : nsTreeRows::iterator iter = mRows.FindByResource(aResource);
389 0 : if (iter == mRows.Last())
390 0 : *aResult = -1;
391 : else
392 0 : *aResult = iter.GetRowIndex();
393 0 : return NS_OK;
394 : }
395 :
396 : NS_IMETHODIMP
397 0 : nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver* aObserver)
398 : {
399 : nsresult rv;
400 0 : if (!mObservers) {
401 0 : rv = NS_NewISupportsArray(getter_AddRefs(mObservers));
402 0 : if (NS_FAILED(rv))
403 0 : return rv;
404 : }
405 :
406 0 : return mObservers->AppendElement(aObserver);
407 : }
408 :
409 : NS_IMETHODIMP
410 0 : nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver* aObserver)
411 : {
412 0 : return mObservers ? mObservers->RemoveElement(aObserver) : NS_ERROR_FAILURE;
413 : }
414 :
415 : NS_IMETHODIMP
416 0 : nsXULTreeBuilder::Sort(nsIDOMElement* aElement)
417 : {
418 0 : nsCOMPtr<nsIContent> header = do_QueryInterface(aElement);
419 0 : if (! header)
420 0 : return NS_ERROR_FAILURE;
421 :
422 0 : if (header->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortLocked,
423 0 : nsGkAtoms::_true, eCaseMatters))
424 0 : return NS_OK;
425 :
426 0 : nsAutoString sort;
427 0 : header->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
428 :
429 0 : if (sort.IsEmpty())
430 0 : return NS_OK;
431 :
432 : // Grab the new sort variable
433 0 : mSortVariable = do_GetAtom(sort);
434 :
435 0 : nsAutoString hints;
436 0 : header->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints);
437 :
438 0 : bool hasNaturalState = true;
439 0 : nsWhitespaceTokenizer tokenizer(hints);
440 0 : while (tokenizer.hasMoreTokens()) {
441 0 : const nsDependentSubstring& token(tokenizer.nextToken());
442 0 : if (token.EqualsLiteral("comparecase"))
443 0 : mSortHints |= nsIXULSortService::SORT_COMPARECASE;
444 0 : else if (token.EqualsLiteral("integer"))
445 0 : mSortHints |= nsIXULSortService::SORT_INTEGER;
446 0 : else if (token.EqualsLiteral("twostate"))
447 0 : hasNaturalState = false;
448 : }
449 :
450 : // Cycle the sort direction
451 0 : nsAutoString dir;
452 0 : header->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, dir);
453 :
454 0 : if (dir.EqualsLiteral("ascending")) {
455 0 : dir.AssignLiteral("descending");
456 0 : mSortDirection = eDirection_Descending;
457 : }
458 0 : else if (hasNaturalState && dir.EqualsLiteral("descending")) {
459 0 : dir.AssignLiteral("natural");
460 0 : mSortDirection = eDirection_Natural;
461 : }
462 : else {
463 0 : dir.AssignLiteral("ascending");
464 0 : mSortDirection = eDirection_Ascending;
465 : }
466 :
467 : // Sort it.
468 0 : SortSubtree(mRows.GetRoot());
469 0 : mRows.InvalidateCachedRow();
470 0 : if (mBoxObject)
471 0 : mBoxObject->Invalidate();
472 :
473 0 : nsTreeUtils::UpdateSortIndicators(header, dir);
474 :
475 0 : return NS_OK;
476 : }
477 :
478 : //----------------------------------------------------------------------
479 : //
480 : // nsITreeView methods
481 : //
482 :
483 : NS_IMETHODIMP
484 0 : nsXULTreeBuilder::GetRowCount(PRInt32* aRowCount)
485 : {
486 0 : *aRowCount = mRows.Count();
487 0 : return NS_OK;
488 : }
489 :
490 : NS_IMETHODIMP
491 0 : nsXULTreeBuilder::GetSelection(nsITreeSelection** aSelection)
492 : {
493 0 : NS_IF_ADDREF(*aSelection = mSelection.get());
494 0 : return NS_OK;
495 : }
496 :
497 : NS_IMETHODIMP
498 0 : nsXULTreeBuilder::SetSelection(nsITreeSelection* aSelection)
499 : {
500 0 : NS_ENSURE_TRUE(!aSelection ||
501 : nsTreeContentView::CanTrustTreeSelection(aSelection),
502 : NS_ERROR_DOM_SECURITY_ERR);
503 0 : mSelection = aSelection;
504 0 : return NS_OK;
505 : }
506 :
507 : NS_IMETHODIMP
508 0 : nsXULTreeBuilder::GetRowProperties(PRInt32 aIndex, nsISupportsArray* aProperties)
509 : {
510 0 : NS_ENSURE_ARG_POINTER(aProperties);
511 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
512 0 : if (aIndex < 0 || aIndex >= mRows.Count())
513 0 : return NS_ERROR_INVALID_ARG;
514 :
515 0 : nsCOMPtr<nsIContent> row;
516 0 : GetTemplateActionRowFor(aIndex, getter_AddRefs(row));
517 0 : if (row) {
518 0 : nsAutoString raw;
519 0 : row->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
520 :
521 0 : if (!raw.IsEmpty()) {
522 0 : nsAutoString cooked;
523 0 : SubstituteText(mRows[aIndex]->mMatch->mResult, raw, cooked);
524 :
525 0 : nsTreeUtils::TokenizeProperties(cooked, aProperties);
526 : }
527 : }
528 :
529 0 : return NS_OK;
530 : }
531 :
532 : NS_IMETHODIMP
533 0 : nsXULTreeBuilder::GetCellProperties(PRInt32 aRow, nsITreeColumn* aCol, nsISupportsArray* aProperties)
534 : {
535 0 : NS_ENSURE_ARG_POINTER(aCol);
536 0 : NS_ENSURE_ARG_POINTER(aProperties);
537 0 : NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row");
538 0 : if (aRow < 0 || aRow >= mRows.Count())
539 0 : return NS_ERROR_INVALID_ARG;
540 :
541 0 : nsCOMPtr<nsIContent> cell;
542 0 : GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
543 0 : if (cell) {
544 0 : nsAutoString raw;
545 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw);
546 :
547 0 : if (!raw.IsEmpty()) {
548 0 : nsAutoString cooked;
549 0 : SubstituteText(mRows[aRow]->mMatch->mResult, raw, cooked);
550 :
551 0 : nsTreeUtils::TokenizeProperties(cooked, aProperties);
552 : }
553 : }
554 :
555 0 : return NS_OK;
556 : }
557 :
558 : NS_IMETHODIMP
559 0 : nsXULTreeBuilder::GetColumnProperties(nsITreeColumn* aCol,
560 : nsISupportsArray* aProperties)
561 : {
562 0 : NS_ENSURE_ARG_POINTER(aCol);
563 0 : NS_ENSURE_ARG_POINTER(aProperties);
564 : // XXX sortactive fu
565 0 : return NS_OK;
566 : }
567 :
568 : NS_IMETHODIMP
569 0 : nsXULTreeBuilder::IsContainer(PRInt32 aIndex, bool* aResult)
570 : {
571 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
572 0 : if (aIndex < 0 || aIndex >= mRows.Count())
573 0 : return NS_ERROR_INVALID_ARG;
574 :
575 0 : nsTreeRows::iterator iter = mRows[aIndex];
576 :
577 : bool isContainer;
578 0 : iter->mMatch->mResult->GetIsContainer(&isContainer);
579 :
580 0 : iter->mContainerType = isContainer
581 : ? nsTreeRows::eContainerType_Container
582 0 : : nsTreeRows::eContainerType_Noncontainer;
583 :
584 0 : *aResult = (iter->mContainerType == nsTreeRows::eContainerType_Container);
585 0 : return NS_OK;
586 : }
587 :
588 : NS_IMETHODIMP
589 0 : nsXULTreeBuilder::IsContainerOpen(PRInt32 aIndex, bool* aOpen)
590 : {
591 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
592 0 : if (aIndex < 0 || aIndex >= mRows.Count())
593 0 : return NS_ERROR_INVALID_ARG;
594 :
595 0 : nsTreeRows::iterator iter = mRows[aIndex];
596 :
597 0 : if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) {
598 : bool isOpen;
599 0 : IsContainerOpen(iter->mMatch->mResult, &isOpen);
600 :
601 0 : iter->mContainerState = isOpen
602 : ? nsTreeRows::eContainerState_Open
603 0 : : nsTreeRows::eContainerState_Closed;
604 : }
605 :
606 0 : *aOpen = (iter->mContainerState == nsTreeRows::eContainerState_Open);
607 0 : return NS_OK;
608 : }
609 :
610 : NS_IMETHODIMP
611 0 : nsXULTreeBuilder::IsContainerEmpty(PRInt32 aIndex, bool* aResult)
612 : {
613 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
614 0 : if (aIndex < 0 || aIndex >= mRows.Count())
615 0 : return NS_ERROR_INVALID_ARG;
616 :
617 0 : nsTreeRows::iterator iter = mRows[aIndex];
618 0 : NS_ASSERTION(iter->mContainerType == nsTreeRows::eContainerType_Container,
619 : "asking for empty state on non-container");
620 :
621 : // if recursion is disabled, pretend that the container is empty. This
622 : // ensures that folders are still displayed as such, yet won't display
623 : // their children
624 0 : if ((mFlags & eDontRecurse) && (iter->mMatch->mResult != mRootResult)) {
625 0 : *aResult = true;
626 0 : return NS_OK;
627 : }
628 :
629 0 : if (iter->mContainerFill == nsTreeRows::eContainerFill_Unknown) {
630 : bool isEmpty;
631 0 : iter->mMatch->mResult->GetIsEmpty(&isEmpty);
632 :
633 0 : iter->mContainerFill = isEmpty
634 : ? nsTreeRows::eContainerFill_Empty
635 0 : : nsTreeRows::eContainerFill_Nonempty;
636 : }
637 :
638 0 : *aResult = (iter->mContainerFill == nsTreeRows::eContainerFill_Empty);
639 0 : return NS_OK;
640 : }
641 :
642 : NS_IMETHODIMP
643 0 : nsXULTreeBuilder::IsSeparator(PRInt32 aIndex, bool* aResult)
644 : {
645 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
646 0 : if (aIndex < 0 || aIndex >= mRows.Count())
647 0 : return NS_ERROR_INVALID_ARG;
648 :
649 0 : nsAutoString type;
650 0 : nsTreeRows::Row& row = *(mRows[aIndex]);
651 0 : row.mMatch->mResult->GetType(type);
652 :
653 0 : *aResult = type.EqualsLiteral("separator");
654 :
655 0 : return NS_OK;
656 : }
657 :
658 : NS_IMETHODIMP
659 0 : nsXULTreeBuilder::GetParentIndex(PRInt32 aRowIndex, PRInt32* aResult)
660 : {
661 0 : NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
662 0 : if (aRowIndex < 0 || aRowIndex >= mRows.Count())
663 0 : return NS_ERROR_INVALID_ARG;
664 :
665 : // Construct a path to the row
666 0 : nsTreeRows::iterator iter = mRows[aRowIndex];
667 :
668 : // The parent of the row will be at the top of the path
669 0 : nsTreeRows::Subtree* parent = iter.GetParent();
670 :
671 : // Now walk through our previous siblings, subtracting off each
672 : // one's subtree size
673 0 : PRInt32 index = iter.GetChildIndex();
674 0 : while (--index >= 0)
675 0 : aRowIndex -= mRows.GetSubtreeSizeFor(parent, index) + 1;
676 :
677 : // Now the parent's index will be the first row's index, less one.
678 0 : *aResult = aRowIndex - 1;
679 0 : return NS_OK;
680 : }
681 :
682 : NS_IMETHODIMP
683 0 : nsXULTreeBuilder::HasNextSibling(PRInt32 aRowIndex, PRInt32 aAfterIndex, bool* aResult)
684 : {
685 0 : NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
686 0 : if (aRowIndex < 0 || aRowIndex >= mRows.Count())
687 0 : return NS_ERROR_INVALID_ARG;
688 :
689 : // Construct a path to the row
690 0 : nsTreeRows::iterator iter = mRows[aRowIndex];
691 :
692 : // The parent of the row will be at the top of the path
693 0 : nsTreeRows::Subtree* parent = iter.GetParent();
694 :
695 : // We have a next sibling if the child is not the last in the
696 : // subtree.
697 0 : *aResult = iter.GetChildIndex() != parent->Count() - 1;
698 0 : return NS_OK;
699 : }
700 :
701 : NS_IMETHODIMP
702 0 : nsXULTreeBuilder::GetLevel(PRInt32 aRowIndex, PRInt32* aResult)
703 : {
704 0 : NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row");
705 0 : if (aRowIndex < 0 || aRowIndex >= mRows.Count())
706 0 : return NS_ERROR_INVALID_ARG;
707 :
708 : // Construct a path to the row; the ``level'' is the path length
709 : // less one.
710 0 : nsTreeRows::iterator iter = mRows[aRowIndex];
711 0 : *aResult = iter.GetDepth() - 1;
712 0 : return NS_OK;
713 : }
714 :
715 : NS_IMETHODIMP
716 0 : nsXULTreeBuilder::GetImageSrc(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
717 : {
718 0 : NS_ENSURE_ARG_POINTER(aCol);
719 0 : NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
720 0 : if (aRow < 0 || aRow >= mRows.Count())
721 0 : return NS_ERROR_INVALID_ARG;
722 :
723 : // Find the <cell> that corresponds to the column we want.
724 0 : nsCOMPtr<nsIContent> cell;
725 0 : GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
726 0 : if (cell) {
727 0 : nsAutoString raw;
728 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, raw);
729 :
730 0 : SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
731 : }
732 : else
733 0 : aResult.Truncate();
734 :
735 0 : return NS_OK;
736 : }
737 :
738 :
739 : NS_IMETHODIMP
740 0 : nsXULTreeBuilder::GetProgressMode(PRInt32 aRow, nsITreeColumn* aCol, PRInt32* aResult)
741 : {
742 0 : NS_ENSURE_ARG_POINTER(aCol);
743 0 : NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
744 0 : if (aRow < 0 || aRow >= mRows.Count())
745 0 : return NS_ERROR_INVALID_ARG;
746 :
747 0 : *aResult = nsITreeView::PROGRESS_NONE;
748 :
749 : // Find the <cell> that corresponds to the column we want.
750 0 : nsCOMPtr<nsIContent> cell;
751 0 : GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
752 0 : if (cell) {
753 0 : nsAutoString raw;
754 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::mode, raw);
755 :
756 0 : nsAutoString mode;
757 0 : SubstituteText(mRows[aRow]->mMatch->mResult, raw, mode);
758 :
759 0 : if (mode.EqualsLiteral("normal"))
760 0 : *aResult = nsITreeView::PROGRESS_NORMAL;
761 0 : else if (mode.EqualsLiteral("undetermined"))
762 0 : *aResult = nsITreeView::PROGRESS_UNDETERMINED;
763 : }
764 :
765 0 : return NS_OK;
766 : }
767 :
768 : NS_IMETHODIMP
769 0 : nsXULTreeBuilder::GetCellValue(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
770 : {
771 0 : NS_ENSURE_ARG_POINTER(aCol);
772 0 : NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
773 0 : if (aRow < 0 || aRow >= mRows.Count())
774 0 : return NS_ERROR_INVALID_ARG;
775 :
776 : // Find the <cell> that corresponds to the column we want.
777 0 : nsCOMPtr<nsIContent> cell;
778 0 : GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
779 0 : if (cell) {
780 0 : nsAutoString raw;
781 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, raw);
782 :
783 0 : SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
784 : }
785 : else
786 0 : aResult.Truncate();
787 :
788 0 : return NS_OK;
789 : }
790 :
791 : NS_IMETHODIMP
792 0 : nsXULTreeBuilder::GetCellText(PRInt32 aRow, nsITreeColumn* aCol, nsAString& aResult)
793 : {
794 0 : NS_ENSURE_ARG_POINTER(aCol);
795 0 : NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
796 0 : if (aRow < 0 || aRow >= mRows.Count())
797 0 : return NS_ERROR_INVALID_ARG;
798 :
799 : // Find the <cell> that corresponds to the column we want.
800 0 : nsCOMPtr<nsIContent> cell;
801 0 : GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
802 0 : if (cell) {
803 0 : nsAutoString raw;
804 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, raw);
805 :
806 0 : SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult);
807 :
808 : }
809 : else
810 0 : aResult.Truncate();
811 :
812 0 : return NS_OK;
813 : }
814 :
815 : NS_IMETHODIMP
816 0 : nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree)
817 : {
818 0 : mBoxObject = aTree;
819 :
820 : // If this is teardown time, then we're done.
821 0 : if (!mBoxObject) {
822 0 : Uninit(false);
823 0 : return NS_OK;
824 : }
825 0 : NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
826 :
827 : // Is our root's principal trusted?
828 0 : bool isTrusted = false;
829 0 : nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted);
830 0 : if (NS_SUCCEEDED(rv) && isTrusted) {
831 : // Get the datasource we intend to use to remember open state.
832 0 : nsAutoString datasourceStr;
833 0 : mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::statedatasource, datasourceStr);
834 :
835 : // since we are trusted, use the user specified datasource
836 : // if non specified, use localstore, which gives us
837 : // persistence across sessions
838 0 : if (! datasourceStr.IsEmpty()) {
839 0 : gRDFService->GetDataSource(NS_ConvertUTF16toUTF8(datasourceStr).get(),
840 0 : getter_AddRefs(mPersistStateStore));
841 : }
842 : else {
843 : gRDFService->GetDataSource("rdf:local-store",
844 0 : getter_AddRefs(mPersistStateStore));
845 : }
846 : }
847 :
848 : // Either no specific datasource was specified, or we failed
849 : // to get one because we are not trusted.
850 : //
851 : // XXX if it were possible to ``write an arbitrary datasource
852 : // back'', then we could also allow an untrusted document to
853 : // use a statedatasource from the same codebase.
854 0 : if (! mPersistStateStore) {
855 : mPersistStateStore =
856 0 : do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource");
857 : }
858 :
859 0 : NS_ASSERTION(mPersistStateStore, "failed to get a persistent state store");
860 0 : if (! mPersistStateStore)
861 0 : return NS_ERROR_FAILURE;
862 :
863 0 : Rebuild();
864 :
865 0 : EnsureSortVariables();
866 0 : if (mSortVariable)
867 0 : SortSubtree(mRows.GetRoot());
868 :
869 0 : return NS_OK;
870 : }
871 :
872 : NS_IMETHODIMP
873 0 : nsXULTreeBuilder::ToggleOpenState(PRInt32 aIndex)
874 : {
875 0 : if (aIndex < 0 || aIndex >= mRows.Count())
876 0 : return NS_ERROR_INVALID_ARG;
877 :
878 0 : nsIXULTemplateResult* result = mRows[aIndex]->mMatch->mResult;
879 0 : if (! result)
880 0 : return NS_ERROR_FAILURE;
881 :
882 0 : if (mFlags & eDontRecurse)
883 0 : return NS_OK;
884 :
885 0 : if (result && result != mRootResult) {
886 : // don't open containers if child processing isn't allowed
887 : bool mayProcessChildren;
888 0 : nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
889 0 : if (NS_FAILED(rv) || !mayProcessChildren)
890 0 : return rv;
891 : }
892 :
893 0 : if (mObservers) {
894 : PRUint32 count;
895 0 : mObservers->Count(&count);
896 0 : for (PRUint32 i = 0; i < count; ++i) {
897 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
898 0 : if (observer)
899 0 : observer->OnToggleOpenState(aIndex);
900 : }
901 : }
902 :
903 0 : if (mPersistStateStore) {
904 : bool isOpen;
905 0 : IsContainerOpen(aIndex, &isOpen);
906 :
907 0 : nsCOMPtr<nsIRDFResource> container;
908 0 : GetResourceFor(aIndex, getter_AddRefs(container));
909 0 : if (! container)
910 0 : return NS_ERROR_FAILURE;
911 :
912 : bool hasProperty;
913 0 : IsContainerOpen(container, &hasProperty);
914 :
915 0 : if (isOpen) {
916 0 : if (hasProperty) {
917 0 : mPersistStateStore->Unassert(container,
918 : nsXULContentUtils::NC_open,
919 0 : nsXULContentUtils::true_);
920 : }
921 :
922 0 : CloseContainer(aIndex);
923 : }
924 : else {
925 0 : if (! hasProperty) {
926 0 : mPersistStateStore->Assert(container,
927 : nsXULContentUtils::NC_open,
928 : nsXULContentUtils::true_,
929 0 : true);
930 : }
931 :
932 0 : OpenContainer(aIndex, result);
933 : }
934 : }
935 :
936 0 : return NS_OK;
937 : }
938 :
939 : NS_IMETHODIMP
940 0 : nsXULTreeBuilder::CycleHeader(nsITreeColumn* aCol)
941 : {
942 0 : NS_ENSURE_ARG_POINTER(aCol);
943 0 : nsCOMPtr<nsIDOMElement> element;
944 0 : aCol->GetElement(getter_AddRefs(element));
945 :
946 0 : if (mObservers) {
947 0 : nsAutoString id;
948 0 : aCol->GetId(id);
949 :
950 : PRUint32 count;
951 0 : mObservers->Count(&count);
952 0 : for (PRUint32 i = 0; i < count; ++i) {
953 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
954 0 : if (observer)
955 0 : observer->OnCycleHeader(id.get(), element);
956 : }
957 : }
958 :
959 0 : return Sort(element);
960 : }
961 :
962 : NS_IMETHODIMP
963 0 : nsXULTreeBuilder::SelectionChanged()
964 : {
965 0 : if (mObservers) {
966 : PRUint32 count;
967 0 : mObservers->Count(&count);
968 0 : for (PRUint32 i = 0; i < count; ++i) {
969 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
970 0 : if (observer)
971 0 : observer->OnSelectionChanged();
972 : }
973 : }
974 :
975 0 : return NS_OK;
976 : }
977 :
978 : NS_IMETHODIMP
979 0 : nsXULTreeBuilder::CycleCell(PRInt32 aRow, nsITreeColumn* aCol)
980 : {
981 0 : NS_ENSURE_ARG_POINTER(aCol);
982 0 : if (mObservers) {
983 0 : nsAutoString id;
984 0 : aCol->GetId(id);
985 :
986 : PRUint32 count;
987 0 : mObservers->Count(&count);
988 0 : for (PRUint32 i = 0; i < count; ++i) {
989 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
990 0 : if (observer)
991 0 : observer->OnCycleCell(aRow, id.get());
992 : }
993 : }
994 :
995 0 : return NS_OK;
996 : }
997 :
998 : NS_IMETHODIMP
999 0 : nsXULTreeBuilder::IsEditable(PRInt32 aRow, nsITreeColumn* aCol, bool* _retval)
1000 : {
1001 0 : *_retval = true;
1002 0 : NS_ENSURE_ARG_POINTER(aCol);
1003 0 : NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
1004 0 : if (aRow < 0 || aRow >= mRows.Count())
1005 0 : return NS_ERROR_INVALID_ARG;
1006 :
1007 : // Find the <cell> that corresponds to the column we want.
1008 0 : nsCOMPtr<nsIContent> cell;
1009 0 : GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
1010 0 : if (cell) {
1011 0 : nsAutoString raw;
1012 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::editable, raw);
1013 :
1014 0 : nsAutoString editable;
1015 0 : SubstituteText(mRows[aRow]->mMatch->mResult, raw, editable);
1016 :
1017 0 : if (editable.EqualsLiteral("false"))
1018 0 : *_retval = false;
1019 : }
1020 :
1021 0 : return NS_OK;
1022 : }
1023 :
1024 : NS_IMETHODIMP
1025 0 : nsXULTreeBuilder::IsSelectable(PRInt32 aRow, nsITreeColumn* aCol, bool* _retval)
1026 : {
1027 0 : NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index");
1028 0 : if (aRow < 0 || aRow >= mRows.Count())
1029 0 : return NS_ERROR_INVALID_ARG;
1030 :
1031 0 : *_retval = true;
1032 :
1033 : // Find the <cell> that corresponds to the column we want.
1034 0 : nsCOMPtr<nsIContent> cell;
1035 0 : GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell));
1036 0 : if (cell) {
1037 0 : nsAutoString raw;
1038 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::selectable, raw);
1039 :
1040 0 : nsAutoString selectable;
1041 0 : SubstituteText(mRows[aRow]->mMatch->mResult, raw, selectable);
1042 :
1043 0 : if (selectable.EqualsLiteral("false"))
1044 0 : *_retval = false;
1045 : }
1046 :
1047 0 : return NS_OK;
1048 : }
1049 :
1050 : NS_IMETHODIMP
1051 0 : nsXULTreeBuilder::SetCellValue(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
1052 : {
1053 0 : NS_ENSURE_ARG_POINTER(aCol);
1054 0 : return NS_OK;
1055 : }
1056 :
1057 : NS_IMETHODIMP
1058 0 : nsXULTreeBuilder::SetCellText(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
1059 : {
1060 0 : NS_ENSURE_ARG_POINTER(aCol);
1061 0 : return NS_OK;
1062 : }
1063 :
1064 : NS_IMETHODIMP
1065 0 : nsXULTreeBuilder::PerformAction(const PRUnichar* aAction)
1066 : {
1067 0 : if (mObservers) {
1068 : PRUint32 count;
1069 0 : mObservers->Count(&count);
1070 0 : for (PRUint32 i = 0; i < count; ++i) {
1071 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1072 0 : if (observer)
1073 0 : observer->OnPerformAction(aAction);
1074 : }
1075 : }
1076 :
1077 0 : return NS_OK;
1078 : }
1079 :
1080 : NS_IMETHODIMP
1081 0 : nsXULTreeBuilder::PerformActionOnRow(const PRUnichar* aAction, PRInt32 aRow)
1082 : {
1083 0 : if (mObservers) {
1084 : PRUint32 count;
1085 0 : mObservers->Count(&count);
1086 0 : for (PRUint32 i = 0; i < count; ++i) {
1087 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1088 0 : if (observer)
1089 0 : observer->OnPerformActionOnRow(aAction, aRow);
1090 : }
1091 : }
1092 :
1093 0 : return NS_OK;
1094 : }
1095 :
1096 : NS_IMETHODIMP
1097 0 : nsXULTreeBuilder::PerformActionOnCell(const PRUnichar* aAction, PRInt32 aRow, nsITreeColumn* aCol)
1098 : {
1099 0 : NS_ENSURE_ARG_POINTER(aCol);
1100 0 : if (mObservers) {
1101 0 : nsAutoString id;
1102 0 : aCol->GetId(id);
1103 :
1104 : PRUint32 count;
1105 0 : mObservers->Count(&count);
1106 0 : for (PRUint32 i = 0; i < count; ++i) {
1107 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1108 0 : if (observer)
1109 0 : observer->OnPerformActionOnCell(aAction, aRow, id.get());
1110 : }
1111 : }
1112 :
1113 0 : return NS_OK;
1114 : }
1115 :
1116 :
1117 : void
1118 0 : nsXULTreeBuilder::NodeWillBeDestroyed(const nsINode* aNode)
1119 : {
1120 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
1121 0 : if (mObservers)
1122 0 : mObservers->Clear();
1123 :
1124 0 : nsXULTemplateBuilder::NodeWillBeDestroyed(aNode);
1125 0 : }
1126 :
1127 : NS_IMETHODIMP
1128 0 : nsXULTreeBuilder::HasGeneratedContent(nsIRDFResource* aResource,
1129 : nsIAtom* aTag,
1130 : bool* aGenerated)
1131 : {
1132 0 : *aGenerated = false;
1133 0 : NS_ENSURE_ARG_POINTER(aResource);
1134 :
1135 0 : if (!mRootResult)
1136 0 : return NS_OK;
1137 :
1138 0 : nsCOMPtr<nsIRDFResource> rootresource;
1139 0 : nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource));
1140 0 : if (NS_FAILED(rv))
1141 0 : return rv;
1142 :
1143 0 : if (aResource == rootresource ||
1144 0 : mRows.FindByResource(aResource) != mRows.Last())
1145 0 : *aGenerated = true;
1146 :
1147 0 : return NS_OK;
1148 : }
1149 :
1150 : bool
1151 0 : nsXULTreeBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult,
1152 : nsCOMArray<nsIContent>** aLocations)
1153 : {
1154 0 : *aLocations = nsnull;
1155 :
1156 : // Get the reference point and check if it is an open container. Rows
1157 : // should not be generated otherwise.
1158 :
1159 0 : nsAutoString ref;
1160 0 : nsresult rv = aResult->GetBindingFor(mRefVariable, ref);
1161 0 : if (NS_FAILED(rv) || ref.IsEmpty())
1162 0 : return false;
1163 :
1164 0 : nsCOMPtr<nsIRDFResource> container;
1165 0 : rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
1166 0 : if (NS_FAILED(rv))
1167 0 : return false;
1168 :
1169 : // Can always insert into the root resource
1170 0 : if (container == mRows.GetRootResource())
1171 0 : return true;
1172 :
1173 0 : nsTreeRows::iterator iter = mRows.FindByResource(container);
1174 0 : if (iter == mRows.Last())
1175 0 : return false;
1176 :
1177 0 : return (iter->mContainerState == nsTreeRows::eContainerState_Open);
1178 : }
1179 :
1180 : nsresult
1181 0 : nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
1182 : nsTemplateMatch* aNewMatch,
1183 : nsTemplateRule* aNewMatchRule,
1184 : void *aLocation)
1185 : {
1186 0 : if (! mBoxObject)
1187 0 : return NS_OK;
1188 :
1189 0 : if (aOldResult) {
1190 : // Grovel through the rows looking for oldresult.
1191 0 : nsTreeRows::iterator iter = mRows.Find(aOldResult);
1192 :
1193 0 : NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
1194 0 : if (iter == mRows.Last())
1195 0 : return NS_ERROR_FAILURE;
1196 :
1197 : // Remove the rows from the view
1198 0 : PRInt32 row = iter.GetRowIndex();
1199 :
1200 : // If the row contains children, remove the matches from the
1201 : // children so that they can be regenerated again if the element
1202 : // gets added back.
1203 0 : PRInt32 delta = mRows.GetSubtreeSizeFor(iter);
1204 0 : if (delta)
1205 0 : RemoveMatchesFor(*(iter->mSubtree));
1206 :
1207 0 : if (mRows.RemoveRowAt(iter) == 0 && iter.GetRowIndex() >= 0) {
1208 :
1209 : // In this case iter now points to its parent
1210 : // Invalidate the row's cached fill state
1211 0 : iter->mContainerFill = nsTreeRows::eContainerFill_Unknown;
1212 :
1213 0 : nsCOMPtr<nsITreeColumns> cols;
1214 0 : mBoxObject->GetColumns(getter_AddRefs(cols));
1215 0 : if (cols) {
1216 0 : nsCOMPtr<nsITreeColumn> primaryCol;
1217 0 : cols->GetPrimaryColumn(getter_AddRefs(primaryCol));
1218 0 : if (primaryCol)
1219 0 : mBoxObject->InvalidateCell(iter.GetRowIndex(), primaryCol);
1220 : }
1221 : }
1222 :
1223 : // Notify the box object
1224 0 : mBoxObject->RowCountChanged(row, -delta - 1);
1225 : }
1226 :
1227 0 : if (aNewMatch && aNewMatch->mResult) {
1228 : // Insertion.
1229 0 : PRInt32 row = -1;
1230 0 : nsTreeRows::Subtree* parent = nsnull;
1231 0 : nsIXULTemplateResult* result = aNewMatch->mResult;
1232 :
1233 0 : nsAutoString ref;
1234 0 : nsresult rv = result->GetBindingFor(mRefVariable, ref);
1235 0 : if (NS_FAILED(rv) || ref.IsEmpty())
1236 0 : return rv;
1237 :
1238 0 : nsCOMPtr<nsIRDFResource> container;
1239 0 : rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container));
1240 0 : if (NS_FAILED(rv))
1241 0 : return rv;
1242 :
1243 0 : if (container != mRows.GetRootResource()) {
1244 0 : nsTreeRows::iterator iter = mRows.FindByResource(container);
1245 0 : row = iter.GetRowIndex();
1246 :
1247 0 : NS_ASSERTION(iter != mRows.Last(), "couldn't find container row");
1248 0 : if (iter == mRows.Last())
1249 0 : return NS_ERROR_FAILURE;
1250 :
1251 : // Use the persist store to remember if the container
1252 : // is open or closed.
1253 0 : bool open = false;
1254 0 : IsContainerOpen(row, &open);
1255 :
1256 : // If it's open, make sure that we've got a subtree structure ready.
1257 0 : if (open)
1258 0 : parent = mRows.EnsureSubtreeFor(iter);
1259 :
1260 : // We know something has just been inserted into the
1261 : // container, so whether its open or closed, make sure
1262 : // that we've got our tree row's state correct.
1263 0 : if ((iter->mContainerType != nsTreeRows::eContainerType_Container) ||
1264 0 : (iter->mContainerFill != nsTreeRows::eContainerFill_Nonempty)) {
1265 0 : iter->mContainerType = nsTreeRows::eContainerType_Container;
1266 0 : iter->mContainerFill = nsTreeRows::eContainerFill_Nonempty;
1267 0 : mBoxObject->InvalidateRow(iter.GetRowIndex());
1268 : }
1269 : }
1270 : else {
1271 0 : parent = mRows.GetRoot();
1272 : }
1273 :
1274 0 : if (parent) {
1275 : // If we get here, then we're inserting into an open
1276 : // container. By default, place the new element at the
1277 : // end of the container
1278 0 : PRInt32 index = parent->Count();
1279 :
1280 0 : if (mSortVariable) {
1281 : // Figure out where to put the new element by doing an
1282 : // insertion sort.
1283 0 : PRInt32 left = 0;
1284 0 : PRInt32 right = index;
1285 :
1286 0 : while (left < right) {
1287 0 : index = (left + right) / 2;
1288 0 : PRInt32 cmp = CompareResults((*parent)[index].mMatch->mResult, result);
1289 0 : if (cmp < 0)
1290 0 : left = ++index;
1291 0 : else if (cmp > 0)
1292 0 : right = index;
1293 : else
1294 0 : break;
1295 : }
1296 : }
1297 :
1298 : nsTreeRows::iterator iter =
1299 0 : mRows.InsertRowAt(aNewMatch, parent, index);
1300 :
1301 0 : mBoxObject->RowCountChanged(iter.GetRowIndex(), +1);
1302 :
1303 : // See if this newly added row is open; in which case,
1304 : // recursively add its children to the tree, too.
1305 :
1306 0 : if (mFlags & eDontRecurse)
1307 0 : return NS_OK;
1308 :
1309 0 : if (result != mRootResult) {
1310 : // don't open containers if child processing isn't allowed
1311 : bool mayProcessChildren;
1312 0 : nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
1313 0 : if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK;
1314 : }
1315 :
1316 : bool open;
1317 0 : IsContainerOpen(result, &open);
1318 0 : if (open)
1319 0 : OpenContainer(iter.GetRowIndex(), result);
1320 : }
1321 : }
1322 :
1323 0 : return NS_OK;
1324 : }
1325 :
1326 : nsresult
1327 0 : nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult* aResult)
1328 : {
1329 0 : if (mBoxObject) {
1330 : // XXX we could be more conservative and just invalidate the cells
1331 : // that got whacked...
1332 :
1333 0 : nsTreeRows::iterator iter = mRows.Find(aResult);
1334 :
1335 0 : NS_ASSERTION(iter != mRows.Last(), "couldn't find row");
1336 0 : if (iter == mRows.Last())
1337 0 : return NS_ERROR_FAILURE;
1338 :
1339 0 : PRInt32 row = iter.GetRowIndex();
1340 0 : if (row >= 0)
1341 0 : mBoxObject->InvalidateRow(row);
1342 :
1343 0 : PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
1344 : ("xultemplate[%p] => row %d", this, row));
1345 : }
1346 :
1347 0 : return NS_OK;
1348 : }
1349 :
1350 : //----------------------------------------------------------------------
1351 :
1352 : nsresult
1353 0 : nsXULTreeBuilder::EnsureSortVariables()
1354 : {
1355 : // Grovel through <treecols> kids to find the <treecol>
1356 : // with the sort attributes.
1357 0 : nsCOMPtr<nsIContent> treecols;
1358 :
1359 : nsXULContentUtils::FindChildByTag(mRoot, kNameSpaceID_XUL,
1360 : nsGkAtoms::treecols,
1361 0 : getter_AddRefs(treecols));
1362 :
1363 0 : if (!treecols)
1364 0 : return NS_OK;
1365 :
1366 0 : for (nsIContent* child = treecols->GetFirstChild();
1367 : child;
1368 0 : child = child->GetNextSibling()) {
1369 :
1370 0 : if (child->NodeInfo()->Equals(nsGkAtoms::treecol,
1371 0 : kNameSpaceID_XUL)) {
1372 0 : if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortActive,
1373 0 : nsGkAtoms::_true, eCaseMatters)) {
1374 0 : nsAutoString sort;
1375 0 : child->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
1376 0 : if (! sort.IsEmpty()) {
1377 0 : mSortVariable = do_GetAtom(sort);
1378 :
1379 : static nsIContent::AttrValuesArray strings[] =
1380 : {&nsGkAtoms::ascending, &nsGkAtoms::descending, nsnull};
1381 0 : switch (child->FindAttrValueIn(kNameSpaceID_None,
1382 : nsGkAtoms::sortDirection,
1383 0 : strings, eCaseMatters)) {
1384 0 : case 0: mSortDirection = eDirection_Ascending; break;
1385 0 : case 1: mSortDirection = eDirection_Descending; break;
1386 0 : default: mSortDirection = eDirection_Natural; break;
1387 : }
1388 : }
1389 : break;
1390 : }
1391 : }
1392 : }
1393 :
1394 0 : return NS_OK;
1395 : }
1396 :
1397 : nsresult
1398 0 : nsXULTreeBuilder::RebuildAll()
1399 : {
1400 0 : NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
1401 :
1402 0 : nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
1403 :
1404 : // Bail out early if we are being torn down.
1405 0 : if (!doc)
1406 0 : return NS_OK;
1407 :
1408 0 : if (! mQueryProcessor)
1409 0 : return NS_OK;
1410 :
1411 0 : if (mBoxObject) {
1412 0 : mBoxObject->BeginUpdateBatch();
1413 : }
1414 :
1415 0 : if (mQueriesCompiled) {
1416 0 : Uninit(false);
1417 : }
1418 0 : else if (mBoxObject) {
1419 0 : PRInt32 count = mRows.Count();
1420 0 : mRows.Clear();
1421 0 : mBoxObject->RowCountChanged(0, -count);
1422 : }
1423 :
1424 0 : nsresult rv = CompileQueries();
1425 0 : if (NS_SUCCEEDED(rv) && mQuerySets.Length() > 0) {
1426 : // Seed the rule network with assignments for the tree row variable
1427 0 : nsAutoString ref;
1428 0 : mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref);
1429 0 : if (!ref.IsEmpty()) {
1430 0 : rv = mQueryProcessor->TranslateRef(mDataSource, ref,
1431 0 : getter_AddRefs(mRootResult));
1432 0 : if (NS_SUCCEEDED(rv) && mRootResult) {
1433 0 : OpenContainer(-1, mRootResult);
1434 :
1435 0 : nsCOMPtr<nsIRDFResource> rootResource;
1436 0 : GetResultResource(mRootResult, getter_AddRefs(rootResource));
1437 :
1438 0 : mRows.SetRootResource(rootResource);
1439 : }
1440 : }
1441 : }
1442 :
1443 0 : if (mBoxObject) {
1444 0 : mBoxObject->EndUpdateBatch();
1445 : }
1446 :
1447 0 : return rv;
1448 : }
1449 :
1450 : nsresult
1451 0 : nsXULTreeBuilder::GetTemplateActionRowFor(PRInt32 aRow, nsIContent** aResult)
1452 : {
1453 : // Get the template in the DOM from which we're supposed to
1454 : // generate text
1455 0 : nsTreeRows::Row& row = *(mRows[aRow]);
1456 :
1457 : // The match stores the indices of the rule and query to use. Use these
1458 : // to look up the right nsTemplateRule and use that rule's action to get
1459 : // the treerow in the template.
1460 0 : PRInt16 ruleindex = row.mMatch->RuleIndex();
1461 0 : if (ruleindex >= 0) {
1462 0 : nsTemplateQuerySet* qs = mQuerySets[row.mMatch->QuerySetPriority()];
1463 0 : nsTemplateRule* rule = qs->GetRuleAt(ruleindex);
1464 0 : if (rule) {
1465 0 : nsCOMPtr<nsIContent> children;
1466 : nsXULContentUtils::FindChildByTag(rule->GetAction(), kNameSpaceID_XUL,
1467 : nsGkAtoms::treechildren,
1468 0 : getter_AddRefs(children));
1469 0 : if (children) {
1470 0 : nsCOMPtr<nsIContent> item;
1471 : nsXULContentUtils::FindChildByTag(children, kNameSpaceID_XUL,
1472 : nsGkAtoms::treeitem,
1473 0 : getter_AddRefs(item));
1474 0 : if (item)
1475 : return nsXULContentUtils::FindChildByTag(item,
1476 : kNameSpaceID_XUL,
1477 : nsGkAtoms::treerow,
1478 0 : aResult);
1479 : }
1480 : }
1481 : }
1482 :
1483 0 : *aResult = nsnull;
1484 0 : return NS_OK;
1485 : }
1486 :
1487 : nsresult
1488 0 : nsXULTreeBuilder::GetTemplateActionCellFor(PRInt32 aRow,
1489 : nsITreeColumn* aCol,
1490 : nsIContent** aResult)
1491 : {
1492 0 : *aResult = nsnull;
1493 :
1494 0 : if (!aCol) return NS_ERROR_INVALID_ARG;
1495 :
1496 0 : nsCOMPtr<nsIContent> row;
1497 0 : GetTemplateActionRowFor(aRow, getter_AddRefs(row));
1498 0 : if (row) {
1499 0 : nsCOMPtr<nsIAtom> colAtom;
1500 : PRInt32 colIndex;
1501 0 : aCol->GetAtom(getter_AddRefs(colAtom));
1502 0 : aCol->GetIndex(&colIndex);
1503 :
1504 0 : PRUint32 j = 0;
1505 0 : for (nsIContent* child = row->GetFirstChild();
1506 : child;
1507 0 : child = child->GetNextSibling()) {
1508 :
1509 0 : if (child->NodeInfo()->Equals(nsGkAtoms::treecell,
1510 0 : kNameSpaceID_XUL)) {
1511 0 : if (colAtom &&
1512 : child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
1513 0 : colAtom, eCaseMatters)) {
1514 0 : *aResult = child;
1515 0 : break;
1516 : }
1517 0 : else if (j == (PRUint32)colIndex)
1518 0 : *aResult = child;
1519 0 : j++;
1520 : }
1521 : }
1522 : }
1523 0 : NS_IF_ADDREF(*aResult);
1524 :
1525 0 : return NS_OK;
1526 : }
1527 :
1528 : nsresult
1529 0 : nsXULTreeBuilder::GetResourceFor(PRInt32 aRow, nsIRDFResource** aResource)
1530 : {
1531 0 : nsTreeRows::Row& row = *(mRows[aRow]);
1532 0 : return GetResultResource(row.mMatch->mResult, aResource);
1533 : }
1534 :
1535 : nsresult
1536 0 : nsXULTreeBuilder::OpenContainer(PRInt32 aIndex, nsIXULTemplateResult* aResult)
1537 : {
1538 : // A row index of -1 in this case means ``open tree body''
1539 0 : NS_ASSERTION(aIndex >= -1 && aIndex < mRows.Count(), "bad row");
1540 0 : if (aIndex < -1 || aIndex >= mRows.Count())
1541 0 : return NS_ERROR_INVALID_ARG;
1542 :
1543 : nsTreeRows::Subtree* container;
1544 :
1545 0 : if (aIndex >= 0) {
1546 0 : nsTreeRows::iterator iter = mRows[aIndex];
1547 : container = mRows.EnsureSubtreeFor(iter.GetParent(),
1548 0 : iter.GetChildIndex());
1549 :
1550 0 : iter->mContainerState = nsTreeRows::eContainerState_Open;
1551 : }
1552 : else
1553 0 : container = mRows.GetRoot();
1554 :
1555 0 : if (! container)
1556 0 : return NS_ERROR_OUT_OF_MEMORY;
1557 :
1558 : PRInt32 count;
1559 0 : OpenSubtreeOf(container, aIndex, aResult, &count);
1560 :
1561 : // Notify the box object
1562 0 : if (mBoxObject) {
1563 0 : if (aIndex >= 0)
1564 0 : mBoxObject->InvalidateRow(aIndex);
1565 :
1566 0 : if (count)
1567 0 : mBoxObject->RowCountChanged(aIndex + 1, count);
1568 : }
1569 :
1570 0 : return NS_OK;
1571 : }
1572 :
1573 : nsresult
1574 0 : nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree* aSubtree,
1575 : PRInt32 aIndex,
1576 : nsIXULTemplateResult *aResult,
1577 : PRInt32* aDelta)
1578 : {
1579 0 : nsAutoTArray<PRInt32, 8> open;
1580 0 : PRInt32 count = 0;
1581 :
1582 0 : PRInt32 rulecount = mQuerySets.Length();
1583 :
1584 0 : for (PRInt32 r = 0; r < rulecount; r++) {
1585 0 : nsTemplateQuerySet* queryset = mQuerySets[r];
1586 0 : OpenSubtreeForQuerySet(aSubtree, aIndex, aResult, queryset, &count, open);
1587 : }
1588 :
1589 : // Now recursively deal with any open sub-containers that just got
1590 : // inserted. We need to do this back-to-front to avoid skewing offsets.
1591 0 : for (PRInt32 i = open.Length() - 1; i >= 0; --i) {
1592 0 : PRInt32 index = open[i];
1593 :
1594 : nsTreeRows::Subtree* child =
1595 0 : mRows.EnsureSubtreeFor(aSubtree, index);
1596 :
1597 0 : nsIXULTemplateResult* result = (*aSubtree)[index].mMatch->mResult;
1598 :
1599 : PRInt32 delta;
1600 0 : OpenSubtreeOf(child, aIndex + index, result, &delta);
1601 0 : count += delta;
1602 : }
1603 :
1604 : // Sort the container.
1605 0 : if (mSortVariable) {
1606 0 : NS_QuickSort(mRows.GetRowsFor(aSubtree),
1607 0 : aSubtree->Count(),
1608 : sizeof(nsTreeRows::Row),
1609 : Compare,
1610 0 : this);
1611 : }
1612 :
1613 0 : *aDelta = count;
1614 0 : return NS_OK;
1615 : }
1616 :
1617 : nsresult
1618 0 : nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
1619 : PRInt32 aIndex,
1620 : nsIXULTemplateResult* aResult,
1621 : nsTemplateQuerySet* aQuerySet,
1622 : PRInt32* aDelta,
1623 : nsTArray<PRInt32>& open)
1624 : {
1625 0 : PRInt32 count = *aDelta;
1626 :
1627 0 : nsCOMPtr<nsISimpleEnumerator> results;
1628 0 : nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult,
1629 : aQuerySet->mCompiledQuery,
1630 0 : getter_AddRefs(results));
1631 0 : if (NS_FAILED(rv))
1632 0 : return rv;
1633 :
1634 : bool hasMoreResults;
1635 0 : rv = results->HasMoreElements(&hasMoreResults);
1636 :
1637 0 : for (; NS_SUCCEEDED(rv) && hasMoreResults;
1638 0 : rv = results->HasMoreElements(&hasMoreResults)) {
1639 0 : nsCOMPtr<nsISupports> nr;
1640 0 : rv = results->GetNext(getter_AddRefs(nr));
1641 0 : if (NS_FAILED(rv))
1642 0 : return rv;
1643 :
1644 0 : nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr);
1645 0 : if (!nextresult)
1646 0 : return NS_ERROR_UNEXPECTED;
1647 :
1648 0 : nsCOMPtr<nsIRDFResource> resultid;
1649 0 : rv = GetResultResource(nextresult, getter_AddRefs(resultid));
1650 0 : if (NS_FAILED(rv))
1651 0 : return rv;
1652 :
1653 0 : if (! resultid)
1654 0 : continue;
1655 :
1656 : // check if there is already an existing match. If so, a previous
1657 : // query already generated content so the match is just added to the
1658 : // end of the set of matches.
1659 :
1660 0 : bool generateContent = true;
1661 :
1662 0 : nsTemplateMatch* prevmatch = nsnull;
1663 0 : nsTemplateMatch* existingmatch = nsnull;
1664 0 : if (mMatchMap.Get(resultid, &existingmatch)){
1665 : // check if there is an existing match that matched a rule
1666 0 : while (existingmatch) {
1667 0 : if (existingmatch->IsActive())
1668 0 : generateContent = false;
1669 0 : prevmatch = existingmatch;
1670 0 : existingmatch = existingmatch->mNext;
1671 : }
1672 : }
1673 :
1674 : nsTemplateMatch *newmatch =
1675 0 : nsTemplateMatch::Create(mPool, aQuerySet->Priority(),
1676 0 : nextresult, nsnull);
1677 0 : if (!newmatch)
1678 0 : return NS_ERROR_OUT_OF_MEMORY;
1679 :
1680 0 : if (generateContent) {
1681 : // Don't allow cyclic graphs to get our knickers in a knot.
1682 0 : bool cyclic = false;
1683 :
1684 0 : if (aIndex >= 0) {
1685 0 : for (nsTreeRows::iterator iter = mRows[aIndex]; iter.GetDepth() > 0; iter.Pop()) {
1686 0 : nsCOMPtr<nsIRDFResource> parentid;
1687 0 : rv = GetResultResource(iter->mMatch->mResult, getter_AddRefs(parentid));
1688 0 : if (NS_FAILED(rv)) {
1689 0 : nsTemplateMatch::Destroy(mPool, newmatch, false);
1690 0 : return rv;
1691 : }
1692 :
1693 0 : if (resultid == parentid) {
1694 0 : cyclic = true;
1695 : break;
1696 : }
1697 : }
1698 : }
1699 :
1700 0 : if (cyclic) {
1701 0 : NS_WARNING("tree cannot handle cyclic graphs");
1702 0 : nsTemplateMatch::Destroy(mPool, newmatch, false);
1703 0 : continue;
1704 : }
1705 :
1706 : PRInt16 ruleindex;
1707 0 : nsTemplateRule* matchedrule = nsnull;
1708 : rv = DetermineMatchedRule(nsnull, nextresult, aQuerySet,
1709 0 : &matchedrule, &ruleindex);
1710 0 : if (NS_FAILED(rv)) {
1711 0 : nsTemplateMatch::Destroy(mPool, newmatch, false);
1712 0 : return rv;
1713 : }
1714 :
1715 0 : if (matchedrule) {
1716 : rv = newmatch->RuleMatched(aQuerySet, matchedrule, ruleindex,
1717 0 : nextresult);
1718 0 : if (NS_FAILED(rv)) {
1719 0 : nsTemplateMatch::Destroy(mPool, newmatch, false);
1720 0 : return rv;
1721 : }
1722 :
1723 : // Remember that this match applied to this row
1724 0 : mRows.InsertRowAt(newmatch, aSubtree, count);
1725 :
1726 : // If this is open, then remember it so we can recursively add
1727 : // *its* rows to the tree.
1728 0 : bool isOpen = false;
1729 0 : IsContainerOpen(nextresult, &isOpen);
1730 0 : if (isOpen) {
1731 0 : if (open.AppendElement(count) == nsnull)
1732 0 : return NS_ERROR_OUT_OF_MEMORY;
1733 : }
1734 :
1735 0 : ++count;
1736 : }
1737 :
1738 0 : if (mFlags & eLoggingEnabled)
1739 0 : OutputMatchToLog(resultid, newmatch, true);
1740 :
1741 : }
1742 :
1743 0 : if (prevmatch) {
1744 0 : prevmatch->mNext = newmatch;
1745 : }
1746 0 : else if (!mMatchMap.Put(resultid, newmatch)) {
1747 0 : nsTemplateMatch::Destroy(mPool, newmatch, true);
1748 0 : return NS_ERROR_OUT_OF_MEMORY;
1749 : }
1750 : }
1751 :
1752 0 : *aDelta = count;
1753 0 : return rv;
1754 : }
1755 :
1756 : nsresult
1757 0 : nsXULTreeBuilder::CloseContainer(PRInt32 aIndex)
1758 : {
1759 0 : NS_ASSERTION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
1760 0 : if (aIndex < 0 || aIndex >= mRows.Count())
1761 0 : return NS_ERROR_INVALID_ARG;
1762 :
1763 0 : nsTreeRows::iterator iter = mRows[aIndex];
1764 :
1765 0 : if (iter->mSubtree)
1766 0 : RemoveMatchesFor(*iter->mSubtree);
1767 :
1768 :
1769 0 : PRInt32 count = mRows.GetSubtreeSizeFor(iter);
1770 0 : mRows.RemoveSubtreeFor(iter);
1771 :
1772 0 : iter->mContainerState = nsTreeRows::eContainerState_Closed;
1773 :
1774 0 : if (mBoxObject) {
1775 0 : mBoxObject->InvalidateRow(aIndex);
1776 :
1777 0 : if (count)
1778 0 : mBoxObject->RowCountChanged(aIndex + 1, -count);
1779 : }
1780 :
1781 0 : return NS_OK;
1782 : }
1783 :
1784 : nsresult
1785 0 : nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree& subtree)
1786 : {
1787 0 : for (PRInt32 i = subtree.Count() - 1; i >= 0; --i) {
1788 0 : nsTreeRows::Row& row = subtree[i];
1789 :
1790 0 : nsTemplateMatch* match = row.mMatch;
1791 :
1792 0 : nsCOMPtr<nsIRDFResource> id;
1793 0 : nsresult rv = GetResultResource(match->mResult, getter_AddRefs(id));
1794 0 : if (NS_FAILED(rv))
1795 0 : return rv;
1796 :
1797 : nsTemplateMatch* existingmatch;
1798 0 : if (mMatchMap.Get(id, &existingmatch)) {
1799 0 : while (existingmatch) {
1800 0 : nsTemplateMatch* nextmatch = existingmatch->mNext;
1801 0 : nsTemplateMatch::Destroy(mPool, existingmatch, true);
1802 0 : existingmatch = nextmatch;
1803 : }
1804 :
1805 0 : mMatchMap.Remove(id);
1806 : }
1807 :
1808 0 : if ((row.mContainerState == nsTreeRows::eContainerState_Open) && row.mSubtree)
1809 0 : RemoveMatchesFor(*(row.mSubtree));
1810 : }
1811 :
1812 0 : return NS_OK;
1813 : }
1814 :
1815 : nsresult
1816 0 : nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen)
1817 : {
1818 : // items are never open if recursion is disabled
1819 0 : if ((mFlags & eDontRecurse) && aResult != mRootResult) {
1820 0 : *aOpen = false;
1821 0 : return NS_OK;
1822 : }
1823 :
1824 0 : nsCOMPtr<nsIRDFResource> id;
1825 0 : nsresult rv = GetResultResource(aResult, getter_AddRefs(id));
1826 0 : if (NS_FAILED(rv))
1827 0 : return rv;
1828 :
1829 0 : return IsContainerOpen(id, aOpen);
1830 : }
1831 :
1832 : nsresult
1833 0 : nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aResource, bool* aOpen)
1834 : {
1835 0 : if (mPersistStateStore)
1836 0 : mPersistStateStore->HasAssertion(aResource,
1837 : nsXULContentUtils::NC_open,
1838 : nsXULContentUtils::true_,
1839 : true,
1840 0 : aOpen);
1841 : else
1842 0 : *aOpen = false;
1843 :
1844 0 : return NS_OK;
1845 : }
1846 :
1847 : int
1848 0 : nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure)
1849 : {
1850 0 : nsXULTreeBuilder* self = static_cast<nsXULTreeBuilder*>(aClosure);
1851 :
1852 : nsTreeRows::Row* left = static_cast<nsTreeRows::Row*>
1853 0 : (const_cast<void*>(aLeft));
1854 :
1855 : nsTreeRows::Row* right = static_cast<nsTreeRows::Row*>
1856 0 : (const_cast<void*>(aRight));
1857 :
1858 0 : return self->CompareResults(left->mMatch->mResult, right->mMatch->mResult);
1859 : }
1860 :
1861 : PRInt32
1862 0 : nsXULTreeBuilder::CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight)
1863 : {
1864 : // this is an extra check done for RDF queries such that results appear in
1865 : // the order they appear in their containing Seq
1866 0 : if (mSortDirection == eDirection_Natural && mDB) {
1867 : // If the sort order is ``natural'', then see if the container
1868 : // is an RDF sequence. If so, we'll try to use the ordinal
1869 : // properties to determine order.
1870 : //
1871 : // XXX the problem with this is, it doesn't always get the
1872 : // *real* container; e.g.,
1873 : //
1874 : // <treerow uri="?uri" />
1875 : //
1876 : // <triple subject="?uri"
1877 : // predicate="http://home.netscape.com/NC-rdf#subheadings"
1878 : // object="?subheadings" />
1879 : //
1880 : // <member container="?subheadings" child="?subheading" />
1881 : //
1882 : // In this case mRefVariable is bound to ?uri, not
1883 : // ?subheadings. (The ``container'' in the template sense !=
1884 : // container in the RDF sense.)
1885 :
1886 0 : nsCOMPtr<nsISupports> ref;
1887 0 : nsresult rv = aLeft->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref));
1888 0 : if (NS_FAILED(rv))
1889 0 : return rv;
1890 :
1891 0 : nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
1892 0 : if (container) {
1893 0 : bool isSequence = false;
1894 0 : gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
1895 0 : if (isSequence) {
1896 : // Determine the indices of the left and right elements
1897 : // in the container.
1898 0 : PRInt32 lindex = 0, rindex = 0;
1899 :
1900 0 : nsCOMPtr<nsIRDFResource> leftitem;
1901 0 : aLeft->GetResource(getter_AddRefs(leftitem));
1902 0 : if (leftitem) {
1903 0 : gRDFContainerUtils->IndexOf(mDB, container, leftitem, &lindex);
1904 0 : if (lindex < 0)
1905 0 : return 0;
1906 : }
1907 :
1908 0 : nsCOMPtr<nsIRDFResource> rightitem;
1909 0 : aRight->GetResource(getter_AddRefs(rightitem));
1910 0 : if (rightitem) {
1911 0 : gRDFContainerUtils->IndexOf(mDB, container, rightitem, &rindex);
1912 0 : if (rindex < 0)
1913 0 : return 0;
1914 : }
1915 :
1916 0 : return lindex - rindex;
1917 : }
1918 : }
1919 : }
1920 :
1921 : PRInt32 sortorder;
1922 0 : mQueryProcessor->CompareResults(aLeft, aRight, mSortVariable, mSortHints, &sortorder);
1923 :
1924 0 : if (sortorder)
1925 0 : sortorder = sortorder * mSortDirection;
1926 0 : return sortorder;
1927 : }
1928 :
1929 : nsresult
1930 0 : nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree* aSubtree)
1931 : {
1932 0 : NS_QuickSort(mRows.GetRowsFor(aSubtree),
1933 0 : aSubtree->Count(),
1934 : sizeof(nsTreeRows::Row),
1935 : Compare,
1936 0 : this);
1937 :
1938 0 : for (PRInt32 i = aSubtree->Count() - 1; i >= 0; --i) {
1939 0 : nsTreeRows::Subtree* child = (*aSubtree)[i].mSubtree;
1940 0 : if (child)
1941 0 : SortSubtree(child);
1942 : }
1943 :
1944 0 : return NS_OK;
1945 : }
1946 :
1947 :
1948 : /* boolean canDrop (in long index, in long orientation); */
1949 : NS_IMETHODIMP
1950 0 : nsXULTreeBuilder::CanDrop(PRInt32 index, PRInt32 orientation,
1951 : nsIDOMDataTransfer* dataTransfer, bool *_retval)
1952 : {
1953 0 : *_retval = false;
1954 0 : if (mObservers) {
1955 : PRUint32 count;
1956 0 : mObservers->Count(&count);
1957 0 : for (PRUint32 i = 0; i < count; ++i) {
1958 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1959 0 : if (observer) {
1960 0 : observer->CanDrop(index, orientation, dataTransfer, _retval);
1961 0 : if (*_retval)
1962 : break;
1963 : }
1964 : }
1965 : }
1966 :
1967 0 : return NS_OK;
1968 : }
1969 :
1970 : NS_IMETHODIMP
1971 0 : nsXULTreeBuilder::Drop(PRInt32 row, PRInt32 orient, nsIDOMDataTransfer* dataTransfer)
1972 : {
1973 0 : if (mObservers) {
1974 : PRUint32 count;
1975 0 : mObservers->Count(&count);
1976 0 : for (PRUint32 i = 0; i < count; ++i) {
1977 0 : nsCOMPtr<nsIXULTreeBuilderObserver> observer = do_QueryElementAt(mObservers, i);
1978 0 : if (observer) {
1979 0 : bool canDrop = false;
1980 0 : observer->CanDrop(row, orient, dataTransfer, &canDrop);
1981 0 : if (canDrop)
1982 0 : observer->OnDrop(row, orient, dataTransfer);
1983 : }
1984 : }
1985 : }
1986 :
1987 0 : return NS_OK;
1988 : }
1989 :
1990 : NS_IMETHODIMP
1991 0 : nsXULTreeBuilder::IsSorted(bool *_retval)
1992 : {
1993 0 : *_retval = (mSortVariable != nsnull);
1994 0 : return NS_OK;
1995 4392 : }
1996 :
|