ftp.nice.ch/pub/next/connectivity/news/Alexandra-0.9.s.tar.gz#/alex/ArticleSet.m

This is ArticleSet.m in view mode; [Download] [Up]

#import "Alexandra.h"
#import "Article.h"
#import "ArticleSet.h"
#import "NewsgroupSet.h"
#import "Newsgroup.h"
#import "ArticleViewControl.h"
#import "Composer.h"
#import "ColumnMatrix.h"
#import "ArticleSetMatrix.h"
#include <assert.h>
#import <misckit/MiscMailApp.h>
#import <misckit/MiscString.h>
#import <misckit/MiscAppDefaults.h>
#import "response_codes.h"
#import "plain-subject.h"
#import "MatrixScroller.h"
#import "descriptors.h"

@implementation ArticleSet

- init
{
   currentNewsgroup=nil;
   unselAction=@selector(clear);
   [super init];
   return self;
}

- awakeFromNib
{
   char buf1[255];

   sprintf(buf1,"SortType %s",[nntpServer serverName]);
   currentSortType=[NXApp defaultIntValue:buf1];

   return self;
}

- free
{
   [Composer killComposerForServer:nntpServer];
   return [super free];
}

- loadGroup:(Newsgroup *)aNewsgroup
{
   int i,j;
   Article *anArticle;

   if(aNewsgroup==nil)
      return self;

   currentNewsgroup=aNewsgroup;

   //get article list
   myMList=[aNewsgroup articleList];
   [myMatrix setMatrixCellList:myMList];
   
   //choose articles to show. Here: all unread
   j=[myMList count];
   for(i=0;i<j;i++){
      anArticle=[myMList objectAt:i];
      if([anArticle isRead])
         [anArticle unsetTag];
      else
         [anArticle setTag];
   }
   [myMatrix setAutodisplay:NO]; 
   [[myMatrix loadMatrix] scrollUp];
   [[myMatrix setAutodisplay:YES] display];
   [self sync];

   return self;
}

- selectArticle:sender
{
   int statusCode;
   Article *oldSelection;

   [[myMatrix window] makeFirstResponder:myMatrix];

   assert(myMList!=NULL); 
   oldSelection=currentSelection;
   [self syncAndReturnList:NO];

   if(oldSelection==currentSelection)
      return self;
 
   if(numSelCells==1){
      statusCode=[theArticleViewControl loadArticle:currentSelection fromGroup:[[theNewsgroupSet currentSelection] stringValue]];
      if(statusCode!=OK_ARTICLE){
         if(statusCode==-1)
            return self;
         //[myMList removeObject:currentSelection];
         if([currentSelection isRead]==FALSE){
            [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1];
            [theNewsgroupSet redisplayMatrix];
         }
         [myMatrix perform:@selector(removeInvalidCell:) with:currentSelection afterDelay:0.0 cancelPrevious:YES];
         return self;
      }
      if([currentSelection isRead]==FALSE){
         [currentSelection setRead];
         [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1];
         [theNewsgroupSet redisplayMatrix];
      }
   } 

   [myMatrix display];
  
   return self;
}

- showAll:sender
{
   if((currentNewsgroup==nil) || (myMList==nil))
      return self;
   [myMList makeObjectsPerform:@selector(setTag)];
   [[myMatrix reloadMatrix] display];

   [myMatrix setButtonTitle:"All Articles"];

   return self;
}

- showUnread:sender
{
   int i,j;
   int oldNumSelCells;

   if((currentNewsgroup==NULL) || (myMList==NULL))
      return self;
   j=[myMList count];
   for(i=0;i<j;i++){
      Article* anArticle;
      anArticle=[myMList objectAt:i];
      if([anArticle isRead]==TRUE)
         [anArticle unsetTag];
      else
         [anArticle setTag];
   }

   if(currentSelection!=nil)
      [currentSelection setTag];

   [[myMatrix reloadMatrix] display];

   oldNumSelCells=numSelCells;
   [self sync];
   
   if((oldNumSelCells!=1)&&(numSelCells==1)){
      currentSelection=nil;
      numSelCells=0;
      [self selectArticle:self];
   }
   [myMatrix setButtonTitle:"Unread Articles"];

   return self;
}

- markSelectionReadOrUnread:(BOOL)flag
{
   /*flag==TRUE <=> Mark unread */
   Article* anArticle;
   int delta;
   List *articleList=nil;
   int i,j;

   if(currentNewsgroup==NULL)
      return self;
   articleList=[myMatrix getCurrSelections];
   j=[articleList count];
   delta=0;
   for(i=0;i<j;i++){
      anArticle=[articleList objectAt:i];
      if([anArticle isRead]==flag){
         if(flag==FALSE){
            [anArticle setRead];
            delta-=1;
         }
         else{
            [anArticle setUnread];
            delta+=1;
         }
      } 
   }
   if(delta!=0){
      [[theNewsgroupSet currentSelection] incNumberUnreadArticles:delta];
      [theNewsgroupSet redisplayMatrix];
      [myMatrix display];
   }
   [articleList free];
   return self;
}

- markRead:sender
{
   [self markSelectionReadOrUnread:FALSE];
   return self;
}

- markUnread:sender
{
   [self markSelectionReadOrUnread:TRUE];
   return self;
}

- followupToSelection:sender
{
   if(currentSelection!=nil){
      if([self postToSelectedNG:self]!=nil)
         [theComposer followup:self];
   }
   return self;
}

- postToSelectedNG:sender
{
   List *selList;
   int i,j;

   EM_DURING
 	
      selList=[[theNewsgroupSet theMatrix] getCurrSelections];

      // warum muss den eine Gruppe ausgewÙhlt sein?
	  EM_CONDERROR(([selList count]==0),ECOMPNeedGroup,NULL,NULL);

      // See if posting is possible in general
 	  if([nntpServer canPost]==FALSE)
		 EM_ERROR(ECOMPNoPosting,NULL,NULL);

      // See if you can post to all groups in list
      j=[selList count];
      for(i=0;i<j;i++){
         char p=[[selList objectAt:i] postable];
    
		 if(p=='n')	
		    EM_ERROR(ECOMPNoPostingToGroup,[[selList objectAt:i] stringValue],NULL);
      }
      // O.K.
	  theComposer=[Composer newWindowForServer:nntpServer];
      [theComposer preparePostTo:selList 
	  	selArticle:currentSelection inView:theArticleViewControl];
		
   EM_EMPTYHANDLER

   [selList free];
   return self;
}

 
- clearMatrix
{
   currentNewsgroup=nil;
   myMList=nil;
   [myMatrix setMatrixCellList:nil];
   [[myMatrix loadMatrix] display];
   [[myMatrix window] flushWindow];
   [self sync];
   return self;
}

- (const char *)stringValue
{
   return ptr_to_articlebody;
}

- forwardArticle:sender
{
   NXStream *aStream;
   int len,maxlen;
   id subjectStr=[[MiscString init] alloc];
	const char *oldSubjectPtr;
	BOOL is_reply;
	id mailer;
	
   if((currentSelection==nil)||(currentNewsgroup==nil)){
      NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
      return self;
   }

   mailer=[MiscMailApp localMailer];
   if(mailer==nil){
      NXRunAlertPanel("ALEXANDRA","Can't reach mail application.",NULL,NULL,NULL);
      return self;
   }
   
   //O.K. first get the articles body
   aStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
   NXPrintf(aStream,"Forwarded Article %s\n",[currentSelection header]->fieldBody[MSG_ID]);
   NXPrintf(aStream,"Newsgroup %s\n",[currentNewsgroup stringValue]);
   NXPrintf(aStream,"From %s\n\n",[currentSelection header]->fieldBody[FROM]);

   [theArticleViewControl writeBody:aStream];
   NXGetMemoryBuffer(aStream,&ptr_to_articlebody,&len,&maxlen);

   //Compose subject
	oldSubjectPtr=plain_subject((char *)[currentSelection stringValue],&is_reply);
	if(is_reply)
	   [subjectStr setStringValue:"Re: "];
	[subjectStr strcat:oldSubjectPtr];
	
   // Open Compose window in mailer
   [mailer sendMailTo:nil subject:currentSelection body:self];
      
   NXCloseMemory(aStream,NX_FREEBUFFER);
   [subjectStr free];
   return self;
}

- replyByMail:sender
{
   NXStream *aStream;
   int len,maxlen;
   MiscString *to,*subject;
   char *r;
   BOOL dummy;
	id mailer;
	
   if((currentSelection==nil)||(currentNewsgroup==nil)){
      NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
      return self;
   }

   mailer=[MiscMailApp localMailer];
   if(mailer==nil){
      NXRunAlertPanel("ALEXANDRA","Can't reach mail application.",NULL,NULL,NULL);
      return self;
   }
   
   //O.K. first get the articles body
   aStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
   NXPrintf(aStream,"In article %s, you wrote:\n",[currentSelection header]->fieldBody[MSG_ID]);
   [theArticleViewControl writeQuotedText:aStream];
   NXGetMemoryBuffer(aStream,&ptr_to_articlebody,&len,&maxlen);

   //Make Subject string
   subject=[[MiscString alloc] init];
   [subject setStringValue:"Re: "];
	[subject strcat:plain_subject([currentSelection header]->fieldBody[SUBJECT], &dummy)]; 

   //Make To
   to=[[MiscString alloc] init];
   r=[currentSelection header]->fieldBody[REPLY_TO];
   if((r==NULL)||(r[0]=='\0'))
      r=[currentSelection header]->fieldBody[FROM];
   [to setStringValue:r];

   // Open Compose window in mailer
   [mailer sendMailTo:to subject:subject body:self];

   NXCloseMemory(aStream,NX_FREEBUFFER);
   [to free];
	[subject free];
	
   return self;
}

- cancelArticle:sender
{
    char	*messageID;
    char	*mailAddress, *articleMailAddress;
    NXStream	*articlestream;
	BOOL	proceed;

    if(currentSelection==nil)
       return nil;

    if ((messageID=[currentSelection header]->fieldBody[MSG_ID]) == NULL)
	return nil;
    
    mailAddress = [[FromHeaderController new] validFromAddress];
	if(!mailAddress)
		return self;

	if(((articleMailAddress = [currentSelection header]->fieldBody[REPLY_TO]) == NULL) &&
	  ((articleMailAddress = [currentSelection header]->fieldBody[FROM]) == NULL))
		{
		if(NXRunAlertPanel("ALEXANDRA", "The currently selected article has no sender address. Are you sure that you want to cancel it?", "Abort", "Proceed", NULL) == NX_ALERTDEFAULT)
			return self;
		}
	else if(strcmp(articleMailAddress, mailAddress) != 0)
		{
		if(NXRunAlertPanel("ALEXANDRA", "The sender's address of the currently selected article is different from yours. If you posted this article from a different system or your address changed in the meantime you should proceed. If, however, you did not post this article yourself you must under no circumstances cancel it!", "Abort", "Proceed", NULL) == NX_ALERTDEFAULT)
			return self;
		}

	if(proceed == NO)
		return self;
    	
    //Post 
    articlestream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
    NXPrintf(articlestream,"Newsgroups: %s\nSubject: cancel\nControl: cancel %s\nFrom: %s\n\ncancel\n",[currentSelection header]->fieldBody[NEWSGROUPS], messageID, mailAddress);
    NXSeek(articlestream, (long)0,NX_FROMSTART);
    [nntpServer postArticle:articlestream];
    
    NXCloseMemory(articlestream,NX_FREEBUFFER);
    free(mailAddress);
    return(self);
}


- setArticleSortType:(int)type
{
   char buf[255];
   int oldType;
   id selNewsgroup;

   sprintf(buf,"SortType %s",[nntpServer serverName]);
   oldType=[NXApp defaultIntValue:buf];
   if(oldType!=type){
      currentSortType=type;
      [NXApp setDefault:buf toInt:type];
      if((selNewsgroup=[theNewsgroupSet currentSelection])!=nil)
         if([selNewsgroup resortIfNeeded:type]){
            [myMatrix reloadMatrix];
            [myMatrix display];
         }
   }

   return self;
}

- sortArticlesByNumber:sender
{
   [self setArticleSortType:SORT_BY_NUMBER];
   return self;
}

- sortArticlesByDate:sender
{
   [self setArticleSortType:SORT_BY_DATE];
   return self;
}

- sortArticlesBySubject:sender
{
   [self setArticleSortType:SORT_BY_SUBJECT];
   return self;
}

- sortArticlesByName:sender
{
   [self setArticleSortType:SORT_BY_REAL_NAME];
   return self;
}

- catchUpThreadAt:(int)index
{
   int i,j;
   id anArticle;
   const char *thread_subject;
   int m=0;
   BOOL dummy;

   thread_subject=plain_subject([[myMatrix cellAt:index :0] header]->fieldBody[SUBJECT],&dummy);
   for(i=0,j=[myMList count];i<j;i++){
      anArticle=[myMList objectAt:i];
      if(!strcmp(plain_subject([anArticle header]->fieldBody[SUBJECT],&dummy),thread_subject)){
         if(![anArticle isRead]){
            [anArticle setRead];
            m++;
         }
      }
   }
   if(m>0)
      [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1*m];

   return self;
}

- skipThreadAndUpOrDown:(int)delta
{
   int i=[myMatrix selectedRow];
   int a,b,s;

   if(i<0){
      NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
      return self;
   }
   [self catchUpThreadAt:i];
   if(delta!=0){
      [myMatrix getNumRows:&a numCols:&b];

      s=i+delta;
      while((s>=0) && (s<a)){
         id anArticle=[myMatrix cellAt:s :0];
         if(![anArticle isRead]){
            [myMatrix selectCellAt:s :0];
            [self selectArticle:self];
            [myMatrix scrollCellToVisible:s upperOffset:((delta==-1)? 1.5:0.0) lowerOffset:((delta==1)? 1.5:0.0)];
            break;
         }
         s+=delta;
      }
   }
   [myMatrix display];
   [theNewsgroupSet redisplayMatrix];

   return self;
}

- skipThreadAndUp:sender
{
   return [self skipThreadAndUpOrDown:-1];
}

- skipThreadAndDown:sender
{
   return [self skipThreadAndUpOrDown:1];
}

- (const char *)stringValueForCellAt:(int)index
{
   int h=[NXApp defaultIntValue:"findGeneralScope"];
   if((h>=0)&&(h<XOVER_COUNT)){
      char *s=[[myMList objectAt:index] header]->fieldBody[h];
      if(s!=NULL)
         return s;
   }
   return "";
}

- (BOOL)selectArticleWithMsgid:(const char *)msgid
{
	int i,j;
	
	for(i=0,j=[myMList count];i<j;i++){
		const char *aMsgid=[[myMList objectAt:i] header]->fieldBody[MSG_ID];
		if(aMsgid && !strcmp(aMsgid,msgid))
			return [self selectArticleWithNumber:[[myMList objectAt:i] number]];
	}
	
	return FALSE;
}

- (BOOL)selectArticleWithNumber:(long)snumber;
{
	int i,j;
	
	for(i=0,j=[myMList count];i<j;i++){
		id aCell=[myMList objectAt:i];
		if([aCell number]==snumber){
			if(![aCell isTaged])
				[self showAll:self];
			return [myMatrix selectArticleWithNumber:snumber];
		}
	}
	
	return FALSE;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.