/*
 *  R.app : a Cocoa front end to: "R A Computer Language for Statistical Data Analysis"
 *  
 *  R.app Copyright notes:
 *                     Copyright (C) 2004  The R Foundation
 *                     written by Stefano M. Iacus and Simon Urbanek
 *
 *                  
 *  R Copyright notes:
 *                     Copyright (C) 1995-1996   Robert Gentleman and Ross Ihaka
 *                     Copyright (C) 1998-2001   The R Development Core Team
 *                     Copyright (C) 2002-2004   The R Foundation
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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 for more details.
 *
 *  A copy of the GNU General Public License is available via WWW at
 *  http://www.gnu.org/copyleft/gpl.html.  You can also obtain it by
 *  writing to the Free Software Foundation, Inc., 59 Temple Place,
 *  Suite 330, Boston, MA  02111-1307  USA.
 */

#include <Rinternals.h>
#include <R_ext/Parse.h>
#include <Fileio.h>
#import <sys/fcntl.h>
#import <sys/select.h>
#import <sys/types.h>
#import <sys/time.h>
#import <sys/wait.h>
#import <signal.h>
#import <unistd.h>
#import "Rcallbacks.h"
#import "Rengine.h"

#import "Authorization.h"

#import "MiscPrefPane.h"
#import "QuartzPrefPane.h"
#import "ColorsPrefPane.h"
#import "EditorPrefPane.h"

// size of the console output cache buffer
#define DEFAULT_WRITE_BUFFER_SIZE 32768
// high water-mark of the buffer - it's [length - x] where x is the smallest possible size to be flushed before a new string will be split.
#define writeBufferHighWaterMark  (DEFAULT_WRITE_BUFFER_SIZE-4096)
// low water-mark of the buffer - if less than the water mark is available then the buffer will be flushed
#define writeBufferLowWaterMark   2048

/*  RController.m: main GUI code originally based on Simon Urbanek's work of embedding R in Cocoa app (RGui?)
	The Code and File Completion is due to Simon U.
	History handler is due to Simon U.
*/

typedef struct {
  ParseStatus    status;
  int            prompt_type;
  int            browselevel;
  unsigned char  buf[1025];
  unsigned char *bufp;
} R_ReplState;

extern R_ReplState state;

void run_Rmainloop(void); // from Rinit.c
extern void RGUI_ReplConsole(SEXP rho, int savestack, int browselevel); // from Rinit.c
extern int RGUI_ReplIteration(SEXP rho, int savestack, int browselevel, R_ReplState *state);

#import "RController.h"
#import "CodeCompletion.h"
#import "FileCompletion.h"
#import "RDocument.h"
#import "PackageManager.h"
#import "DataManager.h"
#import "PackageInstaller.h"
#import "WSBrowser.h"
#import "HelpManager.h"
#import "RQuartz.h"
#import "RDocumentController.h"

#import <unistd.h>
#import <sys/fcntl.h>

static RController* sharedRController;

@interface NSApplication (ScriptingSupport)
- (id)handleDCMDCommand:(NSScriptCommand*)command;
@end

@implementation NSApplication (ScriptingSupport)
- (id)handleDCMDCommand:(NSScriptCommand*)command
{
    NSDictionary *args = [command evaluatedArguments];
    NSString *cmd = [args objectForKey:@""];
    if (!cmd || [cmd isEqualToString:@""])
        return [NSNumber numberWithBool:NO];
	[[RController getRController] sendInput: cmd];
	return [NSNumber numberWithBool:YES];
}
@end

@implementation RController

- (id) init {
	self = [super init];
	
	runSystemAsRoot = NO;
	toolbar = nil;
	toolbarStopItem = nil;
	rootFD = -1;
	childPID = 0;
	RLtimer = nil;
	busyRFlag = YES;
	outputPosition = promptPosition = committedLength = 0;
	consoleInputQueue = [[NSMutableArray alloc] initWithCapacity:8];
	currentConsoleInput = nil;
	forceStdFlush = NO;
	writeBufferLen = DEFAULT_WRITE_BUFFER_SIZE;
	writeBufferPos = writeBuffer = (char*) malloc(writeBufferLen);
	readConsTransBufferSize = 1024; // initial size - will grow as needed
	readConsTransBuffer = (char*) malloc(readConsTransBufferSize);
	
	return self;
}

- (void) setRootFlag: (BOOL) flag
{
	if (!flag) removeRootAuthorization();
	runSystemAsRoot=flag;
	
	{
		NSArray * ia = [toolbar items];
		int l = [ia count], i=0;
		while (i<l) {
			NSToolbarItem *ti = [ia objectAtIndex:i];
			if ([[ti itemIdentifier] isEqual:AuthenticationToolbarItemIdentifier]) {
				[ti setImage: [NSImage imageNamed: flag?@"lock-unlocked":@"lock-locked"]];
				break;
			}
			i++;
		}
	}
}

- (BOOL) getRootFlag { return runSystemAsRoot; }
- (void) setRootFD: (int) fd { rootFD=fd; }

- (NSFont*) currentFont
{
	return [RTextView font];
}

- (void) awakeFromNib {
	NSFont *theFont;
	char *args[4]={ "R", "--no-save", "--gui=cocoa", 0 };
	
	sharedRController = self;
	
	{ // first initialize R_LIBS if necessary
		NSData *theData=[[NSUserDefaults standardUserDefaults] dataForKey:miscRAquaLibPathKey];
		BOOL flag = !isAdmin(); // the default is YES for users and NO for admins
		if (theData!=nil)
			flag=[(NSString *)[NSUnarchiver unarchiveObjectWithData:theData] isEqualToString: @"YES"];
		if (flag) {
			char *cRLIBS = getenv("R_LIBS");
			NSString *addPath = [@"~/Library/R/library" stringByExpandingTildeInPath];
			if (cRLIBS && *cRLIBS)
				addPath = [NSString stringWithFormat: @"%s:%@", cRLIBS, addPath];
			setenv("R_LIBS", [addPath cString], 1);
		}
	}		
	setenv("R_GUI_APP_VERSION", R_GUI_VERSION_STR, 1);
	
	[[[REngine alloc] initWithHandler:self arguments:args] setCocoaHandler:self];
	
	[RConsoleWindow setOpaque:NO]; // Needed so we can see through it when we have clear stuff on top
	[RTextView setDrawsBackground:NO];
	[[RTextView enclosingScrollView] setDrawsBackground:NO];
	quartzPrefPane = NULL;
	colorsPrefPane = NULL;
	miscPrefPane = NULL;
	editorPrefPane = NULL;

	[self setupPrefWindow];

	[self readDefaults];
	[RConsoleWindow setBackgroundColor:backgColor];
	[RConsoleWindow display];
	[RTextView setFont:[NSFont userFixedPitchFontOfSize:currentFontSize]];
	[fontSizeStepper setIntValue:currentFontSize];
    theFont=[RTextView font];
    theFont=[[NSFontManager sharedFontManager] convertFont:theFont toSize:[fontSizeStepper intValue]];
    [RTextView setFont:theFont];
	{
		NSMutableDictionary *md = [[RTextView typingAttributes] mutableCopy];
		[md setObject: inputColor forKey: @"NSColor"];
		[RTextView setTypingAttributes:[NSDictionary dictionaryWithDictionary:md]];
		[md release];
	}
	[RTextView setContinuousSpellCheckingEnabled:NO]; // force 'no spell checker'
	
//	[RTextView changeColor: inputColor];
	[RTextView display];
	[self setupToolbar];
	[self showWindow];
	[ RConsoleWindow setDocumentEdited:YES];
	
    //NSLog(@"RController: awake: done");
	
	WDirtimer = [NSTimer scheduledTimerWithTimeInterval:0.5
												 target:self
											   selector:@selector(showWorkingDir:)
											   userInfo:0
												repeats:YES];
	
	hist=[[History alloc] init];


    BOOL WantThread = YES;
	
    if(WantThread){ // re-route the stdout to our own file descriptor and use ConnectionCache on it
        int pfd[2];
        pipe(pfd);
        dup2(pfd[1], STDOUT_FILENO);
        close(pfd[1]);
        
        stdoutFD=pfd[0];

        pipe(pfd);
#ifndef PLAIN_STDERR
        dup2(pfd[1], STDERR_FILENO);
        close(pfd[1]);
#endif
        
        stderrFD=pfd[0];

		[self addConnectionLog];
    }

	[historyView setDoubleAction: @selector(historyDoubleClick:)];

	currentSize = [[RTextView textContainer] containerSize].width;
	//currentFontSize = [[RTextView font] pointSize];
	currentConsoleWidth = -1;
	chdir(R_ExpandFileName("~/"));
}

-(void) applicationDidFinishLaunching: (NSNotification *)aNotification
{
	if (![[REngine mainEngine] activate]) {
		NSRunAlertPanel(@"Cannot start R",[NSString stringWithFormat:@"Unable to start R: %@", [[REngine mainEngine] lastError]],@"OK",nil,nil);
		exit(-1);
	}
		
	preferences = [[NSMutableDictionary alloc] init];
	[self setOptionWidth:YES];
	[RTextView setEditable:YES];
	[self flushROutput];
	
	[[NSNotificationCenter defaultCenter] 
		addObserver:self
		   selector:@selector(RConsoleDidResize:)
			   name:NSWindowDidResizeNotification
			 object: RConsoleWindow];
	
	timer = [NSTimer scheduledTimerWithTimeInterval:0.05
											 target:self
										   selector:@selector(otherEventLoops:)
										   userInfo:0
											repeats:YES];
	Flushtimer = [NSTimer scheduledTimerWithTimeInterval:0.5
												  target:self
												selector:@selector(flushTimerHook:)
												userInfo:0
												 repeats:YES];

	if (!RLtimer)
		RLtimer = [NSTimer scheduledTimerWithTimeInterval:0.001
												   target:self
												 selector:@selector(runRELP:)
												 userInfo:0
												  repeats:NO];

}

-(void) addConnectionLog
{
	NSPort *port1;
	NSPort *port2;
	NSArray *portArray;
	NSConnection *connectionToTransferServer;
				
	port1 = [NSPort port];
	port2 = [NSPort port];
	connectionToTransferServer = [[NSConnection alloc] initWithReceivePort:port1 sendPort:port2];
	[connectionToTransferServer setRootObject:self];
	
	portArray = [NSArray arrayWithObjects:port2, port1, nil];
	[NSThread detachNewThreadSelector:@selector(readThread:)
							 toTarget:self
						   withObject:portArray];
}

- (void) readThread: (NSArray *)portArray
{
    NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
    NSConnection *connectionToController;
	RController *rc;
	unsigned int bufSize=2048;
    char *buf=(char*) malloc(bufSize);
    int n=0, pib=0, flushMark=bufSize-(bufSize>>2);
	int bufFD=0;
    fd_set readfds;
	struct timeval timv;

	timv.tv_sec=0; timv.tv_usec=300000; /* timeout */

	connectionToController = [NSConnection connectionWithReceivePort:[portArray objectAtIndex:0]
															sendPort:[portArray objectAtIndex:1]];

	rc = ((RController *)[connectionToController rootProxy]);
	
    fcntl(stdoutFD, F_SETFL, O_NONBLOCK);
    fcntl(stderrFD, F_SETFL, O_NONBLOCK);
    while (1) {
		int selr=0, maxfd=stdoutFD;
        FD_ZERO(&readfds);
        FD_SET(stdoutFD,&readfds);
        FD_SET(stderrFD,&readfds); if(stderrFD>maxfd) maxfd=stderrFD;
		if (rootFD!=-1) { FD_SET(rootFD,&readfds); if(rootFD>maxfd) maxfd=rootFD; }
        selr=select(maxfd+1, &readfds, 0, 0, &timv);
        if (FD_ISSET(stdoutFD, &readfds)) {
			if (bufFD!=0 && pib>0) {
				[rc writeLogsWithBytes:buf length:pib type:bufFD];
				pib=0;
			}
			bufFD=0;
            while (pib<bufSize && (n=read(stdoutFD,buf+pib,bufSize-pib))>0)
				pib+=n;
			if (pib>flushMark) { // if we reach the flush mark, dump it
				NS_DURING
					[rc writeLogsWithBytes:buf length:pib type:bufFD];
				NS_HANDLER
					;
				NS_ENDHANDLER
				pib=0;
            }
        } 
		if (FD_ISSET(stderrFD, &readfds)) {
			if (bufFD!=1 && pib>0) {
				[rc writeLogsWithBytes:buf length:pib type:bufFD];
				pib=0;
			}
			bufFD=1;
			while (pib<bufSize && (n=read(stderrFD,buf+pib,bufSize-pib))>0)
				pib+=n;
			if (pib>flushMark) { // if we reach the flush mark, dump it
				NS_DURING
					[rc writeLogsWithBytes:buf length:pib type:bufFD];
				NS_HANDLER
					;
				NS_ENDHANDLER
				pib=0;
			}
		}
		if (rootFD!=-1 && FD_ISSET(rootFD, &readfds)) {
			if (bufFD!=2 && pib>0) {
				[rc writeLogsWithBytes:buf length:pib type:bufFD];
				pib=0;
			}
			bufFD=2;
			while (pib<bufSize && (n=read(rootFD,buf+pib,bufSize-pib))>0)
				pib+=n;
			if (n==0 || pib>flushMark) { // if we reach the flush mark, dump it
				NS_DURING
					[rc writeLogsWithBytes:buf length:pib type:bufFD];
				NS_HANDLER
					;
				NS_ENDHANDLER
				pib=0;
			}
			if (n==0) rootFD=-1;
		}
		if ((forceStdFlush || selr==0) && pib>0) { // dump also if we got a timeout
			NS_DURING
				[rc writeLogsWithBytes:buf length:pib type:bufFD];
			NS_HANDLER
				;
			NS_ENDHANDLER
			pib=0;
		}
    }
    free(buf);
	
    [pool release];
}

- (void) flushStdConsole
{
	fflush(stderr);
	fflush(stdout);
	forceStdFlush=YES;
}

- (void) addChildProcess: (pid_t) pid
{
	childPID=pid;
	if (pid>0 && toolbarStopItem) [toolbarStopItem setEnabled:YES];
}

- (void) rmChildProcess: (pid_t) pid
{
	childPID=0;
	if (!busyRFlag && toolbarStopItem) [toolbarStopItem setEnabled:NO];
	[self flushStdConsole];
}

// When you have the toolbar in text-only mode, this action is called
// by the toolbar item's menu to make the font bigger.  We just change the stepper control and
// call our -changeFontSize: action.
-(IBAction) fontSizeBigger:(id)sender
{
	if ([RConsoleWindow isKeyWindow]) {
		[fontSizeStepper setIntValue:[fontSizeStepper intValue]+1];
		[self changeFontSize:NULL];
	} else
		[[NSFontManager sharedFontManager] modifyFont:sender];
}

// When you have the toolbar in text-only mode, this action is called
// by the toolbar item's menu to make the font smaller.  We just change the stepper control and
// call our -changeFontSize: action.
-(IBAction) fontSizeSmaller:(id)sender
{
	if ([RConsoleWindow isKeyWindow]) {
		[fontSizeStepper setIntValue:[fontSizeStepper intValue]-1];
		[self changeFontSize:NULL];
	} else
		[[NSFontManager sharedFontManager] modifyFont:sender];

}

// This action is called to change the font size.  It's called by the NSPopUpButton in the toolbar item's 
// custom view, and also by the above routines called from the toolbar item's menu (in text-only mode).
-(IBAction) changeFontSize:(id)sender
{
    NSFont *theFont;
    
    [fontSizeField takeIntValueFrom:fontSizeStepper];
    theFont=[RTextView font];
    theFont=[[NSFontManager sharedFontManager] convertFont:theFont toSize:[fontSizeStepper intValue]];
    [RTextView setFont:theFont];
	[self setOptionWidth:NO];
	[[NSUserDefaults standardUserDefaults] setFloat: [fontSizeStepper intValue] forKey:FontSizeKey];
}


extern BOOL isTimeToFinish;

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app {
	if (![self windowShouldClose:self])
		return NSTerminateCancel;
	
	if(timer){
		[timer invalidate];
		timer = nil;
	}

	if(RLtimer){
		[RLtimer invalidate];
		RLtimer = nil;
	}
		
	if(Flushtimer){
		[Flushtimer invalidate];
		Flushtimer = nil;
	}
	
	if(WDirtimer){
		[WDirtimer invalidate];
		WDirtimer = nil;
	}
		
	return NSTerminateNow;
}


- (void) dealloc {
	[[NSNotificationCenter defaultCenter] removeObserver:self];
    [inputColor release];
    [outputColor release];
    [promptColor release];
    [backgColor release];
    [stderrColor release];
    [stdoutColor release];
	[preferences release];
	[super dealloc];
}

- (void)showWindow {
    [RConsoleWindow orderFront:nil];
}

- (void) flushTimerHook: (NSTimer*) source
{
	[self flushROutput];
}

- (void) flushROutput {
	if (writeBuffer!=writeBufferPos) {
		[self writeConsoleDirectly:[NSString stringWithUTF8String:writeBuffer] withColor:outputColor];
		writeBufferPos=writeBuffer;
	}
}

- (void) handleFlushConsole {
	[self flushROutput];
	[self flushStdConsole];
}

/* this writes R output to the Console window, but indirectly by using a buffer */
- (void) handleWriteConsole: (NSString*) txt {
	const char *s = [txt UTF8String];
	int sl = strlen(s);
	int fits = writeBufferLen-(writeBufferPos-writeBuffer)-1;
	
	// let's flush the buffer if the new string is large and it would, but the buffer should be occupied
	if (fits<sl && fits>writeBufferHighWaterMark) {
		// for efficiency we're not using handleFlushConsole, because that would trigger stdxx flush, too
		[self writeConsoleDirectly:[NSString stringWithUTF8String:writeBuffer] withColor:outputColor];
		writeBufferPos=writeBuffer;
		fits = writeBufferLen-1;
	}
	
	while (fits<sl) {	// ok, we're in a situation where we must split the string
		memcpy(writeBufferPos, s, fits);
		writeBufferPos[writeBufferLen-1]=0;
		[self writeConsoleDirectly:[NSString stringWithUTF8String:writeBuffer] withColor:outputColor];
		sl-=fits; s+=fits;
		writeBufferPos=writeBuffer;
		fits=writeBufferLen-1;
	}

	strcpy(writeBufferPos, s);
	writeBufferPos+=sl;
	
	// flush the buffer if the low watermark is reached
	if (fits-sl<writeBufferLowWaterMark) {
		[self writeConsoleDirectly:[NSString stringWithUTF8String:writeBuffer] withColor:outputColor];
		writeBufferPos=writeBuffer;
	}
}

/* this writes R output to the Console window directly, i.e. without using a buffer. Use handleWriteConsole: for the regular way. */
- (void) writeConsoleDirectly: (NSString*) txt withColor: (NSColor*) color{
	NSRange origSel = [RTextView selectedRange];
	NSRange insPt = NSMakeRange(outputPosition, 0);
	unsigned tl = [txt length];
	if (tl>0) {
		unsigned oldCL=committedLength;
		/* NSLog(@"original: %d:%d, insertion: %d, length: %d, prompt: %d, commit: %d", origSel.location,
			  origSel.length, outputPosition, tl, promptPosition, committedLength); */
		committedLength=0;
		[RTextView setSelectedRange:insPt];
		[RTextView insertText:txt];
		insPt.length=tl;
		[RTextView setTextColor:color range:insPt];
		if (outputPosition<=promptPosition) promptPosition+=tl;
		committedLength=oldCL;
		if (outputPosition<=committedLength) committedLength+=tl;
		if (outputPosition<=origSel.location) origSel.location+=tl;
		outputPosition+=tl;
		[RTextView setSelectedRange:origSel];
	}
}

/* Just writes the prompt in a different color */
- (void)handleWritePrompt: (NSString*) prompt {
    [self handleFlushConsole];
	unsigned textLength = [[RTextView textStorage] length];
    int promptLength=[prompt length];
	NSRange lr = [[[RTextView textStorage] string] lineRangeForRange:NSMakeRange(textLength,0)];
    promptPosition=textLength;
	if (lr.location!=textLength) { // the prompt must be on the beginning of the line
        [RTextView setSelectedRange:NSMakeRange(textLength, 0)];
		[RTextView insertText: @"\n"];
		textLength = [[RTextView textStorage] length];
		promptLength++;
	}
	
    if (promptLength>0) {
        [RTextView setSelectedRange:NSMakeRange(textLength, 0)];
        [RTextView insertText:prompt];
        [RTextView setTextColor:promptColor range:NSMakeRange(promptPosition, promptLength)];
		if (promptLength>1) // this is a trick to make sure that the insertion color doesn't change at the prompt
			[RTextView setTextColor:inputColor range:NSMakeRange(promptPosition+promptLength-1, 1)];
        committedLength=promptPosition+promptLength;
    }
	committedLength=promptPosition+promptLength;
}

- (void)  handleProcessingInput: (char*) cmd
{
	NSString *s = [NSString stringWithCString:cmd];
	unsigned textLength = [[RTextView textStorage] length];
	
	[RTextView setSelectedRange:NSMakeRange(committedLength, textLength-committedLength)];
	[RTextView insertText:s];
	textLength = [[RTextView textStorage] length];
	[RTextView setTextColor:inputColor range:NSMakeRange(committedLength, textLength-committedLength)];
	outputPosition=committedLength=textLength;
	
	if((*cmd == '?') || (!strncmp("help(",cmd,5))){ 
		[self openHelpFor: cmd];
		cmd[0] = '\n'; cmd[1] = 0;
	}
}

- (char*) handleReadConsole: (int) addtohist
{
	if (currentConsoleInput) {
		[currentConsoleInput release];
		currentConsoleInput=nil;
	}
	
	while ([consoleInputQueue count]==0)
		[self doProcessEvents: YES];
	
	currentConsoleInput = [consoleInputQueue objectAtIndex:0];
	[consoleInputQueue removeObjectAtIndex:0];

	if (addtohist) {
		// don't register training newline ... FIXME: we should acutally include it and fix history handling at other places ..
		if ([currentConsoleInput length]>0 && [currentConsoleInput characterAtIndex:[currentConsoleInput length]-1]=='\n')
			[hist commit:[currentConsoleInput substringToIndex:[currentConsoleInput length]-1]];			
		else
			[hist commit:currentConsoleInput];
		[historyView reloadData];
	}
	
	{
		const char *c = [currentConsoleInput UTF8String];
		if (!c) return 0;
		if (strlen(c)>readConsTransBufferSize-1) { // grow as necessary
			free(readConsTransBuffer);
			readConsTransBufferSize = (strlen(c)+2048)&0xfffffc00;
			readConsTransBuffer = (char*) malloc(readConsTransBufferSize);
		} // we don't shrink the buffer if gets too large - we may want to think about that ...
		
		strcpy(readConsTransBuffer, c);
	}
	return readConsTransBuffer;
}

- (int) handleEdit: (char*) file
{
	if(!R_FileExists(file))
		return(0);
	
	RDocument *document = [[RDocumentController sharedDocumentController] openRDocumentWithContentsOfFile: [NSString stringWithCString:R_ExpandFileName(file)] display:true];
	[document setREditFlag: YES];
	
	NSEnumerator *e = [[document windowControllers] objectEnumerator];
	NSWindowController *wc = nil;
	while (wc = [e nextObject]) {
		NSWindow *window = [wc window];
		NSModalSession session = [NSApp beginModalSessionForWindow:window];
		while([document hasREditFlag])
			[NSApp runModalSession:session];
		
		[NSApp endModalSession:session];
	}
	
	return(0);
}

/* FIXME: the filename is not set for newvly created files */
- (int) handleEditFiles: (int) nfile withNames: (char**) file titles: (char**) wtitle pager: (char*) pager
{
	int    	i;
    
    if (nfile <=0) return 1;
	
    for (i = 0; i < nfile; i++){
		if(R_FileExists(file[i]))
			[[RDocumentController sharedDocumentController] openRDocumentWithContentsOfFile: [NSString stringWithCString:R_ExpandFileName(file[i])] display:true];
		else
			[[NSDocumentController sharedDocumentController] newDocument: [RController getRController]];
		
		NSDocument *document = [[NSDocumentController sharedDocumentController] currentDocument];
		if(wtitle[i]!=nil)
			[RDocument changeDocumentTitle: document Title: [NSString stringWithCString:wtitle[i]]];
    }
	return 1;
}

- (int) handleShowFiles: (int) nfile withNames: (char**) file headers: (char**) headers windowTitle: (char*) wtitle pager: (char*) pages andDelete: (BOOL) del
{
	int    	i;
    
    if (nfile <=0) return 1;
	
    for (i = 0; i < nfile; i++){
		RDocument *document = [[RDocumentController sharedDocumentController] openRDocumentWithContentsOfFile: [NSString stringWithCString:R_ExpandFileName(file[i])] display:true];
		if(wtitle[i]!=nil)
			[RDocument changeDocumentTitle: document Title: [NSString stringWithCString:wtitle]];
		[document setEditable: NO];
		[document setHighlighting: NO];
    }
	return 1;
}

//======== Cocoa Handler ======

- (int) handlePackages: (int) count withNames: (char**) name descriptions: (char**) desc URLs: (char**) url status: (BOOL*) stat
{
	[[PackageManager sharedController] updatePackages:count withNames:name descriptions:desc URLs:url status:stat];
	return 0;
}

- (BOOL*) handleDatasets: (int) count withNames: (char**) name descriptions: (char**) desc packages: (char**) pkg URLs: (char**) url
{
	[[DataManager sharedController] updateDatasets:count withNames:name descriptions:desc packages:pkg URLs:url];
	return 0; // we don't load the DS this way, we use REngine instead
}

- (int) handleInstalledPackages: (int) count withNames: (char**) name installedVersions: (char**) iver repositoryVersions: (char**) rver update: (BOOL*) stat label: (char*) label
{
	[[PackageInstaller sharedController] updateInstalledPackages:count withNames:name installedVersions:iver repositoryVersions:rver update:stat label:label];
	return 0;
}

- (int) handleSystemCommand: (char*) cmd
{	
	int cstat=-1;
	pid_t pid;
	
	if ([self getRootFlag]) {
		FILE *f;
		char *argv[3] = { "-c", cmd, 0 };
		int fd;
 		NSBundle *b = [NSBundle mainBundle];
		char *sushPath=0;
		if (b) {
			NSString *sush=[[b resourcePath] stringByAppendingString:@"/sush"];
			sushPath = (char*) malloc([sush cStringLength]+1);
			[sush getCString:sushPath maxLength:[sush cStringLength]];
		}
		
		fd = runRootScript(sushPath?sushPath:"/bin/sh",argv,&f,1);
		if (!fd && f)
			[self setRootFD:fileno(f)];
		if (sushPath) free(sushPath);
		return fd;
	}
	
	pid=fork();
	if (pid==0) {
		// int sr;
		// reset signal handlers
		signal(SIGINT, SIG_DFL);
		signal(SIGTERM, SIG_DFL);
		signal(SIGQUIT, SIG_DFL);
		signal(SIGALRM, SIG_DFL);
		signal(SIGCHLD, SIG_DFL);
		execl("/bin/sh","/bin/sh","-c",cmd,0);
		exit(-1);
		//sr=system(cmd);
		//exit(WEXITSTATUS(sr));
	}
	if (pid==-1) return -1;
	
	[[RController getRController] addChildProcess: pid];
	
	while (1) {
		pid_t w = waitpid(pid, &cstat, WNOHANG);
		if (w!=0) break;
		Re_ProcessEvents();
	}
	[[RController getRController] rmChildProcess: pid];
	return cstat;
}	

//==========

- (BOOL)windowShouldClose:(id)sender
{
	
	NSBeginAlertSheet(@"Closing R session",@"Save",@"Don't Save",@"Cancel",[RTextView window],self,@selector(shouldCloseDidEnd:returnCode:contextInfo:),NULL,NULL,@"Save workspace image?");
    return NO;
}

/* this gets called by the "wanna save?" sheet on window close */
- (void) shouldCloseDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
    if (returnCode==NSAlertDefaultReturn)
		[[REngine mainEngine] executeString:@"quit(\"yes\")"];
    if (returnCode==NSAlertAlternateReturn)
		[[REngine mainEngine] executeString:@"quit(\"no\")"];
}

/*  This is used to send commands through the GUI, i.e. from menus 
	The input replaces what the user is currently typing.
*/
- (void) sendInput: (NSString*) text {
	[self consoleInput:text interactive:YES];
	/*
	unsigned textLength = [[RTextView textStorage] length];
	[RTextView setSelectedRange:NSMakeRange(textLength, 0)];
	NSEvent* event = [NSEvent keyEventWithType:NSKeyDown
									  location:NSMakePoint(0,0)
								 modifierFlags:0
									 timestamp:0
								  windowNumber:[RConsoleWindow windowNumber]
									   context:nil
									characters:@"\n"
				   charactersIgnoringModifiers:nil
									 isARepeat:NO
									   keyCode:nil
		];
	[NSApp postEvent:event atStart:YES];
	 */
}

/* These two routines are needed to update the History TableView */
- (int)numberOfRowsInTableView: (NSTableView *)tableView
{
	return [[hist entries] count];
}

- (id)tableView: (NSTableView *)tableView
		objectValueForTableColumn: (NSTableColumn *)tableColumn
		row: (int)row
{
			return (NSString*) [[hist entries]  objectAtIndex: row];
}

/*  Clears the history  and updates the TableView */

- (IBAction)doClearHistory:(id)sender
{
	[hist resetAll];
	[historyView reloadData];
}

/*  Loads the content of the history of a file. The default extension .history
	This file is not compatible with unix history files as it could be multiline.
	This rountien cannot load standard unix history files.
	FIXME: We can probably import standard unix history files
*/

- (IBAction)doLoadHistory:(id)sender
{
	NSOpenPanel *op;
	int answer;
	
	op = [NSOpenPanel openPanel];
	[op setTitle:@"Choose history File"];
	
	answer = [op runModalForTypes: [NSArray arrayWithObject:@"history"]];
	
	if(answer == NSOKButton) {
		if([op filename] != nil){
			[hist resetAll];
			[hist setHist: [NSUnarchiver unarchiveObjectWithFile: [op filename]]];
			[historyView reloadData];
		}
	}
}

/*  Saves the content of the history of a file. The default extension .history
	This file is not compatible with unix history files as it could be multiline.
	FIXME: we can probably allow for exporting as single line
*/
	
- (IBAction)doSaveHistory:(id)sender
{
	int answer;
	NSSavePanel *sp;
	sp = [NSSavePanel savePanel];
	[sp setRequiredFileType:@"history"];
	[sp setTitle:@"Save history File"];
	answer = [sp runModal];
	if(answer == NSOKButton) {
		[NSArchiver archiveRootObject: [hist entries]
					toFile: [sp filename]];
	}
	
}

/*  On double-click on items of the History TableView, the item is pasted into the console
	at current cursor position
*/
- (IBAction)historyDoubleClick:(id)sender {
	NSString *cmd;
	int index = [sender selectedRow];
	if(index == -1) return;
	
	cmd = [[hist entries] objectAtIndex:index];
	[self consoleInput:cmd interactive:NO];
	[RConsoleWindow makeFirstResponder:RTextView];
}


/*  This routine is intended to "cat" some text to the R Console without
	issuing the newline.
- (void) consolePaste: (NSString*) text {
	unsigned textLength = [[RTextView textStorage] length];
	[RTextView setSelectedRange:NSMakeRange(textLength, 0)];
	[RTextView insertText:text];
}
*/
/* This function is used by two threads to write  stderr and/or stdout to the console
   outputType: 0 = stdout, 1 = stderr, 2 = stdout/err as root
*/
- (void) writeLogsWithBytes: (char*) buf length: (int) len type: (int) outputType
{
	NSColor *color=(outputType==0)?stdoutColor:((outputType==1)?stderrColor:[NSColor purpleColor]);
	
	[self flushROutput];
	[self writeConsoleDirectly:[NSString stringWithCString:buf length:len] withColor:color];
}    

+ (id) getRController{
	return sharedRController;
}

/* console input - the string passed here is handled as if it was typed on the console */
- (void) consoleInput: (NSString*) cmd interactive: (BOOL) inter
{
	if (!inter) {
		int textLength = [[RTextView textStorage] length];
		if (textLength>committedLength)
			[RTextView replaceCharactersInRange:NSMakeRange(committedLength,textLength-committedLength) withString:@""];
		[RTextView setSelectedRange:NSMakeRange(committedLength,0)];
		[RTextView insertText: cmd];
		textLength = [[RTextView textStorage] length];
		[RTextView setTextColor:inputColor range:NSMakeRange(committedLength,textLength-committedLength)];
	}

	if (inter) {
		if ([cmd characterAtIndex:[cmd length]-1]!='\n') cmd=[cmd stringByAppendingString: @"\n"];
		[consoleInputQueue addObject:[[NSString alloc] initWithString:cmd]];
	}
}

- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
    BOOL retval = NO;

	//NSLog(@"RTextView commandSelector: %@\n", NSStringFromSelector(commandSelector));
	
    if (@selector(insertNewline:) == commandSelector) {
        unsigned textLength = [[textView textStorage] length];
		[textView setSelectedRange:NSMakeRange(textLength,0)];
        if (textLength >= committedLength) {
			[textView insertText:@"\n"];
			textLength = [[textView textStorage] length];
			[self consoleInput: [[textView attributedSubstringFromRange:NSMakeRange(committedLength, textLength - committedLength)] string] interactive: YES];
			return(YES);
        }
        retval = YES;
    }
	
	// ---- history browsing ----
	if (@selector(moveUp:) == commandSelector) {
        unsigned textLength = [[textView textStorage] length];        
        NSRange sr=[textView selectedRange];
        if (sr.location==committedLength || sr.location==textLength) {
            NSRange rr=NSMakeRange(committedLength, textLength-committedLength);
            NSString *text = [[textView attributedSubstringFromRange:rr] string];
            if ([hist isDirty]) {
                [hist updateDirty: text];
            }
            NSString *news = [hist prev];
            if (news!=nil) {
                [news retain];
                sr.length=0; sr.location=committedLength;
                [textView setSelectedRange:sr];
                [textView replaceCharactersInRange:rr withString:news];
                [textView insertText:@""];
                [news release];
            }
            retval = YES;
        }
    }
    if (@selector(moveDown:) == commandSelector) {
        unsigned textLength = [[textView textStorage] length];        
        NSRange sr=[textView selectedRange];
        if ((sr.location==committedLength || sr.location==textLength) && ![hist isDirty]) {
            NSRange rr=NSMakeRange(committedLength, textLength-committedLength);
            NSString *news = [hist next];
            if (news==nil) news=@""; else [news retain];
            sr.length=0; sr.location=committedLength;
            [textView setSelectedRange:sr];
            [textView replaceCharactersInRange:rr withString:news];
            [textView insertText:@""];
            [news release];
            retval = YES;
        }
    }
    
	// ---- make sure the user won't accidentally get out of the input line ----
	
	if (@selector(moveToBeginningOfParagraph:) == commandSelector || @selector(moveToBeginningOfLine:) == commandSelector) {
        [textView setSelectedRange: NSMakeRange(committedLength,0)];
        retval = YES;
    }

	if (@selector(moveToBeginningOfParagraphAndModifySelection:) == commandSelector || @selector(moveToBeginningOfLineAndModifySelection:) == commandSelector) {
		// FIXME: this kills the selection - we should retain it ...
        [textView setSelectedRange: NSMakeRange(committedLength,0)];
        retval = YES;
    }
	
	if (@selector(moveWordLeft:) == commandSelector || @selector(moveLeft:) == commandSelector ||
		@selector(moveWordLeftAndModifySelection:) == commandSelector || @selector(moveLeftAndModifySelection:) == commandSelector) {
        NSRange sr=[textView selectedRange];
		if (sr.location==committedLength) return YES;
	}
	
	// ---- code/file completion ----
	
	if (@selector(insertTab:) == commandSelector) {
        NSRange sr=[textView selectedRange];
        int bow=sr.location;
        if (bow>committedLength) {
            while (bow>committedLength) bow--;
            {
                NSString *rep=nil;
                NSRange er = NSMakeRange(bow,sr.location-bow);
                NSString *text = [[textView attributedSubstringFromRange:er] string];
                
                // first we need to find out whether we're in a text part or code part
                unichar c;
                int tl = [text length], tp=0, quotes=0, dquotes=0, lastQuote=-1;
                while (tp<tl) {
                    c=[text characterAtIndex:tp];
                    if (c=='\\') tp++; // skip the next char after a backslash (we don't have to worry about \023 and friends)
                    else {
                        if (dquotes==0 && c=='\'') {
                            quotes^=1;
                            if (quotes) lastQuote=tp;
                        }
                        if (quotes==0 && c=='"') {
                            dquotes^=1;
                            if (dquotes) lastQuote=tp;
                        }
                    }
                    tp++;
                }
                
                if (quotes+dquotes>0) { // if we're inside any quotes, use file completion
                    rep=[FileCompletion complete:[text substringFromIndex:lastQuote+1]];
                    er.location+=lastQuote+1;
                    er.length-=lastQuote+1;
                } else { // otherwise use code completion
                    int s = [text length]-1;
                    c = [text characterAtIndex:s];
                    while (((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z'))||((c>='0')&&(c<='9'))||c=='.') {
                        s--;
                        if (s==-1) break;
                        c = [text characterAtIndex:s];
                    }
                    s++;
                    er.location+=s; er.length-=s;
                    rep=[CodeCompletion complete:[text substringFromIndex:s]];
                }
                
                // ok, by now we should get "rep" if completion is possible and "er" modified to match the proper part
                if (rep!=nil) {
                    [textView replaceCharactersInRange:er withString:rep];
                }
            }
        }
        retval = YES; // tab is never passed further        
	}
	
	// ---- cancel ---
	
	if (@selector(cancel:) == commandSelector) {
		[self breakR:self];
		retval = YES;
	}
    
	return retval;
}
	
/* Allow changes only for uncommitted text */
- (BOOL)textView:(NSTextView *)textView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString {
	if (affectedCharRange.location < committedLength) { /* if the insertion is outside editable scope, append at the end */
		[textView setSelectedRange:NSMakeRange([[textView textStorage] length],0)];
		[textView insertText:replacementString];
		return NO;
	}
	return YES;
}
	
- (void) handleBusy: (BOOL) isBusy {
    if (isBusy)
        [progressWheel startAnimation:self];
    else
        [progressWheel stopAnimation:self];

	busyRFlag = isBusy;
	if (toolbarStopItem) {
		if (isBusy || childPID>0)
			[toolbarStopItem setEnabled:YES];
		else
			[toolbarStopItem setEnabled:NO];
	}
}

- (void)  handleShowMessage: (char*) msg
{
	NSRunAlertPanel(@"R Message",[NSString stringWithCString:msg],@"OK",nil,nil);
}

/*  This is called from R with as callback to R_ProcessEvents during
	computation in "src/main/errors.c". The callback is defined at the
	moment inside "src/unix/aqua.c". In "errors.c" this is defined to
	allow for user interrupts but we also use it to make our GUI
	reponsive to user interaction.
*/	



/*  This called by a timer periodically to allow tcltk, x11 etc to process their events 
	like window resizing, locator, widgets interactions, etc.
*/

  
- (IBAction)runRELP:(id)sender {
	run_Rmainloop();
}

- (IBAction)flushconsole:(id)sender {
	[self handleFlushConsole];
}

- (IBAction)otherEventLoops:(id)sender {
	R_runHandlers(R_InputHandlers, R_checkActivity(0, 1));
}


- (IBAction)newQuartzDevice:(id)sender {
	[[REngine mainEngine] executeString:@"quartz()"];
}

- (IBAction)breakR:(id)sender{
	if (childPID)
		kill(childPID, SIGINT);
	else
		onintr();
}

- (IBAction)quitR:(id)sender{
	[self windowShouldClose:RConsoleWindow];
}

- (IBAction)makeConsoleKey:(id)sender
{
	[RConsoleWindow makeKeyAndOrderFront:sender];
}

- (IBAction)toggleHistory:(id)sender{
    NSDrawerState state = [HistoryDrawer state];
    if (NSDrawerOpeningState == state || NSDrawerOpenState == state) {
        [HistoryDrawer close];
    } else {
        [HistoryDrawer open];
    }
}

- (IBAction)toggleAuthentication:(id)sender{
	BOOL isOn = [self getRootFlag];
	
	if (isOn) {
		removeRootAuthorization();
		[self setRootFlag:NO];
	} else {
		if (requestRootAuthorization(1)) return;
		[self setRootFlag:YES];
	}
}

- (IBAction)newDocument:(id)sender{
	[[NSDocumentController sharedDocumentController] newDocument: sender];
}

- (IBAction)openDocument:(id)sender{
	[[NSDocumentController sharedDocumentController] openDocument: sender];
}

- (IBAction)saveDocumentAs:(id)sender{
	NSDocument *cd = [[NSDocumentController sharedDocumentController] currentDocument];
	
	if (cd)
		[cd saveDocumentAs:sender];
	else {
		int answer;
		NSSavePanel *sp;
		sp = [NSSavePanel savePanel];
		[sp setRequiredFileType:@"txt"];
		[sp setTitle:@"Save R Console To File"];
		answer = [sp runModalForDirectory:nil file:@"R Console.txt"];
		
		if(answer == NSOKButton) {
			[[RTextView string] writeToFile:[sp filename] atomically:YES];
		}
	}
}

- (IBAction)saveDocument:(id)sender{
	NSDocument *cd = [[NSDocumentController sharedDocumentController] currentDocument];
	
	if (cd)
		[cd saveDocument:sender];
	else // for the console this is the same as Save As ..
		[self saveDocumentAs:sender];
}

- (int) handleChooseFile:(char *)buf len:(int)len isNew:(int)isNew
{
	int answer;
	NSSavePanel *sp;
	NSOpenPanel *op;

	buf[0] = '\0';
	if(isNew==1){
		sp = [NSSavePanel savePanel];
		[sp setTitle:@"Choose New File Name"];
		answer = [sp runModalForDirectory:nil file:nil];
	
		if(answer == NSOKButton) {
			if([sp filename] != nil){
				CFStringGetCString((CFStringRef)[sp filename], buf, len-1,  kCFStringEncodingMacRoman); 
				buf[len] = '\0';
			}
		}
	} else {
		op = [NSOpenPanel openPanel];
		[op setTitle:@"Choose File"];
		answer = [op runModalForDirectory:nil file:nil];
	
		if(answer == NSOKButton) {
			if([op filename] != nil){
				CFStringGetCString((CFStringRef)[op filename], buf, len-1,  kCFStringEncodingMacRoman); 
				buf[len] = '\0';
			}
		}
	}	
	return strlen(buf);

}	

- (void) loadFile:(char *)fname
{
	int res = [[RController getRController] isImageData:fname];
	
	switch(res){
		case -1:
		NSLog(@"cannot open file");
		break;
		
		case 0:
			[self sendInput: [NSString stringWithFormat:@"load(\"%s\")",fname]];
		break;
		
		case 1:
			[self sendInput: [NSString stringWithFormat:@"source(\"%s\")",fname]];
		break;	
		default:
		break; 
	}
}

// FIXME: is this really sufficient? what about compressed files?
/*  isImageData:	returns -1 on error, 0 if the file is RDX2 or RDX1, 
					1 otherwise.
*/	
- (int)isImageData:(char *)fname
{
	FILE * fp;
	int flen;
	
	char buf[5];
	if( (fp = R_fopen(R_ExpandFileName(fname), "r")) ){
		fseek(fp, 0L, SEEK_END);
		flen = ftell(fp);
		rewind(fp);
		if(flen<4) 
			return(1);
		fread(buf, 1, 4, fp);
		buf[4] = '\0';
		if( (strcmp(buf,"RDX2")==0) || ((strcmp(buf,"RDX1")==0))) return(0);
		else return(1);
	} else
		return(-1);
}

- (void) doProcessEvents: (BOOL) blocking {
	NSEvent *event;

	if (blocking){
		event = [NSApp nextEventMatchingMask:NSAnyEventMask
								   untilDate:[NSDate distantFuture]
									  inMode:NSDefaultRunLoopMode
									 dequeue:YES];
		[NSApp sendEvent:event];	
	} else {
		while( (event = [NSApp nextEventMatchingMask:NSAnyEventMask
										   untilDate:[NSDate dateWithTimeIntervalSinceNow:0.0001]
											  inMode:NSDefaultRunLoopMode 
											 dequeue:YES]))
			[NSApp sendEvent:event];
	}
	return;
}

- (void) handleProcessEvents{
	[self doProcessEvents: NO];
}
	

/* 
This method calls the showHelpFor method of the Help Manager which opens
the internal html browser/help system of R.app
This method is called from ReadConsole.
 
The input C string 'topic' is parsed and the behaviour is the following:

topic = ?something  => showHelpFor:@"something"
topic = help(something) => showHelpFor:@"something"
topic = help(something); print(anotherthing);   =>  showHelpFor:@"something"

which means that all the rest of the input is discarded.
No error message or warning are raised.
*/

- (void) openHelpFor: (char *) topic 
{
	char tmp[300];
	int i;

	if(topic[0] == '?' && (strlen(topic)>1))
		[[HelpManager getHMController] showHelpFor: [NSString stringWithCString:topic+1]];
	if(strncmp("help(",topic,5)==0){
		for(i=5;i<strlen(topic); i++){
			if(topic[i]==')')
				break;
			tmp[i-5] = topic[i];
		}
		tmp[i-5] = '\0';
		[[HelpManager getHMController] showHelpFor: [NSString stringWithCString:tmp]];
	}
}

- (void) setupToolbar {

    // Create a new toolbar instance, and attach it to our document window 
   toolbar = [[[NSToolbar alloc] initWithIdentifier: RToolbarIdentifier] autorelease];
    
    // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults 
    [toolbar setAllowsUserCustomization: YES];
    [toolbar setAutosavesConfiguration: YES];
    [toolbar setDisplayMode: NSToolbarDisplayModeIconOnly];
    
    // We are the delegate
    [toolbar setDelegate: self];
    
    // Attach the toolbar to the document window 
    [RConsoleWindow setToolbar: toolbar];
}

- (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
    // Required delegate method:  Given an item identifier, this method returns an item 
    // The toolbar will use this method to obtain toolbar items that can be displayed in the customization sheet, or in the toolbar itself 
    NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
    
    if ([itemIdent isEqual: SaveDocToolbarItemIdentifier]) {
		// Set the text label to be displayed in the toolbar and customization palette 
		[toolbarItem setLabel: @"Save"];
		[toolbarItem setPaletteLabel: @"Save Console Window"];
		[toolbarItem setToolTip: @"Save R Console Window"];
		[toolbarItem setImage: [NSImage imageNamed: @"SaveDocumentItemImage"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(saveDocument:)];
    } else if ([itemIdent isEqual: NewEditWinToolbarItemIdentifier]) {
		// Set the text label to be displayed in the toolbar and customization palette 
		[toolbarItem setLabel: @"New Document"];
		[toolbarItem setPaletteLabel: @"New Document"];
		[toolbarItem setToolTip: @"Create a new, empty document in the editor"];
		[toolbarItem setImage: [NSImage imageNamed: @"emptyDoc"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(newDocument:)];

    } else  if ([itemIdent isEqual: X11ToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Start X11"];
		[toolbarItem setPaletteLabel: @"Start X11 Server"];
		[toolbarItem setToolTip: @"Start The X11 Window Server To Allow R Using X11 device and Tcl/Tk"];
		[toolbarItem setImage: [NSImage imageNamed: @"X11"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(runX11:)];

    }  else  if ([itemIdent isEqual: SetColorsToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Set Colors"];
		[toolbarItem setPaletteLabel: @"Set R Colors"];
		[toolbarItem setToolTip: @"Set R Console Colors"];
		[toolbarItem setImage: [NSImage imageNamed: @"colors"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(openColors:)];

    } else  if ([itemIdent isEqual: LoadFileInEditorToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Open In Editor"];
		[toolbarItem setPaletteLabel: @"Open In Editor"];
		[toolbarItem setToolTip: @"Open In Editor"];
		[toolbarItem setImage: [NSImage imageNamed: @"RDoc"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(openDocument:)];

    } else  if ([itemIdent isEqual: SourceRCodeToolbarIdentifier]) {
		[toolbarItem setLabel: @"Source/Load"];
		[toolbarItem setPaletteLabel: @"Source Or Load In R"];
		[toolbarItem setToolTip: @"Source Script Or Load Data In R"];
		[toolbarItem setImage: [NSImage imageNamed: @"sourceR"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(sourceOrLoadFile:)];

    } else if([itemIdent isEqual: NewQuartzToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Quartz"];
		[toolbarItem setPaletteLabel: @"Quartz"];
		[toolbarItem setToolTip: @"Open a new Quartz device window"];
		[toolbarItem setImage: [NSImage imageNamed: @"quartz"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(newQuartzDevice:) ];

	} else if([itemIdent isEqual: InterruptToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Abort"];
		[toolbarItem setPaletteLabel: @"Abort"];
		toolbarStopItem = toolbarItem;
		[toolbarItem setToolTip: @"Interrupt current R computation"];
		[toolbarItem setImage: [NSImage imageNamed: @"stop"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(breakR:) ];

	}  else if([itemIdent isEqual: FontSizeToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Font Size"];
		[toolbarItem setPaletteLabel: @"Font Size"];
		[toolbarItem setToolTip: @"Grow or shrink the size of R Console font"];
		[toolbarItem setTarget: self];
		[toolbarItem performSelector:@selector(setView:) withObject:fontSizeView];
		[toolbarItem setAction:NULL];
		[toolbarItem setView:fontSizeView];
		if ([toolbarItem view]!=NULL)
		{
			[toolbarItem setMinSize:[[toolbarItem view] bounds].size];
			[toolbarItem setMaxSize:[[toolbarItem view] bounds].size];
		}

	}  else if([itemIdent isEqual: NewQuartzToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Quartz"];
		[toolbarItem setPaletteLabel: @"Quartz"];
		[toolbarItem setToolTip: @"Open a new Quartz device window"];
		[toolbarItem setImage: [NSImage imageNamed: @"quartz"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(newQuartzDevice:) ];
	
	} else if([itemIdent isEqual: ShowHistoryToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"History"];
		[toolbarItem setPaletteLabel: @"History"];
		[toolbarItem setToolTip: @"Show/Hide R Command History"];
		[toolbarItem setImage: [NSImage imageNamed: @"history"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(toggleHistory:) ];
	
	} else if([itemIdent isEqual: AuthenticationToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Authentication"];
		[toolbarItem setPaletteLabel: @"Authentication"];
		[toolbarItem setToolTip: @"Authorize R to run system commands as root"];
		[toolbarItem setImage: [NSImage imageNamed: @"lock-locked"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(toggleAuthentication:) ];
	
	} else if([itemIdent isEqual: QuitRToolbarItemIdentifier]) {
		[toolbarItem setLabel: @"Quit"];
		[toolbarItem setPaletteLabel: @"Quit"];
		[toolbarItem setToolTip: @"Quit R"];
		[toolbarItem setImage: [NSImage imageNamed: @"quit"]];
		[toolbarItem setTarget: self];
		[toolbarItem setAction: @selector(quitR:) ];
	
	} else {
	// itemIdent refered to a toolbar item that is not provide or supported by us or cocoa 
	// Returning nil will inform the toolbar this kind of item is not supported 
	toolbarItem = nil;
    }
    return toolbarItem;
}


- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar {
    // Required delegate method:  Returns the ordered list of items to be shown in the toolbar by default    
    // If during the toolbar's initialization, no overriding values are found in the user defaults, or if the
    // user chooses to revert to the default items this set will be used 
    return [NSArray arrayWithObjects:	InterruptToolbarItemIdentifier, SourceRCodeToolbarIdentifier,
		NewQuartzToolbarItemIdentifier, X11ToolbarItemIdentifier,
		NSToolbarSeparatorItemIdentifier,
		AuthenticationToolbarItemIdentifier, ShowHistoryToolbarItemIdentifier,
		SetColorsToolbarItemIdentifier,
		NSToolbarSeparatorItemIdentifier, /* SaveDocToolbarItemIdentifier, */
		LoadFileInEditorToolbarItemIdentifier,
		NewEditWinToolbarItemIdentifier, NSToolbarPrintItemIdentifier, 
		NSToolbarSeparatorItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
		QuitRToolbarItemIdentifier, nil];
}

- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar {
    // Required delegate method:  Returns the list of all allowed items by identifier.  By default, the toolbar 
    // does not assume any items are allowed, even the separator.  So, every allowed item must be explicitly listed   
    // The set of allowed items is used to construct the customization palette 
    return [NSArray arrayWithObjects: 	QuitRToolbarItemIdentifier, AuthenticationToolbarItemIdentifier, ShowHistoryToolbarItemIdentifier, 
		InterruptToolbarItemIdentifier, NewQuartzToolbarItemIdentifier, /* SaveDocToolbarItemIdentifier, */
		NewEditWinToolbarItemIdentifier, LoadFileInEditorToolbarItemIdentifier,
		NSToolbarPrintItemIdentifier, NSToolbarCustomizeToolbarItemIdentifier,
		NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, 
		NSToolbarSeparatorItemIdentifier, X11ToolbarItemIdentifier,
		SetColorsToolbarItemIdentifier,
		FontSizeToolbarItemIdentifier, SourceRCodeToolbarIdentifier, nil];
}

- (void) toolbarWillAddItem: (NSNotification *) notif {
    // Optional delegate method:  Before an new item is added to the toolbar, this notification is posted.
    // This is the best place to notice a new item is going into the toolbar.  For instance, if you need to 
    // cache a reference to the toolbar item or need to set up some initial state, this is the best place 
    // to do it.  The notification object is the toolbar to which the item is being added.  The item being 
    // added is found by referencing the @"item" key in the userInfo 
    NSToolbarItem *addedItem = [[notif userInfo] objectForKey: @"item"];
    if ([[addedItem itemIdentifier] isEqual: NSToolbarPrintItemIdentifier]) {
		[addedItem setToolTip: @"Print Your Document"];
		[addedItem setTarget: self];
    }
}  

- (void) toolbarDidRemoveItem: (NSNotification *) notif {
    // Optional delegate method:  After an item is removed from a toolbar, this notification is sent.   This allows 
    // the chance to tear down information related to the item that may have been cached.   The notification object
    // is the toolbar from which the item is being removed.  The item being added is found by referencing the @"item"
    // key in the userInfo 
   // NSToolbarItem *removedItem = [[notif userInfo] objectForKey: @"item"];
}

- (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem {
    // Optional method:  This message is sent to us since we are the target of some toolbar item actions 
    // (for example:  of the save items action) 
    BOOL enable = NO;
    if ([[toolbarItem itemIdentifier] isEqual: SaveDocToolbarItemIdentifier]) {
		enable = [RConsoleWindow isDocumentEdited];
    } else if ([[toolbarItem itemIdentifier] isEqual: SourceRCodeToolbarIdentifier]) {
		enable = YES;
    } else if ([[toolbarItem itemIdentifier] isEqual: X11ToolbarItemIdentifier]) {
		enable = YES;
    } else if ([[toolbarItem itemIdentifier] isEqual: SetColorsToolbarItemIdentifier]) {
		enable = YES;
    } else if ([[toolbarItem itemIdentifier] isEqual: LoadFileInEditorToolbarItemIdentifier]) {
		enable = YES;
    } else if ([[toolbarItem itemIdentifier] isEqual: NewEditWinToolbarItemIdentifier]) {
		enable = YES;
    } else if ([[toolbarItem itemIdentifier] isEqual: NSToolbarPrintItemIdentifier]) {
		enable = YES;
    } else if ([[toolbarItem itemIdentifier] isEqual: NewQuartzToolbarItemIdentifier]) {
		enable = YES;
    } else if ([[toolbarItem itemIdentifier] isEqual: InterruptToolbarItemIdentifier]) {
		enable = (busyRFlag || (childPID>0));
	} else if ([[toolbarItem itemIdentifier] isEqual: ShowHistoryToolbarItemIdentifier]) {
		enable = YES;
	} else if ([[toolbarItem itemIdentifier] isEqual: AuthenticationToolbarItemIdentifier]) {
		enable = YES;
	} else if ([[toolbarItem itemIdentifier] isEqual: QuitRToolbarItemIdentifier]) {
		enable = YES;
    }		
    return enable;
}


/* This is needed to force the NSDocument to know when edited windows are dirty */
- (void) RConsoleDidResize: (NSNotification *)notification{
	[self setOptionWidth:NO];
}

- (void) setOptionWidth:(BOOL)force;
{
	float newSize = [[RTextView textContainer] containerSize].width;
	float newFontSize = [[RTextView font] pointSize];
	if((newSize != currentSize) | (newFontSize != currentFontSize) | force){
		float newConsoleWidth = 1.5*newSize/newFontSize-1.0;
		if((int)newConsoleWidth != (int)currentConsoleWidth){
			R_SetOptionWidth(newConsoleWidth);
			currentSize = newSize;	
			currentFontSize = newFontSize;
			currentConsoleWidth = newConsoleWidth;
		}
	}
}

- (void) setOpenInEditor: (BOOL) flag
{
	openInEditor = flag;
}

- (BOOL) openInEditor
{
	return openInEditor;
}

-(IBAction) checkForUpdates:(id)sender{
	[[REngine mainEngine] executeString: @"Rapp.updates()"];
}

-(IBAction) getWorkingDir:(id)sender
{
	[self sendInput:@"getwd()"];
//	[[REngine mainEngine] evaluateString: @"getwd()"];

}

-(IBAction) resetWorkingDir:(id)sender
{
	chdir(R_ExpandFileName("~/"));
}

-(IBAction) setWorkingDir:(id)sender
{
	NSOpenPanel *op;
	char	buf[301];
	getcwd(buf, 300);
	int answer;
	
	op = [NSOpenPanel openPanel];
	[op setCanChooseDirectories:YES];
	[op setCanChooseFiles:NO];
	[op setTitle:@"Choose New Working Directory"];

	answer = [op runModalForDirectory:[NSString stringWithCString:buf] file:nil types:[NSArray arrayWithObject:@""]];
	[op setCanChooseDirectories:YES];
	[op setCanChooseFiles:NO];

	if(answer == NSOKButton) {
		if([op directory] != nil){
			CFStringGetCString((CFStringRef)[op directory], buf, 300,  kCFStringEncodingMacRoman); 
			chdir(buf);
			}
	}
	

}

- (IBAction) showWorkingDir:(id)sender
{
	char	buf[301];
    
	getcwd(buf, 300);

	[WDirView setEditable:YES];
	[WDirView setStringValue: [NSString stringWithCString:buf]];
	[WDirView setEditable:NO];
}



- (IBAction)installFromDir:(id)sender
{
	NSOpenPanel *op;
	int answer;
	
	op = [NSOpenPanel openPanel];
	[op setCanChooseDirectories:YES];
	[op setCanChooseFiles:NO];
	[op setTitle:@"Select Package Directory"];
	
	answer = [op runModalForDirectory:nil file:nil types:[NSArray arrayWithObject:@""]];
	[op setCanChooseDirectories:NO];
	[op setCanChooseFiles:YES];		

	if(answer == NSOKButton) 
		if([op directory] != nil)
			[[REngine mainEngine] executeString: [NSString stringWithFormat:@"install.from.file(pkg=\"%@\")",[op directory]] ];
}

- (IBAction)installFromBinary:(id)sender
{
	[[REngine mainEngine] executeString: @"install.from.file(binary=TRUE)" ];
}

- (IBAction)installFromSource:(id)sender
{
	[[REngine mainEngine] executeString:@"install.from.file(binary=FALSE)" ];
}

- (IBAction)togglePackageInstaller:(id)sender
{
	[[PackageInstaller sharedController] show];
}

- (IBAction)toggleWSBrowser:(id)sender
{
	[WSBrowser toggleWorkspaceBrowser];
	[[REngine mainEngine] executeString:@"browseEnv(html=F)"];

}

- (IBAction)loadWorkSpace:(id)sender
{
	[self sendInput:@"load(\".RData\")"];
//	[[REngine mainEngine] evaluateString:@"load(\".RData\")" ];

}

- (IBAction)saveWorkSpace:(id)sender
{
	[self sendInput:@"save.image()"];
//	[[REngine mainEngine] evaluateString:@"save.image()"];
	
}

- (IBAction)loadWorkSpaceFile:(id)sender
{
	[[REngine mainEngine] executeString:@"load(file.choose())"];
}					

- (IBAction)saveWorkSpaceFile:(id)sender
{
	[[REngine mainEngine] executeString: @"save.image(file=file.choose(TRUE))"];
}

- (IBAction)showWorkSpace:(id)sender{
	[self sendInput:@"ls()"];
}

- (IBAction)clearWorkSpace:(id)sender
{
	NSBeginAlertSheet(@"Clear Workspace",@"Yes",@"No !!!",nil,RConsoleWindow,self,@selector(shouldClearWS:returnCode:contextInfo:),NULL,NULL,
	@"All objects in the workspace will be removed. Are you sure you want to proceed?");    
}

/* this gets called by the "wanna save?" sheet on window close */
- (void) shouldClearWS:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
    if (returnCode==NSAlertDefaultReturn)
		[[REngine mainEngine] executeString: @"rm(list=ls())"];
}

- (IBAction)togglePackageManager:(id)sender
{
	if ([[PackageManager sharedController] count]==0)
		[[REngine mainEngine] executeString:@"package.manager()"];
	else
		[[PackageManager sharedController] show];
}

- (IBAction)toggleDataManager:(id)sender
{
	if ([[DataManager sharedController] count]==0) {
		[[DataManager sharedController] show];
		[[REngine mainEngine] executeString: @"data.manager()"];
	} else
		[[DataManager sharedController] show];
}


-(IBAction) runX11:(id)sender{
	system("open -a X11.app");
}
			
-(IBAction) openColors:(id)sender{
	[prefsWindow selectPaneWithIdentifier:@"Colors"];
	[prefsWindow showWindow:self];
	[[prefsWindow window] makeKeyAndOrderFront:self];
}



- (IBAction)performHelpSearch:(id)sender {
    if ([[sender stringValue] length]>0) {
//		[self sendInput:[NSString stringWithFormat:@"help.search(\"%@\")", [sender stringValue]]];
		[[REngine mainEngine] executeString: [NSString stringWithFormat:@"print(help.search(\"%@\"))", [sender stringValue]]];
        [helpSearch setStringValue:@""];
    }
}

- (IBAction)sourceOrLoadFile:(id)sender
{
	int answer;
	NSOpenPanel *op;
	op = [NSOpenPanel openPanel];
	[op setTitle:@"R File to Source/Load"];
	answer = [op runModalForTypes:nil];
	
	if (answer==NSOKButton)
		[self loadFile:(char *)[[op filename] cString]];
}

- (IBAction)sourceFile:(id)sender
{
	int answer;
	NSOpenPanel *op;
	op = [NSOpenPanel openPanel];
	[op setTitle:@"R File to Source"];
	answer = [op runModalForTypes:nil];

	if (answer==NSOKButton)
		[self sendInput:[NSString stringWithFormat:@"source(\"%@\")",[op filename]]];
}

- (IBAction)printDocument:(id)sender
{
	NSPrintInfo *printInfo;
	NSPrintInfo *sharedInfo;
	NSPrintOperation *printOp;
	NSMutableDictionary *printInfoDict;
	NSMutableDictionary *sharedDict;
	
	sharedInfo = [NSPrintInfo sharedPrintInfo];
	sharedDict = [sharedInfo dictionary];
	printInfoDict = [NSMutableDictionary dictionaryWithDictionary:
		sharedDict];
	
	printInfo = [[NSPrintInfo alloc] initWithDictionary: printInfoDict];
	[printInfo setHorizontalPagination: NSAutoPagination];
	[printInfo setVerticalPagination: NSAutoPagination];
	[printInfo setVerticallyCentered:NO];
	
	printOp = [NSPrintOperation printOperationWithView:RTextView 
											 printInfo:printInfo];
	[printOp setShowPanels:YES];
	[printOp runOperation];
}

/* This method is called by a ColorWell and by the readDefaults.
In the latter case it could be that the newColor is "nil". In this
case the color is set to its default value.
*/
- (void)setInputColor:(NSColor *)newColor {
	if(inputColor)
		[inputColor release];
	
    if (newColor)
        inputColor = [newColor copyWithZone:[self zone]];
	else 
		inputColor = [[NSColor blueColor] copyWithZone:[self zone]];
	
	[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:inputColor] 
											  forKey:inputColorKey];
	[[colorsPrefPane inputColorWell] setColor: inputColor];
	[RTextView setInsertionPointColor: inputColor];
    [[colorsPrefPane inputColorWell] setNeedsDisplay:YES];
}

- (void)setOutputColor:(NSColor *)newColor {
	if(outputColor)
		[outputColor release];
	
    if (newColor)
        outputColor = [newColor copyWithZone:[self zone]];
	else 
		outputColor = [[NSColor blackColor] copyWithZone:[self zone]];

	[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:outputColor] 
											  forKey:outputColorKey];
	[[colorsPrefPane outputColorWell] setColor: outputColor];
    [[colorsPrefPane outputColorWell] setNeedsDisplay:YES];

}

- (void)setPromptColor:(NSColor *)newColor {
	if(promptColor)
		[promptColor release];
	
    if (newColor)
        promptColor = [newColor copyWithZone:[self zone]];
	else 
		promptColor = [[NSColor purpleColor] copyWithZone:[self zone]];

	[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:promptColor] 
											  forKey:promptColorKey];
	[[colorsPrefPane promptColorWell] setColor: promptColor];
    [[colorsPrefPane promptColorWell] setNeedsDisplay:YES];
	[RTextView setNeedsDisplay: YES];	
}

- (void)setStdoutColor:(NSColor *)newColor {
	if(stdoutColor)
		[stdoutColor release];
	
    if (newColor)
        stdoutColor = [newColor copyWithZone:[self zone]];
	else 
		stdoutColor = [[NSColor lightGrayColor] copyWithZone:[self zone]];

	[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:stdoutColor] 
											  forKey:stdoutColorKey];
	[[colorsPrefPane stdoutColorWell] setColor: stdoutColor];
    [[colorsPrefPane stdoutColorWell] setNeedsDisplay:YES];
}

- (void)setStderrColor:(NSColor *)newColor {
	if(stderrColor)
		[stderrColor release];
	
    if (newColor)
        stderrColor = [newColor copyWithZone:[self zone]];
	else 
		stderrColor = [[NSColor redColor] copyWithZone:[self zone]];

	[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:stderrColor] 
											  forKey:stderrColorKey];
	[[colorsPrefPane stderrColorWell] setColor: stderrColor];
    [[colorsPrefPane stderrColorWell] setNeedsDisplay:YES];
}

- (void)setBackGColor:(NSColor *)newColor {
	if(backgColor)
		[backgColor release];
	
    if (newColor)
		backgColor = [ [NSColor colorWithCalibratedRed:[newColor redComponent] 
												 green:[newColor greenComponent]
												  blue:[newColor blueComponent]
												 alpha:alphaValue] copyWithZone:[self zone]];
	else 
		backgColor = [ [NSColor colorWithCalibratedRed:1.0 
												 green:1.0
												  blue:1.0
												 alpha:alphaValue] copyWithZone:[self zone]];

	[RConsoleWindow setBackgroundColor:backgColor];
	[RConsoleWindow display];
	
	[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:backgColor] 
											  forKey:backgColorKey];
	[[colorsPrefPane backgColorWell] setColor: backgColor];
    [[colorsPrefPane backgColorWell] setNeedsDisplay:YES];
}

- (void) setInternalOrExternal:(int)internal {
	if(internalOrExternal)
		[internalOrExternal release];
	
	if (internal == 1) {
		[[editorPrefPane externalSettings] setHidden:YES]; 
		[[editorPrefPane builtInPrefs] setHidden:NO]; 
		internalOrExternal = @"YES";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:internalOrExternal] 
												  forKey:internalOrExternalKey];
		[[editorPrefPane internalOrExternal] setState:0 atRow:0 column:1];
		[[editorPrefPane internalOrExternal] setState:1 atRow:0 column:0];
	} else {
		[[editorPrefPane externalSettings] setHidden:NO]; 
		[[editorPrefPane builtInPrefs] setHidden:YES]; 
		internalOrExternal = @"NO";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:internalOrExternal] 
												forKey:internalOrExternalKey];
		[[editorPrefPane internalOrExternal] setState:0 atRow:0 column:0];
		[[editorPrefPane internalOrExternal] setState:1 atRow:0 column:1];
	}
}

- (void) setShowSyntaxColoring:(int)state {
	if(showSyntaxColoring)
		[showSyntaxColoring release];
	
	if (state == NSOnState) {
		showSyntaxColoring = @"YES";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:showSyntaxColoring] 
												  forKey:showSyntaxColoringKey];
		[[editorPrefPane showSyntaxColoring] setState:YES]; 
	} else {
		showSyntaxColoring = @"NO";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:showSyntaxColoring] 
												  forKey:showSyntaxColoringKey];
		[[editorPrefPane showSyntaxColoring] setState:NO]; 
	}
}

- (void) setShowBraceHighlighting:(int)state {
	if(showBraceHighlighting)
		[showBraceHighlighting release];
	
	if (state == NSOnState) {
		showBraceHighlighting = @"YES";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:showBraceHighlighting] 
											  forKey:showBraceHighlightingKey];
		[[editorPrefPane showBraceHighlighting] setState:1]; 
	} else {
		showBraceHighlighting = @"NO";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:showBraceHighlighting] 
											  forKey:showBraceHighlightingKey];
		[[editorPrefPane showBraceHighlighting] setState:0]; 
	}
}

- (void) setHighlightInterval:(NSString *)aString {
	if(highlightInterval)
		[highlightInterval release];
	
	if ([aString length] == 0) {
		highlightInterval = @"0.2";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:highlightInterval] 
											  forKey:highlightIntervalKey];
		[[editorPrefPane highlightInterval] setStringValue:highlightInterval]; 
	} else {
		double val = [aString doubleValue];
		if (val < 0.1)
			aString = @"0.1";
		else if (val > 0.8)
			aString = @"0.8";
		highlightInterval = aString;
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:highlightInterval] 
											  forKey:highlightIntervalKey];
		[[editorPrefPane highlightInterval] setStringValue:aString]; 
	}
}

- (void) setShowLineNumbers:(int)state {
	if(showLineNumbers)
		[showLineNumbers release];
	
	if (state == NSOnState) {
		showLineNumbers = @"YES";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:showLineNumbers] 
											  forKey:showLineNumbersKey];
		[[editorPrefPane showLineNumbers] setState:1]; 
	} else {
		showLineNumbers = @"NO";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:showLineNumbers] 
											  forKey:showLineNumbersKey];
		[[editorPrefPane showLineNumbers] setState:0]; 
	}
}

- (void)setExternalEditorName:(NSString *)name {
	if(externalEditorName)
		[externalEditorName release];
	
	externalEditorName = name;
	[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:externalEditorName] 
											  forKey:externalEditorNameKey];
	if ([name length] == 0 )
		[[editorPrefPane externalEditorName] setStringValue: @""];
	else
		[[editorPrefPane externalEditorName] setStringValue: name];
}

- (void) setAppOrCommand:(int)app {
	if(appOrCommand)
		[appOrCommand release];
	
	if (app == 1) {
		appOrCommand = @"YES";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:appOrCommand] 
												  forKey:appOrCommandKey];
		[[editorPrefPane appOrCommand] setState:0 atRow:1 column:0];
		[[editorPrefPane appOrCommand] setState:1 atRow:0 column:0];
	} else {
		appOrCommand = @"NO";
		[[NSUserDefaults standardUserDefaults] setObject:[NSArchiver archivedDataWithRootObject:appOrCommand] 
												  forKey:appOrCommandKey];
		[[editorPrefPane appOrCommand] setState:NSOffState atRow:0 column:0];
		[[editorPrefPane appOrCommand] setState:NSOnState atRow:1 column:0];
	}
}

- (IBAction) changeInternalOrExternal:(id)sender {
	if ([sender selectCellAtRow:0 column:0] == NSOffState)
		[self setInternalOrExternal:0];
	else
		[self setInternalOrExternal:1];
}

- (IBAction) changeShowSyntaxColoring:(id)sender {
    [self setShowSyntaxColoring:[sender state]];
}

- (IBAction) changeShowBraceHighlighting:(id)sender {
    [self setShowBraceHighlighting:[sender state]];
}

- (IBAction) changeHighlightInterval:(id)sender {
    [self setHighlightInterval:[sender stringValue]];
}

- (IBAction) changeShowLineNumbers:(id)sender {
    [self setShowLineNumbers:[sender state]];
}

- (void)changeExternalEditorName:(id)sender {
	NSString *name = [sender stringValue];
	if ([name length] == 0) {
		[self setExternalEditorName:nil];
		 	} else {
				[self setExternalEditorName: name];
			}
}

- (IBAction) changeAppOrCommand:(id)sender {
	if ([sender selectCellAtRow:0 column:0] == NSOffState)
		[self setAppOrCommand:0];
	else
		[self setAppOrCommand:1];
}

- (void)changeInputColor:(id)sender {
    [self setInputColor:[sender color]];
}

- (void)changeOutputColor:(id)sender {
    [self setOutputColor:[sender color]];
}

- (void)changePromptColor:(id)sender {
    [self setPromptColor:[sender color]];
}

- (void)changeStdoutColor:(id)sender {
    [self setStdoutColor:[sender color]];
}

- (void)changeStderrColor:(id)sender {
    [self setStderrColor:[sender color]];
}

- (void)changeBackGColor:(id)sender {
	[self setBackGColor:[sender color]];
	[RTextView setBackgroundColor:[sender color]];
}

- (IBAction) changeAlphaColor:(id)sender {
	[self setAlphaValue:[sender floatValue]];
	[self setBackGColor: [[colorsPrefPane backgColorWell] color]];
	[RTextView setNeedsDisplay: YES];
}

- (void)setAlphaValue:(float)f {
    alphaValue = f;

	if(backgColor)
		[self setBackGColor:backgColor];
	
	[[NSUserDefaults standardUserDefaults] setFloat:alphaValue 
											  forKey:alphaValueKey];
	[[colorsPrefPane alphaStepper] setFloatValue:alphaValue];

}

- (IBAction) setDefaultColors:(id)sender {
	[self setAlphaValue: 1.0];
	[self setBackGColor: nil];
	[self setInputColor: nil];
	[self setOutputColor: nil];
	[self setPromptColor: nil];
	[self setStderrColor: nil];
	[self setStdoutColor: nil];
	
}


- (void) readDefaults {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	
	
	if( [defaults stringForKey:FontSizeKey] == nil )
			currentFontSize = 11.0;
	else
		currentFontSize =  [defaults floatForKey:FontSizeKey];
	
	if( [defaults stringForKey:alphaValueKey] == nil )
		[self setAlphaValue: 1.0];
	else
		[self setAlphaValue: [defaults floatForKey:alphaValueKey]];

	NSData *theData=[defaults dataForKey:backgColorKey];
	if(theData != nil)		
		[self setBackGColor: (NSColor *)[NSUnarchiver unarchiveObjectWithData:theData]];
	else
		[self setBackGColor: nil];
	
	theData=[defaults dataForKey:inputColorKey];
	if(theData != nil)		
		[self setInputColor: (NSColor *)[NSUnarchiver unarchiveObjectWithData:theData]];
	else
		[self setInputColor: nil];

	theData=[defaults dataForKey:outputColorKey];
	if(theData != nil)		
		[self setOutputColor: (NSColor *)[NSUnarchiver unarchiveObjectWithData:theData]];
	else
		[self setOutputColor: nil];

	theData=[defaults dataForKey:promptColorKey];
	if(theData != nil)		
		[self setPromptColor: (NSColor *)[NSUnarchiver unarchiveObjectWithData:theData]];
	else
		[self setPromptColor: nil];
		
	theData = [defaults dataForKey:stderrColorKey];
	if(theData != nil)		
		[self setStderrColor: (NSColor *)[NSUnarchiver unarchiveObjectWithData:theData]];
	else
		[self setStderrColor: nil];
		
	theData=[defaults dataForKey:stdoutColorKey];
	if(theData != nil)		
		[self setStdoutColor: (NSColor *)[NSUnarchiver unarchiveObjectWithData:theData]];
	else
		[self setStdoutColor: nil];
	
	theData=[defaults dataForKey:internalOrExternalKey];
	if(theData != nil){
		if ([(NSString *)[NSUnarchiver unarchiveObjectWithData:theData] isEqualToString: @"NO"]) {
			[self setInternalOrExternal: 0];			
		} else {
			[self setInternalOrExternal: 1];
		}
	} else 
		[self setInternalOrExternal: 1];
	
	theData=[defaults dataForKey:showSyntaxColoringKey];
	if(theData != nil){
		if ([(NSString *)[NSUnarchiver unarchiveObjectWithData:theData] isEqualToString: @"NO"]) {
			[self setShowSyntaxColoring: 0];
		} else {
			[self setShowSyntaxColoring: 1];
		}
	} else 
		[self setShowSyntaxColoring: 1];
	
	theData=[defaults dataForKey:showBraceHighlightingKey];
	if(theData != nil){
		if ([(NSString *)[NSUnarchiver unarchiveObjectWithData:theData] isEqualToString: @"NO"])
			[self setShowBraceHighlighting: 0];
		else
			[self setShowBraceHighlighting: 1];
	} else 
		[self setShowBraceHighlighting: 1];
	
	theData=[defaults dataForKey:highlightIntervalKey];
	if(theData != nil){
		[self setHighlightInterval: (NSString *)[NSUnarchiver unarchiveObjectWithData:theData]];
	} else {
		[self setHighlightInterval: @"0.2"];
	}	
	
	theData=[defaults dataForKey:showLineNumbersKey];
	if(theData != nil){
		if ([(NSString *)[NSUnarchiver unarchiveObjectWithData:theData] isEqualToString: @"NO"])
			[self setShowLineNumbers: 0];
		else
			[self setShowLineNumbers: 1];
	} else 
		[self setShowLineNumbers: 0];
	
	theData=[defaults dataForKey:externalEditorNameKey];
	if(theData != nil){
		[self setExternalEditorName: (NSString *)[NSUnarchiver unarchiveObjectWithData:theData]];
	} else {
		[self setExternalEditorName: nil];
	}	

	theData=[defaults dataForKey:appOrCommandKey];
	if(theData != nil){
		if ([(NSString *)[NSUnarchiver unarchiveObjectWithData:theData] isEqualToString: @"YES"]) {
			[self setAppOrCommand: 1];
		} else {
			[self setAppOrCommand: 0];
		}
	} else 
		[self setAppOrCommand: 1];
	
	theData=[defaults dataForKey:editOrSourceKey];
	[miscPrefPane setOpenInEditor: theData == nil || [(NSString *)[NSUnarchiver unarchiveObjectWithData:theData] isEqualToString: @"YES"]];

	theData=[defaults dataForKey:miscRAquaLibPathKey];
	[miscPrefPane setOpenInEditor: theData == nil || [(NSString *)[NSUnarchiver unarchiveObjectWithData:theData] isEqualToString: @"YES"]];

}


- (void)setupPrefWindow
{
	if (!prefsWindow) {
		// create the preference window controller
		[self setPrefsWindow:[[AMPreferenceWindowController alloc] initWithAutosaveName:@"PreferencePanel"]];
		// we act as delegate ourselves
		[prefsWindow setDelegate:self];
		// load plugins from the bundle's standard plugins folder
		//	[prefsWindow addPluginsOfType:nil fromPath:[[NSBundle mainBundle] builtInPlugInsPath]];
		// creating some test preference panes
		quartzPrefPane = [[[QuartzPrefPane alloc] initWithIdentifier:@"Quartz" label:@"Quartz" category:@"Graphics"] autorelease];
		[prefsWindow addPane:quartzPrefPane withIdentifier:[quartzPrefPane identifier]];
		
		miscPrefPane = [[[MiscPrefPane alloc] initWithIdentifier:@"Misc" label:@"Misc" category:@"General"] autorelease];
		[prefsWindow addPane:miscPrefPane withIdentifier:[miscPrefPane identifier]];
		
		colorsPrefPane = [[[ColorsPrefPane alloc] initWithIdentifier:@"Colors" label:@"Colors" category:@"Console"] autorelease];
		[prefsWindow addPane:colorsPrefPane withIdentifier:[colorsPrefPane identifier]];
		
		editorPrefPane = [[[EditorPrefPane alloc] initWithIdentifier:@"Editor" label:@"Editor" category:@"Editor"] autorelease];
		[prefsWindow addPane:editorPrefPane withIdentifier:[editorPrefPane identifier]];
		
		// set up some configuration options
		[prefsWindow setUsesConfigurationPane:YES];
		[prefsWindow setSortByCategory:YES];
		// select prefs pane for display
		[prefsWindow selectPaneWithIdentifier:@"All"];
	}	
}

- (IBAction)showPrefsWindow:(id)sender
{
	[prefsWindow showWindow:self];
	[[prefsWindow window] makeKeyAndOrderFront:self];
}

- (void)sortByAlphabet:(id)sender
{
	[prefsWindow setSortByCategory:NO];
	[prefsWindow selectIconViewPane];
}

- (void)sortByCategory:(id)sender
{
	[prefsWindow setSortByCategory:YES];
	[prefsWindow selectIconViewPane];
}


- (AMPreferenceWindowController *)prefsWindow
{
    return prefsWindow;
}

- (void)setPrefsWindow:(AMPreferenceWindowController *)newPrefsWindow
{
    id old = nil;
	
    if (newPrefsWindow != prefsWindow) {
        old = prefsWindow;
        prefsWindow = [newPrefsWindow retain];
        [old release];
    }
}

- (BOOL)shouldLoadPreferencePane:(NSString *)identifier
{
//	NSLog(@"shouldLoadPreferencePane: %@", identifier);
	return YES;
}

- (void)willSelectPreferencePane:(NSString *)identifier
{
//	NSLog(@"willSelectPreferencePane: %@", identifier);
}

- (void)didUnselectPreferencePane:(NSString *)identifier
{
//	NSLog(@"didUnselectPreferencePane: %@", identifier);
}

- (NSString *)displayNameForCategory:(NSString *)category
{
	return category;
}


- (NSTextView *)getRTextView{
	return RTextView;
}

- (NSWindow *)getRConsoleWindow{
	return RConsoleWindow;
}

@end


