/* * CPColorPanel.j * AppKit * * Created by Ross Boucher. * Copyright 2008, 280 North, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @import "CPButton.j" @import "CPCookie.j" @import "CPPanel.j" @import "CPView.j" CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification"; var PREVIEW_HEIGHT = 20.0, TOOLBAR_HEIGHT = 32.0, SWATCH_HEIGHT = 14.0, ICON_WIDTH = 32.0, ICON_PADDING = 12.0; var SharedColorPanel = nil, ColorPickerClasses = []; /* A color wheel @global @group CPColorPanelMode */ CPWheelColorPickerMode = 1; /* Slider based picker @global @group CPColorPanelMode */ CPSliderColorPickerMode = 2; CPColorPickerViewWidth = 265, CPColorPickerViewHeight = 370; /*! @ingroup appkit @class CPColorPanel CPColorPanel provides a reusable panel that can be used displayed on screen to prompt the user for a color selection. To obtain the panel, call the \c +sharedColorPanel method. */ @implementation CPColorPanel : CPPanel { _CPColorPanelToolbar _toolbar; _CPColorPanelSwatches _swatchView; _CPColorPanelPreview _previewView; CPSlider _opacitySlider; CPArray _colorPickers; CPView _currentView; id _activePicker; CPColor _color; id _target; SEL _action; int _mode; } /*! A list of color pickers is collected here, and any color panel created will contain any picker in this list up to this point. In other words, call before creating a color panel. */ + (void)provideColorPickerClass:(Class)aColorPickerSubclass { ColorPickerClasses.push(aColorPickerSubclass); } /*! Returns (and if necessary, creates) the shared color panel. */ + (CPColorPanel)sharedColorPanel { if (!SharedColorPanel) SharedColorPanel = [[CPColorPanel alloc] init]; return SharedColorPanel; } /*! Sets the mode for the shared color panel. @param mode the mode to which the color panel will be set */ + (void)setPickerMode:(CPColorPanelMode)mode { var panel = [CPColorPanel sharedColorPanel]; [panel setMode: mode]; } /* To obtain the color panel, use \c +sharedColorPanel. @ignore */ - (id)init { self = [super initWithContentRect:CGRectMake(500.0, 50.0, 219.0, 370.0) styleMask:(CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)]; if (self) { [[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]]; [self setTitle:@"Color Panel"]; [self setLevel:CPFloatingWindowLevel]; [self setFloatingPanel:YES]; [self setBecomesKeyOnlyIfNeeded:YES]; [self setMinSize:CGSizeMake(219.0, 342.0)]; [self setMaxSize:CGSizeMake(323.0, 537.0)]; } return self; } /*! Sets the color of the panel, and updates the picker. Also posts a \c CPColorPanelDidChangeNotification. */ - (void)setColor:(CPColor)aColor { _color = aColor; [_previewView setBackgroundColor: _color]; [CPApp sendAction:@selector(changeColor:) to:nil from:self]; if (_target && _action) [CPApp sendAction:_action to:_target from:self]; [[CPNotificationCenter defaultCenter] postNotificationName:CPColorPanelColorDidChangeNotification object:self]; [_activePicker setColor:_color]; [_opacitySlider setFloatValue:[_color alphaComponent]]; } /*! Sets the selected color of the panel and optionally updates the picker. @param bool whether or not to update the picker @ignore */ - (void)setColor:(CPColor)aColor updatePicker:(BOOL)bool { [self setColor:aColor]; if (bool) [_activePicker setColor:_color]; } /*! Returns the panel's currently selected color. */ - (CPColor)color { return _color; } - (float)opacity { return [_opacitySlider floatValue]; } /*! Sets the target for the color panel. Messages are sent to the target when colors are selected in the panel. */ - (void)setTarget:(id)aTarget { _target = aTarget; } /*! Returns the current target. The target receives messages when colors are selected in the panel. */ - (id)target { return _target; } /*! Sets the action that gets sent to the target. This action is sent whenever a color is selected in the panel. @param anAction the action that will be sent */ - (void)setAction:(selector)anAction { _action = anAction; } /*! Returns the current target action. */ - (selector)action { return _action; } /*! Sets the mode (look) of the color panel. @param mode the mode in which to display the color panel */ - (void)setMode:(CPColorPanelMode)mode { _mode = mode; } - (void)_setPicker:(id)sender { var picker = _colorPickers[[sender tag]], view = [picker provideNewView:NO]; if (!view) view = [picker provideNewView:YES]; if (view == _currentView) return; if (_currentView) [view setFrame:[_currentView frame]]; else { var height = (TOOLBAR_HEIGHT+10+PREVIEW_HEIGHT+5+SWATCH_HEIGHT+32), bounds = [[self contentView] bounds]; [view setFrameSize: CPSizeMake(bounds.size.width - 10, bounds.size.height - height)]; [view setFrameOrigin: CPPointMake(5, height)]; } [_currentView removeFromSuperview]; [[self contentView] addSubview:view]; _currentView = view; _activePicker = picker; [picker setColor:[self color]]; } /*! Returns the color panel's current display mode. */ - (CPColorPanelMode)mode { return _mode; } - (void)orderFront:(id)aSender { [self _loadContentsIfNecessary]; [super orderFront:aSender]; } /* @ignore */ - (void)_loadContentsIfNecessary { if (_toolbar) return; if (!_color) _color = [CPColor whiteColor]; _colorPickers = []; var count = [ColorPickerClasses count]; for (var i=0; i [self bounds].size.width - 1 || point.x < 1) return NO; [_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES]; } - (void)mouseDragged:(CPEvent)anEvent { var point = [self convertPoint:[anEvent locationInWindow] fromView:nil]; if(point.x > [self bounds].size.width - 1 || point.x < 1) return NO; [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self]; var swatch = _swatches[FLOOR(point.x / 13)]; // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox _dragColor = [[swatch subviews][0] backgroundColor]; var bounds = CPRectCreateCopy([swatch bounds]); // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox var dragView = [[CPView alloc] initWithFrame: bounds]; dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)]; [dragView setBackgroundColor:[CPColor blackColor]]; [dragFillView setBackgroundColor:_dragColor]; [dragView addSubview:dragFillView]; [self dragView: dragView at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) offset: CPPointMake(0.0, 0.0) event: anEvent pasteboard: nil source: self slideBack: YES]; } - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType { if(aType == CPColorDragType) [aPasteboard setData:_dragColor forType:aType]; } - (void)performDragOperation:(id )aSender { var location = [self convertPoint:[aSender draggingLocation] fromView:nil], pasteboard = [aSender draggingPasteboard], swatch = nil; if(![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1) return NO; [self setColor:[pasteboard dataForType:CPColorDragType] atIndex: FLOOR(location.x / 13)]; } @end /* @ignore */ @implementation _CPColorPanelPreview : CPView { CPColorPanel _colorPanel; } - (id)initWithFrame:(CPRect)aFrame { self = [super initWithFrame:aFrame]; [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]]; return self; } - (void)setColorPanel:(CPColorPanel)aPanel { _colorPanel = aPanel; } - (CPColorPanel)colorPanel { return _colorPanel; } - (void)performDragOperation:(id )aSender { var pasteboard = [aSender draggingPasteboard]; if(![pasteboard availableTypeFromArray:[CPColorDragType]]) return NO; var color = [pasteboard dataForType:CPColorDragType]; [_colorPanel setColor: color updatePicker: YES]; } - (BOOL)isOpaque { return YES; } - (void)mouseDragged:(CPEvent)anEvent { var point = [self convertPoint:[anEvent locationInWindow] fromView:nil]; [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self]; var bounds = CPRectMake(0, 0, 15, 15); // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox var dragView = [[CPView alloc] initWithFrame: bounds]; dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)]; [dragView setBackgroundColor:[CPColor blackColor]]; [dragFillView setBackgroundColor:[self backgroundColor]]; [dragView addSubview:dragFillView]; [self dragView: dragView at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0) offset: CPPointMake(0.0, 0.0) event: anEvent pasteboard: nil source: self slideBack: YES]; } - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType { if(aType == CPColorDragType) [aPasteboard setData:[self backgroundColor] forType:aType]; } @end @import "CPColorPicker.j" @import "CPSliderColorPicker.j" [CPColorPanel provideColorPickerClass:CPColorWheelColorPicker]; [CPColorPanel provideColorPickerClass:CPSliderColorPicker];