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 * Dispatches keyboard and mouse input to the backend. Handles drag-n-drop of
14 * files onto window. The rendering is done using ATSUI.
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].
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
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;
59 @interface MMAtsuiTextView (Private)
60 - (void)initAtsuStyles;
61 - (void)disposeAtsuStyles;
62 - (void)updateAtsuStyles;
63 - (MMWindowController *)windowController;
64 - (MMVimController *)vimController;
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;
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;
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;
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];
109 @implementation MMAtsuiTextView
111 - (id)initWithFrame:(NSRect)frame
113 if (!(self = [super initWithFrame:frame]))
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 cellSize.width = cellSize.height = 1;
121 imageSize = NSZeroSize;
122 insetSize = NSZeroSize;
124 // NOTE: If the default changes to 'NO' then the intialization of
125 // p_antialias in option.c must change as well.
128 helper = [[MMTextViewHelper alloc] init];
129 [helper setTextView:self];
131 [self initAtsuStyles];
133 [self registerForDraggedTypes:[NSArray arrayWithObjects:
134 NSFilenamesPboardType, NSStringPboardType, nil]];
141 [self disposeAtsuStyles];
142 [font release]; font = nil;
143 [defaultBackgroundColor release]; defaultBackgroundColor = nil;
144 [defaultForegroundColor release]; defaultForegroundColor = nil;
146 [helper setTextView:nil];
147 [helper dealloc]; helper = nil;
157 - (void)getMaxRows:(int*)rows columns:(int*)cols
159 if (rows) *rows = maxRows;
160 if (cols) *cols = maxColumns;
163 - (void)setMaxRows:(int)rows columns:(int)cols
165 // NOTE: Just remember the new values, the actual resizing is done lazily.
170 - (void)setDefaultColorsBackground:(NSColor *)bgColor
171 foreground:(NSColor *)fgColor
173 if (defaultBackgroundColor != bgColor) {
174 [defaultBackgroundColor release];
175 defaultBackgroundColor = bgColor ? [bgColor retain] : nil;
178 // NOTE: The default foreground color isn't actually used for anything, but
179 // other class instances might want to be able to access it so it is stored
181 if (defaultForegroundColor != fgColor) {
182 [defaultForegroundColor release];
183 defaultForegroundColor = fgColor ? [fgColor retain] : nil;
187 - (void)setTextContainerInset:(NSSize)size
192 - (NSRect)rectForRowsInRange:(NSRange)range
194 NSRect rect = { 0, 0, 0, 0 };
195 unsigned start = range.location > maxRows ? maxRows : range.location;
196 unsigned length = range.length;
198 if (start + length > maxRows)
199 length = maxRows - start;
201 rect.origin.y = cellSize.height * start + insetSize.height;
202 rect.size.height = cellSize.height * length;
207 - (NSRect)rectForColumnsInRange:(NSRange)range
209 NSRect rect = { 0, 0, 0, 0 };
210 unsigned start = range.location > maxColumns ? maxColumns : range.location;
211 unsigned length = range.length;
213 if (start+length > maxColumns)
214 length = maxColumns - start;
216 rect.origin.x = cellSize.width * start + insetSize.width;
217 rect.size.width = cellSize.width * length;
223 - (void)setFont:(NSFont *)newFont
225 if (newFont && font != newFont) {
227 font = [newFont retain];
229 float em = [@"m" sizeWithAttributes:
230 [NSDictionary dictionaryWithObject:newFont
231 forKey:NSFontAttributeName]].width;
232 float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
233 floatForKey:MMCellWidthMultiplierKey];
235 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
236 // only render at integer sizes. Hence, we restrict the cell width to
237 // an integer here, otherwise the window width and the actual text
238 // width will not match.
239 cellSize.width = ceilf(em * cellWidthMultiplier);
240 cellSize.height = linespace + defaultLineHeightForFont(newFont);
242 [self updateAtsuStyles];
246 - (void)setWideFont:(NSFont *)newFont
260 - (void)setLinespace:(float)newLinespace
262 linespace = newLinespace;
264 // NOTE: The linespace is added to the cell height in order for a multiline
265 // selection not to have white (background color) gaps between lines. Also
266 // this simplifies the code a lot because there is no need to check the
267 // linespace when calculating the size of the text view etc. When the
268 // linespace is non-zero the baseline will be adjusted as well; check
270 cellSize.height = linespace + defaultLineHeightForFont(font);
276 - (void)setShouldDrawInsertionPoint:(BOOL)on
280 - (void)setPreEditRow:(int)row column:(int)col
284 - (void)hideMarkedTextField
288 - (void)setMouseShape:(int)shape
290 [helper setMouseShape:shape];
293 - (void)setAntialias:(BOOL)state
301 - (void)keyDown:(NSEvent *)event
303 [helper keyDown:event];
306 - (void)insertText:(id)string
308 [helper insertText:string];
311 - (void)doCommandBySelector:(SEL)selector
313 [helper doCommandBySelector:selector];
316 - (BOOL)performKeyEquivalent:(NSEvent *)event
318 return [helper performKeyEquivalent:event];
321 - (BOOL)hasMarkedText
330 - (void)scrollWheel:(NSEvent *)event
332 [helper scrollWheel:event];
335 - (void)mouseDown:(NSEvent *)event
337 [helper mouseDown:event];
340 - (void)rightMouseDown:(NSEvent *)event
342 [helper mouseDown:event];
345 - (void)otherMouseDown:(NSEvent *)event
347 [helper mouseDown:event];
350 - (void)mouseUp:(NSEvent *)event
352 [helper mouseUp:event];
355 - (void)rightMouseUp:(NSEvent *)event
357 [helper mouseUp:event];
360 - (void)otherMouseUp:(NSEvent *)event
362 [helper mouseUp:event];
365 - (void)mouseDragged:(NSEvent *)event
367 [helper mouseDragged:event];
370 - (void)rightMouseDragged:(NSEvent *)event
372 [helper mouseDragged:event];
375 - (void)otherMouseDragged:(NSEvent *)event
377 [helper mouseDragged:event];
380 - (void)mouseMoved:(NSEvent *)event
382 [helper mouseMoved:event];
385 - (void)mouseEntered:(NSEvent *)event
387 [helper mouseEntered:event];
390 - (void)mouseExited:(NSEvent *)event
392 [helper mouseExited:event];
395 - (void)setFrame:(NSRect)frame
397 [super setFrame:frame];
398 [helper setFrame:frame];
401 - (void)viewDidMoveToWindow
403 [helper viewDidMoveToWindow];
406 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
408 [helper viewWillMoveToWindow:newWindow];
411 - (NSMenu*)menuForEvent:(NSEvent *)event
413 // HACK! Return nil to disable default popup menus (Vim provides its own).
414 // Called when user Ctrl-clicks in the view (this is already handled in
419 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
421 return [helper performDragOperation:sender];
424 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
426 return [helper draggingEntered:sender];
429 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
431 return [helper draggingUpdated:sender];
436 - (BOOL)mouseDownCanMoveWindow
446 - (BOOL)acceptsFirstResponder
456 - (void)drawRect:(NSRect)rect
458 NSRect srcRect = NSMakeRect(0, 0, imageSize.width, imageSize.height);
459 NSRect dstRect = srcRect;
461 dstRect.origin.x += insetSize.width;
462 dstRect.origin.y += insetSize.height;
464 [defaultBackgroundColor set];
467 [contentImage drawInRect: dstRect
469 operation: NSCompositeCopy
473 - (BOOL) wantsDefaultClipping
479 #define MM_DEBUG_DRAWING 0
481 - (void)performBatchDrawWithData:(NSData *)data
483 const void *bytes = [data bytes];
484 const void *end = bytes + [data length];
486 if (! NSEqualSizes(imageSize, [self textAreaSize]))
487 [self resizeContentImage];
490 NSLog(@"====> BEGIN %s", _cmd);
494 // TODO: Sanity check input
496 while (bytes < end) {
497 int type = *((int*)bytes); bytes += sizeof(int);
499 if (ClearAllDrawType == type) {
501 NSLog(@" Clear all");
504 } else if (ClearBlockDrawType == type) {
505 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
506 int row1 = *((int*)bytes); bytes += sizeof(int);
507 int col1 = *((int*)bytes); bytes += sizeof(int);
508 int row2 = *((int*)bytes); bytes += sizeof(int);
509 int col2 = *((int*)bytes); bytes += sizeof(int);
512 NSLog(@" Clear block (%d,%d) -> (%d,%d)", row1, col1,
515 [self clearBlockFromRow:row1 column:col1
516 toRow:row2 column:col2
517 color:[NSColor colorWithArgbInt:color]];
518 } else if (DeleteLinesDrawType == type) {
519 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
520 int row = *((int*)bytes); bytes += sizeof(int);
521 int count = *((int*)bytes); bytes += sizeof(int);
522 int bot = *((int*)bytes); bytes += sizeof(int);
523 int left = *((int*)bytes); bytes += sizeof(int);
524 int right = *((int*)bytes); bytes += sizeof(int);
527 NSLog(@" Delete %d line(s) from %d", count, row);
529 [self deleteLinesFromRow:row lineCount:count
530 scrollBottom:bot left:left right:right
531 color:[NSColor colorWithArgbInt:color]];
532 } else if (DrawStringDrawType == type) {
533 int bg = *((int*)bytes); bytes += sizeof(int);
534 int fg = *((int*)bytes); bytes += sizeof(int);
535 int sp = *((int*)bytes); bytes += sizeof(int);
536 int row = *((int*)bytes); bytes += sizeof(int);
537 int col = *((int*)bytes); bytes += sizeof(int);
538 int cells = *((int*)bytes); bytes += sizeof(int);
539 int flags = *((int*)bytes); bytes += sizeof(int);
540 int len = *((int*)bytes); bytes += sizeof(int);
541 // UniChar *string = (UniChar*)bytes; bytes += len;
542 NSString *string = [[NSString alloc]
543 initWithBytesNoCopy:(void*)bytes
545 encoding:NSUTF8StringEncoding
549 NSLog(@" Draw string at (%d,%d) length=%d flags=%d fg=0x%x "
550 "bg=0x%x sp=0x%x", row, col, len, flags, fg, bg, sp);
552 unichar *characters = malloc(sizeof(unichar) * [string length]);
553 [string getCharacters:characters];
555 [self drawString:characters
556 length:[string length]
559 cells:cells withFlags:flags
560 foregroundColor:[NSColor colorWithRgbInt:fg]
561 backgroundColor:[NSColor colorWithArgbInt:bg]
562 specialColor:[NSColor colorWithRgbInt:sp]];
565 } else if (InsertLinesDrawType == type) {
566 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
567 int row = *((int*)bytes); bytes += sizeof(int);
568 int count = *((int*)bytes); bytes += sizeof(int);
569 int bot = *((int*)bytes); bytes += sizeof(int);
570 int left = *((int*)bytes); bytes += sizeof(int);
571 int right = *((int*)bytes); bytes += sizeof(int);
574 NSLog(@" Insert %d line(s) at row %d", count, row);
576 [self insertLinesAtRow:row lineCount:count
577 scrollBottom:bot left:left right:right
578 color:[NSColor colorWithArgbInt:color]];
579 } else if (DrawCursorDrawType == type) {
580 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
581 int row = *((int*)bytes); bytes += sizeof(int);
582 int col = *((int*)bytes); bytes += sizeof(int);
583 int shape = *((int*)bytes); bytes += sizeof(int);
584 int percent = *((int*)bytes); bytes += sizeof(int);
587 NSLog(@" Draw cursor at (%d,%d)", row, col);
589 [self drawInsertionPointAtRow:row column:col shape:shape
591 color:[NSColor colorWithRgbInt:color]];
592 } else if (DrawInvertedRectDrawType == type) {
593 int row = *((int*)bytes); bytes += sizeof(int);
594 int col = *((int*)bytes); bytes += sizeof(int);
595 int nr = *((int*)bytes); bytes += sizeof(int);
596 int nc = *((int*)bytes); bytes += sizeof(int);
597 /*int invert = *((int*)bytes);*/ bytes += sizeof(int);
600 NSLog(@" Draw inverted rect: row=%d col=%d nrows=%d ncols=%d",
603 [self drawInvertedRectAtRow:row column:col numRows:nr
605 } else if (SetCursorPosDrawType == type) {
606 // TODO: This is used for Voice Over support in MMTextView,
607 // MMAtsuiTextView currently does not support Voice Over.
608 /*cursorRow = *((int*)bytes);*/ bytes += sizeof(int);
609 /*cursorCol = *((int*)bytes);*/ bytes += sizeof(int);
611 NSLog(@"WARNING: Unknown draw type (type=%d)", type);
617 // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
618 // and columns are changed (due to ipc delays). Force a redraw here.
619 [self setNeedsDisplay:YES];
620 // [self displayIfNeeded];
623 NSLog(@"<==== END %s", _cmd);
627 - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size
630 // - Rounding errors may cause size change when there should be none
631 // - Desired rows/columns shold not be 'too small'
633 // Constrain the desired size to the given size. Values for the minimum
634 // rows and columns is taken from Vim.
635 NSSize desiredSize = [self desiredSize];
636 int desiredRows = maxRows;
637 int desiredCols = maxColumns;
639 if (size.height != desiredSize.height) {
640 float fh = cellSize.height;
641 float ih = 2 * insetSize.height;
642 if (fh < 1.0f) fh = 1.0f;
644 desiredRows = floor((size.height - ih)/fh);
645 desiredSize.height = fh*desiredRows + ih;
648 if (size.width != desiredSize.width) {
649 float fw = cellSize.width;
650 float iw = 2 * insetSize.width;
651 if (fw < 1.0f) fw = 1.0f;
653 desiredCols = floor((size.width - iw)/fw);
654 desiredSize.width = fw*desiredCols + iw;
657 if (rows) *rows = desiredRows;
658 if (cols) *cols = desiredCols;
663 - (NSSize)desiredSize
665 // Compute the size the text view should be for the entire text area and
666 // inset area to be visible with the present number of rows and columns.
667 return NSMakeSize(maxColumns * cellSize.width + 2 * insetSize.width,
668 maxRows * cellSize.height + 2 * insetSize.height);
673 // Compute the smallest size the text view is allowed to be.
674 return NSMakeSize(MMMinColumns * cellSize.width + 2 * insetSize.width,
675 MMMinRows * cellSize.height + 2 * insetSize.height);
678 - (void)changeFont:(id)sender
680 NSFont *newFont = [sender convertFont:font];
683 NSString *name = [newFont displayName];
684 unsigned len = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
686 NSMutableData *data = [NSMutableData data];
687 float pointSize = [newFont pointSize];
689 [data appendBytes:&pointSize length:sizeof(float)];
691 ++len; // include NUL byte
692 [data appendBytes:&len length:sizeof(unsigned)];
693 [data appendBytes:[name UTF8String] length:len];
695 [[self vimController] sendMessage:SetFontMsgID data:data];
702 // NOTE: The menu items cut/copy/paste/undo/redo/select all/... must be bound
703 // to the same actions as in IB otherwise they will not work with dialogs. All
704 // we do here is forward these actions to the Vim process.
706 - (IBAction)cut:(id)sender
708 [[self windowController] vimMenuItemAction:sender];
711 - (IBAction)copy:(id)sender
713 [[self windowController] vimMenuItemAction:sender];
716 - (IBAction)paste:(id)sender
718 [[self windowController] vimMenuItemAction:sender];
721 - (IBAction)undo:(id)sender
723 [[self windowController] vimMenuItemAction:sender];
726 - (IBAction)redo:(id)sender
728 [[self windowController] vimMenuItemAction:sender];
731 - (IBAction)selectAll:(id)sender
733 [[self windowController] vimMenuItemAction:sender];
736 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
738 // View is not flipped, instead the atsui code draws to a flipped image;
739 // thus we need to 'flip' the coordinate here since the column number
740 // increases in an up-to-down order.
741 point.y = [self frame].size.height - point.y;
743 NSPoint origin = { insetSize.width, insetSize.height };
745 if (!(cellSize.width > 0 && cellSize.height > 0))
748 if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
749 if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
751 //NSLog(@"convertPoint:%@ toRow:%d column:%d", NSStringFromPoint(point),
757 @end // MMAtsuiTextView
762 @implementation MMAtsuiTextView (Private)
764 - (void)initAtsuStyles
767 for (i = 0; i < MMMaxCellsPerChar; i++)
768 ATSUCreateStyle(&atsuStyles[i]);
771 - (void)disposeAtsuStyles
775 for (i = 0; i < MMMaxCellsPerChar; i++)
776 if (atsuStyles[i] != NULL)
778 if (ATSUDisposeStyle(atsuStyles[i]) != noErr)
779 atsuStyles[i] = NULL;
783 - (void)updateAtsuStyles
789 CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
790 ATSStyleRenderingOptions options;
792 fontID = [font _atsFontID];
793 fontSize = Long2Fix([font pointSize]);
794 options = kATSStyleApplyAntiAliasing;
796 ATSUAttributeTag attribTags[] =
798 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
799 kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag,
800 kATSUMaxATSUITagValue + 1
803 ByteCount attribSizes[] =
805 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
806 sizeof(CGAffineTransform), sizeof(ATSStyleRenderingOptions),
810 ATSUAttributeValuePtr attribValues[] =
812 &fontID, &fontSize, &fontWidth, &transform, &options, &font
815 ATSUFontFeatureType featureTypes[] = {
816 kLigaturesType, kLigaturesType
819 ATSUFontFeatureSelector featureSelectors[] = {
820 kCommonLigaturesOffSelector, kRareLigaturesOffSelector
823 for (i = 0; i < MMMaxCellsPerChar; i++)
825 fontWidth = Long2Fix(cellSize.width * (i + 1));
827 if (ATSUSetAttributes(atsuStyles[i],
828 (sizeof attribTags) / sizeof(ATSUAttributeTag),
829 attribTags, attribSizes, attribValues) != noErr)
831 ATSUDisposeStyle(atsuStyles[i]);
832 atsuStyles[i] = NULL;
835 // Turn off ligatures by default
836 ATSUSetFontFeatures(atsuStyles[i],
837 sizeof(featureTypes) / sizeof(featureTypes[0]),
838 featureTypes, featureSelectors);
842 - (MMWindowController *)windowController
844 id windowController = [[self window] windowController];
845 if ([windowController isKindOfClass:[MMWindowController class]])
846 return (MMWindowController*)windowController;
850 - (MMVimController *)vimController
852 return [[self windowController] vimController];
855 @end // MMAtsuiTextView (Private)
860 @implementation MMAtsuiTextView (Drawing)
862 - (NSPoint)originForRow:(int)row column:(int)col
864 return NSMakePoint(col * cellSize.width, row * cellSize.height);
867 - (NSRect)rectFromRow:(int)row1 column:(int)col1
868 toRow:(int)row2 column:(int)col2
870 NSPoint origin = [self originForRow: row1 column: col1];
871 return NSMakeRect(origin.x, origin.y,
872 (col2 + 1 - col1) * cellSize.width,
873 (row2 + 1 - row1) * cellSize.height);
876 - (NSSize)textAreaSize
878 // Calculate the (desired) size of the text area, i.e. the text view area
879 // minus the inset area.
880 return NSMakeSize(maxColumns * cellSize.width, maxRows * cellSize.height);
883 - (void)resizeContentImage
885 //NSLog(@"resizeContentImage");
886 [contentImage release];
887 contentImage = [[NSImage alloc] initWithSize:[self textAreaSize]];
888 [contentImage setFlipped: YES];
889 imageSize = [self textAreaSize];
894 [contentImage lockFocus];
899 [contentImage unlockFocus];
902 #define atsu_style_set_bool(s, t, b) \
903 ATSUSetAttributes(s, 1, &t, &(sizeof(Boolean)), &&b);
904 #define FILL_Y(y) (y * cellSize.height)
906 - (void)drawString:(UniChar *)string length:(UniCharCount)length
907 atRow:(int)row column:(int)col cells:(int)cells
908 withFlags:(int)flags foregroundColor:(NSColor *)fg
909 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp
911 // 'string' consists of 'length' utf-16 code pairs and should cover 'cells'
912 // display cells (a normal character takes up one display cell, a wide
913 // character takes up two)
914 ATSUStyle style = (flags & DRAW_WIDE) ? atsuStyles[1] : atsuStyles[0];
915 ATSUTextLayout layout;
917 // Font selection and rendering options for ATSUI
918 ATSUAttributeTag attribTags[3] = { kATSUQDBoldfaceTag,
920 kATSUStyleRenderingOptionsTag };
922 ByteCount attribSizes[] = { sizeof(Boolean),
923 sizeof(CGAffineTransform),
926 CGAffineTransform theTransform = CGAffineTransformMakeScale(1.0, -1.0);
929 ATSUAttributeValuePtr attribValues[3] = { &useBold, &theTransform,
932 useBold = (flags & DRAW_BOLD) ? true : false;
934 if (flags & DRAW_ITALIC)
935 theTransform.c = Fix2X(kATSItalicQDSkew);
937 useAntialias = antialias ? kATSStyleApplyAntiAliasing
938 : kATSStyleNoAntiAliasing;
940 ATSUSetAttributes(style, sizeof(attribValues) / sizeof(attribValues[0]),
941 attribTags, attribSizes, attribValues);
943 // NSLog(@"drawString: %d", length);
945 ATSUCreateTextLayout(&layout);
946 ATSUSetTextPointerLocation(layout, string,
947 kATSUFromTextBeginning, kATSUToTextEnd,
949 ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd);
951 NSRect rect = NSMakeRect(col * cellSize.width, row * cellSize.height,
952 length * cellSize.width, cellSize.height);
953 if (flags & DRAW_WIDE)
954 rect.size.width = rect.size.width * 2;
955 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
957 ATSUAttributeTag tags[] = { kATSUCGContextTag };
958 ByteCount sizes[] = { sizeof(CGContextRef) };
959 ATSUAttributeValuePtr values[] = { &context };
960 ATSUSetLayoutControls(layout, 1, tags, sizes, values);
962 if (! (flags & DRAW_TRANSP))
970 ATSUSetTransientFontMatching(layout, TRUE);
972 kATSUFromTextBeginning,
974 X2Fix(rect.origin.x),
975 X2Fix(rect.origin.y + [font ascender]));
976 ATSUDisposeTextLayout(layout);
978 if (flags & DRAW_UNDERL)
981 NSRectFill(NSMakeRect(rect.origin.x,
982 (row + 1) * cellSize.height + kUnderlineOffset,
983 rect.size.width, kUnderlineHeight));
986 if (flags & DRAW_UNDERC)
990 float line_end_x = rect.origin.x + rect.size.width;
992 NSRect line_rect = NSMakeRect(
994 (row + 1) * cellSize.height + kUndercurlOffset,
995 kUndercurlDotWidth, kUndercurlHeight);
997 while (line_rect.origin.x < line_end_x)
1000 NSRectFill(line_rect);
1002 line_rect.origin.x += kUndercurlDotDistance;
1008 - (void)scrollRect:(NSRect)rect lineCount:(int)count
1010 NSPoint destPoint = rect.origin;
1011 destPoint.y += count * cellSize.height;
1013 NSCopyBits(0, rect, destPoint);
1016 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
1017 scrollBottom:(int)bottom left:(int)left right:(int)right
1018 color:(NSColor *)color
1020 NSRect rect = [self rectFromRow:row + count
1025 // move rect up for count lines
1026 [self scrollRect:rect lineCount:-count];
1027 [self clearBlockFromRow:bottom - count + 1
1034 - (void)insertLinesAtRow:(int)row lineCount:(int)count
1035 scrollBottom:(int)bottom left:(int)left right:(int)right
1036 color:(NSColor *)color
1038 NSRect rect = [self rectFromRow:row
1040 toRow:bottom - count
1043 // move rect down for count lines
1044 [self scrollRect:rect lineCount:count];
1045 [self clearBlockFromRow:row
1047 toRow:row + count - 1
1052 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
1053 column:(int)col2 color:(NSColor *)color
1056 NSRectFill([self rectFromRow:row1 column:col1 toRow:row2 column:col2]);
1061 [defaultBackgroundColor set];
1062 NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height));
1065 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
1066 fraction:(int)percent color:(NSColor *)color
1068 NSPoint origin = [self originForRow:row column:col];
1069 NSRect rect = NSMakeRect(origin.x, origin.y,
1070 cellSize.width, cellSize.height);
1072 // NSLog(@"shape = %d, fraction: %d", shape, percent);
1074 if (MMInsertionPointHorizontal == shape) {
1075 int frac = (cellSize.height * percent + 99)/100;
1076 rect.origin.y += rect.size.height - frac;
1077 rect.size.height = frac;
1078 } else if (MMInsertionPointVertical == shape) {
1079 int frac = (cellSize.width * percent + 99)/100;
1080 rect.size.width = frac;
1081 } else if (MMInsertionPointVerticalRight == shape) {
1082 int frac = (cellSize.width * percent + 99)/100;
1083 rect.origin.x += rect.size.width - frac;
1084 rect.size.width = frac;
1088 if (MMInsertionPointHollow == shape) {
1095 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
1096 numColumns:(int)ncols
1098 // TODO: THIS CODE HAS NOT BEEN TESTED!
1099 CGContextRef cgctx = [[NSGraphicsContext currentContext] graphicsPort];
1100 CGContextSaveGState(cgctx);
1101 CGContextSetBlendMode(cgctx, kCGBlendModeDifference);
1102 CGContextSetRGBFillColor(cgctx, 1.0, 1.0, 1.0, 1.0);
1104 CGRect rect = { col * cellSize.width, row * cellSize.height,
1105 ncols * cellSize.width, nrows * cellSize.height };
1106 CGContextFillRect(cgctx, rect);
1108 CGContextRestoreGState(cgctx);
1111 @end // MMAtsuiTextView (Drawing)