This is ArticleViewControl.m in view mode; [Download] [Up]
#import "Alexandra.h" #import "ArticleViewControl.h" #import <misckit/MiscAppDefaults.h> #import "response_codes.h" #import "FaceView.h" #import <misckit/MiscClockView.h> #import "parse-header.h" #import "rfc822realname.h" #include <ctype.h> #define QUOTESTRING ">" #define ISEMPTY(string) ((string==NULL)||(*(string)=='\0')) #define FORCEBREAK "\n\t -·1234567890>#}:" @implementation ArticleViewControl - init { [super init]; noArticle=TRUE; rot13=FALSE; [ERROR_MANAGER addObserver:self selector:@selector(updateText) forError:ENOTEPrefsChanged]; [self updateText]; return self; } - awakeFromNib { [theText setFontPanelEnabled:FALSE]; [clockView setMilitaryTime:[NXApp defaultBoolValue:"24HourClock"]]; [clockView setHide:YES]; return self; } - free { if(articleHeader) free(articleHeader); if(articleBody) free(articleBody); [ERROR_MANAGER removeObserver:self forError:ENOTEPrefsChanged]; return [super free]; } - updateText { [self displayArticleScrollUp:NO]; return self; } - (int)loadArticle:(Article *)theArticle fromGroup:(const char *)theGroup { int statusCode; time_t t; rot13=FALSE; // get article header + body if(articleHeader) free(articleHeader); statusCode=[nntpServer loadArticleHeader:theArticle toString:&articleHeader]; if(statusCode!=OK_HEAD){ return statusCode; } subject=[theArticle header]->fieldBody[SUBJECT]; from=[theArticle header]->fieldBody[FROM]; organization=[theArticle header]->fieldBody[ORGANIZATION]; if(articleBody) free(articleBody); statusCode=[nntpServer loadArticleBody:theArticle toString:&articleBody]; if(statusCode!=OK_BODY){ return statusCode; } noArticle=FALSE; [self displayArticleScrollUp:YES]; t=[theArticle time]; if(![imageView showFaceForName:[theArticle header]->fieldBody[FROM]]){ const char *r=[theArticle header]->fieldBody[REPLY_TO]; if(r && *r) [imageView showFaceForName:r]; } [[[clockView setHide:FALSE] setTime:localtime(&t)] display]; return OK_BODY; } - displayArticleScrollUp:(BOOL)scroll { Font *textF,*sigF; const char *signature,*eob,*p,*thisLine; int len,rows,alnum,others; NXSize visibleSize; NXRect theUpperRect; char *tmpArticleBody=articleBody; signatureDetection=[NXApp defaultBoolValue:DEFAULT_SIG_DETECTION]; rewrapping=[NXApp defaultBoolValue:DEFAULT_REWRAP_ARTICLE_TEXT]; headerMode=[NXApp defaultIntValue:DEFAULT_HEADER_MODE]; if(noArticle) return self; // rot13 article body? if(rot13){ int i,j; j=strlen(articleBody); tmpArticleBody=malloc((j+2)*sizeof(char)); for(i=0;i<j;i++) { if(islower(articleBody[i])) tmpArticleBody[i] = (((articleBody[i]-'a')+13)%26)+'a'; else if(isupper(articleBody[i])) tmpArticleBody[i] = (((articleBody[i]-'A')+13)%26)+'A'; else tmpArticleBody[i]=articleBody[i]; } } [[theText window] disableDisplay]; [theText setAutodisplay:NO]; [theText setText:""]; textF=[NXApp defaultFont:DEFAULT_ARTICLE_FONT]; sigF=[Font userFixedPitchFontOfSize:0 matrix:NX_FLIPPEDMATRIX]; [theText setFont:textF]; //load text in text instance if(headerMode==NEWSPAPER_HEADER){ [self writeNewspaperHeader]; } else if(headerMode==FULL_HEADER){ [theText setText:articleHeader]; } else if(headerMode==SMALL_HEADER){ [self writeSelectedHeader]; } len=[theText textLength]; [theText setSel:len :len]; [theText replaceSel:"\n"]; signature=NULL; len=strlen(tmpArticleBody); if(len>4){ eob=tmpArticleBody+len-1; signature=NULL; //find signature if(signatureDetection){ rows=0; for(p=tmpArticleBody+len-4;p>tmpArticleBody && rows<16;p--) if(p[0]=='\n'){ if(p[1]=='-' && p[2]=='-' && p[strspn(p+1,"\t -")+1]=='\n'){ eob=p; signature=p+3; if(*signature=='-') signature++; break; } else rows++; } } thisLine=tmpArticleBody; do{ alnum=others=1; for(p=thisLine;p<eob && p[0]!='\n';p++) if(NXIsAlNum(p[0]) || p[0]==' ') alnum++; else others++; if(rewrapping && p!=eob && alnum>40 && alnum/others>3 && !strchr(FORCEBREAK,p[1])) { [theText appendString:thisLine length:p-thisLine withFont:textF]; if(p[-1]!=' ') [theText appendString:" " withFont:textF]; } else [theText appendString:thisLine length:p-thisLine+1 withFont:textF]; thisLine=p+1; } while(p<eob); if(signatureDetection && signature) { [theText appendString:"\n" withFont:sigF]; [theText appendString:signature withFont:sigF]; } } else [theText appendString:tmpArticleBody length:len withFont:textF]; if(scroll){ [[[theText superview] superview] getContentSize:&visibleSize]; NXSetRect(&theUpperRect,0.0,0.0,visibleSize.width,visibleSize.height); [theText scrollRectToVisible:&theUpperRect]; } [theText setAutodisplay:YES]; [[theText window] reenableDisplay]; [[theText window] display]; if(rot13) free(tmpArticleBody); return self; } - clear { [theText setAutodisplay:NO]; [theText setText:""]; [[theText setAutodisplay:YES] display]; noArticle=TRUE; [imageView showFaceForName:NULL]; [[clockView setHide:TRUE] display]; if(articleBody) free(articleBody); if(articleHeader) free(articleHeader); articleBody=NULL; articleHeader=NULL; return self; } - saveAs:sender { id panel; int fd; NXStream *theStream; static char *dir=NULL; if(dir==NULL) dir=NXCopyStringBuffer([NXApp defaultValue:DEFAULT_SAVE_PATH]); if([theText textLength]==0 || noArticle) return self; panel=[SavePanel new]; if([panel runModalForDirectory:dir file:""]!=NX_CANCELTAG){ free(dir); dir=NXCopyStringBuffer([panel filename]); } else return nil; //Save fd=open([panel filename],O_WRONLY|O_CREAT|O_TRUNC,0666); if(fd<0){ NXRunAlertPanel("ALEXANDRA","Can't save file: %s",NULL,NULL,NULL,strerror(errno)); return self; } theStream=NXOpenFile(fd,NX_WRITEONLY); NXWrite(theStream,articleHeader,strlen(articleHeader)); NXWrite(theStream,"\n",1); NXWrite(theStream,articleBody,strlen(articleBody)); NXClose(theStream); close(fd); return self; } - printText:sender { if([theText textLength]!=0){ [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION]; [theText printPSCode:self]; } return self; } - (const char *)articleBody { if(noArticle) return NULL; return articleBody; } - (const char *)articleHeader { if(noArticle) return NULL; return articleHeader; } - writeBody:(NXStream *)aStream { if(articleBody) NXWrite(aStream,articleBody,strlen(articleBody)); return self; } - writeQuotedText:(NXStream *)aStream { int i; int bodyLen; BOOL wasNewline; const char *quotestring; if(!articleBody) return self; quotestring=[NXApp defaultValue:DEFAULT_QUOTING_PREFIX]; if(!quotestring){ [NXApp setDefault:DEFAULT_QUOTING_PREFIX to:QUOTESTRING]; quotestring=[NXApp defaultValue:DEFAULT_QUOTING_PREFIX]; } bodyLen=strlen(articleBody); wasNewline=TRUE; for(i=0;i<bodyLen;i++){ if(wasNewline) NXPrintf(aStream,"%s",quotestring); NXPutc(aStream,articleBody[i]); if(articleBody[i]=='\n') wasNewline=TRUE; else wasNewline=FALSE; } return self; } - writeSelectedHeader { char **selectedHeaders; char *field_names,*f_buffer,**pattern; const char *default_headers; int count=0; int num_h=0; int i; BOOL start=TRUE; NXStream *aStream=NXOpenMemory(NULL,0,NX_READWRITE); default_headers=[NXApp defaultValue:DEFAULT_HEADER_FILTER]; if(*default_headers=='\0'){ return self; } field_names=NXCopyStringBuffer(default_headers); for(f_buffer=field_names;*f_buffer!='\0';f_buffer++) if(*f_buffer==':') num_h++; selectedHeaders=calloc(num_h,sizeof(char**)); pattern=calloc(num_h,sizeof(char**)); f_buffer=field_names; i=0; for(;*f_buffer!='\0';f_buffer++){ if(start){ pattern[i]=f_buffer; start=FALSE; i++; } else if(*f_buffer==':'){ start=TRUE; *f_buffer='\0'; } } parse_header(articleHeader,strlen(articleHeader),selectedHeaders,pattern,num_h,NO); i=0; for(count=0;count<num_h;count++) if(selectedHeaders[count]!=NULL){ i++; NXPrintf(aStream,"%s: %s\n",pattern[count],selectedHeaders[count]); free(selectedHeaders[count]); } NXPrintf(aStream,"\n"); free(field_names); free(selectedHeaders); free(pattern); NXSeek(aStream,0,NX_FROMSTART); [theText readText:aStream]; NXCloseMemory(aStream,NX_FREEBUFFER); return self; } - writeNewspaperHeader { Font *titleF,*authorF; NXRect visibleRect; int titleFsize=24; [theText getVisibleRect:&visibleRect]; authorF=[Font boldSystemFontOfSize:14 matrix:NX_FLIPPEDMATRIX]; if(!ISEMPTY(subject)){ while(([titleF=[Font boldSystemFontOfSize:titleFsize matrix:NX_FLIPPEDMATRIX] getWidthOf:subject]>NX_WIDTH(&visibleRect)-10) && titleFsize>12) titleFsize-=2; [theText appendString:subject withFont:titleF]; [theText appendString:"\n" withFont:titleF]; } if(!ISEMPTY(from)){ int nameFrom,to; rfc822_realname(from,&nameFrom,&to); [theText appendString:"by " withFont:authorF]; [theText appendString:from+nameFrom length:to-nameFrom+1 withFont:authorF]; if(!ISEMPTY(organization)){ [theText appendString:", " withFont:authorF]; [theText appendString:organization withFont:authorF]; } [theText appendString:"\n" withFont:authorF]; } return self; } -(id)theText { return theText; } - setModeTo:(int)mode { [NXApp setDefault:DEFAULT_HEADER_MODE toInt:mode]; if(!noArticle) [self displayArticleScrollUp:NO]; return self; } - showHeader:sender { [self setModeTo:FULL_HEADER]; return self; } - hideHeader:sender { [self setModeTo:NO_HEADER]; return self; } - smallHeader:sender { [self setModeTo:SMALL_HEADER]; return self; } - newspaperHeader:sender { [self setModeTo:NEWSPAPER_HEADER]; return self; } - (BOOL)headermodeCellEnabled:menuCell { if((headerMode!=FULL_HEADER)&&([menuCell action]==@selector(showHeader:))) return TRUE; else if((headerMode!=NO_HEADER)&&([menuCell action]==@selector(hideHeader:))) return TRUE; else if((headerMode!=SMALL_HEADER)&&([menuCell action]==@selector(smallHeader:))) return TRUE; else if((headerMode!=NEWSPAPER_HEADER)&&([menuCell action]==@selector(newspaperHeader:))) return TRUE; return FALSE; } - rot13:sender { rot13=!rot13; [self displayArticleScrollUp:NO]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.