/* -*-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];
}
