Fix problems with 'fullscreen' and :mksession
[MacVim.git] / src / MacVim / MMAtsuiTextView.m
blob1361581960be8dc7733642d81daef55640a0c248
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  * MMAtsuiTextView
12  *
13  * Dispatches keyboard and mouse input to the backend.  Handles drag-n-drop of
14  * files onto window.  The rendering is done using ATSUI.
15  *
16  * The text view area consists of two parts:
17  *   1. The text area - this is where text is rendered; the size is governed by
18  *      the current number of rows and columns.
19  *   2. The inset area - this is a border around the text area; the size is
20  *      governed by the user defaults MMTextInset[Left|Right|Top|Bottom].
21  *
22  * The current size of the text view frame does not always match the desired
23  * area, i.e. the area determined by the number of rows, columns plus text
24  * inset.  This distinction is particularly important when the view is being
25  * resized.
26  */
28 #import "MMAppController.h"
29 #import "MMAtsuiTextView.h"
30 #import "MMTextViewHelper.h"
31 #import "MMVimController.h"
32 #import "MMWindowController.h"
33 #import "Miscellaneous.h"
36 // TODO: What does DRAW_TRANSP flag do?  If the background isn't drawn when
37 // this flag is set, then sometimes the character after the cursor becomes
38 // blank.  Everything seems to work fine by just ignoring this flag.
39 #define DRAW_TRANSP               0x01    /* draw with transparant bg */
40 #define DRAW_BOLD                 0x02    /* draw bold text */
41 #define DRAW_UNDERL               0x04    /* draw underline text */
42 #define DRAW_UNDERC               0x08    /* draw undercurl text */
43 #define DRAW_ITALIC               0x10    /* draw italic text */
44 #define DRAW_CURSOR               0x20
46 #define kUnderlineOffset            (-2)
47 #define kUnderlineHeight            1
48 #define kUndercurlHeight            2
49 #define kUndercurlOffset            (-2)
50 #define kUndercurlDotWidth          2
51 #define kUndercurlDotDistance       2
54 @interface NSFont (AppKitPrivate)
55 - (ATSUFontID) _atsFontID;
56 @end
59 @interface MMAtsuiTextView (Private)
60 - (void)initAtsuStyles;
61 - (void)disposeAtsuStyles;
62 - (void)updateAtsuStyles;
63 - (MMWindowController *)windowController;
64 - (MMVimController *)vimController;
65 @end
68 @interface MMAtsuiTextView (Drawing)
69 - (NSPoint)originForRow:(int)row column:(int)column;
70 - (NSRect)rectFromRow:(int)row1 column:(int)col1
71                 toRow:(int)row2 column:(int)col2;
72 - (NSSize)textAreaSize;
73 - (void)resizeContentImage;
74 - (void)beginDrawing;
75 - (void)endDrawing;
76 - (void)drawString:(UniChar *)string length:(UniCharCount)length
77              atRow:(int)row column:(int)col cells:(int)cells
78          withFlags:(int)flags foregroundColor:(NSColor *)fg
79    backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp;
80 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
81               scrollBottom:(int)bottom left:(int)left right:(int)right
82                      color:(NSColor *)color;
83 - (void)insertLinesAtRow:(int)row lineCount:(int)count
84             scrollBottom:(int)bottom left:(int)left right:(int)right
85                    color:(NSColor *)color;
86 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
87                    column:(int)col2 color:(NSColor *)color;
88 - (void)clearAll;
89 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
90                        fraction:(int)percent color:(NSColor *)color;
91 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
92                    numColumns:(int)ncols;
93 @end
97     static float
98 defaultLineHeightForFont(NSFont *font)
100     // HACK: -[NSFont defaultLineHeightForFont] is deprecated but since the
101     // ATSUI renderer does not use NSLayoutManager we create one temporarily.
102     NSLayoutManager *lm = [[NSLayoutManager alloc] init];
103     float height = [lm defaultLineHeightForFont:font];
104     [lm release];
106     return height;
109 @implementation MMAtsuiTextView
111 - (id)initWithFrame:(NSRect)frame
113     if (!(self = [super initWithFrame:frame]))
114         return nil;
116     // NOTE!  It does not matter which font is set here, Vim will set its
117     // own font on startup anyway.  Just set some bogus values.
118     font = [[NSFont userFixedPitchFontOfSize:0] retain];
119     ascender = 0;
120     cellSize.width = cellSize.height = 1;
121     contentImage = nil;
122     imageSize = NSZeroSize;
123     insetSize = NSZeroSize;
125     // NOTE: If the default changes to 'NO' then the intialization of
126     // p_antialias in option.c must change as well.
127     antialias = YES;
129     helper = [[MMTextViewHelper alloc] init];
130     [helper setTextView:self];
132     [self initAtsuStyles];
134     [self registerForDraggedTypes:[NSArray arrayWithObjects:
135             NSFilenamesPboardType, NSStringPboardType, nil]];
137     return self;
140 - (void)dealloc
142     [self disposeAtsuStyles];
143     [font release];  font = nil;
144     [defaultBackgroundColor release];  defaultBackgroundColor = nil;
145     [defaultForegroundColor release];  defaultForegroundColor = nil;
147     [helper setTextView:nil];
148     [helper dealloc];  helper = nil;
150     [super dealloc];
153 - (int)maxRows
155     return maxRows;
158 - (void)getMaxRows:(int*)rows columns:(int*)cols
160     if (rows) *rows = maxRows;
161     if (cols) *cols = maxColumns;
164 - (void)setMaxRows:(int)rows columns:(int)cols
166     // NOTE: Just remember the new values, the actual resizing is done lazily.
167     maxRows = rows;
168     maxColumns = cols;
171 - (void)setDefaultColorsBackground:(NSColor *)bgColor
172                         foreground:(NSColor *)fgColor
174     if (defaultBackgroundColor != bgColor) {
175         [defaultBackgroundColor release];
176         defaultBackgroundColor = bgColor ? [bgColor retain] : nil;
177     }
179     // NOTE: The default foreground color isn't actually used for anything, but
180     // other class instances might want to be able to access it so it is stored
181     // here.
182     if (defaultForegroundColor != fgColor) {
183         [defaultForegroundColor release];
184         defaultForegroundColor = fgColor ? [fgColor retain] : nil;
185     }
188 - (void)setTextContainerInset:(NSSize)size
190     insetSize = size;
193 - (NSRect)rectForRowsInRange:(NSRange)range
195     NSRect rect = { 0, 0, 0, 0 };
196     unsigned start = range.location > maxRows ? maxRows : range.location;
197     unsigned length = range.length;
199     if (start + length > maxRows)
200         length = maxRows - start;
202     rect.origin.y = cellSize.height * start + insetSize.height;
203     rect.size.height = cellSize.height * length;
205     return rect;
208 - (NSRect)rectForColumnsInRange:(NSRange)range
210     NSRect rect = { 0, 0, 0, 0 };
211     unsigned start = range.location > maxColumns ? maxColumns : range.location;
212     unsigned length = range.length;
214     if (start+length > maxColumns)
215         length = maxColumns - start;
217     rect.origin.x = cellSize.width * start + insetSize.width;
218     rect.size.width = cellSize.width * length;
220     return rect;
224 - (void)setFont:(NSFont *)newFont
226     if (newFont && font != newFont) {
227         [font release];
228         font = [newFont retain];
229         ascender = roundf([font ascender]);
231         float em = [@"m" sizeWithAttributes:
232                 [NSDictionary dictionaryWithObject:newFont
233                                             forKey:NSFontAttributeName]].width;
234         float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
235                 floatForKey:MMCellWidthMultiplierKey];
237         // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
238         // only render at integer sizes.  Hence, we restrict the cell width to
239         // an integer here, otherwise the window width and the actual text
240         // width will not match.
241         cellSize.width = ceilf(em * cellWidthMultiplier);
242         cellSize.height = linespace + defaultLineHeightForFont(newFont);
244         [self updateAtsuStyles];
245     }
248 - (void)setWideFont:(NSFont *)newFont
252 - (NSFont *)font
254     return font;
257 - (NSSize)cellSize
259     return cellSize;
262 - (void)setLinespace:(float)newLinespace
264     linespace = newLinespace;
266     // NOTE: The linespace is added to the cell height in order for a multiline
267     // selection not to have white (background color) gaps between lines.  Also
268     // this simplifies the code a lot because there is no need to check the
269     // linespace when calculating the size of the text view etc.  When the
270     // linespace is non-zero the baseline will be adjusted as well; check
271     // MMTypesetter.
272     cellSize.height = linespace + defaultLineHeightForFont(font);
278 - (void)setShouldDrawInsertionPoint:(BOOL)on
282 - (void)setPreEditRow:(int)row column:(int)col
286 - (void)hideMarkedTextField
290 - (void)setMouseShape:(int)shape
292     [helper setMouseShape:shape];
295 - (void)setAntialias:(BOOL)state
297     antialias = state;
303 - (void)keyDown:(NSEvent *)event
305     [helper keyDown:event];
308 - (void)insertText:(id)string
310     [helper insertText:string];
313 - (void)doCommandBySelector:(SEL)selector
315     [helper doCommandBySelector:selector];
318 - (BOOL)performKeyEquivalent:(NSEvent *)event
320     return [helper performKeyEquivalent:event];
323 - (BOOL)hasMarkedText
325     return NO;
328 - (void)unmarkText
332 - (void)scrollWheel:(NSEvent *)event
334     [helper scrollWheel:event];
337 - (void)mouseDown:(NSEvent *)event
339     [helper mouseDown:event];
342 - (void)rightMouseDown:(NSEvent *)event
344     [helper mouseDown:event];
347 - (void)otherMouseDown:(NSEvent *)event
349     [helper mouseDown:event];
352 - (void)mouseUp:(NSEvent *)event
354     [helper mouseUp:event];
357 - (void)rightMouseUp:(NSEvent *)event
359     [helper mouseUp:event];
362 - (void)otherMouseUp:(NSEvent *)event
364     [helper mouseUp:event];
367 - (void)mouseDragged:(NSEvent *)event
369     [helper mouseDragged:event];
372 - (void)rightMouseDragged:(NSEvent *)event
374     [helper mouseDragged:event];
377 - (void)otherMouseDragged:(NSEvent *)event
379     [helper mouseDragged:event];
382 - (void)mouseMoved:(NSEvent *)event
384     [helper mouseMoved:event];
387 - (void)mouseEntered:(NSEvent *)event
389     [helper mouseEntered:event];
392 - (void)mouseExited:(NSEvent *)event
394     [helper mouseExited:event];
397 - (void)setFrame:(NSRect)frame
399     [super setFrame:frame];
400     [helper setFrame:frame];
403 - (void)viewDidMoveToWindow
405     [helper viewDidMoveToWindow];
408 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
410     [helper viewWillMoveToWindow:newWindow];
413 - (NSMenu*)menuForEvent:(NSEvent *)event
415     // HACK! Return nil to disable default popup menus (Vim provides its own).
416     // Called when user Ctrl-clicks in the view (this is already handled in
417     // rightMouseDown:).
418     return nil;
421 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
423     return [helper performDragOperation:sender];
426 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
428     return [helper draggingEntered:sender];
431 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
433     return [helper draggingUpdated:sender];
438 - (BOOL)mouseDownCanMoveWindow
440     return NO;
443 - (BOOL)isOpaque
445     return YES;
448 - (BOOL)acceptsFirstResponder
450     return YES;
453 - (BOOL)isFlipped
455     return NO;
458 - (void)drawRect:(NSRect)rect
460     NSRect srcRect = NSMakeRect(0, 0, imageSize.width, imageSize.height);
461     NSRect dstRect = srcRect;
463     dstRect.origin.x += insetSize.width;
464     dstRect.origin.y += insetSize.height;
466     [defaultBackgroundColor set];
467     NSRectFill(rect);
469     [contentImage drawInRect: dstRect
470                     fromRect: srcRect
471                    operation: NSCompositeCopy
472                     fraction: 1.0];
475 - (BOOL) wantsDefaultClipping
477     return NO;
481 #define MM_DEBUG_DRAWING 0
483 - (void)performBatchDrawWithData:(NSData *)data
485     const void *bytes = [data bytes];
486     const void *end = bytes + [data length];
488     if (! NSEqualSizes(imageSize, [self textAreaSize]))
489         [self resizeContentImage];
491 #if MM_DEBUG_DRAWING
492     NSLog(@"====> BEGIN %s", _cmd);
493 #endif
494     [self beginDrawing];
496     // TODO: Sanity check input
498     while (bytes < end) {
499         int type = *((int*)bytes);  bytes += sizeof(int);
501         if (ClearAllDrawType == type) {
502 #if MM_DEBUG_DRAWING
503             NSLog(@"   Clear all");
504 #endif
505             [self clearAll];
506         } else if (ClearBlockDrawType == type) {
507             unsigned color = *((unsigned*)bytes);  bytes += sizeof(unsigned);
508             int row1 = *((int*)bytes);  bytes += sizeof(int);
509             int col1 = *((int*)bytes);  bytes += sizeof(int);
510             int row2 = *((int*)bytes);  bytes += sizeof(int);
511             int col2 = *((int*)bytes);  bytes += sizeof(int);
513 #if MM_DEBUG_DRAWING
514             NSLog(@"   Clear block (%d,%d) -> (%d,%d)", row1, col1,
515                     row2,col2);
516 #endif
517             [self clearBlockFromRow:row1 column:col1
518                     toRow:row2 column:col2
519                     color:[NSColor colorWithArgbInt:color]];
520         } else if (DeleteLinesDrawType == type) {
521             unsigned color = *((unsigned*)bytes);  bytes += sizeof(unsigned);
522             int row = *((int*)bytes);  bytes += sizeof(int);
523             int count = *((int*)bytes);  bytes += sizeof(int);
524             int bot = *((int*)bytes);  bytes += sizeof(int);
525             int left = *((int*)bytes);  bytes += sizeof(int);
526             int right = *((int*)bytes);  bytes += sizeof(int);
528 #if MM_DEBUG_DRAWING
529             NSLog(@"   Delete %d line(s) from %d", count, row);
530 #endif
531             [self deleteLinesFromRow:row lineCount:count
532                     scrollBottom:bot left:left right:right
533                            color:[NSColor colorWithArgbInt:color]];
534         } else if (DrawStringDrawType == type) {
535             int bg = *((int*)bytes);  bytes += sizeof(int);
536             int fg = *((int*)bytes);  bytes += sizeof(int);
537             int sp = *((int*)bytes);  bytes += sizeof(int);
538             int row = *((int*)bytes);  bytes += sizeof(int);
539             int col = *((int*)bytes);  bytes += sizeof(int);
540             int cells = *((int*)bytes);  bytes += sizeof(int);
541             int flags = *((int*)bytes);  bytes += sizeof(int);
542             int len = *((int*)bytes);  bytes += sizeof(int);
543             // UniChar *string = (UniChar*)bytes;  bytes += len;
544             NSString *string = [[NSString alloc]
545                     initWithBytesNoCopy:(void*)bytes
546                                  length:len
547                                encoding:NSUTF8StringEncoding
548                            freeWhenDone:NO];
549             bytes += len;
550 #if MM_DEBUG_DRAWING
551             NSLog(@"   Draw string at (%d,%d) length=%d flags=%d fg=0x%x "
552                     "bg=0x%x sp=0x%x", row, col, len, flags, fg, bg, sp);
553 #endif
554             unichar *characters = malloc(sizeof(unichar) * [string length]);
555             [string getCharacters:characters];
557             [self drawString:characters
558                              length:[string length]
559                               atRow:row
560                              column:col
561                               cells:cells withFlags:flags
562                     foregroundColor:[NSColor colorWithRgbInt:fg]
563                     backgroundColor:[NSColor colorWithArgbInt:bg]
564                        specialColor:[NSColor colorWithRgbInt:sp]];
565             free(characters);
566             [string release];
567         } else if (InsertLinesDrawType == type) {
568             unsigned color = *((unsigned*)bytes);  bytes += sizeof(unsigned);
569             int row = *((int*)bytes);  bytes += sizeof(int);
570             int count = *((int*)bytes);  bytes += sizeof(int);
571             int bot = *((int*)bytes);  bytes += sizeof(int);
572             int left = *((int*)bytes);  bytes += sizeof(int);
573             int right = *((int*)bytes);  bytes += sizeof(int);
575 #if MM_DEBUG_DRAWING
576             NSLog(@"   Insert %d line(s) at row %d", count, row);
577 #endif
578             [self insertLinesAtRow:row lineCount:count
579                              scrollBottom:bot left:left right:right
580                                     color:[NSColor colorWithArgbInt:color]];
581         } else if (DrawCursorDrawType == type) {
582             unsigned color = *((unsigned*)bytes);  bytes += sizeof(unsigned);
583             int row = *((int*)bytes);  bytes += sizeof(int);
584             int col = *((int*)bytes);  bytes += sizeof(int);
585             int shape = *((int*)bytes);  bytes += sizeof(int);
586             int percent = *((int*)bytes);  bytes += sizeof(int);
588 #if MM_DEBUG_DRAWING
589             NSLog(@"   Draw cursor at (%d,%d)", row, col);
590 #endif
591             [self drawInsertionPointAtRow:row column:col shape:shape
592                                      fraction:percent
593                                         color:[NSColor colorWithRgbInt:color]];
594         } else if (DrawInvertedRectDrawType == type) {
595             int row = *((int*)bytes);  bytes += sizeof(int);
596             int col = *((int*)bytes);  bytes += sizeof(int);
597             int nr = *((int*)bytes);  bytes += sizeof(int);
598             int nc = *((int*)bytes);  bytes += sizeof(int);
599             /*int invert = *((int*)bytes);*/  bytes += sizeof(int);
601 #if MM_DEBUG_DRAWING
602             NSLog(@"   Draw inverted rect: row=%d col=%d nrows=%d ncols=%d",
603                     row, col, nr, nc);
604 #endif
605             [self drawInvertedRectAtRow:row column:col numRows:nr
606                              numColumns:nc];
607         } else if (SetCursorPosDrawType == type) {
608             // TODO: This is used for Voice Over support in MMTextView,
609             // MMAtsuiTextView currently does not support Voice Over.
610             /*cursorRow = *((int*)bytes);*/  bytes += sizeof(int);
611             /*cursorCol = *((int*)bytes);*/  bytes += sizeof(int);
612         } else {
613             NSLog(@"WARNING: Unknown draw type (type=%d)", type);
614         }
615     }
617     [self endDrawing];
619     // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
620     // and columns are changed (due to ipc delays). Force a redraw here.
621     [self setNeedsDisplay:YES];
622     // [self displayIfNeeded];
624 #if MM_DEBUG_DRAWING
625     NSLog(@"<==== END   %s", _cmd);
626 #endif
629 - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size
631     // TODO:
632     // - Rounding errors may cause size change when there should be none
633     // - Desired rows/columns shold not be 'too small'
635     // Constrain the desired size to the given size.  Values for the minimum
636     // rows and columns is taken from Vim.
637     NSSize desiredSize = [self desiredSize];
638     int desiredRows = maxRows;
639     int desiredCols = maxColumns;
641     if (size.height != desiredSize.height) {
642         float fh = cellSize.height;
643         float ih = 2 * insetSize.height;
644         if (fh < 1.0f) fh = 1.0f;
646         desiredRows = floor((size.height - ih)/fh);
647         desiredSize.height = fh*desiredRows + ih;
648     }
650     if (size.width != desiredSize.width) {
651         float fw = cellSize.width;
652         float iw = 2 * insetSize.width;
653         if (fw < 1.0f) fw = 1.0f;
655         desiredCols = floor((size.width - iw)/fw);
656         desiredSize.width = fw*desiredCols + iw;
657     }
659     if (rows) *rows = desiredRows;
660     if (cols) *cols = desiredCols;
662     return desiredSize;
665 - (NSSize)desiredSize
667     // Compute the size the text view should be for the entire text area and
668     // inset area to be visible with the present number of rows and columns.
669     return NSMakeSize(maxColumns * cellSize.width + 2 * insetSize.width,
670                       maxRows * cellSize.height + 2 * insetSize.height);
673 - (NSSize)minSize
675     // Compute the smallest size the text view is allowed to be.
676     return NSMakeSize(MMMinColumns * cellSize.width + 2 * insetSize.width,
677                       MMMinRows * cellSize.height + 2 * insetSize.height);
680 - (void)changeFont:(id)sender
682     NSFont *newFont = [sender convertFont:font];
684     if (newFont) {
685         NSString *name = [newFont displayName];
686         unsigned len = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
687         if (len > 0) {
688             NSMutableData *data = [NSMutableData data];
689             float pointSize = [newFont pointSize];
691             [data appendBytes:&pointSize length:sizeof(float)];
693             ++len;  // include NUL byte
694             [data appendBytes:&len length:sizeof(unsigned)];
695             [data appendBytes:[name UTF8String] length:len];
697             [[self vimController] sendMessage:SetFontMsgID data:data];
698         }
699     }
704 // NOTE: The menu items cut/copy/paste/undo/redo/select all/... must be bound
705 // to the same actions as in IB otherwise they will not work with dialogs.  All
706 // we do here is forward these actions to the Vim process.
708 - (IBAction)cut:(id)sender
710     [[self windowController] vimMenuItemAction:sender];
713 - (IBAction)copy:(id)sender
715     [[self windowController] vimMenuItemAction:sender];
718 - (IBAction)paste:(id)sender
720     [[self windowController] vimMenuItemAction:sender];
723 - (IBAction)undo:(id)sender
725     [[self windowController] vimMenuItemAction:sender];
728 - (IBAction)redo:(id)sender
730     [[self windowController] vimMenuItemAction:sender];
733 - (IBAction)selectAll:(id)sender
735     [[self windowController] vimMenuItemAction:sender];
738 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
740     // View is not flipped, instead the atsui code draws to a flipped image;
741     // thus we need to 'flip' the coordinate here since the column number
742     // increases in an up-to-down order.
743     point.y = [self frame].size.height - point.y;
745     NSPoint origin = { insetSize.width, insetSize.height };
747     if (!(cellSize.width > 0 && cellSize.height > 0))
748         return NO;
750     if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
751     if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
753     //NSLog(@"convertPoint:%@ toRow:%d column:%d", NSStringFromPoint(point),
754     //        *row, *column);
756     return YES;
759 @end // MMAtsuiTextView
764 @implementation MMAtsuiTextView (Private)
766 - (void)initAtsuStyles
768     int i;
769     for (i = 0; i < MMMaxCellsPerChar; i++)
770         ATSUCreateStyle(&atsuStyles[i]);
773 - (void)disposeAtsuStyles
775     int i;
777     for (i = 0; i < MMMaxCellsPerChar; i++)
778         if (atsuStyles[i] != NULL)
779         {
780             if (ATSUDisposeStyle(atsuStyles[i]) != noErr)
781                 atsuStyles[i] = NULL;
782         }
785 - (void)updateAtsuStyles
787     ATSUFontID        fontID;
788     Fixed             fontSize;
789     Fixed             fontWidth;
790     int               i;
791     CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
792     ATSStyleRenderingOptions options;
794     fontID    = [font _atsFontID];
795     fontSize  = Long2Fix([font pointSize]);
796     options   = kATSStyleApplyAntiAliasing;
798     ATSUAttributeTag attribTags[] =
799     {
800         kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
801         kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag,
802         kATSUMaxATSUITagValue + 1
803     };
805     ByteCount attribSizes[] =
806     {
807         sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
808         sizeof(CGAffineTransform), sizeof(ATSStyleRenderingOptions),
809         sizeof(font)
810     };
812     ATSUAttributeValuePtr attribValues[] =
813     {
814         &fontID, &fontSize, &fontWidth, &transform, &options, &font
815     };
817     ATSUFontFeatureType featureTypes[] = {
818         kLigaturesType, kLigaturesType
819     };
821     ATSUFontFeatureSelector featureSelectors[] = {
822         kCommonLigaturesOffSelector, kRareLigaturesOffSelector
823     };
825     for (i = 0; i < MMMaxCellsPerChar; i++)
826     {
827         fontWidth = Long2Fix(cellSize.width * (i + 1));
829         if (ATSUSetAttributes(atsuStyles[i],
830                               (sizeof attribTags) / sizeof(ATSUAttributeTag),
831                               attribTags, attribSizes, attribValues) != noErr)
832         {
833             ATSUDisposeStyle(atsuStyles[i]);
834             atsuStyles[i] = NULL;
835         }
837         // Turn off ligatures by default
838         ATSUSetFontFeatures(atsuStyles[i],
839                             sizeof(featureTypes) / sizeof(featureTypes[0]),
840                             featureTypes, featureSelectors);
841     }
844 - (MMWindowController *)windowController
846     id windowController = [[self window] windowController];
847     if ([windowController isKindOfClass:[MMWindowController class]])
848         return (MMWindowController*)windowController;
849     return nil;
852 - (MMVimController *)vimController
854     return [[self windowController] vimController];
857 @end // MMAtsuiTextView (Private)
862 @implementation MMAtsuiTextView (Drawing)
864 - (NSPoint)originForRow:(int)row column:(int)col
866     return NSMakePoint(col * cellSize.width, row * cellSize.height);
869 - (NSRect)rectFromRow:(int)row1 column:(int)col1
870                 toRow:(int)row2 column:(int)col2
872     NSPoint origin = [self originForRow: row1 column: col1];
873     return NSMakeRect(origin.x, origin.y,
874                       (col2 + 1 - col1) * cellSize.width,
875                       (row2 + 1 - row1) * cellSize.height);
878 - (NSSize)textAreaSize
880     // Calculate the (desired) size of the text area, i.e. the text view area
881     // minus the inset area.
882     return NSMakeSize(maxColumns * cellSize.width, maxRows * cellSize.height);
885 - (void)resizeContentImage
887     //NSLog(@"resizeContentImage");
888     [contentImage release];
889     contentImage = [[NSImage alloc] initWithSize:[self textAreaSize]];
890     [contentImage setFlipped: YES];
891     imageSize = [self textAreaSize];
894 - (void)beginDrawing
896     [contentImage lockFocus];
899 - (void)endDrawing
901     [contentImage unlockFocus];
904 #define atsu_style_set_bool(s, t, b) \
905     ATSUSetAttributes(s, 1, &t, &(sizeof(Boolean)), &&b);
906 #define FILL_Y(y)    (y * cellSize.height)
908 - (void)drawString:(UniChar *)string length:(UniCharCount)length
909              atRow:(int)row column:(int)col cells:(int)cells
910          withFlags:(int)flags foregroundColor:(NSColor *)fg
911    backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp
913     // 'string' consists of 'length' utf-16 code pairs and should cover 'cells'
914     // display cells (a normal character takes up one display cell, a wide
915     // character takes up two)
916     ATSUStyle       style = (flags & DRAW_WIDE) ? atsuStyles[1] : atsuStyles[0];
917     ATSUTextLayout  layout;
919     // Font selection and rendering options for ATSUI
920     ATSUAttributeTag      attribTags[3] = { kATSUQDBoldfaceTag,
921                                             kATSUFontMatrixTag,
922                                             kATSUStyleRenderingOptionsTag };
924     ByteCount             attribSizes[] = { sizeof(Boolean),
925                                             sizeof(CGAffineTransform),
926                                             sizeof(UInt32) };
927     Boolean               useBold;
928     CGAffineTransform     theTransform = CGAffineTransformMakeScale(1.0, -1.0);
929     UInt32                useAntialias;
931     ATSUAttributeValuePtr attribValues[3] = { &useBold, &theTransform,
932                                               &useAntialias };
934     useBold      = (flags & DRAW_BOLD) ? true : false;
936     if (flags & DRAW_ITALIC)
937         theTransform.c = Fix2X(kATSItalicQDSkew);
939     useAntialias = antialias ? kATSStyleApplyAntiAliasing
940                              : kATSStyleNoAntiAliasing;
942     ATSUSetAttributes(style, sizeof(attribValues) / sizeof(attribValues[0]),
943                       attribTags, attribSizes, attribValues);
945     // NSLog(@"drawString: %d", length);
947     ATSUCreateTextLayout(&layout);
948     ATSUSetTextPointerLocation(layout, string,
949                                kATSUFromTextBeginning, kATSUToTextEnd,
950                                length);
951     ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd);
953     NSRect rect = NSMakeRect(col * cellSize.width, row * cellSize.height,
954                              length * cellSize.width, cellSize.height);
955     if (flags & DRAW_WIDE)
956         rect.size.width = rect.size.width * 2;
957     CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
959     ATSUAttributeTag tags[] = { kATSUCGContextTag };
960     ByteCount sizes[] = { sizeof(CGContextRef) };
961     ATSUAttributeValuePtr values[] = { &context };
962     ATSUSetLayoutControls(layout, 1, tags, sizes, values);
964     if (! (flags & DRAW_TRANSP))
965     {
966         [bg set];
967         NSRectFill(rect);
968     }
970     [fg set];
972     ATSUSetTransientFontMatching(layout, TRUE);
973     ATSUDrawText(layout,
974                  kATSUFromTextBeginning,
975                  kATSUToTextEnd,
976                  X2Fix(rect.origin.x),
977                  X2Fix(rect.origin.y + ascender));
978     ATSUDisposeTextLayout(layout);
980     if (flags & DRAW_UNDERL)
981     {
982         [fg set];
983         NSRectFill(NSMakeRect(rect.origin.x,
984                               (row + 1) * cellSize.height + kUnderlineOffset,
985                               rect.size.width, kUnderlineHeight));
986     }
988     if (flags & DRAW_UNDERC)
989     {
990         [sp set];
992         float line_end_x = rect.origin.x + rect.size.width;
993         int i = 0;
994         NSRect line_rect = NSMakeRect(
995                 rect.origin.x,
996                 (row + 1) * cellSize.height + kUndercurlOffset,
997                 kUndercurlDotWidth, kUndercurlHeight);
999         while (line_rect.origin.x < line_end_x)
1000         {
1001             if (i % 2)
1002                 NSRectFill(line_rect);
1004             line_rect.origin.x += kUndercurlDotDistance;
1005             i++;
1006         }
1007     }
1010 - (void)scrollRect:(NSRect)rect lineCount:(int)count
1012     NSPoint destPoint = rect.origin;
1013     destPoint.y += count * cellSize.height;
1015     NSCopyBits(0, rect, destPoint);
1018 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
1019               scrollBottom:(int)bottom left:(int)left right:(int)right
1020                      color:(NSColor *)color
1022     NSRect rect = [self rectFromRow:row + count
1023                              column:left
1024                               toRow:bottom
1025                              column:right];
1026     [color set];
1027     // move rect up for count lines
1028     [self scrollRect:rect lineCount:-count];
1029     [self clearBlockFromRow:bottom - count + 1
1030                      column:left
1031                       toRow:bottom
1032                      column:right
1033                       color:color];
1036 - (void)insertLinesAtRow:(int)row lineCount:(int)count
1037             scrollBottom:(int)bottom left:(int)left right:(int)right
1038                    color:(NSColor *)color
1040     NSRect rect = [self rectFromRow:row
1041                              column:left
1042                               toRow:bottom - count
1043                              column:right];
1044     [color set];
1045     // move rect down for count lines
1046     [self scrollRect:rect lineCount:count];
1047     [self clearBlockFromRow:row
1048                      column:left
1049                       toRow:row + count - 1
1050                      column:right
1051                       color:color];
1054 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
1055                    column:(int)col2 color:(NSColor *)color
1057     [color set];
1058     NSRectFill([self rectFromRow:row1 column:col1 toRow:row2 column:col2]);
1061 - (void)clearAll
1063     [defaultBackgroundColor set];
1064     NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height));
1067 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
1068                        fraction:(int)percent color:(NSColor *)color
1070     NSPoint origin = [self originForRow:row column:col];
1071     NSRect rect = NSMakeRect(origin.x, origin.y,
1072                              cellSize.width, cellSize.height);
1074     // NSLog(@"shape = %d, fraction: %d", shape, percent);
1076     if (MMInsertionPointHorizontal == shape) {
1077         int frac = (cellSize.height * percent + 99)/100;
1078         rect.origin.y += rect.size.height - frac;
1079         rect.size.height = frac;
1080     } else if (MMInsertionPointVertical == shape) {
1081         int frac = (cellSize.width * percent + 99)/100;
1082         rect.size.width = frac;
1083     } else if (MMInsertionPointVerticalRight == shape) {
1084         int frac = (cellSize.width * percent + 99)/100;
1085         rect.origin.x += rect.size.width - frac;
1086         rect.size.width = frac;
1087     }
1089     [color set];
1090     if (MMInsertionPointHollow == shape) {
1091         NSFrameRect(rect);
1092     } else {
1093         NSRectFill(rect);
1094     }
1097 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
1098                    numColumns:(int)ncols
1100     // TODO: THIS CODE HAS NOT BEEN TESTED!
1101     CGContextRef cgctx = [[NSGraphicsContext currentContext] graphicsPort];
1102     CGContextSaveGState(cgctx);
1103     CGContextSetBlendMode(cgctx, kCGBlendModeDifference);
1104     CGContextSetRGBFillColor(cgctx, 1.0, 1.0, 1.0, 1.0);
1106     CGRect rect = { col * cellSize.width, row * cellSize.height,
1107                     ncols * cellSize.width, nrows * cellSize.height };
1108     CGContextFillRect(cgctx, rect);
1110     CGContextRestoreGState(cgctx);
1113 @end // MMAtsuiTextView (Drawing)