Fix problems with 'fullscreen' and :mksession
[MacVim.git] / src / MacVim / MMTypesetter.m
blob481b29db3176dfc78a972cda7c19d0cae015f2a3
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:(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
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     //NSLog(@"%s%d", _cmd, charIndex);
64     /*NSTextStorage *ts = [[self layoutManager] textStorage];
66     if ('\n' == [[ts string] characterAtIndex:charIndex])
67         return NSTypesetterLineBreakAction;*/
69     return NSTypesetterWhitespaceAction;
71 #endif
73 #if 0
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];
84 #endif
87 @end // MMTypesetter
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]))
117         return;
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])
123         return;
125     [lm setTextContainer:tc forGlyphRange:
126             [lm glyphRangeForCharacterRange:NSMakeRange(0, [text length])
127                        actualCharacterRange:nil]];
129     //
130     // STEP 1: Locate the line containing 'startCharIdx'.
131     //
132     MMRowCacheEntry *cache = [ts rowCache];
133     unsigned lineIdx = 0, nextLineIdx = 0;
134     int actualRows = [ts actualRows];
135     int line = 0;
137     for (; line < actualRows; ++line, ++cache) {
138         lineIdx = nextLineIdx;
139         nextLineIdx += cache->length;
140         if (startCharIdx < nextLineIdx)
141             break;
142     }
144     //
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.)
150     //
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
175                        usedRect:lineRect];
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];
183     }
185     if (nextGlyph)
186         *nextGlyph = NSMaxRange(glyphRange);
189 @end // MMTypesetter2