/*
SRBookmarksBarView.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 "SRBookmark.h"
#import "SRBookmarkStorage.h"

#import "SRBookmarksBarView.h"
#import "SRBookmarkButton.h"

#import "SRPasteboard.h"
#import "WebKitEx.h"

static const int    SRBookmarksBarEndMarginWidth = 8;
static const int    SRBookmarksBarButtonMarginWidth = 4;
static const int    SRBookmarksBarButtonMarginWidthWOFavicon = 8;

@interface SRBookmarksBarView (private)
- (void)_updateBookmarkButtons;
@end

#pragma mark -

@implementation SRBookmarksBarView

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

- (void)_setDropAcceptForlderIndex:(int)index
{
    // Clear off previous selected button
    if (_dropAccpetFrolderIndex > 0 && _dropAccpetFrolderIndex != index) {
        if ([_bookmarkButtons count] > _dropAccpetFrolderIndex) {
            [[_bookmarkButtons objectAtIndex:_dropAccpetFrolderIndex] highlight:NO];
        }
    }
    
    // Select new one
    if (index != -1) {
        if ([_bookmarkButtons count] > index) {
            [[_bookmarkButtons objectAtIndex:index] highlight:YES];
        }
    }
    
    _dropAccpetFrolderIndex = index;
}

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (!self) {
        return nil;
    }
    
    // Initialize instance variables
    _bookmarkButtons = [[NSMutableArray array] retain];
    _indicatorIndex = -1;
    _dropAccpetFrolderIndex = -1;
    
    // Set accept drag
    _acceptDrag = NO;
    [self setAcceptDrag:YES];
    
    // Register notifications
    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];
    
    return self;
}

- (void)dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:self];
    [_bookmarkButtons release];
    [_bookmarksBar release];
    
    [super dealloc];
}

- (void)setAcceptDrag:(BOOL)acceptDrag
{
    if (_acceptDrag != acceptDrag) {
        _acceptDrag = acceptDrag;
        
        if (_acceptDrag) {
            // Register accept drag type
            NSArray*    types;
            types = [NSArray arrayWithObjects:
                    SRBookmarkPboardType, WebURLsWithTitlesPboardType, nil];
            [self registerForDraggedTypes:types];
        }
        else {
            // Unregister drag type
            [self unregisterDraggedTypes];
        }
    }
}

- (BOOL)acceptsFirstResponder
{
    return YES;
}

- (SRBookmark*)bookmarksBar
{
    return _bookmarksBar;
}

- (void)setBookmarksBar:(SRBookmark*)bookmarksBar
{
    if (_bookmarksBar != bookmarksBar) {
        [_bookmarksBar release];
        _bookmarksBar = [bookmarksBar retain];
    }
    
    // Update bookmark buttons
    [self _updateBookmarkButtons];
}

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

- (void)updateButtons
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get frame
    NSRect  frame;
    frame = [self frame];
    
    // Check favicon availability
    BOOL    isFaviconUsed;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon] && [defaults boolForKey:SRIconUseFaviconBookmarkBar];
    
    // Remove all buttons
    [[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)];
    
    // Get clip menu image widht;
    static int  _clipButtonWidth = -1;
    if (_clipButtonWidth == -1) {
        _clipButtonWidth = [(NSImage*)[NSImage imageNamed:@"ClipIndicator"] size].width;
    }
    
    // Check clip status, and make all buttons non clip menu
    int                 width;
    int                 clipButtonWidth = 0;
    NSEnumerator*       enumerator;
    SRBookmarkButton*   bookmarkButton;
    width = SRBookmarksBarEndMarginWidth;
    enumerator = [_bookmarkButtons objectEnumerator];
    while (bookmarkButton = [enumerator nextObject]) {
        [bookmarkButton setClipMenu:NO];
        width += [[bookmarkButton cell] cellSize].width + 
                (isFaviconUsed ? SRBookmarksBarButtonMarginWidth : SRBookmarksBarButtonMarginWidthWOFavicon);
    }
    width += SRBookmarksBarEndMarginWidth;
    if (width > frame.size.width) {
        // It would have clip menu
        clipButtonWidth = _clipButtonWidth;
    }
    
    // Add buttons
    int currentX;
    id  lastButton;
    currentX = SRBookmarksBarEndMarginWidth;
    lastButton = [_bookmarkButtons lastObject];
    enumerator = [_bookmarkButtons objectEnumerator];
    while (bookmarkButton = [enumerator nextObject]) {
        // Update bookmark button
        [bookmarkButton update];
        
        // Get bookmark button size
        NSSize  buttonSize;
        buttonSize = [[bookmarkButton cell] cellSize];
        
        // Calculate remaining width
        int remainWidth;
        remainWidth = currentX + buttonSize.width + SRBookmarksBarEndMarginWidth;
        if (bookmarkButton != lastButton) {
            remainWidth += clipButtonWidth;
        }
        
        if (remainWidth < frame.size.width) {
            // Add button
            [self addSubview:bookmarkButton];
            [bookmarkButton setFrame:NSMakeRect(
                    currentX, 
                    ceil((frame.size.height - buttonSize.height) / 2), 
                    buttonSize.width, 
                    buttonSize.height)];
            currentX += buttonSize.width + 
                    (isFaviconUsed ? SRBookmarksBarButtonMarginWidth : SRBookmarksBarButtonMarginWidthWOFavicon);
        }
        else {
            break;
        }
    }
    
    // Make it clip menu
    if (bookmarkButton) {
        SRBookmarkButton*   clipedButton;
        clipedButton = bookmarkButton;
        [clipedButton setClipMenu:YES];
        
        // Correct bookmarks from not displayed buttons
        NSMutableArray* bookmarks;
        bookmarks = [NSMutableArray arrayWithObject:[clipedButton bookmark]];
        while (bookmarkButton = [enumerator nextObject]) {
            [bookmarks addObject:[bookmarkButton bookmark]];
        }
        
        // Set cliped bookmarks
        [clipedButton setClipedBookmarks:bookmarks];
        
        // Add button
        [self addSubview:clipedButton];
        NSSize  buttonSize;
        buttonSize = [[clipedButton cell] cellSize];
        [clipedButton setFrame:NSMakeRect(
                frame.size.width - SRBookmarksBarEndMarginWidth - buttonSize.width, 
                (frame.size.height - buttonSize.height) / 2, 
                buttonSize.width, 
                buttonSize.height)];
    }
    
    [self setNeedsDisplay:YES];
}

- (void)updateButtonForURL:(NSString*)URLString
{
    NSEnumerator*       enumerator;
    SRBookmarkButton*   button;
    enumerator = [_bookmarkButtons objectEnumerator];
    while (button = [enumerator nextObject]) {
        if ([[[button bookmark] URLString] isEqualToString:URLString]) {
            // Update bookmark
            [button update];
        }
    }
}

- (void)_updateInsertIndexAtPoint:(NSPoint)location draggingInfo:(id<NSDraggingInfo>)info
{
    int                 index = 0;
    NSEnumerator*       enumerator;
    SRBookmarkButton*   bookmarkButton;
    enumerator = [_bookmarkButtons objectEnumerator];
    while (bookmarkButton = [enumerator nextObject]) {
        // Check button is clipped or not
        if ([bookmarkButton isClipMenu]) {
            break;
        }
        
        // Get button frame
        NSRect  buttonFrame;
        buttonFrame = [bookmarkButton frame];
        
        // Get bookmark
        SRBookmark* bookmark;
        bookmark = [bookmarkButton bookmark];
        
        // Make indicataion rect in left side
        NSRect  rect;
        rect.origin = NSZeroPoint;
        rect.size.width = buttonFrame.origin.x + buttonFrame.size.width / 2;
        rect.size.height = [self frame].size.height;
        
        if (NSPointInRect(location, rect)) {
            // For bookmark folder
            if ([bookmark isFolderType] && 
                [bookmark isMutable] && 
                ![[info draggingSource] isKindOfClass:[SRBookmarkButton class]])
            {
                [self _setDropAcceptForlderIndex:index];
                _indicatorIndex = -1;
            }
            else {
                [self _setDropAcceptForlderIndex:-1];
                _indicatorIndex = index;
            }
            return;
        }
        
        // Make indicataion rect in right side
        rect.size.width = buttonFrame.origin.x + buttonFrame.size.width;
        rect.size.height = [self frame].size.height;
        
        if (NSPointInRect(location, rect)) {
            // For bookmark folder
            if ([bookmark isFolderType] && 
                [bookmark isMutable] && 
                ![[info draggingSource] isKindOfClass:[SRBookmarkButton class]])
            {
                [self _setDropAcceptForlderIndex:index];
                _indicatorIndex = -1;
            }
            else {
                [self _setDropAcceptForlderIndex:-1];
                _indicatorIndex = index + 1;
            }
            return;
        }
        
        index++;
    }
    
    [self _setDropAcceptForlderIndex:-1];
    _indicatorIndex = index;
}

- (void)_updateInsertIndicatorAtPoint:(NSPoint)location draggingInfo:(id<NSDraggingInfo>)info
{
    // Update insert index
    [self _updateInsertIndexAtPoint:location draggingInfo:info];
    
    // Update view
    [self setNeedsDisplay:YES];
}

- (SRBookmarkButton*)_createBookmarkButtonOfBookmark:(SRBookmark*)bookmark
{
    // Create bookmark button
    SRBookmarkButton*   bookmarkButton;
    bookmarkButton = [[SRBookmarkButton alloc] initWithFrame:NSZeroRect];
    [bookmarkButton autorelease];
    [bookmarkButton setBookmark:bookmark];
    [bookmarkButton setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
    [bookmarkButton setTarget:[[self window] windowController]];
    [bookmarkButton setAction:@selector(openBookmarkAction:)];
    
    return bookmarkButton;
}

- (void)_updateBookmarkButtons
{
    // Remove all buttons
    [[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)];
    [_bookmarkButtons removeAllObjects];
    
    // Enumerate bookmark
    NSEnumerator*   enumerator;
    SRBookmark*     bookmark;
    enumerator = [[_bookmarksBar children] objectEnumerator];
    while (bookmark = [enumerator nextObject]) {
        // Create bookmark button
        SRBookmarkButton*   bookmarkButton;
        bookmarkButton = [self _createBookmarkButtonOfBookmark:bookmark];
        
        // Add bookmark button
        [_bookmarkButtons addObject:bookmarkButton];
    }
    
    // Update buttons
    [self updateButtons];
}

- (void)addBookmark:(SRBookmark*)bookmark
{
    // Create bookmark button
    SRBookmarkButton*   bookmarkButton;
    bookmarkButton = [self _createBookmarkButtonOfBookmark:bookmark];
    
    // Add bookmark button
    [_bookmarkButtons addObject:bookmarkButton];
    
    // Update buttons
    [self updateButtons];
}

- (void)insertBookmark:(SRBookmark*)bookmark atIndex:(int)index
{
    // Create bookmark button
    SRBookmarkButton*   bookmarkButton;
    bookmarkButton = [self _createBookmarkButtonOfBookmark:bookmark];
    
    // Insert bookmark button
    [_bookmarkButtons insertObject:bookmarkButton atIndex:index];
    
    // Update buttons
    [self updateButtons];
}

- (void)removeBookmark:(SRBookmark*)bookmark
{
    // Find bookmark button
    NSEnumerator*       enumerator;
    SRBookmarkButton*   bookmarkButton;
    enumerator = [_bookmarkButtons objectEnumerator];
    while (bookmarkButton = [enumerator nextObject]) {
        if ([[bookmarkButton bookmark] isEqual:bookmark]) {
            // Remove bookmark
            [_bookmarkButtons removeObject:bookmarkButton];
        }
    }
    
    // Update buttons
    [self updateButtons];
}

- (void)removeAllBookmarks
{
    // Remove all bookmark buttons
    [_bookmarkButtons removeAllObjects];
    
    // Update buttons
    [self updateButtons];
}

- (void)drawRect:(NSRect)rect
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Check favicon availability
    BOOL    isFaviconUsed;
    isFaviconUsed = [defaults boolForKey:SRIconUseFavicon] && [defaults boolForKey:SRIconUseFaviconBookmarkBar];
    
    // Clear rect
    // It seems no need
    //[[NSColor windowBackgroundColor] set];
    //NSRectFill(rect);

    // Draw top hilite in metal
	if([[self window]styleMask] & NSTexturedBackgroundWindowMask){
        [[NSColor colorWithCalibratedWhite:1.0 alpha:0.4]set];
        float   y=[self bounds].origin.y+[self bounds].size.height-0.5;
        [NSBezierPath strokeLineFromPoint:NSMakePoint([self bounds].origin.x,y) 
                        toPoint:NSMakePoint([self bounds].origin.x+[self bounds].size.width,y)];
	}
    
    // Draw indicator
    if (_indicatorIndex != -1) {
        // Get self frame
        NSRect  frame;
        frame = [self frame];
        
        // Get bookmark button frame
        SRBookmarkButton*   bookmarkButton;
        NSRect              bookmarkFrame;
        if (_indicatorIndex < [_bookmarkButtons count]) {
            bookmarkButton = [_bookmarkButtons objectAtIndex:_indicatorIndex];
            if (![bookmarkButton isClipMenu]) {
                bookmarkFrame = [bookmarkButton frame];
            }
            else {
                NSRect  frame;
                bookmarkButton = [_bookmarkButtons objectAtIndex:_indicatorIndex - 1];
                frame = [bookmarkButton frame];
                bookmarkFrame.origin.x = frame.origin.x + frame.size.width + 
                        (isFaviconUsed ? SRBookmarksBarButtonMarginWidth : SRBookmarksBarButtonMarginWidthWOFavicon);
                bookmarkFrame.origin.y = frame.origin.y;
            }
        }
        else if (_indicatorIndex == [_bookmarkButtons count]) {
            if ([_bookmarkButtons count] == 0) {
                bookmarkFrame.origin.x = SRBookmarksBarEndMarginWidth;
            }
            else {
                NSRect  frame;
                bookmarkButton = [_bookmarkButtons objectAtIndex:_indicatorIndex - 1];
                frame = [bookmarkButton frame];
                bookmarkFrame.origin.x = frame.origin.x + frame.size.width + 
                        (isFaviconUsed ? SRBookmarksBarButtonMarginWidth : SRBookmarksBarButtonMarginWidthWOFavicon);
                bookmarkFrame.origin.y = frame.origin.y;
            }
        }
        else {
            bookmarkFrame.origin.x = frame.size.width - 4;
        }
        
        // Draw indicator
        [[NSColor blackColor] set];
        NSRectFill(NSMakeRect(bookmarkFrame.origin.x - 3, 7, 2, frame.size.height - 10));
        
        NSBezierPath*   path;
        path = [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(bookmarkFrame.origin.x - 4, 3, 4, 4)];
        [path stroke];
    }
}

- (void)setFrame:(NSRect)frame
{
    [super setFrame:frame];
    
    [self updateButtons];
}

//--------------------------------------------------------------//
#pragma mark -- NSDraggingDestination protocol --
//--------------------------------------------------------------//

- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info
{
    NSPoint location;
    location = [self convertPoint:[info draggingLocation] fromView:nil];
    [self _updateInsertIndicatorAtPoint:location draggingInfo:info];
    
    // Check drag source
    id  draggingSource;
    draggingSource = [info draggingSource];
    if ([draggingSource isKindOfClass:[SRBookmarkButton class]]) {
        // Copy when option key is pressed
        if ([info draggingSourceOperationMask] == NSDragOperationCopy) {
            return NSDragOperationCopy;
        }
        
        return NSDragOperationMove;
    }
    
    return NSDragOperationCopy;
}

- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info
{
    NSPoint location;
    location = [self convertPoint:[info draggingLocation] fromView:nil];
    [self _updateInsertIndicatorAtPoint:location draggingInfo:info];
    
    // Check drag source
    id  draggingSource;
    draggingSource = [info draggingSource];
    if ([draggingSource isKindOfClass:[SRBookmarkButton class]]) {
        // Copy with option key
        if ([info draggingSourceOperationMask] == NSDragOperationCopy) {
            return NSDragOperationCopy;
        }
        
        return NSDragOperationMove;
    }
    
    return NSDragOperationCopy;
}

- (void)draggingEnded:(id<NSDraggingInfo>)info
{
    _indicatorIndex = -1;
    [self _setDropAcceptForlderIndex:-1];
    [self setNeedsDisplay:YES];
}

- (void)draggingExited:(id<NSDraggingInfo>)info
{
    _indicatorIndex = -1;
    [self _setDropAcceptForlderIndex:-1];
    [self setNeedsDisplay:YES];
}

- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info
{
    return YES;
}

- (BOOL)performDragOperation:(id<NSDraggingInfo>)info
{
    // Check dragged data type
    NSPasteboard*   pboard;
    NSArray*        types;
    pboard = [info draggingPasteboard];
    types = [pboard types];
    if (![types containsObject:SRBookmarkPboardType] && 
        ![types containsObject:WebURLsWithTitlesPboardType])
    {
        // Do not accept drag
        _indicatorIndex = -1;
        [self _setDropAcceptForlderIndex:-1];
        [self setNeedsDisplay:YES];
        return NO;
    }
    
    // For SRBookmarkPboardType
    if ([types containsObject:SRBookmarkPboardType]) {
        // Read bookmarks
        NSArray*    bookmarks;
        bookmarks = SRReadBookmarksFromPasteboard(pboard);
        if (!bookmarks || [bookmarks count] == 0) {
            _indicatorIndex = -1;
            [self _setDropAcceptForlderIndex:-1];
            [self setNeedsDisplay:YES];
            return NO;
        }
        
        if (_indicatorIndex != -1) {
            if ([[info draggingSource] isKindOfClass:[SRBookmarkButton class]] && 
                [info draggingSourceOperationMask] != NSDragOperationCopy)
            {
                // Tunr off bookmark changed notification
                // It would be invoked when old bookmarks are removed
                [[SRBookmarkStorage sharedInstance] setNotifyFlag:NO];
            }
            
            // Insert bookmarks
            [_bookmarksBar insertChildren:bookmarks atIndex:_indicatorIndex];
            
            // Tunr on bookmark changed notification
            [[SRBookmarkStorage sharedInstance] setNotifyFlag:YES];
        }
        
        if (_dropAccpetFrolderIndex != -1 && ![[info draggingSource] isKindOfClass:[SRBookmarkButton class]]) {
            // Insert bookmarks into bookmark folder
            if ([_bookmarkButtons count] > _dropAccpetFrolderIndex) {
                SRBookmarkButton*   button;
                SRBookmark*         bookmark;
                button = [_bookmarkButtons objectAtIndex:_dropAccpetFrolderIndex];
                bookmark = [button bookmark];
                [bookmark addChildren: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];
        }
        
        if (_indicatorIndex != -1) {
            // Insert bookmarks into bookmarks bar
            [_bookmarksBar insertChildren:bookmarks atIndex:_indicatorIndex];
        }
        
        if (_dropAccpetFrolderIndex != -1) {
            // Insert bookmarks into bookmark folder
            if ([_bookmarkButtons count] > _dropAccpetFrolderIndex) {
                SRBookmarkButton*   button;
                SRBookmark*         bookmark;
                button = [_bookmarkButtons objectAtIndex:_dropAccpetFrolderIndex];
                bookmark = [button bookmark];
                [bookmark addChildren:bookmarks];
            }
        }
        
        return YES;
    }
    
    return NO;
}

- (void)concludeDragOperation:(id<NSDraggingInfo>)info
{
    _indicatorIndex = -1;
    [self _setDropAcceptForlderIndex:-1];
    [self setNeedsDisplay:YES];
}

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

- (void)deleteBookmarkAction:(id)sender
{
    if ([sender respondsToSelector:@selector(representedObject)]) {
        // Get bookmark
        SRBookmarkButton*   bookmarkButton;
        SRBookmark*         bookmark;
        bookmarkButton = [sender representedObject];
        bookmark = [bookmarkButton bookmark];
        
        // Delete bookmark
        // It would cause bookmark updated notification
        [_bookmarksBar removeChild:bookmark];
    }
}

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

- (void)bookmarkUpdated:(NSNotification*)notification
{
    // Update buttons
    [self _updateBookmarkButtons];
}

@end
