diff --git a/AppKit/CPColor.j b/AppKit/CPColor.j index e3fdd083e..7fbac258d 100644 --- a/AppKit/CPColor.j +++ b/AppKit/CPColor.j @@ -467,6 +467,16 @@ var cachedBlackColor, return [[CPColor alloc] _initWithCSSString: aString]; } ++ (CPColor)selectedTextBackgroundColor +{ + return [CPColor colorWithHexString:"99CCFF"]; +} + ++ (CPColor)selectedTextBackgroundColorUnfocussed +{ + return [CPColor colorWithHexString:"CCCCCC"]; +} + /* @ignore */ - (id)_initWithCSSString:(CPString)aString { diff --git a/AppKit/CPControl.j b/AppKit/CPControl.j index a231378d2..94ebda73b 100644 --- a/AppKit/CPControl.j +++ b/AppKit/CPControl.j @@ -30,6 +30,16 @@ @global CPApp +@global CPCancelTextMovement +@global CPLeftTextMovement +@global CPRightTextMovement +@global CPUpTextMovement +@global CPDownTextMovement +@global CPReturnTextMovement +@global CPBacktabTextMovement +@global CPTabTextMovement +@global CPOtherTextMovement + CPLeftTextAlignment = 0; CPRightTextAlignment = 1; CPCenterTextAlignment = 2; diff --git a/AppKit/CPEvent.j b/AppKit/CPEvent.j index 190065d4d..e30abf44b 100644 --- a/AppKit/CPEvent.j +++ b/AppKit/CPEvent.j @@ -28,11 +28,13 @@ @import "CPCompatibility.j" @import "CGGeometry.j" -@import "CPText.j" @class CPTextField @global CPApp +@global CPNewlineCharacter +@global CPCarriageReturnCharacter +@global CPEnterCharacter var _CPEventPeriodicEventPeriod = 0, _CPEventPeriodicEventTimer = nil, diff --git a/AppKit/CPText.j b/AppKit/CPText.j index 7203a8fd4..2df332ab6 100644 --- a/AppKit/CPText.j +++ b/AppKit/CPText.j @@ -27,6 +27,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +@import + +@import "CPPasteboard.j" +@import "CPView.j" +@import "_CPRTFParser.j" +@import "_CPRTFProducer.j" + +@global CPStringPboardType +@class CPAttributedString + @protocol CPTextDelegate - (BOOL)textShouldBeginEditing:(CPText)aTextObject; @@ -86,3 +96,203 @@ CPBaselineOffsetAttributeName = @"CPBaselineOffsetAttributeName"; CPAttachmentAttributeName = @"CPAttachmentAttributeName"; CPLigatureAttributeName = @"CPLigatureAttributeName"; CPKernAttributeName = @"CPKernAttributeName"; + +@implementation CPText : CPView +{ + int _previousSelectionGranularity; +} + +- (void)changeFont:(id)sender +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)copy:(id)sender +{ + var selectedRange = [self selectedRange]; + + if (selectedRange.length < 1) + return; + + var pasteboard = [CPPasteboard generalPasteboard], + stringForPasting = [[self stringValue] substringWithRange:selectedRange]; + + [pasteboard declareTypes:[CPStringPboardType] owner:nil]; + + if ([self isRichText]) + { + // crude hack to make rich pasting possible in chrome and firefox. this requires a RTF roundtrip, unfortunately + var richData = [_CPRTFProducer produceRTF:[[self textStorage] attributedSubstringFromRange:selectedRange] documentAttributes:@{}]; + [pasteboard setString:richData forType:CPStringPboardType]; + } + else + { + [pasteboard setString:stringForPasting forType:CPStringPboardType]; + } +} +- (void)paste:(id)sender +{ + var pasteboard = [CPPasteboard generalPasteboard], + // dataForPasting = [pasteboard dataForType:CPRichStringPboardType], + stringForPasting = [pasteboard stringForType:CPStringPboardType]; + + if ([stringForPasting hasPrefix:"{\\rtf1\\ansi"]) + stringForPasting = [[_CPRTFParser new] parseRTF:stringForPasting]; + + if (![self isRichText] && [stringForPasting isKindOfClass:[CPAttributedString class]]) + stringForPasting = stringForPasting._string; + + if (_previousSelectionGranularity > 0) + { + // FIXME: handle smart pasting + } + + if (stringForPasting) + [self insertText:stringForPasting]; +} + +- (void)copyFont:(id)sender +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)delete:(id)sender +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (CPFont)font:(CPFont)aFont +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return nil; +} + +- (BOOL)isHorizontallyResizable +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return NO; +} + +- (BOOL)isRichText +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return NO; +} + +- (BOOL)isRulerVisible +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return NO; +} + +- (BOOL)isVerticallyResizable +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return NO; +} + +- (CGSize)maxSize +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return CGSizeMake(0,0); +} + +- (CGSize)minSize +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + return CGSizeMake(0,0); +} + +- (void)pasteFont:(id)sender +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)replaceCharactersInRange:(CPRange)aRange withString:(CPString)aString +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)scrollRangeToVisible:(CPRange)aRange +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)selectedAll:(id)sender +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (CPRange)selectedRange +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return CPMakeRange(CPNotFound, 0); +} + +- (void)setFont:(CPFont)aFont +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)setFont:(CPFont)aFont rang:(CPRange)aRange +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)setHorizontallyResizable:(BOOL)flag +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)setMaxSize:(CGSize)aSize +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)setMinSize:(CGSize)aSize +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)setString:(CPString)aString +{ + [self replaceCharactersInRange:CPMakeRange(0, [[self string] length]) withString:aString]; +} + +- (void)setUsesFontPanel:(BOOL)flag +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (void)setVerticallyResizable:(BOOL)flag +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (CPString)string +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return nil; +} + +- (void)underline:(id)sender +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +- (BOOL)usesFontPanel +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); + + return NO; +} + +@end \ No newline at end of file diff --git a/AppKit/CPTextView/CPParagraphStyle.j b/AppKit/CPTextView/CPParagraphStyle.j index f3b509e1a..bf9737290 100755 --- a/AppKit/CPTextView/CPParagraphStyle.j +++ b/AppKit/CPTextView/CPParagraphStyle.j @@ -25,7 +25,6 @@ */ @import -@import "CPControl.j" var _sharedDefaultParagraphStyle, _defaultTabStopArray; @@ -42,8 +41,8 @@ CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName"; @implementation CPTextTab : CPObject { - int _type @accessors(property=tabStopType); - double _location @accessors(property=location); + int _type @accessors(property = tabStopType); + double _location @accessors(property = location); } - (id)initWithType:(CPTabStopType) aType location:(double) aLocation @@ -57,6 +56,10 @@ CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName"; return self; } + +#pragma mark - +#pragma mark Coding methods + - (id)initWithCoder:(id)aCoder { self = [self init]; @@ -78,25 +81,30 @@ CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName"; @end + @implementation CPParagraphStyle : CPObject { - CPArray _tabStops @accessors(property = tabStops); - CPTextAlignment _alignment @accessors(property = alignment); - unsigned _firstLineHeadIndent @accessors(property = firstLineHeadIndent); - unsigned _headIndent @accessors(property = headIndent); - unsigned _tailIndent @accessors(property = tailIndent); - unsigned _paragraphSpacing @accessors(property = paragraphSpacing); - unsigned _minimumLineHeight @accessors(property = minimumLineHeight); - unsigned _maximumLineHeight @accessors(property = maximumLineHeight); - unsigned _lineSpacing @accessors(property = lineSpacing); + CPArray _tabStops @accessors(property = tabStops); + CPTextAlignment _alignment @accessors(property = alignment); + unsigned _firstLineHeadIndent @accessors(property = firstLineHeadIndent); + unsigned _headIndent @accessors(property = headIndent); + unsigned _tailIndent @accessors(property = tailIndent); + unsigned _paragraphSpacing @accessors(property = paragraphSpacing); + unsigned _minimumLineHeight @accessors(property = minimumLineHeight); + unsigned _maximumLineHeight @accessors(property = maximumLineHeight); + unsigned _lineSpacing @accessors(property = lineSpacing); } + +#pragma mark - +#pragma mark Class methods + + (CPParagraphStyle)defaultParagraphStyle { - if (!_sharedDefaultParagraphStyle) + if (!_sharedDefaultParagraphStyle) _sharedDefaultParagraphStyle = [self new]; - return _sharedDefaultParagraphStyle; + return _sharedDefaultParagraphStyle; } + (CPArray)_defaultTabStops @@ -112,18 +120,13 @@ CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName"; _defaultTabStopArray.push([[CPTextTab alloc] initWithType:CPLeftTabStopType location:i * 28]); } } - return _defaultTabStopArray; -} -- (void)addTabStop:(CPTextTab)aStop -{ - _tabStops.push(aStop); + + return _defaultTabStopArray; } -- (void)_initWithDefaults -{ - _alignment = CPLeftTextAlignment; - _tabStops = [[[self class] _defaultTabStops] copy]; -} + +#pragma mark - +#pragma mark Init methods - (id)init { @@ -131,11 +134,7 @@ CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName"; return self; } -- (id)copy -{ - var other = [[self class] alloc]; - return [other initWithParagraphStyle:self]; -} + - (CPParagraphStyle)initWithParagraphStyle:(CPParagraphStyle)other { other._tabStops = [_tabStops copy]; @@ -151,6 +150,28 @@ CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName"; return self; } +- (void)_initWithDefaults +{ + _alignment = CPLeftTextAlignment; + _tabStops = [[[self class] _defaultTabStops] copy]; +} + +- (void)addTabStop:(CPTextTab)aStop +{ + _tabStops.push(aStop); +} + +- (id)copy +{ + var other = [[self class] alloc]; + + return [other initWithParagraphStyle:self]; +} + + +#pragma mark - +#pragma mark Code methods + - (id)initWithCoder:(id)aCoder { self = [self init]; diff --git a/AppKit/CPTextView/CPTextStorage.j b/AppKit/CPTextView/CPTextStorage.j index c57c059fd..6d209bf5d 100755 --- a/AppKit/CPTextView/CPTextStorage.j +++ b/AppKit/CPTextView/CPTextStorage.j @@ -27,7 +27,6 @@ @class CPLayoutManager; - CPTextStorageEditedAttributes = 1; CPTextStorageEditedCharacters = 2; diff --git a/AppKit/CPTextView/CPTextView.j b/AppKit/CPTextView/CPTextView.j index acb5c11f0..dc150ad08 100755 --- a/AppKit/CPTextView/CPTextView.j +++ b/AppKit/CPTextView/CPTextView.j @@ -24,12 +24,12 @@ */ @import "CPText.j" -@import "CPTextStorage.j" -@import "CPTextContainer.j" -@import "CPFontManager.j" -@import "CPLayoutManager.j" @import "CPPasteboard.j" @import "CPColorPanel.j" +@import "CPFontManager.j" +@import "CPTextStorage.j" +@import "CPTextContainer.j" +@import "CPLayoutManager.j" @class _CPRTFProducer; @class _CPRTFParser; @@ -57,22 +57,6 @@ _MidRange = function(a1) return Math.floor((CPMaxRange(a1) + a1.location) / 2); }; - -// FIXME: move to CPColor, and use attribut theme for the color -@implementation CPColor (CPTextViewExtensions) - -+ (CPColor)selectedTextBackgroundColor -{ - return [CPColor colorWithHexString:"99CCFF"]; -} -+ (CPColor)selectedTextBackgroundColorUnfocussed -{ - return [CPColor colorWithHexString:"CCCCCC"]; -} - -@end - - /* CPSelectionGranularity */ @@ -87,197 +71,6 @@ var kDelegateRespondsTo_textShouldBeginEditing kDelegateRespondsTo_textView_shouldChangeTextInRange_replacementString = 0x0008, kDelegateRespondsTo_textView_shouldChangeTypingAttributes_toAttributes = 0x0010; -@implementation CPText : CPView -{ - int _previousSelectionGranularity; -} - -- (void)changeFont:(id)sender -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)copy:(id)sender -{ - var selectedRange = [self selectedRange]; - - if (selectedRange.length < 1) - return; - - var pasteboard = [CPPasteboard generalPasteboard], - stringForPasting = [[self stringValue] substringWithRange:selectedRange]; - - [pasteboard declareTypes:[CPStringPboardType] owner:nil]; - - if ([self isRichText]) - { - // crude hack to make rich pasting possible in chrome and firefox. this requires a RTF roundtrip, unfortunately - var richData = [_CPRTFProducer produceRTF:[[self textStorage] attributedSubstringFromRange:selectedRange] documentAttributes:@{}]; - [pasteboard setString:richData forType:CPStringPboardType]; - } - else - [pasteboard setString:stringForPasting forType:CPStringPboardType]; - -} -- (void)paste:(id)sender -{ - var pasteboard = [CPPasteboard generalPasteboard], - // dataForPasting = [pasteboard dataForType:CPRichStringPboardType], - stringForPasting = [pasteboard stringForType:CPStringPboardType]; - - if ([stringForPasting hasPrefix:"{\\rtf1\\ansi"]) - stringForPasting = [[_CPRTFParser new] parseRTF:stringForPasting]; - - if (![self isRichText] && [stringForPasting isKindOfClass:[CPAttributedString class]]) - stringForPasting = stringForPasting._string; - - if (_previousSelectionGranularity > 0) - { - // FIXME: handle smart pasting - } - - if (stringForPasting) - [self insertText:stringForPasting]; -} - -- (void)copyFont:(id)sender -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)delete:(id)sender -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (CPFont)font:(CPFont)aFont -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return nil; -} - -- (BOOL)isHorizontallyResizable -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return NO; -} - -- (BOOL)isRichText -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return NO; -} - -- (BOOL)isRulerVisible -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return NO; -} - -- (BOOL)isVerticallyResizable -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return NO; -} - -- (CGSize)maxSize -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return CPMakeSize(0,0); -} - -- (CGSize)minSize -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return CPMakeSize(0,0); -} - -- (void)pasteFont:(id)sender -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)replaceCharactersInRange:(CPRange)aRange withString:(CPString)aString -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)scrollRangeToVisible:(CPRange)aRange -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)selectedAll:(id)sender -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (CPRange)selectedRange -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return CPMakeRange(CPNotFound, 0); -} - -- (void)setFont:(CPFont)aFont -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)setFont:(CPFont)aFont rang:(CPRange)aRange -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)setHorizontallyResizable:(BOOL)flag -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)setMaxSize:(CGSize)aSize -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)setMinSize:(CGSize)aSize -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)setString:(CPString)aString -{ - [self replaceCharactersInRange:CPMakeRange(0, [[self string] length]) withString:aString]; -} - -- (void)setUsesFontPanel:(BOOL)flag -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (void)setVerticallyResizable:(BOOL)flag -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (CPString)string -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return nil; -} - -- (void)underline:(id)sender -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); -} - -- (BOOL)usesFontPanel -{ - _CPRaiseInvalidAbstractInvocation(self, _cmd); - return NO; -} - -@end - - /*! @ingroup appkit @class CPTextView @@ -694,7 +487,7 @@ var kDelegateRespondsTo_textShouldBeginEditing - (void)insertText:(CPString)aString { var isAttributed = [aString isKindOfClass:CPAttributedString], - string = (isAttributed)?[aString string]:aString; + string = isAttributed ? [aString string]:aString; if (![self shouldChangeTextInRange:CPMakeRangeCopy(_selectionRange) replacementString:string]) return; diff --git a/AppKit/CPTextView/CPTypesetter.j b/AppKit/CPTextView/CPTypesetter.j index 2b01afc45..9a282d288 100755 --- a/AppKit/CPTextView/CPTypesetter.j +++ b/AppKit/CPTextView/CPTypesetter.j @@ -50,6 +50,7 @@ function _widthOfStringForFont(aString, aFont) { if (!_measuringContext) _measuringContext = CGBitmapGraphicsContextCreate(); + if (!_didTestCanvasSizingValid && CPFeatureIsCompatible(CPHTMLCanvasFeature)) { var teststring = "0123456879abcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.-()"; @@ -57,20 +58,24 @@ function _widthOfStringForFont(aString, aFont) _measuringContext.font = [aFont cssString]; _isCanvasSizingInvalid = [teststring sizeWithFont:aFont].width != _measuringContext.measureText(teststring).width; } + if (!CPFeatureIsCompatible(CPHTMLCanvasFeature) || _isCanvasSizingInvalid) // measuring with canvas is _much_ faster on chrome return [aString sizeWithFont:aFont]; + if (_measuringContextFont !== aFont) { _measuringContextFont = aFont; _measuringContext.font = [aFont cssString]; } + return _measuringContext.measureText(aString); } -var CPSystemTypesetterFactory = Nil; +var CPSystemTypesetterFactory = nil; @implementation CPTypesetter : CPObject { + } + (id)sharedSystemTypesetter @@ -118,9 +123,10 @@ var CPSystemTypesetterFactory = Nil; @end + var _sharedSimpleTypesetter = nil; -@implementation CPSimpleTypesetter:CPTypesetter +@implementation CPSimpleTypesetter : CPTypesetter { CPLayoutManager _layoutManager; CPTextContainer _currentTextContainer; diff --git a/AppKit/CPTextView/_CPRTFParser.j b/AppKit/CPTextView/_CPRTFParser.j index 88b5e8d46..1f6579659 100755 --- a/AppKit/CPTextView/_CPRTFParser.j +++ b/AppKit/CPTextView/_CPRTFParser.j @@ -27,27 +27,29 @@ e.g. using zaach/jison on github @import @import @import "CPFontManager.j" -@import "CPText.j" @import "CPParagraphStyle.j" +@global CPFontAttributeName +@global CPForegroundColorAttributeName + var hexTable = []; // Hold the attributes of the current run @implementation _RTFAttribute : CPObject { - CPRange _range; - CPParagraphStyle paragraph; - CPColor fgColour; - CPColor bgColour; - CPColor ulColour; - CPString fontName; - unsigned fontSize; - BOOL bold; - BOOL italic; - BOOL underline; - BOOL strikethrough; - BOOL script; - BOOL _tabChanged; + CPRange _range; + CPParagraphStyle paragraph; + CPColor fgColour; + CPColor bgColour; + CPColor ulColour; + CPString fontName; + unsigned fontSize; + BOOL bold; + BOOL italic; + BOOL underline; + BOOL strikethrough; + BOOL script; + BOOL _tabChanged; } - (id)init @@ -94,7 +96,6 @@ var hexTable = []; if (font == nil) { - /* Last resort, default font. :-( */ font = [CPFont systemFontOfSize:fontSize]; } @@ -288,6 +289,7 @@ var kRgsymRtf = { _freename = ""; _parsingFontTable = NO; } + return self; } @@ -301,12 +303,14 @@ var kRgsymRtf = { case 1: console.log("skipped : " + sym[4]); - return ''; + return ''; + default: if (sym && sym[4]) return sym[4]; } } + - (BOOL)pushState { _states.push["group"]; @@ -319,112 +323,139 @@ var kRgsymRtf = { if (_curState > 0) _curState--; + return YES; } - (CPString)_parseSpec:(CPArray)sym parameter:(CPString)v { var ch = ''; + switch (sym[4]) { case "ipfnDestSkip": - _curState++; - return ''; + _curState++; + return ''; + case "ipfnHex": - ch = _rtf.charAt(++_currentParseIndex); - var hex = ''; - while (/[a-fA-F0-9\']/.test(ch)) - { - if (ch == "'") - { - _currentParseIndex++; - continue; - } - hex += (ch + ''); - ch = _rtf.charAt(++_currentParseIndex); - } - //ch = parseInt(ch, 16); - console.log("hex : " + hex); - _hexreturn = YES; - _currentParseIndex--; - if (_curState !== 0) - return ''; - else return hex; - break; + ch = _rtf.charAt(++_currentParseIndex); + + var hex = ''; + + while (/[a-fA-F0-9\']/.test(ch)) + { + if (ch == "'") + { + _currentParseIndex++; + continue; + } + + hex += (ch + ''); + ch = _rtf.charAt(++_currentParseIndex); + } + //ch = parseInt(ch, 16); + //console.log("hex : " + hex); + _hexreturn = YES; + _currentParseIndex--; + + if (_curState !== 0) + return ''; + else + return hex; + break; + case "codePage": ch = _rtf.charAt(++_currentParseIndex); + var code = ''; + while (/[0-9]/.test(ch)) { code += (ch + ''); ch = _rtf.charAt(++_currentParseIndex); } + _codePage = code; _currentParseIndex--; - break; + break; } + return ''; } - (void)_flushCurrentRun { var newOffset = 0; + if (_currentRun) { if ([_result length] == _currentRun._range.location) return; + _currentRun._range.length = [_result length] - _currentRun._range.location; newOffset = CPMaxRange(_currentRun._range); + var dict = [_currentRun dictionary]; + [_result setAttributes:dict range:_currentRun._range]; // flush previous run } + _currentRun = [_RTFAttribute new]; _currentRun._range = CPMakeRange(newOffset, 0); // open a new one } + - (CPString)_applyPropChange:sym parameter:param { - console.log("prop : " + sym[0] + " / param : " + param+ ' '); + //console.log("prop : " + sym[0] + " / param : " + param+ ' '); switch (sym[0]) { case "pard": [self _flushCurrentRun]; - break; + break; + case "b": // bold if (param === 0) { if (_currentRun && _currentRun.bold) [self _flushCurrentRun]; _currentRun.bold = NO - } else + } + else { if (_currentRun && !_currentRun.bold) [self _flushCurrentRun] _currentRun.bold = YES; } - break; + + break; + case "i": // italic if (param === 0) { if (_currentRun && _currentRun.italic) [self _flushCurrentRun]; _currentRun.italic = NO - } else + } + else { if (_currentRun && !_currentRun.italic) [self _flushCurrentRun] _currentRun.italic = YES; } - break; + + break; case "qc": // paragraph center [_currentRun.paragraph setAlignment:CPCenterTextAlignment]; - break; + break; + case "paperw": _paper.width = param; - break; + break; + case "paperh": _paper.height = param; - break; + break; } return ''; @@ -437,17 +468,19 @@ var kRgsymRtf = { { case "colortbl": _colorArray.push([CPColor blackColor]); - break; + break; + case "fonttbl": _parsingFontTable = YES; - break; + break; } + if (sym[4] == "destSkip") { console.log("Dest skip start : [" + sym[0] + "]"); _curState++; - } + return ''; } @@ -456,6 +489,7 @@ var kRgsymRtf = { if (kRgsymRtf[keyword] !== undefined) { var sym = kRgsymRtf[keyword]; + switch (sym[3]) { case kRTFParserType_prop: @@ -464,17 +498,21 @@ var kRgsymRtf = { param = sym[1]; } return [self _applyPropChange:sym parameter:param]; + case kRTFParserType_char: return [self _checkChar:sym parameter:param]; + case kRTFParserType_dest: return [self _changeDest:sym]; + case kRTFParserType_spec: return [self _parseSpec:sym parameter:param]; + default: return ''; - break; } - } else + } + else { switch (keyword) { @@ -482,50 +520,66 @@ var kRgsymRtf = { var oldColor = [_colorArray lastObject], green = [oldColor greenComponent], blue = [oldColor blueComponent]; + _colorArray.pop(); _colorArray.push([CPColor colorWithRed: parseInt(param) / 255 green:green blue:blue alpha:1.0]); - break; + break; + case "green": var oldColor = [_colorArray lastObject], red = [oldColor redComponent], blue = [oldColor blueComponent]; + _colorArray.pop(); _colorArray.push([CPColor colorWithRed: red green: parseInt(param) / 255 blue:blue alpha:1.0]); - break; + break; + case "blue": var oldColor = [_colorArray lastObject], green = [oldColor greenComponent], red = [oldColor redComponent]; + _colorArray.pop(); _colorArray.push([CPColor colorWithRed: red green:green blue:parseInt(param) / 255 alpha:1.0]); - break; + break; + case "cf": // change foreground color var fontIndex = parseInt(param) - 1; + if (_currentRun && fontIndex >= 0) _currentRun.fgColour = _colorArray[fontIndex]; - break; + + break; + case "f": // change font var fontIndex = parseInt(param); + if (_currentRun && fontIndex >= 0 && fontIndex < _fontArray.length) _currentRun.fontName = _fontArray[fontIndex]; - break; + break; + case "fs": // change font size _currentRun.fontSize = parseInt(param) / 2; - break; + break; + case "tx": // tabstop var location = parseInt(param) / 20; if (_currentRun) { [_currentRun addTab:location type:CPLeftTabStopType]; } - break; + + break; + default: console.log("skip : " + keyword + " param: " + param); } + if (_states.length > 0) _curState = 1; + return ''; } } @@ -537,16 +591,16 @@ var kRgsymRtf = { fNeg = false, keyword = '', param = ''; + _rtf = rtf; if (++_currentParseIndex >= len) return len; + ch = rtf.charAt(_currentParseIndex); if (!/[a-zA-Z]/.test(ch)) - { return [self _translateKeyword:ch parameter:nil fParameter:fParam]; - } while (/[a-zA-Z]/.test(ch)) { @@ -559,6 +613,7 @@ var kRgsymRtf = { fNeg = true; ch = rtf.charAt(++_currentParseIndex); } + fParam = true; while (/[0-9]/.test(ch)) @@ -566,6 +621,7 @@ var kRgsymRtf = { param += (ch + ''); ch = rtf.charAt(++_currentParseIndex); } + _currentParseIndex--; param = parseInt(param); @@ -574,6 +630,7 @@ var kRgsymRtf = { return [self _translateKeyword:keyword parameter:param fParameter:fParam]; } + - (void)_appendPlainString:(CPString) aString { [_result replaceCharactersInRange:CPMakeRange([_result length], 0) withString:aString]; @@ -587,6 +644,7 @@ var kRgsymRtf = { return ''; } _currentParseIndex = -1; + var len = rtf.length, tmp = '', ch = '', @@ -602,6 +660,7 @@ var kRgsymRtf = { [self _appendPlainString: String.fromCharCode(parseInt((hex), 16))]; hex = ''; } + switch (tmp) { case " ": @@ -613,17 +672,18 @@ var kRgsymRtf = { _freename += tmp; [self _appendPlainString:tmp]; } - break; + break; + case "{": if ([self pushState]) { console.log("push"); } - break; + break; + case "}": if ([self popState]) { - console.log("pop"); } if (_freename) @@ -637,17 +697,17 @@ var kRgsymRtf = { _freename = ""; } [self _flushCurrentRun] - break; + break; + case "\\": _freename = ''; ch = [self _parseKeyword:rtf length:len]; + if (!_hexreturn && ch.length == 0) - { lastchar = 1; - } else - { + else lastchar = 0; - } + if (_hexreturn) { if (ch.length > 0) @@ -655,7 +715,8 @@ var kRgsymRtf = { if (parseInt(ch, 16) & 0x80) { hex += ch.toUpperCase(); - } else + } + else { [self _appendPlainString: String.fromCharCode(parseInt((hex + ch), 16))]; hex = ''; @@ -664,42 +725,49 @@ var kRgsymRtf = { if (hex.length == 4) { var temp = parseInt(hex, 16); + if (hexTable && hexTable[hex.toUpperCase()] !== undefined) { temp = parseInt(hexTable[hex.toUpperCase()], 16); } + [self _appendPlainString: String.fromCharCode(temp)] hex = ''; } - } else + } + else { console.log("hex skipped"); } - _hexreturn = NO; - } else - if (ch !== undefined && _curState === 0) - { - [self _appendPlainString:ch]; - } - break; + _hexreturn = NO; + } + else if (ch !== undefined && _curState === 0) + { + [self _appendPlainString:ch]; + + } + + break; + case 0x0d: case 0x0a: case '\n': case '\r': - break; + break; + default: lastchar = 0; + if (_curState == 0) - { [self _appendPlainString:tmp]; - } else if (tmp !== ';') - { + else if (tmp !== ';') _freename += tmp; - } - break; + + break; } } + return _result; } diff --git a/AppKit/CPTextView/_CPRTFProducer.j b/AppKit/CPTextView/_CPRTFProducer.j index 3a4884cda..a86b4a7a4 100755 --- a/AppKit/CPTextView/_CPRTFProducer.j +++ b/AppKit/CPTextView/_CPRTFProducer.j @@ -1,12 +1,12 @@ /* RTFProducer.j - Serialize CPAttributedString to a RTF String + Serialize CPAttributedString to a RTF String Copyright (C) 2014 Daniel Boehringer This file is based on the RTFProducer from GNUStep (which i co-authored with Fred Kiefer in 1999) - + * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -20,15 +20,23 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ + */ @import @import "CPParagraphStyle.j" @import "CPColor.j" @import "CPGraphics.j" -@import "CPText.j" @import "CPFontManager.j" +@global CPFontAttributeName +@global CPForegroundColorAttributeName +@global CPBackgroundColorAttributeName +@global CPUnderlineStyleAttributeName +@global CPSuperscriptAttributeName +@global CPBaselineOffsetAttributeName +@global CPAttachmentAttributeName +@global CPLigatureAttributeName +@global CPKernAttributeName var PAPERSIZE = @"PaperSize", LEFTMARGIN = @"LeftMargin", @@ -95,8 +103,8 @@ function _points2twips(a) { return (a) * 20.0; } keyArray = [fontDict allKeys]; keyArray = [keyArray sortedArrayUsingSelector:@selector(compare:)]; - fontEnum = [keyArray objectEnumerator]; + while ((currFont = [fontEnum nextObject]) !== nil) { var fontFamily, @@ -118,6 +126,7 @@ function _points2twips(a) { return (a) * 20.0; } [fontDict objectForKey:currFont], fontFamily, currFont]; fontlistString += detail; } + return [CPString stringWithFormat:@"{\\fonttbl%@}\n", fontlistString]; } else @@ -139,14 +148,17 @@ function _points2twips(a) { return (a) * 20.0; } while ((next = [keyEnum nextObject]) != nil) { var cn = [colorDict objectForKey:next]; + [list insertObject:next atIndex:[cn intValue] - 1]; } result = [CPString stringWithString:@"{\\colortbl;"]; + for (i = 0; i < count; i++) { var color = [[list objectAtIndex:i] colorUsingColorSpaceName:CPCalibratedRGBColorSpace]; + result += [CPString stringWithFormat: @"\\red%d\\green%d\\blue%d;", ([color redComponent] * 255), @@ -155,6 +167,7 @@ function _points2twips(a) { return (a) * 20.0; } } result += @"}\n"; + return result; } else @@ -173,45 +186,55 @@ function _points2twips(a) { return (a) * 20.0; } result = [CPString string]; val = [docDict objectForKey:PAPERSIZE]; + if (val != nil) { var size = [val sizeValue]; + detail = [CPString stringWithFormat:@"\\paperw%d \\paperh%d", _points2twips(size.width), _points2twips(size.height)]; + result += detail; } num = [docDict objectForKey:LEFTMARGIN]; + if (num != nil) { var f = [num floatValue]; - detail = [CPString stringWithFormat:@"\\margl%d", - _points2twips(f)]; + + detail = [CPString stringWithFormat:@"\\margl%d", _points2twips(f)]; result+= detail; } + num = [docDict objectForKey:RIGHTMARGIN]; + if (num != nil) { var f = [num floatValue]; - detail = [CPString stringWithFormat:@"\\margr%d", - _points2twips(f)]; + + detail = [CPString stringWithFormat:@"\\margr%d", _points2twips(f)]; result += detail; } + num = [docDict objectForKey:TOPMARGIN]; + if (num != nil) { var f = [num floatValue]; - detail = [CPString stringWithFormat:@"\\margt%d", - _points2twips(f)]; + + detail = [CPString stringWithFormat:@"\\margt%d", _points2twips(f)]; result += detail; } + num = [docDict objectForKey:BUTTOMMARGIN]; + if (num != nil) { var f = [num floatValue]; - detail = [CPString stringWithFormat:@"\\margb%d", - _points2twips(f)]; + + detail = [CPString stringWithFormat:@"\\margb%d", _points2twips(f)]; result += detail; } @@ -263,9 +286,9 @@ function _points2twips(a) { return (a) * 20.0; } { cn = [colorDict count] + 1; - [colorDict setObject:[CPNumber numberWithInt:cn] - forKey:color]; + [colorDict setObject:[CPNumber numberWithInt:cn] forKey:color]; } + var cn = [num intValue]; return cn + 1; @@ -283,52 +306,56 @@ function _points2twips(a) { return (a) * 20.0; } { case CPRightTextAlignment: headerString += @"\\qr"; - break; + break; + case CPCenterTextAlignment: headerString += @"\\qc"; - break; + break; + case CPLeftTextAlignment: headerString += @"\\ql"; - break; + break; + case CPJustifiedTextAlignment: headerString += @"\\qj"; - break; + break; + default: headerString += @"\\ql"; - break; + break; } // write first line indent and left indent var twips = _points2twips([paraStyle firstLineHeadIndent]); + if (twips != 0.0) - { headerString += [CPString stringWithFormat:@"\\fi%d", twips]; - } + twips = _points2twips([paraStyle headIndent]); + if (twips != 0.0) - { headerString += [CPString stringWithFormat:@"\\li%d", twips]; - } + twips = _points2twips([paraStyle tailIndent]); + if (twips != 0.0) - { headerString += [CPString stringWithFormat:@"\\ri%d", twips]; - } + twips = _points2twips([paraStyle paragraphSpacing]); + if (twips != 0.0) - { headerString += [CPString stringWithFormat:@"\\sa%d", twips]; - } + twips = _points2twips([paraStyle minimumLineHeight]); + if (twips != 0.0) - { headerString += [CPString stringWithFormat:@"\\sl%d", twips]; - } + twips = _points2twips([paraStyle maximumLineHeight]); + if (twips != 0.0) - { headerString += [CPString stringWithFormat:@"\\sl-%d", twips]; - } + // tabs if (1) { @@ -383,11 +410,12 @@ function _points2twips(a) { return (a) * 20.0; } * analyze attributes of current run * * FIXME: All the character attributes should be output relative to the font - * attributes of the paragraph. So if the paragraph has underline on it should - * still be possible to switch it off for some characters, which currently is + * attributes of the paragraph. So if the paragraph has underline on it should + * still be possible to switch it off for some characters, which currently is * not possible. */ attribEnum = [attributes keyEnumerator]; + while ((currAttrib = [attribEnum nextObject]) != nil) { if ([currAttrib isEqualToString:CPFontAttributeName]) @@ -406,16 +434,14 @@ function _points2twips(a) { return (a) * 20.0; } /* * font name */ - if (currentFont == nil || - ![fontName isEqualToString:[currentFont familyName]]) + if (currentFont == nil || ![fontName isEqualToString:[currentFont familyName]]) { headerString += [self fontToken:fontName]; } /* * font size */ - if (currentFont == nil || - [font size] != [currentFont size]) + if (currentFont == nil || [font size] != [currentFont size]) { var points = [font size] * 2, pString; @@ -443,6 +469,7 @@ function _points2twips(a) { return (a) * 20.0; } else if ([currAttrib isEqualToString:CPForegroundColorAttributeName]) { var color = [attributes objectForKey:CPForegroundColorAttributeName]; + if (![color isEqual:fgColor]) { headerString += [CPString stringWithFormat:@"\\cf%d", [self numberForColor:color]]; @@ -452,6 +479,7 @@ function _points2twips(a) { return (a) * 20.0; } else if ([currAttrib isEqualToString:CPBackgroundColorAttributeName]) { var color = [attributes objectForKey:CPBackgroundColorAttributeName]; + if (![color isEqual:bgColor]) { headerString += [CPString stringWithFormat:@"\\cb%d", [self numberForColor:color]]; @@ -545,9 +573,8 @@ function _points2twips(a) { return (a) * 20.0; } var string = [text string], result = "", loc = 0, - length = [string length]; - - var currRange = CPMakeRange(loc, 0), + length = [string length], + currRange = CPMakeRange(loc, 0), completeRange = CPMakeRange(0, length), first = YES; @@ -566,9 +593,11 @@ function _points2twips(a) { return (a) * 20.0; } runString = [self runStringForString:substring attributes:attributes paragraphStart:YES]; + result += runString; first = NO; } + return result; }