This is MailWatchView.m in view mode; [Download] [Up]
// // MailWatchView.m // A BackSpace Module // Version 1.1 11/09/94 // by Robert Lutwak robert@amo.mit.edu // // Revision History // // Version 1.1 // // Replaced calls to opendir() and readdir() with stat() on file name. // This way we can catch zero-size mail files left behind // by some mail clients // // Added dwrite for no-mail gray level: dwrite MailWatch DarkGray (0.0-1.0) // // Replaced dumb time-counting loop with call to currentTimeInMS // Added dwrite for when to check (in seconds): dwrite MailWatch TimeToCheck // // Obliterated random color change // Switched to Hue-Saturation-Brightness color method // Now Hue is scaled 0.0-1.0 proportional amount of mail on 0-100KB // // Version 1.2 // // Added "magic" names to list for: "time" "day" "date" // Version 1.3 // Added cool new pref. panel for defaults // Added "hostname" option // Added Energy adjustment // Ya' know, this is just getting to different for minor indices... // // // Version 2.0 // // Restructuring of pretty much everything. // // 2/3/96 Fixed bug in initUsers: which caused an an unitialized user to be // created if the NameList was empty // Added check for MAXU in addName: to avoid writing beyond bounds // of users[]. Now addName: returns -1 if full. // // 2/4/96 Fixed assignment of BOOL users[TAG].active in initFrame: so that // BOOL is assigned 0 or 1 rather than strcmp("YES", "NO") // // 2/7/96 Moved default loading to sizeTo: because BackSpace's "All" // setting doesn't call initFrame: !!! // // 2/24/96 Added check for presence of Help.store in calling program. // If application doesn't support NeXTstep help (like Fiend.app), // don't try to load help supplement, hence avoiding annoying // "Fiend Doesn't support NeXTstep Help" panel. Instead, use // Edit.app to display help files // #import "MailWatchView.h" #import <appkit/appkit.h> #import <strings.h> #import <appkit/Font.h> #import <sys/types.h> #import <sys/stat.h> #import <defaults/defaults.h> #import <math.h> #import <time.h> #import <dpsclient/wraps.h> #import <appkit/FontManager.h> #include <pwd.h> #import "Thinker.h" @implementation MailWatchView - checkMail:(int)tag { struct stat fileinfo; char path[100]; int size; time_t tTime; struct tm *currentTime; if (tag >= NAMETAGSTART) { // Construct full path strcpy(path, MailDirectory); strcat(path, "/"); strcat(path, users[tag].name); // Get file info check size if ( (stat(path, &fileinfo) < 0) || ((size=fileinfo.st_size) == 0) ) { users[tag].HasMail = NO; } else { users[tag].HasMail = YES; if (BW) { users[tag].hue = 1.0; } else { // Change color to indicate amount of Mail users[tag].hue = (double) size / (double)(MaxMail * 1000); if (users[tag].hue > 1.0) users[tag].hue = 1.0; } } } /* Time */ else if (tag == TIMETAG) { time(&tTime); currentTime= localtime(&tTime); strftime(users[tag].name, 30, "%H:%M", currentTime); if (BW) users[tag].hue = 1.0; else users[tag].hue = (float) (currentTime->tm_min) / 60.0; } /* Day */ else if (tag == DAYTAG) { time(&tTime); currentTime= localtime(&tTime); strftime(users[tag].name, 10, "%A", currentTime); if (BW) users[tag].hue = 1.0; else users[tag].hue = (float) (currentTime->tm_wday) / 7.0; } /* Date */ else if (tag == DATETAG) { time(&tTime); currentTime= localtime(&tTime); strftime(users[tag].name, 10, "%m/%d", currentTime); if (BW) users[tag].hue = 1.0; else users[tag].hue = (float) (currentTime->tm_mday) / 31.0; } /* Host */ else if (tag == HOSTTAG) { users[tag].hue += (double)Energy/1.e6; if (users[tag].hue > 1.0) users[tag].hue -= 1.0; } users[tag].rect.size.width = [NameFont[(int)users[tag].HasMail] getWidthOf:users[tag].name]; users[tag].rect.size.height = [NameFont[(int)users[tag].HasMail] pointSize]; users[tag].mass = (float) (users[tag].rect.size.height * users[tag].rect.size.width); return self; } // Elastic collision between two point particles. // Recoil velocities are calculated by simultaneously solving: // m1v1 + m2v2 = m1v1' + m2v2' (momentum conservation) // and // m1 v1^2 + m2 v2^2 = m1 (v1')^2 + m2 (v2')^2 (energy conservation) // - collide:(int)i1:(int)i2 { float newv1, newv2; float m1, m2, v2, v1; m1 = users[i1].mass; m2 = users[i2].mass; v1 = users[i1].v.x; v2 = users[i2].v.x; newv1 = v2 * 2.0 * m2 / (m2 + m1) - v1 * (m2 - m1) / (m2 + m1); newv2 = v2 * (m2 - m1) / (m2 + m1) + v1 * 2.0 * m1 / (m2 + m1); users[i1].v.x = newv1; users[i2].v.x = newv2; v1 = users[i1].v.y; v2 = users[i2].v.y; newv1 = v2 * 2.0 * m2 / (m2 + m1) - v1 * (m2 - m1) / (m2 + m1); newv2 = v2 * (m2 - m1) / (m2 + m1) + v1 * 2.0 * m1 / (m2 + m1); users[i1].v.y = newv1; users[i2].v.y = newv2; return self; } - move:(int)u { int i; // Take a step NXOffsetRect(&users[u].rect, users[u].v.x, users[u].v.y); // Check for Elastic Collisions for (i=0; i < numusers; ++i) { if (i == u) continue; if (!users[i].active) continue; if (NXIntersectsRect(&users[i].rect,&users[u].rect)) { NXOffsetRect(&users[u].rect, -1.0*users[u].v.x, -1.0*users[u].v.y); [self collide:i:u]; NXOffsetRect(&users[u].rect, users[u].v.x, users[u].v.y); } } // Specular reflection from walls if ( (users[u].rect.origin.x <= bounds.origin.x) || ((users[u].rect.origin.x+users[u].rect.size.width) >= bounds.size.width) ) { NXOffsetRect(&users[u].rect, -2.0*users[u].v.x, 0.0); users[u].v.x *= -1.0; } if ( ((users[u].rect.origin.y-users[u].rect.size.height) <= bounds.origin.y) || (users[u].rect.origin.y >= bounds.size.height) ) { NXOffsetRect(&users[u].rect, 0.0, -2.0 * users[u].v.y); users[u].v.y *= -1.0; } return self; } - oneStep { int i; BStimeval CurrentTime; int checkIt=0; // Check for Mail if it's time CurrentTime = currentTimeInMs(); if ((CurrentTime - LastTime) > TimeToCheck) { checkIt = 1; LastTime = CurrentTime; } for (i=0; i < numusers ; ++i) { if (!users[i].active) continue; // Erase last iteration [self eraseName:i]; if (checkIt) [self checkMail:i]; // Move as necessary [self move:i]; [self drawName:i]; } return self; } - eraseName: (int)u { PSsetgray(0.0); PSmoveto(users[u].rect.origin.x,users[u].rect.origin.y); [NameFont[(int)users[u].HasMail] set]; PSshow(users[u].name); return self; } - drawName: (int) tag { // Set Color if (users[tag].HasMail) { if (BW) PSsetgray(users[tag].hue); else PSsethsbcolor(users[tag].hue, 1.0, 1.0); } else { PSsetgray(Gray); } [NameFont[(int)users[tag].HasMail] set]; // Draw this iteration PSmoveto(users[tag].rect.origin.x,users[tag].rect.origin.y); PSshow(users[tag].name); return self; } - (const char *)windowTitle { return "MailWatch"; } - drawSelf:(const NXRect *)rects :(int)rectCount { if (!rects || !rectCount) return self; PSsetgray(0); NXRectFill(rects); return self; } - sizeTo:(NXCoord)width :(NXCoord)height { // if (!alreadyInitialized) [self initialize]; int fontsize; static NXDefaultsVector MailWatchDefaults = { {"NameList", ""}, {"MailDirectory", "/usr/spool/mail"}, {"Gray", "0.3"}, {"MaxMail", "100"}, {"TimeToCheck", "30"}, {"NoMailFont", "Times-Roman"}, {"MailFont", "Times-Italic"}, {"NoMailFontSize", "24"}, {"MailFontSize", "24"}, {"Time", "YES"}, {"Date", "YES"}, {"Day", "YES"}, {"Host", "YES"}, {"Energy", "10000"}, {NULL} }; [super sizeTo:width :height]; // Load Defaults: NXRegisterDefaults("MailWatch", MailWatchDefaults); strncpy(NameList, NXGetDefaultValue("MailWatch", "NameList"), 1000); users[TIMETAG].active = (strcmp(NXGetDefaultValue("MailWatch","Time"),"YES") == 0); users[DAYTAG].active = (strcmp(NXGetDefaultValue("MailWatch","Day"),"YES") == 0); users[DATETAG].active = (strcmp(NXGetDefaultValue("MailWatch","Date"),"YES") == 0); users[HOSTTAG].active = (strcmp(NXGetDefaultValue("MailWatch","Host"),"YES") == 0); strncpy(MailDirectory, NXGetDefaultValue("MailWatch", "MailDirectory"), 100); Gray = atof(NXGetDefaultValue("MailWatch","Gray")); MaxMail = atoi(NXGetDefaultValue("MailWatch","MaxMail")); fontsize = atoi(NXGetDefaultValue("MailWatch", "MailFontSize")); NameFont[YESMAIL] = [Font newFont:NXGetDefaultValue("MailWatch", "MailFont") size:fontsize]; fontsize = atoi(NXGetDefaultValue("MailWatch", "NoMailFontSize")); NameFont[NOMAIL] = [Font newFont:NXGetDefaultValue("MailWatch", "NoMailFont") size:fontsize]; Energy = atoi(NXGetDefaultValue("MailWatch", "Energy")); TimeToCheck = 1000 * atoi(NXGetDefaultValue("MailWatch","TimeToCheck")); LastTime = 0; // [self setFlipped:YES]; // [inspectorPanel display]; // Determine whether to use color or BW if ([Window defaultDepthLimit] == NX_TwoBitGrayDepth) BW = 1; // alreadyInitialized = YES; [self initUsers]; return self; } - initFrame:(const NXRect *)frameRect { [super initFrame:frameRect]; [self allocateGState]; // For faster lock/unlockFocus // [self initialize]; return self; } - initUsers { char *pn, *un, name[30]; BOOL done; int tag; numusers = 0; for (tag=0; tag < NAMETAGSTART; ++tag) { sprintf(name, "tag%d", tag); tag = [self addName:name]; } pn = NameList; done = NO; while (!done) { while (*pn == ' ') ++pn; un = name; while(*pn != ' ') { if (*pn == 0) { done = YES; break; } *un++ = *pn++; } *un = 0; if (*name != 0) { tag = [self addName:name]; if (tag < 0) return self; users[tag].active = YES; } } return self; } //- (BOOL) useBufferedWindow{ return YES;} - inspector:sender { char buf[MAXPATHLEN+1]; int tag; if (!inspectorPanel) { // Load NIB [NXBundle getPath:buf forResource:"MailWatchView" ofType:"nib" inDirectory:[(BSThinker()) moduleDirectory:"MailWatch"] withVersion:0 ]; [NXApp loadNibFile:buf owner:self withNames:NO ]; // Load Help if (helpPanel == nil) { // Check for NeXTstep help system if ([NXBundle getPath:buf forResource:"Help" ofType:"store" inDirectory:[(BSThinker()) appDirectory] withVersion:0 ]) { helpPanel = [NXHelpPanel new]; sprintf(buf, "%s/English.lproj", [(BSThinker()) moduleDirectory:"MailWatch"]); [helpPanel addSupplement:"Help" inPath:buf]; } } // Initialization // Settings Panel // Energy [EnergySlider setIntValue:(int)sqrt((double)Energy)]; // Checking for Mail [MailDirectoryText setStringValue:MailDirectory]; [TimeToCheckSlider setIntValue:(TimeToCheck/1000)]; [TimeToCheckText setIntValue:(TimeToCheck/1000)]; // Colors [GraySlider setFloatValue:Gray]; sprintf(buf, "%3.1f", Gray); [GrayText setStringValue:buf]; [MaxMailSlider setIntValue:MaxMail]; [MaxMailText setIntValue:MaxMail]; // NameList // NameBrowser [NameBrowser setDelegate:self]; [NameBrowser setDoubleAction:@selector(browserChanged:)]; [NameBrowser setTarget:self]; // NameButtons for (tag=0; tag < NAMETAGSTART; ++tag) { [[NameButtons findCellWithTag:tag] setState:users[tag].active]; } } return inspectorPanel; } - setEnergy:sender { char buf[10]; int i; double Eold; double factor; Eold = Energy; Energy = [sender intValue] * [sender intValue]; sprintf(buf, "%d", Energy); NXWriteDefault("MailWatch", "Energy", buf); factor = sqrt(Energy/Eold); for (i=0; i < numusers; ++i) { users[i].v.x *= factor; users[i].v.y *= factor; } return self; } - setTimeToCheck:sender { char buf[10]; int Secs; Secs = [sender intValue]; sprintf(buf, "%d", Secs); NXWriteDefault("MailWatch", "TimeToCheck", buf); [TimeToCheckSlider setIntValue:Secs]; [TimeToCheckText setIntValue:Secs]; TimeToCheck = Secs * 1000; /* in milliseconds */ return self; } - clearNames { int i; id myView; [self lockFocus]; myView = [(BSThinker()) backView]; for (i=0; i < numusers; ++i) [myView eraseName:i]; [self unlockFocus]; return self; } - NameButtonsChanged:sender { id selectedButton; int tag; BOOL State; selectedButton = [sender selectedCell]; State = [selectedButton state]; tag = [selectedButton tag]; users[tag].active = State; NXWriteDefault("MailWatch", [selectedButton title], State ? "YES" : "NO"); [self clearNames]; return self; } - browserChanged:sender { id cell; char title[20]; int tag; cell = [sender selectedCell]; strcpy(title, [cell stringValue]); tag = [self addName:title]; if (tag < 0) return self; if (users[tag].active) { users[tag].active = NO; [cell setImage:[[NXImage findImageNamed:"CheckOff"] copy]]; } else { users[tag].active = YES; [cell setImage:[[NXImage findImageNamed:"CheckOn"] copy]]; } [sender displayAllColumns]; *NameList = 0; for (tag=NAMETAGSTART; tag < numusers; ++tag) if (users[tag].active) sprintf(NameList, "%s %s", NameList, users[tag].name); NXWriteDefault("MailWatch", "NameList", NameList); // Don't leave shadows [self clearNames]; return self; } - setMailDirectory:sender { strncpy(MailDirectory, [sender stringValue], 100); NXWriteDefault("MailWatch", "MailDirectory", MailDirectory); return self; } -(int)browser:sender fillMatrix:(id)matrix inColumn:(int)col { struct passwd *pwentry; int row; id newCell; int tag; if (!CheckOn || !CheckOff) [self initImages]; setpwent(); for (row=0; (pwentry = getpwent()) != NULL;) { if (!strcmp(pwentry->pw_name, "uucp")) continue; if (!strcmp(pwentry->pw_name, "nobody")) continue; if (!strcmp(pwentry->pw_name, "agent")) continue; if (!strcmp(pwentry->pw_name, "daemon")) continue; if (!strcmp(pwentry->pw_name, "sybase")) continue; [matrix addRow]; newCell = [matrix cellAt:row:col]; [newCell initTextCell:pwentry->pw_name]; [newCell setLoaded:YES]; [newCell setLeaf:YES]; for (tag=NAMETAGSTART; tag < numusers; ++tag) { if (!strcmp(users[tag].name, pwentry->pw_name)) break; } if (tag != numusers) [newCell setImage:[[NXImage findImageNamed:"CheckOn"] copy]]; else [newCell setImage:[[NXImage findImageNamed:"CheckOff"] copy]]; ++row; } endpwent(); return(row); } - initImages { CheckOn = [NXImage findImageNamed:"CheckOn"]; CheckOff = [NXImage findImageNamed:"CheckOff"]; return self; return self; } // Returns tag of name // Creating new entry if necessary - (int)addName:(char *)newName { BOOL bad; int j; double v, vx, vy; int tag; // Check to see if it already exists for (tag=0; tag < numusers; ++tag) { if (!strcmp(users[tag].name, newName)) return tag; } // Check to make sure we've got room if (numusers >= MAXU) return(-1); // Gotta create it // fprintf(stderr, "Creating Name %s\n", newName); strcpy(users[tag].name, newName); if (tag < NAMETAGSTART) { users[tag].HasMail = YES; if (tag == HOSTTAG) gethostname(users[HOSTTAG].name, NAMELEN); } else { users[tag].active = NO; } [self checkMail:tag]; // Find an unoccupied place to put it: bad = YES; while (bad) { users[tag].rect.origin.x = randBetween(bounds.origin.x, bounds.size.width-users[tag].rect.size.width); users[tag].rect.origin.y = randBetween(bounds.origin.y+users[tag].rect.size.height, bounds.size.height-users[tag].rect.size.height); for (bad=NO,j=tag-1; j >= 0; --j) { if (NXIntersectsRect(&users[j].rect, &users[tag].rect)) bad = YES; } } v = sqrt(2 * Energy / users[tag].mass); vx = randBetween(0, v); vy = sqrt (v*v - vx * vx); users[tag].v.x = vx; users[tag].v.y = vy; ++numusers; return tag; } - inspectorWillBeRemoved { if (SettingsPanel) [SettingsPanel orderOut:self]; if (NameListInspector) [NameListInspector orderOut:self]; if (InfoPanel) [InfoPanel orderOut:self]; if (fontPanel) [fontPanel orderOut:self]; if (helpPanel) [helpPanel orderOut:self]; return self; } - helpPushed:sender { char HelpPath[MAXPATHLEN + 1]; sprintf(HelpPath, "%s/English.lproj/Help/Overview.rtfd", [(BSThinker()) moduleDirectory:"MailWatch"]); // If NeXTstep help is loaded, use it if (helpPanel) { [helpPanel makeKeyAndOrderFront:self]; [helpPanel showFile:HelpPath atMarker:NULL]; } // Otherwise, use Edit.app else { [[Application workspace] openFile:HelpPath withApplication:"Edit"]; } return self; } - fontPushed:sender { if (fontManager == nil) fontManager = [FontManager new]; [fontManager setSelFont:NameFont[(int)settingMailFont] isMultiple:NO]; if (fontPanel == nil) { fontPanel = [fontManager getFontPanel:YES]; [fontPanel setAccessoryView:fontPopUpView]; } [fontPanel setDelegate:self]; [fontPanel makeKeyAndOrderFront:self]; return self; } - changeFont:sender { char buf1[20], buf2[20]; [self clearNames]; NameFont[(int)settingMailFont] = [fontManager convertFont:NameFont[(int)settingMailFont]]; /* Either "MailFont" or "NoMailFont" */ strcpy(buf1, [[fontPopUp findCellWithTag:(int)settingMailFont] title]); NXWriteDefault("MailWatch", buf1, [NameFont[(int)settingMailFont] name]); /* Make that "MailFontSize" or "NoMailFontSize" */ strcat(buf1, "Size"); sprintf(buf2, "%g", [NameFont[(int)settingMailFont] pointSize]); NXWriteDefault("MailWatch", buf1, buf2); return self; } - fontPopUpChanged:sender { settingMailFont = [[sender selectedCell] tag]; [fontManager setSelFont:NameFont[(int)settingMailFont] isMultiple:NO]; return self; } - setGray:sender { char buf[10]; Gray = [sender floatValue]; sprintf(buf, "%3.1f", Gray); [GrayText setStringValue:buf]; NXWriteDefault("MailWatch", "Gray", buf); return self; } - setMaxMail:sender { char buf[30]; MaxMail = [sender intValue]; [MaxMailText setIntValue:MaxMail]; sprintf(buf, "%d", MaxMail); NXWriteDefault("MailWatch", "MaxMail", buf); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.