/*
SRSBBookmarksController.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 "SRContextMenu.h"

#import "SRAppDelegate.h"
#import "SRMainWindowController.h"
#import "SRBookmark.h"
#import "SRBookmarkStorage.h"
#import "SRSBBookmarksController.h"

#import "SRURLComboBoxCell.h"
#import "SRImageTextCell.h"
#import "SRBookmarkImageView.h"
#import "SROutlineView.h"
#import "SRPasteboard.h"

#import "WebKitEx.h"

static NSString*   _SRBookmarksTabIdentifier = @"bookmark";
static NSString*   _SRFindResultsTabIdentifier = @"findResult";

@interface SRSBBookmarksController (private)
- (void)bookmarkUpdated:(NSNotification*)notification;
@end

#pragma mark -

@implementation SRSBBookmarksController

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

- (id)initWithMainWindowController:(SRMainWindowController*)mainWindowController 
        sideBarController:(SRSideBarController*)sideBarController
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize instance variables
    _mainWindowController = mainWindowController;
    _sideBarController = sideBarController;
    
    // Register notification
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [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];
    
    [center addObserver:self 
            selector:@selector(faviconAdded:) name:WebIconDatabaseDidAddIconNotification object:nil];
    
    // Register key value observation
    [defaults addObserver:self 
            forKeyPath:SRIconUseFavicon options:NSKeyValueObservingOptionNew context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRIconUseFaviconSideBar options:NSKeyValueObservingOptionNew context:NULL];
    [center addObserver:self 
            selector:@selector(bookmarksViewFrameDidChange:) name:NSViewFrameDidChangeNotification object:_bookmarksView];
    
    return self;
}

static int  _bookmarksTabPaddingTop = -1;
static int  _bookmarksTabPaddingBottom = -1;

- (void)awakeFromNib
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    NSTableColumn*      column;
    NSCell*             oldCell;
    SRImageTextCell*    cell;
    
    // Configure bookmark outline
    column = [_bookmarksOutline tableColumnWithIdentifier:@"bookmark"];
    oldCell = [column dataCell];
    cell = [[SRImageTextCell alloc] init];
    [cell autorelease];
    [cell setFont:[oldCell font]];
    [column setDataCell:cell];
    
    [_bookmarksOutline setTarget:self];
    [_bookmarksOutline setAction:@selector(bookmarkSelectedAction:)];
    [_bookmarksOutline setDoubleAction:@selector(openBookmarkAction:)];
    
    [_bookmarksOutline registerForDraggedTypes:[NSArray arrayWithObjects:
            WebURLsWithTitlesPboardType, SRBookmarkPboardType, nil]];
    
    // Configure bookmark find table
    column = [_findTable tableColumnWithIdentifier:@"results"];
    oldCell = [column dataCell];
    cell = [[SRImageTextCell alloc] init];
    [cell autorelease];
    [cell setFont:[oldCell font]];
    [column setDataCell:cell];
    
    [_findTable setTarget:self];
    [_findTable setAction:@selector(bookmarkSelectedAction:)];
    [_findTable setDoubleAction:@selector(openBookmarkAction:)];
    
    // Select tab item
    [_bookmarksTab selectTabViewItemWithIdentifier:_SRBookmarksTabIdentifier];
    
    // Set HTML view as default
    [self setDetailView:SRBookmarkHTMLDetail];
    
    // Reload bookmark
    [self reloadBookmark];
    
    [_bookmarksOutline setAutosaveExpandedItems:YES];
    
    // Get paddings
    if (_bookmarksTabPaddingTop < 0) {
        NSRect  bookmarksTabFrame, bookmarksFrame;
        bookmarksTabFrame = [_bookmarksTab frame];
        bookmarksFrame = [_bookmarksView frame];
        bookmarksFrame.origin = NSZeroPoint;
        _bookmarksTabPaddingTop = (bookmarksFrame.origin.y + bookmarksFrame.size.height) - 
                (bookmarksTabFrame.origin.y + bookmarksTabFrame.size.height);
        _bookmarksTabPaddingBottom = bookmarksTabFrame.origin.y - bookmarksFrame.origin.y;
    }
    
    // Set frame change notifications
    [_bookmarksView setPostsFrameChangedNotifications:YES];
    
}

- (void)dealloc
{
    // Release outlets
    [_bookmarksView release];
    [_htmlView release];
    [_folderView release];
    [_rssView release];
    [_bookmarkTableSelectedIndex release];
    [_findResults release];
    
    // Remove notification
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self];
    
    // Remove observers
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObserver:self forKeyPath:SRIconUseFavicon];
    [defaults removeObserver:self forKeyPath:SRIconUseFaviconSideBar];
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- First responder --
//--------------------------------------------------------------//

- (BOOL)acceptsFirstResponder
{
    return YES;
}

- (void)updateNextResponder
{
    // Get tab item identifier
    id  identifier;
    identifier = [[_bookmarksTab selectedTabViewItem] identifier];
    
    if ([identifier isEqualToString:_SRBookmarksTabIdentifier]) {
        if ([_bookmarksOutline nextResponder] != self) {
            [self setNextResponder:[_bookmarksOutline nextResponder]];
            [_bookmarksOutline setNextResponder:self];
        }
    }
    if ([identifier isEqualToString:_SRFindResultsTabIdentifier]) {
        if ([_findTable nextResponder] != self) {
            [self setNextResponder:[_findTable nextResponder]];
            [_findTable setNextResponder:self];
        }
    }
}

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

- (SRMainWindowController*)mainWindowController
{
    return _mainWindowController;
}

- (void)setMainWindowController:(SRMainWindowController*)mainWindowController
{
    _mainWindowController = mainWindowController;
}

- (SRSideBarController*)sideBarController
{
    return _sideBarController;
}

- (void)setSidebarController:(SRSideBarController*)sideBarController
{
    _sideBarController = sideBarController;
}

//--------------------------------------------------------------//
#pragma mark -- View --
//--------------------------------------------------------------//

- (NSView*)view
{
    return _bookmarksView;
}

- (id)selectedBookmarksView
{
    id  identifier;
    identifier = [[_bookmarksTab selectedTabViewItem] identifier];
    
    if ([identifier isEqualToString:_SRBookmarksTabIdentifier]) {
        return _bookmarksOutline;
    }
    if ([identifier isEqualToString:_SRFindResultsTabIdentifier]) {
        return _findTable;
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- Detail view --
//--------------------------------------------------------------//

- (void)setDetailView:(SRBookmarkDetail)type
{
    // Get detail view
    NSArray*    subviews;
    NSView*     detailView = nil;
    subviews = [_detailView subviews];
    if (subviews && [subviews count] > 0) {
        detailView = [subviews objectAtIndex:0];
    }
    
    // Decide new detail view
    NSView*     newDetailView = nil;
    switch (type) {
    case SRBookmarkHTMLDetail: {
        newDetailView = _htmlView;
        break;
    }
    case SRBookmarkFolderDetail: {
        newDetailView = _folderView;
        break;
    }
    case SRBookmarkRSSDetail: {
        newDetailView = _rssView;
        break;
    }
    }
    
    // Swap detail view
    if (detailView != newDetailView) {
        // Remove current subview
        [detailView removeFromSuperview];
        
        // Add new view
        [newDetailView setFrameOrigin:NSZeroPoint];
        [newDetailView setFrameSize:[_detailView frame].size];
        [_detailView addSubview:newDetailView];
    }
}

- (void)_updateDetailView
{
    // Get selected bookmarks
    NSArray*    bookmarks;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        // Reset all
        NSString*   noSelectString;
        noSelectString = NSLocalizedString(@"No selection", nil);
        
        [_folderImageView setImage:nil];
        [_folderImageView setBookmark:nil];
        [_folderNameLabel setStringValue:@""];
        [[_folderNameLabel cell] setPlaceholderString:noSelectString];
        [_folderNameTextField setStringValue:@""];
        [[_folderNameTextField cell] setPlaceholderString:noSelectString];
        [_folderNameTextField setEditable:NO];
        [_folderAutoTabCheckBox setHidden:NO];
        [_folderAutoTabCheckBox setState:NSOffState];
        [_folderAutoTabCheckBox setEnabled:NO];
        
        [_htmlImageView setImage:nil];
        [_htmlImageView setBookmark:nil];
        [_htmlNameLabel setStringValue:@""];
        [[_htmlNameLabel cell] setPlaceholderString:noSelectString];
        [_htmlNameTextField setStringValue:@""];
        [[_htmlNameTextField cell] setPlaceholderString:noSelectString];
        [_htmlNameTextField setEditable:NO];
        [_htmlAddressTextField setStringValue:@""];
        [[_htmlAddressTextField cell] setPlaceholderString:noSelectString];
        [_htmlAddressTextField setEditable:NO];
        
        return;
    }
    
    // Multiple selection
    if ([bookmarks count] > 1) {
        NSString*   multiString;
        multiString = NSLocalizedString(@"Multiple selection", nil);
        
        [_folderImageView setImage:nil];
        [_folderImageView setBookmark:nil];
        [_folderNameLabel setStringValue:@""];
        [[_folderNameLabel cell] setPlaceholderString:multiString];
        [_folderNameTextField setStringValue:@""];
        [[_folderNameTextField cell] setPlaceholderString:multiString];
        [_folderNameTextField setEditable:NO];
        [_folderAutoTabCheckBox setHidden:NO];
        [_folderAutoTabCheckBox setState:NSOffState];
        
        [_htmlImageView setImage:nil];
        [_htmlImageView setBookmark:nil];
        [_htmlNameLabel setStringValue:@""];
        [[_htmlNameLabel cell] setPlaceholderString:multiString];
        [_htmlNameTextField setStringValue:@""];
        [[_htmlNameTextField cell] setPlaceholderString:multiString];
        [_htmlNameTextField setEditable:NO];
        [_htmlAddressTextField setStringValue:@""];
        [[_htmlAddressTextField cell] setPlaceholderString:multiString];
        [_htmlAddressTextField setEditable:NO];
        
        return;
    }
    
    // Get bookmark
    SRBookmark* bookmark;
    bookmark = [bookmarks objectAtIndex:0];
    
    // Check bookmark type
    if ([bookmark isFolderType]) {
        // Set folder detail view
        [self setDetailView:SRBookmarkFolderDetail];
        
        // Set bookmark property
        [_folderImageView setImage:[bookmark icon]];
        [_folderImageView setBookmark:bookmark];
        [_folderNameLabel setStringValue:[bookmark title]];
        [[_folderNameLabel cell] setPlaceholderString:@""];
        [_folderNameTextField setStringValue:[bookmark title]];
        [[_folderNameTextField cell] setPlaceholderString:@""];
        [_folderNameTextField setEditable:[bookmark isMutable]];
        
        if (bookmark == [[SRBookmarkStorage sharedInstance] shiiraBookmarksBar]) {
            [_folderAutoTabCheckBox setHidden:YES];
        }
        else {
            [_folderAutoTabCheckBox setHidden:NO];
            [_folderAutoTabCheckBox setState:[bookmark isAutoTab] ? NSOnState : NSOffState];
            [_folderAutoTabCheckBox setEnabled:[bookmark isMutable]];
        }
    }
    else if ([bookmark type] == SRBookmarkTypeHTML) {
        // Set HTML detail view
        [self setDetailView:SRBookmarkHTMLDetail];
        
        // Set bookmark property
        [_htmlImageView setImage:[bookmark icon]];
        [_htmlImageView setBookmark:bookmark];
        [_htmlNameLabel setStringValue:[bookmark title]];
        [[_htmlNameLabel cell] setPlaceholderString:@""];
        [_htmlNameTextField setStringValue:[bookmark title]];
        [[_htmlNameTextField cell] setPlaceholderString:@""];
        [_htmlNameTextField setEditable:[bookmark isMutable]];
        [_htmlAddressTextField setStringValue:[bookmark URLString]];
        [[_htmlAddressTextField cell] setPlaceholderString:@""];
        [_htmlAddressTextField setEditable:[bookmark isMutable]];
    }
    else if ([bookmark type] == SRBookmarkTypeRSS) {
        // Set RSS detail view
        [self setDetailView:SRBookmarkRSSDetail];
        
        // Set bookmark property
        [_rssImageView setImage:[bookmark icon]];
        [_rssImageView setBookmark:bookmark];
        [_rssNameLabel setStringValue:[bookmark title]];
        [[_rssNameLabel cell] setPlaceholderString:@""];
        [_rssNameTextField setStringValue:[bookmark title]];
        [[_rssNameTextField cell] setPlaceholderString:@""];
        [_rssNameTextField setEditable:[bookmark isMutable]];
        [_rssAddressTextField setStringValue:[bookmark URLString]];
        [[_rssAddressTextField cell] setPlaceholderString:@""];
        [_rssAddressTextField setEditable:[bookmark isMutable]];
        [_rssUpdateCheckBox setState:[bookmark isAutoUpdate] ? NSOnState : NSOffState];
    }
}

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

- (NSArray*)selectedBookmarks
{
    // Get tab item identifier
    id  identifier;
    identifier = [[_bookmarksTab selectedTabViewItem] identifier];
    
    // Get selected bookmark
    // For bookmark outlie
    if ([identifier isEqualToString:_SRBookmarksTabIdentifier]) {
        // Get index set
        NSIndexSet* indexSet;
        NSEvent*    event;
        indexSet = [_bookmarksOutline selectedRowIndexes];
        event = [NSApp currentEvent];
        if (([event type] == NSLeftMouseDown || [event type] == NSLeftMouseUp) && 
            [event clickCount] > 1)
        {
            indexSet = _bookmarkTableSelectedIndex;
        }
        else {
            [_bookmarkTableSelectedIndex release];
            _bookmarkTableSelectedIndex = [indexSet retain];
        }
        if (!indexSet || [indexSet count] == 0) {
            return nil;
        }
        
        // Get selected rows
        NSMutableArray* bookmarks;
        unsigned int    index;
        bookmarks = [NSMutableArray array];
        index = [indexSet firstIndex];
        do {
            // Get selected bookmark
            SRBookmark* bookmark;
            bookmark = [_bookmarksOutline itemAtRow:index];
            [bookmarks addObject:bookmark];
        } while ((index = [indexSet indexGreaterThanIndex:index]) != NSNotFound);
        
        return bookmarks;
    }
    // For find table
    if ([identifier isEqualToString:_SRFindResultsTabIdentifier]) {
        // Get selected rows
        NSIndexSet* indexSet;
        indexSet = [_findTable selectedRowIndexes];
        if (!indexSet || [indexSet count] == 0) {
            return nil;
        }
        
        // Get selected rows
        NSMutableArray* bookmarks;
        unsigned int    index;
        bookmarks = [NSMutableArray array];
        index = [indexSet firstIndex];
        do {
            // Get selected bookmark
            SRBookmark* bookmark;
            bookmark = [_findResults objectAtIndex:index];
            [bookmarks addObject:bookmark];
        } while ((index = [indexSet indexGreaterThanIndex:index]) != NSNotFound);
        
        return bookmarks;
    }
    
    return nil;
}

- (void)selectBookmark:(SRBookmark*)bookmark
{
    // Select bookmark
    int row;
    row = [_bookmarksOutline rowForItem:bookmark];
    if (row != -1) {
        [_bookmarksOutline selectRowIndexes:[NSIndexSet indexSetWithIndex:row] 
                byExtendingSelection:NO];
        
        // Update detail view
        [self _updateDetailView];
    }
}

- (void)selectBookmarks:(NSArray*)bookmarks
{
    // Create index set
    NSMutableIndexSet*  indexSet;
    NSEnumerator*       enumerator;
    SRBookmark*         bookmark;
    indexSet = [NSMutableIndexSet indexSet];
    enumerator = [bookmarks objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        // Select bookmark
        int row;
        row = [_bookmarksOutline rowForItem:bookmark];
        if (row != -1) {
            [indexSet addIndex:row];
        }
    }
    
    // Select bookmarks
    [_bookmarksOutline selectRowIndexes:indexSet byExtendingSelection:NO];
    
    // Update detail view
    [self _updateDetailView];
}

- (void)selectBookmarkAtRow:(int)row
{
    // Select row
    [_bookmarksOutline selectRowIndexes:[NSIndexSet indexSetWithIndex:row] 
            byExtendingSelection:NO];
    
    // Update detail view
    [self _updateDetailView];
}

- (void)copySelectedBookmarks
{
    // Get selected bookmarks
    NSArray*    bookmarks;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    
    // Write bookmarks to pasteboard
    NSPasteboard*   pboard;
    pboard = [NSPasteboard generalPasteboard];
    SRWriteBookmarksToPasteboard(bookmarks, pboard);
}

- (void)deleteSelectedBookmarks
{
    // Get selected bookmarks
    NSArray*    bookmarks;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    
    // Get fisrt bookmarks
    SRBookmark* bookmark;
    bookmark = [bookmarks objectAtIndex:0];
    if (!bookmark) {
        return;
    }
    
    // Get parent
    SRBookmark* parent = nil;
    if ([bookmarks count] > 1){
        // Use root as parent
        parent = [[SRBookmarkStorage sharedInstance] rootBookmark];
    }
    else {
        parent = [[SRBookmarkStorage sharedInstance] parentOfBookmark:bookmark];
    }
    if (!parent) {
        return;
    }
    
    // Get selected bookmarks view
    id  selectedBookmarksView;
    selectedBookmarksView = [self selectedBookmarksView];
    
    // Get row of first bookmark
    int row = -1;
    if (selectedBookmarksView == _bookmarksOutline) {
        row = [_bookmarksOutline rowForItem:bookmark];
    }
    if (selectedBookmarksView == _findTable) {
        row = [[_findTable selectedRowIndexes] firstIndex];
    }
    
    // Delete bookmark
    [parent removeChildren:bookmarks];
    
    // Select previous one
    if (row > 0) {
        if (selectedBookmarksView == _bookmarksOutline) {
            if (![bookmark isEqual:[_bookmarksOutline itemAtRow:row]]) {
                [self selectBookmarkAtRow:row - 1];
            }
        }
        if (selectedBookmarksView == _findTable) {
            [_findTable selectRowIndexes:[NSIndexSet indexSetWithIndex:row - 1] byExtendingSelection:NO];
        }
    }
}

- (void)pasteBookmarks
{
    // Get bookmarks from pasteboard
    NSPasteboard*   pboard;
    NSArray*        bookmarks;
    pboard = [NSPasteboard generalPasteboard];
    bookmarks = SRReadBookmarksFromPasteboard(pboard);
    if (!bookmarks) {
        return;
    }
    
    // Check selected bookmarks view
    if ([self selectedBookmarksView] != _bookmarksOutline) {
        return;
    }
    
    // Get parent, default is root
    SRBookmark* parent;
    parent = [[SRBookmarkStorage sharedInstance] rootBookmark];
    
    // Check index
    NSArray*    children;
    int         index;
    children = [parent children];
    for (index = 0; index < [children count]; index++) {
        if (![[children objectAtIndex:index] isMutable]) {
            break;
        }
    }
    if (index < 0 || index > [children count]) {
        index = [children count];
    }
    
    // Get selected bookmarks
    NSArray*    selectedBookmarks;
    selectedBookmarks = [self selectedBookmarks];
    if (selectedBookmarks && [selectedBookmarks count] > 0) {
        // Get last bookmarks
        SRBookmark* bookmark;
        bookmark = [selectedBookmarks lastObject];
        
        // For folder
        if ([bookmark isFolderType] && [bookmark isMutable]) {
            parent = bookmark;
            index = [[parent children] count];
        }
        else {
            // Get parent
            SRBookmark* tmp;
            tmp = [[SRBookmarkStorage sharedInstance] parentOfBookmark:bookmark];
            if ([tmp isFolderType] && [tmp isMutable]) {
                parent = tmp;
                
                children = [tmp children];
                index = [children indexOfObject:bookmark];
                if (index == NSNotFound) {
                    index = [children count];
                }
                else {
                    index += 1;
                }
            }
        }
    }
    
    // Insert bookmarks
    [parent insertChildren:bookmarks atIndex:index];
    
    // Select inserted bookmarks
    [self selectBookmarks:bookmarks];
}

- (void)reloadBookmark
{
    // Reload bookmark outline
    [_bookmarksOutline reloadData];
}

- (void)reloadBookmarkFindTable
{
    // Get find string
    NSString*   findString;
    findString = [_searchField stringValue];
    
    // Find bookmark
    NSArray*    results;
    results = [[SRBookmarkStorage sharedInstance] findBookmarkWithString:findString];
    
    // Reload table
    [_findResults release];
    _findResults = [results retain];
    [_findTable reloadData];
}

- (NSMenu*)_contextMenuForView:(id)view 
        event:(NSEvent*)event
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get modifier key flag
    unsigned int    modifierFlags;
    unsigned int    cmdFlag, optionFlag, shiftFlag;
    modifierFlags = [event modifierFlags];
    cmdFlag = modifierFlags & NSCommandKeyMask;
    optionFlag = modifierFlags & NSAlternateKeyMask;
    shiftFlag = modifierFlags & NSShiftKeyMask;
    
    // Create array for tags
    NSMutableArray* tags;
    tags = [NSMutableArray array];
    
    // Select bookmark under the cursor
    NSPoint point;
    int     rowUnderPoint;
    point = [view convertPoint:[event locationInWindow] 
            fromView:nil];
    rowUnderPoint = [view rowAtPoint:point];
    if (![[view selectedRowIndexes] containsIndex:rowUnderPoint]) {
        [view selectRowIndexes:[NSIndexSet indexSetWithIndex:rowUnderPoint] 
                byExtendingSelection:NO];
    }
    
    // Get selected rows
    NSIndexSet* indexSet;
    indexSet = [view selectedRowIndexes];
    
    SRBookmark* bookmark = nil;
    NSArray*    bookmarks = nil;
    
    // No bookmark is selected
    if ([indexSet count] == 0) {
        if ([view isKindOfClass:[SROutlineView class]]) {
            [tags addObject:[NSNumber numberWithInt:SRCreateNewBookmarkFolderTag]];
        }
    }
    else {
        // Just one bookmark is selected
        if ([indexSet count] == 1) {
            // Get bookmark
            bookmark = [[self selectedBookmarks] objectAtIndex:0];
        }
        else {
            // Get bookmarks
            bookmarks = [self selectedBookmarks];
        }
        
        // Check tab availability
        BOOL    enableTabbedBrowsing, selectNewTabs;
        enableTabbedBrowsing = [defaults boolForKey:SRTabEnableTabbedBrowsing];
        selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
        
        // Case of one HTML bookmark
        if (bookmark && ![bookmark isFolderType]) {
            // Create tag array
            [tags addObject:[NSNumber numberWithInt:SROpenBookmarkTag]];
            
            if (enableTabbedBrowsing) {
                if ((selectNewTabs && !shiftFlag) || 
                    (!selectNewTabs && shiftFlag))
                {
                    [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewWindowTag]];
                    [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewTabTag]];
                }
                else {
                    [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewBackgroundWindowTag]];
                    [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewBackgroundTabTag]];
                }
            }
            else {
                [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInNewWindowTag]];
            }
            
            if ([bookmark isMutable]) {
                [tags addObject:[NSNumber numberWithInt:SRDeleteBookmarkTag]];
            }
            if ([view isKindOfClass:[SROutlineView class]]) {
                [tags addObject:[NSNumber numberWithInt:SRCreateNewBookmarkFolderTag]];
            }
        }
        // Case of one bookmark folder or multiple bookmarks
        if ((bookmark && [bookmark isFolderType]) || bookmarks) {
            // Check deletable
            BOOL    isDeletable = NO;
            if (bookmark && [bookmark isMutable]) {
                isDeletable = YES;
            }
            if (bookmarks) {
                NSEnumerator*   enumerator;
                SRBookmark*     tmp;
                enumerator = [bookmarks objectEnumerator];
                while (tmp = [enumerator nextObject]) {
                    if ([tmp isMutable]) {
                        isDeletable = YES;
                        break;
                    }
                }
            }
            
            // Create tag array
            if (enableTabbedBrowsing) {
                [tags addObject:[NSNumber numberWithInt:SROpenBookmarkInTabsTag]];
            }
            if (isDeletable) {
                [tags addObject:[NSNumber numberWithInt:SRDeleteBookmarkTag]];
            }
            if ([view isKindOfClass:[SROutlineView class]]) {
                [tags addObject:[NSNumber numberWithInt:SRCreateNewBookmarkFolderTag]];
            }
        }
    }
    
    if ([tags count] > 0) {
        // Copy menu
        NSMenu* menu;
        menu = [SRContextMenu copyMenuFrom:[SRContextMenu bookmarkContextMenu] 
                ofTags:tags 
                target:_mainWindowController];
        
        // Set represented object
        if (bookmark) {
            [[menu itemArray] makeObjectsPerformSelector:@selector(setRepresentedObject:) 
                    withObject:bookmark];
        }
        else {
            [[menu itemArray] makeObjectsPerformSelector:@selector(setRepresentedObject:) 
                    withObject:bookmarks];
        }
        
        return menu;
    }
    
    return nil;
}

- (void)revealBookmarkWithURLString:(NSString*)URLString
{
    // Find bookmarks
    NSArray*    bookmarks;
    bookmarks = [[SRBookmarkStorage sharedInstance] findBookmarkWithURLString:URLString];
    if (!bookmarks || [bookmarks count] == 0) {
        return;
    }
    
    // Get bookmark path
    SRBookmark* bookmark;
    NSArray*    path;
    bookmark = [bookmarks objectAtIndex:0];
    path = [[SRBookmarkStorage sharedInstance] bookmarkPathWithBookmark:bookmark];
    if (!path) {
        return;
    }
    
    // Reveal bookmark
    int i;
    for (i = 0; i < [path count] - 1; i++) {
        [_bookmarksOutline expandItem:[path objectAtIndex:i]];
    }
    
    int selectedRow;
    selectedRow = [_bookmarksOutline rowForItem:[path lastObject]];
    if (selectedRow > 0) {
        [_bookmarksOutline selectRow:selectedRow byExtendingSelection:NO];
        [_bookmarksOutline scrollRowToVisible:selectedRow];
    }
}

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

- (void)cut:(id)sender
{
    // Copy selected bookmakrs
    [self copySelectedBookmarks];
    
    // Delete selected bookmarks
    [self deleteSelectedBookmarks];
}

- (void)copy:(id)sender
{
    // Copy selected bookmakrs
    [self copySelectedBookmarks];
}

- (void)paste:(id)sender
{
    // Paset bookmarks
    [self pasteBookmarks];
}

- (void)delete:(id)sender
{
    // Delete selected bookmarks
    [self deleteSelectedBookmarks];
}

- (void)bookmarkSelectedAction:(id)sender
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // For single click opening
    if ([defaults integerForKey:SRSidebarClick] == SRSidebarSingleClick) {
        // Open bookmark
        [self openBookmarkAction:sender];
    }
    
    // Update detail view
    [self _updateDetailView];
}

- (void)openBookmarkAction:(id)sender
{
    // Get selected bookmarks
    NSArray*    bookmarks;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks) {
        return;
    }
    
    SRBookmark* bookmark = [bookmarks objectAtIndex:0];
    // For folder
    if ([bookmark isFolderType]) {
        // Get row
        int row;
        row = [_bookmarksOutline rowForItem:bookmark];
        if (row == -1) {
            return;
        }
        
        // Toogle expansion
        if ([_bookmarksOutline isItemExpanded:bookmark]) {
            [_bookmarksOutline collapseItem:bookmark];
        }
        else {
            [_bookmarksOutline expandItem:bookmark];
        }
    }
    // For HTML or RSS
    if ([bookmark type] == SRBookmarkTypeHTML || 
        [bookmark type] == SRBookmarkTypeRSS)
    {
        // Get open action
        SROpenActionType    openAction;
        openAction = SROpenActionTypeFromModifierFlags([[NSApp currentEvent] modifierFlags]);
        
        // Open bookmark wiht open action
        [_mainWindowController openBookmark:bookmark withOpenAction:openAction];
    }
}

- (void)createNewBookmarkFolderAction:(id)sender
{
    // Get selected bookmark
    NSIndexSet* indexSet;
    SRBookmark* bookmark;
    indexSet = [_bookmarksOutline selectedRowIndexes];
    bookmark = [_bookmarksOutline itemAtRow:[indexSet firstIndex]];
    
    // Get parent bookmark folder
    SRBookmark* parent;
    parent = bookmark;
    if (![parent isFolderType]) {
        // Get parent folder
        parent = [[SRBookmarkStorage sharedInstance] parentOfBookmark:bookmark];
    }
    
    // For immutable bookmark folder
    if (![parent isMutable]) {
        // Get root folder
        parent = [[SRBookmarkStorage sharedInstance] rootBookmark];
    }
    
    // Add bookmark folder
    SRBookmark* folder;
    folder = [SRBookmark folderWithTitle:NSLocalizedString(@"Untitled", @"Untitled") 
            originalBrowser:SRBrowserShiira];
    if (parent == [[SRBookmarkStorage sharedInstance] rootBookmark]) {
        // Insert it before immutable folders
        NSArray*    children;
        int         index;
        children = [parent children];
        for (index = 0; index < [children count]; index++) {
            SRBookmark* child;
            child = [children objectAtIndex:index];
            if (![child isMutable]) {
                break;
            }
        }
        
        [parent insertChild:folder atIndex:index];
    }
    else {
        [parent addChild:folder];
    }
    
    // Select bookmark
    [self selectBookmark:folder];
}

- (void)changeBookmarkPropertyAction:(id)sender
{
    // Get selected bookmarks
    NSArray*    bookmarks;
    bookmarks = [self selectedBookmarks];
    if (!bookmarks) {
        return;
    }
    
    SRBookmark* bookmark = [bookmarks objectAtIndex:0];
    // For title
    if (sender == _htmlNameTextField || 
        sender == _folderNameTextField || 
        sender == _rssNameTextField)
    {
        [bookmark setTitle:[sender stringValue]];
    }
    // For URL string
    if (sender == _htmlAddressTextField || 
        sender == _rssAddressTextField) {
        [bookmark setURLString:[sender stringValue]];
    }
    // For auto tab
    if (sender == _folderAutoTabCheckBox) {
        if ([bookmark isFolderType]) {
            [bookmark setAutoTab:[sender state] == NSOnState];
        }
    }
    // For auto update
    if (sender == _rssUpdateCheckBox) {
        if ([bookmark type] == SRBookmarkTypeRSS) {
            [bookmark setAutoUpdate:[sender state] == NSOnState];
        }
    }
}

- (void)findBookmarkAction:(id)sender
{
    // Get find string
    NSString*   findString;
    findString = [_searchField stringValue];
    
    // Switch tab item
    if ([findString length] == 0) {
        [_bookmarksTab selectTabViewItemWithIdentifier:_SRBookmarksTabIdentifier];
        return;
    }
    else {
        [_bookmarksTab selectTabViewItemWithIdentifier:_SRFindResultsTabIdentifier];
    }
    
    // Reload table
    [self reloadBookmarkFindTable];
    
    // Update detail view
    [self _updateDetailView];
}

//--------------------------------------------------------------//
#pragma mark -- NSTableDataSource --
//--------------------------------------------------------------//

- (int)numberOfRowsInTableView:(NSTableView*)tableView
{
    // Find table
    if (tableView == _findTable) {
        return [_findResults count];
    }
    
    return 0;
}

- (id)tableView:(NSTableView*)tableView 
        objectValueForTableColumn:(NSTableColumn*)tableColumn 
        row:(int)rowIndex
{
    // Get identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    // Find table
    if (tableView == _findTable) {
        if ([identifier isEqualToString:@"results"]) {
            return [[_findResults objectAtIndex:rowIndex] title];
        }
    }
    
    return nil;
}

- (BOOL)tableView:(NSTableView*)tableView 
        writeRows:(NSArray*)rows 
        toPasteboard:(NSPasteboard*)pboard
{
    // Find table
    if (tableView == _findTable) {
        // Get specified bookmarks
        NSMutableArray* bookmarks;
        NSEnumerator*   enumerator;
        NSNumber*       rowNumber;
        bookmarks = [NSMutableArray array];
        enumerator = [rows objectEnumerator];
        while (rowNumber = [enumerator nextObject]) {
            if ([rowNumber intValue] < [_findResults count]) {
                [bookmarks addObject:[_findResults objectAtIndex:[rowNumber intValue]]];
            }
        }
        
        // Check Shiira bookmarks bar is involved or not
        if ([bookmarks containsObject:[[SRBookmarkStorage sharedInstance] shiiraBookmarksBar]]) {
            return NO;
        }
        
        // Write bookmarks to pasteboard
        SRWriteBookmarksToPasteboard(bookmarks, pboard);
        
        return YES;
    }
    
    return NO;
}

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

- (void)tableView:(NSTableView*)tableView 
        willDisplayCell:(id)cell 
        forTableColumn:(NSTableColumn*)tableColumn 
        row:(int)rowIndex
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon] && [defaults boolForKey:SRIconUseFaviconSideBar];
    
    // Get column identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    if (tableView == _findTable) {
        // For results column
        if ([identifier isEqualToString:@"results"]) {
            if (isFaviconUsed) {
                // Set image
                [cell setImage:[[_findResults objectAtIndex:rowIndex] icon]];
            }
            else {
                [cell setImage:nil];
            }
            return;
        }
    }
}

- (void)tableViewSelectionDidChange:(NSNotification*)notification
{
    if ([notification object] == _findTable) {
        // Update detail view
        [self _updateDetailView];
    }
}

// Extention method
- (NSMenu*)tableView:(NSTableView*)tableView 
        menuForEvent:(NSEvent*)event
{
    // Find table
    if (tableView == _findTable) {
        return [self _contextMenuForView:tableView event:event];
    }
    
    return nil;
}

// SRTableView delegate
- (void)tableViewDeleteSelectedItem:(NSTableView*)tableView
{
    // Delete selected bookmarks
    [self deleteSelectedBookmarks];
}

//--------------------------------------------------------------//
#pragma mark -- NSOutlineViewDataSource --
//--------------------------------------------------------------//

- (NSArray*)_removeNilTitleOfArray:(NSArray*)historyItems
{
    NSMutableArray* array;
    NSEnumerator*   enumerator;
    WebHistoryItem* item;
    array = [NSMutableArray arrayWithCapacity:[historyItems count]];
    enumerator = [historyItems objectEnumerator];
    while (item = [enumerator nextObject]) {
        if ([item title]) {
            [array addObject:item];
        }
    }
    
    return array;
}

- (id)outlineView:(NSOutlineView*)outlineView 
        child:(int)index 
        ofItem:(id)item
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // For root
        if (!item) {
            item = [[SRBookmarkStorage sharedInstance] rootBookmark];
        }
        return [[item children] objectAtIndex:index];
    }
    
    return nil;
}

- (BOOL)outlineView:(NSOutlineView*)outlineView 
        isItemExpandable:(id)item
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // For root
        if (!item) {
            item = [[SRBookmarkStorage sharedInstance] rootBookmark];
        }
        return [item children] != nil;
    }
    
    return NO;
}

- (int)outlineView:(NSOutlineView*)outlineView 
        numberOfChildrenOfItem:(id)item
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // For root
        if (!item) {
            item = [[SRBookmarkStorage sharedInstance] rootBookmark];
        }
        return [[item children] count];
    }
    
    return 0;
}

- (id)outlineView:(NSOutlineView*)outlineView 
        objectValueForTableColumn:(NSTableColumn*)column 
        byItem:(id)item
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // Get column identifier
        id  identifier;
        identifier = [column identifier];
        
        // For bookmark column
        if ([identifier isEqualToString:@"bookmark"]) {
            return [item title];
        }
    }
    
    return nil;
}

// - outlineView:setObjectValue:forTableColumn:byItem:

- (BOOL)outlineView:(NSOutlineView*)outlineView 
        acceptDrop:(id<NSDraggingInfo>)info 
        item:(id)item 
        childIndex:(int)index
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // Get parent
        SRBookmark* parent;
        parent = item;
        if (!parent) {
            // Root bookmark
            parent = [[SRBookmarkStorage sharedInstance] rootBookmark];
        }
        if (![parent isFolderType]) {
            // Get parent folder
            parent = [[SRBookmarkStorage sharedInstance] parentOfBookmark:parent];
        }
        
        // Check parent type and mutability
        if (![parent isFolderType] || ![parent isMutable]) {
            return NO;
        }
        
        // Check index
        if (index == NSOutlineViewDropOnItemIndex) {
            index = 0;
        }
        
        // Check dragged data type
        NSPasteboard*   pboard;
        NSArray*        types;
        pboard = [info draggingPasteboard];
        types = [pboard types];
        
        // For bookmark data
        if ([types containsObject:SRBookmarkPboardType]) {
            // Read bookmarks
            NSArray*    bookmarks;
            bookmarks = SRReadBookmarksFromPasteboard(pboard);
            if (!bookmarks || [bookmarks count] == 0) {
                return NO;
            }
            
            // Maek them Shiira
            NSEnumerator*   enumerator;
            SRBookmark*     bookmark;
            enumerator = [bookmarks objectEnumerator];
            while (bookmark = [enumerator nextObject]) {
                [bookmark makeItShiira];
            }
            
            // Tunr off bookmark changed notification
            // It would be invoked when old bookmarks are removed
            [[SRBookmarkStorage sharedInstance] setNotifyFlag:NO];
            
            // Insert bookmarks
            if ([[parent children] count] >= index) {
                [parent insertChildren:bookmarks atIndex:index];
            }
            else {
                [parent insertChildren:bookmarks atIndex:index - 1];
            }
            
            // Tunr on bookmark changed notification
            [[SRBookmarkStorage sharedInstance] setNotifyFlag:YES];
            
            // Do I need it?
            [self bookmarkUpdated:nil];
            
            // Select inserted bookmarks
            [self selectBookmarks:bookmarks];
            
            return YES;
        }
        // For WebURLsWithTitlesPboardType
        if ([types containsObject:WebURLsWithTitlesPboardType]) {
            // Get title and URL string
            NSArray*    titles;
            NSArray*    URLStrings;
            titles = [WebURLsWithTitles titlesFromPasteboard:pboard];
            URLStrings = [WebURLsWithTitles _web_userVisibleURLStringsFromPasteboard:pboard];
            
            // Create bookmarks
            NSMutableArray* bookmarks;
            NSEnumerator*   titleEnumerator;
            NSEnumerator*   URLEnumerator;
            NSString*       title;
            NSString*       URLString;
            bookmarks = [NSMutableArray array];
            titleEnumerator = [titles objectEnumerator];
            URLEnumerator = [URLStrings objectEnumerator];
            while ((title = [titleEnumerator nextObject]) && 
                   (URLString = [URLEnumerator nextObject]))
            {
                // Create bookmark
                SRBookmark* bookmark;
                bookmark = [SRBookmark bookmarkWithTitle:title 
                        URLString:URLString 
                        originalBrowser:SRBrowserShiira];
                [bookmarks addObject:bookmark];
            }
            
            // Insert bookmarks
            if ([[parent children] count] >= index) {
                [parent insertChildren:bookmarks atIndex:index];
            }
            else {
                [parent insertChildren:bookmarks atIndex:index - 1];
            }
            
            // Select inserted bookmarks
            [self selectBookmarks:bookmarks];
            
            return YES;
        }
    }
    
    return NO;
}

- (NSDragOperation)outlineView:(NSOutlineView*)outlineView 
        validateDrop:(id<NSDraggingInfo>)info 
        proposedItem:(id)item 
        proposedChildIndex:(int)index
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // Check index
        if (index == NSOutlineViewDropOnItemIndex) {
            return NSDragOperationNone;
        }
        
        // Get parent
        SRBookmark* parent;
        parent = item;
        if (!parent) {
            // Root bookmark
            parent = [[SRBookmarkStorage sharedInstance] rootBookmark];
        }
        if (![parent isFolderType]) {
            // Get parent folder
            parent = [[SRBookmarkStorage sharedInstance] parentOfBookmark:item];
        }
        
        // Check parent type and mutability
        if (![parent isFolderType] ||![parent isMutable]) {
            // Can't accept drop
            return NSDragOperationNone;
        }
        
        // Check for root
        if (parent == [[SRBookmarkStorage sharedInstance] rootBookmark]) {
            // Get child folder
            NSArray*    children;
            SRBookmark* child = nil;
            SRBookmark* preChild = nil;
            children = [parent children];
            if ([children count] > index) {
                child = [children objectAtIndex:index];
            }
            if (index > 0) {
                preChild = [children objectAtIndex:index - 1];
            }
            
            // If child is Shiira bookmarks menu or bar or immutable item
            if ((child == [[SRBookmarkStorage sharedInstance] shiiraBookmarksBar]) || 
                (preChild && ![preChild isMutable]))
            {
                // Can't accept drop
                return NSDragOperationNone;
            }
        }
        
        // Check dragged data type
        NSPasteboard*   pboard;
        NSArray*        types;
        pboard = [info draggingPasteboard];
        types = [pboard types];
        
        // For bookmark data
        if ([types containsObject:SRBookmarkPboardType]) {
            // For dragging from URL combo box
            if ([[info draggingSource] isKindOfClass:[SRURLComboBoxCell class]]) {
                // Copy
                return NSDragOperationCopy;
            }
            
            // Read bookmarks
            NSArray*    bookmarks;
            bookmarks = SRReadBookmarksFromPasteboard(pboard);
            if (!bookmarks || [bookmarks count] == 0) {
                return NSDragOperationNone;
            }
            
            // Check bookmark mutability
            BOOL            isMutable = YES;
            NSEnumerator*   enumerator;
            SRBookmark*     bookmark;
            enumerator = [bookmarks objectEnumerator];
            while (bookmark = [enumerator nextObject]) {
                if (![bookmark isMutable]) {
                    isMutable = NO;
                    break;
                }
            }
            
            // For dragging from bookmark outline
            if ([info draggingSource] == _bookmarksOutline) {
                if (isMutable) {
                    // Copy when option key is pressed
                    if ([info draggingSourceOperationMask] == NSDragOperationCopy) {
                        return NSDragOperationCopy;
                    }
                    
                    // Move
                    return NSDragOperationMove;
                }
                else {
                    // Copy
                    return NSDragOperationCopy;
                }
            }
            
            // Copy for other source
            return NSDragOperationCopy;
        }
        // For WebURLsWithTitlesPboardType
        if ([types containsObject:WebURLsWithTitlesPboardType]) {
            // Copy
            return NSDragOperationCopy;
        }
    }
    
    return NSDragOperationNone;
}

- (id)outlineView:(NSOutlineView*)outlineView 
        itemForPersistentObject:(id)object
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // Return unarchived bookmark
        return [NSUnarchiver unarchiveObjectWithData:object];
    }
    
    return nil;
}

- (id)outlineView:(NSOutlineView*)outlineView 
        persistentObjectForItem:(id)item
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // Return archived bookmark
        return [NSArchiver archivedDataWithRootObject:item];
    }
    
    return nil;
}

- (BOOL)outlineView:(NSOutlineView*)outlineView 
        writeItems:(NSArray*)items 
        toPasteboard:(NSPasteboard*)pboard
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // Check Shiira bookmarks bar is involved or not
        if ([items containsObject:[[SRBookmarkStorage sharedInstance] shiiraBookmarksBar]]) {
            return NO;
        }
        
        // Write bookmarks to pasteboard
        SRWriteBookmarksToPasteboard(items, pboard);
        
        // Store dragging items temporarily
        _draggingBookmarks = items;
        
        return YES;
    }
    
    return NO;
}

//--------------------------------------------------------------//
#pragma mark -- NSOutlineView delegate --
//--------------------------------------------------------------//

- (void)outlineView:(NSOutlineView*)outlineView 
        willDisplayCell:(id)cell 
        forTableColumn:(NSTableColumn*)column 
        item:(id)item
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon] && [defaults boolForKey:SRIconUseFaviconSideBar];
    
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        // Get column identifier
        id  identifier;
        identifier = [column identifier];
        
        // For bookmark column
        if ([identifier isEqualToString:@"bookmark"]) {
            if (isFaviconUsed) {
                // Set image
                [cell setImage:[item icon]];
            }
            else {
                [cell setImage:nil];
            }
            return;
        }
    }
}

- (void)outlineViewSelectionDidChange:(NSNotification*)notification
{
    // Update detail view
    [self _updateDetailView];
}

// Extention method
- (NSMenu*)outlineView:(NSOutlineView*)outlineView 
        menuForEvent:(NSEvent*)event
{
    // Bookmark outline
    if (outlineView == _bookmarksOutline) {
        return [self _contextMenuForView:outlineView event:event];
    }
    
    return nil;
}

// SROutlineView delegate
- (void)outlineViewDeleteSelectedItem:(NSOutlineView*)outlineView
{
    // Delete selected bookmarks
    [self deleteSelectedBookmarks];
}

//--------------------------------------------------------------//
#pragma mark -- NSDraggingSource --
//--------------------------------------------------------------//

- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
    return NSDragOperationMove | NSDragOperationCopy | NSDragOperationDelete;
}

- (void)draggedImage:(NSImage*)image 
        endedAt:(NSPoint)point 
        operation:(NSDragOperation)operation
{
    // For moving and deletion
    if (operation == NSDragOperationMove || operation == NSDragOperationDelete) {
        // Remove bookmarks
        [[[SRBookmarkStorage sharedInstance] rootBookmark] removeChildren:_draggingBookmarks];
        _draggingBookmarks = nil;
    }
}

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

- (void)tabView:(NSTabView*)tabView 
        didSelectTabViewItem:(NSTabViewItem*)tabViewItem
{
    // Update next responder
    [self updateNextResponder];
    
    // Update detail view
    [self _updateDetailView];
}

//--------------------------------------------------------------//
#pragma mark -- Menu item validation --
//--------------------------------------------------------------//

- (BOOL)validateMenuItem:(id<NSMenuItem>)menuItem
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get tag
    int tag;
    tag = [menuItem tag];
    
    switch (tag) {
    // Edit menu
    case SRCutTag: {
        return [self selectedBookmarks] != nil;
    }
    case SRCopyTag: {
        return [self selectedBookmarks] != nil;
    }
    case SRPasteTag: {
        return YES;
    }
    case SRDeleteTag: {
        return [self selectedBookmarks] != nil;
    }
    }
    
    return YES;
}

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

- (void)bookmarkUpdated:(NSNotification*)notification
{
    // Reload bookmark
    [self reloadBookmark];
    [self reloadBookmarkFindTable];
    
    // Update detail view
    [self _updateDetailView];
}

//--------------------------------------------------------------//
#pragma mark -- WebIconDatabase notification --
//--------------------------------------------------------------//

- (void)faviconAdded:(NSNotification*)notification
{
    // Reload bookmark
    [self reloadBookmark];
    [self reloadBookmarkFindTable];
    
    // Update detail view
    [self _updateDetailView];
}

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

- (void)bookmarksViewFrameDidChange:(NSNotification*)notification
{
    // Get bookmarks tab frame and check padding
    NSRect  bookmarksTabFrame, bookmarksFrame;
    int     bookmarksTabPaddingTop, bookmarksTabPaddingBottom;
    bookmarksTabFrame = [_bookmarksTab frame];
    bookmarksFrame = [_bookmarksView frame];
    bookmarksFrame.origin = NSZeroPoint;
    bookmarksTabPaddingTop = (bookmarksFrame.origin.y + bookmarksFrame.size.height) - 
            (bookmarksTabFrame.origin.y + bookmarksTabFrame.size.height);
    bookmarksTabPaddingBottom = bookmarksTabFrame.origin.y - bookmarksFrame.origin.y;
    
    if (bookmarksTabPaddingTop != _bookmarksTabPaddingTop || 
        bookmarksTabPaddingBottom != _bookmarksTabPaddingBottom)
    {
        // Reset padding
        NSRect  newFrame;
        newFrame.origin.x = bookmarksTabFrame.origin.x;
        newFrame.origin.y = bookmarksFrame.origin.y + _bookmarksTabPaddingBottom;
        newFrame.size.width = bookmarksTabFrame.size.width;
        newFrame.size.height = bookmarksFrame.size.height - _bookmarksTabPaddingBottom - _bookmarksTabPaddingTop;
        
        if (newFrame.origin.y > 0 && newFrame.size.height > 0) {
            [_bookmarksTab setFrame:newFrame];
        }
    }
}

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

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void*)context
{
    // For user defaults
    if ([keyPath isEqualToString:SRIconUseFavicon] || 
        [keyPath isEqualToString:SRIconUseFaviconSideBar])
    {
        // Reload bookmark
        [self reloadBookmark];
        [self reloadBookmarkFindTable];
        
        // Update detail view
        [self _updateDetailView];
    }
}

@end
