Release text view helper correctly
[MacVim.git] / src / MacVim / MMTextView.m
blob569c8dd16505662fff8c0b5beb949248c0372f53
1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
2  *
3  * VIM - Vi IMproved            by Bram Moolenaar
4  *                              MacVim GUI port by Bjorn Winckler
5  *
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.
9  */
11  * MMTextView
12  *
13  * Dispatches keyboard and mouse input to the backend.  Handles drag-n-drop of
14  * files onto window.
15  *
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.
19  */
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;
45 @end
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];
65         [typesetter release];
66 #if MM_USE_ROW_CACHE
67     } else if ([typesetterString isEqual:@"MMTypesetter2"]) {
68         NSTypesetter *typesetter = [[MMTypesetter2 alloc] init];
69         [lm setTypesetter:typesetter];
70         [typesetter release];
71 #endif // MM_USE_ROW_CACHE
72     } else {
73         // Only MMTypesetter supports different cell width multipliers.
74         [[NSUserDefaults standardUserDefaults]
75                 setFloat:1.0 forKey:MMCellWidthMultiplierKey];
76     }
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.
91     [tc autorelease];
92     [lm autorelease];
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];
100         return nil;
101     }
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.
108     antialias = YES;
110     return self;
113 - (void)dealloc
115     ASLogDebug(@"");
117     if (invertRects) {
118         free(invertRects);
119         invertRects = NULL;
120         numInvertRects = 0;
121     }
123     [helper setTextView:nil];
124     [helper release];  helper = nil;
126     [super dealloc];
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).
135     return NO;
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];
148     if (!textStorage)
149         return;
151     const void *bytes = [data bytes];
152     const void *end = bytes + [data length];
153     int cursorRow = -1, cursorCol = 0;
155 #if MM_DEBUG_DRAWING
156     ASLogDebug(@"====> BEGIN %s", _cmd);
157 #endif
158     [textStorage beginEditing];
160     // TODO: Sanity check input
162     while (bytes < end) {
163         int type = *((int*)bytes);  bytes += sizeof(int);
165         if (ClearAllDrawType == type) {
166 #if MM_DEBUG_DRAWING
167             ASLogDebug(@"   Clear all");
168 #endif
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);
177 #if MM_DEBUG_DRAWING
178             ASLogDebug(@"   Clear block (%d,%d) -> (%d,%d)", row1, col1,
179                        row2,col2);
180 #endif
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);
192 #if MM_DEBUG_DRAWING
193             ASLogDebug(@"   Delete %d line(s) from %d", count, row);
194 #endif
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];
210             bytes += len;
212 #if MM_DEBUG_DRAWING
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] : @"");
216 #endif
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
225                           withFlags:flags
226                     foregroundColor:[NSColor colorWithRgbInt:fg]
227                     backgroundColor:[NSColor colorWithArgbInt:bg]
228                        specialColor:[NSColor colorWithRgbInt:sp]];
230             [string release];
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);
239 #if MM_DEBUG_DRAWING
240             ASLogDebug(@"   Insert %d line(s) at row %d", count, row);
241 #endif
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);
252 #if MM_DEBUG_DRAWING
253             ASLogDebug(@"   Draw cursor at (%d,%d)", row, col);
254 #endif
255             [helper setInsertionPointColor:[NSColor colorWithRgbInt:color]];
256             [self drawInsertionPointAtRow:row column:col shape:shape
257                                  fraction:percent];
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);
265 #if MM_DEBUG_DRAWING
266             ASLogDebug(@"   Draw inverted rect: row=%d col=%d nrows=%d "
267                        "ncols=%d", row, col, nr, nc);
268 #endif
269             [self drawInvertedRectAtRow:row column:col numRows:nr numColumns:nc
270                                  invert:invert];
271         } else if (SetCursorPosDrawType == type) {
272             cursorRow = *((int*)bytes);  bytes += sizeof(int);
273             cursorCol = *((int*)bytes);  bytes += sizeof(int);
274         } else {
275             ASLogWarn(@"Unknown draw type (type=%d)", type);
276         }
277     }
279     [textStorage endEditing];
281     if (cursorRow >= 0) {
282         unsigned off = [textStorage characterIndexForRow:cursorRow
283                                                   column:cursorCol];
284         unsigned maxoff = [[textStorage string] length];
285         if (off > maxoff) off = maxoff;
287         [self setSelectedRange:NSMakeRange(off, 0)];
288     }
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])
293         [self display];
295 #if MM_DEBUG_DRAWING
296     ASLogDebug(@"<==== END   %s", _cmd);
297 #endif
300 - (void)setMouseShape:(int)shape
302     [helper setMouseShape:shape];
305 - (void)setAntialias:(BOOL)state
307     antialias = state;
310 - (void)setImControl:(BOOL)enable
312     [helper setImControl:enable];
315 - (void)activateIm:(BOOL)enable
317     [helper activateIm:enable];
320 - (NSFont *)font
322     return [(MMTextStorage*)[self textStorage] font];
325 - (void)setFont:(NSFont *)newFont
327     [(MMTextStorage*)[self textStorage] setFont:newFont];
330 - (NSFont *)fontWide
332     return [(MMTextStorage*)[self textStorage] fontWide];
335 - (void)setWideFont:(NSFont *)newFont
337     [(MMTextStorage*)[self textStorage] setWideFont:newFont];
340 - (NSSize)cellSize
342     return [(MMTextStorage*)[self textStorage] cellSize];
345 - (void)setLinespace:(float)newLinespace
347     return [(MMTextStorage*)[self textStorage] setLinespace:newLinespace];
350 - (int)maxRows
352     MMTextStorage *ts = (MMTextStorage *)[self textStorage];
353     return [ts maxRows];
356 - (int)maxColumns
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
410                                                               rows:rows
411                                                            columns:cols];
413     newSize.width += [self textContainerOrigin].x + right;
414     newSize.height += [self textContainerOrigin].y + bot;
416     return newSize;
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;
430     return size;
433 - (NSSize)minSize
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;
445     return size;
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))
453         return NO;
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);
459     return YES;
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;
473     return pt;
476 - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
477           numColumns:(int)nc
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!
482     NSRect rect;
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;
492     return rect;
495 - (BOOL)isOpaque
497     return NO;
500 - (void)drawRect:(NSRect)rect
502     NSGraphicsContext *context = [NSGraphicsContext currentContext];
503     [context setShouldAntialias:antialias];
505     [super drawRect:rect];
507     if (invertRects) {
508         CGContextRef cgctx = (CGContextRef)[context graphicsPort];
509         CGContextSaveGState(cgctx);
510         CGContextSetBlendMode(cgctx, kCGBlendModeDifference);
511         CGContextSetRGBFillColor(cgctx, 1.0, 1.0, 1.0, 1.0);
513         int i;
514         CGRect *rect = (CGRect*)invertRects;
515         for (i = 0; i < numInvertRects; ++i)
516             CGContextFillRect(cgctx, rect[i]);
518         CGContextRestoreGState(cgctx);
520         free(invertRects);
521         invertRects = NULL;
522         numInvertRects = 0;
523     }
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])
553             cols = cols / 2;
554         int done = 0;
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)];
562         done = lend;
563         // Check whether there're charecters that aren't drawn at
564         // the first line. If everything is already done, the follow
565         // check fails.
566         if (done != len) {
567             int r;
568             // Calculate How many rows are needed to draw all the left
569             // characters.
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(
577                         inset.width,
578                         ([helper preEditRow] + r)*[ts cellSize].height
579                             + inset.height)];
580                 done += lend;
581             }
582         }
583     }
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);
603             else
604                 ipRect.origin.x += [ts cellSize].width * 2 *
605                                    ([helper imRange].location +
606                                    [helper imRange].length);
607         }
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;
621         }
623         [[helper insertionPointColor] set];
624         if (MMInsertionPointHollow == insertionPointShape) {
625             NSFrameRect(ipRect);
626         } else {
627             NSRectFill(ipRect);
628         }
630         // NOTE: We only draw the cursor once and rely on Vim to say when it
631         // should be drawn again.
632         shouldDrawInsertionPoint = NO;
633     }
635 #if 0
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)
642         {
643             [[self window] setHasShadow:NO];
644             [[self window] setHasShadow:YES];
645         }
646         else
647             [[self window] invalidateShadow];
649     }
650 #endif
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];
698 - (void)unmarkText
700     [helper unmarkText];
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:).
794     return nil;
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:))
885         return [item tag];
887     return YES;
890 @end // MMTextView
895 @implementation MMTextView (Private)
897 - (MMWindowController *)windowController
899     id windowController = [[self window] windowController];
900     if ([windowController isKindOfClass:[MMWindowController class]])
901         return (MMWindowController*)windowController;
902     return nil;
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
930     if (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
937                                    numColumns:ncols];
938             [self setNeedsDisplayInRect:invertRects[n]];
939         } else {
940             numInvertRects = 0;
941         }
942     } else {
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
946                             numColumns:ncols];
947         [self setNeedsDisplayInRect:rect];
948     }
951 @end // MMTextView (Private)