This is EnhanceMail.m in view mode; [Download] [Up]
/* -*-C-*-
*******************************************************************************
*
* File: EnhanceMail.m
* RCS: /usr/local/sources/CVS/EnhanceMail/EnhanceMail.m,v 1.17 1998/07/07 22:25:03 tom Exp
* Description:
* Author: Carl Edman
* Created: Fri Oct 13 11:48:05 1995
* Modified: Sun Apr 13 22:30:48 1997 Tom Hageman <tom@basil.icce.rug.nl>
* Language: C
* Package: N/A
* Status: Experimental (Do Not Distribute)
*
* (C) Copyright 1995, but otherwise this file is perfect freeware.
*
*******************************************************************************
*/
#import "EnhanceMail.h"
#import "Preferences.h"
#import "TransferPanel.h"
#import <ctype.h>
#import <sys/wait.h>
#import <objc/objc-runtime.h>
#import "regexp.h"
#ifndef EMBEDDEDCELLFIX
# define EMBEDDEDCELLFIX 1
#endif
NXBundle *EnhanceBundle=0;
char *EnhanceVersion="X";
char *regerrval=0;
static const char *CVSName="EMrelease2_2p1";
static BOOL windowsOnUnhide=NO;
void EnhanceBundleInit()
{
if (!EnhanceBundle)
{
EnhanceBundle = [NXBundle bundleForClass:[EnhanceMail class]];
}
}
NXZone *EnhanceScratchZone()
{
static NXZone *zone;
if (zone) return zone;
if ((zone = NXCreateZone(getpagesize(), getpagesize(), YES)) == NULL)
{
return NXDefaultMallocZone();
}
NXNameZone(zone, "EnhanceScratch");
return zone;
}
BOOL EnhanceControlP(void)
{
NXEvent *ev=[NXApp currentEvent];
return ev && (ev->flags & NX_CONTROLMASK);
}
BOOL EnhanceShiftP(void)
{
NXEvent *ev=[NXApp currentEvent];
return ev && (ev->flags & NX_SHIFTMASK);
}
BOOL EnhanceAlternateP(void)
{
NXEvent *ev=[NXApp currentEvent];
return ev && (ev->flags & NX_ALTERNATEMASK);
}
BOOL EnhanceCommandP(void)
{
NXEvent *ev=[NXApp currentEvent];
return ev && (ev->flags & NX_COMMANDMASK);
}
BOOL EnhancePaths(const char *paths[])
{
int i;
char path[MAXPATHLEN+1];
EnhanceBundleInit();
for(i=0;paths[i];i++)
{
if ((paths[i][0]=='~') && (paths[i][1]=='/'))
{
sprintf(path,"%s%s",NXHomeDirectory(),paths[i]+1);
paths[i]=NXUniqueString(path);
}
else if ((paths[i][0]=='#') && (paths[i][1]=='/'))
{
if (![EnhanceBundle getPath:path forResource:&paths[i][2] ofType:NULL])
{
sprintf(path,"%s%s",[EnhanceBundle directory],paths[i]+1);
}
paths[i]=NXUniqueString(path);
}
}
return YES;
}
// Try to determine whether to use KANJI quoting at runtime.
static BOOL use_kanji()
{
#if 0 // Old way; depended on user default language preference:
const char *const *languages=[NXApp systemLanguages];
if (languages && languages[0])
{
if (strcasecmp(languages[0],"Japanese")==0) return YES;
}
return NO;
#endif
// Heuristically determines whether we are loaded into Japanese Mail.app:
return [Defaults instancesRespondTo:@selector(isJapaneseSubjectEnabled)];
}
BOOL EnhanceUseKanji(void)
{
// Cache result of use_kanji().
static int _use_kanji = -1;
if (_use_kanji < 0) _use_kanji = use_kanji();
return (BOOL)_use_kanji;
}
void regerror(char *s)
{
[NXApp logError:"regexp(3): %s",s];
}
// email address pattern matching.
static BOOL _match_addr(const char *str, const char **start, int *len, BOOL addr_only)
{
static regexp *addrrx = 0;
static const int addrsub[] = { 1, 4, 2, 3, 5 };
int i;
if (!(str && *str)) return NO;
if (!addrrx)
addrrx = regcomp("^[ \t\"']*([^<>]*[^ \t\"'<>]|)[ \t\"']*<([^<> \t]*)>[^<>]*$" "|"
"^[ \t]*([^() \t]*)[ \t]*\\(([^()]*)\\)[ \t]*$" "|"
"^[ \t\"']*([^<>()]*[^ \t\"'<>()])[ \t\"']*$");
if (!addrrx) return NO;
if (!regexec(addrrx, str)) return NO;
for (i = (addr_only) ? 2 : 0; i < sizeof addrsub / sizeof addrsub[0]; i++)
{
int sub = addrsub[i];
if (addrrx->endp[sub] > addrrx->startp[sub])
{
(*start) = addrrx->startp[sub];
(*len) = addrrx->endp[sub] - addrrx->startp[sub];
return YES;
}
}
return NO;
}
BOOL EnhanceGetAddress(const char *str, const char **start, int *len)
{
return _match_addr(str, start, len, YES);
}
BOOL EnhanceGetUsername(const char *str, const char **start, int *len)
{
return _match_addr(str, start, len, NO);
}
// Timed entry for periodic unread-mail check in MailBoxes panel.
static void _teNewMailCheckHandler(DPSTimedEntry te, double now, EnhanceTransferPanel *self)
{
[self updateBrowser];
}
static void setNewMailCheckPeriod(id self, int minutes)
{
static DPSTimedEntry te;
static int pollTime = 0;
if (pollTime == minutes) return;
if (te) DPSRemoveTimedEntry(te);
if (minutes > 0) te = DPSAddTimedEntry(minutes * 60, (DPSTimedEntryProc)_teNewMailCheckHandler,
(void *)self, NX_RUNMODALTHRESHOLD);
pollTime = minutes;
}
static void setNewMailCheckTimer()
{
Defaults *d = [Defaults new];
setNewMailCheckPeriod([TransferPanel new], [d autoFetch] ? [d pollTime] : -1);
}
@implementation EnhanceMail
+ finishLoading:(struct mach_header *)header
{
const char *beg=CVSName,*end=CVSName+strlen(CVSName);
[self poseAs:[self superclass]];
[NXApp changeClassTo:[self class]];
while((beg<=end) && !isdigit(*beg)) beg++;
while((beg<=end) && !isdigit(*end)) end--;
if (beg<end)
{
char *c;
end++;
EnhanceVersion=strncpy(malloc(end-beg+1),beg,end-beg);
EnhanceVersion[end-beg]='\0';
for(c=EnhanceVersion;*c;c++) if (*c=='_') *c='.';
}
return self;
}
#if EMBEDDEDCELLFIX
- (void)_fixUrlifierGlitch
{
// Attempt to fix Urlifier '<' copy/paste glitch.
// (using dummy -{read,write}RichText:forView: inherited from Cell)
Class urlClass = objc_lookUpClass("UrlGraphicCell");
if (urlClass)
{
[Text registerDirective:"EnhanceUrlGraphicCell" forClass:urlClass];
}
}
#endif
- (void)_maybeShowPanels
{
Window *origMainWindow = [NXApp mainWindow];
if (EnhanceLaunchMailboxes) [(MailDriver *)NXApp mailboxes:self];
if (EnhanceLaunchAddresses) [(MailDriver *)NXApp address:self];
[origMainWindow makeKeyAndOrderFront:self];
}
- initialCheck:sender
{
///if ([[Defaults new] autoLaunch])
// {{Now always, since persistent summary cache should make it quick enough.}}
{
[[TransferPanel new] loadMailSummaries:YES];
}
if ([self isHidden])
{
windowsOnUnhide=YES;
}
else
{
[self perform:@selector(_maybeShowPanels) with:nil afterDelay:0 cancelPrevious:YES];
}
setNewMailCheckTimer();
#if EMBEDDEDCELLFIX
// Hopefully, at this point all bundles are loaded.
[self _fixUrlifierGlitch];
#endif
return [super initialCheck:sender];
}
- appDidUnhide:sender
{
if (windowsOnUnhide)
{
windowsOnUnhide=NO;
[self perform:@selector(_maybeShowPanels) with:nil afterDelay:0 cancelPrevious:YES];
}
return [super appDidUnhide:sender];
}
#if 0
- (BOOL)updateCell:cell
{
return [super updateCell:cell];
}
#endif
- checkNewMail:(const char *)fp16 beep:(BOOL)fp20
{
id ret = [super checkNewMail:fp16 beep:fp20];
[[TransferPanel new] updateBrowser];
return ret;
}
- (void)defaultsChanged
{
setNewMailCheckTimer();
[super defaultsChanged];
}
- realQuit:sender
{
[[TransferPanel new] flushSummaryCache];
return [super realQuit:sender];
}
@end // EnhanceMail
@implementation Application(EnhanceMail)
- (const char *)appVersion
{
return RC_RELEASE;
}
@end // Application(EnhanceMail)
@implementation Menu(EnhanceMail)
- findCellWithTitle:(const char *)title
{
id cell=nil;
int x, y, rows, cols;
const char *c,*end;
if (title==0) return nil;
for(end=title;(*end) && (*end!='/');end++);
[matrix getNumRows:&rows numCols:&cols];
for(y=0;(y<rows) && (cell==nil);y++) for(x=0;(x<cols) && (cell==nil);x++)
{
if ((cell=[matrix cellAt:y:x])==nil) continue;
if (((c=[cell title])==0)||(strncmp(title,c,end-title)!=0)||(c[end-title]!='\0'))
cell=nil;
}
if (cell==nil)
return nil;
else if ((*end!='\0') && [cell hasSubmenu])
return [[cell target] findCellWithTitle:end+1];
else if (*end=='\0')
return cell;
else
return nil;
}
- addSlashItem:(const char *)title action:(SEL)sel keyEquivalent:(unsigned short)code
{
id cell=nil;
int x, y, rows, cols;
const char *c,*end;
if (title==0) return nil;
for(end=title;(*end) && (*end!='/');end++);
[matrix getNumRows:&rows numCols:&cols];
for(y=0;(y<rows) && (cell==nil);y++) for(x=0;(x<cols) && (cell==nil);x++)
{
if ((cell=[matrix cellAt:y:x])==nil) continue;
if (((c=[cell title])==0)||(strncmp(title,c,end-title)!=0)||(c[end-title]!='\0'))
cell=nil;
}
if ((cell==nil) && (*end=='\0'))
{
cell=[self addItem:title action:sel keyEquivalent:code];
[self display];
return cell;
}
else if (cell==nil)
{
char *d=alloca(end-title+1);
strncpy(d,title,end-title);
d[end-title]='\0';
cell=[self addItem:d action:0 keyEquivalent:0];
[self setSubmenu:[[Menu alloc] initTitle:d] forItem:cell];
[self display];
return [[cell target] addSlashItem:end+1 action:sel keyEquivalent:code];
}
else if ((*end!='\0') && [cell hasSubmenu])
return [[cell target] addSlashItem:end+1 action:sel keyEquivalent:code];
else if (*end=='\0')
return cell;
else
return nil;
}
@end // Menu(EnhanceMail)
@implementation Object(EnhanceMail)
- changeClassTo:class
{
/* Don't try this at home, kids */
isa=class;
return self;
}
@end // Object(EnhanceMail)
@implementation ButtonCell(EnhanceMail)
- (int)state
{
return [self intValue];
}
- setState:(int)anInt
{
return [self setIntValue:anInt];
}
@end // ButtonCell(EnhanceMail)
#if EMBEDDEDCELLFIX
/* Experiment. My hope is this will avoid the `<' left around when
text containing embedded Cell is copied. Unfortunately, this
only helps for Forwarding messages -- conversion to plain-text
(hence also quoting) still leave the droppings. Actually, we're
also leaving droppings except they're invisible. */
@interface EnhanceNullCell:Cell
@end
@implementation EnhanceNullCell
- calcCellSize:(NXSize *)s { s->width = s->height = 0; return self; }
- drawSelf:(const NXRect *)rect inView:view { return self; }
- highlight:(const NXRect*)rect inView:view lit:(BOOL)lit { return self; }
//- (BOOL)trackMouse:(NXEvent *)theEvent inRect:(const NXRect *)rect ofView:view { return YES; }
@end
@implementation Cell (EnhanceTextEmbedFix)
+ finishLoading:(struct mach_header *)header
{
[Text registerDirective:"EnhanceCell" forClass:[Cell class]];
[Text registerDirective:"EnhanceNullCell" forClass:[EnhanceNullCell class]];
return self;
}
- writeRichText:(NXStream *)stream forView:view
{
return self;
}
- readRichText:(NXStream *)stream forView:view
{
[self changeClassTo:[EnhanceNullCell class]]; // Gaaaaaaaaaaahhh!!!
return nil;
}
@end // Cell (EnhanceTextEmbedFix)
#endif
// Adjust pre-/postSelSmartTables for more convenient email address pasting.
// (Original idea from Marc Guenther <yoda@cis.uni-muenchen.de>)
@interface EnhanceText : Text
@end
@implementation EnhanceText
+ finishLoading:(struct mach_header *)header
{
[self poseAs:[self superclass]];
return self;
}
static const unsigned char *cacheAppendTo(const unsigned char *table, const unsigned char *extra)
{
static HashTable *hash;
unsigned char *result;
if (!(extra && *extra)) return table;
if (hash == nil) hash = [[HashTable alloc] initKeyDesc:"!" valueDesc:"*"];
result = [hash valueForKey:table];
if (result == NULL)
{
result = malloc(strlen(table) + strlen(extra) + 1);
if (result == NULL) return table;
strcat(strcpy(result, table), extra);
[hash insertKey:table value:result];
}
return result;
}
- initFrame:(const NXRect *)frameRect
text:(const char *)theText
alignment:(int)mode
{
if ((self = [super initFrame:frameRect text:theText alignment:mode]))
{
if (EnhancePreSelExtra && *EnhancePreSelExtra)
{
[self setPreSelSmartTable:cacheAppendTo([self preSelSmartTable], EnhancePreSelExtra)];
}
if (EnhancePostSelExtra && *EnhancePostSelExtra)
{
[self setPostSelSmartTable:cacheAppendTo([self postSelSmartTable], EnhancePostSelExtra)];
}
}
return self;
}
@end // EnhanceText
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.