Moved MacVim project to src/MacVim and removed runtime folder
[MacVim.git] / src / MacVim / PSMTabBarControl / source / PSMTabBarControl.m
blobdf4c6aed51111293e58d6475757c7cf5ad3e0c50
1 //
2 //  PSMTabBarControl.m
3 //  PSMTabBarControl
4 //
5 //  Created by John Pannell on 10/13/05.
6 //  Copyright 2005 Positive Spin Media. All rights reserved.
7 //
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)
20 // characteristics
21 - (float)availableCellWidth;
22 - (NSRect)genericCellRect;
24     // constructor/destructor
25 - (void)initAddedProperties;
26 - (void)dealloc;
28     // accessors
29 - (NSEvent *)lastMouseDownEvent;
30 - (void)setLastMouseDownEvent:(NSEvent *)event;
32     // contents
33 - (void)addTabViewItem:(NSTabViewItem *)item;
34 - (void)removeTabForCell:(PSMTabBarCell *)cell;
36     // draw
37 - (void)update;
39     // actions
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;
48     // NSTabView delegate
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;
54     // archiving
55 - (void)encodeWithCoder:(NSCoder *)aCoder;
56 - (id)initWithCoder:(NSCoder *)aDecoder;
58     // convenience
59 - (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame;
60 - (PSMTabBarCell *)lastVisibleTab;
61 - (int)numberOfVisibleTabs;
63 @end
65 @implementation PSMTabBarControl
66 #pragma mark -
67 #pragma mark Characteristics
68 + (NSBundle *)bundle;
70     static NSBundle *bundle = nil;
71     if (!bundle) bundle = [NSBundle bundleForClass:[PSMTabBarControl class]];
72     return bundle;
75 - (float)availableCellWidth
77     float width = [self frame].size.width;
78     width = width - [style leftMarginForTabBarControl] - [style rightMarginForTabBarControl];
79     return width;
82 - (NSRect)genericCellRect
84     NSRect aRect=[self frame];
85     aRect.origin.x = [style leftMarginForTabBarControl];
86     aRect.origin.y = 0.0;
87     aRect.size.width = [self availableCellWidth];
88     aRect.size.height = kPSMTabBarControlHeight;
89     return aRect;
92 #pragma mark -
93 #pragma mark Constructor/destructor
95 - (void)initAddedProperties
97     _cells = [[NSMutableArray alloc] initWithCapacity:10];
98     
99     // default config
100     _allowsDragBetweenWindows = YES;
101     _canCloseOnlyTab = NO;
102     _showAddTabButton = NO;
103     _hideForSingleTab = NO;
104     _sizeCellsToFit = NO;
105     _isHidden = NO;
106     _hideIndicators = NO;
107     _awakenedFromNib = NO;
108     _cellMinWidth = 100;
109     _cellMaxWidth = 280;
110     _cellOptimumWidth = 130;
111     style = [[PSMMetalTabStyle alloc] init];
112     
113     // the overflow button/menu
114     NSRect overflowButtonRect = NSMakeRect([self frame].size.width - [style rightMarginForTabBarControl] + 1, 0, [style rightMarginForTabBarControl] - 1, [self frame].size.height);
115     _overflowPopUpButton = [[PSMOverflowPopUpButton alloc] initWithFrame:overflowButtonRect pullsDown:YES];
116     if(_overflowPopUpButton){
117         // configure
118         [_overflowPopUpButton setAutoresizingMask:NSViewNotSizable|NSViewMinXMargin];
119     }
120     
121     // new tab button
122     NSRect addTabButtonRect = NSMakeRect([self frame].size.width - [style rightMarginForTabBarControl] + 1, 3.0, 16.0, 16.0);
123     _addTabButton = [[PSMRolloverButton alloc] initWithFrame:addTabButtonRect];
124     if(_addTabButton){
125         NSImage *newButtonImage = [style addTabButtonImage];
126         if(newButtonImage)
127             [_addTabButton setUsualImage:newButtonImage];
128         newButtonImage = [style addTabButtonPressedImage];
129         if(newButtonImage)
130             [_addTabButton setAlternateImage:newButtonImage];
131         newButtonImage = [style addTabButtonRolloverImage];
132         if(newButtonImage)
133             [_addTabButton setRolloverImage:newButtonImage];
134         [_addTabButton setTitle:@""];
135         [_addTabButton setImagePosition:NSImageOnly];
136         [_addTabButton setButtonType:NSMomentaryChangeButton];
137         [_addTabButton setBordered:NO];
138         [_addTabButton setBezelStyle:NSShadowlessSquareBezelStyle];
139         if(_showAddTabButton){
140             [_addTabButton setHidden:NO];
141         } else {
142             [_addTabButton setHidden:YES];
143         }
144         [_addTabButton setNeedsDisplay:YES];
145     }
147     
148 - (id)initWithFrame:(NSRect)frame
150     self = [super initWithFrame:frame];
151     if (self) {
152         // Initialization
153         [self initAddedProperties];
154         [self registerForDraggedTypes:[NSArray arrayWithObjects: @"PSMTabBarControlItemPBType", nil]];
155     }
156     [self setTarget:self];
157     return self;
160 - (void)dealloc
162     [_overflowPopUpButton release];
163     [_cells release];
164     [tabView release];
165     [_addTabButton release];
166     [partnerView release];
167     [_lastMouseDownEvent release];
168     [style release];
169     [delegate release];
170     
171     [self unregisterDraggedTypes];
172     
173     [super dealloc];
176 - (void)awakeFromNib
178     // build cells from existing tab view items
179     NSArray *existingItems = [tabView tabViewItems];
180     NSEnumerator *e = [existingItems objectEnumerator];
181     NSTabViewItem *item;
182     while(item = [e nextObject]){
183         if(![[self representedTabViewItems] containsObject:item])
184             [self addTabViewItem:item];
185     }
186     
187     // resize
188     [self setPostsFrameChangedNotifications:YES];
189     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameDidChange:) name:NSViewFrameDidChangeNotification object:self];
190     
191     // window status
192     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowStatusDidChange:) name:NSWindowDidBecomeKeyNotification object:[self window]];
193     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowStatusDidChange:) name:NSWindowDidResignKeyNotification object:[self window]];
194     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:[self window]];
198 #pragma mark -
199 #pragma mark Accessors
201 - (NSMutableArray *)cells
203     return _cells;
206 - (NSEvent *)lastMouseDownEvent
208     return _lastMouseDownEvent;
211 - (void)setLastMouseDownEvent:(NSEvent *)event
213     [event retain];
214     [_lastMouseDownEvent release];
215     _lastMouseDownEvent = event;
218 - (id)delegate
220     return delegate;
223 - (void)setDelegate:(id)object
225     [object retain];
226     [delegate release];
227     delegate = object;
230 - (NSTabView *)tabView
232     return tabView;
235 - (void)setTabView:(NSTabView *)view
237     [view retain];
238     [tabView release];
239     tabView = view;
242 - (id<PSMTabStyle>)style
244     return style;
247 - (NSString *)styleName
249     return [style name];
252 - (void)setStyleNamed:(NSString *)name
254     [style release];
255     if([name isEqualToString:@"Aqua"]){
256         style = [[PSMAquaTabStyle alloc] init];
257     }
258         else if ([name isEqualToString:@"Unified"]){
259                 style = [[PSMUnifiedTabStyle alloc] init];
260         }
261         else {
262         style = [[PSMMetalTabStyle alloc] init];
263     }
264    
265     // restyle add tab button
266     if(_addTabButton){
267         NSImage *newButtonImage = [style addTabButtonImage];
268         if(newButtonImage)
269             [_addTabButton setUsualImage:newButtonImage];
270         newButtonImage = [style addTabButtonPressedImage];
271         if(newButtonImage)
272             [_addTabButton setAlternateImage:newButtonImage];
273         newButtonImage = [style addTabButtonRolloverImage];
274         if(newButtonImage)
275             [_addTabButton setRolloverImage:newButtonImage];
276     }
277     
278     [self update];
281 - (BOOL)canCloseOnlyTab
283     return _canCloseOnlyTab;
286 - (void)setCanCloseOnlyTab:(BOOL)value
288     _canCloseOnlyTab = value;
289     if ([_cells count] == 1) {
290         [self update];
291     }
294 - (BOOL)allowsDragBetweenWindows
296         return _allowsDragBetweenWindows;
299 - (void)setAllowsDragBetweenWindows:(BOOL)flag
301         _allowsDragBetweenWindows = flag;
304 - (BOOL)hideForSingleTab
306     return _hideForSingleTab;
309 - (void)setHideForSingleTab:(BOOL)value
311     _hideForSingleTab = value;
312     [self update];
315 - (BOOL)showAddTabButton
317     return _showAddTabButton;
320 - (void)setShowAddTabButton:(BOOL)value
322     _showAddTabButton = value;
323     [self update];
326 - (int)cellMinWidth
328     return _cellMinWidth;
331 - (void)setCellMinWidth:(int)value
333     _cellMinWidth = value;
334     [self update];
337 - (int)cellMaxWidth
339     return _cellMaxWidth;
342 - (void)setCellMaxWidth:(int)value
344     _cellMaxWidth = value;
345     [self update];
348 - (int)cellOptimumWidth
350     return _cellOptimumWidth;
353 - (void)setCellOptimumWidth:(int)value
355     _cellOptimumWidth = value;
356     [self update];
359 - (BOOL)sizeCellsToFit
361     return _sizeCellsToFit;
364 - (void)setSizeCellsToFit:(BOOL)value
366     _sizeCellsToFit = value;
367     [self update];
370 - (PSMRolloverButton *)addTabButton
372     return _addTabButton;
375 - (PSMOverflowPopUpButton *)overflowPopUpButton
377     return _overflowPopUpButton;
380 #pragma mark -
381 #pragma mark Functionality
382 - (void)addTabViewItem:(NSTabViewItem *)item
384     // create cell
385     PSMTabBarCell *cell = [[PSMTabBarCell alloc] initWithControlView:self];
386     [cell setRepresentedObject:item];
387     // bind the indicator to the represented object's status (if it exists)
388     [[cell indicator] setHidden:YES];
389     if([item identifier] != nil){
390         if([[item identifier] respondsToSelector:@selector(content)]){
391             if([[[[cell representedObject] identifier] content] respondsToSelector:@selector(isProcessing)]){
392                 NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
393                 [bindingOptions setObject:NSNegateBooleanTransformerName forKey:@"NSValueTransformerName"];
394                 [[cell indicator] bind:@"animate" toObject:[item identifier] withKeyPath:@"selection.isProcessing" options:nil];
395                 [[cell indicator] bind:@"hidden" toObject:[item identifier] withKeyPath:@"selection.isProcessing" options:bindingOptions];
396                 [[item identifier] addObserver:self forKeyPath:@"selection.isProcessing" options:nil context:nil];
397             } 
398         } 
399     } 
400     
401     // bind for the existence of an icon
402     [cell setHasIcon:NO];
403     if([item identifier] != nil){
404         if([[item identifier] respondsToSelector:@selector(content)]){
405             if([[[[cell representedObject] identifier] content] respondsToSelector:@selector(icon)]){
406                 NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
407                 [bindingOptions setObject:NSIsNotNilTransformerName forKey:@"NSValueTransformerName"];
408                 [cell bind:@"hasIcon" toObject:[item identifier] withKeyPath:@"selection.icon" options:bindingOptions];
409                 [[item identifier] addObserver:self forKeyPath:@"selection.icon" options:nil context:nil];
410             } 
411         } 
412     }
413     
414     // bind for the existence of a counter
415     [cell setCount:0];
416     if([item identifier] != nil){
417         if([[item identifier] respondsToSelector:@selector(content)]){
418             if([[[[cell representedObject] identifier] content] respondsToSelector:@selector(objectCount)]){
419                 [cell bind:@"count" toObject:[item identifier] withKeyPath:@"selection.objectCount" options:nil];
420                 [[item identifier] addObserver:self forKeyPath:@"selection.objectCount" options:nil context:nil];
421             } 
422         } 
423     }
424     
425     // bind my string value to the label on the represented tab
426     [cell bind:@"title" toObject:item withKeyPath:@"label" options:nil];
427     
428     // add to collection
429     [_cells addObject:cell];
430     [cell release];
431     if([_cells count] == [tabView numberOfTabViewItems]){
432         [self update]; // don't update unless all are accounted for!
433     }
436 - (void)removeTabForCell:(PSMTabBarCell *)cell
438     // unbind
439     [[cell indicator] unbind:@"animate"];
440     [[cell indicator] unbind:@"hidden"];
441     [cell unbind:@"hasIcon"];
442     [cell unbind:@"title"];
443     [cell unbind:@"count"];
444     
445     // remove indicator
446     if([[self subviews] containsObject:[cell indicator]]){
447         [[cell indicator] removeFromSuperview];
448     }
449     // remove tracking
450     [[NSNotificationCenter defaultCenter] removeObserver:cell];
451     if([cell closeButtonTrackingTag] != 0){
452         [self removeTrackingRect:[cell closeButtonTrackingTag]];
453     }
454     if([cell cellTrackingTag] != 0){
455         [self removeTrackingRect:[cell cellTrackingTag]];
456     }
458     // pull from collection
459     [_cells removeObject:cell];
461     [self update];
464 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
466     // the progress indicator, label, icon, or count has changed - must redraw
467     [self update];
470 #pragma mark -
471 #pragma mark Hide/Show
473 - (void)hideTabBar:(BOOL)hide animate:(BOOL)animate
475     if(!_awakenedFromNib)
476         return;
477     if(_isHidden && hide)
478         return;
479     if(!_isHidden && !hide)
480         return;
481     
482     [[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
483     _hideIndicators = YES;
484     
485     NSTimer *animationTimer;
486     _isHidden = hide;
487     _currentStep = 0;
488     if(!animate)
489         _currentStep = (int)kPSMHideAnimationSteps;
490     
491     float partnerOriginalHeight, partnerOriginalY, myOriginalHeight, myOriginalY, partnerTargetHeight, partnerTargetY, myTargetHeight, myTargetY;
492     
493     // current (original) values
494     myOriginalHeight = [self frame].size.height;
495     myOriginalY = [self frame].origin.y;
496     if(partnerView){
497         partnerOriginalHeight = [partnerView frame].size.height;
498         partnerOriginalY = [partnerView frame].origin.y;
499     } else {
500         partnerOriginalHeight = [[self window] frame].size.height;
501         partnerOriginalY = [[self window] frame].origin.y;
502     }
503     
504     // target values for partner
505     if(partnerView){
506         // above or below me?
507         if((myOriginalY - 22) > partnerOriginalY){
508             // partner is below me
509             if(_isHidden){
510                 // I'm shrinking
511                 myTargetY = myOriginalY + 21;
512                 myTargetHeight = myOriginalHeight - 21;
513                 partnerTargetY = partnerOriginalY;
514                 partnerTargetHeight = partnerOriginalHeight + 21;
515             } else {
516                 // I'm growing
517                 myTargetY = myOriginalY - 21;
518                 myTargetHeight = myOriginalHeight + 21;
519                 partnerTargetY = partnerOriginalY;
520                 partnerTargetHeight = partnerOriginalHeight - 21;
521             }
522         } else {
523             // partner is above me
524             if(_isHidden){
525                 // I'm shrinking
526                 myTargetY = myOriginalY;
527                 myTargetHeight = myOriginalHeight - 21;
528                 partnerTargetY = partnerOriginalY - 21;
529                 partnerTargetHeight = partnerOriginalHeight + 21;
530             } else {
531                 // I'm growing
532                 myTargetY = myOriginalY;
533                 myTargetHeight = myOriginalHeight + 21;
534                 partnerTargetY = partnerOriginalY + 21;
535                 partnerTargetHeight = partnerOriginalHeight - 21;
536             }
537         }
538     } else {
539         // for window movement
540         if(_isHidden){
541             // I'm shrinking
542             myTargetY = myOriginalY;
543             myTargetHeight = myOriginalHeight - 21;
544             partnerTargetY = partnerOriginalY + 21;
545             partnerTargetHeight = partnerOriginalHeight - 21;
546         } else {
547             // I'm growing
548             myTargetY = myOriginalY;
549             myTargetHeight = myOriginalHeight + 21;
550             partnerTargetY = partnerOriginalY - 21;
551             partnerTargetHeight = partnerOriginalHeight + 21;
552         }
553     }
555     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];
556     animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0/20.0) target:self selector:@selector(animateShowHide:) userInfo:userInfo repeats:YES];
559 - (void)animateShowHide:(NSTimer *)timer
561     // moves the frame of the tab bar and window (or partner view) linearly to hide or show the tab bar
562     NSRect myFrame = [self frame];
563     float myCurrentY = ([[[timer userInfo] objectForKey:@"myOriginalY"] floatValue] + (([[[timer userInfo] objectForKey:@"myTargetY"] floatValue] - [[[timer userInfo] objectForKey:@"myOriginalY"] floatValue]) * (_currentStep/kPSMHideAnimationSteps)));
564     float myCurrentHeight = ([[[timer userInfo] objectForKey:@"myOriginalHeight"] floatValue] + (([[[timer userInfo] objectForKey:@"myTargetHeight"] floatValue] - [[[timer userInfo] objectForKey:@"myOriginalHeight"] floatValue]) * (_currentStep/kPSMHideAnimationSteps)));
565     float partnerCurrentY = ([[[timer userInfo] objectForKey:@"partnerOriginalY"] floatValue] + (([[[timer userInfo] objectForKey:@"partnerTargetY"] floatValue] - [[[timer userInfo] objectForKey:@"partnerOriginalY"] floatValue]) * (_currentStep/kPSMHideAnimationSteps)));
566     float partnerCurrentHeight = ([[[timer userInfo] objectForKey:@"partnerOriginalHeight"] floatValue] + (([[[timer userInfo] objectForKey:@"partnerTargetHeight"] floatValue] - [[[timer userInfo] objectForKey:@"partnerOriginalHeight"] floatValue]) * (_currentStep/kPSMHideAnimationSteps)));
567     
568     NSRect myNewFrame = NSMakeRect(myFrame.origin.x, myCurrentY, myFrame.size.width, myCurrentHeight);
569     
570     if(partnerView){
571         // resize self and view
572         [partnerView setFrame:NSMakeRect([partnerView frame].origin.x, partnerCurrentY, [partnerView frame].size.width, partnerCurrentHeight)];
573         [partnerView setNeedsDisplay:YES];
574         [self setFrame:myNewFrame];
575     } else {
576         // resize self and window
577         [[self window] setFrame:NSMakeRect([[self window] frame].origin.x, partnerCurrentY, [[self window] frame].size.width, partnerCurrentHeight) display:YES];
578         [self setFrame:myNewFrame];
579     }
580     
581     // next
582     _currentStep++;
583     if(_currentStep == kPSMHideAnimationSteps + 1){
584         [timer invalidate];
585         [self viewDidEndLiveResize];
586         _hideIndicators = NO;
587         [self update];
588     }
589     [[self window] display];
592 - (id)partnerView
594     return partnerView;
597 - (void)setPartnerView:(id)view
599     [partnerView release];
600     [view retain];
601     partnerView = view;
604 #pragma mark -
605 #pragma mark Drawing
607 - (BOOL)isFlipped
609     return YES;
612 - (void)drawRect:(NSRect)rect 
614     [style drawTabBar:self inRect:rect];
617 - (void)update
619     // abandon hope, all ye who enter here :-)
620     // 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.
621    
622     // make sure all of our tabs are accounted for before updating
623     if ([tabView numberOfTabViewItems] != [_cells count]) {
624         return;
625     }
627     // hide/show? (these return if already in desired state)
628     if((_hideForSingleTab) && ([_cells count] <= 1)){
629         [self hideTabBar:YES animate:YES];
630     } else {
631         [self hideTabBar:NO animate:YES];
632     }
634     // size all cells appropriately and create tracking rects
635     // nuke old tracking rects
636     int i, cellCount = [_cells count];
637     for(i = 0; i < cellCount; i++){
638         id cell = [_cells objectAtIndex:i];
639         [[NSNotificationCenter defaultCenter] removeObserver:cell];
640         if([cell closeButtonTrackingTag] != 0){
641             [self removeTrackingRect:[cell closeButtonTrackingTag]];
642         }
643         if([cell cellTrackingTag] != 0){
644             [self removeTrackingRect:[cell cellTrackingTag]];
645         }
646     }
647     
648     // calculate number of cells to fit in control and cell widths
649     float availableWidth = [self availableCellWidth];
650     NSMutableArray *newWidths = [NSMutableArray arrayWithCapacity:cellCount];
651     int numberOfVisibleCells = 1;
652     float totalOccupiedWidth = 0.0;
653     NSMenu *overflowMenu = nil;
654     for(i = 0; i < cellCount; i++){
655         PSMTabBarCell *cell = [_cells objectAtIndex:i];
656         float width;
657         
658         // supress close button? 
659         if (cellCount == 1 && [self canCloseOnlyTab] == NO) {
660             [cell setCloseButtonSuppressed:YES];
661         } else {
662             [cell setCloseButtonSuppressed:NO];
663         }
664         
665         // Determine cell width
666         if(_sizeCellsToFit){
667             width = [cell desiredWidthOfCell];
668             if (width > _cellMaxWidth) {
669                 width = _cellMaxWidth;
670             }
671         } else {
672             width = _cellOptimumWidth;
673         }
674         
675         // too much?
676         totalOccupiedWidth += width;
677         if (totalOccupiedWidth >= availableWidth) {
678             numberOfVisibleCells = i;
679             if(_sizeCellsToFit){
680                 int neededWidth = width - (totalOccupiedWidth - availableWidth);
681                 // can I squeeze it in without violating min cell width?
682                 int widthIfAllMin = (numberOfVisibleCells + 1) * _cellMinWidth;
683             
684                 if ((width + widthIfAllMin) <= availableWidth) {
685                     // squeeze - distribute needed sacrifice among all cells
686                     int q;
687                     for(q = (i - 1); q >= 0; q--){
688                         int desiredReduction = (int)neededWidth/(q+1);
689                         if(([[newWidths objectAtIndex:q] floatValue] - desiredReduction) < _cellMinWidth){
690                             int actualReduction = (int)[[newWidths objectAtIndex:q] floatValue] - _cellMinWidth;
691                             [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithFloat:_cellMinWidth]];
692                             neededWidth -= actualReduction;
693                         } else {
694                             int newCellWidth = (int)[[newWidths objectAtIndex:q] floatValue] - desiredReduction;
695                             [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithFloat:newCellWidth]];
696                             neededWidth -= desiredReduction;
697                         }
698                     }
699                     // one cell left!
700                     int thisWidth = width - neededWidth;
701                     [newWidths addObject:[NSNumber numberWithFloat:thisWidth]];
702                     numberOfVisibleCells++;
703                 } else {
704                     // stretch - distribute leftover room among cells
705                     int leftoverWidth = availableWidth - totalOccupiedWidth + width;
706                     int q;
707                     for(q = (i - 1); q >= 0; q--){
708                         int desiredAddition = (int)leftoverWidth/(q+1);
709                         int newCellWidth = (int)[[newWidths objectAtIndex:q] floatValue] + desiredAddition;
710                         [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithFloat:newCellWidth]];
711                         leftoverWidth -= desiredAddition;
712                     }
713                 }
714                 break; // done assigning widths; remaining cells go in overflow menu
715             } else {
716                 int revisedWidth = availableWidth/(i + 1);
717                 if(revisedWidth >= _cellMinWidth){
718                     int q;
719                     totalOccupiedWidth = 0;
720                     for(q = 0; q < [newWidths count]; q++){
721                         [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithFloat:revisedWidth]];
722                         totalOccupiedWidth += revisedWidth;
723                     }
724                     // just squeezed this one in...
725                     [newWidths addObject:[NSNumber numberWithFloat:revisedWidth]];
726                     totalOccupiedWidth += revisedWidth;
727                     numberOfVisibleCells++;
728                 } else {
729                     // couldn't fit that last one...
730                     break;
731                 }
732             }
733         } else {
734             numberOfVisibleCells = cellCount;
735             [newWidths addObject:[NSNumber numberWithFloat:width]];
736         }
737     }
739     // Set up cells with frames and rects
740     NSRect cellRect = [self genericCellRect];
741     for(i = 0; i < cellCount; i++){
742         PSMTabBarCell *cell = [_cells objectAtIndex:i];
743         int tabState = 0;
744         if (i < numberOfVisibleCells) {
745             // set cell frame
746             cellRect.size.width = [[newWidths objectAtIndex:i] floatValue];
747             [cell setFrame:cellRect];
748             NSTrackingRectTag tag;
749             
750             // close button tracking rect
751             if ([cell hasCloseButton]) {
752                 tag = [self addTrackingRect:[cell closeButtonRectForFrame:cellRect] owner:cell userData:nil assumeInside:NO];
753                 [cell setCloseButtonTrackingTag:tag];
754             }
755             
756             // entire tab tracking rect
757             tag = [self addTrackingRect:cellRect owner:cell userData:nil assumeInside:NO];
758             [cell setCellTrackingTag:tag];
759             [cell setEnabled:YES];
760             
761             // selected? set tab states...
762             if([[cell representedObject] isEqualTo:[tabView selectedTabViewItem]]){
763                 [cell setState:NSOnState];
764                 tabState |= PSMTab_SelectedMask;
765                 // previous cell
766                 if(i > 0){
767                     [[_cells objectAtIndex:i-1] setTabState:([(PSMTabBarCell *)[_cells objectAtIndex:i-1] tabState] | PSMTab_RightIsSelectedMask)];
768                 }
769                 // next cell - see below
770             } else {
771                 [cell setState:NSOffState];
772                 // see if prev cell was selected
773                 if(i > 0){
774                     if([[_cells objectAtIndex:i-1] state] == NSOnState){
775                         tabState |= PSMTab_LeftIsSelectedMask;
776                     }
777                 }
778             }
779             // more tab states
780             if(cellCount == 1){
781                 tabState |= PSMTab_PositionLeftMask | PSMTab_PositionRightMask | PSMTab_PositionSingleMask;
782             } else if(i == 0){
783                 tabState |= PSMTab_PositionLeftMask;
784             } else if(i-1 == cellCount){
785                 tabState |= PSMTab_PositionRightMask;
786             }
787             [cell setTabState:tabState];
788             [cell setIsInOverflowMenu:NO];
789             
790             // indicator
791             if(![[cell indicator] isHidden] && !_hideIndicators){
792                 [[cell indicator] setFrame:[cell indicatorRectForFrame:cellRect]];
793                 if(![[self subviews] containsObject:[cell indicator]]){
794                     [self addSubview:[cell indicator]];
795                     [[cell indicator] startAnimation:self];
796                 }
797             }
798             
799             // next...
800             cellRect.origin.x += [[newWidths objectAtIndex:i] floatValue];
801             
802         } else {
803             // set up menu items
804             NSMenuItem *menuItem;
805             if(overflowMenu == nil){
806                 overflowMenu = [[[NSMenu alloc] initWithTitle:@"TITLE"] autorelease];
807                 [overflowMenu insertItemWithTitle:@"FIRST" action:nil keyEquivalent:@"" atIndex:0]; // Because the overflowPupUpButton is a pull down menu
808             }
809             menuItem = [[[NSMenuItem alloc] initWithTitle:[[cell attributedStringValue] string] action:@selector(overflowMenuAction:) keyEquivalent:@""] autorelease];
810             [menuItem setTarget:self];
811             [menuItem setRepresentedObject:[cell representedObject]];
812             [cell setIsInOverflowMenu:YES];
813             [[cell indicator] removeFromSuperview];
814             if ([[cell representedObject] isEqualTo:[tabView selectedTabViewItem]])
815                 [menuItem setState:NSOnState];
816             if([cell hasIcon])
817                 [menuItem setImage:[[[[cell representedObject] identifier] content] icon]];
818             if([cell count] > 0)
819                 [menuItem setTitle:[[menuItem title] stringByAppendingFormat:@" (%d)",[cell count]]];
820             [overflowMenu addItem:menuItem];
821         }
822     }
823     
825     // Overflow menu
826     cellRect.origin.y = 0;
827     cellRect.size.height = kPSMTabBarControlHeight;
828     cellRect.size.width = [style rightMarginForTabBarControl];
829     if (overflowMenu) {
830         cellRect.origin.x = [self frame].size.width - [style rightMarginForTabBarControl] + 1;
831         if(![[self subviews] containsObject:_overflowPopUpButton]){
832             [self addSubview:_overflowPopUpButton];
833         }
834         [_overflowPopUpButton setFrame:cellRect];
835         [_overflowPopUpButton setMenu:overflowMenu];
836         if ([_overflowPopUpButton isHidden]) [_overflowPopUpButton setHidden:NO];
837     } else {
838         if (![_overflowPopUpButton isHidden]) [_overflowPopUpButton setHidden:YES];
839     }
840     
841     // add tab button
842     if(!overflowMenu && _showAddTabButton){
843         if(![[self subviews] containsObject:_addTabButton])
844             [self addSubview:_addTabButton];
845         if([_addTabButton isHidden] && _showAddTabButton)
846             [_addTabButton setHidden:NO];
847         cellRect.size = [_addTabButton frame].size;
848         cellRect.origin.y = MARGIN_Y;
849         cellRect.origin.x += 2;
850         [_addTabButton setImage:[style addTabButtonImage]];
851         [_addTabButton setFrame:cellRect];
852         [_addTabButton setNeedsDisplay:YES];
853     } else {
854         [_addTabButton setHidden:YES];
855         [_addTabButton setNeedsDisplay:YES];
856     }
857     
858     [self setNeedsDisplay:YES];
861 #pragma mark -
862 #pragma mark Mouse Tracking
864 - (BOOL)mouseDownCanMoveWindow
866     return NO;
869 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
871     return YES;
874 - (void)mouseDown:(NSEvent *)theEvent
876     // keep for dragging
877     [self setLastMouseDownEvent:theEvent];
878     // what cell?
879     NSPoint mousePt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
880     NSRect cellFrame;
881     PSMTabBarCell *cell = [self cellForPoint:mousePt cellFrame:&cellFrame];
882     if(cell){
883 #if 0
884         NSRect iconRect = [cell closeButtonRectForFrame:cellFrame];
885         if(NSMouseInRect(mousePt, iconRect,[self isFlipped])){
886             [cell setCloseButtonPressed:YES];
887         } else {
888             [cell setCloseButtonPressed:NO];
889         }
890         [self setNeedsDisplay:YES];
891 #else
892         // HACK!  Let the tabs react on the mouse down instead of mouse up
893         NSRect iconRect = [cell closeButtonRectForFrame:cellFrame];
894         if((NSMouseInRect(mousePt, iconRect,[self isFlipped]))){
895             //[self performSelector:@selector(closeTabClick:) withObject:cell];
896             [self closeTabClick:cell];
897         } else if(NSMouseInRect(mousePt, cellFrame,[self isFlipped])){
898             //[self performSelector:@selector(tabClick:) withObject:cell];
899             [self tabClick:cell];
900         } else {
901             //[self performSelector:@selector(tabNothing:) withObject:cell];
902             [self tabNothing:cell];
903         }
904 #endif
905     }
908 - (void)mouseDragged:(NSEvent *)theEvent
910     if([self lastMouseDownEvent] == nil){
911         return;
912     }
913     
914     if ([_cells count] < 2) {
915         return;
916     }
917     
918     NSRect cellFrame;
919     NSPoint trackingStartPoint = [self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil];
920     PSMTabBarCell *cell = [self cellForPoint:trackingStartPoint cellFrame:&cellFrame];
921     if (!cell) 
922         return;
923     
924     NSPoint currentPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
925     float dx = fabs(currentPoint.x - trackingStartPoint.x);
926     float dy = fabs(currentPoint.y - trackingStartPoint.y);
927     float distance = sqrt(dx * dx + dy * dy);
928     if (distance < 10)
929         return;
930     
931     if(![[PSMTabDragAssistant sharedDragAssistant] isDragging]) {
932         [[PSMTabDragAssistant sharedDragAssistant] startDraggingCell:cell fromTabBar:self withMouseDownEvent:[self lastMouseDownEvent]];
933     }
936 - (void)mouseUp:(NSEvent *)theEvent
938 #if 0  // HACK!  Tabs react on mouse down instead of mouse up
939     // what cell?
940     NSPoint mousePt = [self convertPoint:[theEvent locationInWindow] fromView:nil];
941     NSRect cellFrame, mouseDownCellFrame;
942     PSMTabBarCell *cell = [self cellForPoint:mousePt cellFrame:&cellFrame];
943     PSMTabBarCell *mouseDownCell = [self cellForPoint:[self convertPoint:[[self lastMouseDownEvent] locationInWindow] fromView:nil] cellFrame:&mouseDownCellFrame];
944     if(cell){
945         NSRect iconRect = [mouseDownCell closeButtonRectForFrame:mouseDownCellFrame];
946         if((NSMouseInRect(mousePt, iconRect,[self isFlipped])) && [mouseDownCell closeButtonPressed]){
947             [self performSelector:@selector(closeTabClick:) withObject:cell];
948         } else if(NSMouseInRect(mousePt, mouseDownCellFrame,[self isFlipped])){
949             [mouseDownCell setCloseButtonPressed:NO];
950             [self performSelector:@selector(tabClick:) withObject:cell];
951         } else {
952             [mouseDownCell setCloseButtonPressed:NO];
953             [self performSelector:@selector(tabNothing:) withObject:cell];
954         }
955     }
956 #endif
959 #pragma mark -
960 #pragma mark Drag and Drop
962 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)theEvent
964     return YES;
967 // NSDraggingSource
968 - (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
970     return (isLocal ? NSDragOperationMove : NSDragOperationNone);
973 - (BOOL)ignoreModifierKeysWhileDragging
975     return YES;
978 // NSDraggingDestination
979 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
980 {       
981     if([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
982                 
983                 if ([sender draggingSource] != self && ![self allowsDragBetweenWindows])
984                         return NSDragOperationNone;
985                 
986         [[PSMTabDragAssistant sharedDragAssistant] draggingEnteredTabBar:self atPoint:[self convertPoint:[sender draggingLocation] fromView:nil]];
987         return NSDragOperationMove;
988     }
989         
990     return NSDragOperationNone;
993 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
995     if ([[[sender draggingPasteboard] types] indexOfObject:@"PSMTabBarControlItemPBType"] != NSNotFound) {
996                 
997                 if ([sender draggingSource] != self && ![self allowsDragBetweenWindows])
998                         return NSDragOperationNone;
999                 
1000         [[PSMTabDragAssistant sharedDragAssistant] draggingUpdatedInTabBar:self atPoint:[self convertPoint:[sender draggingLocation] fromView:nil]];
1001         return NSDragOperationMove;
1002     }
1003         
1004     return NSDragOperationNone;
1007 - (void)draggingExited:(id <NSDraggingInfo>)sender
1009     [[PSMTabDragAssistant sharedDragAssistant] draggingExitedTabBar:self];
1012 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
1014     return YES;
1017 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1019 #if 1
1020     // HACK!  Used below.
1021     NSTabViewItem *tvi = [[[PSMTabDragAssistant sharedDragAssistant] draggedCell] representedObject];
1022 #endif
1024     [[PSMTabDragAssistant sharedDragAssistant] performDragOperation];
1026 #if 1
1027     // HACK!  Notify the delegate that a tab was dragged to a new position.
1028     if (delegate && [delegate respondsToSelector:@selector(tabView:didDragTabViewItem:toIndex:)]) {
1029         int idx = [[self representedTabViewItems] indexOfObject:tvi];
1030         if (NSNotFound != idx) {
1031             [delegate tabView:[self tabView] didDragTabViewItem:tvi toIndex:idx];
1032         }
1033     }
1034 #endif
1036     return YES;
1039 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
1041     [[PSMTabDragAssistant sharedDragAssistant] draggedImageEndedAt:aPoint operation:operation];
1044 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
1049 #pragma mark -
1050 #pragma mark Actions
1052 - (void)overflowMenuAction:(id)sender
1054     [tabView selectTabViewItem:[sender representedObject]];
1055     [self update];
1058 - (void)closeTabClick:(id)sender
1060     [sender retain];
1061     if(([_cells count] == 1) && (![self canCloseOnlyTab]))
1062         return;
1063     
1064     if(([self delegate]) && ([[self delegate] respondsToSelector:@selector(tabView:shouldCloseTabViewItem:)])){
1065         if(![[self delegate] tabView:tabView shouldCloseTabViewItem:[sender representedObject]]){
1066             // fix mouse downed close button
1067             [sender setCloseButtonPressed:NO];
1068             return;
1069         }
1070     }
1071     
1072     if(([self delegate]) && ([[self delegate] respondsToSelector:@selector(tabView:willCloseTabViewItem:)])){
1073         [[self delegate] tabView:tabView willCloseTabViewItem:[sender representedObject]];
1074     }
1075     
1076     [[sender representedObject] retain];
1077     [tabView removeTabViewItem:[sender representedObject]];
1078     
1079     if(([self delegate]) && ([[self delegate] respondsToSelector:@selector(tabView:didCloseTabViewItem:)])){
1080         [[self delegate] tabView:tabView didCloseTabViewItem:[sender representedObject]];
1081     }
1082     [[sender representedObject] release];
1083     [sender release];
1086 - (void)tabClick:(id)sender
1088     [tabView selectTabViewItem:[sender representedObject]];
1089     [self update];
1092 - (void)tabNothing:(id)sender
1094     [self update];  // takes care of highlighting based on state
1097 - (void)frameDidChange:(NSNotification *)notification
1099     [self update];
1100     // trying to address the drawing artifacts for the progress indicators - hackery follows
1101     // this one fixes the "blanking" effect when the control hides and shows itself
1102     NSEnumerator *e = [_cells objectEnumerator];
1103     PSMTabBarCell *cell;
1104     while(cell = [e nextObject]){
1105         [[cell indicator] stopAnimation:self];
1106         [[cell indicator] startAnimation:self];
1107     }
1108     [self setNeedsDisplay:YES];
1111 - (void)viewWillStartLiveResize
1113     NSEnumerator *e = [_cells objectEnumerator];
1114     PSMTabBarCell *cell;
1115     while(cell = [e nextObject]){
1116         [[cell indicator] stopAnimation:self];
1117     }
1118     [self setNeedsDisplay:YES];
1121 -(void)viewDidEndLiveResize
1123     NSEnumerator *e = [_cells objectEnumerator];
1124     PSMTabBarCell *cell;
1125     while(cell = [e nextObject]){
1126         [[cell indicator] startAnimation:self];
1127     }
1128     [self setNeedsDisplay:YES];
1131 - (void)windowDidMove:(NSNotification *)aNotification
1133     [self setNeedsDisplay:YES];
1136 - (void)windowStatusDidChange:(NSNotification *)notification
1138     // hide? must readjust things if I'm not supposed to be showing
1139     // this block of code only runs when the app launches
1140     if(_hideForSingleTab && ([_cells count] <= 1) && !_awakenedFromNib){
1141         // must adjust frames now before display
1142         NSRect myFrame = [self frame];
1143         if(partnerView){
1144             NSRect partnerFrame = [partnerView frame];
1145             // above or below me?
1146             if(([self frame].origin.y - 22) > [partnerView frame].origin.y){
1147                 // partner is below me
1148                 [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y + 21, myFrame.size.width, myFrame.size.height - 21)];
1149                 [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y, partnerFrame.size.width, partnerFrame.size.height + 21)];
1150             } else {
1151                 // partner is above me
1152                 [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, myFrame.size.width, myFrame.size.height - 21)];
1153                 [partnerView setFrame:NSMakeRect(partnerFrame.origin.x, partnerFrame.origin.y - 21, partnerFrame.size.width, partnerFrame.size.height + 21)];
1154             }
1155             [partnerView setNeedsDisplay:YES];
1156             [self setNeedsDisplay:YES];
1157         } else {
1158             // for window movement
1159             NSRect windowFrame = [[self window] frame];
1160             [[self window] setFrame:NSMakeRect(windowFrame.origin.x, windowFrame.origin.y + 21, windowFrame.size.width, windowFrame.size.height - 21) display:YES];
1161             [self setFrame:NSMakeRect(myFrame.origin.x, myFrame.origin.y, myFrame.size.width, myFrame.size.height - 21)];
1162         }
1163         _isHidden = YES;
1164         [self setNeedsDisplay:YES];
1165         //[[self window] display];
1166     }
1167      _awakenedFromNib = YES;
1168     [self update];
1171 #pragma mark -
1172 #pragma mark NSTabView Delegate
1174 - (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
1176     // here's a weird one - this message is sent before the "tabViewDidChangeNumberOfTabViewItems"
1177     // message, thus I can end up updating when there are no cells, if no tabs were (yet) present
1178     if([_cells count] > 0){
1179         [self update];
1180     }
1181     if([self delegate]){
1182         if([[self delegate] respondsToSelector:@selector(tabView:didSelectTabViewItem:)]){
1183             [[self delegate] performSelector:@selector(tabView:didSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
1184         }
1185     }
1187     
1188 - (BOOL)tabView:(NSTabView *)aTabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem
1190     if([self delegate]){
1191         if([[self delegate] respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)]){
1192             return (int)[[self delegate] performSelector:@selector(tabView:shouldSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
1193         } else {
1194             return YES;
1195         }
1196     } else {
1197         return YES;
1198     }
1200 - (void)tabView:(NSTabView *)aTabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem
1202     if([self delegate]){
1203         if([[self delegate] respondsToSelector:@selector(tabView:willSelectTabViewItem:)]){
1204             [[self delegate] performSelector:@selector(tabView:willSelectTabViewItem:) withObject:aTabView withObject:tabViewItem];
1205         }
1206     }
1209 - (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)aTabView
1211     NSArray *tabItems = [tabView tabViewItems];
1212     // go through cells, remove any whose representedObjects are not in [tabView tabViewItems]
1213     NSEnumerator *e = [_cells objectEnumerator];
1214     PSMTabBarCell *cell;
1215     while(cell = [e nextObject]){
1216         if(![tabItems containsObject:[cell representedObject]]){
1217             [self removeTabForCell:cell];
1218         }
1219     }
1220     
1221     // go through tab view items, add cell for any not present
1222     NSMutableArray *cellItems = [self representedTabViewItems];
1223     NSEnumerator *ex = [tabItems objectEnumerator];
1224     NSTabViewItem *item;
1225     while(item = [ex nextObject]){
1226         if(![cellItems containsObject:item]){
1227             [self addTabViewItem:item];
1228         }
1229     }
1231 #if 0
1232     // HACK!  Make sure '_cells' is ordered the same as 'tabItems'.
1233     NSMutableArray *temp = [[NSMutableArray alloc] initWithArray:_cells];
1234     e = [_cells objectEnumerator];
1235     int count = [temp count];
1236     while ((cell = [e nextObject])) {
1237         int idx = [tabItems indexOfObject:[cell representedObject]];
1238         if (NSNotFound != idx && idx < count) {
1239             [temp replaceObjectAtIndex:idx withObject:cell];
1240         }
1241     }
1243     [_cells release];
1244     _cells = temp;
1246     if ([_cells count] == [tabView numberOfTabViewItems]) {
1247         [self update]; // don't update unless all are accounted for!
1248     }
1249 #endif
1250   
1251     // pass along for other delegate responses
1252     if([self delegate]){
1253         if([[self delegate] respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)]){
1254             [[self delegate] performSelector:@selector(tabViewDidChangeNumberOfTabViewItems:) withObject:aTabView];
1255         }
1256     }
1259 #pragma mark -
1260 #pragma mark Archiving
1262 - (void)encodeWithCoder:(NSCoder *)aCoder 
1264     [super encodeWithCoder:aCoder];
1265     if ([aCoder allowsKeyedCoding]) {
1266         [aCoder encodeObject:_cells forKey:@"PSMcells"];
1267         [aCoder encodeObject:tabView forKey:@"PSMtabView"];
1268         [aCoder encodeObject:_overflowPopUpButton forKey:@"PSMoverflowPopUpButton"];
1269         [aCoder encodeObject:_addTabButton forKey:@"PSMaddTabButton"];
1270         [aCoder encodeObject:style forKey:@"PSMstyle"];
1271         [aCoder encodeBool:_canCloseOnlyTab forKey:@"PSMcanCloseOnlyTab"];
1272         [aCoder encodeBool:_hideForSingleTab forKey:@"PSMhideForSingleTab"];
1273         [aCoder encodeBool:_showAddTabButton forKey:@"PSMshowAddTabButton"];
1274         [aCoder encodeBool:_sizeCellsToFit forKey:@"PSMsizeCellsToFit"];
1275         [aCoder encodeInt:_cellMinWidth forKey:@"PSMcellMinWidth"];
1276         [aCoder encodeInt:_cellMaxWidth forKey:@"PSMcellMaxWidth"];
1277         [aCoder encodeInt:_cellOptimumWidth forKey:@"PSMcellOptimumWidth"];
1278         [aCoder encodeInt:_currentStep forKey:@"PSMcurrentStep"];
1279         [aCoder encodeBool:_isHidden forKey:@"PSMisHidden"];
1280         [aCoder encodeBool:_hideIndicators forKey:@"PSMhideIndicators"];
1281         [aCoder encodeObject:partnerView forKey:@"PSMpartnerView"];
1282         [aCoder encodeBool:_awakenedFromNib forKey:@"PSMawakenedFromNib"];
1283         [aCoder encodeObject:_lastMouseDownEvent forKey:@"PSMlastMouseDownEvent"];
1284         [aCoder encodeObject:delegate forKey:@"PSMdelegate"];
1285         
1286     }
1289 - (id)initWithCoder:(NSCoder *)aDecoder 
1291     self = [super initWithCoder:aDecoder];
1292     if (self) {
1293         if ([aDecoder allowsKeyedCoding]) {
1294             _cells = [[aDecoder decodeObjectForKey:@"PSMcells"] retain];
1295             tabView = [[aDecoder decodeObjectForKey:@"PSMtabView"] retain];
1296             _overflowPopUpButton = [[aDecoder decodeObjectForKey:@"PSMoverflowPopUpButton"] retain];
1297             _addTabButton = [[aDecoder decodeObjectForKey:@"PSMaddTabButton"] retain];
1298             style = [[aDecoder decodeObjectForKey:@"PSMstyle"] retain];
1299             _canCloseOnlyTab = [aDecoder decodeBoolForKey:@"PSMcanCloseOnlyTab"];
1300             _hideForSingleTab = [aDecoder decodeBoolForKey:@"PSMhideForSingleTab"];
1301             _showAddTabButton = [aDecoder decodeBoolForKey:@"PSMshowAddTabButton"];
1302             _sizeCellsToFit = [aDecoder decodeBoolForKey:@"PSMsizeCellsToFit"];
1303             _cellMinWidth = [aDecoder decodeIntForKey:@"PSMcellMinWidth"];
1304             _cellMaxWidth = [aDecoder decodeIntForKey:@"PSMcellMaxWidth"];
1305             _cellOptimumWidth = [aDecoder decodeIntForKey:@"PSMcellOptimumWidth"];
1306             _currentStep = [aDecoder decodeIntForKey:@"PSMcurrentStep"];
1307             _isHidden = [aDecoder decodeBoolForKey:@"PSMisHidden"];
1308             _hideIndicators = [aDecoder decodeBoolForKey:@"PSMhideIndicators"];
1309             partnerView = [[aDecoder decodeObjectForKey:@"PSMpartnerView"] retain];
1310             _awakenedFromNib = [aDecoder decodeBoolForKey:@"PSMawakenedFromNib"];
1311             _lastMouseDownEvent = [[aDecoder decodeObjectForKey:@"PSMlastMouseDownEvent"] retain];
1312             delegate = [[aDecoder decodeObjectForKey:@"PSMdelegate"] retain];
1313         }
1314     }
1315     return self;
1318 #pragma mark -
1319 #pragma mark IB Palette
1321 - (NSSize)minimumFrameSizeFromKnobPosition:(int)position
1323     return NSMakeSize(100.0, 22.0);
1326 - (NSSize)maximumFrameSizeFromKnobPosition:(int)knobPosition
1328     return NSMakeSize(10000.0, 22.0);
1331 - (void)placeView:(NSRect)newFrame
1333     // this is called any time the view is resized in IB
1334     [self setFrame:newFrame];
1335     [self update];
1338 #pragma mark -
1339 #pragma mark Convenience
1341 - (NSMutableArray *)representedTabViewItems
1343     NSMutableArray *temp = [NSMutableArray arrayWithCapacity:[_cells count]];
1344     NSEnumerator *e = [_cells objectEnumerator];
1345     PSMTabBarCell *cell;
1346     while(cell = [e nextObject]){
1347         [temp addObject:[cell representedObject]];
1348     }
1349     return temp;
1352 - (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame
1354     NSRect aRect = [self genericCellRect];
1355     
1356     if(!NSPointInRect(point,aRect)){
1357         return nil;
1358     }
1359     
1360     int i, cnt = [_cells count];
1361     for(i = 0; i < cnt; i++){
1362         PSMTabBarCell *cell = [_cells objectAtIndex:i];
1363         float width = [cell width];
1364         aRect.size.width = width;
1365         
1366         if(NSPointInRect(point, aRect)){
1367             if(outFrame){
1368                 *outFrame = aRect;
1369             }
1370             return cell;
1371         }
1372         aRect.origin.x += width;
1373     }
1374     return nil;
1377 - (PSMTabBarCell *)lastVisibleTab
1379     int i, cellCount = [_cells count];
1380     for(i = 0; i < cellCount; i++){
1381         if([[_cells objectAtIndex:i] isInOverflowMenu])
1382             return [_cells objectAtIndex:(i-1)];
1383     }
1384     return [_cells objectAtIndex:(cellCount - 1)];
1387 - (int)numberOfVisibleTabs
1389     int i, cellCount = [_cells count];
1390     for(i = 0; i < cellCount; i++){
1391         if([[_cells objectAtIndex:i] isInOverflowMenu])
1392             return i+1;
1393     }
1394     return cellCount;
1396     
1398 @end