Rev 755 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** 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.cextern void RGUI_ReplConsole(SEXP rho, int savestack, int browselevel); // from Rinit.cextern 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 neededreadConsTransBuffer = (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 necessaryNSData *theData=[[NSUserDefaults standardUserDefaults] dataForKey:miscRAquaLibPathKey];BOOL flag = !isAdmin(); // the default is YES for users and NO for adminsif (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.5target:selfselector:@selector(showWorkingDir:)userInfo:0repeats:YES];hist=[[History alloc] init];BOOL WantThread = YES;if(WantThread){ // re-route the stdout to our own file descriptor and use ConnectionCache on itint pfd[2];pipe(pfd);dup2(pfd[1], STDOUT_FILENO);close(pfd[1]);stdoutFD=pfd[0];pipe(pfd);#ifndef PLAIN_STDERRdup2(pfd[1], STDERR_FILENO);close(pfd[1]);#endifstderrFD=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:selfselector:@selector(RConsoleDidResize:)name:NSWindowDidResizeNotificationobject: RConsoleWindow];timer = [NSTimer scheduledTimerWithTimeInterval:0.05target:selfselector:@selector(otherEventLoops:)userInfo:0repeats:YES];Flushtimer = [NSTimer scheduledTimerWithTimeInterval:0.5target:selfselector:@selector(flushTimerHook:)userInfo:0repeats:YES];if (!RLtimer)RLtimer = [NSTimer scheduledTimerWithTimeInterval:0.001target:selfselector:@selector(runRELP:)userInfo:0repeats: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:selfwithObject: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 itNS_DURING[rc writeLogsWithBytes:buf length:pib type:bufFD];NS_HANDLER;NS_ENDHANDLERpib=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 itNS_DURING[rc writeLogsWithBytes:buf length:pib type:bufFD];NS_HANDLER;NS_ENDHANDLERpib=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 itNS_DURING[rc writeLogsWithBytes:buf length:pib type:bufFD];NS_HANDLER;NS_ENDHANDLERpib=0;}if (n==0) rootFD=-1;}if ((forceStdFlush || selr==0) && pib>0) { // dump also if we got a timeoutNS_DURING[rc writeLogsWithBytes:buf length:pib type:bufFD];NS_HANDLER;NS_ENDHANDLERpib=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 occupiedif (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 stringmemcpy(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 reachedif (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 necessaryfree(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 handlerssignal(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 menusThe 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:NSKeyDownlocation:NSMakePoint(0,0)modifierFlags:0timestamp:0windowNumber:[RConsoleWindow windowNumber]context:nilcharacters:@"\n"charactersIgnoringModifiers:nilisARepeat:NOkeyCode: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 *)tableViewobjectValueForTableColumn: (NSTableColumn *)tableColumnrow: (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 .historyThis 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 .historyThis 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 consoleat 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 withoutissuing 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 consoleoutputType: 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 partunichar 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 completionrep=[FileCompletion complete:[text substringFromIndex:lastQuote+1]];er.location+=lastQuote+1;er.length-=lastQuote+1;} else { // otherwise use code completionint 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 partif (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 duringcomputation in "src/main/errors.c". The callback is defined at themoment inside "src/unix/aqua.c". In "errors.c" this is defined toallow for user interrupts but we also use it to make our GUIreponsive to user interaction.*//* This called by a timer periodically to allow tcltk, x11 etc to process their eventslike 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);elseonintr();}- (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);} elsereturn(-1);}- (void) doProcessEvents: (BOOL) blocking {NSEvent *event;if (blocking){event = [NSApp nextEventMatchingMask:NSAnyEventMaskuntilDate:[NSDate distantFuture]inMode:NSDefaultRunLoopModedequeue:YES];[NSApp sendEvent:event];} else {while( (event = [NSApp nextEventMatchingMask:NSAnyEventMaskuntilDate:[NSDate dateWithTimeIntervalSinceNow:0.0001]inMode:NSDefaultRunLoopModedequeue:YES]))[NSApp sendEvent:event];}return;}- (void) handleProcessEvents{[self doProcessEvents: NO];}/*This method calls the showHelpFor method of the Help Manager which opensthe internal html browser/help system of R.appThis 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 windowtoolbar = [[[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 itselfNSToolbarItem *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 supportedtoolbarItem = 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 usedreturn [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 palettereturn [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 userInfoNSToolbarItem *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:RTextViewprintInfo: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 thiscase the color is set to its default value.*/- (void)setInputColor:(NSColor *)newColor {if(inputColor)[inputColor release];if (newColor)inputColor = [newColor copyWithZone:[self zone]];elseinputColor = [[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]];elseoutputColor = [[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]];elsepromptColor = [[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]];elsestdoutColor = [[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]];elsestderrColor = [[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]];elsebackgColor = [ [NSColor colorWithCalibratedRed:1.0green:1.0blue:1.0alpha: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:alphaValueforKey: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;elsecurrentFontSize = [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 panesquartzPrefPane = [[[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