/*
SRTabExposeController.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 "SRBrowserController.h"
#import "SRPageController.h"
#import "SRTabExposeController.h"

@interface SRDummyWebView : NSView
{
}
@end

@implementation SRDummyWebView

- (BOOL)canGoBack
{
    return NO;
}

- (BOOL)canGoForward
{
    return NO;
}

- (BOOL)canMakeTextLarger
{
    return NO;
}

- (BOOL)canMakeTextSmaller
{
    return NO;
}

- (WebFrame*)mainFrame
{
    return nil;
}

@end

#pragma mark -

@implementation SRExposeTitle

- (void)_deleteTexture
{
    if (!_textureId) {
        return;
    }
    
    // Delete texture
    glDeleteTextures(1, &_textureId);
    _textureId = 0;
    _size = NSZeroSize;
}

//--------------------------------------------------------------//
#pragma mark -- Title --
//--------------------------------------------------------------//

- (void)setTitle:(NSString*)title subTitle:(NSString*)subTitle
{
    // Delete texture
	[self _deleteTexture];
    
    // Check title length
    if ([title length] > 64) {
        title = HMTruncateString(title, 64, NSLineBreakByTruncatingMiddle);
    }
    if ([subTitle length] > 64) {
        subTitle = HMTruncateString(subTitle, 80, NSLineBreakByTruncatingTail);
    }
    
    // Create attributed string
    static NSDictionary*    _attr = nil;
    static NSDictionary*    _subAttr = nil;
    if (!_attr) {
        NSShadow*   shadow;
        shadow = [[NSShadow alloc] init];
        [shadow setShadowColor:[NSColor blackColor]];
        [shadow setShadowOffset:NSMakeSize(0, -1)];
        [shadow setShadowBlurRadius:0.0f];
        
        _attr = [[NSDictionary dictionaryWithObjectsAndKeys:
                [NSFont boldSystemFontOfSize:16.0f], NSFontAttributeName, 
                [NSColor whiteColor], NSForegroundColorAttributeName, 
                shadow, NSShadowAttributeName, 
                nil] retain];
        
        _subAttr = [[NSDictionary dictionaryWithObjectsAndKeys:
                [NSFont systemFontOfSize:11.0f], NSFontAttributeName, 
                [NSColor whiteColor], NSForegroundColorAttributeName, 
                nil] retain];
    }
    
    [_titleAttr release], _titleAttr = nil;
    if (title && [title length] > 0) {
        _titleAttr = [[NSAttributedString alloc] initWithString:title attributes:_attr];
    }
    
    [_subTitleAttr release], _subTitleAttr = nil;
    if (subTitle && [subTitle length] > 0) {
        _subTitleAttr = [[NSAttributedString alloc] initWithString:subTitle attributes:_subAttr];
    }
}

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

- (void)_generateTextureWithFrame:(NSRect)frame
{
    // Delete current texture
    [self _deleteTexture];
    
    // Check title
    if (!_titleAttr || [_titleAttr length] == 0) {
        return;
    }
    
    static int  _XPadding = 16;
    static int  _YPadding = 8;
    
    // Decide size
    NSSize          titleSize, subTitleSize = NSZeroSize;
    float           width, height;
    NSRect          backFrame;
    NSBezierPath*   backPath;
    titleSize = [_titleAttr size];
    if (_subTitleAttr) {
        subTitleSize = [_subTitleAttr size];
    }
    
    width = fmax(titleSize.width, subTitleSize.width) + 2 * _XPadding;
    height = titleSize.height + 2 * _YPadding;
    if (subTitleSize.height > 0) {
        height += subTitleSize.height + 4;
    }
    
    _size.width = width + 2;
    _size.height = height + 2;
    
    // Create image
    NSImage*            image;
    NSBitmapImageRep*   bitmapRep;
    image = [[NSImage alloc] initWithSize:_size];
    [image lockFocus];
    
    // Draw background
    backFrame.origin.x = 1;
    backFrame.origin.y = 1;
    backFrame.size.width = width;
    backFrame.size.height = height;
    backPath = [NSBezierPath ellipseInRect:backFrame withRadius:8.0f];
    
    [[NSColor colorWithCalibratedWhite:0 alpha:0.7f] set];
    [backPath fill];
    
    [[NSColor whiteColor] set];
    [backPath setLineWidth:2.0f];
    [backPath stroke];
    
    // Draw title
    NSPoint point;
    point = NSMakePoint(backFrame.origin.x + _XPadding, 
                    backFrame.origin.y + backFrame.size.height - _YPadding - titleSize.height);
    [_titleAttr drawAtPoint:point];
    point = NSMakePoint(backFrame.origin.x + _XPadding, 
                    backFrame.origin.y + _YPadding);
    [_subTitleAttr drawAtPoint:point];
    
    bitmapRep = [[NSBitmapImageRep alloc] 
            initWithFocusedViewRect:NSMakeRect(0, 0, _size.width, _size.height)];
    [image unlockFocus];
    
    // Create texture
    glEnable(GL_TEXTURE_RECTANGLE_EXT);
    glGenTextures(1, &_textureId);
    glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _textureId);
    glTexImage2D(
            GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, 
            _size.width, _size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, [bitmapRep bitmapData]);
    glDisable(GL_TEXTURE_RECTANGLE_EXT);
    [bitmapRep release];
    [image release];
}

- (void)drawTextureInWindowFrame:(NSRect)windowFrame screenFrame:(NSRect)screenFrame
{
    // Generate texture
    if (!_textureId) {
        [self _generateTextureWithFrame:windowFrame];
    }
    
    // Decide position
    NSRect  frame;
    frame.origin.x = floorf(windowFrame.origin.x + (windowFrame.size.width - _size.width) / 2.0f);
    frame.origin.y = floorf(windowFrame.origin.y + (windowFrame.size.height - _size.height) / 2.0f);
    frame.size = _size;
    
    // Draw texture
    glEnable(GL_TEXTURE_RECTANGLE_EXT);
    glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _textureId);
    glBegin(GL_QUADS);
        // Upper left
        glTexCoord2f(0.0f, 0.0f);
        glVertex2f(frame.origin.x, frame.origin.y);
        
        // Lower left
        glTexCoord2f(0.0f, _size.height);
        glVertex2f(frame.origin.x, frame.origin.y + frame.size.height);
        
        // Upper right
        glTexCoord2f(_size.width, _size.height);
        glVertex2f(frame.origin.x + frame.size.width, frame.origin.y + frame.size.height);
        
        // Lower right
        glTexCoord2f(_size.width, 0.0f);
        glVertex2f(frame.origin.x + frame.size.width, frame.origin.y);
    glEnd();
    glDisable(GL_TEXTURE_RECTANGLE_EXT);
}

@end

#pragma mark -

@implementation SRExposeView

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

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (!self) {
        return nil;
    }
    
    // Initialize instance variables
    _images = [[NSMutableArray array] retain];
    _title = [[SRExposeTitle alloc] initWithAttributedString:nil];
    _selectedIndex = -1;
    _lastIndex = -1;
    
    return self;
}

- (void)dealloc
{
    [_images release], _images = nil;
    [_title release], _title = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Accessors --
//--------------------------------------------------------------//

- (NSArray*)images
{
    return _images;
}

- (void)setImages:(NSArray*)images
{
    [_images removeAllObjects];
    [_images addObjectsFromArray:images];
    
    _lastIndex = -1;
}

- (void)setSelectedIndex:(int)selectedIndex
{
    _selectedIndex = selectedIndex;
    _lastIndex = -1;
}

//--------------------------------------------------------------//
#pragma mark -- Mouse handling --
//--------------------------------------------------------------//

- (int)_indexOfFrameWithEvent:(NSEvent*)event
{
    // Get mouse location
    NSPoint mouseLoc;
    mouseLoc = [[event window] convertBaseToScreen:[event locationInWindow]];
    
    // Find frame under mouse
    NSArray*    frames;
    frames = [[SRTabExposeController currentController] finalFrames];
    
    int i;
    for (i = 0; i < [frames count]; i++) {
        // Get frame
        NSRect  frame;
        frame = [[frames objectAtIndex:i] rectValue];
        if (NSPointInRect(mouseLoc, frame)) {
            return i;
        }
    }
    
    return -1;
}

- (void)mouseDown:(NSEvent*)event
{
    // Get index under mouse
    int index;
    index = [self _indexOfFrameWithEvent:event];
    
    // Clear selected color
    int i;
    for (i = 0; i < [_images count]; i++) {
        // Get image
        HMGLImage*  image;
        image = [_images objectAtIndex:i];
        
        // For selected image
        [image setRed:1.0f blue:1.0f greeen:1.0f];
    }
    
    // Deexpose
    if (index != -1) {
        [[SRTabExposeController currentController] deexposeAndSelectAtIndex:index];
        return;
    }
    [[SRTabExposeController currentController] deexpose];
}

- (void)mouseMoved:(NSEvent*)event
{
    // Get index under mouse
    int index;
    index = [self _indexOfFrameWithEvent:event];
    if (index == _lastIndex) {
        return;
    }
    _lastIndex = index;
    
    // Set selected color
    int i;
    for (i = 0; i < [_images count]; i++) {
        // Get image
        HMGLImage*  image;
        image = [_images objectAtIndex:i];
        
        // For selected image
        if (i == index) {
            [image setRed:0.5f blue:0.5f greeen:0.5f];
        }
        else {
            [image setRed:1.0f blue:1.0f greeen:1.0f];
        }
    }
    
    // Get selected title
    SRPageController*   pageController;
    NSString*           title;
    NSString*           subTitle;
    pageController = [[[SRTabExposeController currentController]browserController] pageControllerAtIndex:index];
    title = [[pageController tabViewItem] label];
    subTitle = [[[[[[pageController webView] mainFrame] dataSource] request] URL] absoluteString];
    
    // Set title
    [_title setTitle:title subTitle:subTitle];
    
    // Update appearance
    [self setNeedsDisplay:YES];
}

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

- (void)drawTextureInRect:(NSRect)rect
{
    SRTabExposeController*  exposeController;
    exposeController = [SRTabExposeController currentController];
    
    // Get current frames
    NSArray*    currentFrames;
    currentFrames = [exposeController currentFrames];
    if ([currentFrames count] == 0) {
        return;
    }
    
    // Dceide alpha
    float   alpha;
    alpha = [exposeController progress];
    if ([exposeController exposeState] == SRTabExposeExposingOut) {
        alpha = 1 - alpha;
    }
    
    // Draw images
    int i;
    for (i = 0; i < [currentFrames count]; i++) {
        // Skip selected index
        if (i == _selectedIndex) {
            continue;
        }
        
        // Get frame
        NSValue*    frameValue;
        NSRect      frame;
        frameValue = [currentFrames objectAtIndex:i];
        frame = [frameValue rectValue];
        
        // Get image
        HMGLImage*  image = nil;
        if (i < [_images count]) {
            image = [_images objectAtIndex:i];
        }
        
        // Draw image
        if (image) {
            [image drawInRect:frame fraction:alpha];
        }
    }
    
    if (_selectedIndex >=  0 && _selectedIndex < [_images count]) {
        NSValue*    frameValue;
        NSRect      frame;
        frameValue = [currentFrames objectAtIndex:_selectedIndex];
        frame = [frameValue rectValue];
        
        [[_images objectAtIndex:_selectedIndex] drawInRect:frame fraction:1.0f];
    }
    
    // Draw title
    if (_lastIndex != -1) {
        NSRect  windowFrame, screenFrame;
        windowFrame = [[currentFrames objectAtIndex:_lastIndex] rectValue];
        screenFrame = [[[self window] screen] frame];
        [_title drawTextureInWindowFrame:windowFrame screenFrame:screenFrame];
    }
}

@end

#pragma mark -

@interface SRTabExposeController (private)
- (void)_updateFrames;
@end

@implementation SRTabExposeController

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

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize meber variables
    _exposeState = SRTabExposeDeexpose;
    _duration = 0.1f;
    _currentFrames = [[NSMutableArray array] retain];
    _finalFrames = [[NSMutableArray array] retain];
    
    return self;
}

- (void)dealloc
{
    [_currentFrames release], _currentFrames = nil;
    [_finalFrames release], _finalFrames = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Controller --
//--------------------------------------------------------------//

- (SRBrowserController*)browserController
{
    return _browserController;
}

- (void)setBrowserController:(SRBrowserController*)browserController
{
    _browserController = browserController;
}

//--------------------------------------------------------------//
#pragma mark -- Expose --
//--------------------------------------------------------------//

static BOOL _isExposing = NO;

+ (BOOL)isExposing
{
    return _isExposing;
}

static SRTabExposeController*   _currentController = nil;

+ (SRTabExposeController*)currentController
{
    return _currentController;
}

static int  _progressMarkNum = 5;

- (void)startExposingIn
{
    // Configure animation
    _exposeState = SRTabExposeExposingIn;
    
    // Create NSAnimation
    NSAnimation*    animation;
    animation = [[NSAnimation alloc] 
            initWithDuration:_duration animationCurve:NSAnimationEaseIn];
    [animation setAnimationBlockingMode:NSAnimationBlocking];
    
    int i;
    for (i = 0; i < _progressMarkNum; i++) {
        [animation addProgressMark:(1.0f / (_progressMarkNum - 1)) * i];
    }
    [animation setDelegate:self];
    
    // Start animation
    [animation startAnimation];
    [animation release];
}

- (void)startExposingOut
{
    // Configure animation
    _exposeState = SRTabExposeExposingOut;
    
    // Create NSAnimation
    NSAnimation*    animation;
    animation = [[NSAnimation alloc] 
            initWithDuration:_duration animationCurve:NSAnimationLinear];
    [animation setAnimationBlockingMode:NSAnimationBlocking];
    
    int i;
    for (i = 0; i < _progressMarkNum; i++) {
        [animation addProgressMark:(1.0f / (_progressMarkNum - 1)) * i];
    }
    [animation setDelegate:self];
    
    // Start animation
    [animation startAnimation];
    [animation release];
}

- (NSArray*)currentFrames
{
    return _currentFrames;
}

- (NSArray*)finalFrames
{
    return _finalFrames;
}

- (float)progress
{
    return _progress;
}

- (void)_updateFrames
{
    // Decide value depending on expose state
    float   value = _progress;
    if (_exposeState == SRTabExposeExposingOut) {
        value = 1.0f - _progress;
    }
    
    // Fade out expose view
    [_exposeView setBackgroundColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.4f * value]];
    
    // Get screen frame
    NSRect  screenFrame;
    screenFrame = [[_exposeWindow screen] frame];
    
    // Update image frame
    [_currentFrames removeAllObjects];
    
    int i;
    for (i = 0; i < [_finalFrames count]; i++) {
        // Get final frame
        NSRect  finalFrame;
        finalFrame = [[_finalFrames objectAtIndex:i] rectValue];
        
        // Decide current frame
        NSRect  frame;
        frame.origin.x = _origFrame.origin.x + (finalFrame.origin.x - _origFrame.origin.x) * value;
        frame.origin.y = _origFrame.origin.y + (finalFrame.origin.y - _origFrame.origin.y) * value;
        frame.size.width = _origFrame.size.width + (finalFrame.size.width - _origFrame.size.width) * value;
        frame.size.height = _origFrame.size.height + (finalFrame.size.height - _origFrame.size.height) * value;
        
        frame.origin.y = screenFrame.size.height - (frame.origin.y + frame.size.height);
        
        [_currentFrames addObject:[NSValue valueWithRect:frame]];
    }
    
    // Refresh expose view
    [_exposeView display];
}

- (int)divideNumberOfNumber:(int)number
{
    if (number < 2) {
        return 1;
    }
    
    int divideNumber = 1;
    while (1) {
        if ((number / divideNumber <= divideNumber) || 
            ((number / divideNumber == divideNumber + 1) && 
             (number % divideNumber == 0)))
        {
            return divideNumber;
        }
        
        divideNumber++;
    }
    
    // Do not reach here
    return 1;
}

- (void)decideFinalWindowFrames
{
    static int  _margin = 40;
    static int  _padding = 24;
    
    // Get number of images
    int numberOfImages;
    numberOfImages = [[_exposeView images] count];
    if (numberOfImages < 2) {
        return;
    }
    
    // Get divide number
    int divideNumber;
    divideNumber = [self divideNumberOfNumber:numberOfImages];
    
    // Get screen size
    NSScreen*   screen;
    NSRect      screenFrame, visibleScreenFrame;
    screen = [[_browserController window] screen];
    screenFrame = [screen frame];
    visibleScreenFrame = [screen visibleFrame];
    
    // Decide window size
    NSRect  frame;
    NSSize  origSize;
    int     height;
    float   aspectRatio = 1.0f;
    origSize = _origFrame.size;
    height = origSize.height * divideNumber + _padding * (divideNumber - 1) + _margin * 2;
    if (height > visibleScreenFrame.size.height) {
        aspectRatio = visibleScreenFrame.size.height / height;
    }
    frame.size.width = origSize.width * aspectRatio;
    frame.size.height = origSize.height * aspectRatio;
    
    // Decide window position
    [_finalFrames removeAllObjects];
    [_currentFrames removeAllObjects];
    
    int i, index = 0;
    for (i = 0; i < divideNumber; i++) {
        int count = 1, remainRow;
        remainRow = divideNumber - (i + 1);
        while (count < divideNumber + 1 && index + count < numberOfImages) {
            if (remainRow == 0) {
                count++;
                continue;
            }
            
            int remain;
            remain = numberOfImages - index - count;
            if ((remain / remainRow < count) || 
                ((remain / remainRow == count) && 
                 (remain % remainRow == 0)))
            {
                break;
            }
            count++;
        }
        
        // Calc window position
        float   totalWidth;
        totalWidth = frame.size.width * count + _padding * (count - 1) + _margin * 2;
        
        if (totalWidth > visibleScreenFrame.size.width) {
            // Shrink size
            float   width;
            width = origSize.width * count + _padding * (count - 1) + _margin * 2;
            aspectRatio = visibleScreenFrame.size.width / width;
            frame.size.width = origSize.width * aspectRatio;
            frame.size.height = origSize.height * aspectRatio;
            totalWidth = frame.size.width * count + _padding * (count - 1) + _margin * 2;
        }
        
        float   totalHeight;
        totalHeight = frame.size.height * divideNumber + _padding * (divideNumber - 1) + _margin * 2;
        frame.origin.y = (visibleScreenFrame.origin.y - screenFrame.origin.y) + 
                visibleScreenFrame.size.height - _margin - 
                (visibleScreenFrame.size.height / 2 - totalHeight / 2) - 
                frame.size.height * (i + 1) - _padding * i;
        
        int j;
        for (j = 0; j < count; j++) {
            frame.origin.x = (visibleScreenFrame.origin.x - screenFrame.origin.x) + 
                    _margin + visibleScreenFrame.size.width / 2 - totalWidth / 2 + 
                    (frame.size.width + _padding) * j;
            
            // Add frame
            [_finalFrames addObject:[NSValue valueWithRect:frame]];
            [_currentFrames addObject:[NSValue valueWithRect:_origFrame]];
            
            index++;
        }
    }
}

- (void)expose
{
    if (!_browserController) {
        return;
    }
    
    // Get tab view
    HMTabView*  tabView;
    tabView = [_browserController tabView];
    
    // Decide tab expose frame
    NSTabView*  nsTabView;
    NSRect      frame;
    nsTabView = [tabView nsTabView];
    frame = [nsTabView convertRect:[nsTabView frame] toView:nil];
    frame.origin = [[tabView window] convertBaseToScreen:frame.origin];
    _origFrame = frame;
    
    // Create expose window
    NSRect  screenFrame;
    screenFrame = [[[tabView window] screen] frame];
    
    if (!_exposeWindow) {
        _exposeWindow = [[NSWindow alloc] initWithContentRect:screenFrame 
                styleMask:NSBorderlessWindowMask 
                backing:NSBackingStoreBuffered 
                defer:YES];
        [_exposeWindow setOpaque:NO];
        [_exposeWindow setBackgroundColor:[NSColor clearColor]];
//        [_exposeWindow setLevel:NSFloatingWindowLevel];
        
        _exposeView = [[SRExposeView alloc] initWithFrame:screenFrame];
        [_exposeWindow setContentView:_exposeView];
    }
    [_exposeView setBackgroundColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0]];
    
    if (!NSEqualRects(screenFrame, [_exposeWindow frame])) {
        [_exposeWindow setFrame:screenFrame display:YES];
    }
    [_exposeWindow orderFront:self];
    
    // Create GL images
    NSMutableArray* images;
    images = [NSMutableArray array];
    
    int i, topIndex;
    topIndex = [tabView indexOfSelectedTabViewItem];
    for (i = 0; i < [tabView numberOfTabViewItems]; i++) {
        // Get page controller
        SRPageController*   pageController;
        pageController = [_browserController pageControllerAtIndex:i];
        
        // Create GL image
        NSBitmapImageRep*   bitmapRep;
        HMGLImage*          image;
        bitmapRep = [pageController webViewImageBitmapRep];
        bitmapRep = [NSBitmapImageRep imageRepWithData:[[pageController webViewImage] TIFFRepresentation]];
        image = [[HMGLImage alloc] initWithBitmapImageRep:bitmapRep];
        [images addObject:image];
    }
    [_exposeView setImages:images];
    [_exposeView setSelectedIndex:topIndex];
    
    // Hide tab view
    [tabView setHidden:YES];
    
    // Set current controller
    _currentController = self;
    
    // Expose windows
    _isExposing = YES;
    [self decideFinalWindowFrames];
    [self startExposingIn];
}

- (void)deexpose
{
    [self deexposeAndSelectAtIndex:NSNotFound];
}

- (void)deexposeAndSelectAtIndex:(int)index
{
    // Set selected index
    [_exposeView setSelectedIndex:index];
    
    // Start exposing out
    [self startExposingOut];
    
    // Clear current controller
    _currentController = nil;
    
    // Select web view
    HMTabView*  tabView;
    tabView = [_browserController tabView];
    if (index != NSNotFound && index < [tabView numberOfTabViewItems]) {
        [tabView selectTabViewItemAtIndex:index];
    }
    
    // Change first responder
    [[_browserController window] makeFirstResponder:
            [[_browserController selectedPageController] webView]];
    
    // Show tab view
    [tabView setHidden:NO];
    [tabView display];
    
    // Refresh tab view items
    NSEnumerator*   enumerator;
    HMTabViewItem*  tabViewItem;
    enumerator = [[tabView tabViewItems] objectEnumerator];
    while (tabViewItem = [enumerator nextObject]) {
        [[[tabViewItem tabItemView] nsProgressIndicator] setHidden:YES];
        [[[tabViewItem tabItemView] hmProgressIndicator] setHidden:YES];
    }
    
    // Close window
    [_exposeWindow orderOut:self];
    
    _isExposing = NO;
}

- (int)exposeState
{
    return _exposeState;
}

//--------------------------------------------------------------//
#pragma mark -- Event handling --
//--------------------------------------------------------------//

- (void)mouseMoved:(NSEvent*)event
{
    // Pass to expose view
    [_exposeView mouseMoved:event];
}

//--------------------------------------------------------------//
#pragma mark -- NSAnimation delegate --
//--------------------------------------------------------------//

- (void)animation:(NSAnimation*)animation didReachProgressMark:(NSAnimationProgress)progress
{
    // Set progress
    _progress = progress;
    
    // Update frame
    [self _updateFrames];
}

- (void)animationDidEnd:(NSAnimation*)animation
{
    if (_exposeState == SRTabExposeExposingIn) {
        _exposeState = SRTabExposeExposing;
    }
    else if (_exposeState == SRTabExposeExposingOut) {
        _exposeState = SRTabExposeDeexpose;
    }
}

@end

