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 "mozilla/AsyncEventDispatcher.h"
8 #include "mozilla/dom/Element.h"
9 #include "nsCOMPtr.h"
10 #include "nsTreeSelection.h"
11 #include "XULTreeElement.h"
12 #include "nsITreeView.h"
13 #include "nsString.h"
14 #include "nsIContent.h"
15 #include "nsNameSpaceManager.h"
16 #include "nsGkAtoms.h"
17 #include "nsComponentManagerUtils.h"
18 #include "nsTreeColumns.h"
19
20 using namespace mozilla;
21 using dom::XULTreeElement;
22
23 // A helper class for managing our ranges of selection.
24 struct nsTreeRange {
25 nsTreeSelection* mSelection;
26
27 nsTreeRange* mPrev;
28 nsTreeRange* mNext;
29
30 int32_t mMin;
31 int32_t mMax;
32
nsTreeRangensTreeRange33 nsTreeRange(nsTreeSelection* aSel, int32_t aSingleVal)
34 : mSelection(aSel),
35 mPrev(nullptr),
36 mNext(nullptr),
37 mMin(aSingleVal),
38 mMax(aSingleVal) {}
nsTreeRangensTreeRange39 nsTreeRange(nsTreeSelection* aSel, int32_t aMin, int32_t aMax)
40 : mSelection(aSel),
41 mPrev(nullptr),
42 mNext(nullptr),
43 mMin(aMin),
44 mMax(aMax) {}
45
~nsTreeRangensTreeRange46 ~nsTreeRange() { delete mNext; }
47
ConnectnsTreeRange48 void Connect(nsTreeRange* aPrev = nullptr, nsTreeRange* aNext = nullptr) {
49 if (aPrev)
50 aPrev->mNext = this;
51 else
52 mSelection->mFirstRange = this;
53
54 if (aNext) aNext->mPrev = this;
55
56 mPrev = aPrev;
57 mNext = aNext;
58 }
59
RemoveRangensTreeRange60 nsresult RemoveRange(int32_t aStart, int32_t aEnd) {
61 // This should so be a loop... sigh...
62 // We start past the range to remove, so no more to remove
63 if (aEnd < mMin) return NS_OK;
64 // We are the last range to be affected
65 if (aEnd < mMax) {
66 if (aStart <= mMin) {
67 // Just chop the start of the range off
68 mMin = aEnd + 1;
69 } else {
70 // We need to split the range
71 nsTreeRange* range = new nsTreeRange(mSelection, aEnd + 1, mMax);
72 if (!range) return NS_ERROR_OUT_OF_MEMORY;
73
74 mMax = aStart - 1;
75 range->Connect(this, mNext);
76 }
77 return NS_OK;
78 }
79 nsTreeRange* next = mNext;
80 if (aStart <= mMin) {
81 // The remove includes us, remove ourselves from the list
82 if (mPrev)
83 mPrev->mNext = next;
84 else
85 mSelection->mFirstRange = next;
86
87 if (next) next->mPrev = mPrev;
88 mPrev = mNext = nullptr;
89 delete this;
90 } else if (aStart <= mMax) {
91 // Just chop the end of the range off
92 mMax = aStart - 1;
93 }
94 return next ? next->RemoveRange(aStart, aEnd) : NS_OK;
95 }
96
RemovensTreeRange97 nsresult Remove(int32_t aIndex) {
98 if (aIndex >= mMin && aIndex <= mMax) {
99 // We have found the range that contains us.
100 if (mMin == mMax) {
101 // Delete the whole range.
102 if (mPrev) mPrev->mNext = mNext;
103 if (mNext) mNext->mPrev = mPrev;
104 nsTreeRange* first = mSelection->mFirstRange;
105 if (first == this) mSelection->mFirstRange = mNext;
106 mNext = mPrev = nullptr;
107 delete this;
108 } else if (aIndex == mMin)
109 mMin++;
110 else if (aIndex == mMax)
111 mMax--;
112 else {
113 // We have to break this range.
114 nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex + 1, mMax);
115 if (!newRange) return NS_ERROR_OUT_OF_MEMORY;
116
117 newRange->Connect(this, mNext);
118 mMax = aIndex - 1;
119 }
120 } else if (mNext)
121 return mNext->Remove(aIndex);
122
123 return NS_OK;
124 }
125
AddnsTreeRange126 nsresult Add(int32_t aIndex) {
127 if (aIndex < mMin) {
128 // We have found a spot to insert.
129 if (aIndex + 1 == mMin)
130 mMin = aIndex;
131 else if (mPrev && mPrev->mMax + 1 == aIndex)
132 mPrev->mMax = aIndex;
133 else {
134 // We have to create a new range.
135 nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
136 if (!newRange) return NS_ERROR_OUT_OF_MEMORY;
137
138 newRange->Connect(mPrev, this);
139 }
140 } else if (mNext)
141 mNext->Add(aIndex);
142 else {
143 // Insert on to the end.
144 if (mMax + 1 == aIndex)
145 mMax = aIndex;
146 else {
147 // We have to create a new range.
148 nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
149 if (!newRange) return NS_ERROR_OUT_OF_MEMORY;
150
151 newRange->Connect(this, nullptr);
152 }
153 }
154 return NS_OK;
155 }
156
ContainsnsTreeRange157 bool Contains(int32_t aIndex) {
158 if (aIndex >= mMin && aIndex <= mMax) return true;
159
160 if (mNext) return mNext->Contains(aIndex);
161
162 return false;
163 }
164
CountnsTreeRange165 int32_t Count() {
166 int32_t total = mMax - mMin + 1;
167 if (mNext) total += mNext->Count();
168 return total;
169 }
170
CollectRangesnsTreeRange171 static void CollectRanges(nsTreeRange* aRange, nsTArray<int32_t>& aRanges) {
172 nsTreeRange* cur = aRange;
173 while (cur) {
174 aRanges.AppendElement(cur->mMin);
175 aRanges.AppendElement(cur->mMax);
176 cur = cur->mNext;
177 }
178 }
179
InvalidateRangesnsTreeRange180 static void InvalidateRanges(XULTreeElement* aTree,
181 nsTArray<int32_t>& aRanges) {
182 if (aTree) {
183 RefPtr<nsXULElement> tree = aTree;
184 for (uint32_t i = 0; i < aRanges.Length(); i += 2) {
185 aTree->InvalidateRange(aRanges[i], aRanges[i + 1]);
186 }
187 }
188 }
189
InvalidatensTreeRange190 void Invalidate() {
191 nsTArray<int32_t> ranges;
192 CollectRanges(this, ranges);
193 InvalidateRanges(mSelection->mTree, ranges);
194 }
195
RemoveAllButnsTreeRange196 void RemoveAllBut(int32_t aIndex) {
197 if (aIndex >= mMin && aIndex <= mMax) {
198 // Invalidate everything in this list.
199 nsTArray<int32_t> ranges;
200 CollectRanges(mSelection->mFirstRange, ranges);
201
202 mMin = aIndex;
203 mMax = aIndex;
204
205 nsTreeRange* first = mSelection->mFirstRange;
206 if (mPrev) mPrev->mNext = mNext;
207 if (mNext) mNext->mPrev = mPrev;
208 mNext = mPrev = nullptr;
209
210 if (first != this) {
211 delete mSelection->mFirstRange;
212 mSelection->mFirstRange = this;
213 }
214 InvalidateRanges(mSelection->mTree, ranges);
215 } else if (mNext)
216 mNext->RemoveAllBut(aIndex);
217 }
218
InsertnsTreeRange219 void Insert(nsTreeRange* aRange) {
220 if (mMin >= aRange->mMax)
221 aRange->Connect(mPrev, this);
222 else if (mNext)
223 mNext->Insert(aRange);
224 else
225 aRange->Connect(this, nullptr);
226 }
227 };
228
nsTreeSelection(XULTreeElement * aTree)229 nsTreeSelection::nsTreeSelection(XULTreeElement* aTree)
230 : mTree(aTree),
231 mSuppressed(false),
232 mCurrentIndex(-1),
233 mShiftSelectPivot(-1),
234 mFirstRange(nullptr) {}
235
~nsTreeSelection()236 nsTreeSelection::~nsTreeSelection() {
237 delete mFirstRange;
238 if (mSelectTimer) mSelectTimer->Cancel();
239 }
240
NS_IMPL_CYCLE_COLLECTION(nsTreeSelection,mTree)241 NS_IMPL_CYCLE_COLLECTION(nsTreeSelection, mTree)
242
243 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeSelection)
244 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeSelection)
245
246 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeSelection)
247 NS_INTERFACE_MAP_ENTRY(nsITreeSelection)
248 NS_INTERFACE_MAP_ENTRY(nsINativeTreeSelection)
249 NS_INTERFACE_MAP_ENTRY(nsISupports)
250 NS_INTERFACE_MAP_END
251
252 NS_IMETHODIMP nsTreeSelection::GetTree(XULTreeElement** aTree) {
253 NS_IF_ADDREF(*aTree = mTree);
254 return NS_OK;
255 }
256
SetTree(XULTreeElement * aTree)257 NS_IMETHODIMP nsTreeSelection::SetTree(XULTreeElement* aTree) {
258 if (mSelectTimer) {
259 mSelectTimer->Cancel();
260 mSelectTimer = nullptr;
261 }
262
263 mTree = aTree;
264 return NS_OK;
265 }
266
GetSingle(bool * aSingle)267 NS_IMETHODIMP nsTreeSelection::GetSingle(bool* aSingle) {
268 if (!mTree) {
269 return NS_ERROR_NULL_POINTER;
270 }
271
272 *aSingle = mTree->AttrValueIs(kNameSpaceID_None, nsGkAtoms::seltype,
273 u"single"_ns, eCaseMatters);
274
275 return NS_OK;
276 }
277
IsSelected(int32_t aIndex,bool * aResult)278 NS_IMETHODIMP nsTreeSelection::IsSelected(int32_t aIndex, bool* aResult) {
279 if (mFirstRange)
280 *aResult = mFirstRange->Contains(aIndex);
281 else
282 *aResult = false;
283 return NS_OK;
284 }
285
TimedSelect(int32_t aIndex,int32_t aMsec)286 NS_IMETHODIMP nsTreeSelection::TimedSelect(int32_t aIndex, int32_t aMsec) {
287 bool suppressSelect = mSuppressed;
288
289 if (aMsec != -1) mSuppressed = true;
290
291 nsresult rv = Select(aIndex);
292 if (NS_FAILED(rv)) return rv;
293
294 if (aMsec != -1) {
295 mSuppressed = suppressSelect;
296 if (!mSuppressed) {
297 if (mSelectTimer) mSelectTimer->Cancel();
298
299 nsIEventTarget* target =
300 mTree->OwnerDoc()->EventTargetFor(TaskCategory::Other);
301 NS_NewTimerWithFuncCallback(getter_AddRefs(mSelectTimer), SelectCallback,
302 this, aMsec, nsITimer::TYPE_ONE_SHOT,
303 "nsTreeSelection::SelectCallback", target);
304 }
305 }
306
307 return NS_OK;
308 }
309
Select(int32_t aIndex)310 NS_IMETHODIMP nsTreeSelection::Select(int32_t aIndex) {
311 mShiftSelectPivot = -1;
312
313 nsresult rv = SetCurrentIndex(aIndex);
314 if (NS_FAILED(rv)) return rv;
315
316 if (mFirstRange) {
317 bool alreadySelected = mFirstRange->Contains(aIndex);
318
319 if (alreadySelected) {
320 int32_t count = mFirstRange->Count();
321 if (count > 1) {
322 // We need to deselect everything but our item.
323 mFirstRange->RemoveAllBut(aIndex);
324 FireOnSelectHandler();
325 }
326 return NS_OK;
327 } else {
328 // Clear out our selection.
329 mFirstRange->Invalidate();
330 delete mFirstRange;
331 }
332 }
333
334 // Create our new selection.
335 mFirstRange = new nsTreeRange(this, aIndex);
336 if (!mFirstRange) return NS_ERROR_OUT_OF_MEMORY;
337
338 mFirstRange->Invalidate();
339
340 // Fire the select event
341 FireOnSelectHandler();
342 return NS_OK;
343 }
344
ToggleSelect(int32_t aIndex)345 NS_IMETHODIMP nsTreeSelection::ToggleSelect(int32_t aIndex) {
346 // There are six cases that can occur on a ToggleSelect with our
347 // range code.
348 // (1) A new range should be made for a selection.
349 // (2) A single range is removed from the selection.
350 // (3) The item is added to an existing range.
351 // (4) The item is removed from an existing range.
352 // (5) The addition of the item causes two ranges to be merged.
353 // (6) The removal of the item causes two ranges to be split.
354 mShiftSelectPivot = -1;
355 nsresult rv = SetCurrentIndex(aIndex);
356 if (NS_FAILED(rv)) return rv;
357
358 if (!mFirstRange)
359 Select(aIndex);
360 else {
361 if (!mFirstRange->Contains(aIndex)) {
362 bool single;
363 rv = GetSingle(&single);
364 if (NS_SUCCEEDED(rv) && !single) rv = mFirstRange->Add(aIndex);
365 } else
366 rv = mFirstRange->Remove(aIndex);
367 if (NS_SUCCEEDED(rv)) {
368 if (mTree) mTree->InvalidateRow(aIndex);
369
370 FireOnSelectHandler();
371 }
372 }
373
374 return rv;
375 }
376
RangedSelect(int32_t aStartIndex,int32_t aEndIndex,bool aAugment)377 NS_IMETHODIMP nsTreeSelection::RangedSelect(int32_t aStartIndex,
378 int32_t aEndIndex, bool aAugment) {
379 bool single;
380 nsresult rv = GetSingle(&single);
381 if (NS_FAILED(rv)) return rv;
382
383 if ((mFirstRange || (aStartIndex != aEndIndex)) && single) return NS_OK;
384
385 if (!aAugment) {
386 // Clear our selection.
387 if (mFirstRange) {
388 mFirstRange->Invalidate();
389 delete mFirstRange;
390 mFirstRange = nullptr;
391 }
392 }
393
394 if (aStartIndex == -1) {
395 if (mShiftSelectPivot != -1)
396 aStartIndex = mShiftSelectPivot;
397 else if (mCurrentIndex != -1)
398 aStartIndex = mCurrentIndex;
399 else
400 aStartIndex = aEndIndex;
401 }
402
403 mShiftSelectPivot = aStartIndex;
404 rv = SetCurrentIndex(aEndIndex);
405 if (NS_FAILED(rv)) return rv;
406
407 int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
408 int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
409
410 if (aAugment && mFirstRange) {
411 // We need to remove all the items within our selected range from the
412 // selection, and then we insert our new range into the list.
413 nsresult rv = mFirstRange->RemoveRange(start, end);
414 if (NS_FAILED(rv)) return rv;
415 }
416
417 nsTreeRange* range = new nsTreeRange(this, start, end);
418 if (!range) return NS_ERROR_OUT_OF_MEMORY;
419
420 range->Invalidate();
421
422 if (aAugment && mFirstRange)
423 mFirstRange->Insert(range);
424 else
425 mFirstRange = range;
426
427 FireOnSelectHandler();
428
429 return NS_OK;
430 }
431
ClearRange(int32_t aStartIndex,int32_t aEndIndex)432 NS_IMETHODIMP nsTreeSelection::ClearRange(int32_t aStartIndex,
433 int32_t aEndIndex) {
434 nsresult rv = SetCurrentIndex(aEndIndex);
435 if (NS_FAILED(rv)) return rv;
436
437 if (mFirstRange) {
438 int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
439 int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
440
441 mFirstRange->RemoveRange(start, end);
442
443 if (mTree) mTree->InvalidateRange(start, end);
444 }
445
446 return NS_OK;
447 }
448
ClearSelection()449 NS_IMETHODIMP nsTreeSelection::ClearSelection() {
450 if (mFirstRange) {
451 mFirstRange->Invalidate();
452 delete mFirstRange;
453 mFirstRange = nullptr;
454 }
455 mShiftSelectPivot = -1;
456
457 FireOnSelectHandler();
458
459 return NS_OK;
460 }
461
SelectAll()462 NS_IMETHODIMP nsTreeSelection::SelectAll() {
463 if (!mTree) return NS_OK;
464
465 nsCOMPtr<nsITreeView> view = mTree->GetView();
466 if (!view) return NS_OK;
467
468 int32_t rowCount;
469 view->GetRowCount(&rowCount);
470 bool single;
471 nsresult rv = GetSingle(&single);
472 if (NS_FAILED(rv)) return rv;
473
474 if (rowCount == 0 || (rowCount > 1 && single)) return NS_OK;
475
476 mShiftSelectPivot = -1;
477
478 // Invalidate not necessary when clearing selection, since
479 // we're going to invalidate the world on the SelectAll.
480 delete mFirstRange;
481
482 mFirstRange = new nsTreeRange(this, 0, rowCount - 1);
483 mFirstRange->Invalidate();
484
485 FireOnSelectHandler();
486
487 return NS_OK;
488 }
489
GetRangeCount(int32_t * aResult)490 NS_IMETHODIMP nsTreeSelection::GetRangeCount(int32_t* aResult) {
491 int32_t count = 0;
492 nsTreeRange* curr = mFirstRange;
493 while (curr) {
494 count++;
495 curr = curr->mNext;
496 }
497
498 *aResult = count;
499 return NS_OK;
500 }
501
GetRangeAt(int32_t aIndex,int32_t * aMin,int32_t * aMax)502 NS_IMETHODIMP nsTreeSelection::GetRangeAt(int32_t aIndex, int32_t* aMin,
503 int32_t* aMax) {
504 *aMin = *aMax = -1;
505 int32_t i = -1;
506 nsTreeRange* curr = mFirstRange;
507 while (curr) {
508 i++;
509 if (i == aIndex) {
510 *aMin = curr->mMin;
511 *aMax = curr->mMax;
512 break;
513 }
514 curr = curr->mNext;
515 }
516
517 return NS_OK;
518 }
519
GetCount(int32_t * count)520 NS_IMETHODIMP nsTreeSelection::GetCount(int32_t* count) {
521 if (mFirstRange)
522 *count = mFirstRange->Count();
523 else // No range available, so there's no selected row.
524 *count = 0;
525
526 return NS_OK;
527 }
528
GetSelectEventsSuppressed(bool * aSelectEventsSuppressed)529 NS_IMETHODIMP nsTreeSelection::GetSelectEventsSuppressed(
530 bool* aSelectEventsSuppressed) {
531 *aSelectEventsSuppressed = mSuppressed;
532 return NS_OK;
533 }
534
SetSelectEventsSuppressed(bool aSelectEventsSuppressed)535 NS_IMETHODIMP nsTreeSelection::SetSelectEventsSuppressed(
536 bool aSelectEventsSuppressed) {
537 mSuppressed = aSelectEventsSuppressed;
538 if (!mSuppressed) FireOnSelectHandler();
539 return NS_OK;
540 }
541
GetCurrentIndex(int32_t * aCurrentIndex)542 NS_IMETHODIMP nsTreeSelection::GetCurrentIndex(int32_t* aCurrentIndex) {
543 *aCurrentIndex = mCurrentIndex;
544 return NS_OK;
545 }
546
SetCurrentIndex(int32_t aIndex)547 NS_IMETHODIMP nsTreeSelection::SetCurrentIndex(int32_t aIndex) {
548 if (!mTree) {
549 return NS_ERROR_UNEXPECTED;
550 }
551 if (mCurrentIndex == aIndex) {
552 return NS_OK;
553 }
554 if (mCurrentIndex != -1 && mTree) mTree->InvalidateRow(mCurrentIndex);
555
556 mCurrentIndex = aIndex;
557 if (!mTree) return NS_OK;
558
559 if (aIndex != -1) mTree->InvalidateRow(aIndex);
560
561 // Fire DOMMenuItemActive or DOMMenuItemInactive event for tree.
562 NS_ENSURE_STATE(mTree);
563
564 constexpr auto DOMMenuItemActive = u"DOMMenuItemActive"_ns;
565 constexpr auto DOMMenuItemInactive = u"DOMMenuItemInactive"_ns;
566
567 RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
568 mTree, (aIndex != -1 ? DOMMenuItemActive : DOMMenuItemInactive),
569 CanBubble::eYes, ChromeOnlyDispatch::eNo);
570 return asyncDispatcher->PostDOMEvent();
571 }
572
573 #define ADD_NEW_RANGE(macro_range, macro_selection, macro_start, macro_end) \
574 { \
575 int32_t start = macro_start; \
576 int32_t end = macro_end; \
577 if (start > end) { \
578 end = start; \
579 } \
580 nsTreeRange* macro_new_range = \
581 new nsTreeRange(macro_selection, start, end); \
582 if (macro_range) \
583 macro_range->Insert(macro_new_range); \
584 else \
585 macro_range = macro_new_range; \
586 }
587
588 NS_IMETHODIMP
AdjustSelection(int32_t aIndex,int32_t aCount)589 nsTreeSelection::AdjustSelection(int32_t aIndex, int32_t aCount) {
590 NS_ASSERTION(aCount != 0, "adjusting by zero");
591 if (!aCount) return NS_OK;
592
593 // adjust mShiftSelectPivot, if necessary
594 if ((mShiftSelectPivot != 1) && (aIndex <= mShiftSelectPivot)) {
595 // if we are deleting and the delete includes the shift select pivot, reset
596 // it
597 if (aCount < 0 && (mShiftSelectPivot <= (aIndex - aCount - 1))) {
598 mShiftSelectPivot = -1;
599 } else {
600 mShiftSelectPivot += aCount;
601 }
602 }
603
604 // adjust mCurrentIndex, if necessary
605 if ((mCurrentIndex != -1) && (aIndex <= mCurrentIndex)) {
606 // if we are deleting and the delete includes the current index, reset it
607 if (aCount < 0 && (mCurrentIndex <= (aIndex - aCount - 1))) {
608 mCurrentIndex = -1;
609 } else {
610 mCurrentIndex += aCount;
611 }
612 }
613
614 // no selection, so nothing to do.
615 if (!mFirstRange) return NS_OK;
616
617 bool selChanged = false;
618 nsTreeRange* oldFirstRange = mFirstRange;
619 nsTreeRange* curr = mFirstRange;
620 mFirstRange = nullptr;
621 while (curr) {
622 if (aCount > 0) {
623 // inserting
624 if (aIndex > curr->mMax) {
625 // adjustment happens after the range, so no change
626 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
627 } else if (aIndex <= curr->mMin) {
628 // adjustment happens before the start of the range, so shift down
629 ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount,
630 curr->mMax + aCount);
631 selChanged = true;
632 } else {
633 // adjustment happen inside the range.
634 // break apart the range and create two ranges
635 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1);
636 ADD_NEW_RANGE(mFirstRange, this, aIndex + aCount, curr->mMax + aCount);
637 selChanged = true;
638 }
639 } else {
640 // deleting
641 if (aIndex > curr->mMax) {
642 // adjustment happens after the range, so no change
643 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
644 } else {
645 // remember, aCount is negative
646 selChanged = true;
647 int32_t lastIndexOfAdjustment = aIndex - aCount - 1;
648 if (aIndex <= curr->mMin) {
649 if (lastIndexOfAdjustment < curr->mMin) {
650 // adjustment happens before the start of the range, so shift up
651 ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount,
652 curr->mMax + aCount);
653 } else if (lastIndexOfAdjustment >= curr->mMax) {
654 // adjustment contains the range. remove the range by not adding it
655 // to the newRange
656 } else {
657 // adjustment starts before the range, and ends in the middle of it,
658 // so trim the range
659 ADD_NEW_RANGE(mFirstRange, this, aIndex, curr->mMax + aCount)
660 }
661 } else if (lastIndexOfAdjustment >= curr->mMax) {
662 // adjustment starts in the middle of the current range, and contains
663 // the end of the range, so trim the range
664 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1)
665 } else {
666 // range contains the adjustment, so shorten the range
667 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax + aCount)
668 }
669 }
670 }
671 curr = curr->mNext;
672 }
673
674 delete oldFirstRange;
675
676 // Fire the select event
677 if (selChanged) FireOnSelectHandler();
678
679 return NS_OK;
680 }
681
682 NS_IMETHODIMP
InvalidateSelection()683 nsTreeSelection::InvalidateSelection() {
684 if (mFirstRange) mFirstRange->Invalidate();
685 return NS_OK;
686 }
687
688 NS_IMETHODIMP
GetShiftSelectPivot(int32_t * aIndex)689 nsTreeSelection::GetShiftSelectPivot(int32_t* aIndex) {
690 *aIndex = mShiftSelectPivot;
691 return NS_OK;
692 }
693
FireOnSelectHandler()694 nsresult nsTreeSelection::FireOnSelectHandler() {
695 if (mSuppressed || !mTree) return NS_OK;
696
697 RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
698 mTree, u"select"_ns, CanBubble::eYes, ChromeOnlyDispatch::eNo);
699 asyncDispatcher->RunDOMEventWhenSafe();
700 return NS_OK;
701 }
702
SelectCallback(nsITimer * aTimer,void * aClosure)703 void nsTreeSelection::SelectCallback(nsITimer* aTimer, void* aClosure) {
704 RefPtr<nsTreeSelection> self = static_cast<nsTreeSelection*>(aClosure);
705 if (self) {
706 self->FireOnSelectHandler();
707 aTimer->Cancel();
708 self->mSelectTimer = nullptr;
709 }
710 }
711
712 ///////////////////////////////////////////////////////////////////////////////////
713
NS_NewTreeSelection(XULTreeElement * aTree,nsITreeSelection ** aResult)714 nsresult NS_NewTreeSelection(XULTreeElement* aTree,
715 nsITreeSelection** aResult) {
716 *aResult = new nsTreeSelection(aTree);
717 if (!*aResult) return NS_ERROR_OUT_OF_MEMORY;
718 NS_ADDREF(*aResult);
719 return NS_OK;
720 }
721