/*
SRShelfController.m

Author: Makoto Kinoshita

Copyright 2004-2006 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of 
  conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SHIIRA PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

#import "SRBookmark.h"
#import "SRWebHistory.h"

#import "SRAppController.h"
#import "SRDefaultKeys.h"

#import "SRPrefDefaultKeys.h"

#import "SRConstants.h"

#import "SRShelf.h"
#import "SRShelfController.h"
#import "SRShelfBar.h"

@interface SRShelfController (private)
// Shelf management
- (void)_loadShelfs;
- (void)_updateShelfArray;
- (void)_updateShelf;
@end

@implementation SRShelfController

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

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Initialize instance variables
    _isInitialized = NO;
    _shelfInfos = [[NSMutableArray array] retain];
    
    // Load nib file
    if (![NSBundle loadNibNamed:@"Shelf" owner:self]) {
        // Error
        NSLog(@"Can't load Shelf.nib");
    }
    
    // Register key value observation
    [defaults addObserver:self forKeyPath:SRBookmarkMenuUsageFlags 
            options:NSKeyValueObservingOptionNew context:NULL];
    
    return self;
}

- (void)didLoad
{
    // Configure table view
    [_tableView setWantsLiveResizeToUseCachedImage:NO];
    
    // Set image text cell
    NSTableColumn*          column;
    NSCell*                 oldCell;
    HMImageTextFieldCell*   cell;
    column = [_tableView tableColumnWithIdentifier:@"shelf"];
    oldCell = [column dataCell];
    cell = [[HMImageTextFieldCell alloc] init];
    [cell setFont:[oldCell font]];
    [cell setUseBoldForHighlight:YES];
    [column setDataCell:cell];
    [cell release];
    
    // Set header cell
    NSCell*             oldHeaderCell;
    HMTableHeaderCell*  headerCell;
    oldHeaderCell = [column headerCell];
    headerCell = [[HMTableHeaderCell alloc] init];
    [headerCell setObjectValue:[oldHeaderCell objectValue]];
    [headerCell setAlignment:[oldHeaderCell alignment]];
    [headerCell setFont:[oldHeaderCell font]];
    [column setHeaderCell:headerCell];
    [headerCell release];
    
    // Set header view
    NSTableHeaderView*  oldHeaderView;
    HMTableHeaderView*  headerView;
    oldHeaderView = [_tableView headerView];
    headerView = [[HMTableHeaderView alloc] initWithFrame:[oldHeaderView frame]];
    [headerView setSplitView:_splitView];
    [_tableView setHeaderView:headerView];
    
    // Configure shlef bar
    [_shelfBar setTitle:NSLocalizedString(@"Shelf", nil)];
    [_shelfBar setTitleWidth:[_tableView frame].size.width];
    
    // Configure split view
    [_splitView setSplitType:HMSplitLine];
    
    float   shelfTableWidth;
    shelfTableWidth = [[NSUserDefaults standardUserDefaults] floatForKey:SRShelfTableWidth];
    if ([[_splitView subviews] count] > 0 && shelfTableWidth > 0) {
        // Set subview width
        NSView* subview;
        NSRect  frame;
        float   delta;
        
        subview = [[_splitView subviews] objectAtIndex:0];
        frame = [subview frame];
        delta = frame.size.width - shelfTableWidth;
        frame.size.width = shelfTableWidth;
        [subview setFrame:frame];
        
        subview = [[_splitView subviews] objectAtIndex:1];
        frame = [subview frame];
        frame.size.width += delta;
        [subview setFrame:frame];
        
        [_splitView adjustSubviews];
        
        // Set shelf bar title width
        [_shelfBar setTitleWidth:[_tableView frame].size.width];
    }
    
    // Configure array controller
    NSSortDescriptor*   sortDescriptor;
    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES];
    [_shelfArrayController setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];
    
    _isInitialized = YES;

    // Load shelfs
    [self _loadShelfs];
    [self _updateShelfArray];
    [_shelfArrayController setSelectionIndex:1];
    
    // Update shelf
    [self _updateShelf];
}

- (void)viewDidMoveToHostWindow
{
}

- (void)viewWillMoveToHostWindow:(NSWindow*)hostWindow
{
}

- (void)dealloc
{
    // Remove observer
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObserver:self forKeyPath:SRBookmarkMenuUsageFlags];
    
    [_shelfInfos release], _shelfInfos = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Shelf management --
//--------------------------------------------------------------//

- (id)selectedShelf
{
    return nil;
    //return _selectedShelf;
}

- (void)_loadShelfs
{
    NSMutableArray* shelfBundleIds;
    NSMutableArray* shelfIcons;
    NSMutableArray* shelfTitles;
    shelfBundleIds = [NSMutableArray array];
    shelfIcons = [NSMutableArray array];
    shelfTitles = [NSMutableArray array];
    
    // Load build-in shelfs
    NSString*   shelfPath;
    shelfPath = [[[[NSBundle mainBundle] builtInPlugInsPath] stringByDeletingLastPathComponent] 
            stringByAppendingPathComponent:@"Shelfs"];
    
    // Load shelf bundles
    NSArray*        dirContents;
    NSEnumerator*   enumerator;
    NSString*       dirContent;
    dirContents = [[NSFileManager defaultManager] directoryContentsAtPath:shelfPath];
    enumerator = [dirContents objectEnumerator];
    while (dirContent = [enumerator nextObject]) {
        // Check extention
        if (![dirContent hasSuffix:@".shelf"]) {
            continue;
        }
        
        // Create bundle path
        NSString*   bundlePath;
        bundlePath = [shelfPath stringByAppendingPathComponent:dirContent];
        
        // Create bundle
        NSBundle*       bundle;
        NSDictionary*   infoPlist;
        bundle = [NSBundle bundleWithPath:bundlePath];
        infoPlist = [bundle infoDictionary];
        if (!bundle || !infoPlist) {
            continue;
        }
        
        // Get bundle ID
        NSString*   bundleId;
        bundleId = [bundle bundleIdentifier];
        if (!bundleId) {
            continue;
        }
        if ([shelfBundleIds containsObject:bundleId]) {
            // Error
            NSLog(@"Duplicated bundle ID, %@", bundleId);
            continue;
        }
        
        // Get title
        NSString*   title;
        title = [bundle localizedStringForKey:@"SRShelfTitle" value:nil table:@"InfoPlist"];
        if (!title || [title isEqualToString:@"SRShelfTitle"]) {
            title = [infoPlist objectForKey:@"SRShelfTitle"];
        }
        if (!title) {
            continue;
        }
        
        // Get icon
        NSString*   iconPath;
        NSImage*    icon = nil;
        iconPath = [infoPlist objectForKey:@"SRShelfIconFile"];
        if (iconPath) {
            iconPath = [[bundle resourcePath] stringByAppendingPathComponent:iconPath];
            icon = [[NSImage alloc] initWithContentsOfFile:iconPath];
            if (icon) {
                [icon setSize:NSMakeSize(16.0f, 16.0f)];
                [icon autorelease];
            }
        }
        
        // Get shelf group
        NSString*   shelfGroup;
        shelfGroup = [infoPlist objectForKey:@"SRShelfGroup"];
        if (!shelfGroup) {
            shelfGroup = NSLocalizedString(@"OTHER", nil);
        }
        
        // Add shelf info
        NSMutableDictionary*    shelfInfo;
        shelfInfo = [NSMutableDictionary dictionary];
        [shelfInfo setObject:bundle forKey:@"bundle"];
        [shelfInfo setObject:bundleId forKey:@"bundleId"];
        [shelfInfo setObject:title forKey:@"title"];
        if (icon) {
            [shelfInfo setObject:icon forKey:@"icon"];
        }
        [shelfInfo setObject:shelfGroup forKey:@"group"];
        
        [_shelfInfos addObject:shelfInfo];
    }
    
#if 0
    // Add separator
    NSMutableDictionary*    separator;
    separator = [NSMutableDictionary dictionary];
    [separator setObject:[NSNumber numberWithBool:YES] forKey:@"isSeparator"];
    [separator setObject:[NSNumber numberWithInt:10] forKey:@"index"];
    [_shelfInfos addObject:separator];
    
    separator = [NSMutableDictionary dictionary];
    [separator setObject:[NSNumber numberWithBool:YES] forKey:@"isSeparator"];
    [separator setObject:[NSNumber numberWithInt:20] forKey:@"index"];
    [_shelfInfos addObject:separator];
    
    separator = [NSMutableDictionary dictionary];
    [separator setObject:[NSNumber numberWithBool:YES] forKey:@"isSeparator"];
    [separator setObject:[NSNumber numberWithInt:30] forKey:@"index"];
    [_shelfInfos addObject:separator];
    
    separator = [NSMutableDictionary dictionary];
    [separator setObject:[NSNumber numberWithBool:YES] forKey:@"isSeparator"];
    [separator setObject:[NSNumber numberWithInt:40] forKey:@"index"];
    [_shelfInfos addObject:separator];
#endif
}

- (NSMutableDictionary*)_shelfInfoWithIdentifier:(NSString*)identifier
{
    // Find shelf info
    NSEnumerator*           enumerator;
    NSMutableDictionary*    shelfInfo;
    enumerator = [_shelfInfos objectEnumerator];
    while (shelfInfo = [enumerator nextObject]) {
        // Get identifier
        if ([[shelfInfo objectForKey:@"bundleId"] isEqualToString:identifier]) {
            return shelfInfo;
        }
    }
    
    return nil;
}

- (void)_updateShelfArray
{
    static NSDictionary*    _attr = nil;
    if (!_attr) {
        NSShadow*   shadow;
        shadow = [[NSShadow alloc] init];
        [shadow setShadowBlurRadius:0.0f];
        [shadow setShadowColor:[NSColor whiteColor]];
        [shadow setShadowOffset:NSMakeSize(0, -1.0f)];
        
        _attr = [[NSDictionary alloc] initWithObjectsAndKeys:
                [NSFont boldSystemFontOfSize:11], NSFontAttributeName, 
                [NSColor grayColor], NSForegroundColorAttributeName, 
                shadow, NSShadowAttributeName, 
                nil];
        [shadow release];
    }
    
    // Get usage flags
    NSDictionary*   usageFlags;
    usageFlags = [[NSUserDefaults standardUserDefaults] objectForKey:SRBookmarkMenuUsageFlags];
    if (![usageFlags isKindOfClass:[NSDictionary class]]) {
        usageFlags = nil;
    }
    
    // Remove old shelf info
    [_shelfArrayController removeObjects:[_shelfArrayController content]];
    
    // Collect shelf groups
    NSMutableSet*           groups;
    NSEnumerator*           enumerator;
    NSMutableDictionary*    shelfInfo;
    groups = [NSMutableSet set];
    enumerator = [_shelfInfos objectEnumerator];
    while (shelfInfo = [enumerator nextObject]) {
        // Get shelf group
        NSString*   group;
        group = [shelfInfo objectForKey:@"group"];
        if (![groups containsObject:group]) {
            [groups addObject:group];
        }
    }
    
    NSAttributedString* attrStr;
    NSNumber*           flag;
    int                 index = 0;
    
    //
    // BOOKMARKS shelf
    //
    if ([groups containsObject:NSLocalizedString(@"BOOKMARKS", nil)]) {
        // Add title
        attrStr = [[NSAttributedString alloc] 
                initWithString:NSLocalizedString(@"BOOKMARKS", nil) attributes:_attr];
        shelfInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                attrStr, @"title", 
                [NSNumber numberWithBool:YES], @"isGroupTitle", 
                nil];
        [attrStr release];
        [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
        [_shelfArrayController addObject:shelfInfo];
        
        // Add Shiira bookmarks
        shelfInfo = [self _shelfInfoWithIdentifier:@"jp.hmdt.shiira.bookmarkshelf"];
        if (shelfInfo) {
            [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
            [_shelfArrayController addObject:shelfInfo];
        }
        
        // Add Safari bookmarks
        flag = [usageFlags objectForKey:SRBrowserSafari];
        if (!flag || [flag boolValue]) {
            shelfInfo = [self _shelfInfoWithIdentifier:@"jp.hmdt.shiira.safaribookmarkshelf"];
            if (shelfInfo) {
                [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
                [_shelfArrayController addObject:shelfInfo];
            }
        }
        
        // Add Firefox bookmarks
        flag = [usageFlags objectForKey:SRBrowserFirefox];
        if (!flag || [flag boolValue]) {
            shelfInfo = [self _shelfInfoWithIdentifier:@"jp.hmdt.shiira.firefoxbookmarkshelf"];
            if (shelfInfo) {
                [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
                [_shelfArrayController addObject:shelfInfo];
            }
        }
        
        // Add address book
        shelfInfo = [self _shelfInfoWithIdentifier:@"jp.hmdt.shiira.addressbookshelf"];
        if (shelfInfo) {
            [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
            [_shelfArrayController addObject:shelfInfo];
        }
    }
    
    //
    // HISTORIES shelf
    //
    if ([groups containsObject:NSLocalizedString(@"HISTORIES", nil)]) {
        // Add title
        attrStr = [[NSAttributedString alloc] 
                initWithString:NSLocalizedString(@"HISTORIES", nil) attributes:_attr];
        shelfInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                attrStr, @"title", 
                [NSNumber numberWithBool:YES], @"isGroupTitle", 
                nil];
        [attrStr release];
        [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
        [_shelfArrayController addObject:shelfInfo];
        
        // Add histories
        shelfInfo = [self _shelfInfoWithIdentifier:@"jp.hmdt.shiira.historyshelf"];
        if (shelfInfo) {
            [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
            [_shelfArrayController addObject:shelfInfo];
        }
        
        // Add downalods
        shelfInfo = [self _shelfInfoWithIdentifier:@"jp.hmdt.shiira.downloadshelf"];
        if (shelfInfo) {
            [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
            [_shelfArrayController addObject:shelfInfo];
        }
    }
    
    //
    // COLLECTION shelf
    //
    if ([groups containsObject:NSLocalizedString(@"COLLECTION", nil)]) {
        // Add title
        attrStr = [[NSAttributedString alloc] 
                initWithString:NSLocalizedString(@"COLLECTION", nil) attributes:_attr];
        shelfInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                attrStr, @"title", 
                [NSNumber numberWithBool:YES], @"isGroupTitle", 
                nil];
        [attrStr release];
        [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
        [_shelfArrayController addObject:shelfInfo];
        
        // Add search engine
        shelfInfo = [self _shelfInfoWithIdentifier:@"jp.hmdt.shiira.searchengineshelf"];
        if (shelfInfo) {
            [shelfInfo setObject:[NSNumber numberWithInt:index++] forKey:@"index"];
            [_shelfArrayController addObject:shelfInfo];
        }
    }
    
    //
    // PLUG-IN shelf
    //
    if ([groups containsObject:NSLocalizedString(@"PLUG-IN", nil)]) {
    }
    
    //
    // OTHER shelf
    //
    if ([groups containsObject:NSLocalizedString(@"OTHER", nil)]) {
    }
    
    // Rearrange objects
    [_shelfArrayController rearrangeObjects];
}

- (void)_updateShelf
{
    // Get selected shelf info
    NSArray*                shelfInfos;
    NSMutableDictionary*    shelfInfo;
    shelfInfos = [_shelfArrayController selectedObjects];
    if (!shelfInfos || [shelfInfos count] == 0) {
        return;
    }
    shelfInfo = [shelfInfos objectAtIndex:0];
    
    // Get bundle ID
    NSString*   bundleId;
    bundleId = [shelfInfo objectForKey:@"bundleId"];
    
    // Get tab view item
    NSTabViewItem*  tabViewItem = nil;
    int             index;
    index = [_tabView indexOfTabViewItemWithIdentifier:bundleId];
    if (index != NSNotFound) {
        tabViewItem = [_tabView tabViewItemAtIndex:index];
    }
    if (!tabViewItem) {
        // Create tab view item
        tabViewItem = [[NSTabViewItem alloc] initWithIdentifier:bundleId];
        [_tabView addTabViewItem:tabViewItem];
    }
    
    // Select tab view item
    [_tabView selectTabViewItem:tabViewItem];
    
    // Get shelf instance
    SRShelf*    shelf;
    shelf = [shelfInfo objectForKey:@"shelf"];
    if (!shelf) {
        // Get bundle
        NSBundle*   bundle;
        bundle = [shelfInfo objectForKey:@"bundle"];
        if (!bundle) {
            return;
        }
        
        // Load bundle
        if (![bundle isLoaded]) {
            if (![bundle load]) {
                // Error
                NSLog(@"Failed to load the bundle, %@", bundleId);
                return;
            }
        }
        
        // Create shelf instance
        Class   principalClass;
        principalClass = [bundle principalClass];
        if (!principalClass) {
            // Error
            NSLog(@"principalClass is nil");
            return;
        }
        shelf = [[principalClass alloc] initWithBundle:bundle];
        if (!shelf) {
            // Error
            NSLog(@"Failed to create shelf instance, %@", bundleId);
            return;
        }
        
        // Set controllers
        [shelf setAppController:[SRAppController sharedInstance]];
        
        id  browserController;
        browserController = [[_view window] windowController];
        if ([browserController conformsToProtocol:@protocol(SRBrowserController)]) {
            [shelf setBrowserController:browserController];
        }
        //[shelf setPageController:[]];
        
        id  context, store;
        context = [[SRAppController sharedInstance] managedObjectContext];
        store = [[SRAppController sharedInstance] persistentStore];
        id  bookmarkFactory;
        bookmarkFactory = [SRBookmarkFactory 
                sharedInstanceWithManagedObjectContext:context persistentStore:store];
        [shelf setBookmarkFactory:bookmarkFactory];
        
        [shelf setWebHistory:[SRWebHistory sharedInstance]];
        
        [shelf loadMainNib];
        [shelf didLoad];
        
        // Set shelf
        [shelfInfo setObject:shelf forKey:@"shelf"];
        [shelf release];
    }
    
    // Check subviews
    if ([[[tabViewItem view] subviews] count] == 0) {
        // Get main view
        NSView* mainView;
        mainView = [shelf mainView];
        if (!mainView) {
            return;
        }
        
        // Set view
        [tabViewItem setView:mainView];
    }
    
    // Set bar buttons
    [_shelfBar setLeftButtons:[shelf leftBarButtons]];
    [_shelfBar setRightButtons:[shelf rightBarButtons]];
    
    // Select tab view item
    [_tabView selectTabViewItem:tabViewItem];
}

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

- (NSView*)view
{
    return _view;
}

//--------------------------------------------------------------//
#pragma mark -- Shelf selection --
//--------------------------------------------------------------//

- (NSString*)selectedShelfId
{
    return nil;
}

- (void)setSelectedShelfId:(NSString*)shelfId
{
    // Find shelf with ID
    NSArray*    objects;
    objects = [_shelfArrayController arrangedObjects];
    
    int i;
    for (i = 0; i < [objects count]; i++) {
        NSDictionary*   shelfInfo;
        shelfInfo = [objects objectAtIndex:i];
        
        if ([[shelfInfo objectForKey:@"bundleId"] isEqualToString:shelfId]) {
            break;
        }
    }
    if (i == [objects count]) {
        return;
    }
    
    // Select index
    [_shelfArrayController setSelectionIndex:i];
    
    // Update shelf
    [self _updateShelf];
}

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

- (BOOL)tableView:(NSTableView*)tableView shouldSelectRow:(int)rowIndex
{
    // Get shelf info
    NSArray*        shelfInfos;
    NSDictionary*   shelfInfo;
    shelfInfos = [_shelfArrayController arrangedObjects];
    if ([shelfInfos count] < rowIndex) {
        return NO;
    }
    shelfInfo = [shelfInfos objectAtIndex:rowIndex];
    
    // For group title
    NSNumber*   isGroupTitle;
    isGroupTitle = [shelfInfo objectForKey:@"isGroupTitle"];
    if (isGroupTitle && [isGroupTitle boolValue]) {
        return NO;
    }
    
    // For separator
    NSNumber*   isSeparator;
    isSeparator = [shelfInfo objectForKey:@"isSeparator"];
    if (isSeparator && [isSeparator boolValue]) {
        return NO;
    }
    
    return YES;
}

- (void)tableViewSelectionDidChange:(NSNotification*)notification
{
    // Update shelf
    [self _updateShelf];
}

- (void)tableView:(NSTableView*)tableView 
        willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(int)rowIndex
{
    // Get identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    // For shelf
    if ([identifier isEqualToString:@"shelf"]) {
        // Get shelf info
        NSArray*        shelfInfos;
        NSDictionary*   shelfInfo;
        shelfInfos = [_shelfArrayController arrangedObjects];
        if ([shelfInfos count] < rowIndex) {
            return;
        }
        shelfInfo = [shelfInfos objectAtIndex:rowIndex];
        
        // Get icon
        NSImage*    icon;
        icon = [shelfInfo objectForKey:@"icon"];
        [cell setImage:icon];
        
        // For separator
        NSNumber*   isSeparator;
        isSeparator = [shelfInfo objectForKey:@"isSeparator"];
        if (isSeparator && [isSeparator boolValue]) {
            [cell setSeparator:YES];
        }
        else {
            [cell setSeparator:NO];
        }
    }
}

- (float)tableView:(NSTableView*)tableView heightOfRow:(int)rowIndex
{
    // Get row height
    float   height;
    height = [tableView rowHeight];
    
    // Get shelf info
    NSArray*        shelfInfos;
    NSDictionary*   shelfInfo;
    shelfInfos = [_shelfArrayController arrangedObjects];
    if ([shelfInfos count] < rowIndex) {
        return height;
    }
    shelfInfo = [shelfInfos objectAtIndex:rowIndex];
    
    // For group title
    NSNumber*   isGroupTitle;
    isGroupTitle = [shelfInfo objectForKey:@"isGroupTitle"];
    if (isGroupTitle && [isGroupTitle boolValue]) {
        return 24.0f;
    }
    
    // For separator
    NSNumber*   isSeparator;
    isSeparator = [shelfInfo objectForKey:@"isSeparator"];
    if (isSeparator && [isSeparator boolValue]) {
        return 8.0f;
    }
    
    return height;
}

//--------------------------------------------------------------//
#pragma mark -- HMTableView delegate --
//--------------------------------------------------------------//

- (BOOL)tableView:(NSTableView*)tableView 
        frameOfCellAtColumn:(int)column 
        row:(int)row 
        proposedFrame:(NSRect*)proposedFrame
{
    // Get shelf info
    NSArray*        shelfInfos;
    NSDictionary*   shelfInfo;
    shelfInfos = [_shelfArrayController arrangedObjects];
    if ([shelfInfos count] < row) {
        return NO;
    }
    shelfInfo = [shelfInfos objectAtIndex:row];
    
    // For group title
    NSNumber*   isGroupTitle;
    isGroupTitle = [shelfInfo objectForKey:@"isGroupTitle"];
    if (isGroupTitle && [isGroupTitle boolValue]) {
        return NO;
    }
    
    // Shrink frame
    proposedFrame->origin.x += 11.0f;
    proposedFrame->size.width -= 11.0f;
    
    return YES;
}

//--------------------------------------------------------------//
#pragma mark -- NSSplitView delegate --
//--------------------------------------------------------------//

- (float)splitView:(NSSplitView*)splitView 
        constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)offset
{
    return 100.0f;
}

- (float)splitView:(NSSplitView*)splitView 
        constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)offset
{
    return 200.0f;
}

- (void)splitViewDidResizeSubviews:(NSNotification*)notification
{
    // Set shlef bar title width
    [_shelfBar setTitleWidth:[_tableView frame].size.width];
    
    // Set shelf table width
    if (_isInitialized) {
        [[NSUserDefaults standardUserDefaults] 
                setFloat:[_tableView frame].size.width forKey:SRShelfTableWidth];
    }
}

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

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    // For user defaults
    if (object == [NSUserDefaults standardUserDefaults]) {
        // Bookmark pref
        if ([keyPath isEqualToString:SRBookmarkMenuUsageFlags]) {
            [self _updateShelfArray];
        }
    }
}

@end
