editor/libeditor/base/nsSelectionState.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:019858fc186f
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/Assertions.h" // for MOZ_ASSERT, etc
7 #include "mozilla/dom/Selection.h" // for Selection
8 #include "nsAString.h" // for nsAString_internal::Length
9 #include "nsAutoPtr.h" // for nsRefPtr, getter_AddRefs, etc
10 #include "nsCycleCollectionParticipant.h"
11 #include "nsDebug.h" // for NS_ENSURE_TRUE, etc
12 #include "nsEditor.h" // for nsEditor
13 #include "nsEditorUtils.h" // for nsEditorUtils
14 #include "nsError.h" // for NS_OK, etc
15 #include "nsIDOMCharacterData.h" // for nsIDOMCharacterData
16 #include "nsIDOMNode.h" // for nsIDOMNode
17 #include "nsIDOMRange.h" // for nsIDOMRange, etc
18 #include "nsISelection.h" // for nsISelection
19 #include "nsISupportsImpl.h" // for nsRange::Release
20 #include "nsRange.h" // for nsRange
21 #include "nsSelectionState.h"
22
23 using namespace mozilla;
24 using namespace mozilla::dom;
25
26 /***************************************************************************
27 * class for recording selection info. stores selection as collection of
28 * { {startnode, startoffset} , {endnode, endoffset} } tuples. Can't store
29 * ranges since dom gravity will possibly change the ranges.
30 */
31 nsSelectionState::nsSelectionState() : mArray(){}
32
33 nsSelectionState::~nsSelectionState()
34 {
35 MakeEmpty();
36 }
37
38 void
39 nsSelectionState::DoTraverse(nsCycleCollectionTraversalCallback &cb)
40 {
41 for (uint32_t i = 0, iEnd = mArray.Length(); i < iEnd; ++i)
42 {
43 nsRangeStore* item = mArray[i];
44 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
45 "selection state mArray[i].startNode");
46 cb.NoteXPCOMChild(item->startNode);
47 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
48 "selection state mArray[i].endNode");
49 cb.NoteXPCOMChild(item->endNode);
50 }
51 }
52
53 void
54 nsSelectionState::SaveSelection(Selection* aSel)
55 {
56 MOZ_ASSERT(aSel);
57 int32_t arrayCount = mArray.Length();
58 int32_t rangeCount = aSel->GetRangeCount();
59
60 // if we need more items in the array, new them
61 if (arrayCount < rangeCount) {
62 for (int32_t i = arrayCount; i < rangeCount; i++) {
63 mArray.AppendElement();
64 mArray[i] = new nsRangeStore();
65 }
66 } else if (arrayCount > rangeCount) {
67 // else if we have too many, delete them
68 for (int32_t i = arrayCount - 1; i >= rangeCount; i--) {
69 mArray.RemoveElementAt(i);
70 }
71 }
72
73 // now store the selection ranges
74 for (int32_t i = 0; i < rangeCount; i++) {
75 mArray[i]->StoreRange(aSel->GetRangeAt(i));
76 }
77 }
78
79 nsresult
80 nsSelectionState::RestoreSelection(nsISelection *aSel)
81 {
82 NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER);
83 nsresult res;
84 uint32_t i, arrayCount = mArray.Length();
85
86 // clear out selection
87 aSel->RemoveAllRanges();
88
89 // set the selection ranges anew
90 for (i=0; i<arrayCount; i++)
91 {
92 nsRefPtr<nsRange> range;
93 mArray[i]->GetRange(getter_AddRefs(range));
94 NS_ENSURE_TRUE(range, NS_ERROR_UNEXPECTED);
95
96 res = aSel->AddRange(range);
97 if(NS_FAILED(res)) return res;
98
99 }
100 return NS_OK;
101 }
102
103 bool
104 nsSelectionState::IsCollapsed()
105 {
106 if (1 != mArray.Length()) return false;
107 nsRefPtr<nsRange> range;
108 mArray[0]->GetRange(getter_AddRefs(range));
109 NS_ENSURE_TRUE(range, false);
110 bool bIsCollapsed = false;
111 range->GetCollapsed(&bIsCollapsed);
112 return bIsCollapsed;
113 }
114
115 bool
116 nsSelectionState::IsEqual(nsSelectionState *aSelState)
117 {
118 NS_ENSURE_TRUE(aSelState, false);
119 uint32_t i, myCount = mArray.Length(), itsCount = aSelState->mArray.Length();
120 if (myCount != itsCount) return false;
121 if (myCount < 1) return false;
122
123 for (i=0; i<myCount; i++)
124 {
125 nsRefPtr<nsRange> myRange, itsRange;
126 mArray[i]->GetRange(getter_AddRefs(myRange));
127 aSelState->mArray[i]->GetRange(getter_AddRefs(itsRange));
128 NS_ENSURE_TRUE(myRange && itsRange, false);
129
130 int16_t compResult;
131 nsresult rv;
132 rv = myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
133 if (NS_FAILED(rv) || compResult) return false;
134 rv = myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
135 if (NS_FAILED(rv) || compResult) return false;
136 }
137 // if we got here, they are equal
138 return true;
139 }
140
141 void
142 nsSelectionState::MakeEmpty()
143 {
144 // free any items in the array
145 mArray.Clear();
146 }
147
148 bool
149 nsSelectionState::IsEmpty()
150 {
151 return mArray.IsEmpty();
152 }
153
154 /***************************************************************************
155 * nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions.
156 */
157
158 nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(false) {}
159
160 nsRangeUpdater::~nsRangeUpdater()
161 {
162 // nothing to do, we don't own the items in our array.
163 }
164
165 void
166 nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
167 {
168 if (!aRangeItem) return;
169 if (mArray.Contains(aRangeItem))
170 {
171 NS_ERROR("tried to register an already registered range");
172 return; // don't register it again. It would get doubly adjusted.
173 }
174 mArray.AppendElement(aRangeItem);
175 }
176
177 void
178 nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
179 {
180 if (!aRangeItem) return;
181 mArray.RemoveElement(aRangeItem);
182 }
183
184 nsresult
185 nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
186 {
187 uint32_t i, theCount = aSelState.mArray.Length();
188 if (theCount < 1) return NS_ERROR_FAILURE;
189
190 for (i=0; i<theCount; i++)
191 {
192 RegisterRangeItem(aSelState.mArray[i]);
193 }
194
195 return NS_OK;
196 }
197
198 nsresult
199 nsRangeUpdater::DropSelectionState(nsSelectionState &aSelState)
200 {
201 uint32_t i, theCount = aSelState.mArray.Length();
202 if (theCount < 1) return NS_ERROR_FAILURE;
203
204 for (i=0; i<theCount; i++)
205 {
206 DropRangeItem(aSelState.mArray[i]);
207 }
208
209 return NS_OK;
210 }
211
212 // gravity methods:
213
214 nsresult
215 nsRangeUpdater::SelAdjCreateNode(nsIDOMNode *aParent, int32_t aPosition)
216 {
217 if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
218 NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER);
219 uint32_t i, count = mArray.Length();
220 if (!count) {
221 return NS_OK;
222 }
223
224 nsRangeStore *item;
225
226 for (i=0; i<count; i++)
227 {
228 item = mArray[i];
229 NS_ENSURE_TRUE(item, NS_ERROR_NULL_POINTER);
230
231 if ((item->startNode.get() == aParent) && (item->startOffset > aPosition))
232 item->startOffset++;
233 if ((item->endNode.get() == aParent) && (item->endOffset > aPosition))
234 item->endOffset++;
235 }
236 return NS_OK;
237 }
238
239 nsresult
240 nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, int32_t aPosition)
241 {
242 return SelAdjCreateNode(aParent, aPosition);
243 }
244
245 void
246 nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode)
247 {
248 if (mLock) {
249 // lock set by Will/DidReplaceParent, etc...
250 return;
251 }
252 MOZ_ASSERT(aNode);
253 uint32_t i, count = mArray.Length();
254 if (!count) {
255 return;
256 }
257
258 int32_t offset = 0;
259 nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(aNode, &offset);
260
261 // check for range endpoints that are after aNode and in the same parent
262 nsRangeStore *item;
263 for (i=0; i<count; i++)
264 {
265 item = mArray[i];
266 MOZ_ASSERT(item);
267
268 if ((item->startNode.get() == parent) && (item->startOffset > offset))
269 item->startOffset--;
270 if ((item->endNode.get() == parent) && (item->endOffset > offset))
271 item->endOffset--;
272
273 // check for range endpoints that are in aNode
274 if (item->startNode == aNode)
275 {
276 item->startNode = parent;
277 item->startOffset = offset;
278 }
279 if (item->endNode == aNode)
280 {
281 item->endNode = parent;
282 item->endOffset = offset;
283 }
284
285 // check for range endpoints that are in descendants of aNode
286 nsCOMPtr<nsIDOMNode> oldStart;
287 if (nsEditorUtils::IsDescendantOf(item->startNode, aNode))
288 {
289 oldStart = item->startNode; // save for efficiency hack below.
290 item->startNode = parent;
291 item->startOffset = offset;
292 }
293
294 // avoid having to call IsDescendantOf() for common case of range startnode == range endnode.
295 if ((item->endNode == oldStart) || nsEditorUtils::IsDescendantOf(item->endNode, aNode))
296 {
297 item->endNode = parent;
298 item->endOffset = offset;
299 }
300 }
301 }
302
303
304 nsresult
305 nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, int32_t aOffset, nsIDOMNode *aNewLeftNode)
306 {
307 if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
308 NS_ENSURE_TRUE(aOldRightNode && aNewLeftNode, NS_ERROR_NULL_POINTER);
309 uint32_t i, count = mArray.Length();
310 if (!count) {
311 return NS_OK;
312 }
313
314 int32_t offset;
315 nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(aOldRightNode, &offset);
316
317 // first part is same as inserting aNewLeftnode
318 nsresult result = SelAdjInsertNode(parent,offset-1);
319 NS_ENSURE_SUCCESS(result, result);
320
321 // next step is to check for range enpoints inside aOldRightNode
322 nsRangeStore *item;
323
324 for (i=0; i<count; i++)
325 {
326 item = mArray[i];
327 NS_ENSURE_TRUE(item, NS_ERROR_NULL_POINTER);
328
329 if (item->startNode.get() == aOldRightNode)
330 {
331 if (item->startOffset > aOffset)
332 {
333 item->startOffset -= aOffset;
334 }
335 else
336 {
337 item->startNode = aNewLeftNode;
338 }
339 }
340 if (item->endNode.get() == aOldRightNode)
341 {
342 if (item->endOffset > aOffset)
343 {
344 item->endOffset -= aOffset;
345 }
346 else
347 {
348 item->endNode = aNewLeftNode;
349 }
350 }
351 }
352 return NS_OK;
353 }
354
355
356 nsresult
357 nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
358 nsIDOMNode *aRightNode,
359 nsIDOMNode *aParent,
360 int32_t aOffset,
361 int32_t aOldLeftNodeLength)
362 {
363 if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
364 NS_ENSURE_TRUE(aLeftNode && aRightNode && aParent, NS_ERROR_NULL_POINTER);
365 uint32_t i, count = mArray.Length();
366 if (!count) {
367 return NS_OK;
368 }
369
370 nsRangeStore *item;
371
372 for (i=0; i<count; i++)
373 {
374 item = mArray[i];
375 NS_ENSURE_TRUE(item, NS_ERROR_NULL_POINTER);
376
377 if (item->startNode.get() == aParent)
378 {
379 // adjust start point in aParent
380 if (item->startOffset > aOffset)
381 {
382 item->startOffset--;
383 }
384 else if (item->startOffset == aOffset)
385 {
386 // join keeps right hand node
387 item->startNode = aRightNode;
388 item->startOffset = aOldLeftNodeLength;
389 }
390 }
391 else if (item->startNode.get() == aRightNode)
392 {
393 // adjust start point in aRightNode
394 item->startOffset += aOldLeftNodeLength;
395 }
396 else if (item->startNode.get() == aLeftNode)
397 {
398 // adjust start point in aLeftNode
399 item->startNode = aRightNode;
400 }
401
402 if (item->endNode.get() == aParent)
403 {
404 // adjust end point in aParent
405 if (item->endOffset > aOffset)
406 {
407 item->endOffset--;
408 }
409 else if (item->endOffset == aOffset)
410 {
411 // join keeps right hand node
412 item->endNode = aRightNode;
413 item->endOffset = aOldLeftNodeLength;
414 }
415 }
416 else if (item->endNode.get() == aRightNode)
417 {
418 // adjust end point in aRightNode
419 item->endOffset += aOldLeftNodeLength;
420 }
421 else if (item->endNode.get() == aLeftNode)
422 {
423 // adjust end point in aLeftNode
424 item->endNode = aRightNode;
425 }
426 }
427
428 return NS_OK;
429 }
430
431
432 nsresult
433 nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString)
434 {
435 if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
436
437 uint32_t count = mArray.Length();
438 if (!count) {
439 return NS_OK;
440 }
441 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aTextNode));
442 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
443
444 uint32_t len=aString.Length(), i;
445 nsRangeStore *item;
446 for (i=0; i<count; i++)
447 {
448 item = mArray[i];
449 NS_ENSURE_TRUE(item, NS_ERROR_NULL_POINTER);
450
451 if ((item->startNode.get() == node) && (item->startOffset > aOffset))
452 item->startOffset += len;
453 if ((item->endNode.get() == node) && (item->endOffset > aOffset))
454 item->endOffset += len;
455 }
456 return NS_OK;
457 }
458
459
460 nsresult
461 nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength)
462 {
463 if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
464
465 uint32_t i, count = mArray.Length();
466 if (!count) {
467 return NS_OK;
468 }
469 nsRangeStore *item;
470 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aTextNode));
471 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
472
473 for (i=0; i<count; i++)
474 {
475 item = mArray[i];
476 NS_ENSURE_TRUE(item, NS_ERROR_NULL_POINTER);
477
478 if ((item->startNode.get() == node) && (item->startOffset > aOffset))
479 {
480 item->startOffset -= aLength;
481 if (item->startOffset < 0) item->startOffset = 0;
482 }
483 if ((item->endNode.get() == node) && (item->endOffset > aOffset))
484 {
485 item->endOffset -= aLength;
486 if (item->endOffset < 0) item->endOffset = 0;
487 }
488 }
489 return NS_OK;
490 }
491
492
493 nsresult
494 nsRangeUpdater::WillReplaceContainer()
495 {
496 if (mLock) return NS_ERROR_UNEXPECTED;
497 mLock = true;
498 return NS_OK;
499 }
500
501
502 nsresult
503 nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
504 {
505 NS_ENSURE_TRUE(mLock, NS_ERROR_UNEXPECTED);
506 mLock = false;
507
508 NS_ENSURE_TRUE(aOriginalNode && aNewNode, NS_ERROR_NULL_POINTER);
509 uint32_t i, count = mArray.Length();
510 if (!count) {
511 return NS_OK;
512 }
513
514 nsRangeStore *item;
515
516 for (i=0; i<count; i++)
517 {
518 item = mArray[i];
519 NS_ENSURE_TRUE(item, NS_ERROR_NULL_POINTER);
520
521 if (item->startNode.get() == aOriginalNode)
522 item->startNode = aNewNode;
523 if (item->endNode.get() == aOriginalNode)
524 item->endNode = aNewNode;
525 }
526 return NS_OK;
527 }
528
529
530 nsresult
531 nsRangeUpdater::WillRemoveContainer()
532 {
533 if (mLock) return NS_ERROR_UNEXPECTED;
534 mLock = true;
535 return NS_OK;
536 }
537
538
539 nsresult
540 nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aOffset, uint32_t aNodeOrigLen)
541 {
542 NS_ENSURE_TRUE(mLock, NS_ERROR_UNEXPECTED);
543 mLock = false;
544
545 NS_ENSURE_TRUE(aNode && aParent, NS_ERROR_NULL_POINTER);
546 uint32_t i, count = mArray.Length();
547 if (!count) {
548 return NS_OK;
549 }
550
551 nsRangeStore *item;
552
553 for (i=0; i<count; i++)
554 {
555 item = mArray[i];
556 NS_ENSURE_TRUE(item, NS_ERROR_NULL_POINTER);
557
558 if (item->startNode.get() == aNode)
559 {
560 item->startNode = aParent;
561 item->startOffset += aOffset;
562 }
563 else if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
564 item->startOffset += (int32_t)aNodeOrigLen-1;
565
566 if (item->endNode.get() == aNode)
567 {
568 item->endNode = aParent;
569 item->endOffset += aOffset;
570 }
571 else if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
572 item->endOffset += (int32_t)aNodeOrigLen-1;
573 }
574 return NS_OK;
575 }
576
577
578 nsresult
579 nsRangeUpdater::WillInsertContainer()
580 {
581 if (mLock) return NS_ERROR_UNEXPECTED;
582 mLock = true;
583 return NS_OK;
584 }
585
586
587 nsresult
588 nsRangeUpdater::DidInsertContainer()
589 {
590 NS_ENSURE_TRUE(mLock, NS_ERROR_UNEXPECTED);
591 mLock = false;
592 return NS_OK;
593 }
594
595
596 void
597 nsRangeUpdater::WillMoveNode()
598 {
599 mLock = true;
600 }
601
602
603 void
604 nsRangeUpdater::DidMoveNode(nsINode* aOldParent, int32_t aOldOffset,
605 nsINode* aNewParent, int32_t aNewOffset)
606 {
607 MOZ_ASSERT(aOldParent);
608 MOZ_ASSERT(aNewParent);
609 NS_ENSURE_TRUE_VOID(mLock);
610 mLock = false;
611
612 nsIDOMNode* oldParent = aOldParent->AsDOMNode();
613 nsIDOMNode* newParent = aNewParent->AsDOMNode();
614
615 for (uint32_t i = 0, count = mArray.Length(); i < count; ++i) {
616 nsRangeStore* item = mArray[i];
617 NS_ENSURE_TRUE_VOID(item);
618
619 // like a delete in aOldParent
620 if (item->startNode == oldParent && item->startOffset > aOldOffset) {
621 item->startOffset--;
622 }
623 if (item->endNode == oldParent && item->endOffset > aOldOffset) {
624 item->endOffset--;
625 }
626
627 // and like an insert in aNewParent
628 if (item->startNode == newParent && item->startOffset > aNewOffset) {
629 item->startOffset++;
630 }
631 if (item->endNode == newParent && item->endOffset > aNewOffset) {
632 item->endOffset++;
633 }
634 }
635 }
636
637
638
639 /***************************************************************************
640 * helper class for nsSelectionState. nsRangeStore stores range endpoints.
641 */
642
643 // DEBUG: int32_t nsRangeStore::n = 0;
644
645 nsRangeStore::nsRangeStore()
646 {
647 // DEBUG: n++; printf("range store alloc count=%d\n", n);
648 }
649 nsRangeStore::~nsRangeStore()
650 {
651 // DEBUG: n--; printf("range store alloc count=%d\n", n);
652 }
653
654 nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
655 {
656 NS_ENSURE_TRUE(aRange, NS_ERROR_NULL_POINTER);
657 aRange->GetStartContainer(getter_AddRefs(startNode));
658 aRange->GetEndContainer(getter_AddRefs(endNode));
659 aRange->GetStartOffset(&startOffset);
660 aRange->GetEndOffset(&endOffset);
661 return NS_OK;
662 }
663
664 nsresult nsRangeStore::GetRange(nsRange** outRange)
665 {
666 return nsRange::CreateRange(startNode, startOffset, endNode, endOffset,
667 outRange);
668 }

mercurial