This is DigitalClockView.m in view mode; [Download] [Up]
// // DigitalClockView.m // rev 1.3 // Now supports BackSpace inspector panels // // Matt Pharr- pharr@cs.yale.edu // NeXTMail welcome // #import "DigitalClockView.h" #import <appkit/appkit.h> #import <defaults/defaults.h> #import <dpsclient/wraps.h> #import <libc.h> #import <stdlib.h> #import <strings.h> #import <time.h> #define AM_PM 0 #define MILITARY 1 @implementation DigitalClockView /* Fixes the NXImage so that if there isn't room to display it on the * screen (i.e. it's too big, or the screen/window space is too small) * it still behaves generally nicely. */ - fixPosition { /* If we're doing AM/PM time and a leading 0 was taken off the time * string that strftime returned, let some of the NXImage go off the * edge of the screen before we force it back on. When the leading 0 * is taken off, we have extra room in currentImage, which is a black * area at the right side, so we can let that part go off the screen... */ if (militaryTime == NO && didShorten == YES) { if (currentLocation.x + .9 * currentSize.width >= bounds.size.width) currentLocation.x = bounds.size.width - .9 * currentSize.width; } else { if (currentLocation.x + currentSize.width >= bounds.size.width) currentLocation.x = bounds.size.width - currentSize.width; } if (currentLocation.x <= bounds.origin.x) currentLocation.x = 0; if (currentLocation.y + currentSize.height >= bounds.size.height) currentLocation.y = bounds.size.height - currentSize.height; if (currentLocation.y <= bounds.origin.y) currentLocation.y = 0; return self; } /* This handles bouncing the image- i.e. changing the move vector if needed * when currentImage hits the edge of the screen/window. Note a similar kludge * as in fixPosition that allows for the truncated AM/PM times. */ - bounceIfNeeded { if (currentLocation.x <= bounds.origin.x) moveVector.x= bounceMultiplier * randBetween(0.5, 1.5); if (militaryTime == NO && didShorten == YES) { if (currentLocation.x + .9 * currentSize.width >= bounds.size.width) moveVector.x= bounceMultiplier * (-1) * randBetween(0.5, 1.5); } else { if (currentLocation.x + currentSize.width >= bounds.size.width) moveVector.x= bounceMultiplier * (-1) * randBetween(0.5, 1.5); } if (currentLocation.y <= bounds.origin.y) moveVector.y= bounceMultiplier * randBetween(0.5, 1.5); if (currentLocation.y + currentSize.height >= bounds.size.height) moveVector.y= bounceMultiplier * (-1) * randBetween(0.5, 1.5); return self; } - chooseColor { float dr, dg, db; if ([Window defaultDepthLimit] == NX_TwoBitGrayDepth) { PSsetgray(1.0); /* yipee! */ } else { dr= randBetween(-.1, .1); /* randomly move the red, green, and blue */ dg= randBetween(-.1, .1); /* components of the color around between */ db= randBetween(-.1, .1); /* 0 and 1. There is a minor bummer in that * you only see the effect of the color * change once a second, because it only * draws the time into the bitmap when the * time changes. Would be an easy modification * to get it to redraw the bitmap more * frequently, but its probably not worth * the processor cycles. Depending on how this * ends up looking in color (wish I knew!), it * might be better to replace everything * between the else and the PSsetrgbcolor * with just a PSsetrgbcolor(), with constant * arguments... */ currR += dr; currG += dg; currB += db; if (currR < 0.0) currR= 0.0; if (currR > 1.0) currR= 1.0; if (currG < 0.0) currG= 0.0; if (currG > 1.0) currG= 1.0; if (currB < 0.0) currB= 0.0; if (currB > 1.0) currB= 1.0; PSsetrgbcolor(currR, currG, currB); } return self; } - oneStep { time_t tTime; struct tm *currentTime; /* if a new currentImage has been allocated, most likely the size of the * clock or whether military time is being used or not has changed. Thus, * it's a good idea to clear out the drawing area, so that no little * remnants are left hanging around. */ if (isNew == YES) { PSsetgray(0.0); NXRectFill(&bounds); isNew= NO; } currentLocation.x += moveVector.x; currentLocation.y += moveVector.y; [self bounceIfNeeded]; [self fixPosition]; time(&tTime); currentTime= localtime(&tTime); if (militaryTime == YES) strftime(theTime, 14, "%H:%M:%S", currentTime); else { strftime(theTime, 14, "%I:%M:%S %p", currentTime); if (theTime[0] == '0') { strcpy(theTime, theTime+1); /* take off the leading 0 */ didShorten= YES; } else didShorten= NO; } if (strcmp(theTime, lastTime) != 0) { /* if the time has changed... */ strcpy(lastTime, theTime); currentLocation.x += moveVector.x; /* move it one more time so it's not so jerky */ currentLocation.y += moveVector.y; /* when the time changes... */ [self bounceIfNeeded]; [self fixPosition]; if ([currentImage lockFocus] == YES) { /* draw a new bitmap */ PSsetgray(0.0); PSrectfill(0, 0, currentSize.width, currentSize.height); [theFont set]; PSsetgray(.333); /* give it a little shadow */ PSmoveto(14, clockSize); PSshow(theTime); PSmoveto(10, clockSize + 2); [self chooseColor]; PSshow(theTime); [currentImage unlockFocus]; } } [currentImage composite:NX_COPY toPoint:¤tLocation]; usleep((1*1000000)/68); return self; } - (const char *)windowTitle { return "Digital Clock"; } - drawSelf:(const NXRect *)rects :(int)rectCount { if (!rects || !rectCount) { return self; } PSsetgray(0); NXRectFill(rects); return self; } - newClock { *theTime= '\0'; *lastTime= '\0'; theFont= [Font newFont:"Times-Roman" size:clockSize]; currR= currG= currB= .5; if (militaryTime == YES) { currentSize.width= [theFont getWidthOf:"88:88:88"] + 20; } else { currentSize.width= [theFont getWidthOf:"88:88:88 MM"] + 20; } currentSize.height= clockSize + 20; [currentImage free]; currentImage= [[NXImage alloc] initSize:¤tSize]; [currentImage setFlipped:YES]; isNew= YES; /* make sure oneStep knows it has a new * bitmap to work with, and will clear * the screen as needed... */ return self; } - inspector:sender { char buf[MAXPATHLEN]; if (!sharedInspectorPanel) { sprintf(buf,"%s/%s",[(BSThinker()) moduleDirectory:"DigitalClock"], "DigitalClock.nib"); [NXApp loadNibFile:buf owner:self withNames:NO]; } return sharedInspectorPanel; } - initFrame:(const NXRect *)frameRect { [super initFrame:frameRect]; currentLocation.x= 400.0; currentLocation.y= 400.0; moveVector.x= randBetween(0.5, 1.5); moveVector.y= randBetween(0.5, 1.5); didShorten= NO; [self inspector:self]; /* Must do this ourselves here instead of * letting BackSpace take care of it because * we use the connections defined in there * down below where we sent setFloatValue, etc * to the sliders and other controls. */ if (NXGetDefaultValue([NXApp appName], "digClkViewClockSize") == NULL) { NXWriteDefault([NXApp appName], "digClkViewClockSize", "120.0"); NXWriteDefault([NXApp appName], "digClkViewBounceMult", "1.0"); NXWriteDefault([NXApp appName], "digClkViewMilitary", "NO"); clockSize= 120.0; bounceMultiplier= 1.0; militaryTime= NO; } else { clockSize= atof(NXGetDefaultValue([NXApp appName], "digClkViewClockSize")); if (clockSize < 10.0 || clockSize > 300.0) clockSize= 120.0; bounceMultiplier= atof(NXGetDefaultValue([NXApp appName], "digClkViewBounceMult")); if (bounceMultiplier < .1 || bounceMultiplier > 10.0) bounceMultiplier= 1.0; if (strcmp(NXGetDefaultValue([NXApp appName], "digClkViewMilitary"), "YES") == 0) { militaryTime= YES; } else militaryTime= NO; } [sizeSlider setFloatValue:clockSize]; [sizeSlider update]; [speedSlider setFloatValue:bounceMultiplier]; [speedSlider update]; if (militaryTime == YES) [militarySwitch setState:MILITARY]; else [militarySwitch setState:AM_PM]; [militarySwitch update]; [self newClock]; return self; } - setClockSize:sender { char temp[40]; clockSize= [sender floatValue]; sprintf(temp, "%f", clockSize); NXWriteDefault([NXApp appName], "digClkViewClockSize", temp); [self newClock]; return self; } - setBounceMultiplier:sender { char temp[40]; bounceMultiplier= [sender floatValue]; sprintf(temp, "%f", bounceMultiplier); NXWriteDefault([NXApp appName], "digClkViewBounceMult", temp); /* update the moveVector as needed so that the changes aare reflected * immediately, instead of forcing the user to wait until the clock * bounces off of an edge to see the effect.. */ if (moveVector.x > 0) moveVector.x= bounceMultiplier * randBetween(0.5, 1.5); else moveVector.x= (-1) * bounceMultiplier * randBetween(0.5, 1.5); if (moveVector.y > 0) moveVector.y= bounceMultiplier * randBetween(0.5, 1.5); else moveVector.y= (-1) * bounceMultiplier * randBetween(0.5, 1.5); return self; } - setMilitaryTime:sender { if ([sender state] == MILITARY) { militaryTime= YES; NXWriteDefault([NXApp appName], "digClkViewMilitary", "YES"); } else { militaryTime= NO; NXWriteDefault([NXApp appName], "digClkViewMilitary", "NO"); } [self newClock]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.