/*
SRBrowserController.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 "SRConstants.h"

#import "SRBookmark.h"

#import "SRAppController.h"
#import "SRBookmarkController.h"
#import "SRBrowserController.h"
#import "SRBrowserDocument.h"
#import "SRDefaultKeys.h"
#import "SRDocumentController.h"
#import "SRDownloadManager.h"
#import "SRFullScreenController.h"
#import "SRPageController.h"
#import "SRSearchFieldController.h"
#import "SRIconManager.h"
#import "SRURLCompleteDataSource.h"

#import "SRMenu.h"
#import "SRBookmarkBar.h"
#import "SRSearchField.h"
#import "SRStatusBar.h"
#import "SRToolbarItem.h"

#import "SRPrefDefaultKeys.h"

#import "SRWebView.h"

// Nofitication
NSString*   SRBrowserDidCommitLoad = @"SRBrowserDidCommitLoad";
NSString*   SRBrowserDidReceiveServerRedirect = @"SRBrowserDidReceiveServerRedirect";
NSString*   SRBrowserTitleReceived = @"SRBrowserTitleReceived";
NSString*   SRBrowserProgressStart = @"SRBrowserProgressStart";
NSString*   SRBrowserProgressFinish = @"SRBrowserProgressFinish";
NSString*   SRBrowserDidBecomeMain = @"SRBrowserDidBecomeMain";
NSString*   SRBrowserDidResignMain = @"SRBrowserDidResignMain";
NSString*   SRBrowserSelectedTabChanged = @"SRBrowserSelectedTabChanged";

// Toolbar identifier
NSString*   SRBrowserToolbarIdentifier = @"SRBrowserToolbarIdentifier";
NSString*   SRBrowserToolbarLabelTable = @"BrowserToolbar";

// Toolbar items
NSString*   SRBrowserGoBackItem = @"SRGoBack";
NSString*   SRBrowserGoForwardItem = @"SRGoForward";
NSString*   SRBrowserReloadPageItem = @"SRReloadPage";
NSString*   SRBrowserStopLoadingItem = @"SRStopLoading";
NSString*   SRBrowserGoHomeItem = @"SRGoHome";
NSString*   SRBrowserURLItem = @"SRURL";
NSString*   SRBrowserSearchItem = @"SRSearch";
NSString*   SRBrowserNewTabItem = @"SRNewTab";
NSString*   SRBrowserCloseTabItem = @"SRCloseTab";
NSString*   SRBrowserShelfItem = @"SRShelf";
NSString*   SRBrowserFullScreenItem = @"SRFullScreen";
NSString*   SRBrowserPrintPageItem = @"SRPrintPage";
NSString*   SRBrowserAddBookmarkItem = @"SRAddBookmark";
NSString*   SRBrowserBiggerTextItem = @"SRBiggerText";
NSString*   SRBrowserSmallerTextItem = @"SRSmallerText";
NSString*   SRBrowserBookmarkPanelItem = @"SRBookmarkPanel";
NSString*   SRBrowserHistoryPanelItem = @"SRHistoryPanel";
NSString*   SRBrowserPageInfoPanelItem = @"SRPageInfoPanel";
NSString*   SRBrowserFindPanelItem = @"SRFindPanel";
NSString*   SRBrowserDownloadPanelItem = @"SRDownloadPanel";

// Toolbar icons
NSDictionary*   SRAquaIconImageDict = nil;
NSDictionary*   SRMetalIconImageDict = nil;

@implementation SRBrowserController

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

static NSMutableArray*  _browserControllers = nil;

+ (NSArray*)browserControllers
{
    return _browserControllers;
}

+ (SRBrowserController*)mainBrowserController
{
    NSEnumerator*           enumerator;
    SRBrowserController*    browserController;
    enumerator = [_browserControllers objectEnumerator];
    while (browserController = [enumerator nextObject]) {
        if ([[browserController window] isMainWindow]) {
            return browserController;
        }
    }
    
    return nil;
}

- (id)init
{
    self = [super initWithWindowNibName:@"BrowserWindow"];
    if (!self) {
        return nil;
    }
    
    // Register itself
    if (!_browserControllers) {
        // Crreate array which dose not retain contents
        CFArrayCallBacks    callbacks = { 0, NULL, NULL, NULL, NULL };
        _browserControllers = (NSMutableArray*)CFArrayCreateMutable(NULL, 0, &callbacks);
    }
    [_browserControllers addObject:self];
    
    // Initialize member variables
    _pageControllers = [[NSMutableArray array] retain];
    _searchFieldController = [[SRSearchFieldController alloc] init];
    [_searchFieldController setBrowserController:self];
    [_searchFieldController setSearchField:[self searchField]];
    _tabExposeController = [[SRTabExposeController alloc] init];
    [_tabExposeController setBrowserController:self];
    _untitledTabCounter = 1;
    
    return self;
}

- (void)windowWillLoad
{
    // Register notifications
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(pageDidStartProvisionalLoad:) 
            name:SRPageDidStartProvisionalLoad object:nil];
    [center addObserver:self selector:@selector(pageDidCommitLoad:) 
            name:SRPageDidCommitLoad object:nil];
    [center addObserver:self selector:@selector(pageDidFinishLoad:) 
            name:SRPageDidFinishLoad object:nil];
    [center addObserver:self selector:@selector(pageDidReceiveServerRedirect:) 
            name:SRPageDidReceiveServerRedirect object:nil];
    [center addObserver:self selector:@selector(pageTitleReceived:) 
            name:SRPageTitleReceived object:nil];
    [center addObserver:self selector:@selector(pageIconReceived:) 
            name:SRPageIconReceived object:nil];
    [center addObserver:self selector:@selector(pageProgressStart:) 
            name:SRPageProgressStart object:nil];
    [center addObserver:self selector:@selector(pageProgressEstimateChanged:) 
            name:SRPageProgressEstimateChanged object:nil];
    [center addObserver:self selector:@selector(pageProgressFinish:) 
            name:SRPageProgressFinish object:nil];
    
    [center addObserver:self selector:@selector(bookmarkBarHeightChanged:) 
            name:SRBookmarkBarHeightChanged object:_bookmarkBar];
    
    // Register key value observation
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Bookmark preference
    [defaults addObserver:self forKeyPath:SRBookmarkBarType 
            options:NSKeyValueObservingOptionNew context:NULL];
    [defaults addObserver:self forKeyPath:SRBookmarkBarUseMultiLine 
            options:NSKeyValueObservingOptionNew context:NULL];
    
    // Tab preference
    [defaults addObserver:self forKeyPath:SRTabStyle 
            options:NSKeyValueObservingOptionNew context:NULL];
    [defaults addObserver:self forKeyPath:SRPageDockStyle 
            options:NSKeyValueObservingOptionNew context:NULL];
}

- (NSButton*)_createStatusButtonWithImageName:(NSString*)imageName 
        altImageName:(NSString*)altImageName action:(SEL)action
{
    NSImage*    image;
    NSImage*    altImage;
    NSRect      buttonFrame;
    NSButton*   button;
    image = [NSImage imageNamed:imageName];
    altImage = [NSImage imageNamed:altImageName];
    buttonFrame.origin = NSZeroPoint;
    buttonFrame.size = [image size];
    button = [[NSButton alloc] initWithFrame:buttonFrame];
    [button autorelease];
    if (image) {
        [button setImage:image];
    }
    if (altImage) {
        [button setAlternateImage:altImage];
    }
    [button setBezelStyle:NSRegularSquareBezelStyle];
    [button setButtonType:NSMomentaryChangeButton];
    [button setBordered:NO];
    [button setTarget:self];
    [button setAction:action];
    
    return button;
}

- (void)windowDidLoad
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get default window size
    NSString*   sizeString;
    sizeString = [defaults objectForKey:SRBrowserWindowContentSize];
    
    // Bookmarks bar and status bar visiblity
    [self setBookmarkBarVisible:[defaults boolForKey:SRBrowserWindowBookmarkBarVisible] display:NO];
    [self setStatusBarVisible:YES display:NO];
    
    // Get window
    HMWindow*   window;
    window = (HMWindow*)[self window];
    
    // Set default window size
    if (sizeString) {
        // Restore size
        NSSize  size;
        size = NSSizeFromString(sizeString);
        
        if (size.width > 200 && size.height > 200) { // Minimum size
            // Make window default size
            [window setContentSize:size];
        }
    }
    
    // For first window
    if ([[[NSDocumentController sharedDocumentController] documents] count] == 1) {
        // Get default window origin
        NSString*   originString;
        originString = [defaults objectForKey:SRBrowserWindowFrameOrigin];
        if (originString) {
            // Restore origin
            NSPoint origin;
            origin = NSPointFromString(originString);
            
            if (!NSEqualPoints(origin, NSZeroPoint)) {
                // Make window default origin
                [window setFrameTopLeftPoint:origin];
            }
        }
    }
    // For other windows
    else {
        // Get front window
        NSWindow*   frontWindow;
        frontWindow = SRGetFrontBrowserWindow();
        if (frontWindow) {
            // Calculate cascaded point
            NSRect  frontWindowFrame;
            NSPoint frontWindowPoint;
            frontWindowFrame = [frontWindow frame];
            frontWindowPoint = frontWindowFrame.origin;
            frontWindowPoint.y += frontWindowFrame.size.height;
            [window setFrameTopLeftPoint:[window cascadeTopLeftFromPoint:frontWindowPoint]];
        }
    }
    
    // Load toolbar icons
    [[self class] updateToolbarIcons];
    
    // Configure window
    [window setPassMenuValidationToDelegate:YES];
    
    // Create toolbar
    NSToolbar*  toolbar;
    toolbar = [[NSToolbar alloc] 
            initWithIdentifier:SRBrowserToolbarIdentifier];
    [toolbar setDelegate:self];
    [toolbar setAllowsUserCustomization:YES];
    [toolbar setAutosavesConfiguration:YES];
    
    // Set default configuration
    NSMutableDictionary*    config;
    config = [NSMutableDictionary dictionaryWithDictionary:[toolbar configurationDictionary]];
    [config setObject:[NSNumber numberWithInt:NSToolbarDisplayModeIconOnly] forKey:@"TB Display Mode"];
    [config setObject:[NSNumber numberWithBool:YES] forKey:@"TB Is Shown"];
    [toolbar setConfigurationFromDictionary:config];
    
    [window setToolbar:toolbar];
    [toolbar release];
    
    // Configure bookmark bar
    [_bookmarkBar setTarget:self];
    [_bookmarkBar setAction:@selector(openBookmarkBarBookmarkAction:)];
    
    SRBookmark* bookmarkForBookmarkBar;
    bookmarkForBookmarkBar = [[SRAppController sharedInstance] bookmarkForBookmarkBar];
    if (bookmarkForBookmarkBar) {
        // Set children
        [_bookmarkBar setBookmarks:[bookmarkForBookmarkBar sortedChildren]];
    }
    
    [_bookmarkBar setWrap:[defaults boolForKey:SRBookmarkBarUseMultiLine]];
    [_bookmarkBar sizeToFit];
    
    // Configure status bar
    HMEtchedTextField*  statusTextField;
    statusTextField = [_statusBar statusTextField];
    [statusTextField bind:@"value" 
            toObject:_browserController withKeyPath:@"selection.status" options:nil];
    
    _tabExposeButton = [[self _createStatusButtonWithImageName:@"tabExpose" 
            altImageName:@"tabExposePressed" action:@selector(showAllTabsAction:)] retain];
    [_statusBar addButton:_tabExposeButton];
    
    _pageDockButton = [[self _createStatusButtonWithImageName:@"hidePageDock" 
            altImageName:@"hidePageDockPressed" action:@selector(togglePageDockAction:)] retain];
    [_statusBar addButton:_pageDockButton];
    
    _feedButton = [[self _createStatusButtonWithImageName:@"statusFeed" 
            altImageName:nil action:@selector(toggleFeedAction:)] retain];
    _IDNButton = [[self _createStatusButtonWithImageName:@"statusIDN" 
            altImageName:@"statusIDNSelected" action:@selector(toggleIDNAction:)] retain];
    [_IDNButton setButtonType:NSToggleButton];
    _lockedButton = [[self _createStatusButtonWithImageName:@"statusLocked" 
            altImageName:@"statusLockedSelected" action:@selector(toggleLockedAction:)] retain];
    [_lockedButton setButtonType:NSToggleButton];
    _cookieButton = [[self _createStatusButtonWithImageName:@"statusCookie" 
            altImageName:@"statusCookieSelected" action:@selector(toggleCookieAction:)] retain];
    [_cookieButton setButtonType:NSToggleButton];
    
    // Configure tab view
    int pageDockHeight;
    pageDockHeight = [defaults integerForKey:SRPageDockHeight];
    if (pageDockHeight > 0) {
        [_tabView setTabItemViewHigh:pageDockHeight];
    }
    [_tabView setDelegate:self];
    [[_tabView tabItemGroupView] setDelegate:self];
    [[_tabView tabItemGroupView] addDraggedTypes:
            [NSArray arrayWithObjects:
                    WebURLsWithTitlesPboardType, HMTreeControllerObjectIdPboardType, nil]]; 
    [self setTabStyle:[defaults integerForKey:SRTabStyle]];
}

- (void)dealloc
{
    // Unregister itself
    [_browserControllers removeObject:self];
    
    // Remove itself
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    // Remove observer
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObserver:self forKeyPath:SRBookmarkBarType];
    [defaults removeObserver:self forKeyPath:SRBookmarkBarUseMultiLine];
    [defaults removeObserver:self forKeyPath:SRTabStyle];
    [defaults removeObserver:self forKeyPath:SRPageDockStyle];
    
    // Release instance variables
    [_pageControllers release], _pageControllers = nil;
    [_searchFieldController release], _searchFieldController = nil;
    [_tabExposeController release], _tabExposeController = nil;
    [_URLDataSource release], _URLDataSource = nil;
    [_locationURLDataSource release], _locationURLDataSource = nil;
    [_fullScreenWindow release], _fullScreenWindow = nil;
    [_fullScreenController release], _fullScreenController = nil;
    
    [_tabExposeButton release], _tabExposeButton = nil;
    [_pageDockButton release], _pageDockButton = nil;
    [_feedButton release], _feedButton = nil;
    [_IDNButton release], _IDNButton = nil;
    [_lockedButton release], _lockedButton = nil;
    [_cookieButton release], _cookieButton = nil;
    
    NSMenu* fileMenu;
    fileMenu = SRFileMenu();
    if ([fileMenu delegate] == self) {
        [fileMenu setDelegate:[SRAppController sharedInstance]];
    }
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Page controller --
//--------------------------------------------------------------//

- (NSArray*)pageControllers
{
    return _pageControllers;
}

- (SRPageController*)pageControllerAtIndex:(int)index
{
    if ([_pageControllers count] < index) {
        return nil;
    }
    
    return [_pageControllers objectAtIndex:index];
}

- (SRPageController*)pageControllerForView:(NSView*)view
{
    NSEnumerator*       enumerator;
    SRPageController*   pageController;
    enumerator = [_pageControllers objectEnumerator];
    while (pageController = [enumerator nextObject]) {
        if ([pageController webView] == view) {
            return pageController;
        }
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- Search field controller --
//--------------------------------------------------------------//

- (SRSearchFieldController*)searchFieldController
{
    return _searchFieldController;
}

//--------------------------------------------------------------//
#pragma mark -- Tab management --
//--------------------------------------------------------------//

- (HMTabView*)tabView
{
    return _tabView;
}

- (WebView*)addNewTabWithLabel:(NSString*)label 
        frameName:(NSString*)frameName 
        groupName:(NSString*)groupName 
        select:(BOOL)select
{
    // Create page controller
    SRPageController*   pageController;
    pageController = [[SRPageController alloc] 
            initWithBrowserController:self label:label frameName:frameName groupName:groupName];
    [_pageControllers addObject:pageController];
    [pageController release];
    
    // Add tab view item
    HMTabViewItem*  tabViewItem;
    tabViewItem = [pageController tabViewItem];
    [_tabView addTabViewItem:tabViewItem];
    [[tabViewItem tabItemView] setDelegate:self];
    
    // Select page controller
    if (select) {
        [_tabView selectTabViewItem:tabViewItem];
    }
    
    // Return web view
    return [pageController webView];
}

- (WebView*)insertNewTabWithLabel:(NSString*)label 
        frameName:(NSString*)frameName 
        groupName:(NSString*)groupName 
        atIndex:(unsigned int)index 
        select:(BOOL)select
{
    // Create page controller
    SRPageController*   pageController;
    pageController = [[SRPageController alloc] 
            initWithBrowserController:self label:label frameName:frameName groupName:groupName];
    if (index < [_pageControllers count]) {
        [_pageControllers insertObject:pageController atIndex:index];
    }
    else {
        [_pageControllers addObject:pageController];
    }
    [pageController release];
    
    // Add tab view item
    HMTabViewItem*  tabViewItem;
    tabViewItem = [pageController tabViewItem];
    if (index < [_pageControllers count]) {
        [_tabView insertTabViewItem:tabViewItem atIndex:index];
    }
    else {
        [_tabView addTabViewItem:tabViewItem];
    }
    [[tabViewItem tabItemView] setDelegate:self];
    
    // Select page controller
    if (select) {
        [_tabView selectTabViewItem:tabViewItem];
    }
    
    // Return web view
    return [pageController webView];
}

- (void)removeTabAtIndex:(unsigned int)index
{
    // Check argument
    if (index > [_pageControllers count]) {
        return;
    }
    
    // Stop loading
    [[[_pageControllers objectAtIndex:index] webView] stopLoading:self];
    
    // Remove tab view item
    [_tabView removeTabViewItem:[_tabView tabViewItemAtIndex:index]];
    
    // Remove page controller
    [_pageControllers removeObjectAtIndex:index];
}

- (void)selectLeftTab
{
}

- (void)selectRightTab
{
}

- (BOOL)isPageDockVisible
{
    return [_tabView isTabItemViewShown];
}

- (void)setPageDockVisible:(BOOL)isVisible
{
    // Update PageDock thumbnail image
    if (isVisible) {
        NSEnumerator*       enumerator;
        SRPageController*   pageController;
        enumerator = [_pageControllers objectEnumerator];
        while (pageController = [enumerator nextObject]) {
            [pageController updateDocumentViewImage];
        }
    }
    
    // Set tab view visible
    [_tabView setTabItemViewShown:isVisible];
    [_tabView updateLayout];
    
    // Set button image
    NSImage*    image;
    NSImage*    altImage;
    image = [NSImage imageNamed:isVisible ? @"hidePageDock" : @"showPageDock"];
    altImage = [NSImage imageNamed:isVisible ? @"hidePageDockPressed" : @"showPageDockPressed"];
    [_pageDockButton setImage:image];
    [_pageDockButton setAlternateImage:altImage];
}

//--------------------------------------------------------------//
#pragma mark -- Web view and toolbar items --
//--------------------------------------------------------------//

- (SRPageController*)selectedPageController
{
    // Get selected index of tab
    int index;
    index = [_tabView indexOfSelectedTabViewItem];
    if (index == NSNotFound || [_pageControllers count] <= index) {
        return nil;
    }
    
    return [_pageControllers objectAtIndex:index];
}

- (HMImageComboBox*)URLComboBox
{
    // Get URL combo box from toolbar items
    NSEnumerator*   enumerator;
    NSToolbarItem*  toolbarItem;
    enumerator = [[[[self window] toolbar] visibleItems] objectEnumerator];
    while (toolbarItem = [enumerator nextObject]) {
        if ([[toolbarItem itemIdentifier] isEqualToString:SRBrowserURLItem]) {
            return (HMImageComboBox*)[toolbarItem view];
        }
    }
    
    return nil;
}

- (HMImageComboBox*)URLComboBoxOnSheet
{
    // Return first children of location URL view
    NSArray*    subviews;
    subviews = [[self locationURLView] subviews];
    
    if ([subviews count] < 1) {
        return nil;
    }
    
    id  view;
    view = [subviews objectAtIndex:0];
    
    return view;
}

- (SRSearchField*)searchField
{
    // Get search field from toolbar items
    NSEnumerator*   enumerator;
    NSToolbarItem*  toolbarItem;
    enumerator = [[[[self window] toolbar] visibleItems] objectEnumerator];
    while (toolbarItem = [enumerator nextObject]) {
        if ([[toolbarItem itemIdentifier] isEqualToString:SRBrowserSearchItem]) {
            return (SRSearchField*)[toolbarItem view];
        }
    }
    
    return nil;
}

- (SRSearchField*)searchFieldOnSheet
{
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- Key value coding --
//--------------------------------------------------------------//

- (id)browserContent
{
    return _browserContent;
}

//--------------------------------------------------------------//
#pragma mark -- Opening page with request --
//--------------------------------------------------------------//

- (WebView*)_openRequest:(NSURLRequest*)request withPageController:(SRPageController*)pageController
{
    // Check scheme
    NSString*	scheme;
    NSString*   lowercase;
    scheme = [[request URL] scheme];
    lowercase = [scheme lowercaseString];
    if (scheme && ![scheme isEqualToString:lowercase]) {
        // Change scheme
        NSMutableString*    URLString;
        NSRange             range;
        URLString = [NSMutableString stringWithString:[[request URL] absoluteString]];
        range = [URLString rangeOfString:scheme];
        if (range.location != NSNotFound) {
            [URLString replaceCharactersInRange:range withString:lowercase];
        }
        
        request = [self createRequestWithURLString:URLString];
    }
    
    // For feed scheme
    scheme = [[request URL] scheme];
    if ([scheme isEqualToString:@"feed"]) {
        // Change scheme
        NSMutableString*    URLString;
        NSRange             range;
        URLString = [NSMutableString stringWithString:[[request URL] absoluteString]];
        range = [URLString rangeOfString:scheme];
        if (range.location != NSNotFound) {
            [URLString replaceCharactersInRange:range withString:@"http"];
        }
        
        request = [self createRequestWithURLString:URLString];
    }
    
    // Open request
    WebView*    webView;
    webView = [pageController openRequest:request];
    
    // Set URL string
    [_browserContent setValue:[[request URL] absoluteString] forKey:@"URLString"];
    
    return webView;
}

- (WebView*)openRequest:(NSURLRequest*)request
{
    // Open request with selected page controller
    return [self _openRequest:request withPageController:[self selectedPageController]];
}

- (WebView*)openRequest:(NSURLRequest*)request atIndex:(int)index
{
    // Open request with page controller at index
    return [self _openRequest:request withPageController:[self pageControllerAtIndex:index]];
}

- (id)openRequest:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        groupName:(NSString*)groupName 
        withOpenAction:(SROpenActionType)openAction
{
    switch (openAction) {
    case SROpenAction: {
        return [self openRequest:request];
    }
    case SROpenInNewTabAction: {
        return [self openInNewTabRequest:request frameName:frameName groupName:groupName select:YES];
    }
    case SROpenInNewBackgroundTabAction: {
        return [self openInNewTabRequest:request frameName:frameName groupName:groupName select:NO];
    }
    case SROpenInNewWindowAction: {
        return [self openInNewWindowRequest:request frameName:frameName groupName:groupName];
    }
    case SROpenInNewBackgroundWindowAction: {
        return [self openInNewBackgroundWindowRequest:request frameName:frameName groupName:groupName];
    }
    case SROpenOptionAction: {
        // Download it
        NSURLDownload*  download;
        download = [[NSURLDownload alloc] initWithRequest:request 
                delegate:[SRDownloadManager sharedInstance]];
        [download autorelease];
        
        return download;
    }
    }
    
    // Never reach here
    return nil;
}

- (WebView*)openInNewTabRequest:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        groupName:(NSString*)groupName
{
    return [self openInNewTabRequest:request 
            frameName:frameName 
            groupName:groupName 
            select:[[NSUserDefaults standardUserDefaults] boolForKey:SRTabSelectNewTabs]];
}

- (WebView*)openInNewTabRequest:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        groupName:(NSString*)groupName 
        select:(BOOL)select
{
    // Create untitled label
    NSString*   label;
    _untitledTabCounter++;
    if (_untitledTabCounter == 1) {
        label = NSLocalizedString(@"Untitled", nil);
    }
    else {
        label = [NSString stringWithFormat:@"%@ %d", 
                NSLocalizedString(@"Untitled", nil), _untitledTabCounter];
    }
    
    // Add new tab
    [self addNewTabWithLabel:label 
            frameName:frameName 
            groupName:groupName 
            select:select];
    
    // Open request with last page controller
    return [self _openRequest:request withPageController:[_pageControllers lastObject]];
}

- (SRBrowserDocument*)openInNewWindowRequest:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        groupName:(NSString*)groupName
{
    // Create new document
    SRDocumentController*   documentController;
    SRBrowserDocument*      document;
    documentController = [NSDocumentController sharedDocumentController];
    document = [documentController makeUntitledDocumentOfType:SRHTMLDocumentType];
    [documentController addDocument:document];
    
    // Make window controllers
    [document makeWindowControllersWithFrameName:frameName groupName:groupName];
    
    // Get browser controller
    SRBrowserController*    browserController;
    browserController = [document browserController];
    
    // Get window and make it front
    [[browserController window] makeKeyAndOrderFront:self];
    
    // Open request in new window
    [browserController openRequest:request];
    
    return document;
}

- (SRBrowserDocument*)openInNewBackgroundWindowRequest:(NSURLRequest*)request 
        frameName:(NSString*)frameName 
        groupName:(NSString*)groupName
{
    // Create new document
    SRDocumentController*   documentController;
    SRBrowserDocument*      document;
    documentController = [NSDocumentController sharedDocumentController];
    document = [documentController makeUntitledDocumentOfType:SRHTMLDocumentType];
    [documentController addDocument:document];
    
    // Make window controllers
    [document makeWindowControllersWithFrameName:frameName groupName:groupName];
    
    // Get browser controller
    SRBrowserController*    browserController;
    browserController = [document browserController];
    
    // Get window and make it back
    [[browserController window] orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
    
    // Open bookmark in new window
    [browserController openRequest:request];
    
    return document;
}

//--------------------------------------------------------------//
#pragma mark -- Opening page with URL string --
//--------------------------------------------------------------//

- (NSURLRequest*)createRequestWithURL:(NSURL*)url
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get timeout
    int timeout;
    timeout = [defaults integerForKey:SRGeneralTimeout];
    if (timeout == -1) {
        // Never timeout
        timeout = 60000;
    }
    
    // Create request
    return [NSURLRequest requestWithURL:url 
            cachePolicy:NSURLRequestUseProtocolCachePolicy 
            timeoutInterval:timeout];
}

- (NSURLRequest*)createRequestWithURLString:(NSString*)URLString
{
    return [self createRequestWithURL:[NSURL _web_URLWithUserTypedString:URLString]];
}

- (WebView*)openURLString:(NSString*)URLString
{
    // Create and open request
    return [self openRequest:[self createRequestWithURLString:URLString]];
}

- (WebView*)openURLString:(NSString*)URLString atIndex:(int)index
{
    // Create and open request
    return [self openRequest:[self createRequestWithURLString:URLString] atIndex:index];
}

- (id)openURLString:(NSString*)URLString withOpenAction:(SROpenActionType)openAction
{
    // Create and open request
    return [self openRequest:[self createRequestWithURLString:URLString] 
            frameName:nil 
            groupName:nil 
            withOpenAction:openAction];
}

- (WebView*)openInNewTabURLString:(NSString*)URLString
{
    // Create and open request
    return [self openInNewTabRequest:[self createRequestWithURLString:URLString] 
            frameName:nil 
            groupName:nil];
}

- (WebView*)openInNewTabURLString:(NSString*)URLString select:(BOOL)select
{
    // Create and open request
    return [self openInNewTabRequest:[self createRequestWithURLString:URLString] 
            frameName:nil 
            groupName:nil 
            select:select];
}

- (SRBrowserDocument*)openInNewWindowURLString:(NSString*)URLString
{
    // Create and open request
    return [self openInNewWindowRequest:[self createRequestWithURLString:URLString] 
            frameName:nil 
            groupName:nil];
}

- (SRBrowserDocument*)openInNewBackgroundWindowURLString:(NSString*)URLString
{
    // Create and open request
    return [self openInNewBackgroundWindowRequest:[self createRequestWithURLString:URLString] 
            frameName:nil 
            groupName:nil];
}

- (NSArray*)openInNewTabsURLStrings:(NSArray*)URLStrings select:(BOOL)select
{
    NSMutableArray* webViews;
    webViews = [NSMutableArray array];
    
    NSEnumerator*   enumerator;
    NSString*       URLString;
    enumerator = [URLStrings objectEnumerator];
    while (URLString = [enumerator nextObject]) {
        WebView*    webView;
        webView = [self openInNewTabURLString:URLString select:select];
        if (webView) {
            [webViews addObject:webView];
        }
    }
    
    return webViews;
}

//--------------------------------------------------------------//
#pragma mark -- Opening page with URLs --
//--------------------------------------------------------------//

- (WebView*)openURL:(NSURL*)url
{
    // Create and open request
    return [self openRequest:[self createRequestWithURL:url]];
}

- (WebView*)openURL:(NSURL*)url atIndex:(int)index
{
    // Create and open request
    return [self openRequest:[self createRequestWithURL:url] atIndex:index];
}

- (id)openURL:(NSURL*)url withOpenAction:(SROpenActionType)openAction
{
    // Create and open request
    return [self openRequest:[self createRequestWithURL:url] 
            frameName:nil 
            groupName:nil 
            withOpenAction:openAction];
}

- (WebView*)openInNewTabURL:(NSURL*)url
{
    // Create and open request
    return [self openInNewTabRequest:[self createRequestWithURL:url] 
            frameName:nil 
            groupName:nil];
}

- (WebView*)openInNewTabURL:(NSURL*)url select:(BOOL)select
{
    // Create and open request
    return [self openInNewTabRequest:[self createRequestWithURL:url] 
            frameName:nil 
            groupName:nil 
            select:select];
}

- (SRBrowserDocument*)openInNewWindowURL:(NSURL*)url
{
    // Create and open request
    return [self openInNewWindowRequest:[self createRequestWithURL:url] 
            frameName:nil 
            groupName:nil];
}

- (SRBrowserDocument*)openInNewBackgroundWindowURL:(NSURL*)url
{
    // Create and open request
    return [self openInNewBackgroundWindowRequest:[self createRequestWithURL:url] 
            frameName:nil 
            groupName:nil];
}

//--------------------------------------------------------------//
#pragma mark -- Opening page with bookmark --
//--------------------------------------------------------------//

- (WebView*)openBookmark:(SRBookmark*)bookmark
{
    // For HTML bookmark
    if (![[bookmark isFolder] boolValue]) {
        // Open URL string
        return [self openURLString:[bookmark valueForKey:@"urlString"]];
    }
    
    return nil;
}

- (WebView*)openBookmark:(SRBookmark*)bookmark atIndex:(int)index
{
    // For HTML bookmark
    if (![[bookmark isFolder] boolValue]) {
        // Open URL string
        return [self openURLString:[bookmark valueForKey:@"urlString"] atIndex:index];
    }
    
    return nil;
}

- (id)openBookmark:(SRBookmark*)bookmark withOpenAction:(SROpenActionType)openAction
{
    // For HTML bookmark
    if (![[bookmark isFolder] boolValue]) {
        // Open URL string
        return [self openURLString:[bookmark valueForKey:@"urlString"] withOpenAction:openAction];
    }
    
    return nil;
}

- (WebView*)openInNewTabBookmark:(SRBookmark*)bookmark
{
    // For HTML bookmark
    if (![[bookmark isFolder] boolValue]) {
        // Open URL string
        return [self openInNewTabURLString:[bookmark valueForKey:@"urlString"]];
    }
    
    return nil;
}

- (WebView*)openInNewTabBookmark:(SRBookmark*)bookmark select:(BOOL)select
{
    // For HTML bookmark
    if (![[bookmark isFolder] boolValue]) {
        // Open URL string
        return [self openInNewTabURLString:[bookmark valueForKey:@"urlString"] select:select];
    }
    
    return nil;
}

- (SRBrowserDocument*)openInNewWindowBookmark:(SRBookmark*)bookmark
{
    // For HTML bookmark
    if (![[bookmark isFolder] boolValue]) {
        // Open URL string
        return [self openInNewWindowURLString:[bookmark valueForKey:@"urlString"]];
    }
    
    return nil;
}

- (SRBrowserDocument*)openInNewBackgroundWindowBookmark:(SRBookmark*)bookmark
{
    // For HTML bookmark
    if (![[bookmark isFolder] boolValue]) {
        // Open URL string
        return [self openInNewBackgroundWindowURLString:[bookmark valueForKey:@"urlString"]];
    }
    
    return nil;
}

- (NSArray*)openInTabsBookmark:(SRBookmark*)bookmark
{
    // For bookmark folder
    if ([[bookmark isFolder] boolValue]) {
        // Open children
        return [self openInTabsBookmarks:[[bookmark valueForKey:@"children"] allObjects]];
    }
    
    return nil;
}

- (NSArray*)openInTabsBookmarks:(NSArray*)bookmarks
{
    NSMutableArray* webViews;
    webViews = [NSMutableArray array];
    
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    enumerator = [bookmarks objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        if ([[bookmark isFolder] boolValue]) {
            [webViews addObjectsFromArray:[self openInTabsBookmark:bookmark]];
        }
        else {
            [webViews addObject:[self openInNewTabBookmark:bookmark select:NO]];
        }
    }
    
    return webViews;
}

//--------------------------------------------------------------//
#pragma mark -- Opening page with history --
//--------------------------------------------------------------//

- (WebView*)openHistory:(WebHistoryItem*)historyItem
{
    // Open URL string
    return [self openURLString:[historyItem URLString]];
}

- (WebView*)openHistory:(WebHistoryItem*)historyItem atIndex:(int)index
{
    // Open URL string
    return [self openURLString:[historyItem URLString] atIndex:index];
}

- (id)openHistory:(WebHistoryItem*)historyItem withOpenAction:(SROpenActionType)openAction
{
    // Open URL string
    return [self openURLString:[historyItem URLString] withOpenAction:openAction];
}

- (WebView*)openInNewTabHistory:(WebHistoryItem*)historyItem
{
    // Open URL string
    return [self openInNewTabURLString:[historyItem URLString]];
}

- (WebView*)openInNewTabHistory:(WebHistoryItem*)historyItem select:(BOOL)select
{
    // Open URL string
    return [self openInNewTabURLString:[historyItem URLString] select:select];
}

- (SRBrowserDocument*)openInNewWindowHistory:(WebHistoryItem*)historyItem
{
    // Open URL string
    return [self openInNewWindowURLString:[historyItem URLString]];
}

- (SRBrowserDocument*)openInNewBackgroundWindowHistory:(WebHistoryItem*)historyItem
{
    // Open URL string
    return [self openInNewBackgroundWindowURLString:[historyItem URLString]];
}

- (NSArray*)openInTabsHistory:(NSArray*)historyItems
{
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- Opening Shelf --
//--------------------------------------------------------------//

- (WebView*)openShelf:(NSString*)shelfId
{
    // Create URL for shelf
    NSURL*  url;
    url = [NSURL URLWithString:@"shiira://shelf"];
    
    // If shelf is already opend, use it
    WebView*        shelfWebView = nil;
    NSEnumerator*   enumerator;
    HMTabViewItem*  tabViewItem;
    enumerator = [[_tabView tabViewItems] objectEnumerator];
    while (tabViewItem = [enumerator nextObject]) {
        WebView*    webView;
        webView = (WebView*)[tabViewItem view];
        if ([[[[[webView mainFrame] dataSource] request] URL] isEqual:url]) {
            shelfWebView = webView;
            break;
        }
    }
    
    if (shelfWebView && tabViewItem) {
        // Select it
        [_tabView selectTabViewItem:tabViewItem];
    }
    else {
        // Check selected web view
        WebView*    webView;
        webView = [[self selectedPageController] webView];
        if (webView && ![[webView mainFrame] dataSource]) {
            [[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:url]];
            shelfWebView = webView;
        }
        else {
            // Create web view for shelf
            shelfWebView = [self openInNewTabURL:url select:YES];
        }
    }
    
    // Get page controller for web view
    SRPageController*   pageController;
    pageController = [self pageControllerForView:shelfWebView];
    
    // Show shelf with ID
    [pageController setSelectedShelfId:shelfId];
    
    return shelfWebView;
}

//--------------------------------------------------------------//
#pragma mark -- Opening URL --
//--------------------------------------------------------------//

- (WebView*)reloadURLIfExists:(NSURL*)url
{
    NSEnumerator*   enumerator;
    HMTabViewItem*  tabViewItem;
    enumerator = [[_tabView tabViewItems] objectEnumerator];
    while (tabViewItem = [enumerator nextObject]) {
        WebView*    webView;
        webView = (WebView*)[tabViewItem view];
        if ([webView isKindOfClass:[WebView class]]) {
            if ([url isEqual:[[[[webView mainFrame] dataSource] request] URL]]) {
                // Select and reload URL
                [_tabView selectTabViewItem:tabViewItem];
                [webView reload:self];
                [self showWindow:self];
                
                return webView;
            }
        }
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- Close page --
//--------------------------------------------------------------//

- (void)closePageController:(SRPageController*)pageController
{
    // Check page controller
    if (![_pageControllers containsObject:pageController]) {
        return;
    }
    
    // Where there is only one tab
    if ([_tabView numberOfTabViewItems] == 1) {
        // Close itself
        [[self window] performClose:self];
        return;
    }
    
    // Close tab
    int index;
    index = [_pageControllers indexOfObject:pageController];
    [self removeTabAtIndex:index];
}

//--------------------------------------------------------------//
#pragma mark -- Toolbar icon --
//--------------------------------------------------------------//

+ (NSArray*)toolbarItemIdentifiers
{
    static NSArray* _toolbarItems = nil;
    if (!_toolbarItems) {
        _toolbarItems = [[NSArray arrayWithObjects:
                SRBrowserGoBackItem, SRBrowserGoForwardItem, 
                SRBrowserReloadPageItem, SRBrowserStopLoadingItem, 
                SRBrowserGoHomeItem, SRBrowserURLItem, 
                SRBrowserSearchItem, SRBrowserShelfItem, 
                SRBrowserFullScreenItem, 
                SRBrowserNewTabItem, SRBrowserCloseTabItem, 
                SRBrowserPrintPageItem, 
                SRBrowserAddBookmarkItem, 
                SRBrowserBiggerTextItem, SRBrowserSmallerTextItem, 
                SRBrowserFindPanelItem, SRBrowserPageInfoPanelItem, 
                SRBrowserBookmarkPanelItem, SRBrowserHistoryPanelItem, 
                SRBrowserDownloadPanelItem, 
                nil] retain];
    }
    
    return _toolbarItems;
}

+ (NSArray*)defaultToolbarItemIdentifiers
{
    static NSArray* _toolbarDefaultItems = nil;
    if (!_toolbarDefaultItems) {
        _toolbarDefaultItems = [[NSArray arrayWithObjects:
                SRBrowserGoBackItem, SRBrowserGoForwardItem, 
                SRBrowserReloadPageItem, SRBrowserStopLoadingItem, 
                SRBrowserURLItem, 
                SRBrowserAddBookmarkItem, SRBrowserShelfItem, 
                SRBrowserBookmarkPanelItem, SRBrowserHistoryPanelItem, 
                SRBrowserSearchItem, SRBrowserNewTabItem, 
                nil] retain];
    }
    
    return _toolbarDefaultItems;
}

+ (void)updateToolbarIcons
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Load aqua icons
    NSString*       iconName;
    NSDictionary*   iconImageDict;
    iconName = [defaults stringForKey:SRThemeAquaIconNameKey];
    if (!iconName) {
        iconName = SRIconAquaDefaultName;
    }
    // Load new icons
    iconImageDict = [[SRIconManager sharedInstance] iconDictForName:iconName];
    if (iconImageDict) {
        [SRAquaIconImageDict release];
        SRAquaIconImageDict = [iconImageDict retain];
    }
    
    // Load metal icons
    iconName = [defaults stringForKey:SRThemeMetalIconNameKey];
    if (!iconName) {
        iconName = SRIconMetalDefaultName;
    }
    // Load new icons
    iconImageDict = [[SRIconManager sharedInstance] iconDictForName:iconName];
    if (iconImageDict) {
        [SRMetalIconImageDict release];
        SRMetalIconImageDict = [iconImageDict retain];
    }
    
#if 0
    // Update toolbar icon
    NSArray*        items;
    NSEnumerator*   enumerator;
    NSToolbarItem*  item;
    items = [[[self window] toolbar] items];
    enumerator = [items objectEnumerator];
    while (item = [enumerator nextObject]) {
        NSImage*    image;
        image = [_iconImageDict objectForKey:[item itemIdentifier]];
        if (image) {
            [item setImage:image];
        }
    }
#endif
}

//--------------------------------------------------------------//
#pragma mark -- Outlet accessors --
//--------------------------------------------------------------//

- (void)_loadDialogs
{
    // Load BrowserWindowDialogs.nib
    if (![NSBundle loadNibNamed:@"BrowserWindowDialogs" owner:self]) {
        // Error
        NSLog(@"Could not load BrowserWindowDialogs.nib");
    }
}

- (id)addBookmarkPanel
{
    if (!_addBookmarkPanel) {
        [self _loadDialogs];
    }
    return _addBookmarkPanel;
}

- (id)addBookmarkTextField
{
    if (!_addBookmarkTextField) {
        [self _loadDialogs];
    }
    return _addBookmarkTextField;
}

- (id)addBookmarkPopup
{
    if (!_addBookmarkPopup) {
        [self _loadDialogs];
    }
    return _addBookmarkPopup;
}

- (id)bookmarkAllPanel
{
    if (!_bookmarkAllPanel) {
        [self _loadDialogs];
    }
    return _bookmarkAllPanel;
}

- (id)bookmarkAllPopup
{
    if (!_bookmarkAllPopup) {
        [self _loadDialogs];
    }
    return _bookmarkAllPopup;
}

- (id)newFolderPanel
{
    if (!_newFolderPanel) {
        [self _loadDialogs];
    }
    return _newFolderPanel;
}

- (id)newFolderTextField
{
    if (!_newFolderTextField) {
        [self _loadDialogs];
    }
    return _newFolderTextField;
}

- (id)addToBarPanel
{
    if (!_addToBarPanel) {
        [self _loadDialogs];
    }
    return _addToBarPanel;
}

- (id)addToBarTextField
{
    if (!_addToBarTextField) {
        [self _loadDialogs];
    }
    return _addToBarTextField;
}

- (id)locationPanel
{
    if (!_locationPanel) {
        [self _loadDialogs];
    }
    return _locationPanel;
}

- (id)locationURLView
{
    if (!_locationURLView) {
        [self _loadDialogs];
    }
    return _locationURLView;
}

- (id)searchPanel
{
    if (!_searchPanel) {
        [self _loadDialogs];
    }
    return _searchPanel;
}

- (id)searchFieldView
{
    if (!_searchFieldView) {
        [self _loadDialogs];
    }
    return _searchFieldView;
}

//--------------------------------------------------------------//
#pragma mark -- Appearnace --
//--------------------------------------------------------------//

- (BOOL)isBookmarkBarVisible
{
    return ![_bookmarkBar isHidden];
}

- (void)setBookmarkBarVisible:(BOOL)isVisible 
        display:(BOOL)display
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Show bookmarks bar
    if (isVisible && [_bookmarkBar isHidden]) {
        // Show bookmarks bar
        [_bookmarkBar setHidden:NO];
        
        // Get frames
        NSRect  barFrame, windowFrame;
        barFrame = [_bookmarkBar frame];
        windowFrame = [[self window] frame];
        
        // Expand window
        int mask;
        mask = [_tabView autoresizingMask];
        [_tabView setAutoresizingMask:NSViewMaxYMargin];
        [[self window] setFrame:NSMakeRect(
                windowFrame.origin.x, 
                windowFrame.origin.y - barFrame.size.height - 1, 
                windowFrame.size.width, 
                windowFrame.size.height + barFrame.size.height + 1) display:display animate:YES]; 
        [_tabView setAutoresizingMask:mask];
    }
    
    // Hide bookmarks bar
    else if (!isVisible && ![_bookmarkBar isHidden]) {
        // Get frames
        NSRect  barFrame, windowFrame;
        barFrame = [_bookmarkBar frame];
        windowFrame = [[self window] frame];
        
        // Shrink window
        int mask;
        mask = [_tabView autoresizingMask];
        [_tabView setAutoresizingMask:NSViewMaxYMargin];
        [[self window] setFrame:NSMakeRect(
                windowFrame.origin.x, 
                windowFrame.origin.y + barFrame.size.height + 1, 
                windowFrame.size.width, 
                windowFrame.size.height - barFrame.size.height - 1) display:display animate:YES]; 
        [_tabView setAutoresizingMask:mask];
        
        // Hide bookmarks box
        [_bookmarkBar setHidden:YES];
    }
    
    // Set it default database
    if (!_isResizedByJavaScript) {
        [defaults setBool:isVisible forKey:SRBrowserWindowBookmarkBarVisible];
    }
}

- (BOOL)isStatusBarVisible
{
    return ![_statusBar isHidden];
}

- (void)setStatusBarVisible:(BOOL)isVisible 
        display:(BOOL)display
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Show status bar
    if (isVisible && [_statusBar isHidden]) {
        // Get frames
        NSRect  statusBarFrame, windowFrame;
        statusBarFrame = [_statusBar frame];
        windowFrame = [[self window] frame];
        
        // Expand window
        int mask;
        mask = [_tabView autoresizingMask];
        [_tabView setAutoresizingMask:NSViewMinYMargin];
        [[self window] setFrame:NSMakeRect(
                windowFrame.origin.x, 
                windowFrame.origin.y - statusBarFrame.size.height, 
                windowFrame.size.width, 
                windowFrame.size.height + statusBarFrame.size.height) display:display animate:YES]; 
        [_tabView setAutoresizingMask:mask];
        
        // Show status bar
        [_statusBar setHidden:NO];
    }
    
    // Hide status bar
    else if (!isVisible && ![_statusBar isHidden]) {
        // Get frames
        NSRect  statusBarFrame, windowFrame;
        statusBarFrame = [_statusBar frame];
        windowFrame = [[self window] frame];
        
        // Hide status bar
        [_statusBar setHidden:YES];
        
        // Shrink window
        int mask;
        mask = [_tabView autoresizingMask];
        [_tabView setAutoresizingMask:NSViewMinYMargin];
        [[self window] setFrame:NSMakeRect(
                windowFrame.origin.x, 
                windowFrame.origin.y + statusBarFrame.size.height, 
                windowFrame.size.width, 
                windowFrame.size.height - statusBarFrame.size.height) display:display animate:YES]; 
        [_tabView setAutoresizingMask:mask];
    }
}

- (BOOL)isTabVisible
{
    return [_tabView isTabItemViewShown];
}

- (void)setTabVisible:(BOOL)isVisible 
        display:(BOOL)display
{
    [_tabView setTabItemViewShown:isVisible];
}

- (BOOL)isResizedByJavaScript
{
    return _isResizedByJavaScript;
}

- (void)setResizedByJavaScript:(BOOL)flag
{
    _isResizedByJavaScript = flag;
}

- (NSRect)tabViewFrame
{
    // Get conent view frame
    NSRect  frame;
    frame = [[[self window] contentView] frame];
    frame.size.height += 1;
    
    if ([self isBookmarkBarVisible]) {
        frame.size.height -= [_bookmarkBar bounds].size.height;
    }
    if ([self isStatusBarVisible]) {
        frame.origin.y += [_statusBar bounds].size.height;
        frame.size.height -= [_statusBar bounds].size.height;
    }
    
    return frame;
}

- (SRTabStyleType)tabStyle
{
    if ([_tabView hasThumbnail]) {
        return SRTabStylePageDock;
    }
    return SRTabStyleTab;
}

- (void)setTabStyle:(SRTabStyleType)tabStyle
{
    switch (tabStyle) {
    case SRTabStylePageDock: {
        // Set page dock style
        SRPageDockStyleType pageDockStyle;
        pageDockStyle = [self pageDocSytle];
        
        switch (pageDockStyle) {
        case SRPageDockStyleHorizontal: {
            [_tabView setTabViewPoision:HMTabViewBottom];
            break;
        }
        case SRPageDockStyleVertical: {
            [_tabView setTabViewPoision:HMTabViewRight];
            break;
        }
        }
        
        // Configure tab view
        [_tabView setHasThumbnail:YES];
        [_tabView setTabItemViewShown:YES];
        [_tabView updateLayout];
        
        // Enable page dock button
        [_pageDockButton setEnabled:YES];
        [_statusBar addButton:_pageDockButton];
        
        break;
    }
    case SRTabStyleTab: {
        // Configure tab view
        [_tabView setTabViewPoision:HMTabViewTop];
        [_tabView setHasThumbnail:NO];
        [_tabView updateLayout];
        
        // Disable page dock button
        [_pageDockButton setEnabled:NO];
        [_statusBar removeButton:_pageDockButton];
        
        break;
    }
    }
}

- (SRPageDockStyleType)pageDocSytle
{
    int style;
    style = [[NSUserDefaults standardUserDefaults] integerForKey:SRPageDockStyle];
    return (SRPageDockStyleType)style;
}

- (void)setPageDockStyle:(SRPageDockStyleType)pageDockStyle
{
}

//--------------------------------------------------------------//
#pragma mark -- Full screen --
//--------------------------------------------------------------//

- (void)switchToFullScreenWithMenu:(BOOL)isMenuVisible
{
    // Fade out
    CGError error;
    CGDisplayFadeReservationToken   token = 0;
    error = CGAcquireDisplayFadeReservation(3.0, &token);
    if (error == kCGErrorSuccess) {
        CGDisplayFade(token, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, true);
    }
    
    // Hide system UI
    OSStatus    status;
    status = SetSystemUIMode(kUIModeAllHidden, isMenuVisible ? kUIOptionAutoShowMenuBar : 0);
    
    // Get screen frame
    NSScreen*   screen;
    NSRect      frame;
    screen = [[self window] screen];
    frame = [screen frame];
    
    // Create full screen controller
    if (!_fullScreenController) {
        _fullScreenController = [[SRFullScreenController alloc] init];
        [_fullScreenController setBrowserController:self];
    }
    
    // Create window for full screen
    if (!_fullScreenWindow) {
        _fullScreenWindow = [[HMWindow alloc] initWithContentRect:frame 
                styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
        [_fullScreenWindow setReleasedWhenClosed:NO];
        [_fullScreenWindow addMouseMoveListener:_fullScreenController];
    }
    
    // Swap tab view
    [_tabView retain];
    [_tabView removeFromSuperview];
    [_tabView setFrame:frame];
    [[_fullScreenWindow contentView] addSubview:_tabView];
    [_tabView release];
    
    _fullScreenTabItemViewShown = [_tabView isTabItemViewShown];
    if (_fullScreenTabItemViewShown) {
        [_tabView setTabItemViewShown:NO];
        [_tabView updateLayout];
    }
    
    [_fullScreenWindow makeKeyAndOrderFront:self];
    
    // Fade in
    if (error == kCGErrorSuccess) {
        CGDisplayFade(token, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, true);
    }
    
    // Release token
    if (token) {
        CGReleaseDisplayFadeReservation(token);
    }
}

- (void)backToNormalMode
{
    // Fade out
    CGError error;
    CGDisplayFadeReservationToken   token = 0;
    error = CGAcquireDisplayFadeReservation(3.0, &token);
    if (error == kCGErrorSuccess) {
        CGDisplayFade(token, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, true);
    }
    
    // Show system UI
    OSStatus    status;
    status = SetSystemUIMode(kUIModeNormal, 0);
    
    // Swap tab view
    NSRect  frame;
    frame = [self tabViewFrame];
    [_tabView retain];
    [_tabView removeFromSuperview];
    [_tabView setFrame:frame];
    [[[self window] contentView] addSubview:_tabView];
    [_tabView release];
    
    if ([_tabView isTabItemViewShown] != _fullScreenTabItemViewShown) {
        [_tabView setTabItemViewShown:_fullScreenTabItemViewShown];
        [_tabView updateLayout];
    }
    
    // Close full screen window
    [_fullScreenController backToNormalMode];
    [_fullScreenWindow orderOut:self];
    
    // Fade in
    if (error == kCGErrorSuccess) {
        CGDisplayFade(token, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, true);
    }
    
    // Release token
    if (token) {
        CGReleaseDisplayFadeReservation(token);
    }
}

- (BOOL)isFullScreenMode
{
    if (!_fullScreenWindow) {
        return NO;
    }
    
    return [_fullScreenWindow isVisible];
}

@end

#pragma mark -

@implementation SRURLToolbarItem : NSToolbarItem

- (void)configureForDisplayMode:(int)displayMode andSizeMode:(int)sizeMode
{
    // Decide control size
    NSControlSize   controlSize;
    controlSize = sizeMode == NSToolbarSizeModeSmall ? NSSmallControlSize : NSRegularControlSize;
    
    // Get combo box
    HMImageComboBox*    URLComboBox;
    URLComboBox = (HMImageComboBox*)[self view];
    
    // Set control size
    [[URLComboBox cell] setControlSize:controlSize];
    [URLComboBox setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]]];
    [URLComboBox sizeToFit];
    
    // Set size
    [self setMinSize:NSMakeSize(48, [URLComboBox frame].size.height)];
    [self setMaxSize:NSMakeSize(10224, [URLComboBox frame].size.height)];
}

@end

#pragma mark -

SROpenActionType SROpenActionTypeFromModifierFlags(
        unsigned modifierFlags, 
        BOOL enableTabbedBrowsing, 
        BOOL selectNewTabs)
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get modifier key flag
    unsigned int    cmdFlag, optionFlag, shiftFlag;
    cmdFlag = modifierFlags & NSCommandKeyMask;
    optionFlag = modifierFlags & NSAlternateKeyMask;
    shiftFlag = modifierFlags & NSShiftKeyMask;
    
    // For option key
    if (!cmdFlag && optionFlag && !shiftFlag) {
        return SROpenOptionAction;
    }
    
    // If tabbed browsing is enable
    if (enableTabbedBrowsing) {
        // If select new tabs
        if (selectNewTabs) {
            // Open in new tabs and select it
            if (cmdFlag && !optionFlag && !shiftFlag) {
                return SROpenInNewTabAction;
            }
            // Open in new tabs
            else if (cmdFlag && !optionFlag && shiftFlag) {
                return SROpenInNewBackgroundTabAction;
            }
            // Open in new window
            else if (cmdFlag && optionFlag && !shiftFlag) {
                return SROpenInNewWindowAction;
            }
            // Open in new window in background
            else if (cmdFlag && optionFlag && shiftFlag) {
                return SROpenInNewBackgroundWindowAction;
            }
        }
        // If not select new tabs
        else {
            // Open in new tabs
            if (cmdFlag && !optionFlag && !shiftFlag) {
                return SROpenInNewBackgroundTabAction;
            }
            // Open in new tabs and select it
            else if (cmdFlag && !optionFlag && shiftFlag) {
                return SROpenInNewTabAction;
            }
            // Open in new window in background
            else if (cmdFlag && optionFlag && !shiftFlag) {
                return SROpenInNewBackgroundWindowAction;
            }
            // Open in new window
            else if (cmdFlag && optionFlag && shiftFlag) {
                return SROpenInNewWindowAction;
            }
        }
    }
    // If tabbed browsing is disable
    else {
        // Open in new window
        if (cmdFlag && !shiftFlag) {
            return SROpenInNewWindowAction;
        }
        // Open in new window in background
        else if (cmdFlag && shiftFlag) {
            return SROpenInNewBackgroundWindowAction;
        }
    }
    
    return SROpenAction;
}

NSWindow* SRGetFrontBrowserWindow()
{
    // Get window number list
    int     count;
    int*    list;
    NSCountWindowsForContext((int)NULL, &count);
    list = malloc(sizeof(int) * count);
    NSWindowListForContext((int)NULL, sizeof(int) * count, list);
    
    // Find browser window
    NSWindow*   window = nil;
    int         i;
    for (i = 0; i < count; i++) {
        // Get window
        window = [NSApp windowWithWindowNumber:list[i]];
        if (!window) {
            continue;
        }
        
        // Get window controller
        id  windowController;
        windowController = [window windowController];
        if ([windowController isKindOfClass:[SRBrowserController class]]) {
            break;
        }
    }
    
    free(list);
    
    return window;
}
