mirror of
https://github.com/darlinghq/darling-cocotron.git
synced 2024-11-30 07:20:35 +00:00
381 lines
12 KiB
Objective-C
381 lines
12 KiB
Objective-C
/* Copyright (c) 2006-2007 Christopher J. W. Lloyd <cjwl@objc.net>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
|
|
#import <AppKit/NSApplication.h>
|
|
#import <AppKit/NSColor.h>
|
|
#import <AppKit/NSColorPanel.h>
|
|
#import <AppKit/NSColorWell.h>
|
|
#import <AppKit/NSController.h>
|
|
#import <AppKit/NSDragging.h>
|
|
#import <AppKit/NSEvent.h>
|
|
#import <AppKit/NSGraphicsStyle.h>
|
|
#import <AppKit/NSImage.h>
|
|
#import <AppKit/NSObject+BindingSupport.h>
|
|
#import <AppKit/NSPasteboard.h>
|
|
#import <AppKit/NSRaise.h>
|
|
#import <Foundation/NSKeyValueObserving.h>
|
|
#import <Foundation/NSKeyedArchiver.h>
|
|
|
|
@implementation NSColorWell
|
|
|
|
+ (void) initialize {
|
|
// FIXME: need [NSObject setKeys:triggerChangeNotificationsForDependentKey:]
|
|
|
|
// [self setKeys:[NSArray arrayWithObjects:@"color", @"something", nil]
|
|
// triggerChangeNotificationsForDependentKey:@"value"];
|
|
}
|
|
|
|
- (id) _replacementKeyPathForBinding: (id) binding {
|
|
if ([binding isEqual: @"value"])
|
|
return @"color";
|
|
return [super _replacementKeyPathForBinding: binding];
|
|
}
|
|
|
|
// private
|
|
NSNotificationName _NSColorWellDidBecomeExclusiveNotification =
|
|
@"_NSColorWellDidBecomeExclusiveNotification";
|
|
|
|
- (void) encodeWithCoder: (NSCoder *) coder {
|
|
NSUnimplementedMethod();
|
|
}
|
|
|
|
- (instancetype) initWithCoder: (NSCoder *) coder {
|
|
[super initWithCoder: coder];
|
|
|
|
if ([coder allowsKeyedCoding]) {
|
|
NSKeyedUnarchiver *keyed = (NSKeyedUnarchiver *) coder;
|
|
|
|
_isEnabled = [keyed decodeBoolForKey: @"NSEnabled"];
|
|
_isContinuous = ![keyed decodeBoolForKey: @"NSIsNotContinuous"];
|
|
_isBordered = [keyed decodeBoolForKey: @"NSIsBordered"];
|
|
_color = [[keyed decodeObjectForKey: @"NSColor"] retain];
|
|
} else {
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"-[%@ %s] is not implemented for coder %@",
|
|
[self class], sel_getName(_cmd), coder];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype) initWithFrame: (NSRect) frame {
|
|
[super initWithFrame: frame];
|
|
_isEnabled = YES;
|
|
_isContinuous = YES;
|
|
_isBordered = YES;
|
|
_color = [[NSColor whiteColor] copy];
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(colorPanelWillClose:)
|
|
name: NSWindowWillCloseNotification
|
|
object: [NSColorPanel sharedColorPanel]];
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(colorWellDidBecomeExclusive:)
|
|
name: _NSColorWellDidBecomeExclusiveNotification
|
|
object: nil];
|
|
|
|
[self registerForDraggedTypes: @[ NSColorPboardType ]];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) awakeFromNib {
|
|
// this should be moved the nib initWithCoder:
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(colorPanelWillClose:)
|
|
name: NSWindowWillCloseNotification
|
|
object: [NSColorPanel sharedColorPanel]];
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(colorWellDidBecomeExclusive:)
|
|
name: _NSColorWellDidBecomeExclusiveNotification
|
|
object: nil];
|
|
[self registerForDraggedTypes: @[ NSColorPboardType ]];
|
|
}
|
|
|
|
- (void) dealloc {
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
|
[_color release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void) colorWellDidBecomeExclusive: (NSNotification *) note {
|
|
if ([note object] != self)
|
|
[self deactivate];
|
|
}
|
|
|
|
- (void) colorPanelWillClose: (NSNotification *) note {
|
|
if ([self isActive])
|
|
[self deactivate];
|
|
}
|
|
|
|
- (id) target {
|
|
return _target;
|
|
}
|
|
|
|
- (void) setTarget: (id) target {
|
|
_target = target;
|
|
}
|
|
|
|
- (SEL) action {
|
|
return _action;
|
|
}
|
|
|
|
- (void) setAction: (SEL) action {
|
|
_action = action;
|
|
}
|
|
|
|
- (BOOL) isEnabled {
|
|
return _isEnabled;
|
|
}
|
|
|
|
- (void) setEnabled: (BOOL) flag {
|
|
_isEnabled = flag;
|
|
[self setNeedsDisplay: YES];
|
|
}
|
|
|
|
- (NSColor *) color {
|
|
return _color;
|
|
}
|
|
|
|
- (BOOL) isBordered {
|
|
return _isBordered;
|
|
}
|
|
|
|
- (BOOL) isActive {
|
|
return _isActive && [self isEnabled];
|
|
}
|
|
|
|
- (void) setColor: (NSColor *) color {
|
|
if (NSIsControllerMarker(color))
|
|
return [self setColor: [NSColor blackColor]];
|
|
|
|
if ([_color isEqual: color]) {
|
|
return;
|
|
}
|
|
|
|
color = [color retain];
|
|
[_color release];
|
|
_color = color;
|
|
|
|
if ([self isActive] && color != nil) {
|
|
// Pass it on.
|
|
_notifyingColorPanel = YES;
|
|
[[NSColorPanel sharedColorPanel] setColor: color];
|
|
_notifyingColorPanel = NO;
|
|
}
|
|
[self setNeedsDisplay: YES];
|
|
}
|
|
|
|
- (void) setBordered: (BOOL) flag {
|
|
_isBordered = flag;
|
|
}
|
|
|
|
- (void) activate: (BOOL) exclusive {
|
|
if (_isActive == YES) {
|
|
return;
|
|
}
|
|
|
|
if (exclusive) {
|
|
NSNotification *notification = [NSNotification
|
|
notificationWithName: _NSColorWellDidBecomeExclusiveNotification
|
|
object: self];
|
|
|
|
[[NSNotificationQueue defaultQueue]
|
|
enqueueNotification: notification
|
|
postingStyle: NSPostNow
|
|
coalesceMask: NSNotificationCoalescingOnName
|
|
forModes: nil];
|
|
}
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(changeColorWhenActive:)
|
|
name: NSColorPanelColorDidChangeNotification
|
|
object: [NSColorPanel sharedColorPanel]];
|
|
|
|
// Update the color panel with our color.
|
|
[[NSColorPanel sharedColorPanel] setColor: [self color]];
|
|
|
|
[NSApp orderFrontColorPanel: self];
|
|
_isActive = YES;
|
|
[self setNeedsDisplay: YES];
|
|
}
|
|
|
|
- (void) deactivate {
|
|
if (_isActive == NO)
|
|
return;
|
|
|
|
_isActive = NO;
|
|
[[NSNotificationCenter defaultCenter]
|
|
removeObserver: self
|
|
name: NSColorPanelColorDidChangeNotification
|
|
object: [NSColorPanel sharedColorPanel]];
|
|
[self setNeedsDisplay: YES];
|
|
}
|
|
|
|
- (void) changeColorWhenActive: (NSNotification *) note {
|
|
if (_notifyingColorPanel == NO) {
|
|
[self setColor: [[note object] color]];
|
|
[self sendAction: _action to: _target];
|
|
}
|
|
}
|
|
|
|
- (BOOL) isOpaque {
|
|
return YES;
|
|
}
|
|
|
|
- (void) drawWellInside: (NSRect) rect {
|
|
[_color drawSwatchInRect: rect];
|
|
}
|
|
|
|
- (void) drawRect: (NSRect) rect {
|
|
rect = _bounds;
|
|
|
|
rect = [[self graphicsStyle] drawColorWellBorderInRect: rect
|
|
enabled: [self isEnabled]
|
|
bordered: [self isBordered]
|
|
active: [self isActive]];
|
|
|
|
[self drawWellInside: rect];
|
|
}
|
|
|
|
- (void) takeColorFrom: (id) sender {
|
|
[self setColor: [sender color]];
|
|
}
|
|
|
|
- (void) mouseDown: (NSEvent *) event {
|
|
|
|
if (![self isEnabled]) {
|
|
return;
|
|
}
|
|
|
|
if ([self isBordered]) {
|
|
/*
|
|
* Bordered color wells have interesting logic:
|
|
|
|
o If the user clicks and drags in the swatch then they
|
|
can drag a color out - and the well becomes disabled
|
|
|
|
o If the user clicks on the border and drags then the control
|
|
activates or not depending on whether the mouse is within the
|
|
control or not
|
|
|
|
o If the user simply clicks in the swatch or the border
|
|
the well is toggled between active and inactive states
|
|
*/
|
|
BOOL wasActive = [self isActive];
|
|
NSPoint point = [self convertPoint: [event locationInWindow]
|
|
fromView: nil];
|
|
BOOL mouseInBorder = !NSMouseInRect(point, NSInsetRect(_bounds, 8, 8),
|
|
[self isFlipped]);
|
|
BOOL canStartDrag = !mouseInBorder;
|
|
if (mouseInBorder) {
|
|
// Toggle the initial state.
|
|
if (wasActive)
|
|
[self deactivate];
|
|
else
|
|
[self activate: YES];
|
|
}
|
|
|
|
BOOL shouldStartDrag = NO;
|
|
do {
|
|
event = [[self window]
|
|
nextEventMatchingMask: NSLeftMouseUpMask |
|
|
NSLeftMouseDraggedMask];
|
|
point = [self convertPoint: [event locationInWindow] fromView: nil];
|
|
BOOL mouseInBounds =
|
|
NSMouseInRect(point, _bounds, [self isFlipped]);
|
|
if ([event type] == NSLeftMouseDragged) {
|
|
if (canStartDrag) {
|
|
// Get dragging the color.
|
|
shouldStartDrag = YES;
|
|
} else {
|
|
// Toggle the state based on where the cursor is.
|
|
if (mouseInBounds) {
|
|
if (wasActive) {
|
|
[self deactivate];
|
|
} else {
|
|
[self activate: YES];
|
|
}
|
|
} else {
|
|
if (wasActive) {
|
|
[self activate: YES];
|
|
} else {
|
|
[self deactivate];
|
|
}
|
|
}
|
|
}
|
|
} else if (mouseInBounds) {
|
|
// Just toggle the state.
|
|
if (wasActive) {
|
|
[self deactivate];
|
|
} else {
|
|
[self activate: YES];
|
|
}
|
|
}
|
|
} while ([event type] != NSLeftMouseUp && shouldStartDrag == NO);
|
|
|
|
if (shouldStartDrag == NO) {
|
|
if ([self isActive] == YES) {
|
|
if (!([event modifierFlags] & NSShiftKeyMask)) {
|
|
[self activate: YES];
|
|
}
|
|
[[NSColorPanel sharedColorPanel] setColor: [self color]];
|
|
[NSApp orderFrontColorPanel: self];
|
|
}
|
|
return;
|
|
} else {
|
|
// We're going to drag a swatch so deactivate (like Cocoa)
|
|
[self deactivate];
|
|
}
|
|
}
|
|
|
|
[NSColorPanel dragColor: _color withEvent: event fromView: self];
|
|
}
|
|
|
|
- (NSDragOperation) draggingSourceOperationMaskForLocal: (BOOL) isLocal {
|
|
return NSDragOperationCopy;
|
|
}
|
|
|
|
- (NSDragOperation) draggingEntered: (id<NSDraggingInfo>) sender {
|
|
return NSDragOperationCopy;
|
|
}
|
|
|
|
- (NSDragOperation) draggingUpdated: (id<NSDraggingInfo>) sender {
|
|
return NSDragOperationCopy;
|
|
}
|
|
|
|
- (BOOL) prepareForDragOperation: (id<NSDraggingInfo>) sender {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL) performDragOperation: (id<NSDraggingInfo>) sender {
|
|
NSPasteboard *pboard = [sender draggingPasteboard];
|
|
NSColor *color = [NSColor colorFromPasteboard: pboard];
|
|
|
|
[self setColor: color];
|
|
[self sendAction: _action to: _target];
|
|
|
|
return YES;
|
|
}
|
|
|
|
@end
|