diff --git a/ChangeLog b/ChangeLog index cf17c60fe..0885f3d5d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Fri Sep 3 22:45:00 1999 Richard Frith-Macdonald + + * Source/NSAttributedString.m: implemented (untested) most of the + missing methods, and fixed a few memory leaks. + Thu Sep 2 16:42:00 1999 Richard Frith-Macdonald * NSWorkspace.m: Trivial fix to pass app when opening files. diff --git a/Source/NSAttributedString.m b/Source/NSAttributedString.m index 82bb42358..0326906a6 100644 --- a/Source/NSAttributedString.m +++ b/Source/NSAttributedString.m @@ -1,4 +1,75 @@ #include +#include +#include + +/* + * function to return a character set containing characters that + * separate words. + */ +static NSCharacterSet* +wordBreakCSet() +{ + static NSCharacterSet *cset = nil; + + if (cset == nil) + { + NSMutableCharacterSet *m = [NSMutableCharacterSet new]; + + cset = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + [m formUnionWithCharacterSet: cset]; + cset = [NSCharacterSet punctuationCharacterSet]; + [m formUnionWithCharacterSet: cset]; + cset = [NSCharacterSet controlCharacterSet]; + [m formUnionWithCharacterSet: cset]; + cset = [NSCharacterSet illegalCharacterSet]; + [m formUnionWithCharacterSet: cset]; + cset = [m copy]; + RELEASE(m); + } + return cset; +} + +/* + * function to return a character set containing characters that + * are legal within words. + */ +static NSCharacterSet* +wordCSet() +{ + static NSCharacterSet *cset = nil; + + if (cset == nil) + { + cset = [[wordBreakCSet() invertedSet] copy]; + } + return cset; +} + +/* + * function to return a character set containing paragraph separators + */ +static NSCharacterSet* +paraBreakCSet() +{ + static NSCharacterSet *cset = nil; + + if (cset == nil) + { + NSMutableCharacterSet *m = [NSMutableCharacterSet new]; + + /* + * Build set with characters specified in MacOS-X documentation for + * [NSAttributedString -fixParagraphStyleAttributeInRange:] + */ + [m addCharactersInRange: NSMakeRange(0x000A, 1)]; /* CR */ + [m addCharactersInRange: NSMakeRange(0x000D, 1)]; /* LF */ + [m addCharactersInRange: NSMakeRange(0x2028, 1)]; /* line sep */ + [m addCharactersInRange: NSMakeRange(0x2029, 1)]; /* para sep */ + cset = [m copy]; + RELEASE(m); + } + return cset; +} @implementation NSAttributedString (AppKit) @@ -116,11 +187,85 @@ - (NSRange) doubleClickAtIndex: (unsigned)location { + NSString *str = [self string]; + unsigned length = [str length]; + NSRange scanRange; + NSRange startRange; + NSRange endRange; + + scanRange = NSMakeRange(0, location); + startRange = [str rangeOfCharacterFromSet: wordBreakCSet() + options: NSBackwardsSearch|NSLiteralSearch + range: scanRange]; + scanRange = NSMakeRange(location, length - location); + endRange = [str rangeOfCharacterFromSet: wordBreakCSet() + options: NSLiteralSearch + range: scanRange]; + if (startRange.length == 0) + { + location = 0; + } + else + { + location = startRange.location + startRange.length; + } + if (endRange.length == 0) + { + length = length - location; + } + else + { + length = endRange.location - location; + } + return NSMakeRange(location, length); } - (unsigned) nextWordFromIndex: (unsigned)location forward: (BOOL)isForward { + if (isForward) + { + NSString *str = [self string]; + unsigned length = [str length]; + NSRange range; + + range = NSMakeRange(location, length - location); + range = [str rangeOfCharacterFromSet: wordBreakCSet() + options: NSLiteralSearch + range: range]; + if (range.length == 0) + return NSNotFound; + location = range.location; + range = NSMakeRange(location, length - location); + range = [str rangeOfCharacterFromSet: wordCSet() + options: NSLiteralSearch + range: range]; + if (range.length == 0) + return NSNotFound; + return range.location; + } + else if (location > 0) + { + NSString *str = [self string]; + NSRange range; + + range = NSMakeRange(0, location); + range = [str rangeOfCharacterFromSet: wordBreakCSet() + options: NSBackwardsSearch|NSLiteralSearch + range: range]; + location = range.location; + range = NSMakeRange(0, location); + range = [str rangeOfCharacterFromSet: wordCSet() + options: NSLiteralSearch + range: range]; + if (range.length == 0) + return NSNotFound; + return range.location; + } + else + { + return NSNotFound; + } } /* @@ -206,7 +351,7 @@ documentAttributes: (NSDictionary**)dict sValue++; [self addAttribute: NSSuperscriptAttributeName - value: [[NSNumber alloc] initWithInt: sValue] + value: [NSNumber numberWithInt: sValue] range: range]; } @@ -224,59 +369,199 @@ documentAttributes: (NSDictionary**)dict sValue--; [self addAttribute: NSSuperscriptAttributeName - value: [[NSNumber alloc] initWithInt: sValue] + value: [NSNumber numberWithInt: sValue] range: range]; } -- (void)unscriptRange:(NSRange)range +- (void) unscriptRange: (NSRange)range { - [self addAttribute:NSSuperscriptAttributeName value:[[NSNumber alloc] - initWithInt:0] range:range]; + [self addAttribute: NSSuperscriptAttributeName + value: [NSNumber numberWithInt: 0] + range: range]; } -- (void)applyFontTraits:(NSFontTraitMask)traitMask range:(NSRange)range +- (void) applyFontTraits: (NSFontTraitMask)traitMask range: (NSRange)range { /* We don't use font traits yet, oops. */ /* id value; - value = [self attribute:NSFontAttributeName - atIndex:range.location effectiveRange:range]; + value = [self attribute: NSFontAttributeName + atIndex: range.location + effectiveRange: range]; - [value setFontTraits:traitMask]; + [value setFontTraits: traitMask]; - [self addAttribute:NSFontAttributeName value:value range:range]; + [self addAttribute: NSFontAttributeName value: value range: range]; */ } -- (void)setAlignment:(NSTextAlignment)alignment range:(NSRange)range +- (void) setAlignment: (NSTextAlignment)alignment range: (NSRange)range { id value; - value = [self attribute:NSParagraphStyleAttributeName - atIndex:range.location effectiveRange:&range]; + value = [self attribute: NSParagraphStyleAttributeName + atIndex: range.location + effectiveRange: &range]; - [value setAlignment:alignment]; + [value setAlignment: alignment]; - [self addAttribute:NSParagraphStyleAttributeName value:value range:range]; + [self addAttribute: NSParagraphStyleAttributeName value: value range: range]; } -- (void)fixAttributesInRange:(NSRange)range +- (void) fixAttributesInRange: (NSRange)range { - [self fixFontAttributeInRange:range]; - [self fixParagraphStyleAttributeInRange:range]; - [self fixAttachmentAttributeInRange:range]; + [self fixFontAttributeInRange: range]; + [self fixParagraphStyleAttributeInRange: range]; + [self fixAttachmentAttributeInRange: range]; } -- (void)fixFontAttributeInRange:(NSRange)range +- (void) fixFontAttributeInRange: (NSRange)range { } -- (void)fixParagraphStyleAttributeInRange:(NSRange)range +- (void) fixParagraphStyleAttributeInRange: (NSRange)range { + NSString *str = [self string]; + unsigned length = [str length]; + unsigned location; + NSRange r; + + if (range.location > 0) + { + /* + * Extend range backward to take in entire paragraph if necessary. + */ + r = NSMakeRange(0, range.location); + r = [str rangeOfCharacterFromSet: paraBreakCSet() + options: NSBackwardsSearch|NSLiteralSearch + range: r]; + if (r.length == 0) + { + /* + * No paragraph before this in range - so extend range right + * back to the start of the string. + */ + range.length += range.location; + range.location = 0; + } + else if (r.location + 1 < range.location) + { + range.length += (range.location - r.location - 1); + range.location = r.location + 1; + } + } + + /* + * Extend range forwards to take in entire paragraph if necessary. + */ + location = r.location + r.length; + if (location > 0) + location--; + r = NSMakeRange(location, length - location); + r = [str rangeOfCharacterFromSet: paraBreakCSet() + options: NSLiteralSearch + range: r]; + if (r.length > 0 && r.location > location) + range.length += (r.location - location); + + /* + * Now try to step through the range fixing up the paragraph styles in + * each paragraph so the entire paragraph has the same style as the + * first character in the paragraph. + */ + while (range.length > 0) + { + NSParagraphStyle *style; + NSRange found; + unsigned end; + + /* + * Determine position of next paragraph end. + */ + r = [str rangeOfCharacterFromSet: paraBreakCSet() + options: NSLiteralSearch + range: range]; + if (r.length == 0) + end = NSMaxRange(range); + else + end = r.location + 1; + + /* + * get the style in effect at the paragraph start. + */ + style = [self attribute: NSParagraphStyleAttributeName + atIndex: location + effectiveRange: &found]; + /* + * Fix up this paragraph to have the starting style. + */ + while (NSMaxRange(found) < end) + { + NSParagraphStyle *nextStyle; + NSRange nextFound; + + nextStyle = [self attribute: NSParagraphStyleAttributeName + atIndex: NSMaxRange(found) + effectiveRange: &nextFound]; + if (nextStyle == style || [nextStyle isEqual: style] == YES) + { + found = nextFound; + } + else + { + /* + * Styles differ - add the old style to the remainder of the + * range. + */ + found.location = NSMaxRange(found); + found.length = end - found.location; + [self addAttribute: NSParagraphStyleAttributeName + value: style + range: found]; + } + } + /* + * Adjust the range to start at the beginning of the next paragraph. + */ + range.length = NSMaxRange(range) - end; + range.location = end; + } } -- (void)fixAttachmentAttributeInRange:(NSRange)range +- (void) fixAttachmentAttributeInRange: (NSRange)range { + unsigned location = range.location; + unsigned end = NSMaxRange(range); + + while (location < end) + { + NSDictionary *attr; + + attr = [self attributesAtIndex: location effectiveRange: &range]; + if ([attr objectForKey: NSAttachmentAttributeName] != nil) + { + unichar buf[range.length]; + unsigned pos = 0; + + [[self string] getCharacters: buf range: range]; + while (pos < range.length) + { + unsigned start; + unsigned end; + + while (pos < range.length && buf[pos] == NSAttachmentCharacter) + pos++; + start = pos; + while (pos < range.length && buf[pos] == NSAttachmentCharacter) + pos++; + end = pos; + if (start != end) + [self removeAttribute: NSAttachmentAttributeName + range: NSMakeRange(start, end - start)]; + } + } + location = NSMaxRange(range); + } } @end