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 "MMAtsuiTextView.h"
29 #import "MMVimController.h"
33 // TODO: What does DRAW_TRANSP flag do? If the background isn't drawn when
34 // this flag is set, then sometimes the character after the cursor becomes
35 // blank. Everything seems to work fine by just ignoring this flag.
36 #define DRAW_TRANSP 0x01 /* draw with transparant bg */
37 #define DRAW_BOLD 0x02 /* draw bold text */
38 #define DRAW_UNDERL 0x04 /* draw underline text */
39 #define DRAW_UNDERC 0x08 /* draw undercurl text */
40 #define DRAW_ITALIC 0x10 /* draw italic text */
41 #define DRAW_CURSOR 0x20
44 static char MMKeypadEnter[2] = { 'K', 'A' };
45 static NSString *MMKeypadEnterString = @"KA";
48 // These values are chosen so that the min size is not too small with the
49 // default font (they only affect resizing with the mouse, you can still
50 // use e.g. ":set lines=2" to go below these values).
56 @interface NSFont (AppKitPrivate)
57 - (ATSUFontID) _atsFontID;
61 @interface MMAtsuiTextView (Private)
62 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column;
63 - (void)initAtsuStyles;
64 - (void)disposeAtsuStyles;
65 - (void)updateAtsuStyles;
66 - (void)dispatchKeyEvent:(NSEvent *)event;
67 - (void)sendKeyDown:(const char *)chars length:(int)len modifiers:(int)flags;
68 - (MMVimController *)vimController;
72 @interface MMAtsuiTextView (Drawing)
73 - (NSRect)rectFromRow:(int)row1 column:(int)col1
74 toRow:(int)row2 column:(int)col2;
75 - (NSSize)textAreaSize;
76 - (void)resizeContentImage;
79 - (void)drawString:(UniChar *)string length:(UniCharCount)length
80 atRow:(int)row column:(int)col cells:(int)cells
81 withFlags:(int)flags foregroundColor:(NSColor *)fg
82 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp;
83 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
84 scrollBottom:(int)bottom left:(int)left right:(int)right
85 color:(NSColor *)color;
86 - (void)insertLinesAtRow:(int)row lineCount:(int)count
87 scrollBottom:(int)bottom left:(int)left right:(int)right
88 color:(NSColor *)color;
89 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
90 column:(int)col2 color:(NSColor *)color;
92 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
93 fraction:(int)percent color:(NSColor *)color;
97 @implementation MMAtsuiTextView
99 - (id)initWithFrame:(NSRect)frame
101 if ((self = [super initWithFrame:frame])) {
102 // NOTE! It does not matter which font is set here, Vim will set its
103 // own font on startup anyway. Just set some bogus values.
104 font = [[NSFont userFixedPitchFontOfSize:0] retain];
105 cellSize.width = cellSize.height = 1;
107 imageSize = NSZeroSize;
109 [self initAtsuStyles];
117 [self disposeAtsuStyles];
118 [font release]; font = nil;
119 [defaultBackgroundColor release]; defaultBackgroundColor = nil;
120 [defaultForegroundColor release]; defaultForegroundColor = nil;
125 - (void)getMaxRows:(int*)rows columns:(int*)cols
127 if (rows) *rows = maxRows;
128 if (cols) *cols = maxColumns;
131 - (void)setMaxRows:(int)rows columns:(int)cols
133 // NOTE: Just remember the new values, the actual resizing is done lazily.
138 - (void)setDefaultColorsBackground:(NSColor *)bgColor
139 foreground:(NSColor *)fgColor
141 if (defaultBackgroundColor != bgColor) {
142 [defaultBackgroundColor release];
143 defaultBackgroundColor = bgColor ? [bgColor retain] : nil;
146 // NOTE: The default foreground color isn't actually used for anything, but
147 // other class instances might want to be able to access it so it is stored
149 if (defaultForegroundColor != fgColor) {
150 [defaultForegroundColor release];
151 defaultForegroundColor = fgColor ? [fgColor retain] : nil;
155 - (NSRect)rectForRowsInRange:(NSRange)range
157 // TODO: Add text inset to origin
158 NSRect rect = { 0, 0, 0, 0 };
159 unsigned start = range.location > maxRows ? maxRows : range.location;
160 unsigned length = range.length;
162 if (start+length > maxRows)
163 length = maxRows - start;
165 rect.origin.y = cellSize.height * start;
166 rect.size.height = cellSize.height * length;
171 - (NSRect)rectForColumnsInRange:(NSRange)range
173 // TODO: Add text inset to origin
174 NSRect rect = { 0, 0, 0, 0 };
175 unsigned start = range.location > maxColumns ? maxColumns : range.location;
176 unsigned length = range.length;
178 if (start+length > maxColumns)
179 length = maxColumns - start;
181 rect.origin.x = cellSize.width * start;
182 rect.size.width = cellSize.width * length;
188 - (void)setFont:(NSFont *)newFont
190 if (newFont && font != newFont) {
192 font = [newFont retain];
194 float em = [newFont widthOfString:@"m"];
195 float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
196 floatForKey:MMCellWidthMultiplierKey];
198 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
199 // only render at integer sizes. Hence, we restrict the cell width to
200 // an integer here, otherwise the window width and the actual text
201 // width will not match.
202 cellSize.width = ceilf(em * cellWidthMultiplier);
203 cellSize.height = linespace + [newFont defaultLineHeightForFont];
205 [self updateAtsuStyles];
209 - (void)setWideFont:(NSFont *)newFont
223 - (void)setLinespace:(float)newLinespace
225 linespace = newLinespace;
227 // NOTE: The linespace is added to the cell height in order for a multiline
228 // selection not to have white (background color) gaps between lines. Also
229 // this simplifies the code a lot because there is no need to check the
230 // linespace when calculating the size of the text view etc. When the
231 // linespace is non-zero the baseline will be adjusted as well; check
233 cellSize.height = linespace + [font defaultLineHeightForFont];
239 - (NSEvent *)lastMouseDownEvent
244 - (void)setShouldDrawInsertionPoint:(BOOL)on
248 - (void)setPreEditRow:(int)row column:(int)col
252 - (void)hideMarkedTextField
259 - (void)keyDown:(NSEvent *)event
261 //NSLog(@"%s %@", _cmd, event);
262 // HACK! If control modifier is held, don't pass the event along to
263 // interpretKeyEvents: since some keys are bound to multiple commands which
264 // means doCommandBySelector: is called several times.
266 // TODO: Figure out a way to disable Cocoa key bindings entirely, without
267 // affecting input management.
268 if ([event modifierFlags] & NSControlKeyMask) {
269 NSString *unmod = [event charactersIgnoringModifiers];
270 if ([unmod length] == 1 && [unmod characterAtIndex:0] <= 0x7f
271 && [unmod characterAtIndex:0] >= 0x60) {
272 // HACK! Send Ctrl-letter keys (and C-@, C-[, C-\, C-], C-^, C-_)
273 // as normal text to be added to the Vim input buffer. This must
274 // be done in order for the backend to be able to separate e.g.
275 // Ctrl-i and Ctrl-tab.
276 [self insertText:[event characters]];
278 [self dispatchKeyEvent:event];
281 [self interpretKeyEvents:[NSArray arrayWithObject:event]];
285 - (void)insertText:(id)string
287 //NSLog(@"%s %@", _cmd, string);
288 // NOTE! This method is called for normal key presses but also for
289 // Option-key presses --- even when Ctrl is held as well as Option. When
290 // Ctrl is held, the AppKit translates the character to a Ctrl+key stroke,
291 // so 'string' need not be a printable character! In this case it still
292 // works to pass 'string' on to Vim as a printable character (since
293 // modifiers are already included and should not be added to the input
294 // buffer using CSI, K_MODIFIER).
296 [self hideMarkedTextField];
298 NSEvent *event = [NSApp currentEvent];
300 // HACK! In order to be able to bind to <S-Space>, <S-M-Tab>, etc. we have
301 // to watch for them here.
302 if ([event type] == NSKeyDown
303 && [[event charactersIgnoringModifiers] length] > 0
304 && [event modifierFlags]
305 & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask)) {
306 unichar c = [[event charactersIgnoringModifiers] characterAtIndex:0];
308 // <S-M-Tab> translates to 0x19
309 if (' ' == c || 0x19 == c) {
310 [self dispatchKeyEvent:event];
315 // TODO: Support 'mousehide' (check p_mh)
316 [NSCursor setHiddenUntilMouseMoves:YES];
318 // NOTE: 'string' is either an NSString or an NSAttributedString. Since we
319 // do not support attributes, simply pass the corresponding NSString in the
321 if ([string isKindOfClass:[NSAttributedString class]])
322 string = [string string];
324 //NSLog(@"send InsertTextMsgID: %@", string);
326 [[self vimController] sendMessage:InsertTextMsgID
327 data:[string dataUsingEncoding:NSUTF8StringEncoding]];
330 - (void)doCommandBySelector:(SEL)selector
332 //NSLog(@"%s %@", _cmd, NSStringFromSelector(selector));
333 // By ignoring the selector we effectively disable the key binding
334 // mechanism of Cocoa. Hopefully this is what the user will expect
335 // (pressing Ctrl+P would otherwise result in moveUp: instead of previous
338 // We usually end up here if the user pressed Ctrl+key (but not
341 NSEvent *event = [NSApp currentEvent];
343 if (selector == @selector(cancelOperation:)
344 || selector == @selector(insertNewline:)) {
345 // HACK! If there was marked text which got abandoned as a result of
346 // hitting escape or enter, then 'insertText:' is called with the
347 // abandoned text but '[event characters]' includes the abandoned text
348 // as well. Since 'dispatchKeyEvent:' looks at '[event characters]' we
349 // must intercept these keys here or the abandonded text gets inserted
351 NSString *key = [event charactersIgnoringModifiers];
352 const char *chars = [key UTF8String];
353 int len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
355 if (0x3 == chars[0]) {
356 // HACK! AppKit turns enter (not return) into Ctrl-C, so we need to
357 // handle it separately (else Ctrl-C doesn't work).
358 len = sizeof(MMKeypadEnter)/sizeof(MMKeypadEnter[0]);
359 chars = MMKeypadEnter;
362 [self sendKeyDown:chars length:len modifiers:[event modifierFlags]];
364 [self dispatchKeyEvent:event];
368 - (BOOL)performKeyEquivalent:(NSEvent *)event
370 //NSLog(@"%s %@", _cmd, event);
371 // Called for Cmd+key keystrokes, function keys, arrow keys, page
372 // up/down, home, end.
374 // NOTE: This message cannot be ignored since Cmd+letter keys never are
375 // passed to keyDown:. It seems as if the main menu consumes Cmd-key
376 // strokes, unless the key is a function key.
378 // NOTE: If the event that triggered this method represents a function key
379 // down then we do nothing, otherwise the input method never gets the key
380 // stroke (some input methods use e.g. arrow keys). The function key down
381 // event will still reach Vim though (via keyDown:). The exceptions to
382 // this rule are: PageUp/PageDown (keycode 116/121).
383 int flags = [event modifierFlags];
384 if ([event type] != NSKeyDown || flags & NSFunctionKeyMask
385 && !(116 == [event keyCode] || 121 == [event keyCode]))
388 // HACK! Let the main menu try to handle any key down event, before
389 // passing it on to vim, otherwise key equivalents for menus will
390 // effectively be disabled.
391 if ([[NSApp mainMenu] performKeyEquivalent:event])
394 // HACK! KeyCode 50 represent the key which switches between windows
395 // within an application (like Cmd+Tab is used to switch between
396 // applications). Return NO here, else the window switching does not work.
398 // Will this hack work for all languages / keyboard layouts?
399 if ([event keyCode] == 50)
402 // HACK! On Leopard Ctrl-key events end up here instead of keyDown:.
403 if (flags & NSControlKeyMask) {
404 [self keyDown:event];
408 //NSLog(@"%s%@", _cmd, event);
410 NSString *chars = [event characters];
411 NSString *unmodchars = [event charactersIgnoringModifiers];
412 int len = [unmodchars lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
413 NSMutableData *data = [NSMutableData data];
418 // If 'chars' and 'unmodchars' differs when shift flag is present, then we
419 // can clear the shift flag as it is already included in 'unmodchars'.
420 // Failing to clear the shift flag means <D-Bar> turns into <S-D-Bar> (on
421 // an English keyboard).
422 if (flags & NSShiftKeyMask && ![chars isEqual:unmodchars])
423 flags &= ~NSShiftKeyMask;
425 if (0x3 == [unmodchars characterAtIndex:0]) {
426 // HACK! AppKit turns enter (not return) into Ctrl-C, so we need to
427 // handle it separately (else Cmd-enter turns into Ctrl-C).
428 unmodchars = MMKeypadEnterString;
429 len = [unmodchars lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
432 [data appendBytes:&flags length:sizeof(int)];
433 [data appendBytes:&len length:sizeof(int)];
434 [data appendBytes:[unmodchars UTF8String] length:len];
436 [[self vimController] sendMessage:CmdKeyMsgID data:data];
444 - (BOOL)acceptsFirstResponder
454 - (void)drawRect:(NSRect)rect
456 [contentImage drawInRect: rect
458 operation: NSCompositeCopy
462 - (BOOL) wantsDefaultClipping
468 #define MM_DEBUG_DRAWING 0
470 - (void)performBatchDrawWithData:(NSData *)data
472 const void *bytes = [data bytes];
473 const void *end = bytes + [data length];
475 if (! NSEqualSizes(imageSize, [self textAreaSize]))
476 [self resizeContentImage];
479 NSLog(@"====> BEGIN %s", _cmd);
483 // TODO: Sanity check input
485 while (bytes < end) {
486 int type = *((int*)bytes); bytes += sizeof(int);
488 if (ClearAllDrawType == type) {
490 NSLog(@" Clear all");
493 } else if (ClearBlockDrawType == type) {
494 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
495 int row1 = *((int*)bytes); bytes += sizeof(int);
496 int col1 = *((int*)bytes); bytes += sizeof(int);
497 int row2 = *((int*)bytes); bytes += sizeof(int);
498 int col2 = *((int*)bytes); bytes += sizeof(int);
501 NSLog(@" Clear block (%d,%d) -> (%d,%d)", row1, col1,
504 [self clearBlockFromRow:row1 column:col1
505 toRow:row2 column:col2
506 color:[NSColor colorWithArgbInt:color]];
507 } else if (DeleteLinesDrawType == type) {
508 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
509 int row = *((int*)bytes); bytes += sizeof(int);
510 int count = *((int*)bytes); bytes += sizeof(int);
511 int bot = *((int*)bytes); bytes += sizeof(int);
512 int left = *((int*)bytes); bytes += sizeof(int);
513 int right = *((int*)bytes); bytes += sizeof(int);
516 NSLog(@" Delete %d line(s) from %d", count, row);
518 [self deleteLinesFromRow:row lineCount:count
519 scrollBottom:bot left:left right:right
520 color:[NSColor colorWithArgbInt:color]];
521 } else if (DrawStringDrawType == type) {
522 int bg = *((int*)bytes); bytes += sizeof(int);
523 int fg = *((int*)bytes); bytes += sizeof(int);
524 int sp = *((int*)bytes); bytes += sizeof(int);
525 int row = *((int*)bytes); bytes += sizeof(int);
526 int col = *((int*)bytes); bytes += sizeof(int);
527 int cells = *((int*)bytes); bytes += sizeof(int);
528 int flags = *((int*)bytes); bytes += sizeof(int);
529 int len = *((int*)bytes); bytes += sizeof(int);
530 // UniChar *string = (UniChar*)bytes; bytes += len;
531 NSString *string = [[NSString alloc] initWithBytesNoCopy:(void*)bytes
533 encoding:NSUTF8StringEncoding
537 NSLog(@" Draw string at (%d,%d) length=%d flags=%d fg=0x%x "
538 "bg=0x%x sp=0x%x", row, col, len, flags, fg, bg, sp);
540 unichar *characters = malloc(sizeof(unichar) * [string length]);
541 [string getCharacters:characters];
543 [self drawString:characters length:[string length] atRow:row column:col
544 cells:cells withFlags:flags
545 foregroundColor:[NSColor colorWithRgbInt:fg]
546 backgroundColor:[NSColor colorWithArgbInt:bg]
547 specialColor:[NSColor colorWithRgbInt:sp]];
550 } else if (InsertLinesDrawType == type) {
551 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
552 int row = *((int*)bytes); bytes += sizeof(int);
553 int count = *((int*)bytes); bytes += sizeof(int);
554 int bot = *((int*)bytes); bytes += sizeof(int);
555 int left = *((int*)bytes); bytes += sizeof(int);
556 int right = *((int*)bytes); bytes += sizeof(int);
559 NSLog(@" Insert %d line(s) at row %d", count, row);
561 [self insertLinesAtRow:row lineCount:count
562 scrollBottom:bot left:left right:right
563 color:[NSColor colorWithArgbInt:color]];
564 } else if (DrawCursorDrawType == type) {
565 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
566 int row = *((int*)bytes); bytes += sizeof(int);
567 int col = *((int*)bytes); bytes += sizeof(int);
568 int shape = *((int*)bytes); bytes += sizeof(int);
569 int percent = *((int*)bytes); bytes += sizeof(int);
572 NSLog(@" Draw cursor at (%d,%d)", row, col);
574 [self drawInsertionPointAtRow:row column:col shape:shape
576 color:[NSColor colorWithRgbInt:color]];
578 NSLog(@"WARNING: Unknown draw type (type=%d)", type);
584 // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
585 // and columns are changed (due to ipc delays). Force a redraw here.
586 [self setNeedsDisplay:YES];
587 // [self displayIfNeeded];
590 NSLog(@"<==== END %s", _cmd);
594 - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size
597 // - Take text area inset into consideration
598 // - Rounding errors may cause size change when there should be none
599 // - Desired rows/columns shold not be 'too small'
601 // Constrain the desired size to the given size. Values for the minimum
602 // rows and columns is taken from Vim.
603 NSSize desiredSize = [self desiredSize];
604 int desiredRows = maxRows;
605 int desiredCols = maxColumns;
607 if (size.height != desiredSize.height) {
608 float fh = cellSize.height;
609 if (fh < 1.0f) fh = 1.0f;
611 desiredRows = floor(size.height/fh);
612 desiredSize.height = fh*desiredRows;
615 if (size.width != desiredSize.width) {
616 float fw = cellSize.width;
617 if (fw < 1.0f) fw = 1.0f;
619 desiredCols = floor(size.width/fw);
620 desiredSize.width = fw*desiredCols;
623 if (rows) *rows = desiredRows;
624 if (cols) *cols = desiredCols;
629 - (NSSize)desiredSize
631 // Compute the size the text view should be for the entire text area and
632 // inset area to be visible with the present number of rows and columns.
634 // TODO: Add inset area to size.
635 return NSMakeSize(maxColumns*cellSize.width, maxRows*cellSize.height);
640 // Compute the smallest size the text view is allowed to be.
642 // TODO: Add inset area to size.
643 return NSMakeSize(MMMinColumns*cellSize.width, MMMinRows*cellSize.height);
646 - (void)changeFont:(id)sender
648 NSFont *newFont = [sender convertFont:font];
651 NSString *name = [newFont displayName];
652 unsigned len = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
654 NSMutableData *data = [NSMutableData data];
655 float pointSize = [newFont pointSize];
657 [data appendBytes:&pointSize length:sizeof(float)];
659 ++len; // include NUL byte
660 [data appendBytes:&len length:sizeof(unsigned)];
661 [data appendBytes:[name UTF8String] length:len];
663 [[self vimController] sendMessage:SetFontMsgID data:data];
668 - (void)scrollWheel:(NSEvent *)event
670 if ([event deltaY] == 0)
674 NSPoint pt = [self convertPoint:[event locationInWindow] fromView:nil];
676 // View is not flipped, instead the atsui code draws to a flipped image;
677 // thus we need to 'flip' the coordinate here since the column number
678 // increases in an up-to-down order.
679 pt.y = [self frame].size.height - pt.y;
681 if (![self convertPoint:pt toRow:&row column:&col])
684 int flags = [event modifierFlags];
685 float dy = [event deltaY];
686 NSMutableData *data = [NSMutableData data];
688 [data appendBytes:&row length:sizeof(int)];
689 [data appendBytes:&col length:sizeof(int)];
690 [data appendBytes:&flags length:sizeof(int)];
691 [data appendBytes:&dy length:sizeof(float)];
693 [[self vimController] sendMessage:ScrollWheelMsgID data:data];
696 @end // MMAtsuiTextView
701 @implementation MMAtsuiTextView (Private)
703 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
706 NSPoint origin = { 0,0 };
708 if (!(cellSize.width > 0 && cellSize.height > 0))
711 if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
712 if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
714 //NSLog(@"convertPoint:%@ toRow:%d column:%d", NSStringFromPoint(point),
720 - (void)initAtsuStyles
723 for (i = 0; i < MMMaxCellsPerChar; i++)
724 ATSUCreateStyle(&atsuStyles[i]);
727 - (void)disposeAtsuStyles
731 for (i = 0; i < MMMaxCellsPerChar; i++)
732 if (atsuStyles[i] != NULL)
734 if (ATSUDisposeStyle(atsuStyles[i]) != noErr)
735 atsuStyles[i] = NULL;
739 - (void)updateAtsuStyles
745 CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
746 ATSStyleRenderingOptions options;
748 fontID = [font _atsFontID];
749 fontSize = Long2Fix([font pointSize]);
750 options = kATSStyleApplyAntiAliasing;
752 ATSUAttributeTag attribTags[] =
754 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
755 kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag,
756 kATSUMaxATSUITagValue + 1
759 ByteCount attribSizes[] =
761 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
762 sizeof(CGAffineTransform), sizeof(ATSStyleRenderingOptions),
766 ATSUAttributeValuePtr attribValues[] =
768 &fontID, &fontSize, &fontWidth, &transform, &options, &font
771 for (i = 0; i < MMMaxCellsPerChar; i++)
773 fontWidth = Long2Fix(cellSize.width * (i + 1));
775 if (ATSUSetAttributes(atsuStyles[i],
776 (sizeof attribTags) / sizeof(ATSUAttributeTag),
777 attribTags, attribSizes, attribValues) != noErr)
779 ATSUDisposeStyle(atsuStyles[i]);
780 atsuStyles[i] = NULL;
785 - (void)dispatchKeyEvent:(NSEvent *)event
787 // Only handle the command if it came from a keyDown event
788 if ([event type] != NSKeyDown)
791 NSString *chars = [event characters];
792 NSString *unmodchars = [event charactersIgnoringModifiers];
793 unichar c = [chars characterAtIndex:0];
794 unichar imc = [unmodchars characterAtIndex:0];
796 const char *bytes = 0;
797 int mods = [event modifierFlags];
799 //NSLog(@"%s chars[0]=0x%x unmodchars[0]=0x%x (chars=%@ unmodchars=%@)",
800 // _cmd, c, imc, chars, unmodchars);
802 if (' ' == imc && 0xa0 != c) {
803 // HACK! The AppKit turns <C-Space> into <C-@> which is not standard
804 // Vim behaviour, so bypass this problem. (0xa0 is <M-Space>, which
805 // should be passed on as is.)
806 len = [unmodchars lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
807 bytes = [unmodchars UTF8String];
808 } else if (imc == c && '2' == c) {
809 // HACK! Translate Ctrl+2 to <C-@>.
810 static char ctrl_at = 0;
811 len = 1; bytes = &ctrl_at;
812 } else if (imc == c && '6' == c) {
813 // HACK! Translate Ctrl+6 to <C-^>.
814 static char ctrl_hat = 0x1e;
815 len = 1; bytes = &ctrl_hat;
816 } else if (c == 0x19 && imc == 0x19) {
817 // HACK! AppKit turns back tab into Ctrl-Y, so we need to handle it
818 // separately (else Ctrl-Y doesn't work).
819 static char tab = 0x9;
820 len = 1; bytes = &tab; mods |= NSShiftKeyMask;
822 len = [chars lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
823 bytes = [chars UTF8String];
826 [self sendKeyDown:bytes length:len modifiers:mods];
829 - (void)sendKeyDown:(const char *)chars length:(int)len modifiers:(int)flags
831 if (chars && len > 0) {
832 NSMutableData *data = [NSMutableData data];
834 [data appendBytes:&flags length:sizeof(int)];
835 [data appendBytes:&len length:sizeof(int)];
836 [data appendBytes:chars length:len];
838 // TODO: Support 'mousehide' (check p_mh)
839 [NSCursor setHiddenUntilMouseMoves:YES];
841 //NSLog(@"%s len=%d chars=0x%x", _cmd, len, chars[0]);
842 [[self vimController] sendMessage:KeyDownMsgID data:data];
846 - (MMVimController *)vimController
848 id windowController = [[self window] windowController];
850 // TODO: Make sure 'windowController' is a MMWindowController before type
852 return [(MMWindowController*)windowController vimController];
855 @end // MMAtsuiTextView (Private)
860 @implementation MMAtsuiTextView (Drawing)
862 - (NSRect)rectFromRow:(int)row1 column:(int)col1
863 toRow:(int)row2 column:(int)col2
865 return NSMakeRect(col1 * cellSize.width, row1 * cellSize.height,
866 (col2 + 1 - col1) * cellSize.width,
867 (row2 + 1 - row1) * cellSize.height);
870 - (NSSize)textAreaSize
872 // Calculate the (desired) size of the text area, i.e. the text view area
873 // minus the inset area.
874 return NSMakeSize(maxColumns*cellSize.width, maxRows*cellSize.height);
877 - (void)resizeContentImage
879 //NSLog(@"resizeContentImage");
880 [contentImage release];
881 contentImage = [[NSImage alloc] initWithSize:[self textAreaSize]];
882 [contentImage setFlipped: YES];
883 imageSize = [self textAreaSize];
888 [contentImage lockFocus];
893 [contentImage unlockFocus];
896 - (void)drawString:(UniChar *)string length:(UniCharCount)length
897 atRow:(int)row column:(int)col cells:(int)cells
898 withFlags:(int)flags foregroundColor:(NSColor *)fg
899 backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp
901 // 'string' consists of 'length' utf-16 code pairs and should cover 'cells'
902 // display cells (a normal character takes up one display cell, a wide
903 // character takes up two)
904 ATSUStyle style = atsuStyles[0];
905 ATSUTextLayout layout;
907 // NSLog(@"drawString: %d", length);
909 ATSUCreateTextLayout(&layout);
910 ATSUSetTextPointerLocation(layout, string,
911 kATSUFromTextBeginning, kATSUToTextEnd,
913 ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd);
915 NSRect rect = NSMakeRect(col * cellSize.width, row * cellSize.height,
916 length * cellSize.width, cellSize.height);
917 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
919 ATSUAttributeTag tags[] = { kATSUCGContextTag };
920 ByteCount sizes[] = { sizeof(CGContextRef) };
921 ATSUAttributeValuePtr values[] = { &context };
922 ATSUSetLayoutControls(layout, 1, tags, sizes, values);
924 if (! (flags & DRAW_TRANSP))
932 ATSUSetTransientFontMatching(layout, TRUE);
934 kATSUFromTextBeginning,
936 X2Fix(rect.origin.x),
937 X2Fix(rect.origin.y + [font ascender]));
938 ATSUDisposeTextLayout(layout);
941 - (void)scrollRect:(NSRect)rect lineCount:(int)count
943 NSPoint destPoint = rect.origin;
944 destPoint.y += count * cellSize.height;
946 NSCopyBits(0, rect, destPoint);
949 - (void)deleteLinesFromRow:(int)row lineCount:(int)count
950 scrollBottom:(int)bottom left:(int)left right:(int)right
951 color:(NSColor *)color
953 NSRect rect = [self rectFromRow:row + count
958 // move rect up for count lines
959 [self scrollRect:rect lineCount:-count];
960 [self clearBlockFromRow:bottom - count + 1
967 - (void)insertLinesAtRow:(int)row lineCount:(int)count
968 scrollBottom:(int)bottom left:(int)left right:(int)right
969 color:(NSColor *)color
971 NSRect rect = [self rectFromRow:row
976 // move rect down for count lines
977 [self scrollRect:rect lineCount:count];
978 [self clearBlockFromRow:row
980 toRow:row + count - 1
985 - (void)clearBlockFromRow:(int)row1 column:(int)col1 toRow:(int)row2
986 column:(int)col2 color:(NSColor *)color
989 NSRectFill([self rectFromRow:row1 column:col1 toRow:row2 column:col2]);
994 [defaultBackgroundColor set];
995 NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height));
998 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
999 fraction:(int)percent color:(NSColor *)color
1003 @end // MMAtsuiTextView (Drawing)