diff --git a/MMTextStorage.h b/MMTextStorage.h index 6a223a87df..21aa47b8aa 100644 --- a/MMTextStorage.h +++ b/MMTextStorage.h @@ -58,5 +58,6 @@ - (BOOL)resizeToFitSize:(NSSize)size; - (NSSize)fitToSize:(NSSize)size; - (NSSize)fitToSize:(NSSize)size rows:(int *)rows columns:(int *)columns; +- (float)cellWidth; @end diff --git a/MMTextStorage.m b/MMTextStorage.m index 921f7e3022..c69dff86ff 100644 --- a/MMTextStorage.m +++ b/MMTextStorage.m @@ -29,7 +29,6 @@ @interface MMTextStorage (Private) - (void)doSetMaxRows:(int)rows columns:(int)cols; - (void)lazyResize; -- (float)cellWidth; - (float)widthOfEmptyRow; @end @@ -509,6 +508,11 @@ return fitSize; } +- (float)cellWidth +{ + return [font widthOfString:@"W"]; +} + @end // MMTextStorage @@ -568,11 +572,6 @@ actualRows = rows; actualColumns = cols; } -- (float)cellWidth -{ - return [font widthOfString:@"W"]; -} - - (float)widthOfEmptyRow { return [font widthOfString:[emptyRowString string]]; diff --git a/MMTypesetter.h b/MMTypesetter.h new file mode 100644 index 0000000000..9557c8a0b4 --- /dev/null +++ b/MMTypesetter.h @@ -0,0 +1,22 @@ +/* vi:set ts=8 sts=4 sw=4 ft=objc: + * + * VIM - Vi IMproved by Bram Moolenaar + * MacVim GUI port by Bjorn Winckler + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +#import + + +@interface MMTypesetter : NSTypesetter { +} + +- (void)layoutGlyphsInLayoutManager:(NSLayoutManager *)lm + startingAtGlyphIndex:(unsigned)startGlyphIdx + maxNumberOfLineFragments:(unsigned)maxNumLines + nextGlyphIndex:(unsigned *)nextGlyph; + +@end diff --git a/MMTypesetter.m b/MMTypesetter.m new file mode 100644 index 0000000000..aa65b2d109 --- /dev/null +++ b/MMTypesetter.m @@ -0,0 +1,140 @@ +/* vi:set ts=8 sts=4 sw=4 ft=objc: + * + * VIM - Vi IMproved by Bram Moolenaar + * MacVim GUI port by Bjorn Winckler + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +#import "MMTypesetter.h" +#import "MMTextStorage.h" + + + +#if 0 +@interface MMTypesetter (Private) +- (NSCharacterSet *)hiddenCharSet; +@end +#endif + + + +@implementation MMTypesetter + +// +// Layout glyphs so that each glyph takes up exactly one cell. +// +// The width of a cell is determined by [MMTextStorage cellWidth] (which +// typically sets one cell to equal the width of 'W' in the current font), and +// the height of a cell is given by the default line height for the current +// font. +// +// It is assumed that the text storage is set up so that each wide character is +// followed by a 'zero-width space' character (Unicode 0x200b); these are not +// rendered. If a wide character is not followed by a zero-width space, then +// the next character will render on top of it. +// +- (void)layoutGlyphsInLayoutManager:(NSLayoutManager *)lm + startingAtGlyphIndex:(unsigned)startGlyphIdx + maxNumberOfLineFragments:(unsigned)maxNumLines + nextGlyphIndex:(unsigned *)nextGlyph +{ + // TODO: Check that it really is an MMTextStorage. + MMTextStorage *ts = (MMTextStorage*)[lm textStorage]; + NSTextView *tv = [lm firstTextView]; + NSTextContainer *tc = [tv textContainer]; + NSFont *font = [ts font]; + NSString *text = [ts string]; + unsigned textLen = [text length]; + float cellWidth = [ts cellWidth]; + float cellHeight = [lm defaultLineHeightForFont:font]; + float baseline = [font descender]; + + if (!(ts && tv && tc && font && text && textLen)) + return; + + unsigned startCharIdx = [lm characterIndexForGlyphAtIndex:startGlyphIdx]; + unsigned i, numberOfLines = 0, firstLine = 0; + NSRange firstLineRange = { 0, 0 }; + + // Find first line and its range, and count the number of lines. + for (i = 0; i < textLen; numberOfLines++) { + NSRange lineRange = [text lineRangeForRange:NSMakeRange(i, 0)]; + if (NSLocationInRange(startCharIdx, lineRange)) { + firstLine = numberOfLines; + firstLineRange = lineRange; + } + + i = NSMaxRange(lineRange); + } + + // Perform line fragment generation one line at a time. + NSRange lineRange = firstLineRange; + unsigned endGlyphIdx = startGlyphIdx; + for (i = 0; i < maxNumLines && lineRange.length; ++i) { + NSRange glyphRange = [lm glyphRangeForCharacterRange:lineRange + actualCharacterRange:nil]; + NSRect lineRect = { 0, (firstLine+i)*cellHeight, + cellWidth*(lineRange.length-1), cellHeight }; + unsigned endLineIdx = NSMaxRange(lineRange); + NSPoint glyphPt = { 0, cellHeight+baseline }; + unsigned j; + + endGlyphIdx = NSMaxRange(glyphRange); + + [lm setTextContainer:tc forGlyphRange:glyphRange]; + [lm setLineFragmentRect:lineRect forGlyphRange:glyphRange + usedRect:lineRect]; + //[lm setLocation:glyphPt forStartOfGlyphRange:glyphRange]; + + // Position each glyph individually to ensure they take up exactly one + // cell. + for (j = glyphRange.location; j < endGlyphIdx; ++j) { + [lm setLocation:glyphPt forStartOfGlyphRange:NSMakeRange(j, 1)]; + glyphPt.x += cellWidth; + } + + // Hide non-zero space characters (there is one after every wide + // character). + for (j = lineRange.location; j < endLineIdx; ++j) { + if ([text characterAtIndex:j] == 0x200b) { + NSRange range = { j, 1 }; + range = [lm glyphRangeForCharacterRange:range + actualCharacterRange:nil]; + [lm setNotShownAttribute:YES forGlyphAtIndex:range.location]; + } + } + + lineRange = [text lineRangeForRange:NSMakeRange(endLineIdx, 0)]; + } + + if (nextGlyph) + *nextGlyph = endGlyphIdx; +} + +@end // MMTypesetter + + + + +#if 0 +@implementation MMTypesetter (Private) + +- (NSCharacterSet *)hiddenCharSet +{ + static NSCharacterSet *hiddenCharSet = nil; + + if (!hiddenCharSet) { + NSString *string = [NSString stringWithFormat:@"%C\n", 0x200b]; + hiddenCharSet = [NSCharacterSet + characterSetWithCharactersInString:string]; + [hiddenCharSet retain]; + } + + return hiddenCharSet; +} + +@end // MMTypesetter (Private) +#endif diff --git a/MMWindowController.m b/MMWindowController.m index 8bc91167b5..2f3c863e12 100644 --- a/MMWindowController.m +++ b/MMWindowController.m @@ -8,6 +8,8 @@ * See README.txt for an overview of the Vim source code. */ +#define MM_USE_CUSTOM_TYPESETTER 1 + #import "MMWindowController.h" #import #import "MMTextView.h" @@ -16,6 +18,10 @@ #import "MacVim.h" #import "MMAppController.h" +#if MM_USE_CUSTOM_TYPESETTER +# import "MMTypesetter.h" +#endif + // Scroller type; these must match SBAR_* in gui.h enum { @@ -107,6 +113,12 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) NSTextContainer *tc = [[NSTextContainer alloc] initWithContainerSize: NSMakeSize(1.0e7,1.0e7)]; +#if MM_USE_CUSTOM_TYPESETTER + MMTypesetter *typesetter = [[MMTypesetter alloc] init]; + [lm setTypesetter:typesetter]; + [typesetter release]; +#endif + [tc setWidthTracksTextView:NO]; [tc setHeightTracksTextView:NO]; [tc setLineFragmentPadding:0];