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