This is Preferences.m in view mode; [Download] [Up]
/* -*-ObjC-*-
*******************************************************************************
*
* File: Preferences.m
* RCS: /usr/local/sources/CVS/EnhanceMail/Preferences.m,v 1.25 1998/07/12 10:55:53 tom Exp
* Description:
* Author: Carl Edman
* Created: Wed Nov 1 09:58:01 1995
* Modified: 1-Jun-98 (Tom Hageman)
* Language: C
* Package: N/A
* Status: Experimental (Do Not Distribute)
*
* (C) Copyright 1995, but otherwise this file is perfect freeware.
*
*******************************************************************************
*/
#define PREFEXTERN
#import "Preferences.h"
#undef PREFEXTERN
#import "EnhanceMail.h"
#import "PGP.h"
#import "XFace.h"
#import "XImageURL.h"
#import "regexp.h"
#import <ctype.h>
#import <math.h>
#import <netdb.h>
#define NOTYET 0
#define MSG_OK NXLoadLocalizedStringFromTableInBundle("Buttons", nil, "OK", NULL)
#define MSG_BAD_XFACE NXLocalizedStringFromTableInBundle("Localizable", EnhanceBundle, "%s does not contain monochrome 48x48 image.", NULL, Error message for bad XFace)
#define MSG_BAD_REGEXP NXLocalizedStringFromTableInBundle("Localizable", EnhanceBundle, "%s is not a valid regular expression.", NULL, Error message for bad Regexp)
#define DEFAULT_QUOTE_INTRO NXLocalizedStringFromTableInBundle("Localizable", EnhanceBundle, "You wrote:", NULL, Default introduction to quotes in replies)
#define DEFAULT_FLAG_SEARCH (EnhanceUseKanji() ? " «" : "«")
#define DEFAULT_SMILIES NXLoadLocalized\
StringFromTableInBundle("Localizable", EnhanceBundle, "Smilies", \
":-\\]\nFaceGrinning.tiff\n\
:-[oO]\nFaceStartled.tiff\n\
:-[)>]\nFaceHappy.tiff\n\
;-[>)]\nFaceWinking.tiff\n\
:-[/\\\\]\nFaceIronic.tiff\n\
:-|\nFaceStraight.tiff\n\
[>}]-[(<]\nFaceAngry.tiff\n\
[:&]-[(<]\nFaceSad.tiff\n\
:-d\nFaceTasty.tiff\n\
:-[pP]\nFaceYukky.tiff\n\
:-[yY]\nFaceWry.tiff\n\
[X8]-|\nFaceKOed.tiff\n\
}-)\nFaceEvilGrin.tiff\n\
}->\nFaceSatanicGrin.tiff")
static struct PrefDefault
{
struct PrefDefault *next;
enum PrefDefaultType { bool=1, string, integer, color } type;
union
{
BOOL *bool;
const char **string;
int *integer;
NXColor *color;
} location;
id control;
char name[0];
} *prefroot=0;
static struct PrefDefault *allocPrefDefault(id self, const char *name, const char *con, enum PrefDefaultType type);
static void savePrefs(id self);
static void loadPrefs(id self);
static void downloadPrefsFromPanel(id prefPanel);
static void uploadPrefsIntoPanel(id prefPanel);
static HashTable *PanelHash=nil;
static NXImage *delete_image=nil;
static id XImageURLButton=nil;
static id XFaceButton=nil;
@implementation EnhancePreferences
+ finishLoading:(struct mach_header *)header
{
id pref;
char path[MAXPATHLEN+1];
EnhanceBundleInit();
[self poseAs:[self superclass]];
if ([EnhanceBundle getPath:path forResource:"delete" ofType:"tiff"])
delete_image=[[NXImage alloc] initFromFile:path];
pref=[Preferences new];
if ([EnhanceBundle getPath:path forResource:"EnhancePreferences" ofType:"nib"])
[NXApp loadNibFile:path owner:pref];
[pref addPanel:NXGetNamedObject("Quoting_Box2",pref)];
[pref addPanel:NXGetNamedObject("Mailboxes_Box2",pref)];
[pref addPanel:NXGetNamedObject("Images_Box2",pref)];
[pref addPanel:NXGetNamedObject("PGP_Box2",pref)];
[pref addPanel:NXGetNamedObject("Options_Box2",pref)];
[pref registerBool:"QuoteReplies"
location:&EnhanceQuoteReplies
control:"Quoting_Box2_Quoting_Quote Replies"
default:NO];
[pref registerString:"QuoteIntro"
location:&EnhanceQuoteIntro
control:"Quoting_Box2_Quoting_Form_Introduction"
default:DEFAULT_QUOTE_INTRO];
[pref registerString:"QuotePrefix"
location:&EnhanceQuotePrefix
control:"Quoting_Box2_Quoting_Form_Prefix"
default:"> %0"];
[pref registerString:"QuoteRegex"
location:&EnhanceQuoteRegex
control:"Quoting_Box2_Quoting_Form_Regex"
default:"^[>:# ]*[>:#] ?"];
[pref registerBool:"QuoteColoring"
location:&EnhanceQuoteColoring
control:"Quoting_Box2_Quoting_Color Quotes"
default:NO];
[pref registerBool:"QuoteColoringInMailbox"
location:&EnhanceQuoteColoringInMailbox
control:"Quoting_Box2_Quoting_Color Quotes1"
default:NO];
[pref registerColor:"QuoteColor"
location:&EnhanceQuoteColor
control:"Quoting_Box2_Quoting_Color"
default:NX_COLORBLACK];
[pref registerBool:"QuoteRemoveCellDroppingHack"
location:&EnhanceRemoveCellDroppings
control:0
default:NO];
[pref registerBool:"InsertSignature"
location:&EnhanceInsertSignature
control:"Quoting_Box2_Signatures_Insert Signatures"
default:NO];
[pref registerString:"SignatureFilename"
location:&EnhanceSignatureFilename
control:"Quoting_Box2_Signatures_Form_Filename"
default:".signature"];
[pref registerString:"SignatureSeparator"
location:&EnhanceSignatureSeparator
control:"Quoting_Box2_Signatures_Form_Separator"
default:"---\n"];
[pref registerBool:"InsertXFace"
location:&EnhanceInsertXFace
control:"Images_Box2_XFace_Form_Send XFace"
default:NO];
[pref registerBool:"ShowXFace"
location:&EnhanceShowXFace
control:"Images_Box2_XFace_Form_Show XFace"
default:NO];
XFaceButton=NXGetNamedObject("Images_Box2_XFace_Box_Image",pref);
[pref registerString:"XFace"
location:&EnhanceXFace
control:"Images_Box2_XFace_Path"
default:""];
[pref registerBool:"InsertXImageURL"
location:&EnhanceInsertXImageURL
control:"Images_Box2_XImageURL_Form_Send XImageURL"
default:NO];
[pref registerBool:"ShowXImageURL"
location:&EnhanceShowXImageURL
control:"Images_Box2_XImageURL_Form_Show XImageURL"
default:NO];
XImageURLButton=NXGetNamedObject("Images_Box2_XImageURL_Box_Image",pref);
[pref registerString:"XImageURL"
location:&EnhanceXImageURL
control:"Images_Box2_XImageURL_Path"
default:""];
[pref registerBool:"UsePGP"
location:&EnhanceUsePGP
control:"PGP_Box2_PGP_Use PGP"
default:NO];
[pref registerInteger:"PGPPassTimeout"
location:&EnhancePGPPassTimeout
control:"PGP_Box2_PGP1_Pass Timeout"
default:0];
[pref registerBool:"PGPPassAlwaysAskForSigning"
location:&EnhancePGPPassAskForSigning
control:"PGP_Box2_PGP1_Always Ask"
default:YES];
[pref registerBool:"RetrievePGPKeys"
location:&EnhanceRetrievePGPKeys
control:NOTYET//"PGP_Box2_PGP2_Retrieve PGP Keys"
default:NO];
[pref registerBool:"PGPSign"
location:&EnhancePGPSign
control:"PGP_Box2_PGP_Buttons_Sign"
default:NO];
[pref registerBool:"PGPEncrypt"
location:&EnhancePGPEncrypt
control:"PGP_Box2_PGP_Buttons_Encrypt"
default:NO];
[pref registerString:"PGPPath"
location:&EnhancePGPPath
control:"PGP_Box2_PGP_Texts_PGP Path"
default:"/usr/local/bin/pgp"];
[pref registerInteger:"PGPVersion"
location:&EnhancePGPVersion
control:"PGP_Box2_PGP_Versions"
default:PGP_VERSION_2_6];
[pref registerString:"KeyServerURL"
location:&EnhanceKeyServerURL
control:"PGP_Box2_PGP2_Key Server URL"
default:"http://wwwkeys.pgp.net/pks/lookup?op=get&search="];
[pref registerBool:"PGPFancyDisplay"
location:&EnhancePGPFancyStatusDisplay
control:NOTYET//"PGP_Box2_PGP_Buttons_Fancy Status Display"
default:NO];
[pref registerBool:"RepliesWithOriginals"
location:&EnhanceRepliesWithOriginals
control:"Options_Box2_Other_Form_Keep Replies with Originals"
default:NO];
[pref registerBool:"AutoSpellCheck"
location:&EnhanceAutoSpellCheck
control:"Options_Box2_Other_Form_Automatically spell check messages"
default:NO];
[pref registerBool:"AutoCompact"
location:&EnhanceAutoCompact
control:"Options_Box2_Other_Form_Automatically compact mailboxes"
default:NO];
[pref registerBool:"LaunchMailboxes"
location:&EnhanceLaunchMailboxes
control:"Mailboxes_Box2_Launch_Form_Mailboxes"
default:NO];
[pref registerBool:"LaunchAddresses"
location:&EnhanceLaunchAddresses
control:"Mailboxes_Box2_Launch_Form_Addresses"
default:NO];
[pref registerBool:"FlagReplies"
location:&EnhanceFlagReplies
control:"Options_Box2_Other_Form_Flag Replies"
default:NO];
[pref registerBool:"ExpandSmilies"
location:&EnhanceExpandSmilies
control:"Options_Box2_Other_Form_Expand Smilies"
default:NO];
[pref registerInteger:"MaxTocCheckSize"
location:&EnhanceMaxTocCheckSize
control:"Mailboxes_Box2_Mailboxes_MaxTocCheckSize"
default:100];
[pref registerInteger:"MaxSmileyExpansionMessageSize"
location:&EnhanceMaxSmileyExpansionMessageSize
control:0
default:-1];
[pref registerInteger:"MaxQuoteColorMessageSize"
location:&EnhanceMaxQuoteColorMessageSize
control:0
default:-1];
[pref registerBool:"RestoreDeletesDraft"
location:&EnhanceRestoreDeletesDraft
control:0
default:NO];
[pref registerBool:"BackSpaceBackPages"
location:&EnhanceBackSpaceBackPages
control:0
default:NO];
[pref registerString:"HTTPProxy"
location:&EnhanceHTTPProxy
control:"Images_Box2_Proxy_HTTP Proxy"
default:""];
[pref registerString:"Smilies"
location:&EnhanceSmilies
control:"EnhanceSmileyTableViewController"
default:DEFAULT_SMILIES];
[pref registerBool:"SilentNewMail"
location:&EnhanceSilentNewMail
control:"Mailboxes_Box2_Mailboxes_Form_SilentNewMail"
default:NO];
[pref registerBool:"CheckPendingAppnmail"
location:&EnhanceCheckPendingAppnmail
control:"Mailboxes_Box2_Mailboxes_Form_CheckPendingAppnmail"
default:YES];
[pref registerBool:"ReallyUseFixedPitchFontForPlainMessages"
location:&EnhanceUseFixedPitchFontForPlainMessages
control:"Options_Box2_Other_Form_ReallyUseFixedPitch"
default:YES];
[pref registerBool:"ShowFlagged"
location:&EnhanceShowFlagged
control:"Mailboxes_Box2_Mailboxes_Form_ShowFlagged"
default:NO];
[pref registerInteger:"MailboxCheckInterval"
location:&EnhanceMailboxCheckInterval
control:0
default:0];
[pref registerString:"FlagSearch"
location:&EnhanceFlagSearchString
control:0
default:DEFAULT_FLAG_SEARCH];
[pref registerString:"PreSelExtra"
location:&EnhancePreSelExtra
control:0
default:"@<%!."];
[pref registerString:"PostSelExtra"
location:&EnhancePostSelExtra
control:0
default:"@>"];
loadPrefs(pref);
return self;
}
- (void)downLoad
{
downloadPrefsFromPanel(prefPanel);
savePrefs(self);
[super downLoad];
}
- (void)upLoad
{
loadPrefs(self);
uploadPrefsIntoPanel(prefPanel);
[super upLoad];
}
- show
{
uploadPrefsIntoPanel(prefPanel);
return [super show];
}
- controlFor:(const void *)location
{
struct PrefDefault *pd;
for(pd=prefroot; pd; pd=pd->next)
{
if ((const void *)(pd->location.string) == location) return pd->control;
}
return nil;
}
- addPanel:panel
{
id cell;
id popuplist;
int tag;
if (panel==nil) return nil;
popuplist=[[viewButton cell] target];
for(tag=0;[popuplist findCellWithTag:tag]!=nil;tag++);
cell=[popuplist addItem:[[panel window] title]];
[cell setTarget:self];
[cell setAction:@selector(changeView:)];
[cell setTag:tag];
if (PanelHash==nil) PanelHash=[[HashTable alloc] initKeyDesc:"i" valueDesc:"@"];
[PanelHash insertKey:(const void *)tag value:panel];
return self;
}
- registerBool:(const char *)name location:(BOOL *)loc control:(const char *)con default:(BOOL)def
{
struct PrefDefault *pd=allocPrefDefault(self, name, con, bool);
pd->location.bool=loc;
*loc=def;
return self;
}
- registerString:(const char *)name location:(const char **)loc control:(const char *)con default:(const char *)def
{
struct PrefDefault *pd=allocPrefDefault(self, name, con, string);
pd->location.string=loc;
*loc=def;
return self;
}
- registerInteger:(const char *)name location:(int *)loc control:(const char *)con default:(int)def
{
struct PrefDefault *pd=allocPrefDefault(self, name, con, integer);
pd->location.integer=loc;
*loc=def;
return self;
}
- registerColor:(const char *)name location:(NXColor *)loc control:(const char *)con default:(NXColor)def
{
struct PrefDefault *pd=allocPrefDefault(self, name, con, color);
pd->location.color=loc;
*loc=def;
return self;
}
- changeView:sender
{
id ret,item,panel,ogeneralview;
int tag;
if (((item=[viewButton selectedPopUpItem])==nil)
||((tag=[item tag])==0)
||(PanelHash==nil)
||((panel=[PanelHash valueForKey:(const void *)tag])==nil))
return [super changeView:sender];
ogeneralview=generalView;
generalView=panel;
[item setTag:0];
ret=[super changeView:sender];
[item setTag:tag];
generalView=ogeneralview;
return ret;
}
- noteChangePassPhraseTimeout:sender
// slider action method, instead of direct -takeIntValueFrom:,
// for -noteChange:
{
id textField = [self controlFor:&EnhancePGPPassTimeout];
[textField takeIntValueFrom:sender];
return [self noteChange:sender];
}
- noteChangeXFace:sender
{
id XFace=[self controlFor:&EnhanceXFace];
const char *val;
NXRect r;
NXImage *image;
if (!XFace || !XFaceButton) return nil;
val=[XFace stringValue];
[XFaceButton getBounds:&r];
image=[[NXImage alloc] initFromFile:val xFaceSize:&r.size];
if (image==nil) image=delete_image;
[XFaceButton setImage:image];
return [self noteChange:sender];
}
- noteChangeXImageURL:sender
{
id XImageURL=[self controlFor:&EnhanceXImageURL];
NXRect r;
NXSize size;
NXImage *image;
const char *val;
if (!XImageURL || !XImageURLButton) return nil;
val=[XImageURL stringValue];
[XImageURLButton getBounds:&r];
image=[[NXImage alloc] initURL:val];
if (image==nil) image=delete_image;
// [TRH] Don't scale image if it isn't necessary,
// and keep size/width proportions intact if it is.
[image getSize:&size];
if (size.height > r.size.height || size.width > r.size.width)
{
NXSize newsize = r.size;
if (size.height > size.width)
newsize.width = newsize.width*size.width/size.height;
else if (size.height < size.width)
newsize.height = newsize.height*size.height/size.width;
[image setScalable:YES];
[image setSize:&newsize];
}
[XImageURLButton setImage:image];
return [self noteChange:sender];
}
- setXFace:sender
{
id open;
const char *const types[] = { "tiff", 0 };
id XFaceC=[self controlFor:&EnhanceXFace];
if (((XFaceC=[self controlFor:&EnhanceXFace])==nil)
|| ((open=[OpenPanel new])==nil)
|| ([open allowMultipleFiles:NO]==nil)
|| ([open runModalForDirectory:[XFaceC stringValue] file:0 types:types]!=NX_OKTAG)
|| ([open filename]==0))
return nil;
[XFaceC setStringValue:[open filename]];
return [self noteChangeXFace:sender];
}
@end // EnhancePreferences
// PrefDefault manipulation support functions.
static const char *stringValueOfPref(struct PrefDefault *pd, char *buf)
{
const char *s = "";
switch (pd->type)
{
case bool:
s = (*(pd->location.bool) ? "YES" : "NO");
break;
case string:
s = *(pd->location.string);
break;
case integer:
sprintf(buf, "%d", *(pd->location.integer));
s = buf;
break;
case color:
if ((s = NXColorName(*(pd->location.color))) == NULL)
{
float red, green, blue;
NXConvertColorToRGB(*(pd->location.color), &red, &green, &blue);
sprintf(buf, "RGB%02x%02x%02x", (int)rint(red*0xff), (int)rint(green*0xff), (int)rint(blue*0xff));
s = buf;
}
break;
}
return s;
}
static void setPrefToStringValue(id self, struct PrefDefault *pd, const char *value)
{
int i;
const char *c;
switch (pd->type)
{
case bool:
if (strcasecmp(value,"YES")==0)
*(pd->location.bool)=YES;
else if (strcasecmp(value,"NO")==0)
*(pd->location.bool)=NO;
else
[self logWarning:"%s: Boolean default %s given bad value \"%s\".",[NXApp appName],pd->name,value];
break;
case string:
*(pd->location.string)=value;
break;
case integer:
i=strtol(value,&c,0);
while(isspace(*c)) c++;
if (*c=='\0')
*(pd->location.integer)=i;
else
[self logWarning:"%s: Integer default %s given bad value \"%s\".",[NXApp appName],pd->name,value];
break;
case color:
i=-1;
if ((strncmp(value,"RGB",3)==0)&&(strlen(value)==9))
{
for(c=value+3,i=0;*c;c++)
{
if (*c>='0' && *c<='9') i=16*i+*c-'0';
else if (*c>='a' && *c<='f') i=16*i+*c-('a'-10);
else if (*c>='A' && *c<='F') i=16*i+*c-('A'-10);
else break;
}
if (*c)
i=-1;
else
{
*(pd->location.color)=NXConvertRGBToColor(((i>>16)&0xff)/255.0,((i>>8)&0xff)/255.0,(i&0xff)/255.0);
i=0;
}
}
if (i<0)
{
id colorlistlist=[NXColorList availableColorLists];
for(i=[colorlistlist count]-1;i>=0;i--)
{
if (NXFindColorNamed([[colorlistlist objectAt:i] name],value,pd->location.color))
break;
}
}
if (i<0)
[self logWarning:"%s: Color default %s given bad value \"%s\".",[NXApp appName],pd->name,value];
break;
}
}
/* keep score of the total number of existing PrefDefaults,
and the number of these registered with NXRegisterDefaults -- this
is done lazily registerNewPrefs() to allow batching. */
static int totalPrefs=0, registeredPrefs=0;
static struct PrefDefault *allocPrefDefault(id self, const char *name, const char *con, enum PrefDefaultType type)
{
struct PrefDefault *pd=malloc(sizeof(*pd)+strlen(name)+1);
strcpy(pd->name,name);
pd->type=type;
pd->control=con ? NXGetNamedObject(con,self) : nil;
if (pd->control && [pd->control respondsTo:@selector(setEnabled:)])
{
[pd->control setEnabled:YES];
}
pd->next=prefroot;
prefroot=pd;
++totalPrefs;
return pd;
}
static void registerNewPrefs()
{
int n = (totalPrefs - registeredPrefs);
if (n > 0)
{
char buf[100];
struct _NXDefault defaults[n+1]; // !!! using GNU C extension.
struct PrefDefault *pd = prefroot;
int i;
for (i = 0; i < n; i++)
{
if (pd == NULL) break;
defaults[i].name = pd->name;
/* I'm not sure if NXRegisterDefaults make a private copy of
these values, or it just stores the pointer. In any case,
just to be on the safe side, keep the values laying around. */
defaults[i].value = (char *)NXUniqueString(stringValueOfPref(pd, buf));
pd = pd->next;
}
defaults[i].name = defaults[i].value = NULL;
NXRegisterDefaults([NXApp appName], defaults);
}
registeredPrefs = totalPrefs;
}
static void savePrefs(id self)
{
int n;
registerNewPrefs();
if ((n = totalPrefs) > 0)
{
char buf[100];
struct _NXDefault defaults[n+1]; // !!! using GNU C extension.
struct PrefDefault *pd = prefroot;
int i;
for (i = 0; i < n; i++)
{
if (pd == NULL) break; /* shouldn't happen. */
defaults[i].name = pd->name;
defaults[i].value = NXCopyStringBuffer(stringValueOfPref(pd, buf));
pd = pd->next;
}
defaults[i].name = defaults[i].value = NULL;
NXWriteDefaults([NXApp appName], defaults);
while (--i >= 0) free(defaults[i].value); // cleanup.
}
}
static void loadPrefs(id self)
{
struct PrefDefault *pd;
const char *value;
const char *appname = [NXApp appName];
registerNewPrefs();
NXUpdateDefaults();
for(pd=prefroot; pd; pd=pd->next)
{
if ((value = NXGetDefaultValue(appname, pd->name)) != NULL)
{
setPrefToStringValue(self, pd, value);
}
}
}
static void downloadPrefsFromPanel(id prefPanel)
{
struct PrefDefault *pd;
for(pd=prefroot; pd; pd=pd->next)
{
if (pd->control!=nil)
{
switch (pd->type)
{
case bool:
*pd->location.bool=[pd->control state];
break;
case string:
*pd->location.string=[pd->control stringValue];
break;
case integer:
if ([pd->control isKindOf:[Matrix class]]) // Assume Radio buttons.
*pd->location.integer=[[pd->control selectedCell] tag];
else
*pd->location.integer=[pd->control intValue];
break;
case color:
*pd->location.color=[pd->control color];
break;
}
}
}
}
static void uploadPrefsIntoPanel(id prefPanel)
{
SEL noteChangeSelector = @selector(noteChange:);
SEL sendActionToSelector = @selector(sendAction:to:);
SEL controlViewSelector = @selector(controlView);
struct PrefDefault *pd;
for(pd=prefroot; pd; pd=pd->next)
{
id con;
if (pd->control != nil)
{
switch (pd->type)
{
case bool:
[pd->control setState:*(pd->location.bool)];
break;
case string:
[pd->control setStringValue:*(pd->location.string)];
break;
case integer:
if ([pd->control isKindOf:[Matrix class]]) // Assume Radio buttons.
[pd->control selectCellWithTag:*(pd->location.integer)];
else
[pd->control setIntValue:*(pd->location.integer)];
break;
case color:
[pd->control setColor:*(pd->location.color)];
break;
}
con = pd->control;
if ([con respondsTo:sendActionToSelector] ||
([con respondsTo:controlViewSelector] &&
(con = [con controlView]) != nil &&
[con respondsTo:sendActionToSelector]))
{
SEL action = [pd->control action];
if (action != noteChangeSelector)
{
[con sendAction:action to:[pd->control target]];
}
}
}
}
[prefPanel setDocEdited:NO];
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.