mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-09 12:17:13 +00:00
Fixed: attachment support was missing in CPTextView (#2872)
This commit is contained in:
@@ -46,6 +46,7 @@ CPImagesPboardType = @"CPImagesPboardType";
|
||||
CPVideosPboardType = @"CPVideosPboardType";
|
||||
CPRTFPboardType = @"CPRTFPboardType";
|
||||
_CPSmartPboardType = @"_CPSmartPboardType";
|
||||
_CPASPboardType = @"_CPASPboardType";
|
||||
|
||||
UTF8PboardType = @"public.utf8-plain-text";
|
||||
|
||||
|
||||
+5
-1
@@ -179,7 +179,11 @@ CPKernAttributeName = @"CPKernAttributeName";
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
dataForPasting = [pasteboard stringForType:CPRTFPboardType],
|
||||
stringForPasting = [pasteboard stringForType:CPStringPboardType];
|
||||
stringForPasting = [pasteboard stringForType:CPStringPboardType],
|
||||
attributedStringData = [pasteboard stringForType:_CPASPboardType];
|
||||
|
||||
if ([self isRichText] && attributedStringData)
|
||||
return [CPKeyedUnarchiver unarchiveObjectWithData:[CPData dataWithRawString:attributedStringData]];
|
||||
|
||||
if (dataForPasting || [stringForPasting hasPrefix:"{\\rtf1\\ansi"])
|
||||
stringForPasting = [[_CPRTFParser new] parseRTF:dataForPasting ? dataForPasting : stringForPasting];
|
||||
|
||||
@@ -259,6 +259,9 @@ _oncontextmenuhandler = function () { return false; };
|
||||
{
|
||||
if (_lineFragments[i]._isInvalid)
|
||||
{
|
||||
while (i > 0 && !_lineFragments[i - 1]._isLast)
|
||||
i--;
|
||||
|
||||
startIndex = _lineFragments[i]._range.location;
|
||||
removeRange.location = i;
|
||||
removeRange.length = l - i;
|
||||
@@ -361,7 +364,7 @@ _oncontextmenuhandler = function () { return false; };
|
||||
if (ABS(rangeOffset) !== ABS(newLength - oldLength))
|
||||
return NO;
|
||||
|
||||
var verticalOffset = _lineFragments[targetLine]._fragmentRect.origin.y - _lineFragmentsForRescue[startLineForDOMRemoval]._fragmentRect.origin.y,
|
||||
var verticalOffset = CGRectGetMaxY(_lineFragments[targetLine]._fragmentRect) - CGRectGetMaxY(_lineFragmentsForRescue[startLineForDOMRemoval]._fragmentRect),
|
||||
l = _lineFragmentsForRescue.length,
|
||||
newTargetLine = startLineForDOMRemoval + removalSkip;
|
||||
|
||||
@@ -661,12 +664,10 @@ _oncontextmenuhandler = function () { return false; };
|
||||
{
|
||||
var j = i;
|
||||
|
||||
while (--j > 0 && !_lineFragments[j]._isLast)
|
||||
{
|
||||
// body intentionally left empty
|
||||
}
|
||||
while (j > 0 && !_lineFragments[j - 1]._isLast)
|
||||
j--;
|
||||
|
||||
return _lineFragments[j + 1];
|
||||
return _lineFragments[j];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,13 +677,16 @@ _oncontextmenuhandler = function () { return false; };
|
||||
{
|
||||
var l = _lineFragments.length;
|
||||
|
||||
if (location >= CPMaxRange(_lineFragments[l - 1]._range))
|
||||
return _lineFragments[l - 1];
|
||||
|
||||
for (var i = 0; i < l; i++)
|
||||
{
|
||||
if (CPLocationInRange(location, _lineFragments[i]._range))
|
||||
{
|
||||
var j = i;
|
||||
|
||||
while (!_lineFragments[j]._isLast)
|
||||
while (j < l && !_lineFragments[j]._isLast)
|
||||
j++;
|
||||
|
||||
return _lineFragments[j];
|
||||
@@ -925,15 +929,14 @@ _oncontextmenuhandler = function () { return false; };
|
||||
var correctedRect = CGRectCreateCopy(frames[j]);
|
||||
correctedRect.size.height -= frames[j]._descent;
|
||||
correctedRect.origin.y -= frames[j]._descent;
|
||||
|
||||
if (!rect)
|
||||
rect = CGRectCreateCopy(correctedRect);
|
||||
else
|
||||
rect = CGRectUnion(rect, correctedRect);
|
||||
|
||||
if (_isNewlineCharacter([[_textStorage string] characterAtIndex:MAX(0, CPMaxRange(selectedCharRange) - 1)]))
|
||||
{
|
||||
rect.size.width = containerSize.width - rect.origin.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1095,7 +1098,6 @@ var _objectsInRange = function(aList, aRange)
|
||||
span = document.createElement("span");
|
||||
|
||||
span.oncontextmenu = span.onmousedown = span.onselectstart = _oncontextmenuhandler;
|
||||
// span.contentEditable = true; // this unfortunately does not work to make native pasting work on safari
|
||||
|
||||
style = span.style;
|
||||
style.position = "absolute";
|
||||
@@ -1138,7 +1140,6 @@ var _objectsInRange = function(aList, aRange)
|
||||
else if (CPFeatureIsCompatible(CPJavaScriptTextContentFeature))
|
||||
span.textContent = aString;
|
||||
|
||||
//<!> FIXME aString.replace(/&/g,'&')
|
||||
return span;
|
||||
#else
|
||||
return nil;
|
||||
@@ -1167,16 +1168,29 @@ var _objectsInRange = function(aList, aRange)
|
||||
effectiveRange = attributes ? CPIntersectionRange(aRange, effectiveRange) : aRange;
|
||||
|
||||
var string = [textStorage._string substringWithRange:effectiveRange],
|
||||
font = [textStorage font] || [CPFont systemFontOfSize:12.0],
|
||||
underline = [attributes objectForKey:CPUnderlineStyleAttributeName] || CPUnderlineStyleNone ;
|
||||
underline = [attributes objectForKey:CPUnderlineStyleAttributeName] || CPUnderlineStyleNone;
|
||||
|
||||
if ([attributes containsKey:CPFontAttributeName])
|
||||
font = [attributes objectForKey:CPFontAttributeName];
|
||||
// this is an attachment -> create a run for it
|
||||
if (string === _CPAttachmentCharacterAsString)
|
||||
{
|
||||
if (![attributes objectForKey:_CPAttachmentInvisible])
|
||||
{
|
||||
var view = [attributes objectForKey:_CPAttachmentView],
|
||||
viewCopy = [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:view]],
|
||||
elem = viewCopy._DOMElement,
|
||||
run = {_range:CPMakeRangeCopy(effectiveRange), color:nil, font:nil, elem:elem, string:nil, view:viewCopy};
|
||||
|
||||
var color = [attributes objectForKey:CPForegroundColorAttributeName],
|
||||
run = {_range:CPMakeRangeCopy(effectiveRange), color:color, font:font, elem:nil, string:string, underline:underline};
|
||||
_runs.push(run);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var color = [attributes objectForKey:CPForegroundColorAttributeName],
|
||||
font = [attributes objectForKey:CPFontAttributeName] || [textStorage font] || [CPFont systemFontOfSize:12.0],
|
||||
run = {_range:CPMakeRangeCopy(effectiveRange), color:color, font:font, elem:nil, string:string};
|
||||
|
||||
_runs.push(run);
|
||||
_runs.push(run);
|
||||
}
|
||||
|
||||
if (!CPMaxRange(effectiveRange))
|
||||
break;
|
||||
@@ -1248,7 +1262,12 @@ var _objectsInRange = function(aList, aRange)
|
||||
for (var i = 0; i < l; i++)
|
||||
{
|
||||
if (_runs[i].elem && _runs[i].DOMactive)
|
||||
_textContainer._textView._DOMElement.removeChild(_runs[i].elem);
|
||||
{
|
||||
if (_runs[i].view)
|
||||
[_runs[i].view removeFromSuperview];
|
||||
else
|
||||
_textContainer._textView._DOMElement.removeChild(_runs[i].elem);
|
||||
}
|
||||
|
||||
_runs[i].elem = nil;
|
||||
_runs[i].DOMactive = NO;
|
||||
@@ -1266,9 +1285,7 @@ var _objectsInRange = function(aList, aRange)
|
||||
var run = runs[i];
|
||||
|
||||
if (!run.elem && CPRectIntersectsRect([_textContainer._textView exposedRect], _fragmentRect))
|
||||
{
|
||||
run.elem=[self createDOMElementWithText:run.string andFont:run.font andColor:run.color andUnderline:run.underline];
|
||||
}
|
||||
run.elem = [self createDOMElementWithText:run.string andFont:run.font andColor:run.color andUnderline:run.underline];
|
||||
|
||||
if (run.DOMactive && !run.DOMpatched)
|
||||
continue;
|
||||
@@ -1285,14 +1302,21 @@ var _objectsInRange = function(aList, aRange)
|
||||
run.elem.style.left = (orig.x) + "px";
|
||||
run.elem.style.top = (orig.y) + "px";
|
||||
|
||||
if (run.view)
|
||||
[run.view setFrameOrigin:orig];
|
||||
|
||||
if (!run.DOMactive)
|
||||
_textContainer._textView._DOMElement.appendChild(run.elem);
|
||||
{
|
||||
if (run.view)
|
||||
[self._textContainer._textView addSubview:run.view];
|
||||
else
|
||||
_textContainer._textView._DOMElement.appendChild(run.elem);
|
||||
}
|
||||
|
||||
run.DOMactive = YES;
|
||||
}
|
||||
|
||||
run.DOMpatched = NO;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1344,6 +1368,9 @@ var _objectsInRange = function(aList, aRange)
|
||||
|
||||
if (verticalOffset && _runs[i].elem)
|
||||
{
|
||||
if (_runs[i].view)
|
||||
_runs[i].view._frame.origin.y += verticalOffset;
|
||||
|
||||
_runs[i].elem.top = (_runs[i].elem.top + verticalOffset) + 'px';
|
||||
_runs[i].DOMpatched = YES;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ CPTextStorageDidProcessEditingNotification = @"CPTextStorageDidProcessEditingNot
|
||||
|
||||
@end
|
||||
|
||||
CPAttachmentCharacter = 65532; // "\ufffc";
|
||||
_CPAttachmentCharacterAsString = String.fromCharCode(CPAttachmentCharacter);
|
||||
|
||||
_CPAttachmentView = "_CPAttachmentView";
|
||||
_CPAttachmentInvisible = "_CPAttachmentInvisible";
|
||||
|
||||
var CPTextStorageDelegate_textStorageWillProcessEditing_ = 1 << 1,
|
||||
CPTextStorageDelegate_textStorageDidProcessEditing_ = 1 << 2;
|
||||
@@ -257,6 +262,15 @@ var CPTextStorageDelegate_textStorageWillProcessEditing_ = 1 << 1,
|
||||
return [super attributedSubstringFromRange:aRange];
|
||||
}
|
||||
|
||||
+ (id)attributedStringWithAttachment:(CPView)someView
|
||||
{
|
||||
var result = [[self alloc] initWithString:_CPAttachmentCharacterAsString];
|
||||
|
||||
[result setAttributes:@{_CPAttachmentView:someView} range:CPMakeRange(0, 1)];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -300,4 +314,4 @@ var CPTextStorageDelegate_textStorageWillProcessEditing_ = 1 << 1,
|
||||
[super encodeWithCoder:aCoder];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -454,10 +454,11 @@ var kDelegateRespondsTo_textShouldBeginEditing
|
||||
stringForPasting = [[self textStorage] attributedSubstringFromRange:CPMakeRangeCopy(selectedRange)],
|
||||
richData = [_CPRTFProducer produceRTF:stringForPasting documentAttributes:@{}];
|
||||
|
||||
[pasteboard declareTypes:[CPStringPboardType, CPRTFPboardType] owner:nil];
|
||||
[pasteboard setString:stringForPasting._string forType:CPStringPboardType];
|
||||
[pasteboard declareTypes:[CPStringPboardType, CPRTFPboardType, _CPSmartPboardType, _CPASPboardType] owner:nil];
|
||||
[pasteboard setString:[stringForPasting._string stringByReplacingOccurrencesOfString:_CPAttachmentCharacterAsString withString:''] forType:CPStringPboardType];
|
||||
[pasteboard setString:richData forType:CPRTFPboardType];
|
||||
[pasteboard setString:_previousSelectionGranularity + '' forType:_CPSmartPboardType];
|
||||
[pasteboard setString:[[CPKeyedArchiver archivedDataWithRootObject:stringForPasting] rawString] forType:_CPASPboardType];
|
||||
}
|
||||
|
||||
- (void)_pasteString:(id)stringForPasting
|
||||
@@ -1093,6 +1094,17 @@ Sets the selection to a range of characters in response to user action.
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_hideRange:(CPRange)rangeToHide inDragPlaceholderString:(CPTextView)placeholderString
|
||||
{
|
||||
if (!rangeToHide.length)
|
||||
return;
|
||||
|
||||
[placeholderString addAttribute:CPForegroundColorAttributeName
|
||||
value:[CPColor colorWithRed:1 green:1 blue:1 alpha:0] // invisibleInk
|
||||
range:rangeToHide];
|
||||
[placeholderString addAttribute:_CPAttachmentInvisible value:YES range:rangeToHide];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Mouse Events
|
||||
|
||||
@@ -1112,31 +1124,35 @@ Sets the selection to a range of characters in response to user action.
|
||||
// dragging the selection
|
||||
if ([self selectionGranularity] == CPSelectByCharacter && CPLocationInRange(_startTrackingLocation, _selectionRange))
|
||||
{
|
||||
var lineBeginningIndex = [_layoutManager _firstLineFragmentForLineFromLocation:_selectionRange.location]._range.location,
|
||||
placeholderRange = _MakeRangeFromAbs(lineBeginningIndex, CPMaxRange(_selectionRange)),
|
||||
var visibleRange = [_layoutManager glyphRangeForBoundingRect:_exposedRect inTextContainer:_textContainer],
|
||||
firstFragment = [_layoutManager _firstLineFragmentForLineFromLocation:_selectionRange.location],
|
||||
lastFragment = [_layoutManager _lastLineFragmentForLineFromLocation:CPMaxRange(_selectionRange)],
|
||||
lineBeginningIndex = firstFragment._range.location,
|
||||
lineEndIndex = CPMaxRange(lastFragment._range),
|
||||
placeholderRange = CPIntersectionRange(_MakeRangeFromAbs(lineBeginningIndex, lineEndIndex), visibleRange),
|
||||
placeholderString = [_textStorage attributedSubstringFromRange:placeholderRange],
|
||||
placeholderFrame = CGRectIntersection([_layoutManager boundingRectForGlyphRange:placeholderRange inTextContainer:_textContainer], _frame),
|
||||
rangeToHide = CPMakeRange(0, _selectionRange.location - lineBeginningIndex),
|
||||
rangeToHideLHS = CPMakeRange(0, _selectionRange.location - lineBeginningIndex),
|
||||
rangeToHideRHS = _MakeRangeFromAbs(CPMaxRange(_selectionRange) - lineBeginningIndex, lineEndIndex - lineBeginningIndex),
|
||||
dragPlaceholder;
|
||||
|
||||
// hide the left part of the first line of the selection that is not included
|
||||
[placeholderString addAttribute:CPForegroundColorAttributeName
|
||||
value:[CPColor colorWithRed:1 green:1 blue:1 alpha:0]
|
||||
range:rangeToHide];
|
||||
// hide the left/right parts of the first/last lines of the selection that are not included
|
||||
[self _hideRange:rangeToHideLHS inDragPlaceholderString:placeholderString];
|
||||
[self _hideRange:rangeToHideRHS inDragPlaceholderString:placeholderString];
|
||||
|
||||
_movingSelection = CPMakeRange(_startTrackingLocation, 0);
|
||||
|
||||
dragPlaceholder = [[CPTextView alloc] initWithFrame:placeholderFrame];
|
||||
dragPlaceholder = [[CPTextView alloc] initWithFrame:_frame];
|
||||
[dragPlaceholder._textStorage replaceCharactersInRange:CPMakeRange(0, 0) withAttributedString:placeholderString];
|
||||
|
||||
[dragPlaceholder setBackgroundColor:[CPColor colorWithRed:1 green:1 blue:1 alpha:0]];
|
||||
[dragPlaceholder setAlphaValue:0.5];
|
||||
|
||||
var stringForPasting = [_textStorage attributedSubstringFromRange:CPMakeRangeCopy(_selectionRange)],
|
||||
richData = [_CPRTFProducer produceRTF:stringForPasting documentAttributes:@{}],
|
||||
draggingPasteboard = [CPPasteboard pasteboardWithName:CPDragPboard];
|
||||
[draggingPasteboard declareTypes:[CPRTFPboardType, CPStringPboardType] owner:nil];
|
||||
[draggingPasteboard setString:richData forType:CPRTFPboardType];
|
||||
[draggingPasteboard declareTypes:[_CPASPboardType, CPStringPboardType] owner:nil];
|
||||
[draggingPasteboard setString:[[CPKeyedArchiver archivedDataWithRootObject:stringForPasting] rawString] forType:_CPASPboardType];
|
||||
// this is necessary because the drag will not work without data of kind CPStringPboardType
|
||||
[draggingPasteboard setString:stringForPasting._string forType:CPStringPboardType];
|
||||
|
||||
[self dragView:dragPlaceholder
|
||||
@@ -2155,38 +2171,23 @@ Sets the selection to a range of characters in response to user action.
|
||||
return (_selectionRange.length === 0 && [self _isFocused] && !_placeholderString);
|
||||
}
|
||||
|
||||
- (void)updateInsertionPointStateAndRestartTimer:(BOOL)flag
|
||||
- (CPRect)_getCaretRect
|
||||
{
|
||||
var caretRect,
|
||||
numberOfGlyphs = [_layoutManager numberOfCharacters];
|
||||
var numberOfGlyphs = [_layoutManager numberOfCharacters];
|
||||
|
||||
if (_selectionRange.length)
|
||||
[_caret setVisibility:NO];
|
||||
|
||||
if (_selectionRange.location >= numberOfGlyphs) // cursor is "behind" the last chacacter
|
||||
if (!numberOfGlyphs)
|
||||
{
|
||||
caretRect = [_layoutManager boundingRectForGlyphRange:CPMakeRange(MAX(0,_selectionRange.location - 1), 1) inTextContainer:_textContainer];
|
||||
|
||||
if (!numberOfGlyphs)
|
||||
{
|
||||
var font = [_typingAttributes objectForKey:CPFontAttributeName] || [self font];
|
||||
|
||||
caretRect.size.height = [font size];
|
||||
caretRect.origin.y = ([font ascender] - [font descender]) * 0.5 + _textContainerOrigin.y;
|
||||
}
|
||||
|
||||
caretRect.origin.x += caretRect.size.width;
|
||||
|
||||
if (_selectionRange.location > 0 && [[_textStorage string] characterAtIndex:_selectionRange.location - 1] === '\n')
|
||||
{
|
||||
caretRect.origin.y += caretRect.size.height;
|
||||
caretRect.origin.x = 0;
|
||||
}
|
||||
var font = [_typingAttributes objectForKey:CPFontAttributeName] || [self font];
|
||||
return CGRectMake(1, ([font ascender] - [font descender]) * 0.5 + _textContainerOrigin.y, 1, [font size]);
|
||||
}
|
||||
else
|
||||
caretRect = [_layoutManager boundingRectForGlyphRange:CPMakeRange(_selectionRange.location, 1) inTextContainer:_textContainer];
|
||||
|
||||
var loc = (_selectionRange.location === numberOfGlyphs && numberOfGlyphs > 0) ? _selectionRange.location - 1 : _selectionRange.location,
|
||||
// cursor is at a newline character -> jump to next line
|
||||
if (_selectionRange.location == numberOfGlyphs && _isNewlineCharacter([[_textStorage string] characterAtIndex:_selectionRange.location - 1]))
|
||||
return CGRectCreateCopy([_layoutManager extraLineFragmentRect]);
|
||||
|
||||
var caretRect = [_layoutManager boundingRectForGlyphRange:CPMakeRange(_selectionRange.location, 1) inTextContainer:_textContainer];
|
||||
|
||||
var loc = (_selectionRange.location == numberOfGlyphs) ? _selectionRange.location - 1 : _selectionRange.location,
|
||||
caretOffset = [_layoutManager _characterOffsetAtLocation:loc],
|
||||
oldYPosition = CGRectGetMaxY(caretRect),
|
||||
caretDescend = [_layoutManager _descentAtLocation:loc];
|
||||
@@ -2196,14 +2197,24 @@ Sets the selection to a range of characters in response to user action.
|
||||
caretRect.origin.y += caretOffset;
|
||||
caretRect.size.height = oldYPosition - caretRect.origin.y;
|
||||
}
|
||||
|
||||
if (caretDescend < 0)
|
||||
caretRect.size.height -= caretDescend;
|
||||
|
||||
if (_selectionRange.location == numberOfGlyphs)
|
||||
caretRect.origin.x += caretRect.size.width;
|
||||
|
||||
caretRect.origin.x += _textContainerOrigin.x;
|
||||
caretRect.origin.y += _textContainerOrigin.y;
|
||||
caretRect.size.width = 1;
|
||||
|
||||
[_caret setRect:caretRect];
|
||||
return caretRect;
|
||||
}
|
||||
- (void)updateInsertionPointStateAndRestartTimer:(BOOL)flag
|
||||
{
|
||||
if (_selectionRange.length)
|
||||
[_caret setVisibility:NO];
|
||||
|
||||
[_caret setRect:[self _getCaretRect]];
|
||||
|
||||
if (flag)
|
||||
[_caret startBlinking];
|
||||
@@ -2215,6 +2226,10 @@ Sets the selection to a range of characters in response to user action.
|
||||
location = [self _characterIndexFromRawPoint:CGPointCreateCopy(point)];
|
||||
|
||||
_movingSelection = CPMakeRange(location, 0);
|
||||
|
||||
if (CPLocationInRange(location, _selectionRange))
|
||||
return;
|
||||
|
||||
[_caret _drawCaretAtLocation:_movingSelection.location];
|
||||
[_caret setVisibility:YES];
|
||||
}
|
||||
@@ -2227,7 +2242,7 @@ Sets the selection to a range of characters in response to user action.
|
||||
var location = [self convertPoint:[aSender draggingLocation] fromView:nil],
|
||||
pasteboard = [aSender draggingPasteboard];
|
||||
|
||||
if ([pasteboard availableTypeFromArray:[CPRTFPboardType, CPStringPboardType]])
|
||||
if ([pasteboard availableTypeFromArray:[_CPASPboardType]])
|
||||
{
|
||||
[_caret setVisibility:NO];
|
||||
|
||||
@@ -2244,15 +2259,10 @@ Sets the selection to a range of characters in response to user action.
|
||||
[self _deleteForRange:_selectionRange];
|
||||
[self setSelectedRange:_movingSelection];
|
||||
|
||||
var dataForPasting = [pasteboard stringForType:CPRTFPboardType] || [pasteboard stringForType:CPStringPboardType];
|
||||
|
||||
var stringForPasting = [CPKeyedUnarchiver unarchiveObjectWithData:[CPData dataWithRawString:[pasteboard stringForType:_CPASPboardType]]];
|
||||
// setTimeout is to a work around a transaction issue with the undomanager
|
||||
setTimeout(function(){
|
||||
|
||||
if ([dataForPasting hasPrefix:"{\\rtf"])
|
||||
[self insertText:[[_CPRTFParser new] parseRTF:dataForPasting]];
|
||||
else
|
||||
[self insertText:dataForPasting];
|
||||
[self insertText:stringForPasting];
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -2525,6 +2535,7 @@ var CPTextViewAllowsUndoKey = @"CPTextViewAllowsUndoKey",
|
||||
style.whiteSpace = "pre";
|
||||
style.backgroundColor = "black";
|
||||
_caretDOM.style.width = "1px";
|
||||
_caretDOM.style.zIndex = 10001;
|
||||
_textView = aView;
|
||||
_textView._DOMElement.appendChild(_caretDOM);
|
||||
}
|
||||
|
||||
@@ -199,13 +199,11 @@ var CPSystemTypesetterFactory,
|
||||
[_layoutManager setLocation:CGPointMake(myX, _lineBase) forStartOfGlyphRange:lineRange];
|
||||
[_layoutManager _setAdvancements:advancements forGlyphRange:lineRange];
|
||||
|
||||
if (!sameLine) //fix the _lineFragments when fontsizes differ
|
||||
{
|
||||
var l = _lineFragments.length;
|
||||
//fix the _lineFragments when fontsizes differ
|
||||
var l = _lineFragments.length;
|
||||
|
||||
for (var i = 0 ; i < l ; i++)
|
||||
[_lineFragments[i] _adjustForHeight:_lineHeight];
|
||||
}
|
||||
for (var i = 0 ; i < l ; i++)
|
||||
[_lineFragments[i] _adjustForHeight:_lineHeight];
|
||||
|
||||
if (!lineCount) // do not rescue on first line
|
||||
return NO;
|
||||
@@ -245,6 +243,7 @@ var CPSystemTypesetterFactory,
|
||||
wrapWidth = 0,
|
||||
isNewline = NO,
|
||||
isTabStop = NO,
|
||||
isAttachment = NO,
|
||||
isWordWrapped = NO,
|
||||
numberOfGlyphs= [_textStorage length],
|
||||
leading,
|
||||
@@ -322,6 +321,34 @@ var CPSystemTypesetterFactory,
|
||||
|
||||
switch (currentCharCode) // faster than sending actionForControlCharacterAtIndex: called for each char.
|
||||
{
|
||||
case CPAttachmentCharacter:
|
||||
{
|
||||
var attributes = [_textStorage attributesAtIndex:glyphIndex effectiveRange:nil],
|
||||
view = [attributes objectForKey:_CPAttachmentView],
|
||||
viewSize = view ? view._frame.size : CGSizeMake(0, 0);
|
||||
|
||||
rangeWidth = prevRangeWidth + viewSize.width; // undo sizing of dummy character
|
||||
|
||||
isAttachment = YES;
|
||||
wrapRange = CPMakeRange(lineRange.location, lineRange.length - 1); // wrap before image
|
||||
|
||||
// prevent crash when image is larger than text container
|
||||
if (viewSize.width > containerSizeWidth)
|
||||
wrapRange.length++;
|
||||
|
||||
wrapWidth = rangeWidth;
|
||||
wrapRange._height = _lineHeight;
|
||||
wrapRange._base = _lineBase;
|
||||
|
||||
if (viewSize.height > _lineBase)
|
||||
_lineBase = viewSize.height;
|
||||
|
||||
if (viewSize.height > _lineHeight)
|
||||
_lineHeight = viewSize.height - descent + leading;
|
||||
|
||||
ascent = viewSize.height;
|
||||
break;
|
||||
}
|
||||
case 9: // '\t'
|
||||
{
|
||||
var nextTab = [self textTabForWidth:rangeWidth + lineOrigin.x writingDirection:0];
|
||||
@@ -346,7 +373,6 @@ var CPSystemTypesetterFactory,
|
||||
}
|
||||
|
||||
advancements.push({width: rangeWidth - prevRangeWidth, height: ascent, descent: descent});
|
||||
|
||||
prevRangeWidth = _lineWidth = rangeWidth;
|
||||
|
||||
if (lineOrigin.x + rangeWidth > containerSizeWidth)
|
||||
@@ -364,16 +390,13 @@ var CPSystemTypesetterFactory,
|
||||
glyphIndex = CPMaxRange(lineRange) - 1; // start the line starts directly at current character
|
||||
}
|
||||
|
||||
if (isNewline || isTabStop)
|
||||
if (isNewline || isTabStop || isAttachment)
|
||||
{
|
||||
if ([self _flushRange:lineRange lineOrigin:lineOrigin currentContainer:_currentTextContainer advancements:advancements lineCount:numLines sameLine:!isNewline])
|
||||
return;
|
||||
|
||||
if (isTabStop)
|
||||
{
|
||||
if (isTabStop || isAttachment)
|
||||
lineOrigin.x += rangeWidth;
|
||||
isTabStop = NO;
|
||||
}
|
||||
|
||||
if (isNewline)
|
||||
{
|
||||
@@ -404,6 +427,9 @@ var CPSystemTypesetterFactory,
|
||||
_lineBase = ascent;
|
||||
}
|
||||
|
||||
isTabStop = NO;
|
||||
isAttachment = NO;
|
||||
isWordWrapped = NO;
|
||||
_lineWidth = 0;
|
||||
advancements = [];
|
||||
currentAnchor = 0;
|
||||
@@ -412,18 +438,20 @@ var CPSystemTypesetterFactory,
|
||||
measuringRange = CPMakeRange(glyphIndex + 1, 0);
|
||||
wrapRange = CPMakeRange(0, 0);
|
||||
wrapWidth = 0;
|
||||
isWordWrapped = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// this is to "flush" the remaining characters
|
||||
if (lineRange.length)
|
||||
{
|
||||
[self _flushRange:lineRange lineOrigin:lineOrigin currentContainer:_currentTextContainer advancements:advancements lineCount:numLines sameLine:NO];
|
||||
}
|
||||
|
||||
var rect = CGRectMake(0, lineOrigin.y, containerSizeWidth, [_layoutManager._lineFragments lastObject]._usedRect.size.height - descent);
|
||||
var rect = CGRectMake(1, lineOrigin.y - descent, containerSizeWidth, currentFontLineHeight);
|
||||
[_layoutManager setExtraLineFragmentRect:rect usedRect:rect textContainer:_currentTextContainer];
|
||||
|
||||
var fragment = [_layoutManager._lineFragments lastObject];
|
||||
|
||||
if (fragment)
|
||||
fragment._isLast = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,12 +28,6 @@
|
||||
|
||||
[[theWindow contentView] addSubview:textView];
|
||||
|
||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(400,10,75,25)];
|
||||
[button setTitle:@"Underline"];
|
||||
[button setTarget:textView];
|
||||
[button setAction:@selector(underline:)];
|
||||
[[theWindow contentView] addSubview:button];
|
||||
|
||||
delegateSpy = spy(self);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,36 +101,36 @@
|
||||
item = [mainMenu insertItemWithTitle:@"Format" action:nil keyEquivalent:nil atIndex:0];
|
||||
var formatMenu = [[CPMenu alloc] initWithTitle:@"Format Menu"];
|
||||
[formatMenu addItemWithTitle:@"Font panel" action:@selector(orderFrontFontPanel:) keyEquivalent:@"f"];
|
||||
[formatMenu addItemWithTitle:@"Underline" action:@selector(underline:) keyEquivalent:@"u"];
|
||||
[mainMenu setSubmenu:formatMenu forItem:item];
|
||||
|
||||
//
|
||||
// var centeredParagraph=[CPParagraphStyle new];
|
||||
// [centeredParagraph setAlignment: CPCenterTextAlignment];
|
||||
// [_textView insertText:[[CPAttributedString alloc] initWithString:@"Fusce\n"
|
||||
// attributes:[CPDictionary dictionaryWithObjects:[centeredParagraph, [CPFont boldFontWithName:"Arial" size:18], [CPColor redColor]]
|
||||
// forKeys:[CPParagraphStyleAttributeName, CPFontAttributeName, CPForegroundColorAttributeName]]]];
|
||||
//
|
||||
// [_textView insertText: [[CPAttributedString alloc] initWithString:@"lectus neque cr as eget lectus neque cr as eget lectus cr as eget lectus"
|
||||
// attributes:[CPDictionary dictionaryWithObjects:[ [CPFont fontWithName:"Arial" size:12]] forKeys: [CPFontAttributeName]]]];
|
||||
//
|
||||
// [_textView insertText:[[CPAttributedString alloc] initWithString:@" proin, this is text in boldface "
|
||||
// attributes:[CPDictionary dictionaryWithObjects:[ [CPFont boldFontWithName:"Arial" size:12]] forKeys: [CPFontAttributeName]]]];
|
||||
// [_textView insertText:[[CPAttributedString alloc] initWithString:@"111111 neque cr as eget lectus neque cr as eget lectus cr as eget lectus"
|
||||
// attributes:[CPDictionary dictionaryWithObjects:[ [CPFont fontWithName:"Arial" size:12.0]] forKeys: [CPFontAttributeName]]]];
|
||||
//
|
||||
[_textView insertText:"123"];
|
||||
var tempImageView = [[CPImageView alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
|
||||
[tempImageView setImage:[[CPImage alloc] initWithContentsOfFile:@"Resources/spinner.gif" size:CGSizeMake(32, 32)]]
|
||||
|
||||
[_textView insertText:[CPTextStorage attributedStringWithAttachment:tempImageView]];
|
||||
[_textView insertText:" 456 "];
|
||||
|
||||
var tempButton = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 64, 28)]
|
||||
[_textView insertText:[CPTextStorage attributedStringWithAttachment:tempButton]];
|
||||
|
||||
// var centeredParagraph=[CPParagraphStyle new];
|
||||
// [centeredParagraph setAlignment: CPCenterTextAlignment];
|
||||
// [_textView insertText:[[CPAttributedString alloc] initWithString:@"Fusce\n"
|
||||
// attributes:[CPDictionary dictionaryWithObjects:[centeredParagraph, [CPFont boldFontWithName:"Arial" size:18], [CPColor redColor]]
|
||||
// forKeys:[CPParagraphStyleAttributeName, CPFontAttributeName, CPForegroundColorAttributeName]]]];
|
||||
|
||||
[theWindow orderFront:self];
|
||||
[CPMenu setMenuBarVisible:YES];
|
||||
}
|
||||
|
||||
//
|
||||
// - (void) makeRTF:sender
|
||||
// {
|
||||
// [_textView2 setString: [_CPRTFProducer produceRTF:[_textView textStorage] documentAttributes: @{}] ];
|
||||
// var tc = [_CPRTFParser new];
|
||||
// var mystr=[tc parseRTF:[_textView2 stringValue]];
|
||||
// [_textView selectAll: self];
|
||||
// [_textView insertText: mystr];
|
||||
//
|
||||
// }
|
||||
- (void) makeRTF:sender
|
||||
{
|
||||
[_textView2 setString: [_CPRTFProducer produceRTF:[_textView textStorage] documentAttributes: @{}] ];
|
||||
var tc = [_CPRTFParser new];
|
||||
var mystr=[tc parseRTF:[_textView2 stringValue]];
|
||||
[_textView selectAll: self];
|
||||
[_textView insertText: mystr];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user