34 - (BOOL)textView:(
CPTextView)aTextView doCommandBySelector:(
SEL)aSelector;
35 - (BOOL)textView:(
CPTextView)aTextView shouldChangeTextInRange:(CPRange)affectedCharRange replacementString:(
CPString)replacementString;
37 - (CPRange)textView:(
CPTextView)aTextView willChangeSelectionFromCharacterRange:(CPRange)oldSelectedCharRange toCharacterRange:(CPRange)newSelectedCharRange;
39 - (void)textViewDidChangeTypingAttributes:(
CPNotification)aNotification;
43 _MakeRangeFromAbs =
function(a1, a2)
48 _MidRange =
function(a1)
50 return Math.floor((
CPMaxRange(a1) + a1.location) / 2);
53 function _isWhitespaceCharacter(chr)
55 return (chr ===
'\n' || chr ===
'\r' || chr ===
' ' || chr ===
'\t');
58 _characterTripletFromStringAtIndex =
function(string, index)
61 string =
string._string;
63 var tripletRange = _MakeRangeFromAbs(MAX(0, index - 1), MIN(
string.length, index + 2));
65 return [string substringWithRange:tripletRange];
68 _regexMatchesStringAtIndex=
function(regex, string, index)
70 var triplet = _characterTripletFromStringAtIndex(
string, index);
72 return regex.exec(triplet) !== null;
79 @
typedef CPSelectionGranularity
80 CPSelectByCharacter = 0;
103 BOOL _isHorizontallyResizable;
104 BOOL _isVerticallyResizable;
106 CGPoint _textContainerOrigin;
109 CGSize _textContainerInset;
116 CPRange _selectionRange;
117 CPSelectionGranularity _selectionGranularity;
119 CPSelectionGranularity _previousSelectionGranularity;
125 unsigned _delegateRespondsToSelectorMask;
127 int _startTrackingLocation;
132 BOOL _scrollingDownward;
133 CPRange _movingSelection;
135 int _stickyXLocation;
137 CPArray _selectionSpans;
145 BOOL _firstResponderButNotEditingYet;
147 CPRange _mouseDownOldSelection;
150 + (Class)_binderClassForBinding:(
CPString)aBinding
153 return [_CPTextViewValueBinder class];
155 return [
super _binderClassForBinding:aBinding];
159 #pragma mark Class methods
168 #pragma mark Init methods
172 if (
self = [super initWithFrame:aFrame])
182 _usesFontPanel = YES;
186 forKey:CPBackgroundColorAttributeName];
200 - (id)initWithFrame:(CGRect)aFrame
210 _DOMElement.style.cursor =
"text";
214 _textContainerInset = CGSizeMake(2, 0);
215 _textContainerOrigin = CGPointMake(_bounds.origin.x, _bounds.origin.y);
217 _selectionGranularity = CPSelectByCharacter;
219 _minSize = CGSizeCreateCopy(_frame.size);
220 _maxSize = CGSizeMake(_frame.size.width, 1e7);
222 _isVerticallyResizable = YES;
223 _isHorizontallyResizable = NO;
228 _caret = [[_CPCaret alloc] initWithTextView:self];
229 [_caret setRect:CGRectMake(0, 0, 1, 11)];
231 var pboardTypes = [CPStringPboardType, CPColorDragType];
233 if ([
self isRichText])
236 [
self registerForDraggedTypes:pboardTypes];
239 - (void)_setObserveWindowKeyNotifications:(BOOL)shouldObserve
253 - (void)_removeObservers
258 [
super _removeObservers];
259 [
self _setObserveWindowKeyNotifications:NO];
260 [
self _removeDelegateObservers];
263 - (void)_addObservers
268 [
super _addObservers];
269 [
self _setObserveWindowKeyNotifications:YES];
270 [
self _startObservingClipView];
271 [
self _addDelegateObservers];
274 - (void)_addDelegateObservers
276 if (!_delegate)
return;
281 [nc addObserver:_delegate selector:@selector(textDidBeginEditing:) name:CPTextDidBeginEditingNotification object:self];
284 [nc addObserver:_delegate selector:@selector(textDidChange:) name:CPTextDidChangeNotification object:self];
287 [nc addObserver:_delegate selector:@selector(textDidEndEditing:) name:CPTextDidEndEditingNotification object:self];
290 [nc addObserver:_delegate selector:@selector(textViewDidChangeSelection:) name:CPTextViewDidChangeSelectionNotification object:self];
293 [nc addObserver:_delegate selector:@selector(textViewDidChangeTypingAttributes:) name:CPTextViewDidChangeTypingAttributesNotification object:self];
296 - (void)_removeDelegateObservers
298 if (!_delegate)
return;
302 if (_delegateRespondsToSelectorMask & kDelegateRespondsTo_textView_textDidBeginEditing)
303 [nc removeObserver:_delegate name:CPTextDidBeginEditingNotification object:self];
305 if (_delegateRespondsToSelectorMask & kDelegateRespondsTo_textView_textDidChange)
306 [nc removeObserver:_delegate name:CPTextDidChangeNotification object:self];
308 if (_delegateRespondsToSelectorMask & kDelegateRespondsTo_textView_textDidEndEditing)
309 [nc removeObserver:_delegate name:CPTextDidEndEditingNotification object:self];
311 if (_delegateRespondsToSelectorMask & kDelegateRespondsTo_textView_didChangeSelection)
312 [nc removeObserver:_delegate name:CPTextViewDidChangeSelectionNotification object:self];
315 [nc removeObserver:_delegate name:CPTextViewDidChangeTypingAttributesNotification object:self];
318 - (void)_startObservingClipView
320 if (!_observedClipView)
325 [_observedClipView setPostsFrameChangedNotifications:YES];
326 [_observedClipView setPostsBoundsChangedNotifications:YES];
328 [defaultCenter addObserver:self
329 selector:@selector(superviewFrameChanged:)
330 name:CPViewFrameDidChangeNotification
331 object:_observedClipView];
333 [defaultCenter addObserver:self
334 selector:@selector(superviewBoundsChanged:)
335 name:CPViewBoundsDidChangeNotification
336 object:_observedClipView];
338 - (CGRect)exposedRect
344 if ([superview isKindOfClass:[
CPClipView class]])
345 _exposedRect = [superview bounds];
347 _exposedRect = [
self bounds];
370 - (void)viewWillMoveToSuperview:(
CPView)aView
373 _observedClipView = aView;
375 [
self _stopObservingClipView];
380 - (void)_stopObservingClipView
382 if (!_observedClipView)
387 [defaultCenter removeObserver:self
388 name:CPViewFrameDidChangeNotification
389 object:_observedClipView];
391 [defaultCenter removeObserver:self
392 name:CPViewBoundsDidChangeNotification
393 object:_observedClipView];
395 _observedClipView = nil;
400 if (![[
self window] isKeyWindow])
401 [
self _resignFirstResponder];
406 if ([
self _isFocused])
407 [
self _becomeFirstResponder];
411 #pragma mark Copy and paste methods
413 - (void)copy:(
id)sender
417 if (![
self isRichText])
423 richData = [_CPRTFProducer produceRTF:stringForPasting documentAttributes:@{}];
425 [pasteboard declareTypes:[CPStringPboardType, CPRTFPboardType] owner:nil];
426 [pasteboard setString:stringForPasting._string forType:CPStringPboardType];
427 [pasteboard setString:richData forType:CPRTFPboardType];
428 [pasteboard setString:_previousSelectionGranularity + '' forType:_CPSmartPboardType];
431 - (void)_pasteString:(
id)stringForPasting
433 if (!stringForPasting)
436 var shouldUseSmartPasting = [
self _shouldUseSmartPasting];
438 if (shouldUseSmartPasting && _selectionRange.location > 0)
440 if (!_isWhitespaceCharacter([[_textStorage
string] characterAtIndex:_selectionRange.location - 1]) &&
441 _selectionRange.location != [_layoutManager numberOfCharacters])
443 [
self insertText:" "];
447 [
self insertText:stringForPasting];
449 if (shouldUseSmartPasting)
451 if (!_isWhitespaceCharacter([[_textStorage
string] characterAtIndex:
CPMaxRange(_selectionRange)]) &&
452 !_isNewlineCharacter([[_textStorage
string] characterAtIndex:MAX(0, _selectionRange.location - 1)]) &&
453 _selectionRange.location != [_layoutManager numberOfCharacters])
455 [
self insertText:" "];
459 - (void)pasteAsPlainText:(
id)sender
461 if (![sender isKindOfClass:_CPNativeInputManager] && [[
CPApp currentEvent] type] !=
CPAppKitDefined)
464 [
self _pasteString:[
self _plainStringForPasting]];
467 - (void)paste:(
id)sender
469 if (![sender isKindOfClass:_CPNativeInputManager] && [[
CPApp currentEvent] type] !=
CPAppKitDefined)
472 [
self _pasteString:[
self _stringForPasting]];
476 #pragma mark Responders method
478 - (BOOL)acceptsFirstResponder
483 - (void)_becomeFirstResponder
485 [
self updateInsertionPointStateAndRestartTimer:YES];
487 [
self setNeedsDisplay:YES];
492 - (BOOL)becomeFirstResponder
495 _firstResponderButNotEditingYet = YES;
496 [
self _becomeFirstResponder];
501 - (void)_resignFirstResponder
503 [
self _reverseSetBinding];
504 [_caret stopBlinking];
505 [
self setNeedsDisplay:YES];
506 [_CPNativeInputManager cancelCurrentInputSessionIfNeeded];
509 - (BOOL)resignFirstResponder
511 if (_firstResponderButNotEditingYet)
512 _firstResponderButNotEditingYet = NO;
515 if ([
self _sendDelegateTextShouldEndEditing])
521 [
self _resignFirstResponder];
528 #pragma mark Delegate methods
535 if (aDelegate === _delegate)
538 [
self _removeDelegateObservers];
540 _delegateRespondsToSelectorMask = 0;
541 _delegate = aDelegate;
545 if ([_delegate respondsToSelector:
@selector(textDidChange:)])
548 if ([_delegate respondsToSelector:
@selector(textViewDidChangeSelection:)])
551 if ([_delegate respondsToSelector:
@selector(textViewDidChangeTypingAttributes:)])
554 if ([_delegate respondsToSelector:
@selector(textDidBeginEditing:)])
557 if ([_delegate respondsToSelector:
@selector(textDidEndEditing:)])
560 if ([_delegate respondsToSelector:
@selector(textView:doCommandBySelector:)])
563 if ([_delegate respondsToSelector:
@selector(textShouldBeginEditing:)])
566 if ([_delegate respondsToSelector:
@selector(textShouldEndEditing:)])
569 if ([_delegate respondsToSelector:
@selector(textView:willChangeSelectionFromCharacterRange:toCharacterRange:)])
572 if ([_delegate respondsToSelector:
@selector(textView:shouldChangeTextInRange:replacementString:)])
575 if ([_delegate respondsToSelector:
@selector(textView:shouldChangeTypingAttributes:toAttributes:)])
579 [
self _addDelegateObservers];
585 #pragma mark Key window methods
587 - (void)becomeKeyWindow
595 - (void)resignKeyWindow
600 - (BOOL)_isFirstResponder
602 return [[
self window] firstResponder] ===
self;
607 return [[
self window] isKeyWindow] && [
self _isFirstResponder];
612 #pragma mark Undo redo methods
614 - (void)undo:(
id)sender
620 - (void)redo:(
id)sender
628 #pragma mark Accessors
632 return _textStorage._string;
637 if (_placeholderString)
640 return [
self isRichText]? _textStorage : _textStorage._string;
642 - (void)setObjectValue:(
id)aValue
644 if (_placeholderString)
651 aValue = [aValue description];
656 - (void)setString:(
id)aString
660 [_textStorage replaceCharactersInRange:CPMakeRange(0, [_layoutManager numberOfCharacters]) withAttributedString:aString];
664 [_textStorage replaceCharactersInRange:CPMakeRange(0, [_layoutManager numberOfCharacters]) withString:aString];
667 if (
CPMaxRange(_selectionRange) > [_layoutManager numberOfCharacters])
668 [
self setSelectedRange:CPMakeRange([_layoutManager numberOfCharacters], 0)];
671 [_layoutManager _validateLayoutAndGlyphs];
678 return [_textStorage string];
683 _textContainer = aContainer;
684 _layoutManager = [_textContainer layoutManager];
685 _textStorage = [_layoutManager textStorage];
686 [_textStorage setFont:[
self font]];
687 [_textStorage setForegroundColor:_textColor];
692 - (void)setTextContainerInset:(CGSize)aSize
694 _textContainerInset = aSize;
698 - (void)invalidateTextContainerOrigin
700 _textContainerOrigin.x = _bounds.origin.x;
701 _textContainerOrigin.x += _textContainerInset.width;
703 _textContainerOrigin.y = _bounds.origin.y;
704 _textContainerOrigin.y += _textContainerInset.height;
707 - (void)doCommandBySelector:(
SEL)aSelector
709 if (![
self _sendDelegateDoCommandBySelector:aSelector])
710 [
super doCommandBySelector:aSelector];
713 - (void)didChangeText
718 - (BOOL)_didBeginEditing
720 if (_firstResponderButNotEditingYet)
722 if ([
self _sendDelegateTextShouldBeginEditing])
725 _firstResponderButNotEditingYet = NO;
733 - (BOOL)shouldChangeTextInRange:(CPRange)aRange replacementString:(
CPString)aString
735 if (![
self isEditable])
738 return [
self _sendDelegateShouldChangeTextInRange:aRange replacementString:aString];
743 #pragma mark Insert characters methods
745 - (void)_fixupReplaceForRange:(CPRange)aRange
747 [
self setSelectedRange:aRange];
748 [_layoutManager _validateLayoutAndGlyphs];
750 [
self scrollRangeToVisible:_selectionRange];
751 [
self setNeedsDisplay:YES];
754 - (void)_replaceCharactersInRange:(CPRange)aRange withAttributedString:(
CPString)aString selectionRange:(CPRange)selectionRange
756 [[[[
self window] undoManager] prepareWithInvocationTarget:self]
757 _replaceCharactersInRange:CPMakeRange(aRange.location, [aString
length])
758 withAttributedString:[_textStorage attributedSubstringFromRange:CPMakeRangeCopy(aRange)]
759 selectionRange:CPMakeRangeCopy(_selectionRange)];
761 [_textStorage replaceCharactersInRange:aRange withAttributedString:aString];
762 [
self _fixupReplaceForRange:selectionRange];
767 var isAttributed = [aString
isKindOfClass:CPAttributedString],
768 string = isAttributed ? [aString
string]:aString;
770 if (![
self _didBeginEditing] || ![
self shouldChangeTextInRange:
CPMakeRangeCopy(_selectionRange) replacementString:
string])
775 else if (![
self isRichText])
779 [undoManager setActionName:@"Replace/insert text"];
781 [[undoManager prepareWithInvocationTarget:self]
782 _replaceCharactersInRange:CPMakeRange(_selectionRange.location, [aString
length])
783 withAttributedString:[_textStorage attributedSubstringFromRange:CPMakeRangeCopy(_selectionRange)]
784 selectionRange:CPMakeRangeCopy(_selectionRange)];
786 [
self willChangeValueForKey:@"objectValue"];
787 [_textStorage replaceCharactersInRange:CPMakeRangeCopy(_selectionRange) withAttributedString:aString];
788 [
self didChangeValueForKey:@"objectValue"];
789 [
self _continuouslyReverseSetBinding];
791 [
self _setSelectedRange:CPMakeRange(_selectionRange.location + [
string length], 0) affinity:0 stillSelecting:NO overwriteTypingAttributes:NO];
792 _startTrackingLocation = _selectionRange.location;
795 [_layoutManager _validateLayoutAndGlyphs];
798 _stickyXLocation = MAX(0, _caret._rect.origin.x - 1);
802 #pragma mark Drawing methods
804 - (void)drawInsertionPointInRect:(CGRect)aRect color:(
CPColor)aColor turnedOn:(BOOL)flag
806 [_caret setRect:aRect];
807 [_caret setVisibility:flag stop:NO];
817 - (void)drawRect:(CGRect)aRect
820 var range = [_layoutManager glyphRangeForBoundingRect:aRect inTextContainer:_textContainer];
822 for (var i = 0; i < [_selectionSpans count]; i++)
823 [_selectionSpans[i] removeFromTextView];
825 _selectionSpans = [];
827 if (_selectionRange.length)
829 var rects = [_layoutManager rectArrayForCharacterRange:_selectionRange
830 withinSelectedCharacterRange:_selectionRange
831 inTextContainer:_textContainer
833 effectiveSelectionColor = [
self _isFocused] ? [_selectedTextAttributes objectForKey:CPBackgroundColorAttributeName] : [
CPColor _selectedTextBackgroundColorUnfocussed],
834 lengthRect = rects.length;
836 for (var i = 0; i < lengthRect; i++)
838 rects[i].origin.x += _textContainerOrigin.x;
839 rects[i].origin.y += _textContainerOrigin.y;
841 var newSpan = [[_CPSelectionBox alloc] initWithTextView:self rect:rects[i] color:effectiveSelectionColor];
842 [_selectionSpans addObject:newSpan];
847 [_layoutManager drawGlyphsForGlyphRange:range atPoint:_textContainerOrigin];
849 if ([
self shouldDrawInsertionPoint])
855 [_caret setVisibility:NO];
861 #pragma mark Select methods
863 - (void)selectAll:(
id)sender
865 if ([
self isSelectable])
867 [_caret stopBlinking];
868 [
self setSelectedRange:CPMakeRange(0, [_layoutManager numberOfCharacters])];
876 - (void)setSelectedRange:(CPRange)range
878 [_CPNativeInputManager cancelCurrentInputSessionIfNeeded];
888 - (void)setSelectedRange:(CPRange)range affinity:(CPSelectionAffinity)affinity stillSelecting:(BOOL)selecting
890 [
self _setSelectedRange:range affinity:affinity stillSelecting:selecting overwriteTypingAttributes:YES];
901 - (void)_setSelectedRange:(CPRange)range affinity:(CPSelectionAffinity)affinity stillSelecting:(BOOL)selecting overwriteTypingAttributes:(BOOL)doOverwrite
903 var maxRange =
CPMakeRange(0, [_layoutManager numberOfCharacters]),
908 if (!selecting && [
self _delegateRespondsToWillChangeSelectionFromCharacterRangeToCharacterRange])
910 newSelectionRange = [
self _sendDelegateWillChangeSelectionFromCharacterRange:_selectionRange toCharacterRange:range];
914 newSelectionRange = [
self selectionRangeForProposedRange:range granularity:[
self selectionGranularity]];
917 var isNewSelection = !
CPEqualRanges(newSelectionRange, _selectionRange);
920 _selectionRange = newSelectionRange;
922 if (newSelectionRange.length)
925 [_layoutManager invalidateDisplayForGlyphRange:newSelectionRange];
928 [
self setNeedsDisplay:YES];
932 if ([
self _isFirstResponder])
933 [
self updateInsertionPointStateAndRestartTimer:((newSelectionRange.length === 0) && ![_caret isBlinking])];
936 if (!isNewSelection && _mouseDownOldSelection)
937 isNewSelection = !
CPEqualRanges(newSelectionRange, _mouseDownOldSelection);
939 if (doOverwrite && _placeholderString === nil && isNewSelection)
940 [
self setTypingAttributes:[_textStorage attributesAtIndex:CPMaxRange(range) effectiveRange:nil]];
945 if (!selecting && newSelectionRange.length > 0)
946 [_CPNativeInputManager focusForClipboardOfTextView:self];
950 - (CGPoint)_cumulativeOffset
954 element =
self._DOMElement;
958 top += element.offsetTop || 0;
959 left += element.offsetLeft || 0;
960 element = element.offsetParent;
964 return CGPointMake(left, top);
970 - (void)_activateNativeInputElement:(DOMElemet)aNativeField
972 var attributes = [[
self typingAttributes] copy];
978 var placeholderString = [[
CPAttributedString alloc] initWithString:aNativeField.innerHTML attributes:attributes];
979 [
self insertText:placeholderString];
981 var caretOrigin = [_layoutManager boundingRectForGlyphRange:CPMakeRange(MAX(0, _selectionRange.location - 1), 1) inTextContainer:_textContainer].origin;
982 caretOrigin.y += [_layoutManager _characterOffsetAtLocation:MAX(0, _selectionRange.location - 1)];
984 var cumulativeOffset = [
self _cumulativeOffset];
988 aNativeField.style.left = (caretOrigin.x + cumulativeOffset.x) + "px";
989 aNativeField.style.top = (caretOrigin.y + cumulativeOffset.y) + "px";
990 aNativeField.style.font = [[_typingAttributes objectForKey:CPFontAttributeName] cssString];
991 aNativeField.style.color = [[_typingAttributes objectForKey:CPForegroundColorAttributeName] cssString];
994 [_caret setVisibility:NO];
997 - (CPArray)selectedRanges
999 return [_selectionRange];
1003 #pragma mark Keyboard events
1008 [[_window platformWindow] _propagateCurrentDOMEvent:YES];
1010 if (![_CPNativeInputManager isNativeInputFieldActive] && [event charactersIgnoringModifiers].charCodeAt(0) != 229)
1011 [
self interpretKeyEvents:[event]];
1013 [_caret setPermanentlyVisible:YES];
1018 [
super keyUp:event];
1020 setTimeout(
function() {
1021 [_caret setPermanentlyVisible:NO];
1025 - (CGPoint)_characterIndexFromRawPoint:(CGPoint)point
1028 point = [
self convertPoint:point fromView:nil];
1031 point.x -= _textContainerOrigin.x;
1032 point.y -= _textContainerOrigin.y;
1034 var index = [_layoutManager glyphIndexForPoint:point inTextContainer:_textContainer fractionOfDistanceThroughGlyph:fraction];
1037 index = [_layoutManager numberOfCharacters];
1038 else if (fraction[0] > 0.5)
1043 - (CGPoint)_characterIndexFromEvent:(
CPEvent)event
1048 - (BOOL)needsPanelToBecomeKey
1054 #pragma mark Mouse Events
1058 if (![
self isSelectable])
1061 [_CPNativeInputManager cancelCurrentInputSessionIfNeeded];
1062 [_caret setVisibility:NO];
1064 _startTrackingLocation = [
self _characterIndexFromEvent:event];
1066 var granularities = [CPNotFound, CPSelectByCharacter, CPSelectByWord, CPSelectByParagraph];
1070 if ([
self selectionGranularity] == CPSelectByCharacter &&
CPLocationInRange(_startTrackingLocation, _selectionRange))
1072 var lineBeginningIndex = [_layoutManager _firstLineFragmentForLineFromLocation:_selectionRange.location]._range.location,
1073 placeholderRange = _MakeRangeFromAbs(lineBeginningIndex,
CPMaxRange(_selectionRange)),
1074 placeholderString = [_textStorage attributedSubstringFromRange:placeholderRange],
1075 placeholderFrame = CGRectIntersection([_layoutManager boundingRectForGlyphRange:placeholderRange inTextContainer:_textContainer], _frame),
1076 rangeToHide =
CPMakeRange(0, _selectionRange.location - lineBeginningIndex),
1080 [placeholderString addAttribute:CPForegroundColorAttributeName
1084 _movingSelection =
CPMakeRange(_startTrackingLocation, 0);
1086 dragPlaceholder = [[
CPTextView alloc] initWithFrame:placeholderFrame];
1087 [dragPlaceholder._textStorage replaceCharactersInRange:CPMakeRange(0, 0) withAttributedString:placeholderString];
1090 [dragPlaceholder setAlphaValue:0.5];
1092 var stringForPasting = [_textStorage attributedSubstringFromRange:CPMakeRangeCopy(_selectionRange)],
1093 richData = [_CPRTFProducer produceRTF:stringForPasting documentAttributes:@{}],
1095 [draggingPasteboard declareTypes:[CPRTFPboardType, CPStringPboardType] owner:nil];
1096 [draggingPasteboard setString:richData forType:CPRTFPboardType];
1097 [draggingPasteboard setString:stringForPasting._string forType:CPStringPboardType];
1100 at:placeholderFrame.origin
1110 var setRange =
CPMakeRange(_startTrackingLocation, 0);
1113 setRange = _MakeRangeFromAbs(_startTrackingLocation < _MidRange(_selectionRange) ?
CPMaxRange(_selectionRange) : _selectionRange.location, _startTrackingLocation);
1118 _mouseDownOldSelection = _selectionRange;
1122 - (void)_supportScrolling:(
CPTimer)aTimer
1124 [
self mouseDragged:[CPApp currentEvent]];
1129 if (![
self isSelectable])
1132 if (_movingSelection)
1136 index = [
self _characterIndexFromEvent:event];
1138 if (index > oldRange.location)
1139 _scrollingDownward = YES;
1142 _scrollingDownward = NO;
1155 _movingSelection = nil;
1161 _mouseDownOldSelection = nil;
1163 var point = [_layoutManager locationForGlyphAtIndex:[
self selectedRange].location];
1164 _stickyXLocation = point.x;
1165 _startTrackingLocation = _selectionRange.location;
1167 if (_scrollingTimer)
1169 [_scrollingTimer invalidate];
1170 _scrollingTimer = nil;
1174 - (void)moveDown:(
id)sender
1176 if (![
self isSelectable])
1180 nglyphs = [_layoutManager numberOfCharacters],
1182 rectSource = [_layoutManager boundingRectForGlyphRange:CPMakeRange(sindex, 1) inTextContainer:_textContainer],
1183 rectEnd = nglyphs ? [_layoutManager boundingRectForGlyphRange:CPMakeRange(nglyphs - 1, 1) inTextContainer:_textContainer] : rectSource,
1184 point = rectSource.origin;
1186 if (_stickyXLocation)
1187 point.x = _stickyXLocation;
1190 point.y += 2 + rectSource.size.height;
1192 var dindex = point.y >= CGRectGetMaxY(rectEnd) ? nglyphs : [_layoutManager glyphIndexForPoint:point inTextContainer:_textContainer fractionOfDistanceThroughGlyph:fraction],
1193 oldStickyLoc = _stickyXLocation;
1195 if (fraction[0] > 0.5)
1198 [
self _establishSelection:CPMakeRange(dindex, 0) byExtending:NO];
1199 _stickyXLocation = oldStickyLoc;
1204 - (void)moveDownAndModifySelection:(
id)sender
1206 if (![
self isSelectable])
1209 var oldStartTrackingLocation = _startTrackingLocation;
1211 [
self _performSelectionFixupForRange:CPMakeRange(_selectionRange.location < _startTrackingLocation ? _selectionRange.location : CPMaxRange(_selectionRange), 0)];
1213 _startTrackingLocation = oldStartTrackingLocation;
1214 [
self _performSelectionFixupForRange:_MakeRangeFromAbs(_startTrackingLocation, (_selectionRange.location < _startTrackingLocation ? _selectionRange.location : CPMaxRange(_selectionRange)))];
1217 - (void)moveUp:(
id)sender
1219 if (![
self isSelectable])
1227 var rectSource = [_layoutManager boundingRectForGlyphRange:CPMakeRange(dindex, 1) inTextContainer:_textContainer];
1229 if (!(dindex === [_layoutManager numberOfCharacters] && _isNewlineCharacter([[_textStorage
string] characterAtIndex:dindex - 1])))
1230 dindex = [_layoutManager glyphIndexForPoint:CGPointMake(0, rectSource.origin.y + 1) inTextContainer:_textContainer fractionOfDistanceThroughGlyph:nil];
1236 rectSource = [_layoutManager boundingRectForGlyphRange:CPMakeRange(dindex - 1, 1) inTextContainer:_textContainer];
1237 dindex = [_layoutManager glyphIndexForPoint:CGPointMake(_stickyXLocation, rectSource.origin.y + 1) inTextContainer:_textContainer fractionOfDistanceThroughGlyph:fraction];
1239 if (fraction[0] > 0.5)
1242 var oldStickyLoc = _stickyXLocation;
1243 [
self _establishSelection:CPMakeRange(dindex,0) byExtending:NO];
1244 _stickyXLocation = oldStickyLoc;
1249 - (void)moveUpAndModifySelection:(
id)sender
1251 if (![
self isSelectable])
1254 var oldStartTrackingLocation = _startTrackingLocation;
1256 [
self _performSelectionFixupForRange:CPMakeRange(_selectionRange.location < _startTrackingLocation ? _selectionRange.location : CPMaxRange(_selectionRange), 0)];
1258 _startTrackingLocation = oldStartTrackingLocation;
1259 [
self _performSelectionFixupForRange:_MakeRangeFromAbs(_startTrackingLocation, (_selectionRange.location < _startTrackingLocation ? _selectionRange.location : CPMaxRange(_selectionRange)))];
1262 - (void)_performSelectionFixupForRange:(CPRange)aSel
1264 aSel.location = MAX(0, aSel.location);
1266 if (
CPMaxRange(aSel) > [_layoutManager numberOfCharacters])
1267 aSel =
CPMakeRange([_layoutManager numberOfCharacters], 0);
1269 [
self setSelectedRange:aSel];
1271 var point = [_layoutManager locationForGlyphAtIndex:aSel.location];
1273 _stickyXLocation = point.x;
1276 - (void)_establishSelection:(CPSelection)aSel byExtending:(BOOL)flag
1281 [
self _performSelectionFixupForRange:aSel];
1282 _startTrackingLocation = _selectionRange.location;
1285 - (unsigned)_calculateMoveSelectionFromRange:(CPRange)aRange intoDirection:(integer)move granularity:(CPSelectionGranularity)granularity
1287 var inWord = [
self _isCharacterAtIndex:(move > 0 ? CPMaxRange(aRange) : aRange.location) + move granularity:granularity],
1288 aSel = [
self selectionRangeForProposedRange:CPMakeRange((move > 0 ? CPMaxRange(aRange) : aRange.location) + move, 0) granularity:granularity],
1289 bSel = [
self selectionRangeForProposedRange:CPMakeRange((move > 0 ? CPMaxRange(aSel) : aSel.location) + move, 0) granularity:granularity];
1291 return move > 0 ?
CPMaxRange(inWord? aSel:bSel) : (inWord? aSel:bSel).location;
1294 - (void)_moveSelectionIntoDirection:(integer)move granularity:(CPSelectionGranularity)granularity
1296 var pos = [
self _calculateMoveSelectionFromRange:_selectionRange intoDirection:move granularity:granularity];
1298 [
self _performSelectionFixupForRange:CPMakeRange(pos, 0)];
1299 _startTrackingLocation = _selectionRange.location;
1302 - (void)_extendSelectionIntoDirection:(integer)move granularity:(CPSelectionGranularity)granularity
1306 if (granularity !== CPSelectByCharacter)
1308 var pos = [
self _calculateMoveSelectionFromRange:CPMakeRange(aSel.location < _startTrackingLocation ? aSel.location : CPMaxRange(aSel), 0)
1310 granularity:granularity];
1315 aSel =
CPMakeRange((aSel.location < _startTrackingLocation? aSel.location :
CPMaxRange(aSel)) + move, 0);
1317 aSel = _MakeRangeFromAbs(_startTrackingLocation, aSel.location);
1318 [
self _performSelectionFixupForRange:aSel];
1321 - (void)moveLeftAndModifySelection:(
id)sender
1323 if ([
self isSelectable])
1324 [
self _extendSelectionIntoDirection:-1 granularity:CPSelectByCharacter];
1327 - (void)moveBackward:(
id)sender
1332 - (void)moveBackwardAndModifySelection:(
id)sender
1337 - (void)moveRightAndModifySelection:(
id)sender
1339 if ([
self isSelectable])
1340 [
self _extendSelectionIntoDirection:1 granularity:CPSelectByCharacter];
1343 - (void)moveLeft:(
id)sender
1345 if ([
self isSelectable])
1346 [
self _establishSelection:CPMakeRange(_selectionRange.location - (_selectionRange.length ? 0 : 1), 0) byExtending:NO];
1349 - (void)moveToEndOfParagraph:(
id)sender
1351 if (![
self isSelectable])
1354 if (!_isNewlineCharacter([[_textStorage
string] characterAtIndex:_selectionRange.location]))
1355 [
self _moveSelectionIntoDirection:1 granularity:CPSelectByParagraph];
1357 if (_isNewlineCharacter([[_textStorage
string] characterAtIndex:MAX(0, _selectionRange.location - 1)]))
1361 - (void)moveToEndOfParagraphAndModifySelection:(
id)sender
1363 if ([
self isSelectable])
1364 [
self _extendSelectionIntoDirection:1 granularity:CPSelectByParagraph];
1367 - (void)moveParagraphForwardAndModifySelection:(
id)sender
1369 if ([
self isSelectable])
1370 [
self _extendSelectionIntoDirection:1 granularity:CPSelectByParagraph];
1373 - (void)moveParagraphForward:(
id)sender
1375 if ([
self isSelectable])
1376 [
self _moveSelectionIntoDirection:1 granularity:CPSelectByParagraph];
1379 - (void)moveWordBackwardAndModifySelection:(
id)sender
1384 - (void)moveWordBackward:(
id)sender
1389 - (void)moveWordForwardAndModifySelection:(
id)sender
1394 - (void)moveWordForward:(
id)sender
1399 - (void)moveToBeginningOfDocument:(
id)sender
1401 if ([
self isSelectable])
1402 [
self _establishSelection:CPMakeRange(0, 0) byExtending:NO];
1405 - (void)moveToBeginningOfDocumentAndModifySelection:(
id)sender
1407 if ([
self isSelectable])
1408 [
self _establishSelection:CPMakeRange(0, 0) byExtending:YES];
1411 - (void)moveToEndOfDocument:(
id)sender
1413 if ([
self isSelectable])
1414 [
self _establishSelection:CPMakeRange([_layoutManager numberOfCharacters], 0) byExtending:NO];
1417 - (void)moveToEndOfDocumentAndModifySelection:(
id)sender
1419 if ([
self isSelectable])
1420 [
self _establishSelection:CPMakeRange([_layoutManager numberOfCharacters], 0) byExtending:YES];
1423 - (void)moveWordRight:(
id)sender
1425 if ([
self isSelectable])
1426 [
self _moveSelectionIntoDirection:1 granularity:CPSelectByWord];
1429 - (void)moveToBeginningOfParagraph:(
id)sender
1431 if (![
self isSelectable])
1434 if (!_isNewlineCharacter([[_textStorage
string] characterAtIndex:MAX(0, _selectionRange.location - 1)]))
1435 [
self _moveSelectionIntoDirection:-1 granularity:CPSelectByParagraph];
1438 - (void)moveToBeginningOfParagraphAndModifySelection:(
id)sender
1440 if ([
self isSelectable])
1441 [
self _extendSelectionIntoDirection:-1 granularity:CPSelectByParagraph];
1444 - (void)moveParagraphBackward:(
id)sender
1446 if ([
self isSelectable])
1447 [
self _moveSelectionIntoDirection:-1 granularity:CPSelectByParagraph];
1450 - (void)moveParagraphBackwardAndModifySelection:(
id)sender
1452 if ([
self isSelectable])
1453 [
self _extendSelectionIntoDirection:-1 granularity:CPSelectByParagraph];
1456 - (void)moveWordRightAndModifySelection:(
id)sender
1458 if ([
self isSelectable])
1459 [
self _extendSelectionIntoDirection:+1 granularity:CPSelectByWord];
1462 - (void)deleteToEndOfParagraph:(
id)sender
1464 if (![
self isSelectable] || ![
self isEditable])
1471 - (void)deleteToBeginningOfParagraph:(
id)sender
1473 if (![
self isSelectable] || ![
self isEditable])
1480 - (void)deleteToBeginningOfLine:(
id)sender
1482 if (![
self isSelectable] || ![
self isEditable])
1489 - (void)deleteToEndOfLine:(
id)sender
1491 if (![
self isSelectable] || ![
self isEditable])
1498 - (void)deleteWordBackward:(
id)sender
1500 if (![
self isSelectable] || ![
self isEditable])
1507 - (void)deleteWordForward:(
id)sender
1509 if (![
self isSelectable] || ![
self isEditable])
1516 - (void)moveToLeftEndOfLine:(
id)sender byExtending:(BOOL)flag
1518 if (![
self isSelectable])
1521 var nglyphs = [_layoutManager numberOfCharacters],
1522 loc = nglyphs == _selectionRange.location ? MAX(0, _selectionRange.location - 1) : _selectionRange.location,
1523 fragment = [_layoutManager _firstLineFragmentForLineFromLocation:loc];
1526 [
self _establishSelection:CPMakeRange(fragment._range.location, 0) byExtending:flag];
1529 - (void)moveToLeftEndOfLine:(
id)sender
1534 - (void)moveToLeftEndOfLineAndModifySelection:(
id)sender
1539 - (void)moveToRightEndOfLine:(
id)sender byExtending:(BOOL)flag
1541 if (![
self isSelectable])
1544 var fragment = [_layoutManager _lastLineFragmentForLineFromLocation:_selectionRange.location];
1549 var nglyphs = [_layoutManager numberOfCharacters],
1550 loc = nglyphs ==
CPMaxRange(fragment._range) ? nglyphs : MAX(0,
CPMaxRange(fragment._range) - 1);
1552 [
self _establishSelection:CPMakeRange(loc, 0) byExtending:flag];
1555 - (void)moveToRightEndOfLine:(
id)sender
1560 - (void)moveToRightEndOfLineAndModifySelection:(
id)sender
1565 - (void)moveWordLeftAndModifySelection:(
id)sender
1567 if ([
self isSelectable])
1568 [
self _extendSelectionIntoDirection:-1 granularity:CPSelectByWord];
1571 - (void)moveWordLeft:(
id)sender
1573 if ([
self isSelectable])
1574 [
self _moveSelectionIntoDirection:-1 granularity:CPSelectByWord];
1577 - (void)moveRight:(
id)sender
1579 if ([
self isSelectable])
1580 [
self _establishSelection:CPMakeRange(CPMaxRange(_selectionRange) + (_selectionRange.length ? 0 : 1), 0) byExtending:NO];
1583 - (void)_deleteForRange:(CPRange)changedRange
1585 if (![
self _didBeginEditing] || ![
self shouldChangeTextInRange:changedRange replacementString:
@""])
1590 [[[_window undoManager] prepareWithInvocationTarget:self] _replaceCharactersInRange:CPMakeRange(changedRange.location, 0)
1591 withAttributedString:[_textStorage attributedSubstringFromRange:CPMakeRangeCopy(changedRange)]
1592 selectionRange:CPMakeRangeCopy(_selectionRange)];
1593 [
self willChangeValueForKey:@"objectValue"];
1594 [_textStorage deleteCharactersInRange:CPMakeRangeCopy(changedRange)];
1595 [
self didChangeValueForKey:@"objectValue"];
1596 [
self _continuouslyReverseSetBinding];
1598 [
self setSelectedRange:CPMakeRange(changedRange.location, 0)];
1599 [
self didChangeText];
1600 [_layoutManager _validateLayoutAndGlyphs];
1602 [
self scrollRangeToVisible:CPMakeRange(changedRange.location, 0)];
1603 _stickyXLocation = _caret._rect.origin.x;
1606 - (void)cancelOperation:(
id)sender
1608 [_CPNativeInputManager cancelCurrentInputSessionIfNeeded];
1611 - (void)deleteBackward:(
id)sender handleSmart:(BOOL)handleSmart
1615 if (
CPEmptyRange(_selectionRange) && _selectionRange.location > 0)
1616 changedRange =
CPMakeRange(_selectionRange.location - 1, 1);
1618 changedRange = _selectionRange;
1622 changedRange.location > 0 && _isWhitespaceCharacter([[_textStorage
string] characterAtIndex:_selectionRange.location - 1]) &&
1623 changedRange.location < [[
self string] length] && _isWhitespaceCharacter([[_textStorage
string] characterAtIndex:
CPMaxRange(changedRange)]))
1624 changedRange.length++;
1626 [
self _deleteForRange:changedRange];
1627 _startTrackingLocation = _selectionRange.location;
1630 - (void)deleteBackward:(
id)sender
1635 - (void)deleteForward:(
id)sender
1639 if (
CPEmptyRange(_selectionRange) && _selectionRange.location < [_layoutManager numberOfCharacters])
1640 changedRange =
CPMakeRange(_selectionRange.location, 1);
1642 changedRange = _selectionRange;
1644 [
self _deleteForRange:changedRange];
1647 - (void)cut:(
id)sender
1651 if (selectedRange.length < 1)
1658 - (void)insertLineBreak:(
id)sender
1662 [[
self window] _temporarilyDisableKeyEquivalentForDefaultButton];
1665 - (void)insertTab:(
id)sender
1670 - (void)insertTabIgnoringFieldEditor:(
id)sender
1675 - (void)insertNewlineIgnoringFieldEditor:(
id)sender
1680 - (void)insertNewline:(
id)sender
1685 - (void)_enrichEssentialTypingAttributes:(
CPDictionary)attributes
1691 [attributes
setObject:[
self textColor]
forKey:CPForegroundColorAttributeName];
1699 if ([
self _delegateRespondsToShouldChangeTypingAttributesToAttributes])
1701 _typingAttributes = [
self _sendDelegateShouldChangeTypingAttributes:_typingAttributes toAttributes:attributes];
1705 _typingAttributes = [attributes
copy];
1707 [
self _enrichEssentialTypingAttributes:_typingAttributes];
1714 _mouseDownOldSelection = nil;
1719 var attributes = [[_textStorage attributesAtIndex:CPMaxRange(_selectionRange) effectiveRange:nil] copy];
1721 [
self _enrichEssentialTypingAttributes:attributes];
1726 - (void)delete:(
id)sender
1733 #pragma mark Font methods
1744 var length = [_layoutManager numberOfCharacters];
1748 [_textStorage addAttribute:CPFontAttributeName value:_font range:CPMakeRange(0, length)];
1749 [_textStorage setFont:_font];
1754 - (void)setFont:(
CPFont)font range:(CPRange)range
1756 if (![
self isRichText])
1759 [_textStorage setFont:_font];
1762 var currentAttributes = [_textStorage attributesAtIndex:range.location effectiveRange:nil] || _typingAttributes;
1765 setFont:[currentAttributes objectForKey:CPFontAttributeName] || [
self font]
1766 range:CPMakeRangeCopy(range)];
1768 [_textStorage addAttribute:CPFontAttributeName value:font range:CPMakeRangeCopy(range)];
1769 [_layoutManager _validateLayoutAndGlyphs];
1772 - (void)changeFont:(
id)sender
1774 var currRange =
CPMakeRange(_selectionRange.location, 0),
1780 [undoManager beginUndoGrouping];
1782 if ([
self isRichText])
1788 attributes = [_textStorage attributesAtIndex:CPMaxRange(currRange)
1789 longestEffectiveRange:currRange
1790 inRange:_selectionRange];
1793 [
self setFont:[sender convertFont:oldFont]
range:currRange];
1798 [_typingAttributes setObject:[sender selectedFont] forKey:CPFontAttributeName];
1803 var length = [_textStorage length];
1805 oldFont = [
self font];
1806 [
self setFont:[sender convertFont:oldFont]
range:CPMakeRange(0, length)];
1810 [undoManager endUndoGrouping];
1812 [_layoutManager _validateLayoutAndGlyphs];
1820 #pragma mark Color methods
1822 - (void)changeColor:(
id)sender
1829 _textColor = [aColor copy];
1830 [
self setTextColor:aColor
range:CPMakeRange(0, [_layoutManager numberOfCharacters])];
1831 [_typingAttributes setObject:_textColor forKey:CPForegroundColorAttributeName];
1834 - (void)setTextColor:(
CPColor)aColor range:(CPRange)range
1836 var currentAttributes = [_textStorage attributesAtIndex:range.location effectiveRange:nil] || _typingAttributes;
1839 setTextColor:[currentAttributes objectForKey:CPForegroundColorAttributeName] || _textColor
1840 range:CPMakeRangeCopy(range)];
1845 [_textStorage addAttribute:CPForegroundColorAttributeName value:aColor range:CPMakeRangeCopy(range)];
1847 [_textStorage removeAttribute:CPForegroundColorAttributeName range:CPMakeRangeCopy(range)];
1850 [_typingAttributes setObject:aColor forKey:CPForegroundColorAttributeName];
1852 [_layoutManager textStorage:_textStorage edited:0 range:CPMakeRangeCopy(range) changeInLength:0 invalidatedRange:CPMakeRangeCopy(range)];
1855 - (void)underline:(
id)sender
1857 if (![
self _didBeginEditing] || ![
self shouldChangeTextInRange:_selectionRange replacementString:nil])
1862 var attrib = [_textStorage attributesAtIndex:_selectionRange.location effectiveRange:nil];
1865 [_textStorage removeAttribute:CPUnderlineStyleAttributeName range:_selectionRange];
1867 [_textStorage addAttribute:CPUnderlineStyleAttributeName value:[
CPNumber numberWithInt:1]
range:CPMakeRangeCopy(_selectionRange)];
1877 [_layoutManager textStorage:_textStorage edited:0 range:CPMakeRangeCopy(_selectionRange) changeInLength:0 invalidatedRange:CPMakeRangeCopy(_selectionRange)];
1880 - (CPSelectionAffinity)selectionAffinity
1885 - (BOOL)isRulerVisible
1890 - (void)replaceCharactersInRange:(CPRange)aRange withString:(
CPString)aString
1892 [_textStorage replaceCharactersInRange:aRange withString:aString];
1895 - (void)setConstrainedFrameSize:(CGSize)desiredSize
1905 - (void)setFrameSize:(CGSize)aSize
1909 desiredSize = CGSizeCreateCopy(aSize),
1910 rect = CGRectUnion([_layoutManager boundingRectForGlyphRange:
CPMakeRange(0, 1) inTextContainer:_textContainer],
1911 [_layoutManager boundingRectForGlyphRange:
CPMakeRange(MAX(0, [_layoutManager numberOfCharacters] - 2), 1) inTextContainer:_textContainer]),
1912 myClipviewSize = nil;
1914 if ([[
self superview] isKindOfClass:[
CPClipView class]])
1917 if ([_layoutManager extraLineFragmentTextContainer] === _textContainer)
1918 rect = CGRectUnion(rect, [_layoutManager extraLineFragmentRect]);
1920 if (_isHorizontallyResizable)
1922 rect = [_layoutManager boundingRectForGlyphRange:CPMakeRange(0, MAX(0, [_layoutManager numberOfCharacters] - 1)) inTextContainer:_textContainer];
1924 desiredSize.width = rect.size.width + 2 * _textContainerInset.width;
1926 if (desiredSize.width < minSize.width)
1927 desiredSize.width = minSize.width;
1928 else if (desiredSize.width > maxSize.width)
1929 desiredSize.width = maxSize.width;
1932 if (_isVerticallyResizable)
1934 desiredSize.height = rect.size.height + 2 * _textContainerInset.height;
1936 if (desiredSize.height < minSize.height)
1937 desiredSize.height = minSize.height;
1938 else if (desiredSize.height > maxSize.height)
1939 desiredSize.height = maxSize.height;
1944 if (desiredSize.width < myClipviewSize.width)
1945 desiredSize.width = myClipviewSize.width;
1946 if (desiredSize.height < myClipviewSize.height)
1947 desiredSize.height = myClipviewSize.height;
1953 - (void)scrollRangeToVisible:(CPRange)aRange
1959 if (aRange.location >= [_layoutManager numberOfCharacters])
1960 rect = [_layoutManager extraLineFragmentRect];
1962 rect = [_layoutManager lineFragmentRectForGlyphAtIndex:aRange.location effectiveRange:nil];
1966 rect = [_layoutManager boundingRectForGlyphRange:aRange inTextContainer:_textContainer];
1969 rect.origin.x += _textContainerOrigin.x;
1970 rect.origin.y += _textContainerOrigin.y;
1975 - (BOOL)_isCharacterAtIndex:(
unsigned)index granularity:(CPSelectionGranularity)granularity
1979 switch (granularity)
1982 characterSet = [[
self class] _wordBoundaryRegex];
1986 characterSet = [[
self class] _paragraphBoundaryRegex];
1992 return _regexMatchesStringAtIndex(characterSet, [_textStorage
string], index);
1995 + (JSObject)_wordBoundaryRegex
1997 return new RegExp(
"(^[0-9][\\.,])|(^.[^-\\.,+#'\"!§$%&/\\(<\\[\\]>\\)=?`´*\\s{}\\|¶])",
"m");
2000 + (JSObject)_paragraphBoundaryRegex
2002 return new RegExp(
"^.[^\\n\\r]",
"m");
2005 + (JSObject)_whitespaceRegex
2008 return new RegExp(
"^.[ \\t]+",
"m");
2011 - (CPRange)_characterRangeForIndex:(
unsigned)index asDefinedByRegex:(JSObject)regex
2013 return [
self _characterRangeForIndex:index asDefinedByLRegex:regex andRRegex:regex]
2016 - (CPRange)_characterRangeForIndex:(
unsigned)index asDefinedByLRegex:(JSObject)lregex andRRegex:(JSObject)rregex
2019 numberOfCharacters = [_layoutManager numberOfCharacters],
2020 string = [_textStorage string];
2023 for (var searchIndex = index - 1; searchIndex >= 0 && _regexMatchesStringAtIndex(lregex,
string, searchIndex); searchIndex--)
2024 wordRange.location = searchIndex;
2027 searchIndex = index + 1;
2029 while (searchIndex < numberOfCharacters && _regexMatchesStringAtIndex(rregex,
string, searchIndex))
2032 return _MakeRangeFromAbs(wordRange.location, MIN(MAX(0, numberOfCharacters), searchIndex));
2035 - (CPRange)selectionRangeForProposedRange:(CPRange)proposedRange granularity:(CPSelectionGranularity)granularity
2037 var textStorageLength = [_layoutManager numberOfCharacters];
2039 if (textStorageLength == 0)
2042 if (proposedRange.location >= textStorageLength)
2043 proposedRange =
CPMakeRange(textStorageLength, 0);
2045 if (
CPMaxRange(proposedRange) > textStorageLength)
2046 proposedRange.length = textStorageLength - proposedRange.location;
2048 var
string = [_textStorage string],
2051 lloc = proposedRange.location,
2054 switch (granularity)
2057 lregex = _isWhitespaceCharacter([
string characterAtIndex:lloc])? [[
self class] _whitespaceRegex] : [[
self class] _wordBoundaryRegex];
2058 rregex = _isWhitespaceCharacter([
string characterAtIndex:
CPMaxRange(proposedRange)])? [[
self class] _whitespaceRegex] : [[
self class] _wordBoundaryRegex];
2061 lregex = rregex = [[
self class] _paragraphBoundaryRegex];
2064 if (lloc > 0 && _isNewlineCharacter([
string characterAtIndex:lloc]) &&
2065 !_isNewlineCharacter([
string characterAtIndex:lloc - 1]))
2068 if (rloc > 0 && _isNewlineCharacter([
string characterAtIndex:rloc]))
2073 return proposedRange;
2076 var granularRange = [
self _characterRangeForIndex:lloc
2077 asDefinedByLRegex:lregex
2080 if (proposedRange.length == 0 && _isNewlineCharacter([
string characterAtIndex:proposedRange.location]))
2081 return _MakeRangeFromAbs(_isNewlineCharacter([
string characterAtIndex:lloc])? proposedRange.location : granularRange.location, proposedRange.location + 1);
2083 if (proposedRange.length)
2084 granularRange =
CPUnionRange(granularRange, [
self _characterRangeForIndex:rloc
2085 asDefinedByLRegex:lregex
2090 granularRange.length++;
2092 return granularRange;
2095 - (BOOL)shouldDrawInsertionPoint
2097 return (_selectionRange.length === 0 && [
self _isFocused] && !_placeholderString);
2100 - (void)updateInsertionPointStateAndRestartTimer:(BOOL)flag
2103 numberOfGlyphs = [_layoutManager numberOfCharacters];
2105 if (_selectionRange.length)
2106 [_caret setVisibility:NO];
2108 if (_selectionRange.location >= numberOfGlyphs)
2110 caretRect = [_layoutManager boundingRectForGlyphRange:CPMakeRange(MAX(0,_selectionRange.location - 1), 1) inTextContainer:_textContainer];
2112 if (!numberOfGlyphs)
2114 var font = [_typingAttributes objectForKey:CPFontAttributeName] || [
self font];
2116 caretRect.
size.height = [font size];
2117 caretRect.origin.y = ([font ascender] - [font descender]) * 0.5 + _textContainerOrigin.y;
2120 caretRect.origin.x += caretRect.size.width;
2122 if (_selectionRange.location > 0 && [[_textStorage
string] characterAtIndex:_selectionRange.location - 1] ===
'\n')
2124 caretRect.origin.y += caretRect.size.height;
2125 caretRect.origin.x = 0;
2129 caretRect = [_layoutManager boundingRectForGlyphRange:CPMakeRange(_selectionRange.location, 1) inTextContainer:_textContainer];
2131 var loc = (_selectionRange.location === numberOfGlyphs && numberOfGlyphs > 0) ? _selectionRange.location - 1 : _selectionRange.location,
2132 caretOffset = [_layoutManager _characterOffsetAtLocation:loc],
2133 oldYPosition = CGRectGetMaxY(caretRect),
2134 caretDescend = [_layoutManager _descentAtLocation:loc];
2136 if (caretOffset > 0)
2138 caretRect.origin.y += caretOffset;
2139 caretRect.size.height = oldYPosition - caretRect.origin.y;
2141 if (caretDescend < 0)
2142 caretRect.size.height -= caretDescend;
2144 caretRect.origin.x += _textContainerOrigin.x;
2145 caretRect.origin.y += _textContainerOrigin.y;
2146 caretRect.size.width = 1;
2148 [_caret setRect:caretRect];
2151 [_caret startBlinking];
2157 location = [
self _characterIndexFromRawPoint:CGPointCreateCopy(point)];
2160 [_caret _drawCaretAtLocation:_movingSelection.location];
2161 [_caret setVisibility:YES];
2165 #pragma mark Dragging operation
2174 [_caret setVisibility:NO];
2179 _movingSelection = nil;
2183 if (_movingSelection.location >
CPMaxRange(_selectionRange))
2184 _movingSelection.location -= _selectionRange.length;
2186 [
self _deleteForRange:_selectionRange];
2189 var dataForPasting = [pasteboard stringForType:CPRTFPboardType] || [pasteboard stringForType:CPStringPboardType];
2192 setTimeout(
function(){
2194 if ([dataForPasting hasPrefix:
"{\\rtf"])
2195 [
self insertText:[[_CPRTFParser
new] parseRTF:dataForPasting]];
2205 - (BOOL)isSelectable
2211 return [
super isEditable] && !_placeholderString;
2214 - (void)_setPlaceholderString:(
CPString)aString
2216 if (_placeholderString === aString)
2219 _placeholderString = aString;
2224 - (void)_continuouslyReverseSetBinding
2226 var binderClass = [[
self class] _binderClassForBinding:CPAttributedStringBinding] ||
2227 [[
self class] _binderClassForBinding:CPValueBinding],
2228 theBinding = [binderClass getBinding:CPAttributedStringBinding forObject:self] || [binderClass getBinding:CPValueBinding forObject:self];
2230 if ([theBinding continuouslyUpdatesValue])
2231 [theBinding reverseSetValueFor:@"objectValue"];
2234 - (void)_reverseSetBinding
2236 var binderClass = [[
self class] _binderClassForBinding:CPAttributedStringBinding] ||
2237 [[
self class] _binderClassForBinding:CPValueBinding],
2238 theBinding = [binderClass getBinding:CPAttributedStringBinding forObject:self] || [binderClass getBinding:CPValueBinding forObject:self];
2240 if (theBinding && [
self isEditable])
2241 [theBinding reverseSetValueFor:@"objectValue"];
2249 - (BOOL)_delegateRespondsToShouldChangeTypingAttributesToAttributes
2254 - (BOOL)_delegateRespondsToWillChangeSelectionFromCharacterRangeToCharacterRange
2259 - (BOOL)_sendDelegateDoCommandBySelector:(
SEL)aSelector
2264 return [_delegate textView:self doCommandBySelector:aSelector];
2267 - (BOOL)_sendDelegateTextShouldBeginEditing
2272 return [_delegate textShouldBeginEditing:self];
2275 - (BOOL)_sendDelegateTextShouldEndEditing
2280 return [_delegate textShouldEndEditing:self];
2283 - (BOOL)_sendDelegateShouldChangeTextInRange:(CPRange)aRange replacementString:(
CPString)aString
2288 return [_delegate textView:self shouldChangeTextInRange:aRange replacementString:aString];
2296 return [_delegate textView:self shouldChangeTypingAttributes:typingAttributes toAttributes:attributes];
2299 - (CPRange)_sendDelegateWillChangeSelectionFromCharacterRange:(CPRange)selectionRange toCharacterRange:(CPRange)range
2304 return [_delegate textView:self willChangeSelectionFromCharacterRange:selectionRange toCharacterRange:range];
2331 var selectedTextAttributes = [aCoder decodeObjectForKey:CPTextViewSelectedTextAttributesKey],
2332 enumerator = [selectedTextAttributes keyEnumerator],
2335 while (key = [enumerator nextObject])
2336 [_selectedTextAttributes setObject:[selectedTextAttributes valueForKey:key] forKey:key];
2341 [
self setAllowsUndo:[aCoder decodeBoolForKey:CPTextViewAllowsUndoKey]];
2342 [
self setUsesFontPanel:[aCoder decodeBoolForKey:CPTextViewUsesFontPanelKey]];
2344 [
self setDelegate:[aCoder decodeObjectForKey:CPTextViewDelegateKey]];
2346 var container = [aCoder decodeObjectForKey:CPTextViewContainerKey];
2347 [container setTextView:self];
2349 _typingAttributes = [[_textStorage attributesAtIndex:0 effectiveRange:nil] copy];
2352 [_typingAttributes setObject:[
CPColor blackColor] forKey:CPForegroundColorAttributeName];
2354 _textColor = [_typingAttributes valueForKey:CPForegroundColorAttributeName];
2355 [
self setFont:[_typingAttributes valueForKey:CPFontAttributeName]];
2367 [aCoder encodeObject:_delegate forKey:CPTextViewDelegateKey];
2368 [aCoder encodeObject:_textContainer forKey:CPTextViewContainerKey];
2369 [aCoder encodeObject:_insertionPointColor forKey:CPTextViewInsertionPointColorKey];
2370 [aCoder encodeObject:_selectedTextAttributes forKey:CPTextViewSelectedTextAttributesKey];
2371 [aCoder encodeBool:_allowsUndo forKey:CPTextViewAllowsUndoKey];
2372 [aCoder encodeBool:_usesFontPanel forKey:CPTextViewUsesFontPanelKey];
2378 @implementation _CPSelectionBox :
CPObject
2380 DOMElement _selectionBoxDOM;
2386 - (id)initWithTextView:(
CPTextView)aTextView rect:(CGRect)aRect color:(
CPColor)aColor
2388 if (
self = [super
init])
2390 _textView = aTextView;
2395 _textView._DOMElement.appendChild(_selectionBoxDOM);
2401 - (void)removeFromTextView
2403 _textView._DOMElement.removeChild(_selectionBoxDOM);
2410 _selectionBoxDOM = document.createElement(
"span");
2411 _selectionBoxDOM.style.position =
"absolute";
2412 _selectionBoxDOM.style.visibility =
"visible";
2413 _selectionBoxDOM.style.padding =
"0px";
2414 _selectionBoxDOM.style.margin =
"0px";
2415 _selectionBoxDOM.style.whiteSpace =
"pre";
2418 _selectionBoxDOM.style.width = (_rect.size.width) +
"px";
2419 _selectionBoxDOM.style.left = (_rect.origin.x) +
"px";
2420 _selectionBoxDOM.style.top = (_rect.origin.y) +
"px";
2421 _selectionBoxDOM.style.height = (_rect.size.height) +
"px";
2422 _selectionBoxDOM.style.zIndex = -1000;
2423 _selectionBoxDOM.oncontextmenu = _selectionBoxDOM.onmousedown = _selectionBoxDOM.onselectstart =
function () {
return false; };
2431 @implementation _CPCaret :
CPObject
2434 BOOL _permanentlyVisible;
2438 DOMElement _caretDOM;
2441 - (void)setRect:(CGRect)aRect
2443 _rect = CGRectCreateCopy(aRect);
2446 _caretDOM.style.left = (aRect.origin.x) +
"px";
2447 _caretDOM.style.top = (aRect.origin.y) +
"px";
2448 _caretDOM.style.height = (aRect.size.height) +
"px";
2454 if (
self = [super
init])
2461 _caretDOM = document.createElement(
"span");
2462 style = _caretDOM.style;
2463 style.position =
"absolute";
2464 style.visibility =
"visible";
2465 style.padding =
"0px";
2466 style.margin =
"0px";
2467 style.whiteSpace =
"pre";
2468 style.backgroundColor =
"black";
2469 _caretDOM.style.width =
"1px";
2471 _textView._DOMElement.appendChild(_caretDOM);
2479 - (void)setVisibility:(BOOL)visibilityFlag stop:(BOOL)stopFlag
2483 _caretDOM.style.visibility = visibilityFlag ?
"visible" :
"hidden";
2486 if (!visibilityFlag && stopFlag)
2487 [
self stopBlinking];
2490 - (void)setVisibility:(BOOL)visibilityFlag
2492 [
self setVisibility:visibilityFlag stop:YES];
2495 - (void)_blinkCaret:(
CPTimer)aTimer
2497 _drawCaret = (!_drawCaret) || _permanentlyVisible;
2498 [_textView setNeedsDisplayInRect:_rect];
2501 - (void)startBlinking
2513 return [_caretTimer isValid];
2516 - (void)stopBlinking
2522 [_caretTimer invalidate];
2527 - (void)_drawCaretAtLocation:(
int)aLoc
2529 var rect = [_textView._layoutManager boundingRectForGlyphRange:CPMakeRange(aLoc, 1) inTextContainer:_textView._textContainer];
2531 if (aLoc >= [_textView._layoutManager numberOfCharacters])
2532 rect.origin.x = CGRectGetMaxX(rect);
2534 [
self setRect:rect];
2540 var _CPNativeInputField,
2541 _CPNativeInputFieldKeyDownCalled,
2542 _CPNativeInputFieldKeyUpCalled,
2543 _CPNativeInputFieldKeyPressedCalled,
2544 _CPNativeInputFieldActive;
2546 var _CPCopyPlaceholder =
'-';
2547 @implementation _CPNativeInputManager :
CPObject
2552 + (BOOL)isNativeInputFieldActive
2554 return _CPNativeInputFieldActive;
2557 + (void)cancelCurrentNativeInputSession
2561 _CPNativeInputField.innerHTML =
'';
2564 [
self _endInputSessionWithString:_CPNativeInputField.innerHTML];
2567 + (void)cancelCurrentInputSessionIfNeeded
2569 if (!_CPNativeInputFieldActive)
2572 [
self cancelCurrentNativeInputSession];
2575 + (void)_endInputSessionWithString:(
CPString)aStr
2577 _CPNativeInputFieldActive = NO;
2579 var currentFirstResponder = [[CPApp keyWindow] firstResponder],
2580 placeholderRange =
CPMakeRange([currentFirstResponder selectedRange].location - 1, 1);
2582 [currentFirstResponder setSelectedRange:placeholderRange];
2583 [currentFirstResponder insertText:aStr];
2584 _CPNativeInputField.innerHTML =
'';
2587 [
self hideInputElement];
2588 [currentFirstResponder updateInsertionPointStateAndRestartTimer:YES];
2594 _CPNativeInputField = document.createElement(
"div");
2595 _CPNativeInputField.contentEditable = YES;
2596 _CPNativeInputField.style.width =
"64px";
2597 _CPNativeInputField.style.zIndex = 10000;
2598 _CPNativeInputField.style.position =
"absolute";
2599 _CPNativeInputField.style.visibility =
"visible";
2600 _CPNativeInputField.style.padding =
"0px";
2601 _CPNativeInputField.style.margin =
"0px";
2602 _CPNativeInputField.style.whiteSpace =
"pre";
2603 _CPNativeInputField.style.outline =
"0px solid transparent";
2605 document.body.appendChild(_CPNativeInputField);
2607 _CPNativeInputField.addEventListener(
"keyup",
function(e)
2609 _CPNativeInputFieldKeyUpCalled = YES;
2613 if (e.which < 27 || e.which == 91 || e.which == 93)
2616 _CPNativeInputField.innerHTML =
'';
2618 if (_CPNativeInputField.innerHTML.length == 0 || _CPNativeInputField.innerHTML.length > 2)
2619 [
self cancelCurrentInputSessionIfNeeded];
2624 var currentFirstResponder = [[CPApp keyWindow] firstResponder];
2626 if (![currentFirstResponder respondsToSelector:
@selector(_activateNativeInputElement:)])
2629 var charCode = _CPNativeInputField.innerHTML.charCodeAt(0);
2632 if (charCode == 229 || charCode == 197)
2634 [currentFirstResponder insertText:_CPNativeInputField.innerHTML];
2635 _CPNativeInputField.innerHTML =
'';
2640 if (!_CPNativeInputFieldActive && _CPNativeInputFieldKeyPressedCalled == NO && _CPNativeInputField.innerHTML.length && _CPNativeInputField.innerHTML != _CPCopyPlaceholder && _CPNativeInputField.innerHTML.length < 3)
2642 _CPNativeInputFieldActive = YES;
2643 [currentFirstResponder _activateNativeInputElement:_CPNativeInputField];
2647 if (_CPNativeInputFieldActive)
2648 [
self _endInputSessionWithString:_CPNativeInputField.innerHTML];
2651 if (_CPNativeInputFieldKeyPressedCalled)
2652 _CPNativeInputField.innerHTML =
'';
2655 _CPNativeInputFieldKeyDownCalled = NO;
2660 _CPNativeInputField.addEventListener(
"keydown",
function(e)
2663 if (_CPNativeInputFieldKeyDownCalled)
2666 _CPNativeInputFieldKeyDownCalled = YES;
2667 _CPNativeInputFieldKeyUpCalled = NO;
2668 _CPNativeInputFieldKeyPressedCalled = NO;
2669 var currentFirstResponder = [[CPApp keyWindow] firstResponder];
2673 _CPNativeInputFieldKeyPressedCalled = YES;
2675 if (![currentFirstResponder respondsToSelector:
@selector(_activateNativeInputElement:)])
2680 setTimeout(
function(){
2681 _CPNativeInputFieldKeyDownCalled = NO;
2683 if (!_CPNativeInputFieldActive && _CPNativeInputFieldKeyUpCalled == NO && _CPNativeInputField.innerHTML.length && _CPNativeInputField.innerHTML != _CPCopyPlaceholder && _CPNativeInputField.innerHTML.length < 3 && !e.repeat)
2685 _CPNativeInputFieldActive = YES;
2686 [currentFirstResponder _activateNativeInputElement:_CPNativeInputField];
2688 else if (!_CPNativeInputFieldActive)
2689 [
self hideInputElement];
2695 _CPNativeInputField.addEventListener(
"keypress",
function(e)
2697 _CPNativeInputFieldKeyUpCalled = YES;
2698 _CPNativeInputFieldKeyPressedCalled = YES;
2703 _CPNativeInputField.onpaste =
function(e)
2705 var nativeClipboard = (e.originalEvent || e).clipboardData,
2708 currentFirstResponder = [[
CPApp keyWindow] firstResponder],
2711 if ([currentFirstResponder respondsToSelector:
@selector(isRichText)] && ![currentFirstResponder isRichText])
2715 if ((richtext = nativeClipboard.getData(
'text/rtf')) && !(!!(e.originalEvent || e).shiftKey) && !isPlain)
2720 setTimeout(
function(){
2721 [currentFirstResponder insertText:[[_CPRTFParser new] parseRTF:richtext]]
2729 var data = e.clipboardData.getData(
'text/plain'),
2730 cappString = [pasteboard stringForType:CPStringPboardType];
2732 if (cappString != data)
2734 [pasteboard declareTypes:[CPStringPboardType] owner:nil];
2735 [pasteboard setString:data forType:CPStringPboardType];
2738 setTimeout(
function(){
2739 [currentFirstResponder paste:self];
2747 _CPNativeInputField.oncopy =
function(e)
2751 currentFirstResponder = [[CPApp keyWindow] firstResponder];
2753 [currentFirstResponder copy:self];
2755 var stringForPasting = [pasteboard stringForType:CPStringPboardType];
2756 e.clipboardData.setData(
'text/plain', stringForPasting);
2761 _CPNativeInputField.oncut =
function(e)
2765 currentFirstResponder = [[CPApp keyWindow] firstResponder];
2768 setTimeout(
function(){
2769 [currentFirstResponder cut:self];
2773 [currentFirstResponder copy:self];
2775 var stringForPasting = [pasteboard stringForType:CPStringPboardType];
2777 e.clipboardData.setData(
'text/plain', stringForPasting);
2785 + (void)focusForTextView:(
CPTextView)currentFirstResponder
2787 if (![currentFirstResponder respondsToSelector:
@selector(_activateNativeInputElement:)])
2790 [
self hideInputElement];
2793 _CPNativeInputField.focus();
2798 + (void)focusForClipboardOfTextView:(
CPTextView)textview
2802 if (!_CPNativeInputFieldActive && _CPNativeInputField.innerHTML.length == 0)
2803 _CPNativeInputField.innerHTML = _CPCopyPlaceholder;
2805 [
self focusForTextView:textview];
2808 if (document.body.createTextRange)
2810 var range = document.body.createTextRange();
2812 range.moveToElementText(_CPNativeInputField);
2815 else if (window.getSelection)
2817 var selection = window.getSelection(),
2818 range = document.createRange();
2820 range.selectNodeContents(_CPNativeInputField);
2821 selection.removeAllRanges();
2822 selection.addRange(range);
2828 + (void)hideInputElement
2832 _CPNativeInputField.style.top =
"-10000px";
2833 _CPNativeInputField.style.left =
"-10000px";
2839 @implementation _CPTextViewValueBinder : _CPTextFieldValueBinder
2844 - (void)setPlaceholderValue:(
id)aValue withMarker:(
CPString)aMarker forBinding:(
CPString)aBinding
2846 [_source _setPlaceholderString:aValue];
2849 - (void)setValue:(
id)aValue forBinding:(
CPString)aBinding
2851 if (aValue === nil || (aValue.isa && [aValue isMemberOfClass:
CPNull]))
2852 [_source _setPlaceholderString:[
self _placeholderForMarker:CPNullMarker]];
2854 [_source _setPlaceholderString:nil];
2856 [_source setObjectValue:aValue];
2874 - (void)setAllowsUndo:(BOOL)aValue
2876 _allowsUndo = aValue;
2882 - (BOOL)isHorizontallyResizable
2884 return _isHorizontallyResizable;
2890 - (void)setHorizontallyResizable:(BOOL)aValue
2892 _isHorizontallyResizable = aValue;
2898 - (BOOL)isVerticallyResizable
2900 return _isVerticallyResizable;
2906 - (void)setVerticallyResizable:(BOOL)aValue
2908 _isVerticallyResizable = aValue;
2914 - (BOOL)usesFontPanel
2916 return _usesFontPanel;
2922 - (void)setUsesFontPanel:(BOOL)aValue
2924 _usesFontPanel = aValue;
2930 - (CGPoint)textContainerOrigin
2932 return _textContainerOrigin;
2946 - (void)setMinSize:(CGSize)aValue
2962 - (void)setMaxSize:(CGSize)aValue
2970 - (CGSize)textContainerInset
2972 return _textContainerInset;
2978 - (void)setTextContainerInset:(CGSize)aValue
2980 _textContainerInset = aValue;
2988 return _insertionPointColor;
2996 _insertionPointColor = aValue;
3012 _textColor = aValue;
3020 return _selectedTextAttributes;
3028 _selectedTextAttributes = aValue;
3036 return _typingAttributes;
3044 _typingAttributes = aValue;
3052 return _layoutManager;
3058 - (CPRange)selectedRange
3060 return _selectionRange;
3066 - (CPSelectionGranularity)selectionGranularity
3068 return _selectionGranularity;
3074 - (void)setSelectionGranularity:(CPSelectionGranularity)aValue
3076 _selectionGranularity = aValue;
3084 return _textContainer;
3092 _textContainer = aValue;
3100 return _textStorage;