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