1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/xul/tree/nsTreeSelection.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,870 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/AsyncEventDispatcher.h" 1.10 +#include "nsCOMPtr.h" 1.11 +#include "nsTreeSelection.h" 1.12 +#include "nsIBoxObject.h" 1.13 +#include "nsITreeBoxObject.h" 1.14 +#include "nsITreeView.h" 1.15 +#include "nsString.h" 1.16 +#include "nsIDOMElement.h" 1.17 +#include "nsDOMClassInfoID.h" 1.18 +#include "nsIContent.h" 1.19 +#include "nsNameSpaceManager.h" 1.20 +#include "nsGkAtoms.h" 1.21 +#include "nsAutoPtr.h" 1.22 +#include "nsComponentManagerUtils.h" 1.23 + 1.24 +using namespace mozilla; 1.25 + 1.26 +// A helper class for managing our ranges of selection. 1.27 +struct nsTreeRange 1.28 +{ 1.29 + nsTreeSelection* mSelection; 1.30 + 1.31 + nsTreeRange* mPrev; 1.32 + nsTreeRange* mNext; 1.33 + 1.34 + int32_t mMin; 1.35 + int32_t mMax; 1.36 + 1.37 + nsTreeRange(nsTreeSelection* aSel, int32_t aSingleVal) 1.38 + :mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aSingleVal), mMax(aSingleVal) {} 1.39 + nsTreeRange(nsTreeSelection* aSel, int32_t aMin, int32_t aMax) 1.40 + :mSelection(aSel), mPrev(nullptr), mNext(nullptr), mMin(aMin), mMax(aMax) {} 1.41 + 1.42 + ~nsTreeRange() { delete mNext; } 1.43 + 1.44 + void Connect(nsTreeRange* aPrev = nullptr, nsTreeRange* aNext = nullptr) { 1.45 + if (aPrev) 1.46 + aPrev->mNext = this; 1.47 + else 1.48 + mSelection->mFirstRange = this; 1.49 + 1.50 + if (aNext) 1.51 + aNext->mPrev = this; 1.52 + 1.53 + mPrev = aPrev; 1.54 + mNext = aNext; 1.55 + } 1.56 + 1.57 + nsresult RemoveRange(int32_t aStart, int32_t aEnd) { 1.58 + // This should so be a loop... sigh... 1.59 + // We start past the range to remove, so no more to remove 1.60 + if (aEnd < mMin) 1.61 + return NS_OK; 1.62 + // We are the last range to be affected 1.63 + if (aEnd < mMax) { 1.64 + if (aStart <= mMin) { 1.65 + // Just chop the start of the range off 1.66 + mMin = aEnd + 1; 1.67 + } else { 1.68 + // We need to split the range 1.69 + nsTreeRange* range = new nsTreeRange(mSelection, aEnd + 1, mMax); 1.70 + if (!range) 1.71 + return NS_ERROR_OUT_OF_MEMORY; 1.72 + 1.73 + mMax = aStart - 1; 1.74 + range->Connect(this, mNext); 1.75 + } 1.76 + return NS_OK; 1.77 + } 1.78 + nsTreeRange* next = mNext; 1.79 + if (aStart <= mMin) { 1.80 + // The remove includes us, remove ourselves from the list 1.81 + if (mPrev) 1.82 + mPrev->mNext = next; 1.83 + else 1.84 + mSelection->mFirstRange = next; 1.85 + 1.86 + if (next) 1.87 + next->mPrev = mPrev; 1.88 + mPrev = mNext = nullptr; 1.89 + delete this; 1.90 + } else if (aStart <= mMax) { 1.91 + // Just chop the end of the range off 1.92 + mMax = aStart - 1; 1.93 + } 1.94 + return next ? next->RemoveRange(aStart, aEnd) : NS_OK; 1.95 + } 1.96 + 1.97 + nsresult Remove(int32_t aIndex) { 1.98 + if (aIndex >= mMin && aIndex <= mMax) { 1.99 + // We have found the range that contains us. 1.100 + if (mMin == mMax) { 1.101 + // Delete the whole range. 1.102 + if (mPrev) 1.103 + mPrev->mNext = mNext; 1.104 + if (mNext) 1.105 + mNext->mPrev = mPrev; 1.106 + nsTreeRange* first = mSelection->mFirstRange; 1.107 + if (first == this) 1.108 + mSelection->mFirstRange = mNext; 1.109 + mNext = mPrev = nullptr; 1.110 + delete this; 1.111 + } 1.112 + else if (aIndex == mMin) 1.113 + mMin++; 1.114 + else if (aIndex == mMax) 1.115 + mMax--; 1.116 + else { 1.117 + // We have to break this range. 1.118 + nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex + 1, mMax); 1.119 + if (!newRange) 1.120 + return NS_ERROR_OUT_OF_MEMORY; 1.121 + 1.122 + newRange->Connect(this, mNext); 1.123 + mMax = aIndex - 1; 1.124 + } 1.125 + } 1.126 + else if (mNext) 1.127 + return mNext->Remove(aIndex); 1.128 + 1.129 + return NS_OK; 1.130 + } 1.131 + 1.132 + nsresult Add(int32_t aIndex) { 1.133 + if (aIndex < mMin) { 1.134 + // We have found a spot to insert. 1.135 + if (aIndex + 1 == mMin) 1.136 + mMin = aIndex; 1.137 + else if (mPrev && mPrev->mMax+1 == aIndex) 1.138 + mPrev->mMax = aIndex; 1.139 + else { 1.140 + // We have to create a new range. 1.141 + nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex); 1.142 + if (!newRange) 1.143 + return NS_ERROR_OUT_OF_MEMORY; 1.144 + 1.145 + newRange->Connect(mPrev, this); 1.146 + } 1.147 + } 1.148 + else if (mNext) 1.149 + mNext->Add(aIndex); 1.150 + else { 1.151 + // Insert on to the end. 1.152 + if (mMax+1 == aIndex) 1.153 + mMax = aIndex; 1.154 + else { 1.155 + // We have to create a new range. 1.156 + nsTreeRange* newRange = new nsTreeRange(mSelection, aIndex); 1.157 + if (!newRange) 1.158 + return NS_ERROR_OUT_OF_MEMORY; 1.159 + 1.160 + newRange->Connect(this, nullptr); 1.161 + } 1.162 + } 1.163 + return NS_OK; 1.164 + } 1.165 + 1.166 + bool Contains(int32_t aIndex) { 1.167 + if (aIndex >= mMin && aIndex <= mMax) 1.168 + return true; 1.169 + 1.170 + if (mNext) 1.171 + return mNext->Contains(aIndex); 1.172 + 1.173 + return false; 1.174 + } 1.175 + 1.176 + int32_t Count() { 1.177 + int32_t total = mMax - mMin + 1; 1.178 + if (mNext) 1.179 + total += mNext->Count(); 1.180 + return total; 1.181 + } 1.182 + 1.183 + static void CollectRanges(nsTreeRange* aRange, nsTArray<int32_t>& aRanges) 1.184 + { 1.185 + nsTreeRange* cur = aRange; 1.186 + while (cur) { 1.187 + aRanges.AppendElement(cur->mMin); 1.188 + aRanges.AppendElement(cur->mMax); 1.189 + cur = cur->mNext; 1.190 + } 1.191 + } 1.192 + 1.193 + static void InvalidateRanges(nsITreeBoxObject* aTree, 1.194 + nsTArray<int32_t>& aRanges) 1.195 + { 1.196 + if (aTree) { 1.197 + nsCOMPtr<nsITreeBoxObject> tree = aTree; 1.198 + for (uint32_t i = 0; i < aRanges.Length(); i += 2) { 1.199 + aTree->InvalidateRange(aRanges[i], aRanges[i + 1]); 1.200 + } 1.201 + } 1.202 + } 1.203 + 1.204 + void Invalidate() { 1.205 + nsTArray<int32_t> ranges; 1.206 + CollectRanges(this, ranges); 1.207 + InvalidateRanges(mSelection->mTree, ranges); 1.208 + 1.209 + } 1.210 + 1.211 + void RemoveAllBut(int32_t aIndex) { 1.212 + if (aIndex >= mMin && aIndex <= mMax) { 1.213 + 1.214 + // Invalidate everything in this list. 1.215 + nsTArray<int32_t> ranges; 1.216 + CollectRanges(mSelection->mFirstRange, ranges); 1.217 + 1.218 + mMin = aIndex; 1.219 + mMax = aIndex; 1.220 + 1.221 + nsTreeRange* first = mSelection->mFirstRange; 1.222 + if (mPrev) 1.223 + mPrev->mNext = mNext; 1.224 + if (mNext) 1.225 + mNext->mPrev = mPrev; 1.226 + mNext = mPrev = nullptr; 1.227 + 1.228 + if (first != this) { 1.229 + delete mSelection->mFirstRange; 1.230 + mSelection->mFirstRange = this; 1.231 + } 1.232 + InvalidateRanges(mSelection->mTree, ranges); 1.233 + } 1.234 + else if (mNext) 1.235 + mNext->RemoveAllBut(aIndex); 1.236 + } 1.237 + 1.238 + void Insert(nsTreeRange* aRange) { 1.239 + if (mMin >= aRange->mMax) 1.240 + aRange->Connect(mPrev, this); 1.241 + else if (mNext) 1.242 + mNext->Insert(aRange); 1.243 + else 1.244 + aRange->Connect(this, nullptr); 1.245 + } 1.246 +}; 1.247 + 1.248 +nsTreeSelection::nsTreeSelection(nsITreeBoxObject* aTree) 1.249 + : mTree(aTree), 1.250 + mSuppressed(false), 1.251 + mCurrentIndex(-1), 1.252 + mShiftSelectPivot(-1), 1.253 + mFirstRange(nullptr) 1.254 +{ 1.255 +} 1.256 + 1.257 +nsTreeSelection::~nsTreeSelection() 1.258 +{ 1.259 + delete mFirstRange; 1.260 + if (mSelectTimer) 1.261 + mSelectTimer->Cancel(); 1.262 +} 1.263 + 1.264 +NS_IMPL_CYCLE_COLLECTION(nsTreeSelection, mTree, mCurrentColumn) 1.265 + 1.266 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTreeSelection) 1.267 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTreeSelection) 1.268 + 1.269 +DOMCI_DATA(TreeSelection, nsTreeSelection) 1.270 + 1.271 +// QueryInterface implementation for nsBoxObject 1.272 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTreeSelection) 1.273 + NS_INTERFACE_MAP_ENTRY(nsITreeSelection) 1.274 + NS_INTERFACE_MAP_ENTRY(nsINativeTreeSelection) 1.275 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.276 + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeSelection) 1.277 +NS_INTERFACE_MAP_END 1.278 + 1.279 +NS_IMETHODIMP nsTreeSelection::GetTree(nsITreeBoxObject * *aTree) 1.280 +{ 1.281 + NS_IF_ADDREF(*aTree = mTree); 1.282 + return NS_OK; 1.283 +} 1.284 + 1.285 +NS_IMETHODIMP nsTreeSelection::SetTree(nsITreeBoxObject * aTree) 1.286 +{ 1.287 + if (mSelectTimer) { 1.288 + mSelectTimer->Cancel(); 1.289 + mSelectTimer = nullptr; 1.290 + } 1.291 + 1.292 + // Make sure aTree really implements nsITreeBoxObject and nsIBoxObject! 1.293 + nsCOMPtr<nsIBoxObject> bo = do_QueryInterface(aTree); 1.294 + mTree = do_QueryInterface(bo); 1.295 + NS_ENSURE_STATE(mTree == aTree); 1.296 + return NS_OK; 1.297 +} 1.298 + 1.299 +NS_IMETHODIMP nsTreeSelection::GetSingle(bool* aSingle) 1.300 +{ 1.301 + if (!mTree) 1.302 + return NS_ERROR_NULL_POINTER; 1.303 + 1.304 + nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree); 1.305 + 1.306 + nsCOMPtr<nsIDOMElement> element; 1.307 + boxObject->GetElement(getter_AddRefs(element)); 1.308 + 1.309 + nsCOMPtr<nsIContent> content = do_QueryInterface(element); 1.310 + 1.311 + static nsIContent::AttrValuesArray strings[] = 1.312 + {&nsGkAtoms::single, &nsGkAtoms::cell, &nsGkAtoms::text, nullptr}; 1.313 + 1.314 + *aSingle = content->FindAttrValueIn(kNameSpaceID_None, 1.315 + nsGkAtoms::seltype, 1.316 + strings, eCaseMatters) >= 0; 1.317 + 1.318 + return NS_OK; 1.319 +} 1.320 + 1.321 +NS_IMETHODIMP nsTreeSelection::IsSelected(int32_t aIndex, bool* aResult) 1.322 +{ 1.323 + if (mFirstRange) 1.324 + *aResult = mFirstRange->Contains(aIndex); 1.325 + else 1.326 + *aResult = false; 1.327 + return NS_OK; 1.328 +} 1.329 + 1.330 +NS_IMETHODIMP nsTreeSelection::TimedSelect(int32_t aIndex, int32_t aMsec) 1.331 +{ 1.332 + bool suppressSelect = mSuppressed; 1.333 + 1.334 + if (aMsec != -1) 1.335 + mSuppressed = true; 1.336 + 1.337 + nsresult rv = Select(aIndex); 1.338 + if (NS_FAILED(rv)) 1.339 + return rv; 1.340 + 1.341 + if (aMsec != -1) { 1.342 + mSuppressed = suppressSelect; 1.343 + if (!mSuppressed) { 1.344 + if (mSelectTimer) 1.345 + mSelectTimer->Cancel(); 1.346 + 1.347 + mSelectTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.348 + mSelectTimer->InitWithFuncCallback(SelectCallback, this, aMsec, 1.349 + nsITimer::TYPE_ONE_SHOT); 1.350 + } 1.351 + } 1.352 + 1.353 + return NS_OK; 1.354 +} 1.355 + 1.356 +NS_IMETHODIMP nsTreeSelection::Select(int32_t aIndex) 1.357 +{ 1.358 + mShiftSelectPivot = -1; 1.359 + 1.360 + nsresult rv = SetCurrentIndex(aIndex); 1.361 + if (NS_FAILED(rv)) 1.362 + return rv; 1.363 + 1.364 + if (mFirstRange) { 1.365 + bool alreadySelected = mFirstRange->Contains(aIndex); 1.366 + 1.367 + if (alreadySelected) { 1.368 + int32_t count = mFirstRange->Count(); 1.369 + if (count > 1) { 1.370 + // We need to deselect everything but our item. 1.371 + mFirstRange->RemoveAllBut(aIndex); 1.372 + FireOnSelectHandler(); 1.373 + } 1.374 + return NS_OK; 1.375 + } 1.376 + else { 1.377 + // Clear out our selection. 1.378 + mFirstRange->Invalidate(); 1.379 + delete mFirstRange; 1.380 + } 1.381 + } 1.382 + 1.383 + // Create our new selection. 1.384 + mFirstRange = new nsTreeRange(this, aIndex); 1.385 + if (!mFirstRange) 1.386 + return NS_ERROR_OUT_OF_MEMORY; 1.387 + 1.388 + mFirstRange->Invalidate(); 1.389 + 1.390 + // Fire the select event 1.391 + FireOnSelectHandler(); 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +NS_IMETHODIMP nsTreeSelection::ToggleSelect(int32_t aIndex) 1.396 +{ 1.397 + // There are six cases that can occur on a ToggleSelect with our 1.398 + // range code. 1.399 + // (1) A new range should be made for a selection. 1.400 + // (2) A single range is removed from the selection. 1.401 + // (3) The item is added to an existing range. 1.402 + // (4) The item is removed from an existing range. 1.403 + // (5) The addition of the item causes two ranges to be merged. 1.404 + // (6) The removal of the item causes two ranges to be split. 1.405 + mShiftSelectPivot = -1; 1.406 + nsresult rv = SetCurrentIndex(aIndex); 1.407 + if (NS_FAILED(rv)) 1.408 + return rv; 1.409 + 1.410 + if (!mFirstRange) 1.411 + Select(aIndex); 1.412 + else { 1.413 + if (!mFirstRange->Contains(aIndex)) { 1.414 + bool single; 1.415 + rv = GetSingle(&single); 1.416 + if (NS_SUCCEEDED(rv) && !single) 1.417 + rv = mFirstRange->Add(aIndex); 1.418 + } 1.419 + else 1.420 + rv = mFirstRange->Remove(aIndex); 1.421 + if (NS_SUCCEEDED(rv)) { 1.422 + if (mTree) 1.423 + mTree->InvalidateRow(aIndex); 1.424 + 1.425 + FireOnSelectHandler(); 1.426 + } 1.427 + } 1.428 + 1.429 + return rv; 1.430 +} 1.431 + 1.432 +NS_IMETHODIMP nsTreeSelection::RangedSelect(int32_t aStartIndex, int32_t aEndIndex, bool aAugment) 1.433 +{ 1.434 + bool single; 1.435 + nsresult rv = GetSingle(&single); 1.436 + if (NS_FAILED(rv)) 1.437 + return rv; 1.438 + 1.439 + if ((mFirstRange || (aStartIndex != aEndIndex)) && single) 1.440 + return NS_OK; 1.441 + 1.442 + if (!aAugment) { 1.443 + // Clear our selection. 1.444 + if (mFirstRange) { 1.445 + mFirstRange->Invalidate(); 1.446 + delete mFirstRange; 1.447 + mFirstRange = nullptr; 1.448 + } 1.449 + } 1.450 + 1.451 + if (aStartIndex == -1) { 1.452 + if (mShiftSelectPivot != -1) 1.453 + aStartIndex = mShiftSelectPivot; 1.454 + else if (mCurrentIndex != -1) 1.455 + aStartIndex = mCurrentIndex; 1.456 + else 1.457 + aStartIndex = aEndIndex; 1.458 + } 1.459 + 1.460 + mShiftSelectPivot = aStartIndex; 1.461 + rv = SetCurrentIndex(aEndIndex); 1.462 + if (NS_FAILED(rv)) 1.463 + return rv; 1.464 + 1.465 + int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex; 1.466 + int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex; 1.467 + 1.468 + if (aAugment && mFirstRange) { 1.469 + // We need to remove all the items within our selected range from the selection, 1.470 + // and then we insert our new range into the list. 1.471 + nsresult rv = mFirstRange->RemoveRange(start, end); 1.472 + if (NS_FAILED(rv)) 1.473 + return rv; 1.474 + } 1.475 + 1.476 + nsTreeRange* range = new nsTreeRange(this, start, end); 1.477 + if (!range) 1.478 + return NS_ERROR_OUT_OF_MEMORY; 1.479 + 1.480 + range->Invalidate(); 1.481 + 1.482 + if (aAugment && mFirstRange) 1.483 + mFirstRange->Insert(range); 1.484 + else 1.485 + mFirstRange = range; 1.486 + 1.487 + FireOnSelectHandler(); 1.488 + 1.489 + return NS_OK; 1.490 +} 1.491 + 1.492 +NS_IMETHODIMP nsTreeSelection::ClearRange(int32_t aStartIndex, int32_t aEndIndex) 1.493 +{ 1.494 + nsresult rv = SetCurrentIndex(aEndIndex); 1.495 + if (NS_FAILED(rv)) 1.496 + return rv; 1.497 + 1.498 + if (mFirstRange) { 1.499 + int32_t start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex; 1.500 + int32_t end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex; 1.501 + 1.502 + mFirstRange->RemoveRange(start, end); 1.503 + 1.504 + if (mTree) 1.505 + mTree->InvalidateRange(start, end); 1.506 + } 1.507 + 1.508 + return NS_OK; 1.509 +} 1.510 + 1.511 +NS_IMETHODIMP nsTreeSelection::ClearSelection() 1.512 +{ 1.513 + if (mFirstRange) { 1.514 + mFirstRange->Invalidate(); 1.515 + delete mFirstRange; 1.516 + mFirstRange = nullptr; 1.517 + } 1.518 + mShiftSelectPivot = -1; 1.519 + 1.520 + FireOnSelectHandler(); 1.521 + 1.522 + return NS_OK; 1.523 +} 1.524 + 1.525 +NS_IMETHODIMP nsTreeSelection::InvertSelection() 1.526 +{ 1.527 + return NS_ERROR_NOT_IMPLEMENTED; 1.528 +} 1.529 + 1.530 +NS_IMETHODIMP nsTreeSelection::SelectAll() 1.531 +{ 1.532 + if (!mTree) 1.533 + return NS_OK; 1.534 + 1.535 + nsCOMPtr<nsITreeView> view; 1.536 + mTree->GetView(getter_AddRefs(view)); 1.537 + if (!view) 1.538 + return NS_OK; 1.539 + 1.540 + int32_t rowCount; 1.541 + view->GetRowCount(&rowCount); 1.542 + bool single; 1.543 + nsresult rv = GetSingle(&single); 1.544 + if (NS_FAILED(rv)) 1.545 + return rv; 1.546 + 1.547 + if (rowCount == 0 || (rowCount > 1 && single)) 1.548 + return NS_OK; 1.549 + 1.550 + mShiftSelectPivot = -1; 1.551 + 1.552 + // Invalidate not necessary when clearing selection, since 1.553 + // we're going to invalidate the world on the SelectAll. 1.554 + delete mFirstRange; 1.555 + 1.556 + mFirstRange = new nsTreeRange(this, 0, rowCount-1); 1.557 + mFirstRange->Invalidate(); 1.558 + 1.559 + FireOnSelectHandler(); 1.560 + 1.561 + return NS_OK; 1.562 +} 1.563 + 1.564 +NS_IMETHODIMP nsTreeSelection::GetRangeCount(int32_t* aResult) 1.565 +{ 1.566 + int32_t count = 0; 1.567 + nsTreeRange* curr = mFirstRange; 1.568 + while (curr) { 1.569 + count++; 1.570 + curr = curr->mNext; 1.571 + } 1.572 + 1.573 + *aResult = count; 1.574 + return NS_OK; 1.575 +} 1.576 + 1.577 +NS_IMETHODIMP nsTreeSelection::GetRangeAt(int32_t aIndex, int32_t* aMin, int32_t* aMax) 1.578 +{ 1.579 + *aMin = *aMax = -1; 1.580 + int32_t i = -1; 1.581 + nsTreeRange* curr = mFirstRange; 1.582 + while (curr) { 1.583 + i++; 1.584 + if (i == aIndex) { 1.585 + *aMin = curr->mMin; 1.586 + *aMax = curr->mMax; 1.587 + break; 1.588 + } 1.589 + curr = curr->mNext; 1.590 + } 1.591 + 1.592 + return NS_OK; 1.593 +} 1.594 + 1.595 +NS_IMETHODIMP nsTreeSelection::GetCount(int32_t *count) 1.596 +{ 1.597 + if (mFirstRange) 1.598 + *count = mFirstRange->Count(); 1.599 + else // No range available, so there's no selected row. 1.600 + *count = 0; 1.601 + 1.602 + return NS_OK; 1.603 +} 1.604 + 1.605 +NS_IMETHODIMP nsTreeSelection::GetSelectEventsSuppressed(bool *aSelectEventsSuppressed) 1.606 +{ 1.607 + *aSelectEventsSuppressed = mSuppressed; 1.608 + return NS_OK; 1.609 +} 1.610 + 1.611 +NS_IMETHODIMP nsTreeSelection::SetSelectEventsSuppressed(bool aSelectEventsSuppressed) 1.612 +{ 1.613 + mSuppressed = aSelectEventsSuppressed; 1.614 + if (!mSuppressed) 1.615 + FireOnSelectHandler(); 1.616 + return NS_OK; 1.617 +} 1.618 + 1.619 +NS_IMETHODIMP nsTreeSelection::GetCurrentIndex(int32_t *aCurrentIndex) 1.620 +{ 1.621 + *aCurrentIndex = mCurrentIndex; 1.622 + return NS_OK; 1.623 +} 1.624 + 1.625 +NS_IMETHODIMP nsTreeSelection::SetCurrentIndex(int32_t aIndex) 1.626 +{ 1.627 + if (!mTree) { 1.628 + return NS_ERROR_UNEXPECTED; 1.629 + } 1.630 + if (mCurrentIndex == aIndex) { 1.631 + return NS_OK; 1.632 + } 1.633 + if (mCurrentIndex != -1 && mTree) 1.634 + mTree->InvalidateRow(mCurrentIndex); 1.635 + 1.636 + mCurrentIndex = aIndex; 1.637 + if (!mTree) 1.638 + return NS_OK; 1.639 + 1.640 + if (aIndex != -1) 1.641 + mTree->InvalidateRow(aIndex); 1.642 + 1.643 + // Fire DOMMenuItemActive or DOMMenuItemInactive event for tree. 1.644 + nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree); 1.645 + NS_ASSERTION(boxObject, "no box object!"); 1.646 + if (!boxObject) 1.647 + return NS_ERROR_UNEXPECTED; 1.648 + nsCOMPtr<nsIDOMElement> treeElt; 1.649 + boxObject->GetElement(getter_AddRefs(treeElt)); 1.650 + 1.651 + nsCOMPtr<nsINode> treeDOMNode(do_QueryInterface(treeElt)); 1.652 + NS_ENSURE_STATE(treeDOMNode); 1.653 + 1.654 + NS_NAMED_LITERAL_STRING(DOMMenuItemActive, "DOMMenuItemActive"); 1.655 + NS_NAMED_LITERAL_STRING(DOMMenuItemInactive, "DOMMenuItemInactive"); 1.656 + 1.657 + nsRefPtr<AsyncEventDispatcher> asyncDispatcher = 1.658 + new AsyncEventDispatcher(treeDOMNode, 1.659 + (aIndex != -1 ? DOMMenuItemActive : 1.660 + DOMMenuItemInactive), 1.661 + true, false); 1.662 + return asyncDispatcher->PostDOMEvent(); 1.663 +} 1.664 + 1.665 +NS_IMETHODIMP nsTreeSelection::GetCurrentColumn(nsITreeColumn** aCurrentColumn) 1.666 +{ 1.667 + NS_IF_ADDREF(*aCurrentColumn = mCurrentColumn); 1.668 + return NS_OK; 1.669 +} 1.670 + 1.671 +NS_IMETHODIMP nsTreeSelection::SetCurrentColumn(nsITreeColumn* aCurrentColumn) 1.672 +{ 1.673 + if (!mTree) { 1.674 + return NS_ERROR_UNEXPECTED; 1.675 + } 1.676 + if (mCurrentColumn == aCurrentColumn) { 1.677 + return NS_OK; 1.678 + } 1.679 + 1.680 + if (mCurrentColumn) { 1.681 + if (mFirstRange) 1.682 + mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn); 1.683 + if (mCurrentIndex != -1) 1.684 + mTree->InvalidateCell(mCurrentIndex, mCurrentColumn); 1.685 + } 1.686 + 1.687 + mCurrentColumn = aCurrentColumn; 1.688 + 1.689 + if (mCurrentColumn) { 1.690 + if (mFirstRange) 1.691 + mTree->InvalidateCell(mFirstRange->mMin, mCurrentColumn); 1.692 + if (mCurrentIndex != -1) 1.693 + mTree->InvalidateCell(mCurrentIndex, mCurrentColumn); 1.694 + } 1.695 + 1.696 + return NS_OK; 1.697 +} 1.698 + 1.699 +#define ADD_NEW_RANGE(macro_range, macro_selection, macro_start, macro_end) \ 1.700 + { \ 1.701 + int32_t start = macro_start; \ 1.702 + int32_t end = macro_end; \ 1.703 + if (start > end) { \ 1.704 + end = start; \ 1.705 + } \ 1.706 + nsTreeRange* macro_new_range = new nsTreeRange(macro_selection, start, end); \ 1.707 + if (macro_range) \ 1.708 + macro_range->Insert(macro_new_range); \ 1.709 + else \ 1.710 + macro_range = macro_new_range; \ 1.711 + } 1.712 + 1.713 +NS_IMETHODIMP 1.714 +nsTreeSelection::AdjustSelection(int32_t aIndex, int32_t aCount) 1.715 +{ 1.716 + NS_ASSERTION(aCount != 0, "adjusting by zero"); 1.717 + if (!aCount) return NS_OK; 1.718 + 1.719 + // adjust mShiftSelectPivot, if necessary 1.720 + if ((mShiftSelectPivot != 1) && (aIndex <= mShiftSelectPivot)) { 1.721 + // if we are deleting and the delete includes the shift select pivot, reset it 1.722 + if (aCount < 0 && (mShiftSelectPivot <= (aIndex -aCount -1))) { 1.723 + mShiftSelectPivot = -1; 1.724 + } 1.725 + else { 1.726 + mShiftSelectPivot += aCount; 1.727 + } 1.728 + } 1.729 + 1.730 + // adjust mCurrentIndex, if necessary 1.731 + if ((mCurrentIndex != -1) && (aIndex <= mCurrentIndex)) { 1.732 + // if we are deleting and the delete includes the current index, reset it 1.733 + if (aCount < 0 && (mCurrentIndex <= (aIndex -aCount -1))) { 1.734 + mCurrentIndex = -1; 1.735 + } 1.736 + else { 1.737 + mCurrentIndex += aCount; 1.738 + } 1.739 + } 1.740 + 1.741 + // no selection, so nothing to do. 1.742 + if (!mFirstRange) return NS_OK; 1.743 + 1.744 + bool selChanged = false; 1.745 + nsTreeRange* oldFirstRange = mFirstRange; 1.746 + nsTreeRange* curr = mFirstRange; 1.747 + mFirstRange = nullptr; 1.748 + while (curr) { 1.749 + if (aCount > 0) { 1.750 + // inserting 1.751 + if (aIndex > curr->mMax) { 1.752 + // adjustment happens after the range, so no change 1.753 + ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax); 1.754 + } 1.755 + else if (aIndex <= curr->mMin) { 1.756 + // adjustment happens before the start of the range, so shift down 1.757 + ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount); 1.758 + selChanged = true; 1.759 + } 1.760 + else { 1.761 + // adjustment happen inside the range. 1.762 + // break apart the range and create two ranges 1.763 + ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1); 1.764 + ADD_NEW_RANGE(mFirstRange, this, aIndex + aCount, curr->mMax + aCount); 1.765 + selChanged = true; 1.766 + } 1.767 + } 1.768 + else { 1.769 + // deleting 1.770 + if (aIndex > curr->mMax) { 1.771 + // adjustment happens after the range, so no change 1.772 + ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax); 1.773 + } 1.774 + else { 1.775 + // remember, aCount is negative 1.776 + selChanged = true; 1.777 + int32_t lastIndexOfAdjustment = aIndex - aCount - 1; 1.778 + if (aIndex <= curr->mMin) { 1.779 + if (lastIndexOfAdjustment < curr->mMin) { 1.780 + // adjustment happens before the start of the range, so shift up 1.781 + ADD_NEW_RANGE(mFirstRange, this, curr->mMin + aCount, curr->mMax + aCount); 1.782 + } 1.783 + else if (lastIndexOfAdjustment >= curr->mMax) { 1.784 + // adjustment contains the range. remove the range by not adding it to the newRange 1.785 + } 1.786 + else { 1.787 + // adjustment starts before the range, and ends in the middle of it, so trim the range 1.788 + ADD_NEW_RANGE(mFirstRange, this, aIndex, curr->mMax + aCount) 1.789 + } 1.790 + } 1.791 + else if (lastIndexOfAdjustment >= curr->mMax) { 1.792 + // adjustment starts in the middle of the current range, and contains the end of the range, so trim the range 1.793 + ADD_NEW_RANGE(mFirstRange, this, curr->mMin, aIndex - 1) 1.794 + } 1.795 + else { 1.796 + // range contains the adjustment, so shorten the range 1.797 + ADD_NEW_RANGE(mFirstRange, this, curr->mMin, curr->mMax + aCount) 1.798 + } 1.799 + } 1.800 + } 1.801 + curr = curr->mNext; 1.802 + } 1.803 + 1.804 + delete oldFirstRange; 1.805 + 1.806 + // Fire the select event 1.807 + if (selChanged) 1.808 + FireOnSelectHandler(); 1.809 + 1.810 + return NS_OK; 1.811 +} 1.812 + 1.813 +NS_IMETHODIMP 1.814 +nsTreeSelection::InvalidateSelection() 1.815 +{ 1.816 + if (mFirstRange) 1.817 + mFirstRange->Invalidate(); 1.818 + return NS_OK; 1.819 +} 1.820 + 1.821 +NS_IMETHODIMP 1.822 +nsTreeSelection::GetShiftSelectPivot(int32_t* aIndex) 1.823 +{ 1.824 + *aIndex = mShiftSelectPivot; 1.825 + return NS_OK; 1.826 +} 1.827 + 1.828 + 1.829 +nsresult 1.830 +nsTreeSelection::FireOnSelectHandler() 1.831 +{ 1.832 + if (mSuppressed || !mTree) 1.833 + return NS_OK; 1.834 + 1.835 + nsCOMPtr<nsIBoxObject> boxObject = do_QueryInterface(mTree); 1.836 + NS_ASSERTION(boxObject, "no box object!"); 1.837 + if (!boxObject) 1.838 + return NS_ERROR_UNEXPECTED; 1.839 + nsCOMPtr<nsIDOMElement> elt; 1.840 + boxObject->GetElement(getter_AddRefs(elt)); 1.841 + NS_ENSURE_STATE(elt); 1.842 + 1.843 + nsCOMPtr<nsINode> node(do_QueryInterface(elt)); 1.844 + NS_ENSURE_STATE(node); 1.845 + 1.846 + nsRefPtr<AsyncEventDispatcher> asyncDispatcher = 1.847 + new AsyncEventDispatcher(node, NS_LITERAL_STRING("select"), true, false); 1.848 + asyncDispatcher->RunDOMEventWhenSafe(); 1.849 + return NS_OK; 1.850 +} 1.851 + 1.852 +void 1.853 +nsTreeSelection::SelectCallback(nsITimer *aTimer, void *aClosure) 1.854 +{ 1.855 + nsRefPtr<nsTreeSelection> self = static_cast<nsTreeSelection*>(aClosure); 1.856 + if (self) { 1.857 + self->FireOnSelectHandler(); 1.858 + aTimer->Cancel(); 1.859 + self->mSelectTimer = nullptr; 1.860 + } 1.861 +} 1.862 + 1.863 +/////////////////////////////////////////////////////////////////////////////////// 1.864 + 1.865 +nsresult 1.866 +NS_NewTreeSelection(nsITreeBoxObject* aTree, nsITreeSelection** aResult) 1.867 +{ 1.868 + *aResult = new nsTreeSelection(aTree); 1.869 + if (!*aResult) 1.870 + return NS_ERROR_OUT_OF_MEMORY; 1.871 + NS_ADDREF(*aResult); 1.872 + return NS_OK; 1.873 +}