This is FortuneView.m in view mode; [Download] [Up]
#import "FortuneView.h" #import <math.h> #import <ctype.h> #import "Thinker.h" // this code is copyright Darcy Brockbank, 1994 // // You may freely reuse and distribute this code in any way shape or // form, provided that this notice stays intact. // // darcy@hasc.ca, samurai@cs.mcgill.ca // // If you do improve this thing, send me a copy! // // - darcy // v1.3 (Joe Reiss) <jreiss@magnus.acs.ohio-state.edu> // + Converted database selector from radio buttons to popup list // + Moved list of databases from source code to disk file // + Added Babylon 5 quotes #define INCLUDED (char)0 #define EXCLUDED (char)1 #define SHADOW 0 #define HIGHLIGHT 1 #define PLAIN 2 #define SPEED "FortuneSpeed" #define USECOLOR "FortuneUseColor" #define F_FILE "FortuneFile" #define BACKING "FortuneBacking" #define CLEARAFTER "FortuneClearAfter" #define EXFONTS "FortuneExcludedFonts" #define ROT(c) ((isupper(c))? ('A' + (c - 'A' + 13) % 26) : \ ((islower(c)) ? ('a' + (c - 'a' + 13) % 26) : c)) @interface ThreeField : TextField { int drawBacking; } - setDrawBacking:(int)flag; - drawSelf:(const NXRect *)r :(int)c; @end @implementation ThreeField - setDrawBacking:(int)flag; { drawBacking = flag; return self; } - drawSelf:(const NXRect *)r :(int)c; { NXColor realColor = [self textColor]; NXRect hoffset=*r; if (strlen([self stringValue])==0) { NXSetColor(NX_COLORBLACK); NXRectFill(r); } else if (drawBacking==HIGHLIGHT) { hoffset.origin.x+=1.0; hoffset.origin.y+=1.0; [cell setTextColor:NX_COLORWHITE]; [cell drawInside:&hoffset inView:self]; [cell setTextColor:realColor]; [cell drawInside:r inView:self]; } else { [cell drawInside:r inView:self]; } return self; } @end @interface GrayCell : NXBrowserCell { BOOL isGray; } - setGrayed:(BOOL)flag; @end @implementation GrayCell - setGrayed:(BOOL)flag; { isGray = flag; return self; } - setTextAttributes:sender; { [super setTextAttributes:sender]; if (isGray) [sender setTextGray:NX_DKGRAY]; return self; } @end @implementation FortuneView #define MINX 0.0 #define MINY 0.0 #define SIZE 36.0 typedef struct fortDB { char *title; char *fname; int offend; struct fortDB *next; } FORTDB; FORTDB *fdbs=NULL; - newDatabase:sender; { FORTDB *walk; int sel = [[sender selectedCell] tag],i; for (i=0,walk=fdbs; i<sel; i++,walk=walk->next) ; if (walk->offend) { if (NXRunAlertPanel( "Be sure...", "This option will print fortune entries on the screen " "which some people will find offensive (hence the name). If you " "are offended by offcolor jokes, or sexually explicit remarks, " "or are easily offended at all, then I suggest you not use this option. " "The fact that these fortunes are included as part of this screen " "saver does not imply that I condone or agree with any of them. " "They are included since they were part of the Berkeley 'fortune' package.", "Continue","Cancel Selection",0)==NX_ALERTALTERNATE) { [databaseTrigger setTitle: [[[databaseMatrix itemList] findCellWithTag:fortuneFile] title]]; [[databaseMatrix itemList] selectCellWithTag:fortuneFile]; return self; } } fortuneFile = sel; [self loadWords:walk->fname]; [self perform:@selector(writeFileDefault:) with:self afterDelay:1000 cancelPrevious:YES]; [self lockFocus]; [self doDrawing]; [self unlockFocus]; return self; } - writeThresholdDefault:sender { char tmp[128]; sprintf(tmp,"%u",threshold); NXWriteDefault([NXApp appName],SPEED,tmp); return self; } - writeColorDefault:sender { char tmp[128]; sprintf(tmp,"%s",(useColor)?"YES":"NO"); NXWriteDefault([NXApp appName],USECOLOR,tmp); return self; } - writeFileDefault:sender { char tmp[128]; sprintf(tmp,"%d",fortuneFile); NXWriteDefault([NXApp appName],F_FILE,tmp); return self; } - writeBackingDefault:sender { char tmp[128]; sprintf(tmp,"%d",gray); NXWriteDefault([NXApp appName],BACKING,tmp); return self; } - setThreshold:sender { threshold = [sender intValue]; [self perform:@selector(writeThresholdDefault:) with:self afterDelay:1000 cancelPrevious:YES]; return self; } - setSpeedSlider:sender; { speedSlider = sender; [sender setIntValue:threshold]; return self; } - loadFonts; { fontNames = [[FontManager new] availableFonts]; if (fontNames) { for(numFonts = 0;fontNames[numFonts];numFonts++) ; } return self; } - loadWords:(const char *)database; { char buf[MAXPATHLEN]; if (fortuneStream) { NXCloseMemory(fortuneStream,NX_FREEBUFFER); } sprintf(buf,"%s/db/%s",[appowner moduleDirectory:"Fortune"],database); fortuneStream = NXMapFile(buf,NX_READONLY); if (fortuneStream) { NXGetMemoryBuffer(fortuneStream,&words,&len,&maxlen); } else { NXRunAlertPanel("File not found.", "I couldn't locate the fortune database " "'%s'",NULL,NULL,"OK",buf); } return self; } - (BOOL)isExcluded:(const char *)fontName { int i,count = numFonts; for(i=0;i<count;i++) { if (strcmp(fontName,fontNames[i])==0) { if (excluded[i]==EXCLUDED) { return YES; } else { break; } } } return NO; } - prepFont { float size = SIZE; int fn; fn = ((unsigned)random()) % numFonts; while([self isExcluded:fontNames[fn]]) { fn++; if (!fontNames[fn]) fn=0; } fontNum = fn; font = [Font newFont:fontNames[fn] size:size matrix:NX_FLIPPEDMATRIX]; return self; } - drawSelf:(const NXRect *)r :(int)c { if (!r || !c || c==2) return self; NXSetColor(NX_COLORBLACK); NXRectFill(r); [self doDrawing]; return self; } - prepColor; { if (useColor) { blue = ((float)((unsigned)random()))/(float)INT_MAX; red = ((float)((unsigned)random()))/(float)INT_MAX; green = ((float)((unsigned)random()))/(float)INT_MAX; } else { blue = red = green = (((unsigned)random())%3) / 3.0 + (1.0/3.0); } currentColor = NXConvertRGBToColor(red,blue,green); return self; } - prepPosition; { xpos = (((unsigned)random()) % (unsigned)((maxCoord.x-MINX))) +MINX; ypos = (((unsigned)random()) % (unsigned)((maxCoord.y-MINY))) +MINY; return self; } - prepWord; { FORTDB *walk; unsigned i = ((unsigned)random()) % (maxlen-1); int j,n; numWords = 0; do { i--; for(i=(i)?i:i-1;(words[i]!='\n' && i);i--); } while (i && words[i-1]!='%'); for(j=0,i++;i<maxlen && !(words[i]=='\n' && words[i+1]=='%');i++,j++) { if (j>=maxWordLen) { if (!maxWordLen) maxWordLen = 256; currentWord = realloc(currentWord,maxWordLen*2); maxWordLen*=2; } for (n=0,walk=fdbs; n<fortuneFile; n++,walk=walk->next) ; if (walk->offend) { currentWord[j]=ROT(words[i]); } else { currentWord[j]=words[i]; } if (currentWord[j]==' ' || currentWord[j]=='\t') { numWords++; } } currentWord[j]='\0'; return self; } - prep; { [self prepFont]; [self prepColor]; [self prepPosition]; [self prepWord]; return self; } - setGray:sender { gray = [sender state]; [fe setDrawBacking:gray]; [self perform:@selector(writeBackingDefault:) with:self afterDelay:1000 cancelPrevious:YES]; [self lockFocus]; [self doDrawing]; [self unlockFocus]; return self; } - useColor:sender; { useColor = [sender state]; [self perform:@selector(writeColorDefault:) with:self afterDelay:1000 cancelPrevious:YES]; return self; } - setHighlightSwitch:sender { highlightSwitch = sender; [highlightSwitch setState:gray]; return self; } static void do_size(void) { #ifdef TEST1 system("ps -u | grep BackSpace.app | grep -v grep | awk ' { print \"Size: V=\"$5\", R=\"$6 } '"); #endif } - setDatabaseMatrix:sender; { databaseMatrix = sender; [[databaseMatrix itemList] selectCellWithTag:fortuneFile]; [databaseTrigger setTitle: [[[databaseMatrix itemList] findCellWithTag:fortuneFile] title]]; return self; } - doDrawing; { static const char * oldCurrent = 0; NXRect dr = bounds; do_size(); [self prep]; [window disableFlushWindow]; [fe setStringValue:""]; [fe display]; dr = bounds; dr.size.width-=10.0; [fe setFrame:&dr]; [window disableDisplay]; [fe setTextColor:currentColor]; while(1){ int sz = [font pointSize]; if (sz<=2) { /* * problem in NeXT's stuff it seems. Too many * tabs in the TextFields will make the width * impossible to calculate. If this happens, * then we just try to do our best. */ font = [Font newFont:fontNames[fontNum] size:8 matrix:[font matrix]]; [fe setFrame:&bounds]; [fe setFont:font]; #ifdef TEST2 printf("%s\n",currentWord); #endif break; } #ifdef TESTING [fe getFrame:&dr]; printf("Original font: (%s,%d) \t: {{%.0f,%.0f},{%.0f,%.0f}}\n", [font familyName],sz,dr.origin.x,dr.origin.y,dr.size.width,dr.size.height); #endif [fe setFont:font]; [fe setStringValueNoCopy:currentWord]; [fe sizeToFit]; [fe getFrame:&dr]; #ifdef TESTING printf("Sized to fit ================\t: {{%.0f,%.0f},{%.0f,%.0f}}\n", dr.origin.x,dr.origin.y,dr.size.width,dr.size.height); #endif if (dr.size.width>(bounds.size.width-10.0)) { font = [Font newFont:fontNames[fontNum] size:sz-2 matrix:[font matrix]]; } else if (dr.size.height>=bounds.size.height) { font = [Font newFont:fontNames[fontNum] size:sz-2 matrix:[font matrix]]; } else { break; } } [window reenableDisplay]; [fe moveTo:(bounds.size.width-dr.size.width)/2.0 :(bounds.size.height-dr.size.height)/2.0]; [fe display]; [[window reenableFlushWindow] flushWindow]; oldCurrent = currentWord; return self; } - oneStep { static unsigned lastTime = 0; unsigned thisTime = currentTimeInMs(); if (thisTime-lastTime<(threshold*numWords)) return self; lastTime = thisTime; if (!appowner) return self; [window makeFirstResponder:fe]; [self doDrawing]; return self; } - (BOOL)useBufferedWindow { return YES; } - allocTextObject; { id tf = [[ThreeField alloc] initFrame:&frame]; [tf allocateGState]; [tf setClipping:NO]; [tf setBezeled:NO]; [tf setBordered:NO]; // [tf setBackgroundGray:NX_BLACK]; [tf setBackgroundTransparent:YES]; [tf setAutodisplay:NO]; [tf setEditable:NO]; [tf setSelectable:YES]; [self addSubview:tf]; return tf; } - setup; { FORTDB *walk; char buf[MAXPATHLEN]; int i; appowner = BSThinker(); sprintf(buf,"%s/FortuneView.nib",[appowner moduleDirectory:"Fortune"]); [NXApp loadNibFile:buf owner:self withNames:NO]; if (fdbs == NULL) [self loadDBList]; for (i=0,walk=fdbs; i<fortuneFile; i++,walk=walk->next) ; [self loadWords:walk->fname]; [speedSlider setIntValue:threshold]; [highlightSwitch setState:gray]; fe = [self allocTextObject]; [fe setDrawBacking:gray]; [colorSwitch setState:useColor]; [availableBrowser setCellClass:[GrayCell class]]; [[databaseMatrix itemList] selectCellWithTag:fortuneFile]; [databaseTrigger setTitle: [[[databaseMatrix itemList] findCellWithTag:fortuneFile] title]]; [databaseMatrix setAction:@selector(newDatabase:)]; [databaseMatrix setTarget:self]; return self; } - loadDBList; { char buf[MAXPATHLEN]; NXStream *dblistStream; char *list; int listlen,listmlen; FORTDB *walk,*prev; int i,n; sprintf(buf,"%s/FortuneView.dblist", [appowner moduleDirectory:"Fortune"]); dblistStream = NXMapFile(buf,NX_READONLY); if (dblistStream) { NXGetMemoryBuffer(dblistStream,&list,&listlen,&listmlen); } else { NXRunAlertPanel("File not found.", "I couldn't locate the fortune database list", NULL,NULL,"OK",buf); return self; } prev = walk = fdbs; n = 0; /* FIXME */ for (i=0; i<listmlen; ) { prev=walk; walk=(FORTDB *)malloc(sizeof(FORTDB)); walk->next = NULL; if (prev == NULL) fdbs = walk; else prev->next = walk; if (walk->offend = (list[i] == '?')) i++; walk->fname=list+i; while (!isspace(list[i])) // Get past the file name i++; list[i++] = '\0'; // Null terminate filename while (isspace(list[i])) // Skip any whitespace after file i++; walk->title=list+i; while (i<listmlen && list[i] != '\n') // Go to end of line i++; list[i] = '\0'; // Null terminate title i++; [[databaseMatrix addItem:walk->title] setTag:n++]; } if (fdbs == NULL) [databaseTrigger setEnabled:NO]; else [databaseMatrix removeItemAt:0]; return self; } #define STRDUP(a) ((a)?(strcpy(malloc((strlen(a)+1)*sizeof(char)),a)):0) - writeExcludedDefault:sender { int i,length,count = numFonts; char * def; char * out; for(length=i=0;i<count;i++){ if (excluded[i] == EXCLUDED){ length += (strlen(fontNames[i])+4); } } def = malloc((length+1) * sizeof(char)); *def='\0'; for(out=def,length=i=0;i<count;i++){ if (excluded[i] == EXCLUDED){ const char * current = fontNames[i]; length = strlen(current); strcat(out,current); if (i<(count-1)) strcat(out,"; "); out+=length+2; } } NXWriteDefault([NXApp appName],EXFONTS,def); free(def); return self; } - showFonts:sender; { [fontWindow makeKeyAndOrderFront:self]; return self; } - (int)browser:sender fillMatrix:matrix inColumn:(int)col; { int i; [matrix renewRows:0 cols:0]; /* * I've had some trouble doing this through the browser. */ [[matrix setPrototype:[[GrayCell alloc] init]] free]; for(i=0; fontNames[i]; i++){ id cell; [matrix addRow]; cell = [matrix cellAt:i:0]; [cell setStringValueNoCopy:fontNames[i]]; [cell setGrayed:[self isExcluded:fontNames[i]]]; [cell setLoaded:YES]; [cell setLeaf:YES]; } return i; } - setAvailableBrowser:sender; { availableBrowser = sender; [sender setTarget:self]; [sender setDoubleAction:@selector(toggleFont:)]; return self; } - toggleFont:sender; { id matrix = [availableBrowser matrixInColumn:0]; id cell = [matrix cellAt:[matrix selectedRow]:0]; if ([self isExcluded:[cell stringValue]]){ [self removeFont:self]; } else { [self addFont:self]; } return self; } /* * ADD to the excluded list! */ - addFont:sender; { id matrix = [availableBrowser matrixInColumn:0]; id cell = [matrix cellAt:[matrix selectedRow]:0]; int i,count; for(count=i=0;i<numFonts;i++) count+=(excluded[i]==INCLUDED); if (count==1) { NXRunAlertPanel("Hey!", "You have to keep at least one font around!",0,0,0); } else { excluded[[matrix selectedRow]]=EXCLUDED; [[availableBrowser window] disableFlushWindow]; [cell setGrayed:YES]; [availableBrowser display]; [[[availableBrowser window] reenableFlushWindow] flushWindow]; [self perform:@selector(writeExcludedDefault:) with:self afterDelay:1000 cancelPrevious:YES]; } return self; } /* * REMOVE from the excluded list! */ - removeFont:sender; { int col = [availableBrowser selectedColumn]; if (col==0) { id matrix = [availableBrowser matrixInColumn:col]; int row = [matrix selectedRow]; if (row>=0){ excluded[row]=INCLUDED; [self perform:@selector(writeExcludedDefault:) with:self afterDelay:1000 cancelPrevious:YES]; [[availableBrowser window] disableFlushWindow]; [[matrix cellAt:row:0] setGrayed:NO]; [availableBrowser display]; [[[availableBrowser window] reenableFlushWindow] flushWindow]; } } return self; } - excludeFont:(const char *)p; { int i; for(i=0;fontNames[i];i++){ if (strcmp(p,fontNames[i])==0){ excluded[i]=EXCLUDED; break; } } return self; } - setExcludedFonts:(const char *)list; { char array[strlen(list)+1]; char * p; int i; excluded = (char *)malloc(sizeof(char)*numFonts); for(i=0;i<numFonts;i++) excluded[i]=INCLUDED; strcpy(array,list); for(i=0,p=array;;i++){ if (array[i]==';' || array[i]=='\0') { int j; BOOL shouldBreak = (array[i]=='\0'); array[i]='\0'; for(j=i-1;(array[j]=='\t' || array[j]==' ');j--) array[j]='\0'; [self excludeFont:p]; if (shouldBreak) break; p=array+i+1; while(*p==' ' || *p=='\t') p++; } } return self; } - initFrame:(const NXRect *)frameRect { const char *t; [super initFrame:frameRect]; [self allocateGState]; // For faster lock/unlockFocus [self setClipping:NO];// even faster... [self loadFonts]; [self newViewSize]; srandom(time(0)); if (t = NXReadDefault([NXApp appName],SPEED)){ threshold = atoi(t); } else { threshold = 200; } if (t= NXReadDefault([NXApp appName],BACKING)){ gray = atoi(t); } else { gray = PLAIN; } if (t= NXReadDefault([NXApp appName],USECOLOR)){ useColor = (strcmp(t,"YES")==0); } else { useColor = YES; } if (t= NXReadDefault([NXApp appName],F_FILE)){ fortuneFile = (atoi(t)); } else { fortuneFile = 0; } if (t = NXReadDefault([NXApp appName],EXFONTS)){ [self setExcludedFonts:t]; } else { [self setExcludedFonts:"Symbol ; Lexi"]; } [self setup]; return self; } - sizeTo:(NXCoord)width :(NXCoord)height { [super sizeTo:width :height]; [self newViewSize]; return self; } - newViewSize { if (oldSize.width == bounds.size.width && oldSize.height == bounds.size.height) { return self; }else{ oldSize.width = bounds.size.width; oldSize.height = bounds.size.height; } maxCoord.x = bounds.size.width-150.0; maxCoord.y = bounds.size.height; if (maxCoord.x < 0) maxCoord.x = 0; if (maxCoord.y < 0) maxCoord.y = 0; [fe setFrame:&frame]; if ([self window]){ [self display]; } return self; } - (const char *)windowTitle { return "FortuneView"; } - inspector:sender { appowner=sender; if (!sharedInspectorPanel){ [self setup]; } return sharedInspectorPanel; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.