layout/xul/tree/nsTreeSelection.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial