/*
SRAppDelegate.m

Author: Makoto Kinoshita

Copyright 2004 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 <Growl/Growl.h>

#import "SRDefaultsKey.h"
#import "SRContextMenu.h"

#import "SRMainDocument.h"
#import "SRMainWindowController.h"

#import "SRAppDelegate.h"
#import "SRSidebarController.h"
#import "SRPreferencesController.h"
#import "SRSearchEnginesController.h"
#import "SRFindWindowController.h"
#import "SRCacheController.h"
#import "SRDownloadCenter.h"
#import "SRDownloadHistoryItem.h"
#import "SRDownloadsController.h"
#import "SRIconInstaller.h"
#import "SRAboutController.h"
#import "SRVisualHistoryStorage.h"
#import "SRVHBackForwardController.h"

#import "SRBookmark.h"
#import "SRBookmarkStorage.h"
#import "SRBookmarkIconDatabase.h"
#import "SRDownloadHistory.h"
#import "SREncodings.h"

#import "SRMenu.h"

#import "SRUtil.h"
#import "SRHTTPURLProtocol.h"

// Consntats
int  SRNumberOfDefaultHistoryMenu = 7;
int  SRNumberOfDefaultBookmarkMenu = 6;

// Setting file names
NSString*   SRShiiraDirectory = @"Shiira";
NSString*   SRHistoryFileName = @"Shiira/History.plist";
NSString*   SRBookmarksFileName = @"Shiira/Bookmarks.plist";
NSString*   SRDownloadsFileName = @"Shiira/Downloads.plist";
NSString*   SRIconFileName = @"Shiira/Icon.dat";

// For Growl support
#ifdef SR_SUPPORT_GROWL
NSString*   SRGrowlDownloadCompletedNotification = @"Download completion";
#endif // SR_SUPPORT_GROWL

@implementation SRHistoryMenuCreator

//--------------------------------------------------------------//
#pragma mark -- NSMenu delegate --
//--------------------------------------------------------------//

- (BOOL)menuHasKeyEquivalent:(NSMenu*)menu 
        forEvent:(NSEvent*)event 
        target:(id*)target 
        action:(SEL*)action
{
    return NO;
}

- (int)numberOfItemsInMenu:(NSMenu*)menu
{
    if ([menu delegate] != self) {
        return 0;
    }
    
    // Get web history and last visited days
    WebHistory* history;
    NSArray*    lastVisitedDays;
    history = [WebHistory optionalSharedHistory];
    lastVisitedDays = [history orderedLastVisitedDays];
    
    // For history menu
    if (menu == [SRAppDelegate historyMenu]) {
        return SRNumberOfDefaultHistoryMenu + [lastVisitedDays count];
    }
    // For calendar date menu
    else {
        // Create calendar date from menu title
        NSCalendarDate* calendarDate;
        calendarDate = [[NSCalendarDate alloc] initWithString:[menu title]];
        [calendarDate autorelease];
        
        // Get web history items
        NSArray*    historyItems;
        historyItems = [history orderedItemsLastVisitedOnDay:calendarDate];
        
        return [historyItems count];
    }
    
    return [menu numberOfItems];
}

- (BOOL)menu:(NSMenu*)menu 
        updateItem:(NSMenuItem*)item 
        atIndex:(int)index 
        shouldCancel:(BOOL)shouldCancel
{
    if ([menu delegate] != self) {
        return NO;
    }
    
    // Get web history and last visited days
    WebHistory* history;
    NSArray*    lastVisitedDays;
    history = [WebHistory optionalSharedHistory];
    lastVisitedDays = [history orderedLastVisitedDays];
    
    // For history menu
    if (menu == [SRAppDelegate historyMenu]) {
        if (index < SRNumberOfDefaultHistoryMenu) {
            return YES;
        }
        
        // Get calendar date
        if ([lastVisitedDays count] < index - SRNumberOfDefaultHistoryMenu) {
            return YES;
        }
        NSCalendarDate* calendarDate;
        calendarDate = [lastVisitedDays objectAtIndex:index - SRNumberOfDefaultHistoryMenu];
        
        // Update item
        NSDictionary*   locale;
        locale = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
        [item setTitle:[calendarDate 
                descriptionWithCalendarFormat:NSLocalizedString(@"%a, %B %e", nil) 
                locale:locale]];
        [item setTag:SRHistoryItemTag];
        
        // Create sub menu
        if (![item submenu]) {
            NSMenu* submenu;
            submenu = [[NSMenu alloc] initWithTitle:[calendarDate description]];
            [submenu setDelegate:self];
            
            [item setSubmenu:submenu];
        }
        else {
            NSMenu* submenu;
            submenu = [item submenu];
            [submenu setTitle:[calendarDate description]];
            [submenu update];
        }
        
        return YES;
    }
    // For calendar date menu
    else {
        NSUserDefaults* defaults;
        defaults = [NSUserDefaults standardUserDefaults];
        
        // Check favicon availability
        BOOL    isFaviconUsed, isFaviconUsedBookmarkMenu;
        isFaviconUsed = [defaults boolForKey:SRIconUseFavicon];
        isFaviconUsedBookmarkMenu = [defaults boolForKey:SRIconUseFaviconBookmarkMenu];
        
        // Create calendar date from menu title
        NSCalendarDate* calendarDate;
        calendarDate = [[NSCalendarDate alloc] initWithString:[menu title]];
        [calendarDate autorelease];
        
        // Get web history item
        NSArray*        historyItems;
        WebHistoryItem* historyItem;
        historyItems = [history orderedItemsLastVisitedOnDay:calendarDate];
        if ([historyItems count] < index) {
            return YES;
        }
        historyItem = [historyItems objectAtIndex:index];
        
        // Updaate item
        NSImage*    icon;
        [item setTitle:SRAlternateTitleOfWebHistoryItem(historyItem)];
        [item setAction:@selector(openHistoryItemAction:)];
        icon = [historyItem icon];
        if (isFaviconUsed && isFaviconUsedBookmarkMenu && icon) {
            [item setImage:icon];
        }
        [item setRepresentedObject:historyItem];
        
        return YES;
    }
    
    return YES;
}

@end

#pragma mark -

@implementation SRBookmarkMenuCreator

//--------------------------------------------------------------//
#pragma mark -- NSMenu delegate --
//--------------------------------------------------------------//

- (BOOL)menuHasKeyEquivalent:(NSMenu*)menu 
        forEvent:(NSEvent*)event 
        target:(id*)target 
        action:(SEL*)action
{
    return NO;
}

- (void)_appendBookmarks:(NSArray*)bookmarks inMenu:(NSMenu*)menu
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed, isFaviconUsedBookmarkMenu;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon];
    isFaviconUsedBookmarkMenu = [defaults boolForKey:SRIconUseFaviconBookmarkMenu];
    
    // Enumerate bookmark
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    id<NSMenuItem>  menuItem;
    enumerator = [bookmarks objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        // Case of HTML type
        if (![bookmark isFolderType]) {
            // Create menu item
            menuItem = [menu addItemWithTitle:[bookmark title] 
                    action:@selector(openBookmarkAction:) 
                    keyEquivalent:@""];
            [menuItem setRepresentedObject:bookmark];
            if (isFaviconUsed && isFaviconUsedBookmarkMenu) {
                [menuItem setImage:[bookmark icon]];
            }
        }
        
        // Case of bookmark folder
        else {
            // Create menu item and submenu
            SRMenu* submenu;
            menuItem = [menu addItemWithTitle:[bookmark title] 
                    action:NULL 
                    keyEquivalent:@""];
            submenu = [[SRMenu alloc] initWithTitle:@""];
            [submenu autorelease];
            [submenu setRepresentedObject:bookmark];
            [submenu setDelegate:self];
            
            [menuItem setSubmenu:submenu];
            if (isFaviconUsed && isFaviconUsedBookmarkMenu) {
                [menuItem setImage:[bookmark icon]];
            }
        }
    }
    
    // Check tab availability
    BOOL    isTabAvailable;
    isTabAvailable = [defaults boolForKey:SRTabEnableTabbedBrowsing];
    if (!isTabAvailable) {
        return;
    }
    
    // Append open all in tabs
    static NSString*    _openBookmarksInTabTitle = nil;
    if (!_openBookmarksInTabTitle) {
        _openBookmarksInTabTitle = [[[SRContextMenu bookmarkContextMenu] itemWithTag:SROpenBookmarkInTabsTag] title];
        [_openBookmarksInTabTitle retain];
    }
// Do not append a separator above open all in tabs item
//    [menu addItem:[NSMenuItem separatorItem]];
    menuItem = [menu addItemWithTitle:_openBookmarksInTabTitle 
            action:@selector(openBookmarkInTabsAction:) 
            keyEquivalent:@""];
    [menuItem setRepresentedObject:bookmarks];
}

- (void)menuNeedsUpdate:(NSMenu*)menu
{
    if ([menu delegate] != self) {
        return;
    }
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed, isFaviconUsedBookmarkMenu;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon];
    isFaviconUsedBookmarkMenu = [defaults boolForKey:SRIconUseFaviconBookmarkMenu];
    
    SRBookmarkStorage*  bookmarkStorage;
    bookmarkStorage = [SRBookmarkStorage sharedInstance];
    
    // Get bookmarks bar
    SRBookmark* bookmarksBar = nil;
    switch ([defaults integerForKey:SRBookmarkBookmarksBar]) {
    case SRBrowserShiira: {
        bookmarksBar = [bookmarkStorage shiiraBookmarksBar];
        break;
    }
    case SRBrowserSafari: {
        bookmarksBar = [bookmarkStorage safariBookmarksBar];
        break;
    }
    }
    
    // Get bookmarks menu
    NSArray*    shiiraBookmarksMenu;
    SRBookmark* safariBookmarksMenu;
    SRBookmark* firefoxBookmarksMenu;
    shiiraBookmarksMenu = [bookmarkStorage shiiraBookmarksMenu];
    safariBookmarksMenu = [bookmarkStorage safariBookmarksMenu];
    firefoxBookmarksMenu = [bookmarkStorage firefoxBookmarks];
    
    // Get menu avaialability
    NSArray*    isUsing;
    isUsing = [defaults objectForKey:SRBookmarkIsUsing];
    
    NSMenuItem* menuItem;
    
    // For bookmark menu
    if (menu == [SRAppDelegate bookmarkMenu]) {
        // Remove old bookmarks
        int i;
        for (i = [menu numberOfItems] - 1; i > SRNumberOfDefaultBookmarkMenu - 1; i--) {
            [menu removeItemAtIndex:i];
        }
        
        // Bookmarks bar
        if (bookmarksBar) {
            // Create bookmarks bar item
            menuItem = [[NSMenuItem alloc] 
                    initWithTitle:[bookmarksBar title] action:NULL keyEquivalent:@""];
            [menuItem autorelease];
            if (isFaviconUsed && isFaviconUsedBookmarkMenu) {
                [menuItem setImage:[bookmarksBar icon]];
            }
            
            // Create bookmarks bar menu
            SRMenu*     bookmarksBarMenu;
            bookmarksBarMenu = [[SRMenu alloc] initWithTitle:@"Bookmarks bar"];
            [bookmarksBarMenu autorelease];
            [bookmarksBarMenu setRepresentedObject:bookmarksBar];
            [bookmarksBarMenu setDelegate:self];
            [menuItem setSubmenu:bookmarksBarMenu];
            
            // Add bookmarks bar
            [menu addItem:menuItem];
            [menu addItem:[NSMenuItem separatorItem]];
        }
        
        // Safari bookmark availability
        BOOL    isSafariAvailable = NO;
        if ([isUsing count] > SRBrowserSafari) {
            isSafariAvailable = [[isUsing objectAtIndex:SRBrowserSafari] boolValue];
        }
        
        // Firefox bookmark availability
        BOOL    isFirefoxAvailable = NO;
        if ([isUsing count] > SRBrowserFirefox) {
            isFirefoxAvailable = [[isUsing objectAtIndex:SRBrowserFirefox] boolValue];
        }
        
        // Shiira bookmarks menu
        if (shiiraBookmarksMenu && [shiiraBookmarksMenu count] > 0) {
            // When it has only Shiira menu, dose not include menu title
            if (isSafariAvailable || isFirefoxAvailable) {
                menuItem = (NSMenuItem*)[menu addItemWithTitle:
                        [NSString stringWithFormat:@"- %@ -", 
                                NSLocalizedStringFromTable(@"ShiiraBookmarks", @"Bookmark", @"Shiira Bookmarks")] 
                        action:NULL 
                        keyEquivalent:@""];
                [menuItem setEnabled:NO];
            }
            [self _appendBookmarks:shiiraBookmarksMenu inMenu:menu];
        }
        
        // Safari bookmarks menu
        if (isSafariAvailable && safariBookmarksMenu && [[safariBookmarksMenu children] count] > 0) {
            // Add separator
            [menu addItem:[NSMenuItem separatorItem]];
            
            // Add Safari bookmarks menu
            menuItem = (NSMenuItem*)[menu addItemWithTitle:
                    [NSString stringWithFormat:@"- %@ -", 
                            NSLocalizedStringFromTable(@"SafariBookmarks", @"Bookmark", @"Safari Bookmarks")] 
                    action:NULL 
                    keyEquivalent:@""];
            [menuItem setEnabled:NO];
            [self _appendBookmarks:[safariBookmarksMenu children] inMenu:menu];
        }
        
        // Firefox bookmarks menu
        if (isFirefoxAvailable && firefoxBookmarksMenu && [[firefoxBookmarksMenu children] count] > 0) {
            // Add separator
            [menu addItem:[NSMenuItem separatorItem]];
            
            // Add Firefox bookmarks menu
            menuItem = (NSMenuItem*)[menu addItemWithTitle:
                    [NSString stringWithFormat:@"- %@ -", 
                            NSLocalizedStringFromTable(@"FirefoxBookmarks", @"Bookmark", @"Firefox Bookmarks")] 
                    action:NULL 
                    keyEquivalent:@""];
            [menuItem setEnabled:NO];
            [self _appendBookmarks:[firefoxBookmarksMenu children] inMenu:menu];
        }
    }
    // Other sub menu
    else {
        if ([menu isKindOfClass:[SRMenu class]]) {
            // Get represented object
            id  object;
            object = [(SRMenu*)menu representedObject];
            if (object) {
                SRBookmark* bookmark;
                bookmark = (SRBookmark*)object;
                
                // Append boomarks
                if ([[bookmark children] count] > 0) {
                    [self _appendBookmarks:[bookmark children] inMenu:menu];
                }
            }
        }
    }
}

@end

#pragma mark -

@implementation SRAppDelegate

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

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize member variables
    
    return self;
}

- (void)_setFactoryDefaults
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Main window
    if (![defaults objectForKey:SRMainWindowBookmarksBarVisible]) {
        [defaults setBool:YES forKey:SRMainWindowBookmarksBarVisible];
    }
    if (![defaults objectForKey:SRMainWindowStatusBarVisible]) {
        [defaults setBool:YES forKey:SRMainWindowStatusBarVisible];
    }
    if (![defaults objectForKey:SRMainWindowSidebarVisible]) {
        [defaults setBool:NO forKey:SRMainWindowSidebarVisible];
    }
    if (![defaults objectForKey:SRMainWindowSidebarSelectedTab]) {
        [defaults setInteger:SRSidebarBookmarkTab forKey:SRMainWindowSidebarSelectedTab];
    }
    
    if (![defaults objectForKey:SRToolbarUseSmallURLAndSearchField]) {
        [defaults setBool:YES forKey:SRToolbarUseSmallURLAndSearchField];
    }
    
    // Browse mode
    if (![defaults objectForKey:SRBrowseMode]) {
        [defaults setInteger:SRDefaultBrowseMode forKey:SRBrowseMode];
    }
    
    // Preferences
    if (![defaults objectForKey:SRSelectedPreferencesItem]) {
        [defaults setObject:@"SRGeneral" forKey:SRSelectedPreferencesItem];
    }
    
    if (![defaults objectForKey:SRGeneralNewWindowsOpenWith]) {
        [defaults setInteger:SRGeneralNewWindowsOpenWithEmptyPage forKey:SRGeneralNewWindowsOpenWith];
    }
    if (![defaults objectForKey:SRGeneralHomePgae]) {
        [defaults setObject:@"http://hmdt-web.net/shiira/" forKey:SRGeneralHomePgae];
    }
    if (![defaults objectForKey:SRGeneralDownloadPath]) {
        [defaults setObject:[NSHomeDirectory() stringByAppendingPathComponent:@"Desktop"] 
                forKey:SRGeneralDownloadPath];
    }
    if (![defaults objectForKey:SRDownloadNotification]) {
        [defaults setInteger:SRDownloadNotificationSideBar forKey:SRDownloadNotification];
    }
    if (![defaults objectForKey:SRDownloadItemRemove]) {
        [defaults setInteger:SRDownloadItemRemoveByHand forKey:SRDownloadItemRemove];
    }
    if (![defaults objectForKey:SRInfoCenterAlwaysDisply]) {
        [defaults setBool:YES forKey:SRInfoCenterAlwaysDisply];
    }
    
    if (![defaults objectForKey:SRGeneralTimeout]) {
        [defaults setInteger:60 forKey:SRGeneralTimeout];
    }
    if (![defaults objectForKey:SRGeneralOneClickNavigation]) {
        [defaults setBool:YES forKey:SRGeneralOneClickNavigation];
    }
    if (![defaults objectForKey:SRProtocolLibrary]) {
        [defaults setInteger:0 forKey:SRProtocolLibrary];
    }
    
    if (![defaults objectForKey:SRUsePageTransitionEffect]) {
        [defaults setBool:NO forKey:SRUsePageTransitionEffect];
    }
    if (![defaults objectForKey:SRDefaultTextEncoding]) {
        [defaults setObject:[NSNumber numberWithUnsignedInt:SRJapaneseAutoDetectEncoding] 
                forKey:SRDefaultTextEncoding];
        
        // Set Sfhit JIS as default encoding, it enables Japanese auto detection
        NSValueTransformer* transformer;
        NSString*           encodingName;
        transformer = [NSValueTransformer valueTransformerForName:SRIANAToEncodingTransformerName];
        encodingName = [transformer reverseTransformedValue:
                [NSNumber numberWithUnsignedInt:SRConvertedShiftJISStringEncoding]];
        [[SRPreferencesController defaultWebPreferences] setDefaultTextEncodingName:encodingName];
    }
    
    if (![defaults objectForKey:SRBookmarkBookmarksBar]) {
        [defaults setInteger:0 forKey:SRBookmarkBookmarksBar];
    }
    if (![defaults objectForKey:SRBookmarkIsUsing]) {
        NSMutableArray* array;
        array = [NSMutableArray array];
        [array addObject:[NSNumber numberWithBool:YES]];
        int i;
        for (i = 1; i < SRNumberOfBrowser; i++) {
            [array addObject:[NSNumber numberWithBool:NO]];
        }
        
        [defaults setObject:array forKey:SRBookmarkIsUsing];
    }
    
    if (![defaults objectForKey:SRTabEnableTabbedBrowsing]) {
        [defaults setBool:YES forKey:SRTabEnableTabbedBrowsing];
    }
    if (![defaults objectForKey:SRTabSelectNewTabs]) {
        [defaults setBool:YES forKey:SRTabSelectNewTabs];
    }
    if (![defaults objectForKey:SRTabAlwaysShowTabBar]) {
        [defaults setBool:YES forKey:SRTabAlwaysShowTabBar];
    }
    if (![defaults objectForKey:SRTabAccordingToStringWidth]) {
        [defaults setBool:NO forKey:SRTabAccordingToStringWidth];
    }
    if (![defaults objectForKey:SRTabDefaultWidth]) {
        [defaults setInteger:180 forKey:SRTabDefaultWidth];
    }
    if (![defaults objectForKey:SRTabMinWidth]) {
        [defaults setInteger:100 forKey:SRTabMinWidth];
    }

    if (![defaults objectForKey:SRTabTargetLinkUseTab]) {
        [defaults setBool:NO forKey:SRTabTargetLinkUseTab];
    }
    if (![defaults objectForKey:SRTabOpenURLUseTab]) {
        [defaults setBool:YES forKey:SRTabOpenURLUseTab];
    }
    if (![defaults objectForKey:SRTabSelectionLogic]) {
        [defaults setInteger:SRTabSelectionPreviousTab forKey:SRTabSelectionLogic];
    }

    if (![defaults objectForKey:SRTabShowAllTabsShortcut]) {
        [defaults setInteger:7 forKey:SRTabShowAllTabsShortcut];
    }
    
    if (![defaults objectForKey:SRCookieRemoveAtTermination]) {
        [defaults setBool:NO forKey:SRCookieRemoveAtTermination];
    }
    if (![defaults objectForKey:SRSecurityAllowAllURLSchemes]) {
        [defaults setBool:YES forKey:SRSecurityAllowAllURLSchemes];
    }
    if (![defaults objectForKey:SREnableJavaScirptStatusMessage]) {
        [defaults setBool:YES forKey:SREnableJavaScirptStatusMessage];
    }
    if (![defaults objectForKey:SREnableJavaScriptDialogAlert]) {
        [defaults setBool:NO forKey:SREnableJavaScriptDialogAlert];
    }
    
    if (![defaults objectForKey:SRCacheRemoveAtTermination]) {
        [defaults setBool:NO forKey:SRCacheRemoveAtTermination];
    }
    if (![defaults objectForKey:SRCacheShowAdvancedSettings]) {
        [defaults setBool:NO forKey:SRCacheShowAdvancedSettings];
    }
    
    if (![defaults objectForKey:SRUserAgent]) {
        [defaults setInteger:0 forKey:SRUserAgent];
    }
    int userAgent;
    userAgent = [defaults integerForKey:SRUserAgent];
    [defaults setBool:userAgent == SRUserAgentOther forKey:SRUserAgentOtherIsUging];
    if (![defaults objectForKey:SRUserAgentOtherName]) {
        [defaults setObject:@"" forKey:SRUserAgentOtherName];
    }
    
    if (![defaults objectForKey:SRIconTexture]) {
        [defaults setInteger:SRIconTextureAqua forKey:SRIconTexture];
    }
    if (![defaults objectForKey:SRIconName]) {
        [defaults setObject:@"default" forKey:SRIconName];
    }
    if (![defaults objectForKey:SRIconUseFavicon]) {
        [defaults setBool:YES forKey:SRIconUseFavicon];
    }
    
    if (![defaults objectForKey:SRSidebarEdge]) {
        [defaults setInteger:SRSidebarEdgeRight forKey:SRSidebarEdge];
    }
    if (![defaults objectForKey:SRSidebarAutoresize]) {
        [defaults setBool:YES forKey:SRSidebarAutoresize];
    }
    if (![defaults objectForKey:SRSidebarClick]) {
        [defaults setInteger:SRSidebarDoubleClick forKey:SRSidebarClick];
    }
    
    if (![defaults objectForKey:SRSourceFontName]) {
        [defaults setObject:@"Monaco" forKey:SRSourceFontName];
    }
    if (![defaults objectForKey:SRSourceFontSize]) {
        [defaults setInteger:12 forKey:SRSourceFontSize];
    }
    
    NSColor*    color;
    if (![defaults objectForKey:SRSourceDefaultColor]) {
        color = [NSColor blackColor];
        [defaults setObject:[NSArchiver archivedDataWithRootObject:color] 
                forKey:SRSourceDefaultColor];
    }
    if (![defaults objectForKey:SRSourceTagColor]) {
        color = [NSColor colorWithDeviceRed:0.1 green:0.1 blue:0.648 alpha:1.0];
        [defaults setObject:[NSArchiver archivedDataWithRootObject:color] 
                forKey:SRSourceTagColor];
    }
    if (![defaults objectForKey:SRSourceCommentColor]) {
        color = [NSColor colorWithDeviceRed:0.632 green:0 blue:0 alpha:1.0];
        [defaults setObject:[NSArchiver archivedDataWithRootObject:color] 
                forKey:SRSourceCommentColor];
    }
    if (![defaults objectForKey:SRSourceBackgroundColor]) {
        color = [NSColor whiteColor];
        [defaults setObject:[NSArchiver archivedDataWithRootObject:color] 
                forKey:SRSourceBackgroundColor];
    }
}

- (void)awakeFromNib
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check user defaults
    [self _setFactoryDefaults];
    
    // Create WebHistory
    WebHistory* history;
    history = [[WebHistory alloc] init];
    [WebHistory setOptionalSharedHistory:history];
    
    // Load history and make history menu
    [self loadHistory];
    
    // Create download center
    [SRDownloadCenter sharedInstance];
    
    // Load download history
    [self loadDownloadHistory];
    
    // Load icons
    [self loadIcons];
    
    // Start cache management
    [SRCacheManager startCacheManager];
    
    // Create visual history storage
    //[SRVisualHistoryStorage sharedInstance];
    
    // Update shortcut of 'Show All Tabs'
    int shortcutTag;
    shortcutTag = [defaults integerForKey:SRTabShowAllTabsShortcut];
    if (shortcutTag >= 0 && shortcutTag <= 14) {
        id <NSMenuItem> showAllTabsItem;
        showAllTabsItem = [[SRAppDelegate windowMenu] itemWithTag:SRShowAllTabsTag];
        
        unichar c;
        c = NSF1FunctionKey + shortcutTag;
        [showAllTabsItem setKeyEquivalent:[NSString stringWithCharacters:&c length:1]];
    }
    
    // Set menu delegate
    _historyMenuCreator = [[SRHistoryMenuCreator alloc] init];
    [[SRAppDelegate historyMenu] setDelegate:_historyMenuCreator];
    _bookmarkMenuCreator = [[SRBookmarkMenuCreator alloc] init];
    [[SRAppDelegate bookmarkMenu] setDelegate:_bookmarkMenuCreator];
    
    // Update protocol library
    [self updateProtocolLibary];
    
    // Register notification
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    
    [center addObserver:self 
            selector:@selector(webHistoryItemsUpdated:) 
            name:WebHistoryItemsAddedNotification 
            object:nil];
    [center addObserver:self 
            selector:@selector(webHistoryItemsUpdated:) 
            name:WebHistoryItemsRemovedNotification 
            object:nil];
    [center addObserver:self 
            selector:@selector(webHistoryItemsUpdated:) 
            name:WebHistoryAllItemsRemovedNotification 
            object:nil];
    
    [center addObserver:self 
            selector:@selector(bookmarkUpdated:) 
            name:SRBookmarkAddedNotificationName 
            object:nil];
    [center addObserver:self 
            selector:@selector(bookmarkUpdated:) 
            name:SRBookmarkRemovedNotificationName 
            object:nil];
    [center addObserver:self 
            selector:@selector(bookmarkUpdated:) 
            name:SRBookmarkChangedNotificationName 
            object:nil];
    
    // Load search engines
    [[SRSearchEnginesManager sharedInstance] loadSearchEngines];
    
    // Register key value observation
    [defaults addObserver:self 
            forKeyPath:SRProtocolLibrary 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRBookmarkBookmarksBar 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRIconUseFavicon 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRTabShowAllTabsShortcut 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
}

- (void)dealloc
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    [_findWindowController release];
    [_preferencesController release];
    [_downloadsController release];
    [_searchEnginesController release];
    [_cacheController release];
    [_iconInstaller release];
    [_historyMenuCreator release];
    
	[[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [defaults removeObserver:self forKeyPath:SRBookmarkBookmarksBar];
    [defaults removeObserver:self forKeyPath:SRIconUseFavicon];
    [super dealloc];
}

- (void)applicationWillFinishLaunching:(NSNotification*)notification
{
	[[NSAppleEventManager sharedAppleEventManager]
            setEventHandler:self
		    andSelector:@selector(openURL:withReplyEvent:)
            forEventClass:'GURL' andEventID:'GURL']; 
}

- (void)applicationDidFinishLaunching:(NSNotification*)notification
{
    // Create new document if there is no document
    NSDocumentController*   documentController;
    documentController = [NSDocumentController sharedDocumentController];
    if ([[documentController documents] count] == 0) {
        [documentController openUntitledDocumentOfType:SRHTMLDocumentType display:YES];
    }
    
// For Growl support
#ifdef SR_SUPPORT_GROWL
#if 1
    NSString*   growlPath;
    NSBundle*   growlBundle;
    growlPath = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:@"Growl.framework"];
    growlBundle = [NSBundle bundleWithPath:growlPath];
    if (growlBundle && [growlBundle load]) {
        [GrowlApplicationBridge setGrowlDelegate:self];
    }
#else
// For old Growl
    // Launch Growl
    [GrowlAppBridge launchGrowlIfInstalledNotifyingTarget:self 
            selector:@selector(registerGrowl:) 
            context:NULL];
#endif
#endif // SR_SUPPORT_GROWL
}

//--------------------------------------------------------------//
#pragma mark -- NSApplication delegate --
//--------------------------------------------------------------//

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
{
    // Check download center
    NSArray*                downloadItems;
    NSEnumerator*           enumerator;
    SRDownloadHistoryItem*  downloadItem;
    int                     activeDownloadNum = 0;
    downloadItems = [[SRDownloadCenter sharedInstance] downloadItems];
    enumerator = [downloadItems objectEnumerator];
    while (downloadItem = [enumerator nextObject]) {
        if ([downloadItem status] == SRDownloadStatusActive) {
            activeDownloadNum++;
        }
    }
    
    if (activeDownloadNum > 0) {
        // Show alert
        NSAlert*    alert;
        alert = [[NSAlert alloc] init];
        [alert autorelease];
        [alert setAlertStyle:NSCriticalAlertStyle];
        [alert setMessageText:NSLocalizedString(@"Downloads are in progress.", @"Downloads are in progress.")];
        [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to quit? Shiira is currently downloading %d files. If you quit now, it will not finish downloading these files.", @"Are you sure you want to quit? Shiira is currently downloading %d files. If you quit now, it will not finish downloading these files."), activeDownloadNum]];
        [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK")];
        [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel")];
        
        int result;
        result = [alert runModal];
        if (result != NSAlertFirstButtonReturn) {
            return NSTerminateCancel;
        }
        
        // Pause active items
        enumerator = [downloadItems objectEnumerator];
        while (downloadItem = [enumerator nextObject]) {
            if ([downloadItem status] == SRDownloadStatusActive) {
                [[SRDownloadCenter sharedInstance] pauseDownloadForItem:downloadItem];
            }
        }
    }
    
    return NSTerminateNow;
}

- (void)applicationWillTerminate:(NSNotification*)notification
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Remove active download items
    if ([defaults integerForKey:SRDownloadItemRemove] == SRDownloadItemRemoveAtTremination) {
        [[SRDownloadCenter sharedInstance] removeAllCompletedActiveItems];
    }
    
    // Save settings
    [self saveHistory];
    [self saveBookmarks];
    [[SRSearchEnginesManager sharedInstance] saveSearchEngines];
    [self saveDownloadHistory];
    [self saveIcons];
    
    // Remove cookies
    if ([defaults boolForKey:SRCookieRemoveAtTermination]) {
        NSHTTPCookieStorage*    storage;
        NSEnumerator*           enumerator;
        NSHTTPCookie*           cookie;
        storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        enumerator = [[storage cookies] objectEnumerator];
        while (cookie = [enumerator nextObject]) {
            [storage deleteCookie:cookie];
        }
        
        // Save cookie change forcefully
        Ivar    ivar;
        void*   internal = NULL;
        ivar = object_getInstanceVariable(storage, "_internal", &internal);
        if (internal) {
            void*   diskStorage = NULL;
            ivar = object_getInstanceVariable((id)internal, "storage", &diskStorage);
            if (diskStorage) {
                objc_msgSend(diskStorage, @selector(_saveCookies));
            }
        }
    }
    
    // Remove cache
    if ([defaults boolForKey:SRCacheRemoveAtTermination]) {
        [[NSURLCache sharedURLCache] removeAllCachedResponses];
    }
    
    // Sync user defaults
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (BOOL)applicationShouldHandleReopen:(NSApplication*)application hasVisibleWindows:(BOOL)flag
{
    // Create new document when there is no window
    NSDocumentController*   documentController;
    documentController = [NSDocumentController sharedDocumentController];
    if (flag == NO) {
        [documentController openUntitledDocumentOfType:SRHTMLDocumentType display:YES];
        return NO;
    }
    
    // Check there is browser window or not
    NSArray*        windows;
    NSEnumerator*   enumerator;
    NSWindow*       window;
    NSWindow*       miniaturizedWindow = nil;
    windows = [NSApp windows];
    enumerator = [windows objectEnumerator];
    while (window = [enumerator nextObject]) {
        id  windowController;
        windowController = [window windowController];
        if (!windowController) {
            continue;
        }
        
        if ([windowController isKindOfClass:[SRMainWindowController class]]) {
            // When window is miniaturized
            if ([window isMiniaturized]) {
                miniaturizedWindow = window;
                continue;
            }
            
            // There is a visible window
            return NO;
        }
    }
    
    // Show miniaturized window if it has one
    if (miniaturizedWindow) {
        [miniaturizedWindow deminiaturize:self];
        return NO;
    }
    
    // Create new document
    [documentController openUntitledDocumentOfType:SRHTMLDocumentType display:YES];
    
    return NO;
}

//--------------------------------------------------------------//
#pragma mark -- Preferences --
//--------------------------------------------------------------//

- (SRPreferencesController*)preferencesController
{
    if (!_preferencesController) {
        // Load preferences panel
        _preferencesController = [[SRPreferencesController alloc] init];
        if (![NSBundle loadNibNamed:@"Preferences" owner:_preferencesController]) {
            // Fatal
            SR_FATAL(@"Could not load Preferences.nib");
        }
    }
    
    return _preferencesController;
}

//--------------------------------------------------------------//
#pragma mark -- Menu --
//--------------------------------------------------------------//

+ (NSMenu*)fileMenu
{
    return [[[NSApp mainMenu] itemWithTag:SRFileTag] submenu];
}

+ (NSMenu*)editMenu
{
    return [[[NSApp mainMenu] itemWithTag:SREditTag] submenu];
}

+ (NSMenu*)viewMenu
{
    return [[[NSApp mainMenu] itemWithTag:SRViewTag] submenu];
}

+ (NSMenu*)browserMenu
{
    return [[[[[NSApp mainMenu] itemWithTag:SRViewTag] 
            submenu] itemWithTag:SROpenURLWithTag] submenu];
}

+ (NSMenu*)textEncodingMenu
{
    return [[[[[NSApp mainMenu] itemWithTag:SRViewTag] 
            submenu] itemWithTag:SRTextEncodingTag] submenu];
}

+ (NSMenu*)historyMenu
{
    return [[[NSApp mainMenu] itemWithTag:SRHistoryTag] submenu];
}

+ (NSMenu*)bookmarkMenu
{
    return [[[NSApp mainMenu] itemWithTag:SRBookmarkTag] submenu];
}

+ (NSMenu*)windowMenu
{
    return [[[NSApp mainMenu] itemWithTag:SRWindowTag] submenu];
}

//--------------------------------------------------------------//
#pragma mark -- Text encoding menu --
//--------------------------------------------------------------//

- (void)updateTextEncodingMenu
{
    // Get encoding menu
    NSMenu* encodingMenu;
    encodingMenu = [SRAppDelegate textEncodingMenu];
    
    // Append text encoding menu
    NSArray*        encodingItems;
    NSEnumerator*   enumerator;
    id<NSMenuItem>  item;
    encodingItems = [SREncodings textEncodingMenuItems];
    enumerator = [encodingItems objectEnumerator];
    while (item = [enumerator nextObject]) {
        // Except Japanese auto encoding
        if ([item tag] != SRJapaneseAutoDetectEncoding) {
            [encodingMenu addItem:item];
        }
    }
    
    // Set action for 'Default'
    id<NSMenuItem>  defaultItem;
    defaultItem = [encodingMenu itemAtIndex:0];
    [defaultItem setAction:[[encodingItems objectAtIndex:0] action]];
}

//--------------------------------------------------------------//
#pragma mark -- Application support folder --
//--------------------------------------------------------------//

- (NSString*)libraryFolder
{
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    
    // Get the paths of ~/Library/
    NSArray*	libraryPaths;
    NSString*	libraryPath = nil;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    if ([libraryPaths count] > 0) {
        libraryPath = [libraryPaths objectAtIndex:0];
    }
    
    if (!libraryPath || ![fileMgr fileExistsAtPath:libraryPath]) {
        // Error
        SR_ERROR(@"Could not find library directory");
        return nil;
    }
    
    // Check Shiira directory
    NSString*   shiiraPath;
    shiiraPath = [libraryPath stringByAppendingPathComponent:@"Shiira"];
    if (![fileMgr fileExistsAtPath:shiiraPath]) {
        // Create Shiira directory
        [fileMgr createDirectoryAtPath:shiiraPath attributes:nil];
    }
    
    return shiiraPath;
}

- (NSString*)applicationSupportFolder
{
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    
    // Get the paths of ~/Library/Application Support/
    NSArray*	libraryPaths;
    NSString*	libraryPath = nil;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSApplicationSupportDirectory, NSUserDomainMask, YES);
    if ([libraryPaths count] > 0) {
        libraryPath = [libraryPaths objectAtIndex:0];
    }
    
    if (!libraryPath || ![fileMgr fileExistsAtPath:libraryPath]) {
        // Error
        SR_ERROR(@"Could not find applicaiton support directory");
        return nil;
    }
    
    // Check Shiira directory
    NSString*   shiiraPath;
    shiiraPath = [libraryPath stringByAppendingPathComponent:@"Shiira"];
    if (![fileMgr fileExistsAtPath:shiiraPath]) {
        // Create Shiira directory
        [fileMgr createDirectoryAtPath:shiiraPath attributes:nil];
    }
    
    return shiiraPath;
}

//--------------------------------------------------------------//
#pragma mark -- Bookmark menu --
//--------------------------------------------------------------//

- (void)saveBookmarks
{
    // Get the paths of ~/Library/Shiira/Bookmarks.plist
    NSArray*	libraryPaths;
    NSString*	bookmarksPath;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    bookmarksPath = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRBookmarksFileName];
    
    // Check existense
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    if (![fileMgr fileExistsAtPath:bookmarksPath]) {
        // Create file
        SRCreateFile(bookmarksPath);
    }
    
    // Save bookmarks
    SRBookmarkStorage*  storage;
    NSError*            error;
    storage = [SRBookmarkStorage sharedInstance];
    if (![storage saveShiiraBookmarksToURL:[NSURL fileURLWithPath:bookmarksPath] 
            error:&error])
    {
        // Error
        return;
    }
}

- (void)insertBookmarks:(NSArray*)bookmarks 
        inMenu:(NSMenu*)menu 
        atIndex:(int)index
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon];
    
    // Enumerate bookmark
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    enumerator = [bookmarks objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        // Case of HTML type
        if (![bookmark isFolderType]) {
            // Create menu item
            NSMenuItem* menuItem;
            menuItem = [[NSMenuItem alloc] initWithTitle:[bookmark title] 
                    action:@selector(openBookmarkAction:) 
                    keyEquivalent:@""];
            [menuItem autorelease];
            [menuItem setRepresentedObject:bookmark];
            if (isFaviconUsed) {
                [menuItem setImage:[bookmark icon]];
            }
            
            [menu insertItem:menuItem atIndex:index++];
        }
        
        // Case of bookmark folder
        else {
            // Create menu item and submenu
            NSMenuItem* menuItem;
            NSMenu*     submenu;
            menuItem = [[NSMenuItem alloc] initWithTitle:[bookmark title] 
                    action:NULL 
                    keyEquivalent:@""];
            [menuItem autorelease];
            submenu = [[NSMenu alloc] initWithTitle:@""];
            [submenu autorelease];
            [menuItem setSubmenu:submenu];
            if (isFaviconUsed) {
                [menuItem setImage:[bookmark icon]];
            }
            
            [menu insertItem:menuItem atIndex:index++];
            
            // Insert child menu
            [self insertBookmarks:[bookmark children] inMenu:submenu atIndex:0];
        }
    }
}

- (void)updateBookmarkMenu
{
    NSMenu*         bookmarkMenu;
    NSEnumerator*   enumerator;
    NSMenuItem*     menuItem;
    bookmarkMenu = [SRAppDelegate bookmarkMenu];
    [bookmarkMenu update];
    
    enumerator = [[bookmarkMenu itemArray] objectEnumerator];
    while (menuItem = [enumerator nextObject]) {
        if ([menuItem submenu]) {
            [[menuItem submenu] update];
        }
    }
}

//--------------------------------------------------------------//
#pragma mark -- History menu --
//--------------------------------------------------------------//

- (void)loadHistory
{
    // Get the paths of ~/Library/Shiira/History.plist
    NSArray*	libraryPaths;
    NSString*	historyPath;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    historyPath = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRHistoryFileName];
    
    // Check existense
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    if (![fileMgr fileExistsAtPath:historyPath]) {
        return;
    }
    
    // Load history
    WebHistory* history;
    NSError*    error;
    history = [WebHistory optionalSharedHistory];
    if (![history loadFromURL:[NSURL fileURLWithPath:historyPath] error:&error]) {
        // Error
        return;
    }
}

- (void)saveHistory
{
    // Get the paths of ~/Library/Shiira/Bookmarks.plist
    NSArray*	libraryPaths;
    NSString*	historyPath;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    historyPath = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRHistoryFileName];
    
    // Check existense
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    if (![fileMgr fileExistsAtPath:historyPath]) {
        // Create file
        SRCreateFile(historyPath);
    }
    
    // Save history
    WebHistory* history;
    NSError*    error;
    history = [WebHistory optionalSharedHistory];
    if (![history saveToURL:[NSURL fileURLWithPath:historyPath] error:&error]) {
        // Error
        return;
    }
}

- (void)updateHistoryMenu
{
    NSMenu*         historyMenu;
    historyMenu = [SRAppDelegate historyMenu];
    [historyMenu update];
}

//--------------------------------------------------------------//
#pragma mark -- WebHistory notification --
//--------------------------------------------------------------//

- (void)webHistoryItemsUpdated:(NSNotification*)notification
{
    // Update history menu
    [self updateHistoryMenu];
}

//--------------------------------------------------------------//
#pragma mark -- SRBookmarkStorage notification --
//--------------------------------------------------------------//

- (void)bookmarkUpdated:(NSNotification*)notification
{
    // Update bookmark menu
    [self updateBookmarkMenu];
}

//--------------------------------------------------------------//
#pragma mark -- Browser menu --
//--------------------------------------------------------------//

- (void)updateBrowserMenu
{
    // Get broser menu
    NSMenu* browserMenu;
    browserMenu = [SRAppDelegate browserMenu];
    
    // Remove all items
    int i;
    for (i = [browserMenu numberOfItems] - 1; i >= 0; i--) {
        [browserMenu removeItemAtIndex:i];
    }
    
    // Update browser menu
    NSArray*        browserMenuItems;
    NSEnumerator*   enumerator;
    NSMenuItem*     menuItem;
    browserMenuItems = [self browserMenuItemsWithShiira:NO action:@selector(openURLWithAction:)];
    enumerator = [browserMenuItems objectEnumerator];
    while (menuItem = [enumerator nextObject]) {
        [browserMenu addItem:menuItem];
    }
}

//--------------------------------------------------------------//
#pragma mark -- Search engines --
//--------------------------------------------------------------//

- (SRSearchEnginesController*)searchEnginesController
{
    if (!_searchEnginesController) {
        // Load search engines panel
        _searchEnginesController = [[SRSearchEnginesController alloc] initWithWindowNibName:@"SearchEngines"];
        if (!_searchEnginesController) {
            // Fatal
            SR_FATAL(@"Could not load SearchEngines.nib");
        }
        [_searchEnginesController window];
    }
    
    return _searchEnginesController;
}

- (SRFindWindowController*)findWindowController
{
    if (!_findWindowController) {
        // Load find panel
        _findWindowController = [[SRFindWindowController alloc] initWithWindowNibName:@"FindPanel"];
        if (!_findWindowController) {
            // Fatal
            SR_FATAL(@"Could not load FindPanel.nib");
        }
        //load nib
        [_findWindowController window];
    }
    
    return _findWindowController;
}

//--------------------------------------------------------------//
#pragma mark -- Downloads --
//--------------------------------------------------------------//

- (void)loadDownloadHistory
{
    // Get the paths of ~/Library/Shiira/Downloads.plist
    NSArray*	libraryPaths;
    NSString*	downloadsPath;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    downloadsPath = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRDownloadsFileName];
    
    // Check existense
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    if (![fileMgr fileExistsAtPath:downloadsPath]) {
        return;
    }
    
    // Load download history
    SRDownloadHistory*  history;
    history = [SRDownloadHistory sharedInstance];
    if (![history loadFromURL:[NSURL fileURLWithPath:downloadsPath]]) {
        // Error
        return;
    }
}

- (void)saveDownloadHistory
{
    // Get the paths of ~/Library/Shiira/Downloads.plist
    NSArray*	libraryPaths;
    NSString*	downloadsPath;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    downloadsPath = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRDownloadsFileName];
    
    // Check existense
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    if (![fileMgr fileExistsAtPath:downloadsPath]) {
        // Create file
        SRCreateFile(downloadsPath);
    }
    
    // Pause all active downloads
    [[SRDownloadCenter sharedInstance] pauseAllActiveDownloads];
    
    // Save download history
    if (![[SRDownloadHistory sharedInstance] saveToURL:[NSURL fileURLWithPath:downloadsPath]]) {
        // Error
        return;
    }
}

- (SRDownloadsController*)downloadsController
{
    if (!_downloadsController) {
        // Load downloads panel
        _downloadsController = [[SRDownloadsController alloc] initWithWindowNibName:@"Downloads"];
        if (!_downloadsController) {
            // Fatal
            SR_FATAL(@"Could not load Downloads.nib");
        }
    }
    
    return _downloadsController;
}

//--------------------------------------------------------------//
#pragma mark -- Icons --
//--------------------------------------------------------------//

- (void)loadIcons
{
    // Get the paths of ~/Library/Shiira/ and icon file
    NSArray*	libraryPaths;
    NSString*   iconFilePath;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    iconFilePath = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRIconFileName];
    
    // Check existense
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    if (![fileMgr fileExistsAtPath:iconFilePath]) {
        return;
    }
    
    // Load icons
    SRBookmarkIconDatabase* iconDatabase;
    iconDatabase = [SRBookmarkIconDatabase sharedInstance];
    if (![iconDatabase loadFromFile:iconFilePath]) {
        // Error
        return;
    }
}

- (void)saveIcons
{
    // Get the paths of ~/Library/Shiira/ and icon file
    NSArray*	libraryPaths;
    NSString*   iconFilePath;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    iconFilePath = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRIconFileName];
    
    // Check existense
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    if (![fileMgr fileExistsAtPath:iconFilePath]) {
        // Create file
        SRCreateFile(iconFilePath);
    }
    
    // Save icons
    SRBookmarkIconDatabase* iconDatabase;
    iconDatabase = [SRBookmarkIconDatabase sharedInstance];
    if (![iconDatabase saveToFile:iconFilePath]) {
        // Error
        return;
    }
}

//--------------------------------------------------------------//
#pragma mark -- Cache --
//--------------------------------------------------------------//

- (SRCacheController*)cacheController
{
    if (!_cacheController) {
        // Load cache panel
        _cacheController = [[SRCacheController alloc] initWithWindowNibName:@"Cache"];
        if (!_cacheController) {
            // Fatal
            SR_FATAL(@"Could not load Cache.nib");
        }
    }
    
    return _cacheController;
}

//--------------------------------------------------------------//
#pragma mark -- Icon installer --
//--------------------------------------------------------------//
- (SRIconInstaller*)iconInstaller
{
    if (!_iconInstaller) {
        // Create icon installer
        _iconInstaller = [[SRIconInstaller alloc] init];
        
        // Load 'IconInstaller.nib'
        if (![NSBundle loadNibNamed:@"IconInstaller" owner:_iconInstaller]) {
            // Fatal
            SR_FATAL(@"Could not load IconInstaller.nib");
        }
    }
    
    return _iconInstaller;
}

//--------------------------------------------------------------//
#pragma mark -- Protocol library --
//--------------------------------------------------------------//

- (void)updateProtocolLibary
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    static BOOL _isCURLRegistered = NO;
    
    // Get protocol library
    int library;
    library = [defaults integerForKey:SRProtocolLibrary];
    
    if (library == SRProtocolLibraryWebKit) {
        if (_isCURLRegistered) {
            // Unregister URL protocol
            [NSURLProtocol unregisterClass:[SRHTTPURLProtocol class]];
            _isCURLRegistered = NO;
        }
    }
    if (library == SRProtocolLibraryCURL) {
        if (!_isCURLRegistered) {
            // Register URL protocol
            [NSURLProtocol registerClass:[SRHTTPURLProtocol class]];
            _isCURLRegistered = YES;
        }
    }
}

//--------------------------------------------------------------//
#pragma mark -- For Growl --
//--------------------------------------------------------------//

// For Growl support
#ifdef SR_SUPPORT_GROWL

- (NSDictionary*)registrationDictionaryForGrowl
{
    // Create registration information for Growl
    NSArray*        notifications;
    NSDictionary*   infoDict;
    notifications = [NSArray arrayWithObjects:
            SRGrowlDownloadCompletedNotification, 
            nil];
    infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
            notifications, GROWL_NOTIFICATIONS_ALL, 
            notifications, GROWL_NOTIFICATIONS_DEFAULT, 
            nil];
    
    return infoDict;
}

#if 0
// For old Growl
- (void)registerGrowl:(void*)context
{
    // Activate itself
    [NSApp activateIgnoringOtherApps:YES];
    
    // Get application name
    NSString*   appName;
    appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
    
    // Create registration information for Growl
    NSArray*        notifications;
    NSDictionary*   infoDict;
    notifications = [NSArray arrayWithObjects:
            SRGrowlDownloadCompletedNotification, 
            nil];
    infoDict = [NSDictionary dictionaryWithObjectsAndKeys:
            appName, GROWL_APP_NAME, 
            [[NSApp applicationIconImage] TIFFRepresentation], GROWL_APP_ICON, 
            notifications, GROWL_NOTIFICATIONS_ALL, 
            notifications, GROWL_NOTIFICATIONS_DEFAULT, 
            nil];
    
    // Register notifications
	[[NSDistributedNotificationCenter defaultCenter] 
            postNotificationName:GROWL_APP_REGISTRATION 
            object:nil 
            userInfo:infoDict];
}
#endif

#endif // SR_SUPPORT_GROWL

//--------------------------------------------------------------//
#pragma mark -- Actions --
//--------------------------------------------------------------//

- (IBAction)findAction:(id)sender
{
    // Show find panel
    [[self findWindowController] showWindow:self];
}

- (IBAction)findNextAction:(id)sender
{
    // Pass through to find panel
    [[self findWindowController] findNextAction:sender];
}

- (IBAction)findPreviousAction:(id)sender
{
    // Pass through to find panel
    [[self findWindowController] findPreviousAction:sender];
}

- (IBAction)useSelectionForFindAction:(id)sender
{
    // Pass through to find panel
    [[self findWindowController] useSelectionForFindAction:sender];
}

#if 0
- (IBAction)jumpToSelectionAction:(id)sender
{
    // Pass through to find panel
    //[[self findWindowController] jumpToSelectionAction:sender];
}
#endif

- (IBAction)setBrowseModeAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Set browse mode
    int tag;
    int browseMode = -1;
    tag = [sender tag];
    switch (tag) {
    case SRDefaultBrowsingTag: { browseMode = SRDefaultBrowseMode; break; }
    case SRAlwaysInSameTabTag: { browseMode = SRAlwaysInSameTabBrowseMode; break; }
    case SRAlwaysInNewTabTag: { browseMode = SRAlwaysInNewTabBrowseMode; break; }
    case SRAlwaysInNewBackgroundTabTag: { browseMode = SRAlwaysInNewBackgroundTabBrowseMode; break; }
    }
    if (browseMode == -1) {
        return;
    }
    
    [defaults setInteger:browseMode forKey:SRBrowseMode];
}

- (IBAction)clearHistoryAction:(id)sender
{
    // Remove all web history items
    [[WebHistory optionalSharedHistory] removeAllItems];
}

- (IBAction)showPreferencesAction:(id)sender
{
    [[self preferencesController] showPreferences];
}

- (void)blockPopupAction:(id)sender
{
    // Negate javaScriptCanOpenWindowsAutomatically
    BOOL    flag;
    flag = [[SRPreferencesController defaultWebPreferences] javaScriptCanOpenWindowsAutomatically];
    [[SRPreferencesController defaultWebPreferences] setJavaScriptCanOpenWindowsAutomatically:!flag];
}

- (void)emptyCacheAction:(id)sender
{
    // Show alert
    NSAlert*    alert;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    [alert setAlertStyle:NSCriticalAlertStyle];
    [alert setMessageText:NSLocalizedString(@"Are you sure you want to empty the cache?", @"Are you sure you want to empty the cache?")];
    [alert setInformativeText:NSLocalizedString(UTF8STR("Shiira saves the contents of web pages you open in a cache so that it’s faster to visit them again."), UTF8STR("Shiira saves the contents of web pages you open in a cache so that it’s faster to visit them again."))];
    [alert addButtonWithTitle:NSLocalizedString(@"Empty", @"Empty")];
    [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel")];
    
    int result;
    result = [alert runModal];
    if (result != NSAlertFirstButtonReturn) {
        return;
    }
    
    // Empty cache
    [[self cacheController] removeAllCacheAction:self];
}

- (IBAction)showDownloadsPanelAction:(id)sender
{
    [[self downloadsController] showWindow:sender];
}

- (IBAction)showSearchEnginesAction:(id)sender
{
    [[self searchEnginesController] showWindow:sender];
}

- (IBAction)showCacheAction:(id)sender
{
    [[self cacheController] showWindow:sender];
}

- (IBAction)openBookmarkAction:(id)sender
{
    // Get bookmark as represented object
    SRBookmark* bookmark;
    bookmark = [sender representedObject];
    
    if (bookmark && [bookmark isKindOfClass:[SRBookmark class]]) {
        NSDocumentController*   documentController;
        documentController = [NSDocumentController sharedDocumentController];
        
        // Get current document
        SRMainDocument* document;
        document = [documentController currentDocument];
        if (!document) {
            document = [documentController openUntitledDocumentOfType:SRHTMLDocumentType display:YES];
        }
        
        // Open bookmark
        [[document mainWindowController] openBookmark:bookmark];
    }
}

- (void)openBookmarkInTabsAction:(id)sender
{
    NSDocumentController*   documentController;
    documentController = [NSDocumentController sharedDocumentController];
    
    // Get current document
    SRMainDocument* document;
    document = [documentController currentDocument];
    if (!document) {
        document = [documentController openUntitledDocumentOfType:SRHTMLDocumentType display:YES];
    }
    
    // Open bookmark in tabs
    [[document mainWindowController] openBookmarkInTabsAction:sender];
}

- (void)openHistoryItemAction:(id)sender
{
    NSDocumentController*   documentController;
    documentController = [NSDocumentController sharedDocumentController];
    
    // Get current document
    SRMainDocument* document;
    document = [documentController currentDocument];
    if (!document) {
        document = [documentController openUntitledDocumentOfType:SRHTMLDocumentType display:YES];
    }
    
    // Open history
    [[document mainWindowController] openHistoryItemAction:sender];
}

- (void)goHomeAction:(id)sender
{
    NSDocumentController*   documentController;
    documentController = [NSDocumentController sharedDocumentController];
    
    // Get current document
    SRMainDocument* document;
    document = [documentController currentDocument];
    if (!document) {
        document = [documentController openUntitledDocumentOfType:SRHTMLDocumentType display:YES];
    }
    
    // Go home
    [[document mainWindowController] goHomeAction:sender];
}

- (void)closeAllWindowsAction:(id)sender
{
    // Close all windows
    NSEnumerator*   enumerator;
    NSWindow*       window;
    enumerator = [[NSApp windows] objectEnumerator];
    while (window = [enumerator nextObject]) {
        if  ([window styleMask] & NSClosableWindowMask) {
            [window performClose:self];
        }
    }
}

- (void)showHelpAction:(id)sender
{
    // Open 'http://hmdt-web.net/shiira/ShiiraHelp/'
    [self _openURL:[NSURL URLWithString:@"http://hmdt-web.net/shiira/ShiiraHelp/"]];
}

- (void)showShiiraHomePageAction:(id)sender
{
    // Open 'http://hmdt-web.net/shiira/'
    [self _openURL:[NSURL URLWithString:@"http://hmdt-web.net/shiira/index-e.html"]];
}

- (void)showVisualHistoryBackForwardAction:(id)sender
{
    // Show panel
    //[[[SRVHBackForwardController sharedInstance] window] orderFront:self];
    //[[SRVHBackForwardController sharedInstance] update];
}

- (void)togglePrivateBrowseAction:(id)sender
{
    // Get WebPreferences
    WebPreferences* preferences;
    preferences = [SRPreferencesController defaultWebPreferences];
    
    // Get currnet setting
    BOOL    isPrivate;
    isPrivate = [preferences privateBrowsingEnabled];
    
    // Show alert
    if (!isPrivate) {
        NSAlert*    alert;
        alert = [[NSAlert alloc] init];
        [alert setMessageText:NSLocalizedString(@"Are you sure you want to turn on private browsing?", @"Are you sure you want to turn on private browsing?")];
        [alert setInformativeText:NSLocalizedString(@"When private browsing is turned on, webpages are not added to the history, items are automatically removed from the Downloads window, information isn't saved for AutoFill (including names and passwords), and searches are not added to the pop-up menu in the Google search box. Until you close the window, you can still click the Back and Forward buttons to return to webpages you have opened.", @"When private browsing is turned on, webpages are not added to the history, items are automatically removed from the Downloads window, information isn't saved for AutoFill (including names and passwords), and searches are not added to the pop-up menu in the Google search box. Until you close the window, you can still click the Back and Forward buttons to return to webpages you have opened.")];
        [alert setAlertStyle:NSInformationalAlertStyle];
        [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK")];
        [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel")];
        
        int result;
        result = [alert runModal];
        [alert release];
        if (result != NSAlertFirstButtonReturn) {
            return;
        }
    }
    
    // Toggle private browse mode
    [preferences setPrivateBrowsingEnabled:!isPrivate];
}

//--------------------------------------------------------------//
#pragma mark -- NSMenu delegate --
//--------------------------------------------------------------//

- (void)menuNeedsUpdate:(NSMenu*)menu
{
    // Browser menu
    if (menu == [SRAppDelegate browserMenu]) {
        [self updateBrowserMenu];
    }
    
    // Text encoding menu
    if (menu == [SRAppDelegate textEncodingMenu]) {
        [self updateTextEncodingMenu];
    }
}

- (BOOL)menuHasKeyEquivalent:(NSMenu*)menu 
        forEvent:(NSEvent*)event 
        target:(id*)target 
        action:(SEL*)action
{
    // Get key window and its window controller
    NSWindow*   keyWindow;
    id          windowController;
    keyWindow = [NSApp keyWindow];
    windowController = [keyWindow windowController];
    
    // Get attributes
    unsigned int    modifierFlags;
    BOOL            optFlag, shiftFlag;
    NSString*       characters;
    modifierFlags = [event modifierFlags];
    optFlag = modifierFlags & NSAlternateKeyMask;
    shiftFlag = modifierFlags & NSShiftKeyMask;
    characters = [event charactersIgnoringModifiers];
    
    // For file menu
    if (menu == [SRAppDelegate fileMenu]) {
        // For cmd + 'w'
        if (!optFlag && !shiftFlag && [characters isEqualToString:@"w"]) {
            // For main window controller
            if (windowController && [windowController isKindOfClass:[SRMainWindowController class]]) {
                return [windowController menuHasKeyEquivalent:menu 
                        forEvent:event 
                        target:target 
                        action:action];
            }
            
            // Use close window action
            *target = nil;
            *action = @selector(closeWindowAction:);
            
            return YES;
        }
        
        // For cmd + opt + 'w'
        if (optFlag && !shiftFlag && [characters isEqualToString:@"w"]) {
            // Use close all windows action
            *target = nil;
            *action = @selector(closeAllWindowsAction:);
            
            return YES;
        }
    }
    
    return NO;
}

//--------------------------------------------------------------//
#pragma mark -- NSMenuValidataion method --
//--------------------------------------------------------------//

- (BOOL)validateMenuItem:(id<NSMenuItem>)menuItem
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get tag
    int tag;
    tag = [menuItem tag];
    
    // Get tab browsing
    BOOL    isTabbedBrowsing;
    isTabbedBrowsing = [defaults boolForKey:SRTabEnableTabbedBrowsing];
    
    // Get browse mode
    int browseMode;
    browseMode = [defaults integerForKey:SRBrowseMode];
    
    switch (tag) {
    // Shiira menu
    case SRBlockPopupWindowTag: {
        BOOL    isBlockedPopup;
        isBlockedPopup = ![[SRPreferencesController defaultWebPreferences] javaScriptCanOpenWindowsAutomatically]; 
        [menuItem setState:isBlockedPopup ? NSOnState : NSOffState];
        return YES;
    }
    case SRPrivateBrowseTag: {
        // Get private browse setting
        WebPreferences* preferences;
        BOOL            isPrivate;
        preferences = [SRPreferencesController defaultWebPreferences];
        isPrivate = [preferences privateBrowsingEnabled];
        
        [menuItem setState:isPrivate ? NSOnState : NSOffState];
        return YES;
    }
    
    // View menu
    case SRDefaultBrowsingTag: {
        [menuItem setState:(browseMode == SRDefaultBrowseMode ? NSOnState : NSOffState)];
        return YES;
    }
    case SRAlwaysInSameTabTag: {
        static  NSString*   _openLinkInSameTab = nil;
        static  NSString*   _openLinkInSameWindow = nil;
        if (!_openLinkInSameTab) {
            _openLinkInSameTab = [NSLocalizedString(@"Always Open Link in Same Tab", nil) retain];
            _openLinkInSameWindow = [NSLocalizedString(@"Always Open Link in Same Window", nil) retain];
        }
        
        [menuItem setTitle:(isTabbedBrowsing ? _openLinkInSameTab : _openLinkInSameWindow)];
        [menuItem setState:(browseMode == SRAlwaysInSameTabBrowseMode ? NSOnState : NSOffState)];
        return YES;
    }
    case SRAlwaysInNewTabTag: {
        [menuItem setState:(browseMode == SRAlwaysInNewTabBrowseMode ? NSOnState : NSOffState)];
        return isTabbedBrowsing;
    }
    case SRAlwaysInNewBackgroundTabTag: {
        [menuItem setState:(browseMode == SRAlwaysInNewBackgroundTabBrowseMode ? NSOnState : NSOffState)];
        return isTabbedBrowsing;
    }
    }
    
    return YES;
}

//--------------------------------------------------------------//
#pragma mark -- Key value observation --
//--------------------------------------------------------------//

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    // For NSUserDefault
    if (object == [NSUserDefaults standardUserDefaults]) {
        // General preferences
        if ([keyPath isEqualToString:SRProtocolLibrary]) {
            // Update protocol library
            [self updateProtocolLibary];
            return;
        }
        
        // Bookmark preferences
        if ([keyPath isEqualToString:SRBookmarkBookmarksBar]) {
            // Update bookmark menu
            [self updateBookmarkMenu];
            return;
        }
        if ([keyPath isEqualToString:SRIconUseFavicon] || 
            [keyPath isEqualToString:SRIconUseFaviconBookmarkMenu])
        {
            // Update bookmark and history menu
            [self updateBookmarkMenu];
            [self updateHistoryMenu];
            return;
        }
        if ([keyPath isEqualToString:SRTabShowAllTabsShortcut]) {
            // Get new shortcut
            int shortcutTag;
            shortcutTag = [object integerForKey:SRTabShowAllTabsShortcut];
            if (shortcutTag < 0 || shortcutTag > 14) {
                return;
            }
            
            // Change shortcut of 'Show All Tabs'
            id <NSMenuItem> showAllTabsItem;
            showAllTabsItem = [[SRAppDelegate windowMenu] itemWithTag:SRShowAllTabsTag];
            
            unichar c;
            c = NSF1FunctionKey + shortcutTag;
            [showAllTabsItem setKeyEquivalent:[NSString stringWithCharacters:&c length:1]];
            return;
        }
    }
}

//--------------------------------------------------------------//
#pragma mark -- openURL --
//--------------------------------------------------------------//

- (void)openURL:(NSAppleEventDescriptor*)event 
        withReplyEvent:(NSAppleEventDescriptor*)replyEvent
{
	// Create requested URL
    NSString*   urlString;
    NSURL*      URL;
    urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
	URL = [NSURL URLWithString:urlString];
    
    // Open URL
    [self _openURL:URL];
}

- (void)_openURL:(NSURL*)URL
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    SRMainWindowController*  currentWC=nil;
    NSDocumentController*   docCtl=[NSDocumentController sharedDocumentController];
    // Check exists WebView and get frontmostBrowserDocument
    int i,cnt;
    NSArray* documents=[NSApp orderedDocuments];
    cnt=[documents count];
    for(i=0;i<cnt;i++){
        NSDocument* aDocument=[documents objectAtIndex:i];
        if([aDocument isKindOfClass:[SRMainDocument class]]){
            SRMainWindowController*  mainWindowController;
        
            mainWindowController=(SRMainWindowController*)[(SRMainDocument*)aDocument mainWindowController];
            if(currentWC==nil)  currentWC=mainWindowController;
            if([mainWindowController reloadURLIfExists:URL]){
                return;
            }
        }
    }
    
    if(currentWC){
        // Check web data source of selected web view
        if (![[[currentWC selectedWebView] mainFrame] dataSource]) {
            // Use front empty webView
            [currentWC openURL:URL];
            // Bring to front
            [currentWC showWindow:self];
            return;
        }
        
        // Make new tab of current window
        if([defaults boolForKey:SRTabEnableTabbedBrowsing] && [defaults boolForKey:SRTabOpenURLUseTab]){
            [currentWC openInNewTabURL:URL select:YES];
            // Bring to front
            [currentWC showWindow:self];
            return;
        }
    }
    
    // Create new document
    SRMainDocument* document;
    document = [docCtl openUntitledDocumentOfType:SRHTMLDocumentType display:YES];
    
    // Get main window controller
    currentWC = [document mainWindowController];
    
    // Open bookmark in new window
    [currentWC openURL:URL];
    
    // Open document with URL
//    [[NSDocumentController sharedDocumentController] 
//            openDocumentWithContentsOfURL:URL display:YES];

}

//--------------------------------------------------------------//
#pragma mark -- With other browsers --
//--------------------------------------------------------------//

- (NSArray*)browserURLsWithShiira:(BOOL)flag
{
    // URLs for checking
    NSURL*  httpURL;
    NSURL*  httpsURL;
    NSURL*  fileURL;
    httpURL = [NSURL URLWithString:@"http://www.apple.com/"];
    httpsURL = [NSURL URLWithString:@"https://www.apple.com/"];
    fileURL = [NSURL fileURLWithPath:@"/Users/name/index.html"];
    
    // Get application URLs for http
    CFArrayRef      applicationsRef;
    NSMutableArray* applications;
    applicationsRef = LSCopyApplicationURLsForURL((CFURLRef)httpURL, kLSRolesAll);
    applications = [NSMutableArray array];
    
    int i;
    for (i = 0; i < CFArrayGetCount(applicationsRef); i++) {
        CFURLRef    appURLRef;
        CFStringRef stringRef;
        appURLRef = (CFURLRef)CFArrayGetValueAtIndex(applicationsRef, i);
        stringRef = CFURLGetString(appURLRef);
        
        [applications addObject:[NSURL URLWithString:(NSString*)stringRef]];
        
//        CFRelease(appURLRef);
    }
    
    CFRelease(applicationsRef);
    
    // Check with https and file scheme
    NSMutableArray* browsers;
    NSEnumerator*   enumerator;
    NSURL*          appURL;
    browsers = [NSMutableArray array];
    enumerator = [applications objectEnumerator];
    while (appURL = [enumerator nextObject]) {
        // Except Shiira
        if (!flag) {
            if ([[[appURL path] lastPathComponent] rangeOfString:@"Shiira"].length != 0) {
                continue;
            }
        }
        
        // Check with https
        OSStatus    status;
        Boolean     accepts;
        status = LSCanURLAcceptURL(
                (CFURLRef)httpsURL, (CFURLRef)appURL, kLSRolesViewer/*kLSRolesAll*/, kLSAcceptDefault, &accepts);
        if (!accepts) {
            continue;
        }
        
#if 0
// Dose Tiger support file scheme?
        // Check with file
        status = LSCanURLAcceptURL(
                (CFURLRef)fileURL, (CFURLRef)appURL, kLSRolesViewer/*kLSRolesAll*/, kLSAcceptDefault, &accepts);
        if (!accepts) {
            continue;
        }
#endif
        
        // Recognize it as an browser
        [browsers addObject:appURL];
    }
    
    return browsers;
}

- (NSArray*)browserMenuItemsWithShiira:(BOOL)flag action:(SEL)action
{
    // Get browser URLs
    NSArray*    browserURLs;
    browserURLs = [self browserURLsWithShiira:flag];
    
    // Create menu items
    NSMutableArray* menuItems;
    NSEnumerator*   enumerator;
    NSURL*          appURL;
    menuItems = [NSMutableArray array];
    enumerator = [browserURLs objectEnumerator];
    while (appURL = [enumerator nextObject]) {
        // Get app name
        NSString*   appPath;
        NSString*   appName;
        appPath = [appURL path];
        appName = [[appPath lastPathComponent] stringByDeletingPathExtension];
        
        // Check duplication
        NSEnumerator*   tmpEnumerator;
        NSURL*          tmpAppURL;
        tmpEnumerator = [browserURLs objectEnumerator];
        while (tmpAppURL = [tmpEnumerator nextObject]) {
            if (appURL != tmpAppURL) {
                if ([[[tmpAppURL path] lastPathComponent] isEqualToString:appName]) {
                    // Composite file path to name
                    appName = [NSString stringWithFormat:@"%@ - %@", 
                            appName, [appPath stringByDeletingLastPathComponent]];
                    break;
                }
            }
        }
        
        // Get icon for this app
        NSImage*    icon;
        icon = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
        [icon setSize:NSMakeSize(16, 16)];
        
        // Create menu item
        NSMenuItem* menuItem;
        menuItem = [[NSMenuItem alloc] 
                initWithTitle:appName action:action keyEquivalent:@""];
        [menuItem autorelease];
        [menuItem setTag:SROpenURLWithBrowserItemTag];
        [menuItem setRepresentedObject:appURL];
        if (icon) {
            [menuItem setImage:icon];
        }
        
        // Add menu item
        [menuItems addObject:menuItem];
    }
        
    return menuItems;
}

//--------------------------------------------------------------//
#pragma mark -- About panel --
//--------------------------------------------------------------//

- (SRAboutController*)aboutController
{
    if (!_aboutController) {
        // Load about panel
        _aboutController = [[SRAboutController alloc] initWithWindowNibName:@"About"];
        if (!_aboutController) {
            // Fatal
            SR_FATAL(@"Could not load About.nib");
        }
        //load nib
        [_aboutController window];
    }
    
    return _aboutController;
}

- (void)orderFrontStandardAboutPanel:(id)sender
{
    // Get about controller
    SRAboutController*  aboutController;
    aboutController = [self aboutController];
    
    // Set version
    NSString*   shortVersion;
    NSString*   version;
    shortVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    
    NSString*   versionString = @"";
    if (shortVersion && version && [version length] > 1) {
        versionString = [NSString stringWithFormat:@"%@\n(%@)", shortVersion, version];
    }
    else if (shortVersion) {
        versionString = shortVersion;
    }
    [[aboutController versionTextField] setStringValue:versionString];
    
    
    // Set credit
    NSString*   creditPath;
    creditPath = [[NSBundle mainBundle] pathForResource:@"Credits" ofType:@"rtf"];
    if (creditPath) {
        NSAttributedString* credit;
        credit = [[NSAttributedString alloc] initWithPath:creditPath documentAttributes:NULL];
        [credit autorelease];
        if (credit) {
            [[[aboutController creditTextView] textStorage] setAttributedString:credit];
        }
    }
    
    [[aboutController window] makeKeyAndOrderFront:self];
}

@end

#pragma mark -

//--------------------------------------------------------------//
#pragma mark -- Utitlity --
//--------------------------------------------------------------//

NSString* SRAlternateTitleOfWebHistoryItem(
        WebHistoryItem* historyItem)
{
    NSString*   title;
    
    // Decide title
    title = [historyItem title];
    if (!title || [title length] == 0) {
        title = [historyItem URLString];
    }
    if (!title || [title length] == 0) {
        title = NSLocalizedString(@"Untitled", @"Untitled");
    }
    
    // Truncate title
    int length;
    length = [title length];
    if (length > 51) {
        static NSString*    _horizontalEllipsis = nil;
        if (!_horizontalEllipsis) {
            unichar uni[1];
            uni[0] = 0x2026;
            _horizontalEllipsis = [[NSString stringWithCharacters:uni length:1] retain];
        }
        
        title = [NSString stringWithFormat:@"%@%@%@", 
                [title substringWithRange:NSMakeRange(0, 25)], 
                _horizontalEllipsis, 
                [title substringWithRange:NSMakeRange(length - 25, 25)]];
    }
    
    return title;
}
