Improvement of attributed string loading and attachment display in text

view.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@27705 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Fred Kiefer 2009-01-27 19:16:04 +00:00
parent 7831bedb6d
commit ddf1b1a269
6 changed files with 324 additions and 243 deletions

View file

@ -1,3 +1,15 @@
2009-01-27 Fred Kiefer <FredKiefer@gmx.de>
* Headers/AppKit/NSAttributedString.h: Add missing MacOSX methods.
* Source/NSAttributedString.m: Rewrote data loading and add
setting of NSError.
* Headers/AppKit/NSLayoutManager.h (-attachmentSizeForGlyphAtIndex:,
showAttachmentCell:inRect:characterIndex:): Declare these missing
methods.
* Source/NSLayoutManager.m: Implement and use them.
* Source/NSTextView.m (-dragSelectionWithEvent:...): Use layout
manager attachment methods.
2009-01-26 Fred Kiefer <FredKiefer@gmx.de>
* Source/NSAttributedString.m (-dataFromRange:...,

View file

@ -219,6 +219,10 @@ enum
+ (NSArray *) textUnfilteredFileTypes;
+ (NSArray *) textUnfilteredPasteboardTypes;
#endif
#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST)
+ (NSArray *) textTypes;
+ (NSArray *) textUnfilteredTypes;
#endif
#if OS_API_VERSION(MAC_OS_X_VERSION_10_3, GS_API_LATEST)
- (NSData *) docFormatFromRange: (NSRange)range
documentAttributes: (NSDictionary *)dict;

View file

@ -181,6 +181,11 @@ GNUstep extension.
/* TODO: underline */
- (NSSize) attachmentSizeForGlyphAtIndex: (NSUInteger)glyphIndex;
- (void) showAttachmentCell: (NSCell *)cell
inRect: (NSRect)rect
characterIndex: (NSUInteger)attachmentIndex;
@end
@interface NSLayoutManager (temporaryattributes)

View file

@ -33,6 +33,7 @@
#include <Foundation/NSBundle.h>
#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSDebug.h>
#include <Foundation/NSError.h>
#include <Foundation/NSException.h>
#include <Foundation/NSFileManager.h>
#include <Foundation/NSPathUtilities.h>
@ -231,6 +232,17 @@ static Class converter_class(NSString *format, BOOL producer)
return Nil;
}
static inline NSError*
create_error(int code, NSString* desc)
{
return [NSError errorWithDomain: @"NSAttributedString"
code: code
userInfo: [NSDictionary
dictionaryWithObjectsAndKeys:
NSLocalizedString(desc, @"Error description"),
NSLocalizedDescriptionKey, nil]];
}
@implementation NSAttributedString (AppKit)
+ (NSArray *) textFileTypes
@ -256,6 +268,30 @@ static Class converter_class(NSString *format, BOOL producer)
NSRTFDPboardType, NSHTMLPboardType, nil];
}
+ (NSArray *) textTypes
{
// FIXME
return [self textUnfilteredTypes];
}
+ (NSArray *) textUnfilteredTypes
{
return [NSArray arrayWithObjects: @"public.plain-text",
@"public.rtf",
@"com.apple.rtfd",
@"public.html",
/*
@"public.xml",
@"com.apple.webarchive",
@"com.microsoft.word.doc",
@"com.microsoft.word.wordml",
@"org.openxmlformats.wordprocessingml.document",
@"org.oasis-open.opendocument.text",
@"com.apple.traditional-mac-plain-text",
*/
nil];
}
+ (NSAttributedString *) attributedStringWithAttachment:
(NSTextAttachment *)attachment
{
@ -359,8 +395,16 @@ static Class converter_class(NSString *format, BOOL producer)
return [dictionaryClass dictionary];
}
- (unsigned) lineBreakBeforeIndex: (unsigned)location
withinRange: (NSRange)aRange
- (NSUInteger) lineBreakByHyphenatingBeforeIndex: (NSUInteger)location
withinRange: (NSRange)aRange
{
// FIXME
return [self lineBreakBeforeIndex: location
withinRange: aRange];
}
- (NSUInteger) lineBreakBeforeIndex: (NSUInteger)location
withinRange: (NSRange)aRange
{
NSString *str = [self string];
unsigned length = [str length];
@ -604,37 +648,6 @@ static Class converter_class(NSString *format, BOOL producer)
}
}
- (id) initWithPath: (NSString *)path
documentAttributes: (NSDictionary **)dict
{
// FIXME: This expects the file to be RTFD
NSFileWrapper *fw;
if (path == nil)
{
RELEASE (self);
return nil;
}
fw = [[NSFileWrapper alloc] initWithPath: path];
AUTORELEASE (fw);
return [self initWithRTFDFileWrapper: fw documentAttributes: dict];
}
- (id) initWithURL: (NSURL *)url
documentAttributes: (NSDictionary **)dict
{
NSError *error;
NSDictionary *options = [NSDictionary dictionaryWithObject: [url baseURL]
forKey: NSBaseURLDocumentOption];
return [self initWithURL: url
options: options
documentAttributes: dict
error: &error];
}
- (id) initWithRTFDFileWrapper: (NSFileWrapper *)wrapper
documentAttributes: (NSDictionary **)dict
{
@ -706,6 +719,21 @@ documentAttributes: (NSDictionary **)dict
- (id) initWithHTML: (NSData *)data
baseURL: (NSURL *)base
documentAttributes: (NSDictionary **)dict
{
NSDictionary *options = nil;
if (base != nil)
options = [NSDictionary dictionaryWithObject: base
forKey: NSBaseURLDocumentOption];
return [self initWithHTML: data
options: options
documentAttributes: dict];
}
- (id) initWithHTML: (NSData *)data
options: (NSDictionary *)options
documentAttributes: (NSDictionary **)dict
{
if (data == nil)
{
@ -717,6 +745,169 @@ documentAttributes: (NSDictionary **)dict
return self;
}
- (id) initWithDocFormat: (NSData *)data
documentAttributes: (NSDictionary **)dict
{
NSAttributedString *new;
if (data == nil)
{
RELEASE (self);
return nil;
}
new = [converter_class(@"DOC", NO)
parseData: data
documentAttributes: dict
class: [self class]];
// We do not return self but the newly created object
RELEASE (self);
return RETAIN (new);
}
- (id) initWithData: (NSData *)data
options: (NSDictionary *)options
documentAttributes: (NSDictionary **)dict
error: (NSError **)error
{
NSString *type = [options objectForKey: NSDocumentTypeDocumentOption];
if (data == nil)
{
*error = create_error(0, @"No data specified for data loading.");
RELEASE(self);
return nil;
}
if (type == nil)
{
// FIXME: try to determine type
*error = create_error(0, @"No type specified for data.");
RELEASE(self);
return nil;
}
if ([type isEqualToString: NSDocFormatTextDocumentType])
{
return [self initWithDocFormat: data
documentAttributes: dict];
}
else if ([type isEqualToString: NSHTMLTextDocumentType]
|| [type isEqualToString: @"public.html"]
|| [type isEqualToString: @"html"])
{
return [self initWithHTML: data
options: options
documentAttributes: dict];
}
else if ([type isEqualToString: NSRTFDTextDocumentType]
|| [type isEqualToString: @"com.apple.rtfd"]
|| [type isEqualToString: @"rtfd"])
{
return [self initWithRTFD: data
documentAttributes: dict];
}
else if ([type isEqualToString: NSRTFTextDocumentType]
|| [type isEqualToString: @"public.rtf"]
|| [type isEqualToString: @"rtf"])
{
return [self initWithRTF: data
documentAttributes: dict];
}
else if ([type isEqualToString: NSPlainTextDocumentType]
|| [type isEqualToString: @"public.plain-text"]
|| [type isEqualToString: @"text"])
{
NSStringEncoding encoding = [[options objectForKey: @"CharacterEncoding"]
intValue];
NSDictionary *defaultAttrs = [options objectForKey: @"DefaultAttributes"];
NSString *str = [[NSString alloc] initWithData: data
encoding: encoding];
self = [self initWithString: str
attributes: defaultAttrs];
RELEASE(str);
return self;
}
*error = create_error(0, @"Could not load data.");
RELEASE(self);
return nil;
}
- (id) initWithPath: (NSString *)path
documentAttributes: (NSDictionary **)dict
{
BOOL isDir = NO;
if (path == nil)
{
RELEASE (self);
return nil;
}
if ([[NSFileManager defaultManager]
fileExistsAtPath: path isDirectory: &isDir] && isDir)
{
// FIXME: This expects the file to be RTFD
NSFileWrapper *fw;
fw = [[NSFileWrapper alloc] initWithPath: path];
AUTORELEASE (fw);
return [self initWithRTFDFileWrapper: fw documentAttributes: dict];
}
else
{
return [self initWithURL: [NSURL fileURLWithPath: path]
documentAttributes: dict];
}
}
- (id) initWithURL: (NSURL *)url
documentAttributes: (NSDictionary **)dict
{
NSError *error = nil;
NSDictionary *options = [NSDictionary dictionaryWithObject: [url baseURL]
forKey: NSBaseURLDocumentOption];
return [self initWithURL: url
options: options
documentAttributes: dict
error: &error];
}
- (id) initWithURL: (NSURL *)url
options: (NSDictionary *)options
documentAttributes: (NSDictionary **)dict
error: (NSError **)error
{
NSData *data = [url resourceDataUsingCache: YES];
if (data == nil)
{
*error = create_error(0, @"Could not load data from URL.");
RELEASE(self);
return nil;
}
// Pass on baseURL
if (options == nil)
options = [NSDictionary dictionaryWithObject: [url baseURL]
forKey: NSBaseURLDocumentOption];
else if ([options objectForKey: NSBaseURLDocumentOption] == nil)
{
options = AUTORELEASE([options mutableCopy]);
[(NSMutableDictionary*)options setObject: [url baseURL]
forKey: NSBaseURLDocumentOption];
}
return [self initWithData: data
options: options
documentAttributes: dict
error: error];
}
- (NSData *) RTFFromRange: (NSRange)range
documentAttributes: (NSDictionary *)dict
{
@ -744,110 +935,6 @@ documentAttributes: (NSDictionary **)dict
documentAttributes: dict];
}
- (id) initWithDocFormat: (NSData *)data
documentAttributes: (NSDictionary **)dict
{
NSAttributedString *new;
if (data == nil)
{
RELEASE (self);
return nil;
}
new = [converter_class(@"DOC", NO)
parseData: data
documentAttributes: dict
class: [self class]];
// We do not return self but the newly created object
RELEASE (self);
return RETAIN (new);
}
- (id) initWithHTML: (NSData *)data
options: (NSDictionary *)options
documentAttributes: (NSDictionary **)dict
{
NSURL *baseURL = [options objectForKey: NSBaseURLDocumentOption];
return [self initWithHTML: data
baseURL: baseURL
documentAttributes: dict];
}
- (id) initWithData: (NSData *)data
options: (NSDictionary *)options
documentAttributes: (NSDictionary **)dict
error: (NSError **)error
{
NSString *type = [options objectForKey: NSDocumentTypeDocumentOption];
if (type == nil)
{
// FIXME: try to determine type
}
if ([type isEqualToString: NSDocFormatTextDocumentType])
{
return [self initWithDocFormat: data
documentAttributes: dict];
}
else if ([type isEqualToString: NSHTMLTextDocumentType])
{
return [self initWithHTML: data
options: options
documentAttributes: dict];
}
else if ([type isEqualToString: NSRTFDTextDocumentType])
{
return [self initWithRTFD: data
documentAttributes: dict];
}
else if ([type isEqualToString: NSRTFTextDocumentType])
{
return [self initWithRTF: data
documentAttributes: dict];
}
else if ([type isEqualToString: NSPlainTextDocumentType])
{
NSStringEncoding encoding = [[options objectForKey: @"CharacterEncoding"]
intValue];
NSDictionary *defaultAttrs = [options objectForKey: @"DefaultAttributes"];
NSString *str = [[NSString alloc] initWithData: data
encoding: encoding];
self = [self initWithString: str
attributes: defaultAttrs];
RELEASE(str);
return self;
}
// FIXME: Set error
RELEASE(self);
return nil;
}
- (id) initWithURL: (NSURL *)url
options: (NSDictionary *)options
documentAttributes: (NSDictionary **)dict
error: (NSError **)error
{
NSData *data = [url resourceDataUsingCache: YES];
if (data == nil)
{
// FIXME: Set error
RELEASE(self);
return nil;
}
// FIXME: Pass on baseURL
return [self initWithData: data
options: options
documentAttributes: dict
error: error];
}
- (NSData *) docFormatFromRange: (NSRange)range
documentAttributes: (NSDictionary *)dict
{
@ -863,7 +950,7 @@ documentAttributes: (NSDictionary **)dict
if (type == nil)
{
// FIXME: set error
*error = create_error(0, @"No type specified for data.");
return nil;
}
@ -896,6 +983,7 @@ documentAttributes: (NSDictionary **)dict
return [[self string] dataUsingEncoding: encoding];
}
*error = create_error(0, @"Could not create data for type.");
return nil;
}
@ -916,15 +1004,10 @@ documentAttributes: (NSDictionary **)dict
return AUTORELEASE(wrapper);
}
// FIXME: Set error
return nil;
}
if (*error == nil)
*error = create_error(0, @"Could not create data for type.");
- (unsigned) lineBreakByHyphenatingBeforeIndex: (unsigned)location
withinRange: (NSRange)aRange
{
// FIXME
return NSNotFound;
return nil;
}
- (NSRange) itemNumberInTextList: (NSTextList *)list
@ -1476,78 +1559,19 @@ static NSMutableDictionary *cachedCSets = nil;
documentAttributes: (NSDictionary **)documentAttributes
error: (NSError **)error
{
NSString *extension;
NSString *type;
NSAttributedString *attr;
if (![url isFileURL])
return NO;
extension = [[url path] pathExtension];
type = [[NSDocumentController sharedDocumentController]
typeFromFileExtension: extension];
if (type == nil)
return NO;
if ([type isEqualToString: @"html"])
attr = [[NSAttributedString alloc]
initWithURL: url
options: options
documentAttributes: documentAttributes
error: error];
if (attr != nil)
{
NSData *data = [url resourceDataUsingCache: YES];
NSAttributedString *attr;
attr = [[NSAttributedString alloc]
initWithHTML: data
options: options
documentAttributes: documentAttributes];
[self setAttributedString: attr];
RELEASE(attr);
return YES;
}
else if ([type isEqualToString: @"rtfd"])
{
NSData *data = [url resourceDataUsingCache: YES];
NSAttributedString *attr;
attr = [[NSAttributedString alloc]
initWithRTFD: data
documentAttributes: documentAttributes];
[self setAttributedString: attr];
RELEASE(attr);
return YES;
}
else if ([type isEqualToString: @"rtf"])
{
NSData *data = [url resourceDataUsingCache: YES];
NSAttributedString *attr;
attr = [[NSAttributedString alloc]
initWithRTF: data
documentAttributes: documentAttributes];
[self setAttributedString: attr];
RELEASE(attr);
return YES;
}
else if ([type isEqualToString: @"text"])
{
NSData *data = [url resourceDataUsingCache: YES];
NSStringEncoding encoding = [[options objectForKey: @"CharacterEncoding"]
intValue];
NSDictionary *defaultAttrs = [options objectForKey: @"DefaultAttributes"];
NSString *str = [[NSString alloc] initWithData: data
encoding: encoding];
NSAttributedString *attr;
attr = [[NSAttributedString alloc]
initWithString: str
attributes: defaultAttrs];
RELEASE(str);
[self setAttributedString: attr];
RELEASE(attr);
return YES;
}
// FIXME This should also support all converter bundles
return NO;
}

View file

@ -1275,6 +1275,56 @@ container
}
}
static inline NSSize
attachmentSize(linefrag_t *lf, NSUInteger glyphIndex)
{
linefrag_attachment_t *la;
int la_i;
la = lf->attachments;
la_i = 0;
while (la->pos != glyphIndex && la_i < lf->num_attachments)
{
la++;
la_i++;
}
if (la_i >= lf->num_attachments)
return NSMakeSize(-1.0, -1.0);
return la->size;
}
- (NSSize) attachmentSizeForGlyphAtIndex: (NSUInteger)glyphIndex
{
textcontainer_t *tc;
int i;
linefrag_t *lf;
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
if (tc->pos + tc->length > glyphIndex)
break;
if (i == num_textcontainers)
{
NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
return NSMakeSize(-1.0, -1.0);
}
LINEFRAG_FOR_GLYPH(glyphIndex);
return attachmentSize(lf, glyphIndex);
}
- (void) showAttachmentCell: (NSCell *)cell
inRect: (NSRect)rect
characterIndex: (NSUInteger)attachmentIndex
{
[(id <NSTextAttachmentCell>)cell drawWithFrame: rect
inView: [NSView focusView]
characterIndex: attachmentIndex
layoutManager: self];
}
-(void) drawGlyphsForGlyphRange: (NSRange)range
atPoint: (NSPoint)containerOrigin
@ -1284,9 +1334,6 @@ container
linefrag_t *lf;
linefrag_point_t *lp;
linefrag_attachment_t *la;
int la_i;
NSPoint p;
unsigned int g;
@ -1316,8 +1363,6 @@ container
int gbuf_len, gbuf_size;
NSPoint gbuf_point = NSZeroPoint;
NSView *controlView = nil;
if (!range.length)
return;
[self _doLayoutToGlyph: range.location + range.length - 1];
@ -1356,9 +1401,6 @@ container
LINEFRAG_FOR_GLYPH(range.location);
la = lf->attachments;
la_i = 0;
j = 0;
lp = lf->points;
while (lp->pos + lp->length < range.location)
@ -1451,8 +1493,6 @@ container
lf++;
j = 0;
lp = lf->points;
la = lf->attachments;
la_i = 0;
}
p = lp->p;
p.x += lf->rect.origin.x + containerOrigin.x;
@ -1502,9 +1542,7 @@ container
{
if (glyph->g == GSAttachmentGlyph)
{
/* Silently ignore if we don't have any size information for
it. */
if (g >= range.location && la)
if (g >= range.location)
{
unsigned int char_index =
[self characterRangeForGlyphRange: NSMakeRange(g, 1)
@ -1514,21 +1552,15 @@ container
effectiveRange: NULL] attachmentCell];
NSRect cellFrame;
if (!controlView)
controlView = [NSView focusView];
while (la->pos != g && la_i < lf->num_attachments)
{
la++;
la_i++;
}
if (la_i >= lf->num_attachments)
continue;
cellFrame.origin = p;
cellFrame.size = la->size;
cellFrame.size = attachmentSize(lf, g);
cellFrame.origin.y -= cellFrame.size.height;
/* Silently ignore if we don't have any size information for
it. */
if (NSEqualSizes(cellFrame.size, NSMakeSize(-1.0, -1.0)))
continue;
/* Drawing the cell might mess up our state, so we reset
the font and color afterwards. */
/* TODO:
@ -1540,10 +1572,9 @@ container
should they really be drawn in our coordinate system?
*/
[cell drawWithFrame: cellFrame
inView: controlView
characterIndex: char_index
layoutManager: self];
[self showAttachmentCell: (NSCell*)cell
inRect: cellFrame
characterIndex: char_index];
[f set];
[color set];
}

View file

@ -4810,8 +4810,13 @@ other than copy/paste or dragging. */
if (cell != nil)
{
/* TODO: Where to get the cellFrame? */
NSRect cellFrame = NSMakeRect(0, 0, 0, 0);
NSRect cellFrame;
cellFrame.origin = [_layoutManager
locationForGlyphAtIndex: startIndex];
cellFrame.size = [_layoutManager
attachmentSizeForGlyphAtIndex: startIndex];
cellFrame.origin.y -= cellFrame.size.height;
/* TODO: What about the insertion point ? */
if ([cell wantsToTrackMouseForEvent: theEvent