/*
SRPreferencesController.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 "SRDefaultsKey.h"

#import "SRAppDelegate.h"
#import "SRMainWindowController.h"
#import "SRPreferencesController.h"
#import "SRIconInstaller.h"
#import "SRBookmark.h"
#import "SRBookmarkStorage.h"
#import "SREncodings.h"
#import "SRRSSManager.h"

#import "SRImageTextCell.h"

#import "SRUtil.h"
#import "FoundationEx.h"
#import "AppKitEx.h"
#import "WebKitEx.h"

// Default web preferences identifier
NSString*   SRWebPreferencesIdentifier = @"ShiiraPreferences";

// Toolbar items
static NSArray* _toolbarItemIdentifiers = nil;

// Toolbar identifier
NSString*   SRPreferencesToolbarIdentifier = @"SRPreferencesToolbarIdentifier";

// Toolbar item identifier
NSString*   SRGeneralIdentifier = @"SRGeneral";
NSString*   SRAppearanceIdentifier = @"SRAppearance";
NSString*   SRBookmarksIdentifier = @"SRBookmarks";
NSString*   SRTabsIdentifier = @"SRTabs";
NSString*   SRSecurityIdentifier = @"SRSecurity";
NSString*   SRAdvancedIdentifier = @"SRAdvanced";
NSString*   SRIconIdentifier = @"SRIcon";
NSString*   SRSourceIdentifier = @"SRSource";
NSString*   SRSidebarPrefIdentifier = @"SRSidebarPref";
NSString*   SRRSSIdentifier = @"SRRSS";

NSString*   SRPreferencesToolbarLabelTable = @"PrefToolbar";

// Toolbar icons
static NSDictionary*    _iconImageDict = nil;

// Font size menu tag value
static int  _fontSizeSeparator = 511;
static int  _otherFontSize = 512;
static int  _defaultFontSizeSetting = 0;
static int  _fixedFontSizeSetting = 1;
static int  _minFontSizeSetting = 2;

@interface SRPreferencesController (private)
- (void)_updateIconMatrixWithIconName:(NSString*)name;
- (void)_updateIconDeleteButton;
- (void)_updateRSSUpdatemMinutesTextField;
@end

#pragma mark -

@implementation SRPreferencesController

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

+ (WebPreferences*)defaultWebPreferences
{
    // Default web preferences
    static WebPreferences*  _defaultWebPreferences = nil;
    
    if (!_defaultWebPreferences) {
        // Create default web preferences
        _defaultWebPreferences = [[WebPreferences alloc] 
                initWithIdentifier:SRWebPreferencesIdentifier];
        [_defaultWebPreferences setAutosaves:YES];
    }
    
    return _defaultWebPreferences;
}

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize toolbar identifiers
    static BOOL _isInitialized = NO;
    if (!_isInitialized) {
        // Create toolbar item identifiers
        _toolbarItemIdentifiers = [[NSArray arrayWithObjects:
            SRGeneralIdentifier, SRAppearanceIdentifier, SRBookmarksIdentifier, SRTabsIdentifier, 
            SRSecurityIdentifier, SRAdvancedIdentifier, SRIconIdentifier, SRSourceIdentifier, 
            SRSidebarPrefIdentifier, SRRSSIdentifier, nil] retain];
    }
    
    return self;
}

- (void)awakeFromNib
{
    static BOOL _isInitialized = NO;
    if (_isInitialized) {
        return;
    }
    _isInitialized = YES;
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Configure preferences controller
    [_preferencesController setContent:[SRPreferencesController defaultWebPreferences]];
    
    // Configure cookie storage controller
    [_cookieStorageController 
            setContent:[NSHTTPCookieStorage sharedHTTPCookieStorage]];
    
    // Configure cookies controller
    [_cookiesController 
            setContent:[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]];
    
    // Get toolbar icons
    NSString*   iconName;
    iconName = [defaults stringForKey:SRIconName];
    _iconImageDict = [[[[NSApp delegate] iconInstaller] iconsOfName:iconName] retain];
    
    // Create toolbar
    NSToolbar*  toolbar;
    toolbar = [[[NSToolbar alloc] 
            initWithIdentifier:@"MyFirstPreferences"] autorelease];
    [toolbar setDelegate:self];
    [_preferencesPanel setToolbar:toolbar];
    
    // Set frame top left point
    NSRect  frame;
    frame = NSRectFromString([defaults stringForKey:SRPreferencesPanelFrame]);
    if (!NSIsEmptyRect(frame)) {
        [_preferencesPanel setFrameTopLeftPoint:
                NSMakePoint(frame.origin.x, frame.origin.y + frame.size.height)];
    }
    
    // Register observer
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self 
            selector:@selector(applicationWillTerminate:) 
            name:NSApplicationWillTerminateNotification 
            object:nil];
    
    // Register key value observation
    [defaults addObserver:self 
            forKeyPath:SRIconName 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObserver:self forKeyPath:SRIconName];
    
    [[_preferencesPanel toolbar] setDelegate:nil];
    
    [_browsers release];
    [_isUsing release];
    [_localizedBrowserNames release];
    [_browserIcons release];
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Managing panel --
//--------------------------------------------------------------//

- (void)_loadNibNamed:(NSString*)nibName
{
    if (![NSBundle loadNibNamed:nibName owner:self]) {
        // Fatal
        SR_FATAL(@"Could not load %@", nibName);
    }
}

- (NSView*)viewForIdentifier:(NSString*)identifier
{
    // Load nib files
    if ([identifier isEqualToString:SRGeneralIdentifier]) {
        if (!_generalView) [self _loadNibNamed:@"PreferencesGeneral"];
        return _generalView;
    }
    if ([identifier isEqualToString:SRAppearanceIdentifier]) {
        if (!_appearanceView) [self _loadNibNamed:@"PreferencesAppearance"];
        return _appearanceView;
    }
    if ([identifier isEqualToString:SRBookmarksIdentifier]) {
        if (!_bookmarksView) [self _loadNibNamed:@"PreferencesBookmarks"];
        return _bookmarksView;
    }
    if ([identifier isEqualToString:SRTabsIdentifier]) {
        if (!_tabsView) [self _loadNibNamed:@"PreferencesTabs"];
        return _tabsView;
    }
    if ([identifier isEqualToString:SRSecurityIdentifier]) {
        if (!_securityView) [self _loadNibNamed:@"PreferencesSecurity"];
        return _securityView;
    }
    if ([identifier isEqualToString:SRAdvancedIdentifier]) {
        if (!_advancedView) [self _loadNibNamed:@"PreferencesAdvanced"];
        return _advancedView;
    }
    if ([identifier isEqualToString:SRIconIdentifier]) {
        if (!_iconView) [self _loadNibNamed:@"PreferencesIcon"];
        return _iconView;
    }
    if ([identifier isEqualToString:SRSourceIdentifier]) {
        if (!_sourceView) [self _loadNibNamed:@"PreferencesSource"];
        return _sourceView;
    }
    if ([identifier isEqualToString:SRSidebarPrefIdentifier]) {
        if (!_sidebarPrefView) [self _loadNibNamed:@"PreferencesSidebar"];
        return _sidebarPrefView;
    }
    if ([identifier isEqualToString:SRRSSIdentifier]) {
        if (!_RSSPrefView) [self _loadNibNamed:@"PreferencesRSS"];
        return _RSSPrefView;
    }
    
    return nil;
}

- (void)_initializePreferencesPanel
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Update download path popup
    NSString*   downloadPath;
    downloadPath = [defaults stringForKey:SRGeneralDownloadPath];
    if (downloadPath) {
        if ([_downloadPathPopup numberOfItems] > 0) {
            [_downloadPathPopup removeItemAtIndex:0];
            [_downloadPathPopup insertItemWithTitle:[downloadPath lastPathComponent] atIndex:0];
            [_downloadPathPopup selectItemAtIndex:0];
        }
    }
    
    // Set default font size
    int defaultFontSize;
    defaultFontSize = [[SRPreferencesController defaultWebPreferences] defaultFontSize];
    [self _popup:_defaultFontSizePopup 
            selectItemWithTag:defaultFontSize 
            replaceMenuItem:&_defaultFontSizeOtherItem 
            withTitle:[NSString stringWithFormat:@"%d", defaultFontSize] 
            aboveTag:_fontSizeSeparator];
    
    // Set fixed font size
    int fixedFontSize;
    fixedFontSize = [[SRPreferencesController defaultWebPreferences] defaultFixedFontSize];
    [self _popup:_fixedFontSizePopup 
            selectItemWithTag:fixedFontSize 
            replaceMenuItem:&_fixedFontSizeOtherItem 
            withTitle:[NSString stringWithFormat:@"%d", fixedFontSize] 
            aboveTag:_fontSizeSeparator];
    
    // Set minimum font size
    int minFontSize;
    minFontSize = [[SRPreferencesController defaultWebPreferences] minimumFontSize];
    [self _popup:_minFontSizePopup 
            selectItemWithTag:minFontSize 
            replaceMenuItem:&_minFontSizeOtherItem 
            withTitle:[NSString stringWithFormat:@"%d", minFontSize] 
            aboveTag:_fontSizeSeparator];
    
    // Update default encoding popup
    NSPopUpButton*      popup;
    NSStringEncoding    encoding;
    int                 encodingIndex;
    popup = [_encodings encodingPopup];
    encoding = [[defaults objectForKey:SRDefaultTextEncoding] unsignedIntValue];
    encodingIndex = [popup indexOfItemWithTag:encoding];
    if (encodingIndex != -1 && encodingIndex < [popup numberOfItems]) {
        [popup selectItemAtIndex:encodingIndex];
    }
    
    // Update style sheet popup
    NSURL*      styleSheetURL;
    NSString*   styleSheet;
    styleSheetURL = [[SRPreferencesController defaultWebPreferences] userStyleSheetLocation];
    if (!styleSheetURL) {
        styleSheet = @"";
    }
    else {
        if ([styleSheetURL isFileURL]) {
            styleSheet = [[styleSheetURL path] lastPathComponent];
        }
        else {
            styleSheet = [styleSheetURL absoluteString];
        }
    }
    
    if ([_styleSheetPopup numberOfItems] > 0) {
        [_styleSheetPopup removeItemAtIndex:0];
        [_styleSheetPopup insertItemWithTitle:styleSheet atIndex:0];
        [_styleSheetPopup selectItemAtIndex:0];
    }
    
    // Update user agent popup
    int         userAgentIndex = 1;
    int         userAgentTag = 1;
    NSArray*    userAgents;
    userAgents = [SRPreferencesController userAgents];
    if (userAgents) {
        NSEnumerator*   enumerator;
        NSDictionary*   userAgentInfo;
        enumerator = [userAgents objectEnumerator];
        while (userAgentInfo = [enumerator nextObject]) {
            NSString*   title;
            NSString*   userAgent;
            title = [userAgentInfo objectForKey:@"title"];
            userAgent = [userAgentInfo objectForKey:@"userAgent"];
            
            if (title && userAgent) {
                // Create menu item
                NSMenuItem* menuItem;
                menuItem = [[NSMenuItem alloc] 
                        initWithTitle:title action:@selector(setUserAgentAction:) keyEquivalent:@""];
                [menuItem autorelease];
                [menuItem setTag:userAgentTag++];
                
                [[_userAgentPopup menu] insertItem:menuItem atIndex:userAgentIndex++];
            }
        }
    }
    
    id<NSMenuItem>  oldItem, newItem;
    int otherItemIndex;
    otherItemIndex = [_userAgentPopup indexOfItemWithTag:999];
    if (otherItemIndex != -1 && [_userAgentPopup numberOfItems] > otherItemIndex) {
        oldItem = [[_userAgentPopup itemAtIndex:otherItemIndex] retain];
        [_userAgentPopup removeItemAtIndex:otherItemIndex];
        [_userAgentPopup insertItemWithTitle:[defaults objectForKey:SRUserAgentOtherName] atIndex:otherItemIndex];
        newItem = [_userAgentPopup itemAtIndex:otherItemIndex];
        [newItem setTarget:[oldItem target]];
        [newItem setAction:[oldItem action]];
        [newItem setTag:[oldItem tag]];
        [oldItem release];
    }
    
    int userAgent;
    userAgent = [defaults integerForKey:SRUserAgent];
    userAgentIndex = [_userAgentPopup indexOfItemWithTag:userAgent];
    if (userAgentIndex != -1) {
        [_userAgentPopup selectItemAtIndex:userAgentIndex];
    }
    
    // Update icon popup
    NSRect  iconPopupFrame;
    iconPopupFrame = [_iconPopup frame];
    iconPopupFrame.size.width = 32 * 5 + 36; // 196
    iconPopupFrame.size.height = 32 + 18; // 50
    [_iconPopup setFrame:iconPopupFrame];
    [[_iconPopup cell] setBezelStyle:NSSmallIconButtonBezelStyle];
    [[_iconPopup cell] setArrowPosition:NSPopUpArrowAtBottom];
    
    // Set source font size
    int sourceFontSize;
    int index;
    sourceFontSize = [defaults integerForKey:SRSourceFontSize];
    index = [_sourceFontSizePopup indexOfItemWithTag:sourceFontSize];
    if (index >= 0) {
        [_sourceFontSizePopup selectItemAtIndex:index];
    }
    
    // Update bookmarks bar popup
    NSWorkspace*    workspace;
    workspace = [NSWorkspace sharedWorkspace];
    
    int i;
    for (i = [_bookmarksBarPopup numberOfItems] - 1; i >= 0; i--) {
        // Get menu item
        id <NSMenuItem> item;
        item = [_bookmarksBarPopup itemAtIndex:i];
        if (!item) {
            continue;
        }
        
        // Get tag
        int tag;
        tag = [item tag];
        
        // Check bookmark existence
        if (![[SRBookmarkStorage sharedInstance] hasBookmarkOfBrowser:tag]) {
            [_bookmarksBarPopup removeItemAtIndex:i];
            continue;
        }
        
        // Get browser name
        NSString*   browserName = nil;
        if (tag < [SRBrowserNames count]) {
            browserName = [SRBrowserNames objectAtIndex:tag];
        }
        if (!browserName) {
            [_bookmarksBarPopup removeItemAtIndex:i];
            continue;
        }
        
        // Get browser icon
        NSString*   browserPath;
        NSImage*    icon;
        browserPath = [workspace fullPathForApplication:browserName];
        if (!browserPath) {
            [_bookmarksBarPopup removeItemAtIndex:i];
            continue;
        }
        icon = [workspace iconForFile:browserPath];
        if (icon) {
            [icon setSize:NSMakeSize(16, 16)];
            [item setImage:icon];
        }
    }
    
    // Configure bookmarks menu table
    _browsers = [[NSMutableArray array] retain];
    _isUsing = [[NSMutableArray array] retain];
    _localizedBrowserNames = [[NSMutableArray array] retain];
    _browserIcons = [[NSMutableArray array] retain];
    
    NSArray*    isUsing;
    isUsing = [defaults objectForKey:SRBookmarkIsUsing];
    
    // Skip Shiira
    for (i = 1; i < SRNumberOfBrowser; i++) {
        // Check bookmark existance
        if (![[SRBookmarkStorage sharedInstance] hasBookmarkOfBrowser:i]) {
            continue;
        }
        if ([isUsing count] < i) {
            break;
        }
        
        // Get browser name
        NSString*       browserName = nil;
        NSBundle*       browserBundle;
        NSDictionary*   infoDict;
        NSString*       localizedName;
        if (i < [SRBrowserNames count]) {
            browserName = [SRBrowserNames objectAtIndex:i];
        }
        if (!browserName) {
            continue;
        }
        
        // Get browser icon
        NSString*   browserPath;
        NSImage*    icon;
        browserPath = [workspace fullPathForApplication:browserName];
        if (!browserPath) {
            //[_bookmarksBarPopup removeItemAtIndex:i];
            continue;
        }
        icon = [workspace iconForFile:browserPath];
        if (icon) {
            [icon setSize:NSMakeSize(16, 16)];
        }
        
        // Get localized name
        browserBundle = [NSBundle bundleWithPath:browserPath];
        infoDict = [browserBundle localizedInfoDictionary];
        localizedName = [infoDict objectForKey:@"CFBundleDisplayName"];
        if (localizedName) {
            browserName = localizedName;
        }
        
        // Set browser status
        [_browsers addObject:[NSNumber numberWithInt:i]];
        [_isUsing addObject:[isUsing objectAtIndex:i]];
        [_localizedBrowserNames addObject:browserName];
        [_browserIcons addObject:icon];
    }
    
    // Configure bookmark table
    NSTableColumn*      column;
    NSCell*             oldCell;
    SRImageTextCell*    cell;
    
    column = [_bookmarksTableView tableColumnWithIdentifier:@"browser"];
    oldCell = [column dataCell];
    cell = [[SRImageTextCell alloc] init];
    [cell autorelease];
    [cell setEditable:NO];
    [cell setFont:[oldCell font]];
    [column setDataCell:cell];
    
    // Show or hide transition effect check box
    [_transitionEffectCheckBox setHidden:!SRHasQuartzFramework()];
    
    // Show or hide transition enabled label
    [_transitionEffectLabel setHidden:!SRHasQuartzFramework() || CGDisplayUsesOpenGLAcceleration(CGMainDisplayID())];
    
    // Show or hide unifed toolbar check box
    [_unifiedToolbarCheckBox setHidden:NSAppKitVersionNumber < SRAppKitVersionNumber10_4];
    
    // Update RSS update minutes text field
    [self _updateRSSUpdatemMinutesTextField];
    
    // Setup sound popup
    NSArray*        soundPopups;
    NSArray*        actionNames;
    soundPopups = [NSArray arrayWithObjects:_soundPageLoadDonePopup, _soundPageLoadErrorPopup, nil];
    actionNames = [NSArray arrayWithObjects:SRSoundPageLoadDone, SRSoundPageLoadError, nil];
    for (i = 0; i < [soundPopups count]; i++) {
        NSPopUpButton*  soundPopup;
        soundPopup = [soundPopups objectAtIndex:i];
        
        // Remove all objects
        [soundPopup removeAllItems];
        
        // Get menu
        NSMenu* menu;
        menu = [soundPopup menu];
        
        id<NSMenuItem>  menuItem;
        
        // Add 'None'
        menuItem = [menu addItemWithTitle:NSLocalizedString(@"None", nil) action:NULL keyEquivalent:@""]; 
        [menuItem setRepresentedObject:@"None"];
        [menu addItem:[NSMenuItem separatorItem]];
        
        // Add sound file name
        NSArray*        soundPaths;
        NSEnumerator*   soundPathEnumerator;
        NSString*       soundPath;
        soundPaths = SoundFilePaths();
        soundPathEnumerator = [soundPaths objectEnumerator];
        while (soundPath = [soundPathEnumerator nextObject]) {
            NSString*   soundName;
            soundName = [soundPath lastPathComponent];
            soundName = [soundName stringByDeletingPathExtension];
            
            menuItem = [menu addItemWithTitle:soundName action:NULL keyEquivalent:@""]; 
            [menuItem setRepresentedObject:soundPath];
        }
        
        // Set sound
        NSString*   actionName;
        actionName = [actionNames objectAtIndex:i];
        soundPath = [defaults stringForKey:actionName];
        if (soundPath) {
            int index;
            index = [soundPopup indexOfItemWithRepresentedObject:soundPath];
            if (index != -1) {
                [soundPopup selectItemAtIndex:index];
            }
        }
        else {
            [soundPopup selectItemAtIndex:0];
        }
    }
}

- (void)showPreferences
{
    // Load all nib files
    NSEnumerator*   enumerator;
    NSString*       identifier;
    enumerator = [_toolbarItemIdentifiers objectEnumerator];
    while (identifier = [enumerator nextObject]) {
        [self viewForIdentifier:identifier];
    }
    
    // Initialize preferences
    static _isInitialized = NO;
    if (!_isInitialized) {
        [self _initializePreferencesPanel];
        _isInitialized = YES;
    }
    
    // Check tool bar selection
    NSToolbar*  toolbar;
    toolbar = [_preferencesPanel toolbar];
    if (![toolbar selectedItemIdentifier]) {
        // Get selection from user defaults
        NSUserDefaults* defaults;
        NSString*       identifier;
        defaults = [NSUserDefaults standardUserDefaults];
        identifier = [defaults stringForKey:SRSelectedPreferencesItem];
        if (!identifier) {
            // Default selection
            identifier = SRGeneralIdentifier;
        }
        if (![_toolbarItemIdentifiers containsObject:identifier]) {
            identifier = [_toolbarItemIdentifiers objectAtIndex:0];
        }
        
        // Select toolbar item
        [toolbar setSelectedItemIdentifier:identifier];
        [_preferencesTab selectTabViewItemWithIdentifier:identifier];
        
        // Invoke delegate method forcefully
        [self tabView:_preferencesTab willSelectTabViewItem:[_preferencesTab selectedTabViewItem]];
        
        // For Icon tab
        if ([identifier isEqualToString:SRIconIdentifier]) {
            // Update icon popup
            [[[NSApp delegate] iconInstaller] updateIconPopup:_iconPopup 
                    forTexture:[_iconCheckBoxMatrix selectedTag]];
            
            // Update icon matrix and delete button
            [self _updateIconMatrixWithIconName:[[_iconPopup selectedItem] representedObject]];
            [self _updateIconDeleteButton];
        }
    }
    
    // Show preferences panel
    [_preferencesPanel makeKeyAndOrderFront:self];
}

//--------------------------------------------------------------//
#pragma mark -- Font handling --
//--------------------------------------------------------------//

#define STANDARD_FONT_BUTTON_TAG    0
#define FIXED_FONT_BUTTON_TAG       1
#define SERIF_BUTTON_TAG            2
#define SANS_SERIF_BUTTON_TAG       3
#define CURSIVE_BUTTON_TAG          4
#define FANTASY_BUTTON_TAG          5
#define SOURCE_FONT_BUTTON_TAG      6

- (void)changeFont:(id)sender
{
    // Convert font
	NSFont* font;
	NSFont* convertedFont;
	font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
	convertedFont = [sender convertFont:font];
	
    // Get preferences key
	NSString*   key = nil;
	switch (_fontButtonTag) {
	case STANDARD_FONT_BUTTON_TAG:  key = @"standardFontFamily"; break;
	case FIXED_FONT_BUTTON_TAG:     key = @"fixedFontFamily"; break;
	case SERIF_BUTTON_TAG:          key = @"serifFontFamily"; break;
	case SANS_SERIF_BUTTON_TAG:     key = @"sansSerifFontFamily"; break;
	case CURSIVE_BUTTON_TAG:        key = @"cursiveFontFamily"; break;
	case FANTASY_BUTTON_TAG:        key = @"fantasyFontFamily"; break;
	case SOURCE_FONT_BUTTON_TAG:    key = SRSourceFontName; break;
	}
	if (!key) {
		return;
	}
	
    // Set preferences value
    if (_fontButtonTag == SOURCE_FONT_BUTTON_TAG) {
        NSUserDefaults* defaults;
        defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject:[convertedFont fontName] forKey:key];
    }
    else {
        [[_preferencesController content] 
                setValue:[convertedFont familyName] 
                forKey:key];
    }
}

- (void)_updateIconDeleteButton
{
    // Get current icon name
    NSString*       currentName;
    currentName = [[_iconPopup selectedItem] representedObject];
    
    // Disable icon delete button, if it is dfault
    if ([currentName isEqualToString:@"default"] || 
        [currentName isEqualToString:@"metalDefault"])
    {
        [_iconDeleteButton setEnabled:NO];
        return;
    }
    
    // Enable icon delete button
    [_iconDeleteButton setEnabled:YES];
}

- (NSString*)homeURLString
{
    if (_homeTextField) {
        return [_homeTextField stringValue];
    }
    return [[NSUserDefaults standardUserDefaults] stringForKey:SRGeneralHomePgae];
}

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

- (IBAction)showFontPanelAction:(id)sender
{
    // Get tag of font button
	_fontButtonTag = [sender tag];
	
    // Get preferences key
	NSString*   key = nil;
	switch (_fontButtonTag) {
	case STANDARD_FONT_BUTTON_TAG:  key = @"standardFontFamily"; break;
	case FIXED_FONT_BUTTON_TAG:		key = @"fixedFontFamily"; break;
	case SERIF_BUTTON_TAG:			key = @"serifFontFamily"; break;
	case SANS_SERIF_BUTTON_TAG:		key = @"sansSerifFontFamily"; break;
	case CURSIVE_BUTTON_TAG:		key = @"cursiveFontFamily"; break;
	case FANTASY_BUTTON_TAG:		key = @"fantasyFontFamily"; break;
	case SOURCE_FONT_BUTTON_TAG:    key = SRSourceFontName; break;
	}
	if (!key) {
		return;
	}
	
    // Get preferences value
	NSString*   familyName = nil;
    if (_fontButtonTag == SOURCE_FONT_BUTTON_TAG) {
        NSUserDefaults* defaults;
        defaults = [NSUserDefaults standardUserDefaults];
        familyName = [defaults objectForKey:key];
    }
    else {
        familyName = [[_preferencesController content] valueForKey:key];
	}
    
    // Set font font manager
    NSFontManager*  fontMgr;
    fontMgr = [NSFontManager sharedFontManager];
	[fontMgr setSelectedFont:[NSFont fontWithName:familyName size:[NSFont systemFontSize]] isMultiple:NO];
    [fontMgr setDelegate:self];
	
    // Shoe font panel
	NSFontPanel*	fontPanel;
	fontPanel = [NSFontPanel sharedFontPanel];
	if (![fontPanel isVisible]) {
		[fontPanel orderFront:self];
	}
	[_preferencesPanel makeFirstResponder:nil];
}

- (IBAction)setToCurrentPageAction:(id)sender
{
    // Get front window
    NSWindow*   frontWindow;
    frontWindow = SRGetFrontMainWindow();
    if (!frontWindow) {
        return;
    }
    
    // Get URL string
    NSString*   URLString;
    URLString = [[[[[[[frontWindow windowController] selectedWebView] mainFrame] 
            dataSource] request] URL] _web_userVisibleString];
    if (!URLString) {
        return;
    }
    
    // Set to current page
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:URLString forKey:SRGeneralHomePgae];
}

- (IBAction)showDownloadPathOpenPanelAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get download path
    NSString*   path;
    path = [defaults objectForKey:SRGeneralDownloadPath];
    
    // Show open panel
    NSOpenPanel*    openPanel;
    openPanel = [NSOpenPanel openPanel];
    [openPanel setCanChooseFiles:NO];
    [openPanel setCanChooseDirectories:YES];
    [openPanel beginSheetForDirectory:path 
            file:nil 
            types:nil 
            modalForWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(_downloadPathOpenPanelDidEnd:returnCode:contextInfo:) 
            contextInfo:NULL];
}

- (void)_downloadPathOpenPanelDidEnd:(NSOpenPanel*)sheet 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check return code
    if (returnCode != NSOKButton) {
        return;
    }
    
    // Get download path
    NSString*   path;
    path = [sheet directory];
    if (!path) {
        return;
    }
    
    // Update download path popup
    [_downloadPathPopup removeItemAtIndex:0];
    [_downloadPathPopup insertItemWithTitle:[path lastPathComponent] atIndex:0];
    [_downloadPathPopup selectItemAtIndex:0];
    
    // Set it default database
    [defaults setObject:path forKey:SRGeneralDownloadPath];
}

- (void)_popup:(NSPopUpButton*)popup 
        selectItemWithTag:(int)selectTag 
        replaceMenuItem:(id<NSMenuItem>*)menuItem 
        withTitle:(NSString*)title 
        aboveTag:(int)tag
{
    // Replace menu item
    int index;
    index = [popup indexOfItemWithTag:selectTag];
    if (index == -1) {
        index = [popup indexOfItemWithTag:tag];
        
        // Remove old item
        id<NSMenuItem>  item;
        SR_VERIFY(index - 1 >= 0, @"menu index is less than 0");
        item = [popup itemAtIndex:index - 1];
        if (item == *menuItem) {
            [popup removeItemAtIndex:index - 1];
            index -= 1;
        }
        
        // Insert new menu item
        [popup insertItemWithTitle:title 
                atIndex:index];
        SR_VERIFY(index >= 0, @"menu index is less than 0");
        *menuItem = [popup itemAtIndex:index];
        [*menuItem setTag:selectTag];
        [popup selectItemAtIndex:index];
    }
    else {
        int selectedIndex;
        selectedIndex = [popup indexOfSelectedItem];
        if (index != selectedIndex) {
            [popup selectItemAtIndex:index];
        }
    }
}

- (IBAction)setDefaultFontSizeAction:(id)sender
{
    // Get default font size
    int defaultFontSize = -1;
    if ([sender isKindOfClass:[NSPopUpButton class]]) {
        defaultFontSize = [[sender selectedItem] tag];
    }
    if ([sender isKindOfClass:[NSTextField class]]) {
        defaultFontSize = [sender intValue];
        if (defaultFontSize <= 0 || defaultFontSize >= _fontSizeSeparator) {
            return;
        }
    }
    if (defaultFontSize == -1) {
        return;
    }
    
    // For other font size
    if (defaultFontSize == _otherFontSize) {
        // Select previous value
        int index;
        index = [_defaultFontSizePopup 
                indexOfItemWithTag:[[SRPreferencesController defaultWebPreferences] defaultFontSize]];
        [_defaultFontSizePopup selectItemAtIndex:index];
        
        // Open font size panel
        [_fontSizeTextField setStringValue:@""];
        [NSApp beginSheet:_fontSizePanel 
                modalForWindow:_preferencesPanel 
                modalDelegate:self 
                didEndSelector:@selector(_fontPanelDidEnd:returnCode:contextInfo:) 
                contextInfo:(void*)_defaultFontSizeSetting];
        return;
    }
    
    // Replace menu item
    [self _popup:_defaultFontSizePopup 
            selectItemWithTag:defaultFontSize 
            replaceMenuItem:&_defaultFontSizeOtherItem 
            withTitle:[NSString stringWithFormat:@"%d", defaultFontSize] 
            aboveTag:_fontSizeSeparator];
    
    // Set it web preferences
    [[SRPreferencesController defaultWebPreferences] setDefaultFontSize:defaultFontSize];
}

- (IBAction)setFixedFontSizeAction:(id)sender
{
    // Get fixed font size
    int fixedFontSize = -1;
    if ([sender isKindOfClass:[NSPopUpButton class]]) {
        fixedFontSize = [[sender selectedItem] tag];
    }
    if ([sender isKindOfClass:[NSTextField class]]) {
        fixedFontSize = [sender intValue];
        if (fixedFontSize <= 0 || fixedFontSize >= _fontSizeSeparator) {
            return;
        }
    }
    if (fixedFontSize == -1) {
        return;
    }
    
    // For other font size
    if (fixedFontSize == _otherFontSize) {
        // Select previous value
        int index;
        index = [_fixedFontSizePopup 
                indexOfItemWithTag:[[SRPreferencesController defaultWebPreferences] defaultFixedFontSize]];
        [_fixedFontSizePopup selectItemAtIndex:index];
        
        // Open font size panel
        [NSApp beginSheet:_fontSizePanel 
                modalForWindow:_preferencesPanel 
                modalDelegate:self 
                didEndSelector:@selector(_fontPanelDidEnd:returnCode:contextInfo:) 
                contextInfo:(void*)_fixedFontSizeSetting];
        return;
    }
    
    // Replace menu item
    [self _popup:_fixedFontSizePopup 
            selectItemWithTag:fixedFontSize 
            replaceMenuItem:&_fixedFontSizeOtherItem 
            withTitle:[NSString stringWithFormat:@"%d", fixedFontSize] 
            aboveTag:_fontSizeSeparator];
    
    // Set it web preferences
    [[SRPreferencesController defaultWebPreferences] setDefaultFixedFontSize:fixedFontSize];
}

- (IBAction)setMinFontSizeAction:(id)sender
{
    // Get minimum font size
    int minFontSize = -1;
    if ([sender isKindOfClass:[NSPopUpButton class]]) {
        minFontSize = [[sender selectedItem] tag];
    }
    if ([sender isKindOfClass:[NSTextField class]]) {
        minFontSize = [sender intValue];
        if (minFontSize < 0 || minFontSize >= _fontSizeSeparator) {
            return;
        }
    }
    if (minFontSize == -1) {
        return;
    }
    
    // For other font size
    if (minFontSize == _otherFontSize) {
        // Select previous value
        int index;
        index = [_minFontSizePopup 
                indexOfItemWithTag:[[SRPreferencesController defaultWebPreferences] minimumFontSize]];
        [_minFontSizePopup selectItemAtIndex:index];
        
        // Open font size panel
        [NSApp beginSheet:_fontSizePanel 
                modalForWindow:_preferencesPanel 
                modalDelegate:self 
                didEndSelector:@selector(_fontPanelDidEnd:returnCode:contextInfo:) 
                contextInfo:(void*)_minFontSizeSetting];
        return;
    }
    
    // Replace menu item
    [self _popup:_minFontSizePopup 
            selectItemWithTag:minFontSize 
            replaceMenuItem:&_minFontSizeOtherItem 
            withTitle:[NSString stringWithFormat:@"%d", minFontSize] 
            aboveTag:_fontSizeSeparator];
    
    // Set it web preferences
    [[SRPreferencesController defaultWebPreferences] setMinimumFontSize:minFontSize];
}

- (IBAction)closeFontPanelAction:(id)sender
{
    // End modal loop
    [NSApp endSheet:_fontSizePanel returnCode:[sender tag]];
}

- (void)_fontPanelDidEnd:(NSPanel*)sheet 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Close sheet
    [sheet orderOut:self];
    
    // Check return code
    if (returnCode != NSOKButton) {
        return;
    }
    
    // For default font size
    if ((int)contextInfo == _defaultFontSizeSetting) {
        [self setDefaultFontSizeAction:_fontSizeTextField];
    }
    else if ((int)contextInfo == _fixedFontSizeSetting) {
        [self setFixedFontSizeAction:_fontSizeTextField];
    }
    else if ((int)contextInfo == _minFontSizeSetting) {
        [self setMinFontSizeAction:_fontSizeTextField];
    }
    
    [_preferencesPanel makeKeyAndOrderFront:self];
}

- (void)setTextEncodingAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get encoding value from tag
    NSStringEncoding    encoding;
    encoding = [sender tag];
    
    // Get value transformer for encoding name
    NSValueTransformer* transformer;
    transformer = [NSValueTransformer valueTransformerForName:SRIANAToEncodingTransformerName];
    
    // For Japanese aut detection
    if (encoding == SRJapaneseAutoDetectEncoding) {
        // Set Sfhit JIS as default encoding, it enables Japanese auto detection
        NSString*   encodingName;
        encodingName = [transformer reverseTransformedValue:
                [NSNumber numberWithUnsignedInt:SRConvertedShiftJISStringEncoding]];
        [[SRPreferencesController defaultWebPreferences] setDefaultTextEncodingName:encodingName];
    }
    else {
        // Set web preferences default encoding
        NSString*   encodingName;
        encodingName = [transformer reverseTransformedValue:
                [NSNumber numberWithUnsignedInt:encoding]];
        [[SRPreferencesController defaultWebPreferences] setDefaultTextEncodingName:encodingName];
    }
    
    // Set it default database
    [defaults setObject:[NSNumber numberWithUnsignedInt:encoding] 
            forKey:SRDefaultTextEncoding];
}

- (IBAction)showStyleSheetOpenPanelAction:(id)sender
{
    // Get style sheet path
    NSString*   path;
    path = [[[SRPreferencesController defaultWebPreferences] userStyleSheetLocation] absoluteString];
    
    // Show open panel
    NSOpenPanel*    openPanel;
    openPanel = [NSOpenPanel openPanel];
    [openPanel setCanChooseFiles:YES];
    [openPanel setCanChooseDirectories:NO];
    [openPanel beginSheetForDirectory:path 
            file:nil 
            types:[NSArray arrayWithObjects:@"css", nil] 
            modalForWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(_styleSheetOpenPanelDidEnd:returnCode:contextInfo:) 
            contextInfo:NULL];
}

- (void)_styleSheetOpenPanelDidEnd:(NSOpenPanel*)sheet 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Check return code
    if (returnCode != NSOKButton) {
        return;
    }
    
    // Get download path
    NSString*   path;
    path = [sheet filename];
    if (!path) {
        return;
    }
    
    // Update style sheet popup
    [_styleSheetPopup removeItemAtIndex:0];
    [_styleSheetPopup insertItemWithTitle:[path lastPathComponent] atIndex:0];
    [_styleSheetPopup selectItemAtIndex:0];
    
    // Set it web preferences
    [[SRPreferencesController defaultWebPreferences] setUserStyleSheetLocation:[NSURL fileURLWithPath:path]];
}

- (void)_cookiePanelDidEnd:(NSWindow*)sheet 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Close sheet
    [sheet orderOut:self];
    
    [_preferencesPanel makeKeyAndOrderFront:self];
}

- (IBAction)showCookiePanelAction:(id)sender
{
    // Reset content
    [_cookiesController 
            setContent:[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]];
    
    // Open cookie panel
    [NSApp beginSheet:_cookiePanel 
            modalForWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(_cookiePanelDidEnd:returnCode:contextInfo:) 
            contextInfo:NULL];
}

- (IBAction)closeCookiePanelAction:(id)sender
{
    // End modal loop
    [NSApp endSheet:_cookiePanel];
}

- (IBAction)removeCookieAction:(id)sender
{
    // Get selected cookies
    NSArray*    cookies;
    cookies = [_cookiesController selectedObjects];
    if (!cookies || [cookies count] == 0) {
        return;
    }
    
    // Delete cookies
    NSHTTPCookieStorage*    storage;
    NSEnumerator*           enumerator;
    NSHTTPCookie*           cookie;
    storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    enumerator = [cookies objectEnumerator];
    while (cookie = [enumerator nextObject]) {
        [storage deleteCookie:cookie];
    }
    
    // Reset content
    [_cookiesController 
            setContent:[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]];
}

- (IBAction)removeAllCookiesAction:(id)sender
{
    // Create confirmation dialog
    NSAlert*    alert;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    [alert setAlertStyle:NSCriticalAlertStyle];
    [alert setMessageText:NSLocalizedString(@"Are you sure you want to remove all cookies?", nil)];
    [alert setInformativeText:NSLocalizedString(@"It removes all cookies used by Safari and other applications which are using Web Kit. You cannot undo this action.", nil)];
    [alert addButtonWithTitle:NSLocalizedString(@"Remove All", nil)];
    [alert addButtonWithTitle:NSLocalizedString(@"Cancel", nil)];
    
    // Show dialog
    int result;
    result = [alert runModal];
    
    // Remove all cookies
    if (result == NSAlertFirstButtonReturn) {
        NSHTTPCookieStorage*    storage;
        NSEnumerator*           enumerator;
        NSHTTPCookie*           cookie;
        storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        enumerator = [[storage cookies] objectEnumerator];
        while (cookie = [enumerator nextObject]) {
            [storage deleteCookie:cookie];
        }
        
        // Reset content
        [_cookiesController setContent:[storage cookies]];
    }
}

- (IBAction)setUserAgentAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Set user agent
    [defaults setInteger:[sender tag] forKey:SRUserAgent];
}

- (IBAction)showUserAgentPanelAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Open user agent panel
    [_userAgentTextField setStringValue:[defaults stringForKey:SRUserAgentOtherName]];
    [NSApp beginSheet:_userAgentPanel 
            modalForWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(_userAgentPanelDidEnd:returnCode:contextInfo:) 
            contextInfo:NULL];
}

- (void)_userAgentPanelDidEnd:(NSWindow*)sheet 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Close panel
    [sheet orderOut:self];
    
    // Check return code
    if (returnCode != 0) {
        return;
    }
    
    // Get user agent name
    NSString*   userAgentName;
    userAgentName = [_userAgentTextField stringValue];
    if (!userAgentName) {
        return;
    }
    
    // Replace menu item
    id<NSMenuItem>  oldItem, newItem;
    int otherItemIndex;
    otherItemIndex = [_userAgentPopup indexOfItemWithTag:999];
    SR_VERIFY(otherItemIndex >= 0, @"menu index is less than 0");
    oldItem = [[_userAgentPopup itemAtIndex:otherItemIndex] retain];
    [_userAgentPopup removeItemAtIndex:otherItemIndex];
    [_userAgentPopup insertItemWithTitle:userAgentName atIndex:otherItemIndex];
    newItem = [_userAgentPopup itemAtIndex:otherItemIndex];
    [newItem setTarget:[oldItem target]];
    [newItem setAction:[oldItem action]];
    [newItem setTag:[oldItem tag]];
    [oldItem release];
    [_userAgentPopup selectItemAtIndex:otherItemIndex];
    
    // Set it default database
    [defaults setObject:userAgentName forKey:SRUserAgentOtherName];
    [defaults setInteger:SRUserAgentOther forKey:SRUserAgent];
}

- (IBAction)closeUserAgentPanelAction:(id)sender
{
    // End sheet
    [NSApp endSheet:_userAgentPanel returnCode:[sender tag]];
}

- (IBAction)selectTextureTypeForIconAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Select selected icons
    NSString*   iconName = nil;
    switch ([_iconCheckBoxMatrix selectedTag]) {
    case SRIconTextureAqua: {
        iconName = [defaults stringForKey:SRIconName];
        break;
    }
    case SRIconTextureMetal: {
        iconName = [defaults stringForKey:SRIconMetalName];
        break;
    }
    }
    if (iconName) {
        [_iconPopup selectItemAtIndex:[_iconPopup indexOfItemWithRepresentedObject:iconName]];
    }
    
    // Update icon matrix and delete button
    [self _updateIconMatrixWithIconName:iconName];
    [self _updateIconDeleteButton];
}

- (IBAction)loadIconLocalAction:(id)sender
{
    // Show open dialog
    NSOpenPanel*    openPanel;
    openPanel = [NSOpenPanel openPanel];
    [openPanel setCanChooseFiles:YES];
    [openPanel setCanChooseDirectories:NO];
    [openPanel beginSheetForDirectory:nil 
            file:nil 
            types:[NSArray arrayWithObject:@"plist"] 
            modalForWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(_iconOpenPanelDidEnd:returnCode:contextInfo:) 
            contextInfo:NULL];
}

- (void)_iconOpenPanelDidEnd:(NSOpenPanel*)openPanel 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Close open panel
    [openPanel orderOut:self];
    
    // Check return code
    if (returnCode != NSOKButton) {
        return;
    }
    
    // Get file name
    NSString*   fileName;
    fileName = [openPanel filename];
    
    // Get Shiira icon plist from file name
    NSDictionary*   plist;
    plist = [SRIconInstaller iconPlistAtPath:fileName];
    if (!plist) {
        return;
    }
    
    // Load icons
    SRIconInstaller*    installer;
    installer = [[NSApp delegate] iconInstaller];
    [installer loadIconPlist:plist 
            baseURL:[NSURL fileURLWithPath:[fileName stringByDeletingLastPathComponent]] 
            withModalWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(_loadIconDoneReturnCode:)];
}

- (void)_loadIconDoneReturnCode:(int)returnCode
{
    // Check return code
    if (returnCode != SRIconInstalOK) {
        return;
    }
    
    // Update icon popup
    [[[NSApp delegate] iconInstaller] updateIconPopup:_iconPopup 
            forTexture:[_iconCheckBoxMatrix selectedTag]];
}

- (void)_updateIconMatrixWithIconName:(NSString*)name
{
    // Get icon info and icon images for name
    SRIconInstaller*    installer;
    NSDictionary*       iconInfo;
    NSArray*            iconImages;
    installer = [[NSApp delegate] iconInstaller];
    iconInfo = [installer iconsOfName:name];
    iconImages = [installer iconImagesOfName:name];
    
    // Set icon info
    NSDictionary*   plist;
    NSString*       displayName;
    NSString*       author;
    plist = [iconInfo objectForKey:@"ShiiraIcons"];
    displayName = [plist objectForKey:@"displayName"];
    author = [plist objectForKey:@"author"];
    if (displayName && author) {
        [self setValue:[NSString stringWithFormat:@"%@ (%@)", 
                    displayName, 
                    [NSString stringWithFormat:NSLocalizedString(@"Created by %@", nil), author]] 
                forKey:@"iconDisplayName"];
    }
    else {
        [self setValue:nil forKey:@"iconDisplayName"];
    }
    
    // Set icons matrix
    int             numOfCols, numOfRows;
    int             col = 0, row = 0;
    NSEnumerator*   iconEnum;
    NSImage*        iconImage;
    numOfCols = [_iconMatrix numberOfColumns];
    numOfRows = [_iconMatrix numberOfRows];
    
    iconEnum = [iconImages objectEnumerator];
    while (iconImage = [iconEnum nextObject]) {
        // Check column index
        if (col >= numOfCols) {
            // Go next line
            col = 0;
            row++;
            
            // Check row index
            if (row >= numOfRows) {
                // Insert new row
                [_iconMatrix addRow];
            }
        }
        
        // Insert icon image into matrix
        [[_iconMatrix cellAtRow:row column:col] setImage:iconImage];
        
        // Go next column
        col++;
    }
    
    // Refresh matrix
    [_iconMatrix setNeedsDisplay:YES];
}

- (void)iconSelectedAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get represented object
    id  representedObject;
    representedObject = [sender representedObject];
    
    // Update icon matrix and delete button
    [self _updateIconMatrixWithIconName:representedObject];
    [self _updateIconDeleteButton];
    
    // Set icon name default database
    switch ([_iconCheckBoxMatrix selectedTag]) {
    case SRIconTextureAqua: {
        [defaults setObject:representedObject forKey:SRIconName];
        break;
    }
    case SRIconTextureMetal: {
        [defaults setObject:representedObject forKey:SRIconMetalName];
        break;
    }
    }
}

- (IBAction)deleteIconAction:(id)sender
{
    // Show alert dialog
    NSAlert*    alert;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    [alert setMessageText:NSLocalizedString(@"It removes Shiira icon set.", nil)];
    [alert setInformativeText:NSLocalizedString(@"Are you sure you want to remove this Shiira icon set?", nil)];
    [alert addButtonWithTitle:NSLocalizedString(@"Yes", nil)];
    [alert addButtonWithTitle:NSLocalizedString(@"No", nil)];
    [alert setAlertStyle:NSCriticalAlertStyle];
    [alert beginSheetModalForWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(_iconDeleteAlertDidEnd:returnCode:contextInfo:) 
            contextInfo:NULL];
}

- (void)_iconDeleteAlertDidEnd:(NSAlert*)alert 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Check return code
    if (returnCode != NSAlertFirstButtonReturn) {
        return;
    }
    
    // Get current icon name
    NSString*   currentName;
    currentName = [[_iconPopup selectedItem] representedObject];
    if (!currentName) {
        return;
    }
    
    // Delete current icon
    if (![[[NSApp delegate] iconInstaller] deleteIconOfName:currentName]) {
        return;
    }
    
    // Update icon popup
    [[[NSApp delegate] iconInstaller] updateIconPopup:_iconPopup 
            forTexture:[_iconCheckBoxMatrix selectedTag]];
    
    // Select first one
    [_iconPopup selectItemAtIndex:0];
    [self iconSelectedAction:[_iconPopup selectedItem]];
}

- (IBAction)showFaviconPanelAction:(id)sender
{
    // Get web icon database
    WebIconDatabase*    database;
    database = [WebIconDatabase sharedIconDatabase];
    
    // Get icon database private
    void*   databasePrivate = NULL;
    object_getInstanceVariable(database, "_private", &databasePrivate);
        
    if (databasePrivate) {
        // Get icons
        NSDictionary*   iconURLToIcons = nil;
        object_getInstanceVariable(databasePrivate, "iconURLToIcons", (void**)(&iconURLToIcons));
        
        if (iconURLToIcons) {
            // Create content dictioanry
            NSMutableArray* content;
            NSEnumerator*   enumerator;
            NSString*       urlString;
            content = [NSMutableArray array];
            enumerator = [iconURLToIcons keyEnumerator];
            while (urlString = [enumerator nextObject]) {
                // Get first image
                NSDictionary*   imageInfo;
                NSImage*        image;
                imageInfo = [iconURLToIcons objectForKey:urlString];
                image = [[imageInfo allValues] objectAtIndex:0];
                
                if (image && [image isKindOfClass:[NSImage class]]) {
                    [image setSize:NSMakeSize(16, 16)];
                    
                    NSMutableDictionary*    dict;
                    dict = [NSMutableDictionary dictionary];
                    [dict setValue:urlString forKey:@"url"];
                    [dict setValue:image forKey:@"icon"];
                    
                    [content addObject:dict];
                }
            }
            
            // Reset content
            [_faviconController setContent:content];
        }
    }
    
    // Open favicon panel
    [NSApp beginSheet:_faviconPanel 
            modalForWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(_faviconPanelDidEnd:returnCode:contextInfo:) 
            contextInfo:NULL];
}

- (void)_faviconPanelDidEnd:(NSWindow*)sheet 
        returnCode:(int)returnCode 
        contextInfo:(void*)contextInfo
{
    // Close sheet
    [sheet orderOut:self];
    
    [_preferencesPanel makeKeyAndOrderFront:self];
}

- (IBAction)closeFaviconPanelAction:(id)sender
{
    // End modal loop
    [NSApp endSheet:_faviconPanel];
}

- (void)selectPreferencesTabAction:(id)sender
{
    // Select tab with identifier
    id  identifier;
    identifier = [sender itemIdentifier];
    if ([_preferencesTab indexOfTabViewItemWithIdentifier:identifier] != -1) {
        [_preferencesTab selectTabViewItemWithIdentifier:identifier];
    }
}

- (IBAction)setSourceFontSizeAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get source font size
    int sourceFontSize = -1;
    if ([sender isKindOfClass:[NSPopUpButton class]]) {
        sourceFontSize = [[sender selectedItem] tag];
    }
    if (sourceFontSize == -1) {
        return;
    }
    
    // Set it default database
    [defaults setInteger:sourceFontSize forKey:SRSourceFontSize];
}

- (void)_updateRSSUpdatemMinutesTextField
{
    int RSSUpdate;
    RSSUpdate = [[NSUserDefaults standardUserDefaults] integerForKey:SRRSSUpdate];
    [_RSSUpdateMinutesTextField setHidden:RSSUpdate != SRRSSUpdateEverySpecifiedMinutes];
    [_RSSUpdateMinutesLabel setHidden:RSSUpdate != SRRSSUpdateEverySpecifiedMinutes];
}

- (void)setRSSCheckAction:(id)sender
{
    // Update RSS update minutes text field
    [self _updateRSSUpdatemMinutesTextField];
}

- (void)setRSSRemoveAction:(id)sender
{
}

- (void)removeRSSNowAction:(id)sender
{
    // Show alert
    NSAlert*    alert;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    [alert setMessageText:NSLocalizedString(@"Are you sure you want to remove all RSS articles now?", nil)];
    [alert addButtonWithTitle:NSLocalizedString(@"Remove Now", nil)];
    [alert addButtonWithTitle:NSLocalizedString(@"Cancel", nil)];
    
    [alert beginSheetModalForWindow:_preferencesPanel 
            modalDelegate:self 
            didEndSelector:@selector(removeRSSDidEndAlert:returnCode:contextInfo:) 
            contextInfo:NULL];
}

- (void)removeRSSDidEndAlert:(NSAlert*)alert returnCode:(int)returnCode contextInfo:(void*)contextInfo
{
    if (returnCode != NSAlertFirstButtonReturn) {
        return;
    }
    
    // Remove RSS articles now
    [[SRRSSManager sharedInstance] deleteAllItems];
}

- (void)setSoundAction:(id)sender
{
    // Dcide action name
    NSString*   actionName = nil;
    if (sender == _soundPageLoadDonePopup) {
        actionName = SRSoundPageLoadDone;
    }
    if (sender == _soundPageLoadErrorPopup) {
        actionName = SRSoundPageLoadError;
    }
    if (!actionName) {
        return;
    }
    
    // Get selected sound file
    NSString*   soundFilePath;
    soundFilePath = [[sender selectedItem] representedObject];
    if (!soundFilePath) {
        return;
    }
    
    // Set sound
    SystemSoundActionID soundActionId;
    soundActionId = [[NSApp delegate] setSound:soundFilePath forAction:actionName];
    if (!soundActionId) {
        return;
    }
    
    // Sound test
    SystemSoundPlay(soundActionId);
}

//--------------------------------------------------------------//
#pragma mark -- NSToolbar delegate --
//--------------------------------------------------------------//

- (NSArray*)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
{
    return _toolbarItemIdentifiers;
}

- (NSArray*)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
{
    return _toolbarItemIdentifiers;
}

- (NSArray*)toolbarSelectableItemIdentifiers:(NSToolbar*)toolbar
{
    return _toolbarItemIdentifiers;
}

- (NSToolbarItem*)toolbar:(NSToolbar*)toolbar 
        itemForItemIdentifier:(NSString*)itemIdentifier 
        willBeInsertedIntoToolbar:(BOOL)flag
{
    // Create toolbar item
    NSToolbarItem*  item;
    item = [[NSToolbarItem alloc] 
            initWithItemIdentifier:itemIdentifier];
    [item autorelease];
    [item setTarget:self];
    
    // Get label, image, and action
    NSString*   label = nil;
    NSImage*    image = nil;
    SEL         action = NULL;
    
    // General
    if ([itemIdentifier isEqualToString:SRGeneralIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // Appearance
    else if ([itemIdentifier isEqualToString:SRAppearanceIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // Bookmarks
    else if ([itemIdentifier isEqualToString:SRBookmarksIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // Tabs
    else if ([itemIdentifier isEqualToString:SRTabsIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // Security
    else if ([itemIdentifier isEqualToString:SRSecurityIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // Advanced
    else if ([itemIdentifier isEqualToString:SRAdvancedIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // Icon
    else if ([itemIdentifier isEqualToString:SRIconIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // Sidebar
    else if ([itemIdentifier isEqualToString:SRSidebarPrefIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // Source
    else if ([itemIdentifier isEqualToString:SRSourceIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    // RSS
    else if ([itemIdentifier isEqualToString:SRRSSIdentifier]) {
        label = NSLocalizedStringFromTable(itemIdentifier, SRPreferencesToolbarLabelTable, nil);
        image = [_iconImageDict objectForKey:itemIdentifier];
        action = @selector(selectPreferencesTabAction:);
    }
    
    // Set item attributes
    if (label) {
        [item setLabel:label];
    }
    if (image) {
        [item setImage:image];
    }
    else {
        // Use general
        [item setImage:[_iconImageDict objectForKey:SRGeneralIdentifier]];
    }
    if (action) {
        [item setAction:action];
    }
    return item;
}

//--------------------------------------------------------------//
#pragma mark -- NSWindow delegate --
//--------------------------------------------------------------//

- (void)windowDidBecomeKey:(NSNotification*)notification
{
    // Update file menu
    [[[[NSApp mainMenu] itemWithTag:SRFileTag] submenu] update];
}

- (void)windowDidMove:(NSNotification*)notification
{
    if (![_preferencesPanel isVisible]) {
        return;
    }
    
    [[NSUserDefaults standardUserDefaults] 
            setObject:NSStringFromRect([_preferencesPanel frame]) forKey:SRPreferencesPanelFrame];
}

- (void)windowDidResize:(NSNotification*)notification
{
    if (![_preferencesPanel isVisible]) {
        return;
    }
    
    [[NSUserDefaults standardUserDefaults] 
            setObject:NSStringFromRect([_preferencesPanel frame]) forKey:SRPreferencesPanelFrame];
}

//--------------------------------------------------------------//
#pragma mark -- NSApplication notification --
//--------------------------------------------------------------//

- (void)applicationWillTerminate:(NSNotification*)notification
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Store selection
    NSString*       identifier;
    identifier = [[_preferencesPanel toolbar] selectedItemIdentifier];
    if (identifier) {
        [defaults setObject:identifier forKey:SRSelectedPreferencesItem];
    }
    
    // Store home page
    NSString*   homeURLString;
    homeURLString = [self homeURLString];
    if (homeURLString) {
        [defaults setObject:homeURLString forKey:SRGeneralHomePgae];
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSTableViewDataSoruce --
//--------------------------------------------------------------//

- (int)numberOfRowsInTableView:(NSTableView*)tableView
{
    return [_browsers count];
}

- (id)tableView:(NSTableView*)tableView 
        objectValueForTableColumn:(NSTableColumn*)tableColumn 
        row:(int)row
{
    // Get identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    // For on
    if ([identifier isEqualToString:@"on"]) {
        if ([_isUsing count] < row) {
            return nil;
        }
        return [_isUsing objectAtIndex:row];
    }
    // For browser
    if ([identifier isEqualToString:@"browser"]) {
        if ([_localizedBrowserNames count] < row) {
            return nil;
        }
        return [_localizedBrowserNames objectAtIndex:row];
    }
    
    return nil;
}

- (void)tableView:(NSTableView*)tableView 
        setObjectValue:(id)object 
        forTableColumn:(NSTableColumn*)tableColumn 
        row:(int)row
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    // For on
    if ([identifier isEqualToString:@"on"]) {
        if ([_isUsing count] < row) {
            return;
        }
        [_isUsing replaceObjectAtIndex:row withObject:object];
        
        // Update defaults value
        NSMutableArray* isUsing;
        int             browser;
        isUsing = [NSMutableArray arrayWithArray:[defaults objectForKey:SRBookmarkIsUsing]];
        browser = [[_browsers objectAtIndex:row] intValue];
        if ([isUsing count] < browser) {
            return;
        }
        [isUsing replaceObjectAtIndex:browser withObject:object];
        [defaults setObject:isUsing forKey:SRBookmarkIsUsing];
        return;
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSTableView delegate --
//--------------------------------------------------------------//

- (void)tableView:(NSTableView*)tableView 
        willDisplayCell:(id)cell 
        forTableColumn:(NSTableColumn*)tableColumn 
        row:(int)row
{
    // Get identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    // For browser
    if ([identifier isEqualToString:@"browser"]) {
        if ([_browserIcons count] < row) {
            return;
        }
        
        // Get icon
        NSImage*    icon;
        icon = [_browserIcons objectAtIndex:row];
        if (icon) {
            [cell setImage:icon];
        }
        return;
    }
}

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

- (void)tabView:(NSTabView*)tabView willSelectTabViewItem:(NSTabViewItem*)tabViewItem
{
    // Get identifier
    NSString*   identifier;
    identifier = [tabViewItem identifier];
    
    // Set tab item view
    NSView* nextView;
    nextView = [self viewForIdentifier:identifier];
    if ([[[tabViewItem view] subviews] count] == 0) {
        NSView* view;
        view = [[NSView alloc] initWithFrame:[nextView frame]];
        [view autorelease];
        [view addSubview:nextView];
        [tabViewItem setView:view];
    }
    
    // For Icon tab
    if ([identifier isEqualToString:SRIconIdentifier]) {
        // Update icon popup
        [[[NSApp delegate] iconInstaller] updateIconPopup:_iconPopup 
                forTexture:[_iconCheckBoxMatrix selectedTag]];
        
        // Update icon matrix and delete button
        [self _updateIconMatrixWithIconName:[[_iconPopup selectedItem] representedObject]];
        [self _updateIconDeleteButton];
    }
    
    // Get frame size
    NSRect  windowFrame, contentFrame, newContentFrame;
    windowFrame = [_preferencesPanel frame];
    contentFrame = [[_preferencesPanel contentView] frame];
    newContentFrame = [nextView frame];
    
    // Calc new frame
    NSRect  newWindowFrame;
    float   dheight;
    dheight = newContentFrame.size.height - contentFrame.size.height;
    newWindowFrame.origin.x = windowFrame.origin.x;
    newWindowFrame.origin.y = windowFrame.origin.y - dheight;
    newWindowFrame.size.width = newContentFrame.size.width;
    newWindowFrame.size.height = windowFrame.size.height + dheight;
    
	// Resize window
	[tabView setHidden:YES];
	[_preferencesPanel setFrame:newWindowFrame display:YES animate:YES];
	[tabView setHidden:NO];
}

//- (BOOL)tabView:(NSTabView*)tabView shouldSelectTabViewItem:(NSTabViewItem*)tabViewItem
//- (void)tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem
//- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView*)tabView

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

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Icon preference
    if ([keyPath isEqualToString:SRIconName]) {
        // Get toolbar icons
        NSString*   iconName;
        iconName = [object stringForKey:SRIconName];
        [_iconImageDict release];
        _iconImageDict = [[[[NSApp delegate] iconInstaller] iconsOfName:iconName] retain];
        
        // Update toolbar icon
        NSArray*        items;
        NSEnumerator*   enumerator;
        NSToolbarItem*  item;
        items = [[_preferencesPanel toolbar] items];
        enumerator = [items objectEnumerator];
        while (item = [enumerator nextObject]) {
            NSImage*    image;
            image = [_iconImageDict objectForKey:[item itemIdentifier]];
            if (image) {
                [item setImage:image];
            }
        }
    }
    
    // Bookmark menu
    if ([keyPath isEqualToString:@"arrangedObjects.isUsing"]) {
        // Update user defaults
    }
}

//--------------------------------------------------------------//
#pragma mark -- User agents --
//--------------------------------------------------------------//

+ (NSArray*)userAgents
{
    NSString*   userAgentsPath;
    NSArray*    userAgents;
    userAgentsPath = [[NSBundle mainBundle] pathForResource:@"UserAgent" ofType:@"plist"];
    userAgents = [NSArray arrayWithContentsOfFile:userAgentsPath];
    if (!userAgents) {
        return nil;
    }
    
    // Get locale identifier
    CFLocaleRef locale;
    NSString*   localeId;
    locale = CFLocaleCopyCurrent();
    localeId = (NSString*)CFLocaleGetIdentifier(locale);
    
    // Set locale identifier
    NSMutableArray* array;
    NSEnumerator*   enumerator;
    NSDictionary*   dict;
    array = [NSMutableArray array];
    enumerator = [userAgents objectEnumerator];
    while (dict = [enumerator nextObject]) {
        NSMutableDictionary*    userAgentDict;
        userAgentDict = [NSMutableDictionary dictionaryWithDictionary:dict];
        
        NSString*   userAgent;
        userAgent = [dict objectForKey:@"userAgent"];
        if (userAgent) {
            userAgent = [NSString stringWithFormat:userAgent, localeId];
            if (userAgent) {
                [userAgentDict setObject:userAgent forKey:@"userAgent"];
            }
        }
        
        [array addObject:userAgentDict];
    }
    
    return array;
}

@end
