![]() |
API 0.9.5
|
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