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.