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
16 * Support for input managers is somewhat hacked together. Marked text is
17 * drawn "pseudo-inline"; it will simply draw on top of existing text and it
18 * does not respect Vim-window boundaries.
21 #import "MMAppController.h"
22 #import "MMTextStorage.h"
23 #import "MMTextView.h"
24 #import "MMTextViewHelper.h"
25 #import "MMTypesetter.h"
26 #import "MMVimController.h"
27 #import "MMWindowController.h"
28 #import "Miscellaneous.h"
32 // This is taken from gui.h
33 #define DRAW_CURSOR 0x20
37 @interface MMTextView (Private)
38 - (MMWindowController *)windowController;
39 - (MMVimController *)vimController;
40 - (void)setShouldDrawInsertionPoint:(BOOL)on;
41 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
42 fraction:(int)percent;
43 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
44 numColumns:(int)ncols invert:(int)invert;
49 @implementation MMTextView
51 - (id)initWithFrame:(NSRect)frame
53 // Set up a Cocoa text system. Note that the textStorage is released in
54 // -[MMVimView dealloc].
55 MMTextStorage *textStorage = [[MMTextStorage alloc] init];
56 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
57 NSTextContainer *tc = [[NSTextContainer alloc] initWithContainerSize:
58 NSMakeSize(1.0e7,1.0e7)];
60 NSString *typesetterString = [[NSUserDefaults standardUserDefaults]
61 stringForKey:MMTypesetterKey];
62 if ([typesetterString isEqual:@"MMTypesetter"]) {
63 NSTypesetter *typesetter = [[MMTypesetter alloc] init];
64 [lm setTypesetter:typesetter];
67 } else if ([typesetterString isEqual:@"MMTypesetter2"]) {
68 NSTypesetter *typesetter = [[MMTypesetter2 alloc] init];
69 [lm setTypesetter:typesetter];
71 #endif // MM_USE_ROW_CACHE
73 // Only MMTypesetter supports different cell width multipliers.
74 [[NSUserDefaults standardUserDefaults]
75 setFloat:1.0 forKey:MMCellWidthMultiplierKey];
78 // The characters in the text storage are in display order, so disable
79 // bidirectional text processing (this call is 10.4 only).
80 [[lm typesetter] setBidiProcessingEnabled:NO];
82 [tc setWidthTracksTextView:NO];
83 [tc setHeightTracksTextView:NO];
84 [tc setLineFragmentPadding:0];
86 [textStorage addLayoutManager:lm];
87 [lm addTextContainer:tc];
89 // The text storage retains the layout manager which in turn retains
90 // the text container.
94 // NOTE: This will make the text storage the principal owner of the text
95 // system. Releasing the text storage will in turn release the layout
96 // manager, the text container, and finally the text view (self). This
97 // complicates deallocation somewhat, see -[MMVimView dealloc].
98 if (![super initWithFrame:frame textContainer:tc]) {
99 [textStorage release];
103 helper = [[MMTextViewHelper alloc] init];
104 [helper setTextView:self];
106 // NOTE: If the default changes to 'NO' then the intialization of
107 // p_antialias in option.c must change as well.
123 [helper setTextView:nil];
124 [helper release]; helper = nil;
129 - (BOOL)shouldDrawInsertionPoint
131 // NOTE: The insertion point is drawn manually in drawRect:. It would be
132 // nice to be able to use the insertion point related methods of
133 // NSTextView, but it seems impossible to get them to work properly (search
134 // the cocoabuilder archives).
138 - (void)setPreEditRow:(int)row column:(int)col
140 [helper setPreEditRow:row column:col];
143 #define MM_DEBUG_DRAWING 0
145 - (void)performBatchDrawWithData:(NSData *)data
147 MMTextStorage *textStorage = (MMTextStorage *)[self textStorage];
151 const void *bytes = [data bytes];
152 const void *end = bytes + [data length];
153 int cursorRow = -1, cursorCol = 0;
156 ASLogDebug(@"====> BEGIN %s", _cmd);
158 [textStorage beginEditing];
160 // TODO: Sanity check input
162 while (bytes < end) {
163 int type = *((int*)bytes); bytes += sizeof(int);
165 if (ClearAllDrawType == type) {
167 ASLogDebug(@" Clear all");
169 [textStorage clearAll];
170 } else if (ClearBlockDrawType == type) {
171 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
172 int row1 = *((int*)bytes); bytes += sizeof(int);
173 int col1 = *((int*)bytes); bytes += sizeof(int);
174 int row2 = *((int*)bytes); bytes += sizeof(int);
175 int col2 = *((int*)bytes); bytes += sizeof(int);
178 ASLogDebug(@" Clear block (%d,%d) -> (%d,%d)", row1, col1,
181 [textStorage clearBlockFromRow:row1 column:col1
182 toRow:row2 column:col2
183 color:[NSColor colorWithArgbInt:color]];
184 } else if (DeleteLinesDrawType == type) {
185 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
186 int row = *((int*)bytes); bytes += sizeof(int);
187 int count = *((int*)bytes); bytes += sizeof(int);
188 int bot = *((int*)bytes); bytes += sizeof(int);
189 int left = *((int*)bytes); bytes += sizeof(int);
190 int right = *((int*)bytes); bytes += sizeof(int);
193 ASLogDebug(@" Delete %d line(s) from %d", count, row);
195 [textStorage deleteLinesFromRow:row lineCount:count
196 scrollBottom:bot left:left right:right
197 color:[NSColor colorWithArgbInt:color]];
198 } else if (DrawStringDrawType == type) {
199 int bg = *((int*)bytes); bytes += sizeof(int);
200 int fg = *((int*)bytes); bytes += sizeof(int);
201 int sp = *((int*)bytes); bytes += sizeof(int);
202 int row = *((int*)bytes); bytes += sizeof(int);
203 int col = *((int*)bytes); bytes += sizeof(int);
204 int cells = *((int*)bytes); bytes += sizeof(int);
205 int flags = *((int*)bytes); bytes += sizeof(int);
206 int len = *((int*)bytes); bytes += sizeof(int);
207 NSString *string = [[NSString alloc]
208 initWithBytes:(void*)bytes length:len
209 encoding:NSUTF8StringEncoding];
213 ASLogDebug(@" Draw string at (%d,%d) length=%d flags=%d fg=0x%x "
214 "bg=0x%x sp=0x%x (%@)", row, col, len, flags, fg, bg, sp,
215 len > 0 ? [string substringToIndex:1] : @"");
217 // NOTE: If this is a call to draw the (block) cursor, then cancel
218 // any previous request to draw the insertion point, or it might
219 // get drawn as well.
220 if (flags & DRAW_CURSOR)
221 [self setShouldDrawInsertionPoint:NO];
223 [textStorage drawString:string
224 atRow:row column:col cells:cells
226 foregroundColor:[NSColor colorWithRgbInt:fg]
227 backgroundColor:[NSColor colorWithArgbInt:bg]
228 specialColor:[NSColor colorWithRgbInt:sp]];
231 } else if (InsertLinesDrawType == type) {
232 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
233 int row = *((int*)bytes); bytes += sizeof(int);
234 int count = *((int*)bytes); bytes += sizeof(int);
235 int bot = *((int*)bytes); bytes += sizeof(int);
236 int left = *((int*)bytes); bytes += sizeof(int);
237 int right = *((int*)bytes); bytes += sizeof(int);
240 ASLogDebug(@" Insert %d line(s) at row %d", count, row);
242 [textStorage insertLinesAtRow:row lineCount:count
243 scrollBottom:bot left:left right:right
244 color:[NSColor colorWithArgbInt:color]];
245 } else if (DrawCursorDrawType == type) {
246 unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned);
247 int row = *((int*)bytes); bytes += sizeof(int);
248 int col = *((int*)bytes); bytes += sizeof(int);
249 int shape = *((int*)bytes); bytes += sizeof(int);
250 int percent = *((int*)bytes); bytes += sizeof(int);
253 ASLogDebug(@" Draw cursor at (%d,%d)", row, col);
255 [helper setInsertionPointColor:[NSColor colorWithRgbInt:color]];
256 [self drawInsertionPointAtRow:row column:col shape:shape
258 } else if (DrawInvertedRectDrawType == type) {
259 int row = *((int*)bytes); bytes += sizeof(int);
260 int col = *((int*)bytes); bytes += sizeof(int);
261 int nr = *((int*)bytes); bytes += sizeof(int);
262 int nc = *((int*)bytes); bytes += sizeof(int);
263 int invert = *((int*)bytes); bytes += sizeof(int);
266 ASLogDebug(@" Draw inverted rect: row=%d col=%d nrows=%d "
267 "ncols=%d", row, col, nr, nc);
269 [self drawInvertedRectAtRow:row column:col numRows:nr numColumns:nc
271 } else if (SetCursorPosDrawType == type) {
272 cursorRow = *((int*)bytes); bytes += sizeof(int);
273 cursorCol = *((int*)bytes); bytes += sizeof(int);
275 ASLogWarn(@"Unknown draw type (type=%d)", type);
279 [textStorage endEditing];
281 if (cursorRow >= 0) {
282 unsigned off = [textStorage characterIndexForRow:cursorRow
284 unsigned maxoff = [[textStorage string] length];
285 if (off > maxoff) off = maxoff;
287 [self setSelectedRange:NSMakeRange(off, 0)];
290 // NOTE: During resizing, Cocoa only sends draw messages before Vim's rows
291 // and columns are changed (due to ipc delays). Force a redraw here.
292 if ([self inLiveResize])
296 ASLogDebug(@"<==== END %s", _cmd);
300 - (void)setMouseShape:(int)shape
302 [helper setMouseShape:shape];
305 - (void)setAntialias:(BOOL)state
310 - (void)setImControl:(BOOL)enable
312 [helper setImControl:enable];
315 - (void)activateIm:(BOOL)enable
317 [helper activateIm:enable];
322 return [(MMTextStorage*)[self textStorage] font];
325 - (void)setFont:(NSFont *)newFont
327 [(MMTextStorage*)[self textStorage] setFont:newFont];
332 return [(MMTextStorage*)[self textStorage] fontWide];
335 - (void)setWideFont:(NSFont *)newFont
337 [(MMTextStorage*)[self textStorage] setWideFont:newFont];
342 return [(MMTextStorage*)[self textStorage] cellSize];
345 - (void)setLinespace:(float)newLinespace
347 return [(MMTextStorage*)[self textStorage] setLinespace:newLinespace];
352 MMTextStorage *ts = (MMTextStorage *)[self textStorage];
358 MMTextStorage *ts = (MMTextStorage *)[self textStorage];
359 return [ts maxColumns];
362 - (void)getMaxRows:(int*)rows columns:(int*)cols
364 return [(MMTextStorage*)[self textStorage] getMaxRows:rows columns:cols];
367 - (void)setMaxRows:(int)rows columns:(int)cols
369 return [(MMTextStorage*)[self textStorage] setMaxRows:rows columns:cols];
372 - (NSRect)rectForRowsInRange:(NSRange)range
374 return [(MMTextStorage*)[self textStorage] rectForRowsInRange:range];
377 - (NSRect)rectForColumnsInRange:(NSRange)range
379 return [(MMTextStorage*)[self textStorage] rectForColumnsInRange:range];
382 - (void)setDefaultColorsBackground:(NSColor *)bgColor
383 foreground:(NSColor *)fgColor
385 [self setBackgroundColor:bgColor];
386 return [(MMTextStorage*)[self textStorage]
387 setDefaultColorsBackground:bgColor foreground:fgColor];
390 - (NSColor *)defaultBackgroundColor
392 return [(MMTextStorage*)[self textStorage] defaultBackgroundColor];
395 - (NSColor *)defaultForegroundColor
397 return [(MMTextStorage*)[self textStorage] defaultForegroundColor];
400 - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size
402 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
403 int right = [ud integerForKey:MMTextInsetRightKey];
404 int bot = [ud integerForKey:MMTextInsetBottomKey];
406 size.width -= [self textContainerOrigin].x + right;
407 size.height -= [self textContainerOrigin].y + bot;
409 NSSize newSize = [(MMTextStorage*)[self textStorage] fitToSize:size
413 newSize.width += [self textContainerOrigin].x + right;
414 newSize.height += [self textContainerOrigin].y + bot;
419 - (NSSize)desiredSize
421 NSSize size = [(MMTextStorage*)[self textStorage] size];
423 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
424 int right = [ud integerForKey:MMTextInsetRightKey];
425 int bot = [ud integerForKey:MMTextInsetBottomKey];
427 size.width += [self textContainerOrigin].x + right;
428 size.height += [self textContainerOrigin].y + bot;
435 NSSize cellSize = [(MMTextStorage*)[self textStorage] cellSize];
436 NSSize size = { MMMinColumns*cellSize.width, MMMinRows*cellSize.height };
438 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
439 int right = [ud integerForKey:MMTextInsetRightKey];
440 int bot = [ud integerForKey:MMTextInsetBottomKey];
442 size.width += [self textContainerOrigin].x + right;
443 size.height += [self textContainerOrigin].y + bot;
448 - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
450 MMTextStorage *ts = (MMTextStorage*)[self textStorage];
451 NSSize cellSize = [ts cellSize];
452 if (!(cellSize.width > 0 && cellSize.height > 0))
454 NSPoint origin = [self textContainerOrigin];
456 if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
457 if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
462 - (NSPoint)pointForRow:(int)row column:(int)col
464 // Return the upper-left coordinate for (row,column).
465 // NOTE: The coordinate system is flipped!
466 NSPoint pt = [self textContainerOrigin];
467 MMTextStorage *ts = (MMTextStorage*)[self textStorage];
468 NSSize cellSize = [ts cellSize];
470 pt.x += col * cellSize.width;
471 pt.y += row * cellSize.height;
476 - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
479 // Return the rect for the block which covers the specified rows and
480 // columns. The upper-left corner is the origin of this rect.
481 // NOTE: The coordinate system is flipped!
483 MMTextStorage *ts = (MMTextStorage*)[self textStorage];
484 NSSize cellSize = [ts cellSize];
486 rect.origin = [self textContainerOrigin];
487 rect.origin.x += col * cellSize.width;
488 rect.origin.y += row * cellSize.height;
489 rect.size.width = cellSize.width * nc;
490 rect.size.height = cellSize.height * nr;
500 - (void)drawRect:(NSRect)rect
502 NSGraphicsContext *context = [NSGraphicsContext currentContext];
503 [context setShouldAntialias:antialias];
505 [super drawRect:rect];
508 CGContextRef cgctx = (CGContextRef)[context graphicsPort];
509 CGContextSaveGState(cgctx);
510 CGContextSetBlendMode(cgctx, kCGBlendModeDifference);
511 CGContextSetRGBFillColor(cgctx, 1.0, 1.0, 1.0, 1.0);
514 CGRect *rect = (CGRect*)invertRects;
515 for (i = 0; i < numInvertRects; ++i)
516 CGContextFillRect(cgctx, rect[i]);
518 CGContextRestoreGState(cgctx);
525 #ifdef INCLUDE_OLD_IM_CODE
526 if ([self hasMarkedText] && ![helper useInlineIm]) {
527 shouldDrawInsertionPoint = YES;
528 MMTextStorage *ts = (MMTextStorage*)[self textStorage];
529 NSSize inset = [self textContainerInset];
531 // HACK! Get the baseline of the zeroth glyph and use that as the
532 // baseline for the marked text. (Is there a better way to figure out
533 // what baseline NSTextView uses?)
534 NSLayoutManager *lm = [self layoutManager];
535 NSTypesetter *tsr = [lm typesetter];
536 float baseline = [tsr baselineOffsetInLayoutManager:lm glyphIndex:0];
538 // Also adjust for 'linespace' option (TODO: Why not .5*linespace?)
539 baseline -= floor([ts linespace]);
541 inset.height -= baseline;
543 int len = [[helper markedText] length];
544 // The following implementation should be re-written with
545 // more efficient way...
547 // Calculate how many wide-font characters can be inserted at
548 // a first line, and draw those characters.
549 int cols = ([ts actualColumns] - insertionPointColumn);
550 NSFont *theFont = [[self markedTextAttributes]
551 valueForKey:NSFontAttributeName];
552 if (theFont == [ts fontWide])
555 int lend = cols > len ? len : cols;
556 NSAttributedString *aString = [[helper markedText]
557 attributedSubstringFromRange:NSMakeRange(done, lend)];
558 [aString drawAtPoint:NSMakePoint(
559 [helper preEditColumn]*[ts cellSize].width + inset.width,
560 [helper preEditRow]*[ts cellSize].height + inset.height)];
563 // Check whether there're charecters that aren't drawn at
564 // the first line. If everything is already done, the follow
568 // Calculate How many rows are needed to draw all the left
570 int rows = (len - done) / ([ts actualColumns] / 2) + 1;
571 for (r = 1; r <= rows; r++) {
572 lend = len - done > [ts actualColumns] / 2
573 ? [ts actualColumns] / 2 : len - done;
574 aString = [[helper markedText] attributedSubstringFromRange:
575 NSMakeRange(done, lend)];
576 [aString drawAtPoint:NSMakePoint(
578 ([helper preEditRow] + r)*[ts cellSize].height
584 #endif // INCLUDE_OLD_IM_CODE
586 if (shouldDrawInsertionPoint) {
587 MMTextStorage *ts = (MMTextStorage*)[self textStorage];
589 NSRect ipRect = [ts boundingRectForCharacterAtRow:[helper preEditRow]
590 column:[helper preEditColumn]];
591 ipRect.origin.x += [self textContainerOrigin].x;
592 ipRect.origin.y += [self textContainerOrigin].y;
594 #ifdef INCLUDE_OLD_IM_CODE
595 // Draw insertion point inside marked text.
596 if ([self hasMarkedText] && ![helper useInlineIm]) {
597 NSFont *theFont = [[self markedTextAttributes]
598 valueForKey:NSFontAttributeName];
599 if (theFont == [ts font])
600 ipRect.origin.x += [ts cellSize].width *
601 ([helper imRange].location +
602 [helper imRange].length);
604 ipRect.origin.x += [ts cellSize].width * 2 *
605 ([helper imRange].location +
606 [helper imRange].length);
608 #endif // INCLUDE_OLD_IM_CODE
610 if (MMInsertionPointHorizontal == insertionPointShape) {
611 int frac = ([ts cellSize].height * insertionPointFraction + 99)/100;
612 ipRect.origin.y += ipRect.size.height - frac;
613 ipRect.size.height = frac;
614 } else if (MMInsertionPointVertical == insertionPointShape) {
615 int frac = ([ts cellSize].width * insertionPointFraction + 99)/100;
616 ipRect.size.width = frac;
617 } else if (MMInsertionPointVerticalRight == insertionPointShape) {
618 int frac = ([ts cellSize].width * insertionPointFraction + 99)/100;
619 ipRect.origin.x += ipRect.size.width - frac;
620 ipRect.size.width = frac;
623 [[helper insertionPointColor] set];
624 if (MMInsertionPointHollow == insertionPointShape) {
630 // NOTE: We only draw the cursor once and rely on Vim to say when it
631 // should be drawn again.
632 shouldDrawInsertionPoint = NO;
636 // this code invalidates the shadow, so we don't
637 // get shifting ghost text on scroll and resize
638 // but makes speed unusable
639 MMTextStorage *ts = (MMTextStorage*)[self textStorage];
640 if ([ts defaultBackgroundAlpha] < 1.0f) {
641 if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1)
643 [[self window] setHasShadow:NO];
644 [[self window] setHasShadow:YES];
647 [[self window] invalidateShadow];
653 - (void)keyDown:(NSEvent *)event
655 [helper keyDown:event];
658 - (void)insertText:(id)string
660 [helper insertText:string];
663 - (void)doCommandBySelector:(SEL)selector
665 [helper doCommandBySelector:selector];
668 - (BOOL)performKeyEquivalent:(NSEvent *)event
670 return [helper performKeyEquivalent:event];
673 - (BOOL)hasMarkedText
675 return [helper hasMarkedText];
678 - (NSRange)markedRange
680 return [helper markedRange];
683 - (NSDictionary *)markedTextAttributes
685 return [helper markedTextAttributes];
688 - (void)setMarkedTextAttributes:(NSDictionary *)attr
690 [helper setMarkedTextAttributes:attr];
693 - (void)setMarkedText:(id)text selectedRange:(NSRange)range
695 [helper setMarkedText:text selectedRange:range];
703 - (NSRect)firstRectForCharacterRange:(NSRange)range
705 return [helper firstRectForCharacterRange:range];
708 - (void)scrollWheel:(NSEvent *)event
710 [helper scrollWheel:event];
713 - (void)mouseDown:(NSEvent *)event
715 [helper mouseDown:event];
718 - (void)rightMouseDown:(NSEvent *)event
720 [helper mouseDown:event];
723 - (void)otherMouseDown:(NSEvent *)event
725 [helper mouseDown:event];
728 - (void)mouseUp:(NSEvent *)event
730 [helper mouseUp:event];
733 - (void)rightMouseUp:(NSEvent *)event
735 [helper mouseUp:event];
738 - (void)otherMouseUp:(NSEvent *)event
740 [helper mouseUp:event];
743 - (void)mouseDragged:(NSEvent *)event
745 [helper mouseDragged:event];
748 - (void)rightMouseDragged:(NSEvent *)event
750 [helper mouseDragged:event];
753 - (void)otherMouseDragged:(NSEvent *)event
755 [helper mouseDragged:event];
758 - (void)mouseMoved:(NSEvent *)event
760 [helper mouseMoved:event];
763 - (void)mouseEntered:(NSEvent *)event
765 [helper mouseEntered:event];
768 - (void)mouseExited:(NSEvent *)event
770 [helper mouseExited:event];
773 - (void)setFrame:(NSRect)frame
775 [super setFrame:frame];
776 [helper setFrame:frame];
779 - (void)viewDidMoveToWindow
781 [helper viewDidMoveToWindow];
784 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
786 [helper viewWillMoveToWindow:newWindow];
789 - (NSMenu*)menuForEvent:(NSEvent *)event
791 // HACK! Return nil to disable NSTextView's popup menus (Vim provides its
792 // own). Called when user Ctrl-clicks in the view (this is already handled
793 // in rightMouseDown:).
797 - (NSArray *)acceptableDragTypes
799 return [NSArray arrayWithObjects:NSFilenamesPboardType,
800 NSStringPboardType, nil];
803 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
805 return [helper performDragOperation:sender];
808 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
810 return [helper draggingEntered:sender];
813 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
815 return [helper draggingUpdated:sender];
818 - (void)changeFont:(id)sender
820 [helper changeFont:sender];
823 - (void)resetCursorRects
825 // No need to set up cursor rects since Vim handles cursor changes.
828 - (void)updateFontPanel
830 // The font panel is updated whenever the font is set.
835 // NOTE: The menu items cut/copy/paste/undo/redo/select all/... must be bound
836 // to the same actions as in IB otherwise they will not work with dialogs. All
837 // we do here is forward these actions to the Vim process.
839 - (IBAction)cut:(id)sender
841 [[self windowController] vimMenuItemAction:sender];
844 - (IBAction)copy:(id)sender
846 [[self windowController] vimMenuItemAction:sender];
849 - (IBAction)paste:(id)sender
851 [[self windowController] vimMenuItemAction:sender];
854 - (IBAction)undo:(id)sender
856 [[self windowController] vimMenuItemAction:sender];
859 - (IBAction)redo:(id)sender
861 [[self windowController] vimMenuItemAction:sender];
864 - (IBAction)selectAll:(id)sender
866 [[self windowController] vimMenuItemAction:sender];
869 - (IBAction)cancelOperation:(id)sender
871 // NSTextView overrides this method to send complete:, whereas NSResponder
872 // sends cancel: by default. So override it yet again to revert to the
873 // default behavior (we resond to cancel: in MMTextViewHelper).
874 [self doCommandBySelector:@selector(cancel:)];
877 - (BOOL)validateMenuItem:(NSMenuItem *)item
879 if ([item action] == @selector(cut:)
880 || [item action] == @selector(copy:)
881 || [item action] == @selector(paste:)
882 || [item action] == @selector(undo:)
883 || [item action] == @selector(redo:)
884 || [item action] == @selector(selectAll:))
895 @implementation MMTextView (Private)
897 - (MMWindowController *)windowController
899 id windowController = [[self window] windowController];
900 if ([windowController isKindOfClass:[MMWindowController class]])
901 return (MMWindowController*)windowController;
905 - (MMVimController *)vimController
907 return [[self windowController] vimController];
910 - (void)setShouldDrawInsertionPoint:(BOOL)on
912 shouldDrawInsertionPoint = on;
915 - (void)drawInsertionPointAtRow:(int)row column:(int)col shape:(int)shape
916 fraction:(int)percent
918 // This only stores where to draw the insertion point, the actual drawing
919 // is done in drawRect:.
920 shouldDrawInsertionPoint = YES;
921 insertionPointRow = row;
922 insertionPointColumn = col;
923 insertionPointShape = shape;
924 insertionPointFraction = percent;
927 - (void)drawInvertedRectAtRow:(int)row column:(int)col numRows:(int)nrows
928 numColumns:(int)ncols invert:(int)invert
931 // The result should be inverted.
932 int n = numInvertRects++;
933 invertRects = reallocf(invertRects,
934 numInvertRects*sizeof(NSRect));
935 if (NULL != invertRects) {
936 invertRects[n] = [self rectForRow:row column:col numRows:nrows
938 [self setNeedsDisplayInRect:invertRects[n]];
943 // The result should look normal; all we need to do is to mark
944 // the rect for redrawing and Cocoa will redraw the text.
945 NSRect rect = [self rectForRow:row column:col numRows:nrows
947 [self setNeedsDisplayInRect:rect];
951 @end // MMTextView (Private)