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 * A view class with a tabline, scrollbars, and text view.
18 #import <PSMTabBarControl.h>
20 #import "MMTextView.h"
21 #import "MMTextStorage.h"
22 #import "MMTypesetter.h"
23 #import "MMVimController.h"
25 #import "MMWindowController.h" // needed by MMScroller. TODO: remove
27 // Scroller type; these must match SBAR_* in gui.h
29 MMScrollerTypeLeft = 0,
35 @interface NSTabView (MMExtras)
36 - (void)removeAllTabViewItems;
41 @interface MMScroller : NSScroller {
46 - (id)initWithIdentifier:(long)ident type:(int)type;
50 - (void)setRange:(NSRange)newRange;
54 @interface MMVimView (Private)
55 - (BOOL)bottomScrollbarVisible;
56 - (BOOL)leftScrollbarVisible;
57 - (BOOL)rightScrollbarVisible;
58 - (void)placeScrollbars;
59 - (int)representedIndexOfTabViewItem:(NSTabViewItem *)tvi;
60 - (MMScroller *)scrollbarForIdentifier:(long)ident index:(unsigned *)idx;
61 - (NSSize)contentSizeForTextStorageSize:(NSSize)textViewSize;
62 - (NSSize)textStorageSizeForTextViewSize:(NSSize)textViewSize;
63 - (NSTabView *)tabView;
67 @implementation MMVimView
69 - (NSRect)tabBarFrameForFrame:(NSRect)frame
72 { 0, frame.size.height - 22 },
73 { frame.size.width, 22 }
78 - (MMVimView *)initWithFrame:(NSRect)frame
79 vimController:(MMVimController *)controller {
80 if (![super initWithFrame:frame])
83 vimController = controller;
84 scrollbars = [[NSMutableArray alloc] init];
86 // Set up a complete text system.
87 textStorage = [[MMTextStorage alloc] init];
88 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
89 NSTextContainer *tc = [[NSTextContainer alloc] initWithContainerSize:
90 NSMakeSize(1.0e7,1.0e7)];
92 NSString *typesetterString = [[NSUserDefaults standardUserDefaults]
93 stringForKey:MMTypesetterKey];
94 if ([typesetterString isEqual:@"MMTypesetter"]) {
95 NSTypesetter *typesetter = [[MMTypesetter alloc] init];
96 [lm setTypesetter:typesetter];
98 } else if ([typesetterString isEqual:@"MMTypesetter2"]) {
99 NSTypesetter *typesetter = [[MMTypesetter2 alloc] init];
100 [lm setTypesetter:typesetter];
101 [typesetter release];
103 // Only MMTypesetter supports different cell width multipliers.
104 [[NSUserDefaults standardUserDefaults]
105 setFloat:1.0 forKey:MMCellWidthMultiplierKey];
108 // The characters in the text storage are in display order, so disable
109 // bidirectional text processing (this call is 10.4 only).
110 [[lm typesetter] setBidiProcessingEnabled:NO];
112 [tc setWidthTracksTextView:NO];
113 [tc setHeightTracksTextView:NO];
114 [tc setLineFragmentPadding:0];
116 [textStorage addLayoutManager:lm];
117 [lm addTextContainer:tc];
119 textView = [[MMTextView alloc] initWithFrame:frame
122 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
123 int left = [ud integerForKey:MMTextInsetLeftKey];
124 int top = [ud integerForKey:MMTextInsetTopKey];
125 [textView setTextContainerInset:NSMakeSize(left, top)];
127 [self addSubview:textView];
129 // The text storage retains the layout manager which in turn retains
130 // the text container.
134 // Create the tab view (which is never visible, but the tab bar control
135 // needs it to function).
136 tabView = [[NSTabView alloc] initWithFrame:NSZeroRect];
138 // Create the tab bar control (which is responsible for actually
139 // drawing the tabline and tabs).
140 NSRect tabFrame = [self tabBarFrameForFrame:frame];
141 tabBarControl = [[PSMTabBarControl alloc] initWithFrame:tabFrame];
143 [tabView setDelegate:tabBarControl];
145 [tabBarControl setTabView:tabView];
146 [tabBarControl setDelegate:self];
147 [tabBarControl setHidden:YES];
148 [tabBarControl setCellMinWidth:[ud integerForKey:MMTabMinWidthKey]];
149 [tabBarControl setCellMaxWidth:[ud integerForKey:MMTabMaxWidthKey]];
150 [tabBarControl setCellOptimumWidth:
151 [ud integerForKey:MMTabOptimumWidthKey]];
152 [tabBarControl setShowAddTabButton:YES];
153 [[tabBarControl addTabButton] setTarget:self];
154 [[tabBarControl addTabButton] setAction:@selector(addNewTab:)];
155 [tabBarControl setAllowsDragBetweenWindows:NO];
157 [tabBarControl setPartnerView:[self textView]];
159 // tab bar resizing only works if awakeFromNib is called (that's where
160 // the NSViewFrameDidChangeNotification callback is installed). Sounds like
161 // a PSMTabBarControl bug, let's live with it for now.
162 [tabBarControl awakeFromNib];
164 [self addSubview:tabBarControl];
166 [self setPostsFrameChangedNotifications:YES];
167 [[NSNotificationCenter defaultCenter]
168 addObserver:self selector:@selector(placeViews)
169 name:NSViewFrameDidChangeNotification object:self];
176 [tabBarControl release]; tabBarControl = nil;
177 [tabView release]; tabView = nil;
178 [scrollbars release]; scrollbars = nil;
179 [textView release]; textView = nil;
180 [textStorage release]; textStorage = nil;
185 - (MMTextView *)textView
190 - (MMTextStorage *)textStorage
195 - (NSMutableArray *)scrollbars
202 return [textView inLiveResize];
205 - (PSMTabBarControl *)tabBarControl
207 return tabBarControl;
210 - (NSTabView *)tabView
220 // NOTE! There is a bug in PSMTabBarControl in that it retains the delegate
221 // (which is the MMWindowController) so reset the delegate here, otherwise
222 // the MMWindowController never gets released resulting in a pretty serious
224 [tabView setDelegate:nil];
225 [tabBarControl setDelegate:nil];
226 [tabBarControl setTabView:nil];
227 [[self window] setDelegate:nil];
229 // NOTE! There is another bug in PSMTabBarControl where the control is not
230 // removed as an observer, so remove it here (else lots of evil nasty bugs
231 // will come and gnaw at your feet while you are sleeping).
232 [[NSNotificationCenter defaultCenter] removeObserver:tabBarControl];
234 [[NSNotificationCenter defaultCenter] removeObserver:self];
236 [tabBarControl removeFromSuperviewWithoutNeedingDisplay];
237 [textView removeFromSuperviewWithoutNeedingDisplay];
239 unsigned i, count = [scrollbars count];
240 for (i = 0; i < count; ++i) {
241 MMScroller *sb = [scrollbars objectAtIndex:i];
242 [sb removeFromSuperviewWithoutNeedingDisplay];
245 [tabView removeAllTabViewItems];
248 - (NSSize)desiredSizeForActualRowsAndColumns
250 return [self contentSizeForTextStorageSize:[[self textStorage] size]];
253 - (NSSize)getDesiredRows:(int *)r columns:(int *)c forSize:(NSSize)size
255 NSSize textViewSize = [self textViewRectForContentSize:size].size;
256 NSSize textStorageSize = [self textStorageSizeForTextViewSize:textViewSize];
257 NSSize newSize = [textStorage fitToSize:textStorageSize rows:r columns:c];
258 return [self contentSizeForTextStorageSize:newSize];
261 - (void)getActualRows:(int *)r columns:(int *)c
263 [textStorage getMaxRows:r columns:c];
266 - (void)setActualRows:(int)r columns:(int)c
268 [textStorage setMaxRows:r columns:c];
271 - (IBAction)addNewTab:(id)sender
273 // NOTE! This can get called a lot if the user holds down the key
274 // equivalent for this action, which causes the ports to fill up. If we
275 // wait for the message to be sent then the app might become unresponsive.
276 [vimController sendMessage:AddNewTabMsgID data:nil];
279 - (void)updateTabsWithData:(NSData *)data
281 const void *p = [data bytes];
282 const void *end = p + [data length];
285 // HACK! Current tab is first in the message. This way it is not
286 // necessary to guess which tab should be the selected one (this can be
287 // problematic for instance when new tabs are created).
288 int curtabIdx = *((int*)p); p += sizeof(int);
290 NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems];
293 //int wincount = *((int*)p); p += sizeof(int);
294 int length = *((int*)p); p += sizeof(int);
296 NSString *label = [[NSString alloc]
297 initWithBytesNoCopy:(void*)p
299 encoding:NSUTF8StringEncoding
303 // Set the label of the tab; add a new tab when needed.
304 NSTabViewItem *tvi = [[self tabView] numberOfTabViewItems] <= tabIdx
305 ? [self addNewTabViewItem]
306 : [tabViewItems objectAtIndex:tabIdx];
308 [tvi setLabel:label];
315 // Remove unused tabs from the NSTabView. Note that when a tab is closed
316 // the NSTabView will automatically select another tab, but we want Vim to
317 // take care of which tab to select so set the vimTaskSelectedTab flag to
318 // prevent the tab selection message to be passed on to the VimTask.
319 vimTaskSelectedTab = YES;
320 int i, count = [[self tabView] numberOfTabViewItems];
321 for (i = count-1; i >= tabIdx; --i) {
322 id tvi = [tabViewItems objectAtIndex:i];
323 //NSLog(@"Removing tab with index %d", i);
324 [[self tabView] removeTabViewItem:tvi];
326 vimTaskSelectedTab = NO;
328 [self selectTabWithIndex:curtabIdx];
331 - (void)selectTabWithIndex:(int)idx
333 //NSLog(@"%s%d", _cmd, idx);
335 NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems];
336 if (idx < 0 || idx >= [tabViewItems count]) {
337 NSLog(@"WARNING: No tab with index %d exists.", idx);
341 // Do not try to select a tab if already selected.
342 NSTabViewItem *tvi = [tabViewItems objectAtIndex:idx];
343 if (tvi != [[self tabView] selectedTabViewItem]) {
344 vimTaskSelectedTab = YES;
345 [[self tabView] selectTabViewItem:tvi];
346 vimTaskSelectedTab = NO;
348 // we might need to change the scrollbars that are visible
349 [self placeScrollbars];
353 - (NSTabViewItem *)addNewTabViewItem
355 // NOTE! A newly created tab is not by selected by default; the VimTask
356 // decides which tab should be selected at all times. However, the AppKit
357 // will automatically select the first tab added to a tab view.
359 NSTabViewItem *tvi = [[NSTabViewItem alloc] initWithIdentifier:nil];
361 // NOTE: If this is the first tab it will be automatically selected.
362 vimTaskSelectedTab = YES;
363 [[self tabView] addTabViewItem:tvi];
364 vimTaskSelectedTab = NO;
371 - (int)representedIndexOfTabViewItem:(NSTabViewItem *)tvi
373 NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems];
374 return [tabViewItems indexOfObject:tvi];
377 - (BOOL)bottomScrollbarVisible
379 unsigned i, count = [scrollbars count];
380 for (i = 0; i < count; ++i) {
381 MMScroller *scroller = [scrollbars objectAtIndex:i];
382 if ([scroller type] == MMScrollerTypeBottom && ![scroller isHidden])
389 - (BOOL)leftScrollbarVisible
391 unsigned i, count = [scrollbars count];
392 for (i = 0; i < count; ++i) {
393 MMScroller *scroller = [scrollbars objectAtIndex:i];
394 if ([scroller type] == MMScrollerTypeLeft && ![scroller isHidden])
401 - (BOOL)rightScrollbarVisible
403 unsigned i, count = [scrollbars count];
404 for (i = 0; i < count; ++i) {
405 MMScroller *scroller = [scrollbars objectAtIndex:i];
406 if ([scroller type] == MMScrollerTypeRight && ![scroller isHidden])
413 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
415 //NSLog(@"Create scroller %d of type %d", ident, type);
417 MMScroller *scroller = [[MMScroller alloc] initWithIdentifier:ident
419 [scroller setTarget:self];
420 [scroller setAction:@selector(scroll:)];
422 [self addSubview:scroller];
423 [[self scrollbars] addObject:scroller];
427 - (void)destroyScrollbarWithIdentifier:(long)ident
429 //NSLog(@"Destroy scroller %d", ident);
432 MMScroller *scroller = [self scrollbarForIdentifier:ident index:&idx];
434 [scroller removeFromSuperview];
435 [[self scrollbars] removeObjectAtIndex:idx];
437 if (![scroller isHidden]) {
438 // A visible scroller was removed, so the window must resize to
440 //NSLog(@"Visible scroller %d was destroyed, resizing window.",
442 shouldUpdateWindowSize = YES;
447 - (void)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible
449 MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL];
450 if (!scroller) return;
452 BOOL wasVisible = ![scroller isHidden];
453 //NSLog(@"%s scroller %d (was %svisible)", visible ? "Show" : "Hide",
454 // ident, wasVisible ? "" : "in");
455 [scroller setHidden:!visible];
457 if (wasVisible != visible) {
458 // A scroller was hidden or shown, so the window must resize to fit.
459 //NSLog(@"%s scroller %d and resize.", visible ? "Show" : "Hide",
461 shouldUpdateWindowSize = YES;
465 - (void)setScrollbarThumbValue:(float)val proportion:(float)prop
466 identifier:(long)ident
468 MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL];
469 //NSLog(@"Set thumb value %.2f proportion %.2f for scroller %d",
470 // val, prop, ident);
471 [scroller setFloatValue:val knobProportion:prop];
472 [scroller setEnabled:prop != 1.f];
476 - (void)scroll:(id)sender
478 NSMutableData *data = [NSMutableData data];
479 long ident = [(MMScroller*)sender identifier];
480 int hitPart = [sender hitPart];
481 float value = [sender floatValue];
483 [data appendBytes:&ident length:sizeof(long)];
484 [data appendBytes:&hitPart length:sizeof(int)];
485 [data appendBytes:&value length:sizeof(float)];
487 [vimController sendMessage:ScrollbarEventMsgID data:data];
490 - (void)placeScrollbars
492 NSRect textViewFrame = [textView frame];
493 BOOL lsbVisible = [self leftScrollbarVisible];
495 // HACK! Find the lowest left&right vertical scrollbars, as well as the
496 // rightmost horizontal scrollbar. This hack continues further down.
498 // TODO! Can there be no more than one horizontal scrollbar? If so, the
499 // code can be simplified.
500 unsigned lowestLeftSbIdx = (unsigned)-1;
501 unsigned lowestRightSbIdx = (unsigned)-1;
502 unsigned rightmostSbIdx = (unsigned)-1;
503 unsigned rowMaxLeft = 0, rowMaxRight = 0, colMax = 0;
504 unsigned i, count = [scrollbars count];
505 for (i = 0; i < count; ++i) {
506 MMScroller *scroller = [scrollbars objectAtIndex:i];
507 if (![scroller isHidden]) {
508 NSRange range = [scroller range];
509 if ([scroller type] == MMScrollerTypeLeft
510 && range.location >= rowMaxLeft) {
511 rowMaxLeft = range.location;
513 } else if ([scroller type] == MMScrollerTypeRight
514 && range.location >= rowMaxRight) {
515 rowMaxRight = range.location;
516 lowestRightSbIdx = i;
517 } else if ([scroller type] == MMScrollerTypeBottom
518 && range.location >= colMax) {
519 colMax = range.location;
525 // Place the scrollbars.
526 for (i = 0; i < count; ++i) {
527 MMScroller *scroller = [scrollbars objectAtIndex:i];
528 if ([scroller isHidden])
532 if ([scroller type] == MMScrollerTypeBottom) {
533 rect = [textStorage rectForColumnsInRange:[scroller range]];
534 rect.size.height = [NSScroller scrollerWidth];
536 rect.origin.x += [NSScroller scrollerWidth];
538 // HACK! Make sure the rightmost horizontal scrollbar covers the
539 // text view all the way to the right, otherwise it looks ugly when
540 // the user drags the window to resize.
541 if (i == rightmostSbIdx) {
542 float w = NSMaxX(textViewFrame) - NSMaxX(rect);
544 rect.size.width += w;
547 // Make sure scrollbar rect is bounded by the text view frame.
548 if (rect.origin.x < textViewFrame.origin.x)
549 rect.origin.x = textViewFrame.origin.x;
550 else if (rect.origin.x > NSMaxX(textViewFrame))
551 rect.origin.x = NSMaxX(textViewFrame);
552 if (NSMaxX(rect) > NSMaxX(textViewFrame))
553 rect.size.width -= NSMaxX(rect) - NSMaxX(textViewFrame);
554 if (rect.size.width < 0)
557 rect = [textStorage rectForRowsInRange:[scroller range]];
558 // Adjust for the fact that text layout is flipped.
559 rect.origin.y = NSMaxY(textViewFrame) - rect.origin.y
561 rect.size.width = [NSScroller scrollerWidth];
562 if ([scroller type] == MMScrollerTypeRight)
563 rect.origin.x = NSMaxX(textViewFrame);
565 // HACK! Make sure the lowest vertical scrollbar covers the text
566 // view all the way to the bottom. This is done because Vim only
567 // makes the scrollbar cover the (vim-)window it is associated with
568 // and this means there is always an empty gap in the scrollbar
569 // region next to the command line.
570 // TODO! Find a nicer way to do this.
571 if (i == lowestLeftSbIdx || i == lowestRightSbIdx) {
572 float h = rect.origin.y + rect.size.height
573 - textViewFrame.origin.y;
574 if (rect.size.height < h) {
575 rect.origin.y = textViewFrame.origin.y;
576 rect.size.height = h;
580 // Vertical scrollers must not cover the resize box in the
581 // bottom-right corner of the window.
582 if ([[self window] showsResizeIndicator] // XXX: make this a flag
583 && rect.origin.y < [NSScroller scrollerWidth]) {
584 rect.size.height -= [NSScroller scrollerWidth] - rect.origin.y;
585 rect.origin.y = [NSScroller scrollerWidth];
588 // Make sure scrollbar rect is bounded by the text view frame.
589 if (rect.origin.y < textViewFrame.origin.y) {
590 rect.size.height -= textViewFrame.origin.y - rect.origin.y;
591 rect.origin.y = textViewFrame.origin.y;
592 } else if (rect.origin.y > NSMaxY(textViewFrame))
593 rect.origin.y = NSMaxY(textViewFrame);
594 if (NSMaxY(rect) > NSMaxY(textViewFrame))
595 rect.size.height -= NSMaxY(rect) - NSMaxY(textViewFrame);
596 if (rect.size.height < 0)
597 rect.size.height = 0;
600 //NSLog(@"set scroller #%d frame = %@", i, NSStringFromRect(rect));
601 NSRect oldRect = [scroller frame];
602 if (!NSEqualRects(oldRect, rect)) {
603 [scroller setFrame:rect];
604 // Clear behind the old scroller frame, or parts of the old
605 // scroller might still be visible after setFrame:.
606 [[[self window] contentView] setNeedsDisplayInRect:oldRect];
607 [scroller setNeedsDisplay:YES];
612 - (MMScroller *)scrollbarForIdentifier:(long)ident index:(unsigned *)idx
614 unsigned i, count = [[self scrollbars] count];
615 for (i = 0; i < count; ++i) {
616 MMScroller *scroller = [[self scrollbars] objectAtIndex:i];
617 if ([scroller identifier] == ident) {
626 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
628 MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL];
629 NSRange range = NSMakeRange(pos, len);
630 if (!NSEqualRanges(range, [scroller range])) {
631 //NSLog(@"Set range %@ for scroller %d",
632 // NSStringFromRange(range), ident);
633 [scroller setRange:range];
634 // TODO! Should only do this once per update.
636 // This could be sent because a text window was created or closed, so
637 // we might need to update which scrollbars are visible.
638 [self placeScrollbars];
644 NSRect textViewRect = [self textViewRectForContentSize:[self frame].size];
646 // Give all superfluous space to the text view. It might be smaller or
647 // larger than it wants to be, but this is needed during life resizing
648 [[self textView] setFrame:textViewRect];
650 // for some reason, autoresizing doesn't work...set tab size manually
651 [tabBarControl setFrame:[self tabBarFrameForFrame:[self frame]]];
653 [self placeScrollbars];
656 - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore
658 [textStorage setDefaultColorsBackground:back foreground:fore];
659 [textView setBackgroundColor:back];
662 - (BOOL)shouldUpdateWindowSize
664 return shouldUpdateWindowSize;
667 - (void)setShouldUpdateWindowSize:(BOOL)b
669 shouldUpdateWindowSize = b;
672 - (NSSize)contentSizeForTextStorageSize:(NSSize)textViewSize
674 NSSize size = textViewSize;
676 if (![[self tabBarControl] isHidden])
677 size.height += [[self tabBarControl] frame].size.height;
679 if ([self bottomScrollbarVisible])
680 size.height += [NSScroller scrollerWidth];
681 if ([self leftScrollbarVisible])
682 size.width += [NSScroller scrollerWidth];
683 if ([self rightScrollbarVisible])
684 size.width += [NSScroller scrollerWidth];
686 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
687 int right = [ud integerForKey:MMTextInsetRightKey];
688 int bot = [ud integerForKey:MMTextInsetBottomKey];
690 size.width += [[self textView] textContainerOrigin].x + right;
691 size.height += [[self textView] textContainerOrigin].y + bot;
696 - (NSRect)textViewRectForContentSize:(NSSize)contentSize
698 NSRect rect = { 0, 0, contentSize.width, contentSize.height };
700 if (![[self tabBarControl] isHidden])
701 rect.size.height -= [[self tabBarControl] frame].size.height;
703 if ([self bottomScrollbarVisible]) {
704 rect.size.height -= [NSScroller scrollerWidth];
705 rect.origin.y += [NSScroller scrollerWidth];
707 if ([self leftScrollbarVisible]) {
708 rect.size.width -= [NSScroller scrollerWidth];
709 rect.origin.x += [NSScroller scrollerWidth];
711 if ([self rightScrollbarVisible])
712 rect.size.width -= [NSScroller scrollerWidth];
717 - (NSSize)textStorageSizeForTextViewSize:(NSSize)textViewSize
719 NSSize size = textViewSize;
721 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
722 int right = [ud integerForKey:MMTextInsetRightKey];
723 int bot = [ud integerForKey:MMTextInsetBottomKey];
725 size.width -= [[self textView] textContainerOrigin].x + right;
726 size.height -= [[self textView] textContainerOrigin].y + bot;
732 // -- PSMTabBarControl delegate ----------------------------------------------
735 - (BOOL)tabView:(NSTabView *)theTabView shouldSelectTabViewItem:
736 (NSTabViewItem *)tabViewItem
738 // NOTE: It would be reasonable to think that 'shouldSelect...' implies
739 // that this message only gets sent when the user clicks the tab.
740 // Unfortunately it is not so, which is why we need the
741 // 'vimTaskSelectedTab' flag.
743 // HACK! The selection message should not be propagated to the VimTask if
744 // the VimTask selected the tab (e.g. as opposed the user clicking the
745 // tab). The delegate method has no way of knowing who initiated the
746 // selection so a flag is set when the VimTask initiated the selection.
747 if (!vimTaskSelectedTab) {
748 // Propagate the selection message to the VimTask.
749 int idx = [self representedIndexOfTabViewItem:tabViewItem];
750 if (NSNotFound != idx) {
751 NSData *data = [NSData dataWithBytes:&idx length:sizeof(int)];
752 [vimController sendMessage:SelectTabMsgID data:data];
756 // Unless Vim selected the tab, return NO, and let Vim decide if the tab
757 // should get selected or not.
758 return vimTaskSelectedTab;
761 - (BOOL)tabView:(NSTabView *)theTabView shouldCloseTabViewItem:
762 (NSTabViewItem *)tabViewItem
764 // HACK! This method is only called when the user clicks the close button
765 // on the tab. Instead of letting the tab bar close the tab, we return NO
766 // and pass a message on to Vim to let it handle the closing.
767 int idx = [self representedIndexOfTabViewItem:tabViewItem];
768 //NSLog(@"Closing tab with index %d", idx);
769 NSData *data = [NSData dataWithBytes:&idx length:sizeof(int)];
770 [vimController sendMessage:CloseTabMsgID data:data];
775 - (void)tabView:(NSTabView *)theTabView didDragTabViewItem:
776 (NSTabViewItem *)tabViewItem toIndex:(int)idx
778 NSMutableData *data = [NSMutableData data];
779 [data appendBytes:&idx length:sizeof(int)];
781 [vimController sendMessage:DraggedTabMsgID data:data];
789 @implementation NSTabView (MMExtras)
791 - (void)removeAllTabViewItems
793 NSArray *existingItems = [self tabViewItems];
794 NSEnumerator *e = [existingItems objectEnumerator];
796 while (item = [e nextObject]){
797 [self removeTabViewItem:item];
801 @end // NSTabView (MMExtras)
806 @implementation MMScroller
808 - (id)initWithIdentifier:(long)ident type:(int)theType
810 // HACK! NSScroller creates a horizontal scroller if it is init'ed with a
811 // frame whose with exceeds its height; so create a bogus rect and pass it
813 NSRect frame = theType == MMScrollerTypeBottom
814 ? NSMakeRect(0, 0, 1, 0)
815 : NSMakeRect(0, 0, 0, 1);
817 if ((self = [super initWithFrame:frame])) {
820 [self setHidden:YES];
821 [self setEnabled:YES];
842 - (void)setRange:(NSRange)newRange
847 - (void)scrollWheel:(NSEvent *)event
849 // HACK! Pass message on to the text view.
850 MMWindowController *wc = [[self window] windowController];
851 [[wc textView] scrollWheel:event];