API 0.9.5
AppKit/CPLevelIndicator.j
Go to the documentation of this file.
00001 /*
00002  * CPLevelIndicator.j
00003  * AppKit
00004  *
00005  * Created by Alexander Ljungberg.
00006  * Copyright 2011, WireLoad 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 CPTickMarkBelow                             = 0;
00025 CPTickMarkAbove                             = 1;
00026 CPTickMarkLeft                              = CPTickMarkAbove;
00027 CPTickMarkRight                             = CPTickMarkBelow;
00028 
00029 CPRelevancyLevelIndicatorStyle              = 0;
00030 CPContinuousCapacityLevelIndicatorStyle     = 1;
00031 CPDiscreteCapacityLevelIndicatorStyle       = 2;
00032 CPRatingLevelIndicatorStyle                 = 3;
00033 
00034 var _CPLevelIndicatorBezelColor = nil,
00035     _CPLevelIndicatorSegmentEmptyColor = nil,
00036     _CPLevelIndicatorSegmentNormalColor = nil,
00037     _CPLevelIndicatorSegmentWarningColor = nil,
00038     _CPLevelIndicatorSegmentCriticalColor = nil,
00039 
00040     _CPLevelIndicatorSpacing = 1;
00041 
00048 @implementation CPLevelIndicator : CPControl
00049 {
00050     CPLevelIndicator    _levelIndicatorStyle;
00051     double              _minValue;
00052     double              _maxValue;
00053     double              _warningValue;
00054     double              _criticalValue;
00055     CPTickMarkPosition  _tickMarkPosition;
00056     int                 _numberOfTickMarks;
00057     int                 _numberOfMajorTickMarks;
00058 
00059     BOOL                _isEditable;
00060 
00061     BOOL                _isTracking;
00062 }
00063 
00064 + (void)initialize
00065 {
00066     var bundle = [CPBundle bundleForClass:CPLevelIndicator];
00067 
00068     _CPLevelIndicatorBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
00069         [
00070             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-left.png"] size:CGSizeMake(3.0, 18.0)],
00071             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-center.png"] size:CGSizeMake(1.0, 18.0)],
00072             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-bezel-right.png"] size:CGSizeMake(3.0, 18.0)]
00073         ]
00074         isVertical:NO
00075     ]];
00076 
00077     _CPLevelIndicatorSegmentEmptyColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
00078         [
00079             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-left.png"] size:CGSizeMake(3.0, 17.0)],
00080             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-center.png"] size:CGSizeMake(1.0, 17.0)],
00081             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-empty-right.png"] size:CGSizeMake(3.0, 17.0)]
00082         ]
00083         isVertical:NO
00084     ]];
00085 
00086     _CPLevelIndicatorSegmentNormalColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
00087         [
00088             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-left.png"] size:CGSizeMake(3.0, 17.0)],
00089             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-center.png"] size:CGSizeMake(1.0, 17.0)],
00090             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-normal-right.png"] size:CGSizeMake(3.0, 17.0)]
00091         ]
00092         isVertical:NO
00093     ]];
00094 
00095     _CPLevelIndicatorSegmentWarningColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
00096         [
00097             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-left.png"] size:CGSizeMake(3.0, 17.0)],
00098             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-center.png"] size:CGSizeMake(1.0, 17.0)],
00099             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-warning-right.png"] size:CGSizeMake(3.0, 17.0)]
00100         ]
00101         isVertical:NO
00102     ]];
00103 
00104     _CPLevelIndicatorSegmentCriticalColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
00105         [
00106             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-left.png"] size:CGSizeMake(3.0, 17.0)],
00107             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-center.png"] size:CGSizeMake(1.0, 17.0)],
00108             [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPLevelIndicator/level-indicator-segment-critical-right.png"] size:CGSizeMake(3.0, 17.0)]
00109         ]
00110         isVertical:NO
00111     ]];
00112 }
00113 
00114 - (id)initWithFrame:(CGRect)aFrame
00115 {
00116     self = [super initWithFrame:aFrame];
00117 
00118     if (self)
00119     {
00120         _levelIndicatorStyle = CPDiscreteCapacityLevelIndicatorStyle;
00121         _maxValue = 2;
00122         _warningValue = 2;
00123         _criticalValue = 2;
00124 
00125         [self _init];
00126     }
00127 
00128     return self;
00129 }
00130 
00131 - (void)_init
00132 {
00133 }
00134 
00135 - (void)layoutSubviews
00136 {
00137     var bezelView = [self layoutEphemeralSubviewNamed:"bezel"
00138                                            positioned:CPWindowBelow
00139                       relativeToEphemeralSubviewNamed:nil];
00140     // TODO Make themable.
00141     [bezelView setBackgroundColor:_CPLevelIndicatorBezelColor];
00142 
00143     var segmentCount = _maxValue - _minValue;
00144 
00145     if (segmentCount <= 0)
00146         return;
00147 
00148     var filledColor = _CPLevelIndicatorSegmentNormalColor,
00149         value = [self doubleValue];
00150 
00151     if (value <= _criticalValue)
00152         filledColor = _CPLevelIndicatorSegmentCriticalColor;
00153     else if (value <= _warningValue)
00154         filledColor = _CPLevelIndicatorSegmentWarningColor;
00155 
00156     for (var i = 0; i < segmentCount; i++)
00157     {
00158         var segmentView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
00159                                                positioned:CPWindowAbove
00160                           relativeToEphemeralSubviewNamed:bezelView];
00161 
00162         [segmentView setBackgroundColor:(_minValue + i) < value ? filledColor : _CPLevelIndicatorSegmentEmptyColor];
00163     }
00164 }
00165 
00166 - (CPView)createEphemeralSubviewNamed:(CPString)aName
00167 {
00168     return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
00169 }
00170 
00171 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
00172 {
00173     // TODO Put into theme attributes.
00174     var bezelHeight = 18,
00175         segmentHeight = 17,
00176         bounds = _CGRectCreateCopy([self bounds]);
00177 
00178     if (aViewName == "bezel")
00179     {
00180         bounds.origin.y = (_CGRectGetHeight(bounds) - bezelHeight) / 2.0;
00181         bounds.size.height = bezelHeight;
00182         return bounds;
00183     }
00184     else if (aViewName.indexOf("segment-bezel") === 0)
00185     {
00186         var segment = parseInt(aViewName.substring("segment-bezel-".length), 10),
00187             segmentCount = _maxValue - _minValue;
00188 
00189         if (segment >= segmentCount)
00190             return _CGRectMakeZero();
00191 
00192         var basicSegmentWidth = bounds.size.width / segmentCount,
00193             segmentFrame = CGRectCreateCopy([self bounds]);
00194 
00195         segmentFrame.origin.y = (_CGRectGetHeight(bounds) - bezelHeight) / 2.0;
00196         segmentFrame.origin.x =  FLOOR(segment * basicSegmentWidth);
00197         segmentFrame.size.width = (segment == segmentCount - 1) ? bounds.size.width - segmentFrame.origin.x : FLOOR(((segment + 1) * basicSegmentWidth)) - FLOOR((segment * basicSegmentWidth)) - _CPLevelIndicatorSpacing;
00198         segmentFrame.size.height = segmentHeight;
00199 
00200         return segmentFrame;
00201     }
00202 
00203     return _CGRectMakeZero();
00204 }
00205 
00209 - (void)setEditable:(BOOL)shouldBeEditable
00210 {
00211     if (_isEditable === shouldBeEditable)
00212         return;
00213 
00214     _isEditable = shouldBeEditable;
00215 }
00216 
00220 - (BOOL)isEditable
00221 {
00222     return _isEditable;
00223 }
00224 
00225 - (CPView)hitTest:(CPPoint)aPoint
00226 {
00227     // Don't swallow clicks when displayed in a table.
00228     if (![self isEditable])
00229         return nil;
00230 
00231     return [super hitTest:aPoint];
00232 }
00233 
00234 - (void)mouseDown:(CPEvent)anEvent
00235 {
00236     if (![self isEditable] || ![self isEnabled])
00237         return;
00238 
00239     [self _trackMouse:anEvent];
00240 }
00241 
00242 - (void)_trackMouse:(CPEvent)anEvent
00243 {
00244     var type = [anEvent type];
00245 
00246     if (type == CPLeftMouseDown || type == CPLeftMouseDragged)
00247     {
00248         var segmentCount = _maxValue - _minValue;
00249 
00250         if (segmentCount <= 0)
00251             return;
00252 
00253         var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
00254             bounds = [self bounds],
00255             oldValue = [self doubleValue];
00256             newValue = oldValue;
00257 
00258         // Moving the mouse outside of the widget to the left sets it
00259         // to its minimum, and moving outside on the right sets it to
00260         // its maximum.
00261         if (type == CPLeftMouseDragged && location.x < 0)
00262         {
00263             newValue = _minValue;
00264         }
00265         else if (type == CPLeftMouseDragged && location.x > bounds.size.width)
00266         {
00267             newValue = _maxValue;
00268         }
00269         else
00270         {
00271             for (var i = 0; i < segmentCount; i++)
00272             {
00273                 var rect = [self rectForEphemeralSubviewNamed:"segment-bezel-" + i];
00274 
00275                 // Once we're tracking the mouse, we only care about horizontal mouse movement.
00276                 if (location.x >= CGRectGetMinX(rect) && location.x < CGRectGetMaxX(rect))
00277                 {
00278                     newValue = (_minValue + i + 1);
00279                     break;
00280                 }
00281             }
00282         }
00283 
00284         if (newValue != oldValue)
00285             [self setDoubleValue:newValue];
00286 
00287         // Track the mouse to support click and slide value editing.
00288         _isTracking = YES;
00289         [CPApp setTarget:self selector:@selector(_trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
00290 
00291         if ([self isContinuous])
00292             [self sendAction:[self action] to:[self target]];
00293     }
00294     else if (_isTracking)
00295     {
00296         _isTracking = NO;
00297         [self sendAction:[self action] to:[self target]];
00298     }
00299 }
00300 
00301 /*
00302 - (CPLevelIndicatorStyle)style;
00303 - (void)setLevelIndicatorStyle:(CPLevelIndicatorStyle)style;
00304 */
00305 
00306 - (void)setMinValue:(double)minValue
00307 {
00308     if (_minValue === minValue)
00309         return;
00310     _minValue = minValue;
00311 
00312     [self setNeedsLayout];
00313 }
00314 
00315 - (void)setMaxValue:(double)maxValue
00316 {
00317     if (_maxValue === maxValue)
00318         return;
00319     _maxValue = maxValue;
00320 
00321     [self setNeedsLayout];
00322 }
00323 
00324 - (void)setWarningValue:(double)warningValue;
00325 {
00326     if (_warningValue === warningValue)
00327         return;
00328     _warningValue = warningValue;
00329 
00330     [self setNeedsLayout];
00331 }
00332 
00333 - (void)setCriticalValue:(double)criticalValue;
00334 {
00335     if (_criticalValue === criticalValue)
00336         return;
00337     _criticalValue = criticalValue;
00338 
00339     [self setNeedsLayout];
00340 }
00341 
00342 /*
00343 - (CPTickMarkPosition)tickMarkPosition;
00344 - (void)setTickMarkPosition:(CPTickMarkPosition)position;
00345 
00346 - (int)numberOfTickMarks;
00347 - (void)setNumberOfTickMarks:(int)count;
00348 
00349 - (int)numberOfMajorTickMarks;
00350 - (void)setNumberOfMajorTickMarks:(int)count;
00351 
00352 - (double)tickMarkValueAtIndex:(int)index;
00353 - (CGRect)rectOfTickMarkAtIndex:(int)index;
00354 */
00355 
00356 @end
00357 
00358 var CPLevelIndicatorStyleKey                    = "CPLevelIndicatorStyleKey",
00359     CPLevelIndicatorMinValueKey                 = "CPLevelIndicatorMinValueKey",
00360     CPLevelIndicatorMaxValueKey                 = "CPLevelIndicatorMaxValueKey",
00361     CPLevelIndicatorWarningValueKey             = "CPLevelIndicatorWarningValueKey",
00362     CPLevelIndicatorCriticalValueKey            = "CPLevelIndicatorCriticalValueKey",
00363     CPLevelIndicatorTickMarkPositionKey         = "CPLevelIndicatorTickMarkPositionKey",
00364     CPLevelIndicatorNumberOfTickMarksKey        = "CPLevelIndicatorNumberOfTickMarksKey",
00365     CPLevelIndicatorNumberOfMajorTickMarksKey   = "CPLevelIndicatorNumberOfMajorTickMarksKey",
00366     CPLevelIndicatorIsEditableKey               = "CPLevelIndicatorIsEditableKey";
00367 
00368 @implementation CPLevelIndicator (CPCoding)
00369 
00370 - (id)initWithCoder:(CPCoder)aCoder
00371 {
00372     self = [super initWithCoder:aCoder];
00373 
00374     if (self)
00375     {
00376         _levelIndicatorStyle = [aCoder decodeIntForKey:CPLevelIndicatorStyleKey];
00377         _minValue = [aCoder decodeDoubleForKey:CPLevelIndicatorMinValueKey];
00378         _maxValue = [aCoder decodeDoubleForKey:CPLevelIndicatorMaxValueKey];
00379         _warningValue = [aCoder decodeDoubleForKey:CPLevelIndicatorWarningValueKey];
00380         _criticalValue = [aCoder decodeDoubleForKey:CPLevelIndicatorCriticalValueKey];
00381         _tickMarkPosition = [aCoder decodeIntForKey:CPLevelIndicatorTickMarkPositionKey];
00382         _numberOfTickMarks = [aCoder decodeIntForKey:CPLevelIndicatorNumberOfTickMarksKey];
00383         _numberOfMajorTickMarks = [aCoder decodeIntForKey:CPLevelIndicatorNumberOfMajorTickMarksKey];
00384 
00385         _isEditable = [aCoder decodeBoolForKey:CPLevelIndicatorIsEditableKey];
00386 
00387         [self _init];
00388 
00389         [self setNeedsLayout];
00390         [self setNeedsDisplay:YES];
00391     }
00392 
00393     return self;
00394 }
00395 
00396 - (void)encodeWithCoder:(CPCoder)aCoder
00397 {
00398     [super encodeWithCoder:aCoder];
00399 
00400     [aCoder encodeInt:_levelIndicatorStyle forKey:CPLevelIndicatorStyleKey];
00401     [aCoder encodeDouble:_minValue forKey:CPLevelIndicatorMinValueKey];
00402     [aCoder encodeDouble:_maxValue forKey:CPLevelIndicatorMaxValueKey];
00403     [aCoder encodeDouble:_warningValue forKey:CPLevelIndicatorWarningValueKey];
00404     [aCoder encodeDouble:_criticalValue forKey:CPLevelIndicatorCriticalValueKey];
00405     [aCoder encodeInt:_tickMarkPosition forKey:CPLevelIndicatorTickMarkPositionKey];
00406     [aCoder encodeInt:_numberOfTickMarks forKey:CPLevelIndicatorNumberOfTickMarksKey];
00407     [aCoder encodeInt:_numberOfMajorTickMarks forKey:CPLevelIndicatorNumberOfMajorTickMarksKey];
00408     [aCoder encodeBool:_isEditable forKey:CPLevelIndicatorIsEditableKey];
00409 }
00410 
00411 @end
00412 
00413 @implementation CPLevelIndicator (CPSynthesizedAccessors)
00414 
00418 - (CPLevelIndicator)levelIndicatorStyle
00419 {
00420     return _levelIndicatorStyle;
00421 }
00422 
00426 - (void)setLevelIndicatorStyle:(CPLevelIndicator)aValue
00427 {
00428     _levelIndicatorStyle = aValue;
00429 }
00430 
00434 - (double)minValue
00435 {
00436     return _minValue;
00437 }
00438 
00442 - (void)setMinValue:(double)aValue
00443 {
00444     _minValue = aValue;
00445 }
00446 
00450 - (double)maxValue
00451 {
00452     return _maxValue;
00453 }
00454 
00458 - (void)setMaxValue:(double)aValue
00459 {
00460     _maxValue = aValue;
00461 }
00462 
00466 - (double)warningValue
00467 {
00468     return _warningValue;
00469 }
00470 
00474 - (void)setWarningValue:(double)aValue
00475 {
00476     _warningValue = aValue;
00477 }
00478 
00482 - (double)criticalValue
00483 {
00484     return _criticalValue;
00485 }
00486 
00490 - (void)setCriticalValue:(double)aValue
00491 {
00492     _criticalValue = aValue;
00493 }
00494 
00498 - (CPTickMarkPosition)tickMarkPosition
00499 {
00500     return _tickMarkPosition;
00501 }
00502 
00506 - (void)setTickMarkPosition:(CPTickMarkPosition)aValue
00507 {
00508     _tickMarkPosition = aValue;
00509 }
00510 
00514 - (int)numberOfTickMarks
00515 {
00516     return _numberOfTickMarks;
00517 }
00518 
00522 - (void)setNumberOfTickMarks:(int)aValue
00523 {
00524     _numberOfTickMarks = aValue;
00525 }
00526 
00530 - (int)numberOfMajorTickMarks
00531 {
00532     return _numberOfMajorTickMarks;
00533 }
00534 
00538 - (void)setNumberOfMajorTickMarks:(int)aValue
00539 {
00540     _numberOfMajorTickMarks = aValue;
00541 }
00542 
00543 @end
 All Classes Files Functions Variables Defines