/* 
 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#import "MTabSwitcher.h"

@implementation MTabSwitcher


static NSImage *CloseButtonImagePressed = nil;
static NSImage *CloseButtonImageUnpressed = nil;
static NSDictionary *EditorTabImages = nil;

+ (void)initialize
{
  [self exposeBinding: @"tabStyle"];
  
  NSBundle *bundle = [NSBundle bundleForClass: [MTabSwitcher class]];
  
  CloseButtonImagePressed = [[NSImage alloc] initWithContentsOfFile: 
                             [bundle pathForImageResource: @"TabClose_Pressed.png"]];
  
  CloseButtonImageUnpressed = [[NSImage alloc] initWithContentsOfFile: 
                               [bundle pathForImageResource: @"TabClose_Unpressed.png"]];
  
  
  EditorTabImages = [[NSDictionary dictionaryWithObjectsAndKeys:
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_00.png"]] autorelease], @"bg",
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_01.png"]] autorelease], @"EA", // Empty - Active
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_02.png"]] autorelease], @"A", // Active
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_03.png"]] autorelease], @"AE", // Active - Empty
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_05.png"]] autorelease], @"I", // Inactive
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_06.png"]] autorelease], @"IE", // Inactive Empty
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_07.png"]] autorelease], @"EI", // Empty - Inactive
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_10.png"]] autorelease], @"AC", // Active close
                      [[[NSImage alloc] initWithContentsOfFile: 
                        [bundle pathForImageResource: @"tab_11.png"]] autorelease], @"ACH", // Active close hover
                      nil] retain];
}


- (id)initWithFrame:(NSRect)frameRect
{
  self = [super initWithFrame: frameRect];
  if (self)
  {
    // make editor the default. object editors rely on this default
    [self setTabStyle: MEditorBottomTabSwitcher];
    
    mToolTipTags = [[NSMutableArray alloc] init];
    
    mTrack = [[NSTrackingArea alloc] initWithRect: [self bounds]
                                          options: NSTrackingMouseEnteredAndExited|NSTrackingMouseMoved|NSTrackingActiveAlways
                                            owner: self
                                         userInfo: nil];
    [self addTrackingArea: mTrack];
    
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(frameDidChange:)
                                                 name: NSViewFrameDidChangeNotification
                                               object: self];
  }
  return self;
}

- (void)dealloc
{
  for (id tag in mToolTipTags)
    [self removeToolTip: [tag intValue]];
  [mToolTipTags release];
  [[NSNotificationCenter defaultCenter] removeObserver: self];
  [mTrack release];
  [mLabelAttributes release];
  [mLabelDisabledAttributes release];
  [mLabelShadowAttributes release];
  [super dealloc];
}

- (void)setTabView:(NSTabView*)tabView
{
  if (mTabView != tabView)
  {
    if (mTabView)
      [mTabView setDelegate: mDelegate];
    
    mTabView = tabView;
    mDelegate = [mTabView delegate];
    [mTabView setDelegate: self];
  }
}

- (void)setDefaultTabWidth:(float)w
{
  mMinTabWidth = w;
}

- (void)setDelegate:(id)delegate
{
  mDelegate = delegate;
}

- (void)setAllowTabReordering:(BOOL)flag
{
  mAllowTabReordering = flag;
}

- (id)delegate
{
  return mDelegate;
}


- (NSSize)_sizeOfTabViewItem:(NSTabViewItem*)item 
{
  NSSize size = NSZeroSize;
  if (item)
  {
    NSImage *icon = nil;
    switch (mStyle)
    {
      case MPaletteTabSwitcher:
      case MPaletteTabSwitcherSmallText:
      case MSectionTabSwitcher:
        size.height = 24;
        size.width = MAX(ceil([[item label] sizeWithAttributes: mLabelAttributes].width) + 20, mMinTabWidth);
        if (mMinTabWidth < mDefaultMinTabWidth)
          size.width = mMinTabWidth;
        if (mMaxTabWidth > 0)
          size.width = MIN(mMaxTabWidth, size.width);
        if ([mDelegate respondsToSelector: @selector(tabView:iconForItem:)] && 
            (icon = [mDelegate tabView: mTabView iconForItem: item]))
          size.width += [icon size].width + 5;
        break;
      case MEditorTabSwitcher:
        size.height = 24;
        size.width = MAX(ceil([[item label] sizeWithAttributes: mLabelAttributes].width) + 25*2, mMinTabWidth);
        if (mMaxTabWidth > 0)
          size.width = MIN(mMaxTabWidth, size.width);
        break;
      case MEditorBottomTabSwitcher:
        size.height = 30;
        size.width = MAX(ceil([[item label] sizeWithAttributes: mLabelAttributes].width) + 20, mMinTabWidth);
        if ([mDelegate respondsToSelector: @selector(tabView:iconForItem:)] && 
            (icon = [mDelegate tabView: mTabView iconForItem: item]))
          size.width += [icon size].width + 5;
        if (mMaxTabWidth > 0)
          size.width = MIN(mMaxTabWidth, size.width);
        break;
      case MMainTabSwitcher:
        size.height = 23;
        if ([mDelegate respondsToSelector: @selector(tabView:iconForItem:)] && 
            (icon = [mDelegate tabView: mTabView iconForItem: item]))
          size.width = [icon size].width + 30;
        else
          size.width = MAX(ceil([[item label] sizeWithAttributes: mLabelAttributes].width) + 30, mMinTabWidth);
        if (mMaxTabWidth > 0)
          size.width = MIN(mMaxTabWidth, size.width);
        break;
    }
  }
  else
  {
    switch (mStyle)
    {
      case MPaletteTabSwitcher:
      case MPaletteTabSwitcherSmallText:
        size.height = 24;
        size.width = 0;
        for (NSTabViewItem *item in [mTabView tabViewItems])
        {
          if (![[item view] isHidden])
            size.width += [self _sizeOfTabViewItem: item].width;
        }
        size.width = floor((NSWidth([self frame]) - size.width) / 2);
        if (size.width < 0)
          size.width = 1;
        break;
      case MSectionTabSwitcher:
        size.height = 24;
        size.width = 16;
        break;
      case MEditorTabSwitcher:
        size.height = 24;
        size.width = 0;
        break;        
      case MEditorBottomTabSwitcher:
        size.height = 30;
        size.width = 10;
        break;
      case MMainTabSwitcher:
        size.height = 23;
        size.width = 8;
        break;
    }
  }
  return size;
}


- (void)setTabStyle:(MTabSwitcherStyle)style
{
  mStyle = style;
  [self setNeedsDisplay: YES];
  
  [mLabelDisabledAttributes release];
  mLabelDisabledAttributes = nil;
  [mLabelShadowAttributes release];
  mLabelShadowAttributes = nil;
  [mLabelAttributes release];
  switch (style)
  {
    case MSectionTabSwitcher:
    case MPaletteTabSwitcher:
    {
      NSMutableParagraphStyle *style = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
      [style setAlignment: NSCenterTextAlignment];
      [style setLineBreakMode: NSLineBreakByTruncatingTail];
      
      mLabelAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                          [NSFont boldSystemFontOfSize: [NSFont systemFontSizeForControlSize: NSSmallControlSize]], NSFontAttributeName,
                          [NSColor blackColor], NSForegroundColorAttributeName,
                          style, NSParagraphStyleAttributeName,
                          nil];
      mMinTabWidth = 92;
      mDefaultMinTabWidth = mMinTabWidth;
      break;
    }
    case MPaletteTabSwitcherSmallText:
    {
      NSMutableParagraphStyle *style = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
      [style setAlignment: NSCenterTextAlignment];
      [style setLineBreakMode: NSLineBreakByTruncatingTail];

      mLabelAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                          [NSFont boldSystemFontOfSize: [NSFont systemFontSizeForControlSize: NSMiniControlSize]], NSFontAttributeName,
                          [NSColor blackColor], NSForegroundColorAttributeName,
                          style, NSParagraphStyleAttributeName,
                          nil];
      mMinTabWidth = 70;
      mDefaultMinTabWidth = mMinTabWidth;
      break;
    }
    case MEditorTabSwitcher:
    {
      NSMutableParagraphStyle *style = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
      [style setAlignment: NSCenterTextAlignment];
      [style setLineBreakMode: NSLineBreakByTruncatingTail];
      
      mLabelAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                          [NSFont boldSystemFontOfSize: [NSFont systemFontSizeForControlSize: NSSmallControlSize]], NSFontAttributeName,
                          [NSColor colorWithDeviceWhite: 53/255.0 alpha: 1.0], NSForegroundColorAttributeName,
                          style, NSParagraphStyleAttributeName,
                          nil];
      mLabelShadowAttributes = [mLabelAttributes mutableCopy];
      [mLabelShadowAttributes setObject: [NSColor colorWithDeviceWhite: 245/255.0 alpha: 1.0] forKey: NSForegroundColorAttributeName];
      mLabelDisabledAttributes = [mLabelAttributes mutableCopy];
      [mLabelDisabledAttributes setObject: [NSColor colorWithDeviceWhite: 108/255.0 alpha: 1.0] forKey: NSForegroundColorAttributeName];
      mMinTabWidth = 90;
      mDefaultMinTabWidth = mMinTabWidth;
      break;
    }
    case MEditorBottomTabSwitcher:
      mLabelAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                          [NSFont systemFontOfSize: [NSFont systemFontSizeForControlSize: NSSmallControlSize]], NSFontAttributeName,
                          [NSColor blackColor], NSForegroundColorAttributeName,
                          nil];
      mMinTabWidth = 80;
      mDefaultMinTabWidth = mMinTabWidth;
      break;
    case MMainTabSwitcher:
      mLabelAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                          [NSFont systemFontOfSize: 11.5], NSFontAttributeName,
                          [NSColor blackColor], NSForegroundColorAttributeName,
                          nil];
      mMinTabWidth = 20;
      mDefaultMinTabWidth = mMinTabWidth;
      break;
  }
  if (!mLabelShadowAttributes)
    mLabelShadowAttributes = [mLabelAttributes mutableCopy];
  
  [self setFrameSize: NSMakeSize(NSWidth([self frame]), [self _sizeOfTabViewItem: nil].height)];
}


- (NSRect)_tabItemRect: (NSTabViewItem*)aItem
{
  int skip = mFirstVisibleTab;
  float x = [self _sizeOfTabViewItem: nil].width;
  for (NSTabViewItem *item in [mTabView tabViewItems])
  {
    if ([[item view] isHidden]) continue;
    if (skip > 0)
    {
      skip--;
      continue;
    }
    NSSize size = [self _sizeOfTabViewItem: item];      
    if (item == aItem)
      return NSMakeRect(x, 0, size.width, size.height);
    x += size.width;
  }
  return NSZeroRect;
}


- (NSTabViewItem*)tabViewItemAtPoint:(NSPoint)pos
{  
  switch (mStyle)
  {
    case MEditorBottomTabSwitcher:
      if (pos.y < 8)
        return nil;
      break;
    case MEditorTabSwitcher:
      if (pos.y > NSHeight([self frame])-8)
        return nil;
    default:
      break;
  }
  if (pos.y < 0 || pos.y > NSHeight([self frame]))
    return nil;
  
  int skip = mFirstVisibleTab;
  float x = [self _sizeOfTabViewItem: nil].width;
  for (NSTabViewItem *item in [mTabView tabViewItems])
  {
    if ([[item view] isHidden]) continue;
    if (skip > 0)
    {
      skip--;
      continue;
    }
    float w = [self _sizeOfTabViewItem: item].width;
    if (pos.x >= x && pos.x < x + w)
    {
      return item;
    }
    x += w;
  }
  return nil;
}


- (NSSize)minimumSize
{
  return NSMakeSize(0, [self _sizeOfTabViewItem: nil].height);
}

- (BOOL)expandsOnLayoutVertically:(BOOL)vertically
{
  if (vertically)
    return NO;
  return YES;
}

                          
- (float)drawMainTabStyleInFrame:(NSRect)rect forItem:(NSTabViewItem*)item
{
  float x = NSMinX(rect);
  float width = [self _sizeOfTabViewItem: item].width;
  float height = NSHeight(rect);
  if ([item tabState] != NSBackgroundTab)
  {
    BOOL isMain = [[self window] isMainWindow];
    float selectedItemMinX = x+0.5;
    float selectedItemMaxX = x+width+0.5;
    
    [[NSColor colorWithDeviceWhite: 246/255.0 alpha: 1.0] set];
    
    NSBezierPath *path = [NSBezierPath bezierPath];
    
    [path moveToPoint: NSMakePoint(selectedItemMinX-5, 0)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMinX-5, 5)
                                     radius: 5
                                 startAngle: 270
                                   endAngle: 360];
    [path lineToPoint: NSMakePoint(selectedItemMinX, height-5)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMinX+5, height-5.5)
                                     radius: 5
                                 startAngle: 180
                                   endAngle: 90
                                  clockwise: YES];
    [path lineToPoint: NSMakePoint(selectedItemMaxX - 5, height-0.5)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMaxX-5, height-5.5)
                                     radius: 5
                                 startAngle: 90
                                   endAngle: 0
                                  clockwise: YES];
    [path lineToPoint: NSMakePoint(selectedItemMaxX, 5)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMaxX+5, 5)
                                     radius: 5
                                 startAngle: 180
                                   endAngle: 270];
    
    if (isMain)
    {
      [[[[NSGradient alloc] initWithStartingColor: [NSColor colorWithDeviceWhite: 206/255.0 alpha:1.0]
                                      endingColor: [NSColor colorWithDeviceWhite: 230/255.0 alpha:1.0]] autorelease]
       drawInBezierPath: path angle: 90];
    }
    else
    {
      [[[[NSGradient alloc] initWithStartingColor: [NSColor colorWithDeviceWhite: 233/255.0 alpha:1.0]
                                      endingColor: [NSColor colorWithDeviceWhite: 240/255.0 alpha:1.0]] autorelease]
       drawInBezierPath: path angle: 90];
    }

    [path setLineWidth: 1.0];
    [path stroke];

    // top rounded corners
    if (isMain)
      [[NSColor colorWithDeviceWhite: 161/255.0 alpha: 0.6] set];
    else
      [[NSColor colorWithDeviceWhite: 230/255.0 alpha: 0.6] set];
    path = [NSBezierPath bezierPath];
    [path moveToPoint: NSMakePoint(selectedItemMinX-0.5, height-4.5)];
    
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMinX+4.5, height-4.5)
                                     radius: 5
                                 startAngle: 180
                                   endAngle: 90
                                  clockwise: YES];
    [path lineToPoint: NSMakePoint(selectedItemMaxX-4.5, height+0.5)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMaxX-5, height-4.5)
                                     radius: 5
                                 startAngle: 90
                                   endAngle: 0
                                  clockwise: YES];
    [path setLineWidth: 1.0];
    [path stroke];
    
    // sides
    if (isMain)
      [[NSColor colorWithDeviceWhite: 141/255.0 alpha: 0.8] set];
    else
      [[NSColor colorWithDeviceWhite: 210/255.0 alpha: 0.8] set];
    
    path = [NSBezierPath bezierPath];
    [path moveToPoint: NSMakePoint(selectedItemMinX-5.5, 1.5)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMinX-5.5, 6)
                                     radius: 5
                                 startAngle: 270
                                   endAngle: 360];
    [path lineToPoint: NSMakePoint(selectedItemMinX-0.5, height-4)];
    /*
     [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMinX+5, height-4.5)
     radius: 5
     startAngle: 180
     endAngle: 90
     clockwise: YES];
     [path lineToPoint: NSMakePoint(selectedItemMaxX - 5.0, height+0.5)];
     [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMaxX-5.0, height-4.5)
     radius: 5
     startAngle: 90
     endAngle: 0
     clockwise: YES];
     */
    [path moveToPoint: NSMakePoint(selectedItemMaxX, height-4.5)];
    [path lineToPoint: NSMakePoint(selectedItemMaxX, 6)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMaxX+5.0, 6)
                                     radius: 5
                                 startAngle: 180
                                   endAngle: 270];
    
    [path setLineWidth: 1.0];
    [path stroke];
  }
  
  NSImage *icon = nil;
  if ([mDelegate respondsToSelector: @selector(tabView:iconForItem:)])
      icon = [mDelegate tabView: mTabView iconForItem: item];
    
  if (icon)
  {
    [icon drawInRect: NSMakeRect(x + 15, (int)((height-[icon size].height)/2), [icon size].width, [icon size].height)
            fromRect: NSMakeRect(0, 0, [icon size].width, [icon size].height)
           operation: NSCompositeSourceOver fraction: 1.0];
  }
  else
  {
    if ([mDelegate respondsToSelector: @selector(tabView:itemHasCloseButton:)] && [mDelegate tabView: mTabView itemHasCloseButton: item])
      x -= [CloseButtonImagePressed size].width / 2;

    if ([item tabState] == NSSelectedTab)
      [mLabelShadowAttributes setObject: [NSColor colorWithDeviceWhite: 245/255.0 alpha: 1.0] forKey: NSForegroundColorAttributeName];
    else
      [mLabelShadowAttributes setObject: [NSColor colorWithDeviceWhite: 200/255.0 alpha: 1.0] forKey: NSForegroundColorAttributeName];
    
    [[item label] drawInRect: NSMakeRect(x + 15, 0, width, height-6) withAttributes: mLabelShadowAttributes];
    [[item label] drawInRect: NSMakeRect(x + 14, 1, width, height-6) withAttributes: mLabelAttributes];
    
    if (mHoverItem == item)
    {
      NSImage *image = mClosePressed ? CloseButtonImagePressed : CloseButtonImageUnpressed;
      mCloseButtonRect = NSMakeRect(x+width-[image size].width, 4, [image size].width, [image size].height);
      [image drawInRect: mCloseButtonRect fromRect: NSMakeRect(0, 0, [image size].width, [image size].height)
              operation: NSCompositeSourceOver fraction: 1.0];    
    }
  }
  return width;
}


- (float)drawSectionTabStyleInFrame:(NSRect)rect forItem:(NSTabViewItem*)item
{
  NSString *label = [item label];
  NSSize labelSize = [label sizeWithAttributes: mLabelAttributes];
  NSRect tabRect;
  tabRect.origin = rect.origin;
  tabRect.size = [self _sizeOfTabViewItem: item];

  if ([item tabState] != NSBackgroundTab && [mTabView numberOfTabViewItems] > 1)
  {
    [[[[NSGradient alloc] initWithColors: [NSArray arrayWithObjects: 
                                           [NSColor colorWithDeviceWhite: 225/255.0 alpha:1.0],
                                           [NSColor colorWithDeviceWhite: 221/255.0 alpha:1.0],
                                           [NSColor colorWithDeviceWhite: 226/255.0 alpha:1.0],
                                           [NSColor colorWithDeviceWhite: 247/255.0 alpha:1.0],
                                           nil]] autorelease]
     drawInBezierPath: [NSBezierPath bezierPathWithRect: tabRect]
     angle: 270];
    
    // side bounds
    NSGradient *grad = [[NSGradient alloc] initWithColors: [NSArray arrayWithObjects:
                                                             [NSColor colorWithDeviceWhite: 203/255.0 alpha: 1.0],
                                                             [NSColor colorWithDeviceWhite: 138/255.0 alpha: 1.0],
                                                             [NSColor colorWithDeviceWhite: 226/255.0 alpha: 1.0],
                                                             nil]];
    [grad drawInRect: NSMakeRect(NSMaxX(tabRect), NSMinY(tabRect)+1, 1, NSHeight(tabRect)-2) angle: 270];
    [grad drawInRect: NSMakeRect(NSMinX(tabRect), NSMinY(tabRect)+1, 1, NSHeight(tabRect)-2) angle: 270];
    [grad release];
    
    grad = [[NSGradient alloc] initWithColors: [NSArray arrayWithObjects:
                                                [NSColor colorWithDeviceWhite: 224/255.0 alpha: 1.0],
                                                [NSColor colorWithDeviceWhite: 200/255.0 alpha: 1.0],
                                                [NSColor colorWithDeviceWhite: 246/255.0 alpha: 1.0],
                                                nil]];
    [grad drawInRect: NSMakeRect(NSMaxX(tabRect)-1, NSMinY(tabRect)+1, 1, NSHeight(tabRect)-2) angle: 270];
    [grad drawInRect: NSMakeRect(NSMinX(tabRect)+1, NSMinY(tabRect)+1, 1, NSHeight(tabRect)-2) angle: 270];
    [grad release];    
  }
  tabRect.origin.y = floor(NSMinY(tabRect) + 1 + (NSHeight(tabRect)-labelSize.height)/2);
  [label drawInRect: tabRect//NSMakePoint(NSMinX(tabRect) + (NSWidth(tabRect) - labelSize.width) / 2, NSMinY(tabRect) + 1 + (NSHeight(tabRect)-labelSize.height)/2)
     withAttributes: mLabelAttributes];

  return NSWidth(tabRect);
}


//
// Palette selector
//
- (float)drawPaletteTabStyleInFrame:(NSRect)rect forItem:(NSTabViewItem*)item
{
  NSString *label = [item label];
  NSSize labelSize = [label sizeWithAttributes: mLabelAttributes];
  NSRect tabRect;
  tabRect.origin = NSMakePoint(floor(NSMinX(rect)), floor(NSMinY(rect)));
  tabRect.size = [self _sizeOfTabViewItem: item];
    
  if ([item tabState] != NSBackgroundTab/* && [mTabView numberOfTabViewItems] > 1*/)
  {
    [[[[NSGradient alloc] initWithColorsAndLocations:
       [NSColor colorWithDeviceWhite: 0xba/255.0 alpha:1.0], (CGFloat)0.0,
       [NSColor colorWithDeviceWhite: 0xb8/255.0 alpha:1.0], (CGFloat)0.53,
       [NSColor colorWithDeviceWhite: 0xd2/255.0 alpha:1.0], (CGFloat)1.0,
       nil] autorelease]
     drawInBezierPath: [NSBezierPath bezierPathWithRect: tabRect]
     angle: 90];
    
    // side bounds
    NSGradient *grad = [[NSGradient alloc] initWithColorsAndLocations: 
                        [NSColor colorWithDeviceWhite: 0xaf/255.0 alpha: 1.0], (CGFloat)0.0,
                        [NSColor colorWithDeviceWhite: 0x71/255.0 alpha: 1.0], (CGFloat)0.5,
                        [NSColor colorWithDeviceWhite: 0xc7/255.0 alpha: 1.0], (CGFloat)1.0,
                        nil];
    [grad drawInRect: NSMakeRect(NSMaxX(tabRect), NSMinY(tabRect)+1, 1, NSHeight(tabRect)-2) angle: 90];
    [grad drawInRect: NSMakeRect(NSMinX(tabRect), NSMinY(tabRect)+1, 1, NSHeight(tabRect)-2) angle: 90];
    [grad release];
    
    grad = [[NSGradient alloc] initWithColorsAndLocations:
            [NSColor colorWithDeviceWhite: 0xb9/255.0 alpha: 1.0], (CGFloat)0.0,
            [NSColor colorWithDeviceWhite: 0xa6/255.0 alpha: 1.0], (CGFloat)0.5,
            [NSColor colorWithDeviceWhite: 0xcd/255.0 alpha: 1.0], (CGFloat)1.0,
            nil];
    [grad drawInRect: NSMakeRect(NSMaxX(tabRect)-1, NSMinY(tabRect)+1, 1, NSHeight(tabRect)-2) angle: 90];
    [grad drawInRect: NSMakeRect(NSMinX(tabRect)+1, NSMinY(tabRect)+1, 1, NSHeight(tabRect)-2) angle: 90];
    [grad release];    
  }
  tabRect.origin.y = floor(NSMinY(tabRect) + 1 + (NSHeight(tabRect)-labelSize.height)/2);
  tabRect.size.height = labelSize.height;
  if (labelSize.width < mMinTabWidth - 4)
    [label drawInRect: tabRect withAttributes: mLabelAttributes];
  else
    [label drawInRect: NSMakeRect(NSMinX(tabRect)+2, NSMinY(tabRect), mMinTabWidth - 4, NSHeight(tabRect))
       withAttributes: mLabelAttributes];
  
  return NSWidth(tabRect);
}

//
// Top tabs style.
//
- (float)drawEditorTabStyleInFrame:(NSRect)rect forItem:(NSTabViewItem*)item previousItem:(NSTabViewItem*)previous
{
  NSString *label = [item label];
  NSSize labelSize = [label sizeWithAttributes: mLabelAttributes];
  NSRect tabRect;
  tabRect.origin = rect.origin;
  tabRect.size = [self _sizeOfTabViewItem: item];
  NSImage *icon = [mDelegate respondsToSelector: @selector(tabView:iconForItem:)] ? [mDelegate tabView: mTabView iconForItem: item] : nil;
  NSImage *leftPart, *middlePart, *rightPart, *closePart;
  float leftSize, rightSize;
  float leftPadding, rightPadding;
  NSRect r;
  
  if (NSMaxX(tabRect) > NSMaxX(rect))
    tabRect.size.width = NSMaxX(rect) - NSMinX(tabRect);
  
  if ([item tabState] == NSSelectedTab)
  {
    middlePart = [EditorTabImages objectForKey: @"A"];
    leftPart = [EditorTabImages objectForKey: @"EA"];
    rightPart = [EditorTabImages objectForKey: @"AE"];
  }
  else
  {
    middlePart = [EditorTabImages objectForKey: @"I"];
    leftPart = [EditorTabImages objectForKey: @"EI"];
    rightPart = [EditorTabImages objectForKey: @"IE"];
  }
  if (mHoverItem == item && mCloseHighlighted)
    closePart = [EditorTabImages objectForKey: @"ACH"];
  else
    closePart = [EditorTabImages objectForKey: @"AC"];
  
  rightPadding = [closePart size].width + 2;
  leftPadding = [icon size].width;
  
  r.origin = rect.origin;
  r.size = [leftPart size];
  leftSize = r.size.width;
  [leftPart drawInRect: r
              fromRect: NSZeroRect
             operation: NSCompositeSourceOver
              fraction: 1.0];
  r.origin.x += leftSize;
  rightSize = [rightPart size].width;
  [middlePart drawInRect: NSMakeRect(r.origin.x, r.origin.y, NSWidth(tabRect) - leftSize - 2, [middlePart size].height)
                fromRect: NSZeroRect
               operation: NSCompositeSourceOver
                fraction: 1.0];
  r.size = [rightPart size];
  r.origin.x += NSWidth(tabRect) - leftSize - 2;
  [rightPart drawInRect: r
               fromRect: NSZeroRect
              operation: NSCompositeSourceOver
               fraction: 1.0];
  
  if (item != mBusyTab)
  {
    r.size = [closePart size];
    r.origin.x = NSMaxX(tabRect) - NSWidth(r) - 3;
    r.origin.y = 3;
    [closePart drawAtPoint: r.origin
                  fromRect: NSZeroRect
                 operation: NSCompositeSourceOver
                  fraction: 1.0];
    if (mHoverItem == item)
      mCloseButtonRect = NSMakeRect(NSMinX(r), 3, NSWidth(r), NSWidth(r));
  }
  
  [icon drawAtPoint: NSMakePoint(NSMinX(tabRect) + [leftPart size].width + 2, NSMinY(tabRect) + (NSHeight(tabRect) - 4 - [icon size].height)/2)
           fromRect: NSZeroRect 
          operation: NSCompositeSourceOver
           fraction: [item tabState] == NSSelectedTab ? 1.0 : 0.5];

  if ([item tabState] == NSSelectedTab)
    [label drawInRect: NSMakeRect(NSMinX(tabRect) + leftSize + leftPadding, NSMinY(tabRect) + 2, 
                                  NSWidth(tabRect) - leftSize - leftPadding - rightPadding, labelSize.height) 
        withAttributes: mLabelShadowAttributes];
  [label drawInRect: NSMakeRect(NSMinX(tabRect) + leftSize + leftPadding, NSMinY(tabRect) + 3, 
                                NSWidth(tabRect) - leftSize - leftPadding - rightPadding, labelSize.height)
      withAttributes: [item tabState] == NSSelectedTab ? mLabelAttributes : mLabelDisabledAttributes];
  
  return NSWidth(tabRect);
}

//
// Bottom tabs style.
//
- (float)drawEditorBottomTabStyleInFrame:(NSRect)rect forItem:(NSTabViewItem*)item
{
  NSString *label = [item label];
  NSSize labelSize = [label sizeWithAttributes: mLabelAttributes];
  NSRect tabRect;
  tabRect.origin = rect.origin;
  tabRect.size = [self _sizeOfTabViewItem: item];
  
  if ([item tabState] == NSSelectedTab)
  {
    NSBezierPath *path, *shadowPath;
    float selectedItemMinX = NSMinX(tabRect) + 0.5;
    float selectedItemMaxX = NSMaxX(tabRect) + 0.5;
    float height = NSHeight(tabRect);
    
    path = [NSBezierPath bezierPath];
    [path moveToPoint: NSMakePoint(selectedItemMinX, height)];
    [path lineToPoint: NSMakePoint(selectedItemMinX, 7+7.5)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMinX+7, 8+7.5)
                                     radius: 7
                                 startAngle: 180
                                   endAngle: 270
                                  clockwise: NO];
    [path lineToPoint: NSMakePoint(selectedItemMaxX-7, 8.5)];
    [path appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMaxX-7, 8+7.5)
                                     radius: 7
                                 startAngle: 270
                                   endAngle: 0
                                  clockwise: NO];
    [path lineToPoint: NSMakePoint(selectedItemMaxX, height)];
  
    [[NSColor whiteColor] set];
    [path fill];

    [[NSColor colorWithDeviceWhite: 150.0/256 alpha: 0.8] set];
//    [NSBezierPath strokeLineFromPoint: NSMakePoint(selectedItemMinX+2, 9.5) toPoint: NSMakePoint(selectedItemMaxX-2, 9.5)];
    
    shadowPath = [NSBezierPath bezierPath];
    [shadowPath moveToPoint: NSMakePoint(selectedItemMinX-1, height-1)];
    [shadowPath lineToPoint: NSMakePoint(selectedItemMinX-1, 7+7.5)];
    [shadowPath appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMinX-1+7, 7+7.5)
                                     radius: 7
                                 startAngle: 180
                                   endAngle: 270
                                  clockwise: NO];
    [shadowPath lineToPoint: NSMakePoint(selectedItemMaxX+1-7, 7.5)];
    [shadowPath appendBezierPathWithArcWithCenter: NSMakePoint(selectedItemMaxX+1-7, 8+7.5)
                                     radius: 7
                                 startAngle: 270
                                   endAngle: 0
                                  clockwise: NO];
    [shadowPath lineToPoint: NSMakePoint(selectedItemMaxX+1, height-1)];
    [shadowPath setLineWidth: 1.0];
    [[NSColor colorWithDeviceWhite: 215.0/256 alpha: 0.8] set];
    [shadowPath stroke];
    [NSBezierPath strokeLineFromPoint: NSMakePoint(selectedItemMinX+2, 10) toPoint: NSMakePoint(selectedItemMaxX-2, 10)];
    
    [[NSColor colorWithDeviceWhite: 184.0/256 alpha: 1.0] set];
    [path setLineWidth: 1.0];
    [path stroke];
  }
  
  [label drawAtPoint: NSMakePoint(NSMinX(tabRect) + (NSWidth(tabRect) - labelSize.width) / 2, NSMinY(tabRect) + 9 + (NSHeight(tabRect)-8-labelSize.height)/2)
      withAttributes: mLabelAttributes];

  if (mHoverItem == item)
  {
    NSImage *image = mClosePressed ? CloseButtonImagePressed : CloseButtonImageUnpressed;
    mCloseButtonRect = NSMakeRect(NSMaxX(tabRect)-16, 9+4, [image size].width, [image size].height);
    [image drawInRect: mCloseButtonRect fromRect: NSMakeRect(0, 0, [image size].width, [image size].height)
            operation: NSCompositeSourceOver fraction: 1.0];
  }
  
  return NSWidth(tabRect);
}


- (void)drawRect:(NSRect)rect
{
  rect = [self bounds];
  float padding = [self _sizeOfTabViewItem:nil].width;
  
  switch (mStyle)
  {
    case MSectionTabSwitcher:
    {
      [[[[NSGradient alloc] initWithStartingColor: [NSColor colorWithDeviceWhite: 225/255.0 alpha:1.0]
                                      endingColor: [NSColor colorWithDeviceWhite: 247/255.0 alpha:1.0]] autorelease]
       drawInBezierPath: [NSBezierPath bezierPathWithRect: rect]
       angle: 270];
      
      if (!mUnselected)
      {
        NSRect tabArea = rect;
        tabArea.origin.x = floor(NSMinX(rect)) + padding;
        tabArea.size.width -= 2 * padding;
        for (NSTabViewItem *item in [mTabView tabViewItems])
        {
          float w = [self drawSectionTabStyleInFrame: tabArea forItem: item];
          tabArea.origin.x += w;
          tabArea.size.width -= w;
        }
      }
      [[NSColor colorWithDeviceWhite: 236/256.0 alpha: 1.0] set];
      [NSBezierPath strokeLineFromPoint: NSMakePoint(NSMinX(rect), NSMaxY(rect)-0.5) toPoint: NSMakePoint(NSMaxX(rect), NSMaxY(rect)-0.5)];

      [[NSColor colorWithDeviceWhite: 175/256.0 alpha: 1.0] set];
      [NSBezierPath strokeLineFromPoint: NSMakePoint(NSMinX(rect), NSMinY(rect)+0.5) toPoint: NSMakePoint(NSMaxX(rect), NSMinY(rect)+0.5)];
      break;
    }
      
    case MPaletteTabSwitcher:
    case MPaletteTabSwitcherSmallText:
    {
      [[[[NSGradient alloc] initWithColorsAndLocations:
          [NSColor colorWithDeviceWhite: 0xbb/255.0 alpha:1.0], (CGFloat)0.0,
          [NSColor colorWithDeviceWhite: 0xc9/255.0 alpha:1.0], (CGFloat)0.53,
          [NSColor colorWithDeviceWhite: 0xd4/255.0 alpha:1.0], (CGFloat)0.77,
          [NSColor colorWithDeviceWhite: 0xdb/255.0 alpha:1.0], (CGFloat)1.0,
          nil] autorelease]
       drawInBezierPath: [NSBezierPath bezierPathWithRect: rect]
       angle: 90];
      
      NSRect tabArea = rect;
      tabArea.origin.x = floor(NSMinX(rect)) + padding;
      tabArea.size.width -= 2 * padding;
      for (NSTabViewItem *item in [mTabView tabViewItems])
      {
        if (![[item view] isHidden])
        {
          float w = [self drawPaletteTabStyleInFrame: tabArea forItem: item];
          tabArea.origin.x += w;
          tabArea.size.width -= w;
        }
      }
      [[NSColor colorWithDeviceWhite: 0x7e/256.0 alpha: 1.0] set];
      [NSBezierPath strokeLineFromPoint: NSMakePoint(NSMinX(rect), NSMinY(rect)+0.5) toPoint: NSMakePoint(NSMaxX(rect), NSMinY(rect)+0.5)];
      break;
    }
      
    case MEditorTabSwitcher:
    {
      NSArray *items = [mTabView tabViewItems];
      { // draw background
        NSImage *image = [EditorTabImages objectForKey: @"bg"];
        NSRect r = rect;
        r.size = [image size];
        for (r.origin.x = 0.0; r.origin.x < NSWidth(rect); r.origin.x += [image size].width)
          [image drawInRect: r fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0];
      }
      NSTabViewItem *previous, *current;
      NSTabViewItem *activeTab = nil;
      NSRect activeTabRect;
      int i;
      NSRect tabArea = rect;
      tabArea.origin.x = floor(NSMinX(rect)) + padding;
      tabArea.size.width -= 2 * padding + mReservedSpace;
      
      i = 0;
      previous = nil;
      current = 0 < [items count] ? [items objectAtIndex: 0] : nil;
      while (current)
      {
        if ((mHoverItem != current || !mDraggingTab) && [current tabState] != NSSelectedTab)
        {
          // if tab's don't fit, we need to adjust the size of the last tab to use the exact space left
          if (mMaxTabWidth > 0.0 && i == [items count] - 1)
            tabArea.size.width = NSWidth(rect) - NSMinX(tabArea);  
          float w = [self drawEditorTabStyleInFrame: tabArea 
                                            forItem: current 
                                       previousItem: previous];
          tabArea.origin.x += w;
          tabArea.size.width -= w;
        }
        else
        { // this tab is being dragged or is the active one, so we draw it later
          float w = [self _sizeOfTabViewItem: current].width;
          if (mMaxTabWidth > 0.0 && i == [items count] - 1)
            w = NSWidth(rect) - NSMinX(tabArea);
          activeTabRect = tabArea;
          tabArea.origin.x += w;
          tabArea.size.width -= w;
          activeTab = current;
          current = nil;
        }
        previous = current;
        current = ++i < [items count] ? [items objectAtIndex: i] : nil;
      }      
      if (activeTab)
      {
        tabArea = activeTabRect;
        if (mDraggingTab)
          tabArea.origin.x = mTabDragPosition.x - mClickTabOffset.x;
        tabArea.origin.x += [self drawEditorTabStyleInFrame: tabArea forItem: activeTab previousItem: nil];
      }
      break;
    }
      
    case MEditorBottomTabSwitcher:
    {
      [[NSColor colorWithDeviceWhite: 230/256.0 alpha: 1.0] set];
      NSRectFill(rect);

      [[NSColor colorWithDeviceWhite: 187/256.0 alpha: 1.0] set];
      [NSBezierPath strokeLineFromPoint: NSMakePoint(NSMinX(rect), NSMaxY(rect)-0.5) toPoint: NSMakePoint(NSMaxX(rect), NSMaxY(rect)-0.5)];
      [NSBezierPath strokeLineFromPoint: NSMakePoint(NSMinX(rect), NSMinY(rect)+0.5) toPoint: NSMakePoint(NSMaxX(rect), NSMinY(rect)+0.5)];
      
      NSRect tabArea = rect;
      tabArea.origin.x = floor(NSMinX(rect)) + padding;
      tabArea.size.width -= 2 * padding - mReservedSpace;
      for (NSTabViewItem *item in [mTabView tabViewItems])
      {
        if (mHoverItem != item || !mDraggingTab)
        {
          float w = [self drawEditorBottomTabStyleInFrame: tabArea forItem: item];
          tabArea.origin.x += w;
          tabArea.size.width -= w;
        }
        else
        {
          float w = [self _sizeOfTabViewItem: item].width;
          tabArea.origin.x += w;
          tabArea.size.width -= w;
        }
      }

      if (mHoverItem && mDraggingTab)
      {
        tabArea.origin.x = mTabDragPosition.x - mClickTabOffset.x;
        tabArea.size.width = [self _sizeOfTabViewItem: mHoverItem].width;
        [self drawEditorBottomTabStyleInFrame: tabArea forItem: mHoverItem];
      }
      break;
    }
    case MMainTabSwitcher:
    {
      [super drawRect: rect];
      
      NSRect selectedTabRect = NSZeroRect;
      NSRect tabArea = rect;
      tabArea.origin.x = floor(NSMinX(rect)) + padding;
      tabArea.size.width -= 2 * padding - mReservedSpace;
      for (NSTabViewItem *item in [mTabView tabViewItems])
      {
        float w;

        if ([item tabState] == NSSelectedTab)          
          selectedTabRect = tabArea;
        if (mHoverItem != item || !mDraggingTab)
        {
          w = [self drawMainTabStyleInFrame: tabArea forItem: item];
          tabArea.origin.x += w;
          tabArea.size.width -= w;
        }
        else
        {
          w = [self _sizeOfTabViewItem: item].width;
          tabArea.origin.x += w;
          tabArea.size.width -= w;
        }
        if ([item tabState] == NSSelectedTab)          
          selectedTabRect.size.width = w;
      }
      
      if (mHoverItem && mDraggingTab)
      {
        tabArea.origin.x = mTabDragPosition.x - mClickTabOffset.x;
        tabArea.size.width = [self _sizeOfTabViewItem: mHoverItem].width;
        [self drawMainTabStyleInFrame: tabArea forItem: mHoverItem];
      } 
      
      // stroke the baseline
      [[NSColor colorWithDeviceWhite: 227/255.0 alpha: 1.0] set];
      [NSBezierPath strokeLineFromPoint: NSMakePoint(0, 0.5) toPoint: NSMakePoint(NSMinX(selectedTabRect)-3, 0.5)];  
      [NSBezierPath strokeLineFromPoint: NSMakePoint(NSMaxX(selectedTabRect)+4, 0.5) toPoint: NSMakePoint(NSWidth(rect), 0.5)];
      if ([[self window] isMainWindow])
        [[NSColor colorWithDeviceWhite: 135/255.0 alpha: 1.0] set];
      else
        [[NSColor colorWithDeviceWhite: 210/255.0 alpha: 1.0] set];
      [NSBezierPath strokeLineFromPoint: NSMakePoint(0, 1.5) toPoint: NSMakePoint(NSMinX(selectedTabRect)-5, 1.5)];  
      [NSBezierPath strokeLineFromPoint: NSMakePoint(NSMaxX(selectedTabRect)+3, 1.5) toPoint: NSMakePoint(NSWidth(rect), 1.5)];      
      break;
    }
  }
}

- (void)makeUnselected
{
  mUnselected = YES;
}


- (void)didAddSubview:(NSView *)subview
{
  [self resizeSubviewsWithOldSize: NSZeroSize];
  [self setNeedsDisplay: YES];
}

- (void)didRemoveSubview:(NSView *)subview
{
  [self resizeSubviewsWithOldSize: NSZeroSize];
  [self setNeedsDisplay: YES];
}

- (void)tile
{
  [self resizeSubviewsWithOldSize: [self frame].size];
}


- (void)frameDidChange:(NSNotification*)notif
{
  {
    [self removeTrackingArea: mTrack];
    [mTrack release];
    mTrack = [[NSTrackingArea alloc] initWithRect: [self bounds]
                                          options: NSTrackingMouseEnteredAndExited|NSTrackingMouseMoved|NSTrackingActiveAlways
                                            owner: self
                                         userInfo: nil];
    [self addTrackingArea: mTrack];
  }
  
  float availableWidth = NSWidth([self frame]) - mReservedSpace;

  if (mStyle != MEditorTabSwitcher)
    return;
  
  mMaxTabWidth = 0;
  mMinTabWidth = mDefaultMinTabWidth;
  
  for (id tag in mToolTipTags)
    [self removeToolTip: [tag intValue]];
  [mToolTipTags removeAllObjects];

  float usedTabWidth = [self _sizeOfTabViewItem: nil].width * 2;
  int skip = mFirstVisibleTab;
  int tabCount = 0;
  NSMutableArray *sizeArray = [NSMutableArray array];
  for (NSTabViewItem *item in [mTabView tabViewItems])
  {
    if ([[item view] isHidden]) continue;
    if (skip > 0)
    {
      skip--;
      continue;
    }
    float w = [self _sizeOfTabViewItem: item].width;
    [sizeArray addObject: [NSNumber numberWithFloat: w]];
    usedTabWidth += w;
    tabCount++;
  }
  [sizeArray sortUsingSelector: @selector(compare:)];
  
  // there are more tabs than they'll fit, so try to shrink the biggest ones til they're
  // already at the min size
  if (usedTabWidth > availableWidth && tabCount > 0)
  {
    float usedWidth = 0.0;
    float w = 0.0;
    int itemsLeft = [sizeArray count];
    for (NSNumber *n in sizeArray)
    {
      w = [n floatValue];
      if (usedWidth + itemsLeft * w > availableWidth)
      {
        mMaxTabWidth = floor((availableWidth - usedWidth) / itemsLeft);
        break;
      }
      itemsLeft--;
      usedWidth += w;
    }
    // don't fit anymore
    if (mMaxTabWidth == 0)
    {
      NSLog(@"Not enough space to fit tabs");
    }
  }
  else
  {
    mMinTabWidth = mDefaultMinTabWidth;
    mMaxTabWidth = 0;
  }
  [self setNeedsDisplay: YES];
  
  for (NSTabViewItem *item in [mTabView tabViewItems])
  {
    if ([[item view] isHidden]) continue;
    if (skip > 0)
    {
      skip--;
      continue;
    }
    [mToolTipTags addObject: [NSNumber numberWithInteger: [self addToolTipRect: [self _tabItemRect: item] owner: self userData: item]]];
  }
}


- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)userData
{
  NSTabViewItem *item = [self tabViewItemAtPoint: point];
  if (item && [mDelegate respondsToSelector: @selector(tabView:toolTipForItem:)])
    return [mDelegate tabView: mTabView toolTipForItem: item];
  return nil;
}


- (void)resizeSubviewsWithOldSize:(NSSize)oldSize
{ 
  // layout the additional views (like embedded Apply/Cancel buttons)
  mReservedSpace = 0;
  int count = 0;
  for (NSView *item in [self subviews])
  {
    if (![item isHidden] && item != mBusyTabIndicator)
    {
      count++;
      mReservedSpace += NSWidth([item frame]);
    }
  }
  if (count > 0)
  {
    mReservedSpace += 6 * (count - 1) + 16;
    
    float x = NSWidth([self frame]) - 16;
    for (NSView *item in [[self subviews] reverseObjectEnumerator])
    {
      if (![item isHidden] && item != mBusyTabIndicator)
      {
        NSRect r = [item frame];
        x -= NSWidth(r);
        r.origin.x = x;
        r.origin.y = (NSHeight([self frame]) - NSHeight(r)) / 2;
        [item setFrame: r];
        x -= 6;
      }
    }
  }
//  [self frameDidChange: nil];
}

#pragma mark Event Handlers


- (void)mouseDown:(NSEvent *)theEvent
{
  NSPoint clickPos = [self convertPoint: [theEvent locationInWindow] fromView: nil];
  NSTabViewItem *item = [self tabViewItemAtPoint: clickPos];
  mClosePressed = NO;
  mClickedItem = item;
  if (item)
  {
    if (mHoverItem && item != mBusyTab && NSPointInRect(clickPos, mCloseButtonRect) &&
        ([mDelegate respondsToSelector: @selector(tabView:itemHasCloseButton:)] &&
         [mDelegate tabView: mTabView itemHasCloseButton: item]))
      mClosePressed = YES;
    else
      [mTabView selectTabViewItem: item];

    NSRect tabRect = [self _tabItemRect: item];
    mClickTabOffset = clickPos;
    mClickTabOffset.x -= tabRect.origin.x;
    
    [self setNeedsDisplay: YES];
  }
}


- (void)mouseUp:(NSEvent *)theEvent
{
  if (!mInside)
    mHoverItem = nil;
  
  if (mClosePressed && mHoverItem && NSPointInRect([self convertPoint: [theEvent locationInWindow] fromView: nil], mCloseButtonRect))
  {
    NSTabViewItem *item = mHoverItem;
    [self setNeedsDisplay: YES];
    if ([mDelegate respondsToSelector: @selector(tabView:willCloseTabViewItem:)] &&
        [mDelegate tabView: mTabView willCloseTabViewItem: item] && item != mBusyTab)
    {
      [mTabView removeTabViewItem: item];
      mHoverItem = nil;
    }
  }
  if (mBusyTab)
  {
    [mBusyTabIndicator setHidden: NO];
    [self setBusyTab: mBusyTab]; // force update of indicator pos
  }
  mDraggingTab = NO;
  mClosePressed = NO;
  [self setNeedsDisplay: YES];
}


- (void)closeTabViewItem: (NSTabViewItem*)item
{
  if ([mDelegate respondsToSelector: @selector(tabView:willCloseTabViewItem:)] &&
      [mDelegate tabView: mTabView willCloseTabViewItem: item])
  {
    [mTabView removeTabViewItem: item];
    [self setNeedsDisplay: YES];
  }
}


- (IBAction)handleMenuAction:(id)sender
{
  switch ([sender tag])
  {
    case 1000: // close tab

      if (mClickedItem &&
          (![mDelegate respondsToSelector: @selector(tabView:itemHasCloseButton:)] ||
           [mDelegate tabView: mTabView itemHasCloseButton: mClickedItem]))
      {
        [self closeTabViewItem: mClickedItem];
        mClickedItem = nil;
        mHoverItem = nil;
      }
      break;
    case 1001: // close other tabs
      for (NSTabViewItem *item in [[mTabView tabViewItems] reverseObjectEnumerator])
      {
        if (item != mClickedItem)
        {
          if ((![mDelegate respondsToSelector: @selector(tabView:itemHasCloseButton:)] ||
               [mDelegate tabView: mTabView itemHasCloseButton: item]))
            [self closeTabViewItem: item];
        }
      }
      [self setNeedsDisplay: YES];
      break;
  }
}

- (void)rightMouseDown:(NSEvent *)theEvent
{
  NSPoint clickPos = [self convertPoint: [theEvent locationInWindow] fromView: nil];
  NSTabViewItem *item = [self tabViewItemAtPoint: clickPos];
  mClickedItem = item;
  if (item)
  {
    NSMenu *menu = [self menu];
    
    if (![mDelegate respondsToSelector: @selector(tabView:itemHasCloseButton:)] ||
        [mDelegate tabView: mTabView itemHasCloseButton: item])
      [[menu itemWithTag: 1000] setEnabled: YES];
    else
      [[menu itemWithTag: 1000] setEnabled: NO];
    if ([mTabView numberOfTabViewItems] > 1)
      [[menu itemWithTag: 1001] setEnabled: YES];
    else
      [[menu itemWithTag: 1001] setEnabled: NO];
    
    if ([mDelegate respondsToSelector: @selector(tabView:willDisplayMenu:forTabViewItem:)])
      [mDelegate tabView: mTabView willDisplayMenu: menu forTabViewItem: item];
    
    [NSMenu popUpContextMenu: menu withEvent: theEvent forView: self];
  }
}


- (NSTabViewItem*)clickedItem
{
  return mClickedItem;
}
       
- (void)mouseMoved:(NSEvent *)theEvent
{
  if (!mDraggingTab)
  {
    NSPoint pos = [self convertPoint: [theEvent locationInWindow] fromView: nil];
    NSTabViewItem *item = [self tabViewItemAtPoint: pos];
    mCloseHighlighted = NO;
    if (![mDelegate respondsToSelector: @selector(tabView:itemHasCloseButton:)] ||
        ![mDelegate tabView: mTabView itemHasCloseButton: item])
      item = nil;
    else if (NSPointInRect(pos, mCloseButtonRect))
      mCloseHighlighted = YES;
    mHoverItem = item;
    [self setNeedsDisplay: YES];
  }
}


- (void)mouseDragged:(NSEvent *)theEvent
{
  mClosePressed = NO;
  if (mHoverItem)
  {
    NSPoint clickPos = [self convertPoint: [theEvent locationInWindow] fromView: nil];
    NSTabViewItem *item = [self tabViewItemAtPoint: NSMakePoint(clickPos.x, mClickTabOffset.y)];
    
    if (mAllowTabReordering && [mTabView numberOfTabViewItems] > 1 && item)
    {
      [mBusyTabIndicator setHidden: YES];
      mDraggingTab = YES;
    }
    mTabDragPosition = clickPos;

    if (mHoverItem != item && mDraggingTab) // handle reordering
    {
      NSTabViewItem *draggedItem = mHoverItem;
      BOOL passedThreshold = NO;
      
      if ([mTabView indexOfTabViewItem: draggedItem] > [mTabView indexOfTabViewItem: item])
      {
        if (clickPos.x < NSMaxX([self _tabItemRect: item]) - 20)
          passedThreshold = YES;
      }
      else
      {
        if (clickPos.x > NSMinX([self _tabItemRect: item]) + 20)
          passedThreshold = YES;
      }
      
      if (passedThreshold)
      {
        mReorderingTab = YES;
        [[draggedItem retain] autorelease];
        if (item)
        {
          int idx = [mTabView indexOfTabViewItem: item];
          if (![mDelegate respondsToSelector: @selector(tabView:willReorderTabViewItem:toIndex:)] ||
              [mDelegate tabView: mTabView willReorderTabViewItem: draggedItem toIndex: idx])
          {
            [mTabView removeTabViewItem: draggedItem];
            [mTabView insertTabViewItem: draggedItem atIndex: idx];
          }
        }
        else
        {
          int idx;
          if (clickPos.x < [self _sizeOfTabViewItem: nil].width)
            idx = 0;
          else
            idx = [mTabView numberOfTabViewItems];
          if (![mDelegate respondsToSelector: @selector(tabView:willReorderTabViewItem:toIndex:)] ||
              [mDelegate tabView: mTabView willReorderTabViewItem: draggedItem toIndex: idx])
          {
            [mTabView removeTabViewItem: draggedItem];
            if (idx == 0)
              [mTabView insertTabViewItem: draggedItem atIndex: 0];
            else
              [mTabView addTabViewItem: draggedItem];
          }
        }
        [mTabView selectTabViewItem: draggedItem]; // reselect the tab since it gets unselected when removed
        mReorderingTab = NO;
      }
    }
    [self setNeedsDisplay: YES];
  }
}


- (void)mouseEntered:(NSEvent *)theEvent
{
  mInside = YES;
  if (!mDraggingTab)
  {
    NSTabViewItem *item = [self tabViewItemAtPoint: [self convertPoint: [theEvent locationInWindow] fromView: nil]];
    if ([mDelegate respondsToSelector: @selector(tabView:itemHasCloseButton:)] &&
        [mDelegate tabView: mTabView itemHasCloseButton: item])
    {
      mHoverItem = item;
      [self setNeedsDisplay: YES];
    }
  }
}


- (void)mouseExited:(NSEvent *)theEvent
{
  mInside = NO;
  if (!mDraggingTab)
  {
    [self setNeedsDisplay: YES];
    mHoverItem = nil;
  }
}


- (void)setBusyTab: (NSTabViewItem*)tab
{
  mBusyTab = tab;
  if (tab)
  {
    NSRect rect = [self _tabItemRect: tab];
    switch (mStyle)
    {
      case MEditorTabSwitcher:
        if (mBusyTabIndicator)
          [mBusyTabIndicator setFrameOrigin: NSMakePoint(NSMaxX(rect) - 18, 2)];
        else
        {
          mBusyTabIndicator = [[[NSProgressIndicator alloc] initWithFrame: NSMakeRect(NSMaxX(rect) - 18, 2, 16, 16)] autorelease];
          [mBusyTabIndicator setControlSize: NSSmallControlSize];
          [mBusyTabIndicator setStyle: NSProgressIndicatorSpinningStyle];
          [mBusyTabIndicator setIndeterminate: YES];
          [mBusyTabIndicator startAnimation: nil];
          [self addSubview: mBusyTabIndicator];
        }
        [self setNeedsDisplay: YES];
        break;
      default:
        break;
    }
  }
  else
  {
    [mBusyTabIndicator stopAnimation: nil];
    [mBusyTabIndicator removeFromSuperview];
    mBusyTabIndicator = nil;
  }
}

#pragma mark Delegate Methods

- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)aTabView
{
  [self frameDidChange: nil];
  [self setNeedsDisplay: YES];
  if ([mDelegate respondsToSelector: @selector(tabViewDidChangeNumberOfTabViewItems:)])
    [mDelegate tabViewDidChangeNumberOfTabViewItems: aTabView];

  if (!mReorderingTab)
  {
    if ([mTabView indexOfTabViewItem: mClickedItem] == NSNotFound)
      mClickedItem = nil;
    if ([mTabView indexOfTabViewItem: mHoverItem] == NSNotFound)
      mHoverItem = nil;
  }
}

- (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
{
  mUnselected = NO;
  [self setNeedsDisplay: YES];
  if ([mDelegate respondsToSelector: @selector(tabView:didSelectTabViewItem:)])
    [mDelegate tabView: aTabView didSelectTabViewItem: tabViewItem];
}

/* unfinished
#pragma mark IB Support

- (id)initWithCoder:(NSCoder *)coder
{
  self = [super initWithCoder:coder];
  if (self)
  {
    if ([coder allowsKeyedCoding])
    {
      [self setTabStyle: [[coder decodeObjectForKey: @"tabStyle"] intValue]];
    }
    else
    {
      [self setTabStyle: [[coder decodeObject] intValue]];
    }
    NSRect rect = [self frame];
    rect.origin = NSZeroPoint;
    mTrack = [[NSTrackingArea alloc] initWithRect: rect
                                          options: NSTrackingMouseEnteredAndExited|NSTrackingMouseMoved|NSTrackingActiveAlways
                                            owner: self
                                         userInfo: nil];
    [self addTrackingArea: mTrack];
  }
  return self;
}


- (void)encodeWithCoder:(NSCoder *)coder
{
  [super encodeWithCoder:coder];
  if ([coder allowsKeyedCoding])
  {
    [coder encodeObject: [NSNumber numberWithInt: mStyle]
                 forKey: @"tanStyle"];
  }
  else
  {
    [coder encodeObject: [NSNumber numberWithInt: mStyle]];
  }
}
*/

@end
