Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
6 #include "mozilla/AsyncEventDispatcher.h"
7 #include "nsCOMPtr.h"
8 #include "nsTreeSelection.h"
9 #include "nsIBoxObject.h"
10 #include "nsITreeBoxObject.h"
11 #include "nsITreeView.h"
12 #include "nsString.h"
13 #include "nsIDOMElement.h"
14 #include "nsDOMClassInfoID.h"
15 #include "nsIContent.h"
16 #include "nsNameSpaceManager.h"
17 #include "nsGkAtoms.h"
18 #include "nsAutoPtr.h"
19 #include "nsComponentManagerUtils.h"
21 using namespace mozilla;
23 // A helper class for managing our ranges of selection.
24 struct nsTreeRange
25 {
26 nsTreeSelection* mSelection;
28 nsTreeRange* mPrev;
29 nsTreeRange* mNext;
31 int32_t mMin;
32 int32_t mMax;
34 nsTreeRange(nsTreeSelection* aSel, int32_t aSingleVal)
35 :mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aSingleVal), mMax(aSingleVal) {}
36 nsTreeRange(nsTreeSelection* aSel, int32_t aMin, int32_t aMax)
37 :mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aMin), mMax(aMax) {}
39 ~nsTreeRange() { delete mNext; }
41 void Connect(nsTreeRange* aPrev = nullptr, nsTreeRange* aNext = nullptr) {
42 if (aPrev)
43 aPrev->mNext = this;
44 else
45 mSelection->mFirstRange = this;
47 if (aNext)
48 aNext->mPrev = this;
50 mPrev = aPrev;
51 mNext = aNext;
52 }
54 nsresult RemoveRange(int32_t aStart, int32_t aEnd) {
55 // This should so be a loop... sigh...
56 // We start past the range to remove, so no more to remove
57 if (aEnd < mMin)
58 return NS_OK;
59 // We are the last range to be affected
60 if (aEnd < mMax) {
61 if (aStart <= mMin) {
62 // Just chop the start of the range off
63 mMin = aEnd + 1;
64 } else {
65 // We need to split the range
66 nsTreeRange* range = new nsTreeRange(mSelection, aEnd + 1, mMax);
67 if (!range)
68 return NS_ERROR_OUT_OF_MEMORY;
70 mMax = aStart - 1;
71 range->Connect(this, mNext);
72 }
73 return NS_OK;
74 }
75 nsTreeRange* next = mNext;
76 if (aStart <= mMin) {
77 // The remove includes us, remove ourselves from the list
78 if (mPrev)
79 mPrev->mNext = next;
80 else
81 mSelection->mFirstRange = next;
83 if (next)
84 next->mPrev = mPrev;
85 mPrev = mNext = nullptr;
86 delete this;
87 } else if (aStart <= mMax) {
88 // Just chop the end of the range off
89 mMax = aStart - 1;
90 }
91 return next ? next->RemoveRange(aStart, aEnd) : NS_OK;
92 }
94 nsresult Remove(int32_t aIndex) {
95 if (aIndex >= mMin && aIndex <= mMax) {
96 // We have found the range that contains us.
97 if (mMin == mMax) {
98 // Delete the whole range.
99 if (mPrev)
100 mPrev->mNext = mNext;
101 if (mNext)
102 mNext->mPrev = mPrev;
103 nsTreeRange* first = mSelection->mFirstRange;
104 if (first == this)
105 mSelection->mFirstRange = mNext;
106 mNext = mPrev = nullptr;
107 delete this;
108 }
109 else if (aIndex == mMin)
110 mMin++;
111 else if (aIndex == mMax)
112 mMax--;
113 else {
114 // We have to break this range.
115 nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex + 1, mMax);
116 if (!newRange)
117 return NS_ERROR_OUT_OF_MEMORY;
119 newRange->Connect(this, mNext);
120 mMax = aIndex - 1;
121 }
122 }
123 else if (mNext)
124 return mNext->Remove(aIndex);
126 return NS_OK;
127 }
129 nsresult Add(int32_t aIndex) {
130 if (aIndex < mMin) {
131 // We have found a spot to insert.
132 if (aIndex + 1 == mMin)
133 mMin = aIndex;
134 else if (mPrev && mPrev->mMax+1 == aIndex)
135 mPrev->mMax = aIndex;
136 else {
137 // We have to create a new range.
138 nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
139 if (!newRange)
140 return NS_ERROR_OUT_OF_MEMORY;
142 newRange->Connect(mPrev, this);
143 }
144 }
145 else if (mNext)
146 mNext->Add(aIndex);
147 else {
148 // Insert on to the end.
149 if (mMax+1 == aIndex)
150 mMax = aIndex;
151 else {
152 // We have to create a new range.
153 nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
154 if (!newRange)
155 return NS_ERROR_OUT_OF_MEMORY;
157 newRange->Connect(this, nullptr);
158 }
159 }
160 return NS_OK;
161 }
163 bool Contains(int32_t aIndex) {
164 if (aIndex >= mMin && aIndex <= mMax)
165 return true;
167 if (mNext)
168 return mNext->Contains(aIndex);
170 return false;
171 }
173 int32_t Count() {
174 int32_t total = mMax - mMin + 1;
175 if (mNext)
176 total += mNext->Count();
177 return total;
178 }
180 static void CollectRanges(nsTreeRange* aRange, nsTArray<int32_t>& aRanges)
181 {
182 nsTreeRange* cur = aRange;
183 while (cur) {
184 aRanges.AppendElement(cur->mMin);
185 aRanges.AppendElement(cur->mMax);
186 cur = cur->mNext;
187 }
188 }
190 static void InvalidateRanges(nsITreeBoxObject* aTree,
191 nsTArray<int32_t>& aRanges)
192 {
193 if (aTree) {
194 nsCOMPtr<nsITreeBoxObject> tree = aTree;
195 for (uint32_t i = 0; i < aRanges.Length(); i += 2) {
196 aTree->InvalidateRange(aRanges[i], aRanges[i + 1]);
197 }
198 }
199 }
201 void Invalidate() {
202 nsTArray<int32_t> ranges;
203 CollectRanges(this, ranges);
204 InvalidateRanges(mSelection->mTree, ranges);
206 }
208 void RemoveAllBut(int32_t aIndex) {
209 if (aIndex >= mMin && aIndex <= mMax) {
211 // Invalidate everything in this list.
212 nsTArray<int32_t> ranges;
213 CollectRanges(mSelection->mFirstRange, ranges);
215 mMin = aIndex;
216 mMax = aIndex;
218 nsTreeRange* first = mSelection->mFirstRange;
219 if (mPrev)
220 mPrev->mNext = mNext;
221 if (mNext)
222 mNext->mPrev = mPrev;
223 mNext = mPrev = nullptr;
225 if (first != this) {
226 delete mSelection->mFirstRange;
227 mSelection->mFirstRange = this;
228 }
229 InvalidateRanges(mSelection->mTree, ranges);
230 }
231 else if (mNext)
232 mNext->RemoveAllBut(aIndex);
233 }
235 void Insert(nsTreeRange* aRange) {
236 if (mMin >= aRange->mMax)
237 aRange->Connect(mPrev, this);
238 else if (mNext)
239 mNext->Insert(aRange);
240 else
241 aRange->Connect(this, nullptr);
242 }
243 };
245 nsTreeSelection::nsTreeSelection(nsITreeBoxObject* aTree)
246 : mTree(aTree),
247 mSuppressed(false),
248 mCurrentIndex(-1),
249 mShiftSelectPivot(-1),
250 mFirstRange(nullptr)
251 {
252 }
254 nsTreeSelection::~nsTreeSelection()
255 {
256 delete mFirstRange;
257 if (mSelectTimer)
258 mSelectTimer->Cancel();
259 }
261 NS_IMPL_CYCLE_COLLECTION(nsTreeSelection, mTree, mCurrentColumn)
263 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeSelection)
264 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeSelection)
266 DOMCI_DATA(TreeSelection, nsTreeSelection)
268 // QueryInterface implementation for nsBoxObject
269 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeSelection)
270 NS_INTERFACE_MAP_ENTRY(nsITreeSelection)
271 NS_INTERFACE_MAP_ENTRY(nsINativeTreeSelection)
272 NS_INTERFACE_MAP_ENTRY(nsISupports)
273 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeSelection)
274 NS_INTERFACE_MAP_END
276 NS_IMETHODIMP nsTreeSelection::GetTree(nsITreeBoxObject * *aTree)
277 {
278 NS_IF_ADDREF(*aTree = mTree);
279 return NS_OK;
280 }
282 NS_IMETHODIMP nsTreeSelection::SetTree(nsITreeBoxObject * aTree)
283 {
284 if (mSelectTimer) {
285 mSelectTimer->Cancel();
286 mSelectTimer = nullptr;
287 }
289 // Make sure aTree really implements nsITreeBoxObject and nsIBoxObject!
290 nsCOMPtr<nsIBoxObject> bo = do_QueryInterface(aTree);
291 mTree = do_QueryInterface(bo);
292 NS_ENSURE_STATE(mTree == aTree);
293 return NS_OK;
294 }
296 NS_IMETHODIMP nsTreeSelection::GetSingle(bool* aSingle)
297 {
298 if (!mTree)
299 return NS_ERROR_NULL_POINTER;
301 nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
303 nsCOMPtr<nsIDOMElement> element;
304 boxObject->GetElement(getter_AddRefs(element));
306 nsCOMPtr<nsIContent> content = do_QueryInterface(element);
308 static nsIContent::AttrValuesArray strings[] =
309 {&nsGkAtoms::single, &nsGkAtoms::cell, &nsGkAtoms::text, nullptr};
311 *aSingle = content->FindAttrValueIn(kNameSpaceID_None,
312 nsGkAtoms::seltype,
313 strings, eCaseMatters) >= 0;
315 return NS_OK;
316 }
318 NS_IMETHODIMP nsTreeSelection::IsSelected(int32_t aIndex, bool* aResult)
319 {
320 if (mFirstRange)
321 *aResult = mFirstRange->Contains(aIndex);
322 else
323 *aResult = false;
324 return NS_OK;
325 }
327 NS_IMETHODIMP nsTreeSelection::TimedSelect(int32_t aIndex, int32_t aMsec)
328 {
329 bool suppressSelect = mSuppressed;
331 if (aMsec != -1)
332 mSuppressed = true;
334 nsresult rv = Select(aIndex);
335 if (NS_FAILED(rv))
336 return rv;
338 if (aMsec != -1) {
339 mSuppressed = suppressSelect;
340 if (!mSuppressed) {
341 if (mSelectTimer)
342 mSelectTimer->Cancel();
344 mSelectTimer = do_CreateInstance("@mozilla.org/timer;1");
345 mSelectTimer->InitWithFuncCallback(SelectCallback, this, aMsec,
346 nsITimer::TYPE_ONE_SHOT);
347 }
348 }
350 return NS_OK;
351 }
353 NS_IMETHODIMP nsTreeSelection::Select(int32_t aIndex)
354 {
355 mShiftSelectPivot = -1;
357 nsresult rv = SetCurrentIndex(aIndex);
358 if (NS_FAILED(rv))
359 return rv;
361 if (mFirstRange) {
362 bool alreadySelected = mFirstRange->Contains(aIndex);
364 if (alreadySelected) {
365 int32_t count = mFirstRange->Count();
366 if (count > 1) {
367 // We need to deselect everything but our item.
368 mFirstRange->RemoveAllBut(aIndex);
369 FireOnSelectHandler();
370 }
371 return NS_OK;
372 }
373 else {
374 // Clear out our selection.
375 mFirstRange->Invalidate();
376 delete mFirstRange;
377 }
378 }
380 // Create our new selection.
381 mFirstRange = new nsTreeRange(this, aIndex);
382 if (!mFirstRange)
383 return NS_ERROR_OUT_OF_MEMORY;
385 mFirstRange->Invalidate();
387 // Fire the select event
388 FireOnSelectHandler();
389 return NS_OK;
390 }
392 NS_IMETHODIMP nsTreeSelection::ToggleSelect(int32_t aIndex)
393 {
394 // There are six cases that can occur on a ToggleSelect with our
395 // range code.
396 // (1) A new range should be made for a selection.
397 // (2) A single range is removed from the selection.
398 // (3) The item is added to an existing range.
399 // (4) The item is removed from an existing range.
400 // (5) The addition of the item causes two ranges to be merged.
401 // (6) The removal of the item causes two ranges to be split.
402 mShiftSelectPivot = -1;
403 nsresult rv = SetCurrentIndex(aIndex);
404 if (NS_FAILED(rv))
405 return rv;
407 if (!mFirstRange)
408 Select(aIndex);
409 else {
410 if (!mFirstRange->Contains(aIndex)) {
411 bool single;
412 rv = GetSingle(&single);
413 if (NS_SUCCEEDED(rv) && !single)
414 rv = mFirstRange->Add(aIndex);
415 }
416 else
417 rv = mFirstRange->Remove(aIndex);
418 if (NS_SUCCEEDED(rv)) {
419 if (mTree)
420 mTree->InvalidateRow(aIndex);
422 FireOnSelectHandler();
423 }
424 }
426 return rv;
427 }
429 NS_IMETHODIMP nsTreeSelection::RangedSelect(int32_t aStartIndex, int32_t aEndIndex, bool aAugment)
430 {
431 bool single;
432 nsresult rv = GetSingle(&single);
433 if (NS_FAILED(rv))
434 return rv;
436 if ((mFirstRange || (aStartIndex != aEndIndex)) && single)
437 return NS_OK;
439 if (!aAugment) {
440 // Clear our selection.
441 if (mFirstRange) {
442 mFirstRange->Invalidate();
443 delete mFirstRange;
444 mFirstRange = nullptr;
445 }
446 }
448 if (aStartIndex == -1) {
449 if (mShiftSelectPivot != -1)
450 aStartIndex = mShiftSelectPivot;
451 else if (mCurrentIndex != -1)
452 aStartIndex = mCurrentIndex;
453 else
454 aStartIndex = aEndIndex;
455 }
457 mShiftSelectPivot = aStartIndex;
458 rv = SetCurrentIndex(aEndIndex);
459 if (NS_FAILED(rv))
460 return rv;
462 int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
463 int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
465 if (aAugment && mFirstRange) {
466 // We need to remove all the items within our selected range from the selection,
467 // and then we insert our new range into the list.
468 nsresult rv = mFirstRange->RemoveRange(start, end);
469 if (NS_FAILED(rv))
470 return rv;
471 }
473 nsTreeRange* range = new nsTreeRange(this, start, end);
474 if (!range)
475 return NS_ERROR_OUT_OF_MEMORY;
477 range->Invalidate();
479 if (aAugment && mFirstRange)
480 mFirstRange->Insert(range);
481 else
482 mFirstRange = range;
484 FireOnSelectHandler();
486 return NS_OK;
487 }
489 NS_IMETHODIMP nsTreeSelection::ClearRange(int32_t aStartIndex, int32_t aEndIndex)
490 {
491 nsresult rv = SetCurrentIndex(aEndIndex);
492 if (NS_FAILED(rv))
493 return rv;
495 if (mFirstRange) {
496 int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
497 int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
499 mFirstRange->RemoveRange(start, end);
501 if (mTree)
502 mTree->InvalidateRange(start, end);
503 }
505 return NS_OK;
506 }
508 NS_IMETHODIMP nsTreeSelection::ClearSelection()
509 {
510 if (mFirstRange) {
511 mFirstRange->Invalidate();
512 delete mFirstRange;
513 mFirstRange = nullptr;
514 }
515 mShiftSelectPivot = -1;
517 FireOnSelectHandler();
519 return NS_OK;
520 }
522 NS_IMETHODIMP nsTreeSelection::InvertSelection()
523 {
524 return NS_ERROR_NOT_IMPLEMENTED;
525 }
527 NS_IMETHODIMP nsTreeSelection::SelectAll()
528 {
529 if (!mTree)
530 return NS_OK;
532 nsCOMPtr<nsITreeView> view;
533 mTree->GetView(getter_AddRefs(view));
534 if (!view)
535 return NS_OK;
537 int32_t rowCount;
538 view->GetRowCount(&rowCount);
539 bool single;
540 nsresult rv = GetSingle(&single);
541 if (NS_FAILED(rv))
542 return rv;
544 if (rowCount == 0 || (rowCount > 1 && single))
545 return NS_OK;
547 mShiftSelectPivot = -1;
549 // Invalidate not necessary when clearing selection, since
550 // we're going to invalidate the world on the SelectAll.
551 delete mFirstRange;
553 mFirstRange = new nsTreeRange(this, 0, rowCount-1);
554 mFirstRange->Invalidate();
556 FireOnSelectHandler();
558 return NS_OK;
559 }
561 NS_IMETHODIMP nsTreeSelection::GetRangeCount(int32_t* aResult)
562 {
563 int32_t count = 0;
564 nsTreeRange* curr = mFirstRange;
565 while (curr) {
566 count++;
567 curr = curr->mNext;
568 }
570 *aResult = count;
571 return NS_OK;
572 }
574 NS_IMETHODIMP nsTreeSelection::GetRangeAt(int32_t aIndex, int32_t* aMin, int32_t* aMax)
575 {
576 *aMin = *aMax = -1;
577 int32_t i = -1;
578 nsTreeRange* curr = mFirstRange;
579 while (curr) {
580 i++;
581 if (i == aIndex) {
582 *aMin = curr->mMin;
583 *aMax = curr->mMax;
584 break;
585 }
586 curr = curr->mNext;
587 }
589 return NS_OK;
590 }
592 NS_IMETHODIMP nsTreeSelection::GetCount(int32_t *count)
593 {
594 if (mFirstRange)
595 *count = mFirstRange->Count();
596 else // No range available, so there's no selected row.
597 *count = 0;
599 return NS_OK;
600 }
602 NS_IMETHODIMP nsTreeSelection::GetSelectEventsSuppressed(bool *aSelectEventsSuppressed)
603 {
604 *aSelectEventsSuppressed = mSuppressed;
605 return NS_OK;
606 }
608 NS_IMETHODIMP nsTreeSelection::SetSelectEventsSuppressed(bool aSelectEventsSuppressed)
609 {
610 mSuppressed = aSelectEventsSuppressed;
611 if (!mSuppressed)
612 FireOnSelectHandler();
613 return NS_OK;
614 }
616 NS_IMETHODIMP nsTreeSelection::GetCurrentIndex(int32_t *aCurrentIndex)
617 {
618 *aCurrentIndex = mCurrentIndex;
619 return NS_OK;
620 }
622 NS_IMETHODIMP nsTreeSelection::SetCurrentIndex(int32_t aIndex)
623 {
624 if (!mTree) {
625 return NS_ERROR_UNEXPECTED;
626 }
627 if (mCurrentIndex == aIndex) {
628 return NS_OK;
629 }
630 if (mCurrentIndex != -1 && mTree)
631 mTree->InvalidateRow(mCurrentIndex);
633 mCurrentIndex = aIndex;
634 if (!mTree)
635 return NS_OK;
637 if (aIndex != -1)
638 mTree->InvalidateRow(aIndex);
640 // Fire DOMMenuItemActive or DOMMenuItemInactive event for tree.
641 nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
642 NS_ASSERTION(boxObject, "no box object!");
643 if (!boxObject)
644 return NS_ERROR_UNEXPECTED;
645 nsCOMPtr<nsIDOMElement> treeElt;
646 boxObject->GetElement(getter_AddRefs(treeElt));
648 nsCOMPtr<nsINode> treeDOMNode(do_QueryInterface(treeElt));
649 NS_ENSURE_STATE(treeDOMNode);
651 NS_NAMED_LITERAL_STRING(DOMMenuItemActive, "DOMMenuItemActive");
652 NS_NAMED_LITERAL_STRING(DOMMenuItemInactive, "DOMMenuItemInactive");
654 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
655 new AsyncEventDispatcher(treeDOMNode,
656 (aIndex != -1 ? DOMMenuItemActive :
657 DOMMenuItemInactive),
658 true, false);
659 return asyncDispatcher->PostDOMEvent();
660 }
662 NS_IMETHODIMP nsTreeSelection::GetCurrentColumn(nsITreeColumn** aCurrentColumn)
663 {
664 NS_IF_ADDREF(*aCurrentColumn = mCurrentColumn);
665 return NS_OK;
666 }
668 NS_IMETHODIMP nsTreeSelection::SetCurrentColumn(nsITreeColumn* aCurrentColumn)
669 {
670 if (!mTree) {
671 return NS_ERROR_UNEXPECTED;
672 }
673 if (mCurrentColumn == aCurrentColumn) {
674 return NS_OK;
675 }
677 if (mCurrentColumn) {
678 if (mFirstRange)
679 mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn);
680 if (mCurrentIndex != -1)
681 mTree->InvalidateCell(mCurrentIndex, mCurrentColumn);
682 }
684 mCurrentColumn = aCurrentColumn;
686 if (mCurrentColumn) {
687 if (mFirstRange)
688 mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn);
689 if (mCurrentIndex != -1)
690 mTree->InvalidateCell(mCurrentIndex, mCurrentColumn);
691 }
693 return NS_OK;
694 }
696 #define ADD_NEW_RANGE(macro_range, macro_selection, macro_start, macro_end) \
697 { \
698 int32_t start = macro_start; \
699 int32_t end = macro_end; \
700 if (start > end) { \
701 end = start; \
702 } \
703 nsTreeRange* macro_new_range = new nsTreeRange(macro_selection, start, end); \
704 if (macro_range) \
705 macro_range->Insert(macro_new_range); \
706 else \
707 macro_range = macro_new_range; \
708 }
710 NS_IMETHODIMP
711 nsTreeSelection::AdjustSelection(int32_t aIndex, int32_t aCount)
712 {
713 NS_ASSERTION(aCount != 0, "adjusting by zero");
714 if (!aCount) return NS_OK;
716 // adjust mShiftSelectPivot, if necessary
717 if ((mShiftSelectPivot != 1) && (aIndex <= mShiftSelectPivot)) {
718 // if we are deleting and the delete includes the shift select pivot, reset it
719 if (aCount < 0 && (mShiftSelectPivot <= (aIndex -aCount -1))) {
720 mShiftSelectPivot = -1;
721 }
722 else {
723 mShiftSelectPivot += aCount;
724 }
725 }
727 // adjust mCurrentIndex, if necessary
728 if ((mCurrentIndex != -1) && (aIndex <= mCurrentIndex)) {
729 // if we are deleting and the delete includes the current index, reset it
730 if (aCount < 0 && (mCurrentIndex <= (aIndex -aCount -1))) {
731 mCurrentIndex = -1;
732 }
733 else {
734 mCurrentIndex += aCount;
735 }
736 }
738 // no selection, so nothing to do.
739 if (!mFirstRange) return NS_OK;
741 bool selChanged = false;
742 nsTreeRange* oldFirstRange = mFirstRange;
743 nsTreeRange* curr = mFirstRange;
744 mFirstRange = nullptr;
745 while (curr) {
746 if (aCount > 0) {
747 // inserting
748 if (aIndex > curr->mMax) {
749 // adjustment happens after the range, so no change
750 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
751 }
752 else if (aIndex <= curr->mMin) {
753 // adjustment happens before the start of the range, so shift down
754 ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount);
755 selChanged = true;
756 }
757 else {
758 // adjustment happen inside the range.
759 // break apart the range and create two ranges
760 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1);
761 ADD_NEW_RANGE(mFirstRange, this, aIndex + aCount, curr->mMax + aCount);
762 selChanged = true;
763 }
764 }
765 else {
766 // deleting
767 if (aIndex > curr->mMax) {
768 // adjustment happens after the range, so no change
769 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
770 }
771 else {
772 // remember, aCount is negative
773 selChanged = true;
774 int32_t lastIndexOfAdjustment = aIndex - aCount - 1;
775 if (aIndex <= curr->mMin) {
776 if (lastIndexOfAdjustment < curr->mMin) {
777 // adjustment happens before the start of the range, so shift up
778 ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount);
779 }
780 else if (lastIndexOfAdjustment >= curr->mMax) {
781 // adjustment contains the range. remove the range by not adding it to the newRange
782 }
783 else {
784 // adjustment starts before the range, and ends in the middle of it, so trim the range
785 ADD_NEW_RANGE(mFirstRange, this, aIndex, curr->mMax + aCount)
786 }
787 }
788 else if (lastIndexOfAdjustment >= curr->mMax) {
789 // adjustment starts in the middle of the current range, and contains the end of the range, so trim the range
790 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1)
791 }
792 else {
793 // range contains the adjustment, so shorten the range
794 ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax + aCount)
795 }
796 }
797 }
798 curr = curr->mNext;
799 }
801 delete oldFirstRange;
803 // Fire the select event
804 if (selChanged)
805 FireOnSelectHandler();
807 return NS_OK;
808 }
810 NS_IMETHODIMP
811 nsTreeSelection::InvalidateSelection()
812 {
813 if (mFirstRange)
814 mFirstRange->Invalidate();
815 return NS_OK;
816 }
818 NS_IMETHODIMP
819 nsTreeSelection::GetShiftSelectPivot(int32_t* aIndex)
820 {
821 *aIndex = mShiftSelectPivot;
822 return NS_OK;
823 }
826 nsresult
827 nsTreeSelection::FireOnSelectHandler()
828 {
829 if (mSuppressed || !mTree)
830 return NS_OK;
832 nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
833 NS_ASSERTION(boxObject, "no box object!");
834 if (!boxObject)
835 return NS_ERROR_UNEXPECTED;
836 nsCOMPtr<nsIDOMElement> elt;
837 boxObject->GetElement(getter_AddRefs(elt));
838 NS_ENSURE_STATE(elt);
840 nsCOMPtr<nsINode> node(do_QueryInterface(elt));
841 NS_ENSURE_STATE(node);
843 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
844 new AsyncEventDispatcher(node, NS_LITERAL_STRING("select"), true, false);
845 asyncDispatcher->RunDOMEventWhenSafe();
846 return NS_OK;
847 }
849 void
850 nsTreeSelection::SelectCallback(nsITimer *aTimer, void *aClosure)
851 {
852 nsRefPtr<nsTreeSelection> self = static_cast<nsTreeSelection*>(aClosure);
853 if (self) {
854 self->FireOnSelectHandler();
855 aTimer->Cancel();
856 self->mSelectTimer = nullptr;
857 }
858 }
860 ///////////////////////////////////////////////////////////////////////////////////
862 nsresult
863 NS_NewTreeSelection(nsITreeBoxObject* aTree, nsITreeSelection** aResult)
864 {
865 *aResult = new nsTreeSelection(aTree);
866 if (!*aResult)
867 return NS_ERROR_OUT_OF_MEMORY;
868 NS_ADDREF(*aResult);
869 return NS_OK;
870 }