ATColorTableController.m

/*
     File: ATColorTableController.m 
 Abstract: A controller used by the ATColorTableController to edit the color property. Also demonstrates NSPopover introduced in MacOS 10.7.
  
  Version: 1.3 
  
 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple 
 Inc. ("Apple") in consideration of your agreement to the following 
 terms, and your use, installation, modification or redistribution of 
 this Apple software constitutes acceptance of these terms.  If you do 
 not agree with these terms, please do not use, install, modify or 
 redistribute this Apple software. 
  
 In consideration of your agreement to abide by the following terms, and 
 subject to these terms, Apple grants you a personal, non-exclusive 
 license, under Apple's copyrights in this original Apple software (the 
 "Apple Software"), to use, reproduce, modify and redistribute the Apple 
 Software, with or without modifications, in source and/or binary forms; 
 provided that if you redistribute the Apple Software in its entirety and 
 without modifications, you must retain this notice and the following 
 text and disclaimers in all such redistributions of the Apple Software. 
 Neither the name, trademarks, service marks or logos of Apple Inc. may 
 be used to endorse or promote products derived from the Apple Software 
 without specific prior written permission from Apple.  Except as 
 expressly stated in this notice, no other rights or licenses, express or 
 implied, are granted by Apple herein, including but not limited to any 
 patent rights that may be infringed by your derivative works or by other 
 works in which the Apple Software may be incorporated. 
  
 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE 
 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 
 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 
 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 
 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
  
 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 
 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 
 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 
 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 
 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 
 POSSIBILITY OF SUCH DAMAGE. 
  
 Copyright (C) 2012 Apple Inc. All Rights Reserved. 
  
*/
 
#import "ATColorTableController.h"
#import "ATTableCellView.h"
#import "ATColorView.h"
 
@implementation ATColorTableController
 
+ (ATColorTableController *)sharedColorTableController {
    static ATColorTableController *gSharedColorTableController = nil;
    if (gSharedColorTableController == nil) {
        gSharedColorTableController = [[[self class] alloc] initWithNibName:@"ATColorTable" bundle:[NSBundle bundleForClass:[self class]]];
    }
    return gSharedColorTableController;
}
 
@synthesize delegate = _delegate;
@dynamic selectedColor, selectedColorName;
 
- (void)dealloc {
    [_colorList release];
    [_colorNames release];
    [_popover release];
    [super dealloc];
}
 
- (void)loadView {
    [super loadView];
    _colorList = [[NSColorList colorListNamed:@"Crayons"] retain];
    _colorNames = [[_colorList allKeys] retain];
    [_tableColorList setIntercellSpacing:NSMakeSize(3, 3)];
    [_tableColorList setTarget:self];
    [_tableColorList setAction:@selector(_tableViewAction:)];
}
 
- (NSColor *)selectedColor {
    NSString *name = [self selectedColorName];
    if (name != nil) {
        return [_colorList colorWithKey:name];
    } else {
        return nil;
    }
}
 
- (NSString *)selectedColorName {
    if ([_tableColorList selectedRow] != -1) {
        return [_colorNames objectAtIndex:[_tableColorList selectedRow]];
    } else {
        return nil;
    }
}
 
- (void)_selectColor:(NSColor *)color {
    // Search for that color in our list
    NSInteger row = 0;
    for (NSString *name in _colorNames) {
        NSColor *colorInList = [_colorList colorWithKey:name];
        if ([color isEqual:colorInList]) {
            break;
        }
        row++;
    }    
    _updatingSelection = YES;
    // This is done in an animated fashion
    if (row != -1) {
        [_tableColorList scrollRowToVisible:row];
        [[_tableColorList animator] selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
    } else {
        [_tableColorList scrollRowToVisible:0];
        [[_tableColorList animator] selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
    }
    _updatingSelection = NO;
}
 
- (void)_makePopoverIfNeeded {
    if (_popover == nil) {
        // Create and setup our window
        _popover = [[NSPopover alloc] init];
        // The popover retains us and we retain the popover. We drop the popover whenever it is closed to avoid a cycle.
        _popover.contentViewController = self;
        _popover.behavior = NSPopoverBehaviorTransient;
        _popover.delegate = self;
    }
}
 
- (void)editColor:(NSColor *)color withPositioningView:(NSView *)positioningView {
    [self _makePopoverIfNeeded];
    [self _selectColor:color];
    [_popover showRelativeToRect:[positioningView bounds] ofView:positioningView preferredEdge:NSMinYEdge];
}
 
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return _colorNames.count;
}
 
- (id)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    NSString *name = [_colorNames objectAtIndex:row];
    NSColor *color = [_colorList colorWithKey:name];
    // In IB, the TableColumn's identifier is set to "Automatic". The ATTableCellView's is also set to "Automatic". IB then keeps the two in sync, and we don't have to worry about setting the identifier.
    ATTableCellView *result = [tableView makeViewWithIdentifier:[tableColumn identifier] owner:nil];
    result.colorView.backgroundColor = color;
    result.colorView.drawBorder = YES;
    result.subTitleTextField.stringValue = name;
    return result;
}
 
- (void)_tableViewAction:(id)sender {
    [_popover close];
    if ([self.delegate respondsToSelector:@selector(colorTableController:didChooseColor:named:)]) {
        [self.delegate colorTableController:self didChooseColor:self.selectedColor named:self.selectedColorName];
    }
}
 
- (void)popoverDidClose:(NSNotification *)notification {
    // Free the popover to avoid a cycle. We could also just break the contentViewController property, and reset it when we show the popover
    [_popover release];
    _popover = nil;
}
 
@end