Fix placement of auxiliary IM window for Core Text
[MacVim.git] / src / MacVim / MMTypesetter.m
blob41fa6d448dfdb8c96ec44a83ef73bee783cd480c
1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
2  *
3  * VIM - Vi IMproved            by Bram Moolenaar
4  *                              MacVim GUI port by Bjorn Winckler
5  *
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.
9  */
11  * MMTypesetter
12  *
13  * Ensures that each line has a fixed height and deals with some baseline
14  * issues.
15  */
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:(CGFloat *)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
41     // works.
42     *baselineOffset += floor(.5*[ts linespace]);
45 #if 0
46 - (void)setNotShownAttribute:(BOOL)flag forGlyphRange:(NSRange)glyphRange
48     if (1 != glyphRange.length)
49         return;
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];
57 #endif
59 #if 0
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;
70 #endif
72 #if 0
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];
83 #endif
86 @end // MMTypesetter
91 #if MM_USE_ROW_CACHE
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:(NSUInteger)startGlyphIdx
107            maxNumberOfLineFragments:(NSUInteger)maxNumLines
108                      nextGlyphIndex:(NSUInteger *)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]))
118         return;
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])
124         return;
126     [lm setTextContainer:tc forGlyphRange:
127             [lm glyphRangeForCharacterRange:NSMakeRange(0, [text length])
128                        actualCharacterRange:nil]];
130     //
131     // STEP 1: Locate the line containing 'startCharIdx'.
132     //
133     MMRowCacheEntry *cache = [ts rowCache];
134     unsigned lineIdx = 0, nextLineIdx = 0;
135     int actualRows = [ts actualRows];
136     int line = 0;
138     for (; line < actualRows; ++line, ++cache) {
139         lineIdx = nextLineIdx;
140         nextLineIdx += cache->length;
141         if (startCharIdx < nextLineIdx)
142             break;
143     }
145     //
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.)
151     //
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
176                        usedRect:lineRect];
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];
184     }
186     if (nextGlyph)
187         *nextGlyph = NSMaxRange(glyphRange);
190 @end // MMTypesetter2
192 #endif // MM_USE_ROW_CACHE