API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPLevelIndicator.j
Go to the documentation of this file.
1 /*
2  * CPLevelIndicator.j
3  * AppKit
4  *
5  * Created by Alexander Ljungberg.
6  * Copyright 2011, WireLoad 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 
28 
33 
34 var _CPLevelIndicatorBezelColor = nil,
35  _CPLevelIndicatorSegmentEmptyColor = nil,
36  _CPLevelIndicatorSegmentNormalColor = nil,
37  _CPLevelIndicatorSegmentWarningColor = nil,
38  _CPLevelIndicatorSegmentCriticalColor = nil,
39 
40  _CPLevelIndicatorSpacing = 1;
41 
48 @implementation CPLevelIndicator : CPControl
49 {
50  CPLevelIndicator _levelIndicatorStyle;
51  double _minValue;
52  double _maxValue;
53  double _warningValue;
54  double _criticalValue;
55  CPTickMarkPosition _tickMarkPosition;
56  int _numberOfTickMarks;
57  int _numberOfMajorTickMarks;
58 
59  BOOL _isEditable;
60 
61  BOOL _isTracking;
62 }
63 
64 + (void)initialize
65 {
66  if (self !== [CPLevelIndicator class])
67  return;
68 
69  var bundle = [CPBundle bundleForClass:CPLevelIndicator];
70 
72  [
73  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-left.png"] size:CGSizeMake(3.0, 18.0)],
74  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-center.png"] size:CGSizeMake(1.0, 18.0)],
75  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-right.png"] size:CGSizeMake(3.0, 18.0)]
76  ]
77  isVertical:NO
78  ]];
79 
80  _CPLevelIndicatorSegmentEmptyColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
81  [
82  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-left.png"] size:CGSizeMake(3.0, 17.0)],
83  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-center.png"] size:CGSizeMake(1.0, 17.0)],
84  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-right.png"] size:CGSizeMake(3.0, 17.0)]
85  ]
86  isVertical:NO
87  ]];
88 
89  _CPLevelIndicatorSegmentNormalColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
90  [
91  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-left.png"] size:CGSizeMake(3.0, 17.0)],
92  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-center.png"] size:CGSizeMake(1.0, 17.0)],
93  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-right.png"] size:CGSizeMake(3.0, 17.0)]
94  ]
95  isVertical:NO
96  ]];
97 
98  _CPLevelIndicatorSegmentWarningColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
99  [
100  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-left.png"] size:CGSizeMake(3.0, 17.0)],
101  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-center.png"] size:CGSizeMake(1.0, 17.0)],
102  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-right.png"] size:CGSizeMake(3.0, 17.0)]
103  ]
104  isVertical:NO
105  ]];
106 
107  _CPLevelIndicatorSegmentCriticalColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
108  [
109  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-left.png"] size:CGSizeMake(3.0, 17.0)],
110  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-center.png"] size:CGSizeMake(1.0, 17.0)],
111  [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-right.png"] size:CGSizeMake(3.0, 17.0)]
112  ]
113  isVertical:NO
114  ]];
115 }
116 
117 - (id)initWithFrame:(CGRect)aFrame
118 {
119  self = [super initWithFrame:aFrame];
120 
121  if (self)
122  {
123  _levelIndicatorStyle = CPDiscreteCapacityLevelIndicatorStyle;
124  _maxValue = 2;
125  _warningValue = 2;
126  _criticalValue = 2;
127 
128  [self _init];
129  }
130 
131  return self;
132 }
133 
134 - (void)_init
135 {
136 }
137 
138 - (void)layoutSubviews
139 {
140  var bezelView = [self layoutEphemeralSubviewNamed:"bezel"
141  positioned:CPWindowBelow
143  // TODO Make themable.
144  [bezelView setBackgroundColor:_CPLevelIndicatorBezelColor];
145 
146  var segmentCount = _maxValue - _minValue;
147 
148  if (segmentCount <= 0)
149  return;
150 
151  var filledColor = _CPLevelIndicatorSegmentNormalColor,
152  value = [self doubleValue];
153 
154  if (value <= _criticalValue)
155  filledColor = _CPLevelIndicatorSegmentCriticalColor;
156  else if (value <= _warningValue)
157  filledColor = _CPLevelIndicatorSegmentWarningColor;
158 
159  for (var i = 0; i < segmentCount; i++)
160  {
161  var segmentView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
162  positioned:CPWindowAbove
164 
165  [segmentView setBackgroundColor:(_minValue + i) < value ? filledColor : _CPLevelIndicatorSegmentEmptyColor];
166  }
167 }
168 
169 - (CPView)createEphemeralSubviewNamed:(CPString)aName
170 {
171  return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
172 }
173 
174 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
175 {
176  // TODO Put into theme attributes.
177  var bezelHeight = 18,
178  segmentHeight = 17,
179  bounds = _CGRectCreateCopy([self bounds]);
180 
181  if (aViewName == "bezel")
182  {
183  bounds.origin.y = (_CGRectGetHeight(bounds) - bezelHeight) / 2.0;
184  bounds.size.height = bezelHeight;
185  return bounds;
186  }
187  else if (aViewName.indexOf("segment-bezel") === 0)
188  {
189  var segment = parseInt(aViewName.substring("segment-bezel-".length), 10),
190  segmentCount = _maxValue - _minValue;
191 
192  if (segment >= segmentCount)
193  return _CGRectMakeZero();
194 
195  var basicSegmentWidth = bounds.size.width / segmentCount,
196  segmentFrame = CGRectCreateCopy([self bounds]);
197 
198  segmentFrame.origin.y = (_CGRectGetHeight(bounds) - bezelHeight) / 2.0;
199  segmentFrame.origin.x = FLOOR(segment * basicSegmentWidth);
200  segmentFrame.size.width = (segment == segmentCount - 1) ? bounds.size.width - segmentFrame.origin.x : FLOOR(((segment + 1) * basicSegmentWidth)) - FLOOR((segment * basicSegmentWidth)) - _CPLevelIndicatorSpacing;
201  segmentFrame.size.height = segmentHeight;
202 
203  return segmentFrame;
204  }
205 
206  return _CGRectMakeZero();
207 }
208 
212 - (void)setEditable:(BOOL)shouldBeEditable
213 {
214  if (_isEditable === shouldBeEditable)
215  return;
216 
217  _isEditable = shouldBeEditable;
218 }
219 
223 - (BOOL)isEditable
224 {
225  return _isEditable;
226 }
227 
228 - (CPView)hitTest:(CPPoint)aPoint
229 {
230  // Don't swallow clicks when displayed in a table.
231  if (![self isEditable])
232  return nil;
233 
234  return [super hitTest:aPoint];
235 }
236 
237 - (void)mouseDown:(CPEvent)anEvent
238 {
239  if (![self isEditable] || ![self isEnabled])
240  return;
241 
242  [self _trackMouse:anEvent];
243 }
244 
245 - (void)_trackMouse:(CPEvent)anEvent
246 {
247  var type = [anEvent type];
248 
249  if (type == CPLeftMouseDown || type == CPLeftMouseDragged)
250  {
251  var segmentCount = _maxValue - _minValue;
252 
253  if (segmentCount <= 0)
254  return;
255 
256  var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
257  bounds = [self bounds],
258  oldValue = [self doubleValue],
259  newValue = oldValue;
260 
261  // Moving the mouse outside of the widget to the left sets it
262  // to its minimum, and moving outside on the right sets it to
263  // its maximum.
264  if (type == CPLeftMouseDragged && location.x < 0)
265  {
266  newValue = _minValue;
267  }
268  else if (type == CPLeftMouseDragged && location.x > bounds.size.width)
269  {
270  newValue = _maxValue;
271  }
272  else
273  {
274  for (var i = 0; i < segmentCount; i++)
275  {
276  var rect = [self rectForEphemeralSubviewNamed:"segment-bezel-" + i];
277 
278  // Once we're tracking the mouse, we only care about horizontal mouse movement.
279  if (location.x >= CGRectGetMinX(rect) && location.x < CGRectGetMaxX(rect))
280  {
281  newValue = (_minValue + i + 1);
282  break;
283  }
284  }
285  }
286 
287  if (newValue != oldValue)
288  [self setDoubleValue:newValue];
289 
290  // Track the mouse to support click and slide value editing.
291  _isTracking = YES;
292  [CPApp setTarget:self selector:@selector(_trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
293 
294  if ([self isContinuous])
295  [self sendAction:[self action] to:[self target]];
296  }
297  else if (_isTracking)
298  {
299  _isTracking = NO;
300  [self sendAction:[self action] to:[self target]];
301  }
302 }
303 
304 /*
305 - (CPLevelIndicatorStyle)style;
306 - (void)setLevelIndicatorStyle:(CPLevelIndicatorStyle)style;
307 */
308 
309 - (void)setMinValue:(double)minValue
310 {
311  if (_minValue === minValue)
312  return;
313  _minValue = minValue;
314 
315  [self setNeedsLayout];
316 }
317 
318 - (void)setMaxValue:(double)maxValue
319 {
320  if (_maxValue === maxValue)
321  return;
322  _maxValue = maxValue;
323 
324  [self setNeedsLayout];
325 }
326 
327 - (void)setWarningValue:(double)warningValue;
328 {
329  if (_warningValue === warningValue)
330  return;
331  _warningValue = warningValue;
332 
333  [self setNeedsLayout];
334 }
335 
336 - (void)setCriticalValue:(double)criticalValue;
337 {
338  if (_criticalValue === criticalValue)
339  return;
340  _criticalValue = criticalValue;
341 
342  [self setNeedsLayout];
343 }
344 
345 /*
346 - (CPTickMarkPosition)tickMarkPosition;
347 - (void)setTickMarkPosition:(CPTickMarkPosition)position;
348 
349 - (int)numberOfTickMarks;
350 - (void)setNumberOfTickMarks:(int)count;
351 
352 - (int)numberOfMajorTickMarks;
353 - (void)setNumberOfMajorTickMarks:(int)count;
354 
355 - (double)tickMarkValueAtIndex:(int)index;
356 - (CGRect)rectOfTickMarkAtIndex:(int)index;
357 */
358 
359 @end
360 
361 var CPLevelIndicatorStyleKey = "CPLevelIndicatorStyleKey",
362  CPLevelIndicatorMinValueKey = "CPLevelIndicatorMinValueKey",
363  CPLevelIndicatorMaxValueKey = "CPLevelIndicatorMaxValueKey",
364  CPLevelIndicatorWarningValueKey = "CPLevelIndicatorWarningValueKey",
365  CPLevelIndicatorCriticalValueKey = "CPLevelIndicatorCriticalValueKey",
366  CPLevelIndicatorTickMarkPositionKey = "CPLevelIndicatorTickMarkPositionKey",
367  CPLevelIndicatorNumberOfTickMarksKey = "CPLevelIndicatorNumberOfTickMarksKey",
368  CPLevelIndicatorNumberOfMajorTickMarksKey = "CPLevelIndicatorNumberOfMajorTickMarksKey",
369  CPLevelIndicatorIsEditableKey = "CPLevelIndicatorIsEditableKey";
370 
372 
373 - (id)initWithCoder:(CPCoder)aCoder
374 {
375  self = [super initWithCoder:aCoder];
376 
377  if (self)
378  {
379  _levelIndicatorStyle = [aCoder decodeIntForKey:CPLevelIndicatorStyleKey];
380  _minValue = [aCoder decodeDoubleForKey:CPLevelIndicatorMinValueKey];
381  _maxValue = [aCoder decodeDoubleForKey:CPLevelIndicatorMaxValueKey];
382  _warningValue = [aCoder decodeDoubleForKey:CPLevelIndicatorWarningValueKey];
383  _criticalValue = [aCoder decodeDoubleForKey:CPLevelIndicatorCriticalValueKey];
384  _tickMarkPosition = [aCoder decodeIntForKey:CPLevelIndicatorTickMarkPositionKey];
385  _numberOfTickMarks = [aCoder decodeIntForKey:CPLevelIndicatorNumberOfTickMarksKey];
386  _numberOfMajorTickMarks = [aCoder decodeIntForKey:CPLevelIndicatorNumberOfMajorTickMarksKey];
387 
388  _isEditable = [aCoder decodeBoolForKey:CPLevelIndicatorIsEditableKey];
389 
390  [self _init];
391 
392  [self setNeedsLayout];
393  [self setNeedsDisplay:YES];
394  }
395 
396  return self;
397 }
398 
399 - (void)encodeWithCoder:(CPCoder)aCoder
400 {
401  [super encodeWithCoder:aCoder];
402 
403  [aCoder encodeInt:_levelIndicatorStyle forKey:CPLevelIndicatorStyleKey];
404  [aCoder encodeDouble:_minValue forKey:CPLevelIndicatorMinValueKey];
405  [aCoder encodeDouble:_maxValue forKey:CPLevelIndicatorMaxValueKey];
406  [aCoder encodeDouble:_warningValue forKey:CPLevelIndicatorWarningValueKey];
407  [aCoder encodeDouble:_criticalValue forKey:CPLevelIndicatorCriticalValueKey];
408  [aCoder encodeInt:_tickMarkPosition forKey:CPLevelIndicatorTickMarkPositionKey];
409  [aCoder encodeInt:_numberOfTickMarks forKey:CPLevelIndicatorNumberOfTickMarksKey];
410  [aCoder encodeInt:_numberOfMajorTickMarks forKey:CPLevelIndicatorNumberOfMajorTickMarksKey];
411  [aCoder encodeBool:_isEditable forKey:CPLevelIndicatorIsEditableKey];
412 }
413 
414 @end
415 
417 
421 - (CPLevelIndicator)levelIndicatorStyle
422 {
423  return _levelIndicatorStyle;
424 }
425 
429 - (void)setLevelIndicatorStyle:(CPLevelIndicator)aValue
430 {
431  _levelIndicatorStyle = aValue;
432 }
433 
437 - (double)minValue
438 {
439  return _minValue;
440 }
441 
445 - (void)setMinValue:(double)aValue
446 {
447  _minValue = aValue;
448 }
449 
453 - (double)maxValue
454 {
455  return _maxValue;
456 }
457 
461 - (void)setMaxValue:(double)aValue
462 {
463  _maxValue = aValue;
464 }
465 
469 - (double)warningValue
470 {
471  return _warningValue;
472 }
473 
477 - (void)setWarningValue:(double)aValue
478 {
479  _warningValue = aValue;
480 }
481 
485 - (double)criticalValue
486 {
487  return _criticalValue;
488 }
489 
493 - (void)setCriticalValue:(double)aValue
494 {
495  _criticalValue = aValue;
496 }
497 
501 - (CPTickMarkPosition)tickMarkPosition
502 {
503  return _tickMarkPosition;
504 }
505 
509 - (void)setTickMarkPosition:(CPTickMarkPosition)aValue
510 {
511  _tickMarkPosition = aValue;
512 }
513 
517 - (int)numberOfTickMarks
518 {
519  return _numberOfTickMarks;
520 }
521 
525 - (void)setNumberOfTickMarks:(int)aValue
526 {
527  _numberOfTickMarks = aValue;
528 }
529 
533 - (int)numberOfMajorTickMarks
534 {
535  return _numberOfMajorTickMarks;
536 }
537 
541 - (void)setNumberOfMajorTickMarks:(int)aValue
542 {
543  _numberOfMajorTickMarks = aValue;
544 }
545 
546 @end