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