5 // Created by John Pannell on 10/13/05.
6 // Copyright 2005 Positive Spin Media. All rights reserved.
9 #import "PSMTabBarControl.h"
10 #import "PSMTabBarCell.h"
11 #import "PSMOverflowPopUpButton.h"
12 #import "PSMRolloverButton.h"
13 #import "PSMTabStyle.h"
14 #import "PSMMetalTabStyle.h"
15 #import "PSMAquaTabStyle.h"
16 #import "PSMUnifiedTabStyle.h"
17 #import "PSMTabDragAssistant.h"
19 @interface PSMTabBarControl (Private)
21 - (float)availableCellWidth;
22 - (NSRect)genericCellRect;
24 // constructor/destructor
25 - (void)initAddedProperties;
29 - (NSEvent *)lastMouseDownEvent;
30 - (void)setLastMouseDownEvent:(NSEvent *)event;
33 - (void)addTabViewItem:(NSTabViewItem *)item;
34 - (void)removeTabForCell:(PSMTabBarCell *)cell;
40 - (void)overflowMenuAction:(id)sender;
41 - (void)closeTabClick:(id)sender;
42 - (void)tabClick:(id)sender;
43 - (void)tabNothing:(id)sender;
44 - (void)frameDidChange:(NSNotification *)notification;
45 - (void)windowDidMove:(NSNotification *)aNotification;
46 - (void)windowStatusDidChange:(NSNotification *)notification;
49 - (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
50 - (BOOL)tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem;
51 - (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem;
52 - (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)tabView;
55 - (void)encodeWithCoder:(NSCoder *)aCoder;
56 - (id)initWithCoder:(NSCoder *)aDecoder;
59 - (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame;
60 - (unsigned)indexOfCellAtPoint:(NSPoint)point;
61 - (unsigned)indexOfCellAtPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame;
62 - (PSMTabBarCell *)lastVisibleTab;
63 - (int)numberOfVisibleTabs;
67 @implementation PSMTabBarControl
69 #pragma mark Characteristics
72 static NSBundle *bundle = nil;
73 if (!bundle) bundle = [NSBundle bundleForClass:[PSMTabBarControl class]];
77 - (float)availableCellWidth
79 float width = [self frame].size.width;
80 width = width - [style leftMarginForTabBarControl] - [style rightMarginForTabBarControl];
84 - (NSRect)genericCellRect
86 NSRect aRect=[self frame];
87 aRect.origin.x = [style leftMarginForTabBarControl];
89 aRect.size.width = [self availableCellWidth];
90 aRect.size.height = kPSMTabBarControlHeight;
95 #pragma mark Constructor/destructor
97 - (void)initAddedProperties
99 _cells = [[NSMutableArray alloc] initWithCapacity:10];
102 _allowsDragBetweenWindows = YES;
103 _delegateHandlingDrag = NO;
104 _canCloseOnlyTab = NO;
105 _showAddTabButton = NO;
106 _hideForSingleTab = NO;
107 _sizeCellsToFit = NO;
109 _hideIndicators = NO;
110 _awakenedFromNib = NO;
113 _cellOptimumWidth = 130;
114 style = [[PSMMetalTabStyle alloc] init];
116 // the overflow button/menu
117 NSRect overflowButtonRect = NSMakeRect([self frame].size.width - [style rightMarginForTabBarControl] + 1, 0, [style rightMarginForTabBarControl] - 1, [self frame].size.height);
118 _overflowPopUpButton = [[PSMOverflowPopUpButton alloc] initWithFrame:overflowButtonRect pullsDown:YES];
119 if(_overflowPopUpButton){
121 [_overflowPopUpButton setAutoresizingMask:NSViewNotSizable|NSViewMinXMargin];
125 NSRect addTabButtonRect = NSMakeRect([self frame].size.width - [style rightMarginForTabBarControl] + 1, 3.0, 16.0, 16.0);
126 _addTabButton = [[PSMRolloverButton alloc] initWithFrame:addTabButtonRect];
128 NSImage *newButtonImage = [style addTabButtonImage];
130 [_addTabButton setUsualImage:newButtonImage];
131 newButtonImage = [style addTabButtonPressedImage];
133 [_addTabButton setAlternateImage:newButtonImage];
134 newButtonImage = [style addTabButtonRolloverImage];
136 [_addTabButton setRolloverImage:newButtonImage];
137 [_addTabButton setTitle:@""];
138 [_addTabButton setImagePosition:NSImageOnly];
139 [_addTabButton setButtonType:NSMomentaryChangeButton];
140 [_addTabButton setBordered:NO];
141 [_addTabButton setBezelStyle:NSShadowlessSquareBezelStyle];
142 if(_showAddTabButton){
143 [_addTabButton setHidden:NO];
145 [_addTabButton setHidden:YES];
147 [_addTabButton setNeedsDisplay:YES];
151 - (id)initWithFrame:(NSRect)frame
153 self = [super initWithFrame:frame];
156 [self initAddedProperties];
157 [self registerForDraggedTypes:[NSArray arrayWithObjects: @"PSMTabBarControlItemPBType", nil]];
159 [self setTarget:self];
165 [_overflowPopUpButton release];
168 [_addTabButton release];
169 [partnerView release];
170 [_lastMouseDownEvent release];
174 [self unregisterDraggedTypes];
181 // build cells from existing tab view items
182 NSArray *existingItems = [tabView tabViewItems];
183 NSEnumerator *e = [existingItems objectEnumerator];
185 while(item = [e nextObject]){
186 if(![[self representedTabViewItems] containsObject:item])
187 [self addTabViewItem:item];
191 [self setPostsFrameChangedNotifications:YES];
192 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameDidChange:) name:NSViewFrameDidChangeNotification object:self];
195 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowStatusDidChange:) name:NSWindowDidBecomeKeyNotification object:[self window]];
196 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowStatusDidChange:) name:NSWindowDidResignKeyNotification object:[self window]];
197 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:[self window]];
202 #pragma mark Accessors
204 - (NSMutableArray *)cells
209 - (NSEvent *)lastMouseDownEvent
211 return _lastMouseDownEvent;
214 - (void)setLastMouseDownEvent:(NSEvent *)event
217 [_lastMouseDownEvent release];
218 _lastMouseDownEvent = event;
226 - (void)setDelegate:(id)object
233 - (NSTabView *)tabView
238 - (void)setTabView:(NSTabView *)view
245 - (id<PSMTabStyle>)style
250 - (NSString *)styleName
255 - (void)setStyleNamed:(NSString *)name
258 if([name isEqualToString:@"Aqua"]){
259 style = [[PSMAquaTabStyle alloc] init];
261 else if ([name isEqualToString:@"Unified"]){
262 style = [[PSMUnifiedTabStyle alloc] init];
265 style = [[PSMMetalTabStyle alloc] init];
268 // restyle add tab button
270 NSImage *newButtonImage = [style addTabButtonImage];
272 [_addTabButton setUsualImage:newButtonImage];
273 newButtonImage = [style addTabButtonPressedImage];
275 [_addTabButton setAlternateImage:newButtonImage];
276 newButtonImage = [style addTabButtonRolloverImage];
278 [_addTabButton setRolloverImage:newButtonImage];
284 - (BOOL)canCloseOnlyTab
286 return _canCloseOnlyTab;
289 - (void)setCanCloseOnlyTab:(BOOL)value
291 _canCloseOnlyTab = value;
292 if ([_cells count] == 1) {
297 - (BOOL)allowsDragBetweenWindows
299 return _allowsDragBetweenWindows;
302 - (void)setAllowsDragBetweenWindows:(BOOL)flag
304 _allowsDragBetweenWindows = flag;
307 - (BOOL)hideForSingleTab
309 return _hideForSingleTab;
312 - (void)setHideForSingleTab:(BOOL)value
314 _hideForSingleTab = value;
318 - (BOOL)showAddTabButton
320 return _showAddTabButton;
323 - (void)setShowAddTabButton:(BOOL)value
325 _showAddTabButton = value;
331 return _cellMinWidth;
334 - (void)setCellMinWidth:(int)value
336 _cellMinWidth = value;
342 return _cellMaxWidth;
345 - (void)setCellMaxWidth:(int)value
347 _cellMaxWidth = value;
351 - (int)cellOptimumWidth
353 return _cellOptimumWidth;
356 - (void)setCellOptimumWidth:(int)value
358 _cellOptimumWidth = value;
362 - (BOOL)sizeCellsToFit
364 return _sizeCellsToFit;
367 - (void)setSizeCellsToFit:(BOOL)value
369 _sizeCellsToFit = value;
373 - (PSMRolloverButton *)addTabButton
375 return _addTabButton;
378 - (PSMOverflowPopUpButton *)overflowPopUpButton
380 return _overflowPopUpButton;
384 #pragma mark Tool tips
386 - (void)setToolTip:(NSString *)value forTabViewItem:(NSTabViewItem *)tvi
388 int i, cellCount = [_cells count];
389 for (i = 0; i < cellCount; i++) {
390 PSMTabBarCell *cell = [_cells objectAtIndex:i];
391 if ([cell representedObject] == tvi)
392 [cell setToolTip:value];
400 #pragma mark Functionality
401 - (void)addTabViewItem:(NSTabViewItem *)item
404 PSMTabBarCell *cell = [[PSMTabBarCell alloc] initWithControlView:self];
405 [cell setRepresentedObject:item];
406 // bind the indicator to the represented object's status (if it exists)
407 [[cell indicator] setHidden:YES];
408 if([item identifier] != nil){
409 if([[item identifier] respondsToSelector:@selector(content)]){
410 if([[[[cell representedObject] identifier] content] respondsToSelector:@selector(isProcessing)]){
411 NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
412 [bindingOptions setObject:NSNegateBooleanTransformerName forKey:@"NSValueTransformerName"];
413 [[cell indicator] bind:@"animate" toObject:[item identifier] withKeyPath:@"selection.isProcessing" options:nil];
414 [[cell indicator] bind:@"hidden" toObject:[item identifier] withKeyPath:@"selection.isProcessing" options:bindingOptions];
415 [[item identifier] addObserver:self forKeyPath:@"selection.isProcessing" options:nil context:nil];
420 // bind for the existence of an icon
421 [cell setHasIcon:NO];
422 if([item identifier] != nil){
423 if([[item identifier] respondsToSelector:@selector(content)]){
424 if([[[[cell representedObject] identifier] content] respondsToSelector:@selector(icon)]){
425 NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
426 [bindingOptions setObject:NSIsNotNilTransformerName forKey:@"NSValueTransformerName"];
427 [cell bind:@"hasIcon" toObject:[item identifier] withKeyPath:@"selection.icon" options:bindingOptions];
428 [[item identifier] addObserver:self forKeyPath:@"selection.icon" options:nil context:nil];
433 // bind for the existence of a counter
435 if([item identifier] != nil){
436 if([[item identifier] respondsToSelector:@selector(content)]){
437 if([[[[cell representedObject] identifier] content] respondsToSelector:@selector(objectCount)]){
438 [cell bind:@"count" toObject:[item identifier] withKeyPath:@"selection.objectCount" options:nil];
439 [[item identifier] addObserver:self forKeyPath:@"selection.objectCount" options:nil context:nil];
444 // bind my string value to the label on the represented tab
445 [cell bind:@"title" toObject:item withKeyPath:@"label" options:nil];
448 [_cells addObject:cell];
450 if([_cells count] == [tabView numberOfTabViewItems]){
451 [self update]; // don't update unless all are accounted for!
455 - (void)removeTabForCell:(PSMTabBarCell *)cell
458 [[cell indicator] unbind:@"animate"];
459 [[cell indicator] unbind:@"hidden"];
460 [cell unbind:@"hasIcon"];
461 [cell unbind:@"title"];
462 [cell unbind:@"count"];
465 if([[self subviews] containsObject:[cell indicator]]){
466 [[cell indicator] removeFromSuperview];
469 [[NSNotificationCenter defaultCenter] removeObserver:cell];
470 if([cell closeButtonTrackingTag] != 0){
471 [self removeTrackingRect:[cell closeButtonTrackingTag]];
473 if([cell cellTrackingTag] != 0){
474 [self removeTrackingRect:[cell cellTrackingTag]];
477 // pull from collection
478 [_cells removeObject:cell];
483 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
485 // the progress indicator, label, icon, or count has changed - must redraw
490 #pragma mark Hide/Show
492 - (void)hideTabBar:(BOOL)hide animate:(BOOL)animate
494 if(!_awakenedFromNib)
496 if(_isHidden && hide)
498 if(!_isHidden && !hide)
501 [[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
502 _hideIndicators = YES;
504 NSTimer *animationTimer;
508 _currentStep = (int)kPSMHideAnimationSteps;
510 float partnerOriginalHeight, partnerOriginalY, myOriginalHeight, myOriginalY, partnerTargetHeight, partnerTargetY, myTargetHeight, myTargetY;
512 // current (original) values
513 myOriginalHeight = [self frame].size.height;
514 myOriginalY = [self frame].origin.y;
516 partnerOriginalHeight = [partnerView frame].size.height;
517 partnerOriginalY = [partnerView frame].origin.y;
519 partnerOriginalHeight = [[self window] frame].size.height;
520 partnerOriginalY = [[self window] frame].origin.y;
523 // target values for partner
525 // above or below me?
526 if((myOriginalY - 22) > partnerOriginalY){
527 // partner is below me
530 myTargetY = myOriginalY + 21;
531 myTargetHeight = myOriginalHeight - 21;
532 partnerTargetY = partnerOriginalY;
533 partnerTargetHeight = partnerOriginalHeight + 21;
536 myTargetY = myOriginalY - 21;
537 myTargetHeight = myOriginalHeight + 21;
538 partnerTargetY = partnerOriginalY;
539 partnerTargetHeight = partnerOriginalHeight - 21;
542 // partner is above me
545 myTargetY = myOriginalY;
546 myTargetHeight = myOriginalHeight - 21;
547 partnerTargetY = partnerOriginalY - 21;
548 partnerTargetHeight = partnerOriginalHeight + 21;
551 myTargetY = myOriginalY;
552 myTargetHeight = myOriginalHeight + 21;
553 partnerTargetY = partnerOriginalY + 21;
554 partnerTargetHeight = partnerOriginalHeight - 21;
558 // for window movement
561 myTargetY = myOriginalY;
562 myTargetHeight = myOriginalHeight - 21;
563 partnerTargetY = partnerOriginalY + 21;
564 partnerTargetHeight = partnerOriginalHeight - 21;
567 myTargetY = myOriginalY;
568 myTargetHeight = myOriginalHeight + 21;
569 partnerTargetY = partnerOriginalY - 21;
570 partnerTargetHeight = partnerOriginalHeight + 21;
574 NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat:myOriginalY], @"myOriginalY", [NSNumber numberWithFloat:partnerOriginalY], @"partnerOriginalY", [NSNumber numberWithFloat:myOriginalHeight], @"myOriginalHeight", [NSNumber numberWithFloat:partnerOriginalHeight], @"partnerOriginalHeight", [NSNumber numberWithFloat:myTargetY], @"myTargetY", [NSNumber numberWithFloat:partnerTargetY], @"partnerTargetY", [NSNumber numberWithFloat:myTargetHeight], @"myTargetHeight", [NSNumber numberWithFloat:partnerTargetHeight], @"partnerTargetHeight", nil];
575 animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0/20.0) target:self selector:@selector(animateShowHide:) userInfo:userInfo repeats:YES];
578 - (void)animateShowHide:(NSTimer *)timer
580 // moves the frame of the tab bar and window (or partner view) linearly to hide or show the tab bar
581 NSRect myFrame = [self frame];
582 float myCurrentY = ([[[timer userInfo] objectForKey:@"myOriginalY"] floatValue] + (([[[timer userInfo] objectForKey:@"myTargetY"] floatValue] - [[[timer userInfo] objectForKey:@"myOriginalY"] floatValue]) * (_currentStep/kPSMHideAnimationSteps)));
583 float myCurrentHeight = ([[[timer userInfo] objectForKey:@"myOriginalHeight"] floatValue] + (([[[timer userInfo] objectForKey:@"myTargetHeight"] floatValue] - [[[timer userInfo] objectForKey:@"myOriginalHeight"] floatValue]) * (_currentStep/kPSMHideAnimationSteps)));
584 float partnerCurrentY = ([[[timer userInfo] objectForKey:@"partnerOriginalY"] floatValue] + (([[[timer userInfo] objectForKey:@"partnerTargetY"] floatValue] - [[[timer userInfo] objectForKey:@"partnerOriginalY"] floatValue]) * (_currentStep/kPSMHideAnimationSteps)));
585 float partnerCurrentHeight = ([[[timer userInfo] objectForKey:@"partnerOriginalHeight"] floatValue] + (([[[timer userInfo] objectForKey:@"partnerTargetHeight"] floatValue] - [[[timer userInfo] objectForKey:@"partnerOriginalHeight"] floatValue]) * (_currentStep/kPSMHideAnimationSteps)));
587 NSRect myNewFrame = NSMakeRect(myFrame.origin.x, myCurrentY, myFrame.size.width, myCurrentHeight);
590 // resize self and view
591 [partnerView setFrame:NSMakeRect([partnerView frame].origin.x, partnerCurrentY, [partnerView frame].size.width, partnerCurrentHeight)];
592 [partnerView setNeedsDisplay:YES];
593 [self setFrame:myNewFrame];
595 // resize self and window
596 [[self window] setFrame:NSMakeRect([[self window] frame].origin.x, partnerCurrentY, [[self window] frame].size.width, partnerCurrentHeight) display:YES];
597 [self setFrame:myNewFrame];
602 if(_currentStep == kPSMHideAnimationSteps + 1){
604 [self viewDidEndLiveResize];
605 _hideIndicators = NO;
608 [[self window] display];
616 - (void)setPartnerView:(id)view
618 [partnerView release];
631 - (void)drawRect:(NSRect)rect
633 [style drawTabBar:self inRect:rect];
638 // abandon hope, all ye who enter here :-)
639 // this method handles all of the cell layout, and is called when something changes to require the refresh. This method is not called during drag and drop; see the PSMTabDragAssistant's calculateDragAnimationForTabBar: method, which does layout in that case.
641 // make sure all of our tabs are accounted for before updating
642 if ([tabView numberOfTabViewItems] != [_cells count]) {
646 // hide/show? (these return if already in desired state)
647 if((_hideForSingleTab) && ([_cells count] <= 1)){
648 [self hideTabBar:YES animate:YES];
650 [self hideTabBar:NO animate:YES];
653 // size all cells appropriately and create tracking rects
654 // nuke old tracking rects
655 int i, cellCount = [_cells count];
656 for(i = 0; i < cellCount; i++){
657 id cell = [_cells objectAtIndex:i];
658 [[NSNotificationCenter defaultCenter] removeObserver:cell];
659 if([cell closeButtonTrackingTag] != 0){
660 [self removeTrackingRect:[cell closeButtonTrackingTag]];
662 if([cell cellTrackingTag] != 0){
663 [self removeTrackingRect:[cell cellTrackingTag]];
667 // nuke old tool tips
668 [self removeAllToolTips];
670 // calculate number of cells to fit in control and cell widths
671 float availableWidth = [self availableCellWidth];
672 NSMutableArray *newWidths = [NSMutableArray arrayWithCapacity:cellCount];
673 int numberOfVisibleCells = 1;
674 float totalOccupiedWidth = 0.0;
675 NSMenu *overflowMenu = nil;
676 for(i = 0; i < cellCount; i++){
677 PSMTabBarCell *cell = [_cells objectAtIndex:i];
680 // supress close button?
681 if (cellCount == 1 && [self canCloseOnlyTab] == NO) {
682 [cell setCloseButtonSuppressed:YES];
684 [cell setCloseButtonSuppressed:NO];
687 // Determine cell width
689 width = [cell desiredWidthOfCell];
690 if (width > _cellMaxWidth) {
691 width = _cellMaxWidth;
694 width = _cellOptimumWidth;
698 totalOccupiedWidth += width;
699 if (totalOccupiedWidth >= availableWidth) {
700 numberOfVisibleCells = i;
702 int neededWidth = width - (totalOccupiedWidth - availableWidth);
703 // can I squeeze it in without violating min cell width?
704 int widthIfAllMin = (numberOfVisibleCells + 1) * _cellMinWidth;
706 if ((width + widthIfAllMin) <= availableWidth) {
707 // squeeze - distribute needed sacrifice among all cells
709 for(q = (i - 1); q >= 0; q--){
710 int desiredReduction = (int)neededWidth/(q+1);
711 if(([[newWidths objectAtIndex:q] floatValue] - desiredReduction) < _cellMinWidth){
712 int actualReduction = (int)[[newWidths objectAtIndex:q] floatValue] - _cellMinWidth;
713 [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithFloat:_cellMinWidth]];
714 neededWidth -= actualReduction;
716 int newCellWidth = (int)[[newWidths objectAtIndex:q] floatValue] - desiredReduction;
717 [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithFloat:newCellWidth]];
718 neededWidth -= desiredReduction;
722 int thisWidth = width - neededWidth;
723 [newWidths addObject:[NSNumber numberWithFloat:thisWidth]];
724 numberOfVisibleCells++;
726 // stretch - distribute leftover room among cells
727 int leftoverWidth = availableWidth - totalOccupiedWidth + width;
729 for(q = (i - 1); q >= 0; q--){
730 int desiredAddition = (int)leftoverWidth/(q+1);
731 int newCellWidth = (int)[[newWidths objectAtIndex:q] floatValue] + desiredAddition;
732 [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithFloat:newCellWidth]];
733 leftoverWidth -= desiredAddition;
736 break; // done assigning widths; remaining cells go in overflow menu
738 int revisedWidth = availableWidth/(i + 1);
739 if(revisedWidth >= _cellMinWidth){
741 totalOccupiedWidth = 0;
742 for(q = 0; q < [newWidths count]; q++){
743 [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithFloat:revisedWidth]];
744 totalOccupiedWidth += revisedWidth;
746 // just squeezed this one in...
747 [newWidths addObject:[NSNumber numberWithFloat:revisedWidth]];
748 totalOccupiedWidth += revisedWidth;
749 numberOfVisibleCells++;
751 // couldn't fit that last one...
756 numberOfVisibleCells = cellCount;
757 [newWidths addObject:[NSNumber numberWithFloat:width]];
761 // Set up cells with frames and rects
762 NSRect cellRect = [self genericCellRect];
763 for(i = 0; i < cellCount; i++){
764 PSMTabBarCell *cell = [_cells objectAtIndex:i];
765 NSTabViewItem *tvi = [cell representedObject];
767 if (i < numberOfVisibleCells) {
769 cellRect.size.width = [[newWidths objectAtIndex:i] floatValue];
770 [cell setFrame:cellRect];
771 NSTrackingRectTag tag;
773 // close button tracking rect
774 if ([cell hasCloseButton]) {
775 tag = [self addTrackingRect:[cell closeButtonRectForFrame:cellRect] owner:cell userData:nil assumeInside:NO];
776 [cell setCloseButtonTrackingTag:tag];
779 // entire tab tracking rect
780 tag = [self addTrackingRect:cellRect owner:cell userData:nil assumeInside:NO];
781 [cell setCellTrackingTag:tag];
782 [cell setEnabled:YES];
785 NSString *tt = [cell toolTip];
786 if (tt && [tt length] > 0)
787 [self addToolTipRect:cellRect owner:tt userData:NULL];
789 // selected? set tab states...
790 if([tvi isEqualTo:[tabView selectedTabViewItem]]){
791 [cell setState:NSOnState];
792 tabState |= PSMTab_SelectedMask;
795 [[_cells objectAtIndex:i-1] setTabState:([(PSMTabBarCell *)[_cells objectAtIndex:i-1] tabState] | PSMTab_RightIsSelectedMask)];
797 // next cell - see below
799 [cell setState:NSOffState];
800 // see if prev cell was selected
802 if([[_cells objectAtIndex:i-1] state] == NSOnState){
803 tabState |= PSMTab_LeftIsSelectedMask;
809 tabState |= PSMTab_PositionLeftMask | PSMTab_PositionRightMask | PSMTab_PositionSingleMask;
811 tabState |= PSMTab_PositionLeftMask;
812 } else if(i-1 == cellCount){
813 tabState |= PSMTab_PositionRightMask;
815 [cell setTabState:tabState];
816 [cell setIsInOverflowMenu:NO];
819 if(![[cell indicator] isHidden] && !_hideIndicators){
820 [[cell indicator] setFrame:[cell indicatorRectForFrame:cellRect]];
821 if(![[self subviews] containsObject:[cell indicator]]){
822 [self addSubview:[cell indicator]];
823 [[cell indicator] startAnimation:self];
828 cellRect.origin.x += [[newWidths objectAtIndex:i] floatValue];
832 NSMenuItem *menuItem;
833 if(overflowMenu == nil){
834 overflowMenu = [[[NSMenu alloc] initWithTitle:@"TITLE"] autorelease];
835 [overflowMenu insertItemWithTitle:@"FIRST" action:nil keyEquivalent:@"" atIndex:0]; // Because the overflowPupUpButton is a pull down menu
837 menuItem = [[[NSMenuItem alloc] initWithTitle:[[cell attributedStringValue] string] action:@selector(overflowMenuAction:) keyEquivalent:@""] autorelease];
838 [menuItem setTarget:self];
839 [menuItem setRepresentedObject:tvi];
840 [cell setIsInOverflowMenu:YES];
841 [[cell indicator] removeFromSuperview];
842 if ([tvi isEqualTo:[tabView selectedTabViewItem]])
843 [menuItem setState:NSOnState];
845 [menuItem setImage:[[[tvi identifier] content] icon]];
847 [menuItem setTitle:[[menuItem title] stringByAppendingFormat:@" (%d)",[cell count]]];
848 [overflowMenu addItem:menuItem];
854 cellRect.origin.y = 0;
855 cellRect.size.height = kPSMTabBarControlHeight;
856 cellRect.size.width = [style rightMarginForTabBarControl];
858 cellRect.origin.x = [self frame].size.width - [style rightMarginForTabBarControl] + 1;
859 if(![[self subviews] containsObject:_overflowPopUpButton]){
860 [self addSubview:_overflowPopUpButton];
862 [_overflowPopUpButton setFrame:cellRect];
863 [_overflowPopUpButton setMenu:overflowMenu];
864 if ([_overflowPopUpButton isHidden]) [_overflowPopUpButton setHidden:NO];
866 if (![_overflowPopUpButton isHidden]) [_overflowPopUpButton setHidden:YES];
870 if(!overflowMenu && _showAddTabButton){
871 if(![[self subviews] containsObject:_addTabButton])
872 [self addSubview:_addTabButton];
873 if([_addTabButton isHidden] && _showAddTabButton)
874 [_addTabButton setHidden:NO];
875 cellRect.size = [_addTabButton frame].size;
876 cellRect.origin.y = MARGIN_Y;
877 cellRect.origin.x += 2;
878 [_addTabButton setImage:[style addTabButtonImage]];
879 [_addTabButton setFrame:cellRect];
880 [_addTabButton setNeedsDisplay:YES];
882 [_addTabButton setHidden:YES];
883 [_addTabButton setNeedsDisplay:YES];
886 [self setNeedsDisplay:YES];
890 #pragma mark Mouse Tracking
892 - (BOOL)mouseDownCanMoveWindow
897 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
902 - (void)mouseDown:(NSEvent *)theEvent
905 [self setLastMouseDownEvent:theEvent];
907 NSPoint mousePt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
909 PSMTabBarCell *cell = [self cellForPoint:mousePt cellFrame:&cellFrame];
912 NSRect iconRect = [cell closeButtonRectForFrame:cellFrame];
913 if(NSMouseInRect(mousePt, iconRect,[self isFlipped])){
914 [cell setCloseButtonPressed:YES];
916 [cell setCloseButtonPressed:NO];
918 [self setNeedsDisplay:YES];
920 // HACK! Let the tabs react on the mouse down instead of mouse up
921 NSRect iconRect = [cell closeButtonRectForFrame:cellFrame];
922 if((NSMouseInRect(mousePt, iconRect,[self isFlipped]))){
923 //[self performSelector:@selector(closeTabClick:) withObject:cell];
924 [self closeTabClick:cell];
925 } else if(NSMouseInRect(mousePt, cellFrame,[self isFlipped])){
926 //[self performSelector:@selector(tabClick:) withObject:cell];
927 [self tabClick:cell];
929 //[self performSelector:@selector(tabNothing:) withObject:cell];
930 [self tabNothing:cell];
936 - (void)mouseDragged:(NSEvent *)theEvent
938 if([self lastMouseDownEvent] == nil){
942 if ([_cells count] < 2) {
947 NSPoint trackingStartPoint = [self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil];
948 PSMTabBarCell *cell = [self cellForPoint:trackingStartPoint cellFrame:&cellFrame];
952 NSPoint currentPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
953 float dx = fabs(currentPoint.x - trackingStartPoint.x);
954 float dy = fabs(currentPoint.y - trackingStartPoint.y);
955 float distance = sqrt(dx * dx + dy * dy);
959 if(![[PSMTabDragAssistant sharedDragAssistant] isDragging]) {
960 [[PSMTabDragAssistant sharedDragAssistant] startDraggingCell:cell fromTabBar:self withMouseDownEvent:[self lastMouseDownEvent]];
964 - (void)mouseUp:(NSEvent *)theEvent
966 #if 0 // HACK! Tabs react on mouse down instead of mouse up
968 NSPoint mousePt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
969 NSRect cellFrame, mouseDownCellFrame;
970 PSMTabBarCell *cell = [self cellForPoint:mousePt cellFrame:&cellFrame];
971 PSMTabBarCell *mouseDownCell = [self cellForPoint:[self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil] cellFrame:&mouseDownCellFrame];
973 NSRect iconRect = [mouseDownCell closeButtonRectForFrame:mouseDownCellFrame];
974 if((NSMouseInRect(mousePt, iconRect,[self isFlipped])) && [mouseDownCell closeButtonPressed]){
975 [self performSelector:@selector(closeTabClick:) withObject:cell];
976 } else if(NSMouseInRect(mousePt, mouseDownCellFrame,[self isFlipped])){
977 [mouseDownCell setCloseButtonPressed:NO];
978 [self performSelector:@selector(tabClick:) withObject:cell];
980 [mouseDownCell setCloseButtonPressed:NO];
981 [self performSelector:@selector(tabNothing:) withObject:cell];
988 #pragma mark Drag and Drop
990 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)theEvent
996 - (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
998 return (isLocal ? NSDragOperationMove : NSDragOperationNone);
1001 - (BOOL)ignoreModifierKeysWhileDragging
1006 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
1008 [[PSMTabDragAssistant sharedDragAssistant] draggedImageEndedAt:aPoint operation:operation];
1011 // NSDraggingDestination
1012 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1014 NSPoint point = [self convertPoint:[sender draggingLocation] fromView:nil];
1015 _delegateHandlingDrag = NO;
1016 if([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
1018 if ([sender draggingSource] != self && ![self allowsDragBetweenWindows])
1019 return NSDragOperationNone;
1021 [[PSMTabDragAssistant sharedDragAssistant] draggingEnteredTabBar:self atPoint:point];
1022 return NSDragOperationMove;
1023 } else if (delegate && [delegate respondsToSelector:@selector(tabBarControl:draggingEntered:forTabAtIndex:)]) {
1024 NSDragOperation op = [delegate tabBarControl:self draggingEntered:sender forTabAtIndex:[self indexOfCellAtPoint:point]];
1025 _delegateHandlingDrag = (op != NSDragOperationNone);
1026 _delegateInitialDragOperation = op;
1030 return NSDragOperationNone;
1033 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1035 NSPoint point = [self convertPoint:[sender draggingLocation] fromView:nil];
1036 if ([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
1038 if ([sender draggingSource] != self && ![self allowsDragBetweenWindows])
1039 return NSDragOperationNone;
1041 [[PSMTabDragAssistant sharedDragAssistant] draggingUpdatedInTabBar:self atPoint:point];
1042 return NSDragOperationMove;
1043 } else if (_delegateHandlingDrag) {
1044 if ([delegate respondsToSelector:@selector(tabBarControl:draggingUpdated:forTabAtIndex:)])
1045 return [delegate tabBarControl:self draggingUpdated:sender forTabAtIndex:[self indexOfCellAtPoint:point]];
1047 return _delegateInitialDragOperation;
1050 return NSDragOperationNone;
1053 - (void)draggingExited:(id <NSDraggingInfo>)sender
1055 if (!_delegateHandlingDrag) {
1056 [[PSMTabDragAssistant sharedDragAssistant] draggingExitedTabBar:self];
1057 } else if ([delegate respondsToSelector:@selector(tabBarControl:draggingExited:forTabAtIndex:)]) {
1058 NSPoint point = [self convertPoint:[sender draggingLocation] fromView:nil];
1059 [delegate tabBarControl:self draggingExited:sender forTabAtIndex:[self indexOfCellAtPoint:point]];
1063 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
1065 if (_delegateHandlingDrag && [delegate respondsToSelector:@selector(tabBarControl:prepareForDragOperation:forTabAtIndex:)]) {
1066 NSPoint point = [self convertPoint:[sender draggingLocation] fromView:nil];
1067 return [delegate tabBarControl:self prepareForDragOperation:sender forTabAtIndex:[self indexOfCellAtPoint:point]];
1073 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1075 if (!_delegateHandlingDrag) {
1077 // HACK! Used below.
1078 NSTabViewItem *tvi = [[[PSMTabDragAssistant sharedDragAssistant] draggedCell] representedObject];
1081 [[PSMTabDragAssistant sharedDragAssistant] performDragOperation];
1084 // HACK! Notify the delegate that a tab was dragged to a new position.
1085 if (delegate && [delegate respondsToSelector:@selector(tabView:didDragTabViewItem:toIndex:)]) {
1086 int idx = [[self representedTabViewItems] indexOfObject:tvi];
1087 if (NSNotFound != idx) {
1088 [delegate tabView:[self tabView] didDragTabViewItem:tvi toIndex:idx];
1093 if ([delegate respondsToSelector:@selector(tabBarControl:performDragOperation:forTabAtIndex:)]) {
1094 NSPoint point = [self convertPoint:[sender draggingLocation] fromView:nil];
1095 return [delegate tabBarControl:self performDragOperation:sender forTabAtIndex:[self indexOfCellAtPoint:point]];
1104 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
1106 if (_delegateHandlingDrag && [delegate respondsToSelector:@selector(tabBarControl:concludeDragOperation:forTabAtIndex:)]) {
1107 NSPoint point = [self convertPoint:[sender draggingLocation] fromView:nil];
1108 [delegate tabBarControl:self concludeDragOperation:sender forTabAtIndex:[self indexOfCellAtPoint:point]];
1113 #pragma mark Actions
1115 - (void)overflowMenuAction:(id)sender
1117 [tabView selectTabViewItem:[sender representedObject]];
1121 - (void)closeTabClick:(id)sender
1124 if(([_cells count] == 1) && (![self canCloseOnlyTab]))
1127 if(([self delegate]) && ([[self delegate] respondsToSelector:@selector(tabView:shouldCloseTabViewItem:)])){
1128 if(![[self delegate] tabView:tabView shouldCloseTabViewItem:[sender representedObject]]){
1129 // fix mouse downed close button
1130 [sender setCloseButtonPressed:NO];
1135 if(([self delegate]) && ([[self delegate] respondsToSelector:@selector(tabView:willCloseTabViewItem:)])){
1136 [[self delegate] tabView:tabView willCloseTabViewItem:[sender representedObject]];
1139 [[sender representedObject] retain];
1140 [tabView removeTabViewItem:[sender representedObject]];
1142 if(([self delegate]) && ([[self delegate] respondsToSelector:@selector(tabView:didCloseTabViewItem:)])){
1143 [[self delegate] tabView:tabView didCloseTabViewItem:[sender representedObject]];
1145 [[sender representedObject] release];
1149 - (void)tabClick:(id)sender
1151 [tabView selectTabViewItem:[sender representedObject]];
1155 - (void)tabNothing:(id)sender
1157 [self update]; // takes care of highlighting based on state
1160 - (void)frameDidChange:(NSNotification *)notification
1163 // trying to address the drawing artifacts for the progress indicators - hackery follows
1164 // this one fixes the "blanking" effect when the control hides and shows itself
1165 NSEnumerator *e = [_cells objectEnumerator];
1166 PSMTabBarCell *cell;
1167 while(cell = [e nextObject]){
1168 [[cell indicator] stopAnimation:self];
1169 [[cell indicator] startAnimation:self];
1171 [self setNeedsDisplay:YES];
1174 - (void)viewWillStartLiveResize
1176 NSEnumerator *e = [_cells objectEnumerator];
1177 PSMTabBarCell *cell;
1178 while(cell = [e nextObject]){
1179 [[cell indicator] stopAnimation:self];
1181 [self setNeedsDisplay:YES];
1184 -(void)viewDidEndLiveResize
1186 NSEnumerator *e = [_cells objectEnumerator];
1187 PSMTabBarCell *cell;
1188 while(cell = [e nextObject]){
1189 [[cell indicator] startAnimation:self];
1191 [self setNeedsDisplay:YES];
1194 - (void)windowDidMove:(NSNotification *)aNotification
1196 [self setNeedsDisplay:YES];
1199 - (void)windowStatusDidChange:(NSNotification *)notification
1201 // hide? must readjust things if I'm not supposed to be showing
1202 // this block of code only runs when the app launches
1203 if(_hideForSingleTab && ([_cells count] <= 1) && !_awakenedFromNib){
1204 // must adjust frames now before display
1205 NSRect myFrame = [self frame];
1207 NSRect partnerFrame = [partnerView frame];
1208 // above or below me?
1209 if(([self frame].origin.y - 22) > [partnerView frame].origin.y){
1210 // partner is below me
1211 [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y + 21, myFrame.size.width, myFrame.size.height - 21)];
1212 [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y, partnerFrame.size.width, partnerFrame.size.height + 21)];
1214 // partner is above me
1215 [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, myFrame.size.width, myFrame.size.height - 21)];
1216 [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y - 21, partnerFrame.size.width, partnerFrame.size.height + 21)];
1218 [partnerView setNeedsDisplay:YES];
1219 [self setNeedsDisplay:YES];
1221 // for window movement
1222 NSRect windowFrame = [[self window] frame];
1223 [[self window] setFrame:NSMakeRect(windowFrame.origin.x, windowFrame.origin.y + 21, windowFrame.size.width, windowFrame.size.height - 21) display:YES];
1224 [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, myFrame.size.width, myFrame.size.height - 21)];
1227 [self setNeedsDisplay:YES];
1228 //[[self window] display];
1230 _awakenedFromNib = YES;
1235 #pragma mark NSTabView Delegate
1237 - (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
1239 // here's a weird one - this message is sent before the "tabViewDidChangeNumberOfTabViewItems"
1240 // message, thus I can end up updating when there are no cells, if no tabs were (yet) present
1241 if([_cells count] > 0){
1244 if([self delegate]){
1245 if([[self delegate] respondsToSelector:@selector(tabView:didSelectTabViewItem:)]){
1246 [[self delegate] performSelector:@selector(tabView:didSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
1251 - (BOOL)tabView:(NSTabView *)aTabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem
1253 if([self delegate]){
1254 if([[self delegate] respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)]){
1255 return (int)[[self delegate] performSelector:@selector(tabView:shouldSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
1263 - (void)tabView:(NSTabView *)aTabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem
1265 if([self delegate]){
1266 if([[self delegate] respondsToSelector:@selector(tabView:willSelectTabViewItem:)]){
1267 [[self delegate] performSelector:@selector(tabView:willSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
1272 - (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)aTabView
1274 NSArray *tabItems = [tabView tabViewItems];
1275 // go through cells, remove any whose representedObjects are not in [tabView tabViewItems]
1276 NSEnumerator *e = [_cells objectEnumerator];
1277 PSMTabBarCell *cell;
1278 while(cell = [e nextObject]){
1279 if(![tabItems containsObject:[cell representedObject]]){
1280 [self removeTabForCell:cell];
1284 // go through tab view items, add cell for any not present
1285 NSMutableArray *cellItems = [self representedTabViewItems];
1286 NSEnumerator *ex = [tabItems objectEnumerator];
1287 NSTabViewItem *item;
1288 while(item = [ex nextObject]){
1289 if(![cellItems containsObject:item]){
1290 [self addTabViewItem:item];
1295 // HACK! Make sure '_cells' is ordered the same as 'tabItems'.
1296 NSMutableArray *temp = [[NSMutableArray alloc] initWithArray:_cells];
1297 e = [_cells objectEnumerator];
1298 int count = [temp count];
1299 while ((cell = [e nextObject])) {
1300 int idx = [tabItems indexOfObject:[cell representedObject]];
1301 if (NSNotFound != idx && idx < count) {
1302 [temp replaceObjectAtIndex:idx withObject:cell];
1309 if ([_cells count] == [tabView numberOfTabViewItems]) {
1310 [self update]; // don't update unless all are accounted for!
1314 // pass along for other delegate responses
1315 if([self delegate]){
1316 if([[self delegate] respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)]){
1317 [[self delegate] performSelector:@selector(tabViewDidChangeNumberOfTabViewItems:) withObject:aTabView];
1323 #pragma mark Archiving
1325 - (void)encodeWithCoder:(NSCoder *)aCoder
1327 [super encodeWithCoder:aCoder];
1328 if ([aCoder allowsKeyedCoding]) {
1329 [aCoder encodeObject:_cells forKey:@"PSMcells"];
1330 [aCoder encodeObject:tabView forKey:@"PSMtabView"];
1331 [aCoder encodeObject:_overflowPopUpButton forKey:@"PSMoverflowPopUpButton"];
1332 [aCoder encodeObject:_addTabButton forKey:@"PSMaddTabButton"];
1333 [aCoder encodeObject:style forKey:@"PSMstyle"];
1334 [aCoder encodeBool:_canCloseOnlyTab forKey:@"PSMcanCloseOnlyTab"];
1335 [aCoder encodeBool:_hideForSingleTab forKey:@"PSMhideForSingleTab"];
1336 [aCoder encodeBool:_showAddTabButton forKey:@"PSMshowAddTabButton"];
1337 [aCoder encodeBool:_sizeCellsToFit forKey:@"PSMsizeCellsToFit"];
1338 [aCoder encodeInt:_cellMinWidth forKey:@"PSMcellMinWidth"];
1339 [aCoder encodeInt:_cellMaxWidth forKey:@"PSMcellMaxWidth"];
1340 [aCoder encodeInt:_cellOptimumWidth forKey:@"PSMcellOptimumWidth"];
1341 [aCoder encodeInt:_currentStep forKey:@"PSMcurrentStep"];
1342 [aCoder encodeBool:_isHidden forKey:@"PSMisHidden"];
1343 [aCoder encodeBool:_hideIndicators forKey:@"PSMhideIndicators"];
1344 [aCoder encodeObject:partnerView forKey:@"PSMpartnerView"];
1345 [aCoder encodeBool:_awakenedFromNib forKey:@"PSMawakenedFromNib"];
1346 [aCoder encodeObject:_lastMouseDownEvent forKey:@"PSMlastMouseDownEvent"];
1347 [aCoder encodeObject:delegate forKey:@"PSMdelegate"];
1352 - (id)initWithCoder:(NSCoder *)aDecoder
1354 self = [super initWithCoder:aDecoder];
1356 if ([aDecoder allowsKeyedCoding]) {
1357 _cells = [[aDecoder decodeObjectForKey:@"PSMcells"] retain];
1358 tabView = [[aDecoder decodeObjectForKey:@"PSMtabView"] retain];
1359 _overflowPopUpButton = [[aDecoder decodeObjectForKey:@"PSMoverflowPopUpButton"] retain];
1360 _addTabButton = [[aDecoder decodeObjectForKey:@"PSMaddTabButton"] retain];
1361 style = [[aDecoder decodeObjectForKey:@"PSMstyle"] retain];
1362 _canCloseOnlyTab = [aDecoder decodeBoolForKey:@"PSMcanCloseOnlyTab"];
1363 _hideForSingleTab = [aDecoder decodeBoolForKey:@"PSMhideForSingleTab"];
1364 _showAddTabButton = [aDecoder decodeBoolForKey:@"PSMshowAddTabButton"];
1365 _sizeCellsToFit = [aDecoder decodeBoolForKey:@"PSMsizeCellsToFit"];
1366 _cellMinWidth = [aDecoder decodeIntForKey:@"PSMcellMinWidth"];
1367 _cellMaxWidth = [aDecoder decodeIntForKey:@"PSMcellMaxWidth"];
1368 _cellOptimumWidth = [aDecoder decodeIntForKey:@"PSMcellOptimumWidth"];
1369 _currentStep = [aDecoder decodeIntForKey:@"PSMcurrentStep"];
1370 _isHidden = [aDecoder decodeBoolForKey:@"PSMisHidden"];
1371 _hideIndicators = [aDecoder decodeBoolForKey:@"PSMhideIndicators"];
1372 partnerView = [[aDecoder decodeObjectForKey:@"PSMpartnerView"] retain];
1373 _awakenedFromNib = [aDecoder decodeBoolForKey:@"PSMawakenedFromNib"];
1374 _lastMouseDownEvent = [[aDecoder decodeObjectForKey:@"PSMlastMouseDownEvent"] retain];
1375 delegate = [[aDecoder decodeObjectForKey:@"PSMdelegate"] retain];
1382 #pragma mark IB Palette
1384 - (NSSize)minimumFrameSizeFromKnobPosition:(int)position
1386 return NSMakeSize(100.0, 22.0);
1389 - (NSSize)maximumFrameSizeFromKnobPosition:(int)knobPosition
1391 return NSMakeSize(10000.0, 22.0);
1394 - (void)placeView:(NSRect)newFrame
1396 // this is called any time the view is resized in IB
1397 [self setFrame:newFrame];
1402 #pragma mark Convenience
1404 - (NSMutableArray *)representedTabViewItems
1406 NSMutableArray *temp = [NSMutableArray arrayWithCapacity:[_cells count]];
1407 NSEnumerator *e = [_cells objectEnumerator];
1408 PSMTabBarCell *cell;
1409 while(cell = [e nextObject]){
1410 [temp addObject:[cell representedObject]];
1415 - (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame
1417 unsigned i = [self indexOfCellAtPoint:point cellFrame:outFrame];
1418 if (i == NSNotFound)
1420 PSMTabBarCell *cell = [_cells objectAtIndex:i];
1424 - (unsigned)indexOfCellAtPoint:(NSPoint)point
1426 return [self indexOfCellAtPoint:point cellFrame:NULL];
1429 - (unsigned)indexOfCellAtPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame
1431 NSRect aRect = [self genericCellRect];
1433 if(!NSPointInRect(point,aRect)){
1437 int i, cnt = [_cells count];
1438 for(i = 0; i < cnt; i++){
1439 PSMTabBarCell *cell = [_cells objectAtIndex:i];
1440 float width = [cell width];
1441 aRect.size.width = width;
1443 if(NSPointInRect(point, aRect)){
1449 aRect.origin.x += width;
1454 - (PSMTabBarCell *)lastVisibleTab
1456 int i, cellCount = [_cells count];
1457 for(i = 0; i < cellCount; i++){
1458 if([[_cells objectAtIndex:i] isInOverflowMenu])
1459 return [_cells objectAtIndex:(i-1)];
1461 return [_cells objectAtIndex:(cellCount - 1)];
1464 - (int)numberOfVisibleTabs
1466 int i, cellCount = [_cells count];
1467 for(i = 0; i < cellCount; i++){
1468 if([[_cells objectAtIndex:i] isInOverflowMenu])