API 0.9.5
AppKit/CPColorPanel.j
Go to the documentation of this file.
00001 /*
00002  * CPColorPanel.j
00003  * AppKit
00004  *
00005  * Created by Ross Boucher.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 
00025 CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
00026 
00027 var PREVIEW_HEIGHT = 20.0,
00028     TOOLBAR_HEIGHT = 32.0,
00029     SWATCH_HEIGHT  = 14.0,
00030     ICON_WIDTH     = 32.0,
00031     ICON_PADDING   = 12.0;
00032 
00033 var SharedColorPanel = nil,
00034     ColorPickerClasses = [];
00035 
00036 /*
00037     A color wheel
00038     @global
00039     @group CPColorPanelMode
00040 */
00041 CPWheelColorPickerMode = 1;
00042 /*
00043     Slider based picker
00044     @global
00045     @group CPColorPanelMode
00046 */
00047 CPSliderColorPickerMode = 2;
00048 
00049 CPColorPickerViewWidth  = 265;
00050 CPColorPickerViewHeight = 370;
00051 
00060 @implementation CPColorPanel : CPPanel
00061 {
00062     _CPColorPanelToolbar    _toolbar;
00063     _CPColorPanelSwatches   _swatchView;
00064     _CPColorPanelPreview    _previewView;
00065 
00066     CPSlider        _opacitySlider;
00067 
00068     CPArray         _colorPickers;
00069     CPView          _currentView;
00070     id              _activePicker;
00071 
00072     CPColor         _color;
00073 
00074     id              _target;
00075     SEL             _action;
00076 
00077     int             _mode;
00078 }
00079 
00084 + (void)provideColorPickerClass:(Class)aColorPickerSubclass
00085 {
00086     ColorPickerClasses.push(aColorPickerSubclass);
00087 }
00088 
00092 + (CPColorPanel)sharedColorPanel
00093 {
00094     if (!SharedColorPanel)
00095         SharedColorPanel = [[CPColorPanel alloc] init];
00096 
00097     return SharedColorPanel;
00098 }
00099 
00104 + (void)setPickerMode:(CPColorPanelMode)mode
00105 {
00106     var panel = [CPColorPanel sharedColorPanel];
00107     [panel setMode:mode];
00108 }
00109 
00110 /*
00111     To obtain the color panel, use \c +sharedColorPanel.
00112     @ignore
00113 */
00114 - (id)init
00115 {
00116     self = [super initWithContentRect:CGRectMake(500.0, 50.0, 219.0, 370.0)
00117                             styleMask:(CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)];
00118 
00119     if (self)
00120     {
00121         [[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]];
00122 
00123         [self setTitle:@"Color Panel"];
00124         [self setLevel:CPFloatingWindowLevel];
00125 
00126         [self setFloatingPanel:YES];
00127         [self setBecomesKeyOnlyIfNeeded:YES];
00128 
00129         [self setMinSize:CGSizeMake(219.0, 363.0)];
00130         [self setMaxSize:CGSizeMake(323.0, 537.0)];
00131     }
00132 
00133     return self;
00134 }
00135 
00139 - (void)setColor:(CPColor)aColor
00140 {
00141     _color = aColor;
00142     [_previewView setBackgroundColor: _color];
00143 
00144     [CPApp sendAction:@selector(changeColor:) to:nil from:self];
00145 
00146     if (_target && _action)
00147         [CPApp sendAction:_action to:_target from:self];
00148 
00149     [[CPNotificationCenter defaultCenter]
00150         postNotificationName:CPColorPanelColorDidChangeNotification
00151                       object:self];
00152 
00153     [_activePicker setColor:_color];
00154     [_opacitySlider setFloatValue:[_color alphaComponent]];
00155 }
00156 
00162 - (void)setColor:(CPColor)aColor updatePicker:(BOOL)bool
00163 {
00164     [self setColor:aColor];
00165 
00166     if (bool)
00167         [_activePicker setColor:_color];
00168 }
00169 
00173 - (CPColor)color
00174 {
00175     return _color;
00176 }
00177 
00178 - (float)opacity
00179 {
00180     return [_opacitySlider floatValue];
00181 }
00182 
00187 - (void)setTarget:(id)aTarget
00188 {
00189     _target = aTarget;
00190 }
00191 
00196 - (id)target
00197 {
00198     return _target;
00199 }
00200 
00206 - (void)setAction:(selector)anAction
00207 {
00208     _action = anAction;
00209 }
00210 
00214 - (selector)action
00215 {
00216     return _action;
00217 }
00218 
00223 - (void)setMode:(CPColorPanelMode)mode
00224 {
00225     _mode = mode;
00226 }
00227 
00228 - (void)_setPicker:(id)sender
00229 {
00230     var picker = _colorPickers[[sender tag]],
00231         view = [picker provideNewView:NO];
00232 
00233     if (!view)
00234         view = [picker provideNewView:YES];
00235 
00236     if (view == _currentView)
00237         return;
00238 
00239     if (_currentView)
00240         [view setFrame:[_currentView frame]];
00241     else
00242     {
00243         var height = (TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5 + SWATCH_HEIGHT + 32),
00244             bounds = [[self contentView] bounds];
00245 
00246         [view setFrameSize:CPSizeMake(bounds.size.width - 10, bounds.size.height - height)];
00247         [view setFrameOrigin:CPPointMake(5, height)];
00248     }
00249 
00250     [_currentView removeFromSuperview];
00251     [[self contentView] addSubview:view];
00252 
00253     _currentView = view;
00254     _activePicker = picker;
00255 
00256     [picker setColor:[self color]];
00257 }
00258 
00262 - (CPColorPanelMode)mode
00263 {
00264     return _mode;
00265 }
00266 
00267 - (void)orderFront:(id)aSender
00268 {
00269     [self _loadContentsIfNecessary];
00270     [super orderFront:aSender];
00271 }
00272 
00273 /* @ignore */
00274 - (void)_loadContentsIfNecessary
00275 {
00276     if (_toolbar)
00277         return;
00278 
00279     if (!_color)
00280         _color = [CPColor whiteColor];
00281 
00282     _colorPickers = [];
00283 
00284     var count = [ColorPickerClasses count];
00285     for (var i = 0; i < count; i++)
00286     {
00287         var currentPickerClass = ColorPickerClasses[i],
00288             currentPicker = [[currentPickerClass alloc] initWithPickerMask:0 colorPanel:self];
00289 
00290         _colorPickers.push(currentPicker);
00291     }
00292 
00293     var contentView = [self contentView],
00294         bounds = [contentView bounds];
00295 
00296     _toolbar = [[CPView alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)];
00297     [_toolbar setAutoresizingMask: CPViewWidthSizable];
00298 
00299     var totalToolbarWidth = count * ICON_WIDTH + (count - 1) * ICON_PADDING,
00300         leftOffset = (CGRectGetWidth(bounds) - totalToolbarWidth) / 2.0,
00301         buttonForLater = nil;
00302 
00303     for (var i = 0; i < count; i++)
00304     {
00305         var image = [_colorPickers[i] provideNewButtonImage],
00306             highlightImage = [_colorPickers[i] provideNewAlternateButtonImage],
00307             button = [[CPButton alloc] initWithFrame:CGRectMake(leftOffset + i * (ICON_WIDTH + ICON_PADDING), 0, ICON_WIDTH, ICON_WIDTH)];
00308 
00309         [button setTag:i];
00310         [button setTarget:self];
00311         [button setAction:@selector(_setPicker:)];
00312         [button setBordered:NO];
00313         [button setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
00314 
00315         [button setImage:image];
00316         [button setAlternateImage:highlightImage];
00317 
00318         [_toolbar addSubview:button];
00319 
00320         if (!buttonForLater)
00321             buttonForLater = button;
00322     }
00323 
00324     // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
00325     var previewBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10, CGRectGetWidth(bounds) - 86, PREVIEW_HEIGHT)];
00326 
00327     _previewView = [[_CPColorPanelPreview alloc] initWithFrame:CGRectInset([previewBox bounds], 2.0, 2.0)];
00328 
00329     [_previewView setColorPanel:self];
00330     [_previewView setAutoresizingMask:CPViewWidthSizable];
00331 
00332     [previewBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]];
00333     [previewBox setAutoresizingMask:CPViewWidthSizable];
00334 
00335     [previewBox addSubview:_previewView];
00336 
00337     var _previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 10, 60, 15)];
00338     [_previewLabel setStringValue:"Preview:"];
00339     [_previewLabel setTextColor:[CPColor blackColor]];
00340     [_previewLabel setAlignment:CPRightTextAlignment];
00341 
00342     // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
00343     var swatchBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5, CGRectGetWidth(bounds) - 86, SWATCH_HEIGHT + 2.0)];
00344 
00345     [swatchBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]];
00346     [swatchBox setAutoresizingMask:CPViewWidthSizable];
00347 
00348     _swatchView = [[_CPColorPanelSwatches alloc] initWithFrame:CGRectInset([swatchBox bounds], 1.0, 1.0)];
00349 
00350     [_swatchView setColorPanel:self];
00351     [_swatchView setAutoresizingMask:CPViewWidthSizable];
00352 
00353     [swatchBox addSubview:_swatchView];
00354 
00355     var _swatchLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 6, 60, 15)];
00356     [_swatchLabel setStringValue: "Swatches:"];
00357     [_swatchLabel setTextColor:[CPColor blackColor]];
00358     [_swatchLabel setAlignment:CPRightTextAlignment];
00359 
00360 
00361     var opacityLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 35, 60, 20)];
00362     [opacityLabel setStringValue: "Opacity:"];
00363     [opacityLabel setTextColor:[CPColor blackColor]];
00364     [opacityLabel setAlignment:CPRightTextAlignment];
00365 
00366     _opacitySlider = [[CPSlider alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 34, CGRectGetWidth(bounds) - 86, 20.0)];
00367 
00368     [_opacitySlider setMinValue:0.0];
00369     [_opacitySlider setMaxValue:1.0];
00370     [_opacitySlider setAutoresizingMask:CPViewWidthSizable];
00371 
00372     [_opacitySlider setTarget:self];
00373     [_opacitySlider setAction:@selector(setOpacity:)];
00374 
00375     [contentView addSubview:_toolbar];
00376     [contentView addSubview:previewBox];
00377     [contentView addSubview:_previewLabel];
00378     [contentView addSubview:swatchBox];
00379     [contentView addSubview:_swatchLabel];
00380     [contentView addSubview:opacityLabel];
00381     [contentView addSubview:_opacitySlider];
00382 
00383     _target = nil;
00384     _action = nil;
00385     _activePicker = nil;
00386 
00387     [_previewView setBackgroundColor: _color];
00388 
00389     if (buttonForLater)
00390         [self _setPicker:buttonForLater];
00391 }
00392 
00393 - (void)setOpacity:(id)sender
00394 {
00395     var components = [[self color] components],
00396         alpha = [sender floatValue];
00397 
00398     [self setColor:[_color colorWithAlphaComponent:alpha] updatePicker:YES];
00399 }
00400 
00401 @end
00402 
00403 
00404 CPColorDragType = "CPColorDragType";
00405 var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
00406 
00407 /* @ignore */
00408 @implementation _CPColorPanelSwatches : CPView
00409 {
00410     CPView[]        _swatches;
00411     CPColor         _dragColor;
00412     CPColorPanel    _colorPanel;
00413     CPCookie        _swatchCookie;
00414 }
00415 
00416 - (id)initWithFrame:(CPRect)aFrame
00417 {
00418     self = [super initWithFrame:aFrame];
00419 
00420     [self setBackgroundColor: [CPColor grayColor]];
00421 
00422     [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
00423 
00424     var whiteColor = [CPColor whiteColor];
00425 
00426     _swatchCookie = [[CPCookie alloc] initWithName: CPColorPanelSwatchesCookie];
00427     var colorList = [self startingColorList];
00428 
00429     _swatches = [];
00430 
00431     for (var i = 0; i < 50; i++)
00432     {
00433         // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
00434         var view = [[CPView alloc] initWithFrame: CPRectMake(13 * i + 1, 1, 12, 12)],
00435             fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)];
00436 
00437         [view setBackgroundColor:whiteColor];
00438         [fillView setBackgroundColor: (i < colorList.length) ? colorList[i] : whiteColor];
00439 
00440         [view addSubview:fillView];
00441 
00442         [self addSubview: view];
00443 
00444         _swatches.push(view);
00445     }
00446 
00447     return self;
00448 }
00449 
00450 - (BOOL)isOpaque
00451 {
00452     return YES;
00453 }
00454 
00455 - (CPArray)startingColorList
00456 {
00457     var cookieValue = [_swatchCookie value];
00458 
00459     if (cookieValue == "")
00460     {
00461         return [
00462             [CPColor blackColor],
00463             [CPColor darkGrayColor],
00464             [CPColor grayColor],
00465             [CPColor lightGrayColor],
00466             [CPColor whiteColor],
00467             [CPColor redColor],
00468             [CPColor greenColor],
00469             [CPColor blueColor],
00470             [CPColor yellowColor]
00471         ];
00472     }
00473 
00474     var cookieValue = eval(cookieValue),
00475         result = [];
00476 
00477     for (var i = 0; i < cookieValue.length; i++)
00478         result.push([CPColor colorWithHexString: cookieValue[i]]);
00479 
00480     return result;
00481 }
00482 
00483 - (CPArray)saveColorList
00484 {
00485     var result = [];
00486     // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
00487     for (var i = 0; i < _swatches.length; i++)
00488         result.push([[[_swatches[i] subviews][0] backgroundColor] hexString]);
00489 
00490     var future = new Date();
00491     future.setYear(2019);
00492 
00493     [_swatchCookie setValue: JSON.stringify(result) expires:future domain: nil];
00494 }
00495 
00496 - (void)setColorPanel:(CPColorPanel)panel
00497 {
00498     _colorPanel = panel;
00499 }
00500 
00501 - (CPColorPanel)colorPanel
00502 {
00503     return _colorPanel;
00504 }
00505 
00506 - (CPColor)colorAtIndex:(int)index
00507 {
00508     return [[_swatches[index] subviews][0] backgroundColor];
00509 }
00510 
00511 - (void)setColor:(CPColor)aColor atIndex:(int)index
00512 {
00513     // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
00514     [[_swatches[index] subviews][0] setBackgroundColor:aColor];
00515     [self saveColorList];
00516 }
00517 
00518 - (void)mouseUp:(CPEvent)anEvent
00519 {
00520     var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
00521         bounds = [self bounds];
00522 
00523     if (!CGRectContainsPoint(bounds, point) || point.x > [self bounds].size.width - 1 || point.x < 1)
00524         return NO;
00525 
00526     [_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES];
00527 }
00528 
00529 - (void)mouseDragged:(CPEvent)anEvent
00530 {
00531     var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
00532 
00533      if (point.x > [self bounds].size.width - 1 || point.x < 1)
00534         return NO;
00535 
00536     [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
00537 
00538     var swatch = _swatches[FLOOR(point.x / 13)];
00539 
00540     // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
00541     _dragColor = [[swatch subviews][0] backgroundColor];
00542 
00543     var bounds = CPRectCreateCopy([swatch bounds]);
00544 
00545     // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
00546     var dragView = [[CPView alloc] initWithFrame: bounds],
00547         dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
00548 
00549     [dragView setBackgroundColor:[CPColor blackColor]];
00550     [dragFillView setBackgroundColor:_dragColor];
00551 
00552     [dragView addSubview:dragFillView];
00553 
00554     [self dragView: dragView
00555                 at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
00556             offset: CPPointMake(0.0, 0.0)
00557              event: anEvent
00558         pasteboard: nil
00559             source: self
00560          slideBack: YES];
00561 }
00562 
00563 - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
00564 {
00565     if (aType == CPColorDragType)
00566         [aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType];
00567 }
00568 
00569 - (void)performDragOperation:(id <CPDraggingInfo>)aSender
00570 {
00571     var location = [self convertPoint:[aSender draggingLocation] fromView:nil],
00572         pasteboard = [aSender draggingPasteboard],
00573         swatch = nil;
00574 
00575     if (![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1)
00576         return NO;
00577 
00578     [self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex: FLOOR(location.x / 13)];
00579 }
00580 
00581 @end
00582 
00583 /* @ignore */
00584 @implementation _CPColorPanelPreview : CPView
00585 {
00586     CPColorPanel    _colorPanel;
00587 }
00588 
00589 - (id)initWithFrame:(CPRect)aFrame
00590 {
00591     self = [super initWithFrame:aFrame];
00592 
00593     [self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
00594 
00595     return self;
00596 }
00597 
00598 - (void)setColorPanel:(CPColorPanel)aPanel
00599 {
00600     _colorPanel = aPanel;
00601 }
00602 
00603 - (CPColorPanel)colorPanel
00604 {
00605     return _colorPanel;
00606 }
00607 
00608 - (void)performDragOperation:(id <CPDraggingInfo>)aSender
00609 {
00610     var pasteboard = [aSender draggingPasteboard];
00611 
00612     if (![pasteboard availableTypeFromArray:[CPColorDragType]])
00613         return NO;
00614 
00615     var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]];
00616     [_colorPanel setColor:color updatePicker:YES];
00617 }
00618 
00619 - (BOOL)isOpaque
00620 {
00621     return YES;
00622 }
00623 
00624 - (void)mouseDragged:(CPEvent)anEvent
00625 {
00626     var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
00627 
00628     [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
00629 
00630     var bounds = CPRectMake(0, 0, 15, 15);
00631 
00632     // FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
00633     var dragView = [[CPView alloc] initWithFrame: bounds],
00634         dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
00635 
00636     [dragView setBackgroundColor:[CPColor blackColor]];
00637     [dragFillView setBackgroundColor:[self backgroundColor]];
00638 
00639     [dragView addSubview:dragFillView];
00640 
00641     [self dragView: dragView
00642                 at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
00643             offset: CPPointMake(0.0, 0.0)
00644              event: anEvent
00645         pasteboard: nil
00646             source: self
00647          slideBack: YES];
00648 }
00649 
00650 - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
00651 {
00652     if (aType == CPColorDragType)
00653         [aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:aType];
00654 }
00655 
00656 @end
00657 
00658 
00659 [CPColorPanel provideColorPickerClass:CPColorWheelColorPicker];
00660 [CPColorPanel provideColorPickerClass:CPSliderColorPicker];
 All Classes Files Functions Variables Defines