1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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.org code.
16 : *
17 : * The Initial Developer of the Original Code is Jan Varga.
18 : * Portions created by the Initial Developer are Copyright (C) 2001
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Brian Ryner <bryner@brianryner.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsINameSpaceManager.h"
39 : #include "nsGkAtoms.h"
40 : #include "nsIBoxObject.h"
41 : #include "nsTreeUtils.h"
42 : #include "nsTreeContentView.h"
43 : #include "nsChildIterator.h"
44 : #include "nsDOMClassInfoID.h"
45 : #include "nsEventStates.h"
46 : #include "nsINodeInfo.h"
47 : #include "nsIXULSortService.h"
48 : #include "nsContentUtils.h"
49 : #include "nsTreeBodyFrame.h"
50 : #include "mozilla/dom/Element.h"
51 : #include "nsServiceManagerUtils.h"
52 :
53 : namespace dom = mozilla::dom;
54 :
55 : #define NS_ENSURE_NATIVE_COLUMN(_col) \
56 : nsRefPtr<nsTreeColumn> col = nsTreeBodyFrame::GetColumnImpl(_col); \
57 : if (!col) { \
58 : return NS_ERROR_INVALID_ARG; \
59 : }
60 :
61 : // A content model view implementation for the tree.
62 :
63 : #define ROW_FLAG_CONTAINER 0x01
64 : #define ROW_FLAG_OPEN 0x02
65 : #define ROW_FLAG_EMPTY 0x04
66 : #define ROW_FLAG_SEPARATOR 0x08
67 :
68 : class Row
69 : {
70 : public:
71 : static Row*
72 0 : Create(nsFixedSizeAllocator& aAllocator,
73 : nsIContent* aContent, PRInt32 aParentIndex) {
74 0 : void* place = aAllocator.Alloc(sizeof(Row));
75 0 : return place ? ::new(place) Row(aContent, aParentIndex) : nsnull;
76 : }
77 :
78 : static void
79 0 : Destroy(nsFixedSizeAllocator& aAllocator, Row* aRow) {
80 0 : aRow->~Row();
81 0 : aAllocator.Free(aRow, sizeof(*aRow));
82 0 : }
83 :
84 0 : Row(nsIContent* aContent, PRInt32 aParentIndex)
85 : : mContent(aContent), mParentIndex(aParentIndex),
86 0 : mSubtreeSize(0), mFlags(0) {
87 0 : }
88 :
89 0 : ~Row() {
90 0 : }
91 :
92 0 : void SetContainer(bool aContainer) {
93 0 : aContainer ? mFlags |= ROW_FLAG_CONTAINER : mFlags &= ~ROW_FLAG_CONTAINER;
94 0 : }
95 0 : bool IsContainer() { return mFlags & ROW_FLAG_CONTAINER; }
96 :
97 0 : void SetOpen(bool aOpen) {
98 0 : aOpen ? mFlags |= ROW_FLAG_OPEN : mFlags &= ~ROW_FLAG_OPEN;
99 0 : }
100 0 : bool IsOpen() { return !!(mFlags & ROW_FLAG_OPEN); }
101 :
102 0 : void SetEmpty(bool aEmpty) {
103 0 : aEmpty ? mFlags |= ROW_FLAG_EMPTY : mFlags &= ~ROW_FLAG_EMPTY;
104 0 : }
105 0 : bool IsEmpty() { return !!(mFlags & ROW_FLAG_EMPTY); }
106 :
107 0 : void SetSeparator(bool aSeparator) {
108 0 : aSeparator ? mFlags |= ROW_FLAG_SEPARATOR : mFlags &= ~ROW_FLAG_SEPARATOR;
109 0 : }
110 0 : bool IsSeparator() { return !!(mFlags & ROW_FLAG_SEPARATOR); }
111 :
112 : // Weak reference to a content item.
113 : nsIContent* mContent;
114 :
115 : // The parent index of the item, set to -1 for the top level items.
116 : PRInt32 mParentIndex;
117 :
118 : // Subtree size for this item.
119 : PRInt32 mSubtreeSize;
120 :
121 : private:
122 : // Hide so that only Create() and Destroy() can be used to
123 : // allocate and deallocate from the heap
124 : static void* operator new(size_t) CPP_THROW_NEW { return 0; }
125 : static void operator delete(void*, size_t) {}
126 :
127 : // State flags
128 : PRInt8 mFlags;
129 : };
130 :
131 :
132 : // We don't reference count the reference to the document
133 : // If the document goes away first, we'll be informed and we
134 : // can drop our reference.
135 : // If we go away first, we'll get rid of ourselves from the
136 : // document's observer list.
137 :
138 0 : nsTreeContentView::nsTreeContentView(void) :
139 : mBoxObject(nsnull),
140 : mSelection(nsnull),
141 : mRoot(nsnull),
142 0 : mDocument(nsnull)
143 : {
144 : static const size_t kBucketSizes[] = {
145 : sizeof(Row)
146 : };
147 : static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t);
148 : static const PRInt32 kInitialSize = 16;
149 :
150 0 : mAllocator.Init("nsTreeContentView", kBucketSizes, kNumBuckets, kInitialSize);
151 0 : }
152 :
153 0 : nsTreeContentView::~nsTreeContentView(void)
154 : {
155 : // Remove ourselves from mDocument's observers.
156 0 : if (mDocument)
157 0 : mDocument->RemoveObserver(this);
158 0 : }
159 :
160 : nsresult
161 0 : NS_NewTreeContentView(nsITreeView** aResult)
162 : {
163 0 : *aResult = new nsTreeContentView;
164 0 : if (! *aResult)
165 0 : return NS_ERROR_OUT_OF_MEMORY;
166 0 : NS_ADDREF(*aResult);
167 0 : return NS_OK;
168 : }
169 :
170 1464 : NS_IMPL_CYCLE_COLLECTION_4(nsTreeContentView,
171 : mBoxObject,
172 : mSelection,
173 : mRoot,
174 : mBody)
175 :
176 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeContentView)
177 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeContentView)
178 :
179 : DOMCI_DATA(TreeContentView, nsTreeContentView)
180 :
181 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeContentView)
182 0 : NS_INTERFACE_MAP_ENTRY(nsITreeView)
183 0 : NS_INTERFACE_MAP_ENTRY(nsITreeContentView)
184 0 : NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
185 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
186 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITreeContentView)
187 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeContentView)
188 0 : NS_INTERFACE_MAP_END
189 :
190 : NS_IMETHODIMP
191 0 : nsTreeContentView::GetRowCount(PRInt32* aRowCount)
192 : {
193 0 : *aRowCount = mRows.Length();
194 :
195 0 : return NS_OK;
196 : }
197 :
198 : NS_IMETHODIMP
199 0 : nsTreeContentView::GetSelection(nsITreeSelection** aSelection)
200 : {
201 0 : NS_IF_ADDREF(*aSelection = mSelection);
202 :
203 0 : return NS_OK;
204 : }
205 :
206 : bool
207 0 : nsTreeContentView::CanTrustTreeSelection(nsISupports* aValue)
208 : {
209 : // Untrusted content is only allowed to specify known-good views
210 0 : if (nsContentUtils::IsCallerTrustedForWrite())
211 0 : return true;
212 0 : nsCOMPtr<nsINativeTreeSelection> nativeTreeSel = do_QueryInterface(aValue);
213 0 : return nativeTreeSel && NS_SUCCEEDED(nativeTreeSel->EnsureNative());
214 : }
215 :
216 : NS_IMETHODIMP
217 0 : nsTreeContentView::SetSelection(nsITreeSelection* aSelection)
218 : {
219 0 : NS_ENSURE_TRUE(!aSelection || CanTrustTreeSelection(aSelection),
220 : NS_ERROR_DOM_SECURITY_ERR);
221 :
222 0 : mSelection = aSelection;
223 0 : return NS_OK;
224 : }
225 :
226 : NS_IMETHODIMP
227 0 : nsTreeContentView::GetRowProperties(PRInt32 aIndex, nsISupportsArray* aProperties)
228 : {
229 0 : NS_ENSURE_ARG_POINTER(aProperties);
230 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
231 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
232 0 : return NS_ERROR_INVALID_ARG;
233 :
234 0 : Row* row = mRows[aIndex];
235 : nsIContent* realRow;
236 0 : if (row->IsSeparator())
237 0 : realRow = row->mContent;
238 : else
239 0 : realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
240 :
241 0 : if (realRow) {
242 0 : nsAutoString properties;
243 0 : realRow->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, properties);
244 0 : if (!properties.IsEmpty())
245 0 : nsTreeUtils::TokenizeProperties(properties, aProperties);
246 : }
247 :
248 0 : return NS_OK;
249 : }
250 :
251 : NS_IMETHODIMP
252 0 : nsTreeContentView::GetCellProperties(PRInt32 aRow, nsITreeColumn* aCol, nsISupportsArray* aProperties)
253 : {
254 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
255 0 : NS_ENSURE_ARG_POINTER(aProperties);
256 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
257 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
258 0 : return NS_ERROR_INVALID_ARG;
259 :
260 0 : Row* row = mRows[aRow];
261 : nsIContent* realRow =
262 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
263 0 : if (realRow) {
264 0 : nsIContent* cell = GetCell(realRow, aCol);
265 0 : if (cell) {
266 0 : nsAutoString properties;
267 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, properties);
268 0 : if (!properties.IsEmpty())
269 0 : nsTreeUtils::TokenizeProperties(properties, aProperties);
270 : }
271 : }
272 :
273 0 : return NS_OK;
274 : }
275 :
276 : NS_IMETHODIMP
277 0 : nsTreeContentView::GetColumnProperties(nsITreeColumn* aCol, nsISupportsArray* aProperties)
278 : {
279 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
280 0 : NS_ENSURE_ARG_POINTER(aProperties);
281 0 : nsCOMPtr<nsIDOMElement> element;
282 0 : aCol->GetElement(getter_AddRefs(element));
283 :
284 0 : nsAutoString properties;
285 0 : element->GetAttribute(NS_LITERAL_STRING("properties"), properties);
286 :
287 0 : if (!properties.IsEmpty())
288 0 : nsTreeUtils::TokenizeProperties(properties, aProperties);
289 :
290 0 : return NS_OK;
291 : }
292 :
293 : NS_IMETHODIMP
294 0 : nsTreeContentView::IsContainer(PRInt32 aIndex, bool* _retval)
295 : {
296 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
297 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
298 0 : return NS_ERROR_INVALID_ARG;
299 :
300 0 : *_retval = mRows[aIndex]->IsContainer();
301 :
302 0 : return NS_OK;
303 : }
304 :
305 : NS_IMETHODIMP
306 0 : nsTreeContentView::IsContainerOpen(PRInt32 aIndex, bool* _retval)
307 : {
308 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
309 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
310 0 : return NS_ERROR_INVALID_ARG;
311 :
312 0 : *_retval = mRows[aIndex]->IsOpen();
313 :
314 0 : return NS_OK;
315 : }
316 :
317 : NS_IMETHODIMP
318 0 : nsTreeContentView::IsContainerEmpty(PRInt32 aIndex, bool* _retval)
319 : {
320 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
321 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
322 0 : return NS_ERROR_INVALID_ARG;
323 :
324 0 : *_retval = mRows[aIndex]->IsEmpty();
325 :
326 0 : return NS_OK;
327 : }
328 :
329 : NS_IMETHODIMP
330 0 : nsTreeContentView::IsSeparator(PRInt32 aIndex, bool *_retval)
331 : {
332 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
333 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
334 0 : return NS_ERROR_INVALID_ARG;
335 :
336 0 : *_retval = mRows[aIndex]->IsSeparator();
337 :
338 0 : return NS_OK;
339 : }
340 :
341 : NS_IMETHODIMP
342 0 : nsTreeContentView::IsSorted(bool *_retval)
343 : {
344 0 : *_retval = false;
345 :
346 0 : return NS_OK;
347 : }
348 :
349 : NS_IMETHODIMP
350 0 : nsTreeContentView::CanDrop(PRInt32 aIndex, PRInt32 aOrientation,
351 : nsIDOMDataTransfer* aDataTransfer, bool *_retval)
352 : {
353 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
354 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
355 0 : return NS_ERROR_INVALID_ARG;
356 :
357 0 : *_retval = false;
358 :
359 0 : return NS_OK;
360 : }
361 :
362 : NS_IMETHODIMP
363 0 : nsTreeContentView::Drop(PRInt32 aRow, PRInt32 aOrientation, nsIDOMDataTransfer* aDataTransfer)
364 : {
365 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
366 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
367 0 : return NS_ERROR_INVALID_ARG;
368 :
369 0 : return NS_OK;
370 : }
371 :
372 : NS_IMETHODIMP
373 0 : nsTreeContentView::GetParentIndex(PRInt32 aRowIndex, PRInt32* _retval)
374 : {
375 0 : NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < PRInt32(mRows.Length()),
376 : "bad row index");
377 0 : if (aRowIndex < 0 || aRowIndex >= PRInt32(mRows.Length()))
378 0 : return NS_ERROR_INVALID_ARG;
379 :
380 0 : *_retval = mRows[aRowIndex]->mParentIndex;
381 :
382 0 : return NS_OK;
383 : }
384 :
385 : NS_IMETHODIMP
386 0 : nsTreeContentView::HasNextSibling(PRInt32 aRowIndex, PRInt32 aAfterIndex, bool* _retval)
387 : {
388 0 : NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < PRInt32(mRows.Length()),
389 : "bad row index");
390 0 : if (aRowIndex < 0 || aRowIndex >= PRInt32(mRows.Length()))
391 0 : return NS_ERROR_INVALID_ARG;
392 :
393 : // We have a next sibling if the row is not the last in the subtree.
394 0 : PRInt32 parentIndex = mRows[aRowIndex]->mParentIndex;
395 0 : if (parentIndex >= 0) {
396 : // Compute the last index in this subtree.
397 0 : PRInt32 lastIndex = parentIndex + (mRows[parentIndex])->mSubtreeSize;
398 0 : Row* row = mRows[lastIndex];
399 0 : while (row->mParentIndex != parentIndex) {
400 0 : lastIndex = row->mParentIndex;
401 0 : row = mRows[lastIndex];
402 : }
403 :
404 0 : *_retval = aRowIndex < lastIndex;
405 : }
406 : else {
407 0 : *_retval = PRUint32(aRowIndex) < mRows.Length() - 1;
408 : }
409 :
410 0 : return NS_OK;
411 : }
412 :
413 : NS_IMETHODIMP
414 0 : nsTreeContentView::GetLevel(PRInt32 aIndex, PRInt32* _retval)
415 : {
416 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
417 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
418 0 : return NS_ERROR_INVALID_ARG;
419 :
420 0 : PRInt32 level = 0;
421 0 : Row* row = mRows[aIndex];
422 0 : while (row->mParentIndex >= 0) {
423 0 : level++;
424 0 : row = mRows[row->mParentIndex];
425 : }
426 0 : *_retval = level;
427 :
428 0 : return NS_OK;
429 : }
430 :
431 : NS_IMETHODIMP
432 0 : nsTreeContentView::GetImageSrc(PRInt32 aRow, nsITreeColumn* aCol, nsAString& _retval)
433 : {
434 0 : _retval.Truncate();
435 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
436 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
437 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
438 0 : return NS_ERROR_INVALID_ARG;
439 :
440 0 : Row* row = mRows[aRow];
441 :
442 : nsIContent* realRow =
443 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
444 0 : if (realRow) {
445 0 : nsIContent* cell = GetCell(realRow, aCol);
446 0 : if (cell)
447 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, _retval);
448 : }
449 :
450 0 : return NS_OK;
451 : }
452 :
453 : NS_IMETHODIMP
454 0 : nsTreeContentView::GetProgressMode(PRInt32 aRow, nsITreeColumn* aCol, PRInt32* _retval)
455 : {
456 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
457 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
458 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
459 0 : return NS_ERROR_INVALID_ARG;
460 :
461 0 : *_retval = nsITreeView::PROGRESS_NONE;
462 :
463 0 : Row* row = mRows[aRow];
464 :
465 : nsIContent* realRow =
466 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
467 0 : if (realRow) {
468 0 : nsIContent* cell = GetCell(realRow, aCol);
469 0 : if (cell) {
470 : static nsIContent::AttrValuesArray strings[] =
471 : {&nsGkAtoms::normal, &nsGkAtoms::undetermined, nsnull};
472 0 : switch (cell->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::mode,
473 0 : strings, eCaseMatters)) {
474 0 : case 0: *_retval = nsITreeView::PROGRESS_NORMAL; break;
475 0 : case 1: *_retval = nsITreeView::PROGRESS_UNDETERMINED; break;
476 : }
477 : }
478 : }
479 :
480 0 : return NS_OK;
481 : }
482 :
483 : NS_IMETHODIMP
484 0 : nsTreeContentView::GetCellValue(PRInt32 aRow, nsITreeColumn* aCol, nsAString& _retval)
485 : {
486 0 : _retval.Truncate();
487 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
488 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
489 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
490 0 : return NS_ERROR_INVALID_ARG;
491 :
492 0 : Row* row = mRows[aRow];
493 :
494 : nsIContent* realRow =
495 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
496 0 : if (realRow) {
497 0 : nsIContent* cell = GetCell(realRow, aCol);
498 0 : if (cell)
499 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, _retval);
500 : }
501 :
502 0 : return NS_OK;
503 : }
504 :
505 : NS_IMETHODIMP
506 0 : nsTreeContentView::GetCellText(PRInt32 aRow, nsITreeColumn* aCol, nsAString& _retval)
507 : {
508 0 : _retval.Truncate();
509 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
510 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
511 0 : NS_PRECONDITION(aCol, "bad column");
512 :
513 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()) || !aCol)
514 0 : return NS_ERROR_INVALID_ARG;
515 :
516 0 : Row* row = mRows[aRow];
517 :
518 : // Check for a "label" attribute - this is valid on an <treeitem>
519 : // with a single implied column.
520 0 : if (row->mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, _retval)
521 0 : && !_retval.IsEmpty())
522 0 : return NS_OK;
523 :
524 0 : nsIAtom *rowTag = row->mContent->Tag();
525 0 : if (rowTag == nsGkAtoms::treeitem && row->mContent->IsXUL()) {
526 : nsIContent* realRow =
527 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
528 0 : if (realRow) {
529 0 : nsIContent* cell = GetCell(realRow, aCol);
530 0 : if (cell)
531 0 : cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, _retval);
532 : }
533 : }
534 :
535 0 : return NS_OK;
536 : }
537 :
538 : NS_IMETHODIMP
539 0 : nsTreeContentView::SetTree(nsITreeBoxObject* aTree)
540 : {
541 0 : ClearRows();
542 :
543 0 : mBoxObject = aTree;
544 :
545 0 : if (aTree && !mRoot) {
546 : // Get our root element
547 0 : nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mBoxObject);
548 0 : nsCOMPtr<nsIDOMElement> element;
549 0 : boxObject->GetElement(getter_AddRefs(element));
550 :
551 0 : mRoot = do_QueryInterface(element);
552 0 : NS_ENSURE_STATE(mRoot);
553 :
554 : // Add ourselves to document's observers.
555 0 : nsIDocument* document = mRoot->GetDocument();
556 0 : if (document) {
557 0 : document->AddObserver(this);
558 0 : mDocument = document;
559 : }
560 :
561 0 : nsCOMPtr<nsIDOMElement> bodyElement;
562 0 : mBoxObject->GetTreeBody(getter_AddRefs(bodyElement));
563 0 : if (bodyElement) {
564 0 : mBody = do_QueryInterface(bodyElement);
565 0 : PRInt32 index = 0;
566 0 : Serialize(mBody, -1, &index, mRows);
567 : }
568 : }
569 :
570 0 : return NS_OK;
571 : }
572 :
573 : NS_IMETHODIMP
574 0 : nsTreeContentView::ToggleOpenState(PRInt32 aIndex)
575 : {
576 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
577 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
578 0 : return NS_ERROR_INVALID_ARG;
579 :
580 : // We don't serialize content right here, since content might be generated
581 : // lazily.
582 0 : Row* row = mRows[aIndex];
583 :
584 0 : if (row->IsOpen())
585 0 : row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("false"), true);
586 : else
587 0 : row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("true"), true);
588 :
589 0 : return NS_OK;
590 : }
591 :
592 : NS_IMETHODIMP
593 0 : nsTreeContentView::CycleHeader(nsITreeColumn* aCol)
594 : {
595 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
596 :
597 0 : if (!mRoot)
598 0 : return NS_OK;
599 :
600 0 : nsCOMPtr<nsIDOMElement> element;
601 0 : aCol->GetElement(getter_AddRefs(element));
602 0 : if (element) {
603 0 : nsCOMPtr<nsIContent> column = do_QueryInterface(element);
604 0 : nsAutoString sort;
605 0 : column->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
606 0 : if (!sort.IsEmpty()) {
607 0 : nsCOMPtr<nsIXULSortService> xs = do_GetService("@mozilla.org/xul/xul-sort-service;1");
608 0 : if (xs) {
609 0 : nsAutoString sortdirection;
610 : static nsIContent::AttrValuesArray strings[] =
611 : {&nsGkAtoms::ascending, &nsGkAtoms::descending, nsnull};
612 0 : switch (column->FindAttrValueIn(kNameSpaceID_None,
613 : nsGkAtoms::sortDirection,
614 0 : strings, eCaseMatters)) {
615 0 : case 0: sortdirection.AssignLiteral("descending"); break;
616 0 : case 1: sortdirection.AssignLiteral("natural"); break;
617 0 : default: sortdirection.AssignLiteral("ascending"); break;
618 : }
619 :
620 0 : nsAutoString hints;
621 0 : column->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints);
622 0 : sortdirection.AppendLiteral(" ");
623 0 : sortdirection += hints;
624 :
625 0 : nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
626 0 : xs->Sort(rootnode, sort, sortdirection);
627 : }
628 : }
629 : }
630 :
631 0 : return NS_OK;
632 : }
633 :
634 : NS_IMETHODIMP
635 0 : nsTreeContentView::SelectionChanged()
636 : {
637 0 : return NS_OK;
638 : }
639 :
640 : NS_IMETHODIMP
641 0 : nsTreeContentView::CycleCell(PRInt32 aRow, nsITreeColumn* aCol)
642 : {
643 0 : return NS_OK;
644 : }
645 :
646 : NS_IMETHODIMP
647 0 : nsTreeContentView::IsEditable(PRInt32 aRow, nsITreeColumn* aCol, bool* _retval)
648 : {
649 0 : *_retval = false;
650 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
651 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
652 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
653 0 : return NS_ERROR_INVALID_ARG;
654 :
655 0 : *_retval = true;
656 :
657 0 : Row* row = mRows[aRow];
658 :
659 : nsIContent* realRow =
660 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
661 0 : if (realRow) {
662 0 : nsIContent* cell = GetCell(realRow, aCol);
663 0 : if (cell && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
664 0 : nsGkAtoms::_false, eCaseMatters)) {
665 0 : *_retval = false;
666 : }
667 : }
668 :
669 0 : return NS_OK;
670 : }
671 :
672 : NS_IMETHODIMP
673 0 : nsTreeContentView::IsSelectable(PRInt32 aRow, nsITreeColumn* aCol, bool* _retval)
674 : {
675 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
676 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
677 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
678 0 : return NS_ERROR_INVALID_ARG;
679 :
680 0 : *_retval = true;
681 :
682 0 : Row* row = mRows[aRow];
683 :
684 : nsIContent* realRow =
685 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
686 0 : if (realRow) {
687 0 : nsIContent* cell = GetCell(realRow, aCol);
688 0 : if (cell && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::selectable,
689 0 : nsGkAtoms::_false, eCaseMatters)) {
690 0 : *_retval = false;
691 : }
692 : }
693 :
694 0 : return NS_OK;
695 : }
696 :
697 : NS_IMETHODIMP
698 0 : nsTreeContentView::SetCellValue(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
699 : {
700 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
701 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
702 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
703 0 : return NS_ERROR_INVALID_ARG;
704 :
705 0 : Row* row = mRows[aRow];
706 :
707 : nsIContent* realRow =
708 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
709 0 : if (realRow) {
710 0 : nsIContent* cell = GetCell(realRow, aCol);
711 0 : if (cell)
712 0 : cell->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, true);
713 : }
714 :
715 0 : return NS_OK;
716 : }
717 :
718 : NS_IMETHODIMP
719 0 : nsTreeContentView::SetCellText(PRInt32 aRow, nsITreeColumn* aCol, const nsAString& aValue)
720 : {
721 0 : NS_ENSURE_NATIVE_COLUMN(aCol);
722 0 : NS_PRECONDITION(aRow >= 0 && aRow < PRInt32(mRows.Length()), "bad row");
723 0 : if (aRow < 0 || aRow >= PRInt32(mRows.Length()))
724 0 : return NS_ERROR_INVALID_ARG;
725 :
726 0 : Row* row = mRows[aRow];
727 :
728 : nsIContent* realRow =
729 0 : nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow);
730 0 : if (realRow) {
731 0 : nsIContent* cell = GetCell(realRow, aCol);
732 0 : if (cell)
733 0 : cell->SetAttr(kNameSpaceID_None, nsGkAtoms::label, aValue, true);
734 : }
735 :
736 0 : return NS_OK;
737 : }
738 :
739 : NS_IMETHODIMP
740 0 : nsTreeContentView::PerformAction(const PRUnichar* aAction)
741 : {
742 0 : return NS_OK;
743 : }
744 :
745 : NS_IMETHODIMP
746 0 : nsTreeContentView::PerformActionOnRow(const PRUnichar* aAction, PRInt32 aRow)
747 : {
748 0 : return NS_OK;
749 : }
750 :
751 : NS_IMETHODIMP
752 0 : nsTreeContentView::PerformActionOnCell(const PRUnichar* aAction, PRInt32 aRow, nsITreeColumn* aCol)
753 : {
754 0 : return NS_OK;
755 : }
756 :
757 :
758 : NS_IMETHODIMP
759 0 : nsTreeContentView::GetItemAtIndex(PRInt32 aIndex, nsIDOMElement** _retval)
760 : {
761 0 : NS_PRECONDITION(aIndex >= 0 && aIndex < PRInt32(mRows.Length()), "bad index");
762 0 : if (aIndex < 0 || aIndex >= PRInt32(mRows.Length()))
763 0 : return NS_ERROR_INVALID_ARG;
764 :
765 0 : Row* row = mRows[aIndex];
766 0 : row->mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)_retval);
767 :
768 0 : return NS_OK;
769 : }
770 :
771 : NS_IMETHODIMP
772 0 : nsTreeContentView::GetIndexOfItem(nsIDOMElement* aItem, PRInt32* _retval)
773 : {
774 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aItem);
775 0 : *_retval = FindContent(content);
776 :
777 0 : return NS_OK;
778 : }
779 :
780 : void
781 0 : nsTreeContentView::AttributeChanged(nsIDocument* aDocument,
782 : dom::Element* aElement,
783 : PRInt32 aNameSpaceID,
784 : nsIAtom* aAttribute,
785 : PRInt32 aModType)
786 : {
787 : // Lots of codepaths under here that do all sorts of stuff, so be safe.
788 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
789 :
790 : // Make sure this notification concerns us.
791 : // First check the tag to see if it's one that we care about.
792 0 : nsIAtom* tag = aElement->Tag();
793 :
794 0 : if (mBoxObject && (aElement == mRoot || aElement == mBody)) {
795 0 : mBoxObject->ClearStyleAndImageCaches();
796 0 : mBoxObject->Invalidate();
797 : }
798 :
799 : // We don't consider non-XUL nodes.
800 0 : nsIContent* parent = nsnull;
801 0 : if (!aElement->IsXUL() ||
802 0 : ((parent = aElement->GetParent()) && !parent->IsXUL())) {
803 : return;
804 : }
805 0 : if (tag != nsGkAtoms::treecol &&
806 : tag != nsGkAtoms::treeitem &&
807 : tag != nsGkAtoms::treeseparator &&
808 : tag != nsGkAtoms::treerow &&
809 : tag != nsGkAtoms::treecell) {
810 : return;
811 : }
812 :
813 : // If we have a legal tag, go up to the tree/select and make sure
814 : // that it's ours.
815 :
816 0 : for (nsIContent* element = aElement; element != mBody; element = element->GetParent()) {
817 0 : if (!element)
818 : return; // this is not for us
819 0 : nsIAtom *parentTag = element->Tag();
820 0 : if (element->IsXUL() && parentTag == nsGkAtoms::tree)
821 : return; // this is not for us
822 : }
823 :
824 : // Handle changes of the hidden attribute.
825 0 : if (aAttribute == nsGkAtoms::hidden &&
826 : (tag == nsGkAtoms::treeitem || tag == nsGkAtoms::treeseparator)) {
827 : bool hidden = aElement->AttrValueIs(kNameSpaceID_None,
828 : nsGkAtoms::hidden,
829 0 : nsGkAtoms::_true, eCaseMatters);
830 :
831 0 : PRInt32 index = FindContent(aElement);
832 0 : if (hidden && index >= 0) {
833 : // Hide this row along with its children.
834 0 : PRInt32 count = RemoveRow(index);
835 0 : if (mBoxObject)
836 0 : mBoxObject->RowCountChanged(index, -count);
837 : }
838 0 : else if (!hidden && index < 0) {
839 : // Show this row along with its children.
840 0 : nsCOMPtr<nsIContent> parent = aElement->GetParent();
841 0 : if (parent) {
842 0 : InsertRowFor(parent, aElement);
843 : }
844 : }
845 :
846 : return;
847 : }
848 :
849 0 : if (tag == nsGkAtoms::treecol) {
850 0 : if (aAttribute == nsGkAtoms::properties) {
851 0 : if (mBoxObject) {
852 0 : nsCOMPtr<nsITreeColumns> cols;
853 0 : mBoxObject->GetColumns(getter_AddRefs(cols));
854 0 : if (cols) {
855 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aElement);
856 0 : nsCOMPtr<nsITreeColumn> col;
857 0 : cols->GetColumnFor(element, getter_AddRefs(col));
858 0 : mBoxObject->InvalidateColumn(col);
859 : }
860 : }
861 : }
862 : }
863 0 : else if (tag == nsGkAtoms::treeitem) {
864 0 : PRInt32 index = FindContent(aElement);
865 0 : if (index >= 0) {
866 0 : Row* row = mRows[index];
867 0 : if (aAttribute == nsGkAtoms::container) {
868 : bool isContainer =
869 : aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
870 0 : nsGkAtoms::_true, eCaseMatters);
871 0 : row->SetContainer(isContainer);
872 0 : if (mBoxObject)
873 0 : mBoxObject->InvalidateRow(index);
874 : }
875 0 : else if (aAttribute == nsGkAtoms::open) {
876 : bool isOpen =
877 : aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
878 0 : nsGkAtoms::_true, eCaseMatters);
879 0 : bool wasOpen = row->IsOpen();
880 0 : if (! isOpen && wasOpen)
881 0 : CloseContainer(index);
882 0 : else if (isOpen && ! wasOpen)
883 0 : OpenContainer(index);
884 : }
885 0 : else if (aAttribute == nsGkAtoms::empty) {
886 : bool isEmpty =
887 : aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty,
888 0 : nsGkAtoms::_true, eCaseMatters);
889 0 : row->SetEmpty(isEmpty);
890 0 : if (mBoxObject)
891 0 : mBoxObject->InvalidateRow(index);
892 : }
893 : }
894 : }
895 0 : else if (tag == nsGkAtoms::treeseparator) {
896 0 : PRInt32 index = FindContent(aElement);
897 0 : if (index >= 0) {
898 0 : if (aAttribute == nsGkAtoms::properties && mBoxObject) {
899 0 : mBoxObject->InvalidateRow(index);
900 : }
901 : }
902 : }
903 0 : else if (tag == nsGkAtoms::treerow) {
904 0 : if (aAttribute == nsGkAtoms::properties) {
905 0 : nsCOMPtr<nsIContent> parent = aElement->GetParent();
906 0 : if (parent) {
907 0 : PRInt32 index = FindContent(parent);
908 0 : if (index >= 0 && mBoxObject) {
909 0 : mBoxObject->InvalidateRow(index);
910 : }
911 : }
912 : }
913 : }
914 0 : else if (tag == nsGkAtoms::treecell) {
915 0 : if (aAttribute == nsGkAtoms::ref ||
916 : aAttribute == nsGkAtoms::properties ||
917 : aAttribute == nsGkAtoms::mode ||
918 : aAttribute == nsGkAtoms::src ||
919 : aAttribute == nsGkAtoms::value ||
920 : aAttribute == nsGkAtoms::label) {
921 0 : nsIContent* parent = aElement->GetParent();
922 0 : if (parent) {
923 0 : nsCOMPtr<nsIContent> grandParent = parent->GetParent();
924 0 : if (grandParent && grandParent->IsXUL()) {
925 0 : PRInt32 index = FindContent(grandParent);
926 0 : if (index >= 0 && mBoxObject) {
927 : // XXX Should we make an effort to invalidate only cell ?
928 0 : mBoxObject->InvalidateRow(index);
929 : }
930 : }
931 : }
932 : }
933 : }
934 : }
935 :
936 : void
937 0 : nsTreeContentView::ContentAppended(nsIDocument *aDocument,
938 : nsIContent* aContainer,
939 : nsIContent* aFirstNewContent,
940 : PRInt32 /* unused */)
941 : {
942 0 : for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
943 : // Our contentinserted doesn't use the index
944 0 : ContentInserted(aDocument, aContainer, cur, 0);
945 : }
946 0 : }
947 :
948 : void
949 0 : nsTreeContentView::ContentInserted(nsIDocument *aDocument,
950 : nsIContent* aContainer,
951 : nsIContent* aChild,
952 : PRInt32 /* unused */)
953 : {
954 0 : NS_ASSERTION(aChild, "null ptr");
955 :
956 : // Make sure this notification concerns us.
957 : // First check the tag to see if it's one that we care about.
958 0 : nsIAtom *childTag = aChild->Tag();
959 :
960 : // Don't allow non-XUL nodes.
961 0 : if (!aChild->IsXUL() || !aContainer->IsXUL())
962 0 : return;
963 0 : if (childTag != nsGkAtoms::treeitem &&
964 : childTag != nsGkAtoms::treeseparator &&
965 : childTag != nsGkAtoms::treechildren &&
966 : childTag != nsGkAtoms::treerow &&
967 : childTag != nsGkAtoms::treecell) {
968 0 : return;
969 : }
970 :
971 : // If we have a legal tag, go up to the tree/select and make sure
972 : // that it's ours.
973 :
974 0 : for (nsIContent* element = aContainer; element != mBody; element = element->GetParent()) {
975 0 : if (!element)
976 0 : return; // this is not for us
977 0 : nsIAtom *parentTag = element->Tag();
978 0 : if (element->IsXUL() && parentTag == nsGkAtoms::tree)
979 0 : return; // this is not for us
980 : }
981 :
982 : // Lots of codepaths under here that do all sorts of stuff, so be safe.
983 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
984 :
985 0 : if (childTag == nsGkAtoms::treechildren) {
986 0 : PRInt32 index = FindContent(aContainer);
987 0 : if (index >= 0) {
988 0 : Row* row = mRows[index];
989 0 : row->SetEmpty(false);
990 0 : if (mBoxObject)
991 0 : mBoxObject->InvalidateRow(index);
992 0 : if (row->IsContainer() && row->IsOpen()) {
993 0 : PRInt32 count = EnsureSubtree(index);
994 0 : if (mBoxObject)
995 0 : mBoxObject->RowCountChanged(index + 1, count);
996 : }
997 : }
998 : }
999 0 : else if (childTag == nsGkAtoms::treeitem ||
1000 : childTag == nsGkAtoms::treeseparator) {
1001 0 : InsertRowFor(aContainer, aChild);
1002 : }
1003 0 : else if (childTag == nsGkAtoms::treerow) {
1004 0 : PRInt32 index = FindContent(aContainer);
1005 0 : if (index >= 0 && mBoxObject)
1006 0 : mBoxObject->InvalidateRow(index);
1007 : }
1008 0 : else if (childTag == nsGkAtoms::treecell) {
1009 0 : nsCOMPtr<nsIContent> parent = aContainer->GetParent();
1010 0 : if (parent) {
1011 0 : PRInt32 index = FindContent(parent);
1012 0 : if (index >= 0 && mBoxObject)
1013 0 : mBoxObject->InvalidateRow(index);
1014 : }
1015 : }
1016 : }
1017 :
1018 : void
1019 0 : nsTreeContentView::ContentRemoved(nsIDocument *aDocument,
1020 : nsIContent* aContainer,
1021 : nsIContent* aChild,
1022 : PRInt32 aIndexInContainer,
1023 : nsIContent* aPreviousSibling)
1024 : {
1025 0 : NS_ASSERTION(aChild, "null ptr");
1026 :
1027 : // Make sure this notification concerns us.
1028 : // First check the tag to see if it's one that we care about.
1029 0 : nsIAtom *tag = aChild->Tag();
1030 :
1031 : // We don't consider non-XUL nodes.
1032 0 : if (!aChild->IsXUL() || !aContainer->IsXUL())
1033 0 : return;
1034 0 : if (tag != nsGkAtoms::treeitem &&
1035 : tag != nsGkAtoms::treeseparator &&
1036 : tag != nsGkAtoms::treechildren &&
1037 : tag != nsGkAtoms::treerow &&
1038 : tag != nsGkAtoms::treecell) {
1039 0 : return;
1040 : }
1041 :
1042 : // If we have a legal tag, go up to the tree/select and make sure
1043 : // that it's ours.
1044 :
1045 0 : for (nsIContent* element = aContainer; element != mBody; element = element->GetParent()) {
1046 0 : if (!element)
1047 0 : return; // this is not for us
1048 0 : nsIAtom *parentTag = element->Tag();
1049 0 : if (element->IsXUL() && parentTag == nsGkAtoms::tree)
1050 0 : return; // this is not for us
1051 : }
1052 :
1053 : // Lots of codepaths under here that do all sorts of stuff, so be safe.
1054 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
1055 :
1056 0 : if (tag == nsGkAtoms::treechildren) {
1057 0 : PRInt32 index = FindContent(aContainer);
1058 0 : if (index >= 0) {
1059 0 : Row* row = mRows[index];
1060 0 : row->SetEmpty(true);
1061 0 : PRInt32 count = RemoveSubtree(index);
1062 : // Invalidate also the row to update twisty.
1063 0 : if (mBoxObject) {
1064 0 : mBoxObject->InvalidateRow(index);
1065 0 : mBoxObject->RowCountChanged(index + 1, -count);
1066 : }
1067 : }
1068 : }
1069 0 : else if (tag == nsGkAtoms::treeitem ||
1070 : tag == nsGkAtoms::treeseparator
1071 : ) {
1072 0 : PRInt32 index = FindContent(aChild);
1073 0 : if (index >= 0) {
1074 0 : PRInt32 count = RemoveRow(index);
1075 0 : if (mBoxObject)
1076 0 : mBoxObject->RowCountChanged(index, -count);
1077 0 : }
1078 : }
1079 0 : else if (tag == nsGkAtoms::treerow) {
1080 0 : PRInt32 index = FindContent(aContainer);
1081 0 : if (index >= 0 && mBoxObject)
1082 0 : mBoxObject->InvalidateRow(index);
1083 : }
1084 0 : else if (tag == nsGkAtoms::treecell) {
1085 0 : nsCOMPtr<nsIContent> parent = aContainer->GetParent();
1086 0 : if (parent) {
1087 0 : PRInt32 index = FindContent(parent);
1088 0 : if (index >= 0 && mBoxObject)
1089 0 : mBoxObject->InvalidateRow(index);
1090 : }
1091 : }
1092 : }
1093 :
1094 : void
1095 0 : nsTreeContentView::NodeWillBeDestroyed(const nsINode* aNode)
1096 : {
1097 : // XXXbz do we need this strong ref? Do we drop refs to self in ClearRows?
1098 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
1099 0 : ClearRows();
1100 0 : }
1101 :
1102 :
1103 : // Recursively serialize content, starting with aContent.
1104 : void
1105 0 : nsTreeContentView::Serialize(nsIContent* aContent, PRInt32 aParentIndex,
1106 : PRInt32* aIndex, nsTArray<Row*>& aRows)
1107 : {
1108 : // Don't allow non-XUL nodes.
1109 0 : if (!aContent->IsXUL())
1110 0 : return;
1111 :
1112 0 : ChildIterator iter, last;
1113 0 : for (ChildIterator::Init(aContent, &iter, &last); iter != last; ++iter) {
1114 0 : nsIContent* content = *iter;
1115 0 : nsIAtom *tag = content->Tag();
1116 0 : PRInt32 count = aRows.Length();
1117 :
1118 0 : if (content->IsXUL()) {
1119 0 : if (tag == nsGkAtoms::treeitem)
1120 0 : SerializeItem(content, aParentIndex, aIndex, aRows);
1121 0 : else if (tag == nsGkAtoms::treeseparator)
1122 0 : SerializeSeparator(content, aParentIndex, aIndex, aRows);
1123 : }
1124 0 : *aIndex += aRows.Length() - count;
1125 : }
1126 : }
1127 :
1128 : void
1129 0 : nsTreeContentView::SerializeItem(nsIContent* aContent, PRInt32 aParentIndex,
1130 : PRInt32* aIndex, nsTArray<Row*>& aRows)
1131 : {
1132 0 : if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
1133 0 : nsGkAtoms::_true, eCaseMatters))
1134 0 : return;
1135 :
1136 0 : Row* row = Row::Create(mAllocator, aContent, aParentIndex);
1137 0 : aRows.AppendElement(row);
1138 :
1139 0 : if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
1140 0 : nsGkAtoms::_true, eCaseMatters)) {
1141 0 : row->SetContainer(true);
1142 0 : if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
1143 0 : nsGkAtoms::_true, eCaseMatters)) {
1144 0 : row->SetOpen(true);
1145 : nsIContent* child =
1146 0 : nsTreeUtils::GetImmediateChild(aContent, nsGkAtoms::treechildren);
1147 0 : if (child && child->IsXUL()) {
1148 : // Now, recursively serialize our child.
1149 0 : PRInt32 count = aRows.Length();
1150 0 : PRInt32 index = 0;
1151 0 : Serialize(child, aParentIndex + *aIndex + 1, &index, aRows);
1152 0 : row->mSubtreeSize += aRows.Length() - count;
1153 : }
1154 : else
1155 0 : row->SetEmpty(true);
1156 0 : } else if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::empty,
1157 0 : nsGkAtoms::_true, eCaseMatters)) {
1158 0 : row->SetEmpty(true);
1159 : }
1160 : }
1161 : }
1162 :
1163 : void
1164 0 : nsTreeContentView::SerializeSeparator(nsIContent* aContent,
1165 : PRInt32 aParentIndex, PRInt32* aIndex,
1166 : nsTArray<Row*>& aRows)
1167 : {
1168 0 : if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
1169 0 : nsGkAtoms::_true, eCaseMatters))
1170 0 : return;
1171 :
1172 0 : Row* row = Row::Create(mAllocator, aContent, aParentIndex);
1173 0 : row->SetSeparator(true);
1174 0 : aRows.AppendElement(row);
1175 : }
1176 :
1177 : void
1178 0 : nsTreeContentView::GetIndexInSubtree(nsIContent* aContainer,
1179 : nsIContent* aContent, PRInt32* aIndex)
1180 : {
1181 0 : PRUint32 childCount = aContainer->GetChildCount();
1182 :
1183 0 : if (!aContainer->IsXUL())
1184 0 : return;
1185 :
1186 0 : for (PRUint32 i = 0; i < childCount; i++) {
1187 0 : nsIContent *content = aContainer->GetChildAt(i);
1188 :
1189 0 : if (content == aContent)
1190 0 : break;
1191 :
1192 0 : nsIAtom *tag = content->Tag();
1193 :
1194 0 : if (content->IsXUL()) {
1195 0 : if (tag == nsGkAtoms::treeitem) {
1196 0 : if (! content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
1197 0 : nsGkAtoms::_true, eCaseMatters)) {
1198 0 : (*aIndex)++;
1199 0 : if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container,
1200 0 : nsGkAtoms::_true, eCaseMatters) &&
1201 : content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
1202 0 : nsGkAtoms::_true, eCaseMatters)) {
1203 : nsIContent* child =
1204 0 : nsTreeUtils::GetImmediateChild(content, nsGkAtoms::treechildren);
1205 0 : if (child && child->IsXUL())
1206 0 : GetIndexInSubtree(child, aContent, aIndex);
1207 : }
1208 : }
1209 : }
1210 0 : else if (tag == nsGkAtoms::treeseparator) {
1211 0 : if (! content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
1212 0 : nsGkAtoms::_true, eCaseMatters))
1213 0 : (*aIndex)++;
1214 : }
1215 : }
1216 : }
1217 : }
1218 :
1219 : PRInt32
1220 0 : nsTreeContentView::EnsureSubtree(PRInt32 aIndex)
1221 : {
1222 0 : Row* row = mRows[aIndex];
1223 :
1224 : nsIContent* child;
1225 0 : child = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treechildren);
1226 0 : if (!child || !child->IsXUL()) {
1227 0 : return 0;
1228 : }
1229 :
1230 0 : nsAutoTArray<Row*, 8> rows;
1231 0 : PRInt32 index = 0;
1232 0 : Serialize(child, aIndex, &index, rows);
1233 0 : mRows.InsertElementsAt(aIndex + 1, rows);
1234 0 : PRInt32 count = rows.Length();
1235 :
1236 0 : row->mSubtreeSize += count;
1237 0 : UpdateSubtreeSizes(row->mParentIndex, count);
1238 :
1239 : // Update parent indexes, but skip newly added rows.
1240 : // They already have correct values.
1241 0 : UpdateParentIndexes(aIndex, count + 1, count);
1242 :
1243 0 : return count;
1244 : }
1245 :
1246 : PRInt32
1247 0 : nsTreeContentView::RemoveSubtree(PRInt32 aIndex)
1248 : {
1249 0 : Row* row = mRows[aIndex];
1250 0 : PRInt32 count = row->mSubtreeSize;
1251 :
1252 0 : for(PRInt32 i = 0; i < count; i++) {
1253 0 : Row* nextRow = mRows[aIndex + i + 1];
1254 0 : Row::Destroy(mAllocator, nextRow);
1255 : }
1256 0 : mRows.RemoveElementsAt(aIndex + 1, count);
1257 :
1258 0 : row->mSubtreeSize -= count;
1259 0 : UpdateSubtreeSizes(row->mParentIndex, -count);
1260 :
1261 0 : UpdateParentIndexes(aIndex, 0, -count);
1262 :
1263 0 : return count;
1264 : }
1265 :
1266 : void
1267 0 : nsTreeContentView::InsertRowFor(nsIContent* aParent, nsIContent* aChild)
1268 : {
1269 0 : PRInt32 grandParentIndex = -1;
1270 0 : bool insertRow = false;
1271 :
1272 0 : nsCOMPtr<nsIContent> grandParent = aParent->GetParent();
1273 0 : nsIAtom* grandParentTag = grandParent->Tag();
1274 :
1275 0 : if (grandParent->IsXUL() && grandParentTag == nsGkAtoms::tree) {
1276 : // Allow insertion to the outermost container.
1277 0 : insertRow = true;
1278 : }
1279 : else {
1280 : // Test insertion to an inner container.
1281 :
1282 : // First try to find this parent in our array of rows, if we find one
1283 : // we can be sure that all other parents are open too.
1284 0 : grandParentIndex = FindContent(grandParent);
1285 0 : if (grandParentIndex >= 0) {
1286 : // Got it, now test if it is open.
1287 0 : if (mRows[grandParentIndex]->IsOpen())
1288 0 : insertRow = true;
1289 : }
1290 : }
1291 :
1292 0 : if (insertRow) {
1293 0 : PRInt32 index = 0;
1294 0 : GetIndexInSubtree(aParent, aChild, &index);
1295 :
1296 0 : PRInt32 count = InsertRow(grandParentIndex, index, aChild);
1297 0 : if (mBoxObject)
1298 0 : mBoxObject->RowCountChanged(grandParentIndex + index + 1, count);
1299 : }
1300 0 : }
1301 :
1302 : PRInt32
1303 0 : nsTreeContentView::InsertRow(PRInt32 aParentIndex, PRInt32 aIndex, nsIContent* aContent)
1304 : {
1305 0 : nsAutoTArray<Row*, 8> rows;
1306 0 : nsIAtom *tag = aContent->Tag();
1307 0 : if (aContent->IsXUL()) {
1308 0 : if (tag == nsGkAtoms::treeitem)
1309 0 : SerializeItem(aContent, aParentIndex, &aIndex, rows);
1310 0 : else if (tag == nsGkAtoms::treeseparator)
1311 0 : SerializeSeparator(aContent, aParentIndex, &aIndex, rows);
1312 : }
1313 :
1314 0 : mRows.InsertElementsAt(aParentIndex + aIndex + 1, rows);
1315 0 : PRInt32 count = rows.Length();
1316 :
1317 0 : UpdateSubtreeSizes(aParentIndex, count);
1318 :
1319 : // Update parent indexes, but skip added rows.
1320 : // They already have correct values.
1321 0 : UpdateParentIndexes(aParentIndex + aIndex, count + 1, count);
1322 :
1323 0 : return count;
1324 : }
1325 :
1326 : PRInt32
1327 0 : nsTreeContentView::RemoveRow(PRInt32 aIndex)
1328 : {
1329 0 : Row* row = mRows[aIndex];
1330 0 : PRInt32 count = row->mSubtreeSize + 1;
1331 0 : PRInt32 parentIndex = row->mParentIndex;
1332 :
1333 0 : Row::Destroy(mAllocator, row);
1334 0 : for(PRInt32 i = 1; i < count; i++) {
1335 0 : Row* nextRow = mRows[aIndex + i];
1336 0 : Row::Destroy(mAllocator, nextRow);
1337 : }
1338 0 : mRows.RemoveElementsAt(aIndex, count);
1339 :
1340 0 : UpdateSubtreeSizes(parentIndex, -count);
1341 :
1342 0 : UpdateParentIndexes(aIndex, 0, -count);
1343 :
1344 0 : return count;
1345 : }
1346 :
1347 : void
1348 0 : nsTreeContentView::ClearRows()
1349 : {
1350 0 : for (PRUint32 i = 0; i < mRows.Length(); i++)
1351 0 : Row::Destroy(mAllocator, mRows[i]);
1352 0 : mRows.Clear();
1353 0 : mRoot = nsnull;
1354 0 : mBody = nsnull;
1355 : // Remove ourselves from mDocument's observers.
1356 0 : if (mDocument) {
1357 0 : mDocument->RemoveObserver(this);
1358 0 : mDocument = nsnull;
1359 : }
1360 0 : }
1361 :
1362 : void
1363 0 : nsTreeContentView::OpenContainer(PRInt32 aIndex)
1364 : {
1365 0 : Row* row = mRows[aIndex];
1366 0 : row->SetOpen(true);
1367 :
1368 0 : PRInt32 count = EnsureSubtree(aIndex);
1369 0 : if (mBoxObject) {
1370 0 : mBoxObject->InvalidateRow(aIndex);
1371 0 : mBoxObject->RowCountChanged(aIndex + 1, count);
1372 : }
1373 0 : }
1374 :
1375 : void
1376 0 : nsTreeContentView::CloseContainer(PRInt32 aIndex)
1377 : {
1378 0 : Row* row = mRows[aIndex];
1379 0 : row->SetOpen(false);
1380 :
1381 0 : PRInt32 count = RemoveSubtree(aIndex);
1382 0 : if (mBoxObject) {
1383 0 : mBoxObject->InvalidateRow(aIndex);
1384 0 : mBoxObject->RowCountChanged(aIndex + 1, -count);
1385 : }
1386 0 : }
1387 :
1388 : PRInt32
1389 0 : nsTreeContentView::FindContent(nsIContent* aContent)
1390 : {
1391 0 : for (PRUint32 i = 0; i < mRows.Length(); i++) {
1392 0 : if (mRows[i]->mContent == aContent) {
1393 0 : return i;
1394 : }
1395 : }
1396 :
1397 0 : return -1;
1398 : }
1399 :
1400 : void
1401 0 : nsTreeContentView::UpdateSubtreeSizes(PRInt32 aParentIndex, PRInt32 count)
1402 : {
1403 0 : while (aParentIndex >= 0) {
1404 0 : Row* row = mRows[aParentIndex];
1405 0 : row->mSubtreeSize += count;
1406 0 : aParentIndex = row->mParentIndex;
1407 : }
1408 0 : }
1409 :
1410 : void
1411 0 : nsTreeContentView::UpdateParentIndexes(PRInt32 aIndex, PRInt32 aSkip, PRInt32 aCount)
1412 : {
1413 0 : PRInt32 count = mRows.Length();
1414 0 : for (PRInt32 i = aIndex + aSkip; i < count; i++) {
1415 0 : Row* row = mRows[i];
1416 0 : if (row->mParentIndex > aIndex) {
1417 0 : row->mParentIndex += aCount;
1418 : }
1419 : }
1420 0 : }
1421 :
1422 : nsIContent*
1423 0 : nsTreeContentView::GetCell(nsIContent* aContainer, nsITreeColumn* aCol)
1424 : {
1425 0 : nsCOMPtr<nsIAtom> colAtom;
1426 : PRInt32 colIndex;
1427 0 : aCol->GetAtom(getter_AddRefs(colAtom));
1428 0 : aCol->GetIndex(&colIndex);
1429 :
1430 : // Traverse through cells, try to find the cell by "ref" attribute or by cell
1431 : // index in a row. "ref" attribute has higher priority.
1432 0 : nsIContent* result = nsnull;
1433 0 : PRInt32 j = 0;
1434 0 : ChildIterator iter, last;
1435 0 : for (ChildIterator::Init(aContainer, &iter, &last); iter != last; ++iter) {
1436 0 : nsIContent* cell = *iter;
1437 :
1438 0 : if (cell->Tag() == nsGkAtoms::treecell) {
1439 0 : if (colAtom && cell->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref,
1440 0 : colAtom, eCaseMatters)) {
1441 0 : result = cell;
1442 0 : break;
1443 : }
1444 0 : else if (j == colIndex) {
1445 0 : result = cell;
1446 : }
1447 0 : j++;
1448 : }
1449 : }
1450 :
1451 0 : return result;
1452 4392 : }
|