OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 25 matching lines...) Expand all Loading... |
36 #include "visible_units.h" | 36 #include "visible_units.h" |
37 #include "Range.h" | 37 #include "Range.h" |
38 #include <wtf/Assertions.h> | 38 #include <wtf/Assertions.h> |
39 #include <stdio.h> | 39 #include <stdio.h> |
40 | 40 |
41 namespace WebCore { | 41 namespace WebCore { |
42 | 42 |
43 Selection::Selection() | 43 Selection::Selection() |
44 : m_affinity(DOWNSTREAM) | 44 : m_affinity(DOWNSTREAM) |
45 , m_granularity(CharacterGranularity) | 45 , m_granularity(CharacterGranularity) |
46 , m_state(NONE) | 46 , m_selectionType(NoSelection) |
47 , m_baseIsFirst(true) | 47 , m_baseIsFirst(true) |
48 { | 48 { |
49 } | 49 } |
50 | 50 |
51 Selection::Selection(const Position& pos, EAffinity affinity) | 51 Selection::Selection(const Position& pos, EAffinity affinity) |
52 : m_base(pos) | 52 : m_base(pos) |
53 , m_extent(pos) | 53 , m_extent(pos) |
54 , m_affinity(affinity) | 54 , m_affinity(affinity) |
55 , m_granularity(CharacterGranularity) | 55 , m_granularity(CharacterGranularity) |
56 { | 56 { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 m_extent = position; | 115 m_extent = position; |
116 validate(); | 116 validate(); |
117 } | 117 } |
118 | 118 |
119 void Selection::setExtent(const VisiblePosition& visiblePosition) | 119 void Selection::setExtent(const VisiblePosition& visiblePosition) |
120 { | 120 { |
121 m_extent = visiblePosition.deepEquivalent(); | 121 m_extent = visiblePosition.deepEquivalent(); |
122 validate(); | 122 validate(); |
123 } | 123 } |
124 | 124 |
125 PassRefPtr<Range> Selection::toRange() const | 125 PassRefPtr<Range> Selection::firstRange() const |
| 126 { |
| 127 if (isNone()) |
| 128 return 0; |
| 129 Position start = rangeCompliantEquivalent(m_start); |
| 130 Position end = rangeCompliantEquivalent(m_end); |
| 131 return Range::create(start.node()->document(), start, end); |
| 132 } |
| 133 |
| 134 PassRefPtr<Range> Selection::toNormalizedRange() const |
126 { | 135 { |
127 if (isNone()) | 136 if (isNone()) |
128 return 0; | 137 return 0; |
129 | 138 |
130 // Make sure we have an updated layout since this function is called | 139 // Make sure we have an updated layout since this function is called |
131 // in the course of running edit commands which modify the DOM. | 140 // in the course of running edit commands which modify the DOM. |
132 // Failing to call this can result in equivalentXXXPosition calls returning | 141 // Failing to call this can result in equivalentXXXPosition calls returning |
133 // incorrect results. | 142 // incorrect results. |
134 m_start.node()->document()->updateLayout(); | 143 m_start.node()->document()->updateLayout(); |
135 | 144 |
(...skipping 27 matching lines...) Expand all Loading... |
163 // Make sure the start is before the end. | 172 // Make sure the start is before the end. |
164 // The end can wind up before the start if collapsed whitespace is t
he only thing selected. | 173 // The end can wind up before the start if collapsed whitespace is t
he only thing selected. |
165 Position tmp = s; | 174 Position tmp = s; |
166 s = e; | 175 s = e; |
167 e = tmp; | 176 e = tmp; |
168 } | 177 } |
169 s = rangeCompliantEquivalent(s); | 178 s = rangeCompliantEquivalent(s); |
170 e = rangeCompliantEquivalent(e); | 179 e = rangeCompliantEquivalent(e); |
171 } | 180 } |
172 | 181 |
173 ExceptionCode ec = 0; | 182 // Selections are supposed to always be valid. This constructor will ASSERT |
174 RefPtr<Range> result(Range::create(s.node()->document())); | 183 // if a valid range could not be created, which is fine for this callsite. |
175 result->setStart(s.node(), s.offset(), ec); | 184 return Range::create(s.node()->document(), s, e); |
176 if (ec) { | |
177 LOG_ERROR("Exception setting Range start from Selection: %d", ec); | |
178 return 0; | |
179 } | |
180 result->setEnd(e.node(), e.offset(), ec); | |
181 if (ec) { | |
182 LOG_ERROR("Exception setting Range end from Selection: %d", ec); | |
183 return 0; | |
184 } | |
185 return result.release(); | |
186 } | 185 } |
187 | 186 |
188 bool Selection::expandUsingGranularity(TextGranularity granularity) | 187 bool Selection::expandUsingGranularity(TextGranularity granularity) |
189 { | 188 { |
190 if (isNone()) | 189 if (isNone()) |
191 return false; | 190 return false; |
192 | 191 |
193 m_granularity = granularity; | 192 m_granularity = granularity; |
194 validate(); | 193 validate(); |
195 return true; | 194 return true; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 CharacterIterator charIt(searchRange.get(), true); | 230 CharacterIterator charIt(searchRange.get(), true); |
232 | 231 |
233 for (; charIt.length(); charIt.advance(1)) { | 232 for (; charIt.length(); charIt.advance(1)) { |
234 UChar c = charIt.characters()[0]; | 233 UChar c = charIt.characters()[0]; |
235 if (!isSpaceOrNewline(c) && c != noBreakSpace) | 234 if (!isSpaceOrNewline(c) && c != noBreakSpace) |
236 break; | 235 break; |
237 m_end = charIt.range()->endPosition(); | 236 m_end = charIt.range()->endPosition(); |
238 } | 237 } |
239 } | 238 } |
240 | 239 |
241 void Selection::validate() | 240 void Selection::setBaseAndExtentToDeepEquivalents() |
242 { | 241 { |
243 // Move the selection to rendered positions, if possible. | 242 // Move the selection to rendered positions, if possible. |
244 bool baseAndExtentEqual = m_base == m_extent; | 243 bool baseAndExtentEqual = m_base == m_extent; |
245 if (m_base.isNotNull()) { | 244 if (m_base.isNotNull()) { |
246 m_base = VisiblePosition(m_base, m_affinity).deepEquivalent(); | 245 m_base = VisiblePosition(m_base, m_affinity).deepEquivalent(); |
247 if (baseAndExtentEqual) | 246 if (baseAndExtentEqual) |
248 m_extent = m_base; | 247 m_extent = m_base; |
249 } | 248 } |
250 if (m_extent.isNotNull() && !baseAndExtentEqual) | 249 if (m_extent.isNotNull() && !baseAndExtentEqual) |
251 m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent(); | 250 m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent(); |
252 | 251 |
253 // Make sure we do not have a dangling base or extent. | 252 // Make sure we do not have a dangling base or extent. |
254 if (m_base.isNull() && m_extent.isNull()) | 253 if (m_base.isNull() && m_extent.isNull()) |
255 m_baseIsFirst = true; | 254 m_baseIsFirst = true; |
256 else if (m_base.isNull()) { | 255 else if (m_base.isNull()) { |
257 m_base = m_extent; | 256 m_base = m_extent; |
258 m_baseIsFirst = true; | 257 m_baseIsFirst = true; |
259 } else if (m_extent.isNull()) { | 258 } else if (m_extent.isNull()) { |
260 m_extent = m_base; | 259 m_extent = m_base; |
261 m_baseIsFirst = true; | 260 m_baseIsFirst = true; |
262 } else { | 261 } else |
263 m_baseIsFirst = comparePositions(m_base, m_extent) <= 0; | 262 m_baseIsFirst = comparePositions(m_base, m_extent) <= 0; |
264 } | 263 } |
265 | 264 |
| 265 void Selection::setStartAndEndFromBaseAndExtentRespectingGranularity() |
| 266 { |
266 if (m_baseIsFirst) { | 267 if (m_baseIsFirst) { |
267 m_start = m_base; | 268 m_start = m_base; |
268 m_end = m_extent; | 269 m_end = m_extent; |
269 } else { | 270 } else { |
270 m_start = m_extent; | 271 m_start = m_extent; |
271 m_end = m_base; | 272 m_end = m_base; |
272 } | 273 } |
273 | 274 |
274 // Expand the selection if requested. | |
275 switch (m_granularity) { | 275 switch (m_granularity) { |
276 case CharacterGranularity: | 276 case CharacterGranularity: |
277 // Don't do any expansion. | 277 // Don't do any expansion. |
278 break; | 278 break; |
279 case WordGranularity: { | 279 case WordGranularity: { |
280 // General case: Select the word the caret is positioned inside of,
or at the start of (RightWordIfOnBoundary). | 280 // General case: Select the word the caret is positioned inside of,
or at the start of (RightWordIfOnBoundary). |
281 // Edge case: If the caret is after the last word in a soft-wrapped
line or the last word in | 281 // Edge case: If the caret is after the last word in a soft-wrapped
line or the last word in |
282 // the document, select that last word (LeftWordIfOnBoundary). | 282 // the document, select that last word (LeftWordIfOnBoundary). |
283 // Edge case: If the caret is after the last word in a paragraph, se
lect from the the end of the | 283 // Edge case: If the caret is after the last word in a paragraph, se
lect from the the end of the |
284 // last word to the line break (also RightWordIfOnBoundary); | 284 // last word to the line break (also RightWordIfOnBoundary); |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deep
Equivalent(); | 378 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deep
Equivalent(); |
379 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquiva
lent(); | 379 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquiva
lent(); |
380 break; | 380 break; |
381 } | 381 } |
382 | 382 |
383 // Make sure we do not have a dangling start or end. | 383 // Make sure we do not have a dangling start or end. |
384 if (m_start.isNull()) | 384 if (m_start.isNull()) |
385 m_start = m_end; | 385 m_start = m_end; |
386 if (m_end.isNull()) | 386 if (m_end.isNull()) |
387 m_end = m_start; | 387 m_end = m_start; |
388 | 388 } |
389 adjustForEditableContent(); | 389 |
390 | 390 void Selection::updateSelectionType() |
391 // adjust the state | 391 { |
392 if (m_start.isNull()) { | 392 if (m_start.isNull()) { |
393 ASSERT(m_end.isNull()); | 393 ASSERT(m_end.isNull()); |
394 m_state = NONE; | 394 m_selectionType = NoSelection; |
395 | 395 } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) { |
396 // enforce downstream affinity if not caret, as affinity only | 396 m_selectionType = CaretSelection; |
397 // makes sense for caret | 397 } else |
| 398 m_selectionType = RangeSelection; |
| 399 |
| 400 // Affinity only makes sense for a caret |
| 401 if (m_selectionType != CaretSelection) |
398 m_affinity = DOWNSTREAM; | 402 m_affinity = DOWNSTREAM; |
399 } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) { | 403 } |
400 m_state = CARET; | 404 |
401 } else { | 405 void Selection::validate() |
402 m_state = RANGE; | 406 { |
403 | 407 setBaseAndExtentToDeepEquivalents(); |
404 // enforce downstream affinity if not caret, as affinity only | 408 setStartAndEndFromBaseAndExtentRespectingGranularity(); |
405 // makes sense for caret | 409 adjustSelectionToAvoidCrossingEditingBoundaries(); |
406 m_affinity = DOWNSTREAM; | 410 updateSelectionType(); |
407 | 411 |
| 412 if (selectionType() == RangeSelection) { |
408 // "Constrain" the selection to be the smallest equivalent range of node
s. | 413 // "Constrain" the selection to be the smallest equivalent range of node
s. |
409 // This is a somewhat arbitrary choice, but experience shows that it is | 414 // This is a somewhat arbitrary choice, but experience shows that it is |
410 // useful to make to make the selection "canonical" (if only for | 415 // useful to make to make the selection "canonical" (if only for |
411 // purposes of comparing selections). This is an ideal point of the code | 416 // purposes of comparing selections). This is an ideal point of the code |
412 // to do this operation, since all selection changes that result in a RA
NGE | 417 // to do this operation, since all selection changes that result in a RA
NGE |
413 // come through here before anyone uses it. | 418 // come through here before anyone uses it. |
414 // FIXME: Canonicalizing is good, but haven't we already done it (when w
e | 419 // FIXME: Canonicalizing is good, but haven't we already done it (when w
e |
415 // set these two positions to VisiblePosition deepEquivalent()s above)? | 420 // set these two positions to VisiblePosition deepEquivalent()s above)? |
416 m_start = m_start.downstream(); | 421 m_start = m_start.downstream(); |
417 m_end = m_end.upstream(); | 422 m_end = m_end.upstream(); |
(...skipping 16 matching lines...) Expand all Loading... |
434 m_base = base; | 439 m_base = base; |
435 m_extent = extent; | 440 m_extent = extent; |
436 m_baseIsFirst = comparePositions(base, extent) <= 0; | 441 m_baseIsFirst = comparePositions(base, extent) <= 0; |
437 if (m_baseIsFirst) { | 442 if (m_baseIsFirst) { |
438 m_start = base; | 443 m_start = base; |
439 m_end = extent; | 444 m_end = extent; |
440 } else { | 445 } else { |
441 m_start = extent; | 446 m_start = extent; |
442 m_end = base; | 447 m_end = base; |
443 } | 448 } |
444 m_state = RANGE; | 449 m_selectionType = RangeSelection; |
445 } | 450 } |
446 | 451 |
447 void Selection::adjustForEditableContent() | 452 void Selection::adjustSelectionToAvoidCrossingEditingBoundaries() |
448 { | 453 { |
449 if (m_base.isNull() || m_start.isNull() || m_end.isNull()) | 454 if (m_base.isNull() || m_start.isNull() || m_end.isNull()) |
450 return; | 455 return; |
451 | 456 |
452 Node* baseRoot = highestEditableRoot(m_base); | 457 Node* baseRoot = highestEditableRoot(m_base); |
453 Node* startRoot = highestEditableRoot(m_start); | 458 Node* startRoot = highestEditableRoot(m_start); |
454 Node* endRoot = highestEditableRoot(m_end); | 459 Node* endRoot = highestEditableRoot(m_end); |
455 | 460 |
456 Node* baseEditableAncestor = lowestEditableAncestor(m_base.node()); | 461 Node* baseEditableAncestor = lowestEditableAncestor(m_base.node()); |
457 | 462 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 if (p.isNull() && endRoot && (shadowAncestor != endRoot)) | 503 if (p.isNull() && endRoot && (shadowAncestor != endRoot)) |
499 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); | 504 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); |
500 while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEd
itableAncestor && !isEditablePosition(p))) { | 505 while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEd
itableAncestor && !isEditablePosition(p))) { |
501 Node* root = editableRootForPosition(p); | 506 Node* root = editableRootForPosition(p); |
502 shadowAncestor = root ? root->shadowAncestorNode() : 0; | 507 shadowAncestor = root ? root->shadowAncestorNode() : 0; |
503 p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : prev
iousVisuallyDistinctCandidate(p); | 508 p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : prev
iousVisuallyDistinctCandidate(p); |
504 if (p.isNull() && (shadowAncestor != root)) | 509 if (p.isNull() && (shadowAncestor != root)) |
505 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); | 510 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); |
506 } | 511 } |
507 VisiblePosition previous(p); | 512 VisiblePosition previous(p); |
508 | 513 |
509 if (previous.isNull()) { | 514 if (previous.isNull()) { |
| 515 // The selection crosses an Editing boundary. This is a |
| 516 // programmer error in the editing code. Happy debugging! |
510 ASSERT_NOT_REACHED(); | 517 ASSERT_NOT_REACHED(); |
511 m_base = Position(); | 518 m_base = Position(); |
512 m_extent = Position(); | 519 m_extent = Position(); |
513 validate(); | 520 validate(); |
514 return; | 521 return; |
515 } | 522 } |
516 m_end = previous.deepEquivalent(); | 523 m_end = previous.deepEquivalent(); |
517 } | 524 } |
518 | 525 |
519 // The selection starts in editable content or non-editable content insi
de a different editable ancestor, | 526 // The selection starts in editable content or non-editable content insi
de a different editable ancestor, |
520 // move forward until non-editable content inside the same lowest editab
le ancestor is reached. | 527 // move forward until non-editable content inside the same lowest editab
le ancestor is reached. |
521 Node* startEditableAncestor = lowestEditableAncestor(m_start.node());
| 528 Node* startEditableAncestor = lowestEditableAncestor(m_start.node());
|
522 if (startRoot || startEditableAncestor != baseEditableAncestor) { | 529 if (startRoot || startEditableAncestor != baseEditableAncestor) { |
523 Position p = nextVisuallyDistinctCandidate(m_start); | 530 Position p = nextVisuallyDistinctCandidate(m_start); |
524 Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() :
0; | 531 Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() :
0; |
525 if (p.isNull() && startRoot && (shadowAncestor != startRoot)) | 532 if (p.isNull() && startRoot && (shadowAncestor != startRoot)) |
526 p = Position(shadowAncestor, 0); | 533 p = Position(shadowAncestor, 0); |
527 while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEd
itableAncestor && !isEditablePosition(p))) { | 534 while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEd
itableAncestor && !isEditablePosition(p))) { |
528 Node* root = editableRootForPosition(p); | 535 Node* root = editableRootForPosition(p); |
529 shadowAncestor = root ? root->shadowAncestorNode() : 0; | 536 shadowAncestor = root ? root->shadowAncestorNode() : 0; |
530 p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextV
isuallyDistinctCandidate(p); | 537 p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextV
isuallyDistinctCandidate(p); |
531 if (p.isNull() && (shadowAncestor != root)) | 538 if (p.isNull() && (shadowAncestor != root)) |
532 p = Position(shadowAncestor, 0); | 539 p = Position(shadowAncestor, 0); |
533 } | 540 } |
534 VisiblePosition next(p); | 541 VisiblePosition next(p); |
535 | 542 |
536 if (next.isNull()) { | 543 if (next.isNull()) { |
| 544 // The selection crosses an Editing boundary. This is a |
| 545 // programmer error in the editing code. Happy debugging! |
537 ASSERT_NOT_REACHED(); | 546 ASSERT_NOT_REACHED(); |
538 m_base = Position(); | 547 m_base = Position(); |
539 m_extent = Position(); | 548 m_extent = Position(); |
540 validate(); | 549 validate(); |
541 return; | 550 return; |
542 } | 551 } |
543 m_start = next.deepEquivalent(); | 552 m_start = next.deepEquivalent(); |
544 } | 553 } |
545 } | 554 } |
546 | 555 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
633 sel.showTreeForThis(); | 642 sel.showTreeForThis(); |
634 } | 643 } |
635 | 644 |
636 void showTree(const WebCore::Selection* sel) | 645 void showTree(const WebCore::Selection* sel) |
637 { | 646 { |
638 if (sel) | 647 if (sel) |
639 sel->showTreeForThis(); | 648 sel->showTreeForThis(); |
640 } | 649 } |
641 | 650 |
642 #endif | 651 #endif |
OLD | NEW |