/** NSColorPanel System generic color panel Copyright (C) 1996 Free Software Foundation, Inc. Author: Scott Christley Date: 1996 This file is part of the GNUstep GUI Library. 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 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; see the file COPYING.LIB. If not, see or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #import "config.h" #import #import #import #import #import #import #import "AppKit/NSBox.h" #import "AppKit/NSButton.h" #import "AppKit/NSButtonCell.h" #import "AppKit/NSColor.h" #import "AppKit/NSColorPanel.h" #import "AppKit/NSColorPicker.h" #import "AppKit/NSColorPicking.h" #import "AppKit/NSColorWell.h" #import "AppKit/NSEvent.h" #import "AppKit/NSGraphics.h" #import "AppKit/NSImage.h" #import "AppKit/NSMatrix.h" #import "AppKit/NSPasteboard.h" #import "AppKit/NSPanel.h" #import "AppKit/NSSlider.h" #import "AppKit/NSSplitView.h" #import "AppKit/NSWindow.h" #import "GNUstepGUI/IMLoading.h" #import "GSGuiPrivate.h" #define MAX_ALPHA_VALUE 100.0 static NSLock *_gs_gui_color_panel_lock = nil; static NSColorPanel *_gs_gui_color_panel = nil; static int _gs_gui_color_picker_mask = NSColorPanelAllModesMask; // FIXME: This should be NSWheelModeColorPanel static int _gs_gui_color_picker_mode = NSRGBModeColorPanel; @implementation NSApplication (NSColorPanel) - (void) orderFrontColorPanel: sender { NSColorPanel *colorPanel = [NSColorPanel sharedColorPanel]; if (colorPanel) [colorPanel orderFront: nil]; else NSBeep(); } @end @interface NSColorPanel (PrivateMethods) - (void) _loadPickers; - (void) _loadPickerAtPath: (NSString *)path; - (void) _fixupMatrix; - (void) _setupPickers; - (void) _showNewPicker: (id)sender; - (id) _initWithoutGModel; @end @implementation NSColorPanel (PrivateMethods) - (void) _loadPickers { NSArray *paths; NSString *path; NSEnumerator *pathEnumerator; NSArray *bundles; NSEnumerator *bundleEnumerator; NSString *bundleName; _pickers = [NSMutableArray new]; paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, YES); pathEnumerator = [paths objectEnumerator]; while ((path = [pathEnumerator nextObject])) { path = [path stringByAppendingPathComponent: @"ColorPickers"]; bundles = [[NSFileManager defaultManager] directoryContentsAtPath: path]; bundleEnumerator = [bundles objectEnumerator]; while ((bundleName = [bundleEnumerator nextObject])) { [self _loadPickerAtPath: [path stringByAppendingPathComponent: bundleName]]; } } paths = [[NSBundle mainBundle] pathsForResourcesOfType: @"bundle" inDirectory: @"ColorPickers"]; pathEnumerator = [paths objectEnumerator]; while ((path = [pathEnumerator nextObject])) { [self _loadPickerAtPath: path]; } } - (void) _loadPickerAtPath: (NSString *)path { NSBundle *bundle; Class pickerClass; NSColorPicker *picker; bundle = [NSBundle bundleWithPath: path]; if (bundle && (pickerClass = [bundle principalClass])) { picker = [[pickerClass alloc] initWithPickerMask:_gs_gui_color_picker_mask colorPanel: self]; if (picker && [picker conformsToProtocol:@protocol(NSColorPickingCustom)]) { [(id)picker provideNewView: YES]; [_pickers addObject: picker]; } else { NSLog(@"%@ does not contain a valid color picker.", path); } } } // FIXME - this is a HACK to get around problems in the gmodel code - (void) _fixupMatrix { NSButtonCell *prototype; [_pickerMatrix setFrame: NSMakeRect(4, 190, 192, 36)]; prototype = [[NSButtonCell alloc] initImageCell: nil]; [prototype setButtonType: NSOnOffButton]; [_pickerMatrix setPrototype: prototype]; RELEASE(prototype); } - (void) _setupPickers { id picker; NSButtonCell *cell; NSMutableArray *cells = [NSMutableArray new]; int i, count; NSSize size = [_pickerMatrix frame].size; count = [_pickers count]; for (i = 0; i < count; i++) { cell = [[_pickerMatrix prototype] copy]; [cell setTag: i]; picker = [_pickers objectAtIndex: i]; [picker insertNewButtonImage: [picker provideNewButtonImage] in: cell]; [cells addObject: cell]; } [_pickerMatrix addRowWithCells: cells]; RELEASE(cells); [_pickerMatrix setCellSize: NSMakeSize(size.width / count, size.height)]; [_pickerMatrix setTarget: self]; [_pickerMatrix setAction: @selector(_showNewPicker:)]; // use the space occupied by the matrix of color picker buttons if the // button matrix is useless, i.e. it contains only one button if (count < 2) { [_pickerBox setFrame: NSUnionRect([_pickerBox frame], [_pickerMatrix frame])]; [_pickerBox setNeedsDisplay: YES]; // Display the only picker if (count == 1) [self _showNewPicker: _pickerMatrix]; } } - (void) _showNewPicker: (id)sender { _currentPicker = [_pickers objectAtIndex: [sender selectedColumn]]; [_currentPicker setColor: [_colorWell color]]; [_pickerBox setContentView: [_currentPicker provideNewView: NO]]; } - (id) _initWithoutGModel { NSRect contentRect = {{352, 519}, {200, 270}}; NSSize maxContentSize = {500, 675}; NSRect topViewRect = {{0, 0}, {200, 270}}; NSRect magnifyRect = {{4, 230}, {50, 36}}; NSRect wellRect = {{58, 230}, {138, 36}}; NSRect matrixRect = {{4, 190}, {192, 36}}; NSRect splitRect = {{0, 0}, {200, 190}}; NSRect pickerViewRect = {{0, 40}, {200, 150}}; NSRect pickerRect = {{0, 20}, {200, 130}}; NSRect alphaRect = {{4, 4}, {160, 16}}; NSRect swatchRect = {{4, 4}, {200, 30}}; NSView *v; NSButtonCell *pickerButton; NSView *pickerView; NSView *swatchView; NSColorWell *well; int i; unsigned int style = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSUtilityWindowMask; self = [super initWithContentRect: contentRect styleMask: style backing: NSBackingStoreRetained defer: NO screen: nil]; if (nil == self) return nil; [self setTitle: _(@"Colors")]; [self setBecomesKeyOnlyIfNeeded: YES]; [self setContentMinSize: contentRect.size]; [self setContentMaxSize: maxContentSize]; v = [self contentView]; _topView = [[NSView alloc] initWithFrame: topViewRect]; [_topView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; [v addSubview: _topView]; RELEASE(_topView); _magnifyButton = [[NSButton alloc] initWithFrame: magnifyRect]; [_magnifyButton setAutoresizingMask: (NSViewMaxXMargin | NSViewMinYMargin)]; [_magnifyButton setImage: [NSImage imageNamed: @"MagnifyGlass"]]; [_magnifyButton setBordered: YES]; [_magnifyButton setAction: @selector(_magnify:)]; [_magnifyButton setTarget: self]; [_topView addSubview: _magnifyButton]; _colorWell = [[NSColorWell alloc] initWithFrame: wellRect]; [_colorWell setAutoresizingMask: (NSViewWidthSizable | NSViewMinYMargin)]; [_colorWell setBordered: NO]; [_colorWell setTarget: self]; [_colorWell setAction: @selector(_updatePicker:)]; [_topView addSubview: _colorWell]; // Prototype cell for the matrix pickerButton = [[NSButtonCell alloc] initImageCell: nil]; [pickerButton setButtonType: NSOnOffButton]; [pickerButton setBordered: YES]; _pickerMatrix = [[NSMatrix alloc] initWithFrame: matrixRect mode: NSRadioModeMatrix prototype: pickerButton numberOfRows: 0 numberOfColumns: 0]; RELEASE(pickerButton); [_pickerMatrix setAutoresizingMask: (NSViewWidthSizable | NSViewMinYMargin)]; [_pickerMatrix setCellSize: matrixRect.size]; [_pickerMatrix setIntercellSpacing: NSMakeSize(1, 0)]; [_pickerMatrix setAutosizesCells: YES]; [_topView addSubview: _pickerMatrix]; _splitView = [[NSSplitView alloc] initWithFrame: splitRect]; [_splitView setVertical: NO]; [_splitView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; [_topView addSubview: _splitView]; pickerView = [[NSView alloc] initWithFrame: pickerViewRect]; [pickerView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; _pickerBox = [[NSBox alloc] initWithFrame: pickerRect]; [_pickerBox setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; [_pickerBox setBorderType: NSNoBorder]; [_pickerBox setTitle: @""]; [_pickerBox setTitlePosition: NSNoTitle]; [pickerView addSubview: _pickerBox]; _alphaSlider = [[NSSlider alloc] initWithFrame: alphaRect]; [_alphaSlider setAutoresizingMask: (NSViewWidthSizable | NSViewMaxYMargin)]; [_alphaSlider setMinValue: 0.0]; [_alphaSlider setMaxValue: MAX_ALPHA_VALUE]; [_alphaSlider setFloatValue: MAX_ALPHA_VALUE]; [_alphaSlider setContinuous: YES]; [_alphaSlider setTitle: _(@"Opacity")]; [[_alphaSlider cell] setBezeled: YES]; [_alphaSlider setTarget: self]; [_alphaSlider setAction: @selector(_alphaChanged:)]; [pickerView addSubview: _alphaSlider]; _showsAlpha = YES; swatchView = [[NSView alloc] initWithFrame: swatchRect]; [swatchView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; // Add all the subviews at the end [_splitView addSubview: pickerView]; [_splitView addSubview: swatchView]; RELEASE(pickerView); RELEASE(swatchView); // FIXME: This should be loaded form somewhere. // Perhaps a colour list called "custom"? for (i = 0; i < 14; i++) { NSColor *colour; switch (i) { default: case 0: colour = [NSColor greenColor]; break; case 1: colour = [NSColor whiteColor]; break; case 2: colour = [NSColor blackColor]; break; case 3: colour = [NSColor blueColor]; break; case 4: colour = [NSColor brownColor]; break; case 5: colour = [NSColor cyanColor]; break; case 6: colour = [NSColor darkGrayColor]; break; case 7: colour = [NSColor grayColor]; break; case 8: colour = [NSColor lightGrayColor]; break; case 9: colour = [NSColor magentaColor]; break; case 10: colour = [NSColor orangeColor]; break; case 11: colour = [NSColor purpleColor]; break; case 12: colour = [NSColor redColor]; break; case 13: colour = [NSColor yellowColor]; break; } well = [[NSColorWell alloc] initWithFrame: NSMakeRect(i * 13 + 5, 5, 12, 12)]; [well setColor: colour]; [well setBordered: NO]; [well setEnabled: YES]; [well setTarget: _colorWell]; [well setAction: @selector(_bottomWellAction:)]; [swatchView addSubview: well]; RELEASE(well); } return self; } - (void) _alphaChanged: (id) sender { [self setColor: [[self color] colorWithAlphaComponent: [self alpha]]]; } - (void) _apply: (id) sender { // This is currently not used [NSApp sendAction: @selector(changeColor:) to: nil from: self]; if ((_action) && (_target != nil)) [NSApp sendAction: _action to: _target from: self]; } - (void) _maginify: (id) sender { NSLog(@"Magnification is not implemented"); } - (void) _updatePicker: (id) sender { [self setColor: [_colorWell color]]; } - (void) _bottomWellAction: (id) sender { [self setColor: [sender color]]; } @end /**

TODO Description

*/ @implementation NSColorPanel /* * Class methods */ + (void) initialize { if (self == [NSColorPanel class]) { // Initial version [self setVersion: 1]; _gs_gui_color_panel_lock = [NSLock new]; } } /**

Creates ( if needed ) and returns the shared NSColorPanel.

*/ + (NSColorPanel *)sharedColorPanel { if (_gs_gui_color_panel == nil) { [_gs_gui_color_panel_lock lock]; if (!_gs_gui_color_panel) { // Keep this two lines separated so the check in [init] works. _gs_gui_color_panel = [self alloc]; [_gs_gui_color_panel init]; } [_gs_gui_color_panel_lock unlock]; } return _gs_gui_color_panel; } /**

Returns whether the NSColorPanel has been already created.

*/ + (BOOL) sharedColorPanelExists { return (_gs_gui_color_panel == nil) ? NO : YES; } + (void) setPickerMask: (int)mask { _gs_gui_color_picker_mask = mask; } /** */ + (void) setPickerMode: (int)mode { _gs_gui_color_picker_mode = mode; } /**

Drags aColor frome sourceView at the location give by the event anEvent ( [NSView-convertPoint:fromView:] ). The type declare into the pasteboard is NSColorPboardType

See Also: [NSView-convertPoint:fromView:] [NSView-dragImage:at:offset:event:pasteboard:source:slideBack:

*/ + (BOOL) dragColor: (NSColor *)aColor withEvent: (NSEvent *)anEvent fromView: (NSView *)sourceView { NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSDragPboard]; NSImage *image = [NSImage imageNamed: @"common_ColorSwatch"]; NSSize size; NSPoint point; [pb declareTypes: [NSArray arrayWithObjects: NSColorPboardType, nil] owner: aColor]; [aColor writeToPasteboard: pb]; [image setBackgroundColor: aColor]; size = [image size]; point = [sourceView convertPoint: [anEvent locationInWindow] fromView: nil]; point.x -= size.width/2; point.y -= size.width/2; [sourceView dragImage: image at: point offset: NSMakeSize(0,0) event: anEvent pasteboard: pb source: sourceView slideBack: NO]; return YES; } /* * Instance methods */ - (id) init { if (self != _gs_gui_color_panel) { RELEASE(self); return _gs_gui_color_panel; } // if (![NSBundle loadNibNamed: @"ColorPanel" owner: self]); [self _initWithoutGModel]; [self _loadPickers]; [self _setupPickers]; [self setMode: _gs_gui_color_picker_mode]; [self setShowsAlpha: ![NSColor ignoresAlpha]]; return self; } - (void) dealloc { // As there is only one this will never be called RELEASE(_topView); RELEASE(_colorWell); RELEASE(_magnifyButton); RELEASE(_pickerMatrix); RELEASE(_pickerBox); RELEASE(_alphaSlider); RELEASE(_splitView); RELEASE(_pickers); [super dealloc]; } /**

Returns the NSColorPanel's accessory view if it exists, nil otherwise.

See Also: -setAccessoryView:

*/ - (NSView *) accessoryView { return _accessoryView; } /**

Returns whether the NSColorPanel continuously sends its action message.

See Also: -setContinuous:-setAction: -setTarget:

*/ - (BOOL) isContinuous { return _isContinuous; } /**

Returns the current mode of the NSColorPanel.

See Also: -setMode:

*/ - (int) mode { if (_currentPicker != nil) return [_currentPicker currentMode]; else return 0; } /**

Sets the accessoryView to a view. The old view ( if exists ) will be remove ( and release ). You need to retain it if you want to use it later

See Also: -accessoryView

*/ - (void) setAccessoryView: (NSView *)aView { if (_accessoryView == aView) return; if (_accessoryView != nil) [_accessoryView removeFromSuperview]; _accessoryView = aView; [_splitView addSubview: _accessoryView]; } /**

Sets the NSColorPanl action method to aSelector The action message is usally send in -setColor:, when the picker is updated, when a new picker is show, when the alpha is changed or when one of the color well at the bottom is selected.

*/ - (void) setAction: (SEL)aSelector { _action = aSelector; } /**

Sets whether the NSColorPanel sends continuously action messages

See Also: -isContinuous

*/ - (void) setContinuous: (BOOL)flag { _isContinuous = flag; } /**

Set the NSColorPanel mode to mode.

See Also: -mode

*/ - (void) setMode: (int)mode { int i, count; if (mode == [self mode]) return; count = [_pickers count]; for (i = 0; i < count; i++) { if ([[_pickers objectAtIndex: i] supportsMode: mode]) { [_pickerMatrix selectCellWithTag: i]; [self _showNewPicker: _pickerMatrix]; [_currentPicker setMode: mode]; break; } } } /**

Sets whether the NSColorPanel shows alpha values and the alpha slider.

See Also: -showsAlpha

*/ - (void) setShowsAlpha: (BOOL)flag { if (flag == _showsAlpha) return; if (flag) { NSRect newFrame = [_pickerBox frame]; float offset = [_alphaSlider frame].size.height + 4; [_alphaSlider setFrameOrigin: newFrame.origin]; [[_pickerBox superview] addSubview: _alphaSlider]; newFrame.origin.y += offset; newFrame.size.height -= offset; [_pickerBox setFrame: newFrame]; } else { // Remove the alpha slider, and add its size to the pickeBox [_alphaSlider removeFromSuperview]; [_pickerBox setFrame: NSUnionRect([_pickerBox frame], [_alphaSlider frame])]; } _showsAlpha = flag; [_pickers makeObjectsPerformSelector: @selector(alphaControlAddedOrRemoved:) withObject: self]; [_topView setNeedsDisplay: YES]; } /**

Sets the target object to anObject

*/ - (void) setTarget: (id)anObject { _target = anObject; } /**

Returns whether the NSColorPanel shows alpha values and the alpha slider

See Also: -setShowsAlpha:

*/ - (BOOL) showsAlpha { return _showsAlpha; } // // Attaching a Color List // - (void) attachColorList: (NSColorList *)aColorList { [_pickers makeObjectsPerformSelector: @selector(attachColorList:) withObject: aColorList]; } - (void) detachColorList: (NSColorList *)aColorList { [_pickers makeObjectsPerformSelector: @selector(detachColorList:) withObject: aColorList]; } /**

Returns the alpha value of the NSColorPanel. Returns 1.0 if the NSColorPanel does not show alpha

See Also: -showsAlpha -setShowsAlpha:

*/ - (float) alpha { if ([self showsAlpha]) return [_alphaSlider floatValue] / MAX_ALPHA_VALUE; else return 1.0; } /**

Returns the current NSColor displayed by the NSColorPanel.

See Also : -setColor:

*/ - (NSColor *) color { return [_colorWell color]; } /**

Sets the NSColor displayed to aColor. This method post a NSColorPanelColorDidChangeNotification notification if needed.

See Also: -color [NSColorWell-setColor:]

*/ - (void) setColor: (NSColor *)aColor { [_colorWell setColor: aColor]; [_currentPicker setColor: aColor]; if ([self showsAlpha]) [_alphaSlider setFloatValue: [aColor alphaComponent] * MAX_ALPHA_VALUE]; if (_isContinuous && (_action) && (_target != nil)) [NSApp sendAction: _action to: _target from: self]; [[NSNotificationCenter defaultCenter] postNotificationName: NSColorPanelColorDidChangeNotification object: (id)self]; } - (BOOL) worksWhenModal { return YES; } // // NSCoding protocol // - (void) encodeWithCoder: (NSCoder*)aCoder { [super encodeWithCoder: aCoder]; } - (id) initWithCoder: (NSCoder*)aDecoder { self = [super initWithCoder: aDecoder]; if (nil == self) return nil; return self; } @end