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