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 "MMTextStorage.h"
18 #import "MMTypesetter.h"
19 #import "Miscellaneous.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 /*NSTextStorage *ts = [[self layoutManager] textStorage];
65 if ('\n' == [[ts string] characterAtIndex:charIndex])
66 return NSTypesetterLineBreakAction;*/
68 return NSTypesetterWhitespaceAction;
73 - (void)setLocation:(NSPoint)location
74 withAdvancements:(const float *)advancements
75 forStartOfGlyphRange:(NSRange)glyphRange
77 ASLogDebug(@"setLocation:%@ withAdvancements:%f forStartOfGlyphRange:%@",
78 NSStringFromPoint(location), advancements ? *advancements : 0,
79 NSStringFromRange(glyphRange));
80 [super setLocation:location withAdvancements:advancements
81 forStartOfGlyphRange:glyphRange];
93 @implementation MMTypesetter2
96 // Layout glyphs so that each line fragment has a fixed size.
98 // It is assumed that the font for each character has been chosen so that every
99 // glyph has the right advancement (either 2*cellSize.width or half that,
100 // depending on whether it is a wide character or not). This is taken care of
101 // by MMTextStorage in setAttributes:range: and in setFont:. All that is left
102 // for the typesetter to do is to make sure each line fragment has the same
103 // height and that EOL glyphs are hidden.
105 - (void)layoutGlyphsInLayoutManager:(NSLayoutManager *)lm
106 startingAtGlyphIndex:(unsigned)startGlyphIdx
107 maxNumberOfLineFragments:(unsigned)maxNumLines
108 nextGlyphIndex:(unsigned *)nextGlyph
110 // TODO: Check that it really is an MMTextStorage?
111 MMTextStorage *ts = (MMTextStorage*)[lm textStorage];
112 NSTextContainer *tc = [[lm firstTextView] textContainer];
113 NSFont *font = [ts font];
114 NSString *text = [ts string];
116 if (!(lm && ts && tc && font && text
117 && [lm isValidGlyphIndex:startGlyphIdx]))
120 // Note that we always start laying out lines from the beginning of a line,
121 // even if 'startCharIdx' may be somewhere in the middle.
122 unsigned startCharIdx = [lm characterIndexForGlyphAtIndex:startGlyphIdx];
123 if (startCharIdx >= [text length])
126 [lm setTextContainer:tc forGlyphRange:
127 [lm glyphRangeForCharacterRange:NSMakeRange(0, [text length])
128 actualCharacterRange:nil]];
131 // STEP 1: Locate the line containing 'startCharIdx'.
133 MMRowCacheEntry *cache = [ts rowCache];
134 unsigned lineIdx = 0, nextLineIdx = 0;
135 int actualRows = [ts actualRows];
138 for (; line < actualRows; ++line, ++cache) {
139 lineIdx = nextLineIdx;
140 nextLineIdx += cache->length;
141 if (startCharIdx < nextLineIdx)
146 // STEP 2: Generate line fragment rects one line at a time until there are
147 // no more lines in the text storage, or until 'maxNumLines' have been
148 // exhausted. (There is no point in just laying out one line, the layout
149 // manager will keep calling this method until there are no more lines in
150 // the text storage.)
153 // NOTE: With non-zero linespace the baseline is adjusted so that the text
154 // is centered within a line.
155 float baseline = [font descender] - floor(.5*[ts linespace])
156 + [[NSUserDefaults standardUserDefaults]
157 floatForKey:MMBaselineOffsetKey];
158 NSSize cellSize = [ts cellSize];
159 NSPoint glyphPt = { 0, cellSize.height+baseline };
161 NSRange lineRange = { lineIdx, 0 };
162 NSRange glyphRange = { startGlyphIdx, 0 };
163 NSRect lineRect = { 0, line*cellSize.height,
164 [ts actualColumns]*cellSize.width, cellSize.height };
165 int endLine = line + maxNumLines;
166 if (endLine > actualRows)
167 endLine = actualRows;
169 for (; line < endLine; ++line, ++cache) {
170 lineRange.length = cache->length;
172 glyphRange = [lm glyphRangeForCharacterRange:lineRange
173 actualCharacterRange:nil];
175 [lm setLineFragmentRect:lineRect forGlyphRange:glyphRange
177 [lm setLocation:glyphPt forStartOfGlyphRange:glyphRange];
179 lineRange.location += lineRange.length;
180 lineRect.origin.y += cellSize.height;
182 // Hide EOL character (otherwise a square will be rendered).
183 [lm setNotShownAttribute:YES forGlyphAtIndex:lineRange.location-1];
187 *nextGlyph = NSMaxRange(glyphRange);
190 @end // MMTypesetter2
192 #endif // MM_USE_ROW_CACHE