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];
89 NSLog(@"creating MMTextView");
91 textView = [[MMTextView alloc] initWithFrame:frame];
92 [textView setTextStorage:textStorage];
94 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
95 int left = [ud integerForKey:MMTextInsetLeftKey];
96 int top = [ud integerForKey:MMTextInsetTopKey];
97 [textView setTextContainerInset:NSMakeSize(left, top)];
99 [self addSubview:textView];
101 // Create the tab view (which is never visible, but the tab bar control
102 // needs it to function).
103 tabView = [[NSTabView alloc] initWithFrame:NSZeroRect];
105 // Create the tab bar control (which is responsible for actually
106 // drawing the tabline and tabs).
107 NSRect tabFrame = [self tabBarFrameForFrame:frame];
108 tabBarControl = [[PSMTabBarControl alloc] initWithFrame:tabFrame];
110 [tabView setDelegate:tabBarControl];
112 [tabBarControl setTabView:tabView];
113 [tabBarControl setDelegate:self];
114 [tabBarControl setHidden:YES];
115 [tabBarControl setCellMinWidth:[ud integerForKey:MMTabMinWidthKey]];
116 [tabBarControl setCellMaxWidth:[ud integerForKey:MMTabMaxWidthKey]];
117 [tabBarControl setCellOptimumWidth:
118 [ud integerForKey:MMTabOptimumWidthKey]];
119 [tabBarControl setShowAddTabButton:YES];
120 [[tabBarControl addTabButton] setTarget:self];
121 [[tabBarControl addTabButton] setAction:@selector(addNewTab:)];
122 [tabBarControl setAllowsDragBetweenWindows:NO];
124 [tabBarControl setPartnerView:[self textView]];
126 // tab bar resizing only works if awakeFromNib is called (that's where
127 // the NSViewFrameDidChangeNotification callback is installed). Sounds like
128 // a PSMTabBarControl bug, let's live with it for now.
129 [tabBarControl awakeFromNib];
131 [self addSubview:tabBarControl];
133 [self setPostsFrameChangedNotifications:YES];
134 [[NSNotificationCenter defaultCenter]
135 addObserver:self selector:@selector(placeViews)
136 name:NSViewFrameDidChangeNotification object:self];
144 [tabBarControl release]; tabBarControl = nil;
145 [tabView release]; tabView = nil;
146 [scrollbars release]; scrollbars = nil;
147 [textView release]; textView = nil;
148 [textStorage release]; textStorage = nil;
153 - (MMTextView *)textView
158 - (MMTextStorage *)textStorage
163 - (NSMutableArray *)scrollbars
170 return [textView inLiveResize];
173 - (PSMTabBarControl *)tabBarControl
175 return tabBarControl;
178 - (NSTabView *)tabView
188 // NOTE! There is a bug in PSMTabBarControl in that it retains the delegate
189 // (which is the MMWindowController) so reset the delegate here, otherwise
190 // the MMWindowController never gets released resulting in a pretty serious
192 [tabView setDelegate:nil];
193 [tabBarControl setDelegate:nil];
194 [tabBarControl setTabView:nil];
195 [[self window] setDelegate:nil];
197 // NOTE! There is another bug in PSMTabBarControl where the control is not
198 // removed as an observer, so remove it here (else lots of evil nasty bugs
199 // will come and gnaw at your feet while you are sleeping).
200 [[NSNotificationCenter defaultCenter] removeObserver:tabBarControl];
202 [[NSNotificationCenter defaultCenter] removeObserver:self];
204 [tabBarControl removeFromSuperviewWithoutNeedingDisplay];
205 [textView removeFromSuperviewWithoutNeedingDisplay];
207 unsigned i, count = [scrollbars count];
208 for (i = 0; i < count; ++i) {
209 MMScroller *sb = [scrollbars objectAtIndex:i];
210 [sb removeFromSuperviewWithoutNeedingDisplay];
213 [tabView removeAllTabViewItems];
216 - (NSSize)desiredSizeForActualRowsAndColumns
218 return [self contentSizeForTextStorageSize:[[self textStorage] size]];
221 - (NSSize)getDesiredRows:(int *)r columns:(int *)c forSize:(NSSize)size
223 NSSize textViewSize = [self textViewRectForContentSize:size].size;
224 NSSize textStorageSize = [self textStorageSizeForTextViewSize:textViewSize];
225 NSSize newSize = [textStorage fitToSize:textStorageSize rows:r columns:c];
226 return [self contentSizeForTextStorageSize:newSize];
229 - (void)getActualRows:(int *)r columns:(int *)c
231 [textStorage getMaxRows:r columns:c];
234 - (void)setActualRows:(int)r columns:(int)c
236 [textStorage setMaxRows:r columns:c];
239 - (IBAction)addNewTab:(id)sender
241 // NOTE! This can get called a lot if the user holds down the key
242 // equivalent for this action, which causes the ports to fill up. If we
243 // wait for the message to be sent then the app might become unresponsive.
244 [vimController sendMessage:AddNewTabMsgID data:nil];
247 - (void)updateTabsWithData:(NSData *)data
249 const void *p = [data bytes];
250 const void *end = p + [data length];
253 // HACK! Current tab is first in the message. This way it is not
254 // necessary to guess which tab should be the selected one (this can be
255 // problematic for instance when new tabs are created).
256 int curtabIdx = *((int*)p); p += sizeof(int);
258 NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems];
261 //int wincount = *((int*)p); p += sizeof(int);
262 int length = *((int*)p); p += sizeof(int);
264 NSString *label = [[NSString alloc]
265 initWithBytesNoCopy:(void*)p
267 encoding:NSUTF8StringEncoding
271 // Set the label of the tab; add a new tab when needed.
272 NSTabViewItem *tvi = [[self tabView] numberOfTabViewItems] <= tabIdx
273 ? [self addNewTabViewItem]
274 : [tabViewItems objectAtIndex:tabIdx];
276 [tvi setLabel:label];
283 // Remove unused tabs from the NSTabView. Note that when a tab is closed
284 // the NSTabView will automatically select another tab, but we want Vim to
285 // take care of which tab to select so set the vimTaskSelectedTab flag to
286 // prevent the tab selection message to be passed on to the VimTask.
287 vimTaskSelectedTab = YES;
288 int i, count = [[self tabView] numberOfTabViewItems];
289 for (i = count-1; i >= tabIdx; --i) {
290 id tvi = [tabViewItems objectAtIndex:i];
291 //NSLog(@"Removing tab with index %d", i);
292 [[self tabView] removeTabViewItem:tvi];
294 vimTaskSelectedTab = NO;
296 [self selectTabWithIndex:curtabIdx];
299 - (void)selectTabWithIndex:(int)idx
301 //NSLog(@"%s%d", _cmd, idx);
303 NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems];
304 if (idx < 0 || idx >= [tabViewItems count]) {
305 NSLog(@"WARNING: No tab with index %d exists.", idx);
309 // Do not try to select a tab if already selected.
310 NSTabViewItem *tvi = [tabViewItems objectAtIndex:idx];
311 if (tvi != [[self tabView] selectedTabViewItem]) {
312 vimTaskSelectedTab = YES;
313 [[self tabView] selectTabViewItem:tvi];
314 vimTaskSelectedTab = NO;
316 // we might need to change the scrollbars that are visible
317 [self placeScrollbars];
321 - (NSTabViewItem *)addNewTabViewItem
323 // NOTE! A newly created tab is not by selected by default; the VimTask
324 // decides which tab should be selected at all times. However, the AppKit
325 // will automatically select the first tab added to a tab view.
327 NSTabViewItem *tvi = [[NSTabViewItem alloc] initWithIdentifier:nil];
329 // NOTE: If this is the first tab it will be automatically selected.
330 vimTaskSelectedTab = YES;
331 [[self tabView] addTabViewItem:tvi];
332 vimTaskSelectedTab = NO;
339 - (int)representedIndexOfTabViewItem:(NSTabViewItem *)tvi
341 NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems];
342 return [tabViewItems indexOfObject:tvi];
345 - (BOOL)bottomScrollbarVisible
347 unsigned i, count = [scrollbars count];
348 for (i = 0; i < count; ++i) {
349 MMScroller *scroller = [scrollbars objectAtIndex:i];
350 if ([scroller type] == MMScrollerTypeBottom && ![scroller isHidden])
357 - (BOOL)leftScrollbarVisible
359 unsigned i, count = [scrollbars count];
360 for (i = 0; i < count; ++i) {
361 MMScroller *scroller = [scrollbars objectAtIndex:i];
362 if ([scroller type] == MMScrollerTypeLeft && ![scroller isHidden])
369 - (BOOL)rightScrollbarVisible
371 unsigned i, count = [scrollbars count];
372 for (i = 0; i < count; ++i) {
373 MMScroller *scroller = [scrollbars objectAtIndex:i];
374 if ([scroller type] == MMScrollerTypeRight && ![scroller isHidden])
381 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
383 //NSLog(@"Create scroller %d of type %d", ident, type);
385 MMScroller *scroller = [[MMScroller alloc] initWithIdentifier:ident
387 [scroller setTarget:self];
388 [scroller setAction:@selector(scroll:)];
390 [self addSubview:scroller];
391 [[self scrollbars] addObject:scroller];
395 - (void)destroyScrollbarWithIdentifier:(long)ident
397 //NSLog(@"Destroy scroller %d", ident);
400 MMScroller *scroller = [self scrollbarForIdentifier:ident index:&idx];
402 [scroller removeFromSuperview];
403 [[self scrollbars] removeObjectAtIndex:idx];
405 if (![scroller isHidden]) {
406 // A visible scroller was removed, so the window must resize to
408 //NSLog(@"Visible scroller %d was destroyed, resizing window.",
410 shouldUpdateWindowSize = YES;
415 - (void)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible
417 MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL];
418 if (!scroller) return;
420 BOOL wasVisible = ![scroller isHidden];
421 //NSLog(@"%s scroller %d (was %svisible)", visible ? "Show" : "Hide",
422 // ident, wasVisible ? "" : "in");
423 [scroller setHidden:!visible];
425 if (wasVisible != visible) {
426 // A scroller was hidden or shown, so the window must resize to fit.
427 //NSLog(@"%s scroller %d and resize.", visible ? "Show" : "Hide",
429 shouldUpdateWindowSize = YES;
433 - (void)setScrollbarThumbValue:(float)val proportion:(float)prop
434 identifier:(long)ident
436 MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL];
437 //NSLog(@"Set thumb value %.2f proportion %.2f for scroller %d",
438 // val, prop, ident);
439 [scroller setFloatValue:val knobProportion:prop];
440 [scroller setEnabled:prop != 1.f];
444 - (void)scroll:(id)sender
446 NSMutableData *data = [NSMutableData data];
447 long ident = [(MMScroller*)sender identifier];
448 int hitPart = [sender hitPart];
449 float value = [sender floatValue];
451 [data appendBytes:&ident length:sizeof(long)];
452 [data appendBytes:&hitPart length:sizeof(int)];
453 [data appendBytes:&value length:sizeof(float)];
455 [vimController sendMessage:ScrollbarEventMsgID data:data];
458 - (void)placeScrollbars
460 NSRect textViewFrame = [textView frame];
461 BOOL lsbVisible = [self leftScrollbarVisible];
463 // HACK! Find the lowest left&right vertical scrollbars, as well as the
464 // rightmost horizontal scrollbar. This hack continues further down.
466 // TODO! Can there be no more than one horizontal scrollbar? If so, the
467 // code can be simplified.
468 unsigned lowestLeftSbIdx = (unsigned)-1;
469 unsigned lowestRightSbIdx = (unsigned)-1;
470 unsigned rightmostSbIdx = (unsigned)-1;
471 unsigned rowMaxLeft = 0, rowMaxRight = 0, colMax = 0;
472 unsigned i, count = [scrollbars count];
473 for (i = 0; i < count; ++i) {
474 MMScroller *scroller = [scrollbars objectAtIndex:i];
475 if (![scroller isHidden]) {
476 NSRange range = [scroller range];
477 if ([scroller type] == MMScrollerTypeLeft
478 && range.location >= rowMaxLeft) {
479 rowMaxLeft = range.location;
481 } else if ([scroller type] == MMScrollerTypeRight
482 && range.location >= rowMaxRight) {
483 rowMaxRight = range.location;
484 lowestRightSbIdx = i;
485 } else if ([scroller type] == MMScrollerTypeBottom
486 && range.location >= colMax) {
487 colMax = range.location;
493 // Place the scrollbars.
494 for (i = 0; i < count; ++i) {
495 MMScroller *scroller = [scrollbars objectAtIndex:i];
496 if ([scroller isHidden])
500 if ([scroller type] == MMScrollerTypeBottom) {
501 rect = [textStorage rectForColumnsInRange:[scroller range]];
502 rect.size.height = [NSScroller scrollerWidth];
504 rect.origin.x += [NSScroller scrollerWidth];
506 // HACK! Make sure the rightmost horizontal scrollbar covers the
507 // text view all the way to the right, otherwise it looks ugly when
508 // the user drags the window to resize.
509 if (i == rightmostSbIdx) {
510 float w = NSMaxX(textViewFrame) - NSMaxX(rect);
512 rect.size.width += w;
515 // Make sure scrollbar rect is bounded by the text view frame.
516 if (rect.origin.x < textViewFrame.origin.x)
517 rect.origin.x = textViewFrame.origin.x;
518 else if (rect.origin.x > NSMaxX(textViewFrame))
519 rect.origin.x = NSMaxX(textViewFrame);
520 if (NSMaxX(rect) > NSMaxX(textViewFrame))
521 rect.size.width -= NSMaxX(rect) - NSMaxX(textViewFrame);
522 if (rect.size.width < 0)
525 rect = [textStorage rectForRowsInRange:[scroller range]];
526 // Adjust for the fact that text layout is flipped.
527 rect.origin.y = NSMaxY(textViewFrame) - rect.origin.y
529 rect.size.width = [NSScroller scrollerWidth];
530 if ([scroller type] == MMScrollerTypeRight)
531 rect.origin.x = NSMaxX(textViewFrame);
533 // HACK! Make sure the lowest vertical scrollbar covers the text
534 // view all the way to the bottom. This is done because Vim only
535 // makes the scrollbar cover the (vim-)window it is associated with
536 // and this means there is always an empty gap in the scrollbar
537 // region next to the command line.
538 // TODO! Find a nicer way to do this.
539 if (i == lowestLeftSbIdx || i == lowestRightSbIdx) {
540 float h = rect.origin.y + rect.size.height
541 - textViewFrame.origin.y;
542 if (rect.size.height < h) {
543 rect.origin.y = textViewFrame.origin.y;
544 rect.size.height = h;
548 // Vertical scrollers must not cover the resize box in the
549 // bottom-right corner of the window.
550 if ([[self window] showsResizeIndicator] // XXX: make this a flag
551 && rect.origin.y < [NSScroller scrollerWidth]) {
552 rect.size.height -= [NSScroller scrollerWidth] - rect.origin.y;
553 rect.origin.y = [NSScroller scrollerWidth];
556 // Make sure scrollbar rect is bounded by the text view frame.
557 if (rect.origin.y < textViewFrame.origin.y) {
558 rect.size.height -= textViewFrame.origin.y - rect.origin.y;
559 rect.origin.y = textViewFrame.origin.y;
560 } else if (rect.origin.y > NSMaxY(textViewFrame))
561 rect.origin.y = NSMaxY(textViewFrame);
562 if (NSMaxY(rect) > NSMaxY(textViewFrame))
563 rect.size.height -= NSMaxY(rect) - NSMaxY(textViewFrame);
564 if (rect.size.height < 0)
565 rect.size.height = 0;
568 //NSLog(@"set scroller #%d frame = %@", i, NSStringFromRect(rect));
569 NSRect oldRect = [scroller frame];
570 if (!NSEqualRects(oldRect, rect)) {
571 [scroller setFrame:rect];
572 // Clear behind the old scroller frame, or parts of the old
573 // scroller might still be visible after setFrame:.
574 [[[self window] contentView] setNeedsDisplayInRect:oldRect];
575 [scroller setNeedsDisplay:YES];
580 - (MMScroller *)scrollbarForIdentifier:(long)ident index:(unsigned *)idx
582 unsigned i, count = [[self scrollbars] count];
583 for (i = 0; i < count; ++i) {
584 MMScroller *scroller = [[self scrollbars] objectAtIndex:i];
585 if ([scroller identifier] == ident) {
594 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
596 MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL];
597 NSRange range = NSMakeRange(pos, len);
598 if (!NSEqualRanges(range, [scroller range])) {
599 //NSLog(@"Set range %@ for scroller %d",
600 // NSStringFromRange(range), ident);
601 [scroller setRange:range];
602 // TODO! Should only do this once per update.
604 // This could be sent because a text window was created or closed, so
605 // we might need to update which scrollbars are visible.
606 [self placeScrollbars];
612 NSRect textViewRect = [self textViewRectForContentSize:[self frame].size];
614 // Give all superfluous space to the text view. It might be smaller or
615 // larger than it wants to be, but this is needed during life resizing
616 [[self textView] setFrame:textViewRect];
618 // for some reason, autoresizing doesn't work...set tab size manually
619 [tabBarControl setFrame:[self tabBarFrameForFrame:[self frame]]];
621 [self placeScrollbars];
624 - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore
626 [textStorage setDefaultColorsBackground:back foreground:fore];
627 [textView setBackgroundColor:back];
630 - (BOOL)shouldUpdateWindowSize
632 return shouldUpdateWindowSize;
635 - (void)setShouldUpdateWindowSize:(BOOL)b
637 shouldUpdateWindowSize = b;
640 - (NSSize)contentSizeForTextStorageSize:(NSSize)textViewSize
642 NSSize size = textViewSize;
644 if (![[self tabBarControl] isHidden])
645 size.height += [[self tabBarControl] frame].size.height;
647 if ([self bottomScrollbarVisible])
648 size.height += [NSScroller scrollerWidth];
649 if ([self leftScrollbarVisible])
650 size.width += [NSScroller scrollerWidth];
651 if ([self rightScrollbarVisible])
652 size.width += [NSScroller scrollerWidth];
654 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
655 int right = [ud integerForKey:MMTextInsetRightKey];
656 int bot = [ud integerForKey:MMTextInsetBottomKey];
658 size.width += [[self textView] textContainerOrigin].x + right;
659 size.height += [[self textView] textContainerOrigin].y + bot;
664 - (NSRect)textViewRectForContentSize:(NSSize)contentSize
666 NSRect rect = { 0, 0, contentSize.width, contentSize.height };
668 if (![[self tabBarControl] isHidden])
669 rect.size.height -= [[self tabBarControl] frame].size.height;
671 if ([self bottomScrollbarVisible]) {
672 rect.size.height -= [NSScroller scrollerWidth];
673 rect.origin.y += [NSScroller scrollerWidth];
675 if ([self leftScrollbarVisible]) {
676 rect.size.width -= [NSScroller scrollerWidth];
677 rect.origin.x += [NSScroller scrollerWidth];
679 if ([self rightScrollbarVisible])
680 rect.size.width -= [NSScroller scrollerWidth];
685 - (NSSize)textStorageSizeForTextViewSize:(NSSize)textViewSize
687 NSSize size = textViewSize;
689 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
690 int right = [ud integerForKey:MMTextInsetRightKey];
691 int bot = [ud integerForKey:MMTextInsetBottomKey];
693 size.width -= [[self textView] textContainerOrigin].x + right;
694 size.height -= [[self textView] textContainerOrigin].y + bot;
700 // -- PSMTabBarControl delegate ----------------------------------------------
703 - (BOOL)tabView:(NSTabView *)theTabView shouldSelectTabViewItem:
704 (NSTabViewItem *)tabViewItem
706 // NOTE: It would be reasonable to think that 'shouldSelect...' implies
707 // that this message only gets sent when the user clicks the tab.
708 // Unfortunately it is not so, which is why we need the
709 // 'vimTaskSelectedTab' flag.
711 // HACK! The selection message should not be propagated to the VimTask if
712 // the VimTask selected the tab (e.g. as opposed the user clicking the
713 // tab). The delegate method has no way of knowing who initiated the
714 // selection so a flag is set when the VimTask initiated the selection.
715 if (!vimTaskSelectedTab) {
716 // Propagate the selection message to the VimTask.
717 int idx = [self representedIndexOfTabViewItem:tabViewItem];
718 if (NSNotFound != idx) {
719 NSData *data = [NSData dataWithBytes:&idx length:sizeof(int)];
720 [vimController sendMessage:SelectTabMsgID data:data];
724 // Unless Vim selected the tab, return NO, and let Vim decide if the tab
725 // should get selected or not.
726 return vimTaskSelectedTab;
729 - (BOOL)tabView:(NSTabView *)theTabView shouldCloseTabViewItem:
730 (NSTabViewItem *)tabViewItem
732 // HACK! This method is only called when the user clicks the close button
733 // on the tab. Instead of letting the tab bar close the tab, we return NO
734 // and pass a message on to Vim to let it handle the closing.
735 int idx = [self representedIndexOfTabViewItem:tabViewItem];
736 //NSLog(@"Closing tab with index %d", idx);
737 NSData *data = [NSData dataWithBytes:&idx length:sizeof(int)];
738 [vimController sendMessage:CloseTabMsgID data:data];
743 - (void)tabView:(NSTabView *)theTabView didDragTabViewItem:
744 (NSTabViewItem *)tabViewItem toIndex:(int)idx
746 NSMutableData *data = [NSMutableData data];
747 [data appendBytes:&idx length:sizeof(int)];
749 [vimController sendMessage:DraggedTabMsgID data:data];
757 @implementation NSTabView (MMExtras)
759 - (void)removeAllTabViewItems
761 NSArray *existingItems = [self tabViewItems];
762 NSEnumerator *e = [existingItems objectEnumerator];
764 while (item = [e nextObject]){
765 [self removeTabViewItem:item];
769 @end // NSTabView (MMExtras)
774 @implementation MMScroller
776 - (id)initWithIdentifier:(long)ident type:(int)theType
778 // HACK! NSScroller creates a horizontal scroller if it is init'ed with a
779 // frame whose with exceeds its height; so create a bogus rect and pass it
781 NSRect frame = theType == MMScrollerTypeBottom
782 ? NSMakeRect(0, 0, 1, 0)
783 : NSMakeRect(0, 0, 0, 1);
785 if ((self = [super initWithFrame:frame])) {
788 [self setHidden:YES];
789 [self setEnabled:YES];
810 - (void)setRange:(NSRange)newRange
815 - (void)scrollWheel:(NSEvent *)event
817 // HACK! Pass message on to the text view.
818 MMWindowController *wc = [[self window] windowController];
819 [[wc textView] scrollWheel:event];