1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsNameSpaceManager.h"
8 #include "nsGkAtoms.h"
9 #include "nsIDOMElement.h"
10 #include "nsIBoxObject.h"
11 #include "nsTreeColumns.h"
12 #include "nsTreeUtils.h"
13 #include "nsStyleContext.h"
14 #include "nsDOMClassInfoID.h"
15 #include "nsContentUtils.h"
16 #include "nsTreeBodyFrame.h"
17 #include "mozilla/dom/Element.h"
18 #include "mozilla/dom/TreeBoxObject.h"
19 #include "mozilla/dom/TreeColumnBinding.h"
20 #include "mozilla/dom/TreeColumnsBinding.h"
21
22 using namespace mozilla;
23
24 // Column class that caches all the info about our column.
nsTreeColumn(nsTreeColumns * aColumns,nsIContent * aContent)25 nsTreeColumn::nsTreeColumn(nsTreeColumns* aColumns, nsIContent* aContent)
26 : mContent(aContent), mColumns(aColumns), mPrevious(nullptr) {
27 NS_ASSERTION(aContent && aContent->NodeInfo()->Equals(nsGkAtoms::treecol,
28 kNameSpaceID_XUL),
29 "nsTreeColumn's content must be a <xul:treecol>");
30
31 Invalidate();
32 }
33
~nsTreeColumn()34 nsTreeColumn::~nsTreeColumn() {
35 if (mNext) {
36 mNext->SetPrevious(nullptr);
37 }
38 }
39
40 NS_IMPL_CYCLE_COLLECTION_CLASS(nsTreeColumn)
41
42 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTreeColumn)
43 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
44 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
45 if (tmp->mNext) {
46 tmp->mNext->SetPrevious(nullptr);
47 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNext)
48 }
49 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
50 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTreeColumn)
51 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNext)
53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
54 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsTreeColumn)
55
56 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeColumn)
57 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeColumn)
58
59 // QueryInterface implementation for nsTreeColumn
60 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumn)
61 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
62 NS_INTERFACE_MAP_ENTRY(nsITreeColumn)
63 NS_INTERFACE_MAP_ENTRY(nsISupports)
64 if (aIID.Equals(NS_GET_IID(nsTreeColumn))) {
65 AddRef();
66 *aInstancePtr = this;
67 return NS_OK;
68 } else
69 NS_INTERFACE_MAP_END
70
GetFrame()71 nsIFrame* nsTreeColumn::GetFrame() {
72 NS_ENSURE_TRUE(mContent, nullptr);
73
74 return mContent->GetPrimaryFrame();
75 }
76
IsLastVisible(nsTreeBodyFrame * aBodyFrame)77 bool nsTreeColumn::IsLastVisible(nsTreeBodyFrame* aBodyFrame) {
78 NS_ASSERTION(GetFrame(), "should have checked for this already");
79
80 // cyclers are fixed width, don't adjust them
81 if (IsCycler()) return false;
82
83 // we're certainly not the last visible if we're not visible
84 if (GetFrame()->GetRect().width == 0) return false;
85
86 // try to find a visible successor
87 for (nsTreeColumn* next = GetNext(); next; next = next->GetNext()) {
88 nsIFrame* frame = next->GetFrame();
89 if (frame && frame->GetRect().width > 0) return false;
90 }
91 return true;
92 }
93
GetRect(nsTreeBodyFrame * aBodyFrame,nscoord aY,nscoord aHeight,nsRect * aResult)94 nsresult nsTreeColumn::GetRect(nsTreeBodyFrame* aBodyFrame, nscoord aY,
95 nscoord aHeight, nsRect* aResult) {
96 nsIFrame* frame = GetFrame();
97 if (!frame) {
98 *aResult = nsRect();
99 return NS_ERROR_FAILURE;
100 }
101
102 bool isRTL =
103 aBodyFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
104 *aResult = frame->GetRect();
105 aResult->y = aY;
106 aResult->height = aHeight;
107 if (isRTL)
108 aResult->x += aBodyFrame->mAdjustWidth;
109 else if (IsLastVisible(aBodyFrame))
110 aResult->width += aBodyFrame->mAdjustWidth;
111 return NS_OK;
112 }
113
GetXInTwips(nsTreeBodyFrame * aBodyFrame,nscoord * aResult)114 nsresult nsTreeColumn::GetXInTwips(nsTreeBodyFrame* aBodyFrame,
115 nscoord* aResult) {
116 nsIFrame* frame = GetFrame();
117 if (!frame) {
118 *aResult = 0;
119 return NS_ERROR_FAILURE;
120 }
121 *aResult = frame->GetRect().x;
122 return NS_OK;
123 }
124
GetWidthInTwips(nsTreeBodyFrame * aBodyFrame,nscoord * aResult)125 nsresult nsTreeColumn::GetWidthInTwips(nsTreeBodyFrame* aBodyFrame,
126 nscoord* aResult) {
127 nsIFrame* frame = GetFrame();
128 if (!frame) {
129 *aResult = 0;
130 return NS_ERROR_FAILURE;
131 }
132 *aResult = frame->GetRect().width;
133 if (IsLastVisible(aBodyFrame)) *aResult += aBodyFrame->mAdjustWidth;
134 return NS_OK;
135 }
136
137 NS_IMETHODIMP
GetElement(nsIDOMElement ** aElement)138 nsTreeColumn::GetElement(nsIDOMElement** aElement) {
139 if (mContent) {
140 return CallQueryInterface(mContent, aElement);
141 }
142 *aElement = nullptr;
143 return NS_ERROR_FAILURE;
144 }
145
146 NS_IMETHODIMP
GetColumns(nsITreeColumns ** aColumns)147 nsTreeColumn::GetColumns(nsITreeColumns** aColumns) {
148 NS_IF_ADDREF(*aColumns = mColumns);
149 return NS_OK;
150 }
151
152 NS_IMETHODIMP
GetX(int32_t * aX)153 nsTreeColumn::GetX(int32_t* aX) {
154 nsIFrame* frame = GetFrame();
155 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
156
157 *aX = nsPresContext::AppUnitsToIntCSSPixels(frame->GetRect().x);
158 return NS_OK;
159 }
160
161 NS_IMETHODIMP
GetWidth(int32_t * aWidth)162 nsTreeColumn::GetWidth(int32_t* aWidth) {
163 nsIFrame* frame = GetFrame();
164 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
165
166 *aWidth = nsPresContext::AppUnitsToIntCSSPixels(frame->GetRect().width);
167 return NS_OK;
168 }
169
170 NS_IMETHODIMP
GetId(nsAString & aId)171 nsTreeColumn::GetId(nsAString& aId) {
172 aId = GetId();
173 return NS_OK;
174 }
175
176 NS_IMETHODIMP
GetIdConst(const char16_t ** aIdConst)177 nsTreeColumn::GetIdConst(const char16_t** aIdConst) {
178 *aIdConst = mId.get();
179 return NS_OK;
180 }
181
182 NS_IMETHODIMP
GetIndex(int32_t * aIndex)183 nsTreeColumn::GetIndex(int32_t* aIndex) {
184 *aIndex = GetIndex();
185 return NS_OK;
186 }
187
188 NS_IMETHODIMP
GetPrimary(bool * aPrimary)189 nsTreeColumn::GetPrimary(bool* aPrimary) {
190 *aPrimary = IsPrimary();
191 return NS_OK;
192 }
193
194 NS_IMETHODIMP
GetCycler(bool * aCycler)195 nsTreeColumn::GetCycler(bool* aCycler) {
196 *aCycler = IsCycler();
197 return NS_OK;
198 }
199
200 NS_IMETHODIMP
GetEditable(bool * aEditable)201 nsTreeColumn::GetEditable(bool* aEditable) {
202 *aEditable = IsEditable();
203 return NS_OK;
204 }
205
206 NS_IMETHODIMP
GetSelectable(bool * aSelectable)207 nsTreeColumn::GetSelectable(bool* aSelectable) {
208 *aSelectable = IsSelectable();
209 return NS_OK;
210 }
211
212 NS_IMETHODIMP
GetType(int16_t * aType)213 nsTreeColumn::GetType(int16_t* aType) {
214 *aType = GetType();
215 return NS_OK;
216 }
217
218 NS_IMETHODIMP
GetNext(nsITreeColumn ** _retval)219 nsTreeColumn::GetNext(nsITreeColumn** _retval) {
220 NS_IF_ADDREF(*_retval = GetNext());
221 return NS_OK;
222 }
223
224 NS_IMETHODIMP
GetPrevious(nsITreeColumn ** _retval)225 nsTreeColumn::GetPrevious(nsITreeColumn** _retval) {
226 NS_IF_ADDREF(*_retval = GetPrevious());
227 return NS_OK;
228 }
229
230 NS_IMETHODIMP
Invalidate()231 nsTreeColumn::Invalidate() {
232 nsIFrame* frame = GetFrame();
233 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
234
235 // Fetch the Id.
236 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mId);
237
238 // If we have an Id, cache the Id as an atom.
239 if (!mId.IsEmpty()) {
240 mAtom = NS_Atomize(mId);
241 }
242
243 // Cache our index.
244 nsTreeUtils::GetColumnIndex(mContent->AsElement(), &mIndex);
245
246 const nsStyleVisibility* vis = frame->StyleVisibility();
247
248 // Cache our text alignment policy.
249 const nsStyleText* textStyle = frame->StyleText();
250
251 mTextAlignment = textStyle->mTextAlign;
252 // START or END alignment sometimes means RIGHT
253 if ((mTextAlignment == NS_STYLE_TEXT_ALIGN_START &&
254 vis->mDirection == NS_STYLE_DIRECTION_RTL) ||
255 (mTextAlignment == NS_STYLE_TEXT_ALIGN_END &&
256 vis->mDirection == NS_STYLE_DIRECTION_LTR)) {
257 mTextAlignment = NS_STYLE_TEXT_ALIGN_RIGHT;
258 } else if (mTextAlignment == NS_STYLE_TEXT_ALIGN_START ||
259 mTextAlignment == NS_STYLE_TEXT_ALIGN_END) {
260 mTextAlignment = NS_STYLE_TEXT_ALIGN_LEFT;
261 }
262
263 // Figure out if we're the primary column (that has to have indentation
264 // and twisties drawn.
265 mIsPrimary = mContent->AsElement()->AttrValueIs(
266 kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eCaseMatters);
267
268 // Figure out if we're a cycling column (one that doesn't cause a selection
269 // to happen).
270 mIsCycler = mContent->AsElement()->AttrValueIs(
271 kNameSpaceID_None, nsGkAtoms::cycler, nsGkAtoms::_true, eCaseMatters);
272
273 mIsEditable = mContent->AsElement()->AttrValueIs(
274 kNameSpaceID_None, nsGkAtoms::editable, nsGkAtoms::_true, eCaseMatters);
275
276 mIsSelectable = !mContent->AsElement()->AttrValueIs(
277 kNameSpaceID_None, nsGkAtoms::selectable, nsGkAtoms::_false,
278 eCaseMatters);
279
280 mOverflow = mContent->AsElement()->AttrValueIs(
281 kNameSpaceID_None, nsGkAtoms::overflow, nsGkAtoms::_true, eCaseMatters);
282
283 // Figure out our column type. Default type is text.
284 mType = nsITreeColumn::TYPE_TEXT;
285 static Element::AttrValuesArray typestrings[] = {
286 &nsGkAtoms::checkbox, &nsGkAtoms::password, nullptr};
287 switch (mContent->AsElement()->FindAttrValueIn(
288 kNameSpaceID_None, nsGkAtoms::type, typestrings, eCaseMatters)) {
289 case 0:
290 mType = nsITreeColumn::TYPE_CHECKBOX;
291 break;
292 case 1:
293 mType = nsITreeColumn::TYPE_PASSWORD;
294 break;
295 }
296
297 // Fetch the crop style.
298 mCropStyle = 0;
299 static Element::AttrValuesArray cropstrings[] = {
300 &nsGkAtoms::center, &nsGkAtoms::left, &nsGkAtoms::start, nullptr};
301 switch (mContent->AsElement()->FindAttrValueIn(
302 kNameSpaceID_None, nsGkAtoms::crop, cropstrings, eCaseMatters)) {
303 case 0:
304 mCropStyle = 1;
305 break;
306 case 1:
307 case 2:
308 mCropStyle = 2;
309 break;
310 }
311
312 return NS_OK;
313 }
314
GetParentObject() const315 nsIContent* nsTreeColumn::GetParentObject() const { return mContent; }
316
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)317 /* virtual */ JSObject* nsTreeColumn::WrapObject(
318 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
319 return dom::TreeColumnBinding::Wrap(aCx, this, aGivenProto);
320 }
321
GetElement(mozilla::ErrorResult & aRv)322 mozilla::dom::Element* nsTreeColumn::GetElement(mozilla::ErrorResult& aRv) {
323 nsCOMPtr<nsIDOMElement> element;
324 aRv = GetElement(getter_AddRefs(element));
325 if (aRv.Failed()) {
326 return nullptr;
327 }
328 nsCOMPtr<nsINode> node = do_QueryInterface(element);
329 return node->AsElement();
330 }
331
GetX(mozilla::ErrorResult & aRv)332 int32_t nsTreeColumn::GetX(mozilla::ErrorResult& aRv) {
333 int32_t x;
334 aRv = GetX(&x);
335 return x;
336 }
337
GetWidth(mozilla::ErrorResult & aRv)338 int32_t nsTreeColumn::GetWidth(mozilla::ErrorResult& aRv) {
339 int32_t width;
340 aRv = GetWidth(&width);
341 return width;
342 }
343
Invalidate(mozilla::ErrorResult & aRv)344 void nsTreeColumn::Invalidate(mozilla::ErrorResult& aRv) { aRv = Invalidate(); }
345
nsTreeColumns(nsTreeBodyFrame * aTree)346 nsTreeColumns::nsTreeColumns(nsTreeBodyFrame* aTree) : mTree(aTree) {}
347
~nsTreeColumns()348 nsTreeColumns::~nsTreeColumns() { nsTreeColumns::InvalidateColumns(); }
349
350 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsTreeColumns)
351
352 // QueryInterface implementation for nsTreeColumns
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumns)353 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeColumns)
354 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
355 NS_INTERFACE_MAP_ENTRY(nsITreeColumns)
356 NS_INTERFACE_MAP_ENTRY(nsISupports)
357 NS_INTERFACE_MAP_END
358
359 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeColumns)
360 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeColumns)
361
362 nsIContent* nsTreeColumns::GetParentObject() const {
363 return mTree ? mTree->GetBaseElement() : nullptr;
364 }
365
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)366 /* virtual */ JSObject* nsTreeColumns::WrapObject(
367 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
368 return dom::TreeColumnsBinding::Wrap(aCx, this, aGivenProto);
369 }
370
GetTree() const371 dom::TreeBoxObject* nsTreeColumns::GetTree() const {
372 return mTree ? static_cast<mozilla::dom::TreeBoxObject*>(
373 mTree->GetTreeBoxObject())
374 : nullptr;
375 }
376
377 NS_IMETHODIMP
GetTree(nsITreeBoxObject ** _retval)378 nsTreeColumns::GetTree(nsITreeBoxObject** _retval) {
379 NS_IF_ADDREF(*_retval = GetTree());
380 return NS_OK;
381 }
382
Count()383 uint32_t nsTreeColumns::Count() {
384 EnsureColumns();
385 uint32_t count = 0;
386 for (nsTreeColumn* currCol = mFirstColumn; currCol;
387 currCol = currCol->GetNext()) {
388 ++count;
389 }
390 return count;
391 }
392
393 NS_IMETHODIMP
GetCount(int32_t * _retval)394 nsTreeColumns::GetCount(int32_t* _retval) {
395 *_retval = Count();
396 return NS_OK;
397 }
398
399 NS_IMETHODIMP
GetLength(int32_t * _retval)400 nsTreeColumns::GetLength(int32_t* _retval) {
401 *_retval = Length();
402 return NS_OK;
403 }
404
405 NS_IMETHODIMP
GetFirstColumn(nsITreeColumn ** _retval)406 nsTreeColumns::GetFirstColumn(nsITreeColumn** _retval) {
407 NS_IF_ADDREF(*_retval = GetFirstColumn());
408 return NS_OK;
409 }
410
GetLastColumn()411 nsTreeColumn* nsTreeColumns::GetLastColumn() {
412 EnsureColumns();
413 nsTreeColumn* currCol = mFirstColumn;
414 while (currCol) {
415 nsTreeColumn* next = currCol->GetNext();
416 if (!next) {
417 return currCol;
418 }
419 currCol = next;
420 }
421 return nullptr;
422 }
423
424 NS_IMETHODIMP
GetLastColumn(nsITreeColumn ** _retval)425 nsTreeColumns::GetLastColumn(nsITreeColumn** _retval) {
426 NS_IF_ADDREF(*_retval = GetLastColumn());
427 return NS_OK;
428 }
429
430 NS_IMETHODIMP
GetPrimaryColumn(nsITreeColumn ** _retval)431 nsTreeColumns::GetPrimaryColumn(nsITreeColumn** _retval) {
432 NS_IF_ADDREF(*_retval = GetPrimaryColumn());
433 return NS_OK;
434 }
435
GetSortedColumn()436 nsTreeColumn* nsTreeColumns::GetSortedColumn() {
437 EnsureColumns();
438 for (nsTreeColumn* currCol = mFirstColumn; currCol;
439 currCol = currCol->GetNext()) {
440 if (currCol->mContent &&
441 nsContentUtils::HasNonEmptyAttr(currCol->mContent, kNameSpaceID_None,
442 nsGkAtoms::sortDirection)) {
443 return currCol;
444 }
445 }
446 return nullptr;
447 }
448
449 NS_IMETHODIMP
GetSortedColumn(nsITreeColumn ** _retval)450 nsTreeColumns::GetSortedColumn(nsITreeColumn** _retval) {
451 NS_IF_ADDREF(*_retval = GetSortedColumn());
452 return NS_OK;
453 }
454
GetKeyColumn()455 nsTreeColumn* nsTreeColumns::GetKeyColumn() {
456 EnsureColumns();
457
458 nsTreeColumn* first = nullptr;
459 nsTreeColumn* primary = nullptr;
460 nsTreeColumn* sorted = nullptr;
461
462 for (nsTreeColumn* currCol = mFirstColumn; currCol;
463 currCol = currCol->GetNext()) {
464 // Skip hidden columns.
465 if (!currCol->mContent || !currCol->mContent->IsElement() ||
466 currCol->mContent->AsElement()->AttrValueIs(
467 kNameSpaceID_None, nsGkAtoms::hidden, nsGkAtoms::_true,
468 eCaseMatters))
469 continue;
470
471 // Skip non-text column
472 if (currCol->GetType() != nsITreeColumn::TYPE_TEXT) continue;
473
474 if (!first) first = currCol;
475
476 if (nsContentUtils::HasNonEmptyAttr(currCol->mContent, kNameSpaceID_None,
477 nsGkAtoms::sortDirection)) {
478 // Use sorted column as the key.
479 sorted = currCol;
480 break;
481 }
482
483 if (currCol->IsPrimary())
484 if (!primary) primary = currCol;
485 }
486
487 if (sorted) return sorted;
488 if (primary) return primary;
489 return first;
490 }
491
492 NS_IMETHODIMP
GetKeyColumn(nsITreeColumn ** _retval)493 nsTreeColumns::GetKeyColumn(nsITreeColumn** _retval) {
494 NS_IF_ADDREF(*_retval = GetKeyColumn());
495 return NS_OK;
496 }
497
GetColumnFor(dom::Element * aElement)498 nsTreeColumn* nsTreeColumns::GetColumnFor(dom::Element* aElement) {
499 EnsureColumns();
500 for (nsTreeColumn* currCol = mFirstColumn; currCol;
501 currCol = currCol->GetNext()) {
502 if (currCol->mContent == aElement) {
503 return currCol;
504 }
505 }
506 return nullptr;
507 }
508
509 NS_IMETHODIMP
GetColumnFor(nsIDOMElement * aElement,nsITreeColumn ** _retval)510 nsTreeColumns::GetColumnFor(nsIDOMElement* aElement, nsITreeColumn** _retval) {
511 nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
512 NS_ADDREF(*_retval = GetColumnFor(element));
513 return NS_OK;
514 }
515
NamedGetter(const nsAString & aId,bool & aFound)516 nsTreeColumn* nsTreeColumns::NamedGetter(const nsAString& aId, bool& aFound) {
517 EnsureColumns();
518 for (nsTreeColumn* currCol = mFirstColumn; currCol;
519 currCol = currCol->GetNext()) {
520 if (currCol->GetId().Equals(aId)) {
521 aFound = true;
522 return currCol;
523 }
524 }
525 aFound = false;
526 return nullptr;
527 }
528
GetNamedColumn(const nsAString & aId)529 nsTreeColumn* nsTreeColumns::GetNamedColumn(const nsAString& aId) {
530 bool dummy;
531 return NamedGetter(aId, dummy);
532 }
533
534 NS_IMETHODIMP
GetNamedColumn(const nsAString & aId,nsITreeColumn ** _retval)535 nsTreeColumns::GetNamedColumn(const nsAString& aId, nsITreeColumn** _retval) {
536 NS_IF_ADDREF(*_retval = GetNamedColumn(aId));
537 return NS_OK;
538 }
539
GetSupportedNames(nsTArray<nsString> & aNames)540 void nsTreeColumns::GetSupportedNames(nsTArray<nsString>& aNames) {
541 for (nsTreeColumn* currCol = mFirstColumn; currCol;
542 currCol = currCol->GetNext()) {
543 aNames.AppendElement(currCol->GetId());
544 }
545 }
546
IndexedGetter(uint32_t aIndex,bool & aFound)547 nsTreeColumn* nsTreeColumns::IndexedGetter(uint32_t aIndex, bool& aFound) {
548 EnsureColumns();
549 for (nsTreeColumn* currCol = mFirstColumn; currCol;
550 currCol = currCol->GetNext()) {
551 if (currCol->GetIndex() == static_cast<int32_t>(aIndex)) {
552 aFound = true;
553 return currCol;
554 }
555 }
556 aFound = false;
557 return nullptr;
558 }
559
GetColumnAt(uint32_t aIndex)560 nsTreeColumn* nsTreeColumns::GetColumnAt(uint32_t aIndex) {
561 bool dummy;
562 return IndexedGetter(aIndex, dummy);
563 }
564
565 NS_IMETHODIMP
GetColumnAt(int32_t aIndex,nsITreeColumn ** _retval)566 nsTreeColumns::GetColumnAt(int32_t aIndex, nsITreeColumn** _retval) {
567 NS_IF_ADDREF(*_retval = GetColumnAt(static_cast<uint32_t>(aIndex)));
568 return NS_OK;
569 }
570
571 NS_IMETHODIMP
InvalidateColumns()572 nsTreeColumns::InvalidateColumns() {
573 for (nsTreeColumn* currCol = mFirstColumn; currCol;
574 currCol = currCol->GetNext()) {
575 currCol->SetColumns(nullptr);
576 }
577 mFirstColumn = nullptr;
578 return NS_OK;
579 }
580
581 NS_IMETHODIMP
RestoreNaturalOrder()582 nsTreeColumns::RestoreNaturalOrder() {
583 if (!mTree) return NS_OK;
584
585 nsIContent* content = mTree->GetBaseElement();
586
587 // Strong ref, since we'll be setting attributes
588 nsCOMPtr<nsIContent> colsContent =
589 nsTreeUtils::GetImmediateChild(content, nsGkAtoms::treecols);
590 if (!colsContent) return NS_OK;
591
592 int32_t i = 0;
593 for (nsINode* child = colsContent->GetFirstChild(); child;
594 child = child->GetNextSibling()) {
595 nsAutoString ordinal;
596 ordinal.AppendInt(i++);
597 if (child->IsElement()) {
598 child->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::ordinal,
599 ordinal, true);
600 }
601 }
602
603 nsTreeColumns::InvalidateColumns();
604
605 if (mTree) {
606 mTree->Invalidate();
607 }
608 return NS_OK;
609 }
610
GetPrimaryColumn()611 nsTreeColumn* nsTreeColumns::GetPrimaryColumn() {
612 EnsureColumns();
613 for (nsTreeColumn* currCol = mFirstColumn; currCol;
614 currCol = currCol->GetNext()) {
615 if (currCol->IsPrimary()) {
616 return currCol;
617 }
618 }
619 return nullptr;
620 }
621
EnsureColumns()622 void nsTreeColumns::EnsureColumns() {
623 if (mTree && !mFirstColumn) {
624 nsIContent* treeContent = mTree->GetBaseElement();
625 nsIContent* colsContent =
626 nsTreeUtils::GetDescendantChild(treeContent, nsGkAtoms::treecols);
627 if (!colsContent) return;
628
629 nsIContent* colContent =
630 nsTreeUtils::GetDescendantChild(colsContent, nsGkAtoms::treecol);
631 if (!colContent) return;
632
633 nsIFrame* colFrame = colContent->GetPrimaryFrame();
634 if (!colFrame) return;
635
636 colFrame = colFrame->GetParent();
637 if (!colFrame) return;
638
639 colFrame = colFrame->PrincipalChildList().FirstChild();
640 if (!colFrame) return;
641
642 // Now that we have the first visible column,
643 // we can enumerate the columns in visible order
644 nsTreeColumn* currCol = nullptr;
645 while (colFrame) {
646 nsIContent* colContent = colFrame->GetContent();
647
648 if (colContent->NodeInfo()->Equals(nsGkAtoms::treecol,
649 kNameSpaceID_XUL)) {
650 // Create a new column structure.
651 nsTreeColumn* col = new nsTreeColumn(this, colContent);
652 if (!col) return;
653
654 if (currCol) {
655 currCol->SetNext(col);
656 col->SetPrevious(currCol);
657 } else {
658 mFirstColumn = col;
659 }
660 currCol = col;
661 }
662
663 colFrame = colFrame->GetNextSibling();
664 }
665 }
666 }
667