This is Controller.m in view mode; [Download] [Up]
/* Controller.m * Part of the Moon application for the NeXT computer. * Author: Geoffrey S. Knauth * Date: January 4, 1992 * * Permission to copy this program is hereby granted under the terms * of the Free Software Foundation's GNU General Public License. */ /* Initially generated by Interface Builder */ #import <strings.h> /* strcpy */ #import <time.h> /* struct tm, gmtime, etc. */ #import <dpsclient/dpsclient.h> /* DPSAddTimedEntry */ #import <appkit/Application.h> /* NX_BASETHRESHOLD */ #import <appkit/Font.h> /* NX_IDENTITYMATRIX */ #import <appkit/publicWraps.h> /* NXBeep */ #import <objc/NXStringTable.h> #import "all.h" #import "Controller.h" #import "MoonView.h" #import "MoonIconView.h" static DPSTimedEntry te = 0; void tick(DPSTimedEntry teNumber, double now, void *userData); @implementation Controller - appDidHide :sender { [iconView display]; return self; } - appDidInit:sender { NXRect iconRect = {{0.0, 0.0}, {64.0, 64.0}}; long t; /* seconds since 0000 UTC 1/1/1970 */ struct tm *tmLcl; char buf[80]; nextNewMoon = 0.0; /* invalidate new moon info */ /* make our icon view be the appIcon window's contentView */ iconView = [[MoonIconView alloc] initFrame:&iconRect]; [[[NXApp appIcon] setContentView:iconView] free]; /* The nowButton should not be enabled until the user has gone into * time travel mode. */ [nowButton setEnabled:NO]; [statForm setTextFont: [Font newFont:"Ohlfs" size:10.0 matrix:NX_FLIPPEDMATRIX]]; /* The local time title must be big enough to hold "Local (GMT+11:30)". */ (void) time(&t); tmLcl = localtime(&t); sprintf(buf, "Local (%s)", tmLcl->tm_zone); [statForm setTitle:buf at:iLocalTime]; /* Make sure the function tick (which sends us an update message) * is called on a regular basis. */ te = DPSAddTimedEntry(TICK_SECONDS, tick, self, NX_BASETHRESHOLD); [self update]; /* update now, in case the wait is long */ return self; } - appWillTerminate :sender { DPSRemoveTimedEntry(te); return self; } - getUserGmtTime :(int *)year :(int *)month :(int *)day :(int *)hour :(int *)minute :(int *)second { char buf[80], monthStr[20]; int i, n, expected, hhmm; /* Here are the kinds of input we can accept: * hh:mm:ss dd mmmmm yyyy %d:%d:%d %d %s %d (6) * hh:mm dd mmmmm yyyy %d:%d %d %s %d (5) * hhmm dd mmmmm yyyy %d %d %s %d (4) * dd mmmmm yyyy %d %s %d (3) * mmmmm yyyy %s %d (2) * yyyy %d (1) */ strcpy(buf, [travelText stringValue]); for (i = n = 0; i < 6; n = 0, i++) { expected = *hour = *minute = *second = *day = *month = *year = 0; switch (i) { case 0: expected = 6; n = sscanf(buf, "%d:%d:%d %d %s %d", hour, minute, second, day, monthStr, year); break; case 1: expected = 5; n = sscanf(buf, "%d:%d %d %s %d", hour, minute, day, monthStr, year); break; case 2: expected = 4; n = sscanf(buf, "%d %d %s %d", &hhmm, day, monthStr, year); break; case 3: expected = 3; n = sscanf(buf, "%d %s %d", day, monthStr, year); break; case 4: expected = 2; n = sscanf(buf, "%s %d", monthStr, year); *day = 1; break; case 5: expected = 1; n = sscanf(buf, "%d", year); *day = 1; strcpy(monthStr, [stringTable valueForStringKey:"1"]); break; } if (n && n == expected) break; } if (n) { *month = [self monthNumFromStr:monthStr]; if (*month == 0) goto bad; if (expected == 4) { *hour = hhmm / 100; *minute = hhmm % 100; } return self; } bad: *hour = *minute = *second = *day = *month = *year = 0; return nil; } - (int)monthNumFromStr :(const char *)month { char monthBuf[2+1]; int i; for (i = 12; i > 0; i--) { sprintf(monthBuf, "%d", i); if (strcmp(month, [stringTable valueForStringKey:monthBuf]) == 0) break; } return i; } - pause :sender { /* We need a pause method, so that when we are coming out of pause, * resuming to run, we can generate an update right away, without * making the user wait for the next official tick. */ if ([sender state] == RUNNING) /* state changed before action sent */ [self update]; return self; } - returnToNow :sender { /* When the nowButton is disabled, we are not in time travel mode. * When are not in time travel mode, update gets its time from the system, * not from the user. */ [sender setEnabled:NO]; /* sender is also nowButton */ [pauseButton setEnabled:YES]; /* by definition we will run */ nextNewMoon = 0.0; /* invalidate new moon info */ [self update]; return self; } - timeTravel :sender { /* When the nowButton is enabled, we are in time travel mode. * When are in time travel mode, update gets its time from the user, * not from the system. */ [nowButton setEnabled:YES]; [pauseButton setEnabled:NO]; /* by definition we will pause */ nextNewMoon = 0.0; /* invalidate new moon info */ [self update]; return self; } - update { long t; /* seconds since 0000 UTC 1/1/1970 */ static long gmtoff = -1L; /* timezone offset from UTC in secs */ struct tm *tmUtc, *tmLcl; /* Unix UTC & local time structures */ float p; /* moon phase: 0=new, 0.5=full */ int lunation; double jd, aom, cphase, cdist, cangdia, csund, csuang, lptime; double phasar[5]; double fakeJd; /* don't use for astro calculations! */ char tbuf[80]; char monthBuf[2+1]; /* "1" .. "12" */ int yy, mm, dd, hh, mmm, ss; /* moon times */ int year, month, day; /* our UTC times */ int hour, minute, second; /* our UTC times */ int lyear, lmonth, lday; /* our local times */ int lhour, lminute, lsecond; /* our local times */ if ([pauseButton state] == PAUSED) return nil; if ([nowButton isEnabled]) { /* We're traveling in time. * This is not the initial state, so gmtoff is legitimate. */ if ([self getUserGmtTime :&year :&month :&day :&hour :&minute :&second] == nil) { NXBeep(); [nowButton setEnabled:NO]; /* no time travel after all */ return nil; } jd = ymdhmsToJtime(year, month, day, hour, minute, second); fakeJd = jd + (double)gmtoff / 86400.; } else { /* We're not traveling in time. Unix gives us our time info. * This is the initial state. */ (void) time(&t); tmUtc = gmtime(&t); jd = jtime(tmUtc); year = tmUtc->tm_year + 1900; month = tmUtc->tm_mon + 1; day = tmUtc->tm_mday; hour = tmUtc->tm_hour; minute = tmUtc->tm_min; second = tmUtc->tm_sec; tmLcl = localtime(&t); fakeJd = jtime(tmLcl); if (gmtoff == -1L) { /* Save the time zone offset for later. We'll need it if the user * does any time traveling, because if we go too far into the past * or future, Unix time functions won't help us any more. */ gmtoff = tmLcl->tm_gmtoff; /* remember offset is in seconds */ } } p = (float)jdtophase(jd, &cphase, &aom, &cdist, &cangdia, &csund, &csuang); /* Draw the big image of the moon and shadow it. */ [moonView setPhase:p]; [moonView display]; /* Draw the little icon image of the moon and shadow it. */ [iconView setPhase:p]; [iconView display]; /* Update textual information */ sprintf(tbuf, "%.5f", jd); /* 1979 January 1.0 <==> JD 2443874.5 */ [statForm setStringValue:tbuf at:iJulianDate]; sprintf(monthBuf, "%d", month); sprintf(tbuf, "%02d:%02d:%02d %d %s %d", hour, minute, second, day, [stringTable valueForStringKey:monthBuf], year); [statForm setStringValue:tbuf at:iUniversalTime]; jyear(fakeJd, &lyear, &lmonth, &lday); jhms(fakeJd, &lhour, &lminute, &lsecond); sprintf(monthBuf, "%d", lmonth); sprintf(tbuf, "%02d:%02d:%02d %d %s %d", lhour, lminute, lsecond, lday, [stringTable valueForStringKey:monthBuf], lyear); [statForm setStringValue:tbuf at:iLocalTime]; sprintf(tbuf, "%d%% %s, %s", (int) rint(cphase * 100), [stringTable valueForStringKey:"visible"], p < 0.5 ? [stringTable valueForStringKey:"waxing"] : [stringTable valueForStringKey:"waning"]); [statForm setStringValue:tbuf at:iMoonPhase]; /* Some information about the Moon */ sprintf(tbuf, "%dd %dh %dm", (int) aom, (int) (24 * (aom - floor(aom))), ((int) (1440 * (aom - floor(aom)))) % 60); [statForm setStringValue:tbuf at:iAgeOfMoon]; sprintf(tbuf, "%ld km, %.1f %s", (long) cdist, cdist / earthrad, [stringTable valueForStringKey:"earthRadii"]); [statForm setStringValue:tbuf at:iMoonDistance]; sprintf(tbuf, "%.4f %s", cangdia, [stringTable valueForStringKey:"degrees"]); [statForm setStringValue:tbuf at:iMoonSubtends]; /* Information about the Sun */ sprintf(tbuf, "%.0f km, %.3f AU", csund, csund / sunsmax); [statForm setStringValue:tbuf at:iSunDistance]; sprintf(tbuf, "%.4f %s", csuang, [stringTable valueForStringKey:"degrees"]); [statForm setStringValue:tbuf at:iSunSubtends]; /* Calculate times of phases of this lunation. This is sufficiently * time-consuming that we only do it once a month, or when we begin or * return from time travel. If we've just changed the time travel mode, * nextNewMoon will be zero. */ if (jd > nextNewMoon) { phasehunt(jd, phasar); lptime = phasar[0]; lunation = floor(((lptime + 7) - lunatbase) / synmonth) + 1; jyear(lptime, &yy, &mm, &dd); jhms(lptime, &hh, &mmm, &ss); sprintf(monthBuf, "%d", mm); sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd, [stringTable valueForStringKey:monthBuf], yy); [statForm setStringValue:tbuf at:iLastNewMoon]; sprintf(tbuf, "%d", lunation); [statForm setStringValue:tbuf at:iThisLunation]; lptime = phasar[1]; jyear(lptime, &yy, &mm, &dd); jhms(lptime, &hh, &mmm, &ss); sprintf(monthBuf, "%d", mm); sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd, [stringTable valueForStringKey:monthBuf], yy); [statForm setStringValue:tbuf at:iFirstQuarter]; lptime = phasar[2]; jyear(lptime, &yy, &mm, &dd); jhms(lptime, &hh, &mmm, &ss); sprintf(monthBuf, "%d", mm); sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd, [stringTable valueForStringKey:monthBuf], yy); [statForm setStringValue:tbuf at:iFullMoon]; lptime = phasar[3]; jyear(lptime, &yy, &mm, &dd); jhms(lptime, &hh, &mmm, &ss); sprintf(monthBuf, "%d", mm); sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd, [stringTable valueForStringKey:monthBuf], yy); [statForm setStringValue:tbuf at:iLastQuarter]; nextNewMoon = phasar[4]; jyear(nextNewMoon, &yy, &mm, &dd); jhms(nextNewMoon, &hh, &mmm, &ss); sprintf(monthBuf, "%d", mm); sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd, [stringTable valueForStringKey:monthBuf], yy); [statForm setStringValue:tbuf at:iNextNewMoon]; sprintf(tbuf, "%d", lunation + 1); [statForm setStringValue:tbuf at:iNextLunation]; } return self; } void tick(DPSTimedEntry teNumber, double now, void *userData) { Controller *self = userData; /* this trick lets C do some Obj-C */ /* We only came here because of the interface from the DPSTimedEntry. * Now let's be more objective. */ if ([self->nowButton isEnabled]) { /* We're in time travel mode. * That means only the user should be able to generate update events. * While in this mode, ticks will be ignored. * We do this so that the user may finish typing a time travel date * and tell us when to read it, otherwise we are likely to read it * prematurely. */ return; } [self update]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.