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.