PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/layout/xul/base/src/tree/src/nsTreeSelection.cpp

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