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 !DEBUG 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); } #endif 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.