/* -*-C-*-
*******************************************************************************
*
* File:         Preferences.m
* RCS:          $Header: /usr/local/lib/cvs/EnhanceMail/Preferences.m,v 1.1.1.30 1996/07/01 01:10:26 cedman Exp $
* Description:
* Author:       Carl Edman
* Created:      Wed Nov  1 09:58:01 1995
* Modified:     Thu Jun 27 20:06:19 1996 (Carl Edman) cedman@capitalist.princeton.edu
* 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 "XFace.h"
#import "XImageURL.h"
#import "regexp.h"
#import <ctype.h>
#import <math.h>
#import <netdb.h>

#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)

static struct PrefDefault
   {
   struct PrefDefault *next;
   enum { 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 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];

   if (EnhanceBundle==0) EnhanceBundle=[NXBundle bundleForClass:[self class]];
   [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("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 registerColor:"QuoteColor"
              location:&EnhanceQuoteColor
               control:"Quoting_Box2_Quoting_Color"
               default:NX_COLORBLACK];
   [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_PGP_Pass Timeout"
                 default:0];
   [pref registerBool:"RetrievePGPKeys"
             location:&EnhanceRetrievePGPKeys
              control:"PGP_Box2_PGP_Buttons_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 registerString:"KeyServerURL"
               location:&EnhanceKeyServerURL
                control:"PGP_Box2_PGP_Texts_Key Server URL"
                default:"http://pgp.ai.mit.edu/htbin/pks-extract-key.pl?op=get&search="];
      
   [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:"Options_Box2_Other_Form_Open Mailboxes Window on Launch"
              default:NO];
   [pref registerBool:"LaunchAddresses"
             location:&EnhanceLaunchAddresses
              control:"Options_Box2_Other_Form_Open Addresses Window on Launch"
              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 registerBool:"RestoreDeletesDraft"
             location:&EnhanceRestoreDeletesDraft
              control:0
              default:NO];
   [pref registerBool:"BackSpaceBackPages"
             location:&EnhanceBackSpaceBackPages
              control:0
              default:NO];
   [pref registerString:"HTTPProxy"
               location:&EnhanceHTTPProxy
                control:0
                default:""];
   [pref registerString:"Smilies"
               location:&EnhanceSmilies
                control:0
                default:
":-*\\]\nFaceGrinning.tiff\n\
:-*[oO]\nFaceStartled.tiff\n\
:-*[)>]\nFaceHappy.tiff\n\
;-*[>)]\nFaceWinking.tiff\n\
:-[/\\\\]\nFaceIronic.tiff\n\
:-*|\nFaceStraight.tiff\n\
:-[(<]\nFaceAngry.tiff\n\
:-*d\nFaceTasty.tiff\n\
:-*[pP]\nFaceYukky.tiff\n\
8-*|\nFaceKOed.tiff"
    ];

   [pref upLoad];
   return self;
   }

- (void)downLoad
   {
   struct PrefDefault *pd;
   const char *appname, *tmp;
   char buf[10240];

   appname=[NXApp appName];

   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:
      *pd->location.integer=[pd->control intValue];
      break;
    case color:
      *pd->location.color=[pd->control color];
      break;
      }

   for(pd=prefroot;pd;pd=pd->next) switch (pd->type)
      {
    case bool:
      NXWriteDefault(appname,pd->name,*(pd->location.bool) ? "YES" : "NO");
      break;
    case string:
      NXWriteDefault(appname,pd->name,*(pd->location.string));
      break;
    case integer:
      sprintf(buf,"%d",*(pd->location.integer));
      NXWriteDefault(appname,pd->name,buf);
      break;
    case color:
      if (tmp=NXColorName(*(pd->location.color)))
         NXWriteDefault(appname,pd->name,tmp);
      else
         {
         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));
         NXWriteDefault(appname,pd->name,buf);
         }
      break;
      }

   [super downLoad];
   }

- (void)upLoad
   {
   int i;
   struct PrefDefault *pd;
   const char *appname, *value, *c;
   id colorlistlist=[NXColorList availableColorLists];

   [super upLoad];
   NXUpdateDefaults();
   appname=[NXApp appName];

   for(pd=prefroot;pd;pd=pd->next) if (value=NXReadDefault(appname,pd->name)) 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:"Boolean default %s given bad value \"%s\".",appname,pd->name,value];
      break;
    case string:
      *(pd->location.string)=value;
      break;
    case integer:
      for(c=value;isspace(*c);c++);
      for(i=0;isdigit(*c);c++) i=i*10+(*c-'0');
      while(isspace(*c)) c++;
      if (*c=='\0')
         *(pd->location.integer)=i;
      else
         [self logWarning:"Integer default %s given bad value \"%s\".",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) for(i=[colorlistlist count]-1;i>=0;i--)
         {
         if (NXFindColorNamed([[colorlistlist objectAt:i] name],value,pd->location.color))
            break;
         }

      if (i<0)
         [self logWarning:"Color default %s given bad value \"%s\".",appname,pd->name,value];
      break;
      }

   for(pd=prefroot;pd;pd=pd->next) 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:
         [pd->control setIntValue:*(pd->location.integer)];
         break;
       case color:
         [pd->control setColor:*(pd->location.color)];
         break;
         }

      if (pd->control)
         {
         id con;
         
         if ([pd->control respondsTo:@selector(sendAction:to:)])
            [pd->control sendAction:[pd->control action] to:[pd->control target]];
         else if ([pd->control respondsTo:@selector(controlView)]
                  && ((con=[pd->control controlView])!=nil)
                  && [con respondsTo:@selector(sendAction:to:)])
            [con sendAction:[pd->control action] to:[pd->control target]];
         }
      }
   
   [prefPanel setDocEdited:NO];
   }

- controlFor:(const void *)location
   {
   struct PrefDefault *pd;
   for(pd=prefroot;pd;pd=pd->next)
      if ((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=malloc(sizeof(*pd)+strlen(name)+1);
   pd->next=prefroot;
   pd->type=bool;
   pd->location.bool=loc;
   *loc=def;
   pd->control=con ? NXGetNamedObject(con,self) : nil;
   if ((pd->control!=nil)
       && ([pd->control respondsTo:@selector(setEnabled:)]))
      [pd->control setEnabled:YES];
   strcpy(pd->name,name);
   prefroot=pd;
   return self;
   }

- registerString:(const char *)name location:(const char **)loc control:(const char *)con default:(const char *)def
   {
   struct PrefDefault *pd=malloc(sizeof(*pd)+strlen(name)+1);
   pd->next=prefroot;
   pd->type=string;
   pd->location.string=loc;
   *loc=def;
   pd->control=con ? NXGetNamedObject(con,self) : nil;
   if ((pd->control!=nil)
       && ([pd->control respondsTo:@selector(setEnabled:)]))
      [pd->control setEnabled:YES];
   strcpy(pd->name,name);
   prefroot=pd;
   return self;
   }

- registerInteger:(const char *)name location:(int *)loc control:(const char *)con default:(int)def
   {
   struct PrefDefault *pd=malloc(sizeof(*pd)+strlen(name)+1);
   pd->next=prefroot;
   pd->type=integer;
   pd->location.integer=loc;
   *loc=def;
   pd->control=con ? NXGetNamedObject(con,self) : nil;
   if ((pd->control!=nil)
       && ([pd->control respondsTo:@selector(setEnabled:)]))
      [pd->control setEnabled:YES];
   strcpy(pd->name,name);
   prefroot=pd;
   return self;
   }

- registerColor:(const char *)name location:(NXColor *)loc control:(const char *)con default:(NXColor)def
   {
   struct PrefDefault *pd=malloc(sizeof(*pd)+strlen(name)+1);
   pd->next=prefroot;
   pd->type=color;
   pd->location.color=loc;
   *loc=def;
   pd->control=con ? NXGetNamedObject(con,self) : nil;
   if ((pd->control!=nil)
       && ([pd->control respondsTo:@selector(setEnabled:)]))
      [pd->control setEnabled:YES];
   strcpy(pd->name,name);
   prefroot=pd;
   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;
   }

- 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;
   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;
   [image setScalable:YES];
   [image setSize:&r.size];
   [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
