123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "mozilla/AsyncEventDispatcher.h"
- #include "nsCOMPtr.h"
- #include "nsTreeSelection.h"
- #include "nsIBoxObject.h"
- #include "nsITreeBoxObject.h"
- #include "nsITreeView.h"
- #include "nsString.h"
- #include "nsIDOMElement.h"
- #include "nsDOMClassInfoID.h"
- #include "nsIContent.h"
- #include "nsNameSpaceManager.h"
- #include "nsGkAtoms.h"
- #include "nsComponentManagerUtils.h"
- using namespace mozilla;
- // A helper class for managing our ranges of selection.
- struct nsTreeRange
- {
- nsTreeSelection* mSelection;
- nsTreeRange* mPrev;
- nsTreeRange* mNext;
- int32_t mMin;
- int32_t mMax;
- nsTreeRange(nsTreeSelection* aSel, int32_t aSingleVal)
- :mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aSingleVal), mMax(aSingleVal) {}
- nsTreeRange(nsTreeSelection* aSel, int32_t aMin, int32_t aMax)
- :mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aMin), mMax(aMax) {}
- ~nsTreeRange() { delete mNext; }
- void Connect(nsTreeRange* aPrev = nullptr, nsTreeRange* aNext = nullptr) {
- if (aPrev)
- aPrev->mNext = this;
- else
- mSelection->mFirstRange = this;
- if (aNext)
- aNext->mPrev = this;
- mPrev = aPrev;
- mNext = aNext;
- }
- nsresult RemoveRange(int32_t aStart, int32_t aEnd) {
- // This should so be a loop... sigh...
- // We start past the range to remove, so no more to remove
- if (aEnd < mMin)
- return NS_OK;
- // We are the last range to be affected
- if (aEnd < mMax) {
- if (aStart <= mMin) {
- // Just chop the start of the range off
- mMin = aEnd + 1;
- } else {
- // We need to split the range
- nsTreeRange* range = new nsTreeRange(mSelection, aEnd + 1, mMax);
- if (!range)
- return NS_ERROR_OUT_OF_MEMORY;
- mMax = aStart - 1;
- range->Connect(this, mNext);
- }
- return NS_OK;
- }
- nsTreeRange* next = mNext;
- if (aStart <= mMin) {
- // The remove includes us, remove ourselves from the list
- if (mPrev)
- mPrev->mNext = next;
- else
- mSelection->mFirstRange = next;
- if (next)
- next->mPrev = mPrev;
- mPrev = mNext = nullptr;
- delete this;
- } else if (aStart <= mMax) {
- // Just chop the end of the range off
- mMax = aStart - 1;
- }
- return next ? next->RemoveRange(aStart, aEnd) : NS_OK;
- }
- nsresult Remove(int32_t aIndex) {
- if (aIndex >= mMin && aIndex <= mMax) {
- // We have found the range that contains us.
- if (mMin == mMax) {
- // Delete the whole range.
- if (mPrev)
- mPrev->mNext = mNext;
- if (mNext)
- mNext->mPrev = mPrev;
- nsTreeRange* first = mSelection->mFirstRange;
- if (first == this)
- mSelection->mFirstRange = mNext;
- mNext = mPrev = nullptr;
- delete this;
- }
- else if (aIndex == mMin)
- mMin++;
- else if (aIndex == mMax)
- mMax--;
- else {
- // We have to break this range.
- nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex + 1, mMax);
- if (!newRange)
- return NS_ERROR_OUT_OF_MEMORY;
- newRange->Connect(this, mNext);
- mMax = aIndex - 1;
- }
- }
- else if (mNext)
- return mNext->Remove(aIndex);
- return NS_OK;
- }
- nsresult Add(int32_t aIndex) {
- if (aIndex < mMin) {
- // We have found a spot to insert.
- if (aIndex + 1 == mMin)
- mMin = aIndex;
- else if (mPrev && mPrev->mMax+1 == aIndex)
- mPrev->mMax = aIndex;
- else {
- // We have to create a new range.
- nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
- if (!newRange)
- return NS_ERROR_OUT_OF_MEMORY;
- newRange->Connect(mPrev, this);
- }
- }
- else if (mNext)
- mNext->Add(aIndex);
- else {
- // Insert on to the end.
- if (mMax+1 == aIndex)
- mMax = aIndex;
- else {
- // We have to create a new range.
- nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex);
- if (!newRange)
- return NS_ERROR_OUT_OF_MEMORY;
- newRange->Connect(this, nullptr);
- }
- }
- return NS_OK;
- }
- bool Contains(int32_t aIndex) {
- if (aIndex >= mMin && aIndex <= mMax)
- return true;
- if (mNext)
- return mNext->Contains(aIndex);
- return false;
- }
- int32_t Count() {
- int32_t total = mMax - mMin + 1;
- if (mNext)
- total += mNext->Count();
- return total;
- }
- static void CollectRanges(nsTreeRange* aRange, nsTArray<int32_t>& aRanges)
- {
- nsTreeRange* cur = aRange;
- while (cur) {
- aRanges.AppendElement(cur->mMin);
- aRanges.AppendElement(cur->mMax);
- cur = cur->mNext;
- }
- }
-
- static void InvalidateRanges(nsITreeBoxObject* aTree,
- nsTArray<int32_t>& aRanges)
- {
- if (aTree) {
- nsCOMPtr<nsITreeBoxObject> tree = aTree;
- for (uint32_t i = 0; i < aRanges.Length(); i += 2) {
- aTree->InvalidateRange(aRanges[i], aRanges[i + 1]);
- }
- }
- }
- void Invalidate() {
- nsTArray<int32_t> ranges;
- CollectRanges(this, ranges);
- InvalidateRanges(mSelection->mTree, ranges);
-
- }
- void RemoveAllBut(int32_t aIndex) {
- if (aIndex >= mMin && aIndex <= mMax) {
- // Invalidate everything in this list.
- nsTArray<int32_t> ranges;
- CollectRanges(mSelection->mFirstRange, ranges);
- mMin = aIndex;
- mMax = aIndex;
-
- nsTreeRange* first = mSelection->mFirstRange;
- if (mPrev)
- mPrev->mNext = mNext;
- if (mNext)
- mNext->mPrev = mPrev;
- mNext = mPrev = nullptr;
-
- if (first != this) {
- delete mSelection->mFirstRange;
- mSelection->mFirstRange = this;
- }
- InvalidateRanges(mSelection->mTree, ranges);
- }
- else if (mNext)
- mNext->RemoveAllBut(aIndex);
- }
- void Insert(nsTreeRange* aRange) {
- if (mMin >= aRange->mMax)
- aRange->Connect(mPrev, this);
- else if (mNext)
- mNext->Insert(aRange);
- else
- aRange->Connect(this, nullptr);
- }
- };
- nsTreeSelection::nsTreeSelection(nsITreeBoxObject* aTree)
- : mTree(aTree),
- mSuppressed(false),
- mCurrentIndex(-1),
- mShiftSelectPivot(-1),
- mFirstRange(nullptr)
- {
- }
- nsTreeSelection::~nsTreeSelection()
- {
- delete mFirstRange;
- if (mSelectTimer)
- mSelectTimer->Cancel();
- }
- NS_IMPL_CYCLE_COLLECTION(nsTreeSelection, mTree, mCurrentColumn)
- NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeSelection)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeSelection)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeSelection)
- NS_INTERFACE_MAP_ENTRY(nsITreeSelection)
- NS_INTERFACE_MAP_ENTRY(nsINativeTreeSelection)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
- NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeSelection)
- NS_INTERFACE_MAP_END
- NS_IMETHODIMP nsTreeSelection::GetTree(nsITreeBoxObject * *aTree)
- {
- NS_IF_ADDREF(*aTree = mTree);
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::SetTree(nsITreeBoxObject * aTree)
- {
- if (mSelectTimer) {
- mSelectTimer->Cancel();
- mSelectTimer = nullptr;
- }
- // Make sure aTree really implements nsITreeBoxObject and nsIBoxObject!
- nsCOMPtr<nsIBoxObject> bo = do_QueryInterface(aTree);
- mTree = do_QueryInterface(bo);
- NS_ENSURE_STATE(mTree == aTree);
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::GetSingle(bool* aSingle)
- {
- if (!mTree)
- return NS_ERROR_NULL_POINTER;
- nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
- nsCOMPtr<nsIDOMElement> element;
- boxObject->GetElement(getter_AddRefs(element));
- nsCOMPtr<nsIContent> content = do_QueryInterface(element);
- static nsIContent::AttrValuesArray strings[] =
- {&nsGkAtoms::single, &nsGkAtoms::cell, &nsGkAtoms::text, nullptr};
- *aSingle = content->FindAttrValueIn(kNameSpaceID_None,
- nsGkAtoms::seltype,
- strings, eCaseMatters) >= 0;
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::IsSelected(int32_t aIndex, bool* aResult)
- {
- if (mFirstRange)
- *aResult = mFirstRange->Contains(aIndex);
- else
- *aResult = false;
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::TimedSelect(int32_t aIndex, int32_t aMsec)
- {
- bool suppressSelect = mSuppressed;
- if (aMsec != -1)
- mSuppressed = true;
- nsresult rv = Select(aIndex);
- if (NS_FAILED(rv))
- return rv;
- if (aMsec != -1) {
- mSuppressed = suppressSelect;
- if (!mSuppressed) {
- if (mSelectTimer)
- mSelectTimer->Cancel();
- mSelectTimer = do_CreateInstance("@mozilla.org/timer;1");
- mSelectTimer->InitWithFuncCallback(SelectCallback, this, aMsec,
- nsITimer::TYPE_ONE_SHOT);
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::Select(int32_t aIndex)
- {
- mShiftSelectPivot = -1;
- nsresult rv = SetCurrentIndex(aIndex);
- if (NS_FAILED(rv))
- return rv;
- if (mFirstRange) {
- bool alreadySelected = mFirstRange->Contains(aIndex);
- if (alreadySelected) {
- int32_t count = mFirstRange->Count();
- if (count > 1) {
- // We need to deselect everything but our item.
- mFirstRange->RemoveAllBut(aIndex);
- FireOnSelectHandler();
- }
- return NS_OK;
- }
- else {
- // Clear out our selection.
- mFirstRange->Invalidate();
- delete mFirstRange;
- }
- }
- // Create our new selection.
- mFirstRange = new nsTreeRange(this, aIndex);
- if (!mFirstRange)
- return NS_ERROR_OUT_OF_MEMORY;
- mFirstRange->Invalidate();
- // Fire the select event
- FireOnSelectHandler();
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::ToggleSelect(int32_t aIndex)
- {
- // There are six cases that can occur on a ToggleSelect with our
- // range code.
- // (1) A new range should be made for a selection.
- // (2) A single range is removed from the selection.
- // (3) The item is added to an existing range.
- // (4) The item is removed from an existing range.
- // (5) The addition of the item causes two ranges to be merged.
- // (6) The removal of the item causes two ranges to be split.
- mShiftSelectPivot = -1;
- nsresult rv = SetCurrentIndex(aIndex);
- if (NS_FAILED(rv))
- return rv;
- if (!mFirstRange)
- Select(aIndex);
- else {
- if (!mFirstRange->Contains(aIndex)) {
- bool single;
- rv = GetSingle(&single);
- if (NS_SUCCEEDED(rv) && !single)
- rv = mFirstRange->Add(aIndex);
- }
- else
- rv = mFirstRange->Remove(aIndex);
- if (NS_SUCCEEDED(rv)) {
- if (mTree)
- mTree->InvalidateRow(aIndex);
- FireOnSelectHandler();
- }
- }
- return rv;
- }
- NS_IMETHODIMP nsTreeSelection::RangedSelect(int32_t aStartIndex, int32_t aEndIndex, bool aAugment)
- {
- bool single;
- nsresult rv = GetSingle(&single);
- if (NS_FAILED(rv))
- return rv;
- if ((mFirstRange || (aStartIndex != aEndIndex)) && single)
- return NS_OK;
- if (!aAugment) {
- // Clear our selection.
- if (mFirstRange) {
- mFirstRange->Invalidate();
- delete mFirstRange;
- mFirstRange = nullptr;
- }
- }
- if (aStartIndex == -1) {
- if (mShiftSelectPivot != -1)
- aStartIndex = mShiftSelectPivot;
- else if (mCurrentIndex != -1)
- aStartIndex = mCurrentIndex;
- else
- aStartIndex = aEndIndex;
- }
- mShiftSelectPivot = aStartIndex;
- rv = SetCurrentIndex(aEndIndex);
- if (NS_FAILED(rv))
- return rv;
-
- int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
- int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
- if (aAugment && mFirstRange) {
- // We need to remove all the items within our selected range from the selection,
- // and then we insert our new range into the list.
- nsresult rv = mFirstRange->RemoveRange(start, end);
- if (NS_FAILED(rv))
- return rv;
- }
- nsTreeRange* range = new nsTreeRange(this, start, end);
- if (!range)
- return NS_ERROR_OUT_OF_MEMORY;
- range->Invalidate();
- if (aAugment && mFirstRange)
- mFirstRange->Insert(range);
- else
- mFirstRange = range;
- FireOnSelectHandler();
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::ClearRange(int32_t aStartIndex, int32_t aEndIndex)
- {
- nsresult rv = SetCurrentIndex(aEndIndex);
- if (NS_FAILED(rv))
- return rv;
- if (mFirstRange) {
- int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
- int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;
- mFirstRange->RemoveRange(start, end);
- if (mTree)
- mTree->InvalidateRange(start, end);
- }
-
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::ClearSelection()
- {
- if (mFirstRange) {
- mFirstRange->Invalidate();
- delete mFirstRange;
- mFirstRange = nullptr;
- }
- mShiftSelectPivot = -1;
- FireOnSelectHandler();
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::InvertSelection()
- {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP nsTreeSelection::SelectAll()
- {
- if (!mTree)
- return NS_OK;
- nsCOMPtr<nsITreeView> view;
- mTree->GetView(getter_AddRefs(view));
- if (!view)
- return NS_OK;
- int32_t rowCount;
- view->GetRowCount(&rowCount);
- bool single;
- nsresult rv = GetSingle(&single);
- if (NS_FAILED(rv))
- return rv;
- if (rowCount == 0 || (rowCount > 1 && single))
- return NS_OK;
- mShiftSelectPivot = -1;
- // Invalidate not necessary when clearing selection, since
- // we're going to invalidate the world on the SelectAll.
- delete mFirstRange;
- mFirstRange = new nsTreeRange(this, 0, rowCount-1);
- mFirstRange->Invalidate();
- FireOnSelectHandler();
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::GetRangeCount(int32_t* aResult)
- {
- int32_t count = 0;
- nsTreeRange* curr = mFirstRange;
- while (curr) {
- count++;
- curr = curr->mNext;
- }
- *aResult = count;
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::GetRangeAt(int32_t aIndex, int32_t* aMin, int32_t* aMax)
- {
- *aMin = *aMax = -1;
- int32_t i = -1;
- nsTreeRange* curr = mFirstRange;
- while (curr) {
- i++;
- if (i == aIndex) {
- *aMin = curr->mMin;
- *aMax = curr->mMax;
- break;
- }
- curr = curr->mNext;
- }
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::GetCount(int32_t *count)
- {
- if (mFirstRange)
- *count = mFirstRange->Count();
- else // No range available, so there's no selected row.
- *count = 0;
-
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::GetSelectEventsSuppressed(bool *aSelectEventsSuppressed)
- {
- *aSelectEventsSuppressed = mSuppressed;
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::SetSelectEventsSuppressed(bool aSelectEventsSuppressed)
- {
- mSuppressed = aSelectEventsSuppressed;
- if (!mSuppressed)
- FireOnSelectHandler();
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::GetCurrentIndex(int32_t *aCurrentIndex)
- {
- *aCurrentIndex = mCurrentIndex;
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::SetCurrentIndex(int32_t aIndex)
- {
- if (!mTree) {
- return NS_ERROR_UNEXPECTED;
- }
- if (mCurrentIndex == aIndex) {
- return NS_OK;
- }
- if (mCurrentIndex != -1 && mTree)
- mTree->InvalidateRow(mCurrentIndex);
-
- mCurrentIndex = aIndex;
- if (!mTree)
- return NS_OK;
-
- if (aIndex != -1)
- mTree->InvalidateRow(aIndex);
- // Fire DOMMenuItemActive or DOMMenuItemInactive event for tree.
- nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
- NS_ASSERTION(boxObject, "no box object!");
- if (!boxObject)
- return NS_ERROR_UNEXPECTED;
- nsCOMPtr<nsIDOMElement> treeElt;
- boxObject->GetElement(getter_AddRefs(treeElt));
- nsCOMPtr<nsINode> treeDOMNode(do_QueryInterface(treeElt));
- NS_ENSURE_STATE(treeDOMNode);
- NS_NAMED_LITERAL_STRING(DOMMenuItemActive, "DOMMenuItemActive");
- NS_NAMED_LITERAL_STRING(DOMMenuItemInactive, "DOMMenuItemInactive");
- RefPtr<AsyncEventDispatcher> asyncDispatcher =
- new AsyncEventDispatcher(treeDOMNode,
- (aIndex != -1 ? DOMMenuItemActive :
- DOMMenuItemInactive),
- true, false);
- return asyncDispatcher->PostDOMEvent();
- }
- NS_IMETHODIMP nsTreeSelection::GetCurrentColumn(nsITreeColumn** aCurrentColumn)
- {
- NS_IF_ADDREF(*aCurrentColumn = mCurrentColumn);
- return NS_OK;
- }
- NS_IMETHODIMP nsTreeSelection::SetCurrentColumn(nsITreeColumn* aCurrentColumn)
- {
- if (!mTree) {
- return NS_ERROR_UNEXPECTED;
- }
- if (mCurrentColumn == aCurrentColumn) {
- return NS_OK;
- }
- if (mCurrentColumn) {
- if (mFirstRange)
- mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn);
- if (mCurrentIndex != -1)
- mTree->InvalidateCell(mCurrentIndex, mCurrentColumn);
- }
-
- mCurrentColumn = aCurrentColumn;
-
- if (mCurrentColumn) {
- if (mFirstRange)
- mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn);
- if (mCurrentIndex != -1)
- mTree->InvalidateCell(mCurrentIndex, mCurrentColumn);
- }
- return NS_OK;
- }
- #define ADD_NEW_RANGE(macro_range, macro_selection, macro_start, macro_end) \
- { \
- int32_t start = macro_start; \
- int32_t end = macro_end; \
- if (start > end) { \
- end = start; \
- } \
- nsTreeRange* macro_new_range = new nsTreeRange(macro_selection, start, end); \
- if (macro_range) \
- macro_range->Insert(macro_new_range); \
- else \
- macro_range = macro_new_range; \
- }
- NS_IMETHODIMP
- nsTreeSelection::AdjustSelection(int32_t aIndex, int32_t aCount)
- {
- NS_ASSERTION(aCount != 0, "adjusting by zero");
- if (!aCount) return NS_OK;
- // adjust mShiftSelectPivot, if necessary
- if ((mShiftSelectPivot != 1) && (aIndex <= mShiftSelectPivot)) {
- // if we are deleting and the delete includes the shift select pivot, reset it
- if (aCount < 0 && (mShiftSelectPivot <= (aIndex -aCount -1))) {
- mShiftSelectPivot = -1;
- }
- else {
- mShiftSelectPivot += aCount;
- }
- }
- // adjust mCurrentIndex, if necessary
- if ((mCurrentIndex != -1) && (aIndex <= mCurrentIndex)) {
- // if we are deleting and the delete includes the current index, reset it
- if (aCount < 0 && (mCurrentIndex <= (aIndex -aCount -1))) {
- mCurrentIndex = -1;
- }
- else {
- mCurrentIndex += aCount;
- }
- }
- // no selection, so nothing to do.
- if (!mFirstRange) return NS_OK;
- bool selChanged = false;
- nsTreeRange* oldFirstRange = mFirstRange;
- nsTreeRange* curr = mFirstRange;
- mFirstRange = nullptr;
- while (curr) {
- if (aCount > 0) {
- // inserting
- if (aIndex > curr->mMax) {
- // adjustment happens after the range, so no change
- ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
- }
- else if (aIndex <= curr->mMin) {
- // adjustment happens before the start of the range, so shift down
- ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount);
- selChanged = true;
- }
- else {
- // adjustment happen inside the range.
- // break apart the range and create two ranges
- ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1);
- ADD_NEW_RANGE(mFirstRange, this, aIndex + aCount, curr->mMax + aCount);
- selChanged = true;
- }
- }
- else {
- // deleting
- if (aIndex > curr->mMax) {
- // adjustment happens after the range, so no change
- ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax);
- }
- else {
- // remember, aCount is negative
- selChanged = true;
- int32_t lastIndexOfAdjustment = aIndex - aCount - 1;
- if (aIndex <= curr->mMin) {
- if (lastIndexOfAdjustment < curr->mMin) {
- // adjustment happens before the start of the range, so shift up
- ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount);
- }
- else if (lastIndexOfAdjustment >= curr->mMax) {
- // adjustment contains the range. remove the range by not adding it to the newRange
- }
- else {
- // adjustment starts before the range, and ends in the middle of it, so trim the range
- ADD_NEW_RANGE(mFirstRange, this, aIndex, curr->mMax + aCount)
- }
- }
- else if (lastIndexOfAdjustment >= curr->mMax) {
- // adjustment starts in the middle of the current range, and contains the end of the range, so trim the range
- ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1)
- }
- else {
- // range contains the adjustment, so shorten the range
- ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax + aCount)
- }
- }
- }
- curr = curr->mNext;
- }
- delete oldFirstRange;
- // Fire the select event
- if (selChanged)
- FireOnSelectHandler();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsTreeSelection::InvalidateSelection()
- {
- if (mFirstRange)
- mFirstRange->Invalidate();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsTreeSelection::GetShiftSelectPivot(int32_t* aIndex)
- {
- *aIndex = mShiftSelectPivot;
- return NS_OK;
- }
- nsresult
- nsTreeSelection::FireOnSelectHandler()
- {
- if (mSuppressed || !mTree)
- return NS_OK;
- nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree);
- NS_ASSERTION(boxObject, "no box object!");
- if (!boxObject)
- return NS_ERROR_UNEXPECTED;
- nsCOMPtr<nsIDOMElement> elt;
- boxObject->GetElement(getter_AddRefs(elt));
- NS_ENSURE_STATE(elt);
- nsCOMPtr<nsINode> node(do_QueryInterface(elt));
- NS_ENSURE_STATE(node);
- RefPtr<AsyncEventDispatcher> asyncDispatcher =
- new AsyncEventDispatcher(node, NS_LITERAL_STRING("select"), true, false);
- asyncDispatcher->RunDOMEventWhenSafe();
- return NS_OK;
- }
- void
- nsTreeSelection::SelectCallback(nsITimer *aTimer, void *aClosure)
- {
- RefPtr<nsTreeSelection> self = static_cast<nsTreeSelection*>(aClosure);
- if (self) {
- self->FireOnSelectHandler();
- aTimer->Cancel();
- self->mSelectTimer = nullptr;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////
- nsresult
- NS_NewTreeSelection(nsITreeBoxObject* aTree, nsITreeSelection** aResult)
- {
- *aResult = new nsTreeSelection(aTree);
- if (!*aResult)
- return NS_ERROR_OUT_OF_MEMORY;
- NS_ADDREF(*aResult);
- return NS_OK;
- }
|