blob: a4bf639e58f78b4679fe2fc2141cce49c1c8b865 [file] [log] [blame]
/*
* File: ATColorTableController.m
* Abstract: A controller used by the ATImageTextCell to edit the color property. It is implemented in an abstract enough way to be used by a class other than the cell.
*
* Version: 1.0
*
* 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) 2009 Apple Inc. All Rights Reserved.
*
*/
#import "ATColorTableController.h"
#import "ATPopupWindow.h"
@implementation ATColorTableController
+ (ATColorTableController *) sharedColorTableController {
static ATColorTableController * gSharedColorTableController = nil;
if (gSharedColorTableController == nil) {
gSharedColorTableController = [[[self class] alloc] initWithNibName:@"ColorTable" bundle:[NSBundle bundleForClass:[self class]]];
}
return gSharedColorTableController;
}
@synthesize delegate = _delegate;
@dynamic selectedColor, selectedColorName;
- (void) dealloc {
[_colorList release];
[_colorNames release];
[_window 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;
if (row != -1) {
[_tableColorList scrollRowToVisible:row];
[_tableColorList selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
} else {
[_tableColorList scrollRowToVisible:0];
[_tableColorList selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
}
_updatingSelection = NO;
}
- (void) _createWindowIfNeeded {
if (_window == nil) {
NSRect viewFrame = self.view.frame;
// Create and setup our window
_window = [[ATPopupWindow alloc] initWithContentRect:viewFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
[_window setReleasedWhenClosed:NO];
[_window setLevel:NSPopUpMenuWindowLevel];
[_window setHasShadow:YES];
[[_window contentView] addSubview:self.view];
[_window makeFirstResponder:_tableColorList];
// Make the window have a clear color and be non-opaque for our pop-up animation
[_window setBackgroundColor:[NSColor clearColor]];
[_window setOpaque:NO];
}
}
- (void) _windowClosed:(NSNotification *)note {
if (_eventMonitor) {
[NSEvent removeMonitor:_eventMonitor];
_eventMonitor = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:_window];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidResignActiveNotification object:nil];
}
- (void) _closeAndSendAction:(BOOL)sendAction {
[_window close];
if (sendAction) {
if ([self.delegate respondsToSelector:@selector(colorTableController:didChooseColor:named:)]) {
[self.delegate colorTableController:self didChooseColor:self.selectedColor named:self.selectedColorName];
}
} else {
if ([self.delegate respondsToSelector:@selector(didCancelColorTableController:)]) {
[self.delegate didCancelColorTableController:self];
}
}
}
- (void) _windowShouldClose:(NSNotification *)note {
[self _closeAndSendAction:NO];
}
- (void) editColor:(NSColor *)color locatedAtScreenRect:(NSRect)rect {
[self _createWindowIfNeeded];
[self _selectColor:color];
NSPoint origin = rect.origin;
NSRect windowFrame = [_window frame];
// The origin is the lower left; subtract the window's height
origin.y -= NSHeight(windowFrame);
// Center the popup window under the rect
origin.y += floor(NSHeight(rect) / 3.0);
origin.x -= floor(NSWidth(windowFrame) / 2.0);
origin.x += floor(NSWidth(rect) / 2.0);
[_window setFrameOrigin:origin];
[_window popup];
// Add some watches on the window and application
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(_windowClosed:)
name:NSWindowWillCloseNotification
object:_window];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowShouldClose:) name:NSApplicationDidResignActiveNotification object:nil];
// Start watching events to figure out when to close the window
NSAssert(_eventMonitor == nil, @"_eventMonitor should not be created yet");
_eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask | NSKeyDownMask handler: ^(NSEvent * incomingEvent) {
NSEvent * result = incomingEvent;
NSWindow * targetWindowForEvent = [incomingEvent window];
if (targetWindowForEvent != _window) {
[self _closeAndSendAction:NO];
} else if ([incomingEvent type] == NSKeyDown) {
if ([incomingEvent keyCode] == 53) {
// Escape
[self _closeAndSendAction:NO];
result = nil; // Don't process the event
} else if ([incomingEvent keyCode] == 36) {
// Enter
[self _closeAndSendAction:YES];
result = nil;
}
}
return result;
}];
}
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView {
return _colorNames.count;
}
- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
return [_colorNames objectAtIndex:row];
}
- (void) tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSColor * color = [_colorList colorWithKey:[_colorNames objectAtIndex:row]];
[cell setColor:color];
}
- (void) _tableViewAction:(id)sender {
[self _closeAndSendAction:YES];
}
@end