Coverage Report

Created: 2018-09-25 14:53

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