1/** <title>NSProgressIndicator</title> 2 3 Copyright (C) 1999 Free Software Foundation, Inc. 4 5 Author: Gerrit van Dyk <gerritvd@decimax.com> 6 Date: 1999 7 Author: Fred Kiefer <fredkiefer@gmx.de> 8 Date: 2009 9 10 This file is part of the GNUstep GUI Library. 11 12 This library is free software; you can redistribute it and/or 13 modify it under the terms of the GNU Lesser General Public 14 License as published by the Free Software Foundation; either 15 version 2 of the License, or (at your option) any later version. 16 17 This library is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public 23 License along with this library; see the file COPYING.LIB. 24 If not, see <http://www.gnu.org/licenses/> or write to the 25 Free Software Foundation, 51 Franklin Street, Fifth Floor, 26 Boston, MA 02110-1301, USA. 27*/ 28 29#import <Foundation/NSAutoreleasePool.h> 30#import <Foundation/NSRunLoop.h> 31#import <Foundation/NSThread.h> 32#import <Foundation/NSTimer.h> 33#import "AppKit/NSApplication.h" 34#import "AppKit/NSProgressIndicator.h" 35#import "AppKit/NSGraphics.h" 36#import "AppKit/NSImage.h" 37#import "AppKit/NSWindow.h" 38#import "GNUstepGUI/GSTheme.h" 39#import "GNUstepGUI/GSNibLoading.h" 40 41@implementation NSProgressIndicator 42 43+ (void) initialize 44{ 45 if (self == [NSProgressIndicator class]) 46 { 47 [self setVersion: 1]; 48 } 49} 50 51- (id) initWithFrame: (NSRect)frameRect 52{ 53 self = [super initWithFrame: frameRect]; 54 if (!self) 55 return nil; 56 57 _isIndeterminate = YES; 58 _isDisplayedWhenStopped = YES; 59 _isBezeled = YES; 60 _animationDelay = 5.0 / 60.0; // 1 twelfth a a second 61 _doubleValue = 0.0; 62 _minValue = 0.0; 63 _maxValue = 100.0; 64 _controlTint = NSDefaultControlTint; 65 _controlSize = NSRegularControlSize; 66 [self setStyle: NSProgressIndicatorBarStyle]; 67 //_isVertical = NO; 68 //_usesThreadedAnimation = NO; 69 70 return self; 71} 72 73- (void) dealloc 74{ 75 [self stopAnimation: self]; 76 [super dealloc]; 77} 78 79- (BOOL) isFlipped 80{ 81 return YES; 82} 83 84- (void) animate: (id)sender 85{ 86 if (!_isIndeterminate && (_style == NSProgressIndicatorBarStyle)) 87 return; 88 89 // Let this value overflow when it reachs the limit 90 _count++; 91 92 [self setNeedsDisplay: YES]; 93} 94 95- (NSTimeInterval) animationDelay 96{ 97 return _animationDelay; 98} 99 100- (void) setAnimationDelay: (NSTimeInterval)delay 101{ 102 _animationDelay = delay; 103 if (_isRunning && (_isIndeterminate 104 || (_style == NSProgressIndicatorSpinningStyle))) 105 { 106 [self stopAnimation: self]; 107 [self startAnimation: self]; 108 } 109} 110 111- (void) _animationLoop 112{ 113 while (_isRunning) 114 { 115 CREATE_AUTORELEASE_POOL(pool); 116 117 [self animate: self]; 118 [NSThread sleepForTimeInterval: _animationDelay]; 119 [pool drain]; 120 } 121} 122 123- (void) startAnimation: (id)sender 124{ 125 if (_isRunning || (!_isIndeterminate 126 && (_style == NSProgressIndicatorBarStyle))) 127 return; 128 129 _isRunning = YES; 130 if (!_usesThreadedAnimation) 131 { 132 ASSIGN(_timer, [NSTimer scheduledTimerWithTimeInterval: _animationDelay 133 target: self 134 selector: @selector(animate:) 135 userInfo: nil 136 repeats: YES]); 137 [[NSRunLoop currentRunLoop] addTimer: _timer forMode: NSModalPanelRunLoopMode]; 138 } 139 else 140 { 141 [NSThread detachNewThreadSelector: @selector(_animationLoop) 142 toTarget: self 143 withObject: nil]; 144 } 145} 146 147- (void) stopAnimation: (id)sender 148{ 149 if (!_isRunning || (!_isIndeterminate 150 && (_style == NSProgressIndicatorBarStyle))) 151 return; 152 153 if (!_usesThreadedAnimation) 154 { 155 [_timer invalidate]; 156 DESTROY(_timer); 157 } 158 else 159 { 160 // Done automatically 161 } 162 163 _isRunning = NO; 164 _count = 0; 165 [self setNeedsDisplay: YES]; 166} 167 168- (BOOL) isHidden 169{ 170 if (!_isRunning && !_isDisplayedWhenStopped) 171 { 172 return YES; 173 } 174 return [super isHidden]; 175} 176 177- (BOOL) usesThreadedAnimation 178{ 179 return _usesThreadedAnimation; 180} 181 182- (void) setUsesThreadedAnimation: (BOOL)flag 183{ 184 if (_usesThreadedAnimation != flag) 185 { 186 BOOL wasRunning = _isRunning; 187 188 if (wasRunning) 189 [self stopAnimation: self]; 190 191 _usesThreadedAnimation = flag; 192 193 if (wasRunning) 194 [self startAnimation: self]; 195 } 196} 197 198- (void) incrementBy: (double)delta 199{ 200 [self setDoubleValue: _doubleValue + delta]; 201} 202 203- (double) doubleValue 204{ 205 return _doubleValue; 206} 207 208- (void) setDoubleValue: (double)aValue 209{ 210 if (aValue > _maxValue) 211 aValue = _maxValue; 212 else if (aValue < _minValue) 213 aValue = _minValue; 214 215 if (_doubleValue != aValue) 216 { 217 _doubleValue = aValue; 218 [self setNeedsDisplay: YES]; 219 } 220} 221 222- (double) minValue 223{ 224 return _minValue; 225} 226 227- (void) setMinValue: (double)newMinimum 228{ 229 if (_minValue != newMinimum) 230 { 231 _minValue = newMinimum; 232 if (_minValue > _doubleValue) 233 _doubleValue = _minValue; 234 [self setNeedsDisplay: YES]; 235 } 236} 237 238- (double) maxValue 239{ 240 return _maxValue; 241} 242 243- (void) setMaxValue: (double)newMaximum 244{ 245 if (_maxValue != newMaximum) 246 { 247 _maxValue = newMaximum; 248 if (_maxValue < _doubleValue) 249 _doubleValue = _maxValue; 250 [self setNeedsDisplay: YES]; 251 } 252} 253 254- (BOOL)isBezeled 255{ 256 return _isBezeled; 257} 258 259- (void) setBezeled: (BOOL)flag 260{ 261 if (_isBezeled != flag) 262 { 263 _isBezeled = flag; 264 [self setNeedsDisplay: YES]; 265 } 266} 267 268- (BOOL) isIndeterminate 269{ 270 return _isIndeterminate; 271} 272 273- (void) setIndeterminate: (BOOL)flag 274{ 275 /* Note: We must stop a running animation before setting _isIndeterminate 276 because -stopAnimation: has no effect when _isIndeterminate is NO. */ 277 if (flag == NO && _isRunning) 278 [self stopAnimation: self]; 279 280 _isIndeterminate = flag; 281 // Maybe we need more functionality here when we implement indeterminate 282 283 [self setNeedsDisplay: YES]; 284} 285 286- (BOOL) isDisplayedWhenStopped 287{ 288 return _isDisplayedWhenStopped; 289} 290 291- (void) setDisplayedWhenStopped: (BOOL)flag 292{ 293 if (flag != _isDisplayedWhenStopped) 294 { 295 _isDisplayedWhenStopped = flag; 296 [self setNeedsDisplay: YES]; 297 } 298} 299 300- (NSProgressIndicatorStyle) style 301{ 302 return _style; 303} 304 305- (void) setStyle: (NSProgressIndicatorStyle)style 306{ 307 _style = style; 308 _count = 0; 309 [self setDisplayedWhenStopped: (style == NSProgressIndicatorBarStyle)]; 310 [self setBezeled: (style == NSProgressIndicatorBarStyle)]; 311 [self sizeToFit]; 312 [self setNeedsDisplay: YES]; 313} 314 315- (NSControlSize) controlSize 316{ 317 return _controlSize; 318} 319 320- (void) setControlSize: (NSControlSize)size 321{ 322 _controlSize = size; 323 [self sizeToFit]; 324 [self setNeedsDisplay: YES]; 325} 326 327- (NSControlTint) controlTint 328{ 329 return _controlTint; 330} 331 332- (void) setControlTint: (NSControlTint)tint 333{ 334 _controlTint = tint; 335 [self setNeedsDisplay: YES]; 336} 337 338- (void) sizeToFit 339{ 340 // FIXME 341} 342 343- (void) drawRect: (NSRect)rect 344{ 345 double val; 346 347 if (_doubleValue < _minValue) 348 val = 0.0; 349 else if (_doubleValue > _maxValue) 350 val = 1.0; 351 else 352 val = (_doubleValue - _minValue) / (_maxValue - _minValue); 353 [[GSTheme theme] drawProgressIndicator: self 354 withBounds: _bounds 355 withClip: rect 356 atCount: _count 357 forValue: val]; 358} 359 360// It does not seem that Gnustep has a copyWithZone: on NSView, it is private 361// under openstep 362 363// NSCopying 364/* - (id)copyWithZone:(NSZone *)zone 365{ 366 NSProgressIndicator *newInd; 367 368 newInd = [super copyWithZone:zone]; 369 [newInd setIndeterminate:_isIndeterminate]; 370 [newInd setBezeled:_isBezeled]; 371 [newInd setUsesThreadedAnimation:_usesThreadedAnimation]; 372 [newInd setAnimimationDelay:_animationDelay]; 373 [newInd setDoubleValue:_doubleValue]; 374 [newInd setMinValue:_minValue]; 375 [newInd setMaxValue:_maxValue]; 376 [newInd setVertical:_isVertical]; 377 return newInd; 378} 379*/ 380 381// NSCoding 382- (void) encodeWithCoder: (NSCoder *)aCoder 383{ 384 [super encodeWithCoder: aCoder]; 385 if ([aCoder allowsKeyedCoding]) 386 { 387 unsigned long flags = 0; 388 id matrix = AUTORELEASE([[NSPSMatrix alloc] init]); 389 390 [aCoder encodeDouble: _minValue forKey: @"NSMinValue"]; 391 [aCoder encodeDouble: _maxValue forKey: @"NSMaxValue"]; 392 [aCoder encodeObject: matrix forKey: @"NSDrawMatrix"]; 393 394 // add flag values. 395 flags |= (_isIndeterminate)? 2 : 0; 396 // Hard coded... 397 flags |= 8; 398 flags |= (_controlSize == NSSmallControlSize) ? 0x100 : 0; 399 flags |= (_style == NSProgressIndicatorSpinningStyle) ? 0x1000 : 0; 400 flags |= _isDisplayedWhenStopped ? 0 : 0x2000; 401 [aCoder encodeInt: flags forKey: @"NSpiFlags"]; 402 403 // things which Gorm encodes, but IB doesn't care about. 404 [aCoder encodeDouble: _doubleValue forKey: @"GSDoubleValue"]; 405 [aCoder encodeBool: _isBezeled forKey: @"GSIsBezeled"]; 406 [aCoder encodeBool: _isVertical forKey: @"GSIsVertical"]; 407 [aCoder encodeBool: _usesThreadedAnimation forKey: @"GSUsesThreadAnimation"]; 408 [aCoder encodeDouble: _animationDelay forKey: @"GSAnimationDelay"]; 409 } 410 else 411 { 412 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_isIndeterminate]; 413 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_isBezeled]; 414 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_usesThreadedAnimation]; 415 [aCoder encodeValueOfObjCType: @encode(NSTimeInterval) at: &_animationDelay]; 416 [aCoder encodeValueOfObjCType: @encode(double) at: &_doubleValue]; 417 [aCoder encodeValueOfObjCType: @encode(double) at: &_minValue]; 418 [aCoder encodeValueOfObjCType: @encode(double) at: &_maxValue]; 419 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_isVertical]; 420 } 421} 422 423- (id) initWithCoder: (NSCoder *)aDecoder 424{ 425 self = [super initWithCoder: aDecoder]; 426 if (!self) 427 return nil; 428 429 if ([aDecoder allowsKeyedCoding]) 430 { 431 // things which Gorm encodes, but IB doesn't care about. 432 // process Gorm encodings that IB doesn't care about first 433 // otherwise we overwrite settings read in from XIB... 434 if ([aDecoder containsValueForKey: @"GSDoubleValue"]) 435 { 436 _doubleValue = [aDecoder decodeDoubleForKey: @"GSDoubleValue"]; 437 } 438 else 439 { 440 _doubleValue = _minValue; 441 } 442 443 if ([aDecoder containsValueForKey: @"GSIsBezeled"]) 444 { 445 _isBezeled = [aDecoder decodeBoolForKey: @"GSIsBezeled"]; 446 } 447 else 448 { 449 _isBezeled = YES; 450 } 451 452 if ([aDecoder containsValueForKey: @"GSIsVertical"]) 453 { 454 _isVertical = [aDecoder decodeBoolForKey: @"GSIsVertical"]; 455 } 456 else 457 { 458 _isVertical = NO; 459 } 460 461 if ([aDecoder containsValueForKey: @"GSUsesThreadAnimation"]) 462 { 463 _usesThreadedAnimation = [aDecoder decodeBoolForKey: @"GSUsesThreadAnimation"]; 464 } 465 else 466 { 467 _usesThreadedAnimation = NO; 468 } 469 470 if ([aDecoder containsValueForKey: @"GSAnimationDelay"]) 471 { 472 _animationDelay = [aDecoder decodeDoubleForKey: @"GSAnimationDelay"]; 473 } 474 else 475 { 476 _animationDelay = 5.0 / 60.0; // 1 twelfth a a second 477 } 478 479 // id matrix = [aDecoder decodeObjectForKey: @"NSDrawMatrix"]; 480 if ([aDecoder containsValueForKey: @"NSMaxValue"]) 481 { 482 double max = [aDecoder decodeDoubleForKey: @"NSMaxValue"]; 483 484 [self setMaxValue: max]; 485 } 486 else 487 { 488 _maxValue = 100.0; 489 } 490 if ([aDecoder containsValueForKey: @"NSMinValue"]) 491 { 492 double min = [aDecoder decodeDoubleForKey: @"NSMinValue"]; 493 494 [self setMinValue: min]; 495 } 496 else 497 { 498 _minValue = 0.0; 499 } 500 501 if ([aDecoder containsValueForKey: @"NSpiFlags"]) 502 { 503 int flags = [aDecoder decodeIntForKey: @"NSpiFlags"]; 504 505 _isIndeterminate = ((flags & 2) == 2); 506 _controlTint = NSDefaultControlTint; 507 _controlSize = (flags & 0x100) ? NSSmallControlSize : NSRegularControlSize; 508 [self setStyle: (flags & 0x1000) ? NSProgressIndicatorSpinningStyle 509 : NSProgressIndicatorBarStyle]; 510 _isDisplayedWhenStopped = ((flags & 0x2000) != 0x2000); 511 // ignore the rest, since they are not pertinent to GNUstep. 512 } 513 else 514 { 515 _isIndeterminate = YES; 516 _isDisplayedWhenStopped = YES; 517 _controlTint = NSDefaultControlTint; 518 _controlSize = NSRegularControlSize; 519 [self setStyle: NSProgressIndicatorBarStyle]; 520 } 521 } 522 else 523 { 524 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_isIndeterminate]; 525 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_isBezeled]; 526 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_usesThreadedAnimation]; 527 [aDecoder decodeValueOfObjCType: @encode(NSTimeInterval) 528 at: &_animationDelay]; 529 [aDecoder decodeValueOfObjCType: @encode(double) at: &_doubleValue]; 530 [aDecoder decodeValueOfObjCType: @encode(double) at: &_minValue]; 531 [aDecoder decodeValueOfObjCType: @encode(double) at: &_maxValue]; 532 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_isVertical]; 533 534 _isDisplayedWhenStopped = YES; 535 _controlTint = NSDefaultControlTint; 536 _controlSize = NSRegularControlSize; 537 [self setStyle: NSProgressIndicatorBarStyle]; 538 } 539 return self; 540} 541 542@end 543 544@implementation NSProgressIndicator (GNUstepExtensions) 545 546- (BOOL) isVertical 547{ 548 return _isVertical; 549} 550 551- (void) setVertical: (BOOL)flag 552{ 553 if (_isVertical != flag) 554 { 555 _isVertical = flag; 556 [self setNeedsDisplay:YES]; 557 } 558} 559 560@end 561 562