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.