mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-06 10:53:42 +00:00
Merge remote-tracking branch 'upstream/main' into Aristo3
This commit is contained in:
@@ -464,6 +464,22 @@ following:
|
||||
return _CPRealFontSize(_size);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the font size. Cocoa/AppKit compatibility alias for -size.
|
||||
*/
|
||||
- (float)pointSize
|
||||
{
|
||||
return [self size];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the font name. Cocoa/AppKit compatibility alias for -familyName.
|
||||
*/
|
||||
- (CPString)fontName
|
||||
{
|
||||
return [self familyName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the font as a CSS string
|
||||
*/
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
@import "CPFont.j"
|
||||
|
||||
@global _MakeRangeFromAbs
|
||||
|
||||
@global document
|
||||
@global CPBaselineOffsetAttributeName
|
||||
@global CPSuperscriptAttributeName
|
||||
|
||||
@class CPTextContainer
|
||||
@class CPTextView
|
||||
@@ -964,16 +965,11 @@ _oncontextmenuhandler = function () { return false; };
|
||||
if (frame)
|
||||
{
|
||||
var correctedRect = CGRectCreateCopy(frame);
|
||||
correctedRect.size.height -= frame._descent;
|
||||
correctedRect.origin.y -= frame._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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1225,7 +1221,7 @@ var _objectsInRange = function(aList, aRange)
|
||||
if (![attributes objectForKey:_CPAttachmentInvisible])
|
||||
{
|
||||
var view = [attributes objectForKey:_CPAttachmentView];
|
||||
var run = {_range:CPMakeRangeCopy(effectiveRange), color:nil, font:nil, elem:nil, string:nil, view:view, paragraphStyle:paragraphStyle};
|
||||
var run = {_range:CPMakeRangeCopy(effectiveRange), color:nil, font:nil, elem:nil, string:nil, view:view, paragraphStyle:paragraphStyle, underline:underline, baselineOffset:0.0};
|
||||
_runs.push(run);
|
||||
}
|
||||
}
|
||||
@@ -1235,6 +1231,34 @@ var _objectsInRange = function(aList, aRange)
|
||||
bgcolor = [attributes objectForKey:CPBackgroundColorAttributeName],
|
||||
font = [attributes objectForKey:CPFontAttributeName] || [textStorage font] || [CPFont systemFontOfSize:12.0];
|
||||
|
||||
var baselineOffset = [attributes objectForKey:CPBaselineOffsetAttributeName],
|
||||
superscript = [attributes objectForKey:CPSuperscriptAttributeName];
|
||||
|
||||
if (baselineOffset === nil || baselineOffset === undefined || typeof baselineOffset !== "number")
|
||||
baselineOffset = 0.0;
|
||||
|
||||
if (superscript === nil || superscript === undefined || typeof superscript !== "number")
|
||||
superscript = 0;
|
||||
|
||||
if (superscript !== 0)
|
||||
{
|
||||
var size = [font size],
|
||||
scaledSize = size * 0.65,
|
||||
fontName = [font familyName],
|
||||
isBold = [font isBold],
|
||||
isItalic = [font isItalic];
|
||||
|
||||
font = [CPFont _fontWithName:fontName size:scaledSize bold:isBold italic:isItalic];
|
||||
|
||||
if (baselineOffset === 0.0)
|
||||
{
|
||||
if (superscript > 0)
|
||||
baselineOffset = size * 0.35;
|
||||
else
|
||||
baselineOffset = -size * 0.15;
|
||||
}
|
||||
}
|
||||
|
||||
var currentLoc = effectiveRange.location,
|
||||
strLen = string.length,
|
||||
startIdx = 0;
|
||||
@@ -1254,7 +1278,9 @@ var _objectsInRange = function(aList, aRange)
|
||||
elem: nil,
|
||||
string: subString,
|
||||
bgcolor: bgcolor,
|
||||
paragraphStyle: paragraphStyle
|
||||
paragraphStyle: paragraphStyle,
|
||||
underline: underline,
|
||||
baselineOffset: baselineOffset
|
||||
};
|
||||
_runs.push(run);
|
||||
}
|
||||
@@ -1267,7 +1293,9 @@ var _objectsInRange = function(aList, aRange)
|
||||
elem: nil,
|
||||
string: nil,
|
||||
bgcolor: nil,
|
||||
paragraphStyle: paragraphStyle
|
||||
paragraphStyle: paragraphStyle,
|
||||
underline: underline,
|
||||
baselineOffset: 0.0
|
||||
};
|
||||
_runs.push(tabRun);
|
||||
|
||||
@@ -1286,7 +1314,9 @@ var _objectsInRange = function(aList, aRange)
|
||||
elem: nil,
|
||||
string: subString,
|
||||
bgcolor: bgcolor,
|
||||
paragraphStyle: paragraphStyle
|
||||
paragraphStyle: paragraphStyle,
|
||||
underline: underline,
|
||||
baselineOffset: baselineOffset
|
||||
};
|
||||
_runs.push(run);
|
||||
}
|
||||
@@ -1313,7 +1343,10 @@ var _objectsInRange = function(aList, aRange)
|
||||
{
|
||||
_glyphsFrames[i] = CGRectMake(origin.x, origin.y, someAdvancements[i].width, height);
|
||||
_glyphsFrames[i]._descent = someAdvancements[i].descent;
|
||||
_glyphsOffsets[i] = height - someAdvancements[i].height;
|
||||
|
||||
// Align the run's baseline with the common line baseline (_location.y)
|
||||
_glyphsOffsets[i] = _location.y - someAdvancements[i].height;
|
||||
|
||||
origin.x += someAdvancements[i].width;
|
||||
}
|
||||
}
|
||||
@@ -1458,7 +1491,10 @@ var _objectsInRange = function(aList, aRange)
|
||||
if (!_RectEqualToRectHorizontally(newLineFragment._fragmentRect, _fragmentRect))
|
||||
return NO;
|
||||
|
||||
if (newFragmentRuns[i].color !== oldFragmentRuns[i].color || newFragmentRuns[i].bgcolor !== oldFragmentRuns[i].bgcolor || newFragmentRuns[i].font !== oldFragmentRuns[i].font)
|
||||
if (newFragmentRuns[i].color !== oldFragmentRuns[i].color ||
|
||||
newFragmentRuns[i].bgcolor !== oldFragmentRuns[i].bgcolor ||
|
||||
newFragmentRuns[i].font !== oldFragmentRuns[i].font ||
|
||||
newFragmentRuns[i].baselineOffset !== oldFragmentRuns[i].baselineOffset)
|
||||
return NO;
|
||||
|
||||
var oldStyle = oldFragmentRuns[i].paragraphStyle || [CPParagraphStyle defaultParagraphStyle],
|
||||
|
||||
@@ -148,27 +148,58 @@ CPLineMovesUp = 4;
|
||||
|
||||
- (void)setWidthTracksTextView:(BOOL)flag
|
||||
{
|
||||
_widthTracksTextView = flag;
|
||||
[_textView setPostsFrameChangedNotifications:flag];
|
||||
if (_widthTracksTextView === flag)
|
||||
return;
|
||||
|
||||
if (flag && _textView)
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(textViewFrameChanged:)
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_textView];
|
||||
}
|
||||
else
|
||||
_widthTracksTextView = flag;
|
||||
[self _updateFrameObserver];
|
||||
}
|
||||
|
||||
// Controls whether the receiver adjusts the height of its bounding rectangle when its text view is resized.
|
||||
- (BOOL)heightTracksTextView
|
||||
{
|
||||
return _heightTracksTextView;
|
||||
}
|
||||
|
||||
- (void)setHeightTracksTextView:(BOOL)flag
|
||||
{
|
||||
if (_heightTracksTextView === flag)
|
||||
return;
|
||||
|
||||
_heightTracksTextView = flag;
|
||||
[self _updateFrameObserver];
|
||||
}
|
||||
|
||||
- (void)_updateFrameObserver
|
||||
{
|
||||
if (_textView)
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_textView];
|
||||
|
||||
var flag = _widthTracksTextView || _heightTracksTextView;
|
||||
[_textView setPostsFrameChangedNotifications:flag];
|
||||
|
||||
if (flag)
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(textViewFrameChanged:)
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_textView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)textViewFrameChanged:(CPNotification)aNotification
|
||||
{
|
||||
var newSize = CGSizeMake([_textView frame].size.width, _size.height);
|
||||
var newSize = CGSizeMake(_size.width, _size.height);
|
||||
|
||||
if (_widthTracksTextView)
|
||||
newSize.width = [_textView frame].size.width;
|
||||
|
||||
if (_heightTracksTextView)
|
||||
newSize.height = [_textView frame].size.height;
|
||||
|
||||
[self setContainerSize:newSize];
|
||||
}
|
||||
@@ -177,7 +208,9 @@ CPLineMovesUp = 4;
|
||||
{
|
||||
if (_textView)
|
||||
{
|
||||
[self setWidthTracksTextView:NO]; // We only support width
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_textView];
|
||||
[_textView setTextContainer:nil];
|
||||
}
|
||||
|
||||
@@ -185,7 +218,7 @@ CPLineMovesUp = 4;
|
||||
|
||||
if (_textView)
|
||||
{
|
||||
[self setWidthTracksTextView:_widthTracksTextView]; // We only support width
|
||||
[self _updateFrameObserver];
|
||||
[_textView setTextContainer:self];
|
||||
}
|
||||
|
||||
|
||||
@@ -1010,6 +1010,25 @@ Sets the selection to a range of characters in response to user action.
|
||||
if (doOverwrite && _placeholderString == nil && isNewSelection)
|
||||
[self setTypingAttributes:[_textStorage attributesAtIndex:CPMaxRange(range) effectiveRange:nil]];
|
||||
|
||||
// Update the shared CPColorPanel with the active selection color
|
||||
if ([self _isFirstResponder] && [_textStorage length] > 0)
|
||||
{
|
||||
var currentTextColor = [self textColor] || [CPColor blackColor];
|
||||
|
||||
if ([self isRichText])
|
||||
{
|
||||
var charIndex = _selectionRange.location;
|
||||
if (charIndex >= [_textStorage length])
|
||||
charIndex = MAX(0, charIndex - 1);
|
||||
|
||||
var attributes = [_textStorage attributesAtIndex:charIndex effectiveRange:nil];
|
||||
if ([attributes objectForKey:CPForegroundColorAttributeName])
|
||||
currentTextColor = [attributes objectForKey:CPForegroundColorAttributeName];
|
||||
}
|
||||
|
||||
[[CPColorPanel sharedColorPanel] setColor:currentTextColor];
|
||||
}
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeSelectionNotification object:self];
|
||||
}
|
||||
|
||||
@@ -1804,6 +1823,13 @@ Sets the selection to a range of characters in response to user action.
|
||||
// SYNCHRONIZE ACTIVE PARAGRAPH MARKERS ON TYPING ATTRIBUTES CHANGE
|
||||
[self updateRuler];
|
||||
|
||||
// Synchronize CPColorPanel if text view is active
|
||||
if ([self _isFirstResponder])
|
||||
{
|
||||
var currentTextColor = [_typingAttributes objectForKey:CPForegroundColorAttributeName] || [self textColor] || [CPColor blackColor];
|
||||
[[CPColorPanel sharedColorPanel] setColor:currentTextColor];
|
||||
}
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self];
|
||||
|
||||
// We always clear the saved selection range from the last mouse down event here.
|
||||
@@ -2316,17 +2342,15 @@ Sets the selection to a range of characters in response to user action.
|
||||
|
||||
var loc = (_selectionRange.location == numberOfGlyphs) ? _selectionRange.location - 1 : _selectionRange.location,
|
||||
caretOffset = [_layoutManager _characterOffsetAtLocation:loc],
|
||||
oldYPosition = CGRectGetMaxY(caretRect),
|
||||
caretDescend = [_layoutManager _descentAtLocation:loc];
|
||||
font = [_textStorage attribute:CPFontAttributeName atIndex:loc effectiveRange:nil] || [self font];
|
||||
|
||||
if (caretOffset > 0)
|
||||
{
|
||||
caretRect.origin.y += caretOffset;
|
||||
caretRect.size.height = oldYPosition - caretRect.origin.y;
|
||||
}
|
||||
|
||||
if (caretDescend < 0)
|
||||
caretRect.size.height -= caretDescend;
|
||||
// Set the caret height to match the size of the active font
|
||||
caretRect.size.height = [font size];
|
||||
|
||||
if (_selectionRange.location == numberOfGlyphs)
|
||||
caretRect.origin.x += caretRect.size.width;
|
||||
@@ -2335,7 +2359,7 @@ Sets the selection to a range of characters in response to user action.
|
||||
caretRect.origin.y += _textContainerOrigin.y;
|
||||
|
||||
caretRect.size.width = MAX(1.0, caretRect.size.width);
|
||||
caretRect.size.height = MAX(1.0, caretRect.size.height);
|
||||
caretRect.size.height = MAX(1.0, caretRect.size.height) + 2;
|
||||
|
||||
return caretRect;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
@import "CPTextStorage.j"
|
||||
@import "CPFont.j"
|
||||
|
||||
@global CPBaselineOffsetAttributeName
|
||||
@global CPSuperscriptAttributeName
|
||||
|
||||
// forward declare these classes for type matching
|
||||
@class CPLayoutManager
|
||||
@class CPTextContainer
|
||||
@@ -353,9 +356,41 @@ var CPSystemTypesetterFactory,
|
||||
if (!currentFont)
|
||||
currentFont = [_textStorage font] || [CPFont systemFontOfSize:12.0];
|
||||
|
||||
ascent = [currentFont ascender];
|
||||
descent = [currentFont descender];
|
||||
leading = (ascent - descent) * 0.2; // FAKE leading
|
||||
// Safely retrieve and validate CPBaselineOffsetAttributeName
|
||||
var baselineOffset = [_currentAttributes objectForKey:CPBaselineOffsetAttributeName];
|
||||
if (baselineOffset === nil || baselineOffset === undefined || typeof baselineOffset !== "number")
|
||||
baselineOffset = 0.0;
|
||||
|
||||
// Safely retrieve and validate CPSuperscriptAttributeName
|
||||
var superscript = [_currentAttributes objectForKey:CPSuperscriptAttributeName];
|
||||
if (superscript === nil || superscript === undefined || typeof superscript !== "number")
|
||||
superscript = 0;
|
||||
|
||||
if (superscript !== 0)
|
||||
{
|
||||
var size = [currentFont size],
|
||||
scaledSize = size * 0.65,
|
||||
fontName = [currentFont familyName],
|
||||
isBold = [currentFont isBold],
|
||||
isItalic = [currentFont isItalic];
|
||||
|
||||
currentFont = [CPFont _fontWithName:fontName size:scaledSize bold:isBold italic:isItalic];
|
||||
|
||||
if (baselineOffset === 0.0)
|
||||
{
|
||||
if (superscript > 0)
|
||||
baselineOffset = size * 0.35;
|
||||
else
|
||||
baselineOffset = -size * 0.15;
|
||||
}
|
||||
}
|
||||
|
||||
var fontAscent = [currentFont ascender] || 0.0,
|
||||
fontDescent = [currentFont descender] || 0.0;
|
||||
|
||||
ascent = fontAscent + baselineOffset;
|
||||
descent = fontDescent + baselineOffset;
|
||||
leading = (fontAscent - fontDescent) * 0.2; // FAKE leading
|
||||
|
||||
currentFontLineHeight = ascent - descent + leading;
|
||||
|
||||
@@ -368,11 +403,15 @@ var CPSystemTypesetterFactory,
|
||||
|
||||
}
|
||||
|
||||
if (currentFontLineHeight > _lineHeight)
|
||||
_lineHeight = currentFontLineHeight;
|
||||
// Clean bounds logic to prevent NaN and layout calculation overhead
|
||||
var currentAscent = (ascent === undefined || isNaN(ascent)) ? 0.0 : ascent,
|
||||
currentLineHeight = (currentFontLineHeight === undefined || isNaN(currentFontLineHeight)) ? 12.0 : currentFontLineHeight;
|
||||
|
||||
if (ascent > _lineBase)
|
||||
_lineBase = ascent;
|
||||
if (currentLineHeight > _lineHeight)
|
||||
_lineHeight = currentLineHeight;
|
||||
|
||||
if (currentAscent > _lineBase)
|
||||
_lineBase = currentAscent;
|
||||
|
||||
lineRange.length++;
|
||||
measuringRange.length++;
|
||||
@@ -546,7 +585,7 @@ var CPSystemTypesetterFactory,
|
||||
isNewline = NO;
|
||||
_lineFragments = [];
|
||||
_lineHeight = 0;
|
||||
_lineBase = ascent;
|
||||
_lineBase = 0;
|
||||
isStartOfPhysicalLine = YES;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
@global CPParagraphStyleAttributeName
|
||||
@global CPAttachmentAttributeName
|
||||
@global CPUnderlineStyleAttributeName
|
||||
@global CPBaselineOffsetAttributeName
|
||||
@global CPSuperscriptAttributeName
|
||||
|
||||
@global CPLeftTabStopType
|
||||
@global CPRightTabStopType
|
||||
@@ -72,6 +74,8 @@ var cp1252Map = {
|
||||
BOOL script;
|
||||
BOOL _tabChanged;
|
||||
CPTabStopType _nextTabType;
|
||||
int superscript;
|
||||
float baselineOffset;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -104,6 +108,8 @@ var cp1252Map = {
|
||||
mynew.ulColour = ulColour;
|
||||
mynew._tabChanged = _tabChanged;
|
||||
mynew._nextTabType = _nextTabType;
|
||||
mynew.superscript = superscript;
|
||||
mynew.baselineOffset = baselineOffset;
|
||||
|
||||
return mynew;
|
||||
}
|
||||
@@ -169,6 +175,8 @@ var cp1252Map = {
|
||||
underline = 0;
|
||||
strikethrough = 0;
|
||||
script = 0;
|
||||
superscript = 0;
|
||||
baselineOffset = 0.0;
|
||||
}
|
||||
|
||||
- (void)addTab:(float)location type:(CPTextTabType)type
|
||||
@@ -212,6 +220,12 @@ var cp1252Map = {
|
||||
if (underline)
|
||||
[ret setObject:[CPNumber numberWithInt:1] forKey:CPUnderlineStyleAttributeName];
|
||||
|
||||
if (superscript !== 0)
|
||||
[ret setObject:[CPNumber numberWithInt:superscript] forKey:CPSuperscriptAttributeName];
|
||||
|
||||
if (baselineOffset !== 0.0)
|
||||
[ret setObject:[CPNumber numberWithFloat:baselineOffset] forKey:CPBaselineOffsetAttributeName];
|
||||
|
||||
return ret;
|
||||
}
|
||||
@end
|
||||
@@ -226,6 +240,12 @@ var kRgsymRtf = {
|
||||
"b" : [ "b", 1, false, kRTFParserType_prop, "propBold"],
|
||||
"ul" : [ "ul", 1, false, kRTFParserType_prop, "propUnderline"],
|
||||
"i" : [ "i", 1, false, kRTFParserType_prop, "propItalic"],
|
||||
"super" : [ "super", 1, true, kRTFParserType_prop, "propSuper"],
|
||||
"sub" : [ "sub", 1, true, kRTFParserType_prop, "propSub"],
|
||||
"nosupersub" : [ "nosupersub",1, true, kRTFParserType_prop, "propNoSuperSub"],
|
||||
"up" : [ "up", 6, false, kRTFParserType_prop, "propUp"],
|
||||
"dn" : [ "dn", 6, false, kRTFParserType_prop, "propDn"],
|
||||
"plain" : [ "plain", 0, false, kRTFParserType_prop, "propPlain"],
|
||||
"pgnucltr" : [ "pgnucltr", "pgULtr", true, kRTFParserType_prop, "propPgnFormat"],
|
||||
"pgnlcltr" : [ "pgnlcltr", "pgLLtr", true, kRTFParserType_prop, "propPgnFormat"],
|
||||
"qc" : [ "qc", "justC", true, kRTFParserType_prop, "propJust"],
|
||||
@@ -314,6 +334,7 @@ var kRgsymRtf = {
|
||||
CPArray _fontArray;
|
||||
CPString _freename;
|
||||
BOOL _parsingFontTable;
|
||||
BOOL _keywordIsControlWord;
|
||||
|
||||
// Table parsing state
|
||||
BOOL _inTableActive;
|
||||
@@ -333,6 +354,7 @@ var kRgsymRtf = {
|
||||
_states = [];
|
||||
_currentParseIndex = 0;
|
||||
_hexreturn = NO;
|
||||
_keywordIsControlWord = NO;
|
||||
_result = [CPAttributedString new];
|
||||
_colorArray = [];
|
||||
_fontArray = ['Arial']; // FIXME: should be name of system font
|
||||
@@ -621,6 +643,36 @@ var kRgsymRtf = {
|
||||
}
|
||||
break;
|
||||
|
||||
case "super":
|
||||
[self _flushCurrentRun];
|
||||
_currentRun.superscript = 1;
|
||||
break;
|
||||
|
||||
case "sub":
|
||||
[self _flushCurrentRun];
|
||||
_currentRun.superscript = -1;
|
||||
break;
|
||||
|
||||
case "nosupersub":
|
||||
[self _flushCurrentRun];
|
||||
_currentRun.superscript = 0;
|
||||
break;
|
||||
|
||||
case "up":
|
||||
[self _flushCurrentRun];
|
||||
_currentRun.baselineOffset = parseFloat(param) / 2.0;
|
||||
break;
|
||||
|
||||
case "dn":
|
||||
[self _flushCurrentRun];
|
||||
_currentRun.baselineOffset = -parseFloat(param) / 2.0;
|
||||
break;
|
||||
|
||||
case "plain":
|
||||
[self _flushCurrentRun];
|
||||
[_currentRun resetFont];
|
||||
break;
|
||||
|
||||
case "qc": // paragraph center
|
||||
[_currentRun.paragraph setAlignment:CPCenterTextAlignment];
|
||||
break;
|
||||
@@ -745,10 +797,15 @@ var kRgsymRtf = {
|
||||
[self _flushCurrentRun];
|
||||
var fontIndex = parseInt(param) - 1;
|
||||
|
||||
if (_currentRun && fontIndex >= 0)
|
||||
_currentRun.fgColour = _colorArray[fontIndex];
|
||||
if (_currentRun)
|
||||
{
|
||||
if (fontIndex >= 0 && fontIndex < _colorArray.length)
|
||||
_currentRun.fgColour = _colorArray[fontIndex];
|
||||
else
|
||||
_currentRun.fgColour = nil;
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
case "cb": // change background color
|
||||
case "highlight":
|
||||
@@ -839,7 +896,12 @@ var kRgsymRtf = {
|
||||
ch = rtf.charAt(_currentParseIndex);
|
||||
|
||||
if (!/[a-zA-Z]/.test(ch))
|
||||
{
|
||||
_keywordIsControlWord = NO;
|
||||
return [self _translateKeyword:ch parameter:nil fParameter:fParam];
|
||||
}
|
||||
|
||||
_keywordIsControlWord = YES;
|
||||
|
||||
while (new RegExp("[a-zA-Z]").test(ch))
|
||||
{
|
||||
@@ -922,12 +984,17 @@ var kRgsymRtf = {
|
||||
else
|
||||
{
|
||||
_freename += tmp;
|
||||
[self _appendPlainString:tmp];
|
||||
// Only append literal spaces to the document if we are in the active body state
|
||||
if (_curState == 0)
|
||||
{
|
||||
[self _appendPlainString:tmp];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "{":
|
||||
lastchar = 0;
|
||||
if (_waitingForNextRow)
|
||||
[self _flushTableIfAny];
|
||||
|
||||
@@ -936,6 +1003,7 @@ var kRgsymRtf = {
|
||||
break;
|
||||
|
||||
case "}":
|
||||
lastchar = 0;
|
||||
if (_waitingForNextRow)
|
||||
[self _flushTableIfAny];
|
||||
|
||||
@@ -959,14 +1027,15 @@ var kRgsymRtf = {
|
||||
_freename = '';
|
||||
ch = [self _parseKeyword:rtf length:len];
|
||||
|
||||
if (!_hexreturn && ch.length == 0)
|
||||
if (!_hexreturn && _keywordIsControlWord)
|
||||
lastchar = 1;
|
||||
else
|
||||
lastchar = 0;
|
||||
|
||||
if (_hexreturn)
|
||||
{
|
||||
if (ch.length > 0)
|
||||
// Only append decoded characters if we are in the active body state
|
||||
if (ch.length > 0 && _curState === 0)
|
||||
{
|
||||
var byteVal = parseInt(ch, 16);
|
||||
var unicodeVal = byteVal;
|
||||
@@ -990,6 +1059,7 @@ var kRgsymRtf = {
|
||||
case 0x0a:
|
||||
case '\n':
|
||||
case '\r':
|
||||
lastchar = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -542,6 +542,17 @@ function _points2twips(a) { return (a) * 20.0; }
|
||||
cellText = "";
|
||||
}
|
||||
|
||||
// Safely extract text representation from CPAttributedString / CPTextStorage if present
|
||||
if (cellText && typeof cellText === "object") {
|
||||
if (typeof cellText.string === "function") {
|
||||
cellText = [cellText string];
|
||||
} else if (cellText._string !== undefined) {
|
||||
cellText = cellText._string;
|
||||
} else if (cellText.string !== undefined) {
|
||||
cellText = cellText.string;
|
||||
}
|
||||
}
|
||||
|
||||
cellText = String(cellText);
|
||||
cellText = cellText.replace(/\\/g, '\\\\');
|
||||
cellText = cellText.replace(/{/g, '\\{');
|
||||
@@ -646,28 +657,29 @@ function _points2twips(a) { return (a) * 20.0; }
|
||||
else if ([currAttrib isEqualToString:CPUnderlineStyleAttributeName])
|
||||
{
|
||||
headerString += @"\\ul";
|
||||
trailerString += @"\\ulnone";
|
||||
trailerString += @"\\ulnone "; // trailing space important!
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPSuperscriptAttributeName])
|
||||
{
|
||||
var value = [attributes objectForKey:CPSuperscriptAttributeName],
|
||||
svalue = [value intValue] * 6;
|
||||
ivalue = [value intValue];
|
||||
|
||||
if (svalue > 0)
|
||||
if (ivalue > 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\up%d", svalue];
|
||||
trailerString += @"\\up0";
|
||||
headerString += @"\\super";
|
||||
trailerString += @"\\nosupersub "; // trailing space important!
|
||||
}
|
||||
else if (svalue < 0)
|
||||
else if (ivalue < 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\dn-%d", svalue];
|
||||
trailerString += @"\\dn0";
|
||||
headerString += @"\\sub";
|
||||
trailerString += @"\\nosupersub "; // trailing space important!
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPBaselineOffsetAttributeName])
|
||||
{
|
||||
var value = [attributes objectForKey:CPBaselineOffsetAttributeName],
|
||||
svalue = [value floatValue] * 2;
|
||||
fvalue = [value floatValue],
|
||||
svalue = Math.round(fvalue * 2.0); // Convert standard points to RTF half-points
|
||||
|
||||
if (svalue > 0)
|
||||
{
|
||||
@@ -676,7 +688,8 @@ function _points2twips(a) { return (a) * 20.0; }
|
||||
}
|
||||
else if (svalue < 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\dn-%d", svalue];
|
||||
// Correct negative formatting using safe positive boundary
|
||||
headerString += [CPString stringWithFormat:@"\\dn%d", Math.abs(svalue)];
|
||||
trailerString += @"\\dn0";
|
||||
}
|
||||
}
|
||||
@@ -713,7 +726,7 @@ function _points2twips(a) { return (a) * 20.0; }
|
||||
var nobraces;
|
||||
|
||||
if ([headerString length])
|
||||
nobraces = [CPString stringWithFormat:@"%@ %@}", headerString, substring];
|
||||
nobraces = [CPString stringWithFormat:@"%@ %@", headerString, substring];
|
||||
else
|
||||
nobraces = substring;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/CPTextView.j>
|
||||
@import <AppKit/CPFontPanel.j>
|
||||
@import <AppKit/CPColorPanel.j>
|
||||
@import <AppKit/CPRulerView.j>
|
||||
@import <AppKit/CPSplitView.j>
|
||||
@import <AppKit/CPButton.j>
|
||||
@@ -17,6 +18,9 @@
|
||||
@import <AppKit/CPTextStorage.j>
|
||||
@import <AppKit/_CPTableTextAttachment.j>
|
||||
|
||||
@global CPBaselineOffsetAttributeName
|
||||
@global CPSuperscriptAttributeName
|
||||
|
||||
@implementation AppController : CPObject
|
||||
{
|
||||
CPTextView _textView;
|
||||
@@ -52,6 +56,11 @@
|
||||
[[CPFontManager sharedFontManager] orderFrontFontPanel:self];
|
||||
}
|
||||
|
||||
- (void)orderFrontColorPanel:(id)sender
|
||||
{
|
||||
[[CPColorPanel sharedColorPanel] orderFront:self];
|
||||
}
|
||||
|
||||
- (void)toggleRuler:(id)sender
|
||||
{
|
||||
[_scrollView setRulersVisible:![_scrollView rulersVisible]];
|
||||
@@ -77,6 +86,82 @@
|
||||
[_textView alignJustified:self];
|
||||
}
|
||||
|
||||
- (void)makeSuperscript:(id)sender
|
||||
{
|
||||
var range = [_textView selectedRange];
|
||||
if (range.length > 0)
|
||||
{
|
||||
var textStorage = [_textView textStorage];
|
||||
[textStorage beginEditing];
|
||||
[textStorage removeAttribute:CPBaselineOffsetAttributeName range:range];
|
||||
[textStorage addAttribute:CPSuperscriptAttributeName value:1 range:range];
|
||||
[textStorage endEditing];
|
||||
[_textView setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)makeSubscript:(id)sender
|
||||
{
|
||||
var range = [_textView selectedRange];
|
||||
if (range.length > 0)
|
||||
{
|
||||
var textStorage = [_textView textStorage];
|
||||
[textStorage beginEditing];
|
||||
[textStorage removeAttribute:CPBaselineOffsetAttributeName range:range];
|
||||
[textStorage addAttribute:CPSuperscriptAttributeName value:-1 range:range];
|
||||
[textStorage endEditing];
|
||||
[_textView setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)raiseBaseline:(id)sender
|
||||
{
|
||||
var range = [_textView selectedRange];
|
||||
if (range.length > 0)
|
||||
{
|
||||
var textStorage = [_textView textStorage];
|
||||
[textStorage beginEditing];
|
||||
|
||||
var currentOffset = [textStorage attribute:CPBaselineOffsetAttributeName atIndex:range.location effectiveRange:nil] || 0.0;
|
||||
var newOffset = currentOffset + 2.0;
|
||||
|
||||
[textStorage addAttribute:CPBaselineOffsetAttributeName value:newOffset range:range];
|
||||
[textStorage endEditing];
|
||||
[_textView setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)lowerBaseline:(id)sender
|
||||
{
|
||||
var range = [_textView selectedRange];
|
||||
if (range.length > 0)
|
||||
{
|
||||
var textStorage = [_textView textStorage];
|
||||
[textStorage beginEditing];
|
||||
|
||||
var currentOffset = [textStorage attribute:CPBaselineOffsetAttributeName atIndex:range.location effectiveRange:nil] || 0.0;
|
||||
var newOffset = currentOffset - 2.0;
|
||||
|
||||
[textStorage addAttribute:CPBaselineOffsetAttributeName value:newOffset range:range];
|
||||
[textStorage endEditing];
|
||||
[_textView setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resetBaseline:(id)sender
|
||||
{
|
||||
var range = [_textView selectedRange];
|
||||
if (range.length > 0)
|
||||
{
|
||||
var textStorage = [_textView textStorage];
|
||||
[textStorage beginEditing];
|
||||
[textStorage removeAttribute:CPBaselineOffsetAttributeName range:range];
|
||||
[textStorage removeAttribute:CPSuperscriptAttributeName range:range];
|
||||
[textStorage endEditing];
|
||||
[_textView setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)insertAttachment:(id)sender
|
||||
{
|
||||
// Insert modern spinner image attachment
|
||||
@@ -215,6 +300,48 @@
|
||||
[alignJustifyBtn setTarget:self];
|
||||
[alignJustifyBtn setAction:@selector(alignJustified:)];
|
||||
[toolbarView addSubview:alignJustifyBtn];
|
||||
currentX += 80;
|
||||
|
||||
// Baseline & Script Testing Group
|
||||
var labelBaseline = [[CPTextField alloc] initWithFrame:CGRectMake(currentX, 22, 85, 20)];
|
||||
[labelBaseline setStringValue:@"Baseline:"];
|
||||
[labelBaseline setFont:[CPFont systemFontOfSize:12]];
|
||||
[toolbarView addSubview:labelBaseline];
|
||||
currentX += 85;
|
||||
|
||||
var superBtn = [[CPButton alloc] initWithFrame:CGRectMake(currentX, 15, 45, 30)];
|
||||
[superBtn setTitle:@"x²"];
|
||||
[superBtn setTarget:self];
|
||||
[superBtn setAction:@selector(makeSuperscript:)];
|
||||
[toolbarView addSubview:superBtn];
|
||||
currentX += 50;
|
||||
|
||||
var subBtn = [[CPButton alloc] initWithFrame:CGRectMake(currentX, 15, 45, 30)];
|
||||
[subBtn setTitle:@"x₂"];
|
||||
[subBtn setTarget:self];
|
||||
[subBtn setAction:@selector(makeSubscript:)];
|
||||
[toolbarView addSubview:subBtn];
|
||||
currentX += 50;
|
||||
|
||||
var raiseBtn = [[CPButton alloc] initWithFrame:CGRectMake(currentX, 15, 50, 30)];
|
||||
[raiseBtn setTitle:@"Base+"];
|
||||
[raiseBtn setTarget:self];
|
||||
[raiseBtn setAction:@selector(raiseBaseline:)];
|
||||
[toolbarView addSubview:raiseBtn];
|
||||
currentX += 55;
|
||||
|
||||
var lowerBtn = [[CPButton alloc] initWithFrame:CGRectMake(currentX, 15, 50, 30)];
|
||||
[lowerBtn setTitle:@"Base-"];
|
||||
[lowerBtn setTarget:self];
|
||||
[lowerBtn setAction:@selector(lowerBaseline:)];
|
||||
[toolbarView addSubview:lowerBtn];
|
||||
currentX += 55;
|
||||
|
||||
var normalBtn = [[CPButton alloc] initWithFrame:CGRectMake(currentX, 15, 60, 30)];
|
||||
[normalBtn setTitle:@"Normal"];
|
||||
[normalBtn setTarget:self];
|
||||
[normalBtn setAction:@selector(resetBaseline:)];
|
||||
[toolbarView addSubview:normalBtn];
|
||||
|
||||
// Default return key target test
|
||||
var returnButton = [[CPButton alloc] initWithFrame:CGRectMake(CGRectGetWidth([contentView bounds]) - 270, 15, 250, 30)];
|
||||
@@ -299,12 +426,20 @@
|
||||
var formatMenu = [[CPMenu alloc] initWithTitle:@"Format Menu"];
|
||||
|
||||
[formatMenu addItemWithTitle:@"Font panel" action:@selector(orderFrontFontPanel:) keyEquivalent:@"t"];
|
||||
[formatMenu addItemWithTitle:@"Color panel" action:@selector(orderFrontColorPanel:) keyEquivalent:@"C"];
|
||||
[formatMenu addItem:[CPMenuItem separatorItem]];
|
||||
// Styles
|
||||
[formatMenu addItemWithTitle:@"Bold" action:@selector(bold:) keyEquivalent:@"b"];
|
||||
[formatMenu addItemWithTitle:@"Italic" action:@selector(italic:) keyEquivalent:@"i"];
|
||||
[formatMenu addItemWithTitle:@"Underline" action:@selector(underline:) keyEquivalent:@"u"];
|
||||
[formatMenu addItem:[CPMenuItem separatorItem]];
|
||||
// Baseline & Scripts
|
||||
[formatMenu addItemWithTitle:@"Superscript" action:@selector(makeSuperscript:) keyEquivalent:@"="];
|
||||
[formatMenu addItemWithTitle:@"Subscript" action:@selector(makeSubscript:) keyEquivalent:@"-"];
|
||||
[formatMenu addItemWithTitle:@"Raise Baseline" action:@selector(raiseBaseline:) keyEquivalent:@"+"];
|
||||
[formatMenu addItemWithTitle:@"Lower Baseline" action:@selector(lowerBaseline:) keyEquivalent:@"_"];
|
||||
[formatMenu addItemWithTitle:@"Reset Baseline" action:@selector(resetBaseline:) keyEquivalent:@"0"];
|
||||
[formatMenu addItem:[CPMenuItem separatorItem]];
|
||||
// Alignment
|
||||
[formatMenu addItemWithTitle:@"Align Left" action:@selector(alignLeft:) keyEquivalent:@"{"];
|
||||
[formatMenu addItemWithTitle:@"Center" action:@selector(alignCenter:) keyEquivalent:@"|"];
|
||||
@@ -327,6 +462,51 @@
|
||||
attributes:[CPDictionary dictionaryWithObjects:[centeredParagraph, [CPFont boldFontWithName:@"Arial" size:18], elegantForeground, elegantBackground]
|
||||
forKeys:[CPParagraphStyleAttributeName, CPFontAttributeName, CPForegroundColorAttributeName, CPBackgroundColorAttributeName]]]];
|
||||
|
||||
// VISUAL TEST CASES: Baseline, Superscript, and Subscript Features
|
||||
[_textView insertText:@"\n"];
|
||||
var sectionHeaderColor = [CPColor colorWithRed:0.5 green:0.25 blue:0.1 alpha:1.0];
|
||||
[_textView insertText:[[CPAttributedString alloc] initWithString:@"Baseline Shift & Superscript/Subscript Showcase\n"
|
||||
attributes:[CPDictionary dictionaryWithObjects:[[CPFont boldFontWithName:@"Arial" size:16], sectionHeaderColor]
|
||||
forKeys:[CPFontAttributeName, CPForegroundColorAttributeName]]]];
|
||||
|
||||
var normalFont = [CPFont systemFontOfSize:14.0];
|
||||
|
||||
// Test Case: E = mc² (Superscript)
|
||||
var formulaEnergy = [[CPAttributedString alloc] initWithString:@" • Energy mass equivalence: E = mc" attributes:@{ CPFontAttributeName: normalFont }];
|
||||
var scriptTwo = [[CPAttributedString alloc] initWithString:@"2" attributes:@{ CPFontAttributeName: normalFont, CPSuperscriptAttributeName: 1 }];
|
||||
[_textView insertText:formulaEnergy];
|
||||
[_textView insertText:scriptTwo];
|
||||
[_textView insertText:@"\n"];
|
||||
|
||||
// Test Case: H₂O (Subscript)
|
||||
var formulaWater = [[CPAttributedString alloc] initWithString:@" • Chemical formula: H" attributes:@{ CPFontAttributeName: normalFont }];
|
||||
var scriptSubTwo = [[CPAttributedString alloc] initWithString:@"2" attributes:@{ CPFontAttributeName: normalFont, CPSuperscriptAttributeName: -1 }];
|
||||
var formulaWaterEnd = [[CPAttributedString alloc] initWithString:@"O\n" attributes:@{ CPFontAttributeName: normalFont }];
|
||||
[_textView insertText:formulaWater];
|
||||
[_textView insertText:scriptSubTwo];
|
||||
[_textView insertText:formulaWaterEnd];
|
||||
|
||||
// Test Case: Ordinals
|
||||
var ordText = [[CPAttributedString alloc] initWithString:@" • Ordinals: 1" attributes:@{ CPFontAttributeName: normalFont }];
|
||||
var st = [[CPAttributedString alloc] initWithString:@"st" attributes:@{ CPFontAttributeName: normalFont, CPSuperscriptAttributeName: 1 }];
|
||||
var rdText = [[CPAttributedString alloc] initWithString:@", 3" attributes:@{ CPFontAttributeName: normalFont }];
|
||||
var rd = [[CPAttributedString alloc] initWithString:@"rd" attributes:@{ CPFontAttributeName: normalFont, CPSuperscriptAttributeName: 1 }];
|
||||
[_textView insertText:ordText];
|
||||
[_textView insertText:st];
|
||||
[_textView insertText:rdText];
|
||||
[_textView insertText:rd];
|
||||
[_textView insertText:@"\n"];
|
||||
|
||||
// Test Case: Custom Baseline Offsets
|
||||
var offsetLead = [[CPAttributedString alloc] initWithString:@" • Custom Offsets: " attributes:@{ CPFontAttributeName: normalFont }];
|
||||
var offsetUp = [[CPAttributedString alloc] initWithString:@"Raised " attributes:@{ CPFontAttributeName: normalFont, CPBaselineOffsetAttributeName: 4.0 }];
|
||||
var offsetDown = [[CPAttributedString alloc] initWithString:@"Lowered " attributes:@{ CPFontAttributeName: normalFont, CPBaselineOffsetAttributeName: -4.0 }];
|
||||
var offsetNormal = [[CPAttributedString alloc] initWithString:@"Standard\n" attributes:@{ CPFontAttributeName: normalFont }];
|
||||
[_textView insertText:offsetLead];
|
||||
[_textView insertText:offsetUp];
|
||||
[_textView insertText:offsetDown];
|
||||
[_textView insertText:offsetNormal];
|
||||
|
||||
// Highlighted Heading - Pine & Sage Green tones
|
||||
[_textView insertText:@"\n"];
|
||||
var showcaseForeground = [CPColor colorWithRed:0.15 green:0.25 blue:0.15 alpha:1.0]; // Forest Green
|
||||
|
||||
Reference in New Issue
Block a user