mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-25 17:45:36 +00:00
AppKit: Add Copy Style and Paste Style to CPTextView
CPTextView lacked implementations for copyFont: and pasteFont: (declared as abstract methods in CPText), causing standard Copy Style and Paste Style menu items to trigger exceptions when invoked. * AppKit/CPTextView/CPTextView.j: - Implement copyFont: to copy character attributes (excluding paragraph style, matching Cocoa) of the initial selection character or current typing attributes to the font pasteboard. - Implement pasteFont: to apply copied character attributes to the selection or typing attributes with undo support, preserving individual paragraph styles. - Update menu validation logic to enable Copy Style in rich text and Paste Style in editable rich text when the font pasteboard contains valid styling. * Tests/AppKit/CPTextViewTest.j: - Add unit tests covering style copying and pasting with and without active selections. - Add test coverage for menu validation. * Tests/Manual/TextEditor/WORKLIST.md: - Record Copy Style and Paste Style as tested. Copy Style and Paste Style now function consistently with Cocoa.
This commit is contained in:
1 parent
9d1504704f
commit
178a7d2aec
3 files changed
+171
-3
No files matched your search
@@ -721,8 +721,9 @@ var kDelegateRespondsTo_textShouldBeginEditing
|
||||
For \c -toggleRuler:, the item's title becomes "Hide Ruler" or "Show Ruler" to reflect the
|
||||
ruler's visibility, and the item is enabled only when the text view uses a ruler and lies
|
||||
in a scroll view. \c -copyRuler: is enabled in rich text, and \c -pasteRuler: in editable
|
||||
rich text when the ruler pasteboard holds a ruler. The superscript, subscript and baseline
|
||||
actions are enabled in editable rich text. Every other item is enabled.
|
||||
rich text when the ruler pasteboard holds a ruler. \c -copyFont: is enabled in rich text, and
|
||||
\c -pasteFont: in editable rich text when the font pasteboard holds a style. The superscript,
|
||||
subscript and baseline actions are enabled in editable rich text. Every other item is enabled.
|
||||
@param anItem the item to validate
|
||||
@return \c YES if the item should be enabled
|
||||
*/
|
||||
@@ -748,6 +749,13 @@ var kDelegateRespondsTo_textShouldBeginEditing
|
||||
if (action === @selector(pasteRuler:))
|
||||
return [self isRichText] && [self isEditable] && !![[CPPasteboard pasteboardWithName:CPRulerPboard] availableTypeFromArray:[CPRulerPboardType]];
|
||||
|
||||
// As in Cocoa, styles can be copied and pasted only in rich text.
|
||||
if (action === @selector(copyFont:))
|
||||
return [self isRichText];
|
||||
|
||||
if (action === @selector(pasteFont:))
|
||||
return [self isRichText] && [self isEditable] && !![[CPPasteboard pasteboardWithName:CPFontPboard] availableTypeFromArray:[CPFontPboardType]];
|
||||
|
||||
if (action === @selector(toggleRuler:))
|
||||
{
|
||||
if ([anItem respondsToSelector:@selector(setTitle:)])
|
||||
@@ -2080,6 +2088,104 @@ Sets the selection to a range of characters in response to user action.
|
||||
[[CPFontManager sharedFontManager] addFontTrait:CPItalicFontMask];
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
// MARK: Style methods
|
||||
|
||||
/*!
|
||||
Copies the character attributes at the start of the selection, or the typing attributes
|
||||
when nothing is selected, to the font pasteboard, so that Paste Style can apply them to
|
||||
other text. The attributes include the font, colours, underline and baseline. As in Cocoa,
|
||||
the paragraph style is not included: Copy Ruler copies it.
|
||||
@param aSender the object requesting this
|
||||
*/
|
||||
- (@action)copyFont:(id)aSender
|
||||
{
|
||||
var attributes = _selectionRange.length ? [_textStorage attributesAtIndex:_selectionRange.location effectiveRange:nil] : _typingAttributes,
|
||||
pasteboard = [CPPasteboard pasteboardWithName:CPFontPboard];
|
||||
|
||||
[pasteboard declareTypes:[CPFontPboardType] owner:nil];
|
||||
[pasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self _characterStyleFromAttributes:attributes]] forType:CPFontPboardType];
|
||||
}
|
||||
|
||||
/*!
|
||||
Applies the character attributes on the font pasteboard to the selected text, or to the
|
||||
text typed next when nothing is selected. They replace the text's own character
|
||||
attributes. Each paragraph keeps its paragraph style.
|
||||
@param aSender the object requesting this
|
||||
*/
|
||||
- (@action)pasteFont:(id)aSender
|
||||
{
|
||||
var data = [[CPPasteboard pasteboardWithName:CPFontPboard] dataForType:CPFontPboardType];
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
var characterStyle = [CPKeyedUnarchiver unarchiveObjectWithData:data],
|
||||
range = CPMakeRangeCopy(_selectionRange);
|
||||
|
||||
if (!range.length)
|
||||
{
|
||||
var typingAttributes = [characterStyle mutableCopy],
|
||||
paragraphStyle = [_typingAttributes objectForKey:CPParagraphStyleAttributeName];
|
||||
|
||||
if (paragraphStyle)
|
||||
[typingAttributes setObject:paragraphStyle forKey:CPParagraphStyleAttributeName];
|
||||
|
||||
[self setTypingAttributes:typingAttributes];
|
||||
return;
|
||||
}
|
||||
|
||||
if (![self _beginCharacterAttributeChangeInRange:range])
|
||||
return;
|
||||
|
||||
// Each run keeps the attributes that are not part of a style: its paragraph style and, for an
|
||||
// attachment character, the attachment.
|
||||
var location = range.location,
|
||||
end = CPMaxRange(range);
|
||||
|
||||
while (location < end)
|
||||
{
|
||||
var effectiveRange = CPMakeRange(0, 0),
|
||||
runAttributes = [_textStorage attributesAtIndex:location effectiveRange:effectiveRange],
|
||||
runRange = CPIntersectionRange(effectiveRange, range),
|
||||
newAttributes = [characterStyle mutableCopy],
|
||||
keptKeys = [CPParagraphStyleAttributeName, CPAttachmentAttributeName, _CPAttachmentView, _CPAttachmentInvisible];
|
||||
|
||||
if (!runRange.length)
|
||||
runRange = CPMakeRange(location, end - location);
|
||||
|
||||
for (var i = 0; i < keptKeys.length; i++)
|
||||
{
|
||||
var value = [runAttributes objectForKey:keptKeys[i]];
|
||||
|
||||
if (value)
|
||||
[newAttributes setObject:value forKey:keptKeys[i]];
|
||||
}
|
||||
|
||||
[_textStorage setAttributes:newAttributes range:runRange];
|
||||
|
||||
location = CPMaxRange(runRange);
|
||||
}
|
||||
|
||||
[self _endCharacterAttributeChangeInRange:range];
|
||||
}
|
||||
|
||||
/* @ignore
|
||||
Returns a copy of someAttributes without the attributes that are not part of a character
|
||||
style: the paragraph style and the attachment.
|
||||
*/
|
||||
- (CPDictionary)_characterStyleFromAttributes:(CPDictionary)someAttributes
|
||||
{
|
||||
var characterStyle = [someAttributes mutableCopy] || [CPDictionary dictionary];
|
||||
|
||||
[characterStyle removeObjectForKey:CPParagraphStyleAttributeName];
|
||||
[characterStyle removeObjectForKey:CPAttachmentAttributeName];
|
||||
[characterStyle removeObjectForKey:_CPAttachmentView];
|
||||
[characterStyle removeObjectForKey:_CPAttachmentInvisible];
|
||||
|
||||
return characterStyle;
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
// MARK: Baseline methods
|
||||
|
||||
|
||||
Reference in new issue
Block a user