API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPColorWell.j
Go to the documentation of this file.
1 /*
2  * CPColorWell.j
3  * AppKit
4  *
5  * Created by Ross Boucher.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
26 var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiveNotification";
27 
36 @implementation CPColorWell : CPControl
37 {
38  BOOL _active;
39  BOOL _bordered;
40 
41  CPColor _color;
42 }
43 
44 + (Class)_binderClassForBinding:(CPString)theBinding
45 {
46  if (theBinding == CPValueBinding)
48 
49  return [super _binderClassForBinding:theBinding];
50 }
51 
52 + (CPString)defaultThemeClass
53 {
54  return @"colorwell";
55 }
56 
57 + (id)themeAttributes
58 {
59  return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), [CPNull null], _CGInsetMake(3.0, 3.0, 3.0, 3.0), _CGInsetMakeZero(), [CPNull null]]
60  forKeys:[@"bezel-inset", @"bezel-color", @"content-inset", @"content-border-inset", @"content-border-color"]];
61 }
62 
63 - (void)_reverseSetBinding
64 {
65  var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
66  theBinding = [binderClass getBinding:CPValueBinding forObject:self];
67 
68  [theBinding reverseSetValueFor:@"color"];
69 }
70 
71 - (id)initWithFrame:(CGRect)aFrame
72 {
73  self = [super initWithFrame:aFrame];
74 
75  if (self)
76  {
77  _active = NO;
78  _color = [CPColor whiteColor];
79  [self setBordered:YES];
80 
81  [self _registerForNotifications];
82  }
83 
84  return self;
85 }
86 
87 - (void)_registerForNotifications
88 {
89  var defaultCenter = [CPNotificationCenter defaultCenter];
90 
91  [defaultCenter
92  addObserver:self
93  selector:@selector(colorWellDidBecomeExclusive:)
94  name:_CPColorWellDidBecomeExclusiveNotification
95  object:nil];
96 
97  [defaultCenter
98  addObserver:self
99  selector:@selector(colorPanelWillClose:)
100  name:CPWindowWillCloseNotification
101  object:[CPColorPanel sharedColorPanel]];
102 }
103 
107 - (void)setBordered:(BOOL)shouldBeBordered
108 {
109  if (shouldBeBordered)
110  [self setThemeState:CPThemeStateBordered];
111  else
112  [self unsetThemeState:CPThemeStateBordered];
113 }
114 
118 - (BOOL)isBordered
119 {
120  return [self hasThemeState:CPThemeStateBordered];
121 }
122 
123 // Managing Color From Color Wells
124 
128 - (CPColor)color
129 {
130  return _color;
131 }
132 
136 - (void)setColor:(CPColor)aColor
137 {
138  if (_color == aColor)
139  return;
140 
141  _color = aColor;
142 
143  [self setNeedsLayout];
144 }
145 
150 - (void)takeColorFrom:(id)aSender
151 {
152  [self setColor:[aSender color]];
153 }
154 
155 // Activating and Deactivating Color Wells
161 - (void)activate:(BOOL)shouldBeExclusive
162 {
163  if (shouldBeExclusive)
164  // FIXME: make this queue!
166  postNotificationName:_CPColorWellDidBecomeExclusiveNotification
167  object:self];
168 
169 
170  if ([self isActive])
171  return;
172 
173  _active = YES;
174 
176  addObserver:self
178  name:CPColorPanelColorDidChangeNotification
180 }
181 
185 - (void)deactivate
186 {
187  if (![self isActive])
188  return;
189 
190  _active = NO;
191 
193  removeObserver:self
194  name:CPColorPanelColorDidChangeNotification
196 }
197 
201 - (BOOL)isActive
202 {
203  return _active;
204 }
205 
206 - (void)colorPanelDidChangeColor:(CPNotification)aNotification
207 {
208  [self takeColorFrom:[aNotification object]];
209 
210  [self sendAction:[self action] to:[self target]];
211 }
212 
213 - (void)colorWellDidBecomeExclusive:(CPNotification)aNotification
214 {
215  if (self != [aNotification object])
216  [self deactivate];
217 }
218 
219 - (void)colorPanelWillClose:(CPNotification)aNotification
220 {
221  [self deactivate];
222 }
223 
224 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
225 {
226  [self highlight:NO];
227 
228  if (!mouseIsUp || !CGRectContainsPoint([self bounds], aPoint) || ![self isEnabled])
229  return;
230 
231  [self activate:YES];
232 
233  var colorPanel = [CPColorPanel sharedColorPanel];
234 
235  [colorPanel setColor:_color];
236  [colorPanel orderFront:self];
237 }
238 
239 - (CGRect)contentRectForBounds:(CGRect)bounds
240 {
241  var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
242 
243  return _CGRectInsetByInset(bounds, contentInset);
244 }
245 
246 - (CGRect)bezelRectForBounds:(CGRect)bounds
247 {
248  var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
249 
250  return _CGRectInsetByInset(bounds, bezelInset);
251 }
252 
253 - (CGRect)contentBorderRectForBounds:(CGRect)bounds
254 {
255  var contentBorderInset = [self currentValueForThemeAttribute:@"content-border-inset"];
256 
257  return _CGRectInsetByInset(bounds, contentBorderInset);
258 }
259 
260 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
261 {
262  switch (aName)
263  {
264  case "bezel-view":
265  return [self bezelRectForBounds:[self bounds]];
266  case "content-view":
267  return [self contentRectForBounds:[self bounds]];
268  case "content-border-view":
269  return [self contentBorderRectForBounds:[self bounds]];
270  }
271 
272  return [super rectForEphemeralSubviewNamed:aName];
273 }
274 
275 - (CPView)createEphemeralSubviewNamed:(CPString)aName
276 {
277  var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
278 
279  [view setHitTests:NO];
280 
281  return view;
282 }
283 
284 - (void)layoutSubviews
285 {
286  var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
287  positioned:CPWindowBelow
288  relativeToEphemeralSubviewNamed:@"content-view"];
289 
290  [bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
291 
292  var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
293  positioned:CPWindowAbove
294  relativeToEphemeralSubviewNamed:@"bezel-view"];
295 
296 
297  [contentView setBackgroundColor:_color];
298 
299  var contentBorderView = [self layoutEphemeralSubviewNamed:@"content-border-view"
300  positioned:CPWindowAbove
301  relativeToEphemeralSubviewNamed:@"content-view"];
302 
303  [contentBorderView setBackgroundColor:[self currentValueForThemeAttribute:@"content-border-color"]];
304 }
305 
306 @end
308 {
309  id __doxygen__;
310 }
311 
312 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options
313 {
314  var placeholderColor = [CPColor blueColor];
315 
316  [self _setPlaceholder:placeholderColor forMarker:CPMultipleValuesMarker isDefault:YES];
317  [self _setPlaceholder:placeholderColor forMarker:CPNoSelectionMarker isDefault:YES];
318  [self _setPlaceholder:placeholderColor forMarker:CPNotApplicableMarker isDefault:YES];
319  [self _setPlaceholder:placeholderColor forMarker:CPNullMarker isDefault:YES];
320 }
321 
322 - (void)setValueFor:(CPString)theBinding
323 {
324  var destination = [_info objectForKey:CPObservedObjectKey],
325  keyPath = [_info objectForKey:CPObservedKeyPathKey],
326  options = [_info objectForKey:CPOptionsKey],
327  newValue = [destination valueForKeyPath:keyPath],
328  isPlaceholder = CPIsControllerMarker(newValue);
329 
330  if (isPlaceholder)
331  {
332  if (newValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
333  {
334  [CPException raise:CPGenericException
335  reason:@"can't transform non applicable key on: " + _source + " value: " + newValue];
336  }
337 
338  newValue = [self _placeholderForMarker:newValue];
339  }
340  else
341  {
342  newValue = [self transformValue:newValue withOptions:options];
343  }
344 
345  [_source setColor:newValue];
346 }
347 
348 @end
349 
350 var CPColorWellColorKey = "CPColorWellColorKey",
351  CPColorWellBorderedKey = "CPColorWellBorderedKey";
352 
353 @implementation CPColorWell (CPCoding)
354 
359 - (id)initWithCoder:(CPCoder)aCoder
360 {
361  self = [super initWithCoder:aCoder];
362 
363  if (self)
364  {
365  _active = NO;
366  _color = [aCoder decodeObjectForKey:CPColorWellColorKey];
367  [self setBordered:[aCoder decodeBoolForKey:CPColorWellBorderedKey]];
368 
369  [self _registerForNotifications];
370  }
371 
372  return self;
373 }
374 
379 - (void)encodeWithCoder:(CPCoder)aCoder
380 {
381  [super encodeWithCoder:aCoder];
382 
383  [aCoder encodeObject:_color forKey:CPColorWellColorKey];
384  [aCoder encodeObject:[self isBordered] forKey:CPColorWellBorderedKey];
385 }
386 
387 @end