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
30 #import "MMAppController.h"
31 #import "MMAtsuiTextView.h"
32 #import "MMTextViewHelper.h"
33 #import "MMVimController.h"
34 #import "MMWindowController.h"
35 #import "Miscellaneous.h"
38 // TODO: What does DRAW_TRANSP flag do? If the background isn't drawn when
39 // this flag is set, then sometimes the character after the cursor becomes
40 // blank. Everything seems to work fine by just ignoring this flag.
41 #define DRAW_TRANSP 0x01 /* draw with transparant bg */
42 #define DRAW_BOLD 0x02 /* draw bold text */
43 #define DRAW_UNDERL 0x04 /* draw underline text */
44 #define DRAW_UNDERC 0x08 /* draw undercurl text */
45 #define DRAW_ITALIC 0x10 /* draw italic text */
46 #define DRAW_CURSOR 0x20
48 #define kUnderlineOffset (-2)
49 #define kUnderlineHeight 1
50 #define kUndercurlHeight 2
51 #define kUndercurlOffset (-2)
52 #define kUndercurlDotWidth 2
53 #define kUndercurlDotDistance 2
56 @interface NSFont (AppKitPrivate)
57 - (ATSUFontID) _atsFontID;
61 @interface MMAtsuiTextView (Private)
62 - (void)initAtsuStyles;
63 - (void)disposeAtsuStyles;
64 - (void)updateAtsuStyles;
65 - (MMWindowController *)windowController;
66 - (MMVimController *)vimController;
70 @interface MMAtsuiTextView (Drawing)
71 - (NSPoint)originForRow:(int)row column:(int)column;
72 - (NSRect)rectFromRow:(int)row1 column:(int)col1
73 toRow:(int)row2 column:(int)col2;
74 - (NSSize)textAreaSize;
75 - (void)resizeContentImage;
78 - (void)drawString:(UniChar *)string length:(UniCharCount)length
79 atRow:(int)row column:(int)col cells:(int)cells
80 withFlags:(int)flags foregroundColor:(NSColor *)fg
81 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp;
82 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
83 scrollBottom:(int)bottom left:(int)left right:(int)right
84 color:(NSColor *)color;
85 - (void)insertLinesAtRow:(int)row lineCount:(int)count
86 scrollBottom:(int)bottom left:(int)left right:(int)right
87 color:(NSColor *)color;
88 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
89 column:(int)col2 color:(NSColor *)color;
91 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
92 fraction:(int)percent;
93 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
94 numColumns:(int)ncols;
100 defaultLineHeightForFont(NSFont *font)
102 // HACK: -[NSFont defaultLineHeightForFont] is deprecated but since the
103 // ATSUI renderer does not use NSLayoutManager we create one temporarily.
104 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
105 float height = [lm defaultLineHeightForFont:font];
111 @implementation MMAtsuiTextView
113 - (id)initWithFrame:(NSRect)frame
115 if (!(self = [super initWithFrame:frame]))
118 // NOTE! It does not matter which font is set here, Vim will set its
119 // own font on startup anyway. Just set some bogus values.
120 font = [[NSFont userFixedPitchFontOfSize:0] retain];
122 cellSize.width = cellSize.height = 1;
124 imageSize = NSZeroSize;
125 insetSize = NSZeroSize;
127 // NOTE: If the default changes to 'NO' then the intialization of
128 // p_antialias in option.c must change as well.
131 helper = [[MMTextViewHelper alloc] init];
132 [helper setTextView:self];
134 [self initAtsuStyles];
136 [self registerForDraggedTypes:[NSArray arrayWithObjects:
137 NSFilenamesPboardType, NSStringPboardType, nil]];
146 [self disposeAtsuStyles];
147 [font release]; font = nil;
148 [defaultBackgroundColor release]; defaultBackgroundColor = nil;
149 [defaultForegroundColor release]; defaultForegroundColor = nil;
151 [helper setTextView:nil];
152 [helper release]; helper = nil;
167 - (void)getMaxRows:(int*)rows columns:(int*)cols
169 if (rows) *rows = maxRows;
170 if (cols) *cols = maxColumns;
173 - (void)setMaxRows:(int)rows columns:(int)cols
175 // NOTE: Just remember the new values, the actual resizing is done lazily.
180 - (void)setDefaultColorsBackground:(NSColor *)bgColor
181 foreground:(NSColor *)fgColor
183 if (defaultBackgroundColor != bgColor) {
184 [defaultBackgroundColor release];
185 defaultBackgroundColor = bgColor ? [bgColor retain] : nil;
188 if (defaultForegroundColor != fgColor) {
189 [defaultForegroundColor release];
190 defaultForegroundColor = fgColor ? [fgColor retain] : nil;
194 - (NSColor *)defaultBackgroundColor
196 return defaultBackgroundColor;
199 - (NSColor *)defaultForegroundColor
201 return defaultForegroundColor;
204 - (void)setTextContainerInset:(NSSize)size
209 - (NSRect)rectForRowsInRange:(NSRange)range
211 NSRect rect = { 0, 0, 0, 0 };
212 unsigned start = range.location > maxRows ? maxRows : range.location;
213 unsigned length = range.length;
215 if (start + length > maxRows)
216 length = maxRows - start;
218 rect.origin.y = cellSize.height * start + insetSize.height;
219 rect.size.height = cellSize.height * length;
224 - (NSRect)rectForColumnsInRange:(NSRange)range
226 NSRect rect = { 0, 0, 0, 0 };
227 unsigned start = range.location > maxColumns ? maxColumns : range.location;
228 unsigned length = range.length;
230 if (start+length > maxColumns)
231 length = maxColumns - start;
233 rect.origin.x = cellSize.width * start + insetSize.width;
234 rect.size.width = cellSize.width * length;
240 - (void)setFont:(NSFont *)newFont
242 if (newFont && font != newFont) {
244 font = [newFont retain];
245 ascender = roundf([font ascender]);
247 float em = [@"m" sizeWithAttributes:
248 [NSDictionary dictionaryWithObject:newFont
249 forKey:NSFontAttributeName]].width;
250 float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
251 floatForKey:MMCellWidthMultiplierKey];
253 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
254 // only render at integer sizes. Hence, we restrict the cell width to
255 // an integer here, otherwise the window width and the actual text
256 // width will not match.
257 cellSize.width = ceilf(em * cellWidthMultiplier);
258 cellSize.height = linespace + defaultLineHeightForFont(newFont);
260 [self updateAtsuStyles];
264 - (void)setWideFont:(NSFont *)newFont
267 if (font) [self setWideFont:font];
268 } else if (newFont != fontWide) {
269 float pointSize = [newFont pointSize];
270 NSFontDescriptor *desc = [newFont fontDescriptor];
271 NSDictionary *dictWide = [NSDictionary
272 dictionaryWithObject:[NSNumber numberWithFloat:2*cellSize.width]
273 forKey:NSFontFixedAdvanceAttribute];
274 desc = [desc fontDescriptorByAddingAttributes:dictWide];
275 fontWide = [NSFont fontWithDescriptor:desc size:pointSize];
295 - (void)setLinespace:(float)newLinespace
297 linespace = newLinespace;
299 // NOTE: The linespace is added to the cell height in order for a multiline
300 // selection not to have white (background color) gaps between lines. Also
301 // this simplifies the code a lot because there is no need to check the
302 // linespace when calculating the size of the text view etc. When the
303 // linespace is non-zero the baseline will be adjusted as well; check
305 cellSize.height = linespace + defaultLineHeightForFont(font);
310 - (void)setShouldDrawInsertionPoint:(BOOL)on
314 - (void)setPreEditRow:(int)row column:(int)col
316 [helper setPreEditRow:row column:col];
319 - (void)setMouseShape:(int)shape
321 [helper setMouseShape:shape];
324 - (void)setAntialias:(BOOL)state
329 - (void)setImControl:(BOOL)enable
331 [helper setImControl:enable];
334 - (void)activateIm:(BOOL)enable
336 [helper activateIm:enable];
339 - (void)keyDown:(NSEvent *)event
341 [helper keyDown:event];
344 - (void)insertText:(id)string
346 [helper insertText:string];
349 - (void)doCommandBySelector:(SEL)selector
351 [helper doCommandBySelector:selector];
354 - (BOOL)performKeyEquivalent:(NSEvent *)event
356 return [helper performKeyEquivalent:event];
359 - (BOOL)hasMarkedText
361 return [helper hasMarkedText];
364 - (NSRange)markedRange
366 return [helper markedRange];
369 - (NSDictionary *)markedTextAttributes
371 return [helper markedTextAttributes];
374 - (void)setMarkedTextAttributes:(NSDictionary *)attr
376 [helper setMarkedTextAttributes:attr];
379 - (void)setMarkedText:(id)text selectedRange:(NSRange)range
381 [helper setMarkedText:text selectedRange:range];
389 - (void)scrollWheel:(NSEvent *)event
391 [helper scrollWheel:event];
394 - (void)mouseDown:(NSEvent *)event
396 [helper mouseDown:event];
399 - (void)rightMouseDown:(NSEvent *)event
401 [helper mouseDown:event];
404 - (void)otherMouseDown:(NSEvent *)event
406 [helper mouseDown:event];
409 - (void)mouseUp:(NSEvent *)event
411 [helper mouseUp:event];
414 - (void)rightMouseUp:(NSEvent *)event
416 [helper mouseUp:event];
419 - (void)otherMouseUp:(NSEvent *)event
421 [helper mouseUp:event];
424 - (void)mouseDragged:(NSEvent *)event
426 [helper mouseDragged:event];
429 - (void)rightMouseDragged:(NSEvent *)event
431 [helper mouseDragged:event];
434 - (void)otherMouseDragged:(NSEvent *)event
436 [helper mouseDragged:event];
439 - (void)mouseMoved:(NSEvent *)event
441 [helper mouseMoved:event];
444 - (void)mouseEntered:(NSEvent *)event
446 [helper mouseEntered:event];
449 - (void)mouseExited:(NSEvent *)event
451 [helper mouseExited:event];
454 - (void)setFrame:(NSRect)frame
456 [super setFrame:frame];
457 [helper setFrame:frame];
460 - (void)viewDidMoveToWindow
462 [helper viewDidMoveToWindow];
465 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
467 [helper viewWillMoveToWindow:newWindow];
470 - (NSMenu*)menuForEvent:(NSEvent *)event
472 // HACK! Return nil to disable default popup menus (Vim provides its own).
473 // Called when user Ctrl-clicks in the view (this is already handled in
478 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
480 return [helper performDragOperation:sender];
483 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
485 return [helper draggingEntered:sender];
488 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
490 return [helper draggingUpdated:sender];
495 - (BOOL)mouseDownCanMoveWindow
505 - (BOOL)acceptsFirstResponder
515 - (void)drawRect:(NSRect)rect
517 [defaultBackgroundColor set];
520 NSPoint pt = { insetSize.width, insetSize.height };
521 [contentImage compositeToPoint:pt operation:NSCompositeCopy];
523 #ifdef INCLUDE_OLD_IM_CODE
524 if ([self hasMarkedText] && ![helper useInlineIm]) {
525 int len = [[helper markedText] length];
527 int cols = maxColumns - [helper preEditColumn];
528 NSFont *theFont = [[self markedTextAttributes]
529 valueForKey:NSFontAttributeName];
530 if (theFont == [self fontWide])
533 int lend = cols > len ? len : cols;
534 NSAttributedString *aString = [[helper markedText]
535 attributedSubstringFromRange:NSMakeRange(done, lend)];
536 NSPoint pt = [self pointForRow:[helper preEditRow]
537 column:[helper preEditColumn]];
538 [aString drawAtPoint:pt];
542 rows = (len - done) / (maxColumns / 2) + 1;
543 for (r = 1; r <= rows; r++) {
544 lend = len - done > maxColumns / 2
545 ? maxColumns / 2 : len - done;
546 aString = [[helper markedText] attributedSubstringFromRange:
547 NSMakeRange(done, lend)];
548 NSPoint pt = [self pointForRow:[helper preEditRow]+r
550 [aString drawAtPoint:pt];
555 rows = maxRows - 1 - [helper preEditRow];
556 cols = [helper preEditColumn];
557 if (theFont == fontWide) {
558 cols += ([helper imRange].location+[helper imRange].length) * 2;
559 if (cols >= maxColumns - 1) {
560 rows -= cols / maxColumns;
561 cols = cols % 2 ? cols % maxColumns + 1 :
565 cols += ([helper imRange].location+[helper imRange].length);
566 if (cols >= maxColumns) {
567 rows -= cols / maxColumns;
568 cols = cols % 2 ? cols % maxColumns + 1 :
573 // TODO: Could IM be in "right-left" mode? If so the insertion point
574 // will be on the wrong side.
575 [self drawInsertionPointAtRow:rows
577 shape:MMInsertionPointVertical
580 #endif // INCLUDE_OLD_IM_CODE
583 - (BOOL) wantsDefaultClipping
589 #define MM_DEBUG_DRAWING 0
591 - (void)performBatchDrawWithData:(NSData *)data
593 const void *bytes = [data bytes];
594 const void *end = bytes + [data length];
596 if (! NSEqualSizes(imageSize, [self textAreaSize]))
597 [self resizeContentImage];
600 ASLogDebug(@"====> BEGIN %s", _cmd);
604 // TODO: Sanity check input
606 while (bytes < end) {
607 int type = *((int*)bytes); bytes += sizeof(int);
609 if (ClearAllDrawType == type) {
611 ASLogDebug(@" Clear all");
614 } else if (ClearBlockDrawType == type) {
615 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
616 int row1 = *((int*)bytes); bytes += sizeof(int);
617 int col1 = *((int*)bytes); bytes += sizeof(int);
618 int row2 = *((int*)bytes); bytes += sizeof(int);
619 int col2 = *((int*)bytes); bytes += sizeof(int);
622 ASLogDebug(@" Clear block (%d,%d) -> (%d,%d)", row1, col1,
625 [self clearBlockFromRow:row1 column:col1
626 toRow:row2 column:col2
627 color:[NSColor colorWithArgbInt:color]];
628 } else if (DeleteLinesDrawType == type) {
629 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
630 int row = *((int*)bytes); bytes += sizeof(int);
631 int count = *((int*)bytes); bytes += sizeof(int);
632 int bot = *((int*)bytes); bytes += sizeof(int);
633 int left = *((int*)bytes); bytes += sizeof(int);
634 int right = *((int*)bytes); bytes += sizeof(int);
637 ASLogDebug(@" Delete %d line(s) from %d", count, row);
639 [self deleteLinesFromRow:row lineCount:count
640 scrollBottom:bot left:left right:right
641 color:[NSColor colorWithArgbInt:color]];
642 } else if (DrawStringDrawType == type) {
643 int bg = *((int*)bytes); bytes += sizeof(int);
644 int fg = *((int*)bytes); bytes += sizeof(int);
645 int sp = *((int*)bytes); bytes += sizeof(int);
646 int row = *((int*)bytes); bytes += sizeof(int);
647 int col = *((int*)bytes); bytes += sizeof(int);
648 int cells = *((int*)bytes); bytes += sizeof(int);
649 int flags = *((int*)bytes); bytes += sizeof(int);
650 int len = *((int*)bytes); bytes += sizeof(int);
651 // UniChar *string = (UniChar*)bytes; bytes += len;
652 NSString *string = [[NSString alloc]
653 initWithBytesNoCopy:(void*)bytes
655 encoding:NSUTF8StringEncoding
659 ASLogDebug(@" Draw string at (%d,%d) length=%d flags=%d fg=0x%x "
660 "bg=0x%x sp=0x%x", row, col, len, flags, fg, bg, sp);
662 unichar *characters = malloc(sizeof(unichar) * [string length]);
663 [string getCharacters:characters];
665 [self drawString:characters
666 length:[string length]
669 cells:cells withFlags:flags
670 foregroundColor:[NSColor colorWithRgbInt:fg]
671 backgroundColor:[NSColor colorWithArgbInt:bg]
672 specialColor:[NSColor colorWithRgbInt:sp]];
675 } else if (InsertLinesDrawType == type) {
676 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
677 int row = *((int*)bytes); bytes += sizeof(int);
678 int count = *((int*)bytes); bytes += sizeof(int);
679 int bot = *((int*)bytes); bytes += sizeof(int);
680 int left = *((int*)bytes); bytes += sizeof(int);
681 int right = *((int*)bytes); bytes += sizeof(int);
684 ASLogDebug(@" Insert %d line(s) at row %d", count, row);
686 [self insertLinesAtRow:row lineCount:count
687 scrollBottom:bot left:left right:right
688 color:[NSColor colorWithArgbInt:color]];
689 } else if (DrawCursorDrawType == type) {
690 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
691 int row = *((int*)bytes); bytes += sizeof(int);
692 int col = *((int*)bytes); bytes += sizeof(int);
693 int shape = *((int*)bytes); bytes += sizeof(int);
694 int percent = *((int*)bytes); bytes += sizeof(int);
697 ASLogDebug(@" Draw cursor at (%d,%d)", row, col);
699 [helper setInsertionPointColor:[NSColor colorWithRgbInt:color]];
700 [self drawInsertionPointAtRow:row column:col shape:shape
702 } else if (DrawInvertedRectDrawType == type) {
703 int row = *((int*)bytes); bytes += sizeof(int);
704 int col = *((int*)bytes); bytes += sizeof(int);
705 int nr = *((int*)bytes); bytes += sizeof(int);
706 int nc = *((int*)bytes); bytes += sizeof(int);
707 /*int invert = *((int*)bytes);*/ bytes += sizeof(int);
710 ASLogDebug(@" Draw inverted rect: row=%d col=%d nrows=%d "
711 "ncols=%d", row, col, nr, nc);
713 [self drawInvertedRectAtRow:row column:col numRows:nr
715 } else if (SetCursorPosDrawType == type) {
716 // TODO: This is used for Voice Over support in MMTextView,
717 // MMAtsuiTextView currently does not support Voice Over.
718 /*cursorRow = *((int*)bytes);*/ bytes += sizeof(int);
719 /*cursorCol = *((int*)bytes);*/ bytes += sizeof(int);
721 ASLogWarn(@"Unknown draw type (type=%d)", type);
727 [self setNeedsDisplay:YES];
729 // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
730 // and columns are changed (due to ipc delays). Force a redraw here.
731 if ([self inLiveResize])
735 ASLogDebug(@"<==== END %s", _cmd);
739 - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size
742 // - Rounding errors may cause size change when there should be none
743 // - Desired rows/columns shold not be 'too small'
745 // Constrain the desired size to the given size. Values for the minimum
746 // rows and columns is taken from Vim.
747 NSSize desiredSize = [self desiredSize];
748 int desiredRows = maxRows;
749 int desiredCols = maxColumns;
751 if (size.height != desiredSize.height) {
752 float fh = cellSize.height;
753 float ih = 2 * insetSize.height;
754 if (fh < 1.0f) fh = 1.0f;
756 desiredRows = floor((size.height - ih)/fh);
757 desiredSize.height = fh*desiredRows + ih;
760 if (size.width != desiredSize.width) {
761 float fw = cellSize.width;
762 float iw = 2 * insetSize.width;
763 if (fw < 1.0f) fw = 1.0f;
765 desiredCols = floor((size.width - iw)/fw);
766 desiredSize.width = fw*desiredCols + iw;
769 if (rows) *rows = desiredRows;
770 if (cols) *cols = desiredCols;
775 - (NSSize)desiredSize
777 // Compute the size the text view should be for the entire text area and
778 // inset area to be visible with the present number of rows and columns.
779 return NSMakeSize(maxColumns * cellSize.width + 2 * insetSize.width,
780 maxRows * cellSize.height + 2 * insetSize.height);
785 // Compute the smallest size the text view is allowed to be.
786 return NSMakeSize(MMMinColumns * cellSize.width + 2 * insetSize.width,
787 MMMinRows * cellSize.height + 2 * insetSize.height);
790 - (void)changeFont:(id)sender
792 [helper changeFont:sender];
797 // NOTE: The menu items cut/copy/paste/undo/redo/select all/... must be bound
798 // to the same actions as in IB otherwise they will not work with dialogs. All
799 // we do here is forward these actions to the Vim process.
801 - (IBAction)cut:(id)sender
803 [[self windowController] vimMenuItemAction:sender];
806 - (IBAction)copy:(id)sender
808 [[self windowController] vimMenuItemAction:sender];
811 - (IBAction)paste:(id)sender
813 [[self windowController] vimMenuItemAction:sender];
816 - (IBAction)undo:(id)sender
818 [[self windowController] vimMenuItemAction:sender];
821 - (IBAction)redo:(id)sender
823 [[self windowController] vimMenuItemAction:sender];
826 - (IBAction)selectAll:(id)sender
828 [[self windowController] vimMenuItemAction:sender];
831 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
833 // View is not flipped, instead the atsui code draws to a flipped image;
834 // thus we need to 'flip' the coordinate here since the column number
835 // increases in an up-to-down order.
836 point.y = [self frame].size.height - point.y;
838 NSPoint origin = { insetSize.width, insetSize.height };
840 if (!(cellSize.width > 0 && cellSize.height > 0))
843 if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
844 if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
849 - (NSPoint)pointForRow:(int)row column:(int)col
851 // Return the lower left coordinate of the cell at (row,column).
854 pt.x = insetSize.width + col*cellSize.width;
855 pt.y = [self frame].size.height -
856 (insetSize.height + (1+row)*cellSize.height);
861 - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
864 // Return the rect for the block which covers the specified rows and
865 // columns. The lower-left corner is the origin of this rect.
868 rect.origin.x = insetSize.width + col*cellSize.width;
869 rect.origin.y = [self frame].size.height -
870 (insetSize.height + (nr+row)*cellSize.height);
871 rect.size.width = nc*cellSize.width;
872 rect.size.height = nr*cellSize.height;
877 - (NSArray *)validAttributesForMarkedText
882 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
887 - (NSUInteger)characterIndexForPoint:(NSPoint)point
892 // The return type of this message changed with OS X 10.5 so we need this
893 // kludge in order to avoid compiler warnings on OS X 10.4.
894 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
895 - (NSInteger)conversationIdentifier
897 return (NSInteger)self;
900 - (long)conversationIdentifier
906 - (NSRange)selectedRange
908 return [helper imRange];
911 - (NSRect)firstRectForCharacterRange:(NSRange)range
913 return [helper firstRectForCharacterRange:range];
916 @end // MMAtsuiTextView
921 @implementation MMAtsuiTextView (Private)
923 - (void)initAtsuStyles
926 for (i = 0; i < MMMaxCellsPerChar; i++)
927 ATSUCreateStyle(&atsuStyles[i]);
930 - (void)disposeAtsuStyles
934 for (i = 0; i < MMMaxCellsPerChar; i++)
935 if (atsuStyles[i] != NULL)
937 if (ATSUDisposeStyle(atsuStyles[i]) != noErr)
938 atsuStyles[i] = NULL;
942 - (void)updateAtsuStyles
948 CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
949 ATSStyleRenderingOptions options;
951 fontID = [font _atsFontID];
952 fontSize = Long2Fix([font pointSize]);
953 options = kATSStyleApplyAntiAliasing;
955 ATSUAttributeTag attribTags[] =
957 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
958 kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag,
959 kATSUMaxATSUITagValue + 1
962 ByteCount attribSizes[] =
964 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
965 sizeof(CGAffineTransform), sizeof(ATSStyleRenderingOptions),
969 ATSUAttributeValuePtr attribValues[] =
971 &fontID, &fontSize, &fontWidth, &transform, &options, &font
974 ATSUFontFeatureType featureTypes[] = {
975 kLigaturesType, kLigaturesType
978 ATSUFontFeatureSelector featureSelectors[] = {
979 kCommonLigaturesOffSelector, kRareLigaturesOffSelector
982 for (i = 0; i < MMMaxCellsPerChar; i++)
984 fontWidth = Long2Fix(cellSize.width * (i + 1));
986 if (ATSUSetAttributes(atsuStyles[i],
987 (sizeof attribTags) / sizeof(ATSUAttributeTag),
988 attribTags, attribSizes, attribValues) != noErr)
990 ATSUDisposeStyle(atsuStyles[i]);
991 atsuStyles[i] = NULL;
994 // Turn off ligatures by default
995 ATSUSetFontFeatures(atsuStyles[i],
996 sizeof(featureTypes) / sizeof(featureTypes[0]),
997 featureTypes, featureSelectors);
1001 - (MMWindowController *)windowController
1003 id windowController = [[self window] windowController];
1004 if ([windowController isKindOfClass:[MMWindowController class]])
1005 return (MMWindowController*)windowController;
1009 - (MMVimController *)vimController
1011 return [[self windowController] vimController];
1014 @end // MMAtsuiTextView (Private)
1019 @implementation MMAtsuiTextView (Drawing)
1021 - (NSPoint)originForRow:(int)row column:(int)col
1023 return NSMakePoint(col * cellSize.width, row * cellSize.height);
1026 - (NSRect)rectFromRow:(int)row1 column:(int)col1
1027 toRow:(int)row2 column:(int)col2
1029 NSPoint origin = [self originForRow:row1 column:col1];
1030 return NSMakeRect(origin.x, origin.y,
1031 (col2 + 1 - col1) * cellSize.width,
1032 (row2 + 1 - row1) * cellSize.height);
1035 - (NSSize)textAreaSize
1037 // Calculate the (desired) size of the text area, i.e. the text view area
1038 // minus the inset area.
1039 return NSMakeSize(maxColumns * cellSize.width, maxRows * cellSize.height);
1042 - (void)resizeContentImage
1044 [contentImage release];
1045 contentImage = [[NSImage alloc] initWithSize:[self textAreaSize]];
1046 [contentImage setFlipped:YES];
1047 imageSize = [self textAreaSize];
1050 - (void)beginDrawing
1052 [contentImage lockFocus];
1057 [contentImage unlockFocus];
1060 #define atsu_style_set_bool(s, t, b) \
1061 ATSUSetAttributes(s, 1, &t, &(sizeof(Boolean)), &&b);
1062 #define FILL_Y(y) (y * cellSize.height)
1064 - (void)drawString:(UniChar *)string length:(UniCharCount)length
1065 atRow:(int)row column:(int)col cells:(int)cells
1066 withFlags:(int)flags foregroundColor:(NSColor *)fg
1067 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp
1069 // 'string' consists of 'length' utf-16 code pairs and should cover 'cells'
1070 // display cells (a normal character takes up one display cell, a wide
1071 // character takes up two)
1072 ATSUStyle style = (flags & DRAW_WIDE) ? atsuStyles[1] : atsuStyles[0];
1073 ATSUTextLayout layout;
1075 // Font selection and rendering options for ATSUI
1076 ATSUAttributeTag attribTags[3] = { kATSUQDBoldfaceTag,
1078 kATSUStyleRenderingOptionsTag };
1080 ByteCount attribSizes[] = { sizeof(Boolean),
1081 sizeof(CGAffineTransform),
1084 CGAffineTransform theTransform = CGAffineTransformMakeScale(1.0, -1.0);
1085 UInt32 useAntialias;
1087 ATSUAttributeValuePtr attribValues[3] = { &useBold, &theTransform,
1090 useBold = (flags & DRAW_BOLD) ? true : false;
1092 if (flags & DRAW_ITALIC)
1093 theTransform.c = Fix2X(kATSItalicQDSkew);
1095 useAntialias = antialias ? kATSStyleApplyAntiAliasing
1096 : kATSStyleNoAntiAliasing;
1098 ATSUSetAttributes(style, sizeof(attribValues) / sizeof(attribValues[0]),
1099 attribTags, attribSizes, attribValues);
1101 ATSUCreateTextLayout(&layout);
1102 ATSUSetTextPointerLocation(layout, string,
1103 kATSUFromTextBeginning, kATSUToTextEnd,
1105 ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd);
1107 NSRect rect = NSMakeRect(col * cellSize.width, row * cellSize.height,
1108 length * cellSize.width, cellSize.height);
1109 if (flags & DRAW_WIDE)
1110 rect.size.width = rect.size.width * 2;
1111 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
1113 // Clip drawing to avoid text bleeding into adjacent display cells when
1114 // antialiasing is enabled.
1115 CGContextSaveGState(context);
1116 CGContextClipToRect(context, *(CGRect*)&rect);
1118 ATSUAttributeTag tags[] = { kATSUCGContextTag };
1119 ByteCount sizes[] = { sizeof(CGContextRef) };
1120 ATSUAttributeValuePtr values[] = { &context };
1121 ATSUSetLayoutControls(layout, 1, tags, sizes, values);
1123 if (! (flags & DRAW_TRANSP))
1131 ATSUSetTransientFontMatching(layout, TRUE);
1132 ATSUDrawText(layout,
1133 kATSUFromTextBeginning,
1135 X2Fix(rect.origin.x),
1136 X2Fix(rect.origin.y + ascender));
1137 ATSUDisposeTextLayout(layout);
1139 if (flags & DRAW_UNDERL)
1142 NSRectFill(NSMakeRect(rect.origin.x,
1143 (row + 1) * cellSize.height + kUnderlineOffset,
1144 rect.size.width, kUnderlineHeight));
1147 if (flags & DRAW_UNDERC)
1151 float line_end_x = rect.origin.x + rect.size.width;
1153 NSRect line_rect = NSMakeRect(
1155 (row + 1) * cellSize.height + kUndercurlOffset,
1156 kUndercurlDotWidth, kUndercurlHeight);
1158 while (line_rect.origin.x < line_end_x)
1161 NSRectFill(line_rect);
1163 line_rect.origin.x += kUndercurlDotDistance;
1168 CGContextRestoreGState(context);
1171 - (void)scrollRect:(NSRect)rect lineCount:(int)count
1173 NSPoint destPoint = rect.origin;
1174 destPoint.y += count * cellSize.height;
1176 NSCopyBits(0, rect, destPoint);
1179 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
1180 scrollBottom:(int)bottom left:(int)left right:(int)right
1181 color:(NSColor *)color
1183 NSRect rect = [self rectFromRow:row + count
1188 // move rect up for count lines
1189 [self scrollRect:rect lineCount:-count];
1190 [self clearBlockFromRow:bottom - count + 1
1197 - (void)insertLinesAtRow:(int)row lineCount:(int)count
1198 scrollBottom:(int)bottom left:(int)left right:(int)right
1199 color:(NSColor *)color
1201 NSRect rect = [self rectFromRow:row
1203 toRow:bottom - count
1206 // move rect down for count lines
1207 [self scrollRect:rect lineCount:count];
1208 [self clearBlockFromRow:row
1210 toRow:row + count - 1
1215 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
1216 column:(int)col2 color:(NSColor *)color
1219 NSRectFill([self rectFromRow:row1 column:col1 toRow:row2 column:col2]);
1224 [defaultBackgroundColor set];
1225 NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height));
1228 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
1229 fraction:(int)percent
1231 NSPoint origin = [self originForRow:row column:col];
1232 NSRect rect = NSMakeRect(origin.x, origin.y,
1233 cellSize.width, cellSize.height);
1235 if (MMInsertionPointHorizontal == shape) {
1236 int frac = (cellSize.height * percent + 99)/100;
1237 rect.origin.y += rect.size.height - frac;
1238 rect.size.height = frac;
1239 } else if (MMInsertionPointVertical == shape) {
1240 int frac = (cellSize.width * percent + 99)/100;
1241 rect.size.width = frac;
1242 } else if (MMInsertionPointVerticalRight == shape) {
1243 int frac = (cellSize.width * percent + 99)/100;
1244 rect.origin.x += rect.size.width - frac;
1245 rect.size.width = frac;
1248 [[helper insertionPointColor] set];
1249 if (MMInsertionPointHollow == shape) {
1256 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
1257 numColumns:(int)ncols
1259 // TODO: THIS CODE HAS NOT BEEN TESTED!
1260 CGContextRef cgctx = [[NSGraphicsContext currentContext] graphicsPort];
1261 CGContextSaveGState(cgctx);
1262 CGContextSetBlendMode(cgctx, kCGBlendModeDifference);
1263 CGContextSetRGBFillColor(cgctx, 1.0, 1.0, 1.0, 1.0);
1265 CGRect rect = { col * cellSize.width, row * cellSize.height,
1266 ncols * cellSize.width, nrows * cellSize.height };
1267 CGContextFillRect(cgctx, rect);
1269 CGContextRestoreGState(cgctx);
1272 @end // MMAtsuiTextView (Drawing)
1274 #endif // ENABLE_ATSUI