This is Newsgroup.m in view mode; [Download] [Up]
#import "Newsgroup.h"
#import "Article.h"
#import "NNTP.h"
#import "Interval.h"
#import "ColumnMatrix.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#import "GrayCell.h"
#import "descriptors.h"
#import <ctype.h>
#import "response_codes.h"
#import "rfc822realname.h"
#import "KillFile.h"
#import "Alexandra.h"
static int sortType=SORT_BY_NAME;
@implementation Newsgroup
+ setSortType:(int)t
{
sortType=t;
return self;
}
+ (int)sortType
{
return sortType;
}
- initTextCell:(const char *)aString
{
subscribed=FALSE;
[super initTextCell:aString];
[self setWrap:NO];
numUnreadArticles=0;
articleList=[[MiscSortedList alloc] init];
[articleList setSortEnabled:FALSE];
[articleList setSortOrder:Misc_ASCENDING];
intervalList=[[List alloc] init];
delayed=TRUE;
min=0;
max=-1;
first_unread=0;
return self;
}
- updateArticles
{
int i,j=[articleList count];
for(i=0;i<j;i++)
[[articleList objectAt:i] composeTitle];
return self;
}
- free
{
if(articleList!=nil){
[articleList makeObjectsPerform:@selector(free)];
[articleList free];
}
if(intervalList!=nil){
[intervalList makeObjectsPerform:@selector(free)];
[intervalList free];
}
[super free];
return self;
}
- setSubscribed
{
subscribed=TRUE;
return self;
}
- setUnsubscribed
{
subscribed=FALSE;
return self;
}
- (BOOL)isSubscribed
{
return subscribed;
}
- setNumUnreadArticles:(int)num
{
numUnreadArticles=(unsigned)num;
return self;
}
- incNumberUnreadArticles:(int)delta
{
numUnreadArticles+=delta;
return self;
}
- approxNumUnread
{
long i;
numUnreadArticles=0;
if((min==0)&&(max==0))
return self;
for(i=min;i<=max;i++)
if([self inReadList:i]==FALSE)
numUnreadArticles++;
return self;
}
- (long)numberUnreadArticles
{
return numUnreadArticles;
}
- (int)markAllReadUntil:lastArticle
{
int i,j,ii=0;
Article *anArticle;
if(delayed==TRUE){
if(intervalList!=nil){
[intervalList makeObjectsPerform:@selector(free)];
[intervalList empty];
}
else
intervalList=[[List alloc] init];
if(min<=max)
[intervalList addObject:[[Interval alloc] initWithLower:min upper:max]];
[self setNumUnreadArticles:0];
}
else{
for(i=0,j=[articleList count];i<j;i++){
anArticle=[articleList objectAt:i];
if([anArticle isRead]==FALSE){
ii++;
[anArticle setRead];
}
if(anArticle==lastArticle)
break;
}
if(ii>0)
[self incNumberUnreadArticles:(-1)*ii];
}
return ii;
}
- setTextAttributes:textObj
{
[super setTextAttributes:textObj];
if (subscribed==FALSE)
[textObj setTextGray:NX_DKGRAY];
else
[textObj setTextGray:NX_BLACK];
return textObj;
}
- drawInside:(const NXRect *)cellFrame inView:controlView
{
NXRect numrect;
static id sharedTextCell = nil;
char numstr[40];
//erase cell
PSsetgray((cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY);
NXRectFill(cellFrame);
//make cell
if (!sharedTextCell) {
sharedTextCell = [[GrayCell alloc] init];
[sharedTextCell setWrap:NO];
}
// draw text
if(subscribed==TRUE)
[sharedTextCell setDrawGray:FALSE];
else
[sharedTextCell setDrawGray:TRUE];
[sharedTextCell setFont:[self font]];
[sharedTextCell setStringValueNoCopy:[self stringValue]];
[sharedTextCell drawInside:cellFrame inView:controlView];
// draw number unread articles to the right
if(numUnreadArticles!=0){
sprintf(numstr,"%d",numUnreadArticles);
[sharedTextCell setStringValue:numstr];
NX_WIDTH(&numrect)=[[sharedTextCell font] getWidthOf:numstr]+4.0;
NX_HEIGHT(&numrect)=NX_HEIGHT(cellFrame);
NX_X(&numrect)=NX_X(cellFrame)+NX_WIDTH(cellFrame)- NX_WIDTH(&numrect);
NX_Y(&numrect)=NX_Y(cellFrame);
PSsetgray((cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY);
NXRectFill(&numrect);
[sharedTextCell drawInside:&numrect inView:controlView];
}
PSsetgray(NX_DKGRAY);
if (cFlags1.state || cFlags1.highlighted){
NXRect rectArray[2];
NXSetRect(&(rectArray[0]),NX_X(cellFrame),NX_Y(cellFrame), NX_WIDTH(cellFrame),1);
NXSetRect(&(rectArray[1]),NX_X(cellFrame),NX_MAXY(cellFrame)-1,
NX_WIDTH(cellFrame), 1.0);
NXRectFillList(rectArray, 2);
}
return self;
}
- highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
{
if(cFlags1.highlighted != flag){
cFlags1.highlighted = flag;
[self drawInside:cellFrame inView:controlView];
}
return self;
}
- setReadList:(char *)readlist
{
int x,y,i;
long lower,upper;
if(intervalList==nil)
intervalList=[[List alloc] init];
y=strlen(readlist);
if((y>0)&&(readlist[y-1]=='\n')){
readlist[y-1]='\0';
y--;
}
i=0;
while(i<y){
x=i;
while((i<y)&&(isdigit(readlist[i])!=0))
i++;
if(isdigit(readlist[x])!=0)
lower=atol(readlist+x);
else
lower=0;
if((i==y)||(readlist[i]==',')){
upper=lower;
i++;
}
else if(readlist[i]=='-'){
x=++i;
while((i<y)&&(isdigit(readlist[i])!=0))
i++;
if(isdigit(readlist[x])!=0)
upper=atol(readlist+x);
else
upper=0;
if(readlist[i]==',')
i++;
}
else{
i++;
continue;
}
if((upper!=0) && (lower!=0))
[intervalList addObject:[[Interval alloc] initWithLower:lower upper:upper]];
}
if([intervalList count]>0 && [NXApp defaultBoolValue:DEFAULT_PRIVATE_EXPIRE])
first_unread=[[intervalList objectAt:0] upperBound]+1;
return self;
}
- (BOOL)inReadList:(long)number
{
int i,ii;
ii=[intervalList count];
for(i=0;i<ii;i++)
if([[intervalList objectAt:i] isIn:number]==TRUE)
return TRUE;
return FALSE;
}
- dumpReadList:(NXStream *)aStream
{
long smallest,largest;
BOOL run_started;
long start,stop;
long i,j;
char *bucket;
long bsize;
long c=0;
//ever scanned this newsgroup?
if(delayed==TRUE){
j=[intervalList count];
for(i=0;i<j;i++){
[[intervalList objectAt:i] dumpAsAsciiIn:aStream];
if(i<j-1)
NXWrite(aStream,",",1);
}
return self;
}
// no articles in this group?
j=[articleList count];
if(j==0){
if(min<max)
NXPrintf(aStream,"1-%d",max);
return self;
}
//let's start -- first get smallest and largest article number
smallest=[[articleList objectAt:0] number];
largest=smallest;
for(i=0;i<j;i++){
long num=[[articleList objectAt:i] number];
if(num<smallest) smallest=num;
if(num>largest) largest=num;
}
//malloc bucket and fill it
bsize=largest-smallest+2;
bucket=(char *)calloc(bsize,sizeof(char));
for(i=0;i<j;i++){
id aCell=[articleList objectAt:i];
if([aCell isRead]==FALSE)
bucket[[aCell number]-smallest]=(char)1;
}
bucket[bsize-1]=(char)1; //dummy!!!
//init
run_started=FALSE;
start=1;
stop=0;
if(smallest!=1){
run_started=TRUE;
stop=smallest-1;
}
for(i=0;i<bsize;i++){
if(run_started==TRUE){
if(bucket[i]==(char)1){
if(start<=stop)
if(c>0)
NXPrintf(aStream,",");
else c++;
if(start<stop)
NXPrintf(aStream,"%d-%d",start,stop);
else
if(start==stop)
NXPrintf(aStream,"%d",start);
run_started=FALSE;
}
else // bucket==0
stop++;
}
else
if(bucket[i]==(char)0){
start=i+smallest;
stop=start;
run_started=TRUE;
}
}
free(bucket);
return self;
}
- scanArticles:(NNTP *)nntpServer
{
return [self scanArticles:nntpServer visibleIn:nil];
}
- scanArticles:(NNTP *)nntpServer visibleIn:articleView
{
long oldMin,oldMax;
long from,to,expired_min=0;
Article *anArticle;
int i,j,ii;
Storage *array;
subjectDesc *subDesc;
headerDesc *h;
char buf[512];
if([NXApp defaultBoolValue:DEFAULT_PRIVATE_EXPIRE]){
float a=(float)[NXApp defaultIntValue:DEFAULT_EXPIRE_RELATIV_OFFSET]/100;
int b=[NXApp defaultIntValue:DEFAULT_EXPIRE_ABSOLUTE_OFFSET];
long y1,y2,y1cnt=first_unread-min;
y1= (y1cnt < 1)? min : (long)(first_unread - a*y1cnt);
y1cnt=first_unread-b;
y2= y1cnt<0 ? min : y1cnt;
expired_min=MAX(y1,y2);
}
oldMin=-1; oldMax=-1;
from=-1; to=-1;
j=[articleList count];
if((delayed==TRUE)||(j==0)){
if(expired_min>0)
from=expired_min;
else
from=min;
to=max;
}
else{
oldMin=[[articleList objectAt:0] number];
oldMax=oldMin;
// find min + max
for(i=0;i<j;i++){
long num=[[articleList objectAt:i] number];
if(num>oldMax) oldMax=num;
if(num<oldMin) oldMin=num;
}
// see if there's something to do
if(max>oldMax){
from=oldMax+1;
to=max;
}
}
// get new articles?
if(from!=-1){
array=[[Storage alloc] initCount:0 elementSize:sizeof(subjectDesc) description:"{l*}"];
[nntpServer fetchSubjectHeaders:array from:from to:to];
j=[array count];
for(i=0;i<j;i++){
subDesc=(subjectDesc *)[array elementAt:(unsigned int)i];
//Assert a Subject header
if(!subDesc->fieldBody[SUBJECT])
subDesc->fieldBody[SUBJECT]=NXCopyStringBuffer("(none)");
else if(!(*subDesc->fieldBody[SUBJECT])){
free(subDesc->fieldBody[SUBJECT]);
subDesc->fieldBody[SUBJECT]=NXCopyStringBuffer("(none)");
}
anArticle=[[Article alloc] initWithNumber:subDesc->number];
h=[anArticle header];
for(ii=0;ii<XOVER_COUNT;ii++)
h->fieldBody[ii]=subDesc->fieldBody[ii];
free(subDesc->fieldBody);
[anArticle setSize:subDesc->artsize];
[anArticle setLines:subDesc->lines];
[anArticle composeTitle];
if([self inReadList:subDesc->number]==TRUE)
[[anArticle setRead] unsetTag];
else
[[anArticle setUnread] setTag];
[articleList addObject:anArticle];
}
[array free];
}
// see if there are any expired articles
if((delayed==FALSE) && (oldMin!=-1) && (min>oldMin))
for(i=[articleList count]-1;i>=0;i--)
if([[articleList objectAt:i] number]<min){
id bogusCell=[articleList objectAt:i];
if(articleView!=nil)
[articleView removeInvalidCell:bogusCell andUpdate:FALSE];
else
[[articleList removeObjectAt:i] free];
}
//get sort order
sprintf(buf,"SortType %s",[nntpServer serverName]);
[self resortIfNeeded:[NXApp defaultIntValue:buf]];
//filter articles
[[nntpServer killFile] filterArticles:self andReloadMatrix:FALSE];
// calc number unread articles
numUnreadArticles=0;
j=[articleList count];
for(i=0;i<j;i++)
if([[articleList objectAt:i] isRead]==FALSE)
numUnreadArticles++;
delayed=FALSE;
return self;
}
- (BOOL)isDelayed
{
return delayed;
}
- (List *)articleList
{
return articleList;
}
- unsetTag
{
myTag=0;
return self;
}
- setTag
{
myTag=1;
return self;
}
- (BOOL)isTaged
{
return(myTag==1);
}
- setMin:(long)newMin
{
min=newMin;
return self;
}
- setMax:(long)newMax;
{
max=newMax;
return self;
}
- (long)minNumber
{
return min;
}
- (long)maxNumber
{
return max;
}
- (BOOL)bogus
{
return isBogus;
}
- setBogus:(BOOL)b
{
isBogus=b;
return self;
}
- setPostable:(char)p
{
postable=p;
return self;
}
- (char)postable
{
return postable;
}
- (BOOL)resortIfNeeded:(int)val
{
if(![articleList sorted]||(val!=currentSortType)){
currentSortType=val;
[Article setSortType:val];
[articleList unsort];
[articleList sort];
return TRUE;
}
return FALSE;
}
//MiscCompare protocol implementation
- (int)compare:anObject
{
BOOL t;
int s;
if(anObject==nil) return 0;
if(sortType==SORT_BY_NAME)
return(strcmp([self stringValue],[anObject stringValue]));
t=[anObject isTaged];
if(t!=[self isTaged])
return (t? 1 : -1);
if(t){
s=[anObject state];
if(s!=[self state])
return (s==1? 1 : -1);
}
t=[anObject isSubscribed];
if(t!=[self isSubscribed])
return(t? 1 : -1);
t=[anObject isDelayed];
if(t!=[self isDelayed])
return(t? 1 : -1);
return 0;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.