//
//  PSMAdiumTabStyle.m
//  PSMTabBarControl
//
//  Created by Kent Sutherland on 5/26/06.
//  Copyright 2006 Kent Sutherland. All rights reserved.
//

#import "PSMAdiumTabStyle.h"
#import "PSMTabBarCell.h"
#import "PSMTabBarControl.h"
#import "NSBezierPath_AMShading.h"

#define Adium_CellPadding 2
#define Adium_MARGIN_X 4
#define kPSMAdiumCounterPadding 3.0
#define kPSMAdiumObjectCounterRadius 7.0
#define kPSMAdiumCounterMinWidth 20

#define kPSMTabBarControlSourceListHeight	28

#define kPSMTabBarLargeImageHeight			kPSMTabBarControlSourceListHeight - 4
#define kPSMTabBarLargeImageWidth			kPSMTabBarLargeImageHeight

@implementation PSMAdiumTabStyle

- (NSString *)name
{
    return @"Adium";
}

#pragma mark -
#pragma mark Creation/Destruction

- (id)init
{
    if ( (self = [super init]) ) {
		[self loadImages];
		_drawsUnified = NO;
		_drawsRight = NO;
        
        _objectCountStringAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSFontManager sharedFontManager] convertFont:[NSFont fontWithName:@"Helvetica" size:11.0] toHaveTrait:NSBoldFontMask], NSFontAttributeName,
																					[[NSColor whiteColor] colorWithAlphaComponent:0.85], NSForegroundColorAttributeName,
																					nil, nil];
    }
    return self;
}

- (void)loadImages
{
	_closeButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front"]];
	_closeButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Pressed"]];
	_closeButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Rollover"]];

    _closeDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front"]];
    _closeDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Pressed"]];
    _closeDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Rollover"]];
        	
	_addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNew"]];
    _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewPressed"]];
    _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewRollover"]];
	
	_gradientImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AdiumGradient"]];
}

- (void)dealloc
{
	[_closeButton release];
	[_closeButtonDown release];
	[_closeButtonOver release];
    
	[_closeDirtyButton release];
	[_closeDirtyButtonDown release];
	[_closeDirtyButtonOver release];
	
	[_addTabButtonImage release];
	[_addTabButtonPressedImage release];
	[_addTabButtonRolloverImage release];
	
	[_gradientImage release];
	
    [_objectCountStringAttributes release];
    
    [super dealloc];
}

#pragma mark -
#pragma mark Drawing Style Accessors

- (BOOL)drawsUnified
{
	return _drawsUnified;
}

- (void)setDrawsUnified:(BOOL)value
{
	_drawsUnified = value;
}

- (BOOL)drawsRight
{
	return _drawsRight;
}

- (void)setDrawsRight:(BOOL)value
{
	_drawsRight = value;
}

#pragma mark -
#pragma mark Control Specific

- (CGFloat)leftMarginForTabBarControl
{
    return 3.0f;
}

- (CGFloat)rightMarginForTabBarControl
{
    return 24.0f;
}

- (CGFloat)topMarginForTabBarControl
{
	return 10.0f;
}

- (void)setOrientation:(PSMTabBarOrientation)value
{
	orientation = value;
}

#pragma mark -
#pragma mark Add Tab Button

- (NSImage *)addTabButtonImage
{
    return _addTabButtonImage;
}

- (NSImage *)addTabButtonPressedImage
{
    return _addTabButtonPressedImage;
}

- (NSImage *)addTabButtonRolloverImage
{
    return _addTabButtonRolloverImage;
}

#pragma mark -
#pragma mark Cell Specific

- (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell orientation:(PSMTabBarOrientation)tabOrientation
{
	NSRect dragRect = [cell frame];
	
	if ([cell tabState] & PSMTab_SelectedMask) {
		if (tabOrientation == PSMTabBarHorizontalOrientation) {
			dragRect.size.width++;
			dragRect.size.height -= 2.0;
		}
	}
	
	return dragRect;
}

- (BOOL)closeButtonIsEnabledForCell:(PSMTabBarCell *)cell
{
	return ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]);
	
}
- (NSRect)closeButtonRectForTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)cellFrame
{
	if ([self closeButtonIsEnabledForCell:cell] == NO) {
		return NSZeroRect;
	}

	NSRect result;
	result.size = [_closeButton size];

	switch (orientation) {
		case PSMTabBarHorizontalOrientation:
		{
			result.origin.x = cellFrame.origin.x + Adium_MARGIN_X;
			result.origin.y = cellFrame.origin.y + MARGIN_Y + 2.0;
			if ([cell state] == NSOnState) {
				result.origin.y -= 1;
			}
			break;
		}			

		case PSMTabBarVerticalOrientation:
		{
			result.origin.x = NSMaxX(cellFrame) - (Adium_MARGIN_X*2) - NSWidth(result);
			result.origin.y = NSMinY(cellFrame) + (NSHeight(cellFrame) / 2) - (result.size.height / 2) + 1;
			break;
		}
	}

	return result;
}

- (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell
{
	if ([cell hasIcon] == NO) {
		return NSZeroRect;
	}

	NSRect cellFrame = [cell frame];
	NSImage *icon = [[[cell representedObject] identifier] icon];
	NSSize	iconSize = [icon size];

	NSRect result;
	result.size = iconSize;

	switch (orientation)
	{
		case PSMTabBarHorizontalOrientation:
			result.origin.x = cellFrame.origin.x + Adium_MARGIN_X;
			result.origin.y = cellFrame.origin.y + MARGIN_Y;
			break;

		case PSMTabBarVerticalOrientation:
			result.origin.x = NSMaxX(cellFrame) - (Adium_MARGIN_X * 2) - NSWidth(result);
			result.origin.y = NSMinY(cellFrame) + (NSHeight(cellFrame) / 2) - (NSHeight(result) / 2) + 1;
			break;
	}

	// For horizontal tabs, center in available space (in case icon image is smaller than kPSMTabBarIconWidth)
	if (orientation == PSMTabBarHorizontalOrientation) {
		if (iconSize.width < kPSMTabBarIconWidth)
			result.origin.x += (kPSMTabBarIconWidth - iconSize.width) / 2.0;
		if (iconSize.height < kPSMTabBarIconWidth)
			result.origin.y += (kPSMTabBarIconWidth - iconSize.height) / 2.0;
	}

	if ([cell state] == NSOnState) {
		result.origin.y -= 1;
	}

	return result;
}

- (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell
{
	NSRect cellFrame = [cell frame];

	if ([[cell indicator] isHidden]) {
		return NSZeroRect;
	}

	NSRect result;
	result.size = NSMakeSize(kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth);
	result.origin.x = cellFrame.origin.x + cellFrame.size.width - Adium_MARGIN_X - kPSMTabBarIndicatorWidth;
	result.origin.y = cellFrame.origin.y + MARGIN_Y;

	if ([cell state] == NSOnState) {
		result.origin.y -= 1;
	}

	return result;
}

- (NSSize)sizeForObjectCounterRectForTabCell:(PSMTabBarCell *)cell
{
	NSSize size;
	CGFloat countWidth = [[self attributedObjectCountValueForTabCell:cell] size].width;

	countWidth += (2 * kPSMAdiumObjectCounterRadius - 6.0 + kPSMAdiumCounterPadding);
	
	if (countWidth < kPSMAdiumCounterMinWidth) {
		countWidth = kPSMAdiumCounterMinWidth;
	}
	
	size = NSMakeSize(countWidth, 2 * kPSMAdiumObjectCounterRadius); // temp

	return size;
}

- (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell
{
	NSRect cellFrame;
	NSRect result;

	if ([cell count] == 0) {
		return NSZeroRect;
	}

	cellFrame = [cell frame];
	result.size = [self sizeForObjectCounterRectForTabCell:cell];
	result.origin.x = NSMaxX(cellFrame) - Adium_MARGIN_X - result.size.width;
	result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;

	if (![[cell indicator] isHidden]) {
		result.origin.x -= kPSMTabBarIndicatorWidth + Adium_CellPadding;
	}

	return result;
}

- (CGFloat)minimumWidthOfTabCell:(PSMTabBarCell *)cell
{
	CGFloat resultWidth = 0.0;

	// left margin
	resultWidth = Adium_MARGIN_X;

	// close button?
	if ([self closeButtonIsEnabledForCell:cell]) {
		resultWidth += MAX([_closeButton size].width, NSWidth([self iconRectForTabCell:cell])) + Adium_CellPadding;
	}

	// icon?
	/*if ([cell hasIcon]) {
		resultWidth += kPSMTabBarIconWidth + Adium_CellPadding;
	}*/

	// the label
	resultWidth += kPSMMinimumTitleWidth;

	// object counter?
	if (([cell count] > 0) && (orientation == PSMTabBarHorizontalOrientation)) {
		resultWidth += NSWidth([self objectCounterRectForTabCell:cell]) + Adium_CellPadding;
	}

	// indicator?
	if ([[cell indicator] isHidden] == NO) {
		resultWidth += Adium_CellPadding + kPSMTabBarIndicatorWidth;
	}

	// right margin
	resultWidth += Adium_MARGIN_X;

	return ceil(resultWidth);
}

- (CGFloat)desiredWidthOfTabCell:(PSMTabBarCell *)cell
{
	CGFloat resultWidth = 0.0;

	// left margin
	resultWidth = Adium_MARGIN_X;

	// close button?
	if ([self closeButtonIsEnabledForCell:cell]) {
		resultWidth += MAX([_closeButton size].width, NSWidth([self iconRectForTabCell:cell])) + Adium_CellPadding;
	}

	// icon?
	/*if ([cell hasIcon]) {
		resultWidth += kPSMTabBarIconWidth + Adium_CellPadding;
	}*/

	// the label
	resultWidth += [[cell attributedStringValue] size].width + Adium_CellPadding;

	// object counter?
	if (([cell count] > 0) && (orientation == PSMTabBarHorizontalOrientation)){
		resultWidth += [self objectCounterRectForTabCell:cell].size.width + Adium_CellPadding;
	}

	// indicator?
	if ([[cell indicator] isHidden] == NO) {
		resultWidth += Adium_CellPadding + kPSMTabBarIndicatorWidth;
	}

	// right margin
	resultWidth += Adium_MARGIN_X;

	return ceil(resultWidth);
}

- (CGFloat)tabCellHeight
{
	return ((orientation == PSMTabBarHorizontalOrientation) ? kPSMTabBarControlHeight : kPSMTabBarControlSourceListHeight);
}

#pragma mark -
#pragma mark Cell Values

- (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell
{
	NSString *contents = [NSString stringWithFormat:@"%lu", (unsigned long)[cell count]];
    return [[[NSMutableAttributedString alloc] initWithString:contents attributes:_objectCountStringAttributes] autorelease];
}

- (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell
{
	NSMutableAttributedString *attrStr;
	NSString *contents = [cell stringValue];
	attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
	NSRange range = NSMakeRange(0, [contents length]);

	// Add font attribute
	[attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
	[attrStr addAttribute:NSForegroundColorAttributeName value:[NSColor controlTextColor] range:range];

	// Paragraph Style for Truncating Long Text
	static NSMutableParagraphStyle *TruncatingTailParagraphStyle = nil;
	if (!TruncatingTailParagraphStyle) {
		TruncatingTailParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
		[TruncatingTailParagraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
	}
	[attrStr addAttribute:NSParagraphStyleAttributeName value:TruncatingTailParagraphStyle range:range];

	return attrStr;
}

#pragma mark -
#pragma mark Cell Drawing

- (CGFloat)heightOfAttributedString:(NSAttributedString *)inAttributedString withWidth:(CGFloat)width
{
	static NSMutableDictionary *cache;
	if (!cache)
		cache = [[NSMutableDictionary alloc] init];
	if ([cache count] > 100) //100 items should be trivial in terms of memory overhead, but sufficient
		[cache removeAllObjects];
	NSNumber *cachedHeight = [cache objectForKey:inAttributedString];
	if (cachedHeight)
		return [cachedHeight doubleValue];
	else {
		NSTextStorage		*textStorage = [[NSTextStorage alloc] initWithAttributedString:inAttributedString];
		NSTextContainer 	*textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(width, 1e7)];
		NSLayoutManager 	*layoutManager = [[NSLayoutManager alloc] init];
		
		//Configure
		[textContainer setLineFragmentPadding:0.0];
		[layoutManager addTextContainer:textContainer];
		[textStorage addLayoutManager:layoutManager];
		
		//Force the layout manager to layout its text
		(void)[layoutManager glyphRangeForTextContainer:textContainer];
		
		CGFloat height = [layoutManager usedRectForTextContainer:textContainer].size.height;
		
		[textStorage release];
		[textContainer release];
		[layoutManager release];
		
		[cache setObject:[NSNumber numberWithDouble:height] forKey:inAttributedString];
		
		return height;
	}
}

- (void)drawObjectCounterInCell:(PSMTabBarCell *)cell withRect:(NSRect)myRect
{
    myRect.size.width -= kPSMAdiumCounterPadding;
    myRect.origin.x += kPSMAdiumCounterPadding;
    
	[[cell countColor] ?: [NSColor colorWithCalibratedWhite:0.3 alpha:0.6] set];
	NSBezierPath *path = [NSBezierPath bezierPath];
	[path setLineWidth:1.0];
	
	if ([cell state] == NSOnState) {
		myRect.origin.y -= 1.0;
	}
	
	[path moveToPoint:NSMakePoint(NSMinX(myRect) + kPSMAdiumObjectCounterRadius, NSMinY(myRect))];
	[path lineToPoint:NSMakePoint(NSMaxX(myRect) - kPSMAdiumObjectCounterRadius, NSMinY(myRect))];
	[path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(myRect) - kPSMAdiumObjectCounterRadius, NSMinY(myRect) + kPSMAdiumObjectCounterRadius) 
									 radius:kPSMAdiumObjectCounterRadius
								 startAngle:270.0
								   endAngle:90.0];
	[path lineToPoint:NSMakePoint(NSMinX(myRect) + kPSMAdiumObjectCounterRadius, NSMaxY(myRect))];
	[path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(myRect) + kPSMAdiumObjectCounterRadius, NSMinY(myRect) + kPSMAdiumObjectCounterRadius) 
									 radius:kPSMAdiumObjectCounterRadius
								 startAngle:90.0
								   endAngle:270.0];
	[path fill];
	
	// draw attributed string centered in area
	NSRect counterStringRect;
	NSAttributedString *counterString = [self attributedObjectCountValueForTabCell:cell];
	counterStringRect.size = [counterString size];
	counterStringRect.origin.x = myRect.origin.x + ((myRect.size.width - counterStringRect.size.width) / 2.0) + 0.25;
	counterStringRect.origin.y = myRect.origin.y + ((myRect.size.height - counterStringRect.size.height) / 2.0) + 0.5;
	[counterString drawInRect:counterStringRect];
}

- (NSBezierPath *)bezierPathWithRoundedRect:(NSRect)rect radius:(CGFloat)radius
{
    NSBezierPath	*path = [NSBezierPath bezierPath];
    NSPoint 		topLeft, topRight, bottomLeft, bottomRight;
    
    topLeft = NSMakePoint(rect.origin.x, rect.origin.y);
    topRight = NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y);
    bottomLeft = NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height);
    bottomRight = NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height);
	
    [path appendBezierPathWithArcWithCenter:NSMakePoint(topLeft.x + radius, topLeft.y + radius)
                                     radius:radius
                                 startAngle:180
                                   endAngle:270
                                  clockwise:NO];
    [path lineToPoint:NSMakePoint(topRight.x - radius, topRight.y)];
    
    [path appendBezierPathWithArcWithCenter:NSMakePoint(topRight.x - radius, topRight.y + radius)
                                     radius:radius
                                 startAngle:270
                                   endAngle:0
                                  clockwise:NO];
    [path lineToPoint:NSMakePoint(bottomRight.x, bottomRight.y - radius)];
    
    [path appendBezierPathWithArcWithCenter:NSMakePoint(bottomRight.x - radius, bottomRight.y - radius)
                                     radius:radius
                                 startAngle:0
                                   endAngle:90
                                  clockwise:NO];
    [path lineToPoint:NSMakePoint(bottomLeft.x + radius, bottomLeft.y)];
	
    [path appendBezierPathWithArcWithCenter:NSMakePoint(bottomLeft.x + radius, bottomLeft.y - radius)
                                     radius:radius
                                 startAngle:90
                                   endAngle:180
                                  clockwise:NO];
    [path lineToPoint:NSMakePoint(topLeft.x, topLeft.y + radius)];
	
    return path;
}

- (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView*)controlView
{
	NSRect cellFrame = [cell frame];
	
	if ((orientation == PSMTabBarVerticalOrientation) &&
		[cell hasLargeImage]) {
		NSImage *image = [[[cell representedObject] identifier] largeImage];
		cellFrame.origin.x += Adium_MARGIN_X;

		NSRect imageDrawingRect = NSMakeRect(cellFrame.origin.x,
											 cellFrame.origin.y - ((kPSMTabBarControlSourceListHeight - kPSMTabBarLargeImageHeight) / 2),
											 kPSMTabBarLargeImageWidth, kPSMTabBarLargeImageHeight);

		[NSGraphicsContext saveGraphicsState];
		//Use a transform to draw an arbitrary image in our flipped view
		NSAffineTransform *transform = [NSAffineTransform transform];
		[transform translateXBy:imageDrawingRect.origin.x yBy:(imageDrawingRect.origin.y + imageDrawingRect.size.height)];
		[transform scaleXBy:1.0 yBy:-1.0];
		[transform concat];

		imageDrawingRect.origin = NSMakePoint(0,0);
		
		//Create Rounding.
		CGFloat userIconRoundingRadius = (kPSMTabBarLargeImageWidth / 4.0);
		if (userIconRoundingRadius > 3) userIconRoundingRadius = 3;
		NSBezierPath	*clipPath = [self bezierPathWithRoundedRect:imageDrawingRect radius:userIconRoundingRadius];
		[clipPath addClip];

		[image drawInRect:imageDrawingRect
				 fromRect:NSMakeRect(0, 0, [image size].width, [image size].height)
				operation:NSCompositeSourceOver
				 fraction:1.0];

		[NSGraphicsContext restoreGraphicsState];

		cellFrame.origin.x += imageDrawingRect.size.width;
		cellFrame.size.width -= imageDrawingRect.size.width;
	}

	// label rect
	NSRect labelRect;
	labelRect.origin.x = cellFrame.origin.x + Adium_MARGIN_X;
	labelRect.size.width = cellFrame.size.width - (labelRect.origin.x - cellFrame.origin.x) - Adium_CellPadding;
	labelRect.size.height = cellFrame.size.height;
	switch (orientation)
	{
		case PSMTabBarHorizontalOrientation:
			labelRect.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0;
			break;
		case PSMTabBarVerticalOrientation:
			labelRect.origin.y = cellFrame.origin.y;
			break;
	}
	
	if ([self closeButtonIsEnabledForCell:cell]) {
		/* The close button and the icon (if present) are drawn combined, changing on-hover */
		NSRect closeButtonRect = [cell closeButtonRectForFrame:cellFrame];
		NSRect iconRect = [self iconRectForTabCell:cell];
		NSRect drawingRect;
		NSImage *closeButtonOrIcon = nil;

		if ([cell hasIcon]) {
			/* If the cell has an icon and a close button, determine which rect should be used and use it consistently
			 * This only matters for horizontal tabs; vertical tabs look fine without making this adjustment.
			 */
			if (NSWidth(iconRect) > NSWidth(closeButtonRect)) {
				closeButtonRect.origin.x = NSMinX(iconRect) + NSWidth(iconRect)/2 - NSWidth(closeButtonRect)/2;
			}
		}

		if ([cell closeButtonPressed]) {
			closeButtonOrIcon = ([cell isEdited] ? _closeDirtyButtonDown : _closeButtonDown);
			drawingRect = closeButtonRect;

		} else if ([cell closeButtonOver]) {
            closeButtonOrIcon = ([cell isEdited] ? _closeDirtyButtonOver : _closeButtonOver);
			drawingRect = closeButtonRect;
	
		} else if ((orientation == PSMTabBarVerticalOrientation) &&
				   ([cell count] > 0)) {
			/* In vertical tabs, the count indicator supercedes the icon */
			NSSize counterSize = [self sizeForObjectCounterRectForTabCell:cell];
			if (counterSize.width > NSWidth(closeButtonRect)) {
				closeButtonRect.origin.x -= (counterSize.width - NSWidth(closeButtonRect));
				closeButtonRect.size.width = counterSize.width;
			}

			closeButtonRect.origin.y = cellFrame.origin.y + ((NSHeight(cellFrame) - counterSize.height) / 2);
			closeButtonRect.size.height = counterSize.height;

			drawingRect = closeButtonRect;
			[self drawObjectCounterInCell:cell withRect:drawingRect];
			/* closeButtonOrIcon == nil */

		} else if ([cell hasIcon]) {
			closeButtonOrIcon = [[[cell representedObject] identifier] icon];
			drawingRect = iconRect;
	
		} else {
			closeButtonOrIcon = ([cell isEdited] ? _closeDirtyButton : _closeButton);
			drawingRect = closeButtonRect;
		}
		
		if ([controlView isFlipped]) {
			drawingRect.origin.y += drawingRect.size.height;
		}

		[closeButtonOrIcon compositeToPoint:drawingRect.origin operation:NSCompositeSourceOver fraction:1.0];
		
		// scoot label over
		switch (orientation)
		{
			case PSMTabBarHorizontalOrientation:
			{
				CGFloat oldOrigin = labelRect.origin.x;
				if (NSWidth(iconRect) > NSWidth(closeButtonRect)) {
					labelRect.origin.x = (NSMaxX(iconRect) + (Adium_CellPadding * 2));
				} else {
					labelRect.origin.x = (NSMaxX(closeButtonRect) + (Adium_CellPadding * 2));					
				}
				labelRect.size.width -= (NSMinX(labelRect) - oldOrigin);
				break;
			}
			case PSMTabBarVerticalOrientation:
			{
				//Generate the remaining label rect directly from the location of the close button, allowing for padding
				if (NSWidth(iconRect) > NSWidth(closeButtonRect)) {
					labelRect.size.width = NSMinX(iconRect) - Adium_CellPadding - NSMinX(labelRect);
				} else {
					labelRect.size.width = NSMinX(closeButtonRect) - Adium_CellPadding - NSMinX(labelRect);
				}

				break;
			}
		}

	} else if ([cell hasIcon]) {
		/* The close button is disabled; the cell has an icon */
		NSRect iconRect = [self iconRectForTabCell:cell];
		NSImage *icon = [[[cell representedObject] identifier] icon];

		if ([controlView isFlipped]) {
			iconRect.origin.y += iconRect.size.height;
		}

		[icon compositeToPoint:iconRect.origin operation:NSCompositeSourceOver fraction:1.0];
		
		// scoot label over by the size of the standard close button
		switch (orientation)
		{
			case PSMTabBarHorizontalOrientation:
				labelRect.origin.x += (NSWidth(iconRect) + Adium_CellPadding);
				labelRect.size.width -= (NSWidth(iconRect) + Adium_CellPadding);
				break;
			case PSMTabBarVerticalOrientation:
				labelRect.size.width -= (NSWidth(iconRect) + Adium_CellPadding);
				break;
		}		
	}

	if ([cell state] == NSOnState) {
		labelRect.origin.y -= 1;
	}
	
	if (![[cell indicator] isHidden]) {
		labelRect.size.width -= (kPSMTabBarIndicatorWidth + Adium_CellPadding);
	}
    
	// object counter
	//The object counter takes up space horizontally...
	if (([cell count] > 0) &&
		(orientation == PSMTabBarHorizontalOrientation)) {
		NSRect counterRect = [self objectCounterRectForTabCell:cell];
		
		[self drawObjectCounterInCell:cell withRect:counterRect];
		labelRect.size.width -= NSWidth(counterRect) + Adium_CellPadding;
	}
	
	// draw label
	NSAttributedString *attributedString = [cell attributedStringValue];
	if (orientation == PSMTabBarVerticalOrientation) {
		//Calculate the centered rect
		CGFloat stringHeight = [self heightOfAttributedString:attributedString withWidth:NSWidth(labelRect)];
		if (stringHeight < labelRect.size.height) {
			labelRect.origin.y += (NSHeight(labelRect) - stringHeight) / 2.0;
		}		
	}

	[attributedString drawInRect:labelRect];
}

- (void)drawTabCell:(PSMTabBarCell *)cell
{
	NSRect cellFrame = [cell frame];
	NSColor *lineColor = nil;
    NSBezierPath *bezier = [NSBezierPath bezierPath];
    lineColor = [NSColor grayColor];

	[bezier setLineWidth:1.0];

	//disable antialiasing of bezier paths
	[NSGraphicsContext saveGraphicsState];
	[[NSGraphicsContext currentContext] setShouldAntialias:NO];
	
	NSShadow *shadow = [[NSShadow alloc] init];
	[shadow setShadowOffset:NSMakeSize(-2, -2)];
	[shadow setShadowBlurRadius:2];
	[shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.6 alpha:1.0]];

	if ([cell state] == NSOnState) {
		// selected tab
		if (orientation == PSMTabBarHorizontalOrientation) {
			NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, NSWidth(cellFrame), cellFrame.size.height - 2.5);
			
			// background
			if (_drawsUnified) {
				if ([[[tabBar tabView] window] isKeyWindow]) {
					NSBezierPath *path = [NSBezierPath bezierPathWithRect:aRect];
					[path linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0]
												endColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]];
				} else {
					[[NSColor windowBackgroundColor] set];
					NSRectFill(aRect);
				}
			} else {
				[_gradientImage drawInRect:NSMakeRect(NSMinX(aRect), NSMinY(aRect), NSWidth(aRect), NSHeight(aRect)) fromRect:NSMakeRect(0, 0, [_gradientImage size].width, [_gradientImage size].height) operation:NSCompositeSourceOver fraction:1.0];
			}
			
			// frame
			[lineColor set];
			[bezier setLineWidth:1.0];
			[bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y)];
			[bezier lineToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y + aRect.size.height)];
			
			[shadow setShadowOffset:NSMakeSize(-2, -2)];
			[shadow set];
			[bezier stroke];
			
			bezier = [NSBezierPath bezierPath];
			[bezier setLineWidth:1.0];
			[bezier moveToPoint:NSMakePoint(NSMinX(aRect), NSMaxY(aRect))];
			[bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMaxY(aRect))];
			[bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMinY(aRect))];
			
			if ([[cell controlView] frame].size.height < 2) {
				// special case of hidden control; need line across top of cell
				[bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y + 0.5)];
				[bezier lineToPoint:NSMakePoint(aRect.origin.x+aRect.size.width, aRect.origin.y + 0.5)];
			}
			
			[shadow setShadowOffset:NSMakeSize(2, -2)];
			[shadow set];
			[bezier stroke];
		} else {
			NSRect aRect;
			
			if (_drawsRight) {
				aRect = NSMakeRect(cellFrame.origin.x - 1, cellFrame.origin.y, cellFrame.size.width - 3, cellFrame.size.height);
			} else {
				aRect = NSMakeRect(cellFrame.origin.x + 2, cellFrame.origin.y, cellFrame.size.width - 2, cellFrame.size.height);
			}
			
			// background
			if (_drawsUnified) {
				if ([[[tabBar tabView] window] isKeyWindow]) {
					NSBezierPath *path = [NSBezierPath bezierPathWithRect:aRect];
					[path linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0]
												endColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]];
				} else {
					[[NSColor windowBackgroundColor] set];
					NSRectFill(aRect);
				}
			} else {
				NSBezierPath *path = [NSBezierPath bezierPathWithRect:aRect];
				if (_drawsRight) {
					[path linearVerticalGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.92 alpha:1.0]
														  endColor:[NSColor colorWithCalibratedWhite:0.98 alpha:1.0]];
				} else {
					[path linearVerticalGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.98 alpha:1.0]
														  endColor:[NSColor colorWithCalibratedWhite:0.92 alpha:1.0]];					
				}
			}
			
			// frame
			//top line
			[lineColor set];
			[bezier setLineWidth:1.0];
			[bezier moveToPoint:NSMakePoint(NSMinX(aRect), NSMinY(aRect))];
			[bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMinY(aRect))];
			[bezier stroke];
			
			//outer edge and bottom lines
			bezier = [NSBezierPath bezierPath];
			[bezier setLineWidth:1.0];
			if (_drawsRight) {
				//Right
				[bezier moveToPoint:NSMakePoint(NSMaxX(aRect), NSMinY(aRect))];
				[bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMaxY(aRect))];
				//Bottom
				[bezier lineToPoint:NSMakePoint(NSMinX(aRect), NSMaxY(aRect))];
			} else {
				//Left
				[bezier moveToPoint:NSMakePoint(NSMinX(aRect), NSMinY(aRect))];
				[bezier lineToPoint:NSMakePoint(NSMinX(aRect), NSMaxY(aRect))];
				//Bottom
				[bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMaxY(aRect))];
			}
			[shadow setShadowOffset:NSMakeSize((_drawsRight ? 2 : -2), -2)];
			[shadow set];
			[bezier stroke];
		}
	} else {
		// unselected tab
		NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height);
		
		// rollover
		if ([cell isHighlighted]) {
			[[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set];
			NSRectFillUsingOperation(aRect, NSCompositeSourceAtop);
		}
		
		// frame
		[lineColor set];
		
		if (orientation == PSMTabBarHorizontalOrientation) {
			[bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y)];
			[bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y)];
			if (!([cell tabState] & PSMTab_RightIsSelectedMask)) {
				//draw the tab divider
				[bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + aRect.size.height)];
			}
			[bezier stroke];

		} else {
			//No outline for vertical
		}
	}
	
	[NSGraphicsContext restoreGraphicsState];
	[shadow release];
	
	[self drawInteriorWithTabCell:cell inView:[cell controlView]];
}

- (void)drawBackgroundInRect:(NSRect)rect
{
	//Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area
	rect = [tabBar bounds];

	switch (orientation) {
		case PSMTabBarHorizontalOrientation:
			if (_drawsUnified && [[[tabBar tabView] window] isKeyWindow]) {
				if ([[[tabBar tabView] window] isKeyWindow]) {
					NSBezierPath *backgroundPath = [NSBezierPath bezierPathWithRect:rect];
					[backgroundPath linearGradientFillWithStartColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0]
															endColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]];
				} else {
					[[NSColor windowBackgroundColor] set];
					NSRectFill(rect);
				}
			} else {
				[[NSColor colorWithCalibratedWhite:0.85 alpha:0.6] set];
				[NSBezierPath fillRect:rect];
			}
			break;

		case PSMTabBarVerticalOrientation:
			//This is the Mail.app source list background color... which differs from the iTunes one.
			[[NSColor colorWithCalibratedRed:.9059
									   green:.9294
										blue:.9647
									   alpha:1.0] set];
			NSRectFill(rect);
			break;
	}			

	//Draw the border and shadow around the tab bar itself
	[NSGraphicsContext saveGraphicsState];
	[[NSGraphicsContext currentContext] setShouldAntialias:NO];

	NSShadow *shadow = [[NSShadow alloc] init];
	[shadow setShadowBlurRadius:2];
	[shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.6 alpha:1.0]];
	
	[[NSColor grayColor] set];
	
	NSBezierPath *path = [NSBezierPath bezierPath];
	[path setLineWidth:1.0];
				
	switch (orientation) {
		case PSMTabBarHorizontalOrientation:
		{
			rect.origin.y++;
			[path moveToPoint:NSMakePoint(rect.origin.x, rect.origin.y)];
			[path lineToPoint:NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y)];
			[shadow setShadowOffset:NSMakeSize(2, -2)];
			
			[shadow set];
			[path stroke];

			break;
		}

		case PSMTabBarVerticalOrientation:
		{
			NSPoint startPoint, endPoint;
			NSSize shadowOffset;
			
			//Draw vertical shadow
			if (_drawsRight) {
				startPoint = NSMakePoint(NSMinX(rect), NSMinY(rect));
				endPoint = NSMakePoint(NSMinX(rect), NSMaxY(rect));
				shadowOffset = NSMakeSize(2, -2);
			} else {
				startPoint = NSMakePoint(NSMaxX(rect) - 1, NSMinY(rect));
				endPoint = NSMakePoint(NSMaxX(rect) - 1, NSMaxY(rect));
				shadowOffset = NSMakeSize(-2, -2);
			}
				
			[path moveToPoint:startPoint];
			[path lineToPoint:endPoint];
			[shadow setShadowOffset:shadowOffset];
			
			[shadow set];
			[path stroke];

			[path removeAllPoints];
			
			//Draw top horizontal shadow
			startPoint = NSMakePoint(NSMinX(rect), NSMinY(rect));
			endPoint = NSMakePoint(NSMaxX(rect), NSMinY(rect));
			shadowOffset = NSMakeSize(0, -1);
			
			[path moveToPoint:startPoint];
			[path lineToPoint:endPoint];
			[shadow setShadowOffset:shadowOffset];
			
			[shadow set];
			[path stroke];
			
			break;
		}
	}

	[shadow release];
	[NSGraphicsContext restoreGraphicsState];
}

- (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect
{
	if (orientation != [bar orientation]) {
		orientation = [bar orientation];
	}
	
	if (tabBar != bar) {
		[tabBar release];
		tabBar = [bar retain];
	}
	
	[self drawBackgroundInRect:rect];
	
	// no tab view == not connected
	if (![bar tabView]) {
		NSRect labelRect = rect;
		labelRect.size.height -= 4.0;
		labelRect.origin.y += 4.0;
		NSMutableAttributedString *attrStr;
		NSString *contents = @"PSMTabBarControl";
		attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease];
		NSRange range = NSMakeRange(0, [contents length]);
		[attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range];
		NSMutableParagraphStyle *centeredParagraphStyle = nil;
		
		if (!centeredParagraphStyle) {
			centeredParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
			[centeredParagraphStyle setAlignment:NSCenterTextAlignment];
		}
		
		[attrStr addAttribute:NSParagraphStyleAttributeName value:centeredParagraphStyle range:range];
		[attrStr drawInRect:labelRect];
		return;
	}

	// draw cells
	NSEnumerator *e = [[bar cells] objectEnumerator];
	PSMTabBarCell *cell;
	while ( (cell = [e nextObject]) ) {
		if ([bar isAnimating] || (![cell isInOverflowMenu] && NSIntersectsRect([cell frame], rect))) {
			[cell drawWithFrame:[cell frame] inView:bar];
		}
	}
}

#pragma mark -
#pragma mark Archiving

- (void)encodeWithCoder:(NSCoder *)aCoder 
{
    if ([aCoder allowsKeyedCoding]) {
        [aCoder encodeObject:_closeButton forKey:@"closeButton"];
        [aCoder encodeObject:_closeButtonDown forKey:@"closeButtonDown"];
        [aCoder encodeObject:_closeButtonOver forKey:@"closeButtonOver"];
        [aCoder encodeObject:_closeDirtyButton forKey:@"closeDirtyButton"];
        [aCoder encodeObject:_closeDirtyButtonDown forKey:@"closeDirtyButtonDown"];
        [aCoder encodeObject:_closeDirtyButtonOver forKey:@"closeDirtyButtonOver"];
        [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"];
        [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"];
        [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"];
		[aCoder encodeBool:_drawsUnified forKey:@"drawsUnified"];
		[aCoder encodeBool:_drawsRight forKey:@"drawsRight"];
    }
}

- (id)initWithCoder:(NSCoder *)aDecoder 
{
   if ( (self = [super init]) ) {
        if ([aDecoder allowsKeyedCoding]) {
            _closeButton = [[aDecoder decodeObjectForKey:@"closeButton"] retain];
            _closeButtonDown = [[aDecoder decodeObjectForKey:@"closeButtonDown"] retain];
            _closeButtonOver = [[aDecoder decodeObjectForKey:@"closeButtonOver"] retain];
            _closeDirtyButton = [[aDecoder decodeObjectForKey:@"closeDirtyButton"] retain];
            _closeDirtyButtonDown = [[aDecoder decodeObjectForKey:@"closeDirtyButtonDown"] retain];
            _closeDirtyButtonOver = [[aDecoder decodeObjectForKey:@"closeDirtyButtonOver"] retain];
            _addTabButtonImage = [[aDecoder decodeObjectForKey:@"addTabButtonImage"] retain];
            _addTabButtonPressedImage = [[aDecoder decodeObjectForKey:@"addTabButtonPressedImage"] retain];
            _addTabButtonRolloverImage = [[aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"] retain];
			_drawsUnified = [aDecoder decodeBoolForKey:@"drawsUnified"];
			_drawsRight = [aDecoder decodeBoolForKey:@"drawsRight"];
        }
    }
    return self;
}

@end