This is SpewView.m in view mode; [Download] [Up]
/* $Id$ */
// SpewView is written by Pedja Bogdanovich
// Permission is granted to freely use and distribute this software
// provided this notice is left attached and no monetary gain is made.
#import "SpewView.h"
#import "Thinker.h"
#define TEXTTIMEDEFAULT "Spew TextTime"
#define SLEEPTIMEDEFAULT "Spew SleepTime"
#define MINWIDTHRAT .2
#define MAXWIDTHRAT .75
#define MINCHARS 12
#define MAXFONTRAT .075
#define MINFONTRAT .025
#define MINFONTSIZE 12
#define exp2(x) pow(2,x)
#define rnd() ((random()&0xffffff)/(float)0xffffff)
static const char *fontlist[]={
"Courier", "Courier-Oblique", "Courier-Bold", "Courier-BoldOblique",
"Helvetica", "Helvetica-Oblique", "Helvetica-Bold", "Helvetica-BoldOblique",
"Times-Roman", "Times-Italic", "Times-Bold", "Times-BoldItalic"
// Add more fonts here...
};
static int pointsize[]={
14,18,24,32,36,48,56,64
// Add more font-sizes here...
};
static NXColor pickColor()
{
int i;
float r, g, b;
switch([Window defaultDepthLimit]) {
case NX_TwoBitGrayDepth:
return NXConvertGrayToColor(1.-(random()%2)/3.);
case NX_EightBitGrayDepth:
return NXConvertGrayToColor(1.-(random()%128)/256.);
case NX_TwelveBitRGBDepth:
case NX_TwentyFourBitRGBDepth:
default:
i=random()%4096;
r=fmod(i,16)/16;
g=fmod(i-r,256)/256;
b=(i-fmod(i,256))/4096;
r=.2+r*.8; g=.2+g*.8; b=.2+b*.8; /* Not too dark. */
return NXConvertRGBToColor(r,g,b);
}
}
// fontsizes can be made font specific...
static float pickFontSize(float fmin, float fmax)
{
int i, j=sizeof(pointsize)/sizeof(int);
int imin=j-1, imax=0;
for(i=0;i<j;i++) if(pointsize[i]>=fmin) { imin=i; break; }
for(i=0;i<j;i++) if(pointsize[i]<=fmax) imax=i;
if(imin>=imax) i=imin;
else i=imin+random()%(imax-imin+1);
return pointsize[i];
}
static Font *pickFont(float sz)
{
return [Font newFont:fontlist[random()%(sizeof(fontlist)/sizeof(char*))]
size:sz /* matrix:NX_IDENTITYMATRIX*/];
}
@implementation SpewView
+ initialize
{
static NXDefaultsVector spewDefaults={
{TEXTTIMEDEFAULT,"12"},
{SLEEPTIMEDEFAULT,"2"},
{NULL}};
NXRegisterDefaults([NXApp appName],spewDefaults);
return [super initialize];
}
- initFrame:(const NXRect *)fr
{
NXSize sz={1e6,1e6};
[super initFrame:fr];
textOnScreen=NO;
textView=[[Text alloc] initFrame:NULL];
// [textView setRetainedWhileDrawing:NO];
[textView setVertResizable:YES];
[[textView setHorizResizable:NO] setHorizResizable:NO];
[[textView setSelectable:YES] setEditable:NO];
[textView setMonoFont:YES];
[textView setBackgroundGray:NX_BLACK];
[textView setMaxSize:&sz];
defaultTextTime=atoi(NXGetDefaultValue([NXApp appName],TEXTTIMEDEFAULT));
defaultSleepTime=atoi(NXGetDefaultValue([NXApp appName],SLEEPTIMEDEFAULT));
srandom(time(0));
return self;
}
// spewAgain method will load new spew text into textView.
// textView's text content, font, color, and frame are set.
- spewAgain
{
char buf[200];
char tmpfile[100];
FILE *f;
NXRect r;
static const int alignment[]={NX_LEFTALIGNED,NX_RIGHTALIGNED,NX_CENTERED};
sprintf(tmpfile,"/tmp/.#tmp_spew%08x",getpid());
sprintf(buf,"sh -c 'cd %s; spew >%s'",
[(BSThinker()) moduleDirectory:"Spew"],
tmpfile);
system(buf);
if(f=fopen(tmpfile,"r")) {
struct stat statbuf;
char *spewtext;
float w, h, x, y;
fstat(fileno(f),&statbuf);
spewtext=alloca(statbuf.st_size+1);
fread(spewtext,1,statbuf.st_size,f); spewtext[statbuf.st_size]=0;
fclose(f);
// Random selections below don't work very well for small windows...
// set color
[textView setTextColor:pickColor()];
// set font
h=pickFontSize(MAX(MINFONTSIZE,NX_HEIGHT(&bounds)*MINFONTRAT), /* min */
NX_HEIGHT(&bounds)*MAXFONTRAT); /* max font size */
[textView setFont:pickFont(h)];
// set width
w=MAX(NX_WIDTH(&bounds)*MINWIDTHRAT,MINCHARS*[[textView font] pointSize]);
x=NX_WIDTH(&bounds)*MAXWIDTHRAT;
if(w<x) w+=(x-w)*rnd();
[textView sizeTo:w :50];
// set content
[textView setSel:0 :[textView textLength]];
[textView replaceSel:spewtext];
// set alignment
[textView setAlignment:alignment[random()%(sizeof(alignment)/sizeof(int))]];
// set position; height is calculated by the text object
[textView getFrame:&r];
w=NX_WIDTH(&r); h=NX_HEIGHT(&r);
x=NX_X(&bounds)+(NX_WIDTH(&bounds)-w)*rnd();
y=NX_Y(&bounds)+(NX_HEIGHT(&bounds)-h)*rnd();
[textView moveTo:x :y];
}
unlink(tmpfile);
return self;
}
- drawSelf:(NXRect *)rects :(int)rectCount
{
if(rects==NULL || rectCount==0) return self;
PSsetgray(NX_BLACK); NXRectFill(rects);
return self;
}
- inspector:sender
{
char buf[MAXPATHLEN];
if(!sharedInspectorPanel) {
sprintf(buf,"%s/Spew.nib",[sender moduleDirectory:"Spew"]);
[NXApp loadNibFile:buf owner:self withNames:NO];
// load default values into inspector
[textTimeField setIntValue:defaultTextTime];
[textTimeField sendAction:[textTimeField action] to:[textTimeField target]];
[sleepTimeField setIntValue:defaultSleepTime];
[sleepTimeField sendAction:[sleepTimeField action] to:[sleepTimeField target]];
}
return sharedInspectorPanel;
}
// This method is stolen from BackView
- (BOOL)timePassed:(BStimeval)delay
{
BStimeval now, msec;
BOOL result;
now=currentTimeInMs();
if(SVthen==0) SVthen=now; /* added by shou-h@nexus.or.jp */
msec=now-SVthen;
/* so as not to suck too many cycles, if I'm waiting for some
time more than a tenth of a second in the future, I sleep
a while. This interval is short enough that the app shouldn't
seem unresponsive to user actions.
ok, so you'd never pull this trick if the user had to type.
A better solution would be to coordinate the timed entry better,
but I get slightly better performance from spinning in my
timed entry (a bad idea for most apps...) */
if((msec+120)<delay) {
usleep(110000);
return NO;
}
result=(msec>delay);
if(result) SVthen=now;
return result;
}
- oneStep
{
int t; char buf[12];
if(sharedInspectorPanel) {
t=[textTimeField intValue];
if(t!=defaultTextTime) { /* update default texttime if needed */
sprintf(buf,"%d",t); defaultTextTime=t;
NXWriteDefault([NXApp appName],TEXTTIMEDEFAULT,buf);
}
t=[sleepTimeField intValue];
if(t!=defaultSleepTime) { /* update default sleeptime if needed */
sprintf(buf,"%d",t); defaultSleepTime=t;
NXWriteDefault([NXApp appName],SLEEPTIMEDEFAULT,buf);
}
}
if(textOnScreen) {
if(![self timePassed:defaultTextTime*1000]) return self;
[textView removeFromSuperview];
} else {
if(![self timePassed:defaultSleepTime*1000]) return self;
[self spewAgain];
[self addSubview:textView];
[window makeFirstResponder:textView];
}
textOnScreen=!textOnScreen;
[self display];
return self;
}
@end
/* EOF */These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.