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"
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
45 #define DRAW_WIDE 0x40 /* draw wide text */
47 #define kUnderlineOffset (-2)
48 #define kUnderlineHeight 1
49 #define kUndercurlHeight 2
50 #define kUndercurlOffset (-2)
51 #define kUndercurlDotWidth 2
52 #define kUndercurlDotDistance 2
55 @interface NSFont (AppKitPrivate)
56 - (ATSUFontID) _atsFontID;
60 @interface MMAtsuiTextView (Private)
61 - (void)initAtsuStyles;
62 - (void)disposeAtsuStyles;
63 - (void)updateAtsuStyles;
64 - (MMWindowController *)windowController;
65 - (MMVimController *)vimController;
69 @interface MMAtsuiTextView (Drawing)
70 - (NSPoint)originForRow:(int)row column:(int)column;
71 - (NSRect)rectFromRow:(int)row1 column:(int)col1
72 toRow:(int)row2 column:(int)col2;
73 - (NSSize)textAreaSize;
74 - (void)resizeContentImage;
77 - (void)drawString:(UniChar *)string length:(UniCharCount)length
78 atRow:(int)row column:(int)col cells:(int)cells
79 withFlags:(int)flags foregroundColor:(NSColor *)fg
80 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp;
81 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
82 scrollBottom:(int)bottom left:(int)left right:(int)right
83 color:(NSColor *)color;
84 - (void)insertLinesAtRow:(int)row lineCount:(int)count
85 scrollBottom:(int)bottom left:(int)left right:(int)right
86 color:(NSColor *)color;
87 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
88 column:(int)col2 color:(NSColor *)color;
90 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
91 fraction:(int)percent;
92 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
93 numColumns:(int)ncols;
99 defaultLineHeightForFont(NSFont *font)
101 // HACK: -[NSFont defaultLineHeightForFont] is deprecated but since the
102 // ATSUI renderer does not use NSLayoutManager we create one temporarily.
103 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
104 float height = [lm defaultLineHeightForFont:font];
110 @implementation MMAtsuiTextView
112 - (id)initWithFrame:(NSRect)frame
114 if (!(self = [super initWithFrame:frame]))
117 // NOTE! It does not matter which font is set here, Vim will set its
118 // own font on startup anyway. Just set some bogus values.
119 font = [[NSFont userFixedPitchFontOfSize:0] retain];
121 cellSize.width = cellSize.height = 1;
123 imageSize = NSZeroSize;
124 insetSize = NSZeroSize;
126 // NOTE: If the default changes to 'NO' then the intialization of
127 // p_antialias in option.c must change as well.
130 helper = [[MMTextViewHelper alloc] init];
131 [helper setTextView:self];
133 [self initAtsuStyles];
135 [self registerForDraggedTypes:[NSArray arrayWithObjects:
136 NSFilenamesPboardType, NSStringPboardType, nil]];
145 [self disposeAtsuStyles];
146 [font release]; font = nil;
147 [fontWide release]; fontWide = 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 // Compute rect whose vertical dimensions cover the rows in the given
213 // NOTE: The rect should be in _flipped_ coordinates and the first row must
214 // include the top inset as well. (This method is only used to place the
215 // scrollbars inside MMVimView.)
217 NSRect rect = { {0, 0}, {0, 0} };
218 unsigned start = range.location > maxRows ? maxRows : range.location;
219 unsigned length = range.length;
221 if (start + length > maxRows)
222 length = maxRows - start;
225 rect.origin.y = cellSize.height * start + insetSize.height;
226 rect.size.height = cellSize.height * length;
230 rect.size.height = cellSize.height * length + insetSize.height;
236 - (NSRect)rectForColumnsInRange:(NSRange)range
238 // Compute rect whose horizontal dimensions cover the columns in the given
240 // NOTE: The first column must include the left inset. (This method is
241 // only used to place the scrollbars inside MMVimView.)
243 NSRect rect = { {0, 0}, {0, 0} };
244 unsigned start = range.location > maxColumns ? maxColumns : range.location;
245 unsigned length = range.length;
247 if (start+length > maxColumns)
248 length = maxColumns - start;
251 rect.origin.x = cellSize.width * start + insetSize.width;
252 rect.size.width = cellSize.width * length;
254 // Include left inset
256 rect.size.width = cellSize.width * length + insetSize.width;
263 - (void)setFont:(NSFont *)newFont
265 if (newFont && font != newFont) {
267 font = [newFont retain];
268 ascender = roundf([font ascender]);
270 float em = [@"m" sizeWithAttributes:
271 [NSDictionary dictionaryWithObject:newFont
272 forKey:NSFontAttributeName]].width;
273 float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
274 floatForKey:MMCellWidthMultiplierKey];
276 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
277 // only render at integer sizes. Hence, we restrict the cell width to
278 // an integer here, otherwise the window width and the actual text
279 // width will not match.
280 cellSize.width = ceilf(em * cellWidthMultiplier);
281 cellSize.height = linespace + defaultLineHeightForFont(newFont);
283 [self updateAtsuStyles];
287 - (void)setWideFont:(NSFont *)newFont
290 if (font) [self setWideFont:font];
291 } else if (newFont != fontWide) {
294 float pointSize = [newFont pointSize];
295 NSFontDescriptor *desc = [newFont fontDescriptor];
296 NSDictionary *dictWide = [NSDictionary
297 dictionaryWithObject:[NSNumber numberWithFloat:2*cellSize.width]
298 forKey:NSFontFixedAdvanceAttribute];
299 desc = [desc fontDescriptorByAddingAttributes:dictWide];
300 fontWide = [NSFont fontWithDescriptor:desc size:pointSize];
320 - (void)setLinespace:(float)newLinespace
322 linespace = newLinespace;
324 // NOTE: The linespace is added to the cell height in order for a multiline
325 // selection not to have white (background color) gaps between lines. Also
326 // this simplifies the code a lot because there is no need to check the
327 // linespace when calculating the size of the text view etc. When the
328 // linespace is non-zero the baseline will be adjusted as well; check
330 cellSize.height = linespace + defaultLineHeightForFont(font);
335 - (void)setShouldDrawInsertionPoint:(BOOL)on
339 - (void)setPreEditRow:(int)row column:(int)col
341 [helper setPreEditRow:row column:col];
344 - (void)setMouseShape:(int)shape
346 [helper setMouseShape:shape];
349 - (void)setAntialias:(BOOL)state
354 - (void)setImControl:(BOOL)enable
356 [helper setImControl:enable];
359 - (void)activateIm:(BOOL)enable
361 [helper activateIm:enable];
364 - (BOOL)_wantsKeyDownForEvent:(id)event
366 // HACK! This is an undocumented method which is called from within
367 // -[NSWindow sendEvent] (and perhaps in other places as well) when the
368 // user presses e.g. Ctrl-Tab or Ctrl-Esc . Returning YES here effectively
369 // disables the Cocoa "key view loop" (which is undesirable). It may have
370 // other side-effects, but we really _do_ want to process all key down
371 // events so it seems safe to always return YES.
375 - (void)keyDown:(NSEvent *)event
377 [helper keyDown:event];
380 - (void)insertText:(id)string
382 [helper insertText:string];
385 - (void)doCommandBySelector:(SEL)selector
387 [helper doCommandBySelector:selector];
390 - (BOOL)performKeyEquivalent:(NSEvent *)event
392 return [helper performKeyEquivalent:event];
395 - (BOOL)hasMarkedText
397 return [helper hasMarkedText];
400 - (NSRange)markedRange
402 return [helper markedRange];
405 - (NSDictionary *)markedTextAttributes
407 return [helper markedTextAttributes];
410 - (void)setMarkedTextAttributes:(NSDictionary *)attr
412 [helper setMarkedTextAttributes:attr];
415 - (void)setMarkedText:(id)text selectedRange:(NSRange)range
417 [helper setMarkedText:text selectedRange:range];
425 - (void)scrollWheel:(NSEvent *)event
427 [helper scrollWheel:event];
430 - (void)mouseDown:(NSEvent *)event
432 [helper mouseDown:event];
435 - (void)rightMouseDown:(NSEvent *)event
437 [helper mouseDown:event];
440 - (void)otherMouseDown:(NSEvent *)event
442 [helper mouseDown:event];
445 - (void)mouseUp:(NSEvent *)event
447 [helper mouseUp:event];
450 - (void)rightMouseUp:(NSEvent *)event
452 [helper mouseUp:event];
455 - (void)otherMouseUp:(NSEvent *)event
457 [helper mouseUp:event];
460 - (void)mouseDragged:(NSEvent *)event
462 [helper mouseDragged:event];
465 - (void)rightMouseDragged:(NSEvent *)event
467 [helper mouseDragged:event];
470 - (void)otherMouseDragged:(NSEvent *)event
472 [helper mouseDragged:event];
475 - (void)mouseMoved:(NSEvent *)event
477 [helper mouseMoved:event];
480 - (void)mouseEntered:(NSEvent *)event
482 [helper mouseEntered:event];
485 - (void)mouseExited:(NSEvent *)event
487 [helper mouseExited:event];
490 - (void)setFrame:(NSRect)frame
492 [super setFrame:frame];
493 [helper setFrame:frame];
496 - (void)viewDidMoveToWindow
498 [helper viewDidMoveToWindow];
501 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
503 [helper viewWillMoveToWindow:newWindow];
506 - (NSMenu*)menuForEvent:(NSEvent *)event
508 // HACK! Return nil to disable default popup menus (Vim provides its own).
509 // Called when user Ctrl-clicks in the view (this is already handled in
514 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
516 return [helper performDragOperation:sender];
519 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
521 return [helper draggingEntered:sender];
524 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
526 return [helper draggingUpdated:sender];
531 - (BOOL)mouseDownCanMoveWindow
541 - (BOOL)acceptsFirstResponder
551 - (void)drawRect:(NSRect)rect
553 [defaultBackgroundColor set];
556 NSPoint pt = { insetSize.width, insetSize.height };
557 [contentImage compositeToPoint:pt operation:NSCompositeCopy];
559 #ifdef INCLUDE_OLD_IM_CODE
560 if ([self hasMarkedText] && ![helper useInlineIm]) {
561 int len = [[helper markedText] length];
563 int cols = maxColumns - [helper preEditColumn];
564 NSFont *theFont = [[self markedTextAttributes]
565 valueForKey:NSFontAttributeName];
566 if (theFont == [self fontWide])
569 int lend = cols > len ? len : cols;
570 NSAttributedString *aString = [[helper markedText]
571 attributedSubstringFromRange:NSMakeRange(done, lend)];
572 NSPoint pt = [self pointForRow:[helper preEditRow]
573 column:[helper preEditColumn]];
574 [aString drawAtPoint:pt];
578 rows = (len - done) / (maxColumns / 2) + 1;
579 for (r = 1; r <= rows; r++) {
580 lend = len - done > maxColumns / 2
581 ? maxColumns / 2 : len - done;
582 aString = [[helper markedText] attributedSubstringFromRange:
583 NSMakeRange(done, lend)];
584 NSPoint pt = [self pointForRow:[helper preEditRow]+r
586 [aString drawAtPoint:pt];
591 rows = maxRows - 1 - [helper preEditRow];
592 cols = [helper preEditColumn];
593 if (theFont == fontWide) {
594 cols += ([helper imRange].location+[helper imRange].length) * 2;
595 if (cols >= maxColumns - 1) {
596 rows -= cols / maxColumns;
597 cols = cols % 2 ? cols % maxColumns + 1 :
601 cols += ([helper imRange].location+[helper imRange].length);
602 if (cols >= maxColumns) {
603 rows -= cols / maxColumns;
604 cols = cols % 2 ? cols % maxColumns + 1 :
609 // TODO: Could IM be in "right-left" mode? If so the insertion point
610 // will be on the wrong side.
611 [self drawInsertionPointAtRow:rows
613 shape:MMInsertionPointVertical
616 #endif // INCLUDE_OLD_IM_CODE
619 - (BOOL) wantsDefaultClipping
625 #define MM_DEBUG_DRAWING 0
627 - (void)performBatchDrawWithData:(NSData *)data
629 const void *bytes = [data bytes];
630 const void *end = bytes + [data length];
632 if (! NSEqualSizes(imageSize, [self textAreaSize]))
633 [self resizeContentImage];
636 ASLogDebug(@"====> BEGIN %s", _cmd);
640 // TODO: Sanity check input
642 while (bytes < end) {
643 int type = *((int*)bytes); bytes += sizeof(int);
645 if (ClearAllDrawType == type) {
647 ASLogDebug(@" Clear all");
650 } else if (ClearBlockDrawType == type) {
651 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
652 int row1 = *((int*)bytes); bytes += sizeof(int);
653 int col1 = *((int*)bytes); bytes += sizeof(int);
654 int row2 = *((int*)bytes); bytes += sizeof(int);
655 int col2 = *((int*)bytes); bytes += sizeof(int);
658 ASLogDebug(@" Clear block (%d,%d) -> (%d,%d)", row1, col1,
661 [self clearBlockFromRow:row1 column:col1
662 toRow:row2 column:col2
663 color:[NSColor colorWithArgbInt:color]];
664 } else if (DeleteLinesDrawType == type) {
665 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
666 int row = *((int*)bytes); bytes += sizeof(int);
667 int count = *((int*)bytes); bytes += sizeof(int);
668 int bot = *((int*)bytes); bytes += sizeof(int);
669 int left = *((int*)bytes); bytes += sizeof(int);
670 int right = *((int*)bytes); bytes += sizeof(int);
673 ASLogDebug(@" Delete %d line(s) from %d", count, row);
675 [self deleteLinesFromRow:row lineCount:count
676 scrollBottom:bot left:left right:right
677 color:[NSColor colorWithArgbInt:color]];
678 } else if (DrawStringDrawType == type) {
679 int bg = *((int*)bytes); bytes += sizeof(int);
680 int fg = *((int*)bytes); bytes += sizeof(int);
681 int sp = *((int*)bytes); bytes += sizeof(int);
682 int row = *((int*)bytes); bytes += sizeof(int);
683 int col = *((int*)bytes); bytes += sizeof(int);
684 int cells = *((int*)bytes); bytes += sizeof(int);
685 int flags = *((int*)bytes); bytes += sizeof(int);
686 int len = *((int*)bytes); bytes += sizeof(int);
687 // UniChar *string = (UniChar*)bytes; bytes += len;
688 NSString *string = [[NSString alloc]
689 initWithBytesNoCopy:(void*)bytes
691 encoding:NSUTF8StringEncoding
695 ASLogDebug(@" Draw string at (%d,%d) length=%d flags=%d fg=0x%x "
696 "bg=0x%x sp=0x%x", row, col, len, flags, fg, bg, sp);
698 unichar *characters = malloc(sizeof(unichar) * [string length]);
699 [string getCharacters:characters];
701 [self drawString:characters
702 length:[string length]
705 cells:cells withFlags:flags
706 foregroundColor:[NSColor colorWithRgbInt:fg]
707 backgroundColor:[NSColor colorWithArgbInt:bg]
708 specialColor:[NSColor colorWithRgbInt:sp]];
711 } else if (InsertLinesDrawType == type) {
712 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
713 int row = *((int*)bytes); bytes += sizeof(int);
714 int count = *((int*)bytes); bytes += sizeof(int);
715 int bot = *((int*)bytes); bytes += sizeof(int);
716 int left = *((int*)bytes); bytes += sizeof(int);
717 int right = *((int*)bytes); bytes += sizeof(int);
720 ASLogDebug(@" Insert %d line(s) at row %d", count, row);
722 [self insertLinesAtRow:row lineCount:count
723 scrollBottom:bot left:left right:right
724 color:[NSColor colorWithArgbInt:color]];
725 } else if (DrawCursorDrawType == type) {
726 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
727 int row = *((int*)bytes); bytes += sizeof(int);
728 int col = *((int*)bytes); bytes += sizeof(int);
729 int shape = *((int*)bytes); bytes += sizeof(int);
730 int percent = *((int*)bytes); bytes += sizeof(int);
733 ASLogDebug(@" Draw cursor at (%d,%d)", row, col);
735 [helper setInsertionPointColor:[NSColor colorWithRgbInt:color]];
736 [self drawInsertionPointAtRow:row column:col shape:shape
738 } else if (DrawInvertedRectDrawType == type) {
739 int row = *((int*)bytes); bytes += sizeof(int);
740 int col = *((int*)bytes); bytes += sizeof(int);
741 int nr = *((int*)bytes); bytes += sizeof(int);
742 int nc = *((int*)bytes); bytes += sizeof(int);
743 /*int invert = *((int*)bytes);*/ bytes += sizeof(int);
746 ASLogDebug(@" Draw inverted rect: row=%d col=%d nrows=%d "
747 "ncols=%d", row, col, nr, nc);
749 [self drawInvertedRectAtRow:row column:col numRows:nr
751 } else if (SetCursorPosDrawType == type) {
752 // TODO: This is used for Voice Over support in MMTextView,
753 // MMAtsuiTextView currently does not support Voice Over.
754 /*cursorRow = *((int*)bytes);*/ bytes += sizeof(int);
755 /*cursorCol = *((int*)bytes);*/ bytes += sizeof(int);
757 ASLogWarn(@"Unknown draw type (type=%d)", type);
763 [self setNeedsDisplay:YES];
765 // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
766 // and columns are changed (due to ipc delays). Force a redraw here.
767 if ([self inLiveResize])
771 ASLogDebug(@"<==== END %s", _cmd);
775 - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size
778 // - Rounding errors may cause size change when there should be none
779 // - Desired rows/columns shold not be 'too small'
781 // Constrain the desired size to the given size. Values for the minimum
782 // rows and columns are taken from Vim.
783 NSSize desiredSize = [self desiredSize];
784 int desiredRows = maxRows;
785 int desiredCols = maxColumns;
787 if (size.height != desiredSize.height) {
788 float fh = cellSize.height;
789 float ih = 2 * insetSize.height;
790 if (fh < 1.0f) fh = 1.0f;
792 desiredRows = floor((size.height - ih)/fh);
793 desiredSize.height = fh*desiredRows + ih;
796 if (size.width != desiredSize.width) {
797 float fw = cellSize.width;
798 float iw = 2 * insetSize.width;
799 if (fw < 1.0f) fw = 1.0f;
801 desiredCols = floor((size.width - iw)/fw);
802 desiredSize.width = fw*desiredCols + iw;
805 if (rows) *rows = desiredRows;
806 if (cols) *cols = desiredCols;
811 - (NSSize)desiredSize
813 // Compute the size the text view should be for the entire text area and
814 // inset area to be visible with the present number of rows and columns.
815 return NSMakeSize(maxColumns * cellSize.width + 2 * insetSize.width,
816 maxRows * cellSize.height + 2 * insetSize.height);
821 // Compute the smallest size the text view is allowed to be.
822 return NSMakeSize(MMMinColumns * cellSize.width + 2 * insetSize.width,
823 MMMinRows * cellSize.height + 2 * insetSize.height);
826 - (void)changeFont:(id)sender
828 [helper changeFont:sender];
833 // NOTE: The menu items cut/copy/paste/undo/redo/select all/... must be bound
834 // to the same actions as in IB otherwise they will not work with dialogs. All
835 // we do here is forward these actions to the Vim process.
837 - (IBAction)cut:(id)sender
839 [[self windowController] vimMenuItemAction:sender];
842 - (IBAction)copy:(id)sender
844 [[self windowController] vimMenuItemAction:sender];
847 - (IBAction)paste:(id)sender
849 [[self windowController] vimMenuItemAction:sender];
852 - (IBAction)undo:(id)sender
854 [[self windowController] vimMenuItemAction:sender];
857 - (IBAction)redo:(id)sender
859 [[self windowController] vimMenuItemAction:sender];
862 - (IBAction)selectAll:(id)sender
864 [[self windowController] vimMenuItemAction:sender];
867 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
869 // View is not flipped, instead the atsui code draws to a flipped image;
870 // thus we need to 'flip' the coordinate here since the column number
871 // increases in an up-to-down order.
872 point.y = [self frame].size.height - point.y;
874 NSPoint origin = { insetSize.width, insetSize.height };
876 if (!(cellSize.width > 0 && cellSize.height > 0))
879 if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
880 if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
885 - (NSPoint)pointForRow:(int)row column:(int)col
887 // Return the lower left coordinate of the cell at (row,column).
890 pt.x = insetSize.width + col*cellSize.width;
891 pt.y = [self frame].size.height -
892 (insetSize.height + (1+row)*cellSize.height);
897 - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
900 // Return the rect for the block which covers the specified rows and
901 // columns. The lower-left corner is the origin of this rect.
904 rect.origin.x = insetSize.width + col*cellSize.width;
905 rect.origin.y = [self frame].size.height -
906 (insetSize.height + (nr+row)*cellSize.height);
907 rect.size.width = nc*cellSize.width;
908 rect.size.height = nr*cellSize.height;
913 - (NSArray *)validAttributesForMarkedText
918 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
923 - (NSUInteger)characterIndexForPoint:(NSPoint)point
928 // The return type of this message changed with OS X 10.5 so we need this
929 // kludge in order to avoid compiler warnings on OS X 10.4.
930 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
931 - (NSInteger)conversationIdentifier
933 return (NSInteger)self;
936 - (long)conversationIdentifier
942 - (NSRange)selectedRange
944 return [helper imRange];
947 - (NSRect)firstRectForCharacterRange:(NSRange)range
949 return [helper firstRectForCharacterRange:range];
952 @end // MMAtsuiTextView
957 @implementation MMAtsuiTextView (Private)
959 - (void)initAtsuStyles
962 for (i = 0; i < MMMaxCellsPerChar; i++)
963 ATSUCreateStyle(&atsuStyles[i]);
966 - (void)disposeAtsuStyles
970 for (i = 0; i < MMMaxCellsPerChar; i++)
971 if (atsuStyles[i] != NULL)
973 if (ATSUDisposeStyle(atsuStyles[i]) != noErr)
974 atsuStyles[i] = NULL;
978 - (void)updateAtsuStyles
984 CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
985 ATSStyleRenderingOptions options;
987 fontID = [font _atsFontID];
988 fontSize = Long2Fix([font pointSize]);
989 options = kATSStyleApplyAntiAliasing;
991 ATSUAttributeTag attribTags[] =
993 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
994 kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag,
995 kATSUMaxATSUITagValue + 1
998 ByteCount attribSizes[] =
1000 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
1001 sizeof(CGAffineTransform), sizeof(ATSStyleRenderingOptions),
1005 ATSUAttributeValuePtr attribValues[] =
1007 &fontID, &fontSize, &fontWidth, &transform, &options, &font
1010 ATSUFontFeatureType featureTypes[] = {
1011 kLigaturesType, kLigaturesType
1014 ATSUFontFeatureSelector featureSelectors[] = {
1015 kCommonLigaturesOffSelector, kRareLigaturesOffSelector
1018 for (i = 0; i < MMMaxCellsPerChar; i++)
1020 fontWidth = Long2Fix(cellSize.width * (i + 1));
1022 if (ATSUSetAttributes(atsuStyles[i],
1023 (sizeof attribTags) / sizeof(ATSUAttributeTag),
1024 attribTags, attribSizes, attribValues) != noErr)
1026 ATSUDisposeStyle(atsuStyles[i]);
1027 atsuStyles[i] = NULL;
1030 // Turn off ligatures by default
1031 ATSUSetFontFeatures(atsuStyles[i],
1032 sizeof(featureTypes) / sizeof(featureTypes[0]),
1033 featureTypes, featureSelectors);
1037 - (MMWindowController *)windowController
1039 id windowController = [[self window] windowController];
1040 if ([windowController isKindOfClass:[MMWindowController class]])
1041 return (MMWindowController*)windowController;
1045 - (MMVimController *)vimController
1047 return [[self windowController] vimController];
1050 @end // MMAtsuiTextView (Private)
1055 @implementation MMAtsuiTextView (Drawing)
1057 - (NSPoint)originForRow:(int)row column:(int)col
1059 return NSMakePoint(col * cellSize.width, row * cellSize.height);
1062 - (NSRect)rectFromRow:(int)row1 column:(int)col1
1063 toRow:(int)row2 column:(int)col2
1065 NSPoint origin = [self originForRow:row1 column:col1];
1066 return NSMakeRect(origin.x, origin.y,
1067 (col2 + 1 - col1) * cellSize.width,
1068 (row2 + 1 - row1) * cellSize.height);
1071 - (NSSize)textAreaSize
1073 // Calculate the (desired) size of the text area, i.e. the text view area
1074 // minus the inset area.
1075 return NSMakeSize(maxColumns * cellSize.width, maxRows * cellSize.height);
1078 - (void)resizeContentImage
1080 [contentImage release];
1081 contentImage = [[NSImage alloc] initWithSize:[self textAreaSize]];
1082 [contentImage setFlipped:YES];
1083 imageSize = [self textAreaSize];
1086 - (void)beginDrawing
1088 [contentImage lockFocus];
1093 [contentImage unlockFocus];
1096 #define atsu_style_set_bool(s, t, b) \
1097 ATSUSetAttributes(s, 1, &t, &(sizeof(Boolean)), &&b);
1098 #define FILL_Y(y) (y * cellSize.height)
1100 - (void)drawString:(UniChar *)string length:(UniCharCount)length
1101 atRow:(int)row column:(int)col cells:(int)cells
1102 withFlags:(int)flags foregroundColor:(NSColor *)fg
1103 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp
1105 // 'string' consists of 'length' utf-16 code pairs and should cover 'cells'
1106 // display cells (a normal character takes up one display cell, a wide
1107 // character takes up two)
1108 ATSUStyle style = (flags & DRAW_WIDE) ? atsuStyles[1] : atsuStyles[0];
1109 ATSUTextLayout layout;
1111 // Font selection and rendering options for ATSUI
1112 ATSUAttributeTag attribTags[3] = { kATSUQDBoldfaceTag,
1114 kATSUStyleRenderingOptionsTag };
1116 ByteCount attribSizes[] = { sizeof(Boolean),
1117 sizeof(CGAffineTransform),
1120 CGAffineTransform theTransform = CGAffineTransformMakeScale(1.0, -1.0);
1121 UInt32 useAntialias;
1123 ATSUAttributeValuePtr attribValues[3] = { &useBold, &theTransform,
1126 useBold = (flags & DRAW_BOLD) ? true : false;
1128 if (flags & DRAW_ITALIC)
1129 theTransform.c = Fix2X(kATSItalicQDSkew);
1131 useAntialias = antialias ? kATSStyleApplyAntiAliasing
1132 : kATSStyleNoAntiAliasing;
1134 ATSUSetAttributes(style, sizeof(attribValues) / sizeof(attribValues[0]),
1135 attribTags, attribSizes, attribValues);
1137 ATSUCreateTextLayout(&layout);
1138 ATSUSetTextPointerLocation(layout, string,
1139 kATSUFromTextBeginning, kATSUToTextEnd,
1141 ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd);
1143 NSRect rect = NSMakeRect(col * cellSize.width, row * cellSize.height,
1144 length * cellSize.width, cellSize.height);
1145 if (flags & DRAW_WIDE)
1146 rect.size.width = rect.size.width * 2;
1147 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
1149 // Clip drawing to avoid text bleeding into adjacent display cells when
1150 // antialiasing is enabled.
1151 CGContextSaveGState(context);
1152 CGContextClipToRect(context, *(CGRect*)&rect);
1154 ATSUAttributeTag tags[] = { kATSUCGContextTag };
1155 ByteCount sizes[] = { sizeof(CGContextRef) };
1156 ATSUAttributeValuePtr values[] = { &context };
1157 ATSUSetLayoutControls(layout, 1, tags, sizes, values);
1159 if (! (flags & DRAW_TRANSP))
1167 ATSUSetTransientFontMatching(layout, TRUE);
1168 ATSUDrawText(layout,
1169 kATSUFromTextBeginning,
1171 X2Fix(rect.origin.x),
1172 X2Fix(rect.origin.y + ascender));
1173 ATSUDisposeTextLayout(layout);
1175 if (flags & DRAW_UNDERL)
1178 NSRectFill(NSMakeRect(rect.origin.x,
1179 (row + 1) * cellSize.height + kUnderlineOffset,
1180 rect.size.width, kUnderlineHeight));
1183 if (flags & DRAW_UNDERC)
1187 float line_end_x = rect.origin.x + rect.size.width;
1189 NSRect line_rect = NSMakeRect(
1191 (row + 1) * cellSize.height + kUndercurlOffset,
1192 kUndercurlDotWidth, kUndercurlHeight);
1194 while (line_rect.origin.x < line_end_x)
1197 NSRectFill(line_rect);
1199 line_rect.origin.x += kUndercurlDotDistance;
1204 CGContextRestoreGState(context);
1207 - (void)scrollRect:(NSRect)rect lineCount:(int)count
1209 NSPoint destPoint = rect.origin;
1210 destPoint.y += count * cellSize.height;
1212 NSCopyBits(0, rect, destPoint);
1215 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
1216 scrollBottom:(int)bottom left:(int)left right:(int)right
1217 color:(NSColor *)color
1219 NSRect rect = [self rectFromRow:row + count
1224 // move rect up for count lines
1225 [self scrollRect:rect lineCount:-count];
1226 [self clearBlockFromRow:bottom - count + 1
1233 - (void)insertLinesAtRow:(int)row lineCount:(int)count
1234 scrollBottom:(int)bottom left:(int)left right:(int)right
1235 color:(NSColor *)color
1237 NSRect rect = [self rectFromRow:row
1239 toRow:bottom - count
1242 // move rect down for count lines
1243 [self scrollRect:rect lineCount:count];
1244 [self clearBlockFromRow:row
1246 toRow:row + count - 1
1251 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
1252 column:(int)col2 color:(NSColor *)color
1255 NSRectFill([self rectFromRow:row1 column:col1 toRow:row2 column:col2]);
1260 [defaultBackgroundColor set];
1261 NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height));
1264 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
1265 fraction:(int)percent
1267 NSPoint origin = [self originForRow:row column:col];
1268 NSRect rect = NSMakeRect(origin.x, origin.y,
1269 cellSize.width, cellSize.height);
1271 if (MMInsertionPointHorizontal == shape) {
1272 int frac = (cellSize.height * percent + 99)/100;
1273 rect.origin.y += rect.size.height - frac;
1274 rect.size.height = frac;
1275 } else if (MMInsertionPointVertical == shape) {
1276 int frac = (cellSize.width * percent + 99)/100;
1277 rect.size.width = frac;
1278 } else if (MMInsertionPointVerticalRight == shape) {
1279 int frac = (cellSize.width * percent + 99)/100;
1280 rect.origin.x += rect.size.width - frac;
1281 rect.size.width = frac;
1284 [[helper insertionPointColor] set];
1285 if (MMInsertionPointHollow == shape) {
1292 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
1293 numColumns:(int)ncols
1295 // TODO: THIS CODE HAS NOT BEEN TESTED!
1296 CGContextRef cgctx = [[NSGraphicsContext currentContext] graphicsPort];
1297 CGContextSaveGState(cgctx);
1298 CGContextSetBlendMode(cgctx, kCGBlendModeDifference);
1299 CGContextSetRGBFillColor(cgctx, 1.0, 1.0, 1.0, 1.0);
1301 CGRect rect = { col * cellSize.width, row * cellSize.height,
1302 ncols * cellSize.width, nrows * cellSize.height };
1303 CGContextFillRect(cgctx, rect);
1305 CGContextRestoreGState(cgctx);
1308 @end // MMAtsuiTextView (Drawing)
1310 #endif // MM_ENABLE_ATSUI