/******************************************************************************
FILE
    SqPreference.m
DESCRIPTION
	NeXTstep user Interface for Squeak.
	This class reifies Squeak options/preferences.

	The preferences are loaded from the NeXT defaults, and then 
	overriden with the values from the UNIX environment, and then
	overriden with the values from the UNIX arguments.
	
	When changes in Preference Panel are validated, the new preference
	values are saved into the NeXT defaults.
AUTHOR
	<PJB> Pascal J. Bourguignon
MODIFICATIONS
	1998/08/25 <PJB> Creation.
LEGAL
	Copyright Pascal J. Bourguignon 1998 - 1998

	This program is free software; you can redistribute it and/or
	modify it under the terms of the version 2 of the GNU General Public 
	License as published by the Free Software Foundation.
	
	This program is distributed in the hope that it will be useful, but 
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
	hereafter for more details.
******************************************************************************/
#import "SqPreference.h"
#import "SqApplication.h"


@implementation SqPreference

	-(id)init
	{
		self=[super init];
		if(self!=0){
			// Actually, these values are intialized from the defaults
			// registeredd in registerDefault, and then overriden by the
			// arguments got from parseSqueakArguments.
			heapSize=5*1024*1024;
			stackCacheEntries=16;
			strcpy(imagePath,"squeak.image");
			noTitle=NO;
			fullScreen=NO;
			sleepWhenUnmapped=NO;
			[self registerDefaults];
			[self loadValuesFromDefaults];
			[self parseSqueakArguments];
			[NXApp loadNibSection:"Preferences.nib" owner:self withNames:NO];
		}
		return(self);
	}//init;
	
	
	-(id)free
	{
		if(preferencePanelIsModal){
			[NXApp abortModal];
		}
		return([super free]);
	}//free;


	
// Preference values:

	-(int)squeakArgc
	{
		return(squeakArgc);
	}//squeakArgc;
	
	
	-(const char**)squeakArgv
	{
		return(squeakArgv);
	}//squeakArgv;
	
	
	-(int)heapSize
	{
		return(heapSize);
	}//heapSize;
	
	
	-(int)stackCacheEntries
	{
		return(stackCacheEntries);
	}//stackCacheEntries;
	
	
	-(BOOL)noTitle
	{
		return(noTitle);
	}//noTitle;
	
	
	-(BOOL)fullScreen
	{
		return(fullScreen);
	}//fullScreen;
	
	
	-(BOOL)sleepWhenUnmapped
	{
		return(sleepWhenUnmapped);
	}//sleepWhenUnmapped;
	
	
	-(void)getImagePath:(char*)path
	{
		strcpy(path,imagePath);
	}//getImagePath;
	

	-(const char*)imagePath
	{
		return(imagePath);
	}//imagePath;
	
	
	-(const char*)redButtonString
	{
		return(NXGetDefaultValue([NXApp appName],"redButton"));
	}//redButtonString;
	
	
	-(const char*)yellowButtonString
	{
		return(NXGetDefaultValue([NXApp appName],"yellowButton"));
	}//yellowButtonString;
	
	
	-(const char*)blueButtonString
	{
		return(NXGetDefaultValue([NXApp appName],"blueButton"));
	}//blueButtonString;
	
	

	-(void)setHeapSize:(int)size
	{
		heapSize=size;
	}//setHeapSize:;
	
	
	-(void)setStackCacheEntries:(int)value
	{
		stackCacheEntries=value;
	}//setStackCacheEntries:;
	
	
	-(void)setNoTitle:(BOOL)flag
	{
		noTitle=flag;
	}//setNoTitle:;
	
	
	-(void)setFullScreen:(BOOL)flag
	{
		fullScreen=flag;
	}//setFullScreen:;
	
	
	-(void)setSleepWhenUnmapped:(BOOL)flag
	{
		sleepWhenUnmapped=flag;
	}//setSleepWhenUnmapped:;
	
	
	-(void)setImagePath:(const char*)path
	{
		strncpy(imagePath,path,sizeof(imagePath));
		imagePath[sizeof(imagePath)-1]='\0';
	}//setImagePath:;
	

	-(void)setRedButton:(SqButton*)button
	{
		redButton=button;
	}//setRedButton:;
	
	
	-(void)setYellowButton:(SqButton*)button
	{
		yellowButton=button;
	}//setYellowButton:;
	
	
	-(void)setBlueButton:(SqButton*)button
	{
		blueButton=button;
	}//setBlueButton:;
	


// SqPreference methods:

	-(void)openDialog;
	{
		if([imagePathButton window]==0){
			return;
		}
		[self configurePreference];
		[[imagePathButton window]makeKeyAndOrderFront:self];
	}//openDialog;
	
	
	-(void)runModalDialog
	{
		if([imagePathButton window]==0){
			return;
		}
		[self configurePreference];
		preferencePanelIsModal=YES;
		[NXApp runModalFor:[imagePathButton window]];
		preferencePanelIsModal=NO;
	}//runModalDialog;
	
	
// Protected methods:

	static BOOL strtobool(const char* value)
	{
		return(!((value==0)
			||(strncmp(value,"no",2)==0)
			||(strncmp(value,"No",2)==0)
			||(strncmp(value,"NO",2)==0)
			||(strcmp(value,"0")==0)
			||(strcmp(value,"")==0)));
	}//strtobool;
	
	
	static int strtobkm(const char *str)
	{
		char*   suffix;
		int     value=strtol(str,&suffix,10);
		switch(*suffix){
		case 'k': case 'K':
			value*=1024;
			break;
		case 'm': case 'M':
			value*=1024*1024;
			break;
		}
		return(value);
	}//strtobkm;
	
	
	-(void)registerDefaults
	{
		static const NXDefaultsVector Defaults={
			{"imagePath",NULL},
			{"heapSize","5M"},
			{"stackCacheEntries","16"},
			{"noTitle","NO"},
			{"fullScreen","NO"},
			{"sleepWhenUnmapped","NO"},
			{"redButton","l"},
			{"yellowButton","r"},
			{"blueButton","mr"},
			{NULL,NULL}
		};
		NXRegisterDefaults([NXApp appName],Defaults);
	}//registerDefaults;
		
		
	-(void)loadValuesFromDefaults
	{
		const char* name=[NXApp appName];
		const char* value;
		
		value=NXGetDefaultValue(name,"imagePath");
		if(value!=0){
			strcpy(imagePath,value);
		}else{
			if([[NXBundle mainBundle]getPath:imagePath 
					forResource:"squeak" ofType:"image"]){
			}else{
				strcpy(imagePath,"squeak.image");
			}
		}
		
		value=NXGetDefaultValue(name,"heapSize");
		if(value!=0){
			heapSize=strtobkm(value);
			if(heapSize<128*1024){
				heapSize=128*1024;
			}else if(heapSize>512*1024*1024){
				heapSize=512*1024*1024;
			}
		}
		
		value=NXGetDefaultValue(name,"stackCacheEntries");
		if(value!=0){
			stackCacheEntries=strtobkm(value);
		}

		noTitle=strtobool(NXGetDefaultValue(name,"noTitle"));
		fullScreen=strtobool(NXGetDefaultValue(name,"fullScreen"));
		sleepWhenUnmapped
				=strtobool(NXGetDefaultValue(name,"sleepWhenUnmapped"));
		
		
	}//loadValuesFromDefaults;
	
	
	
	
	-awakeFromNib
	{
		return(self);
	}//awakeFromNib;



	-(void)configurePreference
	{
		[initialHeapSizeText setIntValue:heapSize/1048576];
		[initialHeapSizeSlider setIntValue:heapSize/1048576];
		[imagePathButton setTitle:imagePath];
		[stackCacheEntriesText setIntValue:stackCacheEntries];
		[noTitleSwitch setState:noTitle];
		[fullScreenSwitch setState:fullScreen];
		[sleepWhenUnmappedSwitch setState:sleepWhenUnmapped];
		[redButton    configureMatrix:redMatrix];
		[yellowButton configureMatrix:yellowMatrix];
		[blueButton   configureMatrix:blueMatrix];
	}//configurePreference;
	
	
	-changeImagePath:sender
	{
		const char* types[2]={"image",0};
		OpenPanel*  panel=[OpenPanel new];
		char        path[MAXPATHLEN+1];
		char*       name;
		
		strncpy(path,[sender title],MAXPATHLEN);
		path[MAXPATHLEN]='\0';
		name=strrchr(path,'/');
		if(name!=0){
			*name='\0';
			name++;
		}
		[panel allowMultipleFiles:NO];
		if(NX_OKTAG==[panel runModalForDirectory:path file:name types:types]){
			const char* directory=[panel directory];
			const char* name=[panel filenames][0];
			int         pathlen=strlen(directory)+1+strlen(name);
			if(pathlen>MAXPATHLEN){
				// Note: in NS3.3, it seems that the file panel does not 
				//       allow access to paths longer than MAXPATHLEN.
				if(NX_ALERTDEFAULT==NXRunAlertPanel([NXApp appName],
					"The path to the selected file is too long. "
					"Please choose another image, or move the selected one "
					"higher in the directory hierarchy.",
					"What? Again a futile limitation in this cretaceous OS?\n"
					"And this lazy programmer who wouldn't even try to "
					"circumvent it!\n"
					"All right, I'll try again...",
					"Cancel",NULL)){
					return([self changeImagePath:sender]);
				}
			}else{
				sprintf(path,"%s/%s",directory,name);
				[imagePathButton setTitle:path];
			}
		}
		return(self);
	}//changeImagePath:;
	

	-cancelPreferences:sender
	{
		[[sender window]orderOut:sender];
		if(preferencePanelIsModal){
			[NXApp abortModal];
			preferencePanelIsModal=NO;
		}
		return(self);
	}//cancelPreferences:;
	
	
	-changePreferences:sender
	{
		const char* name=[NXApp appName];
		char        buffer[256];
		
		[self setImagePath:[imagePathButton title]];
		NXWriteDefault(name,"imagePath",[self imagePath]);
		
		heapSize=1048576*[initialHeapSizeText floatValue];
		sprintf(buffer,"%d",heapSize);
		NXWriteDefault(name,"heapSize",buffer);
		
		stackCacheEntries=[stackCacheEntriesText intValue];
		sprintf(buffer,"%d",stackCacheEntries);
		NXWriteDefault(name,"stackCacheEntries",buffer);
		
		noTitle=([noTitleSwitch state]!=0);
		sprintf(buffer,"%s",noTitle?"YES":"NO");
		NXWriteDefault(name,"noTitle",buffer);
		
		fullScreen=([fullScreenSwitch state]!=0);
		sprintf(buffer,"%s",fullScreen?"YES":"NO");
		NXWriteDefault(name,"fullScreen",buffer);
		
		sleepWhenUnmapped=([sleepWhenUnmappedSwitch state]!=0);
		sprintf(buffer,"%s",sleepWhenUnmapped?"YES":"NO");
		NXWriteDefault(name,"sleepWhenUnmapped",buffer);
		
		[redButton    takeFromMatrix:redMatrix];
		[yellowButton takeFromMatrix:yellowMatrix];
		[blueButton   takeFromMatrix:blueMatrix];

		NXWriteDefault(name,"redButton",   [redButton    stringConfiguration]);
		NXWriteDefault(name,"yellowButton",[yellowButton stringConfiguration]);
		NXWriteDefault(name,"blueButton",  [blueButton   stringConfiguration]);

		[[sender window]orderOut:sender];
		if(preferencePanelIsModal){
			[NXApp stopModal];
			preferencePanelIsModal=NO;
		}else{
			[NXApp setSqueakScreen];
		}

		return(self);
	}//changePreferences:;
	
	
	


	-(void)usage
	{
		fprintf(stderr,
				"\nUsage: %s [<options>] [<imageName>]\n\n",[NXApp appName]);
		fprintf(stderr,
		"<options> are:\n"
		"  any option interpreted by the Application class (-NX...)\n"
		"  -notitle             turn off the Squeak window title bar\n"
		"  -fullscreen          occupy the entire screen\n"
		"  -lazy                go to sleep when main window unmapped\n"
		"  -memory <size>[mk]   set initial memory size (default: 5m)\n"
		"  -ccache <size>       set context cache size (default: 16)\n"
		"  -version             print version information,then exit\n"
		"\nNotes:\n"
		"  <imageName> defaults to 'squeak.image'.\n"
		"  -ccache is merely ignored when the virtual machine has not \n"
		"          been compiled with CCACHE.\n"
		"  -fullscreen implies -notitle.\n"
		"  When you use -notitle on NeXT, you should set the size of the\n  "
		"window in the defaults, because you won't be able to move/size it:\n"
		"    dwrite Squeak 'NXWindow Frame SqueakScreen' '8 48 1024 800'\n"
		"\nAll these options can be better set from the Info/Preference\n"
		"panel; they are stored in the user's default database.\n\n");
		[NXApp squeakTerminate];
	}//usage;
	
	
	-(void)parseSqueakArguments
	{
		const char*   ev;
		const char**  argv=NXArgv;
		int           argc=NXArgc;

		ev=getenv("SQUEAK_IMAGE");
		if(ev!=0){
			strncpy(imagePath,ev,MAXPATHLEN);
			imagePath[MAXPATHLEN]='\0';
		}
	
		ev=getenv("SQUEAK_MEMORY");
		if(ev!=0){
			heapSize=strtobkm(ev);
		}
	
		ev=getenv("SQUEAK_LAZY");
		if(ev!=0){
			sleepWhenUnmapped=YES;
		}
	
		ev=getenv("SQUEAK_NOTITLE");
		if(ev!=0){
			noTitle=YES;
		}
		
		ev=getenv("SQUEAK_FULLSCREEN");
		if(ev!=0){
			fullScreen=YES;
		}
	
		ev=getenv("SQUEAK_CCACHE");
		if(ev!=0){
			stackCacheEntries=atoi(ev);
		}
	
		argc--;
		argv++;       /* skip VM name */

		squeakArgc=0;
		squeakArgv=(char**)malloc(sizeof(char*)*(argc+1));
	
		while((argc>0)&&(**argv=='-')){
			
			if(strcmp(*argv,"-help")==0){
				[self usage];
			}else if(strcmp(*argv,"-memory")==0){
				argc--;       /* skip -memory */
				argv++;
				if(argc==0){
					[self usage];
				}
				heapSize=strtobkm(*argv++);
				argc--;
			}else if(strcmp(*argv,"-ccache")==0){
				argc--;       /* skip -ccache */
				argv++;
				if(argc==0){
					[self usage];
				}
				stackCacheEntries=atoi(*argv++);
				argc--;
				if (stackCacheEntries<2){
					fprintf(stderr,"context cache size must be at least 2\n");
					[NXApp squeakTerminate];
				}
			}else if(strcmp(*argv,"-lazy")==0){
				argc--;       /* skip -lazy */
				argv++;
				sleepWhenUnmapped=YES;
			}else if (strcmp(*argv,"-notitle")==0){
				argc--;       /* skip -notitle */
				argv++;
				noTitle=YES;
			}else if(strcmp(*argv,"-fullscreen")==0){
				argc--;       /* skip -fullscreen */
				argv++;
				fullScreen=YES;
			}else if(strcmp(*argv,"-version")==0){
				printf("\n%s\n",[NXApp squeakVersionString]);
				[NXApp squeakTerminate];
			}
	
		/* assume the argument is meant for Squeak, and silently ignore it */
			squeakArgv[squeakArgc++]=*argv++;
			--argc;
		}
	
		if(argc!=0){
			strcpy(imagePath,*argv);
			squeakArgv[squeakArgc++]=*argv++;
			--argc;
		}
		squeakArgv[squeakArgc]=(const char*)0;
	}//parseSqueakArguments;
	
	
@end // SqPreference.

/*** SqPreference.m / Thu Oct  1 05:50:31 GMT+0200 1998 / PJB ***/
