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 CoreText.
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 "Miscellaneous.h" // Defines MM_ENABLE_ATSUI
32 #import "MMAppController.h"
33 #import "MMCoreTextView.h"
34 #import "MMTextViewHelper.h"
35 #import "MMVimController.h"
36 #import "MMWindowController.h"
39 // TODO: What does DRAW_TRANSP flag do? If the background isn't drawn when
40 // this flag is set, then sometimes the character after the cursor becomes
41 // blank. Everything seems to work fine by just ignoring this flag.
42 #define DRAW_TRANSP 0x01 /* draw with transparent bg */
43 #define DRAW_BOLD 0x02 /* draw bold text */
44 #define DRAW_UNDERL 0x04 /* draw underline text */
45 #define DRAW_UNDERC 0x08 /* draw undercurl text */
46 #define DRAW_ITALIC 0x10 /* draw italic text */
47 #define DRAW_CURSOR 0x20
48 #define DRAW_WIDE 0x40 /* draw wide text */
51 #define BLUE(argb) ((argb & 0xff)/255.0f)
52 #define GREEN(argb) (((argb>>8) & 0xff)/255.0f)
53 #define RED(argb) (((argb>>16) & 0xff)/255.0f)
54 #define ALPHA(argb) (((argb>>24) & 0xff)/255.0f)
58 @interface MMCoreTextView (Private)
59 - (MMWindowController *)windowController;
60 - (MMVimController *)vimController;
64 @interface MMCoreTextView (Drawing)
65 - (NSPoint)pointForRow:(int)row column:(int)column;
66 - (NSRect)rectForRow:(int)row column:(int)column numRows:(int)nr
68 - (NSRect)rectFromRow:(int)row1 column:(int)col1
69 toRow:(int)row2 column:(int)col2;
70 - (NSSize)textAreaSize;
71 - (void)batchDrawData:(NSData *)data;
72 - (void)drawString:(const UniChar *)chars length:(UniCharCount)length
73 atRow:(int)row column:(int)col cells:(int)cells
74 withFlags:(int)flags foregroundColor:(int)fg
75 backgroundColor:(int)bg specialColor:(int)sp;
76 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
77 scrollBottom:(int)bottom left:(int)left right:(int)right
79 - (void)insertLinesAtRow:(int)row lineCount:(int)count
80 scrollBottom:(int)bottom left:(int)left right:(int)right
82 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
83 column:(int)col2 color:(int)color;
85 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
86 fraction:(int)percent color:(int)color;
87 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
88 numColumns:(int)ncols;
94 defaultLineHeightForFont(NSFont *font)
96 // HACK: -[NSFont defaultLineHeightForFont] is deprecated but since the
97 // CoreText renderer does not use NSLayoutManager we create one
99 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
100 float height = [lm defaultLineHeightForFont:font];
107 defaultAdvanceForFont(CTFontRef fontRef)
109 // We measure the default advance of a font as the advance of its glyph for
111 double advance = 0.0;
112 UniChar characters[] = { 'm' };
114 CGGlyph glyphs[] = { 0 };
116 if (CTFontGetGlyphsForCharacters(fontRef, characters, glyphs, count)) {
117 advance = CTFontGetAdvancesForGlyphs(fontRef,
118 kCTFontDefaultOrientation,
125 ASLogWarn(@"Could not determine default advance for current font");
132 @implementation MMCoreTextView
134 - (id)initWithFrame:(NSRect)frame
136 if (!(self = [super initWithFrame:frame]))
139 // NOTE! It does not matter which font is set here, Vim will set its
140 // own font on startup anyway. Just set some bogus values.
141 font = [[NSFont userFixedPitchFontOfSize:0] retain];
142 cellSize.width = cellSize.height = 1;
144 // NOTE: If the default changes to 'NO' then the intialization of
145 // p_antialias in option.c must change as well.
148 drawData = [[NSMutableArray alloc] init];
150 helper = [[MMTextViewHelper alloc] init];
151 [helper setTextView:self];
153 [self registerForDraggedTypes:[NSArray arrayWithObjects:
154 NSFilenamesPboardType, NSStringPboardType, nil]];
161 [font release]; font = nil;
162 [defaultBackgroundColor release]; defaultBackgroundColor = nil;
163 [defaultForegroundColor release]; defaultForegroundColor = nil;
164 [drawData release]; drawData = nil;
166 [helper setTextView:nil];
167 [helper release]; helper = nil;
169 if (glyphs) { free(glyphs); glyphs = NULL; }
170 if (advances) { free(advances); advances = NULL; }
185 - (void)getMaxRows:(int*)rows columns:(int*)cols
187 if (rows) *rows = maxRows;
188 if (cols) *cols = maxColumns;
191 - (void)setMaxRows:(int)rows columns:(int)cols
193 // NOTE: Just remember the new values, the actual resizing is done lazily.
198 - (void)setDefaultColorsBackground:(NSColor *)bgColor
199 foreground:(NSColor *)fgColor
201 if (defaultBackgroundColor != bgColor) {
202 [defaultBackgroundColor release];
203 defaultBackgroundColor = bgColor ? [bgColor retain] : nil;
206 // NOTE: The default foreground color isn't actually used for anything, but
207 // other class instances might want to be able to access it so it is stored
209 if (defaultForegroundColor != fgColor) {
210 [defaultForegroundColor release];
211 defaultForegroundColor = fgColor ? [fgColor retain] : nil;
215 - (NSColor *)defaultBackgroundColor
217 return defaultBackgroundColor;
220 - (NSColor *)defaultForegroundColor
222 return defaultForegroundColor;
225 - (void)setTextContainerInset:(NSSize)size
230 - (NSRect)rectForRowsInRange:(NSRange)range
232 // Compute rect whose vertical dimensions cover the rows in the given
234 // NOTE: The rect should be in _flipped_ coordinates and the first row must
235 // include the top inset as well. (This method is only used to place the
236 // scrollbars inside MMVimView.)
238 NSRect rect = { {0, 0}, {0, 0} };
239 unsigned start = range.location > maxRows ? maxRows : range.location;
240 unsigned length = range.length;
242 if (start + length > maxRows)
243 length = maxRows - start;
246 rect.origin.y = cellSize.height * start + insetSize.height;
247 rect.size.height = cellSize.height * length;
251 rect.size.height = cellSize.height * length + insetSize.height;
257 - (NSRect)rectForColumnsInRange:(NSRange)range
259 // Compute rect whose horizontal dimensions cover the columns in the given
261 // NOTE: The first column must include the left inset. (This method is
262 // only used to place the scrollbars inside MMVimView.)
264 NSRect rect = { {0, 0}, {0, 0} };
265 unsigned start = range.location > maxColumns ? maxColumns : range.location;
266 unsigned length = range.length;
268 if (start+length > maxColumns)
269 length = maxColumns - start;
272 rect.origin.x = cellSize.width * start + insetSize.width;
273 rect.size.width = cellSize.width * length;
275 // Include left inset
277 rect.size.width = cellSize.width * length + insetSize.width;
284 - (void)setFont:(NSFont *)newFont
287 if (newFont && font != newFont) {
289 font = [newFont retain];
291 float em = 7.0; //[newFont widthOfString:@"m"];
292 float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
293 floatForKey:MMCellWidthMultiplierKey];
295 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
296 // only render at integer sizes. Hence, we restrict the cell width to
297 // an integer here, otherwise the window width and the actual text
298 // width will not match.
299 cellSize.width = ceilf(em * cellWidthMultiplier);
300 cellSize.height = linespace + 15.0; //[newFont defaultLineHeightForFont];
303 if (!(newFont && font != newFont))
306 double em = round(defaultAdvanceForFont((CTFontRef)newFont));
307 double pt = round([newFont pointSize]);
309 NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys:
310 [newFont displayName], (NSString*)kCTFontNameAttribute,
311 [NSNumber numberWithFloat:pt], (NSString*)kCTFontSizeAttribute,
312 //[NSNumber numberWithFloat:em], (NSString*)kCTFontFixedAdvanceAttribute,
314 CTFontDescriptorRef desc = CTFontDescriptorCreateWithAttributes(
315 (CFDictionaryRef)attr);
316 CTFontRef fontRef = CTFontCreateWithFontDescriptor(desc, pt, NULL);
320 font = (NSFont*)fontRef;
322 float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
323 floatForKey:MMCellWidthMultiplierKey];
325 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
326 // only render at integer sizes. Hence, we restrict the cell width to
327 // an integer here, otherwise the window width and the actual text
328 // width will not match.
329 cellSize.width = ceil(em * cellWidthMultiplier);
330 cellSize.height = linespace + defaultLineHeightForFont(font);
332 fontDescent = ceil(CTFontGetDescent(fontRef));
336 - (void)setWideFont:(NSFont *)newFont
355 - (void)setLinespace:(float)newLinespace
357 linespace = newLinespace;
359 // NOTE: The linespace is added to the cell height in order for a multiline
360 // selection not to have white (background color) gaps between lines. Also
361 // this simplifies the code a lot because there is no need to check the
362 // linespace when calculating the size of the text view etc. When the
363 // linespace is non-zero the baseline will be adjusted as well; check
365 cellSize.height = linespace + defaultLineHeightForFont(font);
371 - (void)setShouldDrawInsertionPoint:(BOOL)on
375 - (void)setPreEditRow:(int)row column:(int)col
379 - (void)setMouseShape:(int)shape
381 [helper setMouseShape:shape];
384 - (void)setAntialias:(BOOL)state
389 - (void)setImControl:(BOOL)enable
391 [helper setImControl:enable];
394 - (void)activateIm:(BOOL)enable
396 [helper activateIm:enable];
399 - (BOOL)_wantsKeyDownForEvent:(id)event
401 // HACK! This is an undocumented method which is called from within
402 // -[NSWindow sendEvent] (and perhaps in other places as well) when the
403 // user presses e.g. Ctrl-Tab or Ctrl-Esc . Returning YES here effectively
404 // disables the Cocoa "key view loop" (which is undesirable). It may have
405 // other side-effects, but we really _do_ want to process all key down
406 // events so it seems safe to always return YES.
410 - (void)keyDown:(NSEvent *)event
412 [helper keyDown:event];
415 - (void)insertText:(id)string
417 [helper insertText:string];
420 - (void)doCommandBySelector:(SEL)selector
422 [helper doCommandBySelector:selector];
425 - (BOOL)performKeyEquivalent:(NSEvent *)event
427 return [helper performKeyEquivalent:event];
430 - (BOOL)hasMarkedText
432 return [helper hasMarkedText];
435 - (NSRange)markedRange
437 return [helper markedRange];
440 - (NSDictionary *)markedTextAttributes
442 return [helper markedTextAttributes];
445 - (void)setMarkedTextAttributes:(NSDictionary *)attr
447 [helper setMarkedTextAttributes:attr];
450 - (void)setMarkedText:(id)text selectedRange:(NSRange)range
452 [helper setMarkedText:text selectedRange:range];
460 - (void)scrollWheel:(NSEvent *)event
462 [helper scrollWheel:event];
465 - (void)mouseDown:(NSEvent *)event
467 [helper mouseDown:event];
470 - (void)rightMouseDown:(NSEvent *)event
472 [helper mouseDown:event];
475 - (void)otherMouseDown:(NSEvent *)event
477 [helper mouseDown:event];
480 - (void)mouseUp:(NSEvent *)event
482 [helper mouseUp:event];
485 - (void)rightMouseUp:(NSEvent *)event
487 [helper mouseUp:event];
490 - (void)otherMouseUp:(NSEvent *)event
492 [helper mouseUp:event];
495 - (void)mouseDragged:(NSEvent *)event
497 [helper mouseDragged:event];
500 - (void)rightMouseDragged:(NSEvent *)event
502 [helper mouseDragged:event];
505 - (void)otherMouseDragged:(NSEvent *)event
507 [helper mouseDragged:event];
510 - (void)mouseMoved:(NSEvent *)event
512 [helper mouseMoved:event];
515 - (void)mouseEntered:(NSEvent *)event
517 [helper mouseEntered:event];
520 - (void)mouseExited:(NSEvent *)event
522 [helper mouseExited:event];
525 - (void)setFrame:(NSRect)frame
527 [super setFrame:frame];
528 [helper setFrame:frame];
531 - (void)viewDidMoveToWindow
533 [helper viewDidMoveToWindow];
536 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
538 [helper viewWillMoveToWindow:newWindow];
541 - (NSMenu*)menuForEvent:(NSEvent *)event
543 // HACK! Return nil to disable default popup menus (Vim provides its own).
544 // Called when user Ctrl-clicks in the view (this is already handled in
549 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
551 return [helper performDragOperation:sender];
554 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
556 return [helper draggingEntered:sender];
559 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
561 return [helper draggingUpdated:sender];
566 - (BOOL)mouseDownCanMoveWindow
576 - (BOOL)acceptsFirstResponder
586 - (void)drawRect:(NSRect)rect
588 //ASLogTmp(@"count=%d rect=%@", [drawData count],
589 // NSStringFromRect(rect));
591 NSGraphicsContext *context = [NSGraphicsContext currentContext];
592 [context setShouldAntialias:antialias];
595 NSEnumerator *e = [drawData objectEnumerator];
596 while ((data = [e nextObject]))
597 [self batchDrawData:data];
599 [drawData removeAllObjects];
602 - (void)performBatchDrawWithData:(NSData *)data
604 [drawData addObject:data];
605 [self setNeedsDisplay:YES];
607 // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
608 // and columns are changed (due to ipc delays). Force a redraw here.
609 if ([self inLiveResize])
613 - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size
616 // - Rounding errors may cause size change when there should be none
617 // - Desired rows/columns shold not be 'too small'
619 // Constrain the desired size to the given size. Values for the minimum
620 // rows and columns are taken from Vim.
621 NSSize desiredSize = [self desiredSize];
622 int desiredRows = maxRows;
623 int desiredCols = maxColumns;
624 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
625 int right = [ud integerForKey:MMTextInsetRightKey];
626 int bot = [ud integerForKey:MMTextInsetBottomKey];
628 if (size.height != desiredSize.height) {
629 float fh = cellSize.height;
630 float ih = insetSize.height + bot;
631 if (fh < 1.0f) fh = 1.0f;
633 desiredRows = floor((size.height - ih)/fh);
634 desiredSize.height = fh*desiredRows + ih;
637 if (size.width != desiredSize.width) {
638 float fw = cellSize.width;
639 float iw = insetSize.width + right;
640 if (fw < 1.0f) fw = 1.0f;
642 desiredCols = floor((size.width - iw)/fw);
643 desiredSize.width = fw*desiredCols + iw;
646 if (rows) *rows = desiredRows;
647 if (cols) *cols = desiredCols;
652 - (NSSize)desiredSize
654 // Compute the size the text view should be for the entire text area and
655 // inset area to be visible with the present number of rows and columns.
656 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
657 int right = [ud integerForKey:MMTextInsetRightKey];
658 int bot = [ud integerForKey:MMTextInsetBottomKey];
660 return NSMakeSize(maxColumns * cellSize.width + insetSize.width + right,
661 maxRows * cellSize.height + insetSize.height + bot);
666 // Compute the smallest size the text view is allowed to be.
667 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
668 int right = [ud integerForKey:MMTextInsetRightKey];
669 int bot = [ud integerForKey:MMTextInsetBottomKey];
671 return NSMakeSize(MMMinColumns * cellSize.width + insetSize.width + right,
672 MMMinRows * cellSize.height + insetSize.height + bot);
675 - (void)changeFont:(id)sender
677 NSFont *newFont = [sender convertFont:font];
680 NSString *name = [newFont displayName];
681 unsigned len = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
683 NSMutableData *data = [NSMutableData data];
684 float pointSize = [newFont pointSize];
686 [data appendBytes:&pointSize length:sizeof(float)];
688 ++len; // include NUL byte
689 [data appendBytes:&len length:sizeof(unsigned)];
690 [data appendBytes:[name UTF8String] length:len];
692 [[self vimController] sendMessage:SetFontMsgID data:data];
699 // NOTE: The menu items cut/copy/paste/undo/redo/select all/... must be bound
700 // to the same actions as in IB otherwise they will not work with dialogs. All
701 // we do here is forward these actions to the Vim process.
703 - (IBAction)cut:(id)sender
705 [[self windowController] vimMenuItemAction:sender];
708 - (IBAction)copy:(id)sender
710 [[self windowController] vimMenuItemAction:sender];
713 - (IBAction)paste:(id)sender
715 [[self windowController] vimMenuItemAction:sender];
718 - (IBAction)undo:(id)sender
720 [[self windowController] vimMenuItemAction:sender];
723 - (IBAction)redo:(id)sender
725 [[self windowController] vimMenuItemAction:sender];
728 - (IBAction)selectAll:(id)sender
730 [[self windowController] vimMenuItemAction:sender];
733 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
735 // View is not flipped, instead the atsui code draws to a flipped image;
736 // thus we need to 'flip' the coordinate here since the column number
737 // increases in an up-to-down order.
738 point.y = [self bounds].size.height - point.y;
740 NSPoint origin = { insetSize.width, insetSize.height };
742 if (!(cellSize.width > 0 && cellSize.height > 0))
745 if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
746 if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
748 //ASLogDebug(@"point=%@ row=%d col=%d",
749 // NSStringFromPoint(point), *row, *column);
754 - (NSArray *)validAttributesForMarkedText
759 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
764 - (NSUInteger)characterIndexForPoint:(NSPoint)point
769 - (NSInteger)conversationIdentifier
771 return (NSInteger)self;
774 - (NSRange)selectedRange
776 return [helper imRange];
779 - (NSRect)firstRectForCharacterRange:(NSRange)range
781 return [helper firstRectForCharacterRange:range];
784 @end // MMCoreTextView
789 @implementation MMCoreTextView (Private)
791 - (MMWindowController *)windowController
793 id windowController = [[self window] windowController];
794 if ([windowController isKindOfClass:[MMWindowController class]])
795 return (MMWindowController*)windowController;
799 - (MMVimController *)vimController
801 return [[self windowController] vimController];
804 @end // MMCoreTextView (Private)
809 @implementation MMCoreTextView (Drawing)
811 - (NSPoint)pointForRow:(int)row column:(int)col
813 NSRect frame = [self bounds];
815 col*cellSize.width + insetSize.width,
816 frame.size.height - (row+1)*cellSize.height - insetSize.height);
819 - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
823 NSRect frame = [self bounds];
825 rect.origin.x = col*cellSize.width + insetSize.width;
826 rect.origin.y = frame.size.height - (row+nr)*cellSize.height -
828 rect.size.width = nc*cellSize.width;
829 rect.size.height = nr*cellSize.height;
834 - (NSRect)rectFromRow:(int)row1 column:(int)col1
835 toRow:(int)row2 column:(int)col2
837 NSRect frame = [self bounds];
839 insetSize.width + col1*cellSize.width,
840 frame.size.height - insetSize.height - (row2+1)*cellSize.height,
841 (col2 + 1 - col1) * cellSize.width,
842 (row2 + 1 - row1) * cellSize.height);
845 - (NSSize)textAreaSize
847 // Calculate the (desired) size of the text area, i.e. the text view area
848 // minus the inset area.
849 return NSMakeSize(maxColumns * cellSize.width, maxRows * cellSize.height);
852 #define MM_DEBUG_DRAWING 0
854 - (void)batchDrawData:(NSData *)data
856 const void *bytes = [data bytes];
857 const void *end = bytes + [data length];
860 ASLogNotice(@"====> BEGIN %s", _cmd);
862 // TODO: Sanity check input
864 while (bytes < end) {
865 int type = *((int*)bytes); bytes += sizeof(int);
867 if (ClearAllDrawType == type) {
869 ASLogNotice(@" Clear all");
872 } else if (ClearBlockDrawType == type) {
873 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
874 int row1 = *((int*)bytes); bytes += sizeof(int);
875 int col1 = *((int*)bytes); bytes += sizeof(int);
876 int row2 = *((int*)bytes); bytes += sizeof(int);
877 int col2 = *((int*)bytes); bytes += sizeof(int);
880 ASLogNotice(@" Clear block (%d,%d) -> (%d,%d)", row1, col1,
883 [self clearBlockFromRow:row1 column:col1
884 toRow:row2 column:col2
886 } else if (DeleteLinesDrawType == type) {
887 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
888 int row = *((int*)bytes); bytes += sizeof(int);
889 int count = *((int*)bytes); bytes += sizeof(int);
890 int bot = *((int*)bytes); bytes += sizeof(int);
891 int left = *((int*)bytes); bytes += sizeof(int);
892 int right = *((int*)bytes); bytes += sizeof(int);
895 ASLogNotice(@" Delete %d line(s) from %d", count, row);
897 [self deleteLinesFromRow:row lineCount:count
898 scrollBottom:bot left:left right:right
900 } else if (DrawStringDrawType == type) {
901 int bg = *((int*)bytes); bytes += sizeof(int);
902 int fg = *((int*)bytes); bytes += sizeof(int);
903 int sp = *((int*)bytes); bytes += sizeof(int);
904 int row = *((int*)bytes); bytes += sizeof(int);
905 int col = *((int*)bytes); bytes += sizeof(int);
906 int cells = *((int*)bytes); bytes += sizeof(int);
907 int flags = *((int*)bytes); bytes += sizeof(int);
908 int len = *((int*)bytes); bytes += sizeof(int);
909 UInt8 *s = (UInt8 *)bytes; bytes += len;
912 ASLogNotice(@" Draw string len=%d row=%d col=%d flags=%#x",
913 len, row, col, flags);
916 // Convert UTF-8 chars to UTF-16
917 CFStringRef sref = CFStringCreateWithBytesNoCopy(NULL, s, len,
918 kCFStringEncodingUTF8, false, kCFAllocatorNull);
919 CFIndex unilength = CFStringGetLength(sref);
920 const UniChar *unichars = CFStringGetCharactersPtr(sref);
921 UniChar *buffer = NULL;
922 if (unichars == NULL) {
923 buffer = malloc(unilength * sizeof(UniChar));
924 CFStringGetCharacters(sref, CFRangeMake(0, unilength), buffer);
928 [self drawString:unichars length:unilength
929 atRow:row column:col cells:cells
940 } else if (InsertLinesDrawType == type) {
941 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
942 int row = *((int*)bytes); bytes += sizeof(int);
943 int count = *((int*)bytes); bytes += sizeof(int);
944 int bot = *((int*)bytes); bytes += sizeof(int);
945 int left = *((int*)bytes); bytes += sizeof(int);
946 int right = *((int*)bytes); bytes += sizeof(int);
949 ASLogNotice(@" Insert %d line(s) at row %d", count, row);
951 [self insertLinesAtRow:row lineCount:count
952 scrollBottom:bot left:left right:right
954 } else if (DrawCursorDrawType == type) {
955 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
956 int row = *((int*)bytes); bytes += sizeof(int);
957 int col = *((int*)bytes); bytes += sizeof(int);
958 int shape = *((int*)bytes); bytes += sizeof(int);
959 int percent = *((int*)bytes); bytes += sizeof(int);
962 ASLogNotice(@" Draw cursor at (%d,%d)", row, col);
964 [self drawInsertionPointAtRow:row column:col shape:shape
967 } else if (DrawInvertedRectDrawType == type) {
968 int row = *((int*)bytes); bytes += sizeof(int);
969 int col = *((int*)bytes); bytes += sizeof(int);
970 int nr = *((int*)bytes); bytes += sizeof(int);
971 int nc = *((int*)bytes); bytes += sizeof(int);
972 /*int invert = *((int*)bytes);*/ bytes += sizeof(int);
975 ASLogNotice(@" Draw inverted rect: row=%d col=%d nrows=%d "
976 "ncols=%d", row, col, nr, nc);
978 [self drawInvertedRectAtRow:row column:col numRows:nr
980 } else if (SetCursorPosDrawType == type) {
981 // TODO: This is used for Voice Over support in MMTextView,
982 // MMCoreTextView currently does not support Voice Over.
984 int row = *((int*)bytes); bytes += sizeof(int);
985 int col = *((int*)bytes); bytes += sizeof(int);
986 ASLogNotice(@" Set cursor row=%d col=%d", row, col);
988 /*cursorRow = *((int*)bytes);*/ bytes += sizeof(int);
989 /*cursorCol = *((int*)bytes);*/ bytes += sizeof(int);
992 ASLogWarn(@"Unknown draw type (type=%d)", type);
997 ASLogNotice(@"<==== END %s", _cmd);
1002 recurseDraw(const unichar *chars, CGGlyph *glyphs, CGSize *advances,
1003 UniCharCount length, CGContextRef context, CTFontRef fontRef,
1006 CGFontRef cgFontRef = CTFontCopyGraphicsFont(fontRef, NULL);
1008 if (CTFontGetGlyphsForCharacters(fontRef, chars, glyphs, length)) {
1009 // All chars were mapped to glyphs, so draw all at once and return.
1010 CGContextSetFont(context, cgFontRef);
1011 CGContextSetTextPosition(context, x, y);
1012 CGContextShowGlyphsWithAdvances(context, glyphs, advances, length);
1013 CGFontRelease(cgFontRef);
1017 CGGlyph *glyphsEnd = glyphs+length, *g = glyphs;
1018 CGSize *a = advances;
1019 const unichar *c = chars;
1021 while (glyphs < glyphsEnd) {
1023 // Draw as many consecutive glyphs as possible in the current font
1024 // (if a glyph is 0 that means it does not exist in the current
1026 while (*g && g < glyphsEnd) {
1033 int count = g-glyphs;
1034 CGContextSetFont(context, cgFontRef);
1035 CGContextSetTextPosition(context, x0, y);
1036 CGContextShowGlyphsWithAdvances(context, glyphs, advances, count);
1038 // Skip past as many consecutive chars as possible which cannot be
1039 // drawn in the current font.
1040 while (0 == *g && g < glyphsEnd) {
1047 // Figure out which font to draw these chars with.
1048 UniCharCount count = c - chars;
1049 CFRange r = { 0, count };
1050 CFStringRef strRef = CFStringCreateWithCharactersNoCopy(
1051 NULL, chars, count, kCFAllocatorNull);
1052 CTFontRef newFontRef = CTFontCreateForString(fontRef, strRef, r);
1054 ASLogNotice(@"Cannot find font to draw chars: %@", strRef);
1055 CGFontRelease(cgFontRef);
1060 recurseDraw(chars, glyphs, advances, count, context, newFontRef,
1063 CFRelease(newFontRef);
1072 CGFontRelease(cgFontRef);
1075 - (void)drawString:(const UniChar *)chars length:(UniCharCount)length
1076 atRow:(int)row column:(int)col cells:(int)cells
1077 withFlags:(int)flags foregroundColor:(int)fg
1078 backgroundColor:(int)bg specialColor:(int)sp
1080 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
1081 NSRect frame = [self bounds];
1082 float x = col*cellSize.width + insetSize.width;
1083 float y = frame.size.height - insetSize.height - (1+row)*cellSize.height;
1084 float w = cellSize.width;
1086 if (flags & DRAW_WIDE) {
1087 // NOTE: It is assumed that either all characters in 'chars' are wide
1088 // or all are normal width.
1092 CGContextSaveGState(context);
1094 // NOTE! 'cells' is zero if we're drawing a composing character
1095 CGFloat clipWidth = cells > 0 ? cells*cellSize.width : w;
1096 CGRect clipRect = { {x, y}, {clipWidth, cellSize.height} };
1097 CGContextClipToRect(context, clipRect);
1099 if (!(flags & DRAW_TRANSP)) {
1100 // Draw the background of the text. Note that if we ignore the
1101 // DRAW_TRANSP flag and always draw the background, then the insert
1102 // mode cursor is drawn over.
1103 CGRect rect = { {x, y}, {cells*cellSize.width, cellSize.height} };
1104 CGContextSetRGBFillColor(context, RED(bg), GREEN(bg), BLUE(bg),
1107 // Antialiasing may cause bleeding effects which are highly undesirable
1108 // when clearing the background (this code is also called to draw the
1109 // cursor sometimes) so disable it temporarily.
1110 CGContextSetShouldAntialias(context, NO);
1111 CGContextSetBlendMode(context, kCGBlendModeCopy);
1112 CGContextFillRect(context, rect);
1113 CGContextSetShouldAntialias(context, antialias);
1114 CGContextSetBlendMode(context, kCGBlendModeNormal);
1117 if (flags & DRAW_UNDERL) {
1119 CGRect rect = { {x, y+0.4*fontDescent}, {cells*cellSize.width, 1} };
1120 CGContextSetRGBFillColor(context, RED(sp), GREEN(sp), BLUE(sp),
1122 CGContextFillRect(context, rect);
1123 } else if (flags & DRAW_UNDERC) {
1124 // Draw curly underline
1126 float x0 = x, y0 = y+1, w = cellSize.width, h = 0.5*fontDescent;
1128 CGContextMoveToPoint(context, x0, y0);
1129 for (k = 0; k < cells; ++k) {
1130 CGContextAddCurveToPoint(context, x0+0.25*w, y0, x0+0.25*w, y0+h,
1132 CGContextAddCurveToPoint(context, x0+0.75*w, y0+h, x0+0.75*w, y0,
1137 CGContextSetRGBStrokeColor(context, RED(sp), GREEN(sp), BLUE(sp),
1139 CGContextStrokePath(context);
1142 if (length > maxlen) {
1143 if (glyphs) free(glyphs);
1144 if (advances) free(advances);
1145 glyphs = (CGGlyph*)malloc(length*sizeof(CGGlyph));
1146 advances = (CGSize*)calloc(length, sizeof(CGSize));
1150 CGContextSetTextMatrix(context, CGAffineTransformIdentity);
1151 CGContextSetTextDrawingMode(context, kCGTextFill);
1152 CGContextSetRGBFillColor(context, RED(fg), GREEN(fg), BLUE(fg), ALPHA(fg));
1153 CGContextSetFontSize(context, [font pointSize]);
1156 for (i = 0; i < length; ++i)
1157 advances[i].width = w;
1159 CTFontRef fontRef = (CTFontRef)[font retain];
1161 unsigned traits = 0;
1162 if (flags & DRAW_ITALIC)
1163 traits |= kCTFontItalicTrait;
1164 if (flags & DRAW_BOLD)
1165 traits |= kCTFontBoldTrait;
1168 CTFontRef fr = CTFontCreateCopyWithSymbolicTraits(fontRef, 0.0, NULL,
1176 recurseDraw(chars, glyphs, advances, length, context, fontRef, x,
1180 CGContextRestoreGState(context);
1183 - (void)scrollRect:(NSRect)rect lineCount:(int)count
1185 NSPoint destPoint = rect.origin;
1186 destPoint.y -= count * cellSize.height;
1188 NSCopyBits(0, rect, destPoint);
1191 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
1192 scrollBottom:(int)bottom left:(int)left right:(int)right
1195 NSRect rect = [self rectFromRow:row + count
1200 // move rect up for count lines
1201 [self scrollRect:rect lineCount:-count];
1202 [self clearBlockFromRow:bottom - count + 1
1209 - (void)insertLinesAtRow:(int)row lineCount:(int)count
1210 scrollBottom:(int)bottom left:(int)left right:(int)right
1213 NSRect rect = [self rectFromRow:row
1215 toRow:bottom - count
1218 // move rect down for count lines
1219 [self scrollRect:rect lineCount:count];
1220 [self clearBlockFromRow:row
1222 toRow:row + count - 1
1227 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
1228 column:(int)col2 color:(int)color
1230 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
1231 NSRect rect = [self rectFromRow:row1 column:col1 toRow:row2 column:col2];
1233 CGContextSetRGBFillColor(context, RED(color), GREEN(color), BLUE(color),
1236 CGContextSetBlendMode(context, kCGBlendModeCopy);
1237 CGContextFillRect(context, *(CGRect*)&rect);
1238 CGContextSetBlendMode(context, kCGBlendModeNormal);
1243 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
1244 NSRect rect = [self bounds];
1245 float r = [defaultBackgroundColor redComponent];
1246 float g = [defaultBackgroundColor greenComponent];
1247 float b = [defaultBackgroundColor blueComponent];
1248 float a = [defaultBackgroundColor alphaComponent];
1250 CGContextSetBlendMode(context, kCGBlendModeCopy);
1251 CGContextSetRGBFillColor(context, r, g, b, a);
1252 CGContextFillRect(context, *(CGRect*)&rect);
1253 CGContextSetBlendMode(context, kCGBlendModeNormal);
1256 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
1257 fraction:(int)percent color:(int)color
1259 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
1260 NSRect rect = [self rectForRow:row column:col numRows:1 numColumns:1];
1262 if (MMInsertionPointHorizontal == shape) {
1263 int frac = (cellSize.height * percent + 99)/100;
1264 rect.size.height = frac;
1265 } else if (MMInsertionPointVertical == shape) {
1266 int frac = (cellSize.width * percent + 99)/100;
1267 rect.size.width = frac;
1268 } else if (MMInsertionPointVerticalRight == shape) {
1269 int frac = (cellSize.width * percent + 99)/100;
1270 rect.origin.x += rect.size.width - frac;
1271 rect.size.width = frac;
1274 // Temporarily disable antialiasing since we are only drawing square
1275 // cursors. Failing to disable antialiasing can cause the cursor to bleed
1276 // over into adjacent display cells and it may look ugly.
1277 CGContextSetShouldAntialias(context, NO);
1279 if (MMInsertionPointHollow == shape) {
1280 // When stroking a rect its size is effectively 1 pixel wider/higher
1281 // than we want so make it smaller to avoid having it bleed over into
1282 // the adjacent display cell.
1283 rect.size.width -= 1;
1284 rect.size.height -= 1;
1286 CGContextSetRGBStrokeColor(context, RED(color), GREEN(color),
1287 BLUE(color), ALPHA(color));
1288 CGContextStrokeRect(context, *(CGRect*)&rect);
1290 CGContextSetRGBFillColor(context, RED(color), GREEN(color), BLUE(color),
1292 CGContextFillRect(context, *(CGRect*)&rect);
1295 CGContextSetShouldAntialias(context, antialias);
1298 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
1299 numColumns:(int)ncols
1301 // TODO: THIS CODE HAS NOT BEEN TESTED!
1302 CGContextRef cgctx = [[NSGraphicsContext currentContext] graphicsPort];
1303 CGContextSaveGState(cgctx);
1304 CGContextSetBlendMode(cgctx, kCGBlendModeDifference);
1305 CGContextSetRGBFillColor(cgctx, 1.0, 1.0, 1.0, 1.0);
1307 NSRect rect = [self rectForRow:row column:col numRows:nrows
1309 CGContextFillRect(cgctx, *(CGRect*)&rect);
1311 CGContextRestoreGState(cgctx);
1314 @end // MMCoreTextView (Drawing)
1316 #endif // !MM_ENABLE_ATSUI