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