00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #import "DownAndOutView.h"
00023 #import "DownAndOutController.h"
00024 #import "DAApplication.h"
00025
00026 #define CLOCK_RADIUS clockRadius
00027 #define CLOCK_WIDTH (2.0 * CLOCK_RADIUS + 3.0)
00028 #define CONTENT_HEIGHT (2.0 * CLOCK_RADIUS)
00029 #define FIXED_WIDTH (3.0 + 7.0 * CLOCK_RADIUS)
00030
00031 #define MAX_V_COMPONENT 1.3
00032
00033
00034 @implementation DownAndOutView
00035
00039 - (void) setDeadlineForSeconds: (int) seconds
00040 {
00041 [deadline release];
00042 secondsLeft = seconds;
00043 deadline = [[NSDate dateWithTimeIntervalSinceNow: secondsLeft] retain];
00044 }
00045
00048 - (void) resetClock
00049 {
00050 [self setDeadlineForSeconds: [controller secondsBeforeLogout]];
00051 }
00052
00059 - (id) initWithFrame: (NSRect) frame isPreview: (BOOL) isPreview
00060 {
00061 self = [super initWithFrame: frame isPreview: isPreview];
00062 if (self) {
00063 [self setAnimationTimeInterval:1/20.0];
00064
00065 clockRadius = -1.0;
00066 controller = [[DownAndOutController alloc] initAsPreview: isPreview forView: self];
00067
00068
00069 state = kDOInitial;
00070 }
00071 return self;
00072 }
00073
00074 - (float) clockRadius { return clockRadius; }
00075
00076 static NSDictionary * attrs = nil;
00077
00078 - (void) setClockRadius: (float) newRadius
00079 {
00080 if (newRadius != clockRadius) {
00081 clockRadius = newRadius;
00082
00083 [pieImage release];
00084 pieImage = [[NSImage alloc] initWithSize: NSMakeSize(2.0 * clockRadius + 2.0, 2.0 * clockRadius + 2.0)];
00085
00086 [textImage release];
00087 textImage = [[NSImage alloc] initWithSize: NSMakeSize(FIXED_WIDTH - CLOCK_WIDTH, CONTENT_HEIGHT)];
00088
00089 [attrs release];
00090 NSFont * times24Ital = [NSFont fontWithName: @"Times-Italic" size: (clockRadius/2.0) - 1.0];
00091 attrs = [[NSDictionary alloc] initWithObjectsAndKeys:
00092 times24Ital, NSFontAttributeName,
00093 [NSColor lightGrayColor], NSForegroundColorAttributeName,
00094 nil];
00095 }
00096 }
00097
00099 - (void) dealloc
00100 {
00101 [controller release];
00102 [deadline release];
00103 [pieImage release];
00104 [textImage release];
00105 [super dealloc];
00106 }
00107
00111 - (void) fillTextImageWithSeconds: (int) seconds
00112 {
00113 NSRect frame = NSMakeRect(0.0, 0.0, FIXED_WIDTH - CLOCK_WIDTH, CONTENT_HEIGHT);
00114
00115 NSString * message = [controller messageTextForSeconds: seconds];
00116
00117 [textImage lockFocus];
00118
00119 [[NSColor blackColor] set];
00120 NSRectFill(frame);
00121
00122 [message drawInRect: frame withAttributes: attrs];
00123
00124 [textImage unlockFocus];
00125 }
00126
00130 - (void) fillTextImageWithString: (NSString *) aString
00131 {
00132 NSRect frame = NSMakeRect(0.0, 0.0, FIXED_WIDTH - CLOCK_WIDTH, CONTENT_HEIGHT);
00133
00134 [textImage lockFocus];
00135
00136 [[NSColor blackColor] set];
00137 NSRectFill(frame);
00138
00139 [aString drawInRect: frame withAttributes: attrs];
00140
00141 [textImage unlockFocus];
00142 }
00143
00147 - (NSRect) addedImageRect
00148 {
00149 NSImage * image = [controller addedImage];
00150 NSRect retval = NSMakeRect(NSMinX(contentRect), NSMinY(contentRect), 0.0, CONTENT_HEIGHT);
00151
00152 if (!image)
00153 return retval;
00154
00155 NSSize imageSize = [image size];
00156
00157 if (imageSize.height <= CONTENT_HEIGHT) {
00158
00159
00160 retval.size = imageSize;
00161 retval.origin.y += (CONTENT_HEIGHT - imageSize.height) / 2.0;
00162 return retval;
00163 }
00164
00165
00166
00167
00168 retval.size.height = CONTENT_HEIGHT;
00169 float aspect = imageSize.width / imageSize.height;
00170 retval.size.width = CONTENT_HEIGHT * aspect;
00171
00172 return retval;
00173 }
00174
00177 - (void) startAnimation
00178 {
00179 NSRect frame = [self frame];
00180 frame.origin = NSZeroPoint;
00181
00182 NSRect imageRect = [self addedImageRect];
00183
00184 contentRect.size = NSMakeSize(FIXED_WIDTH + imageRect.size.width + (imageRect.size.width? 3.0: 0.0), CONTENT_HEIGHT);
00185 contentRect.origin = NSZeroPoint;
00186
00187 if ([controller shouldMove]) {
00188 contentRect.origin = SSRandomPointForSizeWithinRect(contentRect.size, frame);
00189
00190 velocity.x = SSRandomFloatBetween(-MAX_V_COMPONENT, MAX_V_COMPONENT);
00191 velocity.y = SSRandomFloatBetween(-MAX_V_COMPONENT, MAX_V_COMPONENT);
00192 }
00193 else {
00194 contentRect = SSCenteredRectInRect(contentRect, frame);
00195 velocity = NSZeroPoint;
00196 }
00197
00198 [super startAnimation];
00199 [self resetClock];
00200 }
00201
00204 - (void) stopAnimation
00205 {
00206 [super stopAnimation];
00207 [deadline release];
00208 deadline = nil;
00209 }
00210
00211
00218 - (void) drawPieCenter: (NSPoint) center proportion: (float) proportion radius: (float) radius color: (NSColor *) color
00219 {
00220 if (proportion <= 0.0 || proportion >= 1.0) {
00221 NSRect bounds = NSMakeRect(center.x - radius, center.y - radius, 2.0 * radius, 2.0 * radius);
00222 NSBezierPath * circle = [NSBezierPath bezierPathWithOvalInRect: bounds];
00223
00224 [color set];
00225 [circle stroke];
00226
00227 if (proportion <= 0.0)
00228 [[NSColor blackColor] set];
00229 [circle fill];
00230 }
00231 else {
00232 NSBezierPath * emptyPart = [NSBezierPath bezierPath];
00233 [emptyPart moveToPoint: center];
00234 [emptyPart lineToPoint: NSMakePoint(center.x, center.y + radius)];
00235
00236 float endAngle = 90.0 + 360.0 * (1.0 - proportion);
00237
00238 [emptyPart appendBezierPathWithArcWithCenter: center radius: radius startAngle: 90.0 endAngle: endAngle];
00239 [emptyPart lineToPoint: center];
00240 [color set];
00241 [emptyPart stroke];
00242 [[NSColor blackColor] set];
00243 [emptyPart fill];
00244
00245 NSBezierPath * filledPart = [NSBezierPath bezierPath];
00246 [filledPart moveToPoint: center];
00247 [filledPart lineToPoint: NSMakePoint(center.x, center.y + radius)];
00248 [filledPart appendBezierPathWithArcWithCenter: center radius: radius startAngle: 90.0 endAngle: endAngle clockwise: YES];
00249 [filledPart lineToPoint: center];
00250 [color set];
00251 [filledPart fill];
00252 }
00253 }
00254
00259 - (void) fillPieImageInner: (float) inner outer: (float) outer
00260 {
00261 NSPoint pieCenter = NSMakePoint(CLOCK_RADIUS + 1.0, CLOCK_RADIUS + 1.0);
00262
00263 [pieImage lockFocus];
00264 [self drawPieCenter: pieCenter
00265 proportion: outer
00266 radius: CLOCK_RADIUS
00267 color: [NSColor blueColor]];
00268 [self drawPieCenter: pieCenter
00269 proportion: inner
00270 radius: CLOCK_RADIUS * 2.0/3.0
00271 color: [NSColor redColor]];
00272 [pieImage unlockFocus];
00273 }
00274
00278 - (void) drawRect: (NSRect) rect
00279 {
00280
00281 [[NSColor blackColor] set];
00282 NSRectFill(rect);
00283
00284 NSRect imageRect = [self addedImageRect];
00285 NSImage * addedImage = [controller addedImage];
00286
00287 if (addedImage) {
00288 NSRect sourceRect;
00289 sourceRect.origin = NSZeroPoint;
00290 sourceRect.size = [addedImage size];
00291
00292 [addedImage drawInRect: imageRect fromRect: sourceRect operation: NSCompositeCopy fraction: 1.0];
00293 }
00294
00295 [pieImage compositeToPoint: NSMakePoint(NSMaxX(imageRect) + 3.0, NSMinY(contentRect)) operation: NSCompositeCopy];
00296 [textImage compositeToPoint: NSMakePoint(NSMaxX(imageRect) + 3.0 + CLOCK_WIDTH,
00297 NSMinY(contentRect))
00298 operation: NSCompositeCopy];
00299 }
00300
00308 - (void) animateOneFrame
00309 {
00310 NSTimeInterval timeDiff = [deadline timeIntervalSinceDate: [NSDate date]];
00311
00312 switch (state) {
00313 case kDOInitial:
00314 state = kDOCountdown;
00315
00316 case kDOCountdown:
00317 {
00318 contentRect.origin.x += velocity.x;
00319 contentRect.origin.y += velocity.y;
00320
00321 NSRect frame = [self frame];
00322
00323 if (NSMaxX(contentRect) >= NSMaxX(frame) || NSMinX(contentRect) <= NSMinX(frame)) {
00324 velocity.x = -velocity.x;
00325 }
00326 if (NSMaxY(contentRect) >= NSMaxY(frame) || NSMinY(contentRect) <= NSMinY(frame)) {
00327 velocity.y = -velocity.y;
00328 }
00329
00330 if (timeDiff <= 0) {
00331 [self fillPieImageInner: 0.0 outer: 0.0];
00332 state = kDOQuitting;
00333 }
00334 else {
00335 if (floor(timeDiff) < secondsLeft) {
00336 secondsLeft = floor(timeDiff);
00337 [self fillTextImageWithSeconds: secondsLeft];
00338
00339 float minutes = floor(timeDiff / 60.0);
00340 float setMinutes = floor([controller secondsBeforeLogout] / 60.0);
00341 float seconds = floor(timeDiff - 60.0 * minutes);
00342
00343 [self fillPieImageInner: minutes/setMinutes outer: seconds/60.0];
00344 }
00345 [self setNeedsDisplay: YES];
00346 }
00347 }
00348 break;
00349
00350 case kDOQuitting:
00351 if ([self isPreview]) {
00352
00353 [self resetClock];
00354 [self setNeedsDisplay: YES];
00355 state = kDOCountdown;
00356 }
00357 else {
00358 [self fillTextImageWithString: [NSString stringWithFormat: @"Logging out. Qutting %d applications.", [DAApplication countApplications]-1]];
00359 [controller quitApplications];
00360 [self setDeadlineForSeconds: 10];
00361 [self setNeedsDisplay: YES];
00362 state = kDOKilling;
00363 }
00364 break;
00365
00366 case kDOKilling:
00367
00368 if (timeDiff <= 0 || [DAApplication countApplications] != 0) {
00369 [self fillTextImageWithString: [NSString stringWithFormat: @"Killing %d applications.", [DAApplication countApplications]-1]];
00370 [self setNeedsDisplay: YES];
00371 [controller killApplications];
00372 state = kDOLogout;
00373 }
00374 break;
00375 case kDOLogout:
00376 [controller makeTouchFile];
00377 [controller logOut];
00378 state = kDODone;
00379 break;
00380 case kDODone:
00381 break;
00382 }
00383 }
00384
00388 - (BOOL) hasConfigureSheet
00389 {
00390 return YES;
00391 }
00392
00396 - (NSWindow *) configureSheet
00397 {
00398 NSBundle * myBundle = [NSBundle bundleForClass: [self class]];
00399 NSDictionary * context = [NSDictionary dictionaryWithObjectsAndKeys: controller, @"NSOwner", nil];
00400
00401
00402 if ([myBundle loadNibFile: @"DownAndOut" externalNameTable: context withZone: [self zone]])
00403 return [controller panel];
00404 else
00405 return nil;
00406 }
00407
00408 @end