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