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.