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

Reply via email to