/** NSProgressIndicator Copyright (C) 1999 Free Software Foundation, Inc. Author: Gerrit van Dyk Date: 1999 Adapted: H. Nikolaus Schaller 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 Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #import #import #import #import #import @implementation NSProgressIndicator NSColor *fillColour = nil; #define maxCount 20 // NSImage *images[maxCount]; + (void) initialize { if (self == [NSProgressIndicator class]) { // [self setVersion: 1]; // FIXME: Should come from defaults and should be reset when defaults change fillColour = [[NSColor blueColor] retain]; // FIXME: Load the images and set maxCount } } - (id)initWithFrame:(NSRect)frameRect { if((self = [super initWithFrame: frameRect])) { _isIndeterminate = YES; _isBezeled = YES; _usesThreadedAnimation = YES; _animationDelay = 5.0 / 60.0; // 1 twelfth of a second _doubleValue = 0.0; _minValue = 0.0; _maxValue = 100.0; _isDisplayedWhenStopped = YES; } return self; } - (void)dealloc { [_timer release]; // just in case... [super dealloc]; } - (BOOL) isFlipped { return YES; } - (BOOL) isOpaque { return NO; } - (void) animate:(id)sender { if (!_isIndeterminate) return; if (++_count >= maxCount) _count = 0; [self setNeedsDisplay:YES]; } - (NSTimeInterval)animationDelay { return _animationDelay; } - (void)setAnimimationDelay:(NSTimeInterval)delay { _animationDelay = delay; } - (void) startAnimation:(id)sender { if (!_isIndeterminate || _isRunning) return; // already determinate or already running if (_usesThreadedAnimation) { // Not implemented } ASSIGN(_timer, [NSTimer scheduledTimerWithTimeInterval: _animationDelay target: self selector: @selector(animate:) userInfo: nil repeats: YES]); _isRunning = YES; if (_usesThreadedAnimation) [self display]; // most likely the programmer assumes that it becomes at least visible without entering a runloop else [self setNeedsDisplay:YES]; } - (void) stopAnimation:(id)sender { if (!_isIndeterminate || !_isRunning) return; if (_usesThreadedAnimation) { // Not implemented } [_timer invalidate]; [_timer release]; _timer=nil; _isRunning=NO; if (_usesThreadedAnimation) [self display]; // most likely the programmer assumes that it becomes at least visible without entering a runloop else [self setNeedsDisplay:YES]; } - (BOOL) usesThreadedAnimation { return _usesThreadedAnimation; } - (void) setUsesThreadedAnimation:(BOOL)flag { if (_usesThreadedAnimation != flag) { BOOL wasRunning = _isRunning; if (wasRunning) [self stopAnimation: self]; _usesThreadedAnimation = flag; if (wasRunning) [self startAnimation: self]; } } - (void) incrementBy:(double)delta { _doubleValue += delta; if(delta != 0.0) [self setNeedsDisplay:YES]; } - (double) doubleValue { return _doubleValue; } - (void) setDoubleValue:(double)aValue { if (_doubleValue != aValue) { _doubleValue = aValue; [self setNeedsDisplay:YES]; } } - (double) minValue { return _minValue; } - (void) setMinValue:(double) newMinimum { if (_minValue != newMinimum) { _minValue = newMinimum; [self setNeedsDisplay:YES]; } } - (double)maxValue { return _maxValue; } - (void)setMaxValue:(double)newMaximum { if (_maxValue != newMaximum) { _maxValue = newMaximum; [self setNeedsDisplay:YES]; } } - (BOOL)isBezeled { return _isBezeled; } - (void)setBezeled:(BOOL)flag { if (_isBezeled != flag) { _isBezeled = flag; [self setNeedsDisplay:YES]; } } - (BOOL)isIndeterminate { return _isIndeterminate; } - (void)setIndeterminate:(BOOL)flag { _isIndeterminate = flag; if (flag == NO && _isRunning) [self stopAnimation: self]; } - (BOOL) isDisplayedWhenStopped; { return _isDisplayedWhenStopped; } - (void) setDisplayedWhenStopped:(BOOL)flag; { _style=_isDisplayedWhenStopped; } - (NSProgressIndicatorStyle) style; { return _style; } - (void) setStyle:(NSProgressIndicatorStyle)flag; { _style=flag; } - (NSControlSize)controlSize { // FIXME return NSRegularControlSize; } - (void)setControlSize:(NSControlSize)size { // FIXME } - (NSControlTint)controlTint { // FIXME return NSDefaultControlTint; } - (void)setControlTint:(NSControlTint)tint { // FIXME } - (void) drawRect:(NSRect)rect { if(!_isRunning && !_isDisplayedWhenStopped) return; if (_isBezeled) { if(_style == NSProgressIndicatorSpinningStyle) /*NSDrawGrayBezel(_bounds, rect)*/; else NSDrawGrayBezel(_bounds, rect); } if (_isIndeterminate) { // Draw indeterminate float phi=(_count*2*M_PI)/maxCount; if(_isRunning) [[NSColor colorWithCalibratedRed:0.5+0.5*sin(phi) green:0.5+0.5*sin(phi+2*M_PI/3) blue:0.5+0.5*sin(phi+4*M_PI/3) alpha:1.0] set]; else [[NSColor grayColor] set]; if(_style == NSProgressIndicatorSpinningStyle) [[NSBezierPath bezierPathWithOvalInRect:_bounds] fill]; // oval (not spinning...) else NSRectFill(rect); // rectangular (draw updated parts only) } else { // Draw determinate double val=(_doubleValue - _minValue) / (_maxValue - _minValue); NSRect r = NSInsetRect(_bounds, 1.0, 1.0); if(val < 0.0) val=0.0; else if(val>1.0) val=1.0; // clamp if(_style == NSProgressIndicatorSpinningStyle) { // draw "clock" NSBezierPath *progress=[NSBezierPath bezierPath]; NSPoint center=(NSPoint){ NSMidX(r), NSMidY(r) }; [progress moveToPoint:center]; [progress appendBezierPathWithArcWithCenter:center radius:0.25*(NSWidth(r)+NSHeight(r)) startAngle:-90.0 endAngle:360*val-90.0]; // flipped [progress closePath]; [[NSColor blueColor] set]; [progress fill]; [[NSBezierPath bezierPathWithOvalInRect:r] stroke]; // draw circle around } else { // rectangular if (NSHeight(r) > NSWidth(r)) r.size.height = NSHeight(r) * val; else r.size.width = NSWidth(r) * val; [fillColour set]; NSRectFill(r); } } } // NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder { [super encodeWithCoder:aCoder]; [aCoder encodeValueOfObjCType: @encode(BOOL) at:&_isIndeterminate]; [aCoder encodeValueOfObjCType: @encode(BOOL) at:&_isBezeled]; [aCoder encodeValueOfObjCType: @encode(BOOL) at:&_usesThreadedAnimation]; [aCoder encodeValueOfObjCType: @encode(NSTimeInterval) at:&_animationDelay]; [aCoder encodeValueOfObjCType: @encode(double) at:&_doubleValue]; [aCoder encodeValueOfObjCType: @encode(double) at:&_minValue]; [aCoder encodeValueOfObjCType: @encode(double) at:&_maxValue]; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if([aDecoder allowsKeyedCoding]) { int piFlags=[aDecoder decodeIntForKey:@"NSpiFlags"]; #if 1 NSLog(@"piFlags = %08x", piFlags); #endif #define STYLE ((piFlags&0x1000) >> 12) #define DISPLAYED_WHEN_STOPPED ((piFlags&0x2000) == 0) #define INDETERMINATE ((piFlags&0x0002) != 0) _animationDelay = 5.0 / 60.0; // 1 twelfth of a second _style = STYLE; _isDisplayedWhenStopped = DISPLAYED_WHEN_STOPPED; _isIndeterminate = INDETERMINATE; // which piFlag? _isBezeled = YES; // separate key? _usesThreadedAnimation = YES; _minValue=[aDecoder decodeFloatForKey:@"NSMinValue"]; _maxValue=[aDecoder decodeFloatForKey:@"NSMaxValue"]; _doubleValue=[aDecoder decodeFloatForKey:@"NSValue"]; (void) [aDecoder decodeObjectForKey:@"NSDrawMatrix"]; // ignore - may be of class NSPSMatrix return self; } [aDecoder decodeValueOfObjCType: @encode(BOOL) at:&_isIndeterminate]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at:&_isBezeled]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at:&_usesThreadedAnimation]; [aDecoder decodeValueOfObjCType: @encode(NSTimeInterval) at:&_animationDelay]; [aDecoder decodeValueOfObjCType: @encode(double) at:&_doubleValue]; [aDecoder decodeValueOfObjCType: @encode(double) at:&_minValue]; [aDecoder decodeValueOfObjCType: @encode(double) at:&_maxValue]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at:&_isDisplayedWhenStopped]; return self; } - (void) sizeToFit { // based on style } @end