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;
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];
120 cellSize.width = cellSize.height = 1;
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.
129 helper = [[MMTextViewHelper alloc] init];
130 [helper setTextView:self];
132 [self initAtsuStyles];
134 [self registerForDraggedTypes:[NSArray arrayWithObjects:
135 NSFilenamesPboardType, NSStringPboardType, nil]];
144 [self disposeAtsuStyles];
145 [font release]; font = nil;
146 [defaultBackgroundColor release]; defaultBackgroundColor = nil;
147 [defaultForegroundColor release]; defaultForegroundColor = nil;
149 [helper setTextView:nil];
150 [helper dealloc]; helper = nil;
165 - (void)getMaxRows:(int*)rows columns:(int*)cols
167 if (rows) *rows = maxRows;
168 if (cols) *cols = maxColumns;
171 - (void)setMaxRows:(int)rows columns:(int)cols
173 // NOTE: Just remember the new values, the actual resizing is done lazily.
178 - (void)setDefaultColorsBackground:(NSColor *)bgColor
179 foreground:(NSColor *)fgColor
181 if (defaultBackgroundColor != bgColor) {
182 [defaultBackgroundColor release];
183 defaultBackgroundColor = bgColor ? [bgColor retain] : nil;
186 if (defaultForegroundColor != fgColor) {
187 [defaultForegroundColor release];
188 defaultForegroundColor = fgColor ? [fgColor retain] : nil;
192 - (NSColor *)defaultBackgroundColor
194 return defaultBackgroundColor;
197 - (NSColor *)defaultForegroundColor
199 return defaultForegroundColor;
202 - (void)setTextContainerInset:(NSSize)size
207 - (NSRect)rectForRowsInRange:(NSRange)range
209 NSRect rect = { 0, 0, 0, 0 };
210 unsigned start = range.location > maxRows ? maxRows : range.location;
211 unsigned length = range.length;
213 if (start + length > maxRows)
214 length = maxRows - start;
216 rect.origin.y = cellSize.height * start + insetSize.height;
217 rect.size.height = cellSize.height * length;
222 - (NSRect)rectForColumnsInRange:(NSRange)range
224 NSRect rect = { 0, 0, 0, 0 };
225 unsigned start = range.location > maxColumns ? maxColumns : range.location;
226 unsigned length = range.length;
228 if (start+length > maxColumns)
229 length = maxColumns - start;
231 rect.origin.x = cellSize.width * start + insetSize.width;
232 rect.size.width = cellSize.width * length;
238 - (void)setFont:(NSFont *)newFont
240 if (newFont && font != newFont) {
242 font = [newFont retain];
243 ascender = roundf([font ascender]);
245 float em = [@"m" sizeWithAttributes:
246 [NSDictionary dictionaryWithObject:newFont
247 forKey:NSFontAttributeName]].width;
248 float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
249 floatForKey:MMCellWidthMultiplierKey];
251 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
252 // only render at integer sizes. Hence, we restrict the cell width to
253 // an integer here, otherwise the window width and the actual text
254 // width will not match.
255 cellSize.width = ceilf(em * cellWidthMultiplier);
256 cellSize.height = linespace + defaultLineHeightForFont(newFont);
258 [self updateAtsuStyles];
262 - (void)setWideFont:(NSFont *)newFont
265 if (font) [self setWideFont:font];
266 } else if (newFont != fontWide) {
267 float pointSize = [newFont pointSize];
268 NSFontDescriptor *desc = [newFont fontDescriptor];
269 NSDictionary *dictWide = [NSDictionary
270 dictionaryWithObject:[NSNumber numberWithFloat:2*cellSize.width]
271 forKey:NSFontFixedAdvanceAttribute];
272 desc = [desc fontDescriptorByAddingAttributes:dictWide];
273 fontWide = [NSFont fontWithDescriptor:desc size:pointSize];
293 - (void)setLinespace:(float)newLinespace
295 linespace = newLinespace;
297 // NOTE: The linespace is added to the cell height in order for a multiline
298 // selection not to have white (background color) gaps between lines. Also
299 // this simplifies the code a lot because there is no need to check the
300 // linespace when calculating the size of the text view etc. When the
301 // linespace is non-zero the baseline will be adjusted as well; check
303 cellSize.height = linespace + defaultLineHeightForFont(font);
308 - (void)setShouldDrawInsertionPoint:(BOOL)on
312 - (void)setPreEditRow:(int)row column:(int)col
314 [helper setPreEditRow:row column:col];
317 - (void)setMouseShape:(int)shape
319 [helper setMouseShape:shape];
322 - (void)setAntialias:(BOOL)state
327 - (void)setImControl:(BOOL)enable
329 [helper setImControl:enable];
332 - (void)keyDown:(NSEvent *)event
334 [helper keyDown:event];
337 - (void)insertText:(id)string
339 [helper insertText:string];
342 - (void)doCommandBySelector:(SEL)selector
344 [helper doCommandBySelector:selector];
347 - (BOOL)performKeyEquivalent:(NSEvent *)event
349 return [helper performKeyEquivalent:event];
352 - (BOOL)hasMarkedText
354 return [helper hasMarkedText];
357 - (NSRange)markedRange
359 return [helper markedRange];
362 - (NSDictionary *)markedTextAttributes
364 return [helper markedTextAttributes];
367 - (void)setMarkedTextAttributes:(NSDictionary *)attr
369 [helper setMarkedTextAttributes:attr];
372 - (void)setMarkedText:(id)text selectedRange:(NSRange)range
374 [helper setMarkedText:text selectedRange:range];
382 - (void)scrollWheel:(NSEvent *)event
384 [helper scrollWheel:event];
387 - (void)mouseDown:(NSEvent *)event
389 [helper mouseDown:event];
392 - (void)rightMouseDown:(NSEvent *)event
394 [helper mouseDown:event];
397 - (void)otherMouseDown:(NSEvent *)event
399 [helper mouseDown:event];
402 - (void)mouseUp:(NSEvent *)event
404 [helper mouseUp:event];
407 - (void)rightMouseUp:(NSEvent *)event
409 [helper mouseUp:event];
412 - (void)otherMouseUp:(NSEvent *)event
414 [helper mouseUp:event];
417 - (void)mouseDragged:(NSEvent *)event
419 [helper mouseDragged:event];
422 - (void)rightMouseDragged:(NSEvent *)event
424 [helper mouseDragged:event];
427 - (void)otherMouseDragged:(NSEvent *)event
429 [helper mouseDragged:event];
432 - (void)mouseMoved:(NSEvent *)event
434 [helper mouseMoved:event];
437 - (void)mouseEntered:(NSEvent *)event
439 [helper mouseEntered:event];
442 - (void)mouseExited:(NSEvent *)event
444 [helper mouseExited:event];
447 - (void)setFrame:(NSRect)frame
449 [super setFrame:frame];
450 [helper setFrame:frame];
453 - (void)viewDidMoveToWindow
455 [helper viewDidMoveToWindow];
458 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
460 [helper viewWillMoveToWindow:newWindow];
463 - (NSMenu*)menuForEvent:(NSEvent *)event
465 // HACK! Return nil to disable default popup menus (Vim provides its own).
466 // Called when user Ctrl-clicks in the view (this is already handled in
471 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
473 return [helper performDragOperation:sender];
476 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
478 return [helper draggingEntered:sender];
481 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
483 return [helper draggingUpdated:sender];
488 - (BOOL)mouseDownCanMoveWindow
498 - (BOOL)acceptsFirstResponder
508 - (void)drawRect:(NSRect)rect
510 [defaultBackgroundColor set];
513 NSPoint pt = { insetSize.width, insetSize.height };
514 [contentImage compositeToPoint:pt operation:NSCompositeCopy];
516 if ([self hasMarkedText]) {
517 int len = [[helper markedText] length];
519 int cols = maxColumns - [helper preEditColumn];
520 NSFont *theFont = [[self markedTextAttributes]
521 valueForKey:NSFontAttributeName];
522 if (theFont == [self fontWide])
525 int lend = cols > len ? len : cols;
526 NSAttributedString *aString = [[helper markedText]
527 attributedSubstringFromRange:NSMakeRange(done, lend)];
528 NSPoint pt = [self pointForRow:[helper preEditRow]
529 column:[helper preEditColumn]];
530 [aString drawAtPoint:pt];
534 rows = (len - done) / (maxColumns / 2) + 1;
535 for (r = 1; r <= rows; r++) {
536 lend = len - done > maxColumns / 2
537 ? maxColumns / 2 : len - done;
538 aString = [[helper markedText] attributedSubstringFromRange:
539 NSMakeRange(done, lend)];
540 NSPoint pt = [self pointForRow:[helper preEditRow]+r
542 [aString drawAtPoint:pt];
547 rows = maxRows - 1 - [helper preEditRow];
548 cols = [helper preEditColumn];
549 if (theFont == fontWide) {
550 cols += ([helper imRange].location+[helper imRange].length) * 2;
551 if (cols >= maxColumns - 1) {
552 rows -= cols / maxColumns;
553 cols = cols % 2 ? cols % maxColumns + 1 :
557 cols += ([helper imRange].location+[helper imRange].length);
558 if (cols >= maxColumns) {
559 rows -= cols / maxColumns;
560 cols = cols % 2 ? cols % maxColumns + 1 :
565 // TODO: Could IM be in "right-left" mode? If so the insertion point
566 // will be on the wrong side.
567 [self drawInsertionPointAtRow:rows
569 shape:MMInsertionPointVertical
574 - (BOOL) wantsDefaultClipping
580 #define MM_DEBUG_DRAWING 0
582 - (void)performBatchDrawWithData:(NSData *)data
584 const void *bytes = [data bytes];
585 const void *end = bytes + [data length];
587 if (! NSEqualSizes(imageSize, [self textAreaSize]))
588 [self resizeContentImage];
591 NSLog(@"====> BEGIN %s", _cmd);
595 // TODO: Sanity check input
597 while (bytes < end) {
598 int type = *((int*)bytes); bytes += sizeof(int);
600 if (ClearAllDrawType == type) {
602 NSLog(@" Clear all");
605 } else if (ClearBlockDrawType == type) {
606 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
607 int row1 = *((int*)bytes); bytes += sizeof(int);
608 int col1 = *((int*)bytes); bytes += sizeof(int);
609 int row2 = *((int*)bytes); bytes += sizeof(int);
610 int col2 = *((int*)bytes); bytes += sizeof(int);
613 NSLog(@" Clear block (%d,%d) -> (%d,%d)", row1, col1,
616 [self clearBlockFromRow:row1 column:col1
617 toRow:row2 column:col2
618 color:[NSColor colorWithArgbInt:color]];
619 } else if (DeleteLinesDrawType == type) {
620 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
621 int row = *((int*)bytes); bytes += sizeof(int);
622 int count = *((int*)bytes); bytes += sizeof(int);
623 int bot = *((int*)bytes); bytes += sizeof(int);
624 int left = *((int*)bytes); bytes += sizeof(int);
625 int right = *((int*)bytes); bytes += sizeof(int);
628 NSLog(@" Delete %d line(s) from %d", count, row);
630 [self deleteLinesFromRow:row lineCount:count
631 scrollBottom:bot left:left right:right
632 color:[NSColor colorWithArgbInt:color]];
633 } else if (DrawStringDrawType == type) {
634 int bg = *((int*)bytes); bytes += sizeof(int);
635 int fg = *((int*)bytes); bytes += sizeof(int);
636 int sp = *((int*)bytes); bytes += sizeof(int);
637 int row = *((int*)bytes); bytes += sizeof(int);
638 int col = *((int*)bytes); bytes += sizeof(int);
639 int cells = *((int*)bytes); bytes += sizeof(int);
640 int flags = *((int*)bytes); bytes += sizeof(int);
641 int len = *((int*)bytes); bytes += sizeof(int);
642 // UniChar *string = (UniChar*)bytes; bytes += len;
643 NSString *string = [[NSString alloc]
644 initWithBytesNoCopy:(void*)bytes
646 encoding:NSUTF8StringEncoding
650 NSLog(@" Draw string at (%d,%d) length=%d flags=%d fg=0x%x "
651 "bg=0x%x sp=0x%x", row, col, len, flags, fg, bg, sp);
653 unichar *characters = malloc(sizeof(unichar) * [string length]);
654 [string getCharacters:characters];
656 [self drawString:characters
657 length:[string length]
660 cells:cells withFlags:flags
661 foregroundColor:[NSColor colorWithRgbInt:fg]
662 backgroundColor:[NSColor colorWithArgbInt:bg]
663 specialColor:[NSColor colorWithRgbInt:sp]];
666 } else if (InsertLinesDrawType == type) {
667 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
668 int row = *((int*)bytes); bytes += sizeof(int);
669 int count = *((int*)bytes); bytes += sizeof(int);
670 int bot = *((int*)bytes); bytes += sizeof(int);
671 int left = *((int*)bytes); bytes += sizeof(int);
672 int right = *((int*)bytes); bytes += sizeof(int);
675 NSLog(@" Insert %d line(s) at row %d", count, row);
677 [self insertLinesAtRow:row lineCount:count
678 scrollBottom:bot left:left right:right
679 color:[NSColor colorWithArgbInt:color]];
680 } else if (DrawCursorDrawType == type) {
681 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
682 int row = *((int*)bytes); bytes += sizeof(int);
683 int col = *((int*)bytes); bytes += sizeof(int);
684 int shape = *((int*)bytes); bytes += sizeof(int);
685 int percent = *((int*)bytes); bytes += sizeof(int);
688 NSLog(@" Draw cursor at (%d,%d)", row, col);
690 [helper setInsertionPointColor:[NSColor colorWithRgbInt:color]];
691 [self drawInsertionPointAtRow:row column:col shape:shape
693 } else if (DrawInvertedRectDrawType == type) {
694 int row = *((int*)bytes); bytes += sizeof(int);
695 int col = *((int*)bytes); bytes += sizeof(int);
696 int nr = *((int*)bytes); bytes += sizeof(int);
697 int nc = *((int*)bytes); bytes += sizeof(int);
698 /*int invert = *((int*)bytes);*/ bytes += sizeof(int);
701 NSLog(@" Draw inverted rect: row=%d col=%d nrows=%d ncols=%d",
704 [self drawInvertedRectAtRow:row column:col numRows:nr
706 } else if (SetCursorPosDrawType == type) {
707 // TODO: This is used for Voice Over support in MMTextView,
708 // MMAtsuiTextView currently does not support Voice Over.
709 /*cursorRow = *((int*)bytes);*/ bytes += sizeof(int);
710 /*cursorCol = *((int*)bytes);*/ bytes += sizeof(int);
712 NSLog(@"WARNING: Unknown draw type (type=%d)", type);
718 [self setNeedsDisplay:YES];
720 // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
721 // and columns are changed (due to ipc delays). Force a redraw here.
722 if ([self inLiveResize])
726 NSLog(@"<==== END %s", _cmd);
730 - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size
733 // - Rounding errors may cause size change when there should be none
734 // - Desired rows/columns shold not be 'too small'
736 // Constrain the desired size to the given size. Values for the minimum
737 // rows and columns is taken from Vim.
738 NSSize desiredSize = [self desiredSize];
739 int desiredRows = maxRows;
740 int desiredCols = maxColumns;
742 if (size.height != desiredSize.height) {
743 float fh = cellSize.height;
744 float ih = 2 * insetSize.height;
745 if (fh < 1.0f) fh = 1.0f;
747 desiredRows = floor((size.height - ih)/fh);
748 desiredSize.height = fh*desiredRows + ih;
751 if (size.width != desiredSize.width) {
752 float fw = cellSize.width;
753 float iw = 2 * insetSize.width;
754 if (fw < 1.0f) fw = 1.0f;
756 desiredCols = floor((size.width - iw)/fw);
757 desiredSize.width = fw*desiredCols + iw;
760 if (rows) *rows = desiredRows;
761 if (cols) *cols = desiredCols;
766 - (NSSize)desiredSize
768 // Compute the size the text view should be for the entire text area and
769 // inset area to be visible with the present number of rows and columns.
770 return NSMakeSize(maxColumns * cellSize.width + 2 * insetSize.width,
771 maxRows * cellSize.height + 2 * insetSize.height);
776 // Compute the smallest size the text view is allowed to be.
777 return NSMakeSize(MMMinColumns * cellSize.width + 2 * insetSize.width,
778 MMMinRows * cellSize.height + 2 * insetSize.height);
781 - (void)changeFont:(id)sender
783 NSFont *newFont = [sender convertFont:font];
786 NSString *name = [newFont displayName];
787 unsigned len = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
789 NSMutableData *data = [NSMutableData data];
790 float pointSize = [newFont pointSize];
792 [data appendBytes:&pointSize length:sizeof(float)];
794 ++len; // include NUL byte
795 [data appendBytes:&len length:sizeof(unsigned)];
796 [data appendBytes:[name UTF8String] length:len];
798 [[self vimController] sendMessage:SetFontMsgID data:data];
805 // NOTE: The menu items cut/copy/paste/undo/redo/select all/... must be bound
806 // to the same actions as in IB otherwise they will not work with dialogs. All
807 // we do here is forward these actions to the Vim process.
809 - (IBAction)cut:(id)sender
811 [[self windowController] vimMenuItemAction:sender];
814 - (IBAction)copy:(id)sender
816 [[self windowController] vimMenuItemAction:sender];
819 - (IBAction)paste:(id)sender
821 [[self windowController] vimMenuItemAction:sender];
824 - (IBAction)undo:(id)sender
826 [[self windowController] vimMenuItemAction:sender];
829 - (IBAction)redo:(id)sender
831 [[self windowController] vimMenuItemAction:sender];
834 - (IBAction)selectAll:(id)sender
836 [[self windowController] vimMenuItemAction:sender];
839 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
841 // View is not flipped, instead the atsui code draws to a flipped image;
842 // thus we need to 'flip' the coordinate here since the column number
843 // increases in an up-to-down order.
844 point.y = [self frame].size.height - point.y;
846 NSPoint origin = { insetSize.width, insetSize.height };
848 if (!(cellSize.width > 0 && cellSize.height > 0))
851 if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
852 if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
857 - (NSPoint)pointForRow:(int)row column:(int)col
859 // Return the lower left coordinate of the cell at (row,column).
862 pt.x = insetSize.width + col*cellSize.width;
863 pt.y = [self frame].size.height -
864 (insetSize.height + (1+row)*cellSize.height);
869 - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
872 // Return the rect for the block which covers the specified rows and
873 // columns. The lower-left corner is the origin of this rect.
876 rect.origin.x = insetSize.width + col*cellSize.width;
877 rect.origin.y = [self frame].size.height -
878 (insetSize.height + (nr+row)*cellSize.height);
879 rect.size.width = nc*cellSize.width;
880 rect.size.height = nr*cellSize.height;
885 - (NSArray *)validAttributesForMarkedText
890 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
895 - (NSUInteger)characterIndexForPoint:(NSPoint)point
900 // The return type of this message changed with OS X 10.5 so we need this
901 // kludge in order to avoid compiler warnings on OS X 10.4.
902 #if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4)
903 - (NSInteger)conversationIdentifier
905 return (NSInteger)self;
908 - (long)conversationIdentifier
914 - (NSRange)selectedRange
916 return [helper imRange];
919 - (NSRect)firstRectForCharacterRange:(NSRange)range
921 return [helper firstRectForCharacterRange:range];
924 @end // MMAtsuiTextView
929 @implementation MMAtsuiTextView (Private)
931 - (void)initAtsuStyles
934 for (i = 0; i < MMMaxCellsPerChar; i++)
935 ATSUCreateStyle(&atsuStyles[i]);
938 - (void)disposeAtsuStyles
942 for (i = 0; i < MMMaxCellsPerChar; i++)
943 if (atsuStyles[i] != NULL)
945 if (ATSUDisposeStyle(atsuStyles[i]) != noErr)
946 atsuStyles[i] = NULL;
950 - (void)updateAtsuStyles
956 CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
957 ATSStyleRenderingOptions options;
959 fontID = [font _atsFontID];
960 fontSize = Long2Fix([font pointSize]);
961 options = kATSStyleApplyAntiAliasing;
963 ATSUAttributeTag attribTags[] =
965 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
966 kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag,
967 kATSUMaxATSUITagValue + 1
970 ByteCount attribSizes[] =
972 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
973 sizeof(CGAffineTransform), sizeof(ATSStyleRenderingOptions),
977 ATSUAttributeValuePtr attribValues[] =
979 &fontID, &fontSize, &fontWidth, &transform, &options, &font
982 ATSUFontFeatureType featureTypes[] = {
983 kLigaturesType, kLigaturesType
986 ATSUFontFeatureSelector featureSelectors[] = {
987 kCommonLigaturesOffSelector, kRareLigaturesOffSelector
990 for (i = 0; i < MMMaxCellsPerChar; i++)
992 fontWidth = Long2Fix(cellSize.width * (i + 1));
994 if (ATSUSetAttributes(atsuStyles[i],
995 (sizeof attribTags) / sizeof(ATSUAttributeTag),
996 attribTags, attribSizes, attribValues) != noErr)
998 ATSUDisposeStyle(atsuStyles[i]);
999 atsuStyles[i] = NULL;
1002 // Turn off ligatures by default
1003 ATSUSetFontFeatures(atsuStyles[i],
1004 sizeof(featureTypes) / sizeof(featureTypes[0]),
1005 featureTypes, featureSelectors);
1009 - (MMWindowController *)windowController
1011 id windowController = [[self window] windowController];
1012 if ([windowController isKindOfClass:[MMWindowController class]])
1013 return (MMWindowController*)windowController;
1017 - (MMVimController *)vimController
1019 return [[self windowController] vimController];
1022 @end // MMAtsuiTextView (Private)
1027 @implementation MMAtsuiTextView (Drawing)
1029 - (NSPoint)originForRow:(int)row column:(int)col
1031 return NSMakePoint(col * cellSize.width, row * cellSize.height);
1034 - (NSRect)rectFromRow:(int)row1 column:(int)col1
1035 toRow:(int)row2 column:(int)col2
1037 NSPoint origin = [self originForRow:row1 column:col1];
1038 return NSMakeRect(origin.x, origin.y,
1039 (col2 + 1 - col1) * cellSize.width,
1040 (row2 + 1 - row1) * cellSize.height);
1043 - (NSSize)textAreaSize
1045 // Calculate the (desired) size of the text area, i.e. the text view area
1046 // minus the inset area.
1047 return NSMakeSize(maxColumns * cellSize.width, maxRows * cellSize.height);
1050 - (void)resizeContentImage
1052 //NSLog(@"resizeContentImage");
1053 [contentImage release];
1054 contentImage = [[NSImage alloc] initWithSize:[self textAreaSize]];
1055 [contentImage setFlipped:YES];
1056 imageSize = [self textAreaSize];
1059 - (void)beginDrawing
1061 [contentImage lockFocus];
1066 [contentImage unlockFocus];
1069 #define atsu_style_set_bool(s, t, b) \
1070 ATSUSetAttributes(s, 1, &t, &(sizeof(Boolean)), &&b);
1071 #define FILL_Y(y) (y * cellSize.height)
1073 - (void)drawString:(UniChar *)string length:(UniCharCount)length
1074 atRow:(int)row column:(int)col cells:(int)cells
1075 withFlags:(int)flags foregroundColor:(NSColor *)fg
1076 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp
1078 // 'string' consists of 'length' utf-16 code pairs and should cover 'cells'
1079 // display cells (a normal character takes up one display cell, a wide
1080 // character takes up two)
1081 ATSUStyle style = (flags & DRAW_WIDE) ? atsuStyles[1] : atsuStyles[0];
1082 ATSUTextLayout layout;
1084 // Font selection and rendering options for ATSUI
1085 ATSUAttributeTag attribTags[3] = { kATSUQDBoldfaceTag,
1087 kATSUStyleRenderingOptionsTag };
1089 ByteCount attribSizes[] = { sizeof(Boolean),
1090 sizeof(CGAffineTransform),
1093 CGAffineTransform theTransform = CGAffineTransformMakeScale(1.0, -1.0);
1094 UInt32 useAntialias;
1096 ATSUAttributeValuePtr attribValues[3] = { &useBold, &theTransform,
1099 useBold = (flags & DRAW_BOLD) ? true : false;
1101 if (flags & DRAW_ITALIC)
1102 theTransform.c = Fix2X(kATSItalicQDSkew);
1104 useAntialias = antialias ? kATSStyleApplyAntiAliasing
1105 : kATSStyleNoAntiAliasing;
1107 ATSUSetAttributes(style, sizeof(attribValues) / sizeof(attribValues[0]),
1108 attribTags, attribSizes, attribValues);
1110 // NSLog(@"drawString: %d", length);
1112 ATSUCreateTextLayout(&layout);
1113 ATSUSetTextPointerLocation(layout, string,
1114 kATSUFromTextBeginning, kATSUToTextEnd,
1116 ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd);
1118 NSRect rect = NSMakeRect(col * cellSize.width, row * cellSize.height,
1119 length * cellSize.width, cellSize.height);
1120 if (flags & DRAW_WIDE)
1121 rect.size.width = rect.size.width * 2;
1122 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
1124 ATSUAttributeTag tags[] = { kATSUCGContextTag };
1125 ByteCount sizes[] = { sizeof(CGContextRef) };
1126 ATSUAttributeValuePtr values[] = { &context };
1127 ATSUSetLayoutControls(layout, 1, tags, sizes, values);
1129 if (! (flags & DRAW_TRANSP))
1137 ATSUSetTransientFontMatching(layout, TRUE);
1138 ATSUDrawText(layout,
1139 kATSUFromTextBeginning,
1141 X2Fix(rect.origin.x),
1142 X2Fix(rect.origin.y + ascender));
1143 ATSUDisposeTextLayout(layout);
1145 if (flags & DRAW_UNDERL)
1148 NSRectFill(NSMakeRect(rect.origin.x,
1149 (row + 1) * cellSize.height + kUnderlineOffset,
1150 rect.size.width, kUnderlineHeight));
1153 if (flags & DRAW_UNDERC)
1157 float line_end_x = rect.origin.x + rect.size.width;
1159 NSRect line_rect = NSMakeRect(
1161 (row + 1) * cellSize.height + kUndercurlOffset,
1162 kUndercurlDotWidth, kUndercurlHeight);
1164 while (line_rect.origin.x < line_end_x)
1167 NSRectFill(line_rect);
1169 line_rect.origin.x += kUndercurlDotDistance;
1175 - (void)scrollRect:(NSRect)rect lineCount:(int)count
1177 NSPoint destPoint = rect.origin;
1178 destPoint.y += count * cellSize.height;
1180 NSCopyBits(0, rect, destPoint);
1183 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
1184 scrollBottom:(int)bottom left:(int)left right:(int)right
1185 color:(NSColor *)color
1187 NSRect rect = [self rectFromRow:row + count
1192 // move rect up for count lines
1193 [self scrollRect:rect lineCount:-count];
1194 [self clearBlockFromRow:bottom - count + 1
1201 - (void)insertLinesAtRow:(int)row lineCount:(int)count
1202 scrollBottom:(int)bottom left:(int)left right:(int)right
1203 color:(NSColor *)color
1205 NSRect rect = [self rectFromRow:row
1207 toRow:bottom - count
1210 // move rect down for count lines
1211 [self scrollRect:rect lineCount:count];
1212 [self clearBlockFromRow:row
1214 toRow:row + count - 1
1219 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
1220 column:(int)col2 color:(NSColor *)color
1223 NSRectFill([self rectFromRow:row1 column:col1 toRow:row2 column:col2]);
1228 [defaultBackgroundColor set];
1229 NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height));
1232 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
1233 fraction:(int)percent
1235 NSPoint origin = [self originForRow:row column:col];
1236 NSRect rect = NSMakeRect(origin.x, origin.y,
1237 cellSize.width, cellSize.height);
1239 // NSLog(@"shape = %d, fraction: %d", shape, percent);
1241 if (MMInsertionPointHorizontal == shape) {
1242 int frac = (cellSize.height * percent + 99)/100;
1243 rect.origin.y += rect.size.height - frac;
1244 rect.size.height = frac;
1245 } else if (MMInsertionPointVertical == shape) {
1246 int frac = (cellSize.width * percent + 99)/100;
1247 rect.size.width = frac;
1248 } else if (MMInsertionPointVerticalRight == shape) {
1249 int frac = (cellSize.width * percent + 99)/100;
1250 rect.origin.x += rect.size.width - frac;
1251 rect.size.width = frac;
1254 [[helper insertionPointColor] set];
1255 if (MMInsertionPointHollow == shape) {
1262 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
1263 numColumns:(int)ncols
1265 // TODO: THIS CODE HAS NOT BEEN TESTED!
1266 CGContextRef cgctx = [[NSGraphicsContext currentContext] graphicsPort];
1267 CGContextSaveGState(cgctx);
1268 CGContextSetBlendMode(cgctx, kCGBlendModeDifference);
1269 CGContextSetRGBFillColor(cgctx, 1.0, 1.0, 1.0, 1.0);
1271 CGRect rect = { col * cellSize.width, row * cellSize.height,
1272 ncols * cellSize.width, nrows * cellSize.height };
1273 CGContextFillRect(cgctx, rect);
1275 CGContextRestoreGState(cgctx);
1278 @end // MMAtsuiTextView (Drawing)