This is Composer.m in view mode; [Download] [Up]
#import <appkit/appkit.h> #import <misckit/misckit.h> #import <misckit/MiscDateView.h> #import "Alexandra.h" #import "Composer.h" #import "NNTP.h" #import "descriptors.h" #import "ArticleViewControl.h" #import "ArticleSet.h" #import "NntpPanelControl.h" #import "plain-subject.h" #include <ctype.h> #include <dbkit/DBTableView.h> #define OK_POSTED 240 static MiscList *myComposers; static XTDispatchAction *emacs_action; static XTDispatchAction *none_action; @implementation Composer //----------------------------------------------------------- // Klassen Methoden, Verwalten der Composer Liste //----------------------------------------------------------- + initialize { char buf[MAXPATHLEN + 1]; const char *userBindings; if(self==[Composer class]) { myComposers=[[MiscList alloc] init]; emacs_action=[[XTDispatchAction alloc] init]; none_action=[[XTDispatchAction alloc] init]; if([[NXBundle mainBundle] getPath:buf forResource:"emacs" ofType:"keys"]) [emacs_action loadFromFile:buf estream:nil]; if(userBindings=[NXApp defaultValue:"KeyString"]){ [emacs_action addBindings:userBindings estream:nil]; [none_action addBindings:userBindings estream:nil]; } } return self; } + newWindowForServer:(NNTP *)server; { Composer *new; new=[[self alloc] initForServer:server]; [myComposers addObject:new]; return new; } + removeComposer:(Composer *)composer; { [myComposers removeObject:composer]; return self; } + killComposerForServer:(NNTP *)server; { Composer *c; for(c=[myComposers setLastObject];c;c=[myComposers setPreviousObject]) if([c nntpServer]==server) [c free]; return self; } + confirmTermination { int r1,r2; int j=[myComposers count]; if(j>0) do{ r1=NXRunAlertPanel("ALEXANDRA","There are unsend messages.","Review unsend","Quit anyway","Cancel"); if(r1==NX_ALERTOTHER) return nil; else if(r1==NX_ALERTALTERNATE){ int i; for(i=j-1;i>=0;i--) [[myComposers objectAt:i] free]; return self; } else{ int i; BOOL canceled=FALSE; for(i=j-1;i>=0;i--){ id aComposer=[myComposers objectAt:i]; [[aComposer window] makeKeyAndOrderFront:self]; r2=NXRunAlertPanel("ALEXANDRA","Send this message?","Send","Don't send","Cancel"); if(r2==NX_ALERTOTHER){ canceled=TRUE; break; } else if(r2==NX_ALERTDEFAULT) [aComposer post:self]; else [aComposer free]; } if(canceled) j=[myComposers count]; else return self; } } while(j>0); return self; } //----------------------------------------------------------- // INIT & FREE //----------------------------------------------------------- - initForServer:(NNTP *)theServer; { float char78Width; NXRect textFrame; NXRect windowFrame; NXAtom ids[]={[KVPair keyIdentifier],[KVPair valueIdentifier],NULL}; if([NXApp loadNibSection:"Composer.nib" owner:self withNames:NO]==nil) EM_ERROR(EGENFileNotFound,"Composer.nib",NULL); [self setNntpServer:theServer]; headerController=[[[MiscTableController alloc] init] setTableView:headerTable withIdentifiers:ids]; [self getDefaults]; char78Width=78*[[theText font] getWidthOf:"A"]; [theText getFrame:&textFrame]; [[theText window] getFrame:&windowFrame]; char78Width+=NX_WIDTH(&windowFrame)-NX_WIDTH(&textFrame); [[theText window] sizeWindow:char78Width :NX_HEIGHT(&windowFrame)]; if([NXApp defaultBoolValue:DEFAULT_APPEND_SIG]){ NXStream *signatureFile; const char *home=NXHomeDirectory(); char *filename; filename=(char *)NXZoneCalloc(MYZONE,(strlen(home)+12),sizeof(char)); sprintf(filename,"%s/.signature",home); if((signatureFile=NXMapFile(filename,NX_READONLY))!=NULL){ [theText readText:signatureFile]; [theText setSel:0:0]; [theText replaceSel:"\n-- \n"]; [theText setSel:0:0]; NXClose(signatureFile); } else NXRunAlertPanel("Alexandra","Cannot append signature." " File `~/.signature' not found.","Cancel",NULL,NULL); NXZoneFree(MYZONE,filename); } [[theText window] makeKeyAndOrderFront:self]; return self; } - getDefaults { KVPair *emptypair=[[KVPair allocFromZone:MYZONE] init]; char defname[200]; const char *d; int i,n=[NXApp defaultIntValue:DEFAULT_XHEADER_COUNT]; [headerController empty:nil]; for(i=0;i<n;i++) { sprintf(defname,DEFAULT_XHEADERS,i+1); d=[NXApp defaultValue:defname]; if(d) [headerController addRow:[[KVPair alloc] init:d delimiter:':']]; else [headerController addRow:emptypair]; } [emptypair free]; return self; } - free { [[self class] removeComposer:self]; [[[headerTable window] close] free]; [[window close] free]; if(subject!=NULL) free(subject); if(newsgroups!=NULL) free(newsgroups); if(references!=NULL) free(references); if(theQuotingStream!=NULL) NXCloseMemory(theQuotingStream,NX_FREEBUFFER); return [super free]; } - awakeFromNib { if(strcmp([NXApp defaultValue:DEFAULT_KEY_BASE],"emacs")==0) local_action=emacs_action; else local_action=none_action; theText=[theText docView]; [theText setMonoFont:YES]; [theText setFont: [Font userFixedPitchFontOfSize:0 matrix:NX_FLIPPEDMATRIX]]; [theText setInitialAction:local_action]; [headerForm setNextText:theText]; return self; } //----------------------------------------------------------- // quoting //----------------------------------------------------------- - followup:sender { char *newSubject,*oldSubject; BOOL wasReply; if(subject==NULL) return self; oldSubject=plain_subject(subject,&wasReply); newSubject=(char *)NXZoneCalloc(MYZONE,(strlen(subject)+5),sizeof(char)); sprintf(newSubject,"Re: %s",oldSubject); [subjectCell setStringValueNoCopy:newSubject shouldFree:YES]; [newsgroupCell setStringValue:newsgroups]; [followupButton setIcon:[followupButton altIcon]]; [followupButton setTitle:[followupButton altTitle]]; [followupButton setAction:@selector(includeArticle:)]; if(strlen(references)>0) insert_refs=TRUE; return self; } - includeArticle:sender { char *buf; int len,maxlen; if(theQuotingStream!=NULL){ NXGetMemoryBuffer(theQuotingStream,&buf,&len,&maxlen); [theText setSel:0:0]; [theText replaceSel:buf length:len]; } [followupButton setEnabled:FALSE]; return self; } - post:sender { char month_name[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep", "Oct","Nov","Dec"}; char mime_header[]="MIME-Version: 1.0\nContent-Type: text/plain; charset=iso-8859-1\nContent-Transfer-Encoding: 8bit\n"; char *a2String; NXStream *theStream; long bodyPos; int textLength; int line; int pos; NXBreakArray *theBreaks=0; int nbreaks; NXLineDesc *breaks; BOOL asciiBody; BOOL userDefdFrom=NO; int statusCode; MiscList *headers; KVPair *pair; if([newsgroupCell stringValue][0]=='\0'){ NXRunAlertPanel("ALEXANDRA","No Newsgroups header field body. Can't post.",NULL,NULL,NULL); return nil; } if([subjectCell stringValue][0]=='\0'){ NXRunAlertPanel("ALEXANDRA","No Subject header field body. Can't post.",NULL,NULL,NULL); return nil; } if([theText textLength]==0){ NXRunAlertPanel("ALEXANDRA","No article body. Can't post.",NULL,NULL,NULL); return nil; } if([expireButton state] && ([dateView isDateValid:self]==FALSE)) return nil; [headerTable endEditing]; theStream=NXOpenMemory(NULL,0,NX_READWRITE); NXSeek(theStream,0,NX_FROMSTART); //Write MIME-Header asciiBody=[self asciiBody]; if(asciiBody==FALSE) NXPrintf(theStream,"%s",mime_header); // Write Newsgroups: header NXPrintf(theStream,"Newsgroups: %s\n",[newsgroupCell stringValue]); // Write Subject: header NXPrintf(theStream,"Subject: %s\n",[subjectCell stringValue]); // Write References: header if(insert_refs==TRUE) NXPrintf(theStream,"References: %s\n",references); headers=[headerController rows]; for(pair=[headers setFirstObject]; pair; pair=[headers setNextObject]) if([pair hasValue]) { if(!strcasecmp([pair key],"from")) userDefdFrom=YES; NXPrintf(theStream,"%s: %s\n",[pair key],[pair value]); } // Write Expires: header if([expireButton state]){ int day=[dateView getDay:self]; const char *month=month_name[[dateView getMonth:self]-1]; int year=[dateView getYear:self]; NXPrintf(theStream,"Expires: %d %s %d 00:00:00 GMT\n",day,month,year); } if(!userDefdFrom){ a2String=[[FromHeaderController new] validFromAddress]; if(!a2String) { NXCloseMemory(theStream,NX_FREEBUFFER); return nil; } NXPrintf(theStream,"From: %s\n",a2String); free(a2String); } NXPrintf(theStream,"X-Newsreader: %s.app (%s)\n", [NXApp appName],[NXApp appVersion]); //Write end of header NXPutc(theStream,'\n'); bodyPos=NXTell(theStream); /*write body*/ textLength=[theText textLength]; object_getInstanceVariable(theText, "theBreaks", (void **)&theBreaks); nbreaks = theBreaks->chunk.used/sizeof(NXLineDesc); breaks = theBreaks->breaks; for(pos=0,line=0;line<nbreaks && pos<textLength;line++) { int lineChange; int endParagraph; int len,nlen; char *buf,*realbuf; lineChange = breaks[line] & 0x8000; endParagraph = breaks[line] & 0x4000; len = breaks[line] & 0x3fff; buf = malloc(len+2); realbuf=buf; buf++; nlen = [theText getSubstring:buf start:pos length:len]; if(buf[0]=='.'){ buf--; nlen++; buf[0]='.'; } if((nlen>0)&&(buf[nlen-1]!='\n')){ if(buf[nlen-1]=='\0') buf[nlen-1]='\n'; else{ buf[nlen] = '\n'; nlen++; } } NXWrite(theStream,buf,nlen); free(realbuf); pos+=len; if (lineChange!=0) { /* "if the line change bit is set, the descriptor is * the first field of a NXHeightChange...." */ line += sizeof(NXHeightInfo) / sizeof(NXLineDesc); } } statusCode=[nntpServer postArticle:theStream]; if(statusCode==OK_POSTED){ [window close]; NXCloseMemory(theStream,NX_FREEBUFFER); [self free]; return self; } return nil; } - preparePostTo:(List *)newsgroupList selArticle:(Article *)anArticle inView:(ArticleViewControl *)controlView; { int i,j,strlength; char *newsgroup_str; char *ptr; const char *newsgroup; char *msg_id,*refs; //Make newsgrouplist string and set Newsgroups: field strlength=0; j=[newsgroupList count]; for(i=0;i<j;i++) strlength += strlen([[newsgroupList objectAt:i] stringValue]); newsgroup_str=(char *)malloc((strlength+j)*sizeof(char)); ptr=newsgroup_str; for(i=0;i<j;i++){ newsgroup=[[newsgroupList objectAt:i] stringValue]; strcpy(ptr,newsgroup); ptr+=strlen(newsgroup); if(i<j-1) *ptr=','; ptr++; } [newsgroupCell setStringValueNoCopy:newsgroup_str shouldFree:YES]; //remember selected article fields subject=NULL; if(anArticle==nil) [followupButton setEnabled:FALSE]; else{ subject=NXCopyStringBuffer([anArticle header]->fieldBody[SUBJECT]); //where to followup? if([anArticle header]->fieldBody[FOLLOWUP_TO]!=NULL) newsgroups=NXCopyStringBuffer([anArticle header]->fieldBody[FOLLOWUP_TO]); else if([anArticle header]->fieldBody[NEWSGROUPS]!=NULL) newsgroups=NXCopyStringBuffer([anArticle header]->fieldBody[NEWSGROUPS]); //References field i=0; j=0; refs=[anArticle header]->fieldBody[REFS]; msg_id=[anArticle header]->fieldBody[MSG_ID]; if(refs!=NULL) i=strlen(refs); if(msg_id!=NULL) j=strlen(msg_id); references=(char *)malloc((i+j+3)*sizeof(char)); references[0]='\0'; if(refs!=NULL) strcpy(references,refs); if(msg_id!=NULL){ if(strlen(references)>0) strcat(references," "); strcat(references,msg_id); } insert_refs=FALSE; // Copy article body theQuotingStream=NXOpenMemory(NULL,0,NX_READWRITE); if([anArticle header]->fieldBody[FROM]!=NULL) NXPrintf(theQuotingStream,"%s wrote:\n",[anArticle header]->fieldBody[FROM]); [controlView writeQuotedText:theQuotingStream]; } //set DateViews date as today [dateView getTodaysDate:self]; return self; } - window { return window; } - setNntpServer:(NNTP *)nntp { nntpServer=nntp; return self; } - (NNTP *)nntpServer; { return nntpServer; } - printText:sender { if([theText textLength]!=0){ [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION]; [theText printPSCode:self]; } return self; } - (BOOL)asciiBody { NXStream *textStream; int len,maxlen; char *p; textStream= NXOpenMemory(NULL,0,NX_WRITEONLY); [theText writeText:textStream]; NXGetMemoryBuffer(textStream,&p,&len,&maxlen); for(;len>0;p++,len--) if(!NXIsAscii(*p)) break; NXCloseMemory(textStream,NX_FREEBUFFER); return (len<=0); } - theText { return theText; } - windowWillClose:sender { if([theText textLength]!=0){ int answer=NXRunAlertPanel("ALEXANDRA","Message was not posted.","Close Anyway", "Cancel",NULL); if(answer==NX_ALERTALTERNATE) return nil; } return [self free]; } - windowWillMiniaturize:sender toMiniwindow:miniwindow { [sender setMiniwindowIcon:"respond"]; return self; } - windowWillReturnFieldEditor:sender toObject:client { return [XText newFieldEditorFor:sender initialAction:local_action estream:nil]; } - rot13:sender { char *buf; int i,j=[theText textLength]; buf=malloc((j+2)*sizeof(char)); [theText getSubstring:buf start:0 length:j]; buf[j]='\0'; for(i=0;i<j;i++) { if(islower(buf[i])) buf[i]=(((buf[i]-'a')+13)%26)+'a'; else if(isupper(buf[i])) buf[i]=(((buf[i]-'A')+13)%26)+'A'; } [theText setText:buf]; free(buf); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.