/** NSProgressIndicator Copyright (C) 1999 Free Software Foundation, Inc. Author: Gerrit van Dyk Date: 1999 Author: Fred Kiefer Date: 2009 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 Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, see or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "AppKit/NSProgressIndicator.h" #include "AppKit/NSGraphics.h" #include "AppKit/NSImage.h" #include "AppKit/NSWindow.h" #include "GNUstepGUI/GSTheme.h" #include "GNUstepGUI/GSNibLoading.h" @implementation NSProgressIndicator static NSColor *fillColour = nil; #define MaxCount 10 static int indeterminateMaxCount = MaxCount; static int spinningMaxCount = MaxCount; static NSColor *indeterminateColors[MaxCount]; static NSImage *spinningImages[MaxCount]; + (void) initialize { if (self == [NSProgressIndicator class]) { int i; [self setVersion: 1]; // FIXME: Should come from defaults and should be reset when defaults change // FIXME: Should probably get the color from the color extension list (see NSToolbar) fillColour = RETAIN([NSColor controlShadowColor]); // Load images for indeterminate style for (i = 0; i < MaxCount; i++) { NSString *imgName = [NSString stringWithFormat: @"common_ProgressIndeterminate_%d", i + 1]; NSImage *image = [NSImage imageNamed: imgName]; if (image == nil) { indeterminateMaxCount = i; break; } indeterminateColors[i] = RETAIN([NSColor colorWithPatternImage: image]); } // Load images for spinning style for (i = 0; i < MaxCount; i++) { NSString *imgName = [NSString stringWithFormat: @"common_ProgressSpinning_%d", i + 1]; NSImage *image = [NSImage imageNamed: imgName]; if (image == nil) { spinningMaxCount = i; break; } spinningImages[i] = RETAIN(image); } } } - (id)initWithFrame:(NSRect)frameRect { self = [super initWithFrame: frameRect]; if (!self) return nil; _isIndeterminate = YES; _isDisplayedWhenStopped = YES; _isBezeled = YES; _animationDelay = 5.0 / 60.0; // 1 twelfth a a second _doubleValue = 0.0; _minValue = 0.0; _maxValue = 100.0; _controlTint = NSDefaultControlTint; _controlSize = NSRegularControlSize; [self setStyle: NSProgressIndicatorBarStyle]; //_isVertical = NO; //_usesThreadedAnimation = NO; return self; } - (void)dealloc { [self stopAnimation: self]; [super dealloc]; } - (BOOL) isFlipped { return YES; } - (void)animate:(id)sender { if (!_isIndeterminate && (_style == NSProgressIndicatorBarStyle)) return; _count++; if (((_style == NSProgressIndicatorSpinningStyle) && (_count >= spinningMaxCount)) || ((_style == NSProgressIndicatorBarStyle) && (_count >= indeterminateMaxCount))) _count = 0; [self setNeedsDisplay: YES]; } - (NSTimeInterval)animationDelay { return _animationDelay; } - (void)setAnimationDelay:(NSTimeInterval)delay { _animationDelay = delay; if (_isRunning && (_isIndeterminate || (_style == NSProgressIndicatorSpinningStyle))) { [self stopAnimation: self]; [self startAnimation: self]; } } - (void)_animationLoop { while (_isRunning) { CREATE_AUTORELEASE_POOL(pool); [self animate: self]; [NSThread sleepForTimeInterval: _animationDelay]; RELEASE(pool); } } - (void)startAnimation:(id)sender { if (_isRunning || (!_isIndeterminate && (_style == NSProgressIndicatorBarStyle))) return; _isRunning = YES; if (!_usesThreadedAnimation) { ASSIGN(_timer, [NSTimer scheduledTimerWithTimeInterval: _animationDelay target: self selector: @selector(animate:) userInfo: nil repeats: YES]); } else { [NSThread detachNewThreadSelector: @selector(_animationLoop) toTarget: self withObject: nil]; } } - (void)stopAnimation:(id)sender { if (!_isRunning || (!_isIndeterminate && (_style == NSProgressIndicatorBarStyle))) return; if (!_usesThreadedAnimation) { [_timer invalidate]; DESTROY(_timer); } else { // Done automatically } _isRunning = NO; } - (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 { [self setDoubleValue: _doubleValue + delta]; } - (double)doubleValue { return _doubleValue; } - (void)setDoubleValue:(double)aValue { if (aValue > _maxValue) aValue = _maxValue; else if (aValue < _minValue) aValue = _minValue; if (_doubleValue != aValue) { _doubleValue = aValue; [self setNeedsDisplay: YES]; } } - (double)minValue { return _minValue; } - (void)setMinValue:(double)newMinimum { if (_minValue != newMinimum) { _minValue = newMinimum; if (_minValue > _doubleValue) _doubleValue = _minValue; [self setNeedsDisplay: YES]; } } - (double)maxValue { return _maxValue; } - (void)setMaxValue:(double)newMaximum { if (_maxValue != newMaximum) { _maxValue = newMaximum; if (_maxValue < _doubleValue) _doubleValue = _maxValue; [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; // Maybe we need more functionality here when we implement indeterminate if (flag == NO && _isRunning) [self stopAnimation: self]; [self setNeedsDisplay: YES]; } - (BOOL)isDisplayedWhenStopped { return _isDisplayedWhenStopped; } - (void)setDisplayedWhenStopped:(BOOL)flag { _isDisplayedWhenStopped = _isDisplayedWhenStopped; [self setNeedsDisplay: YES]; } - (NSProgressIndicatorStyle) style { return _style; } - (void)setStyle:(NSProgressIndicatorStyle)style { _style = style; _count = 0; [self setDisplayedWhenStopped: (style == NSProgressIndicatorBarStyle)]; [self sizeToFit]; [self setNeedsDisplay: YES]; } - (NSControlSize)controlSize { return _controlSize; } - (void)setControlSize:(NSControlSize)size { _controlSize = size; [self sizeToFit]; [self setNeedsDisplay: YES]; } - (NSControlTint)controlTint { return _controlTint; } - (void)setControlTint:(NSControlTint)tint { _controlTint = tint; [self setNeedsDisplay: YES]; } - (void) sizeToFit { // FIXME } - (void)drawRect:(NSRect)rect { NSRect r; if (!_isRunning && !_isDisplayedWhenStopped) return; // Draw the Bezel if (_isBezeled) { // Calc the inside rect to be drawn r = [[GSTheme theme] drawGrayBezel: _bounds withClip: rect]; } else { r = _bounds; } if (_style == NSProgressIndicatorSpinningStyle) { NSRect imgBox = {{0,0}, {0,0}}; imgBox.size = [spinningImages[_count] size]; [spinningImages[_count] drawInRect: r fromRect: imgBox operation: NSCompositeSourceOver fraction: 1.0]; } else { if (_isIndeterminate) { [indeterminateColors[_count] set]; NSRectFill(r); } else { // Draw determinate if (_doubleValue > _minValue) { double val; if (_doubleValue > _maxValue) val = _maxValue - _minValue; else val = _doubleValue - _minValue; if (_isVertical) { float height = NSHeight(r) * (val / (_maxValue - _minValue)); if ([self isFlipped]) { // Compensate for the flip r.origin.y += NSHeight(r) - height; } r.size.height = height; } else { r.size.width = NSWidth(r) * (val / (_maxValue - _minValue)); } r = NSIntersectionRect(r,rect); if (!NSIsEmptyRect(r)) { [fillColour set]; NSRectFill(r); } } } } } // It does not seem that Gnustep has a copyWithZone: on NSView, it is private // under openstep // NSCopying /* - (id)copyWithZone:(NSZone *)zone { NSProgressIndicator *newInd; newInd = [super copyWithZone:zone]; [newInd setIndeterminate:_isIndeterminate]; [newInd setBezeled:_isBezeled]; [newInd setUsesThreadedAnimation:_usesThreadedAnimation]; [newInd setAnimimationDelay:_animationDelay]; [newInd setDoubleValue:_doubleValue]; [newInd setMinValue:_minValue]; [newInd setMaxValue:_maxValue]; [newInd setVertical:_isVertical]; return newInd; } */ // NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder { [super encodeWithCoder:aCoder]; if ([aCoder allowsKeyedCoding]) { unsigned long flags = 0; id matrix = AUTORELEASE([[NSPSMatrix alloc] init]); [aCoder encodeDouble: _minValue forKey: @"NSMinValue"]; [aCoder encodeDouble: _maxValue forKey: @"NSMaxValue"]; [aCoder encodeObject: matrix forKey: @"NSDrawMatrix"]; // add flag values. flags |= (_isIndeterminate)? 2 : 0; // Hard coded... flags |= 8; flags |= (_controlSize == NSSmallControlSize) ? 0x100 : 0; flags |= (_style == NSProgressIndicatorSpinningStyle) ? 0x1000 : 0; flags |= _isDisplayedWhenStopped ? 0x2000 : 0; [aCoder encodeInt: flags forKey: @"NSpiFlags"]; // things which Gorm encodes, but IB doesn't care about. [aCoder encodeDouble: _doubleValue forKey: @"GSDoubleValue"]; [aCoder encodeBool: _isBezeled forKey: @"GSIsBezeled"]; [aCoder encodeBool: _isVertical forKey: @"GSIsVertical"]; [aCoder encodeBool: _usesThreadedAnimation forKey: @"GSUsesThreadAnimation"]; [aCoder encodeDouble: _animationDelay forKey: @"GSAnimationDelay"]; } else { [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]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_isVertical]; } } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder: aDecoder]; if (!self) return nil; if ([aDecoder allowsKeyedCoding]) { // id matrix = [aDecoder decodeObjectForKey: @"NSDrawMatrix"]; if ([aDecoder containsValueForKey: @"NSMaxValue"]) { double max = [aDecoder decodeDoubleForKey: @"NSMaxValue"]; [self setMaxValue: max]; } else { _maxValue = 100.0; } if ([aDecoder containsValueForKey: @"NSMinValue"]) { double min = [aDecoder decodeDoubleForKey: @"NSMinValue"]; [self setMinValue: min]; } else { _minValue = 0.0; } if ([aDecoder containsValueForKey: @"NSpiFlags"]) { int flags = [aDecoder decodeIntForKey: @"NSpiFlags"]; _isIndeterminate = ((flags & 2) == 2); _controlTint = NSDefaultControlTint; _controlSize = (flags & 0x100) ? NSSmallControlSize : NSRegularControlSize; [self setStyle: (flags & 0x1000) ? NSProgressIndicatorSpinningStyle : NSProgressIndicatorBarStyle]; _isDisplayedWhenStopped = ((flags & 0x2000) == 0x2000); // ignore the rest, since they are not pertinent to GNUstep. } else { _isIndeterminate = YES; _isDisplayedWhenStopped = YES; _controlTint = NSDefaultControlTint; _controlSize = NSRegularControlSize; [self setStyle: NSProgressIndicatorBarStyle]; } // things which Gorm encodes, but IB doesn't care about. if ([aDecoder containsValueForKey: @"GSDoubleValue"]) { _doubleValue = [aDecoder decodeDoubleForKey: @"GSDoubleValue"]; } else { _doubleValue = _minValue; } if ([aDecoder containsValueForKey: @"GSIsBezeled"]) { _isBezeled = [aDecoder decodeBoolForKey: @"GSIsBezeled"]; } else { _isBezeled = YES; } if ([aDecoder containsValueForKey: @"GSIsVertical"]) { _isVertical = [aDecoder decodeBoolForKey: @"GSIsVertical"]; } else { _isVertical = NO; } if ([aDecoder containsValueForKey: @"GSUsesThreadAnimation"]) { _usesThreadedAnimation = [aDecoder decodeBoolForKey: @"GSUsesThreadAnimation"]; } else { _usesThreadedAnimation = NO; } if ([aDecoder containsValueForKey: @"GSAnimationDelay"]) { _animationDelay = [aDecoder decodeDoubleForKey: @"GSAnimationDelay"]; } else { _animationDelay = 5.0 / 60.0; // 1 twelfth a a second } } else { [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: &_isVertical]; _isDisplayedWhenStopped = YES; _controlTint = NSDefaultControlTint; _controlSize = NSRegularControlSize; [self setStyle: NSProgressIndicatorBarStyle]; } return self; } @end @implementation NSProgressIndicator (GNUstepExtensions) - (BOOL)isVertical { return _isVertical; } - (void)setVertical:(BOOL)flag { if (_isVertical != flag) { _isVertical = flag; [self setNeedsDisplay:YES]; } } @end