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