/* * Cocoa Module for R : A Computer Language for Statistical Data Analysis * Copyright (C) 2004 R Development Core Team * Authors: Simon Urbanek and Stefano Iacus * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #import "Controller.h" #import "TextController.h" #define EmptyTrashItemTag 56433 #define ReallyEmptyTrashItemTag 56434 #define Radio1Tag 56533 #define Radio2Tag 56534 #define Radio3Tag 56535 #define Switch1Tag 56633 #define Switch2Tag 56634 static Controller *sharedController; BOOL endRunLoop = NO; // global @implementation Controller /** there is always only one shared controller associated with this bundle */ + (Controller *)sharedController { return sharedController; } - (void)createPackagesMenu { // There's really no need for this menu to be created programatically. It could just as easily be created in IB. I'm creating it here just to make it clear there's no special magic going on to get these slightly trickier features to work. NSMenu *newMenu; NSMenuItem *newItem; // Add the submenu newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Packages" action:NULL keyEquivalent:@""]; newMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Packages"]; [newItem setSubmenu:newMenu]; [newMenu release]; [[NSApp mainMenu] addItem:newItem]; [newItem release]; // Add some tricky items newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Empty Trash..." action:NULL keyEquivalent:@"T"]; [newItem setTag:EmptyTrashItemTag]; [newItem setTarget:self]; [newItem setAction:@selector(emptyTrash:)]; [newMenu addItem:newItem]; [newItem release]; [newMenu addItem:[NSMenuItem separatorItem]]; newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Radio 1" action:NULL keyEquivalent:@""]; [newItem setTag:Radio1Tag]; [newItem setTarget:self]; [newItem setAction:@selector(radioAction:)]; [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]]; [newMenu addItem:newItem]; [newItem release]; newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Radio 2" action:NULL keyEquivalent:@""]; [newItem setTag:Radio2Tag]; [newItem setTarget:self]; [newItem setAction:@selector(radioAction:)]; [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]]; [newMenu addItem:newItem]; [newItem release]; newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Radio 3" action:NULL keyEquivalent:@""]; [newItem setTag:Radio3Tag]; [newItem setTarget:self]; [newItem setAction:@selector(radioAction:)]; [newItem setOnStateImage:[NSImage imageNamed:@"NSMenuRadio"]]; [newMenu addItem:newItem]; [newItem release]; [newMenu addItem:[NSMenuItem separatorItem]]; newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Switch1" action:NULL keyEquivalent:@""]; [newItem setTag:Switch1Tag]; [newItem setTarget:self]; [newItem setAction:@selector(switch1Action:)]; [newMenu addItem:newItem]; [newItem release]; newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Show Something" action:NULL keyEquivalent:@""]; [newItem setTag:Switch2Tag]; [newItem setTarget:self]; [newItem setAction:@selector(switch2Action:)]; [newMenu addItem:newItem]; [newItem release]; } /** initialization of the bundle controller - this is called in initializeBundle. Note that we assume the existence of the Carbon initialization! */ - (id)init { self = [super init]; sharedController = self; NSApplicationLoad(); if (![NSBundle loadNibNamed:@"RGUI" owner:self]) { NSLog(@"failed to load bundle's nib"); } [self createPackagesMenu]; currentRadioSetting = Radio1Tag; currentSwitch1Setting = NO; currentSwitch2Setting = NO; return self; } /** set the associated text controller */ - (void) setTextController: (TextController*) c { txtCtrl = c; } /** retrieve the associated text controller */ - (TextController*) textController { if (!txtCtrl) NSLog(@"Warning! [Controller textController] is nil! A crash is likely to follow!"); return txtCtrl; } /** set the callback function of userInput; we call this function when we want to pass commands to R */ - (void)setUserInputFn:(userInputType) fn { userInputFn = fn; } /** show the associated main window and make it foremost */ - (void)showWindow { [window makeKeyAndOrderFront:nil]; } /** send the text as input to the R process */ - (void) userInput: (NSString*) text { if (userInputFn) userInputFn([text cString]); } - (void)emptyTrash:(id)sender { if (NSRunAlertPanel(@"Warning", @"Do you really want to yada yada yada?", @"Yada?", @"No", NULL) == NSOKButton) { NSLog(@"Yada."); } } - (void)reallyEmptyTrash:(id)sender { NSLog(@"Yada."); } - (void)radioAction:(id)sender { currentRadioSetting = [sender tag]; } - (void)switch1Action:(id)sender { currentSwitch1Setting = (currentSwitch1Setting ? NO : YES); } - (void)switch2Action:(id)sender { currentSwitch2Setting = (currentSwitch2Setting ? NO : YES); } - (BOOL)validateMenuItem:(NSMenuItem *)item { int tag = [item tag]; if ((tag == EmptyTrashItemTag) || (tag == ReallyEmptyTrashItemTag)) { BOOL shouldBeReally = (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) ? YES : NO); if (shouldBeReally && (tag == EmptyTrashItemTag)) { [item setTitle:@"Really Empty Trash"]; [item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)]; [item setAction:@selector(reallyEmptyTrash:)]; [item setTag:ReallyEmptyTrashItemTag]; } else if (!shouldBeReally && (tag == ReallyEmptyTrashItemTag)) { [item setTitle:@"Empty Trash..."]; [item setKeyEquivalentModifierMask:NSCommandKeyMask]; [item setAction:@selector(emptyTrash:)]; [item setTag:EmptyTrashItemTag]; } return YES; } else if ((tag == Radio1Tag) || (tag == Radio2Tag) || (tag == Radio3Tag)) { [item setState:((tag == currentRadioSetting) ? NSOnState : NSOffState)]; return YES; } else if (tag == Switch1Tag) { [item setState:(currentSwitch1Setting ? NSOnState : NSOffState)]; return YES; } else if (tag == Switch2Tag) { [item setTitle:(currentSwitch2Setting ? @"Hide Something" : @"Show Something")]; return YES; } else { return YES; } } @end /*===================================================================== bundle entry functions - those functions are loaded and used by R */ //NSAutoreleasePool *Pool; /** initializes the bundle by creating the main shared controller. returns the feature bitmask of this bundle */ int initializeBundle(userInputType uiFn) { Controller *controller; NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; // Pool = [[NSAutoreleasePool alloc] init]; controller = [[Controller alloc] init]; [controller setUserInputFn: uiFn]; [NSApplication sharedApplication]; [localPool release]; return 7; /* combination of the following possible flags: 1=cocoa_basic; 2=cocoa_loop; 4=cocoa_menu */ } void DeInitializeBundle() { //deallocate the autorelease pool if neccessary. // if (Pool != NULL) // {[Pool release];} } /** calls showWindow of the main controller */ int selectWindow(int wid) { NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; [[Controller sharedController] showWindow]; /* currently we have only one window so we ignore wid */ [localPool release]; return 0; } /** writes string (R output) to the console */ int writeConsole(CFStringRef message) { NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; [[[Controller sharedController] textController] writeConsole:(NSString *)message]; [localPool release]; return 0; } /** writes echoed user input to the console */ int writeUserInput(CFStringRef message) { NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; [[[Controller sharedController] textController] writeUserInput:(NSString *)message]; [localPool release]; return 0; } /** writes prompt to the console */ int writePrompt(CFStringRef message) { NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; NSLog(@"writePrompt: %@\n",(NSString *)message); [[[Controller sharedController] textController] writePrompt:(NSString *)message]; [localPool release]; return 0; } /** indicates whether R is busy or idle */ int RisBusy(int i) { NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; [[[Controller sharedController] textController] RisBusy:i]; [localPool release]; return 0; } int processEvents(int lnr) { BOOL isRunning; NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; NSRunLoop *theRL = [NSRunLoop currentRunLoop]; NSLog(@"processEvents"); //endRunLoop = YES; do { isRunning = [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; if(isRunning) NSLog(@"event"); } while (!endRunLoop); NSLog(@"exiting processEvents"); [localPool release]; return 0; } /* optional, unimplemented functions: */ /* int setupMenu(int what) */