1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * MacVim GUI port by Bjorn Winckler
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
13 * Ensures that each line has a fixed height and deals with some baseline
17 #import "MMTypesetter.h"
18 #import "MMTextStorage.h"
24 @implementation MMTypesetter
26 - (void)willSetLineFragmentRect:(NSRectPointer)lineRect
27 forGlyphRange:(NSRange)glyphRange
28 usedRect:(NSRectPointer)usedRect
29 baselineOffset:(float *)baselineOffset
31 MMTextStorage *ts = (MMTextStorage*)[[self layoutManager] textStorage];
32 float h = [ts cellSize].height;
34 // HACK! Force each line fragment rect to have a fixed height. By also
35 // forcing the 'usedRect' to the same height we also ensure that the cursor
36 // is as high as the line itself.
37 lineRect->size.height = h;
38 usedRect->size.height = h;
40 // See [MMTextStorage setLinespace:] for info on how 'linespace' support
42 *baselineOffset += floor(.5*[ts linespace]);
46 - (void)setNotShownAttribute:(BOOL)flag forGlyphRange:(NSRange)glyphRange
48 if (1 != glyphRange.length)
51 NSLayoutManager *lm = [self layoutManager];
52 unsigned charIdx = [lm characterIndexForGlyphAtIndex:glyphRange.location];
54 if ('\n' == [[[lm textStorage] string] characterAtIndex:charIdx])
55 [lm setNotShownAttribute:flag forGlyphAtIndex:glyphRange.location];
60 - (NSTypesetterControlCharacterAction)
61 actionForControlCharacterAtIndex:(unsigned)charIndex
63 //NSLog(@"%s%d", _cmd, charIndex);
64 /*NSTextStorage *ts = [[self layoutManager] textStorage];
66 if ('\n' == [[ts string] characterAtIndex:charIndex])
67 return NSTypesetterLineBreakAction;*/
69 return NSTypesetterWhitespaceAction;
74 - (void)setLocation:(NSPoint)location
75 withAdvancements:(const float *)advancements
76 forStartOfGlyphRange:(NSRange)glyphRange
78 NSLog(@"setLocation:%@ withAdvancements:%f forStartOfGlyphRange:%@",
79 NSStringFromPoint(location), advancements ? *advancements : 0,
80 NSStringFromRange(glyphRange));
81 [super setLocation:location withAdvancements:advancements
82 forStartOfGlyphRange:glyphRange];
92 @implementation MMTypesetter2
95 // Layout glyphs so that each line fragment has a fixed size.
97 // It is assumed that the font for each character has been chosen so that every
98 // glyph has the right advancement (either 2*cellSize.width or half that,
99 // depending on whether it is a wide character or not). This is taken care of
100 // by MMTextStorage in setAttributes:range: and in setFont:. All that is left
101 // for the typesetter to do is to make sure each line fragment has the same
102 // height and that EOL glyphs are hidden.
104 - (void)layoutGlyphsInLayoutManager:(NSLayoutManager *)lm
105 startingAtGlyphIndex:(unsigned)startGlyphIdx
106 maxNumberOfLineFragments:(unsigned)maxNumLines
107 nextGlyphIndex:(unsigned *)nextGlyph
109 // TODO: Check that it really is an MMTextStorage?
110 MMTextStorage *ts = (MMTextStorage*)[lm textStorage];
111 NSTextContainer *tc = [[lm firstTextView] textContainer];
112 NSFont *font = [ts font];
113 NSString *text = [ts string];
115 if (!(lm && ts && tc && font && text
116 && [lm isValidGlyphIndex:startGlyphIdx]))
119 // Note that we always start laying out lines from the beginning of a line,
120 // even if 'startCharIdx' may be somewhere in the middle.
121 unsigned startCharIdx = [lm characterIndexForGlyphAtIndex:startGlyphIdx];
122 if (startCharIdx >= [text length])
125 [lm setTextContainer:tc forGlyphRange:
126 [lm glyphRangeForCharacterRange:NSMakeRange(0, [text length])
127 actualCharacterRange:nil]];
130 // STEP 1: Locate the line containing 'startCharIdx'.
132 MMRowCacheEntry *cache = [ts rowCache];
133 unsigned lineIdx = 0, nextLineIdx = 0;
134 int actualRows = [ts actualRows];
137 for (; line < actualRows; ++line, ++cache) {
138 lineIdx = nextLineIdx;
139 nextLineIdx += cache->length;
140 if (startCharIdx < nextLineIdx)
145 // STEP 2: Generate line fragment rects one line at a time until there are
146 // no more lines in the text storage, or until 'maxNumLines' have been
147 // exhausted. (There is no point in just laying out one line, the layout
148 // manager will keep calling this method until there are no more lines in
149 // the text storage.)
152 // NOTE: With non-zero linespace the baseline is adjusted so that the text
153 // is centered within a line.
154 float baseline = [font descender] - floor(.5*[ts linespace])
155 + [[NSUserDefaults standardUserDefaults]
156 floatForKey:MMBaselineOffsetKey];
157 NSSize cellSize = [ts cellSize];
158 NSPoint glyphPt = { 0, cellSize.height+baseline };
160 NSRange lineRange = { lineIdx, 0 };
161 NSRange glyphRange = { startGlyphIdx, 0 };
162 NSRect lineRect = { 0, line*cellSize.height,
163 [ts actualColumns]*cellSize.width, cellSize.height };
164 int endLine = line + maxNumLines;
165 if (endLine > actualRows)
166 endLine = actualRows;
168 for (; line < endLine; ++line, ++cache) {
169 lineRange.length = cache->length;
171 glyphRange = [lm glyphRangeForCharacterRange:lineRange
172 actualCharacterRange:nil];
174 [lm setLineFragmentRect:lineRect forGlyphRange:glyphRange
176 [lm setLocation:glyphPt forStartOfGlyphRange:glyphRange];
178 lineRange.location += lineRange.length;
179 lineRect.origin.y += cellSize.height;
181 // Hide EOL character (otherwise a square will be rendered).
182 [lm setNotShownAttribute:YES forGlyphAtIndex:lineRange.location-1];
186 *nextGlyph = NSMaxRange(glyphRange);
189 @end // MMTypesetter2