LCOV - code coverage report
Current view: top level - layout/xul/tree - nsTreeSelection.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 476 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 50 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13