In the R Mac FAQ, users unhappy with the default key bindings are advised to modify the file ~/ Library/ KeyBindings/ DefaultKeyBindings.dict. This is indeed a powerful method for assigning user-specific key BINDINGS and for tying them to a sequence of built-in ACTIONS, mostly drawn from a large list of selector methods for the NSResponder class in Apple's Cocoa ApplicationKit framework (see http://www.hcs.harvard.edu/~jrus/Site/Cocoa%20Text%20System.html or http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/index.html . )
There are a couple of complications with this approach. Firstly, these binding apply to all application that uses the built-in Cocoa Text Framework, which may not be what you want. Even if you can live with a binding that will be unpredictable in other Cocoa applications, it is not obvious how to bind a particular key combination to an existing application keyboard shortcut (e.g. R.App's Edit:Execute command, invoked with Cmd-Return). While I would prefer someone to tell me I've misread, this facility does not appear to support calling one key combination with another (e.g. invoking Cmd-Return as part of a sequence of actions). However, if you browse the R GUI source in Interface Builder, you will see that the Edit:Execute menu control is connected to the 'executeSelection:' method, which can be invoked like any of the built-in selectors, although I have found little documentation that speaks to this functionality. Of course, you need to hope that "executeSelection:" won't electrify your chair if accidentally invoked in some other Cocoa text-handling application :) Although it seems that most people are happy with the existing binding, as I previously wrote (Nov. 17, 2008), I prefer the Emacs (C- c C-n) or Windows (C-r) style binding that combines R.App's Edit:Execute command (Cmd-Return) with a command to step through multi- line R input to the next new line. The default behavior differs from other platforms, presumably because it inherits from classes in the Cocoa ApplicationKit framework. Nevertheless, I thought it might be worthwhile documenting a couple of options on the off chance that someone else might someday wish to modify the default behavior. Obviously, one can just use Emacs or Aquamacs, although I do prefer the combination of power and simplicity in R.App, with the exception of this niggling difference from the behavior of R-GUI on other platforms. To be clear, the question asked was how to perform the following actions sequentially in the document window i) Call Edit:Execute for either the highlighted code or - if no selection has been made - the current line (R.App KeyBinding: Cmd- Return = "@\U000A") ii) Step to end of paragraph (i.e. possibly multi-line input) (Mac OS KeyBinding: Option Down Arrow "~\UF701") iii) Step to next line (Mac OS KeyBinding: Down Arrow "\UF701") To simply re-assign the key binding for the Edit:Execute command, you can just use the Keyboard System Preferences, which supports application-specific user bindings. If you don't mind interposing another layer of software (and any associated instability), you can also chain together a sequence of such keyboard shortcuts using a number of utilities. The shareware Butler (http://www.manytricks.com/butler/ ) allows this to be done in an application-specific way, and is easily configured to bind to the sequence Cmd-Return -> Option-DownArrow -> DownArrow, though you may need to insert delays between the individual steps. The same result is achieved by creating (or modifying) the DefaultKeyBindings.dict file in ~/Library/KeyBindings (which you will probably have to create). With this, a Windows-like Control-r binding becomes //DefaultKeyBindings.dict in ~/Library/KeyBindings //modifier keys are ^ for control, ~ for option, @ for Cmd { "^r" = ("executeSelection:", "moveToEndOfParagraph:","moveDown:"); } where the first selector is R-specific and the others are drawn from the built-in selector methods. If someone knows how to restrict this binding to a R.App, that would be a big plus. One approach is to simply modify executeSelection: in R.App. I admit that I had hoped to avoid Objective C, since my aging synapses cringe at the sight of all those overloaded ":" and "@"s (all the more reason to appreciate the authors of R.App :). Despite this, it is in fact relatively straightforward, so I outline it here for those like me who prefer instructions. On a newer Mac, you should already have installed an up-to-date version of XCode and the gcc compiler (which you will also find on the Apple Developer Disk or at http://developer.apple.com ). First download the R-App source (I used Mac-GUI-1-1.26.tar) from CRAN, and open the project file R.xcodeproj with XCode by double clicking (there is also an older project file R.xcode for older versions of the developer tools). If you use the Edit Menu: Find in Project function to search for the method executeSelection, it will take you to the definition of the method in file RDocumentWinCntrl.m. The original function is shown here, and it will need to be revised slightly - (IBAction)executeSelection:(id)sender { NSRange sr = [textView selectedRange]; if (sr.length>0) { NSString *stx = [[[textView textStorage] string] substringWithRange:sr]; [[RController sharedController] sendInput:stx]; } else { // if nothing is selected, execute the current line NSRange lineRange = [[[textView textStorage] string] lineRangeForRange:sr]; if (lineRange.length < 1) NSBeep(); // nothing to execute else [[RController sharedController] sendInput: [[[textView textStorage] string] substringWithRange: lineRange]]; } execNewlineFlag=YES; } Note the two calls to sendInput:, the first invoked when a range has been selected in the object textView (the document Window). In this case, it sends the command string stx to R via the sendInput:stx selector. Once this is done, you will want to add instructions to perform the same two "steps" outlined above, using selectors from the class NSTextView or its parents NSResponder, NSControl, and NSObject. To wit: [textView moveToEndOfParagraph:(NSNumber *)1]; //textView is type NSTextView, which inherits from NSResponder, NSControl, NSObject [textView moveDown:(NSNumber *)1]; These two lines are added to the body of the if statement, just after the first call to sendInput and before } else { // if nothing is selected, execute the current line If nothing has been highlighted in textView, this else statement selects the current line and sends it instead. So just add the same two lines to the body of the second else after the second call to sendInput: (which ends with the ;). Now, hit the Build and Go button, and a few seconds later, you should have a slightly modified R.App (in the deployment build-folder of the R-GUI source directory). It can be run from any location, but you will probably want to replace the current R.App in your Applications folder. As long as you're mucking about, you can if you wish also revise the key binding to Edit:Execute by double clicking the MainMenu.nib file to open in Interface Builder. I apologize for going on at length, but there may be others who would like to adjust the default behavior, and it wasn't altogether obvious how this was done. There is probably not enough interest to include this as an option in R.App to allow for greater cross-platform consistency, but the discussion of key bindings in the R Mac FAQ could perhaps add a line about the possibility of binding to methods defined in R.App, like executeSelection: I am far too much of a novice to know whether there is an easy way to identify selector methods that an application exports (except by exploring connections in interface builder), but I am curious whether there is something comparable to the dump utility for an old-fashioned C dll? Atul ------------------------ Atul Sharma MD, FRCP(C) Pediatric Nephrologist, Montreal QC Message: 6 Date: Mon, 17 Nov 2008 20:58:23 -0500 From: Atul Sharma <[email protected]> Subject: [R-SIG-Mac] R base functions from R.app To: <[email protected]> Message-ID: <[email protected]> Content-Type: text/plain; charset=US-ASCII; format=flowed; delsp=yes I am enormously grateful for the work and effort that has been put into the Mac port of R and R.app GUI. However, there are a couple of functions I miss from other versions, and I was wondering if there is an easy way to add them. 1) Under windows script editor, I was able to send a single line to the console via Control- R, which stepped the cursor to the next line in the editor. In R.app, I have to execute and then step manually to the next line Is there any easy way to have it step to the next line after executing. I don't mind recompiling R.app, which I do to reassign key bindings in any case, but I can't figure out how to modify this behavior (I am new to XCode, sorry). [[alternative HTML version deleted]] _______________________________________________ R-SIG-Mac mailing list [email protected] https://stat.ethz.ch/mailman/listinfo/r-sig-mac
