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
17 #import "MMAtsuiTextView.h"
18 #import "MMVimController.h"
21 static char MMKeypadEnter[2] = { 'K', 'A' };
22 static NSString *MMKeypadEnterString = @"KA";
24 @interface NSFont (AppKitPrivate)
25 - (ATSUFontID) _atsFontID;
28 @interface MMAtsuiTextView (Private)
29 - (void)initAtsuStyles;
30 - (void)disposeAtsuStyles;
31 - (void)updateAtsuStyles;
32 - (void)dispatchKeyEvent:(NSEvent *)event;
33 - (void)sendKeyDown:(const char *)chars length:(int)len modifiers:(int)flags;
34 - (MMVimController *)vimController;
37 @interface MMAtsuiTextView (Drawing)
38 - (void)fitImageToSize;
41 - (void)drawString:(UniChar *)string length:(UniCharCount)length
42 atRow:(int)row column:(int)col cells:(int)cells
43 withFlags:(int)flags foregroundColor:(NSColor *)fg
44 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp;
45 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
46 scrollBottom:(int)bottom left:(int)left right:(int)right
47 color:(NSColor *)color;
48 - (void)insertLinesAtRow:(int)row lineCount:(int)count
49 scrollBottom:(int)bottom left:(int)left right:(int)right
50 color:(NSColor *)color;
51 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
52 column:(int)col2 color:(NSColor *)color;
57 @implementation MMAtsuiTextView
59 - (id)initWithFrame:(NSRect)frame
61 if ((self = [super initWithFrame:frame])) {
62 // NOTE! It does not matter which font is set here, Vim will set its
63 // own font on startup anyway. Just set some bogus values.
64 font = [[NSFont userFixedPitchFontOfSize:0] retain];
65 cellSize.width = cellSize.height = 1;
67 imageSize = NSZeroSize;
69 [self initAtsuStyles];
77 [self disposeAtsuStyles];
78 [font release]; font = nil;
79 [defaultBackgroundColor release]; defaultBackgroundColor = nil;
80 [defaultForegroundColor release]; defaultForegroundColor = nil;
85 - (void)getMaxRows:(int*)rows columns:(int*)cols
87 if (rows) *rows = maxRows;
88 if (cols) *cols = maxColumns;
91 - (void)setMaxRows:(int)rows columns:(int)cols
93 // NOTE: Just remember the new values, the actual resizing is done lazily.
98 - (void)setDefaultColorsBackground:(NSColor *)bgColor
99 foreground:(NSColor *)fgColor
101 if (defaultBackgroundColor != bgColor) {
102 [defaultBackgroundColor release];
103 defaultBackgroundColor = bgColor ? [bgColor retain] : nil;
106 // NOTE: The default foreground color isn't actually used for anything, but
107 // other class instances might want to be able to access it so it is stored
109 if (defaultForegroundColor != fgColor) {
110 [defaultForegroundColor release];
111 defaultForegroundColor = fgColor ? [fgColor retain] : nil;
117 return NSMakeSize(maxColumns*cellSize.width, maxRows*cellSize.height);
120 - (NSSize)fitToSize:(NSSize)size rows:(int *)rows columns:(int *)columns
122 NSSize curSize = [self size];
123 NSSize fitSize = curSize;
124 int fitRows = maxRows;
125 int fitCols = maxColumns;
127 if (size.height < curSize.height) {
128 // Remove lines until the height of the text storage fits inside
129 // 'size'. However, always make sure there are at least 3 lines in the
130 // text storage. (Why 3? It seem Vim never allows less than 3 lines.)
132 // TODO: No need to search since line height is fixed, just calculate
134 int rowCount = maxRows;
136 for (rowsToRemove = 0; rowsToRemove < maxRows-3; ++rowsToRemove) {
137 float height = cellSize.height*rowCount;
139 if (height <= size.height) {
140 fitSize.height = height;
147 fitRows -= rowsToRemove;
148 } else if (size.height > curSize.height) {
149 float fh = cellSize.height;
150 if (fh < 1.0f) fh = 1.0f;
152 fitRows = floor(size.height/fh);
153 fitSize.height = fh*fitRows;
156 if (size.width != curSize.width) {
157 float fw = cellSize.width;
158 if (fw < 1.0f) fw = 1.0f;
160 fitCols = floor(size.width/fw);
161 fitSize.width = fw*fitCols;
164 if (rows) *rows = fitRows;
165 if (columns) *columns = fitCols;
170 - (NSRect)rectForRowsInRange:(NSRange)range
172 NSRect rect = { 0, 0, 0, 0 };
173 unsigned start = range.location > maxRows ? maxRows : range.location;
174 unsigned length = range.length;
176 if (start+length > maxRows)
177 length = maxRows - start;
179 rect.origin.y = cellSize.height * start;
180 rect.size.height = cellSize.height * length;
185 - (NSRect)rectForColumnsInRange:(NSRange)range
187 NSRect rect = { 0, 0, 0, 0 };
188 unsigned start = range.location > maxColumns ? maxColumns : range.location;
189 unsigned length = range.length;
191 if (start+length > maxColumns)
192 length = maxColumns - start;
194 rect.origin.x = cellSize.width * start;
195 rect.size.width = cellSize.width * length;
201 - (void)setFont:(NSFont *)newFont
203 if (newFont && font != newFont) {
205 font = [newFont retain];
207 float em = [newFont widthOfString:@"m"];
208 float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
209 floatForKey:MMCellWidthMultiplierKey];
211 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
212 // only render at integer sizes. Hence, we restrict the cell width to
213 // an integer here, otherwise the window width and the actual text
214 // width will not match.
215 cellSize.width = ceilf(em * cellWidthMultiplier);
216 cellSize.height = linespace + [newFont defaultLineHeightForFont];
218 [self updateAtsuStyles];
222 - (void)setWideFont:(NSFont *)newFont
236 - (void)setLinespace:(float)newLinespace
238 linespace = newLinespace;
240 // NOTE: The linespace is added to the cell height in order for a multiline
241 // selection not to have white (background color) gaps between lines. Also
242 // this simplifies the code a lot because there is no need to check the
243 // linespace when calculating the size of the text view etc. When the
244 // linespace is non-zero the baseline will be adjusted as well; check
246 cellSize.height = linespace + [font defaultLineHeightForFont];
252 - (NSEvent *)lastMouseDownEvent
257 - (void)setShouldDrawInsertionPoint:(BOOL)on
261 - (void)setPreEditRow:(int)row column:(int)col
265 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
266 fraction:(int)percent color:(NSColor *)color
270 - (void)hideMarkedTextField
277 - (void)keyDown:(NSEvent *)event
279 //NSLog(@"%s %@", _cmd, event);
280 // HACK! If control modifier is held, don't pass the event along to
281 // interpretKeyEvents: since some keys are bound to multiple commands which
282 // means doCommandBySelector: is called several times.
284 // TODO: Figure out a way to disable Cocoa key bindings entirely, without
285 // affecting input management.
286 if ([event modifierFlags] & NSControlKeyMask) {
287 NSString *unmod = [event charactersIgnoringModifiers];
288 if ([unmod length] == 1 && [unmod characterAtIndex:0] <= 0x7f
289 && [unmod characterAtIndex:0] >= 0x60) {
290 // HACK! Send Ctrl-letter keys (and C-@, C-[, C-\, C-], C-^, C-_)
291 // as normal text to be added to the Vim input buffer. This must
292 // be done in order for the backend to be able to separate e.g.
293 // Ctrl-i and Ctrl-tab.
294 [self insertText:[event characters]];
296 [self dispatchKeyEvent:event];
299 [super keyDown:event];
303 - (void)insertText:(id)string
305 //NSLog(@"%s %@", _cmd, string);
306 // NOTE! This method is called for normal key presses but also for
307 // Option-key presses --- even when Ctrl is held as well as Option. When
308 // Ctrl is held, the AppKit translates the character to a Ctrl+key stroke,
309 // so 'string' need not be a printable character! In this case it still
310 // works to pass 'string' on to Vim as a printable character (since
311 // modifiers are already included and should not be added to the input
312 // buffer using CSI, K_MODIFIER).
314 [self hideMarkedTextField];
316 NSEvent *event = [NSApp currentEvent];
318 // HACK! In order to be able to bind to <S-Space>, <S-M-Tab>, etc. we have
319 // to watch for them here.
320 if ([event type] == NSKeyDown
321 && [[event charactersIgnoringModifiers] length] > 0
322 && [event modifierFlags]
323 & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask)) {
324 unichar c = [[event charactersIgnoringModifiers] characterAtIndex:0];
326 // <S-M-Tab> translates to 0x19
327 if (' ' == c || 0x19 == c) {
328 [self dispatchKeyEvent:event];
333 // TODO: Support 'mousehide' (check p_mh)
334 [NSCursor setHiddenUntilMouseMoves:YES];
336 // NOTE: 'string' is either an NSString or an NSAttributedString. Since we
337 // do not support attributes, simply pass the corresponding NSString in the
339 if ([string isKindOfClass:[NSAttributedString class]])
340 string = [string string];
342 //NSLog(@"send InsertTextMsgID: %@", string);
344 [[self vimController] sendMessage:InsertTextMsgID
345 data:[string dataUsingEncoding:NSUTF8StringEncoding]];
348 - (void)doCommandBySelector:(SEL)selector
350 //NSLog(@"%s %@", _cmd, NSStringFromSelector(selector));
351 // By ignoring the selector we effectively disable the key binding
352 // mechanism of Cocoa. Hopefully this is what the user will expect
353 // (pressing Ctrl+P would otherwise result in moveUp: instead of previous
356 // We usually end up here if the user pressed Ctrl+key (but not
359 NSEvent *event = [NSApp currentEvent];
361 if (selector == @selector(cancelOperation:)
362 || selector == @selector(insertNewline:)) {
363 // HACK! If there was marked text which got abandoned as a result of
364 // hitting escape or enter, then 'insertText:' is called with the
365 // abandoned text but '[event characters]' includes the abandoned text
366 // as well. Since 'dispatchKeyEvent:' looks at '[event characters]' we
367 // must intercept these keys here or the abandonded text gets inserted
369 NSString *key = [event charactersIgnoringModifiers];
370 const char *chars = [key UTF8String];
371 int len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
373 if (0x3 == chars[0]) {
374 // HACK! AppKit turns enter (not return) into Ctrl-C, so we need to
375 // handle it separately (else Ctrl-C doesn't work).
376 len = sizeof(MMKeypadEnter)/sizeof(MMKeypadEnter[0]);
377 chars = MMKeypadEnter;
380 [self sendKeyDown:chars length:len modifiers:[event modifierFlags]];
382 [self dispatchKeyEvent:event];
386 - (BOOL)performKeyEquivalent:(NSEvent *)event
388 //NSLog(@"%s %@", _cmd, event);
389 // Called for Cmd+key keystrokes, function keys, arrow keys, page
390 // up/down, home, end.
392 // NOTE: This message cannot be ignored since Cmd+letter keys never are
393 // passed to keyDown:. It seems as if the main menu consumes Cmd-key
394 // strokes, unless the key is a function key.
396 // NOTE: If the event that triggered this method represents a function key
397 // down then we do nothing, otherwise the input method never gets the key
398 // stroke (some input methods use e.g. arrow keys). The function key down
399 // event will still reach Vim though (via keyDown:). The exceptions to
400 // this rule are: PageUp/PageDown (keycode 116/121).
401 int flags = [event modifierFlags];
402 if ([event type] != NSKeyDown || flags & NSFunctionKeyMask
403 && !(116 == [event keyCode] || 121 == [event keyCode]))
406 // HACK! Let the main menu try to handle any key down event, before
407 // passing it on to vim, otherwise key equivalents for menus will
408 // effectively be disabled.
409 if ([[NSApp mainMenu] performKeyEquivalent:event])
412 // HACK! KeyCode 50 represent the key which switches between windows
413 // within an application (like Cmd+Tab is used to switch between
414 // applications). Return NO here, else the window switching does not work.
416 // Will this hack work for all languages / keyboard layouts?
417 if ([event keyCode] == 50)
420 // HACK! On Leopard Ctrl-key events end up here instead of keyDown:.
421 if (flags & NSControlKeyMask) {
422 [self keyDown:event];
426 //NSLog(@"%s%@", _cmd, event);
428 NSString *chars = [event characters];
429 NSString *unmodchars = [event charactersIgnoringModifiers];
430 int len = [unmodchars lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
431 NSMutableData *data = [NSMutableData data];
436 // If 'chars' and 'unmodchars' differs when shift flag is present, then we
437 // can clear the shift flag as it is already included in 'unmodchars'.
438 // Failing to clear the shift flag means <D-Bar> turns into <S-D-Bar> (on
439 // an English keyboard).
440 if (flags & NSShiftKeyMask && ![chars isEqual:unmodchars])
441 flags &= ~NSShiftKeyMask;
443 if (0x3 == [unmodchars characterAtIndex:0]) {
444 // HACK! AppKit turns enter (not return) into Ctrl-C, so we need to
445 // handle it separately (else Cmd-enter turns into Ctrl-C).
446 unmodchars = MMKeypadEnterString;
447 len = [unmodchars lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
450 [data appendBytes:&flags length:sizeof(int)];
451 [data appendBytes:&len length:sizeof(int)];
452 [data appendBytes:[unmodchars UTF8String] length:len];
454 [[self vimController] sendMessage:CmdKeyMsgID data:data];
459 - (NSPoint)textContainerOrigin
464 - (void)setTextContainerInset:(NSSize)inset
468 - (void)setBackgroundColor:(NSColor *)color
475 - (BOOL)acceptsFirstResponder
485 - (void)drawRect:(NSRect)rect
487 [contentImage drawInRect: rect
489 operation: NSCompositeCopy
493 - (BOOL) wantsDefaultClipping
499 #define MM_DEBUG_DRAWING 0
501 - (void)performBatchDrawWithData:(NSData *)data
503 const void *bytes = [data bytes];
504 const void *end = bytes + [data length];
506 if (! NSEqualSizes(imageSize, [self size]))
507 [self fitImageToSize];
510 NSLog(@"====> BEGIN %s", _cmd);
514 // TODO: Sanity check input
516 while (bytes < end) {
517 int type = *((int*)bytes); bytes += sizeof(int);
519 if (ClearAllDrawType == type) {
521 NSLog(@" Clear all");
524 } else if (ClearBlockDrawType == type) {
525 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
526 int row1 = *((int*)bytes); bytes += sizeof(int);
527 int col1 = *((int*)bytes); bytes += sizeof(int);
528 int row2 = *((int*)bytes); bytes += sizeof(int);
529 int col2 = *((int*)bytes); bytes += sizeof(int);
532 NSLog(@" Clear block (%d,%d) -> (%d,%d)", row1, col1,
535 [self clearBlockFromRow:row1 column:col1
536 toRow:row2 column:col2
537 color:[NSColor colorWithArgbInt:color]];
538 } else if (DeleteLinesDrawType == type) {
539 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
540 int row = *((int*)bytes); bytes += sizeof(int);
541 int count = *((int*)bytes); bytes += sizeof(int);
542 int bot = *((int*)bytes); bytes += sizeof(int);
543 int left = *((int*)bytes); bytes += sizeof(int);
544 int right = *((int*)bytes); bytes += sizeof(int);
547 NSLog(@" Delete %d line(s) from %d", count, row);
549 [self deleteLinesFromRow:row lineCount:count
550 scrollBottom:bot left:left right:right
551 color:[NSColor colorWithArgbInt:color]];
552 } else if (DrawStringDrawType == type) {
553 int bg = *((int*)bytes); bytes += sizeof(int);
554 int fg = *((int*)bytes); bytes += sizeof(int);
555 int sp = *((int*)bytes); bytes += sizeof(int);
556 int row = *((int*)bytes); bytes += sizeof(int);
557 int col = *((int*)bytes); bytes += sizeof(int);
558 int cells = *((int*)bytes); bytes += sizeof(int);
559 int flags = *((int*)bytes); bytes += sizeof(int);
560 int len = *((int*)bytes); bytes += sizeof(int);
561 // UniChar *string = (UniChar*)bytes; bytes += len;
562 NSString *string = [[NSString alloc] initWithBytesNoCopy:(void*)bytes
564 encoding:NSUTF8StringEncoding
568 NSLog(@" Draw string at (%d,%d) length=%d flags=%d fg=0x%x "
569 "bg=0x%x sp=0x%x", row, col, len, flags, fg, bg, sp);
571 unichar *characters = malloc(sizeof(unichar) * [string length]);
572 [string getCharacters:characters];
574 [self drawString:characters length:[string length] atRow:row column:col
575 cells:cells withFlags:flags
576 foregroundColor:[NSColor colorWithRgbInt:fg]
577 backgroundColor:[NSColor colorWithArgbInt:bg]
578 specialColor:[NSColor colorWithRgbInt:sp]];
581 } else if (InsertLinesDrawType == type) {
582 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
583 int row = *((int*)bytes); bytes += sizeof(int);
584 int count = *((int*)bytes); bytes += sizeof(int);
585 int bot = *((int*)bytes); bytes += sizeof(int);
586 int left = *((int*)bytes); bytes += sizeof(int);
587 int right = *((int*)bytes); bytes += sizeof(int);
590 NSLog(@" Insert %d line(s) at row %d", count, row);
592 [self insertLinesAtRow:row lineCount:count
593 scrollBottom:bot left:left right:right
594 color:[NSColor colorWithArgbInt:color]];
595 } else if (DrawCursorDrawType == type) {
596 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
597 int row = *((int*)bytes); bytes += sizeof(int);
598 int col = *((int*)bytes); bytes += sizeof(int);
599 int shape = *((int*)bytes); bytes += sizeof(int);
600 int percent = *((int*)bytes); bytes += sizeof(int);
603 NSLog(@" Draw cursor at (%d,%d)", row, col);
605 [self drawInsertionPointAtRow:row column:col shape:shape
607 color:[NSColor colorWithRgbInt:color]];
609 NSLog(@"WARNING: Unknown draw type (type=%d)", type);
615 // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
616 // and columns are changed (due to ipc delays). Force a redraw here.
617 [self setNeedsDisplay:YES];
618 // [self displayIfNeeded];
621 NSLog(@"<==== END %s", _cmd);
625 @end // MMAtsuiTextView
630 @implementation MMAtsuiTextView (Private)
632 - (void)initAtsuStyles
635 for (i = 0; i < MMMaxCellsPerChar; i++)
636 ATSUCreateStyle(&atsuStyles[i]);
639 - (void)disposeAtsuStyles
643 for (i = 0; i < MMMaxCellsPerChar; i++)
644 if (atsuStyles[i] != NULL)
646 if (ATSUDisposeStyle(atsuStyles[i]) != noErr)
647 atsuStyles[i] = NULL;
651 - (void)updateAtsuStyles
657 CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
658 ATSStyleRenderingOptions options;
660 fontID = [font _atsFontID];
661 fontSize = Long2Fix([font pointSize]);
662 options = kATSStyleApplyAntiAliasing;
664 ATSUAttributeTag attribTags[] =
666 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
667 kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag,
668 kATSUMaxATSUITagValue + 1
671 ByteCount attribSizes[] =
673 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
674 sizeof(CGAffineTransform), sizeof(ATSStyleRenderingOptions),
678 ATSUAttributeValuePtr attribValues[] =
680 &fontID, &fontSize, &fontWidth, &transform, &options, &font
683 for (i = 0; i < MMMaxCellsPerChar; i++)
685 fontWidth = Long2Fix(cellSize.width * (i + 1));
687 if (ATSUSetAttributes(atsuStyles[i],
688 (sizeof attribTags) / sizeof(ATSUAttributeTag),
689 attribTags, attribSizes, attribValues) != noErr)
691 ATSUDisposeStyle(atsuStyles[i]);
692 atsuStyles[i] = NULL;
697 - (void)dispatchKeyEvent:(NSEvent *)event
699 // Only handle the command if it came from a keyDown event
700 if ([event type] != NSKeyDown)
703 NSString *chars = [event characters];
704 NSString *unmodchars = [event charactersIgnoringModifiers];
705 unichar c = [chars characterAtIndex:0];
706 unichar imc = [unmodchars characterAtIndex:0];
708 const char *bytes = 0;
709 int mods = [event modifierFlags];
711 //NSLog(@"%s chars[0]=0x%x unmodchars[0]=0x%x (chars=%@ unmodchars=%@)",
712 // _cmd, c, imc, chars, unmodchars);
714 if (' ' == imc && 0xa0 != c) {
715 // HACK! The AppKit turns <C-Space> into <C-@> which is not standard
716 // Vim behaviour, so bypass this problem. (0xa0 is <M-Space>, which
717 // should be passed on as is.)
718 len = [unmodchars lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
719 bytes = [unmodchars UTF8String];
720 } else if (imc == c && '2' == c) {
721 // HACK! Translate Ctrl+2 to <C-@>.
722 static char ctrl_at = 0;
723 len = 1; bytes = &ctrl_at;
724 } else if (imc == c && '6' == c) {
725 // HACK! Translate Ctrl+6 to <C-^>.
726 static char ctrl_hat = 0x1e;
727 len = 1; bytes = &ctrl_hat;
728 } else if (c == 0x19 && imc == 0x19) {
729 // HACK! AppKit turns back tab into Ctrl-Y, so we need to handle it
730 // separately (else Ctrl-Y doesn't work).
731 static char tab = 0x9;
732 len = 1; bytes = &tab; mods |= NSShiftKeyMask;
734 len = [chars lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
735 bytes = [chars UTF8String];
738 [self sendKeyDown:bytes length:len modifiers:mods];
741 - (void)sendKeyDown:(const char *)chars length:(int)len modifiers:(int)flags
743 if (chars && len > 0) {
744 NSMutableData *data = [NSMutableData data];
746 [data appendBytes:&flags length:sizeof(int)];
747 [data appendBytes:&len length:sizeof(int)];
748 [data appendBytes:chars length:len];
750 // TODO: Support 'mousehide' (check p_mh)
751 [NSCursor setHiddenUntilMouseMoves:YES];
753 //NSLog(@"%s len=%d chars=0x%x", _cmd, len, chars[0]);
754 [[self vimController] sendMessage:KeyDownMsgID data:data];
758 - (MMVimController *)vimController
760 id windowController = [[self window] windowController];
762 // TODO: Make sure 'windowController' is a MMWindowController before type
764 return [(MMWindowController*)windowController vimController];
767 @end // MMAtsuiTextView (Private)
772 @implementation MMAtsuiTextView (Drawing)
774 - (NSRect)rectFromRow:(int)row1 column:(int)col1
775 toRow:(int)row2 column:(int)col2
777 return NSMakeRect(col1 * cellSize.width, row1 * cellSize.height,
778 (col2 + 1 - col1) * cellSize.width,
779 (row2 + 1 - row1) * cellSize.height);
784 [contentImage lockFocus];
789 [contentImage unlockFocus];
792 - (void)fitImageToSize
794 NSLog(@"fitImageToSize");
795 [contentImage release];
796 contentImage = [[NSImage alloc] initWithSize:[self size]];
797 [contentImage setFlipped: YES];
798 imageSize = [self size];
801 - (void)drawString:(UniChar *)string length:(UniCharCount)length
802 atRow:(int)row column:(int)col cells:(int)cells
803 withFlags:(int)flags foregroundColor:(NSColor *)fg
804 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp
806 // 'string' consists of 'length' utf-16 code pairs and should cover 'cells'
807 // display cells (a normal character takes up one display cell, a wide
808 // character takes up two)
809 ATSUStyle style = atsuStyles[0];
810 ATSUTextLayout layout;
812 // NSLog(@"drawString: %d", length);
814 ATSUCreateTextLayout(&layout);
815 ATSUSetTextPointerLocation(layout, string,
816 kATSUFromTextBeginning, kATSUToTextEnd,
818 ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd);
820 NSRect rect = NSMakeRect(col * cellSize.width, row * cellSize.height,
821 length * cellSize.width, cellSize.height);
822 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
824 ATSUAttributeTag tags[] = { kATSUCGContextTag };
825 ByteCount sizes[] = { sizeof(CGContextRef) };
826 ATSUAttributeValuePtr values[] = { &context };
827 ATSUSetLayoutControls(layout, 1, tags, sizes, values);
829 if (! (flags & DRAW_TRANSP))
837 ATSUSetTransientFontMatching(layout, TRUE);
839 kATSUFromTextBeginning,
841 X2Fix(rect.origin.x),
842 X2Fix(rect.origin.y + [font ascender]));
843 ATSUDisposeTextLayout(layout);
846 - (void)scrollRect:(NSRect)rect lineCount:(int)count
848 NSPoint destPoint = rect.origin;
849 destPoint.y += count * cellSize.height;
851 NSCopyBits(0, rect, destPoint);
854 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
855 scrollBottom:(int)bottom left:(int)left right:(int)right
856 color:(NSColor *)color
858 NSRect rect = [self rectFromRow:row + count
863 // move rect up for count lines
864 [self scrollRect:rect lineCount:-count];
865 [self clearBlockFromRow:bottom - count + 1
872 - (void)insertLinesAtRow:(int)row lineCount:(int)count
873 scrollBottom:(int)bottom left:(int)left right:(int)right
874 color:(NSColor *)color
876 NSRect rect = [self rectFromRow:row
881 // move rect down for count lines
882 [self scrollRect:rect lineCount:count];
883 [self clearBlockFromRow:row
885 toRow:row + count - 1
890 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
891 column:(int)col2 color:(NSColor *)color
894 NSRectFill([self rectFromRow:row1 column:col1 toRow:row2 column:col2]);
899 [defaultBackgroundColor set];
900 NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height));
903 @end // MMAtsuiTextView (Drawing)