/*
HMTabView.m

Author: Makoto Kinoshita

Copyright 2004-2006 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of 
  conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SHIIRA PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

#import "HMSplitView.h"
#import "HMTabView.h"
#import "HMTabViewItem.h"
#import "HMTabItemGroupView.h"
#import "HMWindow.h"

@implementation HMTabSplitView

//--------------------------------------------------------------//
#pragma mark -- Tab view --
//--------------------------------------------------------------//

- (HMTabView*)tabView
{
    return _tabView;
}

- (void)setTabView:(HMTabView*)tabView
{
    _tabView = tabView;
}

//--------------------------------------------------------------//
#pragma mark -- Divider --
//--------------------------------------------------------------//

- (float)dividerThickness
{
    return 6.0f;
}

- (void)drawDividerInRect:(NSRect)rect
{
    static NSColor* _lineColor = nil;
    static NSColor* _highlightColor = nil;
    static NSColor* _backgroundColor = nil;
    if (!_lineColor) {
        _lineColor = [[NSColor colorWithCalibratedWhite:0.624f alpha:1.0f] retain];
        _highlightColor = [[NSColor colorWithCalibratedWhite:1.0f alpha:1.0f] retain];
        _backgroundColor = [[NSColor colorWithCalibratedWhite:0.871f alpha:1.0f] retain];
    }
    
    switch ([_tabView tabViewPosition]) {
    case HMTabViewRight: {
        // Draw line
        [_lineColor set];
        NSRectFill(NSMakeRect(rect.origin.x, rect.origin.y, 1, rect.size.height));
        
        // Draw highlight
        [_highlightColor set];
        NSRectFill(NSMakeRect(rect.origin.x + 1, rect.origin.y, 1, rect.size.height));
        
        // Draw background
        [_backgroundColor set];
        NSRectFill(NSMakeRect(rect.origin.x + 2, rect.origin.y, 4, rect.size.height));
        
        break;
    }
    case HMTabViewBottom: {
        // Draw line
        [_lineColor set];
        NSRectFill(NSMakeRect(rect.origin.x, rect.origin.y, rect.size.width, 1));
        
        // Draw highlight
        [_highlightColor set];
        NSRectFill(NSMakeRect(rect.origin.x, rect.origin.y + 1, rect.size.width, 1));
        
        // Draw background
        [_backgroundColor set];
        NSRectFill(NSMakeRect(rect.origin.x, rect.origin.y + 2, rect.size.width, 4));
        
        break;
    }
    }
}

@end

#pragma mark -

@interface HMTabView (private)

// Update layout
- (void)_updateTabItemGroupView;
- (void)_updateScrollView;

@end

@implementation HMTabView

//--------------------------------------------------------------//
#pragma mark -- Line color --
//--------------------------------------------------------------//

+ (NSColor*)lineColorWithWindow:(NSWindow*)window
{
    if ([window isMetal]) {
        return [NSColor darkGrayColor];
    }
    else {
        return [NSColor controlShadowColor];
    }
}

+ (NSColor*)highlightLineColorWithWindow:(NSWindow*)window
{
    if ([window isMetal]) {
        static NSColor* _metalHighlightColor = nil;
        if (!_metalHighlightColor) {
            _metalHighlightColor = [[NSColor colorWithCalibratedWhite:1.0f alpha:0.2f] retain];
        }
        
        return _metalHighlightColor;
    }
    else {
        static NSColor* _highlightColor = nil;
        if (!_highlightColor) {
            _highlightColor = [[NSColor colorWithCalibratedWhite:1.0f alpha:0.3f] retain];
        }
        
        return _highlightColor;
    }
}

//--------------------------------------------------------------//
#pragma mark -- Initialize --
//--------------------------------------------------------------//

- (void)_init
{
    // Initialize member variables
    _tabViewItems = [[NSMutableArray array] retain];
    _tabViewPosition = HMTabViewBottom;
//    _tabViewPosition = HMTabViewRight;
    _hasThumbnail = YES;
    _isTabItemViewShown = YES;
    _isAnimating = NO;
    _tabViewHigh = 180;
    
    _splitView = [[HMTabSplitView alloc] initWithFrame:NSZeroRect];
    [_splitView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    [_splitView setTabView:self];
    [_splitView setDelegate:self];
    
    _tabView = [[NSTabView alloc] initWithFrame:NSZeroRect];
    [_tabView setTabViewType:NSNoTabsNoBorder];
    [_tabView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    [_tabView setDelegate:self];
    [_tabView setPostsBoundsChangedNotifications:YES];
    
    _tabItemGroupView = [[HMTabItemGroupView alloc] initWithFrame:NSZeroRect tabView:self];
    
    _scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
    [_scrollView setBackgroundColor:[NSColor colorWithCalibratedWhite:0.871f alpha:1.0f]];
    [_scrollView setBorderType:NSNoBorder];
    [_scrollView setPostsBoundsChangedNotifications:YES];
    [_scrollView setDocumentView:_tabItemGroupView];
    [self _updateScrollView];
    
    // Register notifications
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(splitViewDidResize:) 
            name:NSSplitViewDidResizeSubviewsNotification object:_splitView];
    [center addObserver:self selector:@selector(viewFrameDidChange:) 
            name:NSViewFrameDidChangeNotification object:_scrollView];
    
    // Update layout
    [self updateLayout];
}

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (!self) {
        return nil;
    }
    
    // Common init
    [self _init];
    
    return self;
}

- (id)initWithCoder:(NSCoder*)coder
{
    self = [super initWithCoder:coder];
    if (!self) {
        return nil;
    }
    
    // Common init
    [self _init];
    
    return self;
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [_tabViewItems release], _tabViewItems = nil;
    [_splitView release], _splitView = nil;
    [_tabView release], _tabView = nil;
    [_scrollView release], _scrollView = nil;
    [_tabItemGroupView release], _tabItemGroupView = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Tab items --
//--------------------------------------------------------------//

- (void)addTabViewItem:(HMTabViewItem*)tabViewItem
{
    // Add tab view item
    [_tabViewItems addObject:tabViewItem];
    
    // Configure tab view
    [tabViewItem setTabView:self];
    
    // Add to tab item group
    [_tabItemGroupView addTabViewItem:tabViewItem];
    
    // Create NSTabViewItem
    NSTabViewItem*  nsItem;
    nsItem = [[NSTabViewItem alloc] initWithIdentifier:[tabViewItem identifier]];
    [nsItem setView:[tabViewItem view]];
    
    // Add NSTabViewItem
    [_tabView addTabViewItem:nsItem];
    [nsItem release];
    
    // Update itself
    [self setNeedsDisplay:YES];
}

- (void)insertTabViewItem:(HMTabViewItem*)tabViewItem atIndex:(int)index
{
    // Insert tab view item
    if (index < [_tabViewItems count]) {
        [_tabViewItems insertObject:tabViewItem atIndex:index];
    }
    else {
        [_tabViewItems addObject:tabViewItem];
    }
    
    // Configure tab view
    [tabViewItem setTabView:self];
    
    // Insert to tab item group
    [_tabItemGroupView insertTabViewItem:tabViewItem atIndex:index];
    
    // Create NSTabViewItem
    NSTabViewItem*  nsItem;
    nsItem = [[NSTabViewItem alloc] initWithIdentifier:[tabViewItem identifier]];
    [nsItem setView:[tabViewItem view]];
    
    // Insert NSTabViewItem
    if (index < [_tabView numberOfTabViewItems]) {
        [_tabView insertTabViewItem:nsItem atIndex:index];
    }
    else {
        [_tabView addTabViewItem:nsItem];
    }
    [nsItem release];
    
    // Update itself
    [self setNeedsDisplay:YES];
}

- (void)removeTabViewItem:(HMTabViewItem*)tabViewItem
{
    // Get index of tab view item
    int index;
    index = [_tabViewItems indexOfObject:tabViewItem];
    if (index == NSNotFound) {
        return;
    }
    
    // Remove tab view item
    [_tabViewItems removeObjectAtIndex:index];
    
    // Remove from tab item group
    [_tabItemGroupView removeTabViewItem:tabViewItem];
    
    // Remove NSTabViewItem
    NSTabViewItem*  nsItem;
    nsItem = [_tabView tabViewItemAtIndex:index];
    if (!nsItem) {
        return;
    }
    [_tabView removeTabViewItem:nsItem];
    
    // Update itself
    [self setNeedsDisplay:YES];
}

- (void)moveTabViewItemAtIndex:(int)index toIndex:(int)toIndex
{
    // Check argument
    if (index > [_tabViewItems count] || toIndex > [_tabViewItems count]) {
        return;
    }
    
    // Get tab view item
    HMTabViewItem*  tabViewItem;
    NSTabViewItem*  nsTabViewItem;
    tabViewItem = [_tabViewItems objectAtIndex:index];
    nsTabViewItem = [_tabView tabViewItemAtIndex:index];
    
    // Decide to index
    if (index + 1 < toIndex) {
        toIndex--;
    }
    
    // Move HMTabViewItem
    [tabViewItem retain];
    [_tabViewItems removeObjectAtIndex:index];
    [_tabViewItems insertObject:tabViewItem atIndex:toIndex];
    [tabViewItem release];
    
    // Move NSTabViewItem
    [nsTabViewItem retain];
    [_tabView removeTabViewItem:nsTabViewItem];
    [_tabView insertTabViewItem:nsTabViewItem atIndex:toIndex];
    [nsTabViewItem release];
    
    // Update itself
    [self setNeedsDisplay:YES];
    
    // Notify it
    if (_delegate && [_delegate respondsToSelector:@selector(hmTabViewDidMoveTabViewItems:)]) {
        [_delegate hmTabViewDidMoveTabViewItems:self];
    }
}

- (int)indexOfTabViewItem:(HMTabViewItem*)tabViewItem
{
    return [_tabViewItems indexOfObject:tabViewItem];
}

- (int)indexOfTabViewItemWithIdentifier:(id)identifier
{
    return 0;
}

- (int)numberOfTabViewItems
{
    return [_tabViewItems count];
}

- (HMTabViewItem*)tabViewItemAtIndex:(int)index
{
    if ([_tabViewItems count] <= index) {
        return nil;
    }
    
    return [_tabViewItems objectAtIndex:index];
}

- (NSArray*)tabViewItems
{
    return _tabViewItems;
}

- (int)indexOfCloseButton:(NSButton*)closeButton
{
    return [_tabItemGroupView indexOfCloseButton:closeButton];
}

//--------------------------------------------------------------//
#pragma mark -- Tab selection --
//--------------------------------------------------------------//

- (void)selectTabViewItem:(HMTabViewItem*)tabViewItem
{
    int index;
    index = [_tabViewItems indexOfObject:tabViewItem];
    if (index == NSNotFound) {
        return;
    }
    
    [self selectTabViewItemAtIndex:index];
}

- (void)selectTabViewItemAtIndex:(int)index
{
    // Select tab view item
    [_tabView selectTabViewItemAtIndex:index];
    
    // Get selected tab item view
    HMTabItemView*  tabItemView;
    tabItemView = [[self selectedTabViewItem] tabItemView];
    
    // Scroll to show tab item view
    NSClipView* clipView;
    NSRect      tabFrame, documentVisibleRect;
    clipView = [_scrollView contentView];
    tabFrame = [tabItemView frame];
    documentVisibleRect = [clipView documentVisibleRect];
    if (!NSContainsRect(documentVisibleRect, tabFrame)) {
        NSPoint origin;
        
        // Check left
        if (tabFrame.origin.x < documentVisibleRect.origin.x) {
            if (index == 0) {
                origin.x = 0;
                origin.y = tabFrame.origin.y;
            }
            else {
                origin = tabFrame.origin;
                origin.x -= HMTabViewThumbnailPaddingLeft;
                if (origin.x < 0) {
                    origin.x = 0;
                }
            }
            
            [_tabItemGroupView scrollPoint:origin];
        }
        
        // Check right
        else if (tabFrame.origin.x + tabFrame.size.width > 
                documentVisibleRect.origin.x + documentVisibleRect.size.width)
        {
            if (index == [self numberOfTabViewItems] - 1) {
                NSRect  frame;
                frame = [_tabItemGroupView frame];
                origin.x = frame.origin.x + frame.size.width - documentVisibleRect.size.width;
                origin.y = tabFrame.origin.y;
            }
            else {
                origin.x = tabFrame.origin.x + tabFrame.size.width - documentVisibleRect.size.width;
                origin.x += HMTabViewThumbnailPaddingRight;
                if (origin.x < 0) {
                    origin.x = 0;
                }
                origin.y = tabFrame.origin.y;
            }
            
            [_tabItemGroupView scrollPoint:origin];
        }
    }
    
    // Update itself
    [self setNeedsDisplay:YES];
}

- (void)selectTabViewItemWithIdentifier:(id)identifier
{
}

- (HMTabViewItem*)selectedTabViewItem
{
    // Get index of selected tab view item
    int index;
    index = [self indexOfSelectedTabViewItem];
    if (index == NSNotFound || [_tabViewItems count] <= index) {
        return nil;
    }
    
    return [_tabViewItems objectAtIndex:index];
}

- (int)indexOfSelectedTabViewItem
{
    // Get selected NSTabViewItem
    NSTabViewItem*  nsTabViewItem;
    nsTabViewItem = [_tabView selectedTabViewItem];
    
    // Get index of NSTabViewItem
    return [_tabView indexOfTabViewItem:nsTabViewItem];
}

//--------------------------------------------------------------//
#pragma mark -- Tab position --
//--------------------------------------------------------------//

- (HMTabViewPosition)tabViewPosition
{
    return _tabViewPosition;
}

- (void)setTabViewPoision:(HMTabViewPosition)tabViewPosition
{
    _tabViewPosition = tabViewPosition;
    
    [self _updateScrollView];
}

//--------------------------------------------------------------//
#pragma mark -- Separator and tab --
//--------------------------------------------------------------//

- (BOOL)hasThumbnail
{
    return _hasThumbnail;
}

- (void)setHasThumbnail:(BOOL)hasThumbnail
{
    _hasThumbnail = hasThumbnail;
}

- (BOOL)isTabItemViewShown
{
    return _isTabItemViewShown;
}

- (void)setTabItemViewShown:(BOOL)isShown
{
    _isTabItemViewShown = isShown;
}

- (int)tabItemViewHigh
{
    return _tabViewHigh;
}

- (void)setTabItemViewHigh:(int)high
{
    _tabViewHigh = high;
    
    NSRect  frame;
    frame = [_scrollView frame];
    
    switch (_tabViewPosition) {
    case HMTabViewTop:
    case HMTabViewBottom: {
        frame.size.height = high;
    }
    case HMTabViewLeft:
    case HMTabViewRight: {
        frame.size.width = high;
    }
    }
    
    [_scrollView setFrame:frame];
}

//--------------------------------------------------------------//
#pragma mark -- Delegate --
//--------------------------------------------------------------//

- (id)delegate
{
    return _delegate;
}

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

//--------------------------------------------------------------//
#pragma mark -- Subviews --
//--------------------------------------------------------------//

- (HMTabItemGroupView*)tabItemGroupView
{
    return _tabItemGroupView;
}

- (NSTabView*)nsTabView
{
    return _tabView;
}

- (HMSplitView*)splitView
{
    return _splitView;
}

//--------------------------------------------------------------//
#pragma mark -- Layout --
//--------------------------------------------------------------//

- (void)_updateTabItemViewWithThumbnailLayout
{
    // Get frame
    NSRect  frame;
    frame = [self frame];
    
    // Remove from superview
    NSView* superview;
    superview = [_splitView superview];
    if (superview && superview != self) {
        [_splitView removeFromSuperview];
    }
    
    superview = [_tabView superview];
    if (superview && superview != _splitView) {
        [_tabView removeFromSuperview];
    }
    
    superview = [_scrollView superview];
    if (superview && superview != _splitView) {
        [_scrollView removeFromSuperview];
    }
    
    superview = [_tabItemGroupView superview];
    if (superview && superview != _scrollView) {
        [_tabItemGroupView removeFromSuperview];
    }
    
    // Set tab item group view
    [_scrollView setDocumentView:_tabItemGroupView];
    [self _updateTabItemGroupView];
    [_tabItemGroupView updateTabFrames];
    
    // Set split view orientation and add subview
    NSRect  scrollViewFrame, rect;
    float   thickness;
    scrollViewFrame = [_scrollView frame];
    thickness = [_splitView dividerThickness];
    
    switch (_tabViewPosition) {
    case HMTabViewTop: {
        if ([_splitView isVertical]) {
            [_splitView setVertical:NO];
        }
        
        [_splitView addSubview:_scrollView];
        [_splitView addSubview:_tabView];
        [_splitView adjustSubviews];
        break;
    }
    case HMTabViewLeft: {
        if (![_splitView isVertical]) {
            [_splitView setVertical:YES];
        }
        
        [_splitView addSubview:_scrollView];
        [_splitView addSubview:_tabView];
        break;
    }
    case HMTabViewRight: {
        if (![_splitView isVertical]) {
            [_splitView setVertical:YES];
        }
        
        // Check scroll view
        float   wide;
        wide = _tabViewHigh;
        if (wide < [_tabItemGroupView minTabWide]) {
            wide = [_tabItemGroupView minTabWide];
        }
        else if (wide > [_tabItemGroupView maxTabWide]) {
            wide = [_tabItemGroupView maxTabWide];
        }
        scrollViewFrame.size.width = wide;
        
        // Split view
        rect.origin.x = 0;
        rect.origin.y = 0;
        rect.size.width = frame.size.width + scrollViewFrame.size.width + thickness;
        rect.size.height = frame.size.height - 1;
        [_splitView setFrame:rect];
        
        // Tab view
        rect.origin.x = 0;
        rect.origin.y = 0;
        rect.size.width = frame.size.width;
        rect.size.height = frame.size.height - 1;
        [_tabView setFrame:rect];
        [_splitView addSubview:_tabView];
        
        // Scroll view
        rect.origin.x = frame.size.width + thickness;
        rect.origin.y = 0;
        rect.size = scrollViewFrame.size;
        [_scrollView setFrame:rect];
        [_splitView addSubview:_scrollView];
        
        break;
    }
    case HMTabViewBottom: {
        if ([_splitView isVertical]) {
            [_splitView setVertical:NO];
        }
        
        // Check scroll view
        float   wide;
        wide = scrollViewFrame.size.height;
        if (wide < [_tabItemGroupView minTabWide]) {
            wide = [_tabItemGroupView minTabWide];
        }
        else if (wide > [_tabItemGroupView maxTabWide]) {
            wide = [_tabItemGroupView maxTabWide];
        }
        scrollViewFrame.size.height = wide;
        
        // Split view
        rect.origin.x = 0;
        rect.origin.y = -1 * (scrollViewFrame.size.height + thickness);
        rect.size.width = frame.size.width;
        rect.size.height = frame.size.height + scrollViewFrame.size.height + thickness - 1;
        [_splitView setFrame:rect];
        
        // Tab view
        rect.origin.x = 0;
        rect.origin.y = scrollViewFrame.size.height + thickness;
        rect.size.width = frame.size.width;
        rect.size.height = frame.size.height - 1;
        [_tabView setFrame:rect];
        [_splitView addSubview:_tabView];
        
        // Scroll view
        rect.origin.x = 0;
        rect.origin.y = 0;
        rect.size = scrollViewFrame.size;
        [_scrollView setFrame:rect];
        [_splitView addSubview:_scrollView];
        
        break;
    }
    }
    
    [self addSubview:_splitView];
    
    // PageDock animation
    NSRect  startFrame, endFrame;
    startFrame = [_splitView frame];
    endFrame.origin = NSZeroPoint;
    endFrame.size.width = frame.size.width;
    endFrame.size.height = frame.size.height - 1;
    
    NSViewAnimation*    animation;
    animation = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:
            [NSDictionary dictionaryWithObjectsAndKeys:
                    _splitView, NSViewAnimationTargetKey, 
                    [NSValue valueWithRect:startFrame], NSViewAnimationStartFrameKey, 
                    [NSValue valueWithRect:endFrame], NSViewAnimationEndFrameKey, 
                    nil]]];
    [animation setDuration:0.1f];
    [animation setAnimationCurve:NSAnimationEaseIn];
    [animation setAnimationBlockingMode:NSAnimationBlocking];
    [animation startAnimation];
}

- (void)_updateTabItemViewWithoutThumbnailLayout
{
    // Get frame
    NSRect  frame;
    frame = [self frame];
    
    // Remove from superview
    NSView* superview;
    superview = [_splitView superview];
    if (superview && superview == self) {
        [_splitView removeFromSuperview];
    }
    
    superview = [_tabView superview];
    if (superview && superview != self) {
        [_tabView removeFromSuperview];
    }
    
    superview = [_scrollView superview];
    if (superview && superview == self) {
        [_scrollView removeFromSuperview];
    }
    
    superview = [_tabItemGroupView superview];
    if (superview && superview == _scrollView) {
        [_tabItemGroupView removeFromSuperview];
    }
    
    // Get tab high
    float   tabHigh;
    tabHigh = [_tabItemGroupView minTabHigh];
    
    // Decide view frames
    NSRect          tabViewRect, itemGroupViewRect;
    unsigned int    itemViewMask = 0;
    switch (_tabViewPosition) {
    case HMTabViewTop: {
        tabViewRect.origin.x = 0;
        tabViewRect.origin.y = 0;
        tabViewRect.size.width = frame.size.width;
        tabViewRect.size.height = frame.size.height - tabHigh - 2;
        
        itemGroupViewRect.origin.x = 0;
        itemGroupViewRect.origin.y = frame.size.height - tabHigh - 1;
        itemGroupViewRect.size.width = frame.size.width;
        itemGroupViewRect.size.height = tabHigh;
        
        itemViewMask = NSViewWidthSizable | NSViewMinYMargin;
        break;
    }
    }
    
    // Add tab view
    [_tabView setFrame:tabViewRect];
    [self addSubview:_tabView];
    
    // Add item view
    [_tabItemGroupView setFrame:itemGroupViewRect];
    [_tabItemGroupView setAutoresizingMask:itemViewMask];
    [self addSubview:_tabItemGroupView];
}

- (void)_updateNoTabItemViewWithThumbnailLayout
{
    // Get frame
    NSRect  frame, scrollViewFrame;
    frame = [self frame];
    scrollViewFrame = [_scrollView frame];
    
    // Decide start and end frame
    NSRect  startFrame, endFrame;
    startFrame = [_splitView frame];
    
    switch (_tabViewPosition) {
    case HMTabViewBottom: {
        endFrame = startFrame;
        endFrame.origin.y -= scrollViewFrame.size.height;
        endFrame.size.height += scrollViewFrame.size.height;
        break;
    }
    case HMTabViewRight: {
        endFrame = startFrame;
        endFrame.size.width += scrollViewFrame.size.width;
        break;
    }
    }
    
    NSViewAnimation*    animation;
    animation = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:
            [NSDictionary dictionaryWithObjectsAndKeys:
                    _splitView, NSViewAnimationTargetKey, 
                    [NSValue valueWithRect:startFrame], NSViewAnimationStartFrameKey, 
                    [NSValue valueWithRect:endFrame], NSViewAnimationEndFrameKey, 
                    nil]]];
    [animation setDuration:0.2f];
    [animation setAnimationCurve:NSAnimationEaseIn];
    [animation setAnimationBlockingMode:NSAnimationBlocking];
    [animation startAnimation];
    
    // Remove from superview
    NSView* superview;
    superview = [_splitView superview];
    if (superview) {
        [_splitView removeFromSuperview];
    }
    
    superview = [_tabView superview];
    if (!superview) {
        [self addSubview:_tabView];
    }
    else if (superview != self) {
        [_tabView removeFromSuperview];
        [self addSubview:_tabView];
    }
    
    superview = [_scrollView superview];
    if (superview) {
        [_scrollView removeFromSuperview];
    }
    
    // Set tab frame
    [_tabView setFrame:startFrame];
}

- (void)_updateNoTabItemViewWithoutThumbnailLayout
{
    // Remove from superview
    NSView* superview;
    superview = [_splitView superview];
    if (superview) {
        [_splitView removeFromSuperview];
    }
    
    superview = [_tabItemGroupView superview];
    if (superview) {
        [_tabItemGroupView removeFromSuperview];
    }
    
    superview = [_tabView superview];
    if (!superview) {
        [self addSubview:_tabView];
    }
    else if (superview != self) {
        [_tabView removeFromSuperview];
        [self addSubview:_tabView];
    }
    
    superview = [_scrollView superview];
    if (superview) {
        [_scrollView removeFromSuperview];
    }
    
    // Set tab frame
    NSRect  tabFrame;
    tabFrame = [self bounds];
    tabFrame.size.height -= 1;
    [_tabView setFrame:tabFrame];
}

- (void)updateLayout
{
    // Get frame
    NSRect  frame;
    frame = [self frame];
    
    // When tab item view is shown
    if (_isTabItemViewShown) {
        // When it has thumbnail
        if (_hasThumbnail) {
            [self _updateTabItemViewWithThumbnailLayout];
        }
        // When it has no thumbnail
        else {
            [self _updateTabItemViewWithoutThumbnailLayout];
        }
    }
    
    // When tab item view is not shown
    else {
        // When it has thumbnail
        if (_hasThumbnail) {
            [self _updateNoTabItemViewWithThumbnailLayout];
        }
        // When it has no thumbnail
        else {
            [self _updateNoTabItemViewWithoutThumbnailLayout];
        }
    }
    
    // Set needs display
    [[self superview] setNeedsDisplay:YES];
}

- (void)_updateTabItemGroupView
{
    if ([_scrollView documentView] == _tabItemGroupView) {
        // Get preferred tab view item view size
        NSSize  tabSize;
        tabSize = [_tabItemGroupView preferredSize];
        
        // Get content size
        NSSize  contentSize;
        contentSize = [_scrollView contentSize];
        
        // Set tab view item view frame
        NSRect  tabFrame;
        tabFrame.origin.x = 0;
        tabFrame.origin.y = 0;
        
        switch (_tabViewPosition) {
        case HMTabViewTop:
        case HMTabViewBottom: {
            tabFrame.size.width = contentSize.width > tabSize.width ? contentSize.width : tabSize.width;
            tabFrame.size.height = contentSize.height;
            break;
        }
        case HMTabViewLeft:
        case HMTabViewRight: {
            tabFrame.size.width = contentSize.width;
            tabFrame.size.height = contentSize.height > tabSize.height ? contentSize.height : tabSize.height;
            break;
        }
        }
        
        [_tabItemGroupView setFrame:tabFrame];
        
        // Update label window
        [_tabItemGroupView updateLabelWindow];
    }
}

- (void)_updateScrollView
{
    // Set scrollbar visiblity
    switch (_tabViewPosition) {
    case HMTabViewTop:
    case HMTabViewBottom: {
        [_scrollView setHasHorizontalScroller:YES];
        [_scrollView setHasVerticalScroller:NO];
        break;
    }
    case HMTabViewLeft:
    case HMTabViewRight: {
        [_scrollView setHasHorizontalScroller:NO];
        [_scrollView setHasVerticalScroller:YES];
        break;
    }
    }
}

- (void)_updateSubviewsOfSplitView
{
    // Get frame and divider thickness
    NSRect  frame, tabFrame, scrollViewFrame;
    float   thickness;
    frame = [_splitView frame];
    tabFrame = [_tabView frame];
    scrollViewFrame = [_scrollView frame];
    thickness = [_splitView dividerThickness];
    
    if (NSIsEmptyRect(frame)) {
        return;
    }
    
    // Switch by position
    switch (_tabViewPosition) {
    case HMTabViewRight: {
        // Decide tab frame
        tabFrame.origin.x = 0;
        tabFrame.origin.y = 0;
        tabFrame.size.width = frame.size.width - thickness - scrollViewFrame.size.width;
        tabFrame.size.height = frame.size.height;
        
        // Decide tab item frame
        scrollViewFrame.origin.x = frame.size.width - scrollViewFrame.size.width;
        scrollViewFrame.origin.y = 0;
        scrollViewFrame.size.width = _tabViewHigh;
        scrollViewFrame.size.height = frame.size.height;
        
        break;
    }
    case HMTabViewBottom: {
        // Decide tab frame
        tabFrame.origin.x = 0;
        tabFrame.origin.y = 0;
        tabFrame.size.width = frame.size.width;
        tabFrame.size.height = frame.size.height - thickness - scrollViewFrame.size.height;
        
        // Decide tab item frame
        scrollViewFrame.origin.y = frame.size.height - scrollViewFrame.size.height;
        scrollViewFrame.size.width = frame.size.width;
        scrollViewFrame.size.height = _tabViewHigh;
        
        break;
    }
    }
    
    // Set subframes
    [_tabView setFrame:tabFrame];
    [_scrollView setFrame:scrollViewFrame];
}

//--------------------------------------------------------------//
#pragma mark -- Drawing --
//--------------------------------------------------------------//

- (void)drawRect:(NSRect)rect
{
    [[HMTabView lineColorWithWindow:[self window]] set];
    NSRectFill(rect);
}

//--------------------------------------------------------------//
#pragma mark -- NSSplitView delegate --
//--------------------------------------------------------------//

- (float)splitView:(NSSplitView*)splitView 
        constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)offset
{
    // Get frame and divider thickness
    NSRect  frame;
    float   thickness;
    frame = [splitView frame];
    thickness = [splitView dividerThickness];
    
    // Switch by position
    switch (_tabViewPosition) {
    case HMTabViewBottom: {
        return frame.size.height - [_tabItemGroupView maxTabHigh] - thickness;
    }
    }
    
    return proposedMin;
}

- (float)splitView:(NSSplitView*)splitView 
        constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)offset
{
    // Get frame and divider thickness
    NSRect  frame;
    float   thickness;
    frame = [splitView frame];
    thickness = [splitView dividerThickness];
    
    // Switch by position
    switch (_tabViewPosition) {
    case HMTabViewBottom: {
        return frame.size.height - [_tabItemGroupView minTabHigh] - thickness;
    }
    }
    
    return proposedMax;
}

- (void)splitView:(NSSplitView*)splitView resizeSubviewsWithOldSize:(NSSize)oldSize
{
    [self _updateSubviewsOfSplitView];
}

- (void)splitViewStartDragging:(NSSplitView*)splitView
{
    // Make thumgnail image interpolation low
    NSEnumerator*   enumerator;
    HMTabViewItem*  tabViewItem;
    enumerator = [_tabViewItems objectEnumerator];
    while (tabViewItem = [enumerator nextObject]) {
//        [tabViewItem setThumbnailInterpolation:NSImageInterpolationLow];
    }
    
    // Notify to delegate
    if (_delegate && [_delegate respondsToSelector:@selector(hmTabViewStartDragging:)]) {
        [_delegate hmTabViewStartDragging:self];
    }
}

- (void)splitViewEndDragging:(NSSplitView*)splitView
{
    // Make thumgnail image interpolation high
    NSEnumerator*   enumerator;
    HMTabViewItem*  tabViewItem;
    enumerator = [_tabViewItems objectEnumerator];
    while (tabViewItem = [enumerator nextObject]) {
//        [tabViewItem setThumbnailInterpolation:NSImageInterpolationHigh];
    }
    
    // Notify to delegate
    if (_delegate && [_delegate respondsToSelector:@selector(hmTabViewEndDragging:)]) {
        [_delegate hmTabViewEndDragging:self];
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSSplitView notification --
//--------------------------------------------------------------//

- (void)splitViewDidResize:(NSNotification*)notification
{
    // Set tab view high
    NSRect  scrollViewFrame;
    scrollViewFrame = [_scrollView frame];
    
    switch (_tabViewPosition) {
    case HMTabViewTop:
    case HMTabViewBottom: {
        _tabViewHigh = scrollViewFrame.size.height;
        break;
    }
    case HMTabViewLeft:
    case HMTabViewRight: {
        _tabViewHigh = scrollViewFrame.size.width;
        break;
    }
    }
    
    [self _updateSubviewsOfSplitView];
}

//--------------------------------------------------------------//
#pragma mark -- NSTabView delegate --
//--------------------------------------------------------------//

- (BOOL)tabView:(NSTabView*)tabView shouldSelectTabViewItem:(NSTabViewItem*)tabViewItem
{
    if (_delegate && [_delegate respondsToSelector:@selector(hmTabView:shouldSelectTabViewItem:)]) {
        HMTabViewItem*  item;
        item = [self tabViewItemAtIndex:[tabView indexOfTabViewItem:tabViewItem]];
        return [_delegate hmTabView:self shouldSelectTabViewItem:item];
    }
    return YES;
}

- (void)tabView:(NSTabView*)tabView willSelectTabViewItem:(NSTabViewItem*)tabViewItem
{
    if (_delegate && [_delegate respondsToSelector:@selector(hmTabView:willSelectTabViewItem:)]) {
        HMTabViewItem*  item;
        item = [self tabViewItemAtIndex:[tabView indexOfTabViewItem:tabViewItem]];
        [_delegate hmTabView:self willSelectTabViewItem:item];
    }
}

- (void)tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem
{
    if (_delegate && [_delegate respondsToSelector:@selector(hmTabView:didSelectTabViewItem:)]) {
        HMTabViewItem*  item;
        item = [self tabViewItemAtIndex:[tabView indexOfTabViewItem:tabViewItem]];
        [_delegate hmTabView:self didSelectTabViewItem:item];
    }
}

- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView*)tabView
{
    // Update tab view item view
    [self _updateTabItemGroupView];
    
    if (_delegate && [_delegate respondsToSelector:@selector(hmTabViewDidChangeNumberOfTabViewItems:)]) {
        [_delegate hmTabViewDidChangeNumberOfTabViewItems:self];
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSView notification --
//--------------------------------------------------------------//

- (void)viewFrameDidChange:(NSNotification*)notification
{
    // Get view
    NSView* view;
    view = [notification object];
    
    // Scroll view
    if (view == _scrollView) {
        // Update tab item group view
        [self _updateTabItemGroupView];
    }
}

@end
